Files
ai-terminal-assistant/packages/core/src/permission/checkers/web.ts
T
kurihada 5e32375f0e feat: 重构为 Monorepo 架构并实现 HTTP Server
架构变更:
- 采用 pnpm workspaces 实现 Monorepo 结构
- 将现有代码迁移到 packages/core
- 新增 packages/server HTTP 服务层

Server 功能:
- REST API: 会话管理、工具管理、配置管理
- WebSocket: 实时双向通信支持
- SSE: 服务端事件推送
- Hono + Bun 作为运行时

API 端点:
- GET/POST /api/sessions - 会话 CRUD
- GET/POST /api/sessions/:id/messages - 消息管理
- GET /api/sessions/:id/events - SSE 事件流
- WS /api/ws/:sessionId - WebSocket 连接
- GET/POST /api/tools - 工具管理
- GET/PUT /api/config - 配置管理
2025-12-12 10:42:20 +08:00

160 lines
3.8 KiB
TypeScript

import type {
WebPermissionConfig,
WebPermissionContext,
PermissionCheckResult,
PermissionDecision,
PermissionContext,
} from '../types.js';
import type { PermissionChecker } from './base.js';
// 默认 Web 权限配置
const DEFAULT_CONFIG: WebPermissionConfig = {
default: 'ask', // 默认需要确认
allowAdvancedSearch: true,
allowedTopics: [], // 空数组表示允许所有主题
};
/**
* Web 搜索权限检查器
* 控制网络搜索操作的权限
*/
export class WebPermissionChecker implements PermissionChecker {
readonly name = 'web';
private config: WebPermissionConfig;
private askCallback?: (ctx: PermissionContext) => Promise<PermissionDecision>;
private sessionPermissions = new Map<string, 'allow' | 'deny'>();
constructor() {
this.config = { ...DEFAULT_CONFIG };
}
/**
* 设置权限询问回调
*/
setAskCallback(callback: (ctx: PermissionContext) => Promise<PermissionDecision>): void {
this.askCallback = callback;
}
/**
* 检查 Web 搜索权限
*/
async checkWebPermission(ctx: WebPermissionContext): Promise<PermissionCheckResult> {
const { query, searchDepth, topic } = ctx;
// 1. 检查深度搜索权限
if (searchDepth === 'advanced' && !this.config.allowAdvancedSearch) {
return {
allowed: false,
action: 'deny',
reason: '不允许深度搜索',
};
}
// 2. 检查主题限制
if (this.config.allowedTopics.length > 0 && topic) {
if (!this.config.allowedTopics.includes(topic)) {
return {
allowed: false,
action: 'deny',
reason: `不允许搜索主题: ${topic}`,
};
}
}
// 3. 检查会话级别的临时权限
const sessionKey = `web_search`;
const sessionPerm = this.sessionPermissions.get(sessionKey);
if (sessionPerm === 'allow') {
return {
allowed: true,
action: 'allow',
reason: '本次会话已允许网络搜索',
};
}
if (sessionPerm === 'deny') {
return {
allowed: false,
action: 'deny',
reason: '本次会话已拒绝网络搜索',
};
}
// 4. 根据默认策略处理
const action = this.config.default;
if (action === 'allow') {
return {
allowed: true,
action: 'allow',
reason: '默认允许网络搜索',
};
}
if (action === 'deny') {
return {
allowed: false,
action: 'deny',
reason: '默认拒绝网络搜索',
};
}
// action === 'ask'
if (!this.askCallback) {
return {
allowed: false,
action: 'ask',
needsConfirmation: true,
reason: `搜索: ${query}`,
};
}
// 调用回调询问用户
const decision = await this.askCallback({
command: `web_search: ${query}`,
workdir: process.cwd(),
});
if (decision.remember) {
this.sessionPermissions.set(sessionKey, decision.allow ? 'allow' : 'deny');
}
return {
allowed: decision.allow,
action: decision.allow ? 'allow' : 'deny',
reason: decision.allow ? '用户允许' : '用户拒绝',
};
}
/**
* 实现 PermissionChecker 接口的 check 方法
* 从通用 PermissionContext 中提取 Web 搜索信息
*/
async check(ctx: PermissionContext): Promise<PermissionCheckResult> {
// 从 command 中提取搜索查询
const query = ctx.command.replace(/^web_search:\s*/, '');
return this.checkWebPermission({ query });
}
/**
* 清除会话权限
*/
clearSessionPermissions(): void {
this.sessionPermissions.clear();
}
/**
* 获取当前配置
*/
getConfig(): WebPermissionConfig {
return { ...this.config };
}
/**
* 更新配置
*/
setConfig(config: Partial<WebPermissionConfig>): void {
this.config = { ...this.config, ...config };
}
}