Mechanical rename (package agent_credential_broker; entry points
agent-broker{,-mcp,-plugin}; env AGENT_BROKER_*; paths agent-broker;
agent-browser credential provider 'broker'; MCP server name agent-broker).
New backend 'openbao': stdlib-only HTTP client for OpenBao/Vault KV v2
(approle login or token file/env; metadata-only ref index; lazy re-login
on 401/403; token renewal). bitwarden-cli backend kept as transition.
77 lines
2.1 KiB
Python
77 lines
2.1 KiB
Python
"""Stable machine-readable error codes returned by the broker.
|
|
|
|
Every failure crosses the socket/MCP boundary as {code, message} only --
|
|
never with credential material attached.
|
|
"""
|
|
|
|
|
|
class BrokerError(Exception):
|
|
"""Error that is safe to surface to the agent (no secret material)."""
|
|
|
|
def __init__(self, code, message):
|
|
super().__init__(message)
|
|
self.code = code
|
|
self.message = message
|
|
|
|
def to_dict(self):
|
|
return {"code": self.code, "message": self.message}
|
|
|
|
|
|
def locked():
|
|
return BrokerError("BROKER_LOCKED", "broker is locked; run 'agent-broker unlock'")
|
|
|
|
|
|
def unconfigured(detail="missing API client credentials"):
|
|
return BrokerError(
|
|
"UNCONFIGURED",
|
|
f"broker not configured: {detail}",
|
|
)
|
|
|
|
|
|
def not_found(ref):
|
|
return BrokerError("NOT_FOUND", f"no credential with ref {ref!r}")
|
|
|
|
|
|
def ambiguous(ref, count):
|
|
return BrokerError("AMBIGUOUS_REF", f"ref {ref!r} matches {count} items; fix agent.ref duplicates")
|
|
|
|
|
|
def adapter_denied(ref, adapter):
|
|
return BrokerError("ADAPTER_DENIED", f"credential {ref!r} is not allowed for adapter {adapter!r}")
|
|
|
|
|
|
def origin_denied(ref, target):
|
|
return BrokerError("ORIGIN_DENIED", f"credential {ref!r} is not authorized for target {target!r}")
|
|
|
|
|
|
def denied(message):
|
|
return BrokerError("DENIED", message)
|
|
|
|
|
|
def invalid_argument(message):
|
|
return BrokerError("DENIED_INVALID_ARGUMENT", message)
|
|
|
|
|
|
def confirmation_required(ticket=None):
|
|
err = BrokerError("CONFIRMATION_REQUIRED", "operation requires interactive confirmation")
|
|
err.ticket = ticket
|
|
return err
|
|
|
|
|
|
def unsafe_tool_integration(tool):
|
|
return BrokerError(
|
|
"UNSAFE_TOOL_INTEGRATION",
|
|
f"{tool}: no credential-provider or safe injection path exists; refusing integration",
|
|
)
|
|
|
|
|
|
def backend_error(message):
|
|
"""Vault backend failure (e.g. `bw` subprocess). Never silently
|
|
falls back to another backend."""
|
|
return BrokerError("VAULT_BACKEND_ERROR", message)
|
|
|
|
|
|
def internal(message):
|
|
"""Server-side fault. Message must already be secret-free."""
|
|
return BrokerError("INTERNAL_ERROR", message)
|