mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-10-27 00:01:58 -06:00
Merge pull request #83 from bunny-lab-io/codex/refactor-update.ps1-and-api-endpoints
Remove legacy agent hash aliases
This commit is contained in:
@@ -127,7 +127,7 @@ export default function DeviceList({ onSelectDevice }) {
|
|||||||
const fetchLatestRepoHash = useCallback(async () => {
|
const fetchLatestRepoHash = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams({ repo: "bunny-lab-io/Borealis", branch: "main" });
|
const params = new URLSearchParams({ repo: "bunny-lab-io/Borealis", branch: "main" });
|
||||||
const resp = await fetch(`/api/agent/repo_hash?${params.toString()}`);
|
const resp = await fetch(`/api/repo/current_hash?${params.toString()}`);
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
const sha = (json?.sha || "").trim();
|
const sha = (json?.sha || "").trim();
|
||||||
if (!resp.ok || !sha) {
|
if (!resp.ok || !sha) {
|
||||||
|
|||||||
@@ -356,8 +356,8 @@ def health():
|
|||||||
return jsonify({"status": "ok"})
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/agent/repo_hash", methods=["GET"])
|
@app.route("/api/repo/current_hash", methods=["GET"])
|
||||||
def api_agent_repo_hash():
|
def api_repo_current_hash():
|
||||||
try:
|
try:
|
||||||
repo = (request.args.get('repo') or _DEFAULT_REPO).strip()
|
repo = (request.args.get('repo') or _DEFAULT_REPO).strip()
|
||||||
branch = (request.args.get('branch') or _DEFAULT_BRANCH).strip()
|
branch = (request.args.get('branch') or _DEFAULT_BRANCH).strip()
|
||||||
@@ -390,130 +390,141 @@ def api_agent_repo_hash():
|
|||||||
return jsonify(payload)
|
return jsonify(payload)
|
||||||
return jsonify(payload), 503
|
return jsonify(payload), 503
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
_write_service_log('server', f'/api/agent/repo_hash error: {exc}')
|
_write_service_log('server', f'/api/repo/current_hash error: {exc}')
|
||||||
return jsonify({"error": "internal error"}), 500
|
return jsonify({"error": "internal error"}), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/agent/update_check", methods=["POST"])
|
def _lookup_agent_hash_record(agent_id: str) -> Optional[Dict[str, Any]]:
|
||||||
def api_agent_update_check():
|
agent_id = (agent_id or '').strip()
|
||||||
data = request.get_json(silent=True) or {}
|
|
||||||
agent_id = (data.get("agent_id") or "").strip()
|
|
||||||
if not agent_id:
|
if not agent_id:
|
||||||
return jsonify({"error": "agent_id required"}), 400
|
return None
|
||||||
|
|
||||||
repo_info = _refresh_default_repo_hash()
|
info = registered_agents.get(agent_id) or {}
|
||||||
repo_sha = (repo_info.get("sha") or "").strip()
|
candidate = (info.get('agent_hash') or '').strip()
|
||||||
if not repo_sha:
|
hostname = (info.get('hostname') or '').strip()
|
||||||
payload = {
|
if candidate:
|
||||||
"error": repo_info.get("error") or "repository hash unavailable",
|
payload: Dict[str, Any] = {
|
||||||
"repo_source": repo_info.get("source"),
|
'agent_id': agent_id,
|
||||||
|
'agent_hash': candidate,
|
||||||
|
'source': 'memory',
|
||||||
}
|
}
|
||||||
return jsonify(payload), 503
|
if hostname:
|
||||||
|
payload['hostname'] = hostname
|
||||||
|
return payload
|
||||||
|
|
||||||
registry_info = registered_agents.get(agent_id) or {}
|
|
||||||
hostname = (registry_info.get("hostname") or "").strip() or None
|
|
||||||
stored_hash: Optional[str] = (registry_info.get("agent_hash") or "").strip() or None
|
|
||||||
conn = None
|
conn = None
|
||||||
try:
|
try:
|
||||||
conn = _db_conn()
|
conn = _db_conn()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
rows = _device_rows_for_agent(cur, agent_id)
|
rows = _device_rows_for_agent(cur, agent_id)
|
||||||
except Exception:
|
effective_hostname = hostname or None
|
||||||
rows = []
|
if rows:
|
||||||
|
if not effective_hostname:
|
||||||
|
effective_hostname = rows[0].get('hostname') or effective_hostname
|
||||||
|
for row in rows:
|
||||||
|
if row.get('matched'):
|
||||||
|
normalized_hash = (row.get('agent_hash') or '').strip()
|
||||||
|
if not normalized_hash:
|
||||||
|
details = row.get('details') or {}
|
||||||
|
try:
|
||||||
|
normalized_hash = ((details.get('summary') or {}).get('agent_hash') or '').strip()
|
||||||
|
except Exception:
|
||||||
|
normalized_hash = ''
|
||||||
|
if normalized_hash:
|
||||||
|
payload = {
|
||||||
|
'agent_id': agent_id,
|
||||||
|
'agent_hash': normalized_hash,
|
||||||
|
'hostname': row.get('hostname') or effective_hostname,
|
||||||
|
'source': 'database',
|
||||||
|
}
|
||||||
|
return payload
|
||||||
|
first = rows[0]
|
||||||
|
fallback_hash = (first.get('agent_hash') or '').strip()
|
||||||
|
if not fallback_hash:
|
||||||
|
details = first.get('details') or {}
|
||||||
|
try:
|
||||||
|
fallback_hash = ((details.get('summary') or {}).get('agent_hash') or '').strip()
|
||||||
|
except Exception:
|
||||||
|
fallback_hash = ''
|
||||||
|
if fallback_hash:
|
||||||
|
payload = {
|
||||||
|
'agent_id': agent_id,
|
||||||
|
'agent_hash': fallback_hash,
|
||||||
|
'hostname': first.get('hostname') or effective_hostname,
|
||||||
|
'source': 'database',
|
||||||
|
}
|
||||||
|
return payload
|
||||||
|
cur.execute('SELECT hostname, agent_hash, details FROM device_details')
|
||||||
|
for host, db_hash, details_json in cur.fetchall():
|
||||||
|
try:
|
||||||
|
data = json.loads(details_json or '{}')
|
||||||
|
except Exception:
|
||||||
|
data = {}
|
||||||
|
summary = data.get('summary') or {}
|
||||||
|
summary_agent_id = (summary.get('agent_id') or '').strip()
|
||||||
|
if summary_agent_id != agent_id:
|
||||||
|
continue
|
||||||
|
summary_hash = (summary.get('agent_hash') or '').strip()
|
||||||
|
normalized_hash = (db_hash or '').strip() or summary_hash
|
||||||
|
if normalized_hash:
|
||||||
|
return {
|
||||||
|
'agent_id': agent_id,
|
||||||
|
'agent_hash': normalized_hash,
|
||||||
|
'hostname': host,
|
||||||
|
'source': 'database',
|
||||||
|
}
|
||||||
finally:
|
finally:
|
||||||
if conn:
|
if conn:
|
||||||
try:
|
try:
|
||||||
conn.close()
|
conn.close()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
return None
|
||||||
if rows:
|
|
||||||
hostname = rows[0].get("hostname") or hostname
|
|
||||||
for row in rows:
|
|
||||||
if row.get("matched"):
|
|
||||||
hostname = row.get("hostname") or hostname
|
|
||||||
candidate = (row.get("agent_hash") or "").strip()
|
|
||||||
if not candidate:
|
|
||||||
summary = row.get("details") or {}
|
|
||||||
try:
|
|
||||||
candidate = (summary.get("summary") or {}).get("agent_hash") or ""
|
|
||||||
except Exception:
|
|
||||||
candidate = ""
|
|
||||||
candidate = candidate.strip()
|
|
||||||
stored_hash = candidate or None
|
|
||||||
break
|
|
||||||
if stored_hash is None:
|
|
||||||
first = rows[0]
|
|
||||||
candidate = (first.get("agent_hash") or "").strip()
|
|
||||||
if not candidate:
|
|
||||||
details = first.get("details") or {}
|
|
||||||
try:
|
|
||||||
candidate = (details.get("summary") or {}).get("agent_hash") or ""
|
|
||||||
except Exception:
|
|
||||||
candidate = ""
|
|
||||||
candidate = candidate.strip()
|
|
||||||
stored_hash = candidate or None
|
|
||||||
|
|
||||||
update_available = (not stored_hash) or (stored_hash.strip() != repo_sha)
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"agent_id": agent_id,
|
|
||||||
"hostname": hostname,
|
|
||||||
"repo_hash": repo_sha,
|
|
||||||
"agent_hash": stored_hash,
|
|
||||||
"update_available": bool(update_available),
|
|
||||||
"repo_source": repo_info.get("source"),
|
|
||||||
}
|
|
||||||
if repo_info.get("cached") is not None:
|
|
||||||
payload["cached"] = bool(repo_info.get("cached"))
|
|
||||||
if repo_info.get("age_seconds") is not None:
|
|
||||||
payload["age_seconds"] = repo_info.get("age_seconds")
|
|
||||||
if repo_info.get("error"):
|
|
||||||
payload["repo_error"] = repo_info.get("error")
|
|
||||||
return jsonify(payload)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/agent/agent_hash", methods=["POST"])
|
def _apply_agent_hash_update(agent_id: str, agent_hash: str) -> Tuple[Dict[str, Any], int]:
|
||||||
def api_agent_agent_hash_post():
|
agent_id = (agent_id or '').strip()
|
||||||
data = request.get_json(silent=True) or {}
|
agent_hash = (agent_hash or '').strip()
|
||||||
agent_id = (data.get("agent_id") or "").strip()
|
|
||||||
agent_hash = (data.get("agent_hash") or "").strip()
|
|
||||||
if not agent_id or not agent_hash:
|
if not agent_id or not agent_hash:
|
||||||
return jsonify({"error": "agent_id and agent_hash required"}), 400
|
return {'error': 'agent_id and agent_hash required'}, 400
|
||||||
|
|
||||||
conn = None
|
conn = None
|
||||||
hostname = None
|
hostname = None
|
||||||
|
response_payload: Optional[Dict[str, Any]] = None
|
||||||
|
status_code = 200
|
||||||
try:
|
try:
|
||||||
conn = _db_conn()
|
conn = _db_conn()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
rows = _device_rows_for_agent(cur, agent_id)
|
rows = _device_rows_for_agent(cur, agent_id)
|
||||||
target = None
|
target = None
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if row.get("matched"):
|
if row.get('matched'):
|
||||||
target = row
|
target = row
|
||||||
break
|
break
|
||||||
if not target:
|
if not target:
|
||||||
if conn:
|
response_payload = {
|
||||||
conn.close()
|
'status': 'ignored',
|
||||||
return jsonify({"status": "ignored"}), 200
|
'agent_id': agent_id,
|
||||||
|
'agent_hash': agent_hash,
|
||||||
hostname = target.get("hostname")
|
}
|
||||||
details = target.get("details") or {}
|
else:
|
||||||
summary = details.setdefault("summary", {})
|
hostname = target.get('hostname')
|
||||||
summary["agent_hash"] = agent_hash
|
details = target.get('details') or {}
|
||||||
cur.execute(
|
summary = details.setdefault('summary', {})
|
||||||
"UPDATE device_details SET agent_hash=?, details=? WHERE hostname=?",
|
summary['agent_hash'] = agent_hash
|
||||||
(agent_hash, json.dumps(details), hostname),
|
cur.execute(
|
||||||
)
|
'UPDATE device_details SET agent_hash=?, details=? WHERE hostname=?',
|
||||||
conn.commit()
|
(agent_hash, json.dumps(details), hostname),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
if conn:
|
if conn:
|
||||||
try:
|
try:
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
_write_service_log('server', f'/api/agent/agent_hash error: {exc}')
|
_write_service_log('server', f'/api/agent/hash error: {exc}')
|
||||||
return jsonify({"error": "internal error"}), 500
|
return {'error': 'internal error'}, 500
|
||||||
finally:
|
finally:
|
||||||
if conn:
|
if conn:
|
||||||
try:
|
try:
|
||||||
@@ -521,22 +532,53 @@ def api_agent_agent_hash_post():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if response_payload is not None:
|
||||||
|
return response_payload, status_code
|
||||||
|
|
||||||
normalized_hash = agent_hash
|
normalized_hash = agent_hash
|
||||||
if agent_id in registered_agents:
|
if agent_id in registered_agents:
|
||||||
registered_agents[agent_id]["agent_hash"] = normalized_hash
|
registered_agents[agent_id]['agent_hash'] = normalized_hash
|
||||||
try:
|
try:
|
||||||
for aid, rec in registered_agents.items():
|
for aid, rec in registered_agents.items():
|
||||||
if rec.get("hostname") and hostname and rec["hostname"] == hostname:
|
if rec.get('hostname') and hostname and rec['hostname'] == hostname:
|
||||||
rec["agent_hash"] = normalized_hash
|
rec['agent_hash'] = normalized_hash
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return jsonify({
|
payload: Dict[str, Any] = {
|
||||||
"status": "ok",
|
'status': 'ok',
|
||||||
"agent_id": agent_id,
|
'agent_id': agent_id,
|
||||||
"hostname": hostname,
|
'agent_hash': agent_hash,
|
||||||
"agent_hash": agent_hash,
|
}
|
||||||
})
|
if hostname:
|
||||||
|
payload['hostname'] = hostname
|
||||||
|
return payload, 200
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/agent/hash", methods=["GET", "POST"])
|
||||||
|
def api_agent_hash():
|
||||||
|
if request.method == 'GET':
|
||||||
|
agent_id = (request.args.get('agent_id') or request.args.get('id') or '').strip()
|
||||||
|
if not agent_id:
|
||||||
|
data = request.get_json(silent=True) or {}
|
||||||
|
agent_id = (data.get('agent_id') or '').strip()
|
||||||
|
if not agent_id:
|
||||||
|
return jsonify({'error': 'agent_id required'}), 400
|
||||||
|
try:
|
||||||
|
record = _lookup_agent_hash_record(agent_id)
|
||||||
|
except Exception as exc:
|
||||||
|
_write_service_log('server', f'/api/agent/hash lookup error: {exc}')
|
||||||
|
return jsonify({'error': 'internal error'}), 500
|
||||||
|
if record:
|
||||||
|
return jsonify(record)
|
||||||
|
return jsonify({'error': 'agent hash not found'}), 404
|
||||||
|
|
||||||
|
data = request.get_json(silent=True) or {}
|
||||||
|
agent_id = (data.get('agent_id') or '').strip()
|
||||||
|
agent_hash = (data.get('agent_hash') or '').strip()
|
||||||
|
payload, status = _apply_agent_hash_update(agent_id, agent_hash)
|
||||||
|
return jsonify(payload), status
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------
|
# ---------------------------------------------
|
||||||
# Server Time Endpoint
|
# Server Time Endpoint
|
||||||
@@ -3342,92 +3384,6 @@ def set_device_description(hostname: str):
|
|||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/agent/hash/<path:agent_id>", methods=["GET"])
|
|
||||||
def get_agent_hash(agent_id: str):
|
|
||||||
"""Return the last known repository hash for a specific agent."""
|
|
||||||
|
|
||||||
agent_id = (agent_id or "").strip()
|
|
||||||
if not agent_id:
|
|
||||||
return jsonify({"error": "invalid agent id"}), 400
|
|
||||||
|
|
||||||
# Prefer the in-memory registry (updated on every heartbeat/details post).
|
|
||||||
info = registered_agents.get(agent_id) or {}
|
|
||||||
candidate = (info.get("agent_hash") or "").strip()
|
|
||||||
hostname = (info.get("hostname") or "").strip()
|
|
||||||
|
|
||||||
if candidate:
|
|
||||||
return jsonify({
|
|
||||||
"agent_id": agent_id,
|
|
||||||
"agent_hash": candidate,
|
|
||||||
"source": "memory",
|
|
||||||
})
|
|
||||||
|
|
||||||
# Fall back to the persisted device_details row, if any.
|
|
||||||
try:
|
|
||||||
conn = _db_conn()
|
|
||||||
cur = conn.cursor()
|
|
||||||
rows = _device_rows_for_agent(cur, agent_id)
|
|
||||||
if rows:
|
|
||||||
if not hostname:
|
|
||||||
hostname = rows[0].get("hostname") or hostname
|
|
||||||
for row in rows:
|
|
||||||
if row.get("matched"):
|
|
||||||
normalized_hash = (row.get("agent_hash") or "").strip()
|
|
||||||
if not normalized_hash:
|
|
||||||
details = row.get("details") or {}
|
|
||||||
try:
|
|
||||||
normalized_hash = ((details.get("summary") or {}).get("agent_hash") or "").strip()
|
|
||||||
except Exception:
|
|
||||||
normalized_hash = ""
|
|
||||||
if normalized_hash:
|
|
||||||
effective_hostname = row.get("hostname") or hostname
|
|
||||||
return jsonify({
|
|
||||||
"agent_id": agent_id,
|
|
||||||
"agent_hash": normalized_hash,
|
|
||||||
"hostname": effective_hostname,
|
|
||||||
"source": "database",
|
|
||||||
})
|
|
||||||
first = rows[0]
|
|
||||||
fallback_hash = (first.get("agent_hash") or "").strip()
|
|
||||||
if not fallback_hash:
|
|
||||||
details = first.get("details") or {}
|
|
||||||
try:
|
|
||||||
fallback_hash = ((details.get("summary") or {}).get("agent_hash") or "").strip()
|
|
||||||
except Exception:
|
|
||||||
fallback_hash = ""
|
|
||||||
if fallback_hash:
|
|
||||||
effective_hostname = first.get("hostname") or hostname
|
|
||||||
return jsonify({
|
|
||||||
"agent_id": agent_id,
|
|
||||||
"agent_hash": fallback_hash,
|
|
||||||
"hostname": effective_hostname,
|
|
||||||
"source": "database",
|
|
||||||
})
|
|
||||||
|
|
||||||
# As a final fallback, scan the table for any matching agent_id in case hostname inference failed.
|
|
||||||
cur.execute("SELECT hostname, agent_hash, details FROM device_details")
|
|
||||||
for host, db_hash, details_json in cur.fetchall():
|
|
||||||
try:
|
|
||||||
data = json.loads(details_json or "{}")
|
|
||||||
except Exception:
|
|
||||||
data = {}
|
|
||||||
summary = data.get("summary") or {}
|
|
||||||
summary_agent_id = (summary.get("agent_id") or "").strip()
|
|
||||||
if summary_agent_id != agent_id:
|
|
||||||
continue
|
|
||||||
summary_hash = (summary.get("agent_hash") or "").strip()
|
|
||||||
normalized_hash = (db_hash or "").strip() or summary_hash
|
|
||||||
if normalized_hash:
|
|
||||||
effective_hostname = host or summary.get("hostname") or hostname
|
|
||||||
return jsonify({
|
|
||||||
"agent_id": agent_id,
|
|
||||||
"agent_hash": normalized_hash,
|
|
||||||
"hostname": effective_hostname,
|
|
||||||
"source": "database",
|
|
||||||
})
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return jsonify({"error": "agent hash not found"}), 404
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
|||||||
214
Update.ps1
214
Update.ps1
@@ -137,45 +137,40 @@ function Get-AgentServiceId {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-AgentUpdateCheck {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory = $true)]
|
|
||||||
[string]$ServerBaseUrl,
|
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
|
||||||
[string]$AgentId
|
|
||||||
)
|
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($ServerBaseUrl)) {
|
|
||||||
throw 'Server URL is blank; cannot perform update check.'
|
|
||||||
}
|
|
||||||
|
|
||||||
$base = $ServerBaseUrl.TrimEnd('/')
|
|
||||||
$uri = "$base/api/agent/update_check"
|
|
||||||
$payload = @{ agent_id = $AgentId } | ConvertTo-Json -Depth 3
|
|
||||||
$headers = @{ 'User-Agent' = 'borealis-agent-updater' }
|
|
||||||
|
|
||||||
$resp = Invoke-WebRequest -Uri $uri -Method Post -Headers $headers -Body $payload -ContentType 'application/json' -UseBasicParsing -ErrorAction Stop
|
|
||||||
$json = $resp.Content | ConvertFrom-Json
|
|
||||||
|
|
||||||
if ($resp.StatusCode -ne 200) {
|
|
||||||
$message = $json.error
|
|
||||||
if (-not $message) { $message = "HTTP $($resp.StatusCode)" }
|
|
||||||
throw "Borealis server responded with an error: $message"
|
|
||||||
}
|
|
||||||
|
|
||||||
return $json
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-RepositoryCommitHash {
|
function Get-RepositoryCommitHash {
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$ProjectRoot
|
[string]$ProjectRoot,
|
||||||
|
|
||||||
|
[string]$AgentRoot
|
||||||
)
|
)
|
||||||
|
|
||||||
$candidates = @($ProjectRoot)
|
$candidates = @()
|
||||||
$agentRootCandidate = Join-Path $ProjectRoot 'Agent\Borealis'
|
if ($ProjectRoot -and ($candidates -notcontains $ProjectRoot)) { $candidates += $ProjectRoot }
|
||||||
if (Test-Path $agentRootCandidate -PathType Container) { $candidates += $agentRootCandidate }
|
if ($AgentRoot -and ($candidates -notcontains $AgentRoot)) { $candidates += $AgentRoot }
|
||||||
|
if ($ProjectRoot) {
|
||||||
|
$agentRootCandidate = Join-Path $ProjectRoot 'Agent\Borealis'
|
||||||
|
if (Test-Path $agentRootCandidate -PathType Container -and ($candidates -notcontains $agentRootCandidate)) {
|
||||||
|
$candidates += $agentRootCandidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($root in $candidates) {
|
||||||
|
try {
|
||||||
|
$gitDir = Join-Path $root '.git'
|
||||||
|
$fetchHead = Join-Path $gitDir 'FETCH_HEAD'
|
||||||
|
if (-not (Test-Path $fetchHead -PathType Leaf)) { continue }
|
||||||
|
foreach ($line in Get-Content -Path $fetchHead -ErrorAction Stop) {
|
||||||
|
$trim = ($line).Trim()
|
||||||
|
if (-not $trim -or $trim.StartsWith('#')) { continue }
|
||||||
|
$split = $trim.Split(@("`t", ' '), [StringSplitOptions]::RemoveEmptyEntries)
|
||||||
|
if ($split.Count -gt 0) {
|
||||||
|
$candidate = $split[0].Trim()
|
||||||
|
if ($candidate) { return $candidate }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($root in $candidates) {
|
foreach ($root in $candidates) {
|
||||||
try {
|
try {
|
||||||
@@ -216,22 +211,14 @@ function Get-RepositoryCommitHash {
|
|||||||
if ($candidate) { return $candidate }
|
if ($candidate) { return $candidate }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$fetchHead = Join-Path $gitDir 'FETCH_HEAD'
|
|
||||||
if (Test-Path $fetchHead -PathType Leaf) {
|
|
||||||
foreach ($line in Get-Content -Path $fetchHead -ErrorAction Stop) {
|
|
||||||
$trim = ($line).Trim()
|
|
||||||
if (-not $trim -or $trim.StartsWith('#')) { continue }
|
|
||||||
$split = $trim.Split(@("`t", ' '), [StringSplitOptions]::RemoveEmptyEntries)
|
|
||||||
if ($split.Count -gt 0) {
|
|
||||||
$candidate = $split[0].Trim()
|
|
||||||
if ($candidate) { return $candidate }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($AgentRoot) {
|
||||||
|
$stored = Get-StoredAgentHash -AgentRoot $AgentRoot
|
||||||
|
if ($stored) { return $stored }
|
||||||
|
}
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +259,28 @@ function Set-StoredAgentHash {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-ServerRepositoryHash {
|
function Set-GitFetchHeadHash {
|
||||||
|
param(
|
||||||
|
[string]$ProjectRoot,
|
||||||
|
[string]$CommitHash,
|
||||||
|
[string]$BranchName = 'main'
|
||||||
|
)
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($ProjectRoot) -or [string]::IsNullOrWhiteSpace($CommitHash)) { return }
|
||||||
|
|
||||||
|
try {
|
||||||
|
$gitDir = Join-Path $ProjectRoot '.git'
|
||||||
|
if (-not (Test-Path $gitDir -PathType Container)) {
|
||||||
|
New-Item -ItemType Directory -Force -Path $gitDir | Out-Null
|
||||||
|
}
|
||||||
|
$fetchHead = Join-Path $gitDir 'FETCH_HEAD'
|
||||||
|
$branchSegment = if ([string]::IsNullOrWhiteSpace($BranchName)) { '' } else { "`tbranch '$BranchName'" }
|
||||||
|
$content = "{0}{1}" -f ($CommitHash.Trim()), $branchSegment
|
||||||
|
Set-Content -Path $fetchHead -Value $content -Encoding UTF8
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ServerCurrentRepoHash {
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$ServerBaseUrl
|
[string]$ServerBaseUrl
|
||||||
@@ -281,7 +289,7 @@ function Get-ServerRepositoryHash {
|
|||||||
if ([string]::IsNullOrWhiteSpace($ServerBaseUrl)) { return $null }
|
if ([string]::IsNullOrWhiteSpace($ServerBaseUrl)) { return $null }
|
||||||
|
|
||||||
$base = $ServerBaseUrl.TrimEnd('/')
|
$base = $ServerBaseUrl.TrimEnd('/')
|
||||||
$uri = "$base/api/agent/repo_hash"
|
$uri = "$base/api/repo/current_hash"
|
||||||
$headers = @{ 'User-Agent' = 'borealis-agent-updater' }
|
$headers = @{ 'User-Agent' = 'borealis-agent-updater' }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -310,7 +318,7 @@ function Submit-AgentHash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$base = $ServerBaseUrl.TrimEnd('/')
|
$base = $ServerBaseUrl.TrimEnd('/')
|
||||||
$uri = "$base/api/agent/agent_hash"
|
$uri = "$base/api/agent/hash"
|
||||||
$payload = @{ agent_id = $AgentId; agent_hash = $AgentHash } | ConvertTo-Json -Depth 3
|
$payload = @{ agent_id = $AgentId; agent_hash = $AgentHash } | ConvertTo-Json -Depth 3
|
||||||
$headers = @{ 'User-Agent' = 'borealis-agent-updater' }
|
$headers = @{ 'User-Agent' = 'borealis-agent-updater' }
|
||||||
|
|
||||||
@@ -325,15 +333,22 @@ function Submit-AgentHash {
|
|||||||
|
|
||||||
function Sync-AgentHashRecord {
|
function Sync-AgentHashRecord {
|
||||||
param(
|
param(
|
||||||
|
[string]$ProjectRoot,
|
||||||
[string]$AgentRoot,
|
[string]$AgentRoot,
|
||||||
[string]$AgentHash,
|
[string]$AgentHash,
|
||||||
[string]$ServerBaseUrl,
|
[string]$ServerBaseUrl,
|
||||||
[string]$AgentId
|
[string]$AgentId,
|
||||||
|
[string]$BranchName = 'main'
|
||||||
)
|
)
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($AgentHash)) { return }
|
if ([string]::IsNullOrWhiteSpace($AgentHash)) { return }
|
||||||
|
|
||||||
Set-StoredAgentHash -AgentRoot $AgentRoot -AgentHash $AgentHash
|
if ($ProjectRoot) {
|
||||||
|
Set-GitFetchHeadHash -ProjectRoot $ProjectRoot -CommitHash $AgentHash -BranchName $BranchName
|
||||||
|
}
|
||||||
|
if ($AgentRoot) {
|
||||||
|
Set-StoredAgentHash -AgentRoot $AgentRoot -AgentHash $AgentHash
|
||||||
|
}
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($ServerBaseUrl)) { return }
|
if ([string]::IsNullOrWhiteSpace($ServerBaseUrl)) { return }
|
||||||
|
|
||||||
@@ -426,48 +441,25 @@ function Invoke-BorealisAgentUpdate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$currentHash = Get-RepositoryCommitHash -ProjectRoot $scriptDir
|
$currentHash = Get-RepositoryCommitHash -ProjectRoot $scriptDir -AgentRoot $agentRoot
|
||||||
if ($currentHash) {
|
|
||||||
Set-StoredAgentHash -AgentRoot $agentRoot -AgentHash $currentHash
|
|
||||||
} else {
|
|
||||||
$storedHash = Get-StoredAgentHash -AgentRoot $agentRoot
|
|
||||||
if ($storedHash) { $currentHash = $storedHash }
|
|
||||||
}
|
|
||||||
|
|
||||||
$serverBaseUrl = Get-BorealisServerUrl -AgentRoot $agentRoot
|
$serverBaseUrl = Get-BorealisServerUrl -AgentRoot $agentRoot
|
||||||
$agentId = Get-AgentServiceId -AgentRoot $agentRoot
|
$agentId = Get-AgentServiceId -AgentRoot $agentRoot
|
||||||
|
|
||||||
$serverRepoInfo = Get-ServerRepositoryHash -ServerBaseUrl $serverBaseUrl
|
$serverRepoInfo = Get-ServerCurrentRepoHash -ServerBaseUrl $serverBaseUrl
|
||||||
$serverHash = ''
|
$serverHash = ''
|
||||||
|
$serverBranch = 'main'
|
||||||
if ($serverRepoInfo) {
|
if ($serverRepoInfo) {
|
||||||
try { $serverHash = (($serverRepoInfo.sha) -as [string]).Trim() } catch { $serverHash = '' }
|
try { $serverHash = (($serverRepoInfo.sha) -as [string]).Trim() } catch { $serverHash = '' }
|
||||||
|
try {
|
||||||
|
$branchCandidate = (($serverRepoInfo.branch) -as [string]).Trim()
|
||||||
|
if ($branchCandidate) { $serverBranch = $branchCandidate }
|
||||||
|
} catch { $serverBranch = 'main' }
|
||||||
}
|
}
|
||||||
|
|
||||||
$updateMode = $env:update_mode
|
$updateMode = $env:update_mode
|
||||||
if ($updateMode) { $updateMode = $updateMode.ToLowerInvariant() } else { $updateMode = 'update' }
|
if ($updateMode) { $updateMode = $updateMode.ToLowerInvariant() } else { $updateMode = 'update' }
|
||||||
$forceUpdate = $updateMode -eq 'force_update'
|
$forceUpdate = $updateMode -eq 'force_update'
|
||||||
|
|
||||||
$updateInfo = $null
|
|
||||||
$shouldUpdate = $forceUpdate
|
|
||||||
|
|
||||||
if (-not $forceUpdate) {
|
|
||||||
if (-not $agentId) {
|
|
||||||
Write-Host "Agent ID unavailable; cannot request update status from server." -ForegroundColor Yellow
|
|
||||||
Write-Host "⚠️ Borealis update aborted."
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$updateInfo = Invoke-AgentUpdateCheck -ServerBaseUrl $serverBaseUrl -AgentId $agentId
|
|
||||||
$shouldUpdate = [bool]($updateInfo.update_available)
|
|
||||||
if (-not $serverHash -and $updateInfo.repo_hash) { $serverHash = ($updateInfo.repo_hash).ToString().Trim() }
|
|
||||||
} catch {
|
|
||||||
Write-Host ("Failed to contact Borealis server for update status: {0}" -f $_.Exception.Message) -ForegroundColor Yellow
|
|
||||||
Write-Host "⚠️ Borealis update aborted."
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($currentHash) {
|
if ($currentHash) {
|
||||||
Write-Host ("Local Agent Hash: {0}" -f $currentHash)
|
Write-Host ("Local Agent Hash: {0}" -f $currentHash)
|
||||||
} else {
|
} else {
|
||||||
@@ -483,31 +475,21 @@ function Invoke-BorealisAgentUpdate {
|
|||||||
$normalizedLocalHash = if ($currentHash) { $currentHash.Trim().ToLowerInvariant() } else { '' }
|
$normalizedLocalHash = if ($currentHash) { $currentHash.Trim().ToLowerInvariant() } else { '' }
|
||||||
$normalizedServerHash = if ($serverHash) { $serverHash.Trim().ToLowerInvariant() } else { '' }
|
$normalizedServerHash = if ($serverHash) { $serverHash.Trim().ToLowerInvariant() } else { '' }
|
||||||
$hashesMatch = ($normalizedLocalHash -and $normalizedServerHash -and ($normalizedLocalHash -eq $normalizedServerHash))
|
$hashesMatch = ($normalizedLocalHash -and $normalizedServerHash -and ($normalizedLocalHash -eq $normalizedServerHash))
|
||||||
|
$needsUpdate = $forceUpdate -or (-not $hashesMatch)
|
||||||
|
|
||||||
if ($forceUpdate) {
|
if ($forceUpdate) {
|
||||||
Write-Host "Server update check bypassed (force update requested)."
|
Write-Host "Force update requested; skipping hash comparison." -ForegroundColor Yellow
|
||||||
} elseif ($updateInfo) {
|
} elseif (-not $serverHash) {
|
||||||
if ($shouldUpdate) {
|
Write-Host "Borealis server hash unavailable; cannot continue." -ForegroundColor Yellow
|
||||||
Write-Host "Server reports agent hash mismatch (update required)."
|
|
||||||
} else {
|
|
||||||
Write-Host "Server reports agent is current." -ForegroundColor Green
|
|
||||||
if ($serverHash) {
|
|
||||||
Set-StoredAgentHash -AgentRoot $agentRoot -AgentHash $serverHash
|
|
||||||
}
|
|
||||||
Write-Host "✅ Borealis - Automation Platform Already Up-to-Date"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-Host "Server response unavailable; cannot continue." -ForegroundColor Yellow
|
|
||||||
Write-Host "⚠️ Borealis update aborted."
|
Write-Host "⚠️ Borealis update aborted."
|
||||||
return
|
return
|
||||||
}
|
} elseif (-not $needsUpdate) {
|
||||||
|
Write-Host "Local agent files already match the server repository hash." -ForegroundColor Green
|
||||||
if (-not $forceUpdate -and $updateInfo -and $shouldUpdate -and $hashesMatch) {
|
Sync-AgentHashRecord -ProjectRoot $scriptDir -AgentRoot $agentRoot -AgentHash $serverHash -ServerBaseUrl $serverBaseUrl -AgentId $agentId -BranchName $serverBranch
|
||||||
Write-Host "Local agent files already match the server repository hash; skipping update while syncing server state." -ForegroundColor Green
|
|
||||||
Sync-AgentHashRecord -AgentRoot $agentRoot -AgentHash $serverHash -ServerBaseUrl $serverBaseUrl -AgentId $agentId
|
|
||||||
Write-Host "✅ Borealis - Automation Platform Already Up-to-Date"
|
Write-Host "✅ Borealis - Automation Platform Already Up-to-Date"
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
Write-Host "Repository hash mismatch detected; update required."
|
||||||
}
|
}
|
||||||
|
|
||||||
$mutex = $null
|
$mutex = $null
|
||||||
@@ -555,25 +537,25 @@ function Invoke-BorealisAgentUpdate {
|
|||||||
throw 'Borealis update failed.'
|
throw 'Borealis update failed.'
|
||||||
}
|
}
|
||||||
|
|
||||||
$newHash = Get-RepositoryCommitHash -ProjectRoot $scriptDir
|
$postUpdateInfo = Get-ServerCurrentRepoHash -ServerBaseUrl $serverBaseUrl
|
||||||
if (-not $newHash -and $updateInfo -and $updateInfo.repo_hash) {
|
if ($postUpdateInfo) {
|
||||||
$newHash = ($updateInfo.repo_hash).ToString().Trim()
|
|
||||||
}
|
|
||||||
if (-not $newHash -and $agentId) {
|
|
||||||
try {
|
try {
|
||||||
$postUpdateInfo = Invoke-AgentUpdateCheck -ServerBaseUrl $serverBaseUrl -AgentId $agentId
|
$refreshedSha = (($postUpdateInfo.sha) -as [string]).Trim()
|
||||||
if ($postUpdateInfo -and $postUpdateInfo.repo_hash) {
|
if ($refreshedSha) { $serverHash = $refreshedSha }
|
||||||
$newHash = ($postUpdateInfo.repo_hash).ToString().Trim()
|
} catch {}
|
||||||
}
|
try {
|
||||||
} catch {
|
$branchCandidate = (($postUpdateInfo.branch) -as [string]).Trim()
|
||||||
Write-Verbose ("Post-update hash retrieval failed: {0}" -f $_.Exception.Message)
|
if ($branchCandidate) { $serverBranch = $branchCandidate }
|
||||||
}
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
$newHash = Get-RepositoryCommitHash -ProjectRoot $scriptDir -AgentRoot $agentRoot
|
||||||
|
if (-not $newHash -and $serverHash) {
|
||||||
|
$newHash = $serverHash
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($newHash) {
|
if ($newHash) {
|
||||||
Sync-AgentHashRecord -AgentRoot $agentRoot -AgentHash $newHash -ServerBaseUrl $serverBaseUrl -AgentId $agentId
|
Sync-AgentHashRecord -ProjectRoot $scriptDir -AgentRoot $agentRoot -AgentHash $newHash -ServerBaseUrl $serverBaseUrl -AgentId $agentId -BranchName $serverBranch
|
||||||
} elseif ($serverHash) {
|
|
||||||
Sync-AgentHashRecord -AgentRoot $agentRoot -AgentHash $serverHash -ServerBaseUrl $serverBaseUrl -AgentId $agentId
|
|
||||||
} else {
|
} else {
|
||||||
Write-Host "Unable to determine repository hash for submission; server hash not updated." -ForegroundColor DarkYellow
|
Write-Host "Unable to determine repository hash for submission; server hash not updated." -ForegroundColor DarkYellow
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user