@neat.is/core 0.4.16 → 0.4.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -238,8 +238,16 @@ interface IngestContext {
238
238
  now?: () => number;
239
239
  writeErrorEventInline?: boolean;
240
240
  onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void;
241
+ burstState?: Map<string, BurstState>;
241
242
  }
242
243
  declare function thresholdForEdgeType(edgeType: string, overrides?: Record<string, number>): number;
244
+ interface BurstState {
245
+ count: number;
246
+ firstTs: string;
247
+ lastTs: string;
248
+ lastMs: number;
249
+ codes: Map<number, number>;
250
+ }
243
251
  declare function stitchTrace(graph: NeatGraph, sourceServiceId: string, ts: string): void;
244
252
  declare function handleSpan(ctx: IngestContext, span: ParsedSpan): Promise<void>;
245
253
 
@@ -347,8 +355,25 @@ declare function computeGraphDiff(liveGraph: NeatGraph, baseSnapshot: PersistedS
347
355
  * - Auto-restart on crash. PID file is the supervisor handoff.
348
356
  */
349
357
 
358
+ interface DaemonPorts {
359
+ rest: number;
360
+ otlp: number;
361
+ web: number;
362
+ }
363
+ interface DaemonRecord {
364
+ project: string;
365
+ projectPath: string;
366
+ pid: number;
367
+ status: 'running' | 'stopped';
368
+ ports: DaemonPorts;
369
+ startedAt: string;
370
+ neatVersion: string;
371
+ }
350
372
  interface DaemonOptions {
351
373
  neatHome?: string;
374
+ project?: string;
375
+ projectPath?: string;
376
+ webPort?: number;
352
377
  restPort?: number;
353
378
  otlpPort?: number;
354
379
  host?: string;
@@ -382,6 +407,7 @@ interface DaemonHandle {
382
407
  otlpAddress: string;
383
408
  bootstrap: BootstrapTracker;
384
409
  initialBootstrap: Promise<void>;
410
+ daemonRecord: DaemonRecord | null;
385
411
  }
386
412
  /**
387
413
  * Resolve which project's graph an OTel span belongs to. Looks up the
@@ -419,22 +445,34 @@ declare function routeSpanToProject(serviceName: string | undefined, projects: R
419
445
  declare function startDaemon(opts?: DaemonOptions): Promise<DaemonHandle>;
420
446
 
421
447
  /**
422
- * Machine-level project registry (ADR-048).
448
+ * Machine-level project registry + machine-wide daemon discovery.
449
+ *
450
+ * This module owns two surfaces under `~/.neat/`:
451
+ *
452
+ * 1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One
453
+ * file, per-user, machine-local, not synced. Under the project-daemon
454
+ * contract (ADR-096) it is no longer the coordination point — it is read
455
+ * once for migration and otherwise left to the additive writes the daemon
456
+ * and orchestrator still perform. The read-modify-write helpers below keep
457
+ * their atomic-write + exclusive-lock machinery for that legacy surface.
423
458
  *
424
- * One file: `~/.neat/projects.json`. Per-user, machine-local. Not synced.
425
- * `registry.ts` is the only module that opens it. Everything else — `init`,
426
- * `daemon`, `cli` calls into the helpers below.
459
+ * 2. The machine-wide daemon discovery directory at `~/.neat/daemons/`
460
+ * (ADR-096 §6). One file per running daemon `<project>.json` each owned
461
+ * solely by the daemon that wrote it (on start) and removes it (on graceful
462
+ * stop). Discovery is **append-only and lock-free**: a reader scans the
463
+ * directory and reconciles liveness; it never acquires a shared lock, so it
464
+ * can never deadlock against a daemon (#506). Losing or rebuilding the
465
+ * directory costs discovery convenience, not correctness — each project's
466
+ * own `neat-out/daemon.json` stays authoritative.
427
467
  *
428
- * Two safety properties matter:
429
- * 1. Atomic writes. We tmp + fsync + rename so the daemon never sees a torn
430
- * file when init races against it.
431
- * 2. Cross-process exclusion. We hold an exclusive lock on
432
- * `~/.neat/projects.json.lock` for the read-modify-write window. Two
433
- * concurrent `neat init` runs cannot both win and overwrite each other.
468
+ * `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`
469
+ * verbs read discovery, falling back to the legacy registry where no daemon
470
+ * file is present yet (the migration window before every daemon self-describes).
434
471
  *
435
- * The lock is a file we exclusively-create (`O_EXCL`), hold while we mutate,
436
- * and unlink on the way out. Crude but cross-platform; matches what
437
- * `proper-lockfile` does internally without pulling the dep in.
472
+ * The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we
473
+ * mutate, and unlink on the way out. Crude but cross-platform; matches what
474
+ * `proper-lockfile` does internally without pulling the dep in. It never sits
475
+ * on the discovery path.
438
476
  */
439
477
 
440
478
  declare function registryPath(): string;
package/dist/index.d.ts CHANGED
@@ -238,8 +238,16 @@ interface IngestContext {
238
238
  now?: () => number;
239
239
  writeErrorEventInline?: boolean;
240
240
  onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void;
241
+ burstState?: Map<string, BurstState>;
241
242
  }
242
243
  declare function thresholdForEdgeType(edgeType: string, overrides?: Record<string, number>): number;
244
+ interface BurstState {
245
+ count: number;
246
+ firstTs: string;
247
+ lastTs: string;
248
+ lastMs: number;
249
+ codes: Map<number, number>;
250
+ }
243
251
  declare function stitchTrace(graph: NeatGraph, sourceServiceId: string, ts: string): void;
244
252
  declare function handleSpan(ctx: IngestContext, span: ParsedSpan): Promise<void>;
245
253
 
@@ -347,8 +355,25 @@ declare function computeGraphDiff(liveGraph: NeatGraph, baseSnapshot: PersistedS
347
355
  * - Auto-restart on crash. PID file is the supervisor handoff.
348
356
  */
349
357
 
358
+ interface DaemonPorts {
359
+ rest: number;
360
+ otlp: number;
361
+ web: number;
362
+ }
363
+ interface DaemonRecord {
364
+ project: string;
365
+ projectPath: string;
366
+ pid: number;
367
+ status: 'running' | 'stopped';
368
+ ports: DaemonPorts;
369
+ startedAt: string;
370
+ neatVersion: string;
371
+ }
350
372
  interface DaemonOptions {
351
373
  neatHome?: string;
374
+ project?: string;
375
+ projectPath?: string;
376
+ webPort?: number;
352
377
  restPort?: number;
353
378
  otlpPort?: number;
354
379
  host?: string;
@@ -382,6 +407,7 @@ interface DaemonHandle {
382
407
  otlpAddress: string;
383
408
  bootstrap: BootstrapTracker;
384
409
  initialBootstrap: Promise<void>;
410
+ daemonRecord: DaemonRecord | null;
385
411
  }
386
412
  /**
387
413
  * Resolve which project's graph an OTel span belongs to. Looks up the
@@ -419,22 +445,34 @@ declare function routeSpanToProject(serviceName: string | undefined, projects: R
419
445
  declare function startDaemon(opts?: DaemonOptions): Promise<DaemonHandle>;
420
446
 
421
447
  /**
422
- * Machine-level project registry (ADR-048).
448
+ * Machine-level project registry + machine-wide daemon discovery.
449
+ *
450
+ * This module owns two surfaces under `~/.neat/`:
451
+ *
452
+ * 1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One
453
+ * file, per-user, machine-local, not synced. Under the project-daemon
454
+ * contract (ADR-096) it is no longer the coordination point — it is read
455
+ * once for migration and otherwise left to the additive writes the daemon
456
+ * and orchestrator still perform. The read-modify-write helpers below keep
457
+ * their atomic-write + exclusive-lock machinery for that legacy surface.
423
458
  *
424
- * One file: `~/.neat/projects.json`. Per-user, machine-local. Not synced.
425
- * `registry.ts` is the only module that opens it. Everything else — `init`,
426
- * `daemon`, `cli` calls into the helpers below.
459
+ * 2. The machine-wide daemon discovery directory at `~/.neat/daemons/`
460
+ * (ADR-096 §6). One file per running daemon `<project>.json` each owned
461
+ * solely by the daemon that wrote it (on start) and removes it (on graceful
462
+ * stop). Discovery is **append-only and lock-free**: a reader scans the
463
+ * directory and reconciles liveness; it never acquires a shared lock, so it
464
+ * can never deadlock against a daemon (#506). Losing or rebuilding the
465
+ * directory costs discovery convenience, not correctness — each project's
466
+ * own `neat-out/daemon.json` stays authoritative.
427
467
  *
428
- * Two safety properties matter:
429
- * 1. Atomic writes. We tmp + fsync + rename so the daemon never sees a torn
430
- * file when init races against it.
431
- * 2. Cross-process exclusion. We hold an exclusive lock on
432
- * `~/.neat/projects.json.lock` for the read-modify-write window. Two
433
- * concurrent `neat init` runs cannot both win and overwrite each other.
468
+ * `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`
469
+ * verbs read discovery, falling back to the legacy registry where no daemon
470
+ * file is present yet (the migration window before every daemon self-describes).
434
471
  *
435
- * The lock is a file we exclusively-create (`O_EXCL`), hold while we mutate,
436
- * and unlink on the way out. Crude but cross-platform; matches what
437
- * `proper-lockfile` does internally without pulling the dep in.
472
+ * The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we
473
+ * mutate, and unlink on the way out. Crude but cross-platform; matches what
474
+ * `proper-lockfile` does internally without pulling the dep in. It never sits
475
+ * on the discovery path.
438
476
  */
439
477
 
440
478
  declare function registryPath(): string;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  routeSpanToProject,
3
3
  startDaemon
4
- } from "./chunk-VMLWUK7W.js";
4
+ } from "./chunk-GXWBMJDJ.js";
5
5
  import {
6
6
  ProjectNameCollisionError,
7
7
  addProject,
@@ -37,15 +37,15 @@ import {
37
37
  thresholdForEdgeType,
38
38
  touchLastSeen,
39
39
  writeAtomically
40
- } from "./chunk-XS4CGNRO.js";
40
+ } from "./chunk-T3X6XJPU.js";
41
41
  import {
42
42
  startOtelGrpcReceiver
43
- } from "./chunk-GHPHVXYM.js";
43
+ } from "./chunk-MTXF77TN.js";
44
44
  import {
45
45
  buildOtelReceiver,
46
46
  logSpanHandler,
47
47
  parseOtlpRequest
48
- } from "./chunk-6CO7C4IU.js";
48
+ } from "./chunk-BGPWBRLU.js";
49
49
  export {
50
50
  ProjectNameCollisionError,
51
51
  addProject,