diff --git a/core/framework/tools/queen_lifecycle_tools.py b/core/framework/tools/queen_lifecycle_tools.py index 0549be61ff..2620461692 100644 --- a/core/framework/tools/queen_lifecycle_tools.py +++ b/core/framework/tools/queen_lifecycle_tools.py @@ -1312,6 +1312,15 @@ async def list_credentials(credential_id: str = "") -> str: Returns credential IDs, aliases, status, and identity metadata. Never returns secret values. Optionally filter by credential_id. """ + # Load shell config vars into os.environ — same first step as check-agent. + # Ensures keys set in ~/.zshrc/~/.bashrc are visible to is_available() checks. + try: + from framework.credentials.validation import ensure_credential_key_env + + ensure_credential_key_env() + except Exception: + pass + try: # Primary: CredentialStoreAdapter sees both Aden OAuth and local accounts from aden_tools.credentials import CredentialStoreAdapter @@ -1319,13 +1328,24 @@ async def list_credentials(credential_id: str = "") -> str: store = CredentialStoreAdapter.default() all_accounts = store.get_all_account_info() - # Filter by credential_id / provider if requested + # Filter by credential_id / provider if requested. + # A spec name like "gmail_oauth" maps to provider "google" via + # credential_id field — resolve that alias before filtering. if credential_id: + try: + from aden_tools.credentials import CREDENTIAL_SPECS + + spec = CREDENTIAL_SPECS.get(credential_id) + resolved_provider = ( + (spec.credential_id or credential_id) if spec else credential_id + ) + except Exception: + resolved_provider = credential_id all_accounts = [ a for a in all_accounts if a.get("credential_id", "").startswith(credential_id) - or a.get("provider", "") == credential_id + or a.get("provider", "") in (credential_id, resolved_provider) ] return json.dumps( @@ -1342,13 +1362,43 @@ async def list_credentials(credential_id: str = "") -> str: # Fallback: local encrypted store only try: + from framework.credentials.local.models import LocalAccountInfo from framework.credentials.local.registry import LocalCredentialRegistry + from framework.credentials.storage import EncryptedFileStorage registry = LocalCredentialRegistry.default() accounts = registry.list_accounts( credential_id=credential_id or None, ) + # Also include flat-file credentials saved by the GUI (no "/" separator). + # LocalCredentialRegistry.list_accounts() skips these — read them directly. + seen_cred_ids = {info.credential_id for info in accounts} + storage = EncryptedFileStorage() + for storage_id in storage.list_all(): + if "/" in storage_id: + continue # already handled by LocalCredentialRegistry above + if credential_id and storage_id != credential_id: + continue + if storage_id in seen_cred_ids: + continue + try: + cred_obj = storage.load(storage_id) + except Exception: + continue + if cred_obj is None: + continue + accounts.append( + LocalAccountInfo( + credential_id=storage_id, + alias="default", + status="unknown", + identity=cred_obj.identity, + last_validated=cred_obj.last_refreshed, + created_at=cred_obj.created_at, + ) + ) + credentials = [] for info in accounts: entry: dict[str, Any] = {