diff --git a/desktop/package.json b/desktop/package.json index 3aeb75f..b00f292 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -41,6 +41,7 @@ "@tauri-apps/plugin-opener": "^2.5.3", "@xyflow/react": "^12.10.1", "clsx": "^2.1.1", + "dompurify": "^3.3.3", "framer-motion": "^12.38.0", "lucide-react": "^0.577.0", "react": "^19.2.4", @@ -62,6 +63,7 @@ "@tauri-apps/cli": "^2.10.1", "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.1.0", + "@types/dompurify": "^3.2.0", "@types/js-yaml": "^4.0.9", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", diff --git a/desktop/pnpm-lock.yaml b/desktop/pnpm-lock.yaml index 7fdee04..910df23 100644 --- a/desktop/pnpm-lock.yaml +++ b/desktop/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + dompurify: + specifier: ^3.3.3 + version: 3.3.3 framer-motion: specifier: ^12.38.0 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': 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) + '@types/dompurify': + specifier: ^3.2.0 + version: 3.2.0 '@types/js-yaml': specifier: ^4.0.9 version: 4.0.9 @@ -1199,6 +1205,10 @@ packages: '@types/debug@4.1.13': 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': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} @@ -1235,6 +1245,9 @@ packages: '@types/react@19.2.14': 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': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1719,6 +1732,9 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dompurify@3.3.3: + resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -4198,6 +4214,10 @@ snapshots: dependencies: '@types/ms': 2.1.0 + '@types/dompurify@3.2.0': + dependencies: + dompurify: 3.3.3 + '@types/esrecurse@4.3.1': {} '@types/estree-jsx@1.0.5': @@ -4235,6 +4255,9 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/trusted-types@2.0.7': + optional: true + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -4786,6 +4809,10 @@ snapshots: dom-accessibility-api@0.6.3: {} + dompurify@3.3.3: + optionalDependencies: + '@types/trusted-types': 2.0.7 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index 3ed30fa..c806b37 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -28,6 +28,9 @@ import { invoke } from '@tauri-apps/api/core'; import { useProposalNotifications, ProposalNotificationHandler } from './lib/useProposalNotifications'; import { useToast } from './components/ui/Toast'; import type { Clone } from './store/agentStore'; +import { createLogger } from './lib/logger'; + +const log = createLogger('App'); type View = 'main' | 'settings'; @@ -149,18 +152,18 @@ function App() { if (!isRunning && status.cliAvailable) { setBootstrapStatus('Starting ZCLAW Gateway...'); - console.log('[App] Local gateway not running, auto-starting...'); + log.debug('Local gateway not running, auto-starting...'); await startLocalGateway(); // Wait for gateway to be ready await new Promise(resolve => setTimeout(resolve, 2000)); - console.log('[App] Local gateway started'); + log.debug('Local gateway started'); } else if (isRunning) { - console.log('[App] Local gateway already running'); + log.debug('Local gateway already running'); } } 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.storageSizeBytes ); - console.log('[App] Memory stats synced to heartbeat engine'); + log.debug('Memory stats synced to heartbeat engine'); } catch (statsErr) { - console.warn('[App] Failed to sync memory stats:', statsErr); + log.warn('Failed to sync memory stats:', statsErr); } 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) const MEMORY_STATS_SYNC_INTERVAL = 5 * 60 * 1000; @@ -224,9 +227,9 @@ function App() { stats.totalEntries, stats.storageSizeBytes ); - console.log('[App] Memory stats synced (periodic)'); + log.debug('Memory stats synced (periodic)'); } catch (err) { - console.warn('[App] Periodic memory stats sync failed:', err); + log.warn('Periodic memory stats sync failed:', err); } }, MEMORY_STATS_SYNC_INTERVAL); @@ -234,7 +237,7 @@ function App() { // @ts-expect-error - Global cleanup reference window.__ZCLAW_STATS_SYNC_INTERVAL__ = statsSyncInterval; } catch (err) { - console.warn('[App] Failed to start heartbeat engine:', err); + log.warn('Failed to start heartbeat engine:', err); // Non-critical, continue without heartbeat } @@ -270,10 +273,10 @@ function App() { model: embConfig.model || undefined, endpoint: embConfig.endpoint || undefined, }); - console.log('[App] Embedding configuration restored to backend'); + log.debug('Embedding configuration restored to backend'); } } 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 } @@ -290,17 +293,17 @@ function App() { apiKey: modelConfig.apiKey, model: modelConfig.model || undefined, }); - console.log('[App] Summary driver configured with active LLM'); + log.debug('Summary driver configured with active LLM'); } } 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 } // Step 6: Bootstrap complete setBootstrapping(false); } catch (err) { - console.error('[App] Bootstrap failed:', err); + log.error('Bootstrap failed:', err); // Still allow app to load, connection status will show error setBootstrapping(false); } @@ -391,7 +394,7 @@ function App() { } } } 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 diff --git a/desktop/src/components/AgentOnboardingWizard.tsx b/desktop/src/components/AgentOnboardingWizard.tsx index 6c0d17d..30496f5 100644 --- a/desktop/src/components/AgentOnboardingWizard.tsx +++ b/desktop/src/components/AgentOnboardingWizard.tsx @@ -27,6 +27,9 @@ import { ScenarioTags } from './ScenarioTags'; import type { Clone } from '../store/agentStore'; import { intelligenceClient } from '../lib/intelligence-client'; import { generateSoulContent, generateUserContent } from '../lib/personality-presets'; +import { createLogger } from '../lib/logger'; + +const log = createLogger('AgentOnboardingWizard'); // === Types === @@ -215,9 +218,9 @@ export function AgentOnboardingWizard({ isOpen, onClose, onSuccess }: AgentOnboa // Write USER.md (user profile) 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) { - 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 } diff --git a/desktop/src/components/PipelineResultPreview.tsx b/desktop/src/components/PipelineResultPreview.tsx index 9f1c787..40a1ff6 100644 --- a/desktop/src/components/PipelineResultPreview.tsx +++ b/desktop/src/components/PipelineResultPreview.tsx @@ -23,6 +23,7 @@ import { } from 'lucide-react'; import { PipelineRunResponse } from '../lib/pipeline-client'; import { useToast } from './ui/Toast'; +import DOMPurify from 'dompurify'; import { ClassroomPreviewer, type ClassroomData } from './ClassroomPreviewer'; // === Types === diff --git a/desktop/src/hooks/useAutomationEvents.ts b/desktop/src/hooks/useAutomationEvents.ts index 2aac19d..f75664b 100644 --- a/desktop/src/hooks/useAutomationEvents.ts +++ b/desktop/src/hooks/useAutomationEvents.ts @@ -13,6 +13,9 @@ import { useWorkflowStore } from '../store/workflowStore'; import { useChatStore } from '../store/chatStore'; import type { GatewayClient } from '../lib/gateway-client'; import { speechSynth } from '../lib/speech-synth'; +import { createLogger } from '../lib/logger'; + +const log = createLogger('useAutomationEvents'); // === Event Types === @@ -132,7 +135,7 @@ export function useAutomationEvents( if (!isHandEvent(data)) return; const eventData = data as HandEventData; - console.log('[useAutomationEvents] Hand event:', eventData); + log.debug('Hand event:', eventData); // Refresh hands if status changed if (refreshOnStatusChange) { @@ -175,7 +178,7 @@ export function useAutomationEvents( pitch: typeof res.pitch === 'number' ? res.pitch : undefined, volume: typeof res.volume === 'number' ? res.volume : undefined, }).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; const eventData = data as WorkflowEventData; - console.log('[useAutomationEvents] Workflow event:', eventData); + log.debug('Workflow event:', eventData); // Refresh workflows if status changed if (refreshOnStatusChange) { @@ -250,7 +253,7 @@ export function useAutomationEvents( if (!isApprovalEvent(data)) return; const eventData = data as ApprovalEventData; - console.log('[useAutomationEvents] Approval event:', eventData); + log.debug('Approval event:', eventData); // Refresh approvals list loadApprovals(); diff --git a/desktop/src/lib/audit-logger.ts b/desktop/src/lib/audit-logger.ts index 62373dd..fa5a500 100644 --- a/desktop/src/lib/audit-logger.ts +++ b/desktop/src/lib/audit-logger.ts @@ -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; } diff --git a/desktop/src/lib/autonomy-manager.ts b/desktop/src/lib/autonomy-manager.ts index e42a1f3..02ffbb0 100644 --- a/desktop/src/lib/autonomy-manager.ts +++ b/desktop/src/lib/autonomy-manager.ts @@ -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( 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 }; } } diff --git a/desktop/src/lib/gateway-auth.ts b/desktop/src/lib/gateway-auth.ts index fa2a460..fc385e4 100644 --- a/desktop/src/lib/gateway-auth.ts +++ b/desktop/src/lib/gateway-auth.ts @@ -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 { 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 { export async function clearDeviceKeys(): Promise { 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); } } diff --git a/desktop/src/lib/llm-service.ts b/desktop/src/lib/llm-service.ts index 10d3ce8..e447801 100644 --- a/desktop/src/lib/llm-service.ts +++ b/desktop/src/lib/llm-service.ts @@ -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); } }; diff --git a/desktop/src/lib/request-helper.ts b/desktop/src/lib/request-helper.ts index 0a5179b..f3e5626 100644 --- a/desktop/src/lib/request-helper.ts +++ b/desktop/src/lib/request-helper.ts @@ -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(); diff --git a/desktop/src/lib/security-index.ts b/desktop/src/lib/security-index.ts index 012e345..fc91cb9 100644 --- a/desktop/src/lib/security-index.ts +++ b/desktop/src/lib/security-index.ts @@ -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 { 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 { const { clearKeyCache } = await import('./crypto-utils'); clearKeyCache(); - console.log('[Security] All security modules shut down'); + log.debug('All security modules shut down'); } /** diff --git a/desktop/src/lib/skill-discovery.ts b/desktop/src/lib/skill-discovery.ts index 3eb0380..184039c 100644 --- a/desktop/src/lib/skill-discovery.ts +++ b/desktop/src/lib/skill-discovery.ts @@ -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 }; } diff --git a/desktop/src/lib/use-onboarding.ts b/desktop/src/lib/use-onboarding.ts index bb2b3fd..aa5adc0 100644 --- a/desktop/src/lib/use-onboarding.ts +++ b/desktop/src/lib/use-onboarding.ts @@ -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; } diff --git a/desktop/src/lib/useProposalNotifications.ts b/desktop/src/lib/useProposalNotifications.ts index f2408c4..3d8e98e 100644 --- a/desktop/src/lib/useProposalNotifications.ts +++ b/desktop/src/lib/useProposalNotifications.ts @@ -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'); diff --git a/desktop/src/store/offlineStore.ts b/desktop/src/store/offlineStore.ts index 6921e76..9e56ca4 100644 --- a/desktop/src/store/offlineStore.ts +++ b/desktop/src/store/offlineStore.ts @@ -9,6 +9,9 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { useConnectionStore, getConnectionState } from './connectionStore'; import { generateRandomString } from '../lib/crypto-utils'; +import { createLogger } from '../lib/logger'; + +const log = createLogger('OfflineStore'); // === Types === @@ -153,7 +156,7 @@ export const useOfflineStore = create()( queuedMessages: [...s.queuedMessages, message], })); - console.log(`[OfflineStore] Message queued: ${id}`); + log.debug(`Message queued: ${id}`); return id; }, @@ -192,15 +195,15 @@ export const useOfflineStore = create()( // Check if connected if (getConnectionState() !== 'connected') { - console.log('[OfflineStore] Not connected, cannot retry messages'); + log.debug('Not connected, cannot retry messages'); return; } - console.log(`[OfflineStore] Retrying ${pending.length} queued messages`); + log.debug(`Retrying ${pending.length} queued messages`); for (const msg of pending) { 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'); continue; } @@ -220,11 +223,11 @@ export const useOfflineStore = create()( get().updateMessageStatus(msg.id, 'sent'); // Remove sent message after a short delay setTimeout(() => get().removeMessage(msg.id), 1000); - console.log(`[OfflineStore] Message ${msg.id} sent successfully`); + log.debug(`Message ${msg.id} sent successfully`); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Send failed'; 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()( const attempt = state.reconnectAttempt + 1; const delay = state.nextReconnectDelay; - console.log(`[OfflineStore] Scheduling reconnect attempt ${attempt} in ${delay}ms`); + log.debug(`Scheduling reconnect attempt ${attempt} in ${delay}ms`); set({ isReconnecting: true, @@ -265,7 +268,7 @@ export const useOfflineStore = create()( }, attemptReconnect: async () => { - console.log('[OfflineStore] Attempting to reconnect...'); + log.debug('Attempting to reconnect...'); try { // Try to connect via connection store @@ -273,12 +276,12 @@ export const useOfflineStore = create()( // Check if now connected if (getConnectionState() === 'connected') { - console.log('[OfflineStore] Reconnection successful'); + log.debug('Reconnection successful'); get().setOffline(false); return true; } } catch (err) { - console.warn('[OfflineStore] Reconnection failed:', err); + log.warn('Reconnection failed:', err); } // Still offline, schedule next attempt