emo-online-api / emo /desktop /actions /file_controller.py
Emo Online
Deploy Emo API
dd87944
Raw
History Blame Contribute Delete
4.87 kB
"""Opérations fichiers complètes via pathlib."""
from __future__ import annotations
import os
import shutil
from pathlib import Path
from typing import Any
from emo.desktop.actions._base import SkillResult
SKIP_DIRS = {".git", "node_modules", "__pycache__"}
def list_dir(path: str | Path, depth: int = 1) -> dict:
p = Path(path).expanduser()
if not p.exists():
return {"ok": False, "error": "chemin introuvable"}
if not p.is_dir():
return {"ok": False, "error": "pas un dossier"}
if depth <= 1:
files, dirs = [], []
for entry in sorted(p.iterdir()):
if entry.name.startswith("."):
continue
(dirs if entry.is_dir() else files).append(entry.name)
return {"ok": True, "path": str(p.resolve()), "files": files, "dirs": dirs}
entries = []
root = p.resolve()
def walk(d: Path, lvl: int):
if lvl > depth or len(entries) >= 300:
return
try:
items = sorted(d.iterdir())
except OSError:
return
for entry in items:
if entry.name.startswith("."):
continue
rel = entry.relative_to(root).as_posix()
entries.append({"path": rel, "is_dir": entry.is_dir()})
if entry.is_dir() and lvl < depth and entry.name not in SKIP_DIRS:
walk(entry, lvl + 1)
walk(p, 1)
return {"ok": True, "path": str(root), "entries": entries}
def read_file(path: str | Path, offset: int = 1, limit: int = 0) -> dict:
p = Path(path).expanduser()
if not p.is_file():
return {"ok": False, "error": "fichier introuvable"}
content = p.read_text(encoding="utf-8", errors="replace")
if offset > 1 or limit > 0:
lines = content.splitlines()
start = max(0, offset - 1)
end = len(lines) if limit <= 0 else min(len(lines), start + limit)
content = "\n".join(lines[start:end])
return {"ok": True, "path": str(p.resolve()), "content": content}
def write_file(path: str | Path, content: str) -> dict:
p = Path(path).expanduser()
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(content, encoding="utf-8")
return {"ok": True, "path": str(p.resolve()), "bytes": len(content.encode())}
def delete_path(path: str | Path) -> dict:
p = Path(path).expanduser()
if p.is_dir():
shutil.rmtree(p)
else:
p.unlink(missing_ok=False)
return {"ok": True, "deleted": str(p)}
def move_path(src: str | Path, dst: str | Path) -> dict:
s, d = Path(src).expanduser(), Path(dst).expanduser()
d.parent.mkdir(parents=True, exist_ok=True)
s.rename(d)
return {"ok": True, "from": str(s), "to": str(d.resolve())}
def find_files(root: str | Path, pattern: str, max_results: int = 100) -> dict:
root_p = Path(root).expanduser()
found = []
for dirpath, dirnames, filenames in os.walk(root_p):
dirnames[:] = [d for d in dirnames if not d.startswith(".") and d not in SKIP_DIRS]
for fname in filenames:
if len(found) >= max_results:
break
if "*" in pattern:
if Path(fname).match(pattern):
found.append(str(Path(dirpath) / fname))
elif pattern.lower() in fname.lower():
found.append(str(Path(dirpath) / fname))
return {"ok": True, "files": found, "count": len(found)}
def run(args: dict) -> Any:
action = (args.get("action") or "list").lower()
path = args.get("path") or args.get("query") or "."
# Détection depuis texte naturel
q = (args.get("query") or "").lower()
if "liste" in q or "list" in q or "dossier" in q:
action = "list"
if not Path(str(path)).expanduser().exists():
path = "."
if action == "list":
r = list_dir(path)
elif action == "read":
r = read_file(path, int(args.get("offset") or 1), int(args.get("limit") or 0))
elif action == "write":
content = args.get("content")
if content is None:
return SkillResult.fail("content requis pour write")
r = write_file(path, content)
elif action == "delete":
r = delete_path(path)
elif action == "move":
r = move_path(args.get("from") or path, args.get("to") or "")
elif action == "find":
r = find_files(path, args.get("pattern") or "*")
else:
r = list_dir(path)
if not r.get("ok"):
return SkillResult.fail(r.get("error", "erreur"))
if "entries" in r:
msg = f"{len(r['entries'])} entrées dans {r['path']}"
elif "files" in r and "dirs" in r:
msg = f"{len(r['files'])} fichiers, {len(r['dirs'])} dossiers"
elif "content" in r:
msg = f"Lu {len(r['content'])} caractères"
else:
msg = "OK"
return SkillResult.ok(**r, message=msg)