@mclawnet/agent 0.6.10 → 0.6.12
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/__tests__/skill-sync.test.d.ts +2 -0
- package/dist/__tests__/skill-sync.test.d.ts.map +1 -0
- package/dist/backend-adapter.d.ts +35 -0
- package/dist/backend-adapter.d.ts.map +1 -1
- package/dist/{chunk-AZDR76NN.js → chunk-MSDIRBXF.js} +515 -155
- package/dist/chunk-MSDIRBXF.js.map +1 -0
- package/dist/fs-handler.d.ts +25 -1
- package/dist/fs-handler.d.ts.map +1 -1
- package/dist/hub-connection.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/session-manager.d.ts +12 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/skill-loader.d.ts +22 -3
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +1 -1
- package/package.json +6 -4
- package/dist/chunk-AZDR76NN.js.map +0 -1
|
@@ -2,6 +2,10 @@ import {
|
|
|
2
2
|
loadConfig
|
|
3
3
|
} from "./chunk-CBZIH6FY.js";
|
|
4
4
|
|
|
5
|
+
// src/start.ts
|
|
6
|
+
import { homedir as homedir3 } from "os";
|
|
7
|
+
import { join as join4 } from "path";
|
|
8
|
+
|
|
5
9
|
// src/hub-connection.ts
|
|
6
10
|
import { hostname as osHostname } from "os";
|
|
7
11
|
import WebSocket from "ws";
|
|
@@ -169,12 +173,12 @@ async function handleListHistorySessions(workDir) {
|
|
|
169
173
|
sessions.sort((a, b) => (b.lastModified ?? 0) - (a.lastModified ?? 0));
|
|
170
174
|
return { workDir, sessions };
|
|
171
175
|
}
|
|
172
|
-
async function handleLoadSessionHistory(workDir, claudeSessionId) {
|
|
176
|
+
async function handleLoadSessionHistory(workDir, claudeSessionId, opts) {
|
|
173
177
|
const projectsDir = getClaudeProjectsDir();
|
|
174
178
|
const projectFolder = pathToProjectFolder(workDir);
|
|
175
179
|
const filePath = join(projectsDir, projectFolder, `${claudeSessionId}.jsonl`);
|
|
176
180
|
if (!existsSync(filePath)) {
|
|
177
|
-
return { messages: [] };
|
|
181
|
+
return { messages: [], oldestSeq: 0, hasMore: false };
|
|
178
182
|
}
|
|
179
183
|
const messages = [];
|
|
180
184
|
try {
|
|
@@ -236,11 +240,19 @@ async function handleLoadSessionHistory(workDir, claudeSessionId) {
|
|
|
236
240
|
}
|
|
237
241
|
} catch {
|
|
238
242
|
}
|
|
239
|
-
|
|
243
|
+
const limit = Math.max(1, Math.min(opts?.limit ?? 50, 200));
|
|
244
|
+
const upper = opts?.before === void 0 || opts.before <= 0 || opts.before > messages.length ? messages.length : opts.before;
|
|
245
|
+
const lower = Math.max(0, upper - limit);
|
|
246
|
+
const slice = messages.slice(lower, upper);
|
|
247
|
+
return {
|
|
248
|
+
messages: slice,
|
|
249
|
+
oldestSeq: lower,
|
|
250
|
+
hasMore: lower > 0
|
|
251
|
+
};
|
|
240
252
|
}
|
|
241
253
|
|
|
242
254
|
// src/hub-connection.ts
|
|
243
|
-
import { createLogger } from "@mclawnet/logger";
|
|
255
|
+
import { createLogger, previewFields } from "@mclawnet/logger";
|
|
244
256
|
var log = createLogger({ module: "agent" });
|
|
245
257
|
var HubConnection = class {
|
|
246
258
|
ws = null;
|
|
@@ -425,12 +437,15 @@ var HubConnection = class {
|
|
|
425
437
|
return true;
|
|
426
438
|
}
|
|
427
439
|
if (msg.type === "load_session_history") {
|
|
428
|
-
log.info(
|
|
429
|
-
|
|
440
|
+
log.info(
|
|
441
|
+
{ workDir: msg.workDir, claudeSessionId: msg.claudeSessionId, before: msg.before, limit: msg.limit },
|
|
442
|
+
"load_session_history"
|
|
443
|
+
);
|
|
444
|
+
handleLoadSessionHistory(msg.workDir, msg.claudeSessionId, { before: msg.before, limit: msg.limit }).then((result) => {
|
|
430
445
|
this.send({ type: "session_history_result", requestId: msg.requestId, ...result });
|
|
431
446
|
}).catch((err) => {
|
|
432
447
|
log.error({ err, workDir: msg.workDir }, "load_session_history failed");
|
|
433
|
-
this.send({ type: "session_history_result", requestId: msg.requestId, messages: [] });
|
|
448
|
+
this.send({ type: "session_history_result", requestId: msg.requestId, messages: [], oldestSeq: 0, hasMore: false });
|
|
434
449
|
});
|
|
435
450
|
return true;
|
|
436
451
|
}
|
|
@@ -596,13 +611,12 @@ var HubConnection = class {
|
|
|
596
611
|
}
|
|
597
612
|
if (msg.type === "claude.execute") {
|
|
598
613
|
const { sessionId, content, workDir, claudeSessionId, useBrainCore } = msg;
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
this.sessionManager.sendInput(sessionId, content);
|
|
614
|
+
const sm = this.sessionManager;
|
|
615
|
+
const spawnAndSend = (resumeId, label) => {
|
|
616
|
+
log.info({ sessionId, claudeSessionId: resumeId, workDir, label }, "claude.execute: spawning");
|
|
617
|
+
log.debug({ sessionId, ...previewFields(content) }, "claude.execute: input");
|
|
618
|
+
sm.createSession({ sessionId, workDir, resumeId, useBrainCore, roleId: "role-__assistant__" }).then(() => {
|
|
619
|
+
sm.sendInput(sessionId, content);
|
|
606
620
|
}).catch((err) => {
|
|
607
621
|
this.send({
|
|
608
622
|
type: "session.error",
|
|
@@ -610,6 +624,45 @@ var HubConnection = class {
|
|
|
610
624
|
error: err instanceof Error ? err.message : String(err)
|
|
611
625
|
});
|
|
612
626
|
});
|
|
627
|
+
};
|
|
628
|
+
if (sm.isHealthy(sessionId)) {
|
|
629
|
+
log.info({ sessionId }, "claude.execute: reusing healthy session");
|
|
630
|
+
log.debug({ sessionId, ...previewFields(content) }, "claude.execute: input");
|
|
631
|
+
sm.sendInput(sessionId, content);
|
|
632
|
+
} else if (sm.hasSession(sessionId) && claudeSessionId) {
|
|
633
|
+
log.warn({ sessionId, claudeSessionId }, "claude.execute: session unhealthy, recreating with --resume");
|
|
634
|
+
this.send({
|
|
635
|
+
type: "session.died",
|
|
636
|
+
sessionId,
|
|
637
|
+
reason: "unhealthy_before_input"
|
|
638
|
+
});
|
|
639
|
+
sm.abortSession(sessionId).catch((err) => {
|
|
640
|
+
log.warn({ sessionId, err: err instanceof Error ? err.message : String(err) }, "abortSession failed during unhealthy fallback, proceeding to respawn");
|
|
641
|
+
}).then(() => spawnAndSend(claudeSessionId, "unhealthy_fallback_resume"));
|
|
642
|
+
} else if (claudeSessionId) {
|
|
643
|
+
spawnAndSend(claudeSessionId, "fresh_resume");
|
|
644
|
+
} else {
|
|
645
|
+
spawnAndSend(void 0, "brand_new");
|
|
646
|
+
}
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
if (msg.type === "session.force_restart") {
|
|
650
|
+
const { sessionId, reason } = msg;
|
|
651
|
+
log.warn({ sessionId, reason }, "session.force_restart received");
|
|
652
|
+
const sm = this.sessionManager;
|
|
653
|
+
const reply = {
|
|
654
|
+
type: "session.died",
|
|
655
|
+
sessionId,
|
|
656
|
+
reason: reason ?? "force_restart"
|
|
657
|
+
};
|
|
658
|
+
if (sm.hasSession(sessionId)) {
|
|
659
|
+
sm.abortSession(sessionId).catch((err) => {
|
|
660
|
+
log.warn({ sessionId, err: err instanceof Error ? err.message : String(err) }, "abortSession failed during force_restart");
|
|
661
|
+
}).finally(() => {
|
|
662
|
+
this.send(reply);
|
|
663
|
+
});
|
|
664
|
+
} else {
|
|
665
|
+
this.send(reply);
|
|
613
666
|
}
|
|
614
667
|
return true;
|
|
615
668
|
}
|
|
@@ -642,7 +695,10 @@ var HubConnection = class {
|
|
|
642
695
|
return true;
|
|
643
696
|
}
|
|
644
697
|
if (msg.type === "claude.input") {
|
|
645
|
-
log.
|
|
698
|
+
log.debug(
|
|
699
|
+
{ sessionId: msg.sessionId, ...previewFields(msg.content) },
|
|
700
|
+
"claude.input"
|
|
701
|
+
);
|
|
646
702
|
this.sessionManager.sendInput(msg.sessionId, msg.content);
|
|
647
703
|
return true;
|
|
648
704
|
}
|
|
@@ -731,24 +787,321 @@ var HubConnection = class {
|
|
|
731
787
|
};
|
|
732
788
|
|
|
733
789
|
// src/session-manager.ts
|
|
734
|
-
import { createLogger as
|
|
790
|
+
import { createLogger as createLogger3, previewFields as previewFields2 } from "@mclawnet/logger";
|
|
735
791
|
import { buildMemorySection } from "@mclawnet/memory";
|
|
736
|
-
|
|
792
|
+
|
|
793
|
+
// src/skill-loader.ts
|
|
794
|
+
import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
795
|
+
import { createHash } from "crypto";
|
|
796
|
+
import { join as join2, dirname } from "path";
|
|
797
|
+
import { homedir as homedir2 } from "os";
|
|
798
|
+
import { createRequire } from "module";
|
|
799
|
+
import { fileURLToPath } from "url";
|
|
800
|
+
import { createLogger as createLogger2 } from "@mclawnet/logger";
|
|
801
|
+
import { ManifestManager, mergeSkillSections } from "@mclawnet/skill-manager";
|
|
802
|
+
var log2 = createLogger2({ module: "agent/skill-loader" });
|
|
803
|
+
var CLAWNET_DIR = join2(homedir2(), ".clawnet");
|
|
804
|
+
var SKILLS_DIR = join2(CLAWNET_DIR, ".claude", "skills");
|
|
805
|
+
var MCP_CONFIG_PATH = join2(CLAWNET_DIR, "mcp.json");
|
|
806
|
+
async function initSkills() {
|
|
807
|
+
ensureSkillsDir();
|
|
808
|
+
syncBuiltinSkills(CLAWNET_DIR, defaultBuiltinSourceDir());
|
|
809
|
+
ensureMcpConfig();
|
|
810
|
+
scanSkills();
|
|
811
|
+
}
|
|
812
|
+
function defaultBuiltinSourceDir() {
|
|
813
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
814
|
+
return join2(dirname(thisFile), "..", "skills");
|
|
815
|
+
}
|
|
816
|
+
function sha256(s) {
|
|
817
|
+
return createHash("sha256").update(s).digest("hex");
|
|
818
|
+
}
|
|
819
|
+
function readBuiltinVersion(content) {
|
|
820
|
+
const match = content.match(/^version:\s*(.+)$/m);
|
|
821
|
+
return match ? match[1].trim().replace(/^["']|["']$/g, "") : "1.0.0";
|
|
822
|
+
}
|
|
823
|
+
function syncBuiltinSkills(rootDir, srcDir) {
|
|
824
|
+
const skillsDir = join2(rootDir, ".claude", "skills");
|
|
825
|
+
if (!existsSync2(skillsDir)) mkdirSync(skillsDir, { recursive: true });
|
|
826
|
+
if (!existsSync2(srcDir)) {
|
|
827
|
+
log2.debug({ srcDir }, "no built-in skills directory found, skipping");
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
const manifest = new ManifestManager(rootDir);
|
|
831
|
+
let entries;
|
|
832
|
+
try {
|
|
833
|
+
entries = readdirSync2(srcDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
834
|
+
} catch {
|
|
835
|
+
log2.warn({ srcDir }, "failed to read built-in skills directory");
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
for (const skillName of entries) {
|
|
839
|
+
const srcSkillMd = join2(srcDir, skillName, "SKILL.md");
|
|
840
|
+
if (!existsSync2(srcSkillMd)) continue;
|
|
841
|
+
const destDir = join2(skillsDir, skillName);
|
|
842
|
+
const destSkillMd = join2(destDir, "SKILL.md");
|
|
843
|
+
const baseSnapshotPath = join2(destDir, ".base.md");
|
|
844
|
+
let officialContent;
|
|
845
|
+
try {
|
|
846
|
+
officialContent = readFileSync2(srcSkillMd, "utf-8");
|
|
847
|
+
} catch (err) {
|
|
848
|
+
log2.warn({ skill: skillName, err }, "failed to read built-in skill");
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
const officialHash = sha256(officialContent);
|
|
852
|
+
const officialVersion = readBuiltinVersion(officialContent);
|
|
853
|
+
if (!existsSync2(destSkillMd)) {
|
|
854
|
+
try {
|
|
855
|
+
mkdirSync(destDir, { recursive: true });
|
|
856
|
+
writeFileSync(destSkillMd, officialContent);
|
|
857
|
+
writeFileSync(baseSnapshotPath, officialContent);
|
|
858
|
+
manifest.register(skillName, officialHash, officialVersion);
|
|
859
|
+
log2.info({ skill: skillName, version: officialVersion }, "installed built-in skill");
|
|
860
|
+
} catch (err) {
|
|
861
|
+
log2.warn({ skill: skillName, err }, "failed to install built-in skill");
|
|
862
|
+
}
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
const userContent = readFileSync2(destSkillMd, "utf-8");
|
|
866
|
+
const userHash = sha256(userContent);
|
|
867
|
+
manifest.refresh(skillName, userHash);
|
|
868
|
+
if (!existsSync2(baseSnapshotPath)) {
|
|
869
|
+
const entry = manifest.load().skills[skillName];
|
|
870
|
+
if (entry && !entry.userModified) {
|
|
871
|
+
try {
|
|
872
|
+
writeFileSync(baseSnapshotPath, userContent);
|
|
873
|
+
} catch (err) {
|
|
874
|
+
log2.debug({ skill: skillName, err }, "failed to write lazy .base.md");
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
const action = manifest.determineSyncAction(skillName, officialHash, officialVersion);
|
|
879
|
+
switch (action) {
|
|
880
|
+
case "skip":
|
|
881
|
+
log2.debug({ skill: skillName }, "skill already up to date");
|
|
882
|
+
break;
|
|
883
|
+
case "direct-overwrite":
|
|
884
|
+
try {
|
|
885
|
+
writeFileSync(destSkillMd, officialContent);
|
|
886
|
+
writeFileSync(baseSnapshotPath, officialContent);
|
|
887
|
+
manifest.markSynced(skillName, officialHash, officialHash, officialVersion);
|
|
888
|
+
log2.info({ skill: skillName, version: officialVersion }, "upgraded built-in skill");
|
|
889
|
+
} catch (err) {
|
|
890
|
+
log2.warn({ skill: skillName, err }, "failed to overwrite skill");
|
|
891
|
+
}
|
|
892
|
+
break;
|
|
893
|
+
case "keep-user":
|
|
894
|
+
log2.debug({ skill: skillName }, "keeping user-modified skill");
|
|
895
|
+
break;
|
|
896
|
+
case "needs-merge": {
|
|
897
|
+
if (!existsSync2(baseSnapshotPath)) {
|
|
898
|
+
try {
|
|
899
|
+
writeFileSync(
|
|
900
|
+
destSkillMd + ".conflict",
|
|
901
|
+
renderConflictFile(skillName, officialContent, [
|
|
902
|
+
{
|
|
903
|
+
section: "(entire file)",
|
|
904
|
+
official: officialContent,
|
|
905
|
+
user: userContent
|
|
906
|
+
}
|
|
907
|
+
])
|
|
908
|
+
);
|
|
909
|
+
log2.warn(
|
|
910
|
+
{ skill: skillName },
|
|
911
|
+
"missing .base.md snapshot \u2014 skipping auto-merge, wrote .conflict advisory"
|
|
912
|
+
);
|
|
913
|
+
} catch (err) {
|
|
914
|
+
log2.warn({ skill: skillName, err }, "failed to write .conflict advisory");
|
|
915
|
+
}
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
const baseContent = readFileSync2(baseSnapshotPath, "utf-8");
|
|
919
|
+
const merged = mergeSkillSections(baseContent, officialContent, userContent);
|
|
920
|
+
if (merged.success && merged.content) {
|
|
921
|
+
try {
|
|
922
|
+
writeFileSync(destSkillMd, merged.content);
|
|
923
|
+
writeFileSync(baseSnapshotPath, officialContent);
|
|
924
|
+
const newCurrentHash = sha256(merged.content);
|
|
925
|
+
manifest.markSynced(skillName, officialHash, newCurrentHash, officialVersion);
|
|
926
|
+
log2.info({ skill: skillName }, "auto-merged skill upgrade");
|
|
927
|
+
} catch (err) {
|
|
928
|
+
log2.warn({ skill: skillName, err }, "failed to write merged skill");
|
|
929
|
+
}
|
|
930
|
+
} else {
|
|
931
|
+
try {
|
|
932
|
+
writeFileSync(
|
|
933
|
+
destSkillMd + ".conflict",
|
|
934
|
+
renderConflictFile(skillName, officialContent, merged.conflicts ?? [])
|
|
935
|
+
);
|
|
936
|
+
log2.warn(
|
|
937
|
+
{ skill: skillName, conflicts: merged.conflicts?.length },
|
|
938
|
+
"skill merge conflict \u2014 user version kept, .conflict file written"
|
|
939
|
+
);
|
|
940
|
+
} catch (err) {
|
|
941
|
+
log2.warn({ skill: skillName, err }, "failed to write .conflict file");
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
function renderConflictFile(skillName, officialContent, conflicts) {
|
|
950
|
+
const lines = [
|
|
951
|
+
`# Skill upgrade conflict: ${skillName}`,
|
|
952
|
+
``,
|
|
953
|
+
`The official skill was updated, and your local copy was also modified.`,
|
|
954
|
+
`Please review each section below and reconcile manually.`,
|
|
955
|
+
``,
|
|
956
|
+
`## Conflicted sections`,
|
|
957
|
+
``
|
|
958
|
+
];
|
|
959
|
+
for (const c of conflicts) {
|
|
960
|
+
lines.push(`### ${c.section}`);
|
|
961
|
+
lines.push("");
|
|
962
|
+
lines.push("**Official:**");
|
|
963
|
+
lines.push("```");
|
|
964
|
+
lines.push(c.official);
|
|
965
|
+
lines.push("```");
|
|
966
|
+
lines.push("");
|
|
967
|
+
lines.push("**Your version:**");
|
|
968
|
+
lines.push("```");
|
|
969
|
+
lines.push(c.user);
|
|
970
|
+
lines.push("```");
|
|
971
|
+
lines.push("");
|
|
972
|
+
}
|
|
973
|
+
lines.push("## Full official content");
|
|
974
|
+
lines.push("```");
|
|
975
|
+
lines.push(officialContent);
|
|
976
|
+
lines.push("```");
|
|
977
|
+
return lines.join("\n");
|
|
978
|
+
}
|
|
979
|
+
function ensureSkillsDir() {
|
|
980
|
+
if (!existsSync2(SKILLS_DIR)) {
|
|
981
|
+
mkdirSync(SKILLS_DIR, { recursive: true });
|
|
982
|
+
log2.info({ dir: SKILLS_DIR }, "created skills directory");
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
var cachedSkills = [];
|
|
986
|
+
function scanSkills() {
|
|
987
|
+
if (!existsSync2(SKILLS_DIR)) return [];
|
|
988
|
+
let dirs;
|
|
989
|
+
try {
|
|
990
|
+
dirs = readdirSync2(SKILLS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
991
|
+
} catch {
|
|
992
|
+
log2.warn("failed to read skills directory for scanning");
|
|
993
|
+
return [];
|
|
994
|
+
}
|
|
995
|
+
const skills = [];
|
|
996
|
+
for (const dirName of dirs) {
|
|
997
|
+
const skillMd = join2(SKILLS_DIR, dirName, "SKILL.md");
|
|
998
|
+
if (!existsSync2(skillMd)) continue;
|
|
999
|
+
try {
|
|
1000
|
+
const content = readFileSync2(skillMd, "utf-8");
|
|
1001
|
+
const parsed = parseFrontmatter(content);
|
|
1002
|
+
if (parsed.name) {
|
|
1003
|
+
skills.push({
|
|
1004
|
+
name: parsed.name,
|
|
1005
|
+
description: parsed.description || dirName
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
log2.debug({ skill: dirName, err }, "failed to parse SKILL.md frontmatter");
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
cachedSkills = skills;
|
|
1013
|
+
log2.info({ count: skills.length }, "scanned skills");
|
|
1014
|
+
return skills;
|
|
1015
|
+
}
|
|
1016
|
+
function getSkillList() {
|
|
1017
|
+
return cachedSkills;
|
|
1018
|
+
}
|
|
1019
|
+
function getPendingNotification() {
|
|
1020
|
+
const pendingPath = join2(CLAWNET_DIR, ".claude", "pending-evolutions.json");
|
|
1021
|
+
if (!existsSync2(pendingPath)) return null;
|
|
1022
|
+
try {
|
|
1023
|
+
const raw = JSON.parse(readFileSync2(pendingPath, "utf-8"));
|
|
1024
|
+
const pending = Array.isArray(raw?.pending) ? raw.pending : [];
|
|
1025
|
+
if (pending.length === 0) return null;
|
|
1026
|
+
const lines = pending.map(
|
|
1027
|
+
(p) => `- [${p.signal ?? "?"}] ${p.skillName ?? "?"}: ${p.problem ?? ""} (confidence: ${p.confidence ?? "?"})`
|
|
1028
|
+
);
|
|
1029
|
+
const text = `\u6709 ${pending.length} \u6761 Skill \u8FDB\u5316\u63D0\u6848\u5F85\u5BA1\u6838\uFF1A
|
|
1030
|
+
${lines.join("\n")}
|
|
1031
|
+
\u4F7F\u7528 skill_pending_list \u67E5\u770B\u8BE6\u60C5\uFF0Cskill_pending_approve / skill_pending_reject \u5904\u7406\u3002`;
|
|
1032
|
+
return { count: pending.length, text };
|
|
1033
|
+
} catch (err) {
|
|
1034
|
+
log2.debug({ err }, "failed to read pending-evolutions.json");
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function parseFrontmatter(content) {
|
|
1039
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
1040
|
+
if (!match) return { name: "", description: "" };
|
|
1041
|
+
const yaml = match[1];
|
|
1042
|
+
const nameMatch = yaml.match(/^name:\s*(.+)$/m);
|
|
1043
|
+
const descMatch = yaml.match(/^description:\s*(.+)$/m);
|
|
1044
|
+
const strip = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
1045
|
+
return {
|
|
1046
|
+
name: strip(nameMatch?.[1] ?? ""),
|
|
1047
|
+
description: strip(descMatch?.[1] ?? "")
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
function ensureMcpConfig() {
|
|
1051
|
+
if (existsSync2(MCP_CONFIG_PATH)) {
|
|
1052
|
+
log2.debug("mcp.json already exists, skipping");
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
let mcpServerPath;
|
|
1056
|
+
try {
|
|
1057
|
+
const req = createRequire(import.meta.url);
|
|
1058
|
+
const mcpPkgDir = dirname(req.resolve("@mclawnet/mcp-server/package.json"));
|
|
1059
|
+
mcpServerPath = join2(mcpPkgDir, "dist", "server.js");
|
|
1060
|
+
} catch {
|
|
1061
|
+
log2.warn("could not resolve @mclawnet/mcp-server package path, skipping mcp.json generation");
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
const config = {
|
|
1065
|
+
mcpServers: {
|
|
1066
|
+
"clawnet-mcp": {
|
|
1067
|
+
command: "node",
|
|
1068
|
+
args: [mcpServerPath]
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
try {
|
|
1073
|
+
mkdirSync(CLAWNET_DIR, { recursive: true });
|
|
1074
|
+
writeFileSync(MCP_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
1075
|
+
log2.info({ path: MCP_CONFIG_PATH }, "generated default mcp.json");
|
|
1076
|
+
} catch (err) {
|
|
1077
|
+
log2.warn({ err }, "failed to generate mcp.json");
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/session-manager.ts
|
|
1082
|
+
var log3 = createLogger3({ module: "agent/session-manager" });
|
|
737
1083
|
var DEFAULT_MAX_PROCESSES = 10;
|
|
738
1084
|
var MAX_PROCESSES = Number(process.env.CLAWNET_MAX_PROCESSES) || DEFAULT_MAX_PROCESSES;
|
|
739
1085
|
var SessionManager = class {
|
|
740
1086
|
sessions = /* @__PURE__ */ new Map();
|
|
741
1087
|
conversationBuffer = /* @__PURE__ */ new Map();
|
|
1088
|
+
// Sessions whose abort is in flight. Treated as not-present by hasSession /
|
|
1089
|
+
// isHealthy so a racing claude.execute (e.g., right after force_restart) is
|
|
1090
|
+
// correctly routed to the "spawn new + --resume" branch instead of trying
|
|
1091
|
+
// to write to a process we are about to kill.
|
|
1092
|
+
aborting = /* @__PURE__ */ new Set();
|
|
742
1093
|
adapter;
|
|
743
1094
|
onOutput;
|
|
744
1095
|
onTurnComplete;
|
|
745
1096
|
onSessionError;
|
|
1097
|
+
onSessionStarted;
|
|
746
1098
|
onBeforeClose;
|
|
747
1099
|
constructor(options) {
|
|
748
1100
|
this.adapter = options.adapter;
|
|
749
1101
|
this.onOutput = options.onOutput;
|
|
750
1102
|
this.onTurnComplete = options.onTurnComplete;
|
|
751
1103
|
this.onSessionError = options.onSessionError;
|
|
1104
|
+
this.onSessionStarted = options.onSessionStarted;
|
|
752
1105
|
this.onBeforeClose = options.onBeforeClose;
|
|
753
1106
|
}
|
|
754
1107
|
async createSession(options) {
|
|
@@ -767,11 +1120,27 @@ var SessionManager = class {
|
|
|
767
1120
|
options.systemPrompt = options.systemPrompt ? `${memorySection}${roleHint}
|
|
768
1121
|
|
|
769
1122
|
${options.systemPrompt}` : `${memorySection}${roleHint}`;
|
|
770
|
-
|
|
1123
|
+
log3.debug({ roleId: options.roleId, sessionId: options.sessionId }, "memory prompt + roleId hint injected");
|
|
771
1124
|
} catch (err) {
|
|
772
|
-
|
|
1125
|
+
log3.warn({ err, roleId: options.roleId }, "failed to build memory section, proceeding without");
|
|
773
1126
|
}
|
|
774
1127
|
}
|
|
1128
|
+
try {
|
|
1129
|
+
const notice = getPendingNotification();
|
|
1130
|
+
if (notice) {
|
|
1131
|
+
const noticeBlock = `
|
|
1132
|
+
|
|
1133
|
+
[Skill \u8FDB\u5316\u63D0\u6848]
|
|
1134
|
+
${notice.text}`;
|
|
1135
|
+
options.systemPrompt = options.systemPrompt ? `${options.systemPrompt}${noticeBlock}` : noticeBlock.trimStart();
|
|
1136
|
+
log3.debug(
|
|
1137
|
+
{ sessionId: options.sessionId, pending: notice.count },
|
|
1138
|
+
"pending skill evolution notice injected"
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
} catch (err) {
|
|
1142
|
+
log3.debug({ err }, "failed to inject pending notification");
|
|
1143
|
+
}
|
|
775
1144
|
try {
|
|
776
1145
|
const process2 = await this.adapter.spawn(options);
|
|
777
1146
|
this.sessions.set(options.sessionId, process2);
|
|
@@ -793,6 +1162,29 @@ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
|
|
|
793
1162
|
this.adapter.onError?.(process2, (error) => {
|
|
794
1163
|
this.onSessionError(options.sessionId, error.message);
|
|
795
1164
|
});
|
|
1165
|
+
if (this.onSessionStarted) {
|
|
1166
|
+
this.adapter.onSessionStarted?.(process2, (info) => {
|
|
1167
|
+
if (this.aborting.has(options.sessionId)) {
|
|
1168
|
+
log3.debug(
|
|
1169
|
+
{ sessionId: options.sessionId },
|
|
1170
|
+
"suppressing late session_started \u2014 session is aborting"
|
|
1171
|
+
);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
if (this.sessions.get(options.sessionId) !== process2) {
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
this.onSessionStarted(options.sessionId, info);
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
this.adapter.onExit?.(process2, (code) => {
|
|
1181
|
+
if (this.sessions.get(options.sessionId) === process2) {
|
|
1182
|
+
this.sessions.delete(options.sessionId);
|
|
1183
|
+
this.conversationBuffer.delete(options.sessionId);
|
|
1184
|
+
log3.warn({ sessionId: options.sessionId, exitCode: code }, "backend process exited unexpectedly, evicted from session map");
|
|
1185
|
+
this.onSessionError(options.sessionId, `backend process exited (code=${code ?? "null"})`);
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
796
1188
|
return process2.id;
|
|
797
1189
|
} catch (err) {
|
|
798
1190
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -806,14 +1198,23 @@ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
|
|
|
806
1198
|
this.onSessionError(sessionId, `No active session: ${sessionId}`);
|
|
807
1199
|
return;
|
|
808
1200
|
}
|
|
1201
|
+
log3.debug(
|
|
1202
|
+
{ sessionId, ...previewFields2(input) },
|
|
1203
|
+
"sendInput \u2192 backend stdin"
|
|
1204
|
+
);
|
|
809
1205
|
this.adapter.send(process2, input);
|
|
810
1206
|
}
|
|
811
1207
|
async abortSession(sessionId) {
|
|
812
1208
|
const process2 = this.sessions.get(sessionId);
|
|
813
1209
|
if (!process2) return;
|
|
1210
|
+
this.aborting.add(sessionId);
|
|
814
1211
|
this.conversationBuffer.delete(sessionId);
|
|
815
1212
|
this.sessions.delete(sessionId);
|
|
816
|
-
|
|
1213
|
+
try {
|
|
1214
|
+
await this.adapter.stop(process2);
|
|
1215
|
+
} finally {
|
|
1216
|
+
this.aborting.delete(sessionId);
|
|
1217
|
+
}
|
|
817
1218
|
}
|
|
818
1219
|
async closeSession(sessionId) {
|
|
819
1220
|
const process2 = this.sessions.get(sessionId);
|
|
@@ -844,7 +1245,18 @@ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
|
|
|
844
1245
|
await Promise.all(promises);
|
|
845
1246
|
}
|
|
846
1247
|
hasSession(sessionId) {
|
|
847
|
-
return this.sessions.has(sessionId);
|
|
1248
|
+
return this.sessions.has(sessionId) && !this.aborting.has(sessionId);
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Stricter than hasSession(): also requires the underlying process to still
|
|
1252
|
+
* be alive (not exited, stdin still writable). Callers in the reuse path
|
|
1253
|
+
* must use this to avoid sending input into a dead pipe — if it returns
|
|
1254
|
+
* false they should fall back to `--resume` against db's claudeSessionId.
|
|
1255
|
+
*/
|
|
1256
|
+
isHealthy(sessionId) {
|
|
1257
|
+
if (this.aborting.has(sessionId)) return false;
|
|
1258
|
+
const process2 = this.sessions.get(sessionId);
|
|
1259
|
+
return !!process2 && process2.isAlive();
|
|
848
1260
|
}
|
|
849
1261
|
get activeSessionCount() {
|
|
850
1262
|
return this.sessions.size;
|
|
@@ -857,138 +1269,6 @@ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
|
|
|
857
1269
|
// src/start.ts
|
|
858
1270
|
import { SwarmCoordinator, initRoles } from "@mclawnet/swarm";
|
|
859
1271
|
|
|
860
|
-
// src/skill-loader.ts
|
|
861
|
-
import { existsSync as existsSync2, mkdirSync, copyFileSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
862
|
-
import { join as join2, dirname } from "path";
|
|
863
|
-
import { homedir as homedir2 } from "os";
|
|
864
|
-
import { createRequire } from "module";
|
|
865
|
-
import { fileURLToPath } from "url";
|
|
866
|
-
import { createLogger as createLogger3 } from "@mclawnet/logger";
|
|
867
|
-
var log3 = createLogger3({ module: "agent/skill-loader" });
|
|
868
|
-
var CLAWNET_DIR = join2(homedir2(), ".clawnet");
|
|
869
|
-
var SKILLS_DIR = join2(CLAWNET_DIR, ".claude", "skills");
|
|
870
|
-
var MCP_CONFIG_PATH = join2(CLAWNET_DIR, "mcp.json");
|
|
871
|
-
async function initSkills() {
|
|
872
|
-
ensureSkillsDir();
|
|
873
|
-
copyBuiltinSkills();
|
|
874
|
-
ensureMcpConfig();
|
|
875
|
-
scanSkills();
|
|
876
|
-
}
|
|
877
|
-
function ensureSkillsDir() {
|
|
878
|
-
if (!existsSync2(SKILLS_DIR)) {
|
|
879
|
-
mkdirSync(SKILLS_DIR, { recursive: true });
|
|
880
|
-
log3.info({ dir: SKILLS_DIR }, "created skills directory");
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
function copyBuiltinSkills() {
|
|
884
|
-
const thisFile = fileURLToPath(import.meta.url);
|
|
885
|
-
const srcDir = join2(dirname(thisFile), "..", "skills");
|
|
886
|
-
if (!existsSync2(srcDir)) {
|
|
887
|
-
log3.debug({ srcDir }, "no built-in skills directory found, skipping");
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
890
|
-
let entries;
|
|
891
|
-
try {
|
|
892
|
-
entries = readdirSync2(srcDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
893
|
-
} catch {
|
|
894
|
-
log3.warn({ srcDir }, "failed to read built-in skills directory");
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
|
-
for (const skillName of entries) {
|
|
898
|
-
const srcSkillMd = join2(srcDir, skillName, "SKILL.md");
|
|
899
|
-
const destDir = join2(SKILLS_DIR, skillName);
|
|
900
|
-
const destSkillMd = join2(destDir, "SKILL.md");
|
|
901
|
-
if (!existsSync2(srcSkillMd)) continue;
|
|
902
|
-
if (existsSync2(destSkillMd)) {
|
|
903
|
-
log3.debug({ skill: skillName }, "skill already exists, skipping");
|
|
904
|
-
continue;
|
|
905
|
-
}
|
|
906
|
-
try {
|
|
907
|
-
mkdirSync(destDir, { recursive: true });
|
|
908
|
-
copyFileSync(srcSkillMd, destSkillMd);
|
|
909
|
-
log3.info({ skill: skillName }, "copied built-in skill");
|
|
910
|
-
} catch (err) {
|
|
911
|
-
log3.warn({ skill: skillName, err }, "failed to copy built-in skill");
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
var cachedSkills = [];
|
|
916
|
-
function scanSkills() {
|
|
917
|
-
if (!existsSync2(SKILLS_DIR)) return [];
|
|
918
|
-
let dirs;
|
|
919
|
-
try {
|
|
920
|
-
dirs = readdirSync2(SKILLS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
921
|
-
} catch {
|
|
922
|
-
log3.warn("failed to read skills directory for scanning");
|
|
923
|
-
return [];
|
|
924
|
-
}
|
|
925
|
-
const skills = [];
|
|
926
|
-
for (const dirName of dirs) {
|
|
927
|
-
const skillMd = join2(SKILLS_DIR, dirName, "SKILL.md");
|
|
928
|
-
if (!existsSync2(skillMd)) continue;
|
|
929
|
-
try {
|
|
930
|
-
const content = readFileSync2(skillMd, "utf-8");
|
|
931
|
-
const parsed = parseFrontmatter(content);
|
|
932
|
-
if (parsed.name) {
|
|
933
|
-
skills.push({
|
|
934
|
-
name: parsed.name,
|
|
935
|
-
description: parsed.description || dirName
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
} catch (err) {
|
|
939
|
-
log3.debug({ skill: dirName, err }, "failed to parse SKILL.md frontmatter");
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
cachedSkills = skills;
|
|
943
|
-
log3.info({ count: skills.length }, "scanned skills");
|
|
944
|
-
return skills;
|
|
945
|
-
}
|
|
946
|
-
function getSkillList() {
|
|
947
|
-
return cachedSkills;
|
|
948
|
-
}
|
|
949
|
-
function parseFrontmatter(content) {
|
|
950
|
-
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
951
|
-
if (!match) return { name: "", description: "" };
|
|
952
|
-
const yaml = match[1];
|
|
953
|
-
const nameMatch = yaml.match(/^name:\s*(.+)$/m);
|
|
954
|
-
const descMatch = yaml.match(/^description:\s*(.+)$/m);
|
|
955
|
-
const strip = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
956
|
-
return {
|
|
957
|
-
name: strip(nameMatch?.[1] ?? ""),
|
|
958
|
-
description: strip(descMatch?.[1] ?? "")
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
function ensureMcpConfig() {
|
|
962
|
-
if (existsSync2(MCP_CONFIG_PATH)) {
|
|
963
|
-
log3.debug("mcp.json already exists, skipping");
|
|
964
|
-
return;
|
|
965
|
-
}
|
|
966
|
-
let mcpServerPath;
|
|
967
|
-
try {
|
|
968
|
-
const req = createRequire(import.meta.url);
|
|
969
|
-
const memoryPkgDir = dirname(req.resolve("@mclawnet/memory/package.json"));
|
|
970
|
-
mcpServerPath = join2(memoryPkgDir, "dist", "mcp", "server.js");
|
|
971
|
-
} catch {
|
|
972
|
-
log3.warn("could not resolve @mclawnet/memory package path, skipping mcp.json generation");
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
const config = {
|
|
976
|
-
mcpServers: {
|
|
977
|
-
"clawnet-memory": {
|
|
978
|
-
command: "node",
|
|
979
|
-
args: [mcpServerPath]
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
};
|
|
983
|
-
try {
|
|
984
|
-
mkdirSync(CLAWNET_DIR, { recursive: true });
|
|
985
|
-
writeFileSync(MCP_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
986
|
-
log3.info({ path: MCP_CONFIG_PATH }, "generated default mcp.json");
|
|
987
|
-
} catch (err) {
|
|
988
|
-
log3.warn({ err }, "failed to generate mcp.json");
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
1272
|
// src/brain-bridge.ts
|
|
993
1273
|
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync3 } from "fs";
|
|
994
1274
|
import { join as join3 } from "path";
|
|
@@ -1372,6 +1652,19 @@ var FsBridge = class {
|
|
|
1372
1652
|
|
|
1373
1653
|
// src/start.ts
|
|
1374
1654
|
import { createLogger as createLogger6 } from "@mclawnet/logger";
|
|
1655
|
+
import {
|
|
1656
|
+
initDatabase,
|
|
1657
|
+
MemoryStore,
|
|
1658
|
+
EmbeddingService,
|
|
1659
|
+
createEmbeddingProviders,
|
|
1660
|
+
distillConversation
|
|
1661
|
+
} from "@mclawnet/memory";
|
|
1662
|
+
import {
|
|
1663
|
+
SkillStore,
|
|
1664
|
+
EvolutionPipeline,
|
|
1665
|
+
AccumulationScanner,
|
|
1666
|
+
triggerFromAccumulation
|
|
1667
|
+
} from "@mclawnet/skill-manager";
|
|
1375
1668
|
var log6 = createLogger6({ module: "agent" });
|
|
1376
1669
|
async function startAgent(options) {
|
|
1377
1670
|
const config = loadConfig(options.config);
|
|
@@ -1403,6 +1696,58 @@ async function startAgent(options) {
|
|
|
1403
1696
|
}
|
|
1404
1697
|
});
|
|
1405
1698
|
let swarmCoordinator;
|
|
1699
|
+
const clawnetDir = process.env.CLAWNET_DIR ?? join4(homedir3(), ".clawnet");
|
|
1700
|
+
const dbPath = process.env.CLAWNET_MEMORY_DB ?? join4(clawnetDir, "memory.db");
|
|
1701
|
+
let memoryStore = null;
|
|
1702
|
+
let embeddingService = null;
|
|
1703
|
+
let evolutionPipeline = null;
|
|
1704
|
+
let skillStore = null;
|
|
1705
|
+
let memoryDb = null;
|
|
1706
|
+
try {
|
|
1707
|
+
memoryDb = initDatabase(dbPath);
|
|
1708
|
+
memoryStore = new MemoryStore(memoryDb);
|
|
1709
|
+
embeddingService = new EmbeddingService(memoryDb, createEmbeddingProviders());
|
|
1710
|
+
skillStore = new SkillStore(clawnetDir);
|
|
1711
|
+
evolutionPipeline = new EvolutionPipeline(clawnetDir);
|
|
1712
|
+
} catch (err) {
|
|
1713
|
+
log6.warn({ err }, "failed to init memory/skill infra; distillation disabled");
|
|
1714
|
+
}
|
|
1715
|
+
const onBeforeClose = async (sessionId, messages) => {
|
|
1716
|
+
if (messages.length === 0) return;
|
|
1717
|
+
if (!memoryStore || !embeddingService) return;
|
|
1718
|
+
try {
|
|
1719
|
+
await distillConversation(
|
|
1720
|
+
messages,
|
|
1721
|
+
"role-__assistant__",
|
|
1722
|
+
memoryStore,
|
|
1723
|
+
embeddingService
|
|
1724
|
+
);
|
|
1725
|
+
} catch (err) {
|
|
1726
|
+
log6.warn({ err, sessionId }, "distillation failed (non-fatal)");
|
|
1727
|
+
}
|
|
1728
|
+
try {
|
|
1729
|
+
if (!skillStore || !evolutionPipeline) return;
|
|
1730
|
+
const skills = skillStore.scan();
|
|
1731
|
+
if (skills.length === 0) return;
|
|
1732
|
+
const recent = memoryStore.getMemoriesByRole("role-__assistant__", "working");
|
|
1733
|
+
const refs = recent.map((m) => ({
|
|
1734
|
+
id: m.id,
|
|
1735
|
+
type: m.type,
|
|
1736
|
+
content: m.content,
|
|
1737
|
+
domain: m.domain
|
|
1738
|
+
}));
|
|
1739
|
+
const scanner = new AccumulationScanner();
|
|
1740
|
+
const signals = scanner.scan(
|
|
1741
|
+
refs,
|
|
1742
|
+
skills.map((s) => s.name)
|
|
1743
|
+
);
|
|
1744
|
+
if (signals.length > 0) {
|
|
1745
|
+
await triggerFromAccumulation(signals, evolutionPipeline);
|
|
1746
|
+
}
|
|
1747
|
+
} catch (err) {
|
|
1748
|
+
log6.warn({ err, sessionId }, "accumulation scan failed (non-fatal)");
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1406
1751
|
const sessionManager = new SessionManager({
|
|
1407
1752
|
adapter: options.adapter,
|
|
1408
1753
|
onOutput: (sessionId, data) => {
|
|
@@ -1424,13 +1769,21 @@ async function startAgent(options) {
|
|
|
1424
1769
|
contextUsage: info.contextUsage
|
|
1425
1770
|
});
|
|
1426
1771
|
},
|
|
1772
|
+
onSessionStarted: (sessionId, info) => {
|
|
1773
|
+
hub.send({
|
|
1774
|
+
type: "claude.session_started",
|
|
1775
|
+
sessionId,
|
|
1776
|
+
claudeSessionId: info.claudeSessionId
|
|
1777
|
+
});
|
|
1778
|
+
},
|
|
1427
1779
|
onSessionError: (sessionId, error) => {
|
|
1428
1780
|
hub.send({
|
|
1429
1781
|
type: "session.error",
|
|
1430
1782
|
sessionId,
|
|
1431
1783
|
error
|
|
1432
1784
|
});
|
|
1433
|
-
}
|
|
1785
|
+
},
|
|
1786
|
+
onBeforeClose
|
|
1434
1787
|
});
|
|
1435
1788
|
swarmCoordinator = new SwarmCoordinator(sessionManager, hub);
|
|
1436
1789
|
hub.setSessionManager(sessionManager);
|
|
@@ -1441,6 +1794,13 @@ async function startAgent(options) {
|
|
|
1441
1794
|
log6.info("shutting down");
|
|
1442
1795
|
await sessionManager.closeAll();
|
|
1443
1796
|
hub.destroy();
|
|
1797
|
+
if (memoryDb) {
|
|
1798
|
+
try {
|
|
1799
|
+
memoryDb.close();
|
|
1800
|
+
} catch (err) {
|
|
1801
|
+
log6.warn({ err }, "failed to close memory db");
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1444
1804
|
process.exit(0);
|
|
1445
1805
|
};
|
|
1446
1806
|
process.on("SIGINT", shutdown);
|
|
@@ -1456,4 +1816,4 @@ export {
|
|
|
1456
1816
|
FsBridge,
|
|
1457
1817
|
startAgent
|
|
1458
1818
|
};
|
|
1459
|
-
//# sourceMappingURL=chunk-
|
|
1819
|
+
//# sourceMappingURL=chunk-MSDIRBXF.js.map
|