dev-manager-tauri/src/lib/commands.ts
lanrtop fea43b0b8a feat: 仪表盘全局快启栏
新增 global_tools 表及 CRUD 命令,Dashboard 顶部增加独立的「快启」工具栏,
可配置与项目无关的日常应用。ProjectToolsModal 重构为通用组件,支持注入
自定义 commands / queryKey;ProjectCard 快启行始终显示并内联添加按钮。
2026-04-07 00:46:13 +09:00

1161 lines
36 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Project[]>("get_projects", { status, spaceId });
export const getProject = (id: string) =>
invoke<ProjectDetail>("get_project", { id });
export const createProject = (input: Omit<Project, "updated_at" | "workspace_id">) =>
invoke<void>("create_project", { input });
export const updateProject = (id: string, input: Partial<Omit<Project, "id" | "updated_at">>) =>
invoke<void>("update_project", { id, input });
export const updateProjectTags = (id: string, tags: string) =>
invoke<void>("update_project_tags", { id, tags });
export const deleteProject = (id: string, deleteLocal?: boolean) =>
invoke<void>("delete_project", { id, deleteLocal });
export interface SubFolder { name: string; path: string }
export const scanSubfolders = (dir: string) =>
invoke<SubFolder[]>("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<string[]>("batch_create_projects", { items });
// ── Activity ──────────────────────────────────────────────────────────────────
export const getActivity = (projectId: string, limit?: number) =>
invoke<ActivityLog[]>("get_activity", { projectId, limit });
export const addActivity = (projectId: string, summary: string) =>
invoke<void>("add_activity", { projectId, summary });
// ── Groups ────────────────────────────────────────────────────────────────────
export const getGroups = (spaceId?: string) => invoke<ProductGroup[]>("get_groups", { spaceId });
export const createGroup = (input: { id?: string; name: string; description?: string; space_id?: string }) =>
invoke<void>("create_group", { input });
export const updateGroup = (id: string, input: { name: string; description?: string }) =>
invoke<void>("update_group", { id, input });
export const deleteGroup = (id: string) => invoke<void>("delete_group", { id });
/** 按 ids 数组顺序批量写入 sort_order */
export const reorderGroups = (ids: string[]) => invoke<void>("reorder_groups", { ids });
export const getGroupMaps = () => invoke<ProjectGroupMap[]>("get_group_maps");
export const addGroupMember = (projectId: string, groupId: string, role?: string) =>
invoke<string | null>("add_group_member", { projectId, groupId, role });
export const removeGroupMember = (projectId: string, groupId: string) =>
invoke<void>("remove_group_member", { projectId, groupId });
// ── Settings ──────────────────────────────────────────────────────────────────
export const getSettings = (spaceId?: string) =>
invoke<Record<string, string>>("get_settings", { spaceId });
export const updateSettings = (settings: Record<string, string>, spaceId?: string) =>
invoke<void>("update_settings", { settings, spaceId });
export const detectEditors = () => invoke<EditorCandidate[]>("detect_editors");
export const searchEditorPath = (cmd: string) =>
invoke<string | null>("search_editor_path", { cmd });
export interface McpHealthResult {
ok: boolean;
port: number;
latency_ms?: number;
error?: string;
}
export const checkMcpHealth = () => invoke<McpHealthResult>("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<HealthReport>("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<ProjectTool[]>("get_project_tools", { projectId });
export const addProjectTool = (projectId: string, input: ProjectToolInput) =>
invoke<string>("add_project_tool", { projectId, input });
export const updateProjectTool = (id: string, input: ProjectToolInput) =>
invoke<void>("update_project_tool", { id, input });
export const removeProjectTool = (id: string) =>
invoke<void>("remove_project_tool", { id });
export const launchTool = (exePath: string, args?: string) =>
invoke<void>("launch_tool", { exePath, args });
// ── Global Quick Launch ───────────────────────────────────────────────────────
export const getGlobalTools = () =>
invoke<ProjectTool[]>("get_global_tools");
export const addGlobalTool = (input: ProjectToolInput) =>
invoke<string>("add_global_tool", { input });
export const updateGlobalTool = (id: string, input: ProjectToolInput) =>
invoke<void>("update_global_tool", { id, input });
export const removeGlobalTool = (id: string) =>
invoke<void>("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<ScannedTool[]>("scan_dev_tools");
export const scanWslDevTools = () =>
invoke<ScannedTool[]>("scan_wsl_dev_tools");
export const getDevTools = (env: DevEnv, spaceId?: string) =>
invoke<DevToolRecord[]>("get_dev_tools", { env, spaceId });
export const saveDevTools = (tools: SaveDevToolInput[]) =>
invoke<void>("save_dev_tools", { tools });
export const deleteDevTool = (id: string) =>
invoke<void>("delete_dev_tool", { id });
export const upsertDevTool = (tool: SaveDevToolInput) =>
invoke<void>("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<EnvToolItem[]>('scan_project_env', { projectId, env });
export const getProjectEnvTools = (projectId: string) =>
invoke<EnvToolItem[]>('get_project_env_tools', { projectId });
export const saveProjectEnvTools = (projectId: string, items: SaveEnvToolInput[]) =>
invoke<void>('save_project_env_tools', { projectId, items });
// ── Shell ─────────────────────────────────────────────────────────────────────
export const openEditor = (projectId: string, env: "wsl" | "win", editorPath: string) =>
invoke<string>("open_editor", { projectId, env, editorPath });
export const openTerminal = (projectId: string, env: "wsl" | "win") =>
invoke<string>("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<GitAccount | null>("github_get_account");
export const githubGetToken = () =>
invoke<string | null>("github_get_token");
export const githubGetClientId = () =>
invoke<string>("github_get_client_id");
export const githubSaveAccount = (
username: string,
avatarUrl: string,
accessToken: string
) => invoke<GitAccount>("github_save_account", { username, avatarUrl, accessToken });
export const githubLogout = () =>
invoke<void>("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<void>("github_open_login_window");
/** 轮询授权结果null = 等待中string = token */
export const githubPollOAuth = () => invoke<string | null>("github_poll_oauth");
/** 启动本地回调服务器,返回授权 URL备用外部浏览器方式 */
export const githubStartOAuth = () => invoke<string>("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<GithubRepo[]> {
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<Space[]>("get_spaces");
export const createSpace = (name: string) =>
invoke<Space>("create_space", { name });
export const updateSpace = (id: string, name: string) =>
invoke<void>("update_space", { id, name });
export const deleteSpace = (id: string) =>
invoke<void>("delete_space", { id });
export const setCurrentSpace = (id: string) =>
invoke<void>("set_current_space", { id });
export const getDeployCommands = (projectId: string) =>
invoke<DeployCommand[]>("get_deploy_commands", { projectId });
export const addDeployCommand = (
projectId: string,
name: string,
command: string,
runIn: "windows" | "wsl"
) => invoke<DeployCommand>("add_deploy_command", { projectId, name, command, runIn });
export const removeDeployCommand = (id: string) =>
invoke<void>("remove_deploy_command", { id });
export const executeDeploy = (command: string, runIn: "windows" | "wsl", cwd?: string) =>
invoke<string>("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<void>("git_clone", { repoUrl, localPath, upstreamUrl });
export const gitStatus = (localPath: string) =>
invoke<GitStatus>("git_status", { localPath });
export const gitFetch = (localPath: string) =>
invoke<void>("git_fetch", { localPath });
export const gitPull = (localPath: string) =>
invoke<string>("git_pull", { localPath });
export const gitPush = (localPath: string) =>
invoke<string>("git_push", { localPath });
export const gitHeadCommit = (localPath: string) =>
invoke<string | null>("git_head_commit", { localPath });
export const gitUpstreamBehind = (localPath: string, defaultBranch: string) =>
invoke<number>("git_upstream_behind", { localPath, defaultBranch });
export const gitCreateBranch = (localPath: string, branchName: string) =>
invoke<void>("git_create_branch", { localPath, branchName });
export const gitListBranches = (localPath: string) =>
invoke<string[]>("git_list_branches", { localPath });
export const gitCheckoutBranch = (localPath: string, branchName: string) =>
invoke<void>("git_checkout_branch", { localPath, branchName });
// ── WSL 代理同步 ──────────────────────────────────────────────────────────────
export const getWslIp = () => invoke<string>("get_wsl_ip");
/** 读取 v2rayN 代理端口,写入 WSL /etc/profile.d/v2rayn-proxy.sh */
export const syncWslProxy = (dbPath?: string) =>
invoke<string>("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<WslProxyStatus>("get_wsl_proxy_status");
export const clearWslProxy = () => invoke<void>("clear_wsl_proxy");
export const testWslProxyIp = () => invoke<string>("test_wsl_proxy_ip");
export const testWinProxyIp = () => invoke<string>("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<ActiveV2raynNode>("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<number, string> = {
1: "VMess",
3: "Shadowsocks",
4: "Socks",
5: "VLESS",
6: "Trojan",
7: "Hysteria2",
};
export const scanV2raynNodes = (dbPath?: string) =>
invoke<V2raynNode[]>("scan_v2rayn_nodes", { dbPath });
export const detectV2raynDbPath = () =>
invoke<string | null>("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<GitRepo[]>("get_git_repos");
export const importGitRepos = (repos: ImportGitRepoInput[]) =>
invoke<string[]>("import_git_repos", { repos });
export const updateGitRepoDeploy = (id: string, input: UpdateGitRepoDeployInput) =>
invoke<void>("update_git_repo_deploy", { id, input });
export const mountRepo = (input: MountRepoInput) =>
invoke<void>("mount_repo", { input });
export const deleteGitRepo = (id: string) =>
invoke<void>("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<GitScanResult>("scan_local_git", { path });
export const githubCreateRepo = (name: string, description: string, privateRepo: boolean) =>
invoke<string>("github_create_repo", { name, description, private: privateRepo });
export const gitInitAndCommit = (path: string, message: string) =>
invoke<void>("git_init_and_commit", { path, message });
export const gitRemoteSet = (path: string, url: string) =>
invoke<void>("git_remote_set", { path, url });
export const gitPushToGithub = (path: string, branch: string) =>
invoke<void>("git_push_to_github", { path, branch });
/** 将 Linux WSL 路径转换为 Windows UNC 路径(\\wsl.localhost\distro\... */
export const resolveWslPath = (path: string) =>
invoke<string>("resolve_wsl_path", { path });
/** 列出已安装的 WSL 发行版 */
export const getWslDistros = () =>
invoke<string[]>("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<ProjectTemplate[]>("list_templates");
export const getTemplate = (id: string) =>
invoke<ProjectTemplate>("get_template", { id });
export const saveTemplate = (input: SaveTemplateInput) =>
invoke<string>("save_template", { input });
export const deleteTemplate = (id: string) =>
invoke<void>("delete_template", { id });
export const createProjectFromTemplate = (
templateId: string,
parentPath: string,
projectName: string,
platform: string,
spaceId?: string,
) =>
invoke<CreateProjectResult>("create_project_from_template", {
templateId,
parentPath,
projectName,
platform,
spaceId,
});
export const registerAndLinkRepo = (
projectId: string,
repoUrl: string,
name: string,
description?: string,
defaultBranch?: string,
) => invoke<void>("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<ProjectTag[]>("get_tags");
export const createTag = (name: string, color: string) => invoke<string>("create_tag", { name, color });
export const updateTag = (id: string, name: string, color: string) => invoke<void>("update_tag", { id, name, color });
export const deleteTag = (id: string) => invoke<void>("delete_tag", { id });
export const getProjectTags = (workspaceId: string) => invoke<ProjectTag[]>("get_project_tags", { workspaceId });
export const setProjectTags = (workspaceId: string, tagIds: string[]) => invoke<void>("set_project_tags", { workspaceId, tagIds });
export const getAllProjectTagMaps = () => invoke<ProfileTagMap[]>("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<CicdConfig | null>("get_cicd_config", { projectId });
export const saveCicdConfig = (input: CicdConfig) =>
invoke<CicdConfig>("save_cicd_config", { input });
/** 生成 workflow 文件写入磁盘projectPath 必须是 Windows 可访问路径 */
export const generateWorkflow = (projectId: string, projectPath: string) =>
invoke<WorkflowResult>("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<ActionsRun[]> {
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<void> {
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<BlueprintData | null>("get_blueprint", { projectPath });
export const generateBlueprintPrompt = (projectPath: string, promptType: "init" | "sync") =>
invoke<string>("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<BlueprintStatus>("get_blueprint_status", { projectPath });
export const syncBlueprintRules = (projectPath: string) =>
invoke<SyncResult>("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<FlywheelStats>("get_flywheel_stats", { projectPaths });
export const generateMuhePrompt = (stats: FlywheelStats) =>
invoke<string>("generate_muhe_prompt", { stats });
export interface ArchiveResult {
archived_files: string[];
archive_dir: string;
}
export const archiveProjectDocs = (projectPath: string) =>
invoke<ArchiveResult>("archive_project_docs", { projectPath });
// ── Canvas State ───────────────────────────────────────────────────────────────
export const getCanvasState = (groupId: string) =>
invoke<string | null>("get_canvas_state", { groupId });
export const saveCanvasState = (groupId: string, data: string) =>
invoke<void>("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<MentorContext>("get_mentor_context", { projectId });
export const getGroupMentorReport = (groupId: string) =>
invoke<string>("get_group_mentor_report", { groupId });
export interface StartupAlert {
project_name: string;
alert_type: string;
detail: string;
}
export const getStartupAlerts = () =>
invoke<StartupAlert[]>("get_startup_alerts");
export interface ProjectNote {
id: number;
project_id: string;
source: string;
content: string;
created_at: string;
}
export const getProjectNotes = (projectId: string) =>
invoke<ProjectNote[]>("get_project_notes", { projectId });
export const addProjectNote = (projectId: string, content: string, source: string = "manual") =>
invoke<void>("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<BuffStatus>("apply_blueprint_buff", { projectPath, projectId });
export const removeBlueprintBuff = (projectPath: string) =>
invoke<void>("remove_blueprint_buff", { projectPath });
export const getBuffStatus = (projectPath: string) =>
invoke<BuffStatus | null>("get_buff_status", { projectPath });
export const listBuffedProjects = () =>
invoke<BuffStatus[]>("list_buffed_projects");