Initial commit: OpenClaw 워크스페이스 버전관리 시작
설정·스크립트·스킬·문서·큐레이션 메모리 추적. 시크릿(credentials/identity)·런타임 상태(state/logs/sessions/sqlite)· 백업(clobbered/bak)·dream 캐시는 .gitignore로 제외. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
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:]))
|
||||
Reference in New Issue
Block a user