Hermes Agent commited on
Commit
21859b2
·
1 Parent(s): ee68a9d

feat(oauth): add OAuth paste-credentials, keys capture, and direct SQLite vault sync

Browse files
gateway/credentials_sync.py CHANGED
@@ -122,8 +122,54 @@ def sync_secret_to_huggingface(secret_name: str, secret_val: str):
122
 
123
  def handle_captured_credential(provider_hint: str, api_key: str):
124
  """High-level dispatch to save locally and attempt HF cloud sync."""
125
- if not api_key or len(api_key.strip()) < 5:
126
  return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  env_name = normalize_provider_name(provider_hint)
128
- save_to_local_vault(env_name, api_key.strip())
129
- sync_secret_to_huggingface(env_name, api_key.strip())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  def handle_captured_credential(provider_hint: str, api_key: str):
124
  """High-level dispatch to save locally and attempt HF cloud sync."""
125
+ if not api_key:
126
  return
127
+ api_key_str = str(api_key).strip()
128
+ if len(api_key_str) < 5:
129
+ return
130
+
131
+ # Check if this is a structured JSON string (like Antigravity OAuth object)
132
+ if api_key_str.startswith("{") and api_key_str.endswith("}"):
133
+ try:
134
+ parsed = json.loads(api_key_str)
135
+ if isinstance(parsed, dict):
136
+ # Extract refresh token or access token if available
137
+ rt = parsed.get("refresh_token") or parsed.get("token") or parsed.get("access_token")
138
+ if rt and isinstance(rt, str):
139
+ env_name = normalize_provider_name(provider_hint)
140
+ save_to_local_vault(env_name, rt)
141
+ sync_secret_to_huggingface(env_name, rt)
142
+ except Exception:
143
+ pass
144
+
145
  env_name = normalize_provider_name(provider_hint)
146
+ save_to_local_vault(env_name, api_key_str)
147
+ sync_secret_to_huggingface(env_name, api_key_str)
148
+
149
+ def sync_sqlite_credentials_to_vault(db_path="/root/.omniroute/storage.sqlite"):
150
+ """Scans OmniRoute SQLite database for any newly configured providers and syncs them."""
151
+ import sqlite3
152
+ if not os.path.exists(db_path):
153
+ return
154
+ try:
155
+ conn = sqlite3.connect(db_path)
156
+ cursor = conn.cursor()
157
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
158
+ tables = [t[0] for t in cursor.fetchall()]
159
+
160
+ # Check connections table
161
+ for tbl in ["connections", "provider_connections", "keys"]:
162
+ if tbl in tables:
163
+ cursor.execute(f"SELECT * FROM {tbl}")
164
+ rows = cursor.fetchall()
165
+ col_names = [d[0].lower() for d in cursor.description]
166
+ for r in rows:
167
+ row_dict = dict(zip(col_names, r))
168
+ provider = str(row_dict.get("provider_id") or row_dict.get("provider") or row_dict.get("name") or "")
169
+ key_val = str(row_dict.get("api_key") or row_dict.get("key") or row_dict.get("token") or row_dict.get("credentials") or "")
170
+ if provider and key_val and len(key_val) > 5 and not key_val.startswith("enc:"):
171
+ handle_captured_credential(provider, key_val)
172
+ conn.close()
173
+ except Exception as e:
174
+ logger.debug(f"[VAULT] SQLite scan skipped: {e}")
175
+
gateway/omniroute.py CHANGED
@@ -146,7 +146,7 @@ async def omniroute_dashboard(request: Request, path: str = ""):
146
 
147
  from gateway.credentials_sync import handle_captured_credential
148
 
149
- async def try_capture_credentials(request: Request):
150
  """Inspects write payloads to automatically capture and sync API keys."""
151
  if request.method in ("POST", "PUT", "PATCH"):
152
  try:
@@ -154,8 +154,13 @@ async def try_capture_credentials(request: Request):
154
  if body:
155
  data = json.loads(body.decode("utf-8", "ignore"))
156
  if isinstance(data, dict):
157
- api_key = data.get("apiKey") or data.get("api_key") or data.get("key") or data.get("token")
158
- provider_hint = data.get("provider") or data.get("providerId") or data.get("name") or data.get("id") or ""
 
 
 
 
 
159
  if api_key and isinstance(api_key, str) and len(api_key.strip()) > 5:
160
  handle_captured_credential(str(provider_hint), api_key.strip())
161
  except Exception:
@@ -165,15 +170,22 @@ async def try_capture_credentials(request: Request):
165
  @router.api_route("/api/connections/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
166
  @router.api_route("/api/connection/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
167
  async def omniroute_connections(request: Request, path: str = ""):
168
- await try_capture_credentials(request)
169
  target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/connections/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/connections"
170
  return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
171
 
 
 
 
 
 
 
 
172
  @router.api_route("/api/providers", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
173
  @router.api_route("/api/providers/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
174
  @router.api_route("/api/v1/providers/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
175
  async def omniroute_providers(request: Request, path: str = ""):
176
- await try_capture_credentials(request)
177
  target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/providers/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/providers"
178
  try:
179
  res = await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
@@ -217,6 +229,7 @@ async def omniroute_auth_login(request: Request, path: str = "login"):
217
  @router.api_route("/api/oauth", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
218
  @router.api_route("/api/oauth/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
219
  async def omniroute_oauth(request: Request, path: str = ""):
 
220
  target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/oauth/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/oauth"
221
  return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
222
 
 
146
 
147
  from gateway.credentials_sync import handle_captured_credential
148
 
149
+ async def try_capture_credentials(request: Request, path_hint: str = ""):
150
  """Inspects write payloads to automatically capture and sync API keys."""
151
  if request.method in ("POST", "PUT", "PATCH"):
152
  try:
 
154
  if body:
155
  data = json.loads(body.decode("utf-8", "ignore"))
156
  if isinstance(data, dict):
157
+ api_key = (
158
+ data.get("apiKey") or data.get("api_key") or
159
+ data.get("key") or data.get("token") or
160
+ data.get("credentials") or data.get("refreshToken") or
161
+ data.get("code") or data.get("raw")
162
+ )
163
+ provider_hint = data.get("provider") or data.get("providerId") or data.get("name") or data.get("id") or path_hint or ""
164
  if api_key and isinstance(api_key, str) and len(api_key.strip()) > 5:
165
  handle_captured_credential(str(provider_hint), api_key.strip())
166
  except Exception:
 
170
  @router.api_route("/api/connections/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
171
  @router.api_route("/api/connection/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
172
  async def omniroute_connections(request: Request, path: str = ""):
173
+ await try_capture_credentials(request, path_hint=path)
174
  target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/connections/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/connections"
175
  return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
176
 
177
+ @router.api_route("/api/keys", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
178
+ @router.api_route("/api/keys/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
179
+ async def omniroute_keys(request: Request, path: str = ""):
180
+ await try_capture_credentials(request, path_hint=path)
181
+ target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/keys/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/keys"
182
+ return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
183
+
184
  @router.api_route("/api/providers", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
185
  @router.api_route("/api/providers/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
186
  @router.api_route("/api/v1/providers/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
187
  async def omniroute_providers(request: Request, path: str = ""):
188
+ await try_capture_credentials(request, path_hint=path)
189
  target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/providers/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/providers"
190
  try:
191
  res = await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
 
229
  @router.api_route("/api/oauth", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
230
  @router.api_route("/api/oauth/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
231
  async def omniroute_oauth(request: Request, path: str = ""):
232
+ await try_capture_credentials(request, path_hint=path)
233
  target = f"http://127.0.0.1:{OMNIROUTE_PORT}/api/oauth/{path}" if path else f"http://127.0.0.1:{OMNIROUTE_PORT}/api/oauth"
234
  return await handle_omniroute_proxy(target, request, default_prefix="/omniroute")
235
 
health_doctor.py CHANGED
@@ -101,6 +101,11 @@ def run_health_check_cycle():
101
 
102
  check_disk_space()
103
  purge_old_backups()
 
 
 
 
 
104
 
105
 
106
  def main():
 
101
 
102
  check_disk_space()
103
  purge_old_backups()
104
+ try:
105
+ from gateway.credentials_sync import sync_sqlite_credentials_to_vault
106
+ sync_sqlite_credentials_to_vault("/root/.omniroute/storage.sqlite")
107
+ except Exception:
108
+ pass
109
 
110
 
111
  def main():