diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index 23be7071f..917bfff83 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -3,6 +3,7 @@ import { DockerProcedureContainer } from "./DockerProcedureContainer" import { SystemForEmbassy } from "." import { HostSystemStartOs } from "../../HostSystemStartOs" import { Daemons, T, daemons } from "@start9labs/start-sdk" +import { Daemon } from "@start9labs/start-sdk/cjs/lib/mainFn/Daemon" const EMBASSY_HEALTH_INTERVAL = 15 * 1000 const EMBASSY_PROPERTIES_LOOP = 30 * 1000 @@ -21,8 +22,7 @@ export class MainLoop { private mainEvent: | Promise<{ - daemon: T.DaemonReturned - wait: Promise + daemon: Daemon }> | undefined constructor( @@ -51,7 +51,7 @@ export class MainLoop { if (jsMain) { throw new Error("Unreachable") } - const daemon = await daemons.runDaemon()( + const daemon = await Daemon.of()( this.effects, { id: this.system.manifest.main.image }, currentCommand, @@ -59,14 +59,9 @@ export class MainLoop { overlay: dockerProcedureContainer.overlay, }, ) + daemon.start() return { daemon, - wait: daemon.wait().finally(() => { - this.clean() - effects - .setMainStatus({ status: "stopped" }) - .catch((e) => console.error("Could not set the status to stopped")) - }), } } @@ -121,7 +116,8 @@ export class MainLoop { const main = await mainEvent delete this.mainEvent delete this.healthLoops - if (mainEvent) await main?.daemon.term() + await main?.daemon.stop().catch((e) => console.error(e)) + this.effects.setMainStatus({ status: "stopped" }) if (healthLoops) healthLoops.forEach((x) => clearInterval(x.interval)) } diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index 8988c5446..ada893b2b 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -385,13 +385,15 @@ export class SystemForEmbassy implements System { timeoutMs: number | null, ): Promise { const { currentRunning } = this + this.currentRunning?.clean() delete this.currentRunning if (currentRunning) { await currentRunning.clean({ timeout: this.manifest.main["sigterm-timeout"], }) } - return duration(this.manifest.main["sigterm-timeout"], "s") + const durationValue = duration(this.manifest.main["sigterm-timeout"], "s") + return durationValue } private async createBackup( effects: HostSystemStartOs, diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts index 30e572d97..12c0a58d5 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts @@ -116,7 +116,7 @@ export class PolyfillEffects implements oet.Effects { this.manifest.volumes, ) const daemon = dockerProcedureContainer.then((dockerProcedureContainer) => - daemons.runDaemon()( + daemons.runCommand()( this.effects, { id: this.manifest.main.image }, [input.command, ...(input.args || [])], diff --git a/container-runtime/src/Models/Duration.ts b/container-runtime/src/Models/Duration.ts index 8c701a703..75154c782 100644 --- a/container-runtime/src/Models/Duration.ts +++ b/container-runtime/src/Models/Duration.ts @@ -2,5 +2,5 @@ export type TimeUnit = "d" | "h" | "s" | "ms" export type Duration = `${number}${TimeUnit}` export function duration(timeValue: number, timeUnit: TimeUnit = "s") { - return `${timeValue}${timeUnit}` as Duration + return `${timeValue > 0 ? timeValue : 0}${timeUnit}` as Duration } diff --git a/container-runtime/src/Models/Effects.ts b/container-runtime/src/Models/Effects.ts index 757d51238..bacf8894a 100644 --- a/container-runtime/src/Models/Effects.ts +++ b/container-runtime/src/Models/Effects.ts @@ -1,5 +1,3 @@ import { types as T } from "@start9labs/start-sdk" -export type Effects = T.Effects & { - setMainStatus(o: { status: "running" | "stopped" }): Promise -} +export type Effects = T.Effects diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index 0bd31dc9b..44d17da30 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -581,10 +581,28 @@ struct GetHostInfoParams { callback: Callback, } async fn get_host_info( - _: EffectContext, + ctx: EffectContext, GetHostInfoParams { .. }: GetHostInfoParams, ) -> Result { - todo!() + let ctx = ctx.deref()?; + Ok(json!({ + "id": "fakeId1", + "kind": "multi", + "hostnames": [{ + "kind": "ip", + "networkInterfaceId": "fakeNetworkInterfaceId1", + "public": true, + "hostname":{ + "kind": "domain", + "domain": format!("{}", ctx.id), + "subdomain": (), + "port": (), + "sslPort": () + } + } + + ] + })) } async fn clear_bindings(context: EffectContext, _: Empty) -> Result { @@ -1011,21 +1029,23 @@ async fn set_configured(context: EffectContext, params: SetConfigured) -> Result #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] -enum Status { +enum SetMainStatusStatus { Running, Stopped, + Starting, } -impl FromStr for Status { +impl FromStr for SetMainStatusStatus { type Err = color_eyre::eyre::Report; fn from_str(s: &str) -> Result { match s { "running" => Ok(Self::Running), "stopped" => Ok(Self::Stopped), + "starting" => Ok(Self::Starting), _ => Err(eyre!("unknown status {s}")), } } } -impl ValueParserFactory for Status { +impl ValueParserFactory for SetMainStatusStatus { type Parser = FromStrParser; fn value_parser() -> Self::Parser { FromStrParser::new() @@ -1037,14 +1057,15 @@ impl ValueParserFactory for Status { #[command(rename_all = "camelCase")] #[ts(export)] struct SetMainStatus { - status: Status, + status: SetMainStatusStatus, } async fn set_main_status(context: EffectContext, params: SetMainStatus) -> Result { dbg!(format!("Status for main will be is {params:?}")); let context = context.deref()?; match params.status { - Status::Running => context.started(), - Status::Stopped => context.stopped(), + SetMainStatusStatus::Running => context.started(), + SetMainStatusStatus::Stopped => context.stopped(), + SetMainStatusStatus::Starting => context.stopped(), } Ok(Value::Null) } diff --git a/sdk/lib/mainFn/CommandController.ts b/sdk/lib/mainFn/CommandController.ts new file mode 100644 index 000000000..e2e11dbfc --- /dev/null +++ b/sdk/lib/mainFn/CommandController.ts @@ -0,0 +1,108 @@ +import { NO_TIMEOUT, SIGTERM } from "../StartSdk" +import { SDKManifest } from "../manifest/ManifestTypes" +import { Effects, ValidIfNoStupidEscape } from "../types" +import { MountOptions, Overlay } from "../util/Overlay" +import { splitCommand } from "../util/splitCommand" +import { cpExecFile } from "./Daemons" + +export class CommandController { + private constructor( + readonly runningAnswer: Promise, + readonly overlay: Overlay, + readonly pid: number | undefined, + ) {} + static of() { + return async ( + effects: Effects, + imageId: { + id: Manifest["images"][number] + sharedRun?: boolean + }, + command: ValidIfNoStupidEscape | [string, ...string[]], + options: { + mounts?: { path: string; options: MountOptions }[] + overlay?: Overlay + env?: + | { + [variable: string]: string + } + | undefined + cwd?: string | undefined + user?: string | undefined + onStdout?: (x: Buffer) => void + onStderr?: (x: Buffer) => void + }, + ) => { + const commands = splitCommand(command) + const overlay = options.overlay || (await Overlay.of(effects, imageId)) + for (let mount of options.mounts || []) { + await overlay.mount(mount.options, mount.path) + } + const childProcess = await overlay.spawn(commands, { + env: options.env, + }) + const answer = new Promise((resolve, reject) => { + childProcess.stdout.on( + "data", + options.onStdout ?? + ((data: any) => { + console.log(data.toString()) + }), + ) + childProcess.stderr.on( + "data", + options.onStderr ?? + ((data: any) => { + console.error(data.toString()) + }), + ) + + childProcess.on("exit", (code: any) => { + if (code === 0) { + return resolve(null) + } + return reject(new Error(`${commands[0]} exited with code ${code}`)) + }) + }) + + const pid = childProcess.pid + + return new CommandController(answer, overlay, pid) + } + } + async wait() { + try { + return await this.runningAnswer + } finally { + await cpExecFile("pkill", ["-9", "-s", String(this.pid)]).catch((_) => {}) + await this.overlay.destroy().catch((_) => {}) + } + } + async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) { + try { + await cpExecFile("pkill", [ + `-${signal.replace("SIG", "")}`, + "-s", + String(this.pid), + ]) + + if (timeout > NO_TIMEOUT) { + const didTimeout = await Promise.race([ + new Promise((resolve) => setTimeout(resolve, timeout)).then( + () => true, + ), + this.runningAnswer.then(() => false), + ]) + if (didTimeout) { + await cpExecFile("pkill", [`-9`, "-s", String(this.pid)]).catch( + (_: any) => {}, + ) + } + } else { + await this.runningAnswer + } + } finally { + await this.overlay.destroy() + } + } +} diff --git a/sdk/lib/mainFn/Daemon.ts b/sdk/lib/mainFn/Daemon.ts new file mode 100644 index 000000000..2ec438801 --- /dev/null +++ b/sdk/lib/mainFn/Daemon.ts @@ -0,0 +1,79 @@ +import { SDKManifest } from "../manifest/ManifestTypes" +import { Effects, ValidIfNoStupidEscape } from "../types" +import { MountOptions, Overlay } from "../util/Overlay" +import { CommandController } from "./CommandController" + +const TIMEOUT_INCREMENT_MS = 1000 +const MAX_TIMEOUT_MS = 30000 +/** + * This is a wrapper around CommandController that has a state of off, where the command shouldn't be running + * and the others state of running, where it will keep a living running command + */ + +export class Daemon { + private commandController: CommandController | null = null + private shouldBeRunning = false + private constructor(private startCommand: () => Promise) {} + static of() { + return async ( + effects: Effects, + imageId: { + id: Manifest["images"][number] + sharedRun?: boolean + }, + command: ValidIfNoStupidEscape | [string, ...string[]], + options: { + mounts?: { path: string; options: MountOptions }[] + overlay?: Overlay + env?: + | { + [variable: string]: string + } + | undefined + cwd?: string | undefined + user?: string | undefined + onStdout?: (x: Buffer) => void + onStderr?: (x: Buffer) => void + }, + ) => { + const startCommand = () => + CommandController.of()(effects, imageId, command, options) + return new Daemon(startCommand) + } + } + + async start() { + if (this.commandController) { + return + } + this.shouldBeRunning = true + let timeoutCounter = 0 + new Promise(async () => { + while (this.shouldBeRunning) { + this.commandController = await this.startCommand() + await this.commandController.wait().catch((err) => console.error(err)) + await new Promise((resolve) => setTimeout(resolve, timeoutCounter)) + timeoutCounter += TIMEOUT_INCREMENT_MS + timeoutCounter = Math.max(MAX_TIMEOUT_MS, timeoutCounter) + } + }).catch((err) => { + console.error(err) + }) + } + async term(termOptions?: { + signal?: NodeJS.Signals | undefined + timeout?: number | undefined + }) { + return this.stop(termOptions) + } + async stop(termOptions?: { + signal?: NodeJS.Signals | undefined + timeout?: number | undefined + }) { + this.shouldBeRunning = false + await this.commandController + ?.term(termOptions) + .catch((e) => console.error(e)) + this.commandController = null + } +} diff --git a/sdk/lib/mainFn/Daemons.ts b/sdk/lib/mainFn/Daemons.ts index efa1a2484..5771136c9 100644 --- a/sdk/lib/mainFn/Daemons.ts +++ b/sdk/lib/mainFn/Daemons.ts @@ -13,98 +13,38 @@ import { splitCommand } from "../util/splitCommand" import { promisify } from "node:util" import * as CP from "node:child_process" +export { Daemon } from "./Daemon" +export { CommandController } from "./CommandController" +import { HealthDaemon } from "./HealthDaemon" +import { Daemon } from "./Daemon" +import { CommandController } from "./CommandController" + const cpExec = promisify(CP.exec) -const cpExecFile = promisify(CP.execFile) -type Daemon< +export const cpExecFile = promisify(CP.execFile) +export type Ready = { + display: string | null + fn: () => Promise | CheckResult + trigger?: Trigger +} + +type DaemonsParams< Manifest extends SDKManifest, Ids extends string, Command extends string, Id extends string, > = { - id: "" extends Id ? never : Id command: ValidIfNoStupidEscape | [string, ...string[]] image: { id: Manifest["images"][number]; sharedRun?: boolean } - mounts: Mounts + mounts: { path: string; options: MountOptions }[] env?: Record - ready: { - display: string | null - fn: () => Promise | CheckResult - trigger?: Trigger - } + ready: Ready requires: Exclude[] } type ErrorDuplicateId = `The id '${Id}' is already used` -export const runDaemon = - () => - async ( - effects: Effects, - image: { id: Manifest["images"][number]; sharedRun?: boolean }, - command: ValidIfNoStupidEscape | [string, ...string[]], - options: CommandOptions & { - mounts?: { path: string; options: MountOptions }[] - overlay?: Overlay - }, - ): Promise => { - const commands = splitCommand(command) - const overlay = options.overlay || (await Overlay.of(effects, image)) - for (let mount of options.mounts || []) { - await overlay.mount(mount.options, mount.path) - } - const childProcess = await overlay.spawn(commands, { - env: options.env, - }) - const answer = new Promise((resolve, reject) => { - childProcess.stdout.on("data", (data: any) => { - console.log(data.toString()) - }) - childProcess.stderr.on("data", (data: any) => { - console.error(data.toString()) - }) - - childProcess.on("exit", (code: any) => { - if (code === 0) { - return resolve(null) - } - return reject(new Error(`${commands[0]} exited with code ${code}`)) - }) - }) - - const pid = childProcess.pid - return { - async wait() { - try { - return await answer - } finally { - await cpExecFile("pkill", ["-9", "-s", String(pid)]).catch((_) => {}) - } - }, - async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) { - try { - await cpExecFile("pkill", [`-${signal}`, "-s", String(pid)]) - - if (timeout > NO_TIMEOUT) { - const didTimeout = await Promise.race([ - new Promise((resolve) => setTimeout(resolve, timeout)).then( - () => true, - ), - answer.then(() => false), - ]) - if (didTimeout) { - await cpExecFile("pkill", [`-9`, "-s", String(pid)]).catch( - (_) => {}, - ) - } - } else { - await answer - } - } finally { - await overlay.destroy() - } - }, - } - } +export const runCommand = () => + CommandController.of() /** * A class for defining and controlling the service daemons @@ -133,7 +73,9 @@ export class Daemons { private constructor( readonly effects: Effects, readonly started: (onTerm: () => PromiseLike) => PromiseLike, - readonly daemons?: Daemon[], + readonly daemons: Promise[], + readonly ids: Ids[], + readonly healthDaemons: HealthDaemon[], ) {} /** * Returns an empty new Daemons class with the provided config. @@ -150,7 +92,13 @@ export class Daemons { started: (onTerm: () => PromiseLike) => PromiseLike healthReceipts: HealthReceipt[] }) { - return new Daemons(config.effects, config.started) + return new Daemons( + config.effects, + config.started, + [], + [], + [], + ) } /** * Returns the complete list of daemons, including the one defined here @@ -165,73 +113,60 @@ export class Daemons { ErrorDuplicateId extends Id ? never : Id extends Ids ? ErrorDuplicateId : Id, - newDaemon: Omit, "id">, + options: DaemonsParams, ) { - const daemons = ((this?.daemons ?? []) as any[]).concat({ - ...newDaemon, + const daemonIndex = this.daemons.length + const daemon = Daemon.of()( + this.effects, + options.image, + options.command, + options, + ) + const healthDaemon = new HealthDaemon( + daemon, + daemonIndex, + options.requires + .map((x) => this.ids.indexOf(id as any)) + .filter((x) => x >= 0) + .map((id) => this.healthDaemons[id]), id, - }) - return new Daemons(this.effects, this.started, daemons) + this.ids, + options.ready, + this.effects, + ) + const daemons = this.daemons.concat(daemon) + const ids = [...this.ids, id] as (Ids | Id)[] + const healthDaemons = [...this.healthDaemons, healthDaemon] + return new Daemons( + this.effects, + this.started, + daemons, + ids, + healthDaemons, + ) } async build() { - const daemonsStarted = {} as Record> - const { effects } = this - const daemons = this.daemons ?? [] - for (const daemon of daemons) { - const requiredPromise = Promise.all( - daemon.requires?.map((id) => daemonsStarted[id]) ?? [], - ) - daemonsStarted[daemon.id] = requiredPromise.then(async () => { - const { command, image } = daemon - - const child = runDaemon()(effects, image, command, { - env: daemon.env, - mounts: daemon.mounts.build(), - }) - let currentInput: TriggerInput = {} - const getCurrentInput = () => currentInput - const trigger = (daemon.ready.trigger ?? defaultTrigger)( - getCurrentInput, - ) - return new Promise(async (resolve) => { - for ( - let res = await trigger.next(); - !res.done; - res = await trigger.next() - ) { - const response = await Promise.resolve(daemon.ready.fn()).catch( - (err) => - ({ - status: "failure", - message: "message" in err ? err.message : String(err), - }) as CheckResult, - ) - currentInput.lastResult = response.status || null - if (!currentInput.hadSuccess && response.status === "success") { - currentInput.hadSuccess = true - resolve(child) - } - } - resolve(child) - }) - }) - } + this.updateMainHealth() + this.healthDaemons.forEach((x) => + x.addWatcher(() => this.updateMainHealth()), + ) return { - async term(options?: { signal?: Signals; timeout?: number }) { - await Promise.all( - Object.values>(daemonsStarted).map((x) => - x.then((x) => x.term(options)), - ), - ) - }, - async wait() { - await Promise.all( - Object.values>(daemonsStarted).map((x) => - x.then((x) => x.wait()), - ), - ) + term: async (options?: { signal?: Signals; timeout?: number }) => { + try { + await Promise.all(this.healthDaemons.map((x) => x.term(options))) + } finally { + this.effects.setMainStatus({ status: "stopped" }) + } }, } } + + private updateMainHealth() { + if (this.healthDaemons.every((x) => x.health.status === "success")) { + this.effects.setMainStatus({ status: "running" }) + } else { + this.effects.setMainStatus({ status: "starting" }) + } + } } diff --git a/sdk/lib/mainFn/HealthDaemon.ts b/sdk/lib/mainFn/HealthDaemon.ts new file mode 100644 index 000000000..84e9e34d7 --- /dev/null +++ b/sdk/lib/mainFn/HealthDaemon.ts @@ -0,0 +1,152 @@ +import { CheckResult } from "../health/checkFns" +import { defaultTrigger } from "../trigger/defaultTrigger" +import { Ready } from "./Daemons" +import { Daemon } from "./Daemon" +import { Effects } from "../types" + +const oncePromise = () => { + let resolve: (value: T) => void + const promise = new Promise((res) => { + resolve = res + }) + return { resolve: resolve!, promise } +} + +/** + * Wanted a structure that deals with controlling daemons by their health status + * States: + * -- Waiting for dependencies to be success + * -- Running: Daemon is running and the status is in the health + * + */ +export class HealthDaemon { + #health: CheckResult = { status: "starting", message: null } + #healthWatchers: Array<() => unknown> = [] + #running = false + #hadSuccess = false + constructor( + readonly daemon: Promise, + readonly daemonIndex: number, + readonly dependencies: HealthDaemon[], + readonly id: string, + readonly ids: string[], + readonly ready: Ready, + readonly effects: Effects, + ) { + this.updateStatus() + this.dependencies.forEach((d) => d.addWatcher(() => this.updateStatus())) + } + + /** Run after we want to do cleanup */ + async term(termOptions?: { + signal?: NodeJS.Signals | undefined + timeout?: number | undefined + }) { + this.#healthWatchers = [] + this.#running = false + this.#healthCheckCleanup?.() + + await this.daemon.then((d) => d.stop(termOptions)) + } + + /** Want to add another notifier that the health might have changed */ + addWatcher(watcher: () => unknown) { + this.#healthWatchers.push(watcher) + } + + get health() { + return Object.freeze(this.#health) + } + + private async changeRunning(newStatus: boolean) { + if (this.#running === newStatus) return + + this.#running = newStatus + + if (newStatus) { + ;(await this.daemon).start() + this.setupHealthCheck() + } else { + ;(await this.daemon).stop() + this.turnOffHealthCheck() + + this.setHealth({ status: "starting", message: null }) + } + } + + #healthCheckCleanup: (() => void) | null = null + private turnOffHealthCheck() { + this.#healthCheckCleanup?.() + } + private async setupHealthCheck() { + if (this.#healthCheckCleanup) return + const trigger = (this.ready.trigger ?? defaultTrigger)(() => ({ + hadSuccess: this.#hadSuccess, + lastResult: this.#health.status, + })) + + const { promise: status, resolve: setStatus } = oncePromise<{ + done: true + }>() + new Promise(async () => { + for ( + let res = await Promise.race([status, trigger.next()]); + !res.done; + res = await Promise.race([status, trigger.next()]) + ) { + const response: CheckResult = await Promise.resolve( + this.ready.fn(), + ).catch((err) => { + console.error(err) + return { + status: "failure", + message: "message" in err ? err.message : String(err), + } + }) + this.setHealth(response) + if (response.status === "success") { + this.#hadSuccess = true + } + } + }).catch((err) => console.error(`Daemon ${this.id} failed: ${err}`)) + + this.#healthCheckCleanup = () => { + setStatus({ done: true }) + this.#healthCheckCleanup = null + } + } + + private setHealth(health: CheckResult) { + this.#health = health + this.#healthWatchers.forEach((watcher) => watcher()) + const display = this.ready.display + const status = health.status + if (!display) { + return + } + if ( + status === "success" || + status === "disabled" || + status === "starting" + ) { + this.effects.setHealth({ + result: status, + message: health.message, + id: display, + name: display, + }) + } else { + this.effects.setHealth({ + result: health.status, + message: health.message || "", + id: display, + name: display, + }) + } + } + + private async updateStatus() { + const healths = this.dependencies.map((d) => d.#health) + this.changeRunning(healths.every((x) => x.status === "success")) + } +} diff --git a/sdk/lib/osBindings/SetMainStatus.ts b/sdk/lib/osBindings/SetMainStatus.ts index 32a839984..aa4aff0b2 100644 --- a/sdk/lib/osBindings/SetMainStatus.ts +++ b/sdk/lib/osBindings/SetMainStatus.ts @@ -1,4 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Status } from "./Status" -export type SetMainStatus = { status: Status } +export type SetMainStatus = { status: "running" | "stopped" | "starting" } diff --git a/sdk/lib/test/startosTypeValidation.test.ts b/sdk/lib/test/startosTypeValidation.test.ts index fd11ab5b6..4f6d50f53 100644 --- a/sdk/lib/test/startosTypeValidation.test.ts +++ b/sdk/lib/test/startosTypeValidation.test.ts @@ -1,5 +1,9 @@ import { Effects } from "../types" -import { CheckDependenciesParam, ExecuteAction } from ".././osBindings" +import { + CheckDependenciesParam, + ExecuteAction, + SetMainStatus, +} from ".././osBindings" import { CreateOverlayedImageParams } from ".././osBindings" import { DestroyOverlayedImageParams } from ".././osBindings" import { BindParams } from ".././osBindings" @@ -66,6 +70,7 @@ describe("startosTypeValidation ", () => { mount: {} as MountParams, checkDependencies: {} as CheckDependenciesParam, getDependencies: undefined, + setMainStatus: {} as SetMainStatus, }) typeEquality[0]>( testInput as ExecuteAction, diff --git a/sdk/lib/types.ts b/sdk/lib/types.ts index 96e256766..7db42e5a9 100644 --- a/sdk/lib/types.ts +++ b/sdk/lib/types.ts @@ -4,6 +4,7 @@ import { DependencyRequirement, SetHealth, HealthCheckResult, + SetMainStatus, } from "./osBindings" import { MainEffects, ServiceInterfaceType, Signals } from "./StartSdk" @@ -163,7 +164,7 @@ export type CommandType = | [string, ...string[]] export type DaemonReturned = { - wait(): Promise + wait(): Promise term(options?: { signal?: Signals; timeout?: number }): Promise } @@ -380,6 +381,8 @@ export type Effects = { }): Promise } + setMainStatus(o: SetMainStatus): Promise + getSystemSmtp(input: { callback: (config: unknown, previousConfig: unknown) => void }): Promise diff --git a/sdk/package.json b/sdk/package.json index 409c5bb02..db387c036 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.4.0-rev0.lib0.rc8.beta10", + "version": "0.3.6-alpha1", "description": "Software development kit to facilitate packaging services for StartOS", "main": "./cjs/lib/index.js", "types": "./cjs/lib/index.d.ts",