From bb514d621657333284c622a7bb66673328535a3c Mon Sep 17 00:00:00 2001 From: Jade <2364004+Blu-J@users.noreply.github.com> Date: Fri, 14 Jun 2024 14:16:12 -0600 Subject: [PATCH] Chore/refactoring effects (#2644) * fix mac build * wip * chore: Update the effects to get rid of bad pattern * chore: Some small changes --------- Co-authored-by: Aiden McClelland --- .../src/Adapters/HostSystemStartOs.ts | 519 ++++++------ container-runtime/src/Adapters/RpcListener.ts | 2 +- .../Systems/SystemForEmbassy/MainLoop.ts | 11 +- .../Systems/SystemForEmbassy/index.ts | 72 +- .../SystemForEmbassy/polyfillEffects.ts | 770 +++++++++--------- .../src/Adapters/Systems/SystemForStartOs.ts | 7 +- .../src/Interfaces/HostSystem.ts | 5 +- container-runtime/src/Interfaces/System.ts | 4 +- container-runtime/src/index.ts | 4 +- download-firmware.sh | 3 +- 10 files changed, 704 insertions(+), 693 deletions(-) diff --git a/container-runtime/src/Adapters/HostSystemStartOs.ts b/container-runtime/src/Adapters/HostSystemStartOs.ts index 93905825a..1996af0fd 100644 --- a/container-runtime/src/Adapters/HostSystemStartOs.ts +++ b/container-runtime/src/Adapters/HostSystemStartOs.ts @@ -31,274 +31,273 @@ type RpcError = typeof matchRpcError._TYPE const SOCKET_PATH = "/media/startos/rpc/host.sock" const MAIN = "/main" as const -export class HostSystemStartOs implements Effects { - procedureId: string | null = null - - static of(callbackHolder: CallbackHolder) { - return new HostSystemStartOs(callbackHolder) - } - - constructor(readonly callbackHolder: CallbackHolder) {} - id = 0 - rpcRound( - method: K, - params: Record, - ) { - const id = this.id++ - const client = net.createConnection({ path: SOCKET_PATH }, () => { - client.write( - JSON.stringify({ - id, - method, - params: { ...params, procedureId: this.procedureId }, - }) + "\n", - ) - }) - let bufs: Buffer[] = [] - return new Promise((resolve, reject) => { - client.on("data", (data) => { - try { - bufs.push(data) - if (data.reduce((acc, x) => acc || x == 10, false)) { - const res: unknown = JSON.parse( - Buffer.concat(bufs).toString().split("\n")[0], - ) - if (testRpcError(res)) { - let message = res.error.message - console.error({ method, params, hostSystemStartOs: true }) - if (string.test(res.error.data)) { - message += ": " + res.error.data - console.error(res.error.data) +let hostSystemId = 0 +export const hostSystemStartOs = + (callbackHolder: CallbackHolder) => + (procedureId: null | string): Effects => { + const rpcRound = ( + method: K, + params: Record, + ) => { + const id = hostSystemId++ + const client = net.createConnection({ path: SOCKET_PATH }, () => { + client.write( + JSON.stringify({ + id, + method, + params: { ...params, procedureId: procedureId }, + }) + "\n", + ) + }) + let bufs: Buffer[] = [] + return new Promise((resolve, reject) => { + client.on("data", (data) => { + try { + bufs.push(data) + if (data.reduce((acc, x) => acc || x == 10, false)) { + const res: unknown = JSON.parse( + Buffer.concat(bufs).toString().split("\n")[0], + ) + if (testRpcError(res)) { + let message = res.error.message + console.error({ method, params, hostSystemStartOs: true }) + if (string.test(res.error.data)) { + message += ": " + res.error.data + console.error(res.error.data) + } else { + if (res.error.data?.details) { + message += ": " + res.error.data.details + console.error(res.error.data.details) + } + if (res.error.data?.debug) { + message += "\n" + res.error.data.debug + console.error("Debug: " + res.error.data.debug) + } + } + reject(new Error(`${message}@${method}`)) + } else if (testRpcResult(res)) { + resolve(res.result) } else { - if (res.error.data?.details) { - message += ": " + res.error.data.details - console.error(res.error.data.details) - } - if (res.error.data?.debug) { - message += "\n" + res.error.data.debug - console.error("Debug: " + res.error.data.debug) - } + reject(new Error(`malformed response ${JSON.stringify(res)}`)) } - reject(new Error(`${message}@${method}`)) - } else if (testRpcResult(res)) { - resolve(res.result) - } else { - reject(new Error(`malformed response ${JSON.stringify(res)}`)) } + } catch (error) { + reject(error) } - } catch (error) { + client.end() + }) + client.on("error", (error) => { reject(error) - } - client.end() + }) }) - client.on("error", (error) => { - reject(error) - }) - }) - } - - bind(...[options]: Parameters) { - return this.rpcRound("bind", { - ...options, - stack: new Error().stack, - }) as ReturnType - } - clearBindings(...[]: Parameters) { - return this.rpcRound("clearBindings", {}) as ReturnType< - T.Effects["clearBindings"] - > - } - clearServiceInterfaces( - ...[]: Parameters - ) { - return this.rpcRound("clearServiceInterfaces", {}) as ReturnType< - T.Effects["clearServiceInterfaces"] - > - } - createOverlayedImage(options: { - imageId: string - }): Promise<[string, string]> { - return this.rpcRound("createOverlayedImage", options) as ReturnType< - T.Effects["createOverlayedImage"] - > - } - destroyOverlayedImage(options: { guid: string }): Promise { - return this.rpcRound("destroyOverlayedImage", options) as ReturnType< - T.Effects["destroyOverlayedImage"] - > - } - executeAction(...[options]: Parameters) { - return this.rpcRound("executeAction", options) as ReturnType< - T.Effects["executeAction"] - > - } - exists(...[packageId]: Parameters) { - return this.rpcRound("exists", packageId) as ReturnType - } - exportAction(...[options]: Parameters) { - return this.rpcRound("exportAction", options) as ReturnType< - T.Effects["exportAction"] - > - } - exportServiceInterface: Effects["exportServiceInterface"] = ( - ...[options]: Parameters - ) => { - return this.rpcRound("exportServiceInterface", options) as ReturnType< - T.Effects["exportServiceInterface"] - > - } - exposeForDependents( - ...[options]: Parameters - ) { - return this.rpcRound("exposeForDependents", options) as ReturnType< - T.Effects["exposeForDependents"] - > - } - getConfigured(...[]: Parameters) { - return this.rpcRound("getConfigured", {}) as ReturnType< - T.Effects["getConfigured"] - > - } - getContainerIp(...[]: Parameters) { - return this.rpcRound("getContainerIp", {}) as ReturnType< - T.Effects["getContainerIp"] - > - } - getHostInfo: Effects["getHostInfo"] = (...[allOptions]: any[]) => { - const options = { - ...allOptions, - callback: this.callbackHolder.addCallback(allOptions.callback), } - return this.rpcRound("getHostInfo", options) as ReturnType< - T.Effects["getHostInfo"] - > as any - } - getServiceInterface( - ...[options]: Parameters - ) { - return this.rpcRound("getServiceInterface", { - ...options, - callback: this.callbackHolder.addCallback(options.callback), - }) as ReturnType - } + const self: Effects = { + bind(...[options]: Parameters) { + return rpcRound("bind", { + ...options, + stack: new Error().stack, + }) as ReturnType + }, + clearBindings(...[]: Parameters) { + return rpcRound("clearBindings", {}) as ReturnType< + T.Effects["clearBindings"] + > + }, + clearServiceInterfaces( + ...[]: Parameters + ) { + return rpcRound("clearServiceInterfaces", {}) as ReturnType< + T.Effects["clearServiceInterfaces"] + > + }, + createOverlayedImage(options: { + imageId: string + }): Promise<[string, string]> { + return rpcRound("createOverlayedImage", options) as ReturnType< + T.Effects["createOverlayedImage"] + > + }, + destroyOverlayedImage(options: { guid: string }): Promise { + return rpcRound("destroyOverlayedImage", options) as ReturnType< + T.Effects["destroyOverlayedImage"] + > + }, + executeAction(...[options]: Parameters) { + return rpcRound("executeAction", options) as ReturnType< + T.Effects["executeAction"] + > + }, + exists(...[packageId]: Parameters) { + return rpcRound("exists", packageId) as ReturnType + }, + exportAction(...[options]: Parameters) { + return rpcRound("exportAction", options) as ReturnType< + T.Effects["exportAction"] + > + }, + exportServiceInterface: (( + ...[options]: Parameters + ) => { + return rpcRound("exportServiceInterface", options) as ReturnType< + T.Effects["exportServiceInterface"] + > + }) as Effects["exportServiceInterface"], + exposeForDependents( + ...[options]: Parameters + ) { + return rpcRound("exposeForDependents", options) as ReturnType< + T.Effects["exposeForDependents"] + > + }, + getConfigured(...[]: Parameters) { + return rpcRound("getConfigured", {}) as ReturnType< + T.Effects["getConfigured"] + > + }, + getContainerIp(...[]: Parameters) { + return rpcRound("getContainerIp", {}) as ReturnType< + T.Effects["getContainerIp"] + > + }, + getHostInfo: ((...[allOptions]: any[]) => { + const options = { + ...allOptions, + callback: callbackHolder.addCallback(allOptions.callback), + } + return rpcRound("getHostInfo", options) as ReturnType< + T.Effects["getHostInfo"] + > as any + }) as Effects["getHostInfo"], + getServiceInterface( + ...[options]: Parameters + ) { + return rpcRound("getServiceInterface", { + ...options, + callback: callbackHolder.addCallback(options.callback), + }) as ReturnType + }, - getPrimaryUrl(...[options]: Parameters) { - return this.rpcRound("getPrimaryUrl", { - ...options, - callback: this.callbackHolder.addCallback(options.callback), - }) as ReturnType - } - getServicePortForward( - ...[options]: Parameters - ) { - return this.rpcRound("getServicePortForward", options) as ReturnType< - T.Effects["getServicePortForward"] - > - } - getSslCertificate(options: Parameters[0]) { - return this.rpcRound("getSslCertificate", options) as ReturnType< - T.Effects["getSslCertificate"] - > - } - getSslKey(options: Parameters[0]) { - return this.rpcRound("getSslKey", options) as ReturnType< - T.Effects["getSslKey"] - > - } - getSystemSmtp(...[options]: Parameters) { - return this.rpcRound("getSystemSmtp", { - ...options, - callback: this.callbackHolder.addCallback(options.callback), - }) as ReturnType - } - listServiceInterfaces( - ...[options]: Parameters - ) { - return this.rpcRound("listServiceInterfaces", { - ...options, - callback: this.callbackHolder.addCallback(options.callback), - }) as ReturnType - } - mount(...[options]: Parameters) { - return this.rpcRound("mount", options) as ReturnType - } - removeAction(...[options]: Parameters) { - return this.rpcRound("removeAction", options) as ReturnType< - T.Effects["removeAction"] - > - } - removeAddress(...[options]: Parameters) { - return this.rpcRound("removeAddress", options) as ReturnType< - T.Effects["removeAddress"] - > - } - restart(...[]: Parameters) { - return this.rpcRound("restart", {}) as ReturnType - } - running(...[packageId]: Parameters) { - return this.rpcRound("running", { packageId }) as ReturnType< - T.Effects["running"] - > - } - // runRsync(...[options]: Parameters) { - // - // return this.rpcRound('executeAction', options) as ReturnType - // - // return this.rpcRound('executeAction', options) as ReturnType - // } - setConfigured(...[configured]: Parameters) { - return this.rpcRound("setConfigured", { configured }) as ReturnType< - T.Effects["setConfigured"] - > - } - setDependencies( - dependencies: Parameters[0], - ): ReturnType { - return this.rpcRound("setDependencies", dependencies) as ReturnType< - T.Effects["setDependencies"] - > - } - checkDependencies( - options: Parameters[0], - ): ReturnType { - return this.rpcRound("checkDependencies", options) as ReturnType< - T.Effects["checkDependencies"] - > - } - getDependencies(): ReturnType { - return this.rpcRound("getDependencies", {}) as ReturnType< - T.Effects["getDependencies"] - > - } - setHealth(...[options]: Parameters) { - return this.rpcRound("setHealth", options) as ReturnType< - T.Effects["setHealth"] - > - } + getPrimaryUrl(...[options]: Parameters) { + return rpcRound("getPrimaryUrl", { + ...options, + callback: callbackHolder.addCallback(options.callback), + }) as ReturnType + }, + getServicePortForward( + ...[options]: Parameters + ) { + return rpcRound("getServicePortForward", options) as ReturnType< + T.Effects["getServicePortForward"] + > + }, + getSslCertificate( + options: Parameters[0], + ) { + return rpcRound("getSslCertificate", options) as ReturnType< + T.Effects["getSslCertificate"] + > + }, + getSslKey(options: Parameters[0]) { + return rpcRound("getSslKey", options) as ReturnType< + T.Effects["getSslKey"] + > + }, + getSystemSmtp(...[options]: Parameters) { + return rpcRound("getSystemSmtp", { + ...options, + callback: callbackHolder.addCallback(options.callback), + }) as ReturnType + }, + listServiceInterfaces( + ...[options]: Parameters + ) { + return rpcRound("listServiceInterfaces", { + ...options, + callback: callbackHolder.addCallback(options.callback), + }) as ReturnType + }, + mount(...[options]: Parameters) { + return rpcRound("mount", options) as ReturnType + }, + removeAction(...[options]: Parameters) { + return rpcRound("removeAction", options) as ReturnType< + T.Effects["removeAction"] + > + }, + removeAddress(...[options]: Parameters) { + return rpcRound("removeAddress", options) as ReturnType< + T.Effects["removeAddress"] + > + }, + restart(...[]: Parameters) { + return rpcRound("restart", {}) as ReturnType + }, + running(...[packageId]: Parameters) { + return rpcRound("running", { packageId }) as ReturnType< + T.Effects["running"] + > + }, + // runRsync(...[options]: Parameters) { + // + // return rpcRound('executeAction', options) as ReturnType + // + // return rpcRound('executeAction', options) as ReturnType + // } + setConfigured(...[configured]: Parameters) { + return rpcRound("setConfigured", { configured }) as ReturnType< + T.Effects["setConfigured"] + > + }, + setDependencies( + dependencies: Parameters[0], + ): ReturnType { + return rpcRound("setDependencies", dependencies) as ReturnType< + T.Effects["setDependencies"] + > + }, + checkDependencies( + options: Parameters[0], + ): ReturnType { + return rpcRound("checkDependencies", options) as ReturnType< + T.Effects["checkDependencies"] + > + }, + getDependencies(): ReturnType { + return rpcRound("getDependencies", {}) as ReturnType< + T.Effects["getDependencies"] + > + }, + setHealth(...[options]: Parameters) { + return rpcRound("setHealth", options) as ReturnType< + T.Effects["setHealth"] + > + }, - setMainStatus(o: { status: "running" | "stopped" }): Promise { - return this.rpcRound("setMainStatus", o) as ReturnType< - T.Effects["setHealth"] - > - } + setMainStatus(o: { status: "running" | "stopped" }): Promise { + return rpcRound("setMainStatus", o) as ReturnType< + T.Effects["setHealth"] + > + }, - shutdown(...[]: Parameters) { - return this.rpcRound("shutdown", {}) as ReturnType + shutdown(...[]: Parameters) { + return rpcRound("shutdown", {}) as ReturnType + }, + stopped(...[packageId]: Parameters) { + return rpcRound("stopped", { packageId }) as ReturnType< + T.Effects["stopped"] + > + }, + store: { + get: async (options: any) => + rpcRound("getStore", { + ...options, + callback: callbackHolder.addCallback(options.callback), + }) as any, + set: async (options: any) => + rpcRound("setStore", options) as ReturnType< + T.Effects["store"]["set"] + >, + } as T.Effects["store"], + } + return self } - stopped(...[packageId]: Parameters) { - return this.rpcRound("stopped", { packageId }) as ReturnType< - T.Effects["stopped"] - > - } - store: T.Effects["store"] = { - get: async (options: any) => - this.rpcRound("getStore", { - ...options, - callback: this.callbackHolder.addCallback(options.callback), - }) as any, - set: async (options: any) => - this.rpcRound("setStore", options) as ReturnType< - T.Effects["store"]["set"] - >, - } -} diff --git a/container-runtime/src/Adapters/RpcListener.ts b/container-runtime/src/Adapters/RpcListener.ts index 5391d943e..04e9bc40f 100644 --- a/container-runtime/src/Adapters/RpcListener.ts +++ b/container-runtime/src/Adapters/RpcListener.ts @@ -247,7 +247,7 @@ export class RpcListener { })), ) .when(exitType, async ({ id }) => { - if (this._system) await this._system.exit(this.effects) + if (this._system) await this._system.exit(this.effects(null)) delete this._system delete this._effects diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index 08bf944ed..975bb52ca 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -1,9 +1,10 @@ -import { PolyfillEffects } from "./polyfillEffects" +import { polyfillEffects } from "./polyfillEffects" import { DockerProcedureContainer } from "./DockerProcedureContainer" import { SystemForEmbassy } from "." -import { HostSystemStartOs } from "../../HostSystemStartOs" +import { hostSystemStartOs } from "../../HostSystemStartOs" import { Daemons, T, daemons } from "@start9labs/start-sdk" import { Daemon } from "@start9labs/start-sdk/cjs/lib/mainFn/Daemon" +import { Effects } from "../../../Models/Effects" const EMBASSY_HEALTH_INTERVAL = 15 * 1000 const EMBASSY_PROPERTIES_LOOP = 30 * 1000 @@ -27,7 +28,7 @@ export class MainLoop { | undefined constructor( readonly system: SystemForEmbassy, - readonly effects: HostSystemStartOs, + readonly effects: Effects, ) { this.healthLoops = this.constructHealthLoops() this.mainEvent = this.constructMainEvent() @@ -65,7 +66,7 @@ export class MainLoop { } } - private async setupInterfaces(effects: HostSystemStartOs) { + private async setupInterfaces(effects: T.Effects) { for (const interfaceId in this.system.manifest.interfaces) { const iface = this.system.manifest.interfaces[interfaceId] const internalPorts = new Set() @@ -211,7 +212,7 @@ export class MainLoop { } const result = await method( - new PolyfillEffects(effects, this.system.manifest), + polyfillEffects(effects, this.system.manifest), timeChanged, ) diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index 731b38903..127fb0e09 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -1,7 +1,7 @@ import { types as T, utils, EmVer } from "@start9labs/start-sdk" import * as fs from "fs/promises" -import { PolyfillEffects } from "./polyfillEffects" +import { polyfillEffects } from "./polyfillEffects" import { Duration, duration, fromDuration } from "../../../Models/Duration" import { System } from "../../../Interfaces/System" import { matchManifest, Manifest, Procedure } from "./matchManifest" @@ -27,7 +27,7 @@ import { Parser, array, } from "ts-matches" -import { HostSystemStartOs } from "../../HostSystemStartOs" +import { hostSystemStartOs } from "../../HostSystemStartOs" import { JsonPath, unNestPath } from "../../../Models/JsonPath" import { RpcResult, matchRpcResult } from "../../RpcListener" import { CT } from "@start9labs/start-sdk" @@ -41,6 +41,7 @@ import { MultiHost, } from "@start9labs/start-sdk/cjs/lib/interfaces/Host" import { ServiceInterfaceBuilder } from "@start9labs/start-sdk/cjs/lib/interfaces/ServiceInterfaceBuilder" +import { Effects } from "../../../Models/Effects" type Optional = A | undefined | null function todo(): never { @@ -197,7 +198,7 @@ export class SystemForEmbassy implements System { readonly moduleCode: Partial, ) {} async execute( - effects: HostSystemStartOs, + effectCreator: ReturnType, options: { id: string procedure: JsonPath @@ -205,8 +206,7 @@ export class SystemForEmbassy implements System { timeout?: number | undefined }, ): Promise { - effects = Object.create(effects) - effects.procedureId = options.id + const effects = effectCreator(options.id) return this._execute(effects, options) .then((x) => matches(x) @@ -261,12 +261,12 @@ export class SystemForEmbassy implements System { } }) } - async exit(effects: HostSystemStartOs): Promise { + async exit(): Promise { if (this.currentRunning) await this.currentRunning.clean() delete this.currentRunning } async _execute( - effects: HostSystemStartOs, + effects: Effects, options: { procedure: JsonPath input: unknown @@ -340,7 +340,7 @@ export class SystemForEmbassy implements System { throw new Error(`Could not find the path for ${options.procedure}`) } private async init( - effects: HostSystemStartOs, + effects: Effects, previousVersion: Optional, timeoutMs: number | null, ): Promise { @@ -350,7 +350,7 @@ export class SystemForEmbassy implements System { await this.exportActions(effects) await this.exportNetwork(effects) } - async exportNetwork(effects: HostSystemStartOs) { + async exportNetwork(effects: Effects) { for (const [id, interfaceValue] of Object.entries( this.manifest.interfaces, )) { @@ -428,7 +428,7 @@ export class SystemForEmbassy implements System { ) } } - async exportActions(effects: HostSystemStartOs) { + async exportActions(effects: Effects) { const manifest = this.manifest if (!manifest.actions) return for (const [actionId, action] of Object.entries(manifest.actions)) { @@ -457,7 +457,7 @@ export class SystemForEmbassy implements System { } } private async uninit( - effects: HostSystemStartOs, + effects: Effects, nextVersion: Optional, timeoutMs: number | null, ): Promise { @@ -465,7 +465,7 @@ export class SystemForEmbassy implements System { await effects.setMainStatus({ status: "stopped" }) } private async mainStart( - effects: HostSystemStartOs, + effects: Effects, timeoutMs: number | null, ): Promise { if (!!this.currentRunning) return @@ -473,7 +473,7 @@ export class SystemForEmbassy implements System { this.currentRunning = new MainLoop(this, effects) } private async mainStop( - effects: HostSystemStartOs, + effects: Effects, timeoutMs: number | null, ): Promise { const { currentRunning } = this @@ -491,7 +491,7 @@ export class SystemForEmbassy implements System { return durationValue } private async createBackup( - effects: HostSystemStartOs, + effects: Effects, timeoutMs: number | null, ): Promise { const backup = this.manifest.backup.create @@ -503,13 +503,11 @@ export class SystemForEmbassy implements System { await container.execFail([backup.entrypoint, ...backup.args], timeoutMs) } else { const moduleCode = await this.moduleCode - await moduleCode.createBackup?.( - new PolyfillEffects(effects, this.manifest), - ) + await moduleCode.createBackup?.(polyfillEffects(effects, this.manifest)) } } private async restoreBackup( - effects: HostSystemStartOs, + effects: Effects, timeoutMs: number | null, ): Promise { const restoreBackup = this.manifest.backup.restore @@ -528,19 +526,17 @@ export class SystemForEmbassy implements System { ) } else { const moduleCode = await this.moduleCode - await moduleCode.restoreBackup?.( - new PolyfillEffects(effects, this.manifest), - ) + await moduleCode.restoreBackup?.(polyfillEffects(effects, this.manifest)) } } private async getConfig( - effects: HostSystemStartOs, + effects: Effects, timeoutMs: number | null, ): Promise { return this.getConfigUncleaned(effects, timeoutMs).then(removePointers) } private async getConfigUncleaned( - effects: HostSystemStartOs, + effects: Effects, timeoutMs: number | null, ): Promise { const config = this.manifest.config?.get @@ -564,7 +560,7 @@ export class SystemForEmbassy implements System { const moduleCode = await this.moduleCode const method = moduleCode.getConfig if (!method) throw new Error("Expecting that the method getConfig exists") - return (await method(new PolyfillEffects(effects, this.manifest)).then( + return (await method(polyfillEffects(effects, this.manifest)).then( (x) => { if ("result" in x) return x.result if ("error" in x) throw new Error("Error getting config: " + x.error) @@ -574,7 +570,7 @@ export class SystemForEmbassy implements System { } } private async setConfig( - effects: HostSystemStartOs, + effects: Effects, newConfigWithoutPointers: unknown, timeoutMs: number | null, ): Promise { @@ -617,7 +613,7 @@ export class SystemForEmbassy implements System { const answer = matchSetResult.unsafeCast( await method( - new PolyfillEffects(effects, this.manifest), + polyfillEffects(effects, this.manifest), newConfig as U.Config, ).then((x): T.SetResult => { if ("result" in x) @@ -636,7 +632,7 @@ export class SystemForEmbassy implements System { } } private async setConfigSetConfig( - effects: HostSystemStartOs, + effects: Effects, dependsOn: { [x: string]: readonly string[] }, ) { await effects.setDependencies({ @@ -660,7 +656,7 @@ export class SystemForEmbassy implements System { } private async migration( - effects: HostSystemStartOs, + effects: Effects, fromVersion: string, timeoutMs: number | null, ): Promise { @@ -713,7 +709,7 @@ export class SystemForEmbassy implements System { if (!method) throw new Error("Expecting that the method migration exists") return (await method( - new PolyfillEffects(effects, this.manifest), + polyfillEffects(effects, this.manifest), fromVersion as string, ).then((x) => { if ("result" in x) return x.result @@ -725,7 +721,7 @@ export class SystemForEmbassy implements System { return { configured: true } } private async properties( - effects: HostSystemStartOs, + effects: Effects, timeoutMs: number | null, ): Promise> { // TODO BLU-J set the properties ever so often @@ -754,7 +750,7 @@ export class SystemForEmbassy implements System { if (!method) throw new Error("Expecting that the method properties exists") const properties = matchProperties.unsafeCast( - await method(new PolyfillEffects(effects, this.manifest)).then((x) => { + await method(polyfillEffects(effects, this.manifest)).then((x) => { if ("result" in x) return x.result if ("error" in x) throw new Error("Error getting config: " + x.error) throw new Error("Error getting config: " + x["error-code"][1]) @@ -765,7 +761,7 @@ export class SystemForEmbassy implements System { throw new Error(`Unknown type in the fetch properties: ${setConfigValue}`) } private async action( - effects: HostSystemStartOs, + effects: Effects, actionId: string, formData: unknown, timeoutMs: number | null, @@ -795,7 +791,7 @@ export class SystemForEmbassy implements System { const method = moduleCode.action?.[actionId] if (!method) throw new Error("Expecting that the method action exists") return (await method( - new PolyfillEffects(effects, this.manifest), + polyfillEffects(effects, this.manifest), formData as any, ).then((x) => { if ("result" in x) return x.result @@ -805,7 +801,7 @@ export class SystemForEmbassy implements System { } } private async dependenciesCheck( - effects: HostSystemStartOs, + effects: Effects, id: string, oldConfig: unknown, timeoutMs: number | null, @@ -838,7 +834,7 @@ export class SystemForEmbassy implements System { `Expecting that the method dependency check ${id} exists`, ) return (await method( - new PolyfillEffects(effects, this.manifest), + polyfillEffects(effects, this.manifest), oldConfig as any, ).then((x) => { if ("result" in x) return x.result @@ -850,7 +846,7 @@ export class SystemForEmbassy implements System { } } private async dependenciesAutoconfig( - effects: HostSystemStartOs, + effects: Effects, id: string, oldConfig: unknown, timeoutMs: number | null, @@ -863,7 +859,7 @@ export class SystemForEmbassy implements System { `Expecting that the method dependency autoConfigure ${id} exists`, ) return (await method( - new PolyfillEffects(effects, this.manifest), + polyfillEffects(effects, this.manifest), oldConfig as any, ).then((x) => { if ("result" in x) return x.result @@ -961,7 +957,7 @@ function cleanConfigFromPointers( } async function updateConfig( - effects: HostSystemStartOs, + effects: Effects, manifest: Manifest, spec: unknown, mutConfigValue: unknown, diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts index 3a8d5f624..2b7363cbf 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts @@ -4,390 +4,175 @@ import { Volume } from "../../../Models/Volume" import * as child_process from "child_process" import { promisify } from "util" import { daemons, startSdk, T } from "@start9labs/start-sdk" -import { HostSystemStartOs } from "../../HostSystemStartOs" import "isomorphic-fetch" import { Manifest } from "./matchManifest" import { DockerProcedureContainer } from "./DockerProcedureContainer" import * as cp from "child_process" +import { Effects } from "../../../Models/Effects" export const execFile = promisify(cp.execFile) -export class PolyfillEffects implements oet.Effects { - constructor( - readonly effects: HostSystemStartOs, - private manifest: Manifest, - ) {} - async writeFile(input: { - path: string - volumeId: string - toWrite: string - }): Promise { - await fs.writeFile( - new Volume(input.volumeId, input.path).path, - input.toWrite, - ) - } - async readFile(input: { volumeId: string; path: string }): Promise { - return ( - await fs.readFile(new Volume(input.volumeId, input.path).path) - ).toString() - } - async metadata(input: { - volumeId: string - path: string - }): Promise { - const stats = await fs.stat(new Volume(input.volumeId, input.path).path) - return { - fileType: stats.isFile() ? "file" : "directory", - gid: stats.gid, - uid: stats.uid, - mode: stats.mode, - isDir: stats.isDirectory(), - isFile: stats.isFile(), - isSymlink: stats.isSymbolicLink(), - len: stats.size, - readonly: (stats.mode & 0o200) > 0, - } - } - async createDir(input: { volumeId: string; path: string }): Promise { - const path = new Volume(input.volumeId, input.path).path - await fs.mkdir(path, { recursive: true }) - return path - } - async readDir(input: { volumeId: string; path: string }): Promise { - return fs.readdir(new Volume(input.volumeId, input.path).path) - } - async removeDir(input: { volumeId: string; path: string }): Promise { - const path = new Volume(input.volumeId, input.path).path - await fs.rmdir(new Volume(input.volumeId, input.path).path, { - recursive: true, - }) - return path - } - removeFile(input: { volumeId: string; path: string }): Promise { - return fs.rm(new Volume(input.volumeId, input.path).path) - } - async writeJsonFile(input: { - volumeId: string - path: string - toWrite: Record - }): Promise { - await fs.writeFile( - new Volume(input.volumeId, input.path).path, - JSON.stringify(input.toWrite), - ) - } - async readJsonFile(input: { - volumeId: string - path: string - }): Promise> { - return JSON.parse( - ( +export const polyfillEffects = ( + effects: Effects, + manifest: Manifest, +): oet.Effects => { + const self = { + effects, + manifest, + async writeFile(input: { + path: string + volumeId: string + toWrite: string + }): Promise { + await fs.writeFile( + new Volume(input.volumeId, input.path).path, + input.toWrite, + ) + }, + async readFile(input: { volumeId: string; path: string }): Promise { + return ( await fs.readFile(new Volume(input.volumeId, input.path).path) - ).toString(), - ) - } - runCommand({ - command, - args, - timeoutMillis, - }: { - command: string - args?: string[] | undefined - timeoutMillis?: number | undefined - }): Promise> { - return startSdk - .runCommand( - this.effects, - { id: this.manifest.main.image }, - [command, ...(args || [])], - {}, + ).toString() + }, + async metadata(input: { + volumeId: string + path: string + }): Promise { + const stats = await fs.stat(new Volume(input.volumeId, input.path).path) + return { + fileType: stats.isFile() ? "file" : "directory", + gid: stats.gid, + uid: stats.uid, + mode: stats.mode, + isDir: stats.isDirectory(), + isFile: stats.isFile(), + isSymlink: stats.isSymbolicLink(), + len: stats.size, + readonly: (stats.mode & 0o200) > 0, + } + }, + async createDir(input: { + volumeId: string + path: string + }): Promise { + const path = new Volume(input.volumeId, input.path).path + await fs.mkdir(path, { recursive: true }) + return path + }, + async readDir(input: { + volumeId: string + path: string + }): Promise { + return fs.readdir(new Volume(input.volumeId, input.path).path) + }, + async removeDir(input: { + volumeId: string + path: string + }): Promise { + const path = new Volume(input.volumeId, input.path).path + await fs.rmdir(new Volume(input.volumeId, input.path).path, { + recursive: true, + }) + return path + }, + removeFile(input: { volumeId: string; path: string }): Promise { + return fs.rm(new Volume(input.volumeId, input.path).path) + }, + async writeJsonFile(input: { + volumeId: string + path: string + toWrite: Record + }): Promise { + await fs.writeFile( + new Volume(input.volumeId, input.path).path, + JSON.stringify(input.toWrite), ) - .then((x: any) => ({ - stderr: x.stderr.toString(), - stdout: x.stdout.toString(), - })) - .then((x: any) => - !!x.stderr ? { error: x.stderr } : { result: x.stdout }, + }, + async readJsonFile(input: { + volumeId: string + path: string + }): Promise> { + return JSON.parse( + ( + await fs.readFile(new Volume(input.volumeId, input.path).path) + ).toString(), ) - } - runDaemon(input: { command: string; args?: string[] | undefined }): { - wait(): Promise> - term(): Promise - } { - const dockerProcedureContainer = DockerProcedureContainer.of( - this.effects, - this.manifest.main, - this.manifest.volumes, - ) - const daemon = dockerProcedureContainer.then((dockerProcedureContainer) => - daemons.runCommand()( - this.effects, - { id: this.manifest.main.image }, - [input.command, ...(input.args || [])], - { - overlay: dockerProcedureContainer.overlay, - }, - ), - ) - return { - wait: () => - daemon.then((daemon) => - daemon.wait().then(() => { - return { result: "" } - }), + }, + runCommand({ + command, + args, + timeoutMillis, + }: { + command: string + args?: string[] | undefined + timeoutMillis?: number | undefined + }): Promise> { + return startSdk + .runCommand( + effects, + { id: manifest.main.image }, + [command, ...(args || [])], + {}, + ) + .then((x: any) => ({ + stderr: x.stderr.toString(), + stdout: x.stdout.toString(), + })) + .then((x: any) => + !!x.stderr ? { error: x.stderr } : { result: x.stdout }, + ) + }, + runDaemon(input: { command: string; args?: string[] | undefined }): { + wait(): Promise> + term(): Promise + } { + const dockerProcedureContainer = DockerProcedureContainer.of( + effects, + manifest.main, + manifest.volumes, + ) + const daemon = dockerProcedureContainer.then((dockerProcedureContainer) => + daemons.runCommand()( + effects, + { id: manifest.main.image }, + [input.command, ...(input.args || [])], + { + overlay: dockerProcedureContainer.overlay, + }, ), - term: () => daemon.then((daemon) => daemon.term()), - } - } - async chown(input: { - volumeId: string - path: string - uid: string - }): Promise { - await startSdk - .runCommand( - this.effects, - { id: this.manifest.main.image }, - ["chown", "--recursive", input.uid, `/drive/${input.path}`], - { - mounts: [ - { - path: "/drive", - options: { - type: "volume", - id: input.volumeId, - subpath: null, - readonly: false, - }, - }, - ], - }, ) - .then((x: any) => ({ - stderr: x.stderr.toString(), - stdout: x.stdout.toString(), - })) - .then((x: any) => { - if (!!x.stderr) { - throw new Error(x.stderr) - } - }) - return null - } - async chmod(input: { - volumeId: string - path: string - mode: string - }): Promise { - await startSdk - .runCommand( - this.effects, - { id: this.manifest.main.image }, - ["chmod", "--recursive", input.mode, `/drive/${input.path}`], - { - mounts: [ - { - path: "/drive", - options: { - type: "volume", - id: input.volumeId, - subpath: null, - readonly: false, + return { + wait: () => + daemon.then((daemon) => + daemon.wait().then(() => { + return { result: "" } + }), + ), + term: () => daemon.then((daemon) => daemon.term()), + } + }, + async chown(input: { + volumeId: string + path: string + uid: string + }): Promise { + await startSdk + .runCommand( + effects, + { id: manifest.main.image }, + ["chown", "--recursive", input.uid, `/drive/${input.path}`], + { + mounts: [ + { + path: "/drive", + options: { + type: "volume", + id: input.volumeId, + subpath: null, + readonly: false, + }, }, - }, - ], - }, - ) - .then((x: any) => ({ - stderr: x.stderr.toString(), - stdout: x.stdout.toString(), - })) - .then((x: any) => { - if (!!x.stderr) { - throw new Error(x.stderr) - } - }) - return null - } - sleep(timeMs: number): Promise { - return new Promise((resolve) => setTimeout(resolve, timeMs)) - } - trace(whatToPrint: string): void { - console.trace(whatToPrint) - } - warn(whatToPrint: string): void { - console.warn(whatToPrint) - } - error(whatToPrint: string): void { - console.error(whatToPrint) - } - debug(whatToPrint: string): void { - console.debug(whatToPrint) - } - info(whatToPrint: string): void { - console.log(false) - } - is_sandboxed(): boolean { - return false - } - exists(input: { volumeId: string; path: string }): Promise { - return this.metadata(input) - .then(() => true) - .catch(() => false) - } - async fetch( - url: string, - options?: - | { - method?: - | "GET" - | "POST" - | "PUT" - | "DELETE" - | "HEAD" - | "PATCH" - | undefined - headers?: Record | undefined - body?: string | undefined - } - | undefined, - ): Promise<{ - method: string - ok: boolean - status: number - headers: Record - body?: string | null | undefined - text(): Promise - json(): Promise - }> { - const fetched = await fetch(url, options) - return { - method: fetched.type, - ok: fetched.ok, - status: fetched.status, - headers: Object.fromEntries(fetched.headers.entries()), - body: await fetched.text(), - text: () => fetched.text(), - json: () => fetched.json(), - } - } - - runRsync(rsyncOptions: { - srcVolume: string - dstVolume: string - srcPath: string - dstPath: string - options: oet.BackupOptions - }): { - id: () => Promise - wait: () => Promise - progress: () => Promise - } { - let secondRun: ReturnType | undefined - let firstRun = this._runRsync(rsyncOptions) - let waitValue = firstRun.wait().then((x) => { - secondRun = this._runRsync(rsyncOptions) - return secondRun.wait() - }) - const id = async () => { - return secondRun?.id?.() ?? firstRun.id() - } - const wait = () => waitValue - const progress = async () => { - const secondProgress = secondRun?.progress?.() - if (secondProgress) { - return (await secondProgress) / 2.0 + 0.5 - } - return (await firstRun.progress()) / 2.0 - } - return { id, wait, progress } - } - _runRsync(rsyncOptions: { - srcVolume: string - dstVolume: string - srcPath: string - dstPath: string - options: oet.BackupOptions - }): { - id: () => Promise - wait: () => Promise - progress: () => Promise - } { - const { srcVolume, dstVolume, srcPath, dstPath, options } = rsyncOptions - const command = "rsync" - const args: string[] = [] - if (options.delete) { - args.push("--delete") - } - if (options.force) { - args.push("--force") - } - if (options.ignoreExisting) { - args.push("--ignore-existing") - } - for (const exclude of options.exclude) { - args.push(`--exclude=${exclude}`) - } - args.push("-actAXH") - args.push("--info=progress2") - args.push("--no-inc-recursive") - args.push(new Volume(srcVolume, srcPath).path) - args.push(new Volume(dstVolume, dstPath).path) - const spawned = child_process.spawn(command, args, { detached: true }) - let percentage = 0.0 - spawned.stdout.on("data", (data: unknown) => { - const lines = String(data).replace("\r", "\n").split("\n") - for (const line of lines) { - const parsed = /$([0-9.]+)%/.exec(line)?.[1] - if (!parsed) continue - percentage = Number.parseFloat(parsed) - } - }) - - spawned.stderr.on("data", (data: unknown) => { - console.error(String(data)) - }) - - const id = async () => { - const pid = spawned.pid - if (pid === undefined) { - throw new Error("rsync process has no pid") - } - return String(pid) - } - const waitPromise = new Promise((resolve, reject) => { - spawned.on("exit", (code: any) => { - if (code === 0) { - resolve(null) - } else { - reject(new Error(`rsync exited with code ${code}`)) - } - }) - }) - const wait = () => waitPromise - const progress = () => Promise.resolve(percentage) - return { id, wait, progress } - } - async diskUsage( - options?: { volumeId: string; path: string } | undefined, - ): Promise<{ used: number; total: number }> { - const output = await execFile("df", ["--block-size=1", "-P", "/"]) - .then((x: any) => ({ - stderr: x.stderr.toString(), - stdout: x.stdout.toString(), - })) - .then((x: any) => { - if (!!x.stderr) { - throw new Error(x.stderr) - } - return parseDfOutput(x.stdout) - }) - if (!!options) { - const used = await execFile("du", [ - "-s", - "--block-size=1", - "-P", - new Volume(options.volumeId, options.path).path, - ]) + ], + }, + ) .then((x: any) => ({ stderr: x.stderr.toString(), stdout: x.stdout.toString(), @@ -396,15 +181,244 @@ export class PolyfillEffects implements oet.Effects { if (!!x.stderr) { throw new Error(x.stderr) } - return Number.parseInt(x.stdout.split(/\s+/)[0]) }) + return null + }, + async chmod(input: { + volumeId: string + path: string + mode: string + }): Promise { + await startSdk + .runCommand( + effects, + { id: manifest.main.image }, + ["chmod", "--recursive", input.mode, `/drive/${input.path}`], + { + mounts: [ + { + path: "/drive", + options: { + type: "volume", + id: input.volumeId, + subpath: null, + readonly: false, + }, + }, + ], + }, + ) + .then((x: any) => ({ + stderr: x.stderr.toString(), + stdout: x.stdout.toString(), + })) + .then((x: any) => { + if (!!x.stderr) { + throw new Error(x.stderr) + } + }) + return null + }, + sleep(timeMs: number): Promise { + return new Promise((resolve) => setTimeout(resolve, timeMs)) + }, + trace(whatToPrint: string): void { + console.trace(whatToPrint) + }, + warn(whatToPrint: string): void { + console.warn(whatToPrint) + }, + error(whatToPrint: string): void { + console.error(whatToPrint) + }, + debug(whatToPrint: string): void { + console.debug(whatToPrint) + }, + info(whatToPrint: string): void { + console.log(false) + }, + is_sandboxed(): boolean { + return false + }, + exists(input: { volumeId: string; path: string }): Promise { + return self + .metadata(input) + .then(() => true) + .catch(() => false) + }, + async fetch( + url: string, + options?: + | { + method?: + | "GET" + | "POST" + | "PUT" + | "DELETE" + | "HEAD" + | "PATCH" + | undefined + headers?: Record | undefined + body?: string | undefined + } + | undefined, + ): Promise<{ + method: string + ok: boolean + status: number + headers: Record + body?: string | null | undefined + text(): Promise + json(): Promise + }> { + const fetched = await fetch(url, options) return { - ...output, - used, + method: fetched.type, + ok: fetched.ok, + status: fetched.status, + headers: Object.fromEntries(fetched.headers.entries()), + body: await fetched.text(), + text: () => fetched.text(), + json: () => fetched.json(), } - } - return output + }, + + runRsync(rsyncOptions: { + srcVolume: string + dstVolume: string + srcPath: string + dstPath: string + options: oet.BackupOptions + }): { + id: () => Promise + wait: () => Promise + progress: () => Promise + } { + let secondRun: ReturnType | undefined + let firstRun = self._runRsync(rsyncOptions) + let waitValue = firstRun.wait().then((x) => { + secondRun = self._runRsync(rsyncOptions) + return secondRun.wait() + }) + const id = async () => { + return secondRun?.id?.() ?? firstRun.id() + } + const wait = () => waitValue + const progress = async () => { + const secondProgress = secondRun?.progress?.() + if (secondProgress) { + return (await secondProgress) / 2.0 + 0.5 + } + return (await firstRun.progress()) / 2.0 + } + return { id, wait, progress } + }, + _runRsync(rsyncOptions: { + srcVolume: string + dstVolume: string + srcPath: string + dstPath: string + options: oet.BackupOptions + }): { + id: () => Promise + wait: () => Promise + progress: () => Promise + } { + const { srcVolume, dstVolume, srcPath, dstPath, options } = rsyncOptions + const command = "rsync" + const args: string[] = [] + if (options.delete) { + args.push("--delete") + } + if (options.force) { + args.push("--force") + } + if (options.ignoreExisting) { + args.push("--ignore-existing") + } + for (const exclude of options.exclude) { + args.push(`--exclude=${exclude}`) + } + args.push("-actAXH") + args.push("--info=progress2") + args.push("--no-inc-recursive") + args.push(new Volume(srcVolume, srcPath).path) + args.push(new Volume(dstVolume, dstPath).path) + const spawned = child_process.spawn(command, args, { detached: true }) + let percentage = 0.0 + spawned.stdout.on("data", (data: unknown) => { + const lines = String(data).replace("\r", "\n").split("\n") + for (const line of lines) { + const parsed = /$([0-9.]+)%/.exec(line)?.[1] + if (!parsed) continue + percentage = Number.parseFloat(parsed) + } + }) + + spawned.stderr.on("data", (data: unknown) => { + console.error(String(data)) + }) + + const id = async () => { + const pid = spawned.pid + if (pid === undefined) { + throw new Error("rsync process has no pid") + } + return String(pid) + } + const waitPromise = new Promise((resolve, reject) => { + spawned.on("exit", (code: any) => { + if (code === 0) { + resolve(null) + } else { + reject(new Error(`rsync exited with code ${code}`)) + } + }) + }) + const wait = () => waitPromise + const progress = () => Promise.resolve(percentage) + return { id, wait, progress } + }, + async diskUsage( + options?: { volumeId: string; path: string } | undefined, + ): Promise<{ used: number; total: number }> { + const output = await execFile("df", ["--block-size=1", "-P", "/"]) + .then((x: any) => ({ + stderr: x.stderr.toString(), + stdout: x.stdout.toString(), + })) + .then((x: any) => { + if (!!x.stderr) { + throw new Error(x.stderr) + } + return parseDfOutput(x.stdout) + }) + if (!!options) { + const used = await execFile("du", [ + "-s", + "--block-size=1", + "-P", + new Volume(options.volumeId, options.path).path, + ]) + .then((x: any) => ({ + stderr: x.stderr.toString(), + stdout: x.stdout.toString(), + })) + .then((x: any) => { + if (!!x.stderr) { + throw new Error(x.stderr) + } + return Number.parseInt(x.stdout.split(/\s+/)[0]) + }) + return { + ...output, + used, + } + } + return output + }, } + return self } function parseDfOutput(output: string): { used: number; total: number } { diff --git a/container-runtime/src/Adapters/Systems/SystemForStartOs.ts b/container-runtime/src/Adapters/Systems/SystemForStartOs.ts index bf27f222d..e10434032 100644 --- a/container-runtime/src/Adapters/Systems/SystemForStartOs.ts +++ b/container-runtime/src/Adapters/Systems/SystemForStartOs.ts @@ -1,7 +1,7 @@ import { ExecuteResult, System } from "../../Interfaces/System" import { unNestPath } from "../../Models/JsonPath" import matches, { any, number, object, string, tuple } from "ts-matches" -import { HostSystemStartOs } from "../HostSystemStartOs" +import { hostSystemStartOs } from "../HostSystemStartOs" import { Effects } from "../../Models/Effects" import { RpcResult, matchRpcResult } from "../RpcListener" import { duration } from "../../Models/Duration" @@ -15,7 +15,7 @@ export class SystemForStartOs implements System { } constructor(readonly abi: T.ABI) {} async execute( - effects: HostSystemStartOs, + effectCreator: ReturnType, options: { id: string procedure: @@ -36,8 +36,7 @@ export class SystemForStartOs implements System { timeout?: number | undefined }, ): Promise { - effects = Object.create(effects) - effects.procedureId = options.id + const effects = effectCreator(options.id) return this._execute(effects, options) .then((x) => matches(x) diff --git a/container-runtime/src/Interfaces/HostSystem.ts b/container-runtime/src/Interfaces/HostSystem.ts index 4e04bbcc8..4ba986e3b 100644 --- a/container-runtime/src/Interfaces/HostSystem.ts +++ b/container-runtime/src/Interfaces/HostSystem.ts @@ -2,6 +2,7 @@ import { types as T } from "@start9labs/start-sdk" import { CallbackHolder } from "../Models/CallbackHolder" import { Effects } from "../Models/Effects" - export type HostSystem = Effects -export type GetHostSystem = (callbackHolder: CallbackHolder) => HostSystem +export type GetHostSystem = ( + callbackHolder: CallbackHolder, +) => (procedureId: null | string) => Effects diff --git a/container-runtime/src/Interfaces/System.ts b/container-runtime/src/Interfaces/System.ts index 85ba0fb0f..986288411 100644 --- a/container-runtime/src/Interfaces/System.ts +++ b/container-runtime/src/Interfaces/System.ts @@ -1,7 +1,7 @@ import { types as T } from "@start9labs/start-sdk" import { JsonPath } from "../Models/JsonPath" -import { HostSystemStartOs } from "../Adapters/HostSystemStartOs" import { RpcResult } from "../Adapters/RpcListener" +import { hostSystemStartOs } from "../Adapters/HostSystemStartOs" export type ExecuteResult = | { ok: unknown } | { err: { code: number; message: string } } @@ -12,7 +12,7 @@ export interface System { // stop(effects: Effects, options: { timeout: number, signal?: number }): Promise execute( - effects: T.Effects, + effectCreator: ReturnType, options: { id: string procedure: JsonPath diff --git a/container-runtime/src/index.ts b/container-runtime/src/index.ts index d86111ecb..74be5b73a 100644 --- a/container-runtime/src/index.ts +++ b/container-runtime/src/index.ts @@ -1,12 +1,12 @@ import { RpcListener } from "./Adapters/RpcListener" import { SystemForEmbassy } from "./Adapters/Systems/SystemForEmbassy" -import { HostSystemStartOs } from "./Adapters/HostSystemStartOs" +import { hostSystemStartOs } from "./Adapters/HostSystemStartOs" import { AllGetDependencies } from "./Interfaces/AllGetDependencies" import { getSystem } from "./Adapters/Systems" const getDependencies: AllGetDependencies = { system: getSystem, - hostSystem: () => HostSystemStartOs.of, + hostSystem: () => hostSystemStartOs, } new RpcListener(getDependencies) diff --git a/download-firmware.sh b/download-firmware.sh index 2457b3062..be72e6a6d 100755 --- a/download-firmware.sh +++ b/download-firmware.sh @@ -16,7 +16,8 @@ mkdir -p ./firmware/$PLATFORM cd ./firmware/$PLATFORM -mapfile -t firmwares <<< "$(jq -c ".[] | select(.platform[] | contains(\"$PLATFORM\"))" ../../build/lib/firmware.json)" +firmwares=() +while IFS= read -r line; do firmwares+=("$line"); done < <(jq -c ".[] | select(.platform[] | contains(\"$PLATFORM\"))" ../../build/lib/firmware.json) for firmware in "${firmwares[@]}"; do if [ -n "$firmware" ]; then id=$(echo "$firmware" | jq --raw-output '.id')