@xdevops/issue-auto-finish 1.0.85 → 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-JINMYD56.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-5UPYA6KH.js → chunk-IP3QTP5A.js} +1028 -763
- 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-K2OTLYJI.js → chunk-QZZGIZWC.js} +457 -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-RNXGTDWZ.js → restart-BMILTP5X.js} +6 -5
- package/dist/{restart-RNXGTDWZ.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-5UPYA6KH.js.map +0 -1
- package/dist/chunk-DADQSKPL.js +0 -1
- package/dist/chunk-FWEW5E3B.js.map +0 -1
- package/dist/chunk-K2OTLYJI.js.map +0 -1
- package/dist/chunk-KTYPZTF4.js.map +0 -1
- package/dist/chunk-P4O4ZXEC.js.map +0 -1
- package/dist/start-27GRO4DP.js +0 -14
- package/src/web/frontend/dist/assets/index-C4NXoH9S.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-27GRO4DP.js.map → ai-runner-HLA44WI6.js.map} +0 -0
- /package/dist/{chunk-JINMYD56.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;
|
|
@@ -977,6 +996,7 @@ function createApiRouter(trackerOrDeps, config, agentLogStore, orchestrator, mai
|
|
|
977
996
|
return;
|
|
978
997
|
}
|
|
979
998
|
try {
|
|
999
|
+
poller?.forceReleaseIssue(iid);
|
|
980
1000
|
orch.retryFromPhase(iid, phase);
|
|
981
1001
|
res.json({ success: true, message: `Issue #${iid} reset to phase: ${phase}` });
|
|
982
1002
|
} catch (err) {
|
|
@@ -1390,7 +1410,7 @@ function createApiRouter(trackerOrDeps, config, agentLogStore, orchestrator, mai
|
|
|
1390
1410
|
});
|
|
1391
1411
|
router.put("/api/system/feature-toggle", (req, res) => {
|
|
1392
1412
|
const { feature, enabled } = req.body;
|
|
1393
|
-
const validFeatures = ["brainstorm", "chat", "braindump", "knowledge", "distill"];
|
|
1413
|
+
const validFeatures = ["brainstorm", "chat", "braindump", "knowledge", "distill", "terminal"];
|
|
1394
1414
|
if (!feature || !validFeatures.includes(feature) || typeof enabled !== "boolean") {
|
|
1395
1415
|
res.status(400).json({ error: "Invalid feature or enabled value" });
|
|
1396
1416
|
return;
|
|
@@ -1440,6 +1460,8 @@ function createApiRouter(trackerOrDeps, config, agentLogStore, orchestrator, mai
|
|
|
1440
1460
|
braindumpEnabled: getFeatureEnabled("braindump", cfg),
|
|
1441
1461
|
knowledgeEnabled: getFeatureEnabled("knowledge", cfg),
|
|
1442
1462
|
distillEnabled: getFeatureEnabled("distill", cfg),
|
|
1463
|
+
terminalEnabled: getFeatureEnabled("terminal", cfg),
|
|
1464
|
+
terminalWorkDir: cfg.project.workDir,
|
|
1443
1465
|
knowledgeSyncEnabled: true,
|
|
1444
1466
|
multiRepoEnabled: wsConfig ? isMultiRepo(wsConfig) : false,
|
|
1445
1467
|
linkedRepos: wsConfig?.associates.map((a) => ({
|
|
@@ -1706,7 +1728,7 @@ data: ${JSON.stringify({ time: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
|
1706
1728
|
const scenariosDir = path3.join(outputsDir, "scenarios");
|
|
1707
1729
|
let scenarios = [];
|
|
1708
1730
|
if (fs3.existsSync(scenariosDir) && fs3.statSync(scenariosDir).isDirectory()) {
|
|
1709
|
-
scenarios = collectArtifactFiles(scenariosDir, scenariosDir);
|
|
1731
|
+
scenarios = collectArtifactFiles(scenariosDir, scenariosDir).filter((f) => !f.name.startsWith("config.") && !f.name.startsWith("config-"));
|
|
1710
1732
|
}
|
|
1711
1733
|
res.json({ configured: true, runs, scenarios });
|
|
1712
1734
|
} catch (err) {
|
|
@@ -2404,7 +2426,7 @@ function createKnowledgeRouter(deps) {
|
|
|
2404
2426
|
heartbeat = setInterval(() => {
|
|
2405
2427
|
sendProgress({ step: "analyzing", current: 2, total, message: "AI \u5206\u6790\u4E2D...", elapsed: Date.now() - aiStart });
|
|
2406
2428
|
}, 3e3);
|
|
2407
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
2429
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
2408
2430
|
const runner = createAIRunner2(config.ai);
|
|
2409
2431
|
const { analyze } = await import("./KnowledgeAnalyzer-MTTTSSHX.js");
|
|
2410
2432
|
const knowledge = await analyze({ workDir, aiRunner: runner, syncToProject: config.sync.knowledgeToProject });
|
|
@@ -2449,7 +2471,7 @@ function createKnowledgeRouter(deps) {
|
|
|
2449
2471
|
(async () => {
|
|
2450
2472
|
try {
|
|
2451
2473
|
sendProgress({ step: "collecting", current: 1, total, message: "\u68C0\u6D4B\u53D8\u66F4\u5E76\u6536\u96C6\u4FE1\u606F..." });
|
|
2452
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
2474
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
2453
2475
|
const { analyzeIncremental } = await import("./KnowledgeAnalyzer-MTTTSSHX.js");
|
|
2454
2476
|
const runner = createAIRunner2(config.ai);
|
|
2455
2477
|
sendProgress({ step: "analyzing", current: 2, total, message: "\u589E\u91CF\u5206\u6790\u4E2D..." });
|
|
@@ -2733,7 +2755,7 @@ function createDomainModelRouter(deps) {
|
|
|
2733
2755
|
}
|
|
2734
2756
|
(async () => {
|
|
2735
2757
|
try {
|
|
2736
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
2758
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
2737
2759
|
const runner = createAIRunner2(config.ai);
|
|
2738
2760
|
const model = await analyzer.analyze({
|
|
2739
2761
|
workDir: config.project.workDir,
|
|
@@ -2947,7 +2969,7 @@ function createSystemUseCaseRouter(deps) {
|
|
|
2947
2969
|
}
|
|
2948
2970
|
(async () => {
|
|
2949
2971
|
try {
|
|
2950
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
2972
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
2951
2973
|
const runner = createAIRunner2(config.ai);
|
|
2952
2974
|
const model = await analyzer.analyze({
|
|
2953
2975
|
workDir: config.project.workDir,
|
|
@@ -3137,7 +3159,7 @@ function createSystemUseCaseRouter(deps) {
|
|
|
3137
3159
|
res.status(404).json({ error: "No domain model loaded \u2014 run domain analysis first" });
|
|
3138
3160
|
return;
|
|
3139
3161
|
}
|
|
3140
|
-
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-
|
|
3162
|
+
const { createAIRunner: createAIRunner2 } = await import("./ai-runner-HLA44WI6.js");
|
|
3141
3163
|
const runner = createAIRunner2(config.ai);
|
|
3142
3164
|
const associations = await analyzer.suggestDomainAssociations(
|
|
3143
3165
|
useCaseModel,
|
|
@@ -3968,6 +3990,79 @@ function createAnalyticsRouter(deps) {
|
|
|
3968
3990
|
return router;
|
|
3969
3991
|
}
|
|
3970
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
|
+
|
|
3971
4066
|
// src/knowledge/DomainModelAnalyzer.ts
|
|
3972
4067
|
import fs5 from "fs";
|
|
3973
4068
|
import path6 from "path";
|
|
@@ -4097,7 +4192,7 @@ ${feedback}
|
|
|
4097
4192
|
}
|
|
4098
4193
|
|
|
4099
4194
|
// src/knowledge/DomainModelAnalyzer.ts
|
|
4100
|
-
var
|
|
4195
|
+
var logger22 = logger.child("DomainModelAnalyzer");
|
|
4101
4196
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
4102
4197
|
"node_modules",
|
|
4103
4198
|
"dist",
|
|
@@ -4314,7 +4409,7 @@ var DomainModelAnalyzer = class {
|
|
|
4314
4409
|
}
|
|
4315
4410
|
this.currentModel = model;
|
|
4316
4411
|
emit({ type: "complete", data: model });
|
|
4317
|
-
|
|
4412
|
+
logger22.info("Domain model analysis complete", {
|
|
4318
4413
|
contexts: model.boundedContexts.length,
|
|
4319
4414
|
elements: model.elements.length,
|
|
4320
4415
|
relationships: model.relationships.length
|
|
@@ -4340,7 +4435,7 @@ var DomainModelAnalyzer = class {
|
|
|
4340
4435
|
tags: ["domain-model", "auto-generated"]
|
|
4341
4436
|
});
|
|
4342
4437
|
}
|
|
4343
|
-
|
|
4438
|
+
logger22.info("Domain model confirmed to knowledge store");
|
|
4344
4439
|
}
|
|
4345
4440
|
/**
|
|
4346
4441
|
* Load a previously stored domain model from KnowledgeStore.
|
|
@@ -4355,7 +4450,7 @@ var DomainModelAnalyzer = class {
|
|
|
4355
4450
|
this.currentModel = model;
|
|
4356
4451
|
return model;
|
|
4357
4452
|
} catch {
|
|
4358
|
-
|
|
4453
|
+
logger22.warn("Failed to parse stored domain model");
|
|
4359
4454
|
return null;
|
|
4360
4455
|
}
|
|
4361
4456
|
}
|
|
@@ -4579,7 +4674,7 @@ ${domainModelJson}
|
|
|
4579
4674
|
}
|
|
4580
4675
|
|
|
4581
4676
|
// src/knowledge/SystemUseCaseAnalyzer.ts
|
|
4582
|
-
var
|
|
4677
|
+
var logger23 = logger.child("SystemUseCaseAnalyzer");
|
|
4583
4678
|
function parseJsonFromOutput2(output) {
|
|
4584
4679
|
const jsonBlockMatch = output.match(/```json\s*\n([\s\S]*?)```/);
|
|
4585
4680
|
if (jsonBlockMatch) {
|
|
@@ -4704,7 +4799,7 @@ var SystemUseCaseAnalyzer = class {
|
|
|
4704
4799
|
}
|
|
4705
4800
|
this.currentModel = model;
|
|
4706
4801
|
emit({ type: "complete", data: model });
|
|
4707
|
-
|
|
4802
|
+
logger23.info("Use case analysis complete", {
|
|
4708
4803
|
useCases: model.useCases.length,
|
|
4709
4804
|
relationships: model.relationships.length
|
|
4710
4805
|
});
|
|
@@ -4725,12 +4820,12 @@ var SystemUseCaseAnalyzer = class {
|
|
|
4725
4820
|
});
|
|
4726
4821
|
const associations = /* @__PURE__ */ new Map();
|
|
4727
4822
|
if (!result.success) {
|
|
4728
|
-
|
|
4823
|
+
logger23.warn("AI domain association failed", { output: result.output.slice(0, 200) });
|
|
4729
4824
|
return associations;
|
|
4730
4825
|
}
|
|
4731
4826
|
const parsed = parseJsonFromOutput2(result.output);
|
|
4732
4827
|
if (!parsed || !parsed.associations) {
|
|
4733
|
-
|
|
4828
|
+
logger23.warn("Failed to parse domain association output");
|
|
4734
4829
|
return associations;
|
|
4735
4830
|
}
|
|
4736
4831
|
const rawAssociations = parsed.associations;
|
|
@@ -4760,7 +4855,7 @@ var SystemUseCaseAnalyzer = class {
|
|
|
4760
4855
|
tags: ["system-usecase", "auto-generated"]
|
|
4761
4856
|
});
|
|
4762
4857
|
}
|
|
4763
|
-
|
|
4858
|
+
logger23.info("Use case model confirmed to knowledge store");
|
|
4764
4859
|
}
|
|
4765
4860
|
/**
|
|
4766
4861
|
* Load a previously stored use case model from KnowledgeStore.
|
|
@@ -4775,7 +4870,7 @@ var SystemUseCaseAnalyzer = class {
|
|
|
4775
4870
|
this.currentModel = model;
|
|
4776
4871
|
return model;
|
|
4777
4872
|
} catch {
|
|
4778
|
-
|
|
4873
|
+
logger23.warn("Failed to parse stored use case model");
|
|
4779
4874
|
return null;
|
|
4780
4875
|
}
|
|
4781
4876
|
}
|
|
@@ -4842,7 +4937,7 @@ ${knowledgeSection}${entriesSection}
|
|
|
4842
4937
|
}
|
|
4843
4938
|
|
|
4844
4939
|
// src/services/ChatService.ts
|
|
4845
|
-
var
|
|
4940
|
+
var logger24 = logger.child("Chat");
|
|
4846
4941
|
function agentConfigToAIConfig(agentCfg, timeoutMs) {
|
|
4847
4942
|
return {
|
|
4848
4943
|
mode: agentCfg.mode,
|
|
@@ -4878,7 +4973,7 @@ var ChatService = class {
|
|
|
4878
4973
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4879
4974
|
};
|
|
4880
4975
|
this.sessions.set(session.id, session);
|
|
4881
|
-
|
|
4976
|
+
logger24.info("Created chat session", { sessionId: session.id });
|
|
4882
4977
|
return session;
|
|
4883
4978
|
}
|
|
4884
4979
|
getSession(id) {
|
|
@@ -4923,7 +5018,7 @@ var ChatService = class {
|
|
|
4923
5018
|
${r.content.slice(0, 500)}`).join("\n\n");
|
|
4924
5019
|
}
|
|
4925
5020
|
} catch (err) {
|
|
4926
|
-
|
|
5021
|
+
logger24.debug("Vector search failed, continuing without", { error: err.message });
|
|
4927
5022
|
}
|
|
4928
5023
|
}
|
|
4929
5024
|
const prompt = isFirstMessage ? `${buildChatSystemPrompt(getProjectKnowledge(), knowledgeEntries)}${vectorContext}
|
|
@@ -5004,14 +5099,108 @@ ${r.content.slice(0, 500)}`).join("\n\n");
|
|
|
5004
5099
|
}
|
|
5005
5100
|
};
|
|
5006
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
|
+
|
|
5007
5194
|
// src/web/WebServer.ts
|
|
5008
5195
|
var __dirname2 = path8.dirname(fileURLToPath2(import.meta.url));
|
|
5009
|
-
var
|
|
5196
|
+
var logger26 = logger.child("WebServer");
|
|
5010
5197
|
var WebServer = class _WebServer {
|
|
5011
5198
|
app;
|
|
5012
5199
|
server = null;
|
|
5013
5200
|
host;
|
|
5014
5201
|
port;
|
|
5202
|
+
terminalManager;
|
|
5203
|
+
config;
|
|
5015
5204
|
constructor(trackerOrDeps, config, agentLogStore, orchestrator, mainGit) {
|
|
5016
5205
|
let apiDeps;
|
|
5017
5206
|
if (trackerOrDeps instanceof IssueTracker) {
|
|
@@ -5049,8 +5238,18 @@ var WebServer = class _WebServer {
|
|
|
5049
5238
|
previewReaper: deps.previewReaper
|
|
5050
5239
|
};
|
|
5051
5240
|
}
|
|
5052
|
-
this.app =
|
|
5053
|
-
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
|
+
});
|
|
5054
5253
|
this.app.get("/metrics", (_req, res) => {
|
|
5055
5254
|
res.set("Content-Type", "text/plain; version=0.0.4; charset=utf-8");
|
|
5056
5255
|
res.send(metrics.serialize());
|
|
@@ -5074,12 +5273,12 @@ var WebServer = class _WebServer {
|
|
|
5074
5273
|
if (fs6.existsSync(projectKnowledgeIndex) && !fs6.existsSync(globalKnowledgeIndex)) {
|
|
5075
5274
|
try {
|
|
5076
5275
|
_WebServer.migrateKnowledgeDir(projectKnowledgeDir, globalKnowledgeDataDir);
|
|
5077
|
-
|
|
5276
|
+
logger26.info("Migrated knowledge entries from project to global DATA_DIR", {
|
|
5078
5277
|
src: projectKnowledgeDir,
|
|
5079
5278
|
dest: globalKnowledgeDataDir
|
|
5080
5279
|
});
|
|
5081
5280
|
} catch (err) {
|
|
5082
|
-
|
|
5281
|
+
logger26.warn("Failed to migrate knowledge entries to global DATA_DIR", {
|
|
5083
5282
|
error: err.message
|
|
5084
5283
|
});
|
|
5085
5284
|
}
|
|
@@ -5097,12 +5296,12 @@ var WebServer = class _WebServer {
|
|
|
5097
5296
|
if (fs6.existsSync(legacyKnowledgePath) && !fs6.existsSync(dataDirKnowledgePath)) {
|
|
5098
5297
|
try {
|
|
5099
5298
|
fs6.copyFileSync(legacyKnowledgePath, dataDirKnowledgePath);
|
|
5100
|
-
|
|
5299
|
+
logger26.info("Migrated knowledge.json from project to DATA_DIR", {
|
|
5101
5300
|
src: legacyKnowledgePath,
|
|
5102
5301
|
dest: dataDirKnowledgePath
|
|
5103
5302
|
});
|
|
5104
5303
|
} catch (err) {
|
|
5105
|
-
|
|
5304
|
+
logger26.warn("Failed to migrate knowledge.json to DATA_DIR", {
|
|
5106
5305
|
error: err.message
|
|
5107
5306
|
});
|
|
5108
5307
|
}
|
|
@@ -5159,8 +5358,13 @@ var WebServer = class _WebServer {
|
|
|
5159
5358
|
});
|
|
5160
5359
|
this.app.use(analyticsRouter);
|
|
5161
5360
|
}
|
|
5361
|
+
const terminalRouter = createTerminalRouter({
|
|
5362
|
+
terminalManager: this.terminalManager,
|
|
5363
|
+
config: apiDeps.config
|
|
5364
|
+
});
|
|
5365
|
+
this.app.use(terminalRouter);
|
|
5162
5366
|
const publicDir = apiDeps.config.web.frontendDistDir;
|
|
5163
|
-
this.app.use(
|
|
5367
|
+
this.app.use(express13.static(publicDir));
|
|
5164
5368
|
this.app.get("/{*splat}", (_req, res) => {
|
|
5165
5369
|
res.sendFile("index.html", { root: publicDir });
|
|
5166
5370
|
});
|
|
@@ -5168,16 +5372,22 @@ var WebServer = class _WebServer {
|
|
|
5168
5372
|
start() {
|
|
5169
5373
|
return new Promise((resolve) => {
|
|
5170
5374
|
this.server = this.app.listen(this.port, this.host, () => {
|
|
5171
|
-
|
|
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}`);
|
|
5172
5381
|
resolve();
|
|
5173
5382
|
});
|
|
5174
5383
|
});
|
|
5175
5384
|
}
|
|
5176
5385
|
stop() {
|
|
5386
|
+
this.terminalManager.destroyAll();
|
|
5177
5387
|
if (this.server) {
|
|
5178
5388
|
this.server.close();
|
|
5179
5389
|
this.server = null;
|
|
5180
|
-
|
|
5390
|
+
logger26.info("Web server stopped");
|
|
5181
5391
|
}
|
|
5182
5392
|
}
|
|
5183
5393
|
/**
|
|
@@ -5213,10 +5423,10 @@ var WebServer = class _WebServer {
|
|
|
5213
5423
|
};
|
|
5214
5424
|
|
|
5215
5425
|
// src/webhook/WebhookServer.ts
|
|
5216
|
-
import
|
|
5426
|
+
import express15 from "express";
|
|
5217
5427
|
|
|
5218
5428
|
// src/webhook/WebhookHandler.ts
|
|
5219
|
-
import
|
|
5429
|
+
import express14 from "express";
|
|
5220
5430
|
|
|
5221
5431
|
// src/webhook/CommandParser.ts
|
|
5222
5432
|
var TRIGGERS = ["@issue-auto", "@iaf"];
|
|
@@ -5419,7 +5629,7 @@ function parseExact(commandText) {
|
|
|
5419
5629
|
// src/webhook/CommandExecutor.ts
|
|
5420
5630
|
import path9 from "path";
|
|
5421
5631
|
import fs7 from "fs";
|
|
5422
|
-
var
|
|
5632
|
+
var logger27 = logger.child("CommandExecutor");
|
|
5423
5633
|
var CommandExecutor = class {
|
|
5424
5634
|
tracker;
|
|
5425
5635
|
orchestrator;
|
|
@@ -5438,13 +5648,13 @@ var CommandExecutor = class {
|
|
|
5438
5648
|
this.claimer = deps.claimer;
|
|
5439
5649
|
}
|
|
5440
5650
|
async execute(issueIid, issueId, command) {
|
|
5441
|
-
|
|
5651
|
+
logger27.info("Executing webhook command", { issueIid, command });
|
|
5442
5652
|
let result;
|
|
5443
5653
|
try {
|
|
5444
5654
|
result = await this.dispatch(issueIid, command);
|
|
5445
5655
|
} catch (err) {
|
|
5446
5656
|
const msg = err.message;
|
|
5447
|
-
|
|
5657
|
+
logger27.error("Webhook command failed", { issueIid, error: msg });
|
|
5448
5658
|
result = { success: false, message: msg };
|
|
5449
5659
|
}
|
|
5450
5660
|
await this.replyToIssue(issueId, result);
|
|
@@ -5502,6 +5712,7 @@ var CommandExecutor = class {
|
|
|
5502
5712
|
if (!phase) return this.fail("Phase is required for retry-from (e.g. design, implement)");
|
|
5503
5713
|
if (ctx) this.saveSupplement(iid, ctx);
|
|
5504
5714
|
try {
|
|
5715
|
+
this.poller?.forceReleaseIssue(iid);
|
|
5505
5716
|
this.orchestrator.retryFromPhase(iid, phase);
|
|
5506
5717
|
} catch (err) {
|
|
5507
5718
|
return this.fail(err.message);
|
|
@@ -5575,7 +5786,6 @@ var CommandExecutor = class {
|
|
|
5575
5786
|
}
|
|
5576
5787
|
async handleRestart(iid) {
|
|
5577
5788
|
try {
|
|
5578
|
-
this.poller?.forceReleaseIssue(iid);
|
|
5579
5789
|
if (this.claimer) {
|
|
5580
5790
|
const record = this.tracker.get(iid);
|
|
5581
5791
|
if (record) {
|
|
@@ -5584,6 +5794,7 @@ var CommandExecutor = class {
|
|
|
5584
5794
|
}
|
|
5585
5795
|
}
|
|
5586
5796
|
await this.orchestrator.restartIssue(iid);
|
|
5797
|
+
this.poller?.forceReleaseIssue(iid);
|
|
5587
5798
|
} catch (err) {
|
|
5588
5799
|
return this.fail(err.message);
|
|
5589
5800
|
}
|
|
@@ -5599,7 +5810,7 @@ var CommandExecutor = class {
|
|
|
5599
5810
|
const externalId = getExternalId(record);
|
|
5600
5811
|
await this.claimer.releaseClaim(externalId, iid, "cancelled");
|
|
5601
5812
|
} catch (err) {
|
|
5602
|
-
|
|
5813
|
+
logger27.warn("Failed to release lock on cancel", { iid, error: err.message });
|
|
5603
5814
|
}
|
|
5604
5815
|
}
|
|
5605
5816
|
await this.orchestrator.cancelIssue(iid);
|
|
@@ -5699,7 +5910,7 @@ var CommandExecutor = class {
|
|
|
5699
5910
|
return this.fail(t("conflict.noMr", { iid }));
|
|
5700
5911
|
}
|
|
5701
5912
|
this.orchestrator.resolveConflict(iid).catch((err) => {
|
|
5702
|
-
|
|
5913
|
+
logger27.error("Async conflict resolution failed", { iid, error: err.message });
|
|
5703
5914
|
});
|
|
5704
5915
|
return this.ok(t("conflict.startedMsg"));
|
|
5705
5916
|
}
|
|
@@ -5741,7 +5952,7 @@ ${context}` : context;
|
|
|
5741
5952
|
const prefix = result.success ? "" : "> **Failed**\n>\n> ";
|
|
5742
5953
|
await this.gongfeng.createIssueNote(issueId, `${prefix}${result.message}`);
|
|
5743
5954
|
} catch (err) {
|
|
5744
|
-
|
|
5955
|
+
logger27.warn("Failed to reply", { issueId, error: err.message });
|
|
5745
5956
|
}
|
|
5746
5957
|
}
|
|
5747
5958
|
ok(message) {
|
|
@@ -5796,10 +6007,10 @@ var NoteDeduplicator = class {
|
|
|
5796
6007
|
};
|
|
5797
6008
|
|
|
5798
6009
|
// src/webhook/WebhookHandler.ts
|
|
5799
|
-
var { Router:
|
|
5800
|
-
var
|
|
6010
|
+
var { Router: Router13 } = express14;
|
|
6011
|
+
var logger28 = logger.child("WebhookHandler");
|
|
5801
6012
|
function createWebhookRouter(deps) {
|
|
5802
|
-
const router =
|
|
6013
|
+
const router = Router13();
|
|
5803
6014
|
const executor = new CommandExecutor(deps);
|
|
5804
6015
|
const dedup = new NoteDeduplicator();
|
|
5805
6016
|
const { config, intentRecognizer } = deps;
|
|
@@ -5812,7 +6023,7 @@ function createWebhookRouter(deps) {
|
|
|
5812
6023
|
if (config.webhook.secret) {
|
|
5813
6024
|
const token = req.headers["x-gitlab-token"];
|
|
5814
6025
|
if (token !== config.webhook.secret) {
|
|
5815
|
-
|
|
6026
|
+
logger28.warn("Webhook token mismatch");
|
|
5816
6027
|
res.status(401).json({ error: "Invalid token" });
|
|
5817
6028
|
return;
|
|
5818
6029
|
}
|
|
@@ -5829,7 +6040,7 @@ function createWebhookRouter(deps) {
|
|
|
5829
6040
|
}
|
|
5830
6041
|
const noteId = event.object_attributes.id;
|
|
5831
6042
|
if (dedup.isDuplicate(noteId)) {
|
|
5832
|
-
|
|
6043
|
+
logger28.debug("Duplicate note, skipping", { noteId });
|
|
5833
6044
|
res.json({ ignored: true, reason: "duplicate" });
|
|
5834
6045
|
return;
|
|
5835
6046
|
}
|
|
@@ -5841,7 +6052,7 @@ function createWebhookRouter(deps) {
|
|
|
5841
6052
|
return;
|
|
5842
6053
|
}
|
|
5843
6054
|
if (!issueId) {
|
|
5844
|
-
|
|
6055
|
+
logger28.info("Webhook received with null issue id (likely a test ping)", { issueIid });
|
|
5845
6056
|
res.json({ accepted: true, noteId, issueIid, test: true });
|
|
5846
6057
|
return;
|
|
5847
6058
|
}
|
|
@@ -5881,19 +6092,19 @@ async function processCommandAsync(noteBody, issueIid, issueId, executor, intent
|
|
|
5881
6092
|
if (!command && intentRecognizer && config.webhook.llmFallback) {
|
|
5882
6093
|
const record = tracker.get(issueIid);
|
|
5883
6094
|
const state = record?.state;
|
|
5884
|
-
|
|
6095
|
+
logger28.info("Falling back to LLM intent recognition", { issueIid });
|
|
5885
6096
|
command = await intentRecognizer.recognize(cmdText, state);
|
|
5886
6097
|
}
|
|
5887
6098
|
if (!command) {
|
|
5888
|
-
|
|
6099
|
+
logger28.info("Could not parse command from note", { issueIid, text: cmdText.slice(0, 100) });
|
|
5889
6100
|
await gongfeng.createIssueNote(issueId, HELP_TEXT_FUNC()).catch(
|
|
5890
|
-
(e) =>
|
|
6101
|
+
(e) => logger28.warn("Failed to reply help text", { error: e.message })
|
|
5891
6102
|
);
|
|
5892
6103
|
return;
|
|
5893
6104
|
}
|
|
5894
6105
|
await executor.execute(issueIid, issueId, command);
|
|
5895
6106
|
} catch (err) {
|
|
5896
|
-
|
|
6107
|
+
logger28.error("Failed to process webhook command", {
|
|
5897
6108
|
issueIid,
|
|
5898
6109
|
error: err.message
|
|
5899
6110
|
});
|
|
@@ -5913,7 +6124,7 @@ async function processMRCommandAsync(noteBody, mr, executor, tracker, gongfeng)
|
|
|
5913
6124
|
const command = parseExact(cmdText);
|
|
5914
6125
|
if (!command) {
|
|
5915
6126
|
await gongfeng.createMergeRequestNote(mr.iid, HELP_TEXT_FUNC()).catch(
|
|
5916
|
-
(e) =>
|
|
6127
|
+
(e) => logger28.warn("Failed to reply help text on MR", { error: e.message })
|
|
5917
6128
|
);
|
|
5918
6129
|
return;
|
|
5919
6130
|
}
|
|
@@ -5922,7 +6133,7 @@ async function processMRCommandAsync(noteBody, mr, executor, tracker, gongfeng)
|
|
|
5922
6133
|
mr.iid,
|
|
5923
6134
|
`\`${command.intent}\` command is not supported on MR comments. Please use Issue comments instead.`
|
|
5924
6135
|
).catch(
|
|
5925
|
-
(e) =>
|
|
6136
|
+
(e) => logger28.warn("Failed to reply on MR", { error: e.message })
|
|
5926
6137
|
);
|
|
5927
6138
|
return;
|
|
5928
6139
|
}
|
|
@@ -5933,7 +6144,7 @@ async function processMRCommandAsync(noteBody, mr, executor, tracker, gongfeng)
|
|
|
5933
6144
|
mr.iid,
|
|
5934
6145
|
`Could not find a tracked issue for branch \`${mr.source_branch}\`.`
|
|
5935
6146
|
).catch(
|
|
5936
|
-
(e) =>
|
|
6147
|
+
(e) => logger28.warn("Failed to reply on MR", { error: e.message })
|
|
5937
6148
|
);
|
|
5938
6149
|
return;
|
|
5939
6150
|
}
|
|
@@ -5942,10 +6153,10 @@ async function processMRCommandAsync(noteBody, mr, executor, tracker, gongfeng)
|
|
|
5942
6153
|
const prefix = result.success ? "" : "> **Failed**\n>\n> ";
|
|
5943
6154
|
await gongfeng.createMergeRequestNote(mr.iid, `${prefix}${result.message}`);
|
|
5944
6155
|
} catch (err) {
|
|
5945
|
-
|
|
6156
|
+
logger28.warn("Failed to reply on MR", { mrIid: mr.iid, error: err.message });
|
|
5946
6157
|
}
|
|
5947
6158
|
} catch (err) {
|
|
5948
|
-
|
|
6159
|
+
logger28.error("Failed to process MR webhook command", {
|
|
5949
6160
|
mrIid: mr.iid,
|
|
5950
6161
|
error: err.message
|
|
5951
6162
|
});
|
|
@@ -5957,7 +6168,7 @@ function isSelfNote(_event, _selfToken) {
|
|
|
5957
6168
|
|
|
5958
6169
|
// src/webhook/IntentRecognizer.ts
|
|
5959
6170
|
import { spawn } from "child_process";
|
|
5960
|
-
var
|
|
6171
|
+
var logger29 = logger.child("IntentRecognizer");
|
|
5961
6172
|
var VALID_INTENTS = [
|
|
5962
6173
|
"retry",
|
|
5963
6174
|
"retry-from",
|
|
@@ -5999,7 +6210,7 @@ ${userComment}`;
|
|
|
5999
6210
|
const rawOutput = await this.runLLM(userPrompt);
|
|
6000
6211
|
return this.parseResponse(rawOutput);
|
|
6001
6212
|
} catch (err) {
|
|
6002
|
-
|
|
6213
|
+
logger29.error("Intent recognition failed", { error: err.message });
|
|
6003
6214
|
return null;
|
|
6004
6215
|
}
|
|
6005
6216
|
}
|
|
@@ -6007,20 +6218,20 @@ ${userComment}`;
|
|
|
6007
6218
|
parseResponse(raw) {
|
|
6008
6219
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
6009
6220
|
if (!jsonMatch) {
|
|
6010
|
-
|
|
6221
|
+
logger29.warn("No JSON found in LLM response", { raw: raw.slice(0, 200) });
|
|
6011
6222
|
return null;
|
|
6012
6223
|
}
|
|
6013
6224
|
let parsed;
|
|
6014
6225
|
try {
|
|
6015
6226
|
parsed = JSON.parse(jsonMatch[0]);
|
|
6016
6227
|
} catch {
|
|
6017
|
-
|
|
6228
|
+
logger29.warn("Failed to parse JSON from LLM", { raw: jsonMatch[0].slice(0, 200) });
|
|
6018
6229
|
return null;
|
|
6019
6230
|
}
|
|
6020
6231
|
if (parsed.intent === null || parsed.intent === "null") return null;
|
|
6021
6232
|
const intent = String(parsed.intent);
|
|
6022
6233
|
if (!VALID_INTENTS.includes(intent)) {
|
|
6023
|
-
|
|
6234
|
+
logger29.warn("Invalid intent from LLM", { intent });
|
|
6024
6235
|
return null;
|
|
6025
6236
|
}
|
|
6026
6237
|
const result = { intent };
|
|
@@ -6062,7 +6273,7 @@ ${userComment}`;
|
|
|
6062
6273
|
child.on("close", (code) => {
|
|
6063
6274
|
clearTimeout(timer);
|
|
6064
6275
|
if (code !== 0) {
|
|
6065
|
-
|
|
6276
|
+
logger29.warn("LLM process exited with non-zero code", { code, stderr: stderr.slice(0, 300) });
|
|
6066
6277
|
}
|
|
6067
6278
|
resolve(stdout);
|
|
6068
6279
|
});
|
|
@@ -6077,7 +6288,7 @@ ${userComment}`;
|
|
|
6077
6288
|
};
|
|
6078
6289
|
|
|
6079
6290
|
// src/webhook/WebhookServer.ts
|
|
6080
|
-
var
|
|
6291
|
+
var logger30 = logger.child("WebhookServer");
|
|
6081
6292
|
var WebhookServer = class {
|
|
6082
6293
|
app;
|
|
6083
6294
|
server = null;
|
|
@@ -6101,8 +6312,8 @@ var WebhookServer = class {
|
|
|
6101
6312
|
poller: deps.poller,
|
|
6102
6313
|
claimer: deps.claimer
|
|
6103
6314
|
};
|
|
6104
|
-
this.app =
|
|
6105
|
-
this.app.use(
|
|
6315
|
+
this.app = express15();
|
|
6316
|
+
this.app.use(express15.json());
|
|
6106
6317
|
this.app.use(createWebhookRouter(handlerDeps));
|
|
6107
6318
|
this.app.get("/health", (_req, res) => {
|
|
6108
6319
|
res.json({ status: "ok", service: "webhook" });
|
|
@@ -6111,7 +6322,7 @@ var WebhookServer = class {
|
|
|
6111
6322
|
start() {
|
|
6112
6323
|
return new Promise((resolve) => {
|
|
6113
6324
|
this.server = this.app.listen(this.port, this.host, () => {
|
|
6114
|
-
|
|
6325
|
+
logger30.info(`Webhook server listening on http://${resolveDisplayHost(this.host)}:${this.port}`);
|
|
6115
6326
|
resolve();
|
|
6116
6327
|
});
|
|
6117
6328
|
});
|
|
@@ -6120,7 +6331,7 @@ var WebhookServer = class {
|
|
|
6120
6331
|
if (this.server) {
|
|
6121
6332
|
this.server.close();
|
|
6122
6333
|
this.server = null;
|
|
6123
|
-
|
|
6334
|
+
logger30.info("Webhook server stopped");
|
|
6124
6335
|
}
|
|
6125
6336
|
}
|
|
6126
6337
|
};
|
|
@@ -6128,7 +6339,7 @@ var WebhookServer = class {
|
|
|
6128
6339
|
// src/web/AgentLogStore.ts
|
|
6129
6340
|
import fs8 from "fs";
|
|
6130
6341
|
import path10 from "path";
|
|
6131
|
-
var
|
|
6342
|
+
var logger31 = logger.child("AgentLogStore");
|
|
6132
6343
|
var MAX_LOGS_PER_ISSUE = 2e4;
|
|
6133
6344
|
var DEBUG_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
6134
6345
|
"thinking",
|
|
@@ -6157,7 +6368,7 @@ var AgentLogStore = class {
|
|
|
6157
6368
|
eventBus.on("pipeline:progress", (payload) => {
|
|
6158
6369
|
this.handlePipelineProgress(payload);
|
|
6159
6370
|
});
|
|
6160
|
-
|
|
6371
|
+
logger31.info("AgentLogStore listening for events");
|
|
6161
6372
|
}
|
|
6162
6373
|
getLogs(issueIid) {
|
|
6163
6374
|
const filePath = this.logFilePath(issueIid);
|
|
@@ -6167,7 +6378,7 @@ var AgentLogStore = class {
|
|
|
6167
6378
|
if (!raw) return [];
|
|
6168
6379
|
return raw.split("\n").map((line) => JSON.parse(line));
|
|
6169
6380
|
} catch (err) {
|
|
6170
|
-
|
|
6381
|
+
logger31.warn("Failed to read agent logs", { issueIid, error: err.message });
|
|
6171
6382
|
return [];
|
|
6172
6383
|
}
|
|
6173
6384
|
}
|
|
@@ -6187,7 +6398,7 @@ var AgentLogStore = class {
|
|
|
6187
6398
|
`, "utf-8");
|
|
6188
6399
|
this.trimIfNeeded(issueIid, filePath);
|
|
6189
6400
|
} catch (err) {
|
|
6190
|
-
|
|
6401
|
+
logger31.warn("Failed to write agent log", { issueIid, error: err.message });
|
|
6191
6402
|
}
|
|
6192
6403
|
}
|
|
6193
6404
|
trimIfNeeded(issueIid, filePath) {
|
|
@@ -6199,7 +6410,7 @@ var AgentLogStore = class {
|
|
|
6199
6410
|
const trimmed = lines.slice(-MAX_LOGS_PER_ISSUE);
|
|
6200
6411
|
fs8.writeFileSync(filePath, `${trimmed.join("\n")}
|
|
6201
6412
|
`, "utf-8");
|
|
6202
|
-
|
|
6413
|
+
logger31.info("Agent logs trimmed", { issueIid, from: lines.length, to: trimmed.length });
|
|
6203
6414
|
}
|
|
6204
6415
|
} catch {
|
|
6205
6416
|
}
|
|
@@ -6274,7 +6485,7 @@ import { readFileSync, existsSync } from "fs";
|
|
|
6274
6485
|
import path11 from "path";
|
|
6275
6486
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6276
6487
|
var __dirname3 = path11.dirname(fileURLToPath3(import.meta.url));
|
|
6277
|
-
var
|
|
6488
|
+
var logger32 = logger.child("VersionChecker");
|
|
6278
6489
|
function compareSemver(a, b) {
|
|
6279
6490
|
const pa = a.split(".").map(Number);
|
|
6280
6491
|
const pb = b.split(".").map(Number);
|
|
@@ -6305,7 +6516,7 @@ var VersionChecker = class {
|
|
|
6305
6516
|
async check() {
|
|
6306
6517
|
const latestVersion = await this.fetchLatestVersion();
|
|
6307
6518
|
const hasUpdate = compareSemver(latestVersion, this.currentVersion) > 0;
|
|
6308
|
-
|
|
6519
|
+
logger32.info("Version check completed", {
|
|
6309
6520
|
current: this.currentVersion,
|
|
6310
6521
|
latest: latestVersion,
|
|
6311
6522
|
hasUpdate
|
|
@@ -6353,7 +6564,7 @@ import { cpSync, existsSync as existsSync2, rmSync } from "fs";
|
|
|
6353
6564
|
import path12 from "path";
|
|
6354
6565
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6355
6566
|
var __dirname4 = path12.dirname(fileURLToPath4(import.meta.url));
|
|
6356
|
-
var
|
|
6567
|
+
var logger33 = logger.child("UpdateExecutor");
|
|
6357
6568
|
function findProjectRoot() {
|
|
6358
6569
|
for (let dir = __dirname4; dir !== path12.dirname(dir); dir = path12.dirname(dir)) {
|
|
6359
6570
|
if (existsSync2(path12.join(dir, "package.json"))) {
|
|
@@ -6383,7 +6594,7 @@ var UpdateExecutor = class {
|
|
|
6383
6594
|
async execute(targetVersion) {
|
|
6384
6595
|
const distDir = path12.join(this.projectRoot, "dist");
|
|
6385
6596
|
const backupDir = path12.join(this.projectRoot, "dist.backup");
|
|
6386
|
-
|
|
6597
|
+
logger33.info("Backing up dist directory...");
|
|
6387
6598
|
if (existsSync2(backupDir)) {
|
|
6388
6599
|
rmSync(backupDir, { recursive: true, force: true });
|
|
6389
6600
|
}
|
|
@@ -6391,12 +6602,12 @@ var UpdateExecutor = class {
|
|
|
6391
6602
|
cpSync(distDir, backupDir, { recursive: true });
|
|
6392
6603
|
}
|
|
6393
6604
|
try {
|
|
6394
|
-
|
|
6605
|
+
logger33.info("Running package update...", { package: this.packageName, targetVersion, bin: "npm" });
|
|
6395
6606
|
await run("npm", ["update", this.packageName], this.projectRoot, 12e4);
|
|
6396
|
-
|
|
6607
|
+
logger33.info("Running build...", { bin: "npm" });
|
|
6397
6608
|
await run("npm", ["run", "build"], this.projectRoot, 12e4);
|
|
6398
6609
|
} catch (err) {
|
|
6399
|
-
|
|
6610
|
+
logger33.error("Update failed, rolling back dist...", { error: err.message });
|
|
6400
6611
|
if (existsSync2(backupDir)) {
|
|
6401
6612
|
if (existsSync2(distDir)) {
|
|
6402
6613
|
rmSync(distDir, { recursive: true, force: true });
|
|
@@ -6409,21 +6620,21 @@ var UpdateExecutor = class {
|
|
|
6409
6620
|
rmSync(backupDir, { recursive: true, force: true });
|
|
6410
6621
|
}
|
|
6411
6622
|
}
|
|
6412
|
-
|
|
6623
|
+
logger33.info("Reloading via PM2...");
|
|
6413
6624
|
try {
|
|
6414
6625
|
await run("pm2", ["reload", "issue-auto-finish"], this.projectRoot, 3e4);
|
|
6415
6626
|
} catch (err) {
|
|
6416
|
-
|
|
6627
|
+
logger33.warn("PM2 reload failed, the process may need manual restart", {
|
|
6417
6628
|
error: err.message
|
|
6418
6629
|
});
|
|
6419
6630
|
throw err;
|
|
6420
6631
|
}
|
|
6421
|
-
|
|
6632
|
+
logger33.info("Update completed successfully", { version: targetVersion });
|
|
6422
6633
|
}
|
|
6423
6634
|
};
|
|
6424
6635
|
|
|
6425
6636
|
// src/updater/AutoUpdater.ts
|
|
6426
|
-
var
|
|
6637
|
+
var logger34 = logger.child("AutoUpdater");
|
|
6427
6638
|
var INITIAL_DELAY_MS = 3e4;
|
|
6428
6639
|
var AutoUpdater = class {
|
|
6429
6640
|
config;
|
|
@@ -6445,10 +6656,10 @@ var AutoUpdater = class {
|
|
|
6445
6656
|
}
|
|
6446
6657
|
start() {
|
|
6447
6658
|
if (!this.config.autoUpdate.enabled) {
|
|
6448
|
-
|
|
6659
|
+
logger34.info("Auto-update is disabled");
|
|
6449
6660
|
return;
|
|
6450
6661
|
}
|
|
6451
|
-
|
|
6662
|
+
logger34.info("Auto-updater starting", {
|
|
6452
6663
|
intervalMs: this.config.autoUpdate.intervalMs,
|
|
6453
6664
|
registry: this.config.autoUpdate.registry
|
|
6454
6665
|
});
|
|
@@ -6467,7 +6678,7 @@ var AutoUpdater = class {
|
|
|
6467
6678
|
clearInterval(this.checkTimer);
|
|
6468
6679
|
this.checkTimer = null;
|
|
6469
6680
|
}
|
|
6470
|
-
|
|
6681
|
+
logger34.info("Auto-updater stopped");
|
|
6471
6682
|
}
|
|
6472
6683
|
getStatus() {
|
|
6473
6684
|
return {
|
|
@@ -6507,7 +6718,7 @@ var AutoUpdater = class {
|
|
|
6507
6718
|
}
|
|
6508
6719
|
async triggerUpdate() {
|
|
6509
6720
|
if (this.updateInProgress) {
|
|
6510
|
-
|
|
6721
|
+
logger34.warn("Update already in progress, skipping");
|
|
6511
6722
|
return;
|
|
6512
6723
|
}
|
|
6513
6724
|
this.updateInProgress = true;
|
|
@@ -6515,40 +6726,40 @@ var AutoUpdater = class {
|
|
|
6515
6726
|
if (!this.latestVersion) {
|
|
6516
6727
|
const result = await this.checkForUpdate();
|
|
6517
6728
|
if (!result.hasUpdate) {
|
|
6518
|
-
|
|
6729
|
+
logger34.info("No update available");
|
|
6519
6730
|
return;
|
|
6520
6731
|
}
|
|
6521
6732
|
}
|
|
6522
6733
|
const targetVersion = this.latestVersion;
|
|
6523
6734
|
this.state = "draining";
|
|
6524
|
-
|
|
6735
|
+
logger34.info("Draining active issues before update...");
|
|
6525
6736
|
this.poller.pauseDiscovery();
|
|
6526
6737
|
const drainStart = Date.now();
|
|
6527
6738
|
const drainTimeout = this.config.autoUpdate.drainTimeoutMs;
|
|
6528
6739
|
while (this.poller.getActiveCount() > 0 && Date.now() - drainStart < drainTimeout) {
|
|
6529
|
-
|
|
6740
|
+
logger34.info("Waiting for active issues to complete...", {
|
|
6530
6741
|
active: this.poller.getActiveCount()
|
|
6531
6742
|
});
|
|
6532
6743
|
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
6533
6744
|
}
|
|
6534
6745
|
if (this.poller.getActiveCount() > 0) {
|
|
6535
|
-
|
|
6746
|
+
logger34.warn("Drain timeout reached, proceeding with update", {
|
|
6536
6747
|
active: this.poller.getActiveCount()
|
|
6537
6748
|
});
|
|
6538
6749
|
}
|
|
6539
6750
|
this.state = "updating";
|
|
6540
6751
|
eventBus.emitTyped("update:downloading", { version: targetVersion });
|
|
6541
|
-
|
|
6752
|
+
logger34.info("Executing update...", { targetVersion });
|
|
6542
6753
|
await this.updateExecutor.execute(targetVersion);
|
|
6543
6754
|
this.state = "completed";
|
|
6544
6755
|
eventBus.emitTyped("update:completed", { version: targetVersion });
|
|
6545
|
-
|
|
6756
|
+
logger34.info("Update completed", { version: targetVersion });
|
|
6546
6757
|
} catch (err) {
|
|
6547
6758
|
const message = err.message;
|
|
6548
6759
|
this.lastError = message;
|
|
6549
6760
|
this.state = "failed";
|
|
6550
6761
|
eventBus.emitTyped("update:failed", { error: message });
|
|
6551
|
-
|
|
6762
|
+
logger34.error("Update failed", { error: message });
|
|
6552
6763
|
this.poller.resumeDiscovery();
|
|
6553
6764
|
} finally {
|
|
6554
6765
|
this.updateInProgress = false;
|
|
@@ -6559,20 +6770,20 @@ var AutoUpdater = class {
|
|
|
6559
6770
|
try {
|
|
6560
6771
|
const result = await this.checkForUpdate();
|
|
6561
6772
|
if (result.hasUpdate) {
|
|
6562
|
-
|
|
6773
|
+
logger34.info("New version available, starting update", {
|
|
6563
6774
|
current: result.currentVersion,
|
|
6564
6775
|
latest: result.latestVersion
|
|
6565
6776
|
});
|
|
6566
6777
|
await this.triggerUpdate();
|
|
6567
6778
|
}
|
|
6568
6779
|
} catch (err) {
|
|
6569
|
-
|
|
6780
|
+
logger34.warn("Periodic version check failed", { error: err.message });
|
|
6570
6781
|
}
|
|
6571
6782
|
}
|
|
6572
6783
|
};
|
|
6573
6784
|
|
|
6574
6785
|
// src/config/ConfigReloader.ts
|
|
6575
|
-
var
|
|
6786
|
+
var logger35 = logger.child("ConfigReloader");
|
|
6576
6787
|
var ConfigReloader = class {
|
|
6577
6788
|
config;
|
|
6578
6789
|
poller;
|
|
@@ -6588,11 +6799,11 @@ var ConfigReloader = class {
|
|
|
6588
6799
|
}
|
|
6589
6800
|
async reload(opts) {
|
|
6590
6801
|
const timeoutMs = opts?.waitTimeoutMs ?? 6e4;
|
|
6591
|
-
|
|
6802
|
+
logger35.info("Config reload requested", { timeoutMs });
|
|
6592
6803
|
this.poller.pauseDiscovery();
|
|
6593
6804
|
const drained = await this.waitForDrain(timeoutMs);
|
|
6594
6805
|
if (!drained) {
|
|
6595
|
-
|
|
6806
|
+
logger35.warn("Active issues did not drain within timeout, aborting reload", {
|
|
6596
6807
|
active: this.poller.getActiveCount(),
|
|
6597
6808
|
timeoutMs
|
|
6598
6809
|
});
|
|
@@ -6607,7 +6818,7 @@ var ConfigReloader = class {
|
|
|
6607
6818
|
this.orchestrator.setAIRunner(newRunner);
|
|
6608
6819
|
this.options.onAIRunnerChanged?.(newRunner);
|
|
6609
6820
|
this.gongfeng.updateConfig(this.config.gongfeng);
|
|
6610
|
-
|
|
6821
|
+
logger35.info("Config reloaded successfully", {
|
|
6611
6822
|
aiMode: this.config.ai.mode,
|
|
6612
6823
|
aiModel: this.config.ai.model,
|
|
6613
6824
|
discoveryIntervalMs: this.config.poll.discoveryIntervalMs
|
|
@@ -6628,7 +6839,7 @@ var ConfigReloader = class {
|
|
|
6628
6839
|
|
|
6629
6840
|
// src/distill/DiaryCollector.ts
|
|
6630
6841
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6631
|
-
var
|
|
6842
|
+
var logger36 = logger.child("DiaryCollector");
|
|
6632
6843
|
var DiaryCollector = class {
|
|
6633
6844
|
tracker;
|
|
6634
6845
|
diaryStore;
|
|
@@ -6646,21 +6857,21 @@ var DiaryCollector = class {
|
|
|
6646
6857
|
if (this.stateChangedHandler) return;
|
|
6647
6858
|
this.stateChangedHandler = (payload) => {
|
|
6648
6859
|
this.handleStateChanged(payload).catch((err) => {
|
|
6649
|
-
|
|
6860
|
+
logger36.warn("DiaryCollector failed to handle stateChanged event", {
|
|
6650
6861
|
error: err.message
|
|
6651
6862
|
});
|
|
6652
6863
|
});
|
|
6653
6864
|
};
|
|
6654
6865
|
this.failedHandler = (payload) => {
|
|
6655
6866
|
this.handleFailed(payload).catch((err) => {
|
|
6656
|
-
|
|
6867
|
+
logger36.warn("DiaryCollector failed to handle failed event", {
|
|
6657
6868
|
error: err.message
|
|
6658
6869
|
});
|
|
6659
6870
|
});
|
|
6660
6871
|
};
|
|
6661
6872
|
eventBus.on("issue:stateChanged", this.stateChangedHandler);
|
|
6662
6873
|
eventBus.on("issue:failed", this.failedHandler);
|
|
6663
|
-
|
|
6874
|
+
logger36.info("DiaryCollector started");
|
|
6664
6875
|
}
|
|
6665
6876
|
/** 停止监听事件 */
|
|
6666
6877
|
stop() {
|
|
@@ -6672,7 +6883,7 @@ var DiaryCollector = class {
|
|
|
6672
6883
|
eventBus.off("issue:failed", this.failedHandler);
|
|
6673
6884
|
this.failedHandler = null;
|
|
6674
6885
|
}
|
|
6675
|
-
|
|
6886
|
+
logger36.info("DiaryCollector stopped");
|
|
6676
6887
|
}
|
|
6677
6888
|
/** 处理 issue:stateChanged 事件 */
|
|
6678
6889
|
async handleStateChanged(payload) {
|
|
@@ -6683,7 +6894,7 @@ var DiaryCollector = class {
|
|
|
6683
6894
|
if (!issueIid) return;
|
|
6684
6895
|
const existing = this.diaryStore.getByIssueIid(issueIid);
|
|
6685
6896
|
if (existing.some((d) => d.outcome === "completed")) {
|
|
6686
|
-
|
|
6897
|
+
logger36.debug("Diary already exists for completed issue", { issueIid });
|
|
6687
6898
|
return;
|
|
6688
6899
|
}
|
|
6689
6900
|
await this.collectDiary(issueIid, "completed");
|
|
@@ -6700,7 +6911,7 @@ var DiaryCollector = class {
|
|
|
6700
6911
|
try {
|
|
6701
6912
|
const record = this.tracker.get(issueIid);
|
|
6702
6913
|
if (!record) {
|
|
6703
|
-
|
|
6914
|
+
logger36.warn("Cannot collect diary: issue record not found", { issueIid });
|
|
6704
6915
|
return null;
|
|
6705
6916
|
}
|
|
6706
6917
|
const plan = this.createPlanPersistence?.(issueIid);
|
|
@@ -6724,10 +6935,10 @@ var DiaryCollector = class {
|
|
|
6724
6935
|
};
|
|
6725
6936
|
this.diaryStore.create(diary);
|
|
6726
6937
|
eventBus.emitTyped("distill:diary:created", { issueIid, diaryId: diary.id, outcome });
|
|
6727
|
-
|
|
6938
|
+
logger36.info("Diary collected", { issueIid, diaryId: diary.id, outcome });
|
|
6728
6939
|
return diary;
|
|
6729
6940
|
} catch (err) {
|
|
6730
|
-
|
|
6941
|
+
logger36.warn("Failed to collect diary", {
|
|
6731
6942
|
issueIid,
|
|
6732
6943
|
outcome,
|
|
6733
6944
|
error: err.message
|
|
@@ -6950,7 +7161,7 @@ ${memorySection}
|
|
|
6950
7161
|
}
|
|
6951
7162
|
|
|
6952
7163
|
// src/distill/MemoryDistiller.ts
|
|
6953
|
-
var
|
|
7164
|
+
var logger37 = logger.child("MemoryDistiller");
|
|
6954
7165
|
var MemoryDistiller = class {
|
|
6955
7166
|
aiRunner;
|
|
6956
7167
|
diaryStore;
|
|
@@ -6975,17 +7186,17 @@ var MemoryDistiller = class {
|
|
|
6975
7186
|
async distill(options) {
|
|
6976
7187
|
const undistilled = this.diaryStore.getUndistilled();
|
|
6977
7188
|
if (!options?.force && undistilled.length < this.minDiariesForDistill) {
|
|
6978
|
-
|
|
7189
|
+
logger37.info("Not enough undistilled diaries, skipping memory distillation", {
|
|
6979
7190
|
count: undistilled.length,
|
|
6980
7191
|
threshold: this.minDiariesForDistill
|
|
6981
7192
|
});
|
|
6982
7193
|
return { processedDiaries: 0, actions: 0 };
|
|
6983
7194
|
}
|
|
6984
7195
|
if (undistilled.length === 0) {
|
|
6985
|
-
|
|
7196
|
+
logger37.info("No undistilled diaries available");
|
|
6986
7197
|
return { processedDiaries: 0, actions: 0 };
|
|
6987
7198
|
}
|
|
6988
|
-
|
|
7199
|
+
logger37.info("Starting memory distillation", { diaryCount: undistilled.length });
|
|
6989
7200
|
const existingMemories = this.loadExistingMemories();
|
|
6990
7201
|
const prompt = buildMemoryDistillPrompt(undistilled, existingMemories);
|
|
6991
7202
|
const result = await this.aiRunner.run({
|
|
@@ -6994,12 +7205,12 @@ var MemoryDistiller = class {
|
|
|
6994
7205
|
timeoutMs: this.timeoutMs
|
|
6995
7206
|
});
|
|
6996
7207
|
if (!result.success) {
|
|
6997
|
-
|
|
7208
|
+
logger37.error("AI distillation failed", { output: result.output.slice(0, 500) });
|
|
6998
7209
|
throw new Error(`Memory distillation AI call failed: ${result.output.slice(0, 200)}`);
|
|
6999
7210
|
}
|
|
7000
7211
|
const actions = this.parseActions(result.output);
|
|
7001
7212
|
if (actions.length === 0) {
|
|
7002
|
-
|
|
7213
|
+
logger37.info("No distillation actions returned by AI");
|
|
7003
7214
|
this.diaryStore.markDistilled(undistilled.map((d) => d.id));
|
|
7004
7215
|
return { processedDiaries: undistilled.length, actions: 0 };
|
|
7005
7216
|
}
|
|
@@ -7009,14 +7220,14 @@ var MemoryDistiller = class {
|
|
|
7009
7220
|
this.executeAction(action, existingMemories);
|
|
7010
7221
|
actionCount++;
|
|
7011
7222
|
} catch (err) {
|
|
7012
|
-
|
|
7223
|
+
logger37.warn("Failed to execute distill action", {
|
|
7013
7224
|
action: action.type,
|
|
7014
7225
|
error: err.message
|
|
7015
7226
|
});
|
|
7016
7227
|
}
|
|
7017
7228
|
}
|
|
7018
7229
|
this.diaryStore.markDistilled(undistilled.map((d) => d.id));
|
|
7019
|
-
|
|
7230
|
+
logger37.info("Memory distillation complete", {
|
|
7020
7231
|
processedDiaries: undistilled.length,
|
|
7021
7232
|
actions: actionCount
|
|
7022
7233
|
});
|
|
@@ -7040,18 +7251,18 @@ var MemoryDistiller = class {
|
|
|
7040
7251
|
try {
|
|
7041
7252
|
const jsonMatch = output.match(/```json\s*([\s\S]*?)```/) ?? output.match(/\{[\s\S]*"actions"[\s\S]*\}/);
|
|
7042
7253
|
if (!jsonMatch) {
|
|
7043
|
-
|
|
7254
|
+
logger37.warn("No JSON found in AI output");
|
|
7044
7255
|
return [];
|
|
7045
7256
|
}
|
|
7046
7257
|
const jsonStr = jsonMatch[1] ?? jsonMatch[0];
|
|
7047
7258
|
const parsed = JSON.parse(jsonStr);
|
|
7048
7259
|
if (!Array.isArray(parsed.actions)) {
|
|
7049
|
-
|
|
7260
|
+
logger37.warn("Invalid actions format in AI output");
|
|
7050
7261
|
return [];
|
|
7051
7262
|
}
|
|
7052
7263
|
return parsed.actions;
|
|
7053
7264
|
} catch (err) {
|
|
7054
|
-
|
|
7265
|
+
logger37.warn("Failed to parse AI distillation output", {
|
|
7055
7266
|
error: err.message,
|
|
7056
7267
|
output: output.slice(0, 300)
|
|
7057
7268
|
});
|
|
@@ -7071,7 +7282,7 @@ var MemoryDistiller = class {
|
|
|
7071
7282
|
this.supersedeMemory(action, existingMemories);
|
|
7072
7283
|
break;
|
|
7073
7284
|
default:
|
|
7074
|
-
|
|
7285
|
+
logger37.warn("Unknown distill action type", { type: action.type });
|
|
7075
7286
|
}
|
|
7076
7287
|
}
|
|
7077
7288
|
/** 创建新 memory */
|
|
@@ -7101,13 +7312,13 @@ var MemoryDistiller = class {
|
|
|
7101
7312
|
action: "created",
|
|
7102
7313
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7103
7314
|
});
|
|
7104
|
-
|
|
7315
|
+
logger37.info("Created new memory", { id: memoryEntry.id, theme: action.theme, title: action.title });
|
|
7105
7316
|
}
|
|
7106
7317
|
/** 合并新证据到已有 memory */
|
|
7107
7318
|
mergeMemory(action, existingMemories) {
|
|
7108
7319
|
const existing = existingMemories.find((m) => m.id === action.memoryId);
|
|
7109
7320
|
if (!existing) {
|
|
7110
|
-
|
|
7321
|
+
logger37.warn("Cannot merge: memory not found", { memoryId: action.memoryId });
|
|
7111
7322
|
return;
|
|
7112
7323
|
}
|
|
7113
7324
|
existing.evidence = [.../* @__PURE__ */ new Set([...existing.evidence, ...action.newEvidence])];
|
|
@@ -7140,7 +7351,7 @@ var MemoryDistiller = class {
|
|
|
7140
7351
|
reason: `Merged ${action.newEvidence.length} new evidence(s)`,
|
|
7141
7352
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7142
7353
|
});
|
|
7143
|
-
|
|
7354
|
+
logger37.info("Merged memory", { id: existing.id, newEvidence: action.newEvidence.length });
|
|
7144
7355
|
}
|
|
7145
7356
|
/** 用新 memory 替代旧 memory */
|
|
7146
7357
|
supersedeMemory(action, existingMemories) {
|
|
@@ -7182,7 +7393,7 @@ var MemoryDistiller = class {
|
|
|
7182
7393
|
reason: `Supersedes ${action.oldMemoryId}`,
|
|
7183
7394
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7184
7395
|
});
|
|
7185
|
-
|
|
7396
|
+
logger37.info("Superseded memory", {
|
|
7186
7397
|
oldId: action.oldMemoryId,
|
|
7187
7398
|
newId: newMemory.id,
|
|
7188
7399
|
theme: action.theme
|
|
@@ -7284,7 +7495,7 @@ ${ruleSection}
|
|
|
7284
7495
|
}
|
|
7285
7496
|
|
|
7286
7497
|
// src/distill/AgentRuleDistiller.ts
|
|
7287
|
-
var
|
|
7498
|
+
var logger38 = logger.child("AgentRuleDistiller");
|
|
7288
7499
|
var AgentRuleDistiller = class {
|
|
7289
7500
|
aiRunner;
|
|
7290
7501
|
knowledgeStore;
|
|
@@ -7313,11 +7524,11 @@ var AgentRuleDistiller = class {
|
|
|
7313
7524
|
async distill() {
|
|
7314
7525
|
const matureMemories = this.getMatureMemories();
|
|
7315
7526
|
if (matureMemories.length === 0) {
|
|
7316
|
-
|
|
7527
|
+
logger38.info("No mature memories ready for rule distillation");
|
|
7317
7528
|
return { processedMemories: 0, actions: 0 };
|
|
7318
7529
|
}
|
|
7319
7530
|
const existingRules = this.loadExistingRules();
|
|
7320
|
-
|
|
7531
|
+
logger38.info("Starting rule distillation", {
|
|
7321
7532
|
matureMemories: matureMemories.length,
|
|
7322
7533
|
existingRules: existingRules.length
|
|
7323
7534
|
});
|
|
@@ -7328,12 +7539,12 @@ var AgentRuleDistiller = class {
|
|
|
7328
7539
|
timeoutMs: this.timeoutMs
|
|
7329
7540
|
});
|
|
7330
7541
|
if (!result.success) {
|
|
7331
|
-
|
|
7542
|
+
logger38.error("AI rule distillation failed", { output: result.output.slice(0, 500) });
|
|
7332
7543
|
throw new Error(`Rule distillation AI call failed: ${result.output.slice(0, 200)}`);
|
|
7333
7544
|
}
|
|
7334
7545
|
const actions = this.parseActions(result.output);
|
|
7335
7546
|
if (actions.length === 0) {
|
|
7336
|
-
|
|
7547
|
+
logger38.info("No rule distillation actions returned by AI");
|
|
7337
7548
|
return { processedMemories: matureMemories.length, actions: 0 };
|
|
7338
7549
|
}
|
|
7339
7550
|
let actionCount = 0;
|
|
@@ -7342,13 +7553,13 @@ var AgentRuleDistiller = class {
|
|
|
7342
7553
|
this.executeAction(action, existingRules);
|
|
7343
7554
|
actionCount++;
|
|
7344
7555
|
} catch (err) {
|
|
7345
|
-
|
|
7556
|
+
logger38.warn("Failed to execute rule distill action", {
|
|
7346
7557
|
action: action.type,
|
|
7347
7558
|
error: err.message
|
|
7348
7559
|
});
|
|
7349
7560
|
}
|
|
7350
7561
|
}
|
|
7351
|
-
|
|
7562
|
+
logger38.info("Rule distillation complete", {
|
|
7352
7563
|
processedMemories: matureMemories.length,
|
|
7353
7564
|
actions: actionCount
|
|
7354
7565
|
});
|
|
@@ -7402,7 +7613,7 @@ var AgentRuleDistiller = class {
|
|
|
7402
7613
|
try {
|
|
7403
7614
|
const jsonMatch = output.match(/```json\s*([\s\S]*?)```/) ?? output.match(/\{[\s\S]*"actions"[\s\S]*\}/);
|
|
7404
7615
|
if (!jsonMatch) {
|
|
7405
|
-
|
|
7616
|
+
logger38.warn("No JSON found in AI rule distill output");
|
|
7406
7617
|
return [];
|
|
7407
7618
|
}
|
|
7408
7619
|
const jsonStr = jsonMatch[1] ?? jsonMatch[0];
|
|
@@ -7410,7 +7621,7 @@ var AgentRuleDistiller = class {
|
|
|
7410
7621
|
if (!Array.isArray(parsed.actions)) return [];
|
|
7411
7622
|
return parsed.actions;
|
|
7412
7623
|
} catch (err) {
|
|
7413
|
-
|
|
7624
|
+
logger38.warn("Failed to parse AI rule distill output", {
|
|
7414
7625
|
error: err.message
|
|
7415
7626
|
});
|
|
7416
7627
|
return [];
|
|
@@ -7429,7 +7640,7 @@ var AgentRuleDistiller = class {
|
|
|
7429
7640
|
this.deprecateRule(action, existingRules);
|
|
7430
7641
|
break;
|
|
7431
7642
|
default:
|
|
7432
|
-
|
|
7643
|
+
logger38.warn("Unknown rule distill action type", { type: action.type });
|
|
7433
7644
|
}
|
|
7434
7645
|
}
|
|
7435
7646
|
/** 创建新规则 */
|
|
@@ -7462,13 +7673,13 @@ var AgentRuleDistiller = class {
|
|
|
7462
7673
|
action: "created",
|
|
7463
7674
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7464
7675
|
});
|
|
7465
|
-
|
|
7676
|
+
logger38.info("Created agent rule", { id: ruleEntry.id, ruleName: action.ruleName });
|
|
7466
7677
|
}
|
|
7467
7678
|
/** 更新已有规则 */
|
|
7468
7679
|
updateRule(action, existingRules) {
|
|
7469
7680
|
const existing = existingRules.find((r) => r.id === action.ruleId);
|
|
7470
7681
|
if (!existing) {
|
|
7471
|
-
|
|
7682
|
+
logger38.warn("Cannot update: rule not found", { ruleId: action.ruleId });
|
|
7472
7683
|
return;
|
|
7473
7684
|
}
|
|
7474
7685
|
existing.content = action.content;
|
|
@@ -7498,13 +7709,13 @@ var AgentRuleDistiller = class {
|
|
|
7498
7709
|
action: "merged",
|
|
7499
7710
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7500
7711
|
});
|
|
7501
|
-
|
|
7712
|
+
logger38.info("Updated agent rule", { id: existing.id, ruleName: existing.ruleName });
|
|
7502
7713
|
}
|
|
7503
7714
|
/** 废弃规则 */
|
|
7504
7715
|
deprecateRule(action, existingRules) {
|
|
7505
7716
|
const existing = existingRules.find((r) => r.id === action.ruleId);
|
|
7506
7717
|
if (!existing) {
|
|
7507
|
-
|
|
7718
|
+
logger38.warn("Cannot deprecate: rule not found", { ruleId: action.ruleId });
|
|
7508
7719
|
return;
|
|
7509
7720
|
}
|
|
7510
7721
|
existing.deprecated = true;
|
|
@@ -7533,7 +7744,7 @@ var AgentRuleDistiller = class {
|
|
|
7533
7744
|
reason: action.reason,
|
|
7534
7745
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7535
7746
|
});
|
|
7536
|
-
|
|
7747
|
+
logger38.info("Deprecated agent rule", { id: existing.id, reason: action.reason });
|
|
7537
7748
|
}
|
|
7538
7749
|
/** 同步规则到 DATA_DIR/rules/ MDC 文件,可选同步到项目 */
|
|
7539
7750
|
syncMdcFile(rule) {
|
|
@@ -7549,9 +7760,9 @@ var AgentRuleDistiller = class {
|
|
|
7549
7760
|
ensureDir(this.rulesDir);
|
|
7550
7761
|
const filePath = path13.join(this.rulesDir, `distilled-${rule.ruleName}.mdc`);
|
|
7551
7762
|
fs9.writeFileSync(filePath, mdcContent, "utf-8");
|
|
7552
|
-
|
|
7763
|
+
logger38.debug("Synced MDC file", { path: filePath });
|
|
7553
7764
|
} catch (err) {
|
|
7554
|
-
|
|
7765
|
+
logger38.warn("Failed to sync MDC file", {
|
|
7555
7766
|
ruleName: rule.ruleName,
|
|
7556
7767
|
error: err.message
|
|
7557
7768
|
});
|
|
@@ -7561,9 +7772,9 @@ var AgentRuleDistiller = class {
|
|
|
7561
7772
|
ensureDir(this.projectRulesDir);
|
|
7562
7773
|
const projectFilePath = path13.join(this.projectRulesDir, `distilled-${rule.ruleName}.mdc`);
|
|
7563
7774
|
fs9.writeFileSync(projectFilePath, mdcContent, "utf-8");
|
|
7564
|
-
|
|
7775
|
+
logger38.debug("Synced MDC file to project", { path: projectFilePath });
|
|
7565
7776
|
} catch (err) {
|
|
7566
|
-
|
|
7777
|
+
logger38.warn("Failed to sync MDC file to project", {
|
|
7567
7778
|
ruleName: rule.ruleName,
|
|
7568
7779
|
error: err.message
|
|
7569
7780
|
});
|
|
@@ -7577,10 +7788,10 @@ var AgentRuleDistiller = class {
|
|
|
7577
7788
|
const filePath = path13.join(this.rulesDir, fileName);
|
|
7578
7789
|
if (fs9.existsSync(filePath)) {
|
|
7579
7790
|
fs9.unlinkSync(filePath);
|
|
7580
|
-
|
|
7791
|
+
logger38.debug("Removed MDC file", { path: filePath });
|
|
7581
7792
|
}
|
|
7582
7793
|
} catch (err) {
|
|
7583
|
-
|
|
7794
|
+
logger38.warn("Failed to remove MDC file", {
|
|
7584
7795
|
ruleName,
|
|
7585
7796
|
error: err.message
|
|
7586
7797
|
});
|
|
@@ -7590,10 +7801,10 @@ var AgentRuleDistiller = class {
|
|
|
7590
7801
|
const projectFilePath = path13.join(this.projectRulesDir, fileName);
|
|
7591
7802
|
if (fs9.existsSync(projectFilePath)) {
|
|
7592
7803
|
fs9.unlinkSync(projectFilePath);
|
|
7593
|
-
|
|
7804
|
+
logger38.debug("Removed MDC file from project", { path: projectFilePath });
|
|
7594
7805
|
}
|
|
7595
7806
|
} catch (err) {
|
|
7596
|
-
|
|
7807
|
+
logger38.warn("Failed to remove MDC file from project", {
|
|
7597
7808
|
ruleName,
|
|
7598
7809
|
error: err.message
|
|
7599
7810
|
});
|
|
@@ -7624,7 +7835,7 @@ var AgentRuleDistiller = class {
|
|
|
7624
7835
|
// src/distill/VectorStore.ts
|
|
7625
7836
|
import fs10 from "fs";
|
|
7626
7837
|
import path14 from "path";
|
|
7627
|
-
var
|
|
7838
|
+
var logger39 = logger.child("VectorStore");
|
|
7628
7839
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
7629
7840
|
// 英文
|
|
7630
7841
|
"the",
|
|
@@ -7811,7 +8022,7 @@ var VectorStore = class {
|
|
|
7811
8022
|
async reindex() {
|
|
7812
8023
|
this.rebuildDocumentFreqs();
|
|
7813
8024
|
this.save();
|
|
7814
|
-
|
|
8025
|
+
logger39.info("Vector index rebuilt", { count: this.data.vectors.length });
|
|
7815
8026
|
}
|
|
7816
8027
|
/** 获取索引条目数 */
|
|
7817
8028
|
count() {
|
|
@@ -7888,7 +8099,7 @@ var VectorStore = class {
|
|
|
7888
8099
|
return JSON.parse(raw);
|
|
7889
8100
|
}
|
|
7890
8101
|
} catch (err) {
|
|
7891
|
-
|
|
8102
|
+
logger39.warn("Failed to load vector store", { error: err.message });
|
|
7892
8103
|
}
|
|
7893
8104
|
return { documentFreqs: {}, vectors: [], totalDocs: 0 };
|
|
7894
8105
|
}
|
|
@@ -7906,7 +8117,7 @@ var VectorStore = class {
|
|
|
7906
8117
|
// src/distill/VersionStore.ts
|
|
7907
8118
|
import fs11 from "fs";
|
|
7908
8119
|
import path15 from "path";
|
|
7909
|
-
var
|
|
8120
|
+
var logger40 = logger.child("VersionStore");
|
|
7910
8121
|
var VersionStore = class {
|
|
7911
8122
|
filePath;
|
|
7912
8123
|
data;
|
|
@@ -7938,7 +8149,7 @@ var VersionStore = class {
|
|
|
7938
8149
|
return JSON.parse(raw);
|
|
7939
8150
|
}
|
|
7940
8151
|
} catch (err) {
|
|
7941
|
-
|
|
8152
|
+
logger40.warn("Failed to load version store", { error: err.message });
|
|
7942
8153
|
}
|
|
7943
8154
|
return { records: [] };
|
|
7944
8155
|
}
|
|
@@ -7954,7 +8165,7 @@ var VersionStore = class {
|
|
|
7954
8165
|
};
|
|
7955
8166
|
|
|
7956
8167
|
// src/distill/DistillScheduler.ts
|
|
7957
|
-
var
|
|
8168
|
+
var logger41 = logger.child("DistillScheduler");
|
|
7958
8169
|
var DistillScheduler = class {
|
|
7959
8170
|
diaryStore;
|
|
7960
8171
|
memoryDistiller;
|
|
@@ -7980,29 +8191,29 @@ var DistillScheduler = class {
|
|
|
7980
8191
|
if (this.timer) return;
|
|
7981
8192
|
this.timer = setInterval(() => {
|
|
7982
8193
|
this.runDistill().catch((err) => {
|
|
7983
|
-
|
|
8194
|
+
logger41.error("Scheduled distillation failed", { error: err.message });
|
|
7984
8195
|
});
|
|
7985
8196
|
}, this.intervalMs);
|
|
7986
|
-
|
|
8197
|
+
logger41.info("DistillScheduler started", { intervalMs: this.intervalMs });
|
|
7987
8198
|
}
|
|
7988
8199
|
/** 停止定时调度 */
|
|
7989
8200
|
stop() {
|
|
7990
8201
|
if (this.timer) {
|
|
7991
8202
|
clearInterval(this.timer);
|
|
7992
8203
|
this.timer = null;
|
|
7993
|
-
|
|
8204
|
+
logger41.info("DistillScheduler stopped");
|
|
7994
8205
|
}
|
|
7995
8206
|
}
|
|
7996
8207
|
/** 手动触发蒸馏 */
|
|
7997
8208
|
async runDistill(options) {
|
|
7998
8209
|
if (this.running) {
|
|
7999
|
-
|
|
8210
|
+
logger41.info("Distillation already in progress, skipping");
|
|
8000
8211
|
return { memory: { processedDiaries: 0, actions: 0 }, rule: { processedMemories: 0, actions: 0 }, vectorIndexed: 0 };
|
|
8001
8212
|
}
|
|
8002
8213
|
this.running = true;
|
|
8003
8214
|
eventBus.emitTyped("distill:started", {});
|
|
8004
8215
|
try {
|
|
8005
|
-
|
|
8216
|
+
logger41.info("Starting distillation pipeline");
|
|
8006
8217
|
const memoryResult = await this.memoryDistiller.distill({ force: options?.force });
|
|
8007
8218
|
if (memoryResult.actions > 0) {
|
|
8008
8219
|
eventBus.emitTyped("distill:memory:updated", {
|
|
@@ -8027,7 +8238,7 @@ var DistillScheduler = class {
|
|
|
8027
8238
|
rule: ruleResult,
|
|
8028
8239
|
vectorIndexed
|
|
8029
8240
|
});
|
|
8030
|
-
|
|
8241
|
+
logger41.info("Distillation pipeline complete", {
|
|
8031
8242
|
memoryActions: memoryResult.actions,
|
|
8032
8243
|
ruleActions: ruleResult.actions,
|
|
8033
8244
|
vectorIndexed
|
|
@@ -8095,13 +8306,13 @@ ${content}`
|
|
|
8095
8306
|
});
|
|
8096
8307
|
indexed++;
|
|
8097
8308
|
}
|
|
8098
|
-
|
|
8309
|
+
logger41.info("Vector indexing complete", { indexed });
|
|
8099
8310
|
return indexed;
|
|
8100
8311
|
}
|
|
8101
8312
|
};
|
|
8102
8313
|
|
|
8103
8314
|
// src/deploy/PreviewReaper.ts
|
|
8104
|
-
var
|
|
8315
|
+
var logger42 = logger.child("PreviewReaper");
|
|
8105
8316
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed" /* Completed */, "failed" /* Failed */, "deployed" /* Deployed */]);
|
|
8106
8317
|
var PreviewReaper = class {
|
|
8107
8318
|
tracker;
|
|
@@ -8124,16 +8335,16 @@ var PreviewReaper = class {
|
|
|
8124
8335
|
if (this.timer) return;
|
|
8125
8336
|
this.timer = setInterval(() => {
|
|
8126
8337
|
this.reap().catch((err) => {
|
|
8127
|
-
|
|
8338
|
+
logger42.error("Scheduled preview reap failed", { error: err.message });
|
|
8128
8339
|
});
|
|
8129
8340
|
}, this.intervalMs);
|
|
8130
|
-
|
|
8341
|
+
logger42.info("PreviewReaper started", { intervalMs: this.intervalMs, ttlMs: this.ttlMs });
|
|
8131
8342
|
}
|
|
8132
8343
|
stop() {
|
|
8133
8344
|
if (this.timer) {
|
|
8134
8345
|
clearInterval(this.timer);
|
|
8135
8346
|
this.timer = null;
|
|
8136
|
-
|
|
8347
|
+
logger42.info("PreviewReaper stopped");
|
|
8137
8348
|
}
|
|
8138
8349
|
}
|
|
8139
8350
|
async reap() {
|
|
@@ -8155,13 +8366,13 @@ var PreviewReaper = class {
|
|
|
8155
8366
|
try {
|
|
8156
8367
|
this.orchestrator.stopPreviewServers(iid);
|
|
8157
8368
|
reaped.push(iid);
|
|
8158
|
-
|
|
8369
|
+
logger42.info(t("reaper.reaped", { iid, hours }));
|
|
8159
8370
|
} catch (err) {
|
|
8160
|
-
|
|
8371
|
+
logger42.warn("Failed to reap preview", { iid, error: err.message });
|
|
8161
8372
|
}
|
|
8162
8373
|
}
|
|
8163
8374
|
if (reaped.length > 0) {
|
|
8164
|
-
|
|
8375
|
+
logger42.info(t("reaper.summary", { count: reaped.length }));
|
|
8165
8376
|
this.bus.emitTyped("preview:reaped", { iids: reaped, count: reaped.length });
|
|
8166
8377
|
}
|
|
8167
8378
|
this.lastScanAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -8190,7 +8401,7 @@ import { randomUUID as randomUUID5 } from "crypto";
|
|
|
8190
8401
|
import { execFile as execFile2 } from "child_process";
|
|
8191
8402
|
import { promisify } from "util";
|
|
8192
8403
|
var execFileAsync = promisify(execFile2);
|
|
8193
|
-
var
|
|
8404
|
+
var logger43 = logger.child("OrphanBranchManager");
|
|
8194
8405
|
var SYNC_MANIFEST = [
|
|
8195
8406
|
"knowledge/index.json",
|
|
8196
8407
|
"knowledge/knowledge.json",
|
|
@@ -8261,7 +8472,7 @@ var OrphanBranchManager = class {
|
|
|
8261
8472
|
await this.gitIn(tmpDir, ["add", "-A"]);
|
|
8262
8473
|
const status = await this.gitIn(tmpDir, ["status", "--porcelain"]);
|
|
8263
8474
|
if (!status.trim()) {
|
|
8264
|
-
|
|
8475
|
+
logger43.info("No changes to publish");
|
|
8265
8476
|
return { commitSha: await this.gitIn(tmpDir, ["rev-parse", "HEAD"]) };
|
|
8266
8477
|
}
|
|
8267
8478
|
await this.gitIn(tmpDir, [
|
|
@@ -8272,7 +8483,7 @@ var OrphanBranchManager = class {
|
|
|
8272
8483
|
]);
|
|
8273
8484
|
const commitSha = await this.gitIn(tmpDir, ["rev-parse", "HEAD"]);
|
|
8274
8485
|
await this.pushWithRetry(tmpDir);
|
|
8275
|
-
|
|
8486
|
+
logger43.info("Knowledge published to orphan branch", {
|
|
8276
8487
|
branch: this.branch,
|
|
8277
8488
|
commitSha: commitSha.slice(0, 8),
|
|
8278
8489
|
entriesCount: copied
|
|
@@ -8339,7 +8550,7 @@ var OrphanBranchManager = class {
|
|
|
8339
8550
|
} catch {
|
|
8340
8551
|
}
|
|
8341
8552
|
}
|
|
8342
|
-
|
|
8553
|
+
logger43.info("Knowledge restored from orphan branch", {
|
|
8343
8554
|
branch: this.branch,
|
|
8344
8555
|
entriesCount
|
|
8345
8556
|
});
|
|
@@ -8376,7 +8587,7 @@ var OrphanBranchManager = class {
|
|
|
8376
8587
|
// Private helpers
|
|
8377
8588
|
// -----------------------------------------------------------------------
|
|
8378
8589
|
async git(args) {
|
|
8379
|
-
|
|
8590
|
+
logger43.debug("git exec (main)", { args });
|
|
8380
8591
|
const { stdout } = await execFileAsync("git", args, {
|
|
8381
8592
|
cwd: this.gitRootDir,
|
|
8382
8593
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -8385,7 +8596,7 @@ var OrphanBranchManager = class {
|
|
|
8385
8596
|
return stdout.trim();
|
|
8386
8597
|
}
|
|
8387
8598
|
async gitIn(cwd, args) {
|
|
8388
|
-
|
|
8599
|
+
logger43.debug("git exec (worktree)", { cwd: path16.basename(cwd), args });
|
|
8389
8600
|
const { stdout } = await execFileAsync("git", args, {
|
|
8390
8601
|
cwd,
|
|
8391
8602
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -8485,7 +8696,7 @@ var OrphanBranchManager = class {
|
|
|
8485
8696
|
`Failed to push to orphan branch after ${MAX_PUSH_RETRIES} attempts: ${err.message}`
|
|
8486
8697
|
);
|
|
8487
8698
|
}
|
|
8488
|
-
|
|
8699
|
+
logger43.warn("Push failed, pulling and retrying", {
|
|
8489
8700
|
attempt,
|
|
8490
8701
|
error: err.message
|
|
8491
8702
|
});
|
|
@@ -8498,7 +8709,7 @@ var OrphanBranchManager = class {
|
|
|
8498
8709
|
try {
|
|
8499
8710
|
await this.git(["worktree", "remove", wtDir, "--force"]);
|
|
8500
8711
|
} catch {
|
|
8501
|
-
|
|
8712
|
+
logger43.debug("Worktree cleanup skipped (may not exist)", { dir: wtDir });
|
|
8502
8713
|
}
|
|
8503
8714
|
try {
|
|
8504
8715
|
await this.git(["worktree", "prune"]);
|
|
@@ -8515,7 +8726,7 @@ import fs14 from "fs";
|
|
|
8515
8726
|
import { z } from "zod";
|
|
8516
8727
|
import fs13 from "fs";
|
|
8517
8728
|
import path17 from "path";
|
|
8518
|
-
var
|
|
8729
|
+
var logger44 = logger.child("TenantConfig");
|
|
8519
8730
|
var tenantGongfengSchema = z.object({
|
|
8520
8731
|
apiUrl: z.string().url("apiUrl must be a valid URL"),
|
|
8521
8732
|
privateToken: z.string().min(1, "privateToken is required"),
|
|
@@ -8542,7 +8753,7 @@ var DEFAULT_TENANT_ID = "default";
|
|
|
8542
8753
|
function loadTenantsConfig(configPath) {
|
|
8543
8754
|
if (!configPath) return null;
|
|
8544
8755
|
if (!fs13.existsSync(configPath)) {
|
|
8545
|
-
|
|
8756
|
+
logger44.warn("Tenants config file not found, falling back to single-tenant mode", {
|
|
8546
8757
|
path: configPath
|
|
8547
8758
|
});
|
|
8548
8759
|
return null;
|
|
@@ -8553,14 +8764,14 @@ function loadTenantsConfig(configPath) {
|
|
|
8553
8764
|
const result = tenantsConfigSchema.safeParse(json);
|
|
8554
8765
|
if (!result.success) {
|
|
8555
8766
|
const issues = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
8556
|
-
|
|
8767
|
+
logger44.error(`Tenants config validation failed:
|
|
8557
8768
|
${issues}`);
|
|
8558
8769
|
return null;
|
|
8559
8770
|
}
|
|
8560
8771
|
const ids = result.data.tenants.map((t2) => t2.id);
|
|
8561
8772
|
const dupes = ids.filter((id, i) => ids.indexOf(id) !== i);
|
|
8562
8773
|
if (dupes.length > 0) {
|
|
8563
|
-
|
|
8774
|
+
logger44.error("Duplicate tenant IDs found", { duplicates: [...new Set(dupes)] });
|
|
8564
8775
|
return null;
|
|
8565
8776
|
}
|
|
8566
8777
|
const baseDir = path17.dirname(configPath);
|
|
@@ -8569,12 +8780,12 @@ ${issues}`);
|
|
|
8569
8780
|
tenant.workspace = path17.resolve(baseDir, tenant.workspace);
|
|
8570
8781
|
}
|
|
8571
8782
|
}
|
|
8572
|
-
|
|
8783
|
+
logger44.info("Tenants config loaded", {
|
|
8573
8784
|
tenants: result.data.tenants.map((t2) => t2.id)
|
|
8574
8785
|
});
|
|
8575
8786
|
return result.data;
|
|
8576
8787
|
} catch (err) {
|
|
8577
|
-
|
|
8788
|
+
logger44.error("Failed to parse tenants config", {
|
|
8578
8789
|
path: configPath,
|
|
8579
8790
|
error: err.message
|
|
8580
8791
|
});
|
|
@@ -8637,6 +8848,49 @@ async function main() {
|
|
|
8637
8848
|
const allAiPhaseNames = [...new Set(
|
|
8638
8849
|
[...lifecycleManagers.values()].flatMap((lm) => lm.getExecutablePhaseNames())
|
|
8639
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
|
+
}
|
|
8640
8894
|
validatePhaseRegistry(allAiPhaseNames);
|
|
8641
8895
|
validateRunnerRegistry([config.ai.mode]);
|
|
8642
8896
|
const aiRunner = createAIRunner(config.ai);
|
|
@@ -8894,7 +9148,8 @@ async function main() {
|
|
|
8894
9148
|
configReloader,
|
|
8895
9149
|
orphanManager,
|
|
8896
9150
|
wsConfig: primaryTenant.wsConfig,
|
|
8897
|
-
previewReaper
|
|
9151
|
+
previewReaper,
|
|
9152
|
+
terminalManager: sharedTerminalManager
|
|
8898
9153
|
});
|
|
8899
9154
|
await webServer.start();
|
|
8900
9155
|
}
|
|
@@ -9005,4 +9260,4 @@ function migrateKnowledgeDir(srcDir, destDir) {
|
|
|
9005
9260
|
export {
|
|
9006
9261
|
main
|
|
9007
9262
|
};
|
|
9008
|
-
//# sourceMappingURL=chunk-
|
|
9263
|
+
//# sourceMappingURL=chunk-QZZGIZWC.js.map
|