@neat.is/core 0.4.17 → 0.4.19

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
@@ -63,7 +63,11 @@ declare function compatPairs(): readonly CompatPair[];
63
63
 
64
64
  declare function saveGraphToDisk(graph: NeatGraph, outPath: string): Promise<void>;
65
65
  declare function loadGraphFromDisk(graph: NeatGraph, outPath: string): Promise<void>;
66
- declare function startPersistLoop(graph: NeatGraph, outPath: string, intervalMs?: number): () => void;
66
+ interface PersistLoopOptions {
67
+ intervalMs?: number;
68
+ exitOnSignal?: boolean;
69
+ }
70
+ declare function startPersistLoop(graph: NeatGraph, outPath: string, opts?: PersistLoopOptions): () => void;
67
71
 
68
72
  interface ScoredNode {
69
73
  node: GraphNode;
@@ -125,6 +129,10 @@ interface BuildApiOptions {
125
129
  elapsedMs: number;
126
130
  }>;
127
131
  };
132
+ singleProject?: {
133
+ name: string;
134
+ path: string;
135
+ };
128
136
  }
129
137
  declare function buildApi(opts: BuildApiOptions): Promise<FastifyInstance>;
130
138
 
@@ -355,8 +363,25 @@ declare function computeGraphDiff(liveGraph: NeatGraph, baseSnapshot: PersistedS
355
363
  * - Auto-restart on crash. PID file is the supervisor handoff.
356
364
  */
357
365
 
366
+ interface DaemonPorts {
367
+ rest: number;
368
+ otlp: number;
369
+ web: number;
370
+ }
371
+ interface DaemonRecord {
372
+ project: string;
373
+ projectPath: string;
374
+ pid: number;
375
+ status: 'running' | 'stopped';
376
+ ports: DaemonPorts;
377
+ startedAt: string;
378
+ neatVersion: string;
379
+ }
358
380
  interface DaemonOptions {
359
381
  neatHome?: string;
382
+ project?: string;
383
+ projectPath?: string;
384
+ webPort?: number;
360
385
  restPort?: number;
361
386
  otlpPort?: number;
362
387
  host?: string;
@@ -390,6 +415,7 @@ interface DaemonHandle {
390
415
  otlpAddress: string;
391
416
  bootstrap: BootstrapTracker;
392
417
  initialBootstrap: Promise<void>;
418
+ daemonRecord: DaemonRecord | null;
393
419
  }
394
420
  /**
395
421
  * Resolve which project's graph an OTel span belongs to. Looks up the
@@ -427,22 +453,34 @@ declare function routeSpanToProject(serviceName: string | undefined, projects: R
427
453
  declare function startDaemon(opts?: DaemonOptions): Promise<DaemonHandle>;
428
454
 
429
455
  /**
430
- * Machine-level project registry (ADR-048).
456
+ * Machine-level project registry + machine-wide daemon discovery.
457
+ *
458
+ * This module owns two surfaces under `~/.neat/`:
459
+ *
460
+ * 1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One
461
+ * file, per-user, machine-local, not synced. Under the project-daemon
462
+ * contract (ADR-096) it is no longer the coordination point — it is read
463
+ * once for migration and otherwise left to the additive writes the daemon
464
+ * and orchestrator still perform. The read-modify-write helpers below keep
465
+ * their atomic-write + exclusive-lock machinery for that legacy surface.
431
466
  *
432
- * One file: `~/.neat/projects.json`. Per-user, machine-local. Not synced.
433
- * `registry.ts` is the only module that opens it. Everything else — `init`,
434
- * `daemon`, `cli` calls into the helpers below.
467
+ * 2. The machine-wide daemon discovery directory at `~/.neat/daemons/`
468
+ * (ADR-096 §6). One file per running daemon `<project>.json` each owned
469
+ * solely by the daemon that wrote it (on start) and removes it (on graceful
470
+ * stop). Discovery is **append-only and lock-free**: a reader scans the
471
+ * directory and reconciles liveness; it never acquires a shared lock, so it
472
+ * can never deadlock against a daemon (#506). Losing or rebuilding the
473
+ * directory costs discovery convenience, not correctness — each project's
474
+ * own `neat-out/daemon.json` stays authoritative.
435
475
  *
436
- * Two safety properties matter:
437
- * 1. Atomic writes. We tmp + fsync + rename so the daemon never sees a torn
438
- * file when init races against it.
439
- * 2. Cross-process exclusion. We hold an exclusive lock on
440
- * `~/.neat/projects.json.lock` for the read-modify-write window. Two
441
- * concurrent `neat init` runs cannot both win and overwrite each other.
476
+ * `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`
477
+ * verbs read discovery, falling back to the legacy registry where no daemon
478
+ * file is present yet (the migration window before every daemon self-describes).
442
479
  *
443
- * The lock is a file we exclusively-create (`O_EXCL`), hold while we mutate,
444
- * and unlink on the way out. Crude but cross-platform; matches what
445
- * `proper-lockfile` does internally without pulling the dep in.
480
+ * The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we
481
+ * mutate, and unlink on the way out. Crude but cross-platform; matches what
482
+ * `proper-lockfile` does internally without pulling the dep in. It never sits
483
+ * on the discovery path.
446
484
  */
447
485
 
448
486
  declare function registryPath(): string;
package/dist/index.d.ts CHANGED
@@ -63,7 +63,11 @@ declare function compatPairs(): readonly CompatPair[];
63
63
 
64
64
  declare function saveGraphToDisk(graph: NeatGraph, outPath: string): Promise<void>;
65
65
  declare function loadGraphFromDisk(graph: NeatGraph, outPath: string): Promise<void>;
66
- declare function startPersistLoop(graph: NeatGraph, outPath: string, intervalMs?: number): () => void;
66
+ interface PersistLoopOptions {
67
+ intervalMs?: number;
68
+ exitOnSignal?: boolean;
69
+ }
70
+ declare function startPersistLoop(graph: NeatGraph, outPath: string, opts?: PersistLoopOptions): () => void;
67
71
 
68
72
  interface ScoredNode {
69
73
  node: GraphNode;
@@ -125,6 +129,10 @@ interface BuildApiOptions {
125
129
  elapsedMs: number;
126
130
  }>;
127
131
  };
132
+ singleProject?: {
133
+ name: string;
134
+ path: string;
135
+ };
128
136
  }
129
137
  declare function buildApi(opts: BuildApiOptions): Promise<FastifyInstance>;
130
138
 
@@ -355,8 +363,25 @@ declare function computeGraphDiff(liveGraph: NeatGraph, baseSnapshot: PersistedS
355
363
  * - Auto-restart on crash. PID file is the supervisor handoff.
356
364
  */
357
365
 
366
+ interface DaemonPorts {
367
+ rest: number;
368
+ otlp: number;
369
+ web: number;
370
+ }
371
+ interface DaemonRecord {
372
+ project: string;
373
+ projectPath: string;
374
+ pid: number;
375
+ status: 'running' | 'stopped';
376
+ ports: DaemonPorts;
377
+ startedAt: string;
378
+ neatVersion: string;
379
+ }
358
380
  interface DaemonOptions {
359
381
  neatHome?: string;
382
+ project?: string;
383
+ projectPath?: string;
384
+ webPort?: number;
360
385
  restPort?: number;
361
386
  otlpPort?: number;
362
387
  host?: string;
@@ -390,6 +415,7 @@ interface DaemonHandle {
390
415
  otlpAddress: string;
391
416
  bootstrap: BootstrapTracker;
392
417
  initialBootstrap: Promise<void>;
418
+ daemonRecord: DaemonRecord | null;
393
419
  }
394
420
  /**
395
421
  * Resolve which project's graph an OTel span belongs to. Looks up the
@@ -427,22 +453,34 @@ declare function routeSpanToProject(serviceName: string | undefined, projects: R
427
453
  declare function startDaemon(opts?: DaemonOptions): Promise<DaemonHandle>;
428
454
 
429
455
  /**
430
- * Machine-level project registry (ADR-048).
456
+ * Machine-level project registry + machine-wide daemon discovery.
457
+ *
458
+ * This module owns two surfaces under `~/.neat/`:
459
+ *
460
+ * 1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One
461
+ * file, per-user, machine-local, not synced. Under the project-daemon
462
+ * contract (ADR-096) it is no longer the coordination point — it is read
463
+ * once for migration and otherwise left to the additive writes the daemon
464
+ * and orchestrator still perform. The read-modify-write helpers below keep
465
+ * their atomic-write + exclusive-lock machinery for that legacy surface.
431
466
  *
432
- * One file: `~/.neat/projects.json`. Per-user, machine-local. Not synced.
433
- * `registry.ts` is the only module that opens it. Everything else — `init`,
434
- * `daemon`, `cli` calls into the helpers below.
467
+ * 2. The machine-wide daemon discovery directory at `~/.neat/daemons/`
468
+ * (ADR-096 §6). One file per running daemon `<project>.json` each owned
469
+ * solely by the daemon that wrote it (on start) and removes it (on graceful
470
+ * stop). Discovery is **append-only and lock-free**: a reader scans the
471
+ * directory and reconciles liveness; it never acquires a shared lock, so it
472
+ * can never deadlock against a daemon (#506). Losing or rebuilding the
473
+ * directory costs discovery convenience, not correctness — each project's
474
+ * own `neat-out/daemon.json` stays authoritative.
435
475
  *
436
- * Two safety properties matter:
437
- * 1. Atomic writes. We tmp + fsync + rename so the daemon never sees a torn
438
- * file when init races against it.
439
- * 2. Cross-process exclusion. We hold an exclusive lock on
440
- * `~/.neat/projects.json.lock` for the read-modify-write window. Two
441
- * concurrent `neat init` runs cannot both win and overwrite each other.
476
+ * `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`
477
+ * verbs read discovery, falling back to the legacy registry where no daemon
478
+ * file is present yet (the migration window before every daemon self-describes).
442
479
  *
443
- * The lock is a file we exclusively-create (`O_EXCL`), hold while we mutate,
444
- * and unlink on the way out. Crude but cross-platform; matches what
445
- * `proper-lockfile` does internally without pulling the dep in.
480
+ * The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we
481
+ * mutate, and unlink on the way out. Crude but cross-platform; matches what
482
+ * `proper-lockfile` does internally without pulling the dep in. It never sits
483
+ * on the discovery path.
446
484
  */
447
485
 
448
486
  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-CEDXXMGO.js";
4
+ } from "./chunk-ZV7GHZ2D.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-LUDSPX5N.js";
40
+ } from "./chunk-Q5EDVWKZ.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,