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.
Files changed (85) hide show
  1. kanibako/__init__.py +3 -0
  2. kanibako/__main__.py +6 -0
  3. kanibako/auth_browser.py +296 -0
  4. kanibako/auth_parser.py +51 -0
  5. kanibako/browser_sidecar.py +183 -0
  6. kanibako/browser_state.py +103 -0
  7. kanibako/bun_sea.py +144 -0
  8. kanibako/cli.py +344 -0
  9. kanibako/commands/__init__.py +0 -0
  10. kanibako/commands/archive.py +228 -0
  11. kanibako/commands/box/__init__.py +22 -0
  12. kanibako/commands/box/_duplicate.py +395 -0
  13. kanibako/commands/box/_migrate.py +574 -0
  14. kanibako/commands/box/_parser.py +1178 -0
  15. kanibako/commands/clean.py +166 -0
  16. kanibako/commands/crab_cmd.py +480 -0
  17. kanibako/commands/diagnose.py +239 -0
  18. kanibako/commands/fork_cmd.py +51 -0
  19. kanibako/commands/helper_cmd.py +669 -0
  20. kanibako/commands/image.py +1300 -0
  21. kanibako/commands/install.py +152 -0
  22. kanibako/commands/refresh_credentials.py +67 -0
  23. kanibako/commands/restore.py +298 -0
  24. kanibako/commands/setup_cmd.py +89 -0
  25. kanibako/commands/start.py +1600 -0
  26. kanibako/commands/stop.py +116 -0
  27. kanibako/commands/system_cmd.py +224 -0
  28. kanibako/commands/upgrade.py +161 -0
  29. kanibako/commands/vault_cmd.py +199 -0
  30. kanibako/commands/workset_cmd.py +552 -0
  31. kanibako/config.py +514 -0
  32. kanibako/config_interface.py +573 -0
  33. kanibako/config_io.py +36 -0
  34. kanibako/container.py +607 -0
  35. kanibako/containerfiles.py +58 -0
  36. kanibako/containers/Containerfile.kanibako +99 -0
  37. kanibako/containers/Containerfile.template-android +55 -0
  38. kanibako/containers/Containerfile.template-dotnet +29 -0
  39. kanibako/containers/Containerfile.template-js +43 -0
  40. kanibako/containers/Containerfile.template-jvm +27 -0
  41. kanibako/containers/Containerfile.template-systems +46 -0
  42. kanibako/containers/__init__.py +0 -0
  43. kanibako/crabs.py +89 -0
  44. kanibako/errors.py +33 -0
  45. kanibako/freshness.py +67 -0
  46. kanibako/git.py +114 -0
  47. kanibako/helper_client.py +132 -0
  48. kanibako/helper_listener.py +538 -0
  49. kanibako/helpers.py +339 -0
  50. kanibako/hygiene.py +296 -0
  51. kanibako/image_sharing.py +133 -0
  52. kanibako/instructions.py +160 -0
  53. kanibako/log.py +31 -0
  54. kanibako/names.py +248 -0
  55. kanibako/paths.py +1483 -0
  56. kanibako/plugins/__init__.py +10 -0
  57. kanibako/registry.py +71 -0
  58. kanibako/rig_bundle.py +121 -0
  59. kanibako/rig_meta.py +92 -0
  60. kanibako/rig_registry.py +132 -0
  61. kanibako/rig_resolve.py +182 -0
  62. kanibako/rig_source.py +245 -0
  63. kanibako/scripts/__init__.py +0 -0
  64. kanibako/scripts/helper-init.sh +45 -0
  65. kanibako/scripts/kanibako-entry +12 -0
  66. kanibako/settings_resolve.py +312 -0
  67. kanibako/settings_seeds.py +154 -0
  68. kanibako/settings_shares.py +154 -0
  69. kanibako/shellenv.py +75 -0
  70. kanibako/snapshots.py +281 -0
  71. kanibako/targets/__init__.py +173 -0
  72. kanibako/targets/base.py +243 -0
  73. kanibako/targets/no_agent.py +58 -0
  74. kanibako/templates.py +60 -0
  75. kanibako/templates_image.py +224 -0
  76. kanibako/tweakcc.py +140 -0
  77. kanibako/tweakcc_cache.py +171 -0
  78. kanibako/utils.py +136 -0
  79. kanibako/workset.py +347 -0
  80. kanibako_cli-1.5.0.dev14.dist-info/METADATA +15 -0
  81. kanibako_cli-1.5.0.dev14.dist-info/RECORD +85 -0
  82. kanibako_cli-1.5.0.dev14.dist-info/WHEEL +5 -0
  83. kanibako_cli-1.5.0.dev14.dist-info/entry_points.txt +5 -0
  84. kanibako_cli-1.5.0.dev14.dist-info/licenses/LICENSE.md +594 -0
  85. 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