173 lines
4.3 KiB
TypeScript
173 lines
4.3 KiB
TypeScript
// src/gadget-drone.ts
|
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
import env from "./config/env.ts";
|
|
import assert from "node:assert";
|
|
|
|
import { io, ManagerOptions, SocketOptions, Socket } from "socket.io-client";
|
|
|
|
import AgentService, { IAgentWorkOrder } from "./services/agent.ts";
|
|
import AiService from "./services/ai.ts";
|
|
import PlatformService, {
|
|
DroneStatus,
|
|
PlatformRegistration,
|
|
} from "./services/platform.ts";
|
|
|
|
import { GadgetProcess } from "./lib/process.ts";
|
|
|
|
class GadgetDrone extends GadgetProcess {
|
|
private registration: PlatformRegistration | undefined;
|
|
private socket: Socket | undefined;
|
|
private isShuttingDown: boolean = false;
|
|
|
|
get name(): string {
|
|
return "GadgetDrone";
|
|
}
|
|
|
|
get slug(): string {
|
|
return "gadget-drone";
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
}
|
|
|
|
async start(): Promise<void> {
|
|
/*
|
|
* Initialize the system
|
|
*/
|
|
|
|
this.hookProcessSignals();
|
|
await this.startServices();
|
|
|
|
/*
|
|
* Register this Drone with the Gadget Code web services platform.
|
|
*/
|
|
|
|
const email = "rob@digitaltelepresence.com";
|
|
const password = "ionfrali";
|
|
this.registration = await PlatformService.register(email, password);
|
|
this.log.info("registered with platform", {
|
|
registration: this.registration,
|
|
});
|
|
|
|
/*
|
|
* Connect to the Gadget Code web services platform and configure the real-
|
|
* time messaging system on Socket.IO.
|
|
*/
|
|
await this.connectSocket();
|
|
|
|
/*
|
|
* Mark this Drone as available and ready to accept work orders.
|
|
*/
|
|
|
|
await PlatformService.setStatus(DroneStatus.Available);
|
|
this.log.info(`Gadget Drone v${env.pkg.version} started`);
|
|
}
|
|
|
|
async stop(): Promise<number> {
|
|
this.log.info(`Gadget Drone v${env.pkg.version} shutting down`);
|
|
|
|
if (this.socket) {
|
|
this.socket.disconnect();
|
|
delete this.socket;
|
|
}
|
|
|
|
await PlatformService.unregister();
|
|
await this.stopServices();
|
|
|
|
return 0;
|
|
}
|
|
|
|
async startServices(): Promise<void> {
|
|
this.log.info("starting services");
|
|
|
|
await AgentService.start();
|
|
await AiService.start();
|
|
await PlatformService.start();
|
|
|
|
this.log.info("services started");
|
|
}
|
|
|
|
async stopServices(): Promise<void> {
|
|
this.log.info("stopping services");
|
|
|
|
await AgentService.stop();
|
|
await AiService.stop();
|
|
await PlatformService.stop();
|
|
|
|
this.log.info("services stopped");
|
|
}
|
|
|
|
async connectSocket(): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
assert(this.registration, "must be registered with Gadget Code platform");
|
|
const options: Partial<ManagerOptions & SocketOptions> = {
|
|
auth: { token: this.registration._id },
|
|
reconnectionAttempts: 10,
|
|
timeout: 5000,
|
|
transports: ["websocket"],
|
|
};
|
|
|
|
/*
|
|
* Allow self-signed certs in non-production environments
|
|
*/
|
|
if (env.NODE_ENV !== "production") {
|
|
options.rejectUnauthorized = false;
|
|
}
|
|
|
|
this.log.debug("connecting to Gadget Code platform...");
|
|
this.socket = io("https://code-dev.g4dge7.com:5174/", options);
|
|
this.socket.on("connect_error", (err) => {
|
|
this.log.error("socket connect error", { err });
|
|
reject(err);
|
|
});
|
|
this.socket.on("connect", () => {
|
|
this.log.info("connected to Gadget Code platform.");
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
hookProcessSignals(): void {
|
|
process.title = this.name;
|
|
|
|
process.on("unhandledRejection", async (error: Error, p) => {
|
|
this.log.error("Unhandled rejection", {
|
|
error,
|
|
promise: p,
|
|
stack: error.stack,
|
|
});
|
|
|
|
const exitCode = await this.stop();
|
|
process.exit(exitCode);
|
|
});
|
|
|
|
process.on("warning", (error) => {
|
|
if (error.name === "DeprecationWarning") return;
|
|
this.log.alert("warning", { error });
|
|
});
|
|
|
|
process.on("SIGINT", async () => {
|
|
this.log.info("SIGINT received");
|
|
if (this.isShuttingDown) return;
|
|
|
|
this.log.info("requesting shutdown");
|
|
const exitCode = await this.stop();
|
|
|
|
process.exit(exitCode);
|
|
});
|
|
}
|
|
}
|
|
|
|
(async () => {
|
|
try {
|
|
const drone = new GadgetDrone();
|
|
await drone.start();
|
|
} catch (error) {
|
|
console.error("failed to start gadget-drone", error);
|
|
process.exit(-1);
|
|
}
|
|
})();
|