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:
iven
2026-03-30 16:22:16 +08:00
parent 544358764e
commit ecd7f2e928
16 changed files with 140 additions and 68 deletions

View File

@@ -41,6 +41,7 @@
"@tauri-apps/plugin-opener": "^2.5.3", "@tauri-apps/plugin-opener": "^2.5.3",
"@xyflow/react": "^12.10.1", "@xyflow/react": "^12.10.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dompurify": "^3.3.3",
"framer-motion": "^12.38.0", "framer-motion": "^12.38.0",
"lucide-react": "^0.577.0", "lucide-react": "^0.577.0",
"react": "^19.2.4", "react": "^19.2.4",
@@ -62,6 +63,7 @@
"@tauri-apps/cli": "^2.10.1", "@tauri-apps/cli": "^2.10.1",
"@testing-library/jest-dom": "6.6.3", "@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "16.1.0", "@testing-library/react": "16.1.0",
"@types/dompurify": "^3.2.0",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",

27
desktop/pnpm-lock.yaml generated
View File

@@ -23,6 +23,9 @@ importers:
clsx: clsx:
specifier: ^2.1.1 specifier: ^2.1.1
version: 2.1.1 version: 2.1.1
dompurify:
specifier: ^3.3.3
version: 3.3.3
framer-motion: framer-motion:
specifier: ^12.38.0 specifier: ^12.38.0
version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -81,6 +84,9 @@ importers:
'@testing-library/react': '@testing-library/react':
specifier: 16.1.0 specifier: 16.1.0
version: 16.1.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 16.1.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@types/dompurify':
specifier: ^3.2.0
version: 3.2.0
'@types/js-yaml': '@types/js-yaml':
specifier: ^4.0.9 specifier: ^4.0.9
version: 4.0.9 version: 4.0.9
@@ -1199,6 +1205,10 @@ packages:
'@types/debug@4.1.13': '@types/debug@4.1.13':
resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
'@types/dompurify@3.2.0':
resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==}
deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.
'@types/esrecurse@4.3.1': '@types/esrecurse@4.3.1':
resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
@@ -1235,6 +1245,9 @@ packages:
'@types/react@19.2.14': '@types/react@19.2.14':
resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/unist@2.0.11': '@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
@@ -1719,6 +1732,9 @@ packages:
dom-accessibility-api@0.6.3: dom-accessibility-api@0.6.3:
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
dompurify@3.3.3:
resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==}
dunder-proto@1.0.1: dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -4198,6 +4214,10 @@ snapshots:
dependencies: dependencies:
'@types/ms': 2.1.0 '@types/ms': 2.1.0
'@types/dompurify@3.2.0':
dependencies:
dompurify: 3.3.3
'@types/esrecurse@4.3.1': {} '@types/esrecurse@4.3.1': {}
'@types/estree-jsx@1.0.5': '@types/estree-jsx@1.0.5':
@@ -4235,6 +4255,9 @@ snapshots:
dependencies: dependencies:
csstype: 3.2.3 csstype: 3.2.3
'@types/trusted-types@2.0.7':
optional: true
'@types/unist@2.0.11': {} '@types/unist@2.0.11': {}
'@types/unist@3.0.3': {} '@types/unist@3.0.3': {}
@@ -4786,6 +4809,10 @@ snapshots:
dom-accessibility-api@0.6.3: {} dom-accessibility-api@0.6.3: {}
dompurify@3.3.3:
optionalDependencies:
'@types/trusted-types': 2.0.7
dunder-proto@1.0.1: dunder-proto@1.0.1:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.2 call-bind-apply-helpers: 1.0.2

View File

@@ -28,6 +28,9 @@ import { invoke } from '@tauri-apps/api/core';
import { useProposalNotifications, ProposalNotificationHandler } from './lib/useProposalNotifications'; import { useProposalNotifications, ProposalNotificationHandler } from './lib/useProposalNotifications';
import { useToast } from './components/ui/Toast'; import { useToast } from './components/ui/Toast';
import type { Clone } from './store/agentStore'; import type { Clone } from './store/agentStore';
import { createLogger } from './lib/logger';
const log = createLogger('App');
type View = 'main' | 'settings'; type View = 'main' | 'settings';
@@ -149,18 +152,18 @@ function App() {
if (!isRunning && status.cliAvailable) { if (!isRunning && status.cliAvailable) {
setBootstrapStatus('Starting ZCLAW Gateway...'); setBootstrapStatus('Starting ZCLAW Gateway...');
console.log('[App] Local gateway not running, auto-starting...'); log.debug('Local gateway not running, auto-starting...');
await startLocalGateway(); await startLocalGateway();
// Wait for gateway to be ready // Wait for gateway to be ready
await new Promise(resolve => setTimeout(resolve, 2000)); await new Promise(resolve => setTimeout(resolve, 2000));
console.log('[App] Local gateway started'); log.debug('Local gateway started');
} else if (isRunning) { } else if (isRunning) {
console.log('[App] Local gateway already running'); log.debug('Local gateway already running');
} }
} catch (err) { } catch (err) {
console.warn('[App] Failed to check/start local gateway:', err); log.warn('Failed to check/start local gateway:', err);
} }
} }
@@ -204,13 +207,13 @@ function App() {
stats.totalEntries, stats.totalEntries,
stats.storageSizeBytes stats.storageSizeBytes
); );
console.log('[App] Memory stats synced to heartbeat engine'); log.debug('Memory stats synced to heartbeat engine');
} catch (statsErr) { } catch (statsErr) {
console.warn('[App] Failed to sync memory stats:', statsErr); log.warn('Failed to sync memory stats:', statsErr);
} }
await intelligenceClient.heartbeat.start(defaultAgentId); await intelligenceClient.heartbeat.start(defaultAgentId);
console.log('[App] Heartbeat engine started for self-evolution'); log.debug('Heartbeat engine started for self-evolution');
// Set up periodic memory stats sync (every 5 minutes) // Set up periodic memory stats sync (every 5 minutes)
const MEMORY_STATS_SYNC_INTERVAL = 5 * 60 * 1000; const MEMORY_STATS_SYNC_INTERVAL = 5 * 60 * 1000;
@@ -224,9 +227,9 @@ function App() {
stats.totalEntries, stats.totalEntries,
stats.storageSizeBytes stats.storageSizeBytes
); );
console.log('[App] Memory stats synced (periodic)'); log.debug('Memory stats synced (periodic)');
} catch (err) { } catch (err) {
console.warn('[App] Periodic memory stats sync failed:', err); log.warn('Periodic memory stats sync failed:', err);
} }
}, MEMORY_STATS_SYNC_INTERVAL); }, MEMORY_STATS_SYNC_INTERVAL);
@@ -234,7 +237,7 @@ function App() {
// @ts-expect-error - Global cleanup reference // @ts-expect-error - Global cleanup reference
window.__ZCLAW_STATS_SYNC_INTERVAL__ = statsSyncInterval; window.__ZCLAW_STATS_SYNC_INTERVAL__ = statsSyncInterval;
} catch (err) { } catch (err) {
console.warn('[App] Failed to start heartbeat engine:', err); log.warn('Failed to start heartbeat engine:', err);
// Non-critical, continue without heartbeat // Non-critical, continue without heartbeat
} }
@@ -270,10 +273,10 @@ function App() {
model: embConfig.model || undefined, model: embConfig.model || undefined,
endpoint: embConfig.endpoint || undefined, endpoint: embConfig.endpoint || undefined,
}); });
console.log('[App] Embedding configuration restored to backend'); log.debug('Embedding configuration restored to backend');
} }
} catch (embErr) { } catch (embErr) {
console.warn('[App] Failed to restore embedding config:', embErr); log.warn('Failed to restore embedding config:', embErr);
// Non-critical, semantic search will fall back to TF-IDF // Non-critical, semantic search will fall back to TF-IDF
} }
@@ -290,17 +293,17 @@ function App() {
apiKey: modelConfig.apiKey, apiKey: modelConfig.apiKey,
model: modelConfig.model || undefined, model: modelConfig.model || undefined,
}); });
console.log('[App] Summary driver configured with active LLM'); log.debug('Summary driver configured with active LLM');
} }
} catch (sumErr) { } catch (sumErr) {
console.warn('[App] Failed to configure summary driver:', sumErr); log.warn('Failed to configure summary driver:', sumErr);
// Non-critical, summaries won't be auto-generated // Non-critical, summaries won't be auto-generated
} }
// Step 6: Bootstrap complete // Step 6: Bootstrap complete
setBootstrapping(false); setBootstrapping(false);
} catch (err) { } catch (err) {
console.error('[App] Bootstrap failed:', err); log.error('Bootstrap failed:', err);
// Still allow app to load, connection status will show error // Still allow app to load, connection status will show error
setBootstrapping(false); setBootstrapping(false);
} }
@@ -391,7 +394,7 @@ function App() {
} }
} }
} catch (err) { } catch (err) {
console.warn('[App] Failed to create default agent on skip:', err); log.warn('Failed to create default agent on skip:', err);
} }
// Mark onboarding as completed // Mark onboarding as completed

View File

@@ -27,6 +27,9 @@ import { ScenarioTags } from './ScenarioTags';
import type { Clone } from '../store/agentStore'; import type { Clone } from '../store/agentStore';
import { intelligenceClient } from '../lib/intelligence-client'; import { intelligenceClient } from '../lib/intelligence-client';
import { generateSoulContent, generateUserContent } from '../lib/personality-presets'; import { generateSoulContent, generateUserContent } from '../lib/personality-presets';
import { createLogger } from '../lib/logger';
const log = createLogger('AgentOnboardingWizard');
// === Types === // === Types ===
@@ -215,9 +218,9 @@ export function AgentOnboardingWizard({ isOpen, onClose, onSuccess }: AgentOnboa
// Write USER.md (user profile) // Write USER.md (user profile)
await intelligenceClient.identity.updateFile(clone.id, 'user_profile', userContent); await intelligenceClient.identity.updateFile(clone.id, 'user_profile', userContent);
console.log('[Onboarding] SOUL.md and USER.md persisted for agent:', clone.id); log.debug('SOUL.md and USER.md persisted for agent:', clone.id);
} catch (err) { } catch (err) {
console.warn('[Onboarding] Failed to persist identity files:', err); log.warn('Failed to persist identity files:', err);
// Don't fail the whole onboarding if identity persistence fails // Don't fail the whole onboarding if identity persistence fails
} }

View File

@@ -23,6 +23,7 @@ import {
} from 'lucide-react'; } from 'lucide-react';
import { PipelineRunResponse } from '../lib/pipeline-client'; import { PipelineRunResponse } from '../lib/pipeline-client';
import { useToast } from './ui/Toast'; import { useToast } from './ui/Toast';
import DOMPurify from 'dompurify';
import { ClassroomPreviewer, type ClassroomData } from './ClassroomPreviewer'; import { ClassroomPreviewer, type ClassroomData } from './ClassroomPreviewer';
// === Types === // === Types ===

View File

@@ -13,6 +13,9 @@ import { useWorkflowStore } from '../store/workflowStore';
import { useChatStore } from '../store/chatStore'; import { useChatStore } from '../store/chatStore';
import type { GatewayClient } from '../lib/gateway-client'; import type { GatewayClient } from '../lib/gateway-client';
import { speechSynth } from '../lib/speech-synth'; import { speechSynth } from '../lib/speech-synth';
import { createLogger } from '../lib/logger';
const log = createLogger('useAutomationEvents');
// === Event Types === // === Event Types ===
@@ -132,7 +135,7 @@ export function useAutomationEvents(
if (!isHandEvent(data)) return; if (!isHandEvent(data)) return;
const eventData = data as HandEventData; const eventData = data as HandEventData;
console.log('[useAutomationEvents] Hand event:', eventData); log.debug('Hand event:', eventData);
// Refresh hands if status changed // Refresh hands if status changed
if (refreshOnStatusChange) { if (refreshOnStatusChange) {
@@ -175,7 +178,7 @@ export function useAutomationEvents(
pitch: typeof res.pitch === 'number' ? res.pitch : undefined, pitch: typeof res.pitch === 'number' ? res.pitch : undefined,
volume: typeof res.volume === 'number' ? res.volume : undefined, volume: typeof res.volume === 'number' ? res.volume : undefined,
}).catch((err: unknown) => { }).catch((err: unknown) => {
console.warn('[useAutomationEvents] Browser TTS failed:', err); log.warn('Browser TTS failed:', err);
}); });
} }
} }
@@ -209,7 +212,7 @@ export function useAutomationEvents(
if (!isWorkflowEvent(data)) return; if (!isWorkflowEvent(data)) return;
const eventData = data as WorkflowEventData; const eventData = data as WorkflowEventData;
console.log('[useAutomationEvents] Workflow event:', eventData); log.debug('Workflow event:', eventData);
// Refresh workflows if status changed // Refresh workflows if status changed
if (refreshOnStatusChange) { if (refreshOnStatusChange) {
@@ -250,7 +253,7 @@ export function useAutomationEvents(
if (!isApprovalEvent(data)) return; if (!isApprovalEvent(data)) return;
const eventData = data as ApprovalEventData; const eventData = data as ApprovalEventData;
console.log('[useAutomationEvents] Approval event:', eventData); log.debug('Approval event:', eventData);
// Refresh approvals list // Refresh approvals list
loadApprovals(); loadApprovals();

View File

@@ -5,6 +5,10 @@
* 记录关键操作Hand 触发、Agent 创建等)到本地存储。 * 记录关键操作Hand 触发、Agent 创建等)到本地存储。
*/ */
import { createLogger } from './logger';
const log = createLogger('AuditLogger');
export type AuditAction = export type AuditAction =
| 'hand.trigger' | 'hand.trigger'
| 'hand.approve' | 'hand.approve'
@@ -100,7 +104,7 @@ class AuditLogger {
this.logs.push(entry); this.logs.push(entry);
saveLocalLogs(this.logs); 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; return entry;
} }

View File

@@ -15,6 +15,9 @@
*/ */
import { generateRandomString } from './crypto-utils'; import { generateRandomString } from './crypto-utils';
import { createLogger } from './logger';
const log = createLogger('AutonomyManager');
// === Types === // === Types ===
@@ -286,7 +289,7 @@ export class AutonomyManager {
// Store in localStorage for persistence // Store in localStorage for persistence
this.persistPendingApprovals(); this.persistPendingApprovals();
console.log(`[AutonomyManager] Approval requested: ${approvalId} for ${decision.action}`); log.debug(`Approval requested: ${approvalId} for ${decision.action}`);
return approvalId; return approvalId;
} }
@@ -296,7 +299,7 @@ export class AutonomyManager {
approve(approvalId: string): boolean { approve(approvalId: string): boolean {
const decision = this.pendingApprovals.get(approvalId); const decision = this.pendingApprovals.get(approvalId);
if (!decision) { if (!decision) {
console.warn(`[AutonomyManager] Approval not found: ${approvalId}`); log.warn(`Approval not found: ${approvalId}`);
return false; return false;
} }
@@ -312,7 +315,7 @@ export class AutonomyManager {
// Update audit log // Update audit log
this.updateAuditLogOutcome(approvalId, 'success'); this.updateAuditLogOutcome(approvalId, 'success');
console.log(`[AutonomyManager] Approved: ${approvalId}`); log.info('Approved: ${approvalId}');
return true; return true;
} }
@@ -322,7 +325,7 @@ export class AutonomyManager {
reject(approvalId: string): boolean { reject(approvalId: string): boolean {
const decision = this.pendingApprovals.get(approvalId); const decision = this.pendingApprovals.get(approvalId);
if (!decision) { if (!decision) {
console.warn(`[AutonomyManager] Approval not found: ${approvalId}`); log.warn(`Approval not found: ${approvalId}`);
return false; return false;
} }
@@ -333,7 +336,7 @@ export class AutonomyManager {
// Update audit log // Update audit log
this.updateAuditLogOutcome(approvalId, 'failed'); this.updateAuditLogOutcome(approvalId, 'failed');
console.log(`[AutonomyManager] Rejected: ${approvalId}`); log.info('Rejected: ${approvalId}');
return true; return true;
} }
@@ -384,12 +387,12 @@ export class AutonomyManager {
rollback(auditId: string): boolean { rollback(auditId: string): boolean {
const entry = this.auditLog.find(e => e.id === auditId); const entry = this.auditLog.find(e => e.id === auditId);
if (!entry) { if (!entry) {
console.warn(`[AutonomyManager] Audit entry not found: ${auditId}`); log.warn('Audit entry not found: ${auditId}');
return false; return false;
} }
if (entry.outcome === 'rolled_back') { if (entry.outcome === 'rolled_back') {
console.warn(`[AutonomyManager] Already rolled back: ${auditId}`); log.warn('Already rolled back: ${auditId}');
return false; return false;
} }
@@ -398,7 +401,7 @@ export class AutonomyManager {
entry.rolledBackAt = new Date().toISOString(); entry.rolledBackAt = new Date().toISOString();
this.saveAuditLog(); this.saveAuditLog();
console.log(`[AutonomyManager] Rolled back: ${auditId}`); log.info(`Rolled back: ${auditId}`);
return true; return true;
} }
@@ -431,7 +434,7 @@ export class AutonomyManager {
setLevel(level: AutonomyLevel): void { setLevel(level: AutonomyLevel): void {
this.config = { ...DEFAULT_AUTONOMY_CONFIGS[level], level }; this.config = { ...DEFAULT_AUTONOMY_CONFIGS[level], level };
this.saveConfig(); this.saveConfig();
console.log(`[AutonomyManager] Level changed to: ${level}`); log.info(`Level changed to: ${level}`);
} }
// === Persistence === // === Persistence ===
@@ -537,7 +540,7 @@ export async function executeWithAutonomy<T>(
const result = await executor(); const result = await executor();
return { executed: true, result, decision }; return { executed: true, result, decision };
} catch (error) { } catch (error) {
console.error(`[AutonomyManager] Action ${action} failed:`, error); log.error(`Action ${action} failed:`, error);
return { executed: false, decision }; return { executed: false, decision };
} }
} }

View File

@@ -12,6 +12,9 @@ import {
getDeviceKeys, getDeviceKeys,
deleteDeviceKeys, deleteDeviceKeys,
} from './secure-storage'; } from './secure-storage';
import { createLogger } from './logger';
const log = createLogger('GatewayAuth');
// === Types === // === Types ===
@@ -81,7 +84,7 @@ export async function loadDeviceKeys(): Promise<DeviceKeys> {
publicKeyBase64: b64Encode(storedKeys.publicKey), publicKeyBase64: b64Encode(storedKeys.publicKey),
}; };
} catch (e) { } 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 // Invalid stored keys, clear and regenerate
await deleteDeviceKeys(); await deleteDeviceKeys();
} }
@@ -113,9 +116,9 @@ export async function getLocalDeviceIdentity(): Promise<LocalDeviceIdentity> {
export async function clearDeviceKeys(): Promise<void> { export async function clearDeviceKeys(): Promise<void> {
try { try {
await deleteDeviceKeys(); await deleteDeviceKeys();
console.log('[GatewayClient] Device keys cleared'); log.debug('Device keys cleared');
} catch (e) { } catch (e) {
console.warn('[GatewayClient] Failed to clear device keys:', e); log.warn('Failed to clear device keys:', e);
} }
} }

View File

@@ -15,6 +15,9 @@
*/ */
import { DEFAULT_MODEL_ID, DEFAULT_OPENAI_BASE_URL } from '../constants/models'; import { DEFAULT_MODEL_ID, DEFAULT_OPENAI_BASE_URL } from '../constants/models';
import { createLogger } from './logger';
const log = createLogger('LLMService');
// === Types === // === Types ===
@@ -770,12 +773,12 @@ export function startPromptOTASync(deviceId: string): void {
if (result.updates.length > 0) { if (result.updates.length > 0) {
const applied = applyPromptUpdates(result.updates); const applied = applyPromptUpdates(result.updates);
if (applied > 0) { if (applied > 0) {
console.log(`[Prompt OTA] 已更新 ${applied} 个提示词模板`); log.debug(`已更新 ${applied} 个提示词模板`);
} }
} }
} catch (err) { } catch (err) {
// 静默失败,不影响正常使用 // 静默失败,不影响正常使用
console.debug('[Prompt OTA] 检查更新失败:', err); log.debug('检查更新失败:', err);
} }
}; };

View File

@@ -7,6 +7,10 @@
* @module lib/request-helper * @module lib/request-helper
*/ */
import { createLogger } from './logger';
const log = createLogger('RequestHelper');
// === Configuration Types === // === Configuration Types ===
export interface RequestConfig { export interface RequestConfig {
@@ -161,8 +165,8 @@ export async function requestWithRetry(
// Check if we should retry // Check if we should retry
if (retryOn.includes(response.status) && attempt < retries) { if (retryOn.includes(response.status) && attempt < retries) {
const backoff = calculateBackoff(retryDelay, attempt); const backoff = calculateBackoff(retryDelay, attempt);
console.warn( log.warn(
`[RequestHelper] Request failed (${response.status}), ` + `Request failed (${response.status}), ` +
`retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})` `retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})`
); );
await delay(backoff); await delay(backoff);
@@ -184,8 +188,8 @@ export async function requestWithRetry(
// Check if we should retry // Check if we should retry
if (error.isRetryable(retryOn) && attempt < retries) { if (error.isRetryable(retryOn) && attempt < retries) {
const backoff = calculateBackoff(retryDelay, attempt); const backoff = calculateBackoff(retryDelay, attempt);
console.warn( log.warn(
`[RequestHelper] Request error (${error.status}), ` + `Request error (${error.status}), ` +
`retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})` `retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})`
); );
await delay(backoff); await delay(backoff);
@@ -206,8 +210,8 @@ export async function requestWithRetry(
// Retry on timeout // Retry on timeout
if (attempt < retries) { if (attempt < retries) {
const backoff = calculateBackoff(retryDelay, attempt); const backoff = calculateBackoff(retryDelay, attempt);
console.warn( log.warn(
`[RequestHelper] Request timed out, ` + `Request timed out, ` +
`retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})` `retrying in ${backoff}ms (attempt ${attempt + 1}/${retries})`
); );
await delay(backoff); await delay(backoff);
@@ -411,7 +415,7 @@ export class RequestManager {
cancelAll(): void { cancelAll(): void {
this.controllers.forEach((controller, id) => { this.controllers.forEach((controller, id) => {
controller.abort(); controller.abort();
console.log(`[RequestManager] Cancelled request: ${id}`); log.debug(`Cancelled request: ${id}`);
}); });
this.controllers.clear(); this.controllers.clear();
this.requestConfigs.clear(); this.requestConfigs.clear();

View File

@@ -12,6 +12,10 @@
* - security-utils: Input validation, XSS prevention, rate limiting * - security-utils: Input validation, XSS prevention, rate limiting
*/ */
import { createLogger } from './logger';
const log = createLogger('Security');
// Re-export crypto utilities // Re-export crypto utilities
export { export {
// Core encryption // Core encryption
@@ -194,7 +198,7 @@ export async function initializeSecurity(sessionId?: string): Promise<void> {
const { initializeEncryptedChatStorage } = await import('./encrypted-chat-storage'); const { initializeEncryptedChatStorage } = await import('./encrypted-chat-storage');
await initializeEncryptedChatStorage(); 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'); const { clearKeyCache } = await import('./crypto-utils');
clearKeyCache(); clearKeyCache();
console.log('[Security] All security modules shut down'); log.debug('All security modules shut down');
} }
/** /**

View File

@@ -13,6 +13,9 @@
import { intelligenceClient } from './intelligence-client'; import { intelligenceClient } from './intelligence-client';
import { canAutoExecute } from './autonomy-manager'; import { canAutoExecute } from './autonomy-manager';
import { createLogger } from './logger';
const log = createLogger('SkillDiscovery');
// === Types === // === Types ===
@@ -93,9 +96,9 @@ export class SkillDiscoveryEngine {
this.skills = backendSkills.map(this.convertFromBackend); this.skills = backendSkills.map(this.convertFromBackend);
this.loadedFromBackend = true; this.loadedFromBackend = true;
this.saveIndex(); this.saveIndex();
console.log(`[SkillDiscovery] Loaded ${this.skills.length} skills from backend`); log.debug(`Loaded ${this.skills.length} skills from backend`);
} catch (error) { } 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) // Keep using cached skills (loaded in loadIndex)
this.loadedFromBackend = false; this.loadedFromBackend = false;
} }
@@ -133,7 +136,7 @@ export class SkillDiscoveryEngine {
this.skills = backendSkills.map(this.convertFromBackend); this.skills = backendSkills.map(this.convertFromBackend);
this.loadedFromBackend = true; this.loadedFromBackend = true;
this.saveIndex(); this.saveIndex();
console.log(`[SkillDiscovery] Refreshed ${this.skills.length} skills`); log.debug(`Refreshed ${this.skills.length} skills`);
return this.skills.length; return this.skills.length;
} catch (error) { } catch (error) {
console.error('[SkillDiscovery] Failed to refresh skills:', error); console.error('[SkillDiscovery] Failed to refresh skills:', error);
@@ -335,7 +338,7 @@ export class SkillDiscoveryEngine {
if (!options?.skipAutonomyCheck) { if (!options?.skipAutonomyCheck) {
const action = installed ? 'skill_install' : 'skill_uninstall'; const action = installed ? 'skill_install' : 'skill_uninstall';
const { canProceed, decision } = canAutoExecute(action, 6); 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) { if (!canProceed) {
return { success: false, reason: decision.reason }; return { success: false, reason: decision.reason };
@@ -344,7 +347,7 @@ export class SkillDiscoveryEngine {
skill.installed = installed; skill.installed = installed;
this.saveIndex(); this.saveIndex();
console.log(`[SkillDiscovery] Skill ${skillId} ${installed ? 'installed' : 'uninstalled'}`); log.debug(`Skill ${skillId} ${installed ? 'installed' : 'uninstalled'}`);
return { success: true }; return { success: true };
} }

View File

@@ -6,6 +6,9 @@
*/ */
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { createLogger } from './logger';
const log = createLogger('useOnboarding');
const ONBOARDING_COMPLETED_KEY = 'zclaw-onboarding-completed'; const ONBOARDING_COMPLETED_KEY = 'zclaw-onboarding-completed';
const USER_PROFILE_KEY = 'zclaw-user-profile'; const USER_PROFILE_KEY = 'zclaw-user-profile';
@@ -56,7 +59,7 @@ export function useOnboarding(): OnboardingState {
setIsNeeded(true); setIsNeeded(true);
} }
} catch (err) { } catch (err) {
console.warn('[useOnboarding] Failed to check onboarding status:', err); log.warn('Failed to check onboarding status:', err);
setIsNeeded(true); setIsNeeded(true);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -75,7 +78,7 @@ export function useOnboarding(): OnboardingState {
localStorage.setItem(USER_PROFILE_KEY, JSON.stringify(fullProfile)); localStorage.setItem(USER_PROFILE_KEY, JSON.stringify(fullProfile));
setUserProfile(fullProfile); setUserProfile(fullProfile);
setIsNeeded(false); setIsNeeded(false);
console.log('[useOnboarding] Onboarding completed for user:', profile.userName); log.debug('Onboarding completed for user:', profile.userName);
} catch (err) { } catch (err) {
console.error('[useOnboarding] Failed to save onboarding status:', err); console.error('[useOnboarding] Failed to save onboarding status:', err);
} }
@@ -88,7 +91,7 @@ export function useOnboarding(): OnboardingState {
localStorage.removeItem(USER_PROFILE_KEY); localStorage.removeItem(USER_PROFILE_KEY);
setUserProfile(null); setUserProfile(null);
setIsNeeded(true); setIsNeeded(true);
console.log('[useOnboarding] Onboarding reset'); log.debug('Onboarding reset');
} catch (err) { } catch (err) {
console.error('[useOnboarding] Failed to reset onboarding:', err); console.error('[useOnboarding] Failed to reset onboarding:', err);
} }
@@ -113,7 +116,7 @@ export function getStoredUserProfile(): UserProfile | null {
return JSON.parse(profileStr) as UserProfile; return JSON.parse(profileStr) as UserProfile;
} }
} catch (err) { } catch (err) {
console.warn('[useOnboarding] Failed to get user profile:', err); log.warn('Failed to get user profile:', err);
} }
return null; return null;
} }

View File

@@ -14,6 +14,9 @@
import { useEffect, useRef, useCallback } from 'react'; import { useEffect, useRef, useCallback } from 'react';
import { useChatStore } from '../store/chatStore'; import { useChatStore } from '../store/chatStore';
import { intelligenceClient, type IdentityChangeProposal } from './intelligence-client'; import { intelligenceClient, type IdentityChangeProposal } from './intelligence-client';
import { createLogger } from './logger';
const log = createLogger('ProposalNotifications');
// Configuration // Configuration
const POLL_INTERVAL_MS = 60_000; // 1 minute const POLL_INTERVAL_MS = 60_000; // 1 minute
@@ -109,7 +112,7 @@ export function useProposalNotifications(): {
saveNotifiedProposals(notifiedProposalsRef.current); saveNotifiedProposals(notifiedProposalsRef.current);
} }
} catch (err) { } catch (err) {
console.warn('[ProposalNotifications] Failed to check proposals:', err); log.warn('Failed to check proposals:', err);
} finally { } finally {
isPollingRef.current = false; isPollingRef.current = false;
} }
@@ -164,7 +167,7 @@ export function ProposalNotificationHandler(): null {
const { count } = customEvent.detail; const { count } = customEvent.detail;
// You can integrate with a toast system here // 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: // If using the Toast system from Toast.tsx, you would do:
// toast(`${count} 个新的人格变更提案待审批`, 'info'); // toast(`${count} 个新的人格变更提案待审批`, 'info');

View File

@@ -9,6 +9,9 @@ import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { useConnectionStore, getConnectionState } from './connectionStore'; import { useConnectionStore, getConnectionState } from './connectionStore';
import { generateRandomString } from '../lib/crypto-utils'; import { generateRandomString } from '../lib/crypto-utils';
import { createLogger } from '../lib/logger';
const log = createLogger('OfflineStore');
// === Types === // === Types ===
@@ -153,7 +156,7 @@ export const useOfflineStore = create<OfflineStore>()(
queuedMessages: [...s.queuedMessages, message], queuedMessages: [...s.queuedMessages, message],
})); }));
console.log(`[OfflineStore] Message queued: ${id}`); log.debug(`Message queued: ${id}`);
return id; return id;
}, },
@@ -192,15 +195,15 @@ export const useOfflineStore = create<OfflineStore>()(
// Check if connected // Check if connected
if (getConnectionState() !== 'connected') { if (getConnectionState() !== 'connected') {
console.log('[OfflineStore] Not connected, cannot retry messages'); log.debug('Not connected, cannot retry messages');
return; return;
} }
console.log(`[OfflineStore] Retrying ${pending.length} queued messages`); log.debug(`Retrying ${pending.length} queued messages`);
for (const msg of pending) { for (const msg of pending) {
if (msg.retryCount >= state.maxRetryCount) { if (msg.retryCount >= state.maxRetryCount) {
console.log(`[OfflineStore] Message ${msg.id} exceeded max retries`); log.debug(`Message ${msg.id} exceeded max retries`);
get().updateMessageStatus(msg.id, 'failed', 'Max retry count exceeded'); get().updateMessageStatus(msg.id, 'failed', 'Max retry count exceeded');
continue; continue;
} }
@@ -220,11 +223,11 @@ export const useOfflineStore = create<OfflineStore>()(
get().updateMessageStatus(msg.id, 'sent'); get().updateMessageStatus(msg.id, 'sent');
// Remove sent message after a short delay // Remove sent message after a short delay
setTimeout(() => get().removeMessage(msg.id), 1000); setTimeout(() => get().removeMessage(msg.id), 1000);
console.log(`[OfflineStore] Message ${msg.id} sent successfully`); log.debug(`Message ${msg.id} sent successfully`);
} catch (err) { } catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Send failed'; const errorMessage = err instanceof Error ? err.message : 'Send failed';
get().updateMessageStatus(msg.id, 'failed', errorMessage); get().updateMessageStatus(msg.id, 'failed', errorMessage);
console.warn(`[OfflineStore] Message ${msg.id} failed:`, errorMessage); log.warn(`Message ${msg.id} failed:`, errorMessage);
} }
} }
}, },
@@ -243,7 +246,7 @@ export const useOfflineStore = create<OfflineStore>()(
const attempt = state.reconnectAttempt + 1; const attempt = state.reconnectAttempt + 1;
const delay = state.nextReconnectDelay; const delay = state.nextReconnectDelay;
console.log(`[OfflineStore] Scheduling reconnect attempt ${attempt} in ${delay}ms`); log.debug(`Scheduling reconnect attempt ${attempt} in ${delay}ms`);
set({ set({
isReconnecting: true, isReconnecting: true,
@@ -265,7 +268,7 @@ export const useOfflineStore = create<OfflineStore>()(
}, },
attemptReconnect: async () => { attemptReconnect: async () => {
console.log('[OfflineStore] Attempting to reconnect...'); log.debug('Attempting to reconnect...');
try { try {
// Try to connect via connection store // Try to connect via connection store
@@ -273,12 +276,12 @@ export const useOfflineStore = create<OfflineStore>()(
// Check if now connected // Check if now connected
if (getConnectionState() === 'connected') { if (getConnectionState() === 'connected') {
console.log('[OfflineStore] Reconnection successful'); log.debug('Reconnection successful');
get().setOffline(false); get().setOffline(false);
return true; return true;
} }
} catch (err) { } catch (err) {
console.warn('[OfflineStore] Reconnection failed:', err); log.warn('Reconnection failed:', err);
} }
// Still offline, schedule next attempt // Still offline, schedule next attempt