@xdevops/issue-auto-finish 1.0.86 → 1.0.87
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/AIRunnerRegistry-II3WWSFN.js +31 -0
- package/dist/PtyRunner-6UGI5STW.js +22 -0
- package/dist/TerminalManager-RT2N7N5R.js +8 -0
- package/dist/ai-runner/AIRunner.d.ts +9 -1
- package/dist/ai-runner/AIRunner.d.ts.map +1 -1
- package/dist/ai-runner/AIRunnerRegistry.d.ts +37 -1
- package/dist/ai-runner/AIRunnerRegistry.d.ts.map +1 -1
- package/dist/ai-runner/PtyRunner.d.ts +114 -0
- package/dist/ai-runner/PtyRunner.d.ts.map +1 -0
- package/dist/ai-runner/index.d.ts +3 -1
- package/dist/ai-runner/index.d.ts.map +1 -1
- package/dist/{ai-runner-SVUNA3FX.js → ai-runner-HLA44WI6.js} +12 -3
- package/dist/{analyze-SXXPE5XL.js → analyze-ZIXNC5GN.js} +10 -8
- package/dist/{analyze-SXXPE5XL.js.map → analyze-ZIXNC5GN.js.map} +1 -1
- package/dist/{braindump-4E5SDMSZ.js → braindump-56WAY2RD.js} +10 -8
- package/dist/{braindump-4E5SDMSZ.js.map → braindump-56WAY2RD.js.map} +1 -1
- package/dist/{chunk-ICXB2WP5.js → chunk-2MESXJEZ.js} +3 -3
- package/dist/{chunk-P4O4ZXEC.js → chunk-2YQHKXLL.js} +40 -19
- package/dist/chunk-2YQHKXLL.js.map +1 -0
- package/dist/chunk-AVGZH64A.js +211 -0
- package/dist/chunk-AVGZH64A.js.map +1 -0
- package/dist/{chunk-OUPJMHAL.js → chunk-IP3QTP5A.js} +1026 -764
- package/dist/chunk-IP3QTP5A.js.map +1 -0
- package/dist/chunk-KC5S66OZ.js +177 -0
- package/dist/chunk-KC5S66OZ.js.map +1 -0
- package/dist/{chunk-4QV6D34Y.js → chunk-M5C2WILQ.js} +8 -6
- package/dist/{chunk-4QV6D34Y.js.map → chunk-M5C2WILQ.js.map} +1 -1
- package/dist/{chunk-FWEW5E3B.js → chunk-NZHKAPU6.js} +35 -5
- package/dist/chunk-NZHKAPU6.js.map +1 -0
- package/dist/{chunk-KTYPZTF4.js → chunk-O3WEV5W3.js} +10 -2
- package/dist/chunk-O3WEV5W3.js.map +1 -0
- package/dist/{chunk-QO5VTSMI.js → chunk-QZZGIZWC.js} +455 -202
- package/dist/chunk-QZZGIZWC.js.map +1 -0
- package/dist/{chunk-4LFNFRCL.js → chunk-SAMTXC4A.js} +91 -214
- package/dist/chunk-SAMTXC4A.js.map +1 -0
- package/dist/chunk-U237JSLB.js +1 -0
- package/dist/chunk-U237JSLB.js.map +1 -0
- package/dist/chunk-U6GWFTKA.js +657 -0
- package/dist/chunk-U6GWFTKA.js.map +1 -0
- package/dist/{chunk-HOFYJEJ4.js → chunk-UBQLXQ7I.js} +11 -11
- package/dist/cli/setup/env-metadata.d.ts.map +1 -1
- package/dist/cli.js +8 -7
- package/dist/cli.js.map +1 -1
- package/dist/{config-QLINHCHD.js → config-WTRSZLOC.js} +4 -3
- package/dist/config-WTRSZLOC.js.map +1 -0
- package/dist/config-schema.d.ts +17 -1
- package/dist/config-schema.d.ts.map +1 -1
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/errors/PhaseAbortedError.d.ts +3 -3
- package/dist/errors/PhaseAbortedError.d.ts.map +1 -1
- package/dist/errors-S3BWYA4I.js +43 -0
- package/dist/errors-S3BWYA4I.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -11
- package/dist/{init-TDKDC6YP.js → init-QQDXGTPB.js} +7 -6
- package/dist/{init-TDKDC6YP.js.map → init-QQDXGTPB.js.map} +1 -1
- package/dist/lib.js +9 -7
- package/dist/lib.js.map +1 -1
- package/dist/orchestrator/IssueProcessingContext.d.ts +39 -21
- package/dist/orchestrator/IssueProcessingContext.d.ts.map +1 -1
- package/dist/orchestrator/PipelineOrchestrator.d.ts +10 -1
- package/dist/orchestrator/PipelineOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/steps/PhaseLoopStep.d.ts +1 -1
- package/dist/orchestrator/steps/PhaseLoopStep.d.ts.map +1 -1
- package/dist/orchestrator/steps/SetupStep.d.ts.map +1 -1
- package/dist/persistence/PlanPersistence.d.ts +7 -1
- package/dist/persistence/PlanPersistence.d.ts.map +1 -1
- package/dist/phases/BasePhase.d.ts +31 -42
- package/dist/phases/BasePhase.d.ts.map +1 -1
- package/dist/phases/BuildPhase.d.ts.map +1 -1
- package/dist/phases/PhaseFactory.d.ts +2 -3
- package/dist/phases/PhaseFactory.d.ts.map +1 -1
- package/dist/phases/PhaseOutcome.d.ts +42 -0
- package/dist/phases/PhaseOutcome.d.ts.map +1 -0
- package/dist/phases/PlanPhase.d.ts +1 -1
- package/dist/phases/PlanPhase.d.ts.map +1 -1
- package/dist/phases/ReleasePhase.d.ts +8 -18
- package/dist/phases/ReleasePhase.d.ts.map +1 -1
- package/dist/phases/UatPhase.d.ts +7 -24
- package/dist/phases/UatPhase.d.ts.map +1 -1
- package/dist/phases/VerifyPhase.d.ts +4 -4
- package/dist/phases/VerifyPhase.d.ts.map +1 -1
- package/dist/poller/IssuePoller.d.ts.map +1 -1
- package/dist/prompts/release-templates.d.ts.map +1 -1
- package/dist/prompts/templates.d.ts.map +1 -1
- package/dist/{restart-4NSHDOX3.js → restart-BMILTP5X.js} +6 -5
- package/dist/{restart-4NSHDOX3.js.map → restart-BMILTP5X.js.map} +1 -1
- package/dist/run.js +14 -11
- package/dist/run.js.map +1 -1
- package/dist/settings/ExperimentalSettings.d.ts +1 -1
- package/dist/settings/ExperimentalSettings.d.ts.map +1 -1
- package/dist/start-6QRW6IJI.js +15 -0
- package/dist/start-6QRW6IJI.js.map +1 -0
- package/dist/terminal/TerminalManager.d.ts +62 -0
- package/dist/terminal/TerminalManager.d.ts.map +1 -0
- package/dist/terminal/TerminalWebSocket.d.ts +9 -0
- package/dist/terminal/TerminalWebSocket.d.ts.map +1 -0
- package/dist/tracker/ExecutableTask.d.ts +4 -2
- package/dist/tracker/ExecutableTask.d.ts.map +1 -1
- package/dist/tracker/IssueState.d.ts +11 -1
- package/dist/tracker/IssueState.d.ts.map +1 -1
- package/dist/tracker/IssueTracker.d.ts +19 -1
- package/dist/tracker/IssueTracker.d.ts.map +1 -1
- package/dist/web/WebServer.d.ts +4 -0
- package/dist/web/WebServer.d.ts.map +1 -1
- package/dist/web/routes/terminal.d.ts +11 -0
- package/dist/web/routes/terminal.d.ts.map +1 -0
- package/dist/webhook/CommandExecutor.d.ts.map +1 -1
- package/package.json +7 -1
- package/src/web/frontend/dist/assets/index-COYziOhv.css +1 -0
- package/src/web/frontend/dist/assets/index-D_oTMuJU.js +151 -0
- package/src/web/frontend/dist/index.html +2 -2
- package/dist/chunk-4LFNFRCL.js.map +0 -1
- package/dist/chunk-DADQSKPL.js +0 -1
- package/dist/chunk-FWEW5E3B.js.map +0 -1
- package/dist/chunk-KTYPZTF4.js.map +0 -1
- package/dist/chunk-OUPJMHAL.js.map +0 -1
- package/dist/chunk-P4O4ZXEC.js.map +0 -1
- package/dist/chunk-QO5VTSMI.js.map +0 -1
- package/dist/start-XZIBPLC2.js +0 -14
- package/src/web/frontend/dist/assets/index-BWVpNmFm.js +0 -133
- package/src/web/frontend/dist/assets/index-C7lorIa0.css +0 -1
- /package/dist/{ai-runner-SVUNA3FX.js.map → AIRunnerRegistry-II3WWSFN.js.map} +0 -0
- /package/dist/{chunk-DADQSKPL.js.map → PtyRunner-6UGI5STW.js.map} +0 -0
- /package/dist/{config-QLINHCHD.js.map → TerminalManager-RT2N7N5R.js.map} +0 -0
- /package/dist/{start-XZIBPLC2.js.map → ai-runner-HLA44WI6.js.map} +0 -0
- /package/dist/{chunk-ICXB2WP5.js.map → chunk-2MESXJEZ.js.map} +0 -0
- /package/dist/{chunk-HOFYJEJ4.js.map → chunk-UBQLXQ7I.js.map} +0 -0
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BraindumpOrchestrator,
|
|
3
3
|
BraindumpTracker
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-M5C2WILQ.js";
|
|
5
5
|
import {
|
|
6
6
|
createSetupRouter
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UBQLXQ7I.js";
|
|
8
8
|
import {
|
|
9
9
|
buildLockNoteBody,
|
|
10
10
|
buildReleaseNoteBody,
|
|
11
11
|
findAllLockNotes,
|
|
12
12
|
findLockNote
|
|
13
13
|
} from "./chunk-GXFG4JU6.js";
|
|
14
|
+
import {
|
|
15
|
+
TerminalManager
|
|
16
|
+
} from "./chunk-KC5S66OZ.js";
|
|
14
17
|
import {
|
|
15
18
|
BrainstormService,
|
|
16
19
|
GongfengClient,
|
|
@@ -33,7 +36,7 @@ import {
|
|
|
33
36
|
setE2eOverride,
|
|
34
37
|
setNoteSyncOverride,
|
|
35
38
|
validatePhaseRegistry
|
|
36
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-IP3QTP5A.js";
|
|
37
40
|
import {
|
|
38
41
|
AsyncMutex,
|
|
39
42
|
BaseTracker,
|
|
@@ -42,12 +45,15 @@ import {
|
|
|
42
45
|
getExternalId,
|
|
43
46
|
getIid,
|
|
44
47
|
getTitle
|
|
45
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-2YQHKXLL.js";
|
|
46
49
|
import {
|
|
47
50
|
IwikiImporter,
|
|
48
51
|
getProjectKnowledge,
|
|
49
52
|
loadKnowledge
|
|
50
53
|
} from "./chunk-ACVOOHAR.js";
|
|
54
|
+
import {
|
|
55
|
+
collectStaticInfo
|
|
56
|
+
} from "./chunk-B7TVVODN.js";
|
|
51
57
|
import {
|
|
52
58
|
setLocale,
|
|
53
59
|
t
|
|
@@ -55,23 +61,15 @@ import {
|
|
|
55
61
|
import {
|
|
56
62
|
loadConfig,
|
|
57
63
|
reloadConfig
|
|
58
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-NZHKAPU6.js";
|
|
59
65
|
import {
|
|
60
66
|
resolveDisplayHost
|
|
61
67
|
} from "./chunk-AKXDQH25.js";
|
|
62
|
-
import {
|
|
63
|
-
collectStaticInfo
|
|
64
|
-
} from "./chunk-B7TVVODN.js";
|
|
65
68
|
import {
|
|
66
69
|
ensureDir,
|
|
67
70
|
resolveDataDir
|
|
68
71
|
} from "./chunk-TN2SYADO.js";
|
|
69
72
|
import {
|
|
70
|
-
KnowledgeStore
|
|
71
|
-
} from "./chunk-DAX3FD2O.js";
|
|
72
|
-
import {
|
|
73
|
-
SessionLimitError,
|
|
74
|
-
SessionNotFoundError,
|
|
75
73
|
createAIRunner,
|
|
76
74
|
getDefaultBinary,
|
|
77
75
|
isBinaryAvailable,
|
|
@@ -80,7 +78,14 @@ import {
|
|
|
80
78
|
resolveModelForRunner,
|
|
81
79
|
setShuttingDown,
|
|
82
80
|
validateRunnerRegistry
|
|
83
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-SAMTXC4A.js";
|
|
82
|
+
import {
|
|
83
|
+
SessionLimitError,
|
|
84
|
+
SessionNotFoundError
|
|
85
|
+
} from "./chunk-AVGZH64A.js";
|
|
86
|
+
import {
|
|
87
|
+
KnowledgeStore
|
|
88
|
+
} from "./chunk-DAX3FD2O.js";
|
|
84
89
|
import {
|
|
85
90
|
logger
|
|
86
91
|
} from "./chunk-GF2RRYHB.js";
|
|
@@ -170,6 +175,7 @@ ${sections.join("\n\n")}`;
|
|
|
170
175
|
};
|
|
171
176
|
|
|
172
177
|
// src/poller/IssuePoller.ts
|
|
178
|
+
import crypto from "crypto";
|
|
173
179
|
var logger3 = logger.child("IssuePoller");
|
|
174
180
|
var AUTO_FINISH_LABEL = "auto-finish";
|
|
175
181
|
var AUTO_APPROVE_CHECK_INTERVAL_MS = 3e4;
|
|
@@ -225,6 +231,7 @@ var IssuePoller = class {
|
|
|
225
231
|
this.activeIssues.delete(issueIid);
|
|
226
232
|
logger3.info("Force-released issue from activeIssues", { issueIid });
|
|
227
233
|
}
|
|
234
|
+
this.tracker.clearProcessingLock(issueIid);
|
|
228
235
|
return had;
|
|
229
236
|
}
|
|
230
237
|
pauseDiscovery() {
|
|
@@ -287,8 +294,14 @@ var IssuePoller = class {
|
|
|
287
294
|
max: maxConcurrent
|
|
288
295
|
});
|
|
289
296
|
for (const record of batch) {
|
|
290
|
-
|
|
291
|
-
|
|
297
|
+
const iid = getIid(record);
|
|
298
|
+
const correlationId = crypto.randomUUID();
|
|
299
|
+
if (!this.tracker.acquireProcessingLock(iid, correlationId)) {
|
|
300
|
+
logger3.warn("Failed to acquire processing lock, skipping", { issueIid: iid });
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
this.activeIssues.add(iid);
|
|
304
|
+
this.processInBackground(record, correlationId);
|
|
292
305
|
}
|
|
293
306
|
}
|
|
294
307
|
maybeAutoApproveWaiting() {
|
|
@@ -331,7 +344,7 @@ var IssuePoller = class {
|
|
|
331
344
|
}
|
|
332
345
|
}
|
|
333
346
|
}
|
|
334
|
-
async processInBackground(record) {
|
|
347
|
+
async processInBackground(record, correlationId) {
|
|
335
348
|
const iid = getIid(record);
|
|
336
349
|
try {
|
|
337
350
|
const issue = await this.resolveIssue(getExternalId(record));
|
|
@@ -367,6 +380,7 @@ var IssuePoller = class {
|
|
|
367
380
|
});
|
|
368
381
|
} finally {
|
|
369
382
|
this.activeIssues.delete(iid);
|
|
383
|
+
this.tracker.releaseProcessingLock(iid, correlationId);
|
|
370
384
|
}
|
|
371
385
|
}
|
|
372
386
|
async resolveIssue(issueId) {
|
|
@@ -681,7 +695,7 @@ function generateNodeId() {
|
|
|
681
695
|
}
|
|
682
696
|
|
|
683
697
|
// src/web/WebServer.ts
|
|
684
|
-
import
|
|
698
|
+
import express13 from "express";
|
|
685
699
|
import fs6 from "fs";
|
|
686
700
|
import path8 from "path";
|
|
687
701
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -869,7 +883,7 @@ function createApiRouter(trackerOrDeps, config, agentLogStore, orchestrator, mai
|
|
|
869
883
|
res.status(404).json({ error: "Issue not found" });
|
|
870
884
|
return;
|
|
871
885
|
}
|
|
872
|
-
const progress = await readProgress(iid, cfg, tracker, git);
|
|
886
|
+
const progress = record.phaseProgress ? { phases: record.phaseProgress, currentPhase: record.currentPhase } : await readProgress(iid, cfg, tracker, git);
|
|
873
887
|
const preview = buildPreviewInfo(iid, orch);
|
|
874
888
|
res.json({ ...record, progress, preview });
|
|
875
889
|
});
|
|
@@ -898,6 +912,11 @@ function createApiRouter(trackerOrDeps, config, agentLogStore, orchestrator, mai
|
|
|
898
912
|
return;
|
|
899
913
|
}
|
|
900
914
|
if (filename.endsWith(".json")) {
|
|
915
|
+
if (req.query.format === "html") {
|
|
916
|
+
const html = await marked("```json\n" + JSON.stringify(JSON.parse(content), null, 2) + "\n```");
|
|
917
|
+
res.type("html").send(html);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
901
920
|
res.json(JSON.parse(content));
|
|
902
921
|
return;
|
|
903
922
|
}
|
|
@@ -957,8 +976,8 @@ function createApiRouter(trackerOrDeps, config, agentLogStore, orchestrator, mai
|
|
|
957
976
|
await claimer.releaseClaim(getExternalId(record), iid, "cancelled");
|
|
958
977
|
}
|
|
959
978
|
}
|
|
960
|
-
poller?.forceReleaseIssue(iid);
|
|
961
979
|
await orch.restartIssue(iid);
|
|
980
|
+
poller?.forceReleaseIssue(iid);
|
|
962
981
|
res.json({ success: true, message: `Issue #${iid} restarted` });
|
|
963
982
|
} catch (err) {
|
|
964
983
|
const msg = err.message;
|
|
@@ -1391,7 +1410,7 @@ function createApiRouter(trackerOrDeps, config, agentLogStore, orchestrator, mai
|
|
|
1391
1410
|
});
|
|
1392
1411
|
router.put("/api/system/feature-toggle", (req, res) => {
|
|
1393
1412
|
const { feature, enabled } = req.body;
|
|
1394
|
-
const validFeatures = ["brainstorm", "chat", "braindump", "knowledge", "distill"];
|
|
1413
|
+
const validFeatures = ["brainstorm", "chat", "braindump", "knowledge", "distill", "terminal"];
|
|
1395
1414
|
if (!feature || !validFeatures.includes(feature) || typeof enabled !== "boolean") {
|
|
1396
1415
|
res.status(400).json({ error: "Invalid feature or enabled value" });
|
|
1397
1416
|
return;
|
|
@@ -1441,6 +1460,8 @@ function createApiRouter(trackerOrDeps, config, agentLogStore, orchestrator, mai
|
|
|
1441
1460
|
braindumpEnabled: getFeatureEnabled("braindump", cfg),
|
|
1442
1461
|
knowledgeEnabled: getFeatureEnabled("knowledge", cfg),
|
|
1443
1462
|
distillEnabled: getFeatureEnabled("distill", cfg),
|
|
1463
|
+
terminalEnabled: getFeatureEnabled("terminal", cfg),
|
|
1464
|
+
terminalWorkDir: cfg.project.workDir,
|
|
1444
1465
|
knowledgeSyncEnabled: true,
|
|
1445
1466
|
multiRepoEnabled: wsConfig ? isMultiRepo(wsConfig) : false,
|
|
1446
1467
|
linkedRepos: wsConfig?.associates.map((a) => ({
|
|
@@ -1707,7 +1728,7 @@ data: ${JSON.stringify({ time: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
|
1707
1728
|
const scenariosDir = path3.join(outputsDir, "scenarios");
|
|
1708
1729
|
let scenarios = [];
|
|
1709
1730
|
if (fs3.existsSync(scenariosDir) && fs3.statSync(scenariosDir).isDirectory()) {
|
|
1710
|
-
scenarios = collectArtifactFiles(scenariosDir, scenariosDir);
|
|
1731
|
+
scenarios = collectArtifactFiles(scenariosDir, scenariosDir).filter((f) => !f.name.startsWith("config.") && !f.name.startsWith("config-"));
|
|
1711
1732
|
}
|
|
1712
1733
|
res.json({ configured: true, runs, scenarios });
|
|
1713
1734
|
} catch (err) {
|
|
@@ -2405,7 +2426,7 @@ function createKnowledgeRouter(deps) {
|
|
|
2405
2426
|
heartbeat = setInterval(() => {
|
|
2406
2427
|
sendProgress({ step: "analyzing", current: 2, total, message: "AI \u5206\u6790\u4E2D...", elapsed: Date.now() - aiStart });
|
|
2407
2428
|
}, 3e3);
|
|
2408
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
2429
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
2409
2430
|
const runner = createAIRunner2(config.ai);
|
|
2410
2431
|
const { analyze } = await import("./KnowledgeAnalyzer-MTTTSSHX.js");
|
|
2411
2432
|
const knowledge = await analyze({ workDir, aiRunner: runner, syncToProject: config.sync.knowledgeToProject });
|
|
@@ -2450,7 +2471,7 @@ function createKnowledgeRouter(deps) {
|
|
|
2450
2471
|
(async () => {
|
|
2451
2472
|
try {
|
|
2452
2473
|
sendProgress({ step: "collecting", current: 1, total, message: "\u68C0\u6D4B\u53D8\u66F4\u5E76\u6536\u96C6\u4FE1\u606F..." });
|
|
2453
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
2474
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
2454
2475
|
const { analyzeIncremental } = await import("./KnowledgeAnalyzer-MTTTSSHX.js");
|
|
2455
2476
|
const runner = createAIRunner2(config.ai);
|
|
2456
2477
|
sendProgress({ step: "analyzing", current: 2, total, message: "\u589E\u91CF\u5206\u6790\u4E2D..." });
|
|
@@ -2734,7 +2755,7 @@ function createDomainModelRouter(deps) {
|
|
|
2734
2755
|
}
|
|
2735
2756
|
(async () => {
|
|
2736
2757
|
try {
|
|
2737
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
2758
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
2738
2759
|
const runner = createAIRunner2(config.ai);
|
|
2739
2760
|
const model = await analyzer.analyze({
|
|
2740
2761
|
workDir: config.project.workDir,
|
|
@@ -2948,7 +2969,7 @@ function createSystemUseCaseRouter(deps) {
|
|
|
2948
2969
|
}
|
|
2949
2970
|
(async () => {
|
|
2950
2971
|
try {
|
|
2951
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
2972
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
2952
2973
|
const runner = createAIRunner2(config.ai);
|
|
2953
2974
|
const model = await analyzer.analyze({
|
|
2954
2975
|
workDir: config.project.workDir,
|
|
@@ -3138,7 +3159,7 @@ function createSystemUseCaseRouter(deps) {
|
|
|
3138
3159
|
res.status(404).json({ error: "No domain model loaded \u2014 run domain analysis first" });
|
|
3139
3160
|
return;
|
|
3140
3161
|
}
|
|
3141
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
3162
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
3142
3163
|
const runner = createAIRunner2(config.ai);
|
|
3143
3164
|
const associations = await analyzer.suggestDomainAssociations(
|
|
3144
3165
|
useCaseModel,
|
|
@@ -3969,6 +3990,79 @@ function createAnalyticsRouter(deps) {
|
|
|
3969
3990
|
return router;
|
|
3970
3991
|
}
|
|
3971
3992
|
|
|
3993
|
+
// src/web/routes/terminal.ts
|
|
3994
|
+
import express12 from "express";
|
|
3995
|
+
var { Router: Router12 } = express12;
|
|
3996
|
+
var logger21 = logger.child("TerminalRoutes");
|
|
3997
|
+
function createTerminalRouter(deps) {
|
|
3998
|
+
const router = Router12();
|
|
3999
|
+
const { terminalManager, config } = deps;
|
|
4000
|
+
router.use("/api/terminal", (_req, res, next) => {
|
|
4001
|
+
if (!getFeatureEnabled("terminal", config)) {
|
|
4002
|
+
res.status(503).json({ error: "Terminal feature is disabled" });
|
|
4003
|
+
return;
|
|
4004
|
+
}
|
|
4005
|
+
next();
|
|
4006
|
+
});
|
|
4007
|
+
router.get("/api/terminal/sessions", (_req, res) => {
|
|
4008
|
+
try {
|
|
4009
|
+
const sessions = terminalManager.list();
|
|
4010
|
+
res.json({ success: true, sessions });
|
|
4011
|
+
} catch (err) {
|
|
4012
|
+
logger21.error("Failed to list terminal sessions", { error: err.message });
|
|
4013
|
+
res.status(500).json({ error: err.message });
|
|
4014
|
+
}
|
|
4015
|
+
});
|
|
4016
|
+
router.post("/api/terminal/sessions", (req, res) => {
|
|
4017
|
+
try {
|
|
4018
|
+
const { workDir, issueIid, cols, rows } = req.body;
|
|
4019
|
+
if (!workDir?.trim()) {
|
|
4020
|
+
res.status(400).json({ error: "workDir is required" });
|
|
4021
|
+
return;
|
|
4022
|
+
}
|
|
4023
|
+
const session = terminalManager.create({
|
|
4024
|
+
workDir: workDir.trim(),
|
|
4025
|
+
issueIid,
|
|
4026
|
+
cols,
|
|
4027
|
+
rows
|
|
4028
|
+
});
|
|
4029
|
+
res.json({ success: true, session });
|
|
4030
|
+
} catch (err) {
|
|
4031
|
+
logger21.error("Failed to create terminal session", { error: err.message });
|
|
4032
|
+
res.status(500).json({ error: err.message });
|
|
4033
|
+
}
|
|
4034
|
+
});
|
|
4035
|
+
router.get("/api/terminal/sessions/by-issue/:iid", (req, res) => {
|
|
4036
|
+
try {
|
|
4037
|
+
const iid = parseInt(req.params.iid, 10);
|
|
4038
|
+
if (isNaN(iid)) {
|
|
4039
|
+
res.status(400).json({ error: "Invalid issue IID" });
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
const sessions = terminalManager.list().filter((s) => s.issueIid === iid);
|
|
4043
|
+
res.json({ success: true, sessions });
|
|
4044
|
+
} catch (err) {
|
|
4045
|
+
logger21.error("Failed to find terminal sessions by issue", { error: err.message });
|
|
4046
|
+
res.status(500).json({ error: err.message });
|
|
4047
|
+
}
|
|
4048
|
+
});
|
|
4049
|
+
router.delete("/api/terminal/sessions/:id", (req, res) => {
|
|
4050
|
+
try {
|
|
4051
|
+
const session = terminalManager.get(req.params.id);
|
|
4052
|
+
if (!session) {
|
|
4053
|
+
res.status(404).json({ error: "Session not found" });
|
|
4054
|
+
return;
|
|
4055
|
+
}
|
|
4056
|
+
terminalManager.destroy(req.params.id);
|
|
4057
|
+
res.json({ success: true });
|
|
4058
|
+
} catch (err) {
|
|
4059
|
+
logger21.error("Failed to destroy terminal session", { error: err.message });
|
|
4060
|
+
res.status(500).json({ error: err.message });
|
|
4061
|
+
}
|
|
4062
|
+
});
|
|
4063
|
+
return router;
|
|
4064
|
+
}
|
|
4065
|
+
|
|
3972
4066
|
// src/knowledge/DomainModelAnalyzer.ts
|
|
3973
4067
|
import fs5 from "fs";
|
|
3974
4068
|
import path6 from "path";
|
|
@@ -4098,7 +4192,7 @@ ${feedback}
|
|
|
4098
4192
|
}
|
|
4099
4193
|
|
|
4100
4194
|
// src/knowledge/DomainModelAnalyzer.ts
|
|
4101
|
-
var
|
|
4195
|
+
var logger22 = logger.child("DomainModelAnalyzer");
|
|
4102
4196
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
4103
4197
|
"node_modules",
|
|
4104
4198
|
"dist",
|
|
@@ -4315,7 +4409,7 @@ var DomainModelAnalyzer = class {
|
|
|
4315
4409
|
}
|
|
4316
4410
|
this.currentModel = model;
|
|
4317
4411
|
emit({ type: "complete", data: model });
|
|
4318
|
-
|
|
4412
|
+
logger22.info("Domain model analysis complete", {
|
|
4319
4413
|
contexts: model.boundedContexts.length,
|
|
4320
4414
|
elements: model.elements.length,
|
|
4321
4415
|
relationships: model.relationships.length
|
|
@@ -4341,7 +4435,7 @@ var DomainModelAnalyzer = class {
|
|
|
4341
4435
|
tags: ["domain-model", "auto-generated"]
|
|
4342
4436
|
});
|
|
4343
4437
|
}
|
|
4344
|
-
|
|
4438
|
+
logger22.info("Domain model confirmed to knowledge store");
|
|
4345
4439
|
}
|
|
4346
4440
|
/**
|
|
4347
4441
|
* Load a previously stored domain model from KnowledgeStore.
|
|
@@ -4356,7 +4450,7 @@ var DomainModelAnalyzer = class {
|
|
|
4356
4450
|
this.currentModel = model;
|
|
4357
4451
|
return model;
|
|
4358
4452
|
} catch {
|
|
4359
|
-
|
|
4453
|
+
logger22.warn("Failed to parse stored domain model");
|
|
4360
4454
|
return null;
|
|
4361
4455
|
}
|
|
4362
4456
|
}
|
|
@@ -4580,7 +4674,7 @@ ${domainModelJson}
|
|
|
4580
4674
|
}
|
|
4581
4675
|
|
|
4582
4676
|
// src/knowledge/SystemUseCaseAnalyzer.ts
|
|
4583
|
-
var
|
|
4677
|
+
var logger23 = logger.child("SystemUseCaseAnalyzer");
|
|
4584
4678
|
function parseJsonFromOutput2(output) {
|
|
4585
4679
|
const jsonBlockMatch = output.match(/```json\s*\n([\s\S]*?)```/);
|
|
4586
4680
|
if (jsonBlockMatch) {
|
|
@@ -4705,7 +4799,7 @@ var SystemUseCaseAnalyzer = class {
|
|
|
4705
4799
|
}
|
|
4706
4800
|
this.currentModel = model;
|
|
4707
4801
|
emit({ type: "complete", data: model });
|
|
4708
|
-
|
|
4802
|
+
logger23.info("Use case analysis complete", {
|
|
4709
4803
|
useCases: model.useCases.length,
|
|
4710
4804
|
relationships: model.relationships.length
|
|
4711
4805
|
});
|
|
@@ -4726,12 +4820,12 @@ var SystemUseCaseAnalyzer = class {
|
|
|
4726
4820
|
});
|
|
4727
4821
|
const associations = /* @__PURE__ */ new Map();
|
|
4728
4822
|
if (!result.success) {
|
|
4729
|
-
|
|
4823
|
+
logger23.warn("AI domain association failed", { output: result.output.slice(0, 200) });
|
|
4730
4824
|
return associations;
|
|
4731
4825
|
}
|
|
4732
4826
|
const parsed = parseJsonFromOutput2(result.output);
|
|
4733
4827
|
if (!parsed || !parsed.associations) {
|
|
4734
|
-
|
|
4828
|
+
logger23.warn("Failed to parse domain association output");
|
|
4735
4829
|
return associations;
|
|
4736
4830
|
}
|
|
4737
4831
|
const rawAssociations = parsed.associations;
|
|
@@ -4761,7 +4855,7 @@ var SystemUseCaseAnalyzer = class {
|
|
|
4761
4855
|
tags: ["system-usecase", "auto-generated"]
|
|
4762
4856
|
});
|
|
4763
4857
|
}
|
|
4764
|
-
|
|
4858
|
+
logger23.info("Use case model confirmed to knowledge store");
|
|
4765
4859
|
}
|
|
4766
4860
|
/**
|
|
4767
4861
|
* Load a previously stored use case model from KnowledgeStore.
|
|
@@ -4776,7 +4870,7 @@ var SystemUseCaseAnalyzer = class {
|
|
|
4776
4870
|
this.currentModel = model;
|
|
4777
4871
|
return model;
|
|
4778
4872
|
} catch {
|
|
4779
|
-
|
|
4873
|
+
logger23.warn("Failed to parse stored use case model");
|
|
4780
4874
|
return null;
|
|
4781
4875
|
}
|
|
4782
4876
|
}
|
|
@@ -4843,7 +4937,7 @@ ${knowledgeSection}${entriesSection}
|
|
|
4843
4937
|
}
|
|
4844
4938
|
|
|
4845
4939
|
// src/services/ChatService.ts
|
|
4846
|
-
var
|
|
4940
|
+
var logger24 = logger.child("Chat");
|
|
4847
4941
|
function agentConfigToAIConfig(agentCfg, timeoutMs) {
|
|
4848
4942
|
return {
|
|
4849
4943
|
mode: agentCfg.mode,
|
|
@@ -4879,7 +4973,7 @@ var ChatService = class {
|
|
|
4879
4973
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4880
4974
|
};
|
|
4881
4975
|
this.sessions.set(session.id, session);
|
|
4882
|
-
|
|
4976
|
+
logger24.info("Created chat session", { sessionId: session.id });
|
|
4883
4977
|
return session;
|
|
4884
4978
|
}
|
|
4885
4979
|
getSession(id) {
|
|
@@ -4924,7 +5018,7 @@ var ChatService = class {
|
|
|
4924
5018
|
${r.content.slice(0, 500)}`).join("\n\n");
|
|
4925
5019
|
}
|
|
4926
5020
|
} catch (err) {
|
|
4927
|
-
|
|
5021
|
+
logger24.debug("Vector search failed, continuing without", { error: err.message });
|
|
4928
5022
|
}
|
|
4929
5023
|
}
|
|
4930
5024
|
const prompt = isFirstMessage ? `${buildChatSystemPrompt(getProjectKnowledge(), knowledgeEntries)}${vectorContext}
|
|
@@ -5005,14 +5099,108 @@ ${r.content.slice(0, 500)}`).join("\n\n");
|
|
|
5005
5099
|
}
|
|
5006
5100
|
};
|
|
5007
5101
|
|
|
5102
|
+
// src/terminal/TerminalWebSocket.ts
|
|
5103
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
5104
|
+
var logger25 = logger.child("TerminalWebSocket");
|
|
5105
|
+
function setupTerminalWebSocket(opts) {
|
|
5106
|
+
const { httpServer, terminalManager, isEnabled } = opts;
|
|
5107
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
5108
|
+
httpServer.on("upgrade", (request, socket, head) => {
|
|
5109
|
+
const url = new URL(request.url ?? "/", `http://${request.headers.host ?? "localhost"}`);
|
|
5110
|
+
if (!url.pathname.startsWith("/ws/terminal")) return;
|
|
5111
|
+
if (!isEnabled()) {
|
|
5112
|
+
socket.write("HTTP/1.1 503 Service Unavailable\r\n\r\n");
|
|
5113
|
+
socket.destroy();
|
|
5114
|
+
return;
|
|
5115
|
+
}
|
|
5116
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
5117
|
+
wss.emit("connection", ws, request);
|
|
5118
|
+
});
|
|
5119
|
+
});
|
|
5120
|
+
wss.on("connection", (ws, request) => {
|
|
5121
|
+
const url = new URL(request.url ?? "/", `http://${request.headers.host ?? "localhost"}`);
|
|
5122
|
+
let sessionId = null;
|
|
5123
|
+
try {
|
|
5124
|
+
if (url.pathname === "/ws/terminal/new") {
|
|
5125
|
+
const workDir = url.searchParams.get("workDir") ?? "";
|
|
5126
|
+
const issueIidStr = url.searchParams.get("issueIid");
|
|
5127
|
+
const issueIid = issueIidStr ? parseInt(issueIidStr, 10) : void 0;
|
|
5128
|
+
if (!workDir) {
|
|
5129
|
+
ws.close(1008, "workDir is required");
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
5132
|
+
const info = terminalManager.create({
|
|
5133
|
+
workDir,
|
|
5134
|
+
issueIid: issueIid && !isNaN(issueIid) ? issueIid : void 0
|
|
5135
|
+
});
|
|
5136
|
+
sessionId = info.id;
|
|
5137
|
+
ws.send(JSON.stringify({ type: "session", sessionId: info.id }));
|
|
5138
|
+
} else {
|
|
5139
|
+
sessionId = url.searchParams.get("sessionId");
|
|
5140
|
+
if (!sessionId || !terminalManager.get(sessionId)) {
|
|
5141
|
+
ws.close(1008, "Invalid or missing sessionId");
|
|
5142
|
+
return;
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
terminalManager.onData(sessionId, (data) => {
|
|
5146
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
5147
|
+
try {
|
|
5148
|
+
ws.send(data);
|
|
5149
|
+
} catch {
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5152
|
+
});
|
|
5153
|
+
terminalManager.onExit(sessionId, () => {
|
|
5154
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
5155
|
+
ws.close(1e3, "PTY process exited");
|
|
5156
|
+
}
|
|
5157
|
+
});
|
|
5158
|
+
ws.on("message", (msg) => {
|
|
5159
|
+
if (!sessionId) return;
|
|
5160
|
+
const str = msg.toString();
|
|
5161
|
+
try {
|
|
5162
|
+
const parsed = JSON.parse(str);
|
|
5163
|
+
if (parsed.type === "resize" && parsed.cols && parsed.rows) {
|
|
5164
|
+
terminalManager.resize(sessionId, parsed.cols, parsed.rows);
|
|
5165
|
+
return;
|
|
5166
|
+
}
|
|
5167
|
+
} catch {
|
|
5168
|
+
}
|
|
5169
|
+
terminalManager.write(sessionId, str);
|
|
5170
|
+
});
|
|
5171
|
+
ws.on("close", () => {
|
|
5172
|
+
if (sessionId) {
|
|
5173
|
+
const session = terminalManager.get(sessionId);
|
|
5174
|
+
if (session?.managed) {
|
|
5175
|
+
logger25.info("WebSocket detached from managed session (kept alive)", { sessionId });
|
|
5176
|
+
} else {
|
|
5177
|
+
logger25.info("WebSocket closed, destroying terminal session", { sessionId });
|
|
5178
|
+
terminalManager.destroy(sessionId);
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
});
|
|
5182
|
+
ws.on("error", (err) => {
|
|
5183
|
+
logger25.warn("Terminal WebSocket error", { sessionId, error: err.message });
|
|
5184
|
+
});
|
|
5185
|
+
logger25.info("Terminal WebSocket connected", { sessionId });
|
|
5186
|
+
} catch (err) {
|
|
5187
|
+
logger25.error("Failed to handle terminal WebSocket connection", { error: err.message });
|
|
5188
|
+
ws.close(1011, err.message);
|
|
5189
|
+
}
|
|
5190
|
+
});
|
|
5191
|
+
logger25.info("Terminal WebSocket handler registered");
|
|
5192
|
+
}
|
|
5193
|
+
|
|
5008
5194
|
// src/web/WebServer.ts
|
|
5009
5195
|
var __dirname2 = path8.dirname(fileURLToPath2(import.meta.url));
|
|
5010
|
-
var
|
|
5196
|
+
var logger26 = logger.child("WebServer");
|
|
5011
5197
|
var WebServer = class _WebServer {
|
|
5012
5198
|
app;
|
|
5013
5199
|
server = null;
|
|
5014
5200
|
host;
|
|
5015
5201
|
port;
|
|
5202
|
+
terminalManager;
|
|
5203
|
+
config;
|
|
5016
5204
|
constructor(trackerOrDeps, config, agentLogStore, orchestrator, mainGit) {
|
|
5017
5205
|
let apiDeps;
|
|
5018
5206
|
if (trackerOrDeps instanceof IssueTracker) {
|
|
@@ -5050,8 +5238,18 @@ var WebServer = class _WebServer {
|
|
|
5050
5238
|
previewReaper: deps.previewReaper
|
|
5051
5239
|
};
|
|
5052
5240
|
}
|
|
5053
|
-
this.app =
|
|
5054
|
-
this.app.use(
|
|
5241
|
+
this.app = express13();
|
|
5242
|
+
this.app.use(express13.json());
|
|
5243
|
+
const resolvedConfig = trackerOrDeps instanceof IssueTracker ? config : trackerOrDeps.config;
|
|
5244
|
+
this.config = resolvedConfig;
|
|
5245
|
+
this.terminalManager = (trackerOrDeps instanceof IssueTracker ? void 0 : trackerOrDeps.terminalManager) ?? new TerminalManager({
|
|
5246
|
+
idleTimeoutMs: resolvedConfig.terminal.idleTimeoutMs,
|
|
5247
|
+
maxSessions: resolvedConfig.terminal.maxSessions,
|
|
5248
|
+
allowedBaseDirs: [
|
|
5249
|
+
resolvedConfig.project.worktreeBaseDir ?? resolvedConfig.project.workDir,
|
|
5250
|
+
resolvedConfig.project.workDir
|
|
5251
|
+
].filter(Boolean)
|
|
5252
|
+
});
|
|
5055
5253
|
this.app.get("/metrics", (_req, res) => {
|
|
5056
5254
|
res.set("Content-Type", "text/plain; version=0.0.4; charset=utf-8");
|
|
5057
5255
|
res.send(metrics.serialize());
|
|
@@ -5075,12 +5273,12 @@ var WebServer = class _WebServer {
|
|
|
5075
5273
|
if (fs6.existsSync(projectKnowledgeIndex) && !fs6.existsSync(globalKnowledgeIndex)) {
|
|
5076
5274
|
try {
|
|
5077
5275
|
_WebServer.migrateKnowledgeDir(projectKnowledgeDir, globalKnowledgeDataDir);
|
|
5078
|
-
|
|
5276
|
+
logger26.info("Migrated knowledge entries from project to global DATA_DIR", {
|
|
5079
5277
|
src: projectKnowledgeDir,
|
|
5080
5278
|
dest: globalKnowledgeDataDir
|
|
5081
5279
|
});
|
|
5082
5280
|
} catch (err) {
|
|
5083
|
-
|
|
5281
|
+
logger26.warn("Failed to migrate knowledge entries to global DATA_DIR", {
|
|
5084
5282
|
error: err.message
|
|
5085
5283
|
});
|
|
5086
5284
|
}
|
|
@@ -5098,12 +5296,12 @@ var WebServer = class _WebServer {
|
|
|
5098
5296
|
if (fs6.existsSync(legacyKnowledgePath) && !fs6.existsSync(dataDirKnowledgePath)) {
|
|
5099
5297
|
try {
|
|
5100
5298
|
fs6.copyFileSync(legacyKnowledgePath, dataDirKnowledgePath);
|
|
5101
|
-
|
|
5299
|
+
logger26.info("Migrated knowledge.json from project to DATA_DIR", {
|
|
5102
5300
|
src: legacyKnowledgePath,
|
|
5103
5301
|
dest: dataDirKnowledgePath
|
|
5104
5302
|
});
|
|
5105
5303
|
} catch (err) {
|
|
5106
|
-
|
|
5304
|
+
logger26.warn("Failed to migrate knowledge.json to DATA_DIR", {
|
|
5107
5305
|
error: err.message
|
|
5108
5306
|
});
|
|
5109
5307
|
}
|
|
@@ -5160,8 +5358,13 @@ var WebServer = class _WebServer {
|
|
|
5160
5358
|
});
|
|
5161
5359
|
this.app.use(analyticsRouter);
|
|
5162
5360
|
}
|
|
5361
|
+
const terminalRouter = createTerminalRouter({
|
|
5362
|
+
terminalManager: this.terminalManager,
|
|
5363
|
+
config: apiDeps.config
|
|
5364
|
+
});
|
|
5365
|
+
this.app.use(terminalRouter);
|
|
5163
5366
|
const publicDir = apiDeps.config.web.frontendDistDir;
|
|
5164
|
-
this.app.use(
|
|
5367
|
+
this.app.use(express13.static(publicDir));
|
|
5165
5368
|
this.app.get("/{*splat}", (_req, res) => {
|
|
5166
5369
|
res.sendFile("index.html", { root: publicDir });
|
|
5167
5370
|
});
|
|
@@ -5169,16 +5372,22 @@ var WebServer = class _WebServer {
|
|
|
5169
5372
|
start() {
|
|
5170
5373
|
return new Promise((resolve) => {
|
|
5171
5374
|
this.server = this.app.listen(this.port, this.host, () => {
|
|
5172
|
-
|
|
5375
|
+
setupTerminalWebSocket({
|
|
5376
|
+
httpServer: this.server,
|
|
5377
|
+
terminalManager: this.terminalManager,
|
|
5378
|
+
isEnabled: () => getFeatureEnabled("terminal", this.config)
|
|
5379
|
+
});
|
|
5380
|
+
logger26.info(`Web UI available at http://${resolveDisplayHost(this.host)}:${this.port}`);
|
|
5173
5381
|
resolve();
|
|
5174
5382
|
});
|
|
5175
5383
|
});
|
|
5176
5384
|
}
|
|
5177
5385
|
stop() {
|
|
5386
|
+
this.terminalManager.destroyAll();
|
|
5178
5387
|
if (this.server) {
|
|
5179
5388
|
this.server.close();
|
|
5180
5389
|
this.server = null;
|
|
5181
|
-
|
|
5390
|
+
logger26.info("Web server stopped");
|
|
5182
5391
|
}
|
|
5183
5392
|
}
|
|
5184
5393
|
/**
|
|
@@ -5214,10 +5423,10 @@ var WebServer = class _WebServer {
|
|
|
5214
5423
|
};
|
|
5215
5424
|
|
|
5216
5425
|
// src/webhook/WebhookServer.ts
|
|
5217
|
-
import
|
|
5426
|
+
import express15 from "express";
|
|
5218
5427
|
|
|
5219
5428
|
// src/webhook/WebhookHandler.ts
|
|
5220
|
-
import
|
|
5429
|
+
import express14 from "express";
|
|
5221
5430
|
|
|
5222
5431
|
// src/webhook/CommandParser.ts
|
|
5223
5432
|
var TRIGGERS = ["@issue-auto", "@iaf"];
|
|
@@ -5420,7 +5629,7 @@ function parseExact(commandText) {
|
|
|
5420
5629
|
// src/webhook/CommandExecutor.ts
|
|
5421
5630
|
import path9 from "path";
|
|
5422
5631
|
import fs7 from "fs";
|
|
5423
|
-
var
|
|
5632
|
+
var logger27 = logger.child("CommandExecutor");
|
|
5424
5633
|
var CommandExecutor = class {
|
|
5425
5634
|
tracker;
|
|
5426
5635
|
orchestrator;
|
|
@@ -5439,13 +5648,13 @@ var CommandExecutor = class {
|
|
|
5439
5648
|
this.claimer = deps.claimer;
|
|
5440
5649
|
}
|
|
5441
5650
|
async execute(issueIid, issueId, command) {
|
|
5442
|
-
|
|
5651
|
+
logger27.info("Executing webhook command", { issueIid, command });
|
|
5443
5652
|
let result;
|
|
5444
5653
|
try {
|
|
5445
5654
|
result = await this.dispatch(issueIid, command);
|
|
5446
5655
|
} catch (err) {
|
|
5447
5656
|
const msg = err.message;
|
|
5448
|
-
|
|
5657
|
+
logger27.error("Webhook command failed", { issueIid, error: msg });
|
|
5449
5658
|
result = { success: false, message: msg };
|
|
5450
5659
|
}
|
|
5451
5660
|
await this.replyToIssue(issueId, result);
|
|
@@ -5577,7 +5786,6 @@ var CommandExecutor = class {
|
|
|
5577
5786
|
}
|
|
5578
5787
|
async handleRestart(iid) {
|
|
5579
5788
|
try {
|
|
5580
|
-
this.poller?.forceReleaseIssue(iid);
|
|
5581
5789
|
if (this.claimer) {
|
|
5582
5790
|
const record = this.tracker.get(iid);
|
|
5583
5791
|
if (record) {
|
|
@@ -5586,6 +5794,7 @@ var CommandExecutor = class {
|
|
|
5586
5794
|
}
|
|
5587
5795
|
}
|
|
5588
5796
|
await this.orchestrator.restartIssue(iid);
|
|
5797
|
+
this.poller?.forceReleaseIssue(iid);
|
|
5589
5798
|
} catch (err) {
|
|
5590
5799
|
return this.fail(err.message);
|
|
5591
5800
|
}
|
|
@@ -5601,7 +5810,7 @@ var CommandExecutor = class {
|
|
|
5601
5810
|
const externalId = getExternalId(record);
|
|
5602
5811
|
await this.claimer.releaseClaim(externalId, iid, "cancelled");
|
|
5603
5812
|
} catch (err) {
|
|
5604
|
-
|
|
5813
|
+
logger27.warn("Failed to release lock on cancel", { iid, error: err.message });
|
|
5605
5814
|
}
|
|
5606
5815
|
}
|
|
5607
5816
|
await this.orchestrator.cancelIssue(iid);
|
|
@@ -5701,7 +5910,7 @@ var CommandExecutor = class {
|
|
|
5701
5910
|
return this.fail(t("conflict.noMr", { iid }));
|
|
5702
5911
|
}
|
|
5703
5912
|
this.orchestrator.resolveConflict(iid).catch((err) => {
|
|
5704
|
-
|
|
5913
|
+
logger27.error("Async conflict resolution failed", { iid, error: err.message });
|
|
5705
5914
|
});
|
|
5706
5915
|
return this.ok(t("conflict.startedMsg"));
|
|
5707
5916
|
}
|
|
@@ -5743,7 +5952,7 @@ ${context}` : context;
|
|
|
5743
5952
|
const prefix = result.success ? "" : "> **Failed**\n>\n> ";
|
|
5744
5953
|
await this.gongfeng.createIssueNote(issueId, `${prefix}${result.message}`);
|
|
5745
5954
|
} catch (err) {
|
|
5746
|
-
|
|
5955
|
+
logger27.warn("Failed to reply", { issueId, error: err.message });
|
|
5747
5956
|
}
|
|
5748
5957
|
}
|
|
5749
5958
|
ok(message) {
|
|
@@ -5798,10 +6007,10 @@ var NoteDeduplicator = class {
|
|
|
5798
6007
|
};
|
|
5799
6008
|
|
|
5800
6009
|
// src/webhook/WebhookHandler.ts
|
|
5801
|
-
var { Router:
|
|
5802
|
-
var
|
|
6010
|
+
var { Router: Router13 } = express14;
|
|
6011
|
+
var logger28 = logger.child("WebhookHandler");
|
|
5803
6012
|
function createWebhookRouter(deps) {
|
|
5804
|
-
const router =
|
|
6013
|
+
const router = Router13();
|
|
5805
6014
|
const executor = new CommandExecutor(deps);
|
|
5806
6015
|
const dedup = new NoteDeduplicator();
|
|
5807
6016
|
const { config, intentRecognizer } = deps;
|
|
@@ -5814,7 +6023,7 @@ function createWebhookRouter(deps) {
|
|
|
5814
6023
|
if (config.webhook.secret) {
|
|
5815
6024
|
const token = req.headers["x-gitlab-token"];
|
|
5816
6025
|
if (token !== config.webhook.secret) {
|
|
5817
|
-
|
|
6026
|
+
logger28.warn("Webhook token mismatch");
|
|
5818
6027
|
res.status(401).json({ error: "Invalid token" });
|
|
5819
6028
|
return;
|
|
5820
6029
|
}
|
|
@@ -5831,7 +6040,7 @@ function createWebhookRouter(deps) {
|
|
|
5831
6040
|
}
|
|
5832
6041
|
const noteId = event.object_attributes.id;
|
|
5833
6042
|
if (dedup.isDuplicate(noteId)) {
|
|
5834
|
-
|
|
6043
|
+
logger28.debug("Duplicate note, skipping", { noteId });
|
|
5835
6044
|
res.json({ ignored: true, reason: "duplicate" });
|
|
5836
6045
|
return;
|
|
5837
6046
|
}
|
|
@@ -5843,7 +6052,7 @@ function createWebhookRouter(deps) {
|
|
|
5843
6052
|
return;
|
|
5844
6053
|
}
|
|
5845
6054
|
if (!issueId) {
|
|
5846
|
-
|
|
6055
|
+
logger28.info("Webhook received with null issue id (likely a test ping)", { issueIid });
|
|
5847
6056
|
res.json({ accepted: true, noteId, issueIid, test: true });
|
|
5848
6057
|
return;
|
|
5849
6058
|
}
|
|
@@ -5883,19 +6092,19 @@ async function processCommandAsync(noteBody, issueIid, issueId, executor, intent
|
|
|
5883
6092
|
if (!command && intentRecognizer && config.webhook.llmFallback) {
|
|
5884
6093
|
const record = tracker.get(issueIid);
|
|
5885
6094
|
const state = record?.state;
|
|
5886
|
-
|
|
6095
|
+
logger28.info("Falling back to LLM intent recognition", { issueIid });
|
|
5887
6096
|
command = await intentRecognizer.recognize(cmdText, state);
|
|
5888
6097
|
}
|
|
5889
6098
|
if (!command) {
|
|
5890
|
-
|
|
6099
|
+
logger28.info("Could not parse command from note", { issueIid, text: cmdText.slice(0, 100) });
|
|
5891
6100
|
await gongfeng.createIssueNote(issueId, HELP_TEXT_FUNC()).catch(
|
|
5892
|
-
(e) =>
|
|
6101
|
+
(e) => logger28.warn("Failed to reply help text", { error: e.message })
|
|
5893
6102
|
);
|
|
5894
6103
|
return;
|
|
5895
6104
|
}
|
|
5896
6105
|
await executor.execute(issueIid, issueId, command);
|
|
5897
6106
|
} catch (err) {
|
|
5898
|
-
|
|
6107
|
+
logger28.error("Failed to process webhook command", {
|
|
5899
6108
|
issueIid,
|
|
5900
6109
|
error: err.message
|
|
5901
6110
|
});
|
|
@@ -5915,7 +6124,7 @@ async function processMRCommandAsync(noteBody, mr, executor, tracker, gongfeng)
|
|
|
5915
6124
|
const command = parseExact(cmdText);
|
|
5916
6125
|
if (!command) {
|
|
5917
6126
|
await gongfeng.createMergeRequestNote(mr.iid, HELP_TEXT_FUNC()).catch(
|
|
5918
|
-
(e) =>
|
|
6127
|
+
(e) => logger28.warn("Failed to reply help text on MR", { error: e.message })
|
|
5919
6128
|
);
|
|
5920
6129
|
return;
|
|
5921
6130
|
}
|
|
@@ -5924,7 +6133,7 @@ async function processMRCommandAsync(noteBody, mr, executor, tracker, gongfeng)
|
|
|
5924
6133
|
mr.iid,
|
|
5925
6134
|
`\`${command.intent}\` command is not supported on MR comments. Please use Issue comments instead.`
|
|
5926
6135
|
).catch(
|
|
5927
|
-
(e) =>
|
|
6136
|
+
(e) => logger28.warn("Failed to reply on MR", { error: e.message })
|
|
5928
6137
|
);
|
|
5929
6138
|
return;
|
|
5930
6139
|
}
|
|
@@ -5935,7 +6144,7 @@ async function processMRCommandAsync(noteBody, mr, executor, tracker, gongfeng)
|
|
|
5935
6144
|
mr.iid,
|
|
5936
6145
|
`Could not find a tracked issue for branch \`${mr.source_branch}\`.`
|
|
5937
6146
|
).catch(
|
|
5938
|
-
(e) =>
|
|
6147
|
+
(e) => logger28.warn("Failed to reply on MR", { error: e.message })
|
|
5939
6148
|
);
|
|
5940
6149
|
return;
|
|
5941
6150
|
}
|
|
@@ -5944,10 +6153,10 @@ async function processMRCommandAsync(noteBody, mr, executor, tracker, gongfeng)
|
|
|
5944
6153
|
const prefix = result.success ? "" : "> **Failed**\n>\n> ";
|
|
5945
6154
|
await gongfeng.createMergeRequestNote(mr.iid, `${prefix}${result.message}`);
|
|
5946
6155
|
} catch (err) {
|
|
5947
|
-
|
|
6156
|
+
logger28.warn("Failed to reply on MR", { mrIid: mr.iid, error: err.message });
|
|
5948
6157
|
}
|
|
5949
6158
|
} catch (err) {
|
|
5950
|
-
|
|
6159
|
+
logger28.error("Failed to process MR webhook command", {
|
|
5951
6160
|
mrIid: mr.iid,
|
|
5952
6161
|
error: err.message
|
|
5953
6162
|
});
|
|
@@ -5959,7 +6168,7 @@ function isSelfNote(_event, _selfToken) {
|
|
|
5959
6168
|
|
|
5960
6169
|
// src/webhook/IntentRecognizer.ts
|
|
5961
6170
|
import { spawn } from "child_process";
|
|
5962
|
-
var
|
|
6171
|
+
var logger29 = logger.child("IntentRecognizer");
|
|
5963
6172
|
var VALID_INTENTS = [
|
|
5964
6173
|
"retry",
|
|
5965
6174
|
"retry-from",
|
|
@@ -6001,7 +6210,7 @@ ${userComment}`;
|
|
|
6001
6210
|
const rawOutput = await this.runLLM(userPrompt);
|
|
6002
6211
|
return this.parseResponse(rawOutput);
|
|
6003
6212
|
} catch (err) {
|
|
6004
|
-
|
|
6213
|
+
logger29.error("Intent recognition failed", { error: err.message });
|
|
6005
6214
|
return null;
|
|
6006
6215
|
}
|
|
6007
6216
|
}
|
|
@@ -6009,20 +6218,20 @@ ${userComment}`;
|
|
|
6009
6218
|
parseResponse(raw) {
|
|
6010
6219
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
6011
6220
|
if (!jsonMatch) {
|
|
6012
|
-
|
|
6221
|
+
logger29.warn("No JSON found in LLM response", { raw: raw.slice(0, 200) });
|
|
6013
6222
|
return null;
|
|
6014
6223
|
}
|
|
6015
6224
|
let parsed;
|
|
6016
6225
|
try {
|
|
6017
6226
|
parsed = JSON.parse(jsonMatch[0]);
|
|
6018
6227
|
} catch {
|
|
6019
|
-
|
|
6228
|
+
logger29.warn("Failed to parse JSON from LLM", { raw: jsonMatch[0].slice(0, 200) });
|
|
6020
6229
|
return null;
|
|
6021
6230
|
}
|
|
6022
6231
|
if (parsed.intent === null || parsed.intent === "null") return null;
|
|
6023
6232
|
const intent = String(parsed.intent);
|
|
6024
6233
|
if (!VALID_INTENTS.includes(intent)) {
|
|
6025
|
-
|
|
6234
|
+
logger29.warn("Invalid intent from LLM", { intent });
|
|
6026
6235
|
return null;
|
|
6027
6236
|
}
|
|
6028
6237
|
const result = { intent };
|
|
@@ -6064,7 +6273,7 @@ ${userComment}`;
|
|
|
6064
6273
|
child.on("close", (code) => {
|
|
6065
6274
|
clearTimeout(timer);
|
|
6066
6275
|
if (code !== 0) {
|
|
6067
|
-
|
|
6276
|
+
logger29.warn("LLM process exited with non-zero code", { code, stderr: stderr.slice(0, 300) });
|
|
6068
6277
|
}
|
|
6069
6278
|
resolve(stdout);
|
|
6070
6279
|
});
|
|
@@ -6079,7 +6288,7 @@ ${userComment}`;
|
|
|
6079
6288
|
};
|
|
6080
6289
|
|
|
6081
6290
|
// src/webhook/WebhookServer.ts
|
|
6082
|
-
var
|
|
6291
|
+
var logger30 = logger.child("WebhookServer");
|
|
6083
6292
|
var WebhookServer = class {
|
|
6084
6293
|
app;
|
|
6085
6294
|
server = null;
|
|
@@ -6103,8 +6312,8 @@ var WebhookServer = class {
|
|
|
6103
6312
|
poller: deps.poller,
|
|
6104
6313
|
claimer: deps.claimer
|
|
6105
6314
|
};
|
|
6106
|
-
this.app =
|
|
6107
|
-
this.app.use(
|
|
6315
|
+
this.app = express15();
|
|
6316
|
+
this.app.use(express15.json());
|
|
6108
6317
|
this.app.use(createWebhookRouter(handlerDeps));
|
|
6109
6318
|
this.app.get("/health", (_req, res) => {
|
|
6110
6319
|
res.json({ status: "ok", service: "webhook" });
|
|
@@ -6113,7 +6322,7 @@ var WebhookServer = class {
|
|
|
6113
6322
|
start() {
|
|
6114
6323
|
return new Promise((resolve) => {
|
|
6115
6324
|
this.server = this.app.listen(this.port, this.host, () => {
|
|
6116
|
-
|
|
6325
|
+
logger30.info(`Webhook server listening on http://${resolveDisplayHost(this.host)}:${this.port}`);
|
|
6117
6326
|
resolve();
|
|
6118
6327
|
});
|
|
6119
6328
|
});
|
|
@@ -6122,7 +6331,7 @@ var WebhookServer = class {
|
|
|
6122
6331
|
if (this.server) {
|
|
6123
6332
|
this.server.close();
|
|
6124
6333
|
this.server = null;
|
|
6125
|
-
|
|
6334
|
+
logger30.info("Webhook server stopped");
|
|
6126
6335
|
}
|
|
6127
6336
|
}
|
|
6128
6337
|
};
|
|
@@ -6130,7 +6339,7 @@ var WebhookServer = class {
|
|
|
6130
6339
|
// src/web/AgentLogStore.ts
|
|
6131
6340
|
import fs8 from "fs";
|
|
6132
6341
|
import path10 from "path";
|
|
6133
|
-
var
|
|
6342
|
+
var logger31 = logger.child("AgentLogStore");
|
|
6134
6343
|
var MAX_LOGS_PER_ISSUE = 2e4;
|
|
6135
6344
|
var DEBUG_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
6136
6345
|
"thinking",
|
|
@@ -6159,7 +6368,7 @@ var AgentLogStore = class {
|
|
|
6159
6368
|
eventBus.on("pipeline:progress", (payload) => {
|
|
6160
6369
|
this.handlePipelineProgress(payload);
|
|
6161
6370
|
});
|
|
6162
|
-
|
|
6371
|
+
logger31.info("AgentLogStore listening for events");
|
|
6163
6372
|
}
|
|
6164
6373
|
getLogs(issueIid) {
|
|
6165
6374
|
const filePath = this.logFilePath(issueIid);
|
|
@@ -6169,7 +6378,7 @@ var AgentLogStore = class {
|
|
|
6169
6378
|
if (!raw) return [];
|
|
6170
6379
|
return raw.split("\n").map((line) => JSON.parse(line));
|
|
6171
6380
|
} catch (err) {
|
|
6172
|
-
|
|
6381
|
+
logger31.warn("Failed to read agent logs", { issueIid, error: err.message });
|
|
6173
6382
|
return [];
|
|
6174
6383
|
}
|
|
6175
6384
|
}
|
|
@@ -6189,7 +6398,7 @@ var AgentLogStore = class {
|
|
|
6189
6398
|
`, "utf-8");
|
|
6190
6399
|
this.trimIfNeeded(issueIid, filePath);
|
|
6191
6400
|
} catch (err) {
|
|
6192
|
-
|
|
6401
|
+
logger31.warn("Failed to write agent log", { issueIid, error: err.message });
|
|
6193
6402
|
}
|
|
6194
6403
|
}
|
|
6195
6404
|
trimIfNeeded(issueIid, filePath) {
|
|
@@ -6201,7 +6410,7 @@ var AgentLogStore = class {
|
|
|
6201
6410
|
const trimmed = lines.slice(-MAX_LOGS_PER_ISSUE);
|
|
6202
6411
|
fs8.writeFileSync(filePath, `${trimmed.join("\n")}
|
|
6203
6412
|
`, "utf-8");
|
|
6204
|
-
|
|
6413
|
+
logger31.info("Agent logs trimmed", { issueIid, from: lines.length, to: trimmed.length });
|
|
6205
6414
|
}
|
|
6206
6415
|
} catch {
|
|
6207
6416
|
}
|
|
@@ -6276,7 +6485,7 @@ import { readFileSync, existsSync } from "fs";
|
|
|
6276
6485
|
import path11 from "path";
|
|
6277
6486
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6278
6487
|
var __dirname3 = path11.dirname(fileURLToPath3(import.meta.url));
|
|
6279
|
-
var
|
|
6488
|
+
var logger32 = logger.child("VersionChecker");
|
|
6280
6489
|
function compareSemver(a, b) {
|
|
6281
6490
|
const pa = a.split(".").map(Number);
|
|
6282
6491
|
const pb = b.split(".").map(Number);
|
|
@@ -6307,7 +6516,7 @@ var VersionChecker = class {
|
|
|
6307
6516
|
async check() {
|
|
6308
6517
|
const latestVersion = await this.fetchLatestVersion();
|
|
6309
6518
|
const hasUpdate = compareSemver(latestVersion, this.currentVersion) > 0;
|
|
6310
|
-
|
|
6519
|
+
logger32.info("Version check completed", {
|
|
6311
6520
|
current: this.currentVersion,
|
|
6312
6521
|
latest: latestVersion,
|
|
6313
6522
|
hasUpdate
|
|
@@ -6355,7 +6564,7 @@ import { cpSync, existsSync as existsSync2, rmSync } from "fs";
|
|
|
6355
6564
|
import path12 from "path";
|
|
6356
6565
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6357
6566
|
var __dirname4 = path12.dirname(fileURLToPath4(import.meta.url));
|
|
6358
|
-
var
|
|
6567
|
+
var logger33 = logger.child("UpdateExecutor");
|
|
6359
6568
|
function findProjectRoot() {
|
|
6360
6569
|
for (let dir = __dirname4; dir !== path12.dirname(dir); dir = path12.dirname(dir)) {
|
|
6361
6570
|
if (existsSync2(path12.join(dir, "package.json"))) {
|
|
@@ -6385,7 +6594,7 @@ var UpdateExecutor = class {
|
|
|
6385
6594
|
async execute(targetVersion) {
|
|
6386
6595
|
const distDir = path12.join(this.projectRoot, "dist");
|
|
6387
6596
|
const backupDir = path12.join(this.projectRoot, "dist.backup");
|
|
6388
|
-
|
|
6597
|
+
logger33.info("Backing up dist directory...");
|
|
6389
6598
|
if (existsSync2(backupDir)) {
|
|
6390
6599
|
rmSync(backupDir, { recursive: true, force: true });
|
|
6391
6600
|
}
|
|
@@ -6393,12 +6602,12 @@ var UpdateExecutor = class {
|
|
|
6393
6602
|
cpSync(distDir, backupDir, { recursive: true });
|
|
6394
6603
|
}
|
|
6395
6604
|
try {
|
|
6396
|
-
|
|
6605
|
+
logger33.info("Running package update...", { package: this.packageName, targetVersion, bin: "npm" });
|
|
6397
6606
|
await run("npm", ["update", this.packageName], this.projectRoot, 12e4);
|
|
6398
|
-
|
|
6607
|
+
logger33.info("Running build...", { bin: "npm" });
|
|
6399
6608
|
await run("npm", ["run", "build"], this.projectRoot, 12e4);
|
|
6400
6609
|
} catch (err) {
|
|
6401
|
-
|
|
6610
|
+
logger33.error("Update failed, rolling back dist...", { error: err.message });
|
|
6402
6611
|
if (existsSync2(backupDir)) {
|
|
6403
6612
|
if (existsSync2(distDir)) {
|
|
6404
6613
|
rmSync(distDir, { recursive: true, force: true });
|
|
@@ -6411,21 +6620,21 @@ var UpdateExecutor = class {
|
|
|
6411
6620
|
rmSync(backupDir, { recursive: true, force: true });
|
|
6412
6621
|
}
|
|
6413
6622
|
}
|
|
6414
|
-
|
|
6623
|
+
logger33.info("Reloading via PM2...");
|
|
6415
6624
|
try {
|
|
6416
6625
|
await run("pm2", ["reload", "issue-auto-finish"], this.projectRoot, 3e4);
|
|
6417
6626
|
} catch (err) {
|
|
6418
|
-
|
|
6627
|
+
logger33.warn("PM2 reload failed, the process may need manual restart", {
|
|
6419
6628
|
error: err.message
|
|
6420
6629
|
});
|
|
6421
6630
|
throw err;
|
|
6422
6631
|
}
|
|
6423
|
-
|
|
6632
|
+
logger33.info("Update completed successfully", { version: targetVersion });
|
|
6424
6633
|
}
|
|
6425
6634
|
};
|
|
6426
6635
|
|
|
6427
6636
|
// src/updater/AutoUpdater.ts
|
|
6428
|
-
var
|
|
6637
|
+
var logger34 = logger.child("AutoUpdater");
|
|
6429
6638
|
var INITIAL_DELAY_MS = 3e4;
|
|
6430
6639
|
var AutoUpdater = class {
|
|
6431
6640
|
config;
|
|
@@ -6447,10 +6656,10 @@ var AutoUpdater = class {
|
|
|
6447
6656
|
}
|
|
6448
6657
|
start() {
|
|
6449
6658
|
if (!this.config.autoUpdate.enabled) {
|
|
6450
|
-
|
|
6659
|
+
logger34.info("Auto-update is disabled");
|
|
6451
6660
|
return;
|
|
6452
6661
|
}
|
|
6453
|
-
|
|
6662
|
+
logger34.info("Auto-updater starting", {
|
|
6454
6663
|
intervalMs: this.config.autoUpdate.intervalMs,
|
|
6455
6664
|
registry: this.config.autoUpdate.registry
|
|
6456
6665
|
});
|
|
@@ -6469,7 +6678,7 @@ var AutoUpdater = class {
|
|
|
6469
6678
|
clearInterval(this.checkTimer);
|
|
6470
6679
|
this.checkTimer = null;
|
|
6471
6680
|
}
|
|
6472
|
-
|
|
6681
|
+
logger34.info("Auto-updater stopped");
|
|
6473
6682
|
}
|
|
6474
6683
|
getStatus() {
|
|
6475
6684
|
return {
|
|
@@ -6509,7 +6718,7 @@ var AutoUpdater = class {
|
|
|
6509
6718
|
}
|
|
6510
6719
|
async triggerUpdate() {
|
|
6511
6720
|
if (this.updateInProgress) {
|
|
6512
|
-
|
|
6721
|
+
logger34.warn("Update already in progress, skipping");
|
|
6513
6722
|
return;
|
|
6514
6723
|
}
|
|
6515
6724
|
this.updateInProgress = true;
|
|
@@ -6517,40 +6726,40 @@ var AutoUpdater = class {
|
|
|
6517
6726
|
if (!this.latestVersion) {
|
|
6518
6727
|
const result = await this.checkForUpdate();
|
|
6519
6728
|
if (!result.hasUpdate) {
|
|
6520
|
-
|
|
6729
|
+
logger34.info("No update available");
|
|
6521
6730
|
return;
|
|
6522
6731
|
}
|
|
6523
6732
|
}
|
|
6524
6733
|
const targetVersion = this.latestVersion;
|
|
6525
6734
|
this.state = "draining";
|
|
6526
|
-
|
|
6735
|
+
logger34.info("Draining active issues before update...");
|
|
6527
6736
|
this.poller.pauseDiscovery();
|
|
6528
6737
|
const drainStart = Date.now();
|
|
6529
6738
|
const drainTimeout = this.config.autoUpdate.drainTimeoutMs;
|
|
6530
6739
|
while (this.poller.getActiveCount() > 0 && Date.now() - drainStart < drainTimeout) {
|
|
6531
|
-
|
|
6740
|
+
logger34.info("Waiting for active issues to complete...", {
|
|
6532
6741
|
active: this.poller.getActiveCount()
|
|
6533
6742
|
});
|
|
6534
6743
|
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
6535
6744
|
}
|
|
6536
6745
|
if (this.poller.getActiveCount() > 0) {
|
|
6537
|
-
|
|
6746
|
+
logger34.warn("Drain timeout reached, proceeding with update", {
|
|
6538
6747
|
active: this.poller.getActiveCount()
|
|
6539
6748
|
});
|
|
6540
6749
|
}
|
|
6541
6750
|
this.state = "updating";
|
|
6542
6751
|
eventBus.emitTyped("update:downloading", { version: targetVersion });
|
|
6543
|
-
|
|
6752
|
+
logger34.info("Executing update...", { targetVersion });
|
|
6544
6753
|
await this.updateExecutor.execute(targetVersion);
|
|
6545
6754
|
this.state = "completed";
|
|
6546
6755
|
eventBus.emitTyped("update:completed", { version: targetVersion });
|
|
6547
|
-
|
|
6756
|
+
logger34.info("Update completed", { version: targetVersion });
|
|
6548
6757
|
} catch (err) {
|
|
6549
6758
|
const message = err.message;
|
|
6550
6759
|
this.lastError = message;
|
|
6551
6760
|
this.state = "failed";
|
|
6552
6761
|
eventBus.emitTyped("update:failed", { error: message });
|
|
6553
|
-
|
|
6762
|
+
logger34.error("Update failed", { error: message });
|
|
6554
6763
|
this.poller.resumeDiscovery();
|
|
6555
6764
|
} finally {
|
|
6556
6765
|
this.updateInProgress = false;
|
|
@@ -6561,20 +6770,20 @@ var AutoUpdater = class {
|
|
|
6561
6770
|
try {
|
|
6562
6771
|
const result = await this.checkForUpdate();
|
|
6563
6772
|
if (result.hasUpdate) {
|
|
6564
|
-
|
|
6773
|
+
logger34.info("New version available, starting update", {
|
|
6565
6774
|
current: result.currentVersion,
|
|
6566
6775
|
latest: result.latestVersion
|
|
6567
6776
|
});
|
|
6568
6777
|
await this.triggerUpdate();
|
|
6569
6778
|
}
|
|
6570
6779
|
} catch (err) {
|
|
6571
|
-
|
|
6780
|
+
logger34.warn("Periodic version check failed", { error: err.message });
|
|
6572
6781
|
}
|
|
6573
6782
|
}
|
|
6574
6783
|
};
|
|
6575
6784
|
|
|
6576
6785
|
// src/config/ConfigReloader.ts
|
|
6577
|
-
var
|
|
6786
|
+
var logger35 = logger.child("ConfigReloader");
|
|
6578
6787
|
var ConfigReloader = class {
|
|
6579
6788
|
config;
|
|
6580
6789
|
poller;
|
|
@@ -6590,11 +6799,11 @@ var ConfigReloader = class {
|
|
|
6590
6799
|
}
|
|
6591
6800
|
async reload(opts) {
|
|
6592
6801
|
const timeoutMs = opts?.waitTimeoutMs ?? 6e4;
|
|
6593
|
-
|
|
6802
|
+
logger35.info("Config reload requested", { timeoutMs });
|
|
6594
6803
|
this.poller.pauseDiscovery();
|
|
6595
6804
|
const drained = await this.waitForDrain(timeoutMs);
|
|
6596
6805
|
if (!drained) {
|
|
6597
|
-
|
|
6806
|
+
logger35.warn("Active issues did not drain within timeout, aborting reload", {
|
|
6598
6807
|
active: this.poller.getActiveCount(),
|
|
6599
6808
|
timeoutMs
|
|
6600
6809
|
});
|
|
@@ -6609,7 +6818,7 @@ var ConfigReloader = class {
|
|
|
6609
6818
|
this.orchestrator.setAIRunner(newRunner);
|
|
6610
6819
|
this.options.onAIRunnerChanged?.(newRunner);
|
|
6611
6820
|
this.gongfeng.updateConfig(this.config.gongfeng);
|
|
6612
|
-
|
|
6821
|
+
logger35.info("Config reloaded successfully", {
|
|
6613
6822
|
aiMode: this.config.ai.mode,
|
|
6614
6823
|
aiModel: this.config.ai.model,
|
|
6615
6824
|
discoveryIntervalMs: this.config.poll.discoveryIntervalMs
|
|
@@ -6630,7 +6839,7 @@ var ConfigReloader = class {
|
|
|
6630
6839
|
|
|
6631
6840
|
// src/distill/DiaryCollector.ts
|
|
6632
6841
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6633
|
-
var
|
|
6842
|
+
var logger36 = logger.child("DiaryCollector");
|
|
6634
6843
|
var DiaryCollector = class {
|
|
6635
6844
|
tracker;
|
|
6636
6845
|
diaryStore;
|
|
@@ -6648,21 +6857,21 @@ var DiaryCollector = class {
|
|
|
6648
6857
|
if (this.stateChangedHandler) return;
|
|
6649
6858
|
this.stateChangedHandler = (payload) => {
|
|
6650
6859
|
this.handleStateChanged(payload).catch((err) => {
|
|
6651
|
-
|
|
6860
|
+
logger36.warn("DiaryCollector failed to handle stateChanged event", {
|
|
6652
6861
|
error: err.message
|
|
6653
6862
|
});
|
|
6654
6863
|
});
|
|
6655
6864
|
};
|
|
6656
6865
|
this.failedHandler = (payload) => {
|
|
6657
6866
|
this.handleFailed(payload).catch((err) => {
|
|
6658
|
-
|
|
6867
|
+
logger36.warn("DiaryCollector failed to handle failed event", {
|
|
6659
6868
|
error: err.message
|
|
6660
6869
|
});
|
|
6661
6870
|
});
|
|
6662
6871
|
};
|
|
6663
6872
|
eventBus.on("issue:stateChanged", this.stateChangedHandler);
|
|
6664
6873
|
eventBus.on("issue:failed", this.failedHandler);
|
|
6665
|
-
|
|
6874
|
+
logger36.info("DiaryCollector started");
|
|
6666
6875
|
}
|
|
6667
6876
|
/** 停止监听事件 */
|
|
6668
6877
|
stop() {
|
|
@@ -6674,7 +6883,7 @@ var DiaryCollector = class {
|
|
|
6674
6883
|
eventBus.off("issue:failed", this.failedHandler);
|
|
6675
6884
|
this.failedHandler = null;
|
|
6676
6885
|
}
|
|
6677
|
-
|
|
6886
|
+
logger36.info("DiaryCollector stopped");
|
|
6678
6887
|
}
|
|
6679
6888
|
/** 处理 issue:stateChanged 事件 */
|
|
6680
6889
|
async handleStateChanged(payload) {
|
|
@@ -6685,7 +6894,7 @@ var DiaryCollector = class {
|
|
|
6685
6894
|
if (!issueIid) return;
|
|
6686
6895
|
const existing = this.diaryStore.getByIssueIid(issueIid);
|
|
6687
6896
|
if (existing.some((d) => d.outcome === "completed")) {
|
|
6688
|
-
|
|
6897
|
+
logger36.debug("Diary already exists for completed issue", { issueIid });
|
|
6689
6898
|
return;
|
|
6690
6899
|
}
|
|
6691
6900
|
await this.collectDiary(issueIid, "completed");
|
|
@@ -6702,7 +6911,7 @@ var DiaryCollector = class {
|
|
|
6702
6911
|
try {
|
|
6703
6912
|
const record = this.tracker.get(issueIid);
|
|
6704
6913
|
if (!record) {
|
|
6705
|
-
|
|
6914
|
+
logger36.warn("Cannot collect diary: issue record not found", { issueIid });
|
|
6706
6915
|
return null;
|
|
6707
6916
|
}
|
|
6708
6917
|
const plan = this.createPlanPersistence?.(issueIid);
|
|
@@ -6726,10 +6935,10 @@ var DiaryCollector = class {
|
|
|
6726
6935
|
};
|
|
6727
6936
|
this.diaryStore.create(diary);
|
|
6728
6937
|
eventBus.emitTyped("distill:diary:created", { issueIid, diaryId: diary.id, outcome });
|
|
6729
|
-
|
|
6938
|
+
logger36.info("Diary collected", { issueIid, diaryId: diary.id, outcome });
|
|
6730
6939
|
return diary;
|
|
6731
6940
|
} catch (err) {
|
|
6732
|
-
|
|
6941
|
+
logger36.warn("Failed to collect diary", {
|
|
6733
6942
|
issueIid,
|
|
6734
6943
|
outcome,
|
|
6735
6944
|
error: err.message
|
|
@@ -6952,7 +7161,7 @@ ${memorySection}
|
|
|
6952
7161
|
}
|
|
6953
7162
|
|
|
6954
7163
|
// src/distill/MemoryDistiller.ts
|
|
6955
|
-
var
|
|
7164
|
+
var logger37 = logger.child("MemoryDistiller");
|
|
6956
7165
|
var MemoryDistiller = class {
|
|
6957
7166
|
aiRunner;
|
|
6958
7167
|
diaryStore;
|
|
@@ -6977,17 +7186,17 @@ var MemoryDistiller = class {
|
|
|
6977
7186
|
async distill(options) {
|
|
6978
7187
|
const undistilled = this.diaryStore.getUndistilled();
|
|
6979
7188
|
if (!options?.force && undistilled.length < this.minDiariesForDistill) {
|
|
6980
|
-
|
|
7189
|
+
logger37.info("Not enough undistilled diaries, skipping memory distillation", {
|
|
6981
7190
|
count: undistilled.length,
|
|
6982
7191
|
threshold: this.minDiariesForDistill
|
|
6983
7192
|
});
|
|
6984
7193
|
return { processedDiaries: 0, actions: 0 };
|
|
6985
7194
|
}
|
|
6986
7195
|
if (undistilled.length === 0) {
|
|
6987
|
-
|
|
7196
|
+
logger37.info("No undistilled diaries available");
|
|
6988
7197
|
return { processedDiaries: 0, actions: 0 };
|
|
6989
7198
|
}
|
|
6990
|
-
|
|
7199
|
+
logger37.info("Starting memory distillation", { diaryCount: undistilled.length });
|
|
6991
7200
|
const existingMemories = this.loadExistingMemories();
|
|
6992
7201
|
const prompt = buildMemoryDistillPrompt(undistilled, existingMemories);
|
|
6993
7202
|
const result = await this.aiRunner.run({
|
|
@@ -6996,12 +7205,12 @@ var MemoryDistiller = class {
|
|
|
6996
7205
|
timeoutMs: this.timeoutMs
|
|
6997
7206
|
});
|
|
6998
7207
|
if (!result.success) {
|
|
6999
|
-
|
|
7208
|
+
logger37.error("AI distillation failed", { output: result.output.slice(0, 500) });
|
|
7000
7209
|
throw new Error(`Memory distillation AI call failed: ${result.output.slice(0, 200)}`);
|
|
7001
7210
|
}
|
|
7002
7211
|
const actions = this.parseActions(result.output);
|
|
7003
7212
|
if (actions.length === 0) {
|
|
7004
|
-
|
|
7213
|
+
logger37.info("No distillation actions returned by AI");
|
|
7005
7214
|
this.diaryStore.markDistilled(undistilled.map((d) => d.id));
|
|
7006
7215
|
return { processedDiaries: undistilled.length, actions: 0 };
|
|
7007
7216
|
}
|
|
@@ -7011,14 +7220,14 @@ var MemoryDistiller = class {
|
|
|
7011
7220
|
this.executeAction(action, existingMemories);
|
|
7012
7221
|
actionCount++;
|
|
7013
7222
|
} catch (err) {
|
|
7014
|
-
|
|
7223
|
+
logger37.warn("Failed to execute distill action", {
|
|
7015
7224
|
action: action.type,
|
|
7016
7225
|
error: err.message
|
|
7017
7226
|
});
|
|
7018
7227
|
}
|
|
7019
7228
|
}
|
|
7020
7229
|
this.diaryStore.markDistilled(undistilled.map((d) => d.id));
|
|
7021
|
-
|
|
7230
|
+
logger37.info("Memory distillation complete", {
|
|
7022
7231
|
processedDiaries: undistilled.length,
|
|
7023
7232
|
actions: actionCount
|
|
7024
7233
|
});
|
|
@@ -7042,18 +7251,18 @@ var MemoryDistiller = class {
|
|
|
7042
7251
|
try {
|
|
7043
7252
|
const jsonMatch = output.match(/```json\s*([\s\S]*?)```/) ?? output.match(/\{[\s\S]*"actions"[\s\S]*\}/);
|
|
7044
7253
|
if (!jsonMatch) {
|
|
7045
|
-
|
|
7254
|
+
logger37.warn("No JSON found in AI output");
|
|
7046
7255
|
return [];
|
|
7047
7256
|
}
|
|
7048
7257
|
const jsonStr = jsonMatch[1] ?? jsonMatch[0];
|
|
7049
7258
|
const parsed = JSON.parse(jsonStr);
|
|
7050
7259
|
if (!Array.isArray(parsed.actions)) {
|
|
7051
|
-
|
|
7260
|
+
logger37.warn("Invalid actions format in AI output");
|
|
7052
7261
|
return [];
|
|
7053
7262
|
}
|
|
7054
7263
|
return parsed.actions;
|
|
7055
7264
|
} catch (err) {
|
|
7056
|
-
|
|
7265
|
+
logger37.warn("Failed to parse AI distillation output", {
|
|
7057
7266
|
error: err.message,
|
|
7058
7267
|
output: output.slice(0, 300)
|
|
7059
7268
|
});
|
|
@@ -7073,7 +7282,7 @@ var MemoryDistiller = class {
|
|
|
7073
7282
|
this.supersedeMemory(action, existingMemories);
|
|
7074
7283
|
break;
|
|
7075
7284
|
default:
|
|
7076
|
-
|
|
7285
|
+
logger37.warn("Unknown distill action type", { type: action.type });
|
|
7077
7286
|
}
|
|
7078
7287
|
}
|
|
7079
7288
|
/** 创建新 memory */
|
|
@@ -7103,13 +7312,13 @@ var MemoryDistiller = class {
|
|
|
7103
7312
|
action: "created",
|
|
7104
7313
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7105
7314
|
});
|
|
7106
|
-
|
|
7315
|
+
logger37.info("Created new memory", { id: memoryEntry.id, theme: action.theme, title: action.title });
|
|
7107
7316
|
}
|
|
7108
7317
|
/** 合并新证据到已有 memory */
|
|
7109
7318
|
mergeMemory(action, existingMemories) {
|
|
7110
7319
|
const existing = existingMemories.find((m) => m.id === action.memoryId);
|
|
7111
7320
|
if (!existing) {
|
|
7112
|
-
|
|
7321
|
+
logger37.warn("Cannot merge: memory not found", { memoryId: action.memoryId });
|
|
7113
7322
|
return;
|
|
7114
7323
|
}
|
|
7115
7324
|
existing.evidence = [.../* @__PURE__ */ new Set([...existing.evidence, ...action.newEvidence])];
|
|
@@ -7142,7 +7351,7 @@ var MemoryDistiller = class {
|
|
|
7142
7351
|
reason: `Merged ${action.newEvidence.length} new evidence(s)`,
|
|
7143
7352
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7144
7353
|
});
|
|
7145
|
-
|
|
7354
|
+
logger37.info("Merged memory", { id: existing.id, newEvidence: action.newEvidence.length });
|
|
7146
7355
|
}
|
|
7147
7356
|
/** 用新 memory 替代旧 memory */
|
|
7148
7357
|
supersedeMemory(action, existingMemories) {
|
|
@@ -7184,7 +7393,7 @@ var MemoryDistiller = class {
|
|
|
7184
7393
|
reason: `Supersedes ${action.oldMemoryId}`,
|
|
7185
7394
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7186
7395
|
});
|
|
7187
|
-
|
|
7396
|
+
logger37.info("Superseded memory", {
|
|
7188
7397
|
oldId: action.oldMemoryId,
|
|
7189
7398
|
newId: newMemory.id,
|
|
7190
7399
|
theme: action.theme
|
|
@@ -7286,7 +7495,7 @@ ${ruleSection}
|
|
|
7286
7495
|
}
|
|
7287
7496
|
|
|
7288
7497
|
// src/distill/AgentRuleDistiller.ts
|
|
7289
|
-
var
|
|
7498
|
+
var logger38 = logger.child("AgentRuleDistiller");
|
|
7290
7499
|
var AgentRuleDistiller = class {
|
|
7291
7500
|
aiRunner;
|
|
7292
7501
|
knowledgeStore;
|
|
@@ -7315,11 +7524,11 @@ var AgentRuleDistiller = class {
|
|
|
7315
7524
|
async distill() {
|
|
7316
7525
|
const matureMemories = this.getMatureMemories();
|
|
7317
7526
|
if (matureMemories.length === 0) {
|
|
7318
|
-
|
|
7527
|
+
logger38.info("No mature memories ready for rule distillation");
|
|
7319
7528
|
return { processedMemories: 0, actions: 0 };
|
|
7320
7529
|
}
|
|
7321
7530
|
const existingRules = this.loadExistingRules();
|
|
7322
|
-
|
|
7531
|
+
logger38.info("Starting rule distillation", {
|
|
7323
7532
|
matureMemories: matureMemories.length,
|
|
7324
7533
|
existingRules: existingRules.length
|
|
7325
7534
|
});
|
|
@@ -7330,12 +7539,12 @@ var AgentRuleDistiller = class {
|
|
|
7330
7539
|
timeoutMs: this.timeoutMs
|
|
7331
7540
|
});
|
|
7332
7541
|
if (!result.success) {
|
|
7333
|
-
|
|
7542
|
+
logger38.error("AI rule distillation failed", { output: result.output.slice(0, 500) });
|
|
7334
7543
|
throw new Error(`Rule distillation AI call failed: ${result.output.slice(0, 200)}`);
|
|
7335
7544
|
}
|
|
7336
7545
|
const actions = this.parseActions(result.output);
|
|
7337
7546
|
if (actions.length === 0) {
|
|
7338
|
-
|
|
7547
|
+
logger38.info("No rule distillation actions returned by AI");
|
|
7339
7548
|
return { processedMemories: matureMemories.length, actions: 0 };
|
|
7340
7549
|
}
|
|
7341
7550
|
let actionCount = 0;
|
|
@@ -7344,13 +7553,13 @@ var AgentRuleDistiller = class {
|
|
|
7344
7553
|
this.executeAction(action, existingRules);
|
|
7345
7554
|
actionCount++;
|
|
7346
7555
|
} catch (err) {
|
|
7347
|
-
|
|
7556
|
+
logger38.warn("Failed to execute rule distill action", {
|
|
7348
7557
|
action: action.type,
|
|
7349
7558
|
error: err.message
|
|
7350
7559
|
});
|
|
7351
7560
|
}
|
|
7352
7561
|
}
|
|
7353
|
-
|
|
7562
|
+
logger38.info("Rule distillation complete", {
|
|
7354
7563
|
processedMemories: matureMemories.length,
|
|
7355
7564
|
actions: actionCount
|
|
7356
7565
|
});
|
|
@@ -7404,7 +7613,7 @@ var AgentRuleDistiller = class {
|
|
|
7404
7613
|
try {
|
|
7405
7614
|
const jsonMatch = output.match(/```json\s*([\s\S]*?)```/) ?? output.match(/\{[\s\S]*"actions"[\s\S]*\}/);
|
|
7406
7615
|
if (!jsonMatch) {
|
|
7407
|
-
|
|
7616
|
+
logger38.warn("No JSON found in AI rule distill output");
|
|
7408
7617
|
return [];
|
|
7409
7618
|
}
|
|
7410
7619
|
const jsonStr = jsonMatch[1] ?? jsonMatch[0];
|
|
@@ -7412,7 +7621,7 @@ var AgentRuleDistiller = class {
|
|
|
7412
7621
|
if (!Array.isArray(parsed.actions)) return [];
|
|
7413
7622
|
return parsed.actions;
|
|
7414
7623
|
} catch (err) {
|
|
7415
|
-
|
|
7624
|
+
logger38.warn("Failed to parse AI rule distill output", {
|
|
7416
7625
|
error: err.message
|
|
7417
7626
|
});
|
|
7418
7627
|
return [];
|
|
@@ -7431,7 +7640,7 @@ var AgentRuleDistiller = class {
|
|
|
7431
7640
|
this.deprecateRule(action, existingRules);
|
|
7432
7641
|
break;
|
|
7433
7642
|
default:
|
|
7434
|
-
|
|
7643
|
+
logger38.warn("Unknown rule distill action type", { type: action.type });
|
|
7435
7644
|
}
|
|
7436
7645
|
}
|
|
7437
7646
|
/** 创建新规则 */
|
|
@@ -7464,13 +7673,13 @@ var AgentRuleDistiller = class {
|
|
|
7464
7673
|
action: "created",
|
|
7465
7674
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7466
7675
|
});
|
|
7467
|
-
|
|
7676
|
+
logger38.info("Created agent rule", { id: ruleEntry.id, ruleName: action.ruleName });
|
|
7468
7677
|
}
|
|
7469
7678
|
/** 更新已有规则 */
|
|
7470
7679
|
updateRule(action, existingRules) {
|
|
7471
7680
|
const existing = existingRules.find((r) => r.id === action.ruleId);
|
|
7472
7681
|
if (!existing) {
|
|
7473
|
-
|
|
7682
|
+
logger38.warn("Cannot update: rule not found", { ruleId: action.ruleId });
|
|
7474
7683
|
return;
|
|
7475
7684
|
}
|
|
7476
7685
|
existing.content = action.content;
|
|
@@ -7500,13 +7709,13 @@ var AgentRuleDistiller = class {
|
|
|
7500
7709
|
action: "merged",
|
|
7501
7710
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7502
7711
|
});
|
|
7503
|
-
|
|
7712
|
+
logger38.info("Updated agent rule", { id: existing.id, ruleName: existing.ruleName });
|
|
7504
7713
|
}
|
|
7505
7714
|
/** 废弃规则 */
|
|
7506
7715
|
deprecateRule(action, existingRules) {
|
|
7507
7716
|
const existing = existingRules.find((r) => r.id === action.ruleId);
|
|
7508
7717
|
if (!existing) {
|
|
7509
|
-
|
|
7718
|
+
logger38.warn("Cannot deprecate: rule not found", { ruleId: action.ruleId });
|
|
7510
7719
|
return;
|
|
7511
7720
|
}
|
|
7512
7721
|
existing.deprecated = true;
|
|
@@ -7535,7 +7744,7 @@ var AgentRuleDistiller = class {
|
|
|
7535
7744
|
reason: action.reason,
|
|
7536
7745
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7537
7746
|
});
|
|
7538
|
-
|
|
7747
|
+
logger38.info("Deprecated agent rule", { id: existing.id, reason: action.reason });
|
|
7539
7748
|
}
|
|
7540
7749
|
/** 同步规则到 DATA_DIR/rules/ MDC 文件,可选同步到项目 */
|
|
7541
7750
|
syncMdcFile(rule) {
|
|
@@ -7551,9 +7760,9 @@ var AgentRuleDistiller = class {
|
|
|
7551
7760
|
ensureDir(this.rulesDir);
|
|
7552
7761
|
const filePath = path13.join(this.rulesDir, `distilled-${rule.ruleName}.mdc`);
|
|
7553
7762
|
fs9.writeFileSync(filePath, mdcContent, "utf-8");
|
|
7554
|
-
|
|
7763
|
+
logger38.debug("Synced MDC file", { path: filePath });
|
|
7555
7764
|
} catch (err) {
|
|
7556
|
-
|
|
7765
|
+
logger38.warn("Failed to sync MDC file", {
|
|
7557
7766
|
ruleName: rule.ruleName,
|
|
7558
7767
|
error: err.message
|
|
7559
7768
|
});
|
|
@@ -7563,9 +7772,9 @@ var AgentRuleDistiller = class {
|
|
|
7563
7772
|
ensureDir(this.projectRulesDir);
|
|
7564
7773
|
const projectFilePath = path13.join(this.projectRulesDir, `distilled-${rule.ruleName}.mdc`);
|
|
7565
7774
|
fs9.writeFileSync(projectFilePath, mdcContent, "utf-8");
|
|
7566
|
-
|
|
7775
|
+
logger38.debug("Synced MDC file to project", { path: projectFilePath });
|
|
7567
7776
|
} catch (err) {
|
|
7568
|
-
|
|
7777
|
+
logger38.warn("Failed to sync MDC file to project", {
|
|
7569
7778
|
ruleName: rule.ruleName,
|
|
7570
7779
|
error: err.message
|
|
7571
7780
|
});
|
|
@@ -7579,10 +7788,10 @@ var AgentRuleDistiller = class {
|
|
|
7579
7788
|
const filePath = path13.join(this.rulesDir, fileName);
|
|
7580
7789
|
if (fs9.existsSync(filePath)) {
|
|
7581
7790
|
fs9.unlinkSync(filePath);
|
|
7582
|
-
|
|
7791
|
+
logger38.debug("Removed MDC file", { path: filePath });
|
|
7583
7792
|
}
|
|
7584
7793
|
} catch (err) {
|
|
7585
|
-
|
|
7794
|
+
logger38.warn("Failed to remove MDC file", {
|
|
7586
7795
|
ruleName,
|
|
7587
7796
|
error: err.message
|
|
7588
7797
|
});
|
|
@@ -7592,10 +7801,10 @@ var AgentRuleDistiller = class {
|
|
|
7592
7801
|
const projectFilePath = path13.join(this.projectRulesDir, fileName);
|
|
7593
7802
|
if (fs9.existsSync(projectFilePath)) {
|
|
7594
7803
|
fs9.unlinkSync(projectFilePath);
|
|
7595
|
-
|
|
7804
|
+
logger38.debug("Removed MDC file from project", { path: projectFilePath });
|
|
7596
7805
|
}
|
|
7597
7806
|
} catch (err) {
|
|
7598
|
-
|
|
7807
|
+
logger38.warn("Failed to remove MDC file from project", {
|
|
7599
7808
|
ruleName,
|
|
7600
7809
|
error: err.message
|
|
7601
7810
|
});
|
|
@@ -7626,7 +7835,7 @@ var AgentRuleDistiller = class {
|
|
|
7626
7835
|
// src/distill/VectorStore.ts
|
|
7627
7836
|
import fs10 from "fs";
|
|
7628
7837
|
import path14 from "path";
|
|
7629
|
-
var
|
|
7838
|
+
var logger39 = logger.child("VectorStore");
|
|
7630
7839
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
7631
7840
|
// 英文
|
|
7632
7841
|
"the",
|
|
@@ -7813,7 +8022,7 @@ var VectorStore = class {
|
|
|
7813
8022
|
async reindex() {
|
|
7814
8023
|
this.rebuildDocumentFreqs();
|
|
7815
8024
|
this.save();
|
|
7816
|
-
|
|
8025
|
+
logger39.info("Vector index rebuilt", { count: this.data.vectors.length });
|
|
7817
8026
|
}
|
|
7818
8027
|
/** 获取索引条目数 */
|
|
7819
8028
|
count() {
|
|
@@ -7890,7 +8099,7 @@ var VectorStore = class {
|
|
|
7890
8099
|
return JSON.parse(raw);
|
|
7891
8100
|
}
|
|
7892
8101
|
} catch (err) {
|
|
7893
|
-
|
|
8102
|
+
logger39.warn("Failed to load vector store", { error: err.message });
|
|
7894
8103
|
}
|
|
7895
8104
|
return { documentFreqs: {}, vectors: [], totalDocs: 0 };
|
|
7896
8105
|
}
|
|
@@ -7908,7 +8117,7 @@ var VectorStore = class {
|
|
|
7908
8117
|
// src/distill/VersionStore.ts
|
|
7909
8118
|
import fs11 from "fs";
|
|
7910
8119
|
import path15 from "path";
|
|
7911
|
-
var
|
|
8120
|
+
var logger40 = logger.child("VersionStore");
|
|
7912
8121
|
var VersionStore = class {
|
|
7913
8122
|
filePath;
|
|
7914
8123
|
data;
|
|
@@ -7940,7 +8149,7 @@ var VersionStore = class {
|
|
|
7940
8149
|
return JSON.parse(raw);
|
|
7941
8150
|
}
|
|
7942
8151
|
} catch (err) {
|
|
7943
|
-
|
|
8152
|
+
logger40.warn("Failed to load version store", { error: err.message });
|
|
7944
8153
|
}
|
|
7945
8154
|
return { records: [] };
|
|
7946
8155
|
}
|
|
@@ -7956,7 +8165,7 @@ var VersionStore = class {
|
|
|
7956
8165
|
};
|
|
7957
8166
|
|
|
7958
8167
|
// src/distill/DistillScheduler.ts
|
|
7959
|
-
var
|
|
8168
|
+
var logger41 = logger.child("DistillScheduler");
|
|
7960
8169
|
var DistillScheduler = class {
|
|
7961
8170
|
diaryStore;
|
|
7962
8171
|
memoryDistiller;
|
|
@@ -7982,29 +8191,29 @@ var DistillScheduler = class {
|
|
|
7982
8191
|
if (this.timer) return;
|
|
7983
8192
|
this.timer = setInterval(() => {
|
|
7984
8193
|
this.runDistill().catch((err) => {
|
|
7985
|
-
|
|
8194
|
+
logger41.error("Scheduled distillation failed", { error: err.message });
|
|
7986
8195
|
});
|
|
7987
8196
|
}, this.intervalMs);
|
|
7988
|
-
|
|
8197
|
+
logger41.info("DistillScheduler started", { intervalMs: this.intervalMs });
|
|
7989
8198
|
}
|
|
7990
8199
|
/** 停止定时调度 */
|
|
7991
8200
|
stop() {
|
|
7992
8201
|
if (this.timer) {
|
|
7993
8202
|
clearInterval(this.timer);
|
|
7994
8203
|
this.timer = null;
|
|
7995
|
-
|
|
8204
|
+
logger41.info("DistillScheduler stopped");
|
|
7996
8205
|
}
|
|
7997
8206
|
}
|
|
7998
8207
|
/** 手动触发蒸馏 */
|
|
7999
8208
|
async runDistill(options) {
|
|
8000
8209
|
if (this.running) {
|
|
8001
|
-
|
|
8210
|
+
logger41.info("Distillation already in progress, skipping");
|
|
8002
8211
|
return { memory: { processedDiaries: 0, actions: 0 }, rule: { processedMemories: 0, actions: 0 }, vectorIndexed: 0 };
|
|
8003
8212
|
}
|
|
8004
8213
|
this.running = true;
|
|
8005
8214
|
eventBus.emitTyped("distill:started", {});
|
|
8006
8215
|
try {
|
|
8007
|
-
|
|
8216
|
+
logger41.info("Starting distillation pipeline");
|
|
8008
8217
|
const memoryResult = await this.memoryDistiller.distill({ force: options?.force });
|
|
8009
8218
|
if (memoryResult.actions > 0) {
|
|
8010
8219
|
eventBus.emitTyped("distill:memory:updated", {
|
|
@@ -8029,7 +8238,7 @@ var DistillScheduler = class {
|
|
|
8029
8238
|
rule: ruleResult,
|
|
8030
8239
|
vectorIndexed
|
|
8031
8240
|
});
|
|
8032
|
-
|
|
8241
|
+
logger41.info("Distillation pipeline complete", {
|
|
8033
8242
|
memoryActions: memoryResult.actions,
|
|
8034
8243
|
ruleActions: ruleResult.actions,
|
|
8035
8244
|
vectorIndexed
|
|
@@ -8097,13 +8306,13 @@ ${content}`
|
|
|
8097
8306
|
});
|
|
8098
8307
|
indexed++;
|
|
8099
8308
|
}
|
|
8100
|
-
|
|
8309
|
+
logger41.info("Vector indexing complete", { indexed });
|
|
8101
8310
|
return indexed;
|
|
8102
8311
|
}
|
|
8103
8312
|
};
|
|
8104
8313
|
|
|
8105
8314
|
// src/deploy/PreviewReaper.ts
|
|
8106
|
-
var
|
|
8315
|
+
var logger42 = logger.child("PreviewReaper");
|
|
8107
8316
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed" /* Completed */, "failed" /* Failed */, "deployed" /* Deployed */]);
|
|
8108
8317
|
var PreviewReaper = class {
|
|
8109
8318
|
tracker;
|
|
@@ -8126,16 +8335,16 @@ var PreviewReaper = class {
|
|
|
8126
8335
|
if (this.timer) return;
|
|
8127
8336
|
this.timer = setInterval(() => {
|
|
8128
8337
|
this.reap().catch((err) => {
|
|
8129
|
-
|
|
8338
|
+
logger42.error("Scheduled preview reap failed", { error: err.message });
|
|
8130
8339
|
});
|
|
8131
8340
|
}, this.intervalMs);
|
|
8132
|
-
|
|
8341
|
+
logger42.info("PreviewReaper started", { intervalMs: this.intervalMs, ttlMs: this.ttlMs });
|
|
8133
8342
|
}
|
|
8134
8343
|
stop() {
|
|
8135
8344
|
if (this.timer) {
|
|
8136
8345
|
clearInterval(this.timer);
|
|
8137
8346
|
this.timer = null;
|
|
8138
|
-
|
|
8347
|
+
logger42.info("PreviewReaper stopped");
|
|
8139
8348
|
}
|
|
8140
8349
|
}
|
|
8141
8350
|
async reap() {
|
|
@@ -8157,13 +8366,13 @@ var PreviewReaper = class {
|
|
|
8157
8366
|
try {
|
|
8158
8367
|
this.orchestrator.stopPreviewServers(iid);
|
|
8159
8368
|
reaped.push(iid);
|
|
8160
|
-
|
|
8369
|
+
logger42.info(t("reaper.reaped", { iid, hours }));
|
|
8161
8370
|
} catch (err) {
|
|
8162
|
-
|
|
8371
|
+
logger42.warn("Failed to reap preview", { iid, error: err.message });
|
|
8163
8372
|
}
|
|
8164
8373
|
}
|
|
8165
8374
|
if (reaped.length > 0) {
|
|
8166
|
-
|
|
8375
|
+
logger42.info(t("reaper.summary", { count: reaped.length }));
|
|
8167
8376
|
this.bus.emitTyped("preview:reaped", { iids: reaped, count: reaped.length });
|
|
8168
8377
|
}
|
|
8169
8378
|
this.lastScanAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -8192,7 +8401,7 @@ import { randomUUID as randomUUID5 } from "crypto";
|
|
|
8192
8401
|
import { execFile as execFile2 } from "child_process";
|
|
8193
8402
|
import { promisify } from "util";
|
|
8194
8403
|
var execFileAsync = promisify(execFile2);
|
|
8195
|
-
var
|
|
8404
|
+
var logger43 = logger.child("OrphanBranchManager");
|
|
8196
8405
|
var SYNC_MANIFEST = [
|
|
8197
8406
|
"knowledge/index.json",
|
|
8198
8407
|
"knowledge/knowledge.json",
|
|
@@ -8263,7 +8472,7 @@ var OrphanBranchManager = class {
|
|
|
8263
8472
|
await this.gitIn(tmpDir, ["add", "-A"]);
|
|
8264
8473
|
const status = await this.gitIn(tmpDir, ["status", "--porcelain"]);
|
|
8265
8474
|
if (!status.trim()) {
|
|
8266
|
-
|
|
8475
|
+
logger43.info("No changes to publish");
|
|
8267
8476
|
return { commitSha: await this.gitIn(tmpDir, ["rev-parse", "HEAD"]) };
|
|
8268
8477
|
}
|
|
8269
8478
|
await this.gitIn(tmpDir, [
|
|
@@ -8274,7 +8483,7 @@ var OrphanBranchManager = class {
|
|
|
8274
8483
|
]);
|
|
8275
8484
|
const commitSha = await this.gitIn(tmpDir, ["rev-parse", "HEAD"]);
|
|
8276
8485
|
await this.pushWithRetry(tmpDir);
|
|
8277
|
-
|
|
8486
|
+
logger43.info("Knowledge published to orphan branch", {
|
|
8278
8487
|
branch: this.branch,
|
|
8279
8488
|
commitSha: commitSha.slice(0, 8),
|
|
8280
8489
|
entriesCount: copied
|
|
@@ -8341,7 +8550,7 @@ var OrphanBranchManager = class {
|
|
|
8341
8550
|
} catch {
|
|
8342
8551
|
}
|
|
8343
8552
|
}
|
|
8344
|
-
|
|
8553
|
+
logger43.info("Knowledge restored from orphan branch", {
|
|
8345
8554
|
branch: this.branch,
|
|
8346
8555
|
entriesCount
|
|
8347
8556
|
});
|
|
@@ -8378,7 +8587,7 @@ var OrphanBranchManager = class {
|
|
|
8378
8587
|
// Private helpers
|
|
8379
8588
|
// -----------------------------------------------------------------------
|
|
8380
8589
|
async git(args) {
|
|
8381
|
-
|
|
8590
|
+
logger43.debug("git exec (main)", { args });
|
|
8382
8591
|
const { stdout } = await execFileAsync("git", args, {
|
|
8383
8592
|
cwd: this.gitRootDir,
|
|
8384
8593
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -8387,7 +8596,7 @@ var OrphanBranchManager = class {
|
|
|
8387
8596
|
return stdout.trim();
|
|
8388
8597
|
}
|
|
8389
8598
|
async gitIn(cwd, args) {
|
|
8390
|
-
|
|
8599
|
+
logger43.debug("git exec (worktree)", { cwd: path16.basename(cwd), args });
|
|
8391
8600
|
const { stdout } = await execFileAsync("git", args, {
|
|
8392
8601
|
cwd,
|
|
8393
8602
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -8487,7 +8696,7 @@ var OrphanBranchManager = class {
|
|
|
8487
8696
|
`Failed to push to orphan branch after ${MAX_PUSH_RETRIES} attempts: ${err.message}`
|
|
8488
8697
|
);
|
|
8489
8698
|
}
|
|
8490
|
-
|
|
8699
|
+
logger43.warn("Push failed, pulling and retrying", {
|
|
8491
8700
|
attempt,
|
|
8492
8701
|
error: err.message
|
|
8493
8702
|
});
|
|
@@ -8500,7 +8709,7 @@ var OrphanBranchManager = class {
|
|
|
8500
8709
|
try {
|
|
8501
8710
|
await this.git(["worktree", "remove", wtDir, "--force"]);
|
|
8502
8711
|
} catch {
|
|
8503
|
-
|
|
8712
|
+
logger43.debug("Worktree cleanup skipped (may not exist)", { dir: wtDir });
|
|
8504
8713
|
}
|
|
8505
8714
|
try {
|
|
8506
8715
|
await this.git(["worktree", "prune"]);
|
|
@@ -8517,7 +8726,7 @@ import fs14 from "fs";
|
|
|
8517
8726
|
import { z } from "zod";
|
|
8518
8727
|
import fs13 from "fs";
|
|
8519
8728
|
import path17 from "path";
|
|
8520
|
-
var
|
|
8729
|
+
var logger44 = logger.child("TenantConfig");
|
|
8521
8730
|
var tenantGongfengSchema = z.object({
|
|
8522
8731
|
apiUrl: z.string().url("apiUrl must be a valid URL"),
|
|
8523
8732
|
privateToken: z.string().min(1, "privateToken is required"),
|
|
@@ -8544,7 +8753,7 @@ var DEFAULT_TENANT_ID = "default";
|
|
|
8544
8753
|
function loadTenantsConfig(configPath) {
|
|
8545
8754
|
if (!configPath) return null;
|
|
8546
8755
|
if (!fs13.existsSync(configPath)) {
|
|
8547
|
-
|
|
8756
|
+
logger44.warn("Tenants config file not found, falling back to single-tenant mode", {
|
|
8548
8757
|
path: configPath
|
|
8549
8758
|
});
|
|
8550
8759
|
return null;
|
|
@@ -8555,14 +8764,14 @@ function loadTenantsConfig(configPath) {
|
|
|
8555
8764
|
const result = tenantsConfigSchema.safeParse(json);
|
|
8556
8765
|
if (!result.success) {
|
|
8557
8766
|
const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
8558
|
-
|
|
8767
|
+
logger44.error(`Tenants config validation failed:
|
|
8559
8768
|
${issues}`);
|
|
8560
8769
|
return null;
|
|
8561
8770
|
}
|
|
8562
8771
|
const ids = result.data.tenants.map((t2) => t2.id);
|
|
8563
8772
|
const dupes = ids.filter((id, i) => ids.indexOf(id) !== i);
|
|
8564
8773
|
if (dupes.length > 0) {
|
|
8565
|
-
|
|
8774
|
+
logger44.error("Duplicate tenant IDs found", { duplicates: [...new Set(dupes)] });
|
|
8566
8775
|
return null;
|
|
8567
8776
|
}
|
|
8568
8777
|
const baseDir = path17.dirname(configPath);
|
|
@@ -8571,12 +8780,12 @@ ${issues}`);
|
|
|
8571
8780
|
tenant.workspace = path17.resolve(baseDir, tenant.workspace);
|
|
8572
8781
|
}
|
|
8573
8782
|
}
|
|
8574
|
-
|
|
8783
|
+
logger44.info("Tenants config loaded", {
|
|
8575
8784
|
tenants: result.data.tenants.map((t2) => t2.id)
|
|
8576
8785
|
});
|
|
8577
8786
|
return result.data;
|
|
8578
8787
|
} catch (err) {
|
|
8579
|
-
|
|
8788
|
+
logger44.error("Failed to parse tenants config", {
|
|
8580
8789
|
path: configPath,
|
|
8581
8790
|
error: err.message
|
|
8582
8791
|
});
|
|
@@ -8639,6 +8848,49 @@ async function main() {
|
|
|
8639
8848
|
const allAiPhaseNames = [...new Set(
|
|
8640
8849
|
[...lifecycleManagers.values()].flatMap((lm) => lm.getExecutablePhaseNames())
|
|
8641
8850
|
)];
|
|
8851
|
+
let sharedTerminalManager;
|
|
8852
|
+
if (config.ai.mode === "pty") {
|
|
8853
|
+
const { TerminalManager: TerminalManager2 } = await import("./TerminalManager-RT2N7N5R.js");
|
|
8854
|
+
const { PtyRunner } = await import("./PtyRunner-6UGI5STW.js");
|
|
8855
|
+
const { registerAIRunner: regRunner, getPtyProfile } = await import("./AIRunnerRegistry-II3WWSFN.js");
|
|
8856
|
+
const allPtyAgents = /* @__PURE__ */ new Set([
|
|
8857
|
+
config.pty.defaultAgent,
|
|
8858
|
+
...Object.values(config.pty.phaseAgents)
|
|
8859
|
+
]);
|
|
8860
|
+
for (const agent of allPtyAgents) {
|
|
8861
|
+
if (!getPtyProfile(agent)) {
|
|
8862
|
+
throw new Error(
|
|
8863
|
+
`PTY agent "${agent}" has no PtyProfile. Supported agents: claude-internal, codebuddy, cursor-agent`
|
|
8864
|
+
);
|
|
8865
|
+
}
|
|
8866
|
+
}
|
|
8867
|
+
sharedTerminalManager = new TerminalManager2({
|
|
8868
|
+
idleTimeoutMs: config.terminal.idleTimeoutMs,
|
|
8869
|
+
maxSessions: config.terminal.maxSessions,
|
|
8870
|
+
allowedBaseDirs: [
|
|
8871
|
+
config.project.worktreeBaseDir ?? config.project.workDir,
|
|
8872
|
+
config.project.workDir
|
|
8873
|
+
].filter(Boolean)
|
|
8874
|
+
});
|
|
8875
|
+
const tm = sharedTerminalManager;
|
|
8876
|
+
regRunner("pty", {
|
|
8877
|
+
factoryFn: (_binary, nvmNodeVersion) => new PtyRunner(
|
|
8878
|
+
nvmNodeVersion,
|
|
8879
|
+
tm,
|
|
8880
|
+
config.pty.defaultAgent,
|
|
8881
|
+
config.pty.phaseAgents,
|
|
8882
|
+
config.ai.model,
|
|
8883
|
+
config.pty.idleDetectMs
|
|
8884
|
+
),
|
|
8885
|
+
defaultBinary: "claude",
|
|
8886
|
+
binaryEnvKey: "CLAUDE_BINARY",
|
|
8887
|
+
capabilities: { nativePlanMode: false }
|
|
8888
|
+
});
|
|
8889
|
+
logger.info("PTY runner registered (multi-agent)", {
|
|
8890
|
+
defaultAgent: config.pty.defaultAgent,
|
|
8891
|
+
phaseAgents: config.pty.phaseAgents
|
|
8892
|
+
});
|
|
8893
|
+
}
|
|
8642
8894
|
validatePhaseRegistry(allAiPhaseNames);
|
|
8643
8895
|
validateRunnerRegistry([config.ai.mode]);
|
|
8644
8896
|
const aiRunner = createAIRunner(config.ai);
|
|
@@ -8896,7 +9148,8 @@ async function main() {
|
|
|
8896
9148
|
configReloader,
|
|
8897
9149
|
orphanManager,
|
|
8898
9150
|
wsConfig: primaryTenant.wsConfig,
|
|
8899
|
-
previewReaper
|
|
9151
|
+
previewReaper,
|
|
9152
|
+
terminalManager: sharedTerminalManager
|
|
8900
9153
|
});
|
|
8901
9154
|
await webServer.start();
|
|
8902
9155
|
}
|
|
@@ -9007,4 +9260,4 @@ function migrateKnowledgeDir(srcDir, destDir) {
|
|
|
9007
9260
|
export {
|
|
9008
9261
|
main
|
|
9009
9262
|
};
|
|
9010
|
-
//# sourceMappingURL=chunk-
|
|
9263
|
+
//# sourceMappingURL=chunk-QZZGIZWC.js.map
|