Files
start-os/sdk/lib/types.ts
Aiden McClelland 089199e7c2 Feature/lxc container runtime (#2562)
* wip(fix): Dependencies

* wip: context

* wip(fix) Sorta auth

* wip: warnings

* wip(fix): registry/admin

* wip(fix) marketplace

* wip(fix) Some more converted and fixed with the linter and config

* wip: Working on the static server

* wip(fix)static server

* wip: Remove some asynnc

* wip: Something about the request and regular rpc

* wip: gut install

Co-authored-by: J H <Blu-J@users.noreply.github.com>

* wip: Convert the static server into the new system

* wip delete file

* test

* wip(fix) vhost does not need the with safe defaults

* wip: Adding in the wifi

* wip: Fix the developer and the verify

* wip: new install flow

Co-authored-by: J H <Blu-J@users.noreply.github.com>

* fix middleware

* wip

* wip: Fix the auth

* wip

* continue service refactor

* feature: Service get_config

* feat: Action

* wip: Fighting the great fight against the borrow checker

* wip: Remove an error in a file that I just need to deel with later

* chore: Add in some more lifetime stuff to the services

* wip: Install fix on lifetime

* cleanup

* wip: Deal with the borrow later

* more cleanup

* resolve borrowchecker errors

* wip(feat): add in the handler for the socket, for now

* wip(feat): Update the service_effect_handler::action

* chore: Add in the changes to make sure the from_service goes to context

* chore: Change the

* refactor service map

* fix references to service map

* fill out restore

* wip: Before I work on the store stuff

* fix backup module

* handle some warnings

* feat: add in the ui components on the rust side

* feature: Update the procedures

* chore: Update the js side of the main and a few of the others

* chore: Update the rpc listener to match the persistant container

* wip: Working on updating some things to have a better name

* wip(feat): Try and get the rpc to return the correct shape?

* lxc wip

* wip(feat): Try and get the rpc to return the correct shape?

* build for container runtime wip

* remove container-init

* fix build

* fix error

* chore: Update to work I suppose

* lxc wip

* remove docker module and feature

* download alpine squashfs automatically

* overlays effect

Co-authored-by: Jade <Blu-J@users.noreply.github.com>

* chore: Add the overlay effect

* feat: Add the mounter in the main

* chore: Convert to use the mounts, still need to work with the sandbox

* install fixes

* fix ssl

* fixes from testing

* implement tmpfile for upload

* wip

* misc fixes

* cleanup

* cleanup

* better progress reporting

* progress for sideload

* return real guid

* add devmode script

* fix lxc rootfs path

* fix percentage bar

* fix progress bar styling

* fix build for unstable

* tweaks

* label progress

* tweaks

* update progress more often

* make symlink in rpc_client

* make socket dir

* fix parent path

* add start-cli to container

* add echo and gitInfo commands

* wip: Add the init + errors

* chore: Add in the exit effect for the system

* chore: Change the type to null for failure to parse

* move sigterm timeout to stopping status

* update order

* chore: Update the return type

* remove dbg

* change the map error

* chore: Update the thing to capture id

* chore add some life changes

* chore: Update the loging

* chore: Update the package to run module

* us From for RpcError

* chore: Update to use import instead

* chore: update

* chore: Use require for the backup

* fix a default

* update the type that is wrong

* chore: Update the type of the manifest

* chore: Update to make null

* only symlink if not exists

* get rid of double result

* better debug info for ErrorCollection

* chore: Update effects

* chore: fix

* mount assets and volumes

* add exec instead of spawn

* fix mounting in image

* fix overlay mounts

Co-authored-by: Jade <Blu-J@users.noreply.github.com>

* misc fixes

* feat: Fix two

* fix: systemForEmbassy main

* chore: Fix small part of main loop

* chore: Modify the bundle

* merge

* fixMain loop"

* move tsc to makefile

* chore: Update the return types of the health check

* fix client

* chore: Convert the todo to use tsmatches

* add in the fixes for the seen and create the hack to allow demo

* chore: Update to include the systemForStartOs

* chore UPdate to the latest types from the expected outout

* fixes

* fix typo

* Don't emit if failure on tsc

* wip

Co-authored-by: Jade <Blu-J@users.noreply.github.com>

* add s9pk api

* add inspection

* add inspect manifest

* newline after display serializable

* fix squashfs in image name

* edit manifest

Co-authored-by: Jade <Blu-J@users.noreply.github.com>

* wait for response on repl

* ignore sig for now

* ignore sig for now

* re-enable sig verification

* fix

* wip

* env and chroot

* add profiling logs

* set uid & gid in squashfs to 100000

* set uid of sqfs to 100000

* fix mksquashfs args

* add env to compat

* fix

* re-add docker feature flag

* fix docker output format being stupid

* here be dragons

* chore: Add in the cross compiling for something

* fix npm link

* extract logs from container on exit

* chore: Update for testing

* add log capture to drop trait

* chore: add in the modifications that I make

* chore: Update small things for no updates

* chore: Update the types of something

* chore: Make main not complain

* idmapped mounts

* idmapped volumes

* re-enable kiosk

* chore: Add in some logging for the new system

* bring in start-sdk

* remove avahi

* chore: Update the deps

* switch to musl

* chore: Update the version of prettier

* chore: Organize'

* chore: Update some of the headers back to the standard of fetch

* fix musl build

* fix idmapped mounts

* fix cross build

* use cross compiler for correct arch

* feat: Add in the faked ssl stuff for the effects

* @dr_bonez Did a solution here

* chore: Something that DrBonez

* chore: up

* wip: We have a working server!!!

* wip

* uninstall

* wip

* tes

* misc fixes

* fix cli

* replace interface with host

* chore: Fix the types in some ts files

* chore: quick update for the system for embassy to update the types

* replace br-start9 with lxcbr0

* split patchdb into public/private

* chore: Add changes for config set

* Feat: Adding some debugging for the errors

* wip: Working on getting the set config to work

* chore: Update and fix the small issue with the deserialization

* lightning, masked, schemeOverride, invert host-iface relationship

* feat: Add in the changes for just the sdk

* feat: Add in the changes for the new effects I suppose for now

---------

Co-authored-by: J H <2364004+Blu-J@users.noreply.github.com>
Co-authored-by: J H <Blu-J@users.noreply.github.com>
Co-authored-by: J H <dragondef@gmail.com>
Co-authored-by: Matt Hill <mattnine@protonmail.com>
2024-02-22 21:00:49 +00:00

570 lines
18 KiB
TypeScript

export * as configTypes from "./config/configTypes"
import { InputSpec } from "./config/configTypes"
import { DependenciesReceipt } from "./config/setupConfig"
import { BindOptions } from "./interfaces/Host"
import { Daemons } from "./mainFn/Daemons"
import { UrlString } from "./util/getServiceInterface"
import { ServiceInterfaceType, Signals } from "./util/utils"
export type ExportedAction = (options: {
effects: Effects
input?: Record<string, unknown>
}) => Promise<ActionResult>
export type MaybePromise<A> = Promise<A> | A
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<string, unknown>
}) => Promise<void>
/** 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<ConfigRes>
// /** 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<unknown>
/** 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<unknown>
// /** 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<unknown>;
// };
/**
* 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 = (options: { effects: Effects }) => MaybePromise<{
[id: string]: {
run: ExportedAction
getConfig: (options: { effects: Effects }) => Promise<InputSpec>
}
}>
export type actionsMetadata = (options: {
effects: Effects
}) => Promise<Array<ActionMetadata>>
/**
* 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: () => PromiseLike<void>): PromiseLike<void>
}) => Promise<Daemons<any, any>>
/**
* 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<unknown>
/**
* 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<unknown>
/** 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<unknown>
/** Auto configure is used to make sure that other dependencies have the values t
* that this service could use.
*/
export type dependencyConfig = Record<PackageId, DependencyConfig>
}
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 DependencyConfig = {
/** During autoconfigure, we have access to effects and local data. We are going to figure out all the data that we need and send it to update. For the sdk it is the desired delta */
query(options: { effects: Effects; localConfig: unknown }): Promise<unknown>
/** This is the second part. Given the query results off the previous function, we will determine what to change the remote config to. In our sdk normall we are going to use the previous as a deep merge. */
update(options: {
queryResults: unknown
remoteConfig: unknown
}): Promise<unknown>
}
export type ValidIfNoStupidEscape<A> = 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<string, unknown>
/** 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<string>
term(): Promise<void>
[DaemonProof]: never
}
export type HealthStatus = "passing" | "warning" | "failing" | "disabled"
export type SmtpValue = {
server: string
port: number
from: string
login: string
password: string | null | undefined
}
export type CommandType<A extends string> =
| ValidIfNoStupidEscape<A>
| [string, ...string[]]
export type DaemonReturned = {
wait(): Promise<null>
term(options?: { signal?: Signals; timeout?: number }): Promise<void>
}
export type ActionMetadata = {
name: string
description: string
id: string
input: InputSpec
allowedStatuses: "only-running" | "only-stopped" | "any" | "disabled"
/**
* So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions
*/
group?: string
}
export declare const hostName: unique symbol
// asdflkjadsf.onion | 1.2.3.4
export type Hostname = string & { [hostName]: never }
/** ${scheme}://${username}@${host}:${externalPort}${suffix} */
export type AddressInfo = {
username: string | null
hostId: string
bindOptions: BindOptions
suffix: string
}
export type HostnameInfoIp = {
kind: "ip"
networkInterfaceId: string
public: boolean
hostname:
| {
kind: "ipv4" | "ipv6" | "local"
value: string
port: number | null
sslPort: number | null
}
| {
kind: "domain"
domain: string
subdomain: string | null
port: number | null
sslPort: number | null
}
}
export type HostnameInfoOnion = {
kind: "onion"
hostname: { value: string; port: number | null; sslPort: number | null }
}
export type HostnameInfo = HostnameInfoIp | HostnameInfoOnion
export type SingleHost = {
id: string
kind: "single" | "static"
hostname: HostnameInfo | null
}
export type MultiHost = {
id: string
kind: "multi"
hostnames: HostnameInfo[]
}
export type HostInfo = SingleHost | MultiHost
export type ServiceInterfaceId = string
export type ServiceInterface = {
id: ServiceInterfaceId
/** The title of this field to be displayed */
name: string
/** Human readable description, used as tooltip usually */
description: string
/** Whether or not one address must be the primary address */
hasPrimary: boolean
/** Disabled interfaces do not serve, but they retain their metadata and addresses */
disabled: boolean
/** Whether or not to mask the URIs for this interface. Useful if the URIs contain sensitive information, such as a password, macaroon, or API key */
masked: boolean
/** URI Information */
addressInfo: AddressInfo
/** The network interface could be several types, something like ui, p2p, or network */
type: ServiceInterfaceType
}
export type ServiceInterfaceWithHostInfo = ServiceInterface & {
hostInfo: HostInfo
}
// prettier-ignore
export type ExposeAllServicePaths<Store, PreviousPath extends string = ""> =
Store extends Record<string, unknown> ? {[K in keyof Store & string]: ExposeAllServicePaths<Store[K], `${PreviousPath}/${K & string}`>}[keyof Store & string] :
PreviousPath
// prettier-ignore
export type ExposeAllUiPaths<Store, PreviousPath extends string = ""> =
Store extends Record<string, unknown> ? {[K in keyof Store & string]: ExposeAllUiPaths<Store[K], `${PreviousPath}/${K & string}`>}[keyof Store & string] :
Store extends string ? PreviousPath :
never
export type ExposeServicePaths<Store> = Array<{
/** The path to the value in the Store. [JsonPath](https://jsonpath.com/) */
path: ExposeAllServicePaths<Store>
}>
export type ExposeUiPaths<Store> = Array<{
/** The path to the value in the Store. [JsonPath](https://jsonpath.com/) */
path: ExposeAllUiPaths<Store>
/** A human readable title for the value */
title: string
/** A human readable description or explanation of the value */
description?: string
/** (string/number only) Whether or not to mask the value, for example, when displaying a password */
masked?: boolean
/** (string/number only) Whether or not to include a button for copying the value to clipboard */
copyable?: boolean
/** (string/number only) Whether or not to include a button for displaying the value as a QR code */
qr?: boolean
}>
/** Used to reach out from the pure js runtime */
export type Effects = {
executeAction<Input>(opts: {
serviceId?: string
input: Input
}): Promise<unknown>
/** A low level api used by makeOverlay */
createOverlayedImage(options: { imageId: string }): Promise<string>
/** Removes all network bindings */
clearBindings(): Promise<void>
/** Creates a host connected to the specified port with the provided options */
bind(
options: {
kind: "static" | "single" | "multi"
id: string
internalPort: number
} & BindOptions,
): Promise<void>
/** Retrieves the current hostname(s) associated with a host id */
getHostInfo(options: {
kind: "static" | "single"
serviceInterfaceId: string
packageId?: string
callback: () => void
}): Promise<SingleHost>
getHostInfo(options: {
kind?: "multi"
serviceInterfaceId: string
packageId?: string
callback: () => void
}): Promise<MultiHost>
// /**
// * 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<string>
// wait: () => Promise<null>
// progress: () => Promise<number>
// }
store: {
/** Get a value in a json like data, can be observed and subscribed */
get<Store = never, Path extends string = never>(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 & EnsureStorePath<Store, Path>
callback: (config: unknown, previousConfig: unknown) => void
}): Promise<ExtractStore<Store, Path>>
/** Used to store values that can be accessed and subscribed to */
set<Store = never, Path extends string = never>(options: {
/** Sets the value for the wrapper at the path, it will override, using the [JsonPath](https://jsonpath.com/) */
path: Path & EnsureStorePath<Store, Path>
value: ExtractStore<Store, Path>
}): Promise<void>
}
getSystemSmtp(input: {
callback: (config: unknown, previousConfig: unknown) => void
}): Promise<SmtpValue>
/** Get the IP address of the container */
getContainerIp(): Promise<string>
/**
* Get the port address for another service
*/
getServicePortForward(
internalPort: number,
packageId?: string,
): Promise<number>
/** Removes all network interfaces */
clearServiceInterfaces(): Promise<void>
/** When we want to create a link in the front end interfaces, and example is
* exposing a url to view a web service
*/
exportServiceInterface(options: ServiceInterface): Promise<string>
exposeForDependents<Store = never>(
options: ExposeServicePaths<Store>,
): Promise<void>
exposeUi<Store = never>(options: ExposeUiPaths<Store>): Promise<void>
/**
* 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
*/
getServiceInterface(options: {
packageId?: PackageId
serviceInterfaceId: ServiceInterfaceId
callback: () => void
}): Promise<ServiceInterface>
/**
* The user sets the primary url for a interface
* @param options
*/
getPrimaryUrl(options: {
packageId?: PackageId
serviceInterfaceId: ServiceInterfaceId
callback: () => void
}): Promise<UrlString | null>
/**
* 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
*/
listServiceInterfaces(options: {
packageId?: PackageId
callback: () => void
}): Promise<ServiceInterface[]>
/**
*Remove an address that was exported. Used problably during main or during setConfig.
* @param options
*/
removeAddress(options: { id: string }): Promise<void>
/**
*
* @param options
*/
exportAction(options: ActionMetadata): Promise<void>
/**
* Remove an action that was exported. Used problably during main or during setConfig.
*/
removeAction(options: { id: string }): Promise<void>
getConfigured(): Promise<boolean>
/**
* This called after a valid set config as well as during init.
* @param configured
*/
setConfigured(configured: boolean): Promise<void>
/**
*
* @returns PEM encoded fullchain (ecdsa)
*/
getSslCertificate: (
packageId: string | null,
hostId: string,
algorithm?: "ecdsa" | "ed25519",
) => Promise<[string, string, string]>
/**
* @returns PEM encoded ssl key (ecdsa)
*/
getSslKey: (
packageId: string | null,
hostId: string,
algorithm?: "ecdsa" | "ed25519",
) => Promise<string>
setHealth(o: {
name: string
status: HealthStatus
message?: string
}): Promise<void>
/** Set the dependencies of what the service needs, usually ran during the set config as a best practice */
setDependencies(dependencies: Dependencies): Promise<DependenciesReceipt>
/** Exists could be useful during the runtime to know if some service exists, option dep */
exists(packageId: PackageId): Promise<boolean>
/** Exists could be useful during the runtime to know if some service is running, option dep */
running(packageId: PackageId): Promise<boolean>
/** 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<string, string>) => Record<string, string>
}
}): Promise<{ stop(): Promise<void> }>
restart(): void
shutdown(): void
mount(options: {
location: string
target: {
packageId: string
volumeId: string
path: string
readonly: boolean
}
}): Promise<string>
stopped(packageId?: string): Promise<boolean>
}
// prettier-ignore
export type ExtractStore<Store, Path extends string> =
Path extends `/${infer A }/${infer Rest }` ? (A extends keyof Store ? ExtractStore<Store[A], `/${Rest}`> : never) :
Path extends `/${infer A }` ? (A extends keyof Store ? Store[A] : never) :
Path extends '' ? Store :
never
// prettier-ignore
type _EnsureStorePath<Store, Path extends string, Origin extends string> =
Path extends`/${infer A }/${infer Rest}` ? (Store extends {[K in A & string]: infer NextStore} ? _EnsureStorePath<NextStore, `/${Rest}`, Origin> : never) :
Path extends `/${infer A }` ? (Store extends {[K in A]: infer B} ? Origin : never) :
Path extends '' ? Origin :
never
// prettier-ignore
export type EnsureStorePath<Store, Path extends string> = _EnsureStorePath<Store, Path, Path>
/** 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: Signals
"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<Dependency>
export type DeepPartial<T> = T extends {}
? { [P in keyof T]?: DeepPartial<T[P]> }
: T