Star-Office-UI/backend/app.py
2026-02-26 11:13:51 +08:00

114 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Star Office UI - Backend State Service"""
from flask import Flask, jsonify, send_from_directory
from datetime import datetime
import json
import os
# Paths
ROOT_DIR = "/root/.openclaw/workspace/star-office-ui"
FRONTEND_DIR = os.path.join(ROOT_DIR, "frontend")
STATE_FILE = os.path.join(ROOT_DIR, "state.json")
app = Flask(__name__, static_folder=FRONTEND_DIR, static_url_path="/static")
# Default state
DEFAULT_STATE = {
"state": "idle",
"detail": "等待任务中...",
"progress": 0,
"updated_at": datetime.now().isoformat()
}
def load_state():
"""Load state from file.
Includes a simple auto-idle mechanism:
- If the last update is older than ttl_seconds (default 25s)
and the state is a "working" state, we fall back to idle.
This avoids the UI getting stuck at the desk when no new updates arrive.
"""
state = None
if os.path.exists(STATE_FILE):
try:
with open(STATE_FILE, "r", encoding="utf-8") as f:
state = json.load(f)
except Exception:
state = None
if not isinstance(state, dict):
state = dict(DEFAULT_STATE)
# Auto-idle
try:
ttl = int(state.get("ttl_seconds", 25))
updated_at = state.get("updated_at")
s = state.get("state", "idle")
working_states = {"writing", "researching", "executing"}
if updated_at and s in working_states:
# tolerate both with/without timezone
dt = datetime.fromisoformat(updated_at.replace("Z", "+00:00"))
# Use UTC for aware datetimes; local time for naive.
if dt.tzinfo:
from datetime import timezone
age = (datetime.now(timezone.utc) - dt.astimezone(timezone.utc)).total_seconds()
else:
age = (datetime.now() - dt).total_seconds()
if age > ttl:
state["state"] = "idle"
state["detail"] = "待命中(自动回到休息区)"
state["progress"] = 0
state["updated_at"] = datetime.now().isoformat()
# persist the auto-idle so every client sees it consistently
try:
save_state(state)
except Exception:
pass
except Exception:
pass
return state
def save_state(state: dict):
"""Save state to file"""
with open(STATE_FILE, "w", encoding="utf-8") as f:
json.dump(state, f, ensure_ascii=False, indent=2)
# Initialize state
if not os.path.exists(STATE_FILE):
save_state(DEFAULT_STATE)
@app.route("/", methods=["GET"])
def index():
"""Serve the pixel office UI"""
return send_from_directory(FRONTEND_DIR, "index.html")
@app.route("/status", methods=["GET"])
def get_status():
"""Get current state"""
state = load_state()
return jsonify(state)
@app.route("/health", methods=["GET"])
def health():
"""Health check"""
return jsonify({"status": "ok", "timestamp": datetime.now().isoformat()})
if __name__ == "__main__":
print("=" * 50)
print("Star Office UI - Backend State Service")
print("=" * 50)
print(f"State file: {STATE_FILE}")
print("Listening on: http://0.0.0.0:18791")
print("=" * 50)
app.run(host="0.0.0.0", port=18791, debug=False)