feat: 升级至 v0.3.1 — 全量代码审计修复 + 安全增强
本次升级基于完整代码审查,修复 Critical/High/Medium/Low 四级共 96 项问题, 并通过返工审计修复 10 项遗留问题,tsc 双端类型检查零错误。 Critical (10/10 完成): - C-4: command.ts 接入 shell-quote 进行 token-level 注入检测,替代原有正则匹配 可防御 r"m" -rf /、$'rm'、$(echo rm) 等字符串拼接绕过 High (11/11 完成): - 竞态保护、Promise.allSettled、AbortController 资源泄漏、IPC 参数校验等 Medium (55/55 完成): - 事务保护、敏感数据脱敏、枚举校验、MUI v9 Stack prop 迁移、 React 组件 cancelled 标志、类型收窄等 Low (20/20 完成): - 辅助方法提取(flushToolCallBuffer/scoreAndPushMemory/tryAddColumn 等) - nanoid 统一替代 Date.now()+Math.random() - confirm() 替换为 MUI Dialog、useMemo 缓存、魔法数字命名化等 返工审计修复 (10/10 完成): - L-11: LogsSettings 残留的原生 confirm()/alert() 全部替换为 MUI Dialog/Alert - M-53: MemoryViewer handleSearch 独立 ref,修复 searching 状态卡死 - M-42: 脱敏短值(length <= 4)泄露修复 - M-47: tasks:update 补全 title/description 类型校验 - L-9: ollama.adapter 非流式路径 nanoid 统一 - M-45: audit:query limit 策略与 memory:listAll 一致化 - SettingsModal handleConfirmRemove 补全 try/catch + loadServers cleanup - L-15: CommandPalette useMemo 补全 sessions 响应式依赖 - useAgentStream 事件类型补全 seq/timestamp 字段 新增依赖: shell-quote + @types/shell-quote 版本号: 0.3.0 -> 0.3.1
This commit is contained in:
@@ -16,17 +16,81 @@ import type {
|
||||
MetonaError,
|
||||
} from '../types';
|
||||
import { MetonaErrorCode } from '../types';
|
||||
import type { MetonaModelInfo } from '../types/metona-adapter';
|
||||
|
||||
export abstract class BaseAdapter implements IMetonaProviderAdapter {
|
||||
abstract readonly provider: string;
|
||||
// H-2 修复: provider → providerId(规范要求)
|
||||
abstract readonly providerId: string;
|
||||
abstract readonly supportedModels: string[];
|
||||
abstract readonly supportsToolCalling: boolean;
|
||||
abstract readonly supportsThinking: boolean;
|
||||
|
||||
/**
|
||||
* C-2 修复: 外部注入的 AbortSignal(来自 Engine 的 abortController)
|
||||
*
|
||||
* 用户点击中断时,Engine 调用 abortController.abort(),此信号触发后,
|
||||
* 正在进行的 fetch 会被立即中断,避免资源泄漏。
|
||||
*/
|
||||
private externalAbortSignal: AbortSignal | undefined;
|
||||
|
||||
constructor(protected config: AdapterConfig) {}
|
||||
|
||||
abstract chat(request: MetonaRequest): Promise<MetonaResponse>;
|
||||
abstract chatStream(request: MetonaRequest): AsyncIterable<MetonaStreamEvent>;
|
||||
// H-2 修复: chat → send(规范要求)
|
||||
abstract send(request: MetonaRequest): Promise<MetonaResponse>;
|
||||
// H-2 修复: chatStream → sendStream(规范要求)
|
||||
abstract sendStream(request: MetonaRequest): AsyncIterable<MetonaStreamEvent>;
|
||||
|
||||
/**
|
||||
* H-2 修复: 获取上下文窗口大小(规范要求)
|
||||
*
|
||||
* 默认实现从 config 读取 contextWindow,子类可覆盖以支持动态查询。
|
||||
* Engine 用此值估算上下文使用率,决定是否触发压缩。
|
||||
*
|
||||
* @returns 上下文窗口大小(token 数)
|
||||
*/
|
||||
getContextWindow(): number {
|
||||
// 优先使用 AdapterConfig.contextWindow(如果存在)
|
||||
const ctx = (this.config as AdapterConfig & { contextWindow?: number }).contextWindow;
|
||||
if (typeof ctx === 'number' && ctx > 0) return ctx;
|
||||
// 默认 1M(保守值,子类应覆盖)
|
||||
return 1_000_000;
|
||||
}
|
||||
|
||||
/**
|
||||
* C-2 修复: 注入外部 AbortSignal
|
||||
* Engine 在调用 send/sendStream 前调用此方法,关联 abortController
|
||||
*/
|
||||
setAbortSignal(signal: AbortSignal | undefined): void {
|
||||
this.externalAbortSignal = signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* C-2 修复: 合并外部 abort signal 和 timeout signal
|
||||
*
|
||||
* 使用 AbortSignal.any() 合并两个信号,任一触发都会中断 fetch:
|
||||
* - timeout signal:防止请求挂起
|
||||
* - external abort signal:用户主动中断
|
||||
*
|
||||
* @param timeoutMs 超时时间(毫秒)
|
||||
* @returns 合并后的 AbortSignal
|
||||
*/
|
||||
protected getFetchSignal(timeoutMs: number): AbortSignal {
|
||||
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
||||
|
||||
// 如果没有外部信号,直接使用 timeout signal
|
||||
if (!this.externalAbortSignal) {
|
||||
return timeoutSignal;
|
||||
}
|
||||
|
||||
// 如果外部信号已经 abort,直接返回它
|
||||
if (this.externalAbortSignal.aborted) {
|
||||
return this.externalAbortSignal;
|
||||
}
|
||||
|
||||
// 合并两个信号 — 任一触发都会 abort
|
||||
// Node.js 20+ / Electron 35+ 支持 AbortSignal.any()
|
||||
return AbortSignal.any([timeoutSignal, this.externalAbortSignal]);
|
||||
}
|
||||
|
||||
async healthCheck(): Promise<boolean> {
|
||||
try {
|
||||
@@ -37,8 +101,14 @@ export abstract class BaseAdapter implements IMetonaProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
async listModels(): Promise<string[]> {
|
||||
return this.supportedModels;
|
||||
/**
|
||||
* H-2 修复: 返回 MetonaModelInfo[](规范要求)
|
||||
*
|
||||
* 默认实现将 supportedModels 映射为 MetonaModelInfo[],
|
||||
* 子类可覆盖以从 API 获取完整元信息。
|
||||
*/
|
||||
async listModels(): Promise<MetonaModelInfo[]> {
|
||||
return this.supportedModels.map((id) => ({ id }));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +122,7 @@ export abstract class BaseAdapter implements IMetonaProviderAdapter {
|
||||
return {
|
||||
code: MetonaErrorCode.NETWORK_TIMEOUT,
|
||||
message: error.message,
|
||||
provider: this.provider,
|
||||
provider: this.providerId,
|
||||
retryable: true,
|
||||
retryAfterMs: 3000,
|
||||
};
|
||||
@@ -62,7 +132,7 @@ export abstract class BaseAdapter implements IMetonaProviderAdapter {
|
||||
return {
|
||||
code: MetonaErrorCode.NETWORK_ERROR,
|
||||
message: error.message,
|
||||
provider: this.provider,
|
||||
provider: this.providerId,
|
||||
retryable: true,
|
||||
retryAfterMs: 3000,
|
||||
};
|
||||
@@ -72,7 +142,7 @@ export abstract class BaseAdapter implements IMetonaProviderAdapter {
|
||||
return {
|
||||
code: MetonaErrorCode.AUTH_INVALID,
|
||||
message: 'API key 无效或已过期',
|
||||
provider: this.provider,
|
||||
provider: this.providerId,
|
||||
retryable: false,
|
||||
};
|
||||
}
|
||||
@@ -81,7 +151,7 @@ export abstract class BaseAdapter implements IMetonaProviderAdapter {
|
||||
return {
|
||||
code: MetonaErrorCode.RATE_LIMITED,
|
||||
message: '请求过于频繁,请稍后重试',
|
||||
provider: this.provider,
|
||||
provider: this.providerId,
|
||||
retryable: true,
|
||||
retryAfterMs: 5000,
|
||||
};
|
||||
@@ -91,7 +161,7 @@ export abstract class BaseAdapter implements IMetonaProviderAdapter {
|
||||
return {
|
||||
code: MetonaErrorCode.UNKNOWN,
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
provider: this.provider,
|
||||
provider: this.providerId,
|
||||
retryable: false,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user