slancha-wire 0.17.0

Magic-wormhole for AI agents — bilateral signed-message bus over a mailbox relay
Documentation
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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
//! Cross-platform process-management primitives.
//!
//! Wire historically called `pgrep` + `kill` directly, which gave us
//! "unsupported platform" rot on Windows. v0.7.3 funnels every
//! liveness check / command-line search / SIGTERM through this module
//! so the Windows daemon + relay paths get the same teardown +
//! respawn behavior the Linux + macOS paths have always had.
//!
//! ## Helpers
//!
//! - [`process_alive`] — "is pid <N> still around?"
//! - [`find_processes_by_cmdline`] — `pgrep -f <pattern>` equivalent
//! - [`kill_process`] — SIGTERM / SIGKILL equivalent (taskkill /T on
//!   Windows so the tree dies, not just the parent)
//!
//! Each helper returns conservative defaults on tool failure (empty
//! Vec, `false`) so callers can chain them without aborting an upgrade
//! mid-flight when one query hiccups.
//!
//! ### Bounded shell-out (#284.1)
//!
//! Every Windows shell-out below is wrapped in [`run_with_timeout`].
//! PowerShell's `Get-CimInstance` can wedge — observed on a host with
//! 254 stale `wire.exe` processes piled up by a broken SessionStart
//! loop, but also any corrupted CIM repository — and any `wire status`
//! / `wire up` / `wire doctor` call that lands on a wedged enumeration
//! would block forever waiting on the child. The wrapper kills the
//! child after `WIRE_PLATFORM_TIMEOUT_SECS` (default 5s) and the
//! caller falls through to its existing tool-error fallback (empty
//! Vec, `None`, etc.), so a probe that can't answer in 5s reads as
//! "no answer" rather than "wedge the whole CLI".

use std::process::{Command, Output, Stdio};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

/// Bounded timeout for Windows shell-outs in this module. Override via
/// `WIRE_PLATFORM_TIMEOUT_SECS`. Default 5s — every probe in this
/// module is a single PowerShell / tasklist call that completes in
/// well under 500ms on a healthy host. POSIX builds never call this
/// at runtime (the test module does, hence not `#[cfg(windows)]`-only),
/// so silence the dead-code lint there.
#[cfg_attr(not(windows), allow(dead_code))]
fn platform_shell_timeout() -> Duration {
    std::env::var("WIRE_PLATFORM_TIMEOUT_SECS")
        .ok()
        .and_then(|s| s.parse::<u64>().ok())
        .map(Duration::from_secs)
        .unwrap_or_else(|| Duration::from_secs(5))
}

/// Run `cmd` with a wall-clock timeout. Returns `Some(Output)` on
/// completion, or `None` on timeout (or spawn failure / wait failure).
/// On timeout the child is killed best-effort via a platform-native
/// shell-out (`taskkill /F /T /PID` on Windows, `kill -9` on POSIX) so
/// the wedged process tree exits with the wrapper.
///
/// `Stdio` defaults: stdin null, stdout/stderr piped. Callers may
/// override `stdin` before calling but should leave the pipes alone —
/// the reader thread relies on them being captured to drain output
/// while we wait.
///
/// Implementation: spawn the child, hand `wait_with_output` to a
/// background thread that sends the result through a channel, then
/// `recv_timeout` on the main thread. On timeout we kill the PID via
/// the OS-native tool (we can't call `Child::kill` here because the
/// `Child` moved into the reader thread).
pub fn run_with_timeout(mut cmd: Command, timeout: Duration) -> Option<Output> {
    cmd.stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());
    let child = cmd.spawn().ok()?;
    let pid = child.id();
    let (tx, rx) = mpsc::channel::<Output>();
    thread::spawn(move || {
        if let Ok(out) = child.wait_with_output() {
            let _ = tx.send(out);
        }
    });
    match rx.recv_timeout(timeout) {
        Ok(out) => Some(out),
        Err(_) => {
            // Kill the wedged child by PID. Best-effort: a failure here
            // just means the reader thread keeps waiting; the main
            // thread already moved on with `None`.
            kill_pid_best_effort(pid);
            None
        }
    }
}

fn kill_pid_best_effort(pid: u32) {
    #[cfg(unix)]
    {
        let _ = Command::new("kill")
            .args(["-9", &pid.to_string()])
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .status();
    }
    #[cfg(windows)]
    {
        let _ = Command::new("taskkill.exe")
            .args(["/F", "/T", "/PID", &pid.to_string()])
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .status();
    }
    #[cfg(not(any(unix, windows)))]
    {
        let _ = pid;
    }
}

/// True iff pid is alive.
///
/// - Linux: `/proc/<pid>` exists (no fork, no shell-out).
/// - macOS / BSD: `kill -0 <pid>` (signal 0 = check only).
/// - Windows: `tasklist /FI "PID eq <pid>" /FO CSV /NH`. A miss prints
///   `INFO: No tasks are running...` to stdout AND exits 0, so we
///   detect by content rather than exit code.
pub fn process_alive(pid: u32) -> bool {
    #[cfg(target_os = "linux")]
    {
        std::path::Path::new(&format!("/proc/{pid}")).exists()
    }
    #[cfg(all(unix, not(target_os = "linux")))]
    {
        Command::new("kill")
            .args(["-0", &pid.to_string()])
            .stdin(std::process::Stdio::null())
            .stdout(std::process::Stdio::null())
            .stderr(std::process::Stdio::null())
            .status()
            .map(|s| s.success())
            .unwrap_or(false)
    }
    #[cfg(windows)]
    {
        // Bounded: a wedged `tasklist` would hang every `wire status` /
        // `wire doctor` it touches. 5s default — `tasklist /FI "PID eq …"`
        // completes in well under 100ms on a healthy host.
        let mut cmd = Command::new("tasklist.exe");
        cmd.args(["/FI", &format!("PID eq {pid}"), "/FO", "CSV", "/NH"]);
        match run_with_timeout(cmd, platform_shell_timeout()) {
            Some(o) if o.status.success() => {
                let s = String::from_utf8_lossy(&o.stdout);
                let trimmed = s.trim();
                !trimmed.is_empty() && !trimmed.starts_with("INFO:")
            }
            // Timeout / failure → conservative `false` (treat as dead).
            // Same fallback the old `Err(_) | Ok(non-success)` arm
            // produced; `wire status` already handles "daemon missing"
            // cleanly, and surfacing "timed out probing" is part of
            // #284.1's bounded-but-loud story.
            _ => false,
        }
    }
}

/// The role/subcommand of a `wire <role> ...` process pattern —
/// `cmdline_role("wire daemon") == "daemon"`, `cmdline_role("wire
/// relay-server") == "relay-server"`. A pattern without the `wire ` prefix
/// passes through unchanged.
///
/// The Windows process scan matches this role (not the full `wire daemon`
/// string) against the command line, because the image is `wire.exe` and the
/// contiguous `wire daemon` never matches the real `wire.exe daemon` cmdline.
/// Hoisted out of the `cfg(windows)` block + unit-tested so the `.exe`-match
/// regression (which caused `wire upgrade` to accumulate daemons) is locked on
/// EVERY platform's CI, not only on a Windows runner.
#[cfg_attr(not(windows), allow(dead_code))]
pub(crate) fn cmdline_role(pattern: &str) -> &str {
    pattern.strip_prefix("wire ").unwrap_or(pattern)
}

/// `pgrep -f <pattern>` equivalent: every pid whose command line
/// contains `pattern`. Empty Vec on tool error or zero matches.
///
/// - Unix: `pgrep -f <pattern>` (one fork, parses pid-per-line stdout).
/// - Windows: PowerShell + CIM (`Get-CimInstance Win32_Process` with
///   `CommandLine` filter). `wmic` was the old path but is deprecated
///   in Windows 11 24H2; CIM is the supported replacement and works
///   back to Windows 10. Pattern is single-quoted into the PowerShell
///   `-like` operator so most metacharacters pass through verbatim;
///   callers that need literal `'` or `[`/`]` should escape per
///   PowerShell rules.
pub fn find_processes_by_cmdline(pattern: &str) -> Vec<u32> {
    #[cfg(unix)]
    {
        Command::new("pgrep")
            .args(["-f", pattern])
            .output()
            .ok()
            .filter(|o| o.status.success())
            .map(|o| {
                String::from_utf8_lossy(&o.stdout)
                    .split_whitespace()
                    .filter_map(|s| s.parse::<u32>().ok())
                    .collect()
            })
            .unwrap_or_default()
    }
    #[cfg(windows)]
    {
        // Single-quote the pattern in the PowerShell string. Inside
        // single-quoted PS strings, the only escape is `''` for a
        // literal single quote; we replace pre-emptively.
        // The Windows process image is `wire.exe`, so a Unix-style full
        // pattern like "wire daemon" does NOT match the actual command line
        // "wire.exe daemon" (the ".exe " breaks the contiguous match). Match
        // the wire image by Name and the ROLE/subcommand (the pattern minus a
        // leading "wire ") in the command line. Without this, find returned
        // nothing for the real daemon on Windows, so `wire upgrade` killed no
        // daemons and they ACCUMULATED (glossy-magnolia: 2->3->4->5 over three
        // upgrade cycles — the exact multi-daemon cursor race doctor warns of).
        //
        // Two further guards (glossy-magnolia repro):
        //   - `$_.Name -like 'wire*'` — only wire processes count. Without it
        //     the query SELF-MATCHED: this PowerShell process's own command
        //     line contains the pattern literal, so it showed up as a phantom
        //     "orphan daemon" with a new pid every call (doctor FAILed on
        //     every healthy box).
        //   - `$_.ProcessId -ne $PID` — belt-and-suspenders self-exclusion.
        let role = cmdline_role(pattern);
        let escaped = role.replace('\'', "''");
        let ps = format!(
            "Get-CimInstance Win32_Process | \
             Where-Object {{ $_.Name -like 'wire*' -and $_.ProcessId -ne $PID -and $_.CommandLine -like '*{escaped}*' }} | \
             Select-Object -ExpandProperty ProcessId"
        );
        // Bounded: a wedged `Get-CimInstance` (corrupted CIM repo, or
        // simply slow under heavy WMI contention on a host with
        // hundreds of stale `wire.exe` processes — see #284.1 / #284.2)
        // would hang every CLI invocation it's reached from. 5s default.
        let mut cmd = Command::new("powershell.exe");
        cmd.args(["-NoProfile", "-NonInteractive", "-Command", &ps]);
        run_with_timeout(cmd, platform_shell_timeout())
            .filter(|o| o.status.success())
            .map(|o| {
                String::from_utf8_lossy(&o.stdout)
                    .split_whitespace()
                    .filter_map(|s| s.parse::<u32>().ok())
                    .collect()
            })
            .unwrap_or_default()
    }
    #[cfg(not(any(unix, windows)))]
    {
        let _ = pattern;
        Vec::new()
    }
}

/// Return the command line of a specific pid, or `None` if the pid
/// is missing / unreadable / exited between query and answer.
///
/// v0.14.2 (#162 diagnostic, post-supervisor #170): when `wire status`
/// surfaces orphan pids, the operator wants to know "which session
/// is that daemon serving?" without grepping `ps` themselves —
/// closes the launchd-vs-session-isolation diagnostic gap honey-pine
/// burned multiple sessions on.
///
/// - Linux: read `/proc/<pid>/cmdline` (NUL-separated, replace with spaces).
/// - macOS / BSD: `ps -p <pid> -o command=` (no header, single column).
/// - Windows: PowerShell CIM `Get-CimInstance Win32_Process | Where
///   {$_.ProcessId -eq <pid>} | Select CommandLine`.
///
/// Conservative on failure: returns `None` rather than synthesizing a
/// placeholder. Callers should treat None as "annotation unavailable",
/// not "process is dead" — `process_alive` is the liveness oracle.
pub fn pid_cmdline(pid: u32) -> Option<String> {
    #[cfg(target_os = "linux")]
    {
        let path = format!("/proc/{pid}/cmdline");
        let bytes = std::fs::read(&path).ok()?;
        // `/proc/<pid>/cmdline` is NUL-separated argv. Convert NULs to
        // spaces for human-readable output; trim trailing NUL.
        let s: String = bytes
            .into_iter()
            .map(|b| if b == 0 { b' ' } else { b })
            .map(|b| b as char)
            .collect();
        let trimmed = s.trim().to_string();
        if trimmed.is_empty() {
            None
        } else {
            Some(trimmed)
        }
    }
    #[cfg(all(unix, not(target_os = "linux")))]
    {
        let out = Command::new("ps")
            .args(["-p", &pid.to_string(), "-o", "command="])
            .output()
            .ok()?;
        if !out.status.success() {
            return None;
        }
        let s = String::from_utf8_lossy(&out.stdout).trim().to_string();
        if s.is_empty() { None } else { Some(s) }
    }
    #[cfg(windows)]
    {
        let ps = format!(
            "Get-CimInstance Win32_Process | \
             Where-Object {{ $_.ProcessId -eq {pid} }} | \
             Select-Object -ExpandProperty CommandLine"
        );
        let mut cmd = Command::new("powershell.exe");
        cmd.args(["-NoProfile", "-NonInteractive", "-Command", &ps]);
        let out = run_with_timeout(cmd, platform_shell_timeout())?;
        if !out.status.success() {
            return None;
        }
        let s = String::from_utf8_lossy(&out.stdout).trim().to_string();
        if s.is_empty() { None } else { Some(s) }
    }
    #[cfg(not(any(unix, windows)))]
    {
        let _ = pid;
        None
    }
}

/// Parse `--session <name>` from a wire daemon command line. Returns
/// `None` if not present. v0.14.2 (#170 supervisor pairs a `--session
/// <name>` arg with the WIRE_HOME the daemon serves; this extracts it
/// for orphan-pid diagnostic display).
pub fn parse_session_arg(cmdline: &str) -> Option<&str> {
    let parts: Vec<&str> = cmdline.split_whitespace().collect();
    let i = parts.iter().position(|p| *p == "--session")?;
    parts.get(i + 1).copied()
}

/// Signal a pid to exit. Returns true on successful dispatch (NOT on
/// confirmed exit — poll [`process_alive`] for that). `force=true` is
/// SIGKILL / `taskkill /F`; `force=false` is SIGTERM / `taskkill`
/// (graceful).
///
/// Windows note: we pass `/T` so the whole process tree dies, not just
/// the root. The daemon's `wire daemon` invocation is single-process
/// today but the relay-server spawns hyper worker threads; `/T` is
/// the safe default.
pub fn kill_process(pid: u32, force: bool) -> bool {
    #[cfg(unix)]
    {
        let sig = if force { "-9" } else { "-15" };
        Command::new("kill")
            .args([sig, &pid.to_string()])
            .stdin(std::process::Stdio::null())
            .stdout(std::process::Stdio::null())
            .stderr(std::process::Stdio::null())
            .status()
            .map(|s| s.success())
            .unwrap_or(false)
    }
    #[cfg(windows)]
    {
        let pid_str = pid.to_string();
        let mut args: Vec<&str> = vec!["/PID", &pid_str, "/T"];
        if force {
            args.push("/F");
        }
        Command::new("taskkill.exe")
            .args(&args)
            .stdin(std::process::Stdio::null())
            .stdout(std::process::Stdio::null())
            .stderr(std::process::Stdio::null())
            .status()
            .map(|s| s.success())
            .unwrap_or(false)
    }
    #[cfg(not(any(unix, windows)))]
    {
        let _ = (pid, force);
        false
    }
}

/// Resolve the path of the currently-running executable, robust to the Linux
/// kernel's `(deleted)` marker.
///
/// When a running binary is replaced *in place* — e.g. `cargo install
/// slancha-wire` unlinks and recreates `~/.cargo/bin/wire` while `wire upgrade`
/// is still running — the kernel appends a literal ` (deleted)` suffix to
/// `/proc/self/exe`. That suffix marks the unlinked inode; it is NOT part of
/// the path. [`std::env::current_exe`] surfaces it verbatim, and writing it
/// into a systemd `ExecStart=` / launchd program path corrupts the unit
/// (`error: unrecognized subcommand '(deleted)'`, the unit then flaps forever).
///
/// This strips a trailing ` (deleted)` so callers get the real install path,
/// which the in-place replacement has already recreated on disk. Issues #274,
/// #276.
pub fn current_exe_resolved() -> std::io::Result<std::path::PathBuf> {
    Ok(strip_deleted_suffix(&std::env::current_exe()?))
}

/// Pure inner of [`current_exe_resolved`]: strip a trailing ` (deleted)` kernel
/// marker from an exe path. Only the exact trailing ` (deleted)` token (leading
/// space included) is removed — a path that merely contains the text, or a real
/// filename ending in `(deleted)` without the kernel's leading space, is left
/// untouched. Testable without an actually-unlinked binary.
pub fn strip_deleted_suffix(p: &std::path::Path) -> std::path::PathBuf {
    match p.to_string_lossy().strip_suffix(" (deleted)") {
        Some(stripped) => std::path::PathBuf::from(stripped),
        None => p.to_path_buf(),
    }
}

/// Raw, stable machine identifier bytes for the same-machine attestation
/// fingerprint (RFC-001 amendment #182, `same_machine::machine_fingerprint`).
///
/// - **Linux:** `/etc/machine-id` (systemd), falling back to
///   `/var/lib/dbus/machine-id`.
/// - **macOS:** `IOPlatformUUID` from `ioreg -rd1 -c IOPlatformExpertDevice`.
/// - **Windows:** `HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid`
///   (`reg query`).
///
/// Returns `None` on any read failure — the caller omits the attestation and
/// the session still functions, it just can't join the same-machine lane
/// (fail-closed, §A). The bytes are used only as hash input; their exact
/// encoding is irrelevant as long as it is stable for a given machine.
pub fn machine_id_raw() -> Option<Vec<u8>> {
    #[cfg(target_os = "linux")]
    {
        for path in ["/etc/machine-id", "/var/lib/dbus/machine-id"] {
            if let Ok(s) = std::fs::read_to_string(path) {
                let t = s.trim();
                if !t.is_empty() {
                    return Some(t.as_bytes().to_vec());
                }
            }
        }
        None
    }
    #[cfg(target_os = "macos")]
    {
        let out = Command::new("ioreg")
            .args(["-rd1", "-c", "IOPlatformExpertDevice"])
            .output()
            .ok()?;
        if !out.status.success() {
            return None;
        }
        let text = String::from_utf8_lossy(&out.stdout);
        parse_ioreg_platform_uuid(&text).map(|s| s.into_bytes())
    }
    #[cfg(windows)]
    {
        let mut cmd = Command::new("reg.exe");
        cmd.args([
            "query",
            r"HKLM\SOFTWARE\Microsoft\Cryptography",
            "/v",
            "MachineGuid",
        ]);
        let out = run_with_timeout(cmd, platform_shell_timeout())?;
        if !out.status.success() {
            return None;
        }
        let text = String::from_utf8_lossy(&out.stdout);
        parse_reg_machine_guid(&text).map(|s| s.into_bytes())
    }
    #[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
    {
        None
    }
}

/// Stable per-OS-user identifier bytes — the salt that keeps two different
/// users on one shared host (same `machine_id`) from cross-pairing (#182 §S1).
///
/// - **Unix:** `id -u` (the numeric uid). Shelled out rather than pulling in a
///   `libc` dependency, matching this module's existing shell-out idiom.
/// - **Windows:** the current user's SID from `whoami /user`.
///
/// `None` on read failure (fail-closed, same as [`machine_id_raw`]).
pub fn os_user_id_bytes() -> Option<Vec<u8>> {
    #[cfg(unix)]
    {
        let out = Command::new("id").arg("-u").output().ok()?;
        if !out.status.success() {
            return None;
        }
        let s = String::from_utf8_lossy(&out.stdout).trim().to_string();
        if s.is_empty() {
            None
        } else {
            Some(s.into_bytes())
        }
    }
    #[cfg(windows)]
    {
        let mut cmd = Command::new("whoami.exe");
        cmd.args(["/user", "/fo", "csv", "/nh"]);
        let out = run_with_timeout(cmd, platform_shell_timeout())?;
        if !out.status.success() {
            return None;
        }
        let text = String::from_utf8_lossy(&out.stdout);
        parse_whoami_sid(&text).map(|s| s.into_bytes())
    }
    #[cfg(not(any(unix, windows)))]
    {
        None
    }
}

/// Extract the `IOPlatformUUID` value from `ioreg` output. The line looks like
/// `    "IOPlatformUUID" = "DDDDDDDD-DDDD-DDDD-DDDD-DDDDDDDDDDDD"`. Hoisted +
/// tested on every platform so the parse is locked without a macOS runner.
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
fn parse_ioreg_platform_uuid(text: &str) -> Option<String> {
    let line = text.lines().find(|l| l.contains("IOPlatformUUID"))?;
    let after = line.split_once('=')?.1.trim();
    let unquoted = after.trim_matches('"').trim();
    if unquoted.is_empty() {
        None
    } else {
        Some(unquoted.to_string())
    }
}

/// Extract `MachineGuid` from `reg query` output. The value line looks like
/// `    MachineGuid    REG_SZ    DDDDDDDD-DDDD-...`. Tested on every platform.
#[cfg_attr(not(windows), allow(dead_code))]
fn parse_reg_machine_guid(text: &str) -> Option<String> {
    let line = text.lines().find(|l| l.contains("MachineGuid"))?;
    // Split on REG_SZ; the value is the last whitespace-trimmed token after it.
    let after = line.split("REG_SZ").nth(1)?.trim();
    if after.is_empty() {
        None
    } else {
        Some(after.to_string())
    }
}

/// Extract the SID from `whoami /user /fo csv /nh` output, a single CSV row
/// `"<domain>\<user>","S-1-5-21-...."`. Tested on every platform.
#[cfg_attr(not(windows), allow(dead_code))]
fn parse_whoami_sid(text: &str) -> Option<String> {
    let row = text.lines().find(|l| l.contains("S-1-"))?;
    // The SID is the last quoted field.
    let sid = row.rsplit(',').next()?.trim().trim_matches('"').trim();
    if sid.is_empty() {
        None
    } else {
        Some(sid.to_string())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn strip_deleted_suffix_removes_kernel_marker() {
        use std::path::Path;
        // The repro from #274: cargo in-place replace → /proc/self/exe carries
        // the marker → it must NOT reach the unit's ExecStart.
        assert_eq!(
            strip_deleted_suffix(Path::new("/home/admin/.cargo/bin/wire (deleted)")),
            Path::new("/home/admin/.cargo/bin/wire")
        );
    }

    #[test]
    fn strip_deleted_suffix_leaves_clean_path_untouched() {
        use std::path::Path;
        assert_eq!(
            strip_deleted_suffix(Path::new("/usr/local/bin/wire")),
            Path::new("/usr/local/bin/wire")
        );
    }

    #[test]
    fn strip_deleted_suffix_only_strips_exact_trailing_token() {
        use std::path::Path;
        // No leading space → not the kernel marker shape → leave alone.
        assert_eq!(
            strip_deleted_suffix(Path::new("/opt/wire(deleted)")),
            Path::new("/opt/wire(deleted)")
        );
    }

    #[test]
    fn cmdline_role_strips_wire_prefix() {
        // Locks the Windows .exe-match logic on every platform's CI: the role
        // is what we match against `wire.exe daemon`, not the full pattern.
        assert_eq!(cmdline_role("wire daemon"), "daemon");
        assert_eq!(cmdline_role("wire relay-server"), "relay-server");
        // No `wire ` prefix → unchanged (custom patterns pass through).
        assert_eq!(cmdline_role("daemon"), "daemon");
        assert_eq!(cmdline_role("relay-server"), "relay-server");
    }

    #[test]
    fn process_alive_returns_true_for_self() {
        // Our own pid is alive by definition.
        let me = std::process::id();
        assert!(
            process_alive(me),
            "process_alive should return true for self pid {me}"
        );
    }

    #[test]
    fn process_alive_returns_false_for_clearly_dead_pid() {
        // pid 0 is reserved on every Unix; on Windows it's the
        // "System Idle Process" pseudo-pid and tasklist won't list
        // it under a numeric filter. Either way: should report dead.
        // Use a high pid that's astronomically unlikely to be alive
        // to dodge the pid=0 edge case ambiguity on Windows.
        let dead = 4_000_000_001;
        assert!(
            !process_alive(dead),
            "process_alive should return false for synthetic dead pid {dead}"
        );
    }

    #[test]
    fn parse_session_arg_extracts_following_value() {
        assert_eq!(
            parse_session_arg("wire daemon --session slancha-mesh --interval 5"),
            Some("slancha-mesh")
        );
        assert_eq!(
            parse_session_arg("wire daemon --interval 5 --session wire-dev"),
            Some("wire-dev")
        );
        // Mid-cmdline + extra whitespace is fine — split_whitespace handles it.
        assert_eq!(
            parse_session_arg("/path/to/wire   daemon   --session   foo"),
            Some("foo")
        );
    }

    #[test]
    fn parse_session_arg_returns_none_without_flag() {
        assert_eq!(parse_session_arg("wire daemon --interval 5"), None);
        // Bare `wire daemon --all-sessions` is the supervisor itself —
        // it doesn't carry a single `--session`. Operators reading the
        // supervisor's cmdline should see no annotation, not a
        // misleading session attribution.
        assert_eq!(
            parse_session_arg("wire daemon --all-sessions --interval 5"),
            None
        );
        // Empty input is safe.
        assert_eq!(parse_session_arg(""), None);
    }

    #[test]
    fn parse_session_arg_returns_none_when_flag_is_last_token() {
        // `--session` at end with no value following → None, not a panic.
        assert_eq!(parse_session_arg("wire daemon --session"), None);
    }

    #[test]
    fn pid_cmdline_returns_something_for_self() {
        // Cross-platform sanity: our own process must have a cmdline.
        // We can't assert exact content (test runner cmdlines vary) —
        // just that it returns Some and is non-empty.
        let me = std::process::id();
        let cmd = pid_cmdline(me);
        assert!(
            cmd.is_some() && !cmd.as_ref().unwrap().is_empty(),
            "pid_cmdline(self) should return a non-empty cmdline, got {cmd:?}"
        );
    }

    #[test]
    fn pid_cmdline_returns_none_for_dead_pid() {
        // Use the same astronomically-unlikely pid pattern as
        // process_alive_returns_false_for_clearly_dead_pid above.
        let dead = 4_000_000_003;
        assert_eq!(
            pid_cmdline(dead),
            None,
            "pid_cmdline should return None for synthetic dead pid"
        );
    }

    #[test]
    fn kill_process_on_nonexistent_pid_returns_false_or_noop() {
        // Asserting on the return value is brittle because `kill -15`
        // against a missing pid returns 1 on linux but 0 on some
        // BSDs. The contract is "does not panic" — that alone is
        // worth a test, given the cfg-gated dispatch.
        let dead = 4_000_000_002;
        let _ = kill_process(dead, false);
    }

    // ---------- #284.1: run_with_timeout ----------

    use std::time::Instant;

    #[test]
    fn run_with_timeout_returns_some_on_fast_command() {
        // Pick a tiny command that exists on every platform.
        #[cfg(unix)]
        let cmd = {
            let mut c = Command::new("echo");
            c.arg("hello");
            c
        };
        #[cfg(windows)]
        let cmd = {
            let mut c = Command::new("cmd.exe");
            c.args(["/C", "echo hello"]);
            c
        };
        let out = run_with_timeout(cmd, Duration::from_secs(5));
        assert!(out.is_some(), "echo must complete inside 5s");
        let out = out.unwrap();
        assert!(out.status.success());
        let s = String::from_utf8_lossy(&out.stdout);
        assert!(
            s.contains("hello"),
            "stdout should contain `hello`; got {s:?}"
        );
    }

    #[test]
    fn run_with_timeout_returns_none_and_kills_on_slow_command() {
        // Sleep WAY past the timeout so we can prove the wrapper
        // returns inside the timeout window, not at sleep completion.
        #[cfg(unix)]
        let cmd = {
            let mut c = Command::new("sleep");
            c.arg("60");
            c
        };
        #[cfg(windows)]
        let cmd = {
            let mut c = Command::new("powershell.exe");
            c.args([
                "-NoProfile",
                "-NonInteractive",
                "-Command",
                "Start-Sleep -Seconds 60",
            ]);
            c
        };
        let started = Instant::now();
        let out = run_with_timeout(cmd, Duration::from_millis(500));
        let elapsed = started.elapsed();
        assert!(out.is_none(), "slow command must time out, got {out:?}");
        // Generous upper bound — taskkill / kill spawning takes a beat,
        // and CI runners are not real-time. The point is "not 60s".
        assert!(
            elapsed < Duration::from_secs(10),
            "must return well inside the wedged child's runtime; elapsed={elapsed:?}"
        );
    }

    // ---------- #182: same-machine fingerprint platform parsers ----------

    #[test]
    fn parse_ioreg_platform_uuid_extracts_value() {
        let sample = "\
+-o IOPlatformExpertDevice  <class IOPlatformExpertDevice>
  {
    \"IOPlatformUUID\" = \"564D5E2F-AAAA-BBBB-CCCC-0123456789AB\"
    \"IOPlatformSerialNumber\" = \"C02XX\"
  }
";
        assert_eq!(
            parse_ioreg_platform_uuid(sample),
            Some("564D5E2F-AAAA-BBBB-CCCC-0123456789AB".to_string())
        );
        assert_eq!(parse_ioreg_platform_uuid("no uuid here"), None);
    }

    #[test]
    fn parse_reg_machine_guid_extracts_value() {
        let sample = "\r\n\
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\r\n\
    MachineGuid    REG_SZ    11112222-3333-4444-5555-666677778888\r\n";
        assert_eq!(
            parse_reg_machine_guid(sample),
            Some("11112222-3333-4444-5555-666677778888".to_string())
        );
        assert_eq!(parse_reg_machine_guid("no guid"), None);
    }

    #[test]
    fn parse_whoami_sid_extracts_last_quoted_field() {
        let sample = "\"contoso\\\\alice\",\"S-1-5-21-1111111111-2222222222-3333333333-1001\"\r\n";
        assert_eq!(
            parse_whoami_sid(sample),
            Some("S-1-5-21-1111111111-2222222222-3333333333-1001".to_string())
        );
        assert_eq!(parse_whoami_sid("no sid"), None);
    }

    #[test]
    fn platform_shell_timeout_default_is_5s() {
        // SAFETY: serial tests + this test only reads / restores its own var.
        // Save and restore any existing value so a sibling test isn't
        // perturbed (no global ENV_LOCK in this module).
        let prev = std::env::var("WIRE_PLATFORM_TIMEOUT_SECS").ok();
        unsafe { std::env::remove_var("WIRE_PLATFORM_TIMEOUT_SECS") };
        assert_eq!(platform_shell_timeout(), Duration::from_secs(5));
        unsafe { std::env::set_var("WIRE_PLATFORM_TIMEOUT_SECS", "12") };
        assert_eq!(platform_shell_timeout(), Duration::from_secs(12));
        // Restore.
        match prev {
            Some(v) => unsafe { std::env::set_var("WIRE_PLATFORM_TIMEOUT_SECS", v) },
            None => unsafe { std::env::remove_var("WIRE_PLATFORM_TIMEOUT_SECS") },
        }
    }
}