diff --git a/.agents/skills/parallels-discord-roundtrip/SKILL.md b/.agents/skills/parallels-discord-roundtrip/SKILL.md new file mode 100644 index 00000000000..8fda0da1a23 --- /dev/null +++ b/.agents/skills/parallels-discord-roundtrip/SKILL.md @@ -0,0 +1,59 @@ +--- +name: parallels-discord-roundtrip +description: Run the macOS Parallels smoke harness with Discord end-to-end roundtrip verification, including guest send, host verification, host reply, and guest readback. +--- + +# Parallels Discord Roundtrip + +Use when macOS Parallels smoke must prove Discord two-way delivery end to end. + +## Goal + +Cover: + +- install on fresh macOS snapshot +- onboard + gateway health +- guest `message send` to Discord +- host sees that message on Discord +- host posts a new Discord message +- guest `message read` sees that new message + +## Inputs + +- host env var with Discord bot token +- Discord guild ID +- Discord channel ID +- `OPENAI_API_KEY` + +## Preferred run + +```bash +export OPENCLAW_PARALLELS_DISCORD_TOKEN="$( + ssh peters-mac-studio-1 'jq -r ".channels.discord.token" ~/.openclaw/openclaw.json' | tr -d '\n' +)" + +pnpm test:parallels:macos \ + --discord-token-env OPENCLAW_PARALLELS_DISCORD_TOKEN \ + --discord-guild-id 1456350064065904867 \ + --discord-channel-id 1456744319972282449 \ + --json +``` + +## Notes + +- Snapshot target: closest to `macOS 26.3.1 fresh`. +- Harness configures Discord inside the guest; no checked-in token/config. +- Use the `openclaw` wrapper for guest `message send/read`; `node openclaw.mjs message ...` does not expose the lazy message subcommands the same way. +- Write `channels.discord.guilds` in one JSON object (`--strict-json`), not dotted `config set channels.discord.guilds....` paths; numeric snowflakes get treated like array indexes. +- Avoid `prlctl enter` / expect for long Discord setup scripts; it line-wraps/corrupts long commands. Use `prlctl exec --current-user /bin/sh -lc ...` for the Discord config phase. +- Harness cleanup deletes the temporary Discord smoke messages at exit. +- Per-phase logs: `/tmp/openclaw-parallels-smoke.*` +- Machine summary: pass `--json` +- If roundtrip flakes, inspect `fresh.discord-roundtrip.log` and `discord-last-readback.json` in the run dir first. + +## Pass criteria + +- fresh lane or upgrade lane requested passes +- summary reports `discord=pass` for that lane +- guest outbound nonce appears in channel history +- host inbound nonce appears in `openclaw message read` output diff --git a/.github/labeler.yml b/.github/labeler.yml index d980a8d096e..b6422060fea 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -198,14 +198,6 @@ - changed-files: - any-glob-to-any-file: - "extensions/diagnostics-otel/**" -"extensions: google-antigravity-auth": - - changed-files: - - any-glob-to-any-file: - - "extensions/google-antigravity-auth/**" -"extensions: google-gemini-cli-auth": - - changed-files: - - any-glob-to-any-file: - - "extensions/google-gemini-cli-auth/**" "extensions: llm-task": - changed-files: - any-glob-to-any-file: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9922ceb12f5..f82dea2f230 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,6 +78,50 @@ jobs: node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD + changed-extensions: + needs: [docs-scope, changed-scope] + if: needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_node == 'true' + runs-on: blacksmith-16vcpu-ubuntu-2404 + outputs: + has_changed_extensions: ${{ steps.changed.outputs.has_changed_extensions }} + changed_extensions_matrix: ${{ steps.changed.outputs.changed_extensions_matrix }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1 + fetch-tags: false + submodules: false + + - name: Ensure changed-extensions base commit + uses: ./.github/actions/ensure-base-commit + with: + base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }} + fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }} + + - name: Setup Node environment + uses: ./.github/actions/setup-node-env + with: + install-bun: "false" + install-deps: "false" + use-sticky-disk: "false" + + - name: Detect changed extensions + id: changed + env: + BASE_SHA: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }} + run: | + node --input-type=module <<'EOF' + import { appendFileSync } from "node:fs"; + import { listChangedExtensionIds } from "./scripts/test-extension.mjs"; + + const extensionIds = listChangedExtensionIds({ base: process.env.BASE_SHA, head: "HEAD" }); + const matrix = JSON.stringify({ include: extensionIds.map((extension) => ({ extension })) }); + + appendFileSync(process.env.GITHUB_OUTPUT, `has_changed_extensions=${extensionIds.length > 0}\n`, "utf8"); + appendFileSync(process.env.GITHUB_OUTPUT, `changed_extensions_matrix=${matrix}\n`, "utf8"); + EOF + # Build dist once for Node-relevant changes and share it with downstream jobs. build-artifacts: needs: [docs-scope, changed-scope] @@ -205,6 +249,29 @@ jobs: if: matrix.runtime != 'bun' || github.event_name != 'pull_request' run: ${{ matrix.command }} + extension-fast: + name: "extension-fast (${{ matrix.extension }})" + needs: [docs-scope, changed-scope, changed-extensions] + if: needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_node == 'true' && needs.changed-extensions.outputs.has_changed_extensions == 'true' + runs-on: blacksmith-16vcpu-ubuntu-2404 + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.changed-extensions.outputs.changed_extensions_matrix) }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: false + + - name: Setup Node environment + uses: ./.github/actions/setup-node-env + with: + install-bun: "false" + use-sticky-disk: "false" + + - name: Run changed extension tests + run: pnpm test:extension ${{ matrix.extension }} + # Types, lint, and format check. check: name: "check" diff --git a/.npmignore b/.npmignore index 7cd53fdbc08..fcc490ae35d 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,2 @@ **/node_modules/ +docs/.generated/ diff --git a/AGENTS.md b/AGENTS.md index 1197f6fb48f..df72efbe720 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -212,6 +212,11 @@ - `prlctl exec` is fine for deterministic repo commands, but it can misrepresent interactive shell behavior (`PATH`, `HOME`, `curl | bash`, shebang resolution). For installer parity or shell-sensitive repros, prefer the guest Terminal or `prlctl enter`. - Fresh Tahoe snapshot current reality: `brew` exists, `node` may not be on `PATH` in noninteractive guest exec. Use absolute `/opt/homebrew/bin/node` for repo/CLI runs when needed. - Preferred automation entrypoint: `pnpm test:parallels:macos`. It restores the snapshot most closely matching `macOS 26.3.1 fresh`, serves the current `main` tarball from the host, then runs fresh-install and latest-release-to-main smoke lanes. + - Discord roundtrip smoke is opt-in. Pass `--discord-token-env --discord-guild-id --discord-channel-id `; the harness will configure Discord in-guest, post a guest message, verify host-side visibility via the Discord REST API, post a fresh host-side message back into the channel, then verify `openclaw message read` sees it in-guest. + - Keep the Discord token in a host env var only. For Peter’s Mac Studio bot, fetch it into a temp env var from `~/.openclaw/openclaw.json` over SSH instead of hardcoding it in repo files/shell history. + - For Discord smoke on this snapshot: use `openclaw message send/read` via the installed wrapper, not `node openclaw.mjs message ...`; lazy `message` subcommands do not resolve the same way through the direct module entrypoint. + - For Discord guild allowlists: set `channels.discord.guilds` as one JSON object. Do not use dotted `config set channels.discord.guilds....` paths; numeric snowflakes get treated as array indexes. + - Avoid `prlctl enter` / expect for the Discord config phase; long lines get mangled. Use `prlctl exec --current-user /bin/sh -lc ...` with short commands or temp files. - Gateway verification in smoke runs should use `openclaw gateway status --deep --require-rpc`, not plain `--deep`, so probe failures go non-zero. - Latest-release pre-upgrade diagnostics still need compatibility fallback: stable `2026.3.12` does not know `--require-rpc`, so precheck status dumps should fall back to plain `gateway status --deep` until the guest is upgraded. - Harness output: pass `--json` for machine-readable summary; per-phase logs land under `/tmp/openclaw-parallels-smoke.*`. diff --git a/CHANGELOG.md b/CHANGELOG.md index ca1d5cf8998..df03ad8fc5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,73 +6,92 @@ Docs: https://docs.openclaw.ai ### Changes -- Android/mobile: add a system-aware dark theme across onboarding and post-onboarding screens so the app follows the device theme through setup, chat, and voice flows. (#46249) Thanks @sibbl. - Commands/btw: add `/btw` side questions for quick tool-less answers about the current session without changing future session context, with dismissible in-session TUI answers and explicit BTW replies on external channels. (#45444) Thanks @ngutman. -- Gateway/health monitor: add configurable stale-event thresholds and restart limits, plus per-channel and per-account `healthMonitor.enabled` overrides, while keeping the existing global disable path on `gateway.channelHealthCheckMinutes=0`. (#42107) Thanks @rstar327. -- Feishu/cards: add identity-aware structured card headers and note footers for Feishu replies and direct sends, while keeping that presentation wired through the shared outbound identity path. (#29938) Thanks @nszhsl. -- Feishu/streaming: add `onReasoningStream` and `onReasoningEnd` support to streaming cards, so `/reasoning stream` renders thinking tokens as markdown blockquotes in the same card — matching the Telegram channel's reasoning lane behavior. (#46029) -- Refactor/channels: remove the legacy channel shim directories and point channel-specific imports directly at the extension-owned implementations. (#45967) thanks @scoootscooob. -- Android/nodes: add `callLog.search` plus shared Call Log permission wiring so Android nodes can search recent call history through the gateway. (#44073) Thanks @lxk7280. -- Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898. -- Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs. -- Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy. +- Sandbox/runtime: add pluggable sandbox backends, ship an OpenShell backend with `mirror` and `remote` workspace modes, and make sandbox list/recreate/prune backend-aware instead of Docker-only. +- Sandbox/SSH: add a core SSH sandbox backend with secret-backed key, certificate, and known_hosts inputs, move shared remote exec/filesystem tooling into core, and keep OpenShell focused on sandbox lifecycle plus optional `mirror` mode. +- Web tools/Firecrawl: add Firecrawl as an `onboard`/configure search provider via a bundled plugin, expose explicit `firecrawl_search` and `firecrawl_scrape` tools, and align core `web_fetch` fallback behavior with Firecrawl base-URL/env fallback plus guarded endpoint fetches. - Plugins/bundles: add compatible Codex, Claude, and Cursor bundle discovery/install support, map bundle skills into OpenClaw skills, and apply Claude bundle `settings.json` defaults to embedded Pi with shell overrides sanitized. +- Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy. - Plugins/agent integrations: broaden the plugin surface for app-server integrations with channel-aware commands, interactive callbacks, inbound claims, and Discord/Telegram conversation binding support. (#45318) Thanks @huntharo and @vincentkoc. +- Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs. +- Gateway/health monitor: add configurable stale-event thresholds and restart limits, plus per-channel and per-account `healthMonitor.enabled` overrides, while keeping the existing global disable path on `gateway.channelHealthCheckMinutes=0`. (#42107) Thanks @rstar327. +- Android/mobile: add a system-aware dark theme across onboarding and post-onboarding screens so the app follows the device theme through setup, chat, and voice flows. (#46249) Thanks @sibbl. +- Feishu/ACP: add current-conversation ACP and subagent session binding for supported DMs and topic conversations, including completion delivery back to the originating Feishu conversation. (#46819) +- Plugins/marketplaces: add Claude marketplace registry resolution, `plugin@marketplace` installs, marketplace listing, and update support, plus Docker E2E coverage for local and official marketplace flows. Thanks @vincentkoc. +- Feishu/cards: add structured interactive approval and quick-action launcher cards, preserve callback user and conversation context through routing, and keep legacy card-action fallback behavior so common actions can run without typing raw commands. (#47873) +- Feishu/streaming: add `onReasoningStream` and `onReasoningEnd` support to streaming cards, so `/reasoning stream` renders thinking tokens as markdown blockquotes in the same card — matching the Telegram channel's reasoning lane behavior. (#46029) +- Feishu/cards: add identity-aware structured card headers and note footers for Feishu replies and direct sends, while keeping that presentation wired through the shared outbound identity path. (#29938) Thanks @nszhsl. +- Android/nodes: add `callLog.search` plus shared Call Log permission wiring so Android nodes can search recent call history through the gateway. (#44073) Thanks @lxk7280. +- Plugins/MiniMax: merge the bundled MiniMax API and MiniMax OAuth plugin surfaces into a single default-on `minimax` plugin, while keeping legacy `minimax-portal-auth` config ids aliased for compatibility. +- Telegram/actions: add `topic-edit` for forum-topic renames and icon updates while sharing the same Telegram topic-edit transport used by the plugin runtime. (#47798) Thanks @obviyus. +- Refactor/channels: remove the legacy channel shim directories and point channel-specific imports directly at the extension-owned implementations. (#45967) thanks @scoootscooob. +- Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898. +- secrets: harden read-only SecretRef command paths and diagnostics. (#47794) Thanks @joshavant. + +### Breaking + +- Browser/Chrome MCP: remove the legacy Chrome extension relay path, bundled extension assets, `driver: "extension"`, and `browser.relayBindHost`. Run `openclaw doctor --fix` to migrate host-local browser config to `existing-session` / `user`; Docker, headless, sandbox, and remote browser flows still use raw CDP. Thanks @vincentkoc. ### Fixes -- Group mention gating: reject invalid and unsafe nested-repetition `mentionPatterns`, reuse the shared safe config-regex compiler across mention stripping and detection, and cache strip-time regex compilation so noisy groups avoid repeated recompiles. -- Control UI/chat sessions: show human-readable labels in the grouped session dropdown again, keep unique scoped fallbacks when metadata is missing, and disambiguate duplicate labels only when needed. (#45130) thanks @luzhidong. -- Control UI: scope persisted session selection per gateway, prevent stale session bleed across tokenized gateway opens, and cap stored gateway session history. (#47453) Thanks @sallyom. -- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc. -- Feishu/topic threads: fetch full thread context, including prior bot replies, when starting a topic-thread session so follow-up turns in Feishu topics keep the right conversation state. (#45254) Thanks @Coobiw. -- Configure/startup: move outbound send-deps resolution into a lightweight helper so `openclaw configure` no longer stalls after the banner while eagerly loading channel plugins. (#46301) thanks @scoootscooob. -- Control UI/dashboard: preserve structured gateway shutdown reasons across restart disconnects so config-triggered restarts no longer fall back to `disconnected (1006): no reason`. (#46532) Thanks @vincentkoc. -- Android/chat: theme the thinking dropdown and TLS trust dialogs explicitly so popup surfaces match the active app theme instead of falling back to mismatched Material defaults. -- Z.AI/onboarding: detect a working default model even for explicit `zai-coding-*` endpoint choices, so Coding Plan setup can keep the selected endpoint while defaulting to `glm-5` when available or `glm-4.7` as fallback. (#45969) -- Models/OpenRouter runtime capabilities: fetch uncatalogued OpenRouter model metadata on first use so newly added vision models keep image input instead of silently degrading to text-only, with top-level capability field fallbacks for `/api/v1/models`. (#45824) Thanks @DJjjjhao. -- Z.AI/onboarding: add `glm-5-turbo` to the default Z.AI provider catalog so onboarding-generated configs expose the new model alongside the existing GLM defaults. (#46670) Thanks @tomsun28. -- Zalo Personal/group gating: stop reapplying `dmPolicy.allowFrom` as a sender gate for already-allowlisted groups when `groupAllowFrom` is unset, so any member of an allowed group can trigger replies while DMs stay restricted. (#40146) +- Google auth/Node 25: patch `gaxios` to use native fetch without injecting `globalThis.window`, while translating proxy and mTLS transport settings so Google Vertex and Google Chat auth keep working on Node 25. (#47914) Thanks @pdd-cli. +- Gateway/startup: load bundled channel plugins from compiled `dist/extensions` entries in built installs, so gateway boot no longer recompiles bundled extension TypeScript on every startup and WhatsApp-class cold starts drop back to seconds instead of tens of seconds or worse. +- Plugins/context engines: enforce owner-aware context-engine registration on both loader and public SDK paths so plugins cannot spoof privileged ownership, claim the core `legacy` engine id, or overwrite an existing engine id through direct SDK imports. (#47595) Thanks @vincentkoc. - Browser/remote CDP: honor strict browser SSRF policy during remote CDP reachability and `/json/version` discovery checks, redact sensitive `cdpUrl` tokens from status output, and warn when remote CDP targets private/internal hosts. -- Plugins/install precedence: keep bundled plugins ahead of auto-discovered globals by default, but let an explicitly installed plugin record win its own duplicate-id tie so installed channel plugins load from `~/.openclaw/extensions` after `openclaw plugins install`. -- ACP/acpx: resolve the bundled plugin root from the actual plugin directory so plugin-local installs stay under `dist/extensions/acpx` instead of escaping to `dist/extensions` and failing runtime setup. +- Gateway/plugins: pin runtime webhook routes to the gateway startup registry so channel webhooks keep working across plugin-registry churn, and make plugin auth + dispatch resolve routes from the same live HTTP-route registry. Fixes #46924 and #47041. - Gateway/auth: ignore spoofed loopback hops in trusted forwarding chains and block device approvals that request scopes above the caller session. Thanks @vincentkoc. -- Gateway/config views: strip embedded credentials from URL-based endpoint fields before returning read-only account and config snapshots. Thanks @vincentkoc. -- Tools/apply-patch: revalidate workspace-only delete and directory targets immediately before mutating host paths. Thanks @vincentkoc. -- Webhooks/runtime: move auth earlier and tighten pre-auth body limits and timeouts across bundled webhook handlers, including slow-body handling for Mattermost slash commands. Thanks @vincentkoc. -- Subagents/follow-ups: require the same controller ownership checks for `/subagents send` as other control actions, so leaf sessions cannot message nested child runs they do not control. Thanks @vincentkoc. -- Inbound policy hardening: tighten callback and webhook sender checks across Mattermost and Google Chat, match Nextcloud Talk rooms by stable room token, and treat explicit empty Twitch allowlists as deny-all. (#46787) Thanks @zpbrent, @ijxpwastaken and @vincentkoc. -- macOS/canvas actions: keep unattended local agent actions on trusted in-app canvas surfaces only, and stop exposing the deep-link fallback key to arbitrary page scripts. (#46790) Thanks @vincentkoc. -- Agents/compaction: extend the enclosing run deadline once while compaction is actively in flight, and abort the underlying SDK compaction on timeout/cancel so large-session compactions stop freezing mid-run. (#46889) Thanks @asyncjason. -- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava. -- WhatsApp/reconnect: restore the append recency filter in the extension inbox monitor and handle protobuf `Long` timestamps correctly, so fresh post-reconnect append messages are processed while stale history sync stays suppressed. (#42588) thanks @MonkeyLeeT. -- WhatsApp/login: wait for pending creds writes before reopening after Baileys `515` pairing restarts in both QR login and `channels login` flows, and keep the restart coverage pinned to the real wrapped error shape plus per-account creds queues. (#27910) Thanks @asyncjason. -- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026. -- Security/device pairing: harden `device.token.rotate` deny handling by keeping public failures generic while logging internal deny reasons and preserving approved-baseline enforcement. (`GHSA-7jrw-x62h-64p8`) -- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc. -- Zalo/plugin runtime: export `resolveClientIp` from `openclaw/plugin-sdk/zalo` so installed builds no longer crash on startup when the webhook monitor loads from the packaged extension instead of the monorepo source tree. (#46549) Thanks @No898. -- CI/channel test routing: move the built-in channel suites into `test:channels` and keep them out of `test:extensions`, so extension CI no longer fails after the channel migration while targeted test routing still sends Slack, Signal, and iMessage suites to the right lane. (#46066) Thanks @scoootscooob. -- Browser/profiles: drop the auto-created `chrome-relay` browser profile; users who need the Chrome extension relay must now create their own profile via `openclaw browser create-profile`. (#45777) Thanks @odysseus0. -- Docs/Mintlify: fix MDX marker syntax on Perplexity, Model Providers, Moonshot, and exec approvals pages so local docs preview no longer breaks rendering or leaves stale pages unpublished. (#46695) Thanks @velvet-shark. -- Email/webhook wrapping: sanitize sender and subject metadata before external-content wrapping so metadata fields cannot break the wrapper structure. Thanks @vincentkoc. -- Node/startup: remove leftover debug `console.log("node host PATH: ...")` that printed the resolved PATH on every `openclaw node run` invocation. (#46411) -- Nodes/pending actions: re-check queued foreground actions against the current node command policy before returning them to the node. (#46815) Thanks @zpbrent and @vincentkoc. -- ACP/approvals: use canonical tool identity for prompting decisions and fail closed when conflicting tool identity hints are present. (#46817) Thanks @zpbrent and @vincentkoc. -- Telegram/message send: forward `--force-document` through the `sendPayload` path as well as `sendMedia`, so Telegram payload sends with `channelData` keep uploading images as documents instead of silently falling back to compressed photo sends. (#47119) Thanks @thepagent. -- Telegram/message chunking: preserve spaces, paragraph separators, and word boundaries when HTML overflow rechunking splits formatted replies. (#47274) -- Plugins/scoped ids: preserve scoped plugin ids during install and config keying, and keep bundled plugins ahead of discovered duplicate ids by default so `@scope/name` plugins no longer collide with unscoped installs. Thanks @vincentkoc. -- CLI: avoid loading provider discovery during startup model normalization. (#46522) Thanks @ItsAditya-xyz and @vincentkoc. -- Tlon: honor explicit empty allowlists and defer cite expansion. (#46788) Thanks @zpbrent and @vincentkoc. -- ACP: require admin scope for mutating internal actions. (#46789) Thanks @tdjackey and @vincentkoc. -- Gateway/config validation: stop treating the implicit default memory slot as a required explicit plugin config, so startup no longer fails with `plugins.slots.memory: plugin not found: memory-core` when `memory-core` was only inferred. (#47494) Thanks @ngutman. +- Gateway/restart: defer externally signaled unmanaged restarts through the in-process idle drain, and preserve the restored subagent run as remap fallback during orphan recovery so resumed sessions do not duplicate work. (#47719) Thanks @joeykrug. +- Control UI/session routing: preserve established external delivery routes when webchat views or sends in externally originated sessions, so subagent completions still return to the original channel instead of the dashboard. (#47797) Thanks @brokemac79. +- Configure/startup: move outbound send-deps resolution into a lightweight helper so `openclaw configure` no longer stalls after the banner while eagerly loading channel plugins. (#46301) thanks @scoootscooob. - CLI/startup: lazy-load channel add and root help startup paths to trim avoidable RSS and help latency on constrained hosts. (#46784) Thanks @vincentkoc. - CLI/onboarding: import static provider definitions directly for onboarding model/config helpers so those paths no longer pull provider discovery just for built-in defaults. (#47467) Thanks @vincentkoc. - CLI/auth choice: lazy-load plugin/provider fallback resolution so mapped auth choices stay on the static path and only unknown choices pay the heavy provider load. (#47495) Thanks @vincentkoc. -- CLI/completion: reduce recursive completion-script string churn and fix nested PowerShell command-path matching so generated nested completions resolve on PowerShell too. (#45537) Thanks @yiShanXin and @vincentkoc. -- Gateway/startup: load bundled channel plugins from compiled `dist/extensions` entries in built installs, so gateway boot no longer recompiles bundled extension TypeScript on every startup and WhatsApp-class cold starts drop back to seconds instead of tens of seconds or worse. +- CLI: avoid loading provider discovery during startup model normalization. (#46522) Thanks @ItsAditya-xyz and @vincentkoc. +- Security/device pairing: harden `device.token.rotate` deny handling by keeping public failures generic while logging internal deny reasons and preserving approved-baseline enforcement. (`GHSA-7jrw-x62h-64p8`) +- Inbound policy hardening: tighten callback and webhook sender checks across Mattermost and Google Chat, match Nextcloud Talk rooms by stable room token, and treat explicit empty Twitch allowlists as deny-all. (#46787) Thanks @zpbrent, @ijxpwastaken and @vincentkoc. +- Webhooks/runtime: move auth earlier and tighten pre-auth body limits and timeouts across bundled webhook handlers, including slow-body handling for Mattermost slash commands. Thanks @vincentkoc. +- Email/webhook wrapping: sanitize sender and subject metadata before external-content wrapping so metadata fields cannot break the wrapper structure. Thanks @vincentkoc. +- Tools/apply-patch: revalidate workspace-only delete and directory targets immediately before mutating host paths. Thanks @vincentkoc. +- Gateway/config views: strip embedded credentials from URL-based endpoint fields before returning read-only account and config snapshots. Thanks @vincentkoc. +- ACP/approvals: use canonical tool identity for prompting decisions and fail closed when conflicting tool identity hints are present. (#46817) Thanks @zpbrent and @vincentkoc. +- ACP: require admin scope for mutating internal actions. (#46789) Thanks @tdjackey and @vincentkoc. +- Subagents/follow-ups: require the same controller ownership checks for `/subagents send` as other control actions, so leaf sessions cannot message nested child runs they do not control. Thanks @vincentkoc. +- macOS/canvas actions: keep unattended local agent actions on trusted in-app canvas surfaces only, and stop exposing the deep-link fallback key to arbitrary page scripts. (#46790) Thanks @vincentkoc. +- Agents/compaction: extend the enclosing run deadline once while compaction is actively in flight, and abort the underlying SDK compaction on timeout/cancel so large-session compactions stop freezing mid-run. (#46889) Thanks @asyncjason. +- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026. +- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava. +- Models/OpenRouter runtime capabilities: fetch uncatalogued OpenRouter model metadata on first use so newly added vision models keep image input instead of silently degrading to text-only, with top-level capability field fallbacks for `/api/v1/models`. (#45824) Thanks @DJjjjhao. +- Channels/plugins: keep shared interactive payloads merge-ready by fixing Slack custom callback routing and repeat-click dedupe, allowing interactive-only sends, and preserving ordered Discord shared text blocks. (#47715) Thanks @vincentkoc. +- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc. +- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc. +- Feishu/actions: expand the runtime action surface with message read/edit, explicit thread replies, pinning, and operator-facing chat/member inspection so Feishu can operate more of the workspace directly. +- Feishu/topic threads: fetch full thread context, including prior bot replies, when starting a topic-thread session so follow-up turns in Feishu topics keep the right conversation state. (#45254) Thanks @Coobiw. +- Feishu/media: keep native image, file, audio, and video/media handling aligned across outbound sends, inbound downloads, thread replies, directory/action aliases, and capability docs so unsupported areas are explicit instead of implied. +- WhatsApp/reconnect: restore the append recency filter in the extension inbox monitor and handle protobuf `Long` timestamps correctly, so fresh post-reconnect append messages are processed while stale history sync stays suppressed. (#42588) thanks @MonkeyLeeT. +- WhatsApp/login: wait for pending creds writes before reopening after Baileys `515` pairing restarts in both QR login and `channels login` flows, and keep the restart coverage pinned to the real wrapped error shape plus per-account creds queues. (#27910) Thanks @asyncjason. +- Telegram/message send: forward `--force-document` through the `sendPayload` path as well as `sendMedia`, so Telegram payload sends with `channelData` keep uploading images as documents instead of silently falling back to compressed photo sends. (#47119) Thanks @thepagent. +- Telegram/message chunking: preserve spaces, paragraph separators, and word boundaries when HTML overflow rechunking splits formatted replies. (#47274) +- Z.AI/onboarding: detect a working default model even for explicit `zai-coding-*` endpoint choices, so Coding Plan setup can keep the selected endpoint while defaulting to `glm-5` when available or `glm-4.7` as fallback. (#45969) +- Z.AI/onboarding: add `glm-5-turbo` to the default Z.AI provider catalog so onboarding-generated configs expose the new model alongside the existing GLM defaults. (#46670) Thanks @tomsun28. +- Zalo Personal/group gating: stop reapplying `dmPolicy.allowFrom` as a sender gate for already-allowlisted groups when `groupAllowFrom` is unset, so any member of an allowed group can trigger replies while DMs stay restricted. (#40146) +- Zalo/plugin runtime: export `resolveClientIp` from `openclaw/plugin-sdk/zalo` so installed builds no longer crash on startup when the webhook monitor loads from the packaged extension instead of the monorepo source tree. (#46549) Thanks @No898. +- Plugins/install precedence: keep bundled plugins ahead of auto-discovered globals by default, but let an explicitly installed plugin record win its own duplicate-id tie so installed channel plugins load from `~/.openclaw/extensions` after `openclaw plugins install`. +- Plugins/scoped ids: preserve scoped plugin ids during install and config keying, and keep bundled plugins ahead of discovered duplicate ids by default so `@scope/name` plugins no longer collide with unscoped installs. Thanks @vincentkoc. - Gateway/watch mode: restart on bundled-plugin package and manifest metadata changes, rebuild `dist` for extension source and `tsdown.config.ts` changes, and still ignore extension docs. (#47571) thanks @gumadeiras. - Gateway/watch mode: recreate bundled plugin runtime metadata after clean or stale `dist` states, so `pnpm gateway:watch` no longer fails on missing `dist/extensions/*/openclaw.plugin.json` manifests after a rebuild. Thanks @gumadeiras. -- Plugins/context engines: enforce owner-aware context-engine registration on both loader and public SDK paths so plugins cannot spoof privileged ownership, claim the core `legacy` engine id, or overwrite an existing engine id through direct SDK imports. (#47595) Thanks @vincentkoc. +- Control UI/chat sessions: show human-readable labels in the grouped session dropdown again, keep unique scoped fallbacks when metadata is missing, and disambiguate duplicate labels only when needed. (#45130) thanks @luzhidong. +- Control UI: scope persisted session selection per gateway, prevent stale session bleed across tokenized gateway opens, and cap stored gateway session history. (#47453) Thanks @sallyom. +- Control UI/dashboard: preserve structured gateway shutdown reasons across restart disconnects so config-triggered restarts no longer fall back to `disconnected (1006): no reason`. (#46532) Thanks @vincentkoc. +- Android/chat: theme the thinking dropdown and TLS trust dialogs explicitly so popup surfaces match the active app theme instead of falling back to mismatched Material defaults. +- Group mention gating: reject invalid and unsafe nested-repetition `mentionPatterns`, reuse the shared safe config-regex compiler across mention stripping and detection, and cache strip-time regex compilation so noisy groups avoid repeated recompiles. +- Browser/profiles: drop the auto-created `chrome-relay` browser profile; users who need the Chrome extension relay must now create their own profile via `openclaw browser create-profile`. (#45777) Thanks @odysseus0. +- CI/channel test routing: move the built-in channel suites into `test:channels` and keep them out of `test:extensions`, so extension CI no longer fails after the channel migration while targeted test routing still sends Slack, Signal, and iMessage suites to the right lane. (#46066) Thanks @scoootscooob. +- Docs/Mintlify: fix MDX marker syntax on Perplexity, Model Providers, Moonshot, and exec approvals pages so local docs preview no longer breaks rendering or leaves stale pages unpublished. (#46695) Thanks @velvet-shark. +- Gateway/config validation: stop treating the implicit default memory slot as a required explicit plugin config, so startup no longer fails with `plugins.slots.memory: plugin not found: memory-core` when `memory-core` was only inferred. (#47494) Thanks @ngutman. +- Tlon: honor explicit empty allowlists and defer cite expansion. (#46788) Thanks @zpbrent and @vincentkoc. +- Nodes/pending actions: re-check queued foreground actions against the current node command policy before returning them to the node. (#46815) Thanks @zpbrent and @vincentkoc. +- Node/startup: remove leftover debug `console.log("node host PATH: ...")` that printed the resolved PATH on every `openclaw node run` invocation. (#46411) +- CLI/completion: reduce recursive completion-script string churn and fix nested PowerShell command-path matching so generated nested completions resolve on PowerShell too. (#45537) Thanks @yiShanXin and @vincentkoc. ## 2026.3.13 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4184a550691..9b1fa35d6a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,6 +89,9 @@ Welcome to the lobster tank! 🦞 - Test locally with your OpenClaw instance - Run tests: `pnpm build && pnpm check && pnpm test` +- For extension/plugin changes, run the fast local lane first: + - `pnpm test:extension ` + - If you changed shared plugin or channel surfaces, still run the broader relevant lanes (`pnpm test:extensions`, `pnpm test:channels`, or `pnpm test`) before asking for review - If you have access to Codex, run `codex review --base origin/main` locally before opening or updating your PR. Treat this as the current highest standard of AI review, even if GitHub Codex review also runs. - Ensure CI checks pass - Keep PRs focused (one thing per PR; do not mix unrelated concerns) diff --git a/apps/macos/Sources/OpenClaw/CanvasA2UIActionMessageHandler.swift b/apps/macos/Sources/OpenClaw/CanvasA2UIActionMessageHandler.swift index c81d4b59705..0599f4ab3a6 100644 --- a/apps/macos/Sources/OpenClaw/CanvasA2UIActionMessageHandler.swift +++ b/apps/macos/Sources/OpenClaw/CanvasA2UIActionMessageHandler.swift @@ -8,6 +8,24 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler { static let messageName = "openclawCanvasA2UIAction" static let allMessageNames = [messageName] + // Compatibility helper for debug/test shims. Runtime dispatch remains + // limited to in-app canvas schemes in `didReceive`. + static func isLocalNetworkCanvasURL(_ url: URL) -> Bool { + guard let scheme = url.scheme?.lowercased(), scheme == "http" || scheme == "https" else { + return false + } + guard let host = url.host?.lowercased(), !host.isEmpty else { + return false + } + if host == "localhost" { + return true + } + guard let ip = Self.parseIPv4(host) else { + return false + } + return Self.isLocalNetworkIPv4(ip) + } + private let sessionKey: String init(sessionKey: String) { @@ -104,5 +122,24 @@ final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler { } } } + + private static func parseIPv4(_ host: String) -> (UInt8, UInt8, UInt8, UInt8)? { + let parts = host.split(separator: ".", omittingEmptySubsequences: false) + guard parts.count == 4 else { return nil } + let bytes = parts.compactMap { UInt8($0) } + guard bytes.count == 4 else { return nil } + return (bytes[0], bytes[1], bytes[2], bytes[3]) + } + + private static func isLocalNetworkIPv4(_ ip: (UInt8, UInt8, UInt8, UInt8)) -> Bool { + let (a, b, _, _) = ip + if a == 10 { return true } + if a == 172, (16...31).contains(Int(b)) { return true } + if a == 192, b == 168 { return true } + if a == 127 { return true } + if a == 169, b == 254 { return true } + if a == 100, (64...127).contains(Int(b)) { return true } + return false + } // Formatting helpers live in OpenClawKit (`OpenClawCanvasA2UIAction`). } diff --git a/apps/macos/Sources/OpenClaw/CronModels.swift b/apps/macos/Sources/OpenClaw/CronModels.swift index 40079453974..78016ff9f88 100644 --- a/apps/macos/Sources/OpenClaw/CronModels.swift +++ b/apps/macos/Sources/OpenClaw/CronModels.swift @@ -254,6 +254,71 @@ struct CronJob: Identifiable, Codable, Equatable { case state } + init( + id: String, + agentId: String?, + name: String, + description: String?, + enabled: Bool, + deleteAfterRun: Bool?, + createdAtMs: Int, + updatedAtMs: Int, + schedule: CronSchedule, + sessionTarget: CronSessionTarget, + wakeMode: CronWakeMode, + payload: CronPayload, + delivery: CronDelivery?, + state: CronJobState) + { + self.init( + id: id, + agentId: agentId, + name: name, + description: description, + enabled: enabled, + deleteAfterRun: deleteAfterRun, + createdAtMs: createdAtMs, + updatedAtMs: updatedAtMs, + schedule: schedule, + sessionTarget: .predefined(sessionTarget), + wakeMode: wakeMode, + payload: payload, + delivery: delivery, + state: state) + } + + init( + id: String, + agentId: String?, + name: String, + description: String?, + enabled: Bool, + deleteAfterRun: Bool?, + createdAtMs: Int, + updatedAtMs: Int, + schedule: CronSchedule, + sessionTarget: CronCustomSessionTarget, + wakeMode: CronWakeMode, + payload: CronPayload, + delivery: CronDelivery?, + state: CronJobState) + { + self.id = id + self.agentId = agentId + self.name = name + self.description = description + self.enabled = enabled + self.deleteAfterRun = deleteAfterRun + self.createdAtMs = createdAtMs + self.updatedAtMs = updatedAtMs + self.schedule = schedule + self.sessionTargetRaw = sessionTarget.rawValue + self.wakeMode = wakeMode + self.payload = payload + self.delivery = delivery + self.state = state + } + /// Parsed session target (predefined or custom session ID) var parsedSessionTarget: CronCustomSessionTarget { CronCustomSessionTarget.from(self.sessionTargetRaw) diff --git a/assets/chrome-extension/README.md b/assets/chrome-extension/README.md deleted file mode 100644 index 4ee072c1f2b..00000000000 --- a/assets/chrome-extension/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# OpenClaw Chrome Extension (Browser Relay) - -Purpose: attach OpenClaw to an existing Chrome tab so the Gateway can automate it (via the local CDP relay server). - -## Dev / load unpacked - -1. Build/run OpenClaw Gateway with browser control enabled. -2. Ensure the relay server is reachable at `http://127.0.0.1:18792/` (default). -3. Install the extension to a stable path: - - ```bash - openclaw browser extension install - openclaw browser extension path - ``` - -4. Chrome → `chrome://extensions` → enable “Developer mode”. -5. “Load unpacked” → select the path printed above. -6. Pin the extension. Click the icon on a tab to attach/detach. - -## Options - -- `Relay port`: defaults to `18792`. -- `Gateway token`: required. Set this to `gateway.auth.token` (or `OPENCLAW_GATEWAY_TOKEN`). diff --git a/assets/chrome-extension/background-utils.js b/assets/chrome-extension/background-utils.js deleted file mode 100644 index 82d43359c0a..00000000000 --- a/assets/chrome-extension/background-utils.js +++ /dev/null @@ -1,64 +0,0 @@ -export function reconnectDelayMs( - attempt, - opts = { baseMs: 1000, maxMs: 30000, jitterMs: 1000, random: Math.random }, -) { - const baseMs = Number.isFinite(opts.baseMs) ? opts.baseMs : 1000; - const maxMs = Number.isFinite(opts.maxMs) ? opts.maxMs : 30000; - const jitterMs = Number.isFinite(opts.jitterMs) ? opts.jitterMs : 1000; - const random = typeof opts.random === "function" ? opts.random : Math.random; - const safeAttempt = Math.max(0, Number.isFinite(attempt) ? attempt : 0); - const backoff = Math.min(baseMs * 2 ** safeAttempt, maxMs); - return backoff + Math.max(0, jitterMs) * random(); -} - -export async function deriveRelayToken(gatewayToken, port) { - const enc = new TextEncoder(); - const key = await crypto.subtle.importKey( - "raw", - enc.encode(gatewayToken), - { name: "HMAC", hash: "SHA-256" }, - false, - ["sign"], - ); - const sig = await crypto.subtle.sign( - "HMAC", - key, - enc.encode(`openclaw-extension-relay-v1:${port}`), - ); - return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join(""); -} - -export async function buildRelayWsUrl(port, gatewayToken) { - const token = String(gatewayToken || "").trim(); - if (!token) { - throw new Error( - "Missing gatewayToken in extension settings (chrome.storage.local.gatewayToken)", - ); - } - const relayToken = await deriveRelayToken(token, port); - return `ws://127.0.0.1:${port}/extension?token=${encodeURIComponent(relayToken)}`; -} - -export function isRetryableReconnectError(err) { - const message = err instanceof Error ? err.message : String(err || ""); - if (message.includes("Missing gatewayToken")) { - return false; - } - return true; -} - -export function isMissingTabError(err) { - const message = (err instanceof Error ? err.message : String(err || "")).toLowerCase(); - return ( - message.includes("no tab with id") || - message.includes("no tab with given id") || - message.includes("tab not found") - ); -} - -export function isLastRemainingTab(allTabs, tabIdToClose) { - if (!Array.isArray(allTabs)) { - return true; - } - return allTabs.filter((tab) => tab && tab.id !== tabIdToClose).length === 0; -} diff --git a/assets/chrome-extension/background.js b/assets/chrome-extension/background.js deleted file mode 100644 index 9031a156489..00000000000 --- a/assets/chrome-extension/background.js +++ /dev/null @@ -1,1025 +0,0 @@ -import { - buildRelayWsUrl, - isLastRemainingTab, - isMissingTabError, - isRetryableReconnectError, - reconnectDelayMs, -} from './background-utils.js' - -const DEFAULT_PORT = 18792 - -const BADGE = { - on: { text: 'ON', color: '#FF5A36' }, - off: { text: '', color: '#000000' }, - connecting: { text: '…', color: '#F59E0B' }, - error: { text: '!', color: '#B91C1C' }, -} - -/** @type {WebSocket|null} */ -let relayWs = null -/** @type {Promise|null} */ -let relayConnectPromise = null -let relayGatewayToken = '' -/** @type {string|null} */ -let relayConnectRequestId = null - -let nextSession = 1 - -/** @type {Map} */ -const tabs = new Map() -/** @type {Map} */ -const tabBySession = new Map() -/** @type {Map} */ -const childSessionToTab = new Map() - -/** @type {Mapvoid, reject:(e:Error)=>void}>} */ -const pending = new Map() - -// Per-tab operation locks prevent double-attach races. -/** @type {Set} */ -const tabOperationLocks = new Set() - -// Tabs currently in a detach/re-attach cycle after navigation. -/** @type {Set} */ -const reattachPending = new Set() - -// Reconnect state for exponential backoff. -let reconnectAttempt = 0 -let reconnectTimer = null - -const TAB_VALIDATION_ATTEMPTS = 2 -const TAB_VALIDATION_RETRY_DELAY_MS = 1000 - -function nowStack() { - try { - return new Error().stack || '' - } catch { - return '' - } -} - -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)) -} - -async function validateAttachedTab(tabId) { - try { - await chrome.tabs.get(tabId) - } catch { - return false - } - - for (let attempt = 0; attempt < TAB_VALIDATION_ATTEMPTS; attempt++) { - try { - await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', { - expression: '1', - returnByValue: true, - }) - return true - } catch (err) { - if (isMissingTabError(err)) { - return false - } - if (attempt < TAB_VALIDATION_ATTEMPTS - 1) { - await sleep(TAB_VALIDATION_RETRY_DELAY_MS) - } - } - } - - return false -} - -async function getRelayPort() { - const stored = await chrome.storage.local.get(['relayPort']) - const raw = stored.relayPort - const n = Number.parseInt(String(raw || ''), 10) - if (!Number.isFinite(n) || n <= 0 || n > 65535) return DEFAULT_PORT - return n -} - -async function getGatewayToken() { - const stored = await chrome.storage.local.get(['gatewayToken']) - const token = String(stored.gatewayToken || '').trim() - return token || '' -} - -function setBadge(tabId, kind) { - const cfg = BADGE[kind] - void chrome.action.setBadgeText({ tabId, text: cfg.text }) - void chrome.action.setBadgeBackgroundColor({ tabId, color: cfg.color }) - void chrome.action.setBadgeTextColor({ tabId, color: '#FFFFFF' }).catch(() => {}) -} - -// Persist attached tab state to survive MV3 service worker restarts. -async function persistState() { - try { - const tabEntries = [] - for (const [tabId, tab] of tabs.entries()) { - if (tab.state === 'connected' && tab.sessionId && tab.targetId) { - tabEntries.push({ tabId, sessionId: tab.sessionId, targetId: tab.targetId, attachOrder: tab.attachOrder }) - } - } - await chrome.storage.session.set({ - persistedTabs: tabEntries, - nextSession, - }) - } catch { - // chrome.storage.session may not be available in all contexts. - } -} - -// Rehydrate tab state on service worker startup. Fast path — just restores -// maps and badges. Relay reconnect happens separately in background. -async function rehydrateState() { - try { - const stored = await chrome.storage.session.get(['persistedTabs', 'nextSession']) - if (stored.nextSession) { - nextSession = Math.max(nextSession, stored.nextSession) - } - const entries = stored.persistedTabs || [] - // Phase 1: optimistically restore state and badges. - for (const entry of entries) { - tabs.set(entry.tabId, { - state: 'connected', - sessionId: entry.sessionId, - targetId: entry.targetId, - attachOrder: entry.attachOrder, - }) - tabBySession.set(entry.sessionId, entry.tabId) - setBadge(entry.tabId, 'on') - } - // Retry once so transient busy/navigation states do not permanently drop - // a still-attached tab after a service worker restart. - for (const entry of entries) { - const valid = await validateAttachedTab(entry.tabId) - if (!valid) { - tabs.delete(entry.tabId) - tabBySession.delete(entry.sessionId) - setBadge(entry.tabId, 'off') - } - } - } catch { - // Ignore rehydration errors. - } -} - -async function ensureRelayConnection() { - if (relayWs && relayWs.readyState === WebSocket.OPEN) return - if (relayConnectPromise) return await relayConnectPromise - - relayConnectPromise = (async () => { - const port = await getRelayPort() - const gatewayToken = await getGatewayToken() - const httpBase = `http://127.0.0.1:${port}` - const wsUrl = await buildRelayWsUrl(port, gatewayToken) - - // Fast preflight: is the relay server up? - try { - await fetch(`${httpBase}/`, { method: 'HEAD', signal: AbortSignal.timeout(2000) }) - } catch (err) { - throw new Error(`Relay server not reachable at ${httpBase} (${String(err)})`) - } - - const ws = new WebSocket(wsUrl) - relayWs = ws - relayGatewayToken = gatewayToken - // Bind message handler before open so an immediate first frame (for example - // gateway connect.challenge) cannot be missed. - ws.onmessage = (event) => { - if (ws !== relayWs) return - void whenReady(() => onRelayMessage(String(event.data || ''))) - } - - await new Promise((resolve, reject) => { - const t = setTimeout(() => reject(new Error('WebSocket connect timeout')), 5000) - ws.onopen = () => { - clearTimeout(t) - resolve() - } - ws.onerror = () => { - clearTimeout(t) - reject(new Error('WebSocket connect failed')) - } - ws.onclose = (ev) => { - clearTimeout(t) - reject(new Error(`WebSocket closed (${ev.code} ${ev.reason || 'no reason'})`)) - } - }) - - // Bind permanent handlers. Guard against stale socket: if this WS was - // replaced before its close fires, the handler is a no-op. - ws.onclose = () => { - if (ws !== relayWs) return - onRelayClosed('closed') - } - ws.onerror = () => { - if (ws !== relayWs) return - onRelayClosed('error') - } - })() - - try { - await relayConnectPromise - reconnectAttempt = 0 - } finally { - relayConnectPromise = null - } -} - -// Relay closed — update badges, reject pending requests, auto-reconnect. -// Debugger sessions are kept alive so they survive transient WS drops. -function onRelayClosed(reason) { - relayWs = null - relayGatewayToken = '' - relayConnectRequestId = null - - for (const [id, p] of pending.entries()) { - pending.delete(id) - p.reject(new Error(`Relay disconnected (${reason})`)) - } - - reattachPending.clear() - - for (const [tabId, tab] of tabs.entries()) { - if (tab.state === 'connected') { - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: relay reconnecting…', - }) - } - } - - scheduleReconnect() -} - -function scheduleReconnect() { - if (reconnectTimer) { - clearTimeout(reconnectTimer) - reconnectTimer = null - } - - const delay = reconnectDelayMs(reconnectAttempt) - reconnectAttempt++ - - console.log(`Scheduling reconnect attempt ${reconnectAttempt} in ${Math.round(delay)}ms`) - - reconnectTimer = setTimeout(async () => { - reconnectTimer = null - try { - await ensureRelayConnection() - reconnectAttempt = 0 - console.log('Reconnected successfully') - await reannounceAttachedTabs() - } catch (err) { - const message = err instanceof Error ? err.message : String(err) - console.warn(`Reconnect attempt ${reconnectAttempt} failed: ${message}`) - if (!isRetryableReconnectError(err)) { - return - } - scheduleReconnect() - } - }, delay) -} - -function cancelReconnect() { - if (reconnectTimer) { - clearTimeout(reconnectTimer) - reconnectTimer = null - } - reconnectAttempt = 0 -} - -// Re-announce all attached tabs to the relay after reconnect. -async function reannounceAttachedTabs() { - for (const [tabId, tab] of tabs.entries()) { - if (tab.state !== 'connected' || !tab.sessionId || !tab.targetId) continue - - // Retry once here as well; reconnect races can briefly make an otherwise - // healthy tab look unavailable. - const valid = await validateAttachedTab(tabId) - if (!valid) { - tabs.delete(tabId) - if (tab.sessionId) tabBySession.delete(tab.sessionId) - setBadge(tabId, 'off') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay (click to attach/detach)', - }) - continue - } - - // Send fresh attach event to relay. - // Split into two try-catch blocks so debugger failures and relay send - // failures are handled independently. Previously, a relay send failure - // would fall into the outer catch and set the badge to 'on' even though - // the relay had no record of the tab — causing every subsequent browser - // tool call to fail with "no tab connected" until the next reconnect cycle. - let targetInfo - try { - const info = /** @type {any} */ ( - await chrome.debugger.sendCommand({ tabId }, 'Target.getTargetInfo') - ) - targetInfo = info?.targetInfo - } catch { - // Target.getTargetInfo failed. Preserve at least targetId from - // cached tab state so relay receives a stable identifier. - targetInfo = tab.targetId ? { targetId: tab.targetId } : undefined - } - - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.attachedToTarget', - params: { - sessionId: tab.sessionId, - targetInfo: { ...targetInfo, attached: true }, - waitingForDebugger: false, - }, - }, - }) - - setBadge(tabId, 'on') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: attached (click to detach)', - }) - } catch { - // Relay send failed (e.g. WS closed in the gap between ensureRelayConnection - // resolving and this loop executing). The tab is still valid — leave badge - // as 'connecting' so the reconnect/keepalive cycle will retry rather than - // showing a false-positive 'on' that hides the broken state from the user. - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: relay reconnecting…', - }) - } - } - - await persistState() -} - -function sendToRelay(payload) { - const ws = relayWs - if (!ws || ws.readyState !== WebSocket.OPEN) { - throw new Error('Relay not connected') - } - ws.send(JSON.stringify(payload)) -} - -function ensureGatewayHandshakeStarted(payload) { - if (relayConnectRequestId) return - const nonce = typeof payload?.nonce === 'string' ? payload.nonce.trim() : '' - relayConnectRequestId = `ext-connect-${Date.now()}-${Math.random().toString(16).slice(2, 8)}` - sendToRelay({ - type: 'req', - id: relayConnectRequestId, - method: 'connect', - params: { - minProtocol: 3, - maxProtocol: 3, - client: { - id: 'chrome-relay-extension', - version: '1.0.0', - platform: 'chrome-extension', - mode: 'webchat', - }, - role: 'operator', - scopes: ['operator.read', 'operator.write'], - caps: [], - commands: [], - nonce: nonce || undefined, - auth: relayGatewayToken ? { token: relayGatewayToken } : undefined, - }, - }) -} - -async function maybeOpenHelpOnce() { - try { - const stored = await chrome.storage.local.get(['helpOnErrorShown']) - if (stored.helpOnErrorShown === true) return - await chrome.storage.local.set({ helpOnErrorShown: true }) - await chrome.runtime.openOptionsPage() - } catch { - // ignore - } -} - -function requestFromRelay(command) { - const id = command.id - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - pending.delete(id) - reject(new Error('Relay request timeout (30s)')) - }, 30000) - pending.set(id, { - resolve: (v) => { clearTimeout(timer); resolve(v) }, - reject: (e) => { clearTimeout(timer); reject(e) }, - }) - try { - sendToRelay(command) - } catch (err) { - clearTimeout(timer) - pending.delete(id) - reject(err instanceof Error ? err : new Error(String(err))) - } - }) -} - -async function onRelayMessage(text) { - /** @type {any} */ - let msg - try { - msg = JSON.parse(text) - } catch { - return - } - - if (msg && msg.type === 'event' && msg.event === 'connect.challenge') { - try { - ensureGatewayHandshakeStarted(msg.payload) - } catch (err) { - console.warn('gateway connect handshake start failed', err instanceof Error ? err.message : String(err)) - relayConnectRequestId = null - const ws = relayWs - if (ws && ws.readyState === WebSocket.OPEN) { - ws.close(1008, 'gateway connect failed') - } - } - return - } - - if (msg && msg.type === 'res' && relayConnectRequestId && msg.id === relayConnectRequestId) { - relayConnectRequestId = null - if (!msg.ok) { - const detail = msg?.error?.message || msg?.error || 'gateway connect failed' - console.warn('gateway connect handshake rejected', String(detail)) - const ws = relayWs - if (ws && ws.readyState === WebSocket.OPEN) { - ws.close(1008, 'gateway connect failed') - } - } - return - } - - if (msg && msg.method === 'ping') { - try { - sendToRelay({ method: 'pong' }) - } catch { - // ignore - } - return - } - - if (msg && typeof msg.id === 'number' && (msg.result !== undefined || msg.error !== undefined)) { - const p = pending.get(msg.id) - if (!p) return - pending.delete(msg.id) - if (msg.error) p.reject(new Error(String(msg.error))) - else p.resolve(msg.result) - return - } - - if (msg && typeof msg.id === 'number' && msg.method === 'forwardCDPCommand') { - try { - const result = await handleForwardCdpCommand(msg) - sendToRelay({ id: msg.id, result }) - } catch (err) { - sendToRelay({ id: msg.id, error: err instanceof Error ? err.message : String(err) }) - } - } -} - -function getTabBySessionId(sessionId) { - const direct = tabBySession.get(sessionId) - if (direct) return { tabId: direct, kind: 'main' } - const child = childSessionToTab.get(sessionId) - if (child) return { tabId: child, kind: 'child' } - return null -} - -function getTabByTargetId(targetId) { - for (const [tabId, tab] of tabs.entries()) { - if (tab.targetId === targetId) return tabId - } - return null -} - -async function attachTab(tabId, opts = {}) { - const debuggee = { tabId } - await chrome.debugger.attach(debuggee, '1.3') - await chrome.debugger.sendCommand(debuggee, 'Page.enable').catch(() => {}) - - const info = /** @type {any} */ (await chrome.debugger.sendCommand(debuggee, 'Target.getTargetInfo')) - const targetInfo = info?.targetInfo - const targetId = String(targetInfo?.targetId || '').trim() - if (!targetId) { - throw new Error('Target.getTargetInfo returned no targetId') - } - - const sid = nextSession++ - const sessionId = `cb-tab-${sid}` - const attachOrder = sid - - tabs.set(tabId, { state: 'connected', sessionId, targetId, attachOrder }) - tabBySession.set(sessionId, tabId) - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: attached (click to detach)', - }) - - if (!opts.skipAttachedEvent) { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.attachedToTarget', - params: { - sessionId, - targetInfo: { ...targetInfo, attached: true }, - waitingForDebugger: false, - }, - }, - }) - } - - setBadge(tabId, 'on') - await persistState() - - return { sessionId, targetId } -} - -async function detachTab(tabId, reason) { - const tab = tabs.get(tabId) - - // Send detach events for child sessions first. - for (const [childSessionId, parentTabId] of childSessionToTab.entries()) { - if (parentTabId === tabId) { - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.detachedFromTarget', - params: { sessionId: childSessionId, reason: 'parent_detached' }, - }, - }) - } catch { - // Relay may be down. - } - childSessionToTab.delete(childSessionId) - } - } - - // Send detach event for main session. - if (tab?.sessionId && tab?.targetId) { - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.detachedFromTarget', - params: { sessionId: tab.sessionId, targetId: tab.targetId, reason }, - }, - }) - } catch { - // Relay may be down. - } - } - - if (tab?.sessionId) tabBySession.delete(tab.sessionId) - tabs.delete(tabId) - - try { - await chrome.debugger.detach({ tabId }) - } catch { - // May already be detached. - } - - setBadge(tabId, 'off') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay (click to attach/detach)', - }) - - await persistState() -} - -async function connectOrToggleForActiveTab() { - const [active] = await chrome.tabs.query({ active: true, currentWindow: true }) - const tabId = active?.id - if (!tabId) return - - // Prevent concurrent operations on the same tab. - if (tabOperationLocks.has(tabId)) return - tabOperationLocks.add(tabId) - - try { - if (reattachPending.has(tabId)) { - reattachPending.delete(tabId) - setBadge(tabId, 'off') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay (click to attach/detach)', - }) - return - } - - const existing = tabs.get(tabId) - if (existing?.state === 'connected') { - await detachTab(tabId, 'toggle') - return - } - - // User is manually connecting — cancel any pending reconnect. - cancelReconnect() - - tabs.set(tabId, { state: 'connecting' }) - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: connecting to local relay…', - }) - - try { - await ensureRelayConnection() - await attachTab(tabId) - } catch (err) { - tabs.delete(tabId) - setBadge(tabId, 'error') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: relay not running (open options for setup)', - }) - void maybeOpenHelpOnce() - const message = err instanceof Error ? err.message : String(err) - console.warn('attach failed', message, nowStack()) - } - } finally { - tabOperationLocks.delete(tabId) - } -} - -async function handleForwardCdpCommand(msg) { - const method = String(msg?.params?.method || '').trim() - const params = msg?.params?.params || undefined - const sessionId = typeof msg?.params?.sessionId === 'string' ? msg.params.sessionId : undefined - - const bySession = sessionId ? getTabBySessionId(sessionId) : null - const targetId = typeof params?.targetId === 'string' ? params.targetId : undefined - const tabId = - bySession?.tabId || - (targetId ? getTabByTargetId(targetId) : null) || - (() => { - for (const [id, tab] of tabs.entries()) { - if (tab.state === 'connected') return id - } - return null - })() - - if (!tabId) throw new Error(`No attached tab for method ${method}`) - - /** @type {chrome.debugger.DebuggerSession} */ - const debuggee = { tabId } - - if (method === 'Runtime.enable') { - try { - await chrome.debugger.sendCommand(debuggee, 'Runtime.disable') - await new Promise((r) => setTimeout(r, 50)) - } catch { - // ignore - } - return await chrome.debugger.sendCommand(debuggee, 'Runtime.enable', params) - } - - if (method === 'Target.createTarget') { - const url = typeof params?.url === 'string' ? params.url : 'about:blank' - const tab = await chrome.tabs.create({ url, active: false }) - if (!tab.id) throw new Error('Failed to create tab') - await new Promise((r) => setTimeout(r, 100)) - const attached = await attachTab(tab.id) - return { targetId: attached.targetId } - } - - if (method === 'Target.closeTarget') { - const target = typeof params?.targetId === 'string' ? params.targetId : '' - const toClose = target ? getTabByTargetId(target) : tabId - if (!toClose) return { success: false } - try { - const allTabs = await chrome.tabs.query({}) - if (isLastRemainingTab(allTabs, toClose)) { - console.warn('Refusing to close the last tab: this would kill the browser process') - return { success: false, error: 'Cannot close the last tab' } - } - await chrome.tabs.remove(toClose) - } catch { - return { success: false } - } - return { success: true } - } - - if (method === 'Target.activateTarget') { - const target = typeof params?.targetId === 'string' ? params.targetId : '' - const toActivate = target ? getTabByTargetId(target) : tabId - if (!toActivate) return {} - const tab = await chrome.tabs.get(toActivate).catch(() => null) - if (!tab) return {} - if (tab.windowId) { - await chrome.windows.update(tab.windowId, { focused: true }).catch(() => {}) - } - await chrome.tabs.update(toActivate, { active: true }).catch(() => {}) - return {} - } - - const tabState = tabs.get(tabId) - const mainSessionId = tabState?.sessionId - const debuggerSession = - sessionId && mainSessionId && sessionId !== mainSessionId - ? { ...debuggee, sessionId } - : debuggee - - return await chrome.debugger.sendCommand(debuggerSession, method, params) -} - -function onDebuggerEvent(source, method, params) { - const tabId = source.tabId - if (!tabId) return - const tab = tabs.get(tabId) - if (!tab?.sessionId) return - - if (method === 'Target.attachedToTarget' && params?.sessionId) { - childSessionToTab.set(String(params.sessionId), tabId) - } - - if (method === 'Target.detachedFromTarget' && params?.sessionId) { - childSessionToTab.delete(String(params.sessionId)) - } - - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - sessionId: source.sessionId || tab.sessionId, - method, - params, - }, - }) - } catch { - // Relay may be down. - } -} - -async function onDebuggerDetach(source, reason) { - const tabId = source.tabId - if (!tabId) return - if (!tabs.has(tabId)) return - - // User explicitly cancelled or DevTools replaced the connection — respect their intent - if (reason === 'canceled_by_user' || reason === 'replaced_with_devtools') { - void detachTab(tabId, reason) - return - } - - // Check if tab still exists — distinguishes navigation from tab close - let tabInfo - try { - tabInfo = await chrome.tabs.get(tabId) - } catch { - // Tab is gone (closed) — normal cleanup - void detachTab(tabId, reason) - return - } - - if (tabInfo.url?.startsWith('chrome://') || tabInfo.url?.startsWith('chrome-extension://')) { - void detachTab(tabId, reason) - return - } - - if (reattachPending.has(tabId)) return - - const oldTab = tabs.get(tabId) - const oldSessionId = oldTab?.sessionId - const oldTargetId = oldTab?.targetId - - if (oldSessionId) tabBySession.delete(oldSessionId) - tabs.delete(tabId) - for (const [childSessionId, parentTabId] of childSessionToTab.entries()) { - if (parentTabId === tabId) childSessionToTab.delete(childSessionId) - } - - if (oldSessionId && oldTargetId) { - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.detachedFromTarget', - params: { sessionId: oldSessionId, targetId: oldTargetId, reason: 'navigation-reattach' }, - }, - }) - } catch { - // Relay may be down. - } - } - - reattachPending.add(tabId) - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: re-attaching after navigation…', - }) - - // Extend re-attach window from 2.5 s to ~7.7 s (5 attempts). - // SPAs and pages with heavy JS can take >2.5 s before the Chrome debugger - // is attachable, causing all three original attempts to fail and leaving - // the badge permanently off after every navigation. - const delays = [200, 500, 1000, 2000, 4000] - for (let attempt = 0; attempt < delays.length; attempt++) { - await new Promise((r) => setTimeout(r, delays[attempt])) - - if (!reattachPending.has(tabId)) return - - try { - await chrome.tabs.get(tabId) - } catch { - reattachPending.delete(tabId) - setBadge(tabId, 'off') - return - } - - const relayUp = relayWs && relayWs.readyState === WebSocket.OPEN - - try { - // When relay is down, still attach the debugger but skip sending the - // relay event. reannounceAttachedTabs() will notify the relay once it - // reconnects, so the tab stays tracked across transient relay drops. - await attachTab(tabId, { skipAttachedEvent: !relayUp }) - reattachPending.delete(tabId) - if (!relayUp) { - setBadge(tabId, 'connecting') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: attached, waiting for relay reconnect…', - }) - } - return - } catch { - // continue retries - } - } - - reattachPending.delete(tabId) - setBadge(tabId, 'off') - void chrome.action.setTitle({ - tabId, - title: 'OpenClaw Browser Relay: re-attach failed (click to retry)', - }) -} - -// Tab lifecycle listeners — clean up stale entries. -chrome.tabs.onRemoved.addListener((tabId) => void whenReady(() => { - reattachPending.delete(tabId) - if (!tabs.has(tabId)) return - const tab = tabs.get(tabId) - if (tab?.sessionId) tabBySession.delete(tab.sessionId) - tabs.delete(tabId) - for (const [childSessionId, parentTabId] of childSessionToTab.entries()) { - if (parentTabId === tabId) childSessionToTab.delete(childSessionId) - } - if (tab?.sessionId && tab?.targetId) { - try { - sendToRelay({ - method: 'forwardCDPEvent', - params: { - method: 'Target.detachedFromTarget', - params: { sessionId: tab.sessionId, targetId: tab.targetId, reason: 'tab_closed' }, - }, - }) - } catch { - // Relay may be down. - } - } - void persistState() -})) - -chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => void whenReady(() => { - const tab = tabs.get(removedTabId) - if (!tab) return - tabs.delete(removedTabId) - tabs.set(addedTabId, tab) - if (tab.sessionId) { - tabBySession.set(tab.sessionId, addedTabId) - } - for (const [childSessionId, parentTabId] of childSessionToTab.entries()) { - if (parentTabId === removedTabId) { - childSessionToTab.set(childSessionId, addedTabId) - } - } - setBadge(addedTabId, 'on') - void persistState() -})) - -// Register debugger listeners at module scope so detach/event handling works -// even when the relay WebSocket is down. -chrome.debugger.onEvent.addListener((...args) => void whenReady(() => onDebuggerEvent(...args))) -chrome.debugger.onDetach.addListener((...args) => void whenReady(() => onDebuggerDetach(...args))) - -chrome.action.onClicked.addListener(() => void whenReady(() => connectOrToggleForActiveTab())) - -// Refresh badge after navigation completes — service worker may have restarted -// during navigation, losing ephemeral badge state. -chrome.webNavigation.onCompleted.addListener(({ tabId, frameId }) => void whenReady(() => { - if (frameId !== 0) return - const tab = tabs.get(tabId) - if (tab?.state === 'connected') { - setBadge(tabId, relayWs && relayWs.readyState === WebSocket.OPEN ? 'on' : 'connecting') - } -})) - -// Refresh badge when user switches to an attached tab. -chrome.tabs.onActivated.addListener(({ tabId }) => void whenReady(() => { - const tab = tabs.get(tabId) - if (tab?.state === 'connected') { - setBadge(tabId, relayWs && relayWs.readyState === WebSocket.OPEN ? 'on' : 'connecting') - } -})) - -chrome.runtime.onInstalled.addListener(() => { - void chrome.runtime.openOptionsPage() -}) - -// MV3 keepalive via chrome.alarms — more reliable than setInterval across -// service worker restarts. Checks relay health and refreshes badges. -chrome.alarms.create('relay-keepalive', { periodInMinutes: 0.5 }) - -chrome.alarms.onAlarm.addListener(async (alarm) => { - if (alarm.name !== 'relay-keepalive') return - await initPromise - - if (tabs.size === 0) return - - // Refresh badges (ephemeral in MV3). - for (const [tabId, tab] of tabs.entries()) { - if (tab.state === 'connected') { - setBadge(tabId, relayWs && relayWs.readyState === WebSocket.OPEN ? 'on' : 'connecting') - } - } - - // If relay is down and no reconnect is in progress, trigger one. - if (!relayWs || relayWs.readyState !== WebSocket.OPEN) { - if (!relayConnectPromise && !reconnectTimer) { - console.log('Keepalive: WebSocket unhealthy, triggering reconnect') - await ensureRelayConnection().catch(() => { - // ensureRelayConnection may throw without triggering onRelayClosed - // (e.g. preflight fetch fails before WS is created), so ensure - // reconnect is always scheduled on failure. - if (!reconnectTimer) { - scheduleReconnect() - } - }) - } - } -}) - -// Rehydrate state on service worker startup. Split: rehydration is the gate -// (fast), relay reconnect runs in background (slow, non-blocking). -const initPromise = rehydrateState() - -initPromise.then(() => { - if (tabs.size > 0) { - ensureRelayConnection().then(() => { - reconnectAttempt = 0 - return reannounceAttachedTabs() - }).catch(() => { - scheduleReconnect() - }) - } -}) - -// Shared gate: all state-dependent handlers await this before accessing maps. -async function whenReady(fn) { - await initPromise - return fn() -} - -// Relay check handler for the options page. The service worker has -// host_permissions and bypasses CORS preflight, so the options page -// delegates token-validation requests here. -chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => { - if (msg?.type !== 'relayCheck') return false - const { url, token } = msg - const headers = token ? { 'x-openclaw-relay-token': token } : {} - fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(2000) }) - .then(async (res) => { - const contentType = String(res.headers.get('content-type') || '') - let json = null - if (contentType.includes('application/json')) { - try { - json = await res.json() - } catch { - json = null - } - } - sendResponse({ status: res.status, ok: res.ok, contentType, json }) - }) - .catch((err) => sendResponse({ status: 0, ok: false, error: String(err) })) - return true -}) diff --git a/assets/chrome-extension/manifest.json b/assets/chrome-extension/manifest.json deleted file mode 100644 index 62038276cd7..00000000000 --- a/assets/chrome-extension/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "manifest_version": 3, - "name": "OpenClaw Browser Relay", - "version": "0.1.0", - "description": "Attach OpenClaw to your existing Chrome tab via a local CDP relay server.", - "icons": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "48": "icons/icon48.png", - "128": "icons/icon128.png" - }, - "permissions": ["debugger", "tabs", "activeTab", "storage", "alarms", "webNavigation"], - "host_permissions": ["http://127.0.0.1/*", "http://localhost/*"], - "background": { "service_worker": "background.js", "type": "module" }, - "action": { - "default_title": "OpenClaw Browser Relay (click to attach/detach)", - "default_icon": { - "16": "icons/icon16.png", - "32": "icons/icon32.png", - "48": "icons/icon48.png", - "128": "icons/icon128.png" - } - }, - "options_ui": { "page": "options.html", "open_in_tab": true } -} diff --git a/assets/chrome-extension/options-validation.js b/assets/chrome-extension/options-validation.js deleted file mode 100644 index 53e2cd55014..00000000000 --- a/assets/chrome-extension/options-validation.js +++ /dev/null @@ -1,57 +0,0 @@ -const PORT_GUIDANCE = 'Use gateway port + 3 (for gateway 18789, relay is 18792).' - -function hasCdpVersionShape(data) { - return !!data && typeof data === 'object' && 'Browser' in data && 'Protocol-Version' in data -} - -export function classifyRelayCheckResponse(res, port) { - if (!res) { - return { action: 'throw', error: 'No response from service worker' } - } - - if (res.status === 401) { - return { action: 'status', kind: 'error', message: 'Gateway token rejected. Check token and save again.' } - } - - if (res.error) { - return { action: 'throw', error: res.error } - } - - if (!res.ok) { - return { action: 'throw', error: `HTTP ${res.status}` } - } - - const contentType = String(res.contentType || '') - if (!contentType.includes('application/json')) { - return { - action: 'status', - kind: 'error', - message: `Wrong port: this is likely the gateway, not the relay. ${PORT_GUIDANCE}`, - } - } - - if (!hasCdpVersionShape(res.json)) { - return { - action: 'status', - kind: 'error', - message: `Wrong port: expected relay /json/version response. ${PORT_GUIDANCE}`, - } - } - - return { action: 'status', kind: 'ok', message: `Relay reachable and authenticated at http://127.0.0.1:${port}/` } -} - -export function classifyRelayCheckException(err, port) { - const message = String(err || '').toLowerCase() - if (message.includes('json') || message.includes('syntax')) { - return { - kind: 'error', - message: `Wrong port: this is not a relay endpoint. ${PORT_GUIDANCE}`, - } - } - - return { - kind: 'error', - message: `Relay not reachable/authenticated at http://127.0.0.1:${port}/. Start OpenClaw browser relay and verify token.`, - } -} diff --git a/assets/chrome-extension/options.html b/assets/chrome-extension/options.html deleted file mode 100644 index 17fc6a79eed..00000000000 --- a/assets/chrome-extension/options.html +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - OpenClaw Browser Relay - - - -
-
- -
-

OpenClaw Browser Relay

-

Click the toolbar button on a tab to attach / detach.

-
-
- -
-
-

Getting started

-

- If you see a red ! badge on the extension icon, the relay server is not reachable. - Start OpenClaw’s browser relay on this machine (Gateway or node host), then click the toolbar button again. -

-

- Full guide (install, remote Gateway, security): docs.openclaw.ai/tools/chrome-extension -

-
- -
-

Relay connection

- -
- -
- -
- - -
-
- Default port: 18792. Extension connects to: http://127.0.0.1:<port>/. - Gateway token must match gateway.auth.token (or OPENCLAW_GATEWAY_TOKEN). -
-
-
-
- - -
- - diff --git a/assets/chrome-extension/options.js b/assets/chrome-extension/options.js deleted file mode 100644 index aa6fcc4901f..00000000000 --- a/assets/chrome-extension/options.js +++ /dev/null @@ -1,74 +0,0 @@ -import { deriveRelayToken } from './background-utils.js' -import { classifyRelayCheckException, classifyRelayCheckResponse } from './options-validation.js' - -const DEFAULT_PORT = 18792 - -function clampPort(value) { - const n = Number.parseInt(String(value || ''), 10) - if (!Number.isFinite(n)) return DEFAULT_PORT - if (n <= 0 || n > 65535) return DEFAULT_PORT - return n -} - -function updateRelayUrl(port) { - const el = document.getElementById('relay-url') - if (!el) return - el.textContent = `http://127.0.0.1:${port}/` -} - -function setStatus(kind, message) { - const status = document.getElementById('status') - if (!status) return - status.dataset.kind = kind || '' - status.textContent = message || '' -} - -async function checkRelayReachable(port, token) { - const url = `http://127.0.0.1:${port}/json/version` - const trimmedToken = String(token || '').trim() - if (!trimmedToken) { - setStatus('error', 'Gateway token required. Save your gateway token to connect.') - return - } - try { - const relayToken = await deriveRelayToken(trimmedToken, port) - // Delegate the fetch to the background service worker to bypass - // CORS preflight on the custom x-openclaw-relay-token header. - const res = await chrome.runtime.sendMessage({ - type: 'relayCheck', - url, - token: relayToken, - }) - const result = classifyRelayCheckResponse(res, port) - if (result.action === 'throw') throw new Error(result.error) - setStatus(result.kind, result.message) - } catch (err) { - const result = classifyRelayCheckException(err, port) - setStatus(result.kind, result.message) - } -} - -async function load() { - const stored = await chrome.storage.local.get(['relayPort', 'gatewayToken']) - const port = clampPort(stored.relayPort) - const token = String(stored.gatewayToken || '').trim() - document.getElementById('port').value = String(port) - document.getElementById('token').value = token - updateRelayUrl(port) - await checkRelayReachable(port, token) -} - -async function save() { - const portInput = document.getElementById('port') - const tokenInput = document.getElementById('token') - const port = clampPort(portInput.value) - const token = String(tokenInput.value || '').trim() - await chrome.storage.local.set({ relayPort: port, gatewayToken: token }) - portInput.value = String(port) - tokenInput.value = token - updateRelayUrl(port) - await checkRelayReachable(port, token) -} - -document.getElementById('save').addEventListener('click', () => void save()) -void load() diff --git a/docs/.generated/config-baseline.json b/docs/.generated/config-baseline.json index f6f854b2946..c63572a5e7f 100644 --- a/docs/.generated/config-baseline.json +++ b/docs/.generated/config-baseline.json @@ -2956,6 +2956,16 @@ "tags": [], "hasChildren": true }, + { + "path": "agents.defaults.sandbox.backend", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, { "path": "agents.defaults.sandbox.browser", "kind": "core", @@ -3540,6 +3550,234 @@ "tags": [], "hasChildren": false }, + { + "path": "agents.defaults.sandbox.ssh", + "kind": "core", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": true + }, + { + "path": "agents.defaults.sandbox.ssh.certificateData", + "kind": "core", + "type": [ + "object", + "string" + ], + "required": false, + "deprecated": false, + "sensitive": true, + "tags": [ + "security", + "storage" + ], + "hasChildren": true + }, + { + "path": "agents.defaults.sandbox.ssh.certificateData.id", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.certificateData.provider", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.certificateData.source", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.certificateFile", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.command", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.identityData", + "kind": "core", + "type": [ + "object", + "string" + ], + "required": false, + "deprecated": false, + "sensitive": true, + "tags": [ + "security", + "storage" + ], + "hasChildren": true + }, + { + "path": "agents.defaults.sandbox.ssh.identityData.id", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.identityData.provider", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.identityData.source", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.identityFile", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.knownHostsData", + "kind": "core", + "type": [ + "object", + "string" + ], + "required": false, + "deprecated": false, + "sensitive": true, + "tags": [ + "security", + "storage" + ], + "hasChildren": true + }, + { + "path": "agents.defaults.sandbox.ssh.knownHostsData.id", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.knownHostsData.provider", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.knownHostsData.source", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.knownHostsFile", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.strictHostKeyChecking", + "kind": "core", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.target", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.updateHostKeys", + "kind": "core", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.defaults.sandbox.ssh.workspaceRoot", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, { "path": "agents.defaults.sandbox.workspaceAccess", "kind": "core", @@ -5048,6 +5286,16 @@ "tags": [], "hasChildren": true }, + { + "path": "agents.list.*.sandbox.backend", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, { "path": "agents.list.*.sandbox.browser", "kind": "core", @@ -5632,6 +5880,234 @@ "tags": [], "hasChildren": false }, + { + "path": "agents.list.*.sandbox.ssh", + "kind": "core", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": true + }, + { + "path": "agents.list.*.sandbox.ssh.certificateData", + "kind": "core", + "type": [ + "object", + "string" + ], + "required": false, + "deprecated": false, + "sensitive": true, + "tags": [ + "security", + "storage" + ], + "hasChildren": true + }, + { + "path": "agents.list.*.sandbox.ssh.certificateData.id", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.certificateData.provider", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.certificateData.source", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.certificateFile", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.command", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.identityData", + "kind": "core", + "type": [ + "object", + "string" + ], + "required": false, + "deprecated": false, + "sensitive": true, + "tags": [ + "security", + "storage" + ], + "hasChildren": true + }, + { + "path": "agents.list.*.sandbox.ssh.identityData.id", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.identityData.provider", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.identityData.source", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.identityFile", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.knownHostsData", + "kind": "core", + "type": [ + "object", + "string" + ], + "required": false, + "deprecated": false, + "sensitive": true, + "tags": [ + "security", + "storage" + ], + "hasChildren": true + }, + { + "path": "agents.list.*.sandbox.ssh.knownHostsData.id", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.knownHostsData.provider", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.knownHostsData.source", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.knownHostsFile", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.strictHostKeyChecking", + "kind": "core", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.target", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.updateHostKeys", + "kind": "core", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "agents.list.*.sandbox.ssh.workspaceRoot", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, { "path": "agents.list.*.sandbox.workspaceAccess", "kind": "core", @@ -7559,21 +8035,7 @@ "storage" ], "label": "Browser Profile Driver", - "help": "Per-profile browser driver mode: \"openclaw\" (or legacy \"clawd\") or \"extension\" depending on connection/runtime strategy. Use the driver that matches your browser control stack to avoid protocol mismatches.", - "hasChildren": false - }, - { - "path": "browser.relayBindHost", - "kind": "core", - "type": "string", - "required": false, - "deprecated": false, - "sensitive": false, - "tags": [ - "advanced" - ], - "label": "Browser Relay Bind Address", - "help": "Bind IP address for the Chrome extension relay listener. Leave unset for loopback-only access, or set an explicit non-loopback IP such as 0.0.0.0 only when the relay must be reachable across network namespaces (for example WSL2) and the surrounding network is already trusted.", + "help": "Per-profile browser driver mode. Use \"openclaw\" (or legacy \"clawd\") for CDP-based profiles, or use \"existing-session\" for host-local Chrome MCP attachment.", "hasChildren": false }, { @@ -30047,6 +30509,16 @@ "tags": [], "hasChildren": false }, + { + "path": "channels.telegram.accounts.*.actions.editForumTopic", + "kind": "channel", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, { "path": "channels.telegram.accounts.*.actions.editMessage", "kind": "channel", @@ -31930,6 +32402,16 @@ "tags": [], "hasChildren": false }, + { + "path": "channels.telegram.actions.editForumTopic", + "kind": "channel", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, { "path": "channels.telegram.actions.editMessage", "kind": "channel", @@ -39770,6 +40252,22 @@ "help": "Debounce window (ms) before applying config changes.", "hasChildren": false }, + { + "path": "gateway.reload.deferralTimeoutMs", + "kind": "core", + "type": "integer", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "network", + "performance", + "reliability" + ], + "label": "Restart Deferral Timeout (ms)", + "help": "Maximum time (ms) to wait for in-flight operations to complete before forcing a SIGUSR1 restart. Default: 300000 (5 minutes). Lower values risk aborting active subagent LLM calls.", + "hasChildren": false + }, { "path": "gateway.reload.mode", "kind": "core", @@ -44497,6 +44995,144 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.amazon-bedrock", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/amazon-bedrock-provider", + "help": "OpenClaw Amazon Bedrock provider plugin (plugin: amazon-bedrock)", + "hasChildren": true + }, + { + "path": "plugins.entries.amazon-bedrock.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/amazon-bedrock-provider Config", + "help": "Plugin-defined config payload for amazon-bedrock.", + "hasChildren": false + }, + { + "path": "plugins.entries.amazon-bedrock.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/amazon-bedrock-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.amazon-bedrock.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.amazon-bedrock.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.anthropic", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/anthropic-provider", + "help": "OpenClaw Anthropic provider plugin (plugin: anthropic)", + "hasChildren": true + }, + { + "path": "plugins.entries.anthropic.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/anthropic-provider Config", + "help": "Plugin-defined config payload for anthropic.", + "hasChildren": false + }, + { + "path": "plugins.entries.anthropic.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/anthropic-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.anthropic.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.anthropic.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.bluebubbles", "kind": "plugin", @@ -44566,6 +45202,213 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.brave", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/brave-plugin", + "help": "OpenClaw Brave plugin (plugin: brave)", + "hasChildren": true + }, + { + "path": "plugins.entries.brave.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/brave-plugin Config", + "help": "Plugin-defined config payload for brave.", + "hasChildren": false + }, + { + "path": "plugins.entries.brave.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/brave-plugin", + "hasChildren": false + }, + { + "path": "plugins.entries.brave.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.brave.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.byteplus", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/byteplus-provider", + "help": "OpenClaw BytePlus provider plugin (plugin: byteplus)", + "hasChildren": true + }, + { + "path": "plugins.entries.byteplus.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/byteplus-provider Config", + "help": "Plugin-defined config payload for byteplus.", + "hasChildren": false + }, + { + "path": "plugins.entries.byteplus.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/byteplus-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.byteplus.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.byteplus.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.cloudflare-ai-gateway", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/cloudflare-ai-gateway-provider", + "help": "OpenClaw Cloudflare AI Gateway provider plugin (plugin: cloudflare-ai-gateway)", + "hasChildren": true + }, + { + "path": "plugins.entries.cloudflare-ai-gateway.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/cloudflare-ai-gateway-provider Config", + "help": "Plugin-defined config payload for cloudflare-ai-gateway.", + "hasChildren": false + }, + { + "path": "plugins.entries.cloudflare-ai-gateway.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/cloudflare-ai-gateway-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.cloudflare-ai-gateway.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.cloudflare-ai-gateway.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.copilot-proxy", "kind": "plugin", @@ -45332,7 +46175,7 @@ "hasChildren": false }, { - "path": "plugins.entries.google-gemini-cli-auth", + "path": "plugins.entries.firecrawl", "kind": "plugin", "type": "object", "required": false, @@ -45341,12 +46184,12 @@ "tags": [ "advanced" ], - "label": "@openclaw/google-gemini-cli-auth", - "help": "OpenClaw Gemini CLI OAuth provider plugin (plugin: google-gemini-cli-auth)", + "label": "@openclaw/firecrawl-plugin", + "help": "OpenClaw Firecrawl plugin (plugin: firecrawl)", "hasChildren": true }, { - "path": "plugins.entries.google-gemini-cli-auth.config", + "path": "plugins.entries.firecrawl.config", "kind": "plugin", "type": "object", "required": false, @@ -45355,12 +46198,12 @@ "tags": [ "advanced" ], - "label": "@openclaw/google-gemini-cli-auth Config", - "help": "Plugin-defined config payload for google-gemini-cli-auth.", + "label": "@openclaw/firecrawl-plugin Config", + "help": "Plugin-defined config payload for firecrawl.", "hasChildren": false }, { - "path": "plugins.entries.google-gemini-cli-auth.enabled", + "path": "plugins.entries.firecrawl.enabled", "kind": "plugin", "type": "boolean", "required": false, @@ -45369,11 +46212,11 @@ "tags": [ "advanced" ], - "label": "Enable @openclaw/google-gemini-cli-auth", + "label": "Enable @openclaw/firecrawl-plugin", "hasChildren": false }, { - "path": "plugins.entries.google-gemini-cli-auth.hooks", + "path": "plugins.entries.firecrawl.hooks", "kind": "plugin", "type": "object", "required": false, @@ -45387,7 +46230,145 @@ "hasChildren": true }, { - "path": "plugins.entries.google-gemini-cli-auth.hooks.allowPromptInjection", + "path": "plugins.entries.firecrawl.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.github-copilot", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/github-copilot-provider", + "help": "OpenClaw GitHub Copilot provider plugin (plugin: github-copilot)", + "hasChildren": true + }, + { + "path": "plugins.entries.github-copilot.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/github-copilot-provider Config", + "help": "Plugin-defined config payload for github-copilot.", + "hasChildren": false + }, + { + "path": "plugins.entries.github-copilot.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/github-copilot-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.github-copilot.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.github-copilot.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.google", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/google-plugin", + "help": "OpenClaw Google plugin (plugin: google)", + "hasChildren": true + }, + { + "path": "plugins.entries.google.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/google-plugin Config", + "help": "Plugin-defined config payload for google.", + "hasChildren": false + }, + { + "path": "plugins.entries.google.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/google-plugin", + "hasChildren": false + }, + { + "path": "plugins.entries.google.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.google.hooks.allowPromptInjection", "kind": "plugin", "type": "boolean", "required": false, @@ -45469,6 +46450,75 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.huggingface", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/huggingface-provider", + "help": "OpenClaw Hugging Face provider plugin (plugin: huggingface)", + "hasChildren": true + }, + { + "path": "plugins.entries.huggingface.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/huggingface-provider Config", + "help": "Plugin-defined config payload for huggingface.", + "hasChildren": false + }, + { + "path": "plugins.entries.huggingface.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/huggingface-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.huggingface.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.huggingface.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.imessage", "kind": "plugin", @@ -45607,6 +46657,144 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.kilocode", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/kilocode-provider", + "help": "OpenClaw Kilo Gateway provider plugin (plugin: kilocode)", + "hasChildren": true + }, + { + "path": "plugins.entries.kilocode.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/kilocode-provider Config", + "help": "Plugin-defined config payload for kilocode.", + "hasChildren": false + }, + { + "path": "plugins.entries.kilocode.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/kilocode-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.kilocode.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.kilocode.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.kimi-coding", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/kimi-coding-provider", + "help": "OpenClaw Kimi Coding provider plugin (plugin: kimi-coding)", + "hasChildren": true + }, + { + "path": "plugins.entries.kimi-coding.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/kimi-coding-provider Config", + "help": "Plugin-defined config payload for kimi-coding.", + "hasChildren": false + }, + { + "path": "plugins.entries.kimi-coding.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/kimi-coding-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.kimi-coding.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.kimi-coding.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.line", "kind": "plugin", @@ -46290,7 +47478,7 @@ "hasChildren": false }, { - "path": "plugins.entries.minimax-portal-auth", + "path": "plugins.entries.minimax", "kind": "plugin", "type": "object", "required": false, @@ -46299,12 +47487,12 @@ "tags": [ "performance" ], - "label": "@openclaw/minimax-portal-auth", - "help": "OpenClaw MiniMax Portal OAuth provider plugin (plugin: minimax-portal-auth)", + "label": "@openclaw/minimax-provider", + "help": "OpenClaw MiniMax provider and OAuth plugin (plugin: minimax)", "hasChildren": true }, { - "path": "plugins.entries.minimax-portal-auth.config", + "path": "plugins.entries.minimax.config", "kind": "plugin", "type": "object", "required": false, @@ -46313,12 +47501,12 @@ "tags": [ "performance" ], - "label": "@openclaw/minimax-portal-auth Config", - "help": "Plugin-defined config payload for minimax-portal-auth.", + "label": "@openclaw/minimax-provider Config", + "help": "Plugin-defined config payload for minimax.", "hasChildren": false }, { - "path": "plugins.entries.minimax-portal-auth.enabled", + "path": "plugins.entries.minimax.enabled", "kind": "plugin", "type": "boolean", "required": false, @@ -46327,11 +47515,11 @@ "tags": [ "performance" ], - "label": "Enable @openclaw/minimax-portal-auth", + "label": "Enable @openclaw/minimax-provider", "hasChildren": false }, { - "path": "plugins.entries.minimax-portal-auth.hooks", + "path": "plugins.entries.minimax.hooks", "kind": "plugin", "type": "object", "required": false, @@ -46345,7 +47533,214 @@ "hasChildren": true }, { - "path": "plugins.entries.minimax-portal-auth.hooks.allowPromptInjection", + "path": "plugins.entries.minimax.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.mistral", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/mistral-provider", + "help": "OpenClaw Mistral provider plugin (plugin: mistral)", + "hasChildren": true + }, + { + "path": "plugins.entries.mistral.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/mistral-provider Config", + "help": "Plugin-defined config payload for mistral.", + "hasChildren": false + }, + { + "path": "plugins.entries.mistral.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/mistral-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.mistral.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.mistral.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.modelstudio", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/modelstudio-provider", + "help": "OpenClaw Model Studio provider plugin (plugin: modelstudio)", + "hasChildren": true + }, + { + "path": "plugins.entries.modelstudio.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/modelstudio-provider Config", + "help": "Plugin-defined config payload for modelstudio.", + "hasChildren": false + }, + { + "path": "plugins.entries.modelstudio.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/modelstudio-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.modelstudio.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.modelstudio.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.moonshot", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/moonshot-provider", + "help": "OpenClaw Moonshot provider plugin (plugin: moonshot)", + "hasChildren": true + }, + { + "path": "plugins.entries.moonshot.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/moonshot-provider Config", + "help": "Plugin-defined config payload for moonshot.", + "hasChildren": false + }, + { + "path": "plugins.entries.moonshot.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/moonshot-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.moonshot.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.moonshot.hooks.allowPromptInjection", "kind": "plugin", "type": "boolean", "required": false, @@ -46565,6 +47960,75 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.nvidia", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/nvidia-provider", + "help": "OpenClaw NVIDIA provider plugin (plugin: nvidia)", + "hasChildren": true + }, + { + "path": "plugins.entries.nvidia.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/nvidia-provider Config", + "help": "Plugin-defined config payload for nvidia.", + "hasChildren": false + }, + { + "path": "plugins.entries.nvidia.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/nvidia-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.nvidia.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.nvidia.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.ollama", "kind": "plugin", @@ -46703,6 +48167,587 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.openai", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/openai-provider", + "help": "OpenClaw OpenAI provider plugins (plugin: openai)", + "hasChildren": true + }, + { + "path": "plugins.entries.openai.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/openai-provider Config", + "help": "Plugin-defined config payload for openai.", + "hasChildren": false + }, + { + "path": "plugins.entries.openai.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/openai-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.openai.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.openai.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.opencode", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/opencode-provider", + "help": "OpenClaw OpenCode Zen provider plugin (plugin: opencode)", + "hasChildren": true + }, + { + "path": "plugins.entries.opencode-go", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/opencode-go-provider", + "help": "OpenClaw OpenCode Go provider plugin (plugin: opencode-go)", + "hasChildren": true + }, + { + "path": "plugins.entries.opencode-go.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/opencode-go-provider Config", + "help": "Plugin-defined config payload for opencode-go.", + "hasChildren": false + }, + { + "path": "plugins.entries.opencode-go.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/opencode-go-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.opencode-go.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.opencode-go.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.opencode.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/opencode-provider Config", + "help": "Plugin-defined config payload for opencode.", + "hasChildren": false + }, + { + "path": "plugins.entries.opencode.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/opencode-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.opencode.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.opencode.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.openrouter", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/openrouter-provider", + "help": "OpenClaw OpenRouter provider plugin (plugin: openrouter)", + "hasChildren": true + }, + { + "path": "plugins.entries.openrouter.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/openrouter-provider Config", + "help": "Plugin-defined config payload for openrouter.", + "hasChildren": false + }, + { + "path": "plugins.entries.openrouter.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/openrouter-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.openrouter.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.openrouter.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "OpenShell Sandbox", + "help": "Sandbox backend powered by OpenShell with mirrored local workspaces and SSH-based command execution. (plugin: openshell)", + "hasChildren": true + }, + { + "path": "plugins.entries.openshell.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "OpenShell Sandbox Config", + "help": "Plugin-defined config payload for openshell.", + "hasChildren": true + }, + { + "path": "plugins.entries.openshell.config.autoProviders", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Auto-create Providers", + "help": "When enabled, pass --auto-providers during sandbox create.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.command", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "OpenShell Command", + "help": "Path or command name for the openshell CLI.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.from", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Sandbox Source", + "help": "OpenShell sandbox source for first-time create. Defaults to openclaw.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.gateway", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Gateway Name", + "help": "Optional OpenShell gateway name passed as --gateway.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.gatewayEndpoint", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Gateway Endpoint", + "help": "Optional OpenShell gateway endpoint passed as --gateway-endpoint.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.gpu", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "GPU", + "help": "Request GPU resources when creating the sandbox.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.policy", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Policy File", + "help": "Optional path to a custom OpenShell sandbox policy YAML.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.providers", + "kind": "plugin", + "type": "array", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Providers", + "help": "Provider names to attach when a sandbox is created.", + "hasChildren": true + }, + { + "path": "plugins.entries.openshell.config.providers.*", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.remoteAgentWorkspaceDir", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced", + "storage" + ], + "label": "Remote Agent Dir", + "help": "Mirror path for the real agent workspace when workspaceAccess is read-only.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.remoteWorkspaceDir", + "kind": "plugin", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced", + "storage" + ], + "label": "Remote Workspace Dir", + "help": "Primary writable workspace inside the OpenShell sandbox.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.config.timeoutSeconds", + "kind": "plugin", + "type": "number", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced", + "performance" + ], + "label": "Command Timeout Seconds", + "help": "Timeout for openshell CLI operations such as create/upload/download.", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable OpenShell Sandbox", + "hasChildren": false + }, + { + "path": "plugins.entries.openshell.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.openshell.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.perplexity", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/perplexity-plugin", + "help": "OpenClaw Perplexity plugin (plugin: perplexity)", + "hasChildren": true + }, + { + "path": "plugins.entries.perplexity.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/perplexity-plugin Config", + "help": "Plugin-defined config payload for perplexity.", + "hasChildren": false + }, + { + "path": "plugins.entries.perplexity.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/perplexity-plugin", + "hasChildren": false + }, + { + "path": "plugins.entries.perplexity.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.perplexity.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.phone-control", "kind": "plugin", @@ -46772,6 +48817,75 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.qianfan", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/qianfan-provider", + "help": "OpenClaw Qianfan provider plugin (plugin: qianfan)", + "hasChildren": true + }, + { + "path": "plugins.entries.qianfan.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/qianfan-provider Config", + "help": "Plugin-defined config payload for qianfan.", + "hasChildren": false + }, + { + "path": "plugins.entries.qianfan.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/qianfan-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.qianfan.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.qianfan.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.qwen-portal-auth", "kind": "plugin", @@ -47117,6 +49231,75 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.synthetic", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/synthetic-provider", + "help": "OpenClaw Synthetic provider plugin (plugin: synthetic)", + "hasChildren": true + }, + { + "path": "plugins.entries.synthetic.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/synthetic-provider Config", + "help": "Plugin-defined config payload for synthetic.", + "hasChildren": false + }, + { + "path": "plugins.entries.synthetic.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/synthetic-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.synthetic.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.synthetic.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.talk-voice", "kind": "plugin", @@ -47431,6 +49614,75 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.together", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/together-provider", + "help": "OpenClaw Together provider plugin (plugin: together)", + "hasChildren": true + }, + { + "path": "plugins.entries.together.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/together-provider Config", + "help": "Plugin-defined config payload for together.", + "hasChildren": false + }, + { + "path": "plugins.entries.together.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/together-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.together.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.together.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.twitch", "kind": "plugin", @@ -47500,6 +49752,144 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.venice", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/venice-provider", + "help": "OpenClaw Venice provider plugin (plugin: venice)", + "hasChildren": true + }, + { + "path": "plugins.entries.venice.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/venice-provider Config", + "help": "Plugin-defined config payload for venice.", + "hasChildren": false + }, + { + "path": "plugins.entries.venice.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/venice-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.venice.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.venice.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.vercel-ai-gateway", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/vercel-ai-gateway-provider", + "help": "OpenClaw Vercel AI Gateway provider plugin (plugin: vercel-ai-gateway)", + "hasChildren": true + }, + { + "path": "plugins.entries.vercel-ai-gateway.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/vercel-ai-gateway-provider Config", + "help": "Plugin-defined config payload for vercel-ai-gateway.", + "hasChildren": false + }, + { + "path": "plugins.entries.vercel-ai-gateway.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/vercel-ai-gateway-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.vercel-ai-gateway.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.vercel-ai-gateway.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.vllm", "kind": "plugin", @@ -48999,6 +51389,75 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.volcengine", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/volcengine-provider", + "help": "OpenClaw Volcengine provider plugin (plugin: volcengine)", + "hasChildren": true + }, + { + "path": "plugins.entries.volcengine.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/volcengine-provider Config", + "help": "Plugin-defined config payload for volcengine.", + "hasChildren": false + }, + { + "path": "plugins.entries.volcengine.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/volcengine-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.volcengine.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.volcengine.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.whatsapp", "kind": "plugin", @@ -49068,6 +51527,213 @@ "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", "hasChildren": false }, + { + "path": "plugins.entries.xai", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/xai-plugin", + "help": "OpenClaw xAI plugin (plugin: xai)", + "hasChildren": true + }, + { + "path": "plugins.entries.xai.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/xai-plugin Config", + "help": "Plugin-defined config payload for xai.", + "hasChildren": false + }, + { + "path": "plugins.entries.xai.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/xai-plugin", + "hasChildren": false + }, + { + "path": "plugins.entries.xai.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.xai.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.xiaomi", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/xiaomi-provider", + "help": "OpenClaw Xiaomi provider plugin (plugin: xiaomi)", + "hasChildren": true + }, + { + "path": "plugins.entries.xiaomi.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/xiaomi-provider Config", + "help": "Plugin-defined config payload for xiaomi.", + "hasChildren": false + }, + { + "path": "plugins.entries.xiaomi.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/xiaomi-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.xiaomi.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.xiaomi.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, + { + "path": "plugins.entries.zai", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/zai-provider", + "help": "OpenClaw Z.AI provider plugin (plugin: zai)", + "hasChildren": true + }, + { + "path": "plugins.entries.zai.config", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "@openclaw/zai-provider Config", + "help": "Plugin-defined config payload for zai.", + "hasChildren": false + }, + { + "path": "plugins.entries.zai.enabled", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Enable @openclaw/zai-provider", + "hasChildren": false + }, + { + "path": "plugins.entries.zai.hooks", + "kind": "plugin", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Hook Policy", + "help": "Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.", + "hasChildren": true + }, + { + "path": "plugins.entries.zai.hooks.allowPromptInjection", + "kind": "plugin", + "type": "boolean", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "access" + ], + "label": "Allow Prompt Injection Hooks", + "help": "Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.", + "hasChildren": false + }, { "path": "plugins.entries.zalo", "kind": "plugin", @@ -49272,6 +51938,48 @@ "help": "Resolved npm dist integrity hash for the fetched artifact (if reported by npm).", "hasChildren": false }, + { + "path": "plugins.installs.*.marketplaceName", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Marketplace Name", + "help": "Marketplace display name recorded for marketplace-backed plugin installs (if available).", + "hasChildren": false + }, + { + "path": "plugins.installs.*.marketplacePlugin", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Marketplace Plugin", + "help": "Plugin entry name inside the source marketplace, used for later updates.", + "hasChildren": false + }, + { + "path": "plugins.installs.*.marketplaceSource", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "advanced" + ], + "label": "Plugin Marketplace Source", + "help": "Original marketplace source used to resolve the install (for example a repo path or Git URL).", + "hasChildren": false + }, { "path": "plugins.installs.*.resolvedAt", "kind": "core", @@ -55443,6 +58151,79 @@ "help": "Enable the web_search tool (requires a provider API key).", "hasChildren": false }, + { + "path": "tools.web.search.firecrawl", + "kind": "core", + "type": "object", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": true + }, + { + "path": "tools.web.search.firecrawl.apiKey", + "kind": "core", + "type": [ + "object", + "string" + ], + "required": false, + "deprecated": false, + "sensitive": true, + "tags": [ + "auth", + "security", + "tools" + ], + "label": "Firecrawl Search API Key", + "help": "Firecrawl API key for web search (fallback: FIRECRAWL_API_KEY env var).", + "hasChildren": true + }, + { + "path": "tools.web.search.firecrawl.apiKey.id", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "tools.web.search.firecrawl.apiKey.provider", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "tools.web.search.firecrawl.apiKey.source", + "kind": "core", + "type": "string", + "required": true, + "deprecated": false, + "sensitive": false, + "tags": [], + "hasChildren": false + }, + { + "path": "tools.web.search.firecrawl.baseUrl", + "kind": "core", + "type": "string", + "required": false, + "deprecated": false, + "sensitive": false, + "tags": [ + "tools" + ], + "label": "Firecrawl Search Base URL", + "help": "Firecrawl Search base URL override (default: \"https://api.firecrawl.dev\").", + "hasChildren": false + }, { "path": "tools.web.search.gemini", "kind": "core", @@ -55803,7 +58584,7 @@ "tools" ], "label": "Web Search Provider", - "help": "Search provider (\"brave\", \"gemini\", \"grok\", \"kimi\", or \"perplexity\"). Auto-detected from available API keys if omitted.", + "help": "Search provider (\"brave\", \"firecrawl\", \"gemini\", \"grok\", \"kimi\", or \"perplexity\"). Auto-detected from available API keys if omitted.", "hasChildren": false }, { @@ -56136,7 +58917,7 @@ "advanced" ], "label": "Setup Wizard State", - "help": "Setup wizard state tracking fields that record the most recent guided onboarding run details. Keep these fields for observability and troubleshooting of setup flows across upgrades.", + "help": "Setup wizard state tracking fields that record the most recent guided setup run details. Keep these fields for observability and troubleshooting of setup flows across upgrades.", "hasChildren": true }, { @@ -56150,7 +58931,7 @@ "advanced" ], "label": "Wizard Last Run Timestamp", - "help": "ISO timestamp for when the setup wizard most recently completed on this host. Use this to confirm onboarding recency during support and operational audits.", + "help": "ISO timestamp for when the setup wizard most recently completed on this host. Use this to confirm setup recency during support and operational audits.", "hasChildren": false }, { @@ -56164,7 +58945,7 @@ "advanced" ], "label": "Wizard Last Run Command", - "help": "Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce onboarding steps when verifying setup regressions.", + "help": "Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce setup steps when verifying setup regressions.", "hasChildren": false }, { @@ -56178,7 +58959,7 @@ "advanced" ], "label": "Wizard Last Run Commit", - "help": "Source commit identifier recorded for the last wizard execution in development builds. Use this to correlate onboarding behavior with exact source state during debugging.", + "help": "Source commit identifier recorded for the last wizard execution in development builds. Use this to correlate setup behavior with exact source state during debugging.", "hasChildren": false }, { @@ -56192,7 +58973,7 @@ "advanced" ], "label": "Wizard Last Run Mode", - "help": "Wizard execution mode recorded as \"local\" or \"remote\" for the most recent onboarding flow. Use this to understand whether setup targeted direct local runtime or remote gateway topology.", + "help": "Wizard execution mode recorded as \"local\" or \"remote\" for the most recent setup flow. Use this to understand whether setup targeted direct local runtime or remote gateway topology.", "hasChildren": false }, { @@ -56206,7 +58987,7 @@ "advanced" ], "label": "Wizard Last Run Version", - "help": "OpenClaw version recorded at the time of the most recent wizard run on this config. Use this when diagnosing behavior differences across version-to-version onboarding changes.", + "help": "OpenClaw version recorded at the time of the most recent wizard run on this config. Use this when diagnosing behavior differences across version-to-version setup changes.", "hasChildren": false } ] diff --git a/docs/.generated/config-baseline.jsonl b/docs/.generated/config-baseline.jsonl index 18baeac12b9..f857ff2d7f4 100644 --- a/docs/.generated/config-baseline.jsonl +++ b/docs/.generated/config-baseline.jsonl @@ -1,4 +1,4 @@ -{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":4889} +{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5101} {"recordType":"path","path":"acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACP","help":"ACP runtime controls for enabling dispatch, selecting backends, constraining allowed agent targets, and tuning streamed turn projection behavior.","hasChildren":true} {"recordType":"path","path":"acp.allowedAgents","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"ACP Allowed Agents","help":"Allowlist of ACP target agent ids permitted for ACP runtime sessions. Empty means no additional allowlist restriction.","hasChildren":true} {"recordType":"path","path":"acp.allowedAgents.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -245,6 +245,7 @@ {"recordType":"path","path":"agents.defaults.pdfModel.primary","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"PDF Model","help":"Optional PDF model (provider/model) for the PDF analysis tool. Defaults to imageModel, then session model.","hasChildren":false} {"recordType":"path","path":"agents.defaults.repoRoot","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Repo Root","help":"Optional repository root shown in the system prompt runtime line (overrides auto-detect).","hasChildren":false} {"recordType":"path","path":"agents.defaults.sandbox","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} +{"recordType":"path","path":"agents.defaults.sandbox.backend","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.defaults.sandbox.browser","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} {"recordType":"path","path":"agents.defaults.sandbox.browser.allowHostControl","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.defaults.sandbox.browser.autoStart","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -301,6 +302,27 @@ {"recordType":"path","path":"agents.defaults.sandbox.prune.maxAgeDays","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.defaults.sandbox.scope","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.defaults.sandbox.sessionToolsVisibility","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.certificateData","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["security","storage"],"hasChildren":true} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.certificateData.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.certificateData.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.certificateData.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.certificateFile","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.command","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.identityData","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["security","storage"],"hasChildren":true} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.identityData.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.identityData.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.identityData.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.identityFile","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.knownHostsData","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["security","storage"],"hasChildren":true} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.knownHostsData.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.knownHostsData.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.knownHostsData.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.knownHostsFile","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.strictHostKeyChecking","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.target","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.updateHostKeys","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.defaults.sandbox.ssh.workspaceRoot","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.defaults.sandbox.workspaceAccess","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.defaults.sandbox.workspaceRoot","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.defaults.skipBootstrap","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -445,6 +467,7 @@ {"recordType":"path","path":"agents.list.*.runtime.acp.mode","kind":"core","type":"string","required":false,"enumValues":["persistent","oneshot"],"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent ACP Mode","help":"Optional ACP session mode default for this agent (persistent or oneshot).","hasChildren":false} {"recordType":"path","path":"agents.list.*.runtime.type","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent Runtime Type","help":"Runtime type for this agent: \"embedded\" (default OpenClaw runtime) or \"acp\" (ACP harness defaults).","hasChildren":false} {"recordType":"path","path":"agents.list.*.sandbox","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} +{"recordType":"path","path":"agents.list.*.sandbox.backend","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.list.*.sandbox.browser","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} {"recordType":"path","path":"agents.list.*.sandbox.browser.allowHostControl","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.list.*.sandbox.browser.autoStart","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -501,6 +524,27 @@ {"recordType":"path","path":"agents.list.*.sandbox.prune.maxAgeDays","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.list.*.sandbox.scope","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.list.*.sandbox.sessionToolsVisibility","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.certificateData","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["security","storage"],"hasChildren":true} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.certificateData.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.certificateData.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.certificateData.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.certificateFile","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.command","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.identityData","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["security","storage"],"hasChildren":true} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.identityData.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.identityData.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.identityData.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.identityFile","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.knownHostsData","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["security","storage"],"hasChildren":true} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.knownHostsData.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.knownHostsData.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.knownHostsData.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.knownHostsFile","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.strictHostKeyChecking","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.target","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.updateHostKeys","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"agents.list.*.sandbox.ssh.workspaceRoot","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.list.*.sandbox.workspaceAccess","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.list.*.sandbox.workspaceRoot","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"agents.list.*.skills","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent Skill Filter","help":"Optional allowlist of skills for this agent (omit = all skills; empty = no skills).","hasChildren":true} @@ -663,8 +707,7 @@ {"recordType":"path","path":"browser.profiles.*.cdpPort","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile CDP Port","help":"Per-profile local CDP port used when connecting to browser instances by port instead of URL. Use unique ports per profile to avoid connection collisions.","hasChildren":false} {"recordType":"path","path":"browser.profiles.*.cdpUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile CDP URL","help":"Per-profile CDP websocket URL used for explicit remote browser routing by profile name. Use this when profile connections terminate on remote hosts or tunnels.","hasChildren":false} {"recordType":"path","path":"browser.profiles.*.color","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile Accent Color","help":"Per-profile accent color for visual differentiation in dashboards and browser-related UI hints. Use distinct colors for high-signal operator recognition of active profiles.","hasChildren":false} -{"recordType":"path","path":"browser.profiles.*.driver","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile Driver","help":"Per-profile browser driver mode: \"openclaw\" (or legacy \"clawd\") or \"extension\" depending on connection/runtime strategy. Use the driver that matches your browser control stack to avoid protocol mismatches.","hasChildren":false} -{"recordType":"path","path":"browser.relayBindHost","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Browser Relay Bind Address","help":"Bind IP address for the Chrome extension relay listener. Leave unset for loopback-only access, or set an explicit non-loopback IP such as 0.0.0.0 only when the relay must be reachable across network namespaces (for example WSL2) and the surrounding network is already trusted.","hasChildren":false} +{"recordType":"path","path":"browser.profiles.*.driver","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Browser Profile Driver","help":"Per-profile browser driver mode. Use \"openclaw\" (or legacy \"clawd\") for CDP-based profiles, or use \"existing-session\" for host-local Chrome MCP attachment.","hasChildren":false} {"recordType":"path","path":"browser.remoteCdpHandshakeTimeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Remote CDP Handshake Timeout (ms)","help":"Timeout in milliseconds for post-connect CDP handshake readiness checks against remote browser targets. Raise this for slow-start remote browsers and lower to fail fast in automation loops.","hasChildren":false} {"recordType":"path","path":"browser.remoteCdpTimeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Remote CDP Timeout (ms)","help":"Timeout in milliseconds for connecting to a remote CDP endpoint before failing the browser attach attempt. Increase for high-latency tunnels, or lower for faster failure detection.","hasChildren":false} {"recordType":"path","path":"browser.snapshotDefaults","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Browser Snapshot Defaults","help":"Default snapshot capture configuration used when callers do not provide explicit snapshot options. Tune this for consistent capture behavior across channels and automation paths.","hasChildren":true} @@ -2708,6 +2751,7 @@ {"recordType":"path","path":"channels.telegram.accounts.*.actions","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} {"recordType":"path","path":"channels.telegram.accounts.*.actions.createForumTopic","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"channels.telegram.accounts.*.actions.deleteMessage","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"channels.telegram.accounts.*.actions.editForumTopic","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"channels.telegram.accounts.*.actions.editMessage","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"channels.telegram.accounts.*.actions.poll","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"channels.telegram.accounts.*.actions.reactions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -2883,6 +2927,7 @@ {"recordType":"path","path":"channels.telegram.actions","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} {"recordType":"path","path":"channels.telegram.actions.createForumTopic","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"channels.telegram.actions.deleteMessage","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"channels.telegram.actions.editForumTopic","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"channels.telegram.actions.editMessage","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"channels.telegram.actions.poll","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"channels.telegram.actions.reactions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -3558,6 +3603,7 @@ {"recordType":"path","path":"gateway.push.apns.relay.timeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance"],"label":"Gateway APNs Relay Timeout (ms)","help":"Timeout in milliseconds for relay send requests from the gateway to the APNs relay (default: 10000). Increase for slower relays or networks, or lower to fail wake attempts faster.","hasChildren":false} {"recordType":"path","path":"gateway.reload","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network","reliability"],"label":"Config Reload","help":"Live config-reload policy for how edits are applied and when full restarts are triggered. Keep hybrid behavior for safest operational updates unless debugging reload internals.","hasChildren":true} {"recordType":"path","path":"gateway.reload.debounceMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance","reliability"],"label":"Config Reload Debounce (ms)","help":"Debounce window (ms) before applying config changes.","hasChildren":false} +{"recordType":"path","path":"gateway.reload.deferralTimeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance","reliability"],"label":"Restart Deferral Timeout (ms)","help":"Maximum time (ms) to wait for in-flight operations to complete before forcing a SIGUSR1 restart. Default: 300000 (5 minutes). Lower values risk aborting active subagent LLM calls.","hasChildren":false} {"recordType":"path","path":"gateway.reload.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["network","reliability"],"label":"Config Reload Mode","help":"Controls how config edits are applied: \"off\" ignores live edits, \"restart\" always restarts, \"hot\" applies in-process, and \"hybrid\" tries hot then restarts if required. Keep \"hybrid\" for safest routine updates.","hasChildren":false} {"recordType":"path","path":"gateway.remote","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Remote Gateway","help":"Remote gateway connection settings for direct or SSH transport when this instance proxies to another runtime host. Use remote mode only when split-host operation is intentionally configured.","hasChildren":true} {"recordType":"path","path":"gateway.remote.password","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","network","security"],"label":"Remote Gateway Password","help":"Password credential used for remote gateway authentication when password mode is enabled. Keep this secret managed externally and avoid plaintext values in committed config.","hasChildren":true} @@ -3940,11 +3986,36 @@ {"recordType":"path","path":"plugins.entries.acpx.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable ACPX Runtime","hasChildren":false} {"recordType":"path","path":"plugins.entries.acpx.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.acpx.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.amazon-bedrock","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/amazon-bedrock-provider","help":"OpenClaw Amazon Bedrock provider plugin (plugin: amazon-bedrock)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.amazon-bedrock.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/amazon-bedrock-provider Config","help":"Plugin-defined config payload for amazon-bedrock.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.amazon-bedrock.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/amazon-bedrock-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.amazon-bedrock.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.amazon-bedrock.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.anthropic","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/anthropic-provider","help":"OpenClaw Anthropic provider plugin (plugin: anthropic)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.anthropic.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/anthropic-provider Config","help":"Plugin-defined config payload for anthropic.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.anthropic.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/anthropic-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.anthropic.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.anthropic.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.bluebubbles","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/bluebubbles","help":"OpenClaw BlueBubbles channel plugin (plugin: bluebubbles)","hasChildren":true} {"recordType":"path","path":"plugins.entries.bluebubbles.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/bluebubbles Config","help":"Plugin-defined config payload for bluebubbles.","hasChildren":false} {"recordType":"path","path":"plugins.entries.bluebubbles.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/bluebubbles","hasChildren":false} {"recordType":"path","path":"plugins.entries.bluebubbles.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.bluebubbles.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.brave","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/brave-plugin","help":"OpenClaw Brave plugin (plugin: brave)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.brave.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/brave-plugin Config","help":"Plugin-defined config payload for brave.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.brave.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/brave-plugin","hasChildren":false} +{"recordType":"path","path":"plugins.entries.brave.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.brave.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.byteplus","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/byteplus-provider","help":"OpenClaw BytePlus provider plugin (plugin: byteplus)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.byteplus.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/byteplus-provider Config","help":"Plugin-defined config payload for byteplus.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.byteplus.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/byteplus-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.byteplus.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.byteplus.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.cloudflare-ai-gateway","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/cloudflare-ai-gateway-provider","help":"OpenClaw Cloudflare AI Gateway provider plugin (plugin: cloudflare-ai-gateway)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.cloudflare-ai-gateway.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/cloudflare-ai-gateway-provider Config","help":"Plugin-defined config payload for cloudflare-ai-gateway.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.cloudflare-ai-gateway.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/cloudflare-ai-gateway-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.cloudflare-ai-gateway.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.cloudflare-ai-gateway.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.copilot-proxy","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/copilot-proxy","help":"OpenClaw Copilot Proxy provider plugin (plugin: copilot-proxy)","hasChildren":true} {"recordType":"path","path":"plugins.entries.copilot-proxy.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/copilot-proxy Config","help":"Plugin-defined config payload for copilot-proxy.","hasChildren":false} {"recordType":"path","path":"plugins.entries.copilot-proxy.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/copilot-proxy","hasChildren":false} @@ -3998,16 +4069,31 @@ {"recordType":"path","path":"plugins.entries.feishu.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/feishu","hasChildren":false} {"recordType":"path","path":"plugins.entries.feishu.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.feishu.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} -{"recordType":"path","path":"plugins.entries.google-gemini-cli-auth","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/google-gemini-cli-auth","help":"OpenClaw Gemini CLI OAuth provider plugin (plugin: google-gemini-cli-auth)","hasChildren":true} -{"recordType":"path","path":"plugins.entries.google-gemini-cli-auth.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/google-gemini-cli-auth Config","help":"Plugin-defined config payload for google-gemini-cli-auth.","hasChildren":false} -{"recordType":"path","path":"plugins.entries.google-gemini-cli-auth.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/google-gemini-cli-auth","hasChildren":false} -{"recordType":"path","path":"plugins.entries.google-gemini-cli-auth.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} -{"recordType":"path","path":"plugins.entries.google-gemini-cli-auth.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.firecrawl","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/firecrawl-plugin","help":"OpenClaw Firecrawl plugin (plugin: firecrawl)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.firecrawl.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/firecrawl-plugin Config","help":"Plugin-defined config payload for firecrawl.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.firecrawl.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/firecrawl-plugin","hasChildren":false} +{"recordType":"path","path":"plugins.entries.firecrawl.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.firecrawl.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.github-copilot","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/github-copilot-provider","help":"OpenClaw GitHub Copilot provider plugin (plugin: github-copilot)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.github-copilot.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/github-copilot-provider Config","help":"Plugin-defined config payload for github-copilot.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.github-copilot.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/github-copilot-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.github-copilot.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.github-copilot.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.google","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/google-plugin","help":"OpenClaw Google plugin (plugin: google)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.google.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/google-plugin Config","help":"Plugin-defined config payload for google.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.google.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/google-plugin","hasChildren":false} +{"recordType":"path","path":"plugins.entries.google.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.google.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.googlechat","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/googlechat","help":"OpenClaw Google Chat channel plugin (plugin: googlechat)","hasChildren":true} {"recordType":"path","path":"plugins.entries.googlechat.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/googlechat Config","help":"Plugin-defined config payload for googlechat.","hasChildren":false} {"recordType":"path","path":"plugins.entries.googlechat.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/googlechat","hasChildren":false} {"recordType":"path","path":"plugins.entries.googlechat.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.googlechat.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.huggingface","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/huggingface-provider","help":"OpenClaw Hugging Face provider plugin (plugin: huggingface)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.huggingface.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/huggingface-provider Config","help":"Plugin-defined config payload for huggingface.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.huggingface.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/huggingface-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.huggingface.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.huggingface.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.imessage","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/imessage","help":"OpenClaw iMessage channel plugin (plugin: imessage)","hasChildren":true} {"recordType":"path","path":"plugins.entries.imessage.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/imessage Config","help":"Plugin-defined config payload for imessage.","hasChildren":false} {"recordType":"path","path":"plugins.entries.imessage.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/imessage","hasChildren":false} @@ -4018,6 +4104,16 @@ {"recordType":"path","path":"plugins.entries.irc.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/irc","hasChildren":false} {"recordType":"path","path":"plugins.entries.irc.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.irc.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.kilocode","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/kilocode-provider","help":"OpenClaw Kilo Gateway provider plugin (plugin: kilocode)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.kilocode.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/kilocode-provider Config","help":"Plugin-defined config payload for kilocode.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.kilocode.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/kilocode-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.kilocode.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.kilocode.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.kimi-coding","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/kimi-coding-provider","help":"OpenClaw Kimi Coding provider plugin (plugin: kimi-coding)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.kimi-coding.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/kimi-coding-provider Config","help":"Plugin-defined config payload for kimi-coding.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.kimi-coding.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/kimi-coding-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.kimi-coding.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.kimi-coding.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.line","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/line","help":"OpenClaw LINE channel plugin (plugin: line)","hasChildren":true} {"recordType":"path","path":"plugins.entries.line.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/line Config","help":"Plugin-defined config payload for line.","hasChildren":false} {"recordType":"path","path":"plugins.entries.line.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/line","hasChildren":false} @@ -4069,11 +4165,26 @@ {"recordType":"path","path":"plugins.entries.memory-lancedb.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Enable @openclaw/memory-lancedb","hasChildren":false} {"recordType":"path","path":"plugins.entries.memory-lancedb.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.memory-lancedb.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} -{"recordType":"path","path":"plugins.entries.minimax-portal-auth","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"@openclaw/minimax-portal-auth","help":"OpenClaw MiniMax Portal OAuth provider plugin (plugin: minimax-portal-auth)","hasChildren":true} -{"recordType":"path","path":"plugins.entries.minimax-portal-auth.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"@openclaw/minimax-portal-auth Config","help":"Plugin-defined config payload for minimax-portal-auth.","hasChildren":false} -{"recordType":"path","path":"plugins.entries.minimax-portal-auth.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Enable @openclaw/minimax-portal-auth","hasChildren":false} -{"recordType":"path","path":"plugins.entries.minimax-portal-auth.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} -{"recordType":"path","path":"plugins.entries.minimax-portal-auth.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.minimax","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"@openclaw/minimax-provider","help":"OpenClaw MiniMax provider and OAuth plugin (plugin: minimax)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.minimax.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"@openclaw/minimax-provider Config","help":"Plugin-defined config payload for minimax.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.minimax.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Enable @openclaw/minimax-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.minimax.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.minimax.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.mistral","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/mistral-provider","help":"OpenClaw Mistral provider plugin (plugin: mistral)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.mistral.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/mistral-provider Config","help":"Plugin-defined config payload for mistral.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.mistral.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/mistral-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.mistral.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.mistral.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.modelstudio","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/modelstudio-provider","help":"OpenClaw Model Studio provider plugin (plugin: modelstudio)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.modelstudio.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/modelstudio-provider Config","help":"Plugin-defined config payload for modelstudio.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.modelstudio.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/modelstudio-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.modelstudio.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.modelstudio.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.moonshot","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/moonshot-provider","help":"OpenClaw Moonshot provider plugin (plugin: moonshot)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.moonshot.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/moonshot-provider Config","help":"Plugin-defined config payload for moonshot.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.moonshot.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/moonshot-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.moonshot.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.moonshot.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.msteams","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/msteams","help":"OpenClaw Microsoft Teams channel plugin (plugin: msteams)","hasChildren":true} {"recordType":"path","path":"plugins.entries.msteams.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/msteams Config","help":"Plugin-defined config payload for msteams.","hasChildren":false} {"recordType":"path","path":"plugins.entries.msteams.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/msteams","hasChildren":false} @@ -4089,6 +4200,11 @@ {"recordType":"path","path":"plugins.entries.nostr.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/nostr","hasChildren":false} {"recordType":"path","path":"plugins.entries.nostr.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.nostr.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.nvidia","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/nvidia-provider","help":"OpenClaw NVIDIA provider plugin (plugin: nvidia)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.nvidia.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/nvidia-provider Config","help":"Plugin-defined config payload for nvidia.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.nvidia.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/nvidia-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.nvidia.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.nvidia.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.ollama","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/ollama-provider","help":"OpenClaw Ollama provider plugin (plugin: ollama)","hasChildren":true} {"recordType":"path","path":"plugins.entries.ollama.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/ollama-provider Config","help":"Plugin-defined config payload for ollama.","hasChildren":false} {"recordType":"path","path":"plugins.entries.ollama.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/ollama-provider","hasChildren":false} @@ -4099,11 +4215,58 @@ {"recordType":"path","path":"plugins.entries.open-prose.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable OpenProse","hasChildren":false} {"recordType":"path","path":"plugins.entries.open-prose.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.open-prose.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openai","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/openai-provider","help":"OpenClaw OpenAI provider plugins (plugin: openai)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.openai.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/openai-provider Config","help":"Plugin-defined config payload for openai.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openai.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/openai-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openai.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.openai.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.opencode","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/opencode-provider","help":"OpenClaw OpenCode Zen provider plugin (plugin: opencode)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.opencode-go","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/opencode-go-provider","help":"OpenClaw OpenCode Go provider plugin (plugin: opencode-go)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.opencode-go.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/opencode-go-provider Config","help":"Plugin-defined config payload for opencode-go.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.opencode-go.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/opencode-go-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.opencode-go.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.opencode-go.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.opencode.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/opencode-provider Config","help":"Plugin-defined config payload for opencode.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.opencode.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/opencode-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.opencode.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.opencode.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openrouter","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/openrouter-provider","help":"OpenClaw OpenRouter provider plugin (plugin: openrouter)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.openrouter.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/openrouter-provider Config","help":"Plugin-defined config payload for openrouter.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openrouter.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/openrouter-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openrouter.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.openrouter.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"OpenShell Sandbox","help":"Sandbox backend powered by OpenShell with mirrored local workspaces and SSH-based command execution. (plugin: openshell)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.openshell.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"OpenShell Sandbox Config","help":"Plugin-defined config payload for openshell.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.openshell.config.autoProviders","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Auto-create Providers","help":"When enabled, pass --auto-providers during sandbox create.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.command","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"OpenShell Command","help":"Path or command name for the openshell CLI.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.from","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Sandbox Source","help":"OpenShell sandbox source for first-time create. Defaults to openclaw.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.gateway","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Gateway Name","help":"Optional OpenShell gateway name passed as --gateway.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.gatewayEndpoint","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Gateway Endpoint","help":"Optional OpenShell gateway endpoint passed as --gateway-endpoint.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.gpu","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"GPU","help":"Request GPU resources when creating the sandbox.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.policy","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Policy File","help":"Optional path to a custom OpenShell sandbox policy YAML.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.providers","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Providers","help":"Provider names to attach when a sandbox is created.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.openshell.config.providers.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.remoteAgentWorkspaceDir","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","storage"],"label":"Remote Agent Dir","help":"Mirror path for the real agent workspace when workspaceAccess is read-only.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.remoteWorkspaceDir","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","storage"],"label":"Remote Workspace Dir","help":"Primary writable workspace inside the OpenShell sandbox.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.config.timeoutSeconds","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","performance"],"label":"Command Timeout Seconds","help":"Timeout for openshell CLI operations such as create/upload/download.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable OpenShell Sandbox","hasChildren":false} +{"recordType":"path","path":"plugins.entries.openshell.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.openshell.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.perplexity","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/perplexity-plugin","help":"OpenClaw Perplexity plugin (plugin: perplexity)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.perplexity.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/perplexity-plugin Config","help":"Plugin-defined config payload for perplexity.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.perplexity.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/perplexity-plugin","hasChildren":false} +{"recordType":"path","path":"plugins.entries.perplexity.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.perplexity.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.phone-control","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Phone Control","help":"Arm/disarm high-risk phone node commands (camera/screen/writes) with an optional auto-expiry. (plugin: phone-control)","hasChildren":true} {"recordType":"path","path":"plugins.entries.phone-control.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Phone Control Config","help":"Plugin-defined config payload for phone-control.","hasChildren":false} {"recordType":"path","path":"plugins.entries.phone-control.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable Phone Control","hasChildren":false} {"recordType":"path","path":"plugins.entries.phone-control.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.phone-control.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.qianfan","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/qianfan-provider","help":"OpenClaw Qianfan provider plugin (plugin: qianfan)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.qianfan.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/qianfan-provider Config","help":"Plugin-defined config payload for qianfan.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.qianfan.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/qianfan-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.qianfan.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.qianfan.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.qwen-portal-auth","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"qwen-portal-auth","help":"Plugin entry for qwen-portal-auth.","hasChildren":true} {"recordType":"path","path":"plugins.entries.qwen-portal-auth.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"qwen-portal-auth Config","help":"Plugin-defined config payload for qwen-portal-auth.","hasChildren":false} {"recordType":"path","path":"plugins.entries.qwen-portal-auth.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable qwen-portal-auth","hasChildren":false} @@ -4129,6 +4292,11 @@ {"recordType":"path","path":"plugins.entries.synology-chat.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/synology-chat","hasChildren":false} {"recordType":"path","path":"plugins.entries.synology-chat.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.synology-chat.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.synthetic","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/synthetic-provider","help":"OpenClaw Synthetic provider plugin (plugin: synthetic)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.synthetic.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/synthetic-provider Config","help":"Plugin-defined config payload for synthetic.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.synthetic.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/synthetic-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.synthetic.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.synthetic.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.talk-voice","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Talk Voice","help":"Manage Talk voice selection (list/set). (plugin: talk-voice)","hasChildren":true} {"recordType":"path","path":"plugins.entries.talk-voice.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Talk Voice Config","help":"Plugin-defined config payload for talk-voice.","hasChildren":false} {"recordType":"path","path":"plugins.entries.talk-voice.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable Talk Voice","hasChildren":false} @@ -4152,11 +4320,26 @@ {"recordType":"path","path":"plugins.entries.tlon.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/tlon","hasChildren":false} {"recordType":"path","path":"plugins.entries.tlon.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.tlon.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.together","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/together-provider","help":"OpenClaw Together provider plugin (plugin: together)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.together.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/together-provider Config","help":"Plugin-defined config payload for together.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.together.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/together-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.together.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.together.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.twitch","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/twitch","help":"OpenClaw Twitch channel plugin (plugin: twitch)","hasChildren":true} {"recordType":"path","path":"plugins.entries.twitch.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/twitch Config","help":"Plugin-defined config payload for twitch.","hasChildren":false} {"recordType":"path","path":"plugins.entries.twitch.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/twitch","hasChildren":false} {"recordType":"path","path":"plugins.entries.twitch.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.twitch.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.venice","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/venice-provider","help":"OpenClaw Venice provider plugin (plugin: venice)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.venice.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/venice-provider Config","help":"Plugin-defined config payload for venice.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.venice.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/venice-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.venice.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.venice.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.vercel-ai-gateway","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/vercel-ai-gateway-provider","help":"OpenClaw Vercel AI Gateway provider plugin (plugin: vercel-ai-gateway)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.vercel-ai-gateway.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/vercel-ai-gateway-provider Config","help":"Plugin-defined config payload for vercel-ai-gateway.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.vercel-ai-gateway.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/vercel-ai-gateway-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.vercel-ai-gateway.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.vercel-ai-gateway.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.vllm","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/vllm-provider","help":"OpenClaw vLLM provider plugin (plugin: vllm)","hasChildren":true} {"recordType":"path","path":"plugins.entries.vllm.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/vllm-provider Config","help":"Plugin-defined config payload for vllm.","hasChildren":false} {"recordType":"path","path":"plugins.entries.vllm.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/vllm-provider","hasChildren":false} @@ -4283,11 +4466,31 @@ {"recordType":"path","path":"plugins.entries.voice-call.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/voice-call","hasChildren":false} {"recordType":"path","path":"plugins.entries.voice-call.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.voice-call.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.volcengine","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/volcengine-provider","help":"OpenClaw Volcengine provider plugin (plugin: volcengine)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.volcengine.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/volcengine-provider Config","help":"Plugin-defined config payload for volcengine.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.volcengine.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/volcengine-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.volcengine.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.volcengine.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.whatsapp","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/whatsapp","help":"OpenClaw WhatsApp channel plugin (plugin: whatsapp)","hasChildren":true} {"recordType":"path","path":"plugins.entries.whatsapp.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/whatsapp Config","help":"Plugin-defined config payload for whatsapp.","hasChildren":false} {"recordType":"path","path":"plugins.entries.whatsapp.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/whatsapp","hasChildren":false} {"recordType":"path","path":"plugins.entries.whatsapp.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} {"recordType":"path","path":"plugins.entries.whatsapp.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.xai","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/xai-plugin","help":"OpenClaw xAI plugin (plugin: xai)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.xai.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/xai-plugin Config","help":"Plugin-defined config payload for xai.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.xai.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/xai-plugin","hasChildren":false} +{"recordType":"path","path":"plugins.entries.xai.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.xai.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.xiaomi","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/xiaomi-provider","help":"OpenClaw Xiaomi provider plugin (plugin: xiaomi)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.xiaomi.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/xiaomi-provider Config","help":"Plugin-defined config payload for xiaomi.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.xiaomi.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/xiaomi-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.xiaomi.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.xiaomi.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.zai","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/zai-provider","help":"OpenClaw Z.AI provider plugin (plugin: zai)","hasChildren":true} +{"recordType":"path","path":"plugins.entries.zai.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/zai-provider Config","help":"Plugin-defined config payload for zai.","hasChildren":false} +{"recordType":"path","path":"plugins.entries.zai.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/zai-provider","hasChildren":false} +{"recordType":"path","path":"plugins.entries.zai.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true} +{"recordType":"path","path":"plugins.entries.zai.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false} {"recordType":"path","path":"plugins.entries.zalo","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/zalo","help":"OpenClaw Zalo channel plugin (plugin: zalo)","hasChildren":true} {"recordType":"path","path":"plugins.entries.zalo.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/zalo Config","help":"Plugin-defined config payload for zalo.","hasChildren":false} {"recordType":"path","path":"plugins.entries.zalo.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/zalo","hasChildren":false} @@ -4303,6 +4506,9 @@ {"recordType":"path","path":"plugins.installs.*.installedAt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Install Time","help":"ISO timestamp of last install/update.","hasChildren":false} {"recordType":"path","path":"plugins.installs.*.installPath","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Plugin Install Path","help":"Resolved install directory (usually ~/.openclaw/extensions/).","hasChildren":false} {"recordType":"path","path":"plugins.installs.*.integrity","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Resolved Integrity","help":"Resolved npm dist integrity hash for the fetched artifact (if reported by npm).","hasChildren":false} +{"recordType":"path","path":"plugins.installs.*.marketplaceName","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Marketplace Name","help":"Marketplace display name recorded for marketplace-backed plugin installs (if available).","hasChildren":false} +{"recordType":"path","path":"plugins.installs.*.marketplacePlugin","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Marketplace Plugin","help":"Plugin entry name inside the source marketplace, used for later updates.","hasChildren":false} +{"recordType":"path","path":"plugins.installs.*.marketplaceSource","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Marketplace Source","help":"Original marketplace source used to resolve the install (for example a repo path or Git URL).","hasChildren":false} {"recordType":"path","path":"plugins.installs.*.resolvedAt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Resolution Time","help":"ISO timestamp when npm package metadata was last resolved for this install record.","hasChildren":false} {"recordType":"path","path":"plugins.installs.*.resolvedName","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Resolved Package Name","help":"Resolved npm package name from the fetched artifact.","hasChildren":false} {"recordType":"path","path":"plugins.installs.*.resolvedSpec","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Resolved Package Spec","help":"Resolved exact npm spec (@) from the fetched artifact.","hasChildren":false} @@ -4830,6 +5036,12 @@ {"recordType":"path","path":"tools.web.search.brave.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Brave Search Mode","help":"Brave Search mode: \"web\" (URL results) or \"llm-context\" (pre-extracted page content for LLM grounding).","hasChildren":false} {"recordType":"path","path":"tools.web.search.cacheTtlMinutes","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance","storage","tools"],"label":"Web Search Cache TTL (min)","help":"Cache TTL in minutes for web_search results.","hasChildren":false} {"recordType":"path","path":"tools.web.search.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable Web Search Tool","help":"Enable the web_search tool (requires a provider API key).","hasChildren":false} +{"recordType":"path","path":"tools.web.search.firecrawl","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} +{"recordType":"path","path":"tools.web.search.firecrawl.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"label":"Firecrawl Search API Key","help":"Firecrawl API key for web search (fallback: FIRECRAWL_API_KEY env var).","hasChildren":true} +{"recordType":"path","path":"tools.web.search.firecrawl.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"tools.web.search.firecrawl.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"tools.web.search.firecrawl.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} +{"recordType":"path","path":"tools.web.search.firecrawl.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Firecrawl Search Base URL","help":"Firecrawl Search base URL override (default: \"https://api.firecrawl.dev\").","hasChildren":false} {"recordType":"path","path":"tools.web.search.gemini","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true} {"recordType":"path","path":"tools.web.search.gemini.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"label":"Gemini Search API Key","help":"Gemini API key for Google Search grounding (fallback: GEMINI_API_KEY env var).","hasChildren":true} {"recordType":"path","path":"tools.web.search.gemini.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} @@ -4858,7 +5070,7 @@ {"recordType":"path","path":"tools.web.search.perplexity.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false} {"recordType":"path","path":"tools.web.search.perplexity.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Perplexity Base URL","help":"Optional Perplexity/OpenRouter chat-completions base URL override. Setting this opts Perplexity into the legacy Sonar/OpenRouter compatibility path.","hasChildren":false} {"recordType":"path","path":"tools.web.search.perplexity.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models","tools"],"label":"Perplexity Model","help":"Optional Sonar/OpenRouter model override (default: \"perplexity/sonar-pro\"). Setting this opts Perplexity into the legacy chat-completions compatibility path.","hasChildren":false} -{"recordType":"path","path":"tools.web.search.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Web Search Provider","help":"Search provider (\"brave\", \"gemini\", \"grok\", \"kimi\", or \"perplexity\"). Auto-detected from available API keys if omitted.","hasChildren":false} +{"recordType":"path","path":"tools.web.search.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Web Search Provider","help":"Search provider (\"brave\", \"firecrawl\", \"gemini\", \"grok\", \"kimi\", or \"perplexity\"). Auto-detected from available API keys if omitted.","hasChildren":false} {"recordType":"path","path":"tools.web.search.timeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Search Timeout (sec)","help":"Timeout in seconds for web_search requests.","hasChildren":false} {"recordType":"path","path":"ui","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"UI","help":"UI presentation settings for accenting and assistant identity shown in control surfaces. Use this for branding and readability customization without changing runtime behavior.","hasChildren":true} {"recordType":"path","path":"ui.assistant","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Assistant Appearance","help":"Assistant display identity settings for name and avatar shown in UI surfaces. Keep these values aligned with your operator-facing persona and support expectations.","hasChildren":true} @@ -4882,9 +5094,9 @@ {"recordType":"path","path":"web.reconnect.jitter","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Web Reconnect Jitter","help":"Randomization factor (0-1) applied to reconnect delays to desynchronize clients after outage events. Keep non-zero jitter in multi-client deployments to reduce synchronized spikes.","hasChildren":false} {"recordType":"path","path":"web.reconnect.maxAttempts","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Web Reconnect Max Attempts","help":"Maximum reconnect attempts before giving up for the current failure sequence (0 means no retries). Use finite caps for controlled failure handling in automation-sensitive environments.","hasChildren":false} {"recordType":"path","path":"web.reconnect.maxMs","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Web Reconnect Max Delay (ms)","help":"Maximum reconnect backoff cap in milliseconds to bound retry delay growth over repeated failures. Use a reasonable cap so recovery remains timely after prolonged outages.","hasChildren":false} -{"recordType":"path","path":"wizard","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Setup Wizard State","help":"Setup wizard state tracking fields that record the most recent guided onboarding run details. Keep these fields for observability and troubleshooting of setup flows across upgrades.","hasChildren":true} -{"recordType":"path","path":"wizard.lastRunAt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Timestamp","help":"ISO timestamp for when the setup wizard most recently completed on this host. Use this to confirm onboarding recency during support and operational audits.","hasChildren":false} -{"recordType":"path","path":"wizard.lastRunCommand","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Command","help":"Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce onboarding steps when verifying setup regressions.","hasChildren":false} -{"recordType":"path","path":"wizard.lastRunCommit","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Commit","help":"Source commit identifier recorded for the last wizard execution in development builds. Use this to correlate onboarding behavior with exact source state during debugging.","hasChildren":false} -{"recordType":"path","path":"wizard.lastRunMode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Mode","help":"Wizard execution mode recorded as \"local\" or \"remote\" for the most recent onboarding flow. Use this to understand whether setup targeted direct local runtime or remote gateway topology.","hasChildren":false} -{"recordType":"path","path":"wizard.lastRunVersion","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Version","help":"OpenClaw version recorded at the time of the most recent wizard run on this config. Use this when diagnosing behavior differences across version-to-version onboarding changes.","hasChildren":false} +{"recordType":"path","path":"wizard","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Setup Wizard State","help":"Setup wizard state tracking fields that record the most recent guided setup run details. Keep these fields for observability and troubleshooting of setup flows across upgrades.","hasChildren":true} +{"recordType":"path","path":"wizard.lastRunAt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Timestamp","help":"ISO timestamp for when the setup wizard most recently completed on this host. Use this to confirm setup recency during support and operational audits.","hasChildren":false} +{"recordType":"path","path":"wizard.lastRunCommand","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Command","help":"Command invocation recorded for the latest wizard run to preserve execution context. Use this to reproduce setup steps when verifying setup regressions.","hasChildren":false} +{"recordType":"path","path":"wizard.lastRunCommit","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Commit","help":"Source commit identifier recorded for the last wizard execution in development builds. Use this to correlate setup behavior with exact source state during debugging.","hasChildren":false} +{"recordType":"path","path":"wizard.lastRunMode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Mode","help":"Wizard execution mode recorded as \"local\" or \"remote\" for the most recent setup flow. Use this to understand whether setup targeted direct local runtime or remote gateway topology.","hasChildren":false} +{"recordType":"path","path":"wizard.lastRunVersion","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Wizard Last Run Version","help":"OpenClaw version recorded at the time of the most recent wizard run on this config. Use this when diagnosing behavior differences across version-to-version setup changes.","hasChildren":false} diff --git a/docs/.i18n/glossary.zh-CN.json b/docs/.i18n/glossary.zh-CN.json index f8941862b94..36e44b6d909 100644 --- a/docs/.i18n/glossary.zh-CN.json +++ b/docs/.i18n/glossary.zh-CN.json @@ -47,6 +47,18 @@ "source": "Quick Start", "target": "快速开始" }, + { + "source": "Setup Wizard Reference", + "target": "设置向导参考" + }, + { + "source": "CLI Setup Reference", + "target": "CLI 设置参考" + }, + { + "source": "Setup Wizard (CLI)", + "target": "设置向导(CLI)" + }, { "source": "Docs directory", "target": "文档目录" diff --git a/docs/channels/feishu.md b/docs/channels/feishu.md index 2fc16aed5d4..41882e78264 100644 --- a/docs/channels/feishu.md +++ b/docs/channels/feishu.md @@ -30,9 +30,9 @@ openclaw plugins install @openclaw/feishu There are two ways to add the Feishu channel: -### Method 1: onboarding wizard (recommended) +### Method 1: setup wizard (recommended) -If you just installed OpenClaw, run the wizard: +If you just installed OpenClaw, run the setup wizard: ```bash openclaw onboard @@ -711,7 +711,7 @@ Key options: - ✅ Images - ✅ Files - ✅ Audio -- ✅ Video +- ✅ Video/media - ✅ Stickers ### Send @@ -720,4 +720,28 @@ Key options: - ✅ Images - ✅ Files - ✅ Audio -- ⚠️ Rich text (partial support) +- ✅ Video/media +- ✅ Interactive cards +- ⚠️ Rich text (post-style formatting and cards, not arbitrary Feishu authoring features) + +### Threads and replies + +- ✅ Inline replies +- ✅ Topic-thread replies where Feishu exposes `reply_in_thread` +- ✅ Media replies stay thread-aware when replying to a thread/topic message + +## Runtime action surface + +Feishu currently exposes these runtime actions: + +- `send` +- `read` +- `edit` +- `thread-reply` +- `pin` +- `list-pins` +- `unpin` +- `member-info` +- `channel-info` +- `channel-list` +- `react` and `reactions` when reactions are enabled in config diff --git a/docs/channels/matrix.md b/docs/channels/matrix.md index 9bb56d1ddb7..1536a7c08ac 100644 --- a/docs/channels/matrix.md +++ b/docs/channels/matrix.md @@ -31,7 +31,7 @@ Local checkout (when running from a git repo): openclaw plugins install ./extensions/matrix ``` -If you choose Matrix during configure/onboarding and a git checkout is detected, +If you choose Matrix during setup and a git checkout is detected, OpenClaw will offer the local install path automatically. Details: [Plugins](/tools/plugin) @@ -72,7 +72,7 @@ Details: [Plugins](/tools/plugin) - If both are set, config takes precedence. - With access token: user ID is fetched automatically via `/whoami`. - When set, `channels.matrix.userId` should be the full Matrix ID (example: `@bot:example.org`). -5. Restart the gateway (or finish onboarding). +5. Restart the gateway (or finish setup). 6. Start a DM with the bot or invite it to a room from any Matrix client (Element, Beeper, etc.; see [https://matrix.org/ecosystem/clients/](https://matrix.org/ecosystem/clients/)). Beeper requires E2EE, so set `channels.matrix.encryption: true` and verify the device. diff --git a/docs/channels/mattermost.md b/docs/channels/mattermost.md index 1e3e3f4bad2..2ceb6c17626 100644 --- a/docs/channels/mattermost.md +++ b/docs/channels/mattermost.md @@ -28,7 +28,7 @@ Local checkout (when running from a git repo): openclaw plugins install ./extensions/mattermost ``` -If you choose Mattermost during configure/onboarding and a git checkout is detected, +If you choose Mattermost during setup and a git checkout is detected, OpenClaw will offer the local install path automatically. Details: [Plugins](/tools/plugin) diff --git a/docs/channels/msteams.md b/docs/channels/msteams.md index a24f20c69df..88cba3ce6aa 100644 --- a/docs/channels/msteams.md +++ b/docs/channels/msteams.md @@ -33,7 +33,7 @@ Local checkout (when running from a git repo): openclaw plugins install ./extensions/msteams ``` -If you choose Teams during configure/onboarding and a git checkout is detected, +If you choose Teams during setup and a git checkout is detected, OpenClaw will offer the local install path automatically. Details: [Plugins](/tools/plugin) diff --git a/docs/channels/nextcloud-talk.md b/docs/channels/nextcloud-talk.md index 7797b1276ff..f8be8d74f0c 100644 --- a/docs/channels/nextcloud-talk.md +++ b/docs/channels/nextcloud-talk.md @@ -25,7 +25,7 @@ Local checkout (when running from a git repo): openclaw plugins install ./extensions/nextcloud-talk ``` -If you choose Nextcloud Talk during configure/onboarding and a git checkout is detected, +If you choose Nextcloud Talk during setup and a git checkout is detected, OpenClaw will offer the local install path automatically. Details: [Plugins](/tools/plugin) @@ -43,7 +43,7 @@ Details: [Plugins](/tools/plugin) 4. Configure OpenClaw: - Config: `channels.nextcloud-talk.baseUrl` + `channels.nextcloud-talk.botSecret` - Or env: `NEXTCLOUD_TALK_BOT_SECRET` (default account only) -5. Restart the gateway (or finish onboarding). +5. Restart the gateway (or finish setup). Minimal config: diff --git a/docs/channels/nostr.md b/docs/channels/nostr.md index 3368933d6c4..46888da0352 100644 --- a/docs/channels/nostr.md +++ b/docs/channels/nostr.md @@ -16,7 +16,7 @@ Nostr is a decentralized protocol for social networking. This channel enables Op ### Onboarding (recommended) -- The onboarding wizard (`openclaw onboard`) and `openclaw channels add` list optional channel plugins. +- The setup wizard (`openclaw onboard`) and `openclaw channels add` list optional channel plugins. - Selecting Nostr prompts you to install the plugin on demand. Install defaults: @@ -40,6 +40,15 @@ openclaw plugins install --link /extensions/nostr Restart the Gateway after installing or enabling plugins. +### Non-interactive setup + +```bash +openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" +openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" --relay-urls "wss://relay.damus.io,wss://relay.primal.net" +``` + +Use `--use-env` to keep `NOSTR_PRIVATE_KEY` in the environment instead of storing the key in config. + ## Quick setup 1. Generate a Nostr keypair (if needed): diff --git a/docs/channels/synology-chat.md b/docs/channels/synology-chat.md index 89e96b318a3..aae655f27b7 100644 --- a/docs/channels/synology-chat.md +++ b/docs/channels/synology-chat.md @@ -27,13 +27,17 @@ Details: [Plugins](/tools/plugin) ## Quick setup 1. Install and enable the Synology Chat plugin. + - `openclaw onboard` now shows Synology Chat in the same channel setup list as `openclaw channels add`. + - Non-interactive setup: `openclaw channels add --channel synology-chat --token --url ` 2. In Synology Chat integrations: - Create an incoming webhook and copy its URL. - Create an outgoing webhook with your secret token. 3. Point the outgoing webhook URL to your OpenClaw gateway: - `https://gateway-host/webhook/synology` by default. - Or your custom `channels.synology-chat.webhookPath`. -4. Configure `channels.synology-chat` in OpenClaw. +4. Finish setup in OpenClaw. + - Guided: `openclaw onboard` + - Direct: `openclaw channels add --channel synology-chat --token --url ` 5. Restart gateway and send a DM to the Synology Chat bot. Minimal config: diff --git a/docs/channels/telegram.md b/docs/channels/telegram.md index 37be3bf1111..b5700213830 100644 --- a/docs/channels/telegram.md +++ b/docs/channels/telegram.md @@ -115,7 +115,7 @@ Token resolution order is account-aware. In practice, config values win over env `channels.telegram.allowFrom` accepts numeric Telegram user IDs. `telegram:` / `tg:` prefixes are accepted and normalized. `dmPolicy: "allowlist"` with empty `allowFrom` blocks all DMs and is rejected by config validation. - The onboarding wizard accepts `@username` input and resolves it to numeric IDs. + The setup wizard accepts `@username` input and resolves it to numeric IDs. If you upgraded and your config contains `@username` allowlist entries, run `openclaw doctor --fix` to resolve them (best-effort; requires a Telegram bot token). If you previously relied on pairing-store allowlist files, `openclaw doctor --fix` can recover entries into `channels.telegram.allowFrom` in allowlist flows (for example when `dmPolicy: "allowlist"` has no explicit IDs yet). diff --git a/docs/channels/whatsapp.md b/docs/channels/whatsapp.md index cad9fe77ee3..850d88ffcac 100644 --- a/docs/channels/whatsapp.md +++ b/docs/channels/whatsapp.md @@ -76,7 +76,7 @@ openclaw pairing approve whatsapp -OpenClaw recommends running WhatsApp on a separate number when possible. (The channel metadata and onboarding flow are optimized for that setup, but personal-number setups are also supported.) +OpenClaw recommends running WhatsApp on a separate number when possible. (The channel metadata and setup flow are optimized for that setup, but personal-number setups are also supported.) ## Deployment patterns diff --git a/docs/channels/zalo.md b/docs/channels/zalo.md index cf53b574e42..b327f596f74 100644 --- a/docs/channels/zalo.md +++ b/docs/channels/zalo.md @@ -14,7 +14,7 @@ Status: experimental. DMs are supported. The [Capabilities](#capabilities) secti Zalo ships as a plugin and is not bundled with the core install. - Install via CLI: `openclaw plugins install @openclaw/zalo` -- Or select **Zalo** during onboarding and confirm the install prompt +- Or select **Zalo** during setup and confirm the install prompt - Details: [Plugins](/tools/plugin) ## Quick setup (beginner) @@ -22,11 +22,11 @@ Zalo ships as a plugin and is not bundled with the core install. 1. Install the Zalo plugin: - From a source checkout: `openclaw plugins install ./extensions/zalo` - From npm (if published): `openclaw plugins install @openclaw/zalo` - - Or pick **Zalo** in onboarding and confirm the install prompt + - Or pick **Zalo** in setup and confirm the install prompt 2. Set the token: - Env: `ZALO_BOT_TOKEN=...` - Or config: `channels.zalo.accounts.default.botToken: "..."`. -3. Restart the gateway (or finish onboarding). +3. Restart the gateway (or finish setup). 4. DM access is pairing by default; approve the pairing code on first contact. Minimal config: diff --git a/docs/channels/zalouser.md b/docs/channels/zalouser.md index 58bd2a43923..4847430c8ac 100644 --- a/docs/channels/zalouser.md +++ b/docs/channels/zalouser.md @@ -41,7 +41,7 @@ No external `zca`/`openzca` CLI binary is required. } ``` -4. Restart the Gateway (or finish onboarding). +4. Restart the Gateway (or finish setup). 5. DM access defaults to pairing; approve the pairing code on first contact. ## What it is @@ -74,7 +74,7 @@ openclaw directory groups list --channel zalouser --query "work" `channels.zalouser.dmPolicy` supports: `pairing | allowlist | open | disabled` (default: `pairing`). -`channels.zalouser.allowFrom` accepts user IDs or names. During onboarding, names are resolved to IDs using the plugin's in-process contact lookup. +`channels.zalouser.allowFrom` accepts user IDs or names. During setup, names are resolved to IDs using the plugin's in-process contact lookup. Approve via: diff --git a/docs/cli/browser.md b/docs/cli/browser.md index f9ddc151717..42af08f84f3 100644 --- a/docs/cli/browser.md +++ b/docs/cli/browser.md @@ -1,9 +1,9 @@ --- -summary: "CLI reference for `openclaw browser` (profiles, tabs, actions, extension relay)" +summary: "CLI reference for `openclaw browser` (profiles, tabs, actions, Chrome MCP, and CDP)" read_when: - You use `openclaw browser` and want examples for common tasks - You want to control a browser running on another machine via a node host - - You want to use the Chrome extension relay (attach/detach via toolbar button) + - You want to attach to your local signed-in Chrome via Chrome MCP title: "browser" --- @@ -14,7 +14,6 @@ Manage OpenClaw’s browser control server and run browser actions (tabs, snapsh Related: - Browser tool + API: [Browser tool](/tools/browser) -- Chrome extension relay: [Chrome extension](/tools/chrome-extension) ## Common flags @@ -37,13 +36,14 @@ openclaw browser --browser-profile openclaw snapshot Profiles are named browser routing configs. In practice: -- `openclaw`: launches/attaches to a dedicated OpenClaw-managed Chrome instance (isolated user data dir). +- `openclaw`: launches or attaches to a dedicated OpenClaw-managed Chrome instance (isolated user data dir). - `user`: controls your existing signed-in Chrome session via Chrome DevTools MCP. -- `chrome-relay`: controls your existing Chrome tab(s) via the Chrome extension relay. +- custom CDP profiles: point at a local or remote CDP endpoint. ```bash openclaw browser profiles openclaw browser create-profile --name work --color "#FF5A36" +openclaw browser create-profile --name chrome-live --driver existing-session openclaw browser delete-profile --name work ``` @@ -84,20 +84,17 @@ openclaw browser click openclaw browser type "hello" ``` -## Chrome extension relay (attach via toolbar button) +## Existing Chrome via MCP -This mode lets the agent control an existing Chrome tab that you attach manually (it does not auto-attach). - -Install the unpacked extension to a stable path: +Use the built-in `user` profile, or create your own `existing-session` profile: ```bash -openclaw browser extension install -openclaw browser extension path +openclaw browser --browser-profile user tabs +openclaw browser create-profile --name chrome-live --driver existing-session +openclaw browser --browser-profile chrome-live tabs ``` -Then Chrome → `chrome://extensions` → enable “Developer mode” → “Load unpacked” → select the printed folder. - -Full guide: [Chrome extension](/tools/chrome-extension) +This path is host-only. For Docker, headless servers, Browserless, or other remote setups, use a CDP profile instead. ## Remote browser control (node host proxy) diff --git a/docs/cli/channels.md b/docs/cli/channels.md index 654fbef5fa9..96b9ef33f8c 100644 --- a/docs/cli/channels.md +++ b/docs/cli/channels.md @@ -30,10 +30,11 @@ openclaw channels logs --channel all ```bash openclaw channels add --channel telegram --token +openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" openclaw channels remove --channel telegram --delete ``` -Tip: `openclaw channels add --help` shows per-channel flags (token, app token, signal-cli paths, etc). +Tip: `openclaw channels add --help` shows per-channel flags (token, private key, app token, signal-cli paths, etc). When you run `openclaw channels add` without flags, the interactive wizard can prompt: diff --git a/docs/cli/daemon.md b/docs/cli/daemon.md index 8f6042e7400..f21c3930ece 100644 --- a/docs/cli/daemon.md +++ b/docs/cli/daemon.md @@ -34,13 +34,15 @@ openclaw daemon uninstall ## Common options -- `status`: `--url`, `--token`, `--password`, `--timeout`, `--no-probe`, `--deep`, `--json` +- `status`: `--url`, `--token`, `--password`, `--timeout`, `--no-probe`, `--require-rpc`, `--deep`, `--json` - `install`: `--port`, `--runtime `, `--token`, `--force`, `--json` - lifecycle (`uninstall|start|stop|restart`): `--json` Notes: - `status` resolves configured auth SecretRefs for probe auth when possible. +- If a required auth SecretRef is unresolved in this command path, `daemon status --json` reports `rpc.authWarning` when probe connectivity/auth fails; pass `--token`/`--password` explicitly or resolve the secret source first. +- If the probe succeeds, unresolved auth-ref warnings are suppressed to avoid false positives. - On Linux systemd installs, `status` token-drift checks include both `Environment=` and `EnvironmentFile=` unit sources. - When token auth requires a token and `gateway.auth.token` is SecretRef-managed, `install` validates that the SecretRef is resolvable but does not persist the resolved token into service environment metadata. - If token auth requires a token and the configured token SecretRef is unresolved, install fails closed. diff --git a/docs/cli/docs.md b/docs/cli/docs.md index 6b79aabe6f1..744c50e1432 100644 --- a/docs/cli/docs.md +++ b/docs/cli/docs.md @@ -10,6 +10,6 @@ title: "docs" Search the live docs index. ```bash -openclaw docs browser extension +openclaw docs browser existing-session openclaw docs sandbox allowHostControl ``` diff --git a/docs/cli/doctor.md b/docs/cli/doctor.md index 90e5fa7d7a2..4718135ee68 100644 --- a/docs/cli/doctor.md +++ b/docs/cli/doctor.md @@ -31,6 +31,7 @@ Notes: - Doctor also scans `~/.openclaw/cron/jobs.json` (or `cron.store`) for legacy cron job shapes and can rewrite them in place before the scheduler has to auto-normalize them at runtime. - Doctor includes a memory-search readiness check and can recommend `openclaw configure --section model` when embedding credentials are missing. - If sandbox mode is enabled but Docker is unavailable, doctor reports a high-signal warning with remediation (`install Docker` or `openclaw config set agents.defaults.sandbox.mode off`). +- If `gateway.auth.token`/`gateway.auth.password` are SecretRef-managed and unavailable in the current command path, doctor reports a read-only warning and does not write plaintext fallback credentials. ## macOS: `launchctl` env overrides diff --git a/docs/cli/gateway.md b/docs/cli/gateway.md index 16b05baefce..d36fbde6c35 100644 --- a/docs/cli/gateway.md +++ b/docs/cli/gateway.md @@ -111,7 +111,8 @@ Options: Notes: - `gateway status` resolves configured auth SecretRefs for probe auth when possible. -- If a required auth SecretRef is unresolved in this command path, probe auth can fail; pass `--token`/`--password` explicitly or resolve the secret source first. +- If a required auth SecretRef is unresolved in this command path, `gateway status --json` reports `rpc.authWarning` when probe connectivity/auth fails; pass `--token`/`--password` explicitly or resolve the secret source first. +- If the probe succeeds, unresolved auth-ref warnings are suppressed to avoid false positives. - Use `--require-rpc` in scripts and automation when a listening service is not enough and you need the Gateway RPC itself to be healthy. - On Linux systemd installs, service auth drift checks read both `Environment=` and `EnvironmentFile=` values from the unit (including `%h`, quoted paths, multiple files, and optional `-` files). diff --git a/docs/cli/index.md b/docs/cli/index.md index ddedc7ca1aa..9c4b58d1c35 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -284,7 +284,8 @@ Manage extensions and their config: - `openclaw plugins list` — discover plugins (use `--json` for machine output). - `openclaw plugins info ` — show details for a plugin. -- `openclaw plugins install ` — install a plugin (or add a plugin path to `plugins.load.paths`). +- `openclaw plugins install ` — install a plugin (or add a plugin path to `plugins.load.paths`). +- `openclaw plugins marketplace list ` — list marketplace entries before install. - `openclaw plugins enable ` / `disable ` — toggle `plugins.entries..enabled`. - `openclaw plugins doctor` — report plugin load errors. @@ -317,7 +318,7 @@ Initialize config + workspace. Options: - `--workspace `: agent workspace path (default `~/.openclaw/workspace`). -- `--wizard`: run the onboarding wizard. +- `--wizard`: run the setup wizard. - `--non-interactive`: run wizard without prompts. - `--mode `: wizard mode. - `--remote-url `: remote Gateway URL. @@ -676,7 +677,7 @@ Surfaces: Notes: - Data comes directly from provider usage endpoints (no estimates). -- Providers: Anthropic, GitHub Copilot, OpenAI Codex OAuth, plus Gemini CLI/Antigravity when those provider plugins are enabled. +- Providers: Anthropic, GitHub Copilot, OpenAI Codex OAuth, plus Gemini CLI via the bundled `google` plugin and Antigravity where configured. - If no matching credentials exist, usage is hidden. - Details: see [Usage tracking](/concepts/usage-tracking). @@ -783,6 +784,7 @@ Notes: - `gateway status` supports `--no-probe`, `--deep`, `--require-rpc`, and `--json` for scripting. - `gateway status` also surfaces legacy or extra gateway services when it can detect them (`--deep` adds system-level scans). Profile-named OpenClaw services are treated as first-class and aren't flagged as "extra". - `gateway status` prints which config path the CLI uses vs which config the service likely uses (service env), plus the resolved probe target URL. +- If gateway auth SecretRefs are unresolved in the current command path, `gateway status --json` reports `rpc.authWarning` only when probe connectivity/auth fails (warnings are suppressed when probe succeeds). - On Linux systemd installs, status token-drift checks include both `Environment=` and `EnvironmentFile=` unit sources. - `gateway install|uninstall|start|stop|restart` support `--json` for scripting (default output stays human-friendly). - `gateway install` defaults to Node runtime; bun is **not recommended** (WhatsApp/Telegram bugs). diff --git a/docs/cli/onboard.md b/docs/cli/onboard.md index 4b30e0d52b3..899ccd82713 100644 --- a/docs/cli/onboard.md +++ b/docs/cli/onboard.md @@ -1,5 +1,5 @@ --- -summary: "CLI reference for `openclaw onboard` (interactive onboarding wizard)" +summary: "CLI reference for `openclaw onboard` (interactive setup wizard)" read_when: - You want guided setup for gateway, workspace, auth, channels, and skills title: "onboard" @@ -7,13 +7,13 @@ title: "onboard" # `openclaw onboard` -Interactive onboarding wizard (local or remote Gateway setup). +Interactive setup wizard (local or remote Gateway setup). ## Related guides -- CLI onboarding hub: [Onboarding Wizard (CLI)](/start/wizard) +- CLI onboarding hub: [Setup Wizard (CLI)](/start/wizard) - Onboarding overview: [Onboarding Overview](/start/onboarding-overview) -- CLI onboarding reference: [CLI Onboarding Reference](/start/wizard-cli-reference) +- CLI onboarding reference: [CLI Setup Reference](/start/wizard-cli-reference) - CLI automation: [CLI Automation](/start/wizard-cli-automation) - macOS onboarding: [Onboarding (macOS App)](/start/onboarding) @@ -140,7 +140,7 @@ Flow notes: - `quickstart`: minimal prompts, auto-generates a gateway token. - `manual`: full prompts for port/bind/auth (alias of `advanced`). -- Local onboarding DM scope behavior: [CLI Onboarding Reference](/start/wizard-cli-reference#outputs-and-internals). +- Local onboarding DM scope behavior: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals). - Fastest first chat: `openclaw dashboard` (Control UI, no channel setup). - Custom Provider: connect any OpenAI or Anthropic compatible endpoint, including hosted providers not listed. Use Unknown to auto-detect. diff --git a/docs/cli/plugins.md b/docs/cli/plugins.md index 4d9d1e8e80d..5e551a9c64f 100644 --- a/docs/cli/plugins.md +++ b/docs/cli/plugins.md @@ -1,5 +1,5 @@ --- -summary: "CLI reference for `openclaw plugins` (list, install, uninstall, enable/disable, doctor)" +summary: "CLI reference for `openclaw plugins` (list, install, marketplace, uninstall, enable/disable, doctor)" read_when: - You want to install or manage Gateway plugins or compatible bundles - You want to debug plugin load failures @@ -28,6 +28,7 @@ openclaw plugins uninstall openclaw plugins doctor openclaw plugins update openclaw plugins update --all +openclaw plugins marketplace list ``` Bundled plugins ship with OpenClaw but start disabled. Use `plugins enable` to @@ -46,6 +47,8 @@ capabilities. ```bash openclaw plugins install openclaw plugins install --pin +openclaw plugins install @ +openclaw plugins install --marketplace ``` Security note: treat plugin installs like running code. Prefer pinned versions. @@ -65,6 +68,31 @@ name, use an explicit scoped spec (for example `@scope/diffs`). Supported archives: `.zip`, `.tgz`, `.tar.gz`, `.tar`. +Claude marketplace installs are also supported. + +Use `plugin@marketplace` shorthand when the marketplace name exists in Claude's +local registry cache at `~/.claude/plugins/known_marketplaces.json`: + +```bash +openclaw plugins marketplace list +openclaw plugins install @ +``` + +Use `--marketplace` when you want to pass the marketplace source explicitly: + +```bash +openclaw plugins install --marketplace +openclaw plugins install --marketplace +openclaw plugins install --marketplace ./my-marketplace +``` + +Marketplace sources can be: + +- a Claude known-marketplace name from `~/.claude/plugins/known_marketplaces.json` +- a local marketplace root or `marketplace.json` path +- a GitHub repo shorthand such as `owner/repo` +- a git URL + For local paths and archives, OpenClaw auto-detects: - native OpenClaw plugins (`openclaw.plugin.json`) @@ -114,7 +142,8 @@ openclaw plugins update --all openclaw plugins update --dry-run ``` -Updates only apply to plugins installed from npm (tracked in `plugins.installs`). +Updates apply to tracked installs in `plugins.installs`, currently npm and +marketplace installs. When a stored integrity hash exists and the fetched artifact hash changes, OpenClaw prints a warning and asks for confirmation before proceeding. Use diff --git a/docs/cli/sandbox.md b/docs/cli/sandbox.md index e8e4614a9ff..5764851dc70 100644 --- a/docs/cli/sandbox.md +++ b/docs/cli/sandbox.md @@ -1,17 +1,29 @@ --- title: Sandbox CLI -summary: "Manage sandbox containers and inspect effective sandbox policy" -read_when: "You are managing sandbox containers or debugging sandbox/tool-policy behavior." +summary: "Manage sandbox runtimes and inspect effective sandbox policy" +read_when: "You are managing sandbox runtimes or debugging sandbox/tool-policy behavior." status: active --- # Sandbox CLI -Manage Docker-based sandbox containers for isolated agent execution. +Manage sandbox runtimes for isolated agent execution. ## Overview -OpenClaw can run agents in isolated Docker containers for security. The `sandbox` commands help you manage these containers, especially after updates or configuration changes. +OpenClaw can run agents in isolated sandbox runtimes for security. The `sandbox` commands help you inspect and recreate those runtimes after updates or configuration changes. + +Today that usually means: + +- Docker sandbox containers +- SSH sandbox runtimes when `agents.defaults.sandbox.backend = "ssh"` +- OpenShell sandbox runtimes when `agents.defaults.sandbox.backend = "openshell"` + +For `ssh` and OpenShell `remote`, recreate matters more than with Docker: + +- the remote workspace is canonical after the initial seed +- `openclaw sandbox recreate` deletes that canonical remote workspace for the selected scope +- next use seeds it again from the current local workspace ## Commands @@ -28,7 +40,7 @@ openclaw sandbox explain --json ### `openclaw sandbox list` -List all sandbox containers with their status and configuration. +List all sandbox runtimes with their status and configuration. ```bash openclaw sandbox list @@ -38,15 +50,16 @@ openclaw sandbox list --json # JSON output **Output includes:** -- Container name and status (running/stopped) -- Docker image and whether it matches config +- Runtime name and status +- Backend (`docker`, `openshell`, etc.) +- Config label and whether it matches current config - Age (time since creation) - Idle time (time since last use) - Associated session/agent ### `openclaw sandbox recreate` -Remove sandbox containers to force recreation with updated images/config. +Remove sandbox runtimes to force recreation with updated config. ```bash openclaw sandbox recreate --all # Recreate all containers @@ -64,11 +77,11 @@ openclaw sandbox recreate --all --force # Skip confirmation - `--browser`: Only recreate browser containers - `--force`: Skip confirmation prompt -**Important:** Containers are automatically recreated when the agent is next used. +**Important:** Runtimes are automatically recreated when the agent is next used. ## Use Cases -### After updating Docker images +### After updating a Docker image ```bash # Pull new image @@ -91,6 +104,37 @@ openclaw sandbox recreate --all openclaw sandbox recreate --all ``` +### After changing SSH target or SSH auth material + +```bash +# Edit config: +# - agents.defaults.sandbox.backend +# - agents.defaults.sandbox.ssh.target +# - agents.defaults.sandbox.ssh.workspaceRoot +# - agents.defaults.sandbox.ssh.identityFile / certificateFile / knownHostsFile +# - agents.defaults.sandbox.ssh.identityData / certificateData / knownHostsData + +openclaw sandbox recreate --all +``` + +For the core `ssh` backend, recreate deletes the per-scope remote workspace root +on the SSH target. The next run seeds it again from the local workspace. + +### After changing OpenShell source, policy, or mode + +```bash +# Edit config: +# - agents.defaults.sandbox.backend +# - plugins.entries.openshell.config.from +# - plugins.entries.openshell.config.mode +# - plugins.entries.openshell.config.policy + +openclaw sandbox recreate --all +``` + +For OpenShell `remote` mode, recreate deletes the canonical remote workspace +for that scope. The next run seeds it again from the local workspace. + ### After changing setupCommand ```bash @@ -108,16 +152,16 @@ openclaw sandbox recreate --agent alfred ## Why is this needed? -**Problem:** When you update sandbox Docker images or configuration: +**Problem:** When you update sandbox configuration: -- Existing containers continue running with old settings -- Containers are only pruned after 24h of inactivity -- Regularly-used agents keep old containers running indefinitely +- Existing runtimes continue running with old settings +- Runtimes are only pruned after 24h of inactivity +- Regularly-used agents keep old runtimes alive indefinitely -**Solution:** Use `openclaw sandbox recreate` to force removal of old containers. They'll be recreated automatically with current settings when next needed. +**Solution:** Use `openclaw sandbox recreate` to force removal of old runtimes. They'll be recreated automatically with current settings when next needed. -Tip: prefer `openclaw sandbox recreate` over manual `docker rm`. It uses the -Gateway’s container naming and avoids mismatches when scope/session keys change. +Tip: prefer `openclaw sandbox recreate` over manual backend-specific cleanup. +It uses the Gateway’s runtime registry and avoids mismatches when scope/session keys change. ## Configuration @@ -129,6 +173,7 @@ Sandbox settings live in `~/.openclaw/openclaw.json` under `agents.defaults.sand "defaults": { "sandbox": { "mode": "all", // off, non-main, all + "backend": "docker", // docker, ssh, openshell "scope": "agent", // session, agent, shared "docker": { "image": "openclaw-sandbox:bookworm-slim", diff --git a/docs/cli/security.md b/docs/cli/security.md index cc705b31a30..76a7ae75976 100644 --- a/docs/cli/security.md +++ b/docs/cli/security.md @@ -19,6 +19,8 @@ Related: ```bash openclaw security audit openclaw security audit --deep +openclaw security audit --deep --password +openclaw security audit --deep --token openclaw security audit --fix openclaw security audit --json ``` @@ -40,6 +42,12 @@ It warns when `gateway.auth.mode="none"` leaves Gateway HTTP APIs reachable with Settings prefixed with `dangerous`/`dangerously` are explicit break-glass operator overrides; enabling one is not, by itself, a security vulnerability report. For the complete dangerous-parameter inventory, see the "Insecure or dangerous flags summary" section in [Security](/gateway/security). +SecretRef behavior: + +- `security audit` resolves supported SecretRefs in read-only mode for its targeted paths. +- If a SecretRef is unavailable in the current command path, audit continues and reports `secretDiagnostics` (instead of crashing). +- `--token` and `--password` only override deep-probe auth for that command invocation; they do not rewrite config or SecretRef mappings. + ## JSON output Use `--json` for CI/policy checks: diff --git a/docs/cli/setup.md b/docs/cli/setup.md index 340a53a30d7..d8992ba8a43 100644 --- a/docs/cli/setup.md +++ b/docs/cli/setup.md @@ -1,7 +1,7 @@ --- summary: "CLI reference for `openclaw setup` (initialize config + workspace)" read_when: - - You’re doing first-run setup without the full onboarding wizard + - You’re doing first-run setup without the full setup wizard - You want to set the default workspace path title: "setup" --- diff --git a/docs/cli/status.md b/docs/cli/status.md index 856c341b036..770bf6ab50d 100644 --- a/docs/cli/status.md +++ b/docs/cli/status.md @@ -26,3 +26,4 @@ Notes: - Update info surfaces in the Overview; if an update is available, status prints a hint to run `openclaw update` (see [Updating](/install/updating)). - Read-only status surfaces (`status`, `status --json`, `status --all`) resolve supported SecretRefs for their targeted config paths when possible. - If a supported channel SecretRef is configured but unavailable in the current command path, status stays read-only and reports degraded output instead of crashing. Human output shows warnings such as “configured token unavailable in this command path”, and JSON output includes `secretDiagnostics`. +- When command-local SecretRef resolution succeeds, status prefers the resolved snapshot and clears transient “secret unavailable” channel markers from the final output. diff --git a/docs/concepts/model-providers.md b/docs/concepts/model-providers.md index 3a29c373c1d..6adbb5d0f26 100644 --- a/docs/concepts/model-providers.md +++ b/docs/concepts/model-providers.md @@ -19,10 +19,18 @@ For model selection rules, see [/concepts/models](/concepts/models). - Provider plugins can inject model catalogs via `registerProvider({ catalog })`; OpenClaw merges that output into `models.providers` before writing `models.json`. +- Provider manifests can declare `providerAuthEnvVars` so generic env-based + auth probes do not need to load plugin runtime. The remaining core env-var + map is now just for non-plugin/core providers and a few generic-precedence + cases such as Anthropic API-key-first onboarding. - Provider plugins can also own provider runtime behavior via `resolveDynamicModel`, `prepareDynamicModel`, `normalizeResolvedModel`, - `capabilities`, `prepareExtraParams`, `wrapStreamFn`, - `isCacheTtlEligible`, `prepareRuntimeAuth`, `resolveUsageAuth`, and + `capabilities`, `prepareExtraParams`, `wrapStreamFn`, `formatApiKey`, + `refreshOAuth`, `buildAuthDoctorHint`, + `isCacheTtlEligible`, `buildMissingAuthMessage`, + `suppressBuiltInModel`, `augmentModelCatalog`, `isBinaryThinking`, + `supportsXHighThinking`, `resolveDefaultThinkingLevel`, + `isModernModelRef`, `prepareRuntimeAuth`, `resolveUsageAuth`, and `fetchUsageSnapshot`. ## Plugin-owned provider behavior @@ -32,6 +40,10 @@ the generic inference loop. Typical split: +- `auth[].run` / `auth[].runNonInteractive`: provider owns onboarding/login + flows for `openclaw onboard`, `openclaw models auth`, and headless setup +- `wizard.setup` / `wizard.modelPicker`: provider owns auth-choice labels, + legacy aliases, onboarding allowlist hints, and setup entries in onboarding/model pickers - `catalog`: provider appears in `models.providers` - `resolveDynamicModel`: provider accepts model ids not present in the local static catalog yet @@ -41,7 +53,24 @@ Typical split: - `capabilities`: provider publishes transcript/tooling/provider-family quirks - `prepareExtraParams`: provider defaults or normalizes per-model request params - `wrapStreamFn`: provider applies request headers/body/model compat wrappers +- `formatApiKey`: provider formats stored auth profiles into the runtime + `apiKey` string expected by the transport +- `refreshOAuth`: provider owns OAuth refresh when the shared `pi-ai` + refreshers are not enough +- `buildAuthDoctorHint`: provider appends repair guidance when OAuth refresh + fails - `isCacheTtlEligible`: provider decides which upstream model ids support prompt-cache TTL +- `buildMissingAuthMessage`: provider replaces the generic auth-store error + with a provider-specific recovery hint +- `suppressBuiltInModel`: provider hides stale upstream rows and can return a + vendor-owned error for direct resolution failures +- `augmentModelCatalog`: provider appends synthetic/final catalog rows after + discovery and config merging +- `isBinaryThinking`: provider owns binary on/off thinking UX +- `supportsXHighThinking`: provider opts selected models into `xhigh` +- `resolveDefaultThinkingLevel`: provider owns default `/think` policy for a + model family +- `isModernModelRef`: provider owns live/smoke preferred-model matching - `prepareRuntimeAuth`: provider turns a configured credential into a short lived runtime token - `resolveUsageAuth`: provider resolves usage/quota credentials for `/usage` @@ -51,30 +80,36 @@ Typical split: Current bundled examples: -- `anthropic`: Claude 4.6 forward-compat fallback, usage endpoint fetching, - and cache-TTL/provider-family metadata +- `anthropic`: Claude 4.6 forward-compat fallback, auth repair hints, usage + endpoint fetching, and cache-TTL/provider-family metadata - `openrouter`: pass-through model ids, request wrappers, provider capability hints, and cache-TTL policy -- `github-copilot`: forward-compat model fallback, Claude-thinking transcript - hints, runtime token exchange, and usage endpoint fetching +- `github-copilot`: onboarding/device login, forward-compat model fallback, + Claude-thinking transcript hints, runtime token exchange, and usage endpoint + fetching - `openai`: GPT-5.4 forward-compat fallback, direct OpenAI transport - normalization, and provider-family metadata -- `openai-codex`: forward-compat model fallback, transport normalization, and - default transport params plus usage endpoint fetching -- `google-gemini-cli`: Gemini 3.1 forward-compat fallback plus usage-token - parsing and quota endpoint fetching for usage surfaces + normalization, Codex-aware missing-auth hints, Spark suppression, synthetic + OpenAI/Codex catalog rows, thinking/live-model policy, and + provider-family metadata +- `google` and `google-gemini-cli`: Gemini 3.1 forward-compat fallback and + modern-model matching; Gemini CLI OAuth also owns auth-profile token + formatting, usage-token parsing, and quota endpoint fetching for usage + surfaces - `moonshot`: shared transport, plugin-owned thinking payload normalization - `kilocode`: shared transport, plugin-owned request headers, reasoning payload normalization, Gemini transcript hints, and cache-TTL policy - `zai`: GLM-5 forward-compat fallback, `tool_stream` defaults, cache-TTL - policy, and usage auth + quota fetching + policy, binary-thinking/live-model policy, and usage auth + quota fetching - `mistral`, `opencode`, and `opencode-go`: plugin-owned capability metadata - `byteplus`, `cloudflare-ai-gateway`, `huggingface`, `kimi-coding`, - `minimax-portal`, `modelstudio`, `nvidia`, `qianfan`, `qwen-portal`, - `synthetic`, `together`, `venice`, `vercel-ai-gateway`, and `volcengine`: - plugin-owned catalogs only + `modelstudio`, `nvidia`, `qianfan`, `synthetic`, `together`, `venice`, + `vercel-ai-gateway`, and `volcengine`: plugin-owned catalogs only +- `qwen-portal`: plugin-owned catalog, OAuth login, and OAuth refresh - `minimax` and `xiaomi`: plugin-owned catalogs plus usage auth/snapshot logic +The bundled `openai` plugin now owns both provider ids: `openai` and +`openai-codex`. + That covers providers that still fit OpenClaw's normal transports. A provider that needs a totally custom request executor is a separate, deeper extension surface. @@ -176,16 +211,13 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no** - Compatibility: legacy OpenClaw config using `google/gemini-3.1-flash-preview` is normalized to `google/gemini-3-flash-preview` - CLI: `openclaw onboard --auth-choice gemini-api-key` -### Google Vertex, Antigravity, and Gemini CLI +### Google Vertex and Gemini CLI -- Providers: `google-vertex`, `google-antigravity`, `google-gemini-cli` -- Auth: Vertex uses gcloud ADC; Antigravity/Gemini CLI use their respective auth flows -- Caution: Antigravity and Gemini CLI OAuth in OpenClaw are unofficial integrations. Some users have reported Google account restrictions after using third-party clients. Review Google terms and use a non-critical account if you choose to proceed. -- Antigravity OAuth is shipped as a bundled plugin (`google-antigravity-auth`, disabled by default). - - Enable: `openclaw plugins enable google-antigravity-auth` - - Login: `openclaw models auth login --provider google-antigravity --set-default` -- Gemini CLI OAuth is shipped as a bundled plugin (`google-gemini-cli-auth`, disabled by default). - - Enable: `openclaw plugins enable google-gemini-cli-auth` +- Providers: `google-vertex`, `google-gemini-cli` +- Auth: Vertex uses gcloud ADC; Gemini CLI uses its OAuth flow +- Caution: Gemini CLI OAuth in OpenClaw is an unofficial integration. Some users have reported Google account restrictions after using third-party clients. Review Google terms and use a non-critical account if you choose to proceed. +- Gemini CLI OAuth is shipped as part of the bundled `google` plugin. + - Enable: `openclaw plugins enable google` - Login: `openclaw models auth login --provider google-gemini-cli --set-default` - Note: you do **not** paste a client id or secret into `openclaw.json`. The CLI login flow stores tokens in auth profiles on the gateway host. diff --git a/docs/concepts/models.md b/docs/concepts/models.md index 6323feef04e..e85e605456f 100644 --- a/docs/concepts/models.md +++ b/docs/concepts/models.md @@ -36,7 +36,7 @@ Related: ## Setup wizard (recommended) -If you don’t want to hand-edit config, run the onboarding wizard: +If you don’t want to hand-edit config, run the setup wizard: ```bash openclaw onboard diff --git a/docs/design/kilo-gateway-integration.md b/docs/design/kilo-gateway-integration.md index 4f34e553c0f..39088aaf5b2 100644 --- a/docs/design/kilo-gateway-integration.md +++ b/docs/design/kilo-gateway-integration.md @@ -492,8 +492,8 @@ const needsNonImageSanitize = - Test `resolveEnvApiKey("kilocode")` returns correct env var 2. **Integration Tests:** - - Test onboarding flow with `--auth-choice kilocode-api-key` - - Test non-interactive onboarding with `--kilocode-api-key` + - Test setup flow with `--auth-choice kilocode-api-key` + - Test non-interactive setup with `--kilocode-api-key` - Test model selection with `kilocode/` prefix 3. **E2E Tests:** diff --git a/docs/docs.json b/docs/docs.json index 229699ec37e..80409046397 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1018,7 +1018,6 @@ "pages": [ "tools/browser", "tools/browser-login", - "tools/chrome-extension", "tools/browser-linux-troubleshooting" ] }, @@ -1613,7 +1612,6 @@ "pages": [ "zh-CN/tools/browser", "zh-CN/tools/browser-login", - "zh-CN/tools/chrome-extension", "zh-CN/tools/browser-linux-troubleshooting" ] }, diff --git a/docs/experiments/onboarding-config-protocol.md b/docs/experiments/onboarding-config-protocol.md index 9427d47b7f6..e3b9d9dff10 100644 --- a/docs/experiments/onboarding-config-protocol.md +++ b/docs/experiments/onboarding-config-protocol.md @@ -1,6 +1,6 @@ --- -summary: "RPC protocol notes for onboarding wizard and config schema" -read_when: "Changing onboarding wizard steps or config schema endpoints" +summary: "RPC protocol notes for setup wizard and config schema" +read_when: "Changing setup wizard steps or config schema endpoints" title: "Onboarding and Config Protocol" --- diff --git a/docs/gateway/authentication.md b/docs/gateway/authentication.md index 28314dd85a3..c25501e6cdd 100644 --- a/docs/gateway/authentication.md +++ b/docs/gateway/authentication.md @@ -49,7 +49,7 @@ openclaw models status openclaw doctor ``` -If you’d rather not manage env vars yourself, the onboarding wizard can store +If you’d rather not manage env vars yourself, the setup wizard can store API keys for daemon use: `openclaw onboard`. See [Help](/help) for details on env inheritance (`env.shellEnv`, diff --git a/docs/gateway/configuration-reference.md b/docs/gateway/configuration-reference.md index 78e58edc085..a46f342a360 100644 --- a/docs/gateway/configuration-reference.md +++ b/docs/gateway/configuration-reference.md @@ -1117,7 +1117,7 @@ See [Typing Indicators](/concepts/typing-indicators). ### `agents.defaults.sandbox` -Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway/sandboxing) for the full guide. +Optional sandboxing for the embedded agent. See [Sandboxing](/gateway/sandboxing) for the full guide. ```json5 { @@ -1125,6 +1125,7 @@ Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway defaults: { sandbox: { mode: "non-main", // off | non-main | all + backend: "docker", // docker | ssh | openshell scope: "agent", // session | agent | shared workspaceAccess: "none", // none | ro | rw workspaceRoot: "~/.openclaw/sandboxes", @@ -1153,6 +1154,20 @@ Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway extraHosts: ["internal.service:10.0.0.5"], binds: ["/home/user/source:/source:rw"], }, + ssh: { + target: "user@gateway-host:22", + command: "ssh", + workspaceRoot: "/tmp/openclaw-sandboxes", + strictHostKeyChecking: true, + updateHostKeys: true, + identityFile: "~/.ssh/id_ed25519", + certificateFile: "~/.ssh/id_ed25519-cert.pub", + knownHostsFile: "~/.ssh/known_hosts", + // SecretRefs / inline contents also supported: + // identityData: { source: "env", provider: "default", id: "SSH_IDENTITY" }, + // certificateData: { source: "env", provider: "default", id: "SSH_CERTIFICATE" }, + // knownHostsData: { source: "env", provider: "default", id: "SSH_KNOWN_HOSTS" }, + }, browser: { enabled: false, image: "openclaw-sandbox-browser:bookworm-slim", @@ -1199,6 +1214,39 @@ Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway +**Backend:** + +- `docker`: local Docker runtime (default) +- `ssh`: generic SSH-backed remote runtime +- `openshell`: OpenShell runtime + +When `backend: "openshell"` is selected, runtime-specific settings move to +`plugins.entries.openshell.config`. + +**SSH backend config:** + +- `target`: SSH target in `user@host[:port]` form +- `command`: SSH client command (default: `ssh`) +- `workspaceRoot`: absolute remote root used for per-scope workspaces +- `identityFile` / `certificateFile` / `knownHostsFile`: existing local files passed to OpenSSH +- `identityData` / `certificateData` / `knownHostsData`: inline contents or SecretRefs that OpenClaw materializes into temp files at runtime +- `strictHostKeyChecking` / `updateHostKeys`: OpenSSH host-key policy knobs + +**SSH auth precedence:** + +- `identityData` wins over `identityFile` +- `certificateData` wins over `certificateFile` +- `knownHostsData` wins over `knownHostsFile` +- SecretRef-backed `*Data` values are resolved from the active secrets runtime snapshot before the sandbox session starts + +**SSH backend behavior:** + +- seeds the remote workspace once after create or recreate +- then keeps the remote SSH workspace canonical +- routes `exec`, file tools, and media paths over SSH +- does not sync remote changes back to the host automatically +- does not support sandbox browser containers + **Workspace access:** - `none`: per-scope sandbox workspace under `~/.openclaw/sandboxes` @@ -1211,6 +1259,40 @@ Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway - `agent`: one container + workspace per agent (default) - `shared`: shared container and workspace (no cross-session isolation) +**OpenShell plugin config:** + +```json5 +{ + plugins: { + entries: { + openshell: { + enabled: true, + config: { + mode: "mirror", // mirror | remote + from: "openclaw", + remoteWorkspaceDir: "/sandbox", + remoteAgentWorkspaceDir: "/agent", + gateway: "lab", // optional + gatewayEndpoint: "https://lab.example", // optional + policy: "strict", // optional OpenShell policy id + providers: ["openai"], // optional + autoProviders: true, + timeoutSeconds: 120, + }, + }, + }, + }, +} +``` + +**OpenShell mode:** + +- `mirror`: seed remote from local before exec, sync back after exec; local workspace stays canonical +- `remote`: seed remote once when the sandbox is created, then keep the remote workspace canonical + +In `remote` mode, host-local edits made outside OpenClaw are not synced into the sandbox automatically after the seed step. +Transport is SSH into the OpenShell sandbox, but the plugin owns sandbox lifecycle and optional mirror sync. + **`setupCommand`** runs once after container creation (via `sh -lc`). Needs network egress, writable root, root user. **Containers default to `network: "none"`** — set to `"bridge"` (or a custom bridge network) if the agent needs outbound access. @@ -1260,6 +1342,8 @@ noVNC observer access uses VNC auth by default and OpenClaw emits a short-lived +Browser sandboxing and `sandbox.docker.binds` are currently Docker-only. + Build images: ```bash @@ -2358,13 +2442,13 @@ See [Plugins](/tools/plugin). profiles: { openclaw: { cdpPort: 18800, color: "#FF4500" }, work: { cdpPort: 18801, color: "#0066CC" }, + user: { driver: "existing-session", attachOnly: true, color: "#00AA00" }, remote: { cdpUrl: "http://10.0.0.42:9222", color: "#00AA00" }, }, color: "#FF4500", // headless: false, // noSandbox: false, // extraArgs: [], - // relayBindHost: "0.0.0.0", // only when the extension relay must be reachable across namespaces (for example WSL2) // executablePath: "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser", // attachOnly: false, }, @@ -2378,11 +2462,11 @@ See [Plugins](/tools/plugin). - `ssrfPolicy.allowPrivateNetwork` remains supported as a legacy alias. - In strict mode, use `ssrfPolicy.hostnameAllowlist` and `ssrfPolicy.allowedHostnames` for explicit exceptions. - Remote profiles are attach-only (start/stop/reset disabled). +- `existing-session` profiles are host-only and use Chrome MCP instead of CDP. - Auto-detect order: default browser if Chromium-based → Chrome → Brave → Edge → Chromium → Chrome Canary. - Control service: loopback only (port derived from `gateway.port`, default `18791`). - `extraArgs` appends extra launch flags to local Chromium startup (for example `--disable-gpu`, window sizing, or debug flags). -- `relayBindHost` changes where the Chrome extension relay listens. Leave unset for loopback-only access; set an explicit non-loopback bind address such as `0.0.0.0` only when the relay must cross a namespace boundary (for example WSL2) and the host network is already trusted. --- diff --git a/docs/gateway/doctor.md b/docs/gateway/doctor.md index 95027906750..6c0711c7aea 100644 --- a/docs/gateway/doctor.md +++ b/docs/gateway/doctor.md @@ -63,6 +63,7 @@ cat ~/.openclaw/openclaw.json - Health check + restart prompt. - Skills status summary (eligible/missing/blocked). - Config normalization for legacy values. +- Browser migration checks for legacy Chrome extension configs and Chrome MCP readiness. - OpenCode provider override warnings (`models.providers.opencode` / `models.providers.opencode-go`). - Legacy on-disk state migration (sessions/agent dir/WhatsApp auth). - Legacy cron store migration (`jobId`, `schedule.cron`, top-level delivery/payload fields, payload `provider`, simple `notify: true` webhook fallback jobs). @@ -128,6 +129,8 @@ Current migrations: - `agent.model`/`allowedModels`/`modelAliases`/`modelFallbacks`/`imageModelFallbacks` → `agents.defaults.models` + `agents.defaults.model.primary/fallbacks` + `agents.defaults.imageModel.primary/fallbacks` - `browser.ssrfPolicy.allowPrivateNetwork` → `browser.ssrfPolicy.dangerouslyAllowPrivateNetwork` +- `browser.profiles.*.driver: "extension"` → `"existing-session"` +- remove `browser.relayBindHost` (legacy extension relay setting) Doctor warnings also include account-default guidance for multi-account channels: @@ -141,6 +144,33 @@ manually, it overrides the built-in OpenCode catalog from `@mariozechner/pi-ai`. That can force models onto the wrong API or zero out costs. Doctor warns so you can remove the override and restore per-model API routing + costs. +### 2c) Browser migration and Chrome MCP readiness + +If your browser config still points at the removed Chrome extension path, doctor +normalizes it to the current host-local Chrome MCP attach model: + +- `browser.profiles.*.driver: "extension"` becomes `"existing-session"` +- `browser.relayBindHost` is removed + +Doctor also audits the host-local Chrome MCP path when you use `defaultProfile: +"user"` or a configured `existing-session` profile: + +- checks whether Google Chrome is installed on the same host +- checks the detected Chrome version and warns when it is below Chrome 144 +- reminds you to enable remote debugging in Chrome at + `chrome://inspect/#remote-debugging` + +Doctor cannot enable the Chrome-side setting for you. Host-local Chrome MCP +still requires: + +- Google Chrome 144+ on the gateway/node host +- Chrome running locally +- remote debugging enabled in Chrome +- approving the first attach consent prompt in Chrome + +This check does **not** apply to Docker, sandbox, remote-browser, or other +headless flows. Those continue to use raw CDP. + ### 3) Legacy state migrations (disk layout) Doctor can migrate older on-disk layouts into the current structure: diff --git a/docs/gateway/multiple-gateways.md b/docs/gateway/multiple-gateways.md index d6f35e08a46..6d1cf423b98 100644 --- a/docs/gateway/multiple-gateways.md +++ b/docs/gateway/multiple-gateways.md @@ -70,7 +70,7 @@ openclaw --profile rescue onboard # better choose completely different base port, like 19789, # - rest of the onboarding is the same as normal -# To install the service (if not happened automatically during onboarding) +# To install the service (if not happened automatically during setup) openclaw --profile rescue gateway install ``` diff --git a/docs/gateway/sandboxing.md b/docs/gateway/sandboxing.md index d62af2f4f7d..c6cf839e42d 100644 --- a/docs/gateway/sandboxing.md +++ b/docs/gateway/sandboxing.md @@ -7,7 +7,7 @@ status: active # Sandboxing -OpenClaw can run **tools inside Docker containers** to reduce blast radius. +OpenClaw can run **tools inside sandbox backends** to reduce blast radius. This is **optional** and controlled by configuration (`agents.defaults.sandbox` or `agents.list[].sandbox`). If sandboxing is off, tools run on the host. The Gateway stays on the host; tool execution runs in an isolated sandbox @@ -54,6 +54,187 @@ Not sandboxed: - `"agent"`: one container per agent. - `"shared"`: one container shared by all sandboxed sessions. +## Backend + +`agents.defaults.sandbox.backend` controls **which runtime** provides the sandbox: + +- `"docker"` (default): local Docker-backed sandbox runtime. +- `"ssh"`: generic SSH-backed remote sandbox runtime. +- `"openshell"`: OpenShell-backed sandbox runtime. + +SSH-specific config lives under `agents.defaults.sandbox.ssh`. +OpenShell-specific config lives under `plugins.entries.openshell.config`. + +### SSH backend + +Use `backend: "ssh"` when you want OpenClaw to sandbox `exec`, file tools, and media reads on +an arbitrary SSH-accessible machine. + +```json5 +{ + agents: { + defaults: { + sandbox: { + mode: "all", + backend: "ssh", + scope: "session", + workspaceAccess: "rw", + ssh: { + target: "user@gateway-host:22", + workspaceRoot: "/tmp/openclaw-sandboxes", + strictHostKeyChecking: true, + updateHostKeys: true, + identityFile: "~/.ssh/id_ed25519", + certificateFile: "~/.ssh/id_ed25519-cert.pub", + knownHostsFile: "~/.ssh/known_hosts", + // Or use SecretRefs / inline contents instead of local files: + // identityData: { source: "env", provider: "default", id: "SSH_IDENTITY" }, + // certificateData: { source: "env", provider: "default", id: "SSH_CERTIFICATE" }, + // knownHostsData: { source: "env", provider: "default", id: "SSH_KNOWN_HOSTS" }, + }, + }, + }, + }, +} +``` + +How it works: + +- OpenClaw creates a per-scope remote root under `sandbox.ssh.workspaceRoot`. +- On first use after create or recreate, OpenClaw seeds that remote workspace from the local workspace once. +- After that, `exec`, `read`, `write`, `edit`, `apply_patch`, prompt media reads, and inbound media staging run directly against the remote workspace over SSH. +- OpenClaw does not sync remote changes back to the local workspace automatically. + +Authentication material: + +- `identityFile`, `certificateFile`, `knownHostsFile`: use existing local files and pass them through OpenSSH config. +- `identityData`, `certificateData`, `knownHostsData`: use inline strings or SecretRefs. OpenClaw resolves them through the normal secrets runtime snapshot, writes them to temp files with `0600`, and deletes them when the SSH session ends. +- If both `*File` and `*Data` are set for the same item, `*Data` wins for that SSH session. + +This is a **remote-canonical** model. The remote SSH workspace becomes the real sandbox state after the initial seed. + +Important consequences: + +- Host-local edits made outside OpenClaw after the seed step are not visible remotely until you recreate the sandbox. +- `openclaw sandbox recreate` deletes the per-scope remote root and seeds again from local on next use. +- Browser sandboxing is not supported on the SSH backend. +- `sandbox.docker.*` settings do not apply to the SSH backend. + +```json5 +{ + agents: { + defaults: { + sandbox: { + mode: "all", + backend: "openshell", + scope: "session", + workspaceAccess: "rw", + }, + }, + }, + plugins: { + entries: { + openshell: { + enabled: true, + config: { + from: "openclaw", + mode: "remote", // mirror | remote + remoteWorkspaceDir: "/sandbox", + remoteAgentWorkspaceDir: "/agent", + }, + }, + }, + }, +} +``` + +OpenShell modes: + +- `mirror` (default): local workspace stays canonical. OpenClaw syncs local files into OpenShell before exec and syncs the remote workspace back after exec. +- `remote`: OpenShell workspace is canonical after the sandbox is created. OpenClaw seeds the remote workspace once from the local workspace, then file tools and exec run directly against the remote sandbox without syncing changes back. + +OpenShell reuses the same core SSH transport and remote filesystem bridge as the generic SSH backend. +The plugin adds OpenShell-specific lifecycle (`sandbox create/get/delete`, `sandbox ssh-config`) and the optional `mirror` mode. + +Remote transport details: + +- OpenClaw asks OpenShell for sandbox-specific SSH config via `openshell sandbox ssh-config `. +- Core writes that SSH config to a temp file, opens the SSH session, and reuses the same remote filesystem bridge used by `backend: "ssh"`. +- In `mirror` mode only the lifecycle differs: sync local to remote before exec, then sync back after exec. + +Current OpenShell limitations: + +- sandbox browser is not supported yet +- `sandbox.docker.binds` is not supported on the OpenShell backend +- Docker-specific runtime knobs under `sandbox.docker.*` still apply only to the Docker backend + +## OpenShell workspace modes + +OpenShell has two workspace models. This is the part that matters most in practice. + +### `mirror` + +Use `plugins.entries.openshell.config.mode: "mirror"` when you want the **local workspace to stay canonical**. + +Behavior: + +- Before `exec`, OpenClaw syncs the local workspace into the OpenShell sandbox. +- After `exec`, OpenClaw syncs the remote workspace back to the local workspace. +- File tools still operate through the sandbox bridge, but the local workspace remains the source of truth between turns. + +Use this when: + +- you edit files locally outside OpenClaw and want those changes to show up in the sandbox automatically +- you want the OpenShell sandbox to behave as much like the Docker backend as possible +- you want the host workspace to reflect sandbox writes after each exec turn + +Tradeoff: + +- extra sync cost before and after exec + +### `remote` + +Use `plugins.entries.openshell.config.mode: "remote"` when you want the **OpenShell workspace to become canonical**. + +Behavior: + +- When the sandbox is first created, OpenClaw seeds the remote workspace from the local workspace once. +- After that, `exec`, `read`, `write`, `edit`, and `apply_patch` operate directly against the remote OpenShell workspace. +- OpenClaw does **not** sync remote changes back into the local workspace after exec. +- Prompt-time media reads still work because file and media tools read through the sandbox bridge instead of assuming a local host path. +- Transport is SSH into the OpenShell sandbox returned by `openshell sandbox ssh-config`. + +Important consequences: + +- If you edit files on the host outside OpenClaw after the seed step, the remote sandbox will **not** see those changes automatically. +- If the sandbox is recreated, the remote workspace is seeded from the local workspace again. +- With `scope: "agent"` or `scope: "shared"`, that remote workspace is shared at that same scope. + +Use this when: + +- the sandbox should live primarily on the remote OpenShell side +- you want lower per-turn sync overhead +- you do not want host-local edits to silently overwrite remote sandbox state + +Choose `mirror` if you think of the sandbox as a temporary execution environment. +Choose `remote` if you think of the sandbox as the real workspace. + +## OpenShell lifecycle + +OpenShell sandboxes are still managed through the normal sandbox lifecycle: + +- `openclaw sandbox list` shows OpenShell runtimes as well as Docker runtimes +- `openclaw sandbox recreate` deletes the current runtime and lets OpenClaw recreate it on next use +- prune logic is backend-aware too + +For `remote` mode, recreate is especially important: + +- recreate deletes the canonical remote workspace for that scope +- the next use seeds a fresh remote workspace from the local workspace + +For `mirror` mode, recreate mainly resets the remote execution environment +because the local workspace remains canonical anyway. + ## Workspace access `agents.defaults.sandbox.workspaceAccess` controls **what the sandbox can see**: @@ -62,6 +243,12 @@ Not sandboxed: - `"ro"`: mounts the agent workspace read-only at `/agent` (disables `write`/`edit`/`apply_patch`). - `"rw"`: mounts the agent workspace read/write at `/workspace`. +With the OpenShell backend: + +- `mirror` mode still uses the local workspace as the canonical source between exec turns +- `remote` mode uses the remote OpenShell workspace as the canonical source after the initial seed +- `workspaceAccess: "ro"` and `"none"` still restrict write behavior the same way + Inbound media is copied into the active sandbox workspace (`media/inbound/*`). Skills note: the `read` tool is sandbox-rooted. With `workspaceAccess: "none"`, OpenClaw mirrors eligible skills into the sandbox workspace (`.../skills`) so @@ -116,7 +303,7 @@ Security notes: ## Images + setup -Default image: `openclaw-sandbox:bookworm-slim` +Default Docker image: `openclaw-sandbox:bookworm-slim` Build it once: @@ -145,7 +332,7 @@ Sandboxed browser image: scripts/sandbox-browser-setup.sh ``` -By default, sandbox containers run with **no network**. +By default, Docker sandbox containers run with **no network**. Override with `agents.defaults.sandbox.docker.network`. The bundled sandbox browser image also applies conservative Chromium startup defaults diff --git a/docs/gateway/secrets.md b/docs/gateway/secrets.md index 93cd508d4f1..1379d8e0202 100644 --- a/docs/gateway/secrets.md +++ b/docs/gateway/secrets.md @@ -41,6 +41,9 @@ Examples of inactive surfaces: - Web search provider-specific keys that are not selected by `tools.web.search.provider`. In auto mode (provider unset), keys are consulted by precedence for provider auto-detection until one resolves. After selection, non-selected provider keys are treated as inactive until selected. +- Sandbox SSH auth material (`agents.defaults.sandbox.ssh.identityData`, + `certificateData`, `knownHostsData`, plus per-agent overrides) is active only + when the effective sandbox backend is `ssh` for the default agent or an enabled agent. - `gateway.remote.token` / `gateway.remote.password` SecretRefs are active if one of these is true: - `gateway.mode=remote` - `gateway.remote.url` is configured @@ -67,7 +70,7 @@ active-surface policy, so you can see why a credential was treated as active or When onboarding runs in interactive mode and you choose SecretRef storage, OpenClaw runs preflight validation before saving: -- Env refs: validates env var name and confirms a non-empty value is visible during onboarding. +- Env refs: validates env var name and confirms a non-empty value is visible during setup. - Provider refs (`file` or `exec`): validates provider selection, resolves `id`, and checks resolved value type. - Quickstart reuse path: when `gateway.auth.token` is already a SecretRef, onboarding resolves it before probe/dashboard bootstrap (for `env`, `file`, and `exec` refs) using the same fail-fast gate. @@ -285,6 +288,35 @@ Optional per-id errors: } ``` +## Sandbox SSH auth material + +The core `ssh` sandbox backend also supports SecretRefs for SSH auth material: + +```json5 +{ + agents: { + defaults: { + sandbox: { + mode: "all", + backend: "ssh", + ssh: { + target: "user@gateway-host:22", + identityData: { source: "env", provider: "default", id: "SSH_IDENTITY" }, + certificateData: { source: "env", provider: "default", id: "SSH_CERTIFICATE" }, + knownHostsData: { source: "env", provider: "default", id: "SSH_KNOWN_HOSTS" }, + }, + }, + }, + }, +} +``` + +Runtime behavior: + +- OpenClaw resolves these refs during sandbox activation, not lazily during each SSH call. +- Resolved values are written to temp files with restrictive permissions and used in generated SSH config. +- If the effective sandbox backend is not `ssh`, these refs stay inactive and do not block startup. + ## Supported credential surface Canonical supported and unsupported credentials are listed in: @@ -348,7 +380,7 @@ Command paths can opt into supported SecretRef resolution via gateway snapshot R There are two broad behaviors: - Strict command paths (for example `openclaw memory` remote-memory paths and `openclaw qr --remote`) read from the active snapshot and fail fast when a required SecretRef is unavailable. -- Read-only command paths (for example `openclaw status`, `openclaw status --all`, `openclaw channels status`, `openclaw channels resolve`, and read-only doctor/config repair flows) also prefer the active snapshot, but degrade instead of aborting when a targeted SecretRef is unavailable in that command path. +- Read-only command paths (for example `openclaw status`, `openclaw status --all`, `openclaw channels status`, `openclaw channels resolve`, `openclaw security audit`, and read-only doctor/config repair flows) also prefer the active snapshot, but degrade instead of aborting when a targeted SecretRef is unavailable in that command path. Read-only behavior: diff --git a/docs/gateway/security/index.md b/docs/gateway/security/index.md index f7f6583d794..68be08fbed5 100644 --- a/docs/gateway/security/index.md +++ b/docs/gateway/security/index.md @@ -738,7 +738,7 @@ In minimal mode, the Gateway still broadcasts enough for device discovery (`role Gateway auth is **required by default**. If no token/password is configured, the Gateway refuses WebSocket connections (fail‑closed). -The onboarding wizard generates a token by default (even for loopback) so +The setup wizard generates a token by default (even for loopback) so local clients must authenticate. Set a token so **all** WS clients must authenticate: @@ -990,10 +990,9 @@ access those accounts and data. Treat browser profiles as **sensitive state**: - Treat browser downloads as untrusted input; prefer an isolated downloads directory. - Disable browser sync/password managers in the agent profile if possible (reduces blast radius). - For remote gateways, assume “browser control” is equivalent to “operator access” to whatever that profile can reach. -- Keep the Gateway and node hosts tailnet-only; avoid exposing relay/control ports to LAN or public Internet. -- The Chrome extension relay’s CDP endpoint is auth-gated; only OpenClaw clients can connect. +- Keep the Gateway and node hosts tailnet-only; avoid exposing browser control ports to LAN or public Internet. - Disable browser proxy routing when you don’t need it (`gateway.nodes.browser.mode="off"`). -- Chrome extension relay mode is **not** “safer”; it can take over your existing Chrome tabs. Assume it can act as you in whatever that tab/profile can reach. +- Chrome MCP existing-session mode is **not** “safer”; it can act as you in whatever that host Chrome profile can reach. ### Browser SSRF policy (trusted-network default) diff --git a/docs/gateway/troubleshooting.md b/docs/gateway/troubleshooting.md index 41c697a67f1..aa75b9cf2b5 100644 --- a/docs/gateway/troubleshooting.md +++ b/docs/gateway/troubleshooting.md @@ -289,19 +289,18 @@ Look for: - Valid browser executable path. - CDP profile reachability. -- Extension relay tab attachment (if an extension relay profile is configured). +- Local Chrome availability for `existing-session` / `user` profiles. Common signatures: - `Failed to start Chrome CDP on port` → browser process failed to launch. - `browser.executablePath not found` → configured path is invalid. -- `Chrome extension relay is running, but no tab is connected` → extension relay not attached. +- `No Chrome tabs found for profile="user"` → the Chrome MCP attach profile has no open local Chrome tabs. - `Browser attachOnly is enabled ... not reachable` → attach-only profile has no reachable target. Related: - [/tools/browser-linux-troubleshooting](/tools/browser-linux-troubleshooting) -- [/tools/chrome-extension](/tools/chrome-extension) - [/tools/browser](/tools/browser) ## If you upgraded and something suddenly broke diff --git a/docs/help/faq.md b/docs/help/faq.md index 236097634c1..b32b1aac8c5 100644 --- a/docs/help/faq.md +++ b/docs/help/faq.md @@ -36,7 +36,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS, - [How do I install OpenClaw on a VPS?](#how-do-i-install-openclaw-on-a-vps) - [Where are the cloud/VPS install guides?](#where-are-the-cloudvps-install-guides) - [Can I ask OpenClaw to update itself?](#can-i-ask-openclaw-to-update-itself) - - [What does the onboarding wizard actually do?](#what-does-the-onboarding-wizard-actually-do) + - [What does the setup wizard actually do?](#what-does-the-setup-wizard-actually-do) - [Do I need a Claude or OpenAI subscription to run this?](#do-i-need-a-claude-or-openai-subscription-to-run-this) - [Can I use Claude Max subscription without an API key](#can-i-use-claude-max-subscription-without-an-api-key) - [How does Anthropic "setup-token" auth work?](#how-does-anthropic-setuptoken-auth-work) @@ -80,7 +80,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS, - [Can OpenClaw run tasks on a schedule or continuously in the background?](#can-openclaw-run-tasks-on-a-schedule-or-continuously-in-the-background) - [Can I run Apple macOS-only skills from Linux?](#can-i-run-apple-macos-only-skills-from-linux) - [Do you have a Notion or HeyGen integration?](#do-you-have-a-notion-or-heygen-integration) - - [How do I install the Chrome extension for browser takeover?](#how-do-i-install-the-chrome-extension-for-browser-takeover) + - [How do I use my existing signed-in Chrome with OpenClaw?](#how-do-i-use-my-existing-signed-in-chrome-with-openclaw) - [Sandboxing and memory](#sandboxing-and-memory) - [Is there a dedicated sandboxing doc?](#is-there-a-dedicated-sandboxing-doc) - [How do I bind a host folder into the sandbox?](#how-do-i-bind-a-host-folder-into-the-sandbox) @@ -317,7 +317,7 @@ Install docs: [Install](/install), [Installer flags](/install/installer), [Updat ### What's the recommended way to install and set up OpenClaw -The repo recommends running from source and using the onboarding wizard: +The repo recommends running from source and using the setup wizard: ```bash curl -fsSL https://openclaw.ai/install.sh | bash @@ -627,7 +627,7 @@ More detail: [Install](/install) and [Installer flags](/install/installer). ### How do I install OpenClaw on Linux -Short answer: follow the Linux guide, then run the onboarding wizard. +Short answer: follow the Linux guide, then run the setup wizard. - Linux quick path + service install: [Linux](/platforms/linux). - Full walkthrough: [Getting Started](/start/getting-started). @@ -685,7 +685,7 @@ openclaw gateway restart Docs: [Update](/cli/update), [Updating](/install/updating). -### What does the onboarding wizard actually do +### What does the setup wizard actually do `openclaw onboard` is the recommended setup path. In **local mode** it walks you through: @@ -773,7 +773,7 @@ OpenClaw supports **OpenAI Code (Codex)** via OAuth (ChatGPT sign-in). The wizar Yes. OpenClaw fully supports **OpenAI Code (Codex) subscription OAuth**. OpenAI explicitly allows subscription OAuth usage in external tools/workflows -like OpenClaw. The onboarding wizard can run the OAuth flow for you. +like OpenClaw. The setup wizard can run the OAuth flow for you. See [OAuth](/concepts/oauth), [Model providers](/concepts/model-providers), and [Wizard](/start/wizard). @@ -783,7 +783,7 @@ Gemini CLI uses a **plugin auth flow**, not a client id or secret in `openclaw.j Steps: -1. Enable the plugin: `openclaw plugins enable google-gemini-cli-auth` +1. Enable the plugin: `openclaw plugins enable google` 2. Login: `openclaw models auth login --provider google-gemini-cli --set-default` This stores OAuth tokens in auth profiles on the gateway host. Details: [Model providers](/concepts/model-providers). @@ -844,7 +844,7 @@ without WhatsApp/Telegram. `channels.telegram.allowFrom` is **the human sender's Telegram user ID** (numeric). It is not the bot username. -The onboarding wizard accepts `@username` input and resolves it to a numeric ID, but OpenClaw authorization uses numeric IDs only. +The setup wizard accepts `@username` input and resolves it to a numeric ID, but OpenClaw authorization uses numeric IDs only. Safer (no third-party bot): @@ -1214,22 +1214,23 @@ clawhub update --all ClawHub installs into `./skills` under your current directory (or falls back to your configured OpenClaw workspace); OpenClaw treats that as `/skills` on the next session. For shared skills across agents, place them in `~/.openclaw/skills//SKILL.md`. Some skills expect binaries installed via Homebrew; on Linux that means Linuxbrew (see the Homebrew Linux FAQ entry above). See [Skills](/tools/skills) and [ClawHub](/tools/clawhub). -### How do I install the Chrome extension for browser takeover +### How do I use my existing signed-in Chrome with OpenClaw -Use the built-in installer, then load the unpacked extension in Chrome: +Use the built-in `user` browser profile, which attaches through Chrome DevTools MCP: ```bash -openclaw browser extension install -openclaw browser extension path +openclaw browser --browser-profile user tabs +openclaw browser --browser-profile user snapshot ``` -Then Chrome → `chrome://extensions` → enable "Developer mode" → "Load unpacked" → pick that folder. +If you want a custom name, create an explicit MCP profile: -Full guide (including remote Gateway + security notes): [Chrome extension](/tools/chrome-extension) +```bash +openclaw browser create-profile --name chrome-live --driver existing-session +openclaw browser --browser-profile chrome-live tabs +``` -If the Gateway runs on the same machine as Chrome (default setup), you usually **do not** need anything extra. -If the Gateway runs elsewhere, run a node host on the browser machine so the Gateway can proxy browser actions. -You still need to click the extension button on the tab you want to control (it doesn't auto-attach). +This path is host-local. If the Gateway runs elsewhere, either run a node host on the browser machine or use remote CDP instead. ## Sandboxing and memory @@ -1665,13 +1666,12 @@ setup is an always-on host plus your laptop as a node. - **No inbound SSH required.** Nodes connect out to the Gateway WebSocket and use device pairing. - **Safer execution controls.** `system.run` is gated by node allowlists/approvals on that laptop. - **More device tools.** Nodes expose `canvas`, `camera`, and `screen` in addition to `system.run`. -- **Local browser automation.** Keep the Gateway on a VPS, but run Chrome locally and relay control - with the Chrome extension + a node host on the laptop. +- **Local browser automation.** Keep the Gateway on a VPS, but run Chrome locally through a node host on the laptop, or attach to local Chrome on the host via Chrome MCP. SSH is fine for ad-hoc shell access, but nodes are simpler for ongoing agent workflows and device automation. -Docs: [Nodes](/nodes), [Nodes CLI](/cli/nodes), [Chrome extension](/tools/chrome-extension). +Docs: [Nodes](/nodes), [Nodes CLI](/cli/nodes), [Browser](/tools/browser). ### Should I install on a second laptop or just add a node @@ -1901,7 +1901,7 @@ Non-interactive full reset: openclaw reset --scope full --yes --non-interactive ``` -Then re-run onboarding: +Then re-run setup: ```bash openclaw onboard --install-daemon @@ -1909,7 +1909,7 @@ openclaw onboard --install-daemon Notes: -- The onboarding wizard also offers **Reset** if it sees an existing config. See [Wizard](/start/wizard). +- The setup wizard also offers **Reset** if it sees an existing config. See [Wizard](/start/wizard). - If you used profiles (`--profile` / `OPENCLAW_PROFILE`), reset each state dir (defaults are `~/.openclaw-`). - Dev reset: `openclaw gateway --dev --reset` (dev-only; wipes dev config + credentials + sessions + workspace). @@ -2039,18 +2039,18 @@ Yes. Use **Multi-Agent Routing** to run multiple isolated agents and route inbou channel/account/peer. Slack is supported as a channel and can be bound to specific agents. Browser access is powerful but not "do anything a human can" - anti-bot, CAPTCHAs, and MFA can -still block automation. For the most reliable browser control, use the Chrome extension relay -on the machine that runs the browser (and keep the Gateway anywhere). +still block automation. For the most reliable browser control, use local Chrome MCP on the host, +or use CDP on the machine that actually runs the browser. Best-practice setup: - Always-on Gateway host (VPS/Mac mini). - One agent per role (bindings). - Slack channel(s) bound to those agents. -- Local browser via extension relay (or a node) when needed. +- Local browser via Chrome MCP or a node when needed. Docs: [Multi-Agent Routing](/concepts/multi-agent), [Slack](/channels/slack), -[Browser](/tools/browser), [Chrome extension](/tools/chrome-extension), [Nodes](/nodes). +[Browser](/tools/browser), [Nodes](/nodes). ## Models: defaults, selection, aliases, switching diff --git a/docs/help/testing.md b/docs/help/testing.md index b2057e8a1da..09388dd769e 100644 --- a/docs/help/testing.md +++ b/docs/help/testing.md @@ -61,7 +61,7 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost): - Command: `pnpm test:e2e` - Config: `vitest.e2e.config.ts` -- Files: `src/**/*.e2e.test.ts` +- Files: `src/**/*.e2e.test.ts`, `test/**/*.e2e.test.ts` - Runtime defaults: - Uses Vitest `vmForks` for faster file startup. - Uses adaptive workers (CI: 2-4, local: 4-8). @@ -77,6 +77,23 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost): - No real keys required - More moving parts than unit tests (can be slower) +### E2E: OpenShell backend smoke + +- Command: `pnpm test:e2e:openshell` +- File: `test/openshell-sandbox.e2e.test.ts` +- Scope: + - Starts an isolated OpenShell gateway on the host via Docker + - Creates a sandbox from a temporary local Dockerfile + - Exercises OpenClaw's OpenShell backend over real `sandbox ssh-config` + SSH exec + - Verifies remote-canonical filesystem behavior through the sandbox fs bridge +- Expectations: + - Opt-in only; not part of the default `pnpm test:e2e` run + - Requires a local `openshell` CLI plus a working Docker daemon + - Uses isolated `HOME` / `XDG_CONFIG_HOME`, then destroys the test gateway and sandbox +- Useful overrides: + - `OPENCLAW_E2E_OPENSHELL=1` to enable the test when running the broader e2e suite manually + - `OPENCLAW_E2E_OPENSHELL_COMMAND=/path/to/openshell` to point at a non-default CLI binary or wrapper script + ### Live (real providers + real models) - Command: `pnpm test:live` @@ -345,7 +362,7 @@ If you want to rely on env keys (e.g. exported in your `~/.profile`), run local ## Docker runners (optional “works in Linux” checks) -These run `pnpm test:live` inside the repo Docker image, mounting your local config dir and workspace (and sourcing `~/.profile` if mounted): +These run `pnpm test:live` inside the repo Docker image, mounting your local config dir and workspace (and sourcing `~/.profile` if mounted). They also bind-mount CLI auth homes like `~/.codex`, `~/.claude`, `~/.qwen`, and `~/.minimax` when present so external-CLI OAuth stays available in-container: - Direct models: `pnpm test:docker:live-models` (script: `scripts/test-live-models-docker.sh`) - Gateway + dev agent: `pnpm test:docker:live-gateway` (script: `scripts/test-live-gateway-models-docker.sh`) @@ -367,6 +384,7 @@ Useful env vars: - `OPENCLAW_CONFIG_DIR=...` (default: `~/.openclaw`) mounted to `/home/node/.openclaw` - `OPENCLAW_WORKSPACE_DIR=...` (default: `~/.openclaw/workspace`) mounted to `/home/node/.openclaw/workspace` - `OPENCLAW_PROFILE_FILE=...` (default: `~/.profile`) mounted to `/home/node/.profile` and sourced before running tests +- External CLI auth dirs under `$HOME` (`.codex`, `.claude`, `.qwen`, `.minimax`) are mounted read-only to the matching `/home/node/...` paths when present - `OPENCLAW_LIVE_GATEWAY_MODELS=...` / `OPENCLAW_LIVE_MODELS=...` to narrow the run - `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` to ensure creds come from the profile store (not env) diff --git a/docs/help/troubleshooting.md b/docs/help/troubleshooting.md index a3988c4ea58..1660100ba8c 100644 --- a/docs/help/troubleshooting.md +++ b/docs/help/troubleshooting.md @@ -278,13 +278,13 @@ flowchart TD Good output looks like: - Browser status shows `running: true` and a chosen browser/profile. - - `openclaw` profile starts or `chrome` relay has an attached tab. + - `openclaw` starts, or `user` can see local Chrome tabs. Common log signatures: - `Failed to start Chrome CDP on port` → local browser launch failed. - `browser.executablePath not found` → configured binary path is wrong. - - `Chrome extension relay is running, but no tab is connected` → extension not attached. + - `No Chrome tabs found for profile="user"` → the Chrome MCP attach profile has no open local Chrome tabs. - `Browser attachOnly is enabled ... not reachable` → attach-only profile has no live CDP target. Deep pages: @@ -292,7 +292,6 @@ flowchart TD - [/gateway/troubleshooting#browser-tool-fails](/gateway/troubleshooting#browser-tool-fails) - [/tools/browser-linux-troubleshooting](/tools/browser-linux-troubleshooting) - [/tools/browser-wsl2-windows-remote-cdp-troubleshooting](/tools/browser-wsl2-windows-remote-cdp-troubleshooting) - - [/tools/chrome-extension](/tools/chrome-extension) diff --git a/docs/install/docker.md b/docs/install/docker.md index a68066dcd57..a9f6b578bd0 100644 --- a/docs/install/docker.md +++ b/docs/install/docker.md @@ -51,7 +51,7 @@ From repo root: This script: - builds the gateway image locally (or pulls a remote image if `OPENCLAW_IMAGE` is set) -- runs the onboarding wizard +- runs the setup wizard - prints optional provider setup hints - starts the gateway via Docker Compose - generates a gateway token and writes it to `.env` @@ -713,6 +713,7 @@ an optional noVNC observer (headful via Xvfb). Notes: +- Docker and other headless/container browser flows stay on raw CDP. Chrome MCP `existing-session` is for host-local Chrome, not container takeover. - Headful (Xvfb) reduces bot blocking vs headless. - Headless can still be used by setting `agents.defaults.sandbox.browser.headless=true`. - No full desktop environment (GNOME) is needed; Xvfb provides the display. diff --git a/docs/install/index.md b/docs/install/index.md index 464a457a360..21adfdaa592 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -33,7 +33,7 @@ For VPS/cloud hosts, avoid third-party "1-click" marketplace images when possibl - Downloads the CLI, installs it globally via npm, and launches the onboarding wizard. + Downloads the CLI, installs it globally via npm, and launches the setup wizard. diff --git a/docs/install/updating.md b/docs/install/updating.md index e304fe0322b..a8161cc07f0 100644 --- a/docs/install/updating.md +++ b/docs/install/updating.md @@ -22,7 +22,7 @@ curl -fsSL https://openclaw.ai/install.sh | bash Notes: -- Add `--no-onboard` if you don’t want the onboarding wizard to run again. +- Add `--no-onboard` if you don’t want the setup wizard to run again. - For **source installs**, use: ```bash diff --git a/docs/platforms/raspberry-pi.md b/docs/platforms/raspberry-pi.md index 5e7e35c9544..2050b6395b4 100644 --- a/docs/platforms/raspberry-pi.md +++ b/docs/platforms/raspberry-pi.md @@ -321,7 +321,7 @@ Since the Pi is just the Gateway (models run in the cloud), use API-based models ## Auto-Start on Boot -The onboarding wizard sets this up, but to verify: +The setup wizard sets this up, but to verify: ```bash # Check service is enabled diff --git a/docs/plugins/bundles.md b/docs/plugins/bundles.md index b5f92f8f5ee..2fad626ccfe 100644 --- a/docs/plugins/bundles.md +++ b/docs/plugins/bundles.md @@ -259,12 +259,19 @@ openclaw plugins install ./my-codex-bundle openclaw plugins install ./my-claude-bundle openclaw plugins install ./my-cursor-bundle openclaw plugins install ./my-bundle.tgz +openclaw plugins marketplace list +openclaw plugins install @ openclaw plugins info my-bundle ``` If the directory is a native OpenClaw plugin/package, the native install path still wins. +For Claude marketplace names, OpenClaw reads the local Claude known-marketplace +registry at `~/.claude/plugins/known_marketplaces.json`. Marketplace entries +can resolve to bundle-compatible directories/archives or to native plugin +sources; after resolution, the normal install rules still apply. + ## Troubleshooting ### Bundle is detected but capabilities do not run diff --git a/docs/plugins/manifest.md b/docs/plugins/manifest.md index 9c266744b71..5ef77b9ef68 100644 --- a/docs/plugins/manifest.md +++ b/docs/plugins/manifest.md @@ -56,12 +56,52 @@ Optional keys: - `kind` (string): plugin kind (examples: `"memory"`, `"context-engine"`). - `channels` (array): channel ids registered by this plugin (example: `["matrix"]`). - `providers` (array): provider ids registered by this plugin. +- `providerAuthEnvVars` (object): auth env vars keyed by provider id. Use this + when OpenClaw should resolve provider credentials from env without loading + plugin runtime first. +- `providerAuthChoices` (array): cheap onboarding/auth-choice metadata keyed by + provider + auth method. Use this when OpenClaw should show a provider in + auth-choice pickers, preferred-provider resolution, and CLI help without + loading plugin runtime first. - `skills` (array): skill directories to load (relative to the plugin root). - `name` (string): display name for the plugin. - `description` (string): short plugin summary. - `uiHints` (object): config field labels/placeholders/sensitive flags for UI rendering. - `version` (string): plugin version (informational). +### `providerAuthChoices` shape + +Each entry can declare: + +- `provider`: provider id +- `method`: auth method id +- `choiceId`: stable onboarding/auth-choice id +- `choiceLabel` / `choiceHint`: picker label + short hint +- `groupId` / `groupLabel` / `groupHint`: grouped onboarding bucket metadata +- `optionKey` / `cliFlag` / `cliOption` / `cliDescription`: optional one-flag + CLI wiring for simple auth flows such as API keys + +Example: + +```json +{ + "providerAuthChoices": [ + { + "provider": "openrouter", + "method": "api-key", + "choiceId": "openrouter-api-key", + "choiceLabel": "OpenRouter API key", + "groupId": "openrouter", + "groupLabel": "OpenRouter", + "optionKey": "openrouterApiKey", + "cliFlag": "--openrouter-api-key", + "cliOption": "--openrouter-api-key ", + "cliDescription": "OpenRouter API key" + } + ] +} +``` + ## JSON Schema requirements - **Every plugin must ship a JSON Schema**, even if it accepts no config. @@ -84,6 +124,12 @@ Optional keys: - The manifest is **required for native OpenClaw plugins**, including local filesystem loads. - Runtime still loads the plugin module separately; the manifest is only for discovery + validation. +- `providerAuthEnvVars` is the cheap metadata path for auth probes, env-marker + validation, and similar provider-auth surfaces that should not boot plugin + runtime just to inspect env names. +- `providerAuthChoices` is the cheap metadata path for auth-choice pickers, + `--auth-choice` resolution, preferred-provider mapping, and simple onboarding + CLI flag registration before provider runtime loads. - Exclusive plugin kinds are selected through `plugins.slots.*`. - `kind: "memory"` is selected by `plugins.slots.memory`. - `kind: "context-engine"` is selected by `plugins.slots.contextEngine` diff --git a/docs/providers/anthropic.md b/docs/providers/anthropic.md index 8974bb2dd61..d16d76f6315 100644 --- a/docs/providers/anthropic.md +++ b/docs/providers/anthropic.md @@ -213,7 +213,7 @@ openclaw models auth paste-token --provider anthropic ### CLI setup (setup-token) ```bash -# Paste a setup-token during onboarding +# Paste a setup-token during setup openclaw onboard --auth-choice setup-token ``` diff --git a/docs/providers/huggingface.md b/docs/providers/huggingface.md index d9746d5c166..7b33955f524 100644 --- a/docs/providers/huggingface.md +++ b/docs/providers/huggingface.md @@ -64,7 +64,7 @@ GET https://router.huggingface.co/v1/models (Optional: send `Authorization: Bearer $HUGGINGFACE_HUB_TOKEN` or `$HF_TOKEN` for the full list; some endpoints return a subset without auth.) The response is OpenAI-style `{ "object": "list", "data": [ { "id": "Qwen/Qwen3-8B", "owned_by": "Qwen", ... }, ... ] }`. -When you configure a Hugging Face API key (via onboarding, `HUGGINGFACE_HUB_TOKEN`, or `HF_TOKEN`), OpenClaw uses this GET to discover available chat-completion models. During **interactive onboarding**, after you enter your token you see a **Default Hugging Face model** dropdown populated from that list (or the built-in catalog if the request fails). At runtime (e.g. Gateway startup), when a key is present, OpenClaw again calls **GET** `https://router.huggingface.co/v1/models` to refresh the catalog. The list is merged with a built-in catalog (for metadata like context window and cost). If the request fails or no key is set, only the built-in catalog is used. +When you configure a Hugging Face API key (via onboarding, `HUGGINGFACE_HUB_TOKEN`, or `HF_TOKEN`), OpenClaw uses this GET to discover available chat-completion models. During **interactive setup**, after you enter your token you see a **Default Hugging Face model** dropdown populated from that list (or the built-in catalog if the request fails). At runtime (e.g. Gateway startup), when a key is present, OpenClaw again calls **GET** `https://router.huggingface.co/v1/models` to refresh the catalog. The list is merged with a built-in catalog (for metadata like context window and cost). If the request fails or no key is set, only the built-in catalog is used. ## Model names and editable options diff --git a/docs/providers/minimax.md b/docs/providers/minimax.md index 8cdc5b028f6..0d3635352cc 100644 --- a/docs/providers/minimax.md +++ b/docs/providers/minimax.md @@ -42,7 +42,7 @@ MiniMax highlights these improvements in M2.5: Enable the bundled OAuth plugin and authenticate: ```bash -openclaw plugins enable minimax-portal-auth # skip if already loaded. +openclaw plugins enable minimax # skip if already loaded. openclaw gateway restart # restart if gateway is already running openclaw onboard --auth-choice minimax-portal ``` @@ -52,7 +52,7 @@ You will be prompted to select an endpoint: - **Global** - International users (`api.minimax.io`) - **CN** - Users in China (`api.minimaxi.com`) -See [MiniMax OAuth plugin README](https://github.com/openclaw/openclaw/tree/main/extensions/minimax-portal-auth) for details. +See [MiniMax plugin README](https://github.com/openclaw/openclaw/tree/main/extensions/minimax) for details. ### MiniMax M2.5 (API key) diff --git a/docs/providers/ollama.md b/docs/providers/ollama.md index c4604a8e350..5a1eb2bd27e 100644 --- a/docs/providers/ollama.md +++ b/docs/providers/ollama.md @@ -18,7 +18,7 @@ Ollama is a local LLM runtime that makes it easy to run open-source models on yo ### Onboarding wizard (recommended) -The fastest way to set up Ollama is through the onboarding wizard: +The fastest way to set up Ollama is through the setup wizard: ```bash openclaw onboard @@ -231,7 +231,7 @@ Once configured, all your Ollama models are available: Cloud models let you run cloud-hosted models (for example `kimi-k2.5:cloud`, `minimax-m2.5:cloud`, `glm-5:cloud`) alongside your local models. -To use cloud models, select **Cloud + Local** mode during onboarding. The wizard checks whether you are signed in and opens a browser sign-in flow when needed. If authentication cannot be verified, the wizard falls back to local model defaults. +To use cloud models, select **Cloud + Local** mode during setup. The wizard checks whether you are signed in and opens a browser sign-in flow when needed. If authentication cannot be verified, the wizard falls back to local model defaults. You can also sign in directly at [ollama.com/signin](https://ollama.com/signin). diff --git a/docs/providers/opencode.md b/docs/providers/opencode.md index bf8d54afc9e..da44e5154c0 100644 --- a/docs/providers/opencode.md +++ b/docs/providers/opencode.md @@ -59,6 +59,6 @@ openclaw onboard --opencode-go-api-key "$OPENCODE_API_KEY" ## Notes - `OPENCODE_ZEN_API_KEY` is also supported. -- Entering one OpenCode key during onboarding stores credentials for both runtime providers. +- Entering one OpenCode key during setup stores credentials for both runtime providers. - You sign in to OpenCode, add billing details, and copy your API key. - Billing and catalog availability are managed from the OpenCode dashboard. diff --git a/docs/refactor/cluster.md b/docs/refactor/cluster.md index f3b13186972..1d9c8e6f119 100644 --- a/docs/refactor/cluster.md +++ b/docs/refactor/cluster.md @@ -87,11 +87,11 @@ Risk: - Low -## 3. Onboarding prompt and config-patch steps +## 3. Setup prompt and config-patch steps Large surface area. -Many onboarding files repeat: +Many setup files repeat: - resolve account id - prompt allowlist entries @@ -102,18 +102,18 @@ Many onboarding files repeat: Strong examples: -- `extensions/bluebubbles/src/onboarding.ts` -- `extensions/googlechat/src/onboarding.ts` -- `extensions/msteams/src/onboarding.ts` -- `extensions/zalo/src/onboarding.ts` -- `extensions/zalouser/src/onboarding.ts` -- `extensions/nextcloud-talk/src/onboarding.ts` -- `extensions/matrix/src/onboarding.ts` -- `extensions/irc/src/onboarding.ts` +- `extensions/bluebubbles/src/setup-surface.ts` +- `extensions/googlechat/src/setup-surface.ts` +- `extensions/msteams/src/setup-surface.ts` +- `extensions/zalo/src/setup-surface.ts` +- `extensions/zalouser/src/setup-surface.ts` +- `extensions/nextcloud-talk/src/setup-surface.ts` +- `extensions/matrix/src/setup-surface.ts` +- `extensions/irc/src/setup-surface.ts` Existing helper seam: -- `src/channels/plugins/onboarding/helpers.ts` +- `src/channels/plugins/setup-wizard-helpers.ts` Likely extraction shape: diff --git a/docs/refactor/firecrawl-extension.md b/docs/refactor/firecrawl-extension.md new file mode 100644 index 00000000000..e25e010e7b1 --- /dev/null +++ b/docs/refactor/firecrawl-extension.md @@ -0,0 +1,260 @@ +--- +summary: "Design for an opt-in Firecrawl extension that adds search/scrape value without hardwiring Firecrawl into core defaults" +read_when: + - Designing Firecrawl integration work + - Evaluating web_search/web_fetch plugin seams + - Deciding whether Firecrawl belongs in core or as an extension +title: "Firecrawl Extension Design" +--- + +# Firecrawl Extension Design + +## Goal + +Ship Firecrawl as an **opt-in extension** that adds: + +- explicit Firecrawl tools for agents, +- optional Firecrawl-backed `web_search` integration, +- self-hosted support, +- stronger security defaults than the current core fallback path, + +without pushing Firecrawl into the default setup/onboarding path. + +## Why this shape + +Recent Firecrawl issues/PRs cluster into three buckets: + +1. **Release/schema drift** + - Several releases rejected `tools.web.fetch.firecrawl` even though docs and runtime code supported it. +2. **Security hardening** + - Current `fetchFirecrawlContent()` still posts to the Firecrawl endpoint with raw `fetch()`, while the main web-fetch path uses the SSRF guard. +3. **Product pressure** + - Users want Firecrawl-native search/scrape flows, especially for self-hosted/private setups. + - Maintainers explicitly rejected wiring Firecrawl deeply into core defaults, setup flow, and browser behavior. + +That combination argues for an extension, not more Firecrawl-specific logic in the default core path. + +## Design principles + +- **Opt-in, vendor-scoped**: no auto-enable, no setup hijack, no default tool-profile widening. +- **Extension owns Firecrawl-specific config**: prefer plugin config over growing `tools.web.*` again. +- **Useful on day one**: works even if core `web_search` / `web_fetch` seams stay unchanged. +- **Security-first**: endpoint fetches use the same guarded networking posture as other web tools. +- **Self-hosted-friendly**: config + env fallback, explicit base URL, no hosted-only assumptions. + +## Proposed extension + +Plugin id: `firecrawl` + +### MVP capabilities + +Register explicit tools: + +- `firecrawl_search` +- `firecrawl_scrape` + +Optional later: + +- `firecrawl_crawl` +- `firecrawl_map` + +Do **not** add Firecrawl browser automation in the first version. That was the part of PR #32543 that pulled Firecrawl too far into core behavior and raised the most maintainership concern. + +## Config shape + +Use plugin-scoped config: + +```json5 +{ + plugins: { + entries: { + firecrawl: { + enabled: true, + config: { + apiKey: "FIRECRAWL_API_KEY", + baseUrl: "https://api.firecrawl.dev", + timeoutSeconds: 60, + maxAgeMs: 172800000, + proxy: "auto", + storeInCache: true, + onlyMainContent: true, + search: { + enabled: true, + defaultLimit: 5, + sources: ["web"], + categories: [], + scrapeResults: false, + }, + scrape: { + formats: ["markdown"], + fallbackForWebFetchLikeUse: false, + }, + }, + }, + }, + }, +} +``` + +### Credential resolution + +Precedence: + +1. `plugins.entries.firecrawl.config.apiKey` +2. `FIRECRAWL_API_KEY` + +Base URL precedence: + +1. `plugins.entries.firecrawl.config.baseUrl` +2. `FIRECRAWL_BASE_URL` +3. `https://api.firecrawl.dev` + +### Compatibility bridge + +For the first release, the extension may also **read** existing core config at `tools.web.fetch.firecrawl.*` as a fallback source so existing users do not need to migrate immediately. + +Write path stays plugin-local. Do not keep expanding core Firecrawl config surfaces. + +## Tool design + +### `firecrawl_search` + +Inputs: + +- `query` +- `limit` +- `sources` +- `categories` +- `scrapeResults` +- `timeoutSeconds` + +Behavior: + +- Calls Firecrawl `v2/search` +- Returns normalized OpenClaw-friendly result objects: + - `title` + - `url` + - `snippet` + - `source` + - optional `content` +- Wraps result content as untrusted external content +- Cache key includes query + relevant provider params + +Why explicit tool first: + +- Works today without changing `tools.web.search.provider` +- Avoids current schema/loader constraints +- Gives users Firecrawl value immediately + +### `firecrawl_scrape` + +Inputs: + +- `url` +- `formats` +- `onlyMainContent` +- `maxAgeMs` +- `proxy` +- `storeInCache` +- `timeoutSeconds` + +Behavior: + +- Calls Firecrawl `v2/scrape` +- Returns markdown/text plus metadata: + - `title` + - `finalUrl` + - `status` + - `warning` +- Wraps extracted content the same way `web_fetch` does +- Shares cache semantics with web tool expectations where practical + +Why explicit scrape tool: + +- Sidesteps the unresolved `Readability -> Firecrawl -> basic HTML cleanup` ordering bug in core `web_fetch` +- Gives users a deterministic “always use Firecrawl” path for JS-heavy/bot-protected sites + +## What the extension should not do + +- No auto-adding `browser`, `web_search`, or `web_fetch` to `tools.alsoAllow` +- No default onboarding step in `openclaw setup` +- No Firecrawl-specific browser session lifecycle in core +- No change to built-in `web_fetch` fallback semantics in the extension MVP + +## Phase plan + +### Phase 1: extension-only, no core schema changes + +Implement: + +- `extensions/firecrawl/` +- plugin config schema +- `firecrawl_search` +- `firecrawl_scrape` +- tests for config resolution, endpoint selection, caching, error handling, and SSRF guard usage + +This phase is enough to ship real user value. + +### Phase 2: optional `web_search` provider integration + +Support `tools.web.search.provider = "firecrawl"` only after fixing two core constraints: + +1. `src/plugins/web-search-providers.ts` must load configured/installed web-search-provider plugins instead of a hardcoded bundled list. +2. `src/config/types.tools.ts` and `src/config/zod-schema.agent-runtime.ts` must stop hardcoding the provider enum in a way that blocks plugin-registered ids. + +Recommended shape: + +- keep built-in providers documented, +- allow any registered plugin provider id at runtime, +- validate provider-specific config via the provider plugin or a generic provider bag. + +### Phase 3: optional `web_fetch` provider seam + +Do this only if maintainers want vendor-specific fetch backends to participate in `web_fetch`. + +Needed core addition: + +- `registerWebFetchProvider` or equivalent fetch-backend seam + +Without that seam, the extension should keep `firecrawl_scrape` as an explicit tool rather than trying to patch built-in `web_fetch`. + +## Security requirements + +The extension must treat Firecrawl as a **trusted operator-configured endpoint**, but still harden transport: + +- Use SSRF-guarded fetch for the Firecrawl endpoint call, not raw `fetch()` +- Preserve self-hosted/private-network compatibility using the same trusted-web-tools endpoint policy used elsewhere +- Never log the API key +- Keep endpoint/base URL resolution explicit and predictable +- Treat Firecrawl-returned content as untrusted external content + +This mirrors the intent behind the SSRF hardening PRs without assuming Firecrawl is a hostile multi-tenant surface. + +## Why not a skill + +The repo already closed a Firecrawl skill PR in favor of ClawHub distribution. That is fine for optional user-installed prompt workflows, but it does not solve: + +- deterministic tool availability, +- provider-grade config/credential handling, +- self-hosted endpoint support, +- caching, +- stable typed outputs, +- security review on network behavior. + +This belongs as an extension, not a prompt-only skill. + +## Success criteria + +- Users can install/enable one extension and get reliable Firecrawl search/scrape without touching core defaults. +- Self-hosted Firecrawl works with config/env fallback. +- Extension endpoint fetches use guarded networking. +- No new Firecrawl-specific core onboarding/default behavior. +- Core can later adopt plugin-native `web_search` / `web_fetch` seams without redesigning the extension. + +## Recommended implementation order + +1. Build `firecrawl_scrape` +2. Build `firecrawl_search` +3. Add docs and examples +4. If desired, generalize `web_search` provider loading so the extension can back `web_search` +5. Only then consider a true `web_fetch` provider seam diff --git a/docs/refactor/plugin-sdk.md b/docs/refactor/plugin-sdk.md index 4722644083b..5a630982a97 100644 --- a/docs/refactor/plugin-sdk.md +++ b/docs/refactor/plugin-sdk.md @@ -28,7 +28,7 @@ Contents (examples): - Config helpers: `buildChannelConfigSchema`, `setAccountEnabledInConfigSection`, `deleteAccountFromConfigSection`, `applyAccountNameToChannelSection`. - Pairing helpers: `PAIRING_APPROVED_MESSAGE`, `formatPairingApproveHint`. -- Onboarding helpers: `promptChannelAccessConfig`, `addWildcardAllowFrom`, onboarding types. +- Setup entry points: host-owned `setup` + `setupWizard`; avoid broad public onboarding helpers. - Tool param helpers: `createActionGate`, `readStringParam`, `readNumberParam`, `readReactionParams`, `jsonResult`. - Docs link helper: `formatDocsLink`. @@ -212,3 +212,27 @@ Notes: - External plugins can be developed and updated without core source access. Related docs: [Plugins](/tools/plugin), [Channels](/channels/index), [Configuration](/gateway/configuration). + +## Implemented channel-owned seams + +Recent refactor work widened the channel plugin contract so core can stop owning +channel-specific UX and routing behavior: + +- `messaging.buildCrossContextComponents`: channel-owned cross-context UI markers + (for example Discord components v2 containers) +- `messaging.enableInteractiveReplies`: channel-owned reply normalization toggles + (for example Slack interactive replies) +- `messaging.resolveOutboundSessionRoute`: channel-owned outbound session routing +- `status.formatCapabilitiesProbe` / `status.buildCapabilitiesDiagnostics`: channel-owned + `/channels capabilities` probe display and extra audits/scopes +- `threading.resolveAutoThreadId`: channel-owned same-conversation auto-threading +- `threading.resolveReplyTransport`: channel-owned reply-vs-thread delivery mapping +- `actions.requiresTrustedRequesterSender`: channel-owned privileged action trust gates +- `execApprovals.*`: channel-owned exec approval surface state, forwarding suppression, + pending payload UX, and pre-delivery hooks +- `lifecycle.onAccountConfigChanged` / `lifecycle.onAccountRemoved`: channel-owned cleanup on + config mutation/removal +- `allowlist.supportsScope`: channel-owned allowlist scope advertisement + +These hooks should be preferred over new `channel === "discord"` / `telegram` +branches in shared core flows. diff --git a/docs/reference/wizard.md b/docs/reference/wizard.md index bbaebbdc84f..5bfa3da7f9f 100644 --- a/docs/reference/wizard.md +++ b/docs/reference/wizard.md @@ -1,17 +1,17 @@ --- -summary: "Full reference for the CLI onboarding wizard: every step, flag, and config field" +summary: "Full reference for the CLI setup wizard: every step, flag, and config field" read_when: - Looking up a specific wizard step or flag - Automating onboarding with non-interactive mode - Debugging wizard behavior -title: "Onboarding Wizard Reference" +title: "Setup Wizard Reference" sidebarTitle: "Wizard Reference" --- -# Onboarding Wizard Reference +# Setup Wizard Reference This is the full reference for the `openclaw onboard` CLI wizard. -For a high-level overview, see [Onboarding Wizard](/start/wizard). +For a high-level overview, see [Setup Wizard](/start/wizard). ## Flow details (local mode) @@ -73,12 +73,12 @@ For a high-level overview, see [Onboarding Wizard](/start/wizard). - Port, bind, auth mode, tailscale exposure. - Auth recommendation: keep **Token** even for loopback so local WS clients must authenticate. - - In token mode, interactive onboarding offers: + - In token mode, interactive setup offers: - **Generate/store plaintext token** (default) - **Use SecretRef** (opt-in) - Quickstart reuses existing `gateway.auth.token` SecretRefs across `env`, `file`, and `exec` providers for onboarding probe/dashboard bootstrap. - If that SecretRef is configured but cannot be resolved, onboarding fails early with a clear fix message instead of silently degrading runtime auth. - - In password mode, interactive onboarding also supports plaintext or SecretRef storage. + - In password mode, interactive setup also supports plaintext or SecretRef storage. - Non-interactive token SecretRef path: `--gateway-token-ref-env `. - Requires a non-empty env var in the onboarding process environment. - Cannot be combined with `--gateway-token`. @@ -208,7 +208,7 @@ Typical fields in `~/.openclaw/openclaw.json`: - `agents.defaults.model` / `models.providers` (if Minimax chosen) - `tools.profile` (local onboarding defaults to `"coding"` when unset; existing explicit values are preserved) - `gateway.*` (mode, bind, auth, tailscale) -- `session.dmScope` (behavior details: [CLI Onboarding Reference](/start/wizard-cli-reference#outputs-and-internals)) +- `session.dmScope` (behavior details: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals)) - `channels.telegram.botToken`, `channels.discord.token`, `channels.signal.*`, `channels.imessage.*` - Channel allowlists (Slack/Discord/Matrix/Microsoft Teams) when you opt in during the prompts (names resolve to IDs when possible). - `skills.install.nodeManager` @@ -223,12 +223,12 @@ Typical fields in `~/.openclaw/openclaw.json`: WhatsApp credentials go under `~/.openclaw/credentials/whatsapp//`. Sessions are stored under `~/.openclaw/agents//sessions/`. -Some channels are delivered as plugins. When you pick one during onboarding, the wizard +Some channels are delivered as plugins. When you pick one during setup, the wizard will prompt to install it (npm or a local path) before it can be configured. ## Related docs -- Wizard overview: [Onboarding Wizard](/start/wizard) +- Wizard overview: [Setup Wizard](/start/wizard) - macOS app onboarding: [Onboarding](/start/onboarding) - Config reference: [Gateway configuration](/gateway/configuration) - Providers: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord), [Google Chat](/channels/googlechat), [Signal](/channels/signal), [BlueBubbles](/channels/bluebubbles) (iMessage), [iMessage](/channels/imessage) (legacy) diff --git a/docs/start/getting-started.md b/docs/start/getting-started.md index 26b54b63f6f..3fc64e5087d 100644 --- a/docs/start/getting-started.md +++ b/docs/start/getting-started.md @@ -52,13 +52,13 @@ Check your Node version with `node --version` if you are unsure. - + ```bash openclaw onboard --install-daemon ``` The wizard configures auth, gateway settings, and optional channels. - See [Onboarding Wizard](/start/wizard) for details. + See [Setup Wizard](/start/wizard) for details. @@ -114,7 +114,7 @@ Full environment variable reference: [Environment vars](/help/environment). ## Go deeper - + Full CLI wizard reference and advanced options. diff --git a/docs/start/onboarding-overview.md b/docs/start/onboarding-overview.md index 6227cdc104b..1e94a4db64a 100644 --- a/docs/start/onboarding-overview.md +++ b/docs/start/onboarding-overview.md @@ -17,7 +17,7 @@ and how you prefer to configure providers. - **CLI wizard** for macOS, Linux, and Windows (via WSL2). - **macOS app** for a guided first run on Apple silicon or Intel Macs. -## CLI onboarding wizard +## CLI setup wizard Run the wizard in a terminal: @@ -28,7 +28,7 @@ openclaw onboard Use the CLI wizard when you want full control of the Gateway, workspace, channels, and skills. Docs: -- [Onboarding Wizard (CLI)](/start/wizard) +- [Setup Wizard (CLI)](/start/wizard) - [`openclaw onboard` command](/cli/onboard) ## macOS app onboarding diff --git a/docs/start/onboarding.md b/docs/start/onboarding.md index 3e3401cad64..c1dfb90b676 100644 --- a/docs/start/onboarding.md +++ b/docs/start/onboarding.md @@ -1,5 +1,5 @@ --- -summary: "First-run onboarding flow for OpenClaw (macOS app)" +summary: "First-run setup flow for OpenClaw (macOS app)" read_when: - Designing the macOS onboarding assistant - Implementing auth or identity setup @@ -9,7 +9,7 @@ sidebarTitle: "Onboarding: macOS App" # Onboarding (macOS App) -This doc describes the **current** first‑run onboarding flow. The goal is a +This doc describes the **current** first‑run setup flow. The goal is a smooth “day 0” experience: pick where the Gateway runs, connect auth, run the wizard, and let the agent bootstrap itself. For a general overview of onboarding paths, see [Onboarding Overview](/start/onboarding-overview). diff --git a/docs/start/wizard-cli-automation.md b/docs/start/wizard-cli-automation.md index cd00787c5c7..884d49e143b 100644 --- a/docs/start/wizard-cli-automation.md +++ b/docs/start/wizard-cli-automation.md @@ -33,7 +33,7 @@ openclaw onboard --non-interactive \ Add `--json` for a machine-readable summary. Use `--secret-input-mode ref` to store env-backed refs in auth profiles instead of plaintext values. -Interactive selection between env refs and configured provider refs (`file` or `exec`) is available in the onboarding wizard flow. +Interactive selection between env refs and configured provider refs (`file` or `exec`) is available in the setup wizard flow. In non-interactive `ref` mode, provider env vars must be set in the process environment. Passing inline key flags without the matching env var now fails fast. @@ -210,6 +210,6 @@ Notes: ## Related docs -- Onboarding hub: [Onboarding Wizard (CLI)](/start/wizard) -- Full reference: [CLI Onboarding Reference](/start/wizard-cli-reference) +- Onboarding hub: [Setup Wizard (CLI)](/start/wizard) +- Full reference: [CLI Setup Reference](/start/wizard-cli-reference) - Command reference: [`openclaw onboard`](/cli/onboard) diff --git a/docs/start/wizard-cli-reference.md b/docs/start/wizard-cli-reference.md index 5d3e6be6e72..36bd836a13f 100644 --- a/docs/start/wizard-cli-reference.md +++ b/docs/start/wizard-cli-reference.md @@ -1,16 +1,16 @@ --- -summary: "Complete reference for CLI onboarding flow, auth/model setup, outputs, and internals" +summary: "Complete reference for CLI setup flow, auth/model setup, outputs, and internals" read_when: - You need detailed behavior for openclaw onboard - You are debugging onboarding results or integrating onboarding clients -title: "CLI Onboarding Reference" +title: "CLI Setup Reference" sidebarTitle: "CLI reference" --- -# CLI Onboarding Reference +# CLI Setup Reference This page is the full reference for `openclaw onboard`. -For the short guide, see [Onboarding Wizard (CLI)](/start/wizard). +For the short guide, see [Setup Wizard (CLI)](/start/wizard). ## What the wizard does @@ -51,10 +51,10 @@ It does not install or modify anything on the remote host. - Prompts for port, bind, auth mode, and tailscale exposure. - Recommended: keep token auth enabled even for loopback so local WS clients must authenticate. - - In token mode, interactive onboarding offers: + - In token mode, interactive setup offers: - **Generate/store plaintext token** (default) - **Use SecretRef** (opt-in) - - In password mode, interactive onboarding also supports plaintext or SecretRef storage. + - In password mode, interactive setup also supports plaintext or SecretRef storage. - Non-interactive token SecretRef path: `--gateway-token-ref-env `. - Requires a non-empty env var in the onboarding process environment. - Cannot be combined with `--gateway-token`. @@ -222,7 +222,7 @@ Credential storage mode: - Default onboarding behavior persists API keys as plaintext values in auth profiles. - `--secret-input-mode ref` enables reference mode instead of plaintext key storage. - In interactive onboarding, you can choose either: + In interactive setup, you can choose either: - environment variable ref (for example `keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" }`) - configured provider ref (`file` or `exec`) with provider alias + id - Interactive reference mode runs a fast preflight validation before saving. @@ -234,7 +234,7 @@ Credential storage mode: - Inline key flags (for example `--openai-api-key`) require that env var to be set; otherwise onboarding fails fast. - For custom providers, non-interactive `ref` mode stores `models.providers..apiKey` as `{ source: "env", provider: "default", id: "CUSTOM_API_KEY" }`. - In that custom-provider case, `--custom-api-key` requires `CUSTOM_API_KEY` to be set; otherwise onboarding fails fast. -- Gateway auth credentials support plaintext and SecretRef choices in interactive onboarding: +- Gateway auth credentials support plaintext and SecretRef choices in interactive setup: - Token mode: **Generate/store plaintext token** (default) or **Use SecretRef**. - Password mode: plaintext or SecretRef. - Non-interactive token SecretRef path: `--gateway-token-ref-env `. @@ -270,7 +270,7 @@ WhatsApp credentials go under `~/.openclaw/credentials/whatsapp//`. Sessions are stored under `~/.openclaw/agents//sessions/`. -Some channels are delivered as plugins. When selected during onboarding, the wizard +Some channels are delivered as plugins. When selected during setup, the wizard prompts to install the plugin (npm or local path) before channel configuration. @@ -294,6 +294,6 @@ Signal setup behavior: ## Related docs -- Onboarding hub: [Onboarding Wizard (CLI)](/start/wizard) +- Onboarding hub: [Setup Wizard (CLI)](/start/wizard) - Automation and scripts: [CLI Automation](/start/wizard-cli-automation) - Command reference: [`openclaw onboard`](/cli/onboard) diff --git a/docs/start/wizard.md b/docs/start/wizard.md index 05c09ed53fd..7bbe9df64cf 100644 --- a/docs/start/wizard.md +++ b/docs/start/wizard.md @@ -1,15 +1,15 @@ --- -summary: "CLI onboarding wizard: guided setup for gateway, workspace, channels, and skills" +summary: "CLI setup wizard: guided setup for gateway, workspace, channels, and skills" read_when: - - Running or configuring the onboarding wizard + - Running or configuring the setup wizard - Setting up a new machine -title: "Onboarding Wizard (CLI)" +title: "Setup Wizard (CLI)" sidebarTitle: "Onboarding: CLI" --- -# Onboarding Wizard (CLI) +# Setup Wizard (CLI) -The onboarding wizard is the **recommended** way to set up OpenClaw on macOS, +The setup wizard is the **recommended** way to set up OpenClaw on macOS, Linux, or Windows (via WSL2; strongly recommended). It configures a local Gateway or a remote Gateway connection, plus channels, skills, and workspace defaults in one guided flow. @@ -35,7 +35,7 @@ openclaw agents add -The onboarding wizard includes a web search step where you can pick a provider +The setup wizard includes a web search step where you can pick a provider (Perplexity, Brave, Gemini, Grok, or Kimi) and paste your API key so the agent can use `web_search`. You can also configure this later with `openclaw configure --section web`. Docs: [Web tools](/tools/web). @@ -52,7 +52,7 @@ The wizard starts with **QuickStart** (defaults) vs **Advanced** (full control). - Gateway port **18789** - Gateway auth **Token** (auto‑generated, even on loopback) - Tool policy default for new local setups: `tools.profile: "coding"` (existing explicit profile is preserved) - - DM isolation default: local onboarding writes `session.dmScope: "per-channel-peer"` when unset. Details: [CLI Onboarding Reference](/start/wizard-cli-reference#outputs-and-internals) + - DM isolation default: local onboarding writes `session.dmScope: "per-channel-peer"` when unset. Details: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals) - Tailscale exposure **Off** - Telegram + WhatsApp DMs default to **allowlist** (you'll be prompted for your phone number) @@ -112,7 +112,7 @@ Notes: ## Full reference For detailed step-by-step breakdowns and config outputs, see -[CLI Onboarding Reference](/start/wizard-cli-reference). +[CLI Setup Reference](/start/wizard-cli-reference). For non-interactive examples, see [CLI Automation](/start/wizard-cli-automation). For the deeper technical reference, including RPC details, see [Wizard Reference](/reference/wizard). diff --git a/docs/tools/browser-linux-troubleshooting.md b/docs/tools/browser-linux-troubleshooting.md index 6f9940c1c67..2a5196c3739 100644 --- a/docs/tools/browser-linux-troubleshooting.md +++ b/docs/tools/browser-linux-troubleshooting.md @@ -121,19 +121,18 @@ curl -s http://127.0.0.1:18791/tabs | `browser.attachOnly` | Don't launch browser, only attach to existing | `false` | | `browser.cdpPort` | Chrome DevTools Protocol port | `18800` | -### Problem: "Chrome extension relay is running, but no tab is connected" +### Problem: "No Chrome tabs found for profile=\"user\"" -You're using an extension relay profile. It expects the OpenClaw -browser extension to be attached to a live tab. +You're using an `existing-session` / Chrome MCP profile. OpenClaw can see local Chrome, +but there are no open tabs available to attach to. Fix options: 1. **Use the managed browser:** `openclaw browser start --browser-profile openclaw` (or set `browser.defaultProfile: "openclaw"`). -2. **Use the extension relay:** install the extension, open a tab, and click the - OpenClaw extension icon to attach it. +2. **Use Chrome MCP:** make sure local Chrome is running with at least one open tab, then retry with `--browser-profile user`. Notes: -- The `chrome-relay` profile uses your **system default Chromium browser** when possible. +- `user` is host-only. For Linux servers, containers, or remote hosts, prefer CDP profiles. - Local `openclaw` profiles auto-assign `cdpPort`/`cdpUrl`; only set those for remote CDP. diff --git a/docs/tools/browser-login.md b/docs/tools/browser-login.md index d570b3b2e87..52135a80673 100644 --- a/docs/tools/browser-login.md +++ b/docs/tools/browser-login.md @@ -24,7 +24,6 @@ For agent browser tool calls: - Default choice: the agent should use its isolated `openclaw` browser. - Use `profile="user"` only when existing logged-in sessions matter and the user is at the computer to click/approve any attach prompt. -- Use `profile="chrome-relay"` only for the Chrome extension / toolbar-button attach flow. - If you have multiple user-browser profiles, specify the profile explicitly instead of guessing. Two easy ways to access it: diff --git a/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md b/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md index 2e7844860aa..6824cee6788 100644 --- a/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md +++ b/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md @@ -1,9 +1,9 @@ --- -summary: "Troubleshoot WSL2 Gateway + Windows Chrome remote CDP and extension-relay setups in layers" +summary: "Troubleshoot WSL2 Gateway + Windows Chrome remote CDP in layers" read_when: - Running OpenClaw Gateway in WSL2 while Chrome lives on Windows - Seeing overlapping browser/control-ui errors across WSL2 and Windows - - Deciding between raw remote CDP and the Chrome extension relay in split-host setups + - Deciding between host-local Chrome MCP and raw remote CDP in split-host setups title: "WSL2 + Windows + remote Chrome CDP troubleshooting" --- @@ -21,27 +21,27 @@ It also covers the layered failure pattern from [issue #39369](https://github.co You have two valid patterns: -### Option 1: Raw remote CDP +### Option 1: Raw remote CDP from WSL2 to Windows Use a remote browser profile that points from WSL2 to a Windows Chrome CDP endpoint. Choose this when: -- you only need browser control -- you are comfortable exposing Chrome remote debugging to WSL2 -- you do not need the Chrome extension relay +- the Gateway stays inside WSL2 +- Chrome runs on Windows +- you need browser control to cross the WSL2/Windows boundary -### Option 2: Chrome extension relay +### Option 2: Host-local Chrome MCP -Use the built-in `chrome-relay` profile plus the OpenClaw Chrome extension. +Use `existing-session` / `user` only when the Gateway itself runs on the same host as Chrome. Choose this when: -- you want to attach to an existing Windows Chrome tab with the toolbar button -- you want extension-based control instead of raw `--remote-debugging-port` -- the relay itself must be reachable across the WSL2/Windows boundary +- OpenClaw and Chrome are on the same machine +- you want the local signed-in browser state +- you do not need cross-host browser transport -If you use the extension relay across namespaces, `browser.relayBindHost` is the important setting introduced in [Browser](/tools/browser) and [Chrome extension](/tools/chrome-extension). +For WSL2 Gateway + Windows Chrome, prefer raw remote CDP. Chrome MCP is host-local, not a WSL2-to-Windows bridge. ## Working architecture @@ -62,7 +62,6 @@ Several failures can overlap: - `gateway.controlUi.allowedOrigins` does not match the page origin - token or pairing is missing - the browser profile points at the wrong address -- the extension relay is still loopback-only when you actually need cross-namespace access Because of that, fixing one layer can still leave a different error visible. @@ -145,31 +144,7 @@ Notes: - keep `attachOnly: true` for externally managed browsers - test the same URL with `curl` before expecting OpenClaw to succeed -### Layer 4: If you use the Chrome extension relay instead - -If the browser machine and the Gateway are separated by a namespace boundary, the relay may need a non-loopback bind address. - -Example: - -```json5 -{ - browser: { - enabled: true, - defaultProfile: "chrome-relay", - relayBindHost: "0.0.0.0", - }, -} -``` - -Use this only when needed: - -- default behavior is safer because the relay stays loopback-only -- `0.0.0.0` expands exposure surface -- keep Gateway auth, node pairing, and the surrounding network private - -If you do not need the extension relay, prefer the raw remote CDP profile above. - -### Layer 5: Verify the Control UI layer separately +### Layer 4: Verify the Control UI layer separately Open the UI from Windows: @@ -185,7 +160,7 @@ Helpful page: - [Control UI](/web/control-ui) -### Layer 6: Verify end-to-end browser control +### Layer 5: Verify end-to-end browser control From WSL2: @@ -194,12 +169,6 @@ openclaw browser open https://example.com --browser-profile remote openclaw browser tabs --browser-profile remote ``` -For the extension relay: - -```bash -openclaw browser tabs --browser-profile chrome-relay -``` - Good result: - the tab opens in Windows Chrome @@ -220,8 +189,8 @@ Treat each message as a layer-specific clue: - WSL2 cannot reach the configured `cdpUrl` - `gateway timeout after 1500ms` - often still CDP reachability or a slow/unreachable remote endpoint -- `Chrome extension relay is running, but no tab is connected` - - extension relay profile selected, but no attached tab exists yet +- `No Chrome tabs found for profile="user"` + - local Chrome MCP profile selected where no host-local tabs are available ## Fast triage checklist @@ -229,11 +198,11 @@ Treat each message as a layer-specific clue: 2. WSL2: does `curl http://WINDOWS_HOST_OR_IP:9222/json/version` work? 3. OpenClaw config: does `browser.profiles..cdpUrl` use that exact WSL2-reachable address? 4. Control UI: are you opening `http://127.0.0.1:18789/` instead of a LAN IP? -5. Extension relay only: do you actually need `browser.relayBindHost`, and if so is it set explicitly? +5. Are you trying to use `existing-session` across WSL2 and Windows instead of raw remote CDP? ## Practical takeaway -The setup is usually viable. The hard part is that browser transport, Control UI origin security, token/pairing, and extension-relay topology can each fail independently while looking similar from the user side. +The setup is usually viable. The hard part is that browser transport, Control UI origin security, and token/pairing can each fail independently while looking similar from the user side. When in doubt: diff --git a/docs/tools/browser.md b/docs/tools/browser.md index c760c23998c..19ee23a25ca 100644 --- a/docs/tools/browser.md +++ b/docs/tools/browser.md @@ -18,8 +18,7 @@ Beginner view: - Think of it as a **separate, agent-only browser**. - The `openclaw` profile does **not** touch your personal browser profile. - The agent can **open tabs, read pages, click, and type** in a safe lane. -- The built-in `user` profile attaches to your real signed-in Chrome session; - `chrome-relay` is the explicit extension-relay profile. +- The built-in `user` profile attaches to your real signed-in Chrome session via Chrome MCP. ## What you get @@ -43,21 +42,17 @@ openclaw browser --browser-profile openclaw snapshot If you get “Browser disabled”, enable it in config (see below) and restart the Gateway. -## Profiles: `openclaw` vs `user` vs `chrome-relay` +## Profiles: `openclaw` vs `user` - `openclaw`: managed, isolated browser (no extension required). - `user`: built-in Chrome MCP attach profile for your **real signed-in Chrome** session. -- `chrome-relay`: extension relay to your **system browser** (requires the - OpenClaw extension to be attached to a tab). For agent browser tool calls: - Default: use the isolated `openclaw` browser. - Prefer `profile="user"` when existing logged-in sessions matter and the user is at the computer to click/approve any attach prompt. -- Use `profile="chrome-relay"` only when the user explicitly wants the Chrome - extension / toolbar-button attach flow. - `profile` is the explicit override when you want a specific browser mode. Set `browser.defaultProfile: "openclaw"` if you want managed mode by default. @@ -93,11 +88,6 @@ Browser settings live in `~/.openclaw/openclaw.json`. attachOnly: true, color: "#00AA00", }, - "chrome-relay": { - driver: "extension", - cdpUrl: "http://127.0.0.1:18792", - color: "#00AA00", - }, remote: { cdpUrl: "http://10.0.0.42:9222", color: "#00AA00" }, }, }, @@ -107,10 +97,10 @@ Browser settings live in `~/.openclaw/openclaw.json`. Notes: - The browser control service binds to loopback on a port derived from `gateway.port` - (default: `18791`, which is gateway + 2). The relay uses the next port (`18792`). + (default: `18791`, which is gateway + 2). - If you override the Gateway port (`gateway.port` or `OPENCLAW_GATEWAY_PORT`), the derived browser ports shift to stay in the same “family”. -- `cdpUrl` defaults to the relay port when unset. +- `cdpUrl` defaults to the managed local CDP port when unset. - `remoteCdpTimeoutMs` applies to remote (non-loopback) CDP reachability checks. - `remoteCdpHandshakeTimeoutMs` applies to remote CDP WebSocket reachability checks. - Browser navigation/open-tab is SSRF-guarded before navigation and best-effort re-checked on final `http(s)` URL after navigation. @@ -119,7 +109,7 @@ Notes: - `browser.ssrfPolicy.allowPrivateNetwork` remains supported as a legacy alias for compatibility. - `attachOnly: true` means “never launch a local browser; only attach if it is already running.” - `color` + per-profile `color` tint the browser UI so you can see which profile is active. -- Default profile is `openclaw` (OpenClaw-managed standalone browser). Use `defaultProfile: "user"` to opt into the signed-in user browser, or `defaultProfile: "chrome-relay"` for the extension relay. +- Default profile is `openclaw` (OpenClaw-managed standalone browser). Use `defaultProfile: "user"` to opt into the signed-in user browser. - Auto-detect order: system default browser if Chromium-based; otherwise Chrome → Brave → Edge → Chromium → Chrome Canary. - Local `openclaw` profiles auto-assign `cdpPort`/`cdpUrl` — set those only for remote CDP. - `driver: "existing-session"` uses Chrome DevTools MCP instead of raw CDP. Do @@ -287,77 +277,18 @@ OpenClaw supports multiple named profiles (routing configs). Profiles can be: - **openclaw-managed**: a dedicated Chromium-based browser instance with its own user data directory + CDP port - **remote**: an explicit CDP URL (Chromium-based browser running elsewhere) -- **extension relay**: your existing Chrome tab(s) via the local relay + Chrome extension - **existing session**: your existing Chrome profile via Chrome DevTools MCP auto-connect Defaults: - The `openclaw` profile is auto-created if missing. -- The `chrome-relay` profile is built-in for the Chrome extension relay (points at `http://127.0.0.1:18792` by default). -- Existing-session profiles are opt-in; create them with `--driver existing-session`. +- The `user` profile is built-in for Chrome MCP existing-session attach. +- Existing-session profiles are opt-in beyond `user`; create them with `--driver existing-session`. - Local CDP ports allocate from **18800–18899** by default. - Deleting a profile moves its local data directory to Trash. All control endpoints accept `?profile=`; the CLI uses `--browser-profile`. -## Chrome extension relay (use your existing Chrome) - -OpenClaw can also drive **your existing Chrome tabs** (no separate “openclaw” Chrome instance) via a local CDP relay + a Chrome extension. - -Full guide: [Chrome extension](/tools/chrome-extension) - -Flow: - -- The Gateway runs locally (same machine) or a node host runs on the browser machine. -- A local **relay server** listens at a loopback `cdpUrl` (default: `http://127.0.0.1:18792`). -- You click the **OpenClaw Browser Relay** extension icon on a tab to attach (it does not auto-attach). -- The agent controls that tab via the normal `browser` tool, by selecting the right profile. - -If the Gateway runs elsewhere, run a node host on the browser machine so the Gateway can proxy browser actions. - -### Sandboxed sessions - -If the agent session is sandboxed, the `browser` tool may default to `target="sandbox"` (sandbox browser). -Chrome extension relay takeover requires host browser control, so either: - -- run the session unsandboxed, or -- set `agents.defaults.sandbox.browser.allowHostControl: true` and use `target="host"` when calling the tool. - -### Setup - -1. Load the extension (dev/unpacked): - -```bash -openclaw browser extension install -``` - -- Chrome → `chrome://extensions` → enable “Developer mode” -- “Load unpacked” → select the directory printed by `openclaw browser extension path` -- Pin the extension, then click it on the tab you want to control (badge shows `ON`). - -2. Use it: - -- CLI: `openclaw browser --browser-profile chrome-relay tabs` -- Agent tool: `browser` with `profile="chrome-relay"` - -Optional: if you want a different name or relay port, create your own profile: - -```bash -openclaw browser create-profile \ - --name my-chrome \ - --driver extension \ - --cdp-url http://127.0.0.1:18792 \ - --color "#00AA00" -``` - -Notes: - -- This mode relies on Playwright-on-CDP for most operations (screenshots/snapshots/actions). -- Detach by clicking the extension icon again. -- Agent use: prefer `profile="user"` for logged-in sites. Use `profile="chrome-relay"` - only when you specifically want the extension flow. The user must be present - to click the extension and attach the tab. - ## Chrome existing-session via MCP OpenClaw can also attach to a running Chrome profile through the official @@ -404,13 +335,14 @@ What to check if attach does not work: - Chrome is version `144+` - remote debugging is enabled at `chrome://inspect/#remote-debugging` - Chrome showed and you accepted the attach consent prompt +- `openclaw doctor` migrates old extension-based browser config and checks that + Chrome is installed locally with a compatible version, but it cannot enable + Chrome-side remote debugging for you Agent use: - Use `profile="user"` when you need the user’s logged-in browser state. - If you use a custom existing-session profile, pass that explicit profile name. -- Prefer `profile="user"` over `profile="chrome-relay"` unless the user - explicitly wants the extension / attach-tab flow. - Only choose this mode when the user is at the computer to approve the attach prompt. - the Gateway or node host can spawn `npx chrome-devtools-mcp@latest --autoConnect` @@ -427,21 +359,10 @@ Notes: captures from snapshots, but not CSS `--element` selectors. - Existing-session `wait --url` supports exact, substring, and glob patterns like other browser drivers. `wait --load networkidle` is not supported yet. -- Some features still require the extension relay or managed browser path, such - as PDF export and download interception. -- Leave the relay loopback-only by default. If the relay must be reachable from a different network namespace (for example Gateway in WSL2, Chrome on Windows), set `browser.relayBindHost` to an explicit bind address such as `0.0.0.0` while keeping the surrounding network private and authenticated. - -WSL2 / cross-namespace example: - -```json5 -{ - browser: { - enabled: true, - relayBindHost: "0.0.0.0", - defaultProfile: "chrome-relay", - }, -} -``` +- Some features still require the managed browser path, such as PDF export and + download interception. +- Existing-session is host-local. If Chrome lives on a different machine or a + different network namespace, use remote CDP or a node host instead. ## Isolation guarantees @@ -496,7 +417,6 @@ If gateway auth is configured, browser HTTP routes require auth too: Some features (navigate/act/AI snapshot/role snapshot, element screenshots, PDF) require Playwright. If Playwright isn’t installed, those endpoints return a clear 501 error. ARIA snapshots and basic screenshots still work for openclaw-managed Chrome. -For the Chrome extension relay driver, ARIA snapshots and screenshots require Playwright. If you see `Playwright is not available in this gateway build`, install the full Playwright package (not `playwright-core`) and restart the gateway, or reinstall diff --git a/docs/tools/chrome-extension.md b/docs/tools/chrome-extension.md deleted file mode 100644 index 831897b9bde..00000000000 --- a/docs/tools/chrome-extension.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -summary: "Chrome extension: let OpenClaw drive your existing Chrome tab" -read_when: - - You want the agent to drive an existing Chrome tab (toolbar button) - - You need remote Gateway + local browser automation via Tailscale - - You want to understand the security implications of browser takeover -title: "Chrome Extension" ---- - -# Chrome extension (browser relay) - -The OpenClaw Chrome extension lets the agent control your **existing Chrome tabs** (your normal Chrome window) instead of launching a separate openclaw-managed Chrome profile. - -Attach/detach happens via a **single Chrome toolbar button**. - -If you want Chrome’s official DevTools MCP attach flow instead of the OpenClaw -extension relay, use an `existing-session` browser profile instead. See -[Browser](/tools/browser#chrome-existing-session-via-mcp). For Chrome’s own -setup docs, see [Chrome for Developers: Use Chrome DevTools MCP with your -browser session](https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session) -and the [Chrome DevTools MCP README](https://github.com/ChromeDevTools/chrome-devtools-mcp). - -## What it is (concept) - -There are three parts: - -- **Browser control service** (Gateway or node): the API the agent/tool calls (via the Gateway) -- **Local relay server** (loopback CDP): bridges between the control server and the extension (`http://127.0.0.1:18792` by default) -- **Chrome MV3 extension**: attaches to the active tab using `chrome.debugger` and pipes CDP messages to the relay - -OpenClaw then controls the attached tab through the normal `browser` tool surface (selecting the right profile). - -## Install / load (unpacked) - -1. Install the extension to a stable local path: - -```bash -openclaw browser extension install -``` - -2. Print the installed extension directory path: - -```bash -openclaw browser extension path -``` - -3. Chrome → `chrome://extensions` - -- Enable “Developer mode” -- “Load unpacked” → select the directory printed above - -4. Pin the extension. - -## Updates (no build step) - -The extension ships inside the OpenClaw release (npm package) as static files. There is no separate “build” step. - -After upgrading OpenClaw: - -- Re-run `openclaw browser extension install` to refresh the installed files under your OpenClaw state directory. -- Chrome → `chrome://extensions` → click “Reload” on the extension. - -## Use it (set gateway token once) - -To use the extension relay, create a browser profile for it: - -Before first attach, open extension Options and set: - -- `Port` (default `18792`) -- `Gateway token` (must match `gateway.auth.token` / `OPENCLAW_GATEWAY_TOKEN`) - -Then create a profile: - -```bash -openclaw browser create-profile \ - --name my-chrome \ - --driver extension \ - --cdp-url http://127.0.0.1:18792 \ - --color "#00AA00" -``` - -Use it: - -- CLI: `openclaw browser --browser-profile my-chrome tabs` -- Agent tool: `browser` with `profile="my-chrome"` - -### Custom Gateway ports - -If you're using a custom gateway port, the extension relay port is automatically derived: - -**Extension Relay Port = Gateway Port + 3** - -Example: if `gateway.port: 19001`, then: - -- Extension relay port: `19004` (gateway + 3) - -Configure the extension to use the derived relay port in the extension Options page. - -## Attach / detach (toolbar button) - -- Open the tab you want OpenClaw to control. -- Click the extension icon. - - Badge shows `ON` when attached. -- Click again to detach. - -## Which tab does it control? - -- It does **not** automatically control “whatever tab you’re looking at”. -- It controls **only the tab(s) you explicitly attached** by clicking the toolbar button. -- To switch: open the other tab and click the extension icon there. - -## Badge + common errors - -- `ON`: attached; OpenClaw can drive that tab. -- `…`: connecting to the local relay. -- `!`: relay not reachable/authenticated (most common: relay server not running, or gateway token missing/wrong). - -If you see `!`: - -- Make sure the Gateway is running locally (default setup), or run a node host on this machine if the Gateway runs elsewhere. -- Open the extension Options page; it validates relay reachability + gateway-token auth. - -## Remote Gateway (use a node host) - -### Local Gateway (same machine as Chrome) — usually **no extra steps** - -If the Gateway runs on the same machine as Chrome, it starts the browser control service on loopback -and auto-starts the relay server. The extension talks to the local relay; the CLI/tool calls go to the Gateway. - -### Remote Gateway (Gateway runs elsewhere) — **run a node host** - -If your Gateway runs on another machine, start a node host on the machine that runs Chrome. -The Gateway will proxy browser actions to that node; the extension + relay stay local to the browser machine. - -If multiple nodes are connected, pin one with `gateway.nodes.browser.node` or set `gateway.nodes.browser.mode`. - -## Sandboxing (tool containers) - -If your agent session is sandboxed (`agents.defaults.sandbox.mode != "off"`), the `browser` tool can be restricted: - -- By default, sandboxed sessions often target the **sandbox browser** (`target="sandbox"`), not your host Chrome. -- Chrome extension relay takeover requires controlling the **host** browser control server. - -Options: - -- Easiest: use the extension from a **non-sandboxed** session/agent. -- Or allow host browser control for sandboxed sessions: - -```json5 -{ - agents: { - defaults: { - sandbox: { - browser: { - allowHostControl: true, - }, - }, - }, - }, -} -``` - -Then ensure the tool isn’t denied by tool policy, and (if needed) call `browser` with `target="host"`. - -Debugging: `openclaw sandbox explain` - -## Remote access tips - -- Keep the Gateway and node host on the same tailnet; avoid exposing relay ports to LAN or public Internet. -- Pair nodes intentionally; disable browser proxy routing if you don’t want remote control (`gateway.nodes.browser.mode="off"`). -- Leave the relay on loopback unless you have a real cross-namespace need. For WSL2 or similar split-host setups, set `browser.relayBindHost` to an explicit bind address such as `0.0.0.0`, then keep access constrained with Gateway auth, node pairing, and a private network. - -## How “extension path” works - -`openclaw browser extension path` prints the **installed** on-disk directory containing the extension files. - -The CLI intentionally does **not** print a `node_modules` path. Always run `openclaw browser extension install` first to copy the extension to a stable location under your OpenClaw state directory. - -If you move or delete that install directory, Chrome will mark the extension as broken until you reload it from a valid path. - -## Security implications (read this) - -This is powerful and risky. Treat it like giving the model “hands on your browser”. - -- The extension uses Chrome’s debugger API (`chrome.debugger`). When attached, the model can: - - click/type/navigate in that tab - - read page content - - access whatever the tab’s logged-in session can access -- **This is not isolated** like the dedicated openclaw-managed profile. - - If you attach to your daily-driver profile/tab, you’re granting access to that account state. - -Recommendations: - -- Prefer a dedicated Chrome profile (separate from your personal browsing) for extension relay usage. -- Keep the Gateway and any node hosts tailnet-only; rely on Gateway auth + node pairing. -- Avoid exposing relay ports over LAN (`0.0.0.0`) and avoid Funnel (public). -- The relay blocks non-extension origins and requires gateway-token auth for both `/cdp` and `/extension`. - -Related: - -- Browser tool overview: [Browser](/tools/browser) -- Security audit: [Security](/gateway/security) -- Tailscale setup: [Tailscale](/gateway/tailscale) diff --git a/docs/tools/firecrawl.md b/docs/tools/firecrawl.md index 2cd90a06bf5..901890dfb0a 100644 --- a/docs/tools/firecrawl.md +++ b/docs/tools/firecrawl.md @@ -1,27 +1,71 @@ --- -summary: "Firecrawl fallback for web_fetch (anti-bot + cached extraction)" +summary: "Firecrawl search, scrape, and web_fetch fallback" read_when: - You want Firecrawl-backed web extraction - You need a Firecrawl API key + - You want Firecrawl as a web_search provider - You want anti-bot extraction for web_fetch title: "Firecrawl" --- # Firecrawl -OpenClaw can use **Firecrawl** as a fallback extractor for `web_fetch`. It is a hosted -content extraction service that supports bot circumvention and caching, which helps -with JS-heavy sites or pages that block plain HTTP fetches. +OpenClaw can use **Firecrawl** in three ways: + +- as the `web_search` provider +- as explicit plugin tools: `firecrawl_search` and `firecrawl_scrape` +- as a fallback extractor for `web_fetch` + +It is a hosted extraction/search service that supports bot circumvention and caching, +which helps with JS-heavy sites or pages that block plain HTTP fetches. ## Get an API key 1. Create a Firecrawl account and generate an API key. 2. Store it in config or set `FIRECRAWL_API_KEY` in the gateway environment. -## Configure Firecrawl +## Configure Firecrawl search ```json5 { + plugins: { + entries: { + firecrawl: { + enabled: true, + }, + }, + }, + tools: { + web: { + search: { + provider: "firecrawl", + firecrawl: { + apiKey: "FIRECRAWL_API_KEY_HERE", + baseUrl: "https://api.firecrawl.dev", + }, + }, + }, + }, +} +``` + +Notes: + +- Choosing Firecrawl in onboarding or `openclaw configure --section web` enables the bundled Firecrawl plugin automatically. +- `web_search` with Firecrawl supports `query` and `count`. +- For Firecrawl-specific controls like `sources`, `categories`, or result scraping, use `firecrawl_search`. + +## Configure Firecrawl scrape + web_fetch fallback + +```json5 +{ + plugins: { + entries: { + firecrawl: { + enabled: true, + }, + }, + }, tools: { web: { fetch: { @@ -44,6 +88,38 @@ Notes: - Firecrawl fallback attempts run only when an API key is available (`tools.web.fetch.firecrawl.apiKey` or `FIRECRAWL_API_KEY`). - `maxAgeMs` controls how old cached results can be (ms). Default is 2 days. +`firecrawl_scrape` reuses the same `tools.web.fetch.firecrawl.*` settings and env vars. + +## Firecrawl plugin tools + +### `firecrawl_search` + +Use this when you want Firecrawl-specific search controls instead of generic `web_search`. + +Core parameters: + +- `query` +- `count` +- `sources` +- `categories` +- `scrapeResults` +- `timeoutSeconds` + +### `firecrawl_scrape` + +Use this for JS-heavy or bot-protected pages where plain `web_fetch` is weak. + +Core parameters: + +- `url` +- `extractMode` +- `maxChars` +- `onlyMainContent` +- `maxAgeMs` +- `proxy` +- `storeInCache` +- `timeoutSeconds` + ## Stealth / bot circumvention Firecrawl exposes a **proxy mode** parameter for bot circumvention (`basic`, `stealth`, or `auto`). diff --git a/docs/tools/index.md b/docs/tools/index.md index bdd9b78456f..deb42b0d76a 100644 --- a/docs/tools/index.md +++ b/docs/tools/index.md @@ -256,7 +256,7 @@ Enable with `tools.loopDetection.enabled: true` (default is `false`). ### `web_search` -Search the web using Perplexity, Brave, Gemini, Grok, or Kimi. +Search the web using Brave, Firecrawl, Gemini, Grok, Kimi, or Perplexity. Core parameters: @@ -318,8 +318,7 @@ Common parameters: - All actions accept optional `profile` parameter for multi-instance support. - Omit `profile` for the safe default: isolated OpenClaw-managed browser (`openclaw`). - Use `profile="user"` for the real local host browser when existing logins/cookies matter and the user is present to click/approve any attach prompt. -- Use `profile="chrome-relay"` only for the Chrome extension / toolbar-button attach flow. -- `profile="user"` and `profile="chrome-relay"` are host-only; do not combine them with sandbox/node targets. +- `profile="user"` is host-only; do not combine it with sandbox/node targets. - When `profile` is omitted, uses `browser.defaultProfile` (defaults to `openclaw`). - Profile names: lowercase alphanumeric + hyphens only (max 64 chars). - Port range: 18800-18899 (~100 profiles max). diff --git a/docs/tools/plugin.md b/docs/tools/plugin.md index 8aa7beefa42..770eaa215e0 100644 --- a/docs/tools/plugin.md +++ b/docs/tools/plugin.md @@ -57,6 +57,18 @@ openclaw plugins install ./my-bundle openclaw plugins install ./my-bundle.tgz ``` +For Claude marketplace installs, list the marketplace first, then install by +marketplace entry name: + +```bash +openclaw plugins marketplace list +openclaw plugins install @ +``` + +OpenClaw resolves known Claude marketplace names from +`~/.claude/plugins/known_marketplaces.json`. You can also pass an explicit +marketplace source with `--marketplace`. + ## Architecture OpenClaw's plugin system has four layers: @@ -94,6 +106,10 @@ OpenClaw also recognizes two compatible external bundle layouts: component layout without a manifest - Cursor-style bundles: `.cursor-plugin/plugin.json` +Claude marketplace entries can point at any of these compatible bundles, or at +native OpenClaw plugin sources. OpenClaw resolves the marketplace entry first, +then runs the normal install path for the resolved source. + They are shown in the plugin list as `format=bundle`, with a subtype of `codex` or `claude` in verbose/info output. @@ -167,20 +183,17 @@ Important trust note: - Anthropic provider runtime — bundled as `anthropic` (enabled by default) - BytePlus provider catalog — bundled as `byteplus` (enabled by default) - Cloudflare AI Gateway provider catalog — bundled as `cloudflare-ai-gateway` (enabled by default) -- Google Antigravity OAuth (provider auth) — bundled as `google-antigravity-auth` (disabled by default) -- Gemini CLI OAuth (provider auth) — bundled as `google-gemini-cli-auth` (disabled by default) +- Google web search + Gemini CLI OAuth — bundled as `google` (web search auto-loads it; provider auth stays opt-in) - GitHub Copilot provider runtime — bundled as `github-copilot` (enabled by default) - Hugging Face provider catalog — bundled as `huggingface` (enabled by default) - Kilo Gateway provider runtime — bundled as `kilocode` (enabled by default) - Kimi Coding provider catalog — bundled as `kimi-coding` (enabled by default) -- MiniMax provider catalog + usage — bundled as `minimax` (enabled by default) -- MiniMax OAuth (provider auth + catalog) — bundled as `minimax-portal-auth` (enabled by default) +- MiniMax provider catalog + usage + OAuth — bundled as `minimax` (enabled by default; owns `minimax` and `minimax-portal`) - Mistral provider capabilities — bundled as `mistral` (enabled by default) - Model Studio provider catalog — bundled as `modelstudio` (enabled by default) - Moonshot provider runtime — bundled as `moonshot` (enabled by default) - NVIDIA provider catalog — bundled as `nvidia` (enabled by default) -- OpenAI provider runtime — bundled as `openai` (enabled by default) -- OpenAI Codex provider runtime — bundled as `openai-codex` (enabled by default) +- OpenAI provider runtime — bundled as `openai` (enabled by default; owns both `openai` and `openai-codex`) - OpenCode Go provider capabilities — bundled as `opencode-go` (enabled by default) - OpenCode Zen provider capabilities — bundled as `opencode` (enabled by default) - OpenRouter provider runtime — bundled as `openrouter` (enabled by default) @@ -208,7 +221,7 @@ Native OpenClaw plugins can register: - Background services - Context engines - Provider auth flows and model catalogs -- Provider runtime hooks for dynamic model ids, transport normalization, capability metadata, stream wrapping, cache TTL policy, runtime auth exchange, and usage/billing auth + snapshot resolution +- Provider runtime hooks for dynamic model ids, transport normalization, capability metadata, stream wrapping, cache TTL policy, missing-auth hints, built-in model suppression, catalog augmentation, runtime auth exchange, and usage/billing auth + snapshot resolution - Optional config validation - **Skills** (by listing `skills` directories in the plugin manifest) - **Auto-reply commands** (execute without invoking the AI agent) @@ -220,13 +233,24 @@ Tool authoring guide: [Plugin agent tools](/plugins/agent-tools). Provider plugins now have two layers: +- manifest metadata: `providerAuthEnvVars` for cheap env-auth lookup before + runtime load, plus `providerAuthChoices` for cheap onboarding/auth-choice + labels and CLI flag metadata before runtime load - config-time hooks: `catalog` / legacy `discovery` -- runtime hooks: `resolveDynamicModel`, `prepareDynamicModel`, `normalizeResolvedModel`, `capabilities`, `prepareExtraParams`, `wrapStreamFn`, `isCacheTtlEligible`, `prepareRuntimeAuth`, `resolveUsageAuth`, `fetchUsageSnapshot` +- runtime hooks: `resolveDynamicModel`, `prepareDynamicModel`, `normalizeResolvedModel`, `capabilities`, `prepareExtraParams`, `wrapStreamFn`, `formatApiKey`, `refreshOAuth`, `buildAuthDoctorHint`, `isCacheTtlEligible`, `buildMissingAuthMessage`, `suppressBuiltInModel`, `augmentModelCatalog`, `isBinaryThinking`, `supportsXHighThinking`, `resolveDefaultThinkingLevel`, `isModernModelRef`, `prepareRuntimeAuth`, `resolveUsageAuth`, `fetchUsageSnapshot` OpenClaw still owns the generic agent loop, failover, transcript handling, and tool policy. These hooks are the seam for provider-specific behavior without needing a whole custom inference transport. +Use manifest `providerAuthEnvVars` when the provider has env-based credentials +that generic auth/status/model-picker paths should see without loading plugin +runtime. Use manifest `providerAuthChoices` when onboarding/auth-choice CLI +surfaces should know the provider's choice id, group labels, and simple +one-flag auth wiring without loading provider runtime. Keep provider runtime +`envVars` for operator-facing hints such as onboarding labels or OAuth +client-id/client-secret setup vars. + ### Hook order For model/provider plugins, OpenClaw uses hooks in this rough order: @@ -250,15 +274,39 @@ For model/provider plugins, OpenClaw uses hooks in this rough order: Provider-owned request-param normalization before generic stream option wrappers. 8. `wrapStreamFn` Provider-owned stream wrapper after generic wrappers are applied. -9. `isCacheTtlEligible` - Provider-owned prompt-cache policy for proxy/backhaul providers. -10. `prepareRuntimeAuth` +9. `formatApiKey` + Provider-owned auth-profile formatter used when a stored auth profile needs + to become the runtime `apiKey` string. +10. `refreshOAuth` + Provider-owned OAuth refresh override for custom refresh endpoints or + refresh-failure policy. +11. `buildAuthDoctorHint` + Provider-owned repair hint appended when OAuth refresh fails. +12. `isCacheTtlEligible` + Provider-owned prompt-cache policy for proxy/backhaul providers. +13. `buildMissingAuthMessage` + Provider-owned replacement for the generic missing-auth recovery message. +14. `suppressBuiltInModel` + Provider-owned stale upstream model suppression plus optional user-facing + error hint. +15. `augmentModelCatalog` + Provider-owned synthetic/final catalog rows appended after discovery. +16. `isBinaryThinking` + Provider-owned on/off reasoning toggle for binary-thinking providers. +17. `supportsXHighThinking` + Provider-owned `xhigh` reasoning support for selected models. +18. `resolveDefaultThinkingLevel` + Provider-owned default `/think` level for a specific model family. +19. `isModernModelRef` + Provider-owned modern-model matcher used by live profile filters and smoke + selection. +20. `prepareRuntimeAuth` Exchanges a configured credential into the actual runtime token/key just before inference. -11. `resolveUsageAuth` +21. `resolveUsageAuth` Resolves usage/billing credentials for `/usage` and related status surfaces. -12. `fetchUsageSnapshot` +22. `fetchUsageSnapshot` Fetches and normalizes provider-specific usage/quota snapshots after auth is resolved. @@ -271,7 +319,17 @@ For model/provider plugins, OpenClaw uses hooks in this rough order: - `capabilities`: publish provider-family and transcript/tooling quirks without hardcoding provider ids in core - `prepareExtraParams`: set provider defaults or normalize provider-specific per-model params before generic stream wrapping - `wrapStreamFn`: add provider-specific headers/payload/model compat patches while still using the normal `pi-ai` execution path +- `formatApiKey`: turn a stored auth profile into the runtime `apiKey` string without hardcoding provider token blobs in core +- `refreshOAuth`: own OAuth refresh for providers that do not fit the shared `pi-ai` refreshers +- `buildAuthDoctorHint`: append provider-owned auth repair guidance when refresh fails - `isCacheTtlEligible`: decide whether provider/model pairs should use cache TTL metadata +- `buildMissingAuthMessage`: replace the generic auth-store error with a provider-specific recovery hint +- `suppressBuiltInModel`: hide stale upstream rows and optionally return a provider-owned error for direct resolution failures +- `augmentModelCatalog`: append synthetic/final catalog rows after discovery and config merging +- `isBinaryThinking`: expose binary on/off reasoning UX without hardcoding provider ids in `/think` +- `supportsXHighThinking`: opt specific models into the `xhigh` reasoning level +- `resolveDefaultThinkingLevel`: keep provider/model default reasoning policy out of core +- `isModernModelRef`: keep live/smoke model family inclusion rules with the provider - `prepareRuntimeAuth`: exchange a configured credential into the actual short-lived runtime token/key used for requests - `resolveUsageAuth`: resolve provider-owned credentials for usage/billing endpoints without hardcoding token parsing in core - `fetchUsageSnapshot`: own provider-specific usage endpoint fetch/parsing while core keeps summary fan-out and formatting @@ -285,7 +343,17 @@ Rule of thumb: - provider needs transcript/provider-family quirks: use `capabilities` - provider needs default request params or per-provider param cleanup: use `prepareExtraParams` - provider needs request headers/body/model compat wrappers without a custom transport: use `wrapStreamFn` +- provider stores extra metadata in auth profiles and needs a custom runtime token shape: use `formatApiKey` +- provider needs a custom OAuth refresh endpoint or refresh failure policy: use `refreshOAuth` +- provider needs provider-owned auth repair guidance after refresh failure: use `buildAuthDoctorHint` - provider needs proxy-specific cache TTL gating: use `isCacheTtlEligible` +- provider needs a provider-specific missing-auth recovery hint: use `buildMissingAuthMessage` +- provider needs to hide stale upstream rows or replace them with a vendor hint: use `suppressBuiltInModel` +- provider needs synthetic forward-compat rows in `models list` and pickers: use `augmentModelCatalog` +- provider exposes only binary thinking on/off: use `isBinaryThinking` +- provider wants `xhigh` on only a subset of models: use `supportsXHighThinking` +- provider owns default `/think` policy for a model family: use `resolveDefaultThinkingLevel` +- provider owns live/smoke preferred-model matching: use `isModernModelRef` - provider needs a token exchange or short-lived request credential: use `prepareRuntimeAuth` - provider needs custom usage/quota token parsing or a different usage credential: use `resolveUsageAuth` - provider needs a provider-specific usage endpoint or payload parser: use `fetchUsageSnapshot` @@ -350,28 +418,38 @@ api.registerProvider({ ### Built-in examples -- Anthropic uses `resolveDynamicModel`, `capabilities`, `resolveUsageAuth`, - `fetchUsageSnapshot`, and `isCacheTtlEligible` because it owns Claude 4.6 - forward-compat, provider-family hints, usage endpoint integration, and - prompt-cache eligibility. +- Anthropic uses `resolveDynamicModel`, `capabilities`, `buildAuthDoctorHint`, + `resolveUsageAuth`, `fetchUsageSnapshot`, `isCacheTtlEligible`, + `resolveDefaultThinkingLevel`, and `isModernModelRef` because it owns Claude + 4.6 forward-compat, provider-family hints, auth repair guidance, usage + endpoint integration, prompt-cache eligibility, and Claude default/adaptive + thinking policy. - OpenAI uses `resolveDynamicModel`, `normalizeResolvedModel`, and - `capabilities` because it owns GPT-5.4 forward-compat plus the direct OpenAI - `openai-completions` -> `openai-responses` normalization. + `capabilities` plus `buildMissingAuthMessage`, `suppressBuiltInModel`, + `augmentModelCatalog`, `supportsXHighThinking`, and `isModernModelRef` + because it owns GPT-5.4 forward-compat, the direct OpenAI + `openai-completions` -> `openai-responses` normalization, Codex-aware auth + hints, Spark suppression, synthetic OpenAI list rows, and GPT-5 thinking / + live-model policy. - OpenRouter uses `catalog` plus `resolveDynamicModel` and `prepareDynamicModel` because the provider is pass-through and may expose new model ids before OpenClaw's static catalog updates. -- GitHub Copilot uses `catalog`, `resolveDynamicModel`, and +- GitHub Copilot uses `catalog`, `auth`, `resolveDynamicModel`, and `capabilities` plus `prepareRuntimeAuth` and `fetchUsageSnapshot` because it - needs model fallback behavior, Claude transcript quirks, a GitHub token -> - Copilot token exchange, and a provider-owned usage endpoint. -- OpenAI Codex uses `catalog`, `resolveDynamicModel`, and - `normalizeResolvedModel` plus `prepareExtraParams`, `resolveUsageAuth`, and - `fetchUsageSnapshot` because it still runs on core OpenAI transports but owns - its transport/base URL normalization, default transport choice, and ChatGPT - usage endpoint integration. -- Gemini CLI OAuth uses `resolveDynamicModel`, `resolveUsageAuth`, and - `fetchUsageSnapshot` because it owns Gemini 3.1 forward-compat fallback plus - the token parsing and quota endpoint wiring needed by `/usage`. + needs provider-owned device login, model fallback behavior, Claude transcript + quirks, a GitHub token -> Copilot token exchange, and a provider-owned usage + endpoint. +- OpenAI Codex uses `catalog`, `resolveDynamicModel`, + `normalizeResolvedModel`, `refreshOAuth`, and `augmentModelCatalog` plus + `prepareExtraParams`, `resolveUsageAuth`, and `fetchUsageSnapshot` because it + still runs on core OpenAI transports but owns its transport/base URL + normalization, OAuth refresh fallback policy, default transport choice, + synthetic Codex catalog rows, and ChatGPT usage endpoint integration. +- Google AI Studio and Gemini CLI OAuth use `resolveDynamicModel` and + `isModernModelRef` because they own Gemini 3.1 forward-compat fallback and + modern-model matching; Gemini CLI OAuth also uses `formatApiKey`, + `resolveUsageAuth`, and `fetchUsageSnapshot` for token formatting, token + parsing, and quota endpoint wiring. - OpenRouter uses `capabilities`, `wrapStreamFn`, and `isCacheTtlEligible` to keep provider-specific request headers, routing metadata, reasoning patches, and prompt-cache policy out of core. @@ -382,15 +460,17 @@ api.registerProvider({ reasoning payload normalization, Gemini transcript hints, and Anthropic cache-TTL gating. - Z.AI uses `resolveDynamicModel`, `prepareExtraParams`, `wrapStreamFn`, - `isCacheTtlEligible`, `resolveUsageAuth`, and `fetchUsageSnapshot` because it - owns GLM-5 fallback, `tool_stream` defaults, and both usage auth + quota - fetching. + `isCacheTtlEligible`, `isBinaryThinking`, `isModernModelRef`, + `resolveUsageAuth`, and `fetchUsageSnapshot` because it owns GLM-5 fallback, + `tool_stream` defaults, binary thinking UX, modern-model matching, and both + usage auth + quota fetching. - Mistral, OpenCode Zen, and OpenCode Go use `capabilities` only to keep transcript/tooling quirks out of core. - Catalog-only bundled providers such as `byteplus`, `cloudflare-ai-gateway`, - `huggingface`, `kimi-coding`, `minimax-portal`, `modelstudio`, `nvidia`, - `qianfan`, `qwen-portal`, `synthetic`, `together`, `venice`, - `vercel-ai-gateway`, and `volcengine` use `catalog` only. + `huggingface`, `kimi-coding`, `modelstudio`, `nvidia`, `qianfan`, + `synthetic`, `together`, `venice`, `vercel-ai-gateway`, and `volcengine` use + `catalog` only. +- Qwen portal uses `catalog`, `auth`, and `refreshOAuth`. - MiniMax and Xiaomi use `catalog` plus usage hooks because their `/usage` behavior is plugin-owned even though inference still runs through the shared transports. @@ -507,22 +587,21 @@ Notes: Use SDK subpaths instead of the monolithic `openclaw/plugin-sdk` import when authoring plugins: -- `openclaw/plugin-sdk/core` for generic plugin APIs, provider auth types, and shared helpers. +- `openclaw/plugin-sdk/core` for generic plugin APIs, provider auth types, and shared helpers such as routing/session utilities and logger-backed runtimes. - `openclaw/plugin-sdk/compat` for bundled/internal plugin code that needs broader shared runtime helpers than `core`. -- `openclaw/plugin-sdk/telegram` for Telegram channel plugins. -- `openclaw/plugin-sdk/discord` for Discord channel plugins. -- `openclaw/plugin-sdk/slack` for Slack channel plugins. -- `openclaw/plugin-sdk/signal` for Signal channel plugins. -- `openclaw/plugin-sdk/imessage` for iMessage channel plugins. -- `openclaw/plugin-sdk/whatsapp` for WhatsApp channel plugins. +- `openclaw/plugin-sdk/telegram` for Telegram channel plugin types and shared channel-facing helpers. Built-in Telegram implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/discord` for Discord channel plugin types and shared channel-facing helpers. Built-in Discord implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/slack` for Slack channel plugin types and shared channel-facing helpers. Built-in Slack implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/signal` for Signal channel plugin types and shared channel-facing helpers. Built-in Signal implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/imessage` for iMessage channel plugin types and shared channel-facing helpers. Built-in iMessage implementation internals stay private to the bundled extension. +- `openclaw/plugin-sdk/whatsapp` for WhatsApp channel plugin types and shared channel-facing helpers. Built-in WhatsApp implementation internals stay private to the bundled extension. - `openclaw/plugin-sdk/line` for LINE channel plugins. - `openclaw/plugin-sdk/msteams` for the bundled Microsoft Teams plugin surface. - Bundled extension-specific subpaths are also available: `openclaw/plugin-sdk/acpx`, `openclaw/plugin-sdk/bluebubbles`, `openclaw/plugin-sdk/copilot-proxy`, `openclaw/plugin-sdk/device-pair`, `openclaw/plugin-sdk/diagnostics-otel`, `openclaw/plugin-sdk/diffs`, - `openclaw/plugin-sdk/feishu`, - `openclaw/plugin-sdk/google-gemini-cli-auth`, `openclaw/plugin-sdk/googlechat`, + `openclaw/plugin-sdk/feishu`, `openclaw/plugin-sdk/googlechat`, `openclaw/plugin-sdk/irc`, `openclaw/plugin-sdk/llm-task`, `openclaw/plugin-sdk/lobster`, `openclaw/plugin-sdk/matrix`, `openclaw/plugin-sdk/mattermost`, `openclaw/plugin-sdk/memory-core`, @@ -651,12 +730,12 @@ Default-on bundled plugin examples: - `kilocode` - `kimi-coding` - `minimax` -- `minimax-portal-auth` +- `minimax` - `modelstudio` - `moonshot` - `nvidia` - `ollama` -- `openai-codex` +- `openai` - `openrouter` - `phone-control` - `qianfan` @@ -736,7 +815,8 @@ A plugin directory may include a `package.json` with `openclaw.extensions`: { "name": "my-pack", "openclaw": { - "extensions": ["./src/safety.ts", "./src/tools.ts"] + "extensions": ["./src/safety.ts", "./src/tools.ts"], + "setupEntry": "./src/setup-entry.ts" } } ``` @@ -755,9 +835,16 @@ Security note: `openclaw plugins install` installs plugin dependencies with `npm install --ignore-scripts` (no lifecycle scripts). Keep plugin dependency trees "pure JS/TS" and avoid packages that require `postinstall` builds. +Optional: `openclaw.setupEntry` can point at a lightweight setup-only module. +When OpenClaw needs setup surfaces for a disabled channel plugin, or +when a channel plugin is enabled but still unconfigured, it loads `setupEntry` +instead of the full plugin entry. This keeps startup and setup lighter +when your main plugin entry also wires tools, hooks, or other runtime-only +code. + ### Channel catalog metadata -Channel plugins can advertise onboarding metadata via `openclaw.channel` and +Channel plugins can advertise setup/discovery metadata via `openclaw.channel` and install hints via `openclaw.install`. This keeps the core catalog data-free. Example: @@ -1132,7 +1219,7 @@ A provider plugin can participate in five distinct phases: without prompts. Use this when the provider needs custom headless setup beyond the built-in simple API-key paths. 3. **Wizard integration** - `wizard.onboarding` adds an entry to `openclaw onboard`. + `wizard.setup` adds an entry to `openclaw onboard`. `wizard.modelPicker` adds a setup entry to the model picker. 4. **Implicit discovery** `discovery.run(ctx)` can contribute provider config automatically during @@ -1181,7 +1268,8 @@ The non-interactive context includes: - the current and base config - parsed onboarding CLI options - runtime logging/error helpers -- agent/workspace dirs +- agent/workspace dirs so the provider can persist auth into the same scoped + store used by the rest of onboarding - `resolveApiKey(...)` to read provider keys from flags, env, or existing auth profiles while honoring `--secret-input-mode` - `toApiKeyCredential(...)` to convert a resolved key into an auth-profile @@ -1198,7 +1286,17 @@ errors instead. ### Provider wizard metadata -`wizard.onboarding` controls how the provider appears in grouped onboarding: +Provider auth/onboarding metadata can live in two layers: + +- manifest `providerAuthChoices`: cheap labels, grouping, `--auth-choice` + ids, and simple CLI flag metadata available before runtime load +- runtime `wizard.setup` / `auth[].wizard`: richer behavior that depends on + loaded provider code + +Use manifest metadata for static labels/flags. Use runtime wizard metadata when +setup depends on dynamic auth methods, method fallback, or runtime validation. + +`wizard.setup` controls how the provider appears in grouped onboarding: - `choiceId`: auth-choice value - `choiceLabel`: option label @@ -1207,6 +1305,7 @@ errors instead. - `groupLabel`: group label - `groupHint`: group hint - `methodId`: auth method to run +- `modelAllowlist`: optional post-auth allowlist policy (`allowedKeys`, `initialSelections`, `message`) `wizard.modelPicker` controls how a provider appears as a "set this up now" entry in model selection: @@ -1328,7 +1427,7 @@ api.registerProvider({ }, ], wizard: { - onboarding: { + setup: { choiceId: "acme", choiceLabel: "AcmeAI", groupId: "acme", @@ -1358,14 +1457,22 @@ api.registerProvider({ Notes: - `run` receives a `ProviderAuthContext` with `prompter`, `runtime`, - `openUrl`, and `oauth.createVpsAwareHandlers` helpers. + `openUrl`, `oauth.createVpsAwareHandlers`, `secretInputMode`, and + `allowSecretRefPrompt` helpers/state. Onboarding/configure flows can use + these to honor `--secret-input-mode` or offer env/file/exec secret-ref + capture, while `openclaw models auth` keeps a tighter prompt surface. - `runNonInteractive` receives a `ProviderAuthMethodNonInteractiveContext` - with `opts`, `resolveApiKey`, and `toApiKeyCredential` helpers for - headless onboarding. + with `opts`, `agentDir`, `resolveApiKey`, and `toApiKeyCredential` helpers + for headless onboarding. - Return `configPatch` when you need to add default models or provider config. - Return `defaultModel` so `--set-default` can update agent defaults. -- `wizard.onboarding` adds a provider choice to `openclaw onboard`. +- `wizard.setup` adds a provider choice to onboarding surfaces such as + `openclaw onboard` / `openclaw setup --wizard`. +- `wizard.setup.modelAllowlist` lets the provider narrow the follow-up model + allowlist prompt during onboarding/configure. - `wizard.modelPicker` adds a “setup this provider” entry to the model picker. +- `deprecatedProfileIds` lets the provider own `openclaw doctor` cleanup for + retired auth-profile ids. - `discovery.run` returns either `{ provider }` for the plugin’s own provider id or `{ providers }` for multi-provider discovery. - `discovery.order` controls when the provider runs relative to built-in @@ -1424,16 +1531,6 @@ Preferred setup split: - `plugin.setup` owns account-id normalization, validation, and config writes. - `plugin.setupWizard` lets the host run the common wizard flow while the channel only supplies status, credential, DM allowlist, and channel-access descriptors. -Use `plugin.onboarding` only when the host-owned setup wizard cannot express the flow and the -channel needs to fully own prompting. - -Wizard precedence: - -1. `plugin.setupWizard` (preferred, host-owned prompts) -2. `plugin.onboarding.configureInteractive` -3. `plugin.onboarding.configureWhenConfigured` (already-configured channel only) -4. `plugin.onboarding.configure` - `plugin.setupWizard` is best for channels that fit the shared pattern: - one account picker driven by `plugin.config.listAccountIds` @@ -1445,11 +1542,6 @@ Wizard precedence: - optional DM allowlist resolution (for example `@username` -> numeric id) - optional completion note after setup finishes -`plugin.onboarding` hooks still return the same values as before: - -- `"skip"` leaves selection and account tracking unchanged. -- `{ cfg, accountId? }` applies config updates and records account selection. - ### Write a new messaging channel (step‑by‑step) Use this when you want a **new chat surface** (a "messaging channel"), not a model provider. @@ -1659,6 +1751,7 @@ Recommended packaging: Publishing contract: - Plugin `package.json` must include `openclaw.extensions` with one or more entry files. +- Optional: `openclaw.setupEntry` may point at a lightweight setup-only entry for disabled or still-unconfigured channel setup. - Entry files can be `.js` or `.ts` (jiti loads TS at runtime). - `openclaw plugins install ` uses `npm pack`, extracts into `~/.openclaw/extensions//`, and enables it in config. - Config key stability: scoped packages are normalized to the **unscoped** id for `plugins.entries.*`. diff --git a/docs/tools/web.md b/docs/tools/web.md index a2aa1d37bfd..7cc67c07710 100644 --- a/docs/tools/web.md +++ b/docs/tools/web.md @@ -1,5 +1,5 @@ --- -summary: "Web search + fetch tools (Brave, Gemini, Grok, Kimi, and Perplexity providers)" +summary: "Web search + fetch tools (Brave, Firecrawl, Gemini, Grok, Kimi, and Perplexity providers)" read_when: - You want to enable web_search or web_fetch - You need provider API key setup @@ -11,7 +11,7 @@ title: "Web Tools" OpenClaw ships two lightweight web tools: -- `web_search` — Search the web using Brave Search API, Gemini with Google Search grounding, Grok, Kimi, or Perplexity Search API. +- `web_search` — Search the web using Brave Search API, Firecrawl Search, Gemini with Google Search grounding, Grok, Kimi, or Perplexity Search API. - `web_fetch` — HTTP fetch + readable extraction (HTML → markdown/text). These are **not** browser automation. For JS-heavy sites or logins, use the @@ -24,18 +24,20 @@ These are **not** browser automation. For JS-heavy sites or logins, use the - `web_fetch` does a plain HTTP GET and extracts readable content (HTML → markdown/text). It does **not** execute JavaScript. - `web_fetch` is enabled by default (unless explicitly disabled). +- The bundled Firecrawl plugin also adds `firecrawl_search` and `firecrawl_scrape` when enabled. See [Brave Search setup](/brave-search) and [Perplexity Search setup](/perplexity) for provider-specific details. ## Choosing a search provider -| Provider | Result shape | Provider-specific filters | Notes | API key | -| ------------------------- | ---------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------- | -| **Brave Search API** | Structured results with snippets | `country`, `language`, `ui_lang`, time | Supports Brave `llm-context` mode | `BRAVE_API_KEY` | -| **Gemini** | AI-synthesized answers + citations | — | Uses Google Search grounding | `GEMINI_API_KEY` | -| **Grok** | AI-synthesized answers + citations | — | Uses xAI web-grounded responses | `XAI_API_KEY` | -| **Kimi** | AI-synthesized answers + citations | — | Uses Moonshot web search | `KIMI_API_KEY` / `MOONSHOT_API_KEY` | -| **Perplexity Search API** | Structured results with snippets | `country`, `language`, time, `domain_filter` | Supports content extraction controls; OpenRouter uses Sonar compatibility path | `PERPLEXITY_API_KEY` / `OPENROUTER_API_KEY` | +| Provider | Result shape | Provider-specific filters | Notes | API key | +| ------------------------- | ---------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------- | +| **Brave Search API** | Structured results with snippets | `country`, `language`, `ui_lang`, time | Supports Brave `llm-context` mode | `BRAVE_API_KEY` | +| **Firecrawl Search** | Structured results with snippets | Use `firecrawl_search` for Firecrawl-specific search options | Best for pairing search with Firecrawl scraping/extraction | `FIRECRAWL_API_KEY` | +| **Gemini** | AI-synthesized answers + citations | — | Uses Google Search grounding | `GEMINI_API_KEY` | +| **Grok** | AI-synthesized answers + citations | — | Uses xAI web-grounded responses | `XAI_API_KEY` | +| **Kimi** | AI-synthesized answers + citations | — | Uses Moonshot web search | `KIMI_API_KEY` / `MOONSHOT_API_KEY` | +| **Perplexity Search API** | Structured results with snippets | `country`, `language`, time, `domain_filter` | Supports content extraction controls; OpenRouter uses Sonar compatibility path | `PERPLEXITY_API_KEY` / `OPENROUTER_API_KEY` | ### Auto-detection @@ -46,6 +48,7 @@ The table above is alphabetical. If no `provider` is explicitly set, runtime aut 3. **Grok** — `XAI_API_KEY` env var or `tools.web.search.grok.apiKey` config 4. **Kimi** — `KIMI_API_KEY` / `MOONSHOT_API_KEY` env var or `tools.web.search.kimi.apiKey` config 5. **Perplexity** — `PERPLEXITY_API_KEY`, `OPENROUTER_API_KEY`, or `tools.web.search.perplexity.apiKey` config +6. **Firecrawl** — `FIRECRAWL_API_KEY` env var or `tools.web.search.firecrawl.apiKey` config If no keys are found, it falls back to Brave (you'll get a missing-key error prompting you to configure one). @@ -86,6 +89,7 @@ See [Perplexity Search API Docs](https://docs.perplexity.ai/guides/search-quicks **Via config:** run `openclaw configure --section web`. It stores the key under the provider-specific config path: - Brave: `tools.web.search.apiKey` +- Firecrawl: `tools.web.search.firecrawl.apiKey` - Gemini: `tools.web.search.gemini.apiKey` - Grok: `tools.web.search.grok.apiKey` - Kimi: `tools.web.search.kimi.apiKey` @@ -96,6 +100,7 @@ All of these fields also support SecretRef objects. **Via environment:** set provider env vars in the Gateway process environment: - Brave: `BRAVE_API_KEY` +- Firecrawl: `FIRECRAWL_API_KEY` - Gemini: `GEMINI_API_KEY` - Grok: `XAI_API_KEY` - Kimi: `KIMI_API_KEY` or `MOONSHOT_API_KEY` @@ -121,6 +126,34 @@ For a gateway install, put these in `~/.openclaw/.env` (or your service environm } ``` +**Firecrawl Search:** + +```json5 +{ + plugins: { + entries: { + firecrawl: { + enabled: true, + }, + }, + }, + tools: { + web: { + search: { + enabled: true, + provider: "firecrawl", + firecrawl: { + apiKey: "fc-...", // optional if FIRECRAWL_API_KEY is set + baseUrl: "https://api.firecrawl.dev", + }, + }, + }, + }, +} +``` + +When you choose Firecrawl in onboarding or `openclaw configure --section web`, OpenClaw enables the bundled Firecrawl plugin automatically so `web_search`, `firecrawl_search`, and `firecrawl_scrape` are all available. + **Brave LLM Context mode:** ```json5 @@ -234,6 +267,7 @@ Search the web using your configured provider. - `tools.web.search.enabled` must not be `false` (default: enabled) - API key for your chosen provider: - **Brave**: `BRAVE_API_KEY` or `tools.web.search.apiKey` + - **Firecrawl**: `FIRECRAWL_API_KEY` or `tools.web.search.firecrawl.apiKey` - **Gemini**: `GEMINI_API_KEY` or `tools.web.search.gemini.apiKey` - **Grok**: `XAI_API_KEY` or `tools.web.search.grok.apiKey` - **Kimi**: `KIMI_API_KEY`, `MOONSHOT_API_KEY`, or `tools.web.search.kimi.apiKey` @@ -260,7 +294,7 @@ Search the web using your configured provider. ### Tool parameters -All parameters work for Brave and for native Perplexity Search API unless noted. +Parameters depend on the selected provider. Perplexity's OpenRouter / Sonar compatibility path supports only `query` and `freshness`. If you set `tools.web.search.perplexity.baseUrl` / `model`, use `OPENROUTER_API_KEY`, or configure an `sk-or-...` key, Search API-only filters return explicit errors. @@ -279,6 +313,8 @@ If you set `tools.web.search.perplexity.baseUrl` / `model`, use `OPENROUTER_API_ | `max_tokens` | Total content budget, default 25000 (Perplexity only) | | `max_tokens_per_page` | Per-page token limit, default 2048 (Perplexity only) | +Firecrawl `web_search` supports `query` and `count`. For Firecrawl-specific controls like `sources`, `categories`, result scraping, or scrape timeout, use `firecrawl_search` from the bundled Firecrawl plugin. + **Examples:** ```javascript diff --git a/docs/web/control-ui.md b/docs/web/control-ui.md index 73487cc0eae..204d68605d2 100644 --- a/docs/web/control-ui.md +++ b/docs/web/control-ui.md @@ -28,7 +28,7 @@ Auth is supplied during the WebSocket handshake via: - `connect.params.auth.token` - `connect.params.auth.password` The dashboard settings panel keeps a token for the current browser tab session and selected gateway URL; passwords are not persisted. - The onboarding wizard generates a gateway token by default, so paste it here on first connect. + The setup wizard generates a gateway token by default, so paste it here on first connect. ## Device pairing (first connection) diff --git a/docs/zh-CN/automation/hooks.md b/docs/zh-CN/automation/hooks.md index b5806e2bdd0..b0615569eec 100644 --- a/docs/zh-CN/automation/hooks.md +++ b/docs/zh-CN/automation/hooks.md @@ -1,60 +1,61 @@ --- read_when: - - 你想为 /new、/reset、/stop 和智能体生命周期事件实现事件驱动自动化 - - 你想构建、安装或调试 hooks + - 你希望为 `/new`、`/reset`、`/stop` 和智能体生命周期事件使用事件驱动自动化 + - 你希望构建、安装或调试 Hooks summary: Hooks:用于命令和生命周期事件的事件驱动自动化 title: Hooks x-i18n: - generated_at: "2026-02-03T07:50:59Z" - model: claude-opus-4-5 - provider: pi - source_hash: 853227a0f1abd20790b425fa64dda60efc6b5f93c1b13ecd2dcb788268f71d79 + generated_at: "2026-03-16T06:21:34Z" + model: gpt-5.4 + provider: openai + source_hash: fc1370a05127d778eb685f687ee9a52062aa6f5c895e80152b9de41c3a02c592 source_path: automation/hooks.md workflow: 15 --- # Hooks -Hooks 提供了一个可扩展的事件驱动系统,用于响应智能体命令和事件自动执行操作。Hooks 从目录中自动发现,可以通过 CLI 命令管理,类似于 OpenClaw 中 Skills 的工作方式。 +Hooks 提供了一个可扩展的事件驱动系统,用于在响应智能体命令和事件时自动执行操作。Hooks 会从目录中自动发现,并且可以通过 CLI 命令进行管理,方式与 OpenClaw 中的 Skills 类似。 -## 入门指南 +## 熟悉基础 -Hooks 是在事件发生时运行的小脚本。有两种类型: +Hooks 是在某些事情发生时运行的小脚本。它们有两种类型: -- **Hooks**(本页):当智能体事件触发时在 Gateway 网关内运行,如 `/new`、`/reset`、`/stop` 或生命周期事件。 -- **Webhooks**:外部 HTTP webhooks,让其他系统触发 OpenClaw 中的工作。参见 [Webhook Hooks](/automation/webhook) 或使用 `openclaw webhooks` 获取 Gmail 助手命令。 +- **Hooks**(本页):当智能体事件触发时,在 Gateway 网关内运行,例如 `/new`、`/reset`、`/stop` 或生命周期事件。 +- **Webhooks**:外部 HTTP webhook,可让其他系统在 OpenClaw 中触发工作。请参阅 [Webhook Hooks](/automation/webhook),或使用 `openclaw webhooks` 获取 Gmail 辅助命令。 -Hooks 也可以捆绑在插件中;参见 [插件](/tools/plugin#plugin-hooks)。 +Hooks 也可以打包在插件中;请参阅 [Plugins](/tools/plugin#plugin-hooks)。 常见用途: -- 重置会话时保存记忆快照 -- 保留命令审计跟踪用于故障排除或合规 -- 会话开始或结束时触发后续自动化 -- 事件触发时向智能体工作区写入文件或调用外部 API +- 当你重置会话时保存一份内存快照 +- 为故障排除或合规保留命令审计轨迹 +- 当会话开始或结束时触发后续自动化 +- 当事件触发时,将文件写入智能体工作区或调用外部 API -如果你能写一个小的 TypeScript 函数,你就能写一个 hook。Hooks 会自动发现,你可以通过 CLI 启用或禁用它们。 +如果你会写一个小型 TypeScript 函数,你就能编写一个 hook。Hooks 会被自动发现,你可以通过 CLI 启用或禁用它们。 -## 概述 +## 概览 -hooks 系统允许你: +Hooks 系统允许你: -- 在发出 `/new` 时将会话上下文保存到记忆 +- 当发出 `/new` 时,将会话上下文保存到 memory - 记录所有命令以供审计 - 在智能体生命周期事件上触发自定义自动化 - 在不修改核心代码的情况下扩展 OpenClaw 的行为 -## 入门 +## 入门指南 -### 捆绑的 Hooks +### 内置 Hooks -OpenClaw 附带三个自动发现的捆绑 hooks: +OpenClaw 自带四个会被自动发现的内置 hook: -- **💾 session-memory**:当你发出 `/new` 时将会话上下文保存到智能体工作区(默认 `~/.openclaw/workspace/memory/`) +- **💾 session-memory**:当你发出 `/new` 时,将会话上下文保存到你的智能体工作区(默认是 `~/.openclaw/workspace/memory/`) +- **📎 bootstrap-extra-files**:在 `agent:bootstrap` 期间,从已配置的 glob/路径模式中注入额外的工作区引导文件 - **📝 command-logger**:将所有命令事件记录到 `~/.openclaw/logs/commands.log` - **🚀 boot-md**:当 Gateway 网关启动时运行 `BOOT.md`(需要启用内部 hooks) -列出可用的 hooks: +列出可用 hooks: ```bash openclaw hooks list @@ -80,35 +81,40 @@ openclaw hooks info session-memory ### 新手引导 -在新手引导期间(`openclaw onboard`),你将被提示启用推荐的 hooks。向导会自动发现符合条件的 hooks 并呈现供选择。 +在新手引导期间(`openclaw onboard`),系统会提示你启用推荐的 hooks。向导会自动发现符合条件的 hooks 并供你选择。 ## Hook 发现 -Hooks 从三个目录自动发现(按优先级顺序): +Hooks 会从三个目录中自动发现(按优先级顺序): -1. **工作区 hooks**:`/hooks/`(每智能体,最高优先级) -2. **托管 hooks**:`~/.openclaw/hooks/`(用户安装,跨工作区共享) -3. **捆绑 hooks**:`/dist/hooks/bundled/`(随 OpenClaw 附带) +1. **工作区 hooks**:`/hooks/`(每个智能体单独配置,优先级最高) +2. **托管 hooks**:`~/.openclaw/hooks/`(用户安装,在各工作区之间共享) +3. **内置 hooks**:`/dist/hooks/bundled/`(随 OpenClaw 一起提供) -托管 hook 目录可以是**单个 hook** 或 **hook 包**(包目录)。 +托管 hook 目录既可以是 **单个 hook**,也可以是 **hook 包**(包目录)。 -每个 hook 是一个包含以下内容的目录: +每个 hook 都是一个包含以下内容的目录: ``` my-hook/ ├── HOOK.md # 元数据 + 文档 -└── handler.ts # 处理程序实现 +└── handler.ts # 处理器实现 ``` -## Hook 包(npm/archives) +## Hook 包(npm/归档) -Hook 包是标准的 npm 包,通过 `package.json` 中的 `openclaw.hooks` 导出一个或多个 hooks。使用以下命令安装: +Hook 包是标准的 npm 包,它们通过 `package.json` 中的 `openclaw.hooks` 导出一个或多个 hook。使用以下命令安装它们: ```bash openclaw hooks install ``` -示例 `package.json`: +npm spec 仅支持注册表形式(包名 + 可选的精确版本或 dist-tag)。 +Git/URL/file spec 和 semver 范围会被拒绝。 + +裸 spec 和 `@latest` 会保持在稳定轨道上。如果 npm 将其中任意一种解析为预发布版本,OpenClaw 会停止并要求你通过预发布标签(例如 `@beta`/`@rc`)或精确的预发布版本显式选择加入。 + +`package.json` 示例: ```json { @@ -120,19 +126,23 @@ openclaw hooks install } ``` -每个条目指向包含 `HOOK.md` 和 `handler.ts`(或 `index.ts`)的 hook 目录。 -Hook 包可以附带依赖;它们将安装在 `~/.openclaw/hooks/` 下。 +每个条目都指向一个包含 `HOOK.md` 和 `handler.ts`(或 `index.ts`)的 hook 目录。 +Hook 包可以携带依赖;它们会安装到 `~/.openclaw/hooks/` 下。 +每个 `openclaw.hooks` 条目在解析符号链接后都必须保持在包目录内部;超出目录范围的条目会被拒绝。 + +安全说明:`openclaw hooks install` 会使用 `npm install --ignore-scripts` 安装依赖 +(不运行生命周期脚本)。请保持 hook 包依赖树为“纯 JS/TS”,并避免依赖 `postinstall` 构建的包。 ## Hook 结构 ### HOOK.md 格式 -`HOOK.md` 文件在 YAML frontmatter 中包含元数据,加上 Markdown 文档: +`HOOK.md` 文件包含 YAML frontmatter 中的元数据以及 Markdown 文档: ```markdown --- name: my-hook -description: "Short description of what this hook does" +description: "关于此 hook 功能的简短描述" homepage: https://docs.openclaw.ai/automation/hooks#my-hook metadata: { "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } } @@ -140,49 +150,47 @@ metadata: # My Hook -Detailed documentation goes here... +详细文档写在这里…… -## What It Does +## 它的作用 -- Listens for `/new` commands -- Performs some action -- Logs the result +- 监听 `/new` 命令 +- 执行某些操作 +- 记录结果 -## Requirements +## 要求 -- Node.js must be installed +- 必须安装 Node.js -## Configuration +## 配置 -No configuration needed. +无需配置。 ``` ### 元数据字段 `metadata.openclaw` 对象支持: -- **`emoji`**:CLI 的显示表情符号(例如 `"💾"`) +- **`emoji`**:CLI 显示用 emoji(例如 `"💾"`) - **`events`**:要监听的事件数组(例如 `["command:new", "command:reset"]`) - **`export`**:要使用的命名导出(默认为 `"default"`) - **`homepage`**:文档 URL - **`requires`**:可选要求 - - **`bins`**:PATH 中需要的二进制文件(例如 `["git", "node"]`) - - **`anyBins`**:这些二进制文件中至少有一个必须存在 - - **`env`**:需要的环境变量 - - **`config`**:需要的配置路径(例如 `["workspace.dir"]`) - - **`os`**:需要的平台(例如 `["darwin", "linux"]`) + - **`bins`**:PATH 中必须存在的二进制文件(例如 `["git", "node"]`) + - **`anyBins`**:这些二进制文件中至少要存在一个 + - **`env`**:必需的环境变量 + - **`config`**:必需的配置路径(例如 `["workspace.dir"]`) + - **`os`**:支持的平台(例如 `["darwin", "linux"]`) - **`always`**:绕过资格检查(布尔值) -- **`install`**:安装方法(对于捆绑 hooks:`[{"id":"bundled","kind":"bundled"}]`) +- **`install`**:安装方式(对于内置 hooks:`[{"id":"bundled","kind":"bundled"}]`) -### 处理程序实现 +### 处理器实现 -`handler.ts` 文件导出一个 `HookHandler` 函数: +`handler.ts` 文件会导出一个 `HookHandler` 函数: ```typescript -import type { HookHandler } from "../../src/hooks/hooks.js"; - -const myHandler: HookHandler = async (event) => { - // Only trigger on 'new' command +const myHandler = async (event) => { + // 仅在 'new' 命令时触发 if (event.type !== "command" || event.action !== "new") { return; } @@ -191,9 +199,9 @@ const myHandler: HookHandler = async (event) => { console.log(` Session: ${event.sessionKey}`); console.log(` Timestamp: ${event.timestamp.toISOString()}`); - // Your custom logic here + // 你的自定义逻辑写在这里 - // Optionally send message to user + // 可选:向用户发送消息 event.messages.push("✨ My hook executed!"); }; @@ -202,24 +210,31 @@ export default myHandler; #### 事件上下文 -每个事件包含: +每个事件都包含: ```typescript { - type: 'command' | 'session' | 'agent' | 'gateway', - action: string, // e.g., 'new', 'reset', 'stop' - sessionKey: string, // Session identifier - timestamp: Date, // When the event occurred - messages: string[], // Push messages here to send to user + type: 'command' | 'session' | 'agent' | 'gateway' | 'message', + action: string, // 例如 'new'、'reset'、'stop'、'received'、'sent' + sessionKey: string, // 会话标识符 + timestamp: Date, // 事件发生时间 + messages: string[], // 将消息推入这里以发送给用户 context: { + // 命令事件: sessionEntry?: SessionEntry, sessionId?: string, sessionFile?: string, - commandSource?: string, // e.g., 'whatsapp', 'telegram' + commandSource?: string, // 例如 'whatsapp'、'telegram' senderId?: string, workspaceDir?: string, bootstrapFiles?: WorkspaceBootstrapFile[], - cfg?: OpenClawConfig + cfg?: OpenClawConfig, + // 消息事件(完整详情见“消息事件”部分): + from?: string, // message:received + to?: string, // message:sent + content?: string, + channelId?: string, + success?: boolean, // message:sent } } ``` @@ -228,28 +243,135 @@ export default myHandler; ### 命令事件 -当发出智能体命令时触发: +在发出智能体命令时触发: - **`command`**:所有命令事件(通用监听器) -- **`command:new`**:当发出 `/new` 命令时 -- **`command:reset`**:当发出 `/reset` 命令时 -- **`command:stop`**:当发出 `/stop` 命令时 +- **`command:new`**:发出 `/new` 命令时 +- **`command:reset`**:发出 `/reset` 命令时 +- **`command:stop`**:发出 `/stop` 命令时 + +### 会话事件 + +- **`session:compact:before`**:在压缩开始总结历史记录之前 +- **`session:compact:after`**:在压缩完成并带有摘要元数据之后 + +内部 hook 负载会将这些事件表示为 `type: "session"`,并将 `action` 设为 `"compact:before"` / `"compact:after"`;监听器使用上面的组合键进行订阅。 +具体处理器注册使用字面量键格式 `${type}:${action}`。对于这些事件,请注册 `session:compact:before` 和 `session:compact:after`。 ### 智能体事件 -- **`agent:bootstrap`**:在注入工作区引导文件之前(hooks 可以修改 `context.bootstrapFiles`) +- **`agent:bootstrap`**:在工作区引导文件被注入之前(hooks 可以修改 `context.bootstrapFiles`) ### Gateway 网关事件 -当 Gateway 网关启动时触发: +在 Gateway 网关启动时触发: -- **`gateway:startup`**:在渠道启动和 hooks 加载之后 +- **`gateway:startup`**:在渠道启动且 hooks 已加载之后 + +### 消息事件 + +在消息被接收或发送时触发: + +- **`message`**:所有消息事件(通用监听器) +- **`message:received`**:当从任意渠道收到入站消息时。在处理的早期阶段触发,此时媒体理解尚未完成。对于尚未处理的媒体附件,内容中可能包含类似 `` 的原始占位符。 +- **`message:transcribed`**:当一条消息已被完全处理,包括音频转写和链接理解时触发。此时,`transcript` 包含音频消息的完整转写文本。当你需要访问已转写的音频内容时,请使用此 hook。 +- **`message:preprocessed`**:在所有媒体 + 链接理解完成后,为每条消息触发,使 hooks 可以在智能体看到消息之前访问完全增强的正文(转写、图像描述、链接摘要)。 +- **`message:sent`**:当出站消息成功发送时 + +#### 消息事件上下文 + +消息事件包含关于消息的丰富上下文: + +```typescript +// message:received context +{ + from: string, // 发送者标识符(电话号码、用户 ID 等) + content: string, // 消息内容 + timestamp?: number, // 接收时的 Unix 时间戳 + channelId: string, // 渠道(例如 "whatsapp"、"telegram"、"discord") + accountId?: string, // 多账号设置中的提供商账号 ID + conversationId?: string, // 聊天/会话 ID + messageId?: string, // 提供商返回的消息 ID + metadata?: { // 额外的提供商特定数据 + to?: string, + provider?: string, + surface?: string, + threadId?: string, + senderId?: string, + senderName?: string, + senderUsername?: string, + senderE164?: string, + } +} + +// message:sent context +{ + to: string, // 接收者标识符 + content: string, // 已发送的消息内容 + success: boolean, // 发送是否成功 + error?: string, // 如果发送失败,则为错误消息 + channelId: string, // 渠道(例如 "whatsapp"、"telegram"、"discord") + accountId?: string, // 提供商账号 ID + conversationId?: string, // 聊天/会话 ID + messageId?: string, // 提供商返回的消息 ID + isGroup?: boolean, // 此出站消息是否属于群组/渠道上下文 + groupId?: string, // 用于与 message:received 关联的群组/渠道标识符 +} + +// message:transcribed context +{ + body?: string, // 增强前的原始入站正文 + bodyForAgent?: string, // 对智能体可见的增强正文 + transcript: string, // 音频转写文本 + channelId: string, // 渠道(例如 "telegram"、"whatsapp") + conversationId?: string, + messageId?: string, +} + +// message:preprocessed context +{ + body?: string, // 原始入站正文 + bodyForAgent?: string, // 媒体/链接理解后的最终增强正文 + transcript?: string, // 存在音频时的转写内容 + channelId: string, // 渠道(例如 "telegram"、"whatsapp") + conversationId?: string, + messageId?: string, + isGroup?: boolean, + groupId?: string, +} +``` + +#### 示例:消息记录器 Hook + +```typescript +const isMessageReceivedEvent = (event: { type: string; action: string }) => + event.type === "message" && event.action === "received"; +const isMessageSentEvent = (event: { type: string; action: string }) => + event.type === "message" && event.action === "sent"; + +const handler = async (event) => { + if (isMessageReceivedEvent(event as { type: string; action: string })) { + console.log(`[message-logger] Received from ${event.context.from}: ${event.context.content}`); + } else if (isMessageSentEvent(event as { type: string; action: string })) { + console.log(`[message-logger] Sent to ${event.context.to}: ${event.context.content}`); + } +}; + +export default handler; +``` ### 工具结果 Hooks(插件 API) -这些 hooks 不是事件流监听器;它们让插件在 OpenClaw 持久化工具结果之前同步调整它们。 +这些 hooks 不是事件流监听器;它们允许插件在 OpenClaw 持久化工具结果之前同步调整工具结果。 -- **`tool_result_persist`**:在工具结果写入会话记录之前转换它们。必须是同步的;返回更新后的工具结果负载或 `undefined` 保持原样。参见 [智能体循环](/concepts/agent-loop)。 +- **`tool_result_persist`**:在工具结果写入会话转录之前对其进行转换。必须是同步的;返回更新后的工具结果负载,或返回 `undefined` 以保持原样。请参阅 [Agent Loop](/concepts/agent-loop)。 + +### 插件 Hook 事件 + +通过插件 hook 运行器公开的压缩生命周期 hooks: + +- **`before_compaction`**:在压缩前运行,并带有计数/token 元数据 +- **`after_compaction`**:在压缩后运行,并带有压缩摘要元数据 ### 未来事件 @@ -258,14 +380,12 @@ export default myHandler; - **`session:start`**:当新会话开始时 - **`session:end`**:当会话结束时 - **`agent:error`**:当智能体遇到错误时 -- **`message:sent`**:当消息被发送时 -- **`message:received`**:当消息被接收时 ## 创建自定义 Hooks ### 1. 选择位置 -- **工作区 hooks**(`/hooks/`):每智能体,最高优先级 +- **工作区 hooks**(`/hooks/`):每个智能体单独配置,优先级最高 - **托管 hooks**(`~/.openclaw/hooks/`):跨工作区共享 ### 2. 创建目录结构 @@ -280,27 +400,25 @@ cd ~/.openclaw/hooks/my-hook ```markdown --- name: my-hook -description: "Does something useful" +description: "执行某些有用的事情" metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } } --- # My Custom Hook -This hook does something useful when you issue `/new`. +当你发出 `/new` 时,此 hook 会执行一些有用的事情。 ``` ### 4. 创建 handler.ts ```typescript -import type { HookHandler } from "../../src/hooks/hooks.js"; - -const handler: HookHandler = async (event) => { +const handler = async (event) => { if (event.type !== "command" || event.action !== "new") { return; } console.log("[my-hook] Running!"); - // Your logic here + // 你的逻辑写在这里 }; export default handler; @@ -309,16 +427,16 @@ export default handler; ### 5. 启用并测试 ```bash -# Verify hook is discovered +# 验证 hook 已被发现 openclaw hooks list -# Enable it +# 启用它 openclaw hooks enable my-hook -# Restart your gateway process (menu bar app restart on macOS, or restart your dev process) +# 重启你的 Gateway 网关进程(macOS 上重启菜单栏应用,或重启你的开发进程) -# Trigger the event -# Send /new via your messaging channel +# 触发事件 +# 通过你的消息渠道发送 /new ``` ## 配置 @@ -339,9 +457,9 @@ openclaw hooks enable my-hook } ``` -### 每 Hook 配置 +### 每个 Hook 的配置 -Hooks 可以有自定义配置: +Hooks 可以具有自定义配置: ```json { @@ -378,9 +496,9 @@ Hooks 可以有自定义配置: } ``` -### 遗留配置格式(仍然支持) +### 旧版配置格式(仍受支持) -旧配置格式仍然有效以保持向后兼容: +旧配置格式仍可用于向后兼容: ```json { @@ -399,74 +517,76 @@ Hooks 可以有自定义配置: } ``` -**迁移**:对新 hooks 使用基于发现的新系统。遗留处理程序在基于目录的 hooks 之后加载。 +注意:`module` 必须是相对于工作区的路径。绝对路径和超出工作区范围的遍历路径会被拒绝。 + +**迁移**:对于新的 hooks,请使用基于发现的新系统。旧版 handlers 会在基于目录的 hooks 之后加载。 ## CLI 命令 ### 列出 Hooks ```bash -# List all hooks +# 列出所有 hooks openclaw hooks list -# Show only eligible hooks +# 仅显示符合条件的 hooks openclaw hooks list --eligible -# Verbose output (show missing requirements) +# 详细输出(显示缺失的要求) openclaw hooks list --verbose -# JSON output +# JSON 输出 openclaw hooks list --json ``` ### Hook 信息 ```bash -# Show detailed info about a hook +# 显示某个 hook 的详细信息 openclaw hooks info session-memory -# JSON output +# JSON 输出 openclaw hooks info session-memory --json ``` ### 检查资格 ```bash -# Show eligibility summary +# 显示资格摘要 openclaw hooks check -# JSON output +# JSON 输出 openclaw hooks check --json ``` ### 启用/禁用 ```bash -# Enable a hook +# 启用一个 hook openclaw hooks enable session-memory -# Disable a hook +# 禁用一个 hook openclaw hooks disable command-logger ``` -## 捆绑的 Hooks +## 内置 hook 参考 ### session-memory -当你发出 `/new` 时将会话上下文保存到记忆。 +当你发出 `/new` 时,将会话上下文保存到 memory。 **事件**:`command:new` **要求**:必须配置 `workspace.dir` -**输出**:`/memory/YYYY-MM-DD-slug.md`(默认为 `~/.openclaw/workspace`) +**输出**:`/memory/YYYY-MM-DD-slug.md`(默认是 `~/.openclaw/workspace`) -**功能**: +**它的作用**: -1. 使用预重置会话条目定位正确的记录 -2. 提取最后 15 行对话 -3. 使用 LLM 生成描述性文件名 slug -4. 将会话元数据保存到带日期的记忆文件 +1. 使用重置前的会话条目定位正确的转录 +2. 提取最近 15 行对话 +3. 使用 LLM 生成描述性的文件名 slug +4. 将会话元数据保存到带日期的 memory 文件中 **示例输出**: @@ -482,7 +602,7 @@ openclaw hooks disable command-logger - `2026-01-16-vendor-pitch.md` - `2026-01-16-api-design.md` -- `2026-01-16-1430.md`(如果 slug 生成失败则回退到时间戳) +- `2026-01-16-1430.md`(如果 slug 生成失败,则回退为时间戳) **启用**: @@ -490,9 +610,50 @@ openclaw hooks disable command-logger openclaw hooks enable session-memory ``` +### bootstrap-extra-files + +在 `agent:bootstrap` 期间注入额外的引导文件(例如 monorepo 本地的 `AGENTS.md` / `TOOLS.md`)。 + +**事件**:`agent:bootstrap` + +**要求**:必须配置 `workspace.dir` + +**输出**:不写入文件;仅在内存中修改引导上下文。 + +**配置**: + +```json +{ + "hooks": { + "internal": { + "enabled": true, + "entries": { + "bootstrap-extra-files": { + "enabled": true, + "paths": ["packages/*/AGENTS.md", "packages/*/TOOLS.md"] + } + } + } + } +} +``` + +**说明**: + +- 路径相对于工作区解析。 +- 文件必须保持在工作区内部(通过 realpath 检查)。 +- 仅加载已识别的引导基础文件名。 +- 会保留子智能体允许列表(仅 `AGENTS.md` 和 `TOOLS.md`)。 + +**启用**: + +```bash +openclaw hooks enable bootstrap-extra-files +``` + ### command-logger -将所有命令事件记录到集中审计文件。 +将所有命令事件记录到集中式审计文件。 **事件**:`command` @@ -500,10 +661,10 @@ openclaw hooks enable session-memory **输出**:`~/.openclaw/logs/commands.log` -**功能**: +**它的作用**: 1. 捕获事件详情(命令操作、时间戳、会话键、发送者 ID、来源) -2. 以 JSONL 格式追加到日志文件 +2. 以 JSONL 格式附加到日志文件 3. 在后台静默运行 **示例日志条目**: @@ -516,13 +677,13 @@ openclaw hooks enable session-memory **查看日志**: ```bash -# View recent commands +# 查看最近的命令 tail -n 20 ~/.openclaw/logs/commands.log -# Pretty-print with jq +# 使用 jq 美化输出 cat ~/.openclaw/logs/commands.log | jq . -# Filter by action +# 按操作筛选 grep '"action":"new"' ~/.openclaw/logs/commands.log | jq . ``` @@ -534,18 +695,18 @@ openclaw hooks enable command-logger ### boot-md -当 Gateway 网关启动时运行 `BOOT.md`(在渠道启动之后)。 -必须启用内部 hooks 才能运行。 +当 Gateway 网关启动时(渠道启动之后)运行 `BOOT.md`。 +必须启用内部 hooks,此功能才会运行。 **事件**:`gateway:startup` **要求**:必须配置 `workspace.dir` -**功能**: +**它的作用**: 1. 从你的工作区读取 `BOOT.md` -2. 通过智能体运行器运行指令 -3. 通过 message 工具发送任何请求的出站消息 +2. 通过智能体运行器执行其中的指令 +3. 通过消息工具发送任何请求的出站消息 **启用**: @@ -555,26 +716,26 @@ openclaw hooks enable boot-md ## 最佳实践 -### 保持处理程序快速 +### 保持处理器快速 -Hooks 在命令处理期间运行。保持它们轻量: +Hooks 在命令处理期间运行。请保持其轻量: ```typescript -// ✓ Good - async work, returns immediately +// ✓ 好 - 异步工作,立即返回 const handler: HookHandler = async (event) => { - void processInBackground(event); // Fire and forget + void processInBackground(event); // 触发后不等待 }; -// ✗ Bad - blocks command processing +// ✗ 差 - 阻塞命令处理 const handler: HookHandler = async (event) => { await slowDatabaseQuery(event); await evenSlowerAPICall(event); }; ``` -### 优雅处理错误 +### 优雅地处理错误 -始终包装有风险的操作: +始终包装高风险操作: ```typescript const handler: HookHandler = async (event) => { @@ -582,112 +743,117 @@ const handler: HookHandler = async (event) => { await riskyOperation(event); } catch (err) { console.error("[my-handler] Failed:", err instanceof Error ? err.message : String(err)); - // Don't throw - let other handlers run + // 不要抛出错误 - 让其他处理器继续运行 } }; ``` ### 尽早过滤事件 -如果事件不相关则尽早返回: +如果事件不相关,请尽早返回: ```typescript const handler: HookHandler = async (event) => { - // Only handle 'new' commands + // 仅处理 'new' 命令 if (event.type !== "command" || event.action !== "new") { return; } - // Your logic here + // 你的逻辑写在这里 }; ``` -### 使用特定事件键 +### 使用具体事件键 -尽可能在元数据中指定确切事件: +如果可能,请在元数据中指定精确事件: ```yaml -metadata: { "openclaw": { "events": ["command:new"] } } # Specific +metadata: { "openclaw": { "events": ["command:new"] } } # 精确 ``` 而不是: ```yaml -metadata: { "openclaw": { "events": ["command"] } } # General - more overhead +metadata: { "openclaw": { "events": ["command"] } } # 通用 - 开销更大 ``` ## 调试 ### 启用 Hook 日志 -Gateway 网关在启动时记录 hook 加载: +Gateway 网关会在启动时记录 hook 加载情况: ``` Registered hook: session-memory -> command:new +Registered hook: bootstrap-extra-files -> agent:bootstrap Registered hook: command-logger -> command Registered hook: boot-md -> gateway:startup ``` -### 检查发现 +### 检查发现情况 -列出所有发现的 hooks: +列出所有已发现的 hooks: ```bash openclaw hooks list --verbose ``` -### 检查注册 +### 检查注册情况 -在你的处理程序中,记录它被调用的时间: +在你的处理器中,记录它何时被调用: ```typescript const handler: HookHandler = async (event) => { console.log("[my-handler] Triggered:", event.type, event.action); - // Your logic + // 你的逻辑 }; ``` ### 验证资格 -检查为什么 hook 不符合条件: +检查某个 hook 为什么不符合条件: ```bash openclaw hooks info my-hook ``` -在输出中查找缺失的要求。 +查看输出中缺失的要求。 ## 测试 ### Gateway 网关日志 -监控 Gateway 网关日志以查看 hook 执行: +监控 Gateway 网关日志以查看 hook 执行情况: ```bash # macOS ./scripts/clawlog.sh -f -# Other platforms +# 其他平台 tail -f ~/.openclaw/gateway.log ``` ### 直接测试 Hooks -隔离测试你的处理程序: +单独测试你的 handlers: ```typescript import { test } from "vitest"; -import { createHookEvent } from "./src/hooks/hooks.js"; import myHandler from "./hooks/my-hook/handler.js"; test("my handler works", async () => { - const event = createHookEvent("command", "new", "test-session", { - foo: "bar", - }); + const event = { + type: "command", + action: "new", + sessionKey: "test-session", + timestamp: new Date(), + messages: [], + context: { foo: "bar" }, + }; await myHandler(event); - // Assert side effects + // 断言副作用 }); ``` @@ -696,8 +862,8 @@ test("my handler works", async () => { ### 核心组件 - **`src/hooks/types.ts`**:类型定义 -- **`src/hooks/workspace.ts`**:目录扫描和加载 -- **`src/hooks/frontmatter.ts`**:HOOK.md 元数据解析 +- **`src/hooks/workspace.ts`**:目录扫描与加载 +- **`src/hooks/frontmatter.ts`**:`HOOK.md` 元数据解析 - **`src/hooks/config.ts`**:资格检查 - **`src/hooks/hooks-status.ts`**:状态报告 - **`src/hooks/loader.ts`**:动态模块加载器 @@ -710,15 +876,15 @@ test("my handler works", async () => { ``` Gateway 网关启动 ↓ -扫描目录(工作区 → 托管 → 捆绑) +扫描目录(工作区 → 托管 → 内置) ↓ 解析 HOOK.md 文件 ↓ 检查资格(bins、env、config、os) ↓ -从符合条件的 hooks 加载处理程序 +从符合条件的 hooks 加载 handlers ↓ -为事件注册处理程序 +为事件注册 handlers ``` ### 事件流程 @@ -726,11 +892,11 @@ Gateway 网关启动 ``` 用户发送 /new ↓ -命令验证 +命令校验 ↓ 创建 hook 事件 ↓ -触发 hook(所有注册的处理程序) +触发 hook(所有已注册的 handlers) ↓ 命令处理继续 ↓ @@ -745,17 +911,18 @@ Gateway 网关启动 ```bash ls -la ~/.openclaw/hooks/my-hook/ - # Should show: HOOK.md, handler.ts + # 应显示:HOOK.md, handler.ts ``` 2. 验证 HOOK.md 格式: ```bash cat ~/.openclaw/hooks/my-hook/HOOK.md - # Should have YAML frontmatter with name and metadata + # 应包含带有 name 和 metadata 的 YAML frontmatter ``` -3. 列出所有发现的 hooks: +3. 列出所有已发现的 hooks: + ```bash openclaw hooks list ``` @@ -768,12 +935,12 @@ Gateway 网关启动 openclaw hooks info my-hook ``` -查找缺失的: +查看是否缺少: - 二进制文件(检查 PATH) - 环境变量 - 配置值 -- 操作系统兼容性 +- OS 兼容性 ### Hook 未执行 @@ -781,30 +948,31 @@ openclaw hooks info my-hook ```bash openclaw hooks list - # Should show ✓ next to enabled hooks + # 应在已启用的 hooks 旁显示 ✓ ``` 2. 重启你的 Gateway 网关进程以重新加载 hooks。 3. 检查 Gateway 网关日志中的错误: + ```bash ./scripts/clawlog.sh | grep hook ``` -### 处理程序错误 +### 处理器错误 检查 TypeScript/import 错误: ```bash -# Test import directly +# 直接测试导入 node -e "import('./path/to/handler.ts').then(console.log)" ``` ## 迁移指南 -### 从遗留配置到发现 +### 从旧版配置迁移到发现机制 -**之前**: +**迁移前**: ```json { @@ -822,7 +990,7 @@ node -e "import('./path/to/handler.ts').then(console.log)" } ``` -**之后**: +**迁移后**: 1. 创建 hook 目录: @@ -836,13 +1004,13 @@ node -e "import('./path/to/handler.ts').then(console.log)" ```markdown --- name: my-hook - description: "My custom hook" + description: "我的自定义 hook" metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } } --- # My Hook - Does something useful. + 执行某些有用的事情。 ``` 3. 更新配置: @@ -861,9 +1029,10 @@ node -e "import('./path/to/handler.ts').then(console.log)" ``` 4. 验证并重启你的 Gateway 网关进程: + ```bash openclaw hooks list - # Should show: 🎯 my-hook ✓ + # 应显示:🎯 my-hook ✓ ``` **迁移的好处**: @@ -876,7 +1045,7 @@ node -e "import('./path/to/handler.ts').then(console.log)" ## 另请参阅 -- [CLI 参考:hooks](/cli/hooks) -- [捆绑 Hooks README](https://github.com/openclaw/openclaw/tree/main/src/hooks/bundled) +- [CLI Reference: hooks](/cli/hooks) +- [Bundled Hooks README](https://github.com/openclaw/openclaw/tree/main/src/hooks/bundled) - [Webhook Hooks](/automation/webhook) -- [配置](/gateway/configuration#hooks) +- [Configuration](/gateway/configuration#hooks) diff --git a/docs/zh-CN/channels/bluebubbles.md b/docs/zh-CN/channels/bluebubbles.md index 4ee4cb71e3e..b7f8534065b 100644 --- a/docs/zh-CN/channels/bluebubbles.md +++ b/docs/zh-CN/channels/bluebubbles.md @@ -6,34 +6,35 @@ read_when: summary: 通过 BlueBubbles macOS 服务器使用 iMessage(REST 发送/接收、输入状态、回应、配对、高级操作)。 title: BlueBubbles x-i18n: - generated_at: "2026-02-03T10:04:52Z" - model: claude-opus-4-5 - provider: pi - source_hash: 3aae277a8bec479800a7f6268bfbca912c65a4aadc6e513694057fb873597b69 + generated_at: "2026-03-16T06:21:08Z" + model: gpt-5.4 + provider: openai + source_hash: 877592bf7b9b06abdddd7567d56e756eff229d6ffa5056ef33fa3356086aa580 source_path: channels/bluebubbles.md workflow: 15 --- # BlueBubbles(macOS REST) -状态:内置插件,通过 HTTP 与 BlueBubbles macOS 服务器通信。由于其更丰富的 API 和更简便的设置,**推荐用于 iMessage 集成**,优于旧版 imsg 渠道。 +状态:内置插件,通过 HTTP 与 BlueBubbles macOS 服务器通信。由于其 API 更丰富且设置比旧版 imsg 渠道更简单,**推荐用于 iMessage 集成**。 -## 概述 +## 概览 - 通过 BlueBubbles 辅助应用在 macOS 上运行([bluebubbles.app](https://bluebubbles.app))。 -- 推荐/已测试版本:macOS Sequoia (15)。macOS Tahoe (26) 可用;但在 Tahoe 上编辑功能目前不可用,群组图标更新可能显示成功但实际未同步。 -- OpenClaw 通过其 REST API 与之通信(`GET /api/v1/ping`、`POST /message/text`、`POST /chat/:id/*`)。 -- 传入消息通过 webhook 到达;发出的回复、输入指示器、已读回执和 tapback 均为 REST 调用。 -- 附件和贴纸作为入站媒体被接收(并在可能时呈现给智能体)。 -- 配对/白名单的工作方式与其他渠道相同(`/channels/pairing` 等),使用 `channels.bluebubbles.allowFrom` + 配对码。 -- 回应作为系统事件呈现,与 Slack/Telegram 类似,智能体可以在回复前"提及"它们。 +- 推荐/已测试:macOS Sequoia(15)。macOS Tahoe(26)可用;目前编辑功能在 Tahoe 上已损坏,群组图标更新可能会报告成功但不会同步。 +- OpenClaw 通过其 REST API 与其通信(`GET /api/v1/ping`、`POST /message/text`、`POST /chat/:id/*`)。 +- 传入消息通过 webhook 到达;传出回复、输入状态指示、已读回执和 tapback 都通过 REST 调用完成。 +- 附件和贴纸会作为入站媒体接收(并在可能时展示给智能体)。 +- 配对/allowlist 的工作方式与其他渠道相同(`/channels/pairing` 等),使用 `channels.bluebubbles.allowFrom` + 配对码。 +- 回应会像 Slack/Telegram 一样作为系统事件呈现,因此智能体可以在回复前“提及”它们。 - 高级功能:编辑、撤回、回复线程、消息效果、群组管理。 ## 快速开始 -1. 在你的 Mac 上安装 BlueBubbles 服务器(按照 [bluebubbles.app/install](https://bluebubbles.app/install) 的说明操作)。 -2. 在 BlueBubbles 配置中,启用 web API 并设置密码。 +1. 在你的 Mac 上安装 BlueBubbles 服务器(按照 [bluebubbles.app/install](https://bluebubbles.app/install) 上的说明操作)。 +2. 在 BlueBubbles 配置中启用 Web API 并设置密码。 3. 运行 `openclaw onboard` 并选择 BlueBubbles,或手动配置: + ```json5 { channels: { @@ -46,8 +47,89 @@ x-i18n: }, } ``` + 4. 将 BlueBubbles webhook 指向你的 Gateway 网关(示例:`https://your-gateway-host:3000/bluebubbles-webhook?password=`)。 -5. 启动 Gateway 网关;它将注册 webhook 处理程序并开始配对。 +5. 启动 Gateway 网关;它会注册 webhook 处理器并开始配对。 + +安全说明: + +- 始终设置 webhook 密码。 +- 始终要求进行 webhook 身份验证。无论 loopback/代理拓扑如何,除非 BlueBubbles webhook 请求包含与 `channels.bluebubbles.password` 匹配的 password/guid(例如 `?password=` 或 `x-password`),否则 OpenClaw 会拒绝该请求。 +- 在读取/解析完整 webhook 请求体之前就会检查密码身份验证。 + +## 保持 Messages.app 处于活动状态(VM / 无头设置) + +某些 macOS VM / 常开设置可能会导致 Messages.app 进入“空闲”状态(传入事件会停止,直到应用被打开/切到前台)。一个简单的解决方法是使用 AppleScript + LaunchAgent **每 5 分钟“触碰”一次 Messages**。 + +### 1)保存 AppleScript + +将以下内容保存为: + +- `~/Scripts/poke-messages.scpt` + +示例脚本(非交互式;不会抢占焦点): + +```applescript +try + tell application "Messages" + if not running then + launch + end if + + -- Touch the scripting interface to keep the process responsive. + set _chatCount to (count of chats) + end tell +on error + -- Ignore transient failures (first-run prompts, locked session, etc). +end try +``` + +### 2)安装 LaunchAgent + +将以下内容保存为: + +- `~/Library/LaunchAgents/com.user.poke-messages.plist` + +```xml + + + + + Label + com.user.poke-messages + + ProgramArguments + + /bin/bash + -lc + /usr/bin/osascript "$HOME/Scripts/poke-messages.scpt" + + + RunAtLoad + + + StartInterval + 300 + + StandardOutPath + /tmp/poke-messages.log + StandardErrorPath + /tmp/poke-messages.err + + +``` + +说明: + +- 这会**每 300 秒**运行一次,并且**在登录时**运行。 +- 首次运行可能会触发 macOS 的**自动化**权限提示(`osascript` → Messages)。请在运行该 LaunchAgent 的同一用户会话中批准这些提示。 + +加载它: + +```bash +launchctl unload ~/Library/LaunchAgents/com.user.poke-messages.plist 2>/dev/null || true +launchctl load ~/Library/LaunchAgents/com.user.poke-messages.plist +``` ## 新手引导 @@ -60,10 +142,10 @@ openclaw onboard 向导会提示输入: - **服务器 URL**(必填):BlueBubbles 服务器地址(例如 `http://192.168.1.100:1234`) -- **密码**(必填):来自 BlueBubbles 服务器设置的 API 密码 +- **密码**(必填):来自 BlueBubbles Server 设置的 API 密码 - **Webhook 路径**(可选):默认为 `/bluebubbles-webhook` -- **私信策略**:配对、白名单、开放或禁用 -- **白名单**:电话号码、电子邮件或聊天目标 +- **私信策略**:pairing、allowlist、open 或 disabled +- **允许列表**:电话号码、电子邮件或聊天目标 你也可以通过 CLI 添加 BlueBubbles: @@ -75,12 +157,12 @@ openclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --passwor 私信: -- 默认:`channels.bluebubbles.dmPolicy = "pairing"`。 -- 未知发送者会收到配对码;在批准之前消息会被忽略(配对码 1 小时后过期)。 -- 批准方式: +- 默认值:`channels.bluebubbles.dmPolicy = "pairing"`。 +- 未知发送者会收到一个配对码;在获得批准前,消息会被忽略(代码 1 小时后过期)。 +- 通过以下方式批准: - `openclaw pairing list bluebubbles` - `openclaw pairing approve bluebubbles ` -- 配对是默认的令牌交换方式。详情:[配对](/channels/pairing) +- 配对是默认的令牌交换方式。详情见:[配对](/channels/pairing) 群组: @@ -89,13 +171,13 @@ openclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --passwor ### 提及门控(群组) -BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: +BlueBubbles 支持群聊中的提及门控,与 iMessage/WhatsApp 的行为一致: - 使用 `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)检测提及。 -- 当群组启用 `requireMention` 时,智能体仅在被提及时响应。 +- 当某个群组启用 `requireMention` 时,智能体只有在被提及时才会响应。 - 来自授权发送者的控制命令会绕过提及门控。 -单群组配置: +每群组配置: ```json5 { @@ -104,8 +186,8 @@ BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: groupPolicy: "allowlist", groupAllowFrom: ["+15555550123"], groups: { - "*": { requireMention: true }, // 所有群组的默认设置 - "iMessage;-;chat123": { requireMention: false }, // 特定群组的覆盖设置 + "*": { requireMention: true }, // 所有群组的默认值 + "iMessage;-;chat123": { requireMention: false }, // 对特定群组覆盖 }, }, }, @@ -115,14 +197,14 @@ BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: ### 命令门控 - 控制命令(例如 `/config`、`/model`)需要授权。 -- 使用 `allowFrom` 和 `groupAllowFrom` 确定命令授权。 -- 授权发送者即使在群组中未被提及也可以运行控制命令。 +- 使用 `allowFrom` 和 `groupAllowFrom` 来判断命令授权。 +- 授权发送者即使在群组中未提及,也可以运行控制命令。 ## 输入状态 + 已读回执 -- **输入指示器**:在响应生成前和生成期间自动发送。 +- **输入状态指示**:会在生成响应之前和期间自动发送。 - **已读回执**:由 `channels.bluebubbles.sendReadReceipts` 控制(默认:`true`)。 -- **输入指示器**:OpenClaw 发送输入开始事件;BlueBubbles 在发送或超时时自动清除输入状态(通过 DELETE 手动停止不可靠)。 +- **输入状态指示**:OpenClaw 会发送输入开始事件;BlueBubbles 会在发送后或超时后自动清除输入状态(通过 DELETE 手动停止并不可靠)。 ```json5 { @@ -136,7 +218,7 @@ BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: ## 高级操作 -BlueBubbles 在配置中启用时支持高级消息操作: +在配置中启用后,BlueBubbles 支持高级消息操作: ```json5 { @@ -144,15 +226,15 @@ BlueBubbles 在配置中启用时支持高级消息操作: bluebubbles: { actions: { reactions: true, // tapback(默认:true) - edit: true, // 编辑已发送消息(macOS 13+,在 macOS 26 Tahoe 上不可用) + edit: true, // 编辑已发送消息(macOS 13+,在 macOS 26 Tahoe 上已损坏) unsend: true, // 撤回消息(macOS 13+) - reply: true, // 通过消息 GUID 进行回复线程 + reply: true, // 按消息 GUID 回复线程 sendWithEffect: true, // 消息效果(slam、loud 等) renameGroup: true, // 重命名群聊 setGroupIcon: true, // 设置群聊图标/照片(在 macOS 26 Tahoe 上不稳定) - addParticipant: true, // 将参与者添加到群组 + addParticipant: true, // 向群组添加参与者 removeParticipant: true, // 从群组移除参与者 - leaveGroup: true, // 离开群聊 + leaveGroup: true, // 退出群聊 sendAttachment: true, // 发送附件/媒体 }, }, @@ -163,37 +245,37 @@ BlueBubbles 在配置中启用时支持高级消息操作: 可用操作: - **react**:添加/移除 tapback 回应(`messageId`、`emoji`、`remove`) -- **edit**:编辑已发送的消息(`messageId`、`text`) +- **edit**:编辑已发送消息(`messageId`、`text`) - **unsend**:撤回消息(`messageId`) - **reply**:回复特定消息(`messageId`、`text`、`to`) - **sendWithEffect**:带 iMessage 效果发送(`text`、`to`、`effectId`) - **renameGroup**:重命名群聊(`chatGuid`、`displayName`) -- **setGroupIcon**:设置群聊图标/照片(`chatGuid`、`media`)— 在 macOS 26 Tahoe 上不稳定(API 可能返回成功但图标未同步)。 -- **addParticipant**:将某人添加到群组(`chatGuid`、`address`) -- **removeParticipant**:将某人从群组移除(`chatGuid`、`address`) -- **leaveGroup**:离开群聊(`chatGuid`) +- **setGroupIcon**:设置群聊图标/照片(`chatGuid`、`media`)——在 macOS 26 Tahoe 上不稳定(API 可能返回成功,但图标不会同步)。 +- **addParticipant**:向群组添加某人(`chatGuid`、`address`) +- **removeParticipant**:从群组移除某人(`chatGuid`、`address`) +- **leaveGroup**:退出群聊(`chatGuid`) - **sendAttachment**:发送媒体/文件(`to`、`buffer`、`filename`、`asVoice`) - - 语音备忘录:将 `asVoice: true` 与 **MP3** 或 **CAF** 音频一起设置,以 iMessage 语音消息形式发送。BlueBubbles 在发送语音备忘录时会将 MP3 转换为 CAF。 + - 语音备忘录:将 `asVoice: true` 与 **MP3** 或 **CAF** 音频一起设置,即可作为 iMessage 语音消息发送。BlueBubbles 在发送语音备忘录时会将 MP3 转换为 CAF。 -### 消息 ID(短格式 vs 完整格式) +### 消息 ID(短 ID 与完整 ID) OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 - `MessageSid` / `ReplyToId` 可以是短 ID。 - `MessageSidFull` / `ReplyToIdFull` 包含提供商的完整 ID。 -- 短 ID 存储在内存中;它们可能在重启或缓存清除后过期。 -- 操作接受短或完整的 `messageId`,但如果短 ID 不再可用将会报错。 +- 短 ID 存在于内存中;它们可能会在重启或缓存清除后失效。 +- 操作接受短或完整 `messageId`,但如果短 ID 不再可用,就会报错。 对于持久化自动化和存储,请使用完整 ID: - 模板:`{{MessageSidFull}}`、`{{ReplyToIdFull}}` - 上下文:入站负载中的 `MessageSidFull` / `ReplyToIdFull` -参见[配置](/gateway/configuration)了解模板变量。 +模板变量请参见[配置](/gateway/configuration)。 ## 分块流式传输 -控制响应是作为单条消息发送还是分块流式传输: +控制响应是作为单条消息发送,还是按块流式发送: ```json5 { @@ -208,8 +290,8 @@ OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 ## 媒体 + 限制 - 入站附件会被下载并存储在媒体缓存中。 -- 媒体上限通过 `channels.bluebubbles.mediaMaxMb` 设置(默认:8 MB)。 -- 出站文本按 `channels.bluebubbles.textChunkLimit` 分块(默认:4000 字符)。 +- 通过 `channels.bluebubbles.mediaMaxMb` 控制入站和出站媒体大小上限(默认:8 MB)。 +- 出站文本会按 `channels.bluebubbles.textChunkLimit` 分块(默认:4000 个字符)。 ## 配置参考 @@ -217,22 +299,23 @@ OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 提供商选项: -- `channels.bluebubbles.enabled`:启用/禁用渠道。 +- `channels.bluebubbles.enabled`:启用/禁用该渠道。 - `channels.bluebubbles.serverUrl`:BlueBubbles REST API 基础 URL。 - `channels.bluebubbles.password`:API 密码。 - `channels.bluebubbles.webhookPath`:Webhook 端点路径(默认:`/bluebubbles-webhook`)。 - `channels.bluebubbles.dmPolicy`:`pairing | allowlist | open | disabled`(默认:`pairing`)。 -- `channels.bluebubbles.allowFrom`:私信白名单(句柄、电子邮件、E.164 号码、`chat_id:*`、`chat_guid:*`)。 +- `channels.bluebubbles.allowFrom`:私信 allowlist(handle、电子邮件、E.164 号码、`chat_id:*`、`chat_guid:*`)。 - `channels.bluebubbles.groupPolicy`:`open | allowlist | disabled`(默认:`allowlist`)。 -- `channels.bluebubbles.groupAllowFrom`:群组发送者白名单。 -- `channels.bluebubbles.groups`:单群组配置(`requireMention` 等)。 +- `channels.bluebubbles.groupAllowFrom`:群组发送者 allowlist。 +- `channels.bluebubbles.groups`:每群组配置(`requireMention` 等)。 - `channels.bluebubbles.sendReadReceipts`:发送已读回执(默认:`true`)。 -- `channels.bluebubbles.blockStreaming`:启用分块流式传输(默认:`false`;流式回复必需)。 -- `channels.bluebubbles.textChunkLimit`:出站分块大小(字符)(默认:4000)。 -- `channels.bluebubbles.chunkMode`:`length`(默认)仅在超过 `textChunkLimit` 时分割;`newline` 在长度分块前先按空行(段落边界)分割。 -- `channels.bluebubbles.mediaMaxMb`:入站媒体上限(MB)(默认:8)。 -- `channels.bluebubbles.historyLimit`:上下文的最大群组消息数(0 表示禁用)。 -- `channels.bluebubbles.dmHistoryLimit`:私信历史限制。 +- `channels.bluebubbles.blockStreaming`:启用分块流式传输(默认:`false`;流式回复所必需)。 +- `channels.bluebubbles.textChunkLimit`:出站分块大小(按字符计,默认:4000)。 +- `channels.bluebubbles.chunkMode`:`length`(默认)仅在超过 `textChunkLimit` 时拆分;`newline` 会先按空行(段落边界)拆分,再按长度分块。 +- `channels.bluebubbles.mediaMaxMb`:入站/出站媒体大小上限(MB,默认:8)。 +- `channels.bluebubbles.mediaLocalRoots`:允许用于出站本地媒体路径的绝对本地目录显式 allowlist。默认情况下,本地路径发送会被拒绝,除非配置了此项。每账户覆盖:`channels.bluebubbles.accounts..mediaLocalRoots`。 +- `channels.bluebubbles.historyLimit`:用于上下文的最大群组消息数(0 表示禁用)。 +- `channels.bluebubbles.dmHistoryLimit`:私信历史记录上限。 - `channels.bluebubbles.actions`:启用/禁用特定操作。 - `channels.bluebubbles.accounts`:多账户配置。 @@ -241,31 +324,31 @@ OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 - `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)。 - `messages.responsePrefix`。 -## 地址 / 投递目标 +## 寻址 / 送达目标 -优先使用 `chat_guid` 以获得稳定的路由: +优先使用 `chat_guid` 以获得稳定路由: -- `chat_guid:iMessage;-;+15555550123`(群组推荐) +- `chat_guid:iMessage;-;+15555550123`(群组首选) - `chat_id:123` - `chat_identifier:...` -- 直接句柄:`+15555550123`、`user@example.com` - - 如果直接句柄没有现有的私信聊天,OpenClaw 将通过 `POST /api/v1/chat/new` 创建一个。这需要启用 BlueBubbles Private API。 +- 直接 handle:`+15555550123`、`user@example.com` + - 如果某个直接 handle 没有现有私信聊天,OpenClaw 会通过 `POST /api/v1/chat/new` 创建一个。这要求启用 BlueBubbles Private API。 -## 安全性 +## 安全 -- Webhook 请求通过比较 `guid`/`password` 查询参数或头部与 `channels.bluebubbles.password` 进行身份验证。来自 `localhost` 的请求也会被接受。 -- 保持 API 密码和 webhook 端点的机密性(将它们视为凭证)。 -- localhost 信任意味着同主机的反向代理可能无意中绕过密码验证。如果你使用代理 Gateway 网关,请在代理处要求身份验证并配置 `gateway.trustedProxies`。参见 [Gateway 网关安全性](/gateway/security#reverse-proxy-configuration)。 -- 如果将 BlueBubbles 服务器暴露在局域网之外,请启用 HTTPS + 防火墙规则。 +- 通过将查询参数或请求头中的 `guid`/`password` 与 `channels.bluebubbles.password` 进行比较来验证 webhook 请求。来自 `localhost` 的请求也会被接受。 +- 请妥善保管 API 密码和 webhook 端点(将它们视为凭证)。 +- 对 localhost 的信任意味着同主机上的反向代理可能会无意中绕过密码。如果你对 Gateway 网关进行了代理,请在代理层要求身份验证,并配置 `gateway.trustedProxies`。请参见 [Gateway 网关安全](/gateway/security#reverse-proxy-configuration)。 +- 如果要在局域网外暴露 BlueBubbles 服务器,请启用 HTTPS + 防火墙规则。 ## 故障排除 -- 如果输入/已读事件停止工作,请检查 BlueBubbles webhook 日志并验证 Gateway 网关路径是否与 `channels.bluebubbles.webhookPath` 匹配。 -- 配对码在一小时后过期;使用 `openclaw pairing list bluebubbles` 和 `openclaw pairing approve bluebubbles `。 -- 回应需要 BlueBubbles private API(`POST /api/v1/message/react`);确保服务器版本支持它。 -- 编辑/撤回需要 macOS 13+ 和兼容的 BlueBubbles 服务器版本。在 macOS 26(Tahoe)上,由于 private API 变更,编辑功能目前不可用。 -- 在 macOS 26(Tahoe)上群组图标更新可能不稳定:API 可能返回成功但新图标未同步。 -- OpenClaw 会根据 BlueBubbles 服务器的 macOS 版本自动隐藏已知不可用的操作。如果在 macOS 26(Tahoe)上编辑仍然显示,请使用 `channels.bluebubbles.actions.edit=false` 手动禁用。 -- 查看状态/健康信息:`openclaw status --all` 或 `openclaw status --deep`。 +- 如果输入状态/已读事件停止工作,请检查 BlueBubbles webhook 日志,并验证 Gateway 网关路径与 `channels.bluebubbles.webhookPath` 一致。 +- 配对码会在一小时后过期;使用 `openclaw pairing list bluebubbles` 和 `openclaw pairing approve bluebubbles `。 +- 回应需要 BlueBubbles private API(`POST /api/v1/message/react`);请确保服务器版本提供该接口。 +- 编辑/撤回需要 macOS 13+ 以及兼容的 BlueBubbles 服务器版本。在 macOS 26(Tahoe)上,由于 private API 变更,编辑功能目前已损坏。 +- 群组图标更新在 macOS 26(Tahoe)上可能不稳定:API 可能返回成功,但新图标不会同步。 +- OpenClaw 会根据 BlueBubbles 服务器的 macOS 版本自动隐藏已知损坏的操作。如果在 macOS 26(Tahoe)上仍显示编辑操作,请手动使用 `channels.bluebubbles.actions.edit=false` 禁用它。 +- 状态/健康信息请使用:`openclaw status --all` 或 `openclaw status --deep`。 -有关通用渠道工作流参考,请参阅[渠道](/channels)和[插件](/tools/plugin)指南。 +有关通用渠道工作流程参考,请参见 [Channels](/channels) 和 [Plugins](/tools/plugin) 指南。 diff --git a/docs/zh-CN/channels/feishu.md b/docs/zh-CN/channels/feishu.md index 6a8d8633af9..af561e3a8ea 100644 --- a/docs/zh-CN/channels/feishu.md +++ b/docs/zh-CN/channels/feishu.md @@ -1,22 +1,29 @@ --- -summary: "飞书机器人支持状态、功能和配置" read_when: - - 您想要连接飞书机器人 - - 您正在配置飞书渠道 + - 你想连接一个飞书/Lark 机器人 + - 你正在配置飞书渠道 +summary: 飞书机器人概览、功能和配置 title: 飞书 +x-i18n: + generated_at: "2026-03-16T06:21:11Z" + model: gpt-5.4 + provider: openai + source_hash: 951e78c5c7264471382f863fa896a15ddeaf0717ef782da20d0f1b3eb23396ba + source_path: channels/feishu.md + workflow: 15 --- # 飞书机器人 -状态:生产就绪,支持机器人私聊和群组。使用 WebSocket 长连接模式接收消息。 +飞书(Lark)是企业用于消息沟通与协作的团队聊天平台。此插件通过平台的 WebSocket 事件订阅将 OpenClaw 连接到飞书/Lark 机器人,因此无需暴露公共 webhook URL 即可接收消息。 --- -## 内置插件 +## 捆绑插件 -当前版本的 OpenClaw 已内置 Feishu 插件,因此通常不需要单独安装。 +飞书随当前的 OpenClaw 版本一同捆绑提供,因此无需单独安装插件。 -如果你使用的是较旧版本,或是没有内置 Feishu 的自定义安装,可手动安装: +如果你使用的是较旧版本,或使用了不包含捆绑飞书的自定义安装,请手动安装: ```bash openclaw plugins install @openclaw/feishu @@ -26,75 +33,75 @@ openclaw plugins install @openclaw/feishu ## 快速开始 -添加飞书渠道有两种方式: +有两种方式可添加飞书渠道: -### 方式一:通过安装向导添加(推荐) +### 方法 1:设置向导(推荐) -如果您刚安装完 OpenClaw,可以直接运行向导,根据提示添加飞书: +如果你刚安装 OpenClaw,请运行设置向导: ```bash openclaw onboard ``` -向导会引导您完成: +向导会引导你完成以下步骤: -1. 创建飞书应用并获取凭证 -2. 配置应用凭证 -3. 启动网关 +1. 创建飞书应用并收集凭证 +2. 在 OpenClaw 中配置应用凭证 +3. 启动 Gateway 网关 -✅ **完成配置后**,您可以使用以下命令检查网关状态: +✅ **配置完成后**,检查 Gateway 网关状态: -- `openclaw gateway status` - 查看网关运行状态 -- `openclaw logs --follow` - 查看实时日志 +- `openclaw gateway status` +- `openclaw logs --follow` -### 方式二:通过命令行添加 +### 方法 2:CLI 设置 -如果您已经完成了初始安装,可以用以下命令添加飞书渠道: +如果你已经完成初始安装,可通过 CLI 添加该渠道: ```bash openclaw channels add ``` -然后根据交互式提示选择 Feishu,输入 App ID 和 App Secret 即可。 +选择 **Feishu**,然后输入 App ID 和 App Secret。 -✅ **完成配置后**,您可以使用以下命令管理网关: +✅ **配置完成后**,管理 Gateway 网关: -- `openclaw gateway status` - 查看网关运行状态 -- `openclaw gateway restart` - 重启网关以应用新配置 -- `openclaw logs --follow` - 查看实时日志 +- `openclaw gateway status` +- `openclaw gateway restart` +- `openclaw logs --follow` --- -## 第一步:创建飞书应用 +## 第 1 步:创建飞书应用 ### 1. 打开飞书开放平台 -访问 [飞书开放平台](https://open.feishu.cn/app),使用飞书账号登录。 +访问 [Feishu Open Platform](https://open.feishu.cn/app) 并登录。 -Lark(国际版)请使用 https://open.larksuite.com/app,并在配置中设置 `domain: "lark"`。 +Lark(国际版)租户应使用 [https://open.larksuite.com/app](https://open.larksuite.com/app),并在飞书配置中设置 `domain: "lark"`。 ### 2. 创建应用 -1. 点击 **创建企业自建应用** +1. 点击 **Create enterprise app** 2. 填写应用名称和描述 3. 选择应用图标 -![创建企业自建应用](/images/feishu-step2-create-app.png) +![Create enterprise app](../images/feishu-step2-create-app.png) -### 3. 获取应用凭证 +### 3. 复制凭证 -在应用的 **凭证与基础信息** 页面,复制: +在 **Credentials & Basic Info** 中,复制: -- **App ID**(格式如 `cli_xxx`) +- **App ID**(格式:`cli_xxx`) - **App Secret** -❗ **重要**:请妥善保管 App Secret,不要分享给他人。 +❗ **重要:**请将 App Secret 妥善保密。 -![获取应用凭证](/images/feishu-step3-credentials.png) +![Get credentials](../images/feishu-step3-credentials.png) -### 4. 配置应用权限 +### 4. 配置权限 -在 **权限管理** 页面,点击 **批量导入** 按钮,粘贴以下 JSON 配置一键导入所需权限: +在 **Permissions** 中,点击 **Batch import** 并粘贴: ```json { @@ -105,81 +112,71 @@ Lark(国际版)请使用 https://open.larksuite.com/app,并在配置中设 "application:application.app_message_stats.overview:readonly", "application:application:self_manage", "application:bot.menu:write", + "cardkit:card:read", "cardkit:card:write", "contact:user.employee_id:readonly", "corehr:file:download", - "docs:document.content:read", "event:ip_list", - "im:chat", "im:chat.access_event.bot_p2p_chat:read", "im:chat.members:bot_access", "im:message", "im:message.group_at_msg:readonly", - "im:message.group_msg", "im:message.p2p_msg:readonly", "im:message:readonly", "im:message:send_as_bot", - "im:resource", - "sheets:spreadsheet", - "wiki:wiki:readonly" + "im:resource" ], "user": ["aily:file:read", "aily:file:write", "im:chat.access_event.bot_p2p_chat:read"] } } ``` -![配置应用权限](/images/feishu-step4-permissions.png) +![Configure permissions](../images/feishu-step4-permissions.png) ### 5. 启用机器人能力 -在 **应用能力** > **机器人** 页面: +在 **App Capability** > **Bot** 中: -1. 开启机器人能力 -2. 配置机器人名称 +1. 启用机器人能力 +2. 设置机器人名称 -![启用机器人能力](/images/feishu-step5-bot-capability.png) +![Enable bot capability](../images/feishu-step5-bot-capability.png) ### 6. 配置事件订阅 -⚠️ **重要提醒**:在配置事件订阅前,请务必确保已完成以下步骤: +⚠️ **重要:**在设置事件订阅前,请确保: -1. 运行 `openclaw channels add` 添加了 Feishu 渠道 -2. 网关处于启动状态(可通过 `openclaw gateway status` 检查状态) +1. 你已经为飞书运行过 `openclaw channels add` +2. Gateway 网关正在运行(`openclaw gateway status`) -在 **事件订阅** 页面: +在 **Event Subscription** 中: -1. 选择 **使用长连接接收事件**(WebSocket 模式) -2. 添加事件: - - `im.message.receive_v1` - - `im.message.reaction.created_v1` - - `im.message.reaction.deleted_v1` - - `application.bot.menu_v6` +1. 选择 **Use long connection to receive events**(WebSocket) +2. 添加事件:`im.message.receive_v1` -⚠️ **注意**:如果网关未启动或渠道未添加,长连接设置将保存失败。 +⚠️ 如果 Gateway 网关未运行,长连接设置可能无法保存。 -![配置事件订阅](/images/feishu-step6-event-subscription.png) +![Configure event subscription](../images/feishu-step6-event-subscription.png) ### 7. 发布应用 -1. 在 **版本管理与发布** 页面创建版本 +1. 在 **Version Management & Release** 中创建版本 2. 提交审核并发布 -3. 等待管理员审批(企业自建应用通常自动通过) +3. 等待管理员批准(企业应用通常会自动批准) --- -## 第二步:配置 OpenClaw +## 第 2 步:配置 OpenClaw -### 通过向导配置(推荐) - -运行以下命令,根据提示粘贴 App ID 和 App Secret: +### 使用向导配置(推荐) ```bash openclaw channels add ``` -选择 **Feishu**,然后输入您在第一步获取的凭证即可。 +选择 **Feishu**,然后粘贴你的 App ID 和 App Secret。 -### 通过配置文件配置 +### 通过配置文件进行配置 编辑 `~/.openclaw/openclaw.json`: @@ -193,7 +190,7 @@ openclaw channels add main: { appId: "cli_xxx", appSecret: "xxx", - botName: "我的AI助手", + botName: "My AI assistant", }, }, }, @@ -201,18 +198,20 @@ openclaw channels add } ``` -若使用 `connectionMode: "webhook"`,需设置 `verificationToken`。飞书 Webhook 服务默认绑定 `127.0.0.1`;仅在需要不同监听地址时设置 `webhookHost`。 +如果你使用 `connectionMode: "webhook"`,请同时设置 `verificationToken` 和 `encryptKey`。飞书 webhook 服务器默认绑定到 `127.0.0.1`;只有在你明确需要不同绑定地址时,才设置 `webhookHost`。 -#### 获取 Verification Token(仅 Webhook 模式) +#### Verification Token 和 Encrypt Key(webhook 模式) -使用 Webhook 模式时,需在配置中设置 `channels.feishu.verificationToken`。获取方式: +使用 webhook 模式时,请在配置中同时设置 `channels.feishu.verificationToken` 和 `channels.feishu.encryptKey`。获取这些值的方法如下: -1. 在飞书开放平台打开您的应用 -2. 进入 **开发配置** → **事件与回调** -3. 打开 **加密策略** 选项卡 -4. 复制 **Verification Token**(校验令牌) +1. 在飞书开放平台中,打开你的应用 +2. 前往 **Development** → **Events & Callbacks**(开发配置 → 事件与回调) +3. 打开 **Encryption** 标签页(加密策略) +4. 复制 **Verification Token** 和 **Encrypt Key** -![Verification Token 位置](/images/feishu-verification-token.png) +下图展示了 **Verification Token** 的位置。**Encrypt Key** 位于同一个 **Encryption** 区域中。 + +![Verification Token location](../images/feishu-verification-token.png) ### 通过环境变量配置 @@ -223,7 +222,7 @@ export FEISHU_APP_SECRET="xxx" ### Lark(国际版)域名 -如果您的租户在 Lark(国际版),请设置域名为 `lark`(或完整域名),可配置 `channels.feishu.domain` 或 `channels.feishu.accounts..domain`: +如果你的租户位于 Lark(国际版),请将域名设置为 `lark`(或完整域名字串)。你可以在 `channels.feishu.domain` 设置,也可以按账户设置(`channels.feishu.accounts..domain`)。 ```json5 { @@ -241,14 +240,14 @@ export FEISHU_APP_SECRET="xxx" } ``` -### 配额优化 +### 配额优化标志 -可通过以下可选配置减少飞书 API 调用: +你可以使用两个可选标志来减少飞书 API 使用量: -- `typingIndicator`(默认 `true`):设为 `false` 时不发送“正在输入”状态。 -- `resolveSenderNames`(默认 `true`):设为 `false` 时不拉取发送者资料。 +- `typingIndicator`(默认 `true`):设为 `false` 时,跳过“正在输入”反应调用。 +- `resolveSenderNames`(默认 `true`):设为 `false` 时,跳过发送者资料查询调用。 -可在渠道级或账号级配置: +你可以在顶层或按账户进行设置: ```json5 { @@ -271,9 +270,9 @@ export FEISHU_APP_SECRET="xxx" --- -## 第三步:启动并测试 +## 第 3 步:启动并测试 -### 1. 启动网关 +### 1. 启动 Gateway 网关 ```bash openclaw gateway @@ -281,74 +280,74 @@ openclaw gateway ### 2. 发送测试消息 -在飞书中找到您创建的机器人,发送一条消息。 +在飞书中找到你的机器人并发送一条消息。 -### 3. 配对授权 +### 3. 批准配对 -默认情况下,机器人会回复一个 **配对码**。您需要批准此代码: +默认情况下,机器人会回复一个配对码。批准它: ```bash -openclaw pairing approve feishu <配对码> +openclaw pairing approve feishu ``` -批准后即可正常对话。 +批准后,你就可以正常聊天了。 --- -## 介绍 +## 概览 -- **飞书机器人渠道**:由网关管理的飞书机器人 -- **确定性路由**:回复始终返回飞书,模型不会选择渠道 -- **会话隔离**:私聊共享主会话;群组独立隔离 -- **WebSocket 连接**:使用飞书 SDK 的长连接模式,无需公网 URL +- **飞书机器人渠道**:由 Gateway 网关管理的飞书机器人 +- **确定性路由**:回复始终返回到飞书 +- **会话隔离**:私信共享主会话;群组彼此隔离 +- **WebSocket 连接**:通过飞书 SDK 建立长连接,无需公共 URL --- ## 访问控制 -### 私聊访问 +### 私信 -- **默认**:`dmPolicy: "pairing"`,陌生用户会收到配对码 +- **默认**:`dmPolicy: "pairing"`(未知用户会收到配对码) - **批准配对**: - ```bash - openclaw pairing list feishu # 查看待审批列表 - openclaw pairing approve feishu # 批准 - ``` -- **白名单模式**:通过 `channels.feishu.allowFrom` 配置允许的用户 Open ID -### 群组访问 + ```bash + openclaw pairing list feishu + openclaw pairing approve feishu + ``` + +- **Allowlist 模式**:设置 `channels.feishu.allowFrom`,填入允许的 Open ID + +### 群聊 **1. 群组策略**(`channels.feishu.groupPolicy`): -- `"open"` = 允许群组中所有人(默认) -- `"allowlist"` = 仅允许 `groupAllowFrom` 中的群组 -- `"disabled"` = 禁用群组消息 +- `"open"` = 允许群组中的所有人(默认) +- `"allowlist"` = 仅允许 `groupAllowFrom` +- `"disabled"` = 禁用群消息 -**2. @提及要求**(`channels.feishu.groups..requireMention`): +**2. 提及要求**(`channels.feishu.groups..requireMention`): -- `true` = 需要 @机器人才响应(默认) -- `false` = 无需 @也响应 +- `true` = 需要 @ 提及(默认) +- `false` = 无需提及也会回复 --- ## 群组配置示例 -### 允许所有群组,需要 @提及(默认行为) +### 允许所有群组,要求 @ 提及(默认) ```json5 { channels: { feishu: { groupPolicy: "open", - // 默认 requireMention: true + // Default requireMention: true }, }, } ``` -### 允许所有群组,无需 @提及 - -需要为特定群组配置: +### 允许所有群组,无需 @ 提及 ```json5 { @@ -369,16 +368,16 @@ openclaw pairing approve feishu <配对码> channels: { feishu: { groupPolicy: "allowlist", - // 群组 ID 格式为 oc_xxx + // Feishu group IDs (chat_id) look like: oc_xxx groupAllowFrom: ["oc_xxx", "oc_yyy"], }, }, } ``` -### 仅允许特定成员在群组中发信(发送者白名单) +### 限制哪些发送者可以在群组中发消息(发送者 allowlist) -除群组白名单外,该群组内**所有消息**均按发送者 open_id 校验:仅 `groups..allowFrom` 中列出的用户消息会被处理,其他成员的消息会被忽略(此为发送者级白名单,不仅针对 /reset、/new 等控制命令)。 +除了允许群组本身外,该群组中的**所有消息**还会按发送者 `open_id` 进行限制:只有列在 `groups..allowFrom` 中的用户,其消息才会被处理;其他成员发送的消息会被忽略(这是完整的发送者级限制,而不只是对 `/reset` 或 `/new` 等控制命令生效)。 ```json5 { @@ -388,7 +387,7 @@ openclaw pairing approve feishu <配对码> groupAllowFrom: ["oc_xxx"], groups: { oc_xxx: { - // 用户 open_id 格式为 ou_xxx + // Feishu user IDs (open_id) look like: ou_xxx allowFrom: ["ou_user1", "ou_user2"], }, }, @@ -401,29 +400,31 @@ openclaw pairing approve feishu <配对码> ## 获取群组/用户 ID -### 获取群组 ID(chat_id) +### 群组 ID(`chat_id`) -群组 ID 格式为 `oc_xxx`,可以通过以下方式获取: +群组 ID 看起来像 `oc_xxx`。 -**方法一**(推荐): +**方法 1(推荐)** -1. 启动网关并在群组中 @机器人发消息 -2. 运行 `openclaw logs --follow` 查看日志中的 `chat_id` +1. 启动 Gateway 网关并在群里 @ 提及机器人 +2. 运行 `openclaw logs --follow` 并查找 `chat_id` -**方法二**: -使用飞书 API 调试工具获取机器人所在群组列表。 +**方法 2** -### 获取用户 ID(open_id) +使用飞书 API 调试器列出群聊。 -用户 ID 格式为 `ou_xxx`,可以通过以下方式获取: +### 用户 ID(`open_id`) -**方法一**(推荐): +用户 ID 看起来像 `ou_xxx`。 -1. 启动网关并给机器人发消息 -2. 运行 `openclaw logs --follow` 查看日志中的 `open_id` +**方法 1(推荐)** -**方法二**: -查看配对请求列表,其中包含用户的 Open ID: +1. 启动 Gateway 网关并向机器人发送私信 +2. 运行 `openclaw logs --follow` 并查找 `open_id` + +**方法 2** + +检查配对请求中的用户 Open ID: ```bash openclaw pairing list feishu @@ -433,65 +434,61 @@ openclaw pairing list feishu ## 常用命令 -| 命令 | 说明 | +| Command | Description | | --------- | -------------- | -| `/status` | 查看机器人状态 | -| `/reset` | 重置对话会话 | -| `/model` | 查看/切换模型 | +| `/status` | 显示机器人状态 | +| `/reset` | 重置会话 | +| `/model` | 显示/切换模型 | -飞书机器人菜单建议直接在飞书开放平台的机器人能力页面配置。OpenClaw 当前支持接收 `application.bot.menu_v6` 事件,并把点击事件转换成普通文本命令(例如 `/menu `)继续走现有消息路由,但不通过渠道配置自动创建或同步菜单。 +> 注意:飞书暂不支持原生命令菜单,因此命令必须以文本形式发送。 -## 网关管理命令 +## Gateway 网关管理命令 -在配置和使用飞书渠道时,您可能需要使用以下网关管理命令: - -| 命令 | 说明 | -| -------------------------- | ----------------- | -| `openclaw gateway status` | 查看网关运行状态 | -| `openclaw gateway install` | 安装/启动网关服务 | -| `openclaw gateway stop` | 停止网关服务 | -| `openclaw gateway restart` | 重启网关服务 | -| `openclaw logs --follow` | 实时查看日志输出 | +| Command | Description | +| -------------------------- | -------------------------- | +| `openclaw gateway status` | 显示 Gateway 网关状态 | +| `openclaw gateway install` | 安装/启动 Gateway 网关服务 | +| `openclaw gateway stop` | 停止 Gateway 网关服务 | +| `openclaw gateway restart` | 重启 Gateway 网关服务 | +| `openclaw logs --follow` | 跟踪 Gateway 网关日志 | --- ## 故障排除 -### 机器人在群组中不响应 +### 机器人在群聊中没有响应 -1. 检查机器人是否已添加到群组 -2. 检查是否 @了机器人(默认需要 @提及) -3. 检查 `groupPolicy` 是否为 `"disabled"` -4. 查看日志:`openclaw logs --follow` +1. 确保机器人已加入群组 +2. 确保你 @ 提及了机器人(默认行为) +3. 检查 `groupPolicy` 未设置为 `"disabled"` +4. 检查日志:`openclaw logs --follow` -### 机器人收不到消息 +### 机器人未接收到消息 -1. 检查应用是否已发布并审批通过 -2. 检查事件订阅是否配置正确(`im.message.receive_v1`) -3. 检查是否选择了 **长连接** 模式 -4. 检查应用权限是否完整 -5. 检查网关是否正在运行:`openclaw gateway status` -6. 查看实时日志:`openclaw logs --follow` +1. 确保应用已发布并获批准 +2. 确保事件订阅包含 `im.message.receive_v1` +3. 确保已启用**长连接** +4. 确保应用权限完整 +5. 确保 Gateway 网关正在运行:`openclaw gateway status` +6. 检查日志:`openclaw logs --follow` -### App Secret 泄露怎么办 +### App Secret 泄露 -1. 在飞书开放平台重置 App Secret -2. 更新配置文件中的 App Secret -3. 重启网关 +1. 在飞书开放平台中重置 App Secret +2. 在你的配置中更新 App Secret +3. 重启 Gateway 网关 -### 发送消息失败 +### 消息发送失败 -1. 检查应用是否有 `im:message:send_as_bot` 权限 -2. 检查应用是否已发布 -3. 查看日志获取详细错误信息 +1. 确保应用具有 `im:message:send_as_bot` 权限 +2. 确保应用已发布 +3. 查看日志以获取详细错误信息 --- ## 高级配置 -### 多账号配置 - -如果需要管理多个飞书机器人,可配置 `defaultAccount` 指定出站未显式指定 `accountId` 时使用的账号: +### 多账户 ```json5 { @@ -502,13 +499,13 @@ openclaw pairing list feishu main: { appId: "cli_xxx", appSecret: "xxx", - botName: "主机器人", + botName: "Primary bot", }, backup: { appId: "cli_yyy", appSecret: "yyy", - botName: "备用机器人", - enabled: false, // 暂时禁用 + botName: "Backup bot", + enabled: false, }, }, }, @@ -516,104 +513,102 @@ openclaw pairing list feishu } ``` +`defaultAccount` 用于控制当出站 API 未显式指定 `accountId` 时,使用哪个飞书账户。 + ### 消息限制 -- `textChunkLimit`:出站文本分块大小(默认 2000 字符) -- `mediaMaxMb`:媒体上传/下载限制(默认 30MB) +- `textChunkLimit`:出站文本分块大小(默认:2000 个字符) +- `mediaMaxMb`:媒体上传/下载限制(默认:30 MB) -### 流式输出 +### 流式传输 -飞书支持通过交互式卡片实现流式输出,机器人会实时更新卡片内容显示生成进度。默认配置: +飞书通过交互式卡片支持流式回复。启用后,机器人会在生成文本时更新卡片。 ```json5 { channels: { feishu: { streaming: true, // 启用流式卡片输出(默认 true) - blockStreamingCoalesce: { - enabled: true, - minDelayMs: 50, - maxDelayMs: 250, - }, + blockStreaming: true, // 启用分块流式传输(默认 true) }, }, } ``` -如需禁用流式输出(等待完整回复后一次性发送),可设置 `streaming: false`。 +将 `streaming: false` 设为等待完整回复生成后再发送。 -### 交互式卡片 +### ACP 会话 -OpenClaw 默认会在需要时发送 Markdown 卡片;如果你需要完整的 Feishu 原生交互式卡片,也可以显式发送原始 `card` payload。 +飞书支持以下 ACP 场景: -- 默认路径:文本自动渲染或 Markdown 卡片 -- 显式卡片:通过消息动作的 `card` 参数发送原始交互卡片 -- 更新卡片:同一消息支持后续 patch/update +- 私信 +- 群组话题会话 -卡片按钮回调当前走文本回退路径: +飞书 ACP 由文本命令驱动。没有原生斜杠命令菜单,因此请直接在会话中使用 `/acp ...` 消息。 -- 若 `action.value.text` 存在,则作为入站文本继续处理 -- 若 `action.value.command` 存在,则作为命令文本继续处理 -- 其他对象值会序列化为 JSON 文本 +#### 持久化 ACP 绑定 -这样可以保持与现有消息/命令路由兼容,而不要求下游先理解 Feishu 专有的交互 payload。 - -### 表情反应 - -飞书渠道现已完整支持表情反应生命周期: - -- 接收 `reaction created` -- 接收 `reaction deleted` -- 主动添加反应 -- 主动删除自身反应 -- 查询消息上的反应列表 - -是否把入站反应转成内部消息,可通过 `reactionNotifications` 控制: - -| 值 | 行为 | -| ----- | ---------------------------- | -| `off` | 不生成反应通知 | -| `own` | 仅当反应发生在机器人消息上时 | -| `all` | 所有可验证的反应都生成通知 | - -### 消息引用 - -在群聊中,机器人的回复可以引用用户发送的原始消息,让对话上下文更加清晰。 - -配置选项: +使用顶层类型化 ACP 绑定,将飞书私信或话题会话固定到持久化 ACP 会话。 ```json5 { - channels: { - feishu: { - // 账户级别配置(默认 "all") - replyToMode: "all", - groups: { - oc_xxx: { - // 特定群组可以覆盖 - replyToMode: "first", + agents: { + list: [ + { + id: "codex", + runtime: { + type: "acp", + acp: { + agent: "codex", + backend: "acpx", + mode: "persistent", + cwd: "/workspace/openclaw", + }, }, }, - }, + ], }, + bindings: [ + { + type: "acp", + agentId: "codex", + match: { + channel: "feishu", + accountId: "default", + peer: { kind: "direct", id: "ou_1234567890" }, + }, + }, + { + type: "acp", + agentId: "codex", + match: { + channel: "feishu", + accountId: "default", + peer: { kind: "group", id: "oc_group_chat:topic:om_topic_root" }, + }, + acp: { label: "codex-feishu-topic" }, + }, + ], } ``` -`replyToMode` 值说明: +#### 从聊天中按线程绑定 ACP 生成 -| 值 | 行为 | -| --------- | ---------------------------------- | -| `"off"` | 不引用原消息(私聊默认值) | -| `"first"` | 仅在第一条回复时引用原消息 | -| `"all"` | 所有回复都引用原消息(群聊默认值) | +在飞书私信或话题会话中,你可以就地生成并绑定一个 ACP 会话: -> 注意:消息引用功能与流式卡片输出(`streaming: true`)不能同时使用。当启用流式输出时,回复会以卡片形式呈现,不会显示引用。 +```text +/acp spawn codex --thread here +``` -### 多 Agent 路由 +说明: -通过 `bindings` 配置,您可以用一个飞书机器人对接多个不同功能或性格的 Agent。系统会根据用户 ID 或群组 ID 自动将对话分发到对应的 Agent。 +- `--thread here` 适用于私信和飞书话题。 +- 绑定后的私信/话题中的后续消息会直接路由到该 ACP 会话。 +- v1 不支持针对通用的非话题群聊。 -配置示例: +### 多智能体路由 + +使用 `bindings` 将飞书私信或群组路由到不同的智能体。 ```json5 { @@ -634,91 +629,81 @@ OpenClaw 默认会在需要时发送 Markdown 卡片;如果你需要完整的 }, bindings: [ { - // 用户 A 的私聊 → main agent agentId: "main", match: { channel: "feishu", - peer: { kind: "dm", id: "ou_28b31a88..." }, + peer: { kind: "direct", id: "ou_xxx" }, }, }, { - // 用户 B 的私聊 → clawd-fan agent agentId: "clawd-fan", match: { channel: "feishu", - peer: { kind: "dm", id: "ou_0fe6b1c9..." }, + peer: { kind: "direct", id: "ou_yyy" }, }, }, { - // 某个群组 → clawd-xi agent agentId: "clawd-xi", match: { channel: "feishu", - peer: { kind: "group", id: "oc_xxx..." }, + peer: { kind: "group", id: "oc_zzz" }, }, }, ], } ``` -匹配规则说明: +路由字段: -| 字段 | 说明 | -| ----------------- | --------------------------------------------- | -| `agentId` | 目标 Agent 的 ID,需要在 `agents.list` 中定义 | -| `match.channel` | 渠道类型,这里固定为 `"feishu"` | -| `match.peer.kind` | 对话类型:`"dm"`(私聊)或 `"group"`(群组) | -| `match.peer.id` | 用户 Open ID(`ou_xxx`)或群组 ID(`oc_xxx`) | +- `match.channel`:`"feishu"` +- `match.peer.kind`:`"direct"` 或 `"group"` +- `match.peer.id`:用户 Open ID(`ou_xxx`)或群组 ID(`oc_xxx`) -> 获取 ID 的方法:参见上文 [获取群组/用户 ID](#获取群组用户-id) 章节。 +查找提示请参见 [获取群组/用户 ID](#get-groupuser-ids)。 --- ## 配置参考 -完整配置请参考:[网关配置](/gateway/configuration) +完整配置:[Gateway 网关配置](/gateway/configuration) -主要选项: +关键选项: -| 配置项 | 说明 | 默认值 | -| ------------------------------------------------- | --------------------------------- | ---------------- | -| `channels.feishu.enabled` | 启用/禁用渠道 | `true` | -| `channels.feishu.domain` | API 域名(`feishu` 或 `lark`) | `feishu` | -| `channels.feishu.connectionMode` | 事件传输模式(websocket/webhook) | `websocket` | -| `channels.feishu.defaultAccount` | 出站路由默认账号 ID | `default` | -| `channels.feishu.verificationToken` | Webhook 模式必填 | - | -| `channels.feishu.webhookPath` | Webhook 路由路径 | `/feishu/events` | -| `channels.feishu.webhookHost` | Webhook 监听地址 | `127.0.0.1` | -| `channels.feishu.webhookPort` | Webhook 监听端口 | `3000` | -| `channels.feishu.accounts..appId` | 应用 App ID | - | -| `channels.feishu.accounts..appSecret` | 应用 App Secret | - | -| `channels.feishu.accounts..domain` | 单账号 API 域名覆盖 | `feishu` | -| `channels.feishu.dmPolicy` | 私聊策略 | `pairing` | -| `channels.feishu.allowFrom` | 私聊白名单(open_id 列表) | - | -| `channels.feishu.groupPolicy` | 群组策略 | `allowlist` | -| `channels.feishu.groupAllowFrom` | 群组白名单 | - | -| `channels.feishu.groups..requireMention` | 是否需要 @提及 | `true` | -| `channels.feishu.groups..enabled` | 是否启用该群组 | `true` | -| `channels.feishu.replyInThread` | 群聊回复是否进入飞书话题线程 | `disabled` | -| `channels.feishu.groupSessionScope` | 群聊会话隔离粒度 | `group` | -| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` | -| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` | -| `channels.feishu.streaming` | 启用流式卡片输出 | `true` | -| `channels.feishu.blockStreamingCoalesce.enabled` | 启用块级流式合并 | `true` | -| `channels.feishu.typingIndicator` | 发送“正在输入”状态 | `true` | -| `channels.feishu.resolveSenderNames` | 拉取发送者名称 | `true` | -| `channels.feishu.reactionNotifications` | 入站反应通知策略 | `own` | +| Setting | Description | Default | +| ------------------------------------------------- | -------------------------------- | ---------------- | +| `channels.feishu.enabled` | 启用/禁用渠道 | `true` | +| `channels.feishu.domain` | API 域名(`feishu` 或 `lark`) | `feishu` | +| `channels.feishu.connectionMode` | 事件传输模式 | `websocket` | +| `channels.feishu.defaultAccount` | 出站路由的默认账户 ID | `default` | +| `channels.feishu.verificationToken` | webhook 模式必填 | - | +| `channels.feishu.encryptKey` | webhook 模式必填 | - | +| `channels.feishu.webhookPath` | webhook 路由路径 | `/feishu/events` | +| `channels.feishu.webhookHost` | webhook 绑定主机 | `127.0.0.1` | +| `channels.feishu.webhookPort` | webhook 绑定端口 | `3000` | +| `channels.feishu.accounts..appId` | App ID | - | +| `channels.feishu.accounts..appSecret` | App Secret | - | +| `channels.feishu.accounts..domain` | 按账户覆盖 API 域名 | `feishu` | +| `channels.feishu.dmPolicy` | 私信策略 | `pairing` | +| `channels.feishu.allowFrom` | 私信 allowlist(`open_id` 列表) | - | +| `channels.feishu.groupPolicy` | 群组策略 | `open` | +| `channels.feishu.groupAllowFrom` | 群组 allowlist | - | +| `channels.feishu.groups..requireMention` | 要求 @ 提及 | `true` | +| `channels.feishu.groups..enabled` | 启用群组 | `true` | +| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` | +| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` | +| `channels.feishu.streaming` | 启用流式卡片输出 | `true` | +| `channels.feishu.blockStreaming` | 启用分块流式传输 | `true` | --- -## dmPolicy 策略说明 +## dmPolicy 参考 -| 值 | 行为 | -| ------------- | -------------------------------------------------- | -| `"pairing"` | **默认**。未知用户收到配对码,管理员批准后才能对话 | -| `"allowlist"` | 仅 `allowFrom` 列表中的用户可对话,其他静默忽略 | -| `"open"` | 允许所有人对话(需在 allowFrom 中加 `"*"`) | -| `"disabled"` | 完全禁止私聊 | +| Value | Behavior | +| ------------- | ---------------------------------------------------- | +| `"pairing"` | **默认。**未知用户会收到配对码;必须获批准后才能使用 | +| `"allowlist"` | 只有 `allowFrom` 中的用户可以聊天 | +| `"open"` | 允许所有用户(要求 `allowFrom` 中有 `"*"`) | +| `"disabled"` | 禁用私信 | --- @@ -726,17 +711,17 @@ OpenClaw 默认会在需要时发送 Markdown 卡片;如果你需要完整的 ### 接收 -- ✅ 文本消息 -- ✅ 富文本(帖子) +- ✅ 文本 +- ✅ 富文本(post) - ✅ 图片 - ✅ 文件 - ✅ 音频 - ✅ 视频 -- ✅ 表情包 +- ✅ 贴纸 ### 发送 -- ✅ 文本消息 +- ✅ 文本 - ✅ 图片 - ✅ 文件 - ✅ 音频 diff --git a/docs/zh-CN/channels/nostr.md b/docs/zh-CN/channels/nostr.md index 7d0359ce21d..47efa40aedf 100644 --- a/docs/zh-CN/channels/nostr.md +++ b/docs/zh-CN/channels/nostr.md @@ -1,14 +1,14 @@ --- read_when: - - 你希望 OpenClaw 通过 Nostr 接收私信 - - 你正在设置去中心化消息 -summary: 通过 NIP-04 加密消息的 Nostr 私信渠道 + - 你想让 OpenClaw 通过 Nostr 接收私信 + - 你正在设置去中心化消息传递 +summary: 通过 NIP-04 加密消息实现的 Nostr 私信渠道 title: Nostr x-i18n: - generated_at: "2026-02-03T07:44:13Z" - model: claude-opus-4-5 - provider: pi - source_hash: 6b9fe4c74bf5e7c0f59bbaa129ec5270fd29a248551a8a9a7dde6cff8fb46111 + generated_at: "2026-03-16T06:20:37Z" + model: gpt-5.4 + provider: openai + source_hash: fcce57da49256971420c4bb099aebb7944f8c7e8619b17b163da685add225001 source_path: channels/nostr.md workflow: 15 --- @@ -17,21 +17,21 @@ x-i18n: **状态:** 可选插件(默认禁用)。 -Nostr 是一个去中心化的社交网络协议。此渠道使 OpenClaw 能够通过 NIP-04 接收和回复加密私信(DMs)。 +Nostr 是一种用于社交网络的去中心化协议。此渠道使 OpenClaw 能够通过 NIP-04 接收并回复加密私信。 ## 安装(按需) ### 新手引导(推荐) -- 新手引导向导(`openclaw onboard`)和 `openclaw channels add` 会列出可选的渠道插件。 -- 选择 Nostr 会提示你按需安装插件。 +- 设置向导(`openclaw onboard`)和 `openclaw channels add` 会列出可选渠道插件。 +- 选择 Nostr 时,系统会提示你按需安装该插件。 -安装默认值: +安装默认行为: -- **Dev 渠道 + git checkout 可用:** 使用本地插件路径。 -- **Stable/Beta:** 从 npm 下载。 +- **Dev 渠道 + 可用的 git 检出:** 使用本地插件路径。 +- **稳定版 / Beta:** 从 npm 下载。 -你可以随时在提示中覆盖选择。 +你始终可以在提示中覆盖该选择。 ### 手动安装 @@ -39,24 +39,33 @@ Nostr 是一个去中心化的社交网络协议。此渠道使 OpenClaw 能够 openclaw plugins install @openclaw/nostr ``` -使用本地 checkout(开发工作流): +使用本地检出(dev 工作流): ```bash openclaw plugins install --link /extensions/nostr ``` -安装或启用插件后重启 Gateway 网关。 +安装或启用插件后,重启 Gateway 网关。 + +### 非交互式设置 + +```bash +openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" +openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" --relay-urls "wss://relay.damus.io,wss://relay.primal.net" +``` + +使用 `--use-env` 可将 `NOSTR_PRIVATE_KEY` 保留在环境中,而不是将密钥存储在配置里。 ## 快速设置 -1. 生成 Nostr 密钥对(如需要): +1. 生成一个 Nostr 密钥对(如有需要): ```bash -# 使用 nak +# Using nak nak key generate ``` -2. 添加到配置: +2. 添加到配置中: ```json { @@ -78,19 +87,19 @@ export NOSTR_PRIVATE_KEY="nsec1..." ## 配置参考 -| 键 | 类型 | 默认值 | 描述 | +| 键 | 类型 | 默认值 | 说明 | | ------------ | -------- | ------------------------------------------- | --------------------------- | | `privateKey` | string | 必填 | `nsec` 或十六进制格式的私钥 | | `relays` | string[] | `['wss://relay.damus.io', 'wss://nos.lol']` | 中继 URL(WebSocket) | | `dmPolicy` | string | `pairing` | 私信访问策略 | -| `allowFrom` | string[] | `[]` | 允许的发送者公钥 | -| `enabled` | boolean | `true` | 启用/禁用渠道 | +| `allowFrom` | string[] | `[]` | 允许的发送方公钥 | +| `enabled` | boolean | `true` | 启用 / 禁用渠道 | | `name` | string | - | 显示名称 | -| `profile` | object | - | NIP-01 个人资料元数据 | +| `profile` | object | - | NIP-01 资料元数据 | -## 个人资料元数据 +## 资料元数据 -个人资料数据作为 NIP-01 `kind:0` 事件发布。你可以从控制界面(Channels -> Nostr -> Profile)管理它,或直接在配置中设置。 +资料数据会作为 NIP-01 `kind:0` 事件发布。你可以在控制 UI 中管理它(Channels -> Nostr -> Profile),也可以直接在配置中设置。 示例: @@ -114,19 +123,19 @@ export NOSTR_PRIVATE_KEY="nsec1..." } ``` -注意事项: +注意: -- 个人资料 URL 必须使用 `https://`。 -- 从中继导入会合并字段并保留本地覆盖。 +- 资料 URL 必须使用 `https://`。 +- 从中继导入时会合并字段,并保留本地覆盖项。 ## 访问控制 ### 私信策略 -- **pairing**(默认):未知发送者会收到配对码。 +- **pairing**(默认):未知发送方会收到一个配对码。 - **allowlist**:只有 `allowFrom` 中的公钥可以发送私信。 -- **open**:公开接收私信(需要 `allowFrom: ["*"]`)。 -- **disabled**:忽略接收的私信。 +- **open**:公开接收入站私信(要求 `allowFrom: ["*"]`)。 +- **disabled**:忽略入站私信。 ### 允许列表示例 @@ -166,26 +175,26 @@ export NOSTR_PRIVATE_KEY="nsec1..." 提示: -- 使用 2-3 个中继以实现冗余。 +- 使用 2 到 3 个中继以实现冗余。 - 避免使用过多中继(延迟、重复)。 - 付费中继可以提高可靠性。 -- 本地中继适合测试(`ws://localhost:7777`)。 +- 本地中继也适合测试(`ws://localhost:7777`)。 ## 协议支持 -| NIP | 状态 | 描述 | -| ------ | ------ | ----------------------------- | -| NIP-01 | 已支持 | 基本事件格式 + 个人资料元数据 | -| NIP-04 | 已支持 | 加密私信(`kind:4`) | -| NIP-17 | 计划中 | 礼物包装私信 | -| NIP-44 | 计划中 | 版本化加密 | +| NIP | 状态 | 说明 | +| ------ | ------ | ------------------------- | +| NIP-01 | 已支持 | 基础事件格式 + 资料元数据 | +| NIP-04 | 已支持 | 加密私信(`kind:4`) | +| NIP-17 | 计划中 | Gift-wrapped 私信 | +| NIP-44 | 计划中 | 版本化加密 | ## 测试 ### 本地中继 ```bash -# 启动 strfry +# Start strfry docker run -p 7777:7777 ghcr.io/hoytech/strfry ``` @@ -203,38 +212,38 @@ docker run -p 7777:7777 ghcr.io/hoytech/strfry ### 手动测试 1. 从日志中记下机器人公钥(npub)。 -2. 打开 Nostr 客户端(Damus、Amethyst 等)。 -3. 向机器人公钥发送私信。 -4. 验证响应。 +2. 打开一个 Nostr 客户端(Damus、Amethyst 等)。 +3. 向该机器人公钥发送私信。 +4. 验证回复。 ## 故障排除 -### 未收到消息 +### 未接收到消息 -- 验证私钥是否有效。 -- 确保中继 URL 可访问并使用 `wss://`(本地使用 `ws://`)。 +- 验证私钥有效。 +- 确保中继 URL 可访问,并使用 `wss://`(本地则使用 `ws://`)。 - 确认 `enabled` 不是 `false`。 - 检查 Gateway 网关日志中的中继连接错误。 -### 未发送响应 +### 未发送回复 - 检查中继是否接受写入。 -- 验证出站连接。 +- 验证出站连接性。 - 注意中继速率限制。 -### 重复响应 +### 重复回复 -- 使用多个中继时属于正常现象。 -- 消息按事件 ID 去重;只有首次投递会触发响应。 +- 使用多个中继时这是预期行为。 +- 消息会按事件 ID 去重;只有首次投递会触发回复。 -## 安全 +## 安全性 - 切勿提交私钥。 -- 使用环境变量存储密钥。 -- 生产环境机器人考虑使用 `allowlist`。 +- 对密钥使用环境变量。 +- 对生产机器人考虑使用 `allowlist`。 ## 限制(MVP) - 仅支持私信(不支持群聊)。 - 不支持媒体附件。 -- 仅支持 NIP-04(计划支持 NIP-17 礼物包装)。 +- 仅支持 NIP-04(计划支持 NIP-17 gift-wrap)。 diff --git a/docs/zh-CN/channels/synology-chat.md b/docs/zh-CN/channels/synology-chat.md new file mode 100644 index 00000000000..4323e5d12d7 --- /dev/null +++ b/docs/zh-CN/channels/synology-chat.md @@ -0,0 +1,138 @@ +--- +read_when: + - 设置 OpenClaw 与 Synology Chat + - 调试 Synology Chat webhook 路由 +summary: Synology Chat webhook 设置与 OpenClaw 配置 +title: Synology Chat +x-i18n: + generated_at: "2026-03-16T06:20:51Z" + model: gpt-5.4 + provider: openai + source_hash: 7d77598ea759f89873a1edf0a3a7e7fedc1e4a7067709aaca6b999056a89eb1a + source_path: channels/synology-chat.md + workflow: 15 +--- + +# Synology Chat(插件) + +状态:通过插件支持,作为使用 Synology Chat webhook 的私信渠道。 +该插件接受来自 Synology Chat 出站 webhook 的入站消息,并通过 Synology Chat 入站 webhook 发送回复。 + +## 需要插件 + +Synology Chat 基于插件,不属于默认的核心渠道安装内容。 + +从本地检出安装: + +```bash +openclaw plugins install ./extensions/synology-chat +``` + +详情:[插件](/tools/plugin) + +## 快速设置 + +1. 安装并启用 Synology Chat 插件。 + - `openclaw onboard` 现在会在与 `openclaw channels add` 相同的渠道设置列表中显示 Synology Chat。 + - 非交互式设置:`openclaw channels add --channel synology-chat --token --url ` +2. 在 Synology Chat 集成中: + - 创建一个入站 webhook 并复制其 URL。 + - 使用你的 secret token 创建一个出站 webhook。 +3. 将出站 webhook URL 指向你的 OpenClaw Gateway 网关: + - 默认是 `https://gateway-host/webhook/synology`。 + - 或者使用你自定义的 `channels.synology-chat.webhookPath`。 +4. 在 OpenClaw 中完成设置。 + - 引导式:`openclaw onboard` + - 直接设置:`openclaw channels add --channel synology-chat --token --url ` +5. 重启 Gateway 网关,并向 Synology Chat 机器人发送一条私信。 + +最小配置: + +```json5 +{ + channels: { + "synology-chat": { + enabled: true, + token: "synology-outgoing-token", + incomingUrl: "https://nas.example.com/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=2&token=...", + webhookPath: "/webhook/synology", + dmPolicy: "allowlist", + allowedUserIds: ["123456"], + rateLimitPerMinute: 30, + allowInsecureSsl: false, + }, + }, +} +``` + +## 环境变量 + +对于默认账户,你可以使用环境变量: + +- `SYNOLOGY_CHAT_TOKEN` +- `SYNOLOGY_CHAT_INCOMING_URL` +- `SYNOLOGY_NAS_HOST` +- `SYNOLOGY_ALLOWED_USER_IDS`(逗号分隔) +- `SYNOLOGY_RATE_LIMIT` +- `OPENCLAW_BOT_NAME` + +配置值会覆盖环境变量。 + +## 私信策略与访问控制 + +- 推荐的默认值是 `dmPolicy: "allowlist"`。 +- `allowedUserIds` 接受 Synology 用户 ID 列表(或逗号分隔字符串)。 +- 在 `allowlist` 模式下,空的 `allowedUserIds` 列表会被视为配置错误,webhook 路由将不会启动(如需允许所有人,请使用 `dmPolicy: "open"`)。 +- `dmPolicy: "open"` 允许任何发送方。 +- `dmPolicy: "disabled"` 会阻止私信。 +- 配对批准可配合以下命令使用: + - `openclaw pairing list synology-chat` + - `openclaw pairing approve synology-chat ` + +## 出站投递 + +使用数字形式的 Synology Chat 用户 ID 作为目标。 + +示例: + +```bash +openclaw message send --channel synology-chat --target 123456 --text "Hello from OpenClaw" +openclaw message send --channel synology-chat --target synology-chat:123456 --text "Hello again" +``` + +支持通过基于 URL 的文件投递发送媒体。 + +## 多账户 + +支持在 `channels.synology-chat.accounts` 下配置多个 Synology Chat 账户。 +每个账户都可以覆盖 token、入站 URL、webhook 路径、私信策略和限制。 + +```json5 +{ + channels: { + "synology-chat": { + enabled: true, + accounts: { + default: { + token: "token-a", + incomingUrl: "https://nas-a.example.com/...token=...", + }, + alerts: { + token: "token-b", + incomingUrl: "https://nas-b.example.com/...token=...", + webhookPath: "/webhook/synology-alerts", + dmPolicy: "allowlist", + allowedUserIds: ["987654"], + }, + }, + }, + }, +} +``` + +## 安全说明 + +- 妥善保管 `token`,如果泄露请轮换。 +- 除非你明确可信任本地 NAS 的自签名证书,否则请保持 `allowInsecureSsl: false`。 +- 入站 webhook 请求会按 token 验证,并按发送方进行速率限制。 +- 生产环境优先使用 `dmPolicy: "allowlist"`。 diff --git a/docs/zh-CN/cli/index.md b/docs/zh-CN/cli/index.md index c22fad5c4b4..46be3ef8ab1 100644 --- a/docs/zh-CN/cli/index.md +++ b/docs/zh-CN/cli/index.md @@ -1,14 +1,14 @@ --- read_when: - - 添加或修改 CLI 命令或选项 - - 为新命令界面编写文档 -summary: OpenClaw `openclaw` 命令、子命令和选项的 CLI 参考 + - 添加或修改 CLI 命令或选项时 + - 为新的命令界面编写文档时 +summary: "`openclaw` 命令、子命令和选项的 OpenClaw CLI 参考" title: CLI 参考 x-i18n: - generated_at: "2026-02-03T07:47:54Z" - model: claude-opus-4-5 - provider: pi - source_hash: a73923763d7b89d4b183f569d543927ffbfd1f3e02f9e66639913f6daf226850 + generated_at: "2026-03-16T06:22:35Z" + model: gpt-5.4 + provider: openai + source_hash: a2bca34fca64558a8d91fc640ad3880e79677e81d0f605083edc6cbe86bfba53 source_path: cli/index.md workflow: 15 --- @@ -23,8 +23,10 @@ x-i18n: - [`onboard`](/cli/onboard) - [`configure`](/cli/configure) - [`config`](/cli/config) +- [`completion`](/cli/completion) - [`doctor`](/cli/doctor) - [`dashboard`](/cli/dashboard) +- [`backup`](/cli/backup) - [`reset`](/cli/reset) - [`uninstall`](/cli/uninstall) - [`update`](/cli/update) @@ -40,6 +42,7 @@ x-i18n: - [`system`](/cli/system) - [`models`](/cli/models) - [`memory`](/cli/memory) +- [`directory`](/cli/directory) - [`nodes`](/cli/nodes) - [`devices`](/cli/devices) - [`node`](/cli/node) @@ -53,42 +56,46 @@ x-i18n: - [`hooks`](/cli/hooks) - [`webhooks`](/cli/webhooks) - [`pairing`](/cli/pairing) +- [`qr`](/cli/qr) - [`plugins`](/cli/plugins)(插件命令) - [`channels`](/cli/channels) - [`security`](/cli/security) +- [`secrets`](/cli/secrets) - [`skills`](/cli/skills) +- [`daemon`](/cli/daemon)(Gateway 网关服务命令的旧别名) +- [`clawbot`](/cli/clawbot)(旧别名命名空间) - [`voicecall`](/cli/voicecall)(插件;如已安装) ## 全局标志 -- `--dev`:将状态隔离到 `~/.openclaw-dev` 下并调整默认端口。 +- `--dev`:将状态隔离到 `~/.openclaw-dev` 下,并变更默认端口。 - `--profile `:将状态隔离到 `~/.openclaw-` 下。 - `--no-color`:禁用 ANSI 颜色。 -- `--update`:`openclaw update` 的简写(仅限源码安装)。 -- `-V`、`--version`、`-v`:打印版本并退出。 +- `--update`:`openclaw update` 的简写(仅适用于源码安装)。 +- `-V`, `--version`, `-v`:打印版本并退出。 ## 输出样式 - ANSI 颜色和进度指示器仅在 TTY 会话中渲染。 -- OSC-8 超链接在支持的终端中渲染为可点击链接;否则回退到纯 URL。 -- `--json`(以及支持的地方使用 `--plain`)禁用样式以获得干净输出。 -- `--no-color` 禁用 ANSI 样式;也支持 `NO_COLOR=1`。 -- 长时间运行的命令显示进度指示器(支持时使用 OSC 9;4)。 +- OSC-8 超链接会在受支持的终端中显示为可点击链接;否则会回退为纯 URL。 +- `--json`(以及在支持处的 `--plain`)会禁用样式,以获得干净输出。 +- `--no-color` 会禁用 ANSI 样式;同时也支持 `NO_COLOR=1`。 +- 长时间运行的命令会显示进度指示器(支持时使用 OSC 9;4)。 -## 颜色调色板 +## 调色板 -OpenClaw 在 CLI 输出中使用龙虾调色板。 +OpenClaw 在 CLI 输出中使用龙虾色调调色板。 -- `accent`(#FF5A2D):标题、标签、主要高亮。 -- `accentBright`(#FF7A3D):命令名称、强调。 -- `accentDim`(#D14A22):次要高亮文本。 -- `info`(#FF8A5B):信息性值。 -- `success`(#2FBF71):成功状态。 -- `warn`(#FFB020):警告、回退、注意。 -- `error`(#E23D2D):错误、失败。 -- `muted`(#8B7F77):弱化、元数据。 +- `accent` (#FF5A2D):标题、标签、主要高亮。 +- `accentBright` (#FF7A3D):命令名称、强调。 +- `accentDim` (#D14A22):次级高亮文本。 +- `info` (#FF8A5B):信息性值。 +- `success` (#2FBF71):成功状态。 +- `warn` (#FFB020):警告、回退、注意事项。 +- `error` (#E23D2D):错误、失败。 +- `muted` (#8B7F77):弱化显示、元数据。 -调色板权威来源:`src/terminal/palette.ts`(又名"lobster seam")。 +调色板唯一来源:`src/terminal/palette.ts`(也称为 “lobster seam”)。 ## 命令树 @@ -101,9 +108,17 @@ openclaw [--dev] [--profile ] get set unset + completion doctor + dashboard + backup + create + verify security audit + secrets + reload + migrate reset uninstall update @@ -115,6 +130,7 @@ openclaw [--dev] [--profile ] remove login logout + directory skills list info @@ -152,6 +168,13 @@ openclaw [--dev] [--profile ] stop restart run + daemon + status + install + uninstall + start + stop + restart logs system event @@ -238,49 +261,59 @@ openclaw [--dev] [--profile ] pairing list approve + qr + clawbot + qr docs dns setup tui ``` -注意:插件可以添加额外的顶级命令(例如 `openclaw voicecall`)。 +注意:插件可以添加额外的顶层命令(例如 `openclaw voicecall`)。 ## 安全 -- `openclaw security audit` — 审计配置 + 本地状态中常见的安全隐患。 +- `openclaw security audit` — 审计配置 + 本地状态中常见的安全陷阱。 - `openclaw security audit --deep` — 尽力进行实时 Gateway 网关探测。 -- `openclaw security audit --fix` — 收紧安全默认值并 chmod 状态/配置。 +- `openclaw security audit --fix` — 收紧安全默认值并对状态 / 配置执行 chmod。 + +## 密钥 + +- `openclaw secrets reload` — 重新解析引用,并以原子方式替换运行时快照。 +- `openclaw secrets audit` — 扫描明文残留、未解析引用和优先级漂移。 +- `openclaw secrets configure` — 用于提供商设置 + SecretRef 映射 + 预检 / 应用的交互式助手。 +- `openclaw secrets apply --from ` — 应用先前生成的计划(支持 `--dry-run`)。 ## 插件 管理扩展及其配置: -- `openclaw plugins list` — 发现插件(使用 `--json` 获取机器可读输出)。 +- `openclaw plugins list` — 发现插件(机器输出请使用 `--json`)。 - `openclaw plugins info ` — 显示插件详情。 - `openclaw plugins install ` — 安装插件(或将插件路径添加到 `plugins.load.paths`)。 - `openclaw plugins enable ` / `disable ` — 切换 `plugins.entries..enabled`。 - `openclaw plugins doctor` — 报告插件加载错误。 -大多数插件更改需要重启 Gateway 网关。参见 [/plugin](/tools/plugin)。 +大多数插件更改都需要重启 gateway。参见 [/plugin](/tools/plugin)。 -## 记忆 +## 内存 -对 `MEMORY.md` + `memory/*.md` 进行向量搜索: +对 `MEMORY.md` + `memory/*.md` 执行向量搜索: -- `openclaw memory status` — 显示索引统计。 -- `openclaw memory index` — 重新索引记忆文件。 -- `openclaw memory search ""` — 对记忆进行语义搜索。 +- `openclaw memory status` — 显示索引统计信息。 +- `openclaw memory index` — 重新索引内存文件。 +- `openclaw memory search ""`(或 `--query ""`)— 对内存执行语义搜索。 ## 聊天斜杠命令 聊天消息支持 `/...` 命令(文本和原生)。参见 [/tools/slash-commands](/tools/slash-commands)。 -亮点: +重点: - `/status` 用于快速诊断。 - `/config` 用于持久化配置更改。 -- `/debug` 用于仅运行时的配置覆盖(内存中,不写入磁盘;需要 `commands.debug: true`)。 +- `/debug` 用于仅运行时的配置覆盖(内存中,不写磁盘;要求 `commands.debug: true`)。 ## 设置 + 新手引导 @@ -291,32 +324,35 @@ openclaw [--dev] [--profile ] 选项: - `--workspace `:智能体工作区路径(默认 `~/.openclaw/workspace`)。 -- `--wizard`:运行新手引导向导。 +- `--wizard`:运行设置向导。 - `--non-interactive`:无提示运行向导。 - `--mode `:向导模式。 - `--remote-url `:远程 Gateway 网关 URL。 -- `--remote-token `:远程 Gateway 网关令牌。 +- `--remote-token `:远程 Gateway 网关 token。 -当存在任何向导标志(`--non-interactive`、`--mode`、`--remote-url`、`--remote-token`)时,向导自动运行。 +只要存在任意向导标志(`--non-interactive`, `--mode`, `--remote-url`, `--remote-token`),就会自动运行向导。 ### `onboard` -交互式向导,用于设置 Gateway 网关、工作区和 Skills。 +用于设置 gateway、工作区和 Skills 的交互式向导。 选项: - `--workspace ` -- `--reset`(在向导之前重置配置 + 凭证 + 会话 + 工作区) +- `--reset`(在运行向导前重置配置 + 凭据 + 会话) +- `--reset-scope `(默认 `config+creds+sessions`;使用 `full` 还会删除工作区) - `--non-interactive` - `--mode ` -- `--flow `(manual 是 advanced 的别名) -- `--auth-choice ` -- `--token-provider `(非交互式;与 `--auth-choice token` 配合使用) -- `--token `(非交互式;与 `--auth-choice token` 配合使用) +- `--flow `(`manual` 是 `advanced` 的别名) +- `--auth-choice ` +- `--token-provider `(非交互式;与 `--auth-choice token` 一起使用) +- `--token `(非交互式;与 `--auth-choice token` 一起使用) - `--token-profile-id `(非交互式;默认:`:manual`) - `--token-expires-in `(非交互式;例如 `365d`、`12h`) +- `--secret-input-mode `(默认 `plaintext`;使用 `ref` 可存储提供商默认环境引用,而非明文密钥) - `--anthropic-api-key ` - `--openai-api-key ` +- `--mistral-api-key ` - `--openrouter-api-key ` - `--ai-gateway-api-key ` - `--moonshot-api-key ` @@ -325,10 +361,17 @@ openclaw [--dev] [--profile ] - `--zai-api-key ` - `--minimax-api-key ` - `--opencode-zen-api-key ` +- `--opencode-go-api-key ` +- `--custom-base-url `(非交互式;与 `--auth-choice custom-api-key` 或 `--auth-choice ollama` 一起使用) +- `--custom-model-id `(非交互式;与 `--auth-choice custom-api-key` 或 `--auth-choice ollama` 一起使用) +- `--custom-api-key `(非交互式;可选;与 `--auth-choice custom-api-key` 一起使用;省略时回退到 `CUSTOM_API_KEY`) +- `--custom-provider-id `(非交互式;可选自定义提供商 id) +- `--custom-compatibility `(非交互式;可选;默认 `openai`) - `--gateway-port ` - `--gateway-bind ` - `--gateway-auth ` - `--gateway-token ` +- `--gateway-token-ref-env `(非交互式;将 `gateway.auth.token` 存储为环境 SecretRef;要求该环境变量已设置;不能与 `--gateway-token` 一起使用) - `--gateway-password ` - `--remote-url ` - `--remote-token ` @@ -341,35 +384,39 @@ openclaw [--dev] [--profile ] - `--skip-skills` - `--skip-health` - `--skip-ui` -- `--node-manager `(推荐 pnpm;不建议将 bun 用于 Gateway 网关运行时) +- `--node-manager `(推荐 pnpm;不推荐将 bun 用作 Gateway 网关运行时) - `--json` ### `configure` -交互式配置向导(模型、渠道、Skills、Gateway 网关)。 +交互式配置向导(模型、渠道、Skills、gateway)。 ### `config` -非交互式配置辅助工具(get/set/unset)。不带子命令运行 `openclaw config` 会启动向导。 +非交互式配置助手(get/set/unset/file/validate)。直接运行 `openclaw config` 而不带 +子命令会启动向导。 子命令: -- `config get `:打印配置值(点/括号路径)。 -- `config set `:设置值(JSON5 或原始字符串)。 -- `config unset `:删除值。 +- `config get `:打印一个配置值(点 / 方括号路径)。 +- `config set `:设置一个值(JSON5 或原始字符串)。 +- `config unset `:移除一个值。 +- `config file`:打印当前活动配置文件路径。 +- `config validate`:根据 schema 验证当前配置,而不启动 gateway。 +- `config validate --json`:输出机器可读的 JSON。 ### `doctor` -健康检查 + 快速修复(配置 + Gateway 网关 + 旧版服务)。 +健康检查 + 快速修复(配置 + gateway + 旧版服务)。 选项: -- `--no-workspace-suggestions`:禁用工作区记忆提示。 -- `--yes`:无提示接受默认值(无头模式)。 +- `--no-workspace-suggestions`:禁用工作区内存提示。 +- `--yes`:接受默认值而不提示(无头)。 - `--non-interactive`:跳过提示;仅应用安全迁移。 -- `--deep`:扫描系统服务以查找额外的 Gateway 网关安装。 +- `--deep`:扫描系统服务以查找额外的 gateway 安装。 -## 渠道辅助工具 +## 渠道助手 ### `channels` @@ -378,19 +425,21 @@ openclaw [--dev] [--profile ] 子命令: - `channels list`:显示已配置的渠道和认证配置文件。 -- `channels status`:检查 Gateway 网关可达性和渠道健康状况(`--probe` 运行额外检查;使用 `openclaw health` 或 `openclaw status --deep` 进行 Gateway 网关健康探测)。 -- 提示:`channels status` 在检测到常见配置错误时会打印带有建议修复的警告(然后指向 `openclaw doctor`)。 -- `channels logs`:显示 Gateway 网关日志文件中最近的渠道日志。 -- `channels add`:不传标志时使用向导式设置;标志切换到非交互模式。 -- `channels remove`:默认禁用;传 `--delete` 可无提示删除配置条目。 -- `channels login`:交互式渠道登录(仅限 WhatsApp Web)。 -- `channels logout`:登出渠道会话(如支持)。 +- `channels status`:检查 gateway 可达性和渠道健康状态(`--probe` 会运行额外检查;gateway 健康探测请使用 `openclaw health` 或 `openclaw status --deep`)。 +- 提示:如果能够检测到常见配置错误,`channels status` 会打印带建议修复方式的警告(随后指向 `openclaw doctor`)。 +- `channels logs`:显示 gateway 日志文件中的最近渠道日志。 +- `channels add`:未传入任何标志时为向导式设置;传入标志后切换为非交互模式。 + - 当向仍使用单账户顶层配置的渠道添加非默认账户时,OpenClaw 会先将账户作用域值移动到 `channels..accounts.default`,再写入新账户。 + - 非交互式 `channels add` 不会自动创建 / 升级绑定;仅渠道绑定会继续匹配默认账户。 +- `channels remove`:默认执行禁用;传入 `--delete` 可在无提示下删除配置项。 +- `channels login`:交互式渠道登录(仅 WhatsApp Web)。 +- `channels logout`:登出某个渠道会话(如支持)。 通用选项: - `--channel `:`whatsapp|telegram|discord|googlechat|slack|mattermost|signal|imessage|msteams` - `--account `:渠道账户 id(默认 `default`) -- `--name