@aexol/spectral 0.9.234 → 0.9.241

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 (68) hide show
  1. package/README.md +47 -0
  2. package/dist/agent/index.d.ts +32 -5
  3. package/dist/agent/index.d.ts.map +1 -1
  4. package/dist/agent/index.js +120 -22
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +18 -4
  8. package/dist/commands/desktop.d.ts +167 -0
  9. package/dist/commands/desktop.d.ts.map +1 -0
  10. package/dist/commands/desktop.js +540 -0
  11. package/dist/commands/serve.d.ts +9 -1
  12. package/dist/commands/serve.d.ts.map +1 -1
  13. package/dist/commands/serve.js +78 -1
  14. package/dist/commands/update.d.ts +85 -5
  15. package/dist/commands/update.d.ts.map +1 -1
  16. package/dist/commands/update.js +288 -58
  17. package/dist/extensions/desktop-control/macos.d.ts.map +1 -1
  18. package/dist/extensions/desktop-control/macos.js +2 -2
  19. package/dist/mcp/server-manager.d.ts.map +1 -1
  20. package/dist/mcp/server-manager.js +6 -0
  21. package/dist/memory/tools/receive-agent-observations.d.ts.map +1 -1
  22. package/dist/memory/tools/receive-agent-observations.js +12 -2
  23. package/dist/relay/dispatcher.d.ts.map +1 -1
  24. package/dist/relay/dispatcher.js +30 -2
  25. package/dist/sdk/coding-agent/core/extensions/types.d.ts +8 -0
  26. package/dist/sdk/coding-agent/core/extensions/types.d.ts.map +1 -1
  27. package/dist/sdk/coding-agent/core/system-prompt.d.ts.map +1 -1
  28. package/dist/sdk/coding-agent/core/system-prompt.js +5 -0
  29. package/dist/server/agent-bridge/tps-metrics.d.ts +34 -0
  30. package/dist/server/agent-bridge/tps-metrics.d.ts.map +1 -1
  31. package/dist/server/agent-bridge/tps-metrics.js +22 -0
  32. package/dist/server/agent-bridge.d.ts +9 -0
  33. package/dist/server/agent-bridge.d.ts.map +1 -1
  34. package/dist/server/agent-bridge.js +83 -3
  35. package/dist/server/handlers/paths-list.d.ts +1 -1
  36. package/dist/server/handlers/paths-list.js +1 -1
  37. package/dist/server/inter-agent-broker.d.ts +12 -0
  38. package/dist/server/inter-agent-broker.d.ts.map +1 -1
  39. package/dist/server/inter-agent-broker.js +1 -0
  40. package/dist/server/preset-chain.d.ts +151 -0
  41. package/dist/server/preset-chain.d.ts.map +1 -0
  42. package/dist/server/preset-chain.js +250 -0
  43. package/dist/server/session-stream.d.ts +55 -1
  44. package/dist/server/session-stream.d.ts.map +1 -1
  45. package/dist/server/session-stream.js +203 -24
  46. package/dist/server/storage/rows.d.ts +2 -0
  47. package/dist/server/storage/rows.d.ts.map +1 -1
  48. package/dist/server/storage/schema.d.ts +2 -2
  49. package/dist/server/storage/schema.d.ts.map +1 -1
  50. package/dist/server/storage/schema.js +2 -1
  51. package/dist/server/storage.d.ts +7 -0
  52. package/dist/server/storage.d.ts.map +1 -1
  53. package/dist/server/storage.js +142 -9
  54. package/dist/server/wire.d.ts +71 -0
  55. package/dist/server/wire.d.ts.map +1 -1
  56. package/dist/utils/augment-path.d.ts +51 -0
  57. package/dist/utils/augment-path.d.ts.map +1 -0
  58. package/dist/utils/augment-path.js +234 -0
  59. package/dist/utils/open-browser.d.ts +55 -0
  60. package/dist/utils/open-browser.d.ts.map +1 -0
  61. package/dist/utils/open-browser.js +163 -0
  62. package/dist/utils/serve-state.d.ts +264 -0
  63. package/dist/utils/serve-state.d.ts.map +1 -0
  64. package/dist/utils/serve-state.js +563 -0
  65. package/dist/utils/standalone.d.ts +14 -0
  66. package/dist/utils/standalone.d.ts.map +1 -0
  67. package/dist/utils/standalone.js +23 -0
  68. package/package.json +1 -3
@@ -0,0 +1,264 @@
1
+ /**
2
+ * On-disk rendezvous between `spectral serve` (the relay daemon) and whoever
3
+ * launched it — today `spectral desktop`, tomorrow any supervisor.
4
+ *
5
+ * Two files, both under `getConfigDir()` (`~/.spectral`, or `SPECTRAL_CONFIG_DIR`
6
+ * in tests/CI):
7
+ *
8
+ * serve.pid the daemon's own pid (single-instance lock)
9
+ * serve-ready.json { pid, ts, version } written once the relay socket is open
10
+ *
11
+ * Why both: the pid file alone says "a process exists", not "the relay is
12
+ * connected" — and the web app at aexol.ai/agent subscribes to the relay the
13
+ * moment it loads, so opening the browser before the socket is up produces a
14
+ * `machine_disconnected` on every start (the exact bug the removed Electron app
15
+ * papered over with a 3-second `setTimeout`). `serve-ready.json` plus the
16
+ * `SPECTRAL_SERVE_READY` stdout marker give the launcher a real signal.
17
+ *
18
+ * A stale pid file (SIGKILL, power loss, crash) is expected: every reader
19
+ * sanity-checks liveness with `kill(pid, 0)` and removes dead entries, so a
20
+ * crashed daemon never blocks the next start.
21
+ *
22
+ * Ownership is explicit: the pid file is a *claim*, taken atomically with
23
+ * `O_EXCL` ({@link claimServePidFile}) and only ever rewritten or removed by
24
+ * whoever holds it ({@link ownsServeState}). A launcher that merely *read* the
25
+ * file must never treat it as its own, and a process that lost the claim must
26
+ * not overwrite the state of the daemon that actually owns it.
27
+ *
28
+ * All helpers are synchronous and never throw — a daemon must not fail because
29
+ * a bookkeeping file could not be written.
30
+ */
31
+ /** Line `spectral serve` prints to stdout once the relay socket is open. */
32
+ export declare const SERVE_READY_MARKER = "SPECTRAL_SERVE_READY";
33
+ /** Serve log size threshold past which `spectral desktop` rotates the log. */
34
+ export declare const DEFAULT_SERVE_LOG_MAX_BYTES: number;
35
+ /** Contents of `serve-ready.json`. */
36
+ export interface ServeReadyInfo {
37
+ pid: number;
38
+ /** Unix epoch millis at which the relay became ready. */
39
+ ts: number;
40
+ /** CLI version that produced the ready signal. */
41
+ version: string;
42
+ }
43
+ export declare function getServePidFile(): string;
44
+ export declare function getServeReadyFile(): string;
45
+ /**
46
+ * @param env env to read `SPECTRAL_SERVE_LOG_PATH` from. Defaults to
47
+ * `process.env`, but a caller that holds a custom env (the desktop spawn
48
+ * plan) must pass it — otherwise the wrapper watches one file while the
49
+ * daemon logs into another.
50
+ */
51
+ export declare function getServeLogFile(env?: NodeJS.ProcessEnv): string;
52
+ /**
53
+ * Is `pid` a live process? Uses signal 0, which performs error checking
54
+ * without delivering a signal. `EPERM` counts as alive — the process exists,
55
+ * it just belongs to somebody else.
56
+ */
57
+ export declare function isProcessAlive(pid: number): boolean;
58
+ /**
59
+ * Genealogy verdicts for a live pid we did not spawn:
60
+ *
61
+ * - `ours` — provably a descendant of one of the given ancestors;
62
+ * - `not-ours` — the parent chain was readable and reached init without
63
+ * passing through an ancestor;
64
+ * - `unknown` — parentage could not be read (Windows, no `ps`, transient
65
+ * probe failure under load).
66
+ *
67
+ * The distinction matters: `unknown` is NOT "ours". A live pid nobody can
68
+ * vouch for must never be classified as our daemon.
69
+ */
70
+ export type ServeGenealogy = "ours" | "not-ours" | "unknown";
71
+ /**
72
+ * Is `pid` a descendant of `ancestor`? Used to confirm that the pid file handed
73
+ * over after a spawn really belongs to the process we launched: under `tsx` the
74
+ * CLI re-execs, so the daemon is the *child* of the spawned pid.
75
+ *
76
+ * Best-effort: when parentage cannot be read (Windows, no `ps`, process gone)
77
+ * it answers true, i.e. "unknown" is treated as ours — a launcher must never
78
+ * block a legitimate start over an unverifiable genealogy. Callers that must
79
+ * keep "ours" apart from "unreadable" (see {@link confirmServePidChild}) use the
80
+ * strict tri-state verdict instead.
81
+ */
82
+ export declare function isDescendantOf(pid: number, ancestor: number, maxDepth?: number): boolean;
83
+ /** Write the pid file for the current (or given) daemon process. */
84
+ export declare function writeServePid(pid?: number): void;
85
+ /**
86
+ * Read a pid file WITHOUT a liveness check. Returns null when the file is
87
+ * missing or is not a plain pid. The whole payload must be digits: `parseInt`
88
+ * turns `"12abc"` into 12, and a launcher would then signal a process nobody
89
+ * claimed.
90
+ *
91
+ * @param file defaults to {@link getServePidFile}; tests and alternative
92
+ * layouts may point elsewhere.
93
+ */
94
+ export declare function readServePidFile(file?: string): number | null;
95
+ /**
96
+ * Read the pid file WITH a liveness check. A stale entry (process gone) is
97
+ * deleted and reported as "not running".
98
+ */
99
+ export declare function getLiveServePid(): number | null;
100
+ /**
101
+ * Does `pid` hold the pid-file claim? Every cleanup and every ready-signal
102
+ * write is gated on this, so a second daemon (or a wrapper that merely read
103
+ * the file) can never delete or overwrite the state of the process that owns
104
+ * it.
105
+ */
106
+ export declare function ownsServeState(pid?: number): boolean;
107
+ export type ServePidClaim =
108
+ /** We hold the claim; the file currently records `placeholderPid`. */
109
+ {
110
+ status: "claimed";
111
+ placeholderPid: number;
112
+ }
113
+ /** A live process holds it — reuse that daemon instead of spawning one. */
114
+ | {
115
+ status: "busy";
116
+ pid: number;
117
+ }
118
+ /** The claim could not be taken (e.g. unwritable config dir). */
119
+ | {
120
+ status: "failed";
121
+ error: string;
122
+ };
123
+ /**
124
+ * Atomically claim `serve.pid` BEFORE spawning anything.
125
+ *
126
+ * The naive version — read the file, ask "is it alive?", then spawn — races two
127
+ * concurrent `spectral desktop` invocations into starting two daemons, because
128
+ * both see "not running" before either writes. `open(O_EXCL)` folds the test
129
+ * and the claim into one syscall: exactly one winner, everyone else gets
130
+ * `busy`.
131
+ *
132
+ * The winner writes its *own* pid as a placeholder — a live pid, so a racer
133
+ * that arrives microseconds later also sees `busy` — and hands it over with
134
+ * {@link confirmServePidChild} once the spawn succeeded.
135
+ */
136
+ export declare function claimServePidFile(placeholderPid?: number): ServePidClaim;
137
+ export interface ServePidChildConfirmation {
138
+ status: "child" | "daemon" | "foreign" | "unknown";
139
+ pid: number | null;
140
+ }
141
+ export interface ConfirmServePidChildOptions {
142
+ /**
143
+ * Injectable genealogy verdict for tests. The real probe walks `ps -o ppid=`
144
+ * and can transiently fail under load, so tests pin the verdict to keep the
145
+ * `daemon`/`foreign`/`unknown` classification deterministic.
146
+ */
147
+ genealogy?: (pid: number, ancestors: number[]) => ServeGenealogy;
148
+ }
149
+ /**
150
+ * After a successful spawn, confirm `serve.pid` records the daemon we launched.
151
+ *
152
+ * - `child` — it holds the spawned pid (the normal standalone/node case).
153
+ * - `daemon` — the daemon already rewrote it with its own pid, which happens
154
+ * when the CLI re-execs (`tsx` in dev). Accepted only when that pid is
155
+ * provably our descendant.
156
+ * - `foreign` — a live, unrelated process owns it: the caller must warn and
157
+ * stop touching the state instead of hijacking it.
158
+ * - `unknown` — the hand-off write did not land (read-only dir), or a live pid
159
+ * this command did not spawn could not be verified as ours (unreadable
160
+ * parentage); the caller must warn (unverified holder) but must never signal
161
+ * it — readiness still works via the log marker and the ready file.
162
+ */
163
+ export declare function confirmServePidChild(childPid: number, placeholderPid?: number, opts?: ConfirmServePidChildOptions): ServePidChildConfirmation;
164
+ /**
165
+ * Drop a claim taken by {@link claimServePidFile} when the start failed. Only
166
+ * removes the files while they still record the placeholder or the child we
167
+ * launched — never a foreign daemon's state.
168
+ */
169
+ export declare function releaseServePidClaim(placeholderPid?: number, childPid?: number): void;
170
+ /** Write the readiness marker next to the pid file. */
171
+ export declare function writeServeReady(version: string, pid?: number): ServeReadyInfo;
172
+ /** Read `serve-ready.json`, or null when missing/corrupt. */
173
+ export declare function readServeReadyFile(file?: string): ServeReadyInfo | null;
174
+ /** Remove both the pid file and the ready file. Idempotent. */
175
+ export declare function clearServeState(): void;
176
+ /** Current size of a file, 0 when it does not exist yet. */
177
+ export declare function fileSizeOf(file: string): number;
178
+ /**
179
+ * Truncate a daemon log that outgrew {@link DEFAULT_SERVE_LOG_MAX_BYTES} by
180
+ * moving it aside as `<file>.old` (one generation kept — enough to debug the
181
+ * previous start, without an unbounded log growing forever).
182
+ *
183
+ * Must run BEFORE the new daemon is spawned and before the caller snapshots the
184
+ * log offset: after a rotate the fresh log starts at byte 0.
185
+ *
186
+ * @returns true when a rotation happened.
187
+ */
188
+ export declare function rotateServeLogFile(file?: string, maxBytes?: number): boolean;
189
+ /**
190
+ * Append-mode read: return the bytes written to `file` since `offset` and the
191
+ * new offset. The desktop launcher snapshots the log size before spawning so
192
+ * the `SPECTRAL_SERVE_READY` marker of a *previous* run can never be mistaken
193
+ * for the current one.
194
+ *
195
+ * The returned offset is always the offset to read from next time — including
196
+ * when there is nothing new, and when the file shrank under us (a rotation).
197
+ * A caller that only advanced on new text would stay pinned past end-of-file
198
+ * and never see the next marker.
199
+ */
200
+ export declare function readFromOffset(file: string, offset: number): {
201
+ text: string;
202
+ offset: number;
203
+ };
204
+ export interface WaitForServeReadyOptions {
205
+ /** Pid of the spawned serve process (used for the fast-fail path). */
206
+ pid: number;
207
+ /** Log file to scan for {@link SERVE_READY_MARKER}. */
208
+ logFile?: string;
209
+ /** Ready file to poll as fallback. Defaults to `serve-ready.json`. */
210
+ readyFile?: string;
211
+ /** Pid file consulted to recognise a re-exec'd daemon as the signal owner. */
212
+ pidFile?: string;
213
+ /** Byte offset in `logFile` to start scanning from. */
214
+ logOffset?: number;
215
+ /**
216
+ * Wall-clock millis captured BEFORE the daemon was spawned. A ready signal
217
+ * older than this belongs to an earlier run and is rejected. Defaults to 0
218
+ * (no freshness requirement) for callers that cannot know the spawn time.
219
+ */
220
+ sinceMs?: number;
221
+ /**
222
+ * True when the spawned child re-execs into the daemon (`tsx`/`ts-node` in
223
+ * dev). This pid is then only the wrapper, so the `process-exited` quick-fail
224
+ * is skipped and only the ready signal or the timeout decides.
225
+ */
226
+ expectReExec?: boolean;
227
+ /** Extra pids considered ours (tests, alternative supervisors). */
228
+ expectedPids?: number[];
229
+ timeoutMs?: number;
230
+ intervalMs?: number;
231
+ /** Injectable for tests. */
232
+ alive?: (pid: number) => boolean;
233
+ }
234
+ export type ServeReadyOutcome = "marker" | "ready-file" | "process-exited" | "timeout";
235
+ export interface ServeReadyResult {
236
+ ok: boolean;
237
+ reason: ServeReadyOutcome;
238
+ waitedMs: number;
239
+ /** Ready-file payload when a ready signal was observed. */
240
+ info?: ServeReadyInfo;
241
+ /** Last chunk of daemon output, surfaced in error messages. */
242
+ logTail?: string;
243
+ }
244
+ /**
245
+ * Wait for the daemon to announce readiness, by either signal:
246
+ * - `SPECTRAL_SERVE_READY` appearing in the serve log after `logOffset`, or
247
+ * - a live `serve-ready.json`.
248
+ *
249
+ * A signal only counts when it belongs to THIS launch:
250
+ * - the pid it reports is the spawned pid, or the pid `serve.pid` currently
251
+ * records (the daemon's own pid once it re-exec'd), and
252
+ * - its timestamp is not older than `sinceMs`, and
253
+ * - that pid is alive.
254
+ *
255
+ * The log marker carries no pid of its own, so whenever a ready file exists it
256
+ * must pass the same check — a marker can never be trusted on its own while the
257
+ * ready file proves the signal came from a foreign or stale daemon.
258
+ *
259
+ * Gives up early when the child dies (registration/auth/sqlite failures) so the
260
+ * user sees the real error instead of waiting out the timeout, and always
261
+ * returns (never throws) with the tail of the log for diagnostics.
262
+ */
263
+ export declare function waitForServeReady(opts: WaitForServeReadyOptions): Promise<ServeReadyResult>;
264
+ //# sourceMappingURL=serve-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-state.d.ts","sourceRoot":"","sources":["../../src/utils/serve-state.ts"],"names":[],"mappings":"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"}