stellavault 0.8.4 → 0.8.5
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/README.ja.md +469 -0
- package/README.ko.md +469 -0
- package/README.md +470 -372
- package/README.zh.md +469 -0
- package/dist/graph-ui/assets/{camera_utils-DBFmYlaE.js → camera_utils-C1y6pgyV.js} +1 -1
- package/dist/graph-ui/assets/{hands-CBFUH2Er.js → hands-BbZLpLop.js} +1 -1
- package/dist/graph-ui/assets/index-KgyFgRwK.js +4435 -0
- package/dist/graph-ui/index.html +1 -1
- package/dist/stellavault.js +989 -415
- package/package.json +21 -6
- package/dist/graph-ui/assets/index-BNGAzxHC.js +0 -4426
package/dist/stellavault.js
CHANGED
|
@@ -159,6 +159,29 @@ function findMdFiles(dir, files = []) {
|
|
|
159
159
|
}
|
|
160
160
|
return files;
|
|
161
161
|
}
|
|
162
|
+
function scanFile(vaultPath, filePath) {
|
|
163
|
+
const rel = relative(vaultPath, filePath).replace(/\\/g, "/");
|
|
164
|
+
try {
|
|
165
|
+
const stat = statSync(filePath);
|
|
166
|
+
if (stat.size === 0)
|
|
167
|
+
return { skipped: { path: rel, reason: "empty" } };
|
|
168
|
+
if (stat.size > MAX_FILE_BYTES)
|
|
169
|
+
return { skipped: { path: rel, reason: "too-large", detail: `${stat.size}B` } };
|
|
170
|
+
const doc = parseDocument(vaultPath, filePath);
|
|
171
|
+
if (!doc.content || doc.content.trim().length === 0) {
|
|
172
|
+
return { skipped: { path: rel, reason: "empty", detail: "no content after frontmatter" } };
|
|
173
|
+
}
|
|
174
|
+
return { document: doc };
|
|
175
|
+
} catch (err) {
|
|
176
|
+
const msg = err?.message ?? String(err);
|
|
177
|
+
const reason = /ENOENT|EACCES|EPERM/.test(msg) ? "unreadable" : "parse-error";
|
|
178
|
+
return { skipped: { path: rel, reason, detail: msg.slice(0, 200) } };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function docIdForPath(vaultPath, filePath) {
|
|
182
|
+
const relativePath = relative(vaultPath, filePath).replace(/\\/g, "/");
|
|
183
|
+
return createHash("sha256").update(relativePath).digest("hex").slice(0, 16);
|
|
184
|
+
}
|
|
162
185
|
function parseDocument(vaultPath, filePath) {
|
|
163
186
|
const raw = readFileSync2(filePath, "utf-8");
|
|
164
187
|
const stat = statSync(filePath);
|
|
@@ -654,12 +677,25 @@ var init_retry = __esm({
|
|
|
654
677
|
});
|
|
655
678
|
|
|
656
679
|
// packages/core/dist/indexer/local-embedder.js
|
|
680
|
+
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
681
|
+
import { join as join3 } from "node:path";
|
|
682
|
+
import { homedir as homedir2 } from "node:os";
|
|
657
683
|
function getPipeline(modelName) {
|
|
658
684
|
let p = pipelineCache.get(modelName);
|
|
659
685
|
if (p)
|
|
660
686
|
return p;
|
|
661
687
|
p = (async () => {
|
|
662
|
-
const { pipeline: createPipeline } = await import("@xenova/transformers");
|
|
688
|
+
const { pipeline: createPipeline, env } = await import("@xenova/transformers");
|
|
689
|
+
const cacheOverride = process.env.STELLAVAULT_MODEL_CACHE;
|
|
690
|
+
if (cacheOverride) {
|
|
691
|
+
env.cacheDir = cacheOverride;
|
|
692
|
+
} else if (/\.asar[\\/]/.test(env.cacheDir ?? "")) {
|
|
693
|
+
env.cacheDir = join3(homedir2(), ".stellavault", "model-cache");
|
|
694
|
+
}
|
|
695
|
+
try {
|
|
696
|
+
mkdirSync2(env.cacheDir, { recursive: true });
|
|
697
|
+
} catch {
|
|
698
|
+
}
|
|
663
699
|
return createPipeline("feature-extraction", `Xenova/${modelName}`, { quantized: true });
|
|
664
700
|
})();
|
|
665
701
|
pipelineCache.set(modelName, p);
|
|
@@ -675,6 +711,8 @@ function createLocalEmbedder(modelName = "nomic-embed-text-v1.5") {
|
|
|
675
711
|
pipeline = await getPipeline(modelName);
|
|
676
712
|
},
|
|
677
713
|
async embed(text) {
|
|
714
|
+
if (!pipeline)
|
|
715
|
+
pipeline = await getPipeline(modelName);
|
|
678
716
|
let output = await pipeline(text, { pooling: "mean", normalize: true });
|
|
679
717
|
const result = Array.from(output.data).slice(0, dims);
|
|
680
718
|
try {
|
|
@@ -685,6 +723,8 @@ function createLocalEmbedder(modelName = "nomic-embed-text-v1.5") {
|
|
|
685
723
|
return result;
|
|
686
724
|
},
|
|
687
725
|
async embedBatch(texts, batchSize = 16) {
|
|
726
|
+
if (!pipeline)
|
|
727
|
+
pipeline = await getPipeline(modelName);
|
|
688
728
|
const results = [];
|
|
689
729
|
let processed = 0;
|
|
690
730
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
@@ -793,10 +833,14 @@ __export(indexer_exports, {
|
|
|
793
833
|
chunkDocument: () => chunkDocument,
|
|
794
834
|
createLocalEmbedder: () => createLocalEmbedder,
|
|
795
835
|
createWatcher: () => createWatcher,
|
|
836
|
+
docIdForPath: () => docIdForPath,
|
|
796
837
|
estimateTokens: () => estimateTokens,
|
|
838
|
+
indexFiles: () => indexFiles,
|
|
797
839
|
indexVault: () => indexVault,
|
|
840
|
+
scanFile: () => scanFile,
|
|
798
841
|
scanVault: () => scanVault
|
|
799
842
|
});
|
|
843
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
800
844
|
async function indexVault(vaultPath, options) {
|
|
801
845
|
const start = Date.now();
|
|
802
846
|
const { store, embedder, chunkOptions, onProgress } = options;
|
|
@@ -861,6 +905,80 @@ async function indexVault(vaultPath, options) {
|
|
|
861
905
|
failedFiles
|
|
862
906
|
};
|
|
863
907
|
}
|
|
908
|
+
async function indexFiles(vaultPath, filePaths, options) {
|
|
909
|
+
const start = Date.now();
|
|
910
|
+
const { store, embedder, chunkOptions, onProgress } = options;
|
|
911
|
+
const existingDocs = await store.getAllDocuments();
|
|
912
|
+
const existingMap = new Map(existingDocs.map((d) => [d.id, d.contentHash]));
|
|
913
|
+
let indexed = 0;
|
|
914
|
+
let skipped = 0;
|
|
915
|
+
let deleted = 0;
|
|
916
|
+
let failed = 0;
|
|
917
|
+
let totalChunks = 0;
|
|
918
|
+
const skippedFiles = [];
|
|
919
|
+
const failedFiles = [];
|
|
920
|
+
const uniquePaths = [...new Set(filePaths)];
|
|
921
|
+
for (let i = 0; i < uniquePaths.length; i++) {
|
|
922
|
+
const filePath = uniquePaths[i];
|
|
923
|
+
if (!existsSync2(filePath)) {
|
|
924
|
+
try {
|
|
925
|
+
await store.deleteByDocumentId(docIdForPath(vaultPath, filePath));
|
|
926
|
+
deleted++;
|
|
927
|
+
} catch (err) {
|
|
928
|
+
failed++;
|
|
929
|
+
failedFiles.push({ path: filePath, error: err?.message ?? String(err) });
|
|
930
|
+
console.error(errors.indexingFailed(filePath, err).format());
|
|
931
|
+
}
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
const result = scanFile(vaultPath, filePath);
|
|
935
|
+
if ("skipped" in result) {
|
|
936
|
+
skipped++;
|
|
937
|
+
skippedFiles.push(result.skipped);
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
const doc = result.document;
|
|
941
|
+
onProgress?.(i + 1, uniquePaths.length, doc);
|
|
942
|
+
if (existingMap.get(doc.id) === doc.contentHash) {
|
|
943
|
+
skipped++;
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
const chunks = chunkDocument(doc.id, doc.content, chunkOptions);
|
|
948
|
+
const texts = chunks.map((c) => c.content);
|
|
949
|
+
const embeddings = await withRetry(() => embedder.embedBatch(texts), { maxRetries: 2, baseDelayMs: 1e3 });
|
|
950
|
+
const chunksWithEmbeddings = chunks.map((c, j) => ({
|
|
951
|
+
...c,
|
|
952
|
+
embedding: embeddings[j],
|
|
953
|
+
entities: extractEntities({
|
|
954
|
+
content: c.content,
|
|
955
|
+
heading: c.heading,
|
|
956
|
+
title: doc.title,
|
|
957
|
+
tags: doc.tags
|
|
958
|
+
})
|
|
959
|
+
}));
|
|
960
|
+
await store.upsertDocument(doc);
|
|
961
|
+
await store.upsertChunks(chunksWithEmbeddings);
|
|
962
|
+
indexed++;
|
|
963
|
+
totalChunks += chunks.length;
|
|
964
|
+
} catch (err) {
|
|
965
|
+
failed++;
|
|
966
|
+
failedFiles.push({ path: doc.filePath, error: err?.message ?? String(err) });
|
|
967
|
+
console.error(errors.indexingFailed(doc.filePath, err).format());
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return {
|
|
971
|
+
indexed,
|
|
972
|
+
skipped,
|
|
973
|
+
deleted,
|
|
974
|
+
failed,
|
|
975
|
+
totalChunks,
|
|
976
|
+
elapsedMs: Date.now() - start,
|
|
977
|
+
totalFiles: uniquePaths.length,
|
|
978
|
+
skippedFiles,
|
|
979
|
+
failedFiles
|
|
980
|
+
};
|
|
981
|
+
}
|
|
864
982
|
var init_indexer = __esm({
|
|
865
983
|
"packages/core/dist/indexer/index.js"() {
|
|
866
984
|
"use strict";
|
|
@@ -875,76 +993,52 @@ var init_indexer = __esm({
|
|
|
875
993
|
}
|
|
876
994
|
});
|
|
877
995
|
|
|
878
|
-
// packages/core/dist/utils/math.js
|
|
879
|
-
function cosineSimilarity(a, b) {
|
|
880
|
-
if (a.length !== b.length)
|
|
881
|
-
return 0;
|
|
882
|
-
let dot = 0, normA = 0, normB = 0;
|
|
883
|
-
for (let i = 0; i < a.length; i++) {
|
|
884
|
-
dot += a[i] * b[i];
|
|
885
|
-
normA += a[i] * a[i];
|
|
886
|
-
normB += b[i] * b[i];
|
|
887
|
-
}
|
|
888
|
-
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
889
|
-
return denom === 0 ? 0 : dot / denom;
|
|
890
|
-
}
|
|
891
|
-
function dotProduct(a, b) {
|
|
892
|
-
let sum = 0;
|
|
893
|
-
for (let i = 0; i < a.length; i++)
|
|
894
|
-
sum += a[i] * b[i];
|
|
895
|
-
return sum;
|
|
896
|
-
}
|
|
897
|
-
function normalizeVector(v) {
|
|
898
|
-
let norm = 0;
|
|
899
|
-
for (let i = 0; i < v.length; i++)
|
|
900
|
-
norm += v[i] * v[i];
|
|
901
|
-
norm = Math.sqrt(norm);
|
|
902
|
-
if (norm === 0)
|
|
903
|
-
return v;
|
|
904
|
-
for (let i = 0; i < v.length; i++)
|
|
905
|
-
v[i] /= norm;
|
|
906
|
-
return v;
|
|
907
|
-
}
|
|
908
|
-
function euclideanDist(a, b) {
|
|
909
|
-
let sum = 0;
|
|
910
|
-
for (let i = 0; i < a.length; i++) {
|
|
911
|
-
const diff = a[i] - b[i];
|
|
912
|
-
sum += diff * diff;
|
|
913
|
-
}
|
|
914
|
-
return Math.sqrt(sum);
|
|
915
|
-
}
|
|
916
|
-
var init_math = __esm({
|
|
917
|
-
"packages/core/dist/utils/math.js"() {
|
|
918
|
-
"use strict";
|
|
919
|
-
}
|
|
920
|
-
});
|
|
921
|
-
|
|
922
996
|
// packages/core/dist/api/graph-data.js
|
|
923
997
|
var graph_data_exports = {};
|
|
924
998
|
__export(graph_data_exports, {
|
|
999
|
+
buildClusteredGraph: () => buildClusteredGraph,
|
|
925
1000
|
buildGraphData: () => buildGraphData
|
|
926
1001
|
});
|
|
927
1002
|
import { createHash as createHash2 } from "node:crypto";
|
|
928
1003
|
async function buildGraphData(store, options = {}) {
|
|
929
|
-
const {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1004
|
+
const {
|
|
1005
|
+
// 0.35: 384-dim 임베딩에서 의미있는 코사인 임계. 0.15는 사실상 모든 페어가 통과 →
|
|
1006
|
+
// 중간 neighbor 배열이 수백만 객체로 폭증(빌드 지연) + 시각적으로 과밀한 엣지 거미줄.
|
|
1007
|
+
edgeThreshold = 0.35,
|
|
1008
|
+
maxEdgesPerNode = 5
|
|
1009
|
+
} = options;
|
|
1010
|
+
const docs = await store.getDocumentsMeta();
|
|
934
1011
|
const edges = [];
|
|
935
1012
|
const edgeCounts = /* @__PURE__ */ new Map();
|
|
936
|
-
const
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
}
|
|
941
|
-
const
|
|
942
|
-
const
|
|
1013
|
+
const NODE_CAP = Math.max(200, Math.floor(options.nodeCap ?? (Number(process.env.GRAPH_NODE_CAP) || 1500)));
|
|
1014
|
+
const ranked = [...docs].sort((a, b) => String(b.lastModified ?? "").localeCompare(String(a.lastModified ?? "")));
|
|
1015
|
+
if (docs.length > NODE_CAP) {
|
|
1016
|
+
console.warn(`[graph] capped to ${NODE_CAP} most-recent notes (of ${docs.length}) \u2014 raise GRAPH_NODE_CAP to include more.`);
|
|
1017
|
+
}
|
|
1018
|
+
const embeddings = await store.getDocumentEmbeddingsByIds(ranked.slice(0, NODE_CAP).map((d) => d.id));
|
|
1019
|
+
const docsWithVecs = ranked.filter((d) => embeddings.has(d.id)).slice(0, NODE_CAP);
|
|
1020
|
+
const docIds = docsWithVecs.map((d) => d.id);
|
|
943
1021
|
const n = docIds.length;
|
|
1022
|
+
const dim = n > 0 ? embeddings.get(docIds[0]).length : 0;
|
|
1023
|
+
const flat = new Float32Array(n * dim);
|
|
1024
|
+
for (let i = 0; i < n; i++) {
|
|
1025
|
+
const v = embeddings.get(docIds[i]);
|
|
1026
|
+
let mag = 0;
|
|
1027
|
+
for (let d = 0; d < dim; d++)
|
|
1028
|
+
mag += v[d] * v[d];
|
|
1029
|
+
mag = Math.sqrt(mag) || 1;
|
|
1030
|
+
const off = i * dim;
|
|
1031
|
+
for (let d = 0; d < dim; d++)
|
|
1032
|
+
flat[off + d] = v[d] / mag;
|
|
1033
|
+
}
|
|
944
1034
|
const neighbors = Array.from({ length: n }, () => []);
|
|
945
1035
|
for (let i = 0; i < n; i++) {
|
|
1036
|
+
const oi = i * dim;
|
|
946
1037
|
for (let j = i + 1; j < n; j++) {
|
|
947
|
-
const
|
|
1038
|
+
const oj = j * dim;
|
|
1039
|
+
let sim = 0;
|
|
1040
|
+
for (let d = 0; d < dim; d++)
|
|
1041
|
+
sim += flat[oi + d] * flat[oj + d];
|
|
948
1042
|
if (sim >= edgeThreshold) {
|
|
949
1043
|
neighbors[i].push({ peer: j, sim });
|
|
950
1044
|
neighbors[j].push({ peer: i, sim });
|
|
@@ -991,16 +1085,16 @@ async function buildGraphData(store, options = {}) {
|
|
|
991
1085
|
nodeCount: folderCounts.get(i) ?? 0
|
|
992
1086
|
}));
|
|
993
1087
|
} else {
|
|
994
|
-
const
|
|
995
|
-
const
|
|
996
|
-
const
|
|
997
|
-
const
|
|
1088
|
+
const clusterIds = docIds;
|
|
1089
|
+
const k = options.clusterCount && options.clusterCount > 0 ? Math.floor(options.clusterCount) : Math.min(Math.max(5, Math.round(Math.sqrt(clusterIds.length / 5))), 10);
|
|
1090
|
+
const assignments = kMeans(flat, n, dim, Math.min(k, clusterIds.length || 1), 10);
|
|
1091
|
+
const docById = new Map(docsWithVecs.map((d) => [d.id, d]));
|
|
998
1092
|
const clusterDocInfos = /* @__PURE__ */ new Map();
|
|
999
|
-
for (let i = 0; i <
|
|
1093
|
+
for (let i = 0; i < clusterIds.length; i++) {
|
|
1000
1094
|
const cId = assignments[i];
|
|
1001
1095
|
if (!clusterDocInfos.has(cId))
|
|
1002
1096
|
clusterDocInfos.set(cId, []);
|
|
1003
|
-
const doc =
|
|
1097
|
+
const doc = docById.get(clusterIds[i]);
|
|
1004
1098
|
if (doc)
|
|
1005
1099
|
clusterDocInfos.get(cId).push({ id: doc.id, title: doc.title });
|
|
1006
1100
|
}
|
|
@@ -1021,8 +1115,8 @@ async function buildGraphData(store, options = {}) {
|
|
|
1021
1115
|
});
|
|
1022
1116
|
}
|
|
1023
1117
|
assignmentMap = /* @__PURE__ */ new Map();
|
|
1024
|
-
for (let i = 0; i <
|
|
1025
|
-
assignmentMap.set(
|
|
1118
|
+
for (let i = 0; i < clusterIds.length; i++) {
|
|
1119
|
+
assignmentMap.set(clusterIds[i], assignments[i]);
|
|
1026
1120
|
}
|
|
1027
1121
|
}
|
|
1028
1122
|
const connectionCounts = /* @__PURE__ */ new Map();
|
|
@@ -1031,7 +1125,7 @@ async function buildGraphData(store, options = {}) {
|
|
|
1031
1125
|
connectionCounts.set(edge.target, (connectionCounts.get(edge.target) ?? 0) + 1);
|
|
1032
1126
|
}
|
|
1033
1127
|
const maxConnections = Math.max(1, ...connectionCounts.values());
|
|
1034
|
-
const nodes =
|
|
1128
|
+
const nodes = docsWithVecs.map((doc) => {
|
|
1035
1129
|
const conns = connectionCounts.get(doc.id) ?? 0;
|
|
1036
1130
|
const ratio = conns / maxConnections;
|
|
1037
1131
|
const size = 1 + 6 * Math.pow(ratio, 0.5);
|
|
@@ -1059,40 +1153,278 @@ async function buildGraphData(store, options = {}) {
|
|
|
1059
1153
|
}
|
|
1060
1154
|
};
|
|
1061
1155
|
}
|
|
1062
|
-
function
|
|
1063
|
-
|
|
1156
|
+
async function buildClusteredGraph(store, options = {}) {
|
|
1157
|
+
const mode = options.mode ?? "semantic";
|
|
1158
|
+
const clusterCap = Math.max(200, Math.floor(options.clusterCap ?? (Number(process.env.GRAPH_CLUSTER_CAP) || 3e3)));
|
|
1159
|
+
const clusterCount = Math.max(1, Math.floor(options.clusterCount ?? Math.min(80, Math.max(6, Math.round(Math.sqrt(clusterCap / 2.5))))));
|
|
1160
|
+
const data = await buildGraphData(store, {
|
|
1161
|
+
mode,
|
|
1162
|
+
nodeCap: clusterCap,
|
|
1163
|
+
clusterCount,
|
|
1164
|
+
edgeThreshold: options.edgeThreshold,
|
|
1165
|
+
maxEdgesPerNode: options.maxEdgesPerNode
|
|
1166
|
+
});
|
|
1167
|
+
const nodeCluster = /* @__PURE__ */ new Map();
|
|
1168
|
+
const byCluster = /* @__PURE__ */ new Map();
|
|
1169
|
+
for (const node of data.nodes) {
|
|
1170
|
+
const cid = node.clusterId ?? 0;
|
|
1171
|
+
nodeCluster.set(node.id, cid);
|
|
1172
|
+
(byCluster.get(cid) ?? byCluster.set(cid, []).get(cid)).push(node);
|
|
1173
|
+
}
|
|
1174
|
+
const degree = /* @__PURE__ */ new Map();
|
|
1175
|
+
for (const e of data.edges) {
|
|
1176
|
+
degree.set(e.source, (degree.get(e.source) ?? 0) + 1);
|
|
1177
|
+
degree.set(e.target, (degree.get(e.target) ?? 0) + 1);
|
|
1178
|
+
}
|
|
1179
|
+
const intraByCluster = /* @__PURE__ */ new Map();
|
|
1180
|
+
const metaMap = /* @__PURE__ */ new Map();
|
|
1181
|
+
for (const e of data.edges) {
|
|
1182
|
+
const ca = nodeCluster.get(e.source), cb = nodeCluster.get(e.target);
|
|
1183
|
+
if (ca == null || cb == null)
|
|
1184
|
+
continue;
|
|
1185
|
+
if (ca === cb) {
|
|
1186
|
+
(intraByCluster.get(ca) ?? intraByCluster.set(ca, []).get(ca)).push(e);
|
|
1187
|
+
} else {
|
|
1188
|
+
const lo = Math.min(ca, cb), hi = Math.max(ca, cb);
|
|
1189
|
+
const key = `${lo}:${hi}`;
|
|
1190
|
+
const m = metaMap.get(key);
|
|
1191
|
+
if (m) {
|
|
1192
|
+
m.weight += e.weight;
|
|
1193
|
+
m.count += 1;
|
|
1194
|
+
} else
|
|
1195
|
+
metaMap.set(key, { sourceCluster: lo, targetCluster: hi, weight: e.weight, count: 1 });
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
const clusterLabel = /* @__PURE__ */ new Map();
|
|
1199
|
+
for (const c of data.clusters)
|
|
1200
|
+
clusterLabel.set(c.id, c.label);
|
|
1201
|
+
const superNodes = [];
|
|
1202
|
+
for (const [cid, mem] of byCluster) {
|
|
1203
|
+
let rep = mem[0], repDeg = -1;
|
|
1204
|
+
for (const m of mem) {
|
|
1205
|
+
const d = degree.get(m.id) ?? 0;
|
|
1206
|
+
if (d > repDeg) {
|
|
1207
|
+
repDeg = d;
|
|
1208
|
+
rep = m;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
superNodes.push({
|
|
1212
|
+
clusterId: cid,
|
|
1213
|
+
// strip buildGraphData's trailing " (N)" — memberCount is a separate field.
|
|
1214
|
+
label: (clusterLabel.get(cid) ?? `Cluster ${cid + 1}`).replace(/\s*\(\d+\)\s*$/, ""),
|
|
1215
|
+
color: CLUSTER_COLORS[cid % CLUSTER_COLORS.length],
|
|
1216
|
+
memberCount: mem.length,
|
|
1217
|
+
position: [0, 0, 0],
|
|
1218
|
+
// assigned below by Fibonacci rank
|
|
1219
|
+
size: 2 + Math.min(12, Math.sqrt(mem.length)),
|
|
1220
|
+
representativeId: rep?.id ?? ""
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
superNodes.sort((a, b) => b.memberCount - a.memberCount);
|
|
1224
|
+
layoutSuperNodes(superNodes, metaMap);
|
|
1225
|
+
const members = /* @__PURE__ */ new Map();
|
|
1226
|
+
for (const [cid, mem] of byCluster) {
|
|
1227
|
+
const boundaryEdges = [];
|
|
1228
|
+
for (const e of data.edges) {
|
|
1229
|
+
const ca = nodeCluster.get(e.source), cb = nodeCluster.get(e.target);
|
|
1230
|
+
if (ca === cid && cb !== cid && cb != null)
|
|
1231
|
+
boundaryEdges.push({ source: e.source, targetCluster: cb, weight: e.weight });
|
|
1232
|
+
else if (cb === cid && ca !== cid && ca != null)
|
|
1233
|
+
boundaryEdges.push({ source: e.target, targetCluster: ca, weight: e.weight });
|
|
1234
|
+
}
|
|
1235
|
+
members.set(cid, { clusterId: cid, members: mem, intraEdges: intraByCluster.get(cid) ?? [], boundaryEdges });
|
|
1236
|
+
}
|
|
1237
|
+
const META_PER_CLUSTER = 2;
|
|
1238
|
+
const metaByCluster = /* @__PURE__ */ new Map();
|
|
1239
|
+
for (const m of metaMap.values()) {
|
|
1240
|
+
(metaByCluster.get(m.sourceCluster) ?? metaByCluster.set(m.sourceCluster, []).get(m.sourceCluster)).push(m);
|
|
1241
|
+
(metaByCluster.get(m.targetCluster) ?? metaByCluster.set(m.targetCluster, []).get(m.targetCluster)).push(m);
|
|
1242
|
+
}
|
|
1243
|
+
const keptMeta = /* @__PURE__ */ new Set();
|
|
1244
|
+
for (const list of metaByCluster.values()) {
|
|
1245
|
+
list.sort((a, b) => b.weight - a.weight);
|
|
1246
|
+
for (const m of list.slice(0, META_PER_CLUSTER))
|
|
1247
|
+
keptMeta.add(m);
|
|
1248
|
+
}
|
|
1249
|
+
return {
|
|
1250
|
+
clusterLevel: {
|
|
1251
|
+
level: "galaxy",
|
|
1252
|
+
superNodes,
|
|
1253
|
+
metaEdges: [...keptMeta],
|
|
1254
|
+
totalNodes: data.nodes.length,
|
|
1255
|
+
totalEdges: data.edges.length,
|
|
1256
|
+
layoutVersion: mode
|
|
1257
|
+
},
|
|
1258
|
+
members
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
function fibonacciSphere(i, n, radius) {
|
|
1262
|
+
const phi = Math.acos(1 - 2 * (i + 0.5) / Math.max(1, n));
|
|
1263
|
+
const theta = Math.PI * (1 + Math.sqrt(5)) * i;
|
|
1264
|
+
return [
|
|
1265
|
+
radius * Math.sin(phi) * Math.cos(theta),
|
|
1266
|
+
radius * Math.sin(phi) * Math.sin(theta),
|
|
1267
|
+
radius * Math.cos(phi)
|
|
1268
|
+
];
|
|
1269
|
+
}
|
|
1270
|
+
function layoutSuperNodes(superNodes, metaMap) {
|
|
1271
|
+
const n = superNodes.length;
|
|
1272
|
+
if (n === 0)
|
|
1273
|
+
return;
|
|
1274
|
+
if (n === 1) {
|
|
1275
|
+
superNodes[0].position = [0, 0, 0];
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const idx = /* @__PURE__ */ new Map();
|
|
1279
|
+
superNodes.forEach((s, i) => idx.set(s.clusterId, i));
|
|
1280
|
+
const links = [];
|
|
1281
|
+
let maxW = 0;
|
|
1282
|
+
for (const m of metaMap.values()) {
|
|
1283
|
+
const a = idx.get(m.sourceCluster), b = idx.get(m.targetCluster);
|
|
1284
|
+
if (a == null || b == null || a === b)
|
|
1285
|
+
continue;
|
|
1286
|
+
links.push([a, b, m.weight]);
|
|
1287
|
+
if (m.weight > maxW)
|
|
1288
|
+
maxW = m.weight;
|
|
1289
|
+
}
|
|
1290
|
+
const wn = maxW > 0 ? 1 / maxW : 1;
|
|
1291
|
+
const pos = new Float32Array(n * 3);
|
|
1292
|
+
const vel = new Float32Array(n * 3);
|
|
1293
|
+
for (let i = 0; i < n; i++) {
|
|
1294
|
+
const p = fibonacciSphere(i, n, 100);
|
|
1295
|
+
pos[i * 3] = p[0];
|
|
1296
|
+
pos[i * 3 + 1] = p[1];
|
|
1297
|
+
pos[i * 3 + 2] = p[2];
|
|
1298
|
+
}
|
|
1299
|
+
const REST = 42, CHARGE = 900, ITERS = 600, DAMP = 0.9, CENTER = 0.012;
|
|
1300
|
+
for (let it = 0; it < ITERS; it++) {
|
|
1301
|
+
const alpha = 1 - it / ITERS;
|
|
1302
|
+
for (let i = 0; i < n; i++) {
|
|
1303
|
+
const ix = pos[i * 3], iy = pos[i * 3 + 1], iz = pos[i * 3 + 2];
|
|
1304
|
+
for (let j = i + 1; j < n; j++) {
|
|
1305
|
+
let dx = pos[j * 3] - ix, dy = pos[j * 3 + 1] - iy, dz = pos[j * 3 + 2] - iz;
|
|
1306
|
+
let d2 = dx * dx + dy * dy + dz * dz;
|
|
1307
|
+
if (d2 < 0.01) {
|
|
1308
|
+
dx = (i - j) % 3 * 0.1 || 0.1;
|
|
1309
|
+
dy = 0.1;
|
|
1310
|
+
dz = 0.1;
|
|
1311
|
+
d2 = dx * dx + dy * dy + dz * dz;
|
|
1312
|
+
}
|
|
1313
|
+
const f = CHARGE * alpha / d2;
|
|
1314
|
+
const fx = dx * f, fy = dy * f, fz = dz * f;
|
|
1315
|
+
vel[i * 3] -= fx;
|
|
1316
|
+
vel[i * 3 + 1] -= fy;
|
|
1317
|
+
vel[i * 3 + 2] -= fz;
|
|
1318
|
+
vel[j * 3] += fx;
|
|
1319
|
+
vel[j * 3 + 1] += fy;
|
|
1320
|
+
vel[j * 3 + 2] += fz;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
for (const [a, b, w] of links) {
|
|
1324
|
+
let dx = pos[b * 3] - pos[a * 3], dy = pos[b * 3 + 1] - pos[a * 3 + 1], dz = pos[b * 3 + 2] - pos[a * 3 + 2];
|
|
1325
|
+
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz) || 0.01;
|
|
1326
|
+
const strength = 0.6 * (0.3 + 0.7 * w * wn);
|
|
1327
|
+
const f = (dist - REST) / dist * alpha * strength;
|
|
1328
|
+
const fx = dx * f, fy = dy * f, fz = dz * f;
|
|
1329
|
+
vel[a * 3] += fx;
|
|
1330
|
+
vel[a * 3 + 1] += fy;
|
|
1331
|
+
vel[a * 3 + 2] += fz;
|
|
1332
|
+
vel[b * 3] -= fx;
|
|
1333
|
+
vel[b * 3 + 1] -= fy;
|
|
1334
|
+
vel[b * 3 + 2] -= fz;
|
|
1335
|
+
}
|
|
1336
|
+
for (let i = 0; i < n; i++) {
|
|
1337
|
+
vel[i * 3] -= pos[i * 3] * CENTER * alpha;
|
|
1338
|
+
vel[i * 3 + 1] -= pos[i * 3 + 1] * CENTER * alpha;
|
|
1339
|
+
vel[i * 3 + 2] -= pos[i * 3 + 2] * CENTER * alpha;
|
|
1340
|
+
let vx = vel[i * 3] * DAMP, vy = vel[i * 3 + 1] * DAMP, vz = vel[i * 3 + 2] * DAMP;
|
|
1341
|
+
const sp2 = vx * vx + vy * vy + vz * vz;
|
|
1342
|
+
if (sp2 > 64) {
|
|
1343
|
+
const k = 8 / Math.sqrt(sp2);
|
|
1344
|
+
vx *= k;
|
|
1345
|
+
vy *= k;
|
|
1346
|
+
vz *= k;
|
|
1347
|
+
}
|
|
1348
|
+
vel[i * 3] = vx;
|
|
1349
|
+
vel[i * 3 + 1] = vy;
|
|
1350
|
+
vel[i * 3 + 2] = vz;
|
|
1351
|
+
pos[i * 3] += vx;
|
|
1352
|
+
pos[i * 3 + 1] += vy;
|
|
1353
|
+
pos[i * 3 + 2] += vz;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
let cx = 0, cy = 0, cz = 0;
|
|
1357
|
+
for (let i = 0; i < n; i++) {
|
|
1358
|
+
cx += pos[i * 3];
|
|
1359
|
+
cy += pos[i * 3 + 1];
|
|
1360
|
+
cz += pos[i * 3 + 2];
|
|
1361
|
+
}
|
|
1362
|
+
cx /= n;
|
|
1363
|
+
cy /= n;
|
|
1364
|
+
cz /= n;
|
|
1365
|
+
let maxR = 0;
|
|
1366
|
+
for (let i = 0; i < n; i++) {
|
|
1367
|
+
const dx = pos[i * 3] - cx, dy = pos[i * 3 + 1] - cy, dz = pos[i * 3 + 2] - cz;
|
|
1368
|
+
const r = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
1369
|
+
if (r > maxR)
|
|
1370
|
+
maxR = r;
|
|
1371
|
+
}
|
|
1372
|
+
const scale = maxR > 1 ? 125 / maxR : 1;
|
|
1373
|
+
for (let i = 0; i < n; i++) {
|
|
1374
|
+
superNodes[i].position = [
|
|
1375
|
+
(pos[i * 3] - cx) * scale,
|
|
1376
|
+
(pos[i * 3 + 1] - cy) * scale,
|
|
1377
|
+
(pos[i * 3 + 2] - cz) * scale
|
|
1378
|
+
];
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
function kMeans(flat, n, dims, k, maxIter = 50) {
|
|
1382
|
+
if (n === 0 || dims === 0)
|
|
1064
1383
|
return [];
|
|
1065
|
-
|
|
1066
|
-
const centroids =
|
|
1384
|
+
k = Math.max(1, Math.min(k, n));
|
|
1385
|
+
const centroids = new Float32Array(k * dims);
|
|
1386
|
+
centroids.set(flat.subarray(0, dims), 0);
|
|
1387
|
+
const minDist = new Float64Array(n).fill(Infinity);
|
|
1067
1388
|
for (let c = 1; c < k; c++) {
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1389
|
+
const ocPrev = (c - 1) * dims;
|
|
1390
|
+
let total = 0;
|
|
1391
|
+
for (let i = 0; i < n; i++) {
|
|
1392
|
+
const oi = i * dims;
|
|
1393
|
+
let d = 0;
|
|
1394
|
+
for (let z = 0; z < dims; z++) {
|
|
1395
|
+
const diff = flat[oi + z] - centroids[ocPrev + z];
|
|
1396
|
+
d += diff * diff;
|
|
1397
|
+
}
|
|
1398
|
+
if (d < minDist[i])
|
|
1399
|
+
minDist[i] = d;
|
|
1400
|
+
total += minDist[i];
|
|
1401
|
+
}
|
|
1402
|
+
let r = Math.random() * total;
|
|
1403
|
+
let pick = n - 1;
|
|
1404
|
+
for (let i = 0; i < n; i++) {
|
|
1405
|
+
r -= minDist[i];
|
|
1081
1406
|
if (r <= 0) {
|
|
1082
|
-
|
|
1407
|
+
pick = i;
|
|
1083
1408
|
break;
|
|
1084
1409
|
}
|
|
1085
1410
|
}
|
|
1086
|
-
|
|
1087
|
-
centroids.push(vectors[Math.floor(Math.random() * vectors.length)].slice());
|
|
1411
|
+
centroids.set(flat.subarray(pick * dims, pick * dims + dims), c * dims);
|
|
1088
1412
|
}
|
|
1089
|
-
const assignments = new Array(
|
|
1413
|
+
const assignments = new Array(n).fill(0);
|
|
1414
|
+
const sums = new Float64Array(k * dims);
|
|
1415
|
+
const counts = new Uint32Array(k);
|
|
1090
1416
|
for (let iter = 0; iter < maxIter; iter++) {
|
|
1091
1417
|
let changed = false;
|
|
1092
|
-
for (let i = 0; i <
|
|
1418
|
+
for (let i = 0; i < n; i++) {
|
|
1419
|
+
const oi = i * dims;
|
|
1093
1420
|
let bestC = 0, bestD = Infinity;
|
|
1094
1421
|
for (let c = 0; c < k; c++) {
|
|
1095
|
-
const
|
|
1422
|
+
const oc = c * dims;
|
|
1423
|
+
let d = 0;
|
|
1424
|
+
for (let z = 0; z < dims; z++) {
|
|
1425
|
+
const diff = flat[oi + z] - centroids[oc + z];
|
|
1426
|
+
d += diff * diff;
|
|
1427
|
+
}
|
|
1096
1428
|
if (d < bestD) {
|
|
1097
1429
|
bestD = d;
|
|
1098
1430
|
bestC = c;
|
|
@@ -1105,21 +1437,21 @@ function kMeans(vectors, k, maxIter = 50) {
|
|
|
1105
1437
|
}
|
|
1106
1438
|
if (!changed)
|
|
1107
1439
|
break;
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
for (let i = 0; i <
|
|
1440
|
+
sums.fill(0);
|
|
1441
|
+
counts.fill(0);
|
|
1442
|
+
for (let i = 0; i < n; i++) {
|
|
1111
1443
|
const c = assignments[i];
|
|
1112
1444
|
counts[c]++;
|
|
1113
|
-
const
|
|
1114
|
-
for (let
|
|
1115
|
-
|
|
1445
|
+
const oi = i * dims, oc = c * dims;
|
|
1446
|
+
for (let z = 0; z < dims; z++)
|
|
1447
|
+
sums[oc + z] += flat[oi + z];
|
|
1116
1448
|
}
|
|
1117
1449
|
for (let c = 0; c < k; c++) {
|
|
1118
1450
|
if (counts[c] === 0)
|
|
1119
1451
|
continue;
|
|
1120
|
-
const
|
|
1121
|
-
for (let
|
|
1122
|
-
centroids[
|
|
1452
|
+
const oc = c * dims;
|
|
1453
|
+
for (let z = 0; z < dims; z++)
|
|
1454
|
+
centroids[oc + z] = sums[oc + z] / counts[c];
|
|
1123
1455
|
}
|
|
1124
1456
|
}
|
|
1125
1457
|
return assignments;
|
|
@@ -1128,7 +1460,6 @@ var CLUSTER_COLORS;
|
|
|
1128
1460
|
var init_graph_data = __esm({
|
|
1129
1461
|
"packages/core/dist/api/graph-data.js"() {
|
|
1130
1462
|
"use strict";
|
|
1131
|
-
init_math();
|
|
1132
1463
|
CLUSTER_COLORS = [
|
|
1133
1464
|
"#6366f1",
|
|
1134
1465
|
"#ec4899",
|
|
@@ -1155,8 +1486,6 @@ __export(gap_detector_exports, {
|
|
|
1155
1486
|
detectKnowledgeGaps: () => detectKnowledgeGaps
|
|
1156
1487
|
});
|
|
1157
1488
|
async function detectKnowledgeGaps(store, graphData) {
|
|
1158
|
-
const docs = await store.getAllDocuments();
|
|
1159
|
-
const embeddings = await store.getDocumentEmbeddings();
|
|
1160
1489
|
let gd;
|
|
1161
1490
|
if (!graphData) {
|
|
1162
1491
|
const { buildGraphData: buildGraphData2 } = await Promise.resolve().then(() => (init_graph_data(), graph_data_exports));
|
|
@@ -1165,10 +1494,13 @@ async function detectKnowledgeGaps(store, graphData) {
|
|
|
1165
1494
|
gd = graphData;
|
|
1166
1495
|
}
|
|
1167
1496
|
const { nodes, edges, clusters } = gd;
|
|
1497
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
1498
|
+
for (const n of nodes)
|
|
1499
|
+
nodeById.set(n.id, n);
|
|
1168
1500
|
const clusterEdges = /* @__PURE__ */ new Map();
|
|
1169
1501
|
for (const edge of edges) {
|
|
1170
|
-
const nodeA =
|
|
1171
|
-
const nodeB =
|
|
1502
|
+
const nodeA = nodeById.get(edge.source);
|
|
1503
|
+
const nodeB = nodeById.get(edge.target);
|
|
1172
1504
|
if (!nodeA || !nodeB || nodeA.clusterId === nodeB.clusterId)
|
|
1173
1505
|
continue;
|
|
1174
1506
|
const key = [
|
|
@@ -1229,10 +1561,10 @@ var ask_engine_exports = {};
|
|
|
1229
1561
|
__export(ask_engine_exports, {
|
|
1230
1562
|
askVault: () => askVault
|
|
1231
1563
|
});
|
|
1232
|
-
import { writeFileSync as writeFileSync4, mkdirSync as
|
|
1233
|
-
import { join as
|
|
1564
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync5 } from "node:fs";
|
|
1565
|
+
import { join as join6, resolve as resolve5 } from "node:path";
|
|
1234
1566
|
async function askVault(searchEngine, question, options = {}) {
|
|
1235
|
-
const { limit = 10, save = false, vaultPath, outputDir = "_stellavault/answers", mode = "default" } = options;
|
|
1567
|
+
const { limit = 10, save = false, vaultPath, outputDir = "_stellavault/answers", mode = "default", synthesizer } = options;
|
|
1236
1568
|
const results = await searchEngine.search({ query: question, limit });
|
|
1237
1569
|
const sources = results.map((r) => ({
|
|
1238
1570
|
title: r.document.title,
|
|
@@ -1240,7 +1572,16 @@ async function askVault(searchEngine, question, options = {}) {
|
|
|
1240
1572
|
score: Math.round(r.score * 1e3) / 1e3,
|
|
1241
1573
|
snippet: r.chunk?.content?.substring(0, 200) ?? ""
|
|
1242
1574
|
}));
|
|
1243
|
-
|
|
1575
|
+
let answer;
|
|
1576
|
+
if (synthesizer && results.length > 0 && mode !== "quotes") {
|
|
1577
|
+
try {
|
|
1578
|
+
answer = await synthesizer.synthesize({ question, sources, mode: "ask" });
|
|
1579
|
+
} catch {
|
|
1580
|
+
answer = composeAnswer(question, results);
|
|
1581
|
+
}
|
|
1582
|
+
} else {
|
|
1583
|
+
answer = mode === "quotes" ? composeQuotes(question, results) : composeAnswer(question, results);
|
|
1584
|
+
}
|
|
1244
1585
|
let savedTo = null;
|
|
1245
1586
|
if (save && vaultPath) {
|
|
1246
1587
|
savedTo = saveAnswerToVault(question, answer, sources, vaultPath, outputDir);
|
|
@@ -1311,13 +1652,13 @@ function composeQuotes(question, results) {
|
|
|
1311
1652
|
}
|
|
1312
1653
|
function saveAnswerToVault(question, answer, sources, vaultPath, outputDir) {
|
|
1313
1654
|
const dir = resolve5(vaultPath, outputDir);
|
|
1314
|
-
if (!
|
|
1315
|
-
|
|
1655
|
+
if (!existsSync5(dir)) {
|
|
1656
|
+
mkdirSync6(dir, { recursive: true });
|
|
1316
1657
|
}
|
|
1317
1658
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1318
1659
|
const slug = question.toLowerCase().replace(/[^a-z0-9가-힣\s]/g, "").replace(/\s+/g, "-").substring(0, 60);
|
|
1319
1660
|
const filename = `${date}-${slug}.md`;
|
|
1320
|
-
const filePath =
|
|
1661
|
+
const filePath = join6(outputDir, filename);
|
|
1321
1662
|
const fullPath = resolve5(vaultPath, filePath);
|
|
1322
1663
|
if (!fullPath.startsWith(resolve5(vaultPath))) {
|
|
1323
1664
|
throw new Error("Invalid output path");
|
|
@@ -1352,15 +1693,15 @@ __export(wiki_compiler_exports, {
|
|
|
1352
1693
|
extractConcepts: () => extractConcepts,
|
|
1353
1694
|
scanRawDirectory: () => scanRawDirectory
|
|
1354
1695
|
});
|
|
1355
|
-
import { readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as
|
|
1356
|
-
import { join as
|
|
1696
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync7, existsSync as existsSync6, statSync as statSync2 } from "node:fs";
|
|
1697
|
+
import { join as join7, resolve as resolve6, basename, extname as extname3 } from "node:path";
|
|
1357
1698
|
function scanRawDirectory(rawPath) {
|
|
1358
|
-
if (!
|
|
1699
|
+
if (!existsSync6(rawPath))
|
|
1359
1700
|
return [];
|
|
1360
1701
|
const docs = [];
|
|
1361
1702
|
const files = readdirSync3(rawPath, { recursive: true });
|
|
1362
1703
|
for (const file of files) {
|
|
1363
|
-
const fullPath =
|
|
1704
|
+
const fullPath = join7(rawPath, file);
|
|
1364
1705
|
if (!statSync2(fullPath).isFile())
|
|
1365
1706
|
continue;
|
|
1366
1707
|
const ext = extname3(file).toLowerCase();
|
|
@@ -1420,8 +1761,8 @@ function compileWiki(rawPath, wikiPath, options = {}) {
|
|
|
1420
1761
|
if (docs.length === 0) {
|
|
1421
1762
|
return { rawDocCount: 0, wikiArticles: [], indexFile: "", concepts: [] };
|
|
1422
1763
|
}
|
|
1423
|
-
if (!
|
|
1424
|
-
|
|
1764
|
+
if (!existsSync6(wikiPath)) {
|
|
1765
|
+
mkdirSync7(wikiPath, { recursive: true });
|
|
1425
1766
|
}
|
|
1426
1767
|
const concepts = extractConcepts(docs);
|
|
1427
1768
|
const wikiArticles = [];
|
|
@@ -1542,9 +1883,9 @@ var init_wiki_compiler = __esm({
|
|
|
1542
1883
|
|
|
1543
1884
|
// packages/core/dist/federation/identity.js
|
|
1544
1885
|
import { createHash as createHash3, createPrivateKey, createPublicKey, generateKeyPairSync, randomBytes, sign, verify } from "node:crypto";
|
|
1545
|
-
import { chmodSync, existsSync as
|
|
1546
|
-
import { homedir as
|
|
1547
|
-
import { join as
|
|
1886
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
|
|
1887
|
+
import { homedir as homedir5 } from "node:os";
|
|
1888
|
+
import { join as join9 } from "node:path";
|
|
1548
1889
|
function derivePeerId(publicKey) {
|
|
1549
1890
|
return createHash3("sha256").update(publicKey).digest("hex").slice(0, 16);
|
|
1550
1891
|
}
|
|
@@ -1562,7 +1903,7 @@ function generateIdentity(displayName) {
|
|
|
1562
1903
|
};
|
|
1563
1904
|
}
|
|
1564
1905
|
function persistIdentity(identity) {
|
|
1565
|
-
|
|
1906
|
+
mkdirSync10(IDENTITY_DIR, { recursive: true });
|
|
1566
1907
|
const content = JSON.stringify({
|
|
1567
1908
|
version: IDENTITY_VERSION,
|
|
1568
1909
|
algorithm: IDENTITY_ALGORITHM,
|
|
@@ -1579,7 +1920,7 @@ function persistIdentity(identity) {
|
|
|
1579
1920
|
}
|
|
1580
1921
|
}
|
|
1581
1922
|
function getOrCreateIdentity(displayName) {
|
|
1582
|
-
if (
|
|
1923
|
+
if (existsSync8(IDENTITY_FILE)) {
|
|
1583
1924
|
const raw = JSON.parse(readFileSync7(IDENTITY_FILE, "utf-8"));
|
|
1584
1925
|
const isV2 = raw.version === IDENTITY_VERSION && raw.algorithm === IDENTITY_ALGORITHM;
|
|
1585
1926
|
if (isV2) {
|
|
@@ -1618,8 +1959,8 @@ var IDENTITY_DIR, IDENTITY_FILE, IDENTITY_VERSION, IDENTITY_ALGORITHM;
|
|
|
1618
1959
|
var init_identity = __esm({
|
|
1619
1960
|
"packages/core/dist/federation/identity.js"() {
|
|
1620
1961
|
"use strict";
|
|
1621
|
-
IDENTITY_DIR =
|
|
1622
|
-
IDENTITY_FILE =
|
|
1962
|
+
IDENTITY_DIR = join9(homedir5(), ".stellavault", "federation");
|
|
1963
|
+
IDENTITY_FILE = join9(IDENTITY_DIR, "identity.json");
|
|
1623
1964
|
IDENTITY_VERSION = 2;
|
|
1624
1965
|
IDENTITY_ALGORITHM = "ed25519";
|
|
1625
1966
|
}
|
|
@@ -2088,18 +2429,18 @@ var init_privacy = __esm({
|
|
|
2088
2429
|
});
|
|
2089
2430
|
|
|
2090
2431
|
// packages/core/dist/federation/sharing.js
|
|
2091
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync9, existsSync as
|
|
2092
|
-
import { join as
|
|
2093
|
-
import { homedir as
|
|
2432
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync9, existsSync as existsSync9, mkdirSync as mkdirSync11 } from "node:fs";
|
|
2433
|
+
import { join as join10 } from "node:path";
|
|
2434
|
+
import { homedir as homedir6 } from "node:os";
|
|
2094
2435
|
function loadSharingConfig() {
|
|
2095
|
-
if (
|
|
2436
|
+
if (existsSync9(SHARING_FILE)) {
|
|
2096
2437
|
const raw = JSON.parse(readFileSync8(SHARING_FILE, "utf-8"));
|
|
2097
2438
|
return { ...DEFAULT_CONFIG2, ...raw };
|
|
2098
2439
|
}
|
|
2099
2440
|
return { ...DEFAULT_CONFIG2 };
|
|
2100
2441
|
}
|
|
2101
2442
|
function saveSharingConfig(config) {
|
|
2102
|
-
|
|
2443
|
+
mkdirSync11(join10(homedir6(), ".stellavault", "federation"), { recursive: true });
|
|
2103
2444
|
writeFileSync9(SHARING_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
2104
2445
|
}
|
|
2105
2446
|
function getDocumentLevel(doc, config) {
|
|
@@ -2236,7 +2577,7 @@ var init_sharing = __esm({
|
|
|
2236
2577
|
3: 5,
|
|
2237
2578
|
4: 10
|
|
2238
2579
|
};
|
|
2239
|
-
SHARING_FILE =
|
|
2580
|
+
SHARING_FILE = join10(homedir6(), ".stellavault", "federation", "sharing.json");
|
|
2240
2581
|
SENSITIVE_PATTERNS = [
|
|
2241
2582
|
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/,
|
|
2242
2583
|
/\b\d{3}[-.]?\d{4}[-.]?\d{4}\b/,
|
|
@@ -2391,6 +2732,25 @@ var init_federation = __esm({
|
|
|
2391
2732
|
}
|
|
2392
2733
|
});
|
|
2393
2734
|
|
|
2735
|
+
// packages/core/dist/utils/math.js
|
|
2736
|
+
function cosineSimilarity(a, b) {
|
|
2737
|
+
if (a.length !== b.length)
|
|
2738
|
+
return 0;
|
|
2739
|
+
let dot = 0, normA = 0, normB = 0;
|
|
2740
|
+
for (let i = 0; i < a.length; i++) {
|
|
2741
|
+
dot += a[i] * b[i];
|
|
2742
|
+
normA += a[i] * a[i];
|
|
2743
|
+
normB += b[i] * b[i];
|
|
2744
|
+
}
|
|
2745
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
2746
|
+
return denom === 0 ? 0 : dot / denom;
|
|
2747
|
+
}
|
|
2748
|
+
var init_math = __esm({
|
|
2749
|
+
"packages/core/dist/utils/math.js"() {
|
|
2750
|
+
"use strict";
|
|
2751
|
+
}
|
|
2752
|
+
});
|
|
2753
|
+
|
|
2394
2754
|
// packages/core/dist/intelligence/duplicate-detector.js
|
|
2395
2755
|
var duplicate_detector_exports = {};
|
|
2396
2756
|
__export(duplicate_detector_exports, {
|
|
@@ -2649,9 +3009,9 @@ async function extractTimedTranscriptFromHtml(html) {
|
|
|
2649
3009
|
async function extractTimedTranscriptViaTool(videoId) {
|
|
2650
3010
|
const { execSync: execSync3 } = await import("node:child_process");
|
|
2651
3011
|
const { readdirSync: readdirSync9, readFileSync: readFileSync19, unlinkSync } = await import("node:fs");
|
|
2652
|
-
const { join:
|
|
3012
|
+
const { join: join33 } = await import("node:path");
|
|
2653
3013
|
const tmpDir = (await import("node:os")).tmpdir();
|
|
2654
|
-
const tmpBase =
|
|
3014
|
+
const tmpBase = join33(tmpDir, `sv-sub-${videoId}`);
|
|
2655
3015
|
const url = `https://www.youtube.com/watch?v=${videoId}`;
|
|
2656
3016
|
for (const lang of ["ko", "en"]) {
|
|
2657
3017
|
try {
|
|
@@ -2662,10 +3022,10 @@ async function extractTimedTranscriptViaTool(videoId) {
|
|
|
2662
3022
|
const files = readdirSync9(tmpDir).filter((f) => f.startsWith(`sv-sub-${videoId}`) && f.endsWith(".srv1"));
|
|
2663
3023
|
if (files.length === 0)
|
|
2664
3024
|
continue;
|
|
2665
|
-
const xml = readFileSync19(
|
|
3025
|
+
const xml = readFileSync19(join33(tmpDir, files[0]), "utf-8");
|
|
2666
3026
|
for (const f of files) {
|
|
2667
3027
|
try {
|
|
2668
|
-
unlinkSync(
|
|
3028
|
+
unlinkSync(join33(tmpDir, f));
|
|
2669
3029
|
} catch {
|
|
2670
3030
|
}
|
|
2671
3031
|
}
|
|
@@ -2860,18 +3220,18 @@ var init_youtube_extractor = __esm({
|
|
|
2860
3220
|
});
|
|
2861
3221
|
|
|
2862
3222
|
// packages/core/dist/intelligence/zettelkasten.js
|
|
2863
|
-
import { readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync10, existsSync as
|
|
2864
|
-
import { join as
|
|
3223
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync10, existsSync as existsSync10, statSync as statSync3 } from "node:fs";
|
|
3224
|
+
import { join as join11, resolve as resolve8, extname as extname4 } from "node:path";
|
|
2865
3225
|
function scanFrontmatter(vaultPath, options) {
|
|
2866
3226
|
const entries = [];
|
|
2867
3227
|
const includeArchived = options?.includeArchived ?? false;
|
|
2868
3228
|
function walkDir(dir, rel) {
|
|
2869
|
-
if (!
|
|
3229
|
+
if (!existsSync10(dir))
|
|
2870
3230
|
return;
|
|
2871
3231
|
for (const name of readdirSync4(dir)) {
|
|
2872
3232
|
if (name.startsWith(".") || name === "node_modules")
|
|
2873
3233
|
continue;
|
|
2874
|
-
const full =
|
|
3234
|
+
const full = join11(dir, name);
|
|
2875
3235
|
const relPath = rel ? `${rel}/${name}` : name;
|
|
2876
3236
|
if (statSync3(full).isDirectory()) {
|
|
2877
3237
|
walkDir(full, relPath);
|
|
@@ -2987,7 +3347,7 @@ function archiveFile(fullPath, vaultPath) {
|
|
|
2987
3347
|
if (vaultPath && !resolved.startsWith(resolve8(vaultPath))) {
|
|
2988
3348
|
throw new Error("Path traversal detected: file outside vault");
|
|
2989
3349
|
}
|
|
2990
|
-
if (!
|
|
3350
|
+
if (!existsSync10(resolved))
|
|
2991
3351
|
throw new Error(`File not found: ${fullPath}`);
|
|
2992
3352
|
const content = readFileSync9(resolved, "utf-8");
|
|
2993
3353
|
if (content.startsWith("---\n")) {
|
|
@@ -3008,20 +3368,20 @@ var init_zettelkasten = __esm({
|
|
|
3008
3368
|
});
|
|
3009
3369
|
|
|
3010
3370
|
// packages/core/dist/intelligence/auto-linker.js
|
|
3011
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync10, existsSync as
|
|
3012
|
-
import { join as
|
|
3371
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync10, existsSync as existsSync11 } from "node:fs";
|
|
3372
|
+
import { join as join12, resolve as resolve9, extname as extname5 } from "node:path";
|
|
3013
3373
|
function collectVaultTitles(vaultPath, folders = DEFAULT_FOLDERS) {
|
|
3014
3374
|
const titles = /* @__PURE__ */ new Set();
|
|
3015
3375
|
const dirs = [folders.fleeting, folders.literature, folders.permanent, folders.wiki, "_drafts"];
|
|
3016
3376
|
for (const dir of dirs) {
|
|
3017
3377
|
const fullDir = resolve9(vaultPath, dir);
|
|
3018
|
-
if (!
|
|
3378
|
+
if (!existsSync11(fullDir))
|
|
3019
3379
|
continue;
|
|
3020
3380
|
try {
|
|
3021
3381
|
const files = readdirSync5(fullDir).filter((f) => extname5(f) === ".md");
|
|
3022
3382
|
for (const file of files) {
|
|
3023
3383
|
try {
|
|
3024
|
-
const content = readFileSync10(
|
|
3384
|
+
const content = readFileSync10(join12(fullDir, file), "utf-8");
|
|
3025
3385
|
const titleMatch = content.match(/^title:\s*"?([^"\n]+)"?\s*$/m);
|
|
3026
3386
|
if (titleMatch && titleMatch[1].length > 2) {
|
|
3027
3387
|
titles.add(titleMatch[1].trim());
|
|
@@ -3115,8 +3475,8 @@ __export(ingest_pipeline_exports, {
|
|
|
3115
3475
|
ingestBatch: () => ingestBatch,
|
|
3116
3476
|
promoteNote: () => promoteNote
|
|
3117
3477
|
});
|
|
3118
|
-
import { writeFileSync as writeFileSync11, mkdirSync as
|
|
3119
|
-
import { join as
|
|
3478
|
+
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
|
|
3479
|
+
import { join as join13, resolve as resolve10, basename as basename2 } from "node:path";
|
|
3120
3480
|
function decodeHtmlEntities(text) {
|
|
3121
3481
|
return text.replace(/'/g, "'").replace(/'/g, "'").replace(/"/g, '"').replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/ /g, " ");
|
|
3122
3482
|
}
|
|
@@ -3138,13 +3498,13 @@ function ingest(vaultPath, input, folders = DEFAULT_FOLDERS) {
|
|
|
3138
3498
|
};
|
|
3139
3499
|
const folder = folderMap[autoStage];
|
|
3140
3500
|
const dir = resolve10(vaultPath, folder);
|
|
3141
|
-
if (!
|
|
3142
|
-
|
|
3501
|
+
if (!existsSync12(dir))
|
|
3502
|
+
mkdirSync12(dir, { recursive: true });
|
|
3143
3503
|
const now = /* @__PURE__ */ new Date();
|
|
3144
3504
|
const timestamp = now.toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
3145
3505
|
const slug = title.slice(0, 50).replace(/[^a-zA-Z0-9가-힣\s]/g, "").replace(/\s+/g, "-").toLowerCase();
|
|
3146
3506
|
const filename = `${timestamp}-${slug}.md`;
|
|
3147
|
-
const filePath =
|
|
3507
|
+
const filePath = join13(folder, filename);
|
|
3148
3508
|
const fullPath = resolve10(vaultPath, filePath);
|
|
3149
3509
|
if (!fullPath.startsWith(resolve10(vaultPath))) {
|
|
3150
3510
|
throw new Error("Invalid path");
|
|
@@ -3182,7 +3542,7 @@ function ingest(vaultPath, input, folders = DEFAULT_FOLDERS) {
|
|
|
3182
3542
|
try {
|
|
3183
3543
|
const rawDir = resolve10(vaultPath, folders.fleeting);
|
|
3184
3544
|
const wikiDir = resolve10(vaultPath, folders.wiki);
|
|
3185
|
-
if (
|
|
3545
|
+
if (existsSync12(rawDir)) {
|
|
3186
3546
|
compileWiki(rawDir, wikiDir);
|
|
3187
3547
|
}
|
|
3188
3548
|
} catch (err) {
|
|
@@ -3202,7 +3562,7 @@ function ingestBatch(vaultPath, inputs) {
|
|
|
3202
3562
|
}
|
|
3203
3563
|
function promoteNote(vaultPath, filePath, targetStage, folders = DEFAULT_FOLDERS) {
|
|
3204
3564
|
const fullPath = resolve10(vaultPath, filePath);
|
|
3205
|
-
if (!
|
|
3565
|
+
if (!existsSync12(fullPath))
|
|
3206
3566
|
throw new Error(`File not found: ${filePath}`);
|
|
3207
3567
|
const content = readFileSync11(fullPath, "utf-8");
|
|
3208
3568
|
const updated = content.replace(/^type:\s*.+$/m, `type: ${targetStage}`);
|
|
@@ -3212,9 +3572,9 @@ function promoteNote(vaultPath, filePath, targetStage, folders = DEFAULT_FOLDERS
|
|
|
3212
3572
|
permanent: folders.permanent
|
|
3213
3573
|
};
|
|
3214
3574
|
const newDir = resolve10(vaultPath, folderMap[targetStage]);
|
|
3215
|
-
if (!
|
|
3216
|
-
|
|
3217
|
-
const newPath =
|
|
3575
|
+
if (!existsSync12(newDir))
|
|
3576
|
+
mkdirSync12(newDir, { recursive: true });
|
|
3577
|
+
const newPath = join13(folderMap[targetStage], basename2(filePath));
|
|
3218
3578
|
const newFullPath = resolve10(vaultPath, newPath);
|
|
3219
3579
|
if (!newFullPath.startsWith(resolve10(vaultPath))) {
|
|
3220
3580
|
throw new Error("Invalid path");
|
|
@@ -3640,14 +4000,14 @@ var init_file_extractors = __esm({
|
|
|
3640
4000
|
|
|
3641
4001
|
// packages/cli/dist/mcp-clients.js
|
|
3642
4002
|
import { execSync as execSync2 } from "node:child_process";
|
|
3643
|
-
import { existsSync as
|
|
3644
|
-
import { dirname as dirname4, join as
|
|
3645
|
-
import { homedir as
|
|
4003
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync16 } from "node:fs";
|
|
4004
|
+
import { dirname as dirname4, join as join22 } from "node:path";
|
|
4005
|
+
import { homedir as homedir14 } from "node:os";
|
|
3646
4006
|
function appData() {
|
|
3647
|
-
return process.env.APPDATA ??
|
|
4007
|
+
return process.env.APPDATA ?? join22(homedir14(), "AppData", "Roaming");
|
|
3648
4008
|
}
|
|
3649
4009
|
function xdgConfig() {
|
|
3650
|
-
return process.env.XDG_CONFIG_HOME ??
|
|
4010
|
+
return process.env.XDG_CONFIG_HOME ?? join22(homedir14(), ".config");
|
|
3651
4011
|
}
|
|
3652
4012
|
function resolveServeCommand(override) {
|
|
3653
4013
|
if (override?.command) {
|
|
@@ -3660,26 +4020,26 @@ function resolveServeCommand(override) {
|
|
|
3660
4020
|
}
|
|
3661
4021
|
function claudeDesktopPath() {
|
|
3662
4022
|
if (isWin)
|
|
3663
|
-
return
|
|
4023
|
+
return join22(appData(), "Claude", "claude_desktop_config.json");
|
|
3664
4024
|
if (isMac)
|
|
3665
|
-
return
|
|
3666
|
-
return
|
|
4025
|
+
return join22(homedir14(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
4026
|
+
return join22(xdgConfig(), "Claude", "claude_desktop_config.json");
|
|
3667
4027
|
}
|
|
3668
4028
|
function vscodePath() {
|
|
3669
4029
|
if (isWin)
|
|
3670
|
-
return
|
|
4030
|
+
return join22(appData(), "Code", "User", "mcp.json");
|
|
3671
4031
|
if (isMac)
|
|
3672
|
-
return
|
|
3673
|
-
return
|
|
4032
|
+
return join22(homedir14(), "Library", "Application Support", "Code", "User", "mcp.json");
|
|
4033
|
+
return join22(xdgConfig(), "Code", "User", "mcp.json");
|
|
3674
4034
|
}
|
|
3675
4035
|
function isDetected(client) {
|
|
3676
|
-
return
|
|
4036
|
+
return existsSync16(client.detectDir);
|
|
3677
4037
|
}
|
|
3678
4038
|
function writeClientConfig(client, serve) {
|
|
3679
4039
|
const path = client.configPath;
|
|
3680
4040
|
try {
|
|
3681
4041
|
let json = {};
|
|
3682
|
-
if (
|
|
4042
|
+
if (existsSync16(path)) {
|
|
3683
4043
|
const raw = readFileSync15(path, "utf-8").trim();
|
|
3684
4044
|
if (raw)
|
|
3685
4045
|
json = JSON.parse(raw);
|
|
@@ -3689,7 +4049,7 @@ function writeClientConfig(client, serve) {
|
|
|
3689
4049
|
json[key] = {};
|
|
3690
4050
|
const already = Boolean(json[key].stellavault);
|
|
3691
4051
|
json[key].stellavault = client.needsType ? { type: "stdio", command: serve.command, args: serve.args } : { command: serve.command, args: serve.args };
|
|
3692
|
-
|
|
4052
|
+
mkdirSync17(dirname4(path), { recursive: true });
|
|
3693
4053
|
writeFileSync16(path, JSON.stringify(json, null, 2) + "\n", "utf-8");
|
|
3694
4054
|
return { client: client.label, status: already ? "updated" : "written", path };
|
|
3695
4055
|
} catch (err) {
|
|
@@ -3733,16 +4093,16 @@ var init_mcp_clients = __esm({
|
|
|
3733
4093
|
{
|
|
3734
4094
|
id: "cursor",
|
|
3735
4095
|
label: "Cursor",
|
|
3736
|
-
configPath:
|
|
3737
|
-
detectDir:
|
|
4096
|
+
configPath: join22(homedir14(), ".cursor", "mcp.json"),
|
|
4097
|
+
detectDir: join22(homedir14(), ".cursor"),
|
|
3738
4098
|
serversKey: "mcpServers",
|
|
3739
4099
|
needsType: false
|
|
3740
4100
|
},
|
|
3741
4101
|
{
|
|
3742
4102
|
id: "windsurf",
|
|
3743
4103
|
label: "Windsurf",
|
|
3744
|
-
configPath:
|
|
3745
|
-
detectDir:
|
|
4104
|
+
configPath: join22(homedir14(), ".codeium", "windsurf", "mcp_config.json"),
|
|
4105
|
+
detectDir: join22(homedir14(), ".codeium"),
|
|
3746
4106
|
serversKey: "mcpServers",
|
|
3747
4107
|
needsType: false
|
|
3748
4108
|
},
|
|
@@ -3766,14 +4126,14 @@ __export(setup_cmd_exports, {
|
|
|
3766
4126
|
setupCommand: () => setupCommand
|
|
3767
4127
|
});
|
|
3768
4128
|
import chalk4 from "chalk";
|
|
3769
|
-
import { existsSync as
|
|
3770
|
-
import { join as
|
|
3771
|
-
import { homedir as
|
|
4129
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
4130
|
+
import { join as join23 } from "node:path";
|
|
4131
|
+
import { homedir as homedir15 } from "node:os";
|
|
3772
4132
|
async function setupCommand(options) {
|
|
3773
4133
|
console.log("");
|
|
3774
4134
|
console.log(chalk4.bold(" \u2726 Stellavault \u2014 Connect to your AI clients"));
|
|
3775
4135
|
console.log(chalk4.dim(" Registering Stellavault as an MCP server.\n"));
|
|
3776
|
-
if (!
|
|
4136
|
+
if (!existsSync17(join23(homedir15(), ".stellavault.json"))) {
|
|
3777
4137
|
console.log(chalk4.yellow(" \u26A0 No config found. Run ") + chalk4.cyan("stellavault init") + chalk4.yellow(" first to index your vault.\n"));
|
|
3778
4138
|
}
|
|
3779
4139
|
const serve = resolveServeCommand({ command: options.command, args: options.args });
|
|
@@ -3835,9 +4195,9 @@ import ora from "ora";
|
|
|
3835
4195
|
import chalk from "chalk";
|
|
3836
4196
|
import { createHash as createHash7 } from "node:crypto";
|
|
3837
4197
|
import { writeFileSync as writeFileSync15 } from "node:fs";
|
|
3838
|
-
import { join as
|
|
3839
|
-
import { homedir as
|
|
3840
|
-
import { mkdirSync as
|
|
4198
|
+
import { join as join21 } from "node:path";
|
|
4199
|
+
import { homedir as homedir13 } from "node:os";
|
|
4200
|
+
import { mkdirSync as mkdirSync16 } from "node:fs";
|
|
3841
4201
|
|
|
3842
4202
|
// packages/core/dist/index.js
|
|
3843
4203
|
init_config();
|
|
@@ -3847,13 +4207,21 @@ import Database from "better-sqlite3";
|
|
|
3847
4207
|
import * as sqliteVec from "sqlite-vec";
|
|
3848
4208
|
import { mkdirSync } from "node:fs";
|
|
3849
4209
|
import { dirname } from "node:path";
|
|
4210
|
+
function loadVecExtension(db) {
|
|
4211
|
+
const loadablePath = sqliteVec.getLoadablePath();
|
|
4212
|
+
if (/\.asar[\\/]/.test(loadablePath)) {
|
|
4213
|
+
db.loadExtension(loadablePath.replace(/\.asar([\\/])/, ".asar.unpacked$1"));
|
|
4214
|
+
} else {
|
|
4215
|
+
sqliteVec.load(db);
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
3850
4218
|
function createSqliteVecStore(dbPath, dimensions = 384) {
|
|
3851
4219
|
let db;
|
|
3852
4220
|
return {
|
|
3853
4221
|
async initialize() {
|
|
3854
4222
|
mkdirSync(dirname(dbPath), { recursive: true });
|
|
3855
4223
|
db = new Database(dbPath);
|
|
3856
|
-
|
|
4224
|
+
loadVecExtension(db);
|
|
3857
4225
|
db.pragma("journal_mode = WAL");
|
|
3858
4226
|
db.pragma("foreign_keys = ON");
|
|
3859
4227
|
createTables(db, dimensions);
|
|
@@ -3989,6 +4357,19 @@ function createSqliteVecStore(dbPath, dimensions = 384) {
|
|
|
3989
4357
|
const rows = db.prepare("SELECT * FROM documents ORDER BY last_modified DESC").all();
|
|
3990
4358
|
return rows.map(rowToDocument);
|
|
3991
4359
|
},
|
|
4360
|
+
async getDocumentsMeta(maxDocs) {
|
|
4361
|
+
const lim = typeof maxDocs === "number" && Number.isFinite(maxDocs) && maxDocs > 0 ? Math.floor(maxDocs) : 0;
|
|
4362
|
+
const sql = "SELECT id, file_path, title, frontmatter, tags, last_modified FROM documents ORDER BY last_modified DESC" + (lim > 0 ? ` LIMIT ${lim}` : "");
|
|
4363
|
+
const rows = db.prepare(sql).all();
|
|
4364
|
+
return rows.map((r) => ({
|
|
4365
|
+
id: r.id,
|
|
4366
|
+
filePath: r.file_path,
|
|
4367
|
+
title: r.title,
|
|
4368
|
+
frontmatter: JSON.parse(r.frontmatter || "{}"),
|
|
4369
|
+
tags: JSON.parse(r.tags || "[]"),
|
|
4370
|
+
lastModified: r.last_modified
|
|
4371
|
+
}));
|
|
4372
|
+
},
|
|
3992
4373
|
async getTopics() {
|
|
3993
4374
|
const rows = db.prepare(`
|
|
3994
4375
|
SELECT je.value as tag, COUNT(DISTINCT d.id) as count
|
|
@@ -4015,23 +4396,38 @@ function createSqliteVecStore(dbPath, dimensions = 384) {
|
|
|
4015
4396
|
};
|
|
4016
4397
|
},
|
|
4017
4398
|
async getDocumentEmbeddings(maxDocs = 1e4) {
|
|
4018
|
-
const BATCH_SIZE = 500;
|
|
4019
4399
|
const result = /* @__PURE__ */ new Map();
|
|
4020
|
-
const
|
|
4021
|
-
SELECT c.document_id, ce.embedding
|
|
4400
|
+
const rows = db.prepare(`
|
|
4401
|
+
SELECT c.document_id AS document_id, ce.embedding AS embedding
|
|
4022
4402
|
FROM chunks c
|
|
4023
4403
|
JOIN chunk_embeddings ce ON ce.chunk_id = c.id
|
|
4024
|
-
WHERE c.id IN (
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4404
|
+
WHERE c.id IN (SELECT MIN(id) FROM chunks GROUP BY document_id)
|
|
4405
|
+
LIMIT ?
|
|
4406
|
+
`).all(maxDocs);
|
|
4407
|
+
for (const row of rows) {
|
|
4408
|
+
result.set(row.document_id, bufferToFloat32(row.embedding));
|
|
4409
|
+
}
|
|
4410
|
+
return result;
|
|
4411
|
+
},
|
|
4412
|
+
async getDocumentEmbeddingsByIds(documentIds) {
|
|
4413
|
+
const result = /* @__PURE__ */ new Map();
|
|
4414
|
+
if (documentIds.length === 0)
|
|
4415
|
+
return result;
|
|
4416
|
+
const CHUNK = 800;
|
|
4417
|
+
for (let off = 0; off < documentIds.length; off += CHUNK) {
|
|
4418
|
+
const slice = documentIds.slice(off, off + CHUNK);
|
|
4419
|
+
const ph = slice.map(() => "?").join(",");
|
|
4420
|
+
const cidRows = db.prepare(`SELECT document_id AS d, MIN(id) AS cid FROM chunks WHERE document_id IN (${ph}) GROUP BY document_id`).all(...slice);
|
|
4421
|
+
if (cidRows.length === 0)
|
|
4422
|
+
continue;
|
|
4423
|
+
const docByCid = new Map(cidRows.map((r) => [String(r.cid), r.d]));
|
|
4424
|
+
const cids = cidRows.map((r) => r.cid);
|
|
4425
|
+
const ph2 = cids.map(() => "?").join(",");
|
|
4426
|
+
const embRows = db.prepare(`SELECT chunk_id AS cid, embedding FROM chunk_embeddings WHERE chunk_id IN (${ph2})`).all(...cids);
|
|
4427
|
+
for (const row of embRows) {
|
|
4428
|
+
const docId = docByCid.get(String(row.cid));
|
|
4429
|
+
if (docId)
|
|
4430
|
+
result.set(docId, bufferToFloat32(row.embedding));
|
|
4035
4431
|
}
|
|
4036
4432
|
}
|
|
4037
4433
|
return result;
|
|
@@ -4583,10 +4979,10 @@ async function handleGenerateClaudeMd(searchEngine, store, args) {
|
|
|
4583
4979
|
}
|
|
4584
4980
|
|
|
4585
4981
|
// packages/core/dist/mcp/tools/snapshot.js
|
|
4586
|
-
import { readFileSync as readFileSync3, writeFileSync, existsSync as
|
|
4587
|
-
import { join as
|
|
4588
|
-
import { homedir as
|
|
4589
|
-
var SNAPSHOT_DIR =
|
|
4982
|
+
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
4983
|
+
import { join as join4, resolve as resolve2 } from "node:path";
|
|
4984
|
+
import { homedir as homedir3 } from "node:os";
|
|
4985
|
+
var SNAPSHOT_DIR = join4(homedir3(), ".stellavault", "snapshots");
|
|
4590
4986
|
function sanitizeName(name) {
|
|
4591
4987
|
const sanitized = name.replace(/[^a-zA-Z0-9가-힣_-]/g, "");
|
|
4592
4988
|
if (!sanitized)
|
|
@@ -4624,7 +5020,7 @@ var loadSnapshotToolDef = {
|
|
|
4624
5020
|
}
|
|
4625
5021
|
};
|
|
4626
5022
|
async function handleCreateSnapshot(searchEngine, args) {
|
|
4627
|
-
|
|
5023
|
+
mkdirSync3(SNAPSHOT_DIR, { recursive: true });
|
|
4628
5024
|
const results = [];
|
|
4629
5025
|
for (const query of args.queries) {
|
|
4630
5026
|
const hits = await searchEngine.search({ query, limit: 5 });
|
|
@@ -4659,15 +5055,15 @@ async function handleCreateSnapshot(searchEngine, args) {
|
|
|
4659
5055
|
async function handleLoadSnapshot(args) {
|
|
4660
5056
|
const safeName = sanitizeName(args.name);
|
|
4661
5057
|
const filePath = ensureWithinDir(SNAPSHOT_DIR, `${safeName}.json`);
|
|
4662
|
-
if (!
|
|
5058
|
+
if (!existsSync3(filePath)) {
|
|
4663
5059
|
return { error: `Snapshot not found: ${args.name}` };
|
|
4664
5060
|
}
|
|
4665
5061
|
return JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
4666
5062
|
}
|
|
4667
5063
|
|
|
4668
5064
|
// packages/core/dist/mcp/tools/decision-journal.js
|
|
4669
|
-
import { writeFileSync as writeFileSync2, existsSync as
|
|
4670
|
-
import { join as
|
|
5065
|
+
import { writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "node:fs";
|
|
5066
|
+
import { join as join5, resolve as resolve3 } from "node:path";
|
|
4671
5067
|
var logDecisionToolDef = {
|
|
4672
5068
|
name: "log-decision",
|
|
4673
5069
|
description: '\uAE30\uC220\uC801 \uACB0\uC815\uC744 \uAD6C\uC870\uD654\uD558\uC5EC \uAE30\uB85D\uD569\uB2C8\uB2E4. \uB098\uC911\uC5D0 "\uC65C \uC774 \uC120\uD0DD\uC744 \uD588\uC9C0?"\uC5D0 \uB2F5\uBCC0\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.',
|
|
@@ -4696,8 +5092,8 @@ var findDecisionsToolDef = {
|
|
|
4696
5092
|
}
|
|
4697
5093
|
};
|
|
4698
5094
|
async function handleLogDecision(vaultPath, args) {
|
|
4699
|
-
const decisionsDir =
|
|
4700
|
-
|
|
5095
|
+
const decisionsDir = join5(vaultPath, "decisions");
|
|
5096
|
+
mkdirSync4(decisionsDir, { recursive: true });
|
|
4701
5097
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4702
5098
|
const slug = args.title.replace(/[^a-zA-Z가-힣0-9\s-]/g, "").replace(/\s+/g, "-").slice(0, 50);
|
|
4703
5099
|
if (!slug)
|
|
@@ -4738,13 +5134,13 @@ ${args.reasoning}
|
|
|
4738
5134
|
return { saved: filePath, fileName };
|
|
4739
5135
|
}
|
|
4740
5136
|
async function handleFindDecisions(vaultPath, args) {
|
|
4741
|
-
const decisionsDir =
|
|
4742
|
-
if (!
|
|
5137
|
+
const decisionsDir = join5(vaultPath, "decisions");
|
|
5138
|
+
if (!existsSync4(decisionsDir))
|
|
4743
5139
|
return { decisions: [], message: "No decisions directory" };
|
|
4744
5140
|
const files = readdirSync2(decisionsDir).filter((f) => f.endsWith(".md"));
|
|
4745
5141
|
const query = args.query.toLowerCase();
|
|
4746
5142
|
const matches = files.map((f) => {
|
|
4747
|
-
const content = readFileSync4(
|
|
5143
|
+
const content = readFileSync4(join5(decisionsDir, f), "utf-8");
|
|
4748
5144
|
const score = content.toLowerCase().includes(query) ? 1 : 0;
|
|
4749
5145
|
return { file: f, content: content.slice(0, 300), score };
|
|
4750
5146
|
}).filter((m) => m.score > 0).slice(0, 10);
|
|
@@ -4752,11 +5148,11 @@ async function handleFindDecisions(vaultPath, args) {
|
|
|
4752
5148
|
}
|
|
4753
5149
|
|
|
4754
5150
|
// packages/core/dist/mcp/tools/export.js
|
|
4755
|
-
import { writeFileSync as writeFileSync3, mkdirSync as
|
|
5151
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync5 } from "node:fs";
|
|
4756
5152
|
import { dirname as dirname2, resolve as resolve4 } from "node:path";
|
|
4757
|
-
import { homedir as
|
|
5153
|
+
import { homedir as homedir4 } from "node:os";
|
|
4758
5154
|
var ALLOWED_EXPORT_DIRS = [
|
|
4759
|
-
resolve4(
|
|
5155
|
+
resolve4(homedir4(), ".stellavault"),
|
|
4760
5156
|
resolve4(".")
|
|
4761
5157
|
];
|
|
4762
5158
|
function validateExportPath(outputPath) {
|
|
@@ -4784,7 +5180,7 @@ async function handleExport(store, args) {
|
|
|
4784
5180
|
const stats = await store.getStats();
|
|
4785
5181
|
const topics = await store.getTopics();
|
|
4786
5182
|
const safePath = validateExportPath(args.outputPath);
|
|
4787
|
-
|
|
5183
|
+
mkdirSync5(dirname2(safePath), { recursive: true });
|
|
4788
5184
|
if (args.format === "csv") {
|
|
4789
5185
|
const header = "id,filePath,title,tags,lastModified,contentHash";
|
|
4790
5186
|
const rows = docs.map((d) => `"${d.id}","${d.filePath}","${d.title.replace(/"/g, '""')}","${d.tags.join(";")}","${d.lastModified}","${d.contentHash}"`);
|
|
@@ -4901,6 +5297,17 @@ function updateStability(currentS, difficulty, currentR) {
|
|
|
4901
5297
|
const newS = currentS * (1 + Math.max(0, growth));
|
|
4902
5298
|
return Math.min(newS, 365);
|
|
4903
5299
|
}
|
|
5300
|
+
function updateStabilityGraded(currentS, difficulty, currentR, grade) {
|
|
5301
|
+
if (grade === 1) {
|
|
5302
|
+
const postLapse = FSRS_PARAMS.initialStability * 0.2 * Math.pow(difficulty, -FSRS_PARAMS.b);
|
|
5303
|
+
return Math.max(0.1, Math.min(postLapse, currentS));
|
|
5304
|
+
}
|
|
5305
|
+
const good = updateStability(currentS, difficulty, currentR);
|
|
5306
|
+
const baseGrowth = good - currentS;
|
|
5307
|
+
const gradeFactor = grade === 2 ? 0.5 : grade === 4 ? 1.8 : 1;
|
|
5308
|
+
const newS = currentS + baseGrowth * gradeFactor;
|
|
5309
|
+
return Math.min(newS, 365);
|
|
5310
|
+
}
|
|
4904
5311
|
function estimateInitialStability(contentLength, connectionCount) {
|
|
4905
5312
|
const base = FSRS_PARAMS.initialStability;
|
|
4906
5313
|
const sizeBonus = Math.min(contentLength / 1e3, 10) * FSRS_PARAMS.sizeFactor;
|
|
@@ -4956,7 +5363,7 @@ var DecayEngine = class {
|
|
|
4956
5363
|
if (existing) {
|
|
4957
5364
|
const elapsed = elapsedDays(existing.last_access, now);
|
|
4958
5365
|
const currentR = computeRetrievability(existing.stability, elapsed);
|
|
4959
|
-
const newS = updateStability(existing.stability, existing.difficulty, currentR);
|
|
5366
|
+
const newS = event.grade ? updateStabilityGraded(existing.stability, existing.difficulty, currentR, event.grade) : updateStability(existing.stability, existing.difficulty, currentR);
|
|
4960
5367
|
this.db.prepare(`
|
|
4961
5368
|
UPDATE decay_state SET stability = ?, last_access = ?, retrievability = 1.0, updated_at = ?
|
|
4962
5369
|
WHERE document_id = ?
|
|
@@ -5040,22 +5447,19 @@ var DecayEngine = class {
|
|
|
5040
5447
|
* Get documents below decay threshold.
|
|
5041
5448
|
*/
|
|
5042
5449
|
async getDecaying(threshold = 0.5, limit = 20) {
|
|
5043
|
-
await this.computeAll();
|
|
5044
5450
|
const rows = this.db.prepare(`
|
|
5045
5451
|
SELECT ds.*, d.title FROM decay_state ds
|
|
5046
5452
|
JOIN documents d ON d.id = ds.document_id
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
LIMIT ?
|
|
5050
|
-
`).all(threshold, limit);
|
|
5453
|
+
`).all();
|
|
5454
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5051
5455
|
return rows.map((r) => ({
|
|
5052
5456
|
documentId: r.document_id,
|
|
5053
5457
|
stability: r.stability,
|
|
5054
5458
|
difficulty: r.difficulty,
|
|
5055
5459
|
lastAccess: r.last_access,
|
|
5056
|
-
retrievability: r.
|
|
5460
|
+
retrievability: computeRetrievability(r.stability, elapsedDays(r.last_access, now)),
|
|
5057
5461
|
title: r.title
|
|
5058
|
-
}));
|
|
5462
|
+
})).filter((r) => r.retrievability < threshold).sort((a, b) => a.retrievability - b.retrievability).slice(0, limit);
|
|
5059
5463
|
}
|
|
5060
5464
|
/**
|
|
5061
5465
|
* Design Ref: §B3.3.3 — read-only live retrievability for a set of documents.
|
|
@@ -5277,14 +5681,34 @@ function invalidateGapCache(db) {
|
|
|
5277
5681
|
} catch {
|
|
5278
5682
|
}
|
|
5279
5683
|
}
|
|
5684
|
+
var EMPTY_REPORT = { totalClusters: 0, totalGaps: 0, gaps: [], isolatedNodes: [] };
|
|
5685
|
+
function readAnyCachedGapReport(db) {
|
|
5686
|
+
try {
|
|
5687
|
+
ensureGapCacheTable(db);
|
|
5688
|
+
const row = db.prepare("SELECT * FROM gap_cache WHERE id = 1").get();
|
|
5689
|
+
if (!row || row.version !== CACHE_VERSION)
|
|
5690
|
+
return null;
|
|
5691
|
+
return JSON.parse(row.payload);
|
|
5692
|
+
} catch {
|
|
5693
|
+
return null;
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
function triggerBackgroundCompute(store, db) {
|
|
5697
|
+
void computeAndCacheGaps(store, db).catch((err) => {
|
|
5698
|
+
console.warn("[gap-cache] background recompute failed:", err?.message ?? err);
|
|
5699
|
+
});
|
|
5700
|
+
}
|
|
5280
5701
|
async function getGapReport(store, db, opts = {}) {
|
|
5281
5702
|
if (!opts.forceRefresh) {
|
|
5282
|
-
const
|
|
5283
|
-
if (
|
|
5284
|
-
return { report:
|
|
5703
|
+
const fresh = readCachedGapReport(db, opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS);
|
|
5704
|
+
if (fresh)
|
|
5705
|
+
return { report: fresh, fromCache: true, computing: false };
|
|
5285
5706
|
}
|
|
5286
|
-
|
|
5287
|
-
|
|
5707
|
+
triggerBackgroundCompute(store, db);
|
|
5708
|
+
const stale = readAnyCachedGapReport(db);
|
|
5709
|
+
if (stale)
|
|
5710
|
+
return { report: stale, fromCache: true, computing: true };
|
|
5711
|
+
return { report: EMPTY_REPORT, fromCache: false, computing: true };
|
|
5288
5712
|
}
|
|
5289
5713
|
|
|
5290
5714
|
// packages/core/dist/mcp/tools/detect-gaps.js
|
|
@@ -5309,7 +5733,7 @@ function createDetectGapsTool(store, getDb) {
|
|
|
5309
5733
|
handler: async (args) => {
|
|
5310
5734
|
const minSeverity = args.minSeverity ?? "medium";
|
|
5311
5735
|
const db = getDb();
|
|
5312
|
-
const { report, fromCache } = await getGapReport(store, db, { forceRefresh: args.forceRefresh });
|
|
5736
|
+
const { report, fromCache, computing } = await getGapReport(store, db, { forceRefresh: args.forceRefresh });
|
|
5313
5737
|
const sevOrder = { high: 0, medium: 1, low: 2 };
|
|
5314
5738
|
const threshold = sevOrder[minSeverity] ?? 1;
|
|
5315
5739
|
const filtered = report.gaps.filter((g) => sevOrder[g.severity] <= threshold);
|
|
@@ -5327,8 +5751,8 @@ function createDetectGapsTool(store, getDb) {
|
|
|
5327
5751
|
suggestedTopic: g.suggestedTopic
|
|
5328
5752
|
})),
|
|
5329
5753
|
isolatedNodes: report.isolatedNodes.slice(0, 10),
|
|
5330
|
-
suggestion: filtered.length > 0 ? `${filtered[0].suggestedTopic} \uC8FC\uC81C\uB85C \uB178\uD2B8\uB97C \uC791\uC131\uD558\uBA74 \uC9C0\uC2DD \uAC2D\uC744 \uC904\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4.` : "\uD604\uC7AC \uC2EC\uAC01\uD55C \uC9C0\uC2DD \uAC2D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
5331
|
-
cacheStatus: fromCache ? "cached" : "fresh"
|
|
5754
|
+
suggestion: computing ? "\uAC2D \uBD84\uC11D\uC744 \uBC31\uADF8\uB77C\uC6B4\uB4DC\uC5D0\uC11C \uC7AC\uACC4\uC0B0 \uC911\uC785\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uBA74 \uCD5C\uC2E0 \uACB0\uACFC\uAC00 \uB098\uC635\uB2C8\uB2E4." : filtered.length > 0 ? `${filtered[0].suggestedTopic} \uC8FC\uC81C\uB85C \uB178\uD2B8\uB97C \uC791\uC131\uD558\uBA74 \uC9C0\uC2DD \uAC2D\uC744 \uC904\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4.` : "\uD604\uC7AC \uC2EC\uAC01\uD55C \uC9C0\uC2DD \uAC2D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
5755
|
+
cacheStatus: computing ? fromCache ? "stale (recomputing in background)" : "computing in background" : fromCache ? "cached" : "fresh"
|
|
5332
5756
|
}, null, 2)
|
|
5333
5757
|
}]
|
|
5334
5758
|
};
|
|
@@ -5546,7 +5970,7 @@ function createAskTool(searchEngine, vaultPath) {
|
|
|
5546
5970
|
init_wiki_compiler();
|
|
5547
5971
|
init_config();
|
|
5548
5972
|
import { resolve as resolve7 } from "node:path";
|
|
5549
|
-
import { existsSync as
|
|
5973
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
5550
5974
|
function createGenerateDraftTool(searchEngine, vaultPath) {
|
|
5551
5975
|
return {
|
|
5552
5976
|
name: "generate-draft",
|
|
@@ -5589,7 +6013,7 @@ function createGenerateDraftTool(searchEngine, vaultPath) {
|
|
|
5589
6013
|
const allDocs = [];
|
|
5590
6014
|
for (const dir of [folders.fleeting, folders.literature, folders.permanent, folders.wiki]) {
|
|
5591
6015
|
const fullDir = resolve7(vaultPath, dir);
|
|
5592
|
-
if (
|
|
6016
|
+
if (existsSync7(fullDir)) {
|
|
5593
6017
|
const docs = scanRawDirectory(fullDir);
|
|
5594
6018
|
allDocs.push(...docs);
|
|
5595
6019
|
}
|
|
@@ -5645,8 +6069,8 @@ Please write the ${format} draft based on the context above. Save the result to
|
|
|
5645
6069
|
}
|
|
5646
6070
|
|
|
5647
6071
|
// packages/core/dist/mcp/tools/agentic-graph.js
|
|
5648
|
-
import { writeFileSync as writeFileSync6, mkdirSync as
|
|
5649
|
-
import { join as
|
|
6072
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync8 } from "node:fs";
|
|
6073
|
+
import { join as join8, resolve as resolvePath } from "node:path";
|
|
5650
6074
|
function createAgenticGraphTools(store, embedder, vaultPath) {
|
|
5651
6075
|
let nodeCreationCount = 0;
|
|
5652
6076
|
let nodeCreationWindowStart = Date.now();
|
|
@@ -5717,14 +6141,14 @@ function createAgenticGraphTools(store, embedder, vaultPath) {
|
|
|
5717
6141
|
|
|
5718
6142
|
${content}${relatedSection}`;
|
|
5719
6143
|
const safeTitle = title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ").trim().slice(0, 80);
|
|
5720
|
-
const dir =
|
|
5721
|
-
const filePath =
|
|
6144
|
+
const dir = join8(vaultPath, folder);
|
|
6145
|
+
const filePath = join8(dir, `${safeTitle}.md`);
|
|
5722
6146
|
const resolvedPath = resolvePath(filePath);
|
|
5723
6147
|
const resolvedVault = resolvePath(vaultPath);
|
|
5724
6148
|
if (!resolvedPath.startsWith(resolvedVault)) {
|
|
5725
6149
|
return { content: [{ type: "text", text: "Error: invalid folder path." }] };
|
|
5726
6150
|
}
|
|
5727
|
-
|
|
6151
|
+
mkdirSync8(dir, { recursive: true });
|
|
5728
6152
|
writeFileSync6(filePath, fullContent, "utf-8");
|
|
5729
6153
|
const relatedCount = relatedSection ? relatedSection.split("\n").filter((l) => l.startsWith("- ")).length : 0;
|
|
5730
6154
|
return {
|
|
@@ -5759,7 +6183,7 @@ The note will appear in the graph after next index.`
|
|
|
5759
6183
|
return { content: [{ type: "text", text: `Source note "${args.sourceTitle}" not found.` }] };
|
|
5760
6184
|
}
|
|
5761
6185
|
const { readFileSync: readFileSync19 } = await import("node:fs");
|
|
5762
|
-
const fullPath =
|
|
6186
|
+
const fullPath = join8(vaultPath, source.filePath);
|
|
5763
6187
|
let existing = "";
|
|
5764
6188
|
try {
|
|
5765
6189
|
existing = readFileSync19(fullPath, "utf-8");
|
|
@@ -5821,6 +6245,14 @@ function createMcpServer(options) {
|
|
|
5821
6245
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
5822
6246
|
await ready;
|
|
5823
6247
|
const { name, arguments: args } = request.params;
|
|
6248
|
+
if (options.onToolCall) {
|
|
6249
|
+
try {
|
|
6250
|
+
const a = args ?? {};
|
|
6251
|
+
const detail = String(a.query ?? a.topic ?? a.title ?? a.documentId ?? a.id ?? "").slice(0, 80);
|
|
6252
|
+
options.onToolCall({ tool: name, detail });
|
|
6253
|
+
} catch {
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
5824
6256
|
try {
|
|
5825
6257
|
let result;
|
|
5826
6258
|
switch (name) {
|
|
@@ -5919,6 +6351,9 @@ function createMcpServer(options) {
|
|
|
5919
6351
|
const transport = new StdioServerTransport();
|
|
5920
6352
|
await server.connect(transport);
|
|
5921
6353
|
},
|
|
6354
|
+
// T3-3: returns a closable handle ({ port, close }) so an embedding host (the
|
|
6355
|
+
// desktop "Agent Memory" toggle) can stop the server. Backward compatible —
|
|
6356
|
+
// callers that ignore the return value are unaffected.
|
|
5922
6357
|
async startHttp(port = 3334) {
|
|
5923
6358
|
const { createServer } = await import("node:http");
|
|
5924
6359
|
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => `sv-${Date.now()}` });
|
|
@@ -5944,9 +6379,22 @@ function createMcpServer(options) {
|
|
|
5944
6379
|
}
|
|
5945
6380
|
await transport.handleRequest(req, res);
|
|
5946
6381
|
});
|
|
5947
|
-
|
|
5948
|
-
|
|
6382
|
+
await new Promise((resolveListen, rejectListen) => {
|
|
6383
|
+
httpServer.once("error", rejectListen);
|
|
6384
|
+
httpServer.listen(port, "127.0.0.1", () => {
|
|
6385
|
+
httpServer.removeListener("error", rejectListen);
|
|
6386
|
+
console.error(`\u{1F50C} MCP HTTP server running at http://127.0.0.1:${port}/mcp`);
|
|
6387
|
+
resolveListen();
|
|
6388
|
+
});
|
|
5949
6389
|
});
|
|
6390
|
+
return {
|
|
6391
|
+
port,
|
|
6392
|
+
close: () => new Promise((resolveClose) => {
|
|
6393
|
+
httpServer.close(() => resolveClose());
|
|
6394
|
+
void server.close().catch(() => {
|
|
6395
|
+
});
|
|
6396
|
+
})
|
|
6397
|
+
};
|
|
5950
6398
|
},
|
|
5951
6399
|
server
|
|
5952
6400
|
};
|
|
@@ -6074,10 +6522,10 @@ function extractPackTags(chunks) {
|
|
|
6074
6522
|
}
|
|
6075
6523
|
|
|
6076
6524
|
// packages/core/dist/pack/exporter.js
|
|
6077
|
-
import { writeFileSync as writeFileSync7, mkdirSync as
|
|
6525
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync9 } from "node:fs";
|
|
6078
6526
|
import { dirname as dirname3 } from "node:path";
|
|
6079
6527
|
function exportPack(pack2, outputPath) {
|
|
6080
|
-
|
|
6528
|
+
mkdirSync9(dirname3(outputPath), { recursive: true });
|
|
6081
6529
|
writeFileSync7(outputPath, JSON.stringify(pack2, null, 2), "utf-8");
|
|
6082
6530
|
}
|
|
6083
6531
|
function packToSummary(pack2) {
|
|
@@ -6300,10 +6748,10 @@ function createKnowledgeRouter(opts) {
|
|
|
6300
6748
|
return;
|
|
6301
6749
|
}
|
|
6302
6750
|
const { readFileSync: readFileSync19, writeFileSync: writeFileSync23, unlinkSync } = await import("node:fs");
|
|
6303
|
-
const { join:
|
|
6751
|
+
const { join: join33, resolve: resolve22 } = await import("node:path");
|
|
6304
6752
|
const [keeper, removed] = docA.content.length >= docB.content.length ? [docA, docB] : [docB, docA];
|
|
6305
|
-
const keeperPath = resolve22(
|
|
6306
|
-
const removedPath = resolve22(
|
|
6753
|
+
const keeperPath = resolve22(join33(vaultPath, keeper.filePath));
|
|
6754
|
+
const removedPath = resolve22(join33(vaultPath, removed.filePath));
|
|
6307
6755
|
const vaultRoot = resolve22(vaultPath);
|
|
6308
6756
|
if (!keeperPath.startsWith(vaultRoot) || !removedPath.startsWith(vaultRoot)) {
|
|
6309
6757
|
res.status(400).json({ error: "Invalid file path" });
|
|
@@ -6340,8 +6788,8 @@ ${removed.content}`;
|
|
|
6340
6788
|
res.status(400).json({ error: "clusterA, clusterB required" });
|
|
6341
6789
|
return;
|
|
6342
6790
|
}
|
|
6343
|
-
const { writeFileSync: writeFileSync23, mkdirSync:
|
|
6344
|
-
const { join:
|
|
6791
|
+
const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync24 } = await import("node:fs");
|
|
6792
|
+
const { join: join33, resolve: resolve22 } = await import("node:path");
|
|
6345
6793
|
const nameA = clusterA.replace(/\s*\(\d+\)$/, "");
|
|
6346
6794
|
const nameB = clusterB.replace(/\s*\(\d+\)$/, "");
|
|
6347
6795
|
const resultsA = await searchEngine.search({ query: nameA, limit: 3 });
|
|
@@ -6381,13 +6829,13 @@ ${removed.content}`;
|
|
|
6381
6829
|
""
|
|
6382
6830
|
].join("\n");
|
|
6383
6831
|
const safeTitle = title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ");
|
|
6384
|
-
const dir = resolve22(
|
|
6832
|
+
const dir = resolve22(join33(vaultPath, "01_Knowledge"));
|
|
6385
6833
|
if (!dir.startsWith(resolve22(vaultPath))) {
|
|
6386
6834
|
res.status(400).json({ error: "Invalid path" });
|
|
6387
6835
|
return;
|
|
6388
6836
|
}
|
|
6389
|
-
|
|
6390
|
-
const filePath =
|
|
6837
|
+
mkdirSync24(dir, { recursive: true });
|
|
6838
|
+
const filePath = join33(dir, `${safeTitle}.md`);
|
|
6391
6839
|
writeFileSync23(filePath, content, "utf-8");
|
|
6392
6840
|
res.json({ success: true, title: safeTitle, path: filePath });
|
|
6393
6841
|
} catch (err) {
|
|
@@ -6402,7 +6850,7 @@ ${removed.content}`;
|
|
|
6402
6850
|
import { Router as Router3 } from "express";
|
|
6403
6851
|
import { createHash as createHash5 } from "node:crypto";
|
|
6404
6852
|
function createIngestRouter(opts) {
|
|
6405
|
-
const { store, vaultPath, requireAuth,
|
|
6853
|
+
const { store, vaultPath, requireAuth, assertPublicUrl: assertPublicUrl2 } = opts;
|
|
6406
6854
|
const router = Router3();
|
|
6407
6855
|
router.post("/ingest", requireAuth, async (req, res) => {
|
|
6408
6856
|
try {
|
|
@@ -6419,6 +6867,14 @@ function createIngestRouter(opts) {
|
|
|
6419
6867
|
let autoTitle = title;
|
|
6420
6868
|
let autoTags = tags ?? [];
|
|
6421
6869
|
let autoStage = stage ?? "fleeting";
|
|
6870
|
+
if (input.startsWith("http")) {
|
|
6871
|
+
try {
|
|
6872
|
+
await assertPublicUrl2(input);
|
|
6873
|
+
} catch (e) {
|
|
6874
|
+
res.status(400).json({ error: e.message });
|
|
6875
|
+
return;
|
|
6876
|
+
}
|
|
6877
|
+
}
|
|
6422
6878
|
const isYouTube = /youtube\.com\/watch|youtu\.be\//.test(input);
|
|
6423
6879
|
if (isYouTube) {
|
|
6424
6880
|
try {
|
|
@@ -6442,12 +6898,6 @@ function createIngestRouter(opts) {
|
|
|
6442
6898
|
}
|
|
6443
6899
|
}
|
|
6444
6900
|
} else if (input.startsWith("http")) {
|
|
6445
|
-
try {
|
|
6446
|
-
assertNotPrivateUrl(input);
|
|
6447
|
-
} catch (e) {
|
|
6448
|
-
res.status(400).json({ error: e.message });
|
|
6449
|
-
return;
|
|
6450
|
-
}
|
|
6451
6901
|
try {
|
|
6452
6902
|
const resp = await fetch(input, { signal: AbortSignal.timeout(8e3) });
|
|
6453
6903
|
const html = await resp.text();
|
|
@@ -6539,10 +6989,10 @@ function createIngestRouter(opts) {
|
|
|
6539
6989
|
}
|
|
6540
6990
|
try {
|
|
6541
6991
|
const { writeFileSync: writeFileSync23, unlinkSync } = await import("node:fs");
|
|
6542
|
-
const { join:
|
|
6992
|
+
const { join: join33 } = await import("node:path");
|
|
6543
6993
|
const { tmpdir } = await import("node:os");
|
|
6544
6994
|
const safeName = (file.originalname ?? "upload").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 100);
|
|
6545
|
-
const tmpPath =
|
|
6995
|
+
const tmpPath = join33(tmpdir(), `sv-upload-${Date.now()}-${safeName}`);
|
|
6546
6996
|
writeFileSync23(tmpPath, file.buffer);
|
|
6547
6997
|
const { extractFileContent: extractFileContent2 } = await Promise.resolve().then(() => (init_file_extractors(), file_extractors_exports));
|
|
6548
6998
|
const ext = file.originalname.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -6621,7 +7071,7 @@ function createIngestRouter(opts) {
|
|
|
6621
7071
|
return;
|
|
6622
7072
|
}
|
|
6623
7073
|
try {
|
|
6624
|
-
|
|
7074
|
+
await assertPublicUrl2(url);
|
|
6625
7075
|
} catch (e) {
|
|
6626
7076
|
res.status(400).json({ error: e.message });
|
|
6627
7077
|
return;
|
|
@@ -6649,11 +7099,11 @@ ${desc}
|
|
|
6649
7099
|
if (content.length > 1e4)
|
|
6650
7100
|
content = content.slice(0, 1e4) + "\n\n...(truncated)";
|
|
6651
7101
|
}
|
|
6652
|
-
const { writeFileSync: writeFileSync23, mkdirSync:
|
|
6653
|
-
const { join:
|
|
7102
|
+
const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync24 } = await import("node:fs");
|
|
7103
|
+
const { join: join33 } = await import("node:path");
|
|
6654
7104
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6655
|
-
const clipDir =
|
|
6656
|
-
|
|
7105
|
+
const clipDir = join33(vaultPath || ".", "06_Research", "clips");
|
|
7106
|
+
mkdirSync24(clipDir, { recursive: true });
|
|
6657
7107
|
const fileName = `${date} ${safeTitle}.md`;
|
|
6658
7108
|
const md = `---
|
|
6659
7109
|
title: "${safeTitle}"
|
|
@@ -6667,8 +7117,8 @@ tags: [clip${isYT ? ", youtube" : ""}]
|
|
|
6667
7117
|
> Source: ${url}
|
|
6668
7118
|
|
|
6669
7119
|
${content}`;
|
|
6670
|
-
writeFileSync23(
|
|
6671
|
-
res.json({ success: true, fileName, path:
|
|
7120
|
+
writeFileSync23(join33(clipDir, fileName), md, "utf-8");
|
|
7121
|
+
res.json({ success: true, fileName, path: join33(clipDir, fileName) });
|
|
6672
7122
|
} catch (err) {
|
|
6673
7123
|
console.error(err);
|
|
6674
7124
|
res.status(500).json({ error: "Clip failed" });
|
|
@@ -6921,6 +7371,123 @@ function createAnalyticsRouter(opts) {
|
|
|
6921
7371
|
return router;
|
|
6922
7372
|
}
|
|
6923
7373
|
|
|
7374
|
+
// packages/core/dist/api/ssrf-guard.js
|
|
7375
|
+
import { lookup } from "node:dns/promises";
|
|
7376
|
+
import { isIP } from "node:net";
|
|
7377
|
+
var defaultResolver = async (host) => {
|
|
7378
|
+
const records = await lookup(host, { all: true });
|
|
7379
|
+
return records.map((r) => r.address);
|
|
7380
|
+
};
|
|
7381
|
+
function ipv4ToInt(ip) {
|
|
7382
|
+
const parts = ip.split(".");
|
|
7383
|
+
if (parts.length !== 4)
|
|
7384
|
+
return null;
|
|
7385
|
+
let value = 0;
|
|
7386
|
+
for (const part of parts) {
|
|
7387
|
+
if (!/^\d{1,3}$/.test(part))
|
|
7388
|
+
return null;
|
|
7389
|
+
const octet = Number(part);
|
|
7390
|
+
if (octet > 255)
|
|
7391
|
+
return null;
|
|
7392
|
+
value = value * 256 + octet;
|
|
7393
|
+
}
|
|
7394
|
+
return value >>> 0;
|
|
7395
|
+
}
|
|
7396
|
+
function isPrivateIpv4(ip) {
|
|
7397
|
+
const value = ipv4ToInt(ip);
|
|
7398
|
+
if (value === null)
|
|
7399
|
+
return false;
|
|
7400
|
+
const a = value >>> 24 & 255;
|
|
7401
|
+
const b = value >>> 16 & 255;
|
|
7402
|
+
if (a === 0 || a === 10 || a === 127)
|
|
7403
|
+
return true;
|
|
7404
|
+
if (a === 169 && b === 254)
|
|
7405
|
+
return true;
|
|
7406
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
7407
|
+
return true;
|
|
7408
|
+
if (a === 192 && b === 168)
|
|
7409
|
+
return true;
|
|
7410
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
7411
|
+
return true;
|
|
7412
|
+
return false;
|
|
7413
|
+
}
|
|
7414
|
+
function extractMappedIpv4(ip) {
|
|
7415
|
+
const lower = ip.toLowerCase();
|
|
7416
|
+
const idx = lower.lastIndexOf("::ffff:");
|
|
7417
|
+
if (idx === -1)
|
|
7418
|
+
return null;
|
|
7419
|
+
const tail = lower.slice(idx + "::ffff:".length);
|
|
7420
|
+
if (isIP(tail) === 4)
|
|
7421
|
+
return tail;
|
|
7422
|
+
const hexMatch = tail.match(/^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
|
|
7423
|
+
if (hexMatch) {
|
|
7424
|
+
const high = parseInt(hexMatch[1], 16);
|
|
7425
|
+
const low = parseInt(hexMatch[2], 16);
|
|
7426
|
+
return [high >> 8 & 255, high & 255, low >> 8 & 255, low & 255].join(".");
|
|
7427
|
+
}
|
|
7428
|
+
return null;
|
|
7429
|
+
}
|
|
7430
|
+
function isPrivateIp(ip) {
|
|
7431
|
+
const kind = isIP(ip);
|
|
7432
|
+
if (kind === 4)
|
|
7433
|
+
return isPrivateIpv4(ip);
|
|
7434
|
+
if (kind !== 6) {
|
|
7435
|
+
return true;
|
|
7436
|
+
}
|
|
7437
|
+
const lower = ip.toLowerCase();
|
|
7438
|
+
const mapped = extractMappedIpv4(lower);
|
|
7439
|
+
if (mapped !== null)
|
|
7440
|
+
return isPrivateIpv4(mapped);
|
|
7441
|
+
if (lower === "::" || lower === "0:0:0:0:0:0:0:0")
|
|
7442
|
+
return true;
|
|
7443
|
+
if (lower === "::1" || lower === "0:0:0:0:0:0:0:1")
|
|
7444
|
+
return true;
|
|
7445
|
+
const firstHextet = parseInt(lower.split(":")[0] || "0", 16);
|
|
7446
|
+
if (firstHextet >= 65152 && firstHextet <= 65215)
|
|
7447
|
+
return true;
|
|
7448
|
+
if (firstHextet >= 64512 && firstHextet <= 65023)
|
|
7449
|
+
return true;
|
|
7450
|
+
return false;
|
|
7451
|
+
}
|
|
7452
|
+
async function assertPublicUrl(url, resolver = defaultResolver) {
|
|
7453
|
+
let parsed;
|
|
7454
|
+
try {
|
|
7455
|
+
parsed = new URL(url);
|
|
7456
|
+
} catch {
|
|
7457
|
+
throw new Error("Internal or non-public URL not allowed");
|
|
7458
|
+
}
|
|
7459
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
7460
|
+
throw new Error("Only http/https URLs allowed");
|
|
7461
|
+
}
|
|
7462
|
+
let host = parsed.hostname;
|
|
7463
|
+
if (host.startsWith("[") && host.endsWith("]")) {
|
|
7464
|
+
host = host.slice(1, -1);
|
|
7465
|
+
}
|
|
7466
|
+
const lowerHost = host.toLowerCase();
|
|
7467
|
+
const h = lowerHost.replace(/\.$/, "");
|
|
7468
|
+
if (h === "localhost" || h.endsWith(".localhost") || h.endsWith(".local")) {
|
|
7469
|
+
throw new Error("Internal or non-public URL not allowed");
|
|
7470
|
+
}
|
|
7471
|
+
let addresses;
|
|
7472
|
+
if (isIP(host) !== 0) {
|
|
7473
|
+
addresses = [host];
|
|
7474
|
+
} else {
|
|
7475
|
+
try {
|
|
7476
|
+
addresses = await resolver(host);
|
|
7477
|
+
} catch {
|
|
7478
|
+
throw new Error("Internal or non-public URL not allowed");
|
|
7479
|
+
}
|
|
7480
|
+
}
|
|
7481
|
+
if (addresses.length === 0) {
|
|
7482
|
+
throw new Error("Internal or non-public URL not allowed");
|
|
7483
|
+
}
|
|
7484
|
+
for (const addr of addresses) {
|
|
7485
|
+
if (isPrivateIp(addr)) {
|
|
7486
|
+
throw new Error("Internal or non-public URL not allowed");
|
|
7487
|
+
}
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
|
|
6924
7491
|
// packages/core/dist/api/server.js
|
|
6925
7492
|
function createApiServer(options) {
|
|
6926
7493
|
const { store, searchEngine, port = 3333, vaultName = "", vaultPath = "", decayEngine, graphUiPath } = options;
|
|
@@ -6976,16 +7543,6 @@ function createApiServer(options) {
|
|
|
6976
7543
|
}
|
|
6977
7544
|
res.json({ token: authToken });
|
|
6978
7545
|
});
|
|
6979
|
-
function assertNotPrivateUrl(url) {
|
|
6980
|
-
const parsed = new URL(url);
|
|
6981
|
-
if (!["http:", "https:"].includes(parsed.protocol))
|
|
6982
|
-
throw new Error("Only http/https URLs allowed");
|
|
6983
|
-
const host = parsed.hostname.toLowerCase();
|
|
6984
|
-
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.startsWith("192.168.") || host.startsWith("10.") || // Fix: 172.16.0.0/12 covers 172.16.* through 172.31.*
|
|
6985
|
-
/^172\.(1[6-9]|2\d|3[01])\./.test(host) || host.startsWith("169.254.") || host === "0.0.0.0") {
|
|
6986
|
-
throw new Error("Internal URLs not allowed");
|
|
6987
|
-
}
|
|
6988
|
-
}
|
|
6989
7546
|
if (graphUiPath) {
|
|
6990
7547
|
app.use(express.static(graphUiPath, { index: "index.html", extensions: ["html"] }));
|
|
6991
7548
|
}
|
|
@@ -7156,7 +7713,7 @@ function createApiServer(options) {
|
|
|
7156
7713
|
res.status(404).json({ error: "Document not found" });
|
|
7157
7714
|
return;
|
|
7158
7715
|
}
|
|
7159
|
-
const { resolve: resolve22, join:
|
|
7716
|
+
const { resolve: resolve22, join: join33 } = await import("node:path");
|
|
7160
7717
|
const { writeFileSync: writeFileSync23, readFileSync: readFileSync19 } = await import("node:fs");
|
|
7161
7718
|
const fullPath = resolve22(vaultPath, doc.filePath);
|
|
7162
7719
|
if (!fullPath.startsWith(resolve22(vaultPath))) {
|
|
@@ -7207,13 +7764,13 @@ function createApiServer(options) {
|
|
|
7207
7764
|
return;
|
|
7208
7765
|
}
|
|
7209
7766
|
const { resolve: resolve22 } = await import("node:path");
|
|
7210
|
-
const { unlinkSync, existsSync:
|
|
7767
|
+
const { unlinkSync, existsSync: existsSync28 } = await import("node:fs");
|
|
7211
7768
|
const fullPath = resolve22(vaultPath, doc.filePath);
|
|
7212
7769
|
if (!fullPath.startsWith(resolve22(vaultPath))) {
|
|
7213
7770
|
res.status(403).json({ error: "Access denied" });
|
|
7214
7771
|
return;
|
|
7215
7772
|
}
|
|
7216
|
-
if (
|
|
7773
|
+
if (existsSync28(fullPath)) {
|
|
7217
7774
|
unlinkSync(fullPath);
|
|
7218
7775
|
}
|
|
7219
7776
|
await store.deleteByDocumentId(id);
|
|
@@ -7242,7 +7799,7 @@ function createApiServer(options) {
|
|
|
7242
7799
|
res.status(500).json({ error: "Ask failed" });
|
|
7243
7800
|
}
|
|
7244
7801
|
});
|
|
7245
|
-
app.use("/api", createIngestRouter({ store, vaultPath, requireAuth,
|
|
7802
|
+
app.use("/api", createIngestRouter({ store, vaultPath, requireAuth, assertPublicUrl }));
|
|
7246
7803
|
app.get("/api/recent", async (_req, res) => {
|
|
7247
7804
|
try {
|
|
7248
7805
|
const docs = await store.getAllDocuments();
|
|
@@ -7339,12 +7896,12 @@ function createApiServer(options) {
|
|
|
7339
7896
|
const { resolve: resolve22 } = await import("node:path");
|
|
7340
7897
|
const syncScript = resolve22(process.cwd(), "packages/sync/sync-to-obsidian.mjs");
|
|
7341
7898
|
const syncDir = resolve22(process.cwd(), "packages/sync");
|
|
7342
|
-
const { existsSync:
|
|
7343
|
-
if (!
|
|
7899
|
+
const { existsSync: existsSync28 } = await import("node:fs");
|
|
7900
|
+
if (!existsSync28(syncScript)) {
|
|
7344
7901
|
res.json({ success: false, error: "sync script not found" });
|
|
7345
7902
|
return;
|
|
7346
7903
|
}
|
|
7347
|
-
if (!
|
|
7904
|
+
if (!existsSync28(resolve22(syncDir, ".env"))) {
|
|
7348
7905
|
res.json({ success: false, error: ".env not found" });
|
|
7349
7906
|
return;
|
|
7350
7907
|
}
|
|
@@ -7393,6 +7950,7 @@ function createApiServer(options) {
|
|
|
7393
7950
|
}
|
|
7394
7951
|
|
|
7395
7952
|
// packages/core/dist/index.js
|
|
7953
|
+
init_graph_data();
|
|
7396
7954
|
init_duplicate_detector();
|
|
7397
7955
|
init_gap_detector();
|
|
7398
7956
|
|
|
@@ -7511,6 +8069,7 @@ function cosineSim(a, b) {
|
|
|
7511
8069
|
// packages/core/dist/index.js
|
|
7512
8070
|
init_ask_engine();
|
|
7513
8071
|
init_wiki_compiler();
|
|
8072
|
+
init_auto_linker();
|
|
7514
8073
|
|
|
7515
8074
|
// packages/core/dist/intelligence/knowledge-lint.js
|
|
7516
8075
|
init_gap_detector();
|
|
@@ -7632,18 +8191,30 @@ async function lintKnowledge(store) {
|
|
|
7632
8191
|
init_zettelkasten();
|
|
7633
8192
|
init_ingest_pipeline();
|
|
7634
8193
|
|
|
8194
|
+
// packages/core/dist/intelligence/classify/classify.js
|
|
8195
|
+
init_math();
|
|
8196
|
+
|
|
8197
|
+
// packages/core/dist/intelligence/classify/cluster.js
|
|
8198
|
+
init_math();
|
|
8199
|
+
|
|
8200
|
+
// packages/core/dist/intelligence/classify/discover.js
|
|
8201
|
+
init_math();
|
|
8202
|
+
|
|
8203
|
+
// packages/core/dist/index.js
|
|
8204
|
+
init_file_extractors();
|
|
8205
|
+
|
|
7635
8206
|
// packages/core/dist/multi-vault/index.js
|
|
7636
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync12, existsSync as
|
|
7637
|
-
import { join as
|
|
7638
|
-
import { homedir as
|
|
7639
|
-
var VAULTS_FILE =
|
|
8207
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync12, existsSync as existsSync13, mkdirSync as mkdirSync13 } from "node:fs";
|
|
8208
|
+
import { join as join14 } from "node:path";
|
|
8209
|
+
import { homedir as homedir7 } from "node:os";
|
|
8210
|
+
var VAULTS_FILE = join14(homedir7(), ".stellavault", "vaults.json");
|
|
7640
8211
|
function loadVaults() {
|
|
7641
|
-
if (!
|
|
8212
|
+
if (!existsSync13(VAULTS_FILE))
|
|
7642
8213
|
return [];
|
|
7643
8214
|
return JSON.parse(readFileSync13(VAULTS_FILE, "utf-8"));
|
|
7644
8215
|
}
|
|
7645
8216
|
function saveVaults(vaults) {
|
|
7646
|
-
|
|
8217
|
+
mkdirSync13(join14(homedir7(), ".stellavault"), { recursive: true });
|
|
7647
8218
|
writeFileSync12(VAULTS_FILE, JSON.stringify(vaults, null, 2), "utf-8");
|
|
7648
8219
|
}
|
|
7649
8220
|
function addVault(id, name, vaultPath, dbPath, shared = false) {
|
|
@@ -7674,7 +8245,7 @@ async function searchAllVaults(query, embedder, createStore, options = {}) {
|
|
|
7674
8245
|
const embedding = await embedder.embed(query);
|
|
7675
8246
|
const searches = vaults.map(async (vault2) => {
|
|
7676
8247
|
try {
|
|
7677
|
-
if (!
|
|
8248
|
+
if (!existsSync13(vault2.dbPath))
|
|
7678
8249
|
return;
|
|
7679
8250
|
const store = createStore(vault2.dbPath);
|
|
7680
8251
|
await store.initialize();
|
|
@@ -7703,8 +8274,8 @@ async function searchAllVaults(query, embedder, createStore, options = {}) {
|
|
|
7703
8274
|
|
|
7704
8275
|
// packages/core/dist/capture/voice.js
|
|
7705
8276
|
import { execFileSync, execSync } from "node:child_process";
|
|
7706
|
-
import { writeFileSync as writeFileSync13, mkdirSync as
|
|
7707
|
-
import { join as
|
|
8277
|
+
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, existsSync as existsSync14 } from "node:fs";
|
|
8278
|
+
import { join as join15, basename as basename4 } from "node:path";
|
|
7708
8279
|
var ALLOWED_MODELS = ["tiny", "base", "small", "medium", "large"];
|
|
7709
8280
|
var ALLOWED_LANGUAGES = ["auto", "en", "ko", "ja", "zh", "es", "fr", "de", "it", "pt", "ru", "ar", "hi"];
|
|
7710
8281
|
function validateModel(model) {
|
|
@@ -7727,7 +8298,7 @@ function isWhisperAvailable() {
|
|
|
7727
8298
|
}
|
|
7728
8299
|
async function transcribeAudio(audioPath, options = {}) {
|
|
7729
8300
|
const { model = "base", language } = options;
|
|
7730
|
-
if (!
|
|
8301
|
+
if (!existsSync14(audioPath)) {
|
|
7731
8302
|
throw new Error(`Audio file not found: ${audioPath}`);
|
|
7732
8303
|
}
|
|
7733
8304
|
if (isWhisperAvailable()) {
|
|
@@ -7735,12 +8306,12 @@ async function transcribeAudio(audioPath, options = {}) {
|
|
|
7735
8306
|
const args = [audioPath, "--model", safeModel, "--output_format", "txt", "--output_dir", "/tmp/sv-whisper"];
|
|
7736
8307
|
if (language)
|
|
7737
8308
|
args.push("--language", validateLanguage(language));
|
|
7738
|
-
|
|
8309
|
+
mkdirSync14("/tmp/sv-whisper", { recursive: true });
|
|
7739
8310
|
try {
|
|
7740
8311
|
execFileSync("whisper", args, { stdio: "pipe", timeout: 3e5 });
|
|
7741
8312
|
const outputName = basename4(audioPath).replace(/\.[^.]+$/, ".txt");
|
|
7742
8313
|
const { readFileSync: readFileSync19 } = await import("node:fs");
|
|
7743
|
-
return readFileSync19(
|
|
8314
|
+
return readFileSync19(join15("/tmp/sv-whisper", outputName), "utf-8").trim();
|
|
7744
8315
|
} catch (err) {
|
|
7745
8316
|
throw new Error(`Whisper failed: ${err instanceof Error ? err.message : err}`);
|
|
7746
8317
|
}
|
|
@@ -7797,9 +8368,9 @@ async function captureVoice(audioPath, options) {
|
|
|
7797
8368
|
transcript,
|
|
7798
8369
|
""
|
|
7799
8370
|
].join("\n");
|
|
7800
|
-
const dir =
|
|
7801
|
-
|
|
7802
|
-
const filePath =
|
|
8371
|
+
const dir = join15(vaultPath, folder);
|
|
8372
|
+
mkdirSync14(dir, { recursive: true });
|
|
8373
|
+
const filePath = join15(dir, `${date.slice(0, 10)} ${safeTitle}.md`);
|
|
7803
8374
|
writeFileSync13(filePath, content, "utf-8");
|
|
7804
8375
|
return {
|
|
7805
8376
|
title: safeTitle,
|
|
@@ -7822,12 +8393,12 @@ async function captureVoice(audioPath, options) {
|
|
|
7822
8393
|
|
|
7823
8394
|
// packages/core/dist/cloud/sync.js
|
|
7824
8395
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes4, createHash as createHash6 } from "node:crypto";
|
|
7825
|
-
import { readFileSync as readFileSync14, writeFileSync as writeFileSync14, existsSync as
|
|
7826
|
-
import { join as
|
|
7827
|
-
import { homedir as
|
|
7828
|
-
var CLOUD_DIR =
|
|
7829
|
-
var KEY_FILE =
|
|
7830
|
-
var SYNC_STATE_FILE =
|
|
8396
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync14, existsSync as existsSync15, mkdirSync as mkdirSync15, chmodSync as chmodSync2 } from "node:fs";
|
|
8397
|
+
import { join as join16 } from "node:path";
|
|
8398
|
+
import { homedir as homedir8 } from "node:os";
|
|
8399
|
+
var CLOUD_DIR = join16(homedir8(), ".stellavault", "cloud");
|
|
8400
|
+
var KEY_FILE = join16(CLOUD_DIR, "encryption.key");
|
|
8401
|
+
var SYNC_STATE_FILE = join16(CLOUD_DIR, "sync-state.json");
|
|
7831
8402
|
function encrypt(data, key) {
|
|
7832
8403
|
const iv = randomBytes4(16);
|
|
7833
8404
|
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
@@ -7841,13 +8412,13 @@ function decrypt(encrypted, key, iv, tag) {
|
|
|
7841
8412
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
7842
8413
|
}
|
|
7843
8414
|
function getOrCreateEncryptionKey(userKey) {
|
|
7844
|
-
|
|
8415
|
+
mkdirSync15(CLOUD_DIR, { recursive: true });
|
|
7845
8416
|
if (userKey) {
|
|
7846
8417
|
const key2 = createHash6("sha256").update(userKey).digest();
|
|
7847
8418
|
writeFileSync14(KEY_FILE, key2.toString("hex"), "utf-8");
|
|
7848
8419
|
return key2;
|
|
7849
8420
|
}
|
|
7850
|
-
if (
|
|
8421
|
+
if (existsSync15(KEY_FILE)) {
|
|
7851
8422
|
return Buffer.from(readFileSync14(KEY_FILE, "utf-8").trim(), "hex");
|
|
7852
8423
|
}
|
|
7853
8424
|
const key = randomBytes4(32);
|
|
@@ -7897,7 +8468,7 @@ async function s3Get(config, objectKey) {
|
|
|
7897
8468
|
async function syncToCloud(dbPath, config) {
|
|
7898
8469
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
7899
8470
|
try {
|
|
7900
|
-
if (!
|
|
8471
|
+
if (!existsSync15(dbPath)) {
|
|
7901
8472
|
return { action: "upload", dbSize: 0, encryptedSize: 0, timestamp, success: false, error: "DB not found" };
|
|
7902
8473
|
}
|
|
7903
8474
|
const dbData = readFileSync14(dbPath);
|
|
@@ -7907,7 +8478,7 @@ async function syncToCloud(dbPath, config) {
|
|
|
7907
8478
|
const objectKey = `stellavault/index.db.enc`;
|
|
7908
8479
|
const success = await s3Put(config, objectKey, payload);
|
|
7909
8480
|
const state = { lastSync: timestamp, dbSize: dbData.length, encryptedSize: payload.length, objectKey };
|
|
7910
|
-
|
|
8481
|
+
mkdirSync15(CLOUD_DIR, { recursive: true });
|
|
7911
8482
|
writeFileSync14(SYNC_STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
|
|
7912
8483
|
return { action: "upload", dbSize: dbData.length, encryptedSize: payload.length, timestamp, success };
|
|
7913
8484
|
} catch (err) {
|
|
@@ -7927,7 +8498,7 @@ async function restoreFromCloud(dbPath, config) {
|
|
|
7927
8498
|
const tag = payload.subarray(16, 32);
|
|
7928
8499
|
const encrypted = payload.subarray(32);
|
|
7929
8500
|
const dbData = decrypt(encrypted, key, iv, tag);
|
|
7930
|
-
if (
|
|
8501
|
+
if (existsSync15(dbPath)) {
|
|
7931
8502
|
writeFileSync14(dbPath + ".backup", readFileSync14(dbPath));
|
|
7932
8503
|
}
|
|
7933
8504
|
writeFileSync14(dbPath, dbData);
|
|
@@ -7937,40 +8508,40 @@ async function restoreFromCloud(dbPath, config) {
|
|
|
7937
8508
|
}
|
|
7938
8509
|
}
|
|
7939
8510
|
function getSyncState() {
|
|
7940
|
-
if (!
|
|
8511
|
+
if (!existsSync15(SYNC_STATE_FILE))
|
|
7941
8512
|
return null;
|
|
7942
8513
|
return JSON.parse(readFileSync14(SYNC_STATE_FILE, "utf-8"));
|
|
7943
8514
|
}
|
|
7944
8515
|
|
|
7945
8516
|
// packages/core/dist/team/index.js
|
|
7946
|
-
import { join as
|
|
7947
|
-
import { homedir as
|
|
7948
|
-
var TEAM_DIR =
|
|
7949
|
-
var TEAM_FILE =
|
|
8517
|
+
import { join as join17 } from "node:path";
|
|
8518
|
+
import { homedir as homedir9 } from "node:os";
|
|
8519
|
+
var TEAM_DIR = join17(homedir9(), ".stellavault", "team");
|
|
8520
|
+
var TEAM_FILE = join17(TEAM_DIR, "team.json");
|
|
7950
8521
|
|
|
7951
8522
|
// packages/core/dist/index.js
|
|
7952
8523
|
init_federation();
|
|
7953
8524
|
|
|
7954
8525
|
// packages/core/dist/federation/trust.js
|
|
7955
|
-
import { join as
|
|
7956
|
-
import { homedir as
|
|
7957
|
-
var TRUST_FILE =
|
|
8526
|
+
import { join as join18 } from "node:path";
|
|
8527
|
+
import { homedir as homedir10 } from "node:os";
|
|
8528
|
+
var TRUST_FILE = join18(homedir10(), ".stellavault", "federation", "trust.json");
|
|
7958
8529
|
|
|
7959
8530
|
// packages/core/dist/index.js
|
|
7960
8531
|
init_sharing();
|
|
7961
8532
|
|
|
7962
8533
|
// packages/core/dist/federation/reputation.js
|
|
7963
|
-
import { join as
|
|
7964
|
-
import { homedir as
|
|
7965
|
-
var REP_FILE =
|
|
8534
|
+
import { join as join19 } from "node:path";
|
|
8535
|
+
import { homedir as homedir11 } from "node:os";
|
|
8536
|
+
var REP_FILE = join19(homedir11(), ".stellavault", "federation", "reputation.json");
|
|
7966
8537
|
|
|
7967
8538
|
// packages/core/dist/index.js
|
|
7968
8539
|
init_privacy();
|
|
7969
8540
|
|
|
7970
8541
|
// packages/core/dist/federation/credits.js
|
|
7971
|
-
import { join as
|
|
7972
|
-
import { homedir as
|
|
7973
|
-
var CREDITS_FILE =
|
|
8542
|
+
import { join as join20 } from "node:path";
|
|
8543
|
+
import { homedir as homedir12 } from "node:os";
|
|
8544
|
+
var CREDITS_FILE = join20(homedir12(), ".stellavault", "federation", "credits.json");
|
|
7974
8545
|
|
|
7975
8546
|
// packages/core/dist/index.js
|
|
7976
8547
|
init_retry();
|
|
@@ -8006,15 +8577,15 @@ function createKnowledgeHub(config, options = {}) {
|
|
|
8006
8577
|
// B2.2 — cross-lingual/synonym groups
|
|
8007
8578
|
});
|
|
8008
8579
|
const mcpServer = createMcpServer({ store, searchEngine, vaultPath: config.vaultPath, ready: options.ready });
|
|
8009
|
-
return { store, embedder, searchEngine, mcpServer, config };
|
|
8580
|
+
return { store, embedder, searchEngine, mcpServer, config, getDecayEngine };
|
|
8010
8581
|
}
|
|
8011
8582
|
|
|
8012
8583
|
// packages/cli/dist/commands/index-cmd.js
|
|
8013
8584
|
function getVaultDbPath(vaultPath) {
|
|
8014
8585
|
const hash = createHash7("sha256").update(vaultPath).digest("hex").slice(0, 8);
|
|
8015
|
-
const dir =
|
|
8016
|
-
|
|
8017
|
-
return
|
|
8586
|
+
const dir = join21(homedir13(), ".stellavault", "vaults");
|
|
8587
|
+
mkdirSync16(dir, { recursive: true });
|
|
8588
|
+
return join21(dir, `${hash}.db`);
|
|
8018
8589
|
}
|
|
8019
8590
|
function resolveDbPath(vault2, configDbPath) {
|
|
8020
8591
|
const envDbPath = process.env.STELLAVAULT_DB_PATH?.trim();
|
|
@@ -8305,19 +8876,19 @@ async function statusCommand(_opts, cmd) {
|
|
|
8305
8876
|
import chalk6 from "chalk";
|
|
8306
8877
|
import { spawn } from "node:child_process";
|
|
8307
8878
|
import { resolve as resolve11, dirname as dirname5 } from "node:path";
|
|
8308
|
-
import { existsSync as
|
|
8879
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
8309
8880
|
import { fileURLToPath } from "node:url";
|
|
8310
8881
|
function locateBundledGraphUi() {
|
|
8311
8882
|
try {
|
|
8312
8883
|
const here = dirname5(fileURLToPath(import.meta.url));
|
|
8313
8884
|
const bundled = resolve11(here, "graph-ui");
|
|
8314
|
-
if (
|
|
8885
|
+
if (existsSync18(resolve11(bundled, "index.html")))
|
|
8315
8886
|
return bundled;
|
|
8316
8887
|
const monorepoGraphDist = resolve11(here, "..", "..", "..", "graph", "dist");
|
|
8317
|
-
if (
|
|
8888
|
+
if (existsSync18(resolve11(monorepoGraphDist, "index.html")))
|
|
8318
8889
|
return monorepoGraphDist;
|
|
8319
8890
|
const singleFileBundle = resolve11(here, "..", "dist", "graph-ui");
|
|
8320
|
-
if (
|
|
8891
|
+
if (existsSync18(resolve11(singleFileBundle, "index.html")))
|
|
8321
8892
|
return singleFileBundle;
|
|
8322
8893
|
} catch {
|
|
8323
8894
|
}
|
|
@@ -8368,7 +8939,7 @@ async function graphCommand() {
|
|
|
8368
8939
|
return;
|
|
8369
8940
|
}
|
|
8370
8941
|
const devGraphDir = resolve11(process.cwd(), "packages/graph");
|
|
8371
|
-
const hasDevGraph =
|
|
8942
|
+
const hasDevGraph = existsSync18(resolve11(devGraphDir, "package.json"));
|
|
8372
8943
|
if (hasDevGraph) {
|
|
8373
8944
|
console.error(chalk6.dim(" \u{1F680} Starting Vite dev server..."));
|
|
8374
8945
|
const vite = spawn(process.platform === "win32" ? "npx.cmd" : "npx", ["vite", "--host"], {
|
|
@@ -8430,10 +9001,10 @@ async function cardCommand(options) {
|
|
|
8430
9001
|
|
|
8431
9002
|
// packages/cli/dist/commands/pack-cmd.js
|
|
8432
9003
|
import chalk8 from "chalk";
|
|
8433
|
-
import { resolve as resolve13, join as
|
|
8434
|
-
import { readdirSync as readdirSync6, existsSync as
|
|
8435
|
-
import { homedir as
|
|
8436
|
-
var PACKS_DIR =
|
|
9004
|
+
import { resolve as resolve13, join as join24 } from "node:path";
|
|
9005
|
+
import { readdirSync as readdirSync6, existsSync as existsSync19, readFileSync as readFileSync16, mkdirSync as mkdirSync18 } from "node:fs";
|
|
9006
|
+
import { homedir as homedir16 } from "node:os";
|
|
9007
|
+
var PACKS_DIR = join24(homedir16(), ".stellavault", "packs");
|
|
8437
9008
|
async function packCreateCommand(name, options) {
|
|
8438
9009
|
const config = loadConfig();
|
|
8439
9010
|
const hub = createKnowledgeHub(config);
|
|
@@ -8449,8 +9020,8 @@ async function packCreateCommand(name, options) {
|
|
|
8449
9020
|
description: options.description,
|
|
8450
9021
|
limit: options.limit ? parseInt(options.limit) : 100
|
|
8451
9022
|
});
|
|
8452
|
-
|
|
8453
|
-
const outPath =
|
|
9023
|
+
mkdirSync18(PACKS_DIR, { recursive: true });
|
|
9024
|
+
const outPath = join24(PACKS_DIR, `${name}.sv-pack`);
|
|
8454
9025
|
exportPack(pack2, outPath);
|
|
8455
9026
|
console.error(chalk8.green(`\u2705 Pack created: ${name}`));
|
|
8456
9027
|
console.error(` \u{1F4E6} ${pack2.chunks.length} chunks`);
|
|
@@ -8461,8 +9032,8 @@ async function packCreateCommand(name, options) {
|
|
|
8461
9032
|
await hub.store.close();
|
|
8462
9033
|
}
|
|
8463
9034
|
async function packExportCommand(name, options) {
|
|
8464
|
-
const srcPath =
|
|
8465
|
-
if (!
|
|
9035
|
+
const srcPath = join24(PACKS_DIR, `${name}.sv-pack`);
|
|
9036
|
+
if (!existsSync19(srcPath)) {
|
|
8466
9037
|
console.error(chalk8.red(`\u274C Pack not found: ${name}`));
|
|
8467
9038
|
process.exit(1);
|
|
8468
9039
|
}
|
|
@@ -8474,7 +9045,7 @@ async function packExportCommand(name, options) {
|
|
|
8474
9045
|
}
|
|
8475
9046
|
async function packImportCommand(filePath) {
|
|
8476
9047
|
const absPath = resolve13(process.cwd(), filePath);
|
|
8477
|
-
if (!
|
|
9048
|
+
if (!existsSync19(absPath)) {
|
|
8478
9049
|
console.error(chalk8.red(`\u274C File not found: ${absPath}`));
|
|
8479
9050
|
process.exit(1);
|
|
8480
9051
|
}
|
|
@@ -8493,7 +9064,7 @@ async function packImportCommand(filePath) {
|
|
|
8493
9064
|
await hub.store.close();
|
|
8494
9065
|
}
|
|
8495
9066
|
async function packListCommand() {
|
|
8496
|
-
|
|
9067
|
+
mkdirSync18(PACKS_DIR, { recursive: true });
|
|
8497
9068
|
const files = readdirSync6(PACKS_DIR).filter((f) => f.endsWith(".sv-pack"));
|
|
8498
9069
|
if (files.length === 0) {
|
|
8499
9070
|
console.error(chalk8.dim("No packs found. Create one: stellavault pack create <name> --from-search <query>"));
|
|
@@ -8503,7 +9074,7 @@ async function packListCommand() {
|
|
|
8503
9074
|
`));
|
|
8504
9075
|
for (const file of files) {
|
|
8505
9076
|
try {
|
|
8506
|
-
const pack2 = JSON.parse(readFileSync16(
|
|
9077
|
+
const pack2 = JSON.parse(readFileSync16(join24(PACKS_DIR, file), "utf-8"));
|
|
8507
9078
|
console.error(` ${chalk8.bold(pack2.name)} v${pack2.version} \u2014 ${pack2.chunks.length} chunks (${pack2.license})`);
|
|
8508
9079
|
} catch {
|
|
8509
9080
|
console.error(` ${file} (invalid)`);
|
|
@@ -8511,8 +9082,8 @@ async function packListCommand() {
|
|
|
8511
9082
|
}
|
|
8512
9083
|
}
|
|
8513
9084
|
async function packInfoCommand(name) {
|
|
8514
|
-
const filePath =
|
|
8515
|
-
if (!
|
|
9085
|
+
const filePath = join24(PACKS_DIR, `${name}.sv-pack`);
|
|
9086
|
+
if (!existsSync19(filePath)) {
|
|
8516
9087
|
console.error(chalk8.red(`\u274C Pack not found: ${name}`));
|
|
8517
9088
|
process.exit(1);
|
|
8518
9089
|
}
|
|
@@ -8570,24 +9141,24 @@ async function decayCommand(_opts, cmd) {
|
|
|
8570
9141
|
import chalk10 from "chalk";
|
|
8571
9142
|
import { spawn as spawn2 } from "node:child_process";
|
|
8572
9143
|
import { resolve as resolve14 } from "node:path";
|
|
8573
|
-
import { existsSync as
|
|
9144
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
8574
9145
|
async function syncCommand(options) {
|
|
8575
9146
|
const syncDir = resolve14(process.cwd(), "packages/sync");
|
|
8576
9147
|
const syncScript = resolve14(syncDir, "sync-to-obsidian.mjs");
|
|
8577
|
-
if (!
|
|
9148
|
+
if (!existsSync20(syncScript)) {
|
|
8578
9149
|
console.error(chalk10.red("\u274C packages/sync/sync-to-obsidian.mjs not found"));
|
|
8579
9150
|
console.error(chalk10.dim(" Run from project root: cd notion-obsidian-sync"));
|
|
8580
9151
|
process.exit(1);
|
|
8581
9152
|
}
|
|
8582
9153
|
const envFile = resolve14(syncDir, ".env");
|
|
8583
|
-
if (!
|
|
9154
|
+
if (!existsSync20(envFile)) {
|
|
8584
9155
|
console.error(chalk10.red("\u274C packages/sync/.env not found"));
|
|
8585
9156
|
console.error(chalk10.dim(" Copy .env.example \u2192 .env and set NOTION_TOKEN"));
|
|
8586
9157
|
process.exit(1);
|
|
8587
9158
|
}
|
|
8588
9159
|
if (options.upload) {
|
|
8589
9160
|
const uploadScript = resolve14(syncDir, "upload-pdca-to-notion.mjs");
|
|
8590
|
-
if (!
|
|
9161
|
+
if (!existsSync20(uploadScript)) {
|
|
8591
9162
|
console.error(chalk10.red("\u274C upload-pdca-to-notion.mjs not found"));
|
|
8592
9163
|
process.exit(1);
|
|
8593
9164
|
}
|
|
@@ -8839,8 +9410,8 @@ async function gapsCommand() {
|
|
|
8839
9410
|
|
|
8840
9411
|
// packages/cli/dist/commands/clip-cmd.js
|
|
8841
9412
|
import chalk14 from "chalk";
|
|
8842
|
-
import { writeFileSync as writeFileSync18, mkdirSync as
|
|
8843
|
-
import { join as
|
|
9413
|
+
import { writeFileSync as writeFileSync18, mkdirSync as mkdirSync19 } from "node:fs";
|
|
9414
|
+
import { join as join25 } from "node:path";
|
|
8844
9415
|
async function clipCommand(url, options) {
|
|
8845
9416
|
if (!url) {
|
|
8846
9417
|
console.error(chalk14.red("\u274C Please provide a URL: stellavault clip <url>"));
|
|
@@ -8853,9 +9424,15 @@ async function clipCommand(url, options) {
|
|
|
8853
9424
|
process.exit(1);
|
|
8854
9425
|
}
|
|
8855
9426
|
const folder = options.folder ?? "06_Research/clips";
|
|
8856
|
-
const targetDir =
|
|
8857
|
-
|
|
9427
|
+
const targetDir = join25(vaultPath, folder);
|
|
9428
|
+
mkdirSync19(targetDir, { recursive: true });
|
|
8858
9429
|
console.error(chalk14.dim(`\u{1F4CE} Clipping: ${url}`));
|
|
9430
|
+
try {
|
|
9431
|
+
await assertPublicUrl(url);
|
|
9432
|
+
} catch {
|
|
9433
|
+
console.error(chalk14.yellow("Private/local or non-public URLs are not allowed for security."));
|
|
9434
|
+
process.exit(1);
|
|
9435
|
+
}
|
|
8859
9436
|
try {
|
|
8860
9437
|
const isYouTube = /youtube\.com\/watch|youtu\.be\//.test(url);
|
|
8861
9438
|
let title;
|
|
@@ -8872,7 +9449,7 @@ async function clipCommand(url, options) {
|
|
|
8872
9449
|
const safeTitle = title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ").trim().slice(0, 80);
|
|
8873
9450
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8874
9451
|
const fileName = `${date} ${safeTitle}.md`;
|
|
8875
|
-
const filePath =
|
|
9452
|
+
const filePath = join25(targetDir, fileName);
|
|
8876
9453
|
const md = [
|
|
8877
9454
|
"---",
|
|
8878
9455
|
`title: "${safeTitle}"`,
|
|
@@ -9089,14 +9666,14 @@ async function digestCommand(options) {
|
|
|
9089
9666
|
\u{1F9E0} Health: R=${report.averageR} | Decaying ${report.decayingCount} | Critical ${report.criticalCount}`);
|
|
9090
9667
|
console.log(chalk16.dim("\n\u2550".repeat(50)));
|
|
9091
9668
|
if (options.visual) {
|
|
9092
|
-
const { writeFileSync: writeFileSync23, mkdirSync:
|
|
9093
|
-
const { join:
|
|
9669
|
+
const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync24, existsSync: existsSync28 } = await import("node:fs");
|
|
9670
|
+
const { join: join33, resolve: resolve22 } = await import("node:path");
|
|
9094
9671
|
const outputDir = resolve22(config.vaultPath, "_stellavault/digests");
|
|
9095
|
-
if (!
|
|
9096
|
-
|
|
9672
|
+
if (!existsSync28(outputDir))
|
|
9673
|
+
mkdirSync24(outputDir, { recursive: true });
|
|
9097
9674
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9098
9675
|
const filename = `digest-${date}.md`;
|
|
9099
|
-
const outputPath =
|
|
9676
|
+
const outputPath = join33(outputDir, filename);
|
|
9100
9677
|
const pieData = (typeStats.length > 0 ? typeStats : [{ type: "note", cnt: 1 }]).map((t2) => ` "${t2.type}" : ${t2.cnt}`).join("\n");
|
|
9101
9678
|
const timelineData = dailyActivity.map((d) => ` ${d.day.slice(5)} : ${d.cnt}`).join("\n");
|
|
9102
9679
|
const md = [
|
|
@@ -9143,9 +9720,9 @@ Visual digest saved: ${filename}`));
|
|
|
9143
9720
|
|
|
9144
9721
|
// packages/cli/dist/commands/init-cmd.js
|
|
9145
9722
|
import { createInterface as createInterface2 } from "node:readline";
|
|
9146
|
-
import { existsSync as
|
|
9147
|
-
import { join as
|
|
9148
|
-
import { homedir as
|
|
9723
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync20, writeFileSync as writeFileSync19 } from "node:fs";
|
|
9724
|
+
import { join as join26 } from "node:path";
|
|
9725
|
+
import { homedir as homedir17 } from "node:os";
|
|
9149
9726
|
import ora2 from "ora";
|
|
9150
9727
|
import chalk17 from "chalk";
|
|
9151
9728
|
function ask(rl, question, defaultVal) {
|
|
@@ -9171,8 +9748,8 @@ async function initCommand() {
|
|
|
9171
9748
|
console.log(chalk17.yellow(" Please enter your vault path."));
|
|
9172
9749
|
continue;
|
|
9173
9750
|
}
|
|
9174
|
-
const resolved = input.replace(/^~/,
|
|
9175
|
-
if (!
|
|
9751
|
+
const resolved = input.replace(/^~/, homedir17());
|
|
9752
|
+
if (!existsSync21(resolved)) {
|
|
9176
9753
|
console.log(chalk17.yellow(` Path not found: ${resolved}`));
|
|
9177
9754
|
continue;
|
|
9178
9755
|
}
|
|
@@ -9180,9 +9757,9 @@ async function initCommand() {
|
|
|
9180
9757
|
}
|
|
9181
9758
|
console.log(chalk17.green(` \u2713 Vault: ${vaultPath}
|
|
9182
9759
|
`));
|
|
9183
|
-
const configDir =
|
|
9184
|
-
|
|
9185
|
-
const dbPath =
|
|
9760
|
+
const configDir = join26(homedir17(), ".stellavault");
|
|
9761
|
+
mkdirSync20(configDir, { recursive: true });
|
|
9762
|
+
const dbPath = join26(configDir, "index.db");
|
|
9186
9763
|
const configData = {
|
|
9187
9764
|
vaultPath,
|
|
9188
9765
|
dbPath,
|
|
@@ -9191,7 +9768,7 @@ async function initCommand() {
|
|
|
9191
9768
|
search: { defaultLimit: 10, rrfK: 60 },
|
|
9192
9769
|
mcp: { mode: "stdio", port: 3333 }
|
|
9193
9770
|
};
|
|
9194
|
-
writeFileSync19(
|
|
9771
|
+
writeFileSync19(join26(homedir17(), ".stellavault.json"), JSON.stringify(configData, null, 2), "utf-8");
|
|
9195
9772
|
console.log(chalk17.dim(` Config saved: ~/.stellavault.json`));
|
|
9196
9773
|
console.log("");
|
|
9197
9774
|
console.log(chalk17.cyan(" Step 2/3") + " \u2014 Indexing your vault");
|
|
@@ -9215,8 +9792,8 @@ async function initCommand() {
|
|
|
9215
9792
|
spinner.succeed(chalk17.green(` Indexed ${result.indexed} docs, ${result.totalChunks} chunks (${(result.elapsedMs / 1e3).toFixed(1)}s)`));
|
|
9216
9793
|
if (result.indexed === 0) {
|
|
9217
9794
|
console.log(chalk17.yellow("\n Your vault is empty \u2014 creating 3 starter notes so you can explore.\n"));
|
|
9218
|
-
const rawDir =
|
|
9219
|
-
|
|
9795
|
+
const rawDir = join26(vaultPath, "raw");
|
|
9796
|
+
mkdirSync20(rawDir, { recursive: true });
|
|
9220
9797
|
const samples = [
|
|
9221
9798
|
{
|
|
9222
9799
|
file: "welcome-to-stellavault.md",
|
|
@@ -9289,7 +9866,7 @@ Andrej Karpathy's approach: every session auto-compiles into structured knowledg
|
|
|
9289
9866
|
}
|
|
9290
9867
|
];
|
|
9291
9868
|
for (const s of samples) {
|
|
9292
|
-
writeFileSync19(
|
|
9869
|
+
writeFileSync19(join26(rawDir, s.file), s.content, "utf-8");
|
|
9293
9870
|
}
|
|
9294
9871
|
const reSpinner = ora2({ text: " Indexing sample notes...", indent: 2 }).start();
|
|
9295
9872
|
const reResult = await indexVault(vaultPath, {
|
|
@@ -9950,8 +10527,8 @@ import chalk26 from "chalk";
|
|
|
9950
10527
|
// packages/core/dist/intelligence/draft-generator.js
|
|
9951
10528
|
init_wiki_compiler();
|
|
9952
10529
|
init_config();
|
|
9953
|
-
import { writeFileSync as writeFileSync20, mkdirSync as
|
|
9954
|
-
import { join as
|
|
10530
|
+
import { writeFileSync as writeFileSync20, mkdirSync as mkdirSync21, existsSync as existsSync22 } from "node:fs";
|
|
10531
|
+
import { join as join27, resolve as resolve16, basename as basename5, extname as extname7 } from "node:path";
|
|
9955
10532
|
function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
|
|
9956
10533
|
const { topic, format = "blog", maxSections = 8, blueprint } = options;
|
|
9957
10534
|
const rawDir = resolve16(vaultPath, folders.fleeting);
|
|
@@ -9959,7 +10536,7 @@ function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
|
|
|
9959
10536
|
const litDir = resolve16(vaultPath, folders.literature);
|
|
9960
10537
|
const allDocs = [];
|
|
9961
10538
|
for (const dir of [rawDir, wikiDir, litDir]) {
|
|
9962
|
-
if (
|
|
10539
|
+
if (existsSync22(dir)) {
|
|
9963
10540
|
allDocs.push(...scanRawDirectory(dir));
|
|
9964
10541
|
}
|
|
9965
10542
|
}
|
|
@@ -10027,12 +10604,12 @@ function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
|
|
|
10027
10604
|
}
|
|
10028
10605
|
const wordCount = body.split(/\s+/).filter(Boolean).length;
|
|
10029
10606
|
const draftsDir = resolve16(vaultPath, "_drafts");
|
|
10030
|
-
if (!
|
|
10031
|
-
|
|
10607
|
+
if (!existsSync22(draftsDir))
|
|
10608
|
+
mkdirSync21(draftsDir, { recursive: true });
|
|
10032
10609
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
10033
10610
|
const slug = (topic ?? "knowledge").replace(/[^a-zA-Z0-9가-힣\s]/g, "").replace(/\s+/g, "-").toLowerCase().slice(0, 40);
|
|
10034
10611
|
const filename = `${timestamp}-${slug}.md`;
|
|
10035
|
-
const filePath =
|
|
10612
|
+
const filePath = join27("_drafts", filename);
|
|
10036
10613
|
const fullPath = resolve16(vaultPath, filePath);
|
|
10037
10614
|
writeFileSync20(fullPath, body, "utf-8");
|
|
10038
10615
|
return {
|
|
@@ -10284,8 +10861,8 @@ ${aiContent.text}
|
|
|
10284
10861
|
|
|
10285
10862
|
// packages/cli/dist/commands/session-cmd.js
|
|
10286
10863
|
import chalk27 from "chalk";
|
|
10287
|
-
import { writeFileSync as writeFileSync21, mkdirSync as
|
|
10288
|
-
import { resolve as resolve17, join as
|
|
10864
|
+
import { writeFileSync as writeFileSync21, mkdirSync as mkdirSync22, existsSync as existsSync23, appendFileSync } from "node:fs";
|
|
10865
|
+
import { resolve as resolve17, join as join28 } from "node:path";
|
|
10289
10866
|
async function sessionSaveCommand(options) {
|
|
10290
10867
|
const config = loadConfig();
|
|
10291
10868
|
if (!config.vaultPath) {
|
|
@@ -10294,12 +10871,12 @@ async function sessionSaveCommand(options) {
|
|
|
10294
10871
|
}
|
|
10295
10872
|
const folders = config.folders;
|
|
10296
10873
|
const logDir = resolve17(config.vaultPath, folders.fleeting, "_daily-logs");
|
|
10297
|
-
if (!
|
|
10298
|
-
|
|
10874
|
+
if (!existsSync23(logDir))
|
|
10875
|
+
mkdirSync22(logDir, { recursive: true });
|
|
10299
10876
|
const now = /* @__PURE__ */ new Date();
|
|
10300
10877
|
const dateStr = now.toISOString().split("T")[0];
|
|
10301
10878
|
const timeStr = now.toTimeString().split(" ")[0];
|
|
10302
|
-
const logFile =
|
|
10879
|
+
const logFile = join28(logDir, `daily-log-${dateStr}.md`);
|
|
10303
10880
|
let summary = options.summary ?? "";
|
|
10304
10881
|
if (!summary && !process.stdin.isTTY) {
|
|
10305
10882
|
const chunks = [];
|
|
@@ -10338,7 +10915,7 @@ async function sessionSaveCommand(options) {
|
|
|
10338
10915
|
entry.push("### Action Items", options.actions, "");
|
|
10339
10916
|
}
|
|
10340
10917
|
entry.push("---", "");
|
|
10341
|
-
if (!
|
|
10918
|
+
if (!existsSync23(logFile)) {
|
|
10342
10919
|
const header = [
|
|
10343
10920
|
"---",
|
|
10344
10921
|
`title: "Daily Log \u2014 ${dateStr}"`,
|
|
@@ -10370,8 +10947,8 @@ async function sessionSaveCommand(options) {
|
|
|
10370
10947
|
|
|
10371
10948
|
// packages/cli/dist/commands/flush-cmd.js
|
|
10372
10949
|
import chalk28 from "chalk";
|
|
10373
|
-
import { readdirSync as readdirSync7, readFileSync as readFileSync17, existsSync as
|
|
10374
|
-
import { resolve as resolve18, join as
|
|
10950
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync17, existsSync as existsSync24 } from "node:fs";
|
|
10951
|
+
import { resolve as resolve18, join as join29 } from "node:path";
|
|
10375
10952
|
async function flushCommand() {
|
|
10376
10953
|
const config = loadConfig();
|
|
10377
10954
|
if (!config.vaultPath) {
|
|
@@ -10380,7 +10957,7 @@ async function flushCommand() {
|
|
|
10380
10957
|
}
|
|
10381
10958
|
const folders = config.folders;
|
|
10382
10959
|
const logDir = resolve18(config.vaultPath, folders.fleeting, "_daily-logs");
|
|
10383
|
-
if (!
|
|
10960
|
+
if (!existsSync24(logDir)) {
|
|
10384
10961
|
console.log(chalk28.yellow("No daily logs found. Use `stellavault session-save` or let Claude Code hooks capture sessions."));
|
|
10385
10962
|
return;
|
|
10386
10963
|
}
|
|
@@ -10393,7 +10970,7 @@ async function flushCommand() {
|
|
|
10393
10970
|
let totalSessions = 0;
|
|
10394
10971
|
const allContent = [];
|
|
10395
10972
|
for (const file of logFiles) {
|
|
10396
|
-
const content = readFileSync17(
|
|
10973
|
+
const content = readFileSync17(join29(logDir, file), "utf-8");
|
|
10397
10974
|
const sessions = content.split(/^## Session/m).slice(1);
|
|
10398
10975
|
totalSessions += sessions.length;
|
|
10399
10976
|
allContent.push(content);
|
|
@@ -10521,8 +11098,8 @@ async function lintCommand() {
|
|
|
10521
11098
|
|
|
10522
11099
|
// packages/cli/dist/commands/fleeting-cmd.js
|
|
10523
11100
|
import chalk31 from "chalk";
|
|
10524
|
-
import { writeFileSync as writeFileSync22, mkdirSync as
|
|
10525
|
-
import { join as
|
|
11101
|
+
import { writeFileSync as writeFileSync22, mkdirSync as mkdirSync23, existsSync as existsSync25 } from "node:fs";
|
|
11102
|
+
import { join as join30, resolve as resolve19 } from "node:path";
|
|
10526
11103
|
async function fleetingCommand(text, options) {
|
|
10527
11104
|
if (!text || text.trim().length < 2) {
|
|
10528
11105
|
console.error(chalk31.yellow('Usage: stellavault fleeting "your idea here" [--tags tag1,tag2]'));
|
|
@@ -10530,13 +11107,13 @@ async function fleetingCommand(text, options) {
|
|
|
10530
11107
|
}
|
|
10531
11108
|
const config = loadConfig();
|
|
10532
11109
|
const rawDir = resolve19(config.vaultPath, "raw");
|
|
10533
|
-
if (!
|
|
10534
|
-
|
|
11110
|
+
if (!existsSync25(rawDir))
|
|
11111
|
+
mkdirSync23(rawDir, { recursive: true });
|
|
10535
11112
|
const now = /* @__PURE__ */ new Date();
|
|
10536
11113
|
const timestamp = now.toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
10537
11114
|
const slug = text.slice(0, 40).replace(/[^a-zA-Z0-9가-힣\s]/g, "").replace(/\s+/g, "-").toLowerCase();
|
|
10538
11115
|
const filename = `${timestamp}-${slug}.md`;
|
|
10539
|
-
const filePath =
|
|
11116
|
+
const filePath = join30(rawDir, filename);
|
|
10540
11117
|
if (!resolve19(filePath).startsWith(resolve19(rawDir))) {
|
|
10541
11118
|
console.error(chalk31.red("Invalid file path"));
|
|
10542
11119
|
process.exit(1);
|
|
@@ -10563,15 +11140,15 @@ async function fleetingCommand(text, options) {
|
|
|
10563
11140
|
|
|
10564
11141
|
// packages/cli/dist/commands/ingest-cmd.js
|
|
10565
11142
|
import chalk32 from "chalk";
|
|
10566
|
-
import { readFileSync as readFileSync18, existsSync as
|
|
10567
|
-
import { extname as extname8, resolve as resolve20, join as
|
|
11143
|
+
import { readFileSync as readFileSync18, existsSync as existsSync26, readdirSync as readdirSync8, statSync as statSync5 } from "node:fs";
|
|
11144
|
+
import { extname as extname8, resolve as resolve20, join as join31 } from "node:path";
|
|
10568
11145
|
async function ingestCommand(input, options) {
|
|
10569
11146
|
if (!input) {
|
|
10570
11147
|
console.error(chalk32.yellow("Usage: stellavault ingest <url|file|text|folder/> [--tags t1,t2]"));
|
|
10571
11148
|
process.exit(1);
|
|
10572
11149
|
}
|
|
10573
|
-
if (
|
|
10574
|
-
const files = readdirSync8(input).filter((f) => /\.(md|txt|pdf|docx|pptx|xlsx|xls|csv|json|xml|html|htm|yaml|yml|rtf)$/i.test(f)).map((f) =>
|
|
11150
|
+
if (existsSync26(input) && statSync5(input).isDirectory()) {
|
|
11151
|
+
const files = readdirSync8(input).filter((f) => /\.(md|txt|pdf|docx|pptx|xlsx|xls|csv|json|xml|html|htm|yaml|yml|rtf)$/i.test(f)).map((f) => join31(input, f));
|
|
10575
11152
|
if (files.length === 0) {
|
|
10576
11153
|
console.error(chalk32.yellow(`No supported files found in ${input}`));
|
|
10577
11154
|
process.exit(1);
|
|
@@ -10616,13 +11193,10 @@ async function ingestSingleFile(input, options) {
|
|
|
10616
11193
|
if (/^https?:\/\//.test(input)) {
|
|
10617
11194
|
const isYouTube = /youtube\.com\/watch|youtu\.be\//.test(input);
|
|
10618
11195
|
try {
|
|
10619
|
-
|
|
10620
|
-
const host = url.hostname;
|
|
10621
|
-
if (/^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|0\.|localhost|::1)/i.test(host)) {
|
|
10622
|
-
console.error(chalk32.yellow("Private/local URLs are not allowed for security."));
|
|
10623
|
-
process.exit(1);
|
|
10624
|
-
}
|
|
11196
|
+
await assertPublicUrl(input);
|
|
10625
11197
|
} catch {
|
|
11198
|
+
console.error(chalk32.yellow("Private/local or non-public URLs are not allowed for security."));
|
|
11199
|
+
process.exit(1);
|
|
10626
11200
|
}
|
|
10627
11201
|
if (isYouTube) {
|
|
10628
11202
|
try {
|
|
@@ -10668,7 +11242,7 @@ async function ingestSingleFile(input, options) {
|
|
|
10668
11242
|
source: input
|
|
10669
11243
|
};
|
|
10670
11244
|
}
|
|
10671
|
-
} else if (
|
|
11245
|
+
} else if (existsSync26(input)) {
|
|
10672
11246
|
const ext = extname8(input).toLowerCase();
|
|
10673
11247
|
const binaryExts = /* @__PURE__ */ new Set([".pdf", ".docx", ".pptx", ".xlsx", ".xls"]);
|
|
10674
11248
|
const structuredExts = /* @__PURE__ */ new Set([".json", ".csv", ".xml", ".html", ".htm", ".yaml", ".yml", ".rtf"]);
|
|
@@ -10826,9 +11400,9 @@ async function autopilotCommand(options) {
|
|
|
10826
11400
|
|
|
10827
11401
|
// packages/cli/dist/commands/doctor-cmd.js
|
|
10828
11402
|
import chalk34 from "chalk";
|
|
10829
|
-
import { existsSync as
|
|
10830
|
-
import { join as
|
|
10831
|
-
import { homedir as
|
|
11403
|
+
import { existsSync as existsSync27, statSync as statSync6 } from "node:fs";
|
|
11404
|
+
import { join as join32 } from "node:path";
|
|
11405
|
+
import { homedir as homedir18 } from "node:os";
|
|
10832
11406
|
async function doctorCommand() {
|
|
10833
11407
|
console.log("");
|
|
10834
11408
|
console.log(chalk34.bold(" \u{1FA7A} Stellavault Doctor\n"));
|
|
@@ -10841,10 +11415,10 @@ async function doctorCommand() {
|
|
|
10841
11415
|
fix: nodeVersion2 < 20 ? "Download Node.js 20+: https://nodejs.org" : void 0
|
|
10842
11416
|
});
|
|
10843
11417
|
const configPaths = [
|
|
10844
|
-
|
|
10845
|
-
|
|
11418
|
+
join32(process.cwd(), ".stellavault.json"),
|
|
11419
|
+
join32(homedir18(), ".stellavault.json")
|
|
10846
11420
|
];
|
|
10847
|
-
const configPath = configPaths.find((p) =>
|
|
11421
|
+
const configPath = configPaths.find((p) => existsSync27(p));
|
|
10848
11422
|
checks.push({
|
|
10849
11423
|
name: "Config file",
|
|
10850
11424
|
pass: !!configPath,
|
|
@@ -10869,7 +11443,7 @@ async function doctorCommand() {
|
|
|
10869
11443
|
}
|
|
10870
11444
|
}
|
|
10871
11445
|
if (vaultPath) {
|
|
10872
|
-
const vaultExists =
|
|
11446
|
+
const vaultExists = existsSync27(vaultPath);
|
|
10873
11447
|
checks.push({
|
|
10874
11448
|
name: "Vault path",
|
|
10875
11449
|
pass: vaultExists,
|
|
@@ -10898,7 +11472,7 @@ async function doctorCommand() {
|
|
|
10898
11472
|
}
|
|
10899
11473
|
}
|
|
10900
11474
|
if (dbPath) {
|
|
10901
|
-
const dbExists =
|
|
11475
|
+
const dbExists = existsSync27(dbPath);
|
|
10902
11476
|
checks.push({
|
|
10903
11477
|
name: "Database",
|
|
10904
11478
|
pass: dbExists,
|
|
@@ -10913,20 +11487,20 @@ async function doctorCommand() {
|
|
|
10913
11487
|
fix: "Re-run: stellavault init"
|
|
10914
11488
|
});
|
|
10915
11489
|
}
|
|
10916
|
-
const cacheDir =
|
|
10917
|
-
const hfCache =
|
|
10918
|
-
const xenovaCache =
|
|
10919
|
-
const modelCached =
|
|
11490
|
+
const cacheDir = join32(homedir18(), ".cache", "onnxruntime");
|
|
11491
|
+
const hfCache = join32(homedir18(), ".cache", "huggingface");
|
|
11492
|
+
const xenovaCache = join32(homedir18(), ".cache", "xenova");
|
|
11493
|
+
const modelCached = existsSync27(cacheDir) || existsSync27(hfCache) || existsSync27(xenovaCache) || existsSync27(join32(homedir18(), ".cache", "transformers"));
|
|
10920
11494
|
checks.push({
|
|
10921
11495
|
name: "Embedding model cached",
|
|
10922
11496
|
pass: modelCached,
|
|
10923
11497
|
detail: modelCached ? "local model files found" : "not downloaded yet",
|
|
10924
11498
|
fix: !modelCached ? "Will download automatically on first index (~30MB)" : void 0
|
|
10925
11499
|
});
|
|
10926
|
-
const testDir =
|
|
11500
|
+
const testDir = join32(homedir18(), ".stellavault");
|
|
10927
11501
|
try {
|
|
10928
|
-
const { mkdirSync:
|
|
10929
|
-
|
|
11502
|
+
const { mkdirSync: mkdirSync24 } = await import("node:fs");
|
|
11503
|
+
mkdirSync24(testDir, { recursive: true });
|
|
10930
11504
|
checks.push({ name: "Write permission (~/.stellavault)", pass: true, detail: "OK" });
|
|
10931
11505
|
} catch {
|
|
10932
11506
|
checks.push({
|
|
@@ -10976,7 +11550,7 @@ if (nodeVersion < 20) {
|
|
|
10976
11550
|
process.exit(1);
|
|
10977
11551
|
}
|
|
10978
11552
|
var program = new Command();
|
|
10979
|
-
var SV_VERSION = true ? "0.8.
|
|
11553
|
+
var SV_VERSION = true ? "0.8.5" : "0.0.0-dev";
|
|
10980
11554
|
program.name("stellavault").description("Stellavault \u2014 Self-compiling knowledge base for your Obsidian vault").version(SV_VERSION).option("--json", "Output in JSON format (for scripting)").option("--quiet", "Suppress non-essential output");
|
|
10981
11555
|
program.command("init").description("Interactive setup wizard \u2014 get started in 3 minutes").action(initCommand);
|
|
10982
11556
|
program.command("doctor").description("Diagnose setup issues (config, vault, DB, model, Node version)").action(doctorCommand);
|