Files
Genarrative/apps/ai-game-creator-shell/src/services/pluginHost.ts
T
kdletters c1d26f11df 移除 Cocos 和 Unity 插件的工程类型限制
统一插件列表、启动、面板和 Agent 工具的可用性判断,保留开关与平台约束

调整前端自动启动并修复 Cocos 项目切换的旧连接与订阅快照竞态

补充插件宿主、工具目录和前端回归测试,同步插件技术规范与共享决策
2026-09-20 10:53:16 +08:00

123 lines
3.4 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 { resolveTauriInvoke } from '../app/tauri';
import type {
AgcExtensionSummary,
AgcPluginPanel,
AgcPluginSummary,
} from '../app/types';
export type PluginRpcParams = Record<string, unknown> | unknown[] | null;
function invokeOrThrow() {
const invoke = resolveTauriInvoke();
if (!invoke) throw new Error('需要在 Tauri App 内运行');
return invoke;
}
export async function listAgcPlugins() {
return invokeOrThrow()('list_agc_plugins') as Promise<AgcPluginSummary[]>;
}
export async function listAgcExtensions() {
return invokeOrThrow()('list_agc_extensions') as Promise<
AgcExtensionSummary[]
>;
}
export async function refreshAgcPlugins() {
return invokeOrThrow()('refresh_agc_plugins') as Promise<AgcPluginSummary[]>;
}
export async function startAgcPlugin(id: string) {
return invokeOrThrow()('start_agc_plugin', {
id,
}) as Promise<AgcPluginSummary>;
}
/** 只消费宿主的能力投影;项目类型与平台支持均不在前端再次判断。 */
export async function startAvailableAgcEditorPlugins(
isActive: () => boolean = () => true,
) {
const plugins = await listAgcPlugins();
const available = plugins.filter(
(plugin) =>
plugin.builtin &&
(plugin.id === 'agc-cocos-editor' || plugin.id === 'agc-unity-editor') &&
plugin.enabled &&
plugin.hasRuntime &&
(plugin.status === 'stopped' || plugin.status === 'discovered'),
);
const results = await Promise.allSettled(
available.map((plugin) =>
isActive() ? startAgcPlugin(plugin.id) : Promise.resolve(),
),
);
const errors = results.flatMap((result, index) =>
result.status === 'rejected'
? [
`${available[index]!.name}:${
result.reason instanceof Error
? result.reason.message
: String(result.reason)
}`,
]
: [],
);
if (errors.length) {
throw new Error(errors.join(';'));
}
}
export async function stopAgcPlugin(id: string) {
return invokeOrThrow()('stop_agc_plugin', {
id,
}) as Promise<AgcPluginSummary>;
}
export async function reloadAgcPlugin(id: string) {
return invokeOrThrow()('reload_agc_plugin', {
id,
}) as Promise<AgcPluginSummary>;
}
/** 内置插件的可用开关;禁用后不能启动,对应 Agent 工具也不再出现。 */
export async function setAgcPluginEnabled(id: string, enabled: boolean) {
return invokeOrThrow()('set_agc_plugin_enabled', {
id,
enabled,
}) as Promise<AgcPluginSummary[]>;
}
export async function callAgcPlugin<T = unknown>(
id: string,
method: string,
params: PluginRpcParams = null,
) {
return invokeOrThrow()('call_agc_plugin', {
id,
method,
params,
}) as Promise<T>;
}
export async function readAgcPluginPanel(id: string, panelId: string) {
return invokeOrThrow()('read_agc_plugin_panel', { id, panelId }) as Promise<{
panel: AgcPluginPanel;
html: string;
}>;
}
export async function setAgcPluginProjectPath(projectPath: string | null) {
const invoke = invokeOrThrow();
// 项目 effect 的 cleanup(null) 与下个项目的设置必须按提交顺序抵达宿主。
const next = pluginProjectUpdate
.catch(() => undefined)
.then(
() =>
invoke('set_agc_plugin_project_path', { projectPath }) as Promise<void>,
);
pluginProjectUpdate = next;
return next;
}
let pluginProjectUpdate: Promise<void> = Promise.resolve();