From eca77537f8aebd9b6b626218c4ff2a079d644438 Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sat, 29 Nov 2025 18:47:53 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 15 + README.md | 3 + plugin.lock.json | 57 ++ skills/walrus-code-generator.md | 833 +++++++++++++++++++++++++ skills/walrus-debugger.md | 828 ++++++++++++++++++++++++ skills/walrus-monitor.md | 757 ++++++++++++++++++++++ skills/walrus-performance-optimizer.md | 761 ++++++++++++++++++++++ 7 files changed, 3254 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 plugin.lock.json create mode 100644 skills/walrus-code-generator.md create mode 100644 skills/walrus-debugger.md create mode 100644 skills/walrus-monitor.md create mode 100644 skills/walrus-performance-optimizer.md diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..a54a26a --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,15 @@ +{ + "name": "walrus-ts", + "description": "Walrus TypeScript SDK 专业化 AI 技能集,提供代码生成、错误诊断、监控分析和性能优化功能,专为 Walrus 去中心化存储应用开发者打造", + "version": "1.0.0", + "author": { + "name": "icehugh", + "url": "https://github.com/icehugh" + }, + "skills": [ + "./skills/walrus-code-generator.md", + "./skills/walrus-debugger.md", + "./skills/walrus-monitor.md", + "./skills/walrus-performance-optimizer.md" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9d87c38 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# walrus-ts + +Walrus TypeScript SDK 专业化 AI 技能集,提供代码生成、错误诊断、监控分析和性能优化功能,专为 Walrus 去中心化存储应用开发者打造 diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..9b598c0 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,57 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:IceHugh/web3-skills:plugins/walrus-ts", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "06d68ff1bf73ce56b1d9e51b692e8c69277090b2", + "treeHash": "64320018af6f422c00f00245e9acab8eee6a96350c8d4b0254b190f8419e4376", + "generatedAt": "2025-11-28T10:11:42.066243Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "walrus-ts", + "description": "Walrus TypeScript SDK 专业化 AI 技能集,提供代码生成、错误诊断、监控分析和性能优化功能,专为 Walrus 去中心化存储应用开发者打造", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "c83f9e4d8d2f29b7530bb5ae2c1d5de617ff56cfd8e4d8072600c6a033e87d93" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "dbbf5f4988e401c731ec8cfe4f222e50917f81b88847b9ce2d7a09768eb5db07" + }, + { + "path": "skills/walrus-debugger.md", + "sha256": "08c4cc9a125b1d339e17cfdedd1bba446cbfb6608bdb4c9e9348985a1b6e2486" + }, + { + "path": "skills/walrus-monitor.md", + "sha256": "177168c232f00d9a8c32f91901a7c37165921e41f219f1dc10eb74c38f4abf53" + }, + { + "path": "skills/walrus-code-generator.md", + "sha256": "369009349d62f07b85df07655e447dccaba50579a533ee16f149481f158855d9" + }, + { + "path": "skills/walrus-performance-optimizer.md", + "sha256": "e8a5b724f84d5966fca546a5c3d6f7bd6a9759a1e328cc102c99433a302380c9" + } + ], + "dirSha256": "64320018af6f422c00f00245e9acab8eee6a96350c8d4b0254b190f8419e4376" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/walrus-code-generator.md b/skills/walrus-code-generator.md new file mode 100644 index 0000000..a7b92f4 --- /dev/null +++ b/skills/walrus-code-generator.md @@ -0,0 +1,833 @@ +--- +name: walrus-code-generator +description: 自动生成 Walrus TypeScript SDK 集成代码和最佳实践模板 +parameters: + - name: use-case + type: string + description: 使用场景 (basic-upload/advanced-storage/browser-app/batch-processing) + required: true + - name: framework + type: string + description: 开发框架 (react/vue/nodejs/nextjs/express) + default: nodejs + - name: features + type: array + description: 需要的功能特性 + default: [] + - name: environment + type: string + description: 部署环境 (development/production) + default: development +--- + +# Walrus 代码生成技能 + +## 技能概述 + +`walrus-code-generator` 是一个专业的代码生成助手,根据您的具体需求自动生成 Walrus TypeScript SDK 集成代码。支持多种使用场景、开发框架和部署环境,确保生成的代码符合最佳实践。 + +## 支持的使用场景 + +### 1. 基础文件上传 (basic-upload) + +```typescript +// 自动生成的基础上传服务 +skill: "walrus-code-generator" +// use-case: basic-upload +// framework: nodejs +// features: ["error-handling", "progress-tracking"] + +export class BasicWalrusUploader { + private client: any; + private signer: any; + + constructor(network: 'testnet' | 'mainnet' = 'testnet') { + const suiClient = new SuiJsonRpcClient({ + url: getFullnodeUrl(network), + network, + }).$extend(walrus()); + + this.client = suiClient; + this.signer = this.createSigner(); + } + + async uploadFile( + filePath: string, + options: UploadOptions = {} + ): Promise { + const { + epochs = 10, + deletable = true, + tags = {}, + onProgress = () => {} + } = options; + + try { + onProgress(25, '读取文件...'); + + const fs = await import('fs/promises'); + const fileContent = await fs.readFile(filePath); + const fileName = filePath.split('/').pop() || 'unknown'; + + onProgress(50, '创建 Walrus 文件...'); + + const file = WalrusFile.from({ + contents: fileContent, + identifier: `uploads/${Date.now()}_${fileName}`, + mimeType: this.getMimeType(filePath), + attributes: { + 'original-path': filePath, + 'file-size': fileContent.length.toString(), + 'upload-time': new Date().toISOString(), + ...tags + } + }); + + onProgress(75, '上传到网络...'); + + const result = await this.client.walrus.writeFiles({ + files: [file], + epochs, + deletable, + signer: this.signer + }); + + onProgress(100, '上传完成'); + + return { + success: true, + blobId: result[0].blobId, + fileName, + size: fileContent.length, + storedUntil: result[0].storedUntil + }; + + } catch (error) { + console.error('上传失败:', error.message); + throw new Error(`上传失败: ${error.message}`); + } + } + + private createSigner(): Ed25519Keypair { + const privateKey = process.env.WALRUS_PRIVATE_KEY; + if (!privateKey) { + throw new Error('未找到私钥环境变量 WALRUS_PRIVATE_KEY'); + } + return Ed25519Keypair.fromSecretKey(privateKey); + } + + private getMimeType(filePath: string): string { + const ext = filePath.split('.').pop()?.toLowerCase(); + const mimeTypes: Record = { + 'txt': 'text/plain', + 'pdf': 'application/pdf', + 'jpg': 'image/jpeg', + 'png': 'image/png', + 'json': 'application/json' + }; + return mimeTypes[ext || ''] || 'application/octet-stream'; + } +} + +interface UploadOptions { + epochs?: number; + deletable?: boolean; + tags?: Record; + onProgress?: (progress: number, message: string) => void; +} + +interface UploadResult { + success: boolean; + blobId: string; + fileName: string; + size: number; + storedUntil: Date; +} +``` + +### 2. 高级存储管理 (advanced-storage) + +```typescript +// 自动生成的高级存储管理服务 +skill: "walrus-code-generator" +// use-case: advanced-storage +// framework: nodejs +// features: ["batch-operations", "retry-logic", "cache-management", "metadata-indexing"] + +export class AdvancedWalrusStorage { + private client: any; + private signer: any; + private cache: Map = new Map(); + private retryConfig: RetryConfig; + + constructor(options: AdvancedStorageOptions = {}) { + const { + network = 'testnet', + retryAttempts = 3, + retryDelay = 1000, + cacheEnabled = true, + cacheTTL = 300000 // 5分钟 + } = options; + + const suiClient = new SuiJsonRpcClient({ + url: getFullnodeUrl(network), + network, + }).$extend(walrus({ + storageNodeClientOptions: { + timeout: 60000, + onError: (error) => console.warn('存储节点错误:', error) + } + })); + + this.client = suiClient; + this.signer = this.createSigner(); + this.retryConfig = { attempts: retryAttempts, delay: retryDelay }; + this.cacheEnabled = cacheEnabled; + this.cacheTTL = cacheTTL; + } + + async batchUpload( + files: FileUploadRequest[], + options: BatchUploadOptions = {} + ): Promise { + const { + epochs = 10, + deletable = true, + maxConcurrency = 3, + commonTags = {} + } = options; + + const results: UploadResult[] = []; + const errors: UploadError[] = []; + + // 分批处理 + for (let i = 0; i < files.length; i += maxConcurrency) { + const batch = files.slice(i, i + maxConcurrency); + + const batchPromises = batch.map(async (fileRequest, index) => { + try { + const result = await this.uploadWithRetry(fileRequest, { + epochs, + deletable, + tags: { ...commonTags, ...fileRequest.tags } + }); + + results.push({ + ...result, + originalPath: fileRequest.path, + batchIndex: i + index + }); + + } catch (error) { + errors.push({ + originalPath: fileRequest.path, + error: error.message, + batchIndex: i + index + }); + } + }); + + await Promise.all(batchPromises); + + // 批次间延迟 + if (i + maxConcurrency < files.length) { + await new Promise(resolve => setTimeout(resolve, 500)); + } + } + + return { + totalFiles: files.length, + successful: results.length, + failed: errors.length, + results, + errors + }; + } + + async smartDownload( + blobId: string, + options: SmartDownloadOptions = {} + ): Promise { + const { + forceRefresh = false, + verifyIntegrity = true, + cacheKey = blobId + } = options; + + // 检查缓存 + if (!forceRefresh && this.cacheEnabled) { + const cached = this.getFromCache(cacheKey); + if (cached) { + console.log('从缓存返回文件:', blobId); + return { + ...cached, + fromCache: true + }; + } + } + + try { + const result = await this.downloadWithVerification(blobId, verifyIntegrity); + + // 缓存结果 + if (this.cacheEnabled) { + this.setToCache(cacheKey, result); + } + + return { + ...result, + fromCache: false + }; + + } catch (error) { + throw new Error(`智能下载失败: ${error.message}`); + } + } + + async searchByTags(tags: Record): Promise { + // 这是一个概念性实现 + // 实际搜索需要基于您自己的元数据索引系统 + const indexedFiles = await this.getIndexedFiles(); + + return indexedFiles.filter(file => { + return Object.entries(tags).every(([key, value]) => + file.tags[key] === value + ); + }); + } + + private async uploadWithRetry( + fileRequest: FileUploadRequest, + options: UploadOptions + ): Promise { + const { attempts, delay } = this.retryConfig; + + for (let attempt = 1; attempt <= attempts; attempt++) { + try { + return await this.uploadSingleFile(fileRequest, options); + } catch (error) { + if (attempt < attempts && this.isRetryableError(error)) { + console.log(`上传失败,第 ${attempt} 次重试...`); + await new Promise(resolve => setTimeout(resolve, delay * attempt)); + continue; + } + throw error; + } + } + + throw new Error('上传失败,已达到最大重试次数'); + } + + private async uploadSingleFile( + fileRequest: FileUploadRequest, + options: UploadOptions + ): Promise { + const fs = await import('fs/promises'); + const fileContent = await fs.readFile(fileRequest.path); + + const file = WalrusFile.from({ + contents: fileContent, + identifier: fileRequest.identifier || `files/${Date.now()}_${fileRequest.path}`, + mimeType: fileRequest.mimeType || this.getMimeType(fileRequest.path), + attributes: { + 'original-path': fileRequest.path, + 'file-size': fileContent.length.toString(), + 'upload-time': new Date().toISOString(), + ...options.tags + } + }); + + const result = await this.client.walrus.writeFiles({ + files: [file], + epochs: options.epochs, + deletable: options.deletable, + signer: this.signer + }); + + // 索引文件元数据 + await this.indexFile({ + blobId: result[0].blobId, + identifier: file.identifier, + path: fileRequest.path, + size: fileContent.length, + tags: options.tags, + uploadTime: new Date() + }); + + return { + success: true, + blobId: result[0].blobId, + identifier: file.identifier, + size: fileContent.length, + storedUntil: result[0].storedUntil + }; + } + + private async downloadWithVerification( + blobId: string, + verify: boolean + ): Promise { + const [file] = await this.client.walrus.getFiles({ ids: [blobId] }); + + const exists = await file.exists(); + if (!exists) { + throw new Error(`文件不存在: ${blobId}`); + } + + const content = await file.bytes(); + let verified = true; + + if (verify) { + const verification = await this.verifyFileIntegrity(blobId, content); + verified = verification.valid; + } + + return { + blobId, + content: Buffer.from(content), + size: content.length, + storedUntil: await file.storedUntil(), + verified, + metadata: await this.getFileMetadata(file) + }; + } + + private async verifyFileIntegrity( + blobId: string, + content: Uint8Array + ): Promise<{ valid: boolean; error?: string }> { + try { + // 基本完整性检查 + if (content.length === 0) { + return { valid: false, error: '文件内容为空' }; + } + + // 这里可以添加更复杂的验证逻辑 + return { valid: true }; + } catch (error) { + return { valid: false, error: error.message }; + } + } + + // 缓存管理 + private getFromCache(key: string): CachedFile | null { + const cached = this.cache.get(key); + if (cached && Date.now() - cached.timestamp < this.cacheTTL) { + return cached; + } + this.cache.delete(key); + return null; + } + + private setToCache(key: string, file: SmartDownloadResult): void { + this.cache.set(key, { + ...file, + timestamp: Date.now() + }); + } + + private isRetryableError(error: any): boolean { + return error instanceof RetryableWalrusClientError || + error.message.includes('timeout') || + error.message.includes('network'); + } +} + +// 类型定义 +interface FileUploadRequest { + path: string; + identifier?: string; + mimeType?: string; + tags?: Record; +} + +interface BatchUploadOptions { + epochs?: number; + deletable?: boolean; + maxConcurrency?: number; + commonTags?: Record; +} + +interface BatchUploadResult { + totalFiles: number; + successful: number; + failed: number; + results: (UploadResult & { originalPath: string; batchIndex: number })[]; + errors: UploadError[]; +} + +interface SmartDownloadOptions { + forceRefresh?: boolean; + verifyIntegrity?: boolean; + cacheKey?: string; +} + +interface SmartDownloadResult { + blobId: string; + content: Buffer; + size: number; + storedUntil: Date; + verified: boolean; + metadata: any; + fromCache: boolean; +} + +interface CachedFile extends SmartDownloadResult { + timestamp: number; +} + +interface RetryConfig { + attempts: number; + delay: number; +} +``` + +### 3. React 浏览器应用 (browser-app) + +```typescript +// 自动生成的 React Hook 和组件 +skill: "walrus-code-generator" +// use-case: browser-app +// framework: react +// features: ["wallet-integration", "progress-tracking", "drag-drop"] + +import React, { useState, useCallback, createContext, useContext } from 'react'; +import { useWallet } from '@suiet/wallet-kit'; + +// Walrus Context +const WalrusContext = createContext<{ + upload: (files: FileList) => Promise; + download: (blobId: string, filename?: string) => Promise; + isUploading: boolean; +} | null>(null); + +export const WalrusProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const { connected, signAndExecuteTransactionBlock } = useWallet(); + const [isUploading, setIsUploading] = useState(false); + + const upload = useCallback(async (files: FileList): Promise => { + if (!connected) { + throw new Error('请先连接钱包'); + } + + setIsUploading(true); + const results: UploadResult[] = []; + + try { + for (let i = 0; i < files.length; i++) { + const file = files[i]; + + try { + const result = await uploadFile(file, signAndExecuteTransactionBlock); + results.push({ ...result, originalName: file.name, index: i }); + } catch (error) { + results.push({ + success: false, + originalName: file.name, + index: i, + error: error.message + }); + } + } + } finally { + setIsUploading(false); + } + + return results; + }, [connected, signAndExecuteTransactionBlock]); + + const download = useCallback(async (blobId: string, filename?: string) => { + await downloadFile(blobId, filename); + }, []); + + return ( + + {children} + + ); +}; + +export const useWalrus = () => { + const context = useContext(WalrusContext); + if (!context) { + throw new Error('useWalrus must be used within WalrusProvider'); + } + return context; +}; + +// 文件上传组件 +export const WalrusUpload: React.FC<{ + onUploadComplete?: (results: UploadResult[]) => void; + maxFiles?: number; + acceptedTypes?: string; +}> = ({ onUploadComplete, maxFiles = 10, acceptedTypes = '*' }) => { + const { upload, isUploading } = useWalrus(); + const [dragActive, setDragActive] = useState(false); + const [progress, setProgress] = useState(0); + const fileInputRef = React.useRef(null); + + const handleFiles = useCallback(async (files: FileList) => { + if (files.length > maxFiles) { + alert(`最多只能上传 ${maxFiles} 个文件`); + return; + } + + try { + setProgress(10); + const results = await upload(files); + setProgress(100); + onUploadComplete?.(results); + } catch (error) { + alert(`上传失败: ${error.message}`); + setProgress(0); + } + }, [upload, maxFiles, onUploadComplete]); + + const handleDrop = useCallback((e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + + const files = e.dataTransfer.files; + if (files.length > 0) { + handleFiles(files); + } + }, [handleFiles]); + + const handleChange = useCallback((e: React.ChangeEvent) => { + const files = e.target.files; + if (files && files.length > 0) { + handleFiles(files); + } + }, [handleFiles]); + + return ( +
+
{ + e.preventDefault(); + e.stopPropagation(); + setDragActive(true); + }} + onDragLeave={(e) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + }} + onDragOver={(e) => { + e.preventDefault(); + e.stopPropagation(); + }} + onDrop={handleDrop} + onClick={() => fileInputRef.current?.click()} + > + + + {isUploading ? ( +
+
+
+
+

上传中... {progress}%

+
+ ) : ( +
+
📁
+

拖拽文件到此处或点击上传

+

支持最多 {maxFiles} 个文件

+
+ )} +
+
+ ); +}; + +// 文件列表组件 +export const WalrusFileList: React.FC<{ + files: UploadResult[]; + onDownload?: (blobId: string, filename: string) => void; +}> = ({ files, onDownload }) => { + const { download } = useWalrus(); + + const handleDownload = useCallback(async (blobId: string, filename: string) => { + try { + await onDownload ? onDownload(blobId, filename) : download(blobId, filename); + } catch (error) { + alert(`下载失败: ${error.message}`); + } + }, [download, onDownload]); + + return ( +
+

文件列表

+ {files.map((file, index) => ( +
+
+ {file.originalName} + {file.success ? ( + ✓ 上传成功 + ) : ( + ✗ {file.error} + )} +
+ {file.success && file.blobId && ( +
+ + {file.blobId.slice(0, 16)}... +
+ )} +
+ ))} +
+ ); +}; + +// 辅助函数 +async function uploadFile( + file: File, + signAndExecuteTransactionBlock: any +): Promise { + const arrayBuffer = await file.arrayBuffer(); + const walrusFile = WalrusFile.from({ + contents: new Uint8Array(arrayBuffer), + identifier: `browser/${Date.now()}_${file.name}`, + mimeType: file.type, + attributes: { + 'original-name': file.name, + 'file-size': file.size.toString(), + 'upload-source': 'browser' + } + }); + + const suiClient = new SuiClient({ + url: getFullnodeUrl('testnet'), + }); + + const walrusClient = new WalrusClient({ + network: 'testnet', + suiClient, + }); + + const writeFlow = walrusClient.walrus.writeFilesFlow({ + files: [walrusFile], + epochs: 10, + deletable: true + }); + + const result = await writeFlow.executeWithSigner({ + signAndExecuteTransactionBlock + }); + + return { + success: true, + blobId: result[0].blobId, + size: file.size + }; +} + +async function downloadFile(blobId: string, filename?: string) { + // 实现文件下载逻辑 + const link = document.createElement('a'); + link.href = `#download-${blobId}`; + link.download = filename || 'downloaded-file'; + link.click(); +} + +interface UploadResult { + success: boolean; + blobId?: string; + size?: number; + originalName: string; + index: number; + error?: string; +} +``` + +## 使用方法 + +### 基础代码生成 +```typescript +skill: "walrus-code-generator" +// use-case: basic-upload +// framework: nodejs +// features: ["error-handling", "logging"] +``` + +### React 应用生成 +```typescript +skill: "walrus-code-generator" +// use-case: browser-app +// framework: react +// features: ["wallet-integration", "drag-drop", "progress-bar"] +``` + +### 高级存储系统 +```typescript +skill: "walrus-code-generator" +// use-case: advanced-storage +// framework: nodejs +// features: ["batch-operations", "caching", "retry-logic", "metadata-indexing"] +``` + +### 批量处理系统 +```typescript +skill: "walrus-code-generator" +// use-case: batch-processing +// framework: express +// features: ["api-endpoints", "queue-system", "monitoring"] +``` + +## 生成的代码特性 + +### 🛡️ 错误处理 +- 智能重试机制 +- 详细错误信息 +- 优雅降级策略 + +### 📊 进度跟踪 +- 实时上传进度 +- 批量操作状态 +- 性能监控 + +### 🔧 配置管理 +- 灵活的配置选项 +- 环境适配 +- 类型安全 + +### 🚀 性能优化 +- 缓存机制 +- 并发控制 +- 内存管理 + +### 🔒 安全考虑 +- 输入验证 +- 文件完整性检查 +- 访问控制 + +## 最佳实践 + +1. **错误处理** - 生成的代码包含完整的错误处理逻辑 +2. **类型安全** - 使用 TypeScript 提供完整的类型定义 +3. **性能优化** - 包含缓存、批量处理和并发控制 +4. **可扩展性** - 模块化设计,易于扩展和维护 +5. **监控日志** - 集成日志记录和性能监控 + +--- + +*更新时间:2025-11-11* \ No newline at end of file diff --git a/skills/walrus-debugger.md b/skills/walrus-debugger.md new file mode 100644 index 0000000..23ff99e --- /dev/null +++ b/skills/walrus-debugger.md @@ -0,0 +1,828 @@ +--- +name: walrus-debugger +description: Walrus 错误诊断专家 - 智能分析和解决 Walrus 应用中的问题 +parameters: + - name: error-type + type: string + description: 错误类型 (upload/download/network/configuration/timeout/authentication) + required: true + - name: error-context + type: string + description: 错误信息、代码片段或问题描述 + required: true + - name: environment + type: string + description: 运行环境 (browser/nodejs/nextjs/production) + default: nodejs +--- + +# Walrus 错误诊断技能 + +## 技能概述 + +`walrus-debugger` 是专业的错误诊断助手,专门分析 Walrus 应用中的各种问题,提供智能的解决方案和预防措施。 + +## 常见错误类型及解决方案 + +### 1. 上传失败错误 + +#### 错误类型: Upload Failed +```typescript +// 错误示例 +Error: Upload failed: insufficient storage balance +Error: Upload failed: blob size exceeds maximum limit +Error: Upload failed: transaction rejected + +// 诊断和解决方案 +class UploadErrorDiagnostics { + async diagnoseUploadError(error: Error, context: UploadContext): Promise { + const analysis = this.analyzeErrorMessage(error.message); + + switch (analysis.type) { + case 'INSUFFICIENT_BALANCE': + return this.diagnoseBalanceError(context); + case 'SIZE_LIMIT': + return this.diagnoseSizeError(context); + case 'TRANSACTION_REJECTED': + return this.diagnoseTransactionError(error, context); + case 'TIMEOUT': + return this.diagnoseTimeoutError(context); + default: + return this.diagnoseGenericUploadError(error, context); + } + } + + private async diagnoseBalanceError(context: UploadContext): Promise { + const walletBalance = await this.checkWalletBalance(context.signerAddress); + + return { + errorType: 'INSUFFICIENT_BALANCE', + description: '钱包余额不足,无法支付存储费用', + causes: [ + 'WAL 代币余额不足', + 'SUI 代币余额不足以支付 gas 费', + '存储成本计算错误' + ], + solutions: [ + { + title: '检查并充值钱包', + code: ` +// 检查钱包余额 +async function checkBalances(address: string) { + const suiClient = new SuiClient({ url: getFullnodeUrl('testnet') }); + const balance = await suiClient.getBalance({ owner: address }); + + console.log('SUI Balance:', balance.totalBalance); + + // 检查 WAL 余额(如果支持) + const walBalance = await suiClient.getBalance({ + owner: address, + coinType: '0x2::sui::SUI' // 替换为 WAL coin type + }); + + return { sui: balance.totalBalance, wal: walBalance.totalBalance }; +}` + }, + { + title: '优化存储策略', + code: ` +// 使用更短的存储周期 +const result = await client.walrus.writeFiles({ + files: [file], + epochs: 5, // 减少存储周期 + deletable: true, + signer: keypair +}); + +// 或者使用更小的文件 +function compressFile(file: File): Promise { + return new Promise((resolve) => { + if (file.size > 10 * 1024 * 1024) { // 10MB + // 压缩或分割文件 + compressImage(file).then(resolve); + } else { + resolve(file); + } + }); +}` + } + ], + prevention: [ + '定期检查钱包余额', + '设置余额预警', + '实施费用估算功能' + ] + }; + } + + private async diagnoseSizeError(context: UploadContext): Promise { + const fileStats = await this.analyzeFileSize(context.file); + + return { + errorType: 'SIZE_LIMIT', + description: '文件大小超出限制', + causes: [ + `文件大小 ${fileStats.size}MB 超出限制`, + '网络环境不支持大文件传输', + '客户端内存不足' + ], + solutions: [ + { + title: '文件压缩', + code: ` +// 图片压缩 +async function compressImage(file: File, quality = 0.8): Promise { + return new Promise((resolve) => { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d')!; + const img = new Image(); + + img.onload = () => { + // 计算压缩后的尺寸 + const scale = Math.min(1, 1920 / Math.max(img.width, img.height)); + canvas.width = img.width * scale; + canvas.height = img.height * scale; + + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + + canvas.toBlob((blob) => { + resolve(new File([blob!], file.name, { type: file.type })); + }, file.type, quality); + }; + + img.src = URL.createObjectURL(file); + }); +} + +// 文本文件压缩 +async function compressTextFile(file: File): Promise { + const text = await file.text(); + const compressed = await this.compressString(text); + return new File([compressed], file.name + '.gz', { type: 'application/gzip' }); +}` + }, + { + title: '文件分割上传', + code: ` +// 分割大文件 +async function splitAndUpload(file: File, chunkSize = 5 * 1024 * 1024): Promise { + const chunks: string[] = []; + const totalChunks = Math.ceil(file.size / chunkSize); + + for (let i = 0; i < totalChunks; i++) { + const start = i * chunkSize; + const end = Math.min(start + chunkSize, file.size); + const chunk = file.slice(start, end); + + const chunkFile = new File([chunk], \`\${file.name}.part\${i}\`); + const result = await uploadSingleFile(chunkFile); + chunks.push(result.blobId); + } + + return chunks; +} + +// 创建清单文件记录分割信息 +async function createManifest(chunks: string[], originalFile: File): Promise { + const manifest = { + originalName: originalFile.name, + originalSize: originalFile.size, + chunks: chunks.map((blobId, index) => ({ + blobId, + index, + size: originalFile.size / chunks.length + })) + }; + + const manifestBlob = new Blob([JSON.stringify(manifest)], { type: 'application/json' }); + const manifestFile = new File([manifestBlob], \`\${originalFile.name}.manifest\`); + + const result = await uploadSingleFile(manifestFile); + return result.blobId; +}` + } + ], + prevention: [ + '预先检查文件大小', + '实施文件大小限制', + '提供文件压缩工具' + ] + }; + } +} +``` + +### 2. 网络连接错误 + +#### 错误类型: Network Error +```typescript +// 错误示例 +Error: Network timeout +Error: Connection refused +Error: Failed to fetch + +class NetworkErrorDiagnostics { + async diagnoseNetworkError(error: Error, context: NetworkContext): Promise { + const networkStatus = await this.checkNetworkStatus(); + + return { + errorType: 'NETWORK_ERROR', + description: '网络连接问题', + causes: [ + '网络连接不稳定', + 'RPC 节点不可用', + '防火墙或代理阻止连接', + 'DNS 解析失败' + ], + solutions: [ + { + title: '实施自动重试机制', + code: ` +class RobustWalrusClient { + private readonly MAX_RETRIES = 3; + private readonly RETRY_DELAY_BASE = 1000; + + async withRetry( + operation: () => Promise, + operationName: string + ): Promise { + for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) { + try { + return await operation(); + } catch (error) { + if (!this.isRetryableError(error) || attempt === this.MAX_RETRIES) { + throw new Error(\`\${operationName} failed after \${attempt} attempts: \${error.message}\`); + } + + const delay = this.RETRY_DELAY_BASE * Math.pow(2, attempt - 1); + console.warn(\`\${operationName} failed (attempt \${attempt}/\${this.MAX_RETRIES}), retrying in \${delay}ms\`); + + await this.sleep(delay); + } + } + + throw new Error('Should not reach here'); + } + + private isRetryableError(error: any): boolean { + return error instanceof RetryableWalrusClientError || + error.message.includes('timeout') || + error.message.includes('network') || + error.message.includes('connection') || + error.status >= 500; // Server errors + } + + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + async uploadFiles(files: WalrusFile[]): Promise { + return this.withRetry( + () => this.client.walrus.writeFiles({ files, epochs: 10, signer: this.signer }), + 'File upload' + ); + } +}` + }, + { + title: '实施节点健康检查', + code: ` +// 节点健康检查 +class NodeHealthChecker { + private nodes: string[] = [ + 'https://fullnode.testnet.sui.io', + 'https://fullnode.testnet.sui.io:443', + // 添加备用节点 + ]; + + async getHealthyNode(): Promise { + for (const node of this.nodes) { + try { + const response = await fetch(\`\${node}/\`, { + method: 'HEAD', + timeout: 5000 + }); + + if (response.ok) { + console.log(\`Node \${node} is healthy\`); + return node; + } + } catch (error) { + console.warn(\`Node \${node} is unhealthy: \${error.message}\`); + } + } + + throw new Error('No healthy nodes available'); + } + + async testAllNodes(): Promise { + const results = await Promise.allSettled( + this.nodes.map(async (node) => { + const startTime = Date.now(); + try { + const response = await fetch(\`\${node}/\`, { method: 'HEAD', timeout: 5000 }); + return { + node, + healthy: response.ok, + responseTime: Date.now() - startTime, + error: null + }; + } catch (error) { + return { + node, + healthy: false, + responseTime: Date.now() - startTime, + error: error.message + }; + } + }) + ); + + return results.map(result => + result.status === 'fulfilled' ? result.value : null + ).filter(Boolean) as NodeHealth[]; + } +}` + }, + { + title: '配置超时和重试', + code: ` +// 配置自定义超时和重试 +const client = new SuiJsonRpcClient({ + url: getFullnodeUrl('testnet'), + network: 'testnet', +}).$extend(walrus({ + storageNodeClientOptions: { + timeout: 60000, // 60秒超时 + retryAttempts: 5, + retryDelay: 2000, + onError: (error) => { + console.error('Storage node error:', error); + // 发送错误到监控系统 + this.trackError('storage_node_error', error); + } + } +}));` + } + ], + prevention: [ + '实施网络监控', + '使用多个备用节点', + '设置合理的超时时间', + '实施断路器模式' + ] + }; + } + + private async checkNetworkStatus(): Promise { + try { + const response = await fetch('https://httpbin.org/ip'); + const ipInfo = await response.json(); + + return { + online: true, + ip: ipInfo.ip, + latency: Date.now() - this.startTime + }; + } catch (error) { + return { + online: false, + error: error.message + }; + } + } +} +``` + +### 3. 配置错误 + +#### 错误类型: Configuration Error +```typescript +// 错误示例 +Error: Invalid network configuration +Error: WASM module failed to load +Error: Invalid signer configuration + +class ConfigurationErrorDiagnostics { + async diagnoseConfigError(error: Error, context: ConfigContext): Promise { + return { + errorType: 'CONFIGURATION_ERROR', + description: '配置问题', + causes: [ + '网络配置错误', + 'WASM 模块路径错误', + '签名者配置无效', + '环境变量缺失' + ], + solutions: [ + { + title: '验证配置完整性', + code: ` +// 配置验证器 +class ConfigValidator { + static validateWalrusConfig(config: any): ValidationResult { + const errors: string[] = []; + + // 验证必需字段 + if (!config.network) { + errors.push('network is required (testnet/mainnet)'); + } + + if (!config.rpcUrl) { + errors.push('rpcUrl is required'); + } + + // 验证网络配置 + if (config.network && !['testnet', 'mainnet'].includes(config.network)) { + errors.push('network must be either "testnet" or "mainnet"'); + } + + // 验证 URL 格式 + if (config.rpcUrl && !this.isValidUrl(config.rpcUrl)) { + errors.push('rpcUrl must be a valid URL'); + } + + // 验证超时配置 + if (config.timeout && (config.timeout < 1000 || config.timeout > 300000)) { + errors.push('timeout must be between 1000ms and 300000ms'); + } + + return { + valid: errors.length === 0, + errors + }; + } + + static async validateEnvironment(): Promise { + const errors: string[] = []; + + // 检查环境变量 + const requiredEnvVars = ['WALRUS_NETWORK', 'SUI_RPC_URL']; + for (const envVar of requiredEnvVars) { + if (!process.env[envVar]) { + errors.push(\`Environment variable \${envVar} is required\`); + } + } + + // 检查 WASM 支持 + if (typeof window !== 'undefined' && !window.WebAssembly) { + errors.push('WebAssembly is not supported in this browser'); + } + + // 验证网络连接 + try { + const response = await fetch(process.env.SUI_RPC_URL!, { + method: 'HEAD', + timeout: 5000 + }); + if (!response.ok) { + errors.push('Cannot connect to SUI RPC endpoint'); + } + } catch (error) { + errors.push(\`Network connection failed: \${error.message}\`); + } + + return { + valid: errors.length === 0, + errors + }; + } + + private static isValidUrl(string: string): boolean { + try { + new URL(string); + return true; + } catch { + return false; + } + } +} + +// 使用配置验证 +const config = { + network: 'testnet', + rpcUrl: getFullnodeUrl('testnet'), + timeout: 30000 +}; + +const validation = ConfigValidator.validateWalrusConfig(config); +if (!validation.valid) { + console.error('Configuration validation failed:', validation.errors); + throw new Error('Invalid configuration'); +}` + }, + { + title: 'WASM 模块配置', + code: ` +// WASM 配置验证和加载 +class WasmLoader { + static async loadWasmWithFallback(): Promise { + const strategies = [ + () => this.loadFromBundle(), + () => this.loadFromCDN(), + () => this.loadFromLocal() + ]; + + for (const strategy of strategies) { + try { + const wasm = await strategy(); + console.log('WASM loaded successfully'); + return wasm; + } catch (error) { + console.warn('WASM loading strategy failed:', error.message); + } + } + + throw new Error('All WASM loading strategies failed'); + } + + private static async loadFromBundle(): Promise { + // Vite 示例 + if (typeof import.meta !== 'undefined' && import.meta.url) { + const wasmUrl = new URL('@mysten/walrus-wasm/web/walrus_wasm_bg.wasm', import.meta.url); + const wasmModule = await import(wasmUrl); + return wasmModule.default; + } + throw new Error('Bundle loading not supported'); + } + + private static async loadFromCDN(): Promise { + const cdnUrls = [ + 'https://unpkg.com/@mysten/walrus-wasm@latest/web/walrus_wasm_bg.wasm', + 'https://cdn.jsdelivr.net/npm/@mysten/walrus-wasm@latest/web/walrus_wasm_bg.wasm' + ]; + + for (const url of cdnUrls) { + try { + const response = await fetch(url); + if (response.ok) { + const wasmBuffer = await response.arrayBuffer(); + return WebAssembly.compile(wasmBuffer); + } + } catch (error) { + console.warn(\`CDN loading failed for \${url}:\`, error.message); + } + } + + throw new Error('All CDN URLs failed'); + } + + private static async loadFromLocal(): Promise { + // 尝试从本地路径加载 + const localPaths = [ + '/walrus/walrus_wasm_bg.wasm', + './node_modules/@mysten/walrus-wasm/web/walrus_wasm_bg.wasm' + ]; + + for (const path of localPaths) { + try { + const response = await fetch(path); + if (response.ok) { + const wasmBuffer = await response.arrayBuffer(); + return WebAssembly.compile(wasmBuffer); + } + } catch (error) { + console.warn(\`Local loading failed for \${path}:\`, error.message); + } + } + + throw new Error('Local loading failed'); + } +} + +// 在客户端配置中使用 WASM +const client = new SuiJsonRpcClient({ + url: getFullnodeUrl('testnet'), + network: 'testnet', +}).$extend( + walrus({ + wasmUrl: await WasmLoader.loadWasmWithFallback() + }) +);` + } + ], + prevention: [ + '实施配置验证', + '提供配置模板', + '设置默认值', + '实施配置监控' + ] + }; + } +} +``` + +### 4. 浏览器环境特定错误 + +#### 错误类型: Browser Environment Error +```typescript +class BrowserErrorDiagnostics { + async diagnoseBrowserError(error: Error, context: BrowserContext): Promise { + return { + errorType: 'BROWSER_ERROR', + description: '浏览器环境特定问题', + causes: [ + 'WASM 不支持', + 'CORS 策略阻止', + '内存不足', + '钱包连接问题' + ], + solutions: [ + { + title: '浏览器兼容性检查', + code: ` +// 浏览器兼容性检查 +class BrowserCompatibility { + static checkCompatibility(): BrowserCompatibilityResult { + const checks = { + webAssembly: this.checkWebAssembly(), + fetch: this.checkFetch(), + blob: this.checkBlob(), + fileAPI: this.checkFileAPI(), + webWorkers: this.checkWebWorkers(), + localStorage: this.checkLocalStorage() + }; + + const supported = Object.values(checks).every(check => check.supported); + + return { + supported, + checks, + recommendations: this.getRecommendations(checks) + }; + } + + private static checkWebAssembly(): CompatibilityCheck { + const supported = typeof WebAssembly === 'object' && WebAssembly !== null; + return { + supported, + message: supported ? 'WebAssembly is supported' : 'WebAssembly is not supported', + fix: !supported ? 'Please use a modern browser that supports WebAssembly' : null + }; + } + + private static getRecommendations(checks: Record): string[] { + const recommendations: string[] = []; + + if (!checks.webAssembly.supported) { + recommendations.push('Upgrade to a modern browser (Chrome 57+, Firefox 52+, Safari 11+)'); + } + + if (!checks.webWorkers.supported) { + recommendations.push('Consider enabling Web Workers for better performance'); + } + + return recommendations; + } +} + +// 使用兼容性检查 +const compatibility = BrowserCompatibility.checkCompatibility(); +if (!compatibility.supported) { + console.error('Browser compatibility issues:', compatibility.recommendations); + // 显示用户友好的错误信息 + this.showCompatibilityError(compatibility); +}` + }, + { + title: '内存管理', + code: ` +// 浏览器内存管理 +class BrowserMemoryManager { + private memoryThreshold = 100 * 1024 * 1024; // 100MB + + async checkMemoryUsage(): Promise { + if ('memory' in performance) { + const memory = (performance as any).memory; + return { + used: memory.usedJSHeapSize, + total: memory.totalJSHeapSize, + limit: memory.jsHeapSizeLimit, + usageRatio: memory.usedJSHeapSize / memory.jsHeapSizeLimit + }; + } + return { used: 0, total: 0, limit: 0, usageRatio: 0 }; + } + + async manageMemoryForLargeFile(file: File): Promise { + const memoryUsage = await this.checkMemoryUsage(); + + // 检查是否有足够内存处理文件 + const estimatedUsage = file.size * 3; // 估算处理过程中需要的内存 + const availableMemory = memoryUsage.limit - memoryUsage.used; + + if (estimatedUsage > availableMemory) { + console.warn('Insufficient memory for file processing'); + + // 触发垃圾回收(如果可能) + if (window.gc) { + window.gc(); + } + + // 重新检查内存 + const newUsage = await this.checkMemoryUsage(); + return (file.size * 3) < (newUsage.limit - newUsage.used); + } + + return true; + } + + // 流式处理大文件 + async processLargeFileInChunks( + file: File, + processor: (chunk: Uint8Array) => Promise, + chunkSize = 1024 * 1024 // 1MB + ): Promise { + const reader = new FileReader(); + let offset = 0; + + const processChunk = () => { + if (offset >= file.size) { + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + const chunk = file.slice(offset, offset + chunkSize); + + reader.onload = async (event) => { + try { + if (event.target?.result) { + const arrayBuffer = event.target.result as ArrayBuffer; + const uint8Array = new Uint8Array(arrayBuffer); + + await processor(uint8Array); + offset += chunkSize; + + // 检查内存使用 + const memoryUsage = await this.checkMemoryUsage(); + if (memoryUsage.usageRatio > 0.8) { + console.warn('High memory usage, forcing garbage collection'); + if (window.gc) window.gc(); + } + + await processChunk(); + } + resolve(); + } catch (error) { + reject(error); + } + }; + + reader.onerror = () => reject(new Error('File reading failed')); + reader.readAsArrayBuffer(chunk); + }); + }; + + return processChunk(); + } +}` + } + ], + prevention: [ + '实施浏览器兼容性检查', + '监控内存使用', + '提供降级方案', + '实施优雅错误处理' + ] + }; + } +} +``` + +## 使用方法 + +### 上传错误诊断 +```typescript +skill: "walrus-debugger" +// error-type: upload +// error-context: Upload failed: insufficient storage balance, trying to upload 10MB file to testnet +// environment: browser +``` + +### 网络连接问题 +```typescript +skill: "walrus-debugger" +// error-type: network +// error-context: Network timeout when connecting to storage nodes, intermittent connection issues +// environment: production +``` + +### 配置验证 +```typescript +skill: "walrus-debugger" +// error-type: configuration +// error-context: WASM module failed to load in Vite production build, error: "Failed to resolve module" +// environment: nodejs +``` + +### 性能问题 +```typescript +skill: "walrus-debugger" +// error-type: performance +// error-context: Memory usage spikes when uploading multiple large files, browser crashes +// environment: browser +``` + +--- + +*更新时间:2025-11-11* \ No newline at end of file diff --git a/skills/walrus-monitor.md b/skills/walrus-monitor.md new file mode 100644 index 0000000..896338a --- /dev/null +++ b/skills/walrus-monitor.md @@ -0,0 +1,757 @@ +--- +name: walrus-monitor +description: Walrus 监控分析专家 - 存储使用监控、成本分析和性能指标跟踪 +parameters: + - name: monitor-type + type: string + description: 监控类型 (usage/cost/performance/errors/analytics) + required: true + - name: time-range + type: string + description: 时间范围 (1h/24h/7d/30d/custom) + default: 24h + - name: metrics + type: array + description: 需要监控的具体指标 + default: [] +--- + +# Walrus 监控分析技能 + +## 技能概述 + +`walrus-monitor` 是专业的监控分析助手,提供全面的 Walrus 存储系统监控、成本分析和性能指标跟踪功能,帮助开发者优化资源使用和控制成本。 + +## 监控维度 + +### 1. 存储使用监控 + +#### 实时存储指标 +```typescript +class StorageMonitor { + private metrics: StorageMetrics[] = []; + private alerts: Alert[] = []; + + async collectStorageMetrics(): Promise { + const client = await this.getWalrusClient(); + const accountAddress = this.getAccountAddress(); + + // 获取账户存储信息 + const storageInfo = await client.getStorageInfo(accountAddress); + const blobIds = storageInfo.storedBlobs || []; + + // 计算存储统计 + const metrics: StorageMetrics = { + timestamp: Date.now(), + totalFiles: blobIds.length, + totalSize: 0, + totalCost: 0, + storageDistribution: {}, + fileAges: [], + oldestFile: null, + newestFile: null, + expiringSoon: [], + expired: [] + }; + + // 分析每个文件 + for (const blobId of blobIds) { + try { + const [file] = await client.walrus.getFiles({ ids: [blobId] }); + const metadata = await this.getFileMetadata(file); + const storedUntil = await file.storedUntil(); + + metrics.totalSize += metadata.size; + metrics.totalCost += metadata.estimatedCost; + + // 文件类型分布 + const fileType = this.getFileType(metadata.mimeType); + metrics.storageDistribution[fileType] = + (metrics.storageDistribution[fileType] || 0) + 1; + + // 文件年龄分析 + const age = Date.now() - metadata.uploadTime; + metrics.fileAges.push(age); + + // 检查过期状态 + const daysToExpiry = Math.floor((storedUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24)); + if (daysToExpiry < 7) { + metrics.expiringSoon.push({ + blobId, + fileName: metadata.identifier, + daysToExpiry, + size: metadata.size + }); + } + + if (daysToExpiry < 0) { + metrics.expired.push({ + blobId, + fileName: metadata.identifier, + daysExpired: Math.abs(daysToExpiry), + size: metadata.size + }); + } + + // 更新最新和最旧文件 + if (!metrics.oldestFile || age > metrics.oldestFile.age) { + metrics.oldestFile = { + blobId, + fileName: metadata.identifier, + age, + uploadDate: new Date(metadata.uploadTime) + }; + } + + if (!metrics.newestFile || age < metrics.newestFile.age) { + metrics.newestFile = { + blobId, + fileName: metadata.identifier, + age, + uploadDate: new Date(metadata.uploadTime) + }; + } + + } catch (error) { + console.warn(`Failed to analyze blob ${blobId}:`, error.message); + } + } + + // 存储指标历史 + this.metrics.push(metrics); + if (this.metrics.length > 1000) { + this.metrics.shift(); // 保留最近1000条记录 + } + + // 检查告警条件 + this.checkStorageAlerts(metrics); + + return metrics; + } + + private checkStorageAlerts(metrics: StorageMetrics): void { + // 存储空间告警 + if (metrics.totalSize > 100 * 1024 * 1024 * 1024) { // 100GB + this.createAlert({ + type: 'STORAGE_LIMIT', + severity: 'WARNING', + message: `存储使用量达到 ${this.formatBytes(metrics.totalSize)}`, + recommendation: '考虑清理过期文件或升级存储计划' + }); + } + + // 文件数量告警 + if (metrics.totalFiles > 10000) { + this.createAlert({ + type: 'FILE_COUNT_LIMIT', + severity: 'INFO', + message: `文件数量达到 ${metrics.totalFiles}`, + recommendation: '考虑实施文件归档策略' + }); + } + + // 即将过期告警 + if (metrics.expiringSoon.length > 0) { + this.createAlert({ + type: 'EXPIRING_FILES', + severity: 'WARNING', + message: `${metrics.expiringSoon.length} 个文件将在7天内过期`, + recommendation: '续期或备份重要文件' + }); + } + } + + getStorageTrends(timeRange: number = 7 * 24 * 60 * 60 * 1000): StorageTrends { + const cutoff = Date.now() - timeRange; + const relevantMetrics = this.metrics.filter(m => m.timestamp >= cutoff); + + if (relevantMetrics.length < 2) { + return { + growthRate: 0, + costGrowthRate: 0, + fileGrowthRate: 0, + projectedUsage: null, + projectedCost: null + }; + } + + const oldest = relevantMetrics[0]; + const newest = relevantMetrics[relevantMetrics.length - 1]; + const timeDiff = newest.timestamp - oldest.timestamp; + + return { + growthRate: this.calculateGrowthRate(oldest.totalSize, newest.totalSize, timeDiff), + costGrowthRate: this.calculateGrowthRate(oldest.totalCost, newest.totalCost, timeDiff), + fileGrowthRate: this.calculateGrowthRate(oldest.totalFiles, newest.totalFiles, timeDiff), + projectedUsage: this.projectUsage(newest.totalSize, this.calculateGrowthRate(oldest.totalSize, newest.totalSize, timeDiff)), + projectedCost: this.projectCost(newest.totalCost, this.calculateGrowthRate(oldest.totalCost, newest.totalCost, timeDiff)) + }; + } +} +``` + +### 2. 成本分析 + +#### 成本监控和优化 +```typescript +class CostAnalyzer { + private costHistory: CostRecord[] = []; + private pricingModel: PricingModel; + + constructor() { + this.pricingModel = { + storagePerGBPerEpoch: 0.001, + writeFeePerMB: 0.0001, + readFeePerMB: 0.00001, + gasMultiplier: 1.0 + }; + } + + async analyzeCosts(timeRange: number = 30 * 24 * 60 * 60 * 1000): Promise { + const records = this.getRelevantCostRecords(timeRange); + const storageMetrics = await this.getStorageMetrics(timeRange); + + return { + totalCost: this.calculateTotalCost(records), + storageCost: this.calculateStorageCost(storageMetrics), + transactionCost: this.calculateTransactionCost(records), + breakdown: this.getCostBreakdown(records), + optimization: this.getOptimizationSuggestions(storageMetrics, records), + forecast: this.forecastCosts(records, storageMetrics) + }; + } + + private calculateStorageCost(metrics: StorageMetrics[]): number { + return metrics.reduce((total, metric) => { + const storageEpochs = 30; // 假设30天的轮数 + const sizeGB = metric.totalSize / (1024 * 1024 * 1024); + return total + (sizeGB * this.pricingModel.storagePerGBPerEpoch * storageEpochs); + }, 0); + } + + private calculateTransactionCost(records: CostRecord[]): number { + return records + .filter(r => r.type === 'transaction') + .reduce((total, record) => total + record.amount, 0); + } + + private getOptimizationSuggestions(metrics: StorageMetrics[], records: CostRecord[]): OptimizationSuggestion[] { + const suggestions: OptimizationSuggestion[] = []; + + // 分析文件大小分布 + const sizeDistribution = this.analyzeSizeDistribution(metrics); + if (sizeDistribution.smallFiles > sizeDistribution.largeFiles * 2) { + suggestions.push({ + type: 'BUNDLE_SMALL_FILES', + description: '检测到大量小文件,建议使用 Quilt 打包存储', + potentialSavings: '20-30%', + difficulty: 'medium', + implementation: this.getBundleImplementation() + }); + } + + // 分析存储时长 + const avgStorageTime = this.calculateAverageStorageTime(metrics); + if (avgStorageTime > 90) { // 超过90天 + suggestions.push({ + type: 'OPTIMIZE_STORAGE_DURATION', + description: `平均存储时长 ${avgStorageTime} 天,建议实施生命周期管理`, + potentialSavings: '40-60%', + difficulty: 'low', + implementation: this.getLifecycleImplementation() + }); + } + + // 分析访问模式 + const accessPattern = this.analyzeAccessPattern(records); + if (accessPattern.readHeavy) { + suggestions.push({ + type: 'IMPLEMENT_CACHING', + description: '读取频繁,建议实施缓存层', + potentialSavings: '50-70%', + difficulty: 'medium', + implementation: this.getCacheImplementation() + }); + } + + return suggestions; + } + + private getBundleImplementation(): string { + return ` +// 使用 Quilt 打包小文件 +async function bundleSmallFiles(files: WalrusFile[]): Promise { + const quilt = QuiltBuilder.create() + .addFiles(files) + .build(); + + const result = await client.walrus.writeFiles({ + files: [quilt], + epochs: 30, + deletable: true, + signer: keypair + }); + + return result[0].blobId; +} + +// 原本多个小文件: +// File 1: 10KB, File 2: 15KB, File 3: 8KB = 33KB 总计 +// 打包后:单个 Quilt 包含所有文件,减少存储开销 + `; + } + + private getLifecycleImplementation(): string { + return ` +// 自动生命周期管理 +class StorageLifecycleManager { + async manageExpiredFiles() { + const expiringFiles = await this.getFilesExpiringWithin(7); + + for (const file of expiringFiles) { + const accessFrequency = await this.getAccessFrequency(file.blobId); + + if (accessFrequency === 'never') { + // 删除未访问的文件 + await this.deleteFile(file.blobId); + } else if (accessFrequency === 'rare') { + // 降低存储时长 + await this.reduceStorageEpochs(file.blobId, 10); + } else { + // 续期重要文件 + await this.extendStorage(file.blobId, 30); + } + } + } + + private async getAccessFrequency(blobId: string): Promise<'never' | 'rare' | 'frequent'> { + // 分析访问日志 + const accessLogs = await this.getAccessLogs(blobId, 30); // 30天内 + const accessCount = accessLogs.length; + + if (accessCount === 0) return 'never'; + if (accessCount < 5) return 'rare'; + return 'frequent'; + } +} + `; + } +} +``` + +### 3. 性能监控 + +#### 实时性能指标 +```typescript +class PerformanceMonitor { + private performanceMetrics: PerformanceMetric[] = []; + private slowQueries: SlowQuery[] = []; + private alerts: PerformanceAlert[] = []; + + async recordPerformanceMetric( + operation: string, + duration: number, + success: boolean, + metadata: any = {} + ): Promise { + const metric: PerformanceMetric = { + timestamp: Date.now(), + operation, + duration, + success, + metadata + }; + + this.performanceMetrics.push(metric); + + // 限制历史数据 + if (this.performanceMetrics.length > 10000) { + this.performanceMetrics.shift(); + } + + // 检查性能告警 + this.checkPerformanceAlerts(metric); + + // 记录慢操作 + if (duration > this.getSlowThreshold(operation)) { + this.slowQueries.push({ + ...metric, + threshold: this.getSlowThreshold(operation), + severity: this.getSeverityLevel(duration, this.getSlowThreshold(operation)) + }); + + // 限制慢查询记录 + if (this.slowQueries.length > 1000) { + this.slowQueries.shift(); + } + } + } + + getPerformanceAnalysis(timeRange: number = 24 * 60 * 60 * 1000): PerformanceAnalysis { + const metrics = this.getRelevantMetrics(timeRange); + + return { + overview: this.getPerformanceOverview(metrics), + operations: this.analyzeOperations(metrics), + trends: this.analyzeTrends(metrics), + bottlenecks: this.identifyBottlenecks(metrics), + recommendations: this.getPerformanceRecommendations(metrics) + }; + } + + private analyzeOperations(metrics: PerformanceMetric[]): OperationAnalysis[] { + const operationGroups = this.groupBy(metrics, 'operation'); + const analyses: OperationAnalysis[] = []; + + for (const [operation, operationMetrics] of Object.entries(operationGroups)) { + const durations = operationMetrics.map(m => m.duration); + const successRate = operationMetrics.filter(m => m.success).length / operationMetrics.length; + + analyses.push({ + operation, + totalCalls: operationMetrics.length, + successRate, + avgDuration: durations.reduce((a, b) => a + b, 0) / durations.length, + minDuration: Math.min(...durations), + maxDuration: Math.max(...durations), + p95: this.percentile(durations, 0.95), + p99: this.percentile(durations, 0.99), + errorRate: 1 - successRate, + slowQueries: operationMetrics.filter(m => m.duration > this.getSlowThreshold(operation)).length + }); + } + + return analyses.sort((a, b) => b.avgDuration - a.avgDuration); + } + + private identifyBottlenecks(metrics: PerformanceMetric[]): PerformanceBottleneck[] { + const bottlenecks: PerformanceBottleneck[] = []; + + // 识别慢操作 + const slowOperations = this.analyzeOperations(metrics) + .filter(op => op.avgDuration > this.getSlowThreshold(op.operation)); + + for (const operation of slowOperations) { + bottlenecks.push({ + type: 'SLOW_OPERATION', + operation: operation.operation, + severity: this.getBottleneckSeverity(operation.avgDuration, this.getSlowThreshold(operation.operation)), + impact: 'medium', + description: `\${operation.operation} 平均耗时 \${operation.avgDuration}ms`, + suggestion: this.getOptimizationSuggestion(operation.operation) + }); + } + + // 识别错误率高的操作 + const highErrorOps = this.analyzeOperations(metrics) + .filter(op => op.errorRate > 0.05); // 5% 错误率 + + for (const operation of highErrorOps) { + bottlenecks.push({ + type: 'HIGH_ERROR_RATE', + operation: operation.operation, + severity: 'high', + impact: 'high', + description: `\${operation.operation} 错误率 \${(operation.errorRate * 100).toFixed(1)}%`, + suggestion: this.getErrorOptimizationSuggestion(operation.operation) + }); + } + + return bottlenecks; + } + + private getPerformanceRecommendations(metrics: PerformanceMetric[]): PerformanceRecommendation[] { + const recommendations: PerformanceRecommendation[] = []; + + // 分析上传性能 + const uploadMetrics = metrics.filter(m => m.operation.startsWith('upload')); + if (uploadMetrics.length > 0) { + const avgUploadSize = this.getAverageUploadSize(uploadMetrics); + const avgUploadTime = uploadMetrics.reduce((sum, m) => sum + m.duration, 0) / uploadMetrics.length; + + if (avgUploadSize > 10 * 1024 * 1024 && avgUploadTime > 10000) { // 10MB, 10s + recommendations.push({ + type: 'UPLOAD_OPTIMIZATION', + priority: 'high', + title: '大文件上传优化', + description: '检测到大文件上传性能问题', + implementation: ` +// 实施分块上传和断点续传 +class ChunkedUploader { + async uploadLargeFile(file: File, chunkSize = 5 * 1024 * 1024): Promise { + const chunks = Math.ceil(file.size / chunkSize); + const chunkIds: string[] = []; + + for (let i = 0; i < chunks; i++) { + const start = i * chunkSize; + const end = Math.min(start + chunkSize, file.size); + const chunk = file.slice(start, end); + + const result = await this.uploadChunk(chunk, i, chunks); + chunkIds.push(result.blobId); + + // 更新进度 + this.updateProgress((i + 1) / chunks); + } + + // 创建文件清单 + return await this.createManifest(chunkIds, file); + } +} + +// 使用压缩减少传输量 +const compressedFile = await this.compressFile(file); +const uploadResult = await client.walrus.writeFiles({ + files: [compressedFile], + epochs: 10, + signer: keypair +}); + `, + expectedImprovement: '50-70%' + }); + } + } + + // 分析批量操作性能 + const batchMetrics = metrics.filter(m => m.operation.includes('batch')); + if (batchMetrics.length > 0) { + const concurrencyIssues = batchMetrics.filter(m => m.duration > 60000).length; // 超过1分钟 + + if (concurrencyIssues > batchMetrics.length * 0.3) { // 30%超时 + recommendations.push({ + type: 'BATCH_OPTIMIZATION', + priority: 'medium', + title: '批量操作并发优化', + description: '批量操作存在并发瓶颈', + implementation: ` +// 优化并发控制 +class OptimizedBatchProcessor { + private readonly MAX_CONCURRENT = 3; + private readonly RETRY_DELAY = 1000; + + async processBatch(items: T[], processor: (item: T) => Promise): Promise { + const results: any[] = []; + const chunks = this.chunkArray(items, this.MAX_CONCURRENT); + + for (const chunk of chunks) { + const chunkPromises = chunk.map(async (item, index) => { + try { + return await this.withRetry(() => processor(item), 3); + } catch (error) { + console.error(\`Batch item failed: \${error.message}\`); + return null; + } + }); + + const chunkResults = await Promise.all(chunkPromises); + results.push(...chunkResults); + + // 批次间延迟 + if (chunks.indexOf(chunk) < chunks.length - 1) { + await this.delay(this.RETRY_DELAY); + } + } + + return results; + } +} + `, + expectedImprovement: '30-50%' + }); + } + } + + return recommendations; + } +} +``` + +### 4. 错误监控 + +#### 错误统计和分析 +```typescript +class ErrorMonitor { + private errorLog: ErrorRecord[] = []; + private errorPatterns: ErrorPattern[] = []; + + recordError(error: Error, context: ErrorContext): void { + const errorRecord: ErrorRecord = { + timestamp: Date.now(), + message: error.message, + stack: error.stack, + type: this.classifyError(error), + severity: this.determineSeverity(error, context), + context, + resolved: false + }; + + this.errorLog.push(errorRecord); + + // 分析错误模式 + this.analyzeErrorPattern(errorRecord); + + // 错误告警 + this.checkErrorAlerts(errorRecord); + } + + getErrorAnalysis(timeRange: number = 24 * 60 * 60 * 1000): ErrorAnalysis { + const recentErrors = this.getRecentErrors(timeRange); + + return { + summary: this.getErrorSummary(recentErrors), + trends: this.getErrorTrends(recentErrors), + patterns: this.getErrorPatterns(recentErrors), + recommendations: this.getErrorRecommendations(recentErrors), + resolutionRate: this.calculateResolutionRate(recentErrors) + }; + } + + private analyzeErrorPattern(errorRecord: ErrorRecord): void { + const patternKey = this.getPatternKey(errorRecord); + let pattern = this.errorPatterns.find(p => p.key === patternKey); + + if (!pattern) { + pattern = { + key: patternKey, + type: errorRecord.type, + count: 0, + firstOccurrence: errorRecord.timestamp, + lastOccurrence: errorRecord.timestamp, + contexts: [], + resolutions: [] + }; + this.errorPatterns.push(pattern); + } + + pattern.count++; + pattern.lastOccurrence = errorRecord.timestamp; + + // 记录上下文 + const contextHash = this.hashContext(errorRecord.context); + if (!pattern.contexts.some(c => c.hash === contextHash)) { + pattern.contexts.push({ + hash: contextHash, + context: errorRecord.context, + occurrences: 1 + }); + } else { + const existingContext = pattern.contexts.find(c => c.hash === contextHash); + if (existingContext) { + existingContext.occurrences++; + } + } + } + + private getErrorRecommendations(errors: ErrorRecord[]): ErrorRecommendation[] { + const recommendations: ErrorRecommendation[] = []; + + // 分析最常见的错误类型 + const errorTypes = this.groupBy(errors, 'type'); + const mostCommonType = Object.entries(errorTypes) + .sort(([, a], [, b]) => b.length - a.length)[0]; + + if (mostCommonType && mostCommonType[1].length > errors.length * 0.3) { // 30%以上 + recommendations.push({ + type: 'PREVENTION', + priority: 'high', + title: `高频率 \${mostCommonType[0]} 错误`, + description: `\${mostCommonType[1].length} 次错误中 \${mostCommonType[1].length} 次是 \${mostCommonType[0]} 错误`, + implementation: this.getPreventionImplementation(mostCommonType[0]), + expectedReduction: '60-80%' + }); + } + + // 分析网络相关错误 + const networkErrors = errors.filter(e => e.type === 'network' || e.type === 'timeout'); + if (networkErrors.length > errors.length * 0.2) { // 20%以上 + recommendations.push({ + type: 'NETWORK_OPTIMIZATION', + priority: 'medium', + title: '网络连接优化', + description: '检测到大量网络相关错误', + implementation: ` +// 实施网络优化策略 +class NetworkOptimizer { + private readonly circuitBreaker = new CircuitBreaker({ + failureThreshold: 5, + recoveryTimeout: 60000, + monitoringPeriod: 30000 + }); + + async robustRequest(url: string, options: RequestInit = {}): Promise { + return this.circuitBreaker.execute(async () => { + // 使用指数退避重试 + return this.withRetry(() => fetch(url, { + ...options, + timeout: 30000 + }), 3); + }); + } + + private async withRetry( + operation: () => Promise, + maxRetries: number = 3 + ): Promise { + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + return await operation(); + } catch (error) { + if (attempt === maxRetries || !this.isRetryableError(error)) { + throw error; + } + + const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000); + await this.delay(delay); + } + } + + throw new Error('Max retries exceeded'); + } +} + `, + expectedReduction: '70-90%' + }); + } + + return recommendations; + } +} +``` + +## 使用方法 + +### 存储使用监控 +```typescript +skill: "walrus-monitor" +// monitor-type: usage +// time-range: 7d +// metrics: ["total-size", "file-count", "cost", "aging"] +``` + +### 成本分析 +```typescript +skill: "walrus-monitor" +// monitor-type: cost +// time-range: 30d +// metrics: ["storage-cost", "transaction-cost", "optimization-suggestions"] +``` + +### 性能监控 +```typescript +skill: "walrus-monitor" +// monitor-type: performance +// time-range: 24h +// metrics: ["upload-speed", "download-speed", "error-rate", "bottlenecks"] +``` + +### 错误分析 +```typescript +skill: "walrus-monitor" +// monitor-type: errors +// time-range: 7d +// metrics: ["error-patterns", "resolution-rate", "prevention-suggestions"] +``` + +--- + +*更新时间:2025-11-11* \ No newline at end of file diff --git a/skills/walrus-performance-optimizer.md b/skills/walrus-performance-optimizer.md new file mode 100644 index 0000000..b4d6cfe --- /dev/null +++ b/skills/walrus-performance-optimizer.md @@ -0,0 +1,761 @@ +--- +name: walrus-performance-optimizer +description: Walrus 性能优化专家 - 分析和优化 Walrus 应用的性能瓶颈 +parameters: + - name: analysis-type + type: string + description: 分析类型 (upload/download/batch/memory/network) + required: true + - name: target-metric + type: string + description: 目标指标 (speed/throughput/cost/latency) + default: speed + - name: code-context + type: string + description: 代码上下文或问题描述 + required: true +--- + +# Walrus 性能优化技能 + +## 技能概述 + +`walrus-performance-optimizer` 是专业的性能优化助手,专门分析 Walrus 应用的性能瓶颈,提供数据驱动的优化建议和具体的实施方案。 + +## 性能分析维度 + +### 1. 上传性能优化 + +#### 批量操作优化 +```typescript +// 优化前:逐个上传文件 +async function uploadFilesSlow(files: File[]) { + const results = []; + for (const file of files) { + const result = await uploadSingleFile(file); + results.push(result); + } + return results; +} + +// 优化后:批量并发上传 +class OptimizedUploader { + private readonly MAX_CONCURRENT = 5; + private readonly BATCH_DELAY = 100; + + async uploadFilesOptimized(files: File[]): Promise { + const results: UploadResult[] = []; + const chunks = this.chunkArray(files, this.MAX_CONCURRENT); + + for (const chunk of chunks) { + const chunkPromises = chunk.map(file => this.uploadWithRetry(file)); + const chunkResults = await Promise.allSettled(chunkPromises); + + results.push(...this.processChunkResults(chunkResults)); + + // 批次间延迟,避免过载 + if (chunks.indexOf(chunk) < chunks.length - 1) { + await this.delay(this.BATCH_DELAY); + } + } + + return results; + } + + private async uploadWithRetry(file: File, maxRetries = 3): Promise { + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + return await this.uploadSingleFile(file); + } catch (error) { + if (attempt === maxRetries || !this.isRetryableError(error)) { + throw error; + } + + // 指数退避 + const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000); + await this.delay(delay); + } + } + } + + private chunkArray(array: T[], size: number): T[][] { + const chunks: T[][] = []; + for (let i = 0; i < array.length; i += size) { + chunks.push(array.slice(i, i + size)); + } + return chunks; + } + + private isRetryableError(error: any): boolean { + return error instanceof RetryableWalrusClientError || + error.message.includes('timeout') || + error.message.includes('network'); + } +} +``` + +#### 文件压缩优化 +```typescript +class CompressionOptimizer { + async optimizeUpload(file: File): Promise { + const fileBuffer = await file.arrayBuffer(); + const originalSize = fileBuffer.byteLength; + + // 文本文件压缩 + if (this.isTextFile(file)) { + const compressed = await this.compressText(fileBuffer); + if (compressed.size < originalSize * 0.8) { + return { + data: compressed.data, + originalSize, + compressedSize: compressed.size, + compressionRatio: compressed.size / originalSize, + method: 'gzip' + }; + } + } + + // 图片文件优化 + if (this.isImageFile(file)) { + const optimized = await this.optimizeImage(fileBuffer); + if (optimized.size < originalSize * 0.9) { + return { + data: optimized.data, + originalSize, + compressedSize: optimized.size, + compressionRatio: optimized.size / originalSize, + method: 'image-optimization' + }; + } + } + + return { + data: new Uint8Array(fileBuffer), + originalSize, + compressedSize: originalSize, + compressionRatio: 1, + method: 'none' + }; + } + + private async compressText(data: ArrayBuffer): Promise { + const compressionStream = new CompressionStream('gzip'); + const writer = compressionStream.writable.getWriter(); + const reader = compressionStream.readable.getReader(); + + writer.write(new Uint8Array(data)); + writer.close(); + + const chunks: Uint8Array[] = []; + let totalSize = 0; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + chunks.push(value); + totalSize += value.length; + } + + const combined = new Uint8Array(totalSize); + let offset = 0; + for (const chunk of chunks) { + combined.set(chunk, offset); + offset += chunk.length; + } + + return { data: combined, size: totalSize }; + } + + private async optimizeImage(data: ArrayBuffer): Promise { + // 使用 WebAssembly 进行图片优化 + // 这里可以集成像 sharp.js 或其他图片处理库 + const blob = new Blob([data], { type: 'image/jpeg' }); + + return new Promise((resolve) => { + const img = new Image(); + img.onload = () => { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d')!; + + // 降低质量但保持合理尺寸 + const scale = Math.min(1, 1920 / Math.max(img.width, img.height)); + canvas.width = img.width * scale; + canvas.height = img.height * scale; + + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + + canvas.toBlob((blob) => { + if (blob) { + blob.arrayBuffer().then(buffer => { + const data = new Uint8Array(buffer); + resolve({ data, size: data.length }); + }); + } + }, 'image/jpeg', 0.85); + }; + img.src = URL.createObjectURL(blob); + }); + } +} +``` + +### 2. 下载性能优化 + +#### 智能缓存系统 +```typescript +class IntelligentCache { + private cache = new Map(); + private accessOrder = new Map(); + private accessCounter = 0; + private readonly MAX_SIZE = 100; // 最大缓存条目 + private readonly TTL = 300000; // 5分钟过期 + + async get(blobId: string): Promise { + const entry = this.cache.get(blobId); + + if (!entry) { + return null; + } + + // 检查是否过期 + if (Date.now() - entry.timestamp > this.TTL) { + this.delete(blobId); + return null; + } + + // 更新访问顺序 (LRU) + this.accessOrder.set(blobId, ++this.accessCounter); + + return entry.data; + } + + async set(blobId: string, data: Uint8Array): Promise { + // 检查缓存大小,必要时清理 + if (this.cache.size >= this.MAX_SIZE) { + this.evictLRU(); + } + + this.cache.set(blobId, { + data, + timestamp: Date.now(), + size: data.length + }); + + this.accessOrder.set(blobId, ++this.accessCounter); + } + + private evictLRU(): void { + let oldestKey = ''; + let oldestAccess = Infinity; + + for (const [key, accessTime] of this.accessOrder) { + if (accessTime < oldestAccess) { + oldestAccess = accessTime; + oldestKey = key; + } + } + + if (oldestKey) { + this.delete(oldestKey); + } + } + + private delete(blobId: string): void { + this.cache.delete(blobId); + this.accessOrder.delete(blobId); + } + + getStats(): CacheStats { + return { + size: this.cache.size, + totalMemoryUsage: Array.from(this.cache.values()) + .reduce((sum, entry) => sum + entry.size, 0), + hitRate: this.hitRate, + evictions: this.evictions + }; + } +} +``` + +#### 预加载策略 +```typescript +class PreloadingStrategy { + private preloadQueue = new Set(); + private preloadCache = new Map(); + + async preloadFiles(blobIds: string[], priority: 'high' | 'medium' | 'low' = 'medium') { + const delay = priority === 'high' ? 0 : priority === 'medium' ? 100 : 500; + + for (const blobId of blobIds) { + if (!this.preloadQueue.has(blobId)) { + this.preloadQueue.add(blobId); + + // 延迟执行,避免阻塞主要操作 + setTimeout(() => this.preloadFile(blobId), delay); + } + } + } + + private async preloadFile(blobId: string) { + try { + const startTime = Date.now(); + + // 使用低优先级下载 + const [file] = await client.walrus.getFiles({ + ids: [blobId], + options: { priority: 'low' } + }); + + const content = await file.bytes(); + const loadTime = Date.now() - startTime; + + this.preloadCache.set(blobId, { + data: content, + timestamp: Date.now(), + loadTime + }); + + console.log(`预加载完成: ${blobId} (${loadTime}ms)`); + + } catch (error) { + console.warn(`预加载失败: ${blobId}`, error.message); + } finally { + this.preloadQueue.delete(blobId); + } + } + + async getPreloaded(blobId: string): Promise { + const entry = this.preloadCache.get(blobId); + + if (!entry) { + return null; + } + + // 检查是否过期 + if (Date.now() - entry.timestamp > 600000) { // 10分钟 + this.preloadCache.delete(blobId); + return null; + } + + return entry.data; + } +} +``` + +### 3. 网络性能优化 + +#### 连接池管理 +```typescript +class ConnectionPool { + private connections: Map = new Map(); + private readonly MAX_CONNECTIONS_PER_HOST = 5; + private readonly CONNECTION_TIMEOUT = 30000; + + async getConnection(host: string): Promise { + const pool = this.connections.get(host) || []; + + // 尝试复用现有连接 + for (const conn of pool) { + if (!conn.inUse && this.isConnectionHealthy(conn)) { + conn.inUse = true; + conn.lastUsed = Date.now(); + return conn; + } + } + + // 创建新连接 + if (pool.length < this.MAX_CONNECTIONS_PER_HOST) { + const newConn = await this.createConnection(host); + newConn.inUse = true; + pool.push(newConn); + this.connections.set(host, pool); + return newConn; + } + + // 等待连接可用 + return this.waitForAvailableConnection(host); + } + + releaseConnection(connection: Connection) { + connection.inUse = false; + connection.lastUsed = Date.now(); + } + + private async createConnection(host: string): Promise { + return new Promise((resolve, reject) => { + const ws = new WebSocket(host); + + const timeout = setTimeout(() => { + reject(new Error('Connection timeout')); + }, this.CONNECTION_TIMEOUT); + + ws.onopen = () => { + clearTimeout(timeout); + resolve({ + socket: ws, + created: Date.now(), + lastUsed: Date.now(), + inUse: true + }); + }; + + ws.onerror = () => { + clearTimeout(timeout); + reject(new Error('Connection failed')); + }; + }); + } + + private isConnectionHealthy(conn: Connection): boolean { + return conn.socket.readyState === WebSocket.OPEN && + Date.now() - conn.lastUsed < 300000; // 5分钟未使用则关闭 + } + + private async waitForAvailableConnection(host: string): Promise { + const pool = this.connections.get(host) || []; + + return new Promise((resolve) => { + const checkInterval = setInterval(() => { + const available = pool.find(conn => !conn.inUse && this.isConnectionHealthy(conn)); + if (available) { + clearInterval(checkInterval); + available.inUse = true; + available.lastUsed = Date.now(); + resolve(available); + } + }, 100); + }); + } +} +``` + +#### 请求优化 +```typescript +class RequestOptimizer { + private pendingRequests = new Map>(); + private requestQueue: QueuedRequest[] = []; + private processing = false; + + async batchRequest(requests: BatchRequest[]): Promise { + // 合并相同的请求 + const uniqueRequests = this.deduplicateRequests(requests); + + // 按优先级排序 + uniqueRequests.sort((a, b) => b.priority - a.priority); + + const results: T[] = []; + + for (const request of uniqueRequests) { + try { + const result = await this.executeRequest(request); + results.push(result); + } catch (error) { + console.error(`批量请求失败: ${request.id}`, error); + results.push(null as T); + } + } + + return results; + } + + private deduplicateRequests(requests: BatchRequest[]): BatchRequest[] { + const seen = new Set(); + return requests.filter(req => { + const key = this.getRequestKey(req); + if (seen.has(key)) { + return false; + } + seen.add(key); + return true; + }); + } + + private async executeRequest(request: BatchRequest): Promise { + const key = this.getRequestKey(request); + + // 检查是否有相同请求正在进行 + if (this.pendingRequests.has(key)) { + return await this.pendingRequests.get(key); + } + + const requestPromise = this.performRequest(request); + this.pendingRequests.set(key, requestPromise); + + try { + const result = await requestPromise; + return result; + } finally { + this.pendingRequests.delete(key); + } + } + + private async performRequest(request: BatchRequest): Promise { + // 实现实际的请求逻辑 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), request.timeout); + + try { + const response = await fetch(request.url, { + ...request.options, + signal: controller.signal + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return await response.json(); + } finally { + clearTimeout(timeoutId); + } + } +} +``` + +### 4. 内存优化 + +#### 内存池管理 +```typescript +class MemoryPool { + private pools = new Map(); + private readonly MAX_POOL_SIZE = 50; + private readonly ALLOC_INCREMENT = 1024; // 1KB increments + + allocate(size: number): ArrayBuffer { + const actualSize = Math.ceil(size / this.ALLOC_INCREMENT) * this.ALLOC_INCREMENT; + const pool = this.pools.get(actualSize) || []; + + if (pool.length > 0) { + return pool.pop()!; + } + + return new ArrayBuffer(actualSize); + } + + release(buffer: ArrayBuffer): void { + const size = buffer.byteLength; + const pool = this.pools.get(size) || []; + + if (pool.length < this.MAX_POOL_SIZE) { + // 清零缓冲区(安全考虑) + new Uint8Array(buffer).fill(0); + pool.push(buffer); + this.pools.set(size, pool); + } + } + + getStats(): MemoryPoolStats { + const stats: MemoryPoolStats = { + totalBuffers: 0, + totalMemory: 0, + poolSizes: {} + }; + + for (const [size, pool] of this.pools) { + stats.totalBuffers += pool.length; + stats.totalMemory += size * pool.length; + stats.poolSizes[size] = pool.length; + } + + return stats; + } +} +``` + +#### 流式处理 +```typescript +class StreamProcessor { + async processLargeFile( + file: File, + processor: (chunk: Uint8Array, index: number) => Promise, + chunkSize = 1024 * 1024 // 1MB chunks + ): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + let offset = 0; + let index = 0; + + const processChunk = () => { + if (offset >= file.size) { + resolve(); + return; + } + + const chunk = file.slice(offset, offset + chunkSize); + reader.onload = async (event) => { + if (event.target?.result) { + const arrayBuffer = event.target.result as ArrayBuffer; + const uint8Array = new Uint8Array(arrayBuffer); + + try { + await processor(uint8Array, index); + offset += chunkSize; + index++; + processChunk(); + } catch (error) { + reject(error); + } + } + }; + + reader.readAsArrayBuffer(chunk); + }; + + processChunk(); + }); + } + + async uploadStream( + file: File, + onProgress?: (progress: number) => void + ): Promise { + const chunks: Uint8Array[] = []; + const totalSize = file.size; + + await this.processLargeFile(file, async (chunk, index) => { + chunks.push(chunk); + + const progress = ((index + 1) * chunk.length) / totalSize; + onProgress?.(Math.min(progress, 1)); + }); + + // 合并所有块 + const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0); + const combined = new Uint8Array(totalLength); + let offset = 0; + + for (const chunk of chunks) { + combined.set(chunk, offset); + offset += chunk.length; + } + + // 上传合并后的数据 + const walrusFile = WalrusFile.from({ + contents: combined, + identifier: `stream/${Date.now()}_${file.name}` + }); + + const result = await client.walrus.writeFiles({ + files: [walrusFile], + epochs: 10, + deletable: true + }); + + return result[0].blobId; + } +} +``` + +## 性能监控 + +### 实时性能指标 +```typescript +class PerformanceMonitor { + private metrics = new Map(); + private readonly MAX_METRICS = 1000; + + recordMetric(name: string, value: number, tags: Record = {}) { + const entry: MetricEntry = { + timestamp: Date.now(), + value, + tags + }; + + const existing = this.metrics.get(name) || []; + existing.push(entry); + + // 限制历史数据数量 + if (existing.length > this.MAX_METRICS) { + existing.splice(0, existing.length - this.MAX_METRICS); + } + + this.metrics.set(name, existing); + } + + getMetrics(name: string, timeRange?: number): MetricEntry[] { + const allMetrics = this.metrics.get(name) || []; + + if (!timeRange) { + return allMetrics; + } + + const cutoff = Date.now() - timeRange; + return allMetrics.filter(m => m.timestamp >= cutoff); + } + + getAggregatedMetrics(name: string, timeRange = 300000): AggregatedMetrics { + const metrics = this.getMetrics(name, timeRange); + + if (metrics.length === 0) { + return { + count: 0, + sum: 0, + avg: 0, + min: 0, + max: 0, + p50: 0, + p95: 0, + p99: 0 + }; + } + + const values = metrics.map(m => m.value).sort((a, b) => a - b); + const sum = values.reduce((s, v) => s + v, 0); + + return { + count: values.length, + sum, + avg: sum / values.length, + min: values[0], + max: values[values.length - 1], + p50: this.percentile(values, 0.5), + p95: this.percentile(values, 0.95), + p99: this.percentile(values, 0.99) + }; + } + + private percentile(sorted: number[], p: number): number { + const index = Math.ceil(sorted.length * p) - 1; + return sorted[Math.max(0, index)]; + } +} +``` + +## 使用方法 + +### 基础性能分析 +```typescript +skill: "walrus-performance-optimizer" +// analysis-type: upload +// target-metric: speed +// code-context: 我的文件上传速度很慢,单个10MB文件需要2分钟,如何优化? +``` + +### 批量操作优化 +```typescript +skill: "walrus-performance-optimizer" +// analysis-type: batch +// target-metric: throughput +// code-context: 批量上传100个文件时经常超时,需要优化并发处理 +``` + +### 内存使用优化 +```typescript +skill: "walrus-performance-optimizer" +// analysis-type: memory +// target-metric: cost +// code-context: 大文件处理时内存占用过高,浏览器经常崩溃 +``` + +### 网络优化 +```typescript +skill: "walrus-performance-optimizer" +// analysis-type: network +// target-metric: latency +// code-context: 网络不稳定环境下频繁上传失败,需要优化网络策略 +``` + +--- + +*更新时间:2025-11-11* \ No newline at end of file