provozni zaloha

This commit is contained in:
lachtan
2026-06-24 08:11:12 +02:00
parent 9295dba19f
commit 1db3ec4756
97 changed files with 7698 additions and 817 deletions

View File

@@ -15,17 +15,13 @@ from tasks_common import (
TASKS,
build_task_content,
build_task_filename,
ensure_queue_dirs,
load_preset_names,
log,
resolve_preset,
)
def ensure_queue_dirs() -> None:
for name in ("new", "inbox", "running", "done", "failed"):
(TASKS / name).mkdir(parents=True, exist_ok=True)
def main() -> int:
parser = argparse.ArgumentParser(description="Create a detach task and drop it in inbox/")
parser.add_argument("--goal", required=True, help="Self-contained goal restatement")

View File

@@ -22,11 +22,7 @@ def completed_files() -> list[Path]:
def find_matches(identifier: str) -> list[Path]:
if not identifier:
done = sorted((TASKS / "done").glob("*.md"), key=lambda f: f.name, reverse=True) if (TASKS / "done").exists() else []
if done:
return [done[0]]
failed = sorted((TASKS / "failed").glob("*.md"), key=lambda f: f.name, reverse=True) if (TASKS / "failed").exists() else []
return [failed[0]] if failed else []
return completed_files()[:1]
return [f for f in completed_files() if identifier.lower() in f.name.lower()]

View File

@@ -34,12 +34,10 @@ from datetime import datetime
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from tasks_common import LOG, TASKS, log, parse_frontmatter
from tasks_common import CONFIG, LOG, TASKS, ensure_queue_dirs, log, parse_frontmatter
from nanobot import Nanobot
CONFIG = Path.home() / ".nanobot" / "config.json"
TIMEOUT_SECONDS = 20 * 60
@@ -74,6 +72,49 @@ async def run_agent(goal: str, session_key: str, preset: str | None = None) -> s
return result.content or ""
def finalize_task(
running_path: Path,
content: str,
fm: dict[str, str],
slug: str,
result_text: str,
status: str,
outcome: str,
started: datetime | None,
) -> None:
completed = datetime.now().astimezone()
duration_s = int((completed - started).total_seconds()) if started else 0
appended = (
f"{content}\n\n# Result\n\n{result_text}\n\n"
f"---\ncompleted: {completed.isoformat()}\n"
f"duration_seconds: {duration_s}\nstatus: {status}\n"
)
running_path.write_text(appended)
shutil.move(running_path, TASKS / status / running_path.name)
try:
notify_chat_id, notify_source = resolve_telegram_chat_id(fm)
except Exception as e:
log(f"NOTIFY-RESOLVE-FAILED {running_path.name}: {e}")
notify_chat_id = None
lines = result_text.strip().splitlines()
summary_line = lines[0][:200] if lines else "(prázdný výstup)"
msg = (
f"{outcome}: {slug}\n\n"
f"{summary_line}\n\n"
f"V chatu si vyžádej plný report: výsledek {slug}"
)
if notify_chat_id:
try:
telegram_send(notify_chat_id, msg)
log(f"NOTIFY {running_path.name} chat={notify_chat_id} source={notify_source}")
except Exception as e:
log(f"NOTIFY-FAILED {running_path.name}: {e}")
log(f"END {running_path.name} status={status} duration={duration_s}s")
def process_task(path: Path) -> None:
try:
content = path.read_text()
@@ -88,7 +129,6 @@ def process_task(path: Path) -> None:
shutil.move(path, TASKS / "failed" / path.name)
return
notify_chat_id, notify_source = resolve_telegram_chat_id(fm)
slug = fm.get("slug", path.stem)
preset = fm.get("model")
running = TASKS / "running" / path.name
@@ -116,40 +156,34 @@ def process_task(path: Path) -> None:
outcome = "❌ Selhalo"
log(f"EXCEPTION {path.name}: {e}")
completed = datetime.now().astimezone()
duration_s = int((completed - started).total_seconds())
appended = (
f"{content}\n\n# Result\n\n{result_text}\n\n"
f"---\ncompleted: {completed.isoformat()}\n"
f"duration_seconds: {duration_s}\nstatus: {status}\n"
)
running.write_text(appended)
finalize_task(running, content, fm, slug, result_text, status, outcome, started)
target_dir = TASKS / status
shutil.move(running, target_dir / path.name)
# Telegram notifikace — vždy přes Telegram, chat_id buď z frontmatteru
# (Telegram session) nebo z fallback configu (WebUI / CLI / atd.).
lines = result_text.strip().splitlines()
summary_line = lines[0][:200] if lines else "(prázdný výstup)"
msg = (
f"{outcome}: `{slug}`\n\n"
f"{summary_line}\n\n"
f"V chatu si vyžádej plný report: `výsledek {slug}`"
)
try:
telegram_send(notify_chat_id, msg)
log(f"NOTIFY {path.name} chat={notify_chat_id} source={notify_source}")
except Exception as e:
log(f"NOTIFY-FAILED {path.name}: {e}")
log(f"END {path.name} status={status} duration={duration_s}s")
def reclaim_orphans() -> None:
running = TASKS / "running"
if not running.exists():
return
for path in sorted(running.glob("*.md")):
try:
content = path.read_text()
except Exception as e:
log(f"RECLAIM-READ-FAILED {path.name}: {e}")
shutil.move(path, TASKS / "failed" / path.name)
continue
fm, _ = parse_frontmatter(content)
slug = fm.get("slug", path.stem)
finalize_task(
path, content, fm, slug,
"(INTERRUPTED: daemon restarted while task was running)",
"failed", "⚠️ Přerušeno", None,
)
log(f"RECLAIM {path.name}")
def main() -> int:
for d in ("new", "inbox", "running", "done", "failed"):
(TASKS / d).mkdir(parents=True, exist_ok=True)
ensure_queue_dirs()
LOG.parent.mkdir(parents=True, exist_ok=True)
reclaim_orphans()
inbox = TASKS / "inbox"
tasks = sorted(inbox.glob("*.md"))

View File

@@ -10,6 +10,8 @@ TASKS = WORKSPACE / "tasks"
CONFIG = Path.home() / ".nanobot" / "config.json"
LOG = WORKSPACE / "log" / "detach.log"
QUEUE_DIRS = ("new", "inbox", "running", "done", "failed")
FILENAME_RE = re.compile(
r"^(\d{4}-\d{2}-\d{2}(?:T\d{6}|_\d{2}_\d{2}_\d{2}_\d{6}))-(.+)\.md$"
)
@@ -20,6 +22,11 @@ _NO_INTERACTION_BULLET = (
)
def ensure_queue_dirs() -> None:
for name in QUEUE_DIRS:
(TASKS / name).mkdir(parents=True, exist_ok=True)
def log(msg: str) -> None:
LOG.parent.mkdir(parents=True, exist_ok=True)
with LOG.open("a") as f:

View File

@@ -3,6 +3,7 @@ Description=Trigger detach daemon when tasks/inbox has files
[Path]
DirectoryNotEmpty=%h/.nanobot/workspace/tasks/inbox
DirectoryNotEmpty=%h/.nanobot/workspace/tasks/running
Unit=tasks-daemon.service
[Install]