fix: Windows daemon survives session jobs; quieter wrap note; correct mute error
Verified on a Windows 11 VM over WinRM: doctor, SAPI synthesis, Edge, oto/WASAPI playback, autostart, mute file, wrap exit codes, doctor --probe and toast mode all work. Three findings fixed: - The autostarted daemon died with the WinRM shell: Windows session hosts (WinRM, SSH, CI runners) put children into a job object that is terminated with the session. Spawn now starts the daemon with CREATE_BREAKAWAY_FROM_JOB first and falls back to a start without it when the job forbids breakaway. Confirmed: the daemon now outlives the WinRM shell and `talk daemon stop` still ends it. - `talk mute` without a daemon printed "daemon call failed (<nil>)": the outer err was reported instead of the call's. Now "no daemon (<reason>); writing the mute file directly". - `talk wrap` printed "command did not exit cleanly" for every non-zero exit code. The note is now reserved for commands that produced no exit code of their own (not found, signal); a plain failure is carried by the announcement and the exit code alone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
85fa64cfe6
commit
1b971bc3f6
7 files changed
+52
-22
No files matched your search
+3
-2
@@ -41,14 +41,15 @@ func setMute(muted bool, stdout, stderr io.Writer) int {
|
||||
client := api.NewClient(cfg.Server.Port)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), daemonCallTimeout)
|
||||
defer cancel()
|
||||
if err := client.SetMute(ctx, muted); err == nil {
|
||||
callErr := client.SetMute(ctx, muted)
|
||||
if callErr == nil {
|
||||
printMuteState(muted, stdout)
|
||||
return exitOK
|
||||
}
|
||||
|
||||
// Daemon call failed (usually: not running): write the file directly;
|
||||
// the daemon (and the direct path) re-read it on every announcement.
|
||||
fmt.Fprintf(stderr, "talk: daemon call failed (%v); writing the mute file directly\n", err)
|
||||
fmt.Fprintf(stderr, "talk: no daemon (%v); writing the mute file directly\n", callErr)
|
||||
dir, err := config.Dir(getenv)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "talk: %v\n", err)
|
||||
|
||||
+12
-3
@@ -50,9 +50,11 @@ func runWrap(args []string, opts *sayOptions, stdin io.Reader, stdout, stderr io
|
||||
runErr := execChild(args, stdin, stdout, stderr)
|
||||
duration := time.Since(start)
|
||||
code := childExitCode(runErr)
|
||||
if runErr != nil {
|
||||
// For example: binary not found or killed by a signal. The note goes
|
||||
// to stderr; the announcement names the same code wrap exits with.
|
||||
if runErr != nil && !isPlainExit(runErr) {
|
||||
// Binary not found or killed by a signal: the command never produced
|
||||
// an exit code of its own, so say why wrap exits with the one it
|
||||
// does. A plain non-zero exit code needs no note; the announcement
|
||||
// and the exit code carry it.
|
||||
fmt.Fprintf(stderr, "talk wrap: command did not exit cleanly: %v\n", runErr)
|
||||
}
|
||||
|
||||
@@ -121,6 +123,13 @@ func childExitCode(runErr error) int {
|
||||
return exitNotFound
|
||||
}
|
||||
|
||||
// isPlainExit reports whether runErr is just a non-zero exit code of a
|
||||
// command that ran and exited normally.
|
||||
func isPlainExit(runErr error) bool {
|
||||
var ee *exec.ExitError
|
||||
return errors.As(runErr, &ee) && ee.ExitCode() >= 0
|
||||
}
|
||||
|
||||
// wrapLabel resolves the announcement label: config `wrap.label` (env
|
||||
// override TALK_WRAP_LABEL) beats the command's base name
|
||||
// (`/usr/bin/make` → "make").
|
||||
|
||||
Reference in new issue
Block a user