feat: prepare Zhinian desktop pilot
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-05-07 21:49:20 +08:00
parent cddaf37016
commit 0abc48189c
103 changed files with 10975 additions and 2049 deletions

View File

@@ -0,0 +1,26 @@
import type { IncomingMessage, ServerResponse } from 'http';
import type { HostApiContext } from '../context';
import { sendJson } from '../route-utils';
import {
ensureNianxxPlayServiceStarted,
getNianxxPlayServiceStatus,
} from '../../utils/nianxx-play-service';
export async function handleAppIntegrationRoutes(
req: IncomingMessage,
res: ServerResponse,
url: URL,
_ctx: HostApiContext,
): Promise<boolean> {
if (url.pathname === '/api/apps/nianxx-play/status' && req.method === 'GET') {
sendJson(res, 200, await getNianxxPlayServiceStatus());
return true;
}
if (url.pathname === '/api/apps/nianxx-play/start' && req.method === 'POST') {
sendJson(res, 200, await ensureNianxxPlayServiceStarted());
return true;
}
return false;
}

View File

@@ -27,10 +27,7 @@ import {
listAgentsSnapshotFromConfig,
} from '../../utils/agent-config';
import {
ensureDingTalkPluginInstalled,
ensureFeishuPluginInstalled,
ensureWeChatPluginInstalled,
ensureWeComPluginInstalled,
} from '../../utils/plugin-install';
import {
computeChannelRuntimeStatus,
@@ -83,6 +80,7 @@ import type { HostApiContext } from '../context';
import { parseJsonBody, sendJson } from '../route-utils';
const WECHAT_QR_TIMEOUT_MS = 8 * 60 * 1000;
const DISABLED_PLUGIN_CHANNEL_TYPES = new Set(['dingtalk', 'wecom', 'feishu']);
const activeQrLogins = new Map<string, string>();
interface WebLoginStartResult {
@@ -1559,27 +1557,9 @@ export async function handleChannelRoutes(
return true;
}
const storedChannelType = resolveStoredChannelType(body.channelType);
if (storedChannelType === 'dingtalk') {
const installResult = await ensureDingTalkPluginInstalled();
if (!installResult.installed) {
sendJson(res, 500, { success: false, error: installResult.warning || 'DingTalk plugin install failed' });
return true;
}
}
if (storedChannelType === 'wecom') {
const installResult = await ensureWeComPluginInstalled();
if (!installResult.installed) {
sendJson(res, 500, { success: false, error: installResult.warning || 'WeCom plugin install failed' });
return true;
}
}
// QQBot is a built-in channel since OpenClaw 3.31 — no plugin install needed
if (storedChannelType === 'feishu') {
const installResult = await ensureFeishuPluginInstalled();
if (!installResult.installed) {
sendJson(res, 500, { success: false, error: installResult.warning || 'Feishu plugin install failed' });
return true;
}
if (DISABLED_PLUGIN_CHANNEL_TYPES.has(storedChannelType)) {
sendJson(res, 400, { success: false, error: '当前内测版本未启用该渠道' });
return true;
}
if (storedChannelType === OPENCLAW_WECHAT_CHANNEL_TYPE) {
const installResult = await ensureWeChatPluginInstalled();

View File

@@ -4,6 +4,7 @@ import type { IncomingMessage, ServerResponse } from 'http';
import { logger } from '../../utils/logger';
import { getOpenClawConfigDir } from '../../utils/paths';
import { buildGatewayHealthSummary } from '../../utils/gateway-health';
import { buildYinianModelConfigDiagnostics, ensureYinianModelRuntimeConfigured } from '../../utils/model-diagnostics';
import type { HostApiContext } from '../context';
import { sendJson } from '../route-utils';
import { buildChannelAccountsView, getChannelStatusDiagnostics } from './channels';
@@ -82,5 +83,17 @@ export async function handleDiagnosticsRoutes(
return true;
}
if (url.pathname === '/api/diagnostics/model-config' && req.method === 'GET') {
try {
if (url.searchParams.get('repair') === '1') {
await ensureYinianModelRuntimeConfigured();
}
sendJson(res, 200, await buildYinianModelConfigDiagnostics());
} catch (error) {
sendJson(res, 500, { success: false, error: String(error) });
}
return true;
}
return false;
}

View File

@@ -118,7 +118,7 @@ export async function handleGatewayRoutes(
if (imageAttachments.length > 0) {
rpcParams.attachments = imageAttachments;
}
const result = await ctx.gatewayManager.rpc('chat.send', rpcParams, 120000);
const result = await ctx.gatewayManager.rpc('chat.send', rpcParams, 330000);
sendJson(res, 200, { success: true, result });
} catch (error) {
sendJson(res, 500, { success: false, error: String(error) });

View File

@@ -1,7 +1,7 @@
import type { IncomingMessage, ServerResponse } from 'node:http';
import crypto from 'node:crypto';
import { basename, extname, join } from 'node:path';
import { mkdir, readFile, stat, writeFile, copyFile } from 'node:fs/promises';
import { mkdir, readFile, stat, writeFile, copyFile, rm } from 'node:fs/promises';
import type { HostApiContext } from '../context';
import { parseJsonBody, sendJson } from '../route-utils';
import { getDataDir } from '../../utils/paths';
@@ -225,6 +225,27 @@ export async function buildKnowledgeContext(params: {
};
}
async function deleteKnowledgeDocument(params: {
workspaceId?: string;
documentId: string;
}): Promise<{ deleted: KnowledgeDocument | null }> {
const workspaceId = sanitizeWorkspaceId(params.workspaceId);
const documentId = params.documentId.trim();
if (!documentId) return { deleted: null };
const registry = await readRegistry(workspaceId);
const target = registry.find((doc) => doc.id === documentId);
if (!target) return { deleted: null };
await Promise.all([
target.storedPath ? rm(target.storedPath, { force: true }).catch(() => undefined) : Promise.resolve(),
target.textPath ? rm(target.textPath, { force: true }).catch(() => undefined) : Promise.resolve(),
]);
await writeRegistry(workspaceId, registry.filter((doc) => doc.id !== documentId));
return { deleted: target };
}
export async function handleKnowledgeRoutes(
req: IncomingMessage,
res: ServerResponse,
@@ -238,6 +259,22 @@ export async function handleKnowledgeRoutes(
return true;
}
if (url.pathname.startsWith('/api/knowledge/files/') && req.method === 'DELETE') {
try {
const documentId = decodeURIComponent(url.pathname.slice('/api/knowledge/files/'.length));
const workspaceId = sanitizeWorkspaceId(url.searchParams.get('workspaceId') ?? undefined);
const result = await deleteKnowledgeDocument({ workspaceId, documentId });
if (!result.deleted) {
sendJson(res, 404, { success: false, error: 'Knowledge document not found' });
return true;
}
sendJson(res, 200, { success: true, document: result.deleted });
} catch (error) {
sendJson(res, 500, { success: false, error: String(error) });
}
return true;
}
if (url.pathname === '/api/knowledge/import-paths' && req.method === 'POST') {
try {
const body = await parseJsonBody<{ workspaceId?: string; filePaths?: string[] }>(req);

View File

@@ -5,6 +5,7 @@ import { logger } from '../utils/logger';
import { extensionRegistry } from '../extensions/registry';
import type { HostApiContext } from './context';
import { handleAppRoutes } from './routes/app';
import { handleAppIntegrationRoutes } from './routes/apps';
import { handleGatewayRoutes } from './routes/gateway';
import { handleSettingsRoutes } from './routes/settings';
import { handleProviderRoutes } from './routes/providers';
@@ -29,6 +30,7 @@ type RouteHandler = (
const coreRouteHandlers: RouteHandler[] = [
handleAppRoutes,
handleAppIntegrationRoutes,
handleGatewayRoutes,
handleSettingsRoutes,
handleProviderRoutes,