Hermes shell/script job 타입 추가 보고서 (2026-04-14)
결론
- 완료: Hermes cron에 LLM을 호출하지 않는 deterministic
kind: "script"/kind: "shell"실행 경로를 추가했다. - OpenClaw 쪽 파일은 수정하지 않았다.
shared/llm.py도 수정하지 않았다. - 기존 prompt cron은
kind가 없거나prompt이면 기존 AIAgent 경로로 그대로 간다.
수정 파일
| 파일 | 주요 변경 | 라인 |
|---|---|---|
/Users/ron/.hermes/hermes-agent/cron/scheduler.py |
script/shell job 감지, cwd/env allowlist, command/script 정규화, subprocess.run() 실행, stdout/stderr/exit code/timeout 저장 |
15-16, 48-61, 450-654, 724-725 |
/Users/ron/.hermes/hermes-agent/cron/jobs.py |
create_job() 스키마에 kind, script, command, args, cwd, env, env_allowlist, timeout, shell, silent 추가 |
378-387, 436-507 |
/Users/ron/.hermes/hermes-agent/tools/cronjob_tools.py |
cronjob tool에서 shell/script job 생성·수정·표시 가능하도록 schema/handler 확장 |
116-154, 157-229, 290-337, 401-585 |
새 jobs.json 형식 예시
{
"id": "example-shell-job",
"name": "example-shell-job",
"kind": "shell",
"command": "cd /Users/ron/.openclaw/workspace && python3 scripts/pipeline/example.py",
"cwd": "/Users/ron/.openclaw/workspace",
"timeout": 300,
"env": {"PYTHONPATH": "/Users/ron/.openclaw/workspace/scripts/shared:/Users/ron/.openclaw/workspace/scripts/pipeline"},
"env_allowlist": ["PATH", "HOME"],
"silent": true,
"schedule": {"kind": "cron", "expr": "5 9 * * 1-5", "display": "5 9 * * 1-5"},
"enabled": true
}
실행 동작
kind: "shell": 문자열command를/bin/bash로 실행한다. 배열 command는 shell 없이 직접 실행한다.kind: "script":script경로 +args를 직접 실행한다. 실행권한 없는.py는 현재 Python으로,.sh/.bash는/bin/bash로 실행한다.- exit code
0→ success, non-zero → failure. - stdout/stderr는 Hermes cron output markdown에 캡처한다.
- timeout 초과 시 failure로 기록한다.
- cwd allowlist: 사용자 home,
/tmp, 현재HERMES_HOME하위만 허용. - env allowlist: 기본 최소 환경 +
env_allowlist에 명시한 부모 env +env명시값만 전달한다. - 성공 시 기본
silent=true라 채팅 발송은[SILENT]로 억제되고, output 파일은 저장된다.
검증 결과
1) 문법 검사
cd /Users/ron/.hermes/hermes-agent && source venv/bin/activate && python -m py_compile cron/scheduler.py cron/jobs.py tools/cronjob_tools.py
=> 통과
2) 작은 shell job 정의 → tick 실행
임시 HERMES_HOME=/tmp/hermes-shell-job-test-final-*에서 실제 jobs.json 생성 후 tick() 실행.
tick_executed= 1
out_text= shell-ok
jobs_remaining= 0
output_has_exit_0= True
output_has_stdout= True
3) script path 실행 확인
실행권한 없는 /tmp/hermes_script_smoke.py를 kind="script" + args + env로 실행.
script_executed= 1
script_out_text= script-ok:a,b
4) LLM 우회 확인
run_job()에 kind="shell" job을 직접 넣어 실행했고, run_agent 모듈이 로드되지 않았다.
direct_success= True
direct_error= None
direct_final_response= [SILENT]
run_agent_loaded= False
5) 기존 prompt cron 영향 확인
prompt_is_shell= False
script_is_shell= True
shell_is_shell= True
6) 기존 cron 관련 테스트
실제 운영 gateway가 기본 ~/.hermes/cron/.tick.lock을 잡을 수 있어 테스트는 격리된 HERMES_HOME으로 실행했다.
HERMES_HOME=/tmp/hermes-pytest-shelljob python -m pytest tests/cron/test_scheduler.py tests/tools/test_cronjob_tools.py tests/hermes_cli/test_cron.py -q -n 0
=> 81 passed in 0.68s
참고: HERMES_HOME을 격리하지 않고 실행하면 운영 중 gateway lock과 충돌해 기존 scheduler 테스트 일부가 실패한다. 이번 shell-job 패치 실패가 아니라 운영 인스턴스 lock 공유 문제다.
Diff 요약
cron/jobs.py | 50 +++++
cron/scheduler.py | shell/script 실행 분기 추가 포함
tools/cronjob_tools.py | 133 +++++++++++-
주의: cron/scheduler.py는 작업 시작 시점에 이미 미커밋 lock/status 관련 변경이 있어 전체 git diff 통계에는 기존 변경도 섞여 있다. 이번 작업에서 실제로 건드린 핵심은 위 표의 라인 범위다.
남은 리스크
- 현재 실행 중인 Hermes gateway 프로세스가 이미
scheduler.py를 import한 상태면 새 shell/script 실행 경로는 프로세스 재시작 전까지 반영되지 않을 수 있다. 이 보고서는 코드 패치와 새 Python 프로세스 기준 검증 완료 상태다. - cwd allowlist는 home,
/tmp,HERMES_HOME중심이다. 다른 시스템 경로에서 실행해야 하는 cron은cwd를 home 하위 wrapper로 두거나 allowlist 정책 확장이 필요하다.
자체평가
- 정확성: 4.8/5
- 완성도: 4.7/5
- 검증: 4.7/5
- 최소 변경: 4.4/5 —
cronjobtool schema까지 확장해서 직접 jobs.json 편집뿐 아니라 도구 생성도 가능하게 한 점은 범위 내 확장으로 판단. - 종합: 4.65/5