feat(app): 移除内置agents并添加CORS支持和技能上传优化
移除了agents/registry.json中的所有内置agents配置,将agents数组清空。 为web应用添加了CORS中间件支持,允许指定的前端地址跨域访问。 重构了技能上传功能,增加了LLM重写机制,自动规范化上传的技能格式。 新增了工具名称提取逻辑,从技能正文中自动识别Required Tools段落。 更新了技能学习候选者和草稿的载荷结构,添加评估报告统计信息。 修改了意图路由技能的说明,改进任务状态管理逻辑。
This commit is contained in:
@ -58,6 +58,7 @@ const WS_URL = process.env.NEXT_PUBLIC_WS_URL?.trim();
|
||||
const DEFAULT_API_URL = 'http://127.0.0.1:18080';
|
||||
const ACCESS_TOKEN_KEY = 'beaver_access_token';
|
||||
const REFRESH_TOKEN_KEY = 'beaver_refresh_token';
|
||||
export const AUTH_CLEARED_EVENT = 'beaver-auth-cleared';
|
||||
const REQUEST_TIMEOUT_MS = 8000;
|
||||
const OUTLOOK_REQUEST_TIMEOUT_MS = 45000;
|
||||
const SKILL_LEARNING_REQUEST_TIMEOUT_MS = 120000;
|
||||
@ -117,6 +118,34 @@ type FetchJsonOptions = RequestInit & {
|
||||
timeoutMs?: number;
|
||||
};
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
detail: string;
|
||||
|
||||
constructor(message: string, options: { status: number; detail: string }) {
|
||||
super(message);
|
||||
this.name = 'ApiError';
|
||||
this.status = options.status;
|
||||
this.detail = options.detail;
|
||||
}
|
||||
}
|
||||
|
||||
export function isApiError(error: unknown, status?: number): error is ApiError {
|
||||
return error instanceof ApiError && (status === undefined || error.status === status);
|
||||
}
|
||||
|
||||
function parseErrorDetail(text: string): string {
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
if (parsed && typeof parsed.detail === 'string') {
|
||||
return parsed.detail;
|
||||
}
|
||||
} catch {
|
||||
// keep raw text
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function withTimeout(
|
||||
signal?: AbortSignal,
|
||||
timeoutMs: number = REQUEST_TIMEOUT_MS
|
||||
@ -163,6 +192,7 @@ export function clearTokens(): void {
|
||||
if (!isBrowser()) return;
|
||||
localStorage.removeItem(ACCESS_TOKEN_KEY);
|
||||
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
||||
window.dispatchEvent(new CustomEvent(AUTH_CLEARED_EVENT));
|
||||
}
|
||||
|
||||
export function isLoggedIn(): boolean {
|
||||
@ -215,16 +245,11 @@ async function fetchJSON<T>(path: string, options?: FetchJsonOptions): Promise<T
|
||||
if (res.status === 401) {
|
||||
clearTokens();
|
||||
}
|
||||
let detail = text;
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
if (parsed && typeof parsed.detail === 'string') {
|
||||
detail = parsed.detail;
|
||||
}
|
||||
} catch {
|
||||
// keep raw text
|
||||
}
|
||||
throw new Error(`${pickAppText(locale, '接口错误', 'API error')} ${res.status}: ${detail}`);
|
||||
const detail = parseErrorDetail(text);
|
||||
throw new ApiError(`${pickAppText(locale, '接口错误', 'API error')} ${res.status}: ${detail}`, {
|
||||
status: res.status,
|
||||
detail,
|
||||
});
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
@ -1216,7 +1241,7 @@ export async function uploadSkill(file: File): Promise<Skill> {
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(`接口错误 ${res.status}: ${text}`);
|
||||
throw new Error(`接口错误 ${res.status}: ${parseErrorDetail(text)}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user