tescmd 0.2.0__tar.gz → 0.4.0__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.
- tescmd-0.4.0/.env.example +75 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/.gitignore +3 -1
- tescmd-0.4.0/CHANGELOG.md +192 -0
- tescmd-0.4.0/CLAUDE.md +131 -0
- tescmd-0.4.0/PKG-INFO +300 -0
- tescmd-0.4.0/README.md +247 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/api-costs.md +26 -11
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/architecture.md +129 -11
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/authentication.md +52 -8
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/bot-integration.md +31 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/commands.md +267 -5
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/development.md +13 -5
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/faq.md +22 -2
- tescmd-0.4.0/docs/mcp.md +416 -0
- tescmd-0.4.0/docs/openclaw.md +463 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/setup.md +72 -31
- {tescmd-0.2.0 → tescmd-0.4.0}/docs/vehicle-command-protocol.md +43 -12
- tescmd-0.4.0/images/tescmd_header.jpeg +0 -0
- tescmd-0.4.0/images/tescmd_logo.jpeg +0 -0
- tescmd-0.4.0/images/tescmd_mcp.png +0 -0
- tescmd-0.4.0/images/tescmd_serve.png +0 -0
- tescmd-0.4.0/images/tescmd_waypoints.png +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/pyproject.toml +14 -2
- tescmd-0.4.0/skills/tescmd/SKILL.md +524 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/__init__.py +1 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/client.py +41 -4
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/command.py +1 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/errors.py +5 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/signed_command.py +19 -14
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/auth/oauth.py +15 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/auth/server.py +6 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/auth/token_store.py +8 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cache/response_cache.py +8 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/_client.py +142 -20
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/_options.py +2 -4
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/auth.py +255 -106
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/energy.py +2 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/key.py +6 -7
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/main.py +27 -8
- tescmd-0.4.0/src/tescmd/cli/mcp_cmd.py +153 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/nav.py +3 -1
- tescmd-0.4.0/src/tescmd/cli/openclaw.py +169 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/security.py +7 -1
- tescmd-0.4.0/src/tescmd/cli/serve.py +923 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/setup.py +147 -58
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/sharing.py +2 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/status.py +1 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/trunk.py +8 -17
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/user.py +16 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/vehicle.py +135 -462
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/deploy/github_pages.py +21 -2
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/deploy/tailscale_serve.py +96 -8
- tescmd-0.4.0/src/tescmd/mcp/__init__.py +7 -0
- tescmd-0.4.0/src/tescmd/mcp/server.py +648 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/models/auth.py +5 -2
- tescmd-0.4.0/src/tescmd/openclaw/__init__.py +23 -0
- tescmd-0.4.0/src/tescmd/openclaw/bridge.py +330 -0
- tescmd-0.4.0/src/tescmd/openclaw/config.py +167 -0
- tescmd-0.4.0/src/tescmd/openclaw/dispatcher.py +529 -0
- tescmd-0.4.0/src/tescmd/openclaw/emitter.py +175 -0
- tescmd-0.4.0/src/tescmd/openclaw/filters.py +123 -0
- tescmd-0.4.0/src/tescmd/openclaw/gateway.py +700 -0
- tescmd-0.4.0/src/tescmd/openclaw/telemetry_store.py +53 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/output/rich_output.py +46 -14
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/commands.py +2 -2
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/encoder.py +16 -13
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/payloads.py +132 -11
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/session.py +8 -5
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/signer.py +3 -17
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/__init__.py +9 -0
- tescmd-0.4.0/src/tescmd/telemetry/cache_sink.py +154 -0
- tescmd-0.4.0/src/tescmd/telemetry/csv_sink.py +180 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/dashboard.py +4 -4
- tescmd-0.4.0/src/tescmd/telemetry/fanout.py +49 -0
- tescmd-0.4.0/src/tescmd/telemetry/fields.py +427 -0
- tescmd-0.4.0/src/tescmd/telemetry/mapper.py +239 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/server.py +26 -19
- tescmd-0.4.0/src/tescmd/telemetry/setup.py +468 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/tailscale.py +78 -16
- tescmd-0.4.0/src/tescmd/telemetry/tui.py +1716 -0
- tescmd-0.4.0/src/tescmd/triggers/__init__.py +18 -0
- tescmd-0.4.0/src/tescmd/triggers/manager.py +264 -0
- tescmd-0.4.0/src/tescmd/triggers/models.py +93 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/test_signed_command.py +2 -2
- tescmd-0.4.0/tests/auth/test_oauth_extended.py +243 -0
- tescmd-0.4.0/tests/cli/test_auth.py +831 -0
- tescmd-0.4.0/tests/cli/test_bugfixes.py +236 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_nav_exec.py +1 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_setup.py +644 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_trunk_exec.py +10 -23
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_vehicle_exec.py +1 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_wake_confirmation.py +13 -1
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/deploy/test_tailscale_serve.py +63 -7
- tescmd-0.4.0/tests/integration/test_serve.py +163 -0
- tescmd-0.4.0/tests/mcp/test_server.py +407 -0
- tescmd-0.4.0/tests/mcp/test_tools.py +86 -0
- tescmd-0.4.0/tests/openclaw/test_bridge.py +618 -0
- tescmd-0.4.0/tests/openclaw/test_config.py +196 -0
- tescmd-0.4.0/tests/openclaw/test_dispatcher.py +1152 -0
- tescmd-0.4.0/tests/openclaw/test_emitter.py +165 -0
- tescmd-0.4.0/tests/openclaw/test_filters.py +131 -0
- tescmd-0.4.0/tests/openclaw/test_gateway.py +973 -0
- tescmd-0.4.0/tests/openclaw/test_telemetry_store.py +106 -0
- tescmd-0.4.0/tests/output/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/output/test_rich_output.py +7 -7
- tescmd-0.4.0/tests/protocol/__init__.py +0 -0
- tescmd-0.4.0/tests/protocol/test_boombox.py +74 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/protocol/test_commands.py +15 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/protocol/test_encoder.py +22 -19
- tescmd-0.4.0/tests/protocol/test_navigation.py +142 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/protocol/test_session.py +3 -3
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/protocol/test_signer.py +3 -13
- tescmd-0.4.0/tests/telemetry/__init__.py +0 -0
- tescmd-0.4.0/tests/telemetry/test_cache_sink.py +277 -0
- tescmd-0.4.0/tests/telemetry/test_csv_sink.py +321 -0
- tescmd-0.4.0/tests/telemetry/test_fanout.py +105 -0
- tescmd-0.4.0/tests/telemetry/test_fields.py +119 -0
- tescmd-0.4.0/tests/telemetry/test_mapper.py +192 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/telemetry/test_stream_cmd.py +2 -0
- tescmd-0.4.0/tests/telemetry/test_tui.py +600 -0
- tescmd-0.4.0/tests/triggers/__init__.py +0 -0
- tescmd-0.4.0/tests/triggers/test_manager.py +474 -0
- tescmd-0.4.0/tests/triggers/test_models.py +162 -0
- tescmd-0.2.0/CHANGELOG.md +0 -99
- tescmd-0.2.0/CLAUDE.md +0 -430
- tescmd-0.2.0/PKG-INFO +0 -495
- tescmd-0.2.0/README.md +0 -445
- tescmd-0.2.0/src/tescmd/telemetry/fields.py +0 -248
- tescmd-0.2.0/tests/auth/test_oauth_extended.py +0 -125
- tescmd-0.2.0/tests/cli/test_auth.py +0 -74
- tescmd-0.2.0/tests/telemetry/test_fields.py +0 -73
- {tescmd-0.2.0 → tescmd-0.4.0}/.github/workflows/publish.yml +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/.github/workflows/test.yml +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/LICENSE +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/scripts/validate_fleet_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/spec/fleet_api_spec.json +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/__main__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/_internal/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/_internal/async_utils.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/_internal/permissions.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/_internal/vin.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/charging.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/energy.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/partner.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/sharing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/user.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/api/vehicle.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/auth/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/ble/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cache/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cache/keys.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/billing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/cache.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/charge.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/climate.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/media.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/partner.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/raw.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/cli/software.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/config/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/crypto/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/crypto/ecdh.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/crypto/keys.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/crypto/schnorr.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/deploy/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/models/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/models/command.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/models/config.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/models/energy.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/models/sharing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/models/user.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/models/vehicle.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/output/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/output/formatter.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/output/json_output.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/metadata.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/protobuf/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/protocol/protobuf/messages.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/py.typed +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/decoder.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/flatbuf.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_alert.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_alert_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_alert_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_connectivity.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_connectivity_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_connectivity_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_data.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_data_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_data_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_error.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_error_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_error_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_metric.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_metric_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/src/tescmd/telemetry/protos/vehicle_metric_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/_internal/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/_internal/test_async_utils.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/_internal/test_vin.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/test_client.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/test_command_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/test_energy_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/test_partner_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/test_sharing_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/test_user_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/api/test_vehicle_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/auth/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/auth/test_oauth.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/auth/test_server.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/auth/test_token_store.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/auth/test_token_store_fallback.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/auth/test_token_store_file.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cache/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cache/test_generic_cache.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cache/test_keys.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cache/test_response_cache.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/_helpers.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/conftest.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_auth_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_cache.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_cached_api_call.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_charge_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_cli_integration.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_climate_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_e2e_smoke.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_energy.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_energy_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_error_handlers.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_key.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_key_enroll.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_key_unenroll.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_main_errors.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_media.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_media_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_nav.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_partner.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_raw.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_raw_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_security_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_setup_scope_check.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_sharing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_sharing_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_software.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_software_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_status_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_tier_enforcement.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_user.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_user_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_vcsec_guard.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_vehicle_power_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/cli/test_verbose.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/conftest.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/crypto/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/crypto/test_ecdh.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/crypto/test_keys.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/crypto/test_schnorr.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/deploy/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/deploy/test_github_pages.py +0 -0
- {tescmd-0.2.0/tests/models → tescmd-0.4.0/tests/integration}/__init__.py +0 -0
- {tescmd-0.2.0/tests/output → tescmd-0.4.0/tests/mcp}/__init__.py +0 -0
- {tescmd-0.2.0/tests/protocol → tescmd-0.4.0/tests/models}/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/models/test_auth.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/models/test_config.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/models/test_energy.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/models/test_sharing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/models/test_user_models.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/models/test_vehicle.py +0 -0
- {tescmd-0.2.0/tests/telemetry → tescmd-0.4.0/tests/openclaw}/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/output/test_formatter.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/output/test_json_output.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/protocol/conftest.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/protocol/test_metadata.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/telemetry/conftest.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/telemetry/test_dashboard.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/telemetry/test_decoder.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/telemetry/test_flatbuf.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/telemetry/test_server.py +0 -0
- {tescmd-0.2.0 → tescmd-0.4.0}/tests/telemetry/test_tailscale.py +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# tescmd — Environment Variables
|
|
2
|
+
#
|
|
3
|
+
# Copy this file to .env and uncomment the variables you need.
|
|
4
|
+
# The setup wizard (tescmd setup) will create ~/.config/tescmd/.env
|
|
5
|
+
# for you automatically.
|
|
6
|
+
#
|
|
7
|
+
# Resolution order:
|
|
8
|
+
# 1. Shell environment variables (highest priority)
|
|
9
|
+
# 2. .env in the current working directory
|
|
10
|
+
# 3. ~/.config/tescmd/.env (created by tescmd setup)
|
|
11
|
+
|
|
12
|
+
# ─── Tesla Fleet API Credentials ─────────────────────────────────
|
|
13
|
+
# Required. Obtain from https://developer.tesla.com
|
|
14
|
+
# TESLA_CLIENT_ID=
|
|
15
|
+
# TESLA_CLIENT_SECRET=
|
|
16
|
+
|
|
17
|
+
# ─── Vehicle ──────────────────────────────────────────────────────
|
|
18
|
+
# Default vehicle VIN (avoids passing --vin on every command)
|
|
19
|
+
# TESLA_VIN=
|
|
20
|
+
|
|
21
|
+
# API region: na (North America), eu (Europe), cn (China)
|
|
22
|
+
# TESLA_REGION=na
|
|
23
|
+
|
|
24
|
+
# ─── Token Override / Storage ─────────────────────────────────────
|
|
25
|
+
# Direct token override (bypasses keyring / token file)
|
|
26
|
+
# TESLA_ACCESS_TOKEN=
|
|
27
|
+
# TESLA_REFRESH_TOKEN=
|
|
28
|
+
|
|
29
|
+
# File path for token storage (skips OS keyring)
|
|
30
|
+
# Useful for Docker, headless Linux, CI environments
|
|
31
|
+
# TESLA_TOKEN_FILE=~/.config/tescmd/tokens.json
|
|
32
|
+
|
|
33
|
+
# ─── Config & Cache ──────────────────────────────────────────────
|
|
34
|
+
# TESLA_CONFIG_DIR=~/.config/tescmd
|
|
35
|
+
# TESLA_CACHE_DIR=~/.cache/tescmd
|
|
36
|
+
# TESLA_CACHE_TTL=60
|
|
37
|
+
# TESLA_CACHE_ENABLED=true
|
|
38
|
+
|
|
39
|
+
# ─── Output Format & Display Units ───────────────────────────────
|
|
40
|
+
# Force output format: rich (TTY default), json (piped default), quiet
|
|
41
|
+
# TESLA_OUTPUT_FORMAT=
|
|
42
|
+
|
|
43
|
+
# Display unit preferences
|
|
44
|
+
# TESLA_TEMP_UNIT=F # F or C
|
|
45
|
+
# TESLA_DISTANCE_UNIT=mi # mi or km
|
|
46
|
+
# TESLA_PRESSURE_UNIT=psi # psi or bar
|
|
47
|
+
|
|
48
|
+
# ─── Command Protocol & Key Hosting ──────────────────────────────
|
|
49
|
+
# Command signing: auto (default), signed, unsigned
|
|
50
|
+
# TESLA_COMMAND_PROTOCOL=auto
|
|
51
|
+
|
|
52
|
+
# Domain for key hosting (set by tescmd setup)
|
|
53
|
+
# TESLA_DOMAIN=
|
|
54
|
+
|
|
55
|
+
# Key hosting method: github or tailscale
|
|
56
|
+
# TESLA_HOSTING_METHOD=
|
|
57
|
+
|
|
58
|
+
# GitHub repo for key deployment (e.g. username/username.github.io)
|
|
59
|
+
# TESLA_GITHUB_REPO=
|
|
60
|
+
|
|
61
|
+
# Setup tier: readonly or full
|
|
62
|
+
# TESLA_SETUP_TIER=
|
|
63
|
+
|
|
64
|
+
# Config profile name
|
|
65
|
+
# TESLA_PROFILE=default
|
|
66
|
+
|
|
67
|
+
# ─── MCP Server ──────────────────────────────────────────────────
|
|
68
|
+
# Credentials for MCP OAuth 2.1 authentication
|
|
69
|
+
# Required for tescmd serve and tescmd mcp serve
|
|
70
|
+
# TESCMD_MCP_CLIENT_ID=
|
|
71
|
+
# TESCMD_MCP_CLIENT_SECRET=
|
|
72
|
+
|
|
73
|
+
# ─── OpenClaw ────────────────────────────────────────────────────
|
|
74
|
+
# Gateway authentication token
|
|
75
|
+
# OPENCLAW_GATEWAY_TOKEN=
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.4.0] - 2026-02-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Unique app name generation** — setup wizard generates a `tescmd-<hex>` application name to prevent collisions on the Tesla Developer Portal; reused on re-runs unless `--force` is passed
|
|
13
|
+
- **In-process KeyServer** — ephemeral HTTP server (`KeyServer`) serves the PEM public key on localhost during interactive setup so Tailscale Funnel can proxy it without writing to the serve directory
|
|
14
|
+
- **Key mismatch warning** — setup wizard detects when the remote public key (GitHub Pages or Tailscale) differs from the local key and warns before Phase 2, so the user knows a redeploy is coming
|
|
15
|
+
- **`fetch_tailscale_key_pem()`** — synchronous helper in the deploy module to fetch the raw PEM from a Tailscale Funnel URL, mirroring `github_pages.fetch_key_pem()`
|
|
16
|
+
- **`TailscaleManager.start_proxy()`** — reverse-proxy mode (`tailscale serve --bg http://127.0.0.1:<port>`) for forwarding to a local HTTP server, distinct from the static-file `start_serve()`
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Setup phase reorder** — phases now run: keys → Fleet API registration → OAuth login → key enrollment (was: keys → enrollment → registration → OAuth); registration happens while credentials are fresh, enrollment is last so the user finishes in the Tesla app
|
|
21
|
+
- **Credentials always required** — both Client ID and Client Secret are mandatory with retry loops (3 attempts each); empty input no longer silently skips setup
|
|
22
|
+
- **Auto-save credentials** — `.env` file is written automatically after credential entry; removed the "Save to .env?" prompt
|
|
23
|
+
- **`--force` regenerates app name** — passing `--force` to setup now generates a fresh `tescmd-<hex>` name instead of reusing the saved one
|
|
24
|
+
- **Atomic Tailscale serve + Funnel** — `start_key_serving()` uses a single `tailscale serve --bg --funnel --set-path / <dir>` command instead of separate serve + funnel calls
|
|
25
|
+
- **`TailscaleManager.start_serve()` API** — added `port` and `funnel` keyword arguments for configurable HTTPS port and inline Funnel enablement
|
|
26
|
+
- **Enrollment messaging** — streamlined to focus on QR code scanning; removed duplicate URL display and the "Open in browser?" prompt (browser opens automatically)
|
|
27
|
+
- **GitHub Pages note** — clarified that Tailscale is used alongside GitHub Pages for telemetry streaming, not as a replacement
|
|
28
|
+
- **Funnel cleanup uses `stop_funnel()`** — finally block in `_interactive_setup` now calls the proper `TailscaleManager.stop_funnel()` method instead of the low-level `_run()` static method, preserving state tracking and idempotency
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **CLI module HTTP isolation** — moved direct `httpx.get()` call out of `setup.py` into `tailscale_serve.fetch_tailscale_key_pem()` to comply with single-responsibility layering (CLI handles args + output, deploy modules handle HTTP)
|
|
33
|
+
|
|
34
|
+
## [0.3.2] - 2026-02-02
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- **OAuth URL printed for manual fallback** — `login_flow()` now prints the authorization URL before opening the browser so users can copy-paste it when `webbrowser.open()` fails
|
|
39
|
+
- **422 "already registered" treated as success** — `register_partner_account()` now treats HTTP 422 with "already been taken" as idempotent success instead of raising `AuthError`; re-running setup or `auth register` shows "Already registered — no action needed"
|
|
40
|
+
- **GitHub key comparison on re-deploy** — `_deploy_key_github()` fetches the remote public key and compares it to the local key; if they match, deployment is skipped; if they differ, the user is prompted before overwriting
|
|
41
|
+
|
|
42
|
+
## [0.3.1] - 2026-02-02
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **README overhaul** — header banner, logo, new "What It Does" summary section, expanded Prerequisites table with Python 3.11+, pip, Tesla account, and helpful links
|
|
47
|
+
- **Tailscale Funnel auto-detection in auth setup** — `_interactive_setup` now detects Tailscale and offers to start Funnel so Tesla can verify the origin URL during Developer Portal configuration; cleans up Funnel on exit
|
|
48
|
+
- **Tailscale hostname passthrough** — setup wizard forwards detected Tailscale hostname to auth flow, showing a concrete "Also add" origin URL instead of the generic placeholder
|
|
49
|
+
- **Comprehensive agent skill documentation** — expanded skill covering all command groups, triggers, and OpenClaw dispatch
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **Cross-platform file permissions** — OpenClaw gateway key file now uses `secure_file()` from `_internal.permissions` instead of raw `chmod(0o600)`, adding proper Windows support via `icacls`
|
|
54
|
+
- **12-factor app compliance** — config, disposability, and concurrency improvements across the codebase
|
|
55
|
+
- **Documentation accuracy** — corrected `media_adjust_volume` tool name in MCP docs; emphasized MCP/OpenClaw over direct CLI for cost savings in agent skill
|
|
56
|
+
|
|
57
|
+
## [0.3.0] - 2026-02-01
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- **Unified `tescmd serve`** — single command combining MCP server, Fleet Telemetry streaming, cache warming, and optional OpenClaw bridge; full-screen TUI dashboard shows live telemetry, MCP status, tunnel URL, sink count, and connection health
|
|
62
|
+
- **TUI dashboard** — 8-panel Textual layout with telemetry field table, server info sidebar, activity log, request log, and filter status; command palette (`ctrl+p`), keybindings (`q` to quit, `f` to toggle filters), clean graceful shutdown
|
|
63
|
+
- **OpenClaw Bridge** — `tescmd openclaw bridge [VIN]` streams Fleet Telemetry to an OpenClaw Gateway with configurable delta+throttle filtering per field; supports `--dry-run` for JSONL output without a gateway connection
|
|
64
|
+
- **OpenClaw node role** — bidirectional command dispatch over the gateway WebSocket; bots send commands via the gateway that are forwarded as `node.invoke.request` events, routed through `CommandDispatcher` with read/write separation, tier enforcement, and VCSEC signing guards
|
|
65
|
+
- **Trigger subscription system** — `trigger.create`, `trigger.delete`, `trigger.list`, `trigger.poll` commands let bots register conditions on any telemetry field; supports operators `lt`, `gt`, `lte`, `gte`, `eq`, `neq`, `changed`, `enter`, `leave`; one-shot and persistent modes with configurable cooldown; max 100 triggers, 500 pending notifications
|
|
66
|
+
- **Geofence triggers** — `enter`/`leave` operators on Location field detect boundary crossings using haversine distance; fires only on actual crossing (not "already inside"), requires previous position for comparison
|
|
67
|
+
- **Trigger convenience aliases** — `cabin_temp.trigger`, `outside_temp.trigger`, `battery.trigger`, `location.trigger` pre-fill the field name so bots don't need to know raw telemetry field names
|
|
68
|
+
- **Trigger notification delivery** — dual-channel: OpenClaw push events (`trigger.fired`) for connected bots, and MCP polling via `trigger_poll` tool for agent frameworks
|
|
69
|
+
- **`system.run` meta-dispatch** — allows bots to invoke any registered handler by name with alias mapping (e.g., `door_lock` → `door.lock`, `auto_conditioning_start` → `climate.on`); guards against recursive self-dispatch
|
|
70
|
+
- **MCP Server** — `tescmd mcp serve` exposes all tescmd commands as MCP tools for Claude Desktop/Code and other agent frameworks; supports stdio and streamable-http transports with OAuth 2.1 authentication
|
|
71
|
+
- **MCP custom tool registry** — `MCPServer.register_custom_tool()` allows runtime registration of non-CLI tools (used by trigger system); custom tools appear alongside CLI-backed tools with proper schemas
|
|
72
|
+
- **Agent skill** — `skills/tescmd/SKILL.md` teaches AI agents how to use every tescmd command group with examples, parameter types, and common patterns
|
|
73
|
+
- **Reusable telemetry session** — extracted shared telemetry lifecycle (server → tunnel → partner registration → fleet config → cleanup) into `telemetry/setup.py` for use by serve, stream, and bridge commands
|
|
74
|
+
- **Dual-gate telemetry filter** — `openclaw/filters.py` combines delta threshold (value change) and throttle interval (minimum time between emissions) to reduce noise in telemetry streams; includes haversine distance for location fields
|
|
75
|
+
- **OpenClaw event emitter** — maps telemetry fields to OpenClaw `req:agent` event payloads (location, battery, temperature, speed, charge state transitions, security changes)
|
|
76
|
+
- **Gateway WebSocket client** — implements the OpenClaw operator protocol (challenge → connect → hello-ok handshake) with Ed25519 device key signing and exponential backoff reconnection
|
|
77
|
+
- **Bridge configuration** — `BridgeConfig` pydantic model with per-field filter settings, loadable from JSON file or CLI flags
|
|
78
|
+
- **CSV telemetry logging** — wide-format CSV log with one row per frame and one column per subscribed field; written to `~/.config/tescmd/logs/` by default, disable with `--no-log`
|
|
79
|
+
- **Cache sink** — telemetry-driven cache warming keeps read-command cache fresh while telemetry is active, making agent reads free
|
|
80
|
+
- **Frame fanout** — `FrameFanout` distributes decoded telemetry frames to multiple sinks (dashboard, CSV, cache, OpenClaw bridge, triggers) in parallel
|
|
81
|
+
- **Telemetry field mapper** — `telemetry/mapper.py` maps protobuf field names to tescmd model field names with unit conversion
|
|
82
|
+
- **Command guards in dispatcher** — extracted `check_command_guards()` (tier check + VCSEC signing requirement) into a shared function called by both CLI and OpenClaw dispatcher paths
|
|
83
|
+
- **Standard dependencies** — `websockets>=14.0`, `mcp>=1.0`, and `textual>=1.0` now included in default install
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- Refactored `_cmd_telemetry_stream` in `cli/vehicle.py` to use the shared `telemetry_session()` context manager (no behavioral change)
|
|
88
|
+
- Proto-aligned telemetry field definitions with proper `interval_seconds` for delta fields
|
|
89
|
+
- Separated JSON serialization errors from WebSocket connection errors in gateway send path for clearer diagnostics
|
|
90
|
+
- Raised trigger push notification and lifecycle event failures from debug to warning level for observability
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
|
|
94
|
+
- Fixed prompt for re-authentication when refresh token is expired or revoked (was failing silently)
|
|
95
|
+
- Fixed delta fields requiring `interval_seconds` configuration
|
|
96
|
+
- Fixed log file paths not shown on quit
|
|
97
|
+
- Added `exc_info` to MCP custom tool error logging for full stack traces
|
|
98
|
+
- Added JSON parse guards and explicit parameter validation in MCP tool wrappers
|
|
99
|
+
- Added `system.run` recursive self-dispatch guard
|
|
100
|
+
|
|
101
|
+
## [0.2.0] - 2026-01-31
|
|
102
|
+
|
|
103
|
+
### Added
|
|
104
|
+
|
|
105
|
+
- **Fleet Telemetry Streaming** — `tescmd vehicle telemetry stream [VIN]` starts a local WebSocket server, exposes it via Tailscale Funnel, configures the vehicle to push real-time telemetry, and displays it in an interactive Rich Live dashboard (TTY) or JSONL stream (piped)
|
|
106
|
+
- **Telemetry dashboard** — Rich Live TUI with field name, value, and last-update columns; unit conversion (°F/°C, mi/km, psi/bar); connection status; frame counter; uptime display
|
|
107
|
+
- **Protobuf telemetry decoder** — official Tesla protobuf definitions (`vehicle_data`, `vehicle_alert`, `vehicle_error`, `vehicle_metric`, `vehicle_connectivity`) for fully typed telemetry message parsing
|
|
108
|
+
- **FlatBuffer telemetry support** — `flatbuf.py` parser for Tesla's FlatBuffer-encoded telemetry payloads alongside protobuf
|
|
109
|
+
- **Field presets** — `--fields` option accepts preset names (`default`, `driving`, `charging`, `climate`, `all`) or comma-separated field names with 120+ registered telemetry fields
|
|
110
|
+
- **Interval override** — `--interval` option overrides the polling interval for all fields
|
|
111
|
+
- **Tailscale Funnel integration** — automatic Funnel start/stop with cert retrieval for Fleet Telemetry HTTPS requirement
|
|
112
|
+
- **JSONL output** — piped mode emits one JSON line per telemetry frame for scripting and log ingestion
|
|
113
|
+
- **TunnelError hierarchy** — `TunnelError` parent with `TailscaleError` subtype; actionable install/setup guidance
|
|
114
|
+
- **websockets dependency** — `websockets>=14.0` now included in default install
|
|
115
|
+
- **Tailscale key hosting** — `tescmd key deploy --method tailscale` hosts the public key via Tailscale Funnel at `https://<machine>.tailnet.ts.net/.well-known/appspecific/com.tesla.3p.public-key.pem`; auto-detected as second priority after GitHub Pages
|
|
116
|
+
- **Key hosting priority chain** — setup wizard and `key deploy` auto-detect the best hosting method: GitHub Pages → Tailscale Funnel → manual; `--method` flag overrides auto-detection
|
|
117
|
+
- **`TESLA_HOSTING_METHOD` setting** — persists the chosen key hosting method (`github`, `tailscale`) across sessions
|
|
118
|
+
- **Schnorr signature support** — `crypto/schnorr.py` for Schnorr-based authentication challenges used in telemetry server handshake
|
|
119
|
+
- **`auth import` command** — `tescmd auth import < tokens.json` imports tokens from a JSON file for headless/CI environments
|
|
120
|
+
- **Setup guide** — `docs/setup.md` with step-by-step walkthrough of all 7 setup phases
|
|
121
|
+
- **FAQ** — `docs/faq.md` covering common questions about tescmd, costs, hosting, and configuration
|
|
122
|
+
- **CI/CD workflows** — GitHub Actions for test-on-push (Python 3.11–3.13) and publish-to-PyPI-on-release via trusted publishing
|
|
123
|
+
- **README badges** — PyPI version, Python versions, CI build status, license, and GitHub release badges
|
|
124
|
+
- **E2E smoke tests** — `tests/cli/test_e2e_smoke.py` provides 179 pytest-based end-to-end tests covering every CLI command against the live Fleet API, with JSON envelope validation and save/restore for write commands (`pytest -m e2e`)
|
|
125
|
+
|
|
126
|
+
### Fixed
|
|
127
|
+
|
|
128
|
+
- Fixed telemetry dashboard uptime counter not incrementing
|
|
129
|
+
- Improved tunnel start/stop success messages for clarity
|
|
130
|
+
|
|
131
|
+
## [0.1.2] - 2025-01-31
|
|
132
|
+
|
|
133
|
+
### Added
|
|
134
|
+
|
|
135
|
+
- **Universal read-command caching** — every read command is now transparently cached with tiered TTLs (STATIC 1h, SLOW 5m, DEFAULT 1m, FAST 30s); bots can call tescmd as often as needed — within the TTL window, responses are instant and free
|
|
136
|
+
- **Generic cache key scheme** — `generic_cache_key(scope, identifier, endpoint, params)` generates scope-aware keys (`vin`, `site`, `account`, `partner`) for any API endpoint
|
|
137
|
+
- **`cached_api_call()` helper** — unified async helper that handles cache lookup, fetch, serialisation (Pydantic/dict/list/scalar), and storage for all non-vehicle-state read commands
|
|
138
|
+
- **Site-scoped cache invalidation** — `invalidate_cache_for_site()` clears energy site entries after write commands; `invalidate_cache_for_vin()` now also clears generic vin-scoped keys
|
|
139
|
+
- **`cache clear` options** — `--site SITE_ID` and `--scope {account,partner}` flags for targeted cache clearing alongside existing `--vin`
|
|
140
|
+
- **Partner endpoints** — `partner public-key`, `partner telemetry-error-vins`, `partner telemetry-errors` for partner account data (require client credentials)
|
|
141
|
+
- **Billing endpoints** — `billing history`, `billing sessions`, `billing invoice` for Supercharger charging data
|
|
142
|
+
- **Cross-platform file permissions** — `_internal/permissions.py` provides `secure_file()` using `chmod 0600` on Unix and `icacls` on Windows
|
|
143
|
+
- **Token store file backend** — `_FileBackend` with atomic writes and restricted permissions as fallback when keyring is unavailable
|
|
144
|
+
- **Spec-driven Fleet API validation** — `scripts/validate_fleet_api.py` validates implementation against `spec/fleet_api_spec.json` using AST introspection
|
|
145
|
+
- **6 missing Fleet API commands** — added `managed_charging_set_amps`, `managed_charging_set_location`, `managed_charging_set_schedule`, `add_charge_schedule`, `remove_charge_schedule`, `clear_charge_schedules`
|
|
146
|
+
- **Configurable display units** — `--units metric` flag switches all display values to °C/km/bar; individual env vars (`TESLA_TEMP_UNIT`, `TESLA_DISTANCE_UNIT`, `TESLA_PRESSURE_UNIT`) for granular control
|
|
147
|
+
|
|
148
|
+
### Fixed
|
|
149
|
+
|
|
150
|
+
- Aligned schedule/departure command parameters with Tesla Go SDK (correct param names and types)
|
|
151
|
+
- Fixed energy endpoint paths to match Fleet API spec
|
|
152
|
+
- Fixed Rich markup escaping bug in command output
|
|
153
|
+
- Aligned command parameters (3 param gaps) with Go SDK specs
|
|
154
|
+
|
|
155
|
+
### Changed
|
|
156
|
+
|
|
157
|
+
- Response cache documentation in CLAUDE.md expanded to cover universal caching, TTL tiers, and generic cache key scheme
|
|
158
|
+
|
|
159
|
+
## [0.1.1]
|
|
160
|
+
|
|
161
|
+
### Added
|
|
162
|
+
|
|
163
|
+
- **`status` command** — `tescmd status` shows current configuration, auth, cache, and key status at a glance
|
|
164
|
+
- **Retry option in wake prompt** — when a vehicle is asleep, the interactive prompt now offers `[R] Retry` alongside `[W] Wake via API` and `[C] Cancel`, allowing users to wake the vehicle for free via the Tesla app and retry without restarting the command
|
|
165
|
+
- **Key enrollment** — `tescmd key enroll <VIN>` sends the public key to the vehicle and guides the user through Tesla app approval with interactive [C]heck/[R]esend/[Q]uit prompt, `--wait` auto-polling, and JSON mode support
|
|
166
|
+
- **Tier enforcement** — readonly tier now blocks write commands with a clear error and upgrade guidance (`tescmd setup`)
|
|
167
|
+
- **Vehicle Command Protocol** — ECDH session management, HMAC-SHA256 command signing, and protobuf RoutableMessage encoding for the `signed_command` endpoint; commands are automatically signed when keys are available (`command_protocol=auto`)
|
|
168
|
+
- **SignedCommandAPI** — composition wrapper that transparently routes signed commands through the Vehicle Command Protocol while falling back to unsigned REST for `wake_up` and unknown commands
|
|
169
|
+
- **`command_protocol` setting** — `auto` (default), `signed`, or `unsigned` to control command routing; configurable via `TESLA_COMMAND_PROTOCOL` env var
|
|
170
|
+
- **Enrollment step in setup wizard** — full-tier setup now offers to enroll the key on a vehicle after key generation
|
|
171
|
+
- **Friendly command output** — all vehicle commands now display descriptive success messages (e.g. "Climate control turned on.", "Doors locked.") instead of bare "OK"
|
|
172
|
+
- **E2E smoke tests** — `tests/cli/test_e2e_smoke.py` provides 179 pytest-based end-to-end tests covering every CLI command against the live Fleet API, with JSON envelope validation and save/restore for write commands (`pytest -m e2e`)
|
|
173
|
+
|
|
174
|
+
## [0.1.0]
|
|
175
|
+
|
|
176
|
+
### Added
|
|
177
|
+
|
|
178
|
+
- OAuth2 PKCE authentication with browser-based login flow
|
|
179
|
+
- Vehicle state queries: battery, charge, climate, drive, location, doors, windows, trunks, tire pressure
|
|
180
|
+
- Vehicle commands: charge start/stop/limit/schedule, climate on/off/set/seats/wheel, lock/unlock, sentry, trunk/frunk, windows, media, navigation, software updates, HomeLink, speed limits, PIN management
|
|
181
|
+
- Energy products: Powerwall live status, site info, backup reserve, operation mode, storm mode, TOU settings, charging history, calendar history, grid config
|
|
182
|
+
- User account: profile info, region, orders, feature config
|
|
183
|
+
- Vehicle sharing: add/remove drivers, create/redeem/revoke invites
|
|
184
|
+
- Rich terminal output with tables, panels, and status indicators
|
|
185
|
+
- JSON output mode for scripting and agent integration
|
|
186
|
+
- Configurable display units (F/C, mi/km, PSI/bar)
|
|
187
|
+
- Response caching with configurable TTL for API cost reduction
|
|
188
|
+
- Cost-aware wake confirmation (interactive prompt or `--wake` flag)
|
|
189
|
+
- Multi-profile configuration support
|
|
190
|
+
- EC key generation and Tesla Developer Portal registration
|
|
191
|
+
- Raw API access (`raw get`, `raw post`) for uncovered endpoints
|
|
192
|
+
- First-run setup wizard with Fleet Telemetry cost guidance
|
tescmd-0.4.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# CLAUDE.md — Project Context for Claude Code
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**tescmd** is a Python 3.11+ CLI for querying and controlling Tesla vehicles via the [Tesla Fleet API](https://developer.tesla.com/docs/fleet-api). It covers auth, vehicle queries/commands, energy products, Supercharger billing, Fleet Telemetry streaming, OpenClaw bridge, and an MCP server for agent integration.
|
|
6
|
+
|
|
7
|
+
## Tech Stack
|
|
8
|
+
|
|
9
|
+
- **Python 3.11+**, **pydantic v2**, **click**, **httpx** (async), **rich**, **cryptography**, **protobuf**, **keyring**, **python-dotenv**
|
|
10
|
+
- **websockets** (telemetry + OpenClaw), **mcp** (MCP server) — core dependencies
|
|
11
|
+
- Optional: **bleak** (`[ble]` extra — BLE key enrollment)
|
|
12
|
+
|
|
13
|
+
## Project Structure
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
src/tescmd/
|
|
17
|
+
├── cli/ # Click CLI layer
|
|
18
|
+
│ ├── main.py # Root group, AppContext, _register_commands()
|
|
19
|
+
│ ├── _options.py # Shared Click options/decorators (@global_options)
|
|
20
|
+
│ ├── _client.py # API client builders, auto_wake, cached_vehicle_data, cached_api_call, TTL tiers
|
|
21
|
+
│ ├── auth.py, cache.py, charge.py, billing.py, climate.py, security.py
|
|
22
|
+
│ ├── status.py, trunk.py, vehicle.py, media.py, nav.py, partner.py
|
|
23
|
+
│ ├── software.py, energy.py, user.py, sharing.py, raw.py, key.py
|
|
24
|
+
│ ├── setup.py # Interactive first-run wizard
|
|
25
|
+
│ ├── serve.py # Unified MCP + telemetry + OpenClaw command
|
|
26
|
+
│ ├── openclaw.py # Standalone openclaw bridge command
|
|
27
|
+
│ └── mcp_cmd.py # mcp serve command
|
|
28
|
+
├── api/ # HTTP client + domain APIs (composition pattern)
|
|
29
|
+
│ ├── client.py # TeslaFleetClient (base HTTP, auth headers, retries)
|
|
30
|
+
│ ├── vehicle.py, command.py, signed_command.py, energy.py
|
|
31
|
+
│ ├── charging.py, partner.py, sharing.py, user.py
|
|
32
|
+
│ └── errors.py # AuthError, VehicleAsleepError, TierError, TunnelError, etc.
|
|
33
|
+
├── models/ # Pydantic v2 models (vehicle, energy, user, auth, command, config)
|
|
34
|
+
├── auth/ # OAuth2 PKCE, token_store (keyring + file fallback), callback server
|
|
35
|
+
├── protocol/ # Vehicle Command Protocol (ECDH sessions, HMAC signing, protobuf)
|
|
36
|
+
├── crypto/ # EC key gen, ECDH, Schnorr signatures
|
|
37
|
+
├── cache/ # File-based JSON cache with tiered TTLs
|
|
38
|
+
├── output/ # OutputFormatter, RichOutput (DisplayUnits), JsonOutput
|
|
39
|
+
├── telemetry/ # Fleet Telemetry streaming
|
|
40
|
+
│ ├── setup.py # Reusable telemetry_session() context manager
|
|
41
|
+
│ ├── server.py, decoder.py, fields.py, dashboard.py, tailscale.py
|
|
42
|
+
├── openclaw/ # OpenClaw bridge
|
|
43
|
+
│ ├── config.py # BridgeConfig, FieldFilter, NodeCapabilities (pydantic)
|
|
44
|
+
│ ├── filters.py # DualGateFilter (delta + throttle), haversine()
|
|
45
|
+
│ ├── emitter.py # EventEmitter (telemetry → OpenClaw events)
|
|
46
|
+
│ ├── gateway.py # GatewayClient (WebSocket, node protocol, Ed25519 auth)
|
|
47
|
+
│ ├── bridge.py # TelemetryBridge orchestrator, build_openclaw_pipeline()
|
|
48
|
+
│ ├── dispatcher.py # CommandDispatcher (reads, writes, triggers, system.run)
|
|
49
|
+
│ └── telemetry_store.py # In-memory latest-value cache for telemetry fields
|
|
50
|
+
├── triggers/ # Trigger subscription system
|
|
51
|
+
│ ├── models.py # TriggerOperator, TriggerCondition, TriggerDefinition, TriggerNotification
|
|
52
|
+
│ └── manager.py # TriggerManager (evaluation, cooldown, delivery, geofencing)
|
|
53
|
+
├── mcp/ # MCP server
|
|
54
|
+
│ └── server.py # MCPServer (FastMCP, CliRunner + custom callable tools)
|
|
55
|
+
├── deploy/ # Key hosting (GitHub Pages, Tailscale Funnel)
|
|
56
|
+
└── _internal/ # vin.py, async_utils.py, permissions.py
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Coding Conventions
|
|
60
|
+
|
|
61
|
+
- **Type hints everywhere** — all function signatures, all variables where non-obvious
|
|
62
|
+
- **async/await** — all API calls are async; CLI entry points use `run_async()` helper
|
|
63
|
+
- **Pydantic models** — all API request/response payloads; all configuration
|
|
64
|
+
- **src layout** — code in `src/tescmd/`, tests in `tests/`
|
|
65
|
+
- **No star imports** — explicit imports only
|
|
66
|
+
- **Single responsibility** — CLI modules handle args + output, API modules handle HTTP
|
|
67
|
+
- **Composition over inheritance** — API classes wrap `TeslaFleetClient`, don't extend it
|
|
68
|
+
- **Error stream routing** — JSON/piped mode writes errors to stderr; Rich/TTY mode uses stdout
|
|
69
|
+
|
|
70
|
+
## Key Patterns
|
|
71
|
+
|
|
72
|
+
**CLI command registration:** New command groups are registered in `cli/main.py:_register_commands()`. Each CLI module defines a Click group and is imported there.
|
|
73
|
+
|
|
74
|
+
**Global options propagation:** Use `@global_options` decorator from `cli/_options.py` on commands that need VIN, format, wake, cache flags. Options flow through `AppContext` via `@click.pass_obj`.
|
|
75
|
+
|
|
76
|
+
**API client construction:** `cli/_client.py` provides `get_vehicle_api(app_ctx)` → returns `(client, api)` tuple. Also `get_command_api()` for write commands (handles signed vs unsigned routing).
|
|
77
|
+
|
|
78
|
+
**Cache:** All read commands use `cached_api_call()` with scope-aware TTLs (STATIC 1h, SLOW 5m, DEFAULT 1m, FAST 30s). Write commands invalidate via `invalidate_cache_for_vin()` / `invalidate_cache_for_site()`.
|
|
79
|
+
|
|
80
|
+
**Output:** `OutputFormatter` auto-detects TTY → Rich, piped → JSON, `--quiet` → stderr only. JSON output uses a consistent `{ok, command, data, error, timestamp}` envelope.
|
|
81
|
+
|
|
82
|
+
**Telemetry session lifecycle:** `telemetry/setup.py:telemetry_session()` is an async context manager handling: server start → Tailscale tunnel → partner domain re-registration → fleet config → yield → cleanup. Used by both `cli/vehicle.py` (stream) and `cli/openclaw.py` (bridge).
|
|
83
|
+
|
|
84
|
+
**OpenClaw pipeline:** `TelemetryServer.on_frame` → `DualGateFilter.should_emit()` → `EventEmitter.to_event()` → `GatewayClient.send_event()`. The `TelemetryBridge` class wires these together. `build_openclaw_pipeline()` is the shared factory used by both `cli/openclaw.py` (standalone) and `cli/serve.py` (combined mode). Inbound commands flow: gateway `node.invoke.request` → `CommandDispatcher.dispatch()` → handler → `node.invoke.result`.
|
|
85
|
+
|
|
86
|
+
**Trigger system:** `TriggerManager` evaluates conditions on every telemetry frame (independent of the dual-gate filter). Supports numeric comparison (`lt`, `gt`, `eq`, etc.), `changed` detection, and geofence `enter`/`leave` with haversine distance. One-shot and persistent (with cooldown) firing modes. Notifications delivered via OpenClaw push and MCP polling (`trigger.poll`).
|
|
87
|
+
|
|
88
|
+
**MCP tools:** `mcp/server.py` maps tool names to CLI arg lists via `_CliToolDef` dataclasses. `invoke_tool()` uses `CliRunner.invoke(cli, ["--format", "json", "--wake", *args], env=os.environ.copy())`. Custom tools (e.g. trigger CRUD) use `_CustomToolDef` with direct callable handlers registered via `register_custom_tool()`. Read/write tools are separated for `readOnlyHint` annotations. HTTP transport uses OAuth 2.1 via `_InMemoryOAuthProvider` (auto-approve authorization, dynamic client registration, in-memory token storage) with `_PermissiveClient` wrappers that accept any redirect URI. DNS rebinding protection is configured to allow the Tailscale Funnel hostname when `public_url` is set.
|
|
89
|
+
|
|
90
|
+
## Build & Test
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Build
|
|
94
|
+
python -m build # hatchling via pyproject.toml
|
|
95
|
+
|
|
96
|
+
# Test (parallel by default via pytest-xdist)
|
|
97
|
+
pytest # all ~1600 tests
|
|
98
|
+
pytest tests/openclaw/ -x -v # openclaw tests
|
|
99
|
+
pytest tests/triggers/ -x -v # trigger tests
|
|
100
|
+
pytest tests/mcp/ -x -v # mcp tests
|
|
101
|
+
pytest -m e2e # live API smoke tests (needs TESLA_ACCESS_TOKEN)
|
|
102
|
+
|
|
103
|
+
# Lint
|
|
104
|
+
ruff check src/ tests/
|
|
105
|
+
ruff format src/ tests/
|
|
106
|
+
mypy src/
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Environment Variables
|
|
110
|
+
|
|
111
|
+
| Variable | Description |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `TESLA_CLIENT_ID` / `TESLA_CLIENT_SECRET` | OAuth2 app credentials |
|
|
114
|
+
| `TESLA_VIN` | Default vehicle VIN |
|
|
115
|
+
| `TESLA_REGION` | API region: `na`, `eu`, `cn` |
|
|
116
|
+
| `TESLA_ACCESS_TOKEN` / `TESLA_REFRESH_TOKEN` | Direct token override |
|
|
117
|
+
| `TESLA_TOKEN_FILE` | File path for token storage (skips keyring) |
|
|
118
|
+
| `TESLA_CONFIG_DIR` | Config directory (default: `~/.config/tescmd`) |
|
|
119
|
+
| `TESLA_CACHE_DIR` / `TESLA_CACHE_TTL` / `TESLA_CACHE_ENABLED` | Cache settings |
|
|
120
|
+
| `TESLA_OUTPUT_FORMAT` | Force format: `rich`, `json`, `quiet` |
|
|
121
|
+
| `TESLA_COMMAND_PROTOCOL` | `auto` (default), `signed`, `unsigned` |
|
|
122
|
+
| `TESLA_TEMP_UNIT` / `TESLA_DISTANCE_UNIT` / `TESLA_PRESSURE_UNIT` | Display units |
|
|
123
|
+
| `TESLA_DOMAIN` / `TESLA_HOSTING_METHOD` / `TESLA_GITHUB_REPO` | Key hosting |
|
|
124
|
+
| `TESLA_SETUP_TIER` | `readonly` or `full` |
|
|
125
|
+
| `TESLA_PROFILE` | Active config profile |
|
|
126
|
+
| `OPENCLAW_GATEWAY_TOKEN` | OpenClaw gateway auth token |
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
## Additional Resources
|
|
130
|
+
|
|
131
|
+
Tesla's published vehicle-command proto omits some VehicleAction fields (e.g. navigation commands). The Teslemetry project maintains a more complete proto: https://github.com/Teslemetry/python-tesla-fleet-api/blob/main/proto/car_server.proto
|