@neat.is/core 0.10.6-dev.20261007 → 0.10.6-dev.20261009

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 (46) hide show
  1. package/dist/{chunk-VL7ANDPI.js → chunk-ECBRGTB2.js} +10 -8
  2. package/dist/{chunk-VL7ANDPI.js.map → chunk-ECBRGTB2.js.map} +1 -1
  3. package/dist/{chunk-KTJK2PAH.js → chunk-H2BAPN33.js} +311 -611
  4. package/dist/chunk-H2BAPN33.js.map +1 -0
  5. package/dist/chunk-MR64ICXX.js +19 -0
  6. package/dist/chunk-MR64ICXX.js.map +1 -0
  7. package/dist/{chunk-EFBWQIKG.js → chunk-QPG2QXCW.js} +11 -27
  8. package/dist/chunk-QPG2QXCW.js.map +1 -0
  9. package/dist/chunk-RGYC5U2Q.js +511 -0
  10. package/dist/chunk-RGYC5U2Q.js.map +1 -0
  11. package/dist/{chunk-LXQCL4MP.js → chunk-TZXSWSHX.js} +2 -2
  12. package/dist/{chunk-BC53SCT7.js → chunk-VFZ6CXLI.js} +20 -6
  13. package/dist/chunk-VFZ6CXLI.js.map +1 -0
  14. package/dist/cli.cjs +409 -77
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.d.cts +2 -0
  17. package/dist/cli.d.ts +2 -0
  18. package/dist/cli.js +251 -77
  19. package/dist/cli.js.map +1 -1
  20. package/dist/index.cjs +165 -17
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +3 -99
  23. package/dist/index.d.ts +3 -99
  24. package/dist/index.js +18 -15
  25. package/dist/neatd.cjs +165 -17
  26. package/dist/neatd.cjs.map +1 -1
  27. package/dist/neatd.js +7 -4
  28. package/dist/neatd.js.map +1 -1
  29. package/dist/otel-grpc-CCCI772O.js +11 -0
  30. package/dist/registry.cjs +570 -0
  31. package/dist/registry.cjs.map +1 -0
  32. package/dist/registry.d.cts +220 -0
  33. package/dist/registry.d.ts +220 -0
  34. package/dist/registry.js +62 -0
  35. package/dist/registry.js.map +1 -0
  36. package/dist/server.cjs +185 -23
  37. package/dist/server.cjs.map +1 -1
  38. package/dist/server.js +10 -6
  39. package/dist/server.js.map +1 -1
  40. package/package.json +8 -3
  41. package/dist/chunk-BC53SCT7.js.map +0 -1
  42. package/dist/chunk-EFBWQIKG.js.map +0 -1
  43. package/dist/chunk-KTJK2PAH.js.map +0 -1
  44. package/dist/otel-grpc-B7ABHLV7.js +0 -10
  45. /package/dist/{chunk-LXQCL4MP.js.map → chunk-TZXSWSHX.js.map} +0 -0
  46. /package/dist/{otel-grpc-B7ABHLV7.js.map → otel-grpc-CCCI772O.js.map} +0 -0
@@ -0,0 +1,220 @@
1
+ import { RegistryStatus, RegistryEntry, RegistryFile } from '@neat.is/types';
2
+
3
+ /**
4
+ * Machine-level project registry + machine-wide daemon discovery.
5
+ *
6
+ * This module owns two surfaces under `~/.neat/`:
7
+ *
8
+ * 1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One
9
+ * file, per-user, machine-local, not synced. Under the project-daemon
10
+ * contract (ADR-096) it is no longer the coordination point — it is read
11
+ * once for migration and otherwise left to the additive writes the daemon
12
+ * and orchestrator still perform. The read-modify-write helpers below keep
13
+ * their atomic-write + exclusive-lock machinery for that legacy surface.
14
+ *
15
+ * 2. The machine-wide daemon discovery directory at `~/.neat/daemons/`
16
+ * (ADR-096 §6). One file per running daemon — `<project>.json` — each owned
17
+ * solely by the daemon that wrote it (on start) and removes it (on graceful
18
+ * stop). Discovery is **append-only and lock-free**: a reader scans the
19
+ * directory and reconciles liveness; it never acquires a shared lock, so it
20
+ * can never deadlock against a daemon (#506). Losing or rebuilding the
21
+ * directory costs discovery convenience, not correctness — each project's
22
+ * own `neat-out/daemon.json` stays authoritative.
23
+ *
24
+ * `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`
25
+ * verbs read discovery, falling back to the legacy registry where no daemon
26
+ * file is present yet (the migration window before every daemon self-describes).
27
+ *
28
+ * The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we
29
+ * mutate, and unlink on the way out. Crude but cross-platform; matches what
30
+ * `proper-lockfile` does internally without pulling the dep in. It never sits
31
+ * on the discovery path.
32
+ */
33
+
34
+ declare function registryPath(): string;
35
+ declare function registryLockPath(): string;
36
+ interface DaemonPorts {
37
+ rest: number;
38
+ otlp: number;
39
+ web: number;
40
+ }
41
+ interface DaemonRecord {
42
+ project: string;
43
+ projectPath: string;
44
+ pid: number;
45
+ status: 'running' | 'stopped';
46
+ ports: DaemonPorts;
47
+ startedAt: string;
48
+ neatVersion: string;
49
+ }
50
+ interface DiscoveredDaemon {
51
+ record: DaemonRecord;
52
+ live: boolean;
53
+ source: string;
54
+ }
55
+ declare function daemonsDir(): string;
56
+ declare function isPidAlive(pid: number): boolean;
57
+ interface DiscoveryProbe {
58
+ isPidAlive(pid: number): boolean;
59
+ }
60
+ /**
61
+ * Scan `~/.neat/daemons/` and return every well-formed discovery record with
62
+ * its reconciled liveness. Lock-free: a plain directory read, no rendezvous.
63
+ *
64
+ * A missing directory (no daemon has ever started under this model) yields an
65
+ * empty list — first run, nothing to discover. Malformed or unreadable files
66
+ * are skipped silently; discovery degrades to "fewer entries," never an error.
67
+ */
68
+ declare function discoverDaemons(probe?: DiscoveryProbe): Promise<DiscoveredDaemon[]>;
69
+ /**
70
+ * Remove a daemon's discovery file. A daemon owns its own file, so this is for
71
+ * the per-daemon verbs reconciling a record whose daemon is gone (a crashed
72
+ * daemon that never cleared its own file) — never a rendezvous another process
73
+ * coordinates through. Best-effort: an already-absent file is success.
74
+ */
75
+ declare function removeDaemonRecord(source: string): Promise<void>;
76
+ interface MachineProject {
77
+ project: string;
78
+ projectPath: string;
79
+ state: 'running' | 'stopped' | 'registered';
80
+ ports?: DaemonPorts;
81
+ registryStatus?: RegistryStatus;
82
+ pid?: number;
83
+ }
84
+ /**
85
+ * The machine-wide project view the CLI verbs render. Discovery wins: every
86
+ * `~/.neat/daemons/` record becomes a row (running or stopped per liveness).
87
+ * Legacy registry entries whose path isn't already covered by a discovery
88
+ * record are folded in as `registered` rows so a pre-#508 install still lists
89
+ * its projects. Keyed on resolved path so a registry entry and its daemon
90
+ * record collapse to one row.
91
+ *
92
+ * Read-only on both surfaces. The registry is read once for migration; nothing
93
+ * here writes it back, in keeping with ADR-096 §8.
94
+ */
95
+ declare function listMachineProjects(probe?: DiscoveryProbe): Promise<MachineProject[]>;
96
+ declare function findDaemonByProject(name: string, probe?: DiscoveryProbe): Promise<DiscoveredDaemon | undefined>;
97
+ declare function signalDaemonStop(pid: number): boolean;
98
+ type LockHolder = {
99
+ kind: 'daemon';
100
+ pid: number;
101
+ } | {
102
+ kind: 'command';
103
+ pid: number;
104
+ } | {
105
+ kind: 'stale';
106
+ };
107
+ interface LockHolderProbe {
108
+ isPidAlive(pid: number): boolean;
109
+ daemonPidFromFile(): Promise<number | undefined>;
110
+ daemonResponds(): Promise<boolean>;
111
+ }
112
+ declare function classifyLockHolder(lockPath: string, probe?: LockHolderProbe): Promise<LockHolder>;
113
+ declare function lockHolderMessage(holder: LockHolder, lockPath: string, timeoutMs: number): string;
114
+ /**
115
+ * Path normalisation per ADR-048 #7. Two `init` calls from different relative
116
+ * paths to the same dir must collapse to one entry. `path.resolve` handles
117
+ * relative-to-cwd; we pass it through `fs.realpath` when the dir exists so
118
+ * symlinked paths land on the same canonical entry too.
119
+ */
120
+ declare function normalizeProjectPath(input: string): Promise<string>;
121
+ /**
122
+ * tmp + fsync + rename. The fsync on the data fd guarantees the bytes are on
123
+ * disk before rename swaps the inode; rename itself is atomic on POSIX.
124
+ *
125
+ * Exported so the init flow and test harnesses can use the same helper.
126
+ */
127
+ declare function writeAtomically(target: string, contents: string): Promise<void>;
128
+ /**
129
+ * Read the registry from disk. Returns an empty registry if the file does
130
+ * not exist yet — first run, never registered anything.
131
+ *
132
+ * Throws on parse / schema errors. The contract is single-source-of-truth;
133
+ * a corrupt file is louder than a silent reset.
134
+ */
135
+ declare function readRegistry(): Promise<RegistryFile>;
136
+ interface AddProjectOptions {
137
+ name: string;
138
+ path: string;
139
+ languages?: string[];
140
+ status?: RegistryStatus;
141
+ }
142
+ /**
143
+ * Anything the registry refuses to do.
144
+ *
145
+ * The identity lives on the class, not in the message text. Every caller that
146
+ * prints one already prefixes its own name — the CLI writes `neat: ` — so a
147
+ * message carrying `neat registry:` came out as `neat: neat registry: …`
148
+ * (#1240). A non-CLI caller that wants to know where an error came from reads
149
+ * `err.name`, which is more reliable than a string prefix anyway.
150
+ */
151
+ declare class RegistryError extends Error {
152
+ constructor(message: string);
153
+ }
154
+ /** The registry lock could not be taken, and says who holds it. */
155
+ declare class RegistryLockError extends RegistryError {
156
+ constructor(message: string);
157
+ }
158
+ declare class ProjectNameCollisionError extends RegistryError {
159
+ readonly projectName: string;
160
+ constructor(name: string);
161
+ }
162
+ /**
163
+ * Register a project, or update its `lastSeenAt` if the same path is already
164
+ * registered under the same name (idempotent re-init).
165
+ *
166
+ * Hard error on name collision against a different path — ADR-046 #7. The
167
+ * caller can recover by passing `--project <new-name>`.
168
+ */
169
+ declare function addProject(opts: AddProjectOptions): Promise<RegistryEntry>;
170
+ declare function getProject(name: string): Promise<RegistryEntry | undefined>;
171
+ /**
172
+ * The registered project a directory belongs to: the entry whose path is `dir` itself,
173
+ * or failing that its nearest registered ancestor — so a verb run from `services/api`
174
+ * inside a registered repo still finds the repo. Paths are compared after the same
175
+ * normalisation registration applies (`normalizeProjectPath`), so a symlinked spelling
176
+ * of the directory matches the realpath the registry stored.
177
+ */
178
+ declare function findProjectByPath(dir: string): Promise<RegistryEntry | undefined>;
179
+ /**
180
+ * Synchronous startup lookup for stdio clients such as MCP. This keeps the
181
+ * registry file owned by this module while applying the same nearest-ancestor
182
+ * rule as findProjectByPath and the same discovery parser as the CLI.
183
+ */
184
+ declare function registeredDaemonForPathSync(dir: string): {
185
+ registered: boolean;
186
+ restPort?: number;
187
+ };
188
+ declare function listProjects(): Promise<RegistryEntry[]>;
189
+ declare function setStatus(name: string, status: RegistryStatus): Promise<RegistryEntry>;
190
+ declare function touchLastSeen(name: string, at?: string): Promise<void>;
191
+ /**
192
+ * Remove the registry entry for `name`. Per ADR-048 #6: this only removes the
193
+ * registry row. It does **not** touch `neat-out/`, `policy.json`, or any user
194
+ * file in the project directory. SDK-install rollback is a separate flow
195
+ * (`neat-rollback.patch`) that the caller opts in to.
196
+ */
197
+ declare function removeProject(name: string): Promise<RegistryEntry | undefined>;
198
+ declare const DEFAULT_PRUNE_TTL_MS: number;
199
+ declare function pruneTtlMs(): number;
200
+ type PathStatus = 'gone' | 'present' | 'unknown';
201
+ interface PruneOptions {
202
+ ttlMs?: number;
203
+ statPath?: (p: string) => Promise<PathStatus>;
204
+ now?: () => number;
205
+ }
206
+ /**
207
+ * Remove registry entries whose `path` is definitely gone (ENOENT). Goes
208
+ * through the same lock + atomic-write path as every other mutation — never a
209
+ * raw rewrite — so the contract's atomicity invariant holds.
210
+ *
211
+ * Staleness gate: an ENOENT entry is dropped only when `ttlMs` is 0 (explicit
212
+ * `neat prune`) or its `lastSeenAt` (falling back to `registeredAt`) is older
213
+ * than `ttlMs`. A fresh ENOENT entry under a non-zero TTL is left in place — the
214
+ * daemon still marks it `broken`, the TTL is the safety margin before removal.
215
+ *
216
+ * Returns the entries that were removed.
217
+ */
218
+ declare function pruneRegistry(opts?: PruneOptions): Promise<RegistryEntry[]>;
219
+
220
+ export { type AddProjectOptions, DEFAULT_PRUNE_TTL_MS, type DaemonPorts, type DaemonRecord, type DiscoveredDaemon, type DiscoveryProbe, type LockHolder, type LockHolderProbe, type MachineProject, type PathStatus, ProjectNameCollisionError, type PruneOptions, RegistryError, RegistryLockError, addProject, classifyLockHolder, daemonsDir, discoverDaemons, findDaemonByProject, findProjectByPath, getProject, isPidAlive, listMachineProjects, listProjects, lockHolderMessage, normalizeProjectPath, pruneRegistry, pruneTtlMs, readRegistry, registeredDaemonForPathSync, registryLockPath, registryPath, removeDaemonRecord, removeProject, setStatus, signalDaemonStop, touchLastSeen, writeAtomically };
@@ -0,0 +1,220 @@
1
+ import { RegistryStatus, RegistryEntry, RegistryFile } from '@neat.is/types';
2
+
3
+ /**
4
+ * Machine-level project registry + machine-wide daemon discovery.
5
+ *
6
+ * This module owns two surfaces under `~/.neat/`:
7
+ *
8
+ * 1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One
9
+ * file, per-user, machine-local, not synced. Under the project-daemon
10
+ * contract (ADR-096) it is no longer the coordination point — it is read
11
+ * once for migration and otherwise left to the additive writes the daemon
12
+ * and orchestrator still perform. The read-modify-write helpers below keep
13
+ * their atomic-write + exclusive-lock machinery for that legacy surface.
14
+ *
15
+ * 2. The machine-wide daemon discovery directory at `~/.neat/daemons/`
16
+ * (ADR-096 §6). One file per running daemon — `<project>.json` — each owned
17
+ * solely by the daemon that wrote it (on start) and removes it (on graceful
18
+ * stop). Discovery is **append-only and lock-free**: a reader scans the
19
+ * directory and reconciles liveness; it never acquires a shared lock, so it
20
+ * can never deadlock against a daemon (#506). Losing or rebuilding the
21
+ * directory costs discovery convenience, not correctness — each project's
22
+ * own `neat-out/daemon.json` stays authoritative.
23
+ *
24
+ * `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`
25
+ * verbs read discovery, falling back to the legacy registry where no daemon
26
+ * file is present yet (the migration window before every daemon self-describes).
27
+ *
28
+ * The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we
29
+ * mutate, and unlink on the way out. Crude but cross-platform; matches what
30
+ * `proper-lockfile` does internally without pulling the dep in. It never sits
31
+ * on the discovery path.
32
+ */
33
+
34
+ declare function registryPath(): string;
35
+ declare function registryLockPath(): string;
36
+ interface DaemonPorts {
37
+ rest: number;
38
+ otlp: number;
39
+ web: number;
40
+ }
41
+ interface DaemonRecord {
42
+ project: string;
43
+ projectPath: string;
44
+ pid: number;
45
+ status: 'running' | 'stopped';
46
+ ports: DaemonPorts;
47
+ startedAt: string;
48
+ neatVersion: string;
49
+ }
50
+ interface DiscoveredDaemon {
51
+ record: DaemonRecord;
52
+ live: boolean;
53
+ source: string;
54
+ }
55
+ declare function daemonsDir(): string;
56
+ declare function isPidAlive(pid: number): boolean;
57
+ interface DiscoveryProbe {
58
+ isPidAlive(pid: number): boolean;
59
+ }
60
+ /**
61
+ * Scan `~/.neat/daemons/` and return every well-formed discovery record with
62
+ * its reconciled liveness. Lock-free: a plain directory read, no rendezvous.
63
+ *
64
+ * A missing directory (no daemon has ever started under this model) yields an
65
+ * empty list — first run, nothing to discover. Malformed or unreadable files
66
+ * are skipped silently; discovery degrades to "fewer entries," never an error.
67
+ */
68
+ declare function discoverDaemons(probe?: DiscoveryProbe): Promise<DiscoveredDaemon[]>;
69
+ /**
70
+ * Remove a daemon's discovery file. A daemon owns its own file, so this is for
71
+ * the per-daemon verbs reconciling a record whose daemon is gone (a crashed
72
+ * daemon that never cleared its own file) — never a rendezvous another process
73
+ * coordinates through. Best-effort: an already-absent file is success.
74
+ */
75
+ declare function removeDaemonRecord(source: string): Promise<void>;
76
+ interface MachineProject {
77
+ project: string;
78
+ projectPath: string;
79
+ state: 'running' | 'stopped' | 'registered';
80
+ ports?: DaemonPorts;
81
+ registryStatus?: RegistryStatus;
82
+ pid?: number;
83
+ }
84
+ /**
85
+ * The machine-wide project view the CLI verbs render. Discovery wins: every
86
+ * `~/.neat/daemons/` record becomes a row (running or stopped per liveness).
87
+ * Legacy registry entries whose path isn't already covered by a discovery
88
+ * record are folded in as `registered` rows so a pre-#508 install still lists
89
+ * its projects. Keyed on resolved path so a registry entry and its daemon
90
+ * record collapse to one row.
91
+ *
92
+ * Read-only on both surfaces. The registry is read once for migration; nothing
93
+ * here writes it back, in keeping with ADR-096 §8.
94
+ */
95
+ declare function listMachineProjects(probe?: DiscoveryProbe): Promise<MachineProject[]>;
96
+ declare function findDaemonByProject(name: string, probe?: DiscoveryProbe): Promise<DiscoveredDaemon | undefined>;
97
+ declare function signalDaemonStop(pid: number): boolean;
98
+ type LockHolder = {
99
+ kind: 'daemon';
100
+ pid: number;
101
+ } | {
102
+ kind: 'command';
103
+ pid: number;
104
+ } | {
105
+ kind: 'stale';
106
+ };
107
+ interface LockHolderProbe {
108
+ isPidAlive(pid: number): boolean;
109
+ daemonPidFromFile(): Promise<number | undefined>;
110
+ daemonResponds(): Promise<boolean>;
111
+ }
112
+ declare function classifyLockHolder(lockPath: string, probe?: LockHolderProbe): Promise<LockHolder>;
113
+ declare function lockHolderMessage(holder: LockHolder, lockPath: string, timeoutMs: number): string;
114
+ /**
115
+ * Path normalisation per ADR-048 #7. Two `init` calls from different relative
116
+ * paths to the same dir must collapse to one entry. `path.resolve` handles
117
+ * relative-to-cwd; we pass it through `fs.realpath` when the dir exists so
118
+ * symlinked paths land on the same canonical entry too.
119
+ */
120
+ declare function normalizeProjectPath(input: string): Promise<string>;
121
+ /**
122
+ * tmp + fsync + rename. The fsync on the data fd guarantees the bytes are on
123
+ * disk before rename swaps the inode; rename itself is atomic on POSIX.
124
+ *
125
+ * Exported so the init flow and test harnesses can use the same helper.
126
+ */
127
+ declare function writeAtomically(target: string, contents: string): Promise<void>;
128
+ /**
129
+ * Read the registry from disk. Returns an empty registry if the file does
130
+ * not exist yet — first run, never registered anything.
131
+ *
132
+ * Throws on parse / schema errors. The contract is single-source-of-truth;
133
+ * a corrupt file is louder than a silent reset.
134
+ */
135
+ declare function readRegistry(): Promise<RegistryFile>;
136
+ interface AddProjectOptions {
137
+ name: string;
138
+ path: string;
139
+ languages?: string[];
140
+ status?: RegistryStatus;
141
+ }
142
+ /**
143
+ * Anything the registry refuses to do.
144
+ *
145
+ * The identity lives on the class, not in the message text. Every caller that
146
+ * prints one already prefixes its own name — the CLI writes `neat: ` — so a
147
+ * message carrying `neat registry:` came out as `neat: neat registry: …`
148
+ * (#1240). A non-CLI caller that wants to know where an error came from reads
149
+ * `err.name`, which is more reliable than a string prefix anyway.
150
+ */
151
+ declare class RegistryError extends Error {
152
+ constructor(message: string);
153
+ }
154
+ /** The registry lock could not be taken, and says who holds it. */
155
+ declare class RegistryLockError extends RegistryError {
156
+ constructor(message: string);
157
+ }
158
+ declare class ProjectNameCollisionError extends RegistryError {
159
+ readonly projectName: string;
160
+ constructor(name: string);
161
+ }
162
+ /**
163
+ * Register a project, or update its `lastSeenAt` if the same path is already
164
+ * registered under the same name (idempotent re-init).
165
+ *
166
+ * Hard error on name collision against a different path — ADR-046 #7. The
167
+ * caller can recover by passing `--project <new-name>`.
168
+ */
169
+ declare function addProject(opts: AddProjectOptions): Promise<RegistryEntry>;
170
+ declare function getProject(name: string): Promise<RegistryEntry | undefined>;
171
+ /**
172
+ * The registered project a directory belongs to: the entry whose path is `dir` itself,
173
+ * or failing that its nearest registered ancestor — so a verb run from `services/api`
174
+ * inside a registered repo still finds the repo. Paths are compared after the same
175
+ * normalisation registration applies (`normalizeProjectPath`), so a symlinked spelling
176
+ * of the directory matches the realpath the registry stored.
177
+ */
178
+ declare function findProjectByPath(dir: string): Promise<RegistryEntry | undefined>;
179
+ /**
180
+ * Synchronous startup lookup for stdio clients such as MCP. This keeps the
181
+ * registry file owned by this module while applying the same nearest-ancestor
182
+ * rule as findProjectByPath and the same discovery parser as the CLI.
183
+ */
184
+ declare function registeredDaemonForPathSync(dir: string): {
185
+ registered: boolean;
186
+ restPort?: number;
187
+ };
188
+ declare function listProjects(): Promise<RegistryEntry[]>;
189
+ declare function setStatus(name: string, status: RegistryStatus): Promise<RegistryEntry>;
190
+ declare function touchLastSeen(name: string, at?: string): Promise<void>;
191
+ /**
192
+ * Remove the registry entry for `name`. Per ADR-048 #6: this only removes the
193
+ * registry row. It does **not** touch `neat-out/`, `policy.json`, or any user
194
+ * file in the project directory. SDK-install rollback is a separate flow
195
+ * (`neat-rollback.patch`) that the caller opts in to.
196
+ */
197
+ declare function removeProject(name: string): Promise<RegistryEntry | undefined>;
198
+ declare const DEFAULT_PRUNE_TTL_MS: number;
199
+ declare function pruneTtlMs(): number;
200
+ type PathStatus = 'gone' | 'present' | 'unknown';
201
+ interface PruneOptions {
202
+ ttlMs?: number;
203
+ statPath?: (p: string) => Promise<PathStatus>;
204
+ now?: () => number;
205
+ }
206
+ /**
207
+ * Remove registry entries whose `path` is definitely gone (ENOENT). Goes
208
+ * through the same lock + atomic-write path as every other mutation — never a
209
+ * raw rewrite — so the contract's atomicity invariant holds.
210
+ *
211
+ * Staleness gate: an ENOENT entry is dropped only when `ttlMs` is 0 (explicit
212
+ * `neat prune`) or its `lastSeenAt` (falling back to `registeredAt`) is older
213
+ * than `ttlMs`. A fresh ENOENT entry under a non-zero TTL is left in place — the
214
+ * daemon still marks it `broken`, the TTL is the safety margin before removal.
215
+ *
216
+ * Returns the entries that were removed.
217
+ */
218
+ declare function pruneRegistry(opts?: PruneOptions): Promise<RegistryEntry[]>;
219
+
220
+ export { type AddProjectOptions, DEFAULT_PRUNE_TTL_MS, type DaemonPorts, type DaemonRecord, type DiscoveredDaemon, type DiscoveryProbe, type LockHolder, type LockHolderProbe, type MachineProject, type PathStatus, ProjectNameCollisionError, type PruneOptions, RegistryError, RegistryLockError, addProject, classifyLockHolder, daemonsDir, discoverDaemons, findDaemonByProject, findProjectByPath, getProject, isPidAlive, listMachineProjects, listProjects, lockHolderMessage, normalizeProjectPath, pruneRegistry, pruneTtlMs, readRegistry, registeredDaemonForPathSync, registryLockPath, registryPath, removeDaemonRecord, removeProject, setStatus, signalDaemonStop, touchLastSeen, writeAtomically };
@@ -0,0 +1,62 @@
1
+ import {
2
+ DEFAULT_PRUNE_TTL_MS,
3
+ ProjectNameCollisionError,
4
+ RegistryError,
5
+ RegistryLockError,
6
+ addProject,
7
+ classifyLockHolder,
8
+ daemonsDir,
9
+ discoverDaemons,
10
+ findDaemonByProject,
11
+ findProjectByPath,
12
+ getProject,
13
+ isPidAlive,
14
+ listMachineProjects,
15
+ listProjects,
16
+ lockHolderMessage,
17
+ normalizeProjectPath,
18
+ pruneRegistry,
19
+ pruneTtlMs,
20
+ readRegistry,
21
+ registeredDaemonForPathSync,
22
+ registryLockPath,
23
+ registryPath,
24
+ removeDaemonRecord,
25
+ removeProject,
26
+ setStatus,
27
+ signalDaemonStop,
28
+ touchLastSeen,
29
+ writeAtomically
30
+ } from "./chunk-RGYC5U2Q.js";
31
+ import "./chunk-MR64ICXX.js";
32
+ export {
33
+ DEFAULT_PRUNE_TTL_MS,
34
+ ProjectNameCollisionError,
35
+ RegistryError,
36
+ RegistryLockError,
37
+ addProject,
38
+ classifyLockHolder,
39
+ daemonsDir,
40
+ discoverDaemons,
41
+ findDaemonByProject,
42
+ findProjectByPath,
43
+ getProject,
44
+ isPidAlive,
45
+ listMachineProjects,
46
+ listProjects,
47
+ lockHolderMessage,
48
+ normalizeProjectPath,
49
+ pruneRegistry,
50
+ pruneTtlMs,
51
+ readRegistry,
52
+ registeredDaemonForPathSync,
53
+ registryLockPath,
54
+ registryPath,
55
+ removeDaemonRecord,
56
+ removeProject,
57
+ setStatus,
58
+ signalDaemonStop,
59
+ touchLastSeen,
60
+ writeAtomically
61
+ };
62
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}