openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
374 lines (373 loc) • 16.2 kB
JavaScript
import { c as normalizeOptionalString, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { y as resolveStateDir } from "./paths-mvMm5bYV.js";
import { i as formatErrorMessage } from "./errors-BXgSefBE.js";
import { _ as uniqueStrings, d as normalizeTrimmedStringList } from "./string-normalization-WNUDCpXX.js";
import { c as isBundledSourceOverlayPath } from "./discovery-BDSfxwT6.js";
import { c as isRecord$1 } from "./utils-CCC-BEJH.js";
import { t as createSubsystemLogger } from "./subsystem-BzXSmsuh.js";
import { n as getCachedPluginModuleLoader } from "./plugin-module-loader-cache-lz0qhu-X.js";
import { t as listChannelCatalogEntries } from "./channel-catalog-registry-C_kZJqje.js";
import { h as resolveExistingPluginModulePath, m as loadChannelPluginModule } from "./bundled-DcYUmgzC.js";
import { r as listBundledChannelIds } from "./bootstrap-registry-DdZI5Mh6.js";
import { i as hasNonEmptyString } from "./channel-target-ICEok4QR.js";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
//#region packages/model-catalog-core/src/configured-model-refs.ts
/** Narrow unknown values to plain records. */
function isRecord(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
/** Agent config keys that can contain direct model references. */
const AGENT_MODEL_CONFIG_KEYS = [
"model",
"imageModel",
"imageGenerationModel",
"videoGenerationModel",
"musicGenerationModel",
"voiceModel",
"pdfModel"
];
/** Collect configured model references from agents, channels, hooks, and message config. */
function collectConfiguredModelRefs(config, options = {}) {
const refs = [];
const pushModelRef = (path, value) => {
if (typeof value === "string" && value.trim()) refs.push({
path,
value: value.trim()
});
};
const collectModelConfig = (path, value) => {
if (typeof value === "string") {
pushModelRef(path, value);
return;
}
if (!isRecord(value)) return;
pushModelRef(`${path}.primary`, value.primary);
if (Array.isArray(value.fallbacks)) for (const [index, entry] of value.fallbacks.entries()) pushModelRef(`${path}.fallbacks.${index}`, entry);
};
const collectFromAgent = (path, agent) => {
if (!isRecord(agent)) return;
for (const key of AGENT_MODEL_CONFIG_KEYS) collectModelConfig(`${path}.${key}`, agent[key]);
pushModelRef(`${path}.heartbeat.model`, isRecord(agent.heartbeat) ? agent.heartbeat.model : void 0);
collectModelConfig(`${path}.subagents.model`, isRecord(agent.subagents) ? agent.subagents.model : void 0);
if (isRecord(agent.compaction)) {
pushModelRef(`${path}.compaction.model`, agent.compaction.model);
pushModelRef(`${path}.compaction.memoryFlush.model`, isRecord(agent.compaction.memoryFlush) ? agent.compaction.memoryFlush.model : void 0);
}
if (isRecord(agent.models)) for (const modelRef of Object.keys(agent.models)) pushModelRef(`${path}.models.${modelRef}`, modelRef);
};
const root = isRecord(config) ? config : {};
const agents = isRecord(root.agents) ? root.agents : {};
collectFromAgent("agents.defaults", agents.defaults);
if (Array.isArray(agents.list)) for (const [index, entry] of agents.list.entries()) collectFromAgent(`agents.list.${index}`, entry);
if (options.includeChannelModelOverrides !== false) {
const channels = isRecord(root.channels) ? root.channels : {};
const modelByChannel = isRecord(channels.modelByChannel) ? channels.modelByChannel : {};
for (const [channelId, channelMap] of Object.entries(modelByChannel)) {
if (!isRecord(channelMap)) continue;
for (const [targetId, modelRef] of Object.entries(channelMap)) pushModelRef(`channels.modelByChannel.${channelId}.${targetId}`, modelRef);
}
}
const hooks = isRecord(root.hooks) ? root.hooks : {};
if (Array.isArray(hooks.mappings)) for (const [index, mapping] of hooks.mappings.entries()) pushModelRef(`hooks.mappings.${index}.model`, isRecord(mapping) ? mapping.model : void 0);
pushModelRef("hooks.gmail.model", isRecord(hooks.gmail) ? hooks.gmail.model : void 0);
pushModelRef("messages.tts.summaryModel", isRecord(root.messages) && isRecord(root.messages.tts) ? root.messages.tts.summaryModel : void 0);
pushModelRef("channels.discord.voice.model", isRecord(root.channels) && isRecord(root.channels.discord) && isRecord(root.channels.discord.voice) ? root.channels.discord.voice.model : void 0);
return refs;
}
/** Collect only configured model reference values. */
function collectConfiguredModelRefValues(config, options) {
return collectConfiguredModelRefs(config, options).map((ref) => ref.value);
}
//#endregion
//#region src/channels/plugins/package-state-probes.ts
/**
* Bundled channel package-state probes.
*
* Resolves lightweight configured/auth state checkers from package metadata and source overlays.
*/
const log = createSubsystemLogger("channels");
const sourcePackageStateLoaderCache = /* @__PURE__ */ new Map();
function isSourceModulePath(modulePath) {
return /\.(?:c|m)?tsx?$/iu.test(modulePath);
}
function loadChannelPackageStateModule(params) {
try {
return loadChannelPluginModule(params);
} catch (error) {
if (!isSourceModulePath(params.modulePath)) throw error;
return getCachedPluginModuleLoader({
cache: sourcePackageStateLoaderCache,
modulePath: params.modulePath,
importerUrl: import.meta.url,
tryNative: true,
cacheScopeKey: "channel-package-state"
})(params.modulePath);
}
}
function hasNonEmptyEnvValue(env, key) {
return typeof env?.[key] === "string" && env[key].trim().length > 0;
}
function resolveSourceBundledPluginRoot(rootDir) {
const pluginRoot = path.resolve(rootDir);
const extensionsDir = path.dirname(pluginRoot);
if (path.basename(extensionsDir) !== "extensions") return null;
const packageRoot = path.dirname(extensionsDir);
if (path.basename(packageRoot) === "dist" || path.basename(packageRoot) === "dist-runtime") return null;
return {
packageRoot,
dirName: path.basename(pluginRoot)
};
}
function isBundledSourceOverlayPluginRoot(rootDir) {
const pluginRoot = path.resolve(rootDir);
return isBundledSourceOverlayPath({ sourcePath: pluginRoot }) || path.basename(path.dirname(pluginRoot)) === "extensions" && isBundledSourceOverlayPath({ sourcePath: path.dirname(pluginRoot) });
}
function listBuiltBundledPackageStateModules(params) {
if (isBundledSourceOverlayPluginRoot(params.rootDir)) return [];
const sourceRoot = resolveSourceBundledPluginRoot(params.rootDir);
if (!sourceRoot) return [];
const locations = [];
for (const rootDir of [path.join(sourceRoot.packageRoot, "dist", "extensions", sourceRoot.dirName), path.join(sourceRoot.packageRoot, "dist-runtime", "extensions", sourceRoot.dirName)]) {
const modulePath = resolveExistingPluginModulePath(rootDir, params.specifier);
if (fs.existsSync(modulePath) && !isSourceModulePath(modulePath)) locations.push({
modulePath,
rootDir
});
}
return locations;
}
function resolveChannelPackageStateModuleLocation(params) {
return {
modulePath: resolveExistingPluginModulePath(params.entry.rootDir, params.specifier),
rootDir: params.entry.rootDir
};
}
function listChannelPackageStateModuleLocations(params) {
const source = resolveChannelPackageStateModuleLocation(params);
return [...listBuiltBundledPackageStateModules({
rootDir: params.entry.rootDir,
specifier: params.specifier
}).filter((location) => location.modulePath !== source.modulePath), source];
}
function resolveChannelPackageStateMetadata(entry, metadataKey) {
const metadata = entry.channel[metadataKey];
if (!metadata || typeof metadata !== "object") return null;
const specifier = normalizeOptionalString(metadata.specifier) ?? "";
const exportName = normalizeOptionalString(metadata.exportName) ?? "";
const envMetadata = "env" in metadata ? metadata.env : void 0;
const allOf = normalizeTrimmedStringList(envMetadata?.allOf);
const anyOf = normalizeTrimmedStringList(envMetadata?.anyOf);
const env = allOf.length > 0 || anyOf.length > 0 ? {
allOf,
anyOf
} : void 0;
if ((!specifier || !exportName) && !env) return null;
return {
...specifier ? { specifier } : {},
...exportName ? { exportName } : {},
...env ? { env } : {}
};
}
function listChannelPackageStateCatalog(metadataKey, discovery) {
return listChannelCatalogEntries({
origin: "bundled",
discovery
}).filter((entry) => Boolean(resolveChannelPackageStateMetadata(entry, metadataKey)));
}
function resolveChannelPackageStateChecker(params) {
const metadata = resolveChannelPackageStateMetadata(params.entry, params.metadataKey);
if (!metadata) return null;
if (metadata.env) return ({ env }) => {
const allOf = metadata.env?.allOf ?? [];
const anyOf = metadata.env?.anyOf ?? [];
return allOf.every((key) => hasNonEmptyEnvValue(env, key)) && (anyOf.length === 0 || anyOf.some((key) => hasNonEmptyEnvValue(env, key)));
};
let loadError;
for (const location of listChannelPackageStateModuleLocations({
entry: params.entry,
specifier: metadata.specifier
})) try {
const checker = loadChannelPackageStateModule({
modulePath: location.modulePath,
rootDir: location.rootDir
})[metadata.exportName];
if (typeof checker !== "function") throw new Error(`missing ${params.metadataKey} export ${metadata.exportName}`);
return checker;
} catch (error) {
loadError = error;
}
if (loadError) {
const detail = formatErrorMessage(loadError);
log.warn(`[channels] failed to load ${params.metadataKey} checker for ${params.entry.pluginId}: ${detail}`);
}
return null;
}
function resolvePackageStateChannelId(entry) {
return normalizeOptionalString(entry.channel.id);
}
/**
* Lists bundled channel ids that declare the requested package-state metadata.
*/
function listBundledChannelIdsForPackageState(metadataKey, discovery) {
return listChannelPackageStateCatalog(metadataKey, discovery).map((entry) => resolvePackageStateChannelId(entry)).filter((channelId) => Boolean(channelId)).toSorted((left, right) => left.localeCompare(right));
}
/**
* Returns whether a bundled channel reports configured/auth package state.
*/
function hasBundledChannelPackageState(params) {
const requestedChannelId = normalizeOptionalString(params.channelId);
const entry = listChannelPackageStateCatalog(params.metadataKey, params.discovery).find((candidate) => resolvePackageStateChannelId(candidate) === requestedChannelId);
if (!entry) return false;
const checker = resolveChannelPackageStateChecker({
entry,
metadataKey: params.metadataKey
});
return checker ? checker({
cfg: params.cfg,
env: params.env
}) : false;
}
//#endregion
//#region src/channels/plugins/persisted-auth-state.ts
/**
* Lists bundled channels that declare persisted-auth state metadata.
*/
function listBundledChannelIdsWithPersistedAuthState(discovery) {
return listBundledChannelIdsForPackageState("persistedAuthState", discovery);
}
/**
* Returns whether a bundled channel reports persisted auth state.
*/
function hasBundledChannelPersistedAuthState(params) {
return hasBundledChannelPackageState({
metadataKey: "persistedAuthState",
channelId: params.channelId,
cfg: params.cfg,
env: params.env,
discovery: params.discovery
});
}
//#endregion
//#region src/channels/config-presence.ts
/**
* Channel configuration presence detection.
*
* Finds channels made available by config, env, persisted auth, or plugin discovery signals.
*/
const IGNORED_CHANNEL_CONFIG_KEYS = new Set(["defaults", "modelByChannel"]);
/** Returns true when a channel config entry contains settings beyond enabled/disabled state. */
function hasMeaningfulChannelConfig(value) {
if (!isRecord$1(value)) return false;
return Object.keys(value).some((key) => key !== "enabled");
}
/** Lists channels explicitly disabled in config so activation logic can suppress auto-detection. */
function listExplicitlyDisabledChannelIdsForConfig(cfg) {
const channels = isRecord$1(cfg.channels) ? cfg.channels : null;
if (!channels) return [];
return Object.entries(channels).filter(([, value]) => isRecord$1(value) && value.enabled === false).map(([channelId]) => normalizeOptionalLowercaseString(channelId)).filter((channelId) => Boolean(channelId));
}
function listChannelEnvPrefixes(channelIds) {
return channelIds.map((channelId) => [`${channelId.replace(/[^a-z0-9]+/gi, "_").toUpperCase()}_`, channelId]);
}
function hasPersistedChannelState(env) {
return fs.existsSync(resolveStateDir(env, os.homedir));
}
let persistedAuthStateChannelIds = null;
function listPersistedAuthStateChannelIds(options) {
const override = options.persistedAuthStateProbe?.listChannelIds();
if (override) return override;
if (options.discovery) return listBundledChannelIdsWithPersistedAuthState(options.discovery);
if (persistedAuthStateChannelIds) return persistedAuthStateChannelIds;
persistedAuthStateChannelIds = listBundledChannelIdsWithPersistedAuthState();
return persistedAuthStateChannelIds;
}
function hasPersistedAuthState(params) {
const override = params.options.persistedAuthStateProbe;
if (override) return override.hasState(params);
return hasBundledChannelPersistedAuthState({
channelId: params.channelId,
cfg: params.cfg,
env: params.env,
discovery: params.options.discovery
});
}
/** Lists channel ids detected from config, env vars, or persisted auth state. */
function listPotentialConfiguredChannelIds(cfg, env = process.env, options = {}) {
return uniqueStrings(listPotentialConfiguredChannelPresenceSignals(cfg, env, options).map((signal) => signal.channelId));
}
/** Lists deduplicated channel presence signals with their detection source. */
function listPotentialConfiguredChannelPresenceSignals(cfg, env = process.env, options = {}) {
const signals = [];
const seenSignals = /* @__PURE__ */ new Set();
const addSignal = (channelId, source) => {
const key = `${source}:${channelId}`;
if (seenSignals.has(key)) return;
seenSignals.add(key);
signals.push({
channelId,
source
});
};
const configuredChannelIds = /* @__PURE__ */ new Set();
const channelEnvPrefixes = listChannelEnvPrefixes(options.channelIds ?? listBundledChannelIds(env, options.discovery));
const channels = isRecord$1(cfg.channels) ? cfg.channels : null;
if (channels) for (const [key, value] of Object.entries(channels)) {
if (IGNORED_CHANNEL_CONFIG_KEYS.has(key)) continue;
if (hasMeaningfulChannelConfig(value)) {
configuredChannelIds.add(key);
addSignal(key, "config");
}
}
for (const [key, value] of Object.entries(env)) {
if (!hasNonEmptyString(value)) continue;
for (const [prefix, channelId] of channelEnvPrefixes) if (key.startsWith(prefix)) {
configuredChannelIds.add(channelId);
addSignal(channelId, "env");
}
}
if (options.includePersistedAuthState !== false && hasPersistedChannelState(env)) {
for (const channelId of listPersistedAuthStateChannelIds(options)) if (hasPersistedAuthState({
channelId,
cfg,
env,
options
})) {
configuredChannelIds.add(channelId);
addSignal(channelId, "persisted-auth");
}
}
return signals.filter((signal) => configuredChannelIds.has(signal.channelId));
}
//#endregion
//#region src/secrets/channel-env-var-names.ts
/** Ambient process env names that are too common to imply channel configuration. */
const UNSAFE_CHANNEL_ENV_VAR_TRIGGER_NAMES = new Set([
"CI",
"HOME",
"LANG",
"LC_ALL",
"LC_CTYPE",
"LOGNAME",
"NODE_ENV",
"OLDPWD",
"PATH",
"PWD",
"SHELL",
"SSH_AUTH_SOCK",
"TEMP",
"TERM",
"TMP",
"TMPDIR",
"USER"
]);
/**
* Returns whether a channel env var name is safe to treat as a credential/config trigger.
*/
function isSafeChannelEnvVarTriggerName(key) {
const normalized = key.trim().toUpperCase();
return /^[A-Z][A-Z0-9_]*$/.test(normalized) && !UNSAFE_CHANNEL_ENV_VAR_TRIGGER_NAMES.has(normalized);
}
//#endregion
export { listPotentialConfiguredChannelPresenceSignals as a, AGENT_MODEL_CONFIG_KEYS as c, listPotentialConfiguredChannelIds as i, collectConfiguredModelRefValues as l, hasMeaningfulChannelConfig as n, hasBundledChannelPackageState as o, listExplicitlyDisabledChannelIdsForConfig as r, listBundledChannelIdsForPackageState as s, isSafeChannelEnvVarTriggerName as t, collectConfiguredModelRefs as u };