feat(frontend): align skills bootstrap flow to titan contract
This commit is contained in:
parent
c8536c7da3
commit
021f6e3b3c
|
|
@ -1,34 +0,0 @@
|
|||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
const { normalizeBootstrapRemoteSkillRequest } = await import(
|
||||
new URL("./normalize-bootstrap.ts", import.meta.url).href
|
||||
);
|
||||
|
||||
void test("keeps content_ids as primary contract", () => {
|
||||
const normalized = normalizeBootstrapRemoteSkillRequest({
|
||||
thread_id: "t1",
|
||||
content_ids: [11, 22],
|
||||
});
|
||||
|
||||
assert.deepEqual(normalized.content_ids, [11, 22]);
|
||||
});
|
||||
|
||||
void test("maps legacy content_id to content_ids for compatibility", () => {
|
||||
const normalized = normalizeBootstrapRemoteSkillRequest({
|
||||
thread_id: "t1",
|
||||
content_id: 7,
|
||||
});
|
||||
|
||||
assert.deepEqual(normalized.content_ids, [7]);
|
||||
});
|
||||
|
||||
void test("throws when neither content_ids nor content_id is provided", () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
normalizeBootstrapRemoteSkillRequest({
|
||||
thread_id: "t1",
|
||||
}),
|
||||
/content_ids is required/,
|
||||
);
|
||||
});
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
import { getBackendBaseURL } from "@/core/config";
|
||||
|
||||
import {
|
||||
normalizeBootstrapRemoteSkillRequest,
|
||||
} from "./normalize-bootstrap";
|
||||
import type { Skill } from "./types";
|
||||
import type { Skill } from "./type";
|
||||
|
||||
export async function loadSkills() {
|
||||
const skills = await fetch(`${getBackendBaseURL()}/api/skills`);
|
||||
|
|
@ -38,6 +35,7 @@ export interface InstallSkillResponse {
|
|||
message: string;
|
||||
}
|
||||
|
||||
// [移植自 main 分支 ef9a071] 添加 skill yaml 解析和远程 skill 初始化 API
|
||||
export interface MaterializeSkillYamlRequest {
|
||||
thread_id: string;
|
||||
path: string;
|
||||
|
|
@ -55,9 +53,7 @@ export interface MaterializeSkillYamlResponse {
|
|||
|
||||
export interface BootstrapRemoteSkillRequest {
|
||||
thread_id: string;
|
||||
content_ids?: number[];
|
||||
// Legacy input, kept for minimal compatibility at the API boundary.
|
||||
content_id?: number;
|
||||
content_id: number;
|
||||
language_type?: number;
|
||||
target_dir?: string;
|
||||
clear_target?: boolean;
|
||||
|
|
@ -98,9 +94,14 @@ export async function installSkill(
|
|||
return response.json();
|
||||
}
|
||||
|
||||
// [移植自 main 分支 ef9a071] 解析 skill.yaml 文件并创建目录结构
|
||||
export async function materializeSkillYaml(
|
||||
request: MaterializeSkillYamlRequest,
|
||||
): Promise<MaterializeSkillYamlResponse> {
|
||||
console.log("[skills/api] ========== materializeSkillYaml START ==========");
|
||||
console.log("[skills/api] request:", JSON.stringify(request, null, 2));
|
||||
console.log("[skills/api] API URL:", `${getBackendBaseURL()}/api/skills/materialize-yaml`);
|
||||
|
||||
const response = await fetch(
|
||||
`${getBackendBaseURL()}/api/skills/materialize-yaml`,
|
||||
{
|
||||
|
|
@ -112,20 +113,35 @@ export async function materializeSkillYaml(
|
|||
},
|
||||
);
|
||||
|
||||
console.log("[skills/api] response status:", response.status, response.statusText);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
const errorMessage =
|
||||
errorData.detail ?? `HTTP ${response.status}: ${response.statusText}`;
|
||||
console.error("[skills/api] materializeSkillYaml FAILED:", errorMessage);
|
||||
console.error("[skills/api] error data:", errorData);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
const result = await response.json();
|
||||
console.log("[skills/api] materializeSkillYaml SUCCESS:", result);
|
||||
console.log("[skills/api] ========== materializeSkillYaml END ==========");
|
||||
return result;
|
||||
}
|
||||
|
||||
// [移植自 main 分支 ef9a071] 从远程平台获取 skill 并初始化
|
||||
export async function bootstrapRemoteSkill(
|
||||
request: BootstrapRemoteSkillRequest,
|
||||
): Promise<BootstrapRemoteSkillResponse> {
|
||||
const normalizedRequest = normalizeBootstrapRemoteSkillRequest(request);
|
||||
console.log("[skills/api] ========== bootstrapRemoteSkill START ==========");
|
||||
console.log("[skills/api] request:", JSON.stringify(request, null, 2));
|
||||
console.log("[skills/api] thread_id:", request.thread_id);
|
||||
console.log("[skills/api] content_id:", request.content_id);
|
||||
console.log("[skills/api] language_type:", request.language_type);
|
||||
console.log("[skills/api] target_dir:", request.target_dir);
|
||||
console.log("[skills/api] API URL:", `${getBackendBaseURL()}/api/skills/bootstrap-remote`);
|
||||
|
||||
const response = await fetch(
|
||||
`${getBackendBaseURL()}/api/skills/bootstrap-remote`,
|
||||
{
|
||||
|
|
@ -133,16 +149,26 @@ export async function bootstrapRemoteSkill(
|
|||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(normalizedRequest),
|
||||
body: JSON.stringify(request),
|
||||
},
|
||||
);
|
||||
|
||||
console.log("[skills/api] response status:", response.status, response.statusText);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
const errorMessage =
|
||||
errorData.detail ?? `HTTP ${response.status}: ${response.statusText}`;
|
||||
console.error("[skills/api] bootstrapRemoteSkill FAILED:", errorMessage);
|
||||
console.error("[skills/api] error data:", errorData);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
const result = await response.json();
|
||||
console.log("[skills/api] bootstrapRemoteSkill SUCCESS:", result);
|
||||
console.log("[skills/api] created_directories:", result.created_directories);
|
||||
console.log("[skills/api] created_files:", result.created_files);
|
||||
console.log("[skills/api] sandbox_id:", result.sandbox_id);
|
||||
console.log("[skills/api] ========== bootstrapRemoteSkill END ==========");
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
export interface BootstrapRemoteSkillRequestLike {
|
||||
thread_id: string;
|
||||
content_ids?: number[];
|
||||
content_id?: number;
|
||||
language_type?: number;
|
||||
target_dir?: string;
|
||||
clear_target?: boolean;
|
||||
}
|
||||
|
||||
export interface NormalizedBootstrapRemoteSkillRequest
|
||||
extends Omit<BootstrapRemoteSkillRequestLike, "content_id" | "content_ids"> {
|
||||
content_ids: number[];
|
||||
}
|
||||
|
||||
export function normalizeBootstrapRemoteSkillRequest(
|
||||
request: BootstrapRemoteSkillRequestLike,
|
||||
): NormalizedBootstrapRemoteSkillRequest {
|
||||
const normalizedContentIds = Array.isArray(request.content_ids)
|
||||
? request.content_ids
|
||||
.map((id) => Number(id))
|
||||
.filter((id) => Number.isFinite(id) && id > 0)
|
||||
: [];
|
||||
|
||||
const legacyContentId =
|
||||
request.content_id != null && Number.isFinite(Number(request.content_id))
|
||||
? Number(request.content_id)
|
||||
: undefined;
|
||||
|
||||
const contentIds =
|
||||
normalizedContentIds.length > 0
|
||||
? normalizedContentIds
|
||||
: legacyContentId != null
|
||||
? [legacyContentId]
|
||||
: [];
|
||||
|
||||
if (contentIds.length === 0) {
|
||||
throw new Error("content_ids is required.");
|
||||
}
|
||||
|
||||
return {
|
||||
...request,
|
||||
content_ids: contentIds,
|
||||
};
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type { Skill } from "./type";
|
||||
|
|
@ -79,7 +79,7 @@ export function useSelectedSkillListener({
|
|||
try {
|
||||
const result = await bootstrapRemoteSkill({
|
||||
thread_id: threadId,
|
||||
content_ids: [Number(id)],
|
||||
content_id: Number(id),
|
||||
language_type: languageType,
|
||||
target_dir: "/mnt/user-data/uploads/skill",
|
||||
clear_target: true,
|
||||
|
|
|
|||
Loading…
Reference in New Issue