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.
- gitinstall/__init__.py +61 -0
- gitinstall/_sdk.py +541 -0
- gitinstall/academic.py +831 -0
- gitinstall/admin.html +327 -0
- gitinstall/auto_update.py +384 -0
- gitinstall/autopilot.py +349 -0
- gitinstall/badge.py +476 -0
- gitinstall/checkpoint.py +330 -0
- gitinstall/cicd.py +499 -0
- gitinstall/clawhub.html +718 -0
- gitinstall/config_schema.py +353 -0
- gitinstall/db.py +984 -0
- gitinstall/db_backend.py +445 -0
- gitinstall/dep_chain.py +337 -0
- gitinstall/dependency_audit.py +1153 -0
- gitinstall/detector.py +542 -0
- gitinstall/doctor.py +493 -0
- gitinstall/education.py +869 -0
- gitinstall/enterprise.py +802 -0
- gitinstall/error_fixer.py +953 -0
- gitinstall/event_bus.py +251 -0
- gitinstall/executor.py +577 -0
- gitinstall/feature_flags.py +138 -0
- gitinstall/fetcher.py +921 -0
- gitinstall/huggingface.py +922 -0
- gitinstall/hw_detect.py +988 -0
- gitinstall/i18n.py +664 -0
- gitinstall/installer_registry.py +362 -0
- gitinstall/knowledge_base.py +379 -0
- gitinstall/license_check.py +605 -0
- gitinstall/llm.py +569 -0
- gitinstall/log.py +236 -0
- gitinstall/main.py +1408 -0
- gitinstall/mcp_agent.py +841 -0
- gitinstall/mcp_server.py +386 -0
- gitinstall/monorepo.py +810 -0
- gitinstall/multi_source.py +425 -0
- gitinstall/onboard.py +276 -0
- gitinstall/planner.py +222 -0
- gitinstall/planner_helpers.py +323 -0
- gitinstall/planner_known_projects.py +1010 -0
- gitinstall/planner_templates.py +996 -0
- gitinstall/remote_gpu.py +633 -0
- gitinstall/resilience.py +608 -0
- gitinstall/run_tests.py +572 -0
- gitinstall/skills.py +476 -0
- gitinstall/tool_schemas.py +324 -0
- gitinstall/trending.py +279 -0
- gitinstall/uninstaller.py +415 -0
- gitinstall/validate_top100.py +607 -0
- gitinstall/watchdog.py +180 -0
- gitinstall/web.py +1277 -0
- gitinstall/web_ui.html +2277 -0
- gitinstall-1.1.0.dist-info/METADATA +275 -0
- gitinstall-1.1.0.dist-info/RECORD +59 -0
- gitinstall-1.1.0.dist-info/WHEEL +5 -0
- gitinstall-1.1.0.dist-info/entry_points.txt +3 -0
- gitinstall-1.1.0.dist-info/licenses/LICENSE +21 -0
- gitinstall-1.1.0.dist-info/top_level.txt +1 -0
gitinstall/dep_chain.py
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""
|
|
2
|
+
dep_chain.py - 依赖链可视化 + Lineage 追踪
|
|
3
|
+
=============================================
|
|
4
|
+
|
|
5
|
+
灵感来源:ICE-cluade-SCompany 的 Task 依赖链 + Failure Lineage
|
|
6
|
+
|
|
7
|
+
功能:
|
|
8
|
+
1. 构建安装步骤的依赖 DAG(有向无环图)
|
|
9
|
+
2. 检测循环依赖
|
|
10
|
+
3. 失败 Lineage 追踪:安装失败时追溯根因
|
|
11
|
+
4. ASCII 可视化依赖树
|
|
12
|
+
|
|
13
|
+
例:
|
|
14
|
+
安装 ComfyUI 需要:git → python venv → pip install → pytorch
|
|
15
|
+
如果 pip install 失败 → Lineage 追踪:缺少 gcc → 建议 xcode-select --install
|
|
16
|
+
|
|
17
|
+
零外部依赖,纯 Python 标准库。
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
import time
|
|
24
|
+
from collections import defaultdict
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class DepNode:
|
|
31
|
+
"""依赖节点"""
|
|
32
|
+
id: str # 唯一标识
|
|
33
|
+
name: str # 显示名
|
|
34
|
+
node_type: str = "step" # step, tool, package, system
|
|
35
|
+
status: str = "pending" # pending, running, completed, failed
|
|
36
|
+
depends_on: list[str] = field(default_factory=list) # 依赖的节点 ID
|
|
37
|
+
error: str = ""
|
|
38
|
+
duration_sec: float = 0.0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class FailureLineage:
|
|
43
|
+
"""失败血统链"""
|
|
44
|
+
root_cause: str # 根因描述
|
|
45
|
+
chain: list[str] = field(default_factory=list) # 因果链: [root, ..., leaf]
|
|
46
|
+
suggestion: str = "" # 修复建议
|
|
47
|
+
confidence: float = 0.0 # 置信度 0-1
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class DepChain:
|
|
52
|
+
"""依赖链(DAG)"""
|
|
53
|
+
nodes: dict[str, DepNode] = field(default_factory=dict)
|
|
54
|
+
edges: list[tuple[str, str]] = field(default_factory=list) # (from, to)
|
|
55
|
+
|
|
56
|
+
def add_node(self, node: DepNode):
|
|
57
|
+
self.nodes[node.id] = node
|
|
58
|
+
|
|
59
|
+
def add_edge(self, from_id: str, to_id: str):
|
|
60
|
+
if from_id in self.nodes and to_id in self.nodes:
|
|
61
|
+
self.edges.append((from_id, to_id))
|
|
62
|
+
self.nodes[to_id].depends_on.append(from_id)
|
|
63
|
+
|
|
64
|
+
def get_dependents(self, node_id: str) -> list[str]:
|
|
65
|
+
"""获取依赖于 node_id 的所有节点"""
|
|
66
|
+
return [to_id for from_id, to_id in self.edges if from_id == node_id]
|
|
67
|
+
|
|
68
|
+
def get_dependencies(self, node_id: str) -> list[str]:
|
|
69
|
+
"""获取 node_id 依赖的所有节点"""
|
|
70
|
+
return [from_id for from_id, to_id in self.edges if to_id == node_id]
|
|
71
|
+
|
|
72
|
+
def topological_sort(self) -> list[str]:
|
|
73
|
+
"""拓扑排序(检测循环依赖)"""
|
|
74
|
+
in_degree = defaultdict(int)
|
|
75
|
+
for node_id in self.nodes:
|
|
76
|
+
in_degree[node_id] = 0
|
|
77
|
+
for _, to_id in self.edges:
|
|
78
|
+
in_degree[to_id] += 1
|
|
79
|
+
|
|
80
|
+
queue = [n for n in self.nodes if in_degree[n] == 0]
|
|
81
|
+
result = []
|
|
82
|
+
|
|
83
|
+
while queue:
|
|
84
|
+
node = queue.pop(0)
|
|
85
|
+
result.append(node)
|
|
86
|
+
for dep in self.get_dependents(node):
|
|
87
|
+
in_degree[dep] -= 1
|
|
88
|
+
if in_degree[dep] == 0:
|
|
89
|
+
queue.append(dep)
|
|
90
|
+
|
|
91
|
+
if len(result) != len(self.nodes):
|
|
92
|
+
# 存在循环依赖
|
|
93
|
+
missing = set(self.nodes.keys()) - set(result)
|
|
94
|
+
return result # 返回能排序的部分
|
|
95
|
+
return result
|
|
96
|
+
|
|
97
|
+
def has_cycle(self) -> bool:
|
|
98
|
+
"""检测是否有循环依赖"""
|
|
99
|
+
sorted_nodes = self.topological_sort()
|
|
100
|
+
return len(sorted_nodes) < len(self.nodes)
|
|
101
|
+
|
|
102
|
+
def to_dict(self) -> dict:
|
|
103
|
+
return {
|
|
104
|
+
"nodes": {k: {
|
|
105
|
+
"id": v.id, "name": v.name, "type": v.node_type,
|
|
106
|
+
"status": v.status, "depends_on": v.depends_on,
|
|
107
|
+
"error": v.error,
|
|
108
|
+
} for k, v in self.nodes.items()},
|
|
109
|
+
"edges": self.edges,
|
|
110
|
+
"has_cycle": self.has_cycle(),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# ─────────────────────────────────────────────
|
|
115
|
+
# 从安装计划构建依赖链
|
|
116
|
+
# ─────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
# 工具依赖映射:命令 → 需要的系统工具
|
|
119
|
+
_TOOL_DEPS = {
|
|
120
|
+
"git clone": ["git"],
|
|
121
|
+
"git pull": ["git"],
|
|
122
|
+
"pip install": ["python", "pip"],
|
|
123
|
+
"pip3 install": ["python3", "pip3"],
|
|
124
|
+
"python -m venv": ["python"],
|
|
125
|
+
"python3 -m venv": ["python3"],
|
|
126
|
+
"npm install": ["node", "npm"],
|
|
127
|
+
"npm ci": ["node", "npm"],
|
|
128
|
+
"npx": ["node", "npx"],
|
|
129
|
+
"cargo build": ["rustc", "cargo"],
|
|
130
|
+
"go build": ["go"],
|
|
131
|
+
"go mod": ["go"],
|
|
132
|
+
"docker": ["docker"],
|
|
133
|
+
"docker compose": ["docker", "docker-compose"],
|
|
134
|
+
"conda": ["conda"],
|
|
135
|
+
"brew": ["brew"],
|
|
136
|
+
"cmake": ["cmake"],
|
|
137
|
+
"make": ["make"],
|
|
138
|
+
"gcc": ["gcc"],
|
|
139
|
+
"g++": ["g++"],
|
|
140
|
+
"curl": ["curl"],
|
|
141
|
+
"wget": ["wget"],
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# 常见失败根因映射
|
|
145
|
+
_FAILURE_PATTERNS = {
|
|
146
|
+
r"command not found: gcc|No such file.*gcc|gcc.*not found": {
|
|
147
|
+
"root_cause": "缺少 C/C++ 编译器 (gcc)",
|
|
148
|
+
"suggestion_darwin": "xcode-select --install",
|
|
149
|
+
"suggestion_linux": "sudo apt-get install build-essential",
|
|
150
|
+
"suggestion": "安装 C/C++ 编译器",
|
|
151
|
+
},
|
|
152
|
+
r"command not found: cmake|cmake.*not found": {
|
|
153
|
+
"root_cause": "缺少 CMake",
|
|
154
|
+
"suggestion_darwin": "brew install cmake",
|
|
155
|
+
"suggestion_linux": "sudo apt-get install cmake",
|
|
156
|
+
"suggestion": "安装 CMake",
|
|
157
|
+
},
|
|
158
|
+
r"No module named|ModuleNotFoundError": {
|
|
159
|
+
"root_cause": "Python 模块缺失",
|
|
160
|
+
"suggestion": "确认 pip install 步骤已成功,或检查 Python 环境",
|
|
161
|
+
},
|
|
162
|
+
r"EACCES|Permission denied": {
|
|
163
|
+
"root_cause": "权限不足",
|
|
164
|
+
"suggestion": "检查目录权限,或使用 --dir 指定安装目录",
|
|
165
|
+
},
|
|
166
|
+
r"Could not resolve host|Network is unreachable|ConnectionError": {
|
|
167
|
+
"root_cause": "网络连接失败",
|
|
168
|
+
"suggestion": "检查网络连接,或设置代理 (HTTPS_PROXY)",
|
|
169
|
+
},
|
|
170
|
+
r"No space left|ENOSPC|disk full": {
|
|
171
|
+
"root_cause": "磁盘空间不足",
|
|
172
|
+
"suggestion": "释放磁盘空间后重试",
|
|
173
|
+
},
|
|
174
|
+
r"CUDA|nvidia|GPU.*not found|torch.*cuda": {
|
|
175
|
+
"root_cause": "GPU/CUDA 驱动问题",
|
|
176
|
+
"suggestion": "检查 NVIDIA 驱动或使用 CPU 模式安装",
|
|
177
|
+
},
|
|
178
|
+
r"SSL.*certificate|CERTIFICATE_VERIFY_FAILED": {
|
|
179
|
+
"root_cause": "SSL 证书验证失败",
|
|
180
|
+
"suggestion_darwin": "安装 certifi: pip install certifi",
|
|
181
|
+
"suggestion": "更新 CA 证书或检查网络代理",
|
|
182
|
+
},
|
|
183
|
+
r"fatal: repository.*not found|404.*Not Found": {
|
|
184
|
+
"root_cause": "仓库不存在或无权限",
|
|
185
|
+
"suggestion": "检查仓库地址是否正确,私有仓库需要 GITHUB_TOKEN",
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def build_chain_from_plan(plan: dict,
|
|
191
|
+
env: dict = None) -> DepChain:
|
|
192
|
+
"""从安装计划构建依赖链"""
|
|
193
|
+
chain = DepChain()
|
|
194
|
+
steps = plan.get("steps", [])
|
|
195
|
+
env = env or {}
|
|
196
|
+
|
|
197
|
+
# 添加步骤节点
|
|
198
|
+
prev_id = None
|
|
199
|
+
for i, step in enumerate(steps):
|
|
200
|
+
node_id = f"step_{i}"
|
|
201
|
+
node = DepNode(
|
|
202
|
+
id=node_id,
|
|
203
|
+
name=step.get("description", f"Step {i+1}"),
|
|
204
|
+
node_type="step",
|
|
205
|
+
)
|
|
206
|
+
chain.add_node(node)
|
|
207
|
+
|
|
208
|
+
# 检测该步骤需要的系统工具
|
|
209
|
+
cmd = step.get("command", "")
|
|
210
|
+
for pattern, tools in _TOOL_DEPS.items():
|
|
211
|
+
if pattern in cmd.lower():
|
|
212
|
+
for tool in tools:
|
|
213
|
+
tool_id = f"tool_{tool}"
|
|
214
|
+
if tool_id not in chain.nodes:
|
|
215
|
+
chain.add_node(DepNode(
|
|
216
|
+
id=tool_id, name=tool,
|
|
217
|
+
node_type="tool",
|
|
218
|
+
status="completed", # 假设工具已有
|
|
219
|
+
))
|
|
220
|
+
chain.add_edge(tool_id, node_id)
|
|
221
|
+
|
|
222
|
+
# 步骤间的顺序依赖
|
|
223
|
+
if prev_id:
|
|
224
|
+
chain.add_edge(prev_id, node_id)
|
|
225
|
+
prev_id = node_id
|
|
226
|
+
|
|
227
|
+
return chain
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def trace_failure_lineage(chain: DepChain, failed_step: int,
|
|
231
|
+
error_output: str = "",
|
|
232
|
+
os_type: str = "") -> list[FailureLineage]:
|
|
233
|
+
"""追溯失败的血统链"""
|
|
234
|
+
lineages = []
|
|
235
|
+
|
|
236
|
+
# 1. 基于错误输出模式匹配
|
|
237
|
+
for pattern, info in _FAILURE_PATTERNS.items():
|
|
238
|
+
if re.search(pattern, error_output, re.IGNORECASE):
|
|
239
|
+
suggestion = info.get(f"suggestion_{os_type}", info["suggestion"])
|
|
240
|
+
lineage = FailureLineage(
|
|
241
|
+
root_cause=info["root_cause"],
|
|
242
|
+
chain=[info["root_cause"], f"Step {failed_step + 1} 失败"],
|
|
243
|
+
suggestion=suggestion,
|
|
244
|
+
confidence=0.8,
|
|
245
|
+
)
|
|
246
|
+
lineages.append(lineage)
|
|
247
|
+
|
|
248
|
+
# 2. 基于依赖链追溯
|
|
249
|
+
step_id = f"step_{failed_step}"
|
|
250
|
+
deps = chain.get_dependencies(step_id)
|
|
251
|
+
for dep_id in deps:
|
|
252
|
+
dep_node = chain.nodes.get(dep_id)
|
|
253
|
+
if dep_node and dep_node.status == "failed":
|
|
254
|
+
lineage = FailureLineage(
|
|
255
|
+
root_cause=f"前置依赖 '{dep_node.name}' 失败",
|
|
256
|
+
chain=[dep_node.name, chain.nodes[step_id].name],
|
|
257
|
+
suggestion=f"先修复 '{dep_node.name}' 的问题",
|
|
258
|
+
confidence=0.9,
|
|
259
|
+
)
|
|
260
|
+
lineages.append(lineage)
|
|
261
|
+
|
|
262
|
+
# 3. 如果没有匹配到,给通用建议
|
|
263
|
+
if not lineages:
|
|
264
|
+
lineages.append(FailureLineage(
|
|
265
|
+
root_cause="未知原因",
|
|
266
|
+
chain=[f"Step {failed_step + 1} 失败"],
|
|
267
|
+
suggestion="查看完整错误日志或使用 --llm 模式让 AI 分析",
|
|
268
|
+
confidence=0.3,
|
|
269
|
+
))
|
|
270
|
+
|
|
271
|
+
return sorted(lineages, key=lambda l: l.confidence, reverse=True)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# ─────────────────────────────────────────────
|
|
275
|
+
# 可视化
|
|
276
|
+
# ─────────────────────────────────────────────
|
|
277
|
+
|
|
278
|
+
def format_dep_chain(chain: DepChain) -> str:
|
|
279
|
+
"""ASCII 可视化依赖链"""
|
|
280
|
+
lines = ["🔗 安装依赖链:", ""]
|
|
281
|
+
|
|
282
|
+
status_icons = {
|
|
283
|
+
"pending": "⏳",
|
|
284
|
+
"running": "🔄",
|
|
285
|
+
"completed": "✅",
|
|
286
|
+
"failed": "❌",
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
# 先显示工具节点
|
|
290
|
+
tools = [n for n in chain.nodes.values() if n.node_type == "tool"]
|
|
291
|
+
if tools:
|
|
292
|
+
lines.append(" 系统工具:")
|
|
293
|
+
for t in tools:
|
|
294
|
+
icon = status_icons.get(t.status, "?")
|
|
295
|
+
lines.append(f" {icon} {t.name}")
|
|
296
|
+
lines.append(" │")
|
|
297
|
+
lines.append(" ▼")
|
|
298
|
+
|
|
299
|
+
# 显示步骤节点
|
|
300
|
+
steps = sorted(
|
|
301
|
+
[n for n in chain.nodes.values() if n.node_type == "step"],
|
|
302
|
+
key=lambda n: n.id,
|
|
303
|
+
)
|
|
304
|
+
for i, step in enumerate(steps):
|
|
305
|
+
icon = status_icons.get(step.status, "?")
|
|
306
|
+
err = f" → {step.error[:60]}" if step.error else ""
|
|
307
|
+
dur = f" ({step.duration_sec:.1f}s)" if step.duration_sec > 0 else ""
|
|
308
|
+
lines.append(f" {icon} {step.name}{dur}{err}")
|
|
309
|
+
if i < len(steps) - 1:
|
|
310
|
+
lines.append(" │")
|
|
311
|
+
lines.append(" ▼")
|
|
312
|
+
|
|
313
|
+
# 循环依赖警告
|
|
314
|
+
if chain.has_cycle():
|
|
315
|
+
lines.append("")
|
|
316
|
+
lines.append(" ⚠️ 警告:检测到循环依赖!")
|
|
317
|
+
|
|
318
|
+
return "\n".join(lines)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def format_lineage(lineages: list[FailureLineage]) -> str:
|
|
322
|
+
"""格式化失败血统链"""
|
|
323
|
+
if not lineages:
|
|
324
|
+
return " 无法追溯失败原因"
|
|
325
|
+
|
|
326
|
+
lines = ["🔍 失败根因分析:", ""]
|
|
327
|
+
for i, l in enumerate(lineages[:3]): # 最多显示 3 条
|
|
328
|
+
conf = f"[置信度 {l.confidence:.0%}]"
|
|
329
|
+
lines.append(f" {i+1}. {l.root_cause} {conf}")
|
|
330
|
+
if len(l.chain) > 1:
|
|
331
|
+
chain_str = " → ".join(l.chain)
|
|
332
|
+
lines.append(f" 链路:{chain_str}")
|
|
333
|
+
if l.suggestion:
|
|
334
|
+
lines.append(f" 💡 建议:{l.suggestion}")
|
|
335
|
+
lines.append("")
|
|
336
|
+
|
|
337
|
+
return "\n".join(lines)
|