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
gitinstall/__init__.py ADDED
@@ -0,0 +1,61 @@
1
+ """
2
+ gitinstall — 嵌入式软件安装引擎
3
+ ================================
4
+
5
+ 在任何项目中嵌入 gitinstall,获得跨平台软件安装能力。
6
+
7
+ 核心函数::
8
+
9
+ import gitinstall
10
+
11
+ env = gitinstall.detect() # 检测系统环境
12
+ plan = gitinstall.plan("owner/repo", env=env) # 生成安装方案
13
+ result = gitinstall.install(plan) # 执行安装
14
+ fix = gitinstall.diagnose(stderr, command) # 报错诊断
15
+
16
+ 辅助函数::
17
+
18
+ info = gitinstall.fetch("owner/repo") # 获取项目信息
19
+ report = gitinstall.doctor() # 系统诊断
20
+ audit = gitinstall.audit("owner/repo") # 依赖安全审计
21
+
22
+ 零外部依赖 · 跨平台 · 线程安全 · AI 可选增强
23
+ """
24
+
25
+ from ._sdk import (
26
+ detect,
27
+ plan,
28
+ install,
29
+ diagnose,
30
+ fetch,
31
+ doctor,
32
+ audit,
33
+ __version__,
34
+ )
35
+
36
+ from .tool_schemas import (
37
+ openai_tools,
38
+ anthropic_tools,
39
+ gemini_tools,
40
+ json_schemas,
41
+ call_tool,
42
+ tool_names,
43
+ )
44
+
45
+ __all__ = [
46
+ "detect",
47
+ "plan",
48
+ "install",
49
+ "diagnose",
50
+ "fetch",
51
+ "doctor",
52
+ "audit",
53
+ "__version__",
54
+ # AI 集成
55
+ "openai_tools",
56
+ "anthropic_tools",
57
+ "gemini_tools",
58
+ "json_schemas",
59
+ "call_tool",
60
+ "tool_names",
61
+ ]
gitinstall/_sdk.py ADDED
@@ -0,0 +1,541 @@
1
+ """
2
+ gitinstall SDK — 嵌入式软件安装引擎的公共 API
3
+ ==============================================
4
+
5
+ 四个核心函数:
6
+ detect() → 检测系统环境
7
+ plan() → 生成安装方案
8
+ install() → 执行安装
9
+ diagnose() → 报错诊断
10
+
11
+ 辅助函数:
12
+ fetch() → 获取项目信息
13
+ doctor() → 系统诊断
14
+ audit() → 依赖安全审计
15
+
16
+ 用法::
17
+
18
+ import gitinstall
19
+
20
+ env = gitinstall.detect()
21
+ plan = gitinstall.plan("comfyanonymous/ComfyUI", env=env)
22
+ result = gitinstall.install(plan)
23
+
24
+ if not result["success"]:
25
+ fix = gitinstall.diagnose(result["error_message"], result["last_command"])
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import sys
31
+ from pathlib import Path
32
+ from typing import Callable, Optional
33
+
34
+ # 确保 tools/ 目录在 sys.path 中,使内部 bare import 正常工作
35
+ _THIS_DIR = Path(__file__).parent
36
+ if str(_THIS_DIR) not in sys.path:
37
+ sys.path.insert(0, str(_THIS_DIR))
38
+
39
+
40
+ # ── 核心函数 1:detect ───────────────────────
41
+
42
+ def detect() -> dict:
43
+ """检测当前系统环境。
44
+
45
+ Returns:
46
+ dict: 包含 os/hardware/gpu/runtimes/package_managers/disk 等信息。
47
+
48
+ 示例::
49
+
50
+ env = gitinstall.detect()
51
+ print(env["gpu"]["type"]) # "apple_mps"
52
+ print(env["os"]["arch"]) # "arm64"
53
+ """
54
+ from detector import EnvironmentDetector
55
+ return EnvironmentDetector().detect()
56
+
57
+
58
+ # ── 核心函数 2:plan ─────────────────────────
59
+
60
+ def plan(
61
+ identifier: str,
62
+ *,
63
+ env: dict = None,
64
+ llm: str = None,
65
+ local: bool = False,
66
+ ) -> dict:
67
+ """为指定项目生成安装方案。
68
+
69
+ Args:
70
+ identifier: GitHub 项目标识,如 "comfyanonymous/ComfyUI" 或 URL。
71
+ env: 环境信息 dict(来自 detect())。省略则自动检测。
72
+ llm: 指定 LLM(如 "ollama"/"openai"/"none")。省略则自动选择。
73
+ local: True 时用 git clone 本地分析,避免 GitHub API 限额。
74
+
75
+ Returns:
76
+ dict: 包含 steps/launch_command/confidence/strategy 等字段。
77
+ 失败时 status="error"。
78
+
79
+ 示例::
80
+
81
+ plan = gitinstall.plan("pytorch/pytorch")
82
+ for step in plan["steps"]:
83
+ print(step["command"])
84
+ """
85
+ from main import cmd_plan
86
+ result = cmd_plan(identifier, llm_force=llm, use_local=local)
87
+
88
+ # 如果调用方提供了 env 但 cmd_plan 内部会自行检测,
89
+ # 这里的 env 参数用于未来优化(避免重复检测)。
90
+ # 当前版本 cmd_plan 总是自行检测环境。
91
+
92
+ return result
93
+
94
+
95
+ # ── 核心函数 3:install ──────────────────────
96
+
97
+ def install(
98
+ plan_or_identifier,
99
+ *,
100
+ install_dir: str = None,
101
+ llm: str = None,
102
+ local: bool = False,
103
+ dry_run: bool = False,
104
+ on_progress: Optional[Callable[[dict], None]] = None,
105
+ ) -> dict:
106
+ """执行安装。
107
+
108
+ 可以传入 plan() 返回的 dict,或直接传项目标识符(会自动调用 plan)。
109
+
110
+ Args:
111
+ plan_or_identifier: plan() 的返回值 dict,或项目标识字符串。
112
+ install_dir: 安装目录。省略则使用默认目录。
113
+ llm: 指定 LLM。
114
+ local: 使用本地模式获取项目信息。
115
+ dry_run: True 时只展示计划不执行。
116
+ on_progress: 进度回调函数,接收 event dict。
117
+
118
+ Returns:
119
+ dict: 包含 success/install_dir/launch_command/steps_completed 等字段。
120
+
121
+ 回调事件::
122
+
123
+ on_progress 会收到以下类型的 event:
124
+ {"type": "plan_ready", "steps": [...], "project": "..."}
125
+ {"type": "step_start", "step": 1, "total": 5, "command": "...", "description": "..."}
126
+ {"type": "step_done", "step": 1, "total": 5, "success": True, "duration": 2.3}
127
+ {"type": "step_failed","step": 1, "total": 5, "error": "..."}
128
+ {"type": "install_done", "success": True, "install_dir": "..."}
129
+
130
+ 示例::
131
+
132
+ # 方式 1:直接传标识符
133
+ result = gitinstall.install("comfyanonymous/ComfyUI")
134
+
135
+ # 方式 2:传 plan dict
136
+ p = gitinstall.plan("comfyanonymous/ComfyUI")
137
+ result = gitinstall.install(p)
138
+
139
+ # 方式 3:带进度回调
140
+ def progress(event):
141
+ print(f"[{event.get('step', '?')}/{event.get('total', '?')}] {event.get('type')}")
142
+ result = gitinstall.install("comfyanonymous/ComfyUI", on_progress=progress)
143
+ """
144
+ # 如果传入的是字符串,走完整的 cmd_install 路径
145
+ if isinstance(plan_or_identifier, str):
146
+ if on_progress:
147
+ return _install_with_progress(
148
+ plan_or_identifier,
149
+ install_dir=install_dir,
150
+ llm=llm,
151
+ local=local,
152
+ dry_run=dry_run,
153
+ on_progress=on_progress,
154
+ )
155
+ from main import cmd_install
156
+ return cmd_install(
157
+ plan_or_identifier,
158
+ install_dir=install_dir,
159
+ llm_force=llm,
160
+ dry_run=dry_run,
161
+ use_local=local,
162
+ )
163
+
164
+ # 如果传入的是 plan dict,直接执行
165
+ if isinstance(plan_or_identifier, dict):
166
+ plan_dict = plan_or_identifier
167
+ if plan_dict.get("status") == "error":
168
+ return plan_dict
169
+
170
+ plan_data = plan_dict.get("plan", plan_dict)
171
+ project_name = plan_dict.get("project", "")
172
+ steps = plan_data.get("steps", [])
173
+
174
+ if not steps:
175
+ return {"status": "error", "message": "安装方案中没有步骤。", "success": False}
176
+
177
+ if dry_run:
178
+ return {"status": "ok", "dry_run": True, "plan": plan_data, "success": True}
179
+
180
+ from executor import InstallExecutor
181
+ from llm import create_provider
182
+ from pathlib import Path
183
+
184
+ llm_provider = create_provider(force=llm)
185
+ executor = InstallExecutor(llm_provider=llm_provider, verbose=True)
186
+
187
+ if install_dir:
188
+ base_dir = str(Path(install_dir).expanduser())
189
+ Path(base_dir).mkdir(parents=True, exist_ok=True)
190
+ executor.executor.work_dir = base_dir
191
+ executor.executor._current_dir = base_dir
192
+
193
+ if on_progress:
194
+ on_progress({
195
+ "type": "plan_ready",
196
+ "steps": [s.get("description", "") for s in steps],
197
+ "project": project_name,
198
+ })
199
+
200
+ result = _execute_with_callbacks(executor, plan_data, project_name, on_progress)
201
+ return result
202
+
203
+ raise TypeError(f"install() 需要 str 或 dict,收到 {type(plan_or_identifier).__name__}")
204
+
205
+
206
+ def _install_with_progress(
207
+ identifier: str,
208
+ *,
209
+ install_dir: str = None,
210
+ llm: str = None,
211
+ local: bool = False,
212
+ dry_run: bool = False,
213
+ on_progress: Callable,
214
+ ) -> dict:
215
+ """带进度回调的完整安装流程。"""
216
+ # 1. 规划
217
+ on_progress({"type": "detecting", "message": "检测系统环境..."})
218
+ plan_result = plan(identifier, llm=llm, local=local)
219
+
220
+ if plan_result.get("status") != "ok":
221
+ return plan_result
222
+
223
+ plan_data = plan_result["plan"]
224
+ project_name = plan_result.get("project", identifier)
225
+ steps = plan_data.get("steps", [])
226
+
227
+ on_progress({
228
+ "type": "plan_ready",
229
+ "steps": [s.get("description", "") for s in steps],
230
+ "project": project_name,
231
+ "confidence": plan_result.get("confidence", ""),
232
+ "strategy": plan_data.get("strategy", ""),
233
+ })
234
+
235
+ if dry_run:
236
+ return {"status": "ok", "dry_run": True, "plan": plan_data, "success": True}
237
+
238
+ # 2. 预检
239
+ from resilience import preflight_check
240
+ pf = preflight_check(steps)
241
+ if not pf.all_ready:
242
+ on_progress({
243
+ "type": "preflight",
244
+ "missing_tools": pf.missing_tools,
245
+ "install_commands": [s.get("command", "") for s in pf.install_commands],
246
+ })
247
+ steps = pf.install_commands + steps
248
+ plan_data["steps"] = steps
249
+
250
+ # 3. 执行
251
+ from executor import InstallExecutor
252
+ from llm import create_provider
253
+ from pathlib import Path
254
+
255
+ llm_provider = create_provider(force=llm)
256
+ executor = InstallExecutor(llm_provider=llm_provider, verbose=True)
257
+
258
+ if install_dir:
259
+ base_dir = str(Path(install_dir).expanduser())
260
+ Path(base_dir).mkdir(parents=True, exist_ok=True)
261
+ executor.executor.work_dir = base_dir
262
+ executor.executor._current_dir = base_dir
263
+
264
+ result = _execute_with_callbacks(executor, plan_data, project_name, on_progress)
265
+
266
+ # 4. 失败时尝试回退
267
+ if not result["success"]:
268
+ from resilience import generate_fallback_plans
269
+ owner, _, repo = identifier.partition("/")
270
+ env = detect()
271
+ project_types = plan_result.get("_project_types", [])
272
+ dep_files = plan_result.get("_dependency_files", {})
273
+ primary_strategy = plan_data.get("strategy", "")
274
+
275
+ fallback_plans = generate_fallback_plans(
276
+ owner, repo, project_types, env, dependency_files=dep_files,
277
+ )
278
+ tried = {primary_strategy}
279
+
280
+ for fb in fallback_plans:
281
+ if fb.strategy in tried:
282
+ continue
283
+ tried.add(fb.strategy)
284
+
285
+ on_progress({
286
+ "type": "fallback_start",
287
+ "strategy": fb.strategy,
288
+ "tier": fb.tier,
289
+ })
290
+
291
+ fb_plan = {
292
+ "steps": fb.steps,
293
+ "launch_command": plan_data.get("launch_command", ""),
294
+ "strategy": fb.strategy,
295
+ }
296
+ executor.executor.reset(install_dir)
297
+ fb_result = _execute_with_callbacks(executor, fb_plan, project_name, on_progress)
298
+
299
+ if fb_result["success"]:
300
+ result = fb_result
301
+ break
302
+
303
+ # 5. 遥测记录
304
+ try:
305
+ from db import record_install_telemetry
306
+ record_install_telemetry(
307
+ project=project_name,
308
+ strategy=plan_data.get("strategy", ""),
309
+ gpu_info=plan_result.get("gpu_info"),
310
+ env=plan_result.get("_env") or {},
311
+ success=result["success"],
312
+ error_type="step_failed" if not result["success"] else None,
313
+ error_message=result.get("error_summary") if not result["success"] else None,
314
+ duration_sec=None,
315
+ steps_total=result.get("steps_total", 0),
316
+ steps_completed=result.get("steps_completed", 0),
317
+ )
318
+ except Exception:
319
+ pass
320
+
321
+ return result
322
+
323
+
324
+ def _execute_with_callbacks(
325
+ executor,
326
+ plan_data: dict,
327
+ project_name: str,
328
+ on_progress: Optional[Callable] = None,
329
+ ) -> dict:
330
+ """执行安装计划,在每步前后触发回调。"""
331
+ from executor import check_command_safety
332
+
333
+ steps = plan_data.get("steps", [])
334
+ launch_command = plan_data.get("launch_command", "")
335
+ completed = []
336
+ error_summary = ""
337
+
338
+ for i, step in enumerate(steps, 1):
339
+ command = step.get("command", "").strip()
340
+ description = step.get("description", f"步骤 {i}")
341
+ working_dir = step.get("working_dir", "")
342
+
343
+ if not command:
344
+ continue
345
+
346
+ is_safe, warning = check_command_safety(command)
347
+ if not is_safe:
348
+ error_summary = warning
349
+ if on_progress:
350
+ on_progress({
351
+ "type": "step_blocked",
352
+ "step": i, "total": len(steps),
353
+ "command": command, "reason": warning,
354
+ })
355
+ break
356
+
357
+ if on_progress:
358
+ on_progress({
359
+ "type": "step_start",
360
+ "step": i, "total": len(steps),
361
+ "command": command, "description": description,
362
+ })
363
+
364
+ step_result = executor.executor.run(command, working_dir or None)
365
+
366
+ if step_result.success:
367
+ completed.append(step_result)
368
+ if on_progress:
369
+ on_progress({
370
+ "type": "step_done",
371
+ "step": i, "total": len(steps),
372
+ "success": True,
373
+ "duration": step_result.duration_sec,
374
+ })
375
+ else:
376
+ # 尝试自动修复
377
+ fixed = executor._try_fix(step_result, i, len(steps))
378
+ if fixed:
379
+ completed.append(step_result)
380
+ if on_progress:
381
+ on_progress({
382
+ "type": "step_fixed",
383
+ "step": i, "total": len(steps),
384
+ "fix": step_result.fix_command,
385
+ })
386
+ else:
387
+ error_summary = (
388
+ f"第 {i} 步失败:{description}\n"
389
+ f"命令:{command}\n"
390
+ f"报错:{step_result.stderr[:500]}"
391
+ )
392
+ if on_progress:
393
+ on_progress({
394
+ "type": "step_failed",
395
+ "step": i, "total": len(steps),
396
+ "error": step_result.stderr[:500],
397
+ "command": command,
398
+ })
399
+ break
400
+
401
+ success = len(completed) == len([s for s in steps if s.get("command", "").strip()])
402
+ install_dir = executor.executor._current_dir
403
+
404
+ if on_progress:
405
+ on_progress({
406
+ "type": "install_done",
407
+ "success": success,
408
+ "install_dir": install_dir,
409
+ "steps_completed": len(completed),
410
+ "steps_total": len(steps),
411
+ })
412
+
413
+ return {
414
+ "status": "ok" if success else "error",
415
+ "success": success,
416
+ "project": project_name,
417
+ "install_dir": install_dir,
418
+ "launch_command": launch_command,
419
+ "error_summary": error_summary,
420
+ "steps_completed": len(completed),
421
+ "steps_total": len(steps),
422
+ "plan_strategy": plan_data.get("strategy", ""),
423
+ }
424
+
425
+
426
+ # ── 核心函数 4:diagnose ────────────────────
427
+
428
+ def diagnose(
429
+ stderr: str,
430
+ command: str = "",
431
+ stdout: str = "",
432
+ ) -> dict | None:
433
+ """诊断安装报错并返回修复建议。
434
+
435
+ Args:
436
+ stderr: 错误输出文本。
437
+ command: 引发错误的命令。
438
+ stdout: 标准输出文本(可选)。
439
+
440
+ Returns:
441
+ dict: 包含 root_cause/fix_commands/confidence 等字段。
442
+ 如果无法诊断,返回 None。
443
+
444
+ 示例::
445
+
446
+ fix = gitinstall.diagnose(
447
+ stderr="error: externally-managed-environment",
448
+ command="pip install pandas"
449
+ )
450
+ if fix:
451
+ print(fix["fix_commands"]) # ["pip install --break-system-packages pandas"]
452
+ """
453
+ from error_fixer import diagnose as _diagnose
454
+ result = _diagnose(command, stderr, stdout)
455
+ if result is None:
456
+ return None
457
+ return {
458
+ "root_cause": result.root_cause,
459
+ "fix_commands": result.fix_commands,
460
+ "retry_original": result.retry_original,
461
+ "confidence": result.confidence,
462
+ "outcome": result.outcome,
463
+ }
464
+
465
+
466
+ # ── 辅助函数 ────────────────────────────────
467
+
468
+ def fetch(identifier: str, *, local: bool = False) -> dict:
469
+ """获取 GitHub 项目信息。
470
+
471
+ Args:
472
+ identifier: 项目标识("owner/repo" 或 URL)。
473
+ local: True 时用 git clone 本地分析。
474
+
475
+ Returns:
476
+ dict: 包含项目名、描述、星标、语言、类型、依赖文件等。
477
+ """
478
+ if local:
479
+ from fetcher import fetch_project_local
480
+ info = fetch_project_local(identifier)
481
+ else:
482
+ from fetcher import fetch_project
483
+ info = fetch_project(identifier)
484
+ return {
485
+ "full_name": info.full_name,
486
+ "description": info.description,
487
+ "stars": info.stars,
488
+ "language": info.language,
489
+ "license": info.license,
490
+ "project_type": info.project_type,
491
+ "clone_url": info.clone_url,
492
+ "homepage": info.homepage,
493
+ "dependency_files": list(info.dependency_files.keys()),
494
+ "readme_preview": info.readme[:500] if info.readme else "",
495
+ }
496
+
497
+
498
+ def doctor() -> dict:
499
+ """运行系统诊断,返回检查结果。
500
+
501
+ Returns:
502
+ dict: 包含 checks 列表和 all_ok 布尔值。
503
+ """
504
+ from doctor import run_doctor
505
+ report = run_doctor()
506
+ return {
507
+ "status": "ok",
508
+ "all_ok": report.all_ok,
509
+ "ok_count": report.ok_count,
510
+ "warn_count": report.warn_count,
511
+ "error_count": report.error_count,
512
+ "checks": [
513
+ {
514
+ "name": c.name,
515
+ "level": c.level,
516
+ "message": c.message,
517
+ "detail": c.detail,
518
+ "fix_hint": c.fix_hint,
519
+ }
520
+ for c in report.checks
521
+ ],
522
+ }
523
+
524
+
525
+ def audit(identifier: str, *, online: bool = False) -> dict:
526
+ """审计项目依赖的安全漏洞。
527
+
528
+ Args:
529
+ identifier: 项目标识。
530
+ online: True 时联网查询 CVE 数据库。
531
+
532
+ Returns:
533
+ dict: 包含各生态系统的漏洞报告。
534
+ """
535
+ from main import cmd_audit
536
+ return cmd_audit(identifier, online=online)
537
+
538
+
539
+ # ── 版本信息 ─────────────────────────────────
540
+
541
+ __version__ = "1.1.0"