fix(diagnosis): tighten timeout handling and clarify timeout messaging (#233)

## Summary
- shorten per-item diagnosis command timeout from 2 minutes to 30
seconds while keeping overall request timeout at 2 minutes
- centralize timeout messages in backend constants and apply consistent
timeout fallback handling in diagnosis result assembly
- update frontend forward/tunnel diagnosis timeout copy to clearly
explain single-item and overall timeout limits

## Notes
- includes workspace tool config files under `.claude/` and `.entire/`
as part of this commit
This commit is contained in:
sagit
2026-03-01 18:39:19 +08:00
committed by GitHub
5 changed files with 102 additions and 12 deletions
+84
View File
@@ -0,0 +1,84 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Task",
"hooks": [
{
"type": "command",
"command": "entire hooks claude-code post-task"
}
]
},
{
"matcher": "TodoWrite",
"hooks": [
{
"type": "command",
"command": "entire hooks claude-code post-todo"
}
]
}
],
"PreToolUse": [
{
"matcher": "Task",
"hooks": [
{
"type": "command",
"command": "entire hooks claude-code pre-task"
}
]
}
],
"SessionEnd": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "entire hooks claude-code session-end"
}
]
}
],
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "entire hooks claude-code session-start"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "entire hooks claude-code stop"
}
]
}
],
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "entire hooks claude-code user-prompt-submit"
}
]
}
]
},
"permissions": {
"deny": [
"Read(./.entire/metadata/**)"
]
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"enabled": true,
"telemetry": false
}
@@ -110,9 +110,11 @@ func (h *Handler) buildDiagnosisStreamStartItems(workItems []diagnosisWorkItem)
const diagnosisMaxConcurrency = 8 const diagnosisMaxConcurrency = 8
const ( const (
defaultNodeCommandTimeout = 6 * time.Second defaultNodeCommandTimeout = 6 * time.Second
diagnosisCommandTimeout = 2 * time.Minute diagnosisCommandTimeout = 30 * time.Second
diagnosisRequestTimeout = 2 * time.Minute diagnosisRequestTimeout = 2 * time.Minute
diagnosisCommandTimeoutMsg = "诊断超时(30秒)"
diagnosisRequestTimeoutMsg = "诊断超时(2分钟)"
) )
func (h *Handler) resolveForwardAccess(r *http.Request, forwardID int64) (*forwardRecord, int64, int, error) { func (h *Handler) resolveForwardAccess(r *http.Request, forwardID int64) (*forwardRecord, int64, int, error) {
@@ -848,15 +850,15 @@ func resolveDiagnosisTargets(remoteAddr string) ([]diagnosisTarget, error) {
func diagnosisContextMessage(ctx context.Context) string { func diagnosisContextMessage(ctx context.Context) string {
if ctx == nil { if ctx == nil {
return "诊断超时(2分钟)" return diagnosisRequestTimeoutMsg
} }
switch ctx.Err() { switch ctx.Err() {
case context.DeadlineExceeded: case context.DeadlineExceeded:
return "诊断超时(2分钟)" return diagnosisRequestTimeoutMsg
case context.Canceled: case context.Canceled:
return "诊断已取消" return "诊断已取消"
default: default:
return "诊断超时(2分钟)" return diagnosisRequestTimeoutMsg
} }
} }
@@ -895,7 +897,7 @@ func newDiagnosisTimeoutItem(workItem diagnosisWorkItem, message string) map[str
item := newDiagnosisResultItem(workItem.fromNodeID, workItem.targetIP, targetPort, workItem.description, workItem.metadata) item := newDiagnosisResultItem(workItem.fromNodeID, workItem.targetIP, targetPort, workItem.description, workItem.metadata)
item["success"] = false item["success"] = false
if strings.TrimSpace(message) == "" { if strings.TrimSpace(message) == "" {
message = "诊断超时(2分钟)" message = diagnosisCommandTimeoutMsg
} }
item["message"] = message item["message"] = message
return item return item
@@ -993,7 +995,7 @@ enqueueLoop:
for i := range results { for i := range results {
if results[i] == nil { if results[i] == nil {
results[i] = newDiagnosisTimeoutItem(workItems[i], "诊断超时(2分钟)") results[i] = newDiagnosisTimeoutItem(workItems[i], diagnosisCommandTimeoutMsg)
} }
} }
return results return results
+2 -2
View File
@@ -1003,7 +1003,7 @@ export default function ForwardPage() {
toast.error(streamErrorMessage); toast.error(streamErrorMessage);
} }
if (streamResult.timedOut) { if (streamResult.timedOut) {
toast.error("诊断达到2分钟超时,已返回当前结果"); toast.error("诊断超时(单条30秒 / 整体2分钟),已返回当前结果");
} }
} catch { } catch {
if (abortController.signal.aborted) { if (abortController.signal.aborted) {
@@ -3491,7 +3491,7 @@ export default function ForwardPage() {
{diagnosisProgress.timedOut && ( {diagnosisProgress.timedOut && (
<Alert <Alert
color="warning" color="warning"
description="诊断已达到2分钟超时,以下为当前已完成结果。" description="诊断超时(单条30秒 / 整体2分钟),以下为当前已完成结果。"
title="诊断超时" title="诊断超时"
variant="flat" variant="flat"
/> />
+2 -2
View File
@@ -605,7 +605,7 @@ export default function TunnelPage() {
toast.error(streamErrorMessage); toast.error(streamErrorMessage);
} }
if (streamResult.timedOut) { if (streamResult.timedOut) {
toast.error("诊断达到2分钟超时,已返回当前结果"); toast.error("诊断超时(单条30秒 / 整体2分钟),已返回当前结果");
} }
} catch { } catch {
if (abortController.signal.aborted) { if (abortController.signal.aborted) {
@@ -2141,7 +2141,7 @@ export default function TunnelPage() {
{diagnosisProgress.timedOut && ( {diagnosisProgress.timedOut && (
<Alert <Alert
color="warning" color="warning"
description="诊断已达到2分钟超时,以下为当前已完成结果。" description="诊断超时(单条30秒 / 整体2分钟),以下为当前已完成结果。"
title="诊断超时" title="诊断超时"
variant="flat" variant="flat"
/> />