Files
talk/internal/api/types.go
T
m3ta-chironandClaude Fable 5.1 85fa64cfe6 refactor: switch the whole project to English
talk is going to be published. Code comments, CLI output, error messages,
daemon log lines, MCP descriptions and results, test names and assertions,
README, spec, skill, build scripts and flake are now English. Only the
spoken announcement texts stay bilingual: the wrap result and the test
sentence are German or English depending on the detected language.

Language fallback is now configurable: `lang = "en" | "de"` (env
TALK_LANG, default en) is used when detection cannot tell (numbers, single
words, ties) and for talk's own announcements. langdetect.Detect takes the
fallback as an argument instead of hard-coding German. `talk setup` asks for
it; `talk doctor` and `talk status` show it.

Vocabulary pinned across packages so the process-boundary tests observe
stable strings: chain log lines ("chain: tier X skipped (not available)",
"falling back to the next tier", "spoken via X"), queue lines ("discarded:
quiet hours ... active", "replaced by toast", "max_age"), CLI notes
("speaking directly", "daemon started at", "no daemon", "nothing to cancel"),
status block with aligned English labels, MCP messages, origin guard 403.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 11:15:35 +02:00

155 lines
5.2 KiB
Go

package api
// Request/response types of the daemon HTTP contract (binding fields). The
// CLI and other consumers (MCP) use the same types: one source, no contract
// drift between daemon and client.
// SpeakRequest is the body of POST /speak.
type SpeakRequest struct {
// Text is the announcement text (required).
Text string `json:"text"`
// Priority: low | normal | high (default normal when empty).
Priority string `json:"priority,omitempty"`
// Provider pins the provider chain (--provider).
Provider string `json:"provider,omitempty"`
// Voice pins a voice ID (--voice).
Voice string `json:"voice,omitempty"`
// Lang pins the language de | en (empty = automatic detection).
Lang string `json:"lang,omitempty"`
// Now lets the announcement jump ahead of all waiting ones (--now).
Now bool `json:"now,omitempty"`
}
// SpeakResponse is the response of POST /speak: 202 {"id":"..."}; with mute
// active 202 {"id":"","muted":true} (announcement discarded; documented
// behaviour, not an error). Discarded spam is reported just as
// documented: 202 {"id":"","discarded":"rate_limit"|"quiet_hours"}, plus
// "toast":true when the quiet-hours announcement was replaced by a
// notification (quiet_mode = "toast").
type SpeakResponse struct {
ID string `json:"id"`
Muted bool `json:"muted,omitempty"`
// Discarded is the discard reason ("rate_limit" | "quiet_hours"); empty
// when the announcement was admitted.
Discarded string `json:"discarded,omitempty"`
// Toast reports the replacement by a notification.
Toast bool `json:"toast,omitempty"`
// Truncated reports that the text was cut to queue.max_text and
// admitted that way (the law against read-aloud long answers).
Truncated bool `json:"truncated,omitempty"`
}
// CurrentItem describes the running item in GET /status.
type CurrentItem struct {
ID string `json:"id"`
Text string `json:"text"`
Priority string `json:"priority"`
Now bool `json:"now"`
}
// StatusResponse is the response of GET /status: queue length, current item
// (null when idle), mute state, active provider, daemon version. QueueIDs
// lists the waiting IDs in processing order; the CLI needs them for its
// --wait polling. The law state (rate_limit, quiet_hours) and mcp/cors
// (compat fields: MCP endpoint present, configured CORS origins) are
// additive.
type StatusResponse struct {
Version string `json:"version"`
Queue int `json:"queue"`
QueueIDs []string `json:"queue_ids,omitempty"`
Current *CurrentItem `json:"current"`
Muted bool `json:"muted"`
Provider string `json:"provider"`
RateLimit *RateLimitState `json:"rate_limit,omitempty"`
QuietHours *QuietHoursState `json:"quiet_hours,omitempty"`
Limits *LimitsState `json:"limits,omitempty"`
// MCP reports that the daemon offers the MCP endpoint (always true; the
// field exists for an additive compat view of clients/browsers).
MCP bool `json:"mcp"`
// Cors is the configured CORS allow list of POST /speak (empty [] =
// door closed). Always present (even when empty), not omitempty.
Cors []string `json:"cors"`
}
// RateLimitState is the rate-limit state in GET /status.
type RateLimitState struct {
// Interval is the configured minimum spacing as a duration string
// ("30s"; "0s" = unlimited).
Interval string `json:"interval"`
// AllowHighBurst reports whether priority=high may violate it.
AllowHighBurst bool `json:"allow_high_burst"`
// NextSlot is the next free announcement slot as an RFC3339 timestamp;
// null when playback would be allowed immediately (or unlimited).
NextSlot *string `json:"next_slot"`
}
// LimitsState holds the length and age laws in GET /status.
type LimitsState struct {
// MaxText is the upper bound of the announcement length in characters
// (0 = unlimited).
MaxText int `json:"max_text"`
// MaxAge is the longest wait in the queue as a duration string ("2m0s";
// "0s" = unlimited).
MaxAge string `json:"max_age"`
}
// QuietHoursState is the quiet-hours state in GET /status.
type QuietHoursState struct {
// Window is the configured window ("18:00-08:00"; "" = disabled).
Window string `json:"window"`
// Active reports whether local time is currently inside the window.
Active bool `json:"active"`
// Mode is the configured quiet-hours mode ("discard" | "toast").
Mode string `json:"mode"`
// Until is the window end as an RFC3339 timestamp when active;
// otherwise null.
Until *string `json:"until"`
}
// PendingOrCurrent reports whether the announcement with the ID is still
// waiting or currently playing; the basis of the CLI's --wait polling. If
// it is neither waiting nor playing, it is done (or cancelled); in both
// cases the caller has nothing left to wait for.
func (s StatusResponse) PendingOrCurrent(id string) bool {
if s.Current != nil && s.Current.ID == id {
return true
}
for _, qid := range s.QueueIDs {
if qid == id {
return true
}
}
return false
}
// CancelResponse is the response of POST /cancel.
type CancelResponse struct {
Cancelled int `json:"cancelled"`
}
// MuteResponse is the response of POST /mute and POST /unmute.
type MuteResponse struct {
Muted bool `json:"muted"`
}
// ShutdownResponse is the response of POST /shutdown.
type ShutdownResponse struct {
OK bool `json:"ok"`
}