reachy-cli 0.2.0__tar.gz → 0.3.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.
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/CHANGELOG.md +14 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/CLAUDE.md +22 -4
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/PKG-INFO +48 -8
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/README.md +45 -7
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/pyproject.toml +13 -5
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/__init__.py +3 -1
- reachy_cli-0.3.0/reachy/cli/_commands/daemon.py +165 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/learn.py +4 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/overview.py +1 -0
- reachy_cli-0.3.0/reachy/daemon.py +362 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/explain/catalog.py +58 -3
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/robot/http_transport.py +2 -1
- reachy_cli-0.3.0/tests/test_daemon.py +420 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/tests/test_robot.py +1 -1
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/uv.lock +9 -3
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/agent-config/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/agent-config/scripts/show.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/cicd/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/communicate/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/think/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/think/scripts/think.sh +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/version-bump/SKILL.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.claude/skills.local.yaml.example +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.flake8 +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.github/workflows/publish.yml +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.github/workflows/tests.yml +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.gitignore +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/.markdownlint-cli2.yaml +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/LICENSE +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/culture.yaml +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/docs/adr-0001-sdk-transport-extra.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/docs/skill-sources.md +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/__init__.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/__main__.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/__init__.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/_robot.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/app.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/cli.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/device.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/doctor.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/explain.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/move.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_commands/whoami.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_errors.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/cli/_output.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/explain/__init__.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/robot/__init__.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/robot/sdk_transport.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/reachy/robot/transport.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/sonar-project.properties +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/tests/__init__.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/tests/test_cli.py +0 -0
- {reachy_cli-0.2.0 → reachy_cli-0.3.0}/tests/test_cli_introspection.py +0 -0
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/). This project
|
|
6
6
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.0] - 2026-05-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `daemon` noun group (`start`/`stop`/`status`/`overview`): bring the local `reachy-mini-daemon` process up and down — background spawn + PID/log under `$XDG_STATE_HOME/reachy`, health-poll on `GET /api/daemon/status`, idempotent start, SIGTERM-then-SIGKILL stop.
|
|
13
|
+
- `reachy/daemon.py` — stdlib-only daemon process-lifecycle module (no new runtime dependency).
|
|
14
|
+
- `[daemon]` optional-dependencies extra (`reachy-mini>=1.0`) — the recommended default install, providing the `reachy-mini-daemon` binary.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Inverted the install model: `pip install 'reachy-cli[daemon]'` is now the default (bundles the daemon); the bare `pip install reachy-cli` is the HTTP-only *remote* profile. Base stays zero-runtime-deps.
|
|
19
|
+
- The `http` transport's daemon-unreachable hint now points at `reachy daemon start` and the `[daemon]` install.
|
|
20
|
+
- README + CLAUDE.md document the daemon noun, the install profiles, and the daemon-up wake-up flow.
|
|
21
|
+
|
|
8
22
|
## [0.2.0] - 2026-05-30
|
|
9
23
|
|
|
10
24
|
### Added
|
|
@@ -40,7 +40,9 @@ test assertions in one pass.
|
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
uv sync # create .venv, install (dev deps incl. teken)
|
|
43
|
+
uv sync --extra daemon # + reachy-mini (the reachy-mini-daemon binary)
|
|
43
44
|
uv run reachy whoami # run the CLI (NOT `reachy-mini-cli`)
|
|
45
|
+
uv run reachy daemon start # bring the local daemon up (needs [daemon] extra)
|
|
44
46
|
uv run pytest -n auto # full suite (parallel)
|
|
45
47
|
uv run pytest tests/test_cli.py::test_whoami_text # a single test
|
|
46
48
|
uv run pytest --cov=reachy --cov-report=term # with coverage (CI gate: fail_under=60)
|
|
@@ -93,13 +95,29 @@ you touch the CLI.
|
|
|
93
95
|
line scanner (no YAML library) and walks up from `__file__` to find it.
|
|
94
96
|
`doctor` re-implements the steward invariants (prompt-file-present,
|
|
95
97
|
backend-consistency `claude`→`CLAUDE.md`, skills-present).
|
|
98
|
+
- **`daemon` noun & process module:** `device`/`app`/`move` are *clients* of a
|
|
99
|
+
running daemon; `reachy/cli/_commands/daemon.py` + `reachy/daemon.py` are the
|
|
100
|
+
other half — they start/stop/status the local `reachy-mini-daemon` OS process
|
|
101
|
+
(background spawn + PID/log under `$REACHY_STATE_DIR` / `$XDG_STATE_HOME/reachy`,
|
|
102
|
+
health-poll via `GET /api/daemon/status`). Pure stdlib (`subprocess`/`signal`/
|
|
103
|
+
`urllib`); the daemon *binary* comes from the `[daemon]` extra. Its `overview`
|
|
104
|
+
is hand-built (no `--transport sdk` line) — `daemon` does NOT use a transport,
|
|
105
|
+
so it does not call `_robot.noun_overview`/`get_transport`. A missing binary
|
|
106
|
+
raises a clean exit-2 `CliError` pointing at the `[daemon]` install.
|
|
96
107
|
|
|
97
108
|
## Hard constraints
|
|
98
109
|
|
|
99
|
-
- **Zero runtime dependencies.** `pyproject.toml`
|
|
100
|
-
purpose; `teken` is dev-only. This is why `whoami`
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
- **Zero *base* runtime dependencies.** `pyproject.toml` keeps base
|
|
111
|
+
`dependencies = []` on purpose; `teken` is dev-only. This is why `whoami`
|
|
112
|
+
hand-rolls YAML parsing and `reachy/daemon.py` manages the daemon process with
|
|
113
|
+
stdlib `subprocess`/`urllib` only. The **recommended default install is `pip
|
|
114
|
+
install 'reachy-cli[daemon]'`** (pulls `reachy-mini` for the
|
|
115
|
+
`reachy-mini-daemon` binary); the bare `pip install reachy-cli` is the
|
|
116
|
+
HTTP-only *remote* profile. Keep the *base* dep-free — anything that needs
|
|
117
|
+
`reachy-mini` (the `sdk` transport, the daemon binary) goes behind the
|
|
118
|
+
`[daemon]`/`[sdk]` extras, never into base `dependencies`. Adding a base
|
|
119
|
+
runtime dep needs an explicit decision; it would break the dependency-free
|
|
120
|
+
remote profile the README sells.
|
|
103
121
|
- **Python ≥ 3.12** (uses `X | None`, `tomllib`, etc.).
|
|
104
122
|
- **Every PR bumps the version**, even docs/config/CI-only changes — the
|
|
105
123
|
`version-check` CI job blocks the merge otherwise (it compares
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reachy-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Agent and CLI for operating the Reachy Mini expressive robot — device setup, app management, and runtime ops.
|
|
5
5
|
Project-URL: Homepage, https://github.com/agentculture/reachy-mini-cli
|
|
6
6
|
Project-URL: Issues, https://github.com/agentculture/reachy-mini-cli/issues
|
|
@@ -13,6 +13,8 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Topic :: Software Development
|
|
15
15
|
Requires-Python: >=3.12
|
|
16
|
+
Provides-Extra: daemon
|
|
17
|
+
Requires-Dist: reachy-mini>=1.0; extra == 'daemon'
|
|
16
18
|
Provides-Extra: sdk
|
|
17
19
|
Requires-Dist: reachy-mini>=1.0; extra == 'sdk'
|
|
18
20
|
Description-Content-Type: text/markdown
|
|
@@ -35,10 +37,11 @@ Agent and CLI for operating the Reachy Mini expressive robot — device setup, a
|
|
|
35
37
|
## Quickstart
|
|
36
38
|
|
|
37
39
|
```bash
|
|
38
|
-
uv sync
|
|
40
|
+
uv sync --extra daemon # default: + the local reachy-mini-daemon
|
|
41
|
+
# uv sync # remote profile: HTTP-only, no daemon deps
|
|
39
42
|
uv run pytest -n auto # run the test suite
|
|
40
|
-
uv run reachy
|
|
41
|
-
uv run reachy
|
|
43
|
+
uv run reachy whoami # identity from culture.yaml
|
|
44
|
+
uv run reachy learn # self-teaching prompt (add --json)
|
|
42
45
|
uv run teken cli doctor . --strict # the agent-first rubric gate CI runs
|
|
43
46
|
```
|
|
44
47
|
|
|
@@ -59,8 +62,44 @@ error, `3+` reserved.
|
|
|
59
62
|
|
|
60
63
|
## Robot operations
|
|
61
64
|
|
|
62
|
-
The `device`, `app`, and `move` noun groups operate the Reachy Mini.
|
|
63
|
-
|
|
65
|
+
The `daemon`, `device`, `app`, and `move` noun groups operate the Reachy Mini.
|
|
66
|
+
|
|
67
|
+
### Install profiles
|
|
68
|
+
|
|
69
|
+
The Reachy daemon (`reachy-mini-daemon`) and the in-process SDK ship in
|
|
70
|
+
`reachy-mini`. Choose your install by where the daemon runs:
|
|
71
|
+
|
|
72
|
+
- **Default — with the daemon:** `pip install 'reachy-cli[daemon]'`. Bundles
|
|
73
|
+
`reachy-mini`, so `reachy daemon start` can bring the daemon up locally. This
|
|
74
|
+
is the profile for a machine with a robot attached.
|
|
75
|
+
- **Remote — without the daemon:** `pip install reachy-cli` (bare). The base
|
|
76
|
+
install keeps **zero runtime dependencies** (the `http` transport and the
|
|
77
|
+
`daemon status`/`stop` verbs use only the stdlib). Use it on a control box that
|
|
78
|
+
only talks to a daemon running elsewhere via `--base-url` / `REACHY_BASE_URL`.
|
|
79
|
+
`daemon start` here exits `2` with a hint to install the `[daemon]` extra.
|
|
80
|
+
|
|
81
|
+
`[sdk]` (also `reachy-mini`) adds the in-process `--transport sdk` client.
|
|
82
|
+
|
|
83
|
+
### Bring the daemon up
|
|
84
|
+
|
|
85
|
+
`device`/`app`/`move` are clients of a running daemon; `daemon` is the other
|
|
86
|
+
half — it manages the local `reachy-mini-daemon` process.
|
|
87
|
+
|
|
88
|
+
| Verb | What it does |
|
|
89
|
+
|------|--------------|
|
|
90
|
+
| `daemon start` | Spawn `reachy-mini-daemon` in the background, then poll its health route until ready. Idempotent. |
|
|
91
|
+
| `daemon stop` | Stop the daemon this CLI started (SIGTERM, then SIGKILL). |
|
|
92
|
+
| `daemon status` | Reconcile the tracked process (running/stopped/stale) with the HTTP health check. |
|
|
93
|
+
|
|
94
|
+
`reachy-mini-daemon` defaults to `--wake-up-on-start`, so `daemon start` already
|
|
95
|
+
wakes the robot. Forward daemon args after `--`, e.g.
|
|
96
|
+
`reachy daemon start -- --sim --no-wake-up-on-start`. State (PID + log) lives
|
|
97
|
+
under `$XDG_STATE_HOME/reachy` (`~/.local/state/reachy`).
|
|
98
|
+
|
|
99
|
+
### Transports
|
|
100
|
+
|
|
101
|
+
The `device`, `app`, and `move` verbs talk to a running daemon through a
|
|
102
|
+
selectable **transport flavor**:
|
|
64
103
|
|
|
65
104
|
- **`http`** (default) — the Reachy daemon's REST API. Uses only the Python
|
|
66
105
|
standard library, so the default install keeps **zero runtime dependencies**.
|
|
@@ -89,11 +128,12 @@ with a clean `error:`/`hint:` pair — never a traceback.
|
|
|
89
128
|
Each noun also exposes `overview` (e.g. `reachy move overview`).
|
|
90
129
|
|
|
91
130
|
```bash
|
|
92
|
-
#
|
|
93
|
-
uv run reachy device status
|
|
131
|
+
uv run reachy daemon start # bring the local daemon up (and wake the robot)
|
|
132
|
+
uv run reachy device status # now answers instead of exit-2
|
|
94
133
|
uv run reachy app list --json
|
|
95
134
|
uv run reachy move goto --z 10 --pitch -5 --duration 2
|
|
96
135
|
uv run reachy move wake
|
|
136
|
+
uv run reachy daemon stop # put it back down when you're done
|
|
97
137
|
```
|
|
98
138
|
|
|
99
139
|
## Make it your own
|
|
@@ -16,10 +16,11 @@ Agent and CLI for operating the Reachy Mini expressive robot — device setup, a
|
|
|
16
16
|
## Quickstart
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
uv sync
|
|
19
|
+
uv sync --extra daemon # default: + the local reachy-mini-daemon
|
|
20
|
+
# uv sync # remote profile: HTTP-only, no daemon deps
|
|
20
21
|
uv run pytest -n auto # run the test suite
|
|
21
|
-
uv run reachy
|
|
22
|
-
uv run reachy
|
|
22
|
+
uv run reachy whoami # identity from culture.yaml
|
|
23
|
+
uv run reachy learn # self-teaching prompt (add --json)
|
|
23
24
|
uv run teken cli doctor . --strict # the agent-first rubric gate CI runs
|
|
24
25
|
```
|
|
25
26
|
|
|
@@ -40,8 +41,44 @@ error, `3+` reserved.
|
|
|
40
41
|
|
|
41
42
|
## Robot operations
|
|
42
43
|
|
|
43
|
-
The `device`, `app`, and `move` noun groups operate the Reachy Mini.
|
|
44
|
-
|
|
44
|
+
The `daemon`, `device`, `app`, and `move` noun groups operate the Reachy Mini.
|
|
45
|
+
|
|
46
|
+
### Install profiles
|
|
47
|
+
|
|
48
|
+
The Reachy daemon (`reachy-mini-daemon`) and the in-process SDK ship in
|
|
49
|
+
`reachy-mini`. Choose your install by where the daemon runs:
|
|
50
|
+
|
|
51
|
+
- **Default — with the daemon:** `pip install 'reachy-cli[daemon]'`. Bundles
|
|
52
|
+
`reachy-mini`, so `reachy daemon start` can bring the daemon up locally. This
|
|
53
|
+
is the profile for a machine with a robot attached.
|
|
54
|
+
- **Remote — without the daemon:** `pip install reachy-cli` (bare). The base
|
|
55
|
+
install keeps **zero runtime dependencies** (the `http` transport and the
|
|
56
|
+
`daemon status`/`stop` verbs use only the stdlib). Use it on a control box that
|
|
57
|
+
only talks to a daemon running elsewhere via `--base-url` / `REACHY_BASE_URL`.
|
|
58
|
+
`daemon start` here exits `2` with a hint to install the `[daemon]` extra.
|
|
59
|
+
|
|
60
|
+
`[sdk]` (also `reachy-mini`) adds the in-process `--transport sdk` client.
|
|
61
|
+
|
|
62
|
+
### Bring the daemon up
|
|
63
|
+
|
|
64
|
+
`device`/`app`/`move` are clients of a running daemon; `daemon` is the other
|
|
65
|
+
half — it manages the local `reachy-mini-daemon` process.
|
|
66
|
+
|
|
67
|
+
| Verb | What it does |
|
|
68
|
+
|------|--------------|
|
|
69
|
+
| `daemon start` | Spawn `reachy-mini-daemon` in the background, then poll its health route until ready. Idempotent. |
|
|
70
|
+
| `daemon stop` | Stop the daemon this CLI started (SIGTERM, then SIGKILL). |
|
|
71
|
+
| `daemon status` | Reconcile the tracked process (running/stopped/stale) with the HTTP health check. |
|
|
72
|
+
|
|
73
|
+
`reachy-mini-daemon` defaults to `--wake-up-on-start`, so `daemon start` already
|
|
74
|
+
wakes the robot. Forward daemon args after `--`, e.g.
|
|
75
|
+
`reachy daemon start -- --sim --no-wake-up-on-start`. State (PID + log) lives
|
|
76
|
+
under `$XDG_STATE_HOME/reachy` (`~/.local/state/reachy`).
|
|
77
|
+
|
|
78
|
+
### Transports
|
|
79
|
+
|
|
80
|
+
The `device`, `app`, and `move` verbs talk to a running daemon through a
|
|
81
|
+
selectable **transport flavor**:
|
|
45
82
|
|
|
46
83
|
- **`http`** (default) — the Reachy daemon's REST API. Uses only the Python
|
|
47
84
|
standard library, so the default install keeps **zero runtime dependencies**.
|
|
@@ -70,11 +107,12 @@ with a clean `error:`/`hint:` pair — never a traceback.
|
|
|
70
107
|
Each noun also exposes `overview` (e.g. `reachy move overview`).
|
|
71
108
|
|
|
72
109
|
```bash
|
|
73
|
-
#
|
|
74
|
-
uv run reachy device status
|
|
110
|
+
uv run reachy daemon start # bring the local daemon up (and wake the robot)
|
|
111
|
+
uv run reachy device status # now answers instead of exit-2
|
|
75
112
|
uv run reachy app list --json
|
|
76
113
|
uv run reachy move goto --z 10 --pitch -5 --duration 2
|
|
77
114
|
uv run reachy move wake
|
|
115
|
+
uv run reachy daemon stop # put it back down when you're done
|
|
78
116
|
```
|
|
79
117
|
|
|
80
118
|
## Make it your own
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "reachy-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Agent and CLI for operating the Reachy Mini expressive robot — device setup, app management, and runtime ops."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -15,12 +15,20 @@ classifiers = [
|
|
|
15
15
|
]
|
|
16
16
|
dependencies = []
|
|
17
17
|
|
|
18
|
-
# Optional
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
18
|
+
# Optional extras. The base install stays dependency-free (the http transport and
|
|
19
|
+
# the `daemon` process verbs use only the stdlib), so the bare `pip install
|
|
20
|
+
# reachy-cli` is the lightweight HTTP-only *remote* profile — for a control box
|
|
21
|
+
# that only talks to a daemon running elsewhere via --base-url / REACHY_BASE_URL.
|
|
22
|
+
#
|
|
23
|
+
# The recommended *default* is `pip install 'reachy-cli[daemon]'`: it pulls in
|
|
24
|
+
# `reachy-mini`, which provides the `reachy-mini-daemon` binary that `reachy
|
|
25
|
+
# daemon start` launches. `[sdk]` enables the in-process `--transport sdk` client.
|
|
26
|
+
# Both resolve to the same `reachy-mini` wheel today (it ships both the daemon
|
|
27
|
+
# binary and the SDK client); they stay separately named for intent clarity.
|
|
28
|
+
# Sanctioned exceptions to the zero-runtime-dependency rule — see
|
|
22
29
|
# docs/adr-0001-sdk-transport-extra.md.
|
|
23
30
|
[project.optional-dependencies]
|
|
31
|
+
daemon = ["reachy-mini>=1.0"]
|
|
24
32
|
sdk = ["reachy-mini>=1.0"]
|
|
25
33
|
|
|
26
34
|
[project.urls]
|
|
@@ -64,6 +64,7 @@ def _argv_has_json(argv: list[str] | None) -> bool:
|
|
|
64
64
|
def _build_parser() -> argparse.ArgumentParser:
|
|
65
65
|
from reachy.cli._commands import app as _app_group
|
|
66
66
|
from reachy.cli._commands import cli as _cli_group
|
|
67
|
+
from reachy.cli._commands import daemon as _daemon_group
|
|
67
68
|
from reachy.cli._commands import device as _device_group
|
|
68
69
|
from reachy.cli._commands import doctor as _doctor_cmd
|
|
69
70
|
from reachy.cli._commands import explain as _explain_cmd
|
|
@@ -91,7 +92,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
91
92
|
_overview_cmd.register(sub)
|
|
92
93
|
_doctor_cmd.register(sub)
|
|
93
94
|
_cli_group.register(sub)
|
|
94
|
-
# Robot noun groups (device setup, app
|
|
95
|
+
# Robot noun groups (daemon lifecycle, device setup, app mgmt, runtime ops).
|
|
96
|
+
_daemon_group.register(sub)
|
|
95
97
|
_device_group.register(sub)
|
|
96
98
|
_app_group.register(sub)
|
|
97
99
|
_move_group.register(sub)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""``reachy-mini-cli daemon`` — local daemon process lifecycle.
|
|
2
|
+
|
|
3
|
+
The robot verbs (``device`` / ``app`` / ``move``) talk *to* a running daemon;
|
|
4
|
+
this noun is the other half — it brings the local ``reachy-mini-daemon`` process
|
|
5
|
+
up and down. ``start`` spawns it in the background and waits for its health
|
|
6
|
+
route, ``stop`` terminates it, ``status`` reconciles the tracked process with the
|
|
7
|
+
HTTP health check. The daemon binary ships in the ``[daemon]`` extra; a missing
|
|
8
|
+
binary yields a clean exit-2 hint pointing at the install.
|
|
9
|
+
|
|
10
|
+
Note: ``reachy-mini-daemon`` defaults to ``--wake-up-on-start``, so ``daemon
|
|
11
|
+
start`` already wakes the robot. Forward ``-- --no-wake-up-on-start`` to skip it.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
from reachy import daemon as _daemon
|
|
20
|
+
from reachy.cli._commands._robot import emit_payload
|
|
21
|
+
from reachy.cli._commands.overview import emit_overview
|
|
22
|
+
from reachy.robot.transport import DEFAULT_BASE_URL, DEFAULT_TIMEOUT
|
|
23
|
+
|
|
24
|
+
_JSON_HELP = "Emit structured JSON."
|
|
25
|
+
|
|
26
|
+
_VERBS = [
|
|
27
|
+
"daemon start — start the local reachy-mini-daemon in the background",
|
|
28
|
+
"daemon stop — stop the daemon this CLI started",
|
|
29
|
+
"daemon status — process + HTTP-health state of the daemon",
|
|
30
|
+
"daemon overview — this summary",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _add_health_args(parser: argparse.ArgumentParser) -> None:
|
|
35
|
+
"""Flags shared by verbs that talk to the daemon's health route."""
|
|
36
|
+
parser.add_argument("--json", action="store_true", help=_JSON_HELP)
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"--base-url",
|
|
39
|
+
default=os.environ.get("REACHY_BASE_URL", DEFAULT_BASE_URL),
|
|
40
|
+
help="Daemon base URL for the health check (env REACHY_BASE_URL).",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--timeout",
|
|
44
|
+
type=float,
|
|
45
|
+
default=DEFAULT_TIMEOUT,
|
|
46
|
+
help=f"Health-check request timeout in seconds (default: {DEFAULT_TIMEOUT}).",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def cmd_daemon_overview(args: argparse.Namespace) -> int:
|
|
51
|
+
sections: list[dict[str, object]] = [
|
|
52
|
+
{"title": "Verbs", "items": list(_VERBS)},
|
|
53
|
+
{
|
|
54
|
+
"title": "State",
|
|
55
|
+
"items": [
|
|
56
|
+
f"pid file: {_daemon.pid_file()}",
|
|
57
|
+
f"log file: {_daemon.log_file()}",
|
|
58
|
+
f"health route: {DEFAULT_BASE_URL}{_daemon.HEALTH_PATH}",
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"title": "Conventions",
|
|
63
|
+
"items": [
|
|
64
|
+
"every command supports --json",
|
|
65
|
+
"start spawns reachy-mini-daemon detached, then polls the health route",
|
|
66
|
+
"the daemon ships in the [daemon] extra: pip install 'reachy-cli[daemon]'",
|
|
67
|
+
"override the binary with --daemon-cmd or REACHY_DAEMON_CMD",
|
|
68
|
+
"forward daemon args after '--' (e.g. -- --sim --fastapi-port 9000)",
|
|
69
|
+
"exit codes: 0 ok, 1 user error, 2 environment (binary/daemon missing)",
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
]
|
|
73
|
+
emit_overview(
|
|
74
|
+
"reachy-mini-cli daemon",
|
|
75
|
+
sections,
|
|
76
|
+
json_mode=bool(getattr(args, "json", False)),
|
|
77
|
+
)
|
|
78
|
+
return 0
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def cmd_daemon_start(args: argparse.Namespace) -> int:
|
|
82
|
+
data = _daemon.start(
|
|
83
|
+
base_url=args.base_url,
|
|
84
|
+
wait=not args.no_wait,
|
|
85
|
+
wait_timeout=args.wait_timeout,
|
|
86
|
+
poll_timeout=args.timeout,
|
|
87
|
+
daemon_cmd=args.daemon_cmd,
|
|
88
|
+
extra_args=list(args.daemon_args or []),
|
|
89
|
+
)
|
|
90
|
+
emit_payload(data, json_mode=bool(getattr(args, "json", False)))
|
|
91
|
+
return 0
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def cmd_daemon_stop(args: argparse.Namespace) -> int:
|
|
95
|
+
data = _daemon.stop(timeout=args.timeout)
|
|
96
|
+
emit_payload(data, json_mode=bool(getattr(args, "json", False)))
|
|
97
|
+
return 0
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def cmd_daemon_status(args: argparse.Namespace) -> int:
|
|
101
|
+
data = _daemon.status(base_url=args.base_url, timeout=args.timeout)
|
|
102
|
+
emit_payload(data, json_mode=bool(getattr(args, "json", False)))
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _no_verb(args: argparse.Namespace) -> int:
|
|
107
|
+
return cmd_daemon_overview(args)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def register(sub: argparse._SubParsersAction) -> None:
|
|
111
|
+
p = sub.add_parser(
|
|
112
|
+
"daemon",
|
|
113
|
+
help="Local daemon process lifecycle (see 'reachy-mini-cli daemon overview').",
|
|
114
|
+
)
|
|
115
|
+
p.add_argument("--json", action="store_true", help=_JSON_HELP)
|
|
116
|
+
p.set_defaults(func=_no_verb, json=False)
|
|
117
|
+
noun_sub = p.add_subparsers(dest="daemon_command", parser_class=type(p))
|
|
118
|
+
|
|
119
|
+
ov = noun_sub.add_parser("overview", help="Describe the daemon noun group.")
|
|
120
|
+
ov.add_argument("--json", action="store_true", help=_JSON_HELP)
|
|
121
|
+
ov.set_defaults(func=cmd_daemon_overview)
|
|
122
|
+
|
|
123
|
+
start = noun_sub.add_parser("start", help="Start the local reachy-mini-daemon.")
|
|
124
|
+
_add_health_args(start)
|
|
125
|
+
start.add_argument(
|
|
126
|
+
"--daemon-cmd",
|
|
127
|
+
default=None,
|
|
128
|
+
help="Override the daemon launch command (env REACHY_DAEMON_CMD).",
|
|
129
|
+
)
|
|
130
|
+
start.add_argument(
|
|
131
|
+
"--no-wait",
|
|
132
|
+
action="store_true",
|
|
133
|
+
help="Return immediately after spawning, without polling the health route.",
|
|
134
|
+
)
|
|
135
|
+
start.add_argument(
|
|
136
|
+
"--wait-timeout",
|
|
137
|
+
type=float,
|
|
138
|
+
default=_daemon.DEFAULT_WAIT_TIMEOUT,
|
|
139
|
+
help="Seconds to wait for the daemon to become healthy "
|
|
140
|
+
f"(default: {_daemon.DEFAULT_WAIT_TIMEOUT:g}).",
|
|
141
|
+
)
|
|
142
|
+
start.add_argument(
|
|
143
|
+
"daemon_args",
|
|
144
|
+
nargs="*",
|
|
145
|
+
default=[],
|
|
146
|
+
metavar="-- ARGS",
|
|
147
|
+
help="Args after '--' are forwarded to reachy-mini-daemon "
|
|
148
|
+
"(e.g. -- --sim --fastapi-port 9000).",
|
|
149
|
+
)
|
|
150
|
+
start.set_defaults(func=cmd_daemon_start)
|
|
151
|
+
|
|
152
|
+
stop = noun_sub.add_parser("stop", help="Stop the daemon this CLI started.")
|
|
153
|
+
stop.add_argument("--json", action="store_true", help=_JSON_HELP)
|
|
154
|
+
stop.add_argument(
|
|
155
|
+
"--timeout",
|
|
156
|
+
type=float,
|
|
157
|
+
default=_daemon.DEFAULT_STOP_TIMEOUT,
|
|
158
|
+
help="Seconds to wait after SIGTERM before SIGKILL "
|
|
159
|
+
f"(default: {_daemon.DEFAULT_STOP_TIMEOUT:g}).",
|
|
160
|
+
)
|
|
161
|
+
stop.set_defaults(func=cmd_daemon_stop)
|
|
162
|
+
|
|
163
|
+
st = noun_sub.add_parser("status", help="Report daemon process + HTTP-health state.")
|
|
164
|
+
_add_health_args(st)
|
|
165
|
+
st.set_defaults(func=cmd_daemon_status)
|
|
@@ -31,6 +31,7 @@ Commands
|
|
|
31
31
|
reachy-mini-cli cli overview Describe the CLI surface itself.
|
|
32
32
|
|
|
33
33
|
Robot commands (talk to the Reachy daemon; --transport http|sdk)
|
|
34
|
+
reachy-mini-cli daemon start Start the local daemon; also: stop/status.
|
|
34
35
|
reachy-mini-cli device status Daemon status / device info.
|
|
35
36
|
reachy-mini-cli device state Live robot state (pose, antennas).
|
|
36
37
|
reachy-mini-cli app list Available apps; also: status/start/stop.
|
|
@@ -66,6 +67,9 @@ def _as_json_payload() -> dict[str, object]:
|
|
|
66
67
|
{"path": ["overview"], "summary": "Descriptive snapshot of the agent."},
|
|
67
68
|
{"path": ["doctor"], "summary": "Check the agent-identity invariants."},
|
|
68
69
|
{"path": ["cli", "overview"], "summary": "Describe the CLI surface."},
|
|
70
|
+
{"path": ["daemon", "start"], "summary": "Start the local reachy-mini-daemon."},
|
|
71
|
+
{"path": ["daemon", "stop"], "summary": "Stop the daemon this CLI started."},
|
|
72
|
+
{"path": ["daemon", "status"], "summary": "Daemon process + HTTP-health state."},
|
|
69
73
|
{"path": ["device", "status"], "summary": "Daemon status / device info."},
|
|
70
74
|
{"path": ["device", "state"], "summary": "Live robot state."},
|
|
71
75
|
{"path": ["app", "list"], "summary": "List/start/stop Reachy Mini apps."},
|
|
@@ -30,6 +30,7 @@ _VERBS = [
|
|
|
30
30
|
"explain <path> — markdown docs for a topic",
|
|
31
31
|
"overview — this descriptive snapshot",
|
|
32
32
|
"doctor — check the agent-identity invariants",
|
|
33
|
+
"daemon <verb> — start/stop/check the local reachy-mini-daemon process",
|
|
33
34
|
"device <verb> — daemon/robot status and live state",
|
|
34
35
|
"app <verb> — list/start/stop Reachy Mini apps",
|
|
35
36
|
"move <verb> — runtime motion (goto, wake, sleep)",
|