重构 lib 为扁平模块并支持 Windows schtasks;新增 JS/Bash/PowerShell 模板、WebUI 调度编辑,以及 Cursor Skill 与 MCP 工具供 Agent 管理定时任务。 Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
3.3 KiB
Python
Executable File
111 lines
3.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""shumengya cron 任务开关与运行管理。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent / "lib"))
|
||
|
||
from manager import (
|
||
cron_root,
|
||
get_context,
|
||
iter_task_ids,
|
||
migrate_legacy_disabled_layout,
|
||
run_task,
|
||
set_task_state,
|
||
sync_cron_d,
|
||
)
|
||
from runner import task_enabled_text, task_is_enabled
|
||
|
||
|
||
CRON_ROOT = cron_root()
|
||
|
||
|
||
def _resolve_task_ids(values: list[str]) -> list[str]:
|
||
if not values:
|
||
return iter_task_ids(CRON_ROOT)
|
||
if len(values) == 1 and values[0] == "all":
|
||
return iter_task_ids(CRON_ROOT)
|
||
return values
|
||
|
||
|
||
def _print_status(task_id: str) -> None:
|
||
ctx = get_context(task_id, CRON_ROOT)
|
||
print(f"{ctx.task_id}: {task_enabled_text(ctx)}")
|
||
|
||
|
||
def _set_state(task_id: str, enabled: bool) -> None:
|
||
_info, message = set_task_state(task_id, enabled, CRON_ROOT)
|
||
print(f"{task_id}: {'开启' if enabled else '关闭'}")
|
||
if message:
|
||
print(f" → {message}")
|
||
|
||
|
||
def main(argv: list[str] | None = None) -> int:
|
||
parser = argparse.ArgumentParser(
|
||
prog="cronctl.py",
|
||
description="管理 /shumengya/project/agent/sproutclaw-cron 下每个定时任务的开关状态,或手动执行任务。",
|
||
)
|
||
parser.add_argument(
|
||
"action",
|
||
nargs="?",
|
||
default="status",
|
||
choices=["status", "enable", "disable", "toggle", "on", "off", "run", "sync-cron"],
|
||
help="status / enable / disable / toggle / run / sync-cron",
|
||
)
|
||
parser.add_argument("tasks", nargs="*", help="任务名,status 时默认列出全部;也可传 all")
|
||
args = parser.parse_args(argv)
|
||
|
||
migrate_legacy_disabled_layout(CRON_ROOT)
|
||
|
||
action = args.action
|
||
if action == "on":
|
||
action = "enable"
|
||
elif action == "off":
|
||
action = "disable"
|
||
|
||
task_ids = _resolve_task_ids(list(args.tasks))
|
||
if not task_ids:
|
||
print("未找到可管理的任务。", file=sys.stderr)
|
||
return 1
|
||
|
||
if action in {"enable", "disable", "toggle", "run", "sync-cron"} and not args.tasks:
|
||
print("请至少指定一个任务名,或传 all。", file=sys.stderr)
|
||
return 1
|
||
|
||
rc = 0
|
||
for task_id in task_ids:
|
||
try:
|
||
if action == "run":
|
||
task_rc = run_task(task_id, CRON_ROOT)
|
||
marker = ">" if sys.platform == "win32" else "▶"
|
||
print(f"{marker} {task_id} (exit={task_rc})")
|
||
if task_rc != 0:
|
||
rc = task_rc
|
||
continue
|
||
|
||
if action == "status":
|
||
_print_status(task_id)
|
||
elif action == "sync-cron":
|
||
ctx = get_context(task_id, CRON_ROOT)
|
||
message = sync_cron_d(ctx, task_is_enabled(ctx))
|
||
if message:
|
||
print(f" → {message}")
|
||
elif action == "enable":
|
||
_set_state(task_id, True)
|
||
elif action == "disable":
|
||
_set_state(task_id, False)
|
||
else:
|
||
_set_state(task_id, not task_is_enabled(get_context(task_id, CRON_ROOT)))
|
||
except FileNotFoundError as e:
|
||
print(str(e), file=sys.stderr)
|
||
rc = 1
|
||
return rc
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|