新增: 审批超时可配置 + ScriptNode 命令白/黑名单

- 审批超时: Settings 下拉选择(不限时/5/15/30/60min),默认 15min
- getApprovalTimeoutMs() 从 KV 读取,startApprovalTimer 动态取值
- ScriptNode: 黑名单 > 白名单策略,从环境变量读取
- Settings 加命令执行安全面板(白/黑名单文本框)
- i18n 中英文案 + 搜索索引补全
This commit is contained in:
2026-06-28 13:49:19 +08:00
parent b157bc9077
commit 6ffcba7e4d
7 changed files with 216 additions and 3 deletions

View File

@@ -70,8 +70,26 @@ export function findToolCall(id: string): AiToolCallInfo | undefined {
// ── 审批超时计时器(原 useAiSend,下沉打破 events↔send 循环依赖) ──
/// 审批等待超时阈值(ms) — 用户 5 分钟内不处理则自动拒绝
const APPROVAL_TIMEOUT_MS = Infinity // 已决策:不做超时,用户不审批就一直等
/// 审批超时阈值来源的 appSettings key(运行期可调,默认 15min)。
/// 值语义:正整数=毫秒超时;0=不限时(跳过计时器);缺省=900000(15min)。
const APPROVAL_TIMEOUT_KEY = 'df-approval-timeout'
const APPROVAL_TIMEOUT_DEFAULT_MS = 900_000 // 15 分钟
/**
* 读取当前审批超时阈值(ms):从 appSettings 取 `df-approval-timeout`,
* 缺省 900000(15min),返回 0 表示用户配置为「不限时」(调用方应跳过计时器)。
*
* 每次 startApprovalTimer 调用时取最新值 —— 用户在 Settings 页调整后,下一笔审批即生效
* (已在跑的计时器沿用旧值,符合「不影响已发起审批」的预期)。
*/
function getApprovalTimeoutMs(): number {
const raw = appSettings.get<number>(APPROVAL_TIMEOUT_KEY, APPROVAL_TIMEOUT_DEFAULT_MS)
// 防御:负数 / NaN 视为默认值;0 保留(语义=不限时)
if (typeof raw !== 'number' || !Number.isFinite(raw) || raw < 0) {
return APPROVAL_TIMEOUT_DEFAULT_MS
}
return raw
}
/** toolCallId → 审批超时计时器 */
const _approvalTimers = new Map<string, ReturnType<typeof setTimeout>>()
@@ -91,6 +109,9 @@ export function startApprovalTimer(
kind: 'risk' | 'path' = 'risk',
): void {
if (_approvalTimers.has(toolCallId)) return
// 0 = 用户配置为「不限时」,跳过计时器(对齐旧 Infinity 行为)
const timeoutMs = getApprovalTimeoutMs()
if (timeoutMs === 0) return
const timer = setTimeout(() => {
_approvalTimers.delete(toolCallId)
// 按 kind 分派拒绝路径,避免 path 类错调 ai_approve 致后端 kind 校验拒而卡死
@@ -107,7 +128,7 @@ export function startApprovalTimer(
isError: true,
timestamp: Date.now(),
})
}, APPROVAL_TIMEOUT_MS)
}, timeoutMs)
_approvalTimers.set(toolCallId, timer)
}