#!/usr/bin/env python3 """Operator steering queue for the newOS4 maintenance/implementation jobs. The operator (or Hermes) appends directives here; both cron jobs read this file FIRST and prioritise whatever it says over their own findings. Consumed entries are marked, not deleted, so the history of "what was asked for and when" survives. newos4-steer.py add "fix the download links on mobile" newos4-steer.py list # pending only newos4-steer.py list --all newos4-steer.py done # mark consumed (the jobs do this themselves) newos4-steer.py clear # mark all pending as consumed """ import argparse import fcntl import json import os import time ROOT = '/var/www/webx11.duckdns.org/spaces/newos4' DIR = os.path.join(ROOT, 'data', 'maintenance') FILE = os.path.join(DIR, 'steering.json') MAX = 100 def _load(): os.makedirs(DIR, exist_ok=True) try: with open(FILE, encoding='utf-8') as f: data = json.load(f) return data if isinstance(data, list) else [] except Exception: return [] def _save(data): tmp = FILE + '.tmp' with open(tmp, 'w', encoding='utf-8') as f: json.dump(data[-MAX:], f, indent=2) f.flush() os.fsync(f.fileno()) os.replace(tmp, FILE) os.chmod(FILE, 0o664) def main(): ap = argparse.ArgumentParser() sub = ap.add_subparsers(dest='cmd', required=True) a = sub.add_parser('add'); a.add_argument('text'); a.add_argument('--priority', default='normal', choices=['high', 'normal', 'low']) sub.add_parser('list').add_argument('--all', action='store_true') d = sub.add_parser('done'); d.add_argument('id') sub.add_parser('clear') args = ap.parse_args() lock = open(FILE + '.lock', 'a+') fcntl.flock(lock, fcntl.LOCK_EX) try: data = _load() if args.cmd == 'add': entry = { 'id': 'STEER-' + time.strftime('%Y%m%d-%H%M%S') + '-' + os.urandom(2).hex(), 'created_at': time.strftime('%Y-%m-%dT%H:%M:%S+00:00', time.gmtime()), 'priority': args.priority, 'text': args.text.strip()[:1000], 'done': False, } data.append(entry) _save(data) print(entry['id']) elif args.cmd == 'list': rows = [r for r in data if args.all or not r.get('done')] if not rows: print('(no pending steering directives)') for r in rows: print(f"{r['id']} [{r.get('priority','normal')}] {r.get('text','')}") elif args.cmd == 'done': hit = [r for r in data if r.get('id') == args.id] if not hit: raise SystemExit(f"no such id: {args.id}") hit[0]['done'] = True hit[0]['done_at'] = time.strftime('%Y-%m-%dT%H:%M:%S+00:00', time.gmtime()) _save(data) print('done:', args.id) elif args.cmd == 'clear': n = 0 for r in data: if not r.get('done'): r['done'] = True r['done_at'] = time.strftime('%Y-%m-%dT%H:%M:%S+00:00', time.gmtime()) n += 1 _save(data) print('cleared:', n) finally: fcntl.flock(lock, fcntl.LOCK_UN) lock.close() if __name__ == '__main__': main()