hive-c0re: split OpenAPI summary/description, move param docs to params
utoipa splits a handler's doc comment on the first blank `///` line: everything before it becomes the OpenAPI `summary` (shown in Swagger UI's collapsed endpoint-list row), everything after becomes the `description` (only shown once that row is expanded). With no blank line, the whole doc comment becomes the summary and the description is empty — which is what every handler in hive-c0re/src/dashboard/ was doing, so the all-endpoints list showed full multi-sentence prose next to every route instead of a short one-liner. For every `#[utoipa::path(...)]`-annotated handler across the 19 files in that module: - Inserted a blank `///` line after the first short sentence/clause so utoipa's split produces a real summary + description, where the doc comment had more to say. Left already-short single-clause docs alone (nothing to split). - Where a query struct derives `IntoParams`, moved param prose that duplicated a field's own doc comment out of the handler doc (the field already documents itself in the generated spec), or added a field doc where the handler explained a param that had none. No behavior changes — doc comments and `params()` description text only. Verified `cargo build -p hive-c0re` (clean) and `nix fmt` (zero changes) after. Closes #2969
This commit is contained in:
parent
c5fe61777e
commit
071dbd774c
16 changed files with 193 additions and 133 deletions
|
|
@ -30,7 +30,9 @@ pub(super) struct BuildLogsAllQuery {
|
|||
}
|
||||
|
||||
/// `GET /api/build-logs?limit=N` — most-recent build log headers across
|
||||
/// all agents, newest first. Same JSON shape as the per-agent endpoint.
|
||||
/// all agents, newest first.
|
||||
///
|
||||
/// Same JSON shape as the per-agent endpoint.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/build-logs",
|
||||
|
|
@ -61,10 +63,10 @@ pub(super) struct BuildLogsQuery {
|
|||
}
|
||||
|
||||
/// `GET /api/build-logs/{agent}?limit=N` — most-recent build log
|
||||
/// headers for one agent, newest first. Returns
|
||||
/// `Vec<BuildLogHeader>` (JSON). Limit defaults to 10, server-side
|
||||
/// cap at 50. Backs the per-agent log chip in the agent card and
|
||||
/// the side-panel header list.
|
||||
/// headers for one agent, newest first.
|
||||
///
|
||||
/// Returns `Vec<BuildLogHeader>` (JSON). Backs the per-agent log chip
|
||||
/// in the agent card and the side-panel header list.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/build-logs/{agent}",
|
||||
|
|
@ -102,9 +104,11 @@ pub(super) async fn get_build_logs_agent(
|
|||
}
|
||||
|
||||
/// `GET /api/build-logs/id/{id}` — full build log row (stdout +
|
||||
/// stderr concatenated) by id. Returns `BuildLogFull` (JSON), or
|
||||
/// HTTP 404 when the id doesn't exist (vacuum-reaped, or the
|
||||
/// operator passed a stale id from a refresh race).
|
||||
/// stderr concatenated) by id.
|
||||
///
|
||||
/// Returns `BuildLogFull` (JSON), or HTTP 404 when the id doesn't
|
||||
/// exist (vacuum-reaped, or the operator passed a stale id from a
|
||||
/// refresh race).
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/build-logs/id/{id}",
|
||||
|
|
@ -128,13 +132,15 @@ pub(super) async fn get_build_log_full(
|
|||
}
|
||||
|
||||
/// `GET /api/build-log/{node_id}` — the build log for a **queue node**,
|
||||
/// resolved node id → log-row id → full log. Same `BuildLogFull` JSON
|
||||
/// (`stdout` / `stderr` + header) as `get_build_log_full`; HTTP 404 when the
|
||||
/// node has no linked log (the client gates the request on
|
||||
/// `NodeView.build_log_id`, but a vacuum race can still 404). This is the
|
||||
/// on-demand live-log-panel fetch, distinct from the `build_log_id` on the
|
||||
/// wire — that id is for deep-linking to the BUILD L0GS tab's full history
|
||||
/// view, not for fetching the log content itself.
|
||||
/// resolved node id → log-row id → full log.
|
||||
///
|
||||
/// Same `BuildLogFull` JSON (`stdout` / `stderr` + header) as
|
||||
/// `get_build_log_full`; HTTP 404 when the node has no linked log (the
|
||||
/// client gates the request on `NodeView.build_log_id`, but a vacuum
|
||||
/// race can still 404). This is the on-demand live-log-panel fetch,
|
||||
/// distinct from the `build_log_id` on the wire — that id is for
|
||||
/// deep-linking to the BUILD L0GS tab's full history view, not for
|
||||
/// fetching the log content itself.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/build-log/{node_id}",
|
||||
|
|
@ -201,14 +207,15 @@ struct BuildLogFrame {
|
|||
}
|
||||
|
||||
/// `GET /api/build-logs/id/{id}/stream` — SSE stream that delivers
|
||||
/// incremental stdout/stderr as a build runs. The client connects when
|
||||
/// it opens a running-build panel; the stream closes automatically once
|
||||
/// the build finishes (or the row disappears due to a vacuum).
|
||||
/// incremental stdout/stderr as a build runs.
|
||||
///
|
||||
/// Each frame is a JSON-serialised `BuildLogFrame`. The first frame
|
||||
/// always carries the full accumulated log so far (cursors start at 0);
|
||||
/// subsequent frames carry only new bytes. `done: true` on the final
|
||||
/// frame signals the browser to close the `EventSource`.
|
||||
/// The client connects when it opens a running-build panel; the
|
||||
/// stream closes automatically once the build finishes (or the row
|
||||
/// disappears due to a vacuum). Each frame is a JSON-serialised
|
||||
/// `BuildLogFrame`. The first frame always carries the full
|
||||
/// accumulated log so far (cursors start at 0); subsequent frames
|
||||
/// carry only new bytes. `done: true` on the final frame signals the
|
||||
/// browser to close the `EventSource`.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/build-logs/id/{id}/stream",
|
||||
|
|
@ -308,7 +315,9 @@ pub(super) async fn get_build_log_stream(
|
|||
}
|
||||
|
||||
/// `GET /api/build-logs/id/{id}/raw` — full log as `text/plain` for
|
||||
/// download. Stdout and stderr are concatenated with a `--- stderr ---`
|
||||
/// download.
|
||||
///
|
||||
/// Stdout and stderr are concatenated with a `--- stderr ---`
|
||||
/// separator (same layout the JS side-panel renders). The
|
||||
/// `Content-Disposition` header triggers a browser download with a
|
||||
/// descriptive filename so the operator can save and share the log.
|
||||
|
|
|
|||
|
|
@ -58,11 +58,13 @@ pub(super) struct ExtraForgesQuery {
|
|||
agent: String,
|
||||
}
|
||||
|
||||
/// `GET /api/extra-forges?agent=<name>` — list the external forge accounts
|
||||
/// currently provisioned for `agent`, derived from every `forge-<label>-
|
||||
/// token` file in its state dir (mirrors `matrix_accounts.rs`'s filename-scan
|
||||
/// listing). `base_url` is backfilled from the matching `forge-<label>.json`
|
||||
/// sidecar when present. Never returns a token.
|
||||
/// `GET /api/extra-forges?agent=<name>` — list the external forge
|
||||
/// accounts currently provisioned for `agent`.
|
||||
///
|
||||
/// Derived from every `forge-<label>-token` file in its state dir
|
||||
/// (mirrors `matrix_accounts.rs`'s filename-scan listing). `base_url`
|
||||
/// is backfilled from the matching `forge-<label>.json` sidecar when
|
||||
/// present. Never returns a token.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/extra-forges",
|
||||
|
|
@ -135,8 +137,10 @@ struct ExtraForgeAccountResult {
|
|||
|
||||
/// `POST /api/extra-forge-account` — add persists the operator-pasted
|
||||
/// label/base-URL/token to the agent's state dir via hive-priv; remove
|
||||
/// deletes both files. Purely local — no remote account creation or
|
||||
/// revocation, there is no admin access assumed on the external forge.
|
||||
/// deletes both files.
|
||||
///
|
||||
/// Purely local — no remote account creation or revocation, there is
|
||||
/// no admin access assumed on the external forge.
|
||||
/// Operator-authenticated (dashboard). Never echoes the token back.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
|
|
|
|||
|
|
@ -49,12 +49,13 @@ struct ReadyBody {
|
|||
warnings: Vec<ServerWarning>,
|
||||
}
|
||||
|
||||
/// `GET /health/ready` — readiness. `200` with `{"status":"ok", "warnings":
|
||||
/// [...]}` unless a `crit`-level warning is currently set in
|
||||
/// [`crate::warnings::snapshot`], in which case `503` with
|
||||
/// `{"status":"degraded", ...}`. `warnings` always carries the full
|
||||
/// current list (including `warn`-level entries not affecting the
|
||||
/// status) so a poller gets detail either way.
|
||||
/// `GET /health/ready` — readiness.
|
||||
///
|
||||
/// `200` with `{"status":"ok", "warnings": [...]}` unless a `crit`-level
|
||||
/// warning is currently set in [`crate::warnings::snapshot`], in which
|
||||
/// case `503` with `{"status":"degraded", ...}`. `warnings` always
|
||||
/// carries the full current list (including `warn`-level entries not
|
||||
/// affecting the status) so a poller gets detail either way.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/health/ready",
|
||||
|
|
|
|||
|
|
@ -15,13 +15,14 @@ use hive_priv_sock::{InfraAction, InfraContainer};
|
|||
use super::{AppState, error_response};
|
||||
|
||||
/// `POST /api/infra-container/{name}/{action}` — start / stop / restart a
|
||||
/// hive infrastructure container from the dashboard. `name` parses into
|
||||
/// [`InfraContainer`] (the allowlist; unrecognised names 400), `action`
|
||||
/// into `start` / `stop` / `restart`. Every attempt lands in the audit log
|
||||
/// (actor `"operator"`, action `start_infra` / `stop_infra` /
|
||||
/// `restart_infra`) and streams as an `AuditEntryAdded` event, so
|
||||
/// operator-driven and agent-driven (`infra_admin`) infra actions show up
|
||||
/// in the same AUDIT view.
|
||||
/// hive infrastructure container from the dashboard.
|
||||
///
|
||||
/// `name` parses into [`InfraContainer`] (the allowlist; unrecognised
|
||||
/// names 400), `action` into `start` / `stop` / `restart`. Every attempt
|
||||
/// lands in the audit log (actor `"operator"`, action `start_infra` /
|
||||
/// `stop_infra` / `restart_infra`) and streams as an `AuditEntryAdded`
|
||||
/// event, so operator-driven and agent-driven (`infra_admin`) infra
|
||||
/// actions show up in the same AUDIT view.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/infra-container/{name}/{action}",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ pub(super) struct JournalQuery {
|
|||
}
|
||||
|
||||
/// Read `journalctl -M <container> -b` and return its text output.
|
||||
///
|
||||
/// Operator-only by virtue of the dashboard being host-bound. hive-c0re
|
||||
/// runs unprivileged (privsep), so the `-M` read — which enters the
|
||||
/// container namespace and needs root — is delegated to hive-priv.
|
||||
|
|
@ -160,9 +161,11 @@ pub(super) struct JournalHostQuery {
|
|||
}
|
||||
|
||||
/// `GET /api/journal-host?unit=<unit>&lines=N` — host-side journald (no
|
||||
/// `-M` container flag). Restricted to an allow-list of known host services
|
||||
/// so arbitrary unit names can't be probed. Operator-only by virtue of the
|
||||
/// dashboard binding to a host-only port.
|
||||
/// `-M` container flag).
|
||||
///
|
||||
/// Restricted to an allow-list of known host services so arbitrary unit
|
||||
/// names can't be probed. Operator-only by virtue of the dashboard binding
|
||||
/// to a host-only port.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/journal-host",
|
||||
|
|
|
|||
|
|
@ -60,7 +60,9 @@ pub(super) async fn post_rebuild(
|
|||
}
|
||||
|
||||
/// `POST /api/kill/{name}?graceful=1` — stop `name`, hard by default or
|
||||
/// gracefully (quiesce → drain → stop) when `graceful=1`.
|
||||
/// gracefully when `graceful=1`.
|
||||
///
|
||||
/// Graceful mode: quiesce → drain → stop.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/kill/{name}",
|
||||
|
|
@ -120,7 +122,9 @@ pub(super) async fn post_kill(
|
|||
}
|
||||
|
||||
/// `POST /api/restart/{name}?graceful=1` — restart `name`, hard by default
|
||||
/// or gracefully (quiesce → drain → restart) when `graceful=1`.
|
||||
/// or gracefully when `graceful=1`.
|
||||
///
|
||||
/// Graceful mode: quiesce → drain → restart.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/restart/{name}",
|
||||
|
|
@ -423,9 +427,10 @@ pub(super) struct DestroyForm {
|
|||
purge: Option<String>,
|
||||
}
|
||||
|
||||
/// `POST /api/destroy/{name}` — destroy `name`'s container. Form field
|
||||
/// `purge` (any non-empty value, e.g. `"on"`) also wipes the retained
|
||||
/// state dir instead of leaving a tombstone.
|
||||
/// `POST /api/destroy/{name}` — destroy `name`'s container.
|
||||
///
|
||||
/// Form field `purge` (any non-empty value, e.g. `"on"`) also wipes the
|
||||
/// retained state dir instead of leaving a tombstone.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/destroy/{name}",
|
||||
|
|
|
|||
|
|
@ -104,7 +104,9 @@ fn account_name_from_filename(fname: &str) -> Option<String> {
|
|||
}
|
||||
|
||||
/// `GET /api/matrix-accounts?agent=<name>` — matrix accounts provisioned
|
||||
/// for `agent`, backfilled with `homeserver`/`live`/`user_id` from the daemon's
|
||||
/// for `agent`.
|
||||
///
|
||||
/// Backfilled with `homeserver`/`live`/`user_id` from the daemon's
|
||||
/// snapshot.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
|
|
@ -188,6 +190,7 @@ struct MatrixLoginResult {
|
|||
}
|
||||
|
||||
/// Provision (or refresh) the token for an agent's extra matrix account.
|
||||
///
|
||||
/// password mode → `m.login.password`; token mode → validate via `whoami`.
|
||||
/// On success writes the token to `matrix-token-<account>` via hive-priv and
|
||||
/// kicks the daemon. Operator-authenticated (dashboard). Never echoes the
|
||||
|
|
@ -293,7 +296,9 @@ struct GithubAccountResult {
|
|||
}
|
||||
|
||||
/// Provision (or refresh) an agent's GitHub PAT from the dashboard
|
||||
/// credentials tab. Validates the agent name, then writes the PAT to
|
||||
/// credentials tab.
|
||||
///
|
||||
/// Validates the agent name, then writes the PAT to
|
||||
/// `<state>/github-token` (`0600`, agent-owned) via hive-priv. No account
|
||||
/// creation and no daemon to kick — the agent's `gh` wrapper / git credential
|
||||
/// helper read the file live, so the new token takes effect immediately.
|
||||
|
|
@ -339,9 +344,10 @@ struct GithubAccountStatus {
|
|||
}
|
||||
|
||||
/// `GET /api/github-account?agent=<name>` — whether the agent has a GitHub
|
||||
/// PAT provisioned (its `github-token` file exists). Lets the credentials tab
|
||||
/// show "token stored" vs "not set" instead of a black-hole paste field.
|
||||
/// Never returns the token itself.
|
||||
/// PAT provisioned (its `github-token` file exists).
|
||||
///
|
||||
/// Lets the credentials tab show "token stored" vs "not set" instead of a
|
||||
/// black-hole paste field. Never returns the token itself.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/github-account",
|
||||
|
|
|
|||
|
|
@ -179,11 +179,12 @@ pub(super) struct MetaUpdateForm {
|
|||
}
|
||||
|
||||
/// Bulk-update selected meta flake inputs, then rebuild the affected
|
||||
/// agents in the background. Idempotent w.r.t. selection — choosing
|
||||
/// an input that's already at the latest sha is a no-op (no commit,
|
||||
/// no rebuild ripple). Returns immediately after queueing the work;
|
||||
/// dashboard polls for progress via container `pending` spinners +
|
||||
/// the meta-inputs row sha update.
|
||||
/// agents in the background.
|
||||
///
|
||||
/// Idempotent w.r.t. selection — choosing an input that's already at
|
||||
/// the latest sha is a no-op (no commit, no rebuild ripple). Returns
|
||||
/// immediately after queueing the work; dashboard polls for progress
|
||||
/// via container `pending` spinners + the meta-inputs row sha update.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/meta-update",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use crate::container_stats::ContainerResource;
|
|||
use crate::hive_stats::HiveStats;
|
||||
|
||||
/// Unread operator-directed messages for the dashboard's Y3R C4LL inbox.
|
||||
///
|
||||
/// Returns messages addressed to `"operator"` that haven't been
|
||||
/// acked yet (the operator clears them via the existing
|
||||
/// `POST /api/agent/operator/mark-all-read`). Newest-first; path-shaped
|
||||
|
|
@ -72,12 +73,14 @@ pub(super) async fn api_operator_inbox(State(state): State<AppState>) -> Respons
|
|||
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub(super) struct StatsHiveQuery {
|
||||
/// Stats window; defaults to `24h`.
|
||||
window: Option<String>,
|
||||
}
|
||||
|
||||
/// Hive-wide turn-stats rollup for the dashboard swarm-stats view.
|
||||
///
|
||||
/// Aggregates every agent's `hyperhive-turn-stats.sqlite` read-only
|
||||
/// (skips missing/unreadable ones). Window defaults to `24h`.
|
||||
/// (skips missing/unreadable ones).
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/stats-hive",
|
||||
|
|
@ -97,8 +100,10 @@ pub(super) async fn api_stats_hive(
|
|||
.into_response()
|
||||
}
|
||||
|
||||
/// Live per-agent-container CPU + memory load from cgroup v2. Samples
|
||||
/// CPU over a short interval (~200 ms), so this call briefly awaits.
|
||||
/// Live per-agent-container CPU + memory load from cgroup v2.
|
||||
///
|
||||
/// Samples CPU over a short interval (~200 ms), so this call briefly
|
||||
/// awaits.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/container-resources",
|
||||
|
|
@ -110,8 +115,9 @@ pub(super) async fn api_container_resources() -> Response {
|
|||
}
|
||||
|
||||
/// `GET /api/audit-log` — most-recent agent-initiated privileged-action
|
||||
/// audit entries, newest first (server-clamped to 500). Backs the
|
||||
/// operator dashboard's audit view. Returns
|
||||
/// audit entries, newest first (server-clamped to 500).
|
||||
///
|
||||
/// Backs the operator dashboard's audit view. Returns
|
||||
/// `{ "entries": [AuditEntry…], "total": N }` so the UI can show
|
||||
/// "latest 500 of N" rather than silently capping. `ts_unix` is in
|
||||
/// **seconds**.
|
||||
|
|
@ -138,11 +144,12 @@ pub(super) async fn api_audit_log(State(state): State<AppState>) -> Response {
|
|||
}
|
||||
|
||||
/// Operator-driven "clear this agent's inbox" — backs the side-panel
|
||||
/// "mark all read" button. Marks every message addressed to the
|
||||
/// agent as acked (backfilling `delivered_at` for any still-pending
|
||||
/// rows so vacuum can collect them). Returns `{ "marked": N }` so the
|
||||
/// frontend can show "cleared N messages" feedback without an extra
|
||||
/// fetch.
|
||||
/// "mark all read" button.
|
||||
///
|
||||
/// Marks every message addressed to the agent as acked (backfilling
|
||||
/// `delivered_at` for any still-pending rows so vacuum can collect
|
||||
/// them). Returns `{ "marked": N }` so the frontend can show "cleared
|
||||
/// N messages" feedback without an extra fetch.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/agent/{name}/mark-all-read",
|
||||
|
|
|
|||
|
|
@ -299,13 +299,14 @@ pub(super) struct BatchPermsBody {
|
|||
/// `(logical agent, new groups?, new caps?)`.
|
||||
type StagedPerm = (String, Option<Vec<String>>, Option<Vec<String>>);
|
||||
|
||||
/// Batch permission apply — `POST /api/permissions`. The save-all
|
||||
/// permissions UI sends only the perm-types that actually changed per
|
||||
/// agent; each affected agent gets ONE combined `PermChange`, so the
|
||||
/// dedup key collapses to `(kind, agent)` and an agent whose caps AND
|
||||
/// groups both changed rebuilds once, not twice. The whole batch is
|
||||
/// atomic: every change is validated up front and on any validation
|
||||
/// error nothing is written or enqueued.
|
||||
/// Batch permission apply — `POST /api/permissions`.
|
||||
///
|
||||
/// The save-all permissions UI sends only the perm-types that actually
|
||||
/// changed per agent; each affected agent gets ONE combined
|
||||
/// `PermChange`, so the dedup key collapses to `(kind, agent)` and an
|
||||
/// agent whose caps AND groups both changed rebuilds once, not twice.
|
||||
/// The whole batch is atomic: every change is validated up front and
|
||||
/// on any validation error nothing is written or enqueued.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/permissions",
|
||||
|
|
@ -428,11 +429,12 @@ pub(super) async fn get_stale_permissions(
|
|||
}
|
||||
|
||||
/// Clear all explicit permission entries for a named agent without
|
||||
/// requiring it to exist in the live roster. Used by the P3RM1SS10NS
|
||||
/// tab's "remove" button for agents that have stale explicit entries
|
||||
/// in `tool-groups.json` / `capabilities.json` but are no longer
|
||||
/// running (e.g. an agent that was renamed or destroyed while its
|
||||
/// JSON entries persisted).
|
||||
/// requiring it to exist in the live roster.
|
||||
///
|
||||
/// Used by the P3RM1SS10NS tab's "remove" button for agents that have
|
||||
/// stale explicit entries in `tool-groups.json` / `capabilities.json`
|
||||
/// but are no longer running (e.g. an agent that was renamed or
|
||||
/// destroyed while its JSON entries persisted).
|
||||
///
|
||||
/// Bypasses `guard_agent_name`'s live-roster check intentionally —
|
||||
/// the whole point is to remove entries for non-roster agents. Only
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@ pub(super) async fn post_answer_question(
|
|||
with_cors(resp)
|
||||
}
|
||||
|
||||
/// Resolve a pending operator question with a sentinel answer when
|
||||
/// the operator decides not to / can't answer. The asker harness
|
||||
/// receives a `QuestionAnswered` event with `answer = "[cancelled]"`
|
||||
/// so it can fall back on whatever default it had. Same code path as
|
||||
/// a real answer — just lets the operator close the loop instead of
|
||||
/// letting the question dangle forever.
|
||||
/// `POST /cancel-question/{id}` — resolve a pending question with the
|
||||
/// `[cancelled]` sentinel answer.
|
||||
///
|
||||
/// Used when the operator decides not to / can't answer. The asker
|
||||
/// harness receives a `QuestionAnswered` event with
|
||||
/// `answer = "[cancelled]"` so it can fall back on whatever default
|
||||
/// it had. Same code path as a real answer — just lets the operator
|
||||
/// close the loop instead of letting the question dangle forever.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/cancel-question/{id}",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ use crate::scheduled_prompts_worker::FireNowReport;
|
|||
use super::{AppState, error_problem, error_response};
|
||||
|
||||
/// `GET /api/schedules` — snapshot of every schedule for the
|
||||
/// scheduled-prompts tab. Returns the wire shape directly
|
||||
/// so the frontend can render without an extra translation layer.
|
||||
/// scheduled-prompts tab.
|
||||
///
|
||||
/// Returns the wire shape directly so the frontend can render
|
||||
/// without an extra translation layer.
|
||||
// `hive_sh4re::WireSchedule` (the actual body) has no `ToSchema` — adding
|
||||
// one would pull `utoipa` into the wire-types crate for a single dashboard
|
||||
// endpoint. `serde_json::Value` placeholder; see the batch report.
|
||||
|
|
@ -58,10 +60,11 @@ pub(super) async fn api_schedules(State(state): State<AppState>) -> Response {
|
|||
}
|
||||
|
||||
/// `POST /api/schedules` — operator-direct schedule creation
|
||||
/// (mara: "user can add them manually"). Accepts the same
|
||||
/// `SchedulePromptPayload` shape as the manager request flow but
|
||||
/// skips the approval gate — the operator click *is* the
|
||||
/// approval. The schedule lands directly with
|
||||
/// (mara: "user can add them manually").
|
||||
///
|
||||
/// Accepts the same `SchedulePromptPayload` shape as the manager
|
||||
/// request flow but skips the approval gate — the operator click
|
||||
/// *is* the approval. The schedule lands directly with
|
||||
/// `source = Operator` and the worker picks it up at fire time.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
|
|
@ -120,13 +123,15 @@ pub(super) struct FireNowBody {
|
|||
}
|
||||
|
||||
/// `POST /api/schedules/{id}/fire-now` — operator-initiated
|
||||
/// out-of-band fire of a scheduled prompt. Runs the per-target
|
||||
/// fan-out once immediately and reports per-target outcome counts.
|
||||
/// One-shot schedules are consumed (cancelled) by a manual fire —
|
||||
/// the operator's intent is "send this now, the scheduled time was
|
||||
/// wrong." For recurring schedules the cadence stays intact unless
|
||||
/// the body carries `{"reset_timer": true}`, in which case the
|
||||
/// countdown is re-armed from now (`next_fire_at = now + interval`).
|
||||
/// out-of-band fire of a scheduled prompt.
|
||||
///
|
||||
/// Runs the per-target fan-out once immediately and reports
|
||||
/// per-target outcome counts. One-shot schedules are consumed
|
||||
/// (cancelled) by a manual fire — the operator's intent is "send
|
||||
/// this now, the scheduled time was wrong." For recurring schedules
|
||||
/// the cadence stays intact unless the body carries
|
||||
/// `{"reset_timer": true}`, in which case the countdown is re-armed
|
||||
/// from now (`next_fire_at = now + interval`).
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/schedules/{id}/fire-now",
|
||||
|
|
@ -244,17 +249,19 @@ where
|
|||
}
|
||||
|
||||
/// `PATCH /api/schedules/{id}` — partial update of an existing
|
||||
/// schedule. Mutable fields: `body`, `description`,
|
||||
/// `interval_seconds`, `next_fire_at_unix`, plus the target set
|
||||
/// via `targets_add` / `targets_remove`. Both target lists
|
||||
/// are applied in the same transaction as the scalar fields with
|
||||
/// removes-before-adds; re-adding a previously-removed target
|
||||
/// resets per-target history (fresh start); draining all targets
|
||||
/// auto-cancels the parent schedule. JSON body uses missing-key
|
||||
/// = "leave alone", explicit null = "clear" for `description` +
|
||||
/// `interval_seconds`. Cancelled schedules are refused — submit
|
||||
/// a new one instead. Returns the updated `WireSchedule` so the
|
||||
/// caller's post-edit refresh has the new state inline.
|
||||
/// schedule.
|
||||
///
|
||||
/// Mutable fields: `body`, `description`, `interval_seconds`,
|
||||
/// `next_fire_at_unix`, plus the target set via `targets_add` /
|
||||
/// `targets_remove`. Both target lists are applied in the same
|
||||
/// transaction as the scalar fields with removes-before-adds;
|
||||
/// re-adding a previously-removed target resets per-target history
|
||||
/// (fresh start); draining all targets auto-cancels the parent
|
||||
/// schedule. JSON body uses missing-key = "leave alone", explicit
|
||||
/// null = "clear" for `description` + `interval_seconds`. Cancelled
|
||||
/// schedules are refused — submit a new one instead. Returns the
|
||||
/// updated `WireSchedule` so the caller's post-edit refresh has the
|
||||
/// new state inline.
|
||||
#[utoipa::path(
|
||||
patch,
|
||||
path = "/api/schedules/{id}",
|
||||
|
|
@ -293,8 +300,10 @@ pub(super) async fn patch_schedule(
|
|||
}
|
||||
|
||||
/// `POST /api/schedules/{id}/pause` — pause a schedule so the worker
|
||||
/// skips it until explicitly resumed. Idempotent; no-op on an already-
|
||||
/// paused row. Returns 404 when the schedule is cancelled or not found.
|
||||
/// skips it until explicitly resumed.
|
||||
///
|
||||
/// Idempotent; no-op on an already-paused row. Returns 404 when the
|
||||
/// schedule is cancelled or not found.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/schedules/{id}/pause",
|
||||
|
|
@ -326,6 +335,7 @@ pub(super) async fn post_schedule_pause(
|
|||
}
|
||||
|
||||
/// `POST /api/schedules/{id}/resume` — resume a paused schedule.
|
||||
///
|
||||
/// Idempotent; no-op on an already-active row. Returns 404 when the
|
||||
/// schedule is cancelled or not found.
|
||||
#[utoipa::path(
|
||||
|
|
@ -360,8 +370,10 @@ pub(super) async fn post_schedule_resume(
|
|||
|
||||
/// `POST /api/schedules/{id}/cancel` — operator-side cancel
|
||||
/// (whole schedule when no `targets` field, partial when one is
|
||||
/// provided). Operator bypasses the topology check; the manager
|
||||
/// surface enforces it for agent callers.
|
||||
/// provided).
|
||||
///
|
||||
/// Operator bypasses the topology check; the manager surface
|
||||
/// enforces it for agent callers.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/schedules/{id}/cancel",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ use crate::paths::{AGENTS_ROOT, SHARED_ROOT};
|
|||
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub(super) struct StateFileQuery {
|
||||
/// Absolute path under an agent's `state/` dir or under `shared/`;
|
||||
/// checked against the allow-list (`docs/security.md::State-file
|
||||
/// endpoint`).
|
||||
path: String,
|
||||
}
|
||||
|
||||
|
|
@ -164,10 +167,10 @@ pub fn scan_validated_paths(body: &str) -> Vec<String> {
|
|||
out
|
||||
}
|
||||
|
||||
/// `GET /api/state-file?path=…` — serve an allow-listed per-agent
|
||||
/// `state/` or `shared/` file. Raster images get their real
|
||||
/// content-type; everything else is served as (possibly truncated)
|
||||
/// text.
|
||||
/// `GET /api/state-file?path=…` — serve an allow-listed file.
|
||||
///
|
||||
/// Raster images get their real content-type; everything else is
|
||||
/// served as (possibly truncated) text.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/state-file",
|
||||
|
|
|
|||
|
|
@ -293,10 +293,12 @@ where
|
|||
/// minutes.
|
||||
const CRASH_WARNING_WINDOW: std::time::Duration = std::time::Duration::from_mins(10);
|
||||
|
||||
/// `GET /api/state` — cold-load snapshot of the whole dashboard: roster,
|
||||
/// approvals (+ history), questions (+ history), tombstones, job queue,
|
||||
/// meta inputs, and more. Live clients then follow `/api/dashboard/stream`
|
||||
/// (SSE) for incremental updates keyed off `seq`.
|
||||
/// `GET /api/state` — cold-load snapshot of the whole dashboard.
|
||||
///
|
||||
/// Includes the roster, approvals (+ history), questions (+ history),
|
||||
/// tombstones, job queue, meta inputs, and more. Live clients then
|
||||
/// follow `/api/dashboard/stream` (SSE) for incremental updates keyed
|
||||
/// off `seq`.
|
||||
// `StateSnapshot` is a large tree of nested view types (`ContainerView`,
|
||||
// `ApprovalView`, `QuestionView`, ...) with no `ToSchema` anywhere in that
|
||||
// graph; wiring it up is a schema-modelling project of its own, well past
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ pub(super) struct SetParentBulkEntry {
|
|||
}
|
||||
|
||||
/// `POST /api/topology/set-parent` — operator-driven parent move.
|
||||
///
|
||||
/// Form fields: `child` (required, agent name), `new_parent`
|
||||
/// (optional — empty / absent string ⇒ promote to root). Refuses
|
||||
/// cycles and unknown agents (surfaced async on the job view — this
|
||||
|
|
@ -102,12 +103,13 @@ pub(super) async fn post_set_parent(
|
|||
Ok((StatusCode::OK, "ok").into_response())
|
||||
}
|
||||
|
||||
/// `POST /api/topology/set-parent-bulk` — move multiple agents in a single
|
||||
/// request, producing **one** git commit. JSON body: `[{"child":"name",
|
||||
/// "new_parent":"target-or-null"}, ...]`. Empty array is a no-op (200 OK).
|
||||
/// First identifier that fails to parse aborts the whole batch before
|
||||
/// anything is submitted — a partially-invalid bulk move never reaches
|
||||
/// the queue.
|
||||
/// `POST /api/topology/set-parent-bulk` — move multiple agents in a
|
||||
/// single request, producing **one** git commit.
|
||||
///
|
||||
/// JSON body: `[{"child":"name", "new_parent":"target-or-null"}, ...]`.
|
||||
/// Empty array is a no-op (200 OK). First identifier that fails to
|
||||
/// parse aborts the whole batch before anything is submitted — a
|
||||
/// partially-invalid bulk move never reaches the queue.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/topology/set-parent-bulk",
|
||||
|
|
|
|||
|
|
@ -58,8 +58,10 @@ pub(super) struct PushWebhookRepo {
|
|||
}
|
||||
|
||||
/// POST `/webhook/knowledge` — Forgejo push webhook for
|
||||
/// `internal/knowledge`. Runs `git pull` on the local clone so
|
||||
/// agents see up-to-date documents on their next turn.
|
||||
/// `internal/knowledge`.
|
||||
///
|
||||
/// Runs `git pull` on the local clone so agents see up-to-date documents
|
||||
/// on their next turn.
|
||||
///
|
||||
/// Expected Forgejo webhook configuration:
|
||||
/// - URL: `https://<HYPERHIVE_HIVE_DOMAIN>/webhook/knowledge`
|
||||
|
|
|
|||
Loading…
Reference in a new issue