mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
docs: refresh contributor docs
Co-authored-by: Quratulain-bilal <umayaimanshah@gmail.com> Co-authored-by: Mariano Belinky <mbelinky@gmail.com> Co-authored-by: tao <itaofe@gmail.com> Co-authored-by: julian <julian@tencent.com> Co-authored-by: xenouzik <xenouziq@gmail.com> Co-authored-by: Olamiposi <56056759+posigit@users.noreply.github.com> Co-authored-by: surlymochan <surlymo@apache.org> Co-authored-by: Janaka A <contact@janaka.co.uk> Co-authored-by: choiking <samsamuels1927@gmail.com>
This commit is contained in:
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Changes
|
||||
|
||||
- Docs: clarify browser CDP diagnostics, Plugin SDK allowlist imports, status-reaction timing defaults, queue steering behavior, limited-tool troubleshooting, cron HEARTBEAT handling, Telegram multi-agent groups, Bitwarden SecretRef setup, and EasyRunner deployments. Thanks @Quratulain-bilal, @mbelinky, @Mickey-, @vancece, @xenouzik, @posigit, @surlymochan, @janaka, and @choiking.
|
||||
- Docs: clarify IPv4-only Gateway BYOH binding, trusted-proxy scope clearing, Android pairing approval, macOS Accessibility grants, Zalo profile env vars, password-store SecretRef setup, and Chinese memory navigation. Thanks @itskai-dev, @gwh7078, @longstoryscott, @MoeJaberr, and @yuaiccc.
|
||||
- Docs: consolidate GLM under Z.AI, add the Upstash Box install guide and Gateway exposure runbook, clarify MEDIA directives, Copilot and Voyage setup, config path quoting, real behavior proof, and memory-file write guidance. Thanks @BobDu, @alitariksahin, @Jefsky, @musaabhasan, @OmerZeyveli, @leno23, @WuKongAI-CMU, @luoyanglang, and @majin1102.
|
||||
- Docs: clarify media provider credentials, Codex/OpenClaw code-mode boundaries, Slack and Telegram ack reactions, Feishu dynamic agents, secrets plaintext boundaries, memory guidance, and Chinese glossary terms. Thanks @nielskaspers, @cosmopolitan033, @drclaw-iq, @alexgduarte, @zccyman, @chengoak, and @cassthebandit.
|
||||
|
||||
@@ -99,6 +99,13 @@ This fires ~5–6 times per month instead of 0–1 times per month. OpenClaw use
|
||||
<AccordionGroup>
|
||||
<Accordion title="Main session vs isolated vs custom">
|
||||
**Main session** jobs enqueue a system event into a cron-owned run lane and optionally wake the heartbeat (`--wake now` or `--wake next-heartbeat`). They can use the target main session's last delivery context for replies, but they do not append routine cron turns to the human chat lane and do not extend daily/idle reset freshness for the target session. **Isolated** jobs run a dedicated agent turn with a fresh session. **Custom sessions** (`session:xxx`) persist context across runs, enabling workflows like daily standups that build on previous summaries.
|
||||
|
||||
Main-session cron events are self-contained system-event reminders. They do
|
||||
not automatically include the default heartbeat prompt's "Read
|
||||
HEARTBEAT.md" instruction. If a recurring reminder should consult
|
||||
`HEARTBEAT.md`, say that explicitly in the cron event text or in the
|
||||
agent's own instructions.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="What 'fresh session' means for isolated jobs">
|
||||
For isolated jobs, "fresh session" means a new transcript/session id for each run. OpenClaw may carry safe preferences such as thinking/fast/verbose settings, labels, and explicit user-selected model/auth overrides, but it does not inherit ambient conversation context from an older cron row: channel/group routing, send or queue policy, elevation, origin, or ACP runtime binding. Use `current` or `session:<id>` when a recurring job should deliberately build on the same conversation context.
|
||||
|
||||
@@ -357,6 +357,11 @@ Common channels supporting this pattern include:
|
||||
|
||||
- Create one bot per agent with BotFather and copy each token.
|
||||
- Tokens live in `channels.telegram.accounts.<id>.botToken` (default account can use `TELEGRAM_BOT_TOKEN`).
|
||||
- For multiple bots in the same Telegram group, invite each bot and mention the bot that should answer.
|
||||
- Disable BotFather Privacy Mode for each group bot, then re-add the bot so Telegram applies the setting.
|
||||
- Allow groups with `channels.telegram.groups`, or use `groupPolicy: "open"` only for trusted group deployments.
|
||||
- Put sender user IDs in `groupAllowFrom`. Group and supergroup IDs belong in `channels.telegram.groups`, not `groupAllowFrom`.
|
||||
- Bind by `accountId` so each bot routes to its own agent.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="WhatsApp numbers per agent">
|
||||
|
||||
@@ -78,6 +78,20 @@ quiet window in `steer` mode:
|
||||
|
||||
Defaults: `debounceMs: 500`, `cap: 20`, `drop: summarize`.
|
||||
|
||||
## Steer and streaming
|
||||
|
||||
When channel streaming is `partial` or `block`, steering can look like several
|
||||
short visible replies while the active run reaches runtime boundaries:
|
||||
|
||||
- `partial`: the preview may finalize early, then a new preview starts after
|
||||
steering is accepted.
|
||||
- `block`: draft-sized blocks can create the same sequential appearance.
|
||||
- Without streaming, steering falls back to a followup after the active run when
|
||||
the runtime cannot accept same-turn steering.
|
||||
|
||||
`steer` does not abort in-flight tools. Use `/queue interrupt` when the newest
|
||||
message should abort the current run.
|
||||
|
||||
## Precedence
|
||||
|
||||
For mode selection, OpenClaw resolves:
|
||||
|
||||
@@ -1437,7 +1437,8 @@
|
||||
"platforms/linux",
|
||||
"platforms/windows",
|
||||
"platforms/android",
|
||||
"platforms/ios"
|
||||
"platforms/ios",
|
||||
"platforms/easyrunner"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -311,6 +311,60 @@ the config fields that accept SecretRefs.
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Bitwarden Secrets Manager (`bws`)">
|
||||
Use a resolver wrapper when you want SecretRef ids to map to Bitwarden
|
||||
Secrets Manager item keys. The repository includes
|
||||
`scripts/secrets/openclaw-bws-resolver.mjs`; install or copy it to an absolute
|
||||
trusted path on the host that runs the Gateway.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Bitwarden Secrets Manager CLI (`bws`) installed on the Gateway host.
|
||||
- `BWS_ACCESS_TOKEN` available to the Gateway service.
|
||||
- `PATH` passed to the resolver, or `BWS_BIN` set to the absolute `bws`
|
||||
binary path.
|
||||
|
||||
```json5
|
||||
{
|
||||
secrets: {
|
||||
providers: {
|
||||
bws: {
|
||||
source: "exec",
|
||||
command: "/usr/local/bin/openclaw-bws-resolver.mjs",
|
||||
passEnv: ["BWS_ACCESS_TOKEN", "PATH", "BWS_BIN"],
|
||||
jsonOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
models: [{ id: "gpt-5", name: "gpt-5" }],
|
||||
apiKey: {
|
||||
source: "exec",
|
||||
provider: "bws",
|
||||
id: "openclaw/providers/openai/apiKey",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The resolver batches requested ids, runs `bws secret list`, and returns
|
||||
values for matching secret `key` fields. Use keys that satisfy the exec
|
||||
SecretRef id contract, such as `openclaw/providers/openai/apiKey`; env-var
|
||||
style keys with underscores are rejected before the resolver runs. If more
|
||||
than one visible Bitwarden secret has the same requested key, the resolver
|
||||
fails that id as ambiguous instead of choosing one. After updating config,
|
||||
verify the resolver path:
|
||||
|
||||
```bash
|
||||
openclaw secrets audit --allow-exec
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="HashiCorp Vault CLI">
|
||||
```json5
|
||||
{
|
||||
|
||||
@@ -34,6 +34,31 @@ Good output in one line:
|
||||
gateway is unreachable, the command falls back to config-only summaries.
|
||||
- `openclaw logs --follow` → steady activity, no repeating fatal errors.
|
||||
|
||||
## Assistant feels limited or missing tools
|
||||
|
||||
If the assistant cannot inspect files, run commands, use browser automation, or
|
||||
see expected tools, check the effective tool profile first:
|
||||
|
||||
```bash
|
||||
openclaw status
|
||||
openclaw status --all
|
||||
openclaw doctor
|
||||
```
|
||||
|
||||
Common causes:
|
||||
|
||||
- `tools.profile: "messaging"` is intentionally narrow for chat-only agents.
|
||||
- `tools.profile: "coding"` is the usual profile for repository, file, shell,
|
||||
and runtime workflows.
|
||||
- `tools.profile: "full"` exposes the broadest tool set and should be limited
|
||||
to trusted operator-controlled agents.
|
||||
- Per-agent `agents.list[].tools` overrides can narrow or expand the root
|
||||
profile for one agent.
|
||||
|
||||
Change the root or per-agent tool profile, then restart or reload the Gateway
|
||||
and run `openclaw status --all` again. See [Tools](/tools) for the profile
|
||||
model and allow/deny overrides.
|
||||
|
||||
## Anthropic long context 429
|
||||
|
||||
If you see:
|
||||
|
||||
109
docs/platforms/easyrunner.md
Normal file
109
docs/platforms/easyrunner.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
summary: "Run the OpenClaw Gateway on EasyRunner with Podman and Caddy"
|
||||
read_when:
|
||||
- Deploying OpenClaw on EasyRunner
|
||||
- Running the Gateway behind EasyRunner's Caddy proxy
|
||||
- Choosing persistent volumes and auth for a hosted Gateway
|
||||
title: "EasyRunner"
|
||||
---
|
||||
|
||||
EasyRunner can host the OpenClaw Gateway as a small containerized app behind its
|
||||
Caddy proxy. This guide assumes an EasyRunner host that runs Podman-compatible
|
||||
Compose apps and exposes HTTPS through Caddy.
|
||||
|
||||
## Before you begin
|
||||
|
||||
- An EasyRunner server with a domain routed to it.
|
||||
- A built or published OpenClaw container image.
|
||||
- A persistent config volume for `/home/node/.openclaw`.
|
||||
- A persistent workspace volume for `/workspace`.
|
||||
- A strong Gateway token or password.
|
||||
|
||||
Keep device auth enabled when possible. If your reverse proxy deployment cannot
|
||||
carry device identity correctly, fix trusted-proxy settings first; use
|
||||
dangerous auth bypasses only for a fully private, operator-controlled network.
|
||||
|
||||
## Compose app
|
||||
|
||||
Create an EasyRunner app with a Compose file shaped like this:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
openclaw:
|
||||
image: ghcr.io/openclaw/openclaw:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
|
||||
OPENCLAW_HOME: /home/node
|
||||
OPENCLAW_STATE_DIR: /home/node/.openclaw
|
||||
OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
|
||||
OPENCLAW_WORKSPACE_DIR: /workspace
|
||||
volumes:
|
||||
- openclaw-config:/home/node/.openclaw
|
||||
- openclaw-workspace:/workspace
|
||||
labels:
|
||||
caddy: openclaw.example.com
|
||||
caddy.reverse_proxy: "{{upstreams 1455}}"
|
||||
command: ["openclaw", "gateway", "--bind", "lan", "--port", "1455"]
|
||||
|
||||
volumes:
|
||||
openclaw-config:
|
||||
openclaw-workspace:
|
||||
```
|
||||
|
||||
Replace `openclaw.example.com` with your Gateway hostname. Store
|
||||
`OPENCLAW_GATEWAY_TOKEN` in EasyRunner's secret/environment manager instead of
|
||||
committing it to the app definition.
|
||||
|
||||
## Configure OpenClaw
|
||||
|
||||
Inside the persistent config volume, keep the Gateway reachable only through
|
||||
the proxy and require auth:
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
bind: "lan",
|
||||
port: 1455,
|
||||
auth: {
|
||||
token: "${OPENCLAW_GATEWAY_TOKEN}",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If Caddy terminates TLS for the Gateway, configure trusted proxy settings for
|
||||
the exact proxy path rather than disabling auth checks globally. See
|
||||
[Trusted proxy auth](/gateway/trusted-proxy-auth).
|
||||
|
||||
## Verify
|
||||
|
||||
From your workstation:
|
||||
|
||||
```bash
|
||||
openclaw gateway probe --url https://openclaw.example.com --token <token>
|
||||
openclaw gateway status --url https://openclaw.example.com --token <token>
|
||||
```
|
||||
|
||||
From the EasyRunner host, check the app logs for a listening Gateway and no
|
||||
startup SecretRef, plugin, or channel auth failures.
|
||||
|
||||
## Updates and backups
|
||||
|
||||
- Pull or build the new OpenClaw image, then redeploy the EasyRunner app.
|
||||
- Back up the `openclaw-config` volume before updates.
|
||||
- Back up `openclaw-workspace` if agents write durable project data there.
|
||||
- Run `openclaw doctor` after major updates to catch config migrations and
|
||||
service warnings.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- `gateway probe` cannot connect: confirm the Caddy hostname points at the app
|
||||
and that the container listens on `0.0.0.0:1455`.
|
||||
- Auth fails: rotate the token in EasyRunner secrets and the local client
|
||||
command together.
|
||||
- Files are root-owned after restore: repair the mounted volumes so the
|
||||
container user can write `/home/node/.openclaw` and `/workspace`.
|
||||
- Browser or channel plugins fail: check whether the required external
|
||||
binaries, network egress, and mounted credentials are available inside the
|
||||
container.
|
||||
@@ -30,6 +30,7 @@ Native companion apps for Windows are also planned; the Gateway is recommended v
|
||||
- GCP (Compute Engine): [GCP](/install/gcp)
|
||||
- Azure (Linux VM): [Azure](/install/azure)
|
||||
- exe.dev (VM + HTTPS proxy): [exe.dev](/install/exe-dev)
|
||||
- EasyRunner (Podman + Caddy): [EasyRunner](/platforms/easyrunner)
|
||||
|
||||
## Common links
|
||||
|
||||
|
||||
@@ -563,8 +563,7 @@ releases.
|
||||
| `plugin-sdk/fetch-runtime` | Wrapped fetch/proxy helpers | `resolveFetch`, proxy helpers, EnvHttpProxyAgent option helpers |
|
||||
| `plugin-sdk/host-runtime` | Host normalization helpers | `normalizeHostname`, `normalizeScpRemoteHost` |
|
||||
| `plugin-sdk/retry-runtime` | Retry helpers | `RetryConfig`, `retryAsync`, policy runners |
|
||||
| `plugin-sdk/allow-from` | Allowlist formatting | `formatAllowFromLowercase` |
|
||||
| `plugin-sdk/allowlist-resolution` | Allowlist input mapping | `mapAllowlistResolutionInputs` |
|
||||
| `plugin-sdk/allow-from` | Allowlist formatting and input mapping | `formatAllowFromLowercase`, `mapAllowlistResolutionInputs` |
|
||||
| `plugin-sdk/command-auth` | Command gating and command-surface helpers | `resolveControlCommandGate`, sender-authorization helpers, command registry helpers including dynamic argument menu formatting |
|
||||
| `plugin-sdk/command-status` | Command status/help renderers | `buildCommandsMessage`, `buildCommandsMessagePaginated`, `buildHelpMessage` |
|
||||
| `plugin-sdk/secret-input` | Secret input parsing | Secret input helpers |
|
||||
|
||||
@@ -442,6 +442,10 @@ CDP URL shapes and picks the right connection strategy automatically:
|
||||
providers can still use their root WebSocket endpoint when their discovery
|
||||
endpoint advertises a short-lived URL that is not suitable for Playwright CDP.
|
||||
|
||||
`openclaw browser doctor` uses the same discovery-first, WebSocket-fallback
|
||||
logic as runtime attach, so a bare-root URL that connects successfully is not
|
||||
reported as unreachable by diagnostics.
|
||||
|
||||
### Browserbase
|
||||
|
||||
[Browserbase](https://www.browserbase.com) is a cloud platform for running
|
||||
|
||||
94
scripts/secrets/openclaw-bws-resolver.mjs
Executable file
94
scripts/secrets/openclaw-bws-resolver.mjs
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env node
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const readStdin = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
let input = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
input += chunk;
|
||||
});
|
||||
process.stdin.on("end", () => resolve(input));
|
||||
process.stdin.on("error", reject);
|
||||
});
|
||||
|
||||
const parseRequest = (input) => {
|
||||
try {
|
||||
return JSON.parse(input || "{}");
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse request JSON: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
const isSecretRecord = (value) =>
|
||||
value &&
|
||||
typeof value === "object" &&
|
||||
typeof value.key === "string" &&
|
||||
typeof value.value === "string";
|
||||
|
||||
const main = async () => {
|
||||
const request = parseRequest(await readStdin());
|
||||
if (request.protocolVersion !== 1) {
|
||||
throw new Error("Unsupported SecretRef protocolVersion");
|
||||
}
|
||||
|
||||
const ids = Array.isArray(request.ids)
|
||||
? request.ids.filter((id) => typeof id === "string" && id.length > 0)
|
||||
: [];
|
||||
if (ids.length === 0) {
|
||||
process.stdout.write(JSON.stringify({ protocolVersion: 1, values: {}, errors: {} }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.BWS_ACCESS_TOKEN) {
|
||||
throw new Error("BWS_ACCESS_TOKEN is required");
|
||||
}
|
||||
|
||||
const bwsBin =
|
||||
process.env.BWS_BIN && process.env.BWS_BIN.trim() ? process.env.BWS_BIN.trim() : "bws";
|
||||
const raw = execFileSync(bwsBin, ["secret", "list"], {
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
BWS_ACCESS_TOKEN: process.env.BWS_ACCESS_TOKEN,
|
||||
PATH: process.env.PATH || "",
|
||||
},
|
||||
maxBuffer: 1024 * 1024,
|
||||
timeout: 15_000,
|
||||
});
|
||||
|
||||
let secrets;
|
||||
try {
|
||||
secrets = JSON.parse(raw);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse bws output: ${error.message}`);
|
||||
}
|
||||
|
||||
const byKey = new Map();
|
||||
for (const secret of Array.isArray(secrets) ? secrets : []) {
|
||||
if (isSecretRecord(secret)) {
|
||||
const values = byKey.get(secret.key) || [];
|
||||
values.push(secret.value);
|
||||
byKey.set(secret.key, values);
|
||||
}
|
||||
}
|
||||
|
||||
const values = {};
|
||||
const errors = {};
|
||||
for (const id of ids) {
|
||||
const matches = byKey.get(id) || [];
|
||||
if (matches.length === 1) {
|
||||
values[id] = matches[0];
|
||||
} else if (matches.length > 1) {
|
||||
errors[id] = { message: "ambiguous duplicate key" };
|
||||
} else {
|
||||
errors[id] = { message: "not found" };
|
||||
}
|
||||
}
|
||||
|
||||
process.stdout.write(JSON.stringify({ protocolVersion: 1, values, errors }));
|
||||
};
|
||||
|
||||
main().catch((error) => {
|
||||
process.stderr.write(`${error.message}\n`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1918,7 +1918,7 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
"messages.statusReactions.emojis":
|
||||
"Override default status reaction emojis. Keys: queued, thinking, compacting, tool, coding, web, deploy, build, concierge, done, error, stallSoft, stallHard. Telegram chooses the first supported fallback when a configured emoji is not available in the chat.",
|
||||
"messages.statusReactions.timing":
|
||||
"Override default timing. Keys: debounceMs (700), stallSoftMs (25000), stallHardMs (60000), doneHoldMs (1500), errorHoldMs (2500).",
|
||||
"Override default timing. Keys: debounceMs (700), stallSoftMs (10000), stallHardMs (30000), doneHoldMs (1500), errorHoldMs (2500).",
|
||||
"messages.inbound.debounceMs":
|
||||
"Debounce window (ms) for batching rapid inbound messages from the same sender (0 to disable).",
|
||||
};
|
||||
|
||||
@@ -79,9 +79,9 @@ export type StatusReactionsEmojiConfig = {
|
||||
export type StatusReactionsTimingConfig = {
|
||||
/** Debounce interval for intermediate states (ms). Default: 700. */
|
||||
debounceMs?: number;
|
||||
/** Soft stall warning timeout (ms). Default: 25000. */
|
||||
/** Soft stall warning timeout (ms). Default: 10000. */
|
||||
stallSoftMs?: number;
|
||||
/** Hard stall warning timeout (ms). Default: 60000. */
|
||||
/** Hard stall warning timeout (ms). Default: 30000. */
|
||||
stallHardMs?: number;
|
||||
/** How long to hold done emoji before cleanup (ms). Default: 1500. */
|
||||
doneHoldMs?: number;
|
||||
|
||||
Reference in New Issue
Block a user