Add full ToolSource support to registry
- Add source_json column to store complete source attribution - Update publish API to accept source object or legacy string - Update get_tool API to return parsed source object - Update web UI to display type, author, license, url, original_tool - Add gunicorn config and dependency for production server Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
@@ -574,6 +574,21 @@ def create_app() -> Flask:
|
||||
if not row:
|
||||
return error_response("TOOL_NOT_FOUND", f"Tool '{owner}/{name}' does not exist", 404)
|
||||
|
||||
# Parse source attribution
|
||||
source_obj = None
|
||||
if row["source_json"]:
|
||||
try:
|
||||
source_obj = json.loads(row["source_json"])
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
# Fall back to legacy fields if no source_json
|
||||
if not source_obj and (row["source"] or row["source_url"]):
|
||||
source_obj = {
|
||||
"type": "imported",
|
||||
"original_tool": row["source"],
|
||||
"url": row["source_url"],
|
||||
}
|
||||
|
||||
payload = {
|
||||
"owner": row["owner"],
|
||||
"name": row["name"],
|
||||
@@ -588,6 +603,7 @@ def create_app() -> Flask:
|
||||
"replacement": row["replacement"],
|
||||
"config": row["config_yaml"],
|
||||
"readme": row["readme"],
|
||||
"source": source_obj,
|
||||
}
|
||||
response = jsonify({"data": payload})
|
||||
response.headers["Cache-Control"] = "max-age=60"
|
||||
@@ -1100,9 +1116,25 @@ def create_app() -> Flask:
|
||||
description = (data.get("description") or "").strip()
|
||||
category = (data.get("category") or "").strip() or None
|
||||
tags = data.get("tags") or []
|
||||
source = (data.get("source") or "").strip() or None
|
||||
|
||||
# Handle source attribution - can be a dict (full ToolSource) or string (legacy)
|
||||
source_data = data.get("source")
|
||||
source_json = None
|
||||
source = None
|
||||
source_url = (data.get("source_url") or "").strip() or None
|
||||
|
||||
if isinstance(source_data, dict):
|
||||
# Full source object from tool YAML
|
||||
source_json = json.dumps(source_data)
|
||||
# Keep legacy fields for backward compat
|
||||
source = source_data.get("original_tool") or source_data.get("author")
|
||||
source_url = source_data.get("url") or source_url
|
||||
elif isinstance(source_data, str) and source_data.strip():
|
||||
# Legacy string format
|
||||
source = source_data.strip()
|
||||
# Create a minimal source_json for consistency
|
||||
source_json = json.dumps({"type": "imported", "original_tool": source, "url": source_url})
|
||||
|
||||
if not name or not TOOL_NAME_RE.match(name) or len(name) > MAX_TOOL_NAME_LEN:
|
||||
return error_response("VALIDATION_ERROR", "Invalid tool name")
|
||||
if not version or Semver.parse(version) is None:
|
||||
@@ -1218,8 +1250,8 @@ def create_app() -> Flask:
|
||||
INSERT INTO tools (
|
||||
owner, name, version, description, category, tags, config_yaml, readme,
|
||||
publisher_id, deprecated, deprecated_message, replacement, downloads,
|
||||
scrutiny_status, scrutiny_report, source, source_url, published_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
scrutiny_status, scrutiny_report, source, source_url, source_json, published_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
[
|
||||
owner,
|
||||
@@ -1239,6 +1271,7 @@ def create_app() -> Flask:
|
||||
scrutiny_json,
|
||||
source,
|
||||
source_url,
|
||||
source_json,
|
||||
datetime.utcnow().isoformat(),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -53,6 +53,7 @@ CREATE TABLE IF NOT EXISTS tools (
|
||||
scrutiny_report TEXT,
|
||||
source TEXT,
|
||||
source_url TEXT,
|
||||
source_json TEXT,
|
||||
published_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(owner, name, version)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user