@winspan/claude-forge 3.6.27 → 3.6.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autopilot/issue-tracker.js +1 -1
- package/dist/autopilot/issue-tracker.js.map +1 -1
- package/dist/autopilot/quality-gate.d.ts +20 -2
- package/dist/autopilot/quality-gate.d.ts.map +1 -1
- package/dist/autopilot/quality-gate.js +20 -2
- package/dist/autopilot/quality-gate.js.map +1 -1
- package/dist/cli/commands/logs.js +1 -1
- package/dist/cli/commands/logs.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +5 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/schema.d.ts +9 -1
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +5 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/convention/convention-loader.d.ts.map +1 -1
- package/dist/convention/convention-loader.js +3 -0
- package/dist/convention/convention-loader.js.map +1 -1
- package/dist/convention/types.d.ts +15 -5
- package/dist/convention/types.d.ts.map +1 -1
- package/dist/daemon/engine-registry.d.ts.map +1 -1
- package/dist/daemon/engine-registry.js +6 -0
- package/dist/daemon/engine-registry.js.map +1 -1
- package/dist/daemon/handler-context.d.ts +5 -1
- package/dist/daemon/handler-context.d.ts.map +1 -1
- package/dist/daemon/handlers/post-tool-use-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/post-tool-use-handler.js +59 -2
- package/dist/daemon/handlers/post-tool-use-handler.js.map +1 -1
- package/dist/daemon/handlers/pre-tool-use-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/pre-tool-use-handler.js +25 -2
- package/dist/daemon/handlers/pre-tool-use-handler.js.map +1 -1
- package/dist/daemon/handlers/session-cleanup.d.ts +2 -0
- package/dist/daemon/handlers/session-cleanup.d.ts.map +1 -1
- package/dist/daemon/handlers/session-cleanup.js +20 -0
- package/dist/daemon/handlers/session-cleanup.js.map +1 -1
- package/dist/daemon/handlers/stages/07-pipeline-reply.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/07-pipeline-reply.js +3 -0
- package/dist/daemon/handlers/stages/07-pipeline-reply.js.map +1 -1
- package/dist/daemon/handlers/stages/18-complex-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/18-complex-task.js +3 -0
- package/dist/daemon/handlers/stages/18-complex-task.js.map +1 -1
- package/dist/daemon/handlers/stages/19-moderate-task.d.ts.map +1 -1
- package/dist/daemon/handlers/stages/19-moderate-task.js +3 -0
- package/dist/daemon/handlers/stages/19-moderate-task.js.map +1 -1
- package/dist/daemon/handlers/stop-handler.d.ts.map +1 -1
- package/dist/daemon/handlers/stop-handler.js +1 -0
- package/dist/daemon/handlers/stop-handler.js.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +80 -0
- package/dist/daemon/index.js.map +1 -1
- package/dist/pipeline/completion-gate.d.ts +1 -0
- package/dist/pipeline/completion-gate.d.ts.map +1 -1
- package/dist/pipeline/completion-gate.js +43 -3
- package/dist/pipeline/completion-gate.js.map +1 -1
- package/dist/pipeline/completion-verifier.d.ts +75 -0
- package/dist/pipeline/completion-verifier.d.ts.map +1 -0
- package/dist/pipeline/completion-verifier.js +217 -0
- package/dist/pipeline/completion-verifier.js.map +1 -0
- package/dist/pipeline/diagnosis-service.d.ts +43 -0
- package/dist/pipeline/diagnosis-service.d.ts.map +1 -0
- package/dist/pipeline/diagnosis-service.js +136 -0
- package/dist/pipeline/diagnosis-service.js.map +1 -0
- package/dist/pipeline/dynamic-node-types.d.ts +2 -0
- package/dist/pipeline/dynamic-node-types.d.ts.map +1 -1
- package/dist/pipeline/execution-engine.d.ts +79 -0
- package/dist/pipeline/execution-engine.d.ts.map +1 -0
- package/dist/pipeline/execution-engine.js +227 -0
- package/dist/pipeline/execution-engine.js.map +1 -0
- package/dist/pipeline/index.d.ts +27 -1
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +87 -72
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/node-type-sync.d.ts.map +1 -1
- package/dist/pipeline/node-type-sync.js +25 -22
- package/dist/pipeline/node-type-sync.js.map +1 -1
- package/dist/pipeline/pipeline-state-machine.d.ts +19 -0
- package/dist/pipeline/pipeline-state-machine.d.ts.map +1 -0
- package/dist/pipeline/pipeline-state-machine.js +53 -0
- package/dist/pipeline/pipeline-state-machine.js.map +1 -0
- package/dist/pipeline/plan-service.d.ts +58 -0
- package/dist/pipeline/plan-service.d.ts.map +1 -0
- package/dist/pipeline/plan-service.js +382 -0
- package/dist/pipeline/plan-service.js.map +1 -0
- package/dist/pipeline/strategy-selector.d.ts +41 -0
- package/dist/pipeline/strategy-selector.d.ts.map +1 -0
- package/dist/pipeline/strategy-selector.js +112 -0
- package/dist/pipeline/strategy-selector.js.map +1 -0
- package/dist/pipeline/template-evolver.d.ts +45 -0
- package/dist/pipeline/template-evolver.d.ts.map +1 -0
- package/dist/pipeline/template-evolver.js +223 -0
- package/dist/pipeline/template-evolver.js.map +1 -0
- package/dist/pipeline/template-registry.d.ts.map +1 -1
- package/dist/pipeline/template-registry.js +4 -1
- package/dist/pipeline/template-registry.js.map +1 -1
- package/dist/storage/repositories/base-repository.d.ts +2 -0
- package/dist/storage/repositories/base-repository.d.ts.map +1 -1
- package/dist/storage/repositories/base-repository.js +12 -0
- package/dist/storage/repositories/base-repository.js.map +1 -1
- package/dist/storage/repositories/dynamic-pipeline-repository.d.ts +2 -2
- package/dist/storage/repositories/dynamic-pipeline-repository.d.ts.map +1 -1
- package/dist/storage/repositories/dynamic-pipeline-repository.js +28 -3
- package/dist/storage/repositories/dynamic-pipeline-repository.js.map +1 -1
- package/dist/storage/repositories/dynamic-pipeline-template-repository.d.ts +13 -0
- package/dist/storage/repositories/dynamic-pipeline-template-repository.d.ts.map +1 -0
- package/dist/storage/repositories/dynamic-pipeline-template-repository.js +117 -0
- package/dist/storage/repositories/dynamic-pipeline-template-repository.js.map +1 -0
- package/dist/storage/repositories/event-repository.d.ts.map +1 -1
- package/dist/storage/repositories/event-repository.js +1 -1
- package/dist/storage/repositories/event-repository.js.map +1 -1
- package/dist/storage/repositories/latency-repository.js +1 -1
- package/dist/storage/repositories/latency-repository.js.map +1 -1
- package/dist/storage/repositories/node-attempt-repository.d.ts +29 -0
- package/dist/storage/repositories/node-attempt-repository.d.ts.map +1 -0
- package/dist/storage/repositories/node-attempt-repository.js +57 -0
- package/dist/storage/repositories/node-attempt-repository.js.map +1 -0
- package/dist/storage/repositories/pipeline-plan-repository.d.ts +75 -0
- package/dist/storage/repositories/pipeline-plan-repository.d.ts.map +1 -0
- package/dist/storage/repositories/pipeline-plan-repository.js +123 -0
- package/dist/storage/repositories/pipeline-plan-repository.js.map +1 -0
- package/dist/storage/repositories/quality-repository.d.ts +16 -0
- package/dist/storage/repositories/quality-repository.d.ts.map +1 -0
- package/dist/storage/repositories/quality-repository.js +36 -0
- package/dist/storage/repositories/quality-repository.js.map +1 -0
- package/dist/storage/repositories/session-repository.d.ts +7 -0
- package/dist/storage/repositories/session-repository.d.ts.map +1 -1
- package/dist/storage/repositories/session-repository.js +33 -0
- package/dist/storage/repositories/session-repository.js.map +1 -1
- package/dist/storage/repositories/template-evolution-repository.d.ts +39 -0
- package/dist/storage/repositories/template-evolution-repository.d.ts.map +1 -0
- package/dist/storage/repositories/template-evolution-repository.js +83 -0
- package/dist/storage/repositories/template-evolution-repository.js.map +1 -0
- package/dist/storage/schema/migration-manager.d.ts +1 -1
- package/dist/storage/schema/migration-manager.d.ts.map +1 -1
- package/dist/storage/schema/migration-manager.js +27 -1
- package/dist/storage/schema/migration-manager.js.map +1 -1
- package/dist/storage/sqlite.d.ts +21 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +105 -31
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/session.d.ts +3 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +4 -4
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +24 -10
- package/dist/utils/logger.js.map +1 -1
- package/dist/web/routes/config.d.ts +2 -0
- package/dist/web/routes/config.d.ts.map +1 -1
- package/dist/web/routes/config.js +58 -3
- package/dist/web/routes/config.js.map +1 -1
- package/dist/web/routes/evolution.d.ts +4 -0
- package/dist/web/routes/evolution.d.ts.map +1 -0
- package/dist/web/routes/evolution.js +61 -0
- package/dist/web/routes/evolution.js.map +1 -0
- package/dist/web/routes/knowledge.d.ts.map +1 -1
- package/dist/web/routes/knowledge.js +44 -16
- package/dist/web/routes/knowledge.js.map +1 -1
- package/dist/web/routes/pipelines.d.ts.map +1 -1
- package/dist/web/routes/pipelines.js +72 -0
- package/dist/web/routes/pipelines.js.map +1 -1
- package/dist/web/routes/plans.d.ts +5 -0
- package/dist/web/routes/plans.d.ts.map +1 -0
- package/dist/web/routes/plans.js +163 -0
- package/dist/web/routes/plans.js.map +1 -0
- package/dist/web/routes/quality.d.ts.map +1 -1
- package/dist/web/routes/quality.js +6 -44
- package/dist/web/routes/quality.js.map +1 -1
- package/dist/web/routes/sessions.d.ts.map +1 -1
- package/dist/web/routes/sessions.js +27 -15
- package/dist/web/routes/sessions.js.map +1 -1
- package/dist/web/routes/skills.d.ts.map +1 -1
- package/dist/web/routes/skills.js +13 -6
- package/dist/web/routes/skills.js.map +1 -1
- package/dist/web/routes/stats.d.ts.map +1 -1
- package/dist/web/routes/stats.js +53 -2
- package/dist/web/routes/stats.js.map +1 -1
- package/dist/web/routes/templates.d.ts +4 -0
- package/dist/web/routes/templates.d.ts.map +1 -0
- package/dist/web/routes/templates.js +117 -0
- package/dist/web/routes/templates.js.map +1 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +119 -26
- package/dist/web/server.js.map +1 -1
- package/dist/web/sse-broadcaster.d.ts +23 -0
- package/dist/web/sse-broadcaster.d.ts.map +1 -0
- package/dist/web/sse-broadcaster.js +73 -0
- package/dist/web/sse-broadcaster.js.map +1 -0
- package/dist/web/utils/error-response.d.ts +23 -0
- package/dist/web/utils/error-response.d.ts.map +1 -0
- package/dist/web/utils/error-response.js +26 -0
- package/dist/web/utils/error-response.js.map +1 -0
- package/dist/web/utils/validation.d.ts +10 -0
- package/dist/web/utils/validation.d.ts.map +1 -1
- package/dist/web/utils/validation.js +15 -0
- package/dist/web/utils/validation.js.map +1 -1
- package/dist/web-static/assets/Analytics-Bo74j97W.js +1 -0
- package/dist/web-static/assets/BatchProgress-BQ533tSf.js +1 -0
- package/dist/web-static/assets/Breadcrumb-DtfwnOx6.js +1 -0
- package/dist/web-static/assets/Config-DhfQsbT1.js +1 -0
- package/dist/web-static/assets/ConfirmDialog-CVzB7X5y.js +1 -0
- package/dist/web-static/assets/Conventions-BobuQiOk.js +1 -0
- package/dist/web-static/assets/Dashboard-Bj28rnDe.js +1 -0
- package/dist/web-static/assets/ErrorState-DXHA8lr8.js +1 -0
- package/dist/web-static/assets/Events-C0y7WcDP.js +1 -0
- package/dist/web-static/assets/Evolution-DimZAXFS.js +1 -0
- package/dist/web-static/assets/Knowledge-Y1lX6vor.js +2 -0
- package/dist/web-static/assets/NodeTypes-DyrDqhBp.js +1 -0
- package/dist/web-static/assets/Pagination-lp8b_3NR.js +1 -0
- package/dist/web-static/assets/PipelineDetail-DGKz18zu.js +4 -0
- package/dist/web-static/assets/PipelineTemplates-D4zvL6I1.js +1 -0
- package/dist/web-static/assets/Pipelines-CR9oD3Xy.js +2 -0
- package/dist/web-static/assets/ProjectDetail-RfdtEvTT.js +1 -0
- package/dist/web-static/assets/Projects-RGIPdiJn.js +1 -0
- package/dist/web-static/assets/Quality-BdvL1VQn.js +3 -0
- package/dist/web-static/assets/SessionDetail-BNDIfmmq.js +1 -0
- package/dist/web-static/assets/Sessions-CorfQV78.js +2 -0
- package/dist/web-static/assets/Skeleton-B7PVDJJ_.js +1 -0
- package/dist/web-static/assets/Skills-BJt0OrKj.js +1 -0
- package/dist/web-static/assets/TemplateDetail-DOfBYdQ3.js +1 -0
- package/dist/web-static/assets/Templates-Cp3yC5tv.js +1 -0
- package/dist/web-static/assets/client-BhcjNvkG.js +1 -0
- package/dist/web-static/assets/exportCsv-CSExJI8h.js +3 -0
- package/dist/web-static/assets/index-CqwJts5v.css +2 -0
- package/dist/web-static/assets/index-DdmJVOxm.js +2 -0
- package/dist/web-static/assets/rolldown-runtime-COnpUsM8.js +1 -0
- package/dist/web-static/assets/ui-CDL3BZ13.js +1 -0
- package/dist/web-static/assets/useDebounce-DNfPs3Tv.js +1 -0
- package/dist/web-static/assets/useSmartPolling-DYAVOihL.js +1 -0
- package/dist/web-static/assets/vendor-DRGPi8ui.js +9 -0
- package/dist/web-static/assets/vendor-charts-9eVsQvUV.js +36 -0
- package/dist/web-static/assets/vendor-editor-B1NX2ipj.js +11 -0
- package/dist/web-static/assets/vendor-flow-CHpVij2M.css +1 -0
- package/dist/web-static/assets/vendor-flow-srkes8If.js +7 -0
- package/dist/web-static/assets/vendor-motion-CQmdgnI8.js +9 -0
- package/dist/web-static/assets/vendor-query-DqPOMnuX.js +4 -0
- package/dist/web-static/assets/vendor-react-DJI9oneq.js +11 -0
- package/dist/web-static/index.html +13 -2
- package/package.json +2 -1
- package/dist/web-static/assets/index-BCgOtcuH.js +0 -91
- package/dist/web-static/assets/index-DwWCJY_u.css +0 -1
package/dist/web/server.js
CHANGED
|
@@ -22,9 +22,54 @@ import { registerSkillRoutes } from './routes/skills.js';
|
|
|
22
22
|
import { registerConventionsRoutes } from './routes/conventions.js';
|
|
23
23
|
import { registerProjectConventionRoutes } from './routes/project-conventions.js';
|
|
24
24
|
import { registerNodeTypeRoutes } from './routes/node-types.js';
|
|
25
|
+
import { createPlansRouter } from './routes/plans.js';
|
|
26
|
+
import { createTemplatesRouter } from './routes/templates.js';
|
|
27
|
+
import { createEvolutionRouter } from './routes/evolution.js';
|
|
25
28
|
import { authMiddleware } from './middleware/auth.js';
|
|
29
|
+
import { sendError, ErrorCodes } from './utils/error-response.js';
|
|
30
|
+
import { SseBroadcaster, startHeartbeat } from './sse-broadcaster.js';
|
|
26
31
|
const __filename = fileURLToPath(import.meta.url);
|
|
27
32
|
const __dirname = path.dirname(__filename);
|
|
33
|
+
const RATE_LIMIT_WINDOW_MS = 60_000;
|
|
34
|
+
const RATE_LIMIT_MAX = 200;
|
|
35
|
+
const FILE_UPLOAD_MAX_MB = 10;
|
|
36
|
+
const FILE_UPLOAD_MAX_BYTES = FILE_UPLOAD_MAX_MB * 1024 * 1024;
|
|
37
|
+
const rateLimitBuckets = new Map();
|
|
38
|
+
function getClientIp(req) {
|
|
39
|
+
const forwarded = req.headers['x-forwarded-for'];
|
|
40
|
+
if (typeof forwarded === 'string' && forwarded.length > 0) {
|
|
41
|
+
return forwarded.split(',')[0]?.trim() || req.ip || 'unknown';
|
|
42
|
+
}
|
|
43
|
+
return req.ip || 'unknown';
|
|
44
|
+
}
|
|
45
|
+
function rateLimitMiddleware(req, res, next) {
|
|
46
|
+
const ip = getClientIp(req);
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const bucket = rateLimitBuckets.get(ip) || [];
|
|
49
|
+
const recent = bucket.filter(t => now - t < RATE_LIMIT_WINDOW_MS);
|
|
50
|
+
if (recent.length >= RATE_LIMIT_MAX) {
|
|
51
|
+
sendError(res, 429, ErrorCodes.RATE_LIMITED, '请求过于频繁,请稍后重试', {
|
|
52
|
+
limit: RATE_LIMIT_MAX,
|
|
53
|
+
windowMs: RATE_LIMIT_WINDOW_MS,
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
recent.push(now);
|
|
58
|
+
rateLimitBuckets.set(ip, recent);
|
|
59
|
+
next();
|
|
60
|
+
}
|
|
61
|
+
setInterval(() => {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
for (const [ip, bucket] of rateLimitBuckets.entries()) {
|
|
64
|
+
const recent = bucket.filter(t => now - t < RATE_LIMIT_WINDOW_MS);
|
|
65
|
+
if (recent.length === 0) {
|
|
66
|
+
rateLimitBuckets.delete(ip);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
rateLimitBuckets.set(ip, recent);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}, RATE_LIMIT_WINDOW_MS).unref();
|
|
28
73
|
export class WebServer {
|
|
29
74
|
app;
|
|
30
75
|
server = null;
|
|
@@ -50,45 +95,92 @@ export class WebServer {
|
|
|
50
95
|
},
|
|
51
96
|
credentials: true,
|
|
52
97
|
}));
|
|
53
|
-
this.app.use(express.json({ limit:
|
|
54
|
-
this.app.use(express.urlencoded({ limit:
|
|
98
|
+
this.app.use(express.json({ limit: `${FILE_UPLOAD_MAX_MB}mb` }));
|
|
99
|
+
this.app.use(express.urlencoded({ limit: `${FILE_UPLOAD_MAX_MB}mb`, extended: true }));
|
|
55
100
|
// 全局请求超时(30s),防止长查询挂起连接
|
|
56
101
|
this.app.use((req, res, next) => {
|
|
57
102
|
req.setTimeout(30000, () => {
|
|
58
103
|
if (!res.headersSent)
|
|
59
|
-
res
|
|
104
|
+
sendError(res, 408, ErrorCodes.REQUEST_TIMEOUT, 'Request timeout');
|
|
60
105
|
});
|
|
61
106
|
next();
|
|
62
107
|
});
|
|
108
|
+
// API 限流(每分钟 200 次)
|
|
109
|
+
this.app.use('/api', rateLimitMiddleware);
|
|
110
|
+
// 上传体积限制(10MB):对 Content-Length 做预检,避免大包占满内存
|
|
111
|
+
this.app.use('/api', (req, res, next) => {
|
|
112
|
+
const contentLength = parseInt(req.headers['content-length'] || '0', 10);
|
|
113
|
+
if (contentLength > FILE_UPLOAD_MAX_BYTES) {
|
|
114
|
+
sendError(res, 413, ErrorCodes.PAYLOAD_TOO_LARGE, `请求体超过最大限制(${FILE_UPLOAD_MAX_MB}MB)`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
next();
|
|
118
|
+
});
|
|
63
119
|
// 认证中间件(仅 API 路由)
|
|
64
120
|
if (this.options.authToken) {
|
|
65
121
|
this.app.use('/api', authMiddleware(this.options.authToken));
|
|
66
122
|
}
|
|
123
|
+
// 只读降级模式:拦截所有写请求
|
|
124
|
+
const { storage } = this.options;
|
|
125
|
+
if (storage.isReadonly()) {
|
|
126
|
+
this.app.use('/api', (req, res, next) => {
|
|
127
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
|
|
128
|
+
sendError(res, 503, ErrorCodes.READONLY_MODE, '存储服务不可用(只读降级模式)', {
|
|
129
|
+
reason: storage.getReadonlyReason(),
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
next();
|
|
134
|
+
});
|
|
135
|
+
}
|
|
67
136
|
}
|
|
68
137
|
setupRoutes() {
|
|
69
138
|
const { storage, aiProvider, pipelineEngine } = this.options;
|
|
70
|
-
// API
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
139
|
+
// API 路由(兼容 /api/* 与 /api/v1/*)
|
|
140
|
+
const registerAllRoutes = (app) => {
|
|
141
|
+
registerConfigRoutes(app, storage);
|
|
142
|
+
registerPipelineRoutes(app, storage, pipelineEngine);
|
|
143
|
+
registerQualityRoutes(app, storage, aiProvider);
|
|
144
|
+
registerEventRoutes(app, storage);
|
|
145
|
+
registerSessionRoutes(app, storage);
|
|
146
|
+
registerStatsRoutes(app, storage);
|
|
147
|
+
registerKnowledgeRoutes(app, storage);
|
|
148
|
+
registerDaemonRoutes(app, storage);
|
|
149
|
+
registerLogsRoutes(app, storage);
|
|
150
|
+
registerProjectRoutes(app, storage);
|
|
151
|
+
registerConventionRoutes(app, storage);
|
|
152
|
+
registerClaudeMdRoutes(app, storage);
|
|
153
|
+
registerMemoryRoutes(app);
|
|
154
|
+
registerProjectMetaRoutes(app, storage);
|
|
155
|
+
registerSkillRoutes(app);
|
|
156
|
+
registerConventionsRoutes(app, storage);
|
|
157
|
+
registerProjectConventionRoutes(app);
|
|
158
|
+
registerNodeTypeRoutes(app, storage);
|
|
159
|
+
app.use('/api/plans', createPlansRouter(storage, aiProvider));
|
|
160
|
+
app.use('/api/templates', createTemplatesRouter(storage));
|
|
161
|
+
app.use('/api/evolution', createEvolutionRouter(storage));
|
|
162
|
+
};
|
|
163
|
+
// SSE 实时推送端点(不走限流和超时中间件)
|
|
164
|
+
this.app.get('/api/events/stream', (req, res) => {
|
|
165
|
+
// SSE 连接不设超时
|
|
166
|
+
req.setTimeout(0);
|
|
167
|
+
SseBroadcaster.getInstance().addClient(res);
|
|
168
|
+
});
|
|
169
|
+
registerAllRoutes(this.app);
|
|
89
170
|
// 健康检查
|
|
90
|
-
this.app.get('/api/health', (
|
|
91
|
-
|
|
171
|
+
this.app.get('/api/health', (_req, res) => {
|
|
172
|
+
const { storage } = this.options;
|
|
173
|
+
res.json({
|
|
174
|
+
status: storage.isReadonly() ? 'degraded' : 'ok',
|
|
175
|
+
readonly: storage.isReadonly(),
|
|
176
|
+
readonlyReason: storage.isReadonly() ? storage.getReadonlyReason() : undefined,
|
|
177
|
+
timestamp: new Date().toISOString(),
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
// /api/v1 前缀兼容层(重写到现有 /api/* 路由)
|
|
181
|
+
this.app.use('/api/v1', (req, _res, next) => {
|
|
182
|
+
req.url = `/api${req.url}`;
|
|
183
|
+
next();
|
|
92
184
|
});
|
|
93
185
|
}
|
|
94
186
|
setupStaticFiles() {
|
|
@@ -100,7 +192,7 @@ export class WebServer {
|
|
|
100
192
|
this.app.use((req, res) => {
|
|
101
193
|
// API 路由不走 fallback
|
|
102
194
|
if (req.path.startsWith('/api/')) {
|
|
103
|
-
res
|
|
195
|
+
sendError(res, 404, ErrorCodes.NOT_FOUND, 'API 端点不存在');
|
|
104
196
|
return;
|
|
105
197
|
}
|
|
106
198
|
const indexPath = path.resolve(webDir, 'index.html');
|
|
@@ -128,7 +220,7 @@ export class WebServer {
|
|
|
128
220
|
this.app.use((err, req, res, _next) => {
|
|
129
221
|
logger.warn(`[Web] 请求处理失败:${err.message} | ${req.method} ${req.path}`);
|
|
130
222
|
if (!res.headersSent) {
|
|
131
|
-
res
|
|
223
|
+
sendError(res, 500, ErrorCodes.INTERNAL_ERROR, '服务器内部错误');
|
|
132
224
|
}
|
|
133
225
|
});
|
|
134
226
|
}
|
|
@@ -137,6 +229,7 @@ export class WebServer {
|
|
|
137
229
|
try {
|
|
138
230
|
this.server = this.app.listen(this.options.port, () => {
|
|
139
231
|
logger.info(`[Web] 管理后台已启动:http://localhost:${this.options.port}`);
|
|
232
|
+
startHeartbeat();
|
|
140
233
|
resolve();
|
|
141
234
|
});
|
|
142
235
|
}
|
package/dist/web/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAyB,MAAM,SAAS,CAAC;AAEhD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAyB,MAAM,SAAS,CAAC;AAEhD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtE,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,MAAM,oBAAoB,GAAG,MAAM,CAAC;AACpC,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,qBAAqB,GAAG,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAAC;AAC/D,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAoB,CAAC;AAErD,SAAS,WAAW,CAAC,GAAoB;IACvC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACjD,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;IAChE,CAAC;IACD,OAAO,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;AAC7B,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAoB,EAAE,GAAqB,EAAE,IAA0B;IAClG,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,oBAAoB,CAAC,CAAC;IAElE,IAAI,MAAM,CAAC,MAAM,IAAI,cAAc,EAAE,CAAC;QACpC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,YAAY,EAAE,cAAc,EAAE;YAC3D,KAAK,EAAE,cAAc;YACrB,QAAQ,EAAE,oBAAoB;SAC/B,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjB,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC;AACT,CAAC;AAED,WAAW,CAAC,GAAG,EAAE;IACf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,gBAAgB,CAAC,OAAO,EAAE,EAAE,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,oBAAoB,CAAC,CAAC;QAClE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;AACH,CAAC,EAAE,oBAAoB,CAAC,CAAC,KAAK,EAAE,CAAC;AAUjC,MAAM,OAAO,SAAS;IACZ,GAAG,CAAU;IACb,MAAM,GAAkB,IAAI,CAAC;IAC7B,OAAO,CAAmB;IAElC,YAAY,OAAyB;QACnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC;QACrB,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAEO,eAAe;QACrB,sBAAsB;QACtB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE;gBAC3B,sCAAsC;gBACtC,IAAI,CAAC,MAAM,IAAI,8CAA8C,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC3E,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;gBACvC,CAAC;YACH,CAAC;YACD,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,kBAAkB,IAAI,EAAE,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,GAAG,kBAAkB,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAEvF,wBAAwB;QACxB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAC9B,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE;gBACzB,IAAI,CAAC,GAAG,CAAC,WAAW;oBAAE,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;YAC3F,CAAC,CAAC,CAAC;YACH,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;QAEH,oBAAoB;QACpB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;QAE1C,6CAA6C;QAC7C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YACtC,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;YACzE,IAAI,aAAa,GAAG,qBAAqB,EAAE,CAAC;gBAC1C,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,iBAAiB,EAAE,aAAa,kBAAkB,KAAK,CAAC,CAAC;gBACxF,OAAO;YACT,CAAC;YACD,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;QAEH,kBAAkB;QAClB,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,CAAC;QAED,iBAAiB;QACjB,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QACjC,IAAI,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBACtC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC5D,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,aAAa,EAAE,iBAAiB,EAAE;wBAC/D,MAAM,EAAE,OAAO,CAAC,iBAAiB,EAAE;qBACpC,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBACD,IAAI,EAAE,CAAC;YACT,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAEO,WAAW;QACjB,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAE7D,gCAAgC;QAChC,MAAM,iBAAiB,GAAG,CAAC,GAAY,EAAQ,EAAE;YAC/C,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACnC,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;YACrD,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YAChD,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAClC,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACpC,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAClC,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACtC,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACnC,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACjC,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACpC,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACvC,sBAAsB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACrC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YAC1B,yBAAyB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACxC,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACzB,yBAAyB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACxC,+BAA+B,CAAC,GAAG,CAAC,CAAC;YACrC,sBAAsB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACrC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;YAC9D,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC;YAC1D,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5D,CAAC,CAAC;QAEF,yBAAyB;QACzB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC9C,aAAa;YACb,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAClB,cAAc,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE5B,OAAO;QACP,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YACxC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;YACjC,GAAG,CAAC,IAAI,CAAC;gBACP,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;gBAChD,QAAQ,EAAE,OAAO,CAAC,UAAU,EAAE;gBAC9B,cAAc,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,SAAS;gBAC9E,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,iCAAiC;QACjC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;YAC1C,GAAG,CAAC,GAAG,GAAG,OAAO,GAAG,CAAC,GAAG,EAAE,CAAC;YAC3B,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,gBAAgB;QACtB,oBAAoB;QACpB,4CAA4C;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;QACxD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAErC,uCAAuC;QACvC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACxB,oBAAoB;YACpB,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACjC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;gBACvD,OAAO;YACT,CAAC;YAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;YACrD,IAAI,CAAC;gBACH,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7B,IAAI,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;oBAC/C,8BAA8B;oBAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;wBAC3B,MAAM,WAAW,GAAG,iCAAiC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC;wBACxG,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,WAAW,SAAS,CAAC,CAAC;oBAC1D,CAAC;oBACD,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;oBAC1D,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjB,CAAC;qBAAM,CAAC;oBACN,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,wDAAwD,EAAE,CAAC,CAAC;gBAC9F,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAC;gBAC9C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;YAC/F,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,sBAAsB;QACtB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAU,EAAE,GAAoB,EAAE,GAAqB,EAAE,KAA2B,EAAE,EAAE;YACpG,MAAM,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,OAAO,MAAM,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACvE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE;oBACpD,MAAM,CAAC,IAAI,CAAC,kCAAkC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;oBACnE,cAAc,EAAE,CAAC;oBACjB,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;oBACrB,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;oBAC7B,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
export type SseEventType = 'pipeline:status' | 'pipeline:node' | 'quality:issue' | 'session:event' | 'session:status' | 'daemon:status' | 'heartbeat';
|
|
3
|
+
export interface SseMessage {
|
|
4
|
+
type: SseEventType;
|
|
5
|
+
data: unknown;
|
|
6
|
+
ts?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class SseBroadcaster {
|
|
9
|
+
private clients;
|
|
10
|
+
private clientIdCounter;
|
|
11
|
+
static getInstance(): SseBroadcaster;
|
|
12
|
+
/** 注册新的 SSE 客户端连接,返回 clientId */
|
|
13
|
+
addClient(res: Response): string;
|
|
14
|
+
/** 广播消息到所有连接的客户端 */
|
|
15
|
+
broadcast(msg: SseMessage): void;
|
|
16
|
+
/** 发送给单个客户端 */
|
|
17
|
+
private sendToClient;
|
|
18
|
+
get connectionCount(): number;
|
|
19
|
+
private format;
|
|
20
|
+
}
|
|
21
|
+
/** 启动心跳定时器(每 25s),防止代理/浏览器断开空闲连接 */
|
|
22
|
+
export declare function startHeartbeat(): void;
|
|
23
|
+
//# sourceMappingURL=sse-broadcaster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-broadcaster.d.ts","sourceRoot":"","sources":["../../src/web/sse-broadcaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC,MAAM,MAAM,YAAY,GACpB,iBAAiB,GACjB,eAAe,GACf,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,WAAW,CAAC;AAEhB,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAWD,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,eAAe,CAAK;IAE5B,MAAM,CAAC,WAAW,IAAI,cAAc;IAKpC,iCAAiC;IACjC,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM;IAqBhC,oBAAoB;IACpB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAYhC,eAAe;IACf,OAAO,CAAC,YAAY;IAUpB,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,OAAO,CAAC,MAAM;CAIf;AAED,oCAAoC;AACpC,wBAAgB,cAAc,IAAI,IAAI,CAOrC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { logger } from '../utils/logger.js';
|
|
2
|
+
/** 全局单例,daemon 和 web server 共享同一个 broadcaster 实例 */
|
|
3
|
+
let _instance = null;
|
|
4
|
+
export class SseBroadcaster {
|
|
5
|
+
clients = new Map();
|
|
6
|
+
clientIdCounter = 0;
|
|
7
|
+
static getInstance() {
|
|
8
|
+
if (!_instance)
|
|
9
|
+
_instance = new SseBroadcaster();
|
|
10
|
+
return _instance;
|
|
11
|
+
}
|
|
12
|
+
/** 注册新的 SSE 客户端连接,返回 clientId */
|
|
13
|
+
addClient(res) {
|
|
14
|
+
const id = String(++this.clientIdCounter);
|
|
15
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
16
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
17
|
+
res.setHeader('Connection', 'keep-alive');
|
|
18
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
19
|
+
res.flushHeaders();
|
|
20
|
+
this.clients.set(id, { id, res, connectedAt: Date.now() });
|
|
21
|
+
logger.debug(`[SSE] 客户端连接 #${id},当前连接数:${this.clients.size}`);
|
|
22
|
+
res.on('close', () => {
|
|
23
|
+
this.clients.delete(id);
|
|
24
|
+
logger.debug(`[SSE] 客户端断开 #${id},当前连接数:${this.clients.size}`);
|
|
25
|
+
});
|
|
26
|
+
// 立即发送连接确认
|
|
27
|
+
this.sendToClient(id, { type: 'heartbeat', data: { connected: true } });
|
|
28
|
+
return id;
|
|
29
|
+
}
|
|
30
|
+
/** 广播消息到所有连接的客户端 */
|
|
31
|
+
broadcast(msg) {
|
|
32
|
+
if (this.clients.size === 0)
|
|
33
|
+
return;
|
|
34
|
+
const payload = this.format(msg);
|
|
35
|
+
for (const [id, client] of this.clients) {
|
|
36
|
+
try {
|
|
37
|
+
client.res.write(payload);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
this.clients.delete(id);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** 发送给单个客户端 */
|
|
45
|
+
sendToClient(id, msg) {
|
|
46
|
+
const client = this.clients.get(id);
|
|
47
|
+
if (!client)
|
|
48
|
+
return;
|
|
49
|
+
try {
|
|
50
|
+
client.res.write(this.format(msg));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
this.clients.delete(id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
get connectionCount() {
|
|
57
|
+
return this.clients.size;
|
|
58
|
+
}
|
|
59
|
+
format(msg) {
|
|
60
|
+
const payload = JSON.stringify({ ...msg, ts: msg.ts ?? new Date().toISOString() });
|
|
61
|
+
return `event: ${msg.type}\ndata: ${payload}\n\n`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** 启动心跳定时器(每 25s),防止代理/浏览器断开空闲连接 */
|
|
65
|
+
export function startHeartbeat() {
|
|
66
|
+
const broadcaster = SseBroadcaster.getInstance();
|
|
67
|
+
setInterval(() => {
|
|
68
|
+
if (broadcaster.connectionCount > 0) {
|
|
69
|
+
broadcaster.broadcast({ type: 'heartbeat', data: { ts: Date.now() } });
|
|
70
|
+
}
|
|
71
|
+
}, 25_000).unref();
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=sse-broadcaster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-broadcaster.js","sourceRoot":"","sources":["../../src/web/sse-broadcaster.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
export interface ErrorResponse {
|
|
3
|
+
error: {
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
details?: unknown;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export declare function sendError(res: Response, statusCode: number, code: string, message: string, details?: unknown): void;
|
|
10
|
+
export declare const ErrorCodes: {
|
|
11
|
+
readonly BAD_REQUEST: "BAD_REQUEST";
|
|
12
|
+
readonly UNAUTHORIZED: "UNAUTHORIZED";
|
|
13
|
+
readonly FORBIDDEN: "FORBIDDEN";
|
|
14
|
+
readonly NOT_FOUND: "NOT_FOUND";
|
|
15
|
+
readonly CONFLICT: "CONFLICT";
|
|
16
|
+
readonly PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE";
|
|
17
|
+
readonly RATE_LIMITED: "RATE_LIMITED";
|
|
18
|
+
readonly REQUEST_TIMEOUT: "REQUEST_TIMEOUT";
|
|
19
|
+
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
20
|
+
readonly SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE";
|
|
21
|
+
readonly READONLY_MODE: "READONLY_MODE";
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=error-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-response.d.ts","sourceRoot":"","sources":["../../../src/web/utils/error-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;CACH;AAED,wBAAgB,SAAS,CACvB,GAAG,EAAE,QAAQ,EACb,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,OAAO,GAChB,IAAI,CASN;AAED,eAAO,MAAM,UAAU;;;;;;;;;;;;CAeb,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function sendError(res, statusCode, code, message, details) {
|
|
2
|
+
const response = {
|
|
3
|
+
error: {
|
|
4
|
+
code,
|
|
5
|
+
message,
|
|
6
|
+
details,
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
res.status(statusCode).json(response);
|
|
10
|
+
}
|
|
11
|
+
export const ErrorCodes = {
|
|
12
|
+
// 4xx Client Errors
|
|
13
|
+
BAD_REQUEST: 'BAD_REQUEST',
|
|
14
|
+
UNAUTHORIZED: 'UNAUTHORIZED',
|
|
15
|
+
FORBIDDEN: 'FORBIDDEN',
|
|
16
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
17
|
+
CONFLICT: 'CONFLICT',
|
|
18
|
+
PAYLOAD_TOO_LARGE: 'PAYLOAD_TOO_LARGE',
|
|
19
|
+
RATE_LIMITED: 'RATE_LIMITED',
|
|
20
|
+
REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',
|
|
21
|
+
// 5xx Server Errors
|
|
22
|
+
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
23
|
+
SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',
|
|
24
|
+
READONLY_MODE: 'READONLY_MODE',
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=error-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-response.js","sourceRoot":"","sources":["../../../src/web/utils/error-response.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,SAAS,CACvB,GAAa,EACb,UAAkB,EAClB,IAAY,EACZ,OAAe,EACf,OAAiB;IAEjB,MAAM,QAAQ,GAAkB;QAC9B,KAAK,EAAE;YACL,IAAI;YACJ,OAAO;YACP,OAAO;SACR;KACF,CAAC;IACF,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,oBAAoB;IACpB,WAAW,EAAE,aAAa;IAC1B,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,iBAAiB,EAAE,mBAAmB;IACtC,YAAY,EAAE,cAAc;IAC5B,eAAe,EAAE,iBAAiB;IAElC,oBAAoB;IACpB,cAAc,EAAE,gBAAgB;IAChC,mBAAmB,EAAE,qBAAqB;IAC1C,aAAa,EAAE,eAAe;CACtB,CAAC"}
|
|
@@ -22,6 +22,16 @@ export declare const NumberIdsBodySchema: z.ZodObject<{
|
|
|
22
22
|
export declare const FileUploadSchema: z.ZodObject<{
|
|
23
23
|
filename: z.ZodString;
|
|
24
24
|
}, z.core.$strip>;
|
|
25
|
+
export declare const UrlSchema: z.ZodObject<{
|
|
26
|
+
url: z.ZodString;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
/**
|
|
29
|
+
* 校验文件扩展名是否在白名单中
|
|
30
|
+
* @param filename 文件名或 URL
|
|
31
|
+
* @param allowedExtensions 允许的扩展名列表(如 ['.yaml', '.yml', '.json'])
|
|
32
|
+
* @throws Error 如果扩展名不在白名单中
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateFileExtension(filename: string, allowedExtensions: string[]): void;
|
|
25
35
|
/**
|
|
26
36
|
* 解析并校验请求 body,失败时自动返回 400
|
|
27
37
|
* @returns 解析后的数据,或 null(已写入响应)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/web/utils/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIjD,eAAO,MAAM,gBAAgB;;;;;iBAK3B,CAAC;AAEH,eAAO,MAAM,aAAa;;iBAExB,CAAC;AAEH,eAAO,MAAM,mBAAmB;;iBAE9B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;iBAE9B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;iBAE3B,CAAC;AAEH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAC9C,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,GACZ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAUnB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAC/C,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,GACZ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAUnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,GAAG,SAAI,EACP,GAAG,SAAM,EACT,YAAY,SAAK,GAChB,MAAM,CAMR;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,GAAG,SAAQ,EACX,YAAY,SAAI,GACf,MAAM,CAMR;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAM7E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAMhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,SAAI,EACb,SAAS,SAAO,GACf,MAAM,CAQR;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,SAAS,CAAC,EAAE,GAC1B,CAAC,CAKH;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,SAAM,EACf,SAAS,SAAI,GACZ,CAAC,EAAE,CAWL"}
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/web/utils/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIjD,eAAO,MAAM,gBAAgB;;;;;iBAK3B,CAAC;AAEH,eAAO,MAAM,aAAa;;iBAExB,CAAC;AAEH,eAAO,MAAM,mBAAmB;;iBAE9B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;iBAE9B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;iBAE3B,CAAC;AAEH,eAAO,MAAM,SAAS;;iBAEpB,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,EAAE,GAC1B,IAAI,CAKN;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAC9C,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,GACZ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAUnB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAC/C,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,GACZ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAUnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,GAAG,SAAI,EACP,GAAG,SAAM,EACT,YAAY,SAAK,GAChB,MAAM,CAMR;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,GAAG,SAAQ,EACX,YAAY,SAAI,GACf,MAAM,CAMR;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAM7E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAMhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,SAAI,EACb,SAAS,SAAO,GACf,MAAM,CAQR;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,SAAS,CAAC,EAAE,GAC1B,CAAC,CAKH;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,SAAM,EACf,SAAS,SAAI,GACZ,CAAC,EAAE,CAWL"}
|
|
@@ -22,6 +22,21 @@ export const NumberIdsBodySchema = z.object({
|
|
|
22
22
|
export const FileUploadSchema = z.object({
|
|
23
23
|
filename: z.string().regex(/\.(yaml|yml|json|csv)$/i, '只允许上传 .yaml/.json/.csv 文件'),
|
|
24
24
|
});
|
|
25
|
+
export const UrlSchema = z.object({
|
|
26
|
+
url: z.string().url('无效的 URL 格式').regex(/\.(yaml|yml|json|csv)$/i, 'URL 必须指向 .yaml/.json/.csv 文件'),
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* 校验文件扩展名是否在白名单中
|
|
30
|
+
* @param filename 文件名或 URL
|
|
31
|
+
* @param allowedExtensions 允许的扩展名列表(如 ['.yaml', '.yml', '.json'])
|
|
32
|
+
* @throws Error 如果扩展名不在白名单中
|
|
33
|
+
*/
|
|
34
|
+
export function validateFileExtension(filename, allowedExtensions) {
|
|
35
|
+
const ext = filename.toLowerCase().match(/\.[^.]+$/)?.[0];
|
|
36
|
+
if (!ext || !allowedExtensions.includes(ext)) {
|
|
37
|
+
throw new Error(`不允许的文件类型,只允许:${allowedExtensions.join(', ')}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
25
40
|
/**
|
|
26
41
|
* 解析并校验请求 body,失败时自动返回 400
|
|
27
42
|
* @returns 解析后的数据,或 null(已写入响应)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../../src/web/utils/validation.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../../src/web/utils/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e}from"./rolldown-runtime-COnpUsM8.js";import{Dn as t,Ft as n,Gn as r,Ht as i,Rn as a,Vt as o,fn as ee,gr as s,ir as te,o as c,pr as l,s as u,zn as ne}from"./vendor-DRGPi8ui.js";import{S as d,_ as f,c as p,d as m,f as h,g,h as _,l as v,m as y,p as b,s as x,u as S,v as C,x as w,y as T}from"./vendor-charts-9eVsQvUV.js";import{r as E}from"./vendor-query-DqPOMnuX.js";import{c as D}from"./vendor-flow-srkes8If.js";import{J as re,L as ie,St as ae,Y as oe,ht as se,wt as ce,xt as le}from"./client-BhcjNvkG.js";import{a as O,l as k,n as ue,r as A}from"./ui-CDL3BZ13.js";import{t as de}from"./ErrorState-DXHA8lr8.js";import{t as j}from"./exportCsv-CSExJI8h.js";var M=e(s(),1),N=D();function P({children:e,fallback:t,rootMargin:n=`200px`}){let[r,i]=(0,M.useState)(!1),a=(0,M.useRef)(null);return(0,M.useEffect)(()=>{let e=new IntersectionObserver(([t])=>{t.isIntersecting&&(i(!0),e.disconnect())},{rootMargin:n,threshold:.01});return a.current&&e.observe(a.current),()=>e.disconnect()},[n]),(0,N.jsx)(`div`,{ref:a,style:{minHeight:r?`auto`:`400px`},children:r?e:t||(0,N.jsx)(`div`,{className:`flex items-center justify-center h-96 text-gray-400`,children:`加载中...`})})}var F=[`#667eea`,`#10b981`,`#f59e0b`,`#ef4444`,`#8b5cf6`,`#06b6d4`],fe={"7d":7,"30d":30,"90d":90,custom:0};function I(){let[e,s]=(0,M.useState)(`30d`),[D,I]=(0,M.useState)(``),[L,pe]=(0,M.useState)(``),[R,z]=(0,M.useState)(`sessions`),B=fe[e],V=e===`custom`?D?`${D}T00:00:00`:void 0:u(c(new Date,B),`yyyy-MM-dd'T'HH:mm:ss`),H=e===`custom`&&L?`${L}T23:59:59`:void 0,{data:U,isLoading:me,isError:he,refetch:ge}=E({queryKey:[`sessions`,{limit:100,startTime:V,endTime:H}],queryFn:()=>ce({limit:100,start_time:V,end_time:H}).then(e=>e.data)}),{data:W,isLoading:_e,isError:ve,refetch:ye}=E({queryKey:[`events`,{limit:100,startTime:V,endTime:H}],queryFn:()=>oe({limit:100,start_time:V,end_time:H}).then(e=>e.data)}),{data:be,isLoading:xe}=E({queryKey:[`event-stats`],queryFn:()=>re().then(e=>e.data)}),{data:G}=E({queryKey:[`api-usage-stats`,e],queryFn:()=>ie(e===`custom`?90:B).then(e=>e.data),staleTime:6e4}),{data:K}=E({queryKey:[`roi-stats-analytics`],queryFn:()=>ae().then(e=>e.data),staleTime:6e4}),{data:q}=E({queryKey:[`pipelines-analytics`,{limit:100}],queryFn:()=>se({limit:100}).then(e=>e.data),enabled:R===`pipelines`}),{data:J}=E({queryKey:[`quality-history-analytics`,{limit:100}],queryFn:()=>le({limit:100}).then(e=>e.data),enabled:R===`pipelines`}),Se=me||_e||xe,Ce=he||ve,we=()=>{j(`analytics-sessions-${u(new Date,`yyyyMMdd`)}.csv`,[`日期`,`会话数`],Y.map(e=>[e.date,e.count])),j(`analytics-tools-${u(new Date,`yyyyMMdd`)}.csv`,[`工具`,`使用次数`],De.map(e=>[e.name,e.value])),j(`analytics-projects-${u(new Date,`yyyyMMdd`)}.csv`,[`项目`,`事件数`],X.map(e=>[e.name,e.value])),j(`analytics-durations-${u(new Date,`yyyyMMdd`)}.csv`,[`时长区间`,`会话数`],Z.map(e=>[e.range,e.count]))},Y=(0,M.useMemo)(()=>{if(!U?.items)return[];if(e===`custom`){if(!D||!L)return[];let e=new Date(D),t=new Date(L),n=Math.ceil((t.getTime()-e.getTime())/(1e3*60*60*24))+1;return Array.from({length:n},(t,n)=>{let r=u(new Date(e.getTime()+n*24*60*60*1e3),`MM-dd`);return{date:r,count:U.items.filter(e=>u(k(e.start_time),`MM-dd`)===r).length}})}return Array.from({length:B},(e,t)=>{let n=u(c(new Date,B-1-t),`MM-dd`);return{date:n,count:U.items.filter(e=>u(k(e.start_time),`MM-dd`)===n).length}})},[U?.items,B,e,D,L]),Te=(0,M.useMemo)(()=>W?.items?Array.from({length:24},(e,t)=>{let n=W.items.filter(e=>k(e.timestamp).getHours()===t).length;return{hour:`${t}:00`,count:n}}):[],[W?.items]),X=(0,M.useMemo)(()=>{if(!W?.items)return[];let e=W.items.reduce((e,t)=>{let n=t.project_path.split(`/`).pop()||`Unknown`;return e[n]=(e[n]||0)+1,e},{});return Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,10).map(([e,t])=>({name:e,value:t}))},[W?.items]),Ee={Bash:`Bash 命令`,Read:`读取文件`,Edit:`编辑文件`,Write:`写入文件`,Grep:`搜索内容`,Glob:`查找文件`,Agent:`子代理`,WebFetch:`网页获取`,WebSearch:`网页搜索`,TodoWrite:`任务管理`,NotebookEdit:`笔记编辑`,stop:`停止`,notification:`通知`,UserPrompt:`用户输入`,TaskCreate:`创建任务`,TaskUpdate:`更新任务`,TaskList:`任务列表`,TaskGet:`获取任务`,TaskStop:`停止任务`,TaskOutput:`任务输出`},De=(0,M.useMemo)(()=>{if(!W?.items)return[];let e=W.items.reduce((e,t)=>{let n=t.tool_name||`N/A`;return e[n]=(e[n]||0)+1,e},{});return Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,15).map(([e,t])=>({name:Ee[e]||e,value:t}))},[W?.items]),{sessionDurations:Oe,durationBuckets:Z}=(0,M.useMemo)(()=>{if(!U?.items)return{sessionDurations:[],durationBuckets:[]};let e=U.items.filter(e=>e.end_time).map(e=>{let t=(k(e.end_time).getTime()-k(e.start_time).getTime())/1e3/60;return Math.round(t)});return{sessionDurations:e,durationBuckets:[{range:`0-5分钟`,count:e.filter(e=>e<=5).length},{range:`5-15分钟`,count:e.filter(e=>e>5&&e<=15).length},{range:`15-30分钟`,count:e.filter(e=>e>15&&e<=30).length},{range:`30-60分钟`,count:e.filter(e=>e>30&&e<=60).length},{range:`60+分钟`,count:e.filter(e=>e>60).length}]}},[U?.items]),{avgSessionEvents:ke,avgSessionDuration:Ae}=(0,M.useMemo)(()=>{let e=Oe.filter(e=>e>0&&e<=480),t=e.length>0?e.reduce((e,t)=>e+t,0)/e.length:0,n=U?.items.filter(e=>e.event_count>0&&e.event_count<=500)??[];return{avgSessionEvents:n.length>0?n.reduce((e,t)=>e+t.event_count,0)/n.length:0,avgSessionDuration:t}},[U?.items,Oe]),Q=(0,M.useMemo)(()=>{if(!q?.items)return null;let e=q.items,t=e.filter(e=>e.status===`completed`).length;return{completed:t,failed:e.filter(e=>e.status===`failed`).length,completionRate:e.length>0?(t/e.length*100).toFixed(1):`0`,complexityDist:e.reduce((e,t)=>(e[t.complexity]=(e[t.complexity]||0)+1,e),{}),avgNodes:e.length>0?(e.reduce((e,t)=>e+(t.stats?.totalNodes||0),0)/e.length).toFixed(1):`0`,total:e.length}},[q?.items]),$=(0,M.useMemo)(()=>J?.items?Array.from({length:B},(e,t)=>{let n=u(c(new Date,B-1-t),`MM-dd`),r=J.items.filter(e=>u(k(e.created_at),`MM-dd`)===n);return{date:n,total:r.length,unresolved:r.filter(e=>!e.resolved).length}}):[],[J?.items,B]);return Se?(0,N.jsx)(`div`,{className:`flex items-center justify-center h-96`,children:(0,N.jsx)(`div`,{className:`animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600`})}):Ce?(0,N.jsx)(de,{message:`分析数据加载失败`,onRetry:()=>{ge(),ye()}}):(0,N.jsxs)(`div`,{className:`space-y-6`,children:[(0,N.jsx)(A,{children:(0,N.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,N.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,N.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,N.jsx)(te,{className:`h-4 w-4 text-gray-400`}),(0,N.jsx)(`span`,{className:`text-sm text-gray-600`,children:`时间范围:`}),(0,N.jsx)(`div`,{className:`flex gap-1 border border-gray-200 rounded-lg overflow-hidden`,children:[`7d`,`30d`,`90d`,`custom`].map(t=>(0,N.jsx)(`button`,{onClick:()=>s(t),className:O(`px-3 py-1.5 text-sm transition-colors`,e===t?`bg-blue-50 text-blue-700 font-medium`:`text-gray-600 hover:bg-gray-50`),children:t===`7d`?`7 天`:t===`30d`?`30 天`:t===`90d`?`90 天`:`自定义`},t))})]}),(0,N.jsx)(ue,{variant:`outline`,size:`sm`,icon:a,onClick:we,children:`导出数据`})]}),e===`custom`&&(0,N.jsxs)(`div`,{className:`flex items-center gap-3 pl-6`,children:[(0,N.jsx)(`label`,{className:`text-sm text-gray-600`,children:`开始日期:`}),(0,N.jsx)(`input`,{type:`date`,value:D,onChange:e=>I(e.target.value),className:`px-3 py-1.5 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500`}),(0,N.jsx)(`label`,{className:`text-sm text-gray-600`,children:`结束日期:`}),(0,N.jsx)(`input`,{type:`date`,value:L,onChange:e=>pe(e.target.value),className:`px-3 py-1.5 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500`})]})]})}),(0,N.jsxs)(`div`,{className:`flex gap-1 bg-gray-100 p-1 rounded-lg w-fit`,children:[(0,N.jsxs)(`button`,{onClick:()=>z(`sessions`),className:O(`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors`,R===`sessions`?`bg-white text-gray-900 shadow-sm`:`text-gray-600 hover:text-gray-900`),children:[(0,N.jsx)(l,{className:`h-4 w-4`}),`会话分析`]}),(0,N.jsxs)(`button`,{onClick:()=>z(`pipelines`),className:O(`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors`,R===`pipelines`?`bg-white text-gray-900 shadow-sm`:`text-gray-600 hover:text-gray-900`),children:[(0,N.jsx)(t,{className:`h-4 w-4`}),`Pipeline 分析`]})]}),R===`sessions`&&(0,N.jsxs)(`div`,{className:`space-y-6`,children:[K&&(0,N.jsx)(`div`,{className:`grid grid-cols-2 md:grid-cols-3 gap-3`,children:[{label:`本周会话`,curr:K.week.sessions,pct:K.weekOverWeek.sessions},{label:`本周写入操作`,curr:K.week.writeOps,pct:K.weekOverWeek.writeOps},{label:`本周节省时间`,curr:`${K.week.timeSavedHours}h`,pct:K.weekOverWeek.timeSaved},{label:`本周质量拦截`,curr:K.week.qualityBlocked,pct:K.weekOverWeek.qualityBlocked},{label:`本月会话`,curr:K.month.sessions,pct:K.monthOverMonth.sessions},{label:`本月质量拦截`,curr:K.month.qualityBlocked,pct:K.monthOverMonth.qualityBlocked}].map(e=>(0,N.jsxs)(A,{className:`!p-3`,children:[(0,N.jsx)(`div`,{className:`text-xs text-gray-500 mb-1`,children:e.label}),(0,N.jsxs)(`div`,{className:`flex items-end justify-between`,children:[(0,N.jsx)(`span`,{className:`text-xl font-bold text-gray-900`,children:e.curr}),e.pct!==null&&e.pct!==void 0?(0,N.jsxs)(`span`,{className:O(`flex items-center gap-0.5 text-xs font-medium`,e.pct>0?`text-green-600`:e.pct<0?`text-red-500`:`text-gray-400`),children:[e.pct>0?(0,N.jsx)(o,{className:`h-3 w-3`}):e.pct<0?(0,N.jsx)(i,{className:`h-3 w-3`}):(0,N.jsx)(ee,{className:`h-3 w-3`}),e.pct>0?`+`:``,e.pct,`%`]}):(0,N.jsx)(`span`,{className:`text-xs text-gray-300`,children:`—`})]}),(0,N.jsx)(`div`,{className:`text-xs text-gray-400 mt-0.5`,children:`较上期`})]},e.label))}),(0,N.jsxs)(`div`,{className:`grid grid-cols-2 md:grid-cols-4 gap-4`,children:[(0,N.jsxs)(A,{className:`text-center !p-4`,children:[(0,N.jsx)(l,{className:`h-7 w-7 text-indigo-500 mx-auto mb-2`}),(0,N.jsx)(`div`,{className:`text-2xl font-bold text-gray-900`,children:ke.toFixed(1)}),(0,N.jsx)(`div`,{className:`text-sm text-gray-500`,children:`平均会话事件数`})]}),(0,N.jsxs)(A,{className:`text-center !p-4`,children:[(0,N.jsx)(r,{className:`h-7 w-7 text-green-500 mx-auto mb-2`}),(0,N.jsx)(`div`,{className:`text-2xl font-bold text-gray-900`,children:Ae.toFixed(1)}),(0,N.jsx)(`div`,{className:`text-sm text-gray-500`,children:`平均会话时长(分钟)`})]}),(0,N.jsxs)(A,{className:`text-center !p-4`,children:[(0,N.jsx)(n,{className:`h-7 w-7 text-amber-500 mx-auto mb-2`}),(0,N.jsx)(`div`,{className:`text-2xl font-bold text-gray-900`,children:W?.total||0}),(0,N.jsx)(`div`,{className:`text-sm text-gray-500`,children:`总事件数`})]}),(0,N.jsxs)(A,{className:`text-center !p-4`,children:[(0,N.jsx)(o,{className:`h-7 w-7 text-purple-500 mx-auto mb-2`}),(0,N.jsx)(`div`,{className:`text-2xl font-bold text-gray-900`,children:U?.total||0}),(0,N.jsx)(`div`,{className:`text-sm text-gray-500`,children:`总会话数`})]})]}),(0,N.jsxs)(`div`,{className:`grid grid-cols-1 xl:grid-cols-2 gap-6`,children:[(0,N.jsx)(A,{title:`最近 ${B} 天会话趋势`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(x,{data:Y,children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{dataKey:`date`}),(0,N.jsx)(m,{}),(0,N.jsx)(T,{}),(0,N.jsx)(y,{type:`monotone`,dataKey:`count`,stroke:`#667eea`,fill:`#667eea`,fillOpacity:.3})]})})})}),(0,N.jsx)(A,{title:`24 小时事件分布`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(S,{data:Te,children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{dataKey:`hour`}),(0,N.jsx)(m,{}),(0,N.jsx)(T,{}),(0,N.jsx)(_,{type:`monotone`,dataKey:`count`,stroke:`#10b981`,strokeWidth:2})]})})})}),(0,N.jsx)(A,{title:`项目活跃度 Top 10`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(v,{data:X,layout:`vertical`,children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{type:`number`}),(0,N.jsx)(m,{dataKey:`name`,type:`category`,width:100}),(0,N.jsx)(T,{}),(0,N.jsx)(b,{dataKey:`value`,fill:`#667eea`})]})})})}),(0,N.jsx)(A,{title:`工具使用排行 Top 15`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(v,{data:De,children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{dataKey:`name`,angle:-45,textAnchor:`end`,height:80}),(0,N.jsx)(m,{}),(0,N.jsx)(T,{}),(0,N.jsx)(b,{dataKey:`value`,fill:`#10b981`})]})})})}),(0,N.jsx)(A,{title:`会话时长分布`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(p,{children:[(0,N.jsx)(f,{data:Z,cx:`50%`,cy:`50%`,labelLine:!1,label:({range:e,count:t})=>`${e}: ${t}`,outerRadius:80,fill:`#8884d8`,dataKey:`count`,children:Z.map((e,t)=>(0,N.jsx)(C,{fill:F[t%F.length]},`cell-${t}`))}),(0,N.jsx)(T,{})]})})})}),(0,N.jsx)(A,{title:`事件类型趋势`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(x,{data:be?.stats.slice(0,30)||[],children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{dataKey:`date`}),(0,N.jsx)(m,{}),(0,N.jsx)(T,{}),(0,N.jsx)(w,{}),(0,N.jsx)(y,{type:`monotone`,dataKey:`count`,stackId:`1`,stroke:`#667eea`,fill:`#667eea`})]})})})})]}),G&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`div`,{className:`flex items-center gap-2 mt-2`,children:[(0,N.jsx)(ne,{className:`h-5 w-5 text-gray-500`}),(0,N.jsx)(`h2`,{className:`text-base font-semibold text-gray-800`,children:`Token 与费用统计`})]}),(0,N.jsxs)(`div`,{className:`grid grid-cols-2 md:grid-cols-4 gap-4`,children:[(0,N.jsxs)(A,{className:`text-center !p-4`,children:[(0,N.jsxs)(`div`,{className:`text-2xl font-bold text-gray-900`,children:[`$`,G.summary.totalCost.toFixed(4)]}),(0,N.jsx)(`div`,{className:`text-sm text-gray-500`,children:`总费用(USD)`})]}),(0,N.jsxs)(A,{className:`text-center !p-4`,children:[(0,N.jsxs)(`div`,{className:`text-2xl font-bold text-gray-900`,children:[(G.summary.totalTokens/1e3).toFixed(1),`K`]}),(0,N.jsx)(`div`,{className:`text-sm text-gray-500`,children:`总 Token 数`})]}),(0,N.jsxs)(A,{className:`text-center !p-4`,children:[(0,N.jsxs)(`div`,{className:`text-2xl font-bold text-gray-900`,children:[(G.summary.totalTokensIn/1e3).toFixed(1),`K`]}),(0,N.jsx)(`div`,{className:`text-sm text-gray-500`,children:`输入 Token`})]}),(0,N.jsxs)(A,{className:`text-center !p-4`,children:[(0,N.jsxs)(`div`,{className:`text-2xl font-bold text-gray-900`,children:[(G.summary.totalTokensOut/1e3).toFixed(1),`K`]}),(0,N.jsx)(`div`,{className:`text-sm text-gray-500`,children:`输出 Token`})]})]}),(0,N.jsxs)(`div`,{className:`grid grid-cols-1 xl:grid-cols-2 gap-6`,children:[(0,N.jsx)(A,{title:`每日 Token 趋势`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(x,{data:G.dailyTrend,children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{dataKey:`date`}),(0,N.jsx)(m,{}),(0,N.jsx)(T,{formatter:e=>`${(e/1e3).toFixed(1)}K`}),(0,N.jsx)(w,{}),(0,N.jsx)(y,{type:`monotone`,dataKey:`tokens_in`,name:`输入`,stroke:`#667eea`,fill:`#667eea`,fillOpacity:.3,stackId:`1`}),(0,N.jsx)(y,{type:`monotone`,dataKey:`tokens_out`,name:`输出`,stroke:`#10b981`,fill:`#10b981`,fillOpacity:.3,stackId:`1`})]})})})}),(0,N.jsx)(A,{title:`每日费用趋势(USD)`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(v,{data:G.dailyTrend,children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{dataKey:`date`}),(0,N.jsx)(m,{tickFormatter:e=>`$${e.toFixed(3)}`}),(0,N.jsx)(T,{formatter:e=>`$${e.toFixed(4)}`}),(0,N.jsx)(b,{dataKey:`cost_usd`,name:`费用`,fill:`#f59e0b`})]})})})}),G.byModel.length>0&&(0,N.jsx)(A,{title:`按模型费用分布`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(p,{children:[(0,N.jsx)(f,{data:G.byModel,dataKey:`cost`,nameKey:`model`,cx:`50%`,cy:`50%`,outerRadius:80,label:({model:e,cost:t})=>`${e}: $${t.toFixed(4)}`,children:G.byModel.map((e,t)=>(0,N.jsx)(C,{fill:F[t%F.length]},t))}),(0,N.jsx)(T,{formatter:e=>`$${e.toFixed(4)}`})]})})})})]})]})]}),R===`pipelines`&&Q&&(0,N.jsxs)(`div`,{className:`space-y-6`,children:[(0,N.jsxs)(`div`,{className:`grid grid-cols-1 md:grid-cols-4 gap-4`,children:[(0,N.jsxs)(A,{className:`text-center`,children:[(0,N.jsx)(`div`,{className:`text-3xl font-bold text-blue-600`,children:Q.total}),(0,N.jsx)(`div`,{className:`text-sm text-gray-600 mt-1`,children:`总 Pipeline`})]}),(0,N.jsxs)(A,{className:`text-center`,children:[(0,N.jsxs)(`div`,{className:`text-3xl font-bold text-green-600`,children:[Q.completionRate,`%`]}),(0,N.jsx)(`div`,{className:`text-sm text-gray-600 mt-1`,children:`完成率`})]}),(0,N.jsxs)(A,{className:`text-center`,children:[(0,N.jsx)(`div`,{className:`text-3xl font-bold text-purple-600`,children:Q.avgNodes}),(0,N.jsx)(`div`,{className:`text-sm text-gray-600 mt-1`,children:`平均节点数`})]}),(0,N.jsxs)(A,{className:`text-center`,children:[(0,N.jsx)(`div`,{className:`text-3xl font-bold text-red-600`,children:Q.failed}),(0,N.jsx)(`div`,{className:`text-sm text-gray-600 mt-1`,children:`失败数`})]})]}),(0,N.jsx)(A,{title:`复杂度分布`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(v,{data:Object.entries(Q.complexityDist).map(([e,t])=>({name:e===`simple`?`简单`:e===`moderate`?`中等`:`复杂`,value:t})),children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{dataKey:`name`}),(0,N.jsx)(m,{}),(0,N.jsx)(T,{}),(0,N.jsx)(b,{dataKey:`value`,name:`数量`,fill:`#667eea`})]})})})}),$.length>0&&(0,N.jsx)(A,{title:`质量问题趋势`,children:(0,N.jsx)(P,{children:(0,N.jsx)(d,{width:`100%`,height:250,children:(0,N.jsxs)(S,{data:$,children:[(0,N.jsx)(g,{strokeDasharray:`3 3`}),(0,N.jsx)(h,{dataKey:`date`}),(0,N.jsx)(m,{}),(0,N.jsx)(T,{}),(0,N.jsx)(w,{}),(0,N.jsx)(_,{type:`monotone`,dataKey:`total`,name:`总问题`,stroke:`#667eea`,strokeWidth:2}),(0,N.jsx)(_,{type:`monotone`,dataKey:`unresolved`,name:`未解决`,stroke:`#ef4444`,strokeWidth:2})]})})})})]})]})}export{I as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as e}from"./vendor-flow-srkes8If.js";var t=e();function n({total:e,done:n,label:r=`处理中`}){let i=e>0?Math.round(n/e*100):0;return(0,t.jsxs)(`div`,{className:`flex items-center gap-3 text-sm`,children:[(0,t.jsxs)(`span`,{className:`text-gray-500 whitespace-nowrap`,children:[r,` `,n,`/`,e]}),(0,t.jsx)(`div`,{className:`flex-1 h-1.5 bg-gray-100 rounded-full overflow-hidden min-w-[80px]`,children:(0,t.jsx)(`div`,{className:`h-full bg-blue-500 rounded-full transition-all duration-300`,style:{width:`${i}%`}})}),(0,t.jsxs)(`span`,{className:`text-gray-400 w-8 text-right`,children:[i,`%`]})]})}export{n as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Qn as e}from"./vendor-DRGPi8ui.js";import{n as t}from"./vendor-react-DJI9oneq.js";import{c as n}from"./vendor-flow-srkes8If.js";var r=n();function i({items:n}){return(0,r.jsx)(`nav`,{className:`flex items-center gap-2 text-sm text-gray-600`,children:n.map((n,i)=>(0,r.jsxs)(`div`,{className:`flex items-center gap-2`,children:[i>0&&(0,r.jsx)(e,{className:`h-3.5 w-3.5 text-gray-400`}),n.path?(0,r.jsx)(t,{to:n.path,className:`hover:text-gray-900 transition-colors`,children:n.label}):(0,r.jsx)(`span`,{className:`text-gray-900 font-medium`,children:n.label})]},i))})}export{i as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e}from"./rolldown-runtime-COnpUsM8.js";import{Bn as t,Bt as n,Fn as r,In as i,Lt as a,Qt as o,Rn as s,Tn as c,Vn as l,Yn as u,gr as d,hn as f,nn as p,qn as m,rn as h,s as g,tn as _,zt as v}from"./vendor-DRGPi8ui.js";import{a as y,n as b,r as x}from"./vendor-query-DqPOMnuX.js";import{c as S}from"./vendor-flow-srkes8If.js";import{A as C,Bt as w,Gt as T,H as E,Lt as D,Qt as O,R as k,U as A,a as j,at as M,h as N,it as P,m as F,q as I,r as L,tn as ee,un as R}from"./client-BhcjNvkG.js";import{a as z,n as B,r as V,t as te}from"./ui-CDL3BZ13.js";import{n as H,t as U}from"./vendor-motion-CQmdgnI8.js";import{t as W}from"./ConfirmDialog-CVzB7X5y.js";var G=e(d(),1),K=S();function ne({onConfirm:e}){let n=y(),[a,o]=(0,G.useState)(!1),[l,u]=(0,G.useState)(``),[d,p]=(0,G.useState)(!1),[m,_]=(0,G.useState)({}),{data:v,refetch:S}=x({queryKey:[`backups`],queryFn:()=>D().then(e=>e.data)}),{data:C}=x({queryKey:[`daemon-status`],queryFn:()=>I().then(e=>e.data)}),w=b({mutationFn:()=>j(a?{encrypt:!0,password:l}:{}),onSuccess:()=>{R(`备份创建成功`,`success`),u(``),o(!1),S()},onError:()=>R(`备份创建失败`,`error`)}),E=b({mutationFn:({filename:e,password:t})=>T(e,t),onSuccess:()=>{R(`数据库恢复成功,daemon 将自动重启`,`success`),n.clear()},onError:()=>R(`恢复失败,请检查密码或文件完整性`,`error`)});return(0,K.jsxs)(`div`,{className:`space-y-4`,children:[(0,K.jsx)(V,{children:(0,K.jsxs)(`div`,{className:`flex items-start justify-between`,children:[(0,K.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,K.jsx)(`div`,{className:`flex-shrink-0 w-10 h-10 rounded-full bg-blue-50 flex items-center justify-center`,children:(0,K.jsx)(c,{className:`h-5 w-5 text-blue-600`})}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`h3`,{className:`font-medium text-gray-900`,children:`当前数据库`}),(0,K.jsxs)(`p`,{className:`text-sm text-gray-500 mt-1`,children:[`内存占用:`,C?.memory?`${(C.memory.heapUsed/1024/1024).toFixed(2)} MB`:`加载中...`]})]})]}),(0,K.jsxs)(`div`,{className:`flex flex-col items-end gap-2`,children:[(0,K.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,K.jsxs)(`label`,{className:`flex items-center gap-1.5 text-sm text-gray-600 cursor-pointer`,children:[(0,K.jsx)(`input`,{type:`checkbox`,checked:a,onChange:e=>o(e.target.checked),className:`rounded`}),(0,K.jsx)(f,{className:`h-3.5 w-3.5`}),`加密备份`]})}),a&&(0,K.jsxs)(`div`,{className:`relative`,children:[(0,K.jsx)(`input`,{type:d?`text`:`password`,placeholder:`备份密码(≥8位)`,value:l,onChange:e=>u(e.target.value),className:`px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 pr-8 w-48`}),(0,K.jsx)(`button`,{type:`button`,onClick:()=>p(e=>!e),className:`absolute right-2 top-1/2 -translate-y-1/2 text-gray-400`,children:d?(0,K.jsx)(i,{className:`h-3.5 w-3.5`}):(0,K.jsx)(r,{className:`h-3.5 w-3.5`})})]}),(0,K.jsx)(B,{variant:`primary`,size:`sm`,icon:t,onClick:()=>w.mutate(),loading:w.isPending,disabled:a&&l.length<8,children:`立即备份`})]})]})}),(0,K.jsxs)(V,{children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between mb-4`,children:[(0,K.jsx)(`h3`,{className:`font-medium text-gray-900`,children:`备份历史`}),(0,K.jsx)(B,{variant:`ghost`,size:`sm`,icon:h,onClick:()=>void S(),children:`刷新`})]}),v&&v.items.length>0?(0,K.jsx)(`div`,{className:`space-y-2`,children:v.items.map(t=>(0,K.jsxs)(`div`,{className:`flex flex-col gap-2 py-3 px-4 bg-gray-50 rounded-lg`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(`p`,{className:`text-sm font-medium text-gray-900 truncate`,children:t.filename}),t.encrypted&&(0,K.jsxs)(`span`,{className:`flex items-center gap-0.5 text-xs text-amber-600 bg-amber-50 px-1.5 py-0.5 rounded`,children:[(0,K.jsx)(f,{className:`h-3 w-3`}),` 已加密`]}),!t.hasChecksum&&(0,K.jsx)(`span`,{className:`text-xs text-gray-400 bg-gray-100 px-1.5 py-0.5 rounded`,children:`无校验`})]}),(0,K.jsxs)(`p`,{className:`text-xs text-gray-500 mt-0.5`,children:[g(new Date(t.createdAt),`yyyy-MM-dd HH:mm:ss`),` · `,t.sizeMb,` MB`]})]}),(0,K.jsxs)(`div`,{className:`flex items-center gap-2 ml-3 flex-shrink-0`,children:[(0,K.jsx)(B,{variant:`outline`,size:`sm`,onClick:()=>e({title:`恢复数据库`,description:`确定要从 ${t.filename} 恢复数据库吗?当前数据库将被覆盖(会自动备份)。恢复后 daemon 将自动重启。`,onConfirm:()=>E.mutate({filename:t.filename,password:m[t.filename]})}),loading:E.isPending&&E.variables?.filename===t.filename,children:`恢复`}),(0,K.jsx)(`a`,{href:k(t.filename),download:t.filename,children:(0,K.jsx)(B,{variant:`ghost`,size:`sm`,icon:s,children:`下载`})})]})]}),t.encrypted&&(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(f,{className:`h-3.5 w-3.5 text-amber-500 flex-shrink-0`}),(0,K.jsx)(`input`,{type:`password`,placeholder:`输入解密密码后点击恢复`,value:m[t.filename]||``,onChange:e=>_(n=>({...n,[t.filename]:e.target.value})),className:`flex-1 px-2 py-1 text-xs border border-amber-200 rounded focus:outline-none focus:ring-1 focus:ring-amber-400`})]})]},t.filename))}):(0,K.jsx)(`div`,{className:`text-center py-8 text-gray-400 text-sm`,children:`暂无备份记录`})]}),(0,K.jsxs)(V,{children:[(0,K.jsx)(`div`,{className:`flex items-center justify-between mb-4`,children:(0,K.jsx)(`h3`,{className:`font-medium text-gray-900`,children:`数据管理`})}),(0,K.jsxs)(`div`,{className:`space-y-3`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between py-3 px-4 bg-gray-50 rounded-lg`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`text-sm font-medium text-gray-900`,children:`清理历史事件`}),(0,K.jsx)(`p`,{className:`text-xs text-gray-500 mt-0.5`,children:`删除超过指定天数的事件记录`})]}),(0,K.jsx)(B,{variant:`outline`,size:`sm`,onClick:()=>e({title:`清理历史事件`,description:`确定要清理 30 天前的事件记录吗?此操作不可撤销。`,onConfirm:()=>{L.post(`/events/purge`,{retentionDays:30}).then(()=>R(`事件清理完成`,`success`)).catch(()=>R(`事件清理失败`,`error`))}}),children:`清理 30 天前`})]}),(0,K.jsxs)(`div`,{className:`flex items-center justify-between py-3 px-4 bg-gray-50 rounded-lg`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:`text-sm font-medium text-gray-900`,children:`数据库优化`}),(0,K.jsx)(`p`,{className:`text-xs text-gray-500 mt-0.5`,children:`执行 VACUUM 回收空间,提升性能`})]}),(0,K.jsx)(B,{variant:`outline`,size:`sm`,onClick:()=>e({title:`数据库优化`,description:`确定要执行 VACUUM 优化吗?此操作可能需要几分钟。`,onConfirm:()=>{L.post(`/config/vacuum`).then(()=>R(`数据库优化完成`,`success`)).catch(()=>R(`数据库优化失败`,`error`))}}),children:`立即优化`})]})]})]})]})}function re({onConfirm:e}){let{data:t,refetch:n}=x({queryKey:[`memory-caches`],queryFn:()=>P().then(e=>e.data),refetchInterval:5e3}),{data:r}=x({queryKey:[`memory-process`],queryFn:()=>M().then(e=>e.data),refetchInterval:5e3}),i=b({mutationFn:e=>N(e),onSuccess:()=>{R(`缓存已清空`,`success`),n()},onError:()=>R(`清空失败`,`error`)}),a=b({mutationFn:()=>F(),onSuccess:()=>{R(`所有缓存已清空`,`success`),n()},onError:()=>R(`清空失败`,`error`)}),o=b({mutationFn:()=>O(),onSuccess:()=>R(`GC 已触发`,`success`),onError:()=>R(`GC 触发失败`,`error`)});return(0,K.jsxs)(`div`,{className:`space-y-4`,children:[(0,K.jsx)(V,{children:(0,K.jsxs)(`div`,{className:`flex items-start justify-between mb-4`,children:[(0,K.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,K.jsx)(`div`,{className:`flex-shrink-0 w-10 h-10 rounded-full bg-purple-50 flex items-center justify-center`,children:(0,K.jsx)(l,{className:`h-5 w-5 text-purple-600`})}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`h3`,{className:`font-medium text-gray-900`,children:`进程内存`}),r&&(0,K.jsxs)(`div`,{className:`text-sm text-gray-600 mt-2 space-y-1`,children:[(0,K.jsxs)(`div`,{children:[`RSS: `,r.rssMB,` MB`]}),(0,K.jsxs)(`div`,{children:[`堆已用: `,r.heapUsedMB,` MB / `,r.heapTotalMB,` MB`]}),(0,K.jsxs)(`div`,{children:[`外部: `,r.externalMB,` MB`]})]})]})]}),(0,K.jsx)(B,{variant:`outline`,size:`sm`,onClick:()=>o.mutate(),loading:o.isPending,children:`触发 GC`})]})}),(0,K.jsxs)(V,{children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between mb-4`,children:[(0,K.jsx)(`h3`,{className:`font-medium text-gray-900`,children:`LRU 缓存`}),(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(B,{variant:`ghost`,size:`sm`,icon:h,onClick:()=>void n(),children:`刷新`}),(0,K.jsx)(B,{variant:`outline`,size:`sm`,onClick:()=>e({title:`清空所有缓存`,description:`确定要清空所有 LRU 缓存吗?`,onConfirm:()=>a.mutate()}),loading:a.isPending,children:`清空全部`})]})]}),t&&t.caches.length>0?(0,K.jsx)(`div`,{className:`space-y-2`,children:t.caches.map(e=>(0,K.jsxs)(`div`,{className:`flex items-center justify-between py-3 px-4 bg-gray-50 rounded-lg`,children:[(0,K.jsxs)(`div`,{className:`flex-1`,children:[(0,K.jsx)(`p`,{className:`text-sm font-medium text-gray-900`,children:e.name}),(0,K.jsxs)(`p`,{className:`text-xs text-gray-500 mt-0.5`,children:[e.size,` / `,e.maxSize,` 条`,e.hitRate!==void 0&&` · 命中率 ${(e.hitRate*100).toFixed(1)}%`]})]}),(0,K.jsx)(B,{variant:`ghost`,size:`sm`,onClick:()=>i.mutate(e.name),loading:i.isPending&&i.variables===e.name,children:`清空`})]},e.name))}):(0,K.jsx)(`div`,{className:`text-center py-8 text-gray-400 text-sm`,children:`暂无缓存数据`})]})]})}function ie({currentConfig:e,onRestore:t,onConfirm:n}){let[r,i]=(0,G.useState)(null),{data:a}=x({queryKey:[`config-history`],queryFn:()=>A().then(e=>e.data)});return(0,K.jsxs)(V,{children:[(0,K.jsx)(`div`,{className:`flex items-center justify-between mb-4`,children:(0,K.jsx)(`h3`,{className:`font-medium text-gray-900`,children:`最近 10 次配置变更`})}),a&&a.items.length>0?(0,K.jsx)(`div`,{className:`space-y-2`,children:a.items.map(a=>{let o=r===String(a.id),s=e,c=[];if(o&&s){let e=(t,n=``)=>typeof t!=`object`||!t?{[n]:JSON.stringify(t)}:Object.entries(t).reduce((t,[r,i])=>{let a=n?`${n}.${r}`:r;return typeof i==`object`&&i&&!Array.isArray(i)?Object.assign(t,e(i,a)):t[a]=JSON.stringify(i),t},{}),t=e(a.config),n=e(s);new Set([...Object.keys(t),...Object.keys(n)]).forEach(e=>{let r=t[e]??`(无)`,i=n[e]??`(无)`;r!==i&&c.push({key:e,old:r,cur:i})})}return(0,K.jsxs)(`div`,{className:`py-3 px-4 bg-gray-50 rounded-lg`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsxs)(`span`,{className:`text-sm font-medium text-gray-900`,children:[`#`,a.id]}),(0,K.jsx)(`span`,{className:`text-xs text-gray-500`,children:g(new Date(a.savedAt),`yyyy-MM-dd HH:mm:ss`)})]}),(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(B,{variant:`ghost`,size:`sm`,onClick:()=>i(o?null:String(a.id)),children:o?`收起对比`:`与当前对比`}),(0,K.jsx)(B,{variant:`outline`,size:`sm`,onClick:()=>n({title:`确认恢复配置`,description:`确定要恢复到 ${g(new Date(a.savedAt),`yyyy-MM-dd HH:mm:ss`)} 的配置吗?当前配置将被覆盖。`,onConfirm:()=>t(a.config)}),children:`恢复到此版本`})]})]}),(0,K.jsx)(H,{children:o&&(0,K.jsx)(U.div,{initial:{height:0,opacity:0},animate:{height:`auto`,opacity:1},exit:{height:0,opacity:0},transition:{duration:.2},className:`overflow-hidden`,children:(0,K.jsxs)(`div`,{className:`mt-3 rounded border border-gray-200 overflow-hidden text-xs font-mono`,children:[(0,K.jsxs)(`div`,{className:`grid grid-cols-3 bg-gray-100 px-3 py-1.5 text-gray-500 font-medium text-xs`,children:[(0,K.jsx)(`span`,{children:`配置项`}),(0,K.jsx)(`span`,{className:`text-red-600`,children:`历史值`}),(0,K.jsx)(`span`,{className:`text-green-600`,children:`当前值`})]}),c.length===0?(0,K.jsx)(`div`,{className:`px-3 py-3 text-gray-400 text-center`,children:`与当前配置完全一致`}):c.map(({key:e,old:t,cur:n})=>(0,K.jsxs)(`div`,{className:`grid grid-cols-3 px-3 py-2 border-t border-gray-100 hover:bg-gray-50`,children:[(0,K.jsx)(`span`,{className:`text-gray-700 truncate`,children:e}),(0,K.jsx)(`span`,{className:`text-red-600 truncate`,children:t}),(0,K.jsx)(`span`,{className:`text-green-600 truncate`,children:n})]},e))]})})})]},a.id)})}):(0,K.jsx)(`div`,{className:`text-center py-8 text-gray-400 text-sm`,children:`暂无历史记录`})]})}function ae({content:e,children:t,className:n}){let[r,i]=(0,G.useState)(!1),[a,o]=(0,G.useState)(`top`),s=(0,G.useRef)(null);return(0,G.useEffect)(()=>{r&&s.current&&o(s.current.getBoundingClientRect().top<80?`bottom`:`top`)},[r]),(0,K.jsxs)(`span`,{ref:s,className:z(`relative inline-flex items-center cursor-help`,n),onMouseEnter:()=>i(!0),onMouseLeave:()=>i(!1),children:[t,r&&(0,K.jsx)(`span`,{className:z(`absolute z-50 w-max max-w-xs px-2.5 py-1.5 text-xs text-white bg-gray-800 rounded-lg shadow-lg pointer-events-none whitespace-normal`,a===`top`?`bottom-full mb-1.5 left-1/2 -translate-x-1/2`:`top-full mt-1.5 left-1/2 -translate-x-1/2`),children:e})]})}var q={Pipeline:`自动化工作流,将需求分解为多个节点依次执行`,Daemon:`后台守护进程,监听 Claude Code 事件并驱动编排引擎`,Hook:`Claude Code 生命周期钩子,在工具调用前后触发`,Distill:`将会话事件提炼为结构化知识,沉淀到知识图谱`,"Quality Gate":`质量门禁,在关键节点检测代码/文档质量问题`,Convention:`项目规范配置,定义节点类型、技能绑定和质量标准`,Skill:`可复用的 Claude Code 技能脚本,绑定到 Pipeline 节点`,ROI:`投资回报率,统计 AI 辅助节省的时间和 Token 用量`,Token:`LLM 处理的最小文本单元,影响 API 调用成本`,SSE:`Server-Sent Events,服务端向客户端推送实时事件的协议`},J=[{key:`distill`,title:`蒸馏配置`,description:`AI 知识蒸馏的触发条件和参数`,fields:[{key:`provider`,label:`提供商`,type:`select`,options:[{value:`claude`,label:`Claude`},{value:`openai`,label:`OpenAI`},{value:`ollama`,label:`Ollama`}]},{key:`auto_trigger`,label:`自动触发`,type:`boolean`,description:`达到事件阈值时自动蒸馏`},{key:`event_threshold`,label:`事件阈值`,type:`number`,description:`触发蒸馏的最小事件数`,min:1,max:1e4},{key:`timer_interval_minutes`,label:`定时器间隔(分钟)`,type:`number`,min:1,max:1440},{key:`model`,label:`模型`,type:`text`},{key:`api_key`,label:`API Key`,type:`text`,sensitive:!0,description:`留空自动从环境变量获取`},{key:`base_url`,label:`API 地址`,type:`text`,description:`留空使用默认地址`}]},{key:`autopilot`,title:`自动驾驶`,description:`意图分析、质量门禁、Pipeline 编排`,fields:[{key:`enabled`,label:`启用`,type:`boolean`},{key:`intent_analysis`,label:`意图分析`,type:`boolean`,description:`AI 分析用户意图和任务复杂度`},{key:`quality_gate`,label:`质量门禁`,type:`boolean`,description:`代码变更时自动审查质量`},{key:`quality_review_interval`,label:`审查间隔`,type:`number`,description:`每 N 次 Write/Edit 触发一次审查`,min:1,max:100},{key:`auto_start_pipeline`,label:`自动启动流水线`,type:`boolean`},{key:`pipeline_confirm_mode`,label:`确认模式`,type:`select`,options:[{value:`prompt`,label:`提示确认`},{value:`auto`,label:`自动启动`},{value:`preview`,label:`仅预览`}]}]},{key:`resume`,title:`会话续接`,description:`中断会话的自动恢复`,fields:[{key:`enabled`,label:`启用`,type:`boolean`},{key:`timeout_minutes`,label:`超时时间(分钟)`,type:`number`,min:1,max:1440},{key:`max_age_days`,label:`最大保留天数`,type:`number`,min:1,max:365}]},{key:`claudemd`,title:`CLAUDE.md 管理`,description:`项目级 CLAUDE.md 的自动生成与精炼`,fields:[{key:`auto_generate`,label:`自动生成`,type:`boolean`},{key:`auto_refine`,label:`自动精炼`,type:`boolean`},{key:`refine_interval`,label:`精炼间隔`,type:`number`,description:`每 N 次蒸馏后精炼一次`,min:1,max:100}]},{key:`orchestration`,title:`编排通知`,description:`蒸馏和 CLAUDE.md 更新的通知`,fields:[{key:`enabled`,label:`启用`,type:`boolean`},{key:`notify_on_distill`,label:`蒸馏完成通知`,type:`boolean`},{key:`notify_on_claudemd`,label:`CLAUDE.md 更新通知`,type:`boolean`}]},{key:`web`,title:`Web 管理后台`,description:`Web 服务配置`,fields:[{key:`enabled`,label:`启用`,type:`boolean`},{key:`port`,label:`端口`,type:`number`,min:1024,max:65535}]},{key:`ai`,title:`AI 模型`,description:`分层模型配置(Haiku/Sonnet/Opus)`,fields:[{key:`models.haiku`,label:`Haiku 模型`,type:`text`},{key:`models.sonnet`,label:`Sonnet 模型`,type:`text`},{key:`models.opus`,label:`Opus 模型`,type:`text`}]},{key:`storage`,title:`存储`,description:`数据库路径和容量限制`,fields:[{key:`path`,label:`数据库路径`,type:`text`},{key:`max_size_mb`,label:`最大容量(MB)`,type:`number`,min:100,max:102400}]},{key:`backup`,title:`备份配置`,description:`数据备份策略`,fields:[{key:`include_profile`,label:`包含用户画像`,type:`boolean`,description:`备份时包含用户画像数据`},{key:`encrypt_by_default`,label:`默认加密`,type:`boolean`,description:`备份文件默认加密`}]},{key:`pattern_engine`,title:`模式引擎`,description:`模式识别与演化配置`,fields:[{key:`enabled`,label:`启用`,type:`boolean`},{key:`route_timeout_ms`,label:`路由超时(毫秒)`,type:`number`,min:100,max:6e4},{key:`evolve_interval_days`,label:`演化间隔(天)`,type:`number`,min:1,max:365},{key:`evolve_min_executions`,label:`最小执行次数`,type:`number`,description:`触发演化的最小执行次数`,min:1,max:1e3}]},{key:`ui`,title:`UI 配置`,description:`前端界面参数`,fields:[{key:`skill_repair_limit`,label:`Skill 修复上限`,type:`number`,min:1,max:100},{key:`logs_tail_default`,label:`日志默认条数`,type:`number`,min:10,max:1e3},{key:`pipeline_expire_hours`,label:`Pipeline 过期时间(小时)`,type:`number`,min:1,max:720}]},{key:`history`,title:`配置历史`,description:`查看最近 10 次配置变更记录`,fields:[]}];function Y(){let e=y(),[t,r]=(0,G.useState)(null),[i,c]=(0,G.useState)(`idle`),[l,d]=(0,G.useState)(`distill`),[f,g]=(0,G.useState)(new Set),S=f.size>0,[T,D]=(0,G.useState)(null),[O,k]=(0,G.useState)(new Set),[A,j]=(0,G.useState)({open:!1,title:``,description:``,onConfirm:()=>{}}),{data:M,isLoading:N}=x({queryKey:[`config`],queryFn:()=>E().then(e=>e.data)}),{data:P,refetch:F}=x({queryKey:[`daemon-diagnosis`],queryFn:()=>C().then(e=>e.data),enabled:l===`environment`}),I=b({mutationFn:()=>w(),onSuccess:e=>{D(e.data),F()}});(0,G.useEffect)(()=>{M&&!t&&r(M)},[M,t]);let Y=b({mutationFn:e=>ee(e),onSuccess:()=>{e.invalidateQueries({queryKey:[`config`]}),c(`success`),g(new Set),setTimeout(()=>c(`idle`),3e3)},onError:()=>{c(`error`),setTimeout(()=>c(`idle`),3e3)}}),X=b({mutationFn:()=>L.post(`/config/reset`),onSuccess:()=>{e.invalidateQueries({queryKey:[`config`]}),r(null),c(`success`),setTimeout(()=>c(`idle`),3e3)}});if(N||!t)return(0,K.jsx)(`div`,{className:`flex items-center justify-center h-96`,children:(0,K.jsx)(`div`,{className:`animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600`})});let Z=(e,n)=>{let r=t[e];if(!r)return``;let i=n.split(`.`),a=r;for(let e of i)a=a?.[e];return a??``},Q=(e,t,n)=>{g(t=>new Set(t).add(e)),r(r=>{if(!r)return r;let i={...r[e]||{}},a=t.split(`.`);if(a.length===1)i[t]=n;else{let e={...i[a[0]]||{}};e[a[1]]=n,i[a[0]]=e}return{...r,[e]:i}})},oe=()=>{if(!t)return;let e={};f.forEach(n=>{e[n]=t[n]}),Y.mutate(e)},se=()=>{r(M),g(new Set)},ce=()=>{let e=JSON.stringify(t,null,2),n=new Blob([e],{type:`application/json`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=`claude-forge-config-${new Date().toISOString().slice(0,10)}.json`,i.click(),URL.revokeObjectURL(r)},le=()=>{let e=document.createElement(`input`);e.type=`file`,e.accept=`.json`,e.onchange=e=>{let t=e.target.files?.[0];if(!t)return;let n=new FileReader;n.onload=e=>{try{r(JSON.parse(e.target?.result)),g(new Set(J.map(e=>e.key)))}catch{R(`配置文件格式错误,请选择有效的 JSON 文件`,`error`)}},n.readAsText(t)},e.click()},$=J.find(e=>e.key===l);return(0,K.jsxs)(`div`,{className:`flex gap-6`,children:[(0,K.jsx)(`div`,{className:`w-56 flex-shrink-0`,children:(0,K.jsx)(V,{className:`!p-2 sticky top-6`,children:(0,K.jsxs)(`nav`,{className:`space-y-1`,children:[J.map(e=>(0,K.jsx)(`button`,{onClick:()=>d(e.key),className:z(`w-full text-left px-3 py-2.5 rounded-lg text-sm transition-colors`,l===e.key?`bg-blue-50 text-blue-700 font-medium`:`text-gray-600 hover:bg-gray-50 hover:text-gray-900`),children:q[e.title]?(0,K.jsx)(ae,{content:q[e.title],children:e.title}):e.title},e.key)),(0,K.jsx)(`div`,{className:`border-t border-gray-100 my-1`}),(0,K.jsxs)(`button`,{onClick:()=>d(`backup_mgmt`),className:z(`w-full text-left px-3 py-2.5 rounded-lg text-sm transition-colors flex items-center gap-2`,l===`backup_mgmt`?`bg-blue-50 text-blue-700 font-medium`:`text-gray-600 hover:bg-gray-50 hover:text-gray-900`),children:[(0,K.jsx)(Database,{className:`h-3.5 w-3.5`}),`数据备份`]}),(0,K.jsxs)(`button`,{onClick:()=>d(`memory_mgmt`),className:z(`w-full text-left px-3 py-2.5 rounded-lg text-sm transition-colors flex items-center gap-2`,l===`memory_mgmt`?`bg-blue-50 text-blue-700 font-medium`:`text-gray-600 hover:bg-gray-50 hover:text-gray-900`),children:[(0,K.jsx)(Cpu,{className:`h-3.5 w-3.5`}),`内存管理`]}),(0,K.jsxs)(`button`,{onClick:()=>d(`environment`),className:z(`w-full text-left px-3 py-2.5 rounded-lg text-sm transition-colors flex items-center gap-2`,l===`environment`?`bg-blue-50 text-blue-700 font-medium`:`text-gray-600 hover:bg-gray-50 hover:text-gray-900`),children:[(0,K.jsx)(o,{className:`h-3.5 w-3.5`}),`环境检测`]})]})})}),(0,K.jsxs)(`div`,{className:`flex-1 space-y-6`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`h2`,{className:`text-lg font-semibold text-gray-900`,children:l===`environment`?`环境检测`:l===`backup_mgmt`?`数据备份`:l===`memory_mgmt`?`内存管理`:l===`history`?`配置历史`:$?.title}),(0,K.jsx)(`p`,{className:`text-sm text-gray-500`,children:l===`environment`?`Hook 脚本与 Claude settings.json 注入状态检测与修复`:l===`backup_mgmt`?`数据库备份与历史备份管理`:l===`memory_mgmt`?`LRU 缓存状态与进程内存管理`:l===`history`?`查看最近 10 次配置变更记录`:$?.description??``})]}),l!==`environment`&&l!==`backup_mgmt`&&l!==`memory_mgmt`&&l!==`history`&&(0,K.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,K.jsxs)(H,{children:[i===`success`&&(0,K.jsxs)(U.div,{initial:{opacity:0,x:10},animate:{opacity:1,x:0},exit:{opacity:0},className:`flex items-center gap-1.5 text-green-600 text-sm`,children:[(0,K.jsx)(u,{className:`h-4 w-4`}),`已保存`]}),i===`error`&&(0,K.jsxs)(U.div,{initial:{opacity:0,x:10},animate:{opacity:1,x:0},exit:{opacity:0},className:`flex items-center gap-1.5 text-red-600 text-sm`,children:[(0,K.jsx)(m,{className:`h-4 w-4`}),`保存失败`]})]}),S&&(0,K.jsx)(te,{variant:`warning`,children:`未保存`}),(0,K.jsx)(B,{variant:`ghost`,size:`sm`,icon:s,onClick:ce,children:`导出`}),(0,K.jsx)(B,{variant:`ghost`,size:`sm`,icon:v,onClick:le,children:`导入`}),(0,K.jsx)(B,{variant:`ghost`,size:`sm`,icon:p,onClick:se,disabled:!S,children:`撤销`}),(0,K.jsx)(B,{variant:`primary`,size:`sm`,icon:_,onClick:oe,loading:Y.isPending,disabled:!S,children:`保存`})]})]}),l!==`environment`&&l!==`backup_mgmt`&&l!==`memory_mgmt`&&(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(V,{children:(0,K.jsx)(`div`,{className:`space-y-6`,children:$?.fields.map(e=>(0,K.jsxs)(`div`,{className:`flex items-start gap-4`,children:[(0,K.jsxs)(`div`,{className:`w-48 flex-shrink-0 pt-2`,children:[(0,K.jsx)(`label`,{className:`text-sm font-medium text-gray-700`,children:e.label}),e.description&&(0,K.jsx)(`p`,{className:`text-xs text-gray-400 mt-0.5`,children:e.description})]}),(0,K.jsx)(`div`,{className:`flex-1`,children:e.type===`boolean`?(0,K.jsx)(`button`,{onClick:()=>Q(l,e.key,!Z(l,e.key)),className:z(`relative inline-flex h-6 w-11 items-center rounded-full transition-colors`,Z(l,e.key)?`bg-blue-600`:`bg-gray-300`),children:(0,K.jsx)(`span`,{className:z(`inline-block h-4 w-4 transform rounded-full bg-white transition-transform`,Z(l,e.key)?`translate-x-6`:`translate-x-1`)})}):e.type===`select`?(0,K.jsx)(`select`,{value:String(Z(l,e.key)||``),onChange:t=>Q(l,e.key,t.target.value),className:`w-full max-w-xs px-3 py-2 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm`,children:e.options?.map(e=>(0,K.jsx)(`option`,{value:e.value,children:e.label},e.value))}):e.type===`number`?(0,K.jsx)(`input`,{type:`number`,value:Number(Z(l,e.key))||0,onChange:t=>Q(l,e.key,Number(t.target.value)),min:e.min,max:e.max,className:`w-full max-w-xs px-3 py-2 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm`}):(0,K.jsxs)(`div`,{className:`relative flex items-center max-w-xs`,children:[(0,K.jsx)(`input`,{type:e.sensitive&&!O.has(`${l}.${e.key}`)?`password`:`text`,value:String(Z(l,e.key)||``),onChange:t=>Q(l,e.key,t.target.value),className:`w-full px-3 py-2 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm pr-9`}),e.sensitive&&(0,K.jsx)(`button`,{type:`button`,onClick:()=>k(t=>{let n=new Set(t),r=`${l}.${e.key}`;return n.has(r)?n.delete(r):n.add(r),n}),className:`absolute right-2 text-gray-400 hover:text-gray-600`,children:O.has(`${l}.${e.key}`)?(0,K.jsx)(EyeOff,{className:`h-4 w-4`}):(0,K.jsx)(Eye,{className:`h-4 w-4`})})]})})]},e.key))})}),(0,K.jsx)(V,{className:`!border-red-200`,children:(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`h3`,{className:`text-sm font-semibold text-red-600`,children:`重置配置`}),(0,K.jsx)(`p`,{className:`text-xs text-gray-500 mt-1`,children:`将所有配置恢复为默认值,此操作不可撤销`})]}),(0,K.jsx)(B,{variant:`danger`,size:`sm`,icon:n,onClick:()=>{j({open:!0,title:`重置配置`,description:`确定要重置所有配置为默认值吗?此操作不可撤销。`,onConfirm:()=>{X.mutate(),j({...A,open:!1})}})},loading:X.isPending,children:`重置`})]})})]}),l===`backup_mgmt`&&(0,K.jsx)(ne,{onConfirm:e=>j({...e,open:!0})}),l===`memory_mgmt`&&(0,K.jsx)(re,{onConfirm:e=>j({...e,open:!0})}),l===`history`&&(0,K.jsx)(ie,{currentConfig:M,onRestore:e=>Y.mutate(e),onConfirm:e=>j({...e,open:!0})}),l===`environment`&&(0,K.jsxs)(`div`,{className:`space-y-4`,children:[(0,K.jsx)(V,{children:(0,K.jsxs)(`div`,{className:`flex items-start justify-between`,children:[(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`h3`,{className:`font-medium text-gray-900 mb-1`,children:`Hook 环境诊断`}),(0,K.jsx)(`p`,{className:`text-sm text-gray-500`,children:`检测 Forge hooks 脚本与 Claude settings.json 中 Forge 注入是否完整`})]}),(0,K.jsxs)(`div`,{className:`flex gap-2 flex-shrink-0`,children:[(0,K.jsx)(B,{variant:`secondary`,size:`sm`,icon:h,onClick:()=>F(),children:`重新检测`}),(0,K.jsx)(B,{size:`sm`,icon:a,onClick:()=>I.mutate(),disabled:I.isPending||P?.hooksHealthy,children:I.isPending?`修复中...`:`一键修复`})]})]})}),T&&(0,K.jsx)(V,{children:(0,K.jsxs)(`div`,{className:`flex items-start justify-between`,children:[(0,K.jsxs)(`div`,{className:`flex items-start gap-2`,children:[T.hooksHealthy?(0,K.jsx)(u,{className:`h-4 w-4 text-green-500 mt-0.5 flex-shrink-0`}):(0,K.jsx)(n,{className:`h-4 w-4 text-yellow-500 mt-0.5 flex-shrink-0`}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`p`,{className:z(`text-sm font-medium`,T.hooksHealthy?`text-gray-900`:`text-yellow-600`),children:T.hooksHealthy?`Hooks 环境已恢复正常`:`修复后仍有未通过项,请根据下方列表排查`}),T.backupPath&&(0,K.jsxs)(`p`,{className:`text-xs text-gray-500 mt-0.5`,children:[`已备份 settings: `,T.backupPath.split(`/`).pop()]})]})]}),(0,K.jsx)(B,{variant:`ghost`,size:`sm`,icon:m,onClick:()=>D(null)})]})}),P&&(0,K.jsx)(V,{children:(0,K.jsx)(`div`,{className:`space-y-2`,children:P.items.map(e=>(0,K.jsxs)(`div`,{className:`flex items-start gap-3 py-2 border-b border-gray-50 last:border-0`,children:[(0,K.jsx)(`div`,{className:`flex-shrink-0 mt-0.5`,children:e.ok?(0,K.jsx)(u,{className:`h-4 w-4 text-green-500`}):e.id===`daemon_process`?(0,K.jsx)(n,{className:`h-4 w-4 text-yellow-500`}):(0,K.jsx)(m,{className:`h-4 w-4 text-red-500`})}),(0,K.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,K.jsx)(`p`,{className:`text-sm font-medium text-gray-900`,children:e.label}),(0,K.jsx)(`p`,{className:`text-xs text-gray-500 mt-0.5 break-all`,children:e.detail})]})]},e.id))})}),P&&(0,K.jsx)(V,{className:P.hooksHealthy?`!border-green-200 !bg-green-50`:`!border-yellow-200 !bg-yellow-50`,children:(0,K.jsx)(`div`,{className:`flex items-center gap-2`,children:P.hooksHealthy?(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(u,{className:`h-5 w-5 text-green-600`}),(0,K.jsx)(`p`,{className:`text-sm font-medium text-green-700`,children:`Hooks 相关环境正常`})]}):(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(n,{className:`h-5 w-5 text-yellow-600`}),(0,K.jsx)(`p`,{className:`text-sm font-medium text-yellow-700`,children:`检测到 Hooks 环境不完整,请点击「一键修复」`})]})})})]})]}),(0,K.jsx)(W,{open:A.open,title:A.title,description:A.description,variant:`danger`,onConfirm:A.onConfirm,onCancel:()=>j({...A,open:!1})})]})}export{Y as default};
|