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 start9 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 start9 ui will generate, and later send to the set config */ export type getConfig = (options: { effects: Effects }) => 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 startOS UI */ export type createBackup = (options: { effects: Effects }) => Promise /** For restoring service data that was previously backed up using the startOS UI create backup flow. Backup restores are also triggered via the startOS 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 SmtpValue = { server: string port: number from: string login: string password: string | null | undefined tls: boolean } 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 /** Usable when not sandboxed */ appendFile(input: { path: string volumeId: string toWrite: string }): Promise /** * Move file from src to dst * Usable when not sandboxed */ moveFile(input: { srcVolume: string dstVolume: string srcPath: string dstPath: string }): Promise /** * copy from src to dst * Usable when not sandboxed */ copyFile(input: { srcVolume: string dstVolume: string srcPath: string dstPath: 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[]], options?: { timeoutMillis?: number }, ): Promise runDaemon( command: ValidIfNoStupidEscape | [string, ...string[]], options?: { env?: Record }, ): 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 console: { /** Log at the trace level */ log(whatToPrint: string): Promise /** Log at the trace level */ trace(whatToPrint: string): Promise /** Log at the warn level */ warn(whatToPrint: string): Promise /** Log at the error level */ error(whatToPrint: string): Promise /** Log at the debug level */ debug(whatToPrint: string): Promise /** Log at the info level */ info(whatToPrint: string): Promise } /** 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 }): 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> getSystemSmtp(input: { 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) : Path extends '' ? WrapperData : never // prettier-ignore type _EnsureWrapperDataPath = Path extends`/${infer A }/${infer Rest}` ? (WrapperData extends {[K in A & string]: infer NextWrapperData} ? _EnsureWrapperDataPath : never) : Path extends `/${infer A }` ? (WrapperData extends {[K in A]: any} ? Origin : never) : Path extends '' ? Origin : 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 | { value: 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