feat: per-site SHIFT path with video search, fix agent token injection, accept cpu_temp

- deploy_agent.sh: agent v3.3-shift — use dashboard-configured SHIFT path for
  file fetch/search (SR/SHIFT sites have no TAKELEAP folder), date fast-path;
  fix installer discarding the injected agent token (sentinel was being
  rewritten by the server's placeholder replace, agents ended up tokenless)
- central_api_prototype.py: replace only the token assignment when serving the
  installer; add cpu_temp to TelemetryPayload (agents already send it, it was
  silently dropped); new endpoints set-shift-path, request-search,
  search-results; heartbeat response now carries shift_path + pending search
- commands.json: restore last_reboot/dummy, add update_diag diagnostic
- CLAUDE.md: document deployment layout, procedures, and gotchas
This commit is contained in:
2026-07-16 17:31:33 +05:30
parent b9aeedc5ef
commit c929176cfa
4 changed files with 323 additions and 9 deletions

View File

@@ -40,7 +40,9 @@ if [ -z "$CENTRAL_URL" ]; then CENTRAL_URL="http://rmm-backend.seekright.com"; f
AGENT_TOKEN="__AGENT_TOKEN__"
if [ -n "$ARG_TOKEN" ]; then
AGENT_TOKEN="$ARG_TOKEN"
elif [ "$AGENT_TOKEN" = "__AGENT_TOKEN__" ]; then
elif [ "$AGENT_TOKEN" = "__AGENT_""TOKEN__" ]; then
# (placeholder split in two so the server's token injection can't rewrite this
# comparison — it must only replace the assignment above)
if [ -f "$EXISTING_PY" ]; then
AGENT_TOKEN="$(sed -n 's/^AGENT_TOKEN = "\(.*\)"/\1/p' "$EXISTING_PY" | head -1)"
else
@@ -99,7 +101,7 @@ def get_cpu_temp():
pass
return None
AGENT_VERSION = "3.2-auth"
AGENT_VERSION = "3.3-shift"
CLIENT_ID = "TEMPLATE_CLIENT_ID"
CENTRAL_BASE = "TEMPLATE_CENTRAL_URL"
@@ -123,6 +125,8 @@ CENTRAL_FILE_STATUS_URL = f"{CENTRAL_BASE}/api/file-transfer-status?client_id={C
CACHED_COMMANDS = {}
command_in_progress = False
file_transfer_in_progress = False
SHIFT_PATH = None # per-site SHIFT folder, configured on the dashboard, delivered via heartbeat
search_in_progress = False
def run_command_async(command_key, safe_cmd_list, is_shutdown_or_restart):
global command_in_progress
@@ -367,7 +371,23 @@ def send_heartbeat():
t = threading.Thread(target=handle_file_request_async, args=(fname,))
t.daemon = True
t.start()
# Server-configured SHIFT folder for this node
global SHIFT_PATH
sp = res.get("shift_path")
if sp:
SHIFT_PATH = sp
# Handle video search requests
squery = res.get("search", "none")
if squery and squery != "none":
global search_in_progress
if not search_in_progress:
search_in_progress = True
t = threading.Thread(target=run_file_search, args=(squery,))
t.daemon = True
t.start()
print("[*] Heartbeat successful.")
except Exception as e:
print(f"[!] Failed heartbeat: {e}")
@@ -685,6 +705,75 @@ def list_takeleap_subfolder_files(subfolder_name):
def list_takeleap_subfolder(subfolder_name):
list_takeleap_subfolder_files(subfolder_name)
def find_shift_roots():
# The dashboard-configured path wins; without one, fall back to any
# */TAKELEAP/SHIFT discovered by the legacy drive scan.
if SHIFT_PATH and os.path.isdir(SHIFT_PATH):
return [SHIFT_PATH]
roots = []
if os.name == "nt":
drives = [f"{letter}:\\" for letter in string.ascii_uppercase if os.path.exists(f"{letter}:\\")]
else:
drives = ["/mnt", "/media", "/home"]
if os.path.exists("/TAKELEAP"):
drives.append("/TAKELEAP")
for drive in drives:
for root, dirs, files in os.walk(drive, topdown=True):
if os.name == "nt":
dirs[:] = [d for d in dirs if d.lower() not in {"windows", "program files", "program files (x86)", "appdata", "programdata", "$recycle.bin", "system volume information", "node_modules", ".git"}]
else:
if root == "/":
dirs[:] = [d for d in dirs if d.lower() not in {"proc", "sys", "dev", "var", "lib", "run", "boot", "snap", "node_modules", ".git"}]
elif root == "/mnt":
dirs[:] = [d for d in dirs if not (len(d) == 1 and d.isalpha()) and d.lower() not in {"wslg", "wsl"}]
if os.path.basename(root).upper() == "TAKELEAP":
for sub in dirs:
if sub.upper() == "SHIFT":
roots.append(os.path.join(root, sub))
dirs[:] = [] # don't descend into an already-claimed subtree
return roots
def run_file_search(query):
# Case-insensitive filename substring search in the SHIFT folder(s);
# results are posted back for the dashboard to render.
global search_in_progress
MAX_RESULTS = 200
try:
target = query.lower()
roots = find_shift_roots()
results = []
for base in roots:
for froot, fdirs, ffiles in os.walk(base):
for f in ffiles:
if target in f.lower():
fp = os.path.join(froot, f)
entry = {"name": f, "path": fp}
try:
st = os.stat(fp)
entry["size_mb"] = round(st.st_size / (1024 * 1024), 2)
entry["modified"] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(st.st_mtime))
except Exception:
pass
results.append(entry)
if len(results) >= MAX_RESULTS:
break
if len(results) >= MAX_RESULTS:
break
if len(results) >= MAX_RESULTS:
break
payload = {"query": query, "results": results, "searched_path": ", ".join(roots)}
if not roots:
payload["error"] = "No SHIFT folder configured or found on this system"
data = json.dumps(payload).encode('utf-8')
req = urllib.request.Request(f"{CENTRAL_BASE}/api/search-results?client_id={CLIENT_ID}", data=data, headers={'Content-Type': 'application/json'}, method='POST')
req.add_header('ngrok-skip-browser-warning', 'true')
urllib.request.urlopen(req, timeout=30)
print(f" -> Search '{query}': posted {len(results)} match(es).")
except Exception as e:
print(f" -> [!] File search failed: {e}")
finally:
search_in_progress = False
# ============================================================
# Remote File Transfer: dashboard requests a file by name,
# agent finds it under any TAKELEAP folder and uploads it.
@@ -725,6 +814,23 @@ def find_file_in_takeleap(filename):
except Exception:
pass
# Configured SHIFT path takes priority — SR/SHIFT sites have no TAKELEAP
# folder anywhere, so the legacy scan below can never find their files.
if SHIFT_PATH and os.path.isdir(SHIFT_PATH):
if fast_path_rel:
# SHIFT_PATH ends in .../SHIFT and fast_path_rel starts with SHIFT/,
# so join the date subpath onto SHIFT_PATH's parent.
specific_dir = os.path.join(os.path.dirname(SHIFT_PATH), fast_path_rel)
if os.path.exists(specific_dir):
for froot, fdirs, ffiles in os.walk(specific_dir):
for f in ffiles:
if f.lower() == target:
return os.path.join(froot, f)
for froot, fdirs, ffiles in os.walk(SHIFT_PATH):
for f in ffiles:
if f.lower() == target:
return os.path.join(froot, f)
if os.name == "nt":
drives = [f"{letter}:\\" for letter in string.ascii_uppercase if os.path.exists(f"{letter}:\\")]
else: