aboutsummaryrefslogtreecommitdiff
path: root/scripts/newos4-reply.py
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-23 14:39:38 +0200
committerAgando <roduccomm@gmail.com>2026-10-23 14:39:38 +0200
commitcfe5b7a842890c232358ae191652c5e5e81591e1 (patch)
treeba27d718d1fd1c8df48b9e91eea071949aa1946f /scripts/newos4-reply.py
parentf6a48a90e374e79b5ca8c0f73d2eb9b3d95868ec (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-xscripts/newos4-reply.py65
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()