mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:40:44 +00:00
fix(channels): isolate bundled load failures
This commit is contained in:
@@ -58,6 +58,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Diffs/viewer: re-read remote viewer access policy from live runtime config on each request, so toggling `plugins.entries.diffs.config.security.allowRemoteViewer` closes proxied viewer access immediately instead of waiting for a restart. Thanks @vincentkoc.
|
||||
- Agents/subagents: drop bare `NO_REPLY` from the parent turn when the session still has pending spawned children, so direct-conversation surfaces such as Telegram DMs no longer rewrite the sentinel into visible fallback chatter while waiting for the child completion event. (#69942) Thanks @neeravmakwana.
|
||||
- Plugins/install: keep bundled plugin dependencies off npm install while repairing them when plugins activate from a packaged install, including Feishu/Lark, Browser, and direct bundled channel setup-entry loads.
|
||||
- CLI/channels: skip and cache bundled channel plugin, setup, and secrets load failures during read-only discovery, so one broken unused bundled channel cannot crash `openclaw status` or bootstrap secret scans.
|
||||
- Memory/LanceDB: retry initialization after a failed LanceDB load and report unsupported Intel macOS native runtime clearly instead of caching the failure or repeatedly attempting an install that cannot work.
|
||||
- CLI/Claude: hash only static extra system prompt parts when deciding whether to reuse a CLI session, so per-message inbound metadata no longer resets Claude CLI conversations on every turn. (#70122) Thanks @zijunl.
|
||||
- Hooks/Slack: standardize shared message hook routing fields (`threadId` / `replyToId`) and stop Slack outbound delivery from re-running `message_sending` inside the channel adapter, so plugins like thread-ownership make one outbound routing decision per reply. Thanks @vincentkoc.
|
||||
|
||||
@@ -10,6 +10,7 @@ RUN_CHANNEL_SCENARIOS="${OPENCLAW_BUNDLED_CHANNEL_SCENARIOS:-1}"
|
||||
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}"
|
||||
|
||||
echo "Building Docker image..."
|
||||
run_logged bundled-channel-deps-build docker build -t "$IMAGE_NAME" -f "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR"
|
||||
@@ -1001,6 +1002,167 @@ EOF
|
||||
rm -f "$run_log"
|
||||
}
|
||||
|
||||
run_load_failure_scenario() {
|
||||
local run_log
|
||||
run_log="$(mktemp "${TMPDIR:-/tmp}/openclaw-bundled-channel-load-failure.XXXXXX")"
|
||||
|
||||
echo "Running bundled channel load-failure isolation Docker E2E..."
|
||||
if ! docker run --rm \
|
||||
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'
|
||||
set -euo pipefail
|
||||
|
||||
export HOME="$(mktemp -d "/tmp/openclaw-bundled-channel-load-failure.XXXXXX")"
|
||||
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
||||
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
|
||||
export OPENCLAW_NO_ONBOARD=1
|
||||
|
||||
package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
}
|
||||
|
||||
echo "Packing and installing current OpenClaw build..."
|
||||
pack_dir="$(mktemp -d "/tmp/openclaw-load-failure-pack.XXXXXX")"
|
||||
npm pack --ignore-scripts --pack-destination "$pack_dir" >/tmp/openclaw-load-failure-pack.log 2>&1
|
||||
package_tgz="$(find "$pack_dir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)"
|
||||
if [ -z "$package_tgz" ]; then
|
||||
cat /tmp/openclaw-load-failure-pack.log
|
||||
echo "missing packed OpenClaw tarball" >&2
|
||||
exit 1
|
||||
fi
|
||||
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-load-failure-install.log 2>&1
|
||||
|
||||
root="$(package_root)"
|
||||
plugin_dir="$root/dist/extensions/load-failure-alpha"
|
||||
mkdir -p "$plugin_dir"
|
||||
cat >"$plugin_dir/package.json" <<'JSON'
|
||||
{
|
||||
"name": "@openclaw/load-failure-alpha",
|
||||
"version": "2026.4.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": ["./index.js"],
|
||||
"setupEntry": "./setup-entry.js"
|
||||
}
|
||||
}
|
||||
JSON
|
||||
cat >"$plugin_dir/openclaw.plugin.json" <<'JSON'
|
||||
{
|
||||
"id": "load-failure-alpha",
|
||||
"channels": ["load-failure-alpha"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
cat >"$plugin_dir/index.js" <<'JS'
|
||||
export default {
|
||||
kind: "bundled-channel-entry",
|
||||
id: "load-failure-alpha",
|
||||
name: "Load Failure Alpha",
|
||||
description: "Load Failure Alpha",
|
||||
register() {},
|
||||
loadChannelSecrets() {
|
||||
globalThis.__loadFailureSecrets = (globalThis.__loadFailureSecrets ?? 0) + 1;
|
||||
throw new Error("synthetic channel secrets failure");
|
||||
},
|
||||
loadChannelPlugin() {
|
||||
globalThis.__loadFailurePlugin = (globalThis.__loadFailurePlugin ?? 0) + 1;
|
||||
throw new Error("synthetic channel plugin failure");
|
||||
}
|
||||
};
|
||||
JS
|
||||
cat >"$plugin_dir/setup-entry.js" <<'JS'
|
||||
export default {
|
||||
kind: "bundled-channel-setup-entry",
|
||||
loadSetupSecrets() {
|
||||
globalThis.__loadFailureSetupSecrets = (globalThis.__loadFailureSetupSecrets ?? 0) + 1;
|
||||
throw new Error("synthetic setup secrets failure");
|
||||
},
|
||||
loadSetupPlugin() {
|
||||
globalThis.__loadFailureSetup = (globalThis.__loadFailureSetup ?? 0) + 1;
|
||||
throw new Error("synthetic setup plugin failure");
|
||||
}
|
||||
};
|
||||
JS
|
||||
|
||||
echo "Loading synthetic failing bundled channel through packaged loader..."
|
||||
(
|
||||
cd "$root"
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR="$root/dist/extensions" node --input-type=module - <<'NODE'
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const root = process.cwd();
|
||||
const distDir = path.join(root, "dist");
|
||||
const bundledPath = fs
|
||||
.readdirSync(distDir)
|
||||
.filter((entry) => /^bundled-[A-Za-z0-9_-]+\.js$/.test(entry))
|
||||
.map((entry) => path.join(distDir, entry))
|
||||
.find((entry) => fs.readFileSync(entry, "utf8").includes("src/channels/plugins/bundled.ts"));
|
||||
if (!bundledPath) {
|
||||
throw new Error("missing packaged bundled channel loader artifact");
|
||||
}
|
||||
const bundled = await import(pathToFileURL(bundledPath));
|
||||
const oneArgExports = Object.entries(bundled).filter(
|
||||
([, value]) => typeof value === "function" && value.length === 1,
|
||||
);
|
||||
if (oneArgExports.length === 0) {
|
||||
throw new Error(`missing one-argument bundled loader exports; exports=${Object.keys(bundled).join(",")}`);
|
||||
}
|
||||
|
||||
const id = "load-failure-alpha";
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
for (const [name, fn] of oneArgExports) {
|
||||
try {
|
||||
fn(id);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (message.includes("synthetic")) {
|
||||
throw new Error(`bundled export ${name} leaked synthetic load failure: ${message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const counts = {
|
||||
plugin: globalThis.__loadFailurePlugin,
|
||||
setup: globalThis.__loadFailureSetup,
|
||||
secrets: globalThis.__loadFailureSecrets,
|
||||
setupSecrets: globalThis.__loadFailureSetupSecrets,
|
||||
};
|
||||
for (const [key, value] of Object.entries({
|
||||
plugin: counts.plugin,
|
||||
setup: counts.setup,
|
||||
setupSecrets: counts.setupSecrets,
|
||||
})) {
|
||||
if (value !== 1) {
|
||||
throw new Error(`expected ${key} failure to be cached after one load, got ${value}`);
|
||||
}
|
||||
}
|
||||
if (counts.secrets !== undefined && counts.secrets !== 1) {
|
||||
throw new Error(`expected secrets failure to be cached after one load when exercised, got ${counts.secrets}`);
|
||||
}
|
||||
console.log("synthetic bundled channel load failures were isolated and cached");
|
||||
NODE
|
||||
)
|
||||
|
||||
echo "bundled channel load-failure isolation Docker E2E passed"
|
||||
EOF
|
||||
then
|
||||
cat "$run_log"
|
||||
rm -f "$run_log"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat "$run_log"
|
||||
rm -f "$run_log"
|
||||
}
|
||||
|
||||
if [ "$RUN_CHANNEL_SCENARIOS" != "0" ]; then
|
||||
run_channel_scenario telegram grammy
|
||||
run_channel_scenario discord discord-api-types
|
||||
@@ -1017,3 +1179,6 @@ fi
|
||||
if [ "$RUN_SETUP_ENTRY_SCENARIO" != "0" ]; then
|
||||
run_setup_entry_scenario
|
||||
fi
|
||||
if [ "$RUN_LOAD_FAILURE_SCENARIO" != "0" ]; then
|
||||
run_load_failure_scenario
|
||||
fi
|
||||
|
||||
@@ -127,8 +127,15 @@ export function getBootstrapChannelPlugin(id: ChannelId): ChannelPlugin | undefi
|
||||
if (registry.missingIds.has(resolvedId)) {
|
||||
return undefined;
|
||||
}
|
||||
const runtimePlugin = getBundledChannelPlugin(resolvedId);
|
||||
const setupPlugin = getBundledChannelSetupPlugin(resolvedId);
|
||||
let runtimePlugin: ChannelPlugin | undefined;
|
||||
let setupPlugin: ChannelPlugin | undefined;
|
||||
try {
|
||||
runtimePlugin = getBundledChannelPlugin(resolvedId);
|
||||
setupPlugin = getBundledChannelSetupPlugin(resolvedId);
|
||||
} catch {
|
||||
registry.missingIds.add(resolvedId);
|
||||
return undefined;
|
||||
}
|
||||
const merged =
|
||||
runtimePlugin && setupPlugin
|
||||
? mergeBootstrapPlugin(runtimePlugin, setupPlugin)
|
||||
@@ -154,11 +161,21 @@ export function getBootstrapChannelSecrets(id: ChannelId): ChannelPlugin["secret
|
||||
if (registry.secretsById.has(resolvedId)) {
|
||||
return undefined;
|
||||
}
|
||||
const runtimeSecrets = getBundledChannelSecrets(resolvedId);
|
||||
const setupSecrets = getBundledChannelSetupSecrets(resolvedId);
|
||||
const merged = mergePluginSection(runtimeSecrets, setupSecrets);
|
||||
registry.secretsById.set(resolvedId, merged ?? null);
|
||||
return merged;
|
||||
if (registry.missingIds.has(resolvedId)) {
|
||||
registry.secretsById.set(resolvedId, null);
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const runtimeSecrets = getBundledChannelSecrets(resolvedId);
|
||||
const setupSecrets = getBundledChannelSetupSecrets(resolvedId);
|
||||
const merged = mergePluginSection(runtimeSecrets, setupSecrets);
|
||||
registry.secretsById.set(resolvedId, merged ?? null);
|
||||
return merged;
|
||||
} catch {
|
||||
registry.missingIds.add(resolvedId);
|
||||
registry.secretsById.set(resolvedId, null);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function clearBootstrapChannelPluginCache(): void {
|
||||
|
||||
@@ -144,4 +144,78 @@ describe("bundled root-aware caches", () => {
|
||||
bootstrapRegistry.getBootstrapChannelSecrets("beta")?.secretTargetRegistryEntries?.[0]?.id,
|
||||
).toBe("setup-beta-B");
|
||||
});
|
||||
|
||||
it("marks bundled plugin ids missing when bootstrap plugin loading throws", async () => {
|
||||
const root = makeBundledRoot("openclaw-bootstrap-plugin-throw-");
|
||||
|
||||
vi.doMock("./bundled-ids.js", () => ({
|
||||
listBundledChannelPluginIdsForRoot: (cacheKey: string) =>
|
||||
cacheKey === root.pluginsDir ? ["alpha"] : [],
|
||||
}));
|
||||
|
||||
const getBundledChannelPluginMock = vi.fn(() => {
|
||||
throw new Error("Cannot find module 'nostr-tools'");
|
||||
});
|
||||
const getBundledChannelSecretsMock = vi.fn(() => {
|
||||
throw new Error("secrets should not load after plugin is marked missing");
|
||||
});
|
||||
|
||||
vi.doMock("./bundled.js", () => ({
|
||||
getBundledChannelPlugin: getBundledChannelPluginMock,
|
||||
getBundledChannelSetupPlugin: vi.fn(() => undefined),
|
||||
getBundledChannelSecrets: getBundledChannelSecretsMock,
|
||||
getBundledChannelSetupSecrets: vi.fn(() => undefined),
|
||||
}));
|
||||
|
||||
const bootstrapRegistry = await importFreshModule<typeof import("./bootstrap-registry.js")>(
|
||||
import.meta.url,
|
||||
"./bootstrap-registry.js?scope=bootstrap-plugin-load-guard",
|
||||
);
|
||||
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = root.pluginsDir;
|
||||
expect(bootstrapRegistry.listBootstrapChannelPluginIds()).toEqual(["alpha"]);
|
||||
expect(bootstrapRegistry.getBootstrapChannelPlugin("alpha")).toBeUndefined();
|
||||
expect(bootstrapRegistry.getBootstrapChannelPlugin("alpha")).toBeUndefined();
|
||||
expect(bootstrapRegistry.getBootstrapChannelSecrets("alpha")).toBeUndefined();
|
||||
expect(getBundledChannelPluginMock).toHaveBeenCalledTimes(1);
|
||||
expect(getBundledChannelSecretsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("marks bundled plugin ids missing when bootstrap secrets loading throws", async () => {
|
||||
const root = makeBundledRoot("openclaw-bootstrap-secrets-throw-");
|
||||
|
||||
vi.doMock("./bundled-ids.js", () => ({
|
||||
listBundledChannelPluginIdsForRoot: (cacheKey: string) =>
|
||||
cacheKey === root.pluginsDir ? ["alpha"] : [],
|
||||
}));
|
||||
|
||||
const getBundledChannelSecretsMock = vi.fn(() => {
|
||||
throw new Error("Cannot find module '@larksuiteoapi/node-sdk'");
|
||||
});
|
||||
const getBundledChannelPluginMock = vi.fn(() => ({
|
||||
id: "alpha",
|
||||
meta: { id: "alpha", label: "Alpha" },
|
||||
capabilities: {},
|
||||
config: {},
|
||||
}));
|
||||
|
||||
vi.doMock("./bundled.js", () => ({
|
||||
getBundledChannelPlugin: getBundledChannelPluginMock,
|
||||
getBundledChannelSetupPlugin: vi.fn(() => undefined),
|
||||
getBundledChannelSecrets: getBundledChannelSecretsMock,
|
||||
getBundledChannelSetupSecrets: vi.fn(() => undefined),
|
||||
}));
|
||||
|
||||
const bootstrapRegistry = await importFreshModule<typeof import("./bootstrap-registry.js")>(
|
||||
import.meta.url,
|
||||
"./bootstrap-registry.js?scope=bootstrap-secrets-load-guard",
|
||||
);
|
||||
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = root.pluginsDir;
|
||||
expect(bootstrapRegistry.getBootstrapChannelSecrets("alpha")).toBeUndefined();
|
||||
expect(bootstrapRegistry.getBootstrapChannelSecrets("alpha")).toBeUndefined();
|
||||
expect(bootstrapRegistry.getBootstrapChannelPlugin("alpha")).toBeUndefined();
|
||||
expect(getBundledChannelSecretsMock).toHaveBeenCalledTimes(1);
|
||||
expect(getBundledChannelPluginMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -635,6 +635,95 @@ describe("bundled channel entry shape guards", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("swallows and caches bundled plugin and setup load failures", async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-bundled-load-failure-"));
|
||||
const previousBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
||||
const pluginDir = path.join(root, "dist", "extensions", "alpha");
|
||||
const testGlobal = globalThis as typeof globalThis & {
|
||||
__bundledPluginFailureLoads?: number;
|
||||
__bundledSetupFailureLoads?: number;
|
||||
__bundledSecretsFailureLoads?: number;
|
||||
__bundledSetupSecretsFailureLoads?: number;
|
||||
};
|
||||
fs.mkdirSync(pluginDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(root, "package.json"),
|
||||
JSON.stringify({ name: "openclaw", version: "2026.4.21" }),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "index.js"),
|
||||
[
|
||||
"export default {",
|
||||
" kind: 'bundled-channel-entry',",
|
||||
" id: 'alpha',",
|
||||
" name: 'Alpha',",
|
||||
" description: 'Alpha',",
|
||||
" register() {},",
|
||||
" loadChannelSecrets() {",
|
||||
" globalThis.__bundledSecretsFailureLoads = (globalThis.__bundledSecretsFailureLoads ?? 0) + 1;",
|
||||
" throw new Error('missing channel secrets dep');",
|
||||
" },",
|
||||
" loadChannelPlugin() {",
|
||||
" globalThis.__bundledPluginFailureLoads = (globalThis.__bundledPluginFailureLoads ?? 0) + 1;",
|
||||
" throw new Error('missing channel plugin dep');",
|
||||
" },",
|
||||
"};",
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "setup-entry.js"),
|
||||
[
|
||||
"export default {",
|
||||
" kind: 'bundled-channel-setup-entry',",
|
||||
" loadSetupSecrets() {",
|
||||
" globalThis.__bundledSetupSecretsFailureLoads = (globalThis.__bundledSetupSecretsFailureLoads ?? 0) + 1;",
|
||||
" throw new Error('missing setup secrets dep');",
|
||||
" },",
|
||||
" loadSetupPlugin() {",
|
||||
" globalThis.__bundledSetupFailureLoads = (globalThis.__bundledSetupFailureLoads ?? 0) + 1;",
|
||||
" throw new Error('missing setup plugin dep');",
|
||||
" },",
|
||||
"};",
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
mockAlphaDistExtensionRuntime();
|
||||
|
||||
try {
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = path.join(root, "dist", "extensions");
|
||||
|
||||
const bundled = await importFreshModule<typeof import("./bundled.js")>(
|
||||
import.meta.url,
|
||||
"./bundled.js?scope=bundled-load-failure",
|
||||
);
|
||||
|
||||
expect(bundled.getBundledChannelPlugin("alpha")).toBeUndefined();
|
||||
expect(bundled.getBundledChannelPlugin("alpha")).toBeUndefined();
|
||||
expect(bundled.getBundledChannelSetupPlugin("alpha")).toBeUndefined();
|
||||
expect(bundled.getBundledChannelSetupPlugin("alpha")).toBeUndefined();
|
||||
expect(bundled.getBundledChannelSecrets("alpha")).toBeUndefined();
|
||||
expect(bundled.getBundledChannelSecrets("alpha")).toBeUndefined();
|
||||
expect(bundled.getBundledChannelSetupSecrets("alpha")).toBeUndefined();
|
||||
expect(bundled.getBundledChannelSetupSecrets("alpha")).toBeUndefined();
|
||||
expect(testGlobal.__bundledPluginFailureLoads).toBe(1);
|
||||
expect(testGlobal.__bundledSetupFailureLoads).toBe(1);
|
||||
expect(testGlobal.__bundledSecretsFailureLoads).toBe(1);
|
||||
expect(testGlobal.__bundledSetupSecretsFailureLoads).toBe(1);
|
||||
} finally {
|
||||
restoreBundledPluginsDir(previousBundledPluginsDir);
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
delete testGlobal.__bundledPluginFailureLoads;
|
||||
delete testGlobal.__bundledSetupFailureLoads;
|
||||
delete testGlobal.__bundledSecretsFailureLoads;
|
||||
delete testGlobal.__bundledSetupSecretsFailureLoads;
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps channel entrypoints on the dedicated entry-contract SDK surface", () => {
|
||||
const offenders = collectBundledChannelEntrypointOffenders(
|
||||
bundledPluginRoots,
|
||||
|
||||
@@ -66,8 +66,8 @@ type BundledChannelCacheContext = {
|
||||
setupEntryLoadInProgressIds: Set<ChannelId>;
|
||||
lazyEntriesById: Map<ChannelId, GeneratedBundledChannelEntry | null>;
|
||||
lazySetupEntriesById: Map<ChannelId, BundledChannelSetupEntryRuntimeContract | null>;
|
||||
lazyPluginsById: Map<ChannelId, ChannelPlugin>;
|
||||
lazySetupPluginsById: Map<ChannelId, ChannelPlugin>;
|
||||
lazyPluginsById: Map<ChannelId, ChannelPlugin | null>;
|
||||
lazySetupPluginsById: Map<ChannelId, ChannelPlugin | null>;
|
||||
lazySecretsById: Map<ChannelId, ChannelPlugin["secrets"] | null>;
|
||||
lazySetupSecretsById: Map<ChannelId, ChannelPlugin["secrets"] | null>;
|
||||
lazyAccountInspectorsById: Map<
|
||||
@@ -461,9 +461,8 @@ function getBundledChannelPluginForRoot(
|
||||
rootScope: BundledChannelRootScope,
|
||||
cacheContext: BundledChannelCacheContext,
|
||||
): ChannelPlugin | undefined {
|
||||
const cached = cacheContext.lazyPluginsById.get(id);
|
||||
if (cached) {
|
||||
return cached;
|
||||
if (cacheContext.lazyPluginsById.has(id)) {
|
||||
return cacheContext.lazyPluginsById.get(id) ?? undefined;
|
||||
}
|
||||
if (cacheContext.pluginLoadInProgressIds.has(id)) {
|
||||
return undefined;
|
||||
@@ -486,6 +485,11 @@ function getBundledChannelPluginForRoot(
|
||||
};
|
||||
cacheContext.lazyPluginsById.set(id, normalizedPlugin);
|
||||
return normalizedPlugin;
|
||||
} catch (error) {
|
||||
const detail = formatErrorMessage(error);
|
||||
log.warn(`[channels] failed to load bundled channel ${id}: ${detail}`);
|
||||
cacheContext.lazyPluginsById.set(id, null);
|
||||
return undefined;
|
||||
} finally {
|
||||
cacheContext.pluginLoadInProgressIds.delete(id);
|
||||
}
|
||||
@@ -503,11 +507,18 @@ function getBundledChannelSecretsForRoot(
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
const secrets =
|
||||
entry.loadChannelSecrets?.() ??
|
||||
getBundledChannelPluginForRoot(id, rootScope, cacheContext)?.secrets;
|
||||
cacheContext.lazySecretsById.set(id, secrets ?? null);
|
||||
return secrets;
|
||||
try {
|
||||
const secrets =
|
||||
entry.loadChannelSecrets?.() ??
|
||||
getBundledChannelPluginForRoot(id, rootScope, cacheContext)?.secrets;
|
||||
cacheContext.lazySecretsById.set(id, secrets ?? null);
|
||||
return secrets;
|
||||
} catch (error) {
|
||||
const detail = formatErrorMessage(error);
|
||||
log.warn(`[channels] failed to load bundled channel secrets ${id}: ${detail}`);
|
||||
cacheContext.lazySecretsById.set(id, null);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getBundledChannelAccountInspectorForRoot(
|
||||
@@ -523,9 +534,16 @@ function getBundledChannelAccountInspectorForRoot(
|
||||
cacheContext.lazyAccountInspectorsById.set(id, null);
|
||||
return undefined;
|
||||
}
|
||||
const inspector = entry.loadChannelAccountInspector();
|
||||
cacheContext.lazyAccountInspectorsById.set(id, inspector);
|
||||
return inspector;
|
||||
try {
|
||||
const inspector = entry.loadChannelAccountInspector();
|
||||
cacheContext.lazyAccountInspectorsById.set(id, inspector);
|
||||
return inspector;
|
||||
} catch (error) {
|
||||
const detail = formatErrorMessage(error);
|
||||
log.warn(`[channels] failed to load bundled channel account inspector ${id}: ${detail}`);
|
||||
cacheContext.lazyAccountInspectorsById.set(id, null);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getBundledChannelSetupPluginForRoot(
|
||||
@@ -533,9 +551,8 @@ function getBundledChannelSetupPluginForRoot(
|
||||
rootScope: BundledChannelRootScope,
|
||||
cacheContext: BundledChannelCacheContext,
|
||||
): ChannelPlugin | undefined {
|
||||
const cached = cacheContext.lazySetupPluginsById.get(id);
|
||||
if (cached) {
|
||||
return cached;
|
||||
if (cacheContext.lazySetupPluginsById.has(id)) {
|
||||
return cacheContext.lazySetupPluginsById.get(id) ?? undefined;
|
||||
}
|
||||
if (cacheContext.setupPluginLoadInProgressIds.has(id)) {
|
||||
return undefined;
|
||||
@@ -549,6 +566,11 @@ function getBundledChannelSetupPluginForRoot(
|
||||
const plugin = entry.loadSetupPlugin();
|
||||
cacheContext.lazySetupPluginsById.set(id, plugin);
|
||||
return plugin;
|
||||
} catch (error) {
|
||||
const detail = formatErrorMessage(error);
|
||||
log.warn(`[channels] failed to load bundled channel setup ${id}: ${detail}`);
|
||||
cacheContext.lazySetupPluginsById.set(id, null);
|
||||
return undefined;
|
||||
} finally {
|
||||
cacheContext.setupPluginLoadInProgressIds.delete(id);
|
||||
}
|
||||
@@ -566,11 +588,18 @@ function getBundledChannelSetupSecretsForRoot(
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
const secrets =
|
||||
entry.loadSetupSecrets?.() ??
|
||||
getBundledChannelSetupPluginForRoot(id, rootScope, cacheContext)?.secrets;
|
||||
cacheContext.lazySetupSecretsById.set(id, secrets ?? null);
|
||||
return secrets;
|
||||
try {
|
||||
const secrets =
|
||||
entry.loadSetupSecrets?.() ??
|
||||
getBundledChannelSetupPluginForRoot(id, rootScope, cacheContext)?.secrets;
|
||||
cacheContext.lazySetupSecretsById.set(id, secrets ?? null);
|
||||
return secrets;
|
||||
} catch (error) {
|
||||
const detail = formatErrorMessage(error);
|
||||
log.warn(`[channels] failed to load bundled channel setup secrets ${id}: ${detail}`);
|
||||
cacheContext.lazySetupSecretsById.set(id, null);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function listBundledChannelPlugins(): readonly ChannelPlugin[] {
|
||||
|
||||
Reference in New Issue
Block a user