Merge pull request #5766 from aden-hive/fix/credential-modal-delete

(micro-fix): Fix/credential modal delete
This commit is contained in:
Bryan @ Aden
2026-03-04 04:38:23 +00:00
committed by GitHub
4 changed files with 43 additions and 28 deletions
+21 -10
View File
@@ -621,6 +621,7 @@ class AgentRunner:
requires_account_selection: bool = False,
configure_for_account: Callable | None = None,
list_accounts: Callable | None = None,
credential_store: Any | None = None,
):
"""
Initialize the runner (use AgentRunner.load() instead).
@@ -640,6 +641,7 @@ class AgentRunner:
requires_account_selection: If True, TUI shows account picker before starting.
configure_for_account: Callback(runner, account_dict) to scope tools after selection.
list_accounts: Callback() -> list[dict] to fetch available accounts.
credential_store: Optional shared CredentialStore (avoids creating redundant stores).
"""
self.agent_path = agent_path
self.graph = graph
@@ -653,6 +655,7 @@ class AgentRunner:
self.requires_account_selection = requires_account_selection
self._configure_for_account = configure_for_account
self._list_accounts = list_accounts
self._credential_store = credential_store
# Set up storage
if storage_path:
@@ -750,6 +753,7 @@ class AgentRunner:
model: str | None = None,
interactive: bool = True,
skip_credential_validation: bool | None = None,
credential_store: Any | None = None,
) -> "AgentRunner":
"""
Load an agent from an export folder.
@@ -767,6 +771,7 @@ class AgentRunner:
Set to False from TUI callers that handle setup via their own UI.
skip_credential_validation: If True, skip credential checks at load time.
When None (default), uses the agent module's setting.
credential_store: Optional shared CredentialStore (avoids creating redundant stores).
Returns:
AgentRunner instance ready to run
@@ -856,6 +861,7 @@ class AgentRunner:
requires_account_selection=needs_acct,
configure_for_account=configure_fn,
list_accounts=list_accts_fn,
credential_store=credential_store,
)
# Fallback: load from agent.json (legacy JSON-based agents)
@@ -875,6 +881,7 @@ class AgentRunner:
model=model,
interactive=interactive,
skip_credential_validation=skip_credential_validation or False,
credential_store=credential_store,
)
def register_tool(
@@ -1108,11 +1115,10 @@ class AgentRunner:
# Auto-register GCU MCP server if tools aren't loaded yet
gcu_tool_names = self._tool_registry.get_server_tool_names(GCU_SERVER_NAME)
if not gcu_tool_names:
# Resolve relative cwd against agent path
# Resolve cwd to repo-level tools/ (not relative to agent_path)
gcu_config = dict(GCU_MCP_SERVER_CONFIG)
cwd = gcu_config.get("cwd")
if cwd and not Path(cwd).is_absolute():
gcu_config["cwd"] = str((self.agent_path / cwd).resolve())
_repo_root = Path(__file__).resolve().parent.parent.parent.parent
gcu_config["cwd"] = str(_repo_root / "tools")
self._tool_registry.register_mcp_server(gcu_config)
gcu_tool_names = self._tool_registry.get_server_tool_names(GCU_SERVER_NAME)
@@ -1132,10 +1138,10 @@ class AgentRunner:
files_tool_names = self._tool_registry.get_server_tool_names(FILES_MCP_SERVER_NAME)
if not files_tool_names:
# Resolve cwd to repo-level tools/ (not relative to agent_path)
files_config = dict(FILES_MCP_SERVER_CONFIG)
cwd = files_config.get("cwd")
if cwd and not Path(cwd).is_absolute():
files_config["cwd"] = str((self.agent_path / cwd).resolve())
_repo_root = Path(__file__).resolve().parent.parent.parent.parent
files_config["cwd"] = str(_repo_root / "tools")
self._tool_registry.register_mcp_server(files_config)
files_tool_names = self._tool_registry.get_server_tool_names(FILES_MCP_SERVER_NAME)
@@ -1158,7 +1164,10 @@ class AgentRunner:
try:
from aden_tools.credentials.store_adapter import CredentialStoreAdapter
adapter = CredentialStoreAdapter.default()
if self._credential_store is not None:
adapter = CredentialStoreAdapter(store=self._credential_store)
else:
adapter = CredentialStoreAdapter.default()
accounts_data = adapter.get_all_account_info()
tool_provider_map = adapter.get_tool_provider_map()
if accounts_data:
@@ -1229,9 +1238,11 @@ class AgentRunner:
return None
try:
from framework.credentials import CredentialStore
store = self._credential_store
if store is None:
from framework.credentials import CredentialStore
store = CredentialStore.with_encrypted_storage()
store = CredentialStore.with_encrypted_storage()
return store.get(cred_id)
except Exception:
return None
+6 -6
View File
@@ -176,10 +176,7 @@ def create_app(model: str | None = None) -> web.Application:
"""
app = web.Application(middlewares=[cors_middleware, error_middleware])
# Store manager on app for handlers
app["manager"] = SessionManager(model=model)
# Initialize credential store
# Initialize credential store (before SessionManager so it can be shared)
from framework.credentials.store import CredentialStore
try:
@@ -200,10 +197,13 @@ def create_app(model: str | None = None) -> web.Application:
except Exception as exc:
logger.warning("Could not auto-persist HIVE_CREDENTIAL_KEY: %s", exc)
app["credential_store"] = CredentialStore.with_aden_sync()
credential_store = CredentialStore.with_aden_sync()
except Exception:
logger.debug("Encrypted credential store unavailable, using in-memory fallback")
app["credential_store"] = CredentialStore.for_testing({})
credential_store = CredentialStore.for_testing({})
app["credential_store"] = credential_store
app["manager"] = SessionManager(model=model, credential_store=credential_store)
# Register shutdown hook
app.on_shutdown.append(_on_shutdown)
+3 -1
View File
@@ -52,10 +52,11 @@ class SessionManager:
(blocking I/O) then started on the event loop.
"""
def __init__(self, model: str | None = None) -> None:
def __init__(self, model: str | None = None, credential_store=None) -> None:
self._sessions: dict[str, Session] = {}
self._loading: set[str] = set()
self._model = model
self._credential_store = credential_store
self._lock = asyncio.Lock()
# ------------------------------------------------------------------
@@ -217,6 +218,7 @@ class SessionManager:
model=resolved_model,
interactive=False,
skip_credential_validation=True,
credential_store=self._credential_store,
),
)
@@ -400,17 +400,19 @@ export default function CredentialsModal({
<Pencil className="w-3 h-3" />
</button>
)}
<button
onClick={() => {
setDeletingId(deletingId === row.id ? null : row.id);
if (editingId) { setEditingId(null); setInputValue(""); }
}}
disabled={saving}
className="p-1.5 rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors"
title="Delete credential"
>
<Trash2 className="w-3 h-3" />
</button>
{!(row.adenSupported && row.id !== "aden_api_key") && (
<button
onClick={() => {
setDeletingId(deletingId === row.id ? null : row.id);
if (editingId) { setEditingId(null); setInputValue(""); }
}}
disabled={saving}
className="p-1.5 rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors"
title="Delete credential"
>
<Trash2 className="w-3 h-3" />
</button>
)}
</div>
) : row.adenSupported && !adenPlatformConnected && row.id !== "aden_api_key" ? (
<span className="text-[11px] text-muted-foreground italic flex-shrink-0">