Repair Assistent SQLite natives after extension build completes.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+106
-27
@@ -229,15 +229,27 @@ print(__import__("json").dumps(found))
|
|||||||
_ASSISTENT_SQLITE_BIN_PY = r"""
|
_ASSISTENT_SQLITE_BIN_PY = r"""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import json
|
import json
|
||||||
|
skip = {"obj", "node_modules"}
|
||||||
|
|
||||||
|
def assistent_dlls(root: Path):
|
||||||
|
out = []
|
||||||
|
if not root.is_dir():
|
||||||
|
return out
|
||||||
|
for dll in root.rglob("*.dll"):
|
||||||
|
if any(p in skip for p in dll.parts):
|
||||||
|
continue
|
||||||
|
name = dll.name.lower()
|
||||||
|
if name == "swarmassistentextension.dll" or (
|
||||||
|
name.startswith("swarmextensionswarm-assistent") and name.endswith(".dll")
|
||||||
|
):
|
||||||
|
out.append(dll)
|
||||||
|
return out
|
||||||
|
|
||||||
roots = [Path("/mnt/swarm_data"), Path("/opt/swarmui")]
|
roots = [Path("/mnt/swarm_data"), Path("/opt/swarmui")]
|
||||||
seen = set()
|
seen = set()
|
||||||
found = []
|
found = []
|
||||||
for root in roots:
|
for root in roots:
|
||||||
if not root.is_dir():
|
for dll in assistent_dlls(root):
|
||||||
continue
|
|
||||||
for dll in root.rglob("SwarmAssistentExtension.dll"):
|
|
||||||
if any(p in {"obj", "node_modules"} for p in dll.parts):
|
|
||||||
continue
|
|
||||||
key = str(dll)
|
key = str(dll)
|
||||||
if key in seen:
|
if key in seen:
|
||||||
continue
|
continue
|
||||||
@@ -247,6 +259,7 @@ for root in roots:
|
|||||||
pcl = list(dll_dir.glob("SQLitePCLRaw*.dll"))
|
pcl = list(dll_dir.glob("SQLitePCLRaw*.dll"))
|
||||||
bundle = dll_dir / "SQLitePCLRaw.bundle_e_sqlite3.dll"
|
bundle = dll_dir / "SQLitePCLRaw.bundle_e_sqlite3.dll"
|
||||||
native = list(dll_dir.glob("runtimes/**/e_sqlite3.*")) + list(dll_dir.glob("runtimes/**/libe_sqlite3.*"))
|
native = list(dll_dir.glob("runtimes/**/e_sqlite3.*")) + list(dll_dir.glob("runtimes/**/libe_sqlite3.*"))
|
||||||
|
native_flat = list(dll_dir.glob("e_sqlite3.*")) + list(dll_dir.glob("libe_sqlite3.*"))
|
||||||
found.append({
|
found.append({
|
||||||
"path": str(dll.parent.parent) if dll.parent.name.startswith("net") else str(dll_dir),
|
"path": str(dll.parent.parent) if dll.parent.name.startswith("net") else str(dll_dir),
|
||||||
"name": "swarm-assistent",
|
"name": "swarm-assistent",
|
||||||
@@ -256,51 +269,84 @@ for root in roots:
|
|||||||
"sqlitepcl": bool(pcl),
|
"sqlitepcl": bool(pcl),
|
||||||
"sqlite_bundle": bundle.is_file(),
|
"sqlite_bundle": bundle.is_file(),
|
||||||
"sqlite_native": bool(native),
|
"sqlite_native": bool(native),
|
||||||
|
"sqlite_native_flat": bool(native_flat),
|
||||||
})
|
})
|
||||||
print(json.dumps(found))
|
print(json.dumps(found))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_ASSISTENT_SQLITE_REPAIR_PY = r"""
|
_ASSISTENT_SQLITE_REPAIR_PY = r"""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import json, shutil
|
import json, os, shutil
|
||||||
skip = {"obj", "node_modules"}
|
skip = {"obj", "node_modules"}
|
||||||
dlls = []
|
|
||||||
for root in (Path("/mnt/swarm_data"), Path("/opt/swarmui")):
|
def assistent_dlls(root: Path):
|
||||||
|
out = []
|
||||||
if not root.is_dir():
|
if not root.is_dir():
|
||||||
continue
|
return out
|
||||||
for dll in root.rglob("SwarmAssistentExtension.dll"):
|
for dll in root.rglob("*.dll"):
|
||||||
if any(p in skip for p in dll.parts):
|
if any(p in skip for p in dll.parts):
|
||||||
continue
|
continue
|
||||||
dlls.append(dll)
|
name = dll.name.lower()
|
||||||
|
if name == "swarmassistentextension.dll" or (
|
||||||
|
name.startswith("swarmextensionswarm-assistent") and name.endswith(".dll")
|
||||||
|
):
|
||||||
|
out.append(dll)
|
||||||
|
return out
|
||||||
|
|
||||||
|
def find_native(d: Path):
|
||||||
|
for rel in (
|
||||||
|
Path("runtimes/linux-x64/native/libe_sqlite3.so"),
|
||||||
|
Path("runtimes/linux-x64/native/e_sqlite3.so"),
|
||||||
|
):
|
||||||
|
p = d / rel
|
||||||
|
if p.is_file():
|
||||||
|
return p
|
||||||
|
for p in d.glob("runtimes/**/libe_sqlite3.so"):
|
||||||
|
return p
|
||||||
|
for p in d.glob("runtimes/**/e_sqlite3.so"):
|
||||||
|
return p
|
||||||
|
return None
|
||||||
|
|
||||||
|
def link_native(dest_dir: Path, src: Path):
|
||||||
|
for name in ("libe_sqlite3.so", "e_sqlite3.so"):
|
||||||
|
out = dest_dir / name
|
||||||
|
if out.exists() or out.is_symlink():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
os.symlink(src, out)
|
||||||
|
except OSError:
|
||||||
|
shutil.copy2(src, out)
|
||||||
|
|
||||||
|
dlls = []
|
||||||
|
for root in (Path("/mnt/swarm_data"), Path("/opt/swarmui")):
|
||||||
|
dlls.extend(assistent_dlls(root))
|
||||||
|
|
||||||
def has_sqlite(d):
|
def has_sqlite(d):
|
||||||
if not (d / "Microsoft.Data.Sqlite.dll").is_file():
|
if not (d / "Microsoft.Data.Sqlite.dll").is_file():
|
||||||
return False
|
return False
|
||||||
if not list(d.glob("SQLitePCLRaw*.dll")):
|
if not list(d.glob("SQLitePCLRaw*.dll")):
|
||||||
return False
|
return False
|
||||||
return (d / "SQLitePCLRaw.bundle_e_sqlite3.dll").is_file() or list(
|
if (d / "libe_sqlite3.so").is_file() or (d / "e_sqlite3.so").is_file():
|
||||||
d.glob("runtimes/**/e_sqlite3.*")
|
return True
|
||||||
) or list(d.glob("runtimes/**/libe_sqlite3.*"))
|
return bool(list(d.glob("runtimes/**/libe_sqlite3.*")) or list(d.glob("runtimes/**/e_sqlite3.*")))
|
||||||
|
|
||||||
donors = [d.parent for d in dlls if has_sqlite(d.parent)]
|
donors = [d.parent for d in dlls if has_sqlite(d.parent)]
|
||||||
if not donors:
|
if not donors:
|
||||||
nuget = Path.home() / ".nuget" / "packages"
|
nuget = Path.home() / ".nuget" / "packages"
|
||||||
extra = []
|
|
||||||
if nuget.is_dir():
|
if nuget.is_dir():
|
||||||
extra.extend(nuget.rglob("Microsoft.Data.Sqlite.dll"))
|
for f in nuget.rglob("Microsoft.Data.Sqlite.dll"):
|
||||||
extra.extend(nuget.rglob("SQLitePCLRaw.core.dll"))
|
if f.parent not in donors:
|
||||||
# last resort: any copy on the disk under swarm
|
donors.append(f.parent)
|
||||||
for root in (Path("/opt/swarmui"), Path("/root/.nuget")):
|
|
||||||
if root.is_dir():
|
|
||||||
extra.extend(root.rglob("Microsoft.Data.Sqlite.dll"))
|
|
||||||
for f in extra:
|
|
||||||
if f.parent not in donors:
|
|
||||||
donors.append(f.parent)
|
|
||||||
|
|
||||||
copied = []
|
copied = []
|
||||||
|
linked = []
|
||||||
missing = []
|
missing = []
|
||||||
for dll in dlls:
|
for dll in dlls:
|
||||||
dest = dll.parent
|
dest = dll.parent
|
||||||
|
native = find_native(dest)
|
||||||
|
if native is not None:
|
||||||
|
link_native(dest, native)
|
||||||
|
linked.append({"dll_dir": str(dest), "native": str(native)})
|
||||||
if has_sqlite(dest):
|
if has_sqlite(dest):
|
||||||
continue
|
continue
|
||||||
src = next((d for d in donors if d != dest and has_sqlite(d)), None)
|
src = next((d for d in donors if d != dest and has_sqlite(d)), None)
|
||||||
@@ -311,7 +357,6 @@ for dll in dlls:
|
|||||||
continue
|
continue
|
||||||
names = ["Microsoft.Data.Sqlite.dll"]
|
names = ["Microsoft.Data.Sqlite.dll"]
|
||||||
names += [p.name for p in src.glob("SQLitePCLRaw*.dll")]
|
names += [p.name for p in src.glob("SQLitePCLRaw*.dll")]
|
||||||
names += [p.name for p in src.glob("e_sqlite3.*")]
|
|
||||||
for name in names:
|
for name in names:
|
||||||
fp = src / name
|
fp = src / name
|
||||||
if fp.is_file():
|
if fp.is_file():
|
||||||
@@ -326,7 +371,11 @@ for dll in dlls:
|
|||||||
out.parent.mkdir(parents=True, exist_ok=True)
|
out.parent.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.copy2(fp, out)
|
shutil.copy2(fp, out)
|
||||||
copied.append({"dll_dir": str(dest), "from": str(src)})
|
copied.append({"dll_dir": str(dest), "from": str(src)})
|
||||||
print(json.dumps({"copied": copied, "missing": missing, "dlls": [str(d) for d in dlls]}))
|
n2 = find_native(dest)
|
||||||
|
if n2 is not None:
|
||||||
|
link_native(dest, n2)
|
||||||
|
linked.append({"dll_dir": str(dest), "native": str(n2)})
|
||||||
|
print(json.dumps({"copied": copied, "linked": linked, "missing": missing, "dlls": [str(d) for d in dlls]}))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -394,10 +443,14 @@ def repair_assistent_sqlite_bins(cfg: Config, host: str, log: Log) -> bool:
|
|||||||
return False
|
return False
|
||||||
for row in copied:
|
for row in copied:
|
||||||
log(f"Assistent Sqlite: скопировал рядом с {row.get('dll_dir')}")
|
log(f"Assistent Sqlite: скопировал рядом с {row.get('dll_dir')}")
|
||||||
|
for row in data.get("linked") or []:
|
||||||
|
log(f"Assistent Sqlite: symlink native → {row.get('dll_dir')}")
|
||||||
for path in missing:
|
for path in missing:
|
||||||
log(f"⚠ Assistent Sqlite: нет donor DLL для {path}")
|
log(f"⚠ Assistent Sqlite: нет donor DLL для {path}")
|
||||||
if copied:
|
if copied:
|
||||||
return True
|
return True
|
||||||
|
if data.get("linked"):
|
||||||
|
return True
|
||||||
if not missing:
|
if not missing:
|
||||||
log("Assistent Sqlite: private deps уже рядом с DLL")
|
log("Assistent Sqlite: private deps уже рядом с DLL")
|
||||||
return True
|
return True
|
||||||
@@ -435,7 +488,7 @@ def verify_assistent_sqlite_bins(cfg: Config, host: str, log: Log) -> bool:
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
if row.get("sqlite_dll") and row.get("sqlitepcl") and (
|
if row.get("sqlite_dll") and row.get("sqlitepcl") and (
|
||||||
row.get("sqlite_bundle") or row.get("sqlite_native")
|
row.get("sqlite_bundle") or row.get("sqlite_native") or row.get("sqlite_native_flat")
|
||||||
):
|
):
|
||||||
log(f"Assistent {name}: Microsoft.Data.Sqlite + SQLitePCLRaw (+ native) рядом с DLL")
|
log(f"Assistent {name}: Microsoft.Data.Sqlite + SQLitePCLRaw (+ native) рядом с DLL")
|
||||||
ok_any = True
|
ok_any = True
|
||||||
@@ -1387,13 +1440,39 @@ def ensure_swarmui_running(cfg: Config, host: str, log: Log, restart: bool) -> N
|
|||||||
if restart and active == "active":
|
if restart and active == "active":
|
||||||
log("systemctl restart swarmui (новые extensions/autocomplete)")
|
log("systemctl restart swarmui (новые extensions/autocomplete)")
|
||||||
run_ssh(cfg, host, "sudo -n systemctl restart swarmui", timeout=120)
|
run_ssh(cfg, host, "sudo -n systemctl restart swarmui", timeout=120)
|
||||||
|
_wait_swarmui_extension_build(cfg, host, log)
|
||||||
return
|
return
|
||||||
if active != "active":
|
if active != "active":
|
||||||
log("systemctl start swarmui")
|
log("systemctl start swarmui")
|
||||||
run_ssh(cfg, host, "sudo -n systemctl start swarmui", timeout=120)
|
run_ssh(cfg, host, "sudo -n systemctl start swarmui", timeout=120)
|
||||||
|
_wait_swarmui_extension_build(cfg, host, log)
|
||||||
run_ssh(cfg, host, "sudo -n systemctl enable swarmui", check=False)
|
run_ssh(cfg, host, "sudo -n systemctl enable swarmui", check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_swarmui_extension_build(cfg: Config, host: str, log: Log, timeout_sec: int = 120) -> None:
|
||||||
|
"""Wait until SwarmUI compiles extensions (Assistent DLL appears in bin/extensions)."""
|
||||||
|
import time
|
||||||
|
|
||||||
|
deadline = time.monotonic() + timeout_sec
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
try:
|
||||||
|
raw = run_ssh(
|
||||||
|
cfg,
|
||||||
|
host,
|
||||||
|
"python3 - <<'PY'\n" + _ASSISTENT_SQLITE_BIN_PY + "\nPY",
|
||||||
|
check=False,
|
||||||
|
timeout=25,
|
||||||
|
).strip()
|
||||||
|
rows = json.loads(raw.splitlines()[-1]) if raw else []
|
||||||
|
if isinstance(rows, list) and rows and any(r.get("dll") for r in rows):
|
||||||
|
log("SwarmUI: extension DLL готов")
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(5)
|
||||||
|
log("⚠ SwarmUI: extension DLL не появился за 2 мин — repair Sqlite может не сработать")
|
||||||
|
|
||||||
|
|
||||||
_OLLAMA_TAGS_PY = r"""
|
_OLLAMA_TAGS_PY = r"""
|
||||||
import json, urllib.request
|
import json, urllib.request
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user