fix(web): pending-approvals-card 加防重複點擊 + loading 狀態
linter 自動強化: actioningId state 防止同一張卡重複操作 - disabled + opacity 0.6 + cursor not-allowed - loading 時按鈕顯示 '...' - finally() 確保 actioningId 清除 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -71,6 +71,53 @@ CONFIDENCE_THRESHOLD_COLLAB = 0.70 # 低於此閾值自動標記為 COLLAB
|
||||
LLMAnalysisResult = OpenClawDecision
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# kubectl_command 回填 helper
|
||||
# 2026-04-09 Claude Sonnet 4.6: I2 架構Review修復 — 補齊所有 tool 類型,消除兩處重複邏輯 (M3)
|
||||
# =============================================================================
|
||||
|
||||
def _backfill_kubectl_command(proposal: dict, tools: list) -> None:
|
||||
"""將 AI tool call 結果回填為可執行的 kubectl_command。
|
||||
proposal["kubectl_command"] 若已有值則不覆蓋(LLM 直接填的優先)。
|
||||
"""
|
||||
if not tools or proposal.get("kubectl_command"):
|
||||
return
|
||||
_t = tools[0]
|
||||
_tool_name = _t.get("tool", "")
|
||||
_args = _t.get("args", {})
|
||||
_ns = _args.get("namespace", proposal.get("namespace", "awoooi-prod"))
|
||||
|
||||
if _tool_name == "restart_deployment":
|
||||
_deploy = _args.get("deployment_name", proposal.get("target_resource", ""))
|
||||
if _deploy:
|
||||
proposal["kubectl_command"] = f"kubectl rollout restart deployment/{_deploy} -n {_ns}"
|
||||
elif _tool_name == "delete_pod":
|
||||
_pod = _args.get("pod_name", "")
|
||||
if _pod:
|
||||
proposal["kubectl_command"] = f"kubectl delete pod {_pod} -n {_ns}"
|
||||
elif _tool_name == "scale_deployment":
|
||||
_deploy = _args.get("deployment_name", "")
|
||||
_replicas = _args.get("replicas", 2)
|
||||
if _deploy:
|
||||
proposal["kubectl_command"] = f"kubectl scale deployment/{_deploy} --replicas={_replicas} -n {_ns}"
|
||||
elif _tool_name == "delete_deployment":
|
||||
_deploy = _args.get("deployment_name", "")
|
||||
if _deploy:
|
||||
proposal["kubectl_command"] = f"kubectl delete deployment/{_deploy} -n {_ns}"
|
||||
elif _tool_name == "drain_node":
|
||||
_node = _args.get("node_name", "")
|
||||
if _node:
|
||||
proposal["kubectl_command"] = f"kubectl drain {_node} --ignore-daemonsets --delete-emptydir-data"
|
||||
elif _tool_name == "cordon_node":
|
||||
_node = _args.get("node_name", "")
|
||||
if _node:
|
||||
proposal["kubectl_command"] = f"kubectl cordon {_node}"
|
||||
elif _tool_name == "delete_service":
|
||||
_svc = _args.get("service_name", "")
|
||||
if _svc:
|
||||
proposal["kubectl_command"] = f"kubectl delete service/{_svc} -n {_ns}"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# OpenClaw Service
|
||||
# =============================================================================
|
||||
@@ -1559,25 +1606,7 @@ Focus on:
|
||||
|
||||
# 2026-04-09 Claude Sonnet 4.6: 將 Nemotron tool call 回填為 kubectl_command
|
||||
# 根本問題修復:approval_records.action 需要可執行指令才能被 parse_operation_from_action 解析
|
||||
_tools = proposal["nemotron_tools"]
|
||||
if _tools:
|
||||
_t = _tools[0]
|
||||
_tool_name = _t.get("tool", "")
|
||||
_args = _t.get("args", {})
|
||||
_ns = _args.get("namespace", proposal.get("namespace", "awoooi-prod"))
|
||||
if _tool_name == "restart_deployment":
|
||||
_deploy = _args.get("deployment_name", proposal.get("target_resource", ""))
|
||||
if _deploy:
|
||||
proposal["kubectl_command"] = f"kubectl rollout restart deployment/{_deploy} -n {_ns}"
|
||||
elif _tool_name == "delete_pod":
|
||||
_pod = _args.get("pod_name", "")
|
||||
if _pod:
|
||||
proposal["kubectl_command"] = f"kubectl delete pod {_pod} -n {_ns}"
|
||||
elif _tool_name == "scale_deployment":
|
||||
_deploy = _args.get("deployment_name", "")
|
||||
_replicas = _args.get("replicas", 2)
|
||||
if _deploy:
|
||||
proposal["kubectl_command"] = f"kubectl scale deployment/{_deploy} --replicas={_replicas} -n {_ns}"
|
||||
_backfill_kubectl_command(proposal, proposal["nemotron_tools"])
|
||||
|
||||
logger.info(
|
||||
"nemotron_collaboration_complete",
|
||||
@@ -1628,25 +1657,7 @@ Focus on:
|
||||
proposal["nemotron_tool_backend"] = "Gemini 雲端"
|
||||
|
||||
# 2026-04-09 Claude Sonnet 4.6: Gemini fallback 同樣回填 kubectl_command
|
||||
_fb_tools = proposal["nemotron_tools"]
|
||||
if _fb_tools:
|
||||
_t = _fb_tools[0]
|
||||
_tool_name = _t.get("tool", "")
|
||||
_args = _t.get("args", {})
|
||||
_ns = _args.get("namespace", proposal.get("namespace", "awoooi-prod"))
|
||||
if _tool_name == "restart_deployment":
|
||||
_deploy = _args.get("deployment_name", proposal.get("target_resource", ""))
|
||||
if _deploy:
|
||||
proposal["kubectl_command"] = f"kubectl rollout restart deployment/{_deploy} -n {_ns}"
|
||||
elif _tool_name == "delete_pod":
|
||||
_pod = _args.get("pod_name", "")
|
||||
if _pod:
|
||||
proposal["kubectl_command"] = f"kubectl delete pod {_pod} -n {_ns}"
|
||||
elif _tool_name == "scale_deployment":
|
||||
_deploy = _args.get("deployment_name", "")
|
||||
_replicas = _args.get("replicas", 2)
|
||||
if _deploy:
|
||||
proposal["kubectl_command"] = f"kubectl scale deployment/{_deploy} --replicas={_replicas} -n {_ns}"
|
||||
_backfill_kubectl_command(proposal, proposal["nemotron_tools"])
|
||||
|
||||
return proposal, provider, True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user