feat: M4 文风 + M5 生成/多provider/Skill + Kimi Code 订阅接入 + 本地联调修复
M4(文风): style-auditor 双轨(提取指纹/漂移第四审)+ jobs 长任务框架(zombie reaper) + 回炉 refine + GET /style read-back。 M5(生成+扩展): worldbuilder/character-gen(入库 continuity 409 gate + partition_writes 白名单 + schema→JSONB 形变); 网关多 provider 回退链/熔断/能力降级(Anthropic/Gemini 适配器);Skill registry + 表权限沙箱 + 规则; 前端 角色生成器/世界观/Codex/规则页/技能库/⌘K 命令面板。 K1(Kimi Code 订阅接入): OAuth device-flow(kimi-code)+ 静态 Console key(kimi-code-key)两路径; coding 端点 KimiCLI 伪造头(实测 UA allow-list 门禁,缺则 403)+ JSON 模式结构化(thinking ⊥ tool_choice)。 本地联调修复: CORS 中间件;assemble 注入 premise+「写第N章」指令(修空 prompt 400); GET /outline·/draft read-back + 大纲/工作台/审稿页重载;写页 client/server 常量边界 + notFound 健壮化; 字数 toLocaleString locale 水合;审稿页终稿从已存草稿 seed(修 accept 422)。 门禁: backend ruff/mypy(157)/alembic 无漂移/pytest 451 · frontend lint/tsc/vitest/build。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,19 @@
|
||||
import { serverApiBase } from "./config";
|
||||
import type {
|
||||
CharacterListResponse,
|
||||
DraftView,
|
||||
ForeshadowBoardResponse,
|
||||
OutlineChapterView,
|
||||
OutlineResponse,
|
||||
ProjectListResponse,
|
||||
ProjectResponse,
|
||||
OAuthStatusResponse,
|
||||
ProvidersResponse,
|
||||
ReviewHistoryResponse,
|
||||
RuleListResponse,
|
||||
SkillListResponse,
|
||||
StyleFingerprintResponse,
|
||||
WorldEntityListResponse,
|
||||
} from "./types";
|
||||
|
||||
// 服务端只读取数据(Server Components)。失败时抛出,由页面边界处理。
|
||||
@@ -16,6 +25,16 @@ async function getJson<T>(path: string): Promise<T> {
|
||||
return (await res.json()) as T;
|
||||
}
|
||||
|
||||
// 像上面一样读取,但 404(无指纹)返回 null 而非抛出(首学前页面照常渲染)。
|
||||
async function getJsonOrNull<T>(path: string): Promise<T | null> {
|
||||
const res = await fetch(`${serverApiBase()}${path}`, { cache: "no-store" });
|
||||
if (res.status === 404) return null;
|
||||
if (!res.ok) {
|
||||
throw new Error(`请求失败 ${res.status}: ${path}`);
|
||||
}
|
||||
return (await res.json()) as T;
|
||||
}
|
||||
|
||||
export async function fetchProjects(): Promise<ProjectListResponse> {
|
||||
return getJson<ProjectListResponse>("/projects");
|
||||
}
|
||||
@@ -28,6 +47,13 @@ export async function fetchProviders(): Promise<ProvidersResponse> {
|
||||
return getJson<ProvidersResponse>("/settings/providers");
|
||||
}
|
||||
|
||||
// Kimi Code OAuth 连接态(GET .../oauth/status;无 token 本体)。
|
||||
export async function fetchKimiOauthStatus(): Promise<OAuthStatusResponse> {
|
||||
return getJson<OAuthStatusResponse>(
|
||||
"/settings/providers/kimi-code/oauth/status",
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchReviews(
|
||||
projectId: string,
|
||||
chapterNo: number,
|
||||
@@ -43,3 +69,66 @@ export async function fetchForeshadow(
|
||||
): Promise<ForeshadowBoardResponse> {
|
||||
return getJson<ForeshadowBoardResponse>(`/projects/${projectId}/foreshadow`);
|
||||
}
|
||||
|
||||
// 最新文风指纹(GET /style);无指纹(404)→ null(首学前页面照常渲染)。
|
||||
export async function fetchStyleFingerprint(
|
||||
projectId: string,
|
||||
): Promise<StyleFingerprintResponse | null> {
|
||||
return getJsonOrNull<StyleFingerprintResponse>(
|
||||
`/projects/${projectId}/style`,
|
||||
);
|
||||
}
|
||||
|
||||
// 规则列表(GET .../rules,规则页)。
|
||||
export async function fetchRules(
|
||||
projectId: string,
|
||||
): Promise<RuleListResponse> {
|
||||
return getJson<RuleListResponse>(`/projects/${projectId}/rules`);
|
||||
}
|
||||
|
||||
// 技能库注册表(GET /skills,全局只读)。
|
||||
export async function fetchSkills(): Promise<SkillListResponse> {
|
||||
return getJson<SkillListResponse>("/skills");
|
||||
}
|
||||
|
||||
// 设定库 Codex:跨会话全量已入库角色(GET .../characters;无行→空列表,非 404)。
|
||||
export async function fetchCharacters(
|
||||
projectId: string,
|
||||
): Promise<CharacterListResponse> {
|
||||
return getJson<CharacterListResponse>(`/projects/${projectId}/characters`);
|
||||
}
|
||||
|
||||
// 设定库 Codex:跨会话全量已入库世界观实体(GET .../world_entities;无行→空列表)。
|
||||
export async function fetchWorldEntities(
|
||||
projectId: string,
|
||||
): Promise<WorldEntityListResponse> {
|
||||
return getJson<WorldEntityListResponse>(
|
||||
`/projects/${projectId}/world_entities`,
|
||||
);
|
||||
}
|
||||
|
||||
// 已存大纲(GET .../outline,与 POST 同形,按 chapter_no 排序)。
|
||||
// 无大纲→空列表;任何错误亦降级为空(进页不阻塞,生成流照常工作)。
|
||||
export async function fetchOutline(
|
||||
projectId: string,
|
||||
): Promise<OutlineChapterView[]> {
|
||||
try {
|
||||
const res = await getJson<OutlineResponse>(
|
||||
`/projects/${projectId}/outline`,
|
||||
);
|
||||
return res.chapters ?? [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 已存草稿(GET .../draft,含正文,供工作台重访重载编辑器)。
|
||||
// 404(尚无草稿,新章)→ null,由调用方当空编辑器处理,不抛错。
|
||||
export async function fetchDraft(
|
||||
projectId: string,
|
||||
chapterNo: number,
|
||||
): Promise<DraftView | null> {
|
||||
return getJsonOrNull<DraftView>(
|
||||
`/projects/${projectId}/chapters/${chapterNo}/draft`,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user