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 // /** 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]: { run: ExportedAction getConfig: (options: { effects: Effects }) => Promise } } /** * 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 allowedStatuses: "only-running" | "only-stopped" | "any" /** * So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions */ group?: string } export type NetworkInterface = { id: string /** The title of this field to be dsimplayed */ name: string /** Human readable description, used as tooltip usually */ description: string /** All URIs */ addresses: string[] /** Defaults to false, but describes if this address can be opened in a browser as an * ui interface */ ui?: boolean } /** Used to reach out from the pure js runtime */ export type Effects = { runCommand( command: ValidIfNoStupidEscape | [string, ...string[]], options?: { timeoutMillis?: number env?: Record }, ): 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 /** 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 IP address of the container */ getContainerIp(): 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 */ exportNetworkInterface(options: NetworkInterface): Promise /** * There are times that we want to see the addresses that where exported * @param options.addressId If we want to filter the address id * * Note: any auth should be filtered out already */ getInterface(options: { packageId?: PackageId interfaceId?: string }): 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 /** Instead of creating proxies with nginx, we have a utility to create and maintain a proxy in the lifetime of this running. */ reverseProxy(options: { bind: { /** Optional, default is 0.0.0.0 */ ip?: string port: number ssl: boolean } dst: { /** Optional: default is 127.0.0.1 */ ip?: string // optional, default 127.0.0.1 port: number ssl: boolean } http?: { // optional, will do TCP layer proxy only if not present headers?: (headers: Record) => Record } }): Promise<{ stop(): Promise }> restart(): void shutdown(): void mount(options: { location: { volumeId: string path: string } target: { packageId: string volumeId: string path: string readonly: boolean } }): Promise stopped(packageId?: string): Promise vault: { list(): Promise set(opt: { key: string; value: string }): Promise move(opt: { fromKey: string; toKey: string }): Promise delete(opt: { key: string }): Promise } } // 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]: infer B} ? 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 Dependency = { id: PackageId kind: DependencyKind } export type Dependencies = Array export type DeepPartial = T extends {} ? { [P in keyof T]?: DeepPartial } : T