clonebox 0.1.9__py3-none-any.whl → 0.1.10__py3-none-any.whl

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.
clonebox/cli.py CHANGED
@@ -445,7 +445,72 @@ def cmd_start(args):
445
445
  return
446
446
 
447
447
  cloner = SelectiveVMCloner(user_session=getattr(args, "user", False))
448
- cloner.start_vm(name, open_viewer=not args.no_viewer, console=console)
448
+ open_viewer = getattr(args, "viewer", False) or not getattr(args, "no_viewer", False)
449
+ cloner.start_vm(name, open_viewer=open_viewer, console=console)
450
+
451
+
452
+ def cmd_open(args):
453
+ """Open VM viewer window."""
454
+ import subprocess
455
+
456
+ name = args.name
457
+ user_session = getattr(args, "user", False)
458
+ conn_uri = "qemu:///session" if user_session else "qemu:///system"
459
+
460
+ # If name is a path, load config
461
+ if name and (name.startswith(".") or name.startswith("/") or name.startswith("~")):
462
+ target_path = Path(name).expanduser().resolve()
463
+ config_file = target_path / ".clonebox.yaml" if target_path.is_dir() else target_path
464
+ if config_file.exists():
465
+ config = load_clonebox_config(config_file)
466
+ name = config["vm"]["name"]
467
+ else:
468
+ console.print(f"[red]❌ Config not found: {config_file}[/]")
469
+ return
470
+ elif name == "." or not name:
471
+ config_file = Path.cwd() / ".clonebox.yaml"
472
+ if config_file.exists():
473
+ config = load_clonebox_config(config_file)
474
+ name = config["vm"]["name"]
475
+ else:
476
+ console.print("[red]❌ No VM name specified and no .clonebox.yaml in current directory[/]")
477
+ console.print("[dim]Usage: clonebox open <vm-name> or clonebox open .[/]")
478
+ return
479
+
480
+ # Check if VM is running
481
+ try:
482
+ result = subprocess.run(
483
+ ["virsh", "--connect", conn_uri, "domstate", name],
484
+ capture_output=True, text=True, timeout=10
485
+ )
486
+ state = result.stdout.strip()
487
+
488
+ if state != "running":
489
+ console.print(f"[yellow]⚠️ VM '{name}' is not running (state: {state})[/]")
490
+ if questionary.confirm(
491
+ f"Start VM '{name}' and open viewer?", default=True, style=custom_style
492
+ ).ask():
493
+ cloner = SelectiveVMCloner(user_session=user_session)
494
+ cloner.start_vm(name, open_viewer=True, console=console)
495
+ else:
496
+ console.print("[dim]Use 'clonebox start' to start the VM first.[/]")
497
+ return
498
+ except Exception as e:
499
+ console.print(f"[red]❌ Error checking VM state: {e}[/]")
500
+ return
501
+
502
+ # Open virt-viewer
503
+ console.print(f"[cyan]Opening viewer for VM: {name}[/]")
504
+ try:
505
+ subprocess.run(
506
+ ["virt-viewer", "--connect", conn_uri, name],
507
+ check=True
508
+ )
509
+ except FileNotFoundError:
510
+ console.print("[red]❌ virt-viewer not found[/]")
511
+ console.print("Install with: sudo apt install virt-viewer")
512
+ except subprocess.CalledProcessError as e:
513
+ console.print(f"[red]❌ Failed to open viewer: {e}[/]")
449
514
 
450
515
 
451
516
  def cmd_stop(args):
@@ -924,6 +989,217 @@ def cmd_import(args):
924
989
  shutil.rmtree(temp_dir)
925
990
 
926
991
 
992
+ def cmd_test(args):
993
+ """Test VM configuration and health."""
994
+ import subprocess
995
+ import json
996
+
997
+ name = args.name
998
+ user_session = getattr(args, "user", False)
999
+ quick = getattr(args, "quick", False)
1000
+ verbose = getattr(args, "verbose", False)
1001
+ conn_uri = "qemu:///session" if user_session else "qemu:///system"
1002
+
1003
+ # If name is a path, load config
1004
+ if name and (name.startswith(".") or name.startswith("/") or name.startswith("~")):
1005
+ target_path = Path(name).expanduser().resolve()
1006
+ config_file = target_path / ".clonebox.yaml" if target_path.is_dir() else target_path
1007
+ if not config_file.exists():
1008
+ console.print(f"[red]❌ Config not found: {config_file}[/]")
1009
+ return
1010
+ else:
1011
+ config_file = Path.cwd() / ".clonebox.yaml"
1012
+ if not config_file.exists():
1013
+ console.print("[red]❌ No .clonebox.yaml found in current directory[/]")
1014
+ return
1015
+
1016
+ console.print(f"[bold cyan]🧪 Testing VM configuration: {config_file}[/]\n")
1017
+
1018
+ # Load config
1019
+ try:
1020
+ config = load_clonebox_config(config_file)
1021
+ vm_name = config["vm"]["name"]
1022
+ console.print(f"[green]✅ Config loaded successfully[/]")
1023
+ console.print(f" VM Name: {vm_name}")
1024
+ console.print(f" RAM: {config['vm']['ram_mb']}MB")
1025
+ console.print(f" vCPUs: {config['vm']['vcpus']}")
1026
+ console.print(f" GUI: {'Yes' if config['vm']['gui'] else 'No'}")
1027
+ except Exception as e:
1028
+ console.print(f"[red]❌ Failed to load config: {e}[/]")
1029
+ return
1030
+
1031
+ console.print()
1032
+
1033
+ # Test 1: Check VM exists
1034
+ console.print("[bold]1. VM Existence Check[/]")
1035
+ try:
1036
+ result = subprocess.run(
1037
+ ["virsh", "--connect", conn_uri, "dominfo", vm_name],
1038
+ capture_output=True, text=True, timeout=10
1039
+ )
1040
+ if result.returncode == 0:
1041
+ console.print("[green]✅ VM is defined in libvirt[/]")
1042
+ if verbose:
1043
+ for line in result.stdout.split('\n'):
1044
+ if ':' in line:
1045
+ console.print(f" {line}")
1046
+ else:
1047
+ console.print("[red]❌ VM not found in libvirt[/]")
1048
+ console.print(" Run: clonebox create .clonebox.yaml --start")
1049
+ return
1050
+ except Exception as e:
1051
+ console.print(f"[red]❌ Error checking VM: {e}[/]")
1052
+ return
1053
+
1054
+ console.print()
1055
+
1056
+ # Test 2: Check VM state
1057
+ console.print("[bold]2. VM State Check[/]")
1058
+ try:
1059
+ result = subprocess.run(
1060
+ ["virsh", "--connect", conn_uri, "domstate", vm_name],
1061
+ capture_output=True, text=True, timeout=10
1062
+ )
1063
+ state = result.stdout.strip()
1064
+ if state == "running":
1065
+ console.print("[green]✅ VM is running[/]")
1066
+
1067
+ # Test network if running
1068
+ console.print("\n Checking network...")
1069
+ try:
1070
+ result = subprocess.run(
1071
+ ["virsh", "--connect", conn_uri, "domifaddr", vm_name],
1072
+ capture_output=True, text=True, timeout=10
1073
+ )
1074
+ if "192.168" in result.stdout or "10.0" in result.stdout:
1075
+ console.print("[green]✅ VM has network access[/]")
1076
+ if verbose:
1077
+ for line in result.stdout.split('\n'):
1078
+ if '192.168' in line or '10.0' in line:
1079
+ console.print(f" IP: {line.split()[-1]}")
1080
+ else:
1081
+ console.print("[yellow]⚠️ No IP address detected[/]")
1082
+ except:
1083
+ console.print("[yellow]⚠️ Could not check network[/]")
1084
+ else:
1085
+ console.print(f"[yellow]⚠️ VM is not running (state: {state})[/]")
1086
+ console.print(" Run: clonebox start .")
1087
+ except Exception as e:
1088
+ console.print(f"[red]❌ Error checking VM state: {e}[/]")
1089
+
1090
+ console.print()
1091
+
1092
+ # Test 3: Check cloud-init status (if running)
1093
+ if not quick and state == "running":
1094
+ console.print("[bold]3. Cloud-init Status[/]")
1095
+ try:
1096
+ # Try to get cloud-init status via QEMU guest agent
1097
+ result = subprocess.run(
1098
+ ["virsh", "--connect", conn_uri, "qemu-agent-command", vm_name,
1099
+ '{"execute":"guest-exec","arguments":{"path":"cloud-init","arg":["status"],"capture-output":true}}'],
1100
+ capture_output=True, text=True, timeout=15
1101
+ )
1102
+ if result.returncode == 0:
1103
+ try:
1104
+ response = json.loads(result.stdout)
1105
+ if "return" in response:
1106
+ pid = response["return"]["pid"]
1107
+ # Get output
1108
+ result2 = subprocess.run(
1109
+ ["virsh", "--connect", conn_uri, "qemu-agent-command", vm_name,
1110
+ f'{{"execute":"guest-exec-status","arguments":{"pid":{pid}}}}'],
1111
+ capture_output=True, text=True, timeout=15
1112
+ )
1113
+ if result2.returncode == 0:
1114
+ resp2 = json.loads(result2.stdout)
1115
+ if "return" in resp2 and resp2["return"]["exited"]:
1116
+ output = resp2["return"]["out-data"]
1117
+ if output:
1118
+ import base64
1119
+ status = base64.b64decode(output).decode()
1120
+ if "done" in status.lower():
1121
+ console.print("[green]✅ Cloud-init completed[/]")
1122
+ elif "running" in status.lower():
1123
+ console.print("[yellow]⚠️ Cloud-init still running[/]")
1124
+ else:
1125
+ console.print(f"[yellow]⚠️ Cloud-init status: {status.strip()}[/]")
1126
+ except:
1127
+ pass
1128
+ except:
1129
+ console.print("[yellow]⚠️ Could not check cloud-init (QEMU agent may not be running)[/]")
1130
+
1131
+ console.print()
1132
+
1133
+ # Test 4: Check mounts (if running)
1134
+ if not quick and state == "running":
1135
+ console.print("[bold]4. Mount Points Check[/]")
1136
+ all_paths = config.get("paths", {}).copy()
1137
+ all_paths.update(config.get("app_data_paths", {}))
1138
+
1139
+ if all_paths:
1140
+ for idx, (host_path, guest_path) in enumerate(all_paths.items()):
1141
+ try:
1142
+ result = subprocess.run(
1143
+ ["virsh", "--connect", conn_uri, "qemu-agent-command", vm_name,
1144
+ f'{{"execute":"guest-exec","arguments":{{"path":"test","arg":["-d","{guest_path}"],"capture-output":true}}}}'],
1145
+ capture_output=True, text=True, timeout=10
1146
+ )
1147
+ if result.returncode == 0:
1148
+ try:
1149
+ response = json.loads(result.stdout)
1150
+ if "return" in response:
1151
+ pid = response["return"]["pid"]
1152
+ result2 = subprocess.run(
1153
+ ["virsh", "--connect", conn_uri, "qemu-agent-command", vm_name,
1154
+ f'{{"execute":"guest-exec-status","arguments":{"pid":{pid}}}}'],
1155
+ capture_output=True, text=True, timeout=10
1156
+ )
1157
+ if result2.returncode == 0:
1158
+ resp2 = json.loads(result2.stdout)
1159
+ if "return" in resp2 and resp2["return"]["exited"]:
1160
+ exit_code = resp2["return"]["exitcode"]
1161
+ if exit_code == 0:
1162
+ console.print(f"[green]✅ {guest_path}[/]")
1163
+ else:
1164
+ console.print(f"[red]❌ {guest_path} (not accessible)[/]")
1165
+ continue
1166
+ except:
1167
+ pass
1168
+ console.print(f"[yellow]⚠️ {guest_path} (unknown)[/]")
1169
+ except:
1170
+ console.print(f"[yellow]⚠️ {guest_path} (could not check)[/]")
1171
+ else:
1172
+ console.print("[dim]No mount points configured[/]")
1173
+
1174
+ console.print()
1175
+
1176
+ # Test 5: Run health check (if running and not quick)
1177
+ if not quick and state == "running":
1178
+ console.print("[bold]5. Health Check[/]")
1179
+ try:
1180
+ result = subprocess.run(
1181
+ ["virsh", "--connect", conn_uri, "qemu-agent-command", vm_name,
1182
+ '{"execute":"guest-exec","arguments":{"path":"/usr/local/bin/clonebox-health","capture-output":true}}'],
1183
+ capture_output=True, text=True, timeout=60
1184
+ )
1185
+ if result.returncode == 0:
1186
+ console.print("[green]✅ Health check triggered[/]")
1187
+ console.print(" View results in VM: cat /var/log/clonebox-health.log")
1188
+ else:
1189
+ console.print("[yellow]⚠️ Health check script not found[/]")
1190
+ console.print(" VM may not have been created with health checks")
1191
+ except Exception as e:
1192
+ console.print(f"[yellow]⚠️ Could not run health check: {e}[/]")
1193
+
1194
+ console.print()
1195
+
1196
+ # Summary
1197
+ console.print("[bold]Test Summary[/]")
1198
+ console.print("VM configuration is valid and VM is accessible.")
1199
+ console.print("\n[dim]For detailed health report, run in VM:[/]")
1200
+ console.print("[dim] cat /var/log/clonebox-health.log[/]")
1201
+
1202
+
927
1203
  CLONEBOX_CONFIG_FILE = ".clonebox.yaml"
928
1204
  CLONEBOX_ENV_FILE = ".env"
929
1205
 
@@ -1527,6 +1803,7 @@ def main():
1527
1803
  "name", nargs="?", default=None, help="VM name or '.' to use .clonebox.yaml"
1528
1804
  )
1529
1805
  start_parser.add_argument("--no-viewer", action="store_true", help="Don't open virt-viewer")
1806
+ start_parser.add_argument("--viewer", action="store_true", help="Open virt-viewer GUI")
1530
1807
  start_parser.add_argument(
1531
1808
  "-u",
1532
1809
  "--user",
@@ -1535,6 +1812,19 @@ def main():
1535
1812
  )
1536
1813
  start_parser.set_defaults(func=cmd_start)
1537
1814
 
1815
+ # Open command - open VM viewer
1816
+ open_parser = subparsers.add_parser("open", help="Open VM viewer window")
1817
+ open_parser.add_argument(
1818
+ "name", nargs="?", default=None, help="VM name or '.' to use .clonebox.yaml"
1819
+ )
1820
+ open_parser.add_argument(
1821
+ "-u",
1822
+ "--user",
1823
+ action="store_true",
1824
+ help="Use user session (qemu:///session) - no root required",
1825
+ )
1826
+ open_parser.set_defaults(func=cmd_open)
1827
+
1538
1828
  # Stop command
1539
1829
  stop_parser = subparsers.add_parser("stop", help="Stop a VM")
1540
1830
  stop_parser.add_argument("name", help="VM name")
@@ -1660,6 +1950,22 @@ def main():
1660
1950
  )
1661
1951
  import_parser.set_defaults(func=cmd_import)
1662
1952
 
1953
+ # Test command - validate VM configuration
1954
+ test_parser = subparsers.add_parser("test", help="Test VM configuration and health")
1955
+ test_parser.add_argument(
1956
+ "name", nargs="?", default=None, help="VM name or '.' to use .clonebox.yaml"
1957
+ )
1958
+ test_parser.add_argument(
1959
+ "-u", "--user", action="store_true", help="Use user session (qemu:///session)"
1960
+ )
1961
+ test_parser.add_argument(
1962
+ "--quick", action="store_true", help="Quick test (no deep health checks)"
1963
+ )
1964
+ test_parser.add_argument(
1965
+ "--verbose", "-v", action="store_true", help="Verbose output"
1966
+ )
1967
+ test_parser.set_defaults(func=cmd_test)
1968
+
1663
1969
  args = parser.parse_args()
1664
1970
 
1665
1971
  if hasattr(args, "func"):
clonebox/cloner.py CHANGED
@@ -417,9 +417,10 @@ class SelectiveVMCloner:
417
417
  ET.SubElement(cdrom, "readonly")
418
418
 
419
419
  # 9p filesystem mounts (bind mounts from host)
420
+ # Use accessmode="mapped" to allow VM user to access host files regardless of UID
420
421
  for idx, (host_path, guest_tag) in enumerate(config.paths.items()):
421
422
  if Path(host_path).exists():
422
- fs = ET.SubElement(devices, "filesystem", type="mount", accessmode="passthrough")
423
+ fs = ET.SubElement(devices, "filesystem", type="mount", accessmode="mapped")
423
424
  ET.SubElement(fs, "driver", type="path", wrpolicy="immediate")
424
425
  ET.SubElement(fs, "source", dir=host_path)
425
426
  # Use simple tag names for 9p mounts
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: Clone your workstation environment to an isolated VM with selective apps, paths and services
5
5
  Author: CloneBox Team
6
6
  License: Apache-2.0
@@ -64,6 +64,41 @@ CloneBox lets you create isolated virtual machines with only the applications, d
64
64
  - 🖥️ **GUI support** - SPICE graphics with virt-viewer integration
65
65
  - ⚡ **Fast creation** - No full disk cloning, VMs are ready in seconds
66
66
  - 📥 **Auto-download** - Automatically downloads and caches Ubuntu cloud images (stored in ~/Downloads)
67
+ - 📊 **Health monitoring** - Built-in health checks for packages, services, and mounts
68
+ - 🔄 **VM migration** - Export/import VMs with data between workstations
69
+ - 🧪 **Configuration testing** - Validate VM settings and functionality
70
+ - 📁 **App data sync** - Include browser profiles, IDE settings, and app configs
71
+
72
+
73
+
74
+
75
+ CloneBox to narzędzie CLI do **szybkiego klonowania aktualnego środowiska workstation do izolowanej maszyny wirtualnej (VM)**.
76
+ Zamiast pełnego kopiowania dysku, używa **bind mounts** (udostępnianie katalogów na żywo) i **cloud-init** do selektywnego przeniesienia tylko potrzebnych elementów: uruchomionych usług (Docker, PostgreSQL, nginx), aplikacji, ścieżek projektów i konfiguracji. Automatycznie pobiera obrazy Ubuntu, instaluje pakiety i uruchamia VM z SPICE GUI. Idealne dla deweloperów na Linuxie – VM powstaje w minuty, bez duplikowania danych.
77
+
78
+ Kluczowe komendy:
79
+ - `clonebox` – interaktywny wizard (detect + create + start)
80
+ - `clonebox detect` – skanuje usługi/apps/ścieżki
81
+ - `clonebox clone . --user --run` – szybki klon bieżącego katalogu z użytkownikiem i autostartem
82
+
83
+ ### Dlaczego wirtualne klony workstation mają sens?
84
+
85
+ **Problem**: Developerzy/Vibecoderzy nie izolują środowisk dev/test (np. dla AI agentów), bo ręczne odtwarzanie setupu to ból – godziny na instalację apps, usług, configów, dotfiles. Przechodzenie z fizycznego PC na VM wymagałoby pełnego rebuilda, co blokuje workflow.
86
+
87
+ **Rozwiązanie CloneBox**: Automatycznie **skanuje i klonuje stan "tu i teraz"** (usługi z `ps`, dockery z `docker ps`, projekty z git/.env). VM dziedziczy środowisko bez kopiowania całego śmietnika – tylko wybrane bind mounty.
88
+
89
+ **Korzyści w twoim kontekście (embedded/distributed systems, AI automation)**:
90
+ - **Sandbox dla eksperymentów**: Testuj AI agenty, edge computing (RPi/ESP32 symulacje) czy Camel/ERP integracje w izolacji, bez psucia hosta.
91
+ - **Reprodukcja workstation**: Na firmowym PC masz setup z domu (Python/Rust/Go envs, Docker compose, Postgres dev DB) – klonujesz i pracujesz identycznie.
92
+ - **Szybkość > dotfiles**: Dotfiles odtwarzają configi, ale nie łapią runtime stanu (uruchomione serwery, otwarte projekty). CloneBox to "snapshot na sterydach".
93
+ - **Bezpieczeństwo/cost-optymalizacja**: Izolacja od plików hosta (tylko mounts), zero downtime, tanie w zasobach (libvirt/QEMU). Dla SME: szybki onboarding dev env bez migracji fizycznej.
94
+ - **AI-friendly**: Agenci LLMs (jak te z twoich hobby) mogą działać w VM z pełnym kontekstem, bez ryzyka "zasmiecania" main PC.
95
+
96
+ Przykład: Masz uruchomiony Kubernetes Podman z twoim home labem + projekt automotive leasing. `clonebox clone ~/projects --run` → VM gotowa w 30s, z tymi samymi serwisami, ale izolowana. Lepsze niż Docker (brak GUI/full OS) czy pełna migracja.
97
+
98
+ **Dlaczego ludzie tego nie robią?** Brak automatyzacji – nikt nie chce ręcznie rebuildować.
99
+ - CloneBox rozwiązuje to jednym poleceniem. Super match dla twoich interesów (distributed infra, AI tools, business automation).
100
+
101
+
67
102
 
68
103
  ## Installation
69
104
 
@@ -142,7 +177,6 @@ Simply run `clonebox` to start the interactive wizard:
142
177
  ```bash
143
178
  clonebox
144
179
  clonebox clone . --user --run --replace --base-image ~/ubuntu-22.04-cloud.qcow2
145
-
146
180
  ```
147
181
 
148
182
  The wizard will:
@@ -181,6 +215,130 @@ clonebox detect --json
181
215
 
182
216
  ## Usage Examples
183
217
 
218
+ ### Basic Workflow
219
+
220
+ ```bash
221
+ # 1. Clone current directory with auto-detection
222
+ clonebox clone . --user
223
+
224
+ # 2. Review generated config
225
+ cat .clonebox.yaml
226
+
227
+ # 3. Create and start VM
228
+ clonebox start . --user --viewer
229
+
230
+ # 4. Check VM status
231
+ clonebox status . --user
232
+
233
+ # 5. Open VM window later
234
+ clonebox open . --user
235
+
236
+ # 6. Stop VM when done
237
+ clonebox stop . --user
238
+ ```
239
+
240
+ ### Development Environment with Browser Profiles
241
+
242
+ ```bash
243
+ # Clone with app data (browser profiles, IDE settings)
244
+ clonebox clone . --user --run
245
+
246
+ # VM will have:
247
+ # - All your project directories
248
+ # - Browser profiles (Chrome, Firefox) with bookmarks and passwords
249
+ # - IDE settings (PyCharm, VSCode)
250
+ # - Docker containers and services
251
+
252
+ # Access in VM:
253
+ ls ~/.config/google-chrome # Chrome profile
254
+ ls ~/.mozilla/firefox # Firefox profile
255
+ ls ~/.config/JetBrains # PyCharm settings
256
+ ```
257
+
258
+ ### Testing VM Configuration
259
+
260
+ ```bash
261
+ # Quick test - basic checks
262
+ clonebox test . --user --quick
263
+
264
+ # Full test with verbose output
265
+ clonebox test . --user --verbose
266
+
267
+ # Test output shows:
268
+ # ✅ VM is defined in libvirt
269
+ # ✅ VM is running
270
+ # ✅ VM has network access (IP: 192.168.122.89)
271
+ # ✅ Cloud-init completed
272
+ # ✅ All mount points accessible
273
+ # ✅ Health check triggered
274
+ ```
275
+
276
+ ### VM Health Monitoring
277
+
278
+ ```bash
279
+ # Check overall status
280
+ clonebox status . --user
281
+
282
+ # Output:
283
+ # 📊 Checking VM status: clone-clonebox
284
+ # ✅ VM State: running
285
+ # ✅ VM has network access
286
+ # ☁️ Cloud-init: Still running (packages installing)
287
+ # 🏥 Health Check Status... ⏳ Health check not yet run
288
+
289
+ # Trigger health check
290
+ clonebox status . --user --health
291
+
292
+ # View detailed health report in VM:
293
+ # cat /var/log/clonebox-health.log
294
+ ```
295
+
296
+ ### Export/Import Workflow
297
+
298
+ ```bash
299
+ # On workstation A - Export VM with all data
300
+ clonebox export . --user --include-data -o my-dev-env.tar.gz
301
+
302
+ # Transfer file to workstation B, then import
303
+ clonebox import my-dev-env.tar.gz --user
304
+
305
+ # Start VM on new workstation
306
+ clonebox start . --user
307
+ clonebox open . --user
308
+
309
+ # VM includes:
310
+ # - Complete disk image
311
+ # - All browser profiles and settings
312
+ # - Project files
313
+ # - Docker images and containers
314
+ ```
315
+
316
+ ### Troubleshooting Common Issues
317
+
318
+ ```bash
319
+ # If mounts are empty after reboot:
320
+ clonebox status . --user # Check VM status
321
+ # Then in VM:
322
+ sudo mount -a # Remount all fstab entries
323
+
324
+ # If browser profiles don't sync:
325
+ rm .clonebox.yaml
326
+ clonebox clone . --user --run --replace
327
+
328
+ # If GUI doesn't open:
329
+ clonebox open . --user # Easiest way
330
+ # or:
331
+ virt-viewer --connect qemu:///session clone-clonebox
332
+
333
+ # Check VM details:
334
+ clonebox list # List all VMs
335
+ virsh --connect qemu:///session dominfo clone-clonebox
336
+ ```
337
+
338
+ ## Legacy Examples (Manual Config)
339
+
340
+ These examples use the older `create` command with manual JSON config. For most users, the `clone` command with auto-detection is easier.
341
+
184
342
  ### Python Development Environment
185
343
 
186
344
  ```bash
@@ -222,17 +380,36 @@ clonebox create --name fullstack --config '{
222
380
 
223
381
  ## Inside the VM
224
382
 
225
- After the VM boots, mount shared directories:
383
+ After the VM boots, shared directories are automatically mounted via fstab entries. You can check their status:
226
384
 
227
385
  ```bash
228
- # Mount shared paths (9p filesystem)
229
- sudo mkdir -p /mnt/projects
230
- sudo mount -t 9p -o trans=virtio,version=9p2000.L mount0 /mnt/projects
386
+ # Check mount status
387
+ mount | grep 9p
231
388
 
232
- # Or add to /etc/fstab for permanent mount
233
- echo "mount0 /mnt/projects 9p trans=virtio,version=9p2000.L 0 0" | sudo tee -a /etc/fstab
389
+ # View health check report
390
+ cat /var/log/clonebox-health.log
391
+
392
+ # Re-run health check manually
393
+ clonebox-health
394
+
395
+ # Check cloud-init status
396
+ sudo cloud-init status
397
+
398
+ # Manual mount (if needed)
399
+ sudo mkdir -p /mnt/projects
400
+ sudo mount -t 9p -o trans=virtio,version=9p2000.L,nofail mount0 /mnt/projects
234
401
  ```
235
402
 
403
+ ### Health Check System
404
+
405
+ CloneBox includes automated health checks that verify:
406
+ - Package installation (apt/snap)
407
+ - Service status
408
+ - Mount points accessibility
409
+ - GUI readiness
410
+
411
+ Health check logs are saved to `/var/log/clonebox-health.log` with a summary in `/var/log/clonebox-health-status`.
412
+
236
413
  ## Architecture
237
414
 
238
415
  ```
@@ -399,6 +576,7 @@ clonebox clone . --network auto
399
576
  | `clonebox clone . --network user` | Use user-mode networking (slirp) |
400
577
  | `clonebox clone . --network auto` | Auto-detect network mode (default) |
401
578
  | `clonebox start .` | Start VM from `.clonebox.yaml` in current dir |
579
+ | `clonebox start . --viewer` | Start VM and open GUI window |
402
580
  | `clonebox start <name>` | Start existing VM by name |
403
581
  | `clonebox stop <name>` | Stop a VM (graceful shutdown) |
404
582
  | `clonebox stop -f <name>` | Force stop a VM |
@@ -408,6 +586,14 @@ clonebox clone . --network auto
408
586
  | `clonebox detect --yaml` | Output as YAML config |
409
587
  | `clonebox detect --yaml --dedupe` | YAML with duplicates removed |
410
588
  | `clonebox detect --json` | Output as JSON |
589
+ | `clonebox status . --user` | Check VM health, cloud-init status, and IP address |
590
+ | `clonebox test . --user` | Test VM configuration and validate all settings |
591
+ | `clonebox export . --user` | Export VM for migration to another workstation |
592
+ | `clonebox export . --user --include-data` | Export VM with browser profiles and configs |
593
+ | `clonebox import archive.tar.gz --user` | Import VM from export archive |
594
+ | `clonebox open . --user` | Open GUI viewer for VM (same as virt-viewer) |
595
+ | `virt-viewer --connect qemu:///session <vm>` | Open GUI for running VM |
596
+ | `virsh --connect qemu:///session console <vm>` | Open text console (Ctrl+] to exit) |
411
597
 
412
598
  ## Requirements
413
599
 
@@ -479,6 +665,102 @@ sudo apt install virt-viewer
479
665
  virt-viewer --connect qemu:///session <vm-name>
480
666
  ```
481
667
 
668
+ ### Browser Profiles Not Syncing
669
+
670
+ If browser profiles or app data aren't available:
671
+
672
+ 1. **Regenerate config with app data:**
673
+ ```bash
674
+ rm .clonebox.yaml
675
+ clonebox clone . --user --run --replace
676
+ ```
677
+
678
+ 2. **Check mount permissions in VM:**
679
+ ```bash
680
+ # Verify mounts are accessible
681
+ ls -la ~/.config/google-chrome
682
+ ls -la ~/.mozilla/firefox
683
+ ```
684
+
685
+ ### Mount Points Empty After Reboot
686
+
687
+ If shared directories appear empty after VM restart:
688
+
689
+ 1. **Check fstab entries:**
690
+ ```bash
691
+ cat /etc/fstab | grep 9p
692
+ ```
693
+
694
+ 2. **Mount manually:**
695
+ ```bash
696
+ sudo mount -a
697
+ ```
698
+
699
+ 3. **Verify access mode:**
700
+ - VMs created with `accessmode="mapped"` allow any user to access mounts
701
+ - Older VMs used `accessmode="passthrough"` which preserves host UIDs
702
+
703
+ ## Advanced Usage
704
+
705
+ ### VM Migration Between Workstations
706
+
707
+ Export your complete VM environment:
708
+
709
+ ```bash
710
+ # Export VM with all data
711
+ clonebox export . --user --include-data -o my-dev-env.tar.gz
712
+
713
+ # Transfer to new workstation, then import
714
+ clonebox import my-dev-env.tar.gz --user
715
+ clonebox start . --user
716
+ ```
717
+
718
+ ### Testing VM Configuration
719
+
720
+ Validate your VM setup:
721
+
722
+ ```bash
723
+ # Quick test (basic checks)
724
+ clonebox test . --user --quick
725
+
726
+ # Full test (includes health checks)
727
+ clonebox test . --user --verbose
728
+ ```
729
+
730
+ ### Monitoring VM Health
731
+
732
+ Check VM status from workstation:
733
+
734
+ ```bash
735
+ # Check VM state, IP, cloud-init, and health
736
+ clonebox status . --user
737
+
738
+ # Trigger health check in VM
739
+ clonebox status . --user --health
740
+ ```
741
+
742
+ ### Reopening VM Window
743
+
744
+ If you close the VM window, you can reopen it:
745
+
746
+ ```bash
747
+ # Open GUI viewer (easiest)
748
+ clonebox open . --user
749
+
750
+ # Start VM and open GUI (if VM is stopped)
751
+ clonebox start . --user --viewer
752
+
753
+ # Open GUI for running VM
754
+ virt-viewer --connect qemu:///session clone-clonebox
755
+
756
+ # List VMs to get the correct name
757
+ clonebox list
758
+
759
+ # Text console (no GUI)
760
+ virsh --connect qemu:///session console clone-clonebox
761
+ # Press Ctrl + ] to exit console
762
+ ```
763
+
482
764
  ## License
483
765
 
484
766
  MIT License - see [LICENSE](LICENSE) file.
@@ -0,0 +1,11 @@
1
+ clonebox/__init__.py,sha256=IOk7G0DiSQ33EGbFC0xbnnFB9aou_6yuyFxvycQEvA0,407
2
+ clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
+ clonebox/cli.py,sha256=NFScoojeI1XJ982SuNt01iW52hHIYSttGy2UzZuJCCQ,76413
4
+ clonebox/cloner.py,sha256=0puM04SzifccPfIVqc2CXFFcdNLWKpbiXXbBplrm9s8,31850
5
+ clonebox/detector.py,sha256=4fu04Ty6KC82WkcJZ5UL5TqXpWYE7Kb7R0uJ-9dtbCk,21635
6
+ clonebox-0.1.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
+ clonebox-0.1.10.dist-info/METADATA,sha256=hzdP5bntKA1s_RyRLx0hgbBzGrlt3NBGvDKu5YZfabE,24526
8
+ clonebox-0.1.10.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
+ clonebox-0.1.10.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
10
+ clonebox-0.1.10.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
11
+ clonebox-0.1.10.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- clonebox/__init__.py,sha256=IOk7G0DiSQ33EGbFC0xbnnFB9aou_6yuyFxvycQEvA0,407
2
- clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
- clonebox/cli.py,sha256=BOAzIJCcRb52y_KgR7rU1yZiPt4r_Ws6Hk1hIPIH5bk,62714
4
- clonebox/cloner.py,sha256=Z9vpRu-imAWS-AwcPMG7jysvNhX7xn_Xk5Vze8y7DYE,31765
5
- clonebox/detector.py,sha256=4fu04Ty6KC82WkcJZ5UL5TqXpWYE7Kb7R0uJ-9dtbCk,21635
6
- clonebox-0.1.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
- clonebox-0.1.9.dist-info/METADATA,sha256=A68kRx6uoFsNsEyH989LA1_9I1Oc6-saf3HzrAFtFd8,15582
8
- clonebox-0.1.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
- clonebox-0.1.9.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
10
- clonebox-0.1.9.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
11
- clonebox-0.1.9.dist-info/RECORD,,