export * as configTypes from "./config/configTypes"; import { InputSpec } from "./config/configTypes"; import { DependenciesReceipt } from "./config/setupConfig"; export type ExportedAction = (options: { effects: Effects; input?: Record; }) => Promise; export namespace ExpectedExports { version: 1; /** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */ export type setConfig = (options: { effects: Effects; input: Record; }) => Promise; /** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */ export type getConfig = (options: { effects: Effects; config: unknown; }) => Promise; // /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ // export type dependencies = Dependencies; /** For backing up service data though the embassyOS UI */ export type createBackup = (options: { effects: Effects; }) => Promise; /** For restoring service data that was previously backed up using the embassyOS UI create backup flow. Backup restores are also triggered via the embassyOS UI, or doing a system restore flow during setup. */ export type restoreBackup = (options: { effects: Effects; }) => Promise; /** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */ export type properties = (options: { wrapperData: WrapperData; }) => Promise; // /** Health checks are used to determine if the service is working properly after starting // * A good use case is if we are using a web server, seeing if we can get to the web server. // */ // export type health = { // /** Should be the health check id */ // [id: string]: (options: { effects: Effects; input: TimeMs }) => Promise; // }; /** * Actions are used so we can effect the service, like deleting a directory. * One old use case is to add a action where we add a file, that will then be run during the * service starting, and that file would indicate that it would rescan all the data. */ export type actions = { [id: string]: ExportedAction; }; /** * This is the entrypoint for the main container. Used to start up something like the service that the * package represents, like running a bitcoind in a bitcoind-wrapper. */ export type main = (options: { effects: Effects; started(onTerm: () => void): null; }) => Promise; /** * After a shutdown, if we wanted to do any operations to clean up things, like * set the action as unavailable or something. */ export type afterShutdown = (options: { effects: Effects; }) => Promise; /** * Every time a package completes an install, this function is called before the main. * Can be used to do migration like things. */ export type init = (options: { effects: Effects; previousVersion: null | string; }) => Promise; /** This will be ran during any time a package is uninstalled, for example during a update * this will be called. */ export type uninit = (options: { effects: Effects; nextVersion: null | string; }) => Promise; /** Auto configure is used to make sure that other dependencies have the values t * that this service could use. */ export type autoConfig = Record; } export type TimeMs = number; export type VersionString = string; /** * AutoConfigure is used as the value to the key of package id, * this is used to make sure that other dependencies have the values that this service could use. */ export type AutoConfigure = { /** Checks are called to make sure that our dependency is in the correct shape. If a known error is returned we know that the dependency needs modification */ check(options: { effects: Effects; localConfig: unknown; remoteConfig: unknown; }): Promise; /** This is called after we know that the dependency package needs a new configuration, this would be a transform for defaults */ autoConfigure(options: { effects: Effects; localConfig: unknown; remoteConfig: unknown; }): Promise; }; export type ValidIfNoStupidEscape = A extends | `${string}'"'"'${string}` | `${string}\\"${string}` ? never : "" extends A & "" ? never : A; export type ConfigRes = { /** This should be the previous config, that way during set config we start with the previous */ config?: null | Record; /** Shape that is describing the form in the ui */ spec: InputSpec; }; declare const DaemonProof: unique symbol; export type DaemonReceipt = { [DaemonProof]: never; }; export type Daemon = { wait(): Promise; term(): Promise; [DaemonProof]: never; }; export type HealthStatus = "passing" | "warning" | "failing" | "disabled"; export type CommandType = | ValidIfNoStupidEscape | [string, ...string[]]; export type DaemonReturned = { wait(): Promise; term(): Promise; }; export type ActionMetaData = { name: string; description: string; id: string; input: null | InputSpec; runningOnly: boolean; /** * So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions */ group?: string; }; /** Used to reach out from the pure js runtime */ export type Effects = { /** Usable when not sandboxed */ writeFile(input: { path: string; volumeId: string; toWrite: string; }): Promise; readFile(input: { volumeId: string; path: string }): Promise; metadata(input: { volumeId: string; path: string }): Promise; /** Create a directory. Usable when not sandboxed */ createDir(input: { volumeId: string; path: string }): Promise; readDir(input: { volumeId: string; path: string }): Promise; /** Remove a directory. Usable when not sandboxed */ removeDir(input: { volumeId: string; path: string }): Promise; removeFile(input: { volumeId: string; path: string }): Promise; /** Write a json file into an object. Usable when not sandboxed */ writeJsonFile(input: { volumeId: string; path: string; toWrite: Record; }): Promise; /** Read a json file into an object */ readJsonFile(input: { volumeId: string; path: string; }): Promise>; runCommand( command: ValidIfNoStupidEscape | [string, ...string[]], input?: { timeoutMillis?: number; }, ): Promise; runShellDaemon(command: string): { wait(): Promise; term(): Promise; }; runDaemon( command: ValidIfNoStupidEscape | [string, ...string[]], ): DaemonReturned; /** Uses the chown on the system */ chown(input: { volumeId: string; path: string; uid: string }): Promise; /** Uses the chmod on the system */ chmod(input: { volumeId: string; path: string; mode: string }): Promise; sleep(timeMs: TimeMs): Promise; /** Log at the trace level */ trace(whatToPrint: string): void; /** Log at the warn level */ warn(whatToPrint: string): void; /** Log at the error level */ error(whatToPrint: string): void; /** Log at the debug level */ debug(whatToPrint: string): void; /** Log at the info level */ info(whatToPrint: string): void; /** Sandbox mode lets us read but not write */ is_sandboxed(): boolean; /** Check that a file exists or not */ exists(input: { volumeId: string; path: string }): Promise; /** Declaring that we are opening a interface on some protocal for local network * Returns the port exposed */ bindLan(options: { internalPort: number; name: string }): Promise; /** Declaring that we are opening a interface on some protocal for tor network */ bindTor(options: { internalPort: number; name: string; externalPort: number; }): Promise; /** Similar to the fetch api via the mdn, this is simplified but the point is * to get something from some website, and return the response. */ fetch( url: string, options?: { method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH"; headers?: Record; body?: string; }, ): Promise<{ method: string; ok: boolean; status: number; headers: Record; body?: string | null; /// Returns the body as a string text(): Promise; /// Returns the body as a json json(): Promise; }>; /** * Run rsync between two volumes. This is used to backup data between volumes. * This is a long running process, and a structure that we can either wait for, or get the progress of. */ runRsync(options: { srcVolume: string; dstVolume: string; srcPath: string; dstPath: string; // rsync options: https://linux.die.net/man/1/rsync options: BackupOptions; }): { id: () => Promise; wait: () => Promise; progress: () => Promise; }; /** Get a value in a json like data, can be observed and subscribed */ getWrapperData(options: { /** If there is no packageId it is assumed the current package */ packageId?: string; /** The path defaults to root level, using the [JsonPath](https://jsonpath.com/) */ path: Path & EnsureWrapperDataPath; callback: (config: unknown, previousConfig: unknown) => void; }): Promise>; /** Used to store values that can be accessed and subscribed to */ setWrapperData(options: { /** Sets the value for the wrapper at the path, it will override, using the [JsonPath](https://jsonpath.com/) */ path: Path & EnsureWrapperDataPath; value: ExtractWrapperData; }): Promise; getLocalHostname(): Promise; getIPHostname(): Promise; /** Get the address for another service for tor interfaces */ getServiceTorHostname( interfaceId: string, packageId?: string, ): Promise; /** * Get the port address for another service */ getServicePortForward( internalPort: number, packageId?: string, ): Promise; /** When we want to create a link in the front end interfaces, and example is * exposing a url to view a web service */ exportAddress(options: { /** The title of this field to be dsimplayed */ name: string; /** Human readable description, used as tooltip usually */ description: string; /** URI location */ address: string; id: string; /** Defaults to false, but describes if this address can be opened in a browser as an * ui interface */ ui?: boolean; /** * The id is that a path will create a link in the ui that can go to specific pages, like * admin, or settings, or something like that. * Default = '' */ path?: string; /** * This is the query params in the url, and is a map of key value pairs * Default = {} * if empty then will not be added to the url */ search?: Record; }): Promise; /** *Remove an address that was exported. Used problably during main or during setConfig. * @param options */ removeAddress(options: { id: string }): Promise; /** * * @param options */ exportAction(options: ActionMetaData): Promise; /** * Remove an action that was exported. Used problably during main or during setConfig. */ removeAction(options: { id: string }): Promise; getConfigured(): Promise; /** * This called after a valid set config as well as during init. * @param configured */ setConfigured(configured: boolean): Promise; /** * * @returns PEM encoded fullchain (ecdsa) */ getSslCertificate: ( packageId: string, algorithm?: "ecdsa" | "ed25519", ) => [string, string, string]; /** * @returns PEM encoded ssl key (ecdsa) */ getSslKey: (packageId: string, algorithm?: "ecdsa" | "ed25519") => string; setHealth(o: { name: string; status: HealthStatus; message?: string; }): Promise; /** Set the dependencies of what the service needs, usually ran during the set config as a best practice */ setDependencies(dependencies: Dependencies): Promise; /** Exists could be useful during the runtime to know if some service exists, option dep */ exists(packageId: PackageId): Promise; /** Exists could be useful during the runtime to know if some service is running, option dep */ running(packageId: PackageId): Promise; restart(): void; shutdown(): void; }; // prettier-ignore export type ExtractWrapperData = Path extends `/${infer A }/${infer Rest }` ? (A extends keyof WrapperData ? ExtractWrapperData : never) : Path extends `/${infer A }` ? (A extends keyof WrapperData ? WrapperData[A] : never) : never // prettier-ignore type _EnsureWrapperDataPath = Path extends `/${infer A }/${infer Rest }` ? (A extends keyof WrapperData ? ExtractWrapperData : never) : Path extends `/${infer A }` ? (A extends keyof WrapperData ? Origin : never) : never // prettier-ignore export type EnsureWrapperDataPath = _EnsureWrapperDataPath /* rsync options: https://linux.die.net/man/1/rsync */ export type BackupOptions = { delete: boolean; force: boolean; ignoreExisting: boolean; exclude: string[]; }; /** * This is the metadata that is returned from the metadata call. */ export type Metadata = { fileType: string; isDir: boolean; isFile: boolean; isSymlink: boolean; len: number; modified?: Date; accessed?: Date; created?: Date; readonly: boolean; uid: number; gid: number; mode: number; }; export type MigrationRes = { configured: boolean; }; export type ActionResult = { message: string; value: null | string; copyable: boolean; qr: boolean; }; export type SetResult = { /** These are the unix process signals */ signal: | "SIGTERM" | "SIGHUP" | "SIGINT" | "SIGQUIT" | "SIGILL" | "SIGTRAP" | "SIGABRT" | "SIGBUS" | "SIGFPE" | "SIGKILL" | "SIGUSR1" | "SIGSEGV" | "SIGUSR2" | "SIGPIPE" | "SIGALRM" | "SIGSTKFLT" | "SIGCHLD" | "SIGCONT" | "SIGSTOP" | "SIGTSTP" | "SIGTTIN" | "SIGTTOU" | "SIGURG" | "SIGXCPU" | "SIGXFSZ" | "SIGVTALRM" | "SIGPROF" | "SIGWINCH" | "SIGIO" | "SIGPWR" | "SIGSYS" | "SIGEMT" | "SIGINFO"; "depends-on": DependsOn; }; export type PackageId = string; export type Message = string; export type DependencyKind = "running" | "exists"; export type DependsOn = { [packageId: string]: string[]; }; export type KnownError = | { error: string } | { "error-code": [number, string] | readonly [number, string]; }; export type PackageProperties = PackagePropertyGroup | PackagePropertyString; export type PackagePropertyString = { type: "string"; name: string; description: string | null; value: string; /** Let's the ui make this copyable button */ copyable: boolean; /** Let the ui create a qr for this field */ qr: boolean; /** Hiding the value unless toggled off for field */ masked: boolean; }; export type PackagePropertyGroup = { value: PackageProperties[]; type: "object"; name: string; description: string; }; export type Properties = PackageProperties[]; export type Dependency = { id: PackageId; kind: DependencyKind; }; export type Dependencies = Array; export type DeepPartial = T extends {} ? { [P in keyof T]?: DeepPartial } : T;