clonebox 1.1.3__py3-none-any.whl → 1.1.5__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/backends/libvirt_backend.py +217 -0
- clonebox/backends/qemu_disk.py +52 -0
- clonebox/backends/subprocess_runner.py +56 -0
- clonebox/cli.py +343 -22
- clonebox/cloner.py +327 -189
- clonebox/di.py +176 -0
- clonebox/health/__init__.py +17 -0
- clonebox/health/manager.py +328 -0
- clonebox/health/models.py +194 -0
- clonebox/health/probes.py +337 -0
- clonebox/interfaces/disk.py +40 -0
- clonebox/interfaces/hypervisor.py +89 -0
- clonebox/interfaces/network.py +33 -0
- clonebox/interfaces/process.py +46 -0
- clonebox/logging.py +125 -0
- clonebox/monitor.py +267 -0
- clonebox/p2p.py +4 -2
- clonebox/resource_monitor.py +162 -0
- clonebox/resources.py +222 -0
- clonebox/rollback.py +172 -0
- clonebox/secrets.py +331 -0
- clonebox/snapshots/__init__.py +12 -0
- clonebox/snapshots/manager.py +349 -0
- clonebox/snapshots/models.py +183 -0
- {clonebox-1.1.3.dist-info → clonebox-1.1.5.dist-info}/METADATA +51 -2
- clonebox-1.1.5.dist-info/RECORD +42 -0
- clonebox-1.1.3.dist-info/RECORD +0 -21
- {clonebox-1.1.3.dist-info → clonebox-1.1.5.dist-info}/WHEEL +0 -0
- {clonebox-1.1.3.dist-info → clonebox-1.1.5.dist-info}/entry_points.txt +0 -0
- {clonebox-1.1.3.dist-info → clonebox-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {clonebox-1.1.3.dist-info → clonebox-1.1.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Data models for snapshot management."""
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SnapshotType(Enum):
|
|
12
|
+
"""Type of snapshot."""
|
|
13
|
+
|
|
14
|
+
DISK_ONLY = "disk" # Only disk state (offline)
|
|
15
|
+
FULL = "full" # Disk + memory + device state (online)
|
|
16
|
+
EXTERNAL = "external" # External snapshot file
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SnapshotState(Enum):
|
|
20
|
+
"""State of snapshot operation."""
|
|
21
|
+
|
|
22
|
+
CREATING = "creating"
|
|
23
|
+
READY = "ready"
|
|
24
|
+
REVERTING = "reverting"
|
|
25
|
+
DELETING = "deleting"
|
|
26
|
+
FAILED = "failed"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Snapshot:
|
|
31
|
+
"""Represents a VM snapshot."""
|
|
32
|
+
|
|
33
|
+
name: str
|
|
34
|
+
vm_name: str
|
|
35
|
+
snapshot_type: SnapshotType
|
|
36
|
+
state: SnapshotState
|
|
37
|
+
created_at: datetime
|
|
38
|
+
|
|
39
|
+
description: Optional[str] = None
|
|
40
|
+
|
|
41
|
+
# Snapshot hierarchy
|
|
42
|
+
parent_name: Optional[str] = None
|
|
43
|
+
children: List[str] = field(default_factory=list)
|
|
44
|
+
|
|
45
|
+
# Storage info
|
|
46
|
+
disk_path: Optional[Path] = None
|
|
47
|
+
memory_path: Optional[Path] = None
|
|
48
|
+
size_bytes: int = 0
|
|
49
|
+
|
|
50
|
+
# Metadata
|
|
51
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
52
|
+
tags: List[str] = field(default_factory=list)
|
|
53
|
+
|
|
54
|
+
# Auto-snapshot info
|
|
55
|
+
auto_created: bool = False
|
|
56
|
+
auto_policy: Optional[str] = None
|
|
57
|
+
expires_at: Optional[datetime] = None
|
|
58
|
+
|
|
59
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
60
|
+
"""Convert to dictionary for serialization."""
|
|
61
|
+
return {
|
|
62
|
+
"name": self.name,
|
|
63
|
+
"vm_name": self.vm_name,
|
|
64
|
+
"type": self.snapshot_type.value,
|
|
65
|
+
"state": self.state.value,
|
|
66
|
+
"created_at": self.created_at.isoformat(),
|
|
67
|
+
"description": self.description,
|
|
68
|
+
"parent_name": self.parent_name,
|
|
69
|
+
"children": self.children,
|
|
70
|
+
"disk_path": str(self.disk_path) if self.disk_path else None,
|
|
71
|
+
"memory_path": str(self.memory_path) if self.memory_path else None,
|
|
72
|
+
"size_bytes": self.size_bytes,
|
|
73
|
+
"metadata": self.metadata,
|
|
74
|
+
"tags": self.tags,
|
|
75
|
+
"auto_created": self.auto_created,
|
|
76
|
+
"auto_policy": self.auto_policy,
|
|
77
|
+
"expires_at": self.expires_at.isoformat() if self.expires_at else None,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Snapshot":
|
|
82
|
+
"""Create from dictionary."""
|
|
83
|
+
return cls(
|
|
84
|
+
name=data["name"],
|
|
85
|
+
vm_name=data["vm_name"],
|
|
86
|
+
snapshot_type=SnapshotType(data["type"]),
|
|
87
|
+
state=SnapshotState(data["state"]),
|
|
88
|
+
created_at=datetime.fromisoformat(data["created_at"]),
|
|
89
|
+
description=data.get("description"),
|
|
90
|
+
parent_name=data.get("parent_name"),
|
|
91
|
+
children=data.get("children", []),
|
|
92
|
+
disk_path=Path(data["disk_path"]) if data.get("disk_path") else None,
|
|
93
|
+
memory_path=Path(data["memory_path"]) if data.get("memory_path") else None,
|
|
94
|
+
size_bytes=data.get("size_bytes", 0),
|
|
95
|
+
metadata=data.get("metadata", {}),
|
|
96
|
+
tags=data.get("tags", []),
|
|
97
|
+
auto_created=data.get("auto_created", False),
|
|
98
|
+
auto_policy=data.get("auto_policy"),
|
|
99
|
+
expires_at=(
|
|
100
|
+
datetime.fromisoformat(data["expires_at"]) if data.get("expires_at") else None
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def is_expired(self) -> bool:
|
|
106
|
+
"""Check if snapshot has expired."""
|
|
107
|
+
if self.expires_at is None:
|
|
108
|
+
return False
|
|
109
|
+
return datetime.now() > self.expires_at
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def age(self) -> timedelta:
|
|
113
|
+
"""Get snapshot age."""
|
|
114
|
+
return datetime.now() - self.created_at
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass
|
|
118
|
+
class SnapshotPolicy:
|
|
119
|
+
"""Policy for automatic snapshots."""
|
|
120
|
+
|
|
121
|
+
name: str
|
|
122
|
+
enabled: bool = True
|
|
123
|
+
|
|
124
|
+
# Retention settings
|
|
125
|
+
max_snapshots: int = 10
|
|
126
|
+
max_age_days: int = 30
|
|
127
|
+
min_snapshots: int = 1 # Keep at least N snapshots
|
|
128
|
+
|
|
129
|
+
# Auto-snapshot triggers
|
|
130
|
+
before_operations: List[str] = field(
|
|
131
|
+
default_factory=lambda: ["upgrade", "resize", "config-change"]
|
|
132
|
+
)
|
|
133
|
+
scheduled_interval_hours: Optional[int] = None # e.g., 24 for daily
|
|
134
|
+
|
|
135
|
+
# Naming
|
|
136
|
+
name_prefix: str = "auto-"
|
|
137
|
+
include_timestamp: bool = True
|
|
138
|
+
|
|
139
|
+
# Cleanup
|
|
140
|
+
auto_cleanup: bool = True
|
|
141
|
+
cleanup_on_success: bool = False # Remove pre-operation snapshot if op succeeds
|
|
142
|
+
|
|
143
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
144
|
+
"""Convert to dictionary."""
|
|
145
|
+
return {
|
|
146
|
+
"name": self.name,
|
|
147
|
+
"enabled": self.enabled,
|
|
148
|
+
"max_snapshots": self.max_snapshots,
|
|
149
|
+
"max_age_days": self.max_age_days,
|
|
150
|
+
"min_snapshots": self.min_snapshots,
|
|
151
|
+
"before_operations": self.before_operations,
|
|
152
|
+
"scheduled_interval_hours": self.scheduled_interval_hours,
|
|
153
|
+
"name_prefix": self.name_prefix,
|
|
154
|
+
"include_timestamp": self.include_timestamp,
|
|
155
|
+
"auto_cleanup": self.auto_cleanup,
|
|
156
|
+
"cleanup_on_success": self.cleanup_on_success,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def from_dict(cls, data: Dict[str, Any]) -> "SnapshotPolicy":
|
|
161
|
+
"""Create from dictionary."""
|
|
162
|
+
return cls(
|
|
163
|
+
name=data["name"],
|
|
164
|
+
enabled=data.get("enabled", True),
|
|
165
|
+
max_snapshots=data.get("max_snapshots", 10),
|
|
166
|
+
max_age_days=data.get("max_age_days", 30),
|
|
167
|
+
min_snapshots=data.get("min_snapshots", 1),
|
|
168
|
+
before_operations=data.get("before_operations", ["upgrade", "resize", "config-change"]),
|
|
169
|
+
scheduled_interval_hours=data.get("scheduled_interval_hours"),
|
|
170
|
+
name_prefix=data.get("name_prefix", "auto-"),
|
|
171
|
+
include_timestamp=data.get("include_timestamp", True),
|
|
172
|
+
auto_cleanup=data.get("auto_cleanup", True),
|
|
173
|
+
cleanup_on_success=data.get("cleanup_on_success", False),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def generate_snapshot_name(self, operation: Optional[str] = None) -> str:
|
|
177
|
+
"""Generate snapshot name based on policy."""
|
|
178
|
+
parts = [self.name_prefix]
|
|
179
|
+
if operation:
|
|
180
|
+
parts.append(operation)
|
|
181
|
+
if self.include_timestamp:
|
|
182
|
+
parts.append(datetime.now().strftime("%Y%m%d-%H%M%S"))
|
|
183
|
+
return "-".join(parts)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clonebox
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.5
|
|
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
|
|
@@ -32,6 +32,7 @@ Requires-Dist: psutil>=5.9.0
|
|
|
32
32
|
Requires-Dist: pyyaml>=6.0
|
|
33
33
|
Requires-Dist: pydantic>=2.0.0
|
|
34
34
|
Requires-Dist: python-dotenv>=1.0.0
|
|
35
|
+
Requires-Dist: cryptography>=42.0.0
|
|
35
36
|
Provides-Extra: dev
|
|
36
37
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
37
38
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
@@ -114,6 +115,8 @@ CloneBox excels in scenarios where developers need:
|
|
|
114
115
|
| 🎛️ Profiles System (`ml-dev`, `web-stack`) | ✅ Stable |
|
|
115
116
|
| 🔍 Auto-detection (services, apps, paths) | ✅ Stable |
|
|
116
117
|
| 🔒 P2P Secure Transfer (AES-256) | ✅ **NEW** |
|
|
118
|
+
| 📸 Snapshot Management | ✅ **NEW** |
|
|
119
|
+
| 🏥 Health Check System | ✅ **NEW** |
|
|
117
120
|
| 🧪 95%+ Test Coverage | ✅ |
|
|
118
121
|
|
|
119
122
|
### P2P Secure VM Sharing
|
|
@@ -141,9 +144,55 @@ clonebox sync-key user@hostB # Sync encryption key
|
|
|
141
144
|
clonebox list-remote user@hostB # List remote VMs
|
|
142
145
|
```
|
|
143
146
|
|
|
147
|
+
### Snapshot Management
|
|
148
|
+
|
|
149
|
+
Save and restore VM states:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Create snapshot before risky operation
|
|
153
|
+
clonebox snapshot create my-vm --name "before-upgrade" --user
|
|
154
|
+
|
|
155
|
+
# List all snapshots
|
|
156
|
+
clonebox snapshot list my-vm --user
|
|
157
|
+
|
|
158
|
+
# Restore to previous state
|
|
159
|
+
clonebox snapshot restore my-vm --name "before-upgrade" --user
|
|
160
|
+
|
|
161
|
+
# Delete old snapshot
|
|
162
|
+
clonebox snapshot delete my-vm --name "before-upgrade" --user
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Health Checks
|
|
166
|
+
|
|
167
|
+
Configure health probes in `.clonebox.yaml`:
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
health_checks:
|
|
171
|
+
- name: nginx
|
|
172
|
+
type: http
|
|
173
|
+
url: http://localhost:80/health
|
|
174
|
+
expected_status: 200
|
|
175
|
+
|
|
176
|
+
- name: postgres
|
|
177
|
+
type: tcp
|
|
178
|
+
host: localhost
|
|
179
|
+
port: 5432
|
|
180
|
+
|
|
181
|
+
- name: redis
|
|
182
|
+
type: command
|
|
183
|
+
exec: "redis-cli ping"
|
|
184
|
+
expected_output: "PONG"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Run health checks:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
clonebox health my-vm --user
|
|
191
|
+
```
|
|
192
|
+
|
|
144
193
|
### Roadmap
|
|
145
194
|
|
|
146
|
-
- **v1.2.0**:
|
|
195
|
+
- **v1.2.0**: Resource limits, progress bars, secrets isolation
|
|
147
196
|
- **v1.3.0**: Multi-VM orchestration, cluster mode
|
|
148
197
|
- **v2.0.0**: Cloud provider support (AWS, GCP, Azure), Windows WSL2 support
|
|
149
198
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
clonebox/__init__.py,sha256=CyfHVVq6KqBr4CNERBpXk_O6Q5B35q03YpdQbokVvvI,408
|
|
2
|
+
clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
|
|
3
|
+
clonebox/cli.py,sha256=m2WBz1SDTgpeDvX-dc93bDp4CKp7AcEI17vXWx3D4os,136109
|
|
4
|
+
clonebox/cloner.py,sha256=aH7BwmLgPITHzyHn_n_AKqtEADDm_aUXiRG-nB2rxMQ,90063
|
|
5
|
+
clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
|
|
6
|
+
clonebox/dashboard.py,sha256=dMY6odvPq3j6FronhRRsX7aY3qdCwznB-aCWKEmHDNw,5768
|
|
7
|
+
clonebox/detector.py,sha256=vS65cvFNPmUBCX1Y_TMTnSRljw6r1Ae9dlVtACs5XFc,23075
|
|
8
|
+
clonebox/di.py,sha256=feFMXP5ff-7gwrIqgnoCpk1ivaiZA_lv2wcpkCSKiew,5648
|
|
9
|
+
clonebox/exporter.py,sha256=WIzVvmA0z_jjrpyXxvnXoLp9oaW6fKS7k0PGwzx_PIM,5629
|
|
10
|
+
clonebox/importer.py,sha256=Q9Uk1IOA41mgGhU4ynW2k-h9GEoGxRKI3c9wWE4uxcA,7097
|
|
11
|
+
clonebox/logging.py,sha256=jD_WgkHmt_h99EmX82QWK7D0OKWyxqcLwgT4UDQFp2g,3675
|
|
12
|
+
clonebox/models.py,sha256=zwejkNtEEO_aPy_Q5UzXG5tszU-c7lkqh9LQus9eWMo,8307
|
|
13
|
+
clonebox/monitor.py,sha256=zlIarNf8w_i34XI8hZGxxrg5PVZK_Yxm6FQnkhLavRI,9181
|
|
14
|
+
clonebox/p2p.py,sha256=6o0JnscKqF9-BftQhW5fF1W6YY1wXshY9LEklNcHGJc,5913
|
|
15
|
+
clonebox/profiles.py,sha256=UP37fX_rhrG_O9ehNFJBUcULPmUtN1A8KsJ6cM44oK0,1986
|
|
16
|
+
clonebox/resource_monitor.py,sha256=lDR9KyPbVtImeeOkOBPPVP-5yCgoL5hsVFPZ_UqsY0w,5286
|
|
17
|
+
clonebox/resources.py,sha256=IkuM4OdSDV4qhyc0eIynwbAHBTv0aVSxxW-gghsnCAs,6815
|
|
18
|
+
clonebox/rollback.py,sha256=hpwO-8Ehe1pW0wHuZvJkC_qxZ6yEo9otCJRhGIUArCo,5711
|
|
19
|
+
clonebox/secrets.py,sha256=QtGMIjhTKq14qQRGy-pX0TU3vKLiZt7CMloAlF2GmzQ,10515
|
|
20
|
+
clonebox/validator.py,sha256=CF4hMlY69-AGRH5HdG8HAA9_LNCwDKD4xPlYQPWJ9Rw,36647
|
|
21
|
+
clonebox/backends/libvirt_backend.py,sha256=sIHFIvFO1hIOXEFR_foSkOGBgIzaJVQs-njOU8GdafA,7170
|
|
22
|
+
clonebox/backends/qemu_disk.py,sha256=YsGjYX5sbEf35Y4yjTpNkZat73a4RGBxY-KTVzJhqIs,1687
|
|
23
|
+
clonebox/backends/subprocess_runner.py,sha256=c-IyaMxM1cmUu64h654oAvulm83K5Mu-VQxXJ_0BOds,1506
|
|
24
|
+
clonebox/health/__init__.py,sha256=aKJJPQwJLnoCY728QuKUxYx1TZyooGEnyUVOegZ58Ok,422
|
|
25
|
+
clonebox/health/manager.py,sha256=6nn0a8QtxeEuuafDbn5ZBqHQdaJ2qg7yTstyAGPJWP0,9987
|
|
26
|
+
clonebox/health/models.py,sha256=sPumwj8S-88KgzSGw1Kq9bBbPVRd2RR0R87Z8hKJ_28,6001
|
|
27
|
+
clonebox/health/probes.py,sha256=CkiGcayqRRysqaBJst-YpSrvUzMdwsqD4TiQSluLt3Y,11305
|
|
28
|
+
clonebox/interfaces/disk.py,sha256=F7Xzj2dq5UTZ2KGCuThDM8bwTps6chFbquOUmfLREjI,985
|
|
29
|
+
clonebox/interfaces/hypervisor.py,sha256=8ms4kZLA-5Ba1e_n68mCucwP_K9mufbmTBlo7XzURn4,1991
|
|
30
|
+
clonebox/interfaces/network.py,sha256=YPIquxEB7sZHczbpuopcZpffTjWYI6cKmAu3wAEFllk,853
|
|
31
|
+
clonebox/interfaces/process.py,sha256=njvAIZw_TCjw01KpyVQKIDoRvhTwl0FfVGbQ6mxTROk,1024
|
|
32
|
+
clonebox/snapshots/__init__.py,sha256=ndlrIavPAiA8z4Ep3-D_EPhOcjNKYFnP3rIpEKaGdb8,273
|
|
33
|
+
clonebox/snapshots/manager.py,sha256=hGzM8V6ZJPXjTqj47c4Kr8idlE-c1Q3gPUvuw1HvS1A,11393
|
|
34
|
+
clonebox/snapshots/models.py,sha256=sRnn3OZE8JG9FZJlRuA3ihO-JXoPCQ3nD3SQytflAao,6206
|
|
35
|
+
clonebox/templates/profiles/ml-dev.yaml,sha256=w07MToGh31xtxpjbeXTBk9BkpAN8A3gv8HeA3ESKG9M,461
|
|
36
|
+
clonebox/templates/profiles/web-stack.yaml,sha256=EBnnGMzML5vAjXmIUbCpbTCwmRaNJiuWd3EcL43DOK8,485
|
|
37
|
+
clonebox-1.1.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
38
|
+
clonebox-1.1.5.dist-info/METADATA,sha256=0sp9wPpm969pIM4iwq3m22B3uxh0vC694mJSIRjrUoc,48882
|
|
39
|
+
clonebox-1.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
40
|
+
clonebox-1.1.5.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
|
|
41
|
+
clonebox-1.1.5.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
|
|
42
|
+
clonebox-1.1.5.dist-info/RECORD,,
|
clonebox-1.1.3.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
clonebox/__init__.py,sha256=CyfHVVq6KqBr4CNERBpXk_O6Q5B35q03YpdQbokVvvI,408
|
|
2
|
-
clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
|
|
3
|
-
clonebox/cli.py,sha256=iPc2DWEDZaqBMXRJqpWKubmka9ZuJbAvs6mkMMEHAlw,122910
|
|
4
|
-
clonebox/cloner.py,sha256=2YQO4SHCv0xOsU1hL9IqdgmxxJN-2j75X9pe-LpTpJE,82696
|
|
5
|
-
clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
|
|
6
|
-
clonebox/dashboard.py,sha256=dMY6odvPq3j6FronhRRsX7aY3qdCwznB-aCWKEmHDNw,5768
|
|
7
|
-
clonebox/detector.py,sha256=vS65cvFNPmUBCX1Y_TMTnSRljw6r1Ae9dlVtACs5XFc,23075
|
|
8
|
-
clonebox/exporter.py,sha256=WIzVvmA0z_jjrpyXxvnXoLp9oaW6fKS7k0PGwzx_PIM,5629
|
|
9
|
-
clonebox/importer.py,sha256=Q9Uk1IOA41mgGhU4ynW2k-h9GEoGxRKI3c9wWE4uxcA,7097
|
|
10
|
-
clonebox/models.py,sha256=zwejkNtEEO_aPy_Q5UzXG5tszU-c7lkqh9LQus9eWMo,8307
|
|
11
|
-
clonebox/p2p.py,sha256=LPQQ7wNO84yDnpVrGkaRU-FDUzqmC4URdZXVeHsNOew,5889
|
|
12
|
-
clonebox/profiles.py,sha256=UP37fX_rhrG_O9ehNFJBUcULPmUtN1A8KsJ6cM44oK0,1986
|
|
13
|
-
clonebox/validator.py,sha256=CF4hMlY69-AGRH5HdG8HAA9_LNCwDKD4xPlYQPWJ9Rw,36647
|
|
14
|
-
clonebox/templates/profiles/ml-dev.yaml,sha256=w07MToGh31xtxpjbeXTBk9BkpAN8A3gv8HeA3ESKG9M,461
|
|
15
|
-
clonebox/templates/profiles/web-stack.yaml,sha256=EBnnGMzML5vAjXmIUbCpbTCwmRaNJiuWd3EcL43DOK8,485
|
|
16
|
-
clonebox-1.1.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
17
|
-
clonebox-1.1.3.dist-info/METADATA,sha256=16ilGCqzzvMBvEXxLZ-JlTyNxaI0u6ws74zM121Sw5o,47947
|
|
18
|
-
clonebox-1.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
19
|
-
clonebox-1.1.3.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
|
|
20
|
-
clonebox-1.1.3.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
|
|
21
|
-
clonebox-1.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|