mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
feat: release v0.77.0 with full pending changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -159,6 +159,11 @@ export interface NovelListRunsRequest {
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface NovelDeleteCharacterRequest {
|
||||
project_id: string;
|
||||
character_id: string;
|
||||
}
|
||||
|
||||
export async function createNovelProject(
|
||||
request: CreateNovelProjectRequest,
|
||||
): Promise<NovelProject> {
|
||||
@@ -236,3 +241,9 @@ export async function listNovelRuns(
|
||||
): Promise<NovelGenerationRun[]> {
|
||||
return invoke("novel_list_runs", { request });
|
||||
}
|
||||
|
||||
export async function deleteNovelCharacter(
|
||||
request: NovelDeleteCharacterRequest,
|
||||
): Promise<boolean> {
|
||||
return invoke("novel_delete_character", { request });
|
||||
}
|
||||
|
||||
+12
-1
@@ -259,6 +259,11 @@ export async function resolveProjectRootPath(name: string): Promise<string> {
|
||||
/** 获取项目列表 */
|
||||
export async function listProjects(): Promise<Project[]> {
|
||||
const projects = await invoke<RawProject[]>("workspace_list");
|
||||
// 防御性编程:确保返回数组
|
||||
if (!Array.isArray(projects)) {
|
||||
console.warn("listProjects 返回非数组值:", projects);
|
||||
return [];
|
||||
}
|
||||
return projects.map((project) => normalizeProject(project));
|
||||
}
|
||||
|
||||
@@ -326,7 +331,13 @@ export async function listContents(
|
||||
projectId: string,
|
||||
query?: ListContentQuery,
|
||||
): Promise<ContentListItem[]> {
|
||||
return invoke("content_list", { projectId, query });
|
||||
const contents = await invoke<ContentListItem[]>("content_list", { projectId, query });
|
||||
// 防御性编程:确保返回数组
|
||||
if (!Array.isArray(contents)) {
|
||||
console.warn("listContents 返回非数组值:", contents);
|
||||
return [];
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
/** 更新内容 */
|
||||
|
||||
@@ -3,12 +3,12 @@ import { resolveAppVersion } from "./appVersion";
|
||||
|
||||
describe("appVersion", () => {
|
||||
it("优先返回首个有效版本号", () => {
|
||||
expect(resolveAppVersion("0.76.1", "0.76.0")).toBe("0.76.1");
|
||||
expect(resolveAppVersion("unknown", "0.76.0")).toBe("0.76.0");
|
||||
expect(resolveAppVersion("", " ", "0.76.0")).toBe("0.76.0");
|
||||
expect(resolveAppVersion("0.77.1", "0.77.0")).toBe("0.77.1");
|
||||
expect(resolveAppVersion("unknown", "0.77.0")).toBe("0.77.0");
|
||||
expect(resolveAppVersion("", " ", "0.77.0")).toBe("0.77.0");
|
||||
});
|
||||
|
||||
it("无候选值时回退 package.json 版本", () => {
|
||||
expect(resolveAppVersion(undefined, null, "unknown")).toBe("0.76.0");
|
||||
expect(resolveAppVersion(undefined, null, "unknown")).toBe("0.77.0");
|
||||
});
|
||||
});
|
||||
|
||||
+131
-1
@@ -176,6 +176,89 @@ const defaultMocks: Record<string, any> = {
|
||||
remove_total: 0,
|
||||
},
|
||||
}),
|
||||
get_server_diagnostics: () => ({
|
||||
generated_at: new Date().toISOString(),
|
||||
running: false,
|
||||
host: "127.0.0.1",
|
||||
port: 8787,
|
||||
telemetry_summary: {
|
||||
total_requests: 0,
|
||||
successful_requests: 0,
|
||||
failed_requests: 0,
|
||||
timeout_requests: 0,
|
||||
success_rate: 0,
|
||||
avg_latency_ms: 0,
|
||||
min_latency_ms: null,
|
||||
max_latency_ms: null,
|
||||
total_input_tokens: 0,
|
||||
total_output_tokens: 0,
|
||||
total_tokens: 0,
|
||||
},
|
||||
capability_routing: {
|
||||
filter_eval_total: 0,
|
||||
filter_excluded_total: 0,
|
||||
filter_excluded_tools_total: 0,
|
||||
filter_excluded_vision_total: 0,
|
||||
filter_excluded_context_total: 0,
|
||||
provider_fallback_total: 0,
|
||||
model_fallback_total: 0,
|
||||
all_candidates_excluded_total: 0,
|
||||
},
|
||||
response_cache: {
|
||||
config: {
|
||||
enabled: true,
|
||||
ttl_secs: 600,
|
||||
max_entries: 200,
|
||||
max_body_bytes: 1048576,
|
||||
cacheable_status_codes: [200],
|
||||
},
|
||||
stats: {
|
||||
size: 0,
|
||||
hits: 0,
|
||||
misses: 0,
|
||||
evictions: 0,
|
||||
},
|
||||
hit_rate_percent: 0,
|
||||
},
|
||||
request_dedup: {
|
||||
config: {
|
||||
enabled: true,
|
||||
ttl_secs: 30,
|
||||
wait_timeout_ms: 15000,
|
||||
},
|
||||
stats: {
|
||||
inflight_size: 0,
|
||||
completed_size: 0,
|
||||
check_new_total: 0,
|
||||
check_in_progress_total: 0,
|
||||
check_completed_total: 0,
|
||||
wait_success_total: 0,
|
||||
wait_timeout_total: 0,
|
||||
wait_no_result_total: 0,
|
||||
complete_total: 0,
|
||||
remove_total: 0,
|
||||
},
|
||||
replay_rate_percent: 0,
|
||||
},
|
||||
idempotency: {
|
||||
config: {
|
||||
enabled: false,
|
||||
ttl_secs: 86400,
|
||||
header_name: "Idempotency-Key",
|
||||
},
|
||||
stats: {
|
||||
entries_size: 0,
|
||||
in_progress_size: 0,
|
||||
completed_size: 0,
|
||||
check_new_total: 0,
|
||||
check_in_progress_total: 0,
|
||||
check_completed_total: 0,
|
||||
complete_total: 0,
|
||||
remove_total: 0,
|
||||
},
|
||||
replay_rate_percent: 0,
|
||||
},
|
||||
}),
|
||||
check_server_status: () => ({
|
||||
running: false,
|
||||
host: "127.0.0.1",
|
||||
@@ -752,7 +835,21 @@ const defaultMocks: Record<string, any> = {
|
||||
clear_logs: () => ({}),
|
||||
|
||||
// Test 相关
|
||||
test_api: () => ({ success: true, status: 200, body: "", time_ms: 0 }),
|
||||
test_api: () => ({
|
||||
success: true,
|
||||
status: 200,
|
||||
body: "",
|
||||
time_ms: 0,
|
||||
response_headers: {
|
||||
"x-proxycast-request-id": "mock-request-id",
|
||||
"x-proxycast-cache": "store",
|
||||
"x-proxycast-dedup": "new",
|
||||
"x-proxycast-idempotency": "new",
|
||||
"x-proxycast-requested-provider": "openai",
|
||||
"x-proxycast-effective-provider": "openai",
|
||||
"x-proxycast-model": "gpt-4o-mini",
|
||||
},
|
||||
}),
|
||||
|
||||
// Kiro Credentials 相关
|
||||
get_kiro_credentials: () => ({ loaded: false }),
|
||||
@@ -832,8 +929,30 @@ const defaultMocks: Record<string, any> = {
|
||||
close_screenshot_chat_window: () => ({}),
|
||||
|
||||
// Update 相关
|
||||
get_update_check_settings: () => ({
|
||||
enabled: true,
|
||||
check_interval_hours: 24,
|
||||
show_notification: true,
|
||||
last_check_timestamp: 0,
|
||||
skipped_version: null,
|
||||
remind_later_until: null,
|
||||
}),
|
||||
get_update_notification_metrics: () => ({
|
||||
shown_count: 0,
|
||||
update_now_count: 0,
|
||||
remind_later_count: 0,
|
||||
skip_version_count: 0,
|
||||
dismiss_count: 0,
|
||||
update_now_rate: 0,
|
||||
remind_later_rate: 0,
|
||||
skip_version_rate: 0,
|
||||
dismiss_rate: 0,
|
||||
}),
|
||||
record_update_notification_action: () => ({}),
|
||||
download_update: () => ({ success: true }),
|
||||
skip_update_version: () => ({}),
|
||||
remind_update_later: () => Math.floor(Date.now() / 1000) + 24 * 3600,
|
||||
dismiss_update_notification: () => Math.floor(Date.now() / 1000) + 24 * 3600,
|
||||
close_update_window: () => ({}),
|
||||
set_update_check_settings: () => ({ success: true }),
|
||||
test_update_window: () => ({}),
|
||||
@@ -915,6 +1034,17 @@ const defaultMocks: Record<string, any> = {
|
||||
previousRootPath: null,
|
||||
warning: null,
|
||||
}),
|
||||
workspace_get_projects_root: () => "/mock/workspace/projects",
|
||||
workspace_resolve_project_path: (args: any) => `/mock/workspace/projects/${args?.name ?? "untitled"}`,
|
||||
workspace_create: (args: any) => ({
|
||||
id: `mock-project-${Date.now()}`,
|
||||
name: args?.request?.name ?? "Mock Project",
|
||||
rootPath: args?.request?.rootPath ?? "/mock/workspace/projects/mock-project",
|
||||
workspaceType: args?.request?.workspaceType ?? "general",
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now(),
|
||||
isArchived: false,
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildWorkspaceRepairBatchSummary,
|
||||
buildWorkspaceRepairSummary,
|
||||
clearWorkspaceRepairHistory,
|
||||
getWorkspaceRepairHistory,
|
||||
recordWorkspaceRepair,
|
||||
@@ -42,4 +44,41 @@ describe("workspaceHealthTelemetry", () => {
|
||||
expect(history[0].workspace_id).toBe("ws-55");
|
||||
expect(history[49].workspace_id).toBe("ws-6");
|
||||
});
|
||||
|
||||
it("应生成可复制的自愈摘要", () => {
|
||||
const summary = buildWorkspaceRepairSummary({
|
||||
timestamp: "2026-03-02T10:00:00.000Z",
|
||||
workspace_id: "ws-99",
|
||||
root_path: "/tmp/ws-99",
|
||||
source: "agent_chat_page",
|
||||
});
|
||||
|
||||
expect(summary).toContain("ProxyCast Workspace 自愈记录");
|
||||
expect(summary).toContain("Workspace ID: ws-99");
|
||||
expect(summary).toContain("来源: agent_chat_page");
|
||||
expect(summary).toContain("修复后路径: /tmp/ws-99");
|
||||
});
|
||||
|
||||
it("应生成批量自愈摘要", () => {
|
||||
const summary = buildWorkspaceRepairBatchSummary([
|
||||
{
|
||||
timestamp: "2026-03-02T10:00:00.000Z",
|
||||
workspace_id: "ws-1",
|
||||
root_path: "/tmp/ws-1",
|
||||
source: "app_startup",
|
||||
},
|
||||
{
|
||||
timestamp: "2026-03-02T10:02:00.000Z",
|
||||
workspace_id: "ws-2",
|
||||
root_path: "/tmp/ws-2",
|
||||
source: "agent_chat_page",
|
||||
},
|
||||
]);
|
||||
|
||||
expect(summary).toContain("ProxyCast Workspace 自愈记录(最近)");
|
||||
expect(summary).toContain("## 记录 1");
|
||||
expect(summary).toContain("Workspace ID: ws-1");
|
||||
expect(summary).toContain("## 记录 2");
|
||||
expect(summary).toContain("Workspace ID: ws-2");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,6 +50,38 @@ export function clearWorkspaceRepairHistory(): void {
|
||||
localStorage.removeItem(WORKSPACE_REPAIR_HISTORY_KEY);
|
||||
}
|
||||
|
||||
export function buildWorkspaceRepairSummary(
|
||||
record: WorkspaceRepairRecord,
|
||||
): string {
|
||||
return [
|
||||
"# ProxyCast Workspace 自愈记录",
|
||||
`- 时间: ${record.timestamp}`,
|
||||
`- Workspace ID: ${record.workspace_id}`,
|
||||
`- 来源: ${record.source}`,
|
||||
`- 修复后路径: ${record.root_path}`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export function buildWorkspaceRepairBatchSummary(
|
||||
records: WorkspaceRepairRecord[],
|
||||
): string {
|
||||
if (records.length === 0) {
|
||||
return "# ProxyCast Workspace 自愈记录\n- 暂无记录";
|
||||
}
|
||||
|
||||
const lines: string[] = ["# ProxyCast Workspace 自愈记录(最近)"];
|
||||
records.forEach((record, index) => {
|
||||
lines.push(
|
||||
`\n## 记录 ${index + 1}`,
|
||||
`- 时间: ${record.timestamp}`,
|
||||
`- Workspace ID: ${record.workspace_id}`,
|
||||
`- 来源: ${record.source}`,
|
||||
`- 修复后路径: ${record.root_path}`,
|
||||
);
|
||||
});
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function readWorkspaceRepairHistory(): WorkspaceRepairRecord[] {
|
||||
if (typeof localStorage === "undefined") return [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user