Add automatic agent updates with verified rollback

This commit is contained in:
2026-10-04 10:40:45 -04:00
parent 020894b816
commit 8d1331c67d
26 changed files with 2568 additions and 110 deletions
+16
View File
@@ -208,6 +208,22 @@ export function validateInventory(value: unknown): HostInventory {
for (const key of ['dockerRunning','directoriesWritable']) if (inventory[key] !== undefined && typeof inventory[key] !== 'boolean') throw new Error(`Invalid inventory ${key}.`);
if (inventory.runtimeImages !== undefined && (!Array.isArray(inventory.runtimeImages) || inventory.runtimeImages.some(image => typeof image !== 'string'))) throw new Error('Invalid runtime image inventory.');
if (inventory.capabilities !== undefined && (!Array.isArray(inventory.capabilities) || inventory.capabilities.length > 32 || inventory.capabilities.some(value => typeof value !== 'string' || value.length > 80))) throw new Error('Invalid agent capability inventory.');
if (inventory.agentUpdate !== undefined) {
const update = inventory.agentUpdate;
const version = (value: unknown) => typeof value === 'string' && value.length <= 80 && /^\d+\.\d+\.\d+$/.test(value);
if (!update || typeof update !== 'object' || Array.isArray(update) || typeof update.enabled !== 'boolean'
|| !['current','checking','deferred','updating','updated','failed'].includes(update.state)
|| !version(update.currentVersion) || (update.targetVersion !== undefined && !version(update.targetVersion))
|| (update.error !== undefined && (typeof update.error !== 'string' || update.error.length > 2000))
|| (update.checkedAt !== undefined && (typeof update.checkedAt !== 'string' || update.checkedAt.length > 80 || !Number.isFinite(Date.parse(update.checkedAt))))) {
throw new Error('Invalid agent update inventory.');
}
// Only expose display fields, never the local recovery journal or secrets.
inventory.agentUpdate = { enabled: update.enabled, state: update.state, currentVersion: update.currentVersion,
...(update.targetVersion === undefined ? {} : { targetVersion: update.targetVersion }),
...(update.error === undefined ? {} : { error: update.error }),
...(update.checkedAt === undefined ? {} : { checkedAt: update.checkedAt }) };
}
if (inventory.libraries !== undefined && (!Array.isArray(inventory.libraries) || inventory.libraries.length > 2000 || inventory.libraries.some(library =>
!library || typeof library !== 'object' || typeof library.id !== 'string' || !library.id || library.id.length > 200 || !['missing','downloading','verifying','ready','failed','outdated'].includes(library.state)))) throw new Error('Invalid game library inventory.');
return inventory;