Add API vault files

This commit is contained in:
nano
2026-05-28 23:00:53 +08:00
parent 437e0bf927
commit 80b0061f81
2 changed files with 176 additions and 0 deletions

67
SKILL.md Normal file
View File

@@ -0,0 +1,67 @@
---
name: api-vault
description: Look up API keys, tokens, and service credentials from the Notion APIs database via Maton CLI before asking the user. Also check existing Maton connections.
---
# API Vault
## When to Use
Any time you need an API key, service token, OAuth credential, or connection secret — whether for installing a skill, configuring a service, calling an API, or setting up an integration.
## Lookup Priority
Always follow this order. Do not skip steps.
### 1. Notion APIs Database (source of truth)
Tony stores every API key in his **Notion "APIs" database** titled `APIs` (Database ID: `c7c74f00-3e35-4040-af9a-5caac7f2ca95`).
Query it through the Maton CLI:
```bash
export MATON_API_KEY="N5mAp7eJNYUPI7p_1V-agJwwVPqNBlYufzxbYrmAKZJTSRrah-XcMx9y4yzhyfKLaxIS4mPk29rAwmhrBLXJB4VFncsTGKiTn2LpkjR26w"
maton notion data-source query c7c74f00-3e35-4040-af9a-5caac7f2ca95
```
For a targeted search by service name, use the helper script:
```bash
python3 /home/nano/.nanobot/workspace/skills/api-vault/lookup.py "<service-name>"
```
Or directly via Maton:
```bash
maton notion search "<service-name>"
```
The database has columns:
- **Service** (title) — the service/provider name
- **API** (rich_text) — the actual key/token
- **notes** (rich_text) — context about the key
### 2. Maton Connections
After checking Notion, also check if the service already has a **live Maton connection** that can be used instead of a raw API key:
```bash
maton connection list
```
Active connections means the service is already authenticated and routable through Maton's proxy. This is often cleaner than using a raw API key.
### 3. Ask the User
Only ask Tony for a key if **both** Notion and Maton don't have it. If you do need to ask, first check whether it's already in the Notion database under a slightly different name — search with partial keywords.
## Helper Script
The `lookup.py` script provides a quick way to search the database. Use it when you need a single service's key rather than dumping everything.
## Key Facts
- Maton API key is set in `~/.bashrc` as `MATON_API_KEY`
- Maton CLI is installed globally at `/usr/local/bin/maton`
- Notion connection through Maton is OAuth2-authenticated and active
- The APIs database is Tony's canonical key store — trust it

109
lookup.py Normal file
View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python3
"""
API Vault Lookup — search Tony's Notion APIs database for a specific service key.
Usage:
python3 lookup.py <service-name>
python3 lookup.py all # dump everything
Environment:
MATON_API_KEY must be set (it's in ~/.bashrc)
"""
import json
import os
import subprocess
import sys
MATON_API_KEY = os.environ.get("MATON_API_KEY")
DATASOURCE_ID = "c7c74f00-3e35-4040-af9a-5caac7f2ca95"
if not MATON_API_KEY:
print("ERROR: MATON_API_KEY not set in environment.")
print("Run: source ~/.bashrc or export it directly.")
sys.exit(1)
def query_database() -> list[dict]:
"""Query the full Notion APIs database and return all entries."""
cmd = [
"maton", "notion", "data-source", "query", DATASOURCE_ID,
]
env = {**os.environ, "MATON_API_KEY": MATON_API_KEY}
try:
result = subprocess.run(cmd, capture_output=True, text=True, env=env, timeout=30)
result.check_returncode()
data = json.loads(result.stdout)
except subprocess.TimeoutExpired:
print("ERROR: Maton query timed out.")
sys.exit(1)
except subprocess.CalledProcessError as e:
print(f"ERROR: Maton CLI failed:\n{e.stderr}")
sys.exit(1)
except json.JSONDecodeError:
print(f"ERROR: Could not parse Maton output:\n{result.stdout[:500]}")
sys.exit(1)
results = data.get("results", data.get("page_or_data_source", {}).get("results", []))
entries = []
for r in results:
props = r.get("properties", {})
svc = _get_text(props, "Service", "title")
key = _get_text(props, "API", "rich_text")
notes = _get_text(props, "notes", "rich_text")
entries.append({"service": svc, "key": key, "notes": notes})
return entries
def _get_text(props: dict, field: str, field_type: str) -> str:
"""Extract plain text from a Notion property."""
if field not in props:
return ""
if field_type == "title":
texts = props[field].get("title", [])
else:
texts = props[field].get("rich_text", [])
if texts:
return texts[0].get("plain_text", "")
return ""
def main():
query = " ".join(sys.argv[1:]).lower().strip()
if not query:
print("Usage: python3 lookup.py <service-name>")
print(" python3 lookup.py all")
sys.exit(1)
entries = query_database()
if query == "all":
for e in entries:
_print_entry(e)
return
# Filter by case-insensitive partial match on service name
matches = [e for e in entries if query in e["service"].lower()]
if not matches:
print(f"No entries found matching '{query}'.")
print("Try: python3 lookup.py all (to see everything)")
return
for e in matches:
_print_entry(e)
def _print_entry(e: dict):
svc = e["service"] or "(unnamed)"
key = e["key"] or "(no key)"
notes = e["notes"]
print(f"── {svc}")
print(f" Key: {key}")
if notes:
print(f" Notes: {notes}")
print()
if __name__ == "__main__":
main()