// src/services/platform.ts // Copyright (C) 2026 Rob Colbert // Licensed under the Apache License, Version 2.0 import env from "../config/env.ts"; import assert from "node:assert"; import path from "node:path"; import os from "node:os"; import { GadgetService } from "../lib/service.ts"; import { DroneStatus, IChatSession, IChatTurn, IDroneRegistration, IUser, Types, } from "@gadget/api"; interface PlatformApiResponse { success: boolean; message?: string; } interface PlatformRegistrationResponse extends PlatformApiResponse { data: IDroneRegistration; } interface ChatSessionContextResponse extends PlatformApiResponse { data: IChatTurn[]; } class PlatformService extends GadgetService { registration: IDroneRegistration | undefined; get name(): string { return "PlatformService"; } get slug(): string { return "svc:platform"; } async start(): Promise { this.log.info("started"); } async stop(): Promise { this.log.info("stopped"); } async register( email: string, password: string, workspaceDir: string, workspaceId: string, ): Promise { const url = this.getApiUrl("/drone/registration"); this.log.info("registering with Gadget Code Platform", { workspaceId, email, url, }); const body = JSON.stringify({ email, password, hostname: os.hostname(), workspaceDir, workspaceId, }); const response = await fetch(url, { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", "Content-Length": body.length.toString(), "X-Gadget-Key": env.platform.gadgetKey, }, body, }); const json = (await response.json()) as PlatformRegistrationResponse; this.log.debug("platform registration response", json); if (!json.success) { const error = new Error("failed to register drone with Platform"); error.name = "PlatformError"; error.statusCode = response.status; throw error; } if (!json.data || !json.data._id) { const error = new Error( "registration response did not contain required data", ); error.name = "PlatformError"; throw error; } if (!json.data.user || !(json.data.user as IUser)._id) { const error = new Error( "registration response did not contain required user account data", ); error.name = "PlatformError"; throw error; } this.registration = json.data; return json.data; } async unregister(): Promise { if (!this.registration) { return; } const url = this.getApiUrl(`/drone/registration/${this.registration._id}`); const body = JSON.stringify({ registration: this.registration }); const response = await fetch(url, { method: "DELETE", headers: { Accept: "application/json", "Content-Type": "application/json", "Content-Length": body.length.toString(), "X-Gadget-Key": env.platform.gadgetKey, }, body, }); const json = (await response.json()) as PlatformRegistrationResponse; if (!json.success) { const error = new Error("failed to register drone with Platform"); error.name = "PlatformError"; error.statusCode = response.status; throw error; } } async setStatus(status: DroneStatus): Promise { assert( this.registration, "must register with platform before setting status", ); const url = this.getApiUrl( `/drone/registration/${this.registration._id}/status`, ); const body = JSON.stringify({ status }); const response = await fetch(url, { method: "PUT", headers: { Accept: "application/json", "Content-Type": "application/json", "Content-Length": body.length.toString(), "X-Gadget-Key": env.platform.gadgetKey, }, body, }); const json = (await response.json()) as PlatformRegistrationResponse; if (!json.success) { const error = new Error("failed to register drone with Platform"); error.name = "PlatformError"; error.statusCode = response.status; throw error; } this.log.info("drone status updated on platform", { status }); } async getChatSessionContext( session: IChatSession, ): Promise { assert( this.registration, "must register with platform before setting status", ); const url = this.getApiUrl(`/chat-sessions/${session._id}/turns`); const response = await fetch(url, { method: "GET", headers: { Accept: "application/json", "X-Gadget-Key": env.platform.gadgetKey, }, }); const json = (await response.json()) as ChatSessionContextResponse; if (!json.success) { const error = new Error("failed to retrieve chat session context"); error.name = "PlatformError"; error.statusCode = response.status; throw error; } this.log.info("chat session context received", { turnCount: json.data.length, }); return json; } getApiUrl(url: string): string { return `${env.platform.baseUrl}/api/v1${url}`; } } export default new PlatformService();