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
//! Daemon discovery + reachability helpers shared across CLI subcommands.
//!
//! Why: every subcommand that talks to the running daemon needs the same
//! "where is it listening?" logic -- preferring the canonical
//! `{data_dir}/trusty-search/http_addr` file (written via
//! `trusty_common::write_daemon_addr`), falling back to the legacy port
//! lockfile, and finally to the compiled-in default port. Centralising it
//! removes duplication and gives `main.rs` a thinner footprint.
//!
//! Issue #984: `read_http_addr_file()` / `http_addr_path()` have been replaced
//! with `trusty_common::read_daemon_addr("trusty-search")` /
//! `trusty_common::write_daemon_addr("trusty-search", ...)` so that the CLI
//! reads the same file the daemon writes, regardless of platform data-dir
//! layout. The old `~/.trusty-search/http_addr` path was only correct on
//! platforms where `dirs::home_dir()` happens to equal the data dir fallback;
//! on macOS the canonical path is
//! `~/Library/Application Support/trusty-search/http_addr`.
//!
//! What: pure path resolvers and one async TCP probe.
//! Test: covered indirectly by every CLI subcommand that calls into the
//! daemon -- `status`, `index`, `query`, `doctor`, etc.
use Duration;
/// Resolve the daemon's base URL.
///
/// Why: stdio MCP servers and CLI subcommands need to find the running daemon
/// without configuration. We check the canonical address-discovery file
/// (via `trusty_common::read_daemon_addr`, issue #984) first, then fall back
/// to the legacy port file (`daemon.port`) for backward compatibility, and
/// finally to `127.0.0.1:7878` if neither exists.
///
/// Defensive TCP probe (issue #117): if the discovery file points at a dead
/// address (e.g. left behind by a SIGKILL'd `serve --http`, or by a stopped
/// daemon whose cleanup did not run), we fall back to `daemon.port` and
/// overwrite the discovery file with the live address so future callers are
/// fast. The probe is 200 ms -- short enough to keep CLI startup snappy when
/// the file is current, long enough to tolerate a busy machine.
///
/// What: returns `http://{host}:{port}` (no trailing slash).
/// Test: `daemon_base_url_falls_back_when_http_addr_dead` exercises this path.
/// Synchronous, time-boxed TCP reachability check used by `daemon_base_url()`.
///
/// Why: `daemon_base_url()` is called from sync contexts (e.g. main.rs CLI
/// dispatch) and cannot easily `.await`. A blocking `TcpStream::connect_timeout`
/// is the simplest correct primitive -- 200 ms is well below the perceptual
/// threshold for CLI startup.
/// What: parses `host:port`, attempts a TCP connect with a 200 ms deadline,
/// returns true on success. Any parse or connect error returns false.
/// Test: `address_reachable_returns_false_for_dead_port` unit test below.
/// Path to `~/.trusty-search/mcp_http_addr` -- the MCP HTTP/SSE listener's
/// address-discovery file, written by `trusty-search serve --http`.
///
/// Why: distinct from the daemon's `http_addr` (written via
/// `trusty_common::write_daemon_addr`) so two unrelated processes (the daemon
/// and a `serve --http` MCP transport) cannot clobber each other. Before
/// issue #117 both wrote the same file; a SIGKILL'd `serve --http` would
/// leave a dead-address file behind, stranding subsequent
/// `trusty-search dash`/`status` calls in a 60s timeout loop.
/// What: returns `$HOME/.trusty-search/mcp_http_addr`. This is intentionally
/// in `$HOME/.trusty-search/` (not the platform data dir) because it is a
/// per-session file that must be discovered by both the MCP client process and
/// the `serve` process across a potential `$TRUSTY_DATA_DIR_OVERRIDE` boundary.
/// Test: `mcp_http_addr_path_is_home_relative` unit test below.
/// Path to the daemon port file (`daemon.port` under the resolved data dir).
///
/// Why: the port file records which TCP port the running daemon bound, so CLI
/// subcommands (`status`, `index`, `query`) can discover the daemon without
/// configuration. When `TRUSTY_DATA_DIR` is set (by `--data-dir` or the env
/// var), the port file lives in that directory so an isolated test/cert daemon
/// does not collide with the production daemon's port file (issue #281).
/// What: returns `$TRUSTY_DATA_DIR/daemon.port` when the env var is set,
/// otherwise `<data_local_dir>/trusty-search/daemon.port`.
/// Test: set `TRUSTY_DATA_DIR=/tmp/ts-x`; assert path equals
/// `/tmp/ts-x/daemon.port`.
/// Check whether a TCP port is open (non-blocking connect with 500 ms timeout).
pub async