- Introduce `hostHTTPOperationBridge` to manage, claim, and cancel host HTTP operations with scoped cleanups - Scope HTTP operations and streams to specific plugin instances to isolate lifecycles and prevent cross-plugin access - Add RPC handlers for `MethodHostHTTPOperationOpen` and `MethodHostHTTPCancel` - Link operation cancellation and resource cleanup to host callback contexts and stream termination Closes: #6085
Standard Dynamic Library Plugin Examples
This directory contains standard dynamic library plugin examples for the CLIProxyAPI C ABI.
Layout
simple/: full provider-native skeleton that declares every supported capability.model/: model capability only.auth/: auth provider capability only.frontend-auth/: frontend auth provider capability only.frontend-auth-exclusive/: frontend auth provider that becomes the only request authentication provider when selected.executor/: executor capability only.protocol-format/: minimal executor focused on input/output format declarations.request-translator/: request translation capability only.request-normalizer/: request normalization capability only.codex-service-tier/: Go-only request normalizer that sets Codexgpt-5.5requests to the priority service tier when enabled.request-lifecycle/: Go-only request admission example with concurrency control, active HTTP termination, and terminal callbacks.scheduler/: Go-only scheduler that can select a configured auth ID, delegate to a built-in scheduler, or deny picks.claude-web-search-router/: ModelRouter + executor for Claude Code built-inweb_search(antigravity / codex / xai / Tavily). Seeclaude-web-search-router/README.md.response-translator/: response translation capability only.response-normalizer/: response normalization capability only.thinking/: thinking applier capability only.usage/: usage observer capability only.cli/: command-line capability only.management-api/: Management API and resource capability only.host-callback/: minimal plugin resource that demonstrates host callbacks.host-callback-auth-files/: Go-only plugin resource that calls host auth file callbacks.host-model-callback/: Go-only plugin resource that calls the host model execution callbacks.
Most standard capability examples contain go/, c/, and rust/ subdirectories. Specialized examples may provide only the implementation language they need.
Codex Service Tier
codex-service-tier declares the request normalization capability. When fast is true, it sets service_tier to priority for requests where req.ToFormat is codex and req.Model is gpt-5.5.
plugins:
configs:
codex-service-tier:
enabled: true
priority: 1
fast: false
Request Lifecycle
request-lifecycle combines request_interceptor with request_lifecycle_plugin. It acquires a concurrency slot before auth selection, can return a custom 403 or 429 response without contacting an upstream model, and releases admitted slots from request.complete on success, failure, rejection, or cancellation.
plugins:
configs:
request-lifecycle:
enabled: true
priority: 100
max_concurrency: 2
reject_keyword: "blocked"
See request-lifecycle/README.md for build instructions and lifecycle semantics.
Host Auth Files Callback
host-callback-auth-files declares the Management API capability and exposes a browser resource named Host Auth Files. The resource demonstrates host.auth.list, host.auth.get (physical JSON file), host.auth.get_runtime, and host.auth.save.
plugins:
configs:
host-callback-auth-files:
enabled: true
priority: 1
See host-callback-auth-files/README.md for URL examples.
Host Model Callback
host-model-callback declares the Management API capability and exposes a browser resource named Host Model Callback. The resource calls host.model.execute for non-streaming requests and host.model.execute_stream plus host.model.stream_read for streaming requests. It demonstrates explicit stream close with host.model.stream_close and an implicit_close=true option for RPC-scope host cleanup.
When the resource forwards its host_callback_id, CPA identifies the plugin that initiated the host model callback and skips that same plugin's interceptors for the nested execution. This makes host model callbacks non-recursive for the caller while allowing other plugins to intercept the nested request.
plugins:
configs:
host-model-callback:
enabled: true
priority: 1
The default example model is gpt-5.5, but the request succeeds only when the current CPA model and auth configuration can route that model.
Host HTTP Callback Cancellation
Shared-library plugins can make host.http.do and host.http.do_stream cancelable before a response or stream_id is returned:
- Call
host.http.operation_openwith the same livehost_callback_idthat the HTTP request will use, if any. The callback ID must belong to the calling plugin. The response contains a host-generatedoperation_idscoped to that plugin. - Include that
operation_idat the top level of thehost.http.doorhost.http.do_streamrequest, and forward the samehost_callback_idused when opening the operation. - Call
host.http.cancelwith the sameoperation_idto cancel the in-flight request. For an opened stream, cancellation also closes its host-side stream entry;host.http.stream_closeremains supported.
Each operation ID can be claimed by one HTTP callback. HTTP stream IDs are also scoped to the owning plugin. Cancel an opened operation if it will not be used, and close streams when finished. This API provides explicit cancellation, not a host-managed per-call timeout; a plugin that wants a deadline must arrange its own cancellation call.
Scheduler
scheduler declares the scheduler capability. It can select a configured auth ID from the candidate list, delegate to the built-in fill-first or round-robin scheduler, or reject picks when deny is true.
plugins:
configs:
scheduler:
enabled: true
priority: 1
auth_id: ""
delegate: ""
deny: false
auth_id selects a matching candidate when delegate is empty. delegate accepts "", fill-first, or round-robin; other non-empty values leave the pick unhandled. deny returns a scheduler error.
Plugin Executor Error Handling and HTTP Status
When a plugin executor encounters an upstream failure (such as 401 Unauthorized for invalid credentials, 403 Forbidden for model permission/quota limits, or 429 Too Many Requests for rate limits), it should report the HTTP status code in the error envelope:
- In the JSON RPC error envelope, set the
http_statusfield inside theerrorobject (pluginabi.Error.HTTPStatus). - If
http_statusis omitted or0, CPA defaults to returning HTTP500 Internal Server Error(server_error/internal_server_error), which clients typically treat as a temporary gateway outage and retry with backoff. - When
http_statusis set, CPA maps the status into client-visible error responses:401-> HTTP 401 withtype: "authentication_error",code: "invalid_api_key"403-> HTTP 403 withtype: "permission_error",code: "insufficient_quota"429-> HTTP 429 withtype: "rate_limit_error",code: "rate_limit_exceeded"404-> HTTP 404 withtype: "invalid_request_error",code: "model_not_found">=500-> HTTP 5xx withtype: "server_error",code: "internal_server_error"
- Important: Both non-streaming (
executor.execute) and streaming (executor.execute_stream) call sites must includehttp_statusso failures are classified consistently. - Because native dynamic library plugins communicate across the C ABI via serialized JSON buffers, the status code must be encoded in the serialized JSON envelope (e.g. using
pluginabi.NewErrorEnvelopeor a custom envelope struct with anhttp_statusfield). Returning an unmarshaled Go error does not traverse the C ABI boundary.
Go plugins can import github.com/router-for-me/CLIProxyAPI/v7/sdk/pluginabi and use pluginabi.NewErrorEnvelope(code, message, httpStatus):
// Recommended: construct an error envelope directly using sdk/pluginabi
rawEnvelope, errMarshal := pluginabi.NewErrorEnvelope("insufficient_quota", "plan limit reached", http.StatusForbidden)
Alternatively, plugins defining a custom envelope struct can declare an HTTPStatus field (json:"http_status,omitempty"):
type envelopeError struct {
Code string `json:"code"`
Message string `json:"message"`
HTTPStatus int `json:"http_status,omitempty"`
}
func errorEnvelope(code, message string, httpStatus ...int) []byte {
status := 0
if len(httpStatus) > 0 {
status = httpStatus[0]
}
raw, _ := json.Marshal(envelope{
OK: false,
Error: &envelopeError{
Code: code,
Message: message,
HTTPStatus: status,
},
})
return raw
}
Build All Examples
make -C examples/plugin list
make -C examples/plugin build
Artifacts are written to examples/plugin/bin.
Notes
protocol-format uses a minimal executor because format declarations belong to executor capabilities.
host-callback uses a minimal plugin resource because host callbacks are invoked from plugin methods and are not standalone capabilities.
Menu resources returned by management.register through the resources field are exposed by CPA under /v0/resource/plugins/<pluginID>/.... Authenticated plugin Management API routes remain under /v0/management/....