Feature/consolidate setup (#3092)

* start consolidating

* add start-cli flash-os

* combine install and setup and refactor all

* use http

* undo mock

* fix translation

* translations

* use dialogservice wrapper

* better ST messaging on setup

* only warn on update if breakages (#3097)

* finish setup wizard and ui language-keyboard feature

* fix typo

* wip: localization

* remove start-tunnel readme

* switch to posix strings for language internal

* revert mock

* translate backend strings

* fix missing about text

* help text for args

* feat: add "Add new gateway" option (#3098)

* feat: add "Add new gateway" option

* Update web/projects/ui/src/app/routes/portal/components/form/controls/select.component.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add translation

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Matt Hill <mattnine@protonmail.com>

* fix dns selection

* keyboard keymap also

* ability to shutdown after install

* revert mock

* working setup flow + manifest localization

* (mostly) redundant localization on frontend

* version bump

* omit live medium from disk list and better space management

* ignore missing package archive on 035 migration

* fix device migration

* add i18n helper to sdk

* fix install over 0.3.5.1

* fix grub config

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
Co-authored-by: Alex Inkin <alexander@inkin.ru>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Aiden McClelland
2026-01-27 14:44:41 -08:00
committed by GitHub
parent 99871805bd
commit c65db31fd9
251 changed files with 12163 additions and 3966 deletions

View File

@@ -1,3 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { LocaleString } from "./LocaleString"
export type AddCategoryParams = { id: string; name: string }
export type AddCategoryParams = { id: string; name: LocaleString }

View File

@@ -1,9 +1,10 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { LocaleString } from "./LocaleString"
export type Alerts = {
install: string | null
uninstall: string | null
restore: string | null
start: string | null
stop: string | null
install: LocaleString | null
uninstall: LocaleString | null
restore: LocaleString | null
start: LocaleString | null
stop: LocaleString | null
}

View File

@@ -2,7 +2,7 @@
import type { EncryptedWire } from "./EncryptedWire"
export type AttachParams = {
startOsPassword: EncryptedWire | null
password: EncryptedWire | null
guid: string
kiosk?: boolean
}

View File

@@ -1,3 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { LocaleString } from "./LocaleString"
export type Category = { name: string }
export type Category = { name: LocaleString }

View File

@@ -1,8 +1,9 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { DataUrl } from "./DataUrl"
import type { LocaleString } from "./LocaleString"
export type CurrentDependencyInfo = {
title: string | null
title: LocaleString | null
icon: DataUrl | null
versionRange: string
} & ({ kind: "exists" } | { kind: "running"; healthChecks: string[] })

View File

@@ -1,9 +1,10 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { DataUrl } from "./DataUrl"
import type { LocaleString } from "./LocaleString"
export type DependencyMetadata = {
title: string | null
title: LocaleString | null
icon: DataUrl | null
description: string | null
description: LocaleString | null
optional: boolean
}

View File

@@ -1,3 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { LocaleString } from "./LocaleString"
export type Description = { short: string; long: string }
export type Description = { short: LocaleString; long: LocaleString }

View File

@@ -0,0 +1,9 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type KeyboardOptions = {
layout: string
keymap: string | null
model: string | null
variant: string | null
options: Array<string>
}

View File

@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type LocaleString = string | Record<string, string>

View File

@@ -6,6 +6,7 @@ import type { GitHash } from "./GitHash"
import type { HardwareRequirements } from "./HardwareRequirements"
import type { ImageConfig } from "./ImageConfig"
import type { ImageId } from "./ImageId"
import type { LocaleString } from "./LocaleString"
import type { PackageId } from "./PackageId"
import type { Version } from "./Version"
import type { VolumeId } from "./VolumeId"
@@ -15,7 +16,7 @@ export type Manifest = {
title: string
version: Version
satisfies: Array<Version>
releaseNotes: string
releaseNotes: LocaleString
canMigrateTo: string
canMigrateFrom: string
license: string

View File

@@ -1,4 +1,5 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { LocaleString } from "./LocaleString"
import type { PathOrUrl } from "./PathOrUrl"
export type Metadata = { title: string; icon: PathOrUrl }
export type Metadata = { title: LocaleString; icon: PathOrUrl }

View File

@@ -5,6 +5,7 @@ import type { DependencyMetadata } from "./DependencyMetadata"
import type { Description } from "./Description"
import type { GitHash } from "./GitHash"
import type { HardwareRequirements } from "./HardwareRequirements"
import type { LocaleString } from "./LocaleString"
import type { MerkleArchiveCommitment } from "./MerkleArchiveCommitment"
import type { PackageId } from "./PackageId"
import type { RegistryAsset } from "./RegistryAsset"
@@ -15,7 +16,7 @@ export type PackageVersionInfo = {
title: string
icon: DataUrl
description: Description
releaseNotes: string
releaseNotes: LocaleString
gitHash: GitHash | null
license: string
wrapperRepo: string

View File

@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Pem = string

View File

@@ -1,5 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Governor } from "./Governor"
import type { KeyboardOptions } from "./KeyboardOptions"
import type { LshwDevice } from "./LshwDevice"
import type { NetworkInfo } from "./NetworkInfo"
import type { ServerStatus } from "./ServerStatus"
@@ -27,4 +28,6 @@ export type ServerInfo = {
ram: number
devices: Array<LshwDevice>
kiosk: boolean | null
language: string | null
keyboard: KeyboardOptions | null
}

View File

@@ -3,8 +3,8 @@ import type { EncryptedWire } from "./EncryptedWire"
import type { RecoverySource } from "./RecoverySource"
export type SetupExecuteParams = {
startOsLogicalname: string
startOsPassword: EncryptedWire
guid: string
password: EncryptedWire
recoverySource: RecoverySource<EncryptedWire> | null
kiosk?: boolean
}

View File

@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SetupInfo = { guid: string | null; attach: boolean }

View File

@@ -1,8 +1,8 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Pem } from "./Pem"
export type SetupResult = {
torAddresses: Array<string>
hostname: string
lanAddress: string
rootCa: string
rootCa: Pem
needsRestart: boolean
}

View File

@@ -1,7 +1,10 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SetupInfo } from "./SetupInfo"
import type { SetupProgress } from "./SetupProgress"
import type { SetupResult } from "./SetupResult"
export type SetupStatusRes =
| ({ status: "complete" } & SetupResult)
| { status: "needs-install" }
| ({ status: "incomplete" } & SetupInfo)
| ({ status: "running" } & SetupProgress)
| ({ status: "complete" } & SetupResult)

View File

@@ -121,9 +121,11 @@ export { InstallingState } from "./InstallingState"
export { InstallParams } from "./InstallParams"
export { IpHostname } from "./IpHostname"
export { IpInfo } from "./IpInfo"
export { KeyboardOptions } from "./KeyboardOptions"
export { ListPackageSignersParams } from "./ListPackageSignersParams"
export { ListServiceInterfacesParams } from "./ListServiceInterfacesParams"
export { ListVersionSignersParams } from "./ListVersionSignersParams"
export { LocaleString } from "./LocaleString"
export { LoginParams } from "./LoginParams"
export { LshwDevice } from "./LshwDevice"
export { LshwDisplay } from "./LshwDisplay"
@@ -161,6 +163,7 @@ export { PackageState } from "./PackageState"
export { PackageVersionInfo } from "./PackageVersionInfo"
export { PasswordType } from "./PasswordType"
export { PathOrUrl } from "./PathOrUrl"
export { Pem } from "./Pem"
export { Percentage } from "./Percentage"
export { Progress } from "./Progress"
export { ProgressUnits } from "./ProgressUnits"
@@ -199,6 +202,7 @@ export { SetMainStatusStatus } from "./SetMainStatusStatus"
export { SetMainStatus } from "./SetMainStatus"
export { SetNameParams } from "./SetNameParams"
export { SetupExecuteParams } from "./SetupExecuteParams"
export { SetupInfo } from "./SetupInfo"
export { SetupProgress } from "./SetupProgress"
export { SetupResult } from "./SetupResult"
export { SetupStatusRes } from "./SetupStatusRes"

View File

@@ -48,9 +48,9 @@ export type SDKManifest = {
readonly docsUrl: string
readonly description: {
/** Short description to display on the marketplace list page. Max length 80 chars. */
readonly short: string
readonly short: T.LocaleString
/** Long description to display on the marketplace details page for this service. Max length 500 chars. */
readonly long: string
readonly long: T.LocaleString
}
/**
* override the StartOS version this package was made for
@@ -96,17 +96,17 @@ export type SDKManifest = {
readonly alerts?: {
/** An warning alert requiring user confirmation before proceeding with initial installation of this service. */
readonly install?: string | null
readonly install?: T.LocaleString | null
/** An warning alert requiring user confirmation before updating this service. */
readonly update?: string | null
readonly update?: T.LocaleString | null
/** An warning alert requiring user confirmation before uninstalling this service. */
readonly uninstall?: string | null
readonly uninstall?: T.LocaleString | null
/** An warning alert requiring user confirmation before restoring this service from backup. */
readonly restore?: string | null
readonly restore?: T.LocaleString | null
/** An warning alert requiring user confirmation before starting this service. */
readonly start?: string | null
readonly start?: T.LocaleString | null
/** An warning alert requiring user confirmation before stopping this service. */
readonly stop?: string | null
readonly stop?: T.LocaleString | null
}
/**
* @description A mapping of service dependencies to be displayed to users when viewing the Marketplace

View File

@@ -355,10 +355,13 @@ export class GetServiceInterface<Mapped = ServiceInterfaceFilled | null> {
const watch = this.watch(abort.signal)
const res = await watch.next()
if (this.effects.constRetry) {
watch.next().then(() => {
abort.abort()
this.effects.constRetry && this.effects.constRetry()
})
watch
.next()
.then(() => {
abort.abort()
this.effects.constRetry && this.effects.constRetry()
})
.catch()
}
return res.value
}

View File

@@ -55,10 +55,13 @@ export class GetServiceInterfaces<Mapped = ServiceInterfaceFilled[]> {
const watch = this.watch(abort.signal)
const res = await watch.next()
if (this.effects.constRetry) {
watch.next().then(() => {
abort.abort()
this.effects.constRetry && this.effects.constRetry()
})
watch
.next()
.then(() => {
abort.abort()
this.effects.constRetry && this.effects.constRetry()
})
.catch()
}
return res.value
}

View File

@@ -67,7 +67,7 @@ import {
import { getOwnServiceInterfaces } from "../../base/lib/util/getServiceInterfaces"
import { Volumes, createVolumes } from "./util/Volume"
export const OSVersion = testTypeVersion("0.4.0-alpha.17")
export const OSVersion = testTypeVersion("0.4.0-alpha.18")
// prettier-ignore
type AnyNeverCond<T extends any[], Then, Else> =

View File

@@ -0,0 +1,74 @@
/**
* Internationalization (i18n) utilities for StartOS packages.
*
* @example
* ```typescript
* // In package's i18n/index.ts:
* import { setupI18n } from '@start9labs/start-sdk'
* import defaultDict, { DEFAULT_LANG } from './dictionaries/default'
* import translations from './dictionaries/translations'
*
* export const i18n = setupI18n(defaultDict, translations, DEFAULT_LANG)
* ```
*/
type ParamValue = string | number | Date
/**
* Creates a typed i18n function for a package.
*
* @param defaultDict - The default language dictionary mapping strings to numeric indices
* @param translations - Translation dictionaries for each supported locale
* @param defaultLang - The default language code (e.g., 'en_US')
* @returns A typed i18n function that accepts dictionary keys and optional parameters
*/
export function setupI18n<
Dict extends Record<string, number>,
Translations extends Record<string, Record<number, string>>,
>(defaultDict: Dict, translations: Translations, defaultLang: string) {
const lang = process.env.LANG?.replace(/\.UTF-8$/, "") || defaultLang
// Convert locale format from en_US to en-US for Intl APIs
const intlLocale = lang.replace("_", "-")
function getTranslation(): Record<number, string> | null {
if (lang === defaultLang) return null
const availableLangs = Object.keys(translations) as (keyof Translations)[]
const match =
availableLangs.find((l) => l === lang) ??
availableLangs.find((l) => String(l).startsWith(lang.split("_")[0] + "_"))
return match ? (translations[match] as Record<number, string>) : null
}
const translation = getTranslation()
function formatValue(value: ParamValue): string {
if (typeof value === "number") {
return new Intl.NumberFormat(intlLocale).format(value)
}
if (value instanceof Date) {
return new Intl.DateTimeFormat(intlLocale).format(value)
}
return value
}
return function i18n(
key: keyof Dict,
params?: Record<string, ParamValue>,
): string {
let result = translation
? translation[defaultDict[key as string]]
: (key as string)
if (params) {
for (const [paramName, value] of Object.entries(params)) {
result = result.replace(`\${${paramName}}`, formatValue(value))
}
}
return result
}
}

View File

@@ -23,6 +23,7 @@ export {
matches,
utils,
}
export { setupI18n } from "./i18n"
export * as T from "./types"
export { Daemons } from "./mainFn/Daemons"
export { SubContainer } from "./util/SubContainer"

View File

@@ -19,10 +19,13 @@ export class GetServiceManifest<Mapped = Manifest> {
const watch = this.watch(abort.signal)
const res = await watch.next()
if (this.effects.constRetry) {
watch.next().then(() => {
abort.abort()
this.effects.constRetry && this.effects.constRetry()
})
watch
.next()
.then(() => {
abort.abort()
this.effects.constRetry && this.effects.constRetry()
})
.catch()
}
return res.value
}

View File

@@ -230,10 +230,13 @@ export class FileHelper<A> {
eq,
]
this.consts.push(record)
watch.next().then(() => {
this.consts = this.consts.filter((r) => r !== record)
effects.constRetry && effects.constRetry()
})
watch
.next()
.then(() => {
this.consts = this.consts.filter((r) => r !== record)
effects.constRetry && effects.constRetry()
})
.catch()
}
return res.value
}
@@ -263,6 +266,7 @@ export class FileHelper<A> {
})
.catch((e) => console.error(asError(e)))
if (!prev || !eq(prev.value, newRes)) {
console.error("yielding", JSON.stringify({ prev: prev, newRes }))
yield newRes
}
prev = { value: newRes }

View File

@@ -7,7 +7,7 @@ export type VersionOptions<Version extends string> = {
/** The exver-compliant version number */
version: Version & ValidateExVer<Version>
/** The release notes for this version */
releaseNotes: string
releaseNotes: T.LocaleString
/** Data migrations for this version */
migrations: {
/**

View File

@@ -1,12 +1,12 @@
{
"name": "@start9labs/start-sdk",
"version": "0.4.0-beta.47",
"version": "0.4.0-beta.48",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@start9labs/start-sdk",
"version": "0.4.0-beta.47",
"version": "0.4.0-beta.48",
"license": "MIT",
"dependencies": {
"@iarna/toml": "^3.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@start9labs/start-sdk",
"version": "0.4.0-beta.47",
"version": "0.4.0-beta.48",
"description": "Software development kit to facilitate packaging services for StartOS",
"main": "./package/lib/index.js",
"types": "./package/lib/index.d.ts",