mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 20:50:50 +00:00
fix(doctor): repair configured missing plugins
Fixes #76872.
Doctor now repairs configured-but-missing official plugins during update/doctor recovery, auto-enables the plugin after a successful repair, and preserves config when the download cannot complete. The plugin auto-enable path also honors disabled web search and only enables configured providers/channels when a manifest declares the matching capability.
Verification:
- git diff --check
- fallback-only Korean i18n check
- focused plugin auto-enable/config/doctor Vitest suite
- Crabbox published upgrade-survivor configured-plugin-installs E2E
- CI green on PR head 67ba8ac002
Co-authored-by: Jack Storment <crazycoder131@gmail.com>
This commit is contained in:
@@ -416,6 +416,16 @@ function assertExternalPluginInstall(records, pluginId, packageName) {
|
||||
}
|
||||
}
|
||||
|
||||
function assertConfiguredPluginAvailable(index, pluginId, packageName) {
|
||||
const records = index.installRecords ?? {};
|
||||
const bundled = (index.plugins ?? []).find((plugin) => plugin?.pluginId === pluginId);
|
||||
if (bundled) {
|
||||
assert(bundled.enabled !== false, `configured bundled ${pluginId} plugin is disabled`);
|
||||
return;
|
||||
}
|
||||
assertExternalPluginInstall(records, pluginId, packageName);
|
||||
}
|
||||
|
||||
function assertConfiguredPluginInstalls() {
|
||||
const coverage = getCoverage();
|
||||
const stage = process.env.OPENCLAW_UPGRADE_SURVIVOR_ASSERT_STAGE || "survival";
|
||||
@@ -432,7 +442,7 @@ function assertConfiguredPluginInstalls() {
|
||||
assert(!matrix, "internal matrix plugin should not be installed externally");
|
||||
assert(bundledMatrix, "configured bundled matrix plugin is missing from the plugin index");
|
||||
assert(bundledMatrix.enabled !== false, "configured bundled matrix plugin is disabled");
|
||||
assertExternalPluginInstall(records, "discord", "@openclaw/discord");
|
||||
assertConfiguredPluginAvailable(index, "brave", "@openclaw/brave-plugin");
|
||||
assert(!records.telegram, "internal telegram plugin should not be installed externally");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"allow": ["discord", "telegram", "matrix"],
|
||||
"allow": ["brave", "discord", "telegram", "matrix"],
|
||||
"entries": {
|
||||
"brave": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"webSearch": {
|
||||
"apiKey": {
|
||||
"source": "env",
|
||||
"provider": "default",
|
||||
"id": "BRAVE_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"discord": {
|
||||
"enabled": true
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ export DISCORD_BOT_TOKEN="upgrade-survivor-discord-token"
|
||||
export TELEGRAM_BOT_TOKEN="123456:upgrade-survivor-telegram-token"
|
||||
export FEISHU_APP_SECRET="upgrade-survivor-feishu-secret"
|
||||
export MATRIX_ACCESS_TOKEN="upgrade-survivor-matrix-token"
|
||||
export BRAVE_API_KEY="BSA_upgrade_survivor_brave_key"
|
||||
|
||||
ARTIFACT_ROOT="$(dirname "${OPENCLAW_UPGRADE_SURVIVOR_SUMMARY_JSON:-/tmp/openclaw-upgrade-survivor-artifacts/summary.json}")"
|
||||
mkdir -p "$ARTIFACT_ROOT"
|
||||
@@ -40,6 +41,7 @@ CURRENT_PHASE="setup"
|
||||
FAILURE_PHASE=""
|
||||
FAILURE_MESSAGE=""
|
||||
gateway_pid=""
|
||||
plugin_registry_pid=""
|
||||
baseline_spec=""
|
||||
baseline_version=""
|
||||
baseline_version_expected="0"
|
||||
@@ -191,6 +193,9 @@ NODE
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [ -n "${plugin_registry_pid:-}" ]; then
|
||||
kill "$plugin_registry_pid" >/dev/null 2>&1 || true
|
||||
fi
|
||||
openclaw_e2e_terminate_gateways "${gateway_pid:-}"
|
||||
}
|
||||
|
||||
@@ -281,6 +286,92 @@ configured_plugin_installs_enabled() {
|
||||
[ "$SCENARIO" = "configured-plugin-installs" ]
|
||||
}
|
||||
|
||||
configure_configured_plugin_install_fixture_registry() {
|
||||
configured_plugin_installs_enabled || return 0
|
||||
|
||||
local fixture_root="$ARTIFACT_ROOT/configured-plugin-installs-npm-fixture"
|
||||
local package_dir="$fixture_root/package"
|
||||
local tarball="$fixture_root/openclaw-brave-plugin-2026.5.2.tgz"
|
||||
local port_file="$fixture_root/npm-registry-port"
|
||||
local log_file="$fixture_root/npm-registry.log"
|
||||
mkdir -p "$package_dir"
|
||||
FIXTURE_PACKAGE_DIR="$package_dir" node <<'NODE'
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const root = process.env.FIXTURE_PACKAGE_DIR;
|
||||
fs.mkdirSync(root, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(root, "package.json"),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
name: "@openclaw/brave-plugin",
|
||||
version: "2026.5.2",
|
||||
openclaw: { extensions: ["./index.js"] },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(root, "openclaw.plugin.json"),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
id: "brave",
|
||||
activation: { onStartup: false },
|
||||
providerAuthEnvVars: { brave: ["BRAVE_API_KEY"] },
|
||||
contracts: { webSearchProviders: ["brave"] },
|
||||
configSchema: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
webSearch: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
apiKey: { type: ["string", "object"] },
|
||||
mode: { type: "string", enum: ["web", "llm-context"] },
|
||||
baseUrl: { type: ["string", "object"] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(root, "index.js"),
|
||||
`module.exports = { id: "brave", name: "Brave Fixture", register() {} };\n`,
|
||||
);
|
||||
NODE
|
||||
tar -czf "$tarball" -C "$fixture_root" package
|
||||
node scripts/e2e/lib/plugins/npm-registry-server.mjs \
|
||||
"$port_file" \
|
||||
"@openclaw/brave-plugin" \
|
||||
"2026.5.2" \
|
||||
"$tarball" \
|
||||
>"$log_file" 2>&1 &
|
||||
plugin_registry_pid="$!"
|
||||
|
||||
for _ in $(seq 1 100); do
|
||||
if [ -s "$port_file" ]; then
|
||||
export NPM_CONFIG_REGISTRY="http://127.0.0.1:$(cat "$port_file")"
|
||||
export npm_config_registry="$NPM_CONFIG_REGISTRY"
|
||||
return 0
|
||||
fi
|
||||
if ! kill -0 "$plugin_registry_pid" 2>/dev/null; then
|
||||
cat "$log_file" >&2 || true
|
||||
return 1
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
cat "$log_file" >&2 || true
|
||||
echo "Timed out waiting for configured plugin install npm fixture registry." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
legacy_plugin_dependency_probe_paths() {
|
||||
local plugin="$1"
|
||||
local plugin_dir
|
||||
@@ -699,6 +790,7 @@ phase seed-legacy-runtime-deps-symlink seed_legacy_runtime_deps_symlink
|
||||
phase resolve-candidate resolve_candidate_version
|
||||
phase update-candidate update_candidate
|
||||
phase assert-legacy-plugin-dependency-debris-before-doctor assert_legacy_plugin_dependency_debris_before_doctor
|
||||
phase configure-configured-plugin-install-fixture-registry configure_configured_plugin_install_fixture_registry
|
||||
phase doctor run_doctor
|
||||
phase assert-legacy-plugin-dependency-debris-cleaned assert_legacy_plugin_dependency_debris_cleaned
|
||||
phase assert-legacy-runtime-deps-symlink-repaired assert_legacy_runtime_deps_symlink_repaired
|
||||
|
||||
@@ -143,13 +143,104 @@ export OPENAI_API_KEY="sk-openclaw-upgrade-survivor"
|
||||
export DISCORD_BOT_TOKEN="upgrade-survivor-discord-token"
|
||||
export TELEGRAM_BOT_TOKEN="123456:upgrade-survivor-telegram-token"
|
||||
export FEISHU_APP_SECRET="upgrade-survivor-feishu-secret"
|
||||
export BRAVE_API_KEY="BSA_upgrade_survivor_brave_key"
|
||||
|
||||
gateway_pid=""
|
||||
plugin_registry_pid=""
|
||||
cleanup() {
|
||||
if [ -n "${plugin_registry_pid:-}" ]; then
|
||||
kill "$plugin_registry_pid" >/dev/null 2>&1 || true
|
||||
fi
|
||||
openclaw_e2e_terminate_gateways "${gateway_pid:-}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
configure_configured_plugin_install_fixture_registry() {
|
||||
[ "${OPENCLAW_UPGRADE_SURVIVOR_SCENARIO:-base}" = "configured-plugin-installs" ] || return 0
|
||||
|
||||
local fixture_root="$OPENCLAW_UPGRADE_SURVIVOR_ARTIFACT_ROOT/configured-plugin-installs-npm-fixture"
|
||||
local package_dir="$fixture_root/package"
|
||||
local tarball="$fixture_root/openclaw-brave-plugin-2026.5.2.tgz"
|
||||
local port_file="$fixture_root/npm-registry-port"
|
||||
local log_file="$fixture_root/npm-registry.log"
|
||||
mkdir -p "$package_dir"
|
||||
FIXTURE_PACKAGE_DIR="$package_dir" node <<'"'"'NODE'"'"'
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const root = process.env.FIXTURE_PACKAGE_DIR;
|
||||
fs.mkdirSync(root, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(root, "package.json"),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
name: "@openclaw/brave-plugin",
|
||||
version: "2026.5.2",
|
||||
openclaw: { extensions: ["./index.js"] },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(root, "openclaw.plugin.json"),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
id: "brave",
|
||||
activation: { onStartup: false },
|
||||
providerAuthEnvVars: { brave: ["BRAVE_API_KEY"] },
|
||||
contracts: { webSearchProviders: ["brave"] },
|
||||
configSchema: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
webSearch: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
apiKey: { type: ["string", "object"] },
|
||||
mode: { type: "string", enum: ["web", "llm-context"] },
|
||||
baseUrl: { type: ["string", "object"] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(root, "index.js"),
|
||||
`module.exports = { id: "brave", name: "Brave Fixture", register() {} };\n`,
|
||||
);
|
||||
NODE
|
||||
tar -czf "$tarball" -C "$fixture_root" package
|
||||
node scripts/e2e/lib/plugins/npm-registry-server.mjs \
|
||||
"$port_file" \
|
||||
"@openclaw/brave-plugin" \
|
||||
"2026.5.2" \
|
||||
"$tarball" \
|
||||
>"$log_file" 2>&1 &
|
||||
plugin_registry_pid="$!"
|
||||
|
||||
for _ in $(seq 1 100); do
|
||||
if [ -s "$port_file" ]; then
|
||||
export NPM_CONFIG_REGISTRY="http://127.0.0.1:$(cat "$port_file")"
|
||||
export npm_config_registry="$NPM_CONFIG_REGISTRY"
|
||||
return 0
|
||||
fi
|
||||
if ! kill -0 "$plugin_registry_pid" 2>/dev/null; then
|
||||
cat "$log_file" >&2 || true
|
||||
return 1
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
cat "$log_file" >&2 || true
|
||||
echo "Timed out waiting for configured plugin install npm fixture registry." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
node scripts/e2e/lib/upgrade-survivor/assertions.mjs seed
|
||||
|
||||
@@ -178,6 +269,7 @@ if [ "$update_status" -ne 0 ]; then
|
||||
fi
|
||||
|
||||
echo "Running non-interactive doctor repair..."
|
||||
configure_configured_plugin_install_fixture_registry
|
||||
if ! openclaw doctor --fix --non-interactive >/tmp/openclaw-upgrade-survivor-doctor.log 2>&1; then
|
||||
echo "openclaw doctor failed" >&2
|
||||
cat /tmp/openclaw-upgrade-survivor-doctor.log >&2 || true
|
||||
@@ -220,7 +312,7 @@ node scripts/e2e/lib/upgrade-survivor/probe-gateway.mjs \
|
||||
--base-url "http://127.0.0.1:$PORT" \
|
||||
--path /readyz \
|
||||
--expect ready \
|
||||
--allow-failing discord,telegram,whatsapp,feishu \
|
||||
--allow-failing discord,telegram,whatsapp,feishu,matrix \
|
||||
--out /tmp/openclaw-upgrade-survivor-readyz.json
|
||||
|
||||
echo "Checking gateway RPC status..."
|
||||
|
||||
Reference in New Issue
Block a user