1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
//! Auto-start the trusty-memory daemon when a CLI command requires it.
//!
//! Why: `trusty-memory monitor web` and any future human-facing commands
//! that open the UI silently fail or emit a confusing connection error when
//! the daemon isn't running. This guard probes `/api/v1/health`; if the
//! daemon is down it spawns a detached `<exe> serve --foreground` child
//! (identical to what `trusty-memory start` does) and polls
//! `/api/v1/health` until the daemon is ready or a 30-second budget is
//! exhausted. Users see a single informational line and the command Just Works.
//!
//! What: `ensure_daemon_running(base)` returns `Ok(())` once the daemon is
//! responding to `/api/v1/health`. Returns `Err(...)` when the spawn fails
//! or the daemon doesn't become ready within the budget.
//!
//! Test: `probe_health_returns_false_on_connection_refused` and
//! `probe_health_returns_false_on_bad_url` cover the probe; the live auto-
//! start path is exercised manually via `trusty-memory monitor web` with no
//! daemon running. With the daemon already running, no informational line is
//! printed and behaviour is unchanged.
//!
//! Note: only call this from commands that *require* the daemon (e.g.
//! `monitor web`). Commands like `start`, `stop`, `serve`, `service`, and
//! `setup` deliberately do not call this guard.
use ;
use Colorize;
use Write;
use ;
/// Total wall-clock budget for the daemon to become ready after we spawn it.
///
/// Why: trusty-memory's HTTP port binds in ~1s on a warm machine, so 30s
/// gives generous headroom for a cold redb open (first-time palace hydration)
/// without the user waiting unnecessarily long.
/// What: constant upper bound for the health-poll loop.
/// Test: covered indirectly by the timeout-path comment in `ensure_daemon_running`.
const READY_TIMEOUT: Duration = from_secs;
/// Polling interval between `/api/v1/health` probes while we wait.
///
/// Why: 500ms keeps the spinner feeling responsive without hammering the daemon.
/// What: sleep duration inside the poll loop.
/// Test: covered by the live manual test path.
const POLL_INTERVAL: Duration = from_millis;
/// Per-probe HTTP timeout.
///
/// Why: a hung daemon must not blow our ready budget on a single stalled request.
/// What: short connect + read timeout so a dead daemon fails fast per probe.
/// Test: `probe_health_returns_false_on_connection_refused` verifies the bound.
const PROBE_TIMEOUT: Duration = from_millis;
/// Spinner frames cycled while waiting for `/api/v1/health` to return 2xx.
const SPINNER_FRAMES: & = &;
/// Probe `GET {base}/api/v1/health`. Returns `true` on any 2xx response.
///
/// Why: trusty-memory's health endpoint lives at `/api/v1/health` (not
/// `/health`) — callers must use this helper rather than constructing the URL
/// themselves to stay consistent.
/// What: builds a one-shot `reqwest::Client` with `PROBE_TIMEOUT`, issues a
/// GET, returns `true` on any 2xx status, `false` on any error or non-2xx.
/// Test: `probe_health_returns_false_on_connection_refused`,
/// `probe_health_returns_false_on_bad_url`.
async
/// Spawn `<this exe> serve --foreground` as a detached background process.
///
/// Why: we want the daemon to outlive this CLI invocation. Using
/// `current_exe()` ensures a `cargo run` debugging session boots its own
/// debug daemon and a production install boots the production binary.
/// We pass `--foreground` so the spawned child runs the HTTP server inline
/// rather than recursively self-spawning (which would recurse infinitely).
/// Stdio is fully detached (`Stdio::null()` on all three fds) so the daemon
/// survives terminal close / SIGHUP.
/// What: spawns `<exe> serve --foreground`, returns the child PID on success.
/// Test: covered indirectly by the `ensure_daemon_running` live test path.
/// Resolve the trusty-memory daemon's base URL from the address-discovery file.
///
/// Why: trusty-memory selects a dynamic port from 7070–7079 and writes it to
/// `{data_dir}/http_addr`. Reading that file is the canonical way to find the
/// live daemon without hardcoding a port.
/// What: delegates to `trusty_common::read_daemon_addr("trusty-memory")` and
/// normalises the bare `host:port` string to a full `http://` URL; falls back
/// to `http://127.0.0.1:7070` when no file is found.
/// Test: covered by `trusty_common::read_daemon_addr` unit tests; the fallback
/// path is exercised by `probe_health_returns_false_on_connection_refused`.
/// Ensure the trusty-memory daemon is running and healthy.
///
/// Why: `monitor web` (and any future command that needs the HTTP API) should
/// never tell the user "daemon not running — start it yourself". Auto-starting
/// matches the UX in `trusty-search` and `trusty-analyze` (PR #685).
/// What: if `GET {base}/api/v1/health` returns 2xx, returns `Ok(())` immediately
/// with no output. Otherwise spawns `<exe> serve --foreground`, prints a
/// spinner to stderr, and polls every 500ms for up to 30s. Returns `Err` when
/// the daemon doesn't become ready within the budget.
/// Test: `probe_health_returns_false_on_connection_refused` verifies the probe
/// mechanism; the full auto-start path is exercised manually via
/// `trusty-memory monitor web` with no daemon running.
pub async
/// Open the trusty-memory web dashboard in the default system browser.
///
/// Why: operators and agents should never have to run `trusty-memory start`
/// before `trusty-memory monitor web`. This is the single entry point for
/// the `monitor web` subcommand, mirroring the UX shipped for
/// `trusty-analyze` (PR #685) and `trusty-search` dashboard commands.
/// What: resolves the daemon base URL from the discovery file; calls
/// `ensure_daemon_running` (which probes `/api/v1/health` and auto-spawns
/// a detached `serve --foreground` child when needed); re-resolves the live
/// URL after boot; opens `http://<addr>/ui` in the system browser.
/// Browser-open failure degrades to printing the URL so headless
/// environments still get a usable output.
/// Test: `cargo run -p trusty-memory -- monitor web` with no daemon running
/// prints "Starting trusty-memory daemon…" then a spinner, then opens the
/// browser (or prints the URL).
pub async