fix(system): S61-S62 — API cleanup, drone path routing, naming checker, .backup→.recovery rename (#142)

- API: 18-item P0/P1/P2 cleanup (debug prints→logger, help gate fix, URL injection fix, key masking, unused imports)
- Drone: passport walk-up replaces hardcoded src/aipass/<branch> pattern, 13 naming bypasses removed
- Seedgo: naming checker fixed (__dunder__ skip + column-0 scope detection) — eliminates 71 false positives system-wide
- Spawn: .backup→.recovery rename (pre-change snapshots no longer conflict with backup branch namespace)
- .gitignore: .recovery/ added
- HERALD.md: Updated through S62 (backup audit, .recovery rename, night shift launch)
- README.md: Stats updated (141 PRs, 2900+ tests)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
AIPass
2026-03-29 01:55:21 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent ae459853f0
commit 6772cacce2
27 changed files with 245 additions and 212 deletions
+5
View File
@@ -74,6 +74,11 @@
"file": "apps/modules/google_client.py",
"standard": "unused_function",
"reason": "Public API exports for cross-branch consumption (get_drive_service, authenticate_google, validate_google, reauth_google). Tested in test_google_client.py. Backup migration pending."
},
{
"file": "apps/modules/openrouter_client.py",
"standard": "modules",
"reason": "make_call() is a CLI orchestrator — parses args then delegates to client.get_response(). All business logic lives in handlers/openrouter/client.py."
}
],
"notes": {
+8 -1
View File
@@ -157,8 +157,15 @@ def print_help():
table.add_row("reauth google", "Re-authenticate Google OAuth2")
table.add_row("test", "Test OpenRouter connection status")
table.add_row("models", "List available models from provider")
table.add_row("status", "Check OpenRouter client status")
table.add_row("call", "Make API call to model")
table.add_row("list-providers", "List available API providers")
table.add_row("init", "Initialize .env template")
table.add_row("track", "Track API usage metrics")
table.add_row("stats", "Display API usage statistics")
table.add_row("session", "Show session usage data")
table.add_row("caller-usage", "Show usage by caller module")
table.add_row("cleanup", "Clean up old usage data")
console.print(table)
console.print()
@@ -193,7 +200,7 @@ def print_help():
console.print("─" * 70)
console.print()
console.print("[dim]Commands: get-key, validate, test, models, track, stats, help, --help[/dim]")
console.print("[dim]Commands: get-key, validate, test, models, status, call, list-providers, init, track, stats, session, caller-usage, cleanup[/dim]")
console.print()
+3 -3
View File
@@ -42,9 +42,9 @@ def _guard_branch_access():
import os
if os.environ.get("AIPASS_DEBUG_GUARD"):
import sys
print(f"[GUARD DEBUG] caller_file = {caller_file}", file=sys.stderr)
print(f"[GUARD DEBUG] import_line = {import_line}", file=sys.stderr)
from aipass.prax import logger as _guard_logger
_guard_logger.info(f"[GUARD] caller_file = {caller_file}")
_guard_logger.info(f"[GUARD] import_line = {import_line}")
if caller_file is None:
stack = inspect.stack()
+1 -2
View File
@@ -17,10 +17,9 @@ Functions:
# Infrastructure
from pathlib import Path
import sys
# Standard library
from typing import Optional, Dict
from typing import Optional
# Logging
from aipass.prax import logger
@@ -10,7 +10,6 @@ import json
from pathlib import Path
from datetime import datetime
from typing import Dict, Any, Optional
import sys
import inspect
# Logging
@@ -121,7 +120,7 @@ def ensure_json_exists(module_name: str, json_type: str) -> bool:
if validate_json_structure(data, json_type):
return True
else:
pass # Corrupted - regenerating
logger.warning(f"Corrupted JSON structure at {json_path}, regenerating")
except Exception as e:
logger.warning(f"Unreadable JSON at {json_path}, regenerating: {e}")
@@ -11,7 +11,7 @@
OpenRouter Client Handler
Business logic for OpenRouter API client creation and request execution.
Extracted from archive.temp/openrouter.py following AIPASS standards.
OpenRouter API client creation and request execution.
Functions:
- get_response() - Main API call with tracking integration
@@ -34,8 +34,6 @@ Standards:
"""
# INFRASTRUCTURE IMPORT PATTERN
import sys
from pathlib import Path
# Standard library imports
import time
@@ -193,6 +191,7 @@ def make_api_request(client: OpenAI, messages: List[Dict], model: str, retries:
OpenAI response object or None on failure
"""
if not client or not messages or not model:
logger.warning(f"make_api_request() called with missing params — client={bool(client)}, messages={bool(messages)}, model={bool(model)}")
return None
api_params = {
@@ -242,9 +241,11 @@ def extract_response(response: Any) -> Optional[Dict[str, Any]]:
try:
# Validate response structure
if not hasattr(response, 'choices') or not response.choices:
logger.warning("Response missing 'choices' or choices is empty")
return None
if not hasattr(response.choices[0], 'message'):
logger.warning("Response choice missing 'message' attribute")
return None
# Extract content
@@ -25,12 +25,11 @@ COMPLIANT STANDARDS:
- Under 300 lines
"""
import sys
from pathlib import Path
import json
from datetime import datetime
from typing import Dict, Any, Optional, Tuple
from typing import Dict, Any, Optional
from aipass.prax import logger
@@ -102,7 +101,7 @@ def get_default_caller_config() -> Dict[str, Any]:
Dict with default OpenRouter configuration (model must be set by caller)
"""
return {
"skill_name": "openrouter",
"module_name": "openrouter",
"timestamp": datetime.now().isoformat(),
"config": {
"ai_model": "", # Caller must set their own model
@@ -121,7 +120,7 @@ def get_default_caller_data() -> Dict[str, Any]:
Dict with initial usage tracking data
"""
return {
"skill_name": "openrouter",
"module_name": "openrouter",
"timestamp": datetime.now().isoformat(),
"data": {
"total_requests": 0,
@@ -141,7 +140,7 @@ def get_default_caller_log() -> Dict[str, Any]:
Dict with empty log structure
"""
return {
"skill_name": "openrouter",
"module_name": "openrouter",
"timestamp": datetime.now().isoformat(),
"logs": []
}
@@ -15,7 +15,6 @@ Business logic for aggregating usage statistics:
- Cost, token, and latency aggregation
- Model usage tracking and breakdown
Extracted from legacy archive (api_usage.py).
Functions: get_caller_usage(), get_session_summary()
"""
@@ -15,11 +15,9 @@ Business logic for tracking API usage from OpenRouter:
- Store generation tracking data with newest-first ordering
- Handle HTTP requests with proper error handling
Extracted from legacy archive (api_usage.py).
Functions: track_usage(), _get_generation_metrics(), _store_usage_data()
Functions: track_usage(), get_generation_metrics(), store_usage_data()
"""
import sys
from pathlib import Path
# Standard library imports
@@ -147,7 +145,8 @@ def get_generation_metrics(generation_id: str, api_key: str) -> Optional[Dict[st
# Query the generation endpoint
response = requests.get(
f"{GENERATION_ENDPOINT}?id={generation_id}",
GENERATION_ENDPOINT,
params={"id": generation_id},
headers=headers,
timeout=30
)
@@ -158,7 +157,7 @@ def get_generation_metrics(generation_id: str, api_key: str) -> Optional[Dict[st
# Validate response structure
if not data or "data" not in data:
# Invalid response structure from OpenRouter
logger.warning(f"[{MODULE_NAME}] Invalid response structure from OpenRouter for generation {generation_id}")
return None
# Extract metrics from response
@@ -176,7 +175,7 @@ def get_generation_metrics(generation_id: str, api_key: str) -> Optional[Dict[st
return result
else:
# OpenRouter API returned non-200 status
logger.warning(f"[{MODULE_NAME}] OpenRouter API returned status {response.status_code} for generation {generation_id}")
return None
except requests.exceptions.Timeout as e:
+3 -3
View File
@@ -20,10 +20,9 @@ from pathlib import Path
from typing import List
from aipass.prax.apps.modules.logger import system_logger as logger
from aipass.cli.apps.modules import console, header, success, error, warning, section
from aipass.cli.apps.modules import console, header, success, error
from aipass.api.apps.handlers.json import json_handler
from aipass.api.apps.handlers.auth import keys, env
from aipass.api.apps.handlers.config import provider
def print_introspection():
@@ -110,7 +109,8 @@ def get_key(args: List[str]):
if api_key:
success(f"API key retrieved for {provider_name}")
console.print(f" Key (first 20 chars): {api_key[:20]}...")
masked = api_key[:6] + "****" + api_key[-4:] if len(api_key) > 10 else "****"
console.print(f" Key: {masked}")
else:
error(f"Failed to retrieve API key for {provider_name}")
+6 -5
View File
@@ -124,11 +124,6 @@ def handle_command(command: str, args: List[str]) -> bool:
Returns:
True if command was handled, False to pass through.
"""
# Help gate
if args and args[0] in ("--help", "-h", "help"):
print_help()
return True
# NO-ARGS GATE (seedgo standard)
if not args:
if command == "google":
@@ -140,6 +135,12 @@ def handle_command(command: str, args: List[str]) -> bool:
if args[0] != "google":
return False
# Help gate — only fires after confirming this is a google command
remaining = args[1:] if len(args) > 1 else []
if remaining and remaining[0] in ("--help", "-h", "help"):
print_help()
return True
if command == "validate":
_cmd_validate()
return True
@@ -17,11 +17,10 @@ Orchestrates LLM API client operations:
"""
import sys
from pathlib import Path
from typing import List
from aipass.prax.apps.modules.logger import system_logger as logger
from aipass.cli.apps.modules import console, header, success, error, warning, section
from aipass.cli.apps.modules import console, header, success, error
from aipass.api.apps.handlers.json import json_handler
from aipass.api.apps.handlers.auth import keys
from aipass.api.apps.handlers.openrouter import client, models
@@ -188,7 +187,42 @@ def make_call(args: List[str]):
header("OpenRouter API Call")
console.print()
warning("API call workflow - TODO")
if not args:
error("Prompt required", suggestion="drone @api call \"your prompt\" --model MODEL")
return
# Parse args: first non-flag arg is prompt, --model MODEL is optional
prompt = None
model = None
i = 0
while i < len(args):
if args[i] == "--model" and i + 1 < len(args):
model = args[i + 1]
i += 2
elif prompt is None:
prompt = args[i]
i += 1
else:
i += 1
if not prompt:
error("Prompt required", suggestion="drone @api call \"your prompt\" --model MODEL")
return
if not model:
error("Model required", suggestion="drone @api call \"your prompt\" --model anthropic/claude-3.5-sonnet")
return
console.print(f"[dim]Calling {model}...[/dim]")
response = client.get_response(prompt, caller="cli", model=model)
if response:
success(f"Response received ({len(response['content'])} chars)")
console.print()
console.print(response["content"])
else:
error("API call failed")
def list_models(args: List[str] | None = None):
+17 -2
View File
@@ -21,7 +21,7 @@ from pathlib import Path
from typing import List
from aipass.prax.apps.modules.logger import system_logger as logger
from aipass.cli.apps.modules import console, header, success, error, warning, section
from aipass.cli.apps.modules import console, header, success, error, warning
from aipass.api.apps.handlers.json import json_handler
from aipass.api.apps.handlers.usage import tracking, aggregation, cleanup
@@ -174,7 +174,22 @@ def track_usage(args: List[str]):
header("Track API Usage")
console.print()
warning("Usage tracking workflow - TODO")
if not args:
error("Generation ID required", suggestion="drone @api track <generation_id> [caller]")
return
generation_id = args[0]
caller = args[1] if len(args) > 1 else "manual"
console.print(f"[dim]Tracking generation {generation_id}...[/dim]")
result = tracking.track_usage(generation_id, caller)
if result.get("success"):
metrics = result.get("metrics", {})
success(f"Tracked: {metrics.get('tokens_prompt', 0)} prompt + {metrics.get('tokens_completion', 0)} completion tokens, ${metrics.get('total_cost', 0):.6f}")
else:
error(f"Tracking failed: {result.get('error', 'unknown')}")
def show_stats():
+2 -2
View File
@@ -137,10 +137,10 @@ def test_handle_command_routes_reauth_google(
def test_handle_command_help_gate(
_warn, _err, _succ, _hdr, _json, _retry, _factory, _auth, mock_console
):
"""handle_command prints help when args=['--help'] and returns True."""
"""handle_command prints help when args=['google', '--help'] and returns True."""
from aipass.api.apps.modules import google_client
result = google_client.handle_command("validate", ["--help"])
result = google_client.handle_command("validate", ["google", "--help"])
assert result is True
# print_help calls console.print with the argparse output
@@ -596,17 +596,17 @@ def test_list_models_formats_free_pricing(mock_console, mock_header, mock_keys,
# =============================================
@patch(f"{_MOD}.warning")
@patch(f"{_MOD}.error")
@patch(f"{_MOD}.header")
@patch(f"{_MOD}.console")
def test_make_call_shows_todo_warning(mock_console, mock_header, mock_warning):
"""make_call currently shows a TODO warning."""
def test_make_call_no_model_shows_error(mock_console, mock_header, mock_error):
"""make_call without --model shows error."""
from aipass.api.apps.modules import openrouter_client
openrouter_client.make_call(["What is AI?"])
mock_warning.assert_called_once()
assert "TODO" in mock_warning.call_args[0][0]
mock_error.assert_called_once()
assert "Model required" in mock_error.call_args[0][0]
# =============================================
+3 -3
View File
@@ -60,7 +60,7 @@ def test_create_caller_config_defaults(tmp_path: Path):
assert config["config"]["ai_max_tokens"] == 4000
assert config["config"]["enabled"] is True
assert config["config"]["ai_model"] == ""
assert config["skill_name"] == "openrouter"
assert config["module_name"] == "openrouter"
def test_create_caller_config_data_defaults(tmp_path: Path):
@@ -87,7 +87,7 @@ def test_create_caller_config_log_defaults(tmp_path: Path):
log = read_json(json_folder / "openrouter_skill_log.json")
assert log is not None
assert log["logs"] == []
assert log["skill_name"] == "openrouter"
assert log["module_name"] == "openrouter"
def test_create_caller_config_creates_folder(tmp_path: Path):
@@ -187,7 +187,7 @@ def test_get_default_caller_config_structure():
"""Default config should have expected structure."""
config = get_default_caller_config()
assert "skill_name" in config
assert "module_name" in config
assert "timestamp" in config
assert "config" in config
assert isinstance(config["config"], dict)