Compare commits

..

4 Commits

988 changed files with 6833 additions and 84151 deletions

6
.github/labeler.yml vendored
View File

@@ -9,12 +9,6 @@
- "src/discord/**"
- "extensions/discord/**"
- "docs/channels/discord.md"
"channel: feishu":
- changed-files:
- any-glob-to-any-file:
- "src/feishu/**"
- "extensions/feishu/**"
- "docs/channels/feishu.md"
"channel: googlechat":
- changed-files:
- any-glob-to-any-file:

View File

@@ -89,6 +89,9 @@ jobs:
- runtime: bun
task: test
command: pnpm canvas:a2ui:bundle && bunx vitest run
- runtime: bun
task: build
command: bunx tsc -p tsconfig.json --noEmit false
steps:
- name: Checkout
uses: actions/checkout@v4

View File

@@ -37,22 +37,6 @@ jobs:
node scripts/extract-tool-groups.mjs
node scripts/check-tool-group-alias.mjs
# Drift is about extracted artifacts only; compute it before model checking
# to avoid any incidental file touches affecting the result.
- name: Compute drift (generated/*)
id: drift
run: |
set -euo pipefail
cd clawdbot-formal-models
if git diff --quiet -- generated; then
echo "drift=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "drift=true" >> "$GITHUB_OUTPUT"
git diff -- generated > "${GITHUB_WORKSPACE}/formal-models-drift.diff"
- name: Model check (green suite)
run: |
set -euo pipefail
@@ -67,10 +51,6 @@ jobs:
routing-isolation routing-precedence routing-identitylinks routing-identity-transitive routing-identity-symmetry routing-identity-channel-override \
routing-thread-parent discord-pluralkit \
ingress-retry session-key-stability session-explosion-bound config-normalization \
queue-drain delivery-route-stability delivery-pipeline retry-termination retry-eventual-success \
no-cross-stream multi-event-eventual-emission \
dedupe-collision-fallback crash-restart-dedupe two-worker-dedupe openclaw-session-key-conformance \
routing-thread-parent-channel-override routing-trirule gateway-auth-proxy-header-spoof \
group-alias-check
- name: Model check (negative suite, expected violations)
@@ -89,11 +69,21 @@ jobs:
ingress-gating-negative ingress-idempotency-negative ingress-dedupe-fallback-negative ingress-trace-negative ingress-trace2-negative \
routing-isolation-negative routing-precedence-negative routing-identitylinks-negative routing-identity-transitive-negative routing-identity-symmetry-negative routing-identity-channel-override-negative \
routing-thread-parent-negative discord-pluralkit-negative \
ingress-retry-negative session-key-stability-negative config-normalization-negative \
queue-drain delivery-route-stability-negative delivery-pipeline-negative retry-termination-negative retry-eventual-success-negative \
no-cross-stream-negative multi-event-eventual-emission-negative \
dedupe-collision-fallback-negative crash-restart-dedupe-negative two-worker-dedupe-negative openclaw-session-key-conformance-negative \
routing-thread-parent-channel-override-negative routing-trirule-negative gateway-auth-proxy-header-spoof-negative
ingress-retry-negative session-key-stability-negative config-normalization-negative
- name: Compute drift
id: drift
run: |
set -euo pipefail
cd clawdbot-formal-models
if git diff --quiet; then
echo "drift=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "drift=true" >> "$GITHUB_OUTPUT"
git diff > "${GITHUB_WORKSPACE}/formal-models-drift.diff"
- name: Upload drift diff artifact
if: steps.drift.outputs.drift == 'true'

View File

@@ -3,8 +3,6 @@ name: Labeler
on:
pull_request_target:
types: [opened, synchronize, reopened]
issues:
types: [opened]
permissions: {}
@@ -25,56 +23,3 @@ jobs:
configuration-path: .github/labeler.yml
repo-token: ${{ steps.app-token.outputs.token }}
sync-labels: true
- name: Apply maintainer label for org members
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const association = context.payload.pull_request?.author_association;
if (!association) {
return;
}
if (![
"MEMBER",
"OWNER",
].includes(association)) {
return;
}
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.pull_request.number,
labels: ["maintainer"],
});
label-issues:
permissions:
issues: write
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
id: app-token
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Apply maintainer label for org members
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const association = context.payload.issue?.author_association;
if (!association) {
return;
}
if (![
"MEMBER",
"OWNER",
].includes(association)) {
return;
}
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.issue.number,
labels: ["maintainer"],
});

View File

@@ -14,8 +14,9 @@
"oxc/no-accumulating-spread": "off",
"oxc/no-async-endpoint-handlers": "off",
"oxc/no-map-spread": "off",
"typescript/no-explicit-any": "error",
"typescript/no-extraneous-class": "off",
"typescript/no-redundant-type-constituents": "off",
"typescript/no-unnecessary-template-expression": "off",
"typescript/no-unsafe-type-assertion": "off",
"unicorn/consistent-function-scoping": "off",
"unicorn/require-post-message-target-origin": "off"
@@ -30,6 +31,7 @@
"skills/",
"src/canvas-host/a2ui/a2ui.bundle.js",
"Swabble/",
"vendor/"
"vendor/",
"ui/"
]
}

View File

@@ -28,14 +28,6 @@
- README (GitHub): keep absolute docs URLs (`https://docs.openclaw.ai/...`) so links work on GitHub.
- Docs content must be generic: no personal device names/hostnames/paths; use placeholders like `user@gateway-host` and “gateway host”.
## Docs i18n (zh-CN)
- `docs/zh-CN/**` is generated; do not edit unless the user explicitly asks.
- Pipeline: update English docs → adjust glossary (`docs/.i18n/glossary.zh-CN.json`) → run `scripts/docs-i18n` → apply targeted fixes only if instructed.
- Translation memory: `docs/.i18n/zh-CN.tm.jsonl` (generated).
- See `docs/.i18n/README.md`.
- The pipeline can be slow/inefficient; if its dragging, ping @jospalmbier on Discord instead of hacking around it.
## exe.dev VM ops (general)
- Access: stable path is `ssh exe.dev` then `ssh vm-name` (assume SSH key already set).

View File

@@ -2,213 +2,33 @@
Docs: https://docs.openclaw.ai
## 2026.2.3
### Changes
- Onboarding: add Moonshot (.cn) auth choice and keep the China base URL when preserving defaults. (#7180) Thanks @waynelwz.
- Docs: clarify tmux send-keys for TUI by splitting text and Enter. (#7737) Thanks @Wangnov.
- Cron: add announce delivery mode for isolated jobs (CLI + Control UI) and delivery mode config.
- Cron: default isolated jobs to announce delivery; accept ISO 8601 `schedule.at` in tool inputs.
- Cron: hard-migrate isolated jobs to announce/none delivery; drop legacy post-to-main/payload delivery fields and `atMs` inputs.
- Cron: delete one-shot jobs after success by default; add `--keep-after-run` for CLI.
- Cron: suppress messaging tools during announce delivery so summaries post consistently.
- Cron: avoid duplicate deliveries when isolated runs send messages directly.
### Fixes
- Telegram: honor session model overrides in inline model selection. (#8193) Thanks @gildo.
- iMessage: skip echo replies using recent outbound IDs before falling back to text matching. (#8680) Thanks @Iranb.
- Web UI: resolve header logo path when `gateway.controlUi.basePath` is set. (#7178) Thanks @Yeom-JinHo.
- Web UI: apply button styling to the new-messages indicator.
- Security: keep untrusted channel metadata out of system prompts (Slack/Discord). Thanks @KonstantinMirin.
- Voice call: harden webhook verification with host allowlists/proxy trust and keep ngrok loopback bypass.
- Cron: accept epoch timestamps and 0ms durations in CLI `--at` parsing.
- Cron: reload store data when the store file is recreated or mtime changes.
- Cron: deliver announce runs directly, honor delivery mode, and respect wakeMode for summaries. (#8540) Thanks @tyler6204.
- Telegram: include forward_from_chat metadata in forwarded messages and harden cron delivery target checks. (#8392) Thanks @Glucksberg.
## 2026.2.2-3
### Fixes
- Update: ship legacy daemon-cli shim for pre-tsdown update imports (fixes daemon restart after npm update).
## 2026.2.2-2
### Changes
- Docs: promote BlueBubbles as the recommended iMessage integration; mark imsg channel as legacy. (#8415) Thanks @tyler6204.
### Fixes
- CLI status: resolve build-info from bundled dist output (fixes "unknown" commit in npm builds).
## 2026.2.2-1
### Fixes
- CLI status: fall back to build-info for version detection (fixes "unknown" in beta builds). Thanks @gumadeira.
## 2026.2.2
### Changes
- Feishu: add Feishu/Lark plugin support + docs. (#7313) Thanks @jiulingyun (openclaw-cn).
- Web UI: add Agents dashboard for managing agent files, tools, skills, models, channels, and cron jobs.
- Subagents: discourage direct messaging tool use unless a specific external recipient is requested.
- Memory: implement the opt-in QMD backend for workspace memory. (#3160) Thanks @vignesh07.
- Security: add healthcheck skill and bootstrap audit guidance. (#7641) Thanks @Takhoffman.
- Config: allow setting a default subagent thinking level via `agents.defaults.subagents.thinking` (and per-agent `agents.list[].subagents.thinking`). (#7372) Thanks @tyler6204.
- Docs: zh-CN translations seed + polish, pipeline guidance, nav/landing updates, and typo fixes. (#8202, #6995, #6619, #7242, #7303, #7415) Thanks @AaronWander, @taiyi747, @Explorer1092, @rendaoyuan, @joshp123, @lailoo.
- Docs: add zh-CN i18n guardrails to avoid editing generated translations. (#8416) Thanks @joshp123.
### Fixes
- Docs: finish renaming the QMD memory docs to reference the OpenClaw state dir.
- Onboarding: keep TUI flow exclusive (skip completion prompt + background Web UI seed).
- Onboarding: drop completion prompt now handled by install/update.
- TUI: block onboarding output while TUI is active and restore terminal state on exit.
- CLI: cache shell completion scripts in state dir and source cached files in profiles.
- Zsh completion: escape option descriptions to avoid invalid option errors.
- Agents: repair malformed tool calls and session transcripts. (#7473) Thanks @justinhuangcode.
- fix(agents): validate AbortSignal instances before calling AbortSignal.any() (#7277) (thanks @Elarwei001)
- fix(webchat): respect user scroll position during streaming and refresh (#7226) (thanks @marcomarandiz)
- Telegram: recover from grammY long-poll timed out errors. (#7466) Thanks @macmimi23.
- Media understanding: skip binary media from file text extraction. (#7475) Thanks @AlexZhangji.
- Security: enforce access-group gating for Slack slash commands when channel type lookup fails.
- Security: require validated shared-secret auth before skipping device identity on gateway connect.
- Security: guard skill installer downloads with SSRF checks (block private/localhost URLs).
- Security: harden Windows exec allowlist; block cmd.exe bypass via single &. Thanks @simecek.
- fix(voice-call): harden inbound allowlist; reject anonymous callers; require Telnyx publicKey for allowlist; token-gate Twilio media streams; cap webhook body size (thanks @simecek)
- Media understanding: apply SSRF guardrails to provider fetches; allow private baseUrl overrides explicitly.
- fix(webchat): respect user scroll position during streaming and refresh (#7226) (thanks @marcomarandiz)
- Telegram: recover from grammY long-poll timed out errors. (#7466) Thanks @macmimi23.
- Agents: repair malformed tool calls and session transcripts. (#7473) Thanks @justinhuangcode.
- fix(agents): validate AbortSignal instances before calling AbortSignal.any() (#7277) (thanks @Elarwei001)
- Media understanding: skip binary media from file text extraction. (#7475) Thanks @AlexZhangji.
- Onboarding: keep TUI flow exclusive (skip completion prompt + background Web UI seed); completion prompt now handled by install/update.
- TUI: block onboarding output while TUI is active and restore terminal state on exit.
- CLI/Zsh completion: cache scripts in state dir and escape option descriptions to avoid invalid option errors.
- fix(ui): resolve Control UI asset path correctly.
- fix(ui): refresh agent files after external edits.
- Docs: finish renaming the QMD memory docs to reference the OpenClaw state dir.
- Tests: stub SSRF DNS pinning in web auto-reply + Gemini video coverage. (#6619) Thanks @joshp123.
## 2026.2.1
### Changes
- Docs: onboarding/install/i18n/exec-approvals/Control UI/exe.dev/cacheRetention updates + misc nav/typos. (#3050, #3461, #4064, #4675, #4729, #4763, #5003, #5402, #5446, #5474, #5663, #5689, #5694, #5967, #6270, #6300, #6311, #6416, #6487, #6550, #6789)
- Telegram: use shared pairing store. (#6127) Thanks @obviyus.
- Agents: add OpenRouter app attribution headers. Thanks @alexanderatallah.
- Agents: add system prompt safety guardrails. (#5445) Thanks @joshp123.
- Agents: update pi-ai to 0.50.9 and rename cacheControlTtl -> cacheRetention (with back-compat mapping).
- Agents: extend CreateAgentSessionOptions with systemPrompt/skills/contextFiles.
- Agents: add tool policy conformance snapshot (no runtime behavior change). (#6011)
- Auth: update MiniMax OAuth hint + portal auth note copy.
- Discord: inherit thread parent bindings for routing. (#3892) Thanks @aerolalit.
- Gateway: inject timestamps into agent and chat.send messages. (#3705) Thanks @conroywhitney, @CashWilliams.
- Gateway: require TLS 1.3 minimum for TLS listeners. (#5970) Thanks @loganaden.
- Web UI: refine chat layout + extend session active duration.
- CI: add formal conformance + alias consistency checks. (#5723, #5807)
### Fixes
- Security: guard remote media fetches with SSRF protections (block private/localhost, DNS pinning).
- Updates: clean stale global install rename dirs and extend gateway update timeouts to avoid npm ENOTEMPTY failures.
- Plugins: validate plugin/hook install paths and reject traversal-like names.
- Telegram: add download timeouts for file fetches. (#6914) Thanks @hclsys.
- Telegram: enforce thread specs for DM vs forum sends. (#6833) Thanks @obviyus.
- Streaming: flush block streaming on paragraph boundaries for newline chunking. (#7014)
- Streaming: stabilize partial streaming filters.
- Auto-reply: avoid referencing workspace files in /new greeting prompt. (#5706) Thanks @bravostation.
- Tools: align tool execute adapters/signatures (legacy + parameter order + arg normalization).
- Tools: treat "\*" tool allowlist entries as valid to avoid spurious unknown-entry warnings.
- Skills: update session-logs paths from .clawdbot to .openclaw. (#4502)
- Slack: harden media fetch limits and Slack file URL validation. (#6639) Thanks @davidiach.
- Lint: satisfy curly rule after import sorting. (#6310)
- Process: resolve Windows `spawn()` failures for npm-family CLIs by appending `.cmd` when needed. (#5815) Thanks @thejhinvirtuoso.
- Discord: resolve PluralKit proxied senders for allowlists and labels. (#5838) Thanks @thewilloftheshadow.
- Tlon: add timeout to SSE client fetch calls (CWE-400). (#5926)
- Memory search: L2-normalize local embedding vectors to fix semantic search. (#5332)
- Agents: align embedded runner + typings with pi-coding-agent API updates (pi 0.51.0).
- Agents: ensure OpenRouter attribution headers apply in the embedded runner.
- Agents: cap context window resolution for compaction safeguard. (#6187) Thanks @iamEvanYT.
- System prompt: resolve overrides and hint using session_status for current date/time. (#1897, #1928, #2108, #3677)
- Agents: fix Pi prompt template argument syntax. (#6543)
- Subagents: fix announce failover race (always emit lifecycle end; timeout=0 means no-timeout). (#6621)
- Teams: gate media auth retries.
- Telegram: restore draft streaming partials. (#5543) Thanks @obviyus.
- Onboarding: friendlier Windows onboarding message. (#6242) Thanks @shanselman.
- TUI: prevent crash when searching with digits in the model selector.
- Agents: wire before_tool_call plugin hook into tool execution. (#6570, #6660) Thanks @ryancnelson.
- Browser: secure Chrome extension relay CDP sessions.
- Docker: use container port for gateway command instead of host port. (#5110) Thanks @mise42.
- Docker: start gateway CMD by default for container deployments. (#6635) Thanks @kaizen403.
- fix(lobster): block arbitrary exec via lobsterPath/cwd injection (GHSA-4mhr-g7xj-cg8j). (#5335) Thanks @vignesh07.
- Security: sanitize WhatsApp accountId to prevent path traversal. (#4610)
- Security: restrict MEDIA path extraction to prevent LFI. (#4930)
- Security: validate message-tool filePath/path against sandbox root. (#6398)
- Security: block LD*/DYLD* env overrides for host exec. (#4896) Thanks @HassanFleyah.
- Security: harden web tool content wrapping + file parsing safeguards. (#4058) Thanks @VACInc.
- Security: enforce Twitch `allowFrom` allowlist gating (deny non-allowlisted senders). Thanks @MegaManSec.
## 2026.1.31
### Changes
- Docs: onboarding/install/i18n/exec-approvals/Control UI/exe.dev/cacheRetention updates + misc nav/typos. (#3050, #3461, #4064, #4675, #4729, #4763, #5003, #5402, #5446, #5474, #5663, #5689, #5694, #5967, #6270, #6300, #6311, #6416, #6487, #6550, #6789)
- Docs: onboarding/install/i18n/exec-approvals/Control UI/exe.dev/cacheRetention updates + misc nav/typos.
- Telegram: use shared pairing store. (#6127) Thanks @obviyus.
- Agents: add OpenRouter app attribution headers. Thanks @alexanderatallah.
- Agents: add OpenRouter app attribution headers. (#5050) Thanks @alexanderatallah.
- Agents: add system prompt safety guardrails. (#5445) Thanks @joshp123.
- Agents: update pi-ai to 0.50.9 and rename cacheControlTtl -> cacheRetention (with back-compat mapping).
- Agents: extend CreateAgentSessionOptions with systemPrompt/skills/contextFiles.
- Agents: add tool policy conformance snapshot (no runtime behavior change). (#6011)
- Auth: update MiniMax OAuth hint + portal auth note copy.
- Discord: inherit thread parent bindings for routing. (#3892) Thanks @aerolalit.
- Gateway: inject timestamps into agent and chat.send messages. (#3705) Thanks @conroywhitney, @CashWilliams.
- Gateway: require TLS 1.3 minimum for TLS listeners. (#5970) Thanks @loganaden.
- Web UI: refine chat layout + extend session active duration.
- CI: add formal conformance + alias consistency checks. (#5723, #5807)
### Fixes
- Security: guard remote media fetches with SSRF protections (block private/localhost, DNS pinning).
- Updates: clean stale global install rename dirs and extend gateway update timeouts to avoid npm ENOTEMPTY failures.
- Plugins: validate plugin/hook install paths and reject traversal-like names.
- Telegram: add download timeouts for file fetches. (#6914) Thanks @hclsys.
- Telegram: enforce thread specs for DM vs forum sends. (#6833) Thanks @obviyus.
- Streaming: flush block streaming on paragraph boundaries for newline chunking. (#7014)
- Streaming: stabilize partial streaming filters.
- Auto-reply: avoid referencing workspace files in /new greeting prompt. (#5706) Thanks @bravostation.
- Tools: align tool execute adapters/signatures (legacy + parameter order + arg normalization).
- Tools: treat `"*"` tool allowlist entries as valid to avoid spurious unknown-entry warnings.
- Skills: update session-logs paths from .clawdbot to .openclaw. (#4502)
- Slack: harden media fetch limits and Slack file URL validation. (#6639) Thanks @davidiach.
- Lint: satisfy curly rule after import sorting. (#6310)
- Process: resolve Windows `spawn()` failures for npm-family CLIs by appending `.cmd` when needed. (#5815) Thanks @thejhinvirtuoso.
- Discord: resolve PluralKit proxied senders for allowlists and labels. (#5838) Thanks @thewilloftheshadow.
- Tlon: add timeout to SSE client fetch calls (CWE-400). (#5926)
- Memory search: L2-normalize local embedding vectors to fix semantic search. (#5332)
- Agents: align embedded runner + typings with pi-coding-agent API updates (pi 0.51.0).
- Agents: ensure OpenRouter attribution headers apply in the embedded runner.
- Agents: cap context window resolution for compaction safeguard. (#6187) Thanks @iamEvanYT.
- System prompt: resolve overrides and hint using session_status for current date/time. (#1897, #1928, #2108, #3677)
- Agents: fix Pi prompt template argument syntax. (#6543)
- Subagents: fix announce failover race (always emit lifecycle end; timeout=0 means no-timeout). (#6621)
- Teams: gate media auth retries.
- System prompt: hint using session_status for current date/time. (#1897, #1928, #2108)
- Telegram: restore draft streaming partials. (#5543) Thanks @obviyus.
- Onboarding: friendlier Windows onboarding message. (#6242) Thanks @shanselman.
- TUI: prevent crash when searching with digits in the model selector.
- Agents: wire before_tool_call plugin hook into tool execution. (#6570, #6660) Thanks @ryancnelson.
- Agents: wire before_tool_call plugin hook into tool execution. (#6570) Thanks @ryancnelson.
- Browser: secure Chrome extension relay CDP sessions.
- Docker: use container port for gateway command instead of host port. (#5110) Thanks @mise42.
- Docker: start gateway CMD by default for container deployments. (#6635) Thanks @kaizen403.
- fix(lobster): block arbitrary exec via lobsterPath/cwd injection (GHSA-4mhr-g7xj-cg8j). (#5335) Thanks @vignesh07.
- Security: sanitize WhatsApp accountId to prevent path traversal. (#4610)
- Security: restrict MEDIA path extraction to prevent LFI. (#4930)
- Security: validate message-tool filePath/path against sandbox root. (#6398)
- Security: block LD*/DYLD* env overrides for host exec. (#4896) Thanks @HassanFleyah.
- Security: harden web tool content wrapping + file parsing safeguards. (#4058) Thanks @VACInc.
- Security: enforce Twitch `allowFrom` allowlist gating (deny non-allowlisted senders). Thanks @MegaManSec.
@@ -223,11 +43,11 @@ Docs: https://docs.openclaw.ai
- Auth: switch Kimi Coding to built-in provider; normalize OAuth profile email.
- Auth: add MiniMax OAuth plugin + onboarding option. (#4521) Thanks @Maosghoul.
- Agents: update pi SDK/API usage and dependencies.
- Gateway: inject timestamps into agent and chat.send messages. (#3705) Thanks @conroywhitney, @CashWilliams.
- Web UI: refresh sessions after chat commands and improve session display names.
- Build: move TypeScript builds to `tsdown` + `tsgo` (faster builds, CI typechecks), update tsconfig target, and clean up lint rules.
- Build: align npm tar override and bin metadata so the `openclaw` CLI entrypoint is preserved in npm publishes.
- Docs: add pi/pi-dev docs and update OpenClaw branding + install links.
- Docker E2E: stabilize gateway readiness, plugin installs/manifests, and cleanup/doctor switch entrypoint checks.
### Fixes

View File

@@ -35,21 +35,6 @@ Welcome to the lobster tank! 🦞
- Keep PRs focused (one thing per PR)
- Describe what & why
## Control UI Decorators
The Control UI uses Lit with **legacy** decorators (current Rollup parsing does not support
`accessor` fields required for standard decorators). When adding reactive fields, keep the
legacy style:
```ts
@state() foo = "bar";
@property({ type: Number }) count = 0;
```
The root `tsconfig.json` is configured for legacy decorators (`experimentalDecorators: true`)
with `useDefineForClassFields: false`. Avoid flipping these unless you are also updating the UI
build tooling to support standard decorators.
## AI/Vibe-Coded PRs Welcome! 🤖
Built with Codex, Claude, or other AI tools? **Awesome - just mark it!**

View File

@@ -31,18 +31,9 @@ RUN pnpm ui:build
ENV NODE_ENV=production
# Allow non-root user to write temp files during runtime/tests.
RUN chown -R node:node /app
# Security hardening: Run as non-root user
# The node:22-bookworm image includes a 'node' user (uid 1000)
# This reduces the attack surface by preventing container escape via root privileges
USER node
# Start gateway server with default config.
# Binds to loopback (127.0.0.1) by default for security.
#
# For container platforms requiring external health checks:
# 1. Set OPENCLAW_GATEWAY_TOKEN or OPENCLAW_GATEWAY_PASSWORD env var
# 2. Override CMD: ["node","dist/index.js","gateway","--allow-unconfigured","--bind","lan"]
CMD ["node", "dist/index.js", "gateway", "--allow-unconfigured"]
CMD ["node", "dist/index.js"]

View File

@@ -120,7 +120,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
## Highlights
- **[Local-first Gateway](https://docs.openclaw.ai/gateway)** — single control plane for sessions, channels, tools, and events.
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles (iMessage), iMessage (legacy), Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
- **[Multi-agent routing](https://docs.openclaw.ai/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs.
- **[Live Canvas](https://docs.openclaw.ai/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).
@@ -144,7 +144,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
### Channels
- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (iMessage, recommended), [iMessage](https://docs.openclaw.ai/channels/imessage) (legacy imsg), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams) (extension), [Matrix](https://docs.openclaw.ai/channels/matrix) (extension), [Zalo](https://docs.openclaw.ai/channels/zalo) (extension), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser) (extension), [WebChat](https://docs.openclaw.ai/web/webchat).
- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [iMessage](https://docs.openclaw.ai/channels/imessage) (imsg), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams) (extension), [Matrix](https://docs.openclaw.ai/channels/matrix) (extension), [Zalo](https://docs.openclaw.ai/channels/zalo) (extension), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser) (extension), [WebChat](https://docs.openclaw.ai/web/webchat).
- [Group routing](https://docs.openclaw.ai/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.openclaw.ai/channels).
### Apps + nodes
@@ -375,15 +375,9 @@ Details: [Security guide](https://docs.openclaw.ai/gateway/security) · [Docker
- Requires `signal-cli` and a `channels.signal` config section.
### [BlueBubbles (iMessage)](https://docs.openclaw.ai/channels/bluebubbles)
### [iMessage](https://docs.openclaw.ai/channels/imessage)
- **Recommended** iMessage integration.
- Configure `channels.bluebubbles.serverUrl` + `channels.bluebubbles.password` and a webhook (`channels.bluebubbles.webhookPath`).
- The BlueBubbles server runs on macOS; the Gateway can run on macOS or elsewhere.
### [iMessage (legacy)](https://docs.openclaw.ai/channels/imessage)
- Legacy macOS-only integration via `imsg` (Messages must be signed in).
- macOS only; Messages must be signed in.
- If `channels.imessage.groups` is set, it becomes a group allowlist; include `"*"` to allow all.
### [Microsoft Teams](https://docs.openclaw.ai/channels/msteams)

View File

@@ -2,116 +2,6 @@
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<title>OpenClaw</title>
<item>
<title>2026.2.2</title>
<pubDate>Tue, 03 Feb 2026 17:04:17 -0800</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>8809</sparkle:version>
<sparkle:shortVersionString>2026.2.2</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.2.2</h2>
<h3>Changes</h3>
<ul>
<li>Feishu: add Feishu/Lark plugin support + docs. (#7313) Thanks @jiulingyun (openclaw-cn).</li>
<li>Web UI: add Agents dashboard for managing agent files, tools, skills, models, channels, and cron jobs.</li>
<li>Memory: implement the opt-in QMD backend for workspace memory. (#3160) Thanks @vignesh07.</li>
<li>Security: add healthcheck skill and bootstrap audit guidance. (#7641) Thanks @Takhoffman.</li>
<li>Config: allow setting a default subagent thinking level via <code>agents.defaults.subagents.thinking</code> (and per-agent <code>agents.list[].subagents.thinking</code>). (#7372) Thanks @tyler6204.</li>
<li>Docs: zh-CN translations seed + polish, pipeline guidance, nav/landing updates, and typo fixes. (#8202, #6995, #6619, #7242, #7303, #7415) Thanks @AaronWander, @taiyi747, @Explorer1092, @rendaoyuan, @joshp123, @lailoo.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Security: require operator.approvals for gateway /approve commands. (#1) Thanks @mitsuhiko, @yueyueL.</li>
<li>Security: Matrix allowlists now require full MXIDs; ambiguous name resolution no longer grants access. Thanks @MegaManSec.</li>
<li>Security: enforce access-group gating for Slack slash commands when channel type lookup fails.</li>
<li>Security: require validated shared-secret auth before skipping device identity on gateway connect.</li>
<li>Security: guard skill installer downloads with SSRF checks (block private/localhost URLs).</li>
<li>Security: harden Windows exec allowlist; block cmd.exe bypass via single &. Thanks @simecek.</li>
<li>fix(voice-call): harden inbound allowlist; reject anonymous callers; require Telnyx publicKey for allowlist; token-gate Twilio media streams; cap webhook body size (thanks @simecek)</li>
<li>Media understanding: apply SSRF guardrails to provider fetches; allow private baseUrl overrides explicitly.</li>
<li>fix(webchat): respect user scroll position during streaming and refresh (#7226) (thanks @marcomarandiz)</li>
<li>Telegram: recover from grammY long-poll timed out errors. (#7466) Thanks @macmimi23.</li>
<li>Agents: repair malformed tool calls and session transcripts. (#7473) Thanks @justinhuangcode.</li>
<li>fix(agents): validate AbortSignal instances before calling AbortSignal.any() (#7277) (thanks @Elarwei001)</li>
<li>Media understanding: skip binary media from file text extraction. (#7475) Thanks @AlexZhangji.</li>
<li>Onboarding: keep TUI flow exclusive (skip completion prompt + background Web UI seed); completion prompt now handled by install/update.</li>
<li>TUI: block onboarding output while TUI is active and restore terminal state on exit.</li>
<li>CLI/Zsh completion: cache scripts in state dir and escape option descriptions to avoid invalid option errors.</li>
<li>fix(ui): resolve Control UI asset path correctly.</li>
<li>fix(ui): refresh agent files after external edits.</li>
<li>Docs: finish renaming the QMD memory docs to reference the OpenClaw state dir.</li>
<li>Tests: stub SSRF DNS pinning in web auto-reply + Gemini video coverage. (#6619) Thanks @joshp123.</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.2.2/OpenClaw-2026.2.2.zip" length="22519052" type="application/octet-stream" sparkle:edSignature="a6viD+aS5EfY/RkPIPMfoQQNkJCk6QTdV5WobXFxyYwURskUm8/nXTHVXsCh1c5+0WKUnmlDIyf0i+6IWiavAA=="/>
</item>
<item>
<title>2026.2.1</title>
<pubDate>Mon, 02 Feb 2026 03:53:03 -0800</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>8650</sparkle:version>
<sparkle:shortVersionString>2026.2.1</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.2.1</h2>
<h3>Changes</h3>
<ul>
<li>Docs: onboarding/install/i18n/exec-approvals/Control UI/exe.dev/cacheRetention updates + misc nav/typos. (#3050, #3461, #4064, #4675, #4729, #4763, #5003, #5402, #5446, #5474, #5663, #5689, #5694, #5967, #6270, #6300, #6311, #6416, #6487, #6550, #6789)</li>
<li>Telegram: use shared pairing store. (#6127) Thanks @obviyus.</li>
<li>Agents: add OpenRouter app attribution headers. Thanks @alexanderatallah.</li>
<li>Agents: add system prompt safety guardrails. (#5445) Thanks @joshp123.</li>
<li>Agents: update pi-ai to 0.50.9 and rename cacheControlTtl -> cacheRetention (with back-compat mapping).</li>
<li>Agents: extend CreateAgentSessionOptions with systemPrompt/skills/contextFiles.</li>
<li>Agents: add tool policy conformance snapshot (no runtime behavior change). (#6011)</li>
<li>Auth: update MiniMax OAuth hint + portal auth note copy.</li>
<li>Discord: inherit thread parent bindings for routing. (#3892) Thanks @aerolalit.</li>
<li>Gateway: inject timestamps into agent and chat.send messages. (#3705) Thanks @conroywhitney, @CashWilliams.</li>
<li>Gateway: require TLS 1.3 minimum for TLS listeners. (#5970) Thanks @loganaden.</li>
<li>Web UI: refine chat layout + extend session active duration.</li>
<li>CI: add formal conformance + alias consistency checks. (#5723, #5807)</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Plugins: validate plugin/hook install paths and reject traversal-like names.</li>
<li>Telegram: add download timeouts for file fetches. (#6914) Thanks @hclsys.</li>
<li>Telegram: enforce thread specs for DM vs forum sends. (#6833) Thanks @obviyus.</li>
<li>Streaming: flush block streaming on paragraph boundaries for newline chunking. (#7014)</li>
<li>Streaming: stabilize partial streaming filters.</li>
<li>Auto-reply: avoid referencing workspace files in /new greeting prompt. (#5706) Thanks @bravostation.</li>
<li>Tools: align tool execute adapters/signatures (legacy + parameter order + arg normalization).</li>
<li>Tools: treat <code>"*"</code> tool allowlist entries as valid to avoid spurious unknown-entry warnings.</li>
<li>Skills: update session-logs paths from .clawdbot to .openclaw. (#4502)</li>
<li>Slack: harden media fetch limits and Slack file URL validation. (#6639) Thanks @davidiach.</li>
<li>Lint: satisfy curly rule after import sorting. (#6310)</li>
<li>Process: resolve Windows <code>spawn()</code> failures for npm-family CLIs by appending <code>.cmd</code> when needed. (#5815) Thanks @thejhinvirtuoso.</li>
<li>Discord: resolve PluralKit proxied senders for allowlists and labels. (#5838) Thanks @thewilloftheshadow.</li>
<li>Tlon: add timeout to SSE client fetch calls (CWE-400). (#5926)</li>
<li>Memory search: L2-normalize local embedding vectors to fix semantic search. (#5332)</li>
<li>Agents: align embedded runner + typings with pi-coding-agent API updates (pi 0.51.0).</li>
<li>Agents: ensure OpenRouter attribution headers apply in the embedded runner.</li>
<li>Agents: cap context window resolution for compaction safeguard. (#6187) Thanks @iamEvanYT.</li>
<li>System prompt: resolve overrides and hint using session_status for current date/time. (#1897, #1928, #2108, #3677)</li>
<li>Agents: fix Pi prompt template argument syntax. (#6543)</li>
<li>Subagents: fix announce failover race (always emit lifecycle end; timeout=0 means no-timeout). (#6621)</li>
<li>Teams: gate media auth retries.</li>
<li>Telegram: restore draft streaming partials. (#5543) Thanks @obviyus.</li>
<li>Onboarding: friendlier Windows onboarding message. (#6242) Thanks @shanselman.</li>
<li>TUI: prevent crash when searching with digits in the model selector.</li>
<li>Agents: wire before_tool_call plugin hook into tool execution. (#6570, #6660) Thanks @ryancnelson.</li>
<li>Browser: secure Chrome extension relay CDP sessions.</li>
<li>Docker: use container port for gateway command instead of host port. (#5110) Thanks @mise42.</li>
<li>fix(lobster): block arbitrary exec via lobsterPath/cwd injection (GHSA-4mhr-g7xj-cg8j). (#5335) Thanks @vignesh07.</li>
<li>Security: sanitize WhatsApp accountId to prevent path traversal. (#4610)</li>
<li>Security: restrict MEDIA path extraction to prevent LFI. (#4930)</li>
<li>Security: validate message-tool filePath/path against sandbox root. (#6398)</li>
<li>Security: block LD*/DYLD* env overrides for host exec. (#4896) Thanks @HassanFleyah.</li>
<li>Security: harden web tool content wrapping + file parsing safeguards. (#4058) Thanks @VACInc.</li>
<li>Security: enforce Twitch <code>allowFrom</code> allowlist gating (deny non-allowlisted senders). Thanks @MegaManSec.</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.2.1/OpenClaw-2026.2.1.zip" length="22458919" type="application/octet-stream" sparkle:edSignature="kA/8VQlVdtYphcB1iuFrhWczwWKgkVZMfDfQ7T9WD405D8JKTv5CZ1n8lstIVkpk4xog3UhrfaaoTG8Bf8DMAQ=="/>
</item>
<item>
<title>2026.1.30</title>
<pubDate>Sat, 31 Jan 2026 14:29:57 +0100</pubDate>
@@ -157,5 +47,153 @@
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.1.30/OpenClaw-2026.1.30.zip" length="22458594" type="application/octet-stream" sparkle:edSignature="77/GuEcruKGgu2CJyMq+OVwzaJ2v1VzRQC9NmOirKO3uH5Nn5HaoouwrOHnOanrzlD4OvPW0FS5GH2E4Ntu4CQ=="/>
</item>
<item>
<title>2026.1.29</title>
<pubDate>Fri, 30 Jan 2026 06:24:15 +0100</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>8345</sparkle:version>
<sparkle:shortVersionString>2026.1.29</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.1.29</h2>
Status: stable.
<h3>Changes</h3>
<ul>
<li>Rebrand: rename the npm package/CLI to <code>openclaw</code>, add a <code>openclaw</code> compatibility shim, and move extensions to the <code>@openclaw/*</code> scope.</li>
<li>Onboarding: strengthen security warning copy for beta + access control expectations.</li>
<li>Onboarding: add Venice API key to non-interactive flow. (#1893) Thanks @jonisjongithub.</li>
<li>Config: auto-migrate legacy state/config paths and keep config resolution consistent across legacy filenames.</li>
<li>Gateway: warn on hook tokens via query params; document header auth preference. (#2200) Thanks @YuriNachos.</li>
<li>Gateway: add dangerous Control UI device auth bypass flag + audit warnings. (#2248)</li>
<li>Doctor: warn on gateway exposure without auth. (#2016) Thanks @Alex-Alaniz.</li>
<li>Web UI: keep sub-agent announce replies visible in WebChat. (#1977) Thanks @andrescardonas7.</li>
<li>Browser: route browser control via gateway/node; remove standalone browser control command and control URL config.</li>
<li>Browser: route <code>browser.request</code> via node proxies when available; honor proxy timeouts; derive browser ports from <code>gateway.port</code>.</li>
<li>Browser: fall back to URL matching for extension relay target resolution. (#1999) Thanks @jonit-dev.</li>
<li>Telegram: allow caption param for media sends. (#1888) Thanks @mguellsegarra.</li>
<li>Telegram: support plugin sendPayload channelData (media/buttons) and validate plugin commands. (#1917) Thanks @JoshuaLelon.</li>
<li>Telegram: avoid block replies when streaming is disabled. (#1885) Thanks @ivancasco.</li>
<li>Telegram: add optional silent send flag (disable notifications). (#2382) Thanks @Suksham-sharma.</li>
<li>Telegram: support editing sent messages via message(action="edit"). (#2394) Thanks @marcelomar21.</li>
<li>Telegram: support quote replies for message tool and inbound context. (#2900) Thanks @aduk059.</li>
<li>Telegram: add sticker receive/send with vision caching. (#2629) Thanks @longjos.</li>
<li>Telegram: send sticker pixels to vision models. (#2650)</li>
<li>Telegram: keep topic IDs in restart sentinel notifications. (#1807) Thanks @hsrvc.</li>
<li>Discord: add configurable privileged gateway intents for presences/members. (#2266) Thanks @kentaro.</li>
<li>Slack: clear ack reaction after streamed replies. (#2044) Thanks @fancyboi999.</li>
<li>Matrix: switch plugin SDK to @vector-im/matrix-bot-sdk.</li>
<li>Tlon: format thread reply IDs as @ud. (#1837) Thanks @wca4a.</li>
<li>Tools: add per-sender group tool policies and fix precedence. (#1757) Thanks @adam91holt.</li>
<li>Agents: summarize dropped messages during compaction safeguard pruning. (#2509) Thanks @jogi47.</li>
<li>Agents: expand cron tool description with full schema docs. (#1988) Thanks @tomascupr.</li>
<li>Agents: honor tools.exec.safeBins in exec allowlist checks. (#2281)</li>
<li>Memory Search: allow extra paths for memory indexing (ignores symlinks). (#3600) Thanks @kira-ariaki.</li>
<li>Skills: add multi-image input support to Nano Banana Pro skill. (#1958) Thanks @tyler6204.</li>
<li>Skills: add missing dependency metadata for GitHub, Notion, Slack, Discord. (#1995) Thanks @jackheuberger.</li>
<li>Commands: group /help and /commands output with Telegram paging. (#2504) Thanks @hougangdev.</li>
<li>Routing: add per-account DM session scope and document multi-account isolation. (#3095) Thanks @jarvis-sam.</li>
<li>Routing: precompile session key regexes. (#1697) Thanks @Ray0907.</li>
<li>CLI: use Node's module compile cache for faster startup. (#2808) Thanks @pi0.</li>
<li>Auth: show copyable Google auth URL after ASCII prompt. (#1787) Thanks @robbyczgw-cla.</li>
<li>TUI: avoid width overflow when rendering selection lists. (#1686) Thanks @mossein.</li>
<li>macOS: finish OpenClaw app rename for macOS sources, bundle identifiers, and shared kit paths. (#2844) Thanks @fal3.</li>
<li>Branding: update launchd labels, mobile bundle IDs, and logging subsystems to bot.molt (legacy bundle ID migrations). Thanks @thewilloftheshadow.</li>
<li>macOS: limit project-local <code>node_modules/.bin</code> PATH preference to debug builds (reduce PATH hijacking risk).</li>
<li>macOS: keep custom SSH usernames in remote target. (#2046) Thanks @algal.</li>
<li>macOS: avoid crash when rendering code blocks by bumping Textual to 0.3.1. (#2033) Thanks @garricn.</li>
<li>Update: ignore dist/control-ui for dirty checks and restore after ui builds. (#1976) Thanks @Glucksberg.</li>
<li>Build: bundle A2UI assets during build and stop tracking generated bundles. (#2455) Thanks @0oAstro.</li>
<li>CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi.</li>
<li>Config: apply config.env before ${VAR} substitution. (#1813) Thanks @spanishflu-est1918.</li>
<li>Gateway: prefer newest session metadata when combining stores. (#1823) Thanks @emanuelst.</li>
<li>Docs: tighten Fly private deployment steps. (#2289) Thanks @dguido.</li>
<li>Docs: add migration guide for moving to a new machine. (#2381)</li>
<li>Docs: add Northflank one-click deployment guide. (#2167) Thanks @AdeboyeDN.</li>
<li>Docs: add Vercel AI Gateway to providers sidebar. (#1901) Thanks @jerilynzheng.</li>
<li>Docs: add Render deployment guide. (#1975) Thanks @anurag.</li>
<li>Docs: add Claude Max API Proxy guide. (#1875) Thanks @atalovesyou.</li>
<li>Docs: add DigitalOcean deployment guide. (#1870) Thanks @0xJonHoldsCrypto.</li>
<li>Docs: add Oracle Cloud (OCI) platform guide + cross-links. (#2333) Thanks @hirefrank.</li>
<li>Docs: add Raspberry Pi install guide. (#1871) Thanks @0xJonHoldsCrypto.</li>
<li>Docs: add GCP Compute Engine deployment guide. (#1848) Thanks @hougangdev.</li>
<li>Docs: add LINE channel guide. Thanks @thewilloftheshadow.</li>
<li>Docs: credit both contributors for Control UI refresh. (#1852) Thanks @EnzeD.</li>
<li>Docs: keep docs header sticky so navbar stays visible while scrolling. (#2445) Thanks @chenyuan99.</li>
<li>Docs: update exe.dev install instructions. (#https://github.com/openclaw/openclaw/pull/3047) Thanks @zackerthescar.</li>
</ul>
<h3>Breaking</h3>
<ul>
<li><strong>BREAKING:</strong> Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed).</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Telegram: avoid silent empty replies by tracking normalization skips before fallback. (#3796)</li>
<li>Mentions: honor mentionPatterns even when explicit mentions are present. (#3303) Thanks @HirokiKobayashi-R.</li>
<li>Discord: restore username directory lookup in target resolution. (#3131) Thanks @bonald.</li>
<li>Agents: align MiniMax base URL test expectation with default provider config. (#3131) Thanks @bonald.</li>
<li>Agents: prevent retries on oversized image errors and surface size limits. (#2871) Thanks @Suksham-sharma.</li>
<li>Agents: inherit provider baseUrl/api for inline models. (#2740) Thanks @lploc94.</li>
<li>Memory Search: keep auto provider model defaults and only include remote when configured. (#2576) Thanks @papago2355.</li>
<li>Telegram: include AccountId in native command context for multi-agent routing. (#2942) Thanks @Chloe-VP.</li>
<li>Telegram: handle video note attachments in media extraction. (#2905) Thanks @mylukin.</li>
<li>TTS: read OPENAI_TTS_BASE_URL at runtime instead of module load to honor config.env. (#3341) Thanks @hclsys.</li>
<li>macOS: auto-scroll to bottom when sending a new message while scrolled up. (#2471) Thanks @kennyklee.</li>
<li>Web UI: auto-expand the chat compose textarea while typing (with sensible max height). (#2950) Thanks @shivamraut101.</li>
<li>Gateway: prevent crashes on transient network errors (fetch failures, timeouts, DNS). Added fatal error detection to only exit on truly critical errors. Fixes #2895, #2879, #2873. (#2980) Thanks @elliotsecops.</li>
<li>Agents: guard channel tool listActions to avoid plugin crashes. (#2859) Thanks @mbelinky.</li>
<li>Discord: stop resolveDiscordTarget from passing directory params into messaging target parsers. Fixes #3167. Thanks @thewilloftheshadow.</li>
<li>Discord: avoid resolving bare channel names to user DMs when a username matches. Thanks @thewilloftheshadow.</li>
<li>Discord: fix directory config type import for target resolution. Thanks @thewilloftheshadow.</li>
<li>Providers: update MiniMax API endpoint and compatibility mode. (#3064) Thanks @hlbbbbbbb.</li>
<li>Telegram: treat more network errors as recoverable in polling. (#3013) Thanks @ryancontent.</li>
<li>Discord: resolve usernames to user IDs for outbound messages. (#2649) Thanks @nonggialiang.</li>
<li>Providers: update Moonshot Kimi model references to kimi-k2.5. (#2762) Thanks @MarvinCui.</li>
<li>Gateway: suppress AbortError and transient network errors in unhandled rejections. (#2451) Thanks @Glucksberg.</li>
<li>TTS: keep /tts status replies on text-only commands and avoid duplicate block-stream audio. (#2451) Thanks @Glucksberg.</li>
<li>Security: pin npm overrides to keep tar@7.5.4 for install toolchains.</li>
<li>Security: properly test Windows ACL audit for config includes. (#2403) Thanks @dominicnunez.</li>
<li>CLI: recognize versioned Node executables when parsing argv. (#2490) Thanks @David-Marsh-Photo.</li>
<li>CLI: avoid prompting for gateway runtime under the spinner. (#2874)</li>
<li>BlueBubbles: coalesce inbound URL link preview messages. (#1981) Thanks @tyler6204.</li>
<li>Cron: allow payloads containing "heartbeat" in event filter. (#2219) Thanks @dwfinkelstein.</li>
<li>CLI: avoid loading config for global help/version while registering plugin commands. (#2212) Thanks @dial481.</li>
<li>Agents: include memory.md when bootstrapping memory context. (#2318) Thanks @czekaj.</li>
<li>Agents: release session locks on process termination and cover more signals. (#2483) Thanks @janeexai.</li>
<li>Agents: skip cooldowned providers during model failover. (#2143) Thanks @YiWang24.</li>
<li>Telegram: harden polling + retry behavior for transient network errors and Node 22 transport issues. (#2420) Thanks @techboss.</li>
<li>Telegram: ignore non-forum group message_thread_id while preserving DM thread sessions. (#2731) Thanks @dylanneve1.</li>
<li>Telegram: wrap reasoning italics per line to avoid raw underscores. (#2181) Thanks @YuriNachos.</li>
<li>Telegram: centralize API error logging for delivery and bot calls. (#2492) Thanks @altryne.</li>
<li>Voice Call: enforce Twilio webhook signature verification for ngrok URLs; disable ngrok free tier bypass by default.</li>
<li>Security: harden Tailscale Serve auth by validating identity via local tailscaled before trusting headers.</li>
<li>Media: fix text attachment MIME misclassification with CSV/TSV inference and UTF-16 detection; add XML attribute escaping for file output. (#3628) Thanks @frankekn.</li>
<li>Build: align memory-core peer dependency with lockfile.</li>
<li>Security: add mDNS discovery mode with minimal default to reduce information disclosure. (#1882) Thanks @orlyjamie.</li>
<li>Security: harden URL fetches with DNS pinning to reduce rebinding risk. Thanks Chris Zheng.</li>
<li>Web UI: improve WebChat image paste previews and allow image-only sends. (#1925) Thanks @smartprogrammer93.</li>
<li>Security: wrap external hook content by default with a per-hook opt-out. (#1827) Thanks @mertcicekci0.</li>
<li>Gateway: default auth now fail-closed (token/password required; Tailscale Serve identity remains allowed).</li>
<li>Gateway: treat loopback + non-local Host connections as remote unless trusted proxy headers are present.</li>
<li>Onboarding: remove unsupported gateway auth "off" choice from onboarding/configure flows and CLI flags.</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.1.29/OpenClaw-2026.1.29.zip" length="22458204" type="application/octet-stream" sparkle:edSignature="HqHwZHQyG/CEfBuQnQ/RffJQPKpSbCVrho9C6rgt93S5ek4AH6hUhB3BBKY8sbX1IVFATKK5QZZNE0YPAf7eBw=="/>
</item>
<item>
<title>2026.1.24-1</title>
<pubDate>Sun, 25 Jan 2026 14:05:25 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>7952</sparkle:version>
<sparkle:shortVersionString>2026.1.24-1</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.1.24-1</h2>
<h3>Fixes</h3>
<ul>
<li>Packaging: include dist/shared output in npm tarball (fixes missing reasoning-tags import on install).</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.1.24-1/OpenClaw-2026.1.24-1.zip" length="12396699" type="application/octet-stream" sparkle:edSignature="VaEdWIgEJBrZLIp2UmigoQ6vaq4P/jNFXpHYXvXHD5MsATS0CqBl6ugyyxRq+/GbpUqmdgdlht4dTUVbLRw6BA=="/>
</item>
</channel>
</rss>

View File

@@ -21,8 +21,8 @@ android {
applicationId = "ai.openclaw.android"
minSdk = 31
targetSdk = 36
versionCode = 202602030
versionName = "2026.2.3"
versionCode = 202601290
versionName = "2026.1.30"
}
buildTypes {

View File

@@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.3</string>
<string>2026.1.30</string>
<key>CFBundleVersion</key>
<string>20260202</string>
<string>20260129</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoadsInWebContent</key>

View File

@@ -17,8 +17,8 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.3</string>
<string>2026.1.30</string>
<key>CFBundleVersion</key>
<string>20260202</string>
<string>20260129</string>
</dict>
</plist>

View File

@@ -81,8 +81,8 @@ targets:
properties:
CFBundleDisplayName: OpenClaw
CFBundleIconName: AppIcon
CFBundleShortVersionString: "2026.2.3"
CFBundleVersion: "20260202"
CFBundleShortVersionString: "2026.1.27-beta.1"
CFBundleVersion: "20260126"
UILaunchScreen: {}
UIApplicationSceneManifest:
UIApplicationSupportsMultipleScenes: false
@@ -130,5 +130,5 @@ targets:
path: Tests/Info.plist
properties:
CFBundleDisplayName: OpenClawTests
CFBundleShortVersionString: "2026.2.3"
CFBundleVersion: "20260202"
CFBundleShortVersionString: "2026.1.27-beta.1"
CFBundleVersion: "20260126"

View File

@@ -20,11 +20,9 @@ extension CronJobEditor {
self.wakeMode = job.wakeMode
switch job.schedule {
case let .at(at):
case let .at(atMs):
self.scheduleKind = .at
if let date = CronSchedule.parseAtDate(at) {
self.atDate = date
}
self.atDate = Date(timeIntervalSince1970: TimeInterval(atMs) / 1000)
case let .every(everyMs, _):
self.scheduleKind = .every
self.everyText = self.formatDuration(ms: everyMs)
@@ -38,22 +36,19 @@ extension CronJobEditor {
case let .systemEvent(text):
self.payloadKind = .systemEvent
self.systemEventText = text
case let .agentTurn(message, thinking, timeoutSeconds, _, _, _, _):
case let .agentTurn(message, thinking, timeoutSeconds, deliver, channel, to, bestEffortDeliver):
self.payloadKind = .agentTurn
self.agentMessage = message
self.thinking = thinking ?? ""
self.timeoutSeconds = timeoutSeconds.map(String.init) ?? ""
self.deliver = deliver ?? false
let trimmed = (channel ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
self.channel = trimmed.isEmpty ? "last" : trimmed
self.to = to ?? ""
self.bestEffortDeliver = bestEffortDeliver ?? false
}
if let delivery = job.delivery {
self.deliveryMode = delivery.mode == .announce ? .announce : .none
let trimmed = (delivery.channel ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
self.channel = trimmed.isEmpty ? "last" : trimmed
self.to = delivery.to ?? ""
self.bestEffortDeliver = delivery.bestEffort ?? false
} else if self.sessionTarget == .isolated {
self.deliveryMode = .announce
}
self.postPrefix = job.isolation?.postToMainPrefix ?? "Cron"
}
func save() {
@@ -93,29 +88,15 @@ extension CronJobEditor {
}
if self.sessionTarget == .isolated {
root["delivery"] = self.buildDelivery()
let trimmed = self.postPrefix.trimmingCharacters(in: .whitespacesAndNewlines)
root["isolation"] = [
"postToMainPrefix": trimmed.isEmpty ? "Cron" : trimmed,
]
}
return root.mapValues { AnyCodable($0) }
}
func buildDelivery() -> [String: Any] {
let mode = self.deliveryMode == .announce ? "announce" : "none"
var delivery: [String: Any] = ["mode": mode]
if self.deliveryMode == .announce {
let trimmed = self.channel.trimmingCharacters(in: .whitespacesAndNewlines)
delivery["channel"] = trimmed.isEmpty ? "last" : trimmed
let to = self.to.trimmingCharacters(in: .whitespacesAndNewlines)
if !to.isEmpty { delivery["to"] = to }
if self.bestEffortDeliver {
delivery["bestEffort"] = true
} else if self.job?.delivery?.bestEffort == true {
delivery["bestEffort"] = false
}
}
return delivery
}
func trimmed(_ value: String) -> String {
value.trimmingCharacters(in: .whitespacesAndNewlines)
}
@@ -134,7 +115,7 @@ extension CronJobEditor {
func buildSchedule() throws -> [String: Any] {
switch self.scheduleKind {
case .at:
return ["kind": "at", "at": CronSchedule.formatIsoDate(self.atDate)]
return ["kind": "at", "atMs": Int(self.atDate.timeIntervalSince1970 * 1000)]
case .every:
guard let ms = Self.parseDurationMs(self.everyText) else {
throw NSError(
@@ -228,6 +209,14 @@ extension CronJobEditor {
let thinking = self.thinking.trimmingCharacters(in: .whitespacesAndNewlines)
if !thinking.isEmpty { payload["thinking"] = thinking }
if let n = Int(self.timeoutSeconds), n > 0 { payload["timeoutSeconds"] = n }
payload["deliver"] = self.deliver
if self.deliver {
let trimmed = self.channel.trimmingCharacters(in: .whitespacesAndNewlines)
payload["channel"] = trimmed.isEmpty ? "last" : trimmed
let to = self.to.trimmingCharacters(in: .whitespacesAndNewlines)
if !to.isEmpty { payload["to"] = to }
payload["bestEffortDeliver"] = self.bestEffortDeliver
}
return payload
}

View File

@@ -13,12 +13,13 @@ extension CronJobEditor {
self.payloadKind = .agentTurn
self.agentMessage = "Run diagnostic"
self.deliveryMode = .announce
self.deliver = true
self.channel = "last"
self.to = "+15551230000"
self.thinking = "low"
self.timeoutSeconds = "90"
self.bestEffortDeliver = true
self.postPrefix = "Cron"
_ = self.buildAgentTurnPayload()
_ = try? self.buildPayload()

View File

@@ -16,13 +16,16 @@ struct CronJobEditor: View {
+ "Use an isolated session for agent turns so your main chat stays clean."
static let sessionTargetNote =
"Main jobs post a system event into the current main session. "
+ "Isolated jobs run OpenClaw in a dedicated session and can announce results to a channel."
+ "Isolated jobs run OpenClaw in a dedicated session and can deliver results (WhatsApp/Telegram/Discord/etc)."
static let scheduleKindNote =
"“At” runs once, “Every” repeats with a duration, “Cron” uses a 5-field Unix expression."
static let isolatedPayloadNote =
"Isolated jobs always run an agent turn. Announce sends a short summary to a channel."
"Isolated jobs always run an agent turn. The result can be delivered to a channel, "
+ "and a short summary is posted back to your main chat."
static let mainPayloadNote =
"System events are injected into the current main session. Agent turns require an isolated session target."
static let mainSummaryNote =
"Controls the label used when posting the completion summary back to the main session."
@State var name: String = ""
@State var description: String = ""
@@ -43,13 +46,13 @@ struct CronJobEditor: View {
@State var payloadKind: PayloadKind = .systemEvent
@State var systemEventText: String = ""
@State var agentMessage: String = ""
enum DeliveryChoice: String, CaseIterable, Identifiable { case announce, none; var id: String { rawValue } }
@State var deliveryMode: DeliveryChoice = .announce
@State var deliver: Bool = false
@State var channel: String = "last"
@State var to: String = ""
@State var thinking: String = ""
@State var timeoutSeconds: String = ""
@State var bestEffortDeliver: Bool = false
@State var postPrefix: String = "Cron"
var channelOptions: [String] {
let ordered = self.channelsStore.orderedChannelIds()
@@ -245,6 +248,27 @@ struct CronJobEditor: View {
}
}
if self.sessionTarget == .isolated {
GroupBox("Main session summary") {
Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 14, verticalSpacing: 10) {
GridRow {
self.gridLabel("Prefix")
TextField("Cron", text: self.$postPrefix)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: .infinity)
}
GridRow {
Color.clear
.frame(width: self.labelColumnWidth, height: 1)
Text(
Self.mainSummaryNote)
.font(.footnote)
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 2)
@@ -316,17 +340,13 @@ struct CronJobEditor: View {
.frame(width: 180, alignment: .leading)
}
GridRow {
self.gridLabel("Delivery")
Picker("", selection: self.$deliveryMode) {
Text("Announce summary").tag(DeliveryChoice.announce)
Text("None").tag(DeliveryChoice.none)
}
.labelsHidden()
.pickerStyle(.segmented)
self.gridLabel("Deliver")
Toggle("Deliver result to a channel", isOn: self.$deliver)
.toggleStyle(.switch)
}
}
if self.deliveryMode == .announce {
if self.deliver {
Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 14, verticalSpacing: 10) {
GridRow {
self.gridLabel("Channel")
@@ -347,7 +367,7 @@ struct CronJobEditor: View {
}
GridRow {
self.gridLabel("Best-effort")
Toggle("Do not fail the job if announce fails", isOn: self.$bestEffortDeliver)
Toggle("Do not fail the job if delivery fails", isOn: self.$bestEffortDeliver)
.toggleStyle(.switch)
}
}

View File

@@ -14,26 +14,12 @@ enum CronWakeMode: String, CaseIterable, Identifiable, Codable {
var id: String { self.rawValue }
}
enum CronDeliveryMode: String, CaseIterable, Identifiable, Codable {
case none
case announce
var id: String { self.rawValue }
}
struct CronDelivery: Codable, Equatable {
var mode: CronDeliveryMode
var channel: String?
var to: String?
var bestEffort: Bool?
}
enum CronSchedule: Codable, Equatable {
case at(at: String)
case at(atMs: Int)
case every(everyMs: Int, anchorMs: Int?)
case cron(expr: String, tz: String?)
enum CodingKeys: String, CodingKey { case kind, at, atMs, everyMs, anchorMs, expr, tz }
enum CodingKeys: String, CodingKey { case kind, atMs, everyMs, anchorMs, expr, tz }
var kind: String {
switch self {
@@ -48,21 +34,7 @@ enum CronSchedule: Codable, Equatable {
let kind = try container.decode(String.self, forKey: .kind)
switch kind {
case "at":
if let at = try container.decodeIfPresent(String.self, forKey: .at),
!at.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
{
self = .at(at: at)
return
}
if let atMs = try container.decodeIfPresent(Int.self, forKey: .atMs) {
let date = Date(timeIntervalSince1970: TimeInterval(atMs) / 1000)
self = .at(at: Self.formatIsoDate(date))
return
}
throw DecodingError.dataCorruptedError(
forKey: .at,
in: container,
debugDescription: "Missing schedule.at")
self = try .at(atMs: container.decode(Int.self, forKey: .atMs))
case "every":
self = try .every(
everyMs: container.decode(Int.self, forKey: .everyMs),
@@ -83,8 +55,8 @@ enum CronSchedule: Codable, Equatable {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.kind, forKey: .kind)
switch self {
case let .at(at):
try container.encode(at, forKey: .at)
case let .at(atMs):
try container.encode(atMs, forKey: .atMs)
case let .every(everyMs, anchorMs):
try container.encode(everyMs, forKey: .everyMs)
try container.encodeIfPresent(anchorMs, forKey: .anchorMs)
@@ -93,29 +65,6 @@ enum CronSchedule: Codable, Equatable {
try container.encodeIfPresent(tz, forKey: .tz)
}
}
static func parseAtDate(_ value: String) -> Date? {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmed.isEmpty { return nil }
if let date = isoFormatterWithFractional.date(from: trimmed) { return date }
return isoFormatter.date(from: trimmed)
}
static func formatIsoDate(_ date: Date) -> String {
isoFormatter.string(from: date)
}
private static let isoFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime]
return formatter
}()
private static let isoFormatterWithFractional: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter
}()
}
enum CronPayload: Codable, Equatable {
@@ -182,6 +131,10 @@ enum CronPayload: Codable, Equatable {
}
}
struct CronIsolation: Codable, Equatable {
var postToMainPrefix: String?
}
struct CronJobState: Codable, Equatable {
var nextRunAtMs: Int?
var runningAtMs: Int?
@@ -204,7 +157,7 @@ struct CronJob: Identifiable, Codable, Equatable {
let sessionTarget: CronSessionTarget
let wakeMode: CronWakeMode
let payload: CronPayload
let delivery: CronDelivery?
let isolation: CronIsolation?
let state: CronJobState
var displayName: String {

View File

@@ -17,11 +17,9 @@ extension CronSettings {
func scheduleSummary(_ schedule: CronSchedule) -> String {
switch schedule {
case let .at(at):
if let date = CronSchedule.parseAtDate(at) {
return "at \(date.formatted(date: .abbreviated, time: .standard))"
}
return "at \(at)"
case let .at(atMs):
let date = Date(timeIntervalSince1970: TimeInterval(atMs) / 1000)
return "at \(date.formatted(date: .abbreviated, time: .standard))"
case let .every(everyMs, _):
return "every \(self.formatDuration(ms: everyMs))"
case let .cron(expr, tz):

View File

@@ -128,7 +128,7 @@ extension CronSettings {
.foregroundStyle(.orange)
.textSelection(.enabled)
}
self.payloadSummary(job)
self.payloadSummary(job.payload)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(10)
@@ -205,8 +205,7 @@ extension CronSettings {
.padding(.vertical, 4)
}
func payloadSummary(_ job: CronJob) -> some View {
let payload = job.payload
func payloadSummary(_ payload: CronPayload) -> some View {
VStack(alignment: .leading, spacing: 6) {
Text("Payload")
.font(.caption.weight(.semibold))
@@ -216,7 +215,7 @@ extension CronSettings {
Text(text)
.font(.callout)
.textSelection(.enabled)
case let .agentTurn(message, thinking, timeoutSeconds, _, _, _, _):
case let .agentTurn(message, thinking, timeoutSeconds, deliver, provider, to, _):
VStack(alignment: .leading, spacing: 4) {
Text(message)
.font(.callout)
@@ -224,19 +223,10 @@ extension CronSettings {
HStack(spacing: 8) {
if let thinking, !thinking.isEmpty { StatusPill(text: "think \(thinking)", tint: .secondary) }
if let timeoutSeconds { StatusPill(text: "\(timeoutSeconds)s", tint: .secondary) }
if job.sessionTarget == .isolated {
let delivery = job.delivery
if let delivery {
if delivery.mode == .announce {
StatusPill(text: "announce", tint: .secondary)
if let channel = delivery.channel, !channel.isEmpty {
StatusPill(text: channel, tint: .secondary)
}
if let to = delivery.to, !to.isEmpty { StatusPill(text: to, tint: .secondary) }
} else {
StatusPill(text: "no delivery", tint: .secondary)
}
}
if deliver ?? false {
StatusPill(text: "deliver", tint: .secondary)
if let provider, !provider.isEmpty { StatusPill(text: provider, tint: .secondary) }
if let to, !to.isEmpty { StatusPill(text: to, tint: .secondary) }
}
}
}

View File

@@ -21,11 +21,11 @@ struct CronSettings_Previews: PreviewProvider {
message: "Summarize inbox",
thinking: "low",
timeoutSeconds: 600,
deliver: nil,
channel: nil,
deliver: true,
channel: "last",
to: nil,
bestEffortDeliver: nil),
delivery: CronDelivery(mode: .announce, channel: "last", to: nil, bestEffort: true),
bestEffortDeliver: true),
isolation: CronIsolation(postToMainPrefix: "Cron"),
state: CronJobState(
nextRunAtMs: Int(Date().addingTimeInterval(3600).timeIntervalSince1970 * 1000),
runningAtMs: nil,
@@ -75,11 +75,11 @@ extension CronSettings {
message: "Summarize",
thinking: "low",
timeoutSeconds: 120,
deliver: nil,
channel: nil,
to: nil,
bestEffortDeliver: nil),
delivery: CronDelivery(mode: .announce, channel: "whatsapp", to: "+15551234567", bestEffort: true),
deliver: true,
channel: "whatsapp",
to: "+15551234567",
bestEffortDeliver: true),
isolation: CronIsolation(postToMainPrefix: "[cron] "),
state: CronJobState(
nextRunAtMs: 1_700_000_200_000,
runningAtMs: nil,
@@ -111,7 +111,7 @@ extension CronSettings {
_ = view.detailCard(job)
_ = view.runHistoryCard(job)
_ = view.runRow(run)
_ = view.payloadSummary(job)
_ = view.payloadSummary(job.payload)
_ = view.scheduleSummary(job.schedule)
_ = view.statusTint(job.state.lastStatus)
_ = view.nextRunLabel(Date())

View File

@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.3</string>
<string>2026.1.30</string>
<key>CFBundleVersion</key>
<string>202602020</string>
<string>202601290</string>
<key>CFBundleIconFile</key>
<string>OpenClaw</string>
<key>CFBundleURLTypes</key>

View File

@@ -589,24 +589,20 @@ public struct AgentIdentityResult: Codable, Sendable {
public let agentid: String
public let name: String?
public let avatar: String?
public let emoji: String?
public init(
agentid: String,
name: String?,
avatar: String?,
emoji: String?
avatar: String?
) {
self.agentid = agentid
self.name = name
self.avatar = avatar
self.emoji = emoji
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case name
case avatar
case emoji
}
}
@@ -1560,157 +1556,6 @@ public struct AgentSummary: Codable, Sendable {
}
}
public struct AgentsFileEntry: Codable, Sendable {
public let name: String
public let path: String
public let missing: Bool
public let size: Int?
public let updatedatms: Int?
public let content: String?
public init(
name: String,
path: String,
missing: Bool,
size: Int?,
updatedatms: Int?,
content: String?
) {
self.name = name
self.path = path
self.missing = missing
self.size = size
self.updatedatms = updatedatms
self.content = content
}
private enum CodingKeys: String, CodingKey {
case name
case path
case missing
case size
case updatedatms = "updatedAtMs"
case content
}
}
public struct AgentsFilesListParams: Codable, Sendable {
public let agentid: String
public init(
agentid: String
) {
self.agentid = agentid
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
}
}
public struct AgentsFilesListResult: Codable, Sendable {
public let agentid: String
public let workspace: String
public let files: [AgentsFileEntry]
public init(
agentid: String,
workspace: String,
files: [AgentsFileEntry]
) {
self.agentid = agentid
self.workspace = workspace
self.files = files
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case workspace
case files
}
}
public struct AgentsFilesGetParams: Codable, Sendable {
public let agentid: String
public let name: String
public init(
agentid: String,
name: String
) {
self.agentid = agentid
self.name = name
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case name
}
}
public struct AgentsFilesGetResult: Codable, Sendable {
public let agentid: String
public let workspace: String
public let file: AgentsFileEntry
public init(
agentid: String,
workspace: String,
file: AgentsFileEntry
) {
self.agentid = agentid
self.workspace = workspace
self.file = file
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case workspace
case file
}
}
public struct AgentsFilesSetParams: Codable, Sendable {
public let agentid: String
public let name: String
public let content: String
public init(
agentid: String,
name: String,
content: String
) {
self.agentid = agentid
self.name = name
self.content = content
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case name
case content
}
}
public struct AgentsFilesSetResult: Codable, Sendable {
public let ok: Bool
public let agentid: String
public let workspace: String
public let file: AgentsFileEntry
public init(
ok: Bool,
agentid: String,
workspace: String,
file: AgentsFileEntry
) {
self.ok = ok
self.agentid = agentid
self.workspace = workspace
self.file = file
}
private enum CodingKeys: String, CodingKey {
case ok
case agentid = "agentId"
case workspace
case file
}
}
public struct AgentsListParams: Codable, Sendable {
}
@@ -1785,16 +1630,6 @@ public struct ModelsListResult: Codable, Sendable {
}
public struct SkillsStatusParams: Codable, Sendable {
public let agentid: String?
public init(
agentid: String?
) {
self.agentid = agentid
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
}
}
public struct SkillsBinsParams: Codable, Sendable {
@@ -1872,7 +1707,7 @@ public struct CronJob: Codable, Sendable {
public let sessiontarget: AnyCodable
public let wakemode: AnyCodable
public let payload: AnyCodable
public let delivery: [String: AnyCodable]?
public let isolation: [String: AnyCodable]?
public let state: [String: AnyCodable]
public init(
@@ -1888,7 +1723,7 @@ public struct CronJob: Codable, Sendable {
sessiontarget: AnyCodable,
wakemode: AnyCodable,
payload: AnyCodable,
delivery: [String: AnyCodable]?,
isolation: [String: AnyCodable]?,
state: [String: AnyCodable]
) {
self.id = id
@@ -1903,7 +1738,7 @@ public struct CronJob: Codable, Sendable {
self.sessiontarget = sessiontarget
self.wakemode = wakemode
self.payload = payload
self.delivery = delivery
self.isolation = isolation
self.state = state
}
private enum CodingKeys: String, CodingKey {
@@ -1919,7 +1754,7 @@ public struct CronJob: Codable, Sendable {
case sessiontarget = "sessionTarget"
case wakemode = "wakeMode"
case payload
case delivery
case isolation
case state
}
}
@@ -1950,7 +1785,7 @@ public struct CronAddParams: Codable, Sendable {
public let sessiontarget: AnyCodable
public let wakemode: AnyCodable
public let payload: AnyCodable
public let delivery: [String: AnyCodable]?
public let isolation: [String: AnyCodable]?
public init(
name: String,
@@ -1962,7 +1797,7 @@ public struct CronAddParams: Codable, Sendable {
sessiontarget: AnyCodable,
wakemode: AnyCodable,
payload: AnyCodable,
delivery: [String: AnyCodable]?
isolation: [String: AnyCodable]?
) {
self.name = name
self.agentid = agentid
@@ -1973,7 +1808,7 @@ public struct CronAddParams: Codable, Sendable {
self.sessiontarget = sessiontarget
self.wakemode = wakemode
self.payload = payload
self.delivery = delivery
self.isolation = isolation
}
private enum CodingKeys: String, CodingKey {
case name
@@ -1985,7 +1820,7 @@ public struct CronAddParams: Codable, Sendable {
case sessiontarget = "sessionTarget"
case wakemode = "wakeMode"
case payload
case delivery
case isolation
}
}

View File

@@ -40,11 +40,11 @@ struct CronJobEditorSmokeTests {
message: "Summarize the last day",
thinking: "low",
timeoutSeconds: 120,
deliver: nil,
channel: nil,
to: nil,
bestEffortDeliver: nil),
delivery: CronDelivery(mode: .announce, channel: "whatsapp", to: "+15551234567", bestEffort: true),
deliver: true,
channel: "whatsapp",
to: "+15551234567",
bestEffortDeliver: true),
isolation: CronIsolation(postToMainPrefix: "Cron"),
state: CronJobState(
nextRunAtMs: 1_700_000_100_000,
runningAtMs: nil,

View File

@@ -5,24 +5,12 @@ import Testing
@Suite
struct CronModelsTests {
@Test func scheduleAtEncodesAndDecodes() throws {
let schedule = CronSchedule.at(at: "2026-02-03T18:00:00Z")
let schedule = CronSchedule.at(atMs: 123)
let data = try JSONEncoder().encode(schedule)
let decoded = try JSONDecoder().decode(CronSchedule.self, from: data)
#expect(decoded == schedule)
}
@Test func scheduleAtDecodesLegacyAtMs() throws {
let json = """
{"kind":"at","atMs":1700000000000}
"""
let decoded = try JSONDecoder().decode(CronSchedule.self, from: Data(json.utf8))
if case let .at(at) = decoded {
#expect(at.hasPrefix("2023-"))
} else {
#expect(Bool(false))
}
}
@Test func scheduleEveryEncodesAndDecodesWithAnchor() throws {
let schedule = CronSchedule.every(everyMs: 5000, anchorMs: 10000)
let data = try JSONEncoder().encode(schedule)
@@ -61,11 +49,11 @@ struct CronModelsTests {
deleteAfterRun: true,
createdAtMs: 0,
updatedAtMs: 0,
schedule: .at(at: "2026-02-03T18:00:00Z"),
schedule: .at(atMs: 1_700_000_000_000),
sessionTarget: .main,
wakeMode: .now,
payload: .systemEvent(text: "ping"),
delivery: nil,
isolation: nil,
state: CronJobState())
let data = try JSONEncoder().encode(job)
let decoded = try JSONDecoder().decode(CronJob.self, from: data)
@@ -74,7 +62,7 @@ struct CronModelsTests {
@Test func scheduleDecodeRejectsUnknownKind() {
let json = """
{"kind":"wat","at":"2026-02-03T18:00:00Z"}
{"kind":"wat","atMs":1}
"""
#expect(throws: DecodingError.self) {
_ = try JSONDecoder().decode(CronSchedule.self, from: Data(json.utf8))
@@ -100,11 +88,11 @@ struct CronModelsTests {
deleteAfterRun: nil,
createdAtMs: 0,
updatedAtMs: 0,
schedule: .at(at: "2026-02-03T18:00:00Z"),
schedule: .at(atMs: 0),
sessionTarget: .main,
wakeMode: .now,
payload: .systemEvent(text: "hi"),
delivery: nil,
isolation: nil,
state: CronJobState())
#expect(base.displayName == "hello")
@@ -123,11 +111,11 @@ struct CronModelsTests {
deleteAfterRun: nil,
createdAtMs: 0,
updatedAtMs: 0,
schedule: .at(at: "2026-02-03T18:00:00Z"),
schedule: .at(atMs: 0),
sessionTarget: .main,
wakeMode: .now,
payload: .systemEvent(text: "hi"),
delivery: nil,
isolation: nil,
state: CronJobState(
nextRunAtMs: 1_700_000_000_000,
runningAtMs: nil,

View File

@@ -23,7 +23,7 @@ struct SettingsViewSmokeTests {
sessionTarget: .main,
wakeMode: .now,
payload: .systemEvent(text: "ping"),
delivery: nil,
isolation: nil,
state: CronJobState(
nextRunAtMs: 1_700_000_200_000,
runningAtMs: nil,
@@ -48,11 +48,11 @@ struct SettingsViewSmokeTests {
message: "hello",
thinking: "low",
timeoutSeconds: 30,
deliver: nil,
channel: nil,
to: nil,
bestEffortDeliver: nil),
delivery: CronDelivery(mode: .announce, channel: "sms", to: "+15551234567", bestEffort: true),
deliver: true,
channel: "sms",
to: "+15551234567",
bestEffortDeliver: true),
isolation: CronIsolation(postToMainPrefix: "[cron] "),
state: CronJobState(
nextRunAtMs: nil,
runningAtMs: nil,

View File

@@ -416,9 +416,7 @@ public actor GatewayChannelActor {
guard let self else { return }
await self.watchTicks()
}
if let pushHandler = self.pushHandler {
Task { await pushHandler(.snapshot(ok)) }
}
await self.pushHandler?(.snapshot(ok))
}
private func listen() {

View File

@@ -11,12 +11,10 @@ private struct NodeInvokeRequestPayload: Codable, Sendable {
var idempotencyKey: String?
}
public actor GatewayNodeSession {
private let logger = Logger(subsystem: "ai.openclaw", category: "node.gateway")
private let decoder = JSONDecoder()
private let encoder = JSONEncoder()
private static let defaultInvokeTimeoutMs = 30_000
private var channel: GatewayChannelActor?
private var activeURL: URL?
private var activeToken: String?
@@ -25,78 +23,34 @@ public actor GatewayNodeSession {
private var onConnected: (@Sendable () async -> Void)?
private var onDisconnected: (@Sendable (String) async -> Void)?
private var onInvoke: (@Sendable (BridgeInvokeRequest) async -> BridgeInvokeResponse)?
private var hasNotifiedConnected = false
private var snapshotReceived = false
private var snapshotWaiters: [CheckedContinuation<Bool, Never>] = []
static func invokeWithTimeout(
request: BridgeInvokeRequest,
timeoutMs: Int?,
onInvoke: @escaping @Sendable (BridgeInvokeRequest) async -> BridgeInvokeResponse
) async -> BridgeInvokeResponse {
let timeoutLogger = Logger(subsystem: "ai.openclaw", category: "node.gateway")
let timeout: Int = {
if let timeoutMs { return max(0, timeoutMs) }
return Self.defaultInvokeTimeoutMs
}()
let timeout = max(0, timeoutMs ?? 0)
guard timeout > 0 else {
return await onInvoke(request)
}
// Use an explicit latch so timeouts win even if onInvoke blocks (e.g., permission prompts).
final class InvokeLatch: @unchecked Sendable {
private let lock = NSLock()
private var continuation: CheckedContinuation<BridgeInvokeResponse, Never>?
private var resumed = false
func setContinuation(_ continuation: CheckedContinuation<BridgeInvokeResponse, Never>) {
self.lock.lock()
defer { self.lock.unlock() }
self.continuation = continuation
}
func resume(_ response: BridgeInvokeResponse) {
let cont: CheckedContinuation<BridgeInvokeResponse, Never>?
self.lock.lock()
if self.resumed {
self.lock.unlock()
return
}
self.resumed = true
cont = self.continuation
self.continuation = nil
self.lock.unlock()
cont?.resume(returning: response)
}
}
let latch = InvokeLatch()
var onInvokeTask: Task<Void, Never>?
var timeoutTask: Task<Void, Never>?
defer {
onInvokeTask?.cancel()
timeoutTask?.cancel()
}
let response = await withCheckedContinuation { (cont: CheckedContinuation<BridgeInvokeResponse, Never>) in
latch.setContinuation(cont)
onInvokeTask = Task.detached {
let result = await onInvoke(request)
latch.resume(result)
}
timeoutTask = Task.detached {
return await withTaskGroup(of: BridgeInvokeResponse.self) { group in
group.addTask { await onInvoke(request) }
group.addTask {
try? await Task.sleep(nanoseconds: UInt64(timeout) * 1_000_000)
timeoutLogger.info("node invoke timeout fired id=\(request.id, privacy: .public)")
latch.resume(BridgeInvokeResponse(
return BridgeInvokeResponse(
id: request.id,
ok: false,
error: OpenClawNodeError(
code: .unavailable,
message: "node invoke timed out")
))
)
}
let first = await group.next()!
group.cancelAll()
return first
}
timeoutLogger.info("node invoke race resolved id=\(request.id, privacy: .public) ok=\(response.ok, privacy: .public)")
return response
}
private var serverEventSubscribers: [UUID: AsyncStream<EventFrame>.Continuation] = [:]
private var canvasHostUrl: String?
@@ -124,7 +78,6 @@ public actor GatewayNodeSession {
self.onInvoke = onInvoke
if shouldReconnect {
self.resetConnectionState()
if let existing = self.channel {
await existing.shutdown()
}
@@ -154,8 +107,7 @@ public actor GatewayNodeSession {
do {
try await channel.connect()
_ = await self.waitForSnapshot(timeoutMs: 500)
await self.notifyConnectedIfNeeded()
await onConnected()
} catch {
await onDisconnected(error.localizedDescription)
throw error
@@ -168,7 +120,6 @@ public actor GatewayNodeSession {
self.activeURL = nil
self.activeToken = nil
self.activePassword = nil
self.resetConnectionState()
}
public func currentCanvasHostUrl() -> String? {
@@ -228,8 +179,7 @@ public actor GatewayNodeSession {
case let .snapshot(ok):
let raw = ok.canvashosturl?.trimmingCharacters(in: .whitespacesAndNewlines)
self.canvasHostUrl = (raw?.isEmpty == false) ? raw : nil
self.markSnapshotReceived()
await self.notifyConnectedIfNeeded()
await self.onConnected?()
case let .event(evt):
await self.handleEvent(evt)
default:
@@ -237,98 +187,28 @@ public actor GatewayNodeSession {
}
}
private func resetConnectionState() {
self.hasNotifiedConnected = false
self.snapshotReceived = false
if !self.snapshotWaiters.isEmpty {
let waiters = self.snapshotWaiters
self.snapshotWaiters.removeAll()
for waiter in waiters {
waiter.resume(returning: false)
}
}
}
private func markSnapshotReceived() {
self.snapshotReceived = true
if !self.snapshotWaiters.isEmpty {
let waiters = self.snapshotWaiters
self.snapshotWaiters.removeAll()
for waiter in waiters {
waiter.resume(returning: true)
}
}
}
private func waitForSnapshot(timeoutMs: Int) async -> Bool {
if self.snapshotReceived { return true }
let clamped = max(0, timeoutMs)
return await withCheckedContinuation { cont in
self.snapshotWaiters.append(cont)
Task { [weak self] in
guard let self else { return }
try? await Task.sleep(nanoseconds: UInt64(clamped) * 1_000_000)
await self.timeoutSnapshotWaiters()
}
}
}
private func timeoutSnapshotWaiters() {
guard !self.snapshotReceived else { return }
if !self.snapshotWaiters.isEmpty {
let waiters = self.snapshotWaiters
self.snapshotWaiters.removeAll()
for waiter in waiters {
waiter.resume(returning: false)
}
}
}
private func notifyConnectedIfNeeded() async {
guard !self.hasNotifiedConnected else { return }
self.hasNotifiedConnected = true
await self.onConnected?()
}
private func handleEvent(_ evt: EventFrame) async {
self.broadcastServerEvent(evt)
guard evt.event == "node.invoke.request" else { return }
self.logger.info("node invoke request received")
guard let payload = evt.payload else { return }
do {
let request = try self.decodeInvokeRequest(from: payload)
let timeoutLabel = request.timeoutMs.map(String.init) ?? "none"
self.logger.info("node invoke request decoded id=\(request.id, privacy: .public) command=\(request.command, privacy: .public) timeoutMs=\(timeoutLabel, privacy: .public)")
let data = try self.encoder.encode(payload)
let request = try self.decoder.decode(NodeInvokeRequestPayload.self, from: data)
guard let onInvoke else { return }
let req = BridgeInvokeRequest(id: request.id, command: request.command, paramsJSON: request.paramsJSON)
self.logger.info("node invoke executing id=\(request.id, privacy: .public)")
let response = await Self.invokeWithTimeout(
request: req,
timeoutMs: request.timeoutMs,
onInvoke: onInvoke
)
self.logger.info("node invoke completed id=\(request.id, privacy: .public) ok=\(response.ok, privacy: .public)")
await self.sendInvokeResult(request: request, response: response)
} catch {
self.logger.error("node invoke decode failed: \(error.localizedDescription, privacy: .public)")
}
}
private func decodeInvokeRequest(from payload: OpenClawProtocol.AnyCodable) throws -> NodeInvokeRequestPayload {
do {
let data = try self.encoder.encode(payload)
return try self.decoder.decode(NodeInvokeRequestPayload.self, from: data)
} catch {
if let raw = payload.value as? String, let data = raw.data(using: .utf8) {
return try self.decoder.decode(NodeInvokeRequestPayload.self, from: data)
}
throw error
}
}
private func sendInvokeResult(request: NodeInvokeRequestPayload, response: BridgeInvokeResponse) async {
guard let channel = self.channel else { return }
self.logger.info("node invoke result sending id=\(request.id, privacy: .public) ok=\(response.ok, privacy: .public)")
var params: [String: AnyCodable] = [
"id": AnyCodable(request.id),
"nodeId": AnyCodable(request.nodeId),
@@ -346,7 +226,7 @@ public actor GatewayNodeSession {
do {
try await channel.send(method: "node.invoke.result", params: params)
} catch {
self.logger.error("node invoke result failed id=\(request.id, privacy: .public) error=\(error.localizedDescription, privacy: .public)")
self.logger.error("node invoke result failed: \(error.localizedDescription, privacy: .public)")
}
}

View File

@@ -589,24 +589,20 @@ public struct AgentIdentityResult: Codable, Sendable {
public let agentid: String
public let name: String?
public let avatar: String?
public let emoji: String?
public init(
agentid: String,
name: String?,
avatar: String?,
emoji: String?
avatar: String?
) {
self.agentid = agentid
self.name = name
self.avatar = avatar
self.emoji = emoji
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case name
case avatar
case emoji
}
}
@@ -1560,157 +1556,6 @@ public struct AgentSummary: Codable, Sendable {
}
}
public struct AgentsFileEntry: Codable, Sendable {
public let name: String
public let path: String
public let missing: Bool
public let size: Int?
public let updatedatms: Int?
public let content: String?
public init(
name: String,
path: String,
missing: Bool,
size: Int?,
updatedatms: Int?,
content: String?
) {
self.name = name
self.path = path
self.missing = missing
self.size = size
self.updatedatms = updatedatms
self.content = content
}
private enum CodingKeys: String, CodingKey {
case name
case path
case missing
case size
case updatedatms = "updatedAtMs"
case content
}
}
public struct AgentsFilesListParams: Codable, Sendable {
public let agentid: String
public init(
agentid: String
) {
self.agentid = agentid
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
}
}
public struct AgentsFilesListResult: Codable, Sendable {
public let agentid: String
public let workspace: String
public let files: [AgentsFileEntry]
public init(
agentid: String,
workspace: String,
files: [AgentsFileEntry]
) {
self.agentid = agentid
self.workspace = workspace
self.files = files
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case workspace
case files
}
}
public struct AgentsFilesGetParams: Codable, Sendable {
public let agentid: String
public let name: String
public init(
agentid: String,
name: String
) {
self.agentid = agentid
self.name = name
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case name
}
}
public struct AgentsFilesGetResult: Codable, Sendable {
public let agentid: String
public let workspace: String
public let file: AgentsFileEntry
public init(
agentid: String,
workspace: String,
file: AgentsFileEntry
) {
self.agentid = agentid
self.workspace = workspace
self.file = file
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case workspace
case file
}
}
public struct AgentsFilesSetParams: Codable, Sendable {
public let agentid: String
public let name: String
public let content: String
public init(
agentid: String,
name: String,
content: String
) {
self.agentid = agentid
self.name = name
self.content = content
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case name
case content
}
}
public struct AgentsFilesSetResult: Codable, Sendable {
public let ok: Bool
public let agentid: String
public let workspace: String
public let file: AgentsFileEntry
public init(
ok: Bool,
agentid: String,
workspace: String,
file: AgentsFileEntry
) {
self.ok = ok
self.agentid = agentid
self.workspace = workspace
self.file = file
}
private enum CodingKeys: String, CodingKey {
case ok
case agentid = "agentId"
case workspace
case file
}
}
public struct AgentsListParams: Codable, Sendable {
}
@@ -1785,16 +1630,6 @@ public struct ModelsListResult: Codable, Sendable {
}
public struct SkillsStatusParams: Codable, Sendable {
public let agentid: String?
public init(
agentid: String?
) {
self.agentid = agentid
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
}
}
public struct SkillsBinsParams: Codable, Sendable {
@@ -1872,7 +1707,7 @@ public struct CronJob: Codable, Sendable {
public let sessiontarget: AnyCodable
public let wakemode: AnyCodable
public let payload: AnyCodable
public let delivery: [String: AnyCodable]?
public let isolation: [String: AnyCodable]?
public let state: [String: AnyCodable]
public init(
@@ -1888,7 +1723,7 @@ public struct CronJob: Codable, Sendable {
sessiontarget: AnyCodable,
wakemode: AnyCodable,
payload: AnyCodable,
delivery: [String: AnyCodable]?,
isolation: [String: AnyCodable]?,
state: [String: AnyCodable]
) {
self.id = id
@@ -1903,7 +1738,7 @@ public struct CronJob: Codable, Sendable {
self.sessiontarget = sessiontarget
self.wakemode = wakemode
self.payload = payload
self.delivery = delivery
self.isolation = isolation
self.state = state
}
private enum CodingKeys: String, CodingKey {
@@ -1919,7 +1754,7 @@ public struct CronJob: Codable, Sendable {
case sessiontarget = "sessionTarget"
case wakemode = "wakeMode"
case payload
case delivery
case isolation
case state
}
}
@@ -1950,7 +1785,7 @@ public struct CronAddParams: Codable, Sendable {
public let sessiontarget: AnyCodable
public let wakemode: AnyCodable
public let payload: AnyCodable
public let delivery: [String: AnyCodable]?
public let isolation: [String: AnyCodable]?
public init(
name: String,
@@ -1962,7 +1797,7 @@ public struct CronAddParams: Codable, Sendable {
sessiontarget: AnyCodable,
wakemode: AnyCodable,
payload: AnyCodable,
delivery: [String: AnyCodable]?
isolation: [String: AnyCodable]?
) {
self.name = name
self.agentid = agentid
@@ -1973,7 +1808,7 @@ public struct CronAddParams: Codable, Sendable {
self.sessiontarget = sessiontarget
self.wakemode = wakemode
self.payload = payload
self.delivery = delivery
self.isolation = isolation
}
private enum CodingKeys: String, CodingKey {
case name
@@ -1985,7 +1820,7 @@ public struct CronAddParams: Codable, Sendable {
case sessiontarget = "sessionTarget"
case wakemode = "wakeMode"
case payload
case delivery
case isolation
}
}

View File

@@ -32,7 +32,6 @@ services:
environment:
HOME: /home/node
TERM: xterm-256color
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
BROWSER: echo
CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY}
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY}

View File

@@ -191,12 +191,12 @@ docker compose "${COMPOSE_ARGS[@]}" run --rm openclaw-cli onboard --no-install-d
echo ""
echo "==> Provider setup (optional)"
echo "WhatsApp (QR):"
echo " ${COMPOSE_HINT} run --rm openclaw-cli channels login"
echo " ${COMPOSE_HINT} run --rm openclaw-cli providers login"
echo "Telegram (bot token):"
echo " ${COMPOSE_HINT} run --rm openclaw-cli channels add --channel telegram --token <token>"
echo " ${COMPOSE_HINT} run --rm openclaw-cli providers add --provider telegram --token <token>"
echo "Discord (bot token):"
echo " ${COMPOSE_HINT} run --rm openclaw-cli channels add --channel discord --token <token>"
echo "Docs: https://docs.openclaw.ai/channels"
echo " ${COMPOSE_HINT} run --rm openclaw-cli providers add --provider discord --token <token>"
echo "Docs: https://docs.openclaw.ai/providers"
echo ""
echo "==> Starting gateway"

View File

@@ -5,116 +5,12 @@
},
{
"source": "Gateway",
"target": "Gateway 网关"
"target": "Gateway"
},
{
"source": "Pi",
"target": "Pi"
},
{
"source": "Skills",
"target": "Skills"
},
{
"source": "Skills config",
"target": "Skills 配置"
},
{
"source": "Skills Config",
"target": "Skills 配置"
},
{
"source": "local loopback",
"target": "local loopback"
},
{
"source": "Tailscale",
"target": "Tailscale"
},
{
"source": "Getting Started",
"target": "入门指南"
},
{
"source": "Getting started",
"target": "入门指南"
},
{
"source": "DMs",
"target": "私信"
},
{
"source": "DM",
"target": "私信"
},
{
"source": "sandbox",
"target": "沙箱"
},
{
"source": "Sandbox",
"target": "沙箱"
},
{
"source": "sandboxing",
"target": "沙箱隔离"
},
{
"source": "Sandboxing",
"target": "沙箱隔离"
},
{
"source": "sandboxed",
"target": "沙箱隔离"
},
{
"source": "Sandboxed",
"target": "沙箱隔离"
},
{
"source": "Sandboxing note",
"target": "沙箱注意事项"
},
{
"source": "Companion apps",
"target": "配套应用"
},
{
"source": "expected keys",
"target": "预期键名"
},
{
"source": "block streaming",
"target": "分块流式传输"
},
{
"source": "Block streaming",
"target": "分块流式传输"
},
{
"source": "Discovery + transports",
"target": "设备发现 + 传输协议"
},
{
"source": "Discovery",
"target": "设备发现"
},
{
"source": "Network model",
"target": "网络模型"
},
{
"source": "for full details",
"target": "了解详情"
},
{
"source": "First 60 seconds",
"target": "最初的六十秒"
},
{
"source": "Auth: where it lives (important)",
"target": "凭证:存储位置(重要)"
},
{
"source": "agent",
"target": "智能体"
@@ -149,7 +45,7 @@
},
{
"source": "get unstuck",
"target": "解决问题"
"target": "快速排障"
},
{
"source": "troubleshooting",
@@ -161,11 +57,7 @@
},
{
"source": "onboarding",
"target": "手引导"
},
{
"source": "Onboarding",
"target": "新手引导"
"target": "手引导"
},
{
"source": "wizard",

View File

@@ -3,10 +3,13 @@
{"cache_key":"00ee1ece05b05ab7b12cfe673000c037bb2037fe93a069a71ec2368184e83944","segment_id":"index.md:45e6d69dbe995a36","source_path":"index.md","text_hash":"45e6d69dbe995a36f7bc20755eff4eb4d2afaaedbcac4668ab62540c57219f32","text":"macOS app","translated":"macOS 应用","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:05:06Z"}
{"cache_key":"00eeb87b1774979860c4b016d48e416ab9157539c41f5f3f0c58c1deb8f075c9","segment_id":"environment.md:frontmatter:read_when:2","source_path":"environment.md:frontmatter:read_when:2","text_hash":"822b3d74ce16c1be19059fad4ca5bf7ae9327f58fa1ff4e75e78d5afa75c038f","text":"You are documenting provider auth or deployment environments","translated":"你正在记录提供商认证或部署环境的相关文档","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:15:51Z"}
{"cache_key":"01063749652c55481b7da485911a80de3049ded0257874b376efbc55a14293a7","segment_id":"start/wizard.md:037b8f564390e097","source_path":"start/wizard.md","text_hash":"037b8f564390e09742421c621a1f785d2ee5338d0c680c76f7a9b991518e909d","text":" and optional ","translated":" 和可选的 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:48:48Z"}
{"cache_key":"011732db491eea64ff252f1a211df0eee3edbf29b3839a36468aff0d600565a8","segment_id":"index.md:58d30d963f28264b","source_path":"index.md","text_hash":"58d30d963f28264bd9ba0e2d4c07c2c43c0ac1c1609c25b3fccf475eebf41727","text":"Skills config","translated":"技能配置","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:52:44Z"}
{"cache_key":"01814fd9d09399c075081056c6fa2befa388c67ba4f8745122804fd044fd82d6","segment_id":"start/getting-started.md:d1564fd156e28160","source_path":"start/getting-started.md","text_hash":"d1564fd156e28160c83922ad7a18428ce2c966e790f477e740d1d9f6cadd51e9","text":"WhatsApp (QR login)","translated":"WhatsApp二维码登录","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:37:08Z"}
{"cache_key":"019f9aef85c71dc5ed35acd441246cf7ca7e8734347c659aff797b91a593805e","segment_id":"index.md:22159a426e4f2635","source_path":"index.md","text_hash":"22159a426e4f26356382cc3ac9b2e7af5123c1309250332f5dcbbc6e6f952b0e","text":"Network model","translated":"网络 模型","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:48:28Z"}
{"cache_key":"01b87576d7ade6b91ca28935f65c167c2f4fb5d1b6bfd1189fd416b229500af4","segment_id":"start/getting-started.md:7421b911bc203f6f","source_path":"start/getting-started.md","text_hash":"7421b911bc203f6fe3c677d752379f23dc314719d39d18179406da675f58d039","text":"Scan via WhatsApp → Settings → Linked Devices.","translated":"通过 WhatsApp → 设置 → 已关联设备 进行扫描。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:37:10Z"}
{"cache_key":"01d8d8ec84ad8f4c74e29e254e56c02f7d75005160c27d99e9ce183767e16c55","segment_id":"index.md:6b8ebac7903757ce","source_path":"index.md","text_hash":"6b8ebac7903757ce7399cc729651a27e459903c24c64aa94827b20d8a2a411d2","text":"For Tailnet access, run ","translated":"如需 Tailnet 访问,请运行 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:00:08Z"}
{"cache_key":"024efbb5ac15e07c191effa78c0b23bf173c8af6725e988743ea055e9a4e8c3b","segment_id":"index.md:f9b8279bc46e847b","source_path":"index.md","text_hash":"f9b8279bc46e847bfcc47b8701fd5c5dc27baa304d5add8278a7f97925c3ec13","text":"Mattermost (plugin)","translated":"Mattermost插件","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:40Z"}
{"cache_key":"025574196252a6e36b88a27c440de11b7f0e0d981df3595a0aefda198b2cde9c","segment_id":"index.md:4d705f0fa835fd21","source_path":"index.md","text_hash":"4d705f0fa835fd216c4fd6dea0ee851d33720e23fb714c4c9ea74ac3211fccdc","text":"Discovery + transports","translated":"发现 + 传输","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:02Z"}
{"cache_key":"02d1e10492e8721462f16e39467b94ad3197e4eb76f6d671a09b4246d5b4d27b","segment_id":"start/getting-started.md:7ac362063b9f2046","source_path":"start/getting-started.md","text_hash":"7ac362063b9f204602f38f9f1ec9cf047f03e0d7b83896571c9df6d31ad41e9c","text":"Nodes","translated":"节点","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:38:28Z"}
{"cache_key":"02f39c075115bee6bdb015a49436f2b2a56365b87558fdd7aff7b17cb83bff6c","segment_id":"environment.md:frontmatter:summary","source_path":"environment.md:frontmatter:summary","text_hash":"78351223e7068721146d2de022fdf440c2866b2ee02fbbb50bf64369b999820b","text":"Where OpenClaw loads environment variables and the precedence order","translated":"OpenClaw 加载环境变量的位置及优先级顺序","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:19:12Z"}
{"cache_key":"02f4067265058ed8929f3772d87e1c5dc0af8422b8e7b513b7db155108a422c3","segment_id":"start/wizard.md:961eb43699731759","source_path":"start/wizard.md","text_hash":"961eb43699731759fd0d04f177bb24f09971bddd41426702276e761269d0a5b9","text":" does ","translated":" 会 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:40:48Z"}
@@ -16,8 +19,13 @@
{"cache_key":"0457a19cd3a82171f6cdb92d82d5a0f6358da4c1220d42d5b0575bde871e7f91","segment_id":"environment.md:e234227b0e001687","source_path":"environment.md","text_hash":"e234227b0e001687821541fac3af38fc6be293ec6e13910c6826b9afc8ca33be","text":" syntax:","translated":" 语法:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:13:00Z"}
{"cache_key":"045fb6f3989827561e347dfa56a164069bf8b7afaa50d2d02c20ad264495d351","segment_id":"index.md:e9f63c8876aec738","source_path":"index.md","text_hash":"e9f63c8876aec7381ffb5a68efb39f50525f9fc4e732857488561516d47f5654","text":" — Uses Baileys for WhatsApp Web protocol","translated":" — 使用 Baileys 实现 WhatsApp Web 协议","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:29:31Z"}
{"cache_key":"046c83b658b7dd8bce829f07bd09dcee3413753ab72cf95d638925aa163d3486","segment_id":"start/getting-started.md:f4117324994aaad1","source_path":"start/getting-started.md","text_hash":"f4117324994aaad1d3413064ade8f2037e43ab2fac0b385d731ff154925ec3b3","text":"Windows (PowerShell):","translated":"Windows (PowerShell)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:35:51Z"}
{"cache_key":"04b1191bfbfc3062975be3fbc5b169b9c3151d3fbce07bfffc05483c40191c76","segment_id":"environment.md:28e19c6e69c7a2aa","source_path":"environment.md","text_hash":"28e19c6e69c7a2aa071951dda3ff0a11ca178e3fb295dae8d6ed7dcc994434a4","text":" for full details.","translated":" 了解完整详情。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:13:07Z"}
{"cache_key":"04d48cfdb6b444cb4691ea55a5deb23df20694659ae1bc5e082e242e749f5e3c","segment_id":"help/index.md:bfc5930cc2660330","source_path":"help/index.md","text_hash":"bfc5930cc2660330260afd407e98d86adaec0af48dd72b88dc33ef8e9066e2c9","text":"Install sanity (Node/npm/PATH):","translated":"安装完整性检查Node/npm/PATH","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:39:38Z"}
{"cache_key":"04fee8dc5ef25d6bc83852bc30abc64dab335a974f1a9aa3528d0a463f3df80e","segment_id":"environment.md:a42cc4a7174c83a8","source_path":"environment.md","text_hash":"a42cc4a7174c83a853752b3e74cb001a234f3eca099688fdf0dd2540c60bb1e2","text":" expected keys:","translated":" 预期密钥:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:16:53Z"}
{"cache_key":"05405710e256b2e1031234be855a7c11cf1505c627df14884d655fa42a1568a7","segment_id":"index.md:f0a7f9d068cb7a14","source_path":"index.md","text_hash":"f0a7f9d068cb7a146d0bb89b3703688d690ed0b92734b78bcdb909aace617dbf","text":"WhatsApp group messages","translated":"WhatsApp 群组消息","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:45Z"}
{"cache_key":"060d0a2c79a17edab07399082756201b03bbc948813e274fd902e138a7188268","segment_id":"start/getting-started.md:9bb7dee21b23322b","source_path":"start/getting-started.md","text_hash":"9bb7dee21b23322b15ce4a4400e6fe70a582d3d15f7e61f2c4cdf68654de1f09","text":" is also supported if you want to reuse Claude Code credentials.","translated":" 如果您想复用 Claude Code 凭据,也受支持。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:35Z"}
{"cache_key":"0644dfe5ea449a35c3a87047a17fb132ee1ef58000d49c1849006ad247310f90","segment_id":"index.md:f14185309c5ab262","source_path":"index.md","text_hash":"f14185309c5ab26233fde49831f9fc27857a6e7ac200e91dc247ae3e3b74be27","text":"Companion apps:","translated":"伴侣应用:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:05:03Z"}
{"cache_key":"06489e87ae62a43327838658fac6a96383f6c84a0f1e59319d89b2ce6a6f34b9","segment_id":"environment.md:e4255aa4e8f9e525","source_path":"environment.md","text_hash":"e4255aa4e8f9e52571c9bc93336d0774bcd7f017b7b5297fb33b8e1986166f92","text":"), applied only for missing expected keys.","translated":"),仅对缺失的预期密钥应用。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:57:57Z"}
{"cache_key":"064dcdb5051313b748c0b775ec69683149e1861d84fa47a74c68ddd8086bdebc","segment_id":"index.md:81a1c0449ea684aa","source_path":"index.md","text_hash":"81a1c0449ea684aadad54a7f8575061ddc5bfa713b6ca3eb8a0228843d2a3ea1","text":"Nodes (iOS/Android)","translated":"节点iOS/Android","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:52:57Z"}
{"cache_key":"0687549a28e71ec1e17b261001a9e818e27784ce3286b7d21e856e37c07915a6","segment_id":"start/getting-started.md:bad5d156dc5e0cd3","source_path":"start/getting-started.md","text_hash":"bad5d156dc5e0cd39db3a90645cd150e846743103f3acfa5182ad5a003a172dc","text":"0) Prereqs","translated":"0前提条件","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:35:23Z"}
{"cache_key":"06c13f0dfc6cd5fa142e329fd2cfb2538e19e33de83c4b9d366542f0d03cdf08","segment_id":"index.md:c3af076f92c5ed8d","source_path":"index.md","text_hash":"c3af076f92c5ed8dcb0d0b0d36dd120bc31b68264efea96cf8019ca19f1c13a3","text":"Troubleshooting","translated":"故障排除","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:33:14Z"}
@@ -32,6 +40,7 @@
{"cache_key":"08a071c1e71388ad18ffca39565a37edb304794146d2f7ea1e2bac93493f89d6","segment_id":"start/wizard.md:903ea1cf1f2831b3","source_path":"start/wizard.md","text_hash":"903ea1cf1f2831b3e836aff6e23c7d261a83381614361e65df16ade48e84b26c","text":" (API keys + OAuth).","translated":" API 密钥 + OAuth。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:43:34Z"}
{"cache_key":"08b4ff7a8e04409d740ca4090c8d83bc3b05d7084bce4b83fa4c91b930eb7161","segment_id":"environment.md:62d66b8c36a6c9aa","source_path":"environment.md","text_hash":"62d66b8c36a6c9aa7134c8f9fe5912435cb0b3bfce3172712646a187954e7040","text":"See [Configuration: Env var substitution](/gateway/configuration#env-var-substitution-in-config) for full details.","translated":"详见 [配置:环境变量替换](/gateway/configuration#env-var-substitution-in-config)。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:11:58Z"}
{"cache_key":"08f97e3d7baa10a515db441b79273f697f85c83da040cdf821f9e725243112f2","segment_id":"environment.md:f6b2ffe1d0d5f521","source_path":"environment.md","text_hash":"f6b2ffe1d0d5f521b76cabc67d6e96da2b1170eef8086d530558e9906a7f092d","text":"Models overview","translated":"模型概览","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:17:17Z"}
{"cache_key":"09057abbc867280c96888e1b1eb5d35e4f5b3175c0c5fca9900f147e577fb4b7","segment_id":"index.md:80fc402133201fbe","source_path":"index.md","text_hash":"80fc402133201fbe0e4e9962a9570e741856aa8b0c033f1a20a9bcb06c68e809","text":"Discovery","translated":"发现机制","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:29:24Z"}
{"cache_key":"090f33f5db1fde14d7fc04aaa9febae674e9e6ed0d04ce8f1813dac53ccae3a2","segment_id":"start/wizard.md:ab4386608f0ebc6e","source_path":"start/wizard.md","text_hash":"ab4386608f0ebc6e151eab042c6de71d09863aab6dcb2551665e34210e4a4439","text":"What youll set:","translated":"您需要设置的内容:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:46:15Z"}
{"cache_key":"09824fcf1352f54ff162268163b8670ead0660d4e0a45d1f236b5b3ef938a56b","segment_id":"index.md:86e2bbbc305c31aa","source_path":"index.md","text_hash":"86e2bbbc305c31aa988751196a1e207da68801a48798c48b90485c11578443a0","text":"Providers and UX:","translated":"提供商 和用户体验:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:04:39Z"}
{"cache_key":"0a2b53b4943a0ba87fb991fef20f822df6c2fd0584f88d394de35b081daac564","segment_id":"environment.md:668e5590b5bb9990","source_path":"environment.md","text_hash":"668e5590b5bb9990eeb25bf657f7d17281a4c613ee4442036787cd4b2efd22bb","text":"If the config file is missing entirely, step 4 is skipped; shell import still runs if enabled.","translated":"如果配置文件完全缺失,则跳过第 4 步如果已启用shell 导入仍会运行。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:58:00Z"}
@@ -43,6 +52,7 @@
{"cache_key":"0aaaa653a1bad3c2f1d6bbf34819ea4ae8700ea5d6c593937aa6812051809168","segment_id":"environment.md:453c14128fbfb5f6","source_path":"environment.md","text_hash":"453c14128fbfb5f6757511557132a1dbb3bcbf243267630bfec49db8518c7780","text":"Env var substitution in config","translated":"配置中的 环境变量 替换","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:58:16Z"}
{"cache_key":"0b149311bd258e33ab5e06f16483d6b14bfb23bbf8137339bc4cf8d29e2d3d5c","segment_id":"environment.md:453c14128fbfb5f6","source_path":"environment.md","text_hash":"453c14128fbfb5f6757511557132a1dbb3bcbf243267630bfec49db8518c7780","text":"Env var substitution in config","translated":"配置中的环境变量替换","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:11:54Z"}
{"cache_key":"0b68a76b412628864a90e4b194a0db6bcc593e8700ee9228d04b45427a95c7af","segment_id":"environment.md:cf3f9ba035da9f09","source_path":"environment.md","text_hash":"cf3f9ba035da9f09202ba669adca3109148811ef31d484cc2efa1ff50a1621b1","text":" (what the Gateway process already has from the parent shell/daemon).","translated":" Gateway 进程从父 shell/守护进程继承的已有环境变量)。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:57:23Z"}
{"cache_key":"0b6b7380e75d36476a24a56bb3825600832745e76c1a2d862e6631c0aa48c51e","segment_id":"index.md:41dc1288a547d7d1","source_path":"index.md","text_hash":"41dc1288a547d7d155c2d7b831e8cff388e12ab9d77d4c24cd0757ed47e9e209","text":" — Block streaming + Telegram draft streaming details (","translated":" — 块流式传输 + Telegram 草稿流式传输详情(","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:01:36Z"}
{"cache_key":"0bab5344d37eb10f7f0a1105ba4cf723e069867a7f745d016657752c1dc0c21a","segment_id":"environment.md:5105555b1be5f84b","source_path":"environment.md","text_hash":"5105555b1be5f84b47576d6ea432675cef742e63fa52f7b254ef2aa4c90e7cca","text":" (applied only if","translated":" (仅在","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T11:46:04Z"}
{"cache_key":"0bbc0779389fa7b103e39fff721c2df8f37e36a72350175e61b8334f79dd6555","segment_id":"index.md:0b7e778664921066","source_path":"index.md","text_hash":"0b7e77866492106632e98e7718a8e1e89e8cb0ee3f44c1572dfd9e54845023de","text":"/concepts/streaming","translated":"/concepts/streaming","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:30:06Z"}
{"cache_key":"0bda3d8fa9978471f16800fbab17622f054477505f8a680d6165e924184818eb","segment_id":"index.md:3fc5f55ea5862824","source_path":"index.md","text_hash":"3fc5f55ea5862824fc266d26cd39fb5da22cc56670c11905d5743adac10bc9ef","text":"Mattermost Bot (plugin)","translated":"Mattermost 机器人(插件)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:29:46Z"}
@@ -77,6 +87,7 @@
{"cache_key":"11951539669d912b24dac16f9ed27e1de0a950a3baa481474a65de0ca85fbe7b","segment_id":"start/wizard.md:ec2d0a7d20f3b660","source_path":"start/wizard.md","text_hash":"ec2d0a7d20f3b6602a6593e0abef2337e84ba728ca8f6fef2534dc1e9dbfe06b","text":"Remote mode configures a local client to connect to a Gateway elsewhere.","translated":"远程模式配置本地客户端以连接到其他位置的 Gateway。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:46:13Z"}
{"cache_key":"11a42ddb57b9c1ba4022984efe25b463da52e7b9c5d7ec3a925d7a6d0e5a6c39","segment_id":"index.md:cdb4ee2aea69cc6a","source_path":"index.md","text_hash":"cdb4ee2aea69cc6a83331bbe96dc2caa9a299d21329efb0336fc02a82e1839a8","text":".","translated":".","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:28:19Z"}
{"cache_key":"11a6809809867ab84f2a66da213f7894876530602a0743b37fc93e614c7ccbfe","segment_id":"help/index.md:71095a6d42f5d9c2","source_path":"help/index.md","text_hash":"71095a6d42f5d9c2464a8e3f231fc53636d4ce0f9356b645d245874162ec07e2","text":"Gateway troubleshooting","translated":"Gateway 故障排除","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:39:44Z"}
{"cache_key":"11e66a0f11d149ca8994761cbc3771066650e21d33cb9986d47624a35fb5f177","segment_id":"help/index.md:5c94724fa7810fa9","source_path":"help/index.md","text_hash":"5c94724fa7810fa9902e565cf66c5f5a973074f2961fcd3a40bad4ee4aeca5e0","text":"If you want a quick “get unstuck” flow, start here:","translated":"如果你想快速\"脱困\",从这里开始:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:21:37Z"}
{"cache_key":"1226fe0b47712f49a01581113142855bc5ae36e3289353b5d592ece5191b0159","segment_id":"start/wizard.md:c90e6f2be18d7e02","source_path":"start/wizard.md","text_hash":"c90e6f2be18d7e02413e18d4174fe7d855c9753005652614556204123b37c96e","text":": browser flow; paste the ","translated":":浏览器流程;粘贴 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:42:18Z"}
{"cache_key":"1249a5c279b0761418bca0826571d62b0526075a0c91018c35002331e3c6d6b5","segment_id":"environment.md:aac7246f5e97142c","source_path":"environment.md","text_hash":"aac7246f5e97142c3f257b7d8b84976f10c29e1b89804bb9d3eb7c43cc03cb8e","text":"Environment variables","translated":"环境变量","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:25:14Z"}
{"cache_key":"124e4ad52161941e1842f43e4f5d0c12d573babaf3f319ec7d5db46ba8ee7e84","segment_id":"index.md:0b60fe04b3c5c3c7","source_path":"index.md","text_hash":"0b60fe04b3c5c3c76371b6eca8b19c8e09a0e54c9010711ff87e782d87d2190b","text":"Android app","translated":"Android 应用","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:57Z"}
@@ -104,6 +115,7 @@
{"cache_key":"18bd8d592ca11411d1c02c1a70123dc798352f581db4c9ce297c5ebb04841fa3","segment_id":"index.md:03279877bfe1de07","source_path":"index.md","text_hash":"03279877bfe1de0766393b51e69853dec7e95c287ef887d65d91c8bbe84ff9ff","text":"WebChat + macOS app","translated":"网页聊天 + macOS 应用","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:02:30Z"}
{"cache_key":"190c49164ee5535fac803e9c0f057588d634e056d2c4fc072a0ca26e01ddc391","segment_id":"index.md:7d8b3819c6a9fb72","source_path":"index.md","text_hash":"7d8b3819c6a9fb726f40c191f606079b473f6f72d4080c13bf3b99063a736187","text":"Ops and safety:","translated":"运维和安全:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:05:19Z"}
{"cache_key":"19207e4ed0ae44f965f33707377a0217c1765cf57b09c0268ee36c10fb108dd9","segment_id":"index.md:c6e91f3b51641b1c","source_path":"index.md","text_hash":"c6e91f3b51641b1c43d297281ee782b40d9b3a0bdd7afc144ba86ba329d5f95f","text":"OpenClaw = CLAW + TARDIS","translated":"OpenClaw = CLAW + TARDIS","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:33:18Z"}
{"cache_key":"19429bac6dc1b8ea7457c6d7eb4bcf0f89cef2a5b2a017e79a0ed5d093e1665a","segment_id":"start/getting-started.md:6b65292dc52408c1","source_path":"start/getting-started.md","text_hash":"6b65292dc52408c15bb07aa90735e215262df697d1a7bd2d907c9d1ff294ed5e","text":"If you dont have a global install yet, run the onboarding step via ","translated":"如果您尚未进行全局安装,请通过以下方式运行上手引导步骤 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:37:47Z"}
{"cache_key":"194e63ecfe45556973c28ccafc39f814f42d2478037734ce44eee72f6fc6fc66","segment_id":"index.md:856302569e24c4d6","source_path":"index.md","text_hash":"856302569e24c4d64997e2ec5c37729f852bcccf333ba1e2f71e189c9d172e6d","text":": SSH tunnel or tailnet/VPN; see ","translated":"SSH 隧道或 Tailnet/VPN请参阅 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:29:21Z"}
{"cache_key":"196942db05e9e40cbdf74a89cdd1be042430343a64ac2185009414f0d092af66","segment_id":"environment.md:cda454f61dfcac70","source_path":"environment.md","text_hash":"cda454f61dfcac7007a9edc538f9f58cf38caa0652e253975979308162bccc53","text":"Gateway configuration","translated":"Gateway 配置","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:13:11Z"}
{"cache_key":"19c0ced45bb35a1d8801864910a9f7bc2c460229fdd97366f546255feeb1db0e","segment_id":"index.md:8816c52bc5877a2b","source_path":"index.md","text_hash":"8816c52bc5877a2b24e3a2f4ae7313d29cf4eba0ca568a36f2d00616cfe721d0","text":"Wizard","translated":"向导","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:59:12Z"}
@@ -150,8 +162,10 @@
{"cache_key":"221e7c2c0fe8b9bb39aa23d66ead440852512864ee62242cc3d9290dbd135860","segment_id":"index.md:9bd86b0bbc71de88","source_path":"index.md","text_hash":"9bd86b0bbc71de88337aa8ca00f0365c1333c43613b77aaa46394c431cb9afd8","text":"Maxim Vovshin","translated":"Maxim Vovshin","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:33:49Z"}
{"cache_key":"2220f5ebb94a086ce480f01165b1993d04e470d58154e2aa482056a2eecbb1f1","segment_id":"help/index.md:3c33340bd23b8db8","source_path":"help/index.md","text_hash":"3c33340bd23b8db89f18fe7d05a954738c0dd5ba9623cf6bdb7bb5d1a3729cfc","text":"FAQ (concepts)","translated":"常见问题(概念)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:24:59Z"}
{"cache_key":"2229ff2bff7c65fc1a4cd5515373b1b3319f43a26222f43787452e985cf5e4bb","segment_id":"index.md:11d28de5b79e3973","source_path":"index.md","text_hash":"11d28de5b79e3973f6a3e44d08725cdd5852e3e65e2ff188f6708ae9ce776afc","text":"Docs hubs (all pages linked)","translated":"文档中心(所有页面链接)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:31:49Z"}
{"cache_key":"228b4027bfc7ab84d118c7534132c84e4135f86c319e047f014d862beb938c26","segment_id":"environment.md:a42cc4a7174c83a8","source_path":"environment.md","text_hash":"a42cc4a7174c83a853752b3e74cb001a234f3eca099688fdf0dd2540c60bb1e2","text":" expected keys:","translated":" 预期密钥:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:26:16Z"}
{"cache_key":"22baac03ae69320ee9635f7e23e85e926ed40c441e97357b30b48e271e88770f","segment_id":"index.md:013e11a23ec9833f","source_path":"index.md","text_hash":"013e11a23ec9833f907b2ead492b0949015e25d10ba92461669609aee559335d","text":"Start here:","translated":"从这里开始:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:31:47Z"}
{"cache_key":"22bfdd3e9e4f7a5447edf31592e38d663a8907afca5f46061f314b924280a94b","segment_id":"index.md:d53b75d922286041","source_path":"index.md","text_hash":"d53b75d9222860417f783b0829023b450905d982011d35f0e71de8eed93d90fc","text":"New install from zero:","translated":"从零开始全新安装:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:47:41Z"}
{"cache_key":"22c699e5178ceeaa86c9029a62d9e0cea3b3c6ff75e19666d912f28097ecca91","segment_id":"index.md:5eeecff4ba2df15c","source_path":"index.md","text_hash":"5eeecff4ba2df15c51bcc1ba70a5a2198fbcac141ebe047a2db7acf0e1e83450","text":" — Local UI + menu bar companion for ops and voice wake","translated":" —— 本地界面 + 菜单栏伴侣应用,用于操作和语音唤醒","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:51:05Z"}
{"cache_key":"22c7a06691f087acabe4321804edbb000eaf7520b16060ac2879f19252b639e3","segment_id":"index.md:31365ab9453d6a1e","source_path":"index.md","text_hash":"31365ab9453d6a1ec03731622803d3b44f345b6afad08040d7f3e97290c77913","text":"do nothing","translated":"不做任何操作","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:31:33Z"}
{"cache_key":"22d40e91dde10d2912781df931ab0fac2802d5b81e63fdd93bdb7856c8c43976","segment_id":"environment.md:7175517a370b5cd2","source_path":"environment.md","text_hash":"7175517a370b5cd2e664e3fd29c4ea9db5ce17058eb9772fe090a5485e49dad6","text":" or ","translated":" 或 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:26:00Z"}
{"cache_key":"23004dacbc322d02e170261429793a8b23569f398c4f21352a030b42543cdef9","segment_id":"index.md:6b65292dc52408c1","source_path":"index.md","text_hash":"6b65292dc52408c15bb07aa90735e215262df697d1a7bd2d907c9d1ff294ed5e","text":"If you dont have a global install yet, run the onboarding step via ","translated":"如果您还没有全局安装,请通过以下方式运行 上手引导 步骤 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:51:37Z"}
@@ -474,6 +488,7 @@
{"cache_key":"60cd1a8fee21c221c625fe6961c620592e9f99a88910d9f557d86f92e17d793c","segment_id":"start/wizard.md:1d6bc09c9a9a3dad","source_path":"start/wizard.md","text_hash":"1d6bc09c9a9a3dad8fcbe9ed89a206b2dba3d8cf16046315aee976577d534cae","text":"Downloads the appropriate release asset.","translated":"下载相应的发布资源。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:47:59Z"}
{"cache_key":"60f998f050fe63afd0938f40b2f1cf78a16d5dd9fa6abc631aa8e217ce1e7cc5","segment_id":"index.md:053bc65874ad6098","source_path":"index.md","text_hash":"053bc65874ad6098e58c41c57b378a2f36b0220e5e0b46722245e6c2f796818c","text":"Discord","translated":"Discord","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:15Z"}
{"cache_key":"61277a40a0e409e2f324452a28cc35c44e1ac080b4400e7bdaa3c161ce51d545","segment_id":"start/wizard.md:3fcf806de5c2ace5","source_path":"start/wizard.md","text_hash":"3fcf806de5c2ace5327f65078cfb2139aaa8dd33ffdc3b04e9fef6f11778423c","text":"MiniMax M2.1","translated":"MiniMax M2.1","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:42:55Z"}
{"cache_key":"6131873fe6c607965685280107b0527c8bda0c8c322154c415c74adf0b2d6aea","segment_id":"environment.md:cf0923bd0c80e86a","source_path":"environment.md","text_hash":"cf0923bd0c80e86a7aa644d04aa412cbd7baa3273153c40c625ceca9e012bde8","text":" runs your login shell and imports only **missing** expected keys:","translated":" 运行你的登录 shell 并仅导入**缺失的**预期密钥:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:19:37Z"}
{"cache_key":"613744b9849b1cacbbcdcebd3fcb2637696f177d0364b9e32042a74bf2c1b350","segment_id":"index.md:80fc402133201fbe","source_path":"index.md","text_hash":"80fc402133201fbe0e4e9962a9570e741856aa8b0c033f1a20a9bcb06c68e809","text":"Discovery","translated":"发现","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:49:20Z"}
{"cache_key":"613d01b2aa6e9a9127f428233d5f88e84e2c86b5079776f57becfe4143f86992","segment_id":"start/wizard.md:3ccbb3a92014470f","source_path":"start/wizard.md","text_hash":"3ccbb3a92014470f73c71c81684da45b1e07ee3a49cca372ec678ce89229ea58","text":"Vercel AI Gateway example:","translated":"Vercel AI Gateway 示例:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:47:26Z"}
{"cache_key":"614a1ff5ae5f98f2f46f1ee6bbb53ace3482d9d15a8842906f26dcbad10c4d71","segment_id":"index.md:084514e91f37c3ce","source_path":"index.md","text_hash":"084514e91f37c3ce85360e26c70b77fdc95f0d3551ce309db96fbcf956a53b01","text":"Dashboard (browser Control UI)","translated":"仪表板(浏览器控制界面)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:59:30Z"}
@@ -544,6 +559,7 @@
{"cache_key":"6b14f5e839df1e54026ee6d3db5886a6e9360039fd681101a4a9a2b101ff0919","segment_id":"index.md:084514e91f37c3ce","source_path":"index.md","text_hash":"084514e91f37c3ce85360e26c70b77fdc95f0d3551ce309db96fbcf956a53b01","text":"Dashboard (browser Control UI)","translated":"仪表盘(浏览器控制界面)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:28:21Z"}
{"cache_key":"6b3dbfa396df75c279946f5b8741a67863a0107d3f08c55dc642a8fac173a4c8","segment_id":"index.md:1074116f823ec992","source_path":"index.md","text_hash":"1074116f823ec992e76d7e8be19d3235fec5ddd7020562b06e7242e410174686","text":"Remote use","translated":"远程使用","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:49:11Z"}
{"cache_key":"6b44e5cb8d21527ef6ad754e2792b9416080f2a132c8fd7b6d431fc76113aad9","segment_id":"environment.md:a42cc4a7174c83a8","source_path":"environment.md","text_hash":"a42cc4a7174c83a853752b3e74cb001a234f3eca099688fdf0dd2540c60bb1e2","text":" expected keys:","translated":" 预期的键:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:22:25Z"}
{"cache_key":"6bf1a3983ec9759b076402ea6998c8207a0b0ef0d87b56ef4945599c9f8bd90a","segment_id":"environment.md:e4255aa4e8f9e525","source_path":"environment.md","text_hash":"e4255aa4e8f9e52571c9bc93336d0774bcd7f017b7b5297fb33b8e1986166f92","text":"), applied only for missing expected keys.","translated":"),仅对缺失的预期密钥应用。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:26:02Z"}
{"cache_key":"6c18bb32586b6c812ebf5323b8ed442c63be7b4014bc62e51f0d7f5eb46d223b","segment_id":"environment.md:582967534d0f909d","source_path":"environment.md","text_hash":"582967534d0f909d196b97f9e6921342777aea87b46fa52df165389db1fb8ccf","text":" in ","translated":" 在 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:40:54Z"}
{"cache_key":"6c1b9632694258c227417b61df6433ac71eca1f2d35ff31cb5e145a7188dacfe","segment_id":"start/getting-started.md:d7849463c3ab6a49","source_path":"start/getting-started.md","text_hash":"d7849463c3ab6a496d77b8e6745d00ad430324bc5ed419a859f7c9e494102d68","text":"Manual run (foreground):","translated":"手动运行(前台):","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:51Z"}
{"cache_key":"6c3d2263be9d0d6dd77934bd87f882599e2e9449e67bdee4388f84ab0aa6571b","segment_id":"start/wizard.md:698fdfc9c55bd3e4","source_path":"start/wizard.md","text_hash":"698fdfc9c55bd3e4ed5a9365317ae70aac20783ec38057088da27012a470a901","text":"Gateway port ","translated":"Gateway 端口 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:39:50Z"}
@@ -572,6 +588,7 @@
{"cache_key":"71667555ad1cea654225fec33df1804c97a0b8167affbf3d3c426ccb778e780a","segment_id":"start/wizard.md:82e1216ede141cb1","source_path":"start/wizard.md","text_hash":"82e1216ede141cb1553d20be7356c3f1ab9da9a4a05303cf7cd05ef01142558f","text":"Gateway settings (port/bind/auth/tailscale)","translated":"Gateway 设置(端口/绑定/认证/Tailscale","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:40:23Z"}
{"cache_key":"7170dcd349905701fd3cde7dc5bce0aed2618717e87ffa06e9ab230041f689a1","segment_id":"environment.md:cdb4ee2aea69cc6a","source_path":"environment.md","text_hash":"cdb4ee2aea69cc6a83331bbe96dc2caa9a299d21329efb0336fc02a82e1839a8","text":".","translated":"。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T11:45:26Z"}
{"cache_key":"724a450b6cdfc09dd0fc5acf94bb7f20a45c43e524810239d0e6e7cac65ff74b","segment_id":"index.md:bd293e4db98037bc","source_path":"index.md","text_hash":"bd293e4db98037bc9da5137af50453ac9c81b49e14eb4c47f121b12bed880877","text":" — Direct chats collapse into shared ","translated":" — 直接聊天合并到共享的 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:01:59Z"}
{"cache_key":"726990d1aefefc1ae562bce73f84f1de90c5c6cc094dc9121495e4480aedab92","segment_id":"environment.md:28e19c6e69c7a2aa","source_path":"environment.md","text_hash":"28e19c6e69c7a2aa071951dda3ff0a11ca178e3fb295dae8d6ed7dcc994434a4","text":" for full details.","translated":" 了解完整详情。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:22:42Z"}
{"cache_key":"72d5ce369dd6489f427c02710fae70f6426a51de9441678410a023761cee215b","segment_id":"start/wizard.md:8f7c7d2f15e90b42","source_path":"start/wizard.md","text_hash":"8f7c7d2f15e90b420fb6f2cc7632d7d7a433bc94eeb262d9718286e5ffd9b365","text":"Related docs","translated":"相关文档","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:49:00Z"}
{"cache_key":"730b6369e65b8f27f57a90f6ee355beca28d783793767209a7cfe7beb736769b","segment_id":"start/wizard.md:eda31fe8fb873697","source_path":"start/wizard.md","text_hash":"eda31fe8fb873697fd7d5bfba08f263eaa917808a644bddd2b6d89d3a6b1c868","text":"QuickStart vs Advanced","translated":"快速入门与高级模式","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:39:30Z"}
{"cache_key":"73164a8584f9cc4e546493100199d4ebcbb65ce74c33e21d06da689c6d7b9328","segment_id":"start/wizard.md:ce85fecfbffa2746","source_path":"start/wizard.md","text_hash":"ce85fecfbffa2746f0a9b66464140eb2ed5a085ce85fff062ef0ff8b5686a0a5","text":".\nSessions are stored under ","translated":"下。会话存储在 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:48:54Z"}
@@ -607,6 +624,7 @@
{"cache_key":"77b6a43a45b36b25b51859a5b976fa12609b6d19ed351bc0e84fae2290d32da9","segment_id":"help/index.md:2adc964c084749b1","source_path":"help/index.md","text_hash":"2adc964c084749b1f2d8aef24030988b667dbda2e38a6a1699556c93e07c1cea","text":"Start here","translated":"从这里开始","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:11:12Z"}
{"cache_key":"7806b590e1e2ff8ab2244875f7a2c370ab3b11462fd2061e5f4af9cf72f70d19","segment_id":"start/wizard.md:9c706a2bb9ebcb20","source_path":"start/wizard.md","text_hash":"9c706a2bb9ebcb206633616f2a40867b0c02716657ac4c0e95c7c1939287d3d8","text":"; auth profiles live in ","translated":";认证配置存储在 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:43:31Z"}
{"cache_key":"78161c8de8a14607cd003796d4c4ace7048f9116ecbe036601136d7f0cef4ff3","segment_id":"start/getting-started.md:bfd99edf844f6205","source_path":"start/getting-started.md","text_hash":"bfd99edf844f62050af2f7d37df7cfa7f651b8e1be341eb4f07c3849ca4efc43","text":"Fastest chat: open the Control UI (no channel setup needed). Run ","translated":"最快聊天方式:打开控制界面(无需设置渠道)。运行 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:34:33Z"}
{"cache_key":"78165d4ed88f199c04e34f0686aca8ee87969331cf02c78e26a1851d3673baae","segment_id":"help/index.md:0b554dd0f4b96cff","source_path":"help/index.md","text_hash":"0b554dd0f4b96cff4e1137c5fb22253b12125b6a3dce5d9238c80b20491bcb8e","text":"Help\n\nIf you want a quick “get unstuck” flow, start here:","translated":"# 帮助\n\n如果你想要一个快速的\"脱困\"流程,从这里开始:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:15:12Z"}
{"cache_key":"785cae01bc172c4c47e2e82cda4c5afd7d37d7069a008e44c8a4176eeacafe67","segment_id":"help/index.md:a8ab86b9313a9236","source_path":"help/index.md","text_hash":"a8ab86b9313a92362150f5e5ba8a19de4ee52f2e3162f9bd2bc6cf128a2fcd18","text":"If youre looking for conceptual questions (not “something","translated":"如果你在寻找概念性问题(不是\"出了什么","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T11:45:00Z"}
{"cache_key":"78ae0fabb1aab02156d5bf1b4e148ba155369b079aa0b733aca5a750a3d0cdc2","segment_id":"index.md:329f3c913c0a1636","source_path":"index.md","text_hash":"329f3c913c0a16363949eb8ee7eb0cda7e81137a3851108019f33e5d18b57d8f","text":"Switching between npm and git installs later is easy: install the other flavor and run ","translated":"之后在 npm 和 git 安装之间切换很简单:安装另一种方式然后运行 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:51:30Z"}
{"cache_key":"791458a3464d7dd0036471e90590958905611942f9f0aefd8917c701e4e587d4","segment_id":"start/wizard.md:0516de0bbbd36c95","source_path":"start/wizard.md","text_hash":"0516de0bbbd36c95c5c45902d43caf2abdab59363114c4d6abae961f6ed1c1cb","text":" imply non-interactive mode. Use ","translated":" 意味着非交互模式。请使用 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:40:50Z"}
@@ -651,6 +669,7 @@
{"cache_key":"7eefff451137a5fd592db6fef6e65447cae69abe23699c34cb838a1c3cc04d73","segment_id":"start/wizard.md:d3745cec7a646b22","source_path":"start/wizard.md","text_hash":"d3745cec7a646b229f6d7123ef3557f68640f35a54a593f1e0e32776da0677c1","text":" (autogenerated, even on loopback)","translated":" (自动生成,即使在回环地址上也是如此)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:39:58Z"}
{"cache_key":"7f2e9e14503f22acab8659b458900c0864bdc52ee5055d4a3a742508a8e41314","segment_id":"environment.md:45ca56d179d4788c","source_path":"environment.md","text_hash":"45ca56d179d4788c55ba9f7653b376d62e7faa738e92259e3d4f6f5c1b554f28","text":"Related","translated":"相关","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T11:47:13Z"}
{"cache_key":"7f4d30ae34bbfb95b016db35c14a77f46cdda52ff397a69b63ad655c6128f0f6","segment_id":"index.md:30f035b33a6c35d5","source_path":"index.md","text_hash":"30f035b33a6c35d51e09f9241c61061355c872f2fb9a82822cd2f5f443fd4ad4","text":"Group Chat Support","translated":"群聊支持","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:30:31Z"}
{"cache_key":"7f5759f942e4173b7e990de6fbc0eada6e5b6c3106c5aa6fae08456d7b79dcf8","segment_id":"index.md:6b65292dc52408c1","source_path":"index.md","text_hash":"6b65292dc52408c15bb07aa90735e215262df697d1a7bd2d907c9d1ff294ed5e","text":"If you dont have a global install yet, run the onboarding step via ","translated":"如果尚未进行全局安装,请通过以下方式运行上手引导步骤 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:31:19Z"}
{"cache_key":"7f8a0ec6c0614299ed8aca539dde67e208ecc32d4022975fbb37f7930f3f70e5","segment_id":"start/getting-started.md:4cc7ae6d3b7fbaaf","source_path":"start/getting-started.md","text_hash":"4cc7ae6d3b7fbaaf56673ea3268caa38af191a587867ef1090c9f689ecccec96","text":"Headless/server tip: do OAuth on a normal machine first, then copy ","translated":"无头/服务器提示:先在普通机器上完成 OAuth然后复制 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:40Z"}
{"cache_key":"7fec8c329b4438aef905e1918364b86faca2a2580bb29eded4850a67ba16109b","segment_id":"environment.md:496aca80e4d8f29f","source_path":"environment.md","text_hash":"496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee","text":" block","translated":" 块","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:25:48Z"}
{"cache_key":"8070c35741bdfaa2f8878a7460406a597ccf7fec7994522389adeafea46b6e8e","segment_id":"environment.md:frontmatter:read_when:0","source_path":"environment.md:frontmatter:read_when:0","text_hash":"90fc0487bff88009979cff1061c1a882df8c3b1baa9c43538331d9d5dab15479","text":"You need to know which env vars are loaded, and in what order","translated":"你需要了解加载了哪些环境变量,以及加载的顺序","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T11:45:10Z"}
@@ -662,6 +681,7 @@
{"cache_key":"822efbc5bcf680421493847f6b76e9626f1d8202ff5ff47cd3e141ecdac58a9f","segment_id":"environment.md:496aca80e4d8f29f","source_path":"environment.md","text_hash":"496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee","text":" block","translated":" 块","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:16:27Z"}
{"cache_key":"829cc48b5c60f16b09e63437a5de27acc17910473f8e3dfbc505a0d3e3b593c7","segment_id":"start/wizard.md:79a482cf546c23b0","source_path":"start/wizard.md","text_hash":"79a482cf546c23b04cd48a33d4ca8411f62e5b7dc8c3a8f30165e28e747f263a","text":"iMessage","translated":"iMessage","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:44:54Z"}
{"cache_key":"82d122e7cc5c895b61dec28850c3f07a68e69c19f554d9088318f62c6cd30fe1","segment_id":"environment.md:6d28a9f099e563d9","source_path":"environment.md","text_hash":"6d28a9f099e563d9322b5bcdea9ff98af87e9c213c2222462ae738d2fb27ecbe","text":" block\n\nTwo equivalent ways to set inline env vars (both are non-overriding):","translated":" 块\n\n设置内联环境变量的两种等效方式均为非覆盖式","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:16:44Z"}
{"cache_key":"83090d5cbebceddaa2f500bdb4240d4ea9a8ee14da3654f77128a067e4dc220a","segment_id":"environment.md:e4255aa4e8f9e525","source_path":"environment.md","text_hash":"e4255aa4e8f9e52571c9bc93336d0774bcd7f017b7b5297fb33b8e1986166f92","text":"), applied only for missing expected keys.","translated":"),仅对缺失的预期密钥应用。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:12:37Z"}
{"cache_key":"8334186d1a61e931ed7b3905a26e470159f86593819124c5626df7a012733ee9","segment_id":"environment.md:frontmatter:summary","source_path":"environment.md:frontmatter:summary","text_hash":"78351223e7068721146d2de022fdf440c2866b2ee02fbbb50bf64369b999820b","text":"Where OpenClaw loads environment variables and the precedence order","translated":"其中 OpenClaw 加载 环境变量 及优先级顺序","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:56:59Z"}
{"cache_key":"833685db37cf96f2342238018bd6a4a6e7812d1794a7389dc1e349917b140f50","segment_id":"environment.md:668e5590b5bb9990","source_path":"environment.md","text_hash":"668e5590b5bb9990eeb25bf657f7d17281a4c613ee4442036787cd4b2efd22bb","text":"If the config file is missing entirely, step 4 is skipped; shell import still runs if enabled.","translated":"如果配置文件完全缺失,则跳过第 4 步;如果启用了 shell 导入,它仍会运行。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:19:28Z"}
{"cache_key":"834bd8857aa5700b0ec493efb4625ba88e34c885a8254b13f6c44a75589021d2","segment_id":"index.md:9bcda844990ec646","source_path":"index.md","text_hash":"9bcda844990ec646b3b6ee63cbdf10f70b0403727dea3b5ab601ca55e3949db9","text":" for node WebViews; see ","translated":" 用于节点 WebView请参阅 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:29:13Z"}
@@ -675,6 +695,7 @@
{"cache_key":"84c686db4b4fc386bbb4efa35c380073babbc5fb4b2eb1ba3a8213a5f135a5bc","segment_id":"start/getting-started.md:161660030aa6c9e3","source_path":"start/getting-started.md","text_hash":"161660030aa6c9e32470cc1c023dab32dc748d80b0e61882b368cb775d12638e","text":" → ","translated":" → ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:34:27Z"}
{"cache_key":"84e200d4c823802e34a99da4faa8328d0e250aca858b0a32cc08e3ae12e0cc0e","segment_id":"start/wizard.md:e4442451c634e0db","source_path":"start/wizard.md","text_hash":"e4442451c634e0db2db0fae78725becbeafd567302e3ecbfeb5ccdc5887d29be","text":" from GitHub releases:","translated":" (从 GitHub 发布版本):","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:47:56Z"}
{"cache_key":"85040674d9e2db6adb1ebb8c6215e72171d213a9dac8bd3c6bcb438178adc88b","segment_id":"index.md:0a4a282eda1af348","source_path":"index.md","text_hash":"0a4a282eda1af34874b588bce628b76331fbe907de07b57d39afdedccac2ba14","text":" http://127.0.0.1:18789/ (or http://localhost:18789/)","translated":" http://127.0.0.1:18789/(或 http://localhost:18789/","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:28:15Z"}
{"cache_key":"850dacfab0ff7f9fd9498aeac24c0b84c59f266291d504465d7dead52da552bf","segment_id":"index.md:41dc1288a547d7d1","source_path":"index.md","text_hash":"41dc1288a547d7d155c2d7b831e8cff388e12ab9d77d4c24cd0757ed47e9e209","text":" — Block streaming + Telegram draft streaming details (","translated":" — 块流式传输 + Telegram 草稿流式传输详情(","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:30:04Z"}
{"cache_key":"85cb0b7ed6991128b9fe65b7b103c5f32da742641cb24ffc1a3469002a2bcad6","segment_id":"start/getting-started.md:e24d86fa815827a4","source_path":"start/getting-started.md","text_hash":"e24d86fa815827a4dc5b8b22711caaf036427796512a74167ebaf615c495f9f8","text":"Telegram / Discord / others","translated":"Telegram / Discord / 其他","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:37:17Z"}
{"cache_key":"85e39779810391375b7241f2d999fbd5e6b2830ddf226a9ad561132c40d4fd47","segment_id":"start/wizard.md:21b111cbfe6e8fca","source_path":"start/wizard.md","text_hash":"21b111cbfe6e8fca2d181c43f53ad548b22e38aca955b9824706a504b0a07a2d","text":"Default ","translated":"默认 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:43:41Z"}
{"cache_key":"85fdea7998dfe111261588f998c93aceaa9b04ba174bc16bd188e3bbd8f3228a","segment_id":"environment.md:668e5590b5bb9990","source_path":"environment.md","text_hash":"668e5590b5bb9990eeb25bf657f7d17281a4c613ee4442036787cd4b2efd22bb","text":"If the config file is missing entirely, step 4 is skipped; shell import still runs if enabled.","translated":"如果配置文件完全缺失,则跳过第 4 步如果已启用shell 导入仍会运行。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:12:40Z"}
@@ -691,6 +712,7 @@
{"cache_key":"87b42c17fb63bfdcd059198572016f6b8b3cd297aaa991c4c1dea8723a68fbfe","segment_id":"index.md:9abe8e9025013e78","source_path":"index.md","text_hash":"9abe8e9025013e78a6bf2913f8c20ee43134ad001ce29ced89e2af9c07096d8f","text":"Media: images","translated":"媒体:图片","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:48Z"}
{"cache_key":"87d80d180c9d4789c20123b3bc177f99c4d00909f70c6fe3c209c078bdcafdce","segment_id":"index.md:1074116f823ec992","source_path":"index.md","text_hash":"1074116f823ec992e76d7e8be19d3235fec5ddd7020562b06e7242e410174686","text":"Remote use","translated":"远程使用","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:00:36Z"}
{"cache_key":"87f8e99a729beb8e55fdef7ca70ebe4b11f4ff1c5dbbfcb3e654429198c6bf0f","segment_id":"help/index.md:729bc562eec2658b","source_path":"help/index.md","text_hash":"729bc562eec2658bd11ffdd522fe5277177dc73e86eaca7baac0b472a4d8f8b2","text":"If youre looking for conceptual questions (not “something broke”):","translated":"如果你在寻找概念性问题(不是\"出了故障\"","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:19:05Z"}
{"cache_key":"8819cee05e67d9206c9adc7cf9539b1586a050f9c259e65a3099184303440591","segment_id":"environment.md:a42cc4a7174c83a8","source_path":"environment.md","text_hash":"a42cc4a7174c83a853752b3e74cb001a234f3eca099688fdf0dd2540c60bb1e2","text":" expected keys:","translated":" 预期密钥:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:58:11Z"}
{"cache_key":"88ab429b0aa43b0cfc93a1fc0e69576a2acbf64d0cd407fc1028488a0c27c9fc","segment_id":"index.md:fdef9f917ee2f72f","source_path":"index.md","text_hash":"fdef9f917ee2f72fbd5c08b709272d28a2ae7ad8787c7d3b973063f0ebeeff7a","text":" to update the gateway service entrypoint.","translated":" 以更新网关服务入口点。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:03:03Z"}
{"cache_key":"88d02146dbe2246af19afc2deecbb627547528cd1bf8b9839d358e8987a88a99","segment_id":"index.md:9c870aa6e5e93270","source_path":"index.md","text_hash":"9c870aa6e5e93270170d5a81277ad3e623afe8d4efd186d3e28f3d2b646d52e6","text":"How it works","translated":"工作原理","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:59:42Z"}
{"cache_key":"88f63f39528cb8bcb530a350a6b610125dbf6ab7034c2509a772e2ec28ed9476","segment_id":"help/index.md:frontmatter:read_when:1","source_path":"help/index.md:frontmatter:read_when:1","text_hash":"857eafc389d179e83e21e46c10527fec40894fe064c63847ba06b946b7d5eb73","text":"Something broke and you want the fastest path to a fix","translated":"出了问题,你想找到最快的修复方法","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:15:10Z"}
@@ -703,6 +725,7 @@
{"cache_key":"8a81f73e519177081d755623ff45ac47552fa513f5aaf9c77335ce2c329087f3","segment_id":"start/getting-started.md:524bf322c2034388","source_path":"start/getting-started.md","text_hash":"524bf322c2034388f76cd94c1c7834341cedfa09bc4a864676749a08b243416d","text":"model/auth (OAuth recommended)","translated":"模型/认证(推荐使用 OAuth","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:34:53Z"}
{"cache_key":"8a83aabc21a6b84ce7552d72a9bc0a7c2d99864c31350064cbd39564354421f1","segment_id":"index.md:9adcfa4aa10a4e8b","source_path":"index.md","text_hash":"9adcfa4aa10a4e8b991a72ccc45261cd64f296aed5b257e4caf9c87aff1290a0","text":" — Send and receive images, audio, documents","translated":" —— 发送和接收图片、音频、文档","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:50:51Z"}
{"cache_key":"8a984f774ac8874be4797ffddd21cbdddc9379fa6bc51121620fbe9395cd91cf","segment_id":"help/index.md:bfc5930cc2660330","source_path":"help/index.md","text_hash":"bfc5930cc2660330260afd407e98d86adaec0af48dd72b88dc33ef8e9066e2c9","text":"Install sanity (Node/npm/PATH):","translated":"安装完整性检查Node/npm/PATH","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:15:18Z"}
{"cache_key":"8aba2e1efca29d503bd185064c1e676dd87fa34c81fa9bb059ed6300f6bfd517","segment_id":"environment.md:28e19c6e69c7a2aa","source_path":"environment.md","text_hash":"28e19c6e69c7a2aa071951dda3ff0a11ca178e3fb295dae8d6ed7dcc994434a4","text":" for full details.","translated":" 了解完整详情。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:41:35Z"}
{"cache_key":"8ac55f265f3496db43dce513fde21c137826476afcff2ed1b3e86e613ff28b3c","segment_id":"start/wizard.md:44dab6c89cc5e6d9","source_path":"start/wizard.md","text_hash":"44dab6c89cc5e6d9a3112d3cb45c19cd16c3a9963082276015d4b624e5e67782","text":"Some channels are delivered as plugins. When you pick one during onboarding, the wizard\nwill prompt to install it (npm or a local path) before it can be configured.","translated":"部分渠道以插件形式提供。当您在上手引导期间选择某个渠道时,向导会提示先安装它(通过 npm 或本地路径),然后才能进行配置。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:48:57Z"}
{"cache_key":"8b2c90beec3893be65468e57df762fcbc285a9772042200eee3d4bf8f7ff9c0d","segment_id":"index.md:96be070791b7d545","source_path":"index.md","text_hash":"96be070791b7d545dc75084e59059d2170eed247350b351db5330fbd947e4be6","text":"👥 ","translated":"👥 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:30:29Z"}
{"cache_key":"8b921a960a8b92bc6210c2e228fe886cd93000a5a77f1cb5ac97233de2c4f965","segment_id":"index.md:fb87b8dba88b3edc","source_path":"index.md","text_hash":"fb87b8dba88b3edced028edfe2efa5f884ab2639c1b26efa290ccd0469454d25","text":"Slash commands","translated":"斜杠命令","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:04:03Z"}
@@ -784,6 +807,7 @@
{"cache_key":"9a7478d471c30618239146c8b7adbd3669fd552a2fafba13cc6dc8b51c083243","segment_id":"index.md:a194ca16424ddd17","source_path":"index.md","text_hash":"a194ca16424ddd17dacc45f1cbd7d0e41376d8955a7b6d02bc38c295cedd04e4","text":"RPC adapters","translated":"RPC 适配器","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:04:25Z"}
{"cache_key":"9aaaeb76bc162fe216b19290b0978994ad43023335a81224b65bf7e4849ed5b6","segment_id":"index.md:frontmatter:summary","source_path":"index.md:frontmatter:summary","text_hash":"891b2aa093410f546b89f8cf1aa2b477ba958c2c06d2ae772e126d49786df061","text":"Top-level overview of OpenClaw, features, and purpose","translated":"OpenClaw 的顶层概述、功能和用途","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:47:18Z"}
{"cache_key":"9b0b553b6bb64b97bc340190fc4f10febadb5c4542122d2dea4661534f60b8b6","segment_id":"index.md:a10f6ed8c1ddbc10","source_path":"index.md","text_hash":"a10f6ed8c1ddbc10d3528db7f7b6921c1dd5a5e78aa191ff017bf29ce2d26449","text":"⏱️ ","translated":"⏱️ ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:50:04Z"}
{"cache_key":"9b4a9e428618ff38c3d8e54131d987860c0ebbb45007e3493d99964d9cd436a6","segment_id":"index.md:4d705f0fa835fd21","source_path":"index.md","text_hash":"4d705f0fa835fd216c4fd6dea0ee851d33720e23fb714c4c9ea74ac3211fccdc","text":"Discovery + transports","translated":"发现机制 + 传输方式","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:28Z"}
{"cache_key":"9bb6f5ad39ff9d7aff3bca1fda6f474e19f25c0ffaaffaf3b19c924234d8c03a","segment_id":"index.md:f0d82ba647b4a33d","source_path":"index.md","text_hash":"f0d82ba647b4a33da3008927253f9bed21e380f54eab0608b1136de4cbff1286","text":"OpenClaw bridges WhatsApp (via WhatsApp Web / Baileys), Telegram (Bot API / grammY), Discord (Bot API / channels.discord.js), and iMessage (imsg CLI) to coding agents like ","translated":"OpenClaw 将 WhatsApp通过 WhatsApp Web / Baileys、TelegramBot API / grammY、DiscordBot API / 渠道.discord.js和 iMessageimsg CLI桥接到编程 智能体,例如 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:47:31Z"}
{"cache_key":"9c03abf2c27129fa2698e7640a7b9add5936e84cf6d779d5f189bf9a27940aa6","segment_id":"index.md:310cc8cec6b20a30","source_path":"index.md","text_hash":"310cc8cec6b20a3003ffab12f5aade078a0e7a7d6a27ff166d62ab4c3a1ee23d","text":"If you ","translated":"如果你 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:03:25Z"}
{"cache_key":"9c11b2ec1c922e332f69000a8a937f0a2318b5356faa6278a7580cc49c3526d5","segment_id":"index.md:e47cdb55779aa06a","source_path":"index.md","text_hash":"e47cdb55779aa06a74ae994c998061bd9b7327f5f171c141caf2cf9f626bfe4b","text":"Peter Steinberger","translated":"Peter Steinberger","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:05:52Z"}
@@ -794,6 +818,7 @@
{"cache_key":"9cbdb7ff14fdd8d015b7bcce3b3c0d48b1711e631ff86cae2c699684f8e4d143","segment_id":"start/wizard.md:c4b2896a2081395e","source_path":"start/wizard.md","text_hash":"c4b2896a2081395e282313d6683f07c81e3339ef8b9d2b5a299ea5b626a0998f","text":").","translated":")。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:41:17Z"}
{"cache_key":"9d44e8f510b7e2cf5ea7b08188a9c606937bc3db8c49e22d903828b34b8b04c1","segment_id":"start/wizard.md:19f53c2ccaf19969","source_path":"start/wizard.md","text_hash":"19f53c2ccaf199696e23d43812941e23fed0625900d2a551533304d6ca1980f6","text":" install or change anything on the remote host.","translated":" 在远程主机上安装或更改任何内容。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:40:40Z"}
{"cache_key":"9d7b3ce341253f712ecd8b4ca661ae0a6d85b1ee8e8ddf00b1ec02ca13d67237","segment_id":"help/index.md:569ca49f4aaf7846","source_path":"help/index.md","text_hash":"569ca49f4aaf7846e952c1d4aeca72febd0b79fa1c4f9db08fd3127551218572","text":"Install","translated":"安装","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:24:41Z"}
{"cache_key":"9d829bdffa4f3aa22d063ea4b6391f8094b8f4db9df8a985430559d4a153e286","segment_id":"index.md:58d30d963f28264b","source_path":"index.md","text_hash":"58d30d963f28264bd9ba0e2d4c07c2c43c0ac1c1609c25b3fccf475eebf41727","text":"Skills config","translated":"技能配置","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:04:19Z"}
{"cache_key":"9db03f9dc7b789dbc3b4115e9b644cd22de2a63adeed02eb3b403a223d96b819","segment_id":"index.md:2b402c90e9b15d9c","source_path":"index.md","text_hash":"2b402c90e9b15d9c3ef65c432c4111108f54ee544cda5424db46f6ac974928e4","text":"🔐 ","translated":"🔐 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:30:14Z"}
{"cache_key":"9e0b7ed9895b612971d582145c837e95bfec8b051c6bccddd008d56dff778711","segment_id":"start/wizard.md:28d03596d24eeb4e","source_path":"start/wizard.md","text_hash":"28d03596d24eeb4eab2d6fe21ca1cb95be7cb1fa6f92933db05e2cc4f4cdfa06","text":"Skip","translated":"跳过","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:43:16Z"}
{"cache_key":"9e3a338fc3d6bce679ff4711d74e67c66877245b6ebd2c2a08f182a3a788dae6","segment_id":"start/getting-started.md:fd82e54418ec23cd","source_path":"start/getting-started.md","text_hash":"fd82e54418ec23cda00219878eaf76c3b37337b3dcb7560a941db6a0d2ec249e","text":": background install (launchd/systemd; WSL2 uses systemd)","translated":"后台安装launchd/systemdWSL2 使用 systemd","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:15Z"}
@@ -823,6 +848,7 @@
{"cache_key":"a235aca76de620b9ed0805727dc5f142a660dc6dac3254a01531acad96cb084d","segment_id":"index.md:d53b75d922286041","source_path":"index.md","text_hash":"d53b75d9222860417f783b0829023b450905d982011d35f0e71de8eed93d90fc","text":"New install from zero:","translated":"从零开始全新安装:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:59:05Z"}
{"cache_key":"a28528856eac855eaf431dc468f5d1a9b3918df6dc73a9bb54c488aa7c23faad","segment_id":"start/getting-started.md:387847437e10c06c","source_path":"start/getting-started.md","text_hash":"387847437e10c06cae87567a6579b38e71849aea9c2355eba4a8d090418360b9","text":"The wizard can write tokens/config for you. If you prefer manual config, start with:","translated":"向导可以为您写入令牌/配置。如果您更喜欢手动配置,请从以下内容开始:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:37:19Z"}
{"cache_key":"a28d9fd85bfd4afc9a62b3cfe12607c86001b32a9a97d72eeb6cd50993fb51ee","segment_id":"index.md:c6e91f3b51641b1c","source_path":"index.md","text_hash":"c6e91f3b51641b1c43d297281ee782b40d9b3a0bdd7afc144ba86ba329d5f95f","text":"OpenClaw = CLAW + TARDIS","translated":"OpenClaw = CLAW + TARDIS","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:54:04Z"}
{"cache_key":"a29d82b0936a3237f692bb4c86bb8bcc8b1840db6ab6f2922a249fda830bdc5a","segment_id":"index.md:4d705f0fa835fd21","source_path":"index.md","text_hash":"4d705f0fa835fd216c4fd6dea0ee851d33720e23fb714c4c9ea74ac3211fccdc","text":"Discovery + transports","translated":"发现 + 传输","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:04:36Z"}
{"cache_key":"a2c462e51d228b070aba2a14a09d41aa54e0962d795724d5a090c71c7e242dfe","segment_id":"start/getting-started.md:acdd1e734125f341","source_path":"start/getting-started.md","text_hash":"acdd1e734125f341604c0efbabdcc4c4b0597e8f6235d66c2445edd1812838c1","text":"Telegram","translated":"Telegram","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:37:22Z"}
{"cache_key":"a2f08193fbeb8a9400b75d96157bbbf488ab3aa51d50658094d00bb841646217","segment_id":"help/index.md:2adc964c084749b1","source_path":"help/index.md","text_hash":"2adc964c084749b1f2d8aef24030988b667dbda2e38a6a1699556c93e07c1cea","text":"Start here","translated":"从这里开始","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T11:44:37Z"}
{"cache_key":"a32d46351380765e1ec38639781fc9e5abaccdf74240eee7ab685f570551f487","segment_id":"index.md:7d8b3819c6a9fb72","source_path":"index.md","text_hash":"7d8b3819c6a9fb726f40c191f606079b473f6f72d4080c13bf3b99063a736187","text":"Ops and safety:","translated":"运维与安全:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:33:04Z"}
@@ -832,6 +858,7 @@
{"cache_key":"a3909a297d0e74a4cb418a7a549f495f6eed24048ebf8f12f448eff8d7a20c50","segment_id":"environment.md:1ec31258a6b45ea9","source_path":"environment.md","text_hash":"1ec31258a6b45ea903cd76f5b0190a99ab56afff6241a04f0681eb12b7a02484","text":"Env var equivalents:","translated":"等效的环境变量:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:26:18Z"}
{"cache_key":"a3e59ee4578bdb5fd68940692f78e9389e163da63e350ba9f0689ffbc980d4a5","segment_id":"environment.md:28b1103adde15a9d","source_path":"environment.md","text_hash":"28b1103adde15a9ddd8fc71f0c57dc155395ade46a0564865ccb5135b01c99b7","text":"OpenClaw pulls environment variables from multiple sources. The rule is **never override existing values**.","translated":"OpenClaw 从多个来源拉取环境变量。规则是**永远不覆盖已有的值**。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:19:23Z"}
{"cache_key":"a4384986e5ce06eca0118051e6a851ac0fd3d922d4d1f31b60000687962a2288","segment_id":"start/wizard.md:ec1a3a5d6d6f0bac","source_path":"start/wizard.md","text_hash":"ec1a3a5d6d6f0baca7805bf1ea17fc7b02042416f02f80bc1970ad8c710abd89","text":"Flow details (local)","translated":"流程详情(本地)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:40:56Z"}
{"cache_key":"a44af8d86be9e8883c8df3ed68722e659e4d7bb99e2675df13ee0ab386219e51","segment_id":"index.md:22159a426e4f2635","source_path":"index.md","text_hash":"22159a426e4f26356382cc3ac9b2e7af5123c1309250332f5dcbbc6e6f952b0e","text":"Network model","translated":"网络 模型","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:59:51Z"}
{"cache_key":"a46b3daf9b1e1045e72e437a283e8377ec9b4820cde181d05a24a9a582cbf914","segment_id":"start/wizard.md:12754931af777521","source_path":"start/wizard.md","text_hash":"12754931af777521bcb6a904d2a7d342d0d77e6c4f1f2eb1b8b3753d25a1ab4a","text":"If the Control UI assets are missing, the wizard attempts to build them; fallback is ","translated":"如果 Control UI 资源文件缺失,向导会尝试构建它们;后备方案是 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:46:06Z"}
{"cache_key":"a4a009f8c9411234d5dd3ef4a71fdf292ec59e29a2b74d197acea1c789825536","segment_id":"help/index.md:6cb77499abdccd9a","source_path":"help/index.md","text_hash":"6cb77499abdccd9a2dbb7c93a4d31eed01613dda06302933057970df9ecdeb54","text":"Logs:","translated":"日志:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:24:46Z"}
{"cache_key":"a4b963e5c58f681343b2e7b98ade4df71e3a328906ed382ffc8c0e4853fdf162","segment_id":"environment.md:b1d6b91b67c2afa5","source_path":"environment.md","text_hash":"b1d6b91b67c2afa5e322988d9462638d354ddf8a1ef79dba987f815c22b4baee","text":" at ","translated":" 位于 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:25:39Z"}
@@ -864,6 +891,7 @@
{"cache_key":"a9c30fa450ed436cb03bc256b3075761a9215bd99bcd7bd2891cf15317ffd34f","segment_id":"environment.md:d08a8493f686363a","source_path":"environment.md","text_hash":"d08a8493f686363a78b913d45ebfbd87a3768d1c77b70f23b1fdade3c066e481","text":"Shell env import","translated":"Shell 环境导入","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:16:47Z"}
{"cache_key":"aa80cfc76e76409c5ba7bf331e4fb8aadf72703ead80d203c94e74209da993f9","segment_id":"index.md:310cc8cec6b20a30","source_path":"index.md","text_hash":"310cc8cec6b20a3003ffab12f5aade078a0e7a7d6a27ff166d62ab4c3a1ee23d","text":"If you ","translated":"如果你 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:31:31Z"}
{"cache_key":"aaa5becdcd694b68de2e61f6a13bd932c3f80f8b0b5a959a054a61ad5911beef","segment_id":"index.md:81a1c0449ea684aa","source_path":"index.md","text_hash":"81a1c0449ea684aadad54a7f8575061ddc5bfa713b6ca3eb8a0228843d2a3ea1","text":"Nodes (iOS/Android)","translated":"节点iOS/Android","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:22Z"}
{"cache_key":"aab013ce01ee6fc5b450d86a4cb8582865cc8b2e84ef22a6b5e0191462c1ee45","segment_id":"index.md:6b65292dc52408c1","source_path":"index.md","text_hash":"6b65292dc52408c15bb07aa90735e215262df697d1a7bd2d907c9d1ff294ed5e","text":"If you dont have a global install yet, run the onboarding step via ","translated":"如果尚未进行全局安装,请通过以下方式运行 上手引导 步骤 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:03:09Z"}
{"cache_key":"aacffcbc2a97abf1a5eccd00e5893be1125e364251fa27f3e0c88ef2db2b0248","segment_id":"index.md:acdd1e734125f341","source_path":"index.md","text_hash":"acdd1e734125f341604c0efbabdcc4c4b0597e8f6235d66c2445edd1812838c1","text":"Telegram","translated":"Telegram","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:36Z"}
{"cache_key":"aad00bc21098071ff9c86ff467cb7f5c65d3467ce4bf7d707f560479783e9eaa","segment_id":"index.md:b79cac926e0b2e34","source_path":"index.md","text_hash":"b79cac926e0b2e347e72cc91d5174037c9e17ae7733fd7bdb570f71b10cd7bfc","text":"Help","translated":"帮助","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:31:51Z"}
{"cache_key":"aae01909516ef373ddb2e4996f9016675f297208f7f075a68490f1f48eb0c87f","segment_id":"environment.md:6a26e1694d9e8520","source_path":"environment.md","text_hash":"6a26e1694d9e852038e5a472ed6b54cc023b4ace8ac10d745cad426d5dc057f3","text":" details.","translated":" 详情。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T11:47:11Z"}
@@ -911,6 +939,7 @@
{"cache_key":"b1a0214973416cbfb4dcac01605c51911f412a6b7d862a6b8aed7db6364bb93a","segment_id":"start/wizard.md:1a0f5fc7ca6e8a74","source_path":"start/wizard.md","text_hash":"1a0f5fc7ca6e8a74bc099d9c397a23564b55eca50c3b2e33c472acb7032a6f3b","text":" (if Minimax chosen)","translated":" (如果选择了 Minimax","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:48:35Z"}
{"cache_key":"b1babe6ce88663854adf02aa4a23f21c9a98e036c72bf36dbe4b518d5d025d8b","segment_id":"environment.md:8d076464a84995bc","source_path":"environment.md","text_hash":"8d076464a84995bc095e934b0aa1e4419372f27cd71d033571e4dbba201ee5d8","text":"You can reference env vars directly in config string values using ","translated":"您可以使用以下方式在配置字符串值中直接引用 环境变量 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:41:25Z"}
{"cache_key":"b1bfed2a2039ffc6f83d8201645caf18d6b942a8e5efbe2a28ca24978f750aa7","segment_id":"index.md:a97c0f391117ef55","source_path":"index.md","text_hash":"a97c0f391117ef554586ed43255ab3ff0e15adcfc1829c62b6d359672c0bec93","text":" — Mention-based by default; owner can toggle ","translated":" — 默认基于提及;所有者可切换 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:02:09Z"}
{"cache_key":"b1d6847512a77312c1152a3f04694cdfc058f6d51d29f421a97d1f7799705076","segment_id":"help/index.md:d5d5bf0c0c86cfaa","source_path":"help/index.md","text_hash":"d5d5bf0c0c86cfaa612b370c3c796bb03e31b285fc928b5a690bfd156d177e88","text":"If you want a quick “get unstuck” flow, start","translated":"如果你想要一个快速的\"摆脱困境\"流程,请从这里开始","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T11:44:31Z"}
{"cache_key":"b1e93b43d06bcf0651c4bee0920f356e1f38bceca29db1936d449b4be99e77d2","segment_id":"index.md:8f6fb4eb7f42c0e2","source_path":"index.md","text_hash":"8f6fb4eb7f42c0e245e29e63f5b82cc3ba19852681d1ed9aed291f59cf75ec0e","text":"Security","translated":"安全","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:56Z"}
{"cache_key":"b212246fea49637bc0db899bd39dff2b1762ecf0d8cac3ec6160a8cd4c4da860","segment_id":"start/wizard.md:1f01936efef6e09c","source_path":"start/wizard.md","text_hash":"1f01936efef6e09cd29c9b1a9b6a64c1fcdb35682c9cf25db02dfde331f83fa7","text":" if present or prompts for a key, then saves it to ","translated":" (如果存在)或提示输入密钥,然后保存到 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:42:29Z"}
{"cache_key":"b240cb7927de51aca09fb318798ffd79fe597965722be259f799a2002cbe0f43","segment_id":"start/getting-started.md:4ea5ee68fea05586","source_path":"start/getting-started.md","text_hash":"4ea5ee68fea05586106890ded5733820bb77d919cda27bc4b8139b7cd33b8889","text":" gateway","translated":" Gateway","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:02Z"}
@@ -1002,6 +1031,7 @@
{"cache_key":"c2802148a29fff6480dd7c4126df1d7787f83156807ce1f6e0abb05d2e0a7863","segment_id":"index.md:6e0f6eca4ff17d33","source_path":"index.md","text_hash":"6e0f6eca4ff17d3377c1c3e8e1f73457553ad3b9cfcd5e4f2b94cfb1028b6234","text":"iOS app","translated":"iOS 应用","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:36Z"}
{"cache_key":"c2acf62bea34b4557cbab8b7ceadd55c5cf37516c124b93afc1b8e9f08d62ab0","segment_id":"index.md:39bbb719fa2b9d22","source_path":"index.md","text_hash":"39bbb719fa2b9d2251039cbf2cd072e1120a414278263e2f11d99af0236c4262","text":"Groups","translated":"群组","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:21Z"}
{"cache_key":"c2e74d237df6614199282b8822741be509ff03e31b7319f3184bb2537860e8a9","segment_id":"index.md:bf084dc7b82e1e62","source_path":"index.md","text_hash":"bf084dc7b82e1e62c63727b13451d1eba2269860e27db290d2d5908d7ade0529","text":" — Pairs as a node and exposes Canvas + Chat + Camera","translated":" — 作为节点配对并提供 Canvas + 聊天 + 相机","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:02:43Z"}
{"cache_key":"c2e91312acca3baab311ea42b62c2fcea1bf5ec3fe9f444cc63f3e00c3b1da02","segment_id":"environment.md:7c3c58e5e1838eae","source_path":"environment.md","text_hash":"7c3c58e5e1838eaeec35be812eb7edad1525e370c3420121710cc1d5fb627c1b","text":"), applied only for missing expected keys.\n\nIf the config file is missing entirely, step 4 is skipped; shell import still runs if enabled.","translated":"),仅对缺失的预期密钥应用。\n\n如果配置文件完全不存在则跳过第 4 步如果已启用shell 导入仍会运行。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:16:42Z"}
{"cache_key":"c335a0e455574c0e23a45c10a55511400b6168c38aa7d8e43521b1c8650e58f9","segment_id":"environment.md:frontmatter:read_when:1","source_path":"environment.md:frontmatter:read_when:1","text_hash":"a3a2d99a99de98220c8e0296d6f4e4b2a34024916bd2379d1b3b9179c8fae46f","text":"You are debugging missing API keys in the Gateway","translated":"您正在调试 Gateway 中缺失的 API 密钥","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:40:15Z"}
{"cache_key":"c34f893f16dcd3b37a3752585df805b44212829550f3d82cb5f539fdb50a5a50","segment_id":"environment.md:87e89abb4c1c551f","source_path":"environment.md","text_hash":"87e89abb4c1c551fe08d355d097f18b8de78edca5f556997085681662fce8eed","text":"Config ","translated":"配置 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:25:46Z"}
{"cache_key":"c359a69d5e0e9e6470f36436f1b27a946ef28ef1069e7b7d59e0ea3132f6003c","segment_id":"start/wizard.md:4cd440e57b28aba7","source_path":"start/wizard.md","text_hash":"4cd440e57b28aba7f789ba11d0bb5837f09937ba45bab9a80b9a6a980894250e","text":"Followup reconfiguration:","translated":"后续重新配置:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:39:15Z"}
@@ -1055,6 +1085,7 @@
{"cache_key":"cc8f5dcfbe51a4638b375d367381be97b79d012b56b2c7eadd2e38d164cdd177","segment_id":"start/wizard.md:e18251a039a6b735","source_path":"start/wizard.md","text_hash":"e18251a039a6b7353675decc475898bfdb91d3bd9d37e83c8447d0359b8711c3","text":"Non-interactive flags: ","translated":"非交互标志: ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:47:05Z"}
{"cache_key":"cc906618700533ea8dd9d752b8e2ef28ffb8707654a557d7cef1b867cdd57f1a","segment_id":"index.md:ceee4f2088b9d5ba","source_path":"index.md","text_hash":"ceee4f2088b9d5ba7d417bac7395003acfbcef576fd4cc1dd3063972f038218a","text":"The name","translated":"名称","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:54:01Z"}
{"cache_key":"cc93bf5458542a509cb8460472bf3269d769fe1cdee6201ab736c4b5460d64d5","segment_id":"start/wizard.md:4bba41aa0148ebb4","source_path":"start/wizard.md","text_hash":"4bba41aa0148ebb49b33763f1b38a983af7c0a4dd22fff07d3cf94fdcb96ecd3","text":"Linux (and Windows via WSL2): systemd user unit","translated":"Linux以及通过 WSL2 的 Windowssystemd 用户单元","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:45:17Z"}
{"cache_key":"ccd10d490dbeb4a1e0c3b7b4ccf7653af6ff78a7d498755c92bf4a6c24b2aacd","segment_id":"environment.md:a42cc4a7174c83a8","source_path":"environment.md","text_hash":"a42cc4a7174c83a853752b3e74cb001a234f3eca099688fdf0dd2540c60bb1e2","text":" expected keys:","translated":" 预期密钥:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:12:51Z"}
{"cache_key":"cd2d7cce6f1c10e008e8efe49ecf02b6ac401d686667986409f7e6796e9f1140","segment_id":"environment.md:45ca56d179d4788c","source_path":"environment.md","text_hash":"45ca56d179d4788c55ba9f7653b376d62e7faa738e92259e3d4f6f5c1b554f28","text":"Related","translated":"相关内容","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:22:44Z"}
{"cache_key":"cd4cdcf85e185ce70df30cbda64fb2d77baa6a6c989e67cde3f80315c06b3839","segment_id":"index.md:45e6d69dbe995a36","source_path":"index.md","text_hash":"45e6d69dbe995a36f7bc20755eff4eb4d2afaaedbcac4668ab62540c57219f32","text":"macOS app","translated":"macOS 应用","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:33Z"}
{"cache_key":"cd82c395857efd6e374fec3ad86de5dd8989415770d38a86d8a1980cd372b7f5","segment_id":"start/wizard.md:c4e77a12a2c0b664","source_path":"start/wizard.md","text_hash":"c4e77a12a2c0b664f398de857da71528f66ffb4a70e65769897dcc7147167b2c","text":" or use allowlists.","translated":" 批准,或使用允许名单。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:45:05Z"}
@@ -1067,6 +1098,7 @@
{"cache_key":"ce3c2713f373fff6ebab9c70141debe3262d0a7ff6214fd146fa277b67c1ab3e","segment_id":"start/wizard.md:bd8a6e0ff884f51d","source_path":"start/wizard.md","text_hash":"bd8a6e0ff884f51d6a4a9b70f4680033876871936c72cf8af5df4e4b2836c75c","text":"Wizard runs a model check and warns if the configured model is unknown or missing auth.","translated":"向导会运行模型检查,如果配置的模型未知或缺少认证则发出警告。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:43:24Z"}
{"cache_key":"ce618de323766f3aa222b534fb69a1502c03699a6b57e801e6f1a1b3c32d3431","segment_id":"index.md:9abe8e9025013e78","source_path":"index.md","text_hash":"9abe8e9025013e78a6bf2913f8c20ee43134ad001ce29ced89e2af9c07096d8f","text":"Media: images","translated":"媒体:图片","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:25Z"}
{"cache_key":"ce62c5006939b21f7a2d236f9cdb545ce653778800504e85668fe99075067cbf","segment_id":"environment.md:6db0742daaf9f191","source_path":"environment.md","text_hash":"6db0742daaf9f191ab7816d2c9d317b1ea1693453a8c63b95af8b01477e0f5bb","text":" runs your login shell and imports only ","translated":" 运行你的登录 shell并仅导入 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:12:47Z"}
{"cache_key":"cebc7667fbb15ccecd6359fe5ec38ae1ad00df26f18e56e7debd760a47d30a94","segment_id":"start/getting-started.md:2fa27cf15c3773de","source_path":"start/getting-started.md","text_hash":"2fa27cf15c3773deb54ae880d0f3250d86ef8c316abe07373f5f6a16df7afbed","text":" is also supported.","translated":" 也受支持。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:08Z"}
{"cache_key":"cf001b0403d7ae959797460c96aa4da24818c662362595f2da0be349caeb6a09","segment_id":"index.md:cda454f61dfcac70","source_path":"index.md","text_hash":"cda454f61dfcac7007a9edc538f9f58cf38caa0652e253975979308162bccc53","text":"Gateway configuration","translated":"Gateway 配置","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:00:31Z"}
{"cache_key":"cf9fc66b44905a0c47ca04f98d6e6507821789844f1e97ca2026f7df6e5b1451","segment_id":"environment.md:f7e239a42b7cd986","source_path":"environment.md","text_hash":"f7e239a42b7cd986a1558fed234e975ed2e96e9d37cf0a93f381778c461c89dd","text":"OpenClaw pulls environment variables from multiple sources. The rule is ","translated":"OpenClaw 从多个来源拉取 环境变量。规则是 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:57:11Z"}
{"cache_key":"cfc26997d872d590a2aba69f0aba6f704354d3aea9aa3bd433693ca7182cacdc","segment_id":"start/getting-started.md:1093115897879aa3","source_path":"start/getting-started.md","text_hash":"1093115897879aa3ad9511a1dc2850929cfb60ba45ec741605f69f5d20203472","text":"Runtime","translated":"运行时","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:17Z"}
@@ -1078,6 +1110,7 @@
{"cache_key":"d0f76abf14b1216bff9974f7e507a3c2a43f331f1ebd805279843692ae78f662","segment_id":"index.md:5cf9ea2e20780551","source_path":"index.md","text_hash":"5cf9ea2e2078055129b38cfbc394142ca6ca41556bd6e31cbd527425647c1d1e","text":"One Gateway per host (recommended)","translated":"每台主机一个 Gateway推荐","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:48:30Z"}
{"cache_key":"d12af03e20c20a4ebdcdbf4c32f52081339c0aa7bd1bb44b311875547bb39918","segment_id":"start/wizard.md:14a01a1b76ad6311","source_path":"start/wizard.md","text_hash":"14a01a1b76ad63111eb126c1d124a893abcb5cc90fe893825a9c96362112ab4f","text":" adds gateway health probes to status output (requires a reachable gateway).","translated":" 将 Gateway 健康探测添加到状态输出中(需要可达的 Gateway。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:45:41Z"}
{"cache_key":"d1818c531bc4e1cca14e64f751cf8698cb0701a745fb3da03b37b4fd7129c18b","segment_id":"start/wizard.md:6d0323ac97e5a313","source_path":"start/wizard.md","text_hash":"6d0323ac97e5a3136bae41278bfd46f5985969ee57dea5f25d7faa78bb01c87e","text":" when model is unset or ","translated":" (当模型未设置或为 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:42:24Z"}
{"cache_key":"d181ecac73ffcad6ec7afe0e692144db4ea470fa5de3a2d218f4b8127ad7d588","segment_id":"environment.md:28e19c6e69c7a2aa","source_path":"environment.md","text_hash":"28e19c6e69c7a2aa071951dda3ff0a11ca178e3fb295dae8d6ed7dcc994434a4","text":" for full details.","translated":" 了解完整详情。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:58:28Z"}
{"cache_key":"d1a349d8c1859f2d1c00367b86704fa95d4168c8615ada60834a6890215d1f58","segment_id":"index.md:3c064c83b8d244fe","source_path":"index.md","text_hash":"3c064c83b8d244fef61e5fd8ce5f070b857a3578a71745e61eea02892788c020","text":" — Anthropic (Claude Pro/Max) + OpenAI (ChatGPT/Codex) via OAuth","translated":" —— 通过 OAuth 支持 AnthropicClaude Pro/Max+ OpenAIChatGPT/Codex","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:50:28Z"}
{"cache_key":"d1d30ee69fb8519a966ebbb5cb51d2be029399b2951ef296b23f96d3fea4bc3a","segment_id":"start/wizard.md:3fad3d2e2c01a9ea","source_path":"start/wizard.md","text_hash":"3fad3d2e2c01a9ea3a66cbcb1b05a0d5982e3665cf0e1ec6dee0e031e83137e1","text":"Reads the available skills and checks requirements.","translated":"读取可用技能并检查依赖条件。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:45:43Z"}
{"cache_key":"d234d82b06f65337a5ab45e775d0f0abda696d4e04e6115c6a042853b3b11ca4","segment_id":"index.md:084514e91f37c3ce","source_path":"index.md","text_hash":"084514e91f37c3ce85360e26c70b77fdc95f0d3551ce309db96fbcf956a53b01","text":"Dashboard (browser Control UI)","translated":"仪表板(浏览器控制界面)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:48:04Z"}
@@ -1118,6 +1151,7 @@
{"cache_key":"d8fe9f40df201863d43f4937a52bac7d14019fae82150f1191fe4bb66819d827","segment_id":"help/index.md:3c33340bd23b8db8","source_path":"help/index.md","text_hash":"3c33340bd23b8db89f18fe7d05a954738c0dd5ba9623cf6bdb7bb5d1a3729cfc","text":"FAQ (concepts)","translated":"常见问题(概念)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:11:34Z"}
{"cache_key":"d93001811d4774893fac9a800d8e9c14259b90fc5ed85a3e5e6d381bfb591846","segment_id":"index.md:32ebb1abcc1c601c","source_path":"index.md","text_hash":"32ebb1abcc1c601ceb9c4e3c4faba0caa5b85bb98c4f1e6612c40faa528a91c9","text":" (","translated":" ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:28:10Z"}
{"cache_key":"d952c24b47cb7a9f69823c976f2f5e103fdc731a8bd74cae1436d86f420022df","segment_id":"environment.md:frontmatter:read_when:1","source_path":"environment.md:frontmatter:read_when:1","text_hash":"a3a2d99a99de98220c8e0296d6f4e4b2a34024916bd2379d1b3b9179c8fae46f","text":"You are debugging missing API keys in the Gateway","translated":"你正在调试 Gateway 中缺失的 API 密钥","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:21:50Z"}
{"cache_key":"d963cbde28040dde98de9fd8684bb5552ff2ba0e13e2c9921a8e36d90d7237a4","segment_id":"index.md:58d30d963f28264b","source_path":"index.md","text_hash":"58d30d963f28264bd9ba0e2d4c07c2c43c0ac1c1609c25b3fccf475eebf41727","text":"Skills config","translated":"技能配置","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:15Z"}
{"cache_key":"d972ebc19ef87492ca8c11159fd6342cced6b4e19743d79d81ae33fafe35bbd8","segment_id":"environment.md:f7e239a42b7cd986","source_path":"environment.md","text_hash":"f7e239a42b7cd986a1558fed234e975ed2e96e9d37cf0a93f381778c461c89dd","text":"OpenClaw pulls environment variables from multiple sources. The rule is ","translated":"OpenClaw 从多个来源获取环境变量。规则是 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:21:57Z"}
{"cache_key":"d9923f60f9531ccaaefa870fa682febfe862bf9a38ced5baa99ea8637d7fc5ae","segment_id":"start/getting-started.md:63d3b285bad7d501","source_path":"start/getting-started.md","text_hash":"63d3b285bad7d5015cea4d6e62f972e83221dfce48c6919bd536c5e894a6607d","text":" set an API key (wizard can store it for service use). ","translated":" 设置 API 密钥(向导可以将其存储以供服务使用)。 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:32Z"}
{"cache_key":"d9b22590788b6c0abf9a15102d23d2aeb6608cf4acc0339e69be4e52ae38af48","segment_id":"index.md:f9b8279bc46e847b","source_path":"index.md","text_hash":"f9b8279bc46e847bfcc47b8701fd5c5dc27baa304d5add8278a7f97925c3ec13","text":"Mattermost (plugin)","translated":"Mattermost插件","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:18Z"}
@@ -1133,6 +1167,7 @@
{"cache_key":"dbec24d595565c4c294a91f556c491976ccdeb4f7976d9258e6420af47259608","segment_id":"help/index.md:24669ff48290c187","source_path":"help/index.md","text_hash":"24669ff48290c1875d8067bbd241e8a55444839747bffb8ab99f3a34ef248436","text":"Doctor","translated":"诊断","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:11:29Z"}
{"cache_key":"dbeeb5b2ad003e4152107ceade1290b2001163df5f2fb93a792c8c9d94cec345","segment_id":"start/getting-started.md:922f3f28b57bdd14","source_path":"start/getting-started.md","text_hash":"922f3f28b57bdd146b8892adf494a28a0969d5eaf21333bfdb314db2eb6c8da8","text":"Installer options (install method, non-interactive, from GitHub): ","translated":"安装选项(安装方式、非交互式、从 GitHub 安装): ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:35:48Z"}
{"cache_key":"dbf5bae2a9b91c346475334bdb1294ace20ee07ca1e471c488c5311579ef37ab","segment_id":"index.md:b0d125182029e6c5","source_path":"index.md","text_hash":"b0d125182029e6c500cbcc81011341df77de8fe24d9e80190c32be390c916ec2","text":"🤖 ","translated":"🤖 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:49:58Z"}
{"cache_key":"dc16a7d72c37b5b48ed3034555c195ab7432617c1a8182e92d98e23f1051f615","segment_id":"index.md:f14185309c5ab262","source_path":"index.md","text_hash":"f14185309c5ab26233fde49831f9fc27857a6e7ac200e91dc247ae3e3b74be27","text":"Companion apps:","translated":"伴侣应用:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:53:30Z"}
{"cache_key":"dc745b075f86ec95e5a22fbb2ba14c5a6f2c00911dfa570cbe2f5123627e887d","segment_id":"environment.md:f15f5f9f4ef4d668","source_path":"environment.md","text_hash":"f15f5f9f4ef4d6688876c894f8eba251ed1db6eaf2209084028d43c9e76a8ba1","text":" (aka ","translated":" (即 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:40:44Z"}
{"cache_key":"dc8c80f84e5339af07824daa81e39f2801c9d6beb851b21e632b3eb6ddf79749","segment_id":"start/wizard.md:4b2a013a2a09958e","source_path":"start/wizard.md","text_hash":"4b2a013a2a09958e251e8998bdfa5fd89cc1c69abb1273fe2c1522cf54363cc6","text":"JVM builds require ","translated":"JVM 构建需要 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:48:09Z"}
{"cache_key":"dcb357452715d4a3fee760c79dfdee6719f235e48d176456a053646ffae10f44","segment_id":"environment.md:d08a8493f686363a","source_path":"environment.md","text_hash":"d08a8493f686363a78b913d45ebfbd87a3768d1c77b70f23b1fdade3c066e481","text":"Shell env import","translated":"Shell 环境导入","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:58:04Z"}
@@ -1182,6 +1217,7 @@
{"cache_key":"e5b4eab0ca38617f4b76c99dc5fa36151812c02576b33f954a56cf5f77703696","segment_id":"index.md:bf0e823c81b87c5d","source_path":"index.md","text_hash":"bf0e823c81b87c5de79676155debf20a29b52d6d7eb7e77deda73a56d0afbaaa","text":"🧠 ","translated":"🧠 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:30:08Z"}
{"cache_key":"e5be378ff2d92da3de35b20680f90f5d1aa0a98ce205139d6fcaeac91ef06f65","segment_id":"index.md:9bcda844990ec646","source_path":"index.md","text_hash":"9bcda844990ec646b3b6ee63cbdf10f70b0403727dea3b5ab601ca55e3949db9","text":" for node WebViews; see ","translated":" 用于节点 WebView参见 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:49:04Z"}
{"cache_key":"e5d655052f08f79672770734c9717dc24a5a9359defba7095dc7a9e2cf9e801b","segment_id":"start/wizard.md:bba52d8bacabbacc","source_path":"start/wizard.md","text_hash":"bba52d8bacabbacc510a1902b4eb35435f691903eb2db22fd110d41eadedec8d","text":" exists, the wizard can reuse it.","translated":" 存在,向导可以复用它。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:42:13Z"}
{"cache_key":"e5febac01358bd99e804e54a33e30d0d88ea12bcab990c3e29c66351fb5a598f","segment_id":"index.md:41dc1288a547d7d1","source_path":"index.md","text_hash":"41dc1288a547d7d155c2d7b831e8cff388e12ab9d77d4c24cd0757ed47e9e209","text":" — Block streaming + Telegram draft streaming details (","translated":" —— 块流式传输 + Telegram 草稿流式传输详情(","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:50:10Z"}
{"cache_key":"e628a7773be8d41e10dc53dcb383a11096e0573ec6b470aa13d2a14adcefb8e7","segment_id":"start/wizard.md:e3ba8a2959965f9c","source_path":"start/wizard.md","text_hash":"e3ba8a2959965f9c8360537e304016b2f75d561bdb03655a42adb02ce75a0e3f","text":"Default workspaces follow ","translated":"默认工作区遵循 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:46:57Z"}
{"cache_key":"e62ed5670f8283396dcc6a81182cda94667ff98973f153e4c86a04db364a4895","segment_id":"start/wizard.md:a8dbd136ed7c8e55","source_path":"start/wizard.md","text_hash":"a8dbd136ed7c8e55f9c0ae6e5acd2576d485f642d964a61f3693afc1c0c4ffdf","text":": uses ","translated":":使用 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:41:50Z"}
{"cache_key":"e66b34ec94f9a9c10b99b098ad8806551356222f1ac50f6fec7d719991faceee","segment_id":"start/wizard.md:c36d819e7bc6d2b7","source_path":"start/wizard.md","text_hash":"c36d819e7bc6d2b7da51394411c733db89c395987885ca6770167a3b9bc45c3c","text":"Use ","translated":"使用 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:46:45Z"}
@@ -1189,6 +1225,7 @@
{"cache_key":"e6b4ca13a3b7e39f521b1aadbb4f54f37875d228cd918c6406bd6519d5c7b6c8","segment_id":"index.md:6638cf2301d3109d","source_path":"index.md","text_hash":"6638cf2301d3109da66a44ee3506fbd35b29773fa4ca33ff35eb838c21609e19","text":"Features (high level)","translated":"功能特性(概览)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:29:26Z"}
{"cache_key":"e6e2a9985237253e0478229a54f3693bc7b0472bc450d53a4122dc20dfe08b21","segment_id":"environment.md:6863067eb0a2c749","source_path":"environment.md","text_hash":"6863067eb0a2c7499425c6c189b2c88bac55ca754285a6ab1ef37b75b4cfad4d","text":"See ","translated":"参见 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:22:37Z"}
{"cache_key":"e6e456289628d5a4b6cbbc0fbb263d656ba7d49427a2009ce3c5f608b8505ea0","segment_id":"index.md:f0d82ba647b4a33d","source_path":"index.md","text_hash":"f0d82ba647b4a33da3008927253f9bed21e380f54eab0608b1136de4cbff1286","text":"OpenClaw bridges WhatsApp (via WhatsApp Web / Baileys), Telegram (Bot API / grammY), Discord (Bot API / channels.discord.js), and iMessage (imsg CLI) to coding agents like ","translated":"OpenClaw 将 WhatsApp通过 WhatsApp Web / Baileys、TelegramBot API / grammY、DiscordBot API / channels.discord.js和 iMessageimsg CLI桥接到编程 智能体,例如 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:58:56Z"}
{"cache_key":"e6ede2965d77195fa0296a69f8dc8beb3a2fee2b0264180126200d4adbaf4aa3","segment_id":"index.md:f14185309c5ab262","source_path":"index.md","text_hash":"f14185309c5ab26233fde49831f9fc27857a6e7ac200e91dc247ae3e3b74be27","text":"Companion apps:","translated":"伴侣应用:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:51Z"}
{"cache_key":"e723a0b2ab360a74b84f4ccd08fdc4cc1639b85d5178d45d8103a18069bd3d8d","segment_id":"start/getting-started.md:1b59a1d9fa6d392f","source_path":"start/getting-started.md","text_hash":"1b59a1d9fa6d392f1f68642200583ed0f7b372af2fbc7c01d5f7f00463e229de","text":" also bundles A2UI assets; if you need to run just that step, use ","translated":" 也会打包 A2UI 资源;如果您只需要运行该步骤,请使用 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:37:51Z"}
{"cache_key":"e7279b78eeb5dccdf1897af612ce9f34bbae6f6ad7d8a7fed40a48f2f59c2367","segment_id":"environment.md:frontmatter:summary","source_path":"environment.md:frontmatter:summary","text_hash":"78351223e7068721146d2de022fdf440c2866b2ee02fbbb50bf64369b999820b","text":"Where OpenClaw loads environment variables and the precedence order","translated":"OpenClaw 加载环境变量的位置及优先级顺序","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:25:05Z"}
{"cache_key":"e73887cca1549bd1acf945a50dfbd054a3ec1c87741be5a0a4381a4840ce13e5","segment_id":"index.md:1df4f2299f0d9cc4","source_path":"index.md","text_hash":"1df4f2299f0d9cc466fa05abeb2831e76e9f89583228174ffcd9af415fd869fe","text":"Send a test message (requires a running Gateway):","translated":"发送测试消息(需要运行中的 Gateway","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:03:17Z"}
@@ -1198,6 +1235,7 @@
{"cache_key":"e7bc8ffa042426610faa9c40c7191933bfda50deb769ef153580d4ab1c75d679","segment_id":"start/getting-started.md:cdb4ee2aea69cc6a","source_path":"start/getting-started.md","text_hash":"cdb4ee2aea69cc6a83331bbe96dc2caa9a299d21329efb0336fc02a82e1839a8","text":".","translated":"。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:34:45Z"}
{"cache_key":"e8160fc2a7763ac99c0933d4424a99f211b661b0d7649bb1d33f908c3ff5e0d2","segment_id":"start/getting-started.md:75e23f5184b23835","source_path":"start/getting-started.md","text_hash":"75e23f5184b23835efb6fdc64309312d3c9212d10566350b1a08ff7838c79d03","text":"2) Run the onboarding wizard (and install the service)","translated":"2运行上手引导向导并安装服务","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:35:55Z"}
{"cache_key":"e81fa8ea81e681a305d677a823722958c2fdf42c3afbf4149a2d5cdfc4c6e1df","segment_id":"index.md:4eb58187170dc141","source_path":"index.md","text_hash":"4eb58187170dc14198eacb534c8577bef076349c26f2479e1f6a2e31df8eb948","text":" — An AI, probably high on tokens","translated":" — 一个可能被令牌冲昏头脑的 AI","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:06:53Z"}
{"cache_key":"e83550368cc1a10f9407b5e8da39dc639896bb6669eca7e49f6107ff3a3c306c","segment_id":"environment.md:e4255aa4e8f9e525","source_path":"environment.md","text_hash":"e4255aa4e8f9e52571c9bc93336d0774bcd7f017b7b5297fb33b8e1986166f92","text":"), applied only for missing expected keys.","translated":"),仅对缺失的预期密钥应用。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:41:05Z"}
{"cache_key":"e87435d09fd52a520aeae4097eb83a149aeb498192ccfbdd63da8db57571de09","segment_id":"index.md:d08cec54f66c140c","source_path":"index.md","text_hash":"d08cec54f66c140c655a1631f6d629927c7c38b9c8bfa91c875df9bd3ad3c559","text":"OpenClaw assistant setup","translated":"OpenClaw 助手设置","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:12Z"}
{"cache_key":"e8a313447619fd5d7895acf1c467e347d47a8c35861910facf5ff08f88a8905e","segment_id":"index.md:5928d14b4d45263d","source_path":"index.md","text_hash":"5928d14b4d45263d4964dfd301c84ed2674ca8b4b698c5efeb88fb86076d2bf9","text":"🎮 ","translated":"🎮 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:29:39Z"}
{"cache_key":"e8bfa9777ff1ca6f2921ef47688f6ddb7d1a68c074dc27c7af195521940fb68f","segment_id":"help/index.md:frontmatter:summary","source_path":"help/index.md:frontmatter:summary","text_hash":"aece82a2d540ab1a9a21c7b038127cae6e9db2149491564bb1856b6f8999f205","text":"Help hub: common fixes, install sanity, and where to look when something breaks","translated":"帮助中心:常见修复方法、安装完整性检查,以及出现问题时的排查指南","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:39:25Z"}
@@ -1219,9 +1257,11 @@
{"cache_key":"eca7489e62538a4b68a7d49f3a67df1c6bad8affc75d6411f68ca1e81bef47b2","segment_id":"environment.md:f6b2ffe1d0d5f521","source_path":"environment.md","text_hash":"f6b2ffe1d0d5f521b76cabc67d6e96da2b1170eef8086d530558e9906a7f092d","text":"Models overview","translated":"模型概览","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:13:17Z"}
{"cache_key":"ecb4df64e132ff6212066948863adabaa06122c77d8971d5c924dc2e744df845","segment_id":"index.md:98a670e2fb754896","source_path":"index.md","text_hash":"98a670e2fb7548964e8b78b90fef47f679580423427bfd15e5869aca9681d0dd","text":"\"We're all just playing with our own prompts.\"","translated":"\"我们都只是在玩弄自己的提示词。\"","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:05:43Z"}
{"cache_key":"ecd894720faa37450014e0fe1630be8382cf6ec23cbb9bfe76bc4125495d8fa5","segment_id":"index.md:9adcfa4aa10a4e8b","source_path":"index.md","text_hash":"9adcfa4aa10a4e8b991a72ccc45261cd64f296aed5b257e4caf9c87aff1290a0","text":" — Send and receive images, audio, documents","translated":" — 收发图片、音频、文档","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:30:38Z"}
{"cache_key":"ed00b197a3002ae69c3929cf870943136f802bf17b2850a71b6091111b76527d","segment_id":"environment.md:28e19c6e69c7a2aa","source_path":"environment.md","text_hash":"28e19c6e69c7a2aa071951dda3ff0a11ca178e3fb295dae8d6ed7dcc994434a4","text":" for full details.","translated":" 了解完整详情。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:26:33Z"}
{"cache_key":"ed10c233aa195883b17061f166f647efac5a27535a85ce4d16fc90d40e138882","segment_id":"help/index.md:8cd501e1124c3047","source_path":"help/index.md","text_hash":"8cd501e1124c30473473c06e536a2d145e2a14a6d7dc1b99028ce818e14442e2","text":"Repairs:","translated":"修复:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:39:56Z"}
{"cache_key":"ed15427258ffbf85620a0c9c0c42deb7f37be17b7abeff5993a34962964f0e96","segment_id":"index.md:a194ca16424ddd17","source_path":"index.md","text_hash":"a194ca16424ddd17dacc45f1cbd7d0e41376d8955a7b6d02bc38c295cedd04e4","text":"RPC adapters","translated":"RPC 适配器","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:32:19Z"}
{"cache_key":"ed24753e60b54d629cfd978be87185f4772676322534432302319caf28452d29","segment_id":"index.md:ab201ddd7ab330d0","source_path":"index.md","text_hash":"ab201ddd7ab330d04be364c0ac14ce68c52073a0ee8d164a98c3034e91ce1848","text":" from the repo.","translated":" (在仓库目录中执行)。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:31:21Z"}
{"cache_key":"ed366700bbcca6548c3fb1f7dae544b9a9cb0c56d6b36ca8e26c4880bc4e5667","segment_id":"environment.md:28e19c6e69c7a2aa","source_path":"environment.md","text_hash":"28e19c6e69c7a2aa071951dda3ff0a11ca178e3fb295dae8d6ed7dcc994434a4","text":" for full details.","translated":" 了解完整详情。","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:17:08Z"}
{"cache_key":"ed37a2b1a8c3351a6c04bee81df6f507f306be344485e69eb87b3b2451aad89f","segment_id":"help/index.md:d3ef01b4a9c99103","source_path":"help/index.md","text_hash":"d3ef01b4a9c9910364c9b26b2499c8787a0461d2d24ab80376fff736a288b34c","text":"Logging","translated":"日志记录","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:24:47Z"}
{"cache_key":"ee3f1647acf674397ba7f7e1aee0f9972b9830f978b622695d8ab5360de5a496","segment_id":"index.md:255ce77b7a6a015f","source_path":"index.md","text_hash":"255ce77b7a6a015f8595868a524b67c134e8fb405f4584fdac020e57f4ccd5f6","text":"Loopback-first","translated":"回环优先","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:00:01Z"}
{"cache_key":"ee582fba5363de60fb2c00f9238f2ac9ad6dc7615694d8d23d24d88bf7ec13e1","segment_id":"environment.md:582967534d0f909d","source_path":"environment.md","text_hash":"582967534d0f909d196b97f9e6921342777aea87b46fa52df165389db1fb8ccf","text":" in ","translated":" 在 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:16:29Z"}
@@ -1251,6 +1291,7 @@
{"cache_key":"f2a0941718593a4be66a7a033a4117a7b3a502ef64b25fd7d6d3475c77dd5a1a","segment_id":"environment.md:87e89abb4c1c551f","source_path":"environment.md","text_hash":"87e89abb4c1c551fe08d355d097f18b8de78edca5f556997085681662fce8eed","text":"Config ","translated":"配置 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:16:24Z"}
{"cache_key":"f2a0c70d8b9f94722b586320f11c58339d30dd1fe8ff7250a962bb2db84d5ab4","segment_id":"environment.md:ffa63583dfa6706b","source_path":"environment.md","text_hash":"ffa63583dfa6706b87d284b86b0d693a161e4840aad2c5cf6b5d27c3b9621f7d","text":"missing","translated":"缺失的","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:58:09Z"}
{"cache_key":"f2c14989f888bbff9c7330f2d5b3892af3b900910840435595031590dc8248e3","segment_id":"environment.md:frontmatter:read_when:0","source_path":"environment.md:frontmatter:read_when:0","text_hash":"90fc0487bff88009979cff1061c1a882df8c3b1baa9c43538331d9d5dab15479","text":"You need to know which env vars are loaded, and in what order","translated":"你需要了解加载了哪些环境变量,以及它们的加载顺序","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:15:47Z"}
{"cache_key":"f2e6682f149332f775039f6c872837c04dbab51ea935ed4fe0085aa2a75cabe6","segment_id":"environment.md:a42cc4a7174c83a8","source_path":"environment.md","text_hash":"a42cc4a7174c83a853752b3e74cb001a234f3eca099688fdf0dd2540c60bb1e2","text":" expected keys:","translated":" 预期密钥:","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:41:18Z"}
{"cache_key":"f34789e2cb492196e8c057294dd98c5f9d4b8054d548a7b883a47f113efa1277","segment_id":"index.md:31365ab9453d6a1e","source_path":"index.md","text_hash":"31365ab9453d6a1ec03731622803d3b44f345b6afad08040d7f3e97290c77913","text":"do nothing","translated":"不做任何操作","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:51:55Z"}
{"cache_key":"f36f13a67a73f6768bfbf346d552067475ef4f8137e13edfd4f636e1b7ef2ef8","segment_id":"start/getting-started.md:649cfa2f76a80b42","source_path":"start/getting-started.md","text_hash":"649cfa2f76a80b42e1821c89edd348794689409dcdf619dcd10624fb577c676b","text":"not recommended","translated":"不推荐","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:36:21Z"}
{"cache_key":"f3701b1ce8ac7f8931cafd209250aa5ae388ecfdb0154dbbb21c03fd72ce5d08","segment_id":"help/index.md:729bc562eec2658b","source_path":"help/index.md","text_hash":"729bc562eec2658bd11ffdd522fe5277177dc73e86eaca7baac0b472a4d8f8b2","text":"If youre looking for conceptual questions (not “something broke”):","translated":"如果你在寻找概念性问题(不是\"某个东西坏了\"","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:11:29Z"}
@@ -1299,6 +1340,7 @@
{"cache_key":"fab1c40ef11182f7118f5528b5ba6ed5b5c169c37b302382107e3fbab3d200c1","segment_id":"index.md:3d8fed7c358b2ccf","source_path":"index.md","text_hash":"3d8fed7c358b2ccf225ee16857a0bb9b950fd414319749e0f6fff58c99fa5f22","text":"Subscription auth","translated":"订阅认证","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:50:25Z"}
{"cache_key":"fae191ae8b8380df30a34afd63fc9ba9125258cee9f76e625da9a9c41a858973","segment_id":"start/wizard.md:158ac20b77d1dc12","source_path":"start/wizard.md","text_hash":"158ac20b77d1dc1223a47723e75f03b49fe61d0a6d69de4c3bba9fdd4c123c04","text":" only configures the local client to connect to a Gateway elsewhere.\nIt does ","translated":" 仅配置本地客户端以连接到其他位置的 Gateway。它 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:40:36Z"}
{"cache_key":"faf6394b29b7de4f1af4a5c01405a2c33d4a1f8f58691915d75eedd3572b1d49","segment_id":"index.md:a7a19d4f14d001a5","source_path":"index.md","text_hash":"a7a19d4f14d001a56c27f68a13ff267859a407c7a9ab457c0945693c9067dd1c","text":"Configuration (optional)","translated":"配置(可选)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:03:21Z"}
{"cache_key":"fb2cd6f6a8308f9b9ad6cb30dec3a08de2db675e77bc696aeb2ddb3084c9a6c4","segment_id":"start/wizard.md:58d30d963f28264b","source_path":"start/wizard.md","text_hash":"58d30d963f28264bd9ba0e2d4c07c2c43c0ac1c1609c25b3fccf475eebf41727","text":"Skills config","translated":"技能配置","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:49:20Z"}
{"cache_key":"fc41f7c0ff1d82b20353a8a79f2da756675af014a48e1c36b3e693e2030aca4c","segment_id":"help/index.md:6201111b83a0cb5b","source_path":"help/index.md","text_hash":"6201111b83a0cb5b0922cb37cc442b9a40e24e3b1ce100a4bb204f4c63fd2ac0","text":" and ","translated":" 和 ","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:39:50Z"}
{"cache_key":"fc43ec1fbbcff82d8d617e73687d1fa0c004b3fa731fdb6c9a1b0825ac2df2f5","segment_id":"start/wizard.md:d80c4025fe9728d6","source_path":"start/wizard.md","text_hash":"d80c4025fe9728d67b8330bdbb25a3062c7748ae6779d348b66687d5a796550f","text":"Gateway wizard RPC","translated":"Gateway 向导 RPC","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T13:47:41Z"}
{"cache_key":"fc503e5044847f8c5412b75ba55ec912df5577a3bc37a7a975393684059d9c12","segment_id":"environment.md:61115f6649792387","source_path":"environment.md","text_hash":"61115f664979238731a390e84433a818965b7eaf1d38fa5b4b1507c33ef28c91","text":"Precedence (highest → lowest)","translated":"优先级(从高到低)","provider":"pi","model":"claude-opus-4-5","src_lang":"en","tgt_lang":"zh-CN","updated_at":"2026-02-01T12:16:00Z"}

View File

@@ -1,667 +0,0 @@
(() => {
if (document.getElementById("docs-chat-root")) return;
// Determine if we're on the docs site or embedded elsewhere
const hostname = window.location.hostname;
const isDocsSite = hostname === "localhost" || hostname === "127.0.0.1" ||
hostname.includes("docs.openclaw") || hostname.endsWith(".mintlify.app");
const assetsBase = isDocsSite ? "" : "https://docs.openclaw.ai";
const apiBase = "https://claw-api.openknot.ai/api";
// Load marked for markdown rendering (via CDN)
let markedReady = false;
const loadMarkdownLib = () => {
if (window.marked) {
markedReady = true;
return;
}
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/marked@15.0.6/marked.min.js";
script.onload = () => {
if (window.marked) {
markedReady = true;
}
};
script.onerror = () => console.warn("Failed to load marked library");
document.head.appendChild(script);
};
loadMarkdownLib();
// Markdown renderer with fallback before module loads
const renderMarkdown = (text) => {
if (markedReady && window.marked) {
// Configure marked for security: disable HTML pass-through
const html = window.marked.parse(text, { async: false, gfm: true, breaks: true });
// Open links in new tab by rewriting <a> tags
return html.replace(/<a href="/g, '<a target="_blank" rel="noopener" href="');
}
// Fallback: escape HTML and preserve newlines
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\n/g, "<br>");
};
const style = document.createElement("style");
style.textContent = `
#docs-chat-root { position: fixed; right: 20px; bottom: 20px; z-index: 9999; font-family: var(--font-body, system-ui, -apple-system, sans-serif); }
#docs-chat-root.docs-chat-expanded { right: 0; bottom: 0; top: 0; }
/* Thin scrollbar styling */
#docs-chat-root ::-webkit-scrollbar { width: 6px; height: 6px; }
#docs-chat-root ::-webkit-scrollbar-track { background: transparent; }
#docs-chat-root ::-webkit-scrollbar-thumb { background: var(--docs-chat-panel-border); border-radius: 3px; }
#docs-chat-root ::-webkit-scrollbar-thumb:hover { background: var(--docs-chat-muted); }
#docs-chat-root * { scrollbar-width: thin; scrollbar-color: var(--docs-chat-panel-border) transparent; }
:root {
--docs-chat-accent: var(--accent, #ff7d60);
--docs-chat-text: #1a1a1a;
--docs-chat-muted: #555;
--docs-chat-panel: rgba(255, 255, 255, 0.92);
--docs-chat-panel-border: rgba(0, 0, 0, 0.1);
--docs-chat-surface: rgba(250, 250, 250, 0.95);
--docs-chat-shadow: 0 18px 50px rgba(0,0,0,0.15);
--docs-chat-code-bg: rgba(0, 0, 0, 0.05);
--docs-chat-assistant-bg: #f5f5f5;
}
html[data-theme="dark"] {
--docs-chat-text: #e8e8e8;
--docs-chat-muted: #aaa;
--docs-chat-panel: rgba(28, 28, 30, 0.95);
--docs-chat-panel-border: rgba(255, 255, 255, 0.12);
--docs-chat-surface: rgba(38, 38, 40, 0.95);
--docs-chat-shadow: 0 18px 50px rgba(0,0,0,0.5);
--docs-chat-code-bg: rgba(255, 255, 255, 0.08);
--docs-chat-assistant-bg: #2a2a2c;
}
#docs-chat-button {
display: inline-flex;
align-items: center;
gap: 10px;
background: linear-gradient(140deg, rgba(255,90,54,0.25), rgba(255,90,54,0.06));
color: var(--docs-chat-text);
border: 1px solid rgba(255,90,54,0.4);
border-radius: 999px;
padding: 10px 14px;
cursor: pointer;
box-shadow: 0 8px 30px rgba(255,90,54, 0.08);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
font-family: var(--font-pixel, var(--font-body, system-ui, sans-serif));
}
#docs-chat-button span { font-weight: 600; letter-spacing: 0.04em; font-size: 14px; }
.docs-chat-logo { width: 20px; height: 20px; }
#docs-chat-panel {
width: min(440px, calc(100vw - 40px));
height: min(696px, calc(100vh - 80px));
background: var(--docs-chat-panel);
color: var(--docs-chat-text);
border-radius: 16px;
border: 1px solid var(--docs-chat-panel-border);
box-shadow: var(--docs-chat-shadow);
display: none;
flex-direction: column;
overflow: hidden;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
}
#docs-chat-root.docs-chat-expanded #docs-chat-panel {
width: min(512px, 100vw);
height: 100vh;
height: 100dvh;
border-radius: 18px 0 0 18px;
padding-top: env(safe-area-inset-top, 0);
padding-bottom: env(safe-area-inset-bottom, 0);
}
@media (max-width: 520px) {
#docs-chat-root.docs-chat-expanded #docs-chat-panel {
width: 100vw;
border-radius: 0;
}
#docs-chat-root.docs-chat-expanded { right: 0; left: 0; bottom: 0; top: 0; }
}
#docs-chat-header {
padding: 12px 14px;
font-weight: 600;
font-family: var(--font-pixel, var(--font-body, system-ui, sans-serif));
letter-spacing: 0.03em;
border-bottom: 1px solid var(--docs-chat-panel-border);
display: flex;
justify-content: space-between;
align-items: center;
}
#docs-chat-header-title { display: inline-flex; align-items: center; gap: 8px; }
#docs-chat-header-title span { color: var(--docs-chat-text); font-size: 15px; }
#docs-chat-header-actions { display: inline-flex; align-items: center; gap: 6px; }
.docs-chat-icon-button {
border: 1px solid var(--docs-chat-panel-border);
background: transparent;
color: inherit;
border-radius: 8px;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 16px;
line-height: 1;
}
#docs-chat-messages { flex: 1; padding: 12px 14px; overflow: auto; background: transparent; }
#docs-chat-input {
display: flex;
gap: 8px;
padding: 12px 14px;
border-top: 1px solid var(--docs-chat-panel-border);
background: var(--docs-chat-surface);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
#docs-chat-input textarea {
flex: 1;
resize: none;
border: 1px solid var(--docs-chat-panel-border);
border-radius: 10px;
padding: 9px 10px;
font-size: 14px;
line-height: 1.5;
font-family: inherit;
color: var(--docs-chat-text);
background: var(--docs-chat-surface);
min-height: 42px;
max-height: 120px;
overflow-y: auto;
}
#docs-chat-input textarea::placeholder { color: var(--docs-chat-muted); }
#docs-chat-send {
background: var(--docs-chat-accent);
color: #fff;
border: none;
border-radius: 10px;
padding: 8px 14px;
cursor: pointer;
font-weight: 600;
font-family: inherit;
font-size: 14px;
transition: opacity 0.15s ease;
}
#docs-chat-send:hover { opacity: 0.9; }
#docs-chat-send:active { opacity: 0.8; }
.docs-chat-bubble {
margin-bottom: 10px;
padding: 10px 14px;
border-radius: 12px;
font-size: 14px;
line-height: 1.6;
max-width: 92%;
}
.docs-chat-user {
background: rgba(255, 125, 96, 0.15);
color: var(--docs-chat-text);
border: 1px solid rgba(255, 125, 96, 0.3);
align-self: flex-end;
white-space: pre-wrap;
margin-left: auto;
}
html[data-theme="dark"] .docs-chat-user {
background: rgba(255, 125, 96, 0.18);
border-color: rgba(255, 125, 96, 0.35);
}
.docs-chat-assistant {
background: var(--docs-chat-assistant-bg);
color: var(--docs-chat-text);
border: 1px solid var(--docs-chat-panel-border);
}
/* Markdown content styling for chat bubbles */
.docs-chat-assistant p { margin: 0 0 10px 0; }
.docs-chat-assistant p:last-child { margin-bottom: 0; }
.docs-chat-assistant code {
background: var(--docs-chat-code-bg);
padding: 2px 6px;
border-radius: 5px;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 0.9em;
}
.docs-chat-assistant pre {
background: var(--docs-chat-code-bg);
padding: 10px 12px;
border-radius: 8px;
overflow-x: auto;
margin: 6px 0;
font-size: 0.9em;
max-width: 100%;
white-space: pre;
word-wrap: normal;
}
.docs-chat-assistant pre::-webkit-scrollbar-thumb { background: transparent; }
.docs-chat-assistant pre:hover::-webkit-scrollbar-thumb { background: var(--docs-chat-panel-border); }
@media (hover: none) {
.docs-chat-assistant pre { -webkit-overflow-scrolling: touch; }
.docs-chat-assistant pre::-webkit-scrollbar-thumb { background: var(--docs-chat-panel-border); }
}
.docs-chat-assistant pre code {
background: transparent;
padding: 0;
font-size: inherit;
white-space: pre;
word-wrap: normal;
display: block;
}
/* Compact single-line code blocks */
.docs-chat-assistant pre.compact {
margin: 4px 0;
padding: 6px 10px;
}
/* Longer code blocks with copy button need extra top padding */
.docs-chat-assistant pre:not(.compact) {
padding-top: 28px;
}
.docs-chat-assistant a {
color: var(--docs-chat-accent);
text-decoration: underline;
text-underline-offset: 2px;
}
.docs-chat-assistant a:hover { opacity: 0.8; }
.docs-chat-assistant ul, .docs-chat-assistant ol {
margin: 8px 0;
padding-left: 18px;
list-style: none;
}
.docs-chat-assistant li {
margin: 4px 0;
position: relative;
padding-left: 14px;
}
.docs-chat-assistant li::before {
content: "•";
position: absolute;
left: 0;
color: var(--docs-chat-muted);
}
.docs-chat-assistant strong { font-weight: 600; }
.docs-chat-assistant em { font-style: italic; }
.docs-chat-assistant h1, .docs-chat-assistant h2, .docs-chat-assistant h3 {
font-weight: 600;
margin: 12px 0 6px 0;
line-height: 1.3;
}
.docs-chat-assistant h1 { font-size: 1.2em; }
.docs-chat-assistant h2 { font-size: 1.1em; }
.docs-chat-assistant h3 { font-size: 1.05em; }
.docs-chat-assistant blockquote {
border-left: 3px solid var(--docs-chat-accent);
margin: 10px 0;
padding: 4px 12px;
color: var(--docs-chat-muted);
background: var(--docs-chat-code-bg);
border-radius: 0 6px 6px 0;
}
.docs-chat-assistant hr {
border: none;
height: 1px;
background: var(--docs-chat-panel-border);
margin: 12px 0;
}
/* Copy buttons */
.docs-chat-assistant { position: relative; padding-top: 28px; }
.docs-chat-copy-response {
position: absolute;
top: 8px;
right: 8px;
background: var(--docs-chat-surface);
border: 1px solid var(--docs-chat-panel-border);
border-radius: 5px;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
color: var(--docs-chat-muted);
transition: color 0.15s ease, background 0.15s ease;
}
.docs-chat-copy-response:hover {
color: var(--docs-chat-text);
background: var(--docs-chat-code-bg);
}
.docs-chat-assistant pre {
position: relative;
}
.docs-chat-copy-code {
position: absolute;
top: 8px;
right: 8px;
background: var(--docs-chat-surface);
border: 1px solid var(--docs-chat-panel-border);
border-radius: 4px;
padding: 3px 7px;
font-size: 10px;
cursor: pointer;
color: var(--docs-chat-muted);
transition: color 0.15s ease, background 0.15s ease;
z-index: 1;
}
.docs-chat-copy-code:hover {
color: var(--docs-chat-text);
background: var(--docs-chat-code-bg);
}
/* Resize handle - left edge of expanded panel */
#docs-chat-resize-handle {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 6px;
cursor: ew-resize;
z-index: 10;
display: none;
}
#docs-chat-root.docs-chat-expanded #docs-chat-resize-handle { display: block; }
#docs-chat-resize-handle::after {
content: "";
position: absolute;
left: 1px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 40px;
border-radius: 2px;
background: var(--docs-chat-panel-border);
opacity: 0;
transition: opacity 0.15s ease, background 0.15s ease;
}
#docs-chat-resize-handle:hover::after,
#docs-chat-resize-handle.docs-chat-dragging::after {
opacity: 1;
background: var(--docs-chat-accent);
}
@media (max-width: 520px) {
#docs-chat-resize-handle { display: none !important; }
}
`;
document.head.appendChild(style);
const root = document.createElement("div");
root.id = "docs-chat-root";
const button = document.createElement("button");
button.id = "docs-chat-button";
button.type = "button";
button.innerHTML =
`<img class="docs-chat-logo" src="${assetsBase}/assets/pixel-lobster.svg" alt="OpenClaw">` +
`<span>Ask Molty</span>`;
const panel = document.createElement("div");
panel.id = "docs-chat-panel";
panel.style.display = "none";
// Resize handle for expandable sidebar width (desktop only)
const resizeHandle = document.createElement("div");
resizeHandle.id = "docs-chat-resize-handle";
const header = document.createElement("div");
header.id = "docs-chat-header";
header.innerHTML =
`<div id="docs-chat-header-title">` +
`<img class="docs-chat-logo" src="${assetsBase}/assets/pixel-lobster.svg" alt="OpenClaw">` +
`<span>OpenClaw Docs</span>` +
`</div>` +
`<div id="docs-chat-header-actions"></div>`;
const headerActions = header.querySelector("#docs-chat-header-actions");
const expand = document.createElement("button");
expand.type = "button";
expand.className = "docs-chat-icon-button";
expand.setAttribute("aria-label", "Expand");
expand.textContent = "⤢";
const clear = document.createElement("button");
clear.type = "button";
clear.className = "docs-chat-icon-button";
clear.setAttribute("aria-label", "Clear chat");
clear.textContent = "⌫";
const close = document.createElement("button");
close.type = "button";
close.className = "docs-chat-icon-button";
close.setAttribute("aria-label", "Close");
close.textContent = "×";
headerActions.appendChild(expand);
headerActions.appendChild(clear);
headerActions.appendChild(close);
const messages = document.createElement("div");
messages.id = "docs-chat-messages";
const inputWrap = document.createElement("div");
inputWrap.id = "docs-chat-input";
const textarea = document.createElement("textarea");
textarea.rows = 1;
textarea.placeholder = "Ask about OpenClaw Docs...";
// Auto-expand textarea as user types (up to max-height set in CSS)
const autoExpand = () => {
textarea.style.height = "auto";
textarea.style.height = Math.min(textarea.scrollHeight, 224) + "px";
};
textarea.addEventListener("input", autoExpand);
const send = document.createElement("button");
send.id = "docs-chat-send";
send.type = "button";
send.textContent = "Send";
inputWrap.appendChild(textarea);
inputWrap.appendChild(send);
panel.appendChild(resizeHandle);
panel.appendChild(header);
panel.appendChild(messages);
panel.appendChild(inputWrap);
root.appendChild(button);
root.appendChild(panel);
document.body.appendChild(root);
// Add copy buttons to assistant bubble
const addCopyButtons = (bubble, rawText) => {
// Add copy response button
const copyResponse = document.createElement("button");
copyResponse.className = "docs-chat-copy-response";
copyResponse.textContent = "Copy";
copyResponse.type = "button";
copyResponse.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(rawText);
copyResponse.textContent = "Copied!";
setTimeout(() => (copyResponse.textContent = "Copy"), 1500);
} catch (e) {
copyResponse.textContent = "Failed";
}
});
bubble.appendChild(copyResponse);
// Add copy buttons to code blocks (skip short/single-line blocks)
bubble.querySelectorAll("pre").forEach((pre) => {
const code = pre.querySelector("code") || pre;
const text = code.textContent || "";
const lineCount = text.split("\n").length;
const isShort = lineCount <= 2 && text.length < 100;
if (isShort) {
pre.classList.add("compact");
return; // Skip copy button for compact blocks
}
const copyCode = document.createElement("button");
copyCode.className = "docs-chat-copy-code";
copyCode.textContent = "Copy";
copyCode.type = "button";
copyCode.addEventListener("click", async (e) => {
e.stopPropagation();
try {
await navigator.clipboard.writeText(text);
copyCode.textContent = "Copied!";
setTimeout(() => (copyCode.textContent = "Copy"), 1500);
} catch (err) {
copyCode.textContent = "Failed";
}
});
pre.appendChild(copyCode);
});
};
const addBubble = (text, role, isMarkdown = false) => {
const bubble = document.createElement("div");
bubble.className =
"docs-chat-bubble " +
(role === "user" ? "docs-chat-user" : "docs-chat-assistant");
if (isMarkdown && role === "assistant") {
bubble.innerHTML = renderMarkdown(text);
} else {
bubble.textContent = text;
}
messages.appendChild(bubble);
messages.scrollTop = messages.scrollHeight;
return bubble;
};
let isExpanded = false;
let customWidth = null; // User-set width via drag
const MIN_WIDTH = 320;
const MAX_WIDTH = 800;
// Drag-to-resize logic
let isDragging = false;
let startX, startWidth;
resizeHandle.addEventListener("mousedown", (e) => {
if (!isExpanded) return;
isDragging = true;
startX = e.clientX;
startWidth = panel.offsetWidth;
resizeHandle.classList.add("docs-chat-dragging");
document.body.style.cursor = "ew-resize";
document.body.style.userSelect = "none";
e.preventDefault();
});
document.addEventListener("mousemove", (e) => {
if (!isDragging) return;
// Panel is on right, so dragging left increases width
const delta = startX - e.clientX;
const newWidth = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, startWidth + delta));
customWidth = newWidth;
panel.style.width = newWidth + "px";
});
document.addEventListener("mouseup", () => {
if (!isDragging) return;
isDragging = false;
resizeHandle.classList.remove("docs-chat-dragging");
document.body.style.cursor = "";
document.body.style.userSelect = "";
});
const setOpen = (isOpen) => {
panel.style.display = isOpen ? "flex" : "none";
button.style.display = isOpen ? "none" : "inline-flex";
root.classList.toggle("docs-chat-expanded", isOpen && isExpanded);
if (!isOpen) {
panel.style.width = ""; // Reset to CSS default when closed
} else if (isExpanded && customWidth) {
panel.style.width = customWidth + "px";
}
if (isOpen) textarea.focus();
};
const setExpanded = (next) => {
isExpanded = next;
expand.textContent = isExpanded ? "⤡" : "⤢";
expand.setAttribute("aria-label", isExpanded ? "Collapse" : "Expand");
if (panel.style.display !== "none") {
root.classList.toggle("docs-chat-expanded", isExpanded);
if (isExpanded && customWidth) {
panel.style.width = customWidth + "px";
} else if (!isExpanded) {
panel.style.width = ""; // Reset to CSS default
}
}
};
button.addEventListener("click", () => setOpen(true));
expand.addEventListener("click", () => setExpanded(!isExpanded));
clear.addEventListener("click", () => {
messages.innerHTML = "";
});
close.addEventListener("click", () => {
setOpen(false);
root.classList.remove("docs-chat-expanded");
});
const sendMessage = async () => {
const text = textarea.value.trim();
if (!text) return;
textarea.value = "";
textarea.style.height = "auto"; // Reset height after sending
addBubble(text, "user");
const assistantBubble = addBubble("...", "assistant");
assistantBubble.innerHTML = "";
let fullText = "";
try {
const response = await fetch(`${apiBase}/chat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: text }),
});
// Handle rate limiting
if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After") || "60";
fullText = `You're asking questions too quickly. Please wait ${retryAfter} seconds before trying again.`;
assistantBubble.innerHTML = renderMarkdown(fullText);
addCopyButtons(assistantBubble, fullText);
return;
}
// Handle other errors
if (!response.ok) {
try {
const errorData = await response.json();
fullText = errorData.error || "Something went wrong. Please try again.";
} catch {
fullText = "Something went wrong. Please try again.";
}
assistantBubble.innerHTML = renderMarkdown(fullText);
addCopyButtons(assistantBubble, fullText);
return;
}
if (!response.body) {
fullText = await response.text();
assistantBubble.innerHTML = renderMarkdown(fullText);
addCopyButtons(assistantBubble, fullText);
return;
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
fullText += decoder.decode(value, { stream: true });
// Re-render markdown on each chunk for live preview
assistantBubble.innerHTML = renderMarkdown(fullText);
messages.scrollTop = messages.scrollHeight;
}
// Flush any remaining buffered bytes (partial UTF-8 sequences)
fullText += decoder.decode();
assistantBubble.innerHTML = renderMarkdown(fullText);
// Add copy buttons after streaming completes
addCopyButtons(assistantBubble, fullText);
} catch (err) {
fullText = "Failed to reach docs chat API.";
assistantBubble.innerHTML = renderMarkdown(fullText);
addCopyButtons(assistantBubble, fullText);
}
};
send.addEventListener("click", sendMessage);
textarea.addEventListener("keydown", (event) => {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
sendMessage();
}
});
})();

View File

@@ -23,7 +23,7 @@ cron is the mechanism.
- Jobs persist under `~/.openclaw/cron/` so restarts dont lose schedules.
- Two execution styles:
- **Main session**: enqueue a system event, then run on the next heartbeat.
- **Isolated**: run a dedicated agent turn in `cron:<jobId>`, with delivery (announce by default or none).
- **Isolated**: run a dedicated agent turn in `cron:<jobId>`, optionally deliver output.
- Wakeups are first-class: a job can request “wake now” vs “next heartbeat”.
## Quick start (actionable)
@@ -53,7 +53,7 @@ openclaw cron add \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize overnight updates." \
--announce \
--deliver \
--channel slack \
--to "channel:C1234567890"
```
@@ -86,8 +86,7 @@ Think of a cron job as: **when** to run + **what** to do.
- Main session → `payload.kind = "systemEvent"`
- Isolated session → `payload.kind = "agentTurn"`
Optional: one-shot jobs (`schedule.kind = "at"`) delete after success by default. Set
`deleteAfterRun: false` to keep them (they will disable after success).
Optional: `deleteAfterRun: true` removes successful one-shot jobs from the store.
## Concepts
@@ -97,19 +96,19 @@ A cron job is a stored record with:
- a **schedule** (when it should run),
- a **payload** (what it should do),
- optional **delivery mode** (announce or none).
- optional **delivery** (where output should be sent).
- optional **agent binding** (`agentId`): run the job under a specific agent; if
missing or unknown, the gateway falls back to the default agent.
Jobs are identified by a stable `jobId` (used by CLI/Gateway APIs).
In agent tool calls, `jobId` is canonical; legacy `id` is accepted for compatibility.
One-shot jobs auto-delete after success by default; set `deleteAfterRun: false` to keep them.
Jobs can optionally auto-delete after a successful one-shot run via `deleteAfterRun: true`.
### Schedules
Cron supports three schedule kinds:
- `at`: one-shot timestamp via `schedule.at` (ISO 8601).
- `at`: one-shot timestamp (ms since epoch). Gateway accepts ISO 8601 and coerces to UTC.
- `every`: fixed interval (ms).
- `cron`: 5-field cron expression with optional IANA timezone.
@@ -137,13 +136,9 @@ Key behaviors:
- Prompt is prefixed with `[cron:<jobId> <job name>]` for traceability.
- Each run starts a **fresh session id** (no prior conversation carry-over).
- Default behavior: if `delivery` is omitted, isolated jobs announce a summary (`delivery.mode = "announce"`).
- `delivery.mode` (isolated-only) chooses what happens:
- `announce`: deliver a summary to the target channel and post a brief summary to the main session.
- `none`: internal only (no delivery, no main-session summary).
- `wakeMode` controls when the main-session summary posts:
- `now`: immediate heartbeat.
- `next-heartbeat`: waits for the next scheduled heartbeat.
- A summary is posted to the main session (prefix `Cron`, configurable).
- `wakeMode: "now"` triggers an immediate heartbeat after posting the summary.
- If `payload.deliver: true`, output is delivered to a channel; otherwise it stays internal.
Use isolated jobs for noisy, frequent, or "background chores" that shouldn't spam
your main chat history.
@@ -160,35 +155,16 @@ Common `agentTurn` fields:
- `message`: required text prompt.
- `model` / `thinking`: optional overrides (see below).
- `timeoutSeconds`: optional timeout override.
- `deliver`: `true` to send output to a channel target.
- `channel`: `last` or a specific channel.
- `to`: channel-specific target (phone/chat/channel id).
- `bestEffortDeliver`: avoid failing the job if delivery fails.
Delivery config (isolated jobs only):
Isolation options (only for `session=isolated`):
- `delivery.mode`: `none` | `announce`.
- `delivery.channel`: `last` or a specific channel.
- `delivery.to`: channel-specific target (phone/chat/channel id).
- `delivery.bestEffort`: avoid failing the job if announce delivery fails.
Announce delivery suppresses messaging tool sends for the run; use `delivery.channel`/`delivery.to`
to target the chat instead. When `delivery.mode = "none"`, no summary is posted to the main session.
If `delivery` is omitted for isolated jobs, OpenClaw defaults to `announce`.
#### Announce delivery flow
When `delivery.mode = "announce"`, cron delivers directly via the outbound channel adapters.
The main agent is not spun up to craft or forward the message.
Behavior details:
- Content: delivery uses the isolated run's outbound payloads (text/media) with normal chunking and
channel formatting.
- Heartbeat-only responses (`HEARTBEAT_OK` with no real content) are not delivered.
- If the isolated run already sent a message to the same target via the message tool, delivery is
skipped to avoid duplicates.
- Missing or invalid delivery targets fail the job unless `delivery.bestEffort = true`.
- A short summary is posted to the main session only when `delivery.mode = "announce"`.
- The main-session summary respects `wakeMode`: `now` triggers an immediate heartbeat and
`next-heartbeat` waits for the next scheduled heartbeat.
- `postToMainPrefix` (CLI: `--post-prefix`): prefix for the system event in main.
- `postToMainMode`: `summary` (default) or `full`.
- `postToMainMaxChars`: max chars when `postToMainMode=full` (default 8000).
### Model and thinking overrides
@@ -209,16 +185,19 @@ Resolution priority:
### Delivery (channel + target)
Isolated jobs can deliver output to a channel via the top-level `delivery` config:
Isolated jobs can deliver output to a channel. The job payload can specify:
- `delivery.mode`: `announce` (deliver a summary) or `none`.
- `delivery.channel`: `whatsapp` / `telegram` / `discord` / `slack` / `mattermost` (plugin) / `signal` / `imessage` / `last`.
- `delivery.to`: channel-specific recipient target.
- `channel`: `whatsapp` / `telegram` / `discord` / `slack` / `mattermost` (plugin) / `signal` / `imessage` / `last`
- `to`: channel-specific recipient target
Delivery config is only valid for isolated jobs (`sessionTarget: "isolated"`).
If `channel` or `to` is omitted, cron can fall back to the main sessions “last route”
(the last place the agent replied).
If `delivery.channel` or `delivery.to` is omitted, cron can fall back to the main sessions
“last route” (the last place the agent replied).
Delivery notes:
- If `to` is set, cron auto-delivers the agents final output even if `deliver` is omitted.
- Use `deliver: true` when you want last-route delivery without an explicit `to`.
- Use `deliver: false` to keep output internal even if a `to` is present.
Target format reminders:
@@ -241,8 +220,8 @@ Prefixed targets like `telegram:...` / `telegram:group:...` are also accepted:
## JSON schema for tool calls
Use these shapes when calling Gateway `cron.*` tools directly (agent tool calls or RPC).
CLI flags accept human durations like `20m`, but tool calls should use an ISO 8601 string
for `schedule.at` and milliseconds for `schedule.everyMs`.
CLI flags accept human durations like `20m`, but tool calls use epoch milliseconds for
`atMs` and `everyMs` (ISO timestamps are accepted for `at` times).
### cron.add params
@@ -251,7 +230,7 @@ One-shot, main session job (system event):
```json
{
"name": "Reminder",
"schedule": { "kind": "at", "at": "2026-02-01T16:00:00Z" },
"schedule": { "kind": "at", "atMs": 1738262400000 },
"sessionTarget": "main",
"wakeMode": "now",
"payload": { "kind": "systemEvent", "text": "Reminder text" },
@@ -269,25 +248,22 @@ Recurring, isolated job with delivery:
"wakeMode": "next-heartbeat",
"payload": {
"kind": "agentTurn",
"message": "Summarize overnight updates."
},
"delivery": {
"mode": "announce",
"message": "Summarize overnight updates.",
"deliver": true,
"channel": "slack",
"to": "channel:C1234567890",
"bestEffort": true
}
"bestEffortDeliver": true
},
"isolation": { "postToMainPrefix": "Cron", "postToMainMode": "summary" }
}
```
Notes:
- `schedule.kind`: `at` (`at`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`).
- `schedule.at` accepts ISO 8601 (timezone optional; treated as UTC when omitted).
- `everyMs` is milliseconds.
- `schedule.kind`: `at` (`atMs`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`).
- `atMs` and `everyMs` are epoch milliseconds.
- `sessionTarget` must be `"main"` or `"isolated"` and must match `payload.kind`.
- Optional fields: `agentId`, `description`, `enabled`, `deleteAfterRun` (defaults to true for `at`),
`delivery`.
- Optional fields: `agentId`, `description`, `enabled`, `deleteAfterRun`, `isolation`.
- `wakeMode` defaults to `"next-heartbeat"` when omitted.
### cron.update params
@@ -365,7 +341,7 @@ openclaw cron add \
--wake now
```
Recurring isolated job (announce to WhatsApp):
Recurring isolated job (deliver to WhatsApp):
```bash
openclaw cron add \
@@ -374,7 +350,7 @@ openclaw cron add \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize inbox + calendar for today." \
--announce \
--deliver \
--channel whatsapp \
--to "+15551234567"
```
@@ -388,7 +364,7 @@ openclaw cron add \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize today; send to the nightly topic." \
--announce \
--deliver \
--channel telegram \
--to "-1001234567890:topic:123"
```
@@ -404,7 +380,7 @@ openclaw cron add \
--message "Weekly deep analysis of project progress." \
--model "opus" \
--thinking high \
--announce \
--deliver \
--channel whatsapp \
--to "+15551234567"
```

View File

@@ -90,8 +90,7 @@ Cron jobs run at **exact times** and can run in isolated sessions without affect
- **Exact timing**: 5-field cron expressions with timezone support.
- **Session isolation**: Runs in `cron:<jobId>` without polluting main history.
- **Model overrides**: Use a cheaper or more powerful model per job.
- **Delivery control**: Isolated jobs default to `announce` (summary); choose `none` as needed.
- **Immediate delivery**: Announce mode posts directly without waiting for heartbeat.
- **Delivery control**: Can deliver directly to a channel; still posts a summary to main by default (configurable).
- **No agent context needed**: Runs even if main session is idle or compacted.
- **One-shot support**: `--at` for precise future timestamps.
@@ -105,12 +104,12 @@ openclaw cron add \
--session isolated \
--message "Generate today's briefing: weather, calendar, top emails, news summary." \
--model opus \
--announce \
--deliver \
--channel whatsapp \
--to "+15551234567"
```
This runs at exactly 7:00 AM New York time, uses Opus for quality, and announces a summary directly to WhatsApp.
This runs at exactly 7:00 AM New York time, uses Opus for quality, and delivers directly to WhatsApp.
### Cron example: One-shot reminder
@@ -174,7 +173,7 @@ The most efficient setup uses **both**:
```bash
# Daily morning briefing at 7am
openclaw cron add --name "Morning brief" --cron "0 7 * * *" --session isolated --message "..." --announce
openclaw cron add --name "Morning brief" --cron "0 7 * * *" --session isolated --message "..." --deliver
# Weekly project review on Mondays at 9am
openclaw cron add --name "Weekly review" --cron "0 9 * * 1" --session isolated --message "..." --model opus
@@ -215,13 +214,13 @@ See [Lobster](/tools/lobster) for full usage and examples.
Both heartbeat and cron can interact with the main session, but differently:
| | Heartbeat | Cron (main) | Cron (isolated) |
| ------- | ------------------------------- | ------------------------ | -------------------------- |
| Session | Main | Main (via system event) | `cron:<jobId>` |
| History | Shared | Shared | Fresh each run |
| Context | Full | Full | None (starts clean) |
| Model | Main session model | Main session model | Can override |
| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Announce summary (default) |
| | Heartbeat | Cron (main) | Cron (isolated) |
| ------- | ------------------------------- | ------------------------ | ---------------------- |
| Session | Main | Main (via system event) | `cron:<jobId>` |
| History | Shared | Shared | Fresh each run |
| Context | Full | Full | None (starts clean) |
| Model | Main session model | Main session model | Can override |
| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Summary posted to main |
### When to use main session cron
@@ -246,7 +245,7 @@ Use `--session isolated` when you want:
- A clean slate without prior context
- Different model or thinking settings
- Announce summaries directly to a channel
- Output delivered directly to a channel (summary still posts to main by default)
- History that doesn't clutter main session
```bash
@@ -257,7 +256,7 @@ openclaw cron add \
--message "Weekly codebase analysis..." \
--model opus \
--thinking high \
--announce
--deliver
```
## Cost Considerations

View File

@@ -42,80 +42,6 @@ Status: bundled plugin that talks to the BlueBubbles macOS server over HTTP. **R
4. Point BlueBubbles webhooks to your gateway (example: `https://your-gateway-host:3000/bluebubbles-webhook?password=<password>`).
5. Start the gateway; it will register the webhook handler and start pairing.
## Keeping Messages.app alive (VM / headless setups)
Some macOS VM / always-on setups can end up with Messages.app going “idle” (incoming events stop until the app is opened/foregrounded). A simple workaround is to **poke Messages every 5 minutes** using an AppleScript + LaunchAgent.
### 1) Save the AppleScript
Save this as:
- `~/Scripts/poke-messages.scpt`
Example script (non-interactive; does not steal focus):
```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) Install a LaunchAgent
Save this as:
- `~/Library/LaunchAgents/com.user.poke-messages.plist`
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.poke-messages</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-lc</string>
<string>/usr/bin/osascript &quot;$HOME/Scripts/poke-messages.scpt&quot;</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>300</integer>
<key>StandardOutPath</key>
<string>/tmp/poke-messages.log</string>
<key>StandardErrorPath</key>
<string>/tmp/poke-messages.err</string>
</dict>
</plist>
```
Notes:
- This runs **every 300 seconds** and **on login**.
- The first run may trigger macOS **Automation** prompts (`osascript` → Messages). Approve them in the same user session that runs the LaunchAgent.
Load it:
```bash
launchctl unload ~/Library/LaunchAgents/com.user.poke-messages.plist 2>/dev/null || true
launchctl load ~/Library/LaunchAgents/com.user.poke-messages.plist
```
## Onboarding
BlueBubbles is available in the interactive setup wizard:
@@ -266,7 +192,7 @@ Control whether responses are sent as a single message or streamed in blocks:
{
channels: {
bluebubbles: {
blockStreaming: true, // enable block streaming (off by default)
blockStreaming: true, // enable block streaming (default behavior)
},
},
}
@@ -294,7 +220,7 @@ Provider options:
- `channels.bluebubbles.groupAllowFrom`: Group sender allowlist.
- `channels.bluebubbles.groups`: Per-group config (`requireMention`, etc.).
- `channels.bluebubbles.sendReadReceipts`: Send read receipts (default: `true`).
- `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `false`; required for streaming replies).
- `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `true`).
- `channels.bluebubbles.textChunkLimit`: Outbound chunk size in chars (default: 4000).
- `channels.bluebubbles.chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking.
- `channels.bluebubbles.mediaMaxMb`: Inbound media cap in MB (default: 8).

View File

@@ -100,7 +100,7 @@ In **Bot** → **Privileged Gateway Intents**, enable:
- **Message Content Intent** (required to read message text in most guilds; without it youll see “Used disallowed intents” or the bot will connect but not react to messages)
- **Server Members Intent** (recommended; required for some member/user lookups and allowlist matching in guilds)
You usually do **not** need **Presence Intent**. Setting the bot's own presence (`setPresence` action) uses gateway OP3 and does not require this intent; it is only needed if you want to receive presence updates about other guild members.
You usually do **not** need **Presence Intent**.
### 3) Generate an invite URL (OAuth2 URL Generator)
@@ -278,7 +278,6 @@ Outbound Discord API calls retry on rate limits (429) using Discord `retry_after
voiceStatus: true,
events: true,
moderation: false,
presence: false,
},
replyToMode: "off",
dm: {
@@ -354,7 +353,6 @@ ack reaction after the bot replies.
- `channels` (create/edit/delete channels + categories + permissions)
- `roles` (role add/remove, default `false`)
- `moderation` (timeout/kick/ban, default `false`)
- `presence` (bot status/activity, default `false`)
- `execApprovals`: Discord-only exec approval DMs (button UI). Supports `enabled`, `approvers`, `agentFilter`, `sessionFilter`.
Reaction notifications use `guilds.<id>.reactionNotifications`:
@@ -414,7 +412,6 @@ Allowlist notes (PK-enabled):
| events | enabled | List/create scheduled events |
| roles | disabled | Role add/remove |
| moderation | disabled | Timeout/kick/ban |
| presence | disabled | Bot status/activity (setPresence) |
- `replyToMode`: `off` (default), `first`, or `all`. Applies only when the model includes a reply tag.
@@ -463,7 +460,6 @@ The agent can call `discord` with actions like:
- `searchMessages`, `memberInfo`, `roleInfo`, `roleAdd`, `roleRemove`, `emojiList`
- `channelInfo`, `channelList`, `voiceStatus`, `eventList`, `eventCreate`
- `timeout`, `kick`, `ban`
- `setPresence` (bot activity and online status)
Discord message ids are surfaced in the injected context (`[discord message id: …]` and history lines) so the agent can target them.
Emoji can be unicode (e.g., `✅`) or custom emoji syntax like `<:party_blob:1234567890>`.

View File

@@ -1,507 +0,0 @@
---
summary: "Feishu bot overview, features, and configuration"
read_when:
- You want to connect a Feishu/Lark bot
- You are configuring the Feishu channel
title: Feishu
---
# Feishu bot
Feishu (Lark) is a team chat platform used by companies for messaging and collaboration. This plugin connects OpenClaw to a Feishu/Lark bot using the platforms WebSocket event subscription so messages can be received without exposing a public webhook URL.
---
## Plugin required
Install the Feishu plugin:
```bash
openclaw plugins install @openclaw/feishu
```
Local checkout (when running from a git repo):
```bash
openclaw plugins install ./extensions/feishu
```
---
## Quickstart
There are two ways to add the Feishu channel:
### Method 1: onboarding wizard (recommended)
If you just installed OpenClaw, run the wizard:
```bash
openclaw onboard
```
The wizard guides you through:
1. Creating a Feishu app and collecting credentials
2. Configuring app credentials in OpenClaw
3. Starting the gateway
**After configuration**, check gateway status:
- `openclaw gateway status`
- `openclaw logs --follow`
### Method 2: CLI setup
If you already completed initial install, add the channel via CLI:
```bash
openclaw channels add
```
Choose **Feishu**, then enter the App ID and App Secret.
**After configuration**, manage the gateway:
- `openclaw gateway status`
- `openclaw gateway restart`
- `openclaw logs --follow`
---
## Step 1: Create a Feishu app
### 1. Open Feishu Open Platform
Visit [Feishu Open Platform](https://open.feishu.cn/app) and sign in.
Lark (global) tenants should use https://open.larksuite.com/app and set `domain: "lark"` in the Feishu config.
### 2. Create an app
1. Click **Create enterprise app**
2. Fill in the app name + description
3. Choose an app icon
![Create enterprise app](../images/feishu-step2-create-app.png)
### 3. Copy credentials
From **Credentials & Basic Info**, copy:
- **App ID** (format: `cli_xxx`)
- **App Secret**
**Important:** keep the App Secret private.
![Get credentials](../images/feishu-step3-credentials.png)
### 4. Configure permissions
On **Permissions**, click **Batch import** and paste:
```json
{
"scopes": {
"tenant": [
"aily:file:read",
"aily:file:write",
"application:application.app_message_stats.overview:readonly",
"application:application:self_manage",
"application:bot.menu:write",
"contact:user.employee_id:readonly",
"corehr:file:download",
"event:ip_list",
"im:chat.access_event.bot_p2p_chat:read",
"im:chat.members:bot_access",
"im:message",
"im:message.group_at_msg:readonly",
"im:message.p2p_msg:readonly",
"im:message:readonly",
"im:message:send_as_bot",
"im:resource"
],
"user": ["aily:file:read", "aily:file:write", "im:chat.access_event.bot_p2p_chat:read"]
}
}
```
![Configure permissions](../images/feishu-step4-permissions.png)
### 5. Enable bot capability
In **App Capability** > **Bot**:
1. Enable bot capability
2. Set the bot name
![Enable bot capability](../images/feishu-step5-bot-capability.png)
### 6. Configure event subscription
⚠️ **Important:** before setting event subscription, make sure:
1. You already ran `openclaw channels add` for Feishu
2. The gateway is running (`openclaw gateway status`)
In **Event Subscription**:
1. Choose **Use long connection to receive events** (WebSocket)
2. Add the event: `im.message.receive_v1`
⚠️ If the gateway is not running, the long-connection setup may fail to save.
![Configure event subscription](../images/feishu-step6-event-subscription.png)
### 7. Publish the app
1. Create a version in **Version Management & Release**
2. Submit for review and publish
3. Wait for admin approval (enterprise apps usually auto-approve)
---
## Step 2: Configure OpenClaw
### Configure with the wizard (recommended)
```bash
openclaw channels add
```
Choose **Feishu** and paste your App ID + App Secret.
### Configure via config file
Edit `~/.openclaw/openclaw.json`:
```json5
{
channels: {
feishu: {
enabled: true,
dmPolicy: "pairing",
accounts: {
main: {
appId: "cli_xxx",
appSecret: "xxx",
botName: "My AI assistant",
},
},
},
},
}
```
### Configure via environment variables
```bash
export FEISHU_APP_ID="cli_xxx"
export FEISHU_APP_SECRET="xxx"
```
### Lark (global) domain
If your tenant is on Lark (international), set the domain to `lark` (or a full domain string). You can set it at `channels.feishu.domain` or per account (`channels.feishu.accounts.<id>.domain`).
```json5
{
channels: {
feishu: {
domain: "lark",
accounts: {
main: {
appId: "cli_xxx",
appSecret: "xxx",
},
},
},
},
}
```
---
## Step 3: Start + test
### 1. Start the gateway
```bash
openclaw gateway
```
### 2. Send a test message
In Feishu, find your bot and send a message.
### 3. Approve pairing
By default, the bot replies with a pairing code. Approve it:
```bash
openclaw pairing approve feishu <CODE>
```
After approval, you can chat normally.
---
## Overview
- **Feishu bot channel**: Feishu bot managed by the gateway
- **Deterministic routing**: replies always return to Feishu
- **Session isolation**: DMs share a main session; groups are isolated
- **WebSocket connection**: long connection via Feishu SDK, no public URL needed
---
## Access control
### Direct messages
- **Default**: `dmPolicy: "pairing"` (unknown users get a pairing code)
- **Approve pairing**:
```bash
openclaw pairing list feishu
openclaw pairing approve feishu <CODE>
```
- **Allowlist mode**: set `channels.feishu.allowFrom` with allowed Open IDs
### Group chats
**1. Group policy** (`channels.feishu.groupPolicy`):
- `"open"` = allow everyone in groups (default)
- `"allowlist"` = only allow `groupAllowFrom`
- `"disabled"` = disable group messages
**2. Mention requirement** (`channels.feishu.groups.<chat_id>.requireMention`):
- `true` = require @mention (default)
- `false` = respond without mentions
---
## Group configuration examples
### Allow all groups, require @mention (default)
```json5
{
channels: {
feishu: {
groupPolicy: "open",
// Default requireMention: true
},
},
}
```
### Allow all groups, no @mention required
```json5
{
channels: {
feishu: {
groups: {
oc_xxx: { requireMention: false },
},
},
},
}
```
### Allow specific users in groups only
```json5
{
channels: {
feishu: {
groupPolicy: "allowlist",
groupAllowFrom: ["ou_xxx", "ou_yyy"],
},
},
}
```
---
## Get group/user IDs
### Group IDs (chat_id)
Group IDs look like `oc_xxx`.
**Method 1 (recommended)**
1. Start the gateway and @mention the bot in the group
2. Run `openclaw logs --follow` and look for `chat_id`
**Method 2**
Use the Feishu API debugger to list group chats.
### User IDs (open_id)
User IDs look like `ou_xxx`.
**Method 1 (recommended)**
1. Start the gateway and DM the bot
2. Run `openclaw logs --follow` and look for `open_id`
**Method 2**
Check pairing requests for user Open IDs:
```bash
openclaw pairing list feishu
```
---
## Common commands
| Command | Description |
| --------- | ----------------- |
| `/status` | Show bot status |
| `/reset` | Reset the session |
| `/model` | Show/switch model |
> Note: Feishu does not support native command menus yet, so commands must be sent as text.
## Gateway management commands
| Command | Description |
| -------------------------- | ----------------------------- |
| `openclaw gateway status` | Show gateway status |
| `openclaw gateway install` | Install/start gateway service |
| `openclaw gateway stop` | Stop gateway service |
| `openclaw gateway restart` | Restart gateway service |
| `openclaw logs --follow` | Tail gateway logs |
---
## Troubleshooting
### Bot does not respond in group chats
1. Ensure the bot is added to the group
2. Ensure you @mention the bot (default behavior)
3. Check `groupPolicy` is not set to `"disabled"`
4. Check logs: `openclaw logs --follow`
### Bot does not receive messages
1. Ensure the app is published and approved
2. Ensure event subscription includes `im.message.receive_v1`
3. Ensure **long connection** is enabled
4. Ensure app permissions are complete
5. Ensure the gateway is running: `openclaw gateway status`
6. Check logs: `openclaw logs --follow`
### App Secret leak
1. Reset the App Secret in Feishu Open Platform
2. Update the App Secret in your config
3. Restart the gateway
### Message send failures
1. Ensure the app has `im:message:send_as_bot` permission
2. Ensure the app is published
3. Check logs for detailed errors
---
## Advanced configuration
### Multiple accounts
```json5
{
channels: {
feishu: {
accounts: {
main: {
appId: "cli_xxx",
appSecret: "xxx",
botName: "Primary bot",
},
backup: {
appId: "cli_yyy",
appSecret: "yyy",
botName: "Backup bot",
enabled: false,
},
},
},
},
}
```
### Message limits
- `textChunkLimit`: outbound text chunk size (default: 2000 chars)
- `mediaMaxMb`: media upload/download limit (default: 30MB)
### Streaming
Feishu does not support message editing, so block streaming is enabled by default (`blockStreaming: true`). The bot waits for the full reply before sending.
---
## Configuration reference
Full configuration: [Gateway configuration](/gateway/configuration)
Key options:
| Setting | Description | Default |
| ------------------------------------------------- | ------------------------------- | --------- |
| `channels.feishu.enabled` | Enable/disable channel | `true` |
| `channels.feishu.domain` | API domain (`feishu` or `lark`) | `feishu` |
| `channels.feishu.accounts.<id>.appId` | App ID | - |
| `channels.feishu.accounts.<id>.appSecret` | App Secret | - |
| `channels.feishu.accounts.<id>.domain` | Per-account API domain override | `feishu` |
| `channels.feishu.dmPolicy` | DM policy | `pairing` |
| `channels.feishu.allowFrom` | DM allowlist (open_id list) | - |
| `channels.feishu.groupPolicy` | Group policy | `open` |
| `channels.feishu.groupAllowFrom` | Group allowlist | - |
| `channels.feishu.groups.<chat_id>.requireMention` | Require @mention | `true` |
| `channels.feishu.groups.<chat_id>.enabled` | Enable group | `true` |
| `channels.feishu.textChunkLimit` | Message chunk size | `2000` |
| `channels.feishu.mediaMaxMb` | Media size limit | `30` |
| `channels.feishu.blockStreaming` | Disable streaming | `true` |
---
## dmPolicy reference
| Value | Behavior |
| ------------- | --------------------------------------------------------------- |
| `"pairing"` | **Default.** Unknown users get a pairing code; must be approved |
| `"allowlist"` | Only users in `allowFrom` can chat |
| `"open"` | Allow all users (requires `"*"` in allowFrom) |
| `"disabled"` | Disable DMs |
---
## Supported message types
### Receive
- ✅ Text
- ✅ Images
- ✅ Files
- ✅ Audio
- ✅ Video
- ✅ Stickers
### Send
- ✅ Text
- ✅ Images
- ✅ Files
- ✅ Audio
- ⚠️ Rich text (partial support)

View File

@@ -1,18 +1,14 @@
---
summary: "Legacy iMessage support via imsg (JSON-RPC over stdio). New setups should use BlueBubbles."
summary: "iMessage support via imsg (JSON-RPC over stdio), setup, and chat_id routing"
read_when:
- Setting up iMessage support
- Debugging iMessage send/receive
title: iMessage
---
# iMessage (legacy: imsg)
# iMessage (imsg)
> **Recommended:** Use [BlueBubbles](/channels/bluebubbles) for new iMessage setups.
>
> The `imsg` channel is a legacy external-CLI integration and may be removed in a future release.
Status: legacy external CLI integration. Gateway spawns `imsg rpc` (JSON-RPC over stdio).
Status: external CLI integration. Gateway spawns `imsg rpc` (JSON-RPC over stdio).
## Quick setup (beginner)

View File

@@ -17,12 +17,11 @@ Text is supported everywhere; media and reactions vary by channel.
- [Telegram](/channels/telegram) — Bot API via grammY; supports groups.
- [Discord](/channels/discord) — Discord Bot API + Gateway; supports servers, channels, and DMs.
- [Slack](/channels/slack) — Bolt SDK; workspace apps.
- [Feishu](/channels/feishu) — Feishu/Lark bot via WebSocket (plugin, installed separately).
- [Google Chat](/channels/googlechat) — Google Chat API app via HTTP webhook.
- [Mattermost](/channels/mattermost) — Bot API + WebSocket; channels, groups, DMs (plugin, installed separately).
- [Signal](/channels/signal) — signal-cli; privacy-focused.
- [BlueBubbles](/channels/bluebubbles) — **Recommended for iMessage**; uses the BlueBubbles macOS server REST API with full feature support (edit, unsend, effects, reactions, group management — edit currently broken on macOS 26 Tahoe).
- [iMessage (legacy)](/channels/imessage) — Legacy macOS integration via imsg CLI (deprecated, use BlueBubbles for new setups).
- [iMessage](/channels/imessage) — macOS only; native integration via imsg (legacy, consider BlueBubbles for new setups).
- [Microsoft Teams](/channels/msteams) — Bot Framework; enterprise support (plugin, installed separately).
- [LINE](/channels/line) — LINE Messaging API bot (plugin, installed separately).
- [Nextcloud Talk](/channels/nextcloud-talk) — Self-hosted chat via Nextcloud Talk (plugin, installed separately).

View File

@@ -148,12 +148,12 @@ Once verified, the bot can decrypt messages in encrypted rooms.
- `openclaw pairing list matrix`
- `openclaw pairing approve matrix <CODE>`
- Public DMs: `channels.matrix.dm.policy="open"` plus `channels.matrix.dm.allowFrom=["*"]`.
- `channels.matrix.dm.allowFrom` accepts full Matrix user IDs (example: `@user:server`). The wizard resolves display names to user IDs when directory search finds a single exact match.
- `channels.matrix.dm.allowFrom` accepts user IDs or display names. The wizard resolves display names to user IDs when directory search is available.
## Rooms (groups)
- Default: `channels.matrix.groupPolicy = "allowlist"` (mention-gated). Use `channels.defaults.groupPolicy` to override the default when unset.
- Allowlist rooms with `channels.matrix.groups` (room IDs or aliases; names are resolved to IDs when directory search finds a single exact match):
- Allowlist rooms with `channels.matrix.groups` (room IDs, aliases, or names):
```json5
{
@@ -172,10 +172,10 @@ Once verified, the bot can decrypt messages in encrypted rooms.
- `requireMention: false` enables auto-reply in that room.
- `groups."*"` can set defaults for mention gating across rooms.
- `groupAllowFrom` restricts which senders can trigger the bot in rooms (full Matrix user IDs).
- Per-room `users` allowlists can further restrict senders inside a specific room (use full Matrix user IDs).
- The configure wizard prompts for room allowlists (room IDs, aliases, or names) and resolves names only on an exact, unique match.
- On startup, OpenClaw resolves room/user names in allowlists to IDs and logs the mapping; unresolved entries are ignored for allowlist matching.
- `groupAllowFrom` restricts which senders can trigger the bot in rooms (optional).
- Per-room `users` allowlists can further restrict senders inside a specific room.
- The configure wizard prompts for room allowlists (room IDs, aliases, or names) and resolves names when possible.
- On startup, OpenClaw resolves room/user names in allowlists to IDs and logs the mapping; unresolved entries are kept as typed.
- Invites are auto-joined by default; control with `channels.matrix.autoJoin` and `channels.matrix.autoJoinAllowlist`.
- To allow **no rooms**, set `channels.matrix.groupPolicy: "disabled"` (or keep an empty allowlist).
- Legacy key: `channels.matrix.rooms` (same shape as `groups`).
@@ -220,9 +220,9 @@ Provider options:
- `channels.matrix.textChunkLimit`: outbound text chunk size (chars).
- `channels.matrix.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
- `channels.matrix.dm.policy`: `pairing | allowlist | open | disabled` (default: pairing).
- `channels.matrix.dm.allowFrom`: DM allowlist (full Matrix user IDs). `open` requires `"*"`. The wizard resolves names to IDs when possible.
- `channels.matrix.dm.allowFrom`: DM allowlist (user IDs or display names). `open` requires `"*"`. The wizard resolves names to IDs when possible.
- `channels.matrix.groupPolicy`: `allowlist | open | disabled` (default: allowlist).
- `channels.matrix.groupAllowFrom`: allowlisted senders for group messages (full Matrix user IDs).
- `channels.matrix.groupAllowFrom`: allowlisted senders for group messages.
- `channels.matrix.allowlistOnly`: force allowlist rules for DMs + rooms.
- `channels.matrix.groups`: group allowlist + per-room settings map.
- `channels.matrix.rooms`: legacy group allowlist/config.

View File

@@ -456,7 +456,6 @@ Key settings (see `/gateway/configuration` for shared channel patterns):
- `channels.msteams.textChunkLimit`: outbound text chunk size.
- `channels.msteams.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
- `channels.msteams.mediaAllowHosts`: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains).
- `channels.msteams.mediaAuthAllowHosts`: allowlist for attaching Authorization headers on media retries (defaults to Graph + Bot Framework hosts).
- `channels.msteams.requireMention`: require @mention in channels/groups (default true).
- `channels.msteams.replyStyle`: `thread | top-level` (see [Reply Style](#reply-style-threads-vs-posts)).
- `channels.msteams.teams.<teamId>.replyStyle`: per-team override.
@@ -519,7 +518,6 @@ Teams recently introduced two channel UI styles over the same underlying data mo
Without Graph permissions, channel messages with images will be received as text-only (the image content is not accessible to the bot).
By default, OpenClaw only downloads media from Microsoft/Teams hostnames. Override with `channels.msteams.mediaAllowHosts` (use `["*"]` to allow any host).
Authorization headers are only attached for hosts in `channels.msteams.mediaAuthAllowHosts` (defaults to Graph + Bot Framework hosts). Keep this list strict (avoid multi-tenant suffixes).
## Sending files in group chats

View File

@@ -72,7 +72,6 @@ Minimal config:
- `openclaw pairing list nextcloud-talk`
- `openclaw pairing approve nextcloud-talk <CODE>`
- Public DMs: `channels.nextcloud-talk.dmPolicy="open"` plus `channels.nextcloud-talk.allowFrom=["*"]`.
- `allowFrom` matches Nextcloud user IDs only; display names are ignored.
## Rooms (groups)

View File

@@ -16,17 +16,12 @@ Related:
Tip: run `openclaw cron --help` for the full command surface.
Note: isolated `cron add` jobs default to `--announce` delivery. Use `--no-deliver` to keep
output internal. `--deliver` remains as a deprecated alias for `--announce`.
Note: one-shot (`--at`) jobs delete after success by default. Use `--keep-after-run` to keep them.
## Common edits
Update delivery settings without changing the message:
```bash
openclaw cron edit <job-id> --announce --channel telegram --to "123456789"
openclaw cron edit <job-id> --deliver --channel telegram --to "123456789"
```
Disable delivery for an isolated job:
@@ -34,9 +29,3 @@ Disable delivery for an isolated job:
```bash
openclaw cron edit <job-id> --no-deliver
```
Announce to a specific channel:
```bash
openclaw cron edit <job-id> --announce --channel slack --to "channel:C1234567890"
```

View File

@@ -303,7 +303,7 @@ Options:
- `--non-interactive`
- `--mode <local|remote>`
- `--flow <quickstart|advanced|manual>` (manual is an alias for advanced)
- `--auth-choice <setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|moonshot-api-key-cn|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip>`
- `--auth-choice <setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip>`
- `--token-provider <id>` (non-interactive; used with `--auth-choice token`)
- `--token <token>` (non-interactive; used with `--auth-choice token`)
- `--token-profile-id <id>` (non-interactive; default: `<provider>:manual`)

View File

@@ -22,5 +22,5 @@ openclaw security audit --deep
openclaw security audit --fix
```
The audit warns when multiple DM senders share the main session and recommends **secure DM mode**: `session.dmScope="per-channel-peer"` (or `per-account-channel-peer` for multi-account channels) for shared inboxes.
The audit warns when multiple DM senders share the main session and recommends `session.dmScope="per-channel-peer"` (or `per-account-channel-peer` for multi-account channels) for shared inboxes.
It also warns when small models (`<=300B`) are used without sandboxing and with web/browser tools enabled.

View File

@@ -3,6 +3,7 @@ summary: "How OpenClaw memory works (workspace files + automatic memory flush)"
read_when:
- You want the memory file layout and workflow
- You want to tune the automatic pre-compaction memory flush
title: "Memory"
---
# Memory
@@ -25,7 +26,7 @@ The default workspace layout uses two memory layers:
- **Only load in the main, private session** (never in group contexts).
These files live under the workspace (`agents.defaults.workspace`, default
`~/clawd`). See [Agent workspace](/concepts/agent-workspace) for the full layout.
`~/.openclaw/workspace`). See [Agent workspace](/concepts/agent-workspace) for the full layout.
## When to write memory
@@ -77,8 +78,9 @@ For the full compaction lifecycle, see
## Vector memory search
OpenClaw can build a small vector index over `MEMORY.md` and `memory/*.md` so
semantic queries can find related notes even when wording differs.
OpenClaw can build a small vector index over `MEMORY.md` and `memory/*.md` (plus
any extra directories or files you opt in) so semantic queries can find related
notes even when wording differs.
Defaults:
@@ -99,123 +101,6 @@ embeddings for memory search. For Gemini, use `GEMINI_API_KEY` or
`models.providers.google.apiKey`. When using a custom OpenAI-compatible endpoint,
set `memorySearch.remote.apiKey` (and optional `memorySearch.remote.headers`).
### QMD backend (experimental)
Set `memory.backend = "qmd"` to swap the built-in SQLite indexer for
[QMD](https://github.com/tobi/qmd): a local-first search sidecar that combines
BM25 + vectors + reranking. Markdown stays the source of truth; OpenClaw shells
out to QMD for retrieval. Key points:
**Prereqs**
- Disabled by default. Opt in per-config (`memory.backend = "qmd"`).
- Install the QMD CLI separately (`bun install -g github.com/tobi/qmd` or grab
a release) and make sure the `qmd` binary is on the gateways `PATH`.
- QMD needs an SQLite build that allows extensions (`brew install sqlite` on
macOS).
- QMD runs fully locally via Bun + `node-llama-cpp` and auto-downloads GGUF
models from HuggingFace on first use (no separate Ollama daemon required).
- The gateway runs QMD in a self-contained XDG home under
`~/.openclaw/agents/<agentId>/qmd/` by setting `XDG_CONFIG_HOME` and
`XDG_CACHE_HOME`.
- OS support: macOS and Linux work out of the box once Bun + SQLite are
installed. Windows is best supported via WSL2.
**How the sidecar runs**
- The gateway writes a self-contained QMD home under
`~/.openclaw/agents/<agentId>/qmd/` (config + cache + sqlite DB).
- Collections are rewritten from `memory.qmd.paths` (plus default workspace
memory files) into `index.yml`, then `qmd update` + `qmd embed` run on boot and
on a configurable interval (`memory.qmd.update.interval`, default 5m).
- Searches run via `qmd query --json`. If QMD fails or the binary is missing,
OpenClaw automatically falls back to the builtin SQLite manager so memory tools
keep working.
- **First search may be slow**: QMD may download local GGUF models (reranker/query
expansion) on the first `qmd query` run.
- OpenClaw sets `XDG_CONFIG_HOME`/`XDG_CACHE_HOME` automatically when it runs QMD.
- If you want to pre-download models manually (and warm the same index OpenClaw
uses), run a one-off query with the agents XDG dirs.
OpenClaws QMD state lives under your **state dir** (defaults to `~/.openclaw`).
You can point `qmd` at the exact same index by exporting the same XDG vars
OpenClaw uses:
```bash
# Pick the same state dir OpenClaw uses
STATE_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}"
if [ -d "$HOME/.moltbot" ] && [ ! -d "$HOME/.openclaw" ] \
&& [ -z "${OPENCLAW_STATE_DIR:-}" ]; then
STATE_DIR="$HOME/.moltbot"
fi
export XDG_CONFIG_HOME="$STATE_DIR/agents/main/qmd/xdg-config"
export XDG_CACHE_HOME="$STATE_DIR/agents/main/qmd/xdg-cache"
# (Optional) force an index refresh + embeddings
qmd update
qmd embed
# Warm up / trigger first-time model downloads
qmd query "test" -c memory-root --json >/dev/null 2>&1
```
**Config surface (`memory.qmd.*`)**
- `command` (default `qmd`): override the executable path.
- `includeDefaultMemory` (default `true`): auto-index `MEMORY.md` + `memory/**/*.md`.
- `paths[]`: add extra directories/files (`path`, optional `pattern`, optional
stable `name`).
- `sessions`: opt into session JSONL indexing (`enabled`, `retentionDays`,
`exportDir`).
- `update`: controls refresh cadence (`interval`, `debounceMs`, `onBoot`, `embedInterval`).
- `limits`: clamp recall payload (`maxResults`, `maxSnippetChars`,
`maxInjectedChars`, `timeoutMs`).
- `scope`: same schema as [`session.sendPolicy`](/gateway/configuration#session).
Default is DM-only (`deny` all, `allow` direct chats); loosen it to surface QMD
hits in groups/channels.
- Snippets sourced outside the workspace show up as
`qmd/<collection>/<relative-path>` in `memory_search` results; `memory_get`
understands that prefix and reads from the configured QMD collection root.
- When `memory.qmd.sessions.enabled = true`, OpenClaw exports sanitized session
transcripts (User/Assistant turns) into a dedicated QMD collection under
`~/.openclaw/agents/<id>/qmd/sessions/`, so `memory_search` can recall recent
conversations without touching the builtin SQLite index.
- `memory_search` snippets now include a `Source: <path#line>` footer when
`memory.citations` is `auto`/`on`; set `memory.citations = "off"` to keep
the path metadata internal (the agent still receives the path for
`memory_get`, but the snippet text omits the footer and the system prompt
warns the agent not to cite it).
**Example**
```json5
memory: {
backend: "qmd",
citations: "auto",
qmd: {
includeDefaultMemory: true,
update: { interval: "5m", debounceMs: 15000 },
limits: { maxResults: 6, timeoutMs: 4000 },
scope: {
default: "deny",
rules: [{ action: "allow", match: { chatType: "direct" } }]
},
paths: [
{ name: "docs", path: "~/notes", pattern: "**/*.md" }
]
}
}
```
**Citations & fallback**
- `memory.citations` applies regardless of backend (`auto`/`on`/`off`).
- When `qmd` runs, we tag `status().backend = "qmd"` so diagnostics show which
engine served the results. If the QMD subprocess exits or JSON output cant be
parsed, the search manager logs a warning and returns the builtin provider
(existing Markdown embeddings) until QMD recovers.
### Additional memory paths
If you want to index Markdown files outside the default workspace layout, add
@@ -337,14 +222,14 @@ Local mode:
### How the memory tools work
- `memory_search` semantically searches Markdown chunks (~400 token target, 80-token overlap) from `MEMORY.md` + `memory/**/*.md`. It returns snippet text (capped ~700 chars), file path, line range, score, provider/model, and whether we fell back from local → remote embeddings. No full file payload is returned.
- `memory_get` reads a specific memory Markdown file (workspace-relative), optionally from a starting line and for N lines. Paths outside `MEMORY.md` / `memory/` are rejected.
- `memory_get` reads a specific memory Markdown file (workspace-relative), optionally from a starting line and for N lines. Paths outside `MEMORY.md` / `memory/` are allowed only when explicitly listed in `memorySearch.extraPaths`.
- Both tools are enabled only when `memorySearch.enabled` resolves true for the agent.
### What gets indexed (and when)
- File type: Markdown only (`MEMORY.md`, `memory/**/*.md`).
- File type: Markdown only (`MEMORY.md`, `memory/**/*.md`, plus any `.md` files under `memorySearch.extraPaths`).
- Index storage: per-agent SQLite at `~/.openclaw/memory/<agentId>.sqlite` (configurable via `agents.defaults.memorySearch.store.path`, supports `{agentId}` token).
- Freshness: watcher on `MEMORY.md` + `memory/` marks the index dirty (debounce 1.5s). Sync is scheduled on session start, on search, or on an interval and runs asynchronously. Session transcripts use delta thresholds to trigger background sync.
- Freshness: watcher on `MEMORY.md`, `memory/`, and `memorySearch.extraPaths` marks the index dirty (debounce 1.5s). Sync is scheduled on session start, on search, or on an interval and runs asynchronously. Session transcripts use delta thresholds to trigger background sync.
- Reindex triggers: the index stores the embedding **provider/model + endpoint fingerprint + chunking params**. If any of those change, OpenClaw automatically resets and reindexes the entire store.
### Hybrid search (BM25 + vector)

View File

@@ -17,26 +17,6 @@ Use `session.dmScope` to control how **direct messages** are grouped:
- `per-account-channel-peer`: isolate by account + channel + sender (recommended for multi-account inboxes).
Use `session.identityLinks` to map provider-prefixed peer ids to a canonical identity so the same person shares a DM session across channels when using `per-peer`, `per-channel-peer`, or `per-account-channel-peer`.
### Secure DM mode (recommended)
If your agent can receive DMs from **multiple people** (pairing approvals for more than one sender, a DM allowlist with multiple entries, or `dmPolicy: "open"`), enable **secure DM mode** to avoid cross-user context leakage:
```json5
// ~/.openclaw/openclaw.json
{
session: {
// Secure DM mode: isolate DM context per channel + sender.
dmScope: "per-channel-peer",
},
}
```
Notes:
- Default is `dmScope: "main"` for continuity (all DMs share the main session).
- For multi-account inboxes on the same channel, prefer `per-account-channel-peer`.
- If the same person contacts you on multiple channels, use `session.identityLinks` to collapse their DM sessions into one canonical identity.
## Gateway is the source of truth
All session state is **owned by the gateway** (the “master” OpenClaw). UI clients (macOS app, WebChat, etc.) must query the gateway for session lists and token counts instead of reading local files.

File diff suppressed because it is too large Load Diff

View File

@@ -446,32 +446,6 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
}
```
### Secure DM mode (shared inbox / multi-user DMs)
If more than one person can DM your bot (multiple entries in `allowFrom`, pairing approvals for multiple people, or `dmPolicy: "open"`), enable **secure DM mode** so DMs from different senders dont share one context by default:
```json5
{
// Secure DM mode (recommended for multi-user or sensitive DM agents)
session: { dmScope: "per-channel-peer" },
channels: {
// Example: WhatsApp multi-user inbox
whatsapp: {
dmPolicy: "allowlist",
allowFrom: ["+15555550123", "+15555550124"],
},
// Example: Discord multi-user inbox
discord: {
enabled: true,
token: "YOUR_DISCORD_BOT_TOKEN",
dm: { enabled: true, allowFrom: ["alice", "bob"] },
},
},
}
```
### OAuth with API key failover
```json5

View File

@@ -2039,7 +2039,6 @@ of `every`, keep `HEARTBEAT.md` tiny, and/or choose a cheaper `model`.
- `tools.web.search.cacheTtlMinutes` (default 15)
- `tools.web.fetch.enabled` (default true)
- `tools.web.fetch.maxChars` (default 50000)
- `tools.web.fetch.maxCharsCap` (default 50000; clamps maxChars from config/tool calls)
- `tools.web.fetch.timeoutSeconds` (default 30)
- `tools.web.fetch.cacheTtlMinutes` (default 15)
- `tools.web.fetch.userAgent` (optional override)
@@ -2552,9 +2551,7 @@ Notes:
- Set `MOONSHOT_API_KEY` in the environment or use `openclaw onboard --auth-choice moonshot-api-key`.
- Model ref: `moonshot/kimi-k2.5`.
- For the China endpoint, either:
- Run `openclaw onboard --auth-choice moonshot-api-key-cn` (wizard will set `https://api.moonshot.cn/v1`), or
- Manually set `baseUrl: "https://api.moonshot.cn/v1"` in `models.providers.moonshot`.
- Use `https://api.moonshot.cn/v1` if you need the China endpoint.
### Kimi Coding
@@ -2766,7 +2763,6 @@ Fields:
- `per-peer`: isolate DMs by sender id across channels.
- `per-channel-peer`: isolate DMs per channel + sender (recommended for multi-user inboxes).
- `per-account-channel-peer`: isolate DMs per account + channel + sender (recommended for multi-account inboxes).
- Secure DM mode (recommended): set `session.dmScope: "per-channel-peer"` when multiple people can DM the bot (shared inboxes, multi-person allowlists, or `dmPolicy: "open"`).
- `identityLinks`: map canonical ids to provider-prefixed peers so the same person shares a DM session across channels when using `per-peer`, `per-channel-peer`, or `per-account-channel-peer`.
- Example: `alice: ["telegram:123456789", "discord:987654321012345678"]`.
- `reset`: primary reset policy. Defaults to daily resets at 4:00 AM local time on the gateway host.
@@ -2956,7 +2952,6 @@ Control UI base path:
- `gateway.controlUi.basePath` sets the URL prefix where the Control UI is served.
- Examples: `"/ui"`, `"/openclaw"`, `"/apps/openclaw"`.
- Default: root (`/`) (unchanged).
- `gateway.controlUi.root` sets the filesystem root for Control UI assets (default: `dist/control-ui`).
- `gateway.controlUi.allowInsecureAuth` allows token-only auth for the Control UI when
device identity is omitted (typically over HTTP). Default: `false`. Prefer HTTPS
(Tailscale Serve) or `127.0.0.1`.

View File

@@ -205,16 +205,7 @@ By default, OpenClaw routes **all DMs into the main session** so your assistant
}
```
This prevents cross-user context leakage while keeping group chats isolated.
### Secure DM mode (recommended)
Treat the snippet above as **secure DM mode**:
- Default: `session.dmScope: "main"` (all DMs share one session for continuity).
- Secure DM mode: `session.dmScope: "per-channel-peer"` (each channel+sender pair gets an isolated DM context).
If you run multiple accounts on the same channel, use `per-account-channel-peer` instead. If the same person contacts you on multiple channels, use `session.identityLinks` to collapse those DM sessions into one canonical identity. See [Session Management](/concepts/session) and [Configuration](/gateway/configuration).
This prevents cross-user context leakage while keeping group chats isolated. If you run multiple accounts on the same channel, use `per-account-channel-peer` instead. If the same person contacts you on multiple channels, use `session.identityLinks` to collapse those DM sessions into one canonical identity. See [Session Management](/concepts/session) and [Configuration](/gateway/configuration).
## Allowlists (DM + groups) — terminology

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 KiB

View File

@@ -210,8 +210,7 @@ Example:
- [Telegram](/channels/telegram)
- [Discord](/channels/discord)
- [Mattermost (plugin)](/channels/mattermost)
- [BlueBubbles (iMessage)](/channels/bluebubbles)
- [iMessage (legacy)](/channels/imessage)
- [iMessage](/channels/imessage)
- [Groups](/concepts/groups)
- [WhatsApp group messages](/concepts/group-messages)
- [Media: images](/nodes/images)

View File

@@ -56,7 +56,6 @@ After it finishes:
- Open `http://127.0.0.1:18789/` in your browser.
- Paste the token into the Control UI (Settings → token).
- Need the tokenized URL again? Run `docker compose run --rm openclaw-cli dashboard --no-open`.
It writes config/workspace on the host:
@@ -73,27 +72,6 @@ docker compose run --rm openclaw-cli onboard
docker compose up -d openclaw-gateway
```
Note: run `docker compose ...` from the repo root. If you enabled
`OPENCLAW_EXTRA_MOUNTS` or `OPENCLAW_HOME_VOLUME`, the setup script writes
`docker-compose.extra.yml`; include it when running Compose elsewhere:
```bash
docker compose -f docker-compose.yml -f docker-compose.extra.yml <command>
```
### Control UI token + pairing (Docker)
If you see “unauthorized” or “disconnected (1008): pairing required”, fetch a
fresh dashboard link and approve the browser device:
```bash
docker compose run --rm openclaw-cli dashboard --no-open
docker compose run --rm openclaw-cli devices list
docker compose run --rm openclaw-cli devices approve <requestId>
```
More detail: [Dashboard](/web/dashboard), [Devices](/cli/devices).
### Extra mounts (optional)
If you want to mount additional host directories into the containers, set
@@ -164,61 +142,6 @@ Notes:
- If you change `OPENCLAW_DOCKER_APT_PACKAGES`, rerun `docker-setup.sh` to rebuild
the image.
### Power-user / full-featured container (opt-in)
The default Docker image is **security-first** and runs as the non-root `node`
user. This keeps the attack surface small, but it means:
- no system package installs at runtime
- no Homebrew by default
- no bundled Chromium/Playwright browsers
If you want a more full-featured container, use these opt-in knobs:
1. **Persist `/home/node`** so browser downloads and tool caches survive:
```bash
export OPENCLAW_HOME_VOLUME="openclaw_home"
./docker-setup.sh
```
2. **Bake system deps into the image** (repeatable + persistent):
```bash
export OPENCLAW_DOCKER_APT_PACKAGES="git curl jq"
./docker-setup.sh
```
3. **Install Playwright browsers without `npx`** (avoids npm override conflicts):
```bash
docker compose run --rm openclaw-cli \
node /app/node_modules/playwright-core/cli.js install chromium
```
If you need Playwright to install system deps, rebuild the image with
`OPENCLAW_DOCKER_APT_PACKAGES` instead of using `--with-deps` at runtime.
4. **Persist Playwright browser downloads**:
- Set `PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright` in
`docker-compose.yml`.
- Ensure `/home/node` persists via `OPENCLAW_HOME_VOLUME`, or mount
`/home/node/.cache/ms-playwright` via `OPENCLAW_EXTRA_MOUNTS`.
### Permissions + EACCES
The image runs as `node` (uid 1000). If you see permission errors on
`/home/node/.openclaw`, make sure your host bind mounts are owned by uid 1000.
Example (Linux host):
```bash
sudo chown -R 1000:1000 /path/to/openclaw-config /path/to/openclaw-workspace
```
If you choose to run as root for convenience, you accept the security tradeoff.
### Faster rebuilds (recommended)
To speed up rebuilds, order your Dockerfile so dependency layers are cached.
@@ -276,13 +199,6 @@ docker compose run --rm openclaw-cli channels add --channel discord --token "<to
Docs: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord)
### OpenAI Codex OAuth (headless Docker)
If you pick OpenAI Codex OAuth in the wizard, it opens a browser URL and tries
to capture a callback on `http://127.0.0.1:1455/auth/callback`. In Docker or
headless setups that callback can show a browser error. Copy the full redirect
URL you land on and paste it back into the wizard to finish auth.
### Health check
```bash
@@ -304,7 +220,6 @@ pnpm test:docker:qr
### Notes
- Gateway bind defaults to `lan` for container use.
- Dockerfile CMD uses `--allow-unconfigured`; mounted config with `gateway.mode` not `local` will still start. Override CMD to enforce the guard.
- The gateway container is the source of truth for sessions (`~/.openclaw/agents/<agentId>/sessions/`).
## Agent Sandbox (host gateway + Docker tools)

View File

@@ -79,9 +79,10 @@ Or with pnpm:
```bash
pnpm add -g openclaw@latest
pnpm approve-builds -g # approve openclaw, node-llama-cpp, sharp, etc.
pnpm add -g openclaw@latest # re-run to execute postinstall scripts
```
pnpm requires explicit approval for packages with build scripts. After the first install shows the "Ignored build scripts" warning, run `pnpm approve-builds -g` and select the listed packages.
pnpm requires explicit approval for packages with build scripts. After the first install shows the "Ignored build scripts" warning, run `pnpm approve-builds -g` and select the listed packages, then re-run the install so postinstall scripts execute.
Then:

View File

@@ -29,7 +29,7 @@ Notes:
```
The installer will `git pull --rebase` **only** if the repo is clean.
- For **global installs**, the script uses `npm install -g openclaw@latest` under the hood.
- Legacy note: `clawdbot` remains available as a compatibility shim.
- Legacy note: `openclaw` remains available as a compatibility shim.
## Before you update

View File

@@ -446,10 +446,7 @@ Example voice-call config with ngrok:
"enabled": true,
"config": {
"provider": "twilio",
"tunnel": { "provider": "ngrok" },
"webhookSecurity": {
"allowedHosts": ["example.ngrok.app"]
}
"tunnel": { "provider": "ngrok" }
}
}
}
@@ -457,7 +454,7 @@ Example voice-call config with ngrok:
}
```
The ngrok tunnel runs inside the container and provides a public webhook URL without exposing the Fly app itself. Set `webhookSecurity.allowedHosts` to the public tunnel hostname so forwarded host headers are accepted.
The ngrok tunnel runs inside the container and provides a public webhook URL without exposing the Fly app itself.
### Security benefits

View File

@@ -34,17 +34,17 @@ Notes:
# From repo root; set release IDs so Sparkle feed is enabled.
# APP_BUILD must be numeric + monotonic for Sparkle compare.
BUNDLE_ID=bot.molt.mac \
APP_VERSION=2026.2.3 \
APP_VERSION=2026.1.27-beta.1 \
APP_BUILD="$(git rev-list --count HEAD)" \
BUILD_CONFIG=release \
SIGN_IDENTITY="Developer ID Application: <Developer Name> (<TEAMID>)" \
scripts/package-mac-app.sh
# Zip for distribution (includes resource forks for Sparkle delta support)
ditto -c -k --sequesterRsrc --keepParent dist/OpenClaw.app dist/OpenClaw-2026.2.3.zip
ditto -c -k --sequesterRsrc --keepParent dist/OpenClaw.app dist/OpenClaw-2026.1.27-beta.1.zip
# Optional: also build a styled DMG for humans (drag to /Applications)
scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.2.3.dmg
scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.1.27-beta.1.dmg
# Recommended: build + notarize/staple zip + DMG
# First, create a keychain profile once:
@@ -52,14 +52,14 @@ scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.2.3.dmg
# --apple-id "<apple-id>" --team-id "<team-id>" --password "<app-specific-password>"
NOTARIZE=1 NOTARYTOOL_PROFILE=openclaw-notary \
BUNDLE_ID=bot.molt.mac \
APP_VERSION=2026.2.3 \
APP_VERSION=2026.1.27-beta.1 \
APP_BUILD="$(git rev-list --count HEAD)" \
BUILD_CONFIG=release \
SIGN_IDENTITY="Developer ID Application: <Developer Name> (<TEAMID>)" \
scripts/package-mac-dist.sh
# Optional: ship dSYM alongside the release
ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenClaw-2026.2.3.dSYM.zip
ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenClaw-2026.1.27-beta.1.dSYM.zip
```
## Appcast entry
@@ -67,7 +67,7 @@ ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenCl
Use the release note generator so Sparkle renders formatted HTML notes:
```bash
SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/OpenClaw-2026.2.3.zip https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml
SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/OpenClaw-2026.1.27-beta.1.zip https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml
```
Generates HTML release notes from `CHANGELOG.md` (via [`scripts/changelog-to-html.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/changelog-to-html.sh)) and embeds them in the appcast entry.
@@ -75,7 +75,7 @@ Commit the updated `appcast.xml` alongside the release assets (zip + dSYM) when
## Publish & verify
- Upload `OpenClaw-2026.2.3.zip` (and `OpenClaw-2026.2.3.dSYM.zip`) to the GitHub release for tag `v2026.2.3`.
- Upload `OpenClaw-2026.1.27-beta.1.zip` (and `OpenClaw-2026.1.27-beta.1.dSYM.zip`) to the GitHub release for tag `v2026.1.27-beta.1`.
- Ensure the raw appcast URL matches the baked feed: `https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml`.
- Sanity checks:
- `curl -I https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml` returns 200.

View File

@@ -81,12 +81,6 @@ Set config under `plugins.entries.voice-call.config`:
path: "/voice/webhook",
},
// Webhook security (recommended for tunnels/proxies)
webhookSecurity: {
allowedHosts: ["voice.example.com"],
trustedProxyIPs: ["100.64.0.1"],
},
// Public exposure (pick one)
// publicUrl: "https://example.ngrok.app/voice/webhook",
// tunnel: { provider: "ngrok" },
@@ -117,38 +111,6 @@ Notes:
- `tunnel.allowNgrokFreeTierLoopbackBypass: true` allows Twilio webhooks with invalid signatures **only** when `tunnel.provider="ngrok"` and `serve.bind` is loopback (ngrok local agent). Use for local dev only.
- Ngrok free tier URLs can change or add interstitial behavior; if `publicUrl` drifts, Twilio signatures will fail. For production, prefer a stable domain or Tailscale funnel.
## Webhook Security
When a proxy or tunnel sits in front of the Gateway, the plugin reconstructs the
public URL for signature verification. These options control which forwarded
headers are trusted.
`webhookSecurity.allowedHosts` allowlists hosts from forwarding headers.
`webhookSecurity.trustForwardingHeaders` trusts forwarded headers without an allowlist.
`webhookSecurity.trustedProxyIPs` only trusts forwarded headers when the request
remote IP matches the list.
Example with a stable public host:
```json5
{
plugins: {
entries: {
"voice-call": {
config: {
publicUrl: "https://voice.example.com/voice/webhook",
webhookSecurity: {
allowedHosts: ["voice.example.com"],
},
},
},
},
},
}
```
## TTS for calls
Voice Call uses the core `messages.tts` configuration (OpenAI or ElevenLabs) for

View File

@@ -106,7 +106,6 @@ Current npm plugin list (update as needed):
- @openclaw/bluebubbles
- @openclaw/diagnostics-otel
- @openclaw/discord
- @openclaw/feishu
- @openclaw/lobster
- @openclaw/matrix
- @openclaw/msteams

View File

@@ -1,5 +1,5 @@
---
summary: "RPC adapters for external CLIs (signal-cli, legacy imsg) and gateway patterns"
summary: "RPC adapters for external CLIs (signal-cli, imsg) and gateway patterns"
read_when:
- Adding or changing external CLI integrations
- Debugging RPC adapters (signal-cli, imsg)
@@ -19,11 +19,9 @@ OpenClaw integrates external CLIs via JSON-RPC. Two patterns are used today.
See [Signal](/channels/signal) for setup and endpoints.
## Pattern B: stdio child process (legacy: imsg)
## Pattern B: stdio child process (imsg)
> **Note:** For new iMessage setups, use [BlueBubbles](/channels/bluebubbles) instead.
- OpenClaw spawns `imsg rpc` as a child process (legacy iMessage integration).
- OpenClaw spawns `imsg rpc` as a child process.
- JSON-RPC is line-delimited over stdin/stdout (one JSON object per line).
- No TCP port, no daemon required.
@@ -34,7 +32,7 @@ Core methods used:
- `send`
- `chats.list` (probe/diagnostics)
See [iMessage](/channels/imessage) for legacy setup and addressing (`chat_id` preferred).
See [iMessage](/channels/imessage) for setup and addressing (`chat_id` preferred).
## Adapter guidelines

View File

@@ -11,15 +11,11 @@ title: "Transcript Hygiene"
This document describes **provider-specific fixes** applied to transcripts before a run
(building model context). These are **in-memory** adjustments used to satisfy strict
provider requirements. These hygiene steps do **not** rewrite the stored JSONL transcript
on disk; however, a separate session-file repair pass may rewrite malformed JSONL files
by dropping invalid lines before the session is loaded. When a repair occurs, the original
file is backed up alongside the session file.
provider requirements. They do **not** rewrite the stored JSONL transcript on disk.
Scope includes:
- Tool call id sanitization
- Tool call input validation
- Tool result pairing repair
- Turn validation / ordering
- Thought signature cleanup
@@ -40,11 +36,6 @@ All transcript hygiene is centralized in the embedded runner:
The policy uses `provider`, `modelApi`, and `modelId` to decide what to apply.
Separate from transcript hygiene, session files are repaired (if needed) before load:
- `repairSessionFileIfNeeded` in `src/agents/session-file-repair.ts`
- Called from `run/attempt.ts` and `compact.ts` (embedded runner)
---
## Global rule: image sanitization
@@ -59,19 +50,6 @@ Implementation:
---
## Global rule: malformed tool calls
Assistant tool-call blocks that are missing both `input` and `arguments` are dropped
before model context is built. This prevents provider rejections from partially
persisted tool calls (for example, after a rate limit failure).
Implementation:
- `sanitizeToolCallInputs` in `src/agents/session-transcript-repair.ts`
- Applied in `sanitizeSessionHistory` in `src/agents/pi-embedded-runner/google.ts`
---
## Provider matrix (current behavior)
**OpenAI / OpenAI Codex**

View File

@@ -17,6 +17,11 @@ Use these when a task is clearly tied to a script; otherwise prefer the CLI.
- Prefer CLI surfaces when they exist (example: auth monitoring uses `openclaw models status --check`).
- Assume scripts are hostspecific; read them before running on a new machine.
## Git hooks
- `scripts/setup-git-hooks.js`: best-effort setup for `core.hooksPath` when inside a git repo.
- `scripts/format-staged.js`: pre-commit formatter for staged `src/` and `test/` files.
## Auth monitoring scripts
Auth monitoring scripts are documented here:

View File

@@ -71,8 +71,7 @@ Use these hubs to discover every page, including deep dives and reference docs t
- [Discord](/channels/discord)
- [Mattermost](/channels/mattermost) (plugin)
- [Signal](/channels/signal)
- [BlueBubbles (iMessage)](/channels/bluebubbles)
- [iMessage (legacy)](/channels/imessage)
- [iMessage](/channels/imessage)
- [Location parsing](/channels/location)
- [WebChat](/web/webchat)
- [Webhooks](/automation/webhook)

View File

@@ -80,7 +80,6 @@ Stored under `~/.openclaw/devices/`:
- Telegram: [Telegram](/channels/telegram)
- WhatsApp: [WhatsApp](/channels/whatsapp)
- Signal: [Signal](/channels/signal)
- BlueBubbles (iMessage): [BlueBubbles](/channels/bluebubbles)
- iMessage (legacy): [iMessage](/channels/imessage)
- iMessage: [iMessage](/channels/imessage)
- Discord: [Discord](/channels/discord)
- Slack: [Slack](/channels/slack)

View File

@@ -127,8 +127,7 @@ Tip: `--json` does **not** imply non-interactive mode. Use `--non-interactive` (
- [Google Chat](/channels/googlechat): service account JSON + webhook audience.
- [Mattermost](/channels/mattermost) (plugin): bot token + base URL.
- [Signal](/channels/signal): optional `signal-cli` install + account config.
- [BlueBubbles](/channels/bluebubbles): **recommended for iMessage**; server URL + password + webhook.
- [iMessage](/channels/imessage): legacy `imsg` CLI path + DB access.
- [iMessage](/channels/imessage): local `imsg` CLI path + DB access.
- DM security: default is pairing. First DM sends a code; approve via `openclaw pairing approve <channel> <code>` or use allowlists.
6. **Daemon install**
@@ -330,5 +329,5 @@ will prompt to install it (npm or a local path) before it can be configured.
- 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)
- Providers: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord), [Google Chat](/channels/googlechat), [Signal](/channels/signal), [iMessage](/channels/imessage)
- Skills: [Skills](/tools/skills), [Skills config](/tools/skills-config)

View File

@@ -326,20 +326,6 @@ 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
OpenClaw with browser support.
#### Docker Playwright install
If your Gateway runs in Docker, avoid `npx playwright` (npm override conflicts).
Use the bundled CLI instead:
```bash
docker compose run --rm openclaw-cli \
node /app/node_modules/playwright-core/cli.js install chromium
```
To persist browser downloads, set `PLAYWRIGHT_BROWSERS_PATH` (for example,
`/home/node/.cache/ms-playwright`) and make sure `/home/node` is persisted via
`OPENCLAW_HOME_VOLUME` or a bind mount. See [Docker](/install/docker).
## How it works (internal)
High-level flow:

View File

@@ -11,28 +11,7 @@ title: "ClawHub"
ClawHub is the **public skill registry for OpenClaw**. It is a free service: all skills are public, open, and visible to everyone for sharing and reuse. A skill is just a folder with a `SKILL.md` file (plus supporting text files). You can browse skills in the web app or use the CLI to search, install, update, and publish skills.
Site: [clawhub.ai](https://clawhub.ai)
## What ClawHub is
- A public registry for OpenClaw skills.
- A versioned store of skill bundles and metadata.
- A discovery surface for search, tags, and usage signals.
## How it works
1. A user publishes a skill bundle (files + metadata).
2. ClawHub stores the bundle, parses metadata, and assigns a version.
3. The registry indexes the skill for search and discovery.
4. Users browse, download, and install skills in OpenClaw.
## What you can do
- Publish new skills and new versions of existing skills.
- Discover skills by name, tags, or search.
- Download skill bundles and inspect their files.
- Report skills that are abusive or unsafe.
- If you are a moderator, hide, unhide, delete, or ban.
Site: [clawhub.com](https://clawhub.com)
## Who this is for (beginner-friendly)
@@ -71,22 +50,6 @@ By default, the CLI installs skills into `./skills` under your current working d
For more detail on how skills are loaded, shared, and gated, see
[Skills](/tools/skills).
## Skill system overview
A skill is a versioned bundle of files that teaches OpenClaw how to perform a
specific task. Each publish creates a new version, and the registry keeps a
history of versions so users can audit changes.
A typical skill includes:
- A `SKILL.md` file with the primary description and usage.
- Optional configs, scripts, or supporting files used by the skill.
- Metadata such as tags, summary, and install requirements.
ClawHub uses metadata to power discovery and safely expose skill capabilities.
The registry also tracks usage signals (such as stars and downloads) to improve
ranking and visibility.
## What the service provides (features)
- **Public browsing** of skills and their `SKILL.md` content.
@@ -97,24 +60,6 @@ ranking and visibility.
- **Moderation** hooks for approvals and audits.
- **CLI-friendly API** for automation and scripting.
## Security and moderation
ClawHub is open by default. Anyone can upload skills, but a GitHub account must
be at least one week old to publish. This helps slow down abuse without blocking
legitimate contributors.
Reporting and moderation:
- Any signed in user can report a skill.
- Report reasons are required and recorded.
- Each user can have up to 20 active reports at a time.
- Skills with more than 3 unique reports are auto hidden by default.
- Moderators can view hidden skills, unhide them, delete them, or ban users.
- Abusing the report feature can result in account bans.
Interested in becoming a moderator? Ask in the OpenClaw Discord and contact a
moderator or maintainer.
## CLI commands and parameters
Global options (apply to all commands):

View File

@@ -128,8 +128,6 @@ Shell chaining and redirections are not auto-allowed in allowlist mode.
Shell chaining (`&&`, `||`, `;`) is allowed when every top-level segment satisfies the allowlist
(including safe bins or skill auto-allow). Redirections remain unsupported in allowlist mode.
Command substitution (`$()` / backticks) is rejected during allowlist parsing, including inside
double quotes; use single quotes if you need literal `$()` text.
Default safe bins: `jq`, `grep`, `cut`, `sort`, `uniq`, `head`, `tail`, `tr`, `wc`.

View File

@@ -252,7 +252,6 @@ Core parameters:
Notes:
- Enable via `tools.web.fetch.enabled`.
- `maxChars` is clamped by `tools.web.fetch.maxCharsCap` (default 50000).
- Responses are cached (default 15 min).
- For JS-heavy sites, prefer the browser tool.
- See [Web tools](/tools/web) for setup.

View File

@@ -40,7 +40,6 @@ Use `sessions_spawn`:
- Starts a sub-agent run (`deliver: false`, global lane: `subagent`)
- Then runs an announce step and posts the announce reply to the requester chat channel
- Default model: inherits the caller unless you set `agents.defaults.subagents.model` (or per-agent `agents.list[].subagents.model`); an explicit `sessions_spawn.model` still wins.
- Default thinking: inherits the caller unless you set `agents.defaults.subagents.thinking` (or per-agent `agents.list[].subagents.thinking`); an explicit `sessions_spawn.thinking` still wins.
Tool params:

View File

@@ -221,7 +221,6 @@ Fetch a URL and extract readable content.
fetch: {
enabled: true,
maxChars: 50000,
maxCharsCap: 50000,
timeoutSeconds: 30,
cacheTtlMinutes: 15,
maxRedirects: 3,
@@ -253,7 +252,6 @@ Notes:
- Firecrawl requests use bot-circumvention mode and cache results by default.
- `web_fetch` sends a Chrome-like User-Agent and `Accept-Language` by default; override `userAgent` if needed.
- `web_fetch` blocks private/internal hostnames and re-checks redirects (limit with `maxRedirects`).
- `maxChars` is clamped to `tools.web.fetch.maxCharsCap`.
- `web_fetch` is best-effort extraction; some sites will need the browser tool.
- See [Firecrawl](/tools/firecrawl) for key setup and service details.
- Responses are cached (default 15 minutes) to reduce repeated fetches.

View File

@@ -79,11 +79,6 @@ you revoke it with `openclaw devices revoke --device <id> --role <role>`. See
- Logs: live tail of gateway file logs with filter/export (`logs.tail`)
- Update: run a package/git update + restart (`update.run`) with a restart report
Cron jobs panel notes:
- For isolated jobs, delivery defaults to announce summary. You can switch to none if you want internal-only runs.
- Channel/target fields appear when announce is selected.
## Chat behavior
- `chat.send` is **non-blocking**: it acks immediately with `{ runId, status: "started" }` and the response streams via `chat` events.
@@ -202,20 +197,5 @@ Notes:
- `gatewayUrl` is stored in localStorage after load and removed from the URL.
- `token` is stored in localStorage; `password` is kept in memory only.
- Use `wss://` when the Gateway is behind TLS (Tailscale Serve, HTTPS proxy, etc.).
- `gatewayUrl` is only accepted in a top-level window (not embedded) to prevent clickjacking.
- For cross-origin dev setups (e.g. `pnpm ui:dev` to a remote Gateway), add the UI
origin to `gateway.controlUi.allowedOrigins`.
Example:
```json5
{
gateway: {
controlUi: {
allowedOrigins: ["http://localhost:5173"],
},
},
}
```
Remote access setup details: [Remote access](/gateway/remote).

View File

@@ -99,8 +99,6 @@ Open:
- Non-loopback binds still **require** a shared token/password (`gateway.auth` or env).
- The wizard generates a gateway token by default (even on loopback).
- The UI sends `connect.params.auth.token` or `connect.params.auth.password`.
- The Control UI sends anti-clickjacking headers and only accepts same-origin browser
websocket connections unless `gateway.controlUi.allowedOrigins` is set.
- With Serve, Tailscale identity headers can satisfy auth when
`gateway.auth.allowTailscale` is `true` (no token/password required). Set
`gateway.auth.allowTailscale: false` to require explicit credentials. See

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

View File

@@ -1,59 +0,0 @@
# AGENTS.md - zh-CN 文档翻译工作区
## Read When
- 维护 `docs/zh-CN/**`
- 更新中文翻译流水线glossary/TM/prompt
- 处理中文翻译反馈或回归
## Pipelinedocs-i18n
- 源文档:`docs/**/*.md`
- 目标文档:`docs/zh-CN/**/*.md`
- 术语表:`docs/.i18n/glossary.zh-CN.json`
- 翻译记忆库:`docs/.i18n/zh-CN.tm.jsonl`
- 提示词规则:`scripts/docs-i18n/translator.go`
常用运行方式:
```bash
# 批量doc 模式,可并行)
go run scripts/docs-i18n/main.go -mode doc -parallel 6 docs/**/*.md
# 单文件
go run scripts/docs-i18n/main.go -mode doc docs/channels/matrix.md
# 小范围补丁segment 模式,使用 TM不支持并行
go run scripts/docs-i18n/main.go -mode segment docs/channels/matrix.md
```
注意事项:
- doc 模式用于整页翻译segment 模式用于小范围修补(依赖 TM
- 超大文件若超时,优先做**定点替换**或拆分后再跑。
- 翻译后检查中文引号、CJK-Latin 间距和术语一致性。
## zh-CN 样式规则
- CJK-Latin 间距:遵循 W3C CLREQ`Gateway 网关``Skills 配置`)。
- 中文引号:正文/标题使用 `“”`;代码/CLI/键名保持 ASCII 引号。
- 术语保留英文:`Skills``local loopback``Tailscale`
- 代码块/内联代码:保持原样,不在代码内插入空格或引号替换。
## 关键术语(#6995 修复)
- `Gateway 网关`
- `Skills 配置`
- `沙箱`
- `预期键名`
- `配套应用`
- `分块流式传输`
- `设备发现`
## 反馈与变更记录
- 反馈来源GitHub issue #6995
- 反馈用户:@AaronWander@taiyi747@Explorer1092@rendaoyuan
- 变更要点:更新 prompt 规则、扩充 glossary、清理 TM、批量再生成 + 定点修复
- 参考链接https://github.com/openclaw/openclaw/issues/6995

View File

@@ -1,47 +0,0 @@
---
read_when:
- 设置认证过期监控或告警
- 自动化 Claude Code / Codex OAuth 刷新检查
summary: 监控模型提供商的 OAuth 过期状态
title: 认证监控
x-i18n:
generated_at: "2026-02-03T10:03:53Z"
model: claude-opus-4-5
provider: pi
source_hash: eef179af9545ed7ab881f3ccbef998869437fb50cdb4088de8da7223b614fa2b
source_path: automation/auth-monitoring.md
workflow: 15
---
# 认证监控
OpenClaw 通过 `openclaw models status` 提供 OAuth 过期健康状态。请使用该命令进行自动化和告警;脚本是为手机工作流程提供的可选附加功能。
## 推荐方式CLI 检查(可移植)
```bash
openclaw models status --check
```
退出码:
- `0`:正常
- `1`:凭证过期或缺失
- `2`即将过期24 小时内)
此方式适用于 cron/systemd无需额外脚本。
## 可选脚本(运维 / 手机工作流程)
这些脚本位于 `scripts/` 目录下,属于**可选**内容。它们假定你可以通过 SSH 访问 Gateway 网关主机,并针对 systemd + Termux 进行了调优。
- `scripts/claude-auth-status.sh` 现在使用 `openclaw models status --json` 作为数据来源(如果 CLI 不可用则回退到直接读取文件),因此请确保 `openclaw` 在定时器的 `PATH` 中。
- `scripts/auth-monitor.sh`cron/systemd 定时器目标发送告警ntfy 或手机)。
- `scripts/systemd/openclaw-auth-monitor.{service,timer}`systemd 用户定时器。
- `scripts/claude-auth-status.sh`Claude Code + OpenClaw 认证检查器(完整/json/简洁模式)。
- `scripts/mobile-reauth.sh`:通过 SSH 引导的重新认证流程。
- `scripts/termux-quick-auth.sh`:一键小部件状态查看 + 打开认证 URL。
- `scripts/termux-auth-widget.sh`:完整的引导式小部件流程。
- `scripts/termux-sync-widget.sh`:同步 Claude Code 凭证 → OpenClaw。
如果你不需要手机自动化或 systemd 定时器,可以跳过这些脚本。

View File

@@ -1,424 +0,0 @@
---
read_when:
- 调度后台任务或唤醒
- 配置需要与心跳一起或并行运行的自动化
- 在心跳和定时任务之间做选择
summary: Gateway网关调度器的定时任务与唤醒
title: 定时任务
x-i18n:
generated_at: "2026-02-01T19:37:32Z"
model: claude-opus-4-5
provider: pi
source_hash: d43268b0029f1b13d0825ddcc9c06a354987ea17ce02f3b5428a9c68bf936676
source_path: automation/cron-jobs.md
workflow: 14
---
# 定时任务Gateway网关调度器
> **定时任务还是心跳?** 请参阅[定时任务与心跳对比](/automation/cron-vs-heartbeat)了解何时使用哪种方式。
定时任务是 Gateway网关内置的调度器。它持久化任务、在合适的时间唤醒智能体并可选择将输出发送回聊天。
如果你想要 _"每天早上运行"__"20 分钟后提醒智能体"_,定时任务就是对应的机制。
## 简要概述
- 定时任务运行在 **Gateway网关内部**(而非模型内部)。
- 任务持久化存储在 `~/.openclaw/cron/` 下,因此重启不会丢失计划。
- 两种执行方式:
- **主会话**:入队一个系统事件,然后在下一次心跳时运行。
- **隔离式**:在 `cron:<jobId>` 中运行专用智能体轮次,可投递摘要(默认 announce或不投递。
- 唤醒是一等功能:任务可以请求"立即唤醒"或"下次心跳时"。
## 快速开始(可操作)
创建一个一次性提醒,验证其存在,然后立即运行:
```bash
openclaw cron add \
--name "Reminder" \
--at "2026-02-01T16:00:00Z" \
--session main \
--system-event "Reminder: check the cron docs draft" \
--wake now \
--delete-after-run
openclaw cron list
openclaw cron run <job-id> --force
openclaw cron runs --id <job-id>
```
调度一个带投递功能的周期性隔离任务:
```bash
openclaw cron add \
--name "Morning brief" \
--cron "0 7 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize overnight updates." \
--announce \
--channel slack \
--to "channel:C1234567890"
```
## 工具调用等价形式Gateway网关定时任务工具
有关规范的 JSON 结构和示例,请参阅[工具调用的 JSON 模式](/automation/cron-jobs#json-schema-for-tool-calls)。
## 定时任务的存储位置
定时任务默认持久化存储在 Gateway网关主机的 `~/.openclaw/cron/jobs.json` 中。Gateway网关将文件加载到内存中并在更改时写回因此仅在 Gateway网关停止时手动编辑才是安全的。请优先使用 `openclaw cron add/edit` 或定时任务工具调用 API 进行更改。
## 新手友好概述
将定时任务理解为:**何时**运行 + **做什么**
1. **选择调度计划**
- 一次性提醒 → `schedule.kind = "at"`CLI`--at`
- 重复任务 → `schedule.kind = "every"``schedule.kind = "cron"`
- 如果你的 ISO 时间戳省略了时区,将被视为 **UTC**
2. **选择运行位置**
- `sessionTarget: "main"` → 在下一次心跳时使用主会话上下文运行。
- `sessionTarget: "isolated"` → 在 `cron:<jobId>` 中运行专用智能体轮次。
3. **选择负载**
- 主会话 → `payload.kind = "systemEvent"`
- 隔离会话 → `payload.kind = "agentTurn"`
可选:一次性任务(`schedule.kind = "at"`)默认会在成功运行后删除。设置
`deleteAfterRun: false` 可保留它(成功后会禁用)。
## 概念
### 任务
定时任务是一条存储记录,包含:
- 一个**调度计划**(何时运行),
- 一个**负载**(做什么),
- 可选的**投递**(输出发送到哪里)。
- 可选的**智能体绑定**`agentId`在指定智能体下运行任务如果缺失或未知Gateway网关会回退到默认智能体。
任务通过稳定的 `jobId` 标识(用于 CLI/Gateway网关 API
在智能体工具调用中,`jobId` 是规范字段;旧版 `id` 仍可兼容使用。
一次性任务默认会在成功运行后自动删除;设置 `deleteAfterRun: false` 可保留它。
### 调度计划
定时任务支持三种调度类型:
- `at`一次性时间戳ISO 8601 字符串)。
- `every`:固定间隔(毫秒)。
- `cron`5 字段 cron 表达式,可选 IANA 时区。
Cron 表达式使用 `croner`。如果省略时区,将使用 Gateway网关主机的本地时区。
### 主会话与隔离式执行
#### 主会话任务(系统事件)
主会话任务入队一个系统事件,并可选择唤醒心跳运行器。它们必须使用 `payload.kind = "systemEvent"`
- `wakeMode: "next-heartbeat"`(默认):事件等待下一次计划心跳。
- `wakeMode: "now"`:事件触发立即心跳运行。
当你需要正常的心跳提示 + 主会话上下文时,这是最佳选择。参见[心跳](/gateway/heartbeat)。
#### 隔离任务(专用定时会话)
隔离任务在会话 `cron:<jobId>` 中运行专用智能体轮次。
关键行为:
- 提示以 `[cron:<jobId> <任务名称>]` 为前缀,便于追踪。
- 每次运行都会启动一个**全新的会话 ID**(不继承之前的对话)。
- 如果未指定 `delivery`隔离任务会默认以“announce”方式投递摘要。
- `delivery.mode` 可选 `announce`(投递摘要)或 `none`(内部运行)。
对于嘈杂、频繁或"后台杂务"类任务,使用隔离任务可以避免污染你的主聊天记录。
### 负载结构(运行内容)
支持两种负载类型:
- `systemEvent`:仅限主会话,通过心跳提示路由。
- `agentTurn`:仅限隔离会话,运行专用智能体轮次。
常用 `agentTurn` 字段:
- `message`:必填文本提示。
- `model` / `thinking`:可选覆盖(见下文)。
- `timeoutSeconds`:可选超时覆盖。
### 模型和思维覆盖
隔离任务(`agentTurn`)可以覆盖模型和思维级别:
- `model`:提供商/模型字符串(例如 `anthropic/claude-sonnet-4-20250514`)或别名(例如 `opus`
- `thinking`:思维级别(`off``minimal``low``medium``high``xhigh`;仅限 GPT-5.2 + Codex 模型)
注意:你也可以在主会话任务上设置 `model`,但这会更改共享的主会话模型。我们建议仅对隔离任务使用模型覆盖,以避免意外的上下文切换。
优先级解析顺序:
1. 任务负载覆盖(最高优先级)
2. 钩子特定默认值(例如 `hooks.gmail.model`
3. 智能体配置默认值
### 投递(渠道 + 目标)
隔离任务可以通过顶层 `delivery` 配置投递输出:
- `delivery.mode``announce`(投递摘要)或 `none`
- `delivery.channel``whatsapp` / `telegram` / `discord` / `slack` / `mattermost`(插件)/ `signal` / `imessage` / `last`
- `delivery.to`:渠道特定的接收目标
- `delivery.bestEffort`:投递失败时避免任务失败
当启用 announce 投递时,该轮次会抑制消息工具发送;请使用 `delivery.channel`/`delivery.to` 来指定目标。
如果省略 `delivery.channel``delivery.to`,定时任务会回退到主会话的“最后路由”(智能体最后回复的位置)。
目标格式提醒:
- Slack/Discord/Mattermost插件目标应使用明确前缀例如 `channel:<id>``user:<id>`)以避免歧义。
- Telegram 主题应使用 `:topic:` 格式(见下文)。
#### Telegram 投递目标(主题/论坛帖子)
Telegram 通过 `message_thread_id` 支持论坛主题。对于定时任务投递,你可以将主题/帖子编码到 `to` 字段中:
- `-1001234567890`(仅聊天 ID
- `-1001234567890:topic:123`(推荐:明确的主题标记)
- `-1001234567890:123`(简写:数字后缀)
带前缀的目标如 `telegram:...` / `telegram:group:...` 也可接受:
- `telegram:group:-1001234567890:topic:123`
## 工具调用的 JSON 模式
直接调用 Gateway网关 `cron.*` 工具(智能体工具调用或 RPC时使用这些结构。CLI 标志接受人类可读的时间格式如 `20m`,但工具调用应使用 ISO 8601 字符串作为 `schedule.at`,并使用毫秒作为 `schedule.everyMs`
### cron.add 参数
一次性主会话任务(系统事件):
```json
{
"name": "Reminder",
"schedule": { "kind": "at", "at": "2026-02-01T16:00:00Z" },
"sessionTarget": "main",
"wakeMode": "now",
"payload": { "kind": "systemEvent", "text": "Reminder text" },
"deleteAfterRun": true
}
```
带投递的周期性隔离任务:
```json
{
"name": "Morning brief",
"schedule": { "kind": "cron", "expr": "0 7 * * *", "tz": "America/Los_Angeles" },
"sessionTarget": "isolated",
"wakeMode": "next-heartbeat",
"payload": {
"kind": "agentTurn",
"message": "Summarize overnight updates."
},
"delivery": {
"mode": "announce",
"channel": "slack",
"to": "channel:C1234567890",
"bestEffort": true
}
}
```
说明:
- `schedule.kind``at``at`)、`every``everyMs`)或 `cron``expr`,可选 `tz`)。
- `schedule.at` 接受 ISO 8601可省略时区省略时按 UTC 处理)。
- `everyMs` 为毫秒数。
- `sessionTarget` 必须为 `"main"``"isolated"`,且必须与 `payload.kind` 匹配。
- 可选字段:`agentId``description``enabled``deleteAfterRun``delivery`
- `wakeMode` 省略时默认为 `"next-heartbeat"`
### cron.update 参数
```json
{
"jobId": "job-123",
"patch": {
"enabled": false,
"schedule": { "kind": "every", "everyMs": 3600000 }
}
}
```
说明:
- `jobId` 是规范字段;`id` 可兼容使用。
- 在补丁中使用 `agentId: null` 可清除智能体绑定。
### cron.run 和 cron.remove 参数
```json
{ "jobId": "job-123", "mode": "force" }
```
```json
{ "jobId": "job-123" }
```
## 存储与历史
- 任务存储:`~/.openclaw/cron/jobs.json`Gateway网关管理的 JSON
- 运行历史:`~/.openclaw/cron/runs/<jobId>.jsonl`JSONL自动清理
- 覆盖存储路径:配置中的 `cron.store`
## 配置
```json5
{
cron: {
enabled: true, // 默认 true
store: "~/.openclaw/cron/jobs.json",
maxConcurrentRuns: 1, // 默认 1
},
}
```
完全禁用定时任务:
- `cron.enabled: false`(配置)
- `OPENCLAW_SKIP_CRON=1`(环境变量)
## CLI 快速开始
一次性提醒UTC ISO成功后自动删除
```bash
openclaw cron add \
--name "Send reminder" \
--at "2026-01-12T18:00:00Z" \
--session main \
--system-event "Reminder: submit expense report." \
--wake now \
--delete-after-run
```
一次性提醒(主会话,立即唤醒):
```bash
openclaw cron add \
--name "Calendar check" \
--at "20m" \
--session main \
--system-event "Next heartbeat: check calendar." \
--wake now
```
周期性隔离任务(投递到 WhatsApp
```bash
openclaw cron add \
--name "Morning status" \
--cron "0 7 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize inbox + calendar for today." \
--announce \
--channel whatsapp \
--to "+15551234567"
```
周期性隔离任务(投递到 Telegram 主题):
```bash
openclaw cron add \
--name "Nightly summary (topic)" \
--cron "0 22 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize today; send to the nightly topic." \
--announce \
--channel telegram \
--to "-1001234567890:topic:123"
```
带模型和思维覆盖的隔离任务:
```bash
openclaw cron add \
--name "Deep analysis" \
--cron "0 6 * * 1" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Weekly deep analysis of project progress." \
--model "opus" \
--thinking high \
--announce \
--channel whatsapp \
--to "+15551234567"
```
智能体选择(多智能体配置):
```bash
# 将任务绑定到智能体 "ops"(如果该智能体不存在则回退到默认智能体)
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops
# 切换或清除现有任务的智能体
openclaw cron edit <jobId> --agent ops
openclaw cron edit <jobId> --clear-agent
```
手动运行(调试):
```bash
openclaw cron run <jobId> --force
```
编辑现有任务(补丁字段):
```bash
openclaw cron edit <jobId> \
--message "Updated prompt" \
--model "opus" \
--thinking low
```
运行历史:
```bash
openclaw cron runs --id <jobId> --limit 50
```
不创建任务直接发送系统事件:
```bash
openclaw system event --mode now --text "Next heartbeat: check battery."
```
## Gateway网关 API 接口
- `cron.list``cron.status``cron.add``cron.update``cron.remove`
- `cron.run`(强制或到期)、`cron.runs`
如需不创建任务直接发送系统事件,请使用 [`openclaw system event`](/cli/system)。
## 故障排除
### "没有任何任务运行"
- 检查定时任务是否已启用:`cron.enabled``OPENCLAW_SKIP_CRON`
- 检查 Gateway网关是否持续运行定时任务运行在 Gateway网关进程内部
- 对于 `cron` 调度:确认时区(`--tz`)与主机时区的关系。
### Telegram 投递到了错误的位置
- 对于论坛主题,使用 `-100…:topic:<id>` 以确保明确无歧义。
- 如果你在日志或存储的"最后路由"目标中看到 `telegram:...` 前缀,这是正常的;定时任务投递接受这些前缀并仍能正确解析主题 ID。

View File

@@ -1,286 +0,0 @@
---
read_when:
- 决定如何调度周期性任务
- 设置后台监控或通知
- 优化定期检查的 token 用量
summary: 选择心跳还是定时任务进行自动化的指南
title: 定时任务与心跳对比
x-i18n:
generated_at: "2026-02-01T19:38:18Z"
model: claude-opus-4-5
provider: pi
source_hash: 5f71a63181baa41b1c307eb7bfac561df7943d4627077dfa2861eb9f76ab086b
source_path: automation/cron-vs-heartbeat.md
workflow: 14
---
# 定时任务与心跳:何时使用哪种方式
心跳和定时任务都可以按计划运行任务。本指南帮助你根据使用场景选择合适的机制。
## 快速决策指南
| 使用场景 | 推荐方式 | 原因 |
| ------------------------- | -------------------------- | ---------------------------------------- |
| 每 30 分钟检查收件箱 | 心跳 | 可与其他检查批量处理,具备上下文感知能力 |
| 每天上午 9 点准时发送报告 | 定时任务(隔离式) | 需要精确定时 |
| 监控日历中即将到来的事件 | 心跳 | 天然适合周期性感知 |
| 运行每周深度分析 | 定时任务(隔离式) | 独立任务,可使用不同模型 |
| 20 分钟后提醒我 | 定时任务(主会话,`--at` | 精确定时的一次性任务 |
| 后台项目健康检查 | 心跳 | 搭载在现有周期上 |
## 心跳:周期性感知
心跳在**主会话**中以固定间隔运行默认30 分钟)。它的设计目的是让智能体检查各种事项并呈现重要信息。
### 何时使用心跳
- **多个周期性检查**:与其设置 5 个独立的定时任务分别检查收件箱、日历、天气、通知和项目状态,不如用一次心跳批量处理所有内容。
- **上下文感知决策**:智能体拥有完整的主会话上下文,因此可以智能判断哪些紧急、哪些可以等待。
- **对话连续性**:心跳运行共享同一会话,因此智能体记得最近的对话,可以自然地进行后续跟进。
- **低开销监控**:一次心跳替代多个小型轮询任务。
### 心跳优势
- **批量处理多项检查**:一次智能体轮次可以同时审查收件箱、日历和通知。
- **减少 API 调用**:一次心跳比 5 个隔离式定时任务更经济。
- **上下文感知**:智能体了解你一直在做什么,可以据此排定优先级。
- **智能抑制**:如果没有需要关注的事项,智能体回复 `HEARTBEAT_OK`,不会投递任何消息。
- **自然定时**:会根据队列负载略有漂移,但对大多数监控来说没有问题。
### 心跳示例HEARTBEAT.md 检查清单
```md
# Heartbeat checklist
- Check email for urgent messages
- Review calendar for events in next 2 hours
- If a background task finished, summarize results
- If idle for 8+ hours, send a brief check-in
```
智能体在每次心跳时读取此清单,并在一次轮次中处理所有项目。
### 配置心跳
```json5
{
agents: {
defaults: {
heartbeat: {
every: "30m", // 间隔
target: "last", // 告警投递目标
activeHours: { start: "08:00", end: "22:00" }, // 可选
},
},
},
}
```
完整配置请参阅[心跳](/gateway/heartbeat)。
## 定时任务:精确调度
定时任务在**精确时间**运行,可以在隔离会话中运行而不影响主会话上下文。
### 何时使用定时任务
- **需要精确定时**"每周一上午 9:00 发送"(而不是"大约 9 点左右")。
- **独立任务**:不需要对话上下文的任务。
- **不同的模型/思维级别**:需要更强大模型的深度分析。
- **一次性提醒**:使用 `--at` 实现"20 分钟后提醒我"。
- **嘈杂/频繁的任务**:会把主会话历史搞得杂乱的任务。
- **外部触发器**:无论智能体是否处于活跃状态都应独立运行的任务。
### 定时任务优势
- **精确定时**:支持带时区的 5 字段 cron 表达式。
- **会话隔离**:在 `cron:<jobId>` 中运行,不会污染主会话历史。
- **模型覆盖**:可按任务使用更便宜或更强大的模型。
- **投递控制**:隔离任务默认以 `announce` 投递摘要,可选 `none` 仅内部运行。
- **无需智能体上下文**:即使主会话空闲或已压缩,也能运行。
- **一次性支持**`--at` 用于精确的未来时间戳。
### 定时任务示例:每日早间简报
```bash
openclaw cron add \
--name "Morning briefing" \
--cron "0 7 * * *" \
--tz "America/New_York" \
--session isolated \
--message "Generate today's briefing: weather, calendar, top emails, news summary." \
--model opus \
--announce \
--channel whatsapp \
--to "+15551234567"
```
这会在纽约时间每天早上 7:00 准时运行,使用 Opus 保证质量,并直接投递到 WhatsApp。
### 定时任务示例:一次性提醒
```bash
openclaw cron add \
--name "Meeting reminder" \
--at "20m" \
--session main \
--system-event "Reminder: standup meeting starts in 10 minutes." \
--wake now \
--delete-after-run
```
完整 CLI 参考请参阅[定时任务](/automation/cron-jobs)。
## 决策流程图
```
任务是否需要在精确时间运行?
是 -> 使用定时任务
否 -> 继续...
任务是否需要与主会话隔离?
是 -> 使用定时任务(隔离式)
否 -> 继续...
此任务能否与其他周期性检查批量处理?
是 -> 使用心跳(添加到 HEARTBEAT.md
否 -> 使用定时任务
这是一次性提醒吗?
是 -> 使用定时任务配合 --at
否 -> 继续...
是否需要不同的模型或思维级别?
是 -> 使用定时任务(隔离式)配合 --model/--thinking
否 -> 使用心跳
```
## 组合使用
最高效的配置是**两者结合**
1. **心跳**处理常规监控(收件箱、日历、通知),每 30 分钟批量处理一次。
2. **定时任务**处理精确调度(每日报告、每周回顾)和一次性提醒。
### 示例:高效自动化配置
**HEARTBEAT.md**(每 30 分钟检查一次):
```md
# Heartbeat checklist
- Scan inbox for urgent emails
- Check calendar for events in next 2h
- Review any pending tasks
- Light check-in if quiet for 8+ hours
```
**定时任务**(精确定时):
```bash
# 每天早上 7 点的早间简报
openclaw cron add --name "Morning brief" --cron "0 7 * * *" --session isolated --message "..." --announce
# 每周一上午 9 点的项目回顾
openclaw cron add --name "Weekly review" --cron "0 9 * * 1" --session isolated --message "..." --model opus
# 一次性提醒
openclaw cron add --name "Call back" --at "2h" --session main --system-event "Call back the client" --wake now
```
## Lobster带审批的确定性工作流
Lobster 是用于**多步骤工具管道**的工作流运行时,适用于需要确定性执行和明确审批的场景。当任务不只是单次智能体轮次,且你需要可恢复的带人工检查点的工作流时,使用它。
### 何时适合使用 Lobster
- **多步骤自动化**:你需要一个固定的工具调用管道,而不是一次性提示。
- **审批关卡**:副作用应暂停直到你批准,然后继续执行。
- **可恢复运行**:继续暂停的工作流而无需重新运行之前的步骤。
### 如何与心跳和定时任务配合
- **心跳/定时任务**决定*何时*运行。
- **Lobster** 定义运行开始后*执行哪些步骤*。
对于计划性工作流,使用定时任务或心跳触发一次调用 Lobster 的智能体轮次。对于临时工作流,直接调用 Lobster。
### 操作说明(来自代码)
- Lobster 以**本地子进程**`lobster` CLI在工具模式下运行并返回 **JSON 信封**
- 如果工具返回 `needs_approval`,你需要使用 `resumeToken``approve` 标志来恢复。
- 该工具是**可选插件**;建议通过 `tools.alsoAllow: ["lobster"]` 附加启用。
- 如果传入 `lobsterPath`,必须是**绝对路径**。
完整用法和示例请参阅 [Lobster](/tools/lobster)。
## 主会话与隔离会话
心跳和定时任务都可以与主会话交互,但方式不同:
| | 心跳 | 定时任务(主会话) | 定时任务(隔离式) |
| ------ | ------------------------ | ---------------------- | --------------------- |
| 会话 | 主会话 | 主会话(通过系统事件) | `cron:<jobId>` |
| 历史 | 共享 | 共享 | 每次运行全新 |
| 上下文 | 完整 | 完整 | 无(从零开始) |
| 模型 | 主会话模型 | 主会话模型 | 可覆盖 |
| 输出 | 非 `HEARTBEAT_OK` 时投递 | 心跳提示 + 事件 | announce 摘要(默认) |
### 何时使用主会话定时任务
当你需要以下场景时,使用 `--session main` 配合 `--system-event`
- 提醒/事件出现在主会话上下文中
- 智能体在下一次心跳时带着完整上下文处理它
- 不需要单独的隔离运行
```bash
openclaw cron add \
--name "Check project" \
--every "4h" \
--session main \
--system-event "Time for a project health check" \
--wake now
```
### 何时使用隔离式定时任务
当你需要以下场景时,使用 `--session isolated`
- 无先前上下文的全新环境
- 不同的模型或思维设置
- 输出可通过 `announce` 直接投递摘要(或用 `none` 仅内部运行)
- 不会把主会话搞得杂乱的历史记录
```bash
openclaw cron add \
--name "Deep analysis" \
--cron "0 6 * * 0" \
--session isolated \
--message "Weekly codebase analysis..." \
--model opus \
--thinking high \
--announce
```
## 成本考量
| 机制 | 成本特征 |
| ------------------ | ---------------------------------------------- |
| 心跳 | 每 N 分钟一次轮次;随 HEARTBEAT.md 大小扩展 |
| 定时任务(主会话) | 将事件添加到下一次心跳(无隔离轮次) |
| 定时任务(隔离式) | 每个任务一次完整智能体轮次;可使用更便宜的模型 |
**建议**
- 保持 `HEARTBEAT.md` 精简以减少 token 开销。
- 将类似的检查批量放入心跳,而不是创建多个定时任务。
- 如果只需要内部处理,在心跳上使用 `target: "none"`
- 对常规任务使用隔离式定时任务配合更便宜的模型。
## 相关内容
- [心跳](/gateway/heartbeat) - 完整的心跳配置
- [定时任务](/automation/cron-jobs) - 完整的定时任务 CLI 和 API 参考
- [系统](/cli/system) - 系统事件 + 心跳控制

View File

@@ -1,249 +0,0 @@
---
read_when:
- 将 Gmail 收件箱触发器接入 OpenClaw
- 为智能体唤醒设置 Pub/Sub 推送
summary: 通过 gogcli 将 Gmail Pub/Sub 推送接入 OpenClaw webhooks
title: Gmail PubSub
x-i18n:
generated_at: "2026-02-03T07:43:25Z"
model: claude-opus-4-5
provider: pi
source_hash: dfb92133b69177e4e984b7d072f5dc28aa53a9e0cf984a018145ed811aa96195
source_path: automation/gmail-pubsub.md
workflow: 15
---
# Gmail Pub/Sub -> OpenClaw
目标Gmail watch -> Pub/Sub 推送 -> `gog gmail watch serve` -> OpenClaw webhook。
## 前置条件
- 已安装并登录 `gcloud`[安装指南](https://docs.cloud.google.com/sdk/docs/install-sdk))。
- 已安装 `gog` (gogcli) 并为 Gmail 账户授权([gogcli.sh](https://gogcli.sh/))。
- 已启用 OpenClaw hooks参见 [Webhooks](/automation/webhook))。
- 已登录 `tailscale`[tailscale.com](https://tailscale.com/))。支持的设置使用 Tailscale Funnel 作为公共 HTTPS 端点。
其他隧道服务也可以使用,但需要自行配置/不受支持,需要手动接入。
目前,我们支持的是 Tailscale。
示例 hook 配置(启用 Gmail 预设映射):
```json5
{
hooks: {
enabled: true,
token: "OPENCLAW_HOOK_TOKEN",
path: "/hooks",
presets: ["gmail"],
},
}
```
要将 Gmail 摘要投递到聊天界面,请用设置了 `deliver` 以及可选的 `channel`/`to` 的映射覆盖预设:
```json5
{
hooks: {
enabled: true,
token: "OPENCLAW_HOOK_TOKEN",
presets: ["gmail"],
mappings: [
{
match: { path: "gmail" },
action: "agent",
wakeMode: "now",
name: "Gmail",
sessionKey: "hook:gmail:{{messages[0].id}}",
messageTemplate: "New email from {{messages[0].from}}\nSubject: {{messages[0].subject}}\n{{messages[0].snippet}}\n{{messages[0].body}}",
model: "openai/gpt-5.2-mini",
deliver: true,
channel: "last",
// to: "+15551234567"
},
],
},
}
```
如果你想使用固定渠道,请设置 `channel` + `to`。否则 `channel: "last"` 会使用上次的投递路由(默认回退到 WhatsApp
要为 Gmail 运行强制使用更便宜的模型,请在映射中设置 `model``provider/model` 或别名)。如果你强制启用了 `agents.defaults.models`,请将其包含在内。
要专门为 Gmail hooks 设置默认模型和思考级别,请在配置中添加 `hooks.gmail.model` / `hooks.gmail.thinking`
```json5
{
hooks: {
gmail: {
model: "openrouter/meta-llama/llama-3.3-70b-instruct:free",
thinking: "off",
},
},
}
```
注意事项:
- 映射中的每个 hook 的 `model`/`thinking` 仍会覆盖这些默认值。
- 回退顺序:`hooks.gmail.model``agents.defaults.model.fallbacks` → 主模型(认证/速率限制/超时)。
- 如果设置了 `agents.defaults.models`Gmail 模型必须在允许列表中。
- Gmail hook 内容默认使用外部内容安全边界包装。
要禁用(危险),请设置 `hooks.gmail.allowUnsafeExternalContent: true`
要进一步自定义负载处理,请添加 `hooks.mappings` 或在 `hooks.transformsDir` 下添加 JS/TS 转换模块(参见 [Webhooks](/automation/webhook))。
## 向导(推荐)
使用 OpenClaw 助手将所有内容接入在一起(在 macOS 上通过 brew 安装依赖):
```bash
openclaw webhooks gmail setup \
--account openclaw@gmail.com
```
默认设置:
- 使用 Tailscale Funnel 作为公共推送端点。
-`openclaw webhooks gmail run` 写入 `hooks.gmail` 配置。
- 启用 Gmail hook 预设(`hooks.presets: ["gmail"]`)。
路径说明:当启用 `tailscale.mode`OpenClaw 会自动将 `hooks.gmail.serve.path` 设置为 `/`,并将公共路径保持在 `hooks.gmail.tailscale.path`(默认 `/gmail-pubsub`),因为 Tailscale 在代理之前会剥离设置的路径前缀。
如果你需要后端接收带前缀的路径,请将 `hooks.gmail.tailscale.target`(或 `--tailscale-target`)设置为完整 URL`http://127.0.0.1:8788/gmail-pubsub`,并匹配 `hooks.gmail.serve.path`
想要自定义端点?使用 `--push-endpoint <url>``--tailscale off`
平台说明:在 macOS 上,向导通过 Homebrew 安装 `gcloud``gogcli``tailscale`;在 Linux 上请先手动安装它们。
Gateway 网关自动启动(推荐):
-`hooks.enabled=true` 且设置了 `hooks.gmail.account`Gateway 网关会在启动时运行 `gog gmail watch serve` 并自动续期 watch。
- 设置 `OPENCLAW_SKIP_GMAIL_WATCHER=1` 可退出(如果你自己运行守护进程则很有用)。
- 不要同时运行手动守护进程,否则会遇到 `listen tcp 127.0.0.1:8788: bind: address already in use`
手动守护进程(启动 `gog gmail watch serve` + 自动续期):
```bash
openclaw webhooks gmail run
```
## 一次性设置
1. 选择**拥有 `gog` 使用的 OAuth 客户端**的 GCP 项目。
```bash
gcloud auth login
gcloud config set project <project-id>
```
注意Gmail watch 要求 Pub/Sub 主题与 OAuth 客户端位于同一项目中。
2. 启用 API
```bash
gcloud services enable gmail.googleapis.com pubsub.googleapis.com
```
3. 创建主题:
```bash
gcloud pubsub topics create gog-gmail-watch
```
4. 允许 Gmail push 发布:
```bash
gcloud pubsub topics add-iam-policy-binding gog-gmail-watch \
--member=serviceAccount:gmail-api-push@system.gserviceaccount.com \
--role=roles/pubsub.publisher
```
## 启动 watch
```bash
gog gmail watch start \
--account openclaw@gmail.com \
--label INBOX \
--topic projects/<project-id>/topics/gog-gmail-watch
```
保存输出中的 `history_id`(用于调试)。
## 运行推送处理程序
本地示例(共享 token 认证):
```bash
gog gmail watch serve \
--account openclaw@gmail.com \
--bind 127.0.0.1 \
--port 8788 \
--path /gmail-pubsub \
--token <shared> \
--hook-url http://127.0.0.1:18789/hooks/gmail \
--hook-token OPENCLAW_HOOK_TOKEN \
--include-body \
--max-bytes 20000
```
注意事项:
- `--token` 保护推送端点(`x-gog-token``?token=`)。
- `--hook-url` 指向 OpenClaw `/hooks/gmail`(已映射;隔离运行 + 摘要发送到主线程)。
- `--include-body``--max-bytes` 控制发送到 OpenClaw 的正文片段。
推荐:`openclaw webhooks gmail run` 封装了相同的流程并自动续期 watch。
## 暴露处理程序(高级,不受支持)
如果你需要非 Tailscale 隧道,请手动接入并在推送订阅中使用公共 URL不受支持无保护措施
```bash
cloudflared tunnel --url http://127.0.0.1:8788 --no-autoupdate
```
使用生成的 URL 作为推送端点:
```bash
gcloud pubsub subscriptions create gog-gmail-watch-push \
--topic gog-gmail-watch \
--push-endpoint "https://<public-url>/gmail-pubsub?token=<shared>"
```
生产环境:使用稳定的 HTTPS 端点并配置 Pub/Sub OIDC JWT然后运行
```bash
gog gmail watch serve --verify-oidc --oidc-email <svc@...>
```
## 测试
向被监视的收件箱发送一条消息:
```bash
gog gmail send \
--account openclaw@gmail.com \
--to openclaw@gmail.com \
--subject "watch test" \
--body "ping"
```
检查 watch 状态和历史记录:
```bash
gog gmail watch status --account openclaw@gmail.com
gog gmail history --account openclaw@gmail.com --since <historyId>
```
## 故障排除
- `Invalid topicName`:项目不匹配(主题不在 OAuth 客户端项目中)。
- `User not authorized`:主题缺少 `roles/pubsub.publisher`
- 空消息Gmail push 仅提供 `historyId`;通过 `gog gmail history` 获取。
## 清理
```bash
gog gmail watch stop --account openclaw@gmail.com
gcloud pubsub subscriptions delete gog-gmail-watch-push
gcloud pubsub topics delete gog-gmail-watch
```

View File

@@ -1,76 +0,0 @@
---
read_when:
- 添加或修改投票支持
- 调试从 CLI 或 Gateway 网关发送的投票
summary: 通过 Gateway 网关 + CLI 发送投票
title: 投票
x-i18n:
generated_at: "2026-02-03T07:43:12Z"
model: claude-opus-4-5
provider: pi
source_hash: 760339865d27ec40def7996cac1d294d58ab580748ad6b32cc34d285d0314eaf
source_path: automation/poll.md
workflow: 15
---
# 投票
## 支持的渠道
- WhatsAppWeb 渠道)
- Discord
- MS TeamsAdaptive Cards
## CLI
```bash
# WhatsApp
openclaw message poll --target +15555550123 \
--poll-question "Lunch today?" --poll-option "Yes" --poll-option "No" --poll-option "Maybe"
openclaw message poll --target 123456789@g.us \
--poll-question "Meeting time?" --poll-option "10am" --poll-option "2pm" --poll-option "4pm" --poll-multi
# Discord
openclaw message poll --channel discord --target channel:123456789 \
--poll-question "Snack?" --poll-option "Pizza" --poll-option "Sushi"
openclaw message poll --channel discord --target channel:123456789 \
--poll-question "Plan?" --poll-option "A" --poll-option "B" --poll-duration-hours 48
# MS Teams
openclaw message poll --channel msteams --target conversation:19:abc@thread.tacv2 \
--poll-question "Lunch?" --poll-option "Pizza" --poll-option "Sushi"
```
选项:
- `--channel``whatsapp`(默认)、`discord``msteams`
- `--poll-multi`:允许选择多个选项
- `--poll-duration-hours`:仅限 Discord省略时默认为 24
## Gateway 网关 RPC
方法:`poll`
参数:
- `to`(字符串,必需)
- `question`(字符串,必需)
- `options`(字符串数组,必需)
- `maxSelections`(数字,可选)
- `durationHours`(数字,可选)
- `channel`(字符串,可选,默认:`whatsapp`
- `idempotencyKey`(字符串,必需)
## 渠道差异
- WhatsApp2-12 个选项,`maxSelections` 必须在选项数量范围内,忽略 `durationHours`
- Discord2-10 个选项,`durationHours` 限制在 1-768 小时之间(默认 24`maxSelections > 1` 启用多选Discord 不支持严格的选择数量限制。
- MS TeamsAdaptive Card 投票(由 OpenClaw 管理)。无原生投票 API`durationHours` 被忽略。
## 智能体工具Message
使用 `message` 工具的 `poll` 操作(`to``pollQuestion``pollOption`,可选 `pollMulti``pollDurationHours``channel`)。
注意Discord 没有"恰好选择 N 个"模式;`pollMulti` 映射为多选。
Teams 投票以 Adaptive Cards 形式渲染,需要 Gateway 网关保持在线
以将投票记录到 `~/.openclaw/msteams-polls.json`

View File

@@ -1,163 +0,0 @@
---
read_when:
- 添加或更改 webhook 端点
- 将外部系统接入 OpenClaw
summary: 用于唤醒和隔离智能体运行的 Webhook 入口
title: Webhooks
x-i18n:
generated_at: "2026-02-03T07:43:23Z"
model: claude-opus-4-5
provider: pi
source_hash: f26b88864567be82366b1f66a4772ef2813c7846110c62fce6caf7313568265e
source_path: automation/webhook.md
workflow: 15
---
# Webhooks
Gateway 网关可以暴露一个小型 HTTP webhook 端点用于外部触发。
## 启用
```json5
{
hooks: {
enabled: true,
token: "shared-secret",
path: "/hooks",
},
}
```
注意事项:
-`hooks.enabled=true` 时,`hooks.token` 为必填项。
- `hooks.path` 默认为 `/hooks`
## 认证
每个请求必须包含 hook 令牌。推荐使用请求头:
- `Authorization: Bearer <token>`(推荐)
- `x-openclaw-token: <token>`
- `?token=<token>`(已弃用;会记录警告日志,将在未来的主要版本中移除)
## 端点
### `POST /hooks/wake`
请求体:
```json
{ "text": "System line", "mode": "now" }
```
- `text` **必填**(字符串):事件描述(例如"收到新邮件")。
- `mode` 可选(`now` | `next-heartbeat`):是否立即触发心跳(默认 `now`)或等待下一次定期检查。
效果:
- 为**主**会话加入一个系统事件队列
- 如果 `mode=now`,则立即触发心跳
### `POST /hooks/agent`
请求体:
```json
{
"message": "Run this",
"name": "Email",
"sessionKey": "hook:email:msg-123",
"wakeMode": "now",
"deliver": true,
"channel": "last",
"to": "+15551234567",
"model": "openai/gpt-5.2-mini",
"thinking": "low",
"timeoutSeconds": 120
}
```
- `message` **必填**(字符串):智能体要处理的提示或消息。
- `name` 可选字符串hook 的可读名称(例如"GitHub"),用作会话摘要的前缀。
- `sessionKey` 可选(字符串):用于标识智能体会话的键。默认为随机的 `hook:<uuid>`。使用一致的键可以在 hook 上下文中进行多轮对话。
- `wakeMode` 可选(`now` | `next-heartbeat`):是否立即触发心跳(默认 `now`)或等待下一次定期检查。
- `deliver` 可选(布尔值):如果为 `true`,智能体的响应将发送到消息渠道。默认为 `true`。仅为心跳确认的响应会自动跳过。
- `channel` 可选(字符串):用于投递的消息渠道。可选值:`last``whatsapp``telegram``discord``slack``mattermost`(插件)、`signal``imessage``msteams`。默认为 `last`
- `to` 可选(字符串):渠道的接收者标识符(例如 WhatsApp/Signal 的电话号码、Telegram 的聊天 ID、Discord/Slack/Mattermost插件的频道 ID、MS Teams 的会话 ID。默认为主会话中的最后一个接收者。
- `model` 可选(字符串):模型覆盖(例如 `anthropic/claude-3-5-sonnet` 或别名)。如果有限制,必须在允许的模型列表中。
- `thinking` 可选(字符串):思考级别覆盖(例如 `low``medium``high`)。
- `timeoutSeconds` 可选(数字):智能体运行的最大持续时间(秒)。
效果:
- 运行一个**隔离的**智能体回合(独立的会话键)
- 始终在**主**会话中发布摘要
- 如果 `wakeMode=now`,则立即触发心跳
### `POST /hooks/<name>`(映射)
自定义 hook 名称通过 `hooks.mappings` 解析(见配置)。映射可以将任意请求体转换为 `wake``agent` 操作,支持可选的模板或代码转换。
映射选项(摘要):
- `hooks.presets: ["gmail"]` 启用内置的 Gmail 映射。
- `hooks.mappings` 允许你在配置中定义 `match``action` 和模板。
- `hooks.transformsDir` + `transform.module` 加载 JS/TS 模块用于自定义逻辑。
- 使用 `match.source` 保持通用的接收端点(基于请求体的路由)。
- TS 转换需要 TS 加载器(例如 `bun``tsx`)或运行时预编译的 `.js`
- 在映射上设置 `deliver: true` + `channel`/`to` 可将回复路由到聊天界面(`channel` 默认为 `last`,回退到 WhatsApp
- `allowUnsafeExternalContent: true` 禁用该 hook 的外部内容安全包装(危险;仅用于受信任的内部来源)。
- `openclaw webhooks gmail setup``openclaw webhooks gmail run` 写入 `hooks.gmail` 配置。完整的 Gmail 监听流程请参阅 [Gmail Pub/Sub](/automation/gmail-pubsub)。
## 响应
- `200` 用于 `/hooks/wake`
- `202` 用于 `/hooks/agent`(异步运行已启动)
- `401` 认证失败
- `400` 请求体无效
- `413` 请求体过大
## 示例
```bash
curl -X POST http://127.0.0.1:18789/hooks/wake \
-H 'Authorization: Bearer SECRET' \
-H 'Content-Type: application/json' \
-d '{"text":"New email received","mode":"now"}'
```
```bash
curl -X POST http://127.0.0.1:18789/hooks/agent \
-H 'x-openclaw-token: SECRET' \
-H 'Content-Type: application/json' \
-d '{"message":"Summarize inbox","name":"Email","wakeMode":"next-heartbeat"}'
```
### 使用不同的模型
在智能体请求体(或映射)中添加 `model` 以覆盖该次运行的模型:
```bash
curl -X POST http://127.0.0.1:18789/hooks/agent \
-H 'x-openclaw-token: SECRET' \
-H 'Content-Type: application/json' \
-d '{"message":"Summarize inbox","name":"Email","model":"openai/gpt-5.2-mini"}'
```
如果你启用了 `agents.defaults.models` 限制,请确保覆盖的模型包含在其中。
```bash
curl -X POST http://127.0.0.1:18789/hooks/gmail \
-H 'Authorization: Bearer SECRET' \
-H 'Content-Type: application/json' \
-d '{"source":"gmail","messages":[{"from":"Ada","subject":"Hello","snippet":"Hi"}]}'
```
## 安全
- 将 hook 端点保持在 loopback、tailnet 或受信任的反向代理之后。
- 使用专用的 hook 令牌;不要复用 Gateway 网关认证令牌。
- 避免在 webhook 日志中包含敏感的原始请求体。
- Hook 请求体默认被视为不受信任并使用安全边界包装。如果你必须为特定 hook 禁用此功能,请在该 hook 的映射中设置 `allowUnsafeExternalContent: true`(危险)。

View File

@@ -1,170 +0,0 @@
---
read_when:
- 你想在 OpenClaw 中使用 Amazon Bedrock 模型
- 你需要为模型调用配置 AWS 凭证/区域
summary: 在 OpenClaw 中使用 Amazon BedrockConverse API模型
title: Amazon Bedrock
x-i18n:
generated_at: "2026-02-03T10:04:01Z"
model: claude-opus-4-5
provider: pi
source_hash: 318f1048451a1910b70522e2f7f9dfc87084de26d9e3938a29d372eed32244a8
source_path: bedrock.md
workflow: 15
---
# Amazon Bedrock
OpenClaw 可以通过 piai 的 **Bedrock Converse** 流式提供商使用 **Amazon Bedrock** 模型。Bedrock 认证使用 **AWS SDK 默认凭证链**,而非 API 密钥。
## piai 支持的功能
- 提供商:`amazon-bedrock`
- API`bedrock-converse-stream`
- 认证AWS 凭证(环境变量、共享配置或实例角色)
- 区域:`AWS_REGION``AWS_DEFAULT_REGION`(默认:`us-east-1`
## 自动模型发现
如果检测到 AWS 凭证OpenClaw 可以自动发现支持**流式传输**和**文本输出**的 Bedrock 模型。发现功能使用 `bedrock:ListFoundationModels`并会被缓存默认1 小时)。
配置选项位于 `models.bedrockDiscovery` 下:
```json5
{
models: {
bedrockDiscovery: {
enabled: true,
region: "us-east-1",
providerFilter: ["anthropic", "amazon"],
refreshInterval: 3600,
defaultContextWindow: 32000,
defaultMaxTokens: 4096,
},
},
}
```
注意事项:
- `enabled` 在存在 AWS 凭证时默认为 `true`
- `region` 默认为 `AWS_REGION``AWS_DEFAULT_REGION`,然后是 `us-east-1`
- `providerFilter` 匹配 Bedrock 提供商名称(例如 `anthropic`)。
- `refreshInterval` 单位为秒;设置为 `0` 可禁用缓存。
- `defaultContextWindow`(默认:`32000`)和 `defaultMaxTokens`(默认:`4096`)用于已发现的模型(如果你知道模型限制,可以覆盖这些值)。
## 设置(手动)
1. 确保 AWS 凭证在 **Gateway 网关主机**上可用:
```bash
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_REGION="us-east-1"
# 可选:
export AWS_SESSION_TOKEN="..."
export AWS_PROFILE="your-profile"
# 可选Bedrock API 密钥/Bearer 令牌):
export AWS_BEARER_TOKEN_BEDROCK="..."
```
2. 在配置中添加 Bedrock 提供商和模型(无需 `apiKey`
```json5
{
models: {
providers: {
"amazon-bedrock": {
baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
api: "bedrock-converse-stream",
auth: "aws-sdk",
models: [
{
id: "anthropic.claude-opus-4-5-20251101-v1:0",
name: "Claude Opus 4.5 (Bedrock)",
reasoning: true,
input: ["text", "image"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 200000,
maxTokens: 8192,
},
],
},
},
},
agents: {
defaults: {
model: { primary: "amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0" },
},
},
}
```
## EC2 实例角色
当在附加了 IAM 角色的 EC2 实例上运行 OpenClaw 时AWS SDK 会自动使用实例元数据服务IMDS进行认证。但是OpenClaw 的凭证检测目前只检查环境变量,不检查 IMDS 凭证。
**解决方法:** 设置 `AWS_PROFILE=default` 以表明 AWS 凭证可用。实际认证仍然通过 IMDS 使用实例角色。
```bash
# 添加到 ~/.bashrc 或你的 shell 配置文件
export AWS_PROFILE=default
export AWS_REGION=us-east-1
```
EC2 实例角色**所需的 IAM 权限**
- `bedrock:InvokeModel`
- `bedrock:InvokeModelWithResponseStream`
- `bedrock:ListFoundationModels`(用于自动发现)
或者附加托管策略 `AmazonBedrockFullAccess`
**快速设置:**
```bash
# 1. 创建 IAM 角色和实例配置文件
aws iam create-role --role-name EC2-Bedrock-Access \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "ec2.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
aws iam attach-role-policy --role-name EC2-Bedrock-Access \
--policy-arn arn:aws:iam::aws:policy/AmazonBedrockFullAccess
aws iam create-instance-profile --instance-profile-name EC2-Bedrock-Access
aws iam add-role-to-instance-profile \
--instance-profile-name EC2-Bedrock-Access \
--role-name EC2-Bedrock-Access
# 2. 附加到你的 EC2 实例
aws ec2 associate-iam-instance-profile \
--instance-id i-xxxxx \
--iam-instance-profile Name=EC2-Bedrock-Access
# 3. 在 EC2 实例上启用发现功能
openclaw config set models.bedrockDiscovery.enabled true
openclaw config set models.bedrockDiscovery.region us-east-1
# 4. 设置解决方法所需的环境变量
echo 'export AWS_PROFILE=default' >> ~/.bashrc
echo 'export AWS_REGION=us-east-1' >> ~/.bashrc
source ~/.bashrc
# 5. 验证模型已被发现
openclaw models list
```
## 注意事项
- Bedrock 需要在你的 AWS 账户/区域中启用**模型访问**。
- 自动发现需要 `bedrock:ListFoundationModels` 权限。
- 如果你使用配置文件,请在 Gateway 网关主机上设置 `AWS_PROFILE`
- OpenClaw 按以下顺序获取凭证来源:`AWS_BEARER_TOKEN_BEDROCK`,然后是 `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`,然后是 `AWS_PROFILE`,最后是默认的 AWS SDK 链。
- 推理支持取决于模型;请查看 Bedrock 模型卡了解当前功能。
- 如果你更喜欢托管密钥流程,也可以在 Bedrock 前面放置一个 OpenAI 兼容的代理,并将其配置为 OpenAI 提供商。

View File

@@ -1,48 +0,0 @@
---
read_when:
- 你想使用 Brave Search 进行 web_search
- 你需要 BRAVE_API_KEY 或套餐详情
summary: 用于 web_search 的 Brave Search API 设置
title: Brave Search
x-i18n:
generated_at: "2026-02-03T07:43:09Z"
model: claude-opus-4-5
provider: pi
source_hash: cdcb037b092b8a10609f02acf062b4164cb826ac22bdb3fb2909c842a1405341
source_path: brave-search.md
workflow: 15
---
# Brave Search API
OpenClaw 使用 Brave Search 作为 `web_search` 的默认提供商。
## 获取 API 密钥
1. 在 https://brave.com/search/api/ 创建 Brave Search API 账户
2. 在控制面板中,选择 **Data for Search** 套餐并生成 API 密钥。
3. 将密钥存储在配置中(推荐),或在 Gateway 网关环境中设置 `BRAVE_API_KEY`
## 配置示例
```json5
{
tools: {
web: {
search: {
provider: "brave",
apiKey: "BRAVE_API_KEY_HERE",
maxResults: 5,
timeoutSeconds: 30,
},
},
},
}
```
## 注意事项
- Data for AI 套餐与 `web_search` **不**兼容。
- Brave 提供免费层级和付费套餐;请查看 Brave API 门户了解当前限制。
请参阅 [Web 工具](/tools/web) 了解完整的 web_search 配置。

View File

@@ -1,449 +0,0 @@
---
read_when:
- 配置广播群组
- 调试 WhatsApp 中的多智能体回复
status: experimental
summary: 向多个智能体广播 WhatsApp 消息
title: 广播群组
x-i18n:
generated_at: "2026-02-03T07:43:43Z"
model: claude-opus-4-5
provider: pi
source_hash: eaeb4035912c49413e012177cf0bd28b348130d30d3317674418dca728229b70
source_path: broadcast-groups.md
workflow: 15
---
# 广播群组
**状态:** 实验性功能
**版本:** 于 2026.1.9 版本新增
## 概述
广播群组允许多个智能体同时处理并响应同一条消息。这使你能够在单个 WhatsApp 群组或私信中创建协同工作的专业智能体团队——全部使用同一个手机号码。
当前范围:**仅限 WhatsApp**web 渠道)。
广播群组在渠道白名单和群组激活规则之后进行评估。在 WhatsApp 群组中,这意味着广播会在 OpenClaw 正常回复时发生(例如:被提及时,具体取决于你的群组设置)。
## 使用场景
### 1. 专业智能体团队
部署多个具有原子化、专注职责的智能体:
```
Group: "Development Team"
Agents:
- CodeReviewer (reviews code snippets)
- DocumentationBot (generates docs)
- SecurityAuditor (checks for vulnerabilities)
- TestGenerator (suggests test cases)
```
每个智能体处理相同的消息并提供其专业视角。
### 2. 多语言支持
```
Group: "International Support"
Agents:
- Agent_EN (responds in English)
- Agent_DE (responds in German)
- Agent_ES (responds in Spanish)
```
### 3. 质量保证工作流
```
Group: "Customer Support"
Agents:
- SupportAgent (provides answer)
- QAAgent (reviews quality, only responds if issues found)
```
### 4. 任务自动化
```
Group: "Project Management"
Agents:
- TaskTracker (updates task database)
- TimeLogger (logs time spent)
- ReportGenerator (creates summaries)
```
## 配置
### 基本设置
添加一个顶层 `broadcast` 部分(与 `bindings` 同级)。键为 WhatsApp peer id
- 群聊:群组 JID例如 `120363403215116621@g.us`
- 私信E.164 格式的电话号码(例如 `+15551234567`
```json
{
"broadcast": {
"120363403215116621@g.us": ["alfred", "baerbel", "assistant3"]
}
}
```
**结果:** 当 OpenClaw 在此聊天中回复时,将运行所有三个智能体。
### 处理策略
控制智能体如何处理消息:
#### 并行(默认)
所有智能体同时处理:
```json
{
"broadcast": {
"strategy": "parallel",
"120363403215116621@g.us": ["alfred", "baerbel"]
}
}
```
#### 顺序
智能体按顺序处理(后一个等待前一个完成):
```json
{
"broadcast": {
"strategy": "sequential",
"120363403215116621@g.us": ["alfred", "baerbel"]
}
}
```
### 完整示例
```json
{
"agents": {
"list": [
{
"id": "code-reviewer",
"name": "Code Reviewer",
"workspace": "/path/to/code-reviewer",
"sandbox": { "mode": "all" }
},
{
"id": "security-auditor",
"name": "Security Auditor",
"workspace": "/path/to/security-auditor",
"sandbox": { "mode": "all" }
},
{
"id": "docs-generator",
"name": "Documentation Generator",
"workspace": "/path/to/docs-generator",
"sandbox": { "mode": "all" }
}
]
},
"broadcast": {
"strategy": "parallel",
"120363403215116621@g.us": ["code-reviewer", "security-auditor", "docs-generator"],
"120363424282127706@g.us": ["support-en", "support-de"],
"+15555550123": ["assistant", "logger"]
}
}
```
## 工作原理
### 消息流程
1. **接收消息** 到达 WhatsApp 群组
2. **广播检查**:系统检查 peer ID 是否在 `broadcast`
3. **如果在广播列表中**
- 所有列出的智能体处理该消息
- 每个智能体有自己的会话键和隔离的上下文
- 智能体并行处理(默认)或顺序处理
4. **如果不在广播列表中**
- 应用正常路由(第一个匹配的绑定)
注意:广播群组不会绕过渠道白名单或群组激活规则(提及/命令等)。它们只改变消息符合处理条件时*运行哪些智能体*。
### 会话隔离
广播群组中的每个智能体完全独立维护:
- **会话键**`agent:alfred:whatsapp:group:120363...` vs `agent:baerbel:whatsapp:group:120363...`
- **对话历史**(智能体看不到其他智能体的消息)
- **工作空间**(如果配置了则使用独立的沙箱)
- **工具访问权限**(不同的允许/拒绝列表)
- **记忆/上下文**(独立的 IDENTITY.md、SOUL.md 等)
- **群组上下文缓冲区**(用于上下文的最近群组消息)按 peer 共享,因此所有广播智能体在被触发时看到相同的上下文
这允许每个智能体拥有:
- 不同的个性
- 不同的工具访问权限(例如只读 vs 读写)
- 不同的模型(例如 opus vs sonnet
- 不同的已安装 Skills
### 示例:隔离的会话
在群组 `120363403215116621@g.us` 中,智能体为 `["alfred", "baerbel"]`
**Alfred 的上下文:**
```
Session: agent:alfred:whatsapp:group:120363403215116621@g.us
History: [user message, alfred's previous responses]
Workspace: /Users/pascal/openclaw-alfred/
Tools: read, write, exec
```
**Bärbel 的上下文:**
```
Session: agent:baerbel:whatsapp:group:120363403215116621@g.us
History: [user message, baerbel's previous responses]
Workspace: /Users/pascal/openclaw-baerbel/
Tools: read only
```
## 最佳实践
### 1. 保持智能体专注
将每个智能体设计为具有单一、明确的职责:
```json
{
"broadcast": {
"DEV_GROUP": ["formatter", "linter", "tester"]
}
}
```
**好的做法:** 每个智能体只有一个任务
**不好的做法:** 一个通用的"dev-helper"智能体
### 2. 使用描述性名称
明确每个智能体的功能:
```json
{
"agents": {
"security-scanner": { "name": "Security Scanner" },
"code-formatter": { "name": "Code Formatter" },
"test-generator": { "name": "Test Generator" }
}
}
```
### 3. 配置不同的工具访问权限
只给智能体提供它们需要的工具:
```json
{
"agents": {
"reviewer": {
"tools": { "allow": ["read", "exec"] } // Read-only
},
"fixer": {
"tools": { "allow": ["read", "write", "edit", "exec"] } // Read-write
}
}
}
```
### 4. 监控性能
当有多个智能体时,请考虑:
- 使用 `"strategy": "parallel"`(默认)以提高速度
- 将广播群组限制在 5-10 个智能体
- 为较简单的智能体使用较快的模型
### 5. 优雅地处理失败
智能体独立失败。一个智能体的错误不会阻塞其他智能体:
```
Message → [Agent A ✓, Agent B ✗ error, Agent C ✓]
Result: Agent A and C respond, Agent B logs error
```
## 兼容性
### 提供商
广播群组目前支持:
- ✅ WhatsApp已实现
- 🚧 Telegram计划中
- 🚧 Discord计划中
- 🚧 Slack计划中
### 路由
广播群组与现有路由一起工作:
```json
{
"bindings": [
{
"match": { "channel": "whatsapp", "peer": { "kind": "group", "id": "GROUP_A" } },
"agentId": "alfred"
}
],
"broadcast": {
"GROUP_B": ["agent1", "agent2"]
}
}
```
- `GROUP_A`:只有 alfred 响应(正常路由)
- `GROUP_B`agent1 和 agent2 都响应(广播)
**优先级:** `broadcast` 优先于 `bindings`
## 故障排除
### 智能体不响应
**检查:**
1. 智能体 ID 存在于 `agents.list`
2. Peer ID 格式正确(例如 `120363403215116621@g.us`
3. 智能体不在拒绝列表中
**调试:**
```bash
tail -f ~/.openclaw/logs/gateway.log | grep broadcast
```
### 只有一个智能体响应
**原因:** Peer ID 可能在 `bindings` 中但不在 `broadcast` 中。
**修复:** 添加到广播配置或从绑定中移除。
### 性能问题
**如果智能体较多时速度较慢:**
- 减少每个群组的智能体数量
- 使用较轻的模型sonnet 而非 opus
- 检查沙箱启动时间
## 示例
### 示例 1代码审查团队
```json
{
"broadcast": {
"strategy": "parallel",
"120363403215116621@g.us": [
"code-formatter",
"security-scanner",
"test-coverage",
"docs-checker"
]
},
"agents": {
"list": [
{
"id": "code-formatter",
"workspace": "~/agents/formatter",
"tools": { "allow": ["read", "write"] }
},
{
"id": "security-scanner",
"workspace": "~/agents/security",
"tools": { "allow": ["read", "exec"] }
},
{
"id": "test-coverage",
"workspace": "~/agents/testing",
"tools": { "allow": ["read", "exec"] }
},
{ "id": "docs-checker", "workspace": "~/agents/docs", "tools": { "allow": ["read"] } }
]
}
}
```
**用户发送:** 代码片段
**响应:**
- code-formatter"修复了缩进并添加了类型提示"
- security-scanner"⚠️ 第 12 行存在 SQL 注入漏洞"
- test-coverage"覆盖率为 45%,缺少错误情况的测试"
- docs-checker"函数 `process_data` 缺少文档字符串"
### 示例 2多语言支持
```json
{
"broadcast": {
"strategy": "sequential",
"+15555550123": ["detect-language", "translator-en", "translator-de"]
},
"agents": {
"list": [
{ "id": "detect-language", "workspace": "~/agents/lang-detect" },
{ "id": "translator-en", "workspace": "~/agents/translate-en" },
{ "id": "translator-de", "workspace": "~/agents/translate-de" }
]
}
}
```
## API 参考
### 配置模式
```typescript
interface OpenClawConfig {
broadcast?: {
strategy?: "parallel" | "sequential";
[peerId: string]: string[];
};
}
```
### 字段
- `strategy`(可选):如何处理智能体
- `"parallel"`(默认):所有智能体同时处理
- `"sequential"`:智能体按数组顺序处理
- `[peerId]`WhatsApp 群组 JID、E.164 号码或其他 peer ID
- 值:应处理消息的智能体 ID 数组
## 限制
1. **最大智能体数:** 无硬性限制,但 10 个以上智能体可能会较慢
2. **共享上下文:** 智能体看不到彼此的响应(设计如此)
3. **消息顺序:** 并行响应可能以任意顺序到达
4. **速率限制:** 所有智能体都计入 WhatsApp 速率限制
## 未来增强
计划中的功能:
- [ ] 共享上下文模式(智能体可以看到彼此的响应)
- [ ] 智能体协调(智能体可以相互发信号)
- [ ] 动态智能体选择(根据消息内容选择智能体)
- [ ] 智能体优先级(某些智能体先于其他智能体响应)
## 另请参阅
- [多智能体配置](/multi-agent-sandbox-tools)
- [路由配置](/concepts/channel-routing)
- [会话管理](/concepts/sessions)

View File

@@ -1,271 +0,0 @@
---
read_when:
- 设置 BlueBubbles 渠道
- 排查 webhook 配对问题
- 在 macOS 上配置 iMessage
summary: 通过 BlueBubbles macOS 服务器使用 iMessageREST 发送/接收、输入状态、回应、配对、高级操作)。
title: BlueBubbles
x-i18n:
generated_at: "2026-02-03T10:04:52Z"
model: claude-opus-4-5
provider: pi
source_hash: 3aae277a8bec479800a7f6268bfbca912c65a4aadc6e513694057fb873597b69
source_path: channels/bluebubbles.md
workflow: 15
---
# BlueBubblesmacOS REST
状态:内置插件,通过 HTTP 与 BlueBubbles macOS 服务器通信。由于其更丰富的 API 和更简便的设置,**推荐用于 iMessage 集成**,优于旧版 imsg 渠道。
## 概述
- 通过 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 调用。
- 附件和贴纸作为入站媒体被接收(并在可能时呈现给智能体)。
- 配对/白名单的工作方式与其他渠道相同(`/start/pairing` 等),使用 `channels.bluebubbles.allowFrom` + 配对码。
- 回应作为系统事件呈现,与 Slack/Telegram 类似,智能体可以在回复前"提及"它们。
- 高级功能:编辑、撤回、回复线程、消息效果、群组管理。
## 快速开始
1. 在你的 Mac 上安装 BlueBubbles 服务器(按照 [bluebubbles.app/install](https://bluebubbles.app/install) 的说明操作)。
2. 在 BlueBubbles 配置中,启用 web API 并设置密码。
3. 运行 `openclaw onboard` 并选择 BlueBubbles或手动配置
```json5
{
channels: {
bluebubbles: {
enabled: true,
serverUrl: "http://192.168.1.100:1234",
password: "example-password",
webhookPath: "/bluebubbles-webhook",
},
},
}
```
4. 将 BlueBubbles webhook 指向你的 Gateway 网关(示例:`https://your-gateway-host:3000/bluebubbles-webhook?password=<password>`)。
5. 启动 Gateway 网关;它将注册 webhook 处理程序并开始配对。
## 新手引导
BlueBubbles 可在交互式设置向导中使用:
```
openclaw onboard
```
向导会提示输入:
- **服务器 URL**必填BlueBubbles 服务器地址(例如 `http://192.168.1.100:1234`
- **密码**(必填):来自 BlueBubbles 服务器设置的 API 密码
- **Webhook 路径**(可选):默认为 `/bluebubbles-webhook`
- **私信策略**:配对、白名单、开放或禁用
- **白名单**:电话号码、电子邮件或聊天目标
你也可以通过 CLI 添加 BlueBubbles
```
openclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --password <password>
```
## 访问控制(私信 + 群组)
私信:
- 默认:`channels.bluebubbles.dmPolicy = "pairing"`。
- 未知发送者会收到配对码;在批准之前消息会被忽略(配对码 1 小时后过期)。
- 批准方式:
- `openclaw pairing list bluebubbles`
- `openclaw pairing approve bluebubbles <CODE>`
- 配对是默认的令牌交换方式。详情:[配对](/start/pairing)
群组:
- `channels.bluebubbles.groupPolicy = open | allowlist | disabled`(默认:`allowlist`)。
- 当设置为 `allowlist` 时,`channels.bluebubbles.groupAllowFrom` 控制谁可以在群组中触发。
### 提及门控(群组)
BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致:
- 使用 `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)检测提及。
- 当群组启用 `requireMention` 时,智能体仅在被提及时响应。
- 来自授权发送者的控制命令会绕过提及门控。
单群组配置:
```json5
{
channels: {
bluebubbles: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15555550123"],
groups: {
"*": { requireMention: true }, // 所有群组的默认设置
"iMessage;-;chat123": { requireMention: false }, // 特定群组的覆盖设置
},
},
},
}
```
### 命令门控
- 控制命令(例如 `/config`、`/model`)需要授权。
- 使用 `allowFrom` 和 `groupAllowFrom` 确定命令授权。
- 授权发送者即使在群组中未被提及也可以运行控制命令。
## 输入状态 + 已读回执
- **输入指示器**:在响应生成前和生成期间自动发送。
- **已读回执**:由 `channels.bluebubbles.sendReadReceipts` 控制(默认:`true`)。
- **输入指示器**OpenClaw 发送输入开始事件BlueBubbles 在发送或超时时自动清除输入状态(通过 DELETE 手动停止不可靠)。
```json5
{
channels: {
bluebubbles: {
sendReadReceipts: false, // 禁用已读回执
},
},
}
```
## 高级操作
BlueBubbles 在配置中启用时支持高级消息操作:
```json5
{
channels: {
bluebubbles: {
actions: {
reactions: true, // tapback默认true
edit: true, // 编辑已发送消息macOS 13+,在 macOS 26 Tahoe 上不可用)
unsend: true, // 撤回消息macOS 13+
reply: true, // 通过消息 GUID 进行回复线程
sendWithEffect: true, // 消息效果slam、loud 等)
renameGroup: true, // 重命名群聊
setGroupIcon: true, // 设置群聊图标/照片(在 macOS 26 Tahoe 上不稳定)
addParticipant: true, // 将参与者添加到群组
removeParticipant: true, // 从群组移除参与者
leaveGroup: true, // 离开群聊
sendAttachment: true, // 发送附件/媒体
},
},
},
}
```
可用操作:
- **react**:添加/移除 tapback 回应(`messageId`、`emoji`、`remove`
- **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`
- **sendAttachment**:发送媒体/文件(`to`、`buffer`、`filename`、`asVoice`
- 语音备忘录:将 `asVoice: true` 与 **MP3** 或 **CAF** 音频一起设置,以 iMessage 语音消息形式发送。BlueBubbles 在发送语音备忘录时会将 MP3 转换为 CAF。
### 消息 ID短格式 vs 完整格式)
OpenClaw 可能会显示*短*消息 ID例如 `1`、`2`)以节省 token。
- `MessageSid` / `ReplyToId` 可以是短 ID。
- `MessageSidFull` / `ReplyToIdFull` 包含提供商的完整 ID。
- 短 ID 存储在内存中;它们可能在重启或缓存清除后过期。
- 操作接受短或完整的 `messageId`,但如果短 ID 不再可用将会报错。
对于持久化自动化和存储,请使用完整 ID
- 模板:`{{MessageSidFull}}`、`{{ReplyToIdFull}}`
- 上下文:入站负载中的 `MessageSidFull` / `ReplyToIdFull`
参见[配置](/gateway/configuration)了解模板变量。
## 分块流式传输
控制响应是作为单条消息发送还是分块流式传输:
```json5
{
channels: {
bluebubbles: {
blockStreaming: true, // 启用分块流式传输(默认关闭)
},
},
}
```
## 媒体 + 限制
- 入站附件会被下载并存储在媒体缓存中。
- 媒体上限通过 `channels.bluebubbles.mediaMaxMb` 设置默认8 MB
- 出站文本按 `channels.bluebubbles.textChunkLimit` 分块默认4000 字符)。
## 配置参考
完整配置:[配置](/gateway/configuration)
提供商选项:
- `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.groupPolicy``open | allowlist | disabled`(默认:`allowlist`)。
- `channels.bluebubbles.groupAllowFrom`:群组发送者白名单。
- `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.actions`:启用/禁用特定操作。
- `channels.bluebubbles.accounts`:多账户配置。
相关全局选项:
- `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)。
- `messages.responsePrefix`。
## 地址 / 投递目标
优先使用 `chat_guid` 以获得稳定的路由:
- `chat_guid:iMessage;-;+15555550123`(群组推荐)
- `chat_id:123`
- `chat_identifier:...`
- 直接句柄:`+15555550123`、`user@example.com`
- 如果直接句柄没有现有的私信聊天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 + 防火墙规则。
## 故障排除
- 如果输入/已读事件停止工作,请检查 BlueBubbles webhook 日志并验证 Gateway 网关路径是否与 `channels.bluebubbles.webhookPath` 匹配。
- 配对码在一小时后过期;使用 `openclaw pairing list bluebubbles` 和 `openclaw pairing approve bluebubbles <code>`。
- 回应需要 BlueBubbles private API`POST /api/v1/message/react`);确保服务器版本支持它。
- 编辑/撤回需要 macOS 13+ 和兼容的 BlueBubbles 服务器版本。在 macOS 26Tahoe由于 private API 变更,编辑功能目前不可用。
- 在 macOS 26Tahoe上群组图标更新可能不稳定API 可能返回成功但新图标未同步。
- OpenClaw 会根据 BlueBubbles 服务器的 macOS 版本自动隐藏已知不可用的操作。如果在 macOS 26Tahoe上编辑仍然显示请使用 `channels.bluebubbles.actions.edit=false` 手动禁用。
- 查看状态/健康信息:`openclaw status --all` 或 `openclaw status --deep`。
有关通用渠道工作流参考,请参阅[渠道](/channels)和[插件](/plugins)指南。

View File

@@ -1,468 +0,0 @@
---
read_when:
- 开发 Discord 渠道功能时
summary: Discord 机器人支持状态、功能和配置
title: Discord
x-i18n:
generated_at: "2026-02-03T07:45:45Z"
model: claude-opus-4-5
provider: pi
source_hash: 2f0083b55648f9158668b80d078353421e7dc310135fdc43f2d280b242bf8459
source_path: channels/discord.md
workflow: 15
---
# DiscordBot API
状态:已支持通过官方 Discord 机器人网关进行私信和服务器文字频道通信。
## 快速设置(新手)
1. 创建 Discord 机器人并复制机器人令牌。
2. 在 Discord 应用设置中启用 **Message Content Intent**(如果你计划使用允许列表或名称查找,还需启用 **Server Members Intent**)。
3. 为 OpenClaw 设置令牌:
- 环境变量:`DISCORD_BOT_TOKEN=...`
- 或配置:`channels.discord.token: "..."`
- 如果两者都设置,配置优先(环境变量回退仅适用于默认账户)。
4. 使用消息权限邀请机器人到你的服务器(如果你只想使用私信,可以创建一个私人服务器)。
5. 启动 Gateway 网关。
6. 私信访问默认采用配对模式;首次联系时需批准配对码。
最小配置:
```json5
{
channels: {
discord: {
enabled: true,
token: "YOUR_BOT_TOKEN",
},
},
}
```
## 目标
- 通过 Discord 私信或服务器频道与 OpenClaw 对话。
- 直接聊天会合并到智能体的主会话(默认 `agent:main:main`);服务器频道保持隔离为 `agent:<agentId>:discord:channel:<channelId>`(显示名称使用 `discord:<guildSlug>#<channelSlug>`)。
- 群组私信默认被忽略;通过 `channels.discord.dm.groupEnabled` 启用,并可选择通过 `channels.discord.dm.groupChannels` 进行限制。
- 保持路由确定性:回复始终返回到消息来源的渠道。
## 工作原理
1. 创建 Discord 应用程序 → Bot启用你需要的意图私信 + 服务器消息 + 消息内容),并获取机器人令牌。
2. 使用所需权限邀请机器人到你的服务器,以便在你想使用的地方读取/发送消息。
3. 使用 `channels.discord.token` 配置 OpenClaw或使用 `DISCORD_BOT_TOKEN` 作为回退)。
4. 运行 Gateway 网关;当令牌可用(配置优先,环境变量回退)且 `channels.discord.enabled` 不为 `false` 时,它会自动启动 Discord 渠道。
- 如果你更喜欢使用环境变量,设置 `DISCORD_BOT_TOKEN`(配置块是可选的)。
5. 直接聊天:发送时使用 `user:<id>`(或 `<@id>` 提及);所有对话都进入共享的 `main` 会话。纯数字 ID 是模糊的,会被拒绝。
6. 服务器频道:发送时使用 `channel:<channelId>`。默认需要提及,可以按服务器或按频道设置。
7. 直接聊天:默认通过 `channels.discord.dm.policy` 进行安全保护(默认:`"pairing"`。未知发送者会收到配对码1 小时后过期);通过 `openclaw pairing approve discord <code>` 批准。
- 要保持旧的"对任何人开放"行为:设置 `channels.discord.dm.policy="open"``channels.discord.dm.allowFrom=["*"]`
- 要使用硬编码允许列表:设置 `channels.discord.dm.policy="allowlist"` 并在 `channels.discord.dm.allowFrom` 中列出发送者。
- 要忽略所有私信:设置 `channels.discord.dm.enabled=false``channels.discord.dm.policy="disabled"`
8. 群组私信默认被忽略;通过 `channels.discord.dm.groupEnabled` 启用,并可选择通过 `channels.discord.dm.groupChannels` 进行限制。
9. 可选服务器规则:设置 `channels.discord.guilds`,以服务器 ID首选或 slug 为键,并包含每个频道的规则。
10. 可选原生命令:`commands.native` 默认为 `"auto"`Discord/Telegram 开启Slack 关闭)。使用 `channels.discord.commands.native: true|false|"auto"` 覆盖;`false` 会清除之前注册的命令。文本命令由 `commands.text` 控制,必须作为独立的 `/...` 消息发送。使用 `commands.useAccessGroups: false` 可跳过命令的访问组检查。
- 完整命令列表 + 配置:[斜杠命令](/tools/slash-commands)
11. 可选服务器上下文历史:设置 `channels.discord.historyLimit`(默认 20回退到 `messages.groupChat.historyLimit`)以在回复提及时包含最近 N 条服务器消息作为上下文。设置 `0` 禁用。
12. 表情反应:智能体可以通过 `discord` 工具触发表情反应(受 `channels.discord.actions.*` 控制)。
- 表情反应移除语义:参见 [/tools/reactions](/tools/reactions)。
- `discord` 工具仅在当前渠道是 Discord 时暴露。
13. 原生命令使用隔离的会话键(`agent:<agentId>:discord:slash:<userId>`)而不是共享的 `main` 会话。
注意:名称 → ID 解析使用服务器成员搜索,需要 Server Members Intent如果机器人无法搜索成员请使用 ID 或 `<@id>` 提及。
注意Slug 为小写,空格替换为 `-`。频道名称的 slug 不包含前导 `#`
注意:服务器上下文 `[from:]` 行包含 `author.tag` + `id`,便于进行可提及的回复。
## 配置写入
默认情况下,允许 Discord 写入由 `/config set|unset` 触发的配置更新(需要 `commands.config: true`)。
禁用方式:
```json5
{
channels: { discord: { configWrites: false } },
}
```
## 如何创建自己的机器人
这是在服务器guild频道`#help`)中运行 OpenClaw 的"Discord 开发者门户"设置。
### 1创建 Discord 应用 + 机器人用户
1. Discord 开发者门户 → **Applications****New Application**
2. 在你的应用中:
- **Bot** → **Add Bot**
- 复制 **Bot Token**(这是你放入 `DISCORD_BOT_TOKEN` 的内容)
### 2启用 OpenClaw 需要的网关意图
Discord 会阻止"特权意图",除非你明确启用它们。
**Bot****Privileged Gateway Intents** 中启用:
- **Message Content Intent**(在大多数服务器中读取消息文本所必需;没有它你会看到"Used disallowed intents"或机器人会连接但不响应消息)
- **Server Members Intent**(推荐;服务器中的某些成员/用户查找和允许列表匹配需要)
你通常**不需要** **Presence Intent**
### 3生成邀请 URLOAuth2 URL Generator
在你的应用中:**OAuth2** → **URL Generator**
**Scopes**
-`bot`
-`applications.commands`(原生命令所需)
**Bot Permissions**(最小基线)
- ✅ View Channels
- ✅ Send Messages
- ✅ Read Message History
- ✅ Embed Links
- ✅ Attach Files
- ✅ Add Reactions可选但推荐
- ✅ Use External Emojis / Stickers可选仅当你需要时
除非你在调试并完全信任机器人,否则避免使用 **Administrator**
复制生成的 URL打开它选择你的服务器然后安装机器人。
### 4获取 ID服务器/用户/频道)
Discord 到处使用数字 IDOpenClaw 配置优先使用 ID。
1. Discord桌面/网页)→ **用户设置****高级** → 启用 **开发者模式**
2. 右键点击:
- 服务器名称 → **复制服务器 ID**(服务器 ID
- 频道(例如 `#help`)→ **复制频道 ID**
- 你的用户 → **复制用户 ID**
### 5配置 OpenClaw
#### 令牌
通过环境变量设置机器人令牌(服务器上推荐):
- `DISCORD_BOT_TOKEN=...`
或通过配置:
```json5
{
channels: {
discord: {
enabled: true,
token: "YOUR_BOT_TOKEN",
},
},
}
```
多账户支持:使用 `channels.discord.accounts`,每个账户有自己的令牌和可选的 `name`。参见 [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) 了解通用模式。
#### 允许列表 + 频道路由
示例"单服务器,只允许我,只允许 #help"
```json5
{
channels: {
discord: {
enabled: true,
dm: { enabled: false },
guilds: {
YOUR_GUILD_ID: {
users: ["YOUR_USER_ID"],
requireMention: true,
channels: {
help: { allow: true, requireMention: true },
},
},
},
retry: {
attempts: 3,
minDelayMs: 500,
maxDelayMs: 30000,
jitter: 0.1,
},
},
},
}
```
注意:
- `requireMention: true` 意味着机器人只在被提及时回复(推荐用于共享频道)。
- `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)对于服务器消息也算作提及。
- 多智能体覆盖:在 `agents.list[].groupChat.mentionPatterns` 上设置每个智能体的模式。
- 如果存在 `channels`,任何未列出的频道默认被拒绝。
- 使用 `"*"` 频道条目在所有频道应用默认值;显式频道条目覆盖通配符。
- 话题继承父频道配置(允许列表、`requireMention`、Skills、提示词等除非你显式添加话题频道 ID。
- 机器人发送的消息默认被忽略;设置 `channels.discord.allowBots=true` 允许它们(自己的消息仍被过滤)。
- 警告:如果你允许回复其他机器人(`channels.discord.allowBots=true`),请使用 `requireMention``channels.discord.guilds.*.channels.<id>.users` 允许列表和/或在 `AGENTS.md``SOUL.md` 中设置明确的防护措施来防止机器人之间的回复循环。
### 6验证是否工作
1. 启动 Gateway 网关。
2. 在你的服务器频道中发送:`@Krill hello`(或你的机器人名称)。
3. 如果没有反应:查看下面的**故障排除**。
### 故障排除
- 首先:运行 `openclaw doctor``openclaw channels status --probe`(可操作的警告 + 快速审计)。
- **"Used disallowed intents"**:在开发者门户中启用 **Message Content Intent**(可能还需要 **Server Members Intent**),然后重启 Gateway 网关。
- **机器人连接但从不在服务器频道回复**
- 缺少 **Message Content Intent**,或
- 机器人缺少频道权限View/Send/Read History
- 你的配置需要提及但你没有提及它,或
- 你的服务器/频道允许列表拒绝了该频道/用户。
- **`requireMention: false` 但仍然没有回复**
- `channels.discord.groupPolicy` 默认为 **allowlist**;将其设置为 `"open"` 或在 `channels.discord.guilds` 下添加服务器条目(可选择在 `channels.discord.guilds.<id>.channels` 下列出频道以进行限制)。
- 如果你只设置了 `DISCORD_BOT_TOKEN` 而从未创建 `channels.discord` 部分,运行时会将 `groupPolicy` 默认为 `open`。添加 `channels.discord.groupPolicy``channels.defaults.groupPolicy` 或服务器/频道允许列表来锁定它。
- `requireMention` 必须位于 `channels.discord.guilds`(或特定频道)下。顶层的 `channels.discord.requireMention` 会被忽略。
- **权限审计**`channels status --probe`)只检查数字频道 ID。如果你使用 slug/名称作为 `channels.discord.guilds.*.channels` 键,审计无法验证权限。
- **私信不工作**`channels.discord.dm.enabled=false``channels.discord.dm.policy="disabled"`,或者你尚未被批准(`channels.discord.dm.policy="pairing"`)。
- **Discord 中的执行审批**Discord 支持私信中执行审批的**按钮 UI**(允许一次 / 始终允许 / 拒绝)。`/approve <id> ...` 仅用于转发的审批,不会解析 Discord 的按钮提示。如果你看到 `❌ Failed to submit approval: Error: unknown approval id` 或 UI 从未出现,请检查:
- 你的配置中有 `channels.discord.execApprovals.enabled: true`
- 你的 Discord 用户 ID 在 `channels.discord.execApprovals.approvers` 中列出UI 仅发送给审批者)。
- 使用私信提示中的按钮(**Allow once**、**Always allow**、**Deny**)。
- 参见[执行审批](/tools/exec-approvals)和[斜杠命令](/tools/slash-commands)了解更广泛的审批和命令流程。
## 功能和限制
- 支持私信和服务器文字频道(话题被视为独立频道;不支持语音)。
- 打字指示器尽力发送;消息分块使用 `channels.discord.textChunkLimit`(默认 2000并按行数分割长回复`channels.discord.maxLinesPerMessage`,默认 17
- 可选换行分块:设置 `channels.discord.chunkMode="newline"` 以在空行(段落边界)处分割,然后再进行长度分块。
- 支持文件上传,最大 `channels.discord.mediaMaxMb`(默认 8 MB
- 默认服务器回复需要提及,以避免嘈杂的机器人。
- 当消息引用另一条消息时,会注入回复上下文(引用内容 + ID
- 原生回复线程**默认关闭**;使用 `channels.discord.replyToMode` 和回复标签启用。
## 重试策略
出站 Discord API 调用在速率限制429时使用 Discord `retry_after`(如果可用)进行重试,采用指数退避和抖动。通过 `channels.discord.retry` 配置。参见[重试策略](/concepts/retry)。
## 配置
```json5
{
channels: {
discord: {
enabled: true,
token: "abc.123",
groupPolicy: "allowlist",
guilds: {
"*": {
channels: {
general: { allow: true },
},
},
},
mediaMaxMb: 8,
actions: {
reactions: true,
stickers: true,
emojiUploads: true,
stickerUploads: true,
polls: true,
permissions: true,
messages: true,
threads: true,
pins: true,
search: true,
memberInfo: true,
roleInfo: true,
roles: false,
channelInfo: true,
channels: true,
voiceStatus: true,
events: true,
moderation: false,
},
replyToMode: "off",
dm: {
enabled: true,
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["123456789012345678", "steipete"],
groupEnabled: false,
groupChannels: ["openclaw-dm"],
},
guilds: {
"*": { requireMention: true },
"123456789012345678": {
slug: "friends-of-openclaw",
requireMention: false,
reactionNotifications: "own",
users: ["987654321098765432", "steipete"],
channels: {
general: { allow: true },
help: {
allow: true,
requireMention: true,
users: ["987654321098765432"],
skills: ["search", "docs"],
systemPrompt: "Keep answers short.",
},
},
},
},
},
},
}
```
确认表情反应通过 `messages.ackReaction` + `messages.ackReactionScope` 全局控制。使用 `messages.removeAckAfterReply` 在机器人回复后清除确认表情反应。
- `dm.enabled`:设置 `false` 忽略所有私信(默认 `true`)。
- `dm.policy`:私信访问控制(推荐 `pairing`)。`"open"` 需要 `dm.allowFrom=["*"]`
- `dm.allowFrom`:私信允许列表(用户 ID 或名称)。用于 `dm.policy="allowlist"``dm.policy="open"` 验证。向导接受用户名,并在机器人可以搜索成员时将其解析为 ID。
- `dm.groupEnabled`:启用群组私信(默认 `false`)。
- `dm.groupChannels`:群组私信频道 ID 或 slug 的可选允许列表。
- `groupPolicy`:控制服务器频道处理(`open|disabled|allowlist``allowlist` 需要频道允许列表。
- `guilds`:按服务器规则,以服务器 ID首选或 slug 为键。
- `guilds."*"`:当没有显式条目时应用的默认每服务器设置。
- `guilds.<id>.slug`:用于显示名称的可选友好 slug。
- `guilds.<id>.users`可选的每服务器用户允许列表ID 或名称)。
- `guilds.<id>.tools`:可选的每服务器工具策略覆盖(`allow`/`deny`/`alsoAllow`),在频道覆盖缺失时使用。
- `guilds.<id>.toolsBySender`:服务器级别的可选每发送者工具策略覆盖(在频道覆盖缺失时应用;支持 `"*"` 通配符)。
- `guilds.<id>.channels.<channel>.allow`:当 `groupPolicy="allowlist"` 时允许/拒绝频道。
- `guilds.<id>.channels.<channel>.requireMention`:频道的提及限制。
- `guilds.<id>.channels.<channel>.tools`:可选的每频道工具策略覆盖(`allow`/`deny`/`alsoAllow`)。
- `guilds.<id>.channels.<channel>.toolsBySender`:频道内的可选每发送者工具策略覆盖(支持 `"*"` 通配符)。
- `guilds.<id>.channels.<channel>.users`:可选的每频道用户允许列表。
- `guilds.<id>.channels.<channel>.skills`Skills 过滤器(省略 = 所有 Skills空 = 无)。
- `guilds.<id>.channels.<channel>.systemPrompt`:频道的额外系统提示词(与频道主题组合)。
- `guilds.<id>.channels.<channel>.enabled`:设置 `false` 禁用频道。
- `guilds.<id>.channels`:频道规则(键为频道 slug 或 ID
- `guilds.<id>.requireMention`:每服务器提及要求(可按频道覆盖)。
- `guilds.<id>.reactionNotifications`:表情反应系统事件模式(`off``own``all``allowlist`)。
- `textChunkLimit`出站文本块大小字符。默认2000。
- `chunkMode``length`(默认)仅在超过 `textChunkLimit` 时分割;`newline` 在空行(段落边界)处分割,然后再进行长度分块。
- `maxLinesPerMessage`每条消息的软最大行数。默认17。
- `mediaMaxMb`:限制保存到磁盘的入站媒体大小。
- `historyLimit`:回复提及时作为上下文包含的最近服务器消息数量(默认 20回退到 `messages.groupChat.historyLimit``0` 禁用)。
- `dmHistoryLimit`:私信历史限制(用户轮次)。每用户覆盖:`dms["<user_id>"].historyLimit`
- `retry`:出站 Discord API 调用的重试策略attempts、minDelayMs、maxDelayMs、jitter
- `pluralkit`:解析 PluralKit 代理消息,使系统成员显示为不同的发送者。
- `actions`:每操作工具门控;省略允许所有(设置 `false` 禁用)。
- `reactions`(涵盖表情反应 + 读取表情反应)
- `stickers``emojiUploads``stickerUploads``polls``permissions``messages``threads``pins``search`
- `memberInfo``roleInfo``channelInfo``voiceStatus``events`
- `channels`(创建/编辑/删除频道 + 类别 + 权限)
- `roles`(角色添加/移除,默认 `false`
- `moderation`(超时/踢出/封禁,默认 `false`
- `execApprovals`Discord 专用执行审批私信(按钮 UI。支持 `enabled``approvers``agentFilter``sessionFilter`
表情反应通知使用 `guilds.<id>.reactionNotifications`
- `off`:无表情反应事件。
- `own`:机器人自己消息上的表情反应(默认)。
- `all`:所有消息上的所有表情反应。
- `allowlist`:来自 `guilds.<id>.users` 的用户在所有消息上的表情反应(空列表禁用)。
### PluralKitPK支持
启用 PK 查找,以便代理消息解析到底层系统 + 成员。启用后OpenClaw 使用成员身份进行允许列表匹配,并将发送者标记为 `Member (PK:System)` 以避免意外的 Discord 提及。
```json5
{
channels: {
discord: {
pluralkit: {
enabled: true,
token: "pk_live_...", // 可选;私有系统需要
},
},
},
}
```
允许列表注意事项(启用 PK 时):
-`dm.allowFrom``guilds.<id>.users` 或每频道 `users` 中使用 `pk:<memberId>`
- 成员显示名称也按名称/slug 匹配。
- 查找使用**原始** Discord 消息 ID代理前的消息因此 PK API 只在其 30 分钟窗口内解析它。
- 如果 PK 查找失败(例如,没有令牌的私有系统),代理消息会被视为机器人消息并被丢弃,除非 `channels.discord.allowBots=true`
### 工具操作默认值
| 操作组 | 默认 | 说明 |
| -------------- | ---- | ----------------------------------- |
| reactions | 启用 | 表情反应 + 列出表情反应 + emojiList |
| stickers | 启用 | 发送贴纸 |
| emojiUploads | 启用 | 上传表情 |
| stickerUploads | 启用 | 上传贴纸 |
| polls | 启用 | 创建投票 |
| permissions | 启用 | 频道权限快照 |
| messages | 启用 | 读取/发送/编辑/删除 |
| threads | 启用 | 创建/列出/回复 |
| pins | 启用 | 置顶/取消置顶/列出 |
| search | 启用 | 消息搜索(预览功能) |
| memberInfo | 启用 | 成员信息 |
| roleInfo | 启用 | 角色列表 |
| channelInfo | 启用 | 频道信息 + 列表 |
| channels | 启用 | 频道/类别管理 |
| voiceStatus | 启用 | 语音状态查询 |
| events | 启用 | 列出/创建预定事件 |
| roles | 禁用 | 角色添加/移除 |
| moderation | 禁用 | 超时/踢出/封禁 |
- `replyToMode``off`(默认)、`first``all`。仅在模型包含回复标签时适用。
## 回复标签
要请求线程回复,模型可以在其输出中包含一个标签:
- `[[reply_to_current]]` — 回复触发的 Discord 消息。
- `[[reply_to:<id>]]` — 回复上下文/历史中的特定消息 ID。当前消息 ID 作为 `[message_id: …]` 附加到提示词;历史条目已包含 ID。
行为由 `channels.discord.replyToMode` 控制:
- `off`:忽略标签。
- `first`:只有第一个出站块/附件是回复。
- `all`:每个出站块/附件都是回复。
允许列表匹配注意事项:
- `allowFrom`/`users`/`groupChannels` 接受 ID、名称、标签或像 `<@id>` 这样的提及。
- 支持 `discord:`/`user:`(用户)和 `channel:`(群组私信)等前缀。
- 使用 `*` 允许任何发送者/频道。
- 当存在 `guilds.<id>.channels` 时,未列出的频道默认被拒绝。
- 当省略 `guilds.<id>.channels` 时,允许列表中服务器的所有频道都被允许。
- 要**不允许任何频道**,设置 `channels.discord.groupPolicy: "disabled"`(或保持空允许列表)。
- 配置向导接受 `Guild/Channel` 名称(公开 + 私有)并在可能时将其解析为 ID。
- 启动时OpenClaw 将允许列表中的频道/用户名称解析为 ID当机器人可以搜索成员时并记录映射未解析的条目保持原样。
原生命令注意事项:
- 注册的命令镜像 OpenClaw 的聊天命令。
- 原生命令遵循与私信/服务器消息相同的允许列表(`channels.discord.dm.allowFrom``channels.discord.guilds`、每频道规则)。
- 斜杠命令可能在 Discord UI 中对未在允许列表中的用户仍然可见OpenClaw 在执行时强制执行允许列表并回复"未授权"。
## 工具操作
智能体可以使用以下操作调用 `discord`
- `react` / `reactions`(添加或列出表情反应)
- `sticker``poll``permissions`
- `readMessages``sendMessage``editMessage``deleteMessage`
- 读取/搜索/置顶工具负载包含规范化的 `timestampMs`UTC 纪元毫秒)和 `timestampUtc` 以及原始 Discord `timestamp`
- `threadCreate``threadList``threadReply`
- `pinMessage``unpinMessage``listPins`
- `searchMessages``memberInfo``roleInfo``roleAdd``roleRemove``emojiList`
- `channelInfo``channelList``voiceStatus``eventList``eventCreate`
- `timeout``kick``ban`
Discord 消息 ID 在注入的上下文中显示(`[discord message id: …]` 和历史行),以便智能体可以定位它们。
表情可以是 unicode例如 `✅`)或自定义表情语法如 `<:party_blob:1234567890>`
## 安全与运维
- 像对待密码一样对待机器人令牌;在受监督的主机上优先使用 `DISCORD_BOT_TOKEN` 环境变量,或锁定配置文件权限。
- 只授予机器人所需的权限(通常是读取/发送消息)。
- 如果机器人卡住或受到速率限制,在确认没有其他进程拥有 Discord 会话后重启 Gateway 网关(`openclaw gateway --force`)。

View File

@@ -1,513 +0,0 @@
---
summary: "飞书机器人支持状态、功能和配置"
read_when:
- 您想要连接飞书机器人
- 您正在配置飞书渠道
title: 飞书
---
# 飞书机器人
状态:生产就绪,支持机器人私聊和群组。使用 WebSocket 长连接模式接收消息。
---
## 需要插件
安装 Feishu 插件:
```bash
openclaw plugins install @openclaw/feishu
```
本地 checkout在 git 仓库内运行):
```bash
openclaw plugins install ./extensions/feishu
```
---
## 快速开始
添加飞书渠道有两种方式:
### 方式一:通过安装向导添加(推荐)
如果您刚安装完 OpenClaw可以直接运行向导根据提示添加飞书
```bash
openclaw onboard
```
向导会引导您完成:
1. 创建飞书应用并获取凭证
2. 配置应用凭证
3. 启动网关
**完成配置后**,您可以使用以下命令检查网关状态:
- `openclaw gateway status` - 查看网关运行状态
- `openclaw logs --follow` - 查看实时日志
### 方式二:通过命令行添加
如果您已经完成了初始安装,可以用以下命令添加飞书渠道:
```bash
openclaw channels add
```
然后根据交互式提示选择 Feishu输入 App ID 和 App Secret 即可。
**完成配置后**,您可以使用以下命令管理网关:
- `openclaw gateway status` - 查看网关运行状态
- `openclaw gateway restart` - 重启网关以应用新配置
- `openclaw logs --follow` - 查看实时日志
---
## 第一步:创建飞书应用
### 1. 打开飞书开放平台
访问 [飞书开放平台](https://open.feishu.cn/app),使用飞书账号登录。
Lark国际版请使用 https://open.larksuite.com/app并在配置中设置 `domain: "lark"`
### 2. 创建应用
1. 点击 **创建企业自建应用**
2. 填写应用名称和描述
3. 选择应用图标
![创建企业自建应用](/images/feishu-step2-create-app.png)
### 3. 获取应用凭证
在应用的 **凭证与基础信息** 页面,复制:
- **App ID**(格式如 `cli_xxx`
- **App Secret**
**重要**:请妥善保管 App Secret不要分享给他人。
![获取应用凭证](/images/feishu-step3-credentials.png)
### 4. 配置应用权限
**权限管理** 页面,点击 **批量导入** 按钮,粘贴以下 JSON 配置一键导入所需权限:
```json
{
"scopes": {
"tenant": [
"aily:file:read",
"aily:file:write",
"application:application.app_message_stats.overview:readonly",
"application:application:self_manage",
"application:bot.menu:write",
"contact:user.employee_id:readonly",
"corehr:file:download",
"event:ip_list",
"im:chat.access_event.bot_p2p_chat:read",
"im:chat.members:bot_access",
"im:message",
"im:message.group_at_msg:readonly",
"im:message.p2p_msg:readonly",
"im:message:readonly",
"im:message:send_as_bot",
"im:resource"
],
"user": ["aily:file:read", "aily:file:write", "im:chat.access_event.bot_p2p_chat:read"]
}
}
```
![配置应用权限](/images/feishu-step4-permissions.png)
### 5. 启用机器人能力
**应用能力** > **机器人** 页面:
1. 开启机器人能力
2. 配置机器人名称
![启用机器人能力](/images/feishu-step5-bot-capability.png)
### 6. 配置事件订阅
⚠️ **重要提醒**:在配置事件订阅前,请务必确保已完成以下步骤:
1. 运行 `openclaw channels add` 添加了 Feishu 渠道
2. 网关处于启动状态(可通过 `openclaw gateway status` 检查状态)
**事件订阅** 页面:
1. 选择 **使用长连接接收事件**WebSocket 模式)
2. 添加事件:`im.message.receive_v1`(接收消息)
⚠️ **注意**:如果网关未启动或渠道未添加,长连接设置将保存失败。
![配置事件订阅](/images/feishu-step6-event-subscription.png)
### 7. 发布应用
1.**版本管理与发布** 页面创建版本
2. 提交审核并发布
3. 等待管理员审批(企业自建应用通常自动通过)
---
## 第二步:配置 OpenClaw
### 通过向导配置(推荐)
运行以下命令,根据提示粘贴 App ID 和 App Secret
```bash
openclaw channels add
```
选择 **Feishu**,然后输入您在第一步获取的凭证即可。
### 通过配置文件配置
编辑 `~/.openclaw/openclaw.json`
```json5
{
channels: {
feishu: {
enabled: true,
dmPolicy: "pairing",
accounts: {
main: {
appId: "cli_xxx",
appSecret: "xxx",
botName: "我的AI助手",
},
},
},
},
}
```
### 通过环境变量配置
```bash
export FEISHU_APP_ID="cli_xxx"
export FEISHU_APP_SECRET="xxx"
```
### Lark国际版域名
如果您的租户在 Lark国际版请设置域名为 `lark`(或完整域名),可配置 `channels.feishu.domain``channels.feishu.accounts.<id>.domain`
```json5
{
channels: {
feishu: {
domain: "lark",
accounts: {
main: {
appId: "cli_xxx",
appSecret: "xxx",
},
},
},
},
}
```
---
## 第三步:启动并测试
### 1. 启动网关
```bash
openclaw gateway
```
### 2. 发送测试消息
在飞书中找到您创建的机器人,发送一条消息。
### 3. 配对授权
默认情况下,机器人会回复一个 **配对码**。您需要批准此代码:
```bash
openclaw pairing approve feishu <配对码>
```
批准后即可正常对话。
---
## 介绍
- **飞书机器人渠道**:由网关管理的飞书机器人
- **确定性路由**:回复始终返回飞书,模型不会选择渠道
- **会话隔离**:私聊共享主会话;群组独立隔离
- **WebSocket 连接**:使用飞书 SDK 的长连接模式,无需公网 URL
---
## 访问控制
### 私聊访问
- **默认**`dmPolicy: "pairing"`,陌生用户会收到配对码
- **批准配对**
```bash
openclaw pairing list feishu # 查看待审批列表
openclaw pairing approve feishu <CODE> # 批准
```
- **白名单模式**:通过 `channels.feishu.allowFrom` 配置允许的用户 Open ID
### 群组访问
**1. 群组策略**`channels.feishu.groupPolicy`
- `"open"` = 允许群组中所有人(默认)
- `"allowlist"` = 仅允许 `groupAllowFrom` 中的用户
- `"disabled"` = 禁用群组消息
**2. @提及要求**`channels.feishu.groups.<chat_id>.requireMention`
- `true` = 需要 @机器人才响应(默认)
- `false` = 无需 @也响应
---
## 群组配置示例
### 允许所有群组,需要 @提及(默认行为)
```json5
{
channels: {
feishu: {
groupPolicy: "open",
// 默认 requireMention: true
},
},
}
```
### 允许所有群组,无需 @提及
需要为特定群组配置:
```json5
{
channels: {
feishu: {
groups: {
oc_xxx: { requireMention: false },
},
},
},
}
```
### 仅允许特定用户在群组中使用
```json5
{
channels: {
feishu: {
groupPolicy: "allowlist",
groupAllowFrom: ["ou_xxx", "ou_yyy"],
},
},
}
```
---
## 获取群组/用户 ID
### 获取群组 IDchat_id
群组 ID 格式为 `oc_xxx`,可以通过以下方式获取:
**方法一**(推荐):
1. 启动网关并在群组中 @机器人发消息
2. 运行 `openclaw logs --follow` 查看日志中的 `chat_id`
**方法二**
使用飞书 API 调试工具获取机器人所在群组列表。
### 获取用户 IDopen_id
用户 ID 格式为 `ou_xxx`,可以通过以下方式获取:
**方法一**(推荐):
1. 启动网关并给机器人发消息
2. 运行 `openclaw logs --follow` 查看日志中的 `open_id`
**方法二**
查看配对请求列表,其中包含用户的 Open ID
```bash
openclaw pairing list feishu
```
---
## 常用命令
| 命令 | 说明 |
| --------- | -------------- |
| `/status` | 查看机器人状态 |
| `/reset` | 重置对话会话 |
| `/model` | 查看/切换模型 |
> 注意:飞书目前不支持原生命令菜单,命令需要以文本形式发送。
## 网关管理命令
在配置和使用飞书渠道时,您可能需要使用以下网关管理命令:
| 命令 | 说明 |
| -------------------------- | ----------------- |
| `openclaw gateway status` | 查看网关运行状态 |
| `openclaw gateway install` | 安装/启动网关服务 |
| `openclaw gateway stop` | 停止网关服务 |
| `openclaw gateway restart` | 重启网关服务 |
| `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`
### App Secret 泄露怎么办
1. 在飞书开放平台重置 App Secret
2. 更新配置文件中的 App Secret
3. 重启网关
### 发送消息失败
1. 检查应用是否有 `im:message:send_as_bot` 权限
2. 检查应用是否已发布
3. 查看日志获取详细错误信息
---
## 高级配置
### 多账号配置
如果需要管理多个飞书机器人:
```json5
{
channels: {
feishu: {
accounts: {
main: {
appId: "cli_xxx",
appSecret: "xxx",
botName: "主机器人",
},
backup: {
appId: "cli_yyy",
appSecret: "yyy",
botName: "备用机器人",
enabled: false, // 暂时禁用
},
},
},
},
}
```
### 消息限制
- `textChunkLimit`:出站文本分块大小(默认 2000 字符)
- `mediaMaxMb`:媒体上传/下载限制(默认 30MB
### 流式输出
飞书目前不支持消息编辑,因此默认禁用流式输出(`blockStreaming: true`)。机器人会等待完整回复后一次性发送。
---
## 配置参考
完整配置请参考:[网关配置](/gateway/configuration)
主要选项:
| 配置项 | 说明 | 默认值 |
| ------------------------------------------------- | ------------------------------ | --------- |
| `channels.feishu.enabled` | 启用/禁用渠道 | `true` |
| `channels.feishu.domain` | API 域名(`feishu` 或 `lark` | `feishu` |
| `channels.feishu.accounts.<id>.appId` | 应用 App ID | - |
| `channels.feishu.accounts.<id>.appSecret` | 应用 App Secret | - |
| `channels.feishu.accounts.<id>.domain` | 单账号 API 域名覆盖 | `feishu` |
| `channels.feishu.dmPolicy` | 私聊策略 | `pairing` |
| `channels.feishu.allowFrom` | 私聊白名单open_id 列表) | - |
| `channels.feishu.groupPolicy` | 群组策略 | `open` |
| `channels.feishu.groupAllowFrom` | 群组白名单 | - |
| `channels.feishu.groups.<chat_id>.requireMention` | 是否需要 @提及 | `true` |
| `channels.feishu.groups.<chat_id>.enabled` | 是否启用该群组 | `true` |
| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` |
| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` |
| `channels.feishu.blockStreaming` | 禁用流式输出 | `true` |
---
## dmPolicy 策略说明
| 值 | 行为 |
| ------------- | -------------------------------------------------- |
| `"pairing"` | **默认**。未知用户收到配对码,管理员批准后才能对话 |
| `"allowlist"` | 仅 `allowFrom` 列表中的用户可对话,其他静默忽略 |
| `"open"` | 允许所有人对话(需在 allowFrom 中加 `"*"` |
| `"disabled"` | 完全禁止私聊 |
---
## 支持的消息类型
### 接收
- ✅ 文本消息
- ✅ 图片
- ✅ 文件
- ✅ 音频
- ✅ 视频
- ✅ 表情包
### 发送
- ✅ 文本消息
- ✅ 图片
- ✅ 文件
- ✅ 音频
- ⚠️ 富文本(部分支持)

View File

@@ -1,257 +0,0 @@
---
read_when:
- 开发 Google Chat 渠道功能时
summary: Google Chat 应用支持状态、功能和配置
title: Google Chat
x-i18n:
generated_at: "2026-02-03T07:43:39Z"
model: claude-opus-4-5
provider: pi
source_hash: 3b2bb116cdd12614c3d5afddd0879e9deb05c3606e3a2385cbc07f23552b357e
source_path: channels/googlechat.md
workflow: 15
---
# Google ChatChat API
状态:已支持通过 Google Chat API webhooks仅 HTTP使用私信和空间。
## 快速设置(新手)
1. 创建一个 Google Cloud 项目并启用 **Google Chat API**
- 前往:[Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials)
- 如果 API 尚未启用,请启用它。
2. 创建一个**服务账号**
- 点击 **Create Credentials** > **Service Account**
- 随意命名(例如 `openclaw-chat`)。
- 权限留空(点击 **Continue**)。
- 有访问权限的主账号留空(点击 **Done**)。
3. 创建并下载 **JSON 密钥**
- 在服务账号列表中,点击刚刚创建的账号。
- 前往 **Keys** 标签页。
- 点击 **Add Key** > **Create new key**
- 选择 **JSON** 并点击 **Create**
4. 将下载的 JSON 文件存储在 Gateway 网关主机上(例如 `~/.openclaw/googlechat-service-account.json`)。
5. 在 [Google Cloud Console Chat Configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat) 中创建一个 Google Chat 应用:
- 填写 **Application info**
- **App name**:(例如 `OpenClaw`
- **Avatar URL**:(例如 `https://openclaw.ai/logo.png`
- **Description**:(例如 `Personal AI Assistant`
- 启用 **Interactive features**
-**Functionality** 下,勾选 **Join spaces and group conversations**
-**Connection settings** 下,选择 **HTTP endpoint URL**
-**Triggers** 下,选择 **Use a common HTTP endpoint URL for all triggers** 并将其设置为你的 Gateway 网关公网 URL 后加 `/googlechat`
- _提示运行 `openclaw status` 查看你的 Gateway 网关公网 URL。_
-**Visibility** 下,勾选 **Make this Chat app available to specific people and groups in &lt;Your Domain&gt;**
- 在文本框中输入你的邮箱地址(例如 `user@example.com`)。
- 点击底部的 **Save**
6. **启用应用状态**
- 保存后,**刷新页面**。
- 找到 **App status** 部分(通常在保存后位于顶部或底部附近)。
- 将状态更改为 **Live - available to users**
- 再次点击 **Save**
7. 使用服务账号路径和 webhook audience 配置 OpenClaw
- 环境变量:`GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json`
- 或配置:`channels.googlechat.serviceAccountFile: "/path/to/service-account.json"`
8. 设置 webhook audience 类型和值(与你的 Chat 应用配置匹配)。
9. 启动 Gateway 网关。Google Chat 将向你的 webhook 路径发送 POST 请求。
## 添加到 Google Chat
Gateway 网关运行后,且你的邮箱已添加到可见性列表中:
1. 前往 [Google Chat](https://chat.google.com/)。
2. 点击 **Direct Messages** 旁边的 **+**(加号)图标。
3. 在搜索栏(通常用于添加联系人的位置)中,输入你在 Google Cloud Console 中配置的 **App name**
- **注意**:该机器人*不会*出现在"Marketplace"浏览列表中,因为它是私有应用。你必须按名称搜索。
4. 从结果中选择你的机器人。
5. 点击 **Add****Chat** 开始一对一对话。
6. 发送"Hello"来触发助手!
## 公网 URL仅 Webhook
Google Chat webhooks 需要一个公网 HTTPS 端点。为了安全起见,**只将 `/googlechat` 路径暴露到互联网**。将 OpenClaw 仪表板和其他敏感端点保留在你的私有网络上。
### 方案 ATailscale Funnel推荐
使用 Tailscale Serve 提供私有仪表板,使用 Funnel 提供公网 webhook 路径。这样可以保持 `/` 私有,同时只暴露 `/googlechat`
1. **检查你的 Gateway 网关绑定的地址:**
```bash
ss -tlnp | grep 18789
```
记下 IP 地址(例如 `127.0.0.1`、`0.0.0.0` 或你的 Tailscale IP 如 `100.x.x.x`)。
2. **仅将仪表板暴露给 tailnet端口 8443**
```bash
# 如果绑定到 localhost127.0.0.1 或 0.0.0.0
tailscale serve --bg --https 8443 http://127.0.0.1:18789
# 如果仅绑定到 Tailscale IP例如 100.106.161.80
tailscale serve --bg --https 8443 http://100.106.161.80:18789
```
3. **仅公开暴露 webhook 路径:**
```bash
# 如果绑定到 localhost127.0.0.1 或 0.0.0.0
tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechat
# 如果仅绑定到 Tailscale IP例如 100.106.161.80
tailscale funnel --bg --set-path /googlechat http://100.106.161.80:18789/googlechat
```
4. **授权节点访问 Funnel**
如果出现提示,请访问输出中显示的授权 URL以在你的 tailnet 策略中为此节点启用 Funnel。
5. **验证配置:**
```bash
tailscale serve status
tailscale funnel status
```
你的公网 webhook URL 将是:
`https://<node-name>.<tailnet>.ts.net/googlechat`
你的私有仪表板仅限 tailnet 访问:
`https://<node-name>.<tailnet>.ts.net:8443/`
在 Google Chat 应用配置中使用公网 URL不带 `:8443`)。
> 注意:此配置在重启后会保留。如需稍后移除,请运行 `tailscale funnel reset` 和 `tailscale serve reset`。
### 方案 B反向代理Caddy
如果你使用像 Caddy 这样的反向代理,只代理特定路径:
```caddy
your-domain.com {
reverse_proxy /googlechat* localhost:18789
}
```
使用此配置,任何发往 `your-domain.com/` 的请求将被忽略或返回 404而 `your-domain.com/googlechat` 会安全地路由到 OpenClaw。
### 方案 CCloudflare Tunnel
配置你的隧道入口规则,只路由 webhook 路径:
- **路径**`/googlechat` -> `http://localhost:18789/googlechat`
- **默认规则**HTTP 404未找到
## 工作原理
1. Google Chat 向 Gateway 网关发送 webhook POST 请求。每个请求都包含一个 `Authorization: Bearer <token>` 头。
2. OpenClaw 根据配置的 `audienceType` + `audience` 验证令牌:
- `audienceType: "app-url"` → audience 是你的 HTTPS webhook URL。
- `audienceType: "project-number"` → audience 是 Cloud 项目编号。
3. 消息按空间路由:
- 私信使用会话键 `agent:<agentId>:googlechat:dm:<spaceId>`。
- 空间使用会话键 `agent:<agentId>:googlechat:group:<spaceId>`。
4. 私信访问默认为配对模式。未知发送者会收到配对码;使用以下命令批准:
- `openclaw pairing approve googlechat <code>`
5. 群组空间默认需要 @提及。如果提及检测需要应用的用户名,请使用 `botUser`。
## 目标标识符
使用这些标识符进行消息投递和允许列表:
- 私信:`users/<userId>` 或 `users/<email>`(接受邮箱地址)。
- 空间:`spaces/<spaceId>`。
## 配置要点
```json5
{
channels: {
googlechat: {
enabled: true,
serviceAccountFile: "/path/to/service-account.json",
audienceType: "app-url",
audience: "https://gateway.example.com/googlechat",
webhookPath: "/googlechat",
botUser: "users/1234567890", // 可选;帮助提及检测
dm: {
policy: "pairing",
allowFrom: ["users/1234567890", "name@example.com"],
},
groupPolicy: "allowlist",
groups: {
"spaces/AAAA": {
allow: true,
requireMention: true,
users: ["users/1234567890"],
systemPrompt: "Short answers only.",
},
},
actions: { reactions: true },
typingIndicator: "message",
mediaMaxMb: 20,
},
},
}
```
注意事项:
- 服务账号凭证也可以通过 `serviceAccount`JSON 字符串)内联传递。
- 如果未设置 `webhookPath`,默认 webhook 路径为 `/googlechat`。
- 当 `actions.reactions` 启用时,可通过 `reactions` 工具和 `channels action` 使用表情回应。
- `typingIndicator` 支持 `none`、`message`(默认)和 `reaction`reaction 需要用户 OAuth
- 附件通过 Chat API 下载并存储在媒体管道中(大小受 `mediaMaxMb` 限制)。
## 故障排除
### 405 Method Not Allowed
如果 Google Cloud Logs Explorer 显示如下错误:
```
status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed
```
这意味着 webhook 处理程序未注册。常见原因:
1. **渠道未配置**:配置中缺少 `channels.googlechat` 部分。使用以下命令验证:
```bash
openclaw config get channels.googlechat
```
如果返回"Config path not found",请添加配置(参见[配置要点](#配置要点))。
2. **插件未启用**:检查插件状态:
```bash
openclaw plugins list | grep googlechat
```
如果显示"disabled",请在配置中添加 `plugins.entries.googlechat.enabled: true`。
3. **Gateway 网关未重启**:添加配置后,重启 Gateway 网关:
```bash
openclaw gateway restart
```
验证渠道是否正在运行:
```bash
openclaw channels status
# 应显示Google Chat default: enabled, configured, ...
```
### 其他问题
- 检查 `openclaw channels status --probe` 以查看认证错误或缺少 audience 配置。
- 如果没有收到消息,请确认 Chat 应用的 webhook URL 和事件订阅。
- 如果提及门控阻止了回复,请将 `botUser` 设置为应用的用户资源名称并验证 `requireMention`。
- 在发送测试消息时使用 `openclaw logs --follow` 查看请求是否到达 Gateway 网关。
相关文档:
- [Gateway 网关配置](/gateway/configuration)
- [安全](/gateway/security)
- [表情回应](/tools/reactions)

View File

@@ -1,38 +0,0 @@
---
read_when:
- 开发 Telegram 或 grammY 相关功能时
summary: 通过 grammY 集成 Telegram Bot API附设置说明
title: grammY
x-i18n:
generated_at: "2026-02-03T10:03:55Z"
model: claude-opus-4-5
provider: pi
source_hash: ea7ef23e6d77801f4ef5fc56685ef4470f79f5aecab448d644a72cbab53521b7
source_path: channels/grammy.md
workflow: 15
---
# grammY 集成Telegram Bot API
# 为什么选择 grammY
- 以 TS 为核心的 Bot API 客户端,内置长轮询 + webhook 辅助工具、中间件、错误处理和速率限制器。
- 媒体处理辅助工具比手动编写 fetch + FormData 更简洁;支持所有 Bot API 方法。
- 可扩展:通过自定义 fetch 支持代理,可选的会话中间件,类型安全的上下文。
# 我们发布的内容
- **单一客户端路径:** 移除了基于 fetch 的实现grammY 现在是唯一的 Telegram 客户端(发送 + Gateway 网关),默认启用 grammY throttler。
- **Gateway 网关:** `monitorTelegramProvider` 构建 grammY `Bot`,接入 mention/allowlist 网关控制,通过 `getFile`/`download` 下载媒体,并使用 `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument` 发送回复。通过 `webhookCallback` 支持长轮询或 webhook。
- **代理:** 可选的 `channels.telegram.proxy` 通过 grammY 的 `client.baseFetch` 使用 `undici.ProxyAgent`
- **Webhook 支持:** `webhook-set.ts` 封装了 `setWebhook/deleteWebhook``webhook.ts` 托管回调,支持健康检查和优雅关闭。当设置了 `channels.telegram.webhookUrl` + `channels.telegram.webhookSecret`Gateway 网关启用 webhook 模式(否则使用长轮询)。
- **会话:** 私聊折叠到智能体主会话(`agent:<agentId>:<mainKey>`);群组使用 `agent:<agentId>:telegram:group:<chatId>`;回复路由回同一渠道。
- **配置选项:** `channels.telegram.botToken``channels.telegram.dmPolicy``channels.telegram.groups`allowlist + mention 默认值)、`channels.telegram.allowFrom``channels.telegram.groupAllowFrom``channels.telegram.groupPolicy``channels.telegram.mediaMaxMb``channels.telegram.linkPreview``channels.telegram.proxy``channels.telegram.webhookSecret``channels.telegram.webhookUrl`
- **草稿流式传输:** 可选的 `channels.telegram.streamMode` 在私有话题聊天中使用 `sendMessageDraft`Bot API 9.3+)。这与渠道分块流式传输是分开的。
- **测试:** grammY mock 覆盖了私信 + 群组 mention 网关控制和出站发送;欢迎添加更多媒体/webhook 测试用例。
待解决问题
- 如果遇到 Bot API 429 错误,考虑使用可选的 grammY 插件throttler
- 添加更多结构化媒体测试(贴纸、语音消息)。
- 使 webhook 监听端口可配置(目前固定为 8787除非通过 Gateway 网关配置)。

View File

@@ -1,302 +0,0 @@
---
read_when:
- 设置 iMessage 支持
- 调试 iMessage 发送/接收
summary: 通过 imsg基于 stdio 的 JSON-RPC实现 iMessage 支持、设置及 chat_id 路由
title: iMessage
x-i18n:
generated_at: "2026-02-03T07:44:18Z"
model: claude-opus-4-5
provider: pi
source_hash: bc19756a42ead80a0845f18c4830c3f1f40948f69b2b016a4026598cfb8fef0d
source_path: channels/imessage.md
workflow: 15
---
# iMessage (imsg)
状态:外部 CLI 集成。Gateway 网关生成 `imsg rpc`(基于 stdio 的 JSON-RPC
## 快速设置(新手)
1. 确保在此 Mac 上已登录"信息"。
2. 安装 `imsg`
- `brew install steipete/tap/imsg`
3. 配置 OpenClaw 的 `channels.imessage.cliPath``channels.imessage.dbPath`
4. 启动 Gateway 网关并批准所有 macOS 提示(自动化 + 完全磁盘访问权限)。
最小配置:
```json5
{
channels: {
imessage: {
enabled: true,
cliPath: "/usr/local/bin/imsg",
dbPath: "/Users/<you>/Library/Messages/chat.db",
},
},
}
```
## 简介
- 基于 macOS 上 `imsg` 的 iMessage 渠道。
- 确定性路由:回复始终返回到 iMessage。
- 私信共享智能体的主会话;群组是隔离的(`agent:<agentId>:imessage:group:<chat_id>`)。
- 如果多参与者会话以 `is_group=false` 到达,你仍可使用 `channels.imessage.groups``chat_id` 隔离(参见下方"类群组会话")。
## 配置写入
默认情况下iMessage 允许写入由 `/config set|unset` 触发的配置更新(需要 `commands.config: true`)。
禁用方式:
```json5
{
channels: { imessage: { configWrites: false } },
}
```
## 要求
- 已登录"信息"的 macOS。
- OpenClaw + `imsg` 的完全磁盘访问权限(访问"信息"数据库)。
- 发送时需要自动化权限。
- `channels.imessage.cliPath` 可以指向任何代理 stdin/stdout 的命令(例如,通过 SSH 连接到另一台 Mac 并运行 `imsg rpc` 的包装脚本)。
## 设置(快速路径)
1. 确保在此 Mac 上已登录"信息"。
2. 配置 iMessage 并启动 Gateway 网关。
### 专用机器人 macOS 用户(用于隔离身份)
如果你希望机器人从**独立的 iMessage 身份**发送(并保持你的个人"信息"整洁),请使用专用 Apple ID + 专用 macOS 用户。
1. 创建专用 Apple ID例如`my-cool-bot@icloud.com`)。
- Apple 可能需要电话号码进行验证 / 2FA。
2. 创建 macOS 用户(例如:`openclawhome`)并登录。
3. 在该 macOS 用户中打开"信息"并使用机器人 Apple ID 登录 iMessage。
4. 启用远程登录(系统设置 → 通用 → 共享 → 远程登录)。
5. 安装 `imsg`
- `brew install steipete/tap/imsg`
6. 设置 SSH 使 `ssh <bot-macos-user>@localhost true` 无需密码即可工作。
7.`channels.imessage.accounts.bot.cliPath` 指向以机器人用户身份运行 `imsg` 的 SSH 包装脚本。
首次运行注意事项:发送/接收可能需要在*机器人 macOS 用户*中进行 GUI 批准(自动化 + 完全磁盘访问权限)。如果 `imsg rpc` 看起来卡住或退出,请登录该用户(屏幕共享很有帮助),运行一次 `imsg chats --limit 1` / `imsg send ...`,批准提示,然后重试。
示例包装脚本(`chmod +x`)。将 `<bot-macos-user>` 替换为你的实际 macOS 用户名:
```bash
#!/usr/bin/env bash
set -euo pipefail
# Run an interactive SSH once first to accept host keys:
# ssh <bot-macos-user>@localhost true
exec /usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=5 -T <bot-macos-user>@localhost \
"/usr/local/bin/imsg" "$@"
```
示例配置:
```json5
{
channels: {
imessage: {
enabled: true,
accounts: {
bot: {
name: "Bot",
enabled: true,
cliPath: "/path/to/imsg-bot",
dbPath: "/Users/<bot-macos-user>/Library/Messages/chat.db",
},
},
},
},
}
```
对于单账户设置,使用扁平选项(`channels.imessage.cliPath``channels.imessage.dbPath`)而不是 `accounts` 映射。
### 远程/SSH 变体(可选)
如果你想在另一台 Mac 上使用 iMessage请将 `channels.imessage.cliPath` 设置为通过 SSH 在远程 macOS 主机上运行 `imsg` 的包装脚本。OpenClaw 只需要 stdio。
示例包装脚本:
```bash
#!/usr/bin/env bash
exec ssh -T gateway-host imsg "$@"
```
**远程附件:**`cliPath` 通过 SSH 指向远程主机时,"信息"数据库中的附件路径引用的是远程机器上的文件。OpenClaw 可以通过设置 `channels.imessage.remoteHost` 自动通过 SCP 获取这些文件:
```json5
{
channels: {
imessage: {
cliPath: "~/imsg-ssh", // SSH wrapper to remote Mac
remoteHost: "user@gateway-host", // for SCP file transfer
includeAttachments: true,
},
},
}
```
如果未设置 `remoteHost`OpenClaw 会尝试通过解析包装脚本中的 SSH 命令自动检测。建议显式配置以提高可靠性。
#### 通过 Tailscale 连接远程 Mac示例
如果 Gateway 网关运行在 Linux 主机/虚拟机上但 iMessage 必须运行在 Mac 上Tailscale 是最简单的桥接方式Gateway 网关通过 tailnet 与 Mac 通信,通过 SSH 运行 `imsg`,并通过 SCP 获取附件。
架构:
```
┌──────────────────────────────┐ SSH (imsg rpc) ┌──────────────────────────┐
│ Gateway host (Linux/VM) │──────────────────────────────────▶│ Mac with Messages + imsg │
│ - openclaw gateway │ SCP (attachments) │ - Messages signed in │
│ - channels.imessage.cliPath │◀──────────────────────────────────│ - Remote Login enabled │
└──────────────────────────────┘ └──────────────────────────┘
│ Tailscale tailnet (hostname or 100.x.y.z)
user@gateway-host
```
具体配置示例Tailscale 主机名):
```json5
{
channels: {
imessage: {
enabled: true,
cliPath: "~/.openclaw/scripts/imsg-ssh",
remoteHost: "bot@mac-mini.tailnet-1234.ts.net",
includeAttachments: true,
dbPath: "/Users/bot/Library/Messages/chat.db",
},
},
}
```
示例包装脚本(`~/.openclaw/scripts/imsg-ssh`
```bash
#!/usr/bin/env bash
exec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@"
```
注意事项:
- 确保 Mac 已登录"信息",并已启用远程登录。
- 使用 SSH 密钥使 `ssh bot@mac-mini.tailnet-1234.ts.net` 无需提示即可工作。
- `remoteHost` 应与 SSH 目标匹配,以便 SCP 可以获取附件。
多账户支持:使用 `channels.imessage.accounts` 配置每个账户及可选的 `name`。参见 [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) 了解共享模式。不要提交 `~/.openclaw/openclaw.json`(它通常包含令牌)。
## 访问控制(私信 + 群组)
私信:
- 默认:`channels.imessage.dmPolicy = "pairing"`
- 未知发送者会收到配对码;消息在批准前会被忽略(配对码在 1 小时后过期)。
- 批准方式:
- `openclaw pairing list imessage`
- `openclaw pairing approve imessage <CODE>`
- 配对是 iMessage 私信的默认令牌交换方式。详情:[配对](/start/pairing)
群组:
- `channels.imessage.groupPolicy = open | allowlist | disabled`
- 设置 `allowlist` 时,`channels.imessage.groupAllowFrom` 控制谁可以在群组中触发。
- 提及检测使用 `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`),因为 iMessage 没有原生提及元数据。
- 多智能体覆盖:在 `agents.list[].groupChat.mentionPatterns` 上设置每个智能体的模式。
## 工作原理(行为)
- `imsg` 流式传输消息事件Gateway 网关将它们规范化为共享渠道信封。
- 回复始终路由回相同的 chat id 或 handle。
## 类群组会话(`is_group=false`
某些 iMessage 会话可能有多个参与者,但根据"信息"存储聊天标识符的方式,仍以 `is_group=false` 到达。
如果你在 `channels.imessage.groups` 下显式配置了 `chat_id`OpenClaw 会将该会话视为"群组"用于:
- 会话隔离(独立的 `agent:<agentId>:imessage:group:<chat_id>` 会话键)
- 群组允许列表 / 提及检测行为
示例:
```json5
{
channels: {
imessage: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15555550123"],
groups: {
"42": { requireMention: false },
},
},
},
}
```
当你想为特定会话使用隔离的个性/模型时这很有用(参见[多智能体路由](/concepts/multi-agent))。关于文件系统隔离,参见[沙箱隔离](/gateway/sandboxing)。
## 媒体 + 限制
- 通过 `channels.imessage.includeAttachments` 可选附件摄取。
- 通过 `channels.imessage.mediaMaxMb` 设置媒体上限。
## 限制
- 出站文本按 `channels.imessage.textChunkLimit` 分块(默认 4000
- 可选换行分块:设置 `channels.imessage.chunkMode="newline"` 在长度分块前按空行(段落边界)分割。
- 媒体上传受 `channels.imessage.mediaMaxMb` 限制(默认 16
## 寻址 / 投递目标
优先使用 `chat_id` 进行稳定路由:
- `chat_id:123`(推荐)
- `chat_guid:...`
- `chat_identifier:...`
- 直接 handle`imessage:+1555` / `sms:+1555` / `user@example.com`
列出聊天:
```
imsg chats --limit 20
```
## 配置参考iMessage
完整配置:[配置](/gateway/configuration)
提供商选项:
- `channels.imessage.enabled`:启用/禁用渠道启动。
- `channels.imessage.cliPath``imsg` 路径。
- `channels.imessage.dbPath`"信息"数据库路径。
- `channels.imessage.remoteHost`:当 `cliPath` 指向远程 Mac 时用于 SCP 附件传输的 SSH 主机(例如 `user@gateway-host`)。如未设置则从 SSH 包装脚本自动检测。
- `channels.imessage.service``imessage | sms | auto`
- `channels.imessage.region`:短信区域。
- `channels.imessage.dmPolicy``pairing | allowlist | open | disabled`默认pairing
- `channels.imessage.allowFrom`私信允许列表handle、邮箱、E.164 号码或 `chat_id:*`)。`open` 需要 `"*"`。iMessage 没有用户名;使用 handle 或聊天目标。
- `channels.imessage.groupPolicy``open | allowlist | disabled`默认allowlist
- `channels.imessage.groupAllowFrom`:群组发送者允许列表。
- `channels.imessage.historyLimit` / `channels.imessage.accounts.*.historyLimit`作为上下文包含的最大群组消息数0 禁用)。
- `channels.imessage.dmHistoryLimit`:私信历史限制(用户轮次)。每用户覆盖:`channels.imessage.dms["<handle>"].historyLimit`
- `channels.imessage.groups`:每群组默认值 + 允许列表(使用 `"*"` 作为全局默认值)。
- `channels.imessage.includeAttachments`:将附件摄取到上下文。
- `channels.imessage.mediaMaxMb`:入站/出站媒体上限MB
- `channels.imessage.textChunkLimit`:出站分块大小(字符)。
- `channels.imessage.chunkMode``length`(默认)或 `newline` 在长度分块前按空行(段落边界)分割。
相关全局选项:
- `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)。
- `messages.responsePrefix`

View File

@@ -1,53 +0,0 @@
---
read_when:
- 你想为 OpenClaw 选择一个聊天渠道
- 你需要快速了解支持的消息平台
summary: OpenClaw 可连接的消息平台
title: 聊天渠道
x-i18n:
generated_at: "2026-02-03T07:43:27Z"
model: claude-opus-4-5
provider: pi
source_hash: 2632863def6dee97e0fa8b931762f0969174fd4fb22303a00dcd46527fe4a141
source_path: channels/index.md
workflow: 15
---
# 聊天渠道
OpenClaw 可以在你已经使用的任何聊天应用上与你交流。每个渠道通过 Gateway 网关连接。
所有渠道都支持文本;媒体和表情回应的支持因渠道而异。
## 支持的渠道
- [WhatsApp](/channels/whatsapp) — 最受欢迎;使用 Baileys需要二维码配对。
- [Telegram](/channels/telegram) — 通过 grammY 使用 Bot API支持群组。
- [Discord](/channels/discord) — Discord Bot API + Gateway支持服务器、频道和私信。
- [Slack](/channels/slack) — Bolt SDK工作区应用。
- [飞书](/channels/feishu) — 飞书Lark机器人插件需单独安装
- [Google Chat](/channels/googlechat) — 通过 HTTP webhook 的 Google Chat API 应用。
- [Mattermost](/channels/mattermost) — Bot API + WebSocket频道、群组、私信插件需单独安装
- [Signal](/channels/signal) — signal-cli注重隐私。
- [BlueBubbles](/channels/bluebubbles) — **推荐用于 iMessage**;使用 BlueBubbles macOS 服务器 REST API功能完整编辑、撤回、特效、回应、群组管理——编辑功能在 macOS 26 Tahoe 上目前不可用)。
- [iMessage旧版](/channels/imessage) — 通过 imsg CLI 的旧版 macOS 集成(已弃用,新设置请使用 BlueBubbles
- [Microsoft Teams](/channels/msteams) — Bot Framework企业支持插件需单独安装
- [LINE](/channels/line) — LINE Messaging API 机器人(插件,需单独安装)。
- [Nextcloud Talk](/channels/nextcloud-talk) — 通过 Nextcloud Talk 的自托管聊天(插件,需单独安装)。
- [Matrix](/channels/matrix) — Matrix 协议(插件,需单独安装)。
- [Nostr](/channels/nostr) — 通过 NIP-04 的去中心化私信(插件,需单独安装)。
- [Tlon](/channels/tlon) — 基于 Urbit 的消息应用(插件,需单独安装)。
- [Twitch](/channels/twitch) — 通过 IRC 连接的 Twitch 聊天(插件,需单独安装)。
- [Zalo](/channels/zalo) — Zalo Bot API越南流行的消息应用插件需单独安装
- [Zalo Personal](/channels/zalouser) — 通过二维码登录的 Zalo 个人账号(插件,需单独安装)。
- [WebChat](/web/webchat) — 基于 WebSocket 的 Gateway 网关 WebChat 界面。
## 注意事项
- 渠道可以同时运行配置多个渠道后OpenClaw 会按聊天进行路由。
- 最快的设置方式通常是 **Telegram**简单的机器人令牌。WhatsApp 需要二维码配对,
并在磁盘上存储更多状态。
- 群组行为因渠道而异;参见[群组](/concepts/groups)。
- 为安全起见,私信配对和允许列表会被强制执行;参见[安全](/gateway/security)。
- Telegram 内部机制:[grammY 说明](/channels/grammy)。
- 故障排除:[渠道故障排除](/channels/troubleshooting)。
- 模型提供商单独记录;参见[模型提供商](/providers/models)。

Some files were not shown because too many files have changed in this diff Show More