@mka-rainmaker/ama 0.1.0
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.md +149 -0
- package/dist/analyzers/baseline/analyzer.d.ts +47 -0
- package/dist/analyzers/baseline/analyzer.d.ts.map +1 -0
- package/dist/analyzers/baseline/analyzer.js +84 -0
- package/dist/analyzers/baseline/analyzer.js.map +1 -0
- package/dist/analyzers/baseline/c.d.ts +12 -0
- package/dist/analyzers/baseline/c.d.ts.map +1 -0
- package/dist/analyzers/baseline/c.js +56 -0
- package/dist/analyzers/baseline/c.js.map +1 -0
- package/dist/analyzers/baseline/config.d.ts +21 -0
- package/dist/analyzers/baseline/config.d.ts.map +1 -0
- package/dist/analyzers/baseline/config.js +32 -0
- package/dist/analyzers/baseline/config.js.map +1 -0
- package/dist/analyzers/baseline/csharp.d.ts +9 -0
- package/dist/analyzers/baseline/csharp.d.ts.map +1 -0
- package/dist/analyzers/baseline/csharp.js +107 -0
- package/dist/analyzers/baseline/csharp.js.map +1 -0
- package/dist/analyzers/baseline/go.d.ts +11 -0
- package/dist/analyzers/baseline/go.d.ts.map +1 -0
- package/dist/analyzers/baseline/go.js +66 -0
- package/dist/analyzers/baseline/go.js.map +1 -0
- package/dist/analyzers/baseline/java.d.ts +9 -0
- package/dist/analyzers/baseline/java.d.ts.map +1 -0
- package/dist/analyzers/baseline/java.js +50 -0
- package/dist/analyzers/baseline/java.js.map +1 -0
- package/dist/analyzers/baseline/javascript.d.ts +10 -0
- package/dist/analyzers/baseline/javascript.d.ts.map +1 -0
- package/dist/analyzers/baseline/javascript.js +55 -0
- package/dist/analyzers/baseline/javascript.js.map +1 -0
- package/dist/analyzers/baseline/kotlin.d.ts +11 -0
- package/dist/analyzers/baseline/kotlin.d.ts.map +1 -0
- package/dist/analyzers/baseline/kotlin.js +67 -0
- package/dist/analyzers/baseline/kotlin.js.map +1 -0
- package/dist/analyzers/baseline/paths.d.ts +6 -0
- package/dist/analyzers/baseline/paths.d.ts.map +1 -0
- package/dist/analyzers/baseline/paths.js +17 -0
- package/dist/analyzers/baseline/paths.js.map +1 -0
- package/dist/analyzers/baseline/php.d.ts +11 -0
- package/dist/analyzers/baseline/php.d.ts.map +1 -0
- package/dist/analyzers/baseline/php.js +76 -0
- package/dist/analyzers/baseline/php.js.map +1 -0
- package/dist/analyzers/baseline/python.d.ts +10 -0
- package/dist/analyzers/baseline/python.d.ts.map +1 -0
- package/dist/analyzers/baseline/python.js +63 -0
- package/dist/analyzers/baseline/python.js.map +1 -0
- package/dist/analyzers/baseline/rust.d.ts +10 -0
- package/dist/analyzers/baseline/rust.d.ts.map +1 -0
- package/dist/analyzers/baseline/rust.js +45 -0
- package/dist/analyzers/baseline/rust.js.map +1 -0
- package/dist/analyzers/baseline/swift.d.ts +11 -0
- package/dist/analyzers/baseline/swift.d.ts.map +1 -0
- package/dist/analyzers/baseline/swift.js +19 -0
- package/dist/analyzers/baseline/swift.js.map +1 -0
- package/dist/analyzers/baseline/treesitter.d.ts +11 -0
- package/dist/analyzers/baseline/treesitter.d.ts.map +1 -0
- package/dist/analyzers/baseline/treesitter.js +87 -0
- package/dist/analyzers/baseline/treesitter.js.map +1 -0
- package/dist/analyzers/baseline/walk.d.ts +26 -0
- package/dist/analyzers/baseline/walk.d.ts.map +1 -0
- package/dist/analyzers/baseline/walk.js +76 -0
- package/dist/analyzers/baseline/walk.js.map +1 -0
- package/dist/analyzers/registry.d.ts +19 -0
- package/dist/analyzers/registry.d.ts.map +1 -0
- package/dist/analyzers/registry.js +43 -0
- package/dist/analyzers/registry.js.map +1 -0
- package/dist/analyzers/sfc/analyzer.d.ts +17 -0
- package/dist/analyzers/sfc/analyzer.d.ts.map +1 -0
- package/dist/analyzers/sfc/analyzer.js +141 -0
- package/dist/analyzers/sfc/analyzer.js.map +1 -0
- package/dist/analyzers/sidecar/analyzer.d.ts +29 -0
- package/dist/analyzers/sidecar/analyzer.d.ts.map +1 -0
- package/dist/analyzers/sidecar/analyzer.js +114 -0
- package/dist/analyzers/sidecar/analyzer.js.map +1 -0
- package/dist/analyzers/sidecar/protocol.d.ts +508 -0
- package/dist/analyzers/sidecar/protocol.d.ts.map +1 -0
- package/dist/analyzers/sidecar/protocol.js +102 -0
- package/dist/analyzers/sidecar/protocol.js.map +1 -0
- package/dist/analyzers/types.d.ts +46 -0
- package/dist/analyzers/types.d.ts.map +1 -0
- package/dist/analyzers/types.js +2 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/analyzers/typescript/analyzer.d.ts +126 -0
- package/dist/analyzers/typescript/analyzer.d.ts.map +1 -0
- package/dist/analyzers/typescript/analyzer.js +1600 -0
- package/dist/analyzers/typescript/analyzer.js.map +1 -0
- package/dist/cli/commands/cycles.d.ts +6 -0
- package/dist/cli/commands/cycles.d.ts.map +1 -0
- package/dist/cli/commands/cycles.js +27 -0
- package/dist/cli/commands/cycles.js.map +1 -0
- package/dist/cli/commands/files.d.ts +6 -0
- package/dist/cli/commands/files.d.ts.map +1 -0
- package/dist/cli/commands/files.js +33 -0
- package/dist/cli/commands/files.js.map +1 -0
- package/dist/cli/commands/impact.d.ts +18 -0
- package/dist/cli/commands/impact.d.ts.map +1 -0
- package/dist/cli/commands/impact.js +113 -0
- package/dist/cli/commands/impact.js.map +1 -0
- package/dist/cli/commands/lifecycle.d.ts +5 -0
- package/dist/cli/commands/lifecycle.d.ts.map +1 -0
- package/dist/cli/commands/lifecycle.js +83 -0
- package/dist/cli/commands/lifecycle.js.map +1 -0
- package/dist/cli/commands/query.d.ts +31 -0
- package/dist/cli/commands/query.d.ts.map +1 -0
- package/dist/cli/commands/query.js +187 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/search.d.ts +21 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +160 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/status.d.ts +6 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +63 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +6 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +57 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/emit.d.ts +9 -0
- package/dist/cli/emit.d.ts.map +1 -0
- package/dist/cli/emit.js +10 -0
- package/dist/cli/emit.js.map +1 -0
- package/dist/cli/index.d.ts +37 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +128 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/paths.d.ts +7 -0
- package/dist/cli/paths.d.ts.map +1 -0
- package/dist/cli/paths.js +10 -0
- package/dist/cli/paths.js.map +1 -0
- package/dist/cli/query-runner.d.ts +13 -0
- package/dist/cli/query-runner.d.ts.map +1 -0
- package/dist/cli/query-runner.js +33 -0
- package/dist/cli/query-runner.js.map +1 -0
- package/dist/graph/dispatch.d.ts +17 -0
- package/dist/graph/dispatch.d.ts.map +1 -0
- package/dist/graph/dispatch.js +82 -0
- package/dist/graph/dispatch.js.map +1 -0
- package/dist/graph/id.d.ts +19 -0
- package/dist/graph/id.d.ts.map +1 -0
- package/dist/graph/id.js +17 -0
- package/dist/graph/id.js.map +1 -0
- package/dist/graph/index.d.ts +6 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +4 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/types.d.ts +71 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +52 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/indexer/debouncer.d.ts +32 -0
- package/dist/indexer/debouncer.d.ts.map +1 -0
- package/dist/indexer/debouncer.js +81 -0
- package/dist/indexer/debouncer.js.map +1 -0
- package/dist/indexer/ignore.d.ts +55 -0
- package/dist/indexer/ignore.d.ts.map +1 -0
- package/dist/indexer/ignore.js +170 -0
- package/dist/indexer/ignore.js.map +1 -0
- package/dist/indexer/indexer.d.ts +112 -0
- package/dist/indexer/indexer.d.ts.map +1 -0
- package/dist/indexer/indexer.js +392 -0
- package/dist/indexer/indexer.js.map +1 -0
- package/dist/indexer/watcher.d.ts +50 -0
- package/dist/indexer/watcher.d.ts.map +1 -0
- package/dist/indexer/watcher.js +86 -0
- package/dist/indexer/watcher.js.map +1 -0
- package/dist/mcp/build-info.d.ts +16 -0
- package/dist/mcp/build-info.d.ts.map +1 -0
- package/dist/mcp/build-info.js +54 -0
- package/dist/mcp/build-info.js.map +1 -0
- package/dist/mcp/http.d.ts +18 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.js +145 -0
- package/dist/mcp/http.js.map +1 -0
- package/dist/mcp/server.d.ts +22 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +401 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session.d.ts +155 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +319 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/query/service.d.ts +329 -0
- package/dist/query/service.d.ts.map +1 -0
- package/dist/query/service.js +959 -0
- package/dist/query/service.js.map +1 -0
- package/dist/runtime/entrypoint.d.ts +11 -0
- package/dist/runtime/entrypoint.d.ts.map +1 -0
- package/dist/runtime/entrypoint.js +22 -0
- package/dist/runtime/entrypoint.js.map +1 -0
- package/dist/runtime/quiet-sqlite-warning.d.ts +14 -0
- package/dist/runtime/quiet-sqlite-warning.d.ts.map +1 -0
- package/dist/runtime/quiet-sqlite-warning.js +26 -0
- package/dist/runtime/quiet-sqlite-warning.js.map +1 -0
- package/dist/runtime/wasm-tier.d.ts +2 -0
- package/dist/runtime/wasm-tier.d.ts.map +1 -0
- package/dist/runtime/wasm-tier.js +54 -0
- package/dist/runtime/wasm-tier.js.map +1 -0
- package/dist/store/memory.d.ts +54 -0
- package/dist/store/memory.d.ts.map +1 -0
- package/dist/store/memory.js +210 -0
- package/dist/store/memory.js.map +1 -0
- package/dist/store/sqlite.d.ts +38 -0
- package/dist/store/sqlite.d.ts.map +1 -0
- package/dist/store/sqlite.js +298 -0
- package/dist/store/sqlite.js.map +1 -0
- package/dist/store/types.d.ts +76 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +2 -0
- package/dist/store/types.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { BaselineAnalyzer } from "../analyzers/baseline/analyzer.js";
|
|
6
|
+
import { cSpec, cppSpec } from "../analyzers/baseline/c.js";
|
|
7
|
+
import { csharpSpec } from "../analyzers/baseline/csharp.js";
|
|
8
|
+
import { goSpec } from "../analyzers/baseline/go.js";
|
|
9
|
+
import { javaSpec } from "../analyzers/baseline/java.js";
|
|
10
|
+
import { javascriptSpec } from "../analyzers/baseline/javascript.js";
|
|
11
|
+
import { kotlinSpec } from "../analyzers/baseline/kotlin.js";
|
|
12
|
+
import { phpSpec } from "../analyzers/baseline/php.js";
|
|
13
|
+
import { pythonSpec } from "../analyzers/baseline/python.js";
|
|
14
|
+
import { rustSpec } from "../analyzers/baseline/rust.js";
|
|
15
|
+
import { swiftSpec } from "../analyzers/baseline/swift.js";
|
|
16
|
+
import { AnalyzerRegistry } from "../analyzers/registry.js";
|
|
17
|
+
import { SfcAnalyzer } from "../analyzers/sfc/analyzer.js";
|
|
18
|
+
import { TypeScriptAnalyzer } from "../analyzers/typescript/analyzer.js";
|
|
19
|
+
import { deriveDispatchEdges } from "../graph/index.js";
|
|
20
|
+
import { InMemoryStore } from "../store/memory.js";
|
|
21
|
+
import { MAX_FILE_SIZE_BYTES, isIgnoredPath, loadIgnoreRules, withNestedIgnore, } from "./ignore.js";
|
|
22
|
+
/**
|
|
23
|
+
* Bumped whenever the persisted store's schema or the shape of what we write
|
|
24
|
+
* into it changes. A persisted index stamped with a different version is treated
|
|
25
|
+
* as unusable and rebuilt rather than reopened.
|
|
26
|
+
*/
|
|
27
|
+
const SCHEMA_VERSION = 4; // 2: provenance (m8k.1); 3: source-location (hft.9); 4: call sites (hft.10)
|
|
28
|
+
/** Absolute directories far too broad to index — likely to pull in secrets,
|
|
29
|
+
* exhaust memory, or never finish. A real project lives in a subdirectory, so
|
|
30
|
+
* refusing these never blocks legitimate use. (ama-m8k.10) */
|
|
31
|
+
const UNSAFE_DIRS = new Set([
|
|
32
|
+
"/usr",
|
|
33
|
+
"/etc",
|
|
34
|
+
"/bin",
|
|
35
|
+
"/sbin",
|
|
36
|
+
"/var",
|
|
37
|
+
"/opt",
|
|
38
|
+
"/lib",
|
|
39
|
+
"/dev",
|
|
40
|
+
"/proc",
|
|
41
|
+
"/System",
|
|
42
|
+
"/Library",
|
|
43
|
+
].map((p) => path.resolve(p)));
|
|
44
|
+
/** Throw if `root` resolves to the filesystem root, the user's home directory,
|
|
45
|
+
* or a well-known system directory — a guardrail so a stray `index_repository`
|
|
46
|
+
* call (an agent, a typo) can't walk the whole machine. (ama-m8k.10) */
|
|
47
|
+
export function assertSafeRoot(root) {
|
|
48
|
+
const abs = path.resolve(root);
|
|
49
|
+
if (abs === path.parse(abs).root || abs === path.resolve(os.homedir()) || UNSAFE_DIRS.has(abs)) {
|
|
50
|
+
throw new Error(`Refusing to index ${abs}: that's the filesystem root, your home directory, or a system directory — far too broad. Point Ama at a specific project directory.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Turns a directory into a graph: discover source files, hand each to the
|
|
55
|
+
* analyzer that claims its extension, and collect the resulting nodes/edges
|
|
56
|
+
* into a store. All files for one analyzer are analyzed together so it can
|
|
57
|
+
* resolve cross-file references (e.g. an import's call target).
|
|
58
|
+
*/
|
|
59
|
+
export class Indexer {
|
|
60
|
+
registry;
|
|
61
|
+
createStore;
|
|
62
|
+
constructor(registry,
|
|
63
|
+
/** How to create the backing store for a (resolved) project root — swap this to
|
|
64
|
+
* persist to SQLite. The root is passed so a multi-project session can give each
|
|
65
|
+
* project an independent store; a factory that ignores it and returns one shared
|
|
66
|
+
* store would alias every project onto the last index. (ama-mnj) */
|
|
67
|
+
createStore = () => new InMemoryStore()) {
|
|
68
|
+
this.registry = registry;
|
|
69
|
+
this.createStore = createStore;
|
|
70
|
+
}
|
|
71
|
+
/** The (language, tier) that owns a file, or undefined if no analyzer claims it.
|
|
72
|
+
* Lets a caller recompute per-language coverage live from the current file set,
|
|
73
|
+
* so index_status's census stays correct after incremental syncs — not only
|
|
74
|
+
* after a full index, which is the only thing that writes the cached coverage
|
|
75
|
+
* metadata. (ama-okg) */
|
|
76
|
+
languageOf(rel) {
|
|
77
|
+
const analyzer = this.registry.forFile(rel);
|
|
78
|
+
return analyzer ? { language: analyzer.language, tier: analyzer.tier } : undefined;
|
|
79
|
+
}
|
|
80
|
+
async index(root) {
|
|
81
|
+
// Refuse dangerously broad roots before touching the filesystem.
|
|
82
|
+
assertSafeRoot(root);
|
|
83
|
+
// A clear error beats a raw ENOTDIR/ENOENT when the root isn't a directory.
|
|
84
|
+
if (!fs.statSync(root, { throwIfNoEntry: false })?.isDirectory()) {
|
|
85
|
+
throw new Error(`Not a directory: ${root}`);
|
|
86
|
+
}
|
|
87
|
+
// Discover files BEFORE touching the store: a failing walk must not clear a
|
|
88
|
+
// persistent store reused across indexes — that corrupts the live index.
|
|
89
|
+
// Walking first keeps a failed re-index a no-op.
|
|
90
|
+
const skippedLarge = [];
|
|
91
|
+
const files = discoverFiles(root, (rel) => skippedLarge.push(rel));
|
|
92
|
+
if (skippedLarge.length > 0) {
|
|
93
|
+
// Honest about omissions, like the per-analyzer isolation below: a file too big
|
|
94
|
+
// to parse safely is left out, but said so on stderr (stdout is JSON-RPC only).
|
|
95
|
+
console.error(`[ama] skipped ${skippedLarge.length} file(s) over the ` +
|
|
96
|
+
`${MAX_FILE_SIZE_BYTES / 1024 / 1024} MB parse cap (too large to index): ` +
|
|
97
|
+
skippedLarge.join(", "));
|
|
98
|
+
}
|
|
99
|
+
const store = this.createStore(root);
|
|
100
|
+
store.clear(); // a persistent store may hold a previous index; rebuild clean
|
|
101
|
+
const byAnalyzer = new Map();
|
|
102
|
+
for (const rel of files) {
|
|
103
|
+
const analyzer = this.registry.forFile(rel);
|
|
104
|
+
if (!analyzer)
|
|
105
|
+
continue;
|
|
106
|
+
const list = byAnalyzer.get(analyzer);
|
|
107
|
+
if (list)
|
|
108
|
+
list.push(rel);
|
|
109
|
+
else
|
|
110
|
+
byAnalyzer.set(analyzer, [rel]);
|
|
111
|
+
}
|
|
112
|
+
const languages = [];
|
|
113
|
+
const resolution = { callsTotal: 0, callsResolved: 0, unresolved: {} };
|
|
114
|
+
let fileCount = 0;
|
|
115
|
+
for (const [analyzer, files] of byAnalyzer) {
|
|
116
|
+
// Isolate each analyzer: a crash on one language's batch (a pathological
|
|
117
|
+
// file, an analyzer bug) must not abort the whole index — the other
|
|
118
|
+
// languages still produce a usable graph. The failure is reported to
|
|
119
|
+
// stderr (never silently dropped) and that language is left out of
|
|
120
|
+
// coverage so the index honestly reflects what was analyzed. (ama-m8k.9)
|
|
121
|
+
let result;
|
|
122
|
+
try {
|
|
123
|
+
result = await analyzer.analyze(root, files);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
console.error(`[ama] ${analyzer.language} analyzer failed on ${files.length} file(s); ` +
|
|
127
|
+
`skipping them. ${err instanceof Error ? err.message : String(err)}`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const n of result.nodes)
|
|
131
|
+
store.addNode(n);
|
|
132
|
+
for (const e of result.edges)
|
|
133
|
+
store.addEdge(e);
|
|
134
|
+
for (const rel of files) {
|
|
135
|
+
const meta = fingerprint(root, rel);
|
|
136
|
+
if (meta)
|
|
137
|
+
store.recordFile(meta);
|
|
138
|
+
else
|
|
139
|
+
store.removeFile(rel); // vanished mid-index — drop its just-added nodes
|
|
140
|
+
}
|
|
141
|
+
fileCount += files.length;
|
|
142
|
+
if (result.resolution) {
|
|
143
|
+
resolution.callsTotal += result.resolution.callsTotal;
|
|
144
|
+
resolution.callsResolved += result.resolution.callsResolved;
|
|
145
|
+
for (const [name, n] of Object.entries(result.resolution.unresolved)) {
|
|
146
|
+
resolution.unresolved[name] = (resolution.unresolved[name] ?? 0) + n;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
languages.push({
|
|
150
|
+
language: analyzer.language,
|
|
151
|
+
tier: analyzer.tier,
|
|
152
|
+
files: files.length,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Persist enough to reopen this index next process without re-analyzing:
|
|
156
|
+
// coverage + resolution (for index_status), the root it was built for, and
|
|
157
|
+
// the schema version that wrote it.
|
|
158
|
+
store.setMeta("ama:coverage", JSON.stringify({ fileCount, languages }));
|
|
159
|
+
store.setMeta("ama:resolution", JSON.stringify(resolution));
|
|
160
|
+
store.setMeta("ama:root", root);
|
|
161
|
+
store.setMeta("ama:schema", String(SCHEMA_VERSION));
|
|
162
|
+
return {
|
|
163
|
+
store,
|
|
164
|
+
stats: {
|
|
165
|
+
root,
|
|
166
|
+
nodeCount: store.nodeCount,
|
|
167
|
+
edgeCount: store.edgeCount,
|
|
168
|
+
fileCount,
|
|
169
|
+
languages,
|
|
170
|
+
resolution,
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Reopen a previously-persisted index without re-analyzing: open the store and,
|
|
176
|
+
* if it holds a usable index for `root` (matching schema version and root, with
|
|
177
|
+
* nodes present), reconstruct its {@link IndexStats} from the persisted
|
|
178
|
+
* coverage metadata. Returns undefined — and closes the freshly-opened store —
|
|
179
|
+
* when there is nothing usable (an empty in-memory store, a different root, or
|
|
180
|
+
* an incompatible schema), so the caller falls back to a full {@link index}.
|
|
181
|
+
*/
|
|
182
|
+
async open(root) {
|
|
183
|
+
const store = this.createStore(root);
|
|
184
|
+
const coverageRaw = store.getMeta("ama:coverage");
|
|
185
|
+
const usable = store.getMeta("ama:schema") === String(SCHEMA_VERSION) &&
|
|
186
|
+
store.getMeta("ama:root") === root &&
|
|
187
|
+
store.nodeCount > 0 &&
|
|
188
|
+
coverageRaw !== undefined;
|
|
189
|
+
if (!usable) {
|
|
190
|
+
store.close();
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
const { fileCount, languages } = JSON.parse(coverageRaw);
|
|
194
|
+
// Resolution coverage is additive — an index written before ama-m8k.12 simply
|
|
195
|
+
// lacks it, so it stays undefined rather than gating reopen.
|
|
196
|
+
const resolutionRaw = store.getMeta("ama:resolution");
|
|
197
|
+
const parsedResolution = resolutionRaw
|
|
198
|
+
? JSON.parse(resolutionRaw)
|
|
199
|
+
: undefined;
|
|
200
|
+
// An index written before ama-qbn has no `unresolved` map; default it so the
|
|
201
|
+
// field is always present once `resolution` is.
|
|
202
|
+
const resolution = parsedResolution
|
|
203
|
+
? { ...parsedResolution, unresolved: parsedResolution.unresolved ?? {} }
|
|
204
|
+
: undefined;
|
|
205
|
+
return {
|
|
206
|
+
store,
|
|
207
|
+
stats: {
|
|
208
|
+
root,
|
|
209
|
+
nodeCount: store.nodeCount,
|
|
210
|
+
edgeCount: store.edgeCount,
|
|
211
|
+
fileCount,
|
|
212
|
+
languages,
|
|
213
|
+
...(resolution ? { resolution } : {}),
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Re-index a single changed file into an existing store, in place. Re-analyzes
|
|
219
|
+
* just `rel` and reconciles its delta (so an edit churns only what changed);
|
|
220
|
+
* if `rel` was deleted or is no longer analyzable, its data is dropped instead.
|
|
221
|
+
* Edges from `rel` into files this pass never walks still resolve, because the
|
|
222
|
+
* analyzer falls back to location-derived ids for nodes already in the store.
|
|
223
|
+
*/
|
|
224
|
+
async reindexFile(store, root, rel) {
|
|
225
|
+
const abs = path.resolve(root, rel);
|
|
226
|
+
const analyzer = this.registry.forFile(rel);
|
|
227
|
+
const meta = analyzer && fs.existsSync(abs) ? fingerprint(root, rel) : null;
|
|
228
|
+
if (!analyzer || !meta) {
|
|
229
|
+
store.removeFile(rel); // unhandled language, or the file is gone
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const { nodes, edges } = await analyzer.analyze(root, [rel]);
|
|
233
|
+
store.reconcileFile(rel, nodes, edges);
|
|
234
|
+
store.recordFile(meta);
|
|
235
|
+
}
|
|
236
|
+
// Dispatch fan-out (interface/override) is a whole-graph inference: a single-file
|
|
237
|
+
// analyze can't see other files' implementers, so reconcileFile would drop this
|
|
238
|
+
// file's cross-file dispatch edges. Re-derive them over the full store after the
|
|
239
|
+
// structural change, restoring full-index parity. (ama-tr1)
|
|
240
|
+
redispatch(store);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Catch-up reconcile: compare the tree on disk against the stored fingerprints
|
|
244
|
+
* and re-index everything that drifted — files added or modified since the
|
|
245
|
+
* last index, and files that have since vanished. Detection is cheap
|
|
246
|
+
* (size + mtime, with a content hash only as the tiebreaker), and unchanged
|
|
247
|
+
* files are skipped entirely. The manual counterpart to the live watcher.
|
|
248
|
+
*/
|
|
249
|
+
async sync(store, root) {
|
|
250
|
+
const changed = [];
|
|
251
|
+
const removed = [];
|
|
252
|
+
const current = new Set();
|
|
253
|
+
for (const rel of discoverFiles(root)) {
|
|
254
|
+
if (this.registry.forFile(rel))
|
|
255
|
+
current.add(rel);
|
|
256
|
+
}
|
|
257
|
+
for (const rel of current) {
|
|
258
|
+
const meta = store.getFile(rel);
|
|
259
|
+
if (meta && !isStale(root, rel, meta))
|
|
260
|
+
continue;
|
|
261
|
+
await this.reindexFile(store, root, rel);
|
|
262
|
+
changed.push(rel);
|
|
263
|
+
}
|
|
264
|
+
for (const meta of store.allFiles()) {
|
|
265
|
+
if (!current.has(meta.path)) {
|
|
266
|
+
await this.reindexFile(store, root, meta.path); // gone on disk → removeFile
|
|
267
|
+
removed.push(meta.path);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { changed, removed };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* An indexer wired with the analyzers Ama ships today. Pass a `createStore`
|
|
275
|
+
* factory to persist into SQLite instead of the default in-memory store.
|
|
276
|
+
*/
|
|
277
|
+
export function createDefaultIndexer(createStore) {
|
|
278
|
+
const registry = new AnalyzerRegistry();
|
|
279
|
+
registry.register(new TypeScriptAnalyzer());
|
|
280
|
+
registry.register(new BaselineAnalyzer(pythonSpec));
|
|
281
|
+
registry.register(new BaselineAnalyzer(javascriptSpec));
|
|
282
|
+
registry.register(new BaselineAnalyzer(javaSpec));
|
|
283
|
+
registry.register(new BaselineAnalyzer(csharpSpec));
|
|
284
|
+
registry.register(new BaselineAnalyzer(goSpec));
|
|
285
|
+
registry.register(new BaselineAnalyzer(rustSpec));
|
|
286
|
+
registry.register(new BaselineAnalyzer(phpSpec));
|
|
287
|
+
registry.register(new BaselineAnalyzer(cSpec));
|
|
288
|
+
registry.register(new BaselineAnalyzer(cppSpec));
|
|
289
|
+
registry.register(new BaselineAnalyzer(kotlinSpec));
|
|
290
|
+
registry.register(new BaselineAnalyzer(swiftSpec));
|
|
291
|
+
registry.register(new SfcAnalyzer("vue", [".vue"]));
|
|
292
|
+
registry.register(new SfcAnalyzer("svelte", [".svelte"]));
|
|
293
|
+
return new Indexer(registry, createStore);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Re-derive the whole-graph dispatch edges (interface/override fan-out) over the
|
|
297
|
+
* full store, replacing the prior ones. A full index gets these from the analyzer's
|
|
298
|
+
* per-batch pass, but a single-file reindex can't (it lacks other files' subtypes),
|
|
299
|
+
* so we recompute them store-wide after every reindex — clearing the stale tagged
|
|
300
|
+
* set and re-adding the fresh derivation keeps incremental sync at full-index
|
|
301
|
+
* parity. (ama-tr1) */
|
|
302
|
+
function redispatch(store) {
|
|
303
|
+
const nodes = [...store.allNodes()];
|
|
304
|
+
const base = store.allEdges().filter((e) => e.provenance !== "dispatch");
|
|
305
|
+
store.replaceEdgesByProvenance("dispatch", deriveDispatchEdges(nodes, base));
|
|
306
|
+
}
|
|
307
|
+
/** Fingerprint a file for staleness tracking: size, mtime, and content hash.
|
|
308
|
+
* Returns null when the file has vanished (gone between discovery and now — an
|
|
309
|
+
* editor's atomic save or temp file) so the caller can drop it instead of
|
|
310
|
+
* crashing the index. (ama-7r5) */
|
|
311
|
+
export function fingerprint(root, rel) {
|
|
312
|
+
const abs = path.resolve(root, rel);
|
|
313
|
+
try {
|
|
314
|
+
const stat = fs.statSync(abs);
|
|
315
|
+
const hash = crypto.createHash("sha1").update(fs.readFileSync(abs)).digest("hex");
|
|
316
|
+
return { path: rel, size: stat.size, mtimeMs: stat.mtimeMs, hash };
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Whether a file differs from its recorded fingerprint. Size and mtime are the
|
|
324
|
+
* cheap first check; the content hash is consulted only when they are
|
|
325
|
+
* inconclusive (mtime can change without the bytes changing), so an unchanged
|
|
326
|
+
* file is never re-hashed. A file that has vanished counts as stale, so the
|
|
327
|
+
* caller reindexes it and its `existsSync` check reconciles the removal. (ama-7r5)
|
|
328
|
+
*/
|
|
329
|
+
export function isStale(root, rel, meta) {
|
|
330
|
+
const abs = path.resolve(root, rel);
|
|
331
|
+
let stat;
|
|
332
|
+
try {
|
|
333
|
+
stat = fs.statSync(abs);
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
if (stat.size === meta.size && stat.mtimeMs === meta.mtimeMs)
|
|
339
|
+
return false;
|
|
340
|
+
try {
|
|
341
|
+
const hash = crypto.createHash("sha1").update(fs.readFileSync(abs)).digest("hex");
|
|
342
|
+
return hash !== meta.hash;
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Repo-relative paths of every file under `root`, skipping ignored trees. Files over the
|
|
350
|
+
* parse cap are left out; `onSkipLarge` is invoked with each so a caller can report them
|
|
351
|
+
* instead of dropping them silently. (ama-j0y)
|
|
352
|
+
*/
|
|
353
|
+
function discoverFiles(root, onSkipLarge) {
|
|
354
|
+
const rootRules = loadIgnoreRules(root); // dotfiles + IGNORED_DIRS + the root .gitignore
|
|
355
|
+
const out = [];
|
|
356
|
+
const walk = (dir, dirRel, parent) => {
|
|
357
|
+
// A directory's own .gitignore augments the rules for its subtree, dir-relative
|
|
358
|
+
// (the root's is already folded into `rootRules`). (ama-pyk)
|
|
359
|
+
const rules = dirRel === "" ? parent : withNestedIgnore(dir, dirRel, parent);
|
|
360
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
361
|
+
const abs = path.join(dir, entry.name);
|
|
362
|
+
const rel = path.relative(root, abs);
|
|
363
|
+
// Path-aware so anchored .gitignore patterns (/build, pkg/internal) match
|
|
364
|
+
// root-relatively, not at any depth; covers dotfiles + names/globs too. (ama-yhu)
|
|
365
|
+
if (isIgnoredPath(rel, rules))
|
|
366
|
+
continue;
|
|
367
|
+
if (entry.isDirectory())
|
|
368
|
+
walk(abs, rel, rules);
|
|
369
|
+
else if (entry.isFile()) {
|
|
370
|
+
// Skip oversized files (minified bundles, data blobs) — the same cap the
|
|
371
|
+
// watcher enforces, so the initial index and re-index agree. Report the skip
|
|
372
|
+
// (never silently dropped, like the per-analyzer isolation) so a caller knows a
|
|
373
|
+
// file was omitted. A vanished file is just skipped.
|
|
374
|
+
let size;
|
|
375
|
+
try {
|
|
376
|
+
size = fs.statSync(abs).size;
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (size > MAX_FILE_SIZE_BYTES) {
|
|
382
|
+
onSkipLarge?.(rel);
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
out.push(rel);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
walk(root, "", rootRules);
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
//# sourceMappingURL=indexer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../src/indexer/indexer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a {@link FileWatcher} receives raw change events: given the root and a
|
|
3
|
+
* callback, wire up event delivery and return a handle to stop it. Injectable
|
|
4
|
+
* so tests can drive events synchronously instead of waiting on OS file-event
|
|
5
|
+
* latency (the source of flaky timing tests); the default is {@link fsWatchSource}.
|
|
6
|
+
*/
|
|
7
|
+
export type WatchSource = (root: string, onEvent: (rel: string) => void) => {
|
|
8
|
+
close(): void;
|
|
9
|
+
};
|
|
10
|
+
export interface FileWatcherOptions {
|
|
11
|
+
/** Files larger than this are not reported (default 1 MB). */
|
|
12
|
+
maxFileSizeBytes?: number;
|
|
13
|
+
/** Event source (default: fs.watch). Tests inject a synchronous source. */
|
|
14
|
+
source?: WatchSource;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Recursively watches a directory and reports each file that changes, as a
|
|
18
|
+
* repo-relative path, applying the same ignore rules as the indexer (dot-paths,
|
|
19
|
+
* `node_modules`/`dist`/… , and files over a size cap). It does *not* classify
|
|
20
|
+
* create vs. modify vs. delete — the consumer re-indexes the path and lets the
|
|
21
|
+
* indexer decide (a vanished file is reconciled away). Debouncing bursts of
|
|
22
|
+
* edits is a separate concern (ama-gd5.3); this emits raw change events.
|
|
23
|
+
*
|
|
24
|
+
* Built on Node's native `fs.watch` to avoid a dependency. Recursive watching
|
|
25
|
+
* is supported on macOS and Windows; on Linux the recursive option may not be
|
|
26
|
+
* available, in which case this watches only the top level.
|
|
27
|
+
*/
|
|
28
|
+
export declare class FileWatcher {
|
|
29
|
+
private readonly root;
|
|
30
|
+
private readonly onChange;
|
|
31
|
+
private subscription?;
|
|
32
|
+
private readonly maxFileSizeBytes;
|
|
33
|
+
private readonly source;
|
|
34
|
+
/** Loaded once so the watched set matches what the index built (incl .gitignore). */
|
|
35
|
+
private readonly ignoreRules;
|
|
36
|
+
/** Per-directory accumulated rules (root + each ancestor's nested .gitignore),
|
|
37
|
+
* memoized so a burst of events in one directory reads each .gitignore once. */
|
|
38
|
+
private readonly rulesByDir;
|
|
39
|
+
constructor(root: string, onChange: (rel: string) => void, options?: FileWatcherOptions);
|
|
40
|
+
start(): void;
|
|
41
|
+
close(): void;
|
|
42
|
+
/** Ignore rules in effect inside `dirRel`: the root rules plus every ancestor
|
|
43
|
+
* directory's nested .gitignore, each rebased to its directory, so a changed
|
|
44
|
+
* file is judged exactly as the discovery walk would (ama-pyk). Memoized per
|
|
45
|
+
* directory; like the root rules, a .gitignore edited after start isn't
|
|
46
|
+
* reloaded — restart the watcher for that. (ama-ezf) */
|
|
47
|
+
private rulesForDir;
|
|
48
|
+
private handle;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/indexer/watcher.ts"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,KAAK;IAAE,KAAK,IAAI,IAAI,CAAA;CAAE,CAAC;AAU9F,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;IAWpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAX3B,OAAO,CAAC,YAAY,CAAC,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C;qFACiF;IACjF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;gBAG1C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAChD,OAAO,GAAE,kBAAuB;IAOlC,KAAK,IAAI,IAAI;IAKb,KAAK,IAAI,IAAI;IAKb;;;;6DAIyD;IACzD,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,MAAM;CAef"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { MAX_FILE_SIZE_BYTES, isIgnoredPath, loadIgnoreRules, withNestedIgnore, } from "./ignore.js";
|
|
4
|
+
/** The production source: Node's native recursive `fs.watch`. */
|
|
5
|
+
const fsWatchSource = (root, onEvent) => {
|
|
6
|
+
const watcher = fs.watch(root, { recursive: true }, (_event, filename) => {
|
|
7
|
+
if (filename !== null)
|
|
8
|
+
onEvent(filename.toString());
|
|
9
|
+
});
|
|
10
|
+
return { close: () => watcher.close() };
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Recursively watches a directory and reports each file that changes, as a
|
|
14
|
+
* repo-relative path, applying the same ignore rules as the indexer (dot-paths,
|
|
15
|
+
* `node_modules`/`dist`/… , and files over a size cap). It does *not* classify
|
|
16
|
+
* create vs. modify vs. delete — the consumer re-indexes the path and lets the
|
|
17
|
+
* indexer decide (a vanished file is reconciled away). Debouncing bursts of
|
|
18
|
+
* edits is a separate concern (ama-gd5.3); this emits raw change events.
|
|
19
|
+
*
|
|
20
|
+
* Built on Node's native `fs.watch` to avoid a dependency. Recursive watching
|
|
21
|
+
* is supported on macOS and Windows; on Linux the recursive option may not be
|
|
22
|
+
* available, in which case this watches only the top level.
|
|
23
|
+
*/
|
|
24
|
+
export class FileWatcher {
|
|
25
|
+
root;
|
|
26
|
+
onChange;
|
|
27
|
+
subscription;
|
|
28
|
+
maxFileSizeBytes;
|
|
29
|
+
source;
|
|
30
|
+
/** Loaded once so the watched set matches what the index built (incl .gitignore). */
|
|
31
|
+
ignoreRules;
|
|
32
|
+
/** Per-directory accumulated rules (root + each ancestor's nested .gitignore),
|
|
33
|
+
* memoized so a burst of events in one directory reads each .gitignore once. */
|
|
34
|
+
rulesByDir = new Map();
|
|
35
|
+
constructor(root, onChange, options = {}) {
|
|
36
|
+
this.root = root;
|
|
37
|
+
this.onChange = onChange;
|
|
38
|
+
this.maxFileSizeBytes = options.maxFileSizeBytes ?? MAX_FILE_SIZE_BYTES;
|
|
39
|
+
this.source = options.source ?? fsWatchSource;
|
|
40
|
+
this.ignoreRules = loadIgnoreRules(root);
|
|
41
|
+
}
|
|
42
|
+
start() {
|
|
43
|
+
if (this.subscription)
|
|
44
|
+
return;
|
|
45
|
+
this.subscription = this.source(this.root, (rel) => this.handle(rel));
|
|
46
|
+
}
|
|
47
|
+
close() {
|
|
48
|
+
this.subscription?.close();
|
|
49
|
+
this.subscription = undefined;
|
|
50
|
+
}
|
|
51
|
+
/** Ignore rules in effect inside `dirRel`: the root rules plus every ancestor
|
|
52
|
+
* directory's nested .gitignore, each rebased to its directory, so a changed
|
|
53
|
+
* file is judged exactly as the discovery walk would (ama-pyk). Memoized per
|
|
54
|
+
* directory; like the root rules, a .gitignore edited after start isn't
|
|
55
|
+
* reloaded — restart the watcher for that. (ama-ezf) */
|
|
56
|
+
rulesForDir(dirRel) {
|
|
57
|
+
if (dirRel === "" || dirRel === ".")
|
|
58
|
+
return this.ignoreRules;
|
|
59
|
+
const cached = this.rulesByDir.get(dirRel);
|
|
60
|
+
if (cached)
|
|
61
|
+
return cached;
|
|
62
|
+
const parent = path.dirname(dirRel);
|
|
63
|
+
const rules = withNestedIgnore(path.join(this.root, dirRel), dirRel, this.rulesForDir(parent === "." ? "" : parent));
|
|
64
|
+
this.rulesByDir.set(dirRel, rules);
|
|
65
|
+
return rules;
|
|
66
|
+
}
|
|
67
|
+
handle(rel) {
|
|
68
|
+
const dir = path.dirname(rel);
|
|
69
|
+
if (isIgnoredPath(rel, this.rulesForDir(dir === "." ? "" : dir)))
|
|
70
|
+
return;
|
|
71
|
+
let stat;
|
|
72
|
+
try {
|
|
73
|
+
stat = fs.statSync(path.join(this.root, rel));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// The path is gone (a deletion) — still report it so the consumer can drop it.
|
|
77
|
+
this.onChange(rel);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// A directory event or an oversized file is not something to re-index.
|
|
81
|
+
if (!stat.isFile() || stat.size > this.maxFileSizeBytes)
|
|
82
|
+
return;
|
|
83
|
+
this.onChange(rel);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/indexer/watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stamp identifying the running server build, surfaced on `index_status` so a
|
|
3
|
+
* caller (e.g. the self-improvement loop's Step 0) can detect a stale server —
|
|
4
|
+
* one started before the latest commit. It is captured ONCE at module load, so
|
|
5
|
+
* `revision` reflects the code the process was launched with, not live HEAD: if
|
|
6
|
+
* you commit without restarting, the stamp lags HEAD and the staleness shows.
|
|
7
|
+
*/
|
|
8
|
+
export interface ServerStamp {
|
|
9
|
+
/** Package version from package.json. */
|
|
10
|
+
version: string;
|
|
11
|
+
/** Git HEAD revision at server start, or null when run outside a git repo. */
|
|
12
|
+
revision: string | null;
|
|
13
|
+
}
|
|
14
|
+
/** Captured once at module load — the code the running server was started with. */
|
|
15
|
+
export declare const serverStamp: ServerStamp;
|
|
16
|
+
//# sourceMappingURL=build-info.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-info.d.ts","sourceRoot":"","sources":["../../src/mcp/build-info.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AA8CD,mFAAmF;AACnF,eAAO,MAAM,WAAW,EAAE,WAGzB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
// src/mcp/build-info.ts and dist/mcp/build-info.js both sit two levels under
|
|
6
|
+
// the repo root, so the same relative hop finds package.json and .git either way.
|
|
7
|
+
const repoRoot = path.resolve(here, "../..");
|
|
8
|
+
function readVersion(root) {
|
|
9
|
+
try {
|
|
10
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
11
|
+
return pkg.version ?? "0.0.0";
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return "0.0.0";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Resolve HEAD to a commit SHA from the filesystem (loose ref, then packed-refs). */
|
|
18
|
+
function readRevision(root) {
|
|
19
|
+
try {
|
|
20
|
+
const gitDir = path.join(root, ".git");
|
|
21
|
+
const head = fs.readFileSync(path.join(gitDir, "HEAD"), "utf8").trim();
|
|
22
|
+
const ref = head.match(/^ref:\s*(.+)$/)?.[1];
|
|
23
|
+
if (!ref) {
|
|
24
|
+
// Detached HEAD: the HEAD file holds the SHA directly.
|
|
25
|
+
return /^[0-9a-f]{40}$/.test(head) ? head : null;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const loose = fs.readFileSync(path.join(gitDir, ref), "utf8").trim();
|
|
29
|
+
if (/^[0-9a-f]{40}$/.test(loose))
|
|
30
|
+
return loose;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// No loose ref file — fall through to packed-refs.
|
|
34
|
+
}
|
|
35
|
+
const packed = fs.readFileSync(path.join(gitDir, "packed-refs"), "utf8");
|
|
36
|
+
for (const line of packed.split("\n")) {
|
|
37
|
+
if (!line || line.startsWith("#") || line.startsWith("^"))
|
|
38
|
+
continue;
|
|
39
|
+
const [sha, name] = line.split(" ");
|
|
40
|
+
if (name === ref && sha)
|
|
41
|
+
return sha;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Captured once at module load — the code the running server was started with. */
|
|
50
|
+
export const serverStamp = {
|
|
51
|
+
version: readVersion(repoRoot),
|
|
52
|
+
revision: readRevision(repoRoot),
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=build-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-info.js","sourceRoot":"","sources":["../../src/mcp/build-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAgBzC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,6EAA6E;AAC7E,kFAAkF;AAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAE7C,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAE9E,CAAC;QACF,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACvE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,uDAAuD;YACvD,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,CAAC;QACD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACrE,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,mDAAmD;QACrD,CAAC;QACD,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAC;QACzE,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YACpE,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,IAAI,KAAK,GAAG,IAAI,GAAG;gBAAE,OAAO,GAAG,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC;IAC9B,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC;CACjC,CAAC"}
|