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
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""
|
|
2
|
+
planner_helpers.py - 平台适配辅助函数
|
|
3
|
+
======================================
|
|
4
|
+
|
|
5
|
+
从 planner.py 拆分出来的纯函数,无状态。
|
|
6
|
+
提供 OS/GPU/工具链检测、命令生成等基础设施。
|
|
7
|
+
|
|
8
|
+
GPU 自适应(自动选择正确的 PyTorch 安装命令):
|
|
9
|
+
- Apple MPS (M1/M2/M3/M4) → pip install torch(原生支持 MPS)
|
|
10
|
+
- NVIDIA CUDA 12+ → --index-url .../cu121
|
|
11
|
+
- NVIDIA CUDA 11 → --index-url .../cu118
|
|
12
|
+
- AMD ROCm → --index-url .../rocm5.6
|
|
13
|
+
- CPU 纯算 → --index-url .../cpu
|
|
14
|
+
|
|
15
|
+
平台自适应:
|
|
16
|
+
- macOS → brew / python3 -m venv / source .../activate
|
|
17
|
+
- Linux → apt/snap / python3 / source .../activate
|
|
18
|
+
- Windows → winget/choco / python -m venv / .\\venv\\Scripts\\activate
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import re
|
|
24
|
+
import json
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ─────────────────────────────────────────────
|
|
30
|
+
# 平台适配辅助函数(纯函数,无状态)
|
|
31
|
+
# ─────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
def _os_type(env: dict) -> str:
|
|
34
|
+
return env.get("os", {}).get("type", "linux")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _is_apple_silicon(env: dict) -> bool:
|
|
38
|
+
return (
|
|
39
|
+
env.get("os", {}).get("is_apple_silicon", False)
|
|
40
|
+
or env.get("os", {}).get("chip", "").startswith("M")
|
|
41
|
+
or (env.get("os", {}).get("type") == "macos"
|
|
42
|
+
and env.get("os", {}).get("arch") == "arm64")
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _gpu_type(env: dict) -> str:
|
|
47
|
+
t = env.get("gpu", {}).get("type", "cpu_only")
|
|
48
|
+
# 兼容 detector.py 的不同写法
|
|
49
|
+
if t in ("apple_mps", "mps"):
|
|
50
|
+
return "mps"
|
|
51
|
+
if t == "cuda":
|
|
52
|
+
return "cuda"
|
|
53
|
+
if t == "rocm":
|
|
54
|
+
return "rocm"
|
|
55
|
+
return "cpu_only"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _cuda_major(env: dict) -> int:
|
|
59
|
+
ver = env.get("gpu", {}).get("cuda_version", "0")
|
|
60
|
+
try:
|
|
61
|
+
return int(str(ver).split(".")[0])
|
|
62
|
+
except Exception:
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _has_pm(env: dict, pm: str) -> bool:
|
|
67
|
+
return pm in env.get("package_managers", {})
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _has_runtime(env: dict, rt: str) -> bool:
|
|
71
|
+
return rt in env.get("runtimes", {})
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _python_cmd(env: dict) -> str:
|
|
75
|
+
"""Linux 上优先用 python3,其他用 python"""
|
|
76
|
+
if _os_type(env) == "linux":
|
|
77
|
+
return "python3"
|
|
78
|
+
return "python"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _pip_cmd(env: dict) -> str:
|
|
82
|
+
if _os_type(env) == "linux":
|
|
83
|
+
return "pip3"
|
|
84
|
+
return "pip"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _venv_activate(env: dict, venv_name: str = "venv") -> str:
|
|
88
|
+
"""返回平台正确的 venv 激活命令"""
|
|
89
|
+
if _os_type(env) == "windows":
|
|
90
|
+
return f"{venv_name}\\Scripts\\activate"
|
|
91
|
+
return f"source {venv_name}/bin/activate"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _dep_names(dependency_files: dict[str, str]) -> set[str]:
|
|
95
|
+
return {Path(path).name for path in dependency_files}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _dep_content(dependency_files: dict[str, str], name: str) -> str:
|
|
99
|
+
for path, content in dependency_files.items():
|
|
100
|
+
if Path(path).name == name and "/" not in path:
|
|
101
|
+
return content
|
|
102
|
+
for path, content in dependency_files.items():
|
|
103
|
+
if Path(path).name == name:
|
|
104
|
+
return content
|
|
105
|
+
return ""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _is_maturin_project(types: set[str], dependency_files: dict[str, str]) -> bool:
|
|
109
|
+
if not ({"python", "rust"} <= types):
|
|
110
|
+
return False
|
|
111
|
+
pyproject = _dep_content(dependency_files, "pyproject.toml")
|
|
112
|
+
return bool(pyproject and re.search(r"maturin|setuptools-rust", pyproject, re.IGNORECASE))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _preferred_java_version(readme: str) -> str:
|
|
116
|
+
match = re.search(r"(?:openjdk|jdk|java)[^\d]{0,8}(8|11|17|21)", readme, re.IGNORECASE)
|
|
117
|
+
return match.group(1) if match else "17"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _has_haskell_cabal_file(dependency_files: dict[str, str]) -> bool:
|
|
121
|
+
return any(Path(path).name.endswith(".cabal") for path in dependency_files)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _stack_resolver(dependency_files: dict[str, str]) -> str:
|
|
125
|
+
stack_yaml = _dep_content(dependency_files, "stack.yaml")
|
|
126
|
+
match = re.search(r'^\s*resolver\s*:\s*["\']?([^\s"\']+)', stack_yaml, re.MULTILINE)
|
|
127
|
+
return match.group(1) if match else ""
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _stack_lts_major(resolver: str) -> int:
|
|
131
|
+
match = re.match(r'lts-(\d+)(?:\.\d+)?', resolver, re.IGNORECASE)
|
|
132
|
+
return int(match.group(1)) if match else 0
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _zig_minimum_version(dependency_files: dict[str, str]) -> str:
|
|
136
|
+
build_zon = _dep_content(dependency_files, "build.zig.zon")
|
|
137
|
+
match = re.search(r'\.minimum_zig_version\s*=\s*"([^"]+)"', build_zon)
|
|
138
|
+
if match:
|
|
139
|
+
return match.group(1)
|
|
140
|
+
return _dep_content(dependency_files, ".zig-version").strip()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _zig_fallback_version(dependency_files: dict[str, str]) -> str:
|
|
144
|
+
min_version = _zig_minimum_version(dependency_files)
|
|
145
|
+
if min_version and _version_tuple(min_version) < (0, 15, 0):
|
|
146
|
+
return min_version
|
|
147
|
+
return "0.14.0"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _version_tuple(version: str) -> tuple[int, ...]:
|
|
151
|
+
numbers = [int(part) for part in re.findall(r"\d+", version)]
|
|
152
|
+
return tuple(numbers[:3]) if numbers else ()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _zig_uses_legacy_build_api(dependency_files: dict[str, str]) -> bool:
|
|
156
|
+
build_zig = _dep_content(dependency_files, "build.zig")
|
|
157
|
+
min_version = _zig_minimum_version(dependency_files)
|
|
158
|
+
if _version_tuple(min_version) >= (0, 15, 0):
|
|
159
|
+
return False
|
|
160
|
+
legacy_markers = [
|
|
161
|
+
".root_source_file",
|
|
162
|
+
".source_file",
|
|
163
|
+
"Build.ExecutableOptions",
|
|
164
|
+
]
|
|
165
|
+
return any(marker in build_zig for marker in legacy_markers)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _haskell_system_packages(dependency_files: dict[str, str], env: dict) -> list[str]:
|
|
169
|
+
combined = "\n".join(dependency_files.values()).lower()
|
|
170
|
+
packages: list[str] = ["pkg-config"]
|
|
171
|
+
|
|
172
|
+
if any(token in combined for token in ["pcre-light", "pcre-heavy", "libpcre", "pcre "]):
|
|
173
|
+
packages.append("pcre")
|
|
174
|
+
if any(token in combined for token in ["openssl", "libssl", "http-client-tls", "tls", "hsopenssl"]):
|
|
175
|
+
packages.append("openssl")
|
|
176
|
+
if any(token in combined for token in ["gtk", "pango", "gtk+-", "yi-frontend-pango"]):
|
|
177
|
+
packages.append("gtk+3")
|
|
178
|
+
|
|
179
|
+
deduped = list(dict.fromkeys(packages))
|
|
180
|
+
if _os_type(env) == "macos":
|
|
181
|
+
return deduped
|
|
182
|
+
if _os_type(env) == "linux":
|
|
183
|
+
mapping = {
|
|
184
|
+
"pkg-config": "pkg-config",
|
|
185
|
+
"pcre": "libpcre3-dev",
|
|
186
|
+
"openssl": "libssl-dev",
|
|
187
|
+
"gtk+3": "libgtk-3-dev",
|
|
188
|
+
}
|
|
189
|
+
return [mapping[pkg] for pkg in deduped if pkg in mapping]
|
|
190
|
+
return []
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _haskell_macos_env_prefix(dependency_files: dict[str, str], env: dict) -> str:
|
|
194
|
+
if _os_type(env) != "macos":
|
|
195
|
+
return ""
|
|
196
|
+
|
|
197
|
+
formulas = [pkg for pkg in _haskell_system_packages(dependency_files, env) if pkg in {"pcre", "openssl", "gtk+3"}]
|
|
198
|
+
lines = [
|
|
199
|
+
'BREW_PREFIX="$(brew --prefix)"',
|
|
200
|
+
'export PKG_CONFIG_PATH="$BREW_PREFIX/lib/pkgconfig:$BREW_PREFIX/share/pkgconfig:${PKG_CONFIG_PATH:-}"',
|
|
201
|
+
'export CPATH="$BREW_PREFIX/include:${CPATH:-}"',
|
|
202
|
+
'export LIBRARY_PATH="$BREW_PREFIX/lib:${LIBRARY_PATH:-}"',
|
|
203
|
+
'export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1',
|
|
204
|
+
]
|
|
205
|
+
for formula in formulas:
|
|
206
|
+
lines.append(
|
|
207
|
+
f'if brew --prefix {formula} >/dev/null 2>&1; then FORMULA_PREFIX="$(brew --prefix {formula})"; '
|
|
208
|
+
'export PKG_CONFIG_PATH="$FORMULA_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH"; '
|
|
209
|
+
'export CPATH="$FORMULA_PREFIX/include:$CPATH"; '
|
|
210
|
+
'export LIBRARY_PATH="$FORMULA_PREFIX/lib:$LIBRARY_PATH"; fi'
|
|
211
|
+
)
|
|
212
|
+
return "; ".join(lines) + ";"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _haskell_repo_template(owner: str, repo: str, ghcup_cmd: str, env_prefix: str) -> tuple[str, str, list[str]] | None:
|
|
216
|
+
key = f"{owner}/{repo}".lower()
|
|
217
|
+
|
|
218
|
+
if key == "yi-editor/yi":
|
|
219
|
+
build_cmd = (
|
|
220
|
+
"perl -0pi -e 's/extra-deps:\\n - Hclip-3\\.0\\.0\\.4\\n/"
|
|
221
|
+
"extra-deps:\\n - Hclip-3.0.0.4\\n"
|
|
222
|
+
" - vty-6.5\\@sha256:43a4137de7e55cf438a8334cc525fb0e0b4efe78d2ed8bd31b0716eb34993059,3425\\n"
|
|
223
|
+
" - vty-crossplatform-0.5.0.0\\@sha256:6d057fd8a5582eac3be28c91e99ed3730b729078e107ad19107af46bbb2ea65d,3146\\n"
|
|
224
|
+
" - vty-unix-0.2.0.0\\@sha256:2af3d0bdae3c4b7b7e567ee374efe32c7439fabdf9096465ce011a6c6736e9ae,2932\\n/' stack.yaml"
|
|
225
|
+
f" && {env_prefix}{ghcup_cmd} run --stack latest -- stack build yi --flag yi:-pango"
|
|
226
|
+
)
|
|
227
|
+
launch_cmd = f"{env_prefix}{ghcup_cmd} run --stack latest -- stack exec yi"
|
|
228
|
+
notes = [
|
|
229
|
+
"检测到 yi 的 Apple Silicon/新 resolver 兼容问题:模板会补齐 vty、vty-crossplatform、vty-unix 的 extra-deps。",
|
|
230
|
+
"模板默认关闭 yi 的 pango 前端,优先构建 VTY CLI 版本,避免 GTK/text 版本窗口冲突。",
|
|
231
|
+
]
|
|
232
|
+
return build_cmd, launch_cmd, notes
|
|
233
|
+
|
|
234
|
+
if key == "chrispenner/rasa":
|
|
235
|
+
build_cmd = (
|
|
236
|
+
"rm -rf .gitinstall-patches/eve-0.1.9.0"
|
|
237
|
+
f" && {env_prefix}{ghcup_cmd} run --ghc recommended --cabal recommended -- cabal get eve-0.1.9.0 --destdir .gitinstall-patches"
|
|
238
|
+
" && perl -0pi -e 's/import Control\\.Monad\\.State/import Control.Monad (join, void)\\nimport Control.Monad.State/'"
|
|
239
|
+
" .gitinstall-patches/eve-0.1.9.0/src/Eve/Internal/Actions.hs .gitinstall-patches/eve-0.1.9.0/src/Eve/Internal/Listeners.hs"
|
|
240
|
+
" && perl -0pi -e 's/\\} deriving \\(Eq\\)/} deriving (Eq, Functor)/g' rasa/src/Rasa/Internal/Range.hs"
|
|
241
|
+
" && perl -0pi -e 's/import Control\\.Monad\\.State/import Control.Monad (void)\\nimport Control.Monad.State/' rasa-ext-cursors/src/Rasa/Ext/Cursors/Internal/Base.hs"
|
|
242
|
+
" && printf '%s\\n' 'packages:' ' ./rasa' ' ./rasa-ext-cmd' ' ./rasa-ext-cursors' ' ./rasa-ext-files'"
|
|
243
|
+
" ' ./rasa-ext-logger' ' ./rasa-ext-views' ' ./rasa-ext-vim' ' ./text-lens' ' ./.gitinstall-patches/eve-0.1.9.0' 'allow-newer: true'"
|
|
244
|
+
" > cabal.project.gitinstall-core"
|
|
245
|
+
f" && {env_prefix}{ghcup_cmd} run --ghc recommended --cabal recommended -- cabal build all --project-file=cabal.project.gitinstall-core"
|
|
246
|
+
)
|
|
247
|
+
launch_cmd = f"{env_prefix}{ghcup_cmd} run --ghc recommended --cabal recommended -- cabal repl rasa --project-file=cabal.project.gitinstall-core"
|
|
248
|
+
notes = [
|
|
249
|
+
"检测到 rasa 的旧生态约束:模板会自动下载并补丁 eve-0.1.9.0,再对 rasa 核心源码打现代 GHC 兼容补丁。",
|
|
250
|
+
"模板默认构建 rasa 的核心包集合,并剥离已知与现代 vty API 不兼容的 slate/example 可执行目标。",
|
|
251
|
+
]
|
|
252
|
+
return build_cmd, launch_cmd, notes
|
|
253
|
+
|
|
254
|
+
if key == "lettier/gifcurry":
|
|
255
|
+
build_cmd = f"{env_prefix}{ghcup_cmd} run --ghc recommended --cabal recommended -- cabal build all"
|
|
256
|
+
launch_cmd = f"{env_prefix}{ghcup_cmd} run --ghc recommended --cabal recommended -- cabal run gifcurry_cli"
|
|
257
|
+
notes = [
|
|
258
|
+
"检测到 gifcurry 在 Apple Silicon 上优先应走 ghcup run + cabal 路径,避免旧 stack resolver 直接落到不受支持的 GHC 二进制。",
|
|
259
|
+
"若后续仍被旧 GUI 目标和精确版本窗拖垮,交由通用 Haskell fixer 自动切换到 headless/CLI 构建。",
|
|
260
|
+
]
|
|
261
|
+
return build_cmd, launch_cmd, notes
|
|
262
|
+
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _torch_install_cmd(env: dict) -> str:
|
|
267
|
+
"""返回适配当前 GPU 的 PyTorch 安装命令(这是 AI 项目最容易装错的一步)"""
|
|
268
|
+
pip = _pip_cmd(env)
|
|
269
|
+
gpu = _gpu_type(env)
|
|
270
|
+
|
|
271
|
+
if gpu == "mps":
|
|
272
|
+
# Apple Silicon:PyTorch 原生支持 MPS,直接 pip install
|
|
273
|
+
return f"{pip} install torch torchvision torchaudio"
|
|
274
|
+
|
|
275
|
+
if gpu == "cuda":
|
|
276
|
+
major = _cuda_major(env)
|
|
277
|
+
if major >= 12:
|
|
278
|
+
idx = "https://download.pytorch.org/whl/cu121"
|
|
279
|
+
elif major >= 11:
|
|
280
|
+
idx = "https://download.pytorch.org/whl/cu118"
|
|
281
|
+
else:
|
|
282
|
+
# CUDA 10 及更早版本不受 PyTorch 2.x 支持,降级到 CPU
|
|
283
|
+
idx = "https://download.pytorch.org/whl/cpu"
|
|
284
|
+
return f"{pip} install torch torchvision torchaudio --index-url {idx}"
|
|
285
|
+
|
|
286
|
+
if gpu == "rocm":
|
|
287
|
+
return f"{pip} install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.6"
|
|
288
|
+
|
|
289
|
+
# CPU-only(包含 Intel Mac)
|
|
290
|
+
return f"{pip} install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu"
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _node_pm(env: dict) -> tuple[str, str]:
|
|
294
|
+
"""返回 (install 命令, dev 启动命令),优先 pnpm > yarn > npm"""
|
|
295
|
+
if _has_pm(env, "pnpm"):
|
|
296
|
+
return "pnpm install", "pnpm dev"
|
|
297
|
+
if _has_pm(env, "yarn"):
|
|
298
|
+
return "yarn", "yarn dev"
|
|
299
|
+
return "npm install", "npm run dev"
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# ─────────────────────────────────────────────
|
|
304
|
+
# 模块级工具函数
|
|
305
|
+
# ─────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
def _make_step(cmd: str, desc: str, warn: bool = False) -> dict:
|
|
308
|
+
return {
|
|
309
|
+
"command": cmd,
|
|
310
|
+
"description": desc,
|
|
311
|
+
"_warning": "⚠️ 执行前请确认命令来源可信" if warn else "",
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _get_gpu_name(env: dict) -> str:
|
|
316
|
+
gpu = _gpu_type(env)
|
|
317
|
+
if gpu == "mps":
|
|
318
|
+
return f"Apple {env.get('os', {}).get('chip', 'Silicon')} MPS"
|
|
319
|
+
if gpu == "cuda":
|
|
320
|
+
return f"NVIDIA CUDA {env.get('gpu', {}).get('cuda_version', '')}"
|
|
321
|
+
if gpu == "rocm":
|
|
322
|
+
return "AMD ROCm"
|
|
323
|
+
return "CPU(无 GPU 加速)"
|