mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
fix: honor disabled plugin runtime deps
This commit is contained in:
@@ -46,6 +46,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Plugins/runtime deps: respect explicit plugin and channel disablement when repairing bundled runtime dependencies, so doctor and health checks no longer install deps for disabled configured channels.
|
||||
- Diagnostics: harden tool and model diagnostic events against hostile errors, blocking listeners, and unsafe stability reason fields. Thanks @vincentkoc.
|
||||
- Plugins/onboarding: record local plugin install source metadata without duplicating raw absolute local paths in persisted `plugins.installs`, while preserving linked load-path cleanup. (#70970) Thanks @vincentkoc.
|
||||
- Browser/tool: tell agents not to pass per-call `timeoutMs` on existing-session type, evaluate, and other Chrome MCP actions that reject timeout overrides.
|
||||
|
||||
@@ -537,7 +537,7 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or
|
||||
- Plugins (install smoke + `/plugin` alias + Claude-bundle restart semantics): `pnpm test:docker:plugins` (script: `scripts/e2e/plugins-docker.sh`)
|
||||
- Plugin update unchanged smoke: `pnpm test:docker:plugin-update` (script: `scripts/e2e/plugin-update-unchanged-docker.sh`)
|
||||
- Config reload metadata smoke: `pnpm test:docker:config-reload` (script: `scripts/e2e/config-reload-source-docker.sh`)
|
||||
- Bundled plugin runtime deps: `pnpm test:docker:bundled-channel-deps` builds a small Docker runner image by default, builds and packs OpenClaw once on the host, then mounts that tarball into each Linux install scenario. Reuse the image with `OPENCLAW_SKIP_DOCKER_BUILD=1`, skip the host rebuild after a fresh local build with `OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0`, or point at an existing tarball with `OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ=/path/to/openclaw-*.tgz`. The full Docker aggregate pre-packs this tarball once, then shards bundled channel checks into independent lanes; use `OPENCLAW_BUNDLED_CHANNELS=telegram,slack` to narrow the channel matrix when running the bundled lane directly.
|
||||
- Bundled plugin runtime deps: `pnpm test:docker:bundled-channel-deps` builds a small Docker runner image by default, builds and packs OpenClaw once on the host, then mounts that tarball into each Linux install scenario. Reuse the image with `OPENCLAW_SKIP_DOCKER_BUILD=1`, skip the host rebuild after a fresh local build with `OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0`, or point at an existing tarball with `OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ=/path/to/openclaw-*.tgz`. The full Docker aggregate pre-packs this tarball once, then shards bundled channel checks into independent lanes; use `OPENCLAW_BUNDLED_CHANNELS=telegram,slack` to narrow the channel matrix when running the bundled lane directly. The lane also verifies that `channels.<id>.enabled=false` and `plugins.entries.<id>.enabled=false` suppress doctor/runtime-dependency repair.
|
||||
- Narrow bundled plugin runtime deps while iterating by disabling unrelated scenarios, for example:
|
||||
`OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 pnpm test:docker:bundled-channel-deps`.
|
||||
|
||||
|
||||
@@ -65,6 +65,9 @@ Packaged OpenClaw installs do not eagerly install every bundled plugin's
|
||||
runtime dependency tree. When a bundled OpenClaw-owned plugin is active from
|
||||
plugin config, legacy channel config, or a default-enabled manifest, startup
|
||||
repairs only that plugin's declared runtime dependencies before importing it.
|
||||
Explicit disablement still wins: `plugins.entries.<id>.enabled: false`,
|
||||
`plugins.deny`, `plugins.enabled: false`, and `channels.<id>.enabled: false`
|
||||
prevent automatic bundled runtime-dependency repair for that plugin/channel.
|
||||
External plugins and custom load paths must still be installed through
|
||||
`openclaw plugins install`.
|
||||
|
||||
|
||||
@@ -1457,7 +1457,7 @@
|
||||
"test:coverage:changed": "node scripts/run-vitest.mjs run --config test/vitest/vitest.unit.config.ts --coverage --changed origin/main",
|
||||
"test:docker:all": "node scripts/test-docker-all.mjs",
|
||||
"test:docker:bundled-channel-deps": "bash scripts/e2e/bundled-channel-runtime-deps-docker.sh",
|
||||
"test:docker:bundled-channel-deps:fast": "OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=1 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=1 bash scripts/e2e/bundled-channel-runtime-deps-docker.sh",
|
||||
"test:docker:bundled-channel-deps:fast": "OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=1 OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO=1 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=1 bash scripts/e2e/bundled-channel-runtime-deps-docker.sh",
|
||||
"test:docker:cleanup": "bash scripts/test-cleanup-docker.sh",
|
||||
"test:docker:config-reload": "bash scripts/e2e/config-reload-source-docker.sh",
|
||||
"test:docker:cron-mcp-cleanup": "bash scripts/e2e/cron-mcp-cleanup-docker.sh",
|
||||
|
||||
@@ -14,6 +14,7 @@ RUN_UPDATE_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO:-1}"
|
||||
RUN_ROOT_OWNED_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO:-1}"
|
||||
RUN_SETUP_ENTRY_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO:-1}"
|
||||
RUN_LOAD_FAILURE_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO:-1}"
|
||||
RUN_DISABLED_CONFIG_SCENARIO="${OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO:-1}"
|
||||
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" bundled-channel-deps "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "$DOCKER_TARGET"
|
||||
|
||||
@@ -863,6 +864,124 @@ EOF
|
||||
rm -f "$run_log"
|
||||
}
|
||||
|
||||
run_disabled_config_scenario() {
|
||||
local run_log
|
||||
run_log="$(mktemp "${TMPDIR:-/tmp}/openclaw-bundled-channel-disabled-config.XXXXXX")"
|
||||
|
||||
echo "Running bundled channel disabled-config runtime deps Docker E2E..."
|
||||
if ! docker run --rm \
|
||||
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
"${PACKAGE_DOCKER_ARGS[@]}" \
|
||||
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'
|
||||
set -euo pipefail
|
||||
|
||||
export HOME="$(mktemp -d "/tmp/openclaw-bundled-channel-disabled-config.XXXXXX")"
|
||||
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
||||
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
|
||||
export OPENCLAW_NO_ONBOARD=1
|
||||
export OPENCLAW_PLUGIN_STAGE_DIR="$HOME/.openclaw/plugin-runtime-deps"
|
||||
mkdir -p "$OPENCLAW_PLUGIN_STAGE_DIR"
|
||||
|
||||
package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
}
|
||||
|
||||
assert_dep_absent_everywhere() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root="$3"
|
||||
for candidate in \
|
||||
"$root/dist/extensions/$channel/node_modules/$dep_path/package.json" \
|
||||
"$root/dist/extensions/node_modules/$dep_path/package.json" \
|
||||
"$root/node_modules/$dep_path/package.json"; do
|
||||
if [ -f "$candidate" ]; then
|
||||
echo "disabled $channel unexpectedly installed $dep_path at $candidate" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
if find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -path "*/node_modules/$dep_path/package.json" -type f | grep -q .; then
|
||||
echo "disabled $channel unexpectedly staged $dep_path externally" >&2
|
||||
find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -type f | sort | head -160 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Installing mounted OpenClaw package..."
|
||||
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
|
||||
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-disabled-config-install.log 2>&1
|
||||
|
||||
root="$(package_root)"
|
||||
test -d "$root/dist/extensions/telegram"
|
||||
test -d "$root/dist/extensions/discord"
|
||||
test -d "$root/dist/extensions/slack"
|
||||
rm -rf "$root/dist/extensions/telegram/node_modules"
|
||||
rm -rf "$root/dist/extensions/discord/node_modules"
|
||||
rm -rf "$root/dist/extensions/slack/node_modules"
|
||||
|
||||
node - <<'NODE'
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
|
||||
const config = {
|
||||
plugins: {
|
||||
enabled: true,
|
||||
entries: {
|
||||
discord: { enabled: false },
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
telegram: {
|
||||
enabled: false,
|
||||
botToken: "123456:disabled-config-token",
|
||||
dmPolicy: "disabled",
|
||||
groupPolicy: "disabled",
|
||||
},
|
||||
slack: {
|
||||
enabled: false,
|
||||
botToken: "xoxb-disabled-config-token",
|
||||
appToken: "xapp-disabled-config-token",
|
||||
},
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "disabled-plugin-entry-token",
|
||||
dmPolicy: "disabled",
|
||||
groupPolicy: "disabled",
|
||||
},
|
||||
},
|
||||
};
|
||||
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
||||
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
||||
NODE
|
||||
|
||||
if ! openclaw doctor --non-interactive >/tmp/openclaw-disabled-config-doctor.log 2>&1; then
|
||||
echo "doctor failed for disabled-config runtime deps smoke" >&2
|
||||
cat /tmp/openclaw-disabled-config-doctor.log >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
assert_dep_absent_everywhere telegram grammy "$root"
|
||||
assert_dep_absent_everywhere slack @slack/web-api "$root"
|
||||
assert_dep_absent_everywhere discord discord-api-types "$root"
|
||||
|
||||
if grep -Eq "\\[plugins\\] (telegram|slack|discord) installed bundled runtime deps:" /tmp/openclaw-disabled-config-doctor.log; then
|
||||
echo "doctor installed runtime deps for an explicitly disabled channel/plugin" >&2
|
||||
cat /tmp/openclaw-disabled-config-doctor.log >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "bundled channel disabled-config runtime deps Docker E2E passed"
|
||||
EOF
|
||||
then
|
||||
cat "$run_log"
|
||||
rm -f "$run_log"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat "$run_log"
|
||||
rm -f "$run_log"
|
||||
}
|
||||
|
||||
run_update_scenario() {
|
||||
local run_log
|
||||
run_log="$(mktemp "${TMPDIR:-/tmp}/openclaw-bundled-channel-update.XXXXXX")"
|
||||
@@ -1392,6 +1511,9 @@ fi
|
||||
if [ "$RUN_SETUP_ENTRY_SCENARIO" != "0" ]; then
|
||||
run_setup_entry_scenario
|
||||
fi
|
||||
if [ "$RUN_DISABLED_CONFIG_SCENARIO" != "0" ]; then
|
||||
run_disabled_config_scenario
|
||||
fi
|
||||
if [ "$RUN_LOAD_FAILURE_SCENARIO" != "0" ]; then
|
||||
run_load_failure_scenario
|
||||
fi
|
||||
|
||||
@@ -31,6 +31,20 @@ function writeBundledChannelPlugin(root: string, id: string, dependencies: Recor
|
||||
});
|
||||
}
|
||||
|
||||
function writeDefaultEnabledBundledChannelPlugin(
|
||||
root: string,
|
||||
id: string,
|
||||
dependencies: Record<string, string>,
|
||||
) {
|
||||
writeBundledChannelPlugin(root, id, dependencies);
|
||||
writeJson(path.join(root, "dist", "extensions", id, "openclaw.plugin.json"), {
|
||||
id,
|
||||
channels: [id],
|
||||
enabledByDefault: true,
|
||||
configSchema: { type: "object" },
|
||||
});
|
||||
}
|
||||
|
||||
function createInstalledRuntimeDeps(): InstalledRuntimeDeps {
|
||||
return [];
|
||||
}
|
||||
@@ -153,7 +167,7 @@ describe("doctor bundled plugin runtime deps", () => {
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("can include disabled but configured bundled channel deps for doctor recovery", () => {
|
||||
it("does not include explicitly disabled but configured bundled channel deps", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
writeBundledChannelPlugin(root, "telegram", { "telegram-only": "1.0.0" });
|
||||
@@ -169,12 +183,77 @@ describe("doctor bundled plugin runtime deps", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.missing).toEqual([]);
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("includes configured bundled channel deps for doctor recovery when not explicitly disabled", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
writeBundledChannelPlugin(root, "telegram", { "telegram-only": "1.0.0" });
|
||||
|
||||
const result = scanBundledPluginRuntimeDeps({
|
||||
packageRoot: root,
|
||||
includeConfiguredChannels: true,
|
||||
config: {
|
||||
plugins: { enabled: true },
|
||||
channels: {
|
||||
telegram: { botToken: "123:abc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.missing.map((dep) => `${dep.name}@${dep.version}`)).toEqual([
|
||||
"telegram-only@1.0.0",
|
||||
]);
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not include configured bundled channel deps when the plugin entry is disabled", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
writeBundledChannelPlugin(root, "telegram", { "telegram-only": "1.0.0" });
|
||||
|
||||
const result = scanBundledPluginRuntimeDeps({
|
||||
packageRoot: root,
|
||||
includeConfiguredChannels: true,
|
||||
config: {
|
||||
plugins: {
|
||||
enabled: true,
|
||||
entries: {
|
||||
telegram: { enabled: false },
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
telegram: { botToken: "123:abc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.missing).toEqual([]);
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("lets channel disablement suppress default-enabled bundled channel deps", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
writeDefaultEnabledBundledChannelPlugin(root, "demo", { "demo-only": "1.0.0" });
|
||||
|
||||
const result = scanBundledPluginRuntimeDeps({
|
||||
packageRoot: root,
|
||||
includeConfiguredChannels: true,
|
||||
config: {
|
||||
plugins: { enabled: true },
|
||||
channels: {
|
||||
demo: { enabled: false },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.missing).toEqual([]);
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("reports default-enabled bundled plugin deps", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
@@ -324,7 +403,7 @@ describe("doctor bundled plugin runtime deps", () => {
|
||||
{
|
||||
installRoot: root,
|
||||
missingSpecs: ["grammy@1.37.0"],
|
||||
installSpecs: ["@slack/web-api@7.15.1", "grammy@1.37.0"],
|
||||
installSpecs: ["grammy@1.37.0"],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ import { createHash } from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { normalizeChatChannelId } from "../channels/ids.js";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveHomeRelativePath } from "../infra/home-dir.js";
|
||||
@@ -571,14 +570,24 @@ function isBundledPluginConfiguredForRuntimeDeps(params: {
|
||||
if (entry?.enabled === true) {
|
||||
return true;
|
||||
}
|
||||
let hasExplicitChannelDisable = false;
|
||||
for (const channelId of readBundledPluginChannels(params.pluginDir)) {
|
||||
const normalizedChannelId = normalizeChatChannelId(channelId);
|
||||
const normalizedChannelId = normalizeOptionalLowercaseString(channelId);
|
||||
if (!normalizedChannelId) {
|
||||
continue;
|
||||
}
|
||||
const channelConfig = (params.config.channels as Record<string, unknown> | undefined)?.[
|
||||
normalizedChannelId
|
||||
];
|
||||
if (
|
||||
channelConfig &&
|
||||
typeof channelConfig === "object" &&
|
||||
!Array.isArray(channelConfig) &&
|
||||
(channelConfig as { enabled?: unknown }).enabled === false
|
||||
) {
|
||||
hasExplicitChannelDisable = true;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
channelConfig &&
|
||||
typeof channelConfig === "object" &&
|
||||
@@ -589,6 +598,9 @@ function isBundledPluginConfiguredForRuntimeDeps(params: {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (hasExplicitChannelDisable) {
|
||||
return false;
|
||||
}
|
||||
return readBundledPluginEnabledByDefault(params.pluginDir);
|
||||
}
|
||||
|
||||
|
||||
@@ -510,6 +510,45 @@ describe("resolveGatewayStartupPluginIds", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not treat explicitly disabled stale channel config as startup intent", () => {
|
||||
expectStartupPluginIdsCase({
|
||||
config: {
|
||||
channels: {
|
||||
"demo-channel": {
|
||||
enabled: false,
|
||||
token: "stale",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
env: {},
|
||||
expected: ["browser"],
|
||||
});
|
||||
});
|
||||
|
||||
it("does not treat explicitly disabled stale channel config as deferred startup intent", () => {
|
||||
loadPluginManifestRegistry
|
||||
.mockReset()
|
||||
.mockReturnValue(createManifestRegistryFixtureWithWorkspaceDemoChannel());
|
||||
|
||||
expect(
|
||||
resolveConfiguredDeferredChannelPluginIds({
|
||||
config: {
|
||||
channels: {
|
||||
"demo-channel": {
|
||||
enabled: false,
|
||||
token: "stale",
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
allow: ["workspace-demo-channel-plugin"],
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
workspaceDir: "/tmp",
|
||||
env: {},
|
||||
}),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("includes the explicitly selected memory slot plugin in startup scope", () => {
|
||||
expectStartupPluginIdsCase({
|
||||
config: createStartupConfig({
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
resolveMemoryDreamingPluginConfig,
|
||||
resolveMemoryDreamingPluginId,
|
||||
} from "../memory-host-sdk/dreaming.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import { resolveManifestActivationPluginIds } from "./activation-planner.js";
|
||||
import { hasExplicitChannelConfig } from "./channel-presence-policy.js";
|
||||
import {
|
||||
@@ -18,6 +19,33 @@ import {
|
||||
import { loadPluginManifestRegistry, type PluginManifestRecord } from "./manifest-registry.js";
|
||||
import { hasKind } from "./slots.js";
|
||||
|
||||
function listDisabledChannelIds(config: OpenClawConfig): Set<string> {
|
||||
const channels = config.channels;
|
||||
if (!channels || typeof channels !== "object" || Array.isArray(channels)) {
|
||||
return new Set();
|
||||
}
|
||||
return new Set(
|
||||
Object.entries(channels)
|
||||
.filter(([, value]) => {
|
||||
return (
|
||||
value &&
|
||||
typeof value === "object" &&
|
||||
!Array.isArray(value) &&
|
||||
(value as { enabled?: unknown }).enabled === false
|
||||
);
|
||||
})
|
||||
.map(([channelId]) => normalizeOptionalLowercaseString(channelId))
|
||||
.filter((channelId): channelId is string => Boolean(channelId)),
|
||||
);
|
||||
}
|
||||
|
||||
function listPotentialEnabledChannelIds(config: OpenClawConfig, env: NodeJS.ProcessEnv): string[] {
|
||||
const disabled = listDisabledChannelIds(config);
|
||||
return listPotentialConfiguredChannelIds(config, env)
|
||||
.map((id) => normalizeOptionalLowercaseString(id) ?? "")
|
||||
.filter((id) => id && !disabled.has(id));
|
||||
}
|
||||
|
||||
function hasRuntimeContractSurface(plugin: PluginManifestRecord): boolean {
|
||||
return Boolean(
|
||||
plugin.providers.length > 0 ||
|
||||
@@ -148,9 +176,7 @@ export function resolveConfiguredDeferredChannelPluginIds(params: {
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): string[] {
|
||||
const configuredChannelIds = new Set(
|
||||
listPotentialConfiguredChannelIds(params.config, params.env).map((id) => id.trim()),
|
||||
);
|
||||
const configuredChannelIds = new Set(listPotentialEnabledChannelIds(params.config, params.env));
|
||||
if (configuredChannelIds.size === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -183,9 +209,7 @@ export function resolveGatewayStartupPluginIds(params: {
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): string[] {
|
||||
const configuredChannelIds = new Set(
|
||||
listPotentialConfiguredChannelIds(params.config, params.env).map((id) => id.trim()),
|
||||
);
|
||||
const configuredChannelIds = new Set(listPotentialEnabledChannelIds(params.config, params.env));
|
||||
const pluginsConfig = normalizePluginsConfig(params.config.plugins);
|
||||
// Startup must classify allowlist exceptions against the raw config snapshot,
|
||||
// not the auto-enabled effective snapshot, or configured-only channels can be
|
||||
|
||||
Reference in New Issue
Block a user