gitinstall 1.1.0__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 (59) hide show
  1. gitinstall/__init__.py +61 -0
  2. gitinstall/_sdk.py +541 -0
  3. gitinstall/academic.py +831 -0
  4. gitinstall/admin.html +327 -0
  5. gitinstall/auto_update.py +384 -0
  6. gitinstall/autopilot.py +349 -0
  7. gitinstall/badge.py +476 -0
  8. gitinstall/checkpoint.py +330 -0
  9. gitinstall/cicd.py +499 -0
  10. gitinstall/clawhub.html +718 -0
  11. gitinstall/config_schema.py +353 -0
  12. gitinstall/db.py +984 -0
  13. gitinstall/db_backend.py +445 -0
  14. gitinstall/dep_chain.py +337 -0
  15. gitinstall/dependency_audit.py +1153 -0
  16. gitinstall/detector.py +542 -0
  17. gitinstall/doctor.py +493 -0
  18. gitinstall/education.py +869 -0
  19. gitinstall/enterprise.py +802 -0
  20. gitinstall/error_fixer.py +953 -0
  21. gitinstall/event_bus.py +251 -0
  22. gitinstall/executor.py +577 -0
  23. gitinstall/feature_flags.py +138 -0
  24. gitinstall/fetcher.py +921 -0
  25. gitinstall/huggingface.py +922 -0
  26. gitinstall/hw_detect.py +988 -0
  27. gitinstall/i18n.py +664 -0
  28. gitinstall/installer_registry.py +362 -0
  29. gitinstall/knowledge_base.py +379 -0
  30. gitinstall/license_check.py +605 -0
  31. gitinstall/llm.py +569 -0
  32. gitinstall/log.py +236 -0
  33. gitinstall/main.py +1408 -0
  34. gitinstall/mcp_agent.py +841 -0
  35. gitinstall/mcp_server.py +386 -0
  36. gitinstall/monorepo.py +810 -0
  37. gitinstall/multi_source.py +425 -0
  38. gitinstall/onboard.py +276 -0
  39. gitinstall/planner.py +222 -0
  40. gitinstall/planner_helpers.py +323 -0
  41. gitinstall/planner_known_projects.py +1010 -0
  42. gitinstall/planner_templates.py +996 -0
  43. gitinstall/remote_gpu.py +633 -0
  44. gitinstall/resilience.py +608 -0
  45. gitinstall/run_tests.py +572 -0
  46. gitinstall/skills.py +476 -0
  47. gitinstall/tool_schemas.py +324 -0
  48. gitinstall/trending.py +279 -0
  49. gitinstall/uninstaller.py +415 -0
  50. gitinstall/validate_top100.py +607 -0
  51. gitinstall/watchdog.py +180 -0
  52. gitinstall/web.py +1277 -0
  53. gitinstall/web_ui.html +2277 -0
  54. gitinstall-1.1.0.dist-info/METADATA +275 -0
  55. gitinstall-1.1.0.dist-info/RECORD +59 -0
  56. gitinstall-1.1.0.dist-info/WHEEL +5 -0
  57. gitinstall-1.1.0.dist-info/entry_points.txt +3 -0
  58. gitinstall-1.1.0.dist-info/licenses/LICENSE +21 -0
  59. gitinstall-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,362 @@
1
+ """
2
+ installer_registry.py - 安装器服务注册表
3
+ ==========================================
4
+
5
+ 灵感来源:PersonalBrain 的 Service Registry 模式
6
+
7
+ 将 pip/npm/cargo/go/docker/conda/brew/apt 等包管理器抽象为
8
+ 可注册的 Installer Service,新包管理器只需实现接口即可插入。
9
+
10
+ 架构:
11
+ BaseInstaller (抽象基类)
12
+ ├── PipInstaller
13
+ ├── NpmInstaller
14
+ ├── CargoInstaller
15
+ ├── GoInstaller
16
+ ├── DockerInstaller
17
+ ├── CondaInstaller
18
+ ├── BrewInstaller
19
+ └── AptInstaller
20
+
21
+ 零外部依赖,纯 Python 标准库。
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import shutil
27
+ import subprocess
28
+ from dataclasses import dataclass, field
29
+ from typing import Optional
30
+
31
+
32
+ @dataclass
33
+ class InstallerInfo:
34
+ """安装器元信息"""
35
+ name: str
36
+ display_name: str
37
+ ecosystems: list[str] # python, node, rust, go, system, container
38
+ install_command: str # 主安装命令模板
39
+ version_command: str # 版本检测命令
40
+ available: bool = False
41
+ version: str = ""
42
+ priority: int = 50 # 优先级 (0=最高, 100=最低)
43
+ platforms: list[str] = field(default_factory=lambda: ["darwin", "linux", "win32"])
44
+
45
+
46
+ class BaseInstaller:
47
+ """安装器基类"""
48
+
49
+ info: InstallerInfo
50
+
51
+ def __init__(self):
52
+ self.info = self._get_info()
53
+ self._detect()
54
+
55
+ def _get_info(self) -> InstallerInfo:
56
+ """子类必须实现:返回安装器 meta"""
57
+ raise NotImplementedError
58
+
59
+ def _detect(self):
60
+ """检测安装器是否可用"""
61
+ try:
62
+ cmd = self.info.version_command.split()
63
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
64
+ if result.returncode == 0:
65
+ self.info.available = True
66
+ ver = result.stdout.strip().split("\n")[0]
67
+ # 提取版本号
68
+ for part in ver.split():
69
+ if any(c.isdigit() for c in part):
70
+ self.info.version = part.strip("(),v")
71
+ break
72
+ except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
73
+ self.info.available = False
74
+
75
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
76
+ """判断是否能处理该项目"""
77
+ raise NotImplementedError
78
+
79
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
80
+ """生成安装步骤"""
81
+ raise NotImplementedError
82
+
83
+ def to_dict(self) -> dict:
84
+ return {
85
+ "name": self.info.name,
86
+ "display_name": self.info.display_name,
87
+ "ecosystems": self.info.ecosystems,
88
+ "available": self.info.available,
89
+ "version": self.info.version,
90
+ "priority": self.info.priority,
91
+ }
92
+
93
+
94
+ # ─────────────────────────────────────────────
95
+ # 内置安装器实现
96
+ # ─────────────────────────────────────────────
97
+
98
+ class PipInstaller(BaseInstaller):
99
+ def _get_info(self) -> InstallerInfo:
100
+ return InstallerInfo(
101
+ name="pip", display_name="pip (Python)",
102
+ ecosystems=["python"],
103
+ install_command="pip install -r requirements.txt",
104
+ version_command="pip --version",
105
+ priority=30,
106
+ )
107
+
108
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
109
+ if not self.info.available:
110
+ return False
111
+ py_files = {"requirements.txt", "setup.py", "pyproject.toml",
112
+ "setup.cfg", "Pipfile"}
113
+ return bool(set(dep_files.keys()) & py_files) or "python" in project_types
114
+
115
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
116
+ steps = []
117
+ dep_files = project_info.get("dependency_files", {})
118
+ if "requirements.txt" in dep_files:
119
+ steps.append({
120
+ "command": "pip install -r requirements.txt",
121
+ "description": "安装 Python 依赖",
122
+ })
123
+ elif "pyproject.toml" in dep_files:
124
+ steps.append({
125
+ "command": "pip install -e .",
126
+ "description": "安装 Python 项目(editable)",
127
+ })
128
+ elif "setup.py" in dep_files:
129
+ steps.append({
130
+ "command": "pip install -e .",
131
+ "description": "安装 Python 项目",
132
+ })
133
+ return steps
134
+
135
+
136
+ class NpmInstaller(BaseInstaller):
137
+ def _get_info(self) -> InstallerInfo:
138
+ return InstallerInfo(
139
+ name="npm", display_name="npm (Node.js)",
140
+ ecosystems=["node", "javascript", "typescript"],
141
+ install_command="npm install",
142
+ version_command="npm --version",
143
+ priority=30,
144
+ )
145
+
146
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
147
+ if not self.info.available:
148
+ return False
149
+ return "package.json" in dep_files or "node" in project_types
150
+
151
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
152
+ return [{"command": "npm install", "description": "安装 Node.js 依赖"}]
153
+
154
+
155
+ class CargoInstaller(BaseInstaller):
156
+ def _get_info(self) -> InstallerInfo:
157
+ return InstallerInfo(
158
+ name="cargo", display_name="Cargo (Rust)",
159
+ ecosystems=["rust"],
160
+ install_command="cargo build --release",
161
+ version_command="cargo --version",
162
+ priority=40,
163
+ )
164
+
165
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
166
+ if not self.info.available:
167
+ return False
168
+ return "Cargo.toml" in dep_files or "rust" in project_types
169
+
170
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
171
+ return [{"command": "cargo build --release", "description": "编译 Rust 项目"}]
172
+
173
+
174
+ class GoInstaller(BaseInstaller):
175
+ def _get_info(self) -> InstallerInfo:
176
+ return InstallerInfo(
177
+ name="go", display_name="Go",
178
+ ecosystems=["go"],
179
+ install_command="go build ./...",
180
+ version_command="go version",
181
+ priority=40,
182
+ )
183
+
184
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
185
+ if not self.info.available:
186
+ return False
187
+ return "go.mod" in dep_files or "go" in project_types
188
+
189
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
190
+ return [
191
+ {"command": "go mod download", "description": "下载 Go 依赖"},
192
+ {"command": "go build ./...", "description": "编译 Go 项目"},
193
+ ]
194
+
195
+
196
+ class DockerInstaller(BaseInstaller):
197
+ def _get_info(self) -> InstallerInfo:
198
+ return InstallerInfo(
199
+ name="docker", display_name="Docker",
200
+ ecosystems=["container"],
201
+ install_command="docker compose up -d",
202
+ version_command="docker --version",
203
+ priority=60,
204
+ )
205
+
206
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
207
+ if not self.info.available:
208
+ return False
209
+ docker_files = {"Dockerfile", "docker-compose.yml",
210
+ "docker-compose.yaml", "compose.yml", "compose.yaml"}
211
+ return bool(set(dep_files.keys()) & docker_files) or "docker" in project_types
212
+
213
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
214
+ dep_files = project_info.get("dependency_files", {})
215
+ compose_files = {"docker-compose.yml", "docker-compose.yaml",
216
+ "compose.yml", "compose.yaml"}
217
+ if set(dep_files.keys()) & compose_files:
218
+ return [{"command": "docker compose up -d",
219
+ "description": "启动 Docker 容器"}]
220
+ return [{"command": "docker build -t app .",
221
+ "description": "构建 Docker 镜像"}]
222
+
223
+
224
+ class CondaInstaller(BaseInstaller):
225
+ def _get_info(self) -> InstallerInfo:
226
+ return InstallerInfo(
227
+ name="conda", display_name="Conda",
228
+ ecosystems=["python", "data-science"],
229
+ install_command="conda env create -f environment.yml",
230
+ version_command="conda --version",
231
+ priority=35,
232
+ )
233
+
234
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
235
+ if not self.info.available:
236
+ return False
237
+ conda_files = {"environment.yml", "environment.yaml", "conda.yml"}
238
+ return bool(set(dep_files.keys()) & conda_files)
239
+
240
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
241
+ return [{"command": "conda env create -f environment.yml",
242
+ "description": "创建 Conda 环境"}]
243
+
244
+
245
+ class BrewInstaller(BaseInstaller):
246
+ def _get_info(self) -> InstallerInfo:
247
+ return InstallerInfo(
248
+ name="brew", display_name="Homebrew",
249
+ ecosystems=["system"],
250
+ install_command="brew install",
251
+ version_command="brew --version",
252
+ priority=50,
253
+ platforms=["darwin"],
254
+ )
255
+
256
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
257
+ if not self.info.available:
258
+ return False
259
+ return "Brewfile" in dep_files
260
+
261
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
262
+ return [{"command": "brew bundle", "description": "安装 Homebrew 依赖"}]
263
+
264
+
265
+ class AptInstaller(BaseInstaller):
266
+ def _get_info(self) -> InstallerInfo:
267
+ return InstallerInfo(
268
+ name="apt", display_name="APT (Debian/Ubuntu)",
269
+ ecosystems=["system"],
270
+ install_command="sudo apt-get install -y",
271
+ version_command="apt --version",
272
+ priority=50,
273
+ platforms=["linux"],
274
+ )
275
+
276
+ def can_handle(self, project_types: list[str], dep_files: dict) -> bool:
277
+ return self.info.available
278
+
279
+ def generate_install_steps(self, project_info: dict) -> list[dict]:
280
+ return [] # APT steps are generated dynamically based on missing deps
281
+
282
+
283
+ # ─────────────────────────────────────────────
284
+ # 服务注册表
285
+ # ─────────────────────────────────────────────
286
+
287
+ class InstallerRegistry:
288
+ """安装器注册表(单例模式)"""
289
+
290
+ _instance: Optional["InstallerRegistry"] = None
291
+
292
+ def __new__(cls):
293
+ if cls._instance is None:
294
+ cls._instance = super().__new__(cls)
295
+ cls._instance._installers = {}
296
+ cls._instance._initialized = False
297
+ return cls._instance
298
+
299
+ def __init__(self):
300
+ if not self._initialized:
301
+ self._register_builtins()
302
+ self._initialized = True
303
+
304
+ def _register_builtins(self):
305
+ """注册所有内置安装器"""
306
+ builtin_classes = [
307
+ PipInstaller, NpmInstaller, CargoInstaller, GoInstaller,
308
+ DockerInstaller, CondaInstaller, BrewInstaller, AptInstaller,
309
+ ]
310
+ for cls in builtin_classes:
311
+ try:
312
+ installer = cls()
313
+ self._installers[installer.info.name] = installer
314
+ except Exception:
315
+ pass
316
+
317
+ def register(self, installer: BaseInstaller):
318
+ """注册自定义安装器"""
319
+ self._installers[installer.info.name] = installer
320
+
321
+ def get(self, name: str) -> Optional[BaseInstaller]:
322
+ """获取指定安装器"""
323
+ return self._installers.get(name)
324
+
325
+ def list_available(self) -> list[BaseInstaller]:
326
+ """列出所有可用的安装器"""
327
+ return sorted(
328
+ [i for i in self._installers.values() if i.info.available],
329
+ key=lambda i: i.info.priority,
330
+ )
331
+
332
+ def list_all(self) -> list[BaseInstaller]:
333
+ """列出所有安装器(含不可用的)"""
334
+ return sorted(self._installers.values(), key=lambda i: i.info.priority)
335
+
336
+ def find_matching(self, project_types: list[str],
337
+ dep_files: dict) -> list[BaseInstaller]:
338
+ """查找能处理当前项目的安装器"""
339
+ matching = []
340
+ for installer in self.list_available():
341
+ if installer.can_handle(project_types, dep_files):
342
+ matching.append(installer)
343
+ return matching
344
+
345
+ def format_registry(self) -> str:
346
+ """格式化注册表状态"""
347
+ lines = ["📦 安装器注册表:", ""]
348
+ for installer in self.list_all():
349
+ status = "✅" if installer.info.available else "❌"
350
+ ver = f" v{installer.info.version}" if installer.info.version else ""
351
+ eco = ", ".join(installer.info.ecosystems)
352
+ lines.append(f" {status} {installer.info.display_name}{ver}")
353
+ lines.append(f" 生态:{eco} 优先级:{installer.info.priority}")
354
+ return "\n".join(lines)
355
+
356
+ def to_dict(self) -> dict:
357
+ """序列化为字典"""
358
+ return {
359
+ "installers": [i.to_dict() for i in self.list_all()],
360
+ "available_count": len(self.list_available()),
361
+ "total_count": len(self._installers),
362
+ }