diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 70f4b22..164178d 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -46,11 +46,11 @@ jobs:
python -m pip install .[docs]
- name: Build Sphinx site
run: |
- sphinx-build -b html docs/ docs/_build/html
+ sphinx-build -b html docs/sphinx docs/sphinx/_build/html
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
- path: docs/_build/html
+ path: docs/sphinx/_build/html
deploy:
environment:
diff --git a/.gitignore b/.gitignore
index 97b2d3a..94ff718 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,7 +73,7 @@ instance/
.scrapy
# Sphinx documentation
-docs/_build/
+docs/*/_build/
# PyBuilder
.pybuilder/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 916b746..500b97d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -82,26 +82,30 @@ uv pip install .[docs]
Using the Makefile generated by Sphinx:
```bash
-make -C docs html
+make -C docs/sphinx html
```
Or directly with sphinx-build:
```bash
-sphinx-build -b html docs/ docs/_build/html
+sphinx-build -b html docs/sphinx docs/sphinx/_build/html
```
Open the result in your browser:
```bash
# Linux
-xdg-open docs/_build/html/index.html
+xdg-open docs/sphinx/_build/html/index.html
+
+# Or serve locally
+make -C docs/sphinx serve
+# Opens at http://localhost:8000/
```
Clean build artifacts:
```bash
-make -C docs clean
+make -C docs/sphinx clean
```
### Publishing (CI)
diff --git a/REUSE.toml b/REUSE.toml
index 83eaf08..bce00f8 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -45,3 +45,32 @@ path = "**/*.ico"
SPDX-FileCopyrightText = "2025 InOrbit, Inc."
SPDX-License-Identifier = "MIT"
SPDX-FileComment = "ICO assets"
+
+# Documentation content files (portable between Sphinx and Mintlify)
+# These use YAML frontmatter instead of HTML comments for Mintlify compatibility
+[[annotations]]
+path = "docs/contents/**/*.md"
+SPDX-FileCopyrightText = "2025 InOrbit, Inc."
+SPDX-License-Identifier = "MIT"
+SPDX-FileComment = "Documentation content (Mintlify-compatible)"
+
+# Documentation JSON config
+[[annotations]]
+path = "docs/contents/docs.json"
+SPDX-FileCopyrightText = "2025 InOrbit, Inc."
+SPDX-License-Identifier = "MIT"
+SPDX-FileComment = "Mintlify navigation configuration"
+
+# Mintlify build Makefile (has SPDX header but adding for completeness)
+[[annotations]]
+path = "docs/mintlify/Makefile"
+SPDX-FileCopyrightText = "2025 InOrbit, Inc."
+SPDX-License-Identifier = "MIT"
+SPDX-FileComment = "Mintlify build configuration"
+
+# Sphinx wrapper files (use {include} directives, minimal content)
+[[annotations]]
+path = "docs/sphinx/**/*.md"
+SPDX-FileCopyrightText = "2025 InOrbit, Inc."
+SPDX-License-Identifier = "MIT"
+SPDX-FileComment = "Sphinx documentation wrappers"
diff --git a/docs/_static/favicon.ico b/docs/contents/_static/favicon.ico
similarity index 100%
rename from docs/_static/favicon.ico
rename to docs/contents/_static/favicon.ico
diff --git a/docs/_static/inorbit-logo-black.svg b/docs/contents/_static/inorbit-logo-black.svg
similarity index 100%
rename from docs/_static/inorbit-logo-black.svg
rename to docs/contents/_static/inorbit-logo-black.svg
diff --git a/docs/_static/inorbit-logo-white.svg b/docs/contents/_static/inorbit-logo-white.svg
similarity index 100%
rename from docs/_static/inorbit-logo-white.svg
rename to docs/contents/_static/inorbit-logo-white.svg
diff --git a/docs/_static/mark-github.svg b/docs/contents/_static/mark-github.svg
similarity index 100%
rename from docs/_static/mark-github.svg
rename to docs/contents/_static/mark-github.svg
diff --git a/docs/configuration.md b/docs/contents/configuration.md
similarity index 95%
rename from docs/configuration.md
rename to docs/contents/configuration.md
index 7d08933..2f689cf 100644
--- a/docs/configuration.md
+++ b/docs/contents/configuration.md
@@ -1,10 +1,7 @@
-
-
-# Configuration
+---
+title: "Configuration"
+description: "Configuration models and file formats for connectors"
+---
The `inorbit-connector` framework uses Pydantic models for configuration, providing validation and type safety.
@@ -12,7 +9,7 @@ The `inorbit-connector` framework uses Pydantic models for configuration, provid
The main configuration class is `ConnectorConfig`, which contains all settings for your connector. It includes a `fleet` field containing a list of `RobotConfig` entries.
-Connectors should subclass `inorbit_connector.models.ConnectorConfig` and define a `connector_config` field that contains the configuration for the connector. For more details see the {ref}`creating-a-custom-configuration` section below.
+Connectors should subclass `inorbit_connector.models.ConnectorConfig` and define a `connector_config` field that contains the configuration for the connector. For more details see the [Creating a Custom Configuration](#creating-a-custom-configuration) section below.
### Key Fields
diff --git a/docs/contents/docs.json b/docs/contents/docs.json
new file mode 100644
index 0000000..d89530f
--- /dev/null
+++ b/docs/contents/docs.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://mintlify.com/docs.json",
+ "navigation": {
+ "products": [
+ {
+ "product": "Ground Control",
+ "tabs": [
+ {
+ "tab": "Connecting Devices",
+ "pages": [
+ {
+ "group": "Connector Framework",
+ "pages": [
+ "ground-control/robot-integration/connector-framework/index",
+ "ground-control/robot-integration/connector-framework/getting-started",
+ {
+ "group": "Usage",
+ "pages": [
+ "ground-control/robot-integration/connector-framework/usage/index",
+ "ground-control/robot-integration/connector-framework/usage/single-robot",
+ "ground-control/robot-integration/connector-framework/usage/fleet",
+ "ground-control/robot-integration/connector-framework/usage/commands-handling"
+ ]
+ },
+ "ground-control/robot-integration/connector-framework/configuration",
+ "ground-control/robot-integration/connector-framework/publishing",
+ {
+ "group": "Specification",
+ "pages": [
+ "ground-control/robot-integration/connector-framework/specification/index",
+ "ground-control/robot-integration/connector-framework/specification/connector",
+ "ground-control/robot-integration/connector-framework/specification/models",
+ "ground-control/robot-integration/connector-framework/specification/commands",
+ "ground-control/robot-integration/connector-framework/specification/utils",
+ "ground-control/robot-integration/connector-framework/specification/logging"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
+
+
diff --git a/docs/getting-started.md b/docs/contents/getting-started.md
similarity index 95%
rename from docs/getting-started.md
rename to docs/contents/getting-started.md
index 021d7b0..90b35c4 100644
--- a/docs/getting-started.md
+++ b/docs/contents/getting-started.md
@@ -1,10 +1,7 @@
-
-
-# Getting Started
+---
+title: "Getting Started"
+description: "Installation and setup for the InOrbit Connector Framework"
+---
## Installation
diff --git a/docs/index.md b/docs/contents/index.md
similarity index 76%
rename from docs/index.md
rename to docs/contents/index.md
index 40b291b..4ed5672 100644
--- a/docs/index.md
+++ b/docs/contents/index.md
@@ -1,10 +1,7 @@
-
-
-# inorbit-connector
+---
+title: "Connector Framework"
+description: "Python framework for developing InOrbit robot connectors"
+---
A Python framework for developing *connectors* for the [InOrbit](https://inorbit.ai/) RobOps ecosystem.
@@ -31,21 +28,10 @@ Both connector types provide:
- Python 3.10 or later
- InOrbit account ([it's free to sign up!](https://control.inorbit.ai))
-```{toctree}
-:maxdepth: 2
-:hidden:
-
-getting-started
-specification/index
-usage/index
-configuration
-publishing
-```
-
## Documentation Sections
-- **Getting Started**: Installation, environment setup, and running a connector
-- **Specification**: Package-level specification of the connector API (what you can call and what you can override)
-- **Usage**: Detailed guides for implementing single-robot and fleet connectors
-- **Configuration**: Complete connector customization guide
-- **Publishing**: How to publish data to InOrbit
+- **Getting Started**: Installation, environment setup, and running a connector ([Getting Started](getting-started))
+- **Specification**: Package-level specification of the connector API ([Specification](specification/index))
+- **Usage**: Detailed guides for implementing single-robot and fleet connectors ([Usage](usage/index))
+- **Configuration**: Connector configuration models and file formats ([Configuration](configuration))
+- **Publishing**: How to publish data to InOrbit ([Publishing](publishing))
diff --git a/docs/publishing.md b/docs/contents/publishing.md
similarity index 97%
rename from docs/publishing.md
rename to docs/contents/publishing.md
index 39960fb..09189aa 100644
--- a/docs/publishing.md
+++ b/docs/contents/publishing.md
@@ -1,10 +1,7 @@
-
-
-# Publishing Data
+---
+title: "Publishing Data"
+description: "How to publish robot data to InOrbit"
+---
The connector framework provides methods to publish various types of data to InOrbit. Both single-robot (`Connector`) and fleet (`FleetConnector`) connectors support publishing, with fleet connectors using methods prefixed with `publish_robot_*` that require a `robot_id` parameter.
diff --git a/docs/specification/commands.md b/docs/contents/specification/commands.md
similarity index 91%
rename from docs/specification/commands.md
rename to docs/contents/specification/commands.md
index 728df21..148d395 100644
--- a/docs/specification/commands.md
+++ b/docs/contents/specification/commands.md
@@ -1,10 +1,7 @@
-
-
-# Commands utilities
+---
+title: "Commands Utilities"
+description: "Command handling utilities specification"
+---
This page specifies command handling helpers from `inorbit_connector.commands`.
@@ -46,7 +43,7 @@ These classes support type-safe parsing and validation of structured command par
- `CommandModel` is a Pydantic `BaseModel` configured with `extra="forbid"`, and converts `ValidationError` into `CommandFailure`.
- `ExcludeUnsetMixin` changes `model_dump()` default behavior to `exclude_unset=True` (useful when you want to emit only explicitly-provided fields).
-See {doc}`../usage/commands-handling` for end-to-end usage patterns.
+See [Commands Handling](../usage/commands-handling) for end-to-end usage patterns.
## Note: re-exports via `inorbit_connector.connector`
diff --git a/docs/specification/connector.md b/docs/contents/specification/connector.md
similarity index 80%
rename from docs/specification/connector.md
rename to docs/contents/specification/connector.md
index 2285541..813dafa 100644
--- a/docs/specification/connector.md
+++ b/docs/contents/specification/connector.md
@@ -1,10 +1,7 @@
-
-
-# Connector API
+---
+title: "Connector API"
+description: "Connector and FleetConnector class specifications"
+---
This page specifies the connector base classes you subclass to build connectors.
@@ -12,7 +9,7 @@ This page specifies the connector base classes you subclass to build connectors.
`inorbit_connector.connector.FleetConnector` is the base class for connectors that manage multiple robots.
-(spec-connector-fleetconnector-connect)=
+
### `_connect()`
**Override.**
@@ -25,7 +22,7 @@ Typical responsibilities:
- Optionally fetch the fleet membership and call `update_fleet()` **before** sessions are created.
- Start background polling tasks that keep fresh state for all robots.
-(spec-connector-fleetconnector-execution-loop)=
+
### `_execution_loop()`
**Override.**
@@ -37,14 +34,14 @@ Typical responsibilities:
- For each robot in `self.robot_ids`, fetch the latest state (often from background polling state) and publish data via the `publish_robot_*` methods.
- Handle exceptions inside the loop when possible (the framework logs and continues on exceptions).
-(spec-connector-fleetconnector-disconnect)=
+
### `_disconnect()`
**Override.**
Called once during shutdown, after Edge SDK sessions are disconnected. Use this to stop polling tasks, close sockets, and release resources.
-(spec-connector-fleetconnector-command-handler)=
+
### `_inorbit_robot_command_handler(robot_id, command_name, args, options)`
**Override.**
@@ -55,9 +52,9 @@ Contract:
- `options["result_function"](...)` must be called to report success/failure, or you may raise `CommandFailure` for structured failure reporting.
-See {doc}`../usage/commands-handling` for the full command result contract.
+See [Commands Handling](../usage/commands-handling) for the full command result contract.
-(spec-connector-fleetconnector-fetch-robot-map)=
+
### `fetch_robot_map(robot_id, frame_id) -> MapConfigTemp | None`
**Optional override.**
@@ -70,14 +67,14 @@ If `publish_robot_pose()` references a `frame_id` that is not present in `config
Return `None` if the map can’t be fetched.
-(spec-connector-fleetconnector-is-online)=
+
### `_is_fleet_robot_online(robot_id) -> bool`
**Optional override.**
The Edge SDK uses this callback to determine if a robot should be considered online. Default implementation returns `True`.
-(spec-connector-fleetconnector-lifecycle)=
+
### `start()` / `join()` / `stop()`
**Callable.**
@@ -86,7 +83,7 @@ The Edge SDK uses this callback to determine if a robot should be considered onl
- `join()` blocks until the background thread exits.
- `stop()` signals the event loop to stop and waits briefly for shutdown.
-(spec-connector-fleetconnector-fleet-management)=
+
### `robot_ids` / `update_fleet(fleet)`
**Callable.**
@@ -96,14 +93,14 @@ The Edge SDK uses this callback to determine if a robot should be considered onl
This is designed to be used during `_connect()` when your fleet membership comes from an external system.
-(spec-connector-fleetconnector-publish-robot-pose)=
+
### `publish_robot_pose(robot_id, x, y, yaw, frame_id=None, **kwargs)`
**Callable.**
Publishes pose for one robot. If the `frame_id` differs from the last published `frame_id` for that robot, the framework triggers `publish_robot_map(..., is_update=True)`.
-(spec-connector-fleetconnector-publish-robot-map)=
+
### `publish_robot_map(robot_id, frame_id, is_update=False)`
**Callable.**
@@ -111,22 +108,22 @@ Publishes pose for one robot. If the `frame_id` differs from the last published
- If `frame_id` exists in `config.maps`, publishes the configured map to InOrbit.
- Otherwise schedules an async fetch via `fetch_robot_map()` (see above).
-(spec-connector-fleetconnector-publish-robot-odometry)=
+
### `publish_robot_odometry(robot_id, **kwargs)`
**Callable.** Publishes odometry data for one robot.
-(spec-connector-fleetconnector-publish-robot-key-values)=
+
### `publish_robot_key_values(robot_id, **kwargs)`
**Callable.** Publishes key-value telemetry for one robot.
-(spec-connector-fleetconnector-publish-robot-system-stats)=
+
### `publish_robot_system_stats(robot_id, **kwargs)`
**Callable.** Publishes system stats for one robot.
-(spec-connector-fleetconnector-get-robot-session)=
+
### `_get_robot_session(robot_id) -> RobotSession`
**Callable (advanced).**
@@ -137,7 +134,7 @@ Returns the underlying Edge SDK session for `robot_id`. Use this if you need Edg
`inorbit_connector.connector.Connector` is a single-robot specialization of `FleetConnector`. It selects a single robot out of the fleet config and provides convenience wrappers that omit `robot_id`.
-(spec-connector-connector-lifecycle-hooks)=
+
### Lifecycle hooks
**Override.** Same intent as fleet:
@@ -146,28 +143,28 @@ Returns the underlying Edge SDK session for `robot_id`. Use this if you need Edg
- `_execution_loop()`
- `_disconnect()`
-(spec-connector-connector-command-handler)=
+
### `_inorbit_command_handler(command_name, args, options)`
**Override.**
Single-robot command handler. It is called through the fleet-level handler internally, but without requiring you to accept `robot_id`.
-(spec-connector-connector-fetch-map)=
+
### `fetch_map(frame_id) -> MapConfigTemp | None`
**Optional override.**
Single-robot convenience for map fetching. The framework uses it by delegating `fetch_robot_map()` to `fetch_map()`.
-(spec-connector-connector-is-online)=
+
### `_is_robot_online() -> bool`
**Optional override.**
Single-robot convenience for online status. The fleet-level online check delegates to this method.
-(spec-connector-connector-publishing)=
+
### Publishing wrappers
**Callable.** Single-robot wrappers over the fleet publishing methods:
@@ -177,7 +174,7 @@ Single-robot convenience for online status. The fleet-level online check delegat
- `publish_key_values(...)`
- `publish_system_stats(...)`
-(spec-connector-connector-get-session)=
+
### `_get_session() -> RobotSession`
**Callable (advanced).**
diff --git a/docs/specification/index.md b/docs/contents/specification/index.md
similarity index 54%
rename from docs/specification/index.md
rename to docs/contents/specification/index.md
index abcd99e..6bd9449 100644
--- a/docs/specification/index.md
+++ b/docs/contents/specification/index.md
@@ -1,10 +1,7 @@
-
-
-# Specification
+---
+title: "Specification"
+description: "Public API specification for the inorbit-connector package"
+---
This section specifies the public surface of the `inorbit-connector` package: what you are expected to **override** when implementing a connector, and what you can **call** at runtime.
@@ -38,11 +35,11 @@ For map handling, `publish_pose()` / `publish_robot_pose()` automatically trigge
For narrative guides, see:
-- Single robot: {doc}`../usage/single-robot`
-- Fleet: {doc}`../usage/fleet`
-- Commands: {doc}`../usage/commands-handling`
-- Publishing: {doc}`../publishing`
-- Configuration: {doc}`../configuration`
+- Single robot: [Single-Robot Connector](../usage/single-robot)
+- Fleet: [Fleet Connector](../usage/fleet)
+- Commands: [Commands Handling](../usage/commands-handling)
+- Publishing: [Publishing Data](../publishing)
+- Configuration: [Configuration](../configuration)
## API surface (callable + overridable)
@@ -50,47 +47,42 @@ The table below lists package-defined symbols meant for direct use (call) or ext
| Kind | Symbol | Purpose | Details |
| --- | --- | --- | --- |
-| Override | `FleetConnector._connect()` | Connect to external services (fleet manager, robot backends) before sessions initialize. | {ref}`Details ` |
-| Override | `FleetConnector._execution_loop()` | Periodic loop; publish telemetry for each robot. | {ref}`Details ` |
-| Override | `FleetConnector._disconnect()` | Shutdown external services and release resources. | {ref}`Details ` |
-| Override | `FleetConnector._inorbit_robot_command_handler()` | Handle commands for a specific `robot_id`. | {ref}`Details ` |
-| Override (optional) | `FleetConnector.fetch_robot_map()` | Fetch a missing map (bytes + metadata) when publishing pose refers to an unknown `frame_id`. | {ref}`Details ` |
-| Override (optional) | `FleetConnector._is_fleet_robot_online()` | Provide robot online status; used by Edge SDK callback. | {ref}`Details ` |
-| Call | `FleetConnector.start()` / `join()` / `stop()` | Run and control the connector lifecycle. | {ref}`Details ` |
-| Call | `FleetConnector.update_fleet()` / `FleetConnector.robot_ids` | Update fleet configuration (typically during `_connect()`) and access robot IDs. | {ref}`Details ` |
-| Call | `FleetConnector.publish_robot_pose()` | Publish pose; triggers map publish when `frame_id` changes. | {ref}`Details ` |
-| Call | `FleetConnector.publish_robot_map()` | Publish map metadata/image from configured maps (or after fetch). | {ref}`Details ` |
-| Call | `FleetConnector.publish_robot_odometry()` | Publish odometry. | {ref}`Details ` |
-| Call | `FleetConnector.publish_robot_key_values()` | Publish key-value telemetry. | {ref}`Details ` |
-| Call | `FleetConnector.publish_robot_system_stats()` | Publish system stats telemetry. | {ref}`Details ` |
-| Call (advanced) | `FleetConnector._get_robot_session()` | Access the underlying Edge SDK `RobotSession` for a specific robot. | {ref}`Details ` |
-| Override | `Connector._connect()` / `_execution_loop()` / `_disconnect()` | Same lifecycle hooks as fleet, for a single robot. | {ref}`Details ` |
-| Override | `Connector._inorbit_command_handler()` | Handle commands for the single robot. | {ref}`Details ` |
-| Override (optional) | `Connector.fetch_map()` | Fetch a missing map for the current robot when pose references an unknown `frame_id`. | {ref}`Details ` |
-| Override (optional) | `Connector._is_robot_online()` | Provide online status for the current robot. | {ref}`Details ` |
-| Call | `Connector.publish_pose()` / `publish_map()` | Publish pose/map for the current robot (map handling included). | {ref}`Details ` |
-| Call | `Connector.publish_odometry()` / `publish_key_values()` / `publish_system_stats()` | Publish telemetry for the current robot. | {ref}`Details ` |
-| Call (advanced) | `Connector._get_session()` | Access the underlying Edge SDK `RobotSession` for the current robot. | {ref}`Details ` |
-| Type | `ConnectorConfig` | Base configuration model for connectors. | {ref}`Details ` |
-| Type | `RobotConfig` | Per-robot configuration (robot_id + cameras). | {ref}`Details ` |
-| Type | `MapConfig` / `MapConfigTemp` | Map configuration (file-backed vs in-memory bytes) used by map publishing/fetching. | {ref}`Details ` |
-| Type | `LoggingConfig` / `LogLevels` | Logging configuration and log-level enum. | {ref}`Details ` |
-| Call | `read_yaml()` | Load YAML configuration data (with deprecated `robot_id` selection support). | {ref}`Details ` |
-| Type / Call | `CommandResultCode` / `CommandFailure` | Standard command result code enum and structured failure exception. | {ref}`Details ` |
-| Call | `parse_custom_command_args()` | Parse custom-command args (RunScript action payload) into `(script_name, params)`. | {ref}`Details ` |
-| Type | `CommandModel` / `ExcludeUnsetMixin` | Pydantic-based command argument validation utilities. | {ref}`Details ` |
-| Call | `setup_logger()` | Configure logging from `LoggingConfig`. | {ref}`Details ` |
-| Type | `ConditionalColoredFormatter` | Optional `colorlog`-backed formatter used by the default logging config. | {ref}`Details ` |
-
-```{toctree}
-:maxdepth: 2
-:hidden:
-
-connector
-models
-commands
-utils
-logging
-```
-
-
+| Override | `FleetConnector._connect()` | Connect to external services (fleet manager, robot backends) before sessions initialize. | [Details](connector.md#spec-connector-fleetconnector-connect) |
+| Override | `FleetConnector._execution_loop()` | Periodic loop; publish telemetry for each robot. | [Details](connector.md#spec-connector-fleetconnector-execution-loop) |
+| Override | `FleetConnector._disconnect()` | Shutdown external services and release resources. | [Details](connector.md#spec-connector-fleetconnector-disconnect) |
+| Override | `FleetConnector._inorbit_robot_command_handler()` | Handle commands for a specific `robot_id`. | [Details](connector.md#spec-connector-fleetconnector-command-handler) |
+| Override (optional) | `FleetConnector.fetch_robot_map()` | Fetch a missing map (bytes + metadata) when publishing pose refers to an unknown `frame_id`. | [Details](connector.md#spec-connector-fleetconnector-fetch-robot-map) |
+| Override (optional) | `FleetConnector._is_fleet_robot_online()` | Provide robot online status; used by Edge SDK callback. | [Details](connector.md#spec-connector-fleetconnector-is-online) |
+| Call | `FleetConnector.start()` / `join()` / `stop()` | Run and control the connector lifecycle. | [Details](connector.md#spec-connector-fleetconnector-lifecycle) |
+| Call | `FleetConnector.update_fleet()` / `FleetConnector.robot_ids` | Update fleet configuration (typically during `_connect()`) and access robot IDs. | [Details](connector.md#spec-connector-fleetconnector-fleet-management) |
+| Call | `FleetConnector.publish_robot_pose()` | Publish pose; triggers map publish when `frame_id` changes. | [Details](connector.md#spec-connector-fleetconnector-publish-robot-pose) |
+| Call | `FleetConnector.publish_robot_map()` | Publish map metadata/image from configured maps (or after fetch). | [Details](connector.md#spec-connector-fleetconnector-publish-robot-map) |
+| Call | `FleetConnector.publish_robot_odometry()` | Publish odometry. | [Details](connector.md#spec-connector-fleetconnector-publish-robot-odometry) |
+| Call | `FleetConnector.publish_robot_key_values()` | Publish key-value telemetry. | [Details](connector.md#spec-connector-fleetconnector-publish-robot-key-values) |
+| Call | `FleetConnector.publish_robot_system_stats()` | Publish system stats telemetry. | [Details](connector.md#spec-connector-fleetconnector-publish-robot-system-stats) |
+| Call (advanced) | `FleetConnector._get_robot_session()` | Access the underlying Edge SDK `RobotSession` for a specific robot. | [Details](connector.md#spec-connector-fleetconnector-get-robot-session) |
+| Override | `Connector._connect()` / `_execution_loop()` / `_disconnect()` | Same lifecycle hooks as fleet, for a single robot. | [Details](connector.md#spec-connector-connector-lifecycle-hooks) |
+| Override | `Connector._inorbit_command_handler()` | Handle commands for the single robot. | [Details](connector.md#spec-connector-connector-command-handler) |
+| Override (optional) | `Connector.fetch_map()` | Fetch a missing map for the current robot when pose references an unknown `frame_id`. | [Details](connector.md#spec-connector-connector-fetch-map) |
+| Override (optional) | `Connector._is_robot_online()` | Provide online status for the current robot. | [Details](connector.md#spec-connector-connector-is-online) |
+| Call | `Connector.publish_pose()` / `publish_map()` | Publish pose/map for the current robot (map handling included). | [Details](connector.md#spec-connector-connector-publishing) |
+| Call | `Connector.publish_odometry()` / `publish_key_values()` / `publish_system_stats()` | Publish telemetry for the current robot. | [Details](connector.md#spec-connector-connector-publishing) |
+| Call (advanced) | `Connector._get_session()` | Access the underlying Edge SDK `RobotSession` for the current robot. | [Details](connector.md#spec-connector-connector-get-session) |
+| Type | `ConnectorConfig` | Base configuration model for connectors. | [Details](models.md#spec-models-connectorconfig) |
+| Type | `RobotConfig` | Per-robot configuration (robot_id + cameras). | [Details](models.md#spec-models-robotconfig) |
+| Type | `MapConfig` / `MapConfigTemp` | Map configuration (file-backed vs in-memory bytes) used by map publishing/fetching. | [Details](models.md#spec-models-mapconfig) |
+| Type | `LoggingConfig` / `LogLevels` | Logging configuration and log-level enum. | [Details](logging.md#spec-logging-loggingconfig) |
+| Call | `read_yaml()` | Load YAML configuration data (with deprecated `robot_id` selection support). | [Details](utils.md#spec-utils-readyaml) |
+| Type / Call | `CommandResultCode` / `CommandFailure` | Standard command result code enum and structured failure exception. | [Details](commands.md#spec-commands-commandfailure) |
+| Call | `parse_custom_command_args()` | Parse custom-command args (RunScript action payload) into `(script_name, params)`. | [Details](commands.md#spec-commands-parse-custom-command-args) |
+| Type | `CommandModel` / `ExcludeUnsetMixin` | Pydantic-based command argument validation utilities. | [Details](commands.md#spec-commands-commandmodel) |
+| Call | `setup_logger()` | Configure logging from `LoggingConfig`. | [Details](logging.md#spec-logging-setup-logger) |
+| Type | `ConditionalColoredFormatter` | Optional `colorlog`-backed formatter used by the default logging config. | [Details](logging.md#spec-logging-conditional-colored-formatter) |
+
+## Pages
+
+- [Connector API](connector)
+- [Models (configuration)](models)
+- [Commands utilities](commands)
+- [Utilities](utils)
+- [Logging](logging)
diff --git a/docs/specification/logging.md b/docs/contents/specification/logging.md
similarity index 92%
rename from docs/specification/logging.md
rename to docs/contents/specification/logging.md
index a075645..b6d3ae4 100644
--- a/docs/specification/logging.md
+++ b/docs/contents/specification/logging.md
@@ -1,10 +1,7 @@
-
-
-# Logging
+---
+title: "Logging"
+description: "Logging configuration specification"
+---
This page specifies logging-related helpers from `inorbit_connector.logging`.
diff --git a/docs/specification/models.md b/docs/contents/specification/models.md
similarity index 92%
rename from docs/specification/models.md
rename to docs/contents/specification/models.md
index 3e9878d..3ded9e5 100644
--- a/docs/specification/models.md
+++ b/docs/contents/specification/models.md
@@ -1,10 +1,7 @@
-
-
-# Models (configuration)
+---
+title: "Models"
+description: "Configuration models specification"
+--- (configuration)
This page specifies the configuration models defined by `inorbit_connector.models`.
@@ -58,7 +55,7 @@ Logging configuration used by the connector at startup:
- `log_level`: optional override for the root logger level.
- `defaults`: dictionary passed to the logging config (e.g. `log_file`).
-See {ref}`spec-logging-setup-logger` for how it is applied.
+See [setup_logger()](logging.md#spec-logging-setup-logger) for how it is applied.
## Deprecated: `InorbitConnectorConfig`
diff --git a/docs/specification/utils.md b/docs/contents/specification/utils.md
similarity index 85%
rename from docs/specification/utils.md
rename to docs/contents/specification/utils.md
index 1d556e6..f9947b7 100644
--- a/docs/specification/utils.md
+++ b/docs/contents/specification/utils.md
@@ -1,10 +1,7 @@
-
-
-# Utilities
+---
+title: "Utilities"
+description: "Utility functions specification"
+---
This page specifies utilities from `inorbit_connector.utils`.
@@ -22,7 +19,7 @@ Behavior:
Notes:
-- New configurations should follow the `ConnectorConfig` schema described in {doc}`../configuration`.
+- New configurations should follow the `ConnectorConfig` schema described in [Configuration](../configuration).
## Constants
diff --git a/docs/usage/commands-handling.md b/docs/contents/usage/commands-handling.md
similarity index 97%
rename from docs/usage/commands-handling.md
rename to docs/contents/usage/commands-handling.md
index 5a19131..3cffa52 100644
--- a/docs/usage/commands-handling.md
+++ b/docs/contents/usage/commands-handling.md
@@ -1,10 +1,7 @@
-
-
-# Commands Handling
+---
+title: "Commands Handling"
+description: "How to handle commands from InOrbit"
+---
Commands from InOrbit are automatically routed to your `_inorbit_robot_command_handler()` method in the case of a fleet connector, or to your `_inorbit_command_handler()` method in the case of a single-robot connector.
@@ -145,7 +142,7 @@ async def _inorbit_robot_command_handler(
)
```
-It is recommended to complement its use with the `CommandModel` class (see {ref}`using-commandmodel-for-type-safe-argument-parsing`) for safe type validation and parsing.
+It is recommended to complement its use with the `CommandModel` class (see [Using CommandModel for Type-Safe Argument Parsing](#using-commandmodel-for-type-safe-argument-parsing)) for safe type validation and parsing.
(using-commandmodel-for-type-safe-argument-parsing)=
## Using `CommandModel` for Type-Safe Argument Parsing
@@ -228,7 +225,7 @@ command = CommandQueueMission(**script_args)
```
The exception is automatically handled by the connector's command execution framework, so you don't need to catch `ValidationError` exceptions.
-See the {ref}`reporting-failure` section for more details.
+See the [Reporting Failure](#reporting-failure) section for more details.
### Excluding Unset Fields
diff --git a/docs/usage/fleet.md b/docs/contents/usage/fleet.md
similarity index 98%
rename from docs/usage/fleet.md
rename to docs/contents/usage/fleet.md
index 04075e7..a9f933e 100644
--- a/docs/usage/fleet.md
+++ b/docs/contents/usage/fleet.md
@@ -1,10 +1,7 @@
-
-
-# Fleet Connector
+---
+title: "Fleet Connector"
+description: "Guide for implementing a fleet connector"
+---
Subclass `inorbit_connector.connector.FleetConnector` to manage multiple robots simultaneously.
diff --git a/docs/contents/usage/index.md b/docs/contents/usage/index.md
new file mode 100644
index 0000000..da9d89c
--- /dev/null
+++ b/docs/contents/usage/index.md
@@ -0,0 +1,10 @@
+---
+title: "Usage"
+description: "Guides for implementing connectors"
+---
+
+## Guides
+
+- [Single-Robot Connector](single-robot)
+- [Fleet Connector](fleet)
+- [Commands Handling](commands-handling)
diff --git a/docs/usage/single-robot.md b/docs/contents/usage/single-robot.md
similarity index 98%
rename from docs/usage/single-robot.md
rename to docs/contents/usage/single-robot.md
index feac2e2..bcc6a7c 100644
--- a/docs/usage/single-robot.md
+++ b/docs/contents/usage/single-robot.md
@@ -1,10 +1,7 @@
-
-
-# Single-Robot Connector
+---
+title: "Single-Robot Connector"
+description: "Guide for implementing a single-robot connector"
+---
Subclass `inorbit_connector.connector.Connector` to create a connector for a single robot.
diff --git a/docs/mintlify/Makefile b/docs/mintlify/Makefile
new file mode 100644
index 0000000..d9207be
--- /dev/null
+++ b/docs/mintlify/Makefile
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2025 InOrbit, Inc.
+# SPDX-License-Identifier: MIT
+
+# Makefile for building Mintlify-compatible docs from shared content
+
+CONTENT_DIR = ../contents
+BUILD_DIR = _build
+
+.PHONY: build clean
+
+# Build Mintlify-compatible docs with absolute links
+build:
+ @echo "Building Mintlify-compatible docs..."
+ @python build.py "$(CONTENT_DIR)" "$(BUILD_DIR)"
+ @echo "Done. Output in $(BUILD_DIR)/"
+
+# Clean build artifacts
+clean:
+ @rm -rf "$(BUILD_DIR)"
+ @echo "Cleaned $(BUILD_DIR)/"
+
diff --git a/docs/mintlify/build.py b/docs/mintlify/build.py
new file mode 100644
index 0000000..c778f7f
--- /dev/null
+++ b/docs/mintlify/build.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+# SPDX-FileCopyrightText: 2025 InOrbit, Inc.
+# SPDX-License-Identifier: MIT
+"""Build Mintlify-compatible docs from the shared content files.
+
+This script:
+1. Copies content files to the build directory
+2. Converts relative links to absolute Mintlify routes
+3. Prepares the output for the multirepo action
+"""
+
+import re
+import shutil
+import sys
+from pathlib import Path
+
+# Mintlify route prefix for connector framework docs
+PREFIX = "/ground-control/robot-integration/connector-framework"
+
+# Pattern to match markdown links: [text](path) or [text](path#anchor)
+# Excludes: absolute URLs (http://, https://), absolute paths (/...), and pure anchors (#...)
+LINK_PATTERN = re.compile(r"\[([^\]]+)\]\((?!https?://|mailto:|/|#)([^)]+)\)")
+
+
+def convert_link(match: re.Match, file_path: Path, content_root: Path) -> str:
+ """Convert a relative link to an absolute Mintlify route."""
+ text = match.group(1)
+ target = match.group(2)
+
+ # Get the directory of the current file relative to content root
+ file_dir = file_path.parent.relative_to(content_root)
+
+ # Resolve the relative path
+ if target.startswith("../"):
+ # Go up directories
+ resolved = (file_dir / target).as_posix()
+ # Normalize path (remove ../)
+ parts = []
+ for part in resolved.split("/"):
+ if part == "..":
+ if parts:
+ parts.pop()
+ elif part and part != ".":
+ parts.append(part)
+ resolved = "/".join(parts)
+ else:
+ # Same directory or subdirectory
+ if str(file_dir) == ".":
+ resolved = target
+ else:
+ resolved = f"{file_dir}/{target}".replace("./", "")
+
+ # Strip .md extension for Mintlify routes
+ if resolved.endswith(".md"):
+ resolved = resolved[:-3]
+ # Also handle .md before anchors (e.g., "file.md#anchor" -> "file#anchor")
+ resolved = re.sub(r"\.md(#)", r"\1", resolved)
+
+ # Build absolute Mintlify route
+ absolute_path = f"{PREFIX}/{resolved}"
+
+ return f"[{text}]({absolute_path})"
+
+
+def process_file(src_file: Path, dst_file: Path, content_root: Path) -> int:
+ """Process a single file, converting links. Returns count of conversions."""
+ content = src_file.read_text(encoding="utf-8")
+ count = 0
+
+ def replace_link(match: re.Match) -> str:
+ nonlocal count
+ count += 1
+ return convert_link(match, src_file, content_root)
+
+ new_content = LINK_PATTERN.sub(replace_link, content)
+
+ dst_file.parent.mkdir(parents=True, exist_ok=True)
+ dst_file.write_text(new_content, encoding="utf-8")
+
+ return count
+
+
+def main():
+ if len(sys.argv) < 3:
+ print("Usage: build.py ", file=sys.stderr)
+ sys.exit(1)
+
+ content_dir = Path(sys.argv[1])
+ output_dir = Path(sys.argv[2])
+
+ if not content_dir.is_dir():
+ print(f"Error: {content_dir} is not a directory", file=sys.stderr)
+ sys.exit(1)
+
+ # Clean output directory
+ if output_dir.exists():
+ shutil.rmtree(output_dir)
+ output_dir.mkdir(parents=True)
+
+ total_links = 0
+ total_files = 0
+
+ # Process all markdown files
+ for src_file in content_dir.rglob("*.md"):
+ rel_path = src_file.relative_to(content_dir)
+ dst_file = output_dir / rel_path
+
+ links = process_file(src_file, dst_file, content_dir)
+ if links > 0:
+ print(f" {rel_path}: {links} links converted")
+ total_links += links
+ total_files += 1
+
+ # Copy non-markdown files (images, etc.)
+ for src_file in content_dir.rglob("*"):
+ if src_file.is_file() and src_file.suffix != ".md":
+ rel_path = src_file.relative_to(content_dir)
+ dst_file = output_dir / rel_path
+ dst_file.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copy2(src_file, dst_file)
+
+ print(f"\nTotal: {total_files} files, {total_links} links converted")
+ print(f"Output: {output_dir}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs/Makefile b/docs/sphinx/Makefile
similarity index 80%
rename from docs/Makefile
rename to docs/sphinx/Makefile
index e409574..a02f320 100644
--- a/docs/Makefile
+++ b/docs/sphinx/Makefile
@@ -16,7 +16,12 @@ BUILDDIR = _build
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-.PHONY: help Makefile
+.PHONY: help Makefile serve
+
+# Serve locally for testing
+serve: html
+ @echo "Serving at http://localhost:8000/"
+ @cd "$(BUILDDIR)/html" && python -m http.server 8000
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
diff --git a/docs/conf.py b/docs/sphinx/conf.py
similarity index 59%
rename from docs/conf.py
rename to docs/sphinx/conf.py
index 7bacee7..f3e5394 100644
--- a/docs/conf.py
+++ b/docs/sphinx/conf.py
@@ -7,23 +7,31 @@
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
-# -- Project information -----------------------------------------------------
-# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
-import os
+import re
from pathlib import Path
-import sys
-from inorbit_connector import __version__
-# Ensure the package can be imported so we can read its version
-sys.path.insert(0, os.path.abspath(".."))
+def _read_package_version() -> str:
+ """Read __version__ from the package without importing it.
+
+ This keeps the docs build independent from the environment (no need to have the
+ package importable).
+ """
+ repo_root = Path(__file__).resolve().parents[2]
+ init_py = repo_root / "inorbit_connector" / "__init__.py"
+ text = init_py.read_text(encoding="utf-8")
+ match = re.search(r'^__version__\s*=\s*"([^"]+)"\s*$', text, re.MULTILINE)
+ if not match:
+ raise RuntimeError("Could not determine package version from __init__.py")
+ return match.group(1)
+
project = "inorbit-connector"
copyright = "2025, InOrbit, Inc."
author = "InOrbit, Inc."
-release = __version__
-version = __version__
+release = _read_package_version()
+version = release
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -39,11 +47,18 @@
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+root_doc = "index"
+
+# The portable docs use explicit HTML anchors (e.g. ) for deep
+# linking compatibility across doc systems. MyST can't validate those anchors,
+# and will emit "xref_missing" warnings for links that include a fragment.
+suppress_warnings = ["myst.xref_missing"]
+
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = "furo"
-html_static_path = ["_static"]
+html_static_path = ["../contents/_static"]
# Support both Markdown (MyST) and reStructuredText sources
source_suffix = {
@@ -53,7 +68,7 @@
def _read_static_svg(filename: str) -> str:
- static_dir = Path(__file__).resolve().parent / "_static"
+ static_dir = Path(__file__).resolve().parents[1] / "contents" / "_static"
return (static_dir / filename).read_text(encoding="utf-8")
@@ -63,7 +78,7 @@ def _read_static_svg(filename: str) -> str:
"dark_logo": "inorbit-logo-white.svg",
"source_repository": "https://github.com/inorbit-ai/inorbit-connector-python/",
"source_branch": "main",
- "source_directory": "docs/",
+ "source_directory": "docs/contents/",
"top_of_page_buttons": ["view", "edit"],
"footer_icons": [
{
@@ -76,4 +91,4 @@ def _read_static_svg(filename: str) -> str:
}
# Favicon
-html_favicon = "_static/favicon.ico"
+html_favicon = "../contents/_static/favicon.ico"
diff --git a/docs/sphinx/configuration.md b/docs/sphinx/configuration.md
new file mode 100644
index 0000000..34502bd
--- /dev/null
+++ b/docs/sphinx/configuration.md
@@ -0,0 +1,5 @@
+# Configuration
+
+```{include} ../contents/configuration.md
+:start-line: 5
+```
diff --git a/docs/sphinx/getting-started.md b/docs/sphinx/getting-started.md
new file mode 100644
index 0000000..e204688
--- /dev/null
+++ b/docs/sphinx/getting-started.md
@@ -0,0 +1,6 @@
+# Getting Started
+
+```{include} ../contents/getting-started.md
+:start-line: 5
+:end-before: "## Next Steps"
+```
diff --git a/docs/sphinx/index.md b/docs/sphinx/index.md
new file mode 100644
index 0000000..265e3a8
--- /dev/null
+++ b/docs/sphinx/index.md
@@ -0,0 +1,23 @@
+
+
+# Connector Framework
+
+```{include} ../contents/index.md
+:start-line: 5
+:end-before: "## Documentation Sections"
+```
+
+```{toctree}
+:maxdepth: 2
+:hidden:
+
+getting-started
+specification/index
+usage/index
+configuration
+publishing
+```
diff --git a/docs/sphinx/publishing.md b/docs/sphinx/publishing.md
new file mode 100644
index 0000000..d49c4c9
--- /dev/null
+++ b/docs/sphinx/publishing.md
@@ -0,0 +1,5 @@
+# Publishing Data
+
+```{include} ../contents/publishing.md
+:start-line: 5
+```
diff --git a/docs/sphinx/specification/commands.md b/docs/sphinx/specification/commands.md
new file mode 100644
index 0000000..18f4b21
--- /dev/null
+++ b/docs/sphinx/specification/commands.md
@@ -0,0 +1,5 @@
+# Commands Utilities
+
+```{include} ../../contents/specification/commands.md
+:start-line: 5
+```
diff --git a/docs/sphinx/specification/connector.md b/docs/sphinx/specification/connector.md
new file mode 100644
index 0000000..73a92b3
--- /dev/null
+++ b/docs/sphinx/specification/connector.md
@@ -0,0 +1,5 @@
+# Connector API
+
+```{include} ../../contents/specification/connector.md
+:start-line: 5
+```
diff --git a/docs/sphinx/specification/index.md b/docs/sphinx/specification/index.md
new file mode 100644
index 0000000..104b09e
--- /dev/null
+++ b/docs/sphinx/specification/index.md
@@ -0,0 +1,17 @@
+# Specification
+
+```{include} ../../contents/specification/index.md
+:start-line: 5
+:end-before: "## Pages"
+```
+
+```{toctree}
+:maxdepth: 2
+:hidden:
+
+connector
+models
+commands
+utils
+logging
+```
diff --git a/docs/sphinx/specification/logging.md b/docs/sphinx/specification/logging.md
new file mode 100644
index 0000000..b4372a3
--- /dev/null
+++ b/docs/sphinx/specification/logging.md
@@ -0,0 +1,5 @@
+# Logging
+
+```{include} ../../contents/specification/logging.md
+:start-line: 5
+```
diff --git a/docs/sphinx/specification/models.md b/docs/sphinx/specification/models.md
new file mode 100644
index 0000000..fef5fca
--- /dev/null
+++ b/docs/sphinx/specification/models.md
@@ -0,0 +1,5 @@
+# Models
+
+```{include} ../../contents/specification/models.md
+:start-line: 5
+```
diff --git a/docs/sphinx/specification/utils.md b/docs/sphinx/specification/utils.md
new file mode 100644
index 0000000..2dde3af
--- /dev/null
+++ b/docs/sphinx/specification/utils.md
@@ -0,0 +1,5 @@
+# Utilities
+
+```{include} ../../contents/specification/utils.md
+:start-line: 5
+```
diff --git a/docs/sphinx/usage/commands-handling.md b/docs/sphinx/usage/commands-handling.md
new file mode 100644
index 0000000..f2add3c
--- /dev/null
+++ b/docs/sphinx/usage/commands-handling.md
@@ -0,0 +1,5 @@
+# Commands Handling
+
+```{include} ../../contents/usage/commands-handling.md
+:start-line: 5
+```
diff --git a/docs/sphinx/usage/fleet.md b/docs/sphinx/usage/fleet.md
new file mode 100644
index 0000000..5e45656
--- /dev/null
+++ b/docs/sphinx/usage/fleet.md
@@ -0,0 +1,5 @@
+# Fleet Connector
+
+```{include} ../../contents/usage/fleet.md
+:start-line: 5
+```
diff --git a/docs/usage/index.md b/docs/sphinx/usage/index.md
similarity index 59%
rename from docs/usage/index.md
rename to docs/sphinx/usage/index.md
index 2b2a6fc..7110e09 100644
--- a/docs/usage/index.md
+++ b/docs/sphinx/usage/index.md
@@ -1,9 +1,3 @@
-
-
# Usage
Guides for implementing connectors.
@@ -16,4 +10,3 @@ single-robot
fleet
commands-handling
```
-
diff --git a/docs/sphinx/usage/single-robot.md b/docs/sphinx/usage/single-robot.md
new file mode 100644
index 0000000..6a683cd
--- /dev/null
+++ b/docs/sphinx/usage/single-robot.md
@@ -0,0 +1,5 @@
+# Single-Robot Connector
+
+```{include} ../../contents/usage/single-robot.md
+:start-line: 5
+```