- Add `Error.Error`, `Error.StatusCode`, `NewError`, and `NewErrorEnvelope` helpers in `sdk/pluginabi`. - Support extracting and forwarding HTTP status codes in Go plugin executor examples and generator script. - Document plugin executor error handling and client HTTP status mapping conventions. Closes: #5809
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.
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/....