gemini-computer-use 0.2.4 → 0.4.1

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.
Files changed (41) hide show
  1. package/README.md +733 -16
  2. package/bin/cli.js +112 -17
  3. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  4. package/core/__pycache__/agent.cpython-313.pyc +0 -0
  5. package/core/__pycache__/code_cache.cpython-313.pyc +0 -0
  6. package/core/__pycache__/mcp_tools.cpython-313.pyc +0 -0
  7. package/core/__pycache__/schemas.cpython-313.pyc +0 -0
  8. package/core/__pycache__/server.cpython-313.pyc +0 -0
  9. package/core/__pycache__/vitals.cpython-313.pyc +0 -0
  10. package/core/__pycache__/web_share.cpython-313.pyc +0 -0
  11. package/core/agent.py +503 -18
  12. package/core/agent_harness.py +9 -2
  13. package/core/code_cache.py +15 -3
  14. package/core/device.py +2400 -0
  15. package/core/domain.py +303 -0
  16. package/core/mcp_tools.py +1840 -112
  17. package/core/policies.py +35 -5
  18. package/core/server.py +78 -32
  19. package/core/termux.py +916 -0
  20. package/core/updater.py +1713 -0
  21. package/core/version.py +31 -0
  22. package/core/vitals.py +66 -2
  23. package/core/web_share.py +814 -0
  24. package/gateway.py +1469 -98
  25. package/install-gui.cmd +40 -0
  26. package/install-gui.ps1 +1882 -0
  27. package/install-gui.strings.json +211 -0
  28. package/install.ps1 +960 -92
  29. package/install.sh +1214 -81
  30. package/ops/doctor.ps1 +735 -0
  31. package/ops/mesh-domain.sh +86 -0
  32. package/ops/nginx/README.md +77 -0
  33. package/ops/nginx/antigravity-mesh-mcp.conf +172 -0
  34. package/ops/nginx/mesh-domain.vhost.template +58 -0
  35. package/ops/nginx/render-domain.sh +123 -0
  36. package/ops/update.ps1 +373 -0
  37. package/ops/windows/README.md +298 -0
  38. package/ops/windows/agent-watchdog.ps1 +575 -0
  39. package/ops/windows/run-hidden.vbs +64 -0
  40. package/package.json +6 -2
  41. package/skills/orchestrator.md +12 -0
package/core/policies.py CHANGED
@@ -5,12 +5,27 @@ from google.antigravity import types
5
5
  from google.antigravity.hooks import policy
6
6
  from google.antigravity.hooks.policy import Policy, Decision
7
7
 
8
+ def _platform_root(posix_root: str) -> str:
9
+ """Express a documented deployment root for the host running this node.
10
+
11
+ ``/root/...`` is an absolute path on Linux but only drive-relative on Windows,
12
+ where the SDK refuses it outright ("app_data_dir must be an absolute path") and
13
+ ``os.makedirs`` would scatter a ``\\root\\...`` tree beside the drive root. The
14
+ POSIX path is kept unchanged on POSIX hosts, so deployed services do not move.
15
+ """
16
+ if os.name != "nt":
17
+ return posix_root
18
+ leaf = posix_root.rstrip("/").rsplit("/", 1)[-1]
19
+ return os.path.join(os.path.expanduser("~"), leaf)
20
+
21
+
8
22
  # Workspace roots are read from the environment ONLY -- never hardcode a personal
9
23
  # home directory. Point MESH_COORDINATOR_WORKSPACE / MESH_WORKSPACE at the real
10
- # deployment paths in the service environment.
24
+ # deployment paths in the service environment. The documented defaults are the
25
+ # Linux deployment paths; on Windows the same roots live under the user profile.
11
26
  ALLOWED_WORKSPACES = [
12
- os.environ.get("MESH_COORDINATOR_WORKSPACE", "/root/agy-gdrive-runner"),
13
- os.environ.get("MESH_WORKSPACE", "/root/antigravity-mesh"),
27
+ os.environ.get("MESH_COORDINATOR_WORKSPACE") or _platform_root("/root/agy-gdrive-runner"),
28
+ os.environ.get("MESH_WORKSPACE") or _platform_root("/root/antigravity-mesh"),
14
29
  ]
15
30
 
16
31
  SAFE_COMMAND_PREFIXES = [
@@ -40,12 +55,27 @@ DESTRUCTIVE_COMMAND_PATTERNS = [
40
55
  re.compile(r"\bdd\s+"),
41
56
  ]
42
57
 
58
+ def _is_within(path: str, root: str) -> bool:
59
+ """True when *path* is *root* itself or lives inside it.
60
+
61
+ The comparison has to use the platform separator: ``root + "/"`` never
62
+ matches a normpath'ed Windows path, so every file inside an allowed workspace
63
+ was rejected there and the policy could not be satisfied at all.
64
+ """
65
+ if path == root:
66
+ return True
67
+ root = root.rstrip("/\\")
68
+ for sep in {os.sep, "/", "\\"}:
69
+ if path.startswith(root + sep):
70
+ return True
71
+ return False
72
+
73
+
43
74
  def validate_workspace_path(path: str, allowed_workspaces: Optional[List[str]] = None) -> bool:
44
75
  workspaces = allowed_workspaces or ALLOWED_WORKSPACES
45
76
  abs_path = os.path.abspath(path)
46
77
  for ws in workspaces:
47
- ws_abs = os.path.abspath(ws)
48
- if abs_path == ws_abs or abs_path.startswith(ws_abs.rstrip("/") + "/"):
78
+ if _is_within(abs_path, os.path.abspath(ws)):
49
79
  return True
50
80
  return False
51
81
 
package/core/server.py CHANGED
@@ -169,47 +169,72 @@ def get_coordinator_vitals() -> "NodeVitals":
169
169
  except Exception:
170
170
  load = 0.0
171
171
 
172
+ # ``/proc/meminfo`` and ``/proc/uptime`` are Linux-only. Ask the shared
173
+ # cross-platform collector first (core.vitals knows the Windows and macOS
174
+ # paths), and keep the inline /proc parsing as the last resort.
175
+ native: Optional[Dict[str, Any]] = None
176
+ if _core_get_host_vitals is not None:
177
+ try:
178
+ candidate = _core_get_host_vitals()
179
+ if isinstance(candidate, dict):
180
+ native = candidate
181
+ except Exception:
182
+ native = None
183
+
172
184
  total_mb = 1
173
185
  used_mb = 0
174
- try:
175
- with open("/proc/meminfo", "r") as f:
176
- lines = f.readlines()
177
- mem = {}
178
- for line in lines:
179
- parts = line.split(":")
180
- if len(parts) == 2:
181
- k = parts[0].strip()
182
- v = parts[1].strip().split()[0]
183
- if k in ["MemTotal", "MemAvailable"]:
184
- mem[k] = int(v) // 1024
185
- if "MemTotal" in mem and "MemAvailable" in mem:
186
- total_mb = mem["MemTotal"]
187
- avail_mb = mem["MemAvailable"]
188
- used_mb = max(0, total_mb - avail_mb)
189
- except Exception:
190
- pass
186
+ if native:
187
+ ram = native.get("ram") or {}
188
+ try:
189
+ total_mb = int(ram.get("total_mb") or 0) or 1
190
+ used_mb = max(0, int(ram.get("used_mb") or 0))
191
+ except (TypeError, ValueError):
192
+ total_mb, used_mb = 1, 0
193
+ else:
194
+ try:
195
+ with open("/proc/meminfo", "r") as f:
196
+ lines = f.readlines()
197
+ mem = {}
198
+ for line in lines:
199
+ parts = line.split(":")
200
+ if len(parts) == 2:
201
+ k = parts[0].strip()
202
+ v = parts[1].strip().split()[0]
203
+ if k in ["MemTotal", "MemAvailable"]:
204
+ mem[k] = int(v) // 1024
205
+ if "MemTotal" in mem and "MemAvailable" in mem:
206
+ total_mb = mem["MemTotal"]
207
+ avail_mb = mem["MemAvailable"]
208
+ used_mb = max(0, total_mb - avail_mb)
209
+ except Exception:
210
+ pass
191
211
 
192
212
  usage_pct = round((used_mb / total_mb) * 100.0, 2) if total_mb > 0 else 0.0
193
213
  usage_pct = min(100.0, max(0.0, usage_pct))
194
214
 
195
215
  total_disk_gb = 1.0
196
216
  free_disk_gb = 0.0
197
- try:
198
- d = shutil.disk_usage("/")
199
- total_disk_gb = round(d.total / (1024 ** 3), 2)
200
- free_disk_gb = round(d.free / (1024 ** 3), 2)
201
- except Exception:
202
- pass
217
+ if native:
218
+ disk = native.get("disk") or {}
219
+ try:
220
+ total_disk_gb = float(disk.get("total_gb") or 0.0) or 1.0
221
+ free_disk_gb = max(0.0, float(disk.get("free_gb") or 0.0))
222
+ except (TypeError, ValueError):
223
+ total_disk_gb, free_disk_gb = 1.0, 0.0
224
+ else:
225
+ try:
226
+ d = shutil.disk_usage("C:\\" if platform.system() == "Windows" else "/")
227
+ total_disk_gb = round(d.total / (1024 ** 3), 2)
228
+ free_disk_gb = round(d.free / (1024 ** 3), 2)
229
+ except Exception:
230
+ pass
203
231
 
204
232
  uptime_str = "unknown"
205
- try:
206
- with open("/proc/uptime", "r") as f:
207
- up_secs = float(f.read().split()[0])
208
- hours = int(up_secs // 3600)
209
- mins = int((up_secs % 3600) // 60)
210
- uptime_str = f"{hours}h {mins}m"
211
- except Exception:
212
- pass
233
+ uptime_seconds = mcp_tools.host_uptime_seconds()
234
+ if uptime_seconds is not None:
235
+ hours = int(uptime_seconds // 3600)
236
+ mins = int((uptime_seconds % 3600) // 60)
237
+ uptime_str = f"{hours}h {mins}m"
213
238
 
214
239
  fields = dict(
215
240
  hostname=hostname,
@@ -299,6 +324,9 @@ _REQUIRED_ARGS: Dict[str, Tuple[str, ...]] = {
299
324
  "run_job": ("command",),
300
325
  "job_output": ("job_id",),
301
326
  "job_kill": ("job_id",),
327
+ "share_file": ("path",),
328
+ "serve_dir": ("path",),
329
+ "unshare": ("name",),
302
330
  }
303
331
 
304
332
 
@@ -440,7 +468,16 @@ class AntigravityMeshServer(object):
440
468
  "resources": {"subscribe": False, "listChanged": False},
441
469
  },
442
470
  "serverInfo": {"name": self.name, "version": self.version},
443
- "instructions": "Standalone Local Antigravity Mesh Server.",
471
+ # Short on purpose: the tool descriptions carry the detail. The
472
+ # one thing worth saying up front is where the device branch
473
+ # lives, because a phone node (Termux) is exactly the case where
474
+ # a model otherwise hand-writes termux-* commands through
475
+ # bash_exec instead of using the typed tools.
476
+ "instructions": ("Standalone Local Antigravity Mesh Server. "
477
+ "For the device this node runs on - battery, signal, "
478
+ "time, sensors, cameras, microphones, notifications, "
479
+ "torch, volume - use device_info / device_control; "
480
+ "device_capture and device_messages ask the user first."),
444
481
  },
445
482
  }
446
483
 
@@ -613,6 +650,15 @@ def configure_tools_from_env() -> None:
613
650
  write_roots=os.environ.get("MESH_WRITE_ROOTS") or None,
614
651
  jobs_dir=os.environ.get("MESH_JOBS_DIR") or None,
615
652
  max_output_chars=os.environ.get("MESH_MAX_OUTPUT_CHARS"),
653
+ # Public shares. ``public_url`` is deliberately NOT passed: the public
654
+ # domain is resolved by core/domain.py (MESH_PUBLIC_URL, domain.env, then
655
+ # the one default), and pinning it here to the environment variable alone
656
+ # would bypass the domain file.
657
+ web_dir=os.environ.get("MESH_WEB_DIR") or None,
658
+ mesh_user=os.environ.get("MESH_USER") or None,
659
+ max_share_bytes=os.environ.get("MESH_WEB_MAX_BYTES"),
660
+ max_shares=os.environ.get("MESH_WEB_MAX_SHARES"),
661
+ web_listing=os.environ.get("MESH_WEB_LISTING"),
616
662
  )
617
663
 
618
664