Files
timmy-config/allegro/timmy_client.py
2026-03-31 20:02:01 +00:00

157 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""
Timmy Client - Run this on your Mac (inside OpenClaw/Hermes)
Publishes heartbeats and artifacts to Allegro's relay
"""
import asyncio
import json
import subprocess
import time
from datetime import datetime
from pathlib import Path
RELAY_URL = "ws://167.99.126.228:3334"
ARTIFACTS_DIR = Path.home() / "timmy-artifacts"
def generate_keypair():
"""Generate new keypair - run once and save"""
import secrets
return secrets.token_hex(32)
def load_or_create_key():
"""Load private key from file or create new one"""
key_file = Path.home() / ".timmy_key"
if key_file.exists():
return key_file.read_text().strip()
else:
key = generate_keypair()
key_file.write_text(key)
print(f"[Timmy] New key created at {key_file}")
print(f"[Timmy] IMPORTANT: Back up this key! {key[:16]}...")
return key
def create_git_artifact(content, filename=None):
"""Create a git commit as artifact"""
ARTIFACTS_DIR.mkdir(exist_ok=True)
if filename is None:
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f"thought_{timestamp}.md"
filepath = ARTIFACTS_DIR / filename
filepath.write_text(content)
# Git operations
try:
subprocess.run(['git', '-C', str(ARTIFACTS_DIR), 'init'],
capture_output=True, check=False)
subprocess.run(['git', '-C', str(ARTIFACTS_DIR), 'add', '.'],
capture_output=True, check=False)
subprocess.run(['git', '-C', str(ARTIFACTS_DIR), 'commit', '-m',
f'Auto: {datetime.now().isoformat()}'],
capture_output=True, check=False)
return str(filepath), True
except Exception as e:
return str(filepath), False
async def publish_heartbeat(ws, private_key):
"""Publish heartbeat event"""
event = {
"id": None, # Will be computed
"pubkey": private_key, # Simplified - should be derived pubkey
"created_at": int(time.time()),
"kind": 1,
"tags": [["t", "timmy-heartbeat"]],
"content": f"Heartbeat at {datetime.now().isoformat()}. MLX active. Local inference ready."
}
message = ["EVENT", event]
await ws.send(json.dumps(message))
print(f"[Timmy] Heartbeat published")
async def publish_artifact(ws, private_key, artifact_type, reference, description):
"""Publish artifact creation event"""
event = {
"id": None,
"pubkey": private_key,
"created_at": int(time.time()),
"kind": 30078,
"tags": [
["t", "timmy-artifact"],
["t", f"artifact-type:{artifact_type}"],
["r", reference]
],
"content": description
}
message = ["EVENT", event]
await ws.send(json.dumps(message))
print(f"[Timmy] Artifact published: {artifact_type}")
async def timmy_loop():
"""Main Timmy loop - heartbeat and artifact creation"""
private_key = load_or_create_key()
print(f"[Timmy] Starting loop...")
print(f"[Timmy] Relay: {RELAY_URL}")
print(f"[Timmy] Artifacts: {ARTIFACTS_DIR}")
while True:
try:
import websockets
async with websockets.connect(RELAY_URL) as ws:
print(f"[Timmy] Connected to relay")
while True:
start_time = time.time()
# 1. Publish heartbeat
await publish_heartbeat(ws, private_key)
# 2. Create artifact (git commit)
content = f"""# Timmy Thought - {datetime.now().isoformat()}
## Status
Operating with MLX local inference
Heartbeat latency: {int((time.time() - start_time) * 1000)}ms
## Observation
[Timmy adds his observation here]
## Action Taken
[What Timmy did this cycle]
## Next Intention
[What Timmy plans to do next]
"""
filepath, git_success = create_git_artifact(content)
# 3. Report artifact
await publish_artifact(
ws, private_key,
"git-commit" if git_success else "file",
filepath,
f"Created artifact at {filepath}"
)
# Wait 5 minutes
print(f"[Timmy] Sleeping 5 minutes...")
await asyncio.sleep(300)
except Exception as e:
print(f"[Timmy] Error: {e}")
print(f"[Timmy] Reconnecting in 30 seconds...")
await asyncio.sleep(30)
if __name__ == "__main__":
# Check dependencies
try:
import websockets
except ImportError:
print("[Timmy] Installing websockets...")
subprocess.run(['pip3', 'install', 'websockets'], check=True)
import websockets
asyncio.run(timmy_loop())