fix(coding): close PI-100 review findings
This commit is contained in:
@@ -56,7 +56,7 @@ export async function handleCodingConversationRoutes(
|
||||
sendJson(res, 503, {
|
||||
success: false,
|
||||
code: 'CODING_CORE_UNAVAILABLE',
|
||||
error: 'Coding services are unavailable',
|
||||
error: '本地编程服务暂时不可用。',
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,33 +2,22 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||
import type { HostApiContext } from '../context';
|
||||
import { CodingProductHostError } from '../coding-product-services';
|
||||
import { sendJson } from '../route-utils';
|
||||
import { sendFixedCodingError } from './coding-route-errors';
|
||||
|
||||
function unavailable(res: ServerResponse): void {
|
||||
sendJson(res, 503, {
|
||||
success: false,
|
||||
code: 'CODING_PRODUCT_TOOLS_UNAVAILABLE',
|
||||
error: 'Coding product tools are unavailable',
|
||||
});
|
||||
sendFixedCodingError(res, 503, 'CODING_PRODUCT_TOOLS_UNAVAILABLE');
|
||||
}
|
||||
|
||||
function serviceError(res: ServerResponse, error: unknown): void {
|
||||
if (error instanceof CodingProductHostError) {
|
||||
sendJson(res, error.status, {
|
||||
success: false,
|
||||
code: error.code,
|
||||
error: error.message,
|
||||
});
|
||||
sendFixedCodingError(res, error.status, error.code);
|
||||
return;
|
||||
}
|
||||
const code = error && typeof error === 'object' && 'code' in error
|
||||
? String(error.code)
|
||||
: '';
|
||||
if (code === 'ENOENT') {
|
||||
sendJson(res, 404, {
|
||||
success: false,
|
||||
code: 'CODING_FILE_NOT_FOUND',
|
||||
error: 'Project file does not exist',
|
||||
});
|
||||
sendFixedCodingError(res, 404, 'CODING_FILE_NOT_FOUND');
|
||||
return;
|
||||
}
|
||||
const message = error instanceof Error ? error.message : '';
|
||||
@@ -44,18 +33,10 @@ function serviceError(res: ServerResponse, error: unknown): void {
|
||||
'Project file is not valid UTF-8 text',
|
||||
]);
|
||||
if (knownInputError.has(message)) {
|
||||
sendJson(res, 400, {
|
||||
success: false,
|
||||
code: 'CODING_FILE_REQUEST_INVALID',
|
||||
error: message,
|
||||
});
|
||||
sendFixedCodingError(res, 400, 'CODING_FILE_REQUEST_INVALID');
|
||||
return;
|
||||
}
|
||||
sendJson(res, 500, {
|
||||
success: false,
|
||||
code: 'CODING_PRODUCT_TOOL_FAILED',
|
||||
error: 'Coding product request failed',
|
||||
});
|
||||
sendFixedCodingError(res, 500, 'CODING_PRODUCT_TOOL_FAILED');
|
||||
}
|
||||
|
||||
export async function handleCodingFileRoutes(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||
import type { ProjectType } from '../../../shared/project-config';
|
||||
import type { CodingProjectConfigSnapshot } from '../../coding-projects/project-service';
|
||||
import type { CodingProject } from '../../coding-projects/project-store';
|
||||
import type { HostApiContext } from '../context';
|
||||
import { parseJsonBody, sendJson, sendNoContent } from '../route-utils';
|
||||
import { sendCodingRouteError } from './coding-route-errors';
|
||||
@@ -9,6 +11,19 @@ function isProjectRoute(pathname: string): boolean {
|
||||
|| pathname.startsWith('/api/coding/projects/');
|
||||
}
|
||||
|
||||
function publicProject(project: CodingProject | null): Omit<CodingProject, 'path'> | null {
|
||||
if (!project) return null;
|
||||
const { path: _path, ...safe } = project;
|
||||
return safe;
|
||||
}
|
||||
|
||||
function publicProjectSnapshot(snapshot: CodingProjectConfigSnapshot) {
|
||||
return {
|
||||
...snapshot,
|
||||
project: publicProject(snapshot.project),
|
||||
};
|
||||
}
|
||||
|
||||
export async function handleCodingProjectRoutes(
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
@@ -22,7 +37,7 @@ export async function handleCodingProjectRoutes(
|
||||
sendJson(res, 503, {
|
||||
success: false,
|
||||
code: 'CODING_CORE_UNAVAILABLE',
|
||||
error: 'Coding services are unavailable',
|
||||
error: '本地编程服务暂时不可用。',
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -33,12 +48,15 @@ export async function handleCodingProjectRoutes(
|
||||
projects.listProjects(),
|
||||
projects.getActiveProject(),
|
||||
]);
|
||||
sendJson(res, 200, { projects: items, activeProjectId: activeProject?.id ?? null });
|
||||
sendJson(res, 200, {
|
||||
projects: items.map((project) => publicProject(project)),
|
||||
activeProjectId: activeProject?.id ?? null,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (url.pathname === '/api/coding/projects/open' && req.method === 'POST') {
|
||||
const body = await parseJsonBody<{ projectPath?: string }>(req);
|
||||
sendJson(res, 200, { project: await projects.openProject(body.projectPath ?? '') });
|
||||
sendJson(res, 200, { project: publicProject(await projects.openProject(body.projectPath ?? '')) });
|
||||
return true;
|
||||
}
|
||||
if (url.pathname === '/api/coding/projects/create' && req.method === 'POST') {
|
||||
@@ -48,7 +66,7 @@ export async function handleCodingProjectRoutes(
|
||||
projectName?: string;
|
||||
projectType?: ProjectType;
|
||||
}>(req);
|
||||
sendJson(res, 201, { snapshot: await projects.createProject(body) });
|
||||
sendJson(res, 201, { snapshot: publicProjectSnapshot(await projects.createProject(body)) });
|
||||
return true;
|
||||
}
|
||||
if (url.pathname === '/api/coding/projects/remove' && req.method === 'POST') {
|
||||
@@ -58,24 +76,26 @@ export async function handleCodingProjectRoutes(
|
||||
return true;
|
||||
}
|
||||
if (url.pathname === '/api/coding/projects/active' && req.method === 'GET') {
|
||||
sendJson(res, 200, { project: await projects.getActiveProject() });
|
||||
sendJson(res, 200, { project: publicProject(await projects.getActiveProject()) });
|
||||
return true;
|
||||
}
|
||||
if (url.pathname === '/api/coding/projects/active' && req.method === 'POST') {
|
||||
const body = await parseJsonBody<{ projectId?: string }>(req);
|
||||
sendJson(res, 200, { project: await projects.setActiveProject(body.projectId ?? '') });
|
||||
sendJson(res, 200, { project: publicProject(await projects.setActiveProject(body.projectId ?? '')) });
|
||||
return true;
|
||||
}
|
||||
if (url.pathname === '/api/coding/projects/config' && req.method === 'GET') {
|
||||
sendJson(res, 200, {
|
||||
snapshot: await projects.getConfig(url.searchParams.get('projectId')?.trim() || undefined),
|
||||
snapshot: publicProjectSnapshot(
|
||||
await projects.getConfig(url.searchParams.get('projectId')?.trim() || undefined),
|
||||
),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (url.pathname === '/api/coding/projects/config' && req.method === 'PUT') {
|
||||
const body = await parseJsonBody<{ projectId?: string; config?: unknown }>(req);
|
||||
sendJson(res, 200, {
|
||||
snapshot: await projects.saveConfig(body.projectId ?? '', body.config),
|
||||
snapshot: publicProjectSnapshot(await projects.saveConfig(body.projectId ?? '', body.config)),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,27 +3,72 @@ import { CodingProjectServiceError } from '../../coding-projects/project-service
|
||||
import { CodingConversationServiceError } from '../../coding-runtime/conversation-service';
|
||||
import { sendJson } from '../route-utils';
|
||||
|
||||
const FIXED_CODING_ERROR_MESSAGES: Readonly<Record<string, string>> = {
|
||||
CODING_ACTIVE_PROJECT_REQUIRED: '请先选择一个编程项目。',
|
||||
CODING_AGENT_NOT_FOUND: '当前伙伴不可用,请重新选择。',
|
||||
CODING_AGENT_ID_IMMUTABLE: '已有伙伴标识不能修改。',
|
||||
CODING_CONVERSATION_NOT_FOUND: '指定的对话不存在。',
|
||||
CODING_CONVERSATION_REQUEST_INVALID: '对话请求无效,请检查输入。',
|
||||
CODING_FILE_NOT_FOUND: '指定的项目文件不存在。',
|
||||
CODING_FILE_REQUEST_INVALID: '文件请求无效,请检查输入。',
|
||||
CODING_INTERACTION_REQUEST_INVALID: '交互响应无效,请重试。',
|
||||
CODING_KNOWLEDGE_ALREADY_EXISTS: '同名知识文件已存在。',
|
||||
CODING_KNOWLEDGE_REQUEST_INVALID: '知识文件无效,请检查后重试。',
|
||||
CODING_MIGRATION_MODEL_REQUIRED: '请先为该对话选择一个可用模型。',
|
||||
CODING_MODEL_UNAVAILABLE: '所选模型当前不可用,请重新选择。',
|
||||
CODING_PROJECT_ALREADY_EXISTS: '该编程项目已经存在。',
|
||||
CODING_PROJECT_CONFIG_INVALID: '项目配置无效,请检查后重试。',
|
||||
CODING_PROJECT_IDENTITY_IMMUTABLE: '项目创建标识不能修改。',
|
||||
CODING_PROJECT_NOT_FOUND: '指定的编程项目不存在。',
|
||||
CODING_PROJECT_REQUEST_INVALID: '项目请求无效,请检查输入。',
|
||||
CODING_PROJECT_TYPE_IMMUTABLE: '项目类型创建后不能修改。',
|
||||
CODING_PRODUCT_TOOL_FAILED: '项目工具执行失败,请重试。',
|
||||
CODING_PRODUCT_TOOLS_UNAVAILABLE: '项目工具暂时不可用。',
|
||||
CODING_PROVIDER_AUTH_REQUIRED: 'Provider 凭证不可用,请修复账号后重试。',
|
||||
CODING_REQUEST_CAPACITY_EXCEEDED: '本地请求队列已满,请稍后重试。',
|
||||
CODING_REQUEST_ID_CONFLICT: '该请求标识已用于不同内容,请使用新的标识。',
|
||||
CODING_REQUEST_UNCERTAIN: '请求状态无法确认,请先核对对话后再决定是否重试。',
|
||||
CODING_RUNTIME_PROTOCOL_ERROR: '本地 Agent 通信异常,请执行恢复。',
|
||||
CODING_RUNTIME_READY_TIMEOUT: '本地 Agent 启动超时,请执行恢复。',
|
||||
CODING_RUNTIME_START_FAILED: '本地 Agent 启动失败,请执行恢复。',
|
||||
CODING_RUNTIME_UNAVAILABLE: '本地编程运行时暂时不可用。',
|
||||
CODING_SESSION_UNREADABLE: '对话会话无法读取,原文件已保留。',
|
||||
CODING_STORAGE_WRITE_FAILED: '本地数据写入失败,请检查存储后重试。',
|
||||
};
|
||||
|
||||
function fixedMessage(code: string): string {
|
||||
return FIXED_CODING_ERROR_MESSAGES[code] ?? '编程操作失败,请重试。';
|
||||
}
|
||||
|
||||
export function sendFixedCodingError(
|
||||
res: ServerResponse,
|
||||
status: number,
|
||||
code: string,
|
||||
): void {
|
||||
sendJson(res, status, {
|
||||
success: false,
|
||||
code,
|
||||
error: fixedMessage(code),
|
||||
});
|
||||
}
|
||||
|
||||
export function sendCodingRouteError(res: ServerResponse, error: unknown): void {
|
||||
if (error instanceof CodingProjectServiceError || error instanceof CodingConversationServiceError) {
|
||||
sendJson(res, error.status, {
|
||||
success: false,
|
||||
code: error.code,
|
||||
error: error.message,
|
||||
});
|
||||
sendFixedCodingError(res, error.status, error.code);
|
||||
return;
|
||||
}
|
||||
if (error instanceof SyntaxError) {
|
||||
sendJson(res, 400, {
|
||||
success: false,
|
||||
code: 'CODING_REQUEST_INVALID',
|
||||
error: 'Request JSON is invalid',
|
||||
error: '请求内容无效,请检查后重试。',
|
||||
});
|
||||
return;
|
||||
}
|
||||
sendJson(res, 500, {
|
||||
success: false,
|
||||
code: 'CODING_REQUEST_FAILED',
|
||||
error: 'Coding request failed',
|
||||
error: '编程操作失败,请重试。',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user