流式渲染修复: - runId 机制防止 abort 后旧流事件污染新 run - run lock 防止并发 run 污染引擎状态 - abort race 提前退出工具执行等待 - TERMINATED 状态通过 stateChange 发射 - tool_call_delta 流式参数拼接 + pending 占位替换 - 首轮卡片创建路径统一,traceStep 按 ID 精确匹配 - compressed 事件转发为 toast 通知 安全增强: - ConfirmationHook 支持持久化自动执行(跨会话) - 设置面板新增自动执行工具管理 UI - SandboxManager 双重安全校验 fail-closed - 审计日志链式哈希防篡改 - PromptInjectionDefender 中文注入标记清理 - scanCode 28 模式 + base64/$() 检测 - validatePath realpathSync 防符号链接逃逸 - code-search 使用 execFile 防命令注入 新增工具: - file_editor、code_search、task_manager、diff_viewer 其他: - Agent Loop 加 PARSING/REFLECTING 状态 + 指数退避重试 - MemoryManager TF-IDF 语义检索 - run_command Windows 中文编码修复(chcp 65001) - 版本号 0.2.0 → 0.2.1
60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
/**
|
||
* 格式化工具函数
|
||
*
|
||
* 日期格式化使用 date-fns(成熟第三方库,禁止自写)。
|
||
* Token 数、文件大小等简单格式化为 < 20 行纯函数,允许自写。
|
||
*
|
||
* @see standard/开发规范.md — 第一铁律
|
||
*/
|
||
|
||
import { formatDistanceToNow, format } from 'date-fns';
|
||
import { zhCN } from 'date-fns/locale';
|
||
|
||
// ===== 日期格式化(使用 date-fns)=====
|
||
|
||
/** 相对时间(如 "3 分钟前"、"昨天") */
|
||
export function formatRelativeTime(timestamp: number): string {
|
||
return formatDistanceToNow(new Date(timestamp), { addSuffix: true, locale: zhCN });
|
||
}
|
||
|
||
/** 完整时间格式(如 "2026-06-27 14:30") */
|
||
export function formatTime(timestamp: number): string {
|
||
return format(new Date(timestamp), 'yyyy-MM-dd HH:mm');
|
||
}
|
||
|
||
// ===== Token 格式化(< 20 行纯函数,允许自写)=====
|
||
|
||
export function formatTokens(count: number): string {
|
||
if (count < 1000) return String(count);
|
||
if (count < 10000) return `${(count / 1000).toFixed(1)}K`;
|
||
if (count < 1000000) return `${Math.round(count / 1000)}K`;
|
||
return `${(count / 1000000).toFixed(1)}M`;
|
||
}
|
||
|
||
// ===== 文件大小格式化(< 20 行纯函数,允许自写)=====
|
||
|
||
export function formatFileSize(bytes: number): string {
|
||
if (bytes < 1024) return `${bytes} B`;
|
||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
||
}
|
||
|
||
// ===== 耗时格式化(< 20 行纯函数,允许自写)=====
|
||
|
||
export function formatDuration(ms: number): string {
|
||
if (ms < 1000) return `${ms}ms`;
|
||
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
||
const minutes = Math.floor(ms / 60000);
|
||
const seconds = Math.round((ms % 60000) / 1000);
|
||
return `${minutes}m${seconds}s`;
|
||
}
|
||
|
||
// ===== 截断文本(< 20 行纯函数,允许自写)=====
|
||
|
||
export function truncate(text: string, maxLength: number): string {
|
||
if (typeof text !== 'string' || text.length === 0) return '';
|
||
if (text.length <= maxLength) return text;
|
||
return text.slice(0, maxLength - 1) + '…';
|
||
}
|