diff options
| author | Agando <roduccomm@gmail.com> | 2026-10-23 14:39:38 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-10-23 14:39:38 +0200 |
| commit | cfe5b7a842890c232358ae191652c5e5e81591e1 (patch) | |
| tree | ba27d718d1fd1c8df48b9e91eea071949aa1946f /scripts/newos4-reply.py | |
| parent | f6a48a90e374e79b5ca8c0f73d2eb9b3d95868ec (diff) | |
feat(newos4): maintenance agent channel — triage engine, two-way replies, cron
- scripts/newos4-triage.py: deterministic collector (records vs occurrences, real vs
noise vs self-referential, new reports since last run, checker WARN/FAIL). Untrusted
strings are sanitised and prefixed DATA| before they can reach an agent prompt;
state + output written under flock with mode 644.
- scripts/newos4-reply.py: appends one reply to data/maintenance/replies.json under
flock. Hermes writes here instead of POSTing with an admin cookie, so an expired
session can never look like a delivered reply.
- api/debug/agent-replies.php: GET/POST (admin-gated by nginx) half of the thread.
- api/debug/report.php: accepts thread + kind=maintenance so in-app feedback is
distinguishable from operator bug reports.
- terminal: `feedback <message>` and `feedback-inbox` commands; help.html rows added
(help coverage 357/357, modules-index regenerated).
Live proof: `feedback "…"` filed RPT-20261023-123825-781fba86 with thread
thr-glmfwae2 and kind=maintenance; agent-replies GET returned the posted reply, POST
returned 201; triage first run reported 9 new reports, second run 0.
Cron: newos4-maintenance (7e3b326daa4c) daily 07:15, newos4-skills-weekly
(cb6bc2a16216) Mondays 06:30.
Diffstat (limited to 'scripts/newos4-reply.py')
| -rwxr-xr-x | scripts/newos4-reply.py | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/scripts/newos4-reply.py b/scripts/newos4-reply.py new file mode 100755 index 0000000..8144285 --- /dev/null +++ b/scripts/newos4-reply.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +"""Append one reply to the newOS4 maintenance thread. + +Hermes writes here instead of POSTing to agent-replies.php, so an expired admin cookie can +never make a reply look delivered when it was not. Same JSON schema and file the PHP endpoint +serves to the in-app `feedback-inbox` command. + +Usage: newos4-reply.py "Triage 2026-10-23: ..." [--author hermes] +""" +import argparse, fcntl, json, os, time + +ROOT = '/var/www/webx11.duckdns.org/spaces/newos4' +DIR = os.path.join(ROOT, 'data', 'maintenance') +FILE = os.path.join(DIR, 'replies.json') +MAX = 200 + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument('body') + ap.add_argument('--author', default='hermes', choices=['hermes', 'newos4']) + a = ap.parse_args() + + body = a.body.strip() + if not body: + raise SystemExit('refusing to write an empty reply') + body = body[:2000] + + os.makedirs(DIR, exist_ok=True) + lock = open(FILE + '.lock', 'a+') + fcntl.flock(lock, fcntl.LOCK_EX) + try: + try: + with open(FILE, encoding='utf-8') as f: + data = json.load(f) + except Exception: + data = [] + if not isinstance(data, list): + data = [] + entry = { + 'id': 'REP-' + time.strftime('%Y%m%d-%H%M%S') + '-' + os.urandom(3).hex(), + 'created_at': time.strftime('%Y-%m-%dT%H:%M:%S+00:00', time.gmtime()), + 'author': a.author, + 'body': body, + } + data.append(entry) + 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) + try: + os.chmod(FILE, 0o664) + except Exception: + pass + print(entry['id']) + finally: + fcntl.flock(lock, fcntl.LOCK_UN) + lock.close() + + +if __name__ == '__main__': + main() |