import type { IBalenaDeviceState, IBalenaStateStatus } from './types.ts'; export interface ISupervisorClientOptions { address?: string; apiKey?: string; } export class SupervisorClient { public readonly address?: string; private readonly apiKey?: string; constructor(optionsArg: ISupervisorClientOptions = {}) { this.address = optionsArg.address || Deno.env.get('BALENA_SUPERVISOR_ADDRESS') || undefined; this.apiKey = optionsArg.apiKey || Deno.env.get('BALENA_SUPERVISOR_API_KEY') || undefined; } public isConfigured() { return Boolean(this.address); } public async isAvailable() { if (!this.address) { return false; } try { const response = await fetch(this.buildUrl('/ping', false)); const text = await response.text(); return response.ok && text.trim() === 'OK'; } catch { return false; } } public async getDeviceState(): Promise { return await this.requestJson('/v1/device'); } public async getStateStatus(): Promise { return await this.requestJson('/v2/state/status'); } public async getSupervisorVersion(): Promise { const response = await this.requestJson<{ version?: string }>('/v2/version'); return response.version; } public async triggerUpdate(optionsArg: { force?: boolean; cancel?: boolean } = {}) { await this.requestEmpty('/v1/update', { method: 'POST', body: JSON.stringify({ force: Boolean(optionsArg.force), cancel: Boolean(optionsArg.cancel), }), headers: { 'content-type': 'application/json', }, }); } public async reboot(optionsArg: { force?: boolean } = {}) { await this.requestEmpty('/v1/reboot', { method: 'POST', body: JSON.stringify({ force: Boolean(optionsArg.force), }), headers: { 'content-type': 'application/json', }, }); } private async requestJson( pathArg: string, initArg: RequestInit = {}, ): Promise { const response = await fetch(this.buildUrl(pathArg, true), initArg); if (!response.ok) { throw new Error(`Supervisor request failed: ${pathArg} -> HTTP ${response.status}`); } return await response.json() as TResponse; } private async requestEmpty(pathArg: string, initArg: RequestInit = {}) { const response = await fetch(this.buildUrl(pathArg, true), initArg); if (!response.ok) { throw new Error(`Supervisor request failed: ${pathArg} -> HTTP ${response.status}`); } } private buildUrl(pathArg: string, includeApiKeyArg: boolean) { if (!this.address) { throw new Error('Balena Supervisor address is not configured'); } const url = new URL(pathArg, this.address.endsWith('/') ? this.address : `${this.address}/`); if (includeApiKeyArg) { if (!this.apiKey) { throw new Error('Balena Supervisor API key is not configured'); } url.searchParams.set('apikey', this.apiKey); } return url; } }