mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
fix: apply docs sweep updates
This commit is contained in:
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Changes
|
||||
|
||||
- Packaging: exclude documentation images and assets from the npm tarball, reducing published package size without affecting runtime docs search or CLI behavior. Thanks @SebTardif.
|
||||
- Agents/subagents: limit default sub-agent bootstrap context to `AGENTS.md` and `TOOLS.md`, keeping persona, identity, user, memory, heartbeat, and setup files out of delegated workers by default. (#85283) Thanks @100yenadmin.
|
||||
- Maintainer skills: exclude plugin SDK/API boundary work from `openclaw-landable-bug-sweep` so bugbash sweeps stay focused on small paper-cut fixes.
|
||||
- Plugin SDK: add a generic channel-message poll sender so channel plugins can expose poll delivery without depending on channel-specific SDK facades.
|
||||
@@ -38,6 +39,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- CLI/update: repair managed npm plugin `openclaw` peer links during post-core convergence and reject stale or wrong-target peer links before restart. (#83794) Thanks @fuller-stack-dev.
|
||||
- CLI/agents: default new omitted-account bindings to all accounts when the channel has multiple configured accounts, and clarify account-scope docs. (#49769) Thanks @Gcaufy.
|
||||
- Codex app-server: disable native Code Mode when the effective exec host is `node` and keep OpenClaw `exec`/`process` available, so `/exec host=node` routes shell commands through the selected node instead of the gateway. Fixes #85012. (#85090) Thanks @sahilsatralkar.
|
||||
- Agents: bound embedded auto-compaction session write-lock watchdogs to the compaction timeout instead of the full run timeout, so stuck compaction cannot hold the live session lock for the whole run window. (#84949) Thanks @luoyanglang.
|
||||
- Gateway: defer provider auth-state prewarm until after startup readiness so early gateway tool/session requests are not blocked by provider auth discovery. (#85272) Thanks @dutifulbob.
|
||||
|
||||
@@ -21,6 +21,7 @@ Related:
|
||||
openclaw agents list
|
||||
openclaw agents list --bindings
|
||||
openclaw agents add work --workspace ~/.openclaw/workspace-work
|
||||
openclaw agents add work --workspace ~/.openclaw/workspace-work --bind telegram:*
|
||||
openclaw agents add ops --workspace ~/.openclaw/workspace-ops --bind telegram:ops --non-interactive
|
||||
openclaw agents bindings
|
||||
openclaw agents bind --agent work --bind telegram:ops
|
||||
@@ -50,27 +51,47 @@ Add bindings:
|
||||
openclaw agents bind --agent work --bind telegram:ops --bind discord:guild-a
|
||||
```
|
||||
|
||||
If you omit `accountId` (`--bind <channel>`), OpenClaw resolves it from channel defaults and plugin setup hooks when available.
|
||||
You can also add bindings when creating an agent:
|
||||
|
||||
```bash
|
||||
openclaw agents add work --workspace ~/.openclaw/workspace-work --bind telegram:* --bind discord:*
|
||||
```
|
||||
|
||||
If you omit `accountId` (`--bind <channel>`), OpenClaw resolves it from plugin setup hooks, forced account binding, or the channel's configured account count.
|
||||
|
||||
If you omit `--agent` for `bind` or `unbind`, OpenClaw targets the current default agent.
|
||||
|
||||
### `--bind` format
|
||||
|
||||
| Format | Meaning |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| `--bind <channel>:*` | Match all accounts on the channel. |
|
||||
| `--bind <channel>:<account>` | Match one account. |
|
||||
| `--bind <channel>` | Match the default account only unless the CLI can safely resolve a plugin-specific account scope. |
|
||||
|
||||
### Binding scope behavior
|
||||
|
||||
- A binding without `accountId` matches the channel default account only.
|
||||
- A stored binding without `accountId` matches the channel default account only.
|
||||
- `accountId: "*"` is the channel-wide fallback (all accounts) and is less specific than an explicit account binding.
|
||||
- If the same agent already has a matching channel binding without `accountId`, and you later bind with an explicit or resolved `accountId`, OpenClaw upgrades that existing binding in place instead of adding a duplicate.
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
# match all accounts on the channel
|
||||
openclaw agents bind --agent work --bind telegram:*
|
||||
|
||||
# match a specific account
|
||||
openclaw agents bind --agent work --bind telegram:ops
|
||||
|
||||
# initial channel-only binding
|
||||
openclaw agents bind --agent work --bind telegram
|
||||
|
||||
# later upgrade to account-scoped binding
|
||||
openclaw agents bind --agent work --bind telegram:ops
|
||||
openclaw agents bind --agent work --bind telegram:alerts
|
||||
```
|
||||
|
||||
After the upgrade, routing for that binding is scoped to `telegram:ops`. If you also want default-account routing, add it explicitly (for example `--bind telegram:default`).
|
||||
After the upgrade, routing for that binding is scoped to `telegram:alerts`. If you also want default-account routing, add it explicitly (for example `--bind telegram:default`).
|
||||
|
||||
Remove bindings:
|
||||
|
||||
|
||||
@@ -77,6 +77,36 @@ openclaw devices approve <requestId>
|
||||
openclaw devices approve --latest
|
||||
```
|
||||
|
||||
## Paperclip / `openclaw_gateway` first-run approval
|
||||
|
||||
When a new Paperclip agent connects through the `openclaw_gateway` adapter for the first time, the Gateway may require a one-time device pairing approval before runs can succeed. If Paperclip reports `openclaw_gateway_pairing_required`, approve the pending device and retry.
|
||||
|
||||
For local gateways, preview the latest pending request:
|
||||
|
||||
```bash
|
||||
openclaw devices approve --latest
|
||||
```
|
||||
|
||||
The preview prints the exact `openclaw devices approve <requestId>` command. Verify the request details, then rerun that command with the request ID to approve it.
|
||||
|
||||
For remote gateways or explicit credentials, pass the same options while previewing and approving:
|
||||
|
||||
```bash
|
||||
openclaw devices approve --latest --url <gateway-ws-url> --token <gateway-token>
|
||||
```
|
||||
|
||||
To avoid re-approving after restarts, keep a persistent device key in the Paperclip adapter config instead of generating a new ephemeral identity each run:
|
||||
|
||||
```json
|
||||
{
|
||||
"adapterConfig": {
|
||||
"devicePrivateKeyPem": "<ed25519-private-key-pkcs8-pem>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If approval keeps failing, run `openclaw devices list` first to confirm a pending request exists.
|
||||
|
||||
### `openclaw devices reject <requestId>`
|
||||
|
||||
Reject a pending device pairing request.
|
||||
|
||||
@@ -245,8 +245,9 @@ Bindings are **deterministic** and **most-specific wins**:
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Account-scope detail">
|
||||
- A binding that omits `accountId` matches the default account only.
|
||||
- A binding that omits `accountId` matches the default account only. It does not match all accounts.
|
||||
- Use `accountId: "*"` for a channel-wide fallback across all accounts.
|
||||
- Use `accountId: "<name>"` to match one account.
|
||||
- If you later add the same binding for the same agent with an explicit account id, OpenClaw upgrades the existing channel-only binding to account-scoped instead of duplicating it.
|
||||
|
||||
</Accordion>
|
||||
@@ -457,15 +458,15 @@ Common channels supporting this pattern include:
|
||||
],
|
||||
},
|
||||
bindings: [
|
||||
{ agentId: "chat", match: { channel: "whatsapp" } },
|
||||
{ agentId: "opus", match: { channel: "telegram" } },
|
||||
{ agentId: "chat", match: { channel: "whatsapp", accountId: "*" } },
|
||||
{ agentId: "opus", match: { channel: "telegram", accountId: "*" } },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- If you have multiple accounts for a channel, add `accountId` to the binding (for example `{ channel: "whatsapp", accountId: "personal" }`).
|
||||
- These examples use `accountId: "*"` so the bindings keep working if you add accounts later.
|
||||
- To route a single DM/group to Opus while keeping the rest on chat, add a `match.peer` binding for that peer; peer matches always win over channel-wide rules.
|
||||
|
||||
</Tab>
|
||||
@@ -493,9 +494,9 @@ Common channels supporting this pattern include:
|
||||
bindings: [
|
||||
{
|
||||
agentId: "opus",
|
||||
match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551234567" } },
|
||||
match: { channel: "whatsapp", accountId: "*", peer: { kind: "direct", id: "+15551234567" } },
|
||||
},
|
||||
{ agentId: "chat", match: { channel: "whatsapp" } },
|
||||
{ agentId: "chat", match: { channel: "whatsapp", accountId: "*" } },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@@ -153,12 +153,6 @@ The proxy:
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Links
|
||||
|
||||
- **npm:** [https://www.npmjs.com/package/claude-max-api-proxy](https://www.npmjs.com/package/claude-max-api-proxy)
|
||||
- **GitHub:** [https://github.com/atalovesyou/claude-max-api-proxy](https://github.com/atalovesyou/claude-max-api-proxy)
|
||||
- **Issues:** [https://github.com/atalovesyou/claude-max-api-proxy/issues](https://github.com/atalovesyou/claude-max-api-proxy/issues)
|
||||
|
||||
## Notes
|
||||
|
||||
- This is a **community tool**, not officially supported by Anthropic or OpenClaw
|
||||
|
||||
@@ -56,6 +56,8 @@ If the browser is already paired and you change it from read access to write/adm
|
||||
|
||||
Once approved, the device is remembered and won't require re-approval unless you revoke it with `openclaw devices revoke --device <id> --role <role>`. See [Devices CLI](/cli/devices) for token rotation and revocation.
|
||||
|
||||
Paperclip agents that connect through the `openclaw_gateway` adapter use the same first-run approval flow. After the initial connection attempt, run `openclaw devices approve --latest` to preview the pending request, then rerun the printed `openclaw devices approve <requestId>` command to approve it. Pass explicit `--url` and `--token` values for a remote gateway. To keep approvals stable across restarts, configure a persistent `adapterConfig.devicePrivateKeyPem` in Paperclip instead of letting it generate a new ephemeral device identity each run.
|
||||
|
||||
<Note>
|
||||
- Direct local loopback browser connections (`127.0.0.1` / `localhost`) are auto-approved.
|
||||
- Tailscale Serve can skip the pairing round trip for Control UI operator sessions when `gateway.auth.allowTailscale: true`, Tailscale identity verifies, and the browser presents its device identity.
|
||||
|
||||
@@ -81,6 +81,10 @@
|
||||
"docs/",
|
||||
"!docs/.generated/**",
|
||||
"!docs/channels/qa-channel.md",
|
||||
"!docs/assets/**",
|
||||
"!docs/images/**",
|
||||
"!docs/**/*.jpg",
|
||||
"!docs/**/*.png",
|
||||
"scripts/crabbox-wrapper.mjs",
|
||||
"patches/",
|
||||
"skills/",
|
||||
|
||||
@@ -325,17 +325,24 @@ func (ri *routeIndex) localizeURL(raw string) string {
|
||||
|
||||
func hasURLScheme(raw string) bool {
|
||||
switch {
|
||||
case strings.HasPrefix(raw, "http://"), strings.HasPrefix(raw, "https://"):
|
||||
case hasSchemePrefix(raw, "http://"), hasSchemePrefix(raw, "https://"):
|
||||
return true
|
||||
case strings.HasPrefix(raw, "mailto:"), strings.HasPrefix(raw, "tel:"):
|
||||
case hasSchemePrefix(raw, "mailto:"), hasSchemePrefix(raw, "tel:"):
|
||||
return true
|
||||
case strings.HasPrefix(raw, "data:"), strings.HasPrefix(raw, "javascript:"):
|
||||
case hasSchemePrefix(raw, "data:"), hasSchemePrefix(raw, "javascript:"), hasSchemePrefix(raw, "vbscript:"):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func hasSchemePrefix(raw, prefix string) bool {
|
||||
if len(raw) < len(prefix) {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(raw[:len(prefix)], prefix)
|
||||
}
|
||||
|
||||
func splitURLSuffix(raw string) (string, string) {
|
||||
index := strings.IndexAny(raw, "?#")
|
||||
if index == -1 {
|
||||
|
||||
@@ -49,6 +49,16 @@ func TestLocalizeBodyLinks(t *testing.T) {
|
||||
input: `See [Config](/zh-CN/gateway/configuration).`,
|
||||
want: `See [Config](/zh-CN/gateway/configuration).`,
|
||||
},
|
||||
{
|
||||
name: "vbscript scheme stays unchanged",
|
||||
input: `<a href="vbscript:msgbox(1)">bad</a>`,
|
||||
want: `<a href="vbscript:msgbox(1)">bad</a>`,
|
||||
},
|
||||
{
|
||||
name: "mixed-case javascript scheme stays unchanged",
|
||||
input: `<a href="Javascript:alert(1)">bad</a>`,
|
||||
want: `<a href="Javascript:alert(1)">bad</a>`,
|
||||
},
|
||||
{
|
||||
name: "missing localized page stays unchanged",
|
||||
input: `See [FAQ](/help/faq).`,
|
||||
|
||||
@@ -47,6 +47,7 @@ function createBindingResolverTestPlugin(params: {
|
||||
id: ChannelId;
|
||||
config: Partial<ChannelPlugin["config"]>;
|
||||
resolveBindingAccountId?: NonNullable<ChannelPlugin["setup"]>["resolveBindingAccountId"];
|
||||
forceAccountBinding?: boolean;
|
||||
}): BindingResolverTestPlugin {
|
||||
return {
|
||||
id: params.id,
|
||||
@@ -56,6 +57,7 @@ function createBindingResolverTestPlugin(params: {
|
||||
selectionLabel: params.id,
|
||||
docsPath: `/channels/${params.id}`,
|
||||
blurb: "test stub.",
|
||||
...(params.forceAccountBinding ? { forceAccountBinding: true } : {}),
|
||||
},
|
||||
capabilities: { chatTypes: ["direct"] },
|
||||
config: {
|
||||
@@ -93,6 +95,14 @@ vi.mock("../channels/plugins/bundled.js", () => {
|
||||
"telegram",
|
||||
createBindingResolverTestPlugin({ id: "telegram", config: { listAccountIds: () => [] } }),
|
||||
],
|
||||
[
|
||||
"whatsapp",
|
||||
createBindingResolverTestPlugin({
|
||||
id: "whatsapp",
|
||||
config: { listAccountIds: () => ["default", "biz"] },
|
||||
forceAccountBinding: true,
|
||||
}),
|
||||
],
|
||||
]);
|
||||
return {
|
||||
getBundledChannelSetupPlugin: (channel: string) => {
|
||||
@@ -160,6 +170,22 @@ describe("agents bind/unbind commands", () => {
|
||||
expect(runtime.exit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses a wildcard account binding for multi-account channels", async () => {
|
||||
readConfigFileSnapshotMock.mockResolvedValue({
|
||||
...baseConfigSnapshot,
|
||||
config: {},
|
||||
});
|
||||
|
||||
await agentsBindCommand({ bind: ["whatsapp"] }, runtime);
|
||||
|
||||
expect(writeConfigFileMock).toHaveBeenCalledTimes(1);
|
||||
const writtenConfig = firstWrittenConfig();
|
||||
expect(writtenConfig?.bindings).toStrictEqual([
|
||||
{ type: "route", agentId: "main", match: { channel: "whatsapp", accountId: "*" } },
|
||||
]);
|
||||
expect(runtime.exit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("binds manifest-known external channels without loading plugin runtime", async () => {
|
||||
readConfigFileSnapshotMock.mockResolvedValue({
|
||||
...baseConfigSnapshot,
|
||||
|
||||
@@ -266,6 +266,10 @@ function resolveBindingAccountId(params: {
|
||||
return pluginAccountId.trim();
|
||||
}
|
||||
|
||||
if (plugin && plugin.config.listAccountIds(params.config).length > 1) {
|
||||
return "*";
|
||||
}
|
||||
|
||||
if (plugin?.meta.forceAccountBinding) {
|
||||
return resolveDefaultAccountId(params.config, params.channel);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,12 @@ export type AgentRuntimeConfig =
|
||||
|
||||
export type AgentBindingMatch = {
|
||||
channel: string;
|
||||
/**
|
||||
* Channel account to match.
|
||||
* - Omitted/empty: matches only the channel default account.
|
||||
* - "*": matches every account on the channel.
|
||||
* - Any other string: matches that specific account id.
|
||||
*/
|
||||
accountId?: string;
|
||||
peer?: { kind: ChatType; id: string };
|
||||
guildId?: string;
|
||||
|
||||
Reference in New Issue
Block a user