Add reusable multi-session skill and five-page project workbench
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Keep the local queue worker and visible-history mirror available without exporting auth."""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from worker import atomic_json,session_lock
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--config',required=True)
|
||||
args=parser.parse_args()
|
||||
config_path=Path(args.config).resolve()
|
||||
cfg=json.loads(config_path.read_text(encoding='utf-8-sig'))
|
||||
root=Path(cfg['state_file']).expanduser().resolve().parent
|
||||
root.mkdir(parents=True,exist_ok=True,mode=0o700)
|
||||
if os.name=='posix':os.umask(0o077)
|
||||
identity='supervisor:'+json.dumps({'control':cfg['control'],'session_id':cfg['session_id']},sort_keys=True)
|
||||
processes={}
|
||||
logs={}
|
||||
next_start={}
|
||||
with session_lock(identity):
|
||||
try:
|
||||
while True:
|
||||
state_path=Path(cfg['state_file']).expanduser()
|
||||
state=json.loads(state_path.read_text(encoding='utf-8')) if state_path.exists() else {}
|
||||
for role in ('worker','mirror'):
|
||||
process=processes.get(role)
|
||||
if process and process.poll() is None:continue
|
||||
if role=='worker' and (state.get('needs_reconciliation') or (state.get('inflight') and not state.get('pending_completion'))):
|
||||
continue
|
||||
if time.monotonic()<next_start.get(role,0):continue
|
||||
if role not in logs:logs[role]=open(root/(role+'.log'),'a',encoding='utf-8')
|
||||
command=[sys.executable,'-X','utf8',str(Path(__file__).with_name(role+'.py')),'--config',str(config_path)]
|
||||
process=subprocess.Popen(command,stdout=logs[role],stderr=logs[role],
|
||||
**({'creationflags':subprocess.CREATE_NO_WINDOW} if os.name=='nt' else {}))
|
||||
processes[role]=process
|
||||
next_start[role]=time.monotonic()+15
|
||||
atomic_json(root/'bridge-service.json',{'pid':os.getpid(),'updated':time.time(),
|
||||
'children':{name:{'pid':p.pid,'running':p.poll() is None} for name,p in processes.items()},
|
||||
'worker_paused_for_reconciliation':bool((state.get('needs_reconciliation') or (state.get('inflight') and not state.get('pending_completion')))
|
||||
and ('worker' not in processes or processes['worker'].poll() is not None))})
|
||||
time.sleep(5)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
for process in processes.values():
|
||||
if process.poll() is None:process.terminate()
|
||||
for process in processes.values():
|
||||
try:process.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired:process.kill()
|
||||
for stream in logs.values():stream.close()
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Reference in New Issue
Block a user