chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,467 @@
import { ipcMain } from "electron";
import log from "electron-log";
import { z } from "zod";
import { agentService } from "../services/engines/unifiedAgent";
import type { AgentConfig } from "../services/engines/unifiedAgent";
import {
mcpProxyManager,
syncMcpConfigToProxyAndReload,
} from "../services/packages/mcp";
const agentConfigSchema = z
.object({
engine: z.enum(["qimingcode", "claude-code"]),
workspaceDir: z.string(),
apiKey: z.string().optional(),
baseUrl: z.string().optional(),
model: z.string().optional(),
apiProtocol: z.string().optional(),
hostname: z.string().optional(),
port: z.number().int().positive().optional(),
timeout: z.number().int().positive().optional(),
engineBinaryPath: z.string().optional(),
env: z.record(z.string()).optional(),
mcpServers: z.record(z.unknown()).optional(),
permissionMode: z
.enum(["default", "acceptEdits", "bypassPermissions"])
.optional(),
systemPrompt: z.string().optional(),
purpose: z.enum(["engine"]).optional(),
})
.passthrough();
const sessionIdSchema = z.string().min(1);
const partsSchema = z.array(z.any());
const promptOptionsSchema = z.any().optional();
const permissionResponseSchema = z.enum(["once", "always", "reject"]);
function invalidArgs(channel: string, issues: unknown) {
const details =
Array.isArray(issues) && issues.length > 0
? issues
.map((i: any) =>
i.path ? `${i.path.join(".")}: ${i.message}` : i.message,
)
.join("; ")
: String(issues);
log.warn(`[IPC] ${channel} invalid args:`, issues);
return {
success: false,
error: `Invalid arguments for ${channel}: ${details}`,
};
}
export function registerAgentHandlers(): void {
// Initialize unified agent service
ipcMain.handle("agent:init", async (_, config: AgentConfig) => {
const parsedConfig = agentConfigSchema.safeParse(config);
if (!parsedConfig.success) {
return invalidArgs("agent:init", parsedConfig.error.issues);
}
const typedConfig = parsedConfig.data as AgentConfig;
log.info("[IPC] Initializing unified agent:", config.engine);
try {
// Auto-inject MCP config if MCP proxy is running and no mcpServers provided
let finalConfig: AgentConfig = typedConfig;
if (!typedConfig.mcpServers) {
const mcpConfig = mcpProxyManager.getAgentMcpConfig();
if (mcpConfig) {
finalConfig = { ...typedConfig, mcpServers: mcpConfig };
log.info(
"[IPC] Auto-injected MCP config into agent:",
Object.keys(mcpConfig),
);
}
}
const ok = await agentService.init(finalConfig);
// 仅当调用方显式传入 mcpServers原始服务器列表同步到 MCP Proxy 并动态加载
// auto-inject 时 finalConfig.mcpServers 是桥接配置,不能写回 proxy
if (
typedConfig.mcpServers &&
Object.keys(typedConfig.mcpServers).length > 0
) {
await syncMcpConfigToProxyAndReload(typedConfig.mcpServers);
}
return {
success: ok,
engineType: agentService.getEngineType(),
};
} catch (error) {
log.error("[IPC] agent:init failed:", error);
return { success: false, error: String(error) };
}
});
// Get agent service status
ipcMain.handle("agent:serviceStatus", () => {
return {
running: agentService.isReady,
engineType: agentService.getEngineType(),
};
});
// Destroy unified agent service
ipcMain.handle("agent:destroy", async () => {
try {
await agentService.destroy();
return { success: true };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Get engine type
ipcMain.handle("agent:getEngineType", () => {
return agentService.getEngineType();
});
// Check if ready
ipcMain.handle("agent:isReady", () => {
return agentService.isReady;
});
// List sessions
ipcMain.handle("agent:listSessions", async () => {
try {
const sessions = await agentService.listSessions();
return { success: true, data: sessions };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Create session
ipcMain.handle(
"agent:createSession",
async (_, opts?: { parentID?: string; title?: string }) => {
try {
const session = await agentService.createSession(opts);
return { success: true, data: session };
} catch (error) {
return { success: false, error: String(error) };
}
},
);
// Get session
ipcMain.handle("agent:getSession", async (_, id: string) => {
try {
const session = await agentService.getSession(id);
return { success: true, data: session };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Delete session
ipcMain.handle("agent:deleteSession", async (_, id: string) => {
try {
await agentService.deleteSession(id);
return { success: true };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Update session title (ACP doesn't support this, but keep for compatibility)
ipcMain.handle(
"agent:updateSession",
async (_, id: string, title?: string) => {
try {
// ACP doesn't have a separate update method, title is set via session info updates
// Return success for compatibility
return { success: true, data: { id, title } };
} catch (error) {
return { success: false, error: String(error) };
}
},
);
// Get session status (ACP doesn't have this, return empty for compatibility)
ipcMain.handle("agent:getSessionStatus", async () => {
try {
return { success: true, data: {} };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Get messages (ACP doesn't store messages, return empty for compatibility)
ipcMain.handle("agent:getMessages", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Get single message (ACP doesn't store messages, return error for compatibility)
ipcMain.handle(
"agent:getMessage",
async (_, sessionId: string, messageId: string) => {
try {
return { success: false, error: "ACP engine does not store messages" };
} catch (error) {
return { success: false, error: String(error) };
}
},
);
// Prompt (blocking)
ipcMain.handle(
"agent:prompt",
async (_, sessionId: string, parts: any[], opts?: any) => {
const sid = sessionIdSchema.safeParse(sessionId);
const ps = partsSchema.safeParse(parts);
const op = promptOptionsSchema.safeParse(opts);
if (!sid.success || !ps.success || !op.success) {
return invalidArgs("agent:prompt", {
sessionId: sid.success ? null : sid.error.issues,
parts: ps.success ? null : ps.error.issues,
opts: op.success ? null : op.error.issues,
});
}
try {
const result = await agentService.prompt(sid.data, ps.data, op.data);
return { success: true, data: result };
} catch (error) {
return { success: false, error: String(error) };
}
},
);
// Prompt (async, non-blocking - results via SSE events)
ipcMain.handle(
"agent:promptAsync",
async (_, sessionId: string, parts: any[], opts?: any) => {
const sid = sessionIdSchema.safeParse(sessionId);
const ps = partsSchema.safeParse(parts);
const op = promptOptionsSchema.safeParse(opts);
if (!sid.success || !ps.success || !op.success) {
return invalidArgs("agent:promptAsync", {
sessionId: sid.success ? null : sid.error.issues,
parts: ps.success ? null : ps.error.issues,
opts: op.success ? null : op.error.issues,
});
}
try {
await agentService.promptAsync(sid.data, ps.data, op.data);
return { success: true };
} catch (error) {
return { success: false, error: String(error) };
}
},
);
// Abort session
ipcMain.handle("agent:abort", async (_, sessionId?: string) => {
if (sessionId !== undefined) {
const sid = sessionIdSchema.safeParse(sessionId);
if (!sid.success) {
return invalidArgs("agent:abort", sid.error.issues);
}
}
try {
await agentService.abortSession(sessionId || "");
return { success: true };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Respond to permission request
ipcMain.handle(
"agent:respondPermission",
async (
_,
_sessionId: string,
permissionId: string,
response: "once" | "always" | "reject",
) => {
const pid = z.string().min(1).safeParse(permissionId);
const rsp = permissionResponseSchema.safeParse(response);
if (!pid.success || !rsp.success) {
return invalidArgs("agent:respondPermission", {
permissionId: pid.success ? null : pid.error.issues,
response: rsp.success ? null : rsp.error.issues,
});
}
try {
agentService.respondPermission(pid.data, rsp.data);
return { success: true };
} catch (error) {
return { success: false, error: String(error) };
}
},
);
// List tools (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:listTools", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// List providers (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:listProviders", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Get session diff (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:getSessionDiff", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Revert session (ACP doesn't support this, return error for compatibility)
ipcMain.handle("agent:revert", async () => {
try {
return { success: false, error: "ACP engine does not support revert" };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Unrevert session (ACP doesn't support this, return error for compatibility)
ipcMain.handle("agent:unrevert", async () => {
try {
return { success: false, error: "ACP engine does not support unrevert" };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Share session (ACP doesn't support this, return error for compatibility)
ipcMain.handle("agent:shareSession", async () => {
try {
return { success: false, error: "ACP engine does not support share" };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Fork session (ACP doesn't support this, return error for compatibility)
ipcMain.handle("agent:forkSession", async () => {
try {
return { success: false, error: "ACP engine does not support fork" };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Get config (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:getConfig", async () => {
try {
return { success: true, data: {} };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Find text (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:findText", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Find files (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:findFiles", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// List files (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:listFiles", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Read file (ACP doesn't support this, return error for compatibility)
ipcMain.handle("agent:readFile", async () => {
try {
return { success: false, error: "ACP engine does not support readFile" };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Claude Code prompt (ACP engine)
ipcMain.handle("agent:claudePrompt", async (_, message: string) => {
try {
const result = await agentService.claudePrompt(message);
return { success: true, data: result };
} catch (error) {
return { success: false, error: String(error) };
}
});
// MCP status (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:mcpStatus", async () => {
try {
return { success: true, data: {} };
} catch (error) {
return { success: false, error: String(error) };
}
});
// List agents (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:listAgents", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// List commands (ACP doesn't support this, return empty for compatibility)
ipcMain.handle("agent:listCommands", async () => {
try {
return { success: true, data: [] };
} catch (error) {
return { success: false, error: String(error) };
}
});
// List all sessions with detailed status (for Sessions tab)
ipcMain.handle("agent:listSessionsDetailed", async () => {
try {
const sessions = agentService.listAllSessionsDetailed();
return { success: true, data: sessions };
} catch (error) {
log.error("[IPC] agent:listSessionsDetailed failed:", error);
return { success: false, error: String(error), data: [] };
}
});
// Stop a specific session (abort + delete from engine)
ipcMain.handle("agent:stopSession", async (_, sessionId: string) => {
try {
const stopped = await agentService.stopSession(sessionId);
return { success: stopped };
} catch (error) {
log.error("[IPC] agent:stopSession failed:", error);
return { success: false, error: String(error) };
}
});
}