dev-manager-tauri/src/lib/commands.ts
lanrtop 97578b2261
All checks were successful
Push & PR Check / check (push) Successful in 1m27s
Push & PR Check / check (pull_request) Successful in 1m6s
feat(onboard/parity): 一键接入 MCP 与 Tauri 投影、Codex 信任计 pending_user 与仓库布局说明(P8e)
- MCP 新增 plan_onboarding(readOnly)与 onboard_project(幂等写操作):schema 各自单独成函数,
  dispatch 一行委托到 mcp_inject 生产入口(中间只做 JSON 类型解析);全局与项目路由都可见,
  项目路由强制本项目、跨项目返回 scope_violation;onboard_project required 含 expected_digest
  与 confirmed;共用可选选项 bypass_permissions / skip_targets;进 STRUCTURED_TOOLS 并给一行摘要
- Tauri 新增同名 async 命令(async fn + spawn_blocking),commands.ts 同步 OnboardOptions、
  OnboardingPlan、OnboardReport 类型与 planOnboarding / onboardProject 封装
- 统筹决定(P8d 审查 ⑩):L1 Codex 列仅因 codex_project_untrusted 未就绪的格不计入 done,
  completion 单列「Codex 信任」(pending_user),next_actions 保留 codex_trust;仓库子目录与
  worktree 补 next_action repo_layout 说明为何到不了 done;非 git 维持不能 done
- 契约同步:project-context.yaml、agent-button-system.md、AGENTS.md 架构速查;P8d 卡记统筹决定落地
- cargo test 413 passed / 0 failed / 6 ignored;pnpm typecheck、pnpm test 通过;三轮 10 处变异全部被杀

Feature-Confirmed: true
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 19:50:21 +09:00

2405 lines
73 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";
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 type AgentRuntimeState = "ready" | "missing" | "unavailable" | "protocol_error";
export interface AgentRuntimeLayer {
state: AgentRuntimeState;
detail: string;
required?: boolean;
}
export interface AgentRuntimeStatus {
project_id: string;
ready: boolean;
configured: AgentRuntimeLayer;
reachable: AgentRuntimeLayer;
handshake: AgentRuntimeLayer;
tools_ready: AgentRuntimeLayer;
reload_required: AgentRuntimeLayer;
}
export const getAgentRuntimeStatus = (projectId: string) =>
invoke<AgentRuntimeStatus>("get_agent_runtime_status", { projectId });
export interface AgentAutostartStatus {
supported: boolean;
eligible: boolean;
enabled: boolean;
build_kind: "debug" | "release_uninstalled" | "installed_release" | "unsupported";
executable: string;
detail: string;
}
export const getAgentAutostartStatus = () =>
invoke<AgentAutostartStatus>("get_agent_autostart_status");
export const setAgentAutostart = (enabled: boolean) =>
invoke<AgentAutostartStatus>("set_agent_autostart", { enabled });
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 });
export interface RuntimeToolStatus {
tool: string;
declared_version?: string;
declared_source?: string;
actual_version?: string;
executable?: string;
status: 'ready_managed' | 'compliant_unmanaged' | 'mismatch' | 'conflict' | 'missing_declaration' | 'unavailable';
recommendation?: string;
}
export interface ToolchainStatus {
project_id: string;
project_root: string;
node: RuntimeToolStatus;
pnpm: RuntimeToolStatus;
resolver: {
kind: string;
configured: boolean;
executable?: string;
actual_version?: string;
status: 'ready' | 'missing' | 'available_unconfigured' | 'unavailable';
};
conflicts: string[];
}
export const getToolchainStatus = (projectId: string) =>
invoke<ToolchainStatus>('get_toolchain_status', { projectId });
export interface PrepareToolchainResult {
command: string[];
before: ToolchainStatus;
after: ToolchainStatus;
verified: boolean;
stdout: string;
stderr: string;
}
export const prepareToolchain = (projectId: string, confirmed: boolean) =>
invoke<PrepareToolchainResult>('prepare_toolchain', { projectId, confirmed });
// ── 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 });
// ── 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 });
// ── 本地 Git 工具(扫描 / init / remote / WSL 路径)───────────────────────────
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 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 });
/** 将 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 TemplateVariant {
label: string;
description: string;
initCommands: string;
}
export interface ProjectTemplate {
id: string;
name: string;
description?: string;
platform: string; // "win" | "wsl" | "both"
/** 逗号字符串(旧格式)或分类 JSON `{"客户端":[],"语言":[],"框架/工具":[]}` */
techStack?: string;
initCommands?: string;
isBuiltin: boolean;
createdAt?: string;
files: TemplateFile[];
/** JSON 字符串,解析后为 TemplateVariant[],存在时显示下拉选择 */
variants?: string;
}
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,
variantInitCommands?: string,
) =>
invoke<CreateProjectResult>("create_project_from_template", {
templateId,
parentPath,
projectName,
platform,
spaceId,
variantInitCommands,
});
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 type CicdWorkflowType = "pr_ci" | "deepseek_review" | "web_ssh" | "tauri_oss";
export interface WorkflowPlan {
project_id: string;
workflow_type: CicdWorkflowType;
content: string;
path: string;
content_digest: string;
change: "create" | "update" | "noop";
required_secrets: string[];
node_source?: string;
pnpm_source?: string;
}
export interface WorkflowApplyResult {
plan: WorkflowPlan;
written: boolean;
}
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 });
/** 只读预览:不写文件、不触发 CI 或部署。 */
export const planCicdWorkflow = (projectId: string, workflowType: CicdWorkflowType) =>
invoke<WorkflowPlan>("plan_cicd_workflow", { projectId, workflowType });
/** 写入预览对应的 workflow;调用方必须传入用户确认过的摘要。 */
export const applyCicdWorkflow = (
projectId: string,
workflowType: CicdWorkflowType,
expectedDigest: string,
confirmed: boolean,
) =>
invoke<WorkflowApplyResult>("apply_cicd_workflow", {
projectId,
workflowType,
expectedDigest,
confirmed,
});
/** 根据节点数据构造 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;
agents_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 UserConventionsStatus {
source: "builtin" | "user";
version: string;
content: string;
builtin_version: string;
outdated_from_builtin: boolean;
}
export interface SaveConventionsResult {
version: string;
synced_count: number;
}
export const getUserConventionsStatus = () =>
invoke<UserConventionsStatus>("get_user_conventions_status");
export const saveUserConventions = (content: string) =>
invoke<SaveConventionsResult>("save_user_conventions", { content });
export const resetConventionsToBuiltin = () =>
invoke<SaveConventionsResult>("reset_conventions_to_builtin");
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 ProjectNoteEntry {
project_name: string;
content: string;
created_at: string;
}
export interface ExecutionQuality {
complexity_dist: Record<string, number>;
total_rework_count: number;
avg_rounds_m: number | null;
}
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;
/** 跨项目复盘笔记(最近 50 条) */
project_notes: ProjectNoteEntry[];
/** 跨项目执行质量指标(阶段二扩展) */
execution_quality: ExecutionQuality;
}
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 ReviewCoverage {
completed_ml_tasks: number;
note_count: number;
}
export interface MentorContext {
project_id: string;
project_name: string;
health: ProjectHealth;
blueprint_summary?: BlueprintSummary;
recent_errors: RuntimeLogEntry[];
review_coverage?: ReviewCoverage;
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 });
// ── 用户项目个人备忘 ──────────────────────────────────────────────────────────
export interface UserTodo {
id: number;
project_id: string;
content: string;
done: boolean;
sort_order: number;
created_at: string;
}
export const getUserTodos = (projectId: string) =>
invoke<UserTodo[]>("get_user_todos", { projectId });
export const addUserTodo = (projectId: string, content: string) =>
invoke<UserTodo>("add_user_todo", { projectId, content });
export const toggleUserTodo = (id: number) =>
invoke<void>("toggle_user_todo", { id });
export const deleteUserTodo = (id: number) =>
invoke<void>("delete_user_todo", { id });
// ── Servers ──────────────────────────────────────────────────────────────────
export interface QuickLink {
label: string;
url: string;
}
export interface Server {
id: string;
name: string;
server_host: string;
server_port?: string;
username?: string;
password?: string;
ssh_key_path?: string;
quick_links?: string; // JSON string of QuickLink[]
notes?: string;
created_at?: string;
updated_at?: string;
}
export interface SaveServerInput {
name: string;
server_host: string;
server_port?: string;
username?: string;
password?: string;
ssh_key_path?: string;
quick_links?: string;
notes?: string;
}
export interface ProjectServerLink {
project_id: string;
server_id: string;
role?: string;
deploy_type?: "bare" | "docker";
deploy_path?: string;
container_name?: string;
start_command?: string;
notes?: string;
server_name?: string;
server_host?: string;
server_port?: string;
username?: string;
password?: string;
ssh_key_path?: string;
quick_links?: string;
}
export interface LinkServerInput {
role?: string;
deploy_type?: string;
deploy_path?: string;
container_name?: string;
start_command?: string;
notes?: string;
}
export const getServers = () =>
invoke<Server[]>("get_servers");
export const addServer = (input: SaveServerInput) =>
invoke<Server>("add_server", { input });
export const editServer = (id: string, input: SaveServerInput) =>
invoke<Server>("edit_server", { id, input });
export const deleteServer = (id: string) =>
invoke<void>("delete_server", { id });
export const linkServer = (projectId: string, serverId: string, input: LinkServerInput) =>
invoke<void>("link_server", { projectId, serverId, input });
export const unlinkServer = (projectId: string, serverId: string) =>
invoke<void>("unlink_server", { projectId, serverId });
export const getProjectServerLinks = (projectId: string) =>
invoke<ProjectServerLink[]>("get_project_server_links", { projectId });
export const getServerProjectLinks = (serverId: string) =>
invoke<ProjectServerLink[]>("get_server_project_links", { serverId });
// ── Server Service Configs(服务连接配置)─────────────────────────────────────
export interface ServiceConfigEntry {
key: string;
value: string;
/** 密码/密钥等敏感值,独立隐藏和复制 */
secretValue?: string;
}
export interface ServiceConfig {
id: string;
server_id: string;
name: string;
category?: string;
scope?: "system" | "docker";
config_data: string; // JSON of ServiceConfigEntry[]
sort_order?: number;
created_at?: string;
updated_at?: string;
}
export interface SaveServiceConfigInput {
name: string;
category?: string;
scope?: string;
config_data: string;
sort_order?: number;
}
export const getServiceConfigs = (serverId: string, scope?: string) =>
invoke<ServiceConfig[]>("get_service_configs", { serverId, scope });
export const addServiceConfig = (serverId: string, input: SaveServiceConfigInput) =>
invoke<ServiceConfig>("add_service_config", { serverId, input });
export const editServiceConfig = (id: string, input: SaveServiceConfigInput) =>
invoke<ServiceConfig>("edit_service_config", { id, input });
export const deleteServiceConfig = (id: string) =>
invoke<void>("delete_service_config", { id });
// ── Host Apps(宿主机部署应用)────────────────────────────────────────────────
export interface HostApp {
id: string;
server_id: string;
name: string;
deploy_type: string;
service_name?: string;
binary_path?: string;
config_path?: string;
log_path?: string;
port?: string;
start_cmd?: string;
stop_cmd?: string;
restart_cmd?: string;
status_cmd?: string;
notes?: string;
sort_order?: number;
created_at?: string;
updated_at?: string;
}
export interface SaveHostAppInput {
name: string;
deploy_type: string;
service_name?: string;
binary_path?: string;
config_path?: string;
log_path?: string;
port?: string;
start_cmd?: string;
stop_cmd?: string;
restart_cmd?: string;
status_cmd?: string;
notes?: string;
sort_order?: number;
}
export const getHostApps = (serverId: string) =>
invoke<HostApp[]>("get_host_apps", { serverId });
export const addHostApp = (serverId: string, input: SaveHostAppInput) =>
invoke<HostApp>("add_host_app", { serverId, input });
export const editHostApp = (id: string, input: SaveHostAppInput) =>
invoke<HostApp>("edit_host_app", { id, input });
export const deleteHostApp = (id: string) =>
invoke<void>("delete_host_app", { id });
// ── API Keys ─────────────────────────────────────────────────────────────────
export interface ApiKey {
id: string;
name: string;
provider: string;
base_url?: string;
model?: string;
notes?: string;
created_at?: string;
updated_at?: string;
}
export interface SaveApiKeyInput {
name: string;
provider: string;
api_key: string;
base_url?: string;
model?: string;
notes?: string;
}
export const getApiKeys = () =>
invoke<ApiKey[]>("get_api_keys");
export const addApiKey = (input: SaveApiKeyInput) =>
invoke<ApiKey>("add_api_key", { input });
export const editApiKey = (id: string, input: SaveApiKeyInput) =>
invoke<ApiKey>("edit_api_key", { id, input });
export const deleteApiKey = (id: string) =>
invoke<void>("delete_api_key", { id });
export const revealApiKey = (id: string) =>
invoke<string>("reveal_api_key", { id });
// ── Project development variables ──────────────────────────────────────────
export interface ProjectDevVariableStatus {
project_id: string;
name: string;
purpose: string;
kind: "scalar" | "test_account";
service: string | null;
environment: string | null;
configured: boolean;
updated_at: string;
}
export const listProjectDevVariables = (projectId: string) =>
invoke<ProjectDevVariableStatus[]>("list_project_dev_variables", { projectId });
export const saveProjectDevVariable = (projectId: string, name: string, value: string) =>
invoke<void>("save_project_dev_variable", { projectId, name, value });
export const saveProjectTestAccount = (
projectId: string,
name: string,
username: string,
password: string,
) => invoke<void>("save_project_test_account", { projectId, name, username, password });
/** 仅在人显式点「修改」时取回原值;不经 MCP。 */
export type ProjectDevVariableReveal =
| { kind: "scalar"; value: string }
| { kind: "test_account"; username: string; password: string };
export const revealProjectDevVariable = (projectId: string, name: string) =>
invoke<ProjectDevVariableReveal>("reveal_project_dev_variable", { projectId, name });
export const revokeProjectDevVariable = (projectId: string, name: string) =>
invoke<void>("revoke_project_dev_variable", { projectId, name });
// ── SSH Config & VS Code Remote ──────────────────────────────────────────────
export interface SshConfigStatus {
config_path: string;
file_exists: boolean;
has_devmanager_block: boolean;
}
export interface VscodeStatus {
installed: boolean;
path?: string;
remote_ssh_hint: string;
}
export const syncSshConfig = () =>
invoke<string>("sync_ssh_config");
export const getSshConfigStatus = () =>
invoke<SshConfigStatus>("get_ssh_config_status");
export const checkVscodeStatus = () =>
invoke<VscodeStatus>("check_vscode_status");
export const openVscodeRemote = (serverId: string, remotePath?: string) =>
invoke<string>("open_vscode_remote", { serverId, remotePath });
export const openServerTerminal = (serverId: string) =>
invoke<string>("open_server_terminal", { serverId });
export interface SshConfigEntry {
host_alias: string;
hostname?: string;
port?: string;
user?: string;
identity_file?: string;
}
export const parseSshConfigEntries = () =>
invoke<SshConfigEntry[]>("parse_ssh_config_entries");
export const importSshConfigEntries = (entries: SshConfigEntry[]) =>
invoke<number>("import_ssh_config_entries", { entries });
// ── Server Software & Mirrors ────────────────────────────────────────────────
export interface GithubMirror {
id: string;
name: string;
url_prefix: string;
is_builtin: boolean;
sort_order: number;
}
export interface ServerNetworkConfig {
server_id: string;
github_mirror_id?: string;
last_tested?: string;
test_results?: string;
}
export interface MirrorTestResult {
mirror_id: string;
reachable: boolean;
latency_ms?: number;
error?: string;
}
export interface ServerSoftware {
id: string;
name: string;
description?: string;
category?: string;
install_type: "package" | "github";
install_cmd?: string;
github_url?: string;
remote_path?: string;
post_cmd?: string;
is_builtin: boolean;
sort_order: number;
created_at?: string;
}
export interface SaveSoftwareInput {
name: string;
description?: string;
category?: string;
install_type: string;
install_cmd?: string;
github_url?: string;
remote_path?: string;
post_cmd?: string;
}
export interface SaveMirrorInput {
name: string;
url_prefix: string;
}
export interface SoftwareCategory {
id: string;
name: string;
color: string;
sort_order: number;
}
export interface SaveCategoryInput {
name: string;
color?: string;
}
export const getSoftwareCategories = () =>
invoke<SoftwareCategory[]>("get_software_categories");
export const addSoftwareCategory = (input: SaveCategoryInput) =>
invoke<SoftwareCategory>("add_software_category", { input });
export const updateSoftwareCategory = (id: string, input: SaveCategoryInput) =>
invoke<void>("update_software_category", { id, input });
export const deleteSoftwareCategory = (id: string) =>
invoke<void>("delete_software_category", { id });
export const getGithubMirrors = () =>
invoke<GithubMirror[]>("get_github_mirrors");
export const addGithubMirror = (input: SaveMirrorInput) =>
invoke<GithubMirror>("add_github_mirror", { input });
export const deleteGithubMirror = (id: string) =>
invoke<void>("delete_github_mirror", { id });
export const testMirrorLocal = (mirrorId: string) =>
invoke<MirrorTestResult>("test_mirror_local", { mirrorId });
export const getServerNetworkConfig = (serverId: string) =>
invoke<ServerNetworkConfig | null>("get_server_network_config", { serverId });
export const setServerMirror = (serverId: string, mirrorId?: string) =>
invoke<void>("set_server_mirror", { serverId, mirrorId });
export const getServerSoftwareList = () =>
invoke<ServerSoftware[]>("get_server_software_list");
export const addServerSoftware = (input: SaveSoftwareInput) =>
invoke<ServerSoftware>("add_server_software", { input });
export const updateServerSoftware = (id: string, input: SaveSoftwareInput) =>
invoke<void>("update_server_software", { id, input });
export const deleteServerSoftware = (id: string) =>
invoke<void>("delete_server_software", { id });
export const generateInstallCommand = (softwareId: string, serverId: string) =>
invoke<string[]>("generate_install_command", { softwareId, serverId });
// ── 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");
// ── Onboarding Pack(接入包)──────────────────────────────────────────────────
export interface TemplateResult {
name: string;
action: "written" | "updated" | "appended" | "skipped" | "failed";
detail: string;
/** 混合文件预警:写入前该文件块外已有未提交改动,本次又改写了其中的托管块(只提示不阻断) */
warning?: string;
}
export interface HookActivation {
status: "activated" | "missing_binary" | "failed";
detail: string;
}
export interface OnboardingReport {
templates: TemplateResult[];
hook: HookActivation;
buff: BuffStatus;
}
export const applyOnboardingPack = (projectPath: string, projectId: string) =>
invoke<OnboardingReport>("apply_onboarding_pack", { projectPath, projectId });
// ── 三端对等 L1 零模型检查(agent-client-parity P4a)────────────────────────────
// 类型与 src-tauri/src/onboard/probe_l1.rs 的 L1Report 对应;MCP probe_client_parity 返回同一结构。
/** A 项目规则 / B lian-jing skill / C MCP 工具集 / D get_diagnostics / E 列出开发变量 / F 敏感读取认证链 */
export type ParityCheck = "A" | "B" | "C" | "D" | "E" | "F";
/** 证据等级,从弱到强;L1 至多到 protocol */
export type ParityEvidence =
| "static"
| "client_introspection"
| "protocol"
| "session_event"
| "server_trace";
/** not_installed 与 not_applicable 不计失败 */
export type ParityCellState =
| "ready"
| "fail"
| "unknown"
| "not_installed"
| "not_applicable"
| "blocked"
| "timeout";
export type ParityCause =
| "control_plane"
| "wsl"
| "timeout"
| "unreachable"
| "client_error"
| "not_configured"
| "route_global"
| "route_mismatch"
| "not_connected"
| "codex_project_untrusted"
| "server_disabled"
| "manual_config"
| "rules_not_loaded"
| "skill_not_visible"
| "session_plugin_mismatch"
| "shim_missing"
| "helper_failed"
| "auth_header_missing"
| "auth_failed"
| "tools_mismatch"
| "tool_error"
| "adapter_drift"
/** 一键接入编排(P8d):该端依赖的接入步骤未完成,本端未检查 */
| "dependency";
export interface ParityCell {
state: ParityCellState;
cause?: ParityCause;
evidence: ParityEvidence;
detail: string;
/** cause 为 dependency 时:未完成的接入步骤名(claude / codex / dsh) */
dep?: string;
}
export interface ParityClientColumn {
client: "claude" | "codex" | "dsh";
version: string | null;
/** 该端对本项目实际会连的地址;未得出为 null */
route: string | null;
cells: Record<ParityCheck, ParityCell>;
duration_ms: number;
}
export interface ClientParityL1Report {
project_id: string;
tier: "l1";
probe_spec_version: number;
profile: "project" | "control_plane";
expected_route: string;
expected_tools: number;
nonce: string;
clients: ParityClientColumn[];
all_applicable_ready: boolean;
duration_ms: number;
note: string;
}
/** 约 5–10 秒:起零模型客户端自省并重放认证链(认证头不出炼境进程) */
export const probeClientParity = (projectId: string, tier: "l1" = "l1") =>
invoke<ClientParityL1Report>("probe_client_parity", { projectId, tier });
// ── 一键接入(agent-client-parity P8c 计划 / P8d 编排 / P8e 投影)────────────────────
// 类型与 src-tauri/src/onboard/plan.rs 的 OnboardingPlan、onboard/apply.rs 的 OnboardOptions、
// onboard/report.rs 的 OnboardReport 对应;MCP plan_onboarding / onboard_project 返回同一结构。
// 流程:planOnboarding → 给用户看计划 → 用户确认 → onboardProject(plan.digest, true, 同一组 options)。
/** 计划与应用共用的选项:两次必须一致,否则 digest 不符被拒 */
export interface OnboardOptions {
/** 默认 true:项目 .claude/settings.json 还没有 permissions 时写入 bypassPermissions;false 即按项目取消(写显式 default) */
bypass_permissions?: boolean;
/** 用户逐项选择「跳过」的阻断项 target(plan.blocked[].target);只有 manual_entry / shared_key 可跳过 */
skip_targets?: string[];
}
export type OnboardFileAction = "create" | "update" | "noop";
export interface OnboardPlannedFile {
/** 相对项目根,统一用 / */
path: string;
action: OnboardFileAction;
/** update 的有上限行级差异;create 的新文件概要 */
diff?: string;
/** 混合文件预警(同时记入 blocked) */
warning?: string;
content_sha256: string;
/** update 时写入前内容的 sha256(进入 digest) */
before_sha256?: string;
}
/** 只有 manual_entry 与 shared_key 可在确认时跳过;其余只能处理后重新计划 */
export type OnboardBlockReason =
| "mixed_file"
| "identity_conflict"
| "manual_entry"
| "shared_key"
| "needs_human";
export interface OnboardBlockedItem {
target: string;
reason: OnboardBlockReason;
detail: string;
}
/** 本机七步:token、shim、claude、codex、dsh、product_group_buff、git_hooks */
export interface OnboardMachineStep {
step: string;
/** noop | apply | not_installed | not_applicable | unavailable | manual_kept | other_project | needs_human */
status: string;
detail: string;
}
export type CodexTrust = "trusted" | "untrusted" | "not_installed" | "unknown";
/** 机器只列出、由人完成的事项(不阻断):codex_trust、gitea_repo(默认建公开仓库)、ci、commit、repo_layout 等 */
export interface OnboardNextAction {
action: string;
detail: string;
}
export interface OnboardingPlan {
project_id: string;
/** 将要写入的项目根目录(进入 digest) */
root: string;
files: OnboardPlannedFile[];
machine_changes: OnboardMachineStep[];
/** 非空时 onboardProject 一律 refused */
blocked: OnboardBlockedItem[];
/** 按 skip_targets 跳过的阻断项(进入 digest) */
skipped?: OnboardBlockedItem[];
/** 本次会新写入 permissions.defaultMode=bypassPermissions */
bypass_permissions: boolean;
codex_trust: CodexTrust;
next_actions: OnboardNextAction[];
/** onboardProject 须带回同一 digest */
digest: string;
}
export type OnboardStatus = "done" | "partial" | "failed" | "refused" | "busy";
/** identity、project_files、git_hooks、product_group_buff、token、shim、claude、codex、dsh */
export type OnboardStepReport = { step: string; detail: string } & (
| { status: "converged" | "applied" }
| { status: "skipped" | "not_applicable" | "failed" | "needs_human"; reason: string }
| { status: "blocked"; dep: string }
);
export interface OnboardCompletionItem {
item: string;
source: "l0" | "clients" | "l1" | "repo";
ready: boolean;
detail: string;
/** 只等用户在客户端里操作(目前只有「Codex 信任」):ready=false 但不计入 done */
pending_user: boolean;
}
export interface OnboardPendingFile {
/** 相对项目根 */
path: string;
/** porcelain 两位状态码(?? 未跟踪、" M" 已修改等) */
code: string;
}
export type OnboardRepoChanges =
| {
state: "available";
status_porcelain: string[];
diff_stat: string;
/** 待提交的接入文件:提交时只 add 这些 */
onboard_files: OnboardPendingFile[];
/** 不属于接入的改动条数:勿混进接入提交 */
other_changes: number;
}
| { state: "unavailable"; reason: string };
export interface OnboardHeadCheck {
before: string | null;
after: string | null;
unchanged: boolean;
}
export interface OnboardReport {
project_id: string;
status: OnboardStatus;
summary: string;
/** failed / refused / busy 的原因 */
reason?: string;
digest?: string;
/** refused(digest 不符、根目录或连接参数变了、有阻断项)时附当前计划,须重新给用户确认 */
plan?: OnboardingPlan;
blocked?: OnboardBlockedItem[];
bypass_permissions: boolean;
steps: OnboardStepReport[];
completion: OnboardCompletionItem[];
/** 客户端步骤未完成的一端整列 blocked(cause=dependency) */
l1?: ClientParityL1Report;
/** 最近一次 L3 结果说明(L3 尚未提供) */
l3: string;
written_files: string[];
repo_changes?: OnboardRepoChanges;
/** 非 git 仓库为空 */
head?: OnboardHeadCheck;
suggested_commit_message?: string;
next_actions: OnboardNextAction[];
duration_ms: number;
}
/** 只读:一键接入写入前计划(不写文件、不写 DB、不起客户端 CLI) */
export const planOnboarding = (projectId: string, options?: OnboardOptions) =>
invoke<OnboardingPlan>("plan_onboarding", { projectId, options });
/** 一键接入:须带回用户确认过的计划 digest 与 confirmed=true、同一组 options;首次可能 30 秒以上。结果看 status,refused 等不抛错 */
export const onboardProject = (
projectId: string,
expectedDigest: string,
confirmed: boolean,
options?: OnboardOptions,
) =>
invoke<OnboardReport>("onboard_project", {
projectId,
expectedDigest,
confirmed,
options,
});
// ── Agent Infrastructure ──────────────────────────────────────────────────────
export interface DetectedContract {
label: string;
paths: string[];
doc: string;
confidence: 'high' | 'medium' | 'low';
}
export interface AgentInfraStack {
detected_contracts: DetectedContract[];
meta_rules_present: boolean;
context_yaml_exists: boolean;
}
export const scanAgentInfraStack = (projectPath: string) =>
invoke<AgentInfraStack>('scan_agent_infra_stack', { projectPath });
export interface ContractInput {
label: string;
paths: string[];
doc: string;
}
export interface FileResult {
path: string;
action: 'written' | 'updated' | 'skipped' | 'failed';
detail: string;
}
export interface GenerateReport {
files: FileResult[];
}
export const generateAgentInfraFiles = (projectPath: string, contracts: ContractInput[]) =>
invoke<GenerateReport>('generate_agent_infra_files', { projectPath, contracts });
export interface FreshnessDetail {
contract_path: string;
contract_ts: number | null;
yaml_ts: number | null;
gap_days: number | null;
level: 'green' | 'yellow' | 'red';
}
export interface FreshnessResult {
level: 'green' | 'yellow' | 'red';
details: FreshnessDetail[];
checked_at: string;
}
export interface CachedFreshness {
score: 'green' | 'yellow' | 'red';
details: string; // JSON string
checked_at: string;
}
export const scanDocFreshness = (projectPath: string, projectId: string) =>
invoke<FreshnessResult>('scan_doc_freshness', { projectPath, projectId });
export const getDocFreshnessCache = (projectId: string) =>
invoke<CachedFreshness | null>('get_doc_freshness_cache', { projectId });
export interface AgentHealthIssue {
level: 'error' | 'warn';
title: string;
fix: string;
}
export interface AgentHealthReport {
overall: 'green' | 'yellow' | 'red';
issues: AgentHealthIssue[];
fix_prompt: string;
checked_at: string;
}
export const getAgentHealth = (projectPath: string, projectId: string) =>
invoke<AgentHealthReport>('get_agent_health', { projectPath, projectId });
export interface CommitStats {
total: number;
conventional: number;
rework: number;
}
export const getCommitStats = (projectId: string) =>
invoke<CommitStats>("get_commit_stats", { projectId });
export interface IngestResult {
imported: number;
skipped: number;
/** 存量行中被清洗掉未知规则 ID 的行数(U3;项目无 CONVENTIONS.md 时为 0) */
sanitized: number;
}
export const ingestFullGitHistory = (projectId: string, projectPath: string) =>
invoke<IngestResult>("ingest_full_git_history", { projectId, projectPath });
export interface ScopeActivity {
scope: string;
commit_count: number;
rework_count: number;
}
export interface ProjectCommitHealth {
project_id: string;
total_commits: number;
conventional_commits: number;
conventional_rate: number;
rework_commits: number;
rework_rate: number;
top_scopes: ScopeActivity[];
}
export const getProjectCommitHealth = (projectIds: string[]) =>
invoke<ProjectCommitHealth[]>("get_project_commit_health", { projectIds });
export interface CommitHealthWithName extends ProjectCommitHealth {
project_name: string;
}
export const getAllCommitHealth = () =>
invoke<CommitHealthWithName[]>("get_all_commit_health");
// 飞轮阶段三:跨项目返工热区(样本 ≥minCommits 且有非 CI 返工的 scope,按返工率降序)
export interface HighRiskScope {
project_name: string;
scope: string;
commits: number;
rework: number;
rework_rate: number;
}
export const getHighRiskScopes = (minCommits?: number, limit?: number) =>
invoke<HighRiskScope[]>("get_high_risk_scopes", { minCommits, limit });
// ── Gitea Integration ────────────────────────────────────────────────────────
export interface GiteaInstance {
id: string;
name: string;
url: string;
is_default: boolean;
created_at?: string;
}
export interface SaveGiteaInstanceInput {
name: string;
url: string;
access_token: string;
is_default?: boolean;
}
export interface GiteaUser {
login: string;
full_name: string;
email: string;
}
export interface GiteaLinkedRepo {
id: string;
instance_id: string;
instance_name: string;
instance_url: string;
project_id: string;
gitea_owner: string;
gitea_repo_name: string;
clone_url?: string;
has_webhook: boolean;
webhook_id?: number;
linked_at?: string;
}
export const giteaSaveInstance = (input: SaveGiteaInstanceInput) =>
invoke<GiteaInstance>("gitea_save_instance", { input });
export const giteaListInstances = () =>
invoke<GiteaInstance[]>("gitea_list_instances");
export const giteaDeleteInstance = (id: string) =>
invoke<void>("gitea_delete_instance", { id });
export const giteaUpdateInstance = (
id: string,
fields: { name?: string; url?: string; access_token?: string; is_default?: boolean }
// Tauri 命令参数按 camelCase 匹配 Rust snake_case,直接展开 snake_case 字段会被静默丢弃
) => invoke<GiteaInstance>("gitea_update_instance", {
id,
name: fields.name,
url: fields.url,
accessToken: fields.access_token,
isDefault: fields.is_default,
});
export const giteaTestConnection = (instanceId: string) =>
invoke<GiteaUser>("gitea_test_connection", { instanceId });
export interface ResourceReadiness {
state: "configured" | "pending_migration" | "not_configured" | "unavailable" | "needs_repair";
source: "protected_store" | "legacy_pending_migration" | "gitea_binding";
}
export interface ProjectResourceStatus {
project_id: string;
gitea_pr: ResourceReadiness;
gitea_webhook: ResourceReadiness;
}
export const getProjectResourceStatus = (projectId: string) =>
invoke<ProjectResourceStatus>("get_project_resource_status", { projectId });
export const giteaListLinkedRepos = () =>
invoke<GiteaLinkedRepo[]>("gitea_list_linked_repos");
/** 后端 GiteaRepo 结构(单项目绑定详情,与 GiteaLinkedRepo 列表视图字段不同) */
export interface GiteaRepoLink {
id: string;
instance_id: string;
project_id: string;
gitea_owner: string;
gitea_repo_name: string;
clone_url?: string;
webhook_id?: number;
linked_at?: string;
}
export const giteaGetRepoLink = (projectId: string) =>
invoke<GiteaRepoLink | null>("gitea_get_repo_link", { projectId });
export const giteaCreateRepo = (
instanceId: string,
projectId: string,
repoName: string,
description?: string,
privateRepo?: boolean
) => invoke<GiteaRepoLink>("gitea_create_repo", { instanceId, projectId, repoName, description, private: privateRepo });
export const giteaMigrateRepo = (
instanceId: string,
projectId: string,
cloneAddr: string,
repoName: string,
authToken?: string,
privateRepo?: boolean
) => invoke<GiteaRepoLink>("gitea_migrate_repo", { instanceId, projectId, cloneAddr, repoName, authToken, private: privateRepo });
export const giteaLinkRepo = (
instanceId: string,
projectId: string,
owner: string,
repoName: string
) => invoke<GiteaRepoLink>("gitea_link_repo", { instanceId, projectId, owner, repoName });
export const giteaSetupWebhook = (projectId: string, callbackBase: string) =>
invoke<GiteaRepoLink>("gitea_setup_webhook", { projectId, callbackBase });
export const giteaUnlinkRepo = (projectId: string) =>
invoke<void>("gitea_unlink_repo", { projectId });
/** 手动触发一次远端轮询采集(后台每 10 分钟自动跑一次) */
export const giteaPollNow = () =>
invoke<{ polled: number; ingested: number }>("gitea_poll_now");
// ── Beast Global Status (read-only) ───────────────────────────────────────────
export interface BeastGlobalStatus {
/** Beast 是否已安装 */
installed: boolean;
/** 当前是否处于狂暴模式 */
active: boolean;
/** ~/.claude/CLAUDE.md 是否含 Beast 注入的 bash 规则 */
has_bash_rules: boolean;
}
export const getBeastGlobalStatus = () =>
invoke<BeastGlobalStatus>("get_beast_global_status");
// ── Source Library (源码库) ──────────────────────────────────────────────────
export interface SourceCategory {
id: string;
name: string;
icon: string;
color: string;
sort_order: number;
}
export interface SourceProject {
id: string;
name: string;
description?: string;
category_id?: string;
language?: string;
tags?: string;
repo_url?: string;
introduction?: string;
readme?: string;
notes?: string;
created_at?: string;
updated_at?: string;
category_name?: string;
version_count?: number;
}
export interface SourceVersion {
id: string;
project_id: string;
version: string;
file_path: string;
archive_path?: string;
file_size: number;
is_primary: boolean;
dir_tree?: string;
notes?: string;
imported_at?: string;
}
export interface DirEntry {
name: string;
type: "dir" | "file";
children_count?: number;
}
// categories
export const getSourceCategories = () =>
invoke<SourceCategory[]>("get_source_categories");
export const createSourceCategory = (name: string, icon?: string, color?: string) =>
invoke<SourceCategory>("create_source_category", { name, icon, color });
export const updateSourceCategory = (id: string, name: string, icon: string, color: string) =>
invoke<void>("update_source_category", { id, name, icon, color });
export const deleteSourceCategory = (id: string) =>
invoke<void>("delete_source_category", { id });
// projects
export const getSourceProjects = (categoryId?: string, search?: string) =>
invoke<SourceProject[]>("get_source_projects", { categoryId, search });
export const createSourceProject = (
name: string,
description?: string,
categoryId?: string,
language?: string,
repoUrl?: string,
introduction?: string,
) => invoke<SourceProject>("create_source_project", { name, description, categoryId, language, repoUrl, introduction });
export const updateSourceProject = (
id: string,
name: string,
description?: string,
categoryId?: string,
language?: string,
repoUrl?: string,
introduction?: string,
notes?: string,
) => invoke<void>("update_source_project", { id, name, description, categoryId, language, repoUrl, introduction, notes });
export const deleteSourceProject = (id: string) =>
invoke<void>("delete_source_project", { id });
// versions
export const getSourceVersions = (projectId: string) =>
invoke<SourceVersion[]>("get_source_versions", { projectId });
export const addSourceVersion = (projectId: string, projectName: string, version: string, filePath: string) =>
invoke<SourceVersion>("add_source_version", { projectId, projectName, version, filePath });
export const setPrimaryVersion = (versionId: string, projectId: string) =>
invoke<void>("set_primary_version", { versionId, projectId });
export const deleteSourceVersion = (versionId: string) =>
invoke<void>("delete_source_version", { versionId });
export const updateVersionNotes = (versionId: string, notes: string) =>
invoke<void>("update_version_notes", { versionId, notes });
export const scanSourceDirTree = (path: string) =>
invoke<DirEntry[]>("scan_source_dir_tree", { path });
export const revealSourceInExplorer = (path: string) =>
invoke<void>("reveal_source_in_explorer", { path });
export const copySourceTo = (source: string, destDir: string) =>
invoke<string>("copy_source_to", { source, destDir });
// ── Cloud Products ────────────────────────────────────────────────────────────
export interface CloudProduct {
id: string;
vendor: string; // aliyun | tencent | custom
product_type: string; // ecs/cvm/rds/tdsql/oss/cos/cdn/domain/redis/clb/custom
name: string;
region?: string;
instance_id?: string;
endpoint?: string;
expire_at?: string;
status: string; // active | expired | stopped
config_data: string; // JSON string
notes?: string;
sort_order: number;
created_at?: string;
updated_at?: string;
}
export interface SaveCloudProductInput {
vendor: string;
product_type: string;
name: string;
region?: string;
instance_id?: string;
endpoint?: string;
expire_at?: string;
status: string;
config_data?: string;
notes?: string;
sort_order?: number;
}
export const getCloudProducts = () =>
invoke<CloudProduct[]>("get_cloud_products");
export const addCloudProduct = (input: SaveCloudProductInput) =>
invoke<CloudProduct>("add_cloud_product", { input });
export const editCloudProduct = (id: string, input: SaveCloudProductInput) =>
invoke<CloudProduct>("edit_cloud_product", { id, input });
export const deleteCloudProduct = (id: string) =>
invoke<void>("delete_cloud_product", { id });
// ── Cloud DB Databases ────────────────────────────────────────────────────────
export interface CloudDatabase {
id: string;
product_id: string;
name: string;
db_driver: string; // postgresql | mysql | redis | mongodb | custom
username?: string;
password?: string;
charset?: string;
notes?: string;
sort_order: number;
created_at?: string;
updated_at?: string;
}
export interface SaveCloudDatabaseInput {
product_id: string;
name: string;
db_driver: string;
username?: string;
password?: string;
charset?: string;
notes?: string;
sort_order?: number;
}
export const getCloudDatabases = (productId: string) =>
invoke<CloudDatabase[]>("get_cloud_databases", { productId });
export const addCloudDatabase = (input: SaveCloudDatabaseInput) =>
invoke<CloudDatabase>("add_cloud_database", { input });
export const editCloudDatabase = (id: string, input: SaveCloudDatabaseInput) =>
invoke<CloudDatabase>("edit_cloud_database", { id, input });
export const deleteCloudDatabase = (id: string) =>
invoke<void>("delete_cloud_database", { id });
// ── Docker Apps ──────────────────────────────────────────────────────────────
export interface DockerApp {
id: string;
product_id: string;
name: string;
access_url?: string;
username?: string;
password?: string;
notes?: string;
sort_order: number;
created_at?: string;
updated_at?: string;
}
export interface SaveDockerAppInput {
product_id: string;
name: string;
access_url?: string;
username?: string;
password?: string;
notes?: string;
sort_order?: number;
}
export const getDockerApps = (productId: string) =>
invoke<DockerApp[]>("get_docker_apps", { productId });
export const addDockerApp = (input: SaveDockerAppInput) =>
invoke<DockerApp>("add_docker_app", { input });
export const editDockerApp = (id: string, input: SaveDockerAppInput) =>
invoke<DockerApp>("edit_docker_app", { id, input });
export const deleteDockerApp = (id: string) =>
invoke<void>("delete_docker_app", { id });