@neat.is/core 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/compat.json +120 -0
  2. package/dist/chunk-6JT6L2OV.js +164 -0
  3. package/dist/chunk-6JT6L2OV.js.map +1 -0
  4. package/dist/chunk-6SFEITLJ.js +3371 -0
  5. package/dist/chunk-6SFEITLJ.js.map +1 -0
  6. package/dist/chunk-I5IMCXRO.js +325 -0
  7. package/dist/chunk-I5IMCXRO.js.map +1 -0
  8. package/dist/chunk-T2U4U256.js +462 -0
  9. package/dist/chunk-T2U4U256.js.map +1 -0
  10. package/dist/chunk-WX55TLUT.js +184 -0
  11. package/dist/chunk-WX55TLUT.js.map +1 -0
  12. package/dist/chunk-XOOCA5T7.js +290 -0
  13. package/dist/chunk-XOOCA5T7.js.map +1 -0
  14. package/dist/cli.cjs +5754 -0
  15. package/dist/cli.cjs.map +1 -0
  16. package/dist/cli.d.cts +36 -0
  17. package/dist/cli.d.ts +36 -0
  18. package/dist/cli.js +1175 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/index.cjs +4552 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +408 -0
  23. package/dist/index.d.ts +408 -0
  24. package/dist/index.js +93 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/neatd.cjs +3070 -0
  27. package/dist/neatd.cjs.map +1 -0
  28. package/dist/neatd.d.cts +1 -0
  29. package/dist/neatd.d.ts +1 -0
  30. package/dist/neatd.js +114 -0
  31. package/dist/neatd.js.map +1 -0
  32. package/dist/otel-grpc-B4XBSI4W.js +9 -0
  33. package/dist/otel-grpc-B4XBSI4W.js.map +1 -0
  34. package/dist/server.cjs +4499 -0
  35. package/dist/server.cjs.map +1 -0
  36. package/dist/server.d.cts +2 -0
  37. package/dist/server.d.ts +2 -0
  38. package/dist/server.js +97 -0
  39. package/dist/server.js.map +1 -0
  40. package/package.json +77 -0
  41. package/proto/opentelemetry/proto/collector/trace/v1/trace_service.proto +31 -0
  42. package/proto/opentelemetry/proto/common/v1/common.proto +46 -0
  43. package/proto/opentelemetry/proto/resource/v1/resource.proto +19 -0
  44. package/proto/opentelemetry/proto/trace/v1/trace.proto +93 -0
@@ -0,0 +1,408 @@
1
+ import { MultiDirectedGraph } from 'graphology';
2
+ import { GraphNode, GraphEdge, ErrorEvent, BlastRadiusResult, RootCauseResult, RegistryEntry, RegistryStatus, RegistryFile } from '@neat.is/types';
3
+ import { FastifyInstance } from 'fastify';
4
+
5
+ type NeatGraph = MultiDirectedGraph<GraphNode, GraphEdge>;
6
+ declare function getGraph(project?: string): NeatGraph;
7
+ declare function resetGraph(project?: string): void;
8
+
9
+ interface ExtractResult {
10
+ nodesAdded: number;
11
+ edgesAdded: number;
12
+ frontiersPromoted: number;
13
+ }
14
+ interface ExtractOptions {
15
+ onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void;
16
+ }
17
+ declare function extractFromDirectory(graph: NeatGraph, scanPath: string, opts?: ExtractOptions): Promise<ExtractResult>;
18
+
19
+ interface CompatibilityResult {
20
+ compatible: boolean;
21
+ reason?: string;
22
+ minDriverVersion?: string;
23
+ }
24
+ interface CompatPair {
25
+ kind?: 'driver-engine';
26
+ driver: string;
27
+ engine: string;
28
+ minDriverVersion: string;
29
+ minEngineVersion?: string;
30
+ reason: string;
31
+ }
32
+ declare function checkCompatibility(driver: string, driverVersion: string, engine: string, engineVersion: string): CompatibilityResult;
33
+ declare function compatPairs(): readonly CompatPair[];
34
+
35
+ declare function saveGraphToDisk(graph: NeatGraph, outPath: string): Promise<void>;
36
+ declare function loadGraphFromDisk(graph: NeatGraph, outPath: string): Promise<void>;
37
+ declare function startPersistLoop(graph: NeatGraph, outPath: string, intervalMs?: number): () => void;
38
+
39
+ interface ScoredNode {
40
+ node: GraphNode;
41
+ score: number;
42
+ }
43
+ interface SearchResponse {
44
+ query: string;
45
+ provider: 'ollama' | 'transformers' | 'substring';
46
+ matches: ScoredNode[];
47
+ }
48
+ interface SearchIndex {
49
+ readonly provider: SearchResponse['provider'];
50
+ search(query: string, limit?: number): Promise<SearchResponse>;
51
+ refresh(graph: NeatGraph): Promise<void>;
52
+ }
53
+
54
+ interface ProjectPaths {
55
+ snapshotPath: string;
56
+ errorsPath: string;
57
+ staleEventsPath: string;
58
+ embeddingsCachePath: string;
59
+ policyViolationsPath: string;
60
+ }
61
+ interface ProjectContext {
62
+ name: string;
63
+ graph: NeatGraph;
64
+ scanPath?: string;
65
+ paths: ProjectPaths;
66
+ searchIndex?: SearchIndex;
67
+ }
68
+ declare class Projects {
69
+ private contexts;
70
+ upsert(ctx: ProjectContext): void;
71
+ set(name: string, init: Omit<ProjectContext, 'name' | 'graph'> & {
72
+ graph?: NeatGraph;
73
+ }): ProjectContext;
74
+ get(name: string): ProjectContext | undefined;
75
+ has(name: string): boolean;
76
+ list(): string[];
77
+ attachSearchIndex(name: string, index: SearchIndex | undefined): void;
78
+ }
79
+
80
+ interface BuildApiOptions {
81
+ projects?: Projects;
82
+ startedAt?: number;
83
+ graph?: NeatGraph;
84
+ scanPath?: string;
85
+ errorsPath?: string;
86
+ staleEventsPath?: string;
87
+ searchIndex?: SearchIndex;
88
+ }
89
+ declare function buildApi(opts: BuildApiOptions): Promise<FastifyInstance>;
90
+
91
+ interface ParsedSpan {
92
+ service: string;
93
+ traceId: string;
94
+ spanId: string;
95
+ parentSpanId?: string;
96
+ name: string;
97
+ kind?: number;
98
+ startTimeUnixNano: string;
99
+ endTimeUnixNano: string;
100
+ startTimeIso?: string;
101
+ durationNanos: bigint;
102
+ attributes: Record<string, AttributeValue>;
103
+ dbSystem?: string;
104
+ dbName?: string;
105
+ statusCode?: number;
106
+ errorMessage?: string;
107
+ exception?: {
108
+ type?: string;
109
+ message?: string;
110
+ stacktrace?: string;
111
+ };
112
+ }
113
+ type AttributeValue = string | number | boolean | bigint | string[] | number[] | boolean[] | null;
114
+ type SpanHandler = (span: ParsedSpan) => void | Promise<void>;
115
+ interface BuildOtelReceiverOptions {
116
+ onSpan: SpanHandler;
117
+ onErrorSpanSync?: (span: ParsedSpan) => Promise<void>;
118
+ bodyLimit?: number;
119
+ }
120
+ interface OtlpKeyValue {
121
+ key: string;
122
+ value?: OtlpAnyValue;
123
+ }
124
+ interface OtlpAnyValue {
125
+ stringValue?: string;
126
+ intValue?: string | number;
127
+ doubleValue?: number;
128
+ boolValue?: boolean;
129
+ arrayValue?: {
130
+ values?: OtlpAnyValue[];
131
+ };
132
+ }
133
+ interface OtlpStatus {
134
+ code?: number;
135
+ message?: string;
136
+ }
137
+ interface OtlpEvent {
138
+ name?: string;
139
+ timeUnixNano?: string;
140
+ attributes?: OtlpKeyValue[];
141
+ }
142
+ interface OtlpSpan {
143
+ traceId?: string;
144
+ spanId?: string;
145
+ parentSpanId?: string;
146
+ name?: string;
147
+ kind?: number;
148
+ startTimeUnixNano?: string;
149
+ endTimeUnixNano?: string;
150
+ attributes?: OtlpKeyValue[];
151
+ events?: OtlpEvent[];
152
+ status?: OtlpStatus;
153
+ }
154
+ interface OtlpScopeSpans {
155
+ spans?: OtlpSpan[];
156
+ }
157
+ interface OtlpResourceSpans {
158
+ resource?: {
159
+ attributes?: OtlpKeyValue[];
160
+ };
161
+ scopeSpans?: OtlpScopeSpans[];
162
+ }
163
+ interface OtlpTracesRequest {
164
+ resourceSpans?: OtlpResourceSpans[];
165
+ }
166
+ declare function parseOtlpRequest(body: OtlpTracesRequest): ParsedSpan[];
167
+ declare function buildOtelReceiver(opts: BuildOtelReceiverOptions): Promise<FastifyInstance & {
168
+ flushPending: () => Promise<void>;
169
+ }>;
170
+ declare function logSpanHandler(span: ParsedSpan): void;
171
+
172
+ interface BuildOtelGrpcReceiverOptions {
173
+ onSpan: SpanHandler;
174
+ }
175
+ interface OtelGrpcReceiver {
176
+ address: string;
177
+ stop: () => Promise<void>;
178
+ }
179
+ declare function startOtelGrpcReceiver(opts: BuildOtelGrpcReceiverOptions & {
180
+ host?: string;
181
+ port?: number;
182
+ }): Promise<OtelGrpcReceiver>;
183
+
184
+ interface IngestContext {
185
+ graph: NeatGraph;
186
+ errorsPath: string;
187
+ now?: () => number;
188
+ writeErrorEventInline?: boolean;
189
+ onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void;
190
+ }
191
+ declare function thresholdForEdgeType(edgeType: string, overrides?: Record<string, number>): number;
192
+ declare function stitchTrace(graph: NeatGraph, sourceServiceId: string, ts: string): void;
193
+ declare function handleSpan(ctx: IngestContext, span: ParsedSpan): Promise<void>;
194
+
195
+ declare function makeSpanHandler(ctx: IngestContext): (span: ParsedSpan) => Promise<void>;
196
+ interface StaleEvent {
197
+ edgeId: string;
198
+ source: string;
199
+ target: string;
200
+ edgeType: string;
201
+ thresholdMs: number;
202
+ ageMs: number;
203
+ lastObserved: string;
204
+ transitionedAt: string;
205
+ }
206
+ interface MarkStaleOptions {
207
+ thresholds?: Record<string, number>;
208
+ now?: number;
209
+ staleEventsPath?: string;
210
+ }
211
+ declare function markStaleEdges(graph: NeatGraph, options?: MarkStaleOptions): Promise<{
212
+ count: number;
213
+ events: StaleEvent[];
214
+ }>;
215
+ declare function readStaleEvents(staleEventsPath: string): Promise<StaleEvent[]>;
216
+ interface StalenessLoopOptions {
217
+ thresholds?: Record<string, number>;
218
+ intervalMs?: number;
219
+ staleEventsPath?: string;
220
+ onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void;
221
+ }
222
+ declare function startStalenessLoop(graph: NeatGraph, options?: StalenessLoopOptions): () => void;
223
+ declare function readErrorEvents(errorsPath: string): Promise<ErrorEvent[]>;
224
+
225
+ declare function confidenceForEdge(edge: GraphEdge, now?: number): number;
226
+ declare function getRootCause(graph: NeatGraph, errorNodeId: string, errorEvent?: ErrorEvent): RootCauseResult | null;
227
+ declare function getBlastRadius(graph: NeatGraph, nodeId: string, maxDepth?: number): BlastRadiusResult;
228
+
229
+ interface PersistedNodeEntry {
230
+ key?: string;
231
+ attributes?: Record<string, unknown>;
232
+ }
233
+ interface PersistedEdgeEntry {
234
+ key?: string;
235
+ source?: string;
236
+ target?: string;
237
+ attributes?: Record<string, unknown>;
238
+ }
239
+ interface PersistedSnapshot {
240
+ schemaVersion?: number;
241
+ exportedAt?: string;
242
+ graph?: {
243
+ nodes?: PersistedNodeEntry[];
244
+ edges?: PersistedEdgeEntry[];
245
+ };
246
+ }
247
+ interface GraphDiff {
248
+ base: {
249
+ exportedAt?: string;
250
+ };
251
+ current: {
252
+ exportedAt: string;
253
+ };
254
+ added: {
255
+ nodes: GraphNode[];
256
+ edges: GraphEdge[];
257
+ };
258
+ removed: {
259
+ nodes: GraphNode[];
260
+ edges: GraphEdge[];
261
+ };
262
+ changed: {
263
+ nodes: {
264
+ id: string;
265
+ before: GraphNode;
266
+ after: GraphNode;
267
+ }[];
268
+ edges: {
269
+ id: string;
270
+ before: GraphEdge;
271
+ after: GraphEdge;
272
+ }[];
273
+ };
274
+ }
275
+ declare function loadSnapshotForDiff(target: string): Promise<PersistedSnapshot>;
276
+ declare function computeGraphDiff(liveGraph: NeatGraph, baseSnapshot: PersistedSnapshot, currentExportedAt?: string): GraphDiff;
277
+
278
+ /**
279
+ * Multi-project daemon (ADR-049).
280
+ *
281
+ * Single long-lived process watching every project in the machine-level registry.
282
+ * Per-project graph isolation: each registered project owns its own
283
+ * `MultiDirectedGraph` slot keyed by name (ADR-026), and a failure during
284
+ * one project's bootstrap is logged + marked `broken` without taking down
285
+ * the rest of the daemon.
286
+ *
287
+ * MVP scope (v0.2.5):
288
+ * - Read registry; refuse to boot when it's missing.
289
+ * - Write PID at `~/.neat/neatd.pid` for external supervisors.
290
+ * - Per project: load any existing snapshot, run initial extraction,
291
+ * start a per-project persist loop.
292
+ * - SIGHUP triggers a reload — re-reads the registry, picks up new
293
+ * projects, drops removed ones, leaves untouched ones in place.
294
+ * - Provide `routeSpanToProject(serviceName, projects)` for OTel ingest
295
+ * to dispatch by `service.name` across registered projects, falling
296
+ * back to `default` for unknown services per ADR-033.
297
+ *
298
+ * Out of MVP scope (deferred):
299
+ * - Live OTel listener wiring per project — daemon exposes the routing
300
+ * primitive; the actual receiver attachment lands alongside v0.2.6.
301
+ * - Policy reload on `policy.json` mtime — `startWatch` already does this
302
+ * per-project; the daemon-level loop reuses that machinery in a follow-up.
303
+ * - Auto-restart on crash. PID file is the supervisor handoff.
304
+ */
305
+
306
+ interface DaemonOptions {
307
+ neatHome?: string;
308
+ }
309
+ interface ProjectSlot {
310
+ entry: RegistryEntry;
311
+ graph: NeatGraph;
312
+ outPath: string;
313
+ stopPersist: () => void;
314
+ status: 'active' | 'broken';
315
+ errorReason?: string;
316
+ }
317
+ interface DaemonHandle {
318
+ slots: Map<string, ProjectSlot>;
319
+ reload: () => Promise<void>;
320
+ stop: () => Promise<void>;
321
+ pidPath: string;
322
+ }
323
+ /**
324
+ * Resolve which project's graph an OTel span belongs to. Looks up the
325
+ * `service.name` against the registry and returns the matching project's
326
+ * name, or `DEFAULT_PROJECT` for unknown services so the FrontierNode
327
+ * auto-creation flow keeps working per ADR-033.
328
+ *
329
+ * Pure function. Daemon callers pass a snapshot of the registry to avoid
330
+ * per-span fs reads.
331
+ */
332
+ declare function routeSpanToProject(serviceName: string | undefined, projects: ReadonlyArray<RegistryEntry>): string;
333
+ declare function startDaemon(opts?: DaemonOptions): Promise<DaemonHandle>;
334
+
335
+ /**
336
+ * Machine-level project registry (ADR-048).
337
+ *
338
+ * One file: `~/.neat/projects.json`. Per-user, machine-local. Not synced.
339
+ * `registry.ts` is the only module that opens it. Everything else — `init`,
340
+ * `daemon`, `cli` — calls into the helpers below.
341
+ *
342
+ * Two safety properties matter:
343
+ * 1. Atomic writes. We tmp + fsync + rename so the daemon never sees a torn
344
+ * file when init races against it.
345
+ * 2. Cross-process exclusion. We hold an exclusive lock on
346
+ * `~/.neat/projects.json.lock` for the read-modify-write window. Two
347
+ * concurrent `neat init` runs cannot both win and overwrite each other.
348
+ *
349
+ * The lock is a file we exclusively-create (`O_EXCL`), hold while we mutate,
350
+ * and unlink on the way out. Crude but cross-platform; matches what
351
+ * `proper-lockfile` does internally without pulling the dep in.
352
+ */
353
+
354
+ declare function registryPath(): string;
355
+ declare function registryLockPath(): string;
356
+ /**
357
+ * Path normalisation per ADR-048 #7. Two `init` calls from different relative
358
+ * paths to the same dir must collapse to one entry. `path.resolve` handles
359
+ * relative-to-cwd; we pass it through `fs.realpath` when the dir exists so
360
+ * symlinked paths land on the same canonical entry too.
361
+ */
362
+ declare function normalizeProjectPath(input: string): Promise<string>;
363
+ /**
364
+ * tmp + fsync + rename. The fsync on the data fd guarantees the bytes are on
365
+ * disk before rename swaps the inode; rename itself is atomic on POSIX.
366
+ *
367
+ * Exported so the init flow and test harnesses can use the same helper.
368
+ */
369
+ declare function writeAtomically(target: string, contents: string): Promise<void>;
370
+ /**
371
+ * Read the registry from disk. Returns an empty registry if the file does
372
+ * not exist yet — first run, never registered anything.
373
+ *
374
+ * Throws on parse / schema errors. The contract is single-source-of-truth;
375
+ * a corrupt file is louder than a silent reset.
376
+ */
377
+ declare function readRegistry(): Promise<RegistryFile>;
378
+ interface AddProjectOptions {
379
+ name: string;
380
+ path: string;
381
+ languages?: string[];
382
+ status?: RegistryStatus;
383
+ }
384
+ declare class ProjectNameCollisionError extends Error {
385
+ readonly projectName: string;
386
+ constructor(name: string);
387
+ }
388
+ /**
389
+ * Register a project, or update its `lastSeenAt` if the same path is already
390
+ * registered under the same name (idempotent re-init).
391
+ *
392
+ * Hard error on name collision against a different path — ADR-046 #7. The
393
+ * caller can recover by passing `--project <new-name>`.
394
+ */
395
+ declare function addProject(opts: AddProjectOptions): Promise<RegistryEntry>;
396
+ declare function getProject(name: string): Promise<RegistryEntry | undefined>;
397
+ declare function listProjects(): Promise<RegistryEntry[]>;
398
+ declare function setStatus(name: string, status: RegistryStatus): Promise<RegistryEntry>;
399
+ declare function touchLastSeen(name: string, at?: string): Promise<void>;
400
+ /**
401
+ * Remove the registry entry for `name`. Per ADR-048 #6: this only removes the
402
+ * registry row. It does **not** touch `neat-out/`, `policy.json`, or any user
403
+ * file in the project directory. SDK-install rollback is a separate flow
404
+ * (`neat-rollback.patch`) that the caller opts in to.
405
+ */
406
+ declare function removeProject(name: string): Promise<RegistryEntry | undefined>;
407
+
408
+ export { type BuildApiOptions, type BuildOtelGrpcReceiverOptions, type BuildOtelReceiverOptions, type CompatPair, type CompatibilityResult, type DaemonHandle, type DaemonOptions, type ExtractResult, type GraphDiff, type IngestContext, type MarkStaleOptions, type NeatGraph, type OtelGrpcReceiver, type OtlpTracesRequest, type ParsedSpan, type PersistedSnapshot, ProjectNameCollisionError, type ProjectSlot, type SpanHandler, type StaleEvent, type StalenessLoopOptions, addProject, buildApi, buildOtelReceiver, checkCompatibility, compatPairs, computeGraphDiff, confidenceForEdge, extractFromDirectory, getBlastRadius, getGraph, getProject, getRootCause, handleSpan, listProjects, loadGraphFromDisk, loadSnapshotForDiff, logSpanHandler, makeSpanHandler, markStaleEdges, normalizeProjectPath, parseOtlpRequest, readErrorEvents, readRegistry, readStaleEvents, registryLockPath, registryPath, removeProject, resetGraph, routeSpanToProject, saveGraphToDisk, setStatus, startDaemon, startOtelGrpcReceiver, startPersistLoop, startStalenessLoop, stitchTrace, thresholdForEdgeType, touchLastSeen, writeAtomically };