c1d26f11df
统一插件列表、启动、面板和 Agent 工具的可用性判断,保留开关与平台约束 调整前端自动启动并修复 Cocos 项目切换的旧连接与订阅快照竞态 补充插件宿主、工具目录和前端回归测试,同步插件技术规范与共享决策
123 lines
3.4 KiB
TypeScript
123 lines
3.4 KiB
TypeScript
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();
|