fix(plugins): mirror SDK alias for staged sidecars

This commit is contained in:
Peter Steinberger
2026-04-24 04:24:46 +01:00
parent e0d3256311
commit b5779b992f
2 changed files with 116 additions and 32 deletions

View File

@@ -349,6 +349,16 @@ assert_no_dep_sentinel() {
fi
}
assert_no_install_stage() {
local channel="$1"
local stage="$package_root/dist/extensions/$channel/.openclaw-install-stage"
if [ -e "$stage" ]; then
echo "install stage should be cleaned after activation for $channel" >&2
find "$stage" -maxdepth 4 -type f | sort | head -80 >&2 || true
exit 1
fi
}
echo "Starting baseline gateway with OpenAI configured..."
write_config baseline
start_gateway "/tmp/openclaw-$CHANNEL-baseline.log"
@@ -362,6 +372,7 @@ start_gateway "/tmp/openclaw-$CHANNEL-first.log"
wait_for_gateway_health
assert_installed_once "/tmp/openclaw-$CHANNEL-first.log" "$CHANNEL" "$DEP_SENTINEL"
assert_dep_sentinel "$CHANNEL" "$DEP_SENTINEL"
assert_no_install_stage "$CHANNEL"
assert_channel_status "$CHANNEL"
stop_gateway
@@ -369,6 +380,7 @@ echo "Restarting gateway again; $CHANNEL deps must stay installed..."
start_gateway "/tmp/openclaw-$CHANNEL-second.log"
wait_for_gateway_health
assert_not_installed "/tmp/openclaw-$CHANNEL-second.log" "$CHANNEL"
assert_no_install_stage "$CHANNEL"
assert_channel_status "$CHANNEL"
stop_gateway
@@ -505,44 +517,20 @@ start_gateway() {
exit 1
}
wait_for_gateway_health() {
for _ in $(seq 1 120); do
if runuser -u appuser -- env HOME=/home/appuser openclaw gateway health --url "ws://127.0.0.1:$PORT" --token "$TOKEN" --json >/dev/null 2>&1; then
wait_for_slack_provider_start() {
for _ in $(seq 1 180); do
if grep -Eq "\\[slack\\] \\[default\\] starting provider|An API error occurred: invalid_auth" /tmp/openclaw-root-owned-gateway.log; then
return 0
fi
sleep 0.25
sleep 1
done
echo "timed out waiting for gateway health" >&2
return 1
}
assert_channel_status() {
local out="/tmp/openclaw-root-owned-channel-status.json"
runuser -u appuser -- env HOME=/home/appuser openclaw gateway call channels.status \
--url "ws://127.0.0.1:$PORT" \
--token "$TOKEN" \
--timeout 30000 \
--json \
--params '{"probe":false}' >"$out"
if ! node - <<'NODE' "$out" "$CHANNEL"
const fs = require("node:fs");
const raw = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const payload = raw.result ?? raw.data ?? raw;
const channel = process.argv[3];
if (!payload.channels || !payload.channels[channel]) {
throw new Error(`missing channels.${channel}\n${JSON.stringify(raw, null, 2).slice(0, 4000)}`);
}
console.log(`${channel} channel plugin visible`);
NODE
then
cat /tmp/openclaw-root-owned-gateway.log >&2
exit 1
fi
echo "timed out waiting for slack provider startup" >&2
cat /tmp/openclaw-root-owned-gateway.log >&2
exit 1
}
start_gateway /tmp/openclaw-root-owned-gateway.log
wait_for_gateway_health
assert_channel_status
wait_for_slack_provider_start
if [ -e "$root/dist/extensions/$CHANNEL/node_modules/$DEP_SENTINEL/package.json" ]; then
echo "root-owned package tree was mutated" >&2
@@ -555,6 +543,22 @@ if ! find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -path "*/node_modules/$DEP_S
cat /tmp/openclaw-root-owned-gateway.log >&2
exit 1
fi
if [ -e "$root/dist/extensions/node_modules/openclaw/package.json" ]; then
echo "root-owned package tree was mutated with SDK alias" >&2
find "$root/dist/extensions/node_modules/openclaw" -maxdepth 4 -type f | sort | head -80 >&2 || true
exit 1
fi
if ! find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -path "*/dist/extensions/node_modules/openclaw/package.json" -type f | grep -q .; then
echo "missing external staged openclaw/plugin-sdk alias" >&2
find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -type f | sort | head -120 >&2 || true
cat /tmp/openclaw-root-owned-gateway.log >&2
exit 1
fi
if grep -Eq "failed to install bundled runtime deps|Cannot find package 'openclaw'|Cannot find module 'openclaw/plugin-sdk'" /tmp/openclaw-root-owned-gateway.log; then
echo "root-owned gateway hit bundled runtime dependency errors" >&2
cat /tmp/openclaw-root-owned-gateway.log >&2
exit 1
fi
echo "root-owned global install Docker E2E passed"
EOF
@@ -962,6 +966,7 @@ command -v openclaw >/dev/null
baseline_root="$(package_root)"
test -d "$baseline_root/dist/extensions/telegram"
test -d "$baseline_root/dist/extensions/feishu"
test -d "$baseline_root/dist/extensions/acpx"
echo "Replicating configured Telegram missing-runtime state..."
write_config telegram
@@ -1026,6 +1031,14 @@ cat /tmp/openclaw-update-memory-lancedb.json
assert_update_ok /tmp/openclaw-update-memory-lancedb.json "$candidate_version"
assert_dep_available memory-lancedb @lancedb/lancedb
echo "Removing ACPX runtime package and rerunning same-version update path..."
remove_runtime_dep acpx acpx
assert_no_dep_available acpx acpx
run_update_and_capture acpx /tmp/openclaw-update-acpx.json
cat /tmp/openclaw-update-acpx.json
assert_update_ok /tmp/openclaw-update-acpx.json "$candidate_version"
assert_dep_available acpx acpx
echo "bundled channel runtime deps Docker update E2E passed"
EOF
then

View File

@@ -134,6 +134,7 @@ function prepareBundledPluginRuntimeDistMirror(params: {
}
}
}
ensureOpenClawPluginSdkAlias(mirrorDistRoot);
return mirrorExtensionsRoot;
}
@@ -165,3 +166,73 @@ function copyBundledPluginRuntimeRoot(sourceRoot: string, targetRoot: string): v
}
}
}
function writeRuntimeJsonFile(targetPath: string, value: unknown): void {
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(targetPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
}
function hasRuntimeDefaultExport(sourcePath: string): boolean {
const text = fs.readFileSync(sourcePath, "utf8");
return /\bexport\s+default\b/u.test(text) || /\bas\s+default\b/u.test(text);
}
function writeRuntimeModuleWrapper(sourcePath: string, targetPath: string): void {
const specifier = path.relative(path.dirname(targetPath), sourcePath).replaceAll(path.sep, "/");
const normalizedSpecifier = specifier.startsWith(".") ? specifier : `./${specifier}`;
const defaultForwarder = hasRuntimeDefaultExport(sourcePath)
? [
`import defaultModule from ${JSON.stringify(normalizedSpecifier)};`,
`let defaultExport = defaultModule;`,
`for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {`,
` defaultExport = defaultExport.default;`,
`}`,
]
: [
`import * as module from ${JSON.stringify(normalizedSpecifier)};`,
`let defaultExport = "default" in module ? module.default : module;`,
`for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {`,
` defaultExport = defaultExport.default;`,
`}`,
];
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(
targetPath,
[
`export * from ${JSON.stringify(normalizedSpecifier)};`,
...defaultForwarder,
"export { defaultExport as default };",
"",
].join("\n"),
"utf8",
);
}
function ensureOpenClawPluginSdkAlias(distRoot: string): void {
const pluginSdkDir = path.join(distRoot, "plugin-sdk");
if (!fs.existsSync(pluginSdkDir)) {
return;
}
const aliasDir = path.join(distRoot, "extensions", "node_modules", "openclaw");
const pluginSdkAliasDir = path.join(aliasDir, "plugin-sdk");
writeRuntimeJsonFile(path.join(aliasDir, "package.json"), {
name: "openclaw",
type: "module",
exports: {
"./plugin-sdk": "./plugin-sdk/index.js",
"./plugin-sdk/*": "./plugin-sdk/*.js",
},
});
fs.rmSync(pluginSdkAliasDir, { recursive: true, force: true });
fs.mkdirSync(pluginSdkAliasDir, { recursive: true });
for (const entry of fs.readdirSync(pluginSdkDir, { withFileTypes: true })) {
if (!entry.isFile() || path.extname(entry.name) !== ".js") {
continue;
}
writeRuntimeModuleWrapper(
path.join(pluginSdkDir, entry.name),
path.join(pluginSdkAliasDir, entry.name),
);
}
}