Files
openclaw-zero-token/commands/channel-setup/channel-plugin-resolution.ts
sjhu 571e14a236 feat: upgrade to upstream v2026.3.28
Major upgrade from e26988a38 to upstream v2026.3.28 (f9b107928).
Key changes:
- Upstream src/, ui/, extensions/ (89 bundled extensions)
- Zero-token web providers preserved in src/zero-token/
- AskOnce plugin restored and registered as CLI command
- Added missing packages: @anthropic-ai/vertex-sdk, @modelcontextprotocol/sdk
- Fixed tsconfig rootDir, skipLibCheck for plugin-sdk DTS build
- Added askonce to bundled plugin metadata and package.json exports
- Fixed AskOnce CLI command registration (missing commands metadata)
- Restored AskOnce adapter imports (correct 5-level relative paths)
- Removed stale migration artifacts from root directory
2026-03-30 17:58:12 +08:00

193 lines
5.6 KiB
TypeScript

import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
import {
getChannelPluginCatalogEntry,
listChannelPluginCatalogEntries,
type ChannelPluginCatalogEntry,
} from "../../channels/plugins/catalog.js";
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
import type { ChannelId, ChannelPlugin } from "../../channels/plugins/types.js";
import type { OpenClawConfig } from "../../config/config.js";
import type { RuntimeEnv } from "../../runtime.js";
import { createClackPrompter } from "../../wizard/clack-prompter.js";
import type { WizardPrompter } from "../../wizard/prompts.js";
import {
ensureChannelSetupPluginInstalled,
loadChannelSetupPluginRegistrySnapshotForChannel,
} from "./plugin-install.js";
type ChannelPluginSnapshot = {
channels: Array<{ plugin: ChannelPlugin }>;
channelSetups: Array<{ plugin: ChannelPlugin }>;
};
type ResolveInstallableChannelPluginResult = {
cfg: OpenClawConfig;
channelId?: ChannelId;
plugin?: ChannelPlugin;
catalogEntry?: ChannelPluginCatalogEntry;
configChanged: boolean;
};
function resolveWorkspaceDir(cfg: OpenClawConfig) {
return resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
}
function resolveResolvedChannelId(params: {
rawChannel?: string | null;
catalogEntry?: ChannelPluginCatalogEntry;
}): ChannelId | undefined {
const normalized = normalizeChannelId(params.rawChannel);
if (normalized) {
return normalized;
}
if (!params.catalogEntry) {
return undefined;
}
return normalizeChannelId(params.catalogEntry.id) ?? (params.catalogEntry.id as ChannelId);
}
export function resolveCatalogChannelEntry(raw: string, cfg: OpenClawConfig | null) {
const trimmed = raw.trim().toLowerCase();
if (!trimmed) {
return undefined;
}
const workspaceDir = cfg ? resolveWorkspaceDir(cfg) : undefined;
return listChannelPluginCatalogEntries({ workspaceDir }).find((entry) => {
if (entry.id.toLowerCase() === trimmed) {
return true;
}
return (entry.meta.aliases ?? []).some((alias) => alias.trim().toLowerCase() === trimmed);
});
}
function findScopedChannelPlugin(
snapshot: ChannelPluginSnapshot,
channelId: ChannelId,
): ChannelPlugin | undefined {
return (
snapshot.channels.find((entry) => entry.plugin.id === channelId)?.plugin ??
snapshot.channelSetups.find((entry) => entry.plugin.id === channelId)?.plugin
);
}
function loadScopedChannelPlugin(params: {
cfg: OpenClawConfig;
runtime: RuntimeEnv;
channelId: ChannelId;
pluginId?: string;
workspaceDir?: string;
}): ChannelPlugin | undefined {
const snapshot = loadChannelSetupPluginRegistrySnapshotForChannel({
cfg: params.cfg,
runtime: params.runtime,
channel: params.channelId,
...(params.pluginId ? { pluginId: params.pluginId } : {}),
workspaceDir: params.workspaceDir,
});
return findScopedChannelPlugin(snapshot, params.channelId);
}
export async function resolveInstallableChannelPlugin(params: {
cfg: OpenClawConfig;
runtime: RuntimeEnv;
rawChannel?: string | null;
channelId?: ChannelId;
allowInstall?: boolean;
prompter?: WizardPrompter;
supports?: (plugin: ChannelPlugin) => boolean;
}): Promise<ResolveInstallableChannelPluginResult> {
const supports = params.supports ?? (() => true);
let nextCfg = params.cfg;
const workspaceDir = resolveWorkspaceDir(nextCfg);
const catalogEntry =
(params.rawChannel ? resolveCatalogChannelEntry(params.rawChannel, nextCfg) : undefined) ??
(params.channelId
? getChannelPluginCatalogEntry(params.channelId, {
workspaceDir,
})
: undefined);
const channelId =
params.channelId ??
resolveResolvedChannelId({
rawChannel: params.rawChannel,
catalogEntry,
});
if (!channelId) {
return {
cfg: nextCfg,
catalogEntry,
configChanged: false,
};
}
const existing = getChannelPlugin(channelId);
if (existing && supports(existing)) {
return {
cfg: nextCfg,
channelId,
plugin: existing,
catalogEntry,
configChanged: false,
};
}
const resolvedPluginId = catalogEntry?.pluginId;
if (catalogEntry) {
const scoped = loadScopedChannelPlugin({
cfg: nextCfg,
runtime: params.runtime,
channelId,
pluginId: resolvedPluginId,
workspaceDir,
});
if (scoped && supports(scoped)) {
return {
cfg: nextCfg,
channelId,
plugin: scoped,
catalogEntry,
configChanged: false,
};
}
if (params.allowInstall !== false) {
const installResult = await ensureChannelSetupPluginInstalled({
cfg: nextCfg,
entry: catalogEntry,
prompter: params.prompter ?? createClackPrompter(),
runtime: params.runtime,
workspaceDir,
});
nextCfg = installResult.cfg;
const installedPluginId = installResult.pluginId ?? resolvedPluginId;
const installedPlugin = installResult.installed
? loadScopedChannelPlugin({
cfg: nextCfg,
runtime: params.runtime,
channelId,
pluginId: installedPluginId,
workspaceDir: resolveWorkspaceDir(nextCfg),
})
: undefined;
return {
cfg: nextCfg,
channelId,
plugin: installedPlugin ?? existing,
catalogEntry:
installedPluginId && catalogEntry.pluginId !== installedPluginId
? { ...catalogEntry, pluginId: installedPluginId }
: catalogEntry,
configChanged: nextCfg !== params.cfg,
};
}
}
return {
cfg: nextCfg,
channelId,
plugin: existing,
catalogEntry,
configChanged: false,
};
}