mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description Hardens the bundled OpenClaw plugin so configured proxy routing is fail-closed and `autoStart` is opt-in. Closes: N/A This follow-up is intentionally separate from the ContentRouter cache fix because it changes plugin/gateway behavior rather than core compression routing. The plugin should not mutate upstream provider routing unless a configured proxy URL is reachable and looks like Headroom. It should also avoid unhandled startup promise rejections when proxy startup is fire-and-forget. Why this shape: - `autoStart: false` by default matches deployments where Headroom is supervised externally, for example by systemd. The plugin should not silently start or assume ownership of a proxy unless the operator opted in. - Provider routing is fail-closed: a configured URL must first respond like Headroom, not merely expose a generic liveness endpoint. This prevents accidentally routing model traffic through the wrong local service. - `/readyz` is treated as liveness, not identity. Identity comes from Headroom-shaped stats endpoints (`/v1/retrieve/stats` or `/stats`) because those are harder for unrelated services to satisfy by accident. - Startup remains asynchronous, but errors are captured and exposed instead of becoming unhandled promise rejections. - This is a separate PR because the core cache fix is about compression correctness, while this patch is about integration safety around OpenClaw gateway routing. ## Type of Change - [x] Bug fix (non-breaking change fixes issue) - [ ] New feature (non-breaking change adds functionality) - [ ] Breaking change (fix or feature would cause existing functionality change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Make proxy `autoStart` opt-in (`default: false`). - Probe configured `proxyUrl` before applying provider routing. - Treat `/readyz` as liveness only; require Headroom-shaped `/v1/retrieve/stats` or `/stats` for identity. - Observe fire-and-forget startup promise rejection and expose startup error for callers. - Isolate proxy-ready listener failures. - Keep provider routing deferred when no active/probed Headroom proxy exists. - Register retrieve tool with explicit `headroom_retrieve` name. - Extend plugin/unit tests for configured proxy failures, generic non-Headroom endpoints, path collisions, and routing behavior. Changed files: - `plugins/openclaw/README.md` - `plugins/openclaw/openclaw.plugin.json` - `plugins/openclaw/src/engine.ts` - `plugins/openclaw/src/plugin/index.ts` - `plugins/openclaw/src/proxy-manager.ts` - `plugins/openclaw/test/engine.test.ts` - `plugins/openclaw/test/gateway-config.test.ts` - `plugins/openclaw/test/plugin-runtime-routing.test.ts` - `plugins/openclaw/test/proxy-manager.test.ts` ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [x] Type checking passes (`mypy headroom`) - [x] New tests added new functionality - [x] Manual testing performed ### Test Output ```text $ npm test Test Files 6 passed (6) Tests 74 passed (74) $ npm run typecheck tsc --noEmit $ npm run build tsup && node prepare-dist.mjs Build success ``` ## Real Behavior Proof - Environment: local OpenClaw plugin package in the Headroom repo. - Exact command / steps: - Run plugin test suite. - Run TypeScript typecheck. - Run plugin build. - Observed result: - Tests passed: `74/74`. - Typecheck passed. - Build passed. - Not tested: - Full OpenClaw Gateway integration as part of this standalone PR prep. ## Review Readiness - [x] I performed self-review - [x] This PR ready for human review ## Checklist - [x] My code follows project's style guidelines - [x] I performed self-review my code - [ ] I commented my code, particularly in hard-to-understand areas - [x] I made corresponding changes documentation - [x] My changes generate no new warnings - [x] I added tests prove fix is effective or feature works - [x] New and existing unit tests pass locally my changes - [ ] I updated CHANGELOG.md if applicable ## Screenshots (if applicable) N/A. ## Additional Notes Checklist items left unchecked intentionally: - No CHANGELOG update included. - No extra comments were needed beyond existing code structure. Co-authored-by: Björn-Christian Bönkost <bjoern@v2202603344248440850.hotsrv.de>
98 lines
3 KiB
JSON
98 lines
3 KiB
JSON
{
|
|
"id": "headroom",
|
|
"kind": "context-engine",
|
|
"uiHints": {
|
|
"proxyUrl": {
|
|
"label": "Proxy URL",
|
|
"help": "Optional. URL Headroom proxy (example: http://127.0.0.1:8787 or https://headroom.example.com). Configured URLs probe-gated before provider routing. Auto-start opt-in only works local addresses."
|
|
},
|
|
"proxyPort": {
|
|
"label": "Proxy Port",
|
|
"help": "Default port used for auto-detect/auto-start when proxyUrl is not set (default: 8787)."
|
|
},
|
|
"pythonPath": {
|
|
"label": "Python Path",
|
|
"help": "Optional explicit python executable for python fallback launcher (for example: python, python3, py, or full path)."
|
|
},
|
|
"retryMaxAttempts": {
|
|
"label": "Retry Max Attempts",
|
|
"help": "Optional maximum number of upstream retry attempts for connection/read/5xx failures when the plugin auto-starts a local Headroom proxy. Lower values fail faster for interactive chat."
|
|
},
|
|
"connectTimeoutSeconds": {
|
|
"label": "Connect Timeout Seconds",
|
|
"help": "Optional upstream connection timeout for the auto-started local Headroom proxy. Lower values surface network failures sooner."
|
|
},
|
|
"routeCodexViaProxy": {
|
|
"label": "Route OpenAI Codex Via Headroom",
|
|
"help": "When enabled, OpenClaw will use the active Headroom proxy as the in-memory upstream base URL for the built-in openai-codex provider so provider traffic flows through Headroom."
|
|
},
|
|
"gatewayProviderIds": {
|
|
"label": "Gateway Provider IDs",
|
|
"help": "Optional list of OpenClaw provider ids to route through the active Headroom proxy in memory. Friendly aliases codex, claude, copilot, and gemini are also accepted. When set, this overrides the default openai-codex-only routing."
|
|
}
|
|
},
|
|
"configSchema": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"proxyUrl": {
|
|
"type": "string",
|
|
"pattern": "^https?:\\/\\/.+(:\\d+)?\\/?$"
|
|
},
|
|
"proxyPort": {
|
|
"type": "integer",
|
|
"minimum": 1,
|
|
"maximum": 65535,
|
|
"default": 8787
|
|
},
|
|
"pythonPath": {
|
|
"type": "string"
|
|
},
|
|
"autoStart": {
|
|
"type": "boolean",
|
|
"default": false
|
|
},
|
|
"startupTimeoutMs": {
|
|
"type": "integer",
|
|
"minimum": 1000,
|
|
"maximum": 120000,
|
|
"default": 20000
|
|
},
|
|
"retryMaxAttempts": {
|
|
"type": "integer",
|
|
"minimum": 1
|
|
},
|
|
"connectTimeoutSeconds": {
|
|
"type": "integer",
|
|
"minimum": 1
|
|
},
|
|
"routeCodexViaProxy": {
|
|
"type": "boolean",
|
|
"default": true
|
|
},
|
|
"gatewayProviderIds": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"default": []
|
|
}
|
|
}
|
|
},
|
|
"capabilities": {
|
|
"network": {
|
|
"allow": [
|
|
"http://*:*",
|
|
"https://*:*"
|
|
]
|
|
}
|
|
},
|
|
"contracts": {
|
|
"tools": [
|
|
"headroom_retrieve"
|
|
]
|
|
}
|
|
}
|