Files
openclaw/agents/stock/workspace/scripts/orders/sidecar.py
T
hyowons fed3526b20 Initial commit: OpenClaw 워크스페이스 버전관리 시작
설정·스크립트·스킬·문서·큐레이션 메모리 추적.
시크릿(credentials/identity)·런타임 상태(state/logs/sessions/sqlite)·
백업(clobbered/bak)·dream 캐시는 .gitignore로 제외.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:39:41 +09:00

107 lines
2.8 KiB
Python

"""
Kill switch.
state/orders_disabled 파일 존재 시 모든 진입점에서 거부.
LLM 에이전트(클로·레이) 세션에서 호출 시 즉시 차단 (OPENCLAW_AGENT 환경변수).
모든 매매 진입점 함수 첫 줄에서 guard_or_raise() 호출이 강제이다.
"""
from __future__ import annotations
import json
import os
import sys
from datetime import datetime, timezone, timedelta
from pathlib import Path
WORKSPACE_ROOT = Path(__file__).resolve().parent.parent.parent
LIMITS_FILE = Path(__file__).resolve().parent / 'limits.json'
KST = timezone(timedelta(hours=9))
class SidecarBlocked(RuntimeError):
pass
def _limits() -> dict:
return json.loads(LIMITS_FILE.read_text(encoding='utf-8'))
def _kill_switch_path() -> Path:
return WORKSPACE_ROOT / _limits()['kill_switch_file']
def _agent_env_blocked() -> bool:
var = _limits().get('telegram', {}).get('block_when_agent_env_set')
return bool(var and os.getenv(var))
def is_disabled() -> bool:
return _kill_switch_path().exists()
def guard_or_raise() -> None:
if _agent_env_blocked():
raise SidecarBlocked('LLM agent session cannot invoke order module (OPENCLAW_AGENT env set)')
if is_disabled():
raise SidecarBlocked('Order module is disabled (sidecar ON)')
def disable(reason: str = 'manual') -> Path:
p = _kill_switch_path()
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(
json.dumps({'disabled_at': datetime.now(KST).isoformat(), 'reason': reason}, ensure_ascii=False, indent=2),
encoding='utf-8',
)
try:
os.chmod(p, 0o600)
except OSError:
pass
return p
def enable() -> bool:
p = _kill_switch_path()
if not p.exists():
return False
p.unlink()
return True
def status() -> dict:
p = _kill_switch_path()
if not p.exists():
return {'disabled': False, 'path': str(p), 'agent_env_blocked': _agent_env_blocked()}
try:
meta = json.loads(p.read_text(encoding='utf-8'))
except (OSError, ValueError):
meta = {}
meta.update({'disabled': True, 'path': str(p), 'agent_env_blocked': _agent_env_blocked()})
return meta
def _cli(argv: list[str]) -> int:
if not argv or argv[0] == 'status':
print(json.dumps(status(), ensure_ascii=False, indent=2))
return 0
cmd = argv[0]
if cmd == 'disable':
reason = ' '.join(argv[1:]) or 'manual'
p = disable(reason)
print(f'sidecar disabled: {p}')
return 0
if cmd == 'enable':
ok = enable()
print('sidecar enabled' if ok else 'sidecar already enabled (no file)')
return 0
print(f'unknown command: {cmd}', file=sys.stderr)
print('usage: sidecar.py {status | disable [reason] | enable}', file=sys.stderr)
return 2
if __name__ == '__main__':
sys.exit(_cli(sys.argv[1:]))