@ssmeo/mcp-local-rag 0.18.6
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/LICENSE +21 -0
- package/README.de.md +441 -0
- package/README.es.md +441 -0
- package/README.fr.md +441 -0
- package/README.md +530 -0
- package/README.pt-BR.md +441 -0
- package/README.zh-CN.md +441 -0
- package/dist/bin/install-skills.d.ts +19 -0
- package/dist/bin/install-skills.d.ts.map +1 -0
- package/dist/bin/install-skills.js +218 -0
- package/dist/bin/install-skills.js.map +1 -0
- package/dist/chunker/index.d.ts +24 -0
- package/dist/chunker/index.d.ts.map +1 -0
- package/dist/chunker/index.js +2 -0
- package/dist/chunker/index.js.map +1 -0
- package/dist/chunker/semantic-chunker.d.ts +96 -0
- package/dist/chunker/semantic-chunker.d.ts.map +1 -0
- package/dist/chunker/semantic-chunker.js +362 -0
- package/dist/chunker/semantic-chunker.js.map +1 -0
- package/dist/chunker/sentence-splitter.d.ts +32 -0
- package/dist/chunker/sentence-splitter.d.ts.map +1 -0
- package/dist/chunker/sentence-splitter.js +231 -0
- package/dist/chunker/sentence-splitter.js.map +1 -0
- package/dist/chunker/token-containment.d.ts +39 -0
- package/dist/chunker/token-containment.d.ts.map +1 -0
- package/dist/chunker/token-containment.js +83 -0
- package/dist/chunker/token-containment.js.map +1 -0
- package/dist/cli/common.d.ts +34 -0
- package/dist/cli/common.d.ts.map +1 -0
- package/dist/cli/common.js +121 -0
- package/dist/cli/common.js.map +1 -0
- package/dist/cli/delete.d.ts +3 -0
- package/dist/cli/delete.d.ts.map +1 -0
- package/dist/cli/delete.js +169 -0
- package/dist/cli/delete.js.map +1 -0
- package/dist/cli/file-collection.d.ts +2 -0
- package/dist/cli/file-collection.d.ts.map +1 -0
- package/dist/cli/file-collection.js +53 -0
- package/dist/cli/file-collection.js.map +1 -0
- package/dist/cli/ingest.d.ts +69 -0
- package/dist/cli/ingest.d.ts.map +1 -0
- package/dist/cli/ingest.js +323 -0
- package/dist/cli/ingest.js.map +1 -0
- package/dist/cli/list.d.ts +27 -0
- package/dist/cli/list.d.ts.map +1 -0
- package/dist/cli/list.js +207 -0
- package/dist/cli/list.js.map +1 -0
- package/dist/cli/options.d.ts +89 -0
- package/dist/cli/options.d.ts.map +1 -0
- package/dist/cli/options.js +244 -0
- package/dist/cli/options.js.map +1 -0
- package/dist/cli/query.d.ts +17 -0
- package/dist/cli/query.d.ts.map +1 -0
- package/dist/cli/query.js +194 -0
- package/dist/cli/query.js.map +1 -0
- package/dist/cli/read-neighbors.d.ts +3 -0
- package/dist/cli/read-neighbors.d.ts.map +1 -0
- package/dist/cli/read-neighbors.js +192 -0
- package/dist/cli/read-neighbors.js.map +1 -0
- package/dist/cli/status.d.ts +6 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +78 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/sync.d.ts +6 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +271 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/cli-main.d.ts +9 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +60 -0
- package/dist/cli-main.js.map +1 -0
- package/dist/embedder/index.d.ts +143 -0
- package/dist/embedder/index.d.ts.map +1 -0
- package/dist/embedder/index.js +429 -0
- package/dist/embedder/index.js.map +1 -0
- package/dist/embedder/remote.d.ts +51 -0
- package/dist/embedder/remote.d.ts.map +1 -0
- package/dist/embedder/remote.js +182 -0
- package/dist/embedder/remote.js.map +1 -0
- package/dist/features/list.d.ts +37 -0
- package/dist/features/list.d.ts.map +1 -0
- package/dist/features/list.js +41 -0
- package/dist/features/list.js.map +1 -0
- package/dist/features/sync.d.ts +219 -0
- package/dist/features/sync.d.ts.map +1 -0
- package/dist/features/sync.js +461 -0
- package/dist/features/sync.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/compute.d.ts +63 -0
- package/dist/ingest/compute.d.ts.map +1 -0
- package/dist/ingest/compute.js +161 -0
- package/dist/ingest/compute.js.map +1 -0
- package/dist/ingest/file.d.ts +40 -0
- package/dist/ingest/file.d.ts.map +1 -0
- package/dist/ingest/file.js +74 -0
- package/dist/ingest/file.js.map +1 -0
- package/dist/ingest/visual.d.ts +52 -0
- package/dist/ingest/visual.d.ts.map +1 -0
- package/dist/ingest/visual.js +294 -0
- package/dist/ingest/visual.js.map +1 -0
- package/dist/parser/docx-parser.d.ts +12 -0
- package/dist/parser/docx-parser.d.ts.map +1 -0
- package/dist/parser/docx-parser.js +375 -0
- package/dist/parser/docx-parser.js.map +1 -0
- package/dist/parser/html-parser.d.ts +16 -0
- package/dist/parser/html-parser.d.ts.map +1 -0
- package/dist/parser/html-parser.js +119 -0
- package/dist/parser/html-parser.js.map +1 -0
- package/dist/parser/index.d.ts +125 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +364 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/pdf-extract.d.ts +49 -0
- package/dist/parser/pdf-extract.d.ts.map +1 -0
- package/dist/parser/pdf-extract.js +114 -0
- package/dist/parser/pdf-extract.js.map +1 -0
- package/dist/parser/pdf-filter.d.ts +91 -0
- package/dist/parser/pdf-filter.d.ts.map +1 -0
- package/dist/parser/pdf-filter.js +522 -0
- package/dist/parser/pdf-filter.js.map +1 -0
- package/dist/parser/title-extractor.d.ts +28 -0
- package/dist/parser/title-extractor.d.ts.map +1 -0
- package/dist/parser/title-extractor.js +105 -0
- package/dist/parser/title-extractor.js.map +1 -0
- package/dist/pdf-visual/captioner.d.ts +11 -0
- package/dist/pdf-visual/captioner.d.ts.map +1 -0
- package/dist/pdf-visual/captioner.js +48 -0
- package/dist/pdf-visual/captioner.js.map +1 -0
- package/dist/pdf-visual/captioners/fast.d.ts +7 -0
- package/dist/pdf-visual/captioners/fast.d.ts.map +1 -0
- package/dist/pdf-visual/captioners/fast.js +108 -0
- package/dist/pdf-visual/captioners/fast.js.map +1 -0
- package/dist/pdf-visual/captioners/quality.d.ts +7 -0
- package/dist/pdf-visual/captioners/quality.d.ts.map +1 -0
- package/dist/pdf-visual/captioners/quality.js +101 -0
- package/dist/pdf-visual/captioners/quality.js.map +1 -0
- package/dist/pdf-visual/captioners/shared.d.ts +81 -0
- package/dist/pdf-visual/captioners/shared.d.ts.map +1 -0
- package/dist/pdf-visual/captioners/shared.js +156 -0
- package/dist/pdf-visual/captioners/shared.js.map +1 -0
- package/dist/pdf-visual/detector.d.ts +9 -0
- package/dist/pdf-visual/detector.d.ts.map +1 -0
- package/dist/pdf-visual/detector.js +269 -0
- package/dist/pdf-visual/detector.js.map +1 -0
- package/dist/pdf-visual/index.d.ts +13 -0
- package/dist/pdf-visual/index.d.ts.map +1 -0
- package/dist/pdf-visual/index.js +53 -0
- package/dist/pdf-visual/index.js.map +1 -0
- package/dist/pdf-visual/renderer.d.ts +9 -0
- package/dist/pdf-visual/renderer.d.ts.map +1 -0
- package/dist/pdf-visual/renderer.js +186 -0
- package/dist/pdf-visual/renderer.js.map +1 -0
- package/dist/pdf-visual/types.d.ts +53 -0
- package/dist/pdf-visual/types.d.ts.map +1 -0
- package/dist/pdf-visual/types.js +17 -0
- package/dist/pdf-visual/types.js.map +1 -0
- package/dist/server/error-utils.d.ts +63 -0
- package/dist/server/error-utils.d.ts.map +1 -0
- package/dist/server/error-utils.js +124 -0
- package/dist/server/error-utils.js.map +1 -0
- package/dist/server/index.d.ts +216 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +1068 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/list-scanner.d.ts +30 -0
- package/dist/server/list-scanner.d.ts.map +1 -0
- package/dist/server/list-scanner.js +50 -0
- package/dist/server/list-scanner.js.map +1 -0
- package/dist/server/tool-definitions.d.ts +8 -0
- package/dist/server/tool-definitions.d.ts.map +1 -0
- package/dist/server/tool-definitions.js +181 -0
- package/dist/server/tool-definitions.js.map +1 -0
- package/dist/server/tool-input.d.ts +37 -0
- package/dist/server/tool-input.d.ts.map +1 -0
- package/dist/server/tool-input.js +211 -0
- package/dist/server/tool-input.js.map +1 -0
- package/dist/server/types.d.ts +307 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +3 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server-main.d.ts +40 -0
- package/dist/server-main.d.ts.map +1 -0
- package/dist/server-main.js +254 -0
- package/dist/server-main.js.map +1 -0
- package/dist/utils/base-dirs.d.ts +145 -0
- package/dist/utils/base-dirs.d.ts.map +1 -0
- package/dist/utils/base-dirs.js +344 -0
- package/dist/utils/base-dirs.js.map +1 -0
- package/dist/utils/errors.d.ts +32 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +61 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/limits.d.ts +26 -0
- package/dist/utils/limits.d.ts.map +1 -0
- package/dist/utils/limits.js +28 -0
- package/dist/utils/limits.js.map +1 -0
- package/dist/utils/list-sources.d.ts +38 -0
- package/dist/utils/list-sources.d.ts.map +1 -0
- package/dist/utils/list-sources.js +40 -0
- package/dist/utils/list-sources.js.map +1 -0
- package/dist/utils/raw-data-utils.d.ts +76 -0
- package/dist/utils/raw-data-utils.d.ts.map +1 -0
- package/dist/utils/raw-data-utils.js +202 -0
- package/dist/utils/raw-data-utils.js.map +1 -0
- package/dist/utils/scan.d.ts +114 -0
- package/dist/utils/scan.d.ts.map +1 -0
- package/dist/utils/scan.js +209 -0
- package/dist/utils/scan.js.map +1 -0
- package/dist/utils/scope-match.d.ts +38 -0
- package/dist/utils/scope-match.d.ts.map +1 -0
- package/dist/utils/scope-match.js +86 -0
- package/dist/utils/scope-match.js.map +1 -0
- package/dist/utils/sensitive-path.d.ts +9 -0
- package/dist/utils/sensitive-path.d.ts.map +1 -0
- package/dist/utils/sensitive-path.js +78 -0
- package/dist/utils/sensitive-path.js.map +1 -0
- package/dist/utils/sync-path-key.d.ts +14 -0
- package/dist/utils/sync-path-key.d.ts.map +1 -0
- package/dist/utils/sync-path-key.js +27 -0
- package/dist/utils/sync-path-key.js.map +1 -0
- package/dist/utils/type-guards.d.ts +18 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +34 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/dist/utils/visual-profile.d.ts +10 -0
- package/dist/utils/visual-profile.d.ts.map +1 -0
- package/dist/utils/visual-profile.js +15 -0
- package/dist/utils/visual-profile.js.map +1 -0
- package/dist/vectordb/index.d.ts +132 -0
- package/dist/vectordb/index.d.ts.map +1 -0
- package/dist/vectordb/index.js +617 -0
- package/dist/vectordb/index.js.map +1 -0
- package/dist/vectordb/search-filters.d.ts +15 -0
- package/dist/vectordb/search-filters.d.ts.map +1 -0
- package/dist/vectordb/search-filters.js +129 -0
- package/dist/vectordb/search-filters.js.map +1 -0
- package/dist/vectordb/types.d.ts +194 -0
- package/dist/vectordb/types.d.ts.map +1 -0
- package/dist/vectordb/types.js +237 -0
- package/dist/vectordb/types.js.map +1 -0
- package/package.json +108 -0
- package/skills/mcp-local-rag/SKILL.md +234 -0
- package/skills/mcp-local-rag/references/cli-reference.md +190 -0
- package/skills/mcp-local-rag/references/html-ingestion.md +78 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// Shared bounded directory scan for supported document files: the single
|
|
2
|
+
// walker behind CLI `ingest`, CLI `list`, and the MCP `list_files` scan.
|
|
3
|
+
//
|
|
4
|
+
// The collect predicates live in `classifyScanEntry` so a path a caller names
|
|
5
|
+
// explicitly (`classifyRequestedPath`) is judged by the same rules as one the
|
|
6
|
+
// walk discovers — sync accepts both, and only one used to be filtered.
|
|
7
|
+
//
|
|
8
|
+
// Warning wording and sort/dedupe stay with each caller. This helper returns
|
|
9
|
+
// path-granular coverage facts instead, so a caller can tell an unobserved
|
|
10
|
+
// region apart from a whole-scan failure.
|
|
11
|
+
import { lstat, readdir, realpath } from 'node:fs/promises';
|
|
12
|
+
import { basename, dirname, extname, join } from 'node:path';
|
|
13
|
+
import { SUPPORTED_EXTENSIONS } from '../parser/index.js';
|
|
14
|
+
import { MAX_SCAN_DEPTH } from './limits.js';
|
|
15
|
+
import { isInScope, isUnderOrEqual, shouldVisitDir } from './scope-match.js';
|
|
16
|
+
import { errorCode } from './type-guards.js';
|
|
17
|
+
/**
|
|
18
|
+
* Canonical identity key for the `list`/`list_files` cross-reference: a file's
|
|
19
|
+
* realpath, falling back to the input path when realpath fails (orphaned or
|
|
20
|
+
* raw-data entries). Matching ingested DB entries against scanned files by this
|
|
21
|
+
* key recognizes the same physical file across symlinked spellings (prefix or
|
|
22
|
+
* alias). Storage, lookup, and display still use the normal resolve() path —
|
|
23
|
+
* realpath here is the file-identity comparison, not a user-facing value.
|
|
24
|
+
*/
|
|
25
|
+
export async function realpathForMatch(filePath) {
|
|
26
|
+
try {
|
|
27
|
+
return await realpath(filePath);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return filePath;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Canonical form of one explicitly requested path: its parent chain resolved
|
|
35
|
+
* through symbolic links, with the final component appended verbatim. `null` when
|
|
36
|
+
* the parent chain cannot be resolved at all — absent, or a directory this
|
|
37
|
+
* process may not traverse — which a caller must treat as "not contained",
|
|
38
|
+
* because telling those cases apart would report the state of paths outside its
|
|
39
|
+
* configured roots.
|
|
40
|
+
*
|
|
41
|
+
* Only the parent chain is resolved, because the requested entry itself is judged
|
|
42
|
+
* by {@link classifyRequestedPath}'s `lstat`: a symbolic link named directly
|
|
43
|
+
* inside a root is an in-root entry that is refused as a link, not a path to be
|
|
44
|
+
* reported by whatever it points at.
|
|
45
|
+
*
|
|
46
|
+
* `realpath` here is the containment (security) boundary, the same role it plays
|
|
47
|
+
* in `DocumentParser.validateFilePath` — never a spelling anything is stored,
|
|
48
|
+
* looked up, or displayed under. Those stay `resolve()`-only.
|
|
49
|
+
*/
|
|
50
|
+
export async function canonicalizeRequestedPath(path) {
|
|
51
|
+
try {
|
|
52
|
+
return join(await realpath(dirname(path)), basename(path));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* True when `fullPath` sits under one of the configured excluded prefixes (the
|
|
60
|
+
* database or cache directory).
|
|
61
|
+
*
|
|
62
|
+
* Case-folded on Windows, whose filesystem is case-insensitive: the prefixes are
|
|
63
|
+
* built with `resolve()` only, which preserves whatever case `BASE_DIRS` and
|
|
64
|
+
* `DB_PATH` were spelled in, so a raw comparison let `C:\Docs\lancedb\raw.md`
|
|
65
|
+
* past `c:\docs\lancedb\`. That is worse than a plain miss, because sync's prune
|
|
66
|
+
* guard compares case-folded keys (`toSyncPathKey`): the internals were ingested
|
|
67
|
+
* and then could never be pruned. Both sides now agree.
|
|
68
|
+
*
|
|
69
|
+
* Exact-or-descendant via `isUnderOrEqual`: the prefixes carry a trailing
|
|
70
|
+
* separator, so `startsWith` matched the directory's contents but not the
|
|
71
|
+
* directory itself, and the walk descended into it once per run.
|
|
72
|
+
*
|
|
73
|
+
* Purely lexical — no `realpath`, `stat`, or any other syscall, because this runs
|
|
74
|
+
* once per directory entry on the walk shared with `list_files`, CLI `list`, and
|
|
75
|
+
* CLI `ingest`.
|
|
76
|
+
*/
|
|
77
|
+
function isUnderExcludedPrefix(fullPath, excludePaths, platform) {
|
|
78
|
+
const fold = (path) => (platform === 'win32' ? path.toLowerCase() : path);
|
|
79
|
+
const candidate = fold(fullPath);
|
|
80
|
+
return excludePaths.some((prefix) => isUnderOrEqual(candidate, fold(prefix)));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The collect predicates of {@link bfsCollectSupportedFiles} as one decision,
|
|
84
|
+
* so a discovered entry and an explicitly requested path are judged by the same
|
|
85
|
+
* rules rather than by two implementations that drift.
|
|
86
|
+
*
|
|
87
|
+
* Evaluation order is part of the contract: a symbolic link is reported as a
|
|
88
|
+
* link even under an excluded prefix, and a directory needs no extension test.
|
|
89
|
+
*
|
|
90
|
+
* `platform` is a parameter so the Windows exclusion semantics are provable on
|
|
91
|
+
* a POSIX host. Both `Dirent` and `Stats` satisfy {@link EntryTypeFacts}.
|
|
92
|
+
*/
|
|
93
|
+
export function classifyScanEntry(fullPath, entry, excludePaths, platform = process.platform) {
|
|
94
|
+
if (entry.isSymbolicLink()) {
|
|
95
|
+
return 'symlink';
|
|
96
|
+
}
|
|
97
|
+
if (isUnderExcludedPrefix(fullPath, excludePaths, platform)) {
|
|
98
|
+
return 'excluded';
|
|
99
|
+
}
|
|
100
|
+
if (entry.isDirectory()) {
|
|
101
|
+
return 'directory';
|
|
102
|
+
}
|
|
103
|
+
if (!entry.isFile()) {
|
|
104
|
+
return 'irregular';
|
|
105
|
+
}
|
|
106
|
+
return SUPPORTED_EXTENSIONS.has(extname(fullPath).toLowerCase()) ? 'file' : 'unsupported';
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Classify one explicitly requested path with {@link classifyScanEntry}, so a
|
|
110
|
+
* path a caller names is subject to the same predicates as a path the walker
|
|
111
|
+
* discovers.
|
|
112
|
+
*
|
|
113
|
+
* `lstat` rather than `stat`, so a symbolic link is reported as a link instead of
|
|
114
|
+
* as whatever it points at; and `lstat` rather than any read, so a caller can
|
|
115
|
+
* refuse the path before its bytes cost anything — reading a FIFO blocks forever,
|
|
116
|
+
* and reading through a link reaches outside the configured roots.
|
|
117
|
+
*
|
|
118
|
+
* Any stat failure is `'missing'`: an unreachable path and an absent one are the
|
|
119
|
+
* same non-answer to "what is here".
|
|
120
|
+
*/
|
|
121
|
+
export async function classifyRequestedPath(path, excludePaths, platform = process.platform) {
|
|
122
|
+
try {
|
|
123
|
+
return classifyScanEntry(path, await lstat(path), excludePaths, platform);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return 'missing';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** Directory entries, or the errno that made the directory unreadable. */
|
|
130
|
+
async function readDirEntries(dirPath) {
|
|
131
|
+
try {
|
|
132
|
+
return { entries: await readdir(dirPath, { withFileTypes: true, encoding: 'utf8' }) };
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
return { code: errorCode(error) ?? 'UNKNOWN' };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Route one directory's entries: symlinks are recorded and never followed,
|
|
140
|
+
* in-scope directories are enqueued one level deeper, and in-scope supported
|
|
141
|
+
* files are collected.
|
|
142
|
+
*/
|
|
143
|
+
function sortEntries(entries, visit, rules, sinks) {
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
const fullPath = join(visit.dirPath, entry.name);
|
|
146
|
+
const kind = classifyScanEntry(fullPath, entry, rules.excludePaths, rules.platform);
|
|
147
|
+
if (kind === 'symlink') {
|
|
148
|
+
sinks.skippedSymlinks.push(fullPath);
|
|
149
|
+
}
|
|
150
|
+
else if (kind === 'directory' && shouldVisitDir(fullPath, rules.scope)) {
|
|
151
|
+
sinks.queue.push({ dirPath: fullPath, depth: visit.depth + 1 });
|
|
152
|
+
}
|
|
153
|
+
else if (kind === 'file' && isInScope(fullPath, rules.scope)) {
|
|
154
|
+
sinks.files.push(fullPath);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Bounded BFS scan of a single root, depth counted from `rootPath` itself.
|
|
160
|
+
* Symlinks are never followed. An unreadable directory is recorded and does
|
|
161
|
+
* not abort the scan.
|
|
162
|
+
*
|
|
163
|
+
* Does not sort, dedupe, or emit warnings — each caller owns its own output
|
|
164
|
+
* contract for those.
|
|
165
|
+
*/
|
|
166
|
+
export async function bfsCollectSupportedFiles(rootPath, excludePaths, options = {}) {
|
|
167
|
+
const { maxDepth = MAX_SCAN_DEPTH, scope, platform = process.platform } = options;
|
|
168
|
+
const files = [];
|
|
169
|
+
const unreadableDirs = [];
|
|
170
|
+
const depthLimitedDirs = [];
|
|
171
|
+
const skippedSymlinks = [];
|
|
172
|
+
// Scope pushdown (shared with scanBaseDir via scope-match): visit a directory
|
|
173
|
+
// only if it is in-scope or an ancestor of the scoped subtree, and collect a
|
|
174
|
+
// file only if it is in-scope. A root intersecting no prefix is skipped
|
|
175
|
+
// without any `readdir`; absent scope leaves traversal/collection unchanged.
|
|
176
|
+
const queue = shouldVisitDir(rootPath, scope)
|
|
177
|
+
? [{ dirPath: rootPath, depth: 0 }]
|
|
178
|
+
: [];
|
|
179
|
+
while (queue.length > 0) {
|
|
180
|
+
const visit = queue.shift();
|
|
181
|
+
if (visit === undefined) {
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
if (visit.depth >= maxDepth) {
|
|
185
|
+
// `dirPath` was reached but never read, so it is the first unvisited
|
|
186
|
+
// directory of this branch: it and all its descendants are unobserved.
|
|
187
|
+
depthLimitedDirs.push(visit.dirPath);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const read = await readDirEntries(visit.dirPath);
|
|
191
|
+
if ('code' in read) {
|
|
192
|
+
unreadableDirs.push({ dirPath: visit.dirPath, code: read.code });
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
sortEntries(read.entries, visit, { excludePaths, scope, platform }, {
|
|
196
|
+
files,
|
|
197
|
+
skippedSymlinks,
|
|
198
|
+
queue,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
files,
|
|
203
|
+
unreadableDirs,
|
|
204
|
+
depthLimitedDirs,
|
|
205
|
+
skippedSymlinks,
|
|
206
|
+
depthLimited: depthLimitedDirs.length > 0,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/utils/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface NormalizedScopePrefix {
|
|
2
|
+
exact: string;
|
|
3
|
+
descendant: string;
|
|
4
|
+
}
|
|
5
|
+
/** Normalize one scope prefix for both in-memory and LanceDB matching. */
|
|
6
|
+
export declare function normalizeScopePrefix(prefix: string): NormalizedScopePrefix;
|
|
7
|
+
/**
|
|
8
|
+
* True when `path` equals `prefix` or descends from it, with a separator
|
|
9
|
+
* boundary so `/foo/bar` does not match `/foo/barista`. Trailing separators on
|
|
10
|
+
* `prefix` are normalized; `path` is compared verbatim, matching the SQL
|
|
11
|
+
* contract in `buildPrefixPredicate`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isUnderOrEqual(path: string, prefix: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* True when `path` is under-or-equal any prefix in `prefixes` (union). Empty or
|
|
16
|
+
* undefined `prefixes` semantics are the caller's concern; an empty list yields
|
|
17
|
+
* false (membership against no prefixes).
|
|
18
|
+
*/
|
|
19
|
+
export declare function matchesAnyScope(path: string, prefixes: string[]): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* The prefixes in `scope` that are not absolute paths. Such a prefix matches
|
|
22
|
+
* nothing under the exact-or-descendant contract, so both list surfaces warn
|
|
23
|
+
* while keeping that "matches nothing" result.
|
|
24
|
+
*/
|
|
25
|
+
export declare function nonAbsolutePrefixes(scope: string[]): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Directory-visit predicate for the scoped walk, shared by both walkers. Visit
|
|
28
|
+
* `dir` when there is no scope, when it is in-scope, or when it is an ancestor
|
|
29
|
+
* of a prefix and must be descended to reach the scoped subtree.
|
|
30
|
+
*/
|
|
31
|
+
export declare function shouldVisitDir(dir: string, scope?: string[]): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* File-collect predicate for the scoped BFS walk, shared by both walkers.
|
|
34
|
+
* Collect `path` when there is no scope, or when `path` is in-scope. An absent
|
|
35
|
+
* or empty `scope` collects every supported file (collection unchanged).
|
|
36
|
+
*/
|
|
37
|
+
export declare function isInScope(path: string, scope?: string[]): boolean;
|
|
38
|
+
//# sourceMappingURL=scope-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-match.d.ts","sourceRoot":"","sources":["../../src/utils/scope-match.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAO1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGpE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAE7D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAKrE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAEjE"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Boundary-safe exact-or-descendant prefix matcher, shared by both BFS walkers
|
|
2
|
+
// and both list surfaces.
|
|
3
|
+
//
|
|
4
|
+
// The JS counterpart of `vectordb`'s SQL `buildPrefixPredicate`: same
|
|
5
|
+
// exact-or-descendant contract, same separator-boundary and trailing-separator
|
|
6
|
+
// normalization. A change to one must be mirrored in the other.
|
|
7
|
+
import { isAbsolute, sep as PATH_SEP } from 'node:path';
|
|
8
|
+
// A slash identifies slash-style paths even when a legal filename segment
|
|
9
|
+
// contains a backslash. Pure backslash-style paths retain Windows support.
|
|
10
|
+
function deriveSeparator(prefix) {
|
|
11
|
+
if (prefix.includes('/')) {
|
|
12
|
+
return '/';
|
|
13
|
+
}
|
|
14
|
+
if (prefix.includes('\\')) {
|
|
15
|
+
return '\\';
|
|
16
|
+
}
|
|
17
|
+
return PATH_SEP;
|
|
18
|
+
}
|
|
19
|
+
// Strip trailing separators so `/a/b`, `/a/b/`, `/a/b//` normalize alike. A
|
|
20
|
+
// prefix of only separators (e.g. a lone posix root `/`) is kept as a single
|
|
21
|
+
// separator so its descendant boundary is `/<sep>` rather than empty.
|
|
22
|
+
function stripTrailingSeparators(prefix, separator) {
|
|
23
|
+
let end = prefix.length;
|
|
24
|
+
while (end > 0 && prefix[end - 1] === separator) {
|
|
25
|
+
end--;
|
|
26
|
+
}
|
|
27
|
+
if (end === 0) {
|
|
28
|
+
return separator;
|
|
29
|
+
}
|
|
30
|
+
return prefix.slice(0, end);
|
|
31
|
+
}
|
|
32
|
+
/** Normalize one scope prefix for both in-memory and LanceDB matching. */
|
|
33
|
+
export function normalizeScopePrefix(prefix) {
|
|
34
|
+
const separator = deriveSeparator(prefix);
|
|
35
|
+
const exact = stripTrailingSeparators(prefix, separator);
|
|
36
|
+
return {
|
|
37
|
+
exact,
|
|
38
|
+
descendant: exact.endsWith(separator) ? exact : exact + separator,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* True when `path` equals `prefix` or descends from it, with a separator
|
|
43
|
+
* boundary so `/foo/bar` does not match `/foo/barista`. Trailing separators on
|
|
44
|
+
* `prefix` are normalized; `path` is compared verbatim, matching the SQL
|
|
45
|
+
* contract in `buildPrefixPredicate`.
|
|
46
|
+
*/
|
|
47
|
+
export function isUnderOrEqual(path, prefix) {
|
|
48
|
+
const { exact, descendant } = normalizeScopePrefix(prefix);
|
|
49
|
+
return path === exact || path.startsWith(descendant);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* True when `path` is under-or-equal any prefix in `prefixes` (union). Empty or
|
|
53
|
+
* undefined `prefixes` semantics are the caller's concern; an empty list yields
|
|
54
|
+
* false (membership against no prefixes).
|
|
55
|
+
*/
|
|
56
|
+
export function matchesAnyScope(path, prefixes) {
|
|
57
|
+
return prefixes.some((prefix) => isUnderOrEqual(path, prefix));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The prefixes in `scope` that are not absolute paths. Such a prefix matches
|
|
61
|
+
* nothing under the exact-or-descendant contract, so both list surfaces warn
|
|
62
|
+
* while keeping that "matches nothing" result.
|
|
63
|
+
*/
|
|
64
|
+
export function nonAbsolutePrefixes(scope) {
|
|
65
|
+
return scope.filter((prefix) => !isAbsolute(prefix));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Directory-visit predicate for the scoped walk, shared by both walkers. Visit
|
|
69
|
+
* `dir` when there is no scope, when it is in-scope, or when it is an ancestor
|
|
70
|
+
* of a prefix and must be descended to reach the scoped subtree.
|
|
71
|
+
*/
|
|
72
|
+
export function shouldVisitDir(dir, scope) {
|
|
73
|
+
if (!scope || scope.length === 0) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return matchesAnyScope(dir, scope) || scope.some((prefix) => isUnderOrEqual(prefix, dir));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* File-collect predicate for the scoped BFS walk, shared by both walkers.
|
|
80
|
+
* Collect `path` when there is no scope, or when `path` is in-scope. An absent
|
|
81
|
+
* or empty `scope` collects every supported file (collection unchanged).
|
|
82
|
+
*/
|
|
83
|
+
export function isInScope(path, scope) {
|
|
84
|
+
return !scope || scope.length === 0 || matchesAnyScope(path, scope);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=scope-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-match.js","sourceRoot":"","sources":["../../src/utils/scope-match.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,0BAA0B;AAC1B,EAAE;AACF,sEAAsE;AACtE,+EAA+E;AAC/E,gEAAgE;AAEhE,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEvD,0EAA0E;AAC1E,2EAA2E;AAC3E,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,4EAA4E;AAC5E,6EAA6E;AAC7E,sEAAsE;AACtE,SAAS,uBAAuB,CAAC,MAAc,EAAE,SAAiB;IAChE,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAA;IACvB,OAAO,GAAG,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QAChD,GAAG,EAAE,CAAA;IACP,CAAC;IACD,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;QACd,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAC7B,CAAC;AAOD,0EAA0E;AAC1E,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CAAC,CAAA;IACzC,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACxD,OAAO;QACL,KAAK;QACL,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,SAAS;KAClE,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,MAAc;IACzD,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAA;IAC1D,OAAO,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAA;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAAkB;IAC9D,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAe;IACjD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,KAAgB;IAC1D,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;AAC3F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAgB;IACtD,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AACrE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The literal prefixes joined with their `realpath`-resolved forms. Without
|
|
3
|
+
* canonicalization macOS would let `/etc` (which realpaths to `/private/etc`)
|
|
4
|
+
* slip past. The literal is always kept, so a realpath failure cannot weaken
|
|
5
|
+
* the policy.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildSensitivePrefixes(realpathSyncFn?: (p: string) => string): string[];
|
|
8
|
+
export declare function checkSensitivePath(value: string, flagName: string): string | undefined;
|
|
9
|
+
//# sourceMappingURL=sensitive-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitive-path.d.ts","sourceRoot":"","sources":["../../src/utils/sensitive-path.ts"],"names":[],"mappings":"AAsBA;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAqB,GACnD,MAAM,EAAE,CAcV;AAmCD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUtF"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Sensitive-path policy shared by the CLI and the MCP server entry point, so
|
|
2
|
+
// neither can drift: pre-multi-root code enforced it at the CLI surface only,
|
|
3
|
+
// which left `BASE_DIRS=["/etc"]` silently accepted in the server.
|
|
4
|
+
//
|
|
5
|
+
// Deliberately simple — a small exclusion list of system mount points and
|
|
6
|
+
// credential directories under `$HOME`, not a sandbox. `DocumentParser`
|
|
7
|
+
// remains the authoritative path-traversal / symlink-escape boundary.
|
|
8
|
+
import { realpathSync } from 'node:fs';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
// Normalize a path for comparison: forward-slash separators throughout and
|
|
11
|
+
// lower-case on Windows (case-insensitive filesystem). Used by the security
|
|
12
|
+
// boundary checks below so `C:\Users\me\.ssh` and `c:/users/me/.ssh` resolve
|
|
13
|
+
// to the same comparable form.
|
|
14
|
+
function toComparable(p) {
|
|
15
|
+
const slashed = p.replace(/\\/g, '/');
|
|
16
|
+
return process.platform === 'win32' ? slashed.toLowerCase() : slashed;
|
|
17
|
+
}
|
|
18
|
+
const SENSITIVE_PATH_LITERALS = ['/etc', '/usr', '/sys', '/proc', '/var'];
|
|
19
|
+
/**
|
|
20
|
+
* The literal prefixes joined with their `realpath`-resolved forms. Without
|
|
21
|
+
* canonicalization macOS would let `/etc` (which realpaths to `/private/etc`)
|
|
22
|
+
* slip past. The literal is always kept, so a realpath failure cannot weaken
|
|
23
|
+
* the policy.
|
|
24
|
+
*/
|
|
25
|
+
export function buildSensitivePrefixes(realpathSyncFn = realpathSync) {
|
|
26
|
+
const set = new Set();
|
|
27
|
+
for (const literal of SENSITIVE_PATH_LITERALS) {
|
|
28
|
+
set.add(literal);
|
|
29
|
+
try {
|
|
30
|
+
const canonical = realpathSyncFn(literal);
|
|
31
|
+
if (typeof canonical === 'string' && canonical.length > 0) {
|
|
32
|
+
set.add(canonical);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// realpath unavailable on this platform; literal already retained.
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return [...set];
|
|
40
|
+
}
|
|
41
|
+
const SENSITIVE_PATH_PREFIXES = buildSensitivePrefixes();
|
|
42
|
+
/**
|
|
43
|
+
* Directories under `$HOME` that hold credentials and must never be opened
|
|
44
|
+
* as document roots even when the user expands the path themselves.
|
|
45
|
+
*/
|
|
46
|
+
const SENSITIVE_HOME_PREFIXES = ['.ssh', '.gnupg'];
|
|
47
|
+
/**
|
|
48
|
+
* Returns a user-facing error string when `value` resolves to a sensitive
|
|
49
|
+
* system or credential directory, `undefined` otherwise.
|
|
50
|
+
*
|
|
51
|
+
* `flagName` is interpolated so the rejection names the surface actually
|
|
52
|
+
* consulted — `--base-dir`, `BASE_DIR`, or `BASE_DIRS`. The trailing-separator
|
|
53
|
+
* check guards against a sibling like `/etcetera`, and both `~/.ssh` and its
|
|
54
|
+
* expanded form are rejected so the policy holds when `$HOME` is unset.
|
|
55
|
+
*/
|
|
56
|
+
/** True when `pathCmp` is `prefixCmp` itself or sits underneath it. */
|
|
57
|
+
function isAtOrUnder(pathCmp, prefixCmp) {
|
|
58
|
+
return pathCmp === prefixCmp || pathCmp.startsWith(`${prefixCmp}/`);
|
|
59
|
+
}
|
|
60
|
+
/** True when `value` names one of the sensitive home subdirectories. */
|
|
61
|
+
function matchesSensitiveHomeDir(value, valueCmp, home) {
|
|
62
|
+
return SENSITIVE_HOME_PREFIXES.some((dir) => {
|
|
63
|
+
if (home.length > 0 && isAtOrUnder(valueCmp, toComparable(`${home}/${dir}`))) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
// Unexpanded `~/...` form — caught even when `home` is empty.
|
|
67
|
+
return value === `~/${dir}` || value.startsWith(`~/${dir}/`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export function checkSensitivePath(value, flagName) {
|
|
71
|
+
const home = process.env['HOME'] || homedir();
|
|
72
|
+
const expanded = value.startsWith('~/') ? `${home}/${value.slice(2)}` : value;
|
|
73
|
+
const valueCmp = toComparable(expanded);
|
|
74
|
+
const isSensitive = SENSITIVE_PATH_PREFIXES.some((prefix) => isAtOrUnder(valueCmp, toComparable(prefix))) ||
|
|
75
|
+
matchesSensitiveHomeDir(value, valueCmp, home);
|
|
76
|
+
return isSensitive ? `Refusing to use sensitive system path for ${flagName}: ${value}` : undefined;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=sensitive-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitive-path.js","sourceRoot":"","sources":["../../src/utils/sensitive-path.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve `path` into the absolute, normalized key sync reconciles with.
|
|
3
|
+
* Windows keys are case-folded, POSIX keys keep their case.
|
|
4
|
+
*
|
|
5
|
+
* `platform` is a parameter and selects the resolver as well as the case-fold,
|
|
6
|
+
* so the Windows branch is provable on POSIX — the host-bound `resolve()` would
|
|
7
|
+
* otherwise turn a Windows path into a cwd-relative POSIX one.
|
|
8
|
+
*
|
|
9
|
+
* Purely lexical: symbolic links are not followed, and a path no longer on disk
|
|
10
|
+
* must still yield a key. This is an internal reconciliation identity — never a
|
|
11
|
+
* replacement for the verbatim stored spellings used for deletion.
|
|
12
|
+
*/
|
|
13
|
+
export declare function toSyncPathKey(path: string, platform?: NodeJS.Platform): string;
|
|
14
|
+
//# sourceMappingURL=sync-path-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-path-key.d.ts","sourceRoot":"","sources":["../../src/utils/sync-path-key.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,MAAM,CAKhG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Sync-only comparison-key generation.
|
|
2
|
+
//
|
|
3
|
+
// This module deliberately holds no boundary logic. Containment is delegated to
|
|
4
|
+
// the unchanged `isUnderOrEqual` in `scope-match.ts`, composed by the caller as
|
|
5
|
+
// `isUnderOrEqual(toSyncPathKey(candidate), toSyncPathKey(prefix))`, so the
|
|
6
|
+
// exact-or-descendant, separator-boundary, and trailing-separator semantics
|
|
7
|
+
// stay in exactly one place.
|
|
8
|
+
import { posix, win32 } from 'node:path';
|
|
9
|
+
/**
|
|
10
|
+
* Resolve `path` into the absolute, normalized key sync reconciles with.
|
|
11
|
+
* Windows keys are case-folded, POSIX keys keep their case.
|
|
12
|
+
*
|
|
13
|
+
* `platform` is a parameter and selects the resolver as well as the case-fold,
|
|
14
|
+
* so the Windows branch is provable on POSIX — the host-bound `resolve()` would
|
|
15
|
+
* otherwise turn a Windows path into a cwd-relative POSIX one.
|
|
16
|
+
*
|
|
17
|
+
* Purely lexical: symbolic links are not followed, and a path no longer on disk
|
|
18
|
+
* must still yield a key. This is an internal reconciliation identity — never a
|
|
19
|
+
* replacement for the verbatim stored spellings used for deletion.
|
|
20
|
+
*/
|
|
21
|
+
export function toSyncPathKey(path, platform = process.platform) {
|
|
22
|
+
if (platform === 'win32') {
|
|
23
|
+
return win32.resolve(path).toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
return posix.resolve(path);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=sync-path-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-path-key.js","sourceRoot":"","sources":["../../src/utils/sync-path-key.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,4EAA4E;AAC5E,4EAA4E;AAC5E,6BAA6B;AAE7B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAExC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,QAAQ,GAAoB,OAAO,CAAC,QAAQ;IACtF,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** True for any non-null object, narrowing it to an indexable record. */
|
|
2
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* True for anything property access works on, functions included. Use this
|
|
5
|
+
* where the value may be a callable third-party handle rather than a plain
|
|
6
|
+
* object; {@link isRecord} covers the plain-object case.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isObjectLike(value: unknown): value is Record<string, unknown>;
|
|
9
|
+
/** True when `value` is one of `list`'s members, narrowing to that union. */
|
|
10
|
+
export declare function isMemberOf<T extends string>(list: readonly T[], value: string): value is T;
|
|
11
|
+
/** True for a finite integer, narrowing `unknown` to `number`. */
|
|
12
|
+
export declare function isInteger(value: unknown): value is number;
|
|
13
|
+
/**
|
|
14
|
+
* The `code` a Node system error carries (`ENOENT`, `EACCES`, ...), or
|
|
15
|
+
* `undefined` when the caught value is not one.
|
|
16
|
+
*/
|
|
17
|
+
export declare function errorCode(error: unknown): string | undefined;
|
|
18
|
+
//# sourceMappingURL=type-guards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-guards.d.ts","sourceRoot":"","sources":["../../src/utils/type-guards.ts"],"names":[],"mappings":"AAGA,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE7E;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,CAAC,CAE1F;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAM5D"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Narrowing helpers for values from outside the type system: parsed JSON,
|
|
2
|
+
// third-party results, and `catch` bindings.
|
|
3
|
+
/** True for any non-null object, narrowing it to an indexable record. */
|
|
4
|
+
export function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* True for anything property access works on, functions included. Use this
|
|
9
|
+
* where the value may be a callable third-party handle rather than a plain
|
|
10
|
+
* object; {@link isRecord} covers the plain-object case.
|
|
11
|
+
*/
|
|
12
|
+
export function isObjectLike(value) {
|
|
13
|
+
return (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
14
|
+
}
|
|
15
|
+
/** True when `value` is one of `list`'s members, narrowing to that union. */
|
|
16
|
+
export function isMemberOf(list, value) {
|
|
17
|
+
return list.some((member) => member === value);
|
|
18
|
+
}
|
|
19
|
+
/** True for a finite integer, narrowing `unknown` to `number`. */
|
|
20
|
+
export function isInteger(value) {
|
|
21
|
+
return typeof value === 'number' && Number.isInteger(value);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The `code` a Node system error carries (`ENOENT`, `EACCES`, ...), or
|
|
25
|
+
* `undefined` when the caught value is not one.
|
|
26
|
+
*/
|
|
27
|
+
export function errorCode(error) {
|
|
28
|
+
if (!isRecord(error)) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const code = error['code'];
|
|
32
|
+
return typeof code === 'string' ? code : undefined;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=type-guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-guards.js","sourceRoot":"","sources":["../../src/utils/type-guards.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,6CAA6C;AAE7C,yEAAyE;AACzE,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAA;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI,OAAO,KAAK,KAAK,UAAU,CAAA;AACrF,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,UAAU,CAAmB,IAAkB,EAAE,KAAa;IAC5E,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,KAAK,CAAC,CAAA;AAChD,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;IAC1B,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;AACpD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visual-quality profile. `fast` is SmolVLM-256M / IDEFICS3 (~250 MB cache);
|
|
3
|
+
* `quality` is Qwen2.5-VL-3B-Instruct-ONNX (~2.9 GB, ~2x per-page inference,
|
|
4
|
+
* better on figures with in-image text).
|
|
5
|
+
*/
|
|
6
|
+
export declare const QUALITY_PROFILES: readonly ['fast', 'quality'];
|
|
7
|
+
export type QualityProfile = (typeof QUALITY_PROFILES)[number];
|
|
8
|
+
/** True for one of the supported profiles; anything else, including a stored legacy value, is not. */
|
|
9
|
+
export declare function isQualityProfile(value: unknown): value is QualityProfile;
|
|
10
|
+
//# sourceMappingURL=visual-profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-profile.d.ts","sourceRoot":"","sources":["../../src/utils/visual-profile.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,YAAI,MAAM,EAAE,SAAS,CAAU,CAAA;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9D,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAExE"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// The visual-quality profile vocabulary, shared by ingestion, storage-facing
|
|
2
|
+
// planning, CLI and MCP. It lives in `utils/` so a consumer that only needs the
|
|
3
|
+
// domain value never takes a runtime dependency on the VLM modules.
|
|
4
|
+
import { isMemberOf } from './type-guards.js';
|
|
5
|
+
/**
|
|
6
|
+
* Visual-quality profile. `fast` is SmolVLM-256M / IDEFICS3 (~250 MB cache);
|
|
7
|
+
* `quality` is Qwen2.5-VL-3B-Instruct-ONNX (~2.9 GB, ~2x per-page inference,
|
|
8
|
+
* better on figures with in-image text).
|
|
9
|
+
*/
|
|
10
|
+
export const QUALITY_PROFILES = ['fast', 'quality'];
|
|
11
|
+
/** True for one of the supported profiles; anything else, including a stored legacy value, is not. */
|
|
12
|
+
export function isQualityProfile(value) {
|
|
13
|
+
return typeof value === 'string' && isMemberOf(QUALITY_PROFILES, value);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=visual-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-profile.js","sourceRoot":"","sources":["../../src/utils/visual-profile.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,gFAAgF;AAChF,oEAAoE;AAEpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,SAAS,CAAU,CAAA;AAG5D,sGAAsG;AACtG,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,UAAU,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;AACzE,CAAC"}
|