dosync 0.6.1__tar.gz → 0.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dosync-0.6.1 → dosync-0.6.2}/PKG-INFO +1 -1
- {dosync-0.6.1 → dosync-0.6.2}/dosync/__init__.py +1 -1
- {dosync-0.6.1 → dosync-0.6.2}/dosync/server.py +21 -1
- {dosync-0.6.1 → dosync-0.6.2}/dosync.egg-info/PKG-INFO +1 -1
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_server.py +18 -0
- {dosync-0.6.1 → dosync-0.6.2}/LICENSE +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/README.md +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapter_drafting.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/__init__.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/ble.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/declarative.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/homeassistant.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/matter.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/mavlink.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/mqtt.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/notifications.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/shelly.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/adapters/wiz.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/audit_backup.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/auth.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/auth_fastapi.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/cert_signing.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/certify.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/cli.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/composite_operations.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/config_reference.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/dashboard.html +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/db.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/declarative.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/device_arbiter.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/discoverers.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/discoverers_mdns.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/discoverers_ssdp.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/discovery.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/ed25519_pure.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/examples/__init__.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/examples/declarative/3d-printer.yaml +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/examples/declarative/air-conditioner.yaml +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/examples/declarative/building-lighting.json +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/examples/declarative/industrial-conveyor.yaml +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/examples/declarative/light-generic.yaml +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/examples/declarative/television.yaml +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/executor.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/geo.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/hub.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/hub_monitor.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/lightweight.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/manage.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/mcp_server.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/metrics.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/models.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/operation_guards.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/operation_supervisor.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/operations.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/paths.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/plugins.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/policies.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/policy_config.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/py.typed +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/reconciler.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/route_composer.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/security.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/spec_coverage.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/templates/adapter-draft-prompt.md +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync/validation.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync.egg-info/SOURCES.txt +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync.egg-info/dependency_links.txt +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync.egg-info/entry_points.txt +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync.egg-info/requires.txt +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/dosync.egg-info/top_level.txt +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/pyproject.toml +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/setup.cfg +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_adapter_drafting.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_adapters.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_audit_archive.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_audit_backup.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_audit_chain_integrity.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_audit_provenance.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_auth.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_ble_adapter.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_certification_honesty.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_claim_state_machine.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_composite_operations.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_composite_orchestration.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_composition_kind_db.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_composition_kind_endpoint.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_composition_routing.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_dashboard_tells_the_truth.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_db.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_declarative_adapters.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_declarative_quarantine.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_deployment_env_contract.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_deployment_layout.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_device_health.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_device_heartbeat.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_device_identity.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_direct_action_governance.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_discovery_adoption.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_documentation_is_navigable.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_drone_policies.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_ed25519_pure.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_emergency_preemption.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_env_loading_is_explicit.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_evaluation_metrics.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_event_loop_migration.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_explain_consistency.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_explain_resolve_parity.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_geo.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_ha_bridge_hygiene.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_hub_monitor.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_idempotency.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_independent_observation.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_integration_suite.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_lightweight_heartbeat.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_mavlink_adapter.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_mavlink_channels.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_mavlink_listener.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_mavlink_return_home.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_mavlink_single_reader.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_mavlink_telemetry_closure.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_mcp_dynamic_intents.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_mcp_partial_progress.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_metrics.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_models.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_multihub_endpoints.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_no_operator_data.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_notification_templates.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_operation_guards.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_operation_supervisor.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_operations.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_operations_endpoints.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_operations_persistence.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_operations_wiring.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_panel_polish_2026_07_21.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_policies.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_policy_config.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_public_claims.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_pushed_verification.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_quickstart_is_runnable.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_reachability_cause.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_recall_benchmark_postpolicy.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_reconciler.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_resolution_wiring.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_resolver_scoring.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_resolver_semantics.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_route_composer.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_sensor_kind.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_simulation_is_declared.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_telemetry_bridge.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_third_party_adapters.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_transport_discoverers.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_universal_intent_contract.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_validation.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_validation_integration.py +0 -0
- {dosync-0.6.1 → dosync-0.6.2}/tests/test_wiring_audit.py +0 -0
|
@@ -12,7 +12,8 @@ import json
|
|
|
12
12
|
import os
|
|
13
13
|
import re
|
|
14
14
|
from fastapi import Depends, FastAPI, HTTPException, Request, WebSocket, WebSocketDisconnect
|
|
15
|
-
from fastapi.responses import FileResponse, HTMLResponse,
|
|
15
|
+
from fastapi.responses import (FileResponse, HTMLResponse, JSONResponse,
|
|
16
|
+
PlainTextResponse)
|
|
16
17
|
from fastapi.staticfiles import StaticFiles
|
|
17
18
|
from pydantic import BaseModel, field_validator
|
|
18
19
|
|
|
@@ -933,7 +934,26 @@ async def lifespan(app: FastAPI):
|
|
|
933
934
|
log.warning("Could not flush the audit head on shutdown: %s", _fh_e)
|
|
934
935
|
log.info("DoSync Hub shutting down")
|
|
935
936
|
|
|
937
|
+
class _UTF8JSONResponse(JSONResponse):
|
|
938
|
+
"""`application/json; charset=utf-8`, said out loud rather than assumed.
|
|
939
|
+
|
|
940
|
+
JSON is UTF-8 by definition and FastAPI therefore omits the charset — which
|
|
941
|
+
is correct and, for one widely used client, useless. Windows PowerShell 5.1
|
|
942
|
+
falls back to Latin-1 when a response does not declare one, so an operator
|
|
943
|
+
following this project's own Windows instructions saw
|
|
944
|
+
`read-only status query â device has no sensors` where an em dash belonged,
|
|
945
|
+
and would see the same for every accented character a deployment uses in a
|
|
946
|
+
device name.
|
|
947
|
+
|
|
948
|
+
Being right about the specification while a reader gets mojibake is the
|
|
949
|
+
kind of correctness this project has decided is not worth much.
|
|
950
|
+
"""
|
|
951
|
+
|
|
952
|
+
media_type = "application/json; charset=utf-8"
|
|
953
|
+
|
|
954
|
+
|
|
936
955
|
app = FastAPI(
|
|
956
|
+
default_response_class=_UTF8JSONResponse,
|
|
937
957
|
title="DoSync Hub",
|
|
938
958
|
description=(
|
|
939
959
|
"DoSync Protocol — REST API\n\n"
|
|
@@ -199,3 +199,21 @@ def test_the_shipped_gpio_adapter_uses_the_current_endpoint():
|
|
|
199
199
|
src = (REPO / "gpio_adapter.py").read_text()
|
|
200
200
|
assert 'hub_post("/v1/intent/async"' in src
|
|
201
201
|
assert 'hub_post("/v1/intent"' not in src
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def test_json_responses_declare_utf8():
|
|
205
|
+
"""`read-only status query â device has no sensors to read`.
|
|
206
|
+
|
|
207
|
+
That is what a Windows operator saw following this project's own
|
|
208
|
+
instructions. JSON is UTF-8 by definition, so FastAPI omits the charset —
|
|
209
|
+
correct, and useless for Windows PowerShell 5.1, which falls back to
|
|
210
|
+
Latin-1 when a response does not declare one. Every em dash and every
|
|
211
|
+
accented character in a device name arrives mangled.
|
|
212
|
+
|
|
213
|
+
The README documents `Invoke-RestMethod` as the way to call this API from
|
|
214
|
+
Windows, so the tool it recommends was the one showing broken text.
|
|
215
|
+
"""
|
|
216
|
+
response = client.get("/v1/status")
|
|
217
|
+
assert "charset=utf-8" in response.headers.get("content-type", "").lower(), (
|
|
218
|
+
"JSON responses do not declare their charset, so clients that guess "
|
|
219
|
+
"wrongly — PowerShell 5.1 among them — mangle every non-ASCII byte")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|