cyclo-manager 1.0.0.dev10__tar.gz → 1.1.0.dev2__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.
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/PKG-INFO +16 -9
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/README.md +15 -8
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_host_agent/main.py +3 -1
- cyclo_manager-1.1.0.dev2/cyclo_host_agent/models.py +242 -0
- cyclo_manager-1.1.0.dev2/cyclo_host_agent/routers/files.py +540 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_host_agent/routers/repos.py +178 -4
- cyclo_manager-1.1.0.dev2/cyclo_host_agent/routers/system_stats.py +156 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager.egg-info/PKG-INFO +16 -9
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager.egg-info/SOURCES.txt +2 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager_cli/docker/docker-compose.yml +2 -2
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/pyproject.toml +1 -1
- cyclo_manager-1.0.0.dev10/cyclo_host_agent/models.py +0 -105
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_host_agent/__init__.py +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_host_agent/routers/__init__.py +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_host_agent/routers/update.py +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager.egg-info/dependency_links.txt +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager.egg-info/entry_points.txt +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager.egg-info/requires.txt +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager.egg-info/top_level.txt +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager_cli/__init__.py +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager_cli/cli.py +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/cyclo_manager_cli/config/config.yml +0 -0
- {cyclo_manager-1.0.0.dev10 → cyclo_manager-1.1.0.dev2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cyclo-manager
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0.dev2
|
|
4
4
|
Summary: cyclo_manager CLI: pip-installable launcher for cyclo_manager server and UI containers. Run 'cyclo_manager up' to start Docker stack.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -18,7 +18,7 @@ Requires-Dist: ruff; extra == "dev"
|
|
|
18
18
|
**Console commands:** `cyclo_manager` (underscore), `cyclo-manager` (hyphen)
|
|
19
19
|
**Host agent:** `cyclo_host_agent` (installed by the package, run via systemd)
|
|
20
20
|
|
|
21
|
-
This package is the **pip-installable launcher** for the cyclo_manager stack on a robot host. It:
|
|
21
|
+
This package is the **pip-installable launcher** for the cyclo_manager stack on a robot host. It is intended for production robots that run the pre-built Docker images. It:
|
|
22
22
|
|
|
23
23
|
1. Runs **`docker compose`** against a **bundled** [`cyclo_manager_cli/docker/docker-compose.yml`](cyclo_manager_cli/docker/docker-compose.yml) to start the **API** and **web UI** containers.
|
|
24
24
|
2. Installs and maintains **`cyclo_host_agent`** as a **systemd** service for host-level operations (git repo updates, `cyclo_manager` package update from the UI).
|
|
@@ -50,11 +50,12 @@ For the full monorepo (API source, UI source, dev compose), see the [repository
|
|
|
50
50
|
|
|
51
51
|
## Prerequisites
|
|
52
52
|
|
|
53
|
-
- **Docker** with **Compose v2** (`docker compose
|
|
53
|
+
- **Docker** with **Compose v2** (`docker compose`; the legacy `docker-compose` binary alone is not enough)
|
|
54
54
|
- **Python 3.10+**
|
|
55
|
+
- **systemd**: required for the managed `cyclo_host_agent.service`
|
|
55
56
|
- **`sudo`** — required on normal-user hosts to install the host agent systemd unit, socket directory permissions, and sudoers rules
|
|
56
57
|
- For **`cyclo_manager update`:** **`pip`** or **`pip3`** on `PATH`
|
|
57
|
-
- **Root shell handling** — root
|
|
58
|
+
- **Root shell handling** — `cyclo_manager up` rejects direct root execution when a normal login user exists. On root-only devices, the host agent is installed as root.
|
|
58
59
|
- **Agent sockets** on the host under `/var/run/robotis/agent_sockets/` (robot containers and host agent). The bundled Compose file bind-mounts this tree into the API container as `/agents/`.
|
|
59
60
|
|
|
60
61
|
---
|
|
@@ -94,6 +95,8 @@ python3 -m cyclo_manager_cli.cli up
|
|
|
94
95
|
| `cyclo_manager update --no-pull` | Same as `update`, but skip image pull (use locally cached images). |
|
|
95
96
|
| `cyclo_manager --help` | Subcommand overview |
|
|
96
97
|
|
|
98
|
+
The CLI also has an internal `refresh-host-agent` subcommand. It is used by the sudoers rule and should normally not be invoked by users directly.
|
|
99
|
+
|
|
97
100
|
**`down` vs `update`:** `down` removes the host agent entirely. `update` only stops Docker containers, upgrades the pip package, restarts the stack, and refreshes the host agent — it does **not** call `cyclo_manager down`.
|
|
98
101
|
|
|
99
102
|
---
|
|
@@ -120,18 +123,21 @@ The pip package installs a small **FastAPI** server that listens on a **Unix dom
|
|
|
120
123
|
|------|------|
|
|
121
124
|
| Socket | `/var/run/robotis/agent_sockets/host/host_agent.sock` |
|
|
122
125
|
| systemd unit | `cyclo_host_agent.service` |
|
|
123
|
-
| sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts; `refresh-host-agent` CLI subcommand for
|
|
126
|
+
| sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts; `refresh-host-agent` CLI subcommand for package-update refresh; skipped on root-only devices) |
|
|
124
127
|
|
|
125
128
|
The API container reaches it at `/agents/host/host_agent.sock` (see bundled `config.yml`).
|
|
126
129
|
|
|
127
130
|
**Responsibilities:**
|
|
128
131
|
|
|
129
|
-
- List
|
|
130
|
-
-
|
|
132
|
+
- List managed **ROBOTIS-GIT** git repositories in the configured workspace.
|
|
133
|
+
- Check update availability from remote tags and local `package.xml` versions.
|
|
134
|
+
- Update repos on allowed branches (`main`, `jazzy`) using stash or reset workflows.
|
|
135
|
+
- Stop/start a repo's `docker/container.sh` helper during an update when requested by the UI.
|
|
136
|
+
- Run Cyclo Manager package updates from the UI by delegating to `cyclo_manager update`.
|
|
131
137
|
|
|
132
138
|
`cyclo_manager up` installs the `refresh-host-agent` CLI subcommand into sudoers. It is registered as `sys.executable -m cyclo_manager_cli.cli refresh-host-agent` with `SETENV`, so UI-triggered updates can pass the current `PYTHONPATH` to sudo and do not depend on `PATH`. The command is idempotent and refreshes the socket directory, sudoers file, systemd unit, and service state after package upgrades.
|
|
133
139
|
|
|
134
|
-
Repository scanning
|
|
140
|
+
Repository scanning and the Files UI use `CYCLO_HOST_AGENT_WORKSPACE` when set. Otherwise they use the service user's home directory, except root-only devices with `/data/docker`, where `/data/docker` is used automatically. Only repositories whose `origin` remote belongs to `ROBOTIS-GIT` are returned by the repo list/update endpoints.
|
|
135
141
|
|
|
136
142
|
---
|
|
137
143
|
|
|
@@ -211,6 +217,7 @@ So `/agents/ai_worker/s6_agent.sock` in config corresponds to the host path abov
|
|
|
211
217
|
| Variable | Set by | Purpose |
|
|
212
218
|
|----------|--------|---------|
|
|
213
219
|
| **`CYCLO_MANAGER_CONFIG_FILE`** | CLI (`up`, `down`, `update`) | Absolute path to bundled `config.yml` on the host; mounted into the API container |
|
|
220
|
+
| **`CYCLO_HOST_AGENT_WORKSPACE`** | User or CLI-generated systemd unit | Workspace scanned by `cyclo_host_agent` for managed git repositories and used as the Files UI/API root |
|
|
214
221
|
| **`HOSTNAME`** | CLI (default: machine hostname) | Passed to API as `HOST_HOSTNAME` |
|
|
215
222
|
| **`CONFIG_FILE`** | Compose (`/app/config.yml`) | Path inside the API container |
|
|
216
223
|
| **`ROS_DOMAIN_ID`** | **Not** set by CLI | Set inside robot containers (e.g. `~/.bashrc`) so DDS matches your fleet |
|
|
@@ -252,7 +259,7 @@ Python dependencies declared in [`pyproject.toml`](pyproject.toml):
|
|
|
252
259
|
|---------|---------|
|
|
253
260
|
| **fastapi** | `cyclo_host_agent` HTTP API |
|
|
254
261
|
| **uvicorn** | `cyclo_host_agent` server |
|
|
255
|
-
| **psutil** |
|
|
262
|
+
| **psutil** | Declared package dependency for host/system utility support |
|
|
256
263
|
|
|
257
264
|
The **`cyclo_manager`** CLI itself uses only the Python standard library plus **`subprocess`** calls to **`docker compose`**, **`pip`**, **`systemctl`**, and **`sudo`**.
|
|
258
265
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
**Console commands:** `cyclo_manager` (underscore), `cyclo-manager` (hyphen)
|
|
5
5
|
**Host agent:** `cyclo_host_agent` (installed by the package, run via systemd)
|
|
6
6
|
|
|
7
|
-
This package is the **pip-installable launcher** for the cyclo_manager stack on a robot host. It:
|
|
7
|
+
This package is the **pip-installable launcher** for the cyclo_manager stack on a robot host. It is intended for production robots that run the pre-built Docker images. It:
|
|
8
8
|
|
|
9
9
|
1. Runs **`docker compose`** against a **bundled** [`cyclo_manager_cli/docker/docker-compose.yml`](cyclo_manager_cli/docker/docker-compose.yml) to start the **API** and **web UI** containers.
|
|
10
10
|
2. Installs and maintains **`cyclo_host_agent`** as a **systemd** service for host-level operations (git repo updates, `cyclo_manager` package update from the UI).
|
|
@@ -36,11 +36,12 @@ For the full monorepo (API source, UI source, dev compose), see the [repository
|
|
|
36
36
|
|
|
37
37
|
## Prerequisites
|
|
38
38
|
|
|
39
|
-
- **Docker** with **Compose v2** (`docker compose
|
|
39
|
+
- **Docker** with **Compose v2** (`docker compose`; the legacy `docker-compose` binary alone is not enough)
|
|
40
40
|
- **Python 3.10+**
|
|
41
|
+
- **systemd**: required for the managed `cyclo_host_agent.service`
|
|
41
42
|
- **`sudo`** — required on normal-user hosts to install the host agent systemd unit, socket directory permissions, and sudoers rules
|
|
42
43
|
- For **`cyclo_manager update`:** **`pip`** or **`pip3`** on `PATH`
|
|
43
|
-
- **Root shell handling** — root
|
|
44
|
+
- **Root shell handling** — `cyclo_manager up` rejects direct root execution when a normal login user exists. On root-only devices, the host agent is installed as root.
|
|
44
45
|
- **Agent sockets** on the host under `/var/run/robotis/agent_sockets/` (robot containers and host agent). The bundled Compose file bind-mounts this tree into the API container as `/agents/`.
|
|
45
46
|
|
|
46
47
|
---
|
|
@@ -80,6 +81,8 @@ python3 -m cyclo_manager_cli.cli up
|
|
|
80
81
|
| `cyclo_manager update --no-pull` | Same as `update`, but skip image pull (use locally cached images). |
|
|
81
82
|
| `cyclo_manager --help` | Subcommand overview |
|
|
82
83
|
|
|
84
|
+
The CLI also has an internal `refresh-host-agent` subcommand. It is used by the sudoers rule and should normally not be invoked by users directly.
|
|
85
|
+
|
|
83
86
|
**`down` vs `update`:** `down` removes the host agent entirely. `update` only stops Docker containers, upgrades the pip package, restarts the stack, and refreshes the host agent — it does **not** call `cyclo_manager down`.
|
|
84
87
|
|
|
85
88
|
---
|
|
@@ -106,18 +109,21 @@ The pip package installs a small **FastAPI** server that listens on a **Unix dom
|
|
|
106
109
|
|------|------|
|
|
107
110
|
| Socket | `/var/run/robotis/agent_sockets/host/host_agent.sock` |
|
|
108
111
|
| systemd unit | `cyclo_host_agent.service` |
|
|
109
|
-
| sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts; `refresh-host-agent` CLI subcommand for
|
|
112
|
+
| sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts; `refresh-host-agent` CLI subcommand for package-update refresh; skipped on root-only devices) |
|
|
110
113
|
|
|
111
114
|
The API container reaches it at `/agents/host/host_agent.sock` (see bundled `config.yml`).
|
|
112
115
|
|
|
113
116
|
**Responsibilities:**
|
|
114
117
|
|
|
115
|
-
- List
|
|
116
|
-
-
|
|
118
|
+
- List managed **ROBOTIS-GIT** git repositories in the configured workspace.
|
|
119
|
+
- Check update availability from remote tags and local `package.xml` versions.
|
|
120
|
+
- Update repos on allowed branches (`main`, `jazzy`) using stash or reset workflows.
|
|
121
|
+
- Stop/start a repo's `docker/container.sh` helper during an update when requested by the UI.
|
|
122
|
+
- Run Cyclo Manager package updates from the UI by delegating to `cyclo_manager update`.
|
|
117
123
|
|
|
118
124
|
`cyclo_manager up` installs the `refresh-host-agent` CLI subcommand into sudoers. It is registered as `sys.executable -m cyclo_manager_cli.cli refresh-host-agent` with `SETENV`, so UI-triggered updates can pass the current `PYTHONPATH` to sudo and do not depend on `PATH`. The command is idempotent and refreshes the socket directory, sudoers file, systemd unit, and service state after package upgrades.
|
|
119
125
|
|
|
120
|
-
Repository scanning
|
|
126
|
+
Repository scanning and the Files UI use `CYCLO_HOST_AGENT_WORKSPACE` when set. Otherwise they use the service user's home directory, except root-only devices with `/data/docker`, where `/data/docker` is used automatically. Only repositories whose `origin` remote belongs to `ROBOTIS-GIT` are returned by the repo list/update endpoints.
|
|
121
127
|
|
|
122
128
|
---
|
|
123
129
|
|
|
@@ -197,6 +203,7 @@ So `/agents/ai_worker/s6_agent.sock` in config corresponds to the host path abov
|
|
|
197
203
|
| Variable | Set by | Purpose |
|
|
198
204
|
|----------|--------|---------|
|
|
199
205
|
| **`CYCLO_MANAGER_CONFIG_FILE`** | CLI (`up`, `down`, `update`) | Absolute path to bundled `config.yml` on the host; mounted into the API container |
|
|
206
|
+
| **`CYCLO_HOST_AGENT_WORKSPACE`** | User or CLI-generated systemd unit | Workspace scanned by `cyclo_host_agent` for managed git repositories and used as the Files UI/API root |
|
|
200
207
|
| **`HOSTNAME`** | CLI (default: machine hostname) | Passed to API as `HOST_HOSTNAME` |
|
|
201
208
|
| **`CONFIG_FILE`** | Compose (`/app/config.yml`) | Path inside the API container |
|
|
202
209
|
| **`ROS_DOMAIN_ID`** | **Not** set by CLI | Set inside robot containers (e.g. `~/.bashrc`) so DDS matches your fleet |
|
|
@@ -238,7 +245,7 @@ Python dependencies declared in [`pyproject.toml`](pyproject.toml):
|
|
|
238
245
|
|---------|---------|
|
|
239
246
|
| **fastapi** | `cyclo_host_agent` HTTP API |
|
|
240
247
|
| **uvicorn** | `cyclo_host_agent` server |
|
|
241
|
-
| **psutil** |
|
|
248
|
+
| **psutil** | Declared package dependency for host/system utility support |
|
|
242
249
|
|
|
243
250
|
The **`cyclo_manager`** CLI itself uses only the Python standard library plus **`subprocess`** calls to **`docker compose`**, **`pip`**, **`systemctl`**, and **`sudo`**.
|
|
244
251
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import logging
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
|
|
24
|
-
from cyclo_host_agent.routers import repos, update
|
|
24
|
+
from cyclo_host_agent.routers import files, repos, system_stats, update
|
|
25
25
|
from cyclo_manager_cli import __version__
|
|
26
26
|
from fastapi import FastAPI
|
|
27
27
|
import uvicorn
|
|
@@ -41,6 +41,8 @@ app = FastAPI(
|
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
app.include_router(repos.router)
|
|
44
|
+
app.include_router(files.router)
|
|
45
|
+
app.include_router(system_stats.router)
|
|
44
46
|
app.include_router(update.router)
|
|
45
47
|
|
|
46
48
|
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 ROBOTIS CO., LTD.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
#
|
|
17
|
+
# Author: Hyungyu Kim
|
|
18
|
+
|
|
19
|
+
"""Pydantic models for cyclo_host_agent API."""
|
|
20
|
+
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
from pydantic import BaseModel
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RepoInfo(BaseModel):
|
|
27
|
+
"""Single git repository info."""
|
|
28
|
+
|
|
29
|
+
name: str
|
|
30
|
+
path: str
|
|
31
|
+
branch: Optional[str] = None
|
|
32
|
+
remote: Optional[str] = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class RepoListResponse(BaseModel):
|
|
36
|
+
"""Response for GET /repos."""
|
|
37
|
+
|
|
38
|
+
repos: list[RepoInfo]
|
|
39
|
+
workspace_path: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RepoUpdateStatus(BaseModel):
|
|
43
|
+
"""Version update status for a single repository."""
|
|
44
|
+
|
|
45
|
+
name: str
|
|
46
|
+
branch: Optional[str] = None
|
|
47
|
+
current_version: Optional[str] = None
|
|
48
|
+
latest_version: Optional[str] = None
|
|
49
|
+
has_update: bool
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class RepoUpdatesResponse(BaseModel):
|
|
53
|
+
"""Response for GET /repos/updates."""
|
|
54
|
+
|
|
55
|
+
repos: list[RepoUpdateStatus]
|
|
56
|
+
workspace_path: str
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class FileChange(BaseModel):
|
|
60
|
+
"""A single entry from git status --porcelain."""
|
|
61
|
+
|
|
62
|
+
path: str
|
|
63
|
+
status: str # e.g. M, D, ??, etc.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class RepoStatusResponse(BaseModel):
|
|
67
|
+
"""Response for GET /repos/{name}/status."""
|
|
68
|
+
|
|
69
|
+
name: str
|
|
70
|
+
changes: list[FileChange]
|
|
71
|
+
has_changes: bool
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class RepoBranchCheckResponse(BaseModel):
|
|
75
|
+
"""Response for GET /repos/{name}/branch."""
|
|
76
|
+
|
|
77
|
+
name: str
|
|
78
|
+
branch: Optional[str] = None
|
|
79
|
+
allowed: bool
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class UpdateRequest(BaseModel):
|
|
83
|
+
"""Request body for POST /repos/{name}/update."""
|
|
84
|
+
|
|
85
|
+
strategy: str # 'stash' | 'reset'
|
|
86
|
+
preserve_files: list[str] = [] # files to keep when using the reset strategy
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class UpdateResponse(BaseModel):
|
|
90
|
+
"""Response for POST /repos/{name}/update."""
|
|
91
|
+
|
|
92
|
+
name: str
|
|
93
|
+
success: bool
|
|
94
|
+
output: str
|
|
95
|
+
stash_conflict: bool = False
|
|
96
|
+
stash_conflict_files: list[str] = []
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ContainerScriptResponse(BaseModel):
|
|
100
|
+
"""Response for POST /repos/{name}/container/{action}."""
|
|
101
|
+
|
|
102
|
+
name: str
|
|
103
|
+
action: str
|
|
104
|
+
success: bool
|
|
105
|
+
output: str
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ContainerStartStatusResponse(BaseModel):
|
|
109
|
+
"""Current output/status for a container start operation."""
|
|
110
|
+
|
|
111
|
+
running: bool
|
|
112
|
+
output: str
|
|
113
|
+
success: bool | None = None
|
|
114
|
+
error: str = ''
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class HostSystemStatsResponse(BaseModel):
|
|
118
|
+
"""Host CPU/memory/disk/uptime/temperature status."""
|
|
119
|
+
|
|
120
|
+
cpu_percent: float
|
|
121
|
+
memory_used_mb: int
|
|
122
|
+
memory_total_mb: int
|
|
123
|
+
disk_used_gb: float
|
|
124
|
+
disk_total_gb: float
|
|
125
|
+
ssd_used_gb: float | None = None
|
|
126
|
+
ssd_total_gb: float | None = None
|
|
127
|
+
ssd_mount_path: str | None = None
|
|
128
|
+
uptime_seconds: int
|
|
129
|
+
temperature_celsius: float | None = None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class HostProcessInfo(BaseModel):
|
|
133
|
+
"""Single host process CPU/memory usage sample."""
|
|
134
|
+
|
|
135
|
+
pid: int
|
|
136
|
+
user: str
|
|
137
|
+
cpu_percent: float
|
|
138
|
+
memory_percent: float
|
|
139
|
+
rss_kb: int | None = None
|
|
140
|
+
command: str
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class HostProcessesResponse(BaseModel):
|
|
144
|
+
"""Host process list with aggregate resource summary."""
|
|
145
|
+
|
|
146
|
+
cpu_percent: float
|
|
147
|
+
memory_used_mb: int
|
|
148
|
+
memory_total_mb: int
|
|
149
|
+
processes: list[HostProcessInfo]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class FileTreeEntry(BaseModel):
|
|
153
|
+
"""Single file browser entry."""
|
|
154
|
+
|
|
155
|
+
name: str
|
|
156
|
+
path: str
|
|
157
|
+
type: str
|
|
158
|
+
size: int | None = None
|
|
159
|
+
modified: float | None = None
|
|
160
|
+
readonly: bool = False
|
|
161
|
+
hidden: bool = False
|
|
162
|
+
symlink: bool = False
|
|
163
|
+
git_status: str | None = None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class FileTreeResponse(BaseModel):
|
|
167
|
+
"""Response for GET /files/tree."""
|
|
168
|
+
|
|
169
|
+
root_path: str
|
|
170
|
+
path: str
|
|
171
|
+
entries: list[FileTreeEntry]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class FileSearchResponse(BaseModel):
|
|
175
|
+
"""Response for GET /files/search."""
|
|
176
|
+
|
|
177
|
+
root_path: str
|
|
178
|
+
path: str
|
|
179
|
+
query: str
|
|
180
|
+
entries: list[FileTreeEntry]
|
|
181
|
+
truncated: bool = False
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class FileReadResponse(BaseModel):
|
|
185
|
+
"""Response for GET /files/read."""
|
|
186
|
+
|
|
187
|
+
path: str
|
|
188
|
+
content: str
|
|
189
|
+
size: int
|
|
190
|
+
modified: float
|
|
191
|
+
readonly: bool = False
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class FileDiffResponse(BaseModel):
|
|
195
|
+
"""Response for GET /files/diff."""
|
|
196
|
+
|
|
197
|
+
path: str
|
|
198
|
+
status: str
|
|
199
|
+
original_content: str
|
|
200
|
+
current_content: str
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class FileWriteRequest(BaseModel):
|
|
204
|
+
"""Request body for POST /files/write."""
|
|
205
|
+
|
|
206
|
+
path: str
|
|
207
|
+
content: str
|
|
208
|
+
expected_modified: float | None = None
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class FileCreateRequest(BaseModel):
|
|
212
|
+
"""Request body for POST /files/create."""
|
|
213
|
+
|
|
214
|
+
path: str
|
|
215
|
+
type: str
|
|
216
|
+
content: str = ''
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class FileRenameRequest(BaseModel):
|
|
220
|
+
"""Request body for POST /files/rename."""
|
|
221
|
+
|
|
222
|
+
path: str
|
|
223
|
+
new_name: str
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class FileUploadResponse(BaseModel):
|
|
227
|
+
"""Response for POST /files/upload."""
|
|
228
|
+
|
|
229
|
+
name: str
|
|
230
|
+
path: str
|
|
231
|
+
size: int
|
|
232
|
+
overwritten: bool = False
|
|
233
|
+
success: bool
|
|
234
|
+
message: str
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class FileOperationResponse(BaseModel):
|
|
238
|
+
"""Generic file operation response."""
|
|
239
|
+
|
|
240
|
+
path: str
|
|
241
|
+
success: bool
|
|
242
|
+
message: str
|