5e32375f0e
架构变更: - 采用 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 - 配置管理
160 lines
3.8 KiB
TypeScript
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 };
|
|
}
|
|
}
|