KManager / app /api /patch.py
StarrySkyWorld's picture
Initial commit
494c89b
"""
Kiro Patch API - Manage Machine ID patching
"""
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import Optional
from services.kiro_patcher_service import KiroPatcherService
from services.machine_id_service import MachineIdService
from app.websocket import get_manager
router = APIRouter()
patcher = KiroPatcherService()
machine_id_service = MachineIdService()
class PatchStatus(BaseModel):
isPatched: bool
kiroVersion: Optional[str] = None
patchVersion: Optional[str] = None
currentMachineId: Optional[str] = None
backupExists: bool = False
backupPath: Optional[str] = None
error: Optional[str] = None
class TelemetryStatus(BaseModel):
machineId: Optional[str] = None
sqmId: Optional[str] = None
devDeviceId: Optional[str] = None
serviceMachineId: Optional[str] = None
kiroInstalled: bool = False
@router.get("/status", response_model=PatchStatus)
async def get_patch_status():
"""Get Kiro patch status"""
status = patcher.get_status()
return PatchStatus(
isPatched=status.is_patched,
kiroVersion=status.kiro_version,
patchVersion=status.patch_version,
currentMachineId=status.current_machine_id,
backupExists=status.backup_exists,
backupPath=status.backup_path,
error=status.error
)
@router.post("/apply")
async def apply_patch(force: bool = False):
"""Apply Kiro patch"""
ws = get_manager()
await ws.broadcast_log("πŸ”§ Applying Kiro patch...", "info")
result = patcher.patch(force=force, skip_running_check=True)
if result.success:
await ws.broadcast_log(f"βœ… {result.message}", "success")
await ws.broadcast_log(f"πŸ“ Backup: {result.backup_path}", "info")
return {
"success": True,
"message": result.message,
"backupPath": result.backup_path,
"patchedFile": result.patched_file
}
else:
await ws.broadcast_log(f"❌ {result.message}", "error")
raise HTTPException(status_code=400, detail=result.message)
@router.post("/remove")
async def remove_patch():
"""Remove Kiro patch (restore original)"""
ws = get_manager()
await ws.broadcast_log("πŸ”§ Removing Kiro patch...", "info")
result = patcher.unpatch(skip_running_check=True)
if result.success:
await ws.broadcast_log(f"βœ… {result.message}", "success")
return {"success": True, "message": result.message}
else:
await ws.broadcast_log(f"❌ {result.message}", "error")
raise HTTPException(status_code=400, detail=result.message)
@router.post("/generate-id")
async def generate_machine_id():
"""Generate new custom Machine ID"""
ws = get_manager()
new_id = patcher.generate_machine_id()
await ws.broadcast_log(f"🎲 New Machine ID: {new_id[:32]}...", "success")
# Check if patch is applied
status = patcher.get_status()
if not status.is_patched:
await ws.broadcast_log("⚠️ Kiro is not patched. Apply patch for ID to take effect.", "warning")
return {
"success": True,
"machineId": new_id,
"isPatched": status.is_patched
}
@router.get("/telemetry", response_model=TelemetryStatus)
async def get_telemetry_status():
"""Get Kiro telemetry IDs status"""
info = machine_id_service.get_telemetry_info()
return TelemetryStatus(
machineId=info.machine_id,
sqmId=info.sqm_id,
devDeviceId=info.dev_device_id,
serviceMachineId=info.service_machine_id,
kiroInstalled=info.kiro_installed
)
@router.post("/reset-telemetry")
async def reset_telemetry():
"""Reset Kiro telemetry IDs"""
ws = get_manager()
await ws.broadcast_log("πŸ”„ Resetting Kiro telemetry IDs...", "info")
try:
new_ids = machine_id_service.reset_telemetry(check_running=False)
await ws.broadcast_log(f"βœ… machineId: {new_ids.machine_id[:32]}...", "success")
await ws.broadcast_log(f"βœ… sqmId: {new_ids.sqm_id}", "success")
await ws.broadcast_log("⚠️ Restart Kiro for changes to take effect", "warning")
return {
"success": True,
"machineId": new_ids.machine_id,
"sqmId": new_ids.sqm_id,
"devDeviceId": new_ids.dev_device_id
}
except Exception as e:
await ws.broadcast_log(f"❌ Error: {str(e)}", "error")
raise HTTPException(status_code=500, detail=str(e))
@router.post("/check")
async def check_patch():
"""Check if patch needs update (e.g., after Kiro update)"""
needs_update, reason = patcher.check_update_needed()
return {
"needsUpdate": needs_update,
"reason": reason
}