2026-10-03 19:48:09 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""
|
2026-10-03 21:36:42 +00:00
|
|
|
DM: Headless Direct Message API (muse.ai) — with logging and verification.
|
2026-10-03 19:48:09 +00:00
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
Every send generates a UUID, logs to dm-log.jsonl, and verifies via read-back.
|
|
|
|
|
No trust required: success means the UUID was found in the chat.
|
2026-10-03 19:48:09 +00:00
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
dm.py send --agent 646 --target <chat_id|main> "message"
|
|
|
|
|
dm.py read --agent 646 --target <chat_id|main> [n]
|
2026-10-03 21:36:42 +00:00
|
|
|
dm.py verify --agent 646 --target <chat_id|main> <uuid>
|
|
|
|
|
dm.py log [--n 20]
|
2026-10-03 19:48:09 +00:00
|
|
|
"""
|
|
|
|
|
import argparse
|
|
|
|
|
import subprocess
|
|
|
|
|
import time
|
|
|
|
|
import sys
|
2026-10-03 21:36:42 +00:00
|
|
|
import uuid
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
from datetime import datetime, timezone
|
2026-10-03 19:48:09 +00:00
|
|
|
|
|
|
|
|
API = "/home/super/Projects/NetVM/bin/muse-chat-api.py"
|
|
|
|
|
NETVM_EXEC = "/home/super/Projects/NetVM/bin/netvm-exec.sh"
|
2026-10-03 20:50:53 +00:00
|
|
|
VALID_AGENTS = ["muse", "pip", "646", "opm"]
|
2026-10-03 21:36:42 +00:00
|
|
|
LOG_FILE = "/home/super/Projects/NetVM/dm-log.jsonl"
|
|
|
|
|
|
|
|
|
|
def log_event(event):
|
|
|
|
|
"""Append to JSONL log."""
|
|
|
|
|
event["ts"] = datetime.now(timezone.utc).isoformat()
|
|
|
|
|
with open(LOG_FILE, "a") as f:
|
|
|
|
|
f.write(json.dumps(event) + "\n")
|
2026-10-03 19:48:09 +00:00
|
|
|
|
|
|
|
|
def run(cmd, timeout=60):
|
|
|
|
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
|
|
|
|
|
return result.stdout.strip()
|
|
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
def dm_send(agent, target, message, verify=True):
|
|
|
|
|
"""Send a DM with UUID and verification."""
|
2026-10-03 19:48:09 +00:00
|
|
|
if agent not in VALID_AGENTS:
|
|
|
|
|
print(f"ERROR: Unknown agent {agent}", file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
msg_id = str(uuid.uuid4())[:8]
|
|
|
|
|
# Embed ID in message for verification
|
|
|
|
|
tagged = f"[{msg_id}] {message}"
|
|
|
|
|
|
|
|
|
|
log_event({"type": "send_start", "id": msg_id, "agent": agent, "target": target, "msg": message[:100]})
|
|
|
|
|
|
2026-10-03 19:48:09 +00:00
|
|
|
# Navigate to target
|
|
|
|
|
if target == "main":
|
|
|
|
|
run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat main")
|
|
|
|
|
else:
|
|
|
|
|
run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat use {target}")
|
|
|
|
|
|
|
|
|
|
time.sleep(2)
|
|
|
|
|
|
|
|
|
|
# Send
|
2026-10-03 21:36:42 +00:00
|
|
|
safe = tagged.replace('"', '\\"').replace('$', '\\$').replace('`', '\\`')[:1000]
|
|
|
|
|
run(f'{NETVM_EXEC} {agent} -- python3 {API} --account {agent} send "{safe}"')
|
2026-10-03 19:48:09 +00:00
|
|
|
|
|
|
|
|
# Back to main
|
|
|
|
|
run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat main")
|
|
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
log_event({"type": "send_done", "id": msg_id, "agent": agent, "target": target})
|
|
|
|
|
|
|
|
|
|
if verify:
|
|
|
|
|
time.sleep(3)
|
|
|
|
|
# Read back and check for our ID
|
|
|
|
|
msgs = dm_read(agent, target, n=5, quiet=True)
|
|
|
|
|
if msg_id in msgs:
|
|
|
|
|
log_event({"type": "verified", "id": msg_id, "agent": agent, "target": target})
|
|
|
|
|
print(f"DM {msg_id} to {agent}/{target}: VERIFIED")
|
|
|
|
|
return msg_id
|
|
|
|
|
else:
|
|
|
|
|
log_event({"type": "verify_failed", "id": msg_id, "agent": agent, "target": target})
|
|
|
|
|
print(f"DM {msg_id} to {agent}/{target}: FAILED (not found in read-back)", file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
else:
|
|
|
|
|
print(f"DM {msg_id} to {agent}/{target}: SENT (unverified)")
|
|
|
|
|
return msg_id
|
2026-10-03 19:48:09 +00:00
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
def dm_read(agent, target, n=5, quiet=False):
|
|
|
|
|
"""Read DMs via headless."""
|
2026-10-03 19:48:09 +00:00
|
|
|
if agent not in VALID_AGENTS:
|
|
|
|
|
print(f"ERROR: Unknown agent {agent}", file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
if target == "main":
|
|
|
|
|
run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat main")
|
|
|
|
|
else:
|
|
|
|
|
run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat use {target}")
|
|
|
|
|
|
|
|
|
|
time.sleep(2)
|
|
|
|
|
msgs = run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} messages {n}")
|
|
|
|
|
run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat main")
|
|
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
if not quiet:
|
|
|
|
|
print(msgs)
|
2026-10-03 19:48:09 +00:00
|
|
|
return msgs
|
|
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
def dm_verify(agent, target, msg_id):
|
|
|
|
|
"""Check if a message ID exists in the chat."""
|
|
|
|
|
msgs = dm_read(agent, target, n=10, quiet=True)
|
|
|
|
|
if msg_id in msgs:
|
|
|
|
|
print(f"VERIFIED: {msg_id} found in {agent}/{target}")
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
print(f"NOT FOUND: {msg_id} in {agent}/{target}", file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
def dm_log(n=20):
|
|
|
|
|
"""Show recent log entries."""
|
|
|
|
|
if not os.path.exists(LOG_FILE):
|
|
|
|
|
print("No log file yet")
|
|
|
|
|
return
|
|
|
|
|
with open(LOG_FILE) as f:
|
|
|
|
|
lines = f.readlines()
|
|
|
|
|
for line in lines[-n:]:
|
|
|
|
|
e = json.loads(line)
|
|
|
|
|
print(f"{e['ts'][:19]} {e['type']:12} {e.get('id','-'):8} {e.get('agent','-')}/{e.get('target','-')}")
|
|
|
|
|
|
2026-10-03 19:48:09 +00:00
|
|
|
def dm_thread(from_agent, to_agent, target, message):
|
|
|
|
|
attributed = f"[{from_agent}] {message}"
|
|
|
|
|
return dm_send(to_agent, target, attributed)
|
|
|
|
|
|
|
|
|
|
def main():
|
2026-10-03 21:36:42 +00:00
|
|
|
p = argparse.ArgumentParser(description="DM: Headless Direct Messages with verification")
|
2026-10-03 19:48:09 +00:00
|
|
|
sub = p.add_subparsers(dest='cmd', required=True)
|
|
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
ps = sub.add_parser('send', help='Send a DM with verification')
|
2026-10-03 19:48:09 +00:00
|
|
|
ps.add_argument('--agent', required=True, choices=VALID_AGENTS)
|
2026-10-03 21:36:42 +00:00
|
|
|
ps.add_argument('--target', required=True)
|
|
|
|
|
ps.add_argument('--no-verify', action='store_true', help='Skip verification')
|
|
|
|
|
ps.add_argument('message')
|
|
|
|
|
ps.set_defaults(func=lambda a: dm_send(a.agent, a.target, a.message, verify=not a.no_verify))
|
2026-10-03 19:48:09 +00:00
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
pr = sub.add_parser('read', help='Read DMs')
|
2026-10-03 19:48:09 +00:00
|
|
|
pr.add_argument('--agent', required=True, choices=VALID_AGENTS)
|
2026-10-03 21:36:42 +00:00
|
|
|
pr.add_argument('--target', required=True)
|
|
|
|
|
pr.add_argument('--n', type=int, default=5)
|
2026-10-03 19:48:09 +00:00
|
|
|
pr.set_defaults(func=lambda a: dm_read(a.agent, a.target, a.n))
|
|
|
|
|
|
2026-10-03 21:36:42 +00:00
|
|
|
pv = sub.add_parser('verify', help='Verify a message ID exists')
|
|
|
|
|
pv.add_argument('--agent', required=True, choices=VALID_AGENTS)
|
|
|
|
|
pv.add_argument('--target', required=True)
|
|
|
|
|
pv.add_argument('msg_id')
|
|
|
|
|
pv.set_defaults(func=lambda a: dm_verify(a.agent, a.target, a.msg_id))
|
|
|
|
|
|
|
|
|
|
pl = sub.add_parser('log', help='Show DM log')
|
|
|
|
|
pl.add_argument('--n', type=int, default=20)
|
|
|
|
|
pl.set_defaults(func=lambda a: dm_log(a.n))
|
|
|
|
|
|
2026-10-03 19:48:09 +00:00
|
|
|
pt = sub.add_parser('thread', help='Thread from one agent to another')
|
|
|
|
|
pt.add_argument('--from', dest='from_agent', required=True, choices=VALID_AGENTS)
|
|
|
|
|
pt.add_argument('--to', dest='to_agent', required=True, choices=VALID_AGENTS)
|
2026-10-03 21:36:42 +00:00
|
|
|
pt.add_argument('--target', required=True)
|
|
|
|
|
pt.add_argument('message')
|
2026-10-03 19:48:09 +00:00
|
|
|
pt.set_defaults(func=lambda a: dm_thread(a.from_agent, a.to_agent, a.target, a.message))
|
|
|
|
|
|
|
|
|
|
args = p.parse_args()
|
|
|
|
|
args.func(args)
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|