When a packaged bundled plugin's `pluginRoot` is used directly as the npm
execution cwd, `npm install <specs>` resolves the plugin's own
`package.json` as the project manifest and fails with
`EUNSUPPORTEDPROTOCOL: Unsupported URL Type "workspace:": workspace:*`
whenever that manifest declares a `workspace:` runtime dep (e.g.
`"@openclaw/plugin-sdk": "workspace:*"`). This takes out every plugin
with any runtime deps at gateway startup.
`ensureBundledPluginRuntimeDeps` already filters `workspace:` specs from
the CLI arguments, but npm's own resolver reads the cwd manifest
regardless, so the filter alone is not enough. The existing isolated
execution-root + `replaceNodeModulesDir` machinery handles this exact
problem for source-checkout + cache-hit installs. This change activates
the same staging path for the packaged case: when `installRoot ===
pluginRoot` and we are not in the source-checkout cache path, stage the
install inside `<pluginRoot>/.openclaw-install-stage` (which has a
minimal generated `package.json`) and move the produced `node_modules/`
back to the plugin root as before.
- Add regression test `stages plugin-root install when the plugin's own
package.json declares workspace:* deps` covering the Docker scenario
(mixed `workspace:*` + concrete runtime dep, e.g. anthropic-style
`@openclaw/plugin-sdk` + `@anthropic-ai/sdk`).
- Update existing plugin-root-install expectations (`installs
plugin-local runtime deps when one is missing`, `skips workspace-only
runtime deps before npm install`, `installs deps that are only present
in the package root`, `does not trust runtime deps that only resolve
from the package root`, `does not treat sibling extension runtime deps
as satisfying a plugin`) to assert the new `installExecutionRoot`.
Reported in #70844; same root cause as #70701, #70756, #70773, #70818,
#70839 which see the downstream "Cannot find package 'openclaw' from
plugin-runtime-deps" symptom because their
`resolveBundledRuntimeDependencyInstallRoot` resolves to an external
stage dir (clean manifest) so the install succeeds but the resulting
node_modules tree cannot satisfy the filtered-out workspace packages at
ESM import time.
## AI assistance
This PR was AI-assisted with Claude Code.
Testing degree: fully tested for the touched `bundled-runtime-deps`
install staging surface.
- `pnpm exec vitest run --config test/vitest/vitest.plugins.config.ts src/plugins/bundled-runtime-deps.test.ts` (31/31)
- `pnpm exec vitest run --config test/vitest/vitest.plugins.config.ts src/plugins/` (43/43 across 8 files)
- `pnpm exec tsgo --noEmit -p tsconfig.core.json`, `pnpm exec tsgo --noEmit -p tsconfig.core.test.json` (clean)
- `pnpm exec oxlint src/plugins/bundled-runtime-deps.ts src/plugins/bundled-runtime-deps.test.ts` (0 warnings, 0 errors)
- `node scripts/check-src-extension-import-boundary.mjs --json` and `node scripts/check-sdk-package-extension-import-boundary.mjs --json` (both `[]`)
I understand the code path changed here: packaged bundled plugins now
stage their runtime-dep install one directory below `pluginRoot` so npm
never reads the plugin's `workspace:*`-containing manifest during
install; after install completes, the produced `node_modules/` is moved
back to `pluginRoot` via the existing `replaceNodeModulesDir` helper.
Signed-off-by: Simone Macario <simone@sharly.ai>
Derive Claude CLI bypass mode from OpenClaw exec YOLO policy, preserve raw Claude permission-mode overrides, update docs/changelog, and cover global/per-agent policy behavior.
Replace legacy qrcode-terminal usage with shared qrcode-tui media helpers, bound QR PNG rendering options, and raise bundled plugin host floors for the new SDK runtime surface.
The dependency-tree security scan rejects node_modules symlinks whose
targets resolve outside the install root. Our trusted host-to-plugin
symlink violates that rule by design, so running the scan AFTER
linkOpenClawPeerDependencies would fail every install with
SECURITY_SCAN_FAILED.
Reorder afterInstall so the scan runs first (walking only the plugin's
own staged source, catching any pre-existing malicious openclaw-named
symlink a source might smuggle in), then the trusted link is
materialised on the now-safe tree.
Also use braces on guard clauses in the new unit tests to satisfy the
oxlint no-unreachable-single-statement-if rule.
Tests three cases via installPluginFromDir:
- symlink created when peerDependencies declares openclaw
- no symlink when peer list is empty
- idempotent re-install replaces existing symlink
- warns and skips when host root cannot be resolved
Also removes the single-element Set in favour of a direct name
comparison (peerName === "openclaw"), and adds Closes#54428 to
address the same root cause in the weixin connector.
Closes#54428
* fix(logging): tolerate malformed subsystem labels
Guard console subsystem filtering and probe suppression against malformed subsystem labels, and normalize bad subsystem names to a stable fallback during console emission.
Fixes#70502
* test(plugins): ignore extension test-support helpers in seam guardrail
Exclude extension files named *.test-support.ts from the plugin sdk seam guardrail so test-only helpers do not trip public seam enforcement on unrelated PRs.
Skip `__proto__`, `prototype`, and `constructor` keys while recursively
merging provider-auth `configPatch` payloads. Plugins construct the
patch in-process today, but JSON-parsed sources can preserve these keys
and the assignment `next[key] = value` would otherwise mutate the
merge target's prototype chain.
Made-with: Cursor
`openclaw models auth login` was replacing `agents.defaults.models`
wholesale whenever a provider returned a `configPatch` with that key,
even if the patch only listed the new default model. Re-authenticating
an OAuth provider such as OpenAI Codex wiped aliases and per-model
params for every other provider.
Make replacement opt-in via `ProviderAuthResult.replaceDefaultModels`.
Ordinary logins merge their allowlist patch so unrelated entries
survive; the Anthropic -> Claude CLI migration opts in because it
renames keys the merge path would otherwise keep stale.
Fixes#69414.
Made-with: Cursor