feat(cloud-console): task listing, attempt history, CORS, and console SPA

Implements the cloud-console OpenSpec change: adds GET /v1/tasks (filterable,
bounded pagination, tasks:read) and GET /v1/tasks/{id}/attempts (404 on unknown
task) to the platform SDK, with matching CloudClient methods and a closed-by-
default CLOUD_CONSOLE_CORS_ORIGINS allow-list wired through CloudControlConfig.
Ships an independent Vue 3 + Vite SPA at cloud-console/ that authenticates with
an operator-supplied bearer token held in sessionStorage, renders tasks with
attempt history, device pool, host registry, and the plugin registry with a
registration form.

Backend test suite: 438 passed (-m "not integration"); cloud-console typecheck
and production build both succeed. PostgreSQL-backed repository tests and
manual end-to-end verification remain pending external infrastructure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 14:00:23 +08:00
co-authored by Claude Opus 4.6
parent 62923b9285
commit 2169bb03d9
32 changed files with 3415 additions and 24 deletions
@@ -32,6 +32,7 @@ class CloudControlConfig:
allow_insecure_anonymous: bool = False
credentials: tuple[BearerCredential, ...] = ()
enrollment_credentials: tuple[EnrollmentCredential, ...] = ()
cors_allowed_origins: tuple[str, ...] = ()
def load_control_config(
@@ -90,6 +91,9 @@ def load_control_config(
enrollment_credentials=_parse_enrollment_credentials(
values.get("CLOUD_ENROLLMENT_TOKENS_JSON")
),
cors_allowed_origins=_parse_cors_origins(
values.get("CLOUD_CONSOLE_CORS_ORIGINS")
),
)
validate_control_config(config)
return config
@@ -219,3 +223,9 @@ def _parse_bool(value: str | None, *, default: bool) -> bool:
if normalized in {"0", "false", "no", "off", "disabled", ""}:
return False
raise CloudConfigurationError("boolean configuration value is invalid")
def _parse_cors_origins(raw_value: str | None) -> tuple[str, ...]:
if raw_value is None or not raw_value.strip():
return ()
return tuple(origin.strip() for origin in raw_value.split(",") if origin.strip())