import { invoke } from "@tauri-apps/api/core"; import { fetch as tauriFetch } from "@tauri-apps/plugin-http"; export interface Project { /** workspace.id(也是旧 projects.id) */ id: string; /** profile.id,跨空间共享的项目档案 */ profile_id?: string; /** 同 id,前端可用于语义区分 */ workspace_id?: string; /** 关联的 git_repos.id */ repo_id?: string; name: string; description?: string; type?: string; // ── profile 级(项目档案,跨空间共享)──────────────────────── status?: string; priority?: string; tech_stack?: string; tags?: string; repo_url?: string; upstream_url?: string; default_branch?: string; staging_url?: string; prod_url?: string; server_note?: string; // ── workspace 级(每空间独立)─────────────────────────────── space_id?: string; wsl_path?: string; win_path?: string; platform?: "windows" | "wsl"; recent_focus?: string; startup_commands?: string; last_push_at?: string; updated_at?: string; } export interface ProjectDetail extends Project { recent_activities: ActivityLog[]; } export interface ActivityLog { id: number; project_id: string; summary: string; created_at?: string; } export interface ProductGroup { id: string; name: string; description?: string; space_id?: string; sort_order: number; } export interface ProjectGroupMap { project_id: string; group_id: string; role?: string; } export interface EditorCandidate { name: string; cmd: string; path: string | null; found: boolean; } // ── Projects ────────────────────────────────────────────────────────────────── export const getProjects = (status?: string, spaceId?: string) => invoke("get_projects", { status, spaceId }); export const getProject = (id: string) => invoke("get_project", { id }); export const createProject = (input: Omit) => invoke("create_project", { input }); export const updateProject = (id: string, input: Partial>) => invoke("update_project", { id, input }); export const updateProjectTags = (id: string, tags: string) => invoke("update_project_tags", { id, tags }); export const deleteProject = (id: string, deleteLocal?: boolean) => invoke("delete_project", { id, deleteLocal }); export interface SubFolder { name: string; path: string } export const scanSubfolders = (dir: string) => invoke("scan_subfolders", { dir }); export interface BatchProjectInput { id: string; name: string; win_path?: string; wsl_path?: string; status: string; priority: string; space_id?: string; platform?: string; } export const batchCreateProjects = (items: BatchProjectInput[]) => invoke("batch_create_projects", { items }); // ── Activity ────────────────────────────────────────────────────────────────── export const getActivity = (projectId: string, limit?: number) => invoke("get_activity", { projectId, limit }); export const addActivity = (projectId: string, summary: string) => invoke("add_activity", { projectId, summary }); // ── Groups ──────────────────────────────────────────────────────────────────── export const getGroups = (spaceId?: string) => invoke("get_groups", { spaceId }); export const createGroup = (input: { id?: string; name: string; description?: string; space_id?: string }) => invoke("create_group", { input }); export const updateGroup = (id: string, input: { name: string; description?: string }) => invoke("update_group", { id, input }); export const deleteGroup = (id: string) => invoke("delete_group", { id }); /** 按 ids 数组顺序批量写入 sort_order */ export const reorderGroups = (ids: string[]) => invoke("reorder_groups", { ids }); export const getGroupMaps = () => invoke("get_group_maps"); export const addGroupMember = (projectId: string, groupId: string, role?: string) => invoke("add_group_member", { projectId, groupId, role }); export const removeGroupMember = (projectId: string, groupId: string) => invoke("remove_group_member", { projectId, groupId }); // ── Settings ────────────────────────────────────────────────────────────────── export const getSettings = (spaceId?: string) => invoke>("get_settings", { spaceId }); export const updateSettings = (settings: Record, spaceId?: string) => invoke("update_settings", { settings, spaceId }); export const detectEditors = () => invoke("detect_editors"); export const searchEditorPath = (cmd: string) => invoke("search_editor_path", { cmd }); export interface McpHealthResult { ok: boolean; port: number; latency_ms?: number; error?: string; } export const checkMcpHealth = () => invoke("check_mcp_health"); export interface HealthItem { id: string; name: string; status: "ok" | "warn" | "error" | "skip"; detail?: string; } export interface DormantProject { id: string; name: string; days_since_push: number; } export interface OrphanedProject { id: string; name: string; } export interface QualityMetrics { dormant_projects: DormantProject[]; orphaned_projects: OrphanedProject[]; groups_with_inject_warn: string[]; projects_without_path: number; } export interface HealthReport { checked_at: string; items: HealthItem[]; quality: QualityMetrics; } export const runHealthCheck = () => invoke("run_health_check"); // ── Project Tools ───────────────────────────────────────────────────────────── export interface ProjectTool { id: string; project_id: string; name: string; icon: string; exe_path: string; args: string | null; sort_order: number; } export interface ProjectToolInput { name: string; icon: string; exe_path: string; args?: string; sort_order?: number; } export const getProjectTools = (projectId: string) => invoke("get_project_tools", { projectId }); export const addProjectTool = (projectId: string, input: ProjectToolInput) => invoke("add_project_tool", { projectId, input }); export const updateProjectTool = (id: string, input: ProjectToolInput) => invoke("update_project_tool", { id, input }); export const removeProjectTool = (id: string) => invoke("remove_project_tool", { id }); export const launchTool = (exePath: string, args?: string) => invoke("launch_tool", { exePath, args }); // ── Global Quick Launch ─────────────────────────────────────────────────────── export const getGlobalTools = () => invoke("get_global_tools"); export const addGlobalTool = (input: ProjectToolInput) => invoke("add_global_tool", { input }); export const updateGlobalTool = (id: string, input: ProjectToolInput) => invoke("update_global_tool", { id, input }); export const removeGlobalTool = (id: string) => invoke("remove_global_tool", { id }); // ── Dev Tools ───────────────────────────────────────────────────────────────── export interface ScannedTool { id: string; name: string; category: string; cmd: string; path: string | null; version: string | null; found: boolean; } export type DevEnv = "windows" | "wsl"; export interface DevToolRecord { id: string; name: string; category: string; cmd: string | null; path: string | null; version: string | null; note: string | null; env: DevEnv; space_id?: string | null; } export interface SaveDevToolInput { id: string; name: string; category: string; cmd?: string; path?: string; version?: string; note?: string; env?: DevEnv; space_id?: string; } export const scanDevTools = () => invoke("scan_dev_tools"); export const scanWslDevTools = () => invoke("scan_wsl_dev_tools"); export const getDevTools = (env: DevEnv, spaceId?: string) => invoke("get_dev_tools", { env, spaceId }); export const saveDevTools = (tools: SaveDevToolInput[]) => invoke("save_dev_tools", { tools }); export const deleteDevTool = (id: string) => invoke("delete_dev_tool", { id }); export const upsertDevTool = (tool: SaveDevToolInput) => invoke("upsert_dev_tool", { tool }); // ── Project Env Scan ────────────────────────────────────────────────────────── export interface EnvToolItem { id: string; project_id: string; layer: number; name: string; source?: string; version?: string; note?: string; } export interface SaveEnvToolInput { id: string; layer: number; name: string; source?: string; version?: string; note?: string; } export const scanProjectEnv = (projectId: string, env: 'win' | 'wsl') => invoke('scan_project_env', { projectId, env }); export const getProjectEnvTools = (projectId: string) => invoke('get_project_env_tools', { projectId }); export const saveProjectEnvTools = (projectId: string, items: SaveEnvToolInput[]) => invoke('save_project_env_tools', { projectId, items }); // ── Shell ───────────────────────────────────────────────────────────────────── export const openEditor = (projectId: string, env: "wsl" | "win", editorPath: string) => invoke("open_editor", { projectId, env, editorPath }); export const openTerminal = (projectId: string, env: "wsl" | "win") => invoke("open_terminal", { projectId, env }); // ── GitHub 账户(DB 操作,Rust 侧)──────────────────────────────────────────── export interface GitAccount { id: string; username: string; avatar_url?: string; provider: string; created_at?: string; } export const githubGetAccount = () => invoke("github_get_account"); export const githubGetToken = () => invoke("github_get_token"); export const githubGetClientId = () => invoke("github_get_client_id"); export const githubSaveAccount = ( username: string, avatarUrl: string, accessToken: string ) => invoke("github_save_account", { username, avatarUrl, accessToken }); export const githubLogout = () => invoke("github_logout"); // ── GitHub API(前端 fetch)─────────────────────────────────────────────────── export interface GithubRepo { id: number; name: string; full_name: string; private: boolean; clone_url: string; ssh_url: string; description?: string; default_branch: string; } /** 打开应用内 GitHub 登录窗口(用户在窗口内完成授权,无需外部浏览器) */ export const githubOpenLoginWindow = () => invoke("github_open_login_window"); /** 轮询授权结果(null = 等待中,string = token) */ export const githubPollOAuth = () => invoke("github_poll_oauth"); /** 启动本地回调服务器,返回授权 URL(备用:外部浏览器方式) */ export const githubStartOAuth = () => invoke("github_start_oauth"); /** 用 token 获取 GitHub 用户信息 */ export async function githubFetchUser(token: string) { const resp = await tauriFetch("https://api.github.com/user", { headers: { Authorization: `Bearer ${token}`, "User-Agent": "dev-manager-tauri" }, }); if (!resp.ok) throw new Error("获取用户信息失败"); return resp.json() as Promise<{ login: string; avatar_url: string }>; } /** 获取用户的 GitHub 仓库列表 */ export async function githubListRepos(token: string): Promise { const resp = await tauriFetch( "https://api.github.com/user/repos?per_page=100&sort=updated&affiliation=owner", { headers: { Authorization: `Bearer ${token}`, "User-Agent": "dev-manager-tauri" } } ); if (!resp.ok) throw new Error("获取仓库列表失败"); return resp.json(); } // ── Spaces ──────────────────────────────────────────────────────────────────── export interface Space { id: string; name: string; is_current: boolean; created_at?: string; } export interface DeployCommand { id: string; project_id: string; name: string; command: string; run_in: "windows" | "wsl"; } export const getSpaces = () => invoke("get_spaces"); export const createSpace = (name: string) => invoke("create_space", { name }); export const updateSpace = (id: string, name: string) => invoke("update_space", { id, name }); export const deleteSpace = (id: string) => invoke("delete_space", { id }); export const setCurrentSpace = (id: string) => invoke("set_current_space", { id }); export const getDeployCommands = (projectId: string) => invoke("get_deploy_commands", { projectId }); export const addDeployCommand = ( projectId: string, name: string, command: string, runIn: "windows" | "wsl" ) => invoke("add_deploy_command", { projectId, name, command, runIn }); export const removeDeployCommand = (id: string) => invoke("remove_deploy_command", { id }); export const executeDeploy = (command: string, runIn: "windows" | "wsl", cwd?: string) => invoke("execute_deploy", { command, runIn, cwd }); // ── Git 操作 ────────────────────────────────────────────────────────────────── export interface GitStatus { branch: string; ahead: number; behind: number; has_changes: boolean; last_commit_msg?: string; last_commit_time?: string; } export const gitClone = (repoUrl: string, localPath: string, upstreamUrl?: string) => invoke("git_clone", { repoUrl, localPath, upstreamUrl }); export const gitStatus = (localPath: string) => invoke("git_status", { localPath }); export const gitFetch = (localPath: string) => invoke("git_fetch", { localPath }); export const gitPull = (localPath: string) => invoke("git_pull", { localPath }); export const gitPush = (localPath: string) => invoke("git_push", { localPath }); export const gitHeadCommit = (localPath: string) => invoke("git_head_commit", { localPath }); export const gitUpstreamBehind = (localPath: string, defaultBranch: string) => invoke("git_upstream_behind", { localPath, defaultBranch }); export const gitCreateBranch = (localPath: string, branchName: string) => invoke("git_create_branch", { localPath, branchName }); export const gitListBranches = (localPath: string) => invoke("git_list_branches", { localPath }); export const gitCheckoutBranch = (localPath: string, branchName: string) => invoke("git_checkout_branch", { localPath, branchName }); // ── WSL 代理同步 ────────────────────────────────────────────────────────────── export const getWslIp = () => invoke("get_wsl_ip"); /** 读取 v2rayN 代理端口,写入 WSL /etc/profile.d/v2rayn-proxy.sh */ export const syncWslProxy = (dbPath?: string) => invoke("sync_wsl_proxy", { dbPath }); export interface WslProxyStatus { configured: boolean; port?: number; /** /etc/environment 中记录的 IP 与当前网关不一致(Windows 重启后常见) */ stale?: boolean; configured_ip?: string; current_ip?: string; /** /etc/bash.bashrc 中的 source 行是否存在(非登录 shell 代理是否生效) */ bashrc_ok?: boolean; } export const getWslProxyStatus = () => invoke("get_wsl_proxy_status"); export const clearWslProxy = () => invoke("clear_wsl_proxy"); export const testWslProxyIp = () => invoke("test_wsl_proxy_ip"); export const testWinProxyIp = () => invoke("test_win_proxy_ip"); export interface ActiveV2raynNode { index_id: string; remarks: string; address: string; port: number; config_type: number; share_link: string; } export const getActiveV2raynNode = (dbPath?: string) => invoke("get_active_v2rayn_node", { dbPath }); // ── v2rayN 节点导入 ─────────────────────────────────────────────────────────── export interface V2raynNode { index_id: string; config_type: number; // 1=VMess 3=SS 5=VLESS 6=Trojan 7=Hysteria2 remarks: string; address: string; port: number; id?: string; // UUID security?: string; network?: string; stream_security?: string; flow?: string; sni?: string; fingerprint?: string; public_key?: string; short_id?: string; spider_x?: string; request_host?: string; path?: string; alter_id?: number; } export const CONFIG_TYPE_LABEL: Record = { 1: "VMess", 3: "Shadowsocks", 4: "Socks", 5: "VLESS", 6: "Trojan", 7: "Hysteria2", }; export const scanV2raynNodes = (dbPath?: string) => invoke("scan_v2rayn_nodes", { dbPath }); export const detectV2raynDbPath = () => invoke("detect_v2rayn_db_path"); // ── Git Repos Registry(仓库注册表)────────────────────────────────────────── export interface GitRepo { id: string; repo_url: string; name: string; description?: string; default_branch?: string; upstream_url?: string; staging_url?: string; prod_url?: string; server_note?: string; status?: string; priority?: string; tech_stack?: string; last_synced_at?: string; created_at?: string; } export interface ImportGitRepoInput { id: string; repo_url: string; name: string; description?: string; default_branch?: string; upstream_url?: string; tech_stack?: string; } export interface UpdateGitRepoDeployInput { staging_url?: string; prod_url?: string; server_note?: string; upstream_url?: string; tech_stack?: string; status?: string; priority?: string; } export interface MountRepoInput { workspace_id: string; repo_id: string; space_id?: string; win_path?: string; wsl_path?: string; platform?: string; } export const getGitRepos = () => invoke("get_git_repos"); export const importGitRepos = (repos: ImportGitRepoInput[]) => invoke("import_git_repos", { repos }); export const updateGitRepoDeploy = (id: string, input: UpdateGitRepoDeployInput) => invoke("update_git_repo_deploy", { id, input }); export const mountRepo = (input: MountRepoInput) => invoke("mount_repo", { input }); export const deleteGitRepo = (id: string) => invoke("delete_git_repo", { id }); // ── Publish to GitHub ───────────────────────────────────────────────────────── export interface ChangedFile { status: string; // "M" | "A" | "D" | "?" | "R" path: string; } export interface GitRemoteInfo { name: string; url: string; } export interface GitScanResult { has_git: boolean; has_commits: boolean; branch: string; has_changes: boolean; changed_files: ChangedFile[]; remotes: GitRemoteInfo[]; has_gitignore: boolean; suspicious_files: string[]; suggested_templates: string[]; } export const scanLocalGit = (path: string) => invoke("scan_local_git", { path }); export const githubCreateRepo = (name: string, description: string, privateRepo: boolean) => invoke("github_create_repo", { name, description, private: privateRepo }); export const gitInitAndCommit = (path: string, message: string) => invoke("git_init_and_commit", { path, message }); export const gitRemoteSet = (path: string, url: string) => invoke("git_remote_set", { path, url }); export const gitPushToGithub = (path: string, branch: string) => invoke("git_push_to_github", { path, branch }); /** 将 Linux WSL 路径转换为 Windows UNC 路径(\\wsl.localhost\distro\...) */ export const resolveWslPath = (path: string) => invoke("resolve_wsl_path", { path }); /** 列出已安装的 WSL 发行版 */ export const getWslDistros = () => invoke("get_wsl_distros"); // ── 项目模板 ────────────────────────────────────────────────────────────────── export interface TemplateFile { id: string; template_id: string; path: string; content: string; } export interface ProjectTemplate { id: string; name: string; description?: string; platform: string; // "win" | "wsl" | "both" techStack?: string; initCommands?: string; isBuiltin: boolean; createdAt?: string; files: TemplateFile[]; } export interface SaveTemplateInput { id?: string; name: string; description?: string; platform: string; techStack?: string; initCommands?: string; files: { path: string; content: string }[]; } export interface CreateProjectResult { projectId: string; logs: string[]; } export const listTemplates = () => invoke("list_templates"); export const getTemplate = (id: string) => invoke("get_template", { id }); export const saveTemplate = (input: SaveTemplateInput) => invoke("save_template", { input }); export const deleteTemplate = (id: string) => invoke("delete_template", { id }); export const createProjectFromTemplate = ( templateId: string, parentPath: string, projectName: string, platform: string, spaceId?: string, ) => invoke("create_project_from_template", { templateId, parentPath, projectName, platform, spaceId, }); export const registerAndLinkRepo = ( projectId: string, repoUrl: string, name: string, description?: string, defaultBranch?: string, ) => invoke("register_and_link_repo", { projectId, repoUrl, name, description, defaultBranch }); // ── 项目标签 ────────────────────────────────────────────────────────────────── export interface ProjectTag { id: string; name: string; color: string; } export interface ProfileTagMap { profile_id: string; tag_id: string; } export const getTags = () => invoke("get_tags"); export const createTag = (name: string, color: string) => invoke("create_tag", { name, color }); export const updateTag = (id: string, name: string, color: string) => invoke("update_tag", { id, name, color }); export const deleteTag = (id: string) => invoke("delete_tag", { id }); export const getProjectTags = (workspaceId: string) => invoke("get_project_tags", { workspaceId }); export const setProjectTags = (workspaceId: string, tagIds: string[]) => invoke("set_project_tags", { workspaceId, tagIds }); export const getAllProjectTagMaps = () => invoke("get_all_project_tag_maps"); // ── CI/CD 配置 ──────────────────────────────────────────────────────────────── export interface CicdConfig { id: string; project_id: string; /** 'web_ssh' | 'tauri_oss' */ config_type: string; trigger_branch: string; build_command?: string; node_version?: string; // web_ssh 专属 server_host?: string; server_user?: string; deploy_path?: string; start_command?: string; // tauri_oss 专属 oss_endpoint?: string; created_at?: string; } export interface WorkflowResult { content: string; path: string; secrets: string[]; } export const getCicdConfig = (projectId: string) => invoke("get_cicd_config", { projectId }); export const saveCicdConfig = (input: CicdConfig) => invoke("save_cicd_config", { input }); /** 生成 workflow 文件写入磁盘,projectPath 必须是 Windows 可访问路径 */ export const generateWorkflow = (projectId: string, projectPath: string) => invoke("generate_workflow", { projectId, projectPath }); // ── GitHub Actions API(前端直接调用,使用已存储的 token)──────────────────── export interface ActionsRun { id: number; name: string; status: string; // "queued" | "in_progress" | "completed" conclusion: string | null; // "success" | "failure" | "cancelled" | null head_branch: string; created_at: string; updated_at: string; html_url: string; run_number: number; } /** 解析 repo_url 为 {owner}/{repo} 格式,失败返回 null */ export function parseRepoPath(repoUrl: string): string | null { const m = repoUrl.match(/github\.com[/:](.+?)(?:\.git)?$/); return m ? m[1] : null; } /** 获取最近 N 次 Actions run */ export async function fetchActionsRuns( token: string, repoPath: string, perPage = 5 ): Promise { const res = await fetch( `https://api.github.com/repos/${repoPath}/actions/runs?per_page=${perPage}`, { headers: { Authorization: `Bearer ${token}`, "User-Agent": "dev-manager-tauri" } } ); if (!res.ok) throw new Error(`GitHub API ${res.status}`); const data = await res.json(); return data.workflow_runs ?? []; } /** 手动触发 workflow_dispatch */ export async function triggerWorkflow( token: string, repoPath: string, workflowFile: string, branch: string ): Promise { const res = await fetch( `https://api.github.com/repos/${repoPath}/actions/workflows/${workflowFile}/dispatches`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "User-Agent": "dev-manager-tauri", "Content-Type": "application/json", }, body: JSON.stringify({ ref: branch }), } ); if (!res.ok && res.status !== 204) throw new Error(`触发失败: ${res.status}`); } /** 根据节点数据构造 share link(前端完成,避免 Rust 侧 URL 编码依赖) */ export function buildShareLink(node: V2raynNode): string { const remarks = encodeURIComponent(node.remarks); if (node.config_type === 5) { // VLESS const uuid = node.id ?? ""; const params = new URLSearchParams(); params.set("encryption", "none"); params.set("security", node.stream_security ?? "none"); if (node.network) params.set("type", node.network); if (node.stream_security === "reality" || node.stream_security === "tls") { if (node.sni) params.set("sni", node.sni); if (node.fingerprint) params.set("fp", node.fingerprint); } if (node.stream_security === "reality") { if (node.public_key) params.set("pbk", node.public_key); if (node.short_id) params.set("sid", node.short_id); if (node.spider_x) params.set("spx", node.spider_x); } if (node.flow) params.set("flow", node.flow); if (node.request_host) params.set("host", node.request_host); if (node.path) params.set("path", node.path); return `vless://${uuid}@${node.address}:${node.port}?${params.toString()}#${remarks}`; } if (node.config_type === 6) { // Trojan const params = new URLSearchParams(); params.set("security", node.stream_security ?? "tls"); if (node.sni) params.set("sni", node.sni); if (node.fingerprint) params.set("fp", node.fingerprint); if (node.network) params.set("type", node.network); return `trojan://${node.id ?? ""}@${node.address}:${node.port}?${params.toString()}#${remarks}`; } if (node.config_type === 1) { // VMess — base64 JSON 格式 const obj = { v: "2", ps: node.remarks, add: node.address, port: String(node.port), id: node.id ?? "", aid: String(node.alter_id ?? 0), scy: node.security ?? "auto", net: node.network ?? "tcp", type: "none", host: node.request_host ?? "", path: node.path ?? "", tls: node.stream_security ?? "", sni: node.sni ?? "", fp: node.fingerprint ?? "", }; return `vmess://${btoa(JSON.stringify(obj))}`; } return ""; } // ── Blueprint ────────────────────────────────────────────────────────────── export interface BlueprintTask { title: string; status: string; complexity?: string; files?: string; depends?: string; acceptance?: string; notes?: string; blocked_reason?: string; locked?: boolean; } export interface BlueprintModule { id: string; name: string; area: string; status: string; progress: number; position: number[]; description?: string; decisions: string[]; tasks: BlueprintTask[]; } export interface BlueprintEdge { from: string; to: string; edge_type: string; } export interface BlueprintArea { id: string; name: string; color?: string; } export interface BlueprintStats { total_modules: number; done: number; in_progress: number; planned: number; concept: number; blocked: number; total_tasks: number; tasks_done: number; tasks_blocked: number; tasks_locked: number; dispatchable: number; } export interface BlueprintData { manifest: { version: number; name: string; description?: string; updated?: string; /** 当前迭代号,默认 1,重构已完成模块时递增 */ iteration?: number; areas: BlueprintArea[]; modules: BlueprintModule[]; edges: BlueprintEdge[]; }; stats: BlueprintStats; } export const getBlueprint = (projectPath: string) => invoke("get_blueprint", { projectPath }); export const generateBlueprintPrompt = (projectPath: string, promptType: "init" | "sync") => invoke("generate_blueprint_prompt", { projectPath, promptType }); export interface BlueprintStatus { status: "none" | "rules_outdated" | "content_stale" | "synced"; conventions_version?: string; claude_md_managed: boolean; claude_md_version?: string; manifest_updated?: string; master_version: string; } export interface SyncResult { success: boolean; conventions_written: boolean; claude_md_action: string; message: string; } export const getBlueprintStatus = (projectPath: string) => invoke("get_blueprint_status", { projectPath }); export const syncBlueprintRules = (projectPath: string) => invoke("sync_blueprint_rules", { projectPath }); export interface FlywheelCohort { conventions_version: string; project_count: number; /** 可派发率:dispatched(可派发数) / total tasks(最新快照均值) */ dispatchable_rate: number; /** 任务完成率:done / total tasks(最新快照均值,反映整体进度) */ completion_rate: number; /** 飞轮持续率:30天内有快照的项目比例 */ continuity_rate: number; /** 阻塞密度:blocked / total tasks(最新快照均值) */ blocked_density: number; } export interface BlockedReasonCount { reason: string; count: number; } export interface StalledProject { name: string; /** UTC 日期字符串 "YYYY-MM-DD",未知时为 "unknown" */ last_active: string; } export interface IterationJump { project: string; from_iteration: number; to_iteration: number; /** 跳变前最后一条快照的任务完成率 */ rate_before: number; /** 跳变后第一条快照的任务完成率 */ rate_after: number; } export interface FlywheelStats { cohorts: FlywheelCohort[]; top_blocked_reasons: BlockedReasonCount[]; /** 飞轮停转项目(60天无快照) */ stalled_projects: StalledProject[]; /** AI文档休眠项目(90天无快照) */ ai_doc_stale_projects: StalledProject[]; /** 迭代跳变记录(iteration 递增,说明发生了主动重构) */ iteration_jumps: IterationJump[]; /** 停滞模块(in_progress 但无可推进任务,格式 "project/module_id") */ stalled_modules: string[]; total_projects_scanned: number; projects_with_data: number; } export const getFlywheelStats = (projectPaths: string[]) => invoke("get_flywheel_stats", { projectPaths }); export const generateMuhePrompt = (stats: FlywheelStats) => invoke("generate_muhe_prompt", { stats }); export interface ArchiveResult { archived_files: string[]; archive_dir: string; } export const archiveProjectDocs = (projectPath: string) => invoke("archive_project_docs", { projectPath }); // ── Canvas State ─────────────────────────────────────────────────────────────── export const getCanvasState = (groupId: string) => invoke("get_canvas_state", { groupId }); export const saveCanvasState = (groupId: string, data: string) => invoke("save_canvas_state", { groupId, data }); // ── Mentor ──────────────────────────────────────────────────────────────────── export interface ProjectHealth { path_valid: boolean; has_git: boolean; last_commit?: string; tech_stack?: string; } export interface BlueprintSummary { module_count: number; done_count: number; in_progress: number; planned: number; total_tasks: number; done_tasks: number; dispatchable: number; } export interface RuntimeLogEntry { ts: string; category: string; level: string; message: string; } export interface MentorContext { project_id: string; project_name: string; health: ProjectHealth; blueprint_summary?: BlueprintSummary; recent_errors: RuntimeLogEntry[]; mentor_markdown: string; } export const getMentorContext = (projectId: string) => invoke("get_mentor_context", { projectId }); export const getGroupMentorReport = (groupId: string) => invoke("get_group_mentor_report", { groupId }); export interface StartupAlert { project_name: string; alert_type: string; detail: string; } export const getStartupAlerts = () => invoke("get_startup_alerts"); export interface ProjectNote { id: number; project_id: string; source: string; content: string; created_at: string; } export const getProjectNotes = (projectId: string) => invoke("get_project_notes", { projectId }); export const addProjectNote = (projectId: string, content: string, source: string = "manual") => invoke("add_project_note", { projectId, content, source }); // ── Blueprint Buff ──────────────────────────────────────────────────────────── export interface BuffStatus { project_path: string; project_id: string; applied_at: string; last_sync_at?: string; last_commit?: string; status: string; } export const applyBlueprintBuff = (projectPath: string, projectId: string) => invoke("apply_blueprint_buff", { projectPath, projectId }); export const removeBlueprintBuff = (projectPath: string) => invoke("remove_blueprint_buff", { projectPath }); export const getBuffStatus = (projectPath: string) => invoke("get_buff_status", { projectPath }); export const listBuffedProjects = () => invoke("list_buffed_projects");