




















@@ -0,0 +1,310 @@
1+---
2+summary: "Build a plugin that registers a local AI CLI backend"
3+title: "Building CLI backend plugins"
4+sidebarTitle: "CLI backend plugins"
5+read_when:
6+ - You are building a local AI CLI backend plugin
7+ - You want to register a backend for model refs such as acme-cli/model
8+ - You need to map a third-party CLI into OpenClaw's text fallback runner
9+---
10+11+CLI backend plugins let OpenClaw call a local AI CLI as a text inference
12+backend. The backend appears as a provider prefix in model refs:
13+14+```text
15+acme-cli/acme-large
16+```
17+18+Use a CLI backend when the upstream integration is already exposed as a local
19+command, when the CLI owns local login state, or when the CLI is a useful
20+fallback if API providers are unavailable.
21+22+<Info>
23+ If the upstream service exposes a normal HTTP model API, write a
24+[provider plugin](/plugins/sdk-provider-plugins) instead. If the upstream
25+ runtime owns complete agent sessions, tool events, compaction, or background
26+ task state, use an [agent harness](/plugins/sdk-agent-harness).
27+</Info>
28+29+## What the plugin owns
30+31+A CLI backend plugin has three contracts:
32+33+| Contract | File | Purpose |
34+| -------------------- | ---------------------- | --------------------------------------------------------- |
35+| Package entry | `package.json` | Points OpenClaw at the plugin runtime module |
36+| Manifest ownership | `openclaw.plugin.json` | Declares the backend id before runtime loads |
37+| Runtime registration | `index.ts` | Calls `api.registerCliBackend(...)` with command defaults |
38+39+The manifest is discovery metadata. It does not execute the CLI and does not
40+register runtime behavior. Runtime behavior starts when the plugin entry calls
41+`api.registerCliBackend(...)`.
42+43+## Minimal backend plugin
44+45+<Steps>
46+<Step title="Create package metadata">
47+```json package.json
48+{
49+ "name": "@acme/openclaw-acme-cli",
50+ "version": "1.0.0",
51+ "type": "module",
52+ "openclaw": {
53+ "extensions": ["./index.ts"],
54+ "compat": {
55+ "pluginApi": ">=2026.3.24-beta.2",
56+ "minGatewayVersion": "2026.3.24-beta.2"
57+ },
58+ "build": {
59+ "openclawVersion": "2026.3.24-beta.2",
60+ "pluginSdkVersion": "2026.3.24-beta.2"
61+ }
62+ },
63+ "dependencies": {
64+ "openclaw": "^2026.3.24"
65+ },
66+ "devDependencies": {
67+ "typescript": "^5.9.0"
68+ }
69+}
70+```
71+72+Published packages must ship built JavaScript runtime files. If your source
73+entry is `./src/index.ts`, add `openclaw.runtimeExtensions` that points at
74+the built JavaScript peer. See [Entry points](/plugins/sdk-entrypoints).
75+76+</Step>
77+78+<Step title="Declare backend ownership">
79+```json openclaw.plugin.json
80+{
81+ "id": "acme-cli",
82+ "name": "Acme CLI",
83+ "description": "Run Acme's local AI CLI through OpenClaw",
84+ "cliBackends": ["acme-cli"],
85+ "setup": {
86+ "cliBackends": ["acme-cli"],
87+ "requiresRuntime": false
88+ },
89+ "activation": {
90+ "onStartup": false
91+ },
92+ "configSchema": {
93+ "type": "object",
94+ "additionalProperties": false
95+ }
96+}
97+```
98+99+`cliBackends` is the runtime ownership list. It lets OpenClaw auto-load the
100+plugin when config or model selection mentions `acme-cli/...`.
101+102+`setup.cliBackends` is the descriptor-first setup surface. Add it when
103+model discovery, onboarding, or status should recognize the backend without
104+loading plugin runtime. Use `requiresRuntime: false` only when those static
105+descriptors are enough for setup.
106+107+</Step>
108+109+<Step title="Register the backend">
110+```typescript index.ts
111+import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
112+import {
113+ CLI_FRESH_WATCHDOG_DEFAULTS,
114+ CLI_RESUME_WATCHDOG_DEFAULTS,
115+ type CliBackendPlugin,
116+} from "openclaw/plugin-sdk/cli-backend";
117+118+function buildAcmeCliBackend(): CliBackendPlugin {
119+ return {
120+ id: "acme-cli",
121+ liveTest: {
122+ defaultModelRef: "acme-cli/acme-large",
123+ defaultImageProbe: false,
124+ defaultMcpProbe: false,
125+ docker: {
126+ npmPackage: "@acme/acme-cli",
127+ binaryName: "acme",
128+ },
129+ },
130+ config: {
131+ command: "acme",
132+ args: ["chat", "--json"],
133+ output: "json",
134+ input: "stdin",
135+ modelArg: "--model",
136+ sessionArg: "--session",
137+ sessionMode: "existing",
138+ sessionIdFields: ["session_id", "conversation_id"],
139+ systemPromptFileArg: "--system-file",
140+ systemPromptWhen: "first",
141+ imageArg: "--image",
142+ imageMode: "repeat",
143+ reliability: {
144+ watchdog: {
145+ fresh: { ...CLI_FRESH_WATCHDOG_DEFAULTS },
146+ resume: { ...CLI_RESUME_WATCHDOG_DEFAULTS },
147+ },
148+ },
149+ serialize: true,
150+ },
151+ };
152+}
153+154+export default definePluginEntry({
155+ id: "acme-cli",
156+ name: "Acme CLI",
157+ description: "Run Acme's local AI CLI through OpenClaw",
158+ register(api) {
159+ api.registerCliBackend(buildAcmeCliBackend());
160+ },
161+});
162+```
163+164+The backend id must match the manifest `cliBackends` entry. The registered
165+`config` is only the default; user config under
166+`agents.defaults.cliBackends.acme-cli` is merged over it at runtime.
167+168+</Step>
169+</Steps>
170+171+## Config shape
172+173+`CliBackendConfig` describes how OpenClaw should launch and parse the CLI:
174+175+| Field | Use |
176+| ----------------------------------------- | ----------------------------------------------------------- |
177+| `command` | Binary name or absolute command path |
178+| `args` | Base argv for fresh runs |
179+| `resumeArgs` | Alternate argv for resumed sessions; supports `{sessionId}` |
180+| `output` / `resumeOutput` | Parser: `json`, `jsonl`, or `text` |
181+| `input` | Prompt transport: `arg` or `stdin` |
182+| `modelArg` | Flag used before the model id |
183+| `modelAliases` | Map OpenClaw model ids to CLI-native ids |
184+| `sessionArg` / `sessionArgs` | How to pass a session id |
185+| `sessionMode` | `always`, `existing`, or `none` |
186+| `sessionIdFields` | JSON fields OpenClaw reads from CLI output |
187+| `systemPromptArg` / `systemPromptFileArg` | System prompt transport |
188+| `systemPromptWhen` | `first`, `always`, or `never` |
189+| `imageArg` / `imageMode` | Image path support |
190+| `serialize` | Keep same-backend runs ordered |
191+| `reliability.watchdog` | No-output timeout tuning |
192+193+Prefer the smallest static config that matches the CLI. Add plugin callbacks
194+only for behavior that really belongs to the backend.
195+196+## Advanced backend hooks
197+198+`CliBackendPlugin` can also define:
199+200+| Hook | Use |
201+| ---------------------------------- | ------------------------------------------------------ |
202+| `normalizeConfig(config, context)` | Rewrite legacy user config after merge |
203+| `resolveExecutionArgs(ctx)` | Add request-scoped flags such as thinking effort |
204+| `prepareExecution(ctx)` | Create temporary auth or config bridges before launch |
205+| `transformSystemPrompt(ctx)` | Apply a final CLI-specific system prompt transform |
206+| `textTransforms` | Bidirectional prompt/output replacements |
207+| `defaultAuthProfileId` | Prefer a specific OpenClaw auth profile |
208+| `authEpochMode` | Decide how auth changes invalidate stored CLI sessions |
209+| `nativeToolMode` | Declare whether the CLI has always-on native tools |
210+| `bundleMcp` / `bundleMcpMode` | Opt into OpenClaw's loopback MCP tool bridge |
211+212+Keep these hooks provider-owned. Do not add CLI-specific branches to core when a
213+backend hook can express the behavior.
214+215+## MCP tool bridge
216+217+CLI backends do not receive OpenClaw tools by default. If the CLI can consume an
218+MCP configuration, opt in explicitly:
219+220+```typescript
221+return {
222+ id: "acme-cli",
223+ bundleMcp: true,
224+ bundleMcpMode: "codex-config-overrides",
225+ config: {
226+ command: "acme",
227+ args: ["chat", "--json"],
228+ output: "json",
229+ },
230+};
231+```
232+233+Supported bridge modes are:
234+235+| Mode | Use |
236+| ------------------------ | ---------------------------------------------------------------- |
237+| `claude-config-file` | CLIs that accept an MCP config file |
238+| `codex-config-overrides` | CLIs that accept config overrides on argv |
239+| `gemini-system-settings` | CLIs that read MCP settings from their system settings directory |
240+241+Only enable the bridge when the CLI can actually consume it. If the CLI has its
242+own built-in tool layer that cannot be disabled, set `nativeToolMode:
243+"always-on"` so OpenClaw can fail closed when a caller requires no native tools.
244+245+## User configuration
246+247+Users can override any backend default:
248+249+```json5
250+{
251+ agents: {
252+ defaults: {
253+ cliBackends: {
254+"acme-cli": {
255+ command: "/opt/acme/bin/acme",
256+ args: ["chat", "--json", "--profile", "work"],
257+ modelAliases: {
258+ large: "acme-large-2026",
259+ },
260+ },
261+ },
262+ model: {
263+ primary: "openai/gpt-5.5",
264+ fallbacks: ["acme-cli/large"],
265+ },
266+ },
267+ },
268+}
269+```
270+271+Document the minimum override users are likely to need. Usually that is only
272+`command` when the binary is outside `PATH`.
273+274+## Verification
275+276+For bundled plugins, add a focused test around the builder and setup
277+registration, then run the plugin's targeted test lane:
278+279+```bash
280+pnpm test extensions/acme-cli
281+```
282+283+For local or installed plugins, verify discovery and one real model run:
284+285+```bash
286+openclaw plugins inspect acme-cli --runtime --json
287+openclaw agent --message "reply exactly: backend ok" --model acme-cli/acme-large
288+```
289+290+If the backend supports images or MCP, add a live smoke that proves those paths
291+with the real CLI. Do not rely on static inspection for prompt, image, MCP, or
292+session-resume behavior.
293+294+## Checklist
295+296+<Check>`package.json` has `openclaw.extensions` and built runtime entries for published packages</Check>
297+<Check>`openclaw.plugin.json` declares `cliBackends` and intentional `activation.onStartup`</Check>
298+<Check>`setup.cliBackends` is present when setup/model discovery should see the backend cold</Check>
299+<Check>`api.registerCliBackend(...)` uses the same backend id as the manifest</Check>
300+<Check>User overrides under `agents.defaults.cliBackends.<id>` still win</Check>
301+<Check>Session, system prompt, image, and output parser settings match the real CLI contract</Check>
302+<Check>Targeted tests and at least one live CLI smoke prove the backend path</Check>
303+304+## Related
305+306+- [CLI backends](/gateway/cli-backends) - user configuration and runtime behavior
307+- [Building plugins](/plugins/building-plugins) - package and manifest basics
308+- [Plugin SDK overview](/plugins/sdk-overview) - registration API reference
309+- [Plugin manifest](/plugins/manifest) - `cliBackends` and setup descriptors
310+- [Agent harness](/plugins/sdk-agent-harness) - full external agent runtimes
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。