mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-21 14:32:03 +08:00
Compare commits
4 Commits
fix/imessa
...
fix/local-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
171b133f64 | ||
|
|
fc0fdebd8b | ||
|
|
8160d80f35 | ||
|
|
61c82998cc |
6
.github/labeler.yml
vendored
6
.github/labeler.yml
vendored
@@ -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:
|
||||
|
||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
40
.github/workflows/formal-conformance.yml
vendored
40
.github/workflows/formal-conformance.yml
vendored
@@ -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'
|
||||
|
||||
55
.github/workflows/labeler.yml
vendored
55
.github/workflows/labeler.yml
vendored
@@ -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"],
|
||||
});
|
||||
|
||||
@@ -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/"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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 it’s 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).
|
||||
|
||||
190
CHANGELOG.md
190
CHANGELOG.md
@@ -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
|
||||
|
||||
|
||||
@@ -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!**
|
||||
|
||||
11
Dockerfile
11
Dockerfile
@@ -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"]
|
||||
|
||||
14
README.md
14
README.md
@@ -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)
|
||||
|
||||
258
appcast.xml
258
appcast.xml
@@ -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>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 you’ll 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 don’t 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 don’t 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 you’re 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":" (auto‑generated, 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 don’t 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 you’re 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)、Telegram(Bot API / grammY)、Discord(Bot API / 渠道.discord.js)和 iMessage(imsg 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/systemd;WSL2 使用 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 don’t 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":"Follow‑up 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 的 Windows):systemd 用户单元","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 支持 Anthropic(Claude Pro/Max)+ OpenAI(ChatGPT/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)、Telegram(Bot API / grammY)、Discord(Bot API / channels.discord.js)和 iMessage(imsg 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 you’re 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"}
|
||||
|
||||
@@ -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, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.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();
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -23,7 +23,7 @@ cron is the mechanism.
|
||||
- Jobs persist under `~/.openclaw/cron/` so restarts don’t 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 session’s “last route”
|
||||
(the last place the agent replied).
|
||||
|
||||
If `delivery.channel` or `delivery.to` is omitted, cron can fall back to the main session’s
|
||||
“last route” (the last place the agent replied).
|
||||
Delivery notes:
|
||||
|
||||
- If `to` is set, cron auto-delivers the agent’s 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"
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 "$HOME/Scripts/poke-messages.scpt"</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).
|
||||
|
||||
@@ -100,7 +100,7 @@ In **Bot** → **Privileged Gateway Intents**, enable:
|
||||
- **Message Content Intent** (required to read message text in most guilds; without it you’ll 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>`.
|
||||
|
||||
@@ -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 platform’s 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
|
||||
|
||||

|
||||
|
||||
### 3. Copy credentials
|
||||
|
||||
From **Credentials & Basic Info**, copy:
|
||||
|
||||
- **App ID** (format: `cli_xxx`)
|
||||
- **App Secret**
|
||||
|
||||
❗ **Important:** keep the App Secret private.
|
||||
|
||||

|
||||
|
||||
### 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"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 5. Enable bot capability
|
||||
|
||||
In **App Capability** > **Bot**:
|
||||
|
||||
1. Enable bot capability
|
||||
2. Set the bot name
|
||||
|
||||

|
||||
|
||||
### 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.
|
||||
|
||||

|
||||
|
||||
### 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)
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
@@ -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`)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 gateway’s `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 5 m).
|
||||
- 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 agent’s XDG dirs.
|
||||
|
||||
OpenClaw’s 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 can’t 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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
1267
docs/docs.json
1267
docs/docs.json
File diff suppressed because it is too large
Load Diff
@@ -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 don’t 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
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
381
docs/help/faq.md
381
docs/help/faq.md
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 |
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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**
|
||||
|
||||
@@ -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 host‑specific; 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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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 |
@@ -1,59 +0,0 @@
|
||||
# AGENTS.md - zh-CN 文档翻译工作区
|
||||
|
||||
## Read When
|
||||
|
||||
- 维护 `docs/zh-CN/**`
|
||||
- 更新中文翻译流水线(glossary/TM/prompt)
|
||||
- 处理中文翻译反馈或回归
|
||||
|
||||
## Pipeline(docs-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
|
||||
@@ -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 定时器,可以跳过这些脚本。
|
||||
@@ -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。
|
||||
@@ -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) - 系统事件 + 心跳控制
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
---
|
||||
|
||||
# 投票
|
||||
|
||||
## 支持的渠道
|
||||
|
||||
- WhatsApp(Web 渠道)
|
||||
- Discord
|
||||
- MS Teams(Adaptive 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`(字符串,必需)
|
||||
|
||||
## 渠道差异
|
||||
|
||||
- WhatsApp:2-12 个选项,`maxSelections` 必须在选项数量范围内,忽略 `durationHours`。
|
||||
- Discord:2-10 个选项,`durationHours` 限制在 1-768 小时之间(默认 24)。`maxSelections > 1` 启用多选;Discord 不支持严格的选择数量限制。
|
||||
- MS Teams:Adaptive Card 投票(由 OpenClaw 管理)。无原生投票 API;`durationHours` 被忽略。
|
||||
|
||||
## 智能体工具(Message)
|
||||
|
||||
使用 `message` 工具的 `poll` 操作(`to`、`pollQuestion`、`pollOption`,可选 `pollMulti`、`pollDurationHours`、`channel`)。
|
||||
|
||||
注意:Discord 没有"恰好选择 N 个"模式;`pollMulti` 映射为多选。
|
||||
Teams 投票以 Adaptive Cards 形式渲染,需要 Gateway 网关保持在线
|
||||
以将投票记录到 `~/.openclaw/msteams-polls.json`。
|
||||
@@ -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`(危险)。
|
||||
@@ -1,170 +0,0 @@
|
||||
---
|
||||
read_when:
|
||||
- 你想在 OpenClaw 中使用 Amazon Bedrock 模型
|
||||
- 你需要为模型调用配置 AWS 凭证/区域
|
||||
summary: 在 OpenClaw 中使用 Amazon Bedrock(Converse 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 可以通过 pi‑ai 的 **Bedrock Converse** 流式提供商使用 **Amazon Bedrock** 模型。Bedrock 认证使用 **AWS SDK 默认凭证链**,而非 API 密钥。
|
||||
|
||||
## pi‑ai 支持的功能
|
||||
|
||||
- 提供商:`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 提供商。
|
||||
@@ -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 配置。
|
||||
@@ -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)
|
||||
@@ -1,271 +0,0 @@
|
||||
---
|
||||
read_when:
|
||||
- 设置 BlueBubbles 渠道
|
||||
- 排查 webhook 配对问题
|
||||
- 在 macOS 上配置 iMessage
|
||||
summary: 通过 BlueBubbles macOS 服务器使用 iMessage(REST 发送/接收、输入状态、回应、配对、高级操作)。
|
||||
title: BlueBubbles
|
||||
x-i18n:
|
||||
generated_at: "2026-02-03T10:04:52Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: 3aae277a8bec479800a7f6268bfbca912c65a4aadc6e513694057fb873597b69
|
||||
source_path: channels/bluebubbles.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# BlueBubbles(macOS 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 26(Tahoe)上,由于 private API 变更,编辑功能目前不可用。
|
||||
- 在 macOS 26(Tahoe)上群组图标更新可能不稳定:API 可能返回成功但新图标未同步。
|
||||
- OpenClaw 会根据 BlueBubbles 服务器的 macOS 版本自动隐藏已知不可用的操作。如果在 macOS 26(Tahoe)上编辑仍然显示,请使用 `channels.bluebubbles.actions.edit=false` 手动禁用。
|
||||
- 查看状态/健康信息:`openclaw status --all` 或 `openclaw status --deep`。
|
||||
|
||||
有关通用渠道工作流参考,请参阅[渠道](/channels)和[插件](/plugins)指南。
|
||||
@@ -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
|
||||
---
|
||||
|
||||
# Discord(Bot 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)生成邀请 URL(OAuth2 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 到处使用数字 ID;OpenClaw 配置优先使用 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` 的用户在所有消息上的表情反应(空列表禁用)。
|
||||
|
||||
### PluralKit(PK)支持
|
||||
|
||||
启用 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`)。
|
||||
@@ -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. 选择应用图标
|
||||
|
||||

|
||||
|
||||
### 3. 获取应用凭证
|
||||
|
||||
在应用的 **凭证与基础信息** 页面,复制:
|
||||
|
||||
- **App ID**(格式如 `cli_xxx`)
|
||||
- **App Secret**
|
||||
|
||||
❗ **重要**:请妥善保管 App Secret,不要分享给他人。
|
||||
|
||||

|
||||
|
||||
### 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"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 5. 启用机器人能力
|
||||
|
||||
在 **应用能力** > **机器人** 页面:
|
||||
|
||||
1. 开启机器人能力
|
||||
2. 配置机器人名称
|
||||
|
||||

|
||||
|
||||
### 6. 配置事件订阅
|
||||
|
||||
⚠️ **重要提醒**:在配置事件订阅前,请务必确保已完成以下步骤:
|
||||
|
||||
1. 运行 `openclaw channels add` 添加了 Feishu 渠道
|
||||
2. 网关处于启动状态(可通过 `openclaw gateway status` 检查状态)
|
||||
|
||||
在 **事件订阅** 页面:
|
||||
|
||||
1. 选择 **使用长连接接收事件**(WebSocket 模式)
|
||||
2. 添加事件:`im.message.receive_v1`(接收消息)
|
||||
|
||||
⚠️ **注意**:如果网关未启动或渠道未添加,长连接设置将保存失败。
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||
### 获取群组 ID(chat_id)
|
||||
|
||||
群组 ID 格式为 `oc_xxx`,可以通过以下方式获取:
|
||||
|
||||
**方法一**(推荐):
|
||||
|
||||
1. 启动网关并在群组中 @机器人发消息
|
||||
2. 运行 `openclaw logs --follow` 查看日志中的 `chat_id`
|
||||
|
||||
**方法二**:
|
||||
使用飞书 API 调试工具获取机器人所在群组列表。
|
||||
|
||||
### 获取用户 ID(open_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"` | 完全禁止私聊 |
|
||||
|
||||
---
|
||||
|
||||
## 支持的消息类型
|
||||
|
||||
### 接收
|
||||
|
||||
- ✅ 文本消息
|
||||
- ✅ 图片
|
||||
- ✅ 文件
|
||||
- ✅ 音频
|
||||
- ✅ 视频
|
||||
- ✅ 表情包
|
||||
|
||||
### 发送
|
||||
|
||||
- ✅ 文本消息
|
||||
- ✅ 图片
|
||||
- ✅ 文件
|
||||
- ✅ 音频
|
||||
- ⚠️ 富文本(部分支持)
|
||||
@@ -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 Chat(Chat 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 <Your Domain>**。
|
||||
- 在文本框中输入你的邮箱地址(例如 `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 仪表板和其他敏感端点保留在你的私有网络上。
|
||||
|
||||
### 方案 A:Tailscale 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
|
||||
# 如果绑定到 localhost(127.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
|
||||
# 如果绑定到 localhost(127.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。
|
||||
|
||||
### 方案 C:Cloudflare 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)
|
||||
@@ -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 网关配置)。
|
||||
@@ -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`。
|
||||
@@ -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
Reference in New Issue
Block a user