Fix publish state tracking and add sync status feature

- Track moderation status (pending/approved/rejected) in local tool config
- Add new "pending" state indicator (◐ yellow) distinct from "published" (✓ green)
- Add "Sync Status" button to check registry for updated moderation status
- Add /me/tools/<name>/status API endpoint for checking tool status
- Improve admin panel error handling with better error messages

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
rob
2026-01-16 07:52:42 -04:00
co-authored by Claude Opus 4.5
parent 3fccf854be
commit f1c462146b
5 changed files with 151 additions and 11 deletions
+3 -1
View File
@@ -183,14 +183,16 @@ class PublishDialog(QDialog):
self.status_label.setText("Published successfully!")
self.status_label.setStyleSheet("color: #38a169; font-weight: 600;")
# Save registry_hash to local config for publish state tracking
# Save registry_hash and moderation status to local config for publish state tracking
config_hash = result.get("config_hash")
moderation_status = result.get("status", "pending")
if config_hash and hasattr(self._tool, 'path') and self._tool.path:
try:
config_path = self._tool.path
if config_path.exists():
config_data = yaml.safe_load(config_path.read_text()) or {}
config_data["registry_hash"] = config_hash
config_data["registry_status"] = moderation_status
config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False))
except Exception:
pass # Non-critical - just won't show publish state
+84 -6
View File
@@ -29,7 +29,8 @@ def get_tool_publish_state(tool_name: str) -> Tuple[str, Optional[str]]:
Returns:
Tuple of (state, registry_hash) where state is:
- "published" - has registry_hash and current hash matches
- "published" - approved in registry and current hash matches
- "pending" - submitted but awaiting moderation
- "modified" - has registry_hash but current hash differs
- "local" - no registry_hash (never published/downloaded)
"""
@@ -40,6 +41,7 @@ def get_tool_publish_state(tool_name: str) -> Tuple[str, Optional[str]]:
try:
config = yaml.safe_load(config_path.read_text())
registry_hash = config.get("registry_hash")
registry_status = config.get("registry_status", "pending")
if not registry_hash:
return ("local", None)
@@ -47,10 +49,15 @@ def get_tool_publish_state(tool_name: str) -> Tuple[str, Optional[str]]:
# Compute current hash (excluding hash fields)
current_hash = compute_config_hash(config)
if current_hash == registry_hash:
if current_hash != registry_hash:
return ("modified", registry_hash)
# Hash matches - check moderation status
if registry_status == "approved":
return ("published", registry_hash)
else:
return ("modified", registry_hash)
# pending, rejected, or unknown
return ("pending", registry_hash)
except Exception:
return ("local", None)
@@ -156,6 +163,14 @@ class ToolsPage(QWidget):
btn_layout.addStretch()
# Sync status button (only when connected)
self.btn_sync = QPushButton("Sync Status")
self.btn_sync.setObjectName("secondary")
self.btn_sync.clicked.connect(self._sync_tool_status)
self.btn_sync.setEnabled(False)
self.btn_sync.setToolTip("Check registry for updated moderation status")
btn_layout.addWidget(self.btn_sync)
# Connect/Publish button
config = load_config()
if config.registry.token:
@@ -219,8 +234,12 @@ class ToolsPage(QWidget):
# Show state indicator in display name
if state == "published":
display_name = f"{name} ✓"
tooltip = "Published to registry - up to date"
tooltip = "Published to registry - approved"
color = QColor(56, 161, 105) # Green
elif state == "pending":
display_name = f"{name} ◐"
tooltip = "Submitted to registry - pending review"
color = QColor(214, 158, 46) # Yellow/amber
elif state == "modified":
display_name = f"{name} ●"
tooltip = "Published to registry - local modifications"
@@ -298,7 +317,13 @@ class ToolsPage(QWidget):
lines.append(
"<p style='background: #c6f6d5; color: #276749; padding: 6px 10px; "
"border-radius: 4px; margin-bottom: 12px; font-size: 12px;'>"
"✓ Published to registry - up to date</p>"
"✓ Published to registry - approved</p>"
)
elif state == "pending":
lines.append(
"<p style='background: #fef3c7; color: #92400e; padding: 6px 10px; "
"border-radius: 4px; margin-bottom: 12px; font-size: 12px;'>"
"◐ Submitted to registry - pending review</p>"
)
elif state == "modified":
lines.append(
@@ -360,9 +385,16 @@ class ToolsPage(QWidget):
if config.registry.token:
# Connected - enable Publish when tool selected
self.btn_publish.setEnabled(has_selection)
# Enable Sync when tool selected and has registry_hash
if has_selection:
state, registry_hash = get_tool_publish_state(self._current_tool.name)
self.btn_sync.setEnabled(registry_hash is not None)
else:
self.btn_sync.setEnabled(False)
else:
# Not connected - Connect button is always enabled
self.btn_publish.setEnabled(True)
self.btn_sync.setEnabled(False)
def _create_tool(self):
"""Create a new tool."""
@@ -413,8 +445,54 @@ class ToolsPage(QWidget):
if not self._current_tool:
return
tool_name = self._current_tool.name # Save before refresh clears it
from ..dialogs.publish_dialog import PublishDialog
dialog = PublishDialog(self, self._current_tool)
if dialog.exec():
self.refresh() # Refresh to show updated publish state indicator
self.main_window.show_status(f"Published '{self._current_tool.name}'")
self.main_window.show_status(f"Published '{tool_name}'")
def _sync_tool_status(self):
"""Sync the moderation status of the selected tool from the registry."""
if not self._current_tool:
return
tool_name = self._current_tool.name
config_path = get_tools_dir() / tool_name / "config.yaml"
if not config_path.exists():
return
try:
from ...registry_client import RegistryClient, RegistryError
config = load_config()
client = RegistryClient()
client.token = config.registry.token
# Get status from registry
status_data = client.get_my_tool_status(tool_name)
new_status = status_data.get("status", "pending")
# Update local config
config_data = yaml.safe_load(config_path.read_text()) or {}
old_status = config_data.get("registry_status", "pending")
if old_status != new_status:
config_data["registry_status"] = new_status
config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False))
self.refresh()
if new_status == "approved":
self.main_window.show_status(f"Tool '{tool_name}' has been approved!")
elif new_status == "rejected":
self.main_window.show_status(f"Tool '{tool_name}' was rejected")
else:
self.main_window.show_status(f"Status updated: {new_status}")
else:
self.main_window.show_status(f"Status unchanged: {new_status}")
except RegistryError as e:
QMessageBox.warning(self, "Sync Error", f"Could not sync status:\n{e.message}")
except Exception as e:
QMessageBox.warning(self, "Sync Error", f"Could not sync status:\n{e}")