reconcile: sync repo to live deployment (wake_fleet + apropos tools, stash_recall helper)
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
import urllib.request, json, threading, queue, sys
|
||||||
|
BASE="http://192.168.88.184:8080"
|
||||||
|
q=queue.Queue()
|
||||||
|
def sse():
|
||||||
|
try:
|
||||||
|
r=urllib.request.urlopen(BASE+"/sse", timeout=30)
|
||||||
|
for raw in r:
|
||||||
|
s=raw.decode(errors="replace").strip()
|
||||||
|
if s.startswith("data:"): q.put(s[5:].strip())
|
||||||
|
except Exception as e: q.put("ERR:"+str(e))
|
||||||
|
threading.Thread(target=sse,daemon=True).start()
|
||||||
|
try:
|
||||||
|
ep=q.get(timeout=10)
|
||||||
|
if ep.startswith("ERR:"): print("stash unreachable:",ep); sys.exit(1)
|
||||||
|
purl=BASE+ep if ep.startswith("/") else ep
|
||||||
|
def post(o):
|
||||||
|
urllib.request.urlopen(urllib.request.Request(purl,data=json.dumps(o).encode(),headers={"Content-Type":"application/json"}),timeout=15).read()
|
||||||
|
post({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"apropos","version":"1"}}})
|
||||||
|
q.get(timeout=10)
|
||||||
|
post({"jsonrpc":"2.0","method":"notifications/initialized"})
|
||||||
|
query=sys.argv[1] if len(sys.argv)>1 else ""
|
||||||
|
limit=int(sys.argv[2]) if len(sys.argv)>2 else 3
|
||||||
|
post({"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"recall","arguments":{"query":query,"limit":limit}}})
|
||||||
|
d=json.loads(q.get(timeout=25))
|
||||||
|
txt=d.get("result",{}).get("content",[{}])[0].get("text","[]")
|
||||||
|
facts=json.loads(txt)
|
||||||
|
if not facts: print("(no memory found for: %s)"%query); sys.exit(0)
|
||||||
|
for f in facts: print("- %s (score %.2f)" % (f.get("content","").strip(), f.get("score",0)))
|
||||||
|
except Exception as e: print("recall error:",e); sys.exit(1)
|
||||||
@@ -254,3 +254,39 @@ server:tool("mediagrab",
|
|||||||
return run("incus exec doppler -- python3 /opt/mediagrab/mediagrab.py "
|
return run("incus exec doppler -- python3 /opt/mediagrab/mediagrab.py "
|
||||||
.. a.command, 120)
|
.. a.command, 120)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- ---- Fleet wake (pipi: wake-only, no power-off) ----
|
||||||
|
server:tool("wake_fleet",
|
||||||
|
"Wake a fleet NUC (pve1..pve4) via Fritz!Box Wake-on-LAN. Powers a node ON only; it cannot power anything off. Node boots in ~30-60s.",
|
||||||
|
{
|
||||||
|
type = "object",
|
||||||
|
properties = {
|
||||||
|
node = { type = "string", description = "Node to wake: '1'..'4' or 'pve1'..'pve4'" },
|
||||||
|
},
|
||||||
|
required = { "node" },
|
||||||
|
},
|
||||||
|
function(a)
|
||||||
|
local node = tostring(a.node or ""):gsub("[^%w]", "")
|
||||||
|
if not node:match("^p?v?e?[1-4]$") then
|
||||||
|
return "Error: node must be 1-4 or pve1-pve4 (got: " .. tostring(a.node) .. ")"
|
||||||
|
end
|
||||||
|
return run("sudo /root/.local/bin/wake-pve " .. node .. " 2>&1", 15)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- ---- apropos: lean facade for stash recall (read-only memory) ----
|
||||||
|
server:tool("apropos",
|
||||||
|
"Search shared fleet memory (stash) for facts about the fleet, projects, decisions, and preferences. query = 2-6 words on the topic; limit = max results (default 3). Read-only.",
|
||||||
|
{
|
||||||
|
type = "object",
|
||||||
|
properties = {
|
||||||
|
query = { type = "string", description = "2-6 words describing what to recall" },
|
||||||
|
limit = { type = "integer", description = "max results, default 3" },
|
||||||
|
},
|
||||||
|
required = { "query" },
|
||||||
|
},
|
||||||
|
function(a)
|
||||||
|
local q = tostring(a.query or ""):gsub("[^%w%s%-%.]", " "):gsub("%s+", " ")
|
||||||
|
if q:gsub("%s","") == "" then return "Error: query required" end
|
||||||
|
local lim = tonumber(a.limit) or 3
|
||||||
|
return run("python3 /opt/lmcp/helpers/stash_recall.py '" .. q .. "' " .. lim, 30)
|
||||||
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user