gemini-computer-use 0.4.3 → 0.4.4
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.
- package/README.md +18 -1086
- package/cli.js +56 -0
- package/package.json +14 -24
- package/LICENSE +0 -21
- package/bin/cli.js +0 -122
- package/core/__init__.py +0 -0
- package/core/agent.py +0 -767
- package/core/agent_harness.py +0 -67
- package/core/code_cache.py +0 -244
- package/core/device.py +0 -2400
- package/core/domain.py +0 -303
- package/core/hooks.py +0 -61
- package/core/mcp_tools.py +0 -3497
- package/core/policies.py +0 -132
- package/core/schemas.py +0 -49
- package/core/server.py +0 -972
- package/core/subagents.py +0 -55
- package/core/termux.py +0 -916
- package/core/triggers.py +0 -70
- package/core/updater.py +0 -1713
- package/core/version.py +0 -31
- package/core/vitals.py +0 -181
- package/core/web_share.py +0 -814
- package/gateway.py +0 -2862
- package/install-gui.cmd +0 -40
- package/install-gui.ps1 +0 -1882
- package/install-gui.strings.json +0 -211
- package/install.ps1 +0 -1287
- package/install.sh +0 -2013
- package/ops/doctor.ps1 +0 -735
- package/ops/mesh-domain.sh +0 -86
- package/ops/nginx/README.md +0 -77
- package/ops/nginx/antigravity-mesh-mcp.conf +0 -172
- package/ops/nginx/mesh-domain.vhost.template +0 -58
- package/ops/nginx/render-domain.sh +0 -123
- package/ops/update.ps1 +0 -373
- package/ops/windows/README.md +0 -298
- package/ops/windows/agent-watchdog.ps1 +0 -575
- package/ops/windows/run-hidden.vbs +0 -64
- package/skills/orchestrator.md +0 -26
package/README.md
CHANGED
|
@@ -1,1094 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# gemini-computer-use
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
**This package is an alias.** The project was renamed to
|
|
4
|
+
[**remote-harness-mcp**](https://www.npmjs.com/package/remote-harness-mcp), and
|
|
5
|
+
this name is kept so existing installs, scripts and documentation keep working.
|
|
5
6
|
|
|
6
|
-
[English](README.md) | [🇷🇺 Русская версия](#-русская-версия)
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## 💡 The idea
|
|
11
|
-
|
|
12
|
-
Gemini in a browser or on a phone can talk, but it cannot *do* anything on your computer.
|
|
13
|
-
**Gemini Computer Use** closes that gap. It is a free, open-source **Model Context Protocol (MCP)** server that you connect to **Gemini Spark** ([gemini.google.com/spark/apps](https://gemini.google.com/spark/apps) or the Gemini mobile app) with a single link. After that, Gemini gets hands:
|
|
14
|
-
|
|
15
|
-
- runs shell commands, builds and tests code, manages git;
|
|
16
|
-
- reads, writes and edits files;
|
|
17
|
-
- searches the file system;
|
|
18
|
-
- starts long-running jobs and checks on them later;
|
|
19
|
-
- watches CPU, RAM and disk.
|
|
20
|
-
|
|
21
|
-
```text
|
|
22
|
-
📱 Gemini app / 🌐 gemini.google.com (Spark)
|
|
23
|
-
│ MCP (SSE / Streamable HTTP)
|
|
24
|
-
▼
|
|
25
|
-
☁️ Gateway racknerd-5a24bf9.merino-carob.ts.net ← relay only, TLS
|
|
26
|
-
│ outbound WebSocket tunnel (no open ports)
|
|
27
|
-
▼
|
|
28
|
-
💻 Your PC · laptop · VPS · home server → executes the task
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Result:** your phone becomes a remote control for every machine you own — and Gemini is the agent that operates them.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## 🔥 What you can do with it
|
|
36
|
-
|
|
37
|
-
Just ask Gemini in plain language, from anywhere:
|
|
38
|
-
|
|
39
|
-
- *"Check why my home server is slow and show the top processes"*
|
|
40
|
-
- *"Pull the latest changes in ~/projects/api, run the tests and tell me what failed"*
|
|
41
|
-
- *"Fix the typo in config.yaml and restart the service"*
|
|
42
|
-
- *"Start a full backup in the background and tell me when it's done"*
|
|
43
|
-
- *"How much free disk space is left on my laptop?"*
|
|
44
|
-
- *"Turn on the phone's torch, and tell me its battery level and signal"*
|
|
45
|
-
|
|
46
|
-
No SSH client, no terminal on your phone, no VPN — only a chat.
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## ✨ Why it works this way
|
|
51
|
-
|
|
52
|
-
| | |
|
|
53
|
-
| :--- | :--- |
|
|
54
|
-
| 📱 **Mobile-first agent** | Works in the official Gemini mobile app and on the web through Gemini Spark. |
|
|
55
|
-
| 🖥️ **Any PC** | Linux, macOS, Windows — desktops, laptops, VPS, containers. **Android phones and tablets** run the same node inside Termux. |
|
|
56
|
-
| 🌐 **Behind any NAT** | The node opens an *outbound* WebSocket tunnel. No public IP, no port forwarding, no router setup. |
|
|
57
|
-
| ⚡ **One-line install** | Detects OS, architecture and device type; installs autostart (`systemd` / `launchd` / Windows task / runit + Termux:Boot on Android); copies your connection link to the clipboard. |
|
|
58
|
-
| 🛡️ **Token-gated** | Every call needs a personal 128-bit token (`?token=…` or `Authorization: Bearer`). Everything else gets HTTP 401. |
|
|
59
|
-
| 🔁 **Many machines, one gateway** | Each node is addressed by `?user=<node-name>` on one shared domain. |
|
|
60
|
-
| 🆓 **Free & open source** | MIT license, works with the free Gemini tier. |
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## 🛠️ What Gemini can do on your machine (MCP tools)
|
|
65
|
-
|
|
66
|
-
**26 tools.** Every one of them runs on your machine under your own user account — the gateway only carries the calls.
|
|
67
|
-
|
|
68
|
-
### Which calls ask for your confirmation
|
|
69
|
-
|
|
70
|
-
Gemini Spark decides whether to stop and ask *"confirm this action?"* from the **MCP annotation hints** each tool advertises (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`). A tool advertised with no hints at all is treated as destructive by the spec's defaults — that is why a server that declares none gets a confirmation prompt on *every single call*. Here the hints are explicit:
|
|
71
|
-
|
|
72
|
-
| Class | Tools | Confirmation |
|
|
73
|
-
| :--- | :--- | :--- |
|
|
74
|
-
| Read-only | `mesh_status`, `system_info`, `system_vitals`, `get_orchestration_skill`, `list_dir`, `read_file`, `grep_search`, `glob_find`, `job_output`, `job_list`, `share_list`, `device_info` | never asked |
|
|
75
|
-
| Local work — commands, builds, file writes, jobs, shares, device actions | `bash_exec`, `run_job`, `write_file`, `edit_file`, `job_kill`, `share_file`, `serve_dir`, `device_control` | not asked |
|
|
76
|
-
| **Sensitive — observes the physical world or a person's private data** | `device_capture` (camera, microphone, location, fingerprint, USB, infrared), `device_messages` (SMS, call log, contacts, calls) | **asked first** |
|
|
77
|
-
| **Confirmed — install/delete, `sudo`, system paths** | `system_change` (installs/removes software, deletes data, runs `sudo`, edits system paths by shell), `system_write` (writes file content into a system path), `mesh_update` (installs a release), `unshare` (revokes and deletes the published copy) | **asked first** |
|
|
78
|
-
|
|
79
|
-
`device_capture` and `device_messages` delete nothing, yet they are advertised with `destructiveHint`. That hint is the only one clients such as Gemini Spark reliably turn into a *"confirm this action?"* prompt, and a camera, a microphone or an SMS list that fires silently is worse than an honest over-classification. They were put in that class deliberately. `device_control` stays ordinary local work for the same reason: marking a torch or a vibrate as destructive would only train you to click through the prompts that matter.
|
|
80
|
-
|
|
81
|
-
Four classes are gated, and the gateway — not the hint — enforces them, because a hint is static per tool and cannot tell `ls` from `apt install`:
|
|
82
|
-
|
|
83
|
-
1. **Installing or removing software** — package managers, uninstallers, `msiexec`, `dpkg`/`rpm`, `curl … | bash`, `iwr … | iex`.
|
|
84
|
-
2. **Deleting data** — `rm`, `Remove-Item`, `del`, `rd`, `shred`, `dd`, `mkfs`, `git clean -fd`, `docker rm`/`prune`, `truncate -s 0`.
|
|
85
|
-
3. **Running as root** — `sudo`, `doas`, `pkexec`, `runas`, `Start-Process -Verb RunAs`.
|
|
86
|
-
4. **System paths** — `/etc`, `/usr`, `/boot`, `/var/lib`, `/opt`, `/dev`, `C:\Windows`, `C:\Program Files`, `ProgramData`, the registry. Reads of those paths (`cat /etc/os-release`, `systemctl status`) are *not* gated, and neither is ordinary work in your own directories.
|
|
87
|
-
|
|
88
|
-
`bash_exec`/`run_job` refuse such commands and tell the model to re-issue them as `system_change`; `write_file`/`edit_file` refuse a system path and point at `system_write` (which takes the exact content, so there is no shell quoting to get wrong). The gate is moved, not removed — inspection, builds, tests, config edits in your projects and the like still run without a dialog.
|
|
89
|
-
|
|
90
|
-
The tool surface and the confirmation policy live in [gateway.py](gateway.py) (annotations, `INSTALL_COMMAND_PATTERNS` / `DELETE_COMMAND_PATTERNS`, `SYSTEM_PATH_RE`, `classify_command()`) and in [core/mcp_tools.py](core/mcp_tools.py) (`TOOL_ANNOTATIONS` for the node's own surface, which also carries the device branch's four tools); tests compare the two surfaces and pin the classifier, so a gated command cannot slip through.
|
|
91
|
-
|
|
92
|
-
> [!NOTE]
|
|
93
|
-
> The classifier matches a verb at the start of a command segment (after `sudo`/`env`/`timeout`/`powershell -Command` prefixes), so `echo "rm -rf /"` or `grep rm notes.txt` still run, while `curl … | bash` counts as an install. It is a UX gate, not a sandbox: for hard guarantees independent of any prompt use the node's own switches — `MESH_READ_ONLY=1` and `MESH_WRITE_ROOTS` ([core/agent.py](core/agent.py)).
|
|
94
|
-
|
|
95
|
-
### Status and host information
|
|
96
|
-
|
|
97
|
-
| Tool | What it does |
|
|
98
|
-
| :--- | :--- |
|
|
99
|
-
| `mesh_status()` | Confirms the node is reachable, with live evidence from the host itself: what kind of machine answered (`device_class`, `scenario`) and its `battery_percent`. Call it first if the machine looks offline. |
|
|
100
|
-
| `system_info()` | One-call host summary: OS, desktop, user, home, disks, memory, load, top processes, the current wallpaper, `command_shell` — the shell `bash_exec` will actually use — and the device blocks: battery and charging, network and signal, language, time and timezone, CPU, RAM, storage, cameras, microphones and sensors. |
|
|
101
|
-
| `system_vitals()` | CPU, RAM and disk metrics, plus the battery block and the thermal sensors the platform exposes. |
|
|
102
|
-
|
|
103
|
-
### Shell
|
|
104
|
-
|
|
105
|
-
| Tool | What it does |
|
|
106
|
-
| :--- | :--- |
|
|
107
|
-
| `bash_exec(command, timeout_sec, max_chars, cursor)` | Runs a shell command. The shell matches the **host**, not the tool's name — `bash` on Linux/macOS, PowerShell or `cmd.exe` on Windows; check `command_shell` from `system_info()` first. Output is paginated: when it is cut, call again with `cursor=next_cursor`, nothing is dropped; output above 2 MB is spooled to a file returned in `saved_to`. `timeout_sec` is 1–120 (default 25). Installs/removals, `sudo` and system paths are **refused here** and have to go through `system_change`. |
|
|
108
|
-
| `system_change(command, timeout_sec, background, cwd, max_chars, cursor)` | Installs, removes or deletes on the host, runs privileged commands and edits system paths — the confirmed twin of `bash_exec`, and the only tool allowed to run `apt`/`dnf`/`pacman`/`pip`/`npm`/`winget`/`msiexec`/`rm`/`Remove-Item`/`sudo` or disk tools. It is advertised as destructive, so Gemini asks you first. `background=true` runs it as a job (for long installs) and returns a `job_id` for `job_output`; `cwd` applies to that mode. |
|
|
109
|
-
|
|
110
|
-
### Files
|
|
111
|
-
|
|
112
|
-
| Tool | What it does |
|
|
113
|
-
| :--- | :--- |
|
|
114
|
-
| `list_dir(path)` | Lists files and directories (workspace by default). |
|
|
115
|
-
| `read_file(path, start_line, end_line, max_chars, cursor)` | Reads a text file with line numbers, optionally restricted to a line range. Paginated like `bash_exec`. |
|
|
116
|
-
| `write_file(path, content, create_dirs, mode)` | Atomically creates or overwrites a file (temp file + `os.replace`). `mode` is POSIX-only: on Windows it is not honoured, and the result says so instead of pretending. A path inside the system is **refused** and has to go through `system_write`. |
|
|
117
|
-
| `edit_file(path, old_string, new_string, expected_sha256, replace_all)` | Replaces an exact substring. `old_string` must match exactly once unless `replace_all` is set; `expected_sha256` guards against overwriting a file that changed since it was read. System paths are refused — read the file and send the whole new content to `system_write`. |
|
|
118
|
-
| `system_write(path, content, create_dirs, mode)` | Writes a file inside a system path (`/etc`, `/usr`, `/boot`, `/var/lib`, `C:\Windows`, `C:\Program Files`, `ProgramData`, the registry) — the confirmed twin of `write_file` and the only way to write there. Gemini asks you first. Content is exact, so there is no shell quoting to get wrong. |
|
|
119
|
-
|
|
120
|
-
### Search
|
|
121
|
-
|
|
122
|
-
| Tool | What it does |
|
|
123
|
-
| :--- | :--- |
|
|
124
|
-
| `grep_search(pattern, path, glob, limit, ignore_case, fixed, context)` | Recursively searches file contents, skipping binary files and heavy directories. `fixed` treats the pattern as literal text, `context` adds surrounding lines, `limit` is 1–1000 (default 200). |
|
|
125
|
-
| `glob_find(pattern, path)` | Finds files by glob pattern (`*` and `**`). |
|
|
126
|
-
|
|
127
|
-
### Long-running work
|
|
128
|
-
|
|
129
|
-
| Tool | What it does |
|
|
130
|
-
| :--- | :--- |
|
|
131
|
-
| `run_job(command, cwd)` | Starts a command in the background and returns a `job_id`. On an Android node it takes the wake lock for the lifetime of the job, so Android does not freeze it when the screen goes off. |
|
|
132
|
-
| `job_output(job_id, wait_ms, max_chars, cursor)` | Reads a job's output, optionally waiting up to 20 s for completion. Paginated. |
|
|
133
|
-
| `job_kill(job_id, signal)` | Terminates a job. `signal` is `TERM` (default), `KILL`, `INT`, `HUP` or `QUIT`. |
|
|
134
|
-
| `job_list(limit)` | Lists recent jobs, newest first (20 by default, max 50), including whether each one holds a `wake_lock`. |
|
|
135
|
-
|
|
136
|
-
### Device (phone, laptop or server)
|
|
137
|
-
|
|
138
|
-
These four tools read and act on the device the node itself runs on. They are advertised on **every** node — a phone-only action on a laptop answers with `available: false`, the reason and, where one exists, the fix, instead of failing.
|
|
139
|
-
|
|
140
|
-
| Tool | What it does |
|
|
141
|
-
| :--- | :--- |
|
|
142
|
-
| `device_info(section, sensor, fresh, quick)` | One-call device report: battery and charging, network interfaces with Wi-Fi and cellular signal, language, time and timezone, CPU, RAM, storage, cameras, microphones, sensors and what the platform can actually reach. `section` is `summary`, `all` (default) or one of `device`, `battery`, `network`, `locale`, `time`, `hardware`, `storage`, `cameras`, `microphones`, `sensors`, `capabilities`. `sensor` takes one live sample from a named sensor (Android; the name comes from the `sensors` block), `fresh` bypasses the few-second cache and `quick` skips the slow sections. Every block carries `available` and `source`; a block the platform cannot answer says why instead of showing a zero. |
|
|
143
|
-
| `device_control(action, on, value, stream, text, title, id, url, path, state, timeout_sec)` | Twenty reversible actions: `torch`, `vibrate`, `volume`, `volume_get`, `brightness`, `tts_speak`, `toast`, `notify`, `notify_list`, `notify_remove`, `clipboard_get`, `clipboard_set`, `media`, `media_scan`, `wakelock`, `download`, `open`, `share`, `dialog`, `wallpaper`. `value` is milliseconds for `vibrate`, a 0–15 level for `volume` and 0–255 for `brightness`; `stream` picks the audio stream (`music` by default); `text` is the body for TTS, toast, notification, clipboard and dialog, and `play\|pause\|stop\|info` for `media`. Nothing here is destructive. |
|
|
144
|
-
| `device_capture(action, camera_id, path, seconds, provider, frequency, pattern)` | Eleven actions: `camera_list`, `camera_photo`, `mic_record_start`, `mic_record_stop`, `mic_record_status`, `location`, `fingerprint`, `usb_list`, `usb_access`, `infrared_frequencies`, `infrared_transmit`. **Asks first.** `path` is the output file for `camera_photo` and `mic_record_start` (default: the node's capture directory) and the device path from `usb_list` for `usb_access`; `camera_id` comes from `camera_list`, `seconds` limits a recording, `provider` is `gps`, `network` or `passive`, and `frequency`/`pattern` drive `infrared_transmit`. A photo or a recording stays in the capture directory and is never uploaded by itself — `share_file` publishes it only when you ask for that. `fingerprint` returns only the verdict; `usb_access` asks Android for permission and returns the descriptor but deliberately refuses to run a program for you. |
|
|
145
|
-
| `device_messages(action, number, text, limit, offset, type, query)` | Five actions: `sms_list`, `sms_send`, `call_log`, `contacts`, `call`. **Asks first, and is off until the operator enables it with `MESH_DEVICE_PIM=1`** — it is the private data of whoever holds the phone. `limit` is 1–50 (default 10), `offset` pages through the list, `type` filters it and `query` filters contacts on the node. |
|
|
146
|
-
|
|
147
|
-
Example:
|
|
148
|
-
|
|
149
|
-
```text
|
|
150
|
-
"Check the phone's battery and signal."
|
|
151
|
-
→ device_info(section="summary")
|
|
152
|
-
|
|
153
|
-
"Turn on the torch."
|
|
154
|
-
→ device_control(action="torch", on=true)
|
|
155
|
-
|
|
156
|
-
"Take a photo and share it with me."
|
|
157
|
-
→ device_capture(action="camera_photo") # asks for confirmation
|
|
158
|
-
→ share_file(path=<the path it returned>)
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Device switches (operator)
|
|
162
|
-
|
|
163
|
-
Read from `agent.env` at startup ([core/agent.py](core/agent.py), applied in [core/mcp_tools.py](core/mcp_tools.py) and [core/device.py](core/device.py)):
|
|
164
|
-
|
|
165
|
-
| Variable | Default | What it does |
|
|
166
|
-
| :--- | :--- | :--- |
|
|
167
|
-
| `MESH_DEVICE` | `auto` | `auto` keeps the branch answering everywhere (only the answer differs per platform), `0` switches the branch off, `1` forces it on. |
|
|
168
|
-
| `MESH_DEVICE_ACTIONS` | empty | Comma-separated allowlist of action names for `device_control`, `device_capture` and `device_messages`; empty means every action the build implements. |
|
|
169
|
-
| `MESH_DEVICE_CAPTURE_DIR` | shared storage on a phone once `termux-setup-storage` has been granted (`~/storage/dcim/antigravity-mesh` or `~/storage/shared/AntigravityMesh`), otherwise `~/.cache/antigravity-mesh/captures` | Where photos and recordings are written. |
|
|
170
|
-
| `MESH_DEVICE_PIM` | `0` | `1` enables SMS, the call log, contacts and placing a call. |
|
|
171
|
-
| `MESH_DEVICE_QUICK` | `0` | `1` keeps `system_info` instant by skipping network, cameras, microphones and sensors. |
|
|
172
|
-
| `MESH_READ_ONLY` | `0` | `1` refuses every `device_control` and `device_capture` action, and blocks `sms_send` and `call`. This is the node-side guarantee that does not depend on any client prompt. |
|
|
173
|
-
|
|
174
|
-
### Publishing files and web pages
|
|
175
|
-
|
|
176
|
-
These four make something on your machine readable from the internet. The link is served by the gateway on the shared domain, so **the node has to stay connected for it to work**.
|
|
177
|
-
|
|
178
|
-
| Tool | What it does |
|
|
179
|
-
| :--- | :--- |
|
|
180
|
-
| `share_file(path, name, overwrite)` | Publishes **one file**: it is copied into the node's share root and you get `https://<shared-domain>/<node>/<name>-<random>/<filename>`. `overwrite` replaces an existing share with the same name. |
|
|
181
|
-
| `serve_dir(path, name)` | Serves a **directory** in place — no copy is made. `index.html` is used when present, otherwise a directory listing is shown. Read-only (GET/HEAD only). Returns `https://<shared-domain>/<node>/<name>-<random>/`. |
|
|
182
|
-
| `share_list()` | Lists active shares with their URLs, their roots, and whether the local server is running. |
|
|
183
|
-
| `unshare(name)` | Stops a share and revokes its link. Accepts the share name, its slug or the full URL. A file share also deletes the copy in the share root; a served directory is left untouched on disk. |
|
|
184
|
-
|
|
185
|
-
> [!IMPORTANT]
|
|
186
|
-
> The random part of the path **is** the credential — anyone who has the link can read the file or browse the directory. Treat a share link like a password, and `unshare` it when you are done. Size limits: 32 MiB per file by default, 64 MiB ceiling via `MESH_WEB_MAX_BYTES`.
|
|
187
|
-
|
|
188
|
-
### Agent behaviour
|
|
189
|
-
|
|
190
|
-
| Tool | What it does |
|
|
191
|
-
| :--- | :--- |
|
|
192
|
-
| `get_orchestration_skill()` | Loads the agent's current operating rules and orchestration skill. |
|
|
193
|
-
|
|
194
|
-
### Keeping the node up to date
|
|
195
|
-
|
|
196
|
-
| Tool | What it does |
|
|
197
|
-
| :--- | :--- |
|
|
198
|
-
| `mesh_update(action, force, offline, restart)` | Reports, checks for or installs a newer release of the node's own code. `status` reads local state (no network), `check` asks GitHub, `apply` downloads the payload, verifies its published SHA-256, installs it with a rollback backup and restarts the agent. |
|
|
199
|
-
|
|
200
|
-
> [!TIP]
|
|
201
|
-
> Nodes update themselves: a running agent checks for a newer release in the background (every 6 hours by default) and installs what it finds, and on Windows the `AntigravityMeshUpdater` task checks once a day even when no agent is running. `MESH_UPDATE_AUTO=0` switches that to "report only". Details, environment variables and rollback: [docs/UPDATES.md](docs/UPDATES.md).
|
|
202
|
-
|
|
203
|
-
> [!TIP]
|
|
204
|
-
> Gemini gives a single tool call roughly 30 seconds. For anything longer (builds, backups, downloads) the agent uses `run_job` and polls `job_output`, so tasks never get cut off.
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
## 🚀 Quick start — 2 minutes
|
|
209
|
-
|
|
210
|
-
### 1. Install the node on the PC you want to control
|
|
211
|
-
|
|
212
|
-
**🐧 Linux / 🍎 macOS**
|
|
213
|
-
```bash
|
|
214
|
-
curl -fsSL https://racknerd-5a24bf9.merino-carob.ts.net/install.sh | bash
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
**📱 Android (Termux)** — no root, works from F-Droid's Termux
|
|
218
|
-
```bash
|
|
219
|
-
curl -fsSL https://racknerd-5a24bf9.merino-carob.ts.net/install.sh | bash
|
|
220
|
-
```
|
|
221
|
-
Autostart on a phone is a runit service plus the Termux:Boot app, and the node is
|
|
222
|
-
named after the device model — see [docs/TERMUX.md](docs/TERMUX.md).
|
|
223
|
-
|
|
224
|
-
**🪟 Windows (PowerShell)**
|
|
225
|
-
```powershell
|
|
226
|
-
irm https://racknerd-5a24bf9.merino-carob.ts.net/install.ps1 | iex
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
**🪟 Windows — visual installer** (from a clone or a release)
|
|
230
|
-
|
|
231
|
-
```powershell
|
|
232
|
-
.\install-gui.cmd
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
A WinForms wizard that mirrors the console installer's variants, switches language at
|
|
236
|
-
runtime and shows the MCP link at the end — see [docs/GUI_INSTALLER.md](docs/GUI_INSTALLER.md).
|
|
237
|
-
|
|
238
|
-
**📦 Node.js (any OS)**
|
|
239
|
-
```bash
|
|
240
|
-
npx gemini-computer-use
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
**🛠️ From source**
|
|
244
|
-
```bash
|
|
245
|
-
git clone https://github.com/LevRa7/Computer-use-for-Gemini-App-Web.git
|
|
246
|
-
cd Computer-use-for-Gemini-App-Web
|
|
247
|
-
./install.sh --quick
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
At the end the installer prints your personal MCP link and copies it to the clipboard:
|
|
251
|
-
```text
|
|
252
|
-
https://racknerd-5a24bf9.merino-carob.ts.net/sse?user=<your-node-name>&token=<your_secret_token>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### 2. Connect it to Gemini Spark
|
|
256
|
-
|
|
257
|
-
1. Open **[gemini.google.com/spark/apps](https://gemini.google.com/spark/apps)** in a browser or in the Gemini mobile app.
|
|
258
|
-
2. Tap **Add App** (or **Settings ⚙️ → Tools / Extensions (MCP)**).
|
|
259
|
-
3. Paste the link (**Ctrl+V**) and save.
|
|
260
|
-
|
|
261
|
-
### 3. Give it a task
|
|
262
|
-
|
|
263
|
-
> *"Show system_vitals and list the 5 biggest folders in my home directory."*
|
|
264
|
-
|
|
265
|
-
That's it — Gemini is now an agent running on your machine. Repeat step 1 on other computers to control all of them from the same phone.
|
|
266
|
-
|
|
267
|
-
---
|
|
268
|
-
|
|
269
|
-
## 🖥️ Deployment modes
|
|
270
|
-
|
|
271
|
-
| Mode | Command | When to use |
|
|
272
|
-
| :--- | :--- | :--- |
|
|
273
|
-
| **Gateway + tunnel** (recommended) | `./install.sh --quick` | Home PCs and laptops behind NAT — the mobile-agent scenario. |
|
|
274
|
-
| **Remote over SSH** | `./install.sh --ssh=user@host` | Deploy a node onto a remote Linux server from your terminal. |
|
|
275
|
-
| **Local standalone** | `./install.sh --mode=standalone --port=8096` | Pure localhost FastMCP server at `http://localhost:8096/sse`, no cloud relay. |
|
|
276
|
-
|
|
277
|
-
On Windows the same variants are available in the visual installer (`.\install-gui.cmd`); local standalone stays console-only there (`.\install.ps1 -Mode standalone -Port 8096`).
|
|
278
|
-
|
|
279
|
-
On **Android/Termux** the same `./install.sh` variants work; autostart is a runit service plus the Termux:Boot app instead of systemd, and standalone stays reachable from that phone alone — see [docs/TERMUX.md](docs/TERMUX.md).
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
## 🪟 Windows installation
|
|
284
|
-
|
|
285
|
-
Windows already ships PowerShell 5.1, so there is nothing to prepare — no Python, no Node.js, no administrator rights. Both installers finish by putting the same MCP link on the clipboard.
|
|
286
|
-
|
|
287
|
-
| | Console installer | Visual installer |
|
|
288
|
-
| :--- | :--- | :--- |
|
|
289
|
-
| Start | `irm https://racknerd-5a24bf9.merino-carob.ts.net/install.ps1 \| iex` | `.\install-gui.cmd` |
|
|
290
|
-
| Needs | nothing but PowerShell | a clone or an unpacked release — it drives `install.ps1`, `core/` and `install.sh` |
|
|
291
|
-
| Variants | `-Mode tunnel` (default), `-Mode standalone`, `-User`, `-Gateway`, `-Token`, `-Port`, `-DryRun` | Quick setup, Custom setup, Remote over SSH |
|
|
292
|
-
| Language | `-Lang en` / `-Lang ru` | switch in the window header, or `-Lang` |
|
|
293
|
-
|
|
294
|
-
### Automatic dependencies and architecture
|
|
295
|
-
|
|
296
|
-
Nothing has to be prepared by hand first. Both installers obtain what they need, and if
|
|
297
|
-
they cannot, they stop with an explicit message instead of writing an autostart entry that
|
|
298
|
-
can never work.
|
|
299
|
-
|
|
300
|
-
| | Windows (`install.ps1`) | Linux / macOS (`install.sh`) |
|
|
301
|
-
| :--- | :--- | :--- |
|
|
302
|
-
| Python | `winget` → the python.org installer for **this** architecture → `uv` | `apt` / `dnf` / `yum` / `zypper` / `pacman` / `apk` / `xbps` / `brew` → `uv` |
|
|
303
|
-
| `websockets` | `pip` → `pip --user` → `ensurepip` → `uv` → a venv | `pip` → `pip --break-system-packages` → `pip --user` → `ensurepip` → `uv` → a venv |
|
|
304
|
-
| If all of that fails | stops, and prints the exact command to run | stops, and prints the exact command to run |
|
|
305
|
-
|
|
306
|
-
`websockets` is the only external Python requirement: `core/mcp_tools.py` and
|
|
307
|
-
`core/server.py` are standard library only. `uv` is fetched only when the cheaper paths
|
|
308
|
-
have already failed.
|
|
309
|
-
|
|
310
|
-
**The architecture decides which build is downloaded.** The installer asks the *OS*, not
|
|
311
|
-
the process, because a 32-bit PowerShell on 64-bit Windows reports `x86` and hides the
|
|
312
|
-
real value in `PROCESSOR_ARCHITEW6432`, and an emulated x64 process on ARM64 reports
|
|
313
|
-
`AMD64`:
|
|
314
|
-
|
|
315
|
-
| Machine | Python build | `uv` archive |
|
|
316
|
-
| :--- | :--- | :--- |
|
|
317
|
-
| Windows x64 | `python-3.12.5-amd64.exe` | `x86_64-pc-windows-msvc` |
|
|
318
|
-
| Windows ARM64 | `python-3.12.5-arm64.exe` | `aarch64-pc-windows-msvc` |
|
|
319
|
-
| Windows 32-bit | `python-3.12.5.exe` | — (uv ships no 32-bit Windows build) |
|
|
320
|
-
| Linux `x86_64` | the package manager's own build | `x86_64-unknown-linux-gnu` |
|
|
321
|
-
| Linux `aarch64` | the package manager's own build | `aarch64-unknown-linux-gnu` |
|
|
322
|
-
| Linux `armv7l` / `i686` | the package manager's own build | `armv7-unknown-linux-gnueabihf` / `i686-unknown-linux-gnu` |
|
|
323
|
-
| macOS `arm64` / `x86_64` | `brew`, or `uv` | `aarch64-apple-darwin` / `x86_64-apple-darwin` |
|
|
324
|
-
|
|
325
|
-
The interpreter that ends up pinned is the one that actually has `websockets` — which can
|
|
326
|
-
be a venv or a `uv`-managed CPython, both deliberately outside `PATH`. `install.ps1 -DryRun`
|
|
327
|
-
and `install.sh --dry-run` report the architecture without changing anything, and the visual
|
|
328
|
-
installer shows it in its preflight.
|
|
329
|
-
|
|
330
|
-
### Download the setup executable
|
|
331
|
-
|
|
332
|
-
The release also ships a single compiled installer, for machines where you would rather
|
|
333
|
-
not clone or download anything else — `AntigravityMesh-Setup-<version>.exe`:
|
|
334
|
-
|
|
335
|
-
```powershell
|
|
336
|
-
.\AntigravityMesh-Setup-0.4.3.exe # open the visual installer
|
|
337
|
-
.\AntigravityMesh-Setup-0.4.3.exe -Lang ru # start in Russian
|
|
338
|
-
.\AntigravityMesh-Setup-0.4.3.exe -SelfTest # headless self-check, prints JSON
|
|
339
|
-
.\AntigravityMesh-Setup-0.4.3.exe --version # print the version
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
It carries the wizard, `install.ps1`, `core/` and `install.sh` inside itself, unpacks them
|
|
343
|
-
to `%LOCALAPPDATA%\AntigravityMesh\setup\<version>` (override with `MESH_SETUP_DIR`) and
|
|
344
|
-
runs the wizard from there. It needs nothing but the .NET Framework and PowerShell that
|
|
345
|
-
Windows already has, and it is built from this repository by
|
|
346
|
-
`.\build-installer-exe.ps1` — no SDK, no NuGet, no network.
|
|
347
|
-
|
|
348
|
-
It is **not code-signed**, so SmartScreen may ask for confirmation the first time you run
|
|
349
|
-
it.
|
|
350
|
-
|
|
351
|
-
### Visual installer
|
|
352
|
-
|
|
353
|
-

|
|
354
|
-
|
|
355
|
-
- **Quick setup** (recommended) — node name = PC name, shared gateway domain, Windows autostart. The one to pick on a home PC or laptop.
|
|
356
|
-
- **Custom setup** — your own node name, your own shared domain, optional token.
|
|
357
|
-
- **Remote over SSH** — copy the node code to a Linux host and run `install.sh` there. Needs the Windows *OpenSSH Client* feature and key-based authentication; interactive password prompts are not supported.
|
|
358
|
-
|
|
359
|
-
The first screen runs `install.ps1 -DryRun` and reports what it found — Python, `websockets`, the resolved gateway domain, the config directory, the autostart path — without changing anything. Before starting, the window shows the exact command it will run; during the run it streams the installer output; at the end it shows the MCP link with a copy button, the three-step Gemini instructions and the paths it created.
|
|
360
|
-
|
|
361
|
-
Full details, screenshots and the SSH notes: [docs/GUI_INSTALLER.md](docs/GUI_INSTALLER.md).
|
|
362
|
-
|
|
363
|
-
### What a Windows installation creates
|
|
364
|
-
|
|
365
|
-
| Path | What it is |
|
|
366
|
-
| :--- | :--- |
|
|
367
|
-
| `%USERPROFILE%\.config\antigravity-mesh\agent.env` | `MESH_GATEWAY`, `MESH_USER`, `MESH_TOKEN` |
|
|
368
|
-
| `%USERPROFILE%\.config\antigravity-mesh\domain.env` | `MESH_PUBLIC_URL` — the resolved shared domain, so the node's share links, its tunnel and the gateway name the same host (not written when the built-in default is all that is configured) |
|
|
369
|
-
| `…\Start Menu\Programs\Startup\antigravity-agent.vbs` | autostart entry, so the node comes back after a reboot |
|
|
370
|
-
| `%USERPROFILE%\.config\antigravity-mesh\agent.log` | agent output, for when a silent autostart fails |
|
|
371
|
-
|
|
372
|
-
### Language
|
|
373
|
-
|
|
374
|
-
The visual installer starts in the language of your Windows language settings — the display language and the preferred-languages list are read from the registry — and falls back to English. `-Lang ru` / `-Lang en` overrides the detection. The switch in the window header changes every label immediately.
|
|
375
|
-
|
|
376
|
-
### Troubleshooting
|
|
377
|
-
|
|
378
|
-
- **"No gateway domain configured"** — this copy was not published with a domain. Pass one: use *Custom setup* in the wizard, or `.\install.ps1 -Gateway <shared-domain>`, or set `MESH_PUBLIC_URL`.
|
|
379
|
-
- **Registration fails** — the installer stops *before* writing anything when the gateway cannot be reached. Check that the domain resolves, then retry with an explicit `-Gateway`.
|
|
380
|
-
- **`python` opens the Microsoft Store** — that is the App Execution Alias, not an interpreter. Both installers resolve a real `python.exe` by full path and ignore the alias; the wizard never pins an alias into the autostart entry.
|
|
381
|
-
- **The node does not come back after a reboot** — run `.\ops\doctor.ps1`: it prints the autostart interpreter, the heartbeat age, the last `agent.log` lines (including a torn final line) and the gateway's own view of this node. `.\ops\windows\agent-watchdog.ps1` performs that check once and restarts the agent; the scheduled task `AntigravityMeshWatchdog` runs it every five minutes, so a dead agent comes back on its own.
|
|
382
|
-
|
|
383
|
-
### Standalone on Windows
|
|
384
|
-
|
|
385
|
-
The wizard does not offer it; use the console installer:
|
|
386
|
-
|
|
387
|
-
```powershell
|
|
388
|
-
.\install.ps1 -Mode standalone -Port 8096
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
That starts a local-only FastMCP server at `http://localhost:8096/sse` and puts that URL on the clipboard.
|
|
392
|
-
|
|
393
|
-
---
|
|
394
|
-
|
|
395
|
-
## 📱 Android installation (Termux)
|
|
396
|
-
|
|
397
|
-
The same `install.sh` installs a node on an unrooted phone or tablet, and the phone
|
|
398
|
-
then shows up in Gemini Spark like any other machine. Full guide:
|
|
399
|
-
**[docs/TERMUX.md](docs/TERMUX.md)**.
|
|
400
|
-
|
|
401
|
-
```bash
|
|
402
|
-
# 1. Termux from F-Droid (not Google Play); optionally Termux:Boot and Termux:API
|
|
403
|
-
pkg update -y
|
|
404
|
-
# 2. the usual one-liner
|
|
405
|
-
curl -fsSL https://racknerd-5a24bf9.merino-carob.ts.net/install.sh | bash
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
What a phone changes, and how the installer handles it:
|
|
409
|
-
|
|
410
|
-
| | |
|
|
411
|
-
| :--- | :--- |
|
|
412
|
-
| **Packages** | `pkg` instead of `apt`, `python`/`python-pip` instead of `python3-pip`/`python3-venv` — a phone is never root and has no `sudo`. |
|
|
413
|
-
| **Node name** | The device model (`Pixel 7 Pro` → `pixel7pro`): Android answers `localhost` to every app, and the gateway keeps one tunnel per name. |
|
|
414
|
-
| **Domain file** | `~/.config/antigravity-mesh/domain.env` — there is no `/etc` to write to. The installer records the resolved shared domain there, so the phone's share links, its tunnel and the gateway name the same host (the built-in default is never recorded). |
|
|
415
|
-
| **Autostart** | A **runit** service (`termux-services`, the `Restart=always` equivalent) plus a **Termux:Boot** script that takes the wake lock after a reboot. |
|
|
416
|
-
| **Clipboard** | `termux-clipboard-set` (Termux:API), so the MCP link goes straight into the Gemini app. |
|
|
417
|
-
|
|
418
|
-
> [!IMPORTANT]
|
|
419
|
-
> Two Android-side switches are yours to flip: install **Termux:Boot** and open it
|
|
420
|
-
> once, and set battery optimisation to *Unrestricted* for Termux. Without them
|
|
421
|
-
> Android unloads the node with the screen off.
|
|
422
|
-
|
|
423
|
-
The phone must also be able to **resolve the gateway name**. A gateway that lives on
|
|
424
|
-
a private network (Tailscale, a VPN, a DNS override on your laptop) resolves there and
|
|
425
|
-
nowhere else — mobile data resolves nothing private. Put the phone on that network, or
|
|
426
|
-
give the gateway a publicly resolvable domain; the installer detects a name it cannot
|
|
427
|
-
resolve and says so before writing anything: see
|
|
428
|
-
[docs/TERMUX.md](docs/TERMUX.md#private-gateway-tailscale--vpn).
|
|
429
|
-
|
|
430
|
-
```bash
|
|
431
|
-
sv status agy-agent # is it up?
|
|
432
|
-
sv restart agy-agent # restart now
|
|
433
|
-
tail -f $PREFIX/var/log/sv/agy-agent/current
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
`--mode=standalone` also works, but it serves `127.0.0.1` only — reachable from
|
|
437
|
-
inside that phone alone. Use the default tunnel mode to drive the phone from Gemini.
|
|
438
|
-
|
|
439
|
-
---
|
|
440
|
-
|
|
441
|
-
## 🔄 Automatic updates
|
|
442
|
-
|
|
443
|
-
A node installs itself from a GitHub release and then keeps itself current the same
|
|
444
|
-
way. You do not re-install anything by hand, and nothing unverified is installed.
|
|
445
|
-
|
|
446
|
-
- **It checks by itself.** A running agent asks the GitHub Releases API every 6
|
|
447
|
-
hours (`MESH_UPDATE_CHECK_INTERVAL`), compares the tag with the version in
|
|
448
|
-
`core/version.py` and installs what it finds. On Windows the daily
|
|
449
|
-
`AntigravityMeshUpdater` task does the same for a machine whose agent is not
|
|
450
|
-
running. `MESH_UPDATE_AUTO=0` turns installing off and keeps reporting.
|
|
451
|
-
- **It verifies before it installs.** The payload is downloaded, its SHA-256 is
|
|
452
|
-
compared with the published checksum, and only then are `core/`, `skills/` and
|
|
453
|
-
`ops/` replaced. No checksum, no update.
|
|
454
|
-
- **It can always go back.** The replaced directories are saved under
|
|
455
|
-
`%USERPROFILE%\.config\antigravity-mesh\backups\<version>-<timestamp>\`, and an
|
|
456
|
-
update interrupted by a kill or a power cut is rolled back automatically on the
|
|
457
|
-
next start.
|
|
458
|
-
- **The node comes back new.** The agent is restarted through whatever supervises
|
|
459
|
-
it (the watchdog task on Windows, `systemctl --user restart agy-agent.service`,
|
|
460
|
-
`launchctl kickstart -k`) — or started directly when nothing supervises it.
|
|
461
|
-
|
|
462
|
-
By hand, any time:
|
|
463
|
-
|
|
464
|
-
```powershell
|
|
465
|
-
gemini-computer-use update -Check # is a newer release published?
|
|
466
|
-
gemini-computer-use update # install it now and restart
|
|
467
|
-
gemini-computer-use update -Check -Json
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
From Gemini: ask the node to run `mesh_update` (`status`, then `check`, then
|
|
471
|
-
`apply`).
|
|
472
|
-
|
|
473
|
-
The state of the last check is in the heartbeat, so `ops/doctor.ps1` shows it, and
|
|
474
|
-
the full history is in `%USERPROFILE%\.config\antigravity-mesh\update.log`.
|
|
475
|
-
Configuration, exit codes, rollback and troubleshooting: [docs/UPDATES.md](docs/UPDATES.md).
|
|
476
|
-
|
|
477
|
-
---
|
|
478
|
-
|
|
479
|
-
## 🔗 One shared domain (canonical URL contract)
|
|
480
|
-
|
|
481
|
-
A node is **never** addressed by its own hostname. The gateway publishes a single shared domain, and the node name travels as a query parameter:
|
|
482
|
-
|
|
483
|
-
| Purpose | Canonical URL |
|
|
484
|
-
| :--- | :--- |
|
|
485
|
-
| MCP over SSE | `https://<shared-domain>/sse?user=<node-name>&token=<token>` |
|
|
486
|
-
| MCP Streamable HTTP | `https://<shared-domain>/mcp?user=<node-name>&token=<token>` |
|
|
487
|
-
| Reverse tunnel (agent) | `wss://<shared-domain>/ws/tunnel?user=<node-name>&token=<token>` |
|
|
488
|
-
|
|
489
|
-
Why: every extra hostname would need its own DNS record *and* its own SAN in the TLS certificate. A node whose name is missing from the certificate fails the TLS handshake, and the Gemini client then reports an opaque "cannot connect to host". With one shared domain the certificate covers every node forever, and adding a node is only a registration call.
|
|
490
|
-
|
|
491
|
-
- The shared domain defaults to `smart-server.online`; override it with `./install.sh --domain=<shared-domain>` (node side) or `MESH_PUBLIC_URL` (gateway side).
|
|
492
|
-
- Legacy per-device subdomain URLs still resolve for backwards compatibility and log a deprecation warning; set `MESH_LEGACY_SUBDOMAIN=0` on the gateway to reject them outright.
|
|
493
|
-
- The installer now records the domain it resolved in the domain file, so the node's share links, its tunnel and the gateway all name the same host: `MESH_PUBLIC_URL=https://<shared-domain>` in `domain.env`, written in both install branches (standalone, and cloud-gateway/tunnel right after `agent.env`). The value is normalised first, the `__MESH_DOMAIN__` placeholder and an empty value are never written, the write is skipped when the file already names that host, and a file that cannot be written is not fatal — the installer prints the exact command to run by hand. The built-in default is never recorded at all: it is a fallback, not a configuration, and pinning it would stop `core/domain.py` from consulting the legacy `MESH_GATEWAY` at all, so a later change in `agent.env` would be silently ignored — nothing is lost, because with no file that same value is already the resolver's last fallback. `--dry-run` reports the file and the value it would write — including `not written (the built-in default is a fallback, not a configuration)` — and still changes nothing on disk.
|
|
494
|
-
- Why this matters: share links are built by `core/domain.py`, whose chain is `MESH_PUBLIC_URL` → `AGY_PUBLIC_BASE_URL` → the domain file → the built-in default, and which deliberately never reads the legacy `MESH_GATEWAY` that `agent.env` carries (only `gateway_host()`, the tunnel host, does). A node installed the normal way — `agent.env` with `MESH_GATEWAY`/`MESH_USER`/`MESH_TOKEN` and no domain file — therefore dialled the right gateway while minting links on the built-in default. On an already-installed node, re-run the installer or write the one line by hand: `mkdir -p ~/.config/antigravity-mesh && echo 'MESH_PUBLIC_URL=https://<domain>' > ~/.config/antigravity-mesh/domain.env`, or the same with `sudo tee /etc/antigravity-mesh/domain.env` on Linux. Existing share links keep working; new ones use the configured domain.
|
|
495
|
-
- `MESH_DOMAIN_FILE` only overrides the *path*, and it belongs to the node's own configuration rather than to the installer invocation: keep it in `agent.env` (every `MESH_*` key there is exported to the node), or the domain is written into a file the node never reads. The installer creates the parent directory of whichever file is in effect. Over `--ssh=<host>` the domain resolved here is handed to the remote `install.sh` as `--domain=`, so the target records that host instead of re-resolving it (a repository copy still carrying the `__MESH_DOMAIN__` placeholder would otherwise fall back to the built-in default).
|
|
496
|
-
|
|
497
|
-
---
|
|
498
|
-
|
|
499
|
-
## 🔒 Security
|
|
500
|
-
|
|
501
|
-
- **Token-gated endpoints** — every request must carry the node's secret token.
|
|
502
|
-
- **Outbound-only tunnel** — nodes never listen on public ports; the agent dials out to the gateway.
|
|
503
|
-
- **Gateway is a relay** — it terminates TLS and forwards calls; commands run only on your node.
|
|
504
|
-
- **Unprivileged by default** — the agent runs in user space, without root.
|
|
505
|
-
|
|
506
|
-
> [!WARNING]
|
|
507
|
-
> Whoever has your MCP link can run commands on that machine. Treat it like a password: don't publish it, and reinstall the node to rotate the token if it leaks.
|
|
508
|
-
|
|
509
|
-
---
|
|
510
|
-
|
|
511
|
-
## 🚢 Self-hosting the gateway
|
|
512
|
-
|
|
513
|
-
The gateway is a single host serving every node. Publish it only through the deploy script, so the running gateway, the served installers and the node bootstrap payload cannot drift apart:
|
|
514
|
-
|
|
515
|
-
```bash
|
|
516
|
-
MESH_GATEWAY_SSH=root@<gateway-ip> ./deploy_gateway.sh # deploy
|
|
517
|
-
MESH_GATEWAY_SSH=root@<gateway-ip> ./deploy_gateway.sh --dry-run # preview
|
|
518
|
-
MESH_GATEWAY_SSH=root@<gateway-ip> MESH_GATEWAY_SSH_PASS_FILE=~/.ssh/gw-pass \
|
|
519
|
-
./deploy_gateway.sh # password auth
|
|
520
|
-
```
|
|
521
|
-
It verifies every upload with an sha256 manifest on the host, takes a timestamped backup, installs with the right owner, restarts the service and finishes with a public health check. Nothing is installed if verification fails.
|
|
522
|
-
|
|
523
|
-
> [!TIP]
|
|
524
|
-
> **One place for the domain.** `MESH_PUBLIC_URL` in `domain.env`
|
|
525
|
-
> (`/etc/antigravity-mesh/domain.env` on Linux, `%USERPROFILE%\.config\antigravity-mesh\domain.env`
|
|
526
|
-
> on Windows) is the only setting that names it: the node, the gateway, the installers, the public
|
|
527
|
-
> file links and the nginx vhosts all resolve it through `core/domain.py`. Switching domains is one
|
|
528
|
-
> value plus `ops/nginx/render-domain.sh --apply` — see [docs/DOMAIN.md](docs/DOMAIN.md).
|
|
529
|
-
|
|
530
|
-
> [!IMPORTANT]
|
|
531
|
-
> **`install.ps1` encoding — one file, two representations.** The repository copy is UTF-8 *with* a BOM: Windows PowerShell 5.1 decodes a BOM-less script with the ANSI code page, the Russian strings then turn into smart quotes and the parser rejects the whole file, so `.\install.ps1` from a clone would not run. The copy the gateway serves is written *without* the BOM by `deploy_gateway.sh`, because `irm … | iex` receives the BOM as part of the first token and `param(...)` then stops being the first statement. nginx declares `charset utf-8` for that location, so the BOM-less copy still decodes correctly. Please keep both properties when editing the file.
|
|
532
|
-
|
|
533
|
-
**TLS certificate policy — one name, no per-device SANs.** The certificate must cover the shared domain only; a node is selected by `?user=`, never by a hostname:
|
|
534
|
-
|
|
535
|
-
```bash
|
|
536
|
-
# what the certificate currently covers
|
|
537
|
-
sudo certbot certificates | grep -A1 'Certificate Name: smart-server.online'
|
|
538
|
-
|
|
539
|
-
# canonical renewal check (staging, does not touch the live certificate)
|
|
540
|
-
sudo certbot renew --dry-run --cert-name smart-server.online
|
|
541
|
-
```
|
|
542
|
-
Legacy per-device SANs may still be present from before this contract. They are harmless and keep old bookmarked URLs working; drop them at the next renewal once no client uses subdomain URLs any more (see the `domains =` line in `/etc/letsencrypt/renewal/<name>.conf`, or re-issue with a single `-d`).
|
|
543
|
-
|
|
544
|
-
---
|
|
545
|
-
|
|
546
|
-
## 📄 License
|
|
547
|
-
|
|
548
|
-
**MIT** — see [LICENSE](LICENSE).
|
|
549
|
-
|
|
550
|
-
---
|
|
551
|
-
---
|
|
552
|
-
|
|
553
|
-
# 🇷🇺 Русская версия
|
|
554
|
-
|
|
555
|
-
> **MCP-сервер, который превращает Google Gemini из чат-бота в полноценного мобильного агента.**
|
|
556
|
-
> Напишите задачу в приложении Gemini на телефоне — ваш ПК, ноутбук или сервер выполнит её, а Gemini вернётся с реальным результатом.
|
|
557
|
-
|
|
558
|
-
---
|
|
559
|
-
|
|
560
|
-
## 💡 Идея
|
|
561
|
-
|
|
562
|
-
Gemini в браузере или на телефоне умеет разговаривать, но ничего не может *сделать* на вашем компьютере.
|
|
563
|
-
**Gemini Computer Use** устраняет этот разрыв. Это бесплатный open-source сервер **Model Context Protocol (MCP)**, который подключается к **Gemini Spark** ([gemini.google.com/spark/apps](https://gemini.google.com/spark/apps) или мобильное приложение Gemini) одной ссылкой. После этого у Gemini появляются «руки»:
|
|
564
|
-
|
|
565
|
-
- выполняет команды терминала, собирает и тестирует код, работает с git;
|
|
566
|
-
- читает, создаёт и редактирует файлы;
|
|
567
|
-
- ищет по файловой системе;
|
|
568
|
-
- запускает долгие задачи в фоне и проверяет их позже;
|
|
569
|
-
- следит за CPU, RAM и диском.
|
|
570
|
-
|
|
571
|
-
```text
|
|
572
|
-
📱 Приложение Gemini / 🌐 gemini.google.com (Spark)
|
|
573
|
-
│ MCP (SSE / Streamable HTTP)
|
|
574
|
-
▼
|
|
575
|
-
☁️ Шлюз racknerd-5a24bf9.merino-carob.ts.net ← только релей, TLS
|
|
576
|
-
│ исходящий WebSocket-туннель (без открытых портов)
|
|
577
|
-
▼
|
|
578
|
-
💻 Ваш ПК · ноутбук · VPS · домашний сервер → выполняет задачу
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
**Итог:** телефон становится пультом управления всеми вашими машинами, а Gemini — агентом, который ими управляет.
|
|
582
|
-
|
|
583
|
-
---
|
|
584
|
-
|
|
585
|
-
## 🔥 Что можно делать
|
|
586
|
-
|
|
587
|
-
Просто попросите Gemini обычным языком, откуда угодно:
|
|
588
|
-
|
|
589
|
-
- *«Посмотри, почему тормозит домашний сервер, и покажи топ процессов»*
|
|
590
|
-
- *«Подтяни последние изменения в ~/projects/api, прогони тесты и скажи, что упало»*
|
|
591
|
-
- *«Исправь опечатку в config.yaml и перезапусти сервис»*
|
|
592
|
-
- *«Запусти полный бэкап в фоне и сообщи, когда закончится»*
|
|
593
|
-
- *«Сколько свободного места осталось на ноутбуке?»*
|
|
594
|
-
- *«Включи фонарик на телефоне и скажи уровень заряда и сигнал»*
|
|
595
|
-
|
|
596
|
-
Без SSH-клиента, без терминала на телефоне, без VPN — только чат.
|
|
597
|
-
|
|
598
|
-
---
|
|
599
|
-
|
|
600
|
-
## ✨ Почему это удобно
|
|
601
|
-
|
|
602
|
-
| | |
|
|
603
|
-
| :--- | :--- |
|
|
604
|
-
| 📱 **Мобильный агент** | Работает в официальном приложении Gemini и в вебе через Gemini Spark. |
|
|
605
|
-
| 🖥️ **Любой ПК** | Linux, macOS, Windows — десктопы, ноутбуки, VPS, контейнеры. |
|
|
606
|
-
| 🌐 **За любым NAT** | Узел сам открывает *исходящий* WebSocket-туннель. Не нужны белый IP, проброс портов и настройка роутера. |
|
|
607
|
-
| ⚡ **Установка одной командой** | Определяет ОС, архитектуру и тип устройства; ставит автозапуск (`systemd` / `launchd` / задача Windows); копирует ссылку подключения в буфер обмена. |
|
|
608
|
-
| 🛡️ **Доступ по токену** | Каждый вызов требует личный 128-битный токен (`?token=…` или `Authorization: Bearer`). Остальным — HTTP 401. |
|
|
609
|
-
| 🔁 **Много машин, один шлюз** | Каждый узел выбирается параметром `?user=<имя-узла>` на одном общем домене. |
|
|
610
|
-
| 🆓 **Бесплатно и открыто** | Лицензия MIT, работает на бесплатном тарифе Gemini. |
|
|
611
|
-
|
|
612
|
-
---
|
|
613
|
-
|
|
614
|
-
## 🛠️ Что Gemini может делать на вашей машине (MCP-инструменты)
|
|
615
|
-
|
|
616
|
-
**26 инструментов.** Все они выполняются на вашей машине под вашей учётной записью — шлюз только передаёт вызовы.
|
|
617
|
-
|
|
618
|
-
### На какие вызовы Gemini спросит подтверждение
|
|
619
|
-
|
|
620
|
-
Gemini Spark решает, останавливаться ли с вопросом *«подтвердить действие?»*, по **подсказкам MCP-аннотаций** (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), которые инструмент объявляет в `tools/list`. Инструмент без аннотаций считается деструктивным по умолчанию спецификации — поэтому сервер, который их не объявляет, получает запрос подтверждения на **каждом** вызове. Здесь подсказки заданы явно:
|
|
621
|
-
|
|
622
|
-
| Класс | Инструменты | Подтверждение |
|
|
623
|
-
| :--- | :--- | :--- |
|
|
624
|
-
| Только чтение | `mesh_status`, `system_info`, `system_vitals`, `get_orchestration_skill`, `list_dir`, `read_file`, `grep_search`, `glob_find`, `job_output`, `job_list`, `share_list`, `device_info` | не запрашивается |
|
|
625
|
-
| Локальная работа — команды, сборки, запись файлов, задачи, публикации, действия с устройством | `bash_exec`, `run_job`, `write_file`, `edit_file`, `job_kill`, `share_file`, `serve_dir`, `device_control` | не запрашивается |
|
|
626
|
-
| **Чувствительные — наблюдают за физическим миром или личными данными** | `device_capture` (камера, микрофон, местоположение, отпечаток, USB, ИК-порт), `device_messages` (SMS, журнал вызовов, контакты, звонки) | **запрашивается** |
|
|
627
|
-
| **Подтверждаемые — установка/удаление, `sudo`, системные пути** | `system_change` (установка/удаление ПО, удаление данных, `sudo`, правка системных путей через оболочку), `system_write` (запись файла в системный путь), `mesh_update` (устанавливает релиз), `unshare` (отзывает и удаляет опубликованную копию) | **запрашивается** |
|
|
628
|
-
|
|
629
|
-
`device_capture` и `device_messages` ничего не удаляют, но объявлены с `destructiveHint`. Эта подсказка — единственная, которую клиенты вроде Gemini Spark надёжно превращают в вопрос *«подтвердить действие?»*, а молча сработавшая камера, микрофон или список SMS хуже честной перестраховки. В этот класс они отнесены намеренно. `device_control` по той же причине остаётся обычной локальной работой: пометить так фонарик или вибрацию — значит приучить вас закрывать те диалоги, которые действительно важны.
|
|
630
|
-
|
|
631
|
-
Под барьером четыре класса, и следит за ними шлюз, а не подсказка: подсказка статична на инструмент и не отличает `ls` от `apt install`.
|
|
632
|
-
|
|
633
|
-
1. **Установка и удаление ПО** — пакетные менеджеры, деинсталляторы, `msiexec`, `dpkg`/`rpm`, `curl … | bash`, `iwr … | iex`.
|
|
634
|
-
2. **Удаление данных** — `rm`, `Remove-Item`, `del`, `rd`, `shred`, `dd`, `mkfs`, `git clean -fd`, `docker rm`/`prune`, `truncate -s 0`.
|
|
635
|
-
3. **Работа от root** — `sudo`, `doas`, `pkexec`, `runas`, `Start-Process -Verb RunAs`.
|
|
636
|
-
4. **Системные разделы** — `/etc`, `/usr`, `/boot`, `/var/lib`, `/opt`, `/dev`, `C:\Windows`, `C:\Program Files`, `ProgramData`, реестр. **Чтение** этих путей (`cat /etc/os-release`, `systemctl status`) не блокируется, как и обычная работа в ваших каталогах.
|
|
637
|
-
|
|
638
|
-
`bash_exec`/`run_job` отклоняют такие команды и предлагают переоформить их как `system_change`; `write_file`/`edit_file` отклоняют системный путь и направляют в `system_write` (он принимает точное содержимое, поэтому экранировать ничего не нужно). Барьер не убран, а перенесён: осмотр, сборки, тесты, правка конфигов в ваших проектах идут без диалога.
|
|
639
|
-
|
|
640
|
-
Поверхность инструментов и политика подтверждений живут в [gateway.py](gateway.py) (аннотации, `INSTALL_COMMAND_PATTERNS` / `DELETE_COMMAND_PATTERNS`, `SYSTEM_PATH_RE`, `classify_command()`) и в [core/mcp_tools.py](core/mcp_tools.py) (`TOOL_ANNOTATIONS` для собственной поверхности узла, включая четыре инструмента ветки устройства); тесты сравнивают обе поверхности и фиксируют классификатор, чтобы закрытая команда не просочилась.
|
|
641
|
-
|
|
642
|
-
> [!NOTE]
|
|
643
|
-
> Классификатор ищет глагол в начале сегмента команды (после префиксов `sudo`/`env`/`timeout`/`powershell -Command`), поэтому `echo "rm -rf /"` или `grep rm notes.txt` выполняются как раньше, а `curl … | bash` считается установкой. Это UX-барьер, а не песочница: жёсткая гарантия — переключатели узла `MESH_READ_ONLY` и `MESH_WRITE_ROOTS` ([core/agent.py](core/agent.py)).
|
|
644
|
-
|
|
645
|
-
### Состояние и сведения о хосте
|
|
646
|
-
|
|
647
|
-
| Инструмент | Что делает |
|
|
648
|
-
| :--- | :--- |
|
|
649
|
-
| `mesh_status()` | Подтверждает, что узел доступен, живыми данными с самого хоста: что за машина ответила (`device_class`, `scenario`) и её `battery_percent`. Вызывать первым, если машина кажется offline. |
|
|
650
|
-
| `system_info()` | Сводка о хосте одним вызовом: ОС, рабочий стол, пользователь, домашний каталог, диски, память, загрузка, топ процессов, текущие обои, `command_shell` — та оболочка, которую реально использует `bash_exec`, — и блоки устройства: батарея и зарядка, сеть и сигнал, язык, время и часовой пояс, CPU, ОЗУ, накопитель, камеры, микрофоны и датчики. |
|
|
651
|
-
| `system_vitals()` | Метрики CPU, ОЗУ и дисков, плюс блок батареи и термические датчики, которые отдаёт платформа. |
|
|
652
|
-
|
|
653
|
-
### Оболочка
|
|
654
|
-
|
|
655
|
-
| Инструмент | Что делает |
|
|
656
|
-
| :--- | :--- |
|
|
657
|
-
| `bash_exec(command, timeout_sec, max_chars, cursor)` | Выполняет команду оболочки. Оболочка соответствует **хосту**, а не названию инструмента — `bash` на Linux/macOS, PowerShell или `cmd.exe` на Windows; сначала посмотрите `command_shell` из `system_info()`. Вывод постраничный: если обрезан, вызовите снова с `cursor=next_cursor`, ничего не теряется; вывод больше 2 МБ сохраняется в файл, путь в `saved_to`. `timeout_sec` — 1–120 (по умолчанию 25). Установка/удаление, `sudo` и системные пути здесь **отклоняются** — их надо выполнять через `system_change`. |
|
|
658
|
-
| `system_change(command, timeout_sec, background, cwd, max_chars, cursor)` | Установка, удаление ПО и данных, привилегированные команды и правка системных путей — «подтверждаемый» двойник `bash_exec` и единственный инструмент, которому разрешены `apt`/`dnf`/`pacman`/`pip`/`npm`/`winget`/`msiexec`/`rm`/`Remove-Item`/`sudo` и работа с дисками. Объявлен деструктивным, поэтому Gemini спрашивает подтверждение. `background=true` запускает его как фоновую задачу (для долгих установок) и возвращает `job_id` для `job_output`; `cwd` действует только в этом режиме. |
|
|
659
|
-
|
|
660
|
-
### Файлы
|
|
661
|
-
|
|
662
|
-
| Инструмент | Что делает |
|
|
663
|
-
| :--- | :--- |
|
|
664
|
-
| `list_dir(path)` | Список файлов и каталогов (по умолчанию — рабочий каталог). |
|
|
665
|
-
| `read_file(path, start_line, end_line, max_chars, cursor)` | Читает текстовый файл с номерами строк, при желании — диапазон строк. Постранично, как `bash_exec`. |
|
|
666
|
-
| `write_file(path, content, create_dirs, mode)` | Атомарно создаёт или перезаписывает файл (временный файл + `os.replace`). `mode` — только для POSIX: на Windows он не применяется, и результат об этом честно сообщает. Путь внутри системы **отклоняется** — для него есть `system_write`. |
|
|
667
|
-
| `edit_file(path, old_string, new_string, expected_sha256, replace_all)` | Заменяет точную подстроку. `old_string` должен встречаться ровно один раз, если не задан `replace_all`; `expected_sha256` защищает от перезаписи файла, изменившегося после чтения. Системные пути отклоняются — прочитайте файл и отправьте новое содержимое целиком в `system_write`. |
|
|
668
|
-
| `system_write(path, content, create_dirs, mode)` | Запись файла в системный путь (`/etc`, `/usr`, `/boot`, `/var/lib`, `C:\Windows`, `C:\Program Files`, `ProgramData`, реестр) — «подтверждаемый» двойник `write_file` и единственный способ туда писать. Gemini спрашивает подтверждение. Содержимое передаётся точно, экранировать для оболочки ничего не нужно. |
|
|
669
|
-
|
|
670
|
-
### Поиск
|
|
671
|
-
|
|
672
|
-
| Инструмент | Что делает |
|
|
673
|
-
| :--- | :--- |
|
|
674
|
-
| `grep_search(pattern, path, glob, limit, ignore_case, fixed, context)` | Рекурсивно ищет по содержимому файлов, пропуская двоичные файлы и тяжёлые каталоги. `fixed` — поиск как по обычному тексту, `context` — строки вокруг совпадения, `limit` — 1–1000 (по умолчанию 200). |
|
|
675
|
-
| `glob_find(pattern, path)` | Ищет файлы по маске (`*` и `**`). |
|
|
676
|
-
|
|
677
|
-
### Долгие задачи
|
|
678
|
-
|
|
679
|
-
| Инструмент | Что делает |
|
|
680
|
-
| :--- | :--- |
|
|
681
|
-
| `run_job(command, cwd)` | Запускает команду в фоне и возвращает `job_id`. На Android-узле берёт wake lock на всё время задачи, чтобы Android не заморозил её при выключенном экране. |
|
|
682
|
-
| `job_output(job_id, wait_ms, max_chars, cursor)` | Читает вывод задачи, при желании ожидая завершения до 20 с. Постранично. |
|
|
683
|
-
| `job_kill(job_id, signal)` | Завершает задачу. `signal` — `TERM` (по умолчанию), `KILL`, `INT`, `HUP` или `QUIT`. |
|
|
684
|
-
| `job_list(limit)` | Список последних задач, новые сверху (20 по умолчанию, максимум 50), с признаком `wake_lock` у каждой. |
|
|
685
|
-
|
|
686
|
-
### Устройство (телефон, ноутбук или сервер)
|
|
687
|
-
|
|
688
|
-
Эти четыре инструмента читают само устройство, на котором работает узел, и управляют им. Они объявлены на **каждом** узле — действие для телефона на ноутбуке отвечает `available: false`, причиной и, где она есть, подсказкой `fix`, а не отказом.
|
|
689
|
-
|
|
690
|
-
| Инструмент | Что делает |
|
|
691
|
-
| :--- | :--- |
|
|
692
|
-
| `device_info(section, sensor, fresh, quick)` | Отчёт об устройстве одним вызовом: батарея и зарядка, сетевые интерфейсы с уровнем Wi-Fi и сотового сигнала, язык, время и часовой пояс, CPU, ОЗУ, накопитель, камеры, микрофоны, датчики и то, до чего платформа реально дотягивается. `section` — `summary`, `all` (по умолчанию) или один из `device`, `battery`, `network`, `locale`, `time`, `hardware`, `storage`, `cameras`, `microphones`, `sensors`, `capabilities`. `sensor` берёт один живой замер с датчика по имени (Android; имя — из блока `sensors`), `fresh` обходит короткий кэш, `quick` пропускает медленные разделы. У каждого блока есть `available` и `source`; блок, на который у платформы нет ответа, говорит причину, а не показывает ноль. |
|
|
693
|
-
| `device_control(action, on, value, stream, text, title, id, url, path, state, timeout_sec)` | Двадцать обратимых действий: `torch`, `vibrate`, `volume`, `volume_get`, `brightness`, `tts_speak`, `toast`, `notify`, `notify_list`, `notify_remove`, `clipboard_get`, `clipboard_set`, `media`, `media_scan`, `wakelock`, `download`, `open`, `share`, `dialog`, `wallpaper`. `value` — миллисекунды для `vibrate`, уровень 0–15 для `volume` и 0–255 для `brightness`; `stream` выбирает аудиопоток (`music` по умолчанию); `text` — тело для TTS, toast, уведомления, буфера обмена и диалога, а для `media` — `play\|pause\|stop\|info`. Ничего деструктивного здесь нет. |
|
|
694
|
-
| `device_capture(action, camera_id, path, seconds, provider, frequency, pattern)` | Одиннадцать действий: `camera_list`, `camera_photo`, `mic_record_start`, `mic_record_stop`, `mic_record_status`, `location`, `fingerprint`, `usb_list`, `usb_access`, `infrared_frequencies`, `infrared_transmit`. **Спрашивает подтверждение.** `path` задаёт выходной файл для `camera_photo` и `mic_record_start` (по умолчанию — каталог съёмки узла) и путь устройства из `usb_list` для `usb_access`; `camera_id` берётся из `camera_list`, `seconds` ограничивает запись, `provider` — `gps`, `network` или `passive`, а `frequency`/`pattern` — для `infrared_transmit`. Фото и запись остаются в каталоге съёмки и сами никуда не отправляются — `share_file` публикует файл только по отдельной просьбе. `fingerprint` возвращает только вердикт; `usb_access` запрашивает у Android разрешение и отдаёт дескриптор, но намеренно отказывается запускать программу за вас. |
|
|
695
|
-
| `device_messages(action, number, text, limit, offset, type, query)` | Пять действий: `sms_list`, `sms_send`, `call_log`, `contacts`, `call`. **Спрашивает подтверждение и выключен, пока оператор не включит его через `MESH_DEVICE_PIM=1`** — это личные данные того, у кого телефон в руках. `limit` — 1–50 (по умолчанию 10), `offset` листает список, `type` фильтрует его, а `query` фильтрует контакты на узле. |
|
|
696
|
-
|
|
697
|
-
Пример:
|
|
698
|
-
|
|
699
|
-
```text
|
|
700
|
-
«Проверь батарею и сигнал на телефоне».
|
|
701
|
-
→ device_info(section="summary")
|
|
702
|
-
|
|
703
|
-
«Включи фонарик».
|
|
704
|
-
→ device_control(action="torch", on=true)
|
|
705
|
-
|
|
706
|
-
«Сделай фото и поделись им со мной».
|
|
707
|
-
→ device_capture(action="camera_photo") # спрашивает подтверждение
|
|
708
|
-
→ share_file(path=<путь, который он вернул>)
|
|
709
|
-
```
|
|
710
|
-
|
|
711
|
-
### Переключатели устройства (оператор)
|
|
712
|
-
|
|
713
|
-
Читаются из `agent.env` при запуске ([core/agent.py](core/agent.py), применяются в [core/mcp_tools.py](core/mcp_tools.py) и [core/device.py](core/device.py)):
|
|
714
|
-
|
|
715
|
-
| Переменная | По умолчанию | Что делает |
|
|
716
|
-
| :--- | :--- | :--- |
|
|
717
|
-
| `MESH_DEVICE` | `auto` | `auto` — ветка отвечает везде (различается только ответ), `0` — выключает ветку, `1` — включает принудительно. |
|
|
718
|
-
| `MESH_DEVICE_ACTIONS` | пусто | Список разрешённых имён действий через запятую для `device_control`, `device_capture` и `device_messages`; пусто — все действия, реализованные сборкой. |
|
|
719
|
-
| `MESH_DEVICE_CAPTURE_DIR` | общее хранилище телефона после `termux-setup-storage` (`~/storage/dcim/antigravity-mesh` или `~/storage/shared/AntigravityMesh`), иначе `~/.cache/antigravity-mesh/captures` | Куда пишутся фото и записи. |
|
|
720
|
-
| `MESH_DEVICE_PIM` | `0` | `1` включает SMS, журнал вызовов, контакты и звонок. |
|
|
721
|
-
| `MESH_DEVICE_QUICK` | `0` | `1` оставляет `system_info` мгновенным, пропуская сеть, камеры, микрофоны и датчики. |
|
|
722
|
-
| `MESH_READ_ONLY` | `0` | `1` отказывает в каждом действии `device_control` и `device_capture`, а также блокирует `sms_send` и `call`. Это гарантия на стороне узла, не зависящая от диалога клиента. |
|
|
723
|
-
|
|
724
|
-
### Публикация файлов и веб-страниц
|
|
725
|
-
|
|
726
|
-
Эти четыре делают что-то на вашей машине доступным из интернета. Ссылку отдаёт шлюз на общем домене, поэтому **узел должен оставаться подключённым**.
|
|
727
|
-
|
|
728
|
-
| Инструмент | Что делает |
|
|
729
|
-
| :--- | :--- |
|
|
730
|
-
| `share_file(path, name, overwrite)` | Публикует **один файл**: он копируется в каталог публикаций узла, и вы получаете `https://<общий-домен>/<узел>/<имя>-<случайное>/<файл>`. `overwrite` заменяет публикацию с тем же именем. |
|
|
731
|
-
| `serve_dir(path, name)` | Отдаёт **каталог** на месте — копия не создаётся. Если есть `index.html`, отдаётся он, иначе показывается список файлов. Только чтение (GET/HEAD). Возвращает `https://<общий-домен>/<узел>/<имя>-<случайное>/`. |
|
|
732
|
-
| `share_list()` | Список активных публикаций: ссылки, корни и запущен ли локальный сервер. |
|
|
733
|
-
| `unshare(name)` | Останавливает публикацию и отзывает ссылку. Принимает имя, slug или полную ссылку. Для файла удаляется и копия в каталоге публикаций; отдаваемый каталог на диске не трогается. |
|
|
734
|
-
|
|
735
|
-
> [!IMPORTANT]
|
|
736
|
-
> Случайная часть пути **и есть** пароль — любой, у кого есть ссылка, прочитает файл или просмотрит каталог. Относитесь к ссылке как к паролю и снимайте публикацию через `unshare`, когда она больше не нужна. Ограничения: 32 МиБ на файл по умолчанию, потолок 64 МиБ через `MESH_WEB_MAX_BYTES`.
|
|
737
|
-
|
|
738
|
-
### Поведение агента
|
|
739
|
-
|
|
740
|
-
| Инструмент | Что делает |
|
|
741
|
-
| :--- | :--- |
|
|
742
|
-
| `get_orchestration_skill()` | Загружает текущие рабочие правила агента и навык оркестрации. |
|
|
743
|
-
|
|
744
|
-
### Поддержание узла в актуальном состоянии
|
|
745
|
-
|
|
746
|
-
| Инструмент | Что делает |
|
|
747
|
-
| :--- | :--- |
|
|
748
|
-
| `mesh_update(action, force, offline, restart)` | Сообщает, проверяет или устанавливает новую версию кода самого узла. `status` — локальное состояние (без сети), `check` — запрос к GitHub, `apply` — скачивает payload, проверяет опубликованный SHA-256, ставит с бэкапом для отката и перезапускает агент. |
|
|
749
|
-
|
|
750
|
-
> [!TIP]
|
|
751
|
-
> Узлы обновляются сами: работающий агент проверяет новые релизы в фоне (по умолчанию раз в 6 часов) и устанавливает найденное, а на Windows задача `AntigravityMeshUpdater` проверяет раз в сутки даже когда агент не запущен. `MESH_UPDATE_AUTO=0` переводит это в режим «только сообщать». Подробности, переменные окружения и откат: [docs/UPDATES.md](docs/UPDATES.md).
|
|
752
|
-
|
|
753
|
-
> [!TIP]
|
|
754
|
-
> Gemini даёт одному вызову инструмента около 30 секунд. Всё, что дольше (сборки, бэкапы, загрузки), агент запускает через `run_job` и забирает результат через `job_output` — задачи не обрываются.
|
|
755
|
-
|
|
756
|
-
---
|
|
757
|
-
|
|
758
|
-
## 🚀 Быстрый старт — 2 минуты
|
|
759
|
-
|
|
760
|
-
### 1. Установите узел на ПК, которым хотите управлять
|
|
761
|
-
|
|
762
|
-
**🐧 Linux / 🍎 macOS**
|
|
763
|
-
```bash
|
|
764
|
-
curl -fsSL https://racknerd-5a24bf9.merino-carob.ts.net/install.sh | bash
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
**📱 Android (Termux)** — без root, Termux из F-Droid
|
|
768
|
-
```bash
|
|
769
|
-
curl -fsSL https://racknerd-5a24bf9.merino-carob.ts.net/install.sh | bash
|
|
770
|
-
```
|
|
771
|
-
Автозапуск на телефоне — служба runit плюс приложение Termux:Boot, а имя узла
|
|
772
|
-
берётся из модели устройства: см. [docs/TERMUX.md](docs/TERMUX.md).
|
|
773
|
-
|
|
774
|
-
**🪟 Windows (PowerShell)**
|
|
775
|
-
```powershell
|
|
776
|
-
irm https://racknerd-5a24bf9.merino-carob.ts.net/install.ps1 | iex
|
|
777
|
-
```
|
|
778
|
-
|
|
779
|
-
**🪟 Windows — визуальный установщик** (из клона или релиза)
|
|
780
|
-
|
|
781
|
-
```powershell
|
|
782
|
-
.\install-gui.cmd
|
|
783
|
-
```
|
|
784
|
-
|
|
785
|
-
Мастер на WinForms: те же варианты, что и в консольном установщике, переключение языка
|
|
786
|
-
на ходу и готовая ссылка MCP в конце — см. [docs/GUI_INSTALLER.md](docs/GUI_INSTALLER.md).
|
|
787
|
-
|
|
788
|
-
**📦 Node.js (любая ОС)**
|
|
789
7
|
```bash
|
|
8
|
+
# both commands do the same thing:
|
|
790
9
|
npx gemini-computer-use
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
**🛠️ Из исходников**
|
|
794
|
-
```bash
|
|
795
|
-
git clone https://github.com/LevRa7/Computer-use-for-Gemini-App-Web.git
|
|
796
|
-
cd Computer-use-for-Gemini-App-Web
|
|
797
|
-
./install.sh --quick
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
В конце установщик выведет вашу личную MCP-ссылку и скопирует её в буфер обмена:
|
|
801
|
-
```text
|
|
802
|
-
https://racknerd-5a24bf9.merino-carob.ts.net/sse?user=<имя-вашего-узла>&token=<ваш_секретный_токен>
|
|
803
|
-
```
|
|
804
|
-
|
|
805
|
-
### 2. Подключите её к Gemini Spark
|
|
10
|
+
npx remote-harness-mcp
|
|
806
11
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
### 3. Дайте задачу
|
|
812
|
-
|
|
813
|
-
> *«Покажи system_vitals и 5 самых больших папок в домашнем каталоге.»*
|
|
814
|
-
|
|
815
|
-
Готово — Gemini теперь агент, работающий на вашей машине. Повторите шаг 1 на других компьютерах, чтобы управлять ими всеми с одного телефона.
|
|
816
|
-
|
|
817
|
-
---
|
|
818
|
-
|
|
819
|
-
## 🖥️ Режимы развёртывания
|
|
820
|
-
|
|
821
|
-
| Режим | Команда | Когда использовать |
|
|
822
|
-
| :--- | :--- | :--- |
|
|
823
|
-
| **Шлюз + туннель** (рекомендуется) | `./install.sh --quick` | Домашние ПК и ноутбуки за NAT — сценарий мобильного агента. |
|
|
824
|
-
| **Удалённо через SSH** | `./install.sh --ssh=user@host` | Развернуть узел на удалённом Linux-сервере прямо из терминала. |
|
|
825
|
-
| **Локальный автономный** | `./install.sh --mode=standalone --port=8096` | Чисто локальный FastMCP-сервер на `http://localhost:8096/sse`, без облачного релея. |
|
|
826
|
-
|
|
827
|
-
На Windows те же варианты есть в визуальном установщике (`.\install-gui.cmd`); локальный автономный режим там остаётся консольным (`.\install.ps1 -Mode standalone -Port 8096`).
|
|
828
|
-
|
|
829
|
-
На **Android/Termux** работают те же варианты `./install.sh`; автозапуск — служба runit плюс приложение Termux:Boot вместо systemd, а автономный режим доступен только внутри самого телефона: см. [docs/TERMUX.md](docs/TERMUX.md).
|
|
830
|
-
|
|
831
|
-
---
|
|
832
|
-
|
|
833
|
-
## 🪟 Установка на Windows
|
|
834
|
-
|
|
835
|
-
В Windows уже есть PowerShell 5.1, поэтому готовить ничего не нужно — ни Python, ни Node.js, ни права администратора. Оба установщика заканчивают одинаково: кладут ссылку MCP в буфер обмена.
|
|
836
|
-
|
|
837
|
-
| | Консольный установщик | Визуальный установщик |
|
|
838
|
-
| :--- | :--- | :--- |
|
|
839
|
-
| Запуск | `irm https://racknerd-5a24bf9.merino-carob.ts.net/install.ps1 \| iex` | `.\install-gui.cmd` |
|
|
840
|
-
| Что нужно | только PowerShell | клон или распакованный релиз — он вызывает `install.ps1`, `core/` и `install.sh` |
|
|
841
|
-
| Варианты | `-Mode tunnel` (по умолчанию), `-Mode standalone`, `-User`, `-Gateway`, `-Token`, `-Port`, `-DryRun` | Быстрая настройка, Кастомная настройка, Удалённо по SSH |
|
|
842
|
-
| Язык | `-Lang en` / `-Lang ru` | переключатель в шапке окна или `-Lang` |
|
|
843
|
-
|
|
844
|
-
### Автоматические зависимости и архитектура
|
|
845
|
-
|
|
846
|
-
Заранее готовить ничего не нужно. Оба установщика сами добывают всё необходимое, а если
|
|
847
|
-
не смогли — останавливаются с явным сообщением, вместо того чтобы прописать автозапуск,
|
|
848
|
-
который заведомо не заработает.
|
|
849
|
-
|
|
850
|
-
| | Windows (`install.ps1`) | Linux / macOS (`install.sh`) |
|
|
851
|
-
| :--- | :--- | :--- |
|
|
852
|
-
| Python | `winget` → установщик python.org для **этой** архитектуры → `uv` | `apt` / `dnf` / `yum` / `zypper` / `pacman` / `apk` / `xbps` / `brew` → `uv` |
|
|
853
|
-
| `websockets` | `pip` → `pip --user` → `ensurepip` → `uv` → venv | `pip` → `pip --break-system-packages` → `pip --user` → `ensurepip` → `uv` → venv |
|
|
854
|
-
| Если не вышло ничего | остановка и точная команда для ручного запуска | остановка и точная команда для ручного запуска |
|
|
855
|
-
|
|
856
|
-
`websockets` — единственная внешняя зависимость Python: `core/mcp_tools.py` и
|
|
857
|
-
`core/server.py` используют только стандартную библиотеку. `uv` скачивается лишь после
|
|
858
|
-
того, как более дешёвые пути уже не сработали.
|
|
859
|
-
|
|
860
|
-
**Архитектура определяет, какая сборка скачивается.** Установщик спрашивает саму *ОС*, а
|
|
861
|
-
не процесс: 32-битный PowerShell на 64-битной Windows сообщает `x86` и прячет настоящее
|
|
862
|
-
значение в `PROCESSOR_ARCHITEW6432`, а эмулируемый x64-процесс на ARM64 сообщает `AMD64`:
|
|
863
|
-
|
|
864
|
-
| Машина | Сборка Python | Архив `uv` |
|
|
865
|
-
| :--- | :--- | :--- |
|
|
866
|
-
| Windows x64 | `python-3.12.5-amd64.exe` | `x86_64-pc-windows-msvc` |
|
|
867
|
-
| Windows ARM64 | `python-3.12.5-arm64.exe` | `aarch64-pc-windows-msvc` |
|
|
868
|
-
| Windows 32-бит | `python-3.12.5.exe` | — (32-битных сборок uv для Windows нет) |
|
|
869
|
-
| Linux `x86_64` | сборка пакетного менеджера | `x86_64-unknown-linux-gnu` |
|
|
870
|
-
| Linux `aarch64` | сборка пакетного менеджера | `aarch64-unknown-linux-gnu` |
|
|
871
|
-
| Linux `armv7l` / `i686` | сборка пакетного менеджера | `armv7-unknown-linux-gnueabihf` / `i686-unknown-linux-gnu` |
|
|
872
|
-
| macOS `arm64` / `x86_64` | `brew` или `uv` | `aarch64-apple-darwin` / `x86_64-apple-darwin` |
|
|
873
|
-
|
|
874
|
-
В автозапуск прописывается тот интерпретатор, у которого реально есть `websockets`, — это
|
|
875
|
-
может быть venv или CPython, поставленный `uv`, и оба намеренно живут вне `PATH`.
|
|
876
|
-
`install.ps1 -DryRun` и `install.sh --dry-run` показывают архитектуру, ничего не меняя, а
|
|
877
|
-
визуальный установщик выводит её в предполётной проверке.
|
|
878
|
-
|
|
879
|
-
### Готовый .exe установщика
|
|
880
|
-
|
|
881
|
-
В релизе есть один собранный установщик — `AntigravityMesh-Setup-<версия>.exe`, для машин,
|
|
882
|
-
где не хочется ничего клонировать:
|
|
883
|
-
|
|
884
|
-
```powershell
|
|
885
|
-
.\AntigravityMesh-Setup-0.4.3.exe # открыть визуальный установщик
|
|
886
|
-
.\AntigravityMesh-Setup-0.4.3.exe -Lang ru # начать на русском
|
|
887
|
-
.\AntigravityMesh-Setup-0.4.3.exe -SelfTest # самопроверка без окна, печатает JSON
|
|
888
|
-
.\AntigravityMesh-Setup-0.4.3.exe --version # показать версию
|
|
889
|
-
```
|
|
890
|
-
|
|
891
|
-
Внутри него лежат мастер, `install.ps1`, `core/` и `install.sh`; он распаковывает их в
|
|
892
|
-
`%LOCALAPPDATA%\AntigravityMesh\setup\<версия>` (можно переопределить переменной
|
|
893
|
-
`MESH_SETUP_DIR`) и запускает мастера оттуда. Ему нужны только .NET Framework и
|
|
894
|
-
PowerShell, которые в Windows уже есть, а собирается он из этого же репозитория скриптом
|
|
895
|
-
`.\build-installer-exe.ps1` — без SDK, без NuGet и без сети.
|
|
896
|
-
|
|
897
|
-
Файл **не подписан**, поэтому SmartScreen при первом запуске может попросить
|
|
898
|
-
подтверждение.
|
|
899
|
-
|
|
900
|
-
### Визуальный установщик
|
|
901
|
-
|
|
902
|
-

|
|
903
|
-
|
|
904
|
-
- **Быстрая настройка** (рекомендуется) — имя узла = имя ПК, общий домен шлюза, автозапуск Windows. Это вариант для домашнего ПК или ноутбука.
|
|
905
|
-
- **Кастомная настройка** — своё имя узла, свой общий домен, необязательный токен.
|
|
906
|
-
- **Удалённо по SSH** — код узла копируется на Linux-хост, там запускается `install.sh`. Нужен компонент Windows *«Клиент OpenSSH»* и вход по ключу; ввод пароля не поддерживается.
|
|
907
|
-
|
|
908
|
-
Первый экран запускает `install.ps1 -DryRun` и показывает, что найдено: Python, `websockets`, разрешённый домен шлюза, каталог конфигурации и путь автозапуска — ничего не меняя. Перед запуском окно показывает точную команду, во время установки — построчный вывод установщика, в конце — ссылку MCP с кнопкой копирования, инструкцию из трёх шагов и созданные пути.
|
|
909
|
-
|
|
910
|
-
Подробности, снимки экрана и особенности SSH: [docs/GUI_INSTALLER.md](docs/GUI_INSTALLER.md).
|
|
911
|
-
|
|
912
|
-
### Что создаётся при установке на Windows
|
|
913
|
-
|
|
914
|
-
| Путь | Что это |
|
|
915
|
-
| :--- | :--- |
|
|
916
|
-
| `%USERPROFILE%\.config\antigravity-mesh\agent.env` | `MESH_GATEWAY`, `MESH_USER`, `MESH_TOKEN` |
|
|
917
|
-
| `%USERPROFILE%\.config\antigravity-mesh\domain.env` | `MESH_PUBLIC_URL` — разрешённый общий домен, чтобы ссылки узла, его туннель и шлюз называли один и тот же хост (не пишется, если настроено только встроенное значение по умолчанию) |
|
|
918
|
-
| `…\Start Menu\Programs\Startup\antigravity-agent.vbs` | запись автозапуска, чтобы узел поднимался после перезагрузки |
|
|
919
|
-
| `%USERPROFILE%\.config\antigravity-mesh\agent.log` | вывод агента — на случай, если автозапуск молча не сработал |
|
|
920
|
-
|
|
921
|
-
### Язык
|
|
922
|
-
|
|
923
|
-
Визуальный установщик стартует на языке ваших языковых настроек Windows — язык интерфейса и список предпочитаемых языков читаются из реестра, — а если ничего не найдено, на английском. Флаг `-Lang ru` / `-Lang en` переопределяет автоопределение. Переключатель в шапке окна меняет все подписи сразу.
|
|
924
|
-
|
|
925
|
-
### Если что-то не работает
|
|
926
|
-
|
|
927
|
-
- **«Домен шлюза не задан»** — эта копия не была опубликована с доменом. Укажите его: режим *«Кастомная настройка»* в мастере, либо `.\install.ps1 -Gateway <общий-домен>`, либо переменная `MESH_PUBLIC_URL`.
|
|
928
|
-
- **Регистрация не проходит** — установщик останавливается **до** записи файлов, если шлюз недоступен. Проверьте, что домен разрешается, и повторите с явным `-Gateway`.
|
|
929
|
-
- **`python` открывает Microsoft Store** — это заглушка App Execution Alias, а не интерпретатор. Оба установщика находят настоящий `python.exe` по полному пути; в автозапуск заглушка не попадает никогда.
|
|
930
|
-
- **Узел не поднимается после перезагрузки** — запустите `.\ops\doctor.ps1`: он покажет интерпретатор из автозапуска, свежесть heartbeat, последние строки `agent.log` (включая оборванную) и то, что о узле думает шлюз. `.\ops\windows\agent-watchdog.ps1` выполняет ту же проверку один раз и поднимает агент; задача `AntigravityMeshWatchdog` повторяет её каждые пять минут, поэтому умерший агент поднимается сам.
|
|
931
|
-
|
|
932
|
-
### Автономный режим на Windows
|
|
933
|
-
|
|
934
|
-
В мастере его нет, используйте консольный установщик:
|
|
935
|
-
|
|
936
|
-
```powershell
|
|
937
|
-
.\install.ps1 -Mode standalone -Port 8096
|
|
938
|
-
```
|
|
939
|
-
|
|
940
|
-
Он поднимает локальный FastMCP-сервер на `http://localhost:8096/sse` и кладёт эту ссылку в буфер обмена.
|
|
941
|
-
|
|
942
|
-
---
|
|
943
|
-
|
|
944
|
-
## 📱 Установка на Android (Termux)
|
|
945
|
-
|
|
946
|
-
Тот же `install.sh` ставит узел на смартфон или планшет без root, и телефон
|
|
947
|
-
появляется в Gemini Spark как обычная машина. Полная инструкция:
|
|
948
|
-
**[docs/TERMUX.md](docs/TERMUX.md)**.
|
|
949
|
-
|
|
950
|
-
```bash
|
|
951
|
-
# 1. Termux из F-Droid (не из Google Play); по желанию Termux:Boot и Termux:API
|
|
952
|
-
pkg update -y
|
|
953
|
-
# 2. обычная однострочная установка
|
|
954
|
-
curl -fsSL https://racknerd-5a24bf9.merino-carob.ts.net/install.sh | bash
|
|
955
|
-
```
|
|
956
|
-
|
|
957
|
-
Что меняется на телефоне и как это решает установщик:
|
|
958
|
-
|
|
959
|
-
| | |
|
|
960
|
-
| :--- | :--- |
|
|
961
|
-
| **Пакеты** | `pkg` вместо `apt`, `python`/`python-pip` вместо `python3-pip`/`python3-venv`: на телефоне нет root и нет `sudo`. |
|
|
962
|
-
| **Имя узла** | Модель устройства (`Pixel 7 Pro` → `pixel7pro`): Android отвечает `localhost` всем приложениям, а шлюз держит один туннель на имя. |
|
|
963
|
-
| **Файл домена** | `~/.config/antigravity-mesh/domain.env` — писать в `/etc` некуда. Установщик записывает туда разрешённый общий домен, чтобы ссылки телефона, его туннель и шлюз называли один и тот же хост (встроенное значение по умолчанию не записывается). |
|
|
964
|
-
| **Автозапуск** | Служба **runit** (`termux-services`, аналог `Restart=always`) плюс скрипт **Termux:Boot**, который берёт wake lock после перезагрузки. |
|
|
965
|
-
| **Буфер обмена** | `termux-clipboard-set` (Termux:API): ссылка MCP сразу вставляется в приложение Gemini. |
|
|
966
|
-
|
|
967
|
-
> [!IMPORTANT]
|
|
968
|
-
> Два переключателя на стороне Android нужно включить вам: установить
|
|
969
|
-
> **Termux:Boot** и открыть его один раз, а также поставить оптимизацию батареи в
|
|
970
|
-
> «Без ограничений» для Termux. Без этого Android выгружает узел при выключенном
|
|
971
|
-
> экране.
|
|
972
|
-
|
|
973
|
-
Телефон также должен **резолвить имя шлюза**. Шлюз в приватной сети (Tailscale,
|
|
974
|
-
VPN, запись в DNS на ноутбуке) виден только там, а мобильная сеть приватные имена
|
|
975
|
-
не знает. Подключите телефон к этой сети либо дайте шлюзу публично резолвимое имя:
|
|
976
|
-
установщик распознаёт нерезолвимое имя и сообщает об этом до любых записей — см.
|
|
977
|
-
[docs/TERMUX.md](docs/TERMUX.md#10-приватный-шлюз-tailscale--vpn).
|
|
978
|
-
|
|
979
|
-
```bash
|
|
980
|
-
sv status agy-agent # работает ли
|
|
981
|
-
sv restart agy-agent # перезапустить
|
|
982
|
-
tail -f $PREFIX/var/log/sv/agy-agent/current
|
|
983
|
-
```
|
|
984
|
-
|
|
985
|
-
`--mode=standalone` тоже работает, но слушает только `127.0.0.1` — доступен лишь
|
|
986
|
-
внутри этого телефона. Чтобы управлять телефоном из Gemini, используйте режим
|
|
987
|
-
туннеля (по умолчанию).
|
|
988
|
-
|
|
989
|
-
---
|
|
990
|
-
|
|
991
|
-
## 🔄 Автоматические обновления
|
|
992
|
-
|
|
993
|
-
Узел устанавливается из релиза на GitHub и дальше поддерживает себя в актуальном
|
|
994
|
-
состоянии сам. Ничего не нужно переустанавливать руками, и ничего
|
|
995
|
-
непроверенного не устанавливается.
|
|
996
|
-
|
|
997
|
-
- **Узел проверяет сам.** Работающий агент раз в 6 часов
|
|
998
|
-
(`MESH_UPDATE_CHECK_INTERVAL`) спрашивает GitHub Releases API, сравнивает тег с
|
|
999
|
-
версией в `core/version.py` и ставит найденное. На Windows то же самое раз в
|
|
1000
|
-
сутки делает задача `AntigravityMeshUpdater` — даже если агент не запущен.
|
|
1001
|
-
`MESH_UPDATE_AUTO=0` отключает установку, оставляя только уведомления.
|
|
1002
|
-
- **Сначала проверка, потом установка.** Payload скачивается, его SHA-256
|
|
1003
|
-
сравнивается с опубликованной контрольной суммой, и только затем заменяются
|
|
1004
|
-
`core/`, `skills/` и `ops/`. Нет суммы — нет обновления.
|
|
1005
|
-
- **Откат возможен всегда.** Заменённые каталоги сохраняются в
|
|
1006
|
-
`%USERPROFILE%\.config\antigravity-mesh\backups\<версия>-<время>\`, а
|
|
1007
|
-
обновление, прерванное убийством процесса или отключением питания,
|
|
1008
|
-
откатывается автоматически при следующем запуске.
|
|
1009
|
-
- **Узел возвращается уже новым.** Агент перезапускается тем, что за ним
|
|
1010
|
-
присматривает (задача-сторож на Windows, `systemctl --user restart agy-agent.service`,
|
|
1011
|
-
`launchctl kickstart -k`), а если надзора нет — запускается напрямую.
|
|
1012
|
-
|
|
1013
|
-
Вручную, в любой момент:
|
|
1014
|
-
|
|
1015
|
-
```powershell
|
|
1016
|
-
gemini-computer-use update -Check # вышел ли новый релиз?
|
|
1017
|
-
gemini-computer-use update # установить сейчас и перезапустить
|
|
1018
|
-
gemini-computer-use update -Check -Json
|
|
12
|
+
# both install the same node:
|
|
13
|
+
npm install -g gemini-computer-use
|
|
14
|
+
npm install -g remote-harness-mcp
|
|
1019
15
|
```
|
|
1020
16
|
|
|
1021
|
-
|
|
1022
|
-
`
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
`ops\doctor.ps1`, а полная история — в `%USERPROFILE%\.config\antigravity-mesh\update.log`.
|
|
1026
|
-
Настройки, коды выхода, откат и диагностика: [docs/UPDATES.md](docs/UPDATES.md).
|
|
1027
|
-
|
|
1028
|
-
---
|
|
1029
|
-
|
|
1030
|
-
## 🔗 Один общий домен (канонический формат URL)
|
|
1031
|
-
|
|
1032
|
-
Узел **никогда** не адресуется собственным именем хоста. Шлюз публикует один общий домен, а имя узла передаётся параметром:
|
|
1033
|
-
|
|
1034
|
-
| Назначение | Канонический URL |
|
|
1035
|
-
| :--- | :--- |
|
|
1036
|
-
| MCP через SSE | `https://<общий-домен>/sse?user=<имя-узла>&token=<токен>` |
|
|
1037
|
-
| MCP Streamable HTTP | `https://<общий-домен>/mcp?user=<имя-узла>&token=<токен>` |
|
|
1038
|
-
| Обратный туннель (агент) | `wss://<общий-домен>/ws/tunnel?user=<имя-узла>&token=<токен>` |
|
|
1039
|
-
|
|
1040
|
-
Почему так: каждому дополнительному имени хоста нужна своя DNS-запись **и** свой SAN в TLS-сертификате. Если имени нет в сертификате, TLS-рукопожатие падает, и клиент Gemini показывает невнятную ошибку «не удаётся подключиться к хосту». С одним общим доменом сертификат покрывает все узлы сразу, а добавление узла — это только вызов регистрации.
|
|
1041
|
-
|
|
1042
|
-
- Общий домен по умолчанию `smart-server.online`; переопределяется через `./install.sh --domain=<общий-домен>` (на стороне узла) или `MESH_PUBLIC_URL` (на стороне шлюза).
|
|
1043
|
-
- Старые ссылки с субдоменами устройства продолжают работать для совместимости и пишут предупреждение в лог; `MESH_LEGACY_SUBDOMAIN=0` на шлюзе отключает их.
|
|
1044
|
-
- Установщик теперь записывает разрешённый домен в файл домена, чтобы публичные ссылки узла, его туннель и шлюз называли один и тот же хост: `MESH_PUBLIC_URL=https://<общий-домен>` в `domain.env` — в обеих ветках установки (standalone и «облачный шлюз/туннель» сразу после `agent.env`). Значение сначала нормализуется, плейсхолдер `__MESH_DOMAIN__` и пустое значение не пишутся никогда, уже названный хост не переписывается, а неудачная запись не фатальна — установщик печатает точную команду для ручного запуска. Встроенное значение по умолчанию не записывается вообще: это запасной вариант, а не конфигурация, и закрепление его в файле заставило бы `core/domain.py` вовсе перестать смотреть на устаревший `MESH_GATEWAY`, так что позднейшая правка `agent.env` молча игнорировалась бы — потерь нет, потому что без файла то же значение и есть последний запасной вариант резолвера. Сухой прогон сообщает файл и значение, которое было бы записано, — включая `not written (the built-in default is a fallback, not a configuration)`, — и по-прежнему ничего не меняет на диске.
|
|
1045
|
-
- Зачем это нужно: ссылки на файлы строит `core/domain.py` по цепочке `MESH_PUBLIC_URL` → `AGY_PUBLIC_BASE_URL` → файл `domain.env` → встроенное значение по умолчанию, а устаревший `MESH_GATEWAY` из `agent.env` он намеренно не читает (его читает только `gateway_host()` — хост туннеля). Поэтому узел, поставленный обычным способом (`agent.env` с `MESH_GATEWAY`/`MESH_USER`/`MESH_TOKEN` и без файла домена), звонил на правильный шлюз, а ссылки публиковал на встроенном домене. На уже установленном узле: повторите установку или допишите одну строку вручную — `mkdir -p ~/.config/antigravity-mesh && echo 'MESH_PUBLIC_URL=https://<домен>' > ~/.config/antigravity-mesh/domain.env`, в Linux то же через `sudo tee /etc/antigravity-mesh/domain.env`. Уже выданные ссылки продолжают работать, новые берут настроенный домен.
|
|
1046
|
-
- `MESH_DOMAIN_FILE` переопределяет только *путь*, и задавать его нужно в конфигурации самого узла, а не в запуске установщика: держите его в `agent.env` (все ключи `MESH_*` оттуда экспортируются узлу), иначе домен окажется в файле, который узел никогда не прочитает. Каталог того файла, который реально используется, установщик создаёт сам. При `--ssh=<хост>` разрешённый здесь домен передаётся удалённому `install.sh` как `--domain=`, поэтому цель записывает именно этот хост, а не разрешает домен заново (копия из репозитория с плейсхолдером `__MESH_DOMAIN__` иначе ушла бы на встроенное значение).
|
|
1047
|
-
|
|
1048
|
-
---
|
|
1049
|
-
|
|
1050
|
-
## 🔒 Безопасность
|
|
1051
|
-
|
|
1052
|
-
- **Доступ только по токену** — каждый запрос должен содержать секретный токен узла.
|
|
1053
|
-
- **Только исходящий туннель** — узлы не слушают публичные порты; агент сам подключается к шлюзу.
|
|
1054
|
-
- **Шлюз — только релей** — он терминирует TLS и передаёт вызовы; команды выполняются только на вашем узле.
|
|
1055
|
-
- **Без root по умолчанию** — агент работает в пространстве пользователя.
|
|
1056
|
-
|
|
1057
|
-
> [!WARNING]
|
|
1058
|
-
> Любой, у кого есть ваша MCP-ссылка, может выполнять команды на этой машине. Храните её как пароль: не публикуйте, а при утечке переустановите узел, чтобы сменить токен.
|
|
1059
|
-
|
|
1060
|
-
---
|
|
1061
|
-
|
|
1062
|
-
## 🚢 Свой шлюз (self-hosting)
|
|
1063
|
-
|
|
1064
|
-
Шлюз — один хост на все узлы. Публикуйте его только скриптом, чтобы работающий шлюз, отдаваемые установщики и bootstrap-код узлов не расходились:
|
|
1065
|
-
|
|
1066
|
-
```bash
|
|
1067
|
-
MESH_GATEWAY_SSH=root@<ip-шлюза> ./deploy_gateway.sh # задеплоить
|
|
1068
|
-
MESH_GATEWAY_SSH=root@<ip-шлюза> ./deploy_gateway.sh --dry-run # предпросмотр
|
|
1069
|
-
```
|
|
1070
|
-
Скрипт проверяет каждую загрузку манифестом sha256 на хосте, делает бэкап с меткой времени, ставит файлы с нужным владельцем, перезапускает службу и завершает публичной проверкой здоровья. При несовпадении хэшей ничего не устанавливается.
|
|
1071
|
-
|
|
1072
|
-
> [!TIP]
|
|
1073
|
-
> **Домен задаётся в одном месте.** `MESH_PUBLIC_URL` в `domain.env`
|
|
1074
|
-
> (`/etc/antigravity-mesh/domain.env` в Linux, `%USERPROFILE%\.config\antigravity-mesh\domain.env`
|
|
1075
|
-
> в Windows) — единственная настройка, где он назван: узел, шлюз, установщики, публичные ссылки
|
|
1076
|
-
> на файлы и конфиги nginx берут его через `core/domain.py`. Смена домена — это одно значение и
|
|
1077
|
-
> `ops/nginx/render-domain.sh --apply`, подробности в [docs/DOMAIN.md](docs/DOMAIN.md).
|
|
1078
|
-
|
|
1079
|
-
> [!IMPORTANT]
|
|
1080
|
-
> **Кодировка `install.ps1` — один файл, два представления.** В репозитории файл лежит в UTF-8 *с* меткой BOM: Windows PowerShell 5.1 читает скрипт без метки в ANSI-кодировке, русский текст превращается в «умные кавычки», и парсер отвергает файл целиком — то есть `.\install.ps1` из клона просто не запустится. Копию, которую отдаёт шлюз, `deploy_gateway.sh` записывает *без* метки: `irm … | iex` получает метку как часть первого токена, и `param(...)` перестаёт быть первым оператором. Для этого адреса nginx объявляет `charset utf-8`, поэтому копия без метки читается корректно. Пожалуйста, сохраняйте оба свойства при правке файла.
|
|
1081
|
-
|
|
1082
|
-
**Политика TLS-сертификата: только общий домен, без SAN на устройства.** Имена узлов в сертификат не добавляются никогда — узел выбирается `?user=`:
|
|
1083
|
-
|
|
1084
|
-
```bash
|
|
1085
|
-
sudo certbot certificates | grep -A1 'Certificate Name: smart-server.online'
|
|
1086
|
-
sudo certbot renew --dry-run --cert-name smart-server.online # проверка продления
|
|
1087
|
-
```
|
|
1088
|
-
Старые SAN'ы устройств могут остаться с прежнего контракта: они безвредны и поддерживают совместимость старых ссылок. Убрать их можно при следующем продлении, когда субдоменными ссылками никто не пользуется.
|
|
1089
|
-
|
|
1090
|
-
---
|
|
1091
|
-
|
|
1092
|
-
## 📄 Лицензия
|
|
17
|
+
`gemini-computer-use` contains no code of its own: it depends on
|
|
18
|
+
`remote-harness-mcp` and forwards every argument to that package's CLI. An
|
|
19
|
+
existing `gemini-computer-use` install therefore keeps receiving the current
|
|
20
|
+
version instead of a frozen copy.
|
|
1093
21
|
|
|
1094
|
-
|
|
22
|
+
* Repository: <https://github.com/LevRa7/remote-harness-mcp>
|
|
23
|
+
* What the CLI can do (`doctor`, `restart`, `update`, installer bootstrap):
|
|
24
|
+
see the [main README](https://github.com/LevRa7/remote-harness-mcp#readme).
|
|
25
|
+
* Why the rename happened, and what stays compatible:
|
|
26
|
+
[docs/UPDATES.md § 0](https://github.com/LevRa7/remote-harness-mcp/blob/main/docs/UPDATES.md).
|