@velvetmonkey/flywheel-memory 2.5.12 → 2.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +530 -255
- package/dist/integrity-worker.js +49 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -344,7 +344,7 @@ function resolveWorkerPath() {
|
|
|
344
344
|
async function initEmbeddings() {
|
|
345
345
|
if (workerReady && worker) return;
|
|
346
346
|
if (workerInitPromise) return workerInitPromise;
|
|
347
|
-
workerInitPromise = new Promise((
|
|
347
|
+
workerInitPromise = new Promise((resolve4, reject) => {
|
|
348
348
|
try {
|
|
349
349
|
const workerPath = resolveWorkerPath();
|
|
350
350
|
console.error(`[Semantic] Spawning embedding worker: ${workerPath}`);
|
|
@@ -359,7 +359,7 @@ async function initEmbeddings() {
|
|
|
359
359
|
console.error(`[Semantic] Probed model ${activeModelConfig.id}: ${msg.dims} dims`);
|
|
360
360
|
}
|
|
361
361
|
console.error(`[Semantic] Worker ready (model: ${activeModelConfig.id}, dims: ${msg.dims})`);
|
|
362
|
-
|
|
362
|
+
resolve4();
|
|
363
363
|
break;
|
|
364
364
|
case "result": {
|
|
365
365
|
const pending = pendingEmbeds.get(msg.id);
|
|
@@ -435,8 +435,8 @@ async function embedText(text) {
|
|
|
435
435
|
throw new Error("Embedding worker not available");
|
|
436
436
|
}
|
|
437
437
|
const id = ++embedRequestId;
|
|
438
|
-
return new Promise((
|
|
439
|
-
pendingEmbeds.set(id, { resolve:
|
|
438
|
+
return new Promise((resolve4, reject) => {
|
|
439
|
+
pendingEmbeds.set(id, { resolve: resolve4, reject });
|
|
440
440
|
worker.postMessage({ type: "embed", id, text });
|
|
441
441
|
});
|
|
442
442
|
}
|
|
@@ -2696,7 +2696,7 @@ function isLockContentionError(error) {
|
|
|
2696
2696
|
return false;
|
|
2697
2697
|
}
|
|
2698
2698
|
function sleep(ms) {
|
|
2699
|
-
return new Promise((
|
|
2699
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
2700
2700
|
}
|
|
2701
2701
|
function calculateDelay(attempt, config2) {
|
|
2702
2702
|
let delay = config2.baseDelayMs * Math.pow(2, attempt);
|
|
@@ -5185,16 +5185,16 @@ var init_tool_embeddings_generated = __esm({
|
|
|
5185
5185
|
|
|
5186
5186
|
// src/core/write/path-security.ts
|
|
5187
5187
|
import fs20 from "fs/promises";
|
|
5188
|
-
import
|
|
5188
|
+
import path23 from "path";
|
|
5189
5189
|
function isSensitivePath(filePath) {
|
|
5190
5190
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
5191
5191
|
return SENSITIVE_PATH_PATTERNS.some((pattern) => pattern.test(normalizedPath));
|
|
5192
5192
|
}
|
|
5193
5193
|
function isWithinDirectory(child, parent, allowEqual = false) {
|
|
5194
|
-
const rel =
|
|
5194
|
+
const rel = path23.relative(path23.resolve(parent), path23.resolve(child));
|
|
5195
5195
|
if (rel === "") return allowEqual;
|
|
5196
|
-
const firstSeg = rel.split(
|
|
5197
|
-
return firstSeg !== ".." && !
|
|
5196
|
+
const firstSeg = rel.split(path23.sep)[0];
|
|
5197
|
+
return firstSeg !== ".." && !path23.isAbsolute(rel);
|
|
5198
5198
|
}
|
|
5199
5199
|
function validatePath(vaultPath2, notePath) {
|
|
5200
5200
|
if (notePath.startsWith("/")) {
|
|
@@ -5206,11 +5206,11 @@ function validatePath(vaultPath2, notePath) {
|
|
|
5206
5206
|
if (notePath.startsWith("\\")) {
|
|
5207
5207
|
return false;
|
|
5208
5208
|
}
|
|
5209
|
-
return isWithinDirectory(
|
|
5209
|
+
return isWithinDirectory(path23.resolve(vaultPath2, notePath), vaultPath2);
|
|
5210
5210
|
}
|
|
5211
5211
|
function sanitizeNotePath(notePath) {
|
|
5212
|
-
const dir =
|
|
5213
|
-
let filename =
|
|
5212
|
+
const dir = path23.dirname(notePath);
|
|
5213
|
+
let filename = path23.basename(notePath);
|
|
5214
5214
|
const ext = filename.endsWith(".md") ? ".md" : "";
|
|
5215
5215
|
let stem2 = ext ? filename.slice(0, -ext.length) : filename;
|
|
5216
5216
|
stem2 = stem2.replace(/\s+/g, "-");
|
|
@@ -5219,7 +5219,7 @@ function sanitizeNotePath(notePath) {
|
|
|
5219
5219
|
stem2 = stem2.replace(/-{2,}/g, "-");
|
|
5220
5220
|
stem2 = stem2.replace(/^-+|-+$/g, "");
|
|
5221
5221
|
filename = stem2 + (ext || ".md");
|
|
5222
|
-
return dir === "." ? filename :
|
|
5222
|
+
return dir === "." ? filename : path23.join(dir, filename).replace(/\\/g, "/");
|
|
5223
5223
|
}
|
|
5224
5224
|
async function validatePathSecure(vaultPath2, notePath) {
|
|
5225
5225
|
if (notePath.startsWith("/")) {
|
|
@@ -5240,14 +5240,14 @@ async function validatePathSecure(vaultPath2, notePath) {
|
|
|
5240
5240
|
reason: "Absolute paths not allowed"
|
|
5241
5241
|
};
|
|
5242
5242
|
}
|
|
5243
|
-
const firstSeg =
|
|
5243
|
+
const firstSeg = path23.normalize(notePath).split(path23.sep).filter(Boolean)[0];
|
|
5244
5244
|
if (firstSeg === "..") {
|
|
5245
5245
|
return {
|
|
5246
5246
|
valid: false,
|
|
5247
5247
|
reason: "Path traversal not allowed"
|
|
5248
5248
|
};
|
|
5249
5249
|
}
|
|
5250
|
-
if (!isWithinDirectory(
|
|
5250
|
+
if (!isWithinDirectory(path23.resolve(vaultPath2, notePath), vaultPath2)) {
|
|
5251
5251
|
return {
|
|
5252
5252
|
valid: false,
|
|
5253
5253
|
reason: "Path traversal not allowed"
|
|
@@ -5260,7 +5260,7 @@ async function validatePathSecure(vaultPath2, notePath) {
|
|
|
5260
5260
|
};
|
|
5261
5261
|
}
|
|
5262
5262
|
try {
|
|
5263
|
-
const fullPath =
|
|
5263
|
+
const fullPath = path23.join(vaultPath2, notePath);
|
|
5264
5264
|
try {
|
|
5265
5265
|
await fs20.access(fullPath);
|
|
5266
5266
|
const realPath = await fs20.realpath(fullPath);
|
|
@@ -5271,7 +5271,7 @@ async function validatePathSecure(vaultPath2, notePath) {
|
|
|
5271
5271
|
reason: "Symlink target is outside vault"
|
|
5272
5272
|
};
|
|
5273
5273
|
}
|
|
5274
|
-
const relativePath =
|
|
5274
|
+
const relativePath = path23.relative(realVaultPath, realPath);
|
|
5275
5275
|
if (isSensitivePath(relativePath)) {
|
|
5276
5276
|
return {
|
|
5277
5277
|
valid: false,
|
|
@@ -5279,7 +5279,7 @@ async function validatePathSecure(vaultPath2, notePath) {
|
|
|
5279
5279
|
};
|
|
5280
5280
|
}
|
|
5281
5281
|
} catch {
|
|
5282
|
-
const parentDir =
|
|
5282
|
+
const parentDir = path23.dirname(fullPath);
|
|
5283
5283
|
try {
|
|
5284
5284
|
await fs20.access(parentDir);
|
|
5285
5285
|
const realParentPath = await fs20.realpath(parentDir);
|
|
@@ -6058,7 +6058,7 @@ var init_content_mutation = __esm({
|
|
|
6058
6058
|
|
|
6059
6059
|
// src/core/write/file-io.ts
|
|
6060
6060
|
import fs21 from "fs/promises";
|
|
6061
|
-
import
|
|
6061
|
+
import path24 from "path";
|
|
6062
6062
|
import matter5 from "gray-matter";
|
|
6063
6063
|
import { createHash as createHash2 } from "node:crypto";
|
|
6064
6064
|
function computeContentHash(rawContent) {
|
|
@@ -6069,7 +6069,7 @@ async function readVaultFile(vaultPath2, notePath) {
|
|
|
6069
6069
|
if (!validation.valid) {
|
|
6070
6070
|
throw new Error(`Invalid path: ${validation.reason}`);
|
|
6071
6071
|
}
|
|
6072
|
-
const fullPath =
|
|
6072
|
+
const fullPath = path24.join(vaultPath2, notePath);
|
|
6073
6073
|
const [rawContent, stat4] = await Promise.all([
|
|
6074
6074
|
fs21.readFile(fullPath, "utf-8"),
|
|
6075
6075
|
fs21.stat(fullPath)
|
|
@@ -6124,7 +6124,7 @@ async function writeVaultFile(vaultPath2, notePath, content, frontmatter, lineEn
|
|
|
6124
6124
|
if (!validation.valid) {
|
|
6125
6125
|
throw new Error(`Invalid path: ${validation.reason}`);
|
|
6126
6126
|
}
|
|
6127
|
-
const fullPath =
|
|
6127
|
+
const fullPath = path24.join(vaultPath2, notePath);
|
|
6128
6128
|
if (expectedHash) {
|
|
6129
6129
|
const currentRaw = await fs21.readFile(fullPath, "utf-8");
|
|
6130
6130
|
const currentHash = computeContentHash(currentRaw);
|
|
@@ -6192,8 +6192,8 @@ function createContext(variables = {}) {
|
|
|
6192
6192
|
steps: {}
|
|
6193
6193
|
};
|
|
6194
6194
|
}
|
|
6195
|
-
function resolvePath(obj,
|
|
6196
|
-
const parts =
|
|
6195
|
+
function resolvePath(obj, path41) {
|
|
6196
|
+
const parts = path41.split(".");
|
|
6197
6197
|
let current = obj;
|
|
6198
6198
|
for (const part of parts) {
|
|
6199
6199
|
if (current === void 0 || current === null) {
|
|
@@ -6651,7 +6651,7 @@ __export(conditions_exports, {
|
|
|
6651
6651
|
shouldStepExecute: () => shouldStepExecute
|
|
6652
6652
|
});
|
|
6653
6653
|
import fs29 from "fs/promises";
|
|
6654
|
-
import
|
|
6654
|
+
import path32 from "path";
|
|
6655
6655
|
async function evaluateCondition(condition, vaultPath2, context) {
|
|
6656
6656
|
const interpolatedPath = condition.path ? interpolate(condition.path, context) : void 0;
|
|
6657
6657
|
const interpolatedSection = condition.section ? interpolate(condition.section, context) : void 0;
|
|
@@ -6704,7 +6704,7 @@ async function evaluateCondition(condition, vaultPath2, context) {
|
|
|
6704
6704
|
}
|
|
6705
6705
|
}
|
|
6706
6706
|
async function evaluateFileExists(vaultPath2, notePath, expectExists) {
|
|
6707
|
-
const fullPath =
|
|
6707
|
+
const fullPath = path32.join(vaultPath2, notePath);
|
|
6708
6708
|
try {
|
|
6709
6709
|
await fs29.access(fullPath);
|
|
6710
6710
|
return {
|
|
@@ -6719,7 +6719,7 @@ async function evaluateFileExists(vaultPath2, notePath, expectExists) {
|
|
|
6719
6719
|
}
|
|
6720
6720
|
}
|
|
6721
6721
|
async function evaluateSectionExists(vaultPath2, notePath, sectionName, expectExists) {
|
|
6722
|
-
const fullPath =
|
|
6722
|
+
const fullPath = path32.join(vaultPath2, notePath);
|
|
6723
6723
|
try {
|
|
6724
6724
|
await fs29.access(fullPath);
|
|
6725
6725
|
} catch {
|
|
@@ -6750,7 +6750,7 @@ async function evaluateSectionExists(vaultPath2, notePath, sectionName, expectEx
|
|
|
6750
6750
|
}
|
|
6751
6751
|
}
|
|
6752
6752
|
async function evaluateFrontmatterExists(vaultPath2, notePath, fieldName, expectExists) {
|
|
6753
|
-
const fullPath =
|
|
6753
|
+
const fullPath = path32.join(vaultPath2, notePath);
|
|
6754
6754
|
try {
|
|
6755
6755
|
await fs29.access(fullPath);
|
|
6756
6756
|
} catch {
|
|
@@ -6781,7 +6781,7 @@ async function evaluateFrontmatterExists(vaultPath2, notePath, fieldName, expect
|
|
|
6781
6781
|
}
|
|
6782
6782
|
}
|
|
6783
6783
|
async function evaluateFrontmatterEquals(vaultPath2, notePath, fieldName, expectedValue) {
|
|
6784
|
-
const fullPath =
|
|
6784
|
+
const fullPath = path32.join(vaultPath2, notePath);
|
|
6785
6785
|
try {
|
|
6786
6786
|
await fs29.access(fullPath);
|
|
6787
6787
|
} catch {
|
|
@@ -6925,10 +6925,10 @@ var init_taskHelpers = __esm({
|
|
|
6925
6925
|
});
|
|
6926
6926
|
|
|
6927
6927
|
// src/index.ts
|
|
6928
|
-
import * as
|
|
6929
|
-
import { readFileSync as readFileSync6, realpathSync, existsSync as
|
|
6930
|
-
import { fileURLToPath as
|
|
6931
|
-
import { dirname as
|
|
6928
|
+
import * as path40 from "path";
|
|
6929
|
+
import { readFileSync as readFileSync6, realpathSync, existsSync as existsSync4 } from "fs";
|
|
6930
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6931
|
+
import { dirname as dirname8, join as join21 } from "path";
|
|
6932
6932
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6933
6933
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6934
6934
|
import { performance as performance2 } from "node:perf_hooks";
|
|
@@ -7165,8 +7165,8 @@ function updateIndexProgress(parsed, total) {
|
|
|
7165
7165
|
function normalizeTarget(target) {
|
|
7166
7166
|
return target.toLowerCase().replace(/\.md$/, "");
|
|
7167
7167
|
}
|
|
7168
|
-
function normalizeNotePath(
|
|
7169
|
-
return
|
|
7168
|
+
function normalizeNotePath(path41) {
|
|
7169
|
+
return path41.toLowerCase().replace(/\.md$/, "");
|
|
7170
7170
|
}
|
|
7171
7171
|
async function buildVaultIndex(vaultPath2, options = {}) {
|
|
7172
7172
|
const { timeoutMs = DEFAULT_TIMEOUT_MS, onProgress } = options;
|
|
@@ -7214,7 +7214,7 @@ async function buildVaultIndexInternal(vaultPath2, startTime, onProgress) {
|
|
|
7214
7214
|
console.error(`Parsed ${parsedCount}/${files.length} files (${elapsed}s)`);
|
|
7215
7215
|
onProgress?.(parsedCount, files.length);
|
|
7216
7216
|
}
|
|
7217
|
-
await new Promise((
|
|
7217
|
+
await new Promise((resolve4) => setImmediate(resolve4));
|
|
7218
7218
|
}
|
|
7219
7219
|
if (parseErrors.length > 0) {
|
|
7220
7220
|
const msg = `Failed to parse ${parseErrors.length} file(s):`;
|
|
@@ -7340,7 +7340,7 @@ function findSimilarEntity(index, target) {
|
|
|
7340
7340
|
}
|
|
7341
7341
|
const maxDist = normalizedLen <= 10 ? 1 : 2;
|
|
7342
7342
|
let bestMatch;
|
|
7343
|
-
for (const [entity,
|
|
7343
|
+
for (const [entity, path41] of index.entities) {
|
|
7344
7344
|
const lenDiff = Math.abs(entity.length - normalizedLen);
|
|
7345
7345
|
if (lenDiff > maxDist) {
|
|
7346
7346
|
continue;
|
|
@@ -7348,7 +7348,7 @@ function findSimilarEntity(index, target) {
|
|
|
7348
7348
|
const dist = levenshteinDistance(normalized, entity);
|
|
7349
7349
|
if (dist > 0 && dist <= maxDist) {
|
|
7350
7350
|
if (!bestMatch || dist < bestMatch.distance) {
|
|
7351
|
-
bestMatch = { path:
|
|
7351
|
+
bestMatch = { path: path41, entity, distance: dist };
|
|
7352
7352
|
if (dist === 1) {
|
|
7353
7353
|
return bestMatch;
|
|
7354
7354
|
}
|
|
@@ -7907,30 +7907,30 @@ var EventQueue = class {
|
|
|
7907
7907
|
* Add a new event to the queue
|
|
7908
7908
|
*/
|
|
7909
7909
|
push(type, rawPath) {
|
|
7910
|
-
const
|
|
7910
|
+
const path41 = normalizePath(rawPath);
|
|
7911
7911
|
const now = Date.now();
|
|
7912
7912
|
const event = {
|
|
7913
7913
|
type,
|
|
7914
|
-
path:
|
|
7914
|
+
path: path41,
|
|
7915
7915
|
timestamp: now
|
|
7916
7916
|
};
|
|
7917
|
-
let pending = this.pending.get(
|
|
7917
|
+
let pending = this.pending.get(path41);
|
|
7918
7918
|
if (!pending) {
|
|
7919
7919
|
pending = {
|
|
7920
7920
|
events: [],
|
|
7921
7921
|
timer: null,
|
|
7922
7922
|
lastEvent: now
|
|
7923
7923
|
};
|
|
7924
|
-
this.pending.set(
|
|
7924
|
+
this.pending.set(path41, pending);
|
|
7925
7925
|
}
|
|
7926
7926
|
pending.events.push(event);
|
|
7927
7927
|
pending.lastEvent = now;
|
|
7928
|
-
console.error(`[flywheel] QUEUE: pushed ${type} for ${
|
|
7928
|
+
console.error(`[flywheel] QUEUE: pushed ${type} for ${path41}, pending=${this.pending.size}`);
|
|
7929
7929
|
if (pending.timer) {
|
|
7930
7930
|
clearTimeout(pending.timer);
|
|
7931
7931
|
}
|
|
7932
7932
|
pending.timer = setTimeout(() => {
|
|
7933
|
-
this.flushPath(
|
|
7933
|
+
this.flushPath(path41);
|
|
7934
7934
|
}, this.config.debounceMs);
|
|
7935
7935
|
if (this.pending.size >= this.config.batchSize) {
|
|
7936
7936
|
this.flush();
|
|
@@ -7951,10 +7951,10 @@ var EventQueue = class {
|
|
|
7951
7951
|
/**
|
|
7952
7952
|
* Flush a single path's events
|
|
7953
7953
|
*/
|
|
7954
|
-
flushPath(
|
|
7955
|
-
const pending = this.pending.get(
|
|
7954
|
+
flushPath(path41) {
|
|
7955
|
+
const pending = this.pending.get(path41);
|
|
7956
7956
|
if (!pending || pending.events.length === 0) return;
|
|
7957
|
-
console.error(`[flywheel] QUEUE: flushing ${
|
|
7957
|
+
console.error(`[flywheel] QUEUE: flushing ${path41}, events=${pending.events.length}`);
|
|
7958
7958
|
if (pending.timer) {
|
|
7959
7959
|
clearTimeout(pending.timer);
|
|
7960
7960
|
pending.timer = null;
|
|
@@ -7963,7 +7963,7 @@ var EventQueue = class {
|
|
|
7963
7963
|
if (coalescedType) {
|
|
7964
7964
|
const coalesced = {
|
|
7965
7965
|
type: coalescedType,
|
|
7966
|
-
path:
|
|
7966
|
+
path: path41,
|
|
7967
7967
|
originalEvents: [...pending.events]
|
|
7968
7968
|
};
|
|
7969
7969
|
this.onBatch({
|
|
@@ -7972,7 +7972,7 @@ var EventQueue = class {
|
|
|
7972
7972
|
timestamp: Date.now()
|
|
7973
7973
|
});
|
|
7974
7974
|
}
|
|
7975
|
-
this.pending.delete(
|
|
7975
|
+
this.pending.delete(path41);
|
|
7976
7976
|
}
|
|
7977
7977
|
/**
|
|
7978
7978
|
* Flush all pending events
|
|
@@ -7984,7 +7984,7 @@ var EventQueue = class {
|
|
|
7984
7984
|
}
|
|
7985
7985
|
if (this.pending.size === 0) return;
|
|
7986
7986
|
const events = [];
|
|
7987
|
-
for (const [
|
|
7987
|
+
for (const [path41, pending] of this.pending) {
|
|
7988
7988
|
if (pending.timer) {
|
|
7989
7989
|
clearTimeout(pending.timer);
|
|
7990
7990
|
}
|
|
@@ -7992,7 +7992,7 @@ var EventQueue = class {
|
|
|
7992
7992
|
if (coalescedType) {
|
|
7993
7993
|
events.push({
|
|
7994
7994
|
type: coalescedType,
|
|
7995
|
-
path:
|
|
7995
|
+
path: path41,
|
|
7996
7996
|
originalEvents: [...pending.events]
|
|
7997
7997
|
});
|
|
7998
7998
|
}
|
|
@@ -8292,7 +8292,7 @@ async function processBatch(index, vaultPath2, batch, options = {}) {
|
|
|
8292
8292
|
}
|
|
8293
8293
|
onProgress?.(processed, total);
|
|
8294
8294
|
if (processed % YIELD_INTERVAL === 0 && processed < total) {
|
|
8295
|
-
await new Promise((
|
|
8295
|
+
await new Promise((resolve4) => setImmediate(resolve4));
|
|
8296
8296
|
}
|
|
8297
8297
|
}
|
|
8298
8298
|
const durationMs = Date.now() - startTime;
|
|
@@ -8384,31 +8384,31 @@ function createVaultWatcher(options) {
|
|
|
8384
8384
|
usePolling: config2.usePolling,
|
|
8385
8385
|
interval: config2.usePolling ? config2.pollInterval : void 0
|
|
8386
8386
|
});
|
|
8387
|
-
watcher.on("add", (
|
|
8388
|
-
console.error(`[flywheel] RAW EVENT: add ${
|
|
8389
|
-
if (shouldWatch(
|
|
8390
|
-
console.error(`[flywheel] ACCEPTED: add ${
|
|
8391
|
-
eventQueue.push("add",
|
|
8387
|
+
watcher.on("add", (path41) => {
|
|
8388
|
+
console.error(`[flywheel] RAW EVENT: add ${path41}`);
|
|
8389
|
+
if (shouldWatch(path41, vaultPath2)) {
|
|
8390
|
+
console.error(`[flywheel] ACCEPTED: add ${path41}`);
|
|
8391
|
+
eventQueue.push("add", path41);
|
|
8392
8392
|
} else {
|
|
8393
|
-
console.error(`[flywheel] FILTERED: add ${
|
|
8393
|
+
console.error(`[flywheel] FILTERED: add ${path41}`);
|
|
8394
8394
|
}
|
|
8395
8395
|
});
|
|
8396
|
-
watcher.on("change", (
|
|
8397
|
-
console.error(`[flywheel] RAW EVENT: change ${
|
|
8398
|
-
if (shouldWatch(
|
|
8399
|
-
console.error(`[flywheel] ACCEPTED: change ${
|
|
8400
|
-
eventQueue.push("change",
|
|
8396
|
+
watcher.on("change", (path41) => {
|
|
8397
|
+
console.error(`[flywheel] RAW EVENT: change ${path41}`);
|
|
8398
|
+
if (shouldWatch(path41, vaultPath2)) {
|
|
8399
|
+
console.error(`[flywheel] ACCEPTED: change ${path41}`);
|
|
8400
|
+
eventQueue.push("change", path41);
|
|
8401
8401
|
} else {
|
|
8402
|
-
console.error(`[flywheel] FILTERED: change ${
|
|
8402
|
+
console.error(`[flywheel] FILTERED: change ${path41}`);
|
|
8403
8403
|
}
|
|
8404
8404
|
});
|
|
8405
|
-
watcher.on("unlink", (
|
|
8406
|
-
console.error(`[flywheel] RAW EVENT: unlink ${
|
|
8407
|
-
if (shouldWatch(
|
|
8408
|
-
console.error(`[flywheel] ACCEPTED: unlink ${
|
|
8409
|
-
eventQueue.push("unlink",
|
|
8405
|
+
watcher.on("unlink", (path41) => {
|
|
8406
|
+
console.error(`[flywheel] RAW EVENT: unlink ${path41}`);
|
|
8407
|
+
if (shouldWatch(path41, vaultPath2)) {
|
|
8408
|
+
console.error(`[flywheel] ACCEPTED: unlink ${path41}`);
|
|
8409
|
+
eventQueue.push("unlink", path41);
|
|
8410
8410
|
} else {
|
|
8411
|
-
console.error(`[flywheel] FILTERED: unlink ${
|
|
8411
|
+
console.error(`[flywheel] FILTERED: unlink ${path41}`);
|
|
8412
8412
|
}
|
|
8413
8413
|
});
|
|
8414
8414
|
watcher.on("ready", () => {
|
|
@@ -8448,8 +8448,6 @@ import {
|
|
|
8448
8448
|
getProtectedZones,
|
|
8449
8449
|
rangeOverlapsProtectedZone,
|
|
8450
8450
|
detectImplicitEntities as detectImplicitEntities2,
|
|
8451
|
-
checkDbIntegrity,
|
|
8452
|
-
safeBackupAsync,
|
|
8453
8451
|
recordEntityMention as recordEntityMention2
|
|
8454
8452
|
} from "@velvetmonkey/vault-core";
|
|
8455
8453
|
init_serverLog();
|
|
@@ -9740,6 +9738,17 @@ var PipelineRunner = class {
|
|
|
9740
9738
|
suggestionResults = [];
|
|
9741
9739
|
async run() {
|
|
9742
9740
|
const { p, tracker } = this;
|
|
9741
|
+
if (p.ctx.integrityState === "failed") {
|
|
9742
|
+
serverLog("watcher", `Skipping batch for ${p.ctx.name}: StateDb integrity failed`, "warn");
|
|
9743
|
+
this.activity.busy = false;
|
|
9744
|
+
this.activity.current_step = null;
|
|
9745
|
+
this.activity.last_completed_at = Date.now();
|
|
9746
|
+
this.activity.last_completed_trigger = "watcher";
|
|
9747
|
+
this.activity.last_completed_duration_ms = 0;
|
|
9748
|
+
this.activity.last_completed_files = p.events.length;
|
|
9749
|
+
this.activity.last_completed_steps = [];
|
|
9750
|
+
return;
|
|
9751
|
+
}
|
|
9743
9752
|
this.activity.busy = true;
|
|
9744
9753
|
this.activity.trigger = "watcher";
|
|
9745
9754
|
this.activity.started_at = this.batchStart;
|
|
@@ -10625,21 +10634,15 @@ var PipelineRunner = class {
|
|
|
10625
10634
|
async integrityCheck() {
|
|
10626
10635
|
const { p } = this;
|
|
10627
10636
|
if (!p.sd) return { skipped: true, reason: "no statedb" };
|
|
10628
|
-
const
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
}
|
|
10634
|
-
const result = checkDbIntegrity(p.sd.db);
|
|
10635
|
-
p.sd.setMetadataValue.run("last_integrity_check", String(Date.now()));
|
|
10636
|
-
if (result.ok) {
|
|
10637
|
-
await safeBackupAsync(p.sd.db, p.sd.dbPath);
|
|
10638
|
-
return { integrity: "ok", backed_up: true };
|
|
10639
|
-
} else {
|
|
10637
|
+
const result = await p.runIntegrityCheck(p.ctx, "watcher");
|
|
10638
|
+
if (result.status === "healthy") {
|
|
10639
|
+
return { integrity: "ok", backed_up: result.backupCreated };
|
|
10640
|
+
}
|
|
10641
|
+
if (result.status === "failed") {
|
|
10640
10642
|
serverLog("watcher", `Integrity check FAILED: ${result.detail}`, "error");
|
|
10641
10643
|
return { integrity: "failed", detail: result.detail };
|
|
10642
10644
|
}
|
|
10645
|
+
return { skipped: true, reason: result.detail ?? "integrity runner unavailable" };
|
|
10643
10646
|
}
|
|
10644
10647
|
// ── Maintenance: periodic incremental vacuum ─────────────────────
|
|
10645
10648
|
async maintenance() {
|
|
@@ -10999,8 +11002,77 @@ function getToolSelectionReport(stateDb2, daysBack = 7) {
|
|
|
10999
11002
|
};
|
|
11000
11003
|
}
|
|
11001
11004
|
|
|
11005
|
+
// src/core/read/integrity.ts
|
|
11006
|
+
import * as path16 from "node:path";
|
|
11007
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
11008
|
+
import { Worker as Worker2 } from "node:worker_threads";
|
|
11009
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
11010
|
+
var INTEGRITY_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
11011
|
+
var INTEGRITY_CHECK_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
11012
|
+
var INTEGRITY_BACKUP_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
11013
|
+
var INTEGRITY_METADATA_KEYS = {
|
|
11014
|
+
checkedAt: "last_integrity_check",
|
|
11015
|
+
status: "last_integrity_status",
|
|
11016
|
+
durationMs: "last_integrity_duration_ms",
|
|
11017
|
+
detail: "last_integrity_detail"
|
|
11018
|
+
};
|
|
11019
|
+
function resolveWorkerSpec() {
|
|
11020
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
11021
|
+
const thisDir = path16.dirname(thisFile);
|
|
11022
|
+
const prodPath = path16.join(thisDir, "integrity-worker.js");
|
|
11023
|
+
if (existsSync3(prodPath)) return { filename: prodPath };
|
|
11024
|
+
const distPath = path16.resolve(thisDir, "..", "..", "..", "dist", "integrity-worker.js");
|
|
11025
|
+
if (existsSync3(distPath)) return { filename: distPath };
|
|
11026
|
+
const srcPath = path16.join(thisDir, "integrity-worker.ts");
|
|
11027
|
+
return { filename: srcPath, execArgv: ["--import", "tsx"] };
|
|
11028
|
+
}
|
|
11029
|
+
async function runIntegrityWorker(message, timeoutMs = INTEGRITY_CHECK_TIMEOUT_MS) {
|
|
11030
|
+
const workerSpec = resolveWorkerSpec();
|
|
11031
|
+
return new Promise((resolve4) => {
|
|
11032
|
+
const worker2 = new Worker2(workerSpec.filename, {
|
|
11033
|
+
execArgv: workerSpec.execArgv
|
|
11034
|
+
});
|
|
11035
|
+
let settled = false;
|
|
11036
|
+
const finish = (result) => {
|
|
11037
|
+
if (settled) return;
|
|
11038
|
+
settled = true;
|
|
11039
|
+
clearTimeout(timer2);
|
|
11040
|
+
void worker2.terminate().catch(() => {
|
|
11041
|
+
});
|
|
11042
|
+
resolve4(result);
|
|
11043
|
+
};
|
|
11044
|
+
const timer2 = setTimeout(() => {
|
|
11045
|
+
finish({
|
|
11046
|
+
status: "error",
|
|
11047
|
+
detail: `Integrity worker timed out after ${timeoutMs}ms`,
|
|
11048
|
+
durationMs: timeoutMs,
|
|
11049
|
+
backupCreated: false
|
|
11050
|
+
});
|
|
11051
|
+
}, timeoutMs);
|
|
11052
|
+
worker2.once("message", (result) => finish(result));
|
|
11053
|
+
worker2.once("error", (err) => {
|
|
11054
|
+
finish({
|
|
11055
|
+
status: "error",
|
|
11056
|
+
detail: err.message,
|
|
11057
|
+
durationMs: 0,
|
|
11058
|
+
backupCreated: false
|
|
11059
|
+
});
|
|
11060
|
+
});
|
|
11061
|
+
worker2.once("exit", (code) => {
|
|
11062
|
+
if (settled || code === 0) return;
|
|
11063
|
+
finish({
|
|
11064
|
+
status: "error",
|
|
11065
|
+
detail: `Integrity worker exited with code ${code}`,
|
|
11066
|
+
durationMs: 0,
|
|
11067
|
+
backupCreated: false
|
|
11068
|
+
});
|
|
11069
|
+
});
|
|
11070
|
+
worker2.postMessage(message);
|
|
11071
|
+
});
|
|
11072
|
+
}
|
|
11073
|
+
|
|
11002
11074
|
// src/index.ts
|
|
11003
|
-
import { openStateDb, scanVaultEntities as scanVaultEntities5, getAllEntitiesFromDb as getAllEntitiesFromDb6, loadContentHashes, saveContentHashBatch, renameContentHash
|
|
11075
|
+
import { openStateDb, scanVaultEntities as scanVaultEntities5, getAllEntitiesFromDb as getAllEntitiesFromDb6, loadContentHashes, saveContentHashBatch, renameContentHash } from "@velvetmonkey/vault-core";
|
|
11004
11076
|
|
|
11005
11077
|
// src/core/write/memory.ts
|
|
11006
11078
|
init_wikilinkFeedback();
|
|
@@ -12074,8 +12146,8 @@ function getNoteAccessFrequency(stateDb2, daysBack = 30) {
|
|
|
12074
12146
|
}
|
|
12075
12147
|
}
|
|
12076
12148
|
}
|
|
12077
|
-
return Array.from(noteMap.entries()).map(([
|
|
12078
|
-
path:
|
|
12149
|
+
return Array.from(noteMap.entries()).map(([path41, stats]) => ({
|
|
12150
|
+
path: path41,
|
|
12079
12151
|
access_count: stats.access_count,
|
|
12080
12152
|
last_accessed: stats.last_accessed,
|
|
12081
12153
|
tools_used: Array.from(stats.tools)
|
|
@@ -12762,10 +12834,10 @@ Use "flywheel_config" to inspect runtime configuration and set "tool_tier_overri
|
|
|
12762
12834
|
}
|
|
12763
12835
|
|
|
12764
12836
|
// src/tool-registry.ts
|
|
12765
|
-
import * as
|
|
12766
|
-
import { dirname as
|
|
12837
|
+
import * as path39 from "path";
|
|
12838
|
+
import { dirname as dirname6, join as join19 } from "path";
|
|
12767
12839
|
import { statSync as statSync6, readFileSync as readFileSync5 } from "fs";
|
|
12768
|
-
import { fileURLToPath as
|
|
12840
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
12769
12841
|
import { z as z39 } from "zod";
|
|
12770
12842
|
import { CallToolRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
12771
12843
|
import { getSessionId } from "@velvetmonkey/vault-core";
|
|
@@ -13114,13 +13186,13 @@ function multiHopBackfill(primaryResults, index, stateDb2, config2 = {}) {
|
|
|
13114
13186
|
candidates.sort((a, b) => b.score - a.score);
|
|
13115
13187
|
return candidates.slice(0, cfg.maxBackfill).map((c) => c.result);
|
|
13116
13188
|
}
|
|
13117
|
-
function scoreCandidate(
|
|
13118
|
-
const note = index.notes.get(
|
|
13189
|
+
function scoreCandidate(path41, index, stateDb2) {
|
|
13190
|
+
const note = index.notes.get(path41);
|
|
13119
13191
|
const decay = recencyDecay(note?.modified);
|
|
13120
13192
|
let hubScore = 1;
|
|
13121
13193
|
if (stateDb2) {
|
|
13122
13194
|
try {
|
|
13123
|
-
const title = note?.title ??
|
|
13195
|
+
const title = note?.title ?? path41.replace(/\.md$/, "").split("/").pop() ?? "";
|
|
13124
13196
|
const entity = getEntityByName3(stateDb2, title);
|
|
13125
13197
|
if (entity) hubScore = entity.hubScore ?? 1;
|
|
13126
13198
|
} catch {
|
|
@@ -13357,7 +13429,7 @@ init_stemmer();
|
|
|
13357
13429
|
|
|
13358
13430
|
// src/tools/read/structure.ts
|
|
13359
13431
|
import * as fs12 from "fs";
|
|
13360
|
-
import * as
|
|
13432
|
+
import * as path17 from "path";
|
|
13361
13433
|
var HEADING_REGEX2 = /^(#{1,6})\s+(.+)$/;
|
|
13362
13434
|
function extractHeadings2(content) {
|
|
13363
13435
|
const lines = content.replace(/\r\n/g, "\n").split("\n");
|
|
@@ -13411,7 +13483,7 @@ function buildSections(headings, totalLines) {
|
|
|
13411
13483
|
async function getNoteStructure(index, notePath, vaultPath2) {
|
|
13412
13484
|
const note = index.notes.get(notePath);
|
|
13413
13485
|
if (!note) return null;
|
|
13414
|
-
const absolutePath =
|
|
13486
|
+
const absolutePath = path17.join(vaultPath2, notePath);
|
|
13415
13487
|
let content;
|
|
13416
13488
|
try {
|
|
13417
13489
|
content = await fs12.promises.readFile(absolutePath, "utf-8");
|
|
@@ -13435,7 +13507,7 @@ async function getNoteStructure(index, notePath, vaultPath2) {
|
|
|
13435
13507
|
async function getSectionContent(index, notePath, headingText, vaultPath2, includeSubheadings = true) {
|
|
13436
13508
|
const note = index.notes.get(notePath);
|
|
13437
13509
|
if (!note) return null;
|
|
13438
|
-
const absolutePath =
|
|
13510
|
+
const absolutePath = path17.join(vaultPath2, notePath);
|
|
13439
13511
|
let content;
|
|
13440
13512
|
try {
|
|
13441
13513
|
content = await fs12.promises.readFile(absolutePath, "utf-8");
|
|
@@ -13478,7 +13550,7 @@ async function findSections(index, headingPattern, vaultPath2, folder) {
|
|
|
13478
13550
|
const results = [];
|
|
13479
13551
|
for (const note of index.notes.values()) {
|
|
13480
13552
|
if (folder && !note.path.startsWith(folder)) continue;
|
|
13481
|
-
const absolutePath =
|
|
13553
|
+
const absolutePath = path17.join(vaultPath2, note.path);
|
|
13482
13554
|
let content;
|
|
13483
13555
|
try {
|
|
13484
13556
|
content = await fs12.promises.readFile(absolutePath, "utf-8");
|
|
@@ -13580,11 +13652,11 @@ function applyEntityBridging(results, stateDb2, maxBridgesPerResult = 5) {
|
|
|
13580
13652
|
const linkMap = /* @__PURE__ */ new Map();
|
|
13581
13653
|
try {
|
|
13582
13654
|
const paths = results.map((r) => r.path).filter(Boolean);
|
|
13583
|
-
for (const
|
|
13655
|
+
for (const path41 of paths) {
|
|
13584
13656
|
const rows = stateDb2.db.prepare(
|
|
13585
13657
|
"SELECT target FROM note_links WHERE note_path = ?"
|
|
13586
|
-
).all(
|
|
13587
|
-
linkMap.set(
|
|
13658
|
+
).all(path41);
|
|
13659
|
+
linkMap.set(path41, new Set(rows.map((r) => r.target)));
|
|
13588
13660
|
}
|
|
13589
13661
|
} catch {
|
|
13590
13662
|
return;
|
|
@@ -14137,7 +14209,7 @@ init_vault_scope();
|
|
|
14137
14209
|
|
|
14138
14210
|
// src/tools/read/graph.ts
|
|
14139
14211
|
import * as fs13 from "fs";
|
|
14140
|
-
import * as
|
|
14212
|
+
import * as path18 from "path";
|
|
14141
14213
|
import { z as z2 } from "zod";
|
|
14142
14214
|
|
|
14143
14215
|
// src/tools/read/graphAdvanced.ts
|
|
@@ -14571,7 +14643,7 @@ function detectCycles(index, maxLength = 10, limit = 20) {
|
|
|
14571
14643
|
// src/tools/read/graph.ts
|
|
14572
14644
|
async function getContext(vaultPath2, sourcePath, line, contextLines = 1) {
|
|
14573
14645
|
try {
|
|
14574
|
-
const fullPath =
|
|
14646
|
+
const fullPath = path18.join(vaultPath2, sourcePath);
|
|
14575
14647
|
const content = await fs13.promises.readFile(fullPath, "utf-8");
|
|
14576
14648
|
const allLines = content.split("\n");
|
|
14577
14649
|
let fmLines = 0;
|
|
@@ -15116,14 +15188,14 @@ function registerWikilinkTools(server2, getIndex, getVaultPath, getStateDb4 = ()
|
|
|
15116
15188
|
};
|
|
15117
15189
|
function findSimilarEntity2(target, entities) {
|
|
15118
15190
|
const targetLower = target.toLowerCase();
|
|
15119
|
-
for (const [name,
|
|
15191
|
+
for (const [name, path41] of entities) {
|
|
15120
15192
|
if (name.startsWith(targetLower) || targetLower.startsWith(name)) {
|
|
15121
|
-
return
|
|
15193
|
+
return path41;
|
|
15122
15194
|
}
|
|
15123
15195
|
}
|
|
15124
|
-
for (const [name,
|
|
15196
|
+
for (const [name, path41] of entities) {
|
|
15125
15197
|
if (name.includes(targetLower) || targetLower.includes(name)) {
|
|
15126
|
-
return
|
|
15198
|
+
return path41;
|
|
15127
15199
|
}
|
|
15128
15200
|
}
|
|
15129
15201
|
return void 0;
|
|
@@ -15666,7 +15738,17 @@ function getProactiveLinkingOneLiner(stateDb2, daysBack = 1) {
|
|
|
15666
15738
|
init_wikilinkFeedback();
|
|
15667
15739
|
init_embeddings();
|
|
15668
15740
|
var STALE_THRESHOLD_SECONDS = 300;
|
|
15669
|
-
function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () => ({}), getStateDb4 = () => null, getWatcherStatus2 = () => null, getVersion = () => "unknown", getPipelineActivityState = () => null) {
|
|
15741
|
+
function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () => ({}), getStateDb4 = () => null, getWatcherStatus2 = () => null, getVersion = () => "unknown", getPipelineActivityState = () => null, getVaultRuntimeState = () => ({
|
|
15742
|
+
bootState: "booting",
|
|
15743
|
+
integrityState: "unknown",
|
|
15744
|
+
integrityCheckInProgress: false,
|
|
15745
|
+
integrityStartedAt: null,
|
|
15746
|
+
integritySource: null,
|
|
15747
|
+
lastIntegrityCheckedAt: null,
|
|
15748
|
+
lastIntegrityDurationMs: null,
|
|
15749
|
+
lastIntegrityDetail: null,
|
|
15750
|
+
lastBackupAt: null
|
|
15751
|
+
})) {
|
|
15670
15752
|
const IndexProgressSchema = z4.object({
|
|
15671
15753
|
parsed: z4.coerce.number().describe("Number of files parsed so far"),
|
|
15672
15754
|
total: z4.coerce.number().describe("Total number of files to parse")
|
|
@@ -15819,18 +15901,14 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15819
15901
|
}
|
|
15820
15902
|
let dbIntegrityFailed = false;
|
|
15821
15903
|
const stateDb2 = getStateDb4();
|
|
15822
|
-
|
|
15823
|
-
|
|
15824
|
-
|
|
15825
|
-
|
|
15826
|
-
|
|
15827
|
-
|
|
15828
|
-
|
|
15829
|
-
|
|
15830
|
-
} catch (err) {
|
|
15831
|
-
dbIntegrityFailed = true;
|
|
15832
|
-
recommendations.push(`Database integrity check error: ${err instanceof Error ? err.message : err}`);
|
|
15833
|
-
}
|
|
15904
|
+
const runtimeState = getVaultRuntimeState();
|
|
15905
|
+
if (runtimeState.integrityState === "failed") {
|
|
15906
|
+
dbIntegrityFailed = true;
|
|
15907
|
+
recommendations.push(`Database integrity check failed: ${runtimeState.lastIntegrityDetail ?? "unknown integrity failure"}`);
|
|
15908
|
+
} else if (runtimeState.integrityState === "error") {
|
|
15909
|
+
recommendations.push(`Database integrity check error: ${runtimeState.lastIntegrityDetail ?? "integrity runner error"}`);
|
|
15910
|
+
} else if (runtimeState.integrityCheckInProgress) {
|
|
15911
|
+
recommendations.push("Database integrity check is still running.");
|
|
15834
15912
|
}
|
|
15835
15913
|
const indexBuilt = indexState2 === "ready" && index !== void 0 && index.notes !== void 0;
|
|
15836
15914
|
let lastIndexActivityAt;
|
|
@@ -16024,6 +16102,14 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
16024
16102
|
tasks_ready: isTaskCacheReady(),
|
|
16025
16103
|
tasks_building: isTaskCacheBuilding(),
|
|
16026
16104
|
watcher_state: getWatcherStatus2()?.state,
|
|
16105
|
+
boot_state: runtimeState.bootState,
|
|
16106
|
+
integrity_state: runtimeState.integrityState,
|
|
16107
|
+
integrity_check_in_progress: runtimeState.integrityCheckInProgress,
|
|
16108
|
+
integrity_started_at: runtimeState.integrityStartedAt,
|
|
16109
|
+
integrity_source: runtimeState.integritySource,
|
|
16110
|
+
integrity_last_checked_at: runtimeState.lastIntegrityCheckedAt,
|
|
16111
|
+
integrity_duration_ms: runtimeState.lastIntegrityDurationMs,
|
|
16112
|
+
integrity_detail: runtimeState.lastIntegrityDetail,
|
|
16027
16113
|
watcher_pending: getWatcherStatus2()?.pendingEvents,
|
|
16028
16114
|
last_index_activity_at: lastIndexActivityAt,
|
|
16029
16115
|
last_index_activity_ago_seconds: lastIndexActivityAt ? Math.floor((Date.now() - lastIndexActivityAt) / 1e3) : void 0,
|
|
@@ -16075,6 +16161,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
16075
16161
|
async ({ detail = false }) => {
|
|
16076
16162
|
const activity = getPipelineActivityState();
|
|
16077
16163
|
const now = Date.now();
|
|
16164
|
+
const runtimeState = getVaultRuntimeState();
|
|
16078
16165
|
const output = {
|
|
16079
16166
|
busy: activity?.busy ?? false,
|
|
16080
16167
|
trigger: activity?.trigger ?? null,
|
|
@@ -16083,6 +16170,9 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
16083
16170
|
current_step: activity?.current_step ?? null,
|
|
16084
16171
|
progress: activity && activity.busy && activity.total_steps > 0 ? `${activity.completed_steps}/${activity.total_steps} steps` : null,
|
|
16085
16172
|
pending_events: activity?.pending_events ?? 0,
|
|
16173
|
+
boot_state: runtimeState.bootState,
|
|
16174
|
+
integrity_state: runtimeState.integrityState,
|
|
16175
|
+
integrity_check_in_progress: runtimeState.integrityCheckInProgress,
|
|
16086
16176
|
last_completed: activity?.last_completed_at ? {
|
|
16087
16177
|
at: activity.last_completed_at,
|
|
16088
16178
|
ago_seconds: Math.floor((now - activity.last_completed_at) / 1e3),
|
|
@@ -16143,8 +16233,8 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
16143
16233
|
daily_counts: z4.record(z4.number())
|
|
16144
16234
|
}).describe("Activity summary for the last 7 days")
|
|
16145
16235
|
};
|
|
16146
|
-
function isPeriodicNote3(
|
|
16147
|
-
const filename =
|
|
16236
|
+
function isPeriodicNote3(path41) {
|
|
16237
|
+
const filename = path41.split("/").pop() || "";
|
|
16148
16238
|
const nameWithoutExt = filename.replace(/\.md$/, "");
|
|
16149
16239
|
const patterns = [
|
|
16150
16240
|
/^\d{4}-\d{2}-\d{2}$/,
|
|
@@ -16159,7 +16249,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
16159
16249
|
// YYYY (yearly)
|
|
16160
16250
|
];
|
|
16161
16251
|
const periodicFolders = ["daily", "weekly", "monthly", "quarterly", "yearly", "journal", "journals"];
|
|
16162
|
-
const folder =
|
|
16252
|
+
const folder = path41.split("/")[0]?.toLowerCase() || "";
|
|
16163
16253
|
return patterns.some((p) => p.test(nameWithoutExt)) || periodicFolders.includes(folder);
|
|
16164
16254
|
}
|
|
16165
16255
|
async function runVaultStats() {
|
|
@@ -17266,13 +17356,13 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17266
17356
|
max_content_chars: z6.number().default(2e4).describe("Max total chars of section content to include. Sections are truncated at paragraph boundaries.")
|
|
17267
17357
|
}
|
|
17268
17358
|
},
|
|
17269
|
-
async ({ path:
|
|
17359
|
+
async ({ path: path41, include_content, max_content_chars }) => {
|
|
17270
17360
|
const index = getIndex();
|
|
17271
17361
|
const vaultPath2 = getVaultPath();
|
|
17272
|
-
const result = await getNoteStructure(index,
|
|
17362
|
+
const result = await getNoteStructure(index, path41, vaultPath2);
|
|
17273
17363
|
if (!result) {
|
|
17274
17364
|
return {
|
|
17275
|
-
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path:
|
|
17365
|
+
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path41 }, null, 2) }]
|
|
17276
17366
|
};
|
|
17277
17367
|
}
|
|
17278
17368
|
let totalChars = 0;
|
|
@@ -17283,7 +17373,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17283
17373
|
truncated = true;
|
|
17284
17374
|
break;
|
|
17285
17375
|
}
|
|
17286
|
-
const sectionResult = await getSectionContent(index,
|
|
17376
|
+
const sectionResult = await getSectionContent(index, path41, section.heading.text, vaultPath2, true);
|
|
17287
17377
|
if (sectionResult) {
|
|
17288
17378
|
let content = sectionResult.content;
|
|
17289
17379
|
const remaining = max_content_chars - totalChars;
|
|
@@ -17298,13 +17388,13 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17298
17388
|
}
|
|
17299
17389
|
}
|
|
17300
17390
|
}
|
|
17301
|
-
const note = index.notes.get(
|
|
17391
|
+
const note = index.notes.get(path41);
|
|
17302
17392
|
const enriched = { ...result };
|
|
17303
17393
|
if (note) {
|
|
17304
17394
|
enriched.frontmatter = note.frontmatter;
|
|
17305
17395
|
enriched.tags = note.tags;
|
|
17306
17396
|
enriched.aliases = note.aliases;
|
|
17307
|
-
const normalizedPath =
|
|
17397
|
+
const normalizedPath = path41.toLowerCase().replace(/\.md$/, "");
|
|
17308
17398
|
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
17309
17399
|
enriched.backlink_count = backlinks.length;
|
|
17310
17400
|
enriched.outlink_count = note.outlinks.length;
|
|
@@ -17342,15 +17432,15 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17342
17432
|
max_content_chars: z6.number().default(1e4).describe("Max chars of section content. Truncated at paragraph boundaries.")
|
|
17343
17433
|
}
|
|
17344
17434
|
},
|
|
17345
|
-
async ({ path:
|
|
17435
|
+
async ({ path: path41, heading, include_subheadings, max_content_chars }) => {
|
|
17346
17436
|
const index = getIndex();
|
|
17347
17437
|
const vaultPath2 = getVaultPath();
|
|
17348
|
-
const result = await getSectionContent(index,
|
|
17438
|
+
const result = await getSectionContent(index, path41, heading, vaultPath2, include_subheadings);
|
|
17349
17439
|
if (!result) {
|
|
17350
17440
|
return {
|
|
17351
17441
|
content: [{ type: "text", text: JSON.stringify({
|
|
17352
17442
|
error: "Section not found",
|
|
17353
|
-
path:
|
|
17443
|
+
path: path41,
|
|
17354
17444
|
heading
|
|
17355
17445
|
}, null, 2) }]
|
|
17356
17446
|
};
|
|
@@ -17411,16 +17501,16 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17411
17501
|
offset: z6.coerce.number().default(0).describe("Number of results to skip (for pagination)")
|
|
17412
17502
|
}
|
|
17413
17503
|
},
|
|
17414
|
-
async ({ path:
|
|
17504
|
+
async ({ path: path41, status, has_due_date, folder, tag, limit: requestedLimit, offset }) => {
|
|
17415
17505
|
const limit = Math.min(requestedLimit ?? 25, MAX_LIMIT);
|
|
17416
17506
|
const index = getIndex();
|
|
17417
17507
|
const vaultPath2 = getVaultPath();
|
|
17418
17508
|
const config2 = getConfig2();
|
|
17419
|
-
if (
|
|
17420
|
-
const result2 = await getTasksFromNote(index,
|
|
17509
|
+
if (path41) {
|
|
17510
|
+
const result2 = await getTasksFromNote(index, path41, vaultPath2, getExcludeTags(config2));
|
|
17421
17511
|
if (!result2) {
|
|
17422
17512
|
return {
|
|
17423
|
-
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path:
|
|
17513
|
+
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path41 }, null, 2) }]
|
|
17424
17514
|
};
|
|
17425
17515
|
}
|
|
17426
17516
|
let filtered = result2;
|
|
@@ -17430,7 +17520,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17430
17520
|
const paged2 = filtered.slice(offset, offset + limit);
|
|
17431
17521
|
return {
|
|
17432
17522
|
content: [{ type: "text", text: JSON.stringify({
|
|
17433
|
-
path:
|
|
17523
|
+
path: path41,
|
|
17434
17524
|
total_count: filtered.length,
|
|
17435
17525
|
returned_count: paged2.length,
|
|
17436
17526
|
open: result2.filter((t) => t.status === "open").length,
|
|
@@ -17586,7 +17676,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17586
17676
|
// src/tools/read/migrations.ts
|
|
17587
17677
|
import { z as z7 } from "zod";
|
|
17588
17678
|
import * as fs15 from "fs/promises";
|
|
17589
|
-
import * as
|
|
17679
|
+
import * as path19 from "path";
|
|
17590
17680
|
import matter2 from "gray-matter";
|
|
17591
17681
|
function getNotesInFolder(index, folder) {
|
|
17592
17682
|
const notes = [];
|
|
@@ -17599,7 +17689,7 @@ function getNotesInFolder(index, folder) {
|
|
|
17599
17689
|
return notes;
|
|
17600
17690
|
}
|
|
17601
17691
|
async function readFileContent(notePath, vaultPath2) {
|
|
17602
|
-
const fullPath =
|
|
17692
|
+
const fullPath = path19.join(vaultPath2, notePath);
|
|
17603
17693
|
try {
|
|
17604
17694
|
return await fs15.readFile(fullPath, "utf-8");
|
|
17605
17695
|
} catch {
|
|
@@ -17607,7 +17697,7 @@ async function readFileContent(notePath, vaultPath2) {
|
|
|
17607
17697
|
}
|
|
17608
17698
|
}
|
|
17609
17699
|
async function writeFileContent(notePath, vaultPath2, content) {
|
|
17610
|
-
const fullPath =
|
|
17700
|
+
const fullPath = path19.join(vaultPath2, notePath);
|
|
17611
17701
|
try {
|
|
17612
17702
|
await fs15.writeFile(fullPath, content, "utf-8");
|
|
17613
17703
|
return true;
|
|
@@ -17788,7 +17878,7 @@ function registerMigrationTools(server2, getIndex, getVaultPath) {
|
|
|
17788
17878
|
|
|
17789
17879
|
// src/tools/read/graphAnalysis.ts
|
|
17790
17880
|
import fs16 from "node:fs";
|
|
17791
|
-
import
|
|
17881
|
+
import path20 from "node:path";
|
|
17792
17882
|
import { z as z8 } from "zod";
|
|
17793
17883
|
|
|
17794
17884
|
// src/tools/read/schema.ts
|
|
@@ -18314,7 +18404,7 @@ function registerGraphAnalysisTools(server2, getIndex, getVaultPath, getStateDb4
|
|
|
18314
18404
|
const scored = allNotes.map((note) => {
|
|
18315
18405
|
let wordCount = 0;
|
|
18316
18406
|
try {
|
|
18317
|
-
const content = fs16.readFileSync(
|
|
18407
|
+
const content = fs16.readFileSync(path20.join(vaultPath2, note.path), "utf-8");
|
|
18318
18408
|
const body = content.replace(/^---[\s\S]*?---\n?/, "");
|
|
18319
18409
|
wordCount = body.split(/\s+/).filter((w) => w.length > 0).length;
|
|
18320
18410
|
} catch {
|
|
@@ -18945,12 +19035,12 @@ import { z as z11 } from "zod";
|
|
|
18945
19035
|
|
|
18946
19036
|
// src/tools/read/bidirectional.ts
|
|
18947
19037
|
import * as fs17 from "fs/promises";
|
|
18948
|
-
import * as
|
|
19038
|
+
import * as path21 from "path";
|
|
18949
19039
|
import matter3 from "gray-matter";
|
|
18950
19040
|
var PROSE_PATTERN_REGEX = /^([A-Za-z][A-Za-z0-9 _-]*):\s*(?:\[\[([^\]]+)\]\]|"([^"]+)"|([^\n]+?))\s*$/gm;
|
|
18951
19041
|
var CODE_BLOCK_REGEX2 = /```[\s\S]*?```|`[^`\n]+`/g;
|
|
18952
19042
|
async function readFileContent2(notePath, vaultPath2) {
|
|
18953
|
-
const fullPath =
|
|
19043
|
+
const fullPath = path21.join(vaultPath2, notePath);
|
|
18954
19044
|
try {
|
|
18955
19045
|
return await fs17.readFile(fullPath, "utf-8");
|
|
18956
19046
|
} catch {
|
|
@@ -19129,10 +19219,10 @@ async function suggestWikilinksInFrontmatter(index, notePath, vaultPath2) {
|
|
|
19129
19219
|
|
|
19130
19220
|
// src/tools/read/computed.ts
|
|
19131
19221
|
import * as fs18 from "fs/promises";
|
|
19132
|
-
import * as
|
|
19222
|
+
import * as path22 from "path";
|
|
19133
19223
|
import matter4 from "gray-matter";
|
|
19134
19224
|
async function readFileContent3(notePath, vaultPath2) {
|
|
19135
|
-
const fullPath =
|
|
19225
|
+
const fullPath = path22.join(vaultPath2, notePath);
|
|
19136
19226
|
try {
|
|
19137
19227
|
return await fs18.readFile(fullPath, "utf-8");
|
|
19138
19228
|
} catch {
|
|
@@ -19140,7 +19230,7 @@ async function readFileContent3(notePath, vaultPath2) {
|
|
|
19140
19230
|
}
|
|
19141
19231
|
}
|
|
19142
19232
|
async function getFileStats(notePath, vaultPath2) {
|
|
19143
|
-
const fullPath =
|
|
19233
|
+
const fullPath = path22.join(vaultPath2, notePath);
|
|
19144
19234
|
try {
|
|
19145
19235
|
const stats = await fs18.stat(fullPath);
|
|
19146
19236
|
return {
|
|
@@ -19413,7 +19503,7 @@ function registerNoteIntelligenceTools(server2, getIndex, getVaultPath, getConfi
|
|
|
19413
19503
|
init_writer();
|
|
19414
19504
|
import { z as z12 } from "zod";
|
|
19415
19505
|
import fs24 from "fs/promises";
|
|
19416
|
-
import
|
|
19506
|
+
import path27 from "path";
|
|
19417
19507
|
|
|
19418
19508
|
// src/core/write/validator.ts
|
|
19419
19509
|
var TIMESTAMP_PATTERN = /^\*\*\d{2}:\d{2}\*\*/;
|
|
@@ -19632,16 +19722,16 @@ init_writer();
|
|
|
19632
19722
|
init_wikilinks();
|
|
19633
19723
|
init_wikilinkFeedback();
|
|
19634
19724
|
import fs23 from "fs/promises";
|
|
19635
|
-
import
|
|
19725
|
+
import path26 from "path";
|
|
19636
19726
|
|
|
19637
19727
|
// src/core/write/policy/policyPaths.ts
|
|
19638
19728
|
import fs22 from "fs/promises";
|
|
19639
|
-
import
|
|
19729
|
+
import path25 from "path";
|
|
19640
19730
|
function getPoliciesDir(vaultPath2) {
|
|
19641
|
-
return
|
|
19731
|
+
return path25.join(vaultPath2, ".flywheel", "policies");
|
|
19642
19732
|
}
|
|
19643
19733
|
function getLegacyPoliciesDir(vaultPath2) {
|
|
19644
|
-
return
|
|
19734
|
+
return path25.join(vaultPath2, ".claude", "policies");
|
|
19645
19735
|
}
|
|
19646
19736
|
async function ensurePoliciesDir(vaultPath2) {
|
|
19647
19737
|
const dir = getPoliciesDir(vaultPath2);
|
|
@@ -19658,14 +19748,14 @@ async function migratePoliciesIfNeeded(vaultPath2) {
|
|
|
19658
19748
|
const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
19659
19749
|
if (yamlFiles.length === 0) {
|
|
19660
19750
|
await tryRmdir(legacyDir);
|
|
19661
|
-
await tryRmdir(
|
|
19751
|
+
await tryRmdir(path25.join(vaultPath2, ".claude"));
|
|
19662
19752
|
return;
|
|
19663
19753
|
}
|
|
19664
19754
|
await ensurePoliciesDir(vaultPath2);
|
|
19665
19755
|
const destDir = getPoliciesDir(vaultPath2);
|
|
19666
19756
|
for (const file of yamlFiles) {
|
|
19667
|
-
const src =
|
|
19668
|
-
const dest =
|
|
19757
|
+
const src = path25.join(legacyDir, file);
|
|
19758
|
+
const dest = path25.join(destDir, file);
|
|
19669
19759
|
try {
|
|
19670
19760
|
await fs22.access(dest);
|
|
19671
19761
|
} catch {
|
|
@@ -19674,7 +19764,7 @@ async function migratePoliciesIfNeeded(vaultPath2) {
|
|
|
19674
19764
|
await fs22.unlink(src);
|
|
19675
19765
|
}
|
|
19676
19766
|
await tryRmdir(legacyDir);
|
|
19677
|
-
await tryRmdir(
|
|
19767
|
+
await tryRmdir(path25.join(vaultPath2, ".claude"));
|
|
19678
19768
|
}
|
|
19679
19769
|
async function tryRmdir(dir) {
|
|
19680
19770
|
try {
|
|
@@ -19687,7 +19777,7 @@ async function tryRmdir(dir) {
|
|
|
19687
19777
|
}
|
|
19688
19778
|
var migrationCache = /* @__PURE__ */ new Map();
|
|
19689
19779
|
async function ensureMigrated(vaultPath2) {
|
|
19690
|
-
const key =
|
|
19780
|
+
const key = path25.resolve(vaultPath2);
|
|
19691
19781
|
if (!migrationCache.has(key)) {
|
|
19692
19782
|
migrationCache.set(key, migratePoliciesIfNeeded(vaultPath2));
|
|
19693
19783
|
}
|
|
@@ -19754,7 +19844,7 @@ async function getPolicyHint(vaultPath2) {
|
|
|
19754
19844
|
return "";
|
|
19755
19845
|
}
|
|
19756
19846
|
async function ensureFileExists(vaultPath2, notePath) {
|
|
19757
|
-
const fullPath =
|
|
19847
|
+
const fullPath = path26.join(vaultPath2, notePath);
|
|
19758
19848
|
try {
|
|
19759
19849
|
await fs23.access(fullPath);
|
|
19760
19850
|
return null;
|
|
@@ -19959,7 +20049,7 @@ async function executeCreateNote(options) {
|
|
|
19959
20049
|
if (!pathCheck.valid) {
|
|
19960
20050
|
return { success: false, result: errorResult(notePath, `Path blocked: ${pathCheck.reason}`), filesWritten: [] };
|
|
19961
20051
|
}
|
|
19962
|
-
const fullPath =
|
|
20052
|
+
const fullPath = path26.join(vaultPath2, notePath);
|
|
19963
20053
|
let fileExists = false;
|
|
19964
20054
|
try {
|
|
19965
20055
|
await fs23.access(fullPath);
|
|
@@ -19969,7 +20059,7 @@ async function executeCreateNote(options) {
|
|
|
19969
20059
|
if (fileExists && !overwrite) {
|
|
19970
20060
|
return { success: false, result: errorResult(notePath, `File already exists: ${notePath}. Use overwrite=true to replace.`), filesWritten: [] };
|
|
19971
20061
|
}
|
|
19972
|
-
await fs23.mkdir(
|
|
20062
|
+
await fs23.mkdir(path26.dirname(fullPath), { recursive: true });
|
|
19973
20063
|
const { maybeApplyWikilinks: maybeApplyWikilinks2 } = await Promise.resolve().then(() => (init_wikilinks(), wikilinks_exports));
|
|
19974
20064
|
const { content: processedContent } = maybeApplyWikilinks2(content, skipWikilinks ?? false, notePath);
|
|
19975
20065
|
let finalFrontmatter = frontmatter;
|
|
@@ -20003,7 +20093,7 @@ async function executeDeleteNote(options) {
|
|
|
20003
20093
|
if (!pathCheck.valid) {
|
|
20004
20094
|
return { success: false, result: errorResult(notePath, `Path blocked: ${pathCheck.reason}`), filesWritten: [] };
|
|
20005
20095
|
}
|
|
20006
|
-
const fullPath =
|
|
20096
|
+
const fullPath = path26.join(vaultPath2, notePath);
|
|
20007
20097
|
try {
|
|
20008
20098
|
await fs23.access(fullPath);
|
|
20009
20099
|
} catch {
|
|
@@ -20027,10 +20117,10 @@ async function createNoteFromTemplate(vaultPath2, notePath, config2) {
|
|
|
20027
20117
|
if (!validation.valid) {
|
|
20028
20118
|
throw new Error(`Path blocked: ${validation.reason}`);
|
|
20029
20119
|
}
|
|
20030
|
-
const fullPath =
|
|
20031
|
-
await fs24.mkdir(
|
|
20120
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
20121
|
+
await fs24.mkdir(path27.dirname(fullPath), { recursive: true });
|
|
20032
20122
|
const templates = config2.templates || {};
|
|
20033
|
-
const filename =
|
|
20123
|
+
const filename = path27.basename(notePath, ".md").toLowerCase();
|
|
20034
20124
|
let templatePath;
|
|
20035
20125
|
let periodicType;
|
|
20036
20126
|
const dailyPattern = /^\d{4}-\d{2}-\d{2}/;
|
|
@@ -20063,7 +20153,7 @@ async function createNoteFromTemplate(vaultPath2, notePath, config2) {
|
|
|
20063
20153
|
];
|
|
20064
20154
|
for (const candidate of candidates) {
|
|
20065
20155
|
try {
|
|
20066
|
-
await fs24.access(
|
|
20156
|
+
await fs24.access(path27.join(vaultPath2, candidate));
|
|
20067
20157
|
templatePath = candidate;
|
|
20068
20158
|
console.error(`[Flywheel] Template not in config but found at ${candidate} \u2014 using it`);
|
|
20069
20159
|
break;
|
|
@@ -20074,11 +20164,11 @@ async function createNoteFromTemplate(vaultPath2, notePath, config2) {
|
|
|
20074
20164
|
let templateContent;
|
|
20075
20165
|
if (templatePath) {
|
|
20076
20166
|
try {
|
|
20077
|
-
const absTemplatePath =
|
|
20167
|
+
const absTemplatePath = path27.join(vaultPath2, templatePath);
|
|
20078
20168
|
templateContent = await fs24.readFile(absTemplatePath, "utf-8");
|
|
20079
20169
|
} catch {
|
|
20080
20170
|
console.error(`[Flywheel] Template at ${templatePath} not readable, using minimal fallback`);
|
|
20081
|
-
const title =
|
|
20171
|
+
const title = path27.basename(notePath, ".md");
|
|
20082
20172
|
templateContent = `---
|
|
20083
20173
|
---
|
|
20084
20174
|
|
|
@@ -20090,7 +20180,7 @@ async function createNoteFromTemplate(vaultPath2, notePath, config2) {
|
|
|
20090
20180
|
if (periodicType) {
|
|
20091
20181
|
console.error(`[Flywheel] No ${periodicType} template found in config or vault \u2014 using minimal fallback`);
|
|
20092
20182
|
}
|
|
20093
|
-
const title =
|
|
20183
|
+
const title = path27.basename(notePath, ".md");
|
|
20094
20184
|
templateContent = `---
|
|
20095
20185
|
---
|
|
20096
20186
|
|
|
@@ -20099,7 +20189,7 @@ async function createNoteFromTemplate(vaultPath2, notePath, config2) {
|
|
|
20099
20189
|
}
|
|
20100
20190
|
const now = /* @__PURE__ */ new Date();
|
|
20101
20191
|
const dateStr = now.toISOString().split("T")[0];
|
|
20102
|
-
templateContent = templateContent.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g,
|
|
20192
|
+
templateContent = templateContent.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, path27.basename(notePath, ".md"));
|
|
20103
20193
|
const matter9 = (await import("gray-matter")).default;
|
|
20104
20194
|
const parsed = matter9(templateContent);
|
|
20105
20195
|
if (!parsed.data.date) {
|
|
@@ -20138,7 +20228,7 @@ function registerMutationTools(server2, getVaultPath, getConfig2 = () => ({})) {
|
|
|
20138
20228
|
let noteCreated = false;
|
|
20139
20229
|
let templateUsed;
|
|
20140
20230
|
if (create_if_missing && !dry_run) {
|
|
20141
|
-
const fullPath =
|
|
20231
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
20142
20232
|
try {
|
|
20143
20233
|
await fs24.access(fullPath);
|
|
20144
20234
|
} catch {
|
|
@@ -20149,7 +20239,7 @@ function registerMutationTools(server2, getVaultPath, getConfig2 = () => ({})) {
|
|
|
20149
20239
|
}
|
|
20150
20240
|
}
|
|
20151
20241
|
if (create_if_missing && dry_run) {
|
|
20152
|
-
const fullPath =
|
|
20242
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
20153
20243
|
try {
|
|
20154
20244
|
await fs24.access(fullPath);
|
|
20155
20245
|
} catch {
|
|
@@ -20636,7 +20726,7 @@ init_writer();
|
|
|
20636
20726
|
init_wikilinks();
|
|
20637
20727
|
import { z as z15 } from "zod";
|
|
20638
20728
|
import fs25 from "fs/promises";
|
|
20639
|
-
import
|
|
20729
|
+
import path28 from "path";
|
|
20640
20730
|
function registerNoteTools(server2, getVaultPath, getIndex) {
|
|
20641
20731
|
server2.tool(
|
|
20642
20732
|
"vault_create_note",
|
|
@@ -20662,23 +20752,23 @@ function registerNoteTools(server2, getVaultPath, getIndex) {
|
|
|
20662
20752
|
if (!validatePath(vaultPath2, notePath)) {
|
|
20663
20753
|
return formatMcpResult(errorResult(notePath, "Invalid path: path traversal not allowed"));
|
|
20664
20754
|
}
|
|
20665
|
-
const fullPath =
|
|
20755
|
+
const fullPath = path28.join(vaultPath2, notePath);
|
|
20666
20756
|
const existsCheck = await ensureFileExists(vaultPath2, notePath);
|
|
20667
20757
|
if (existsCheck === null && !overwrite) {
|
|
20668
20758
|
return formatMcpResult(errorResult(notePath, `File already exists: ${notePath}. Use overwrite=true to replace.`));
|
|
20669
20759
|
}
|
|
20670
|
-
const dir =
|
|
20760
|
+
const dir = path28.dirname(fullPath);
|
|
20671
20761
|
await fs25.mkdir(dir, { recursive: true });
|
|
20672
20762
|
let effectiveContent = content;
|
|
20673
20763
|
let effectiveFrontmatter = frontmatter;
|
|
20674
20764
|
if (template) {
|
|
20675
|
-
const templatePath =
|
|
20765
|
+
const templatePath = path28.join(vaultPath2, template);
|
|
20676
20766
|
try {
|
|
20677
20767
|
const raw = await fs25.readFile(templatePath, "utf-8");
|
|
20678
20768
|
const matter9 = (await import("gray-matter")).default;
|
|
20679
20769
|
const parsed = matter9(raw);
|
|
20680
20770
|
const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
20681
|
-
const title =
|
|
20771
|
+
const title = path28.basename(notePath, ".md");
|
|
20682
20772
|
let templateContent = parsed.content.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, title);
|
|
20683
20773
|
if (content) {
|
|
20684
20774
|
templateContent = templateContent.trimEnd() + "\n\n" + content;
|
|
@@ -20697,7 +20787,7 @@ function registerNoteTools(server2, getVaultPath, getIndex) {
|
|
|
20697
20787
|
effectiveFrontmatter.created = now.toISOString();
|
|
20698
20788
|
}
|
|
20699
20789
|
const warnings = [];
|
|
20700
|
-
const noteName =
|
|
20790
|
+
const noteName = path28.basename(notePath, ".md");
|
|
20701
20791
|
const existingAliases = Array.isArray(effectiveFrontmatter?.aliases) ? effectiveFrontmatter.aliases.filter((a) => typeof a === "string") : [];
|
|
20702
20792
|
const preflight = await checkPreflightSimilarity(noteName);
|
|
20703
20793
|
if (preflight.existingEntity) {
|
|
@@ -20838,7 +20928,7 @@ ${sources}`;
|
|
|
20838
20928
|
}
|
|
20839
20929
|
return formatMcpResult(errorResult(notePath, previewLines.join("\n")));
|
|
20840
20930
|
}
|
|
20841
|
-
const fullPath =
|
|
20931
|
+
const fullPath = path28.join(vaultPath2, notePath);
|
|
20842
20932
|
await fs25.unlink(fullPath);
|
|
20843
20933
|
const gitInfo = await handleGitCommit(vaultPath2, notePath, commit, "[Flywheel:Delete]");
|
|
20844
20934
|
const message = backlinkWarning ? `Deleted note: ${notePath}
|
|
@@ -20860,7 +20950,7 @@ init_git();
|
|
|
20860
20950
|
init_wikilinks();
|
|
20861
20951
|
import { z as z16 } from "zod";
|
|
20862
20952
|
import fs26 from "fs/promises";
|
|
20863
|
-
import
|
|
20953
|
+
import path29 from "path";
|
|
20864
20954
|
import matter6 from "gray-matter";
|
|
20865
20955
|
function escapeRegex(str) {
|
|
20866
20956
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -20879,7 +20969,7 @@ function extractWikilinks2(content) {
|
|
|
20879
20969
|
return wikilinks;
|
|
20880
20970
|
}
|
|
20881
20971
|
function getTitleFromPath(filePath) {
|
|
20882
|
-
return
|
|
20972
|
+
return path29.basename(filePath, ".md");
|
|
20883
20973
|
}
|
|
20884
20974
|
async function findBacklinks(vaultPath2, targetTitle, targetAliases) {
|
|
20885
20975
|
const results = [];
|
|
@@ -20888,7 +20978,7 @@ async function findBacklinks(vaultPath2, targetTitle, targetAliases) {
|
|
|
20888
20978
|
const files = [];
|
|
20889
20979
|
const entries = await fs26.readdir(dir, { withFileTypes: true });
|
|
20890
20980
|
for (const entry of entries) {
|
|
20891
|
-
const fullPath =
|
|
20981
|
+
const fullPath = path29.join(dir, entry.name);
|
|
20892
20982
|
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
20893
20983
|
files.push(...await scanDir(fullPath));
|
|
20894
20984
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -20899,7 +20989,7 @@ async function findBacklinks(vaultPath2, targetTitle, targetAliases) {
|
|
|
20899
20989
|
}
|
|
20900
20990
|
const allFiles = await scanDir(vaultPath2);
|
|
20901
20991
|
for (const filePath of allFiles) {
|
|
20902
|
-
const relativePath =
|
|
20992
|
+
const relativePath = path29.relative(vaultPath2, filePath);
|
|
20903
20993
|
const content = await fs26.readFile(filePath, "utf-8");
|
|
20904
20994
|
const wikilinks = extractWikilinks2(content);
|
|
20905
20995
|
const matchingLinks = [];
|
|
@@ -20986,8 +21076,8 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20986
21076
|
};
|
|
20987
21077
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
20988
21078
|
}
|
|
20989
|
-
const oldFullPath =
|
|
20990
|
-
const newFullPath =
|
|
21079
|
+
const oldFullPath = path29.join(vaultPath2, oldPath);
|
|
21080
|
+
const newFullPath = path29.join(vaultPath2, newPath);
|
|
20991
21081
|
try {
|
|
20992
21082
|
await fs26.access(oldFullPath);
|
|
20993
21083
|
} catch {
|
|
@@ -21068,7 +21158,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
21068
21158
|
};
|
|
21069
21159
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
21070
21160
|
}
|
|
21071
|
-
const destDir =
|
|
21161
|
+
const destDir = path29.dirname(newFullPath);
|
|
21072
21162
|
await fs26.mkdir(destDir, { recursive: true });
|
|
21073
21163
|
await fs26.rename(oldFullPath, newFullPath);
|
|
21074
21164
|
let gitCommit;
|
|
@@ -21144,10 +21234,10 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
21144
21234
|
if (sanitizedTitle !== newTitle) {
|
|
21145
21235
|
console.error(`[Flywheel] Title sanitized: "${newTitle}" \u2192 "${sanitizedTitle}"`);
|
|
21146
21236
|
}
|
|
21147
|
-
const fullPath =
|
|
21148
|
-
const dir =
|
|
21149
|
-
const newPath = dir === "." ? `${sanitizedTitle}.md` :
|
|
21150
|
-
const newFullPath =
|
|
21237
|
+
const fullPath = path29.join(vaultPath2, notePath);
|
|
21238
|
+
const dir = path29.dirname(notePath);
|
|
21239
|
+
const newPath = dir === "." ? `${sanitizedTitle}.md` : path29.join(dir, `${sanitizedTitle}.md`);
|
|
21240
|
+
const newFullPath = path29.join(vaultPath2, newPath);
|
|
21151
21241
|
try {
|
|
21152
21242
|
await fs26.access(fullPath);
|
|
21153
21243
|
} catch {
|
|
@@ -21279,7 +21369,7 @@ init_writer();
|
|
|
21279
21369
|
init_wikilinks();
|
|
21280
21370
|
import { z as z17 } from "zod";
|
|
21281
21371
|
import fs27 from "fs/promises";
|
|
21282
|
-
import
|
|
21372
|
+
import path30 from "path";
|
|
21283
21373
|
function registerMergeTools(server2, getVaultPath) {
|
|
21284
21374
|
server2.tool(
|
|
21285
21375
|
"merge_entities",
|
|
@@ -21576,14 +21666,14 @@ async function findSourceNote(vaultPath2, sourceName, excludePath) {
|
|
|
21576
21666
|
}
|
|
21577
21667
|
for (const entry of entries) {
|
|
21578
21668
|
if (entry.name.startsWith(".")) continue;
|
|
21579
|
-
const fullPath =
|
|
21669
|
+
const fullPath = path30.join(dir, entry.name);
|
|
21580
21670
|
if (entry.isDirectory()) {
|
|
21581
21671
|
const found = await scanDir(fullPath);
|
|
21582
21672
|
if (found) return found;
|
|
21583
21673
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
21584
|
-
const basename5 =
|
|
21674
|
+
const basename5 = path30.basename(entry.name, ".md");
|
|
21585
21675
|
if (basename5.toLowerCase() === targetLower) {
|
|
21586
|
-
const relative2 =
|
|
21676
|
+
const relative2 = path30.relative(vaultPath2, fullPath).replace(/\\/g, "/");
|
|
21587
21677
|
if (relative2 !== excludePath) return relative2;
|
|
21588
21678
|
}
|
|
21589
21679
|
}
|
|
@@ -21704,7 +21794,7 @@ Message: ${undoResult.undoneCommit.message}` : void 0
|
|
|
21704
21794
|
}
|
|
21705
21795
|
|
|
21706
21796
|
// src/tools/write/policy.ts
|
|
21707
|
-
import * as
|
|
21797
|
+
import * as path35 from "path";
|
|
21708
21798
|
import { z as z20 } from "zod";
|
|
21709
21799
|
|
|
21710
21800
|
// src/core/write/policy/index.ts
|
|
@@ -21714,7 +21804,7 @@ init_schema();
|
|
|
21714
21804
|
// src/core/write/policy/parser.ts
|
|
21715
21805
|
init_schema();
|
|
21716
21806
|
import fs28 from "fs/promises";
|
|
21717
|
-
import
|
|
21807
|
+
import path31 from "path";
|
|
21718
21808
|
import matter7 from "gray-matter";
|
|
21719
21809
|
function parseYaml(content) {
|
|
21720
21810
|
const parsed = matter7(`---
|
|
@@ -21765,12 +21855,12 @@ async function loadPolicyFile(filePath) {
|
|
|
21765
21855
|
async function loadPolicy(vaultPath2, policyName) {
|
|
21766
21856
|
await ensureMigrated(vaultPath2);
|
|
21767
21857
|
const policiesDir = getPoliciesDir(vaultPath2);
|
|
21768
|
-
const policyPath =
|
|
21858
|
+
const policyPath = path31.join(policiesDir, `${policyName}.yaml`);
|
|
21769
21859
|
try {
|
|
21770
21860
|
await fs28.access(policyPath);
|
|
21771
21861
|
return loadPolicyFile(policyPath);
|
|
21772
21862
|
} catch {
|
|
21773
|
-
const ymlPath =
|
|
21863
|
+
const ymlPath = path31.join(policiesDir, `${policyName}.yml`);
|
|
21774
21864
|
try {
|
|
21775
21865
|
await fs28.access(ymlPath);
|
|
21776
21866
|
return loadPolicyFile(ymlPath);
|
|
@@ -21913,7 +22003,7 @@ init_writer();
|
|
|
21913
22003
|
init_git();
|
|
21914
22004
|
init_wikilinks();
|
|
21915
22005
|
import fs30 from "fs/promises";
|
|
21916
|
-
import
|
|
22006
|
+
import path33 from "path";
|
|
21917
22007
|
init_constants2();
|
|
21918
22008
|
async function executeStep(step, vaultPath2, context, conditionResults, searchFn) {
|
|
21919
22009
|
const { execute, reason } = shouldStepExecute(step.when, conditionResults);
|
|
@@ -22104,12 +22194,12 @@ async function executeCreateNote2(params, vaultPath2, context) {
|
|
|
22104
22194
|
let frontmatter = params.frontmatter || {};
|
|
22105
22195
|
if (params.template) {
|
|
22106
22196
|
try {
|
|
22107
|
-
const templatePath =
|
|
22197
|
+
const templatePath = path33.join(vaultPath2, String(params.template));
|
|
22108
22198
|
const raw = await fs30.readFile(templatePath, "utf-8");
|
|
22109
22199
|
const matter9 = (await import("gray-matter")).default;
|
|
22110
22200
|
const parsed = matter9(raw);
|
|
22111
22201
|
const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
22112
|
-
const title =
|
|
22202
|
+
const title = path33.basename(notePath, ".md");
|
|
22113
22203
|
let templateContent = parsed.content.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, title);
|
|
22114
22204
|
if (content) {
|
|
22115
22205
|
templateContent = templateContent.trimEnd() + "\n\n" + content;
|
|
@@ -22142,7 +22232,7 @@ async function executeToggleTask(params, vaultPath2) {
|
|
|
22142
22232
|
const notePath = String(params.path || "");
|
|
22143
22233
|
const task = String(params.task || "");
|
|
22144
22234
|
const section = params.section ? String(params.section) : void 0;
|
|
22145
|
-
const fullPath =
|
|
22235
|
+
const fullPath = path33.join(vaultPath2, notePath);
|
|
22146
22236
|
try {
|
|
22147
22237
|
await fs30.access(fullPath);
|
|
22148
22238
|
} catch {
|
|
@@ -22425,7 +22515,7 @@ async function rollbackChanges(vaultPath2, originalContents, filesModified) {
|
|
|
22425
22515
|
const pathCheck = await validatePathSecure(vaultPath2, filePath);
|
|
22426
22516
|
if (!pathCheck.valid) continue;
|
|
22427
22517
|
const original = originalContents.get(filePath);
|
|
22428
|
-
const fullPath =
|
|
22518
|
+
const fullPath = path33.join(vaultPath2, filePath);
|
|
22429
22519
|
if (original === null) {
|
|
22430
22520
|
try {
|
|
22431
22521
|
await fs30.unlink(fullPath);
|
|
@@ -22480,7 +22570,7 @@ async function previewPolicy(policy, vaultPath2, variables) {
|
|
|
22480
22570
|
|
|
22481
22571
|
// src/core/write/policy/storage.ts
|
|
22482
22572
|
import fs31 from "fs/promises";
|
|
22483
|
-
import
|
|
22573
|
+
import path34 from "path";
|
|
22484
22574
|
async function listPolicies(vaultPath2) {
|
|
22485
22575
|
await ensureMigrated(vaultPath2);
|
|
22486
22576
|
const dir = getPoliciesDir(vaultPath2);
|
|
@@ -22491,7 +22581,7 @@ async function listPolicies(vaultPath2) {
|
|
|
22491
22581
|
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) {
|
|
22492
22582
|
continue;
|
|
22493
22583
|
}
|
|
22494
|
-
const filePath =
|
|
22584
|
+
const filePath = path34.join(dir, file);
|
|
22495
22585
|
const stat4 = await fs31.stat(filePath);
|
|
22496
22586
|
const content = await fs31.readFile(filePath, "utf-8");
|
|
22497
22587
|
const metadata = extractPolicyMetadata(content);
|
|
@@ -22517,7 +22607,7 @@ async function writePolicyRaw(vaultPath2, policyName, content, overwrite = false
|
|
|
22517
22607
|
const dir = getPoliciesDir(vaultPath2);
|
|
22518
22608
|
await ensurePoliciesDir(vaultPath2);
|
|
22519
22609
|
const filename = `${policyName}.yaml`;
|
|
22520
|
-
const filePath =
|
|
22610
|
+
const filePath = path34.join(dir, filename);
|
|
22521
22611
|
if (!overwrite) {
|
|
22522
22612
|
try {
|
|
22523
22613
|
await fs31.access(filePath);
|
|
@@ -22631,7 +22721,7 @@ function registerPolicyTools(server2, getVaultPath, getSearchFn) {
|
|
|
22631
22721
|
const policies = await listPolicies(vaultPath2);
|
|
22632
22722
|
const response = {
|
|
22633
22723
|
success: true,
|
|
22634
|
-
vault:
|
|
22724
|
+
vault: path35.basename(vaultPath2),
|
|
22635
22725
|
vault_path: vaultPath2,
|
|
22636
22726
|
count: policies.length,
|
|
22637
22727
|
policies: policies.map((p) => ({
|
|
@@ -23065,7 +23155,7 @@ import { z as z21 } from "zod";
|
|
|
23065
23155
|
|
|
23066
23156
|
// src/core/write/tagRename.ts
|
|
23067
23157
|
import * as fs32 from "fs/promises";
|
|
23068
|
-
import * as
|
|
23158
|
+
import * as path36 from "path";
|
|
23069
23159
|
import matter8 from "gray-matter";
|
|
23070
23160
|
import { getProtectedZones as getProtectedZones2 } from "@velvetmonkey/vault-core";
|
|
23071
23161
|
function getNotesInFolder3(index, folder) {
|
|
@@ -23171,7 +23261,7 @@ async function renameTag(index, vaultPath2, oldTag, newTag, options) {
|
|
|
23171
23261
|
const previews = [];
|
|
23172
23262
|
let totalChanges = 0;
|
|
23173
23263
|
for (const note of affectedNotes) {
|
|
23174
|
-
const fullPath =
|
|
23264
|
+
const fullPath = path36.join(vaultPath2, note.path);
|
|
23175
23265
|
let fileContent;
|
|
23176
23266
|
try {
|
|
23177
23267
|
fileContent = await fs32.readFile(fullPath, "utf-8");
|
|
@@ -24301,7 +24391,7 @@ init_wikilinks();
|
|
|
24301
24391
|
init_wikilinkFeedback();
|
|
24302
24392
|
import { z as z28 } from "zod";
|
|
24303
24393
|
import * as fs33 from "fs/promises";
|
|
24304
|
-
import * as
|
|
24394
|
+
import * as path37 from "path";
|
|
24305
24395
|
import { scanVaultEntities as scanVaultEntities4, SCHEMA_VERSION as SCHEMA_VERSION2 } from "@velvetmonkey/vault-core";
|
|
24306
24396
|
init_embeddings();
|
|
24307
24397
|
function hasSkipWikilinks(content) {
|
|
@@ -24317,13 +24407,13 @@ async function collectMarkdownFiles(dirPath, basePath, excludeFolders) {
|
|
|
24317
24407
|
const entries = await fs33.readdir(dirPath, { withFileTypes: true });
|
|
24318
24408
|
for (const entry of entries) {
|
|
24319
24409
|
if (entry.name.startsWith(".")) continue;
|
|
24320
|
-
const fullPath =
|
|
24410
|
+
const fullPath = path37.join(dirPath, entry.name);
|
|
24321
24411
|
if (entry.isDirectory()) {
|
|
24322
24412
|
if (excludeFolders.some((f) => entry.name.toLowerCase() === f.toLowerCase())) continue;
|
|
24323
24413
|
const sub = await collectMarkdownFiles(fullPath, basePath, excludeFolders);
|
|
24324
24414
|
results.push(...sub);
|
|
24325
24415
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
24326
|
-
results.push(
|
|
24416
|
+
results.push(path37.relative(basePath, fullPath));
|
|
24327
24417
|
}
|
|
24328
24418
|
}
|
|
24329
24419
|
} catch {
|
|
@@ -24353,7 +24443,7 @@ var EXCLUDE_FOLDERS = [
|
|
|
24353
24443
|
];
|
|
24354
24444
|
function buildStatusReport(stateDb2, vaultPath2) {
|
|
24355
24445
|
const recommendations = [];
|
|
24356
|
-
const dbPath =
|
|
24446
|
+
const dbPath = path37.join(vaultPath2, ".flywheel", "state.db");
|
|
24357
24447
|
const statedbExists = stateDb2 !== null;
|
|
24358
24448
|
if (!statedbExists) {
|
|
24359
24449
|
recommendations.push("StateDb not initialized \u2014 server needs restart");
|
|
@@ -24480,7 +24570,7 @@ async function executeRun(stateDb2, vaultPath2) {
|
|
|
24480
24570
|
const allFiles = await collectMarkdownFiles(vaultPath2, vaultPath2, EXCLUDE_FOLDERS);
|
|
24481
24571
|
let eligible = 0;
|
|
24482
24572
|
for (const relativePath of allFiles) {
|
|
24483
|
-
const fullPath =
|
|
24573
|
+
const fullPath = path37.join(vaultPath2, relativePath);
|
|
24484
24574
|
let content;
|
|
24485
24575
|
try {
|
|
24486
24576
|
content = await fs33.readFile(fullPath, "utf-8");
|
|
@@ -24538,7 +24628,7 @@ async function executeEnrich(stateDb2, vaultPath2, dryRun, batchSize, offset) {
|
|
|
24538
24628
|
const eligible = [];
|
|
24539
24629
|
let notesSkipped = 0;
|
|
24540
24630
|
for (const relativePath of allFiles) {
|
|
24541
|
-
const fullPath =
|
|
24631
|
+
const fullPath = path37.join(vaultPath2, relativePath);
|
|
24542
24632
|
let content;
|
|
24543
24633
|
try {
|
|
24544
24634
|
content = await fs33.readFile(fullPath, "utf-8");
|
|
@@ -24568,7 +24658,7 @@ async function executeEnrich(stateDb2, vaultPath2, dryRun, batchSize, offset) {
|
|
|
24568
24658
|
match_count: result.linksAdded
|
|
24569
24659
|
});
|
|
24570
24660
|
if (!dryRun) {
|
|
24571
|
-
const fullPath =
|
|
24661
|
+
const fullPath = path37.join(vaultPath2, relativePath);
|
|
24572
24662
|
await fs33.writeFile(fullPath, result.content, "utf-8");
|
|
24573
24663
|
notesModified++;
|
|
24574
24664
|
if (stateDb2) {
|
|
@@ -24747,7 +24837,7 @@ import { z as z30 } from "zod";
|
|
|
24747
24837
|
|
|
24748
24838
|
// src/core/read/similarity.ts
|
|
24749
24839
|
import * as fs34 from "fs";
|
|
24750
|
-
import * as
|
|
24840
|
+
import * as path38 from "path";
|
|
24751
24841
|
init_embeddings();
|
|
24752
24842
|
|
|
24753
24843
|
// src/core/read/mmr.ts
|
|
@@ -24817,7 +24907,7 @@ function extractKeyTerms(content, maxTerms = 15) {
|
|
|
24817
24907
|
}
|
|
24818
24908
|
function findSimilarNotes(db4, vaultPath2, index, sourcePath, options = {}) {
|
|
24819
24909
|
const limit = options.limit ?? 10;
|
|
24820
|
-
const absPath =
|
|
24910
|
+
const absPath = path38.join(vaultPath2, sourcePath);
|
|
24821
24911
|
let content;
|
|
24822
24912
|
try {
|
|
24823
24913
|
content = fs34.readFileSync(absPath, "utf-8");
|
|
@@ -24959,7 +25049,7 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
24959
25049
|
diversity: z30.number().min(0).max(1).optional().describe("Relevance vs diversity tradeoff (0=max diversity, 1=pure relevance, default: 0.7)")
|
|
24960
25050
|
}
|
|
24961
25051
|
},
|
|
24962
|
-
async ({ path:
|
|
25052
|
+
async ({ path: path41, limit, diversity }) => {
|
|
24963
25053
|
const index = getIndex();
|
|
24964
25054
|
const vaultPath2 = getVaultPath();
|
|
24965
25055
|
const stateDb2 = getStateDb4();
|
|
@@ -24968,10 +25058,10 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
24968
25058
|
content: [{ type: "text", text: JSON.stringify({ error: "StateDb not available" }) }]
|
|
24969
25059
|
};
|
|
24970
25060
|
}
|
|
24971
|
-
if (!index.notes.has(
|
|
25061
|
+
if (!index.notes.has(path41)) {
|
|
24972
25062
|
return {
|
|
24973
25063
|
content: [{ type: "text", text: JSON.stringify({
|
|
24974
|
-
error: `Note not found: ${
|
|
25064
|
+
error: `Note not found: ${path41}`,
|
|
24975
25065
|
hint: "Use the full relative path including .md extension"
|
|
24976
25066
|
}, null, 2) }]
|
|
24977
25067
|
};
|
|
@@ -24983,12 +25073,12 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
24983
25073
|
};
|
|
24984
25074
|
const useHybrid = hasEmbeddingsIndex();
|
|
24985
25075
|
const method = useHybrid ? "hybrid" : "bm25";
|
|
24986
|
-
const results = useHybrid ? await findHybridSimilarNotes(stateDb2.db, vaultPath2, index,
|
|
25076
|
+
const results = useHybrid ? await findHybridSimilarNotes(stateDb2.db, vaultPath2, index, path41, opts) : findSimilarNotes(stateDb2.db, vaultPath2, index, path41, opts);
|
|
24987
25077
|
return {
|
|
24988
25078
|
content: [{
|
|
24989
25079
|
type: "text",
|
|
24990
25080
|
text: JSON.stringify({
|
|
24991
|
-
source:
|
|
25081
|
+
source: path41,
|
|
24992
25082
|
method,
|
|
24993
25083
|
count: results.length,
|
|
24994
25084
|
similar: results
|
|
@@ -26862,9 +26952,9 @@ function registerVaultResources(server2, getIndex) {
|
|
|
26862
26952
|
}
|
|
26863
26953
|
|
|
26864
26954
|
// src/tool-registry.ts
|
|
26865
|
-
var __trFilename =
|
|
26866
|
-
var __trDirname =
|
|
26867
|
-
var trPkg = JSON.parse(readFileSync5(
|
|
26955
|
+
var __trFilename = fileURLToPath3(import.meta.url);
|
|
26956
|
+
var __trDirname = dirname6(__trFilename);
|
|
26957
|
+
var trPkg = JSON.parse(readFileSync5(join19(__trDirname, "../package.json"), "utf-8"));
|
|
26868
26958
|
var ACTIVATION_PATTERNS = [
|
|
26869
26959
|
{
|
|
26870
26960
|
category: "graph",
|
|
@@ -26902,6 +26992,34 @@ var ACTIVATION_PATTERNS = [
|
|
|
26902
26992
|
patterns: [/\b(delete note|move note|rename note|merge entities|merge notes?)\b/i]
|
|
26903
26993
|
}
|
|
26904
26994
|
];
|
|
26995
|
+
var MUTATING_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
26996
|
+
"vault_add_to_section",
|
|
26997
|
+
"vault_remove_from_section",
|
|
26998
|
+
"vault_replace_in_section",
|
|
26999
|
+
"vault_add_task",
|
|
27000
|
+
"vault_toggle_task",
|
|
27001
|
+
"vault_update_frontmatter",
|
|
27002
|
+
"vault_create_note",
|
|
27003
|
+
"vault_delete_note",
|
|
27004
|
+
"vault_move_note",
|
|
27005
|
+
"vault_rename_note",
|
|
27006
|
+
"merge_entities",
|
|
27007
|
+
"absorb_as_alias",
|
|
27008
|
+
"vault_undo_last_mutation",
|
|
27009
|
+
"policy",
|
|
27010
|
+
"rename_tag",
|
|
27011
|
+
"wikilink_feedback",
|
|
27012
|
+
"tool_selection_feedback",
|
|
27013
|
+
"vault_record_correction",
|
|
27014
|
+
"vault_resolve_correction",
|
|
27015
|
+
"memory",
|
|
27016
|
+
"flywheel_config",
|
|
27017
|
+
"vault_init",
|
|
27018
|
+
"rename_field",
|
|
27019
|
+
"migrate_field_values",
|
|
27020
|
+
"refresh_index",
|
|
27021
|
+
"init_semantic"
|
|
27022
|
+
]);
|
|
26905
27023
|
function getPatternSignals(raw) {
|
|
26906
27024
|
if (!raw) return [];
|
|
26907
27025
|
return ACTIVATION_PATTERNS.filter(({ patterns }) => patterns.some((pattern) => pattern.test(raw))).map(({ category, tier }) => ({ category, tier }));
|
|
@@ -27094,7 +27212,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
27094
27212
|
let totalBytes = 0;
|
|
27095
27213
|
for (const p of notePaths) {
|
|
27096
27214
|
try {
|
|
27097
|
-
totalBytes += statSync6(
|
|
27215
|
+
totalBytes += statSync6(path39.join(vp, p)).size;
|
|
27098
27216
|
} catch {
|
|
27099
27217
|
}
|
|
27100
27218
|
}
|
|
@@ -27127,6 +27245,26 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
27127
27245
|
};
|
|
27128
27246
|
}
|
|
27129
27247
|
const isMultiVault = registry?.isMultiVault ?? false;
|
|
27248
|
+
function getTargetVaultContext(params) {
|
|
27249
|
+
if (!registry) return null;
|
|
27250
|
+
if (isMultiVault) {
|
|
27251
|
+
const vaultName = typeof params?.vault === "string" ? params.vault : void 0;
|
|
27252
|
+
return registry.getContext(vaultName);
|
|
27253
|
+
}
|
|
27254
|
+
return registry.getContext();
|
|
27255
|
+
}
|
|
27256
|
+
function wrapWithIntegrityGate(toolName, handler) {
|
|
27257
|
+
if (!MUTATING_TOOL_NAMES.has(toolName)) return handler;
|
|
27258
|
+
return async (...args) => {
|
|
27259
|
+
const params = args[0] && typeof args[0] === "object" ? args[0] : void 0;
|
|
27260
|
+
const vaultCtx = getTargetVaultContext(params);
|
|
27261
|
+
const integrityState = vaultCtx?.integrityState ?? getActiveScopeOrNull()?.integrityState;
|
|
27262
|
+
if (integrityState === "failed") {
|
|
27263
|
+
throw new Error("StateDb integrity failed; write operations are disabled until recovery/restart.");
|
|
27264
|
+
}
|
|
27265
|
+
return handler(...args);
|
|
27266
|
+
};
|
|
27267
|
+
}
|
|
27130
27268
|
function wrapWithVaultActivation(toolName, handler) {
|
|
27131
27269
|
if (!isMultiVault || !registry || !vaultCallbacks) return handler;
|
|
27132
27270
|
return async (...args) => {
|
|
@@ -27242,6 +27380,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
27242
27380
|
if (args.length > 0 && typeof args[args.length - 1] === "function") {
|
|
27243
27381
|
let handler = args[args.length - 1];
|
|
27244
27382
|
handler = wrapWithVaultActivation(name, handler);
|
|
27383
|
+
handler = wrapWithIntegrityGate(name, handler);
|
|
27245
27384
|
args[args.length - 1] = wrapWithTracking(name, handler);
|
|
27246
27385
|
}
|
|
27247
27386
|
const registered = origTool(name, ...args);
|
|
@@ -27256,6 +27395,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
27256
27395
|
if (args.length > 0 && typeof args[args.length - 1] === "function") {
|
|
27257
27396
|
let handler = args[args.length - 1];
|
|
27258
27397
|
handler = wrapWithVaultActivation(name, handler);
|
|
27398
|
+
handler = wrapWithIntegrityGate(name, handler);
|
|
27259
27399
|
args[args.length - 1] = wrapWithTracking(name, handler);
|
|
27260
27400
|
}
|
|
27261
27401
|
const registered = origRegisterTool(name, ...args);
|
|
@@ -27349,7 +27489,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
27349
27489
|
}
|
|
27350
27490
|
function registerAllTools(targetServer, ctx, controller) {
|
|
27351
27491
|
const { getVaultPath: gvp, getVaultIndex: gvi, getStateDb: gsd, getFlywheelConfig: gcf } = ctx;
|
|
27352
|
-
registerHealthTools(targetServer, gvi, gvp, gcf, gsd, ctx.getWatcherStatus, () => trPkg.version, ctx.getPipelineActivity);
|
|
27492
|
+
registerHealthTools(targetServer, gvi, gvp, gcf, gsd, ctx.getWatcherStatus, () => trPkg.version, ctx.getPipelineActivity, ctx.getVaultRuntimeState);
|
|
27353
27493
|
registerSystemTools(
|
|
27354
27494
|
targetServer,
|
|
27355
27495
|
gvi,
|
|
@@ -27445,9 +27585,9 @@ function registerAllTools(targetServer, ctx, controller) {
|
|
|
27445
27585
|
}
|
|
27446
27586
|
|
|
27447
27587
|
// src/index.ts
|
|
27448
|
-
var __filename2 =
|
|
27449
|
-
var __dirname =
|
|
27450
|
-
var pkg = JSON.parse(readFileSync6(
|
|
27588
|
+
var __filename2 = fileURLToPath4(import.meta.url);
|
|
27589
|
+
var __dirname = dirname8(__filename2);
|
|
27590
|
+
var pkg = JSON.parse(readFileSync6(join21(__dirname, "../package.json"), "utf-8"));
|
|
27451
27591
|
var vaultPath;
|
|
27452
27592
|
var resolvedVaultPath;
|
|
27453
27593
|
var vaultIndex;
|
|
@@ -27463,6 +27603,7 @@ var lastMcpRequestAt = 0;
|
|
|
27463
27603
|
var lastFullRebuildAt = 0;
|
|
27464
27604
|
var startupScanFiles = null;
|
|
27465
27605
|
var deferredScheduler = null;
|
|
27606
|
+
var integrityRuns = /* @__PURE__ */ new Map();
|
|
27466
27607
|
function getWatcherStatus() {
|
|
27467
27608
|
if (vaultRegistry) {
|
|
27468
27609
|
const name = globalThis.__flywheel_active_vault;
|
|
@@ -27507,6 +27648,20 @@ function buildRegistryContext() {
|
|
|
27507
27648
|
getFlywheelConfig: () => getActiveScopeOrNull()?.flywheelConfig ?? flywheelConfig,
|
|
27508
27649
|
getWatcherStatus,
|
|
27509
27650
|
getPipelineActivity: () => getActiveScopeOrNull()?.pipelineActivity ?? null,
|
|
27651
|
+
getVaultRuntimeState: () => {
|
|
27652
|
+
const scope = getActiveScopeOrNull();
|
|
27653
|
+
return {
|
|
27654
|
+
bootState: scope?.bootState ?? "booting",
|
|
27655
|
+
integrityState: scope?.integrityState ?? "unknown",
|
|
27656
|
+
integrityCheckInProgress: scope?.integrityCheckInProgress ?? false,
|
|
27657
|
+
integrityStartedAt: scope?.integrityStartedAt ?? null,
|
|
27658
|
+
integritySource: scope?.integritySource ?? null,
|
|
27659
|
+
lastIntegrityCheckedAt: scope?.lastIntegrityCheckedAt ?? null,
|
|
27660
|
+
lastIntegrityDurationMs: scope?.lastIntegrityDurationMs ?? null,
|
|
27661
|
+
lastIntegrityDetail: scope?.lastIntegrityDetail ?? null,
|
|
27662
|
+
lastBackupAt: scope?.lastBackupAt ?? null
|
|
27663
|
+
};
|
|
27664
|
+
},
|
|
27510
27665
|
updateVaultIndex,
|
|
27511
27666
|
updateFlywheelConfig
|
|
27512
27667
|
};
|
|
@@ -27608,6 +27763,110 @@ function loadVaultCooccurrence(ctx) {
|
|
|
27608
27763
|
serverLog("index", `[${ctx.name}] Co-occurrence: loaded from cache (${Object.keys(cachedCooc.index.associations).length} entities, ${cachedCooc.index._metadata.total_associations} associations)`);
|
|
27609
27764
|
}
|
|
27610
27765
|
}
|
|
27766
|
+
function hydrateIntegrityMetadata(ctx) {
|
|
27767
|
+
if (!ctx.stateDb) return;
|
|
27768
|
+
const checkedAtRow = ctx.stateDb.getMetadataValue.get(INTEGRITY_METADATA_KEYS.checkedAt);
|
|
27769
|
+
const statusRow = ctx.stateDb.getMetadataValue.get(INTEGRITY_METADATA_KEYS.status);
|
|
27770
|
+
const durationRow = ctx.stateDb.getMetadataValue.get(INTEGRITY_METADATA_KEYS.durationMs);
|
|
27771
|
+
const detailRow = ctx.stateDb.getMetadataValue.get(INTEGRITY_METADATA_KEYS.detail);
|
|
27772
|
+
ctx.lastIntegrityCheckedAt = checkedAtRow ? parseInt(checkedAtRow.value, 10) || null : null;
|
|
27773
|
+
ctx.lastIntegrityDurationMs = durationRow ? parseInt(durationRow.value, 10) || null : null;
|
|
27774
|
+
ctx.lastIntegrityDetail = detailRow?.value ? detailRow.value : null;
|
|
27775
|
+
const status = statusRow?.value;
|
|
27776
|
+
if (status === "healthy" || status === "failed" || status === "error") {
|
|
27777
|
+
ctx.integrityState = status;
|
|
27778
|
+
}
|
|
27779
|
+
}
|
|
27780
|
+
function setBootState(ctx, state2) {
|
|
27781
|
+
ctx.bootState = state2;
|
|
27782
|
+
if (globalThis.__flywheel_active_vault === ctx.name) {
|
|
27783
|
+
setActiveScope(buildVaultScope(ctx));
|
|
27784
|
+
}
|
|
27785
|
+
}
|
|
27786
|
+
function setIntegrityState(ctx, state2, detail = ctx.lastIntegrityDetail, durationMs = ctx.lastIntegrityDurationMs) {
|
|
27787
|
+
ctx.integrityState = state2;
|
|
27788
|
+
ctx.lastIntegrityDetail = detail;
|
|
27789
|
+
ctx.lastIntegrityDurationMs = durationMs;
|
|
27790
|
+
if (state2 === "failed") {
|
|
27791
|
+
ctx.bootState = "degraded";
|
|
27792
|
+
}
|
|
27793
|
+
if (globalThis.__flywheel_active_vault === ctx.name) {
|
|
27794
|
+
setActiveScope(buildVaultScope(ctx));
|
|
27795
|
+
}
|
|
27796
|
+
}
|
|
27797
|
+
function persistIntegrityMetadata(ctx) {
|
|
27798
|
+
if (!ctx.stateDb || ctx.lastIntegrityCheckedAt == null) return;
|
|
27799
|
+
ctx.stateDb.setMetadataValue.run(INTEGRITY_METADATA_KEYS.checkedAt, String(ctx.lastIntegrityCheckedAt));
|
|
27800
|
+
ctx.stateDb.setMetadataValue.run(INTEGRITY_METADATA_KEYS.status, ctx.integrityState);
|
|
27801
|
+
if (ctx.lastIntegrityDurationMs != null) {
|
|
27802
|
+
ctx.stateDb.setMetadataValue.run(INTEGRITY_METADATA_KEYS.durationMs, String(ctx.lastIntegrityDurationMs));
|
|
27803
|
+
}
|
|
27804
|
+
if (ctx.lastIntegrityDetail) {
|
|
27805
|
+
ctx.stateDb.setMetadataValue.run(INTEGRITY_METADATA_KEYS.detail, ctx.lastIntegrityDetail);
|
|
27806
|
+
} else {
|
|
27807
|
+
ctx.stateDb.setMetadataValue.run(INTEGRITY_METADATA_KEYS.detail, "");
|
|
27808
|
+
}
|
|
27809
|
+
}
|
|
27810
|
+
function shouldRunBackup(ctx) {
|
|
27811
|
+
if (ctx.lastBackupAt == null) return true;
|
|
27812
|
+
return Date.now() - ctx.lastBackupAt >= INTEGRITY_BACKUP_INTERVAL_MS;
|
|
27813
|
+
}
|
|
27814
|
+
async function runIntegrityCheck(ctx, source, options = {}) {
|
|
27815
|
+
if (!ctx.stateDb) {
|
|
27816
|
+
return { status: "error", detail: "StateDb not available", durationMs: 0, backupCreated: false };
|
|
27817
|
+
}
|
|
27818
|
+
if (!options.force && ctx.integrityState === "healthy" && ctx.lastIntegrityCheckedAt != null) {
|
|
27819
|
+
if (Date.now() - ctx.lastIntegrityCheckedAt < INTEGRITY_CHECK_INTERVAL_MS) {
|
|
27820
|
+
return {
|
|
27821
|
+
status: "healthy",
|
|
27822
|
+
detail: ctx.lastIntegrityDetail,
|
|
27823
|
+
durationMs: ctx.lastIntegrityDurationMs ?? 0,
|
|
27824
|
+
backupCreated: false
|
|
27825
|
+
};
|
|
27826
|
+
}
|
|
27827
|
+
}
|
|
27828
|
+
const existing = integrityRuns.get(ctx.name);
|
|
27829
|
+
if (existing) return existing;
|
|
27830
|
+
ctx.integrityCheckInProgress = true;
|
|
27831
|
+
ctx.integrityStartedAt = Date.now();
|
|
27832
|
+
ctx.integritySource = source;
|
|
27833
|
+
setIntegrityState(ctx, "checking", ctx.lastIntegrityDetail, ctx.lastIntegrityDurationMs);
|
|
27834
|
+
serverLog("statedb", `[${ctx.name}] Integrity check started (${source})`);
|
|
27835
|
+
const promise = runIntegrityWorker({
|
|
27836
|
+
dbPath: ctx.stateDb.dbPath,
|
|
27837
|
+
runBackup: shouldRunBackup(ctx),
|
|
27838
|
+
busyTimeoutMs: 5e3
|
|
27839
|
+
}).then((result) => {
|
|
27840
|
+
ctx.integrityCheckInProgress = false;
|
|
27841
|
+
ctx.integrityStartedAt = null;
|
|
27842
|
+
ctx.integritySource = source;
|
|
27843
|
+
ctx.lastIntegrityCheckedAt = Date.now();
|
|
27844
|
+
ctx.lastIntegrityDurationMs = result.durationMs;
|
|
27845
|
+
ctx.lastIntegrityDetail = result.detail;
|
|
27846
|
+
if (result.backupCreated) {
|
|
27847
|
+
ctx.lastBackupAt = Date.now();
|
|
27848
|
+
}
|
|
27849
|
+
if (result.status === "healthy") {
|
|
27850
|
+
setIntegrityState(ctx, "healthy", result.detail, result.durationMs);
|
|
27851
|
+
serverLog("statedb", `[${ctx.name}] Integrity check passed in ${result.durationMs}ms`);
|
|
27852
|
+
} else if (result.status === "failed") {
|
|
27853
|
+
setIntegrityState(ctx, "failed", result.detail, result.durationMs);
|
|
27854
|
+
serverLog("statedb", `[${ctx.name}] Integrity check failed: ${result.detail}`, "error");
|
|
27855
|
+
} else {
|
|
27856
|
+
setIntegrityState(ctx, "error", result.detail, result.durationMs);
|
|
27857
|
+
serverLog("statedb", `[${ctx.name}] Integrity check error: ${result.detail}`, "warn");
|
|
27858
|
+
}
|
|
27859
|
+
persistIntegrityMetadata(ctx);
|
|
27860
|
+
return result;
|
|
27861
|
+
}).finally(() => {
|
|
27862
|
+
integrityRuns.delete(ctx.name);
|
|
27863
|
+
if (globalThis.__flywheel_active_vault === ctx.name) {
|
|
27864
|
+
setActiveScope(buildVaultScope(ctx));
|
|
27865
|
+
}
|
|
27866
|
+
});
|
|
27867
|
+
integrityRuns.set(ctx.name, promise);
|
|
27868
|
+
return promise;
|
|
27869
|
+
}
|
|
27611
27870
|
async function initializeVault(name, vaultPathArg) {
|
|
27612
27871
|
const ctx = {
|
|
27613
27872
|
name,
|
|
@@ -27625,11 +27884,21 @@ async function initializeVault(name, vaultPathArg) {
|
|
|
27625
27884
|
lastEntityScanAt: 0,
|
|
27626
27885
|
lastHubScoreRebuildAt: 0,
|
|
27627
27886
|
lastIndexCacheSaveAt: 0,
|
|
27628
|
-
pipelineActivity: createEmptyPipelineActivity()
|
|
27887
|
+
pipelineActivity: createEmptyPipelineActivity(),
|
|
27888
|
+
bootState: "booting",
|
|
27889
|
+
integrityState: "unknown",
|
|
27890
|
+
integrityCheckInProgress: false,
|
|
27891
|
+
integrityStartedAt: null,
|
|
27892
|
+
integritySource: null,
|
|
27893
|
+
lastIntegrityCheckedAt: null,
|
|
27894
|
+
lastIntegrityDurationMs: null,
|
|
27895
|
+
lastIntegrityDetail: null,
|
|
27896
|
+
lastBackupAt: null
|
|
27629
27897
|
};
|
|
27630
27898
|
try {
|
|
27631
27899
|
ctx.stateDb = openStateDb(vaultPathArg);
|
|
27632
27900
|
serverLog("statedb", `[${name}] StateDb initialized`);
|
|
27901
|
+
hydrateIntegrityMetadata(ctx);
|
|
27633
27902
|
const vaultInitRow = ctx.stateDb.getMetadataValue.get("vault_init_last_run_at");
|
|
27634
27903
|
if (!vaultInitRow) {
|
|
27635
27904
|
serverLog("server", `[${name}] Vault not initialized \u2014 call vault_init to enrich legacy notes`);
|
|
@@ -27653,7 +27922,16 @@ function buildVaultScope(ctx) {
|
|
|
27653
27922
|
indexError: ctx.indexError,
|
|
27654
27923
|
embeddingsBuilding: ctx.embeddingsBuilding,
|
|
27655
27924
|
entityEmbeddingsMap: getEntityEmbeddingsMap(),
|
|
27656
|
-
pipelineActivity: ctx.pipelineActivity
|
|
27925
|
+
pipelineActivity: ctx.pipelineActivity,
|
|
27926
|
+
bootState: ctx.bootState,
|
|
27927
|
+
integrityState: ctx.integrityState,
|
|
27928
|
+
integrityCheckInProgress: ctx.integrityCheckInProgress,
|
|
27929
|
+
integrityStartedAt: ctx.integrityStartedAt,
|
|
27930
|
+
integritySource: ctx.integritySource,
|
|
27931
|
+
lastIntegrityCheckedAt: ctx.lastIntegrityCheckedAt,
|
|
27932
|
+
lastIntegrityDurationMs: ctx.lastIntegrityDurationMs,
|
|
27933
|
+
lastIntegrityDetail: ctx.lastIntegrityDetail,
|
|
27934
|
+
lastBackupAt: ctx.lastBackupAt
|
|
27657
27935
|
};
|
|
27658
27936
|
}
|
|
27659
27937
|
function activateVault(ctx, skipEmbeddingLoad = false) {
|
|
@@ -27807,6 +28085,9 @@ async function bootVault(ctx, startTime) {
|
|
|
27807
28085
|
serverLog("index", `[${ctx.name}] Failed to build vault index: ${err instanceof Error ? err.message : err}`, "error");
|
|
27808
28086
|
}
|
|
27809
28087
|
}
|
|
28088
|
+
if (ctx.bootState !== "degraded") {
|
|
28089
|
+
setBootState(ctx, "ready");
|
|
28090
|
+
}
|
|
27810
28091
|
}
|
|
27811
28092
|
async function main() {
|
|
27812
28093
|
const vaultConfigs = parseVaultConfig();
|
|
@@ -27816,7 +28097,7 @@ async function main() {
|
|
|
27816
28097
|
} catch {
|
|
27817
28098
|
resolvedVaultPath = vaultPath.replace(/\\/g, "/");
|
|
27818
28099
|
}
|
|
27819
|
-
if (!
|
|
28100
|
+
if (!existsSync4(resolvedVaultPath)) {
|
|
27820
28101
|
console.error(`[flywheel] Fatal: vault path does not exist: ${resolvedVaultPath}`);
|
|
27821
28102
|
console.error(`[flywheel] Set PROJECT_PATH or VAULT_PATH to a valid Obsidian vault directory.`);
|
|
27822
28103
|
process.exit(1);
|
|
@@ -27831,14 +28112,17 @@ async function main() {
|
|
|
27831
28112
|
vaultRegistry.addContext(primaryCtx2);
|
|
27832
28113
|
stateDb = primaryCtx2.stateDb;
|
|
27833
28114
|
activateVault(primaryCtx2, true);
|
|
28115
|
+
serverLog("server", `[${primaryCtx2.name}] stateDb_open=${Date.now() - startTime}ms`);
|
|
27834
28116
|
} else {
|
|
27835
28117
|
vaultRegistry = new VaultRegistry("default");
|
|
27836
28118
|
const ctx = await initializeVault("default", vaultPath);
|
|
27837
28119
|
vaultRegistry.addContext(ctx);
|
|
27838
28120
|
stateDb = ctx.stateDb;
|
|
27839
28121
|
activateVault(ctx, true);
|
|
28122
|
+
serverLog("server", `[${ctx.name}] stateDb_open=${Date.now() - startTime}ms`);
|
|
27840
28123
|
}
|
|
27841
28124
|
await initToolRouting();
|
|
28125
|
+
serverLog("server", `tool_routing=${Date.now() - startTime}ms`);
|
|
27842
28126
|
if (stateDb) {
|
|
27843
28127
|
try {
|
|
27844
28128
|
const vaultName = vaultRegistry?.primaryName ?? "default";
|
|
@@ -27930,29 +28214,16 @@ async function main() {
|
|
|
27930
28214
|
});
|
|
27931
28215
|
}
|
|
27932
28216
|
const primaryCtx = vaultRegistry.getContext();
|
|
27933
|
-
|
|
27934
|
-
|
|
27935
|
-
|
|
27936
|
-
|
|
27937
|
-
|
|
27938
|
-
});
|
|
27939
|
-
} else {
|
|
27940
|
-
serverLog("statedb", `[${primaryCtx.name}] Integrity check failed: ${integrity.detail} \u2014 recreating`, "error");
|
|
27941
|
-
const dbPath = primaryCtx.stateDb.dbPath;
|
|
27942
|
-
preserveCorruptedDb(dbPath);
|
|
27943
|
-
primaryCtx.stateDb.close();
|
|
27944
|
-
deleteStateDbFiles(dbPath);
|
|
27945
|
-
primaryCtx.stateDb = openStateDb(primaryCtx.vaultPath);
|
|
27946
|
-
attemptSalvage(primaryCtx.stateDb.db, dbPath);
|
|
27947
|
-
stateDb = primaryCtx.stateDb;
|
|
27948
|
-
activateVault(primaryCtx, true);
|
|
27949
|
-
}
|
|
27950
|
-
serverLog("statedb", `[${primaryCtx.name}] Integrity check passed in ${Date.now() - startTime}ms`);
|
|
27951
|
-
}
|
|
28217
|
+
setBootState(primaryCtx, "transport_connected");
|
|
28218
|
+
serverLog("server", `[${primaryCtx.name}] transport_connect=${Date.now() - startTime}ms`);
|
|
28219
|
+
serverLog("server", `[${primaryCtx.name}] integrity_check_started=${Date.now() - startTime}ms`);
|
|
28220
|
+
void runIntegrityCheck(primaryCtx, "startup");
|
|
28221
|
+
setBootState(primaryCtx, "booting");
|
|
27952
28222
|
loadVaultCooccurrence(primaryCtx);
|
|
27953
28223
|
activateVault(primaryCtx);
|
|
27954
28224
|
await bootVault(primaryCtx, startTime);
|
|
27955
28225
|
activateVault(primaryCtx);
|
|
28226
|
+
serverLog("server", `[${primaryCtx.name}] boot_complete=${Date.now() - startTime}ms`);
|
|
27956
28227
|
serverReady = true;
|
|
27957
28228
|
const watchdogInterval = parseInt(process.env.FLYWHEEL_WATCHDOG_INTERVAL ?? "0", 10);
|
|
27958
28229
|
if (watchdogInterval > 0 && (transportMode === "http" || transportMode === "both")) {
|
|
@@ -28014,6 +28285,9 @@ async function main() {
|
|
|
28014
28285
|
const ctx = await initializeVault(vc.name, vc.path);
|
|
28015
28286
|
vaultRegistry.addContext(ctx);
|
|
28016
28287
|
invalidateHttpPool();
|
|
28288
|
+
setBootState(ctx, "transport_connected");
|
|
28289
|
+
void runIntegrityCheck(ctx, "startup");
|
|
28290
|
+
setBootState(ctx, "booting");
|
|
28017
28291
|
loadVaultCooccurrence(ctx);
|
|
28018
28292
|
activateVault(ctx);
|
|
28019
28293
|
await bootVault(ctx, startTime);
|
|
@@ -28229,7 +28503,7 @@ async function runPostIndexWork(ctx) {
|
|
|
28229
28503
|
if (attempt < MAX_BUILD_RETRIES) {
|
|
28230
28504
|
const delay = 1e4;
|
|
28231
28505
|
serverLog("semantic", `Build failed (attempt ${attempt}/${MAX_BUILD_RETRIES}): ${msg}. Retrying in ${delay / 1e3}s...`, "error");
|
|
28232
|
-
await new Promise((
|
|
28506
|
+
await new Promise((resolve4) => setTimeout(resolve4, delay));
|
|
28233
28507
|
return attemptBuild(attempt + 1);
|
|
28234
28508
|
}
|
|
28235
28509
|
serverLog("semantic", `Embeddings build failed after ${MAX_BUILD_RETRIES} attempts: ${msg}`, "error");
|
|
@@ -28285,8 +28559,8 @@ async function runPostIndexWork(ctx) {
|
|
|
28285
28559
|
}
|
|
28286
28560
|
} catch {
|
|
28287
28561
|
try {
|
|
28288
|
-
const dir =
|
|
28289
|
-
const base =
|
|
28562
|
+
const dir = path40.dirname(rawPath);
|
|
28563
|
+
const base = path40.basename(rawPath);
|
|
28290
28564
|
const resolvedDir = realpathSync(dir).replace(/\\/g, "/");
|
|
28291
28565
|
for (const prefix of vaultPrefixes) {
|
|
28292
28566
|
if (resolvedDir.startsWith(prefix + "/") || resolvedDir === prefix) {
|
|
@@ -28318,7 +28592,7 @@ async function runPostIndexWork(ctx) {
|
|
|
28318
28592
|
continue;
|
|
28319
28593
|
}
|
|
28320
28594
|
try {
|
|
28321
|
-
const content = await fs35.readFile(
|
|
28595
|
+
const content = await fs35.readFile(path40.join(vp, event.path), "utf-8");
|
|
28322
28596
|
const hash = createHash4("sha256").update(content).digest("hex").slice(0, 16);
|
|
28323
28597
|
if (lastContentHashes.get(event.path) === hash) {
|
|
28324
28598
|
serverLog("watcher", `Hash unchanged, skipping: ${event.path}`);
|
|
@@ -28405,7 +28679,8 @@ async function runPostIndexWork(ctx) {
|
|
|
28405
28679
|
updateEntitiesInStateDb,
|
|
28406
28680
|
getVaultIndex: () => vaultIndex,
|
|
28407
28681
|
buildVaultIndex,
|
|
28408
|
-
deferredScheduler: deferredScheduler ?? void 0
|
|
28682
|
+
deferredScheduler: deferredScheduler ?? void 0,
|
|
28683
|
+
runIntegrityCheck
|
|
28409
28684
|
});
|
|
28410
28685
|
await runner.run();
|
|
28411
28686
|
};
|