feat: add seat firmware workbench and OTA delivery

This commit is contained in:
vee
2026-09-16 17:55:30 +08:00
parent 3eeca2ea75
commit ab8e43050c
70 changed files with 22594 additions and 316 deletions
+167 -3
View File
@@ -43,6 +43,10 @@ from cognitive_agent.application.codex_assistant import ( # noqa: E402
CodexAssistantService,
CodexVehicleToolError,
)
from cognitive_agent.application.firmware_workbench import ( # noqa: E402
FirmwareWorkbenchError,
FirmwareWorkbenchService,
)
from cognitive_agent.application.gesture_bridge import GestureCapabilityBridge # noqa: E402
from cognitive_agent.application.mode_profile_intent import ( # noqa: E402
normalize_mode_profile_name,
@@ -103,7 +107,11 @@ PUBLIC_AUTH_PATHS = {
"/network.js",
"/api/auth/status",
"/api/auth/login",
"/api/firmware-workbench/releases/latest",
}
PUBLIC_OTA_DOWNLOAD_PATH = re.compile(
r"/api/firmware-workbench/releases/[a-z0-9-]+/download"
)
NON_DISPATCH_DISPLAY_SOURCES = frozenset({"mode_editor_compile"})
@@ -207,6 +215,7 @@ def _lan_ipv4_address() -> str | None:
class SeatDemoHandler(BaseHTTPRequestHandler):
service = SeatDemoService()
firmware_workbench = FirmwareWorkbenchService(REPOSITORY_ROOT)
# Codex defaults to its own empty temporary workspace. A deployment may
# opt into another isolated directory through CODEX_ASSISTANT_WORKSPACE,
# but it must never inherit access to this application's source tree.
@@ -317,6 +326,63 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
if path == "/api/assistant/capabilities":
self._json(self._assistant_capability_catalog().snapshot())
return
if path == "/api/firmware-workbench/state":
self._json(self.firmware_workbench.snapshot())
return
if path == "/api/firmware-workbench/releases/latest":
try:
self._json(self.firmware_workbench.latest_release())
except FirmwareWorkbenchError as exc:
self._json(
{"error": exc.code, "message": str(exc)}, HTTPStatus(exc.status)
)
return
firmware_task_match = re.fullmatch(
r"/api/firmware-workbench/tasks/(?P<task_id>[a-z0-9-]+)", path
)
if firmware_task_match:
try:
self._json(
self.firmware_workbench.task(firmware_task_match.group("task_id"))
)
except FirmwareWorkbenchError as exc:
self._json(
{"error": exc.code, "message": str(exc)}, HTTPStatus(exc.status)
)
return
firmware_artifact_download_match = re.fullmatch(
r"/api/firmware-workbench/artifacts/(?P<artifact_id>[a-z0-9-]+)/download",
path,
)
if firmware_artifact_download_match:
self._serve_firmware_download(
"artifact", firmware_artifact_download_match.group("artifact_id")
)
return
firmware_release_download_match = re.fullmatch(
r"/api/firmware-workbench/releases/(?P<release_id>[a-z0-9-]+)/download",
path,
)
if firmware_release_download_match:
self._serve_firmware_download(
"release", firmware_release_download_match.group("release_id")
)
return
firmware_artifact_match = re.fullmatch(
r"/api/firmware-workbench/artifacts/(?P<artifact_id>[a-z0-9-]+)", path
)
if firmware_artifact_match:
try:
self._json(
self.firmware_workbench.artifact(
firmware_artifact_match.group("artifact_id")
)
)
except FirmwareWorkbenchError as exc:
self._json(
{"error": exc.code, "message": str(exc)}, HTTPStatus(exc.status)
)
return
if path == "/api/runtime":
self._json(self.service.runtime_info())
return
@@ -510,6 +576,11 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
return
if not self._require_json_content_type():
return
if path == "/api/firmware-workbench/tasks" or path.startswith(
"/api/firmware-workbench/"
):
self._handle_firmware_workbench_post(path)
return
if path in {
"/api/hardware/access/request",
"/api/hardware/access/release",
@@ -2529,6 +2600,24 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
HTTPStatus.BAD_REQUEST,
)
return
firmware_artifact_match = re.fullmatch(
r"/api/firmware-workbench/artifacts/(?P<artifact_id>[a-z0-9-]+)",
path,
)
if firmware_artifact_match is not None:
try:
self._json(
self.firmware_workbench.delete_artifact(
firmware_artifact_match.group("artifact_id")
),
HTTPStatus.OK,
)
except FirmwareWorkbenchError as exc:
self._json(
{"error": exc.code, "message": str(exc)},
HTTPStatus(exc.status),
)
return
prefix = "/api/mode-profiles/"
if not path.startswith(prefix):
self._json({"error": "not_found"}, HTTPStatus.NOT_FOUND)
@@ -3259,6 +3348,70 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
raise ValueError("request body must be a JSON object")
return payload
def _handle_firmware_workbench_post(self, path: str) -> None:
try:
payload = self._read_json_body(max_bytes=16 * 1024)
if path == "/api/firmware-workbench/tasks":
result = self.firmware_workbench.start_task(
str(payload.get("instruction", "")),
str(payload.get("baseline_id", "time_only")),
)
self._json(result, HTTPStatus.ACCEPTED)
return
task_action_match = re.fullmatch(
r"/api/firmware-workbench/tasks/(?P<task_id>[a-z0-9-]+)/(?P<action>cancel|retry)",
path,
)
if task_action_match:
task_id = task_action_match.group("task_id")
if task_action_match.group("action") == "cancel":
self._json(self.firmware_workbench.cancel_task(task_id))
else:
self._json(
self.firmware_workbench.retry_task(task_id), HTTPStatus.ACCEPTED
)
return
artifact_publish_match = re.fullmatch(
r"/api/firmware-workbench/artifacts/(?P<artifact_id>[a-z0-9-]+)/publish",
path,
)
if artifact_publish_match:
self._json(
self.firmware_workbench.publish_artifact(
artifact_publish_match.group("artifact_id")
)
)
return
self._json({"error": "not_found"}, HTTPStatus.NOT_FOUND)
except (ValueError, json.JSONDecodeError) as exc:
self._json(
{"error": "invalid_request", "message": str(exc)},
HTTPStatus.BAD_REQUEST,
)
except FirmwareWorkbenchError as exc:
self._json({"error": exc.code, "message": str(exc)}, HTTPStatus(exc.status))
def _serve_firmware_download(self, kind: str, item_id: str) -> None:
try:
if kind == "release":
path, filename = self.firmware_workbench.resolve_release_download(item_id)
else:
path, filename = self.firmware_workbench.resolve_artifact_download(item_id)
content = path.read_bytes()
self.send_response(HTTPStatus.OK)
self.send_header("Content-Type", "application/zip")
self.send_header("Content-Length", str(len(content)))
self.send_header("Cache-Control", "private, no-store")
self.send_header(
"Content-Disposition",
f"attachment; filename*=UTF-8''{quote(filename, safe='.-_')}",
)
self._send_security_headers()
self.end_headers()
self.wfile.write(content)
except FirmwareWorkbenchError as exc:
self._json({"error": exc.code, "message": str(exc)}, HTTPStatus(exc.status))
def _require_json_content_type(self) -> bool:
if is_json_content_type(self.headers.get("Content-Type", "")):
return True
@@ -3505,7 +3658,11 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
pass
def _authorize(self, path: str) -> bool:
if not self.auth.config.enabled or path in PUBLIC_AUTH_PATHS:
if (
not self.auth.config.enabled
or path in PUBLIC_AUTH_PATHS
or PUBLIC_OTA_DOWNLOAD_PATH.fullmatch(path)
):
return True
if self._session_username() is not None:
return True
@@ -3780,8 +3937,13 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
self.end_headers()
def _send_security_headers(self) -> None:
allow_same_origin_frame = (
unquote(urlparse(self.path).path) == "/firmware-workbench.html"
)
self.send_header("X-Content-Type-Options", "nosniff")
self.send_header("X-Frame-Options", "DENY")
self.send_header(
"X-Frame-Options", "SAMEORIGIN" if allow_same_origin_frame else "DENY"
)
self.send_header("Referrer-Policy", "no-referrer")
self.send_header(
"Permissions-Policy",
@@ -3796,7 +3958,8 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
"media-src 'self' blob:; "
"frame-src 'self' http://*:3001; "
"connect-src 'self' blob: http://172.20.10.6:8889; "
"object-src 'none'; base-uri 'none'; frame-ancestors 'none'",
"object-src 'none'; base-uri 'none'; frame-ancestors "
+ ("'self'" if allow_same_origin_frame else "'none'"),
)
if self._is_https():
self.send_header(
@@ -3893,6 +4056,7 @@ def main() -> None:
SeatDemoHandler.nfc.close()
SeatDemoHandler.calibration.close()
SeatDemoHandler.repair.close()
SeatDemoHandler.firmware_workbench.close()
if __name__ == "__main__":