Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  read-only.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { isBlockedObjectKey } from "../../infra/prototype-keys.js";
import {
  hasExplicitChannelConfig,
  listConfiguredChannelIdsForReadOnlyScope,
  resolveDiscoverableScopedChannelPluginIds,
} from "../../plugins/channel-plugin-ids.js";
import { loadOpenClawPlugins } from "../../plugins/loader.js";
import {
  loadPluginManifestRegistry,
  type PluginManifestRecord,
} from "../../plugins/manifest-registry.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
import { sanitizeForLog } from "../../terminal/ansi.js";
import { getBundledChannelSetupPlugin } from "./bundled.js";
import { listChannelPlugins } from "./registry.js";
import type { ChannelPlugin } from "./types.plugin.js";

const SAFE_MANIFEST_CHANNEL_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;

type ReadOnlyChannelPluginOptions = {
  env?: NodeJS.ProcessEnv;
  workspaceDir?: string;
  activationSourceConfig?: OpenClawConfig;
  includePersistedAuthState?: boolean;
  cache?: boolean;
};

type ReadOnlyChannelPluginResolution = {
  plugins: ChannelPlugin[];
  configuredChannelIds: string[];
  missingConfiguredChannelIds: string[];
};
type ManifestChannelConfigRecord = NonNullable<PluginManifestRecord["channelConfigs"]>[string];

function addChannelPlugins(
  byId: Map<string, ChannelPlugin>,
  plugins: Iterable<ChannelPlugin | undefined>,
  options?: {
    onlyIds?: ReadonlySet<string>;
    allowOverwrite?: boolean;
  },
): void {
  for (const plugin of plugins) {
    if (!plugin) {
      continue;
    }
    if (options?.onlyIds && !options.onlyIds.has(plugin.id)) {
      continue;
    }
    if (options?.allowOverwrite === false && byId.has(plugin.id)) {
      continue;
    }
    byId.set(plugin.id, plugin);
  }
}

function rebindChannelScopedString(
  value: string,
  sourceChannelId: string,
  targetChannelId: string,
): string {
  const sourcePrefix = `channels.${sourceChannelId}`;
  if (value === sourcePrefix) {
    return `channels.${targetChannelId}`;
  }
  if (value.startsWith(`${sourcePrefix}.`)) {
    return `channels.${targetChannelId}${value.slice(sourcePrefix.length)}`;
  }
  return value;
}

function isSafeManifestChannelId(channelId: string): boolean {
  return SAFE_MANIFEST_CHANNEL_ID_PATTERN.test(channelId) && !isBlockedObjectKey(channelId);
}

function readOwnRecordValue(record: Record<string, unknown>, key: string): unknown {
  if (isBlockedObjectKey(key) || !Object.prototype.hasOwnProperty.call(record, key)) {
    return undefined;
  }
  return record[key];
}

function normalizeManifestText(value: string | undefined, fallback: string): string {
  return sanitizeForLog(value?.trim() || fallback).trim();
}

function rebindChannelConfig(
  cfg: OpenClawConfig,
  sourceChannelId: string,
  targetChannelId: string,
): OpenClawConfig {
  if (sourceChannelId === targetChannelId || !cfg.channels) {
    return cfg;
  }
  return {
    ...cfg,
    channels: {
      ...cfg.channels,
      [sourceChannelId]: (cfg.channels as Record<string, unknown>)[targetChannelId],
    },
  };
}

function restoreReboundChannelConfig(params: {
  original: OpenClawConfig;
  updated: OpenClawConfig;
  sourceChannelId: string;
  targetChannelId: string;
}): OpenClawConfig {
  if (params.sourceChannelId === params.targetChannelId || !params.updated.channels) {
    return params.updated;
  }
  const nextChannels = { ...params.updated.channels };
  if (Object.prototype.hasOwnProperty.call(nextChannels, params.sourceChannelId)) {
    nextChannels[params.targetChannelId] = nextChannels[params.sourceChannelId];
  } else {
    delete nextChannels[params.targetChannelId];
  }
  if (
    params.original.channels &&
    Object.prototype.hasOwnProperty.call(params.original.channels, params.sourceChannelId)
  ) {
    nextChannels[params.sourceChannelId] = params.original.channels[params.sourceChannelId];
  } else {
    delete nextChannels[params.sourceChannelId];
  }
  return {
    ...params.updated,
    channels: nextChannels,
  };
}

function getChannelConfigRecord(cfg: OpenClawConfig, channelId: string): Record<string, unknown> {
  if (!isSafeManifestChannelId(channelId)) {
    return {};
  }
  const channels = cfg.channels;
  if (!channels || typeof channels !== "object" || Array.isArray(channels)) {
    return {};
  }
  const entry = readOwnRecordValue(channels as Record<string, unknown>, channelId);
  return entry && typeof entry === "object" && !Array.isArray(entry)
    ? (entry as Record<string, unknown>)
    : {};
}

function listManifestChannelAccountIds(cfg: OpenClawConfig, channelId: string): string[] {
  const channelConfig = getChannelConfigRecord(cfg, channelId);
  const accounts = channelConfig.accounts;
  if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
    return [
      ...new Set(
        Object.keys(accounts)
          .filter((accountId) => !isBlockedObjectKey(accountId))
          .map((accountId) => normalizeAccountId(accountId))
          .filter((accountId) => !isBlockedObjectKey(accountId)),
      ),
    ].toSorted((left, right) => left.localeCompare(right));
  }
  return hasExplicitChannelConfig({ config: cfg, channelId }) ? [DEFAULT_ACCOUNT_ID] : [];
}

function resolveManifestChannelAccountConfig(params: {
  cfg: OpenClawConfig;
  channelId: string;
  accountId?: string | null;
}): Record<string, unknown> {
  const channelConfig = getChannelConfigRecord(params.cfg, params.channelId);
  const resolvedAccountId = normalizeAccountId(params.accountId);
  const accounts = channelConfig.accounts;
  if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
    const accountConfig = readOwnRecordValue(
      accounts as Record<string, unknown>,
      resolvedAccountId,
    );
    if (accountConfig && typeof accountConfig === "object" && !Array.isArray(accountConfig)) {
      return accountConfig as Record<string, unknown>;
    }
  }
  return channelConfig;
}

function buildManifestChannelPlugin(params: {
  record: PluginManifestRecord;
  channelId: string;
}): ChannelPlugin | undefined {
  if (!isSafeManifestChannelId(params.channelId)) {
    return undefined;
  }
  const channelConfigValue = params.record.channelConfigs
    ? readOwnRecordValue(params.record.channelConfigs as Record<string, unknown>, params.channelId)
    : undefined;
  if (
    !channelConfigValue ||
    typeof channelConfigValue !== "object" ||
    Array.isArray(channelConfigValue)
  ) {
    return undefined;
  }
  const channelConfig = channelConfigValue as ManifestChannelConfigRecord;
  const label =
    normalizeManifestText(channelConfig.label, params.record.name || params.channelId) ||
    params.channelId;
  const blurb = normalizeManifestText(channelConfig.description, params.record.description || "");
  return {
    id: params.channelId,
    meta: {
      id: params.channelId,
      label,
      selectionLabel: label,
      docsPath: `/channels/${encodeURIComponent(params.channelId)}`,
      blurb,
      ...(channelConfig.preferOver?.length ? { preferOver: channelConfig.preferOver } : {}),
    },
    capabilities: { chatTypes: ["direct"] },
    configSchema: {
      schema: channelConfig.schema,
      ...(channelConfig.uiHints ? { uiHints: channelConfig.uiHints } : {}),
      ...(channelConfig.runtime ? { runtime: channelConfig.runtime } : {}),
    },
    config: {
      listAccountIds: (cfg) => listManifestChannelAccountIds(cfg, params.channelId),
      defaultAccountId: () => DEFAULT_ACCOUNT_ID,
      resolveAccount: (cfg, accountId) => ({
        accountId: normalizeAccountId(accountId),
        config: resolveManifestChannelAccountConfig({
          cfg,
          channelId: params.channelId,
          accountId,
        }),
      }),
      isEnabled: (_account, cfg) => getChannelConfigRecord(cfg, params.channelId).enabled !== false,
      isConfigured: (_account, cfg) =>
        hasExplicitChannelConfig({
          config: cfg,
          channelId: params.channelId,
        }),
      hasConfiguredState: ({ cfg }) =>
        hasExplicitChannelConfig({
          config: cfg,
          channelId: params.channelId,
        }),
    },
  };
}

function canUseManifestChannelPlugin(record: PluginManifestRecord): boolean {
  return record.setup?.requiresRuntime === false || !record.setupSource;
}

function rebindChannelPluginConfig(
  config: ChannelPlugin["config"],
  sourceChannelId: string,
  targetChannelId: string,
): ChannelPlugin["config"] {
  const rebind = (cfg: OpenClawConfig) =>
    rebindChannelConfig(cfg, sourceChannelId, targetChannelId);
  return {
    ...config,
    listAccountIds: (cfg) => config.listAccountIds(rebind(cfg)),
    resolveAccount: (cfg, accountId) => config.resolveAccount(rebind(cfg), accountId),
    inspectAccount: config.inspectAccount
      ? (cfg, accountId) => config.inspectAccount?.(rebind(cfg), accountId)
      : undefined,
    defaultAccountId: config.defaultAccountId
      ? (cfg) => config.defaultAccountId?.(rebind(cfg)) ?? ""
      : undefined,
    setAccountEnabled: config.setAccountEnabled
      ? (params) =>
          restoreReboundChannelConfig({
            original: params.cfg,
            updated:
              config.setAccountEnabled?.({ ...params, cfg: rebind(params.cfg) }) ?? params.cfg,
            sourceChannelId,
            targetChannelId,
          })
      : undefined,
    deleteAccount: config.deleteAccount
      ? (params) =>
          restoreReboundChannelConfig({
            original: params.cfg,
            updated: config.deleteAccount?.({ ...params, cfg: rebind(params.cfg) }) ?? params.cfg,
            sourceChannelId,
            targetChannelId,
          })
      : undefined,
    isEnabled: config.isEnabled
      ? (account, cfg) => config.isEnabled?.(account, rebind(cfg)) ?? false
      : undefined,
    disabledReason: config.disabledReason
      ? (account, cfg) => config.disabledReason?.(account, rebind(cfg)) ?? ""
      : undefined,
    isConfigured: config.isConfigured
      ? (account, cfg) => config.isConfigured?.(account, rebind(cfg)) ?? false
      : undefined,
    unconfiguredReason: config.unconfiguredReason
      ? (account, cfg) => config.unconfiguredReason?.(account, rebind(cfg)) ?? ""
      : undefined,
    describeAccount: config.describeAccount
      ? (account, cfg) => config.describeAccount!(account, rebind(cfg))
      : undefined,
    resolveAllowFrom: config.resolveAllowFrom
      ? (params) => config.resolveAllowFrom?.({ ...params, cfg: rebind(params.cfg) })
      : undefined,
    formatAllowFrom: config.formatAllowFrom
      ? (params) => config.formatAllowFrom?.({ ...params, cfg: rebind(params.cfg) }) ?? []
      : undefined,
    hasConfiguredState: config.hasConfiguredState
      ? (params) => config.hasConfiguredState?.({ ...params, cfg: rebind(params.cfg) }) ?? false
      : undefined,
    hasPersistedAuthState: config.hasPersistedAuthState
      ? (params) => config.hasPersistedAuthState?.({ ...params, cfg: rebind(params.cfg) }) ?? false
      : undefined,
    resolveDefaultTo: config.resolveDefaultTo
      ? (params) => config.resolveDefaultTo?.({ ...params, cfg: rebind(params.cfg) })
      : undefined,
  };
}

function rebindChannelPluginSecrets(
  secrets: ChannelPlugin["secrets"],
  sourceChannelId: string,
  targetChannelId: string,
): ChannelPlugin["secrets"] {
  if (!secrets) {
    return undefined;
  }
  return {
    ...secrets,
    secretTargetRegistryEntries: secrets.secretTargetRegistryEntries?.map((entry) => ({
      ...entry,
      id: rebindChannelScopedString(entry.id, sourceChannelId, targetChannelId),
      pathPattern: rebindChannelScopedString(entry.pathPattern, sourceChannelId, targetChannelId),
      ...(entry.refPathPattern
        ? {
            refPathPattern: rebindChannelScopedString(
              entry.refPathPattern,
              sourceChannelId,
              targetChannelId,
            ),
          }
        : {}),
    })),
    unsupportedSecretRefSurfacePatterns: secrets.unsupportedSecretRefSurfacePatterns?.map(
      (pattern) => rebindChannelScopedString(pattern, sourceChannelId, targetChannelId),
    ),
    collectRuntimeConfigAssignments: secrets.collectRuntimeConfigAssignments
      ? (params) =>
          secrets.collectRuntimeConfigAssignments?.({
            ...params,
            config: rebindChannelConfig(params.config, sourceChannelId, targetChannelId),
          })
      : undefined,
  };
}

function cloneChannelPluginForChannelId(plugin: ChannelPlugin, channelId: string): ChannelPlugin {
  if (plugin.id === channelId && plugin.meta.id === channelId) {
    return plugin;
  }
  const sourceChannelId = plugin.id;
  return {
    ...plugin,
    id: channelId,
    meta: {
      ...plugin.meta,
      id: channelId,
    },
    config: rebindChannelPluginConfig(plugin.config, sourceChannelId, channelId),
    secrets: rebindChannelPluginSecrets(plugin.secrets, sourceChannelId, channelId),
  };
}

function addSetupChannelPlugins(
  byId: Map<string, ChannelPlugin>,
  setups: Iterable<{
    pluginId: string;
    plugin: ChannelPlugin;
  }>,
  options: {
    ownedChannelIdsByPluginId: ReadonlyMap<string, readonly string[]>;
    ownedMissingChannelIdsByPluginId: ReadonlyMap<string, readonly string[]>;
  },
): void {
  for (const setup of setups) {
    const ownedMissingChannelIds = options.ownedMissingChannelIdsByPluginId
      .get(setup.pluginId)
      ?.filter(isSafeManifestChannelId);
    if (!ownedMissingChannelIds || ownedMissingChannelIds.length === 0) {
      continue;
    }
    if (ownedMissingChannelIds.includes(setup.plugin.id)) {
      addChannelPlugins(byId, [setup.plugin], {
        onlyIds: new Set(ownedMissingChannelIds),
        allowOverwrite: false,
      });
      addChannelPlugins(
        byId,
        ownedMissingChannelIds
          .filter((channelId) => channelId !== setup.plugin.id)
          .map((channelId) => cloneChannelPluginForChannelId(setup.plugin, channelId)),
        {
          onlyIds: new Set(ownedMissingChannelIds),
          allowOverwrite: false,
        },
      );
      continue;
    }
    const ownedChannelIds = (options.ownedChannelIdsByPluginId.get(setup.pluginId) ?? []).filter(
      isSafeManifestChannelId,
    );
    if (setup.plugin.id !== setup.pluginId && !ownedChannelIds.includes(setup.plugin.id)) {
      continue;
    }
    addChannelPlugins(
      byId,
      ownedMissingChannelIds.map((channelId) =>
        cloneChannelPluginForChannelId(setup.plugin, channelId),
      ),
      {
        onlyIds: new Set(ownedMissingChannelIds),
        allowOverwrite: false,
      },
    );
  }
}

function addManifestChannelPlugins(
  byId: Map<string, ChannelPlugin>,
  records: readonly PluginManifestRecord[],
  options: {
    pluginIds: ReadonlySet<string>;
    channelIds: readonly string[];
  },
): void {
  const channelIds = new Set(options.channelIds);
  for (const record of records) {
    if (!options.pluginIds.has(record.id)) {
      continue;
    }
    if (!canUseManifestChannelPlugin(record)) {
      continue;
    }
    for (const channelId of record.channels) {
      if (!isSafeManifestChannelId(channelId)) {
        continue;
      }
      if (!channelIds.has(channelId)) {
        continue;
      }
      addChannelPlugins(byId, [buildManifestChannelPlugin({ record, channelId })], {
        onlyIds: channelIds,
        allowOverwrite: false,
      });
    }
  }
}

function resolveReadOnlyWorkspaceDir(
  cfg: OpenClawConfig,
  options: ReadOnlyChannelPluginOptions,
): string | undefined {
  return options.workspaceDir ?? resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
}

function listExternalChannelManifestRecords(
  records: readonly PluginManifestRecord[],
): PluginManifestRecord[] {
  return records.filter((plugin) => plugin.origin !== "bundled" && plugin.channels.length > 0);
}

function resolveExternalReadOnlyChannelPluginIds(params: {
  cfg: OpenClawConfig;
  activationSourceConfig?: OpenClawConfig;
  channelIds: readonly string[];
  records: readonly PluginManifestRecord[];
  workspaceDir?: string;
  env: NodeJS.ProcessEnv;
  cache?: boolean;
}): string[] {
  if (params.channelIds.length === 0) {
    return [];
  }
  const candidatePluginIds = resolveDiscoverableScopedChannelPluginIds({
    config: params.cfg,
    activationSourceConfig: params.activationSourceConfig,
    channelIds: params.channelIds,
    workspaceDir: params.workspaceDir,
    env: params.env,
    cache: params.cache,
  });
  if (candidatePluginIds.length === 0) {
    return [];
  }

  const requestedChannelIds = new Set(params.channelIds);
  const candidatePluginIdSet = new Set(candidatePluginIds);
  return params.records
    .filter(
      (plugin) =>
        candidatePluginIdSet.has(plugin.id) &&
        plugin.channels.some((channelId) => requestedChannelIds.has(channelId)),
    )
    .map((plugin) => plugin.id)
    .toSorted((left, right) => left.localeCompare(right));
}

export function listReadOnlyChannelPluginsForConfig(
  cfg: OpenClawConfig,
  options?: ReadOnlyChannelPluginOptions,
): ChannelPlugin[] {
  return resolveReadOnlyChannelPluginsForConfig(cfg, options).plugins;
}

export function resolveReadOnlyChannelPluginsForConfig(
  cfg: OpenClawConfig,
  options: ReadOnlyChannelPluginOptions = {},
): ReadOnlyChannelPluginResolution {
  const env = options.env ?? process.env;
  const workspaceDir = resolveReadOnlyWorkspaceDir(cfg, options);
  const manifestRecords = loadPluginManifestRegistry({
    config: cfg,
    workspaceDir,
    env,
    cache: options.cache,
  }).plugins;
  const externalManifestRecords = listExternalChannelManifestRecords(manifestRecords);
  const configuredChannelIds = [
    ...new Set(
      listConfiguredChannelIdsForReadOnlyScope({
        config: cfg,
        activationSourceConfig: options.activationSourceConfig ?? cfg,
        workspaceDir,
        env,
        cache: options.cache,
        includePersistedAuthState: options.includePersistedAuthState,
        manifestRecords,
      }),
    ),
  ].filter(isSafeManifestChannelId);
  const byId = new Map<string, ChannelPlugin>();

  addChannelPlugins(byId, listChannelPlugins());

  for (const channelId of configuredChannelIds) {
    if (byId.has(channelId)) {
      continue;
    }
    addChannelPlugins(byId, [getBundledChannelSetupPlugin(channelId)]);
  }

  const missingConfiguredChannelIds = configuredChannelIds.filter(
    (channelId) => !byId.has(channelId),
  );
  const externalPluginIds = resolveExternalReadOnlyChannelPluginIds({
    cfg,
    activationSourceConfig: options.activationSourceConfig ?? cfg,
    channelIds: missingConfiguredChannelIds,
    records: externalManifestRecords,
    workspaceDir,
    env,
    cache: options.cache,
  });
  if (externalPluginIds.length > 0) {
    const externalPluginIdSet = new Set(externalPluginIds);
    addManifestChannelPlugins(byId, externalManifestRecords, {
      pluginIds: externalPluginIdSet,
      channelIds: missingConfiguredChannelIds,
    });

    const setupMissingChannelIds = missingConfiguredChannelIds.filter(
      (channelId) => !byId.has(channelId),
    );
    const missingChannelIdSet = new Set(setupMissingChannelIds);
    const ownedChannelIdsByPluginId = new Map(
      externalManifestRecords
        .filter((record) => externalPluginIdSet.has(record.id))
        .map((record) => [record.id, record.channels] as const),
    );
    const ownedMissingChannelIdsByPluginId = new Map(
      [...ownedChannelIdsByPluginId].map(
        ([pluginId, channelIds]) =>
          [pluginId, channelIds.filter((channelId) => missingChannelIdSet.has(channelId))] as const,
      ),
    );
    if (setupMissingChannelIds.length > 0) {
      const registry = loadOpenClawPlugins({
        config: cfg,
        activationSourceConfig: options.activationSourceConfig ?? cfg,
        env,
        workspaceDir,
        cache: false,
        activate: false,
        includeSetupOnlyChannelPlugins: true,
        forceSetupOnlyChannelPlugins: true,
        requireSetupEntryForSetupOnlyChannelPlugins: true,
        onlyPluginIds: externalPluginIds,
      });
      addSetupChannelPlugins(byId, registry.channelSetups, {
        ownedChannelIdsByPluginId,
        ownedMissingChannelIdsByPluginId,
      });
    }
  }

  const plugins = [...byId.values()];
  return {
    plugins,
    configuredChannelIds,
    missingConfiguredChannelIds: configuredChannelIds.filter((channelId) => !byId.has(channelId)),
  };
}

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge