@goondocks/myco 0.5.1 → 0.6.1
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +14 -11
- package/dist/{chunk-JJL6AMDA.js → chunk-24DOZEUJ.js} +255 -6
- package/dist/chunk-24DOZEUJ.js.map +1 -0
- package/dist/{chunk-ZWUFTOG3.js → chunk-2GSX3BK2.js} +4 -4
- package/dist/{chunk-FIRMTYFH.js → chunk-2YBUL3IL.js} +4 -37
- package/dist/chunk-2YBUL3IL.js.map +1 -0
- package/dist/{chunk-HL2S5QZG.js → chunk-2ZBB3MQT.js} +319 -40
- package/dist/chunk-2ZBB3MQT.js.map +1 -0
- package/dist/{chunk-HJG7Z6SJ.js → chunk-3EM23DMD.js} +2 -2
- package/dist/{chunk-XQXXF6MU.js → chunk-4RMSHZE4.js} +12 -1
- package/dist/{chunk-XQXXF6MU.js.map → chunk-4RMSHZE4.js.map} +1 -1
- package/dist/{chunk-T7OC6GH5.js → chunk-5FNZ7AMX.js} +2 -2
- package/dist/{chunk-X6TKHO22.js → chunk-5QWZT4AB.js} +2 -2
- package/dist/{chunk-B6WVNDA5.js → chunk-6BSDCZ5Q.js} +8 -2
- package/dist/{chunk-B6WVNDA5.js.map → chunk-6BSDCZ5Q.js.map} +1 -1
- package/dist/{chunk-R6LQT3U7.js → chunk-B5UZSHQV.js} +8 -12
- package/dist/{chunk-R6LQT3U7.js.map → chunk-B5UZSHQV.js.map} +1 -1
- package/dist/{chunk-7KQB22DP.js → chunk-E7OBRBCQ.js} +2 -2
- package/dist/{chunk-RCV2I4AI.js → chunk-GDYYJTTT.js} +5 -3
- package/dist/{chunk-RCV2I4AI.js.map → chunk-GDYYJTTT.js.map} +1 -1
- package/dist/{chunk-MIU3DKLN.js → chunk-GNR3QAER.js} +2 -2
- package/dist/{chunk-BMJX2IDQ.js → chunk-H7PRCVGQ.js} +2 -2
- package/dist/{chunk-6LTNFMXO.js → chunk-KC7ENQTN.js} +2 -2
- package/dist/chunk-KUMVJIJW.js +117 -0
- package/dist/chunk-KUMVJIJW.js.map +1 -0
- package/dist/{chunk-ND4VK6C7.js → chunk-L25U7PIG.js} +2 -2
- package/dist/{chunk-6UJWI4IW.js → chunk-MQSYSQ6T.js} +7 -5
- package/dist/{chunk-6UJWI4IW.js.map → chunk-MQSYSQ6T.js.map} +1 -1
- package/dist/{chunk-TBRZAJ7W.js → chunk-P3WO3N3I.js} +11 -3
- package/dist/chunk-P3WO3N3I.js.map +1 -0
- package/dist/{chunk-JI6M2L2W.js → chunk-QGJ2ZIUZ.js} +7 -4
- package/dist/chunk-QGJ2ZIUZ.js.map +1 -0
- package/dist/{chunk-5EZ7QF6J.js → chunk-QLUE3BUL.js} +66 -1
- package/dist/chunk-QLUE3BUL.js.map +1 -0
- package/dist/{chunk-AK6GNLPV.js → chunk-TWSTAVLO.js} +17 -1
- package/dist/{chunk-AK6GNLPV.js.map → chunk-TWSTAVLO.js.map} +1 -1
- package/dist/{chunk-FIA5NTRH.js → chunk-UVGAVYWZ.js} +11 -13
- package/dist/chunk-UVGAVYWZ.js.map +1 -0
- package/dist/{chunk-UKWO26VI.js → chunk-YTANWAGE.js} +2 -2
- package/dist/chunk-ZMYNRTTD.js +64 -0
- package/dist/chunk-ZMYNRTTD.js.map +1 -0
- package/dist/{cli-BLYNNKGJ.js → cli-K7SUTP7A.js} +22 -22
- package/dist/{client-5GB4WVXE.js → client-YJMNTITQ.js} +5 -5
- package/dist/{config-5FGLQGCW.js → config-G5GGT5A6.js} +3 -3
- package/dist/curate-6T5NKVXK.js +80 -0
- package/dist/curate-6T5NKVXK.js.map +1 -0
- package/dist/{detect-providers-BIHYFK5M.js → detect-providers-S3M5TAMW.js} +3 -3
- package/dist/{digest-7NKYXM6G.js → digest-O35VHYFP.js} +31 -40
- package/dist/digest-O35VHYFP.js.map +1 -0
- package/dist/{init-HPQ77WWF.js → init-TFLSATB3.js} +9 -11
- package/dist/init-TFLSATB3.js.map +1 -0
- package/dist/{logs-BSTBZHDR.js → logs-IENORIYR.js} +3 -3
- package/dist/{main-NFQ4II75.js → main-JEUQS3BY.js} +1218 -294
- package/dist/main-JEUQS3BY.js.map +1 -0
- package/dist/rebuild-7SH5GSNX.js +66 -0
- package/dist/rebuild-7SH5GSNX.js.map +1 -0
- package/dist/{reprocess-ZL4HKTSC.js → reprocess-Q4YH2ZBK.js} +20 -22
- package/dist/{reprocess-ZL4HKTSC.js.map → reprocess-Q4YH2ZBK.js.map} +1 -1
- package/dist/{restart-FYW662DR.js → restart-NLJLB52D.js} +7 -6
- package/dist/{restart-FYW662DR.js.map → restart-NLJLB52D.js.map} +1 -1
- package/dist/{search-E5JQMTXV.js → search-2BVRF54H.js} +10 -10
- package/dist/{server-TV3D35HZ.js → server-4AMZNP4F.js} +51 -97
- package/dist/{server-TV3D35HZ.js.map → server-4AMZNP4F.js.map} +1 -1
- package/dist/{session-QF6MILAC.js → session-F326AWCH.js} +2 -2
- package/dist/{session-start-5MFEOVQ5.js → session-start-AZAF3DTE.js} +10 -10
- package/dist/setup-digest-YLZZGSSR.js +15 -0
- package/dist/setup-llm-JOXBSLXC.js +15 -0
- package/dist/src/cli.js +4 -4
- package/dist/src/daemon/main.js +4 -4
- package/dist/src/hooks/post-tool-use.js +5 -5
- package/dist/src/hooks/session-end.js +5 -5
- package/dist/src/hooks/session-start.js +4 -4
- package/dist/src/hooks/stop.js +7 -7
- package/dist/src/hooks/user-prompt-submit.js +5 -5
- package/dist/src/mcp/server.js +4 -4
- package/dist/src/prompts/consolidation.md +46 -0
- package/dist/src/templates/portal.md +5 -0
- package/dist/stats-MKDIZFIQ.js +58 -0
- package/dist/stats-MKDIZFIQ.js.map +1 -0
- package/dist/templates-XPRBOWCE.js +38 -0
- package/dist/templates-XPRBOWCE.js.map +1 -0
- package/dist/ui/assets/index-D37IoDXS.css +1 -0
- package/dist/ui/assets/index-DA61Ial2.js +289 -0
- package/dist/ui/favicon.svg +11 -0
- package/dist/ui/fonts/GeistMono-LICENSE.txt +92 -0
- package/dist/ui/fonts/GeistMono-Variable.woff2 +0 -0
- package/dist/ui/index.html +14 -0
- package/dist/{verify-RACBFT2P.js → verify-7DW7LAND.js} +6 -6
- package/dist/{version-HJTVNPOO.js → version-RQLD7VBP.js} +4 -4
- package/package.json +3 -2
- package/dist/chunk-2AMAOSRF.js +0 -105
- package/dist/chunk-2AMAOSRF.js.map +0 -1
- package/dist/chunk-5EZ7QF6J.js.map +0 -1
- package/dist/chunk-FIA5NTRH.js.map +0 -1
- package/dist/chunk-FIRMTYFH.js.map +0 -1
- package/dist/chunk-HL2S5QZG.js.map +0 -1
- package/dist/chunk-IURC35BF.js +0 -49
- package/dist/chunk-IURC35BF.js.map +0 -1
- package/dist/chunk-JI6M2L2W.js.map +0 -1
- package/dist/chunk-JJL6AMDA.js.map +0 -1
- package/dist/chunk-KYL67SKZ.js +0 -150
- package/dist/chunk-KYL67SKZ.js.map +0 -1
- package/dist/chunk-TBRZAJ7W.js.map +0 -1
- package/dist/curate-S4HOYWXA.js +0 -231
- package/dist/curate-S4HOYWXA.js.map +0 -1
- package/dist/digest-7NKYXM6G.js.map +0 -1
- package/dist/init-HPQ77WWF.js.map +0 -1
- package/dist/main-NFQ4II75.js.map +0 -1
- package/dist/rebuild-KQ6G2GZM.js +0 -86
- package/dist/rebuild-KQ6G2GZM.js.map +0 -1
- package/dist/setup-digest-DZAFIBEF.js +0 -15
- package/dist/setup-llm-4BZM33YT.js +0 -15
- package/dist/stats-ZIIJ2GB3.js +0 -77
- package/dist/stats-ZIIJ2GB3.js.map +0 -1
- /package/dist/{chunk-ZWUFTOG3.js.map → chunk-2GSX3BK2.js.map} +0 -0
- /package/dist/{chunk-HJG7Z6SJ.js.map → chunk-3EM23DMD.js.map} +0 -0
- /package/dist/{chunk-T7OC6GH5.js.map → chunk-5FNZ7AMX.js.map} +0 -0
- /package/dist/{chunk-X6TKHO22.js.map → chunk-5QWZT4AB.js.map} +0 -0
- /package/dist/{chunk-7KQB22DP.js.map → chunk-E7OBRBCQ.js.map} +0 -0
- /package/dist/{chunk-MIU3DKLN.js.map → chunk-GNR3QAER.js.map} +0 -0
- /package/dist/{chunk-BMJX2IDQ.js.map → chunk-H7PRCVGQ.js.map} +0 -0
- /package/dist/{chunk-6LTNFMXO.js.map → chunk-KC7ENQTN.js.map} +0 -0
- /package/dist/{chunk-ND4VK6C7.js.map → chunk-L25U7PIG.js.map} +0 -0
- /package/dist/{chunk-UKWO26VI.js.map → chunk-YTANWAGE.js.map} +0 -0
- /package/dist/{cli-BLYNNKGJ.js.map → cli-K7SUTP7A.js.map} +0 -0
- /package/dist/{client-5GB4WVXE.js.map → client-YJMNTITQ.js.map} +0 -0
- /package/dist/{config-5FGLQGCW.js.map → config-G5GGT5A6.js.map} +0 -0
- /package/dist/{detect-providers-BIHYFK5M.js.map → detect-providers-S3M5TAMW.js.map} +0 -0
- /package/dist/{logs-BSTBZHDR.js.map → logs-IENORIYR.js.map} +0 -0
- /package/dist/{search-E5JQMTXV.js.map → search-2BVRF54H.js.map} +0 -0
- /package/dist/{session-QF6MILAC.js.map → session-F326AWCH.js.map} +0 -0
- /package/dist/{session-start-5MFEOVQ5.js.map → session-start-AZAF3DTE.js.map} +0 -0
- /package/dist/{setup-digest-DZAFIBEF.js.map → setup-digest-YLZZGSSR.js.map} +0 -0
- /package/dist/{setup-llm-4BZM33YT.js.map → setup-llm-JOXBSLXC.js.map} +0 -0
- /package/dist/{verify-RACBFT2P.js.map → verify-7DW7LAND.js.map} +0 -0
- /package/dist/{version-HJTVNPOO.js.map → version-RQLD7VBP.js.map} +0 -0
|
@@ -4,14 +4,24 @@ import {
|
|
|
4
4
|
TranscriptMiner,
|
|
5
5
|
extractTurnsFromBuffer,
|
|
6
6
|
writeObservationNotes
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-B5UZSHQV.js";
|
|
8
|
+
import {
|
|
9
|
+
gatherStats
|
|
10
|
+
} from "./chunk-ZMYNRTTD.js";
|
|
8
11
|
import {
|
|
9
12
|
DigestEngine,
|
|
10
|
-
Metabolism
|
|
11
|
-
|
|
13
|
+
Metabolism,
|
|
14
|
+
appendTraceRecord,
|
|
15
|
+
readLastTimestamp,
|
|
16
|
+
runCuration,
|
|
17
|
+
runDigest,
|
|
18
|
+
runRebuild
|
|
19
|
+
} from "./chunk-2ZBB3MQT.js";
|
|
20
|
+
import "./chunk-3JCXYLHD.js";
|
|
12
21
|
import {
|
|
22
|
+
consolidateSpores,
|
|
13
23
|
handleMycoContext
|
|
14
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-KUMVJIJW.js";
|
|
15
25
|
import {
|
|
16
26
|
VaultWriter,
|
|
17
27
|
bareSessionId,
|
|
@@ -19,60 +29,72 @@ import {
|
|
|
19
29
|
sessionNoteId,
|
|
20
30
|
sessionRelativePath,
|
|
21
31
|
sessionWikilink
|
|
22
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-KC7ENQTN.js";
|
|
23
33
|
import {
|
|
24
34
|
DaemonLogger
|
|
25
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-QLUE3BUL.js";
|
|
36
|
+
import {
|
|
37
|
+
VectorIndex
|
|
38
|
+
} from "./chunk-4RMSHZE4.js";
|
|
26
39
|
import {
|
|
27
|
-
checkSupersession
|
|
28
|
-
|
|
40
|
+
checkSupersession,
|
|
41
|
+
isActiveSpore
|
|
42
|
+
} from "./chunk-UVGAVYWZ.js";
|
|
29
43
|
import {
|
|
44
|
+
buildSimilarityPrompt,
|
|
45
|
+
extractNumber,
|
|
46
|
+
formatNotesForPrompt,
|
|
30
47
|
indexNote,
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
loadPrompt,
|
|
49
|
+
rebuildIndex,
|
|
50
|
+
stripReasoningTokens
|
|
51
|
+
} from "./chunk-24DOZEUJ.js";
|
|
33
52
|
import {
|
|
34
53
|
generateEmbedding
|
|
35
54
|
} from "./chunk-RGVBGTD6.js";
|
|
36
|
-
import {
|
|
37
|
-
VectorIndex
|
|
38
|
-
} from "./chunk-XQXXF6MU.js";
|
|
39
|
-
import {
|
|
40
|
-
buildSimilarityPrompt,
|
|
41
|
-
extractNumber
|
|
42
|
-
} from "./chunk-KYL67SKZ.js";
|
|
43
|
-
import "./chunk-2AMAOSRF.js";
|
|
44
55
|
import {
|
|
45
56
|
createEmbeddingProvider,
|
|
46
57
|
createLlmProvider
|
|
47
|
-
} from "./chunk-
|
|
48
|
-
import "./chunk-
|
|
58
|
+
} from "./chunk-GDYYJTTT.js";
|
|
59
|
+
import "./chunk-GNR3QAER.js";
|
|
49
60
|
import {
|
|
50
61
|
initFts
|
|
51
62
|
} from "./chunk-6FQISQNA.js";
|
|
52
63
|
import {
|
|
53
64
|
MycoIndex
|
|
54
|
-
} from "./chunk-
|
|
55
|
-
import "./chunk-
|
|
65
|
+
} from "./chunk-TWSTAVLO.js";
|
|
66
|
+
import "./chunk-2YBUL3IL.js";
|
|
67
|
+
import "./chunk-SAKJMNSR.js";
|
|
56
68
|
import {
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
LmStudioBackend,
|
|
70
|
+
OllamaBackend
|
|
71
|
+
} from "./chunk-QGJ2ZIUZ.js";
|
|
59
72
|
import {
|
|
73
|
+
CONFIG_FILENAME,
|
|
74
|
+
loadConfig,
|
|
75
|
+
saveConfig
|
|
76
|
+
} from "./chunk-P3WO3N3I.js";
|
|
77
|
+
import {
|
|
78
|
+
MycoConfigSchema,
|
|
60
79
|
external_exports,
|
|
61
80
|
require_dist
|
|
62
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-MQSYSQ6T.js";
|
|
63
82
|
import {
|
|
64
83
|
EventBuffer
|
|
65
84
|
} from "./chunk-HIN3UVOG.js";
|
|
66
85
|
import {
|
|
67
86
|
getPluginVersion
|
|
68
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-E7OBRBCQ.js";
|
|
69
88
|
import {
|
|
70
89
|
claudeCodeAdapter,
|
|
71
90
|
createPerProjectAdapter,
|
|
72
91
|
extensionForMimeType
|
|
73
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-5QWZT4AB.js";
|
|
74
93
|
import {
|
|
75
94
|
CANDIDATE_CONTENT_PREVIEW,
|
|
95
|
+
CONSOLIDATION_MAX_TOKENS,
|
|
96
|
+
CONSOLIDATION_MIN_CLUSTER_SIZE,
|
|
97
|
+
CONSOLIDATION_VECTOR_FETCH_LIMIT,
|
|
76
98
|
CONTENT_SNIPPET_CHARS,
|
|
77
99
|
CONTEXT_SESSION_PREVIEW_CHARS,
|
|
78
100
|
EMBEDDING_INPUT_LIMIT,
|
|
@@ -86,40 +108,138 @@ import {
|
|
|
86
108
|
RELATED_SPORES_LIMIT,
|
|
87
109
|
SESSION_CONTEXT_MAX_PLANS,
|
|
88
110
|
STALE_BUFFER_MAX_AGE_MS
|
|
89
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-6BSDCZ5Q.js";
|
|
90
112
|
import {
|
|
91
113
|
__toESM
|
|
92
114
|
} from "./chunk-PZUWP5VK.js";
|
|
93
115
|
|
|
94
116
|
// src/daemon/server.ts
|
|
95
117
|
import http from "http";
|
|
118
|
+
import fs2 from "fs";
|
|
119
|
+
import path2 from "path";
|
|
120
|
+
|
|
121
|
+
// src/daemon/router.ts
|
|
122
|
+
var Router = class {
|
|
123
|
+
routes = [];
|
|
124
|
+
add(method, pattern, handler) {
|
|
125
|
+
const type = pattern.includes(":") ? "param" : pattern.endsWith("/*") ? "prefix" : "exact";
|
|
126
|
+
const segments = type === "param" ? pattern.split("/") : void 0;
|
|
127
|
+
this.routes.push({ method, pattern, handler, type, segments });
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Match a request against registered routes.
|
|
131
|
+
* Priority: exact > parameterized > prefix. Within parameterized routes,
|
|
132
|
+
* first-registered wins if multiple patterns match at the same depth.
|
|
133
|
+
*/
|
|
134
|
+
match(method, rawUrl) {
|
|
135
|
+
const url = new URL(rawUrl, "http://localhost");
|
|
136
|
+
const pathname = url.pathname;
|
|
137
|
+
const query = {};
|
|
138
|
+
url.searchParams.forEach((v, k) => {
|
|
139
|
+
query[k] = v;
|
|
140
|
+
});
|
|
141
|
+
let paramMatch;
|
|
142
|
+
let prefixMatch;
|
|
143
|
+
for (const route of this.routes) {
|
|
144
|
+
if (route.method !== method) continue;
|
|
145
|
+
if (route.type === "exact" && route.pattern === pathname) {
|
|
146
|
+
return { handler: route.handler, params: {}, query, pathname };
|
|
147
|
+
}
|
|
148
|
+
if (route.type === "param" && !paramMatch && route.segments) {
|
|
149
|
+
const parts = pathname.split("/");
|
|
150
|
+
if (parts.length === route.segments.length) {
|
|
151
|
+
const params = {};
|
|
152
|
+
let matched = true;
|
|
153
|
+
for (let i = 0; i < route.segments.length; i++) {
|
|
154
|
+
if (route.segments[i].startsWith(":")) {
|
|
155
|
+
params[route.segments[i].slice(1)] = parts[i];
|
|
156
|
+
} else if (route.segments[i] !== parts[i]) {
|
|
157
|
+
matched = false;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (matched) {
|
|
162
|
+
paramMatch = { handler: route.handler, params, query, pathname };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (route.type === "prefix" && !prefixMatch) {
|
|
167
|
+
const prefix = route.pattern.slice(0, -1);
|
|
168
|
+
if (pathname.startsWith(prefix)) {
|
|
169
|
+
prefixMatch = { handler: route.handler, params: {}, query, pathname };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return paramMatch ?? prefixMatch;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/daemon/static.ts
|
|
96
178
|
import fs from "fs";
|
|
97
179
|
import path from "path";
|
|
180
|
+
var HASHED_ASSET_PREFIX = "/assets/";
|
|
181
|
+
var IMMUTABLE_CACHE = "public, max-age=31536000, immutable";
|
|
182
|
+
var NO_CACHE = "no-cache";
|
|
183
|
+
var MIME_TYPES = {
|
|
184
|
+
".html": "text/html",
|
|
185
|
+
".js": "application/javascript",
|
|
186
|
+
".css": "text/css",
|
|
187
|
+
".json": "application/json",
|
|
188
|
+
".svg": "image/svg+xml",
|
|
189
|
+
".png": "image/png",
|
|
190
|
+
".ico": "image/x-icon",
|
|
191
|
+
".woff": "font/woff",
|
|
192
|
+
".woff2": "font/woff2",
|
|
193
|
+
".ttf": "font/ttf"
|
|
194
|
+
};
|
|
195
|
+
function resolveStaticFile(uiDir, pathname) {
|
|
196
|
+
const relative3 = pathname.startsWith("/") ? pathname.slice(1) : pathname;
|
|
197
|
+
const resolved = path.resolve(uiDir, relative3 || "index.html");
|
|
198
|
+
if (!resolved.startsWith(path.resolve(uiDir))) {
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) {
|
|
202
|
+
const ext = path.extname(resolved);
|
|
203
|
+
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
204
|
+
const cacheControl = pathname.startsWith(HASHED_ASSET_PREFIX) ? IMMUTABLE_CACHE : NO_CACHE;
|
|
205
|
+
return { filePath: resolved, contentType, cacheControl };
|
|
206
|
+
}
|
|
207
|
+
const indexPath = path.join(uiDir, "index.html");
|
|
208
|
+
if (fs.existsSync(indexPath)) {
|
|
209
|
+
return { filePath: indexPath, contentType: "text/html", cacheControl: NO_CACHE };
|
|
210
|
+
}
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/daemon/server.ts
|
|
215
|
+
var DEFAULT_STATUS = 200;
|
|
98
216
|
var DaemonServer = class {
|
|
99
217
|
port = 0;
|
|
100
218
|
version;
|
|
219
|
+
uiDir;
|
|
101
220
|
server = null;
|
|
102
221
|
vaultDir;
|
|
103
222
|
logger;
|
|
104
|
-
|
|
223
|
+
router = new Router();
|
|
105
224
|
constructor(config) {
|
|
106
225
|
this.vaultDir = config.vaultDir;
|
|
107
226
|
this.logger = config.logger;
|
|
227
|
+
this.uiDir = config.uiDir ?? null;
|
|
108
228
|
this.version = getPluginVersion();
|
|
109
229
|
this.registerDefaultRoutes();
|
|
110
230
|
}
|
|
111
231
|
registerRoute(method, routePath, handler) {
|
|
112
|
-
this.
|
|
232
|
+
this.router.add(method, routePath, handler);
|
|
113
233
|
}
|
|
114
|
-
async start() {
|
|
234
|
+
async start(port = 0) {
|
|
115
235
|
return new Promise((resolve3, reject) => {
|
|
116
236
|
this.server = http.createServer((req, res) => this.handleRequest(req, res));
|
|
117
237
|
this.server.on("error", reject);
|
|
118
|
-
this.server.listen(
|
|
238
|
+
this.server.listen(port, "127.0.0.1", () => {
|
|
119
239
|
const addr = this.server.address();
|
|
120
240
|
this.port = addr.port;
|
|
121
241
|
this.writeDaemonJson();
|
|
122
|
-
this.logger.info("daemon", "Server started", { port: this.port });
|
|
242
|
+
this.logger.info("daemon", "Server started", { port: this.port, dashboard: `http://localhost:${this.port}/` });
|
|
123
243
|
resolve3();
|
|
124
244
|
});
|
|
125
245
|
});
|
|
@@ -139,40 +259,66 @@ var DaemonServer = class {
|
|
|
139
259
|
}
|
|
140
260
|
registerDefaultRoutes() {
|
|
141
261
|
this.registerRoute("GET", "/health", async () => ({
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
262
|
+
body: {
|
|
263
|
+
myco: true,
|
|
264
|
+
version: this.version,
|
|
265
|
+
pid: process.pid,
|
|
266
|
+
uptime: process.uptime()
|
|
267
|
+
}
|
|
146
268
|
}));
|
|
147
269
|
}
|
|
148
270
|
async handleRequest(req, res) {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
271
|
+
const match = this.router.match(req.method, req.url);
|
|
272
|
+
if (match) {
|
|
273
|
+
try {
|
|
274
|
+
const body = req.method === "POST" || req.method === "PUT" ? await readBody(req) : void 0;
|
|
275
|
+
const result = await match.handler({
|
|
276
|
+
body,
|
|
277
|
+
query: match.query,
|
|
278
|
+
params: match.params,
|
|
279
|
+
pathname: match.pathname
|
|
280
|
+
});
|
|
281
|
+
const status = result.status ?? DEFAULT_STATUS;
|
|
282
|
+
const headers = { "Content-Type": "application/json", ...result.headers };
|
|
283
|
+
res.writeHead(status, headers);
|
|
284
|
+
res.end(JSON.stringify(result.body));
|
|
285
|
+
} catch (error) {
|
|
286
|
+
this.logger.error("daemon", "Request handler error", {
|
|
287
|
+
path: req.url,
|
|
288
|
+
error: error.message
|
|
289
|
+
});
|
|
290
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
291
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
292
|
+
}
|
|
154
293
|
return;
|
|
155
294
|
}
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
const result =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
295
|
+
if (this.uiDir && req.method === "GET") {
|
|
296
|
+
const pathname = new URL(req.url, "http://localhost").pathname;
|
|
297
|
+
const result = resolveStaticFile(this.uiDir, pathname);
|
|
298
|
+
if (result) {
|
|
299
|
+
try {
|
|
300
|
+
const content = await fs2.promises.readFile(result.filePath);
|
|
301
|
+
res.writeHead(200, {
|
|
302
|
+
"Content-Type": result.contentType,
|
|
303
|
+
"Cache-Control": result.cacheControl
|
|
304
|
+
});
|
|
305
|
+
res.end(content);
|
|
306
|
+
} catch {
|
|
307
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
308
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
168
312
|
}
|
|
313
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
314
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
169
315
|
}
|
|
170
316
|
updateDaemonJsonSessions(sessions) {
|
|
171
|
-
const jsonPath =
|
|
317
|
+
const jsonPath = path2.join(this.vaultDir, "daemon.json");
|
|
172
318
|
try {
|
|
173
|
-
const info = JSON.parse(
|
|
319
|
+
const info = JSON.parse(fs2.readFileSync(jsonPath, "utf-8"));
|
|
174
320
|
info.sessions = sessions;
|
|
175
|
-
|
|
321
|
+
fs2.writeFileSync(jsonPath, JSON.stringify(info, null, 2));
|
|
176
322
|
} catch {
|
|
177
323
|
}
|
|
178
324
|
}
|
|
@@ -183,13 +329,13 @@ var DaemonServer = class {
|
|
|
183
329
|
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
184
330
|
sessions: []
|
|
185
331
|
};
|
|
186
|
-
const jsonPath =
|
|
187
|
-
|
|
332
|
+
const jsonPath = path2.join(this.vaultDir, "daemon.json");
|
|
333
|
+
fs2.writeFileSync(jsonPath, JSON.stringify(info, null, 2));
|
|
188
334
|
}
|
|
189
335
|
removeDaemonJson() {
|
|
190
|
-
const jsonPath =
|
|
336
|
+
const jsonPath = path2.join(this.vaultDir, "daemon.json");
|
|
191
337
|
try {
|
|
192
|
-
|
|
338
|
+
fs2.unlinkSync(jsonPath);
|
|
193
339
|
} catch {
|
|
194
340
|
}
|
|
195
341
|
}
|
|
@@ -299,8 +445,8 @@ var BatchManager = class {
|
|
|
299
445
|
};
|
|
300
446
|
|
|
301
447
|
// src/daemon/lineage.ts
|
|
302
|
-
import
|
|
303
|
-
import
|
|
448
|
+
import fs3 from "fs";
|
|
449
|
+
import path3 from "path";
|
|
304
450
|
var LINEAGE_IMMEDIATE_GAP_SECONDS = 5;
|
|
305
451
|
var LINEAGE_FALLBACK_MAX_HOURS = 24;
|
|
306
452
|
var LINEAGE_SIMILARITY_THRESHOLD = 0.7;
|
|
@@ -313,8 +459,8 @@ var LineageGraph = class {
|
|
|
313
459
|
state;
|
|
314
460
|
filePath;
|
|
315
461
|
constructor(vaultDir) {
|
|
316
|
-
this.filePath =
|
|
317
|
-
|
|
462
|
+
this.filePath = path3.join(vaultDir, "lineage.json");
|
|
463
|
+
fs3.mkdirSync(path3.dirname(this.filePath), { recursive: true });
|
|
318
464
|
this.state = this.load();
|
|
319
465
|
}
|
|
320
466
|
addLink(link) {
|
|
@@ -394,7 +540,7 @@ var LineageGraph = class {
|
|
|
394
540
|
}
|
|
395
541
|
load() {
|
|
396
542
|
try {
|
|
397
|
-
const raw = JSON.parse(
|
|
543
|
+
const raw = JSON.parse(fs3.readFileSync(this.filePath, "utf-8"));
|
|
398
544
|
const sessionArtifacts = raw.sessionArtifacts ?? raw.sessionPlans ?? {};
|
|
399
545
|
return { links: raw.links ?? [], sessionArtifacts };
|
|
400
546
|
} catch {
|
|
@@ -403,8 +549,8 @@ var LineageGraph = class {
|
|
|
403
549
|
}
|
|
404
550
|
persist() {
|
|
405
551
|
const tmp = this.filePath + ".tmp";
|
|
406
|
-
|
|
407
|
-
|
|
552
|
+
fs3.writeFileSync(tmp, JSON.stringify(this.state, null, 2));
|
|
553
|
+
fs3.renameSync(tmp, this.filePath);
|
|
408
554
|
}
|
|
409
555
|
};
|
|
410
556
|
|
|
@@ -498,7 +644,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
498
644
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
499
645
|
const statMethod = opts.lstat ? lstat : stat;
|
|
500
646
|
if (wantBigintFsStats) {
|
|
501
|
-
this._stat = (
|
|
647
|
+
this._stat = (path10) => statMethod(path10, { bigint: true });
|
|
502
648
|
} else {
|
|
503
649
|
this._stat = statMethod;
|
|
504
650
|
}
|
|
@@ -523,8 +669,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
523
669
|
const par = this.parent;
|
|
524
670
|
const fil = par && par.files;
|
|
525
671
|
if (fil && fil.length > 0) {
|
|
526
|
-
const { path:
|
|
527
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
672
|
+
const { path: path10, depth } = par;
|
|
673
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path10));
|
|
528
674
|
const awaited = await Promise.all(slice);
|
|
529
675
|
for (const entry of awaited) {
|
|
530
676
|
if (!entry)
|
|
@@ -564,20 +710,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
564
710
|
this.reading = false;
|
|
565
711
|
}
|
|
566
712
|
}
|
|
567
|
-
async _exploreDir(
|
|
713
|
+
async _exploreDir(path10, depth) {
|
|
568
714
|
let files;
|
|
569
715
|
try {
|
|
570
|
-
files = await readdir(
|
|
716
|
+
files = await readdir(path10, this._rdOptions);
|
|
571
717
|
} catch (error) {
|
|
572
718
|
this._onError(error);
|
|
573
719
|
}
|
|
574
|
-
return { files, depth, path:
|
|
720
|
+
return { files, depth, path: path10 };
|
|
575
721
|
}
|
|
576
|
-
async _formatEntry(dirent,
|
|
722
|
+
async _formatEntry(dirent, path10) {
|
|
577
723
|
let entry;
|
|
578
724
|
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
579
725
|
try {
|
|
580
|
-
const fullPath = presolve(pjoin(
|
|
726
|
+
const fullPath = presolve(pjoin(path10, basename3));
|
|
581
727
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
|
|
582
728
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
583
729
|
} catch (err) {
|
|
@@ -977,16 +1123,16 @@ var delFromSet = (main2, prop, item) => {
|
|
|
977
1123
|
};
|
|
978
1124
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
979
1125
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
980
|
-
function createFsWatchInstance(
|
|
1126
|
+
function createFsWatchInstance(path10, options, listener, errHandler, emitRaw) {
|
|
981
1127
|
const handleEvent = (rawEvent, evPath) => {
|
|
982
|
-
listener(
|
|
983
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
984
|
-
if (evPath &&
|
|
985
|
-
fsWatchBroadcast(sp.resolve(
|
|
1128
|
+
listener(path10);
|
|
1129
|
+
emitRaw(rawEvent, evPath, { watchedPath: path10 });
|
|
1130
|
+
if (evPath && path10 !== evPath) {
|
|
1131
|
+
fsWatchBroadcast(sp.resolve(path10, evPath), KEY_LISTENERS, sp.join(path10, evPath));
|
|
986
1132
|
}
|
|
987
1133
|
};
|
|
988
1134
|
try {
|
|
989
|
-
return fs_watch(
|
|
1135
|
+
return fs_watch(path10, {
|
|
990
1136
|
persistent: options.persistent
|
|
991
1137
|
}, handleEvent);
|
|
992
1138
|
} catch (error) {
|
|
@@ -1002,12 +1148,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
1002
1148
|
listener(val1, val2, val3);
|
|
1003
1149
|
});
|
|
1004
1150
|
};
|
|
1005
|
-
var setFsWatchListener = (
|
|
1151
|
+
var setFsWatchListener = (path10, fullPath, options, handlers) => {
|
|
1006
1152
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
1007
1153
|
let cont = FsWatchInstances.get(fullPath);
|
|
1008
1154
|
let watcher;
|
|
1009
1155
|
if (!options.persistent) {
|
|
1010
|
-
watcher = createFsWatchInstance(
|
|
1156
|
+
watcher = createFsWatchInstance(path10, options, listener, errHandler, rawEmitter);
|
|
1011
1157
|
if (!watcher)
|
|
1012
1158
|
return;
|
|
1013
1159
|
return watcher.close.bind(watcher);
|
|
@@ -1018,7 +1164,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
1018
1164
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
1019
1165
|
} else {
|
|
1020
1166
|
watcher = createFsWatchInstance(
|
|
1021
|
-
|
|
1167
|
+
path10,
|
|
1022
1168
|
options,
|
|
1023
1169
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
1024
1170
|
errHandler,
|
|
@@ -1033,7 +1179,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
1033
1179
|
cont.watcherUnusable = true;
|
|
1034
1180
|
if (isWindows && error.code === "EPERM") {
|
|
1035
1181
|
try {
|
|
1036
|
-
const fd = await open(
|
|
1182
|
+
const fd = await open(path10, "r");
|
|
1037
1183
|
await fd.close();
|
|
1038
1184
|
broadcastErr(error);
|
|
1039
1185
|
} catch (err) {
|
|
@@ -1064,7 +1210,7 @@ var setFsWatchListener = (path7, fullPath, options, handlers) => {
|
|
|
1064
1210
|
};
|
|
1065
1211
|
};
|
|
1066
1212
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
1067
|
-
var setFsWatchFileListener = (
|
|
1213
|
+
var setFsWatchFileListener = (path10, fullPath, options, handlers) => {
|
|
1068
1214
|
const { listener, rawEmitter } = handlers;
|
|
1069
1215
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
1070
1216
|
const copts = cont && cont.options;
|
|
@@ -1086,7 +1232,7 @@ var setFsWatchFileListener = (path7, fullPath, options, handlers) => {
|
|
|
1086
1232
|
});
|
|
1087
1233
|
const currmtime = curr.mtimeMs;
|
|
1088
1234
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
1089
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
1235
|
+
foreach(cont.listeners, (listener2) => listener2(path10, curr));
|
|
1090
1236
|
}
|
|
1091
1237
|
})
|
|
1092
1238
|
};
|
|
@@ -1116,13 +1262,13 @@ var NodeFsHandler = class {
|
|
|
1116
1262
|
* @param listener on fs change
|
|
1117
1263
|
* @returns closer for the watcher instance
|
|
1118
1264
|
*/
|
|
1119
|
-
_watchWithNodeFs(
|
|
1265
|
+
_watchWithNodeFs(path10, listener) {
|
|
1120
1266
|
const opts = this.fsw.options;
|
|
1121
|
-
const directory = sp.dirname(
|
|
1122
|
-
const basename3 = sp.basename(
|
|
1267
|
+
const directory = sp.dirname(path10);
|
|
1268
|
+
const basename3 = sp.basename(path10);
|
|
1123
1269
|
const parent = this.fsw._getWatchedDir(directory);
|
|
1124
1270
|
parent.add(basename3);
|
|
1125
|
-
const absolutePath = sp.resolve(
|
|
1271
|
+
const absolutePath = sp.resolve(path10);
|
|
1126
1272
|
const options = {
|
|
1127
1273
|
persistent: opts.persistent
|
|
1128
1274
|
};
|
|
@@ -1132,12 +1278,12 @@ var NodeFsHandler = class {
|
|
|
1132
1278
|
if (opts.usePolling) {
|
|
1133
1279
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
1134
1280
|
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
1135
|
-
closer = setFsWatchFileListener(
|
|
1281
|
+
closer = setFsWatchFileListener(path10, absolutePath, options, {
|
|
1136
1282
|
listener,
|
|
1137
1283
|
rawEmitter: this.fsw._emitRaw
|
|
1138
1284
|
});
|
|
1139
1285
|
} else {
|
|
1140
|
-
closer = setFsWatchListener(
|
|
1286
|
+
closer = setFsWatchListener(path10, absolutePath, options, {
|
|
1141
1287
|
listener,
|
|
1142
1288
|
errHandler: this._boundHandleError,
|
|
1143
1289
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -1159,7 +1305,7 @@ var NodeFsHandler = class {
|
|
|
1159
1305
|
let prevStats = stats;
|
|
1160
1306
|
if (parent.has(basename3))
|
|
1161
1307
|
return;
|
|
1162
|
-
const listener = async (
|
|
1308
|
+
const listener = async (path10, newStats) => {
|
|
1163
1309
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
1164
1310
|
return;
|
|
1165
1311
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -1173,11 +1319,11 @@ var NodeFsHandler = class {
|
|
|
1173
1319
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
1174
1320
|
}
|
|
1175
1321
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
1176
|
-
this.fsw._closeFile(
|
|
1322
|
+
this.fsw._closeFile(path10);
|
|
1177
1323
|
prevStats = newStats2;
|
|
1178
1324
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
1179
1325
|
if (closer2)
|
|
1180
|
-
this.fsw._addPathCloser(
|
|
1326
|
+
this.fsw._addPathCloser(path10, closer2);
|
|
1181
1327
|
} else {
|
|
1182
1328
|
prevStats = newStats2;
|
|
1183
1329
|
}
|
|
@@ -1209,7 +1355,7 @@ var NodeFsHandler = class {
|
|
|
1209
1355
|
* @param item basename of this item
|
|
1210
1356
|
* @returns true if no more processing is needed for this entry.
|
|
1211
1357
|
*/
|
|
1212
|
-
async _handleSymlink(entry, directory,
|
|
1358
|
+
async _handleSymlink(entry, directory, path10, item) {
|
|
1213
1359
|
if (this.fsw.closed) {
|
|
1214
1360
|
return;
|
|
1215
1361
|
}
|
|
@@ -1219,7 +1365,7 @@ var NodeFsHandler = class {
|
|
|
1219
1365
|
this.fsw._incrReadyCount();
|
|
1220
1366
|
let linkPath;
|
|
1221
1367
|
try {
|
|
1222
|
-
linkPath = await fsrealpath(
|
|
1368
|
+
linkPath = await fsrealpath(path10);
|
|
1223
1369
|
} catch (e) {
|
|
1224
1370
|
this.fsw._emitReady();
|
|
1225
1371
|
return true;
|
|
@@ -1229,12 +1375,12 @@ var NodeFsHandler = class {
|
|
|
1229
1375
|
if (dir.has(item)) {
|
|
1230
1376
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
1231
1377
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
1232
|
-
this.fsw._emit(EV.CHANGE,
|
|
1378
|
+
this.fsw._emit(EV.CHANGE, path10, entry.stats);
|
|
1233
1379
|
}
|
|
1234
1380
|
} else {
|
|
1235
1381
|
dir.add(item);
|
|
1236
1382
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
1237
|
-
this.fsw._emit(EV.ADD,
|
|
1383
|
+
this.fsw._emit(EV.ADD, path10, entry.stats);
|
|
1238
1384
|
}
|
|
1239
1385
|
this.fsw._emitReady();
|
|
1240
1386
|
return true;
|
|
@@ -1264,9 +1410,9 @@ var NodeFsHandler = class {
|
|
|
1264
1410
|
return;
|
|
1265
1411
|
}
|
|
1266
1412
|
const item = entry.path;
|
|
1267
|
-
let
|
|
1413
|
+
let path10 = sp.join(directory, item);
|
|
1268
1414
|
current.add(item);
|
|
1269
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
1415
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path10, item)) {
|
|
1270
1416
|
return;
|
|
1271
1417
|
}
|
|
1272
1418
|
if (this.fsw.closed) {
|
|
@@ -1275,8 +1421,8 @@ var NodeFsHandler = class {
|
|
|
1275
1421
|
}
|
|
1276
1422
|
if (item === target || !target && !previous.has(item)) {
|
|
1277
1423
|
this.fsw._incrReadyCount();
|
|
1278
|
-
|
|
1279
|
-
this._addToNodeFs(
|
|
1424
|
+
path10 = sp.join(dir, sp.relative(dir, path10));
|
|
1425
|
+
this._addToNodeFs(path10, initialAdd, wh, depth + 1);
|
|
1280
1426
|
}
|
|
1281
1427
|
}).on(EV.ERROR, this._boundHandleError);
|
|
1282
1428
|
return new Promise((resolve3, reject) => {
|
|
@@ -1345,13 +1491,13 @@ var NodeFsHandler = class {
|
|
|
1345
1491
|
* @param depth Child path actually targeted for watch
|
|
1346
1492
|
* @param target Child path actually targeted for watch
|
|
1347
1493
|
*/
|
|
1348
|
-
async _addToNodeFs(
|
|
1494
|
+
async _addToNodeFs(path10, initialAdd, priorWh, depth, target) {
|
|
1349
1495
|
const ready = this.fsw._emitReady;
|
|
1350
|
-
if (this.fsw._isIgnored(
|
|
1496
|
+
if (this.fsw._isIgnored(path10) || this.fsw.closed) {
|
|
1351
1497
|
ready();
|
|
1352
1498
|
return false;
|
|
1353
1499
|
}
|
|
1354
|
-
const wh = this.fsw._getWatchHelpers(
|
|
1500
|
+
const wh = this.fsw._getWatchHelpers(path10);
|
|
1355
1501
|
if (priorWh) {
|
|
1356
1502
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
1357
1503
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -1367,8 +1513,8 @@ var NodeFsHandler = class {
|
|
|
1367
1513
|
const follow = this.fsw.options.followSymlinks;
|
|
1368
1514
|
let closer;
|
|
1369
1515
|
if (stats.isDirectory()) {
|
|
1370
|
-
const absPath = sp.resolve(
|
|
1371
|
-
const targetPath = follow ? await fsrealpath(
|
|
1516
|
+
const absPath = sp.resolve(path10);
|
|
1517
|
+
const targetPath = follow ? await fsrealpath(path10) : path10;
|
|
1372
1518
|
if (this.fsw.closed)
|
|
1373
1519
|
return;
|
|
1374
1520
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -1378,29 +1524,29 @@ var NodeFsHandler = class {
|
|
|
1378
1524
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
1379
1525
|
}
|
|
1380
1526
|
} else if (stats.isSymbolicLink()) {
|
|
1381
|
-
const targetPath = follow ? await fsrealpath(
|
|
1527
|
+
const targetPath = follow ? await fsrealpath(path10) : path10;
|
|
1382
1528
|
if (this.fsw.closed)
|
|
1383
1529
|
return;
|
|
1384
1530
|
const parent = sp.dirname(wh.watchPath);
|
|
1385
1531
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
1386
1532
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
1387
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
1533
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path10, wh, targetPath);
|
|
1388
1534
|
if (this.fsw.closed)
|
|
1389
1535
|
return;
|
|
1390
1536
|
if (targetPath !== void 0) {
|
|
1391
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
1537
|
+
this.fsw._symlinkPaths.set(sp.resolve(path10), targetPath);
|
|
1392
1538
|
}
|
|
1393
1539
|
} else {
|
|
1394
1540
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
1395
1541
|
}
|
|
1396
1542
|
ready();
|
|
1397
1543
|
if (closer)
|
|
1398
|
-
this.fsw._addPathCloser(
|
|
1544
|
+
this.fsw._addPathCloser(path10, closer);
|
|
1399
1545
|
return false;
|
|
1400
1546
|
} catch (error) {
|
|
1401
1547
|
if (this.fsw._handleError(error)) {
|
|
1402
1548
|
ready();
|
|
1403
|
-
return
|
|
1549
|
+
return path10;
|
|
1404
1550
|
}
|
|
1405
1551
|
}
|
|
1406
1552
|
}
|
|
@@ -1443,24 +1589,24 @@ function createPattern(matcher) {
|
|
|
1443
1589
|
}
|
|
1444
1590
|
return () => false;
|
|
1445
1591
|
}
|
|
1446
|
-
function normalizePath(
|
|
1447
|
-
if (typeof
|
|
1592
|
+
function normalizePath(path10) {
|
|
1593
|
+
if (typeof path10 !== "string")
|
|
1448
1594
|
throw new Error("string expected");
|
|
1449
|
-
|
|
1450
|
-
|
|
1595
|
+
path10 = sp2.normalize(path10);
|
|
1596
|
+
path10 = path10.replace(/\\/g, "/");
|
|
1451
1597
|
let prepend = false;
|
|
1452
|
-
if (
|
|
1598
|
+
if (path10.startsWith("//"))
|
|
1453
1599
|
prepend = true;
|
|
1454
|
-
|
|
1600
|
+
path10 = path10.replace(DOUBLE_SLASH_RE, "/");
|
|
1455
1601
|
if (prepend)
|
|
1456
|
-
|
|
1457
|
-
return
|
|
1602
|
+
path10 = "/" + path10;
|
|
1603
|
+
return path10;
|
|
1458
1604
|
}
|
|
1459
1605
|
function matchPatterns(patterns, testString, stats) {
|
|
1460
|
-
const
|
|
1606
|
+
const path10 = normalizePath(testString);
|
|
1461
1607
|
for (let index = 0; index < patterns.length; index++) {
|
|
1462
1608
|
const pattern = patterns[index];
|
|
1463
|
-
if (pattern(
|
|
1609
|
+
if (pattern(path10, stats)) {
|
|
1464
1610
|
return true;
|
|
1465
1611
|
}
|
|
1466
1612
|
}
|
|
@@ -1498,19 +1644,19 @@ var toUnix = (string) => {
|
|
|
1498
1644
|
}
|
|
1499
1645
|
return str;
|
|
1500
1646
|
};
|
|
1501
|
-
var normalizePathToUnix = (
|
|
1502
|
-
var normalizeIgnored = (cwd = "") => (
|
|
1503
|
-
if (typeof
|
|
1504
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
1647
|
+
var normalizePathToUnix = (path10) => toUnix(sp2.normalize(toUnix(path10)));
|
|
1648
|
+
var normalizeIgnored = (cwd = "") => (path10) => {
|
|
1649
|
+
if (typeof path10 === "string") {
|
|
1650
|
+
return normalizePathToUnix(sp2.isAbsolute(path10) ? path10 : sp2.join(cwd, path10));
|
|
1505
1651
|
} else {
|
|
1506
|
-
return
|
|
1652
|
+
return path10;
|
|
1507
1653
|
}
|
|
1508
1654
|
};
|
|
1509
|
-
var getAbsolutePath = (
|
|
1510
|
-
if (sp2.isAbsolute(
|
|
1511
|
-
return
|
|
1655
|
+
var getAbsolutePath = (path10, cwd) => {
|
|
1656
|
+
if (sp2.isAbsolute(path10)) {
|
|
1657
|
+
return path10;
|
|
1512
1658
|
}
|
|
1513
|
-
return sp2.join(cwd,
|
|
1659
|
+
return sp2.join(cwd, path10);
|
|
1514
1660
|
};
|
|
1515
1661
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
1516
1662
|
var DirEntry = class {
|
|
@@ -1575,10 +1721,10 @@ var WatchHelper = class {
|
|
|
1575
1721
|
dirParts;
|
|
1576
1722
|
followSymlinks;
|
|
1577
1723
|
statMethod;
|
|
1578
|
-
constructor(
|
|
1724
|
+
constructor(path10, follow, fsw) {
|
|
1579
1725
|
this.fsw = fsw;
|
|
1580
|
-
const watchPath =
|
|
1581
|
-
this.path =
|
|
1726
|
+
const watchPath = path10;
|
|
1727
|
+
this.path = path10 = path10.replace(REPLACER_RE, "");
|
|
1582
1728
|
this.watchPath = watchPath;
|
|
1583
1729
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
1584
1730
|
this.dirParts = [];
|
|
@@ -1718,20 +1864,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
1718
1864
|
this._closePromise = void 0;
|
|
1719
1865
|
let paths = unifyPaths(paths_);
|
|
1720
1866
|
if (cwd) {
|
|
1721
|
-
paths = paths.map((
|
|
1722
|
-
const absPath = getAbsolutePath(
|
|
1867
|
+
paths = paths.map((path10) => {
|
|
1868
|
+
const absPath = getAbsolutePath(path10, cwd);
|
|
1723
1869
|
return absPath;
|
|
1724
1870
|
});
|
|
1725
1871
|
}
|
|
1726
|
-
paths.forEach((
|
|
1727
|
-
this._removeIgnoredPath(
|
|
1872
|
+
paths.forEach((path10) => {
|
|
1873
|
+
this._removeIgnoredPath(path10);
|
|
1728
1874
|
});
|
|
1729
1875
|
this._userIgnored = void 0;
|
|
1730
1876
|
if (!this._readyCount)
|
|
1731
1877
|
this._readyCount = 0;
|
|
1732
1878
|
this._readyCount += paths.length;
|
|
1733
|
-
Promise.all(paths.map(async (
|
|
1734
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
1879
|
+
Promise.all(paths.map(async (path10) => {
|
|
1880
|
+
const res = await this._nodeFsHandler._addToNodeFs(path10, !_internal, void 0, 0, _origAdd);
|
|
1735
1881
|
if (res)
|
|
1736
1882
|
this._emitReady();
|
|
1737
1883
|
return res;
|
|
@@ -1753,17 +1899,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
1753
1899
|
return this;
|
|
1754
1900
|
const paths = unifyPaths(paths_);
|
|
1755
1901
|
const { cwd } = this.options;
|
|
1756
|
-
paths.forEach((
|
|
1757
|
-
if (!sp2.isAbsolute(
|
|
1902
|
+
paths.forEach((path10) => {
|
|
1903
|
+
if (!sp2.isAbsolute(path10) && !this._closers.has(path10)) {
|
|
1758
1904
|
if (cwd)
|
|
1759
|
-
|
|
1760
|
-
|
|
1905
|
+
path10 = sp2.join(cwd, path10);
|
|
1906
|
+
path10 = sp2.resolve(path10);
|
|
1761
1907
|
}
|
|
1762
|
-
this._closePath(
|
|
1763
|
-
this._addIgnoredPath(
|
|
1764
|
-
if (this._watched.has(
|
|
1908
|
+
this._closePath(path10);
|
|
1909
|
+
this._addIgnoredPath(path10);
|
|
1910
|
+
if (this._watched.has(path10)) {
|
|
1765
1911
|
this._addIgnoredPath({
|
|
1766
|
-
path:
|
|
1912
|
+
path: path10,
|
|
1767
1913
|
recursive: true
|
|
1768
1914
|
});
|
|
1769
1915
|
}
|
|
@@ -1827,38 +1973,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
1827
1973
|
* @param stats arguments to be passed with event
|
|
1828
1974
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
1829
1975
|
*/
|
|
1830
|
-
async _emit(event,
|
|
1976
|
+
async _emit(event, path10, stats) {
|
|
1831
1977
|
if (this.closed)
|
|
1832
1978
|
return;
|
|
1833
1979
|
const opts = this.options;
|
|
1834
1980
|
if (isWindows)
|
|
1835
|
-
|
|
1981
|
+
path10 = sp2.normalize(path10);
|
|
1836
1982
|
if (opts.cwd)
|
|
1837
|
-
|
|
1838
|
-
const args = [
|
|
1983
|
+
path10 = sp2.relative(opts.cwd, path10);
|
|
1984
|
+
const args = [path10];
|
|
1839
1985
|
if (stats != null)
|
|
1840
1986
|
args.push(stats);
|
|
1841
1987
|
const awf = opts.awaitWriteFinish;
|
|
1842
1988
|
let pw;
|
|
1843
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
1989
|
+
if (awf && (pw = this._pendingWrites.get(path10))) {
|
|
1844
1990
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
1845
1991
|
return this;
|
|
1846
1992
|
}
|
|
1847
1993
|
if (opts.atomic) {
|
|
1848
1994
|
if (event === EVENTS.UNLINK) {
|
|
1849
|
-
this._pendingUnlinks.set(
|
|
1995
|
+
this._pendingUnlinks.set(path10, [event, ...args]);
|
|
1850
1996
|
setTimeout(() => {
|
|
1851
|
-
this._pendingUnlinks.forEach((entry,
|
|
1997
|
+
this._pendingUnlinks.forEach((entry, path11) => {
|
|
1852
1998
|
this.emit(...entry);
|
|
1853
1999
|
this.emit(EVENTS.ALL, ...entry);
|
|
1854
|
-
this._pendingUnlinks.delete(
|
|
2000
|
+
this._pendingUnlinks.delete(path11);
|
|
1855
2001
|
});
|
|
1856
2002
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
1857
2003
|
return this;
|
|
1858
2004
|
}
|
|
1859
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
2005
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path10)) {
|
|
1860
2006
|
event = EVENTS.CHANGE;
|
|
1861
|
-
this._pendingUnlinks.delete(
|
|
2007
|
+
this._pendingUnlinks.delete(path10);
|
|
1862
2008
|
}
|
|
1863
2009
|
}
|
|
1864
2010
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -1876,16 +2022,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
1876
2022
|
this.emitWithAll(event, args);
|
|
1877
2023
|
}
|
|
1878
2024
|
};
|
|
1879
|
-
this._awaitWriteFinish(
|
|
2025
|
+
this._awaitWriteFinish(path10, awf.stabilityThreshold, event, awfEmit);
|
|
1880
2026
|
return this;
|
|
1881
2027
|
}
|
|
1882
2028
|
if (event === EVENTS.CHANGE) {
|
|
1883
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
2029
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path10, 50);
|
|
1884
2030
|
if (isThrottled)
|
|
1885
2031
|
return this;
|
|
1886
2032
|
}
|
|
1887
2033
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
1888
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
2034
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path10) : path10;
|
|
1889
2035
|
let stats2;
|
|
1890
2036
|
try {
|
|
1891
2037
|
stats2 = await stat3(fullPath);
|
|
@@ -1916,23 +2062,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
1916
2062
|
* @param timeout duration of time to suppress duplicate actions
|
|
1917
2063
|
* @returns tracking object or false if action should be suppressed
|
|
1918
2064
|
*/
|
|
1919
|
-
_throttle(actionType,
|
|
2065
|
+
_throttle(actionType, path10, timeout) {
|
|
1920
2066
|
if (!this._throttled.has(actionType)) {
|
|
1921
2067
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
1922
2068
|
}
|
|
1923
2069
|
const action = this._throttled.get(actionType);
|
|
1924
2070
|
if (!action)
|
|
1925
2071
|
throw new Error("invalid throttle");
|
|
1926
|
-
const actionPath = action.get(
|
|
2072
|
+
const actionPath = action.get(path10);
|
|
1927
2073
|
if (actionPath) {
|
|
1928
2074
|
actionPath.count++;
|
|
1929
2075
|
return false;
|
|
1930
2076
|
}
|
|
1931
2077
|
let timeoutObject;
|
|
1932
2078
|
const clear = () => {
|
|
1933
|
-
const item = action.get(
|
|
2079
|
+
const item = action.get(path10);
|
|
1934
2080
|
const count = item ? item.count : 0;
|
|
1935
|
-
action.delete(
|
|
2081
|
+
action.delete(path10);
|
|
1936
2082
|
clearTimeout(timeoutObject);
|
|
1937
2083
|
if (item)
|
|
1938
2084
|
clearTimeout(item.timeoutObject);
|
|
@@ -1940,7 +2086,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
1940
2086
|
};
|
|
1941
2087
|
timeoutObject = setTimeout(clear, timeout);
|
|
1942
2088
|
const thr = { timeoutObject, clear, count: 0 };
|
|
1943
|
-
action.set(
|
|
2089
|
+
action.set(path10, thr);
|
|
1944
2090
|
return thr;
|
|
1945
2091
|
}
|
|
1946
2092
|
_incrReadyCount() {
|
|
@@ -1954,44 +2100,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
1954
2100
|
* @param event
|
|
1955
2101
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
1956
2102
|
*/
|
|
1957
|
-
_awaitWriteFinish(
|
|
2103
|
+
_awaitWriteFinish(path10, threshold, event, awfEmit) {
|
|
1958
2104
|
const awf = this.options.awaitWriteFinish;
|
|
1959
2105
|
if (typeof awf !== "object")
|
|
1960
2106
|
return;
|
|
1961
2107
|
const pollInterval = awf.pollInterval;
|
|
1962
2108
|
let timeoutHandler;
|
|
1963
|
-
let fullPath =
|
|
1964
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
1965
|
-
fullPath = sp2.join(this.options.cwd,
|
|
2109
|
+
let fullPath = path10;
|
|
2110
|
+
if (this.options.cwd && !sp2.isAbsolute(path10)) {
|
|
2111
|
+
fullPath = sp2.join(this.options.cwd, path10);
|
|
1966
2112
|
}
|
|
1967
2113
|
const now = /* @__PURE__ */ new Date();
|
|
1968
2114
|
const writes = this._pendingWrites;
|
|
1969
2115
|
function awaitWriteFinishFn(prevStat) {
|
|
1970
2116
|
statcb(fullPath, (err, curStat) => {
|
|
1971
|
-
if (err || !writes.has(
|
|
2117
|
+
if (err || !writes.has(path10)) {
|
|
1972
2118
|
if (err && err.code !== "ENOENT")
|
|
1973
2119
|
awfEmit(err);
|
|
1974
2120
|
return;
|
|
1975
2121
|
}
|
|
1976
2122
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
1977
2123
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
1978
|
-
writes.get(
|
|
2124
|
+
writes.get(path10).lastChange = now2;
|
|
1979
2125
|
}
|
|
1980
|
-
const pw = writes.get(
|
|
2126
|
+
const pw = writes.get(path10);
|
|
1981
2127
|
const df = now2 - pw.lastChange;
|
|
1982
2128
|
if (df >= threshold) {
|
|
1983
|
-
writes.delete(
|
|
2129
|
+
writes.delete(path10);
|
|
1984
2130
|
awfEmit(void 0, curStat);
|
|
1985
2131
|
} else {
|
|
1986
2132
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
1987
2133
|
}
|
|
1988
2134
|
});
|
|
1989
2135
|
}
|
|
1990
|
-
if (!writes.has(
|
|
1991
|
-
writes.set(
|
|
2136
|
+
if (!writes.has(path10)) {
|
|
2137
|
+
writes.set(path10, {
|
|
1992
2138
|
lastChange: now,
|
|
1993
2139
|
cancelWait: () => {
|
|
1994
|
-
writes.delete(
|
|
2140
|
+
writes.delete(path10);
|
|
1995
2141
|
clearTimeout(timeoutHandler);
|
|
1996
2142
|
return event;
|
|
1997
2143
|
}
|
|
@@ -2002,8 +2148,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2002
2148
|
/**
|
|
2003
2149
|
* Determines whether user has asked to ignore this path.
|
|
2004
2150
|
*/
|
|
2005
|
-
_isIgnored(
|
|
2006
|
-
if (this.options.atomic && DOT_RE.test(
|
|
2151
|
+
_isIgnored(path10, stats) {
|
|
2152
|
+
if (this.options.atomic && DOT_RE.test(path10))
|
|
2007
2153
|
return true;
|
|
2008
2154
|
if (!this._userIgnored) {
|
|
2009
2155
|
const { cwd } = this.options;
|
|
@@ -2013,17 +2159,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2013
2159
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
2014
2160
|
this._userIgnored = anymatch(list, void 0);
|
|
2015
2161
|
}
|
|
2016
|
-
return this._userIgnored(
|
|
2162
|
+
return this._userIgnored(path10, stats);
|
|
2017
2163
|
}
|
|
2018
|
-
_isntIgnored(
|
|
2019
|
-
return !this._isIgnored(
|
|
2164
|
+
_isntIgnored(path10, stat4) {
|
|
2165
|
+
return !this._isIgnored(path10, stat4);
|
|
2020
2166
|
}
|
|
2021
2167
|
/**
|
|
2022
2168
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
2023
2169
|
* @param path file or directory pattern being watched
|
|
2024
2170
|
*/
|
|
2025
|
-
_getWatchHelpers(
|
|
2026
|
-
return new WatchHelper(
|
|
2171
|
+
_getWatchHelpers(path10) {
|
|
2172
|
+
return new WatchHelper(path10, this.options.followSymlinks, this);
|
|
2027
2173
|
}
|
|
2028
2174
|
// Directory helpers
|
|
2029
2175
|
// -----------------
|
|
@@ -2055,63 +2201,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2055
2201
|
* @param item base path of item/directory
|
|
2056
2202
|
*/
|
|
2057
2203
|
_remove(directory, item, isDirectory) {
|
|
2058
|
-
const
|
|
2059
|
-
const fullPath = sp2.resolve(
|
|
2060
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
2061
|
-
if (!this._throttle("remove",
|
|
2204
|
+
const path10 = sp2.join(directory, item);
|
|
2205
|
+
const fullPath = sp2.resolve(path10);
|
|
2206
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path10) || this._watched.has(fullPath);
|
|
2207
|
+
if (!this._throttle("remove", path10, 100))
|
|
2062
2208
|
return;
|
|
2063
2209
|
if (!isDirectory && this._watched.size === 1) {
|
|
2064
2210
|
this.add(directory, item, true);
|
|
2065
2211
|
}
|
|
2066
|
-
const wp = this._getWatchedDir(
|
|
2212
|
+
const wp = this._getWatchedDir(path10);
|
|
2067
2213
|
const nestedDirectoryChildren = wp.getChildren();
|
|
2068
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
2214
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path10, nested));
|
|
2069
2215
|
const parent = this._getWatchedDir(directory);
|
|
2070
2216
|
const wasTracked = parent.has(item);
|
|
2071
2217
|
parent.remove(item);
|
|
2072
2218
|
if (this._symlinkPaths.has(fullPath)) {
|
|
2073
2219
|
this._symlinkPaths.delete(fullPath);
|
|
2074
2220
|
}
|
|
2075
|
-
let relPath =
|
|
2221
|
+
let relPath = path10;
|
|
2076
2222
|
if (this.options.cwd)
|
|
2077
|
-
relPath = sp2.relative(this.options.cwd,
|
|
2223
|
+
relPath = sp2.relative(this.options.cwd, path10);
|
|
2078
2224
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
2079
2225
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
2080
2226
|
if (event === EVENTS.ADD)
|
|
2081
2227
|
return;
|
|
2082
2228
|
}
|
|
2083
|
-
this._watched.delete(
|
|
2229
|
+
this._watched.delete(path10);
|
|
2084
2230
|
this._watched.delete(fullPath);
|
|
2085
2231
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
2086
|
-
if (wasTracked && !this._isIgnored(
|
|
2087
|
-
this._emit(eventName,
|
|
2088
|
-
this._closePath(
|
|
2232
|
+
if (wasTracked && !this._isIgnored(path10))
|
|
2233
|
+
this._emit(eventName, path10);
|
|
2234
|
+
this._closePath(path10);
|
|
2089
2235
|
}
|
|
2090
2236
|
/**
|
|
2091
2237
|
* Closes all watchers for a path
|
|
2092
2238
|
*/
|
|
2093
|
-
_closePath(
|
|
2094
|
-
this._closeFile(
|
|
2095
|
-
const dir = sp2.dirname(
|
|
2096
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
2239
|
+
_closePath(path10) {
|
|
2240
|
+
this._closeFile(path10);
|
|
2241
|
+
const dir = sp2.dirname(path10);
|
|
2242
|
+
this._getWatchedDir(dir).remove(sp2.basename(path10));
|
|
2097
2243
|
}
|
|
2098
2244
|
/**
|
|
2099
2245
|
* Closes only file-specific watchers
|
|
2100
2246
|
*/
|
|
2101
|
-
_closeFile(
|
|
2102
|
-
const closers = this._closers.get(
|
|
2247
|
+
_closeFile(path10) {
|
|
2248
|
+
const closers = this._closers.get(path10);
|
|
2103
2249
|
if (!closers)
|
|
2104
2250
|
return;
|
|
2105
2251
|
closers.forEach((closer) => closer());
|
|
2106
|
-
this._closers.delete(
|
|
2252
|
+
this._closers.delete(path10);
|
|
2107
2253
|
}
|
|
2108
|
-
_addPathCloser(
|
|
2254
|
+
_addPathCloser(path10, closer) {
|
|
2109
2255
|
if (!closer)
|
|
2110
2256
|
return;
|
|
2111
|
-
let list = this._closers.get(
|
|
2257
|
+
let list = this._closers.get(path10);
|
|
2112
2258
|
if (!list) {
|
|
2113
2259
|
list = [];
|
|
2114
|
-
this._closers.set(
|
|
2260
|
+
this._closers.set(path10, list);
|
|
2115
2261
|
}
|
|
2116
2262
|
list.push(closer);
|
|
2117
2263
|
}
|
|
@@ -2140,7 +2286,7 @@ function watch(paths, options = {}) {
|
|
|
2140
2286
|
}
|
|
2141
2287
|
|
|
2142
2288
|
// src/daemon/watcher.ts
|
|
2143
|
-
import
|
|
2289
|
+
import path4 from "path";
|
|
2144
2290
|
var PlanWatcher = class {
|
|
2145
2291
|
config;
|
|
2146
2292
|
fsWatcher = null;
|
|
@@ -2166,7 +2312,7 @@ var PlanWatcher = class {
|
|
|
2166
2312
|
source: "tool",
|
|
2167
2313
|
filePath,
|
|
2168
2314
|
sessionId: event.session_id,
|
|
2169
|
-
detail: `${event.tool_name} on plan file: ${
|
|
2315
|
+
detail: `${event.tool_name} on plan file: ${path4.basename(filePath)}`,
|
|
2170
2316
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2171
2317
|
});
|
|
2172
2318
|
}
|
|
@@ -2174,7 +2320,7 @@ var PlanWatcher = class {
|
|
|
2174
2320
|
}
|
|
2175
2321
|
startFileWatcher() {
|
|
2176
2322
|
const absPaths = this.config.watchPaths.map(
|
|
2177
|
-
(p) =>
|
|
2323
|
+
(p) => path4.resolve(this.config.projectRoot, p)
|
|
2178
2324
|
);
|
|
2179
2325
|
this.fsWatcher = watch(absPaths, {
|
|
2180
2326
|
ignoreInitial: true,
|
|
@@ -2190,7 +2336,7 @@ var PlanWatcher = class {
|
|
|
2190
2336
|
this.fsWatcher = null;
|
|
2191
2337
|
}
|
|
2192
2338
|
onFileChange(absolutePath, action) {
|
|
2193
|
-
const rel =
|
|
2339
|
+
const rel = path4.relative(this.config.projectRoot, absolutePath);
|
|
2194
2340
|
this.knownPlans.add(absolutePath);
|
|
2195
2341
|
this.config.onPlan({
|
|
2196
2342
|
source: "filesystem",
|
|
@@ -2200,17 +2346,299 @@ var PlanWatcher = class {
|
|
|
2200
2346
|
});
|
|
2201
2347
|
}
|
|
2202
2348
|
isInPlanDirectory(filePath) {
|
|
2203
|
-
const abs =
|
|
2349
|
+
const abs = path4.isAbsolute(filePath) ? filePath : path4.resolve(this.config.projectRoot, filePath);
|
|
2204
2350
|
return this.config.watchPaths.some(
|
|
2205
|
-
(wp) => abs.startsWith(
|
|
2351
|
+
(wp) => abs.startsWith(path4.resolve(this.config.projectRoot, wp))
|
|
2206
2352
|
);
|
|
2207
2353
|
}
|
|
2208
2354
|
};
|
|
2209
2355
|
|
|
2356
|
+
// src/daemon/consolidation.ts
|
|
2357
|
+
import path5 from "path";
|
|
2358
|
+
var consolidationResponseSchema = external_exports.discriminatedUnion("consolidate", [
|
|
2359
|
+
external_exports.object({ consolidate: external_exports.literal(false), reason: external_exports.string().optional() }),
|
|
2360
|
+
external_exports.object({
|
|
2361
|
+
consolidate: external_exports.literal(true),
|
|
2362
|
+
title: external_exports.string(),
|
|
2363
|
+
content: external_exports.string(),
|
|
2364
|
+
source_ids: external_exports.array(external_exports.string()),
|
|
2365
|
+
tags: external_exports.array(external_exports.string()).optional()
|
|
2366
|
+
})
|
|
2367
|
+
]);
|
|
2368
|
+
var CONSOLIDATION_TRACE_FILENAME = "consolidation-trace.jsonl";
|
|
2369
|
+
var ConsolidationEngine = class {
|
|
2370
|
+
deps;
|
|
2371
|
+
log;
|
|
2372
|
+
lastTimestampCache = void 0;
|
|
2373
|
+
constructor(config) {
|
|
2374
|
+
this.deps = {
|
|
2375
|
+
vaultDir: config.vaultDir,
|
|
2376
|
+
index: config.index,
|
|
2377
|
+
vectorIndex: config.vectorIndex,
|
|
2378
|
+
llmProvider: config.llmProvider,
|
|
2379
|
+
embeddingProvider: config.embeddingProvider
|
|
2380
|
+
};
|
|
2381
|
+
this.log = config.log ?? (() => {
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
/**
|
|
2385
|
+
* Read the timestamp of the last consolidation pass from the trace file.
|
|
2386
|
+
* Cached in memory after first read — subsequent calls are O(1).
|
|
2387
|
+
*/
|
|
2388
|
+
getLastTimestamp() {
|
|
2389
|
+
if (this.lastTimestampCache !== void 0) return this.lastTimestampCache;
|
|
2390
|
+
const tracePath = path5.join(this.deps.vaultDir, "digest", CONSOLIDATION_TRACE_FILENAME);
|
|
2391
|
+
this.lastTimestampCache = readLastTimestamp(tracePath);
|
|
2392
|
+
return this.lastTimestampCache;
|
|
2393
|
+
}
|
|
2394
|
+
/**
|
|
2395
|
+
* Append a consolidation pass result as a JSON line to the trace file.
|
|
2396
|
+
* Updates the in-memory timestamp cache.
|
|
2397
|
+
*/
|
|
2398
|
+
appendTrace(record) {
|
|
2399
|
+
const tracePath = path5.join(this.deps.vaultDir, "digest", CONSOLIDATION_TRACE_FILENAME);
|
|
2400
|
+
appendTraceRecord(tracePath, record);
|
|
2401
|
+
this.lastTimestampCache = record.timestamp;
|
|
2402
|
+
}
|
|
2403
|
+
/**
|
|
2404
|
+
* Run one consolidation pass.
|
|
2405
|
+
*
|
|
2406
|
+
* Algorithm:
|
|
2407
|
+
* 1. Early-exit if vectorIndex or llmProvider is absent.
|
|
2408
|
+
* 2. Query index for spores created since lastTimestamp.
|
|
2409
|
+
* 3. Filter for active spores.
|
|
2410
|
+
* 4. For each new spore (skip if already processed):
|
|
2411
|
+
* a. Embed it.
|
|
2412
|
+
* b. Vector-search for similar spores.
|
|
2413
|
+
* c. Fetch candidate notes; filter same-type + active + not-processed.
|
|
2414
|
+
* d. Include trigger spore in cluster if not already present.
|
|
2415
|
+
* e. Skip if cluster < CONSOLIDATION_MIN_CLUSTER_SIZE.
|
|
2416
|
+
* f. Ask LLM whether to consolidate.
|
|
2417
|
+
* g. If approved, call consolidateSpores(); track counts.
|
|
2418
|
+
* h. Mark cluster members as processed regardless of outcome.
|
|
2419
|
+
* 5. Append trace record, return result.
|
|
2420
|
+
*
|
|
2421
|
+
* Returns null if no new spores were found or dependencies are absent.
|
|
2422
|
+
*/
|
|
2423
|
+
async runPass() {
|
|
2424
|
+
const { vaultDir, index, vectorIndex, llmProvider, embeddingProvider } = this.deps;
|
|
2425
|
+
if (!vectorIndex || !llmProvider || !embeddingProvider) {
|
|
2426
|
+
this.log("debug", "ConsolidationEngine: skipped \u2014 vectorIndex, llmProvider, or embeddingProvider unavailable");
|
|
2427
|
+
return null;
|
|
2428
|
+
}
|
|
2429
|
+
const startTime = Date.now();
|
|
2430
|
+
const lastTimestamp = this.getLastTimestamp();
|
|
2431
|
+
const allSpores = index.query({ type: "spore", since: lastTimestamp ?? void 0 });
|
|
2432
|
+
const newSpores = allSpores.filter((n) => isActiveSpore(n.frontmatter));
|
|
2433
|
+
this.log("debug", "ConsolidationEngine: spores to check", {
|
|
2434
|
+
count: newSpores.length,
|
|
2435
|
+
lastTimestamp: lastTimestamp ?? "never"
|
|
2436
|
+
});
|
|
2437
|
+
if (newSpores.length === 0) {
|
|
2438
|
+
return null;
|
|
2439
|
+
}
|
|
2440
|
+
const processedIds = /* @__PURE__ */ new Set();
|
|
2441
|
+
let clustersFound = 0;
|
|
2442
|
+
let consolidated = 0;
|
|
2443
|
+
let sporesSuperseded = 0;
|
|
2444
|
+
const template = loadPrompt("consolidation");
|
|
2445
|
+
for (const triggerSpore of newSpores) {
|
|
2446
|
+
if (processedIds.has(triggerSpore.id)) continue;
|
|
2447
|
+
const observationType = triggerSpore.frontmatter["observation_type"];
|
|
2448
|
+
let embedding;
|
|
2449
|
+
const storedEmbedding = vectorIndex.getEmbedding(triggerSpore.id);
|
|
2450
|
+
if (storedEmbedding) {
|
|
2451
|
+
embedding = storedEmbedding;
|
|
2452
|
+
} else {
|
|
2453
|
+
try {
|
|
2454
|
+
const embeddingText = triggerSpore.content.slice(0, EMBEDDING_INPUT_LIMIT);
|
|
2455
|
+
const result = await generateEmbedding(embeddingProvider, embeddingText);
|
|
2456
|
+
embedding = result.embedding;
|
|
2457
|
+
} catch (err) {
|
|
2458
|
+
this.log("warn", "ConsolidationEngine: embedding failed", { id: triggerSpore.id, error: String(err) });
|
|
2459
|
+
processedIds.add(triggerSpore.id);
|
|
2460
|
+
continue;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
const vectorResults = vectorIndex.search(embedding, {
|
|
2464
|
+
type: "spore",
|
|
2465
|
+
limit: CONSOLIDATION_VECTOR_FETCH_LIMIT
|
|
2466
|
+
});
|
|
2467
|
+
if (vectorResults.length === 0) {
|
|
2468
|
+
processedIds.add(triggerSpore.id);
|
|
2469
|
+
continue;
|
|
2470
|
+
}
|
|
2471
|
+
const candidateIds = vectorResults.map((r) => r.id);
|
|
2472
|
+
const candidateNotes = index.queryByIds(candidateIds);
|
|
2473
|
+
const filtered = candidateNotes.filter((note) => {
|
|
2474
|
+
if (processedIds.has(note.id)) return false;
|
|
2475
|
+
if (!isActiveSpore(note.frontmatter)) return false;
|
|
2476
|
+
if (observationType && note.frontmatter["observation_type"] !== observationType) return false;
|
|
2477
|
+
return true;
|
|
2478
|
+
});
|
|
2479
|
+
const clusterMap = new Map(filtered.map((n) => [n.id, n]));
|
|
2480
|
+
if (!clusterMap.has(triggerSpore.id)) {
|
|
2481
|
+
clusterMap.set(triggerSpore.id, triggerSpore);
|
|
2482
|
+
}
|
|
2483
|
+
const cluster = Array.from(clusterMap.values());
|
|
2484
|
+
if (cluster.length < CONSOLIDATION_MIN_CLUSTER_SIZE) {
|
|
2485
|
+
this.log("debug", "ConsolidationEngine: cluster too small \u2014 skipping", {
|
|
2486
|
+
triggerId: triggerSpore.id,
|
|
2487
|
+
clusterSize: cluster.length,
|
|
2488
|
+
minRequired: CONSOLIDATION_MIN_CLUSTER_SIZE
|
|
2489
|
+
});
|
|
2490
|
+
cluster.forEach((n) => processedIds.add(n.id));
|
|
2491
|
+
continue;
|
|
2492
|
+
}
|
|
2493
|
+
clustersFound++;
|
|
2494
|
+
const candidatesText = formatNotesForPrompt(cluster);
|
|
2495
|
+
const prompt = template.replace("{{count}}", String(cluster.length)).replace("{{observation_type}}", observationType ?? "unknown").replace("{{candidates}}", candidatesText);
|
|
2496
|
+
let responseText;
|
|
2497
|
+
try {
|
|
2498
|
+
const response = await llmProvider.summarize(prompt, {
|
|
2499
|
+
maxTokens: CONSOLIDATION_MAX_TOKENS,
|
|
2500
|
+
reasoning: LLM_REASONING_MODE
|
|
2501
|
+
});
|
|
2502
|
+
responseText = stripReasoningTokens(response.text);
|
|
2503
|
+
} catch (err) {
|
|
2504
|
+
this.log("warn", "ConsolidationEngine: LLM call failed", {
|
|
2505
|
+
triggerId: triggerSpore.id,
|
|
2506
|
+
error: String(err)
|
|
2507
|
+
});
|
|
2508
|
+
cluster.forEach((n) => processedIds.add(n.id));
|
|
2509
|
+
continue;
|
|
2510
|
+
}
|
|
2511
|
+
let parsed;
|
|
2512
|
+
try {
|
|
2513
|
+
const raw = JSON.parse(responseText);
|
|
2514
|
+
const result = consolidationResponseSchema.safeParse(raw);
|
|
2515
|
+
if (!result.success) {
|
|
2516
|
+
this.log("warn", "ConsolidationEngine: LLM response failed schema validation", {
|
|
2517
|
+
triggerId: triggerSpore.id,
|
|
2518
|
+
responseText
|
|
2519
|
+
});
|
|
2520
|
+
cluster.forEach((n) => processedIds.add(n.id));
|
|
2521
|
+
continue;
|
|
2522
|
+
}
|
|
2523
|
+
parsed = result.data;
|
|
2524
|
+
} catch {
|
|
2525
|
+
this.log("warn", "ConsolidationEngine: failed to parse LLM response", {
|
|
2526
|
+
triggerId: triggerSpore.id,
|
|
2527
|
+
responseText
|
|
2528
|
+
});
|
|
2529
|
+
cluster.forEach((n) => processedIds.add(n.id));
|
|
2530
|
+
continue;
|
|
2531
|
+
}
|
|
2532
|
+
if (!parsed.consolidate) {
|
|
2533
|
+
this.log("debug", "ConsolidationEngine: LLM declined to consolidate", {
|
|
2534
|
+
triggerId: triggerSpore.id,
|
|
2535
|
+
reason: parsed.reason
|
|
2536
|
+
});
|
|
2537
|
+
cluster.forEach((n) => processedIds.add(n.id));
|
|
2538
|
+
continue;
|
|
2539
|
+
}
|
|
2540
|
+
const clusterIdSet = new Set(cluster.map((n) => n.id));
|
|
2541
|
+
const validSourceIds = parsed.source_ids.filter((id) => clusterIdSet.has(id));
|
|
2542
|
+
if (validSourceIds.length < CONSOLIDATION_MIN_CLUSTER_SIZE) {
|
|
2543
|
+
this.log("warn", "ConsolidationEngine: insufficient valid source_ids after validation", {
|
|
2544
|
+
triggerId: triggerSpore.id,
|
|
2545
|
+
sourceIds: parsed.source_ids,
|
|
2546
|
+
validCount: validSourceIds.length
|
|
2547
|
+
});
|
|
2548
|
+
cluster.forEach((n) => processedIds.add(n.id));
|
|
2549
|
+
continue;
|
|
2550
|
+
}
|
|
2551
|
+
try {
|
|
2552
|
+
const consolidateResult = await consolidateSpores(
|
|
2553
|
+
{
|
|
2554
|
+
sourceSporeIds: validSourceIds,
|
|
2555
|
+
consolidatedContent: parsed.content,
|
|
2556
|
+
observationType: observationType ?? "gotcha",
|
|
2557
|
+
tags: parsed.tags
|
|
2558
|
+
},
|
|
2559
|
+
{
|
|
2560
|
+
vaultDir,
|
|
2561
|
+
index,
|
|
2562
|
+
vectorIndex,
|
|
2563
|
+
embeddingProvider: embeddingProvider ?? null
|
|
2564
|
+
}
|
|
2565
|
+
);
|
|
2566
|
+
consolidated++;
|
|
2567
|
+
sporesSuperseded += consolidateResult.sources_archived;
|
|
2568
|
+
this.log("info", "ConsolidationEngine: consolidated cluster", {
|
|
2569
|
+
wisdomId: consolidateResult.wisdom_id,
|
|
2570
|
+
sourcesArchived: consolidateResult.sources_archived,
|
|
2571
|
+
clusterSize: cluster.length
|
|
2572
|
+
});
|
|
2573
|
+
} catch (err) {
|
|
2574
|
+
this.log("warn", "ConsolidationEngine: consolidateSpores failed", {
|
|
2575
|
+
triggerId: triggerSpore.id,
|
|
2576
|
+
error: String(err)
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
cluster.forEach((n) => processedIds.add(n.id));
|
|
2580
|
+
}
|
|
2581
|
+
const passTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2582
|
+
const durationMs = Date.now() - startTime;
|
|
2583
|
+
const passResult = {
|
|
2584
|
+
timestamp: passTimestamp,
|
|
2585
|
+
sporesChecked: newSpores.length,
|
|
2586
|
+
clustersFound,
|
|
2587
|
+
consolidated,
|
|
2588
|
+
sporesSuperseded,
|
|
2589
|
+
durationMs
|
|
2590
|
+
};
|
|
2591
|
+
if (consolidated > 0) {
|
|
2592
|
+
this.appendTrace(passResult);
|
|
2593
|
+
} else {
|
|
2594
|
+
this.lastTimestampCache = passTimestamp;
|
|
2595
|
+
}
|
|
2596
|
+
this.log("info", "ConsolidationEngine: pass complete", {
|
|
2597
|
+
sporesChecked: newSpores.length,
|
|
2598
|
+
clustersFound,
|
|
2599
|
+
consolidated,
|
|
2600
|
+
sporesSuperseded,
|
|
2601
|
+
durationMs
|
|
2602
|
+
});
|
|
2603
|
+
return passResult;
|
|
2604
|
+
}
|
|
2605
|
+
};
|
|
2606
|
+
|
|
2607
|
+
// src/daemon/port.ts
|
|
2608
|
+
import { createHash } from "crypto";
|
|
2609
|
+
import net from "net";
|
|
2610
|
+
var PORT_RANGE_START = 19200;
|
|
2611
|
+
var PORT_RANGE_SIZE = 1e4;
|
|
2612
|
+
var PORT_RETRY_COUNT = 10;
|
|
2613
|
+
function derivePort(vaultPath) {
|
|
2614
|
+
const hash = createHash("md5").update(vaultPath).digest();
|
|
2615
|
+
const num = hash.readUInt16LE(0);
|
|
2616
|
+
return PORT_RANGE_START + num % PORT_RANGE_SIZE;
|
|
2617
|
+
}
|
|
2618
|
+
async function resolvePort(configPort, vaultPath) {
|
|
2619
|
+
const basePort = configPort ?? derivePort(vaultPath);
|
|
2620
|
+
for (let offset = 0; offset < PORT_RETRY_COUNT; offset++) {
|
|
2621
|
+
const candidate = basePort + offset;
|
|
2622
|
+
if (candidate > 65535) break;
|
|
2623
|
+
if (await isPortAvailable(candidate)) return candidate;
|
|
2624
|
+
}
|
|
2625
|
+
return 0;
|
|
2626
|
+
}
|
|
2627
|
+
function isPortAvailable(port) {
|
|
2628
|
+
return new Promise((resolve3) => {
|
|
2629
|
+
const server = net.createServer();
|
|
2630
|
+
server.once("error", () => resolve3(false));
|
|
2631
|
+
server.once("listening", () => {
|
|
2632
|
+
server.close(() => resolve3(true));
|
|
2633
|
+
});
|
|
2634
|
+
server.listen(port, "127.0.0.1");
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2210
2638
|
// src/artifacts/candidates.ts
|
|
2211
2639
|
import { execFileSync } from "child_process";
|
|
2212
|
-
import
|
|
2213
|
-
import
|
|
2640
|
+
import fs4 from "fs";
|
|
2641
|
+
import path6 from "path";
|
|
2214
2642
|
var EXCLUDED_FILENAMES = /* @__PURE__ */ new Set([
|
|
2215
2643
|
"claude.md",
|
|
2216
2644
|
"agents.md",
|
|
@@ -2231,7 +2659,7 @@ var EXCLUDED_PREFIXES = [
|
|
|
2231
2659
|
".github/"
|
|
2232
2660
|
];
|
|
2233
2661
|
function isExcludedPath(relativePath) {
|
|
2234
|
-
const basename3 =
|
|
2662
|
+
const basename3 = path6.basename(relativePath).toLowerCase();
|
|
2235
2663
|
if (EXCLUDED_FILENAMES.has(basename3)) return true;
|
|
2236
2664
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
2237
2665
|
return EXCLUDED_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
@@ -2239,7 +2667,7 @@ function isExcludedPath(relativePath) {
|
|
|
2239
2667
|
function collectArtifactCandidates(filePaths, config, projectRoot) {
|
|
2240
2668
|
if (filePaths.size === 0) return [];
|
|
2241
2669
|
const extFiltered = [...filePaths].filter(
|
|
2242
|
-
(absPath) => config.artifact_extensions.includes(
|
|
2670
|
+
(absPath) => config.artifact_extensions.includes(path6.extname(absPath))
|
|
2243
2671
|
);
|
|
2244
2672
|
if (extFiltered.length === 0) return [];
|
|
2245
2673
|
const ignoredSet = getGitIgnored(extFiltered, projectRoot);
|
|
@@ -2247,8 +2675,8 @@ function collectArtifactCandidates(filePaths, config, projectRoot) {
|
|
|
2247
2675
|
for (const absPath of extFiltered) {
|
|
2248
2676
|
if (ignoredSet.has(absPath)) continue;
|
|
2249
2677
|
try {
|
|
2250
|
-
const content =
|
|
2251
|
-
const relativePath =
|
|
2678
|
+
const content = fs4.readFileSync(absPath, "utf-8");
|
|
2679
|
+
const relativePath = path6.relative(projectRoot, absPath);
|
|
2252
2680
|
if (isExcludedPath(relativePath)) continue;
|
|
2253
2681
|
candidates.push({ path: relativePath, content });
|
|
2254
2682
|
} catch {
|
|
@@ -2271,9 +2699,9 @@ function getGitIgnored(filePaths, cwd) {
|
|
|
2271
2699
|
|
|
2272
2700
|
// src/artifacts/slugify.ts
|
|
2273
2701
|
import crypto from "crypto";
|
|
2274
|
-
import
|
|
2702
|
+
import path7 from "path";
|
|
2275
2703
|
function slugifyPath(relativePath) {
|
|
2276
|
-
const ext =
|
|
2704
|
+
const ext = path7.extname(relativePath);
|
|
2277
2705
|
const withoutExt = ext ? relativePath.slice(0, -ext.length) : relativePath;
|
|
2278
2706
|
let slug = withoutExt.replace(/[/\\]/g, "-").toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
2279
2707
|
if (slug.length > MAX_SLUG_LENGTH) {
|
|
@@ -2283,10 +2711,416 @@ function slugifyPath(relativePath) {
|
|
|
2283
2711
|
return slug;
|
|
2284
2712
|
}
|
|
2285
2713
|
|
|
2714
|
+
// src/daemon/api/config.ts
|
|
2715
|
+
async function handleGetConfig(vaultDir) {
|
|
2716
|
+
const config = loadConfig(vaultDir);
|
|
2717
|
+
return { body: config };
|
|
2718
|
+
}
|
|
2719
|
+
async function handlePutConfig(vaultDir, body) {
|
|
2720
|
+
const result = MycoConfigSchema.safeParse(body);
|
|
2721
|
+
if (!result.success) {
|
|
2722
|
+
return {
|
|
2723
|
+
status: 400,
|
|
2724
|
+
body: { error: "validation_failed", issues: result.error.issues }
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
saveConfig(vaultDir, result.data);
|
|
2728
|
+
return { body: result.data };
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// src/daemon/api/stats.ts
|
|
2732
|
+
import { createHash as createHash2 } from "crypto";
|
|
2733
|
+
import fs5 from "fs";
|
|
2734
|
+
import path8 from "path";
|
|
2735
|
+
async function handleGetStats(deps) {
|
|
2736
|
+
const baseStats = gatherStats(deps.vaultDir, deps.index, deps.vectorIndex ?? void 0);
|
|
2737
|
+
const digestConfig = deps.config.digest;
|
|
2738
|
+
const digest = {
|
|
2739
|
+
enabled: digestConfig.enabled,
|
|
2740
|
+
consolidation_enabled: digestConfig.consolidation,
|
|
2741
|
+
metabolism_state: deps.metabolism?.state ?? null,
|
|
2742
|
+
last_cycle: null,
|
|
2743
|
+
substrate_queue: 0
|
|
2744
|
+
};
|
|
2745
|
+
return {
|
|
2746
|
+
body: {
|
|
2747
|
+
daemon: {
|
|
2748
|
+
...baseStats.daemon,
|
|
2749
|
+
pid: baseStats.daemon?.pid ?? process.pid,
|
|
2750
|
+
port: baseStats.daemon?.port ?? 0,
|
|
2751
|
+
version: deps.version,
|
|
2752
|
+
uptime_seconds: process.uptime(),
|
|
2753
|
+
active_sessions: baseStats.daemon?.active_sessions ?? [],
|
|
2754
|
+
config_hash: deps.configHash
|
|
2755
|
+
},
|
|
2756
|
+
vault: baseStats.vault,
|
|
2757
|
+
index: baseStats.index,
|
|
2758
|
+
digest,
|
|
2759
|
+
intelligence: {
|
|
2760
|
+
processor: {
|
|
2761
|
+
provider: deps.config.intelligence.llm.provider,
|
|
2762
|
+
model: deps.config.intelligence.llm.model
|
|
2763
|
+
},
|
|
2764
|
+
digest: digestConfig.intelligence.provider ? {
|
|
2765
|
+
provider: digestConfig.intelligence.provider,
|
|
2766
|
+
model: digestConfig.intelligence.model ?? deps.config.intelligence.llm.model
|
|
2767
|
+
} : null,
|
|
2768
|
+
embedding: {
|
|
2769
|
+
provider: deps.config.intelligence.embedding.provider,
|
|
2770
|
+
model: deps.config.intelligence.embedding.model
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
};
|
|
2775
|
+
}
|
|
2776
|
+
function computeConfigHash(vaultDir) {
|
|
2777
|
+
try {
|
|
2778
|
+
const configPath = path8.join(vaultDir, CONFIG_FILENAME);
|
|
2779
|
+
const raw = fs5.readFileSync(configPath, "utf-8");
|
|
2780
|
+
return createHash2("md5").update(raw).digest("hex");
|
|
2781
|
+
} catch {
|
|
2782
|
+
return "";
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
// src/daemon/api/logs.ts
|
|
2787
|
+
async function handleGetLogs(ringBuffer, query) {
|
|
2788
|
+
const since = query.since || null;
|
|
2789
|
+
const level = query.level;
|
|
2790
|
+
const limit = query.limit ? parseInt(query.limit, 10) : void 0;
|
|
2791
|
+
const result = ringBuffer.since(since, { level, limit: isNaN(limit) ? void 0 : limit });
|
|
2792
|
+
const entries = result.entries.map((entry) => {
|
|
2793
|
+
const { component, ...rest } = entry;
|
|
2794
|
+
return { ...rest, category: component };
|
|
2795
|
+
});
|
|
2796
|
+
return {
|
|
2797
|
+
body: {
|
|
2798
|
+
entries,
|
|
2799
|
+
cursor: result.cursor,
|
|
2800
|
+
...result.cursor_reset ? { cursor_reset: true } : {}
|
|
2801
|
+
}
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
// src/daemon/api/restart.ts
|
|
2806
|
+
import { spawn } from "child_process";
|
|
2807
|
+
var RestartBodySchema = external_exports.object({
|
|
2808
|
+
force: external_exports.boolean().optional()
|
|
2809
|
+
}).optional();
|
|
2810
|
+
var RESTART_RESPONSE_FLUSH_MS = 500;
|
|
2811
|
+
var RESTART_CHILD_DELAY_SECONDS = 3;
|
|
2812
|
+
async function handleRestart(deps, body) {
|
|
2813
|
+
const parsed = RestartBodySchema.safeParse(body);
|
|
2814
|
+
const force = parsed.success ? parsed.data?.force : false;
|
|
2815
|
+
if (!force && deps.progressTracker.hasActiveOperations()) {
|
|
2816
|
+
return {
|
|
2817
|
+
status: 409,
|
|
2818
|
+
body: { status: "busy", message: "Active operations in progress. Use force=true to override." }
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
const daemonScript = process.argv[1];
|
|
2822
|
+
const shellCmd = `sleep ${RESTART_CHILD_DELAY_SECONDS} && ${process.execPath} ${daemonScript} --vault ${deps.vaultDir}`;
|
|
2823
|
+
const child = spawn("/bin/sh", ["-c", shellCmd], {
|
|
2824
|
+
detached: true,
|
|
2825
|
+
stdio: "ignore"
|
|
2826
|
+
});
|
|
2827
|
+
child.unref();
|
|
2828
|
+
setTimeout(() => {
|
|
2829
|
+
process.kill(process.pid, "SIGTERM");
|
|
2830
|
+
}, RESTART_RESPONSE_FLUSH_MS);
|
|
2831
|
+
return { body: { status: "restarting" } };
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
// src/daemon/api/progress.ts
|
|
2835
|
+
import { randomUUID } from "crypto";
|
|
2836
|
+
var MAX_CONCURRENT_OPERATIONS = 10;
|
|
2837
|
+
var PROGRESS_TTL_MS = 5 * 60 * 1e3;
|
|
2838
|
+
var ProgressTracker = class {
|
|
2839
|
+
entries = /* @__PURE__ */ new Map();
|
|
2840
|
+
/**
|
|
2841
|
+
* Create a new tracked operation. Returns the existing token if an
|
|
2842
|
+
* operation of the same type is already running (duplicate prevention).
|
|
2843
|
+
* Throws if the maximum concurrent operations limit is reached.
|
|
2844
|
+
*/
|
|
2845
|
+
/**
|
|
2846
|
+
* Create a new tracked operation or return existing one.
|
|
2847
|
+
* Returns `{ token, isNew }` — if `isNew` is false, the operation
|
|
2848
|
+
* was already running and the caller should NOT launch it again.
|
|
2849
|
+
* Throws if the maximum concurrent operations limit is reached.
|
|
2850
|
+
*/
|
|
2851
|
+
create(type) {
|
|
2852
|
+
this.cleanup();
|
|
2853
|
+
for (const entry of this.entries.values()) {
|
|
2854
|
+
if (entry.type === type && entry.status === "running") {
|
|
2855
|
+
return { token: entry.token, isNew: false };
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
const runningCount = [...this.entries.values()].filter((e) => e.status === "running").length;
|
|
2859
|
+
if (runningCount >= MAX_CONCURRENT_OPERATIONS) {
|
|
2860
|
+
throw new Error(`Maximum concurrent operations reached (${MAX_CONCURRENT_OPERATIONS})`);
|
|
2861
|
+
}
|
|
2862
|
+
const token = randomUUID();
|
|
2863
|
+
const now = Date.now();
|
|
2864
|
+
this.entries.set(token, {
|
|
2865
|
+
token,
|
|
2866
|
+
type,
|
|
2867
|
+
status: "running",
|
|
2868
|
+
created: now,
|
|
2869
|
+
updated: now
|
|
2870
|
+
});
|
|
2871
|
+
return { token, isNew: true };
|
|
2872
|
+
}
|
|
2873
|
+
/**
|
|
2874
|
+
* Update progress for a tracked operation.
|
|
2875
|
+
*/
|
|
2876
|
+
update(token, data) {
|
|
2877
|
+
const entry = this.entries.get(token);
|
|
2878
|
+
if (!entry) return;
|
|
2879
|
+
if (data.percent !== void 0) entry.percent = data.percent;
|
|
2880
|
+
if (data.message !== void 0) entry.message = data.message;
|
|
2881
|
+
if (data.status !== void 0) entry.status = data.status;
|
|
2882
|
+
entry.updated = Date.now();
|
|
2883
|
+
}
|
|
2884
|
+
/**
|
|
2885
|
+
* Get the current state of a tracked operation.
|
|
2886
|
+
*/
|
|
2887
|
+
get(token) {
|
|
2888
|
+
return this.entries.get(token);
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Check whether any operations are currently running.
|
|
2892
|
+
*/
|
|
2893
|
+
hasActiveOperations() {
|
|
2894
|
+
for (const entry of this.entries.values()) {
|
|
2895
|
+
if (entry.status === "running") return true;
|
|
2896
|
+
}
|
|
2897
|
+
return false;
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Remove completed/failed entries older than PROGRESS_TTL_MS.
|
|
2901
|
+
*/
|
|
2902
|
+
cleanup() {
|
|
2903
|
+
const cutoff = Date.now() - PROGRESS_TTL_MS;
|
|
2904
|
+
for (const [token, entry] of this.entries) {
|
|
2905
|
+
if (entry.status !== "running" && entry.updated < cutoff) {
|
|
2906
|
+
this.entries.delete(token);
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
};
|
|
2911
|
+
async function handleGetProgress(tracker, token) {
|
|
2912
|
+
const entry = tracker.get(token);
|
|
2913
|
+
if (!entry) {
|
|
2914
|
+
return { status: 404, body: { error: "not_found", message: "Progress token not found" } };
|
|
2915
|
+
}
|
|
2916
|
+
return { body: entry };
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
// src/daemon/api/operations.ts
|
|
2920
|
+
var PROGRESS_COMPLETE = 100;
|
|
2921
|
+
var CurateBody = external_exports.object({
|
|
2922
|
+
dry_run: external_exports.boolean().optional()
|
|
2923
|
+
}).optional();
|
|
2924
|
+
var DigestBody = external_exports.object({
|
|
2925
|
+
tier: external_exports.number().int().positive().optional(),
|
|
2926
|
+
full: external_exports.boolean().optional()
|
|
2927
|
+
}).optional();
|
|
2928
|
+
async function handleRebuild(deps) {
|
|
2929
|
+
const { token, isNew } = deps.progressTracker.create("rebuild");
|
|
2930
|
+
if (!isNew) {
|
|
2931
|
+
return { body: { token, status: "already_running" } };
|
|
2932
|
+
}
|
|
2933
|
+
runRebuild(
|
|
2934
|
+
{
|
|
2935
|
+
vaultDir: deps.vaultDir,
|
|
2936
|
+
config: deps.config,
|
|
2937
|
+
index: deps.index,
|
|
2938
|
+
vectorIndex: deps.vectorIndex ?? void 0,
|
|
2939
|
+
log: deps.log
|
|
2940
|
+
},
|
|
2941
|
+
deps.embeddingProvider,
|
|
2942
|
+
(done, total) => {
|
|
2943
|
+
const percent = total > 0 ? Math.round(done / total * PROGRESS_COMPLETE) : 0;
|
|
2944
|
+
deps.progressTracker.update(token, {
|
|
2945
|
+
percent,
|
|
2946
|
+
message: `Embedded ${done}/${total} notes`
|
|
2947
|
+
});
|
|
2948
|
+
}
|
|
2949
|
+
).then((result) => {
|
|
2950
|
+
deps.progressTracker.update(token, {
|
|
2951
|
+
status: "completed",
|
|
2952
|
+
percent: PROGRESS_COMPLETE,
|
|
2953
|
+
message: `FTS: ${result.ftsCount}, embedded: ${result.embeddedCount}, failed: ${result.failedCount}, skipped: ${result.skippedCount}`
|
|
2954
|
+
});
|
|
2955
|
+
deps.log("info", "Rebuild completed via API", {
|
|
2956
|
+
fts: result.ftsCount,
|
|
2957
|
+
embedded: result.embeddedCount,
|
|
2958
|
+
failed: result.failedCount
|
|
2959
|
+
});
|
|
2960
|
+
}).catch((err) => {
|
|
2961
|
+
deps.progressTracker.update(token, {
|
|
2962
|
+
status: "failed",
|
|
2963
|
+
message: err.message
|
|
2964
|
+
});
|
|
2965
|
+
deps.log("warn", "Rebuild failed via API", { error: err.message });
|
|
2966
|
+
});
|
|
2967
|
+
return { body: { token } };
|
|
2968
|
+
}
|
|
2969
|
+
async function handleDigest(deps, body) {
|
|
2970
|
+
const parsed = DigestBody.safeParse(body);
|
|
2971
|
+
if (!parsed.success) {
|
|
2972
|
+
return { status: 400, body: { error: "validation_failed", issues: parsed.error.issues } };
|
|
2973
|
+
}
|
|
2974
|
+
if (!deps.config.digest.enabled) {
|
|
2975
|
+
return { status: 400, body: { error: "digest_disabled", message: "Digest is not enabled in myco.yaml" } };
|
|
2976
|
+
}
|
|
2977
|
+
const options = parsed.data;
|
|
2978
|
+
const { token, isNew } = deps.progressTracker.create("digest");
|
|
2979
|
+
if (!isNew) {
|
|
2980
|
+
return { body: { token, status: "already_running" } };
|
|
2981
|
+
}
|
|
2982
|
+
runDigest(
|
|
2983
|
+
{
|
|
2984
|
+
vaultDir: deps.vaultDir,
|
|
2985
|
+
config: deps.config,
|
|
2986
|
+
index: deps.index,
|
|
2987
|
+
vectorIndex: deps.vectorIndex ?? void 0,
|
|
2988
|
+
log: deps.log
|
|
2989
|
+
},
|
|
2990
|
+
deps.llmProvider,
|
|
2991
|
+
options ?? void 0
|
|
2992
|
+
).then((result) => {
|
|
2993
|
+
if (result) {
|
|
2994
|
+
deps.progressTracker.update(token, {
|
|
2995
|
+
status: "completed",
|
|
2996
|
+
percent: PROGRESS_COMPLETE,
|
|
2997
|
+
message: `Tiers: [${result.tiersGenerated.join(", ")}], substrate: ${Object.values(result.substrate).flat().length} notes, ${(result.durationMs / 1e3).toFixed(1)}s`
|
|
2998
|
+
});
|
|
2999
|
+
deps.log("info", "Digest completed via API", {
|
|
3000
|
+
tiers: result.tiersGenerated,
|
|
3001
|
+
duration: result.durationMs
|
|
3002
|
+
});
|
|
3003
|
+
} else {
|
|
3004
|
+
deps.progressTracker.update(token, {
|
|
3005
|
+
status: "completed",
|
|
3006
|
+
percent: PROGRESS_COMPLETE,
|
|
3007
|
+
message: "No substrate found \u2014 nothing to digest"
|
|
3008
|
+
});
|
|
3009
|
+
}
|
|
3010
|
+
}).catch((err) => {
|
|
3011
|
+
deps.progressTracker.update(token, {
|
|
3012
|
+
status: "failed",
|
|
3013
|
+
message: err.message
|
|
3014
|
+
});
|
|
3015
|
+
deps.log("warn", "Digest failed via API", { error: err.message });
|
|
3016
|
+
});
|
|
3017
|
+
return { body: { token } };
|
|
3018
|
+
}
|
|
3019
|
+
async function handleCurate(deps, body, runCuration2) {
|
|
3020
|
+
const parsed = CurateBody.safeParse(body);
|
|
3021
|
+
if (!parsed.success) {
|
|
3022
|
+
return { status: 400, body: { error: "validation_failed", issues: parsed.error.issues } };
|
|
3023
|
+
}
|
|
3024
|
+
const isDryRun = parsed.data?.dry_run ?? false;
|
|
3025
|
+
if (!deps.vectorIndex) {
|
|
3026
|
+
return { status: 400, body: { error: "vector_index_unavailable", message: "Curate requires a working embedding provider" } };
|
|
3027
|
+
}
|
|
3028
|
+
const curationDeps = {
|
|
3029
|
+
vaultDir: deps.vaultDir,
|
|
3030
|
+
config: deps.config,
|
|
3031
|
+
index: deps.index,
|
|
3032
|
+
vectorIndex: deps.vectorIndex,
|
|
3033
|
+
llmProvider: deps.llmProvider,
|
|
3034
|
+
embeddingProvider: deps.embeddingProvider,
|
|
3035
|
+
log: deps.log
|
|
3036
|
+
};
|
|
3037
|
+
if (isDryRun) {
|
|
3038
|
+
try {
|
|
3039
|
+
const result = await runCuration2(curationDeps, true);
|
|
3040
|
+
return { body: { dry_run: true, ...result } };
|
|
3041
|
+
} catch (err) {
|
|
3042
|
+
return { status: 500, body: { error: "curation_failed", message: err.message } };
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
const { token, isNew } = deps.progressTracker.create("curate");
|
|
3046
|
+
if (!isNew) {
|
|
3047
|
+
return { body: { token, status: "already_running" } };
|
|
3048
|
+
}
|
|
3049
|
+
runCuration2(curationDeps, false).then((result) => {
|
|
3050
|
+
deps.progressTracker.update(token, {
|
|
3051
|
+
status: "completed",
|
|
3052
|
+
percent: PROGRESS_COMPLETE,
|
|
3053
|
+
message: `Scanned: ${result.scanned}, clusters: ${result.clustersEvaluated}, superseded: ${result.superseded}`
|
|
3054
|
+
});
|
|
3055
|
+
deps.log("info", "Curation completed via API", { ...result });
|
|
3056
|
+
}).catch((err) => {
|
|
3057
|
+
deps.progressTracker.update(token, {
|
|
3058
|
+
status: "failed",
|
|
3059
|
+
message: err.message
|
|
3060
|
+
});
|
|
3061
|
+
deps.log("warn", "Curation failed via API", { error: err.message });
|
|
3062
|
+
});
|
|
3063
|
+
return { body: { token } };
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
// src/daemon/api/models.ts
|
|
3067
|
+
var MODEL_LIST_TIMEOUT_MS = 5e3;
|
|
3068
|
+
var ANTHROPIC_MODELS = [
|
|
3069
|
+
"claude-opus-4-6",
|
|
3070
|
+
"claude-sonnet-4-6",
|
|
3071
|
+
"claude-haiku-4-5-20251001"
|
|
3072
|
+
];
|
|
3073
|
+
var EMBEDDING_PATTERNS = [
|
|
3074
|
+
"embed",
|
|
3075
|
+
"bge-",
|
|
3076
|
+
"nomic-embed",
|
|
3077
|
+
"e5-",
|
|
3078
|
+
"gte-",
|
|
3079
|
+
"granite-embedding"
|
|
3080
|
+
];
|
|
3081
|
+
function filterEmbeddingModels(models) {
|
|
3082
|
+
return models.filter((m) => {
|
|
3083
|
+
const name = m.toLowerCase();
|
|
3084
|
+
return EMBEDDING_PATTERNS.some((p) => name.includes(p));
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
function filterLlmModels(models) {
|
|
3088
|
+
return models.filter((m) => {
|
|
3089
|
+
const name = m.toLowerCase();
|
|
3090
|
+
return !EMBEDDING_PATTERNS.some((p) => name.includes(p));
|
|
3091
|
+
});
|
|
3092
|
+
}
|
|
3093
|
+
async function handleGetModels(req) {
|
|
3094
|
+
const provider = req.query.provider;
|
|
3095
|
+
const type = req.query.type;
|
|
3096
|
+
if (!provider) {
|
|
3097
|
+
return { status: 400, body: { error: "provider query parameter required" } };
|
|
3098
|
+
}
|
|
3099
|
+
let models = [];
|
|
3100
|
+
try {
|
|
3101
|
+
if (provider === "ollama") {
|
|
3102
|
+
const backend = new OllamaBackend({ base_url: req.query.base_url });
|
|
3103
|
+
models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);
|
|
3104
|
+
} else if (provider === "lm-studio" || provider === "openai-compatible") {
|
|
3105
|
+
const backend = new LmStudioBackend({ base_url: req.query.base_url });
|
|
3106
|
+
models = await backend.listModels(MODEL_LIST_TIMEOUT_MS);
|
|
3107
|
+
} else if (provider === "anthropic") {
|
|
3108
|
+
models = ANTHROPIC_MODELS;
|
|
3109
|
+
}
|
|
3110
|
+
} catch {
|
|
3111
|
+
}
|
|
3112
|
+
if (type === "embedding") {
|
|
3113
|
+
models = filterEmbeddingModels(models);
|
|
3114
|
+
} else if (type === "llm") {
|
|
3115
|
+
models = filterLlmModels(models);
|
|
3116
|
+
}
|
|
3117
|
+
return { body: { provider, models } };
|
|
3118
|
+
}
|
|
3119
|
+
|
|
2286
3120
|
// src/daemon/main.ts
|
|
2287
3121
|
var import_yaml = __toESM(require_dist(), 1);
|
|
2288
|
-
import
|
|
2289
|
-
import
|
|
3122
|
+
import fs6 from "fs";
|
|
3123
|
+
import path9 from "path";
|
|
2290
3124
|
function indexAndEmbed(relativePath, noteId, embeddingText, metadata, deps) {
|
|
2291
3125
|
indexNote(deps.index, deps.vaultDir, relativePath);
|
|
2292
3126
|
if (deps.vectorIndex && embeddingText) {
|
|
@@ -2358,28 +3192,28 @@ ${candidate.content}`,
|
|
|
2358
3192
|
}
|
|
2359
3193
|
}
|
|
2360
3194
|
function migrateSporeFiles(vaultDir) {
|
|
2361
|
-
const sporesDir =
|
|
2362
|
-
if (!
|
|
3195
|
+
const sporesDir = path9.join(vaultDir, "spores");
|
|
3196
|
+
if (!fs6.existsSync(sporesDir)) return 0;
|
|
2363
3197
|
let moved = 0;
|
|
2364
|
-
const entries =
|
|
3198
|
+
const entries = fs6.readdirSync(sporesDir);
|
|
2365
3199
|
for (const entry of entries) {
|
|
2366
|
-
const fullPath =
|
|
3200
|
+
const fullPath = path9.join(sporesDir, entry);
|
|
2367
3201
|
if (!entry.endsWith(".md")) continue;
|
|
2368
|
-
if (
|
|
3202
|
+
if (fs6.statSync(fullPath).isDirectory()) continue;
|
|
2369
3203
|
try {
|
|
2370
|
-
const content =
|
|
3204
|
+
const content = fs6.readFileSync(fullPath, "utf-8");
|
|
2371
3205
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
2372
3206
|
if (!fmMatch) continue;
|
|
2373
3207
|
const parsed = import_yaml.default.parse(fmMatch[1]);
|
|
2374
3208
|
const obsType = parsed.observation_type;
|
|
2375
3209
|
if (!obsType) continue;
|
|
2376
3210
|
const normalizedType = obsType.replace(/_/g, "-");
|
|
2377
|
-
const targetDir =
|
|
2378
|
-
|
|
2379
|
-
const targetPath =
|
|
2380
|
-
|
|
3211
|
+
const targetDir = path9.join(sporesDir, normalizedType);
|
|
3212
|
+
fs6.mkdirSync(targetDir, { recursive: true });
|
|
3213
|
+
const targetPath = path9.join(targetDir, entry);
|
|
3214
|
+
fs6.renameSync(fullPath, targetPath);
|
|
2381
3215
|
const now = /* @__PURE__ */ new Date();
|
|
2382
|
-
|
|
3216
|
+
fs6.utimesSync(targetPath, now, now);
|
|
2383
3217
|
moved++;
|
|
2384
3218
|
} catch {
|
|
2385
3219
|
}
|
|
@@ -2392,13 +3226,28 @@ async function main() {
|
|
|
2392
3226
|
process.stderr.write("Usage: mycod --vault <path>\n");
|
|
2393
3227
|
process.exit(1);
|
|
2394
3228
|
}
|
|
2395
|
-
const vaultDir =
|
|
3229
|
+
const vaultDir = path9.resolve(vaultArg);
|
|
2396
3230
|
const config = loadConfig(vaultDir);
|
|
2397
|
-
const logger = new DaemonLogger(
|
|
3231
|
+
const logger = new DaemonLogger(path9.join(vaultDir, "logs"), {
|
|
2398
3232
|
level: config.daemon.log_level,
|
|
2399
3233
|
maxSize: config.daemon.max_log_size
|
|
2400
3234
|
});
|
|
2401
|
-
|
|
3235
|
+
let uiDir = null;
|
|
3236
|
+
{
|
|
3237
|
+
let dir = path9.dirname(new URL(import.meta.url).pathname);
|
|
3238
|
+
for (let i = 0; i < 5; i++) {
|
|
3239
|
+
const candidate = path9.join(dir, "dist", "ui");
|
|
3240
|
+
if (fs6.existsSync(path9.join(dir, "package.json")) && fs6.existsSync(candidate)) {
|
|
3241
|
+
uiDir = candidate;
|
|
3242
|
+
break;
|
|
3243
|
+
}
|
|
3244
|
+
dir = path9.dirname(dir);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
if (uiDir) {
|
|
3248
|
+
logger.debug("daemon", "Static UI directory found", { path: uiDir });
|
|
3249
|
+
}
|
|
3250
|
+
const server = new DaemonServer({ vaultDir, logger, uiDir: uiDir ?? void 0 });
|
|
2402
3251
|
const registry = new SessionRegistry({
|
|
2403
3252
|
gracePeriod: config.daemon.grace_period,
|
|
2404
3253
|
onEmpty: async () => {
|
|
@@ -2417,14 +3266,14 @@ async function main() {
|
|
|
2417
3266
|
let vectorIndex = null;
|
|
2418
3267
|
try {
|
|
2419
3268
|
const testEmbed = await embeddingProvider.embed("test");
|
|
2420
|
-
vectorIndex = new VectorIndex(
|
|
3269
|
+
vectorIndex = new VectorIndex(path9.join(vaultDir, "vectors.db"), testEmbed.dimensions);
|
|
2421
3270
|
logger.info("embeddings", "Vector index initialized", { dimensions: testEmbed.dimensions });
|
|
2422
3271
|
} catch (error) {
|
|
2423
3272
|
logger.warn("embeddings", "Vector index unavailable", { error: error.message });
|
|
2424
3273
|
}
|
|
2425
3274
|
const processor = new BufferProcessor(llmProvider, config.intelligence.llm.context_window, config.capture);
|
|
2426
3275
|
const vault = new VaultWriter(vaultDir);
|
|
2427
|
-
const index = new MycoIndex(
|
|
3276
|
+
const index = new MycoIndex(path9.join(vaultDir, "index.db"));
|
|
2428
3277
|
const lineageGraph = new LineageGraph(vaultDir);
|
|
2429
3278
|
const transcriptMiner = new TranscriptMiner({
|
|
2430
3279
|
additionalAdapters: config.capture.transcript_paths.map(
|
|
@@ -2433,17 +3282,17 @@ async function main() {
|
|
|
2433
3282
|
});
|
|
2434
3283
|
let activeStopProcessing = null;
|
|
2435
3284
|
const indexDeps = { index, vaultDir, vectorIndex, embeddingProvider, llmProvider, logger };
|
|
2436
|
-
const bufferDir =
|
|
3285
|
+
const bufferDir = path9.join(vaultDir, "buffer");
|
|
2437
3286
|
const sessionBuffers = /* @__PURE__ */ new Map();
|
|
2438
3287
|
const sessionFilePaths = /* @__PURE__ */ new Map();
|
|
2439
3288
|
const capturedArtifactPaths = /* @__PURE__ */ new Map();
|
|
2440
|
-
if (
|
|
3289
|
+
if (fs6.existsSync(bufferDir)) {
|
|
2441
3290
|
const cutoff = Date.now() - STALE_BUFFER_MAX_AGE_MS;
|
|
2442
|
-
for (const file of
|
|
2443
|
-
const filePath =
|
|
2444
|
-
const stat4 =
|
|
3291
|
+
for (const file of fs6.readdirSync(bufferDir)) {
|
|
3292
|
+
const filePath = path9.join(bufferDir, file);
|
|
3293
|
+
const stat4 = fs6.statSync(filePath);
|
|
2445
3294
|
if (stat4.mtimeMs < cutoff) {
|
|
2446
|
-
|
|
3295
|
+
fs6.unlinkSync(filePath);
|
|
2447
3296
|
logger.debug("daemon", "Cleaned stale buffer", { file });
|
|
2448
3297
|
}
|
|
2449
3298
|
}
|
|
@@ -2480,10 +3329,10 @@ async function main() {
|
|
|
2480
3329
|
logger.info("watcher", "Plan detected", { source: event.source, file: event.filePath });
|
|
2481
3330
|
if (event.filePath) {
|
|
2482
3331
|
try {
|
|
2483
|
-
const content =
|
|
2484
|
-
const relativePath =
|
|
2485
|
-
const title = content.match(/^#\s+(.+)$/m)?.[1] ??
|
|
2486
|
-
const planId = `plan-${
|
|
3332
|
+
const content = fs6.readFileSync(event.filePath, "utf-8");
|
|
3333
|
+
const relativePath = path9.relative(vaultDir, event.filePath);
|
|
3334
|
+
const title = content.match(/^#\s+(.+)$/m)?.[1] ?? path9.basename(event.filePath);
|
|
3335
|
+
const planId = `plan-${path9.basename(event.filePath, ".md")}`;
|
|
2487
3336
|
indexAndEmbed(
|
|
2488
3337
|
relativePath,
|
|
2489
3338
|
planId,
|
|
@@ -2522,6 +3371,23 @@ ${content}`,
|
|
|
2522
3371
|
config,
|
|
2523
3372
|
log: (level, message, data) => logger[level]("digest", message, data)
|
|
2524
3373
|
});
|
|
3374
|
+
if (config.digest.consolidation) {
|
|
3375
|
+
const consolidationEngine = new ConsolidationEngine({
|
|
3376
|
+
vaultDir,
|
|
3377
|
+
index,
|
|
3378
|
+
vectorIndex,
|
|
3379
|
+
embeddingProvider,
|
|
3380
|
+
llmProvider: digestLlm,
|
|
3381
|
+
log: (level, message, data) => logger[level]("consolidation", message, data)
|
|
3382
|
+
});
|
|
3383
|
+
digestEngine.registerPrePass("consolidation", async () => {
|
|
3384
|
+
const result = await consolidationEngine.runPass();
|
|
3385
|
+
if (result && result.consolidated > 0) {
|
|
3386
|
+
logger.info("consolidation", `Consolidation pass: ${result.consolidated} wisdom notes, ${result.sporesSuperseded} spores superseded`);
|
|
3387
|
+
}
|
|
3388
|
+
});
|
|
3389
|
+
logger.info("consolidation", "Auto-consolidation enabled as digest pre-pass");
|
|
3390
|
+
}
|
|
2525
3391
|
metabolism = new Metabolism(config.digest.metabolism);
|
|
2526
3392
|
logger.debug("digest", "Firing initial digest cycle (background)");
|
|
2527
3393
|
digestEngine.runCycle().then((result) => {
|
|
@@ -2580,7 +3446,7 @@ ${content}`,
|
|
|
2580
3446
|
}
|
|
2581
3447
|
const captured = capturedArtifactPaths.get(sessionId);
|
|
2582
3448
|
for (const c of candidates) {
|
|
2583
|
-
const absPath =
|
|
3449
|
+
const absPath = path9.resolve(process.cwd(), c.path);
|
|
2584
3450
|
captured.add(absPath);
|
|
2585
3451
|
}
|
|
2586
3452
|
}).catch((err) => logger.warn("processor", "Incremental artifact capture failed", {
|
|
@@ -2590,8 +3456,8 @@ ${content}`,
|
|
|
2590
3456
|
}
|
|
2591
3457
|
}
|
|
2592
3458
|
});
|
|
2593
|
-
server.registerRoute("POST", "/sessions/register", async (
|
|
2594
|
-
const { session_id, branch, started_at } = RegisterBody.parse(body);
|
|
3459
|
+
server.registerRoute("POST", "/sessions/register", async (req) => {
|
|
3460
|
+
const { session_id, branch, started_at } = RegisterBody.parse(req.body);
|
|
2595
3461
|
const resolvedStartedAt = started_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2596
3462
|
registry.register(session_id, { started_at: resolvedStartedAt, branch });
|
|
2597
3463
|
server.updateDaemonJsonSessions(registry.sessions);
|
|
@@ -2613,21 +3479,21 @@ ${content}`,
|
|
|
2613
3479
|
}
|
|
2614
3480
|
metabolism?.activate();
|
|
2615
3481
|
logger.info("lifecycle", "Session registered", { session_id, branch });
|
|
2616
|
-
return { ok: true, sessions: registry.sessions };
|
|
3482
|
+
return { body: { ok: true, sessions: registry.sessions } };
|
|
2617
3483
|
});
|
|
2618
|
-
server.registerRoute("POST", "/sessions/unregister", async (
|
|
2619
|
-
const { session_id } = UnregisterBody.parse(body);
|
|
3484
|
+
server.registerRoute("POST", "/sessions/unregister", async (req) => {
|
|
3485
|
+
const { session_id } = UnregisterBody.parse(req.body);
|
|
2620
3486
|
registry.unregister(session_id);
|
|
2621
3487
|
try {
|
|
2622
3488
|
const cutoff = Date.now() - STALE_BUFFER_MAX_AGE_MS;
|
|
2623
|
-
for (const file of
|
|
3489
|
+
for (const file of fs6.readdirSync(bufferDir)) {
|
|
2624
3490
|
if (!file.endsWith(".jsonl")) continue;
|
|
2625
3491
|
const bufferSessionId = file.replace(".jsonl", "");
|
|
2626
3492
|
if (bufferSessionId === session_id) continue;
|
|
2627
|
-
const filePath =
|
|
2628
|
-
const stat4 =
|
|
3493
|
+
const filePath = path9.join(bufferDir, file);
|
|
3494
|
+
const stat4 = fs6.statSync(filePath);
|
|
2629
3495
|
if (stat4.mtimeMs < cutoff) {
|
|
2630
|
-
|
|
3496
|
+
fs6.unlinkSync(filePath);
|
|
2631
3497
|
logger.debug("daemon", "Cleaned stale buffer", { file });
|
|
2632
3498
|
}
|
|
2633
3499
|
}
|
|
@@ -2638,10 +3504,10 @@ ${content}`,
|
|
|
2638
3504
|
capturedArtifactPaths.delete(session_id);
|
|
2639
3505
|
server.updateDaemonJsonSessions(registry.sessions);
|
|
2640
3506
|
logger.info("lifecycle", "Session unregistered", { session_id });
|
|
2641
|
-
return { ok: true, sessions: registry.sessions };
|
|
3507
|
+
return { body: { ok: true, sessions: registry.sessions } };
|
|
2642
3508
|
});
|
|
2643
|
-
server.registerRoute("POST", "/events", async (
|
|
2644
|
-
const validated = EventBody.parse(body);
|
|
3509
|
+
server.registerRoute("POST", "/events", async (req) => {
|
|
3510
|
+
const validated = EventBody.parse(req.body);
|
|
2645
3511
|
const event = { ...validated, timestamp: validated.timestamp ?? (/* @__PURE__ */ new Date()).toISOString() };
|
|
2646
3512
|
logger.debug("hooks", "Event received", { type: event.type, session_id: event.session_id });
|
|
2647
3513
|
if (!registry.getSession(event.session_id)) {
|
|
@@ -2668,10 +3534,10 @@ ${content}`,
|
|
|
2668
3534
|
}
|
|
2669
3535
|
}
|
|
2670
3536
|
}
|
|
2671
|
-
return { ok: true };
|
|
3537
|
+
return { body: { ok: true } };
|
|
2672
3538
|
});
|
|
2673
|
-
server.registerRoute("POST", "/events/stop", async (
|
|
2674
|
-
const { session_id: sessionId, user, transcript_path: hookTranscriptPath, last_assistant_message: lastAssistantMessage } = StopBody.parse(body);
|
|
3539
|
+
server.registerRoute("POST", "/events/stop", async (req) => {
|
|
3540
|
+
const { session_id: sessionId, user, transcript_path: hookTranscriptPath, last_assistant_message: lastAssistantMessage } = StopBody.parse(req.body);
|
|
2675
3541
|
if (!registry.getSession(sessionId)) {
|
|
2676
3542
|
registry.register(sessionId, { started_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2677
3543
|
logger.debug("lifecycle", "Auto-registered session from stop event", { session_id: sessionId });
|
|
@@ -2685,7 +3551,7 @@ ${content}`,
|
|
|
2685
3551
|
activeStopProcessing = prev.then(run).finally(() => {
|
|
2686
3552
|
activeStopProcessing = null;
|
|
2687
3553
|
});
|
|
2688
|
-
return { ok: true };
|
|
3554
|
+
return { body: { ok: true } };
|
|
2689
3555
|
});
|
|
2690
3556
|
async function processStopEvent(sessionId, user, sessionMeta, hookTranscriptPath, lastAssistantMessage) {
|
|
2691
3557
|
const lastBatch = batchManager.finalize(sessionId);
|
|
@@ -2722,15 +3588,15 @@ ${content}`,
|
|
|
2722
3588
|
}
|
|
2723
3589
|
const ended = (/* @__PURE__ */ new Date()).toISOString();
|
|
2724
3590
|
let started = allTurns.length > 0 && allTurns[0].timestamp ? allTurns[0].timestamp : ended;
|
|
2725
|
-
const sessionsDir =
|
|
3591
|
+
const sessionsDir = path9.join(vaultDir, "sessions");
|
|
2726
3592
|
const sessionFileName = `${sessionNoteId(sessionId)}.md`;
|
|
2727
3593
|
let existingContent;
|
|
2728
3594
|
const duplicatePaths = [];
|
|
2729
3595
|
try {
|
|
2730
|
-
for (const dateDir of
|
|
2731
|
-
const candidate =
|
|
3596
|
+
for (const dateDir of fs6.readdirSync(sessionsDir)) {
|
|
3597
|
+
const candidate = path9.join(sessionsDir, dateDir, sessionFileName);
|
|
2732
3598
|
try {
|
|
2733
|
-
const content =
|
|
3599
|
+
const content = fs6.readFileSync(candidate, "utf-8");
|
|
2734
3600
|
if (!existingContent || content.length > existingContent.length) {
|
|
2735
3601
|
existingContent = content;
|
|
2736
3602
|
}
|
|
@@ -2793,20 +3659,20 @@ ${conversationText}`;
|
|
|
2793
3659
|
}
|
|
2794
3660
|
const date = started.slice(0, 10);
|
|
2795
3661
|
const relativePath = sessionRelativePath(sessionId, date);
|
|
2796
|
-
const targetFullPath =
|
|
3662
|
+
const targetFullPath = path9.join(vaultDir, relativePath);
|
|
2797
3663
|
for (const dup of duplicatePaths) {
|
|
2798
3664
|
if (dup !== targetFullPath) {
|
|
2799
3665
|
try {
|
|
2800
|
-
|
|
3666
|
+
fs6.unlinkSync(dup);
|
|
2801
3667
|
logger.debug("lifecycle", "Removed duplicate session file", { path: dup });
|
|
2802
3668
|
} catch {
|
|
2803
3669
|
}
|
|
2804
3670
|
}
|
|
2805
3671
|
}
|
|
2806
|
-
const attachmentsDir =
|
|
3672
|
+
const attachmentsDir = path9.join(vaultDir, "attachments");
|
|
2807
3673
|
const hasImages = allTurns.some((t) => t.images?.length);
|
|
2808
3674
|
if (hasImages) {
|
|
2809
|
-
|
|
3675
|
+
fs6.mkdirSync(attachmentsDir, { recursive: true });
|
|
2810
3676
|
}
|
|
2811
3677
|
const turnImageNames = /* @__PURE__ */ new Map();
|
|
2812
3678
|
for (let i = 0; i < allTurns.length; i++) {
|
|
@@ -2817,9 +3683,9 @@ ${conversationText}`;
|
|
|
2817
3683
|
const img = turn.images[j];
|
|
2818
3684
|
const ext = extensionForMimeType(img.mediaType);
|
|
2819
3685
|
const filename = `${bareSessionId(sessionId)}-t${i + 1}-${j + 1}.${ext}`;
|
|
2820
|
-
const filePath =
|
|
2821
|
-
if (!
|
|
2822
|
-
|
|
3686
|
+
const filePath = path9.join(attachmentsDir, filename);
|
|
3687
|
+
if (!fs6.existsSync(filePath)) {
|
|
3688
|
+
fs6.writeFileSync(filePath, Buffer.from(img.data, "base64"));
|
|
2823
3689
|
logger.debug("processor", "Image saved", { filename, turn: i + 1 });
|
|
2824
3690
|
}
|
|
2825
3691
|
names.push(filename);
|
|
@@ -2920,8 +3786,8 @@ ${conversationText}`;
|
|
|
2920
3786
|
}
|
|
2921
3787
|
logger.info("processor", "Session note written", { session_id: sessionId, path: relativePath });
|
|
2922
3788
|
}
|
|
2923
|
-
server.registerRoute("POST", "/context", async (
|
|
2924
|
-
const { session_id, branch } = ContextBody.parse(body);
|
|
3789
|
+
server.registerRoute("POST", "/context", async (req) => {
|
|
3790
|
+
const { session_id, branch } = ContextBody.parse(req.body);
|
|
2925
3791
|
logger.debug("hooks", "Session context query", { session_id });
|
|
2926
3792
|
try {
|
|
2927
3793
|
if (config.digest.enabled && config.digest.inject_tier) {
|
|
@@ -2932,7 +3798,7 @@ ${conversationText}`;
|
|
|
2932
3798
|
Branch:: \`${branch}\``);
|
|
2933
3799
|
meta.push(`Session:: \`${session_id}\``);
|
|
2934
3800
|
logger.debug("context", `Injecting digest extract (tier ${result.tier})`, { session_id, fallback: result.fallback });
|
|
2935
|
-
return { text: meta.join("\n\n"), source: "digest", tier: result.tier };
|
|
3801
|
+
return { body: { text: meta.join("\n\n"), source: "digest", tier: result.tier } };
|
|
2936
3802
|
}
|
|
2937
3803
|
}
|
|
2938
3804
|
const parts = [];
|
|
@@ -2965,22 +3831,22 @@ ${planLines.join("\n")}`);
|
|
|
2965
3831
|
}
|
|
2966
3832
|
parts.push(`Session:: \`${session_id}\``);
|
|
2967
3833
|
if (parts.length > 0) {
|
|
2968
|
-
return { text: parts.join("\n\n") };
|
|
3834
|
+
return { body: { text: parts.join("\n\n") } };
|
|
2969
3835
|
}
|
|
2970
|
-
return { text: "" };
|
|
3836
|
+
return { body: { text: "" } };
|
|
2971
3837
|
} catch (error) {
|
|
2972
3838
|
logger.error("daemon", "Session context failed", { error: error.message });
|
|
2973
|
-
return { text: "" };
|
|
3839
|
+
return { body: { text: "" } };
|
|
2974
3840
|
}
|
|
2975
3841
|
});
|
|
2976
3842
|
const PromptContextBody = external_exports.object({
|
|
2977
3843
|
prompt: external_exports.string(),
|
|
2978
3844
|
session_id: external_exports.string().optional()
|
|
2979
3845
|
});
|
|
2980
|
-
server.registerRoute("POST", "/context/prompt", async (
|
|
2981
|
-
const { prompt, session_id } = PromptContextBody.parse(body);
|
|
3846
|
+
server.registerRoute("POST", "/context/prompt", async (req) => {
|
|
3847
|
+
const { prompt, session_id } = PromptContextBody.parse(req.body);
|
|
2982
3848
|
if (!prompt || prompt.length < PROMPT_CONTEXT_MIN_LENGTH || !vectorIndex) {
|
|
2983
|
-
return { text: "" };
|
|
3849
|
+
return { body: { text: "" } };
|
|
2984
3850
|
}
|
|
2985
3851
|
try {
|
|
2986
3852
|
const emb = await generateEmbedding(embeddingProvider, prompt.slice(0, EMBEDDING_INPUT_LIMIT));
|
|
@@ -2989,7 +3855,7 @@ ${planLines.join("\n")}`);
|
|
|
2989
3855
|
type: "spore",
|
|
2990
3856
|
relativeThreshold: PROMPT_CONTEXT_MIN_SIMILARITY
|
|
2991
3857
|
});
|
|
2992
|
-
if (results.length === 0) return { text: "" };
|
|
3858
|
+
if (results.length === 0) return { body: { text: "" } };
|
|
2993
3859
|
const noteMap = new Map(
|
|
2994
3860
|
index.queryByIds(results.map((r) => r.id)).map((n) => [n.id, n])
|
|
2995
3861
|
);
|
|
@@ -3002,7 +3868,7 @@ ${planLines.join("\n")}`);
|
|
|
3002
3868
|
const obsType = fm.observation_type ?? "note";
|
|
3003
3869
|
lines.push(`- [${obsType}] ${note.title}: ${note.content.slice(0, CONTENT_SNIPPET_CHARS)} \`[${note.id}]\``);
|
|
3004
3870
|
}
|
|
3005
|
-
if (lines.length === 0) return { text: "" };
|
|
3871
|
+
if (lines.length === 0) return { body: { text: "" } };
|
|
3006
3872
|
const injected = `**Relevant spores for this task:**
|
|
3007
3873
|
${lines.join("\n")}`;
|
|
3008
3874
|
logger.debug("context", "Prompt context injected", {
|
|
@@ -3010,14 +3876,72 @@ ${lines.join("\n")}`;
|
|
|
3010
3876
|
spores: lines.length,
|
|
3011
3877
|
prompt_preview: prompt.slice(0, 50)
|
|
3012
3878
|
});
|
|
3013
|
-
return { text: injected };
|
|
3879
|
+
return { body: { text: injected } };
|
|
3014
3880
|
} catch (err) {
|
|
3015
3881
|
logger.debug("context", "Prompt context failed", { error: err.message });
|
|
3016
|
-
return { text: "" };
|
|
3882
|
+
return { body: { text: "" } };
|
|
3017
3883
|
}
|
|
3018
3884
|
});
|
|
3019
|
-
|
|
3885
|
+
const progressTracker = new ProgressTracker();
|
|
3886
|
+
let configHash = computeConfigHash(vaultDir);
|
|
3887
|
+
server.registerRoute("GET", "/api/config", async () => handleGetConfig(vaultDir));
|
|
3888
|
+
server.registerRoute("PUT", "/api/config", async (req) => {
|
|
3889
|
+
const result = await handlePutConfig(vaultDir, req.body);
|
|
3890
|
+
if (!result.status || result.status < 400) {
|
|
3891
|
+
configHash = computeConfigHash(vaultDir);
|
|
3892
|
+
}
|
|
3893
|
+
return result;
|
|
3894
|
+
});
|
|
3895
|
+
server.registerRoute("GET", "/api/stats", async () => handleGetStats({
|
|
3896
|
+
vaultDir,
|
|
3897
|
+
index,
|
|
3898
|
+
vectorIndex,
|
|
3899
|
+
version: server.version,
|
|
3900
|
+
config,
|
|
3901
|
+
configHash,
|
|
3902
|
+
metabolism
|
|
3903
|
+
}));
|
|
3904
|
+
server.registerRoute("GET", "/api/logs", async (req) => handleGetLogs(logger.getRingBuffer(), req.query));
|
|
3905
|
+
server.registerRoute("GET", "/api/models", async (req) => handleGetModels(req));
|
|
3906
|
+
server.registerRoute("POST", "/api/restart", async (req) => handleRestart({ vaultDir, progressTracker }, req.body));
|
|
3907
|
+
server.registerRoute("GET", "/api/progress/:token", async (req) => handleGetProgress(progressTracker, req.params.token));
|
|
3908
|
+
const operationDeps = {
|
|
3909
|
+
vaultDir,
|
|
3910
|
+
config,
|
|
3911
|
+
index,
|
|
3912
|
+
vectorIndex,
|
|
3913
|
+
llmProvider,
|
|
3914
|
+
embeddingProvider,
|
|
3915
|
+
progressTracker,
|
|
3916
|
+
log: (level, message, data) => {
|
|
3917
|
+
const fn = logger[level];
|
|
3918
|
+
if (typeof fn === "function") fn.call(logger, "operations", message, data);
|
|
3919
|
+
}
|
|
3920
|
+
};
|
|
3921
|
+
server.registerRoute("POST", "/api/rebuild", async () => handleRebuild(operationDeps));
|
|
3922
|
+
server.registerRoute("POST", "/api/digest", async (req) => handleDigest(operationDeps, req.body));
|
|
3923
|
+
server.registerRoute("POST", "/api/curate", async (req) => handleCurate(operationDeps, req.body, runCuration));
|
|
3924
|
+
const resolvedPort = await resolvePort(config.daemon.port, vaultDir);
|
|
3925
|
+
if (resolvedPort === 0) {
|
|
3926
|
+
logger.warn("daemon", "All preferred ports occupied, using ephemeral port");
|
|
3927
|
+
}
|
|
3928
|
+
await server.start(resolvedPort);
|
|
3020
3929
|
logger.info("daemon", "Daemon ready", { vault: vaultDir, port: server.port });
|
|
3930
|
+
if (config.daemon.port === null && resolvedPort !== 0) {
|
|
3931
|
+
try {
|
|
3932
|
+
config.daemon.port = resolvedPort;
|
|
3933
|
+
saveConfig(vaultDir, config);
|
|
3934
|
+
logger.info("daemon", "Persisted auto-derived port to myco.yaml", { port: resolvedPort });
|
|
3935
|
+
} catch (err) {
|
|
3936
|
+
logger.warn("daemon", "Failed to persist auto-derived port", { error: err.message });
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
try {
|
|
3940
|
+
const { loadTemplate } = await import("./templates-XPRBOWCE.js");
|
|
3941
|
+
const portalContent = loadTemplate("portal", { port: String(server.port) });
|
|
3942
|
+
fs6.writeFileSync(path9.join(vaultDir, "_portal.md"), portalContent, "utf-8");
|
|
3943
|
+
} catch {
|
|
3944
|
+
}
|
|
3021
3945
|
if (needsMigrationReindex) {
|
|
3022
3946
|
setImmediate(() => {
|
|
3023
3947
|
logger.info("daemon", "Rebuilding index after memories\u2192spores migration (background)");
|
|
@@ -3053,4 +3977,4 @@ export {
|
|
|
3053
3977
|
chokidar/index.js:
|
|
3054
3978
|
(*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) *)
|
|
3055
3979
|
*/
|
|
3056
|
-
//# sourceMappingURL=main-
|
|
3980
|
+
//# sourceMappingURL=main-JEUQS3BY.js.map
|