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.
71 lines
2.1 KiB
Python
71 lines
2.1 KiB
Python
"""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())
|