agent-credential-broker: fork of vaultwarden-agent-broker + OpenBao backend

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.
This commit is contained in:
me committed 2026-10-14 13:27:33 -06:00
commit be87900794
35 files changed
+5003

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
"""agent-browser ``credential.read`` plugin (agent-browser.plugin.v1).
agent-browser writes ONE JSON envelope on stdin::
{"protocol": "agent-browser.plugin.v1",
"type": "credential.resolve",
"capability": "credential.read",
"request": {"profileName": "...", "itemRef": "...", "url": "..."}}
and expects one JSON object on stdout::
{"protocol": "agent-browser.plugin.v1", "success": true,
"credential": {"username": ..., "password": ..., "url": ...,
"usernameSelector": ..., "passwordSelector": ...,
"submitSelector": ...}}
The plugin is a thin proxy: it forwards (itemRef, url) to the broker over
the unix socket and relays the credential. The broker enforces the
item's adapter policy and origin validation before releasing anything.
Secret values go agent-browser-stdin -> plugin -> agent-browser; the
agent model never sees them.
"""
from __future__ import annotations
import json
import sys
from .errors import BrokerError
from .server import call
PROTOCOL = "agent-browser.plugin.v1"
def _fail(message):
sys.stdout.write(json.dumps({
"protocol": PROTOCOL, "success": False, "error": str(message)[:300],
}) + "\n")
def main():
try:
raw = sys.stdin.read(64 * 1024)
envelope = json.loads(raw)
except Exception:
_fail("bad plugin envelope")
return 1
if (envelope.get("protocol") != PROTOCOL
or envelope.get("type") != "credential.resolve"):
_fail("unsupported plugin request")
return 1
req = envelope.get("request") or {}
item_ref = req.get("itemRef")
url = req.get("url")
if not item_ref:
_fail("missing itemRef")
return 1
try:
cred = call("resolve", {"ref": item_ref, "url": url})
except BrokerError as e:
_fail(f"{e.code}: {e.message}")
return 0 # a clean denial is still a protocol-level response
sys.stdout.write(json.dumps({
"protocol": PROTOCOL, "success": True, "credential": cred,
}) + "\n")
return 0
if __name__ == "__main__":
sys.exit(main())