Files
goodbuddy/src/shared/application-settings-contracts.ts
T
mesalogo 05838d2c50 feat(hosts): move host management into the Hosts application
Hosts becomes a built-in application opened from the Apps menu, with Hosts
and Agent packages tabs. The remote capability switch keeps using
remoteProjectsEnabled and moves to the application settings, so Main-side
gating and saved values are unchanged. Saved navigation gains the new entry
at the end, unpinned.

Settings drops the SSH hosts category and the Remote projects tab. The host
list collapses to one-line summaries with status badges, versions show as a
table, and application settings switches align to the right edge.
2026-10-13 01:15:53 +08:00

140 lines
5.7 KiB
TypeScript

import { z } from 'zod'
import { supervisionReviewSettingsSchema } from './supervision-review-contracts'
import { magicNoteCommentFormatSchema } from './magic-notes-contracts'
import { localToolEnvironmentSettingsSchema } from './local-tool-environment-contracts'
import { modelDownloadSourceSchema } from './model-download-contracts'
import { settingsWarningsSchema } from './settings-warning-contracts'
export {
artifactDownloadSourceSchema,
defaultLocalToolEnvironmentSettings,
localToolEnvironmentSettingsSchema,
localToolExecutablePathSchema,
localToolRuntimeSelectionSchema,
type ArtifactDownloadSource,
type LocalToolEnvironmentSettings,
type LocalToolRuntimeSelection
} from './local-tool-environment-contracts'
export {
modelDownloadSourceSchema,
type ModelDownloadSource
} from './model-download-contracts'
export const magicNoteCommentModeSchema = z.enum([
'immediate',
'after-save-auto',
'after-save-manual'
])
export type MagicNoteCommentMode = z.infer<typeof magicNoteCommentModeSchema>
export const magicNoteCanvasPageCountSchema = z.number().int().min(1).max(8)
export const updateSourceSchema = z.enum(['github', 'mirror'])
export type UpdateSource = z.infer<typeof updateSourceSchema>
export const defaultSupervisionTimeoutSeconds = 240
export const defaultSupervisorModelConcurrency = 1
export const supervisorModelConcurrencySchema = z.number().int().min(1).max(4)
export const supervisionTimeoutSecondsSchema = z.number().int().min(30).max(600)
export const builtInApplicationIds = ['magic-notes', 'knowledge', 'heartbeat', 'local-inference', 'device-sharing', 'remote-hosts'] as const
export type BuiltInApplicationId = typeof builtInApplicationIds[number]
export const editableApplicationIds = ['magic-notes', 'heartbeat', 'local-inference', 'device-sharing', 'remote-hosts'] as const
export type EditableApplicationId = typeof editableApplicationIds[number]
export const defaultApplicationNavigation = {
order: ['knowledge', 'heartbeat', 'magic-notes', 'local-inference', 'device-sharing', 'remote-hosts'] as BuiltInApplicationId[],
pinned: { 'magic-notes': true, heartbeat: true, 'local-inference': false, 'remote-hosts': false }
}
export const applicationNavigationSchema = z.object({
order: z.array(z.enum(builtInApplicationIds)).length(builtInApplicationIds.length)
.refine(ids => new Set(ids).size === builtInApplicationIds.length, 'Each application must occur exactly once'),
pinned: z.object({
'magic-notes': z.boolean(),
heartbeat: z.boolean().default(true),
'local-inference': z.boolean(),
// Optional on input so callers written before the application existed keep working.
'remote-hosts': z.boolean().default(false)
}).strict()
}).strict()
const applicationPreferencesSchema = z
.object({
checkUpdatesOnStartup: z.boolean(),
desktopNotificationsEnabled: z.boolean().default(true),
transparentFrostedEffectEnabled: z.boolean().default(true),
chatMinimapEnabled: z.boolean().default(true),
updateSource: updateSourceSchema,
modelDownloadSource: modelDownloadSourceSchema,
localToolEnvironment: localToolEnvironmentSettingsSchema,
conversationHtmlRenderingEnabled: z.boolean(),
remoteProjectsEnabled: z.boolean(),
applicationNavigation: applicationNavigationSchema.default(defaultApplicationNavigation),
localInferenceEnabled: z.boolean().default(true),
deviceSharingEnabled: z.boolean().default(false),
magicNotesEnabled: z.boolean().default(true),
heartbeatEnabled: z.boolean().optional(),
heartbeatReportTimeoutSeconds: supervisionTimeoutSecondsSchema.optional(),
supervisorOrganizeTimeoutSeconds: supervisionTimeoutSecondsSchema.optional(),
supervisorModelConcurrency: supervisorModelConcurrencySchema.optional(),
supervisorModelProfileId: z.string().uuid().nullable().optional(),
supervisionReview: supervisionReviewSettingsSchema.optional(),
magicNotesShowIncompleteTodoCount: z.boolean().default(true),
magicNoteCommentMode: magicNoteCommentModeSchema.default('immediate'),
magicNoteCommentFormat: magicNoteCommentFormatSchema.default('combined'),
magicNoteCanvasPageCount: magicNoteCanvasPageCountSchema.default(1)
})
.strict()
export const applicationSettingsSchema = applicationPreferencesSchema
.extend({
warnings: settingsWarningsSchema.optional()
})
.strict()
export const applicationSettingsUpdateSchema = applicationPreferencesSchema
.partial()
.extend({
applicationNavigation: applicationNavigationSchema.optional(),
desktopNotificationsEnabled: z.boolean().optional(),
transparentFrostedEffectEnabled: z.boolean().optional(),
chatMinimapEnabled: z.boolean().optional(),
localInferenceEnabled: z.boolean().optional(),
deviceSharingEnabled: z.boolean().optional(),
magicNotesEnabled: z.boolean().optional(),
heartbeatEnabled: z.boolean().optional(),
magicNotesShowIncompleteTodoCount: z.boolean().optional(),
magicNoteCommentMode: magicNoteCommentModeSchema.optional(),
magicNoteCommentFormat: magicNoteCommentFormatSchema.optional(),
magicNoteCanvasPageCount: magicNoteCanvasPageCountSchema.optional()
})
.refine((input) => Object.keys(input).length > 0, {
message: 'At least one application setting is required'
})
export type ApplicationSettings = z.infer<typeof applicationSettingsSchema>
export type ApplicationSettingsUpdate = z.infer<
typeof applicationSettingsUpdateSchema
>
export type VersionCheckFile = {
name: string
size: number
sha256: string
}
export type VersionCheckTarget = {
platform: 'windows' | 'macos' | 'linux'
arch: 'x64' | 'arm64'
formats: string[]
files: VersionCheckFile[]
}
export type VersionCheckResult = {
updateAvailable: boolean
currentVersion: string
latestVersion: string
releaseUrl: string
target: VersionCheckTarget
}