import { apiRequest, ApiRequestError, createUuid, type ApiErrorEnvelope, } from "./_shared"; export type ScriptType = "python" | "notebook"; export type Visibility = "private" | "workspace" | "public"; export type ScriptItem = { script_id: string; workspace_id: string; current_object_id: string; owner_user_id: string; owner_display_name: string | null; script_name: string; script_type: ScriptType; visibility: Visibility; status: string; is_locked: boolean; relative_path: string; jupyter_path: string; content_hash: string; size_bytes: number; created_at: string; updated_at: string; }; export async function listScripts( workspaceId: string, parentPath: string = "", ownerUserId?: string, ): Promise { // Default (no ownerUserId) scopes to the requester's own subtree; passing // ownerUserId scopes to that owner's subtree (workspace/public only — the // backend excludes their private) so the tree can lazily fetch another // member's content when their group is expanded. const parameters = new URLSearchParams(); if (parentPath) parameters.set("parent_path", parentPath); if (ownerUserId) parameters.set("owner_user_id", ownerUserId); const query = parameters.toString(); return apiRequest( `/api/v1/scripts${query ? `?${query}` : ""}`, {}, workspaceId, ); } export async function countScripts( workspaceId: string, ): Promise { // Backend route /api/v1/scripts/count must be declared BEFORE the // /scripts/{script_id} route on the server side. Returns // { data: { total: number } } — the dashboard's single source of // truth for "total active scripts in workspace", independent of the // lazy-loaded scripts[] in the workspace store. const envelope = await apiRequest<{ total: number }>( "/api/v1/scripts/count", {}, workspaceId, ); return envelope.total; } function initialContent(scriptType: ScriptType): string { if (scriptType === "python") { return [ '"""模型实验开发平台构建脚本。"""', "", "", "def main() -> None:", ' print("Hello, Model Platform!")', "", "", 'if __name__ == "__main__":', " main()", "", ].join("\n"); } return JSON.stringify( { cells: [ { id: "intro", cell_type: "code", execution_count: null, metadata: {}, outputs: [], source: ["print('Hello, Model Platform!')\n"], }, ], metadata: { kernelspec: { display_name: "Python 3", language: "python", name: "python3", }, language_info: { name: "python", version: "3.12", }, }, nbformat: 4, nbformat_minor: 5, }, null, 2, ); } export async function createScript( workspaceId: string, input: { name: string; scriptType: ScriptType; visibility: Visibility; parentPath?: string | null; }, ): Promise { return apiRequest( "/api/v1/scripts", { method: "POST", body: JSON.stringify({ script_name: input.name.trim(), script_type: input.scriptType, visibility: input.visibility, content: initialContent(input.scriptType), parent_path: input.parentPath, }), }, workspaceId, ); } export async function uploadScript( workspaceId: string, file: File, parentPath = "", visibility: Visibility = "workspace", ): Promise { const parameters = new URLSearchParams({ file_name: file.name, parent_path: parentPath, visibility, }); return apiRequest( `/api/v1/scripts/upload?${parameters.toString()}`, { method: "POST", headers: { "Content-Type": "application/octet-stream" }, body: file, }, workspaceId, ); } export async function setScriptLock( workspaceId: string, scriptId: string, isLocked: boolean, ): Promise { return apiRequest( `/api/v1/scripts/${encodeURIComponent(scriptId)}/lock`, { method: "PATCH", body: JSON.stringify({ is_locked: isLocked }), }, workspaceId, ); } export async function updateScript( workspaceId: string, scriptId: string, input: { content: string }, ): Promise { return apiRequest( `/api/v1/scripts/${scriptId}`, { method: "PUT", body: JSON.stringify(input) }, workspaceId, ); } export async function getScriptContent( workspaceId: string, scriptId: string, ): Promise<{ script_id: string; script_type: ScriptType; content: string | object; format: string; }> { const response = await fetch( `/api/v1/scripts/${scriptId}/content?workspace_id=${encodeURIComponent(workspaceId)}`, { credentials: "same-origin", headers: { "X-Request-ID": createUuid().replaceAll("-", ""), }, }, ); if (response.status === 401 && typeof window !== "undefined") { const here = window.location.pathname; if (here !== "/login") { window.location.assign("/login"); } throw new ApiRequestError("未登录或登录已过期", 401); } const payload = await response.json(); if (!response.ok) { const error = payload as ApiErrorEnvelope; const detailMessage = typeof error.detail === "string" ? error.detail : error.detail?.message; throw new ApiRequestError( detailMessage ?? `请求失败(HTTP ${response.status})`, response.status, ); } const data = (payload as { data: { script_id: string; script_type: ScriptType; content: string | object; format: string } }).data; if (!data || !data.script_type) { throw new ApiRequestError("响应数据格式错误", response.status); } return data; } export async function deleteScript( workspaceId: string, scriptId: string, ): Promise<{ script_id: string; status: string; versions_preserved: boolean }> { return apiRequest( `/api/v1/scripts/${scriptId}`, { method: "DELETE" }, workspaceId, ); } export type StableVersion = { versions_id: string; workspace_id: string; script_id: string; source_object_id: string; artifact_object_id: string; version_no: number; version_label: string; source_path: string; artifact_path: string; content_hash: string; file_size_bytes: number; visibility: Visibility; release_note: string | null; created_by: string; created_at: string; }; export type LatestVersion = { versions_id: string; version_label: string; }; export async function getLatestScriptVersion( workspaceId: string, scriptId: string, ): Promise { return apiRequest( `/api/v1/scripts/${scriptId}/latest-version`, {}, workspaceId, ); } export async function publishScriptVersion( workspaceId: string, input: { script: ScriptItem; releaseNote: string; visibility: Visibility; }, ): Promise { return apiRequest( `/api/v1/scripts/${input.script.script_id}/versions`, { method: "POST", body: JSON.stringify({ source_object_id: input.script.current_object_id, release_note: input.releaseNote.trim() || null, visibility: input.visibility, }), }, workspaceId, ); }