initial platform gateway service
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
import { config } from '../config'
|
||||
import type { Platform } from '../types'
|
||||
|
||||
export class AgentClient {
|
||||
async health(platform: Platform): Promise<{ ok: boolean; message?: string }> {
|
||||
if (!platform.agentUrl) return { ok: false, message: 'platform_agent_url_missing' }
|
||||
|
||||
const controller = new AbortController()
|
||||
const timeout = setTimeout(() => controller.abort(), Math.min(config.agentRequestTimeoutMs, 3000))
|
||||
|
||||
try {
|
||||
const response = await fetch(`${platform.agentUrl.replace(/\/+$/, '')}/health`, {
|
||||
headers: this.agentHeaders(),
|
||||
signal: controller.signal
|
||||
})
|
||||
if (!response.ok) return { ok: false, message: `agent_health_failed:${response.status}` }
|
||||
const payload = await response.json().catch(() => ({ ok: true }))
|
||||
return { ok: payload?.ok !== false }
|
||||
} catch (error) {
|
||||
return { ok: false, message: error instanceof Error ? error.message : String(error) }
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
async visualizationStart(platform: Platform, input: {
|
||||
stack?: string;
|
||||
rosDomainId?: string;
|
||||
profile?: unknown;
|
||||
}): Promise<{ ok: boolean; running: boolean; stack: string | null; message?: string }> {
|
||||
return this.agentJson(platform, '/visualization/start', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
|
||||
async visualizationStop(platform: Platform, input: {
|
||||
stack?: string;
|
||||
profile?: unknown;
|
||||
} = {}): Promise<{ ok: boolean; running: boolean; stack: string | null; message?: string }> {
|
||||
return this.agentJson(platform, '/visualization/stop', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
|
||||
async visualizationStatus(platform: Platform): Promise<{ ok: boolean; running: boolean; stack: string | null; message?: string }> {
|
||||
return this.agentJson(platform, '/visualization/status');
|
||||
}
|
||||
|
||||
async moduleStatuses(platform: Platform): Promise<unknown> {
|
||||
return this.agentJson(platform, '/api/modules/docker-statuses');
|
||||
}
|
||||
|
||||
async moduleStart(platform: Platform, input: { folderName?: string; rosDomainId?: string }): Promise<unknown> {
|
||||
return this.agentJson(platform, '/api/modules/start', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
|
||||
async moduleStop(platform: Platform, input: { containerKey?: string }): Promise<unknown> {
|
||||
return this.agentJson(platform, '/api/modules/stop', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
|
||||
async syncAuthorizedKeys(platform: Platform, publicKeys: string[], sshUser?: string | null): Promise<unknown> {
|
||||
return this.agentJson(platform, '/api/ssh/authorized-keys', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ publicKeys, sshUser }),
|
||||
});
|
||||
}
|
||||
|
||||
async runCommand(platform: Platform, command: string, args: string[]): Promise<{ commandId: string; accepted: boolean }> {
|
||||
if (!platform.agentUrl) return { commandId: crypto.randomUUID(), accepted: false }
|
||||
|
||||
const controller = new AbortController()
|
||||
const timeout = setTimeout(() => controller.abort(), config.agentRequestTimeoutMs)
|
||||
|
||||
try {
|
||||
const response = await fetch(`${platform.agentUrl}/commands`, {
|
||||
method: 'POST',
|
||||
headers: this.agentHeaders({ 'content-type': 'application/json' }),
|
||||
body: JSON.stringify({ command, args }),
|
||||
signal: controller.signal
|
||||
})
|
||||
if (!response.ok) throw new Error(`agent_command_failed:${response.status}`)
|
||||
const payload = await response.json().catch(() => ({}))
|
||||
return { commandId: String(payload.commandId ?? crypto.randomUUID()), accepted: true }
|
||||
} catch {
|
||||
return { commandId: crypto.randomUUID(), accepted: false }
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
async emergencyStop(platform: Platform): Promise<{ accepted: boolean }> {
|
||||
const controller = new AbortController()
|
||||
const timeout = setTimeout(() => controller.abort(), config.agentRequestTimeoutMs)
|
||||
|
||||
try {
|
||||
const response = await fetch(`${platform.agentUrl}/emergency-stop`, {
|
||||
method: 'POST',
|
||||
headers: this.agentHeaders(),
|
||||
signal: controller.signal
|
||||
})
|
||||
return { accepted: response.ok }
|
||||
} catch {
|
||||
return { accepted: false }
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
private async agentJson<T>(platform: Platform, path: string, init?: RequestInit): Promise<T> {
|
||||
if (!platform.agentUrl) throw new Error('platform_agent_url_missing')
|
||||
|
||||
const controller = new AbortController()
|
||||
const timeout = setTimeout(() => controller.abort(), config.agentRequestTimeoutMs)
|
||||
|
||||
try {
|
||||
const response = await fetch(`${platform.agentUrl.replace(/\/+$/, '')}${path}`, {
|
||||
...init,
|
||||
headers: this.agentHeaders(init?.headers),
|
||||
signal: controller.signal,
|
||||
})
|
||||
const payload = await response.json().catch(() => ({}))
|
||||
if (!response.ok) throw new Error(String(payload?.message ?? payload?.error ?? `agent_request_failed:${response.status}`))
|
||||
return payload as T
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
private agentHeaders(headers?: HeadersInit): HeadersInit {
|
||||
const next = new Headers(headers)
|
||||
if (config.agentGatewayToken) {
|
||||
next.set('x-agent-gateway-token', config.agentGatewayToken)
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
export const agentClient = new AgentClient()
|
||||
Reference in New Issue
Block a user