2026-10-18 17:10:26 +02:00
|
|
|
package api
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// 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.
|
2026-10-18 17:10:26 +02:00
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// SpeakRequest is the body of POST /speak.
|
2026-10-18 17:10:26 +02:00
|
|
|
type SpeakRequest struct {
|
2026-10-24 11:15:35 +02:00
|
|
|
// Text is the announcement text (required).
|
2026-10-18 17:10:26 +02:00
|
|
|
Text string `json:"text"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Priority: low | normal | high (default normal when empty).
|
2026-10-18 17:10:26 +02:00
|
|
|
Priority string `json:"priority,omitempty"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Provider pins the provider chain (--provider).
|
2026-10-18 17:10:26 +02:00
|
|
|
Provider string `json:"provider,omitempty"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Voice pins a voice ID (--voice).
|
2026-10-18 17:10:26 +02:00
|
|
|
Voice string `json:"voice,omitempty"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Lang pins the language de | en (empty = automatic detection).
|
2026-10-18 17:10:26 +02:00
|
|
|
Lang string `json:"lang,omitempty"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Now lets the announcement jump ahead of all waiting ones (--now).
|
2026-10-18 17:10:26 +02:00
|
|
|
Now bool `json:"now,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// 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").
|
2026-10-18 17:10:26 +02:00
|
|
|
type SpeakResponse struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Muted bool `json:"muted,omitempty"`
|
2026-10-18 17:40:46 +02:00
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Discarded is the discard reason ("rate_limit" | "quiet_hours"); empty
|
|
|
|
|
// when the announcement was admitted.
|
2026-10-18 17:40:46 +02:00
|
|
|
Discarded string `json:"discarded,omitempty"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Toast reports the replacement by a notification.
|
2026-10-18 17:40:46 +02:00
|
|
|
Toast bool `json:"toast,omitempty"`
|
2026-10-24 09:54:40 +02:00
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Truncated reports that the text was cut to queue.max_text and
|
|
|
|
|
// admitted that way (the law against read-aloud long answers).
|
2026-10-24 09:54:40 +02:00
|
|
|
Truncated bool `json:"truncated,omitempty"`
|
2026-10-18 17:10:26 +02:00
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// CurrentItem describes the running item in GET /status.
|
2026-10-18 17:10:26 +02:00
|
|
|
type CurrentItem struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Text string `json:"text"`
|
|
|
|
|
Priority string `json:"priority"`
|
|
|
|
|
Now bool `json:"now"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// 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.
|
2026-10-18 17:10:26 +02:00
|
|
|
type StatusResponse struct {
|
2026-10-18 17:40:46 +02:00
|
|
|
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"`
|
2026-10-24 09:54:40 +02:00
|
|
|
Limits *LimitsState `json:"limits,omitempty"`
|
2026-10-18 20:06:05 +02:00
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// MCP reports that the daemon offers the MCP endpoint (always true; the
|
|
|
|
|
// field exists for an additive compat view of clients/browsers).
|
2026-10-18 20:06:05 +02:00
|
|
|
MCP bool `json:"mcp"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Cors is the configured CORS allow list of POST /speak (empty [] =
|
|
|
|
|
// door closed). Always present (even when empty), not omitempty.
|
2026-10-18 20:06:05 +02:00
|
|
|
Cors []string `json:"cors"`
|
2026-10-18 17:40:46 +02:00
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// RateLimitState is the rate-limit state in GET /status.
|
2026-10-18 17:40:46 +02:00
|
|
|
type RateLimitState struct {
|
2026-10-24 11:15:35 +02:00
|
|
|
// Interval is the configured minimum spacing as a duration string
|
|
|
|
|
// ("30s"; "0s" = unlimited).
|
2026-10-18 17:40:46 +02:00
|
|
|
Interval string `json:"interval"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// AllowHighBurst reports whether priority=high may violate it.
|
2026-10-18 17:40:46 +02:00
|
|
|
AllowHighBurst bool `json:"allow_high_burst"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// NextSlot is the next free announcement slot as an RFC3339 timestamp;
|
|
|
|
|
// null when playback would be allowed immediately (or unlimited).
|
2026-10-18 17:40:46 +02:00
|
|
|
NextSlot *string `json:"next_slot"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// LimitsState holds the length and age laws in GET /status.
|
2026-10-24 09:54:40 +02:00
|
|
|
type LimitsState struct {
|
2026-10-24 11:15:35 +02:00
|
|
|
// MaxText is the upper bound of the announcement length in characters
|
|
|
|
|
// (0 = unlimited).
|
2026-10-24 09:54:40 +02:00
|
|
|
MaxText int `json:"max_text"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// MaxAge is the longest wait in the queue as a duration string ("2m0s";
|
|
|
|
|
// "0s" = unlimited).
|
2026-10-24 09:54:40 +02:00
|
|
|
MaxAge string `json:"max_age"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// QuietHoursState is the quiet-hours state in GET /status.
|
2026-10-18 17:40:46 +02:00
|
|
|
type QuietHoursState struct {
|
2026-10-24 11:15:35 +02:00
|
|
|
// Window is the configured window ("18:00-08:00"; "" = disabled).
|
2026-10-18 17:40:46 +02:00
|
|
|
Window string `json:"window"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Active reports whether local time is currently inside the window.
|
2026-10-18 17:40:46 +02:00
|
|
|
Active bool `json:"active"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Mode is the configured quiet-hours mode ("discard" | "toast").
|
2026-10-18 17:40:46 +02:00
|
|
|
Mode string `json:"mode"`
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Until is the window end as an RFC3339 timestamp when active;
|
|
|
|
|
// otherwise null.
|
2026-10-18 17:40:46 +02:00
|
|
|
Until *string `json:"until"`
|
2026-10-18 17:10:26 +02:00
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// 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.
|
2026-10-18 17:10:26 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// CancelResponse is the response of POST /cancel.
|
2026-10-18 17:10:26 +02:00
|
|
|
type CancelResponse struct {
|
|
|
|
|
Cancelled int `json:"cancelled"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// MuteResponse is the response of POST /mute and POST /unmute.
|
2026-10-18 17:10:26 +02:00
|
|
|
type MuteResponse struct {
|
|
|
|
|
Muted bool `json:"muted"`
|
|
|
|
|
}
|
2026-10-24 09:49:16 +02:00
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// ShutdownResponse is the response of POST /shutdown.
|
2026-10-24 09:49:16 +02:00
|
|
|
type ShutdownResponse struct {
|
|
|
|
|
OK bool `json:"ok"`
|
|
|
|
|
}
|
2026-10-24 12:14:38 +02:00
|
|
|
|
|
|
|
|
// CancelItemRequest is the body of POST /cancel-item: the id of the
|
|
|
|
|
// announcement to cancel, if it is the one playing.
|
|
|
|
|
type CancelItemRequest struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CancelItemResponse is the answer of POST /cancel-item: whether the id was
|
|
|
|
|
// the playing announcement and has been cancelled.
|
|
|
|
|
type CancelItemResponse struct {
|
|
|
|
|
Cancelled bool `json:"cancelled"`
|
|
|
|
|
}
|