kanibako-cli 1.5.0.dev14__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.
- kanibako/__init__.py +3 -0
- kanibako/__main__.py +6 -0
- kanibako/auth_browser.py +296 -0
- kanibako/auth_parser.py +51 -0
- kanibako/browser_sidecar.py +183 -0
- kanibako/browser_state.py +103 -0
- kanibako/bun_sea.py +144 -0
- kanibako/cli.py +344 -0
- kanibako/commands/__init__.py +0 -0
- kanibako/commands/archive.py +228 -0
- kanibako/commands/box/__init__.py +22 -0
- kanibako/commands/box/_duplicate.py +395 -0
- kanibako/commands/box/_migrate.py +574 -0
- kanibako/commands/box/_parser.py +1178 -0
- kanibako/commands/clean.py +166 -0
- kanibako/commands/crab_cmd.py +480 -0
- kanibako/commands/diagnose.py +239 -0
- kanibako/commands/fork_cmd.py +51 -0
- kanibako/commands/helper_cmd.py +669 -0
- kanibako/commands/image.py +1300 -0
- kanibako/commands/install.py +152 -0
- kanibako/commands/refresh_credentials.py +67 -0
- kanibako/commands/restore.py +298 -0
- kanibako/commands/setup_cmd.py +89 -0
- kanibako/commands/start.py +1600 -0
- kanibako/commands/stop.py +116 -0
- kanibako/commands/system_cmd.py +224 -0
- kanibako/commands/upgrade.py +161 -0
- kanibako/commands/vault_cmd.py +199 -0
- kanibako/commands/workset_cmd.py +552 -0
- kanibako/config.py +514 -0
- kanibako/config_interface.py +573 -0
- kanibako/config_io.py +36 -0
- kanibako/container.py +607 -0
- kanibako/containerfiles.py +58 -0
- kanibako/containers/Containerfile.kanibako +99 -0
- kanibako/containers/Containerfile.template-android +55 -0
- kanibako/containers/Containerfile.template-dotnet +29 -0
- kanibako/containers/Containerfile.template-js +43 -0
- kanibako/containers/Containerfile.template-jvm +27 -0
- kanibako/containers/Containerfile.template-systems +46 -0
- kanibako/containers/__init__.py +0 -0
- kanibako/crabs.py +89 -0
- kanibako/errors.py +33 -0
- kanibako/freshness.py +67 -0
- kanibako/git.py +114 -0
- kanibako/helper_client.py +132 -0
- kanibako/helper_listener.py +538 -0
- kanibako/helpers.py +339 -0
- kanibako/hygiene.py +296 -0
- kanibako/image_sharing.py +133 -0
- kanibako/instructions.py +160 -0
- kanibako/log.py +31 -0
- kanibako/names.py +248 -0
- kanibako/paths.py +1483 -0
- kanibako/plugins/__init__.py +10 -0
- kanibako/registry.py +71 -0
- kanibako/rig_bundle.py +121 -0
- kanibako/rig_meta.py +92 -0
- kanibako/rig_registry.py +132 -0
- kanibako/rig_resolve.py +182 -0
- kanibako/rig_source.py +245 -0
- kanibako/scripts/__init__.py +0 -0
- kanibako/scripts/helper-init.sh +45 -0
- kanibako/scripts/kanibako-entry +12 -0
- kanibako/settings_resolve.py +312 -0
- kanibako/settings_seeds.py +154 -0
- kanibako/settings_shares.py +154 -0
- kanibako/shellenv.py +75 -0
- kanibako/snapshots.py +281 -0
- kanibako/targets/__init__.py +173 -0
- kanibako/targets/base.py +243 -0
- kanibako/targets/no_agent.py +58 -0
- kanibako/templates.py +60 -0
- kanibako/templates_image.py +224 -0
- kanibako/tweakcc.py +140 -0
- kanibako/tweakcc_cache.py +171 -0
- kanibako/utils.py +136 -0
- kanibako/workset.py +347 -0
- kanibako_cli-1.5.0.dev14.dist-info/METADATA +15 -0
- kanibako_cli-1.5.0.dev14.dist-info/RECORD +85 -0
- kanibako_cli-1.5.0.dev14.dist-info/WHEEL +5 -0
- kanibako_cli-1.5.0.dev14.dist-info/entry_points.txt +5 -0
- kanibako_cli-1.5.0.dev14.dist-info/licenses/LICENSE.md +594 -0
- kanibako_cli-1.5.0.dev14.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""kanibako diagnose: system and per-scope health checks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _format_check(status: str, label: str, detail: str) -> str:
|
|
10
|
+
"""Format a single diagnostic check line."""
|
|
11
|
+
return f"[{status}] {label}: {detail}"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _check_runtime() -> tuple[str, str]:
|
|
15
|
+
"""Check container runtime availability. Returns (status, detail)."""
|
|
16
|
+
try:
|
|
17
|
+
import subprocess
|
|
18
|
+
|
|
19
|
+
from kanibako.container import ContainerRuntime
|
|
20
|
+
|
|
21
|
+
runtime = ContainerRuntime()
|
|
22
|
+
result = subprocess.run(
|
|
23
|
+
[runtime.cmd, "--version"],
|
|
24
|
+
capture_output=True,
|
|
25
|
+
text=True,
|
|
26
|
+
)
|
|
27
|
+
version = (
|
|
28
|
+
result.stdout.strip() if result.returncode == 0 else "unknown version"
|
|
29
|
+
)
|
|
30
|
+
return "ok", f"{runtime.cmd} ({version})"
|
|
31
|
+
except Exception:
|
|
32
|
+
return "!!", "not found -- install podman (https://podman.io/) or Docker"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _check_image(config: object) -> tuple[str, str]:
|
|
36
|
+
"""Check if the configured container image exists locally."""
|
|
37
|
+
try:
|
|
38
|
+
from kanibako.container import ContainerRuntime
|
|
39
|
+
|
|
40
|
+
runtime = ContainerRuntime()
|
|
41
|
+
image_name: str = getattr(config, "box_image", "")
|
|
42
|
+
data = runtime.image_inspect(image_name)
|
|
43
|
+
if data is not None:
|
|
44
|
+
return "ok", f"{image_name} (available locally)"
|
|
45
|
+
return (
|
|
46
|
+
"!!",
|
|
47
|
+
f"{image_name} (not found locally -- will be pulled on first use)",
|
|
48
|
+
)
|
|
49
|
+
except Exception:
|
|
50
|
+
return "--", "cannot check (no container runtime)"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _check_agents() -> list[tuple[str, str, str]]:
|
|
54
|
+
"""Check all discovered agent targets.
|
|
55
|
+
|
|
56
|
+
Returns list of (status, label, detail).
|
|
57
|
+
"""
|
|
58
|
+
from kanibako.targets import discover_targets
|
|
59
|
+
|
|
60
|
+
targets = discover_targets()
|
|
61
|
+
results: list[tuple[str, str, str]] = []
|
|
62
|
+
if not targets:
|
|
63
|
+
results.append(("!!", "Agents", "no agent plugins installed"))
|
|
64
|
+
return results
|
|
65
|
+
for name, cls in targets.items():
|
|
66
|
+
try:
|
|
67
|
+
instance = cls()
|
|
68
|
+
install = instance.detect()
|
|
69
|
+
if install is not None:
|
|
70
|
+
detail_parts: list[str] = []
|
|
71
|
+
binary = getattr(install, "binary", None)
|
|
72
|
+
if binary:
|
|
73
|
+
detail_parts.append(f"({binary})")
|
|
74
|
+
detail = " ".join(detail_parts) if detail_parts else "detected"
|
|
75
|
+
results.append(("ok", f"Agent: {instance.display_name}", detail))
|
|
76
|
+
else:
|
|
77
|
+
results.append(("!!", f"Agent: {instance.display_name}", "not found"))
|
|
78
|
+
except Exception as e:
|
|
79
|
+
results.append(("!!", f"Agent: {name}", str(e)))
|
|
80
|
+
return results
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _check_storage(data_path: Path) -> tuple[str, str]:
|
|
84
|
+
"""Check available disk space at the data path."""
|
|
85
|
+
try:
|
|
86
|
+
usage = shutil.disk_usage(data_path)
|
|
87
|
+
free_gb = usage.free / (1024**3)
|
|
88
|
+
total_gb = usage.total / (1024**3)
|
|
89
|
+
if free_gb < 1:
|
|
90
|
+
return (
|
|
91
|
+
"!!",
|
|
92
|
+
f"{free_gb:.1f} GB free of {total_gb:.0f} GB in {data_path}",
|
|
93
|
+
)
|
|
94
|
+
return (
|
|
95
|
+
"ok",
|
|
96
|
+
f"{free_gb:.1f} GB free of {total_gb:.0f} GB in {data_path}",
|
|
97
|
+
)
|
|
98
|
+
except Exception:
|
|
99
|
+
return "--", f"cannot check ({data_path})"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def run_system_diagnose(args: object) -> int:
|
|
103
|
+
"""Run full system diagnostics."""
|
|
104
|
+
from kanibako.config import config_file_path, load_config, load_merged_config
|
|
105
|
+
from kanibako.paths import xdg
|
|
106
|
+
|
|
107
|
+
print("Kanibako System Diagnostics")
|
|
108
|
+
print("=" * 40)
|
|
109
|
+
print()
|
|
110
|
+
|
|
111
|
+
# Runtime
|
|
112
|
+
status, detail = _check_runtime()
|
|
113
|
+
print(_format_check(status, "Container runtime", detail))
|
|
114
|
+
|
|
115
|
+
# Image
|
|
116
|
+
try:
|
|
117
|
+
config_home = xdg("XDG_CONFIG_HOME", ".config")
|
|
118
|
+
cf = config_file_path(config_home)
|
|
119
|
+
merged = load_merged_config(cf, None)
|
|
120
|
+
status, detail = _check_image(merged)
|
|
121
|
+
print(_format_check(status, "Image", detail))
|
|
122
|
+
except Exception:
|
|
123
|
+
print(_format_check("--", "Image", "cannot check (not configured)"))
|
|
124
|
+
|
|
125
|
+
# Agents
|
|
126
|
+
for status, label, detail in _check_agents():
|
|
127
|
+
print(_format_check(status, label, detail))
|
|
128
|
+
|
|
129
|
+
# Storage
|
|
130
|
+
try:
|
|
131
|
+
config_home = xdg("XDG_CONFIG_HOME", ".config")
|
|
132
|
+
cf = config_file_path(config_home)
|
|
133
|
+
config = load_config(cf)
|
|
134
|
+
from kanibako.paths import resolve_system_paths
|
|
135
|
+
data_home = xdg("XDG_DATA_HOME", ".local/share")
|
|
136
|
+
data_path = resolve_system_paths(
|
|
137
|
+
config.system_paths, data_home=data_home, home=Path.home(),
|
|
138
|
+
)["system.path.data"]
|
|
139
|
+
status, detail = _check_storage(data_path)
|
|
140
|
+
print(_format_check(status, "Storage", detail))
|
|
141
|
+
except Exception:
|
|
142
|
+
print(_format_check("--", "Storage", "cannot check"))
|
|
143
|
+
|
|
144
|
+
print()
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def run_box_diagnose(args: object) -> int:
|
|
149
|
+
"""Run diagnostics for a specific project box."""
|
|
150
|
+
from kanibako.config import config_file_path, load_config
|
|
151
|
+
from kanibako.paths import load_std_paths, resolve_any_project, xdg
|
|
152
|
+
|
|
153
|
+
config_home = xdg("XDG_CONFIG_HOME", ".config")
|
|
154
|
+
cf = config_file_path(config_home)
|
|
155
|
+
config = load_config(cf)
|
|
156
|
+
std = load_std_paths(config)
|
|
157
|
+
|
|
158
|
+
project_dir = getattr(args, "project", None) or getattr(args, "path", None)
|
|
159
|
+
try:
|
|
160
|
+
proj = resolve_any_project(std, config, project_dir)
|
|
161
|
+
except Exception as e:
|
|
162
|
+
print(f"Error: {e}")
|
|
163
|
+
return 1
|
|
164
|
+
|
|
165
|
+
print(f"Box Diagnostics: {proj.project_path}")
|
|
166
|
+
print("=" * 40)
|
|
167
|
+
print()
|
|
168
|
+
|
|
169
|
+
# Project directory
|
|
170
|
+
if proj.project_path and proj.project_path.is_dir():
|
|
171
|
+
print(_format_check("ok", "Project directory", str(proj.project_path)))
|
|
172
|
+
else:
|
|
173
|
+
print(_format_check("!!", "Project directory", "missing"))
|
|
174
|
+
|
|
175
|
+
# Shell directory
|
|
176
|
+
if proj.shell_path and proj.shell_path.is_dir():
|
|
177
|
+
print(_format_check("ok", "Shell directory", str(proj.shell_path)))
|
|
178
|
+
else:
|
|
179
|
+
print(_format_check("!!", "Shell directory", "missing or not initialized"))
|
|
180
|
+
|
|
181
|
+
# Runtime check
|
|
182
|
+
status, detail = _check_runtime()
|
|
183
|
+
print(_format_check(status, "Container runtime", detail))
|
|
184
|
+
|
|
185
|
+
print()
|
|
186
|
+
return 0
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def run_crab_diagnose(args: object) -> int:
|
|
190
|
+
"""Run diagnostics for agent/crab configuration."""
|
|
191
|
+
print("Crab (Agent) Diagnostics")
|
|
192
|
+
print("=" * 40)
|
|
193
|
+
print()
|
|
194
|
+
|
|
195
|
+
for status, label, detail in _check_agents():
|
|
196
|
+
print(_format_check(status, label, detail))
|
|
197
|
+
|
|
198
|
+
print()
|
|
199
|
+
return 0
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def run_rig_diagnose(args: object) -> int:
|
|
203
|
+
"""Run diagnostics for rig/image status."""
|
|
204
|
+
from kanibako.config import config_file_path, load_merged_config
|
|
205
|
+
from kanibako.paths import xdg
|
|
206
|
+
|
|
207
|
+
print("Rig (Image) Diagnostics")
|
|
208
|
+
print("=" * 40)
|
|
209
|
+
print()
|
|
210
|
+
|
|
211
|
+
status, detail = _check_runtime()
|
|
212
|
+
print(_format_check(status, "Container runtime", detail))
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
config_home = xdg("XDG_CONFIG_HOME", ".config")
|
|
216
|
+
cf = config_file_path(config_home)
|
|
217
|
+
merged = load_merged_config(cf, None)
|
|
218
|
+
status, detail = _check_image(merged)
|
|
219
|
+
print(_format_check(status, "Configured image", detail))
|
|
220
|
+
except Exception:
|
|
221
|
+
print(_format_check("--", "Configured image", "cannot check"))
|
|
222
|
+
|
|
223
|
+
# List local images
|
|
224
|
+
try:
|
|
225
|
+
from kanibako.container import ContainerRuntime
|
|
226
|
+
|
|
227
|
+
runtime = ContainerRuntime()
|
|
228
|
+
images = runtime.list_local_images()
|
|
229
|
+
if images:
|
|
230
|
+
print(_format_check("ok", "Local images", f"{len(images)} found"))
|
|
231
|
+
for repo, size in images:
|
|
232
|
+
print(f" {repo} {size}")
|
|
233
|
+
else:
|
|
234
|
+
print(_format_check("!!", "Local images", "none found"))
|
|
235
|
+
except Exception:
|
|
236
|
+
print(_format_check("--", "Local images", "cannot check"))
|
|
237
|
+
|
|
238
|
+
print()
|
|
239
|
+
return 0
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""kanibako fork: fork the current project from inside a container."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
11
|
+
p = subparsers.add_parser(
|
|
12
|
+
"fork",
|
|
13
|
+
help="Fork this project into a new directory",
|
|
14
|
+
description=(
|
|
15
|
+
"Fork the current project into a sibling directory. "
|
|
16
|
+
"The fork is a full copy of the workspace and metadata, "
|
|
17
|
+
"assigned a new project name."
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
p.add_argument(
|
|
21
|
+
"name",
|
|
22
|
+
help="Fork name (appended with dot to workspace path)",
|
|
23
|
+
)
|
|
24
|
+
p.set_defaults(func=run_fork, command="fork")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def run_fork(args: argparse.Namespace) -> int:
|
|
28
|
+
socket_path = Path.home() / ".local" / "state" / "kanibako" / "helper.sock"
|
|
29
|
+
if not socket_path.exists():
|
|
30
|
+
print(
|
|
31
|
+
"Error: fork requires a running kanibako session with helpers enabled.",
|
|
32
|
+
file=sys.stderr,
|
|
33
|
+
)
|
|
34
|
+
return 1
|
|
35
|
+
|
|
36
|
+
from kanibako.helper_client import send_request
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
resp = send_request(socket_path, {"action": "fork", "name": args.name})
|
|
40
|
+
except Exception as e:
|
|
41
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
if resp.get("status") == "ok":
|
|
45
|
+
print(f"Forked to: {resp['path']}")
|
|
46
|
+
print(f"Project name: {resp['name']}")
|
|
47
|
+
print("Open a new terminal and run kanibako in that directory.")
|
|
48
|
+
return 0
|
|
49
|
+
else:
|
|
50
|
+
print(f"Error: {resp.get('message', 'unknown error')}", file=sys.stderr)
|
|
51
|
+
return 1
|