@mohou/host 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +10 -0
  2. package/package.json +65 -0
  3. package/src/apps/monogram.ts +15 -0
  4. package/src/apps/registry.ts +238 -0
  5. package/src/apps/startup-seed.ts +95 -0
  6. package/src/bash/client.ts +21 -0
  7. package/src/bash/codes.ts +21 -0
  8. package/src/compile/allowlist.ts +89 -0
  9. package/src/compile/asset-path.ts +48 -0
  10. package/src/compile/autogen.ts +71 -0
  11. package/src/compile/build-vendor-cli.ts +8 -0
  12. package/src/compile/build-vendor.ts +160 -0
  13. package/src/compile/bundle-ui.ts +137 -0
  14. package/src/compile/codes.ts +22 -0
  15. package/src/compile/failure.ts +38 -0
  16. package/src/compile/imports.ts +43 -0
  17. package/src/compile/leave.ts +47 -0
  18. package/src/compile/load-backend.ts +95 -0
  19. package/src/compile/review.ts +342 -0
  20. package/src/compile/runner.ts +205 -0
  21. package/src/compile/sheet.ts +174 -0
  22. package/src/compile/wrapper.ts +297 -0
  23. package/src/credentials/codes.ts +26 -0
  24. package/src/credentials/create.ts +69 -0
  25. package/src/credentials/file.ts +113 -0
  26. package/src/credentials/provider.ts +66 -0
  27. package/src/events/app-events.ts +214 -0
  28. package/src/events/error-ring.ts +109 -0
  29. package/src/events/host-events.ts +39 -0
  30. package/src/events/view-bridge.ts +402 -0
  31. package/src/events/view-eval.ts +213 -0
  32. package/src/files/codes.ts +28 -0
  33. package/src/files/skip.ts +23 -0
  34. package/src/files/tools.ts +215 -0
  35. package/src/history/codes.ts +22 -0
  36. package/src/history/store.ts +287 -0
  37. package/src/host/activity.ts +110 -0
  38. package/src/host/author-mcp.ts +248 -0
  39. package/src/host/author-skill.ts +196 -0
  40. package/src/host/codes.ts +21 -0
  41. package/src/host/config.ts +239 -0
  42. package/src/host/heat.ts +35 -0
  43. package/src/host/layout.ts +81 -0
  44. package/src/host/log.ts +142 -0
  45. package/src/host/mcp-editor.ts +205 -0
  46. package/src/host/mcp-import.ts +141 -0
  47. package/src/host/mcp.ts +109 -0
  48. package/src/host/port.ts +83 -0
  49. package/src/host/session.ts +358 -0
  50. package/src/host/workbench.ts +38 -0
  51. package/src/http/app.ts +46 -0
  52. package/src/http/asset.ts +49 -0
  53. package/src/http/author.ts +157 -0
  54. package/src/http/bind.ts +217 -0
  55. package/src/http/client.ts +175 -0
  56. package/src/http/codes.ts +29 -0
  57. package/src/http/env.ts +3 -0
  58. package/src/http/guard.ts +20 -0
  59. package/src/http/iframe.ts +212 -0
  60. package/src/http/layout.ts +63 -0
  61. package/src/http/owner.ts +380 -0
  62. package/src/http/ports.ts +114 -0
  63. package/src/http/reply.ts +53 -0
  64. package/src/http/route-codes.ts +19 -0
  65. package/src/http/updates.ts +258 -0
  66. package/src/index.ts +201 -0
  67. package/src/install/codes.ts +21 -0
  68. package/src/install/install.ts +230 -0
  69. package/src/install/layout.ts +16 -0
  70. package/src/kernel/bind-brain.ts +117 -0
  71. package/src/kernel/call.ts +134 -0
  72. package/src/kernel/codes.ts +21 -0
  73. package/src/kernel/index.ts +13 -0
  74. package/src/kernel/model-policy.ts +83 -0
  75. package/src/kernel/model-stream.ts +86 -0
  76. package/src/kernel/present.ts +4 -0
  77. package/src/kernel/working-directory.ts +35 -0
  78. package/src/metrics/codes.ts +22 -0
  79. package/src/metrics/read.ts +48 -0
  80. package/src/owner/read.ts +85 -0
  81. package/src/pwsh/client.ts +47 -0
  82. package/src/pwsh/codes.ts +21 -0
  83. package/src/shell/command.ts +172 -0
  84. package/src/storage/codes.ts +33 -0
  85. package/src/storage/layout.ts +44 -0
  86. package/src/storage/open.ts +461 -0
  87. package/src/storage/schema.ts +176 -0
  88. package/src/storage/sql.ts +40 -0
  89. package/src/theme/codes.ts +20 -0
  90. package/src/theme/layout.ts +42 -0
  91. package/src/theme/paint.ts +108 -0
  92. package/src/theme/parse.ts +58 -0
  93. package/src/theme/pin.ts +197 -0
  94. package/src/theme/tokens.ts +30 -0
  95. package/src/tools/author.ts +970 -0
  96. package/src/tools/codes.ts +21 -0
  97. package/src/tools/diagnostics.ts +53 -0
  98. package/src/tools/http.ts +48 -0
  99. package/src/tools/mcp-list.ts +90 -0
  100. package/src/tools/schemas.ts +185 -0
  101. package/themes/theme-default.css +4 -0
  102. package/themes/theme-forest.css +4 -0
  103. package/themes/theme-hokage.css +4 -0
  104. package/themes/theme-matcha.css +4 -0
  105. package/themes/theme-slate.css +4 -0
  106. package/themes/theme-tokyo.css +4 -0
  107. package/themes/theme-yellow.css +4 -0
  108. package/themes/theme-zoro.css +4 -0
@@ -0,0 +1,258 @@
1
+ import { readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
2
+ import { homedir } from 'node:os'
3
+ import path from 'node:path'
4
+
5
+ import { homePackagesDir, hostUpdateResultPath } from '../host/layout.ts'
6
+ import { aboutInfo } from './ports.ts'
7
+
8
+ /** Why an install did not finish. The panel owns the wording of each code. */
9
+ export const updateFailureCodes = ['timeout', 'exit', 'prepare', 'closed', 'leftover', 'verify', 'boot'] as const
10
+
11
+ export type UpdateFailureCode = (typeof updateFailureCodes)[number]
12
+
13
+ /**
14
+ * What the launcher recorded about the last install. `from` is the version that was running
15
+ * when the attempt started, and `at` is epoch milliseconds.
16
+ */
17
+ export type UpdateAttempt =
18
+ | {
19
+ readonly state: 'done'
20
+ readonly from?: string
21
+ readonly to?: string
22
+ readonly at: number
23
+ }
24
+ | {
25
+ readonly state: 'failed'
26
+ readonly code: UpdateFailureCode
27
+ readonly from?: string
28
+ readonly to?: string
29
+ readonly rolledBack: boolean
30
+ readonly exitCode?: number
31
+ readonly log?: string
32
+ readonly at: number
33
+ }
34
+
35
+ export interface UpdateCheck {
36
+ readonly name: string
37
+ readonly current: string
38
+ readonly latest: string | null
39
+ readonly updateAvailable: boolean
40
+ readonly channel?: 'registry' | 'tarball'
41
+ readonly installable?: boolean
42
+ readonly error?: string
43
+ /** The last install this machine recorded, when one is on disk. */
44
+ readonly lastAttempt?: UpdateAttempt
45
+ }
46
+
47
+ interface PrefixUpdate {
48
+ readonly dir: string
49
+ readonly channel: 'registry' | 'tarball'
50
+ readonly registry?: string
51
+ readonly tarballDir?: string
52
+ readonly packages: readonly string[]
53
+ }
54
+
55
+ /** Ask the install channel once. A dev tree with no prefix still asks the registry and cannot install. */
56
+ export async function checkPackageUpdate(
57
+ about: ReturnType<typeof aboutInfo> = aboutInfo(),
58
+ env: NodeJS.ProcessEnv = process.env,
59
+ cwd = process.cwd(),
60
+ ): Promise<UpdateCheck> {
61
+ const empty = { name: about.name, current: about.current, latest: null, updateAvailable: false }
62
+ if (about.name.length === 0) return { ...empty, error: 'package name is missing' }
63
+ const prefix = readPrefixUpdate(cwd, env)
64
+ if (prefix?.channel === 'tarball') {
65
+ const latest = newestTarball(prefix.tarballDir ?? homePackagesDir(homedir()), 'shell')
66
+ return {
67
+ name: '@mohou/shell',
68
+ current: about.current,
69
+ latest,
70
+ channel: 'tarball',
71
+ installable: true,
72
+ updateAvailable: latest !== null && compareVersion(latest, about.current) > 0,
73
+ }
74
+ }
75
+ if (about.private && prefix === undefined) return { ...empty, latest: about.current, installable: false }
76
+ const registry = prefix?.registry ?? 'https://registry.npmjs.org'
77
+ const name = prefix === undefined ? about.name : '@mohou/shell'
78
+ try {
79
+ const response = await fetch(`${registry.replace(/\/$/, '')}/${name}/latest`, {
80
+ signal: AbortSignal.timeout(3_000),
81
+ })
82
+ if (!response.ok) return { ...empty, name, ...prefix === undefined ? {} : { channel: prefix.channel }, installable: prefix !== undefined, error: `package registry returned ${response.status}` }
83
+ const body = await response.json() as { version?: unknown }
84
+ const latest = typeof body.version === 'string' ? body.version : null
85
+ return {
86
+ name,
87
+ current: about.current,
88
+ latest,
89
+ channel: prefix?.channel ?? 'registry',
90
+ installable: prefix !== undefined,
91
+ updateAvailable: latest !== null && latest !== about.current,
92
+ }
93
+ } catch (error) {
94
+ return { ...empty, name, ...prefix === undefined ? {} : { channel: prefix.channel }, installable: prefix !== undefined, error: error instanceof Error ? error.message : 'update check failed' }
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Read the launcher's record of the last install. A missing, unreadable, or malformed file reads as
100
+ * no attempt: the panel shows nothing rather than a guessed outcome.
101
+ * @param runtimeRoot - the directory the launcher and Host share
102
+ */
103
+ export function readUpdateResult(runtimeRoot: string): UpdateAttempt | undefined {
104
+ let text: string
105
+ try {
106
+ text = readFileSync(hostUpdateResultPath(runtimeRoot), 'utf8')
107
+ } catch {
108
+ return undefined
109
+ }
110
+ let value: unknown
111
+ try {
112
+ value = JSON.parse(text)
113
+ } catch {
114
+ return undefined
115
+ }
116
+ return admitUpdateAttempt(value)
117
+ }
118
+
119
+ /**
120
+ * Forget the record the panel just showed. `at` names the attempt, so a newer one survives.
121
+ * @param runtimeRoot - the directory the launcher and Host share
122
+ * @param at - epoch milliseconds of the attempt the panel displayed
123
+ */
124
+ export function discardUpdateResult(runtimeRoot: string, at: number): void {
125
+ const current = readUpdateResult(runtimeRoot)
126
+ if (current === undefined || current.at !== at) return
127
+ rmSync(hostUpdateResultPath(runtimeRoot), { force: true })
128
+ }
129
+
130
+ /** Write `update.json` for the launcher to run after this process exits. */
131
+ export function stagePackageUpdate(version: string, env: NodeJS.ProcessEnv = process.env, cwd = process.cwd()): void {
132
+ const prefix = readPrefixUpdate(cwd, env)
133
+ if (prefix === undefined) throw new Error('update install needs an app prefix')
134
+ const args = prefix.channel === 'tarball'
135
+ ? tarballInstallArgs(prefix, version)
136
+ : ['install', `@mohou/shell@${version}`, ...installFlags(), '--registry', prefix.registry ?? 'https://registry.npmjs.org']
137
+ writeFileSync(path.join(prefix.dir, 'update.json'), `${JSON.stringify({ version, args })}\n`)
138
+ }
139
+
140
+ function installFlags(): string[] {
141
+ return ['--no-fund', '--no-audit', '--omit=peer', '--fetch-retries=1', '--fetch-timeout=20000']
142
+ }
143
+
144
+ function tarballInstallArgs(prefix: PrefixUpdate, version: string): string[] {
145
+ const dir = prefix.tarballDir ?? homePackagesDir(homedir())
146
+ const specs = prefix.packages.map((name) => {
147
+ const file = path.join(dir, packedName(name, version))
148
+ return `file:${file.replaceAll('\\', '/')}`
149
+ })
150
+ return ['install', ...specs, ...installFlags()]
151
+ }
152
+
153
+ function packedName(packageName: string, version: string): string {
154
+ return `${packageName.replace(/^@/, '').replaceAll('/', '-')}-${version}.tgz`
155
+ }
156
+
157
+ export function newestTarball(dir: string, slug: string): string | null {
158
+ let names: string[]
159
+ try {
160
+ names = readdirSync(dir)
161
+ } catch {
162
+ return null
163
+ }
164
+ const versions = names.flatMap((name) => {
165
+ const match = new RegExp(`^mohou-${slug}-(\\d+\\.\\d+\\.\\d+)\\.tgz$`).exec(name)
166
+ return match?.[1] === undefined ? [] : [match[1]]
167
+ })
168
+ return versions.sort(compareVersion).at(-1) ?? null
169
+ }
170
+
171
+ function compareVersion(left: string, right: string): number {
172
+ const a = left.split('.').map(part => Number(part))
173
+ const b = right.split('.').map(part => Number(part))
174
+ const width = Math.max(a.length, b.length)
175
+ for (let index = 0; index < width; index += 1) {
176
+ const av = a[index] ?? 0
177
+ const bv = b[index] ?? 0
178
+ if (av !== bv) return av > bv ? 1 : -1
179
+ }
180
+ return 0
181
+ }
182
+
183
+ /** The value a launcher writes. Untrusted at this boundary: a bad shape reads as no attempt. */
184
+ function admitUpdateAttempt(value: unknown): UpdateAttempt | undefined {
185
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined
186
+ const row = value as Record<string, unknown>
187
+ if (typeof row.at !== 'number' || !Number.isFinite(row.at)) return undefined
188
+ const from = updateVersion(row.from)
189
+ const to = updateVersion(row.to)
190
+ if (row.state === 'done') {
191
+ return {
192
+ state: 'done',
193
+ at: row.at,
194
+ ...from === undefined ? {} : { from },
195
+ ...to === undefined ? {} : { to },
196
+ }
197
+ }
198
+ if (row.state !== 'failed') return undefined
199
+ const code = updateFailureCode(row.code)
200
+ if (code === undefined) return undefined
201
+ return {
202
+ state: 'failed',
203
+ code,
204
+ rolledBack: row.rolledBack === true,
205
+ at: row.at,
206
+ ...from === undefined ? {} : { from },
207
+ ...to === undefined ? {} : { to },
208
+ ...typeof row.exitCode === 'number' && Number.isFinite(row.exitCode) ? { exitCode: row.exitCode } : {},
209
+ ...typeof row.log === 'string' && row.log.length > 0 ? { log: row.log } : {},
210
+ }
211
+ }
212
+
213
+ function updateVersion(value: unknown): string | undefined {
214
+ return typeof value === 'string' && value.length > 0 ? value : undefined
215
+ }
216
+
217
+ function updateFailureCode(value: unknown): UpdateFailureCode | undefined {
218
+ return typeof value === 'string' && (updateFailureCodes as readonly string[]).includes(value)
219
+ ? value as UpdateFailureCode
220
+ : undefined
221
+ }
222
+
223
+ function readPrefixUpdate(cwd: string, env: NodeJS.ProcessEnv): PrefixUpdate | undefined {
224
+ const dir = findPrefix(cwd)
225
+ if (dir === undefined) return undefined
226
+ const parsed = JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8')) as {
227
+ dependencies?: Record<string, string>
228
+ mohou?: { channel?: string; registry?: string; tarballDir?: string }
229
+ }
230
+ const channel = parsed.mohou?.channel
231
+ if (channel !== 'registry' && channel !== 'tarball') return undefined
232
+ const packages = Object.keys(parsed.dependencies ?? {}).filter(name => name.startsWith('@mohou/'))
233
+ const override = env.MINI_APP_TARBALL_DIR
234
+ const tarballDir = override !== undefined && override.length > 0 ? override : parsed.mohou?.tarballDir
235
+ return {
236
+ dir,
237
+ channel,
238
+ ...typeof parsed.mohou?.registry === 'string' ? { registry: parsed.mohou.registry } : {},
239
+ ...tarballDir === undefined ? {} : { tarballDir },
240
+ packages: packages.length === 0 ? ['@mohou/shell'] : packages,
241
+ }
242
+ }
243
+
244
+ function findPrefix(start: string): string | undefined {
245
+ let dir = path.resolve(start)
246
+ for (let depth = 0; depth < 8; depth += 1) {
247
+ try {
248
+ const parsed = JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8')) as { name?: string }
249
+ if (parsed.name === 'mohou-app') return dir
250
+ } catch {
251
+ // keep walking
252
+ }
253
+ const parent = path.dirname(dir)
254
+ if (parent === dir) return undefined
255
+ dir = parent
256
+ }
257
+ return undefined
258
+ }
package/src/index.ts ADDED
@@ -0,0 +1,201 @@
1
+ /** Host package. Kernel is the call loop. @module @mohou/host */
2
+
3
+ export const packageId = '@mohou/host' as const
4
+
5
+ export {
6
+ HostError,
7
+ bindBrain,
8
+ hostCodes,
9
+ resolveWorkingDirectory,
10
+ runCall,
11
+ type BoundBrain,
12
+ type BrainBinding,
13
+ type CallCapabilities,
14
+ type HostCode,
15
+ type ModelPolicy,
16
+ type WorkingDirectoryInput,
17
+ } from './kernel/index.ts'
18
+ export { StorageError, storageCodes, type StorageCode } from './storage/codes.ts'
19
+ export { DEFAULT_STORAGE_NOTICE_BYTES, openStorage, type StorageConnect, type StorageFile, type StorageOpenOptions } from './storage/open.ts'
20
+ export {
21
+ storageBackup,
22
+ storageBackupMeta,
23
+ storageBlocked,
24
+ storageDatabase,
25
+ storageDir,
26
+ storageLayout,
27
+ storageQuarantine,
28
+ } from './storage/layout.ts'
29
+ export { restoreStorageBackup } from './storage/schema.ts'
30
+ export { HttpError, httpCodes, type HttpCode } from './http/codes.ts'
31
+ export { createHttp, type HttpPolicy } from './http/client.ts'
32
+ export { appResource, appsResource, historyListBound, httpLayout, runnerPath } from './http/layout.ts'
33
+ export { BashError, bashCodes, type BashCode } from './bash/codes.ts'
34
+ export { createBash, scrubBashEnv, type BashHandle, type BashPolicy, type BashResult } from './bash/client.ts'
35
+ export { PwshError, pwshCodes, type PwshCode } from './pwsh/codes.ts'
36
+ export { createPwsh, pwshCandidates } from './pwsh/client.ts'
37
+ export { scrubShellEnv, stopChild, type CommandPolicy, type CommandResult } from './shell/command.ts'
38
+ export { MetricsError, metricsCodes, type MetricsCode } from './metrics/codes.ts'
39
+ export { readMetrics } from './metrics/read.ts'
40
+ export {
41
+ bindAppHooks,
42
+ createAppEvents,
43
+ forwardFrames,
44
+ DEFAULT_APP_EVENT_PING_MS,
45
+ DEFAULT_APP_EVENT_RETRY_MS,
46
+ type AppEvents,
47
+ type AppGap,
48
+ type AppHooks,
49
+ type AppPing,
50
+ type AppRetry,
51
+ type AppStreamItem,
52
+ type AuthorEvent,
53
+ } from './events/app-events.ts'
54
+ export { admitErrorReport, createErrorRing, errorKinds, type AppErrorRecord, type ErrorKind } from './events/error-ring.ts'
55
+ export { createHostEvents, type HostEvent } from './events/host-events.ts'
56
+ export {
57
+ DEFAULT_VIEW_CODE,
58
+ DEFAULT_VIEW_MAX_BYTES,
59
+ DEFAULT_VIEW_TIMEOUT_MS,
60
+ createViewQueries,
61
+ viewStates,
62
+ type ViewEnvelope,
63
+ type ViewState,
64
+ type ViewStop,
65
+ } from './events/view-eval.ts'
66
+ export { FileToolError, fileToolCodes, type FileToolCode } from './files/codes.ts'
67
+ export { createFileTools, resolveAppPath, type FileCommit, type FileRead } from './files/tools.ts'
68
+ export {
69
+ listMcpForAuthor,
70
+ toolsMcpForAuthor,
71
+ type AuthorMcpServerNames,
72
+ type AuthorMcpServerTools,
73
+ type AuthorMcpTool,
74
+ } from './tools/mcp-list.ts'
75
+ export { AuthorError, authorCodes, type AuthorCode } from './tools/codes.ts'
76
+ export { HistoryError, historyCodes, type HistoryCode } from './history/codes.ts'
77
+ export { InstallError, installCodes, type InstallCode } from './install/codes.ts'
78
+ export {
79
+ DEFAULT_INSTALL_TIMEOUT_MS,
80
+ installApp,
81
+ runNpm,
82
+ type InstallRequest,
83
+ type InstallResult,
84
+ type NpmRun,
85
+ } from './install/install.ts'
86
+ export { installLayout, packageLock, packageManifest } from './install/layout.ts'
87
+ export {
88
+ commitApp,
89
+ historyBounds,
90
+ listHistory,
91
+ readAppCommit,
92
+ resetApp,
93
+ type HistoryNode,
94
+ type HistoryTip,
95
+ } from './history/store.ts'
96
+ export {
97
+ authorByteToolNames,
98
+ authorMcpToolNames,
99
+ authorToolNames,
100
+ createAuthorTools,
101
+ isAuthorMcpTool,
102
+ ensureAuthoringToken,
103
+ readAuthoringToken,
104
+ type AuthorCallPorts,
105
+ type AuthorToolName,
106
+ } from './tools/author.ts'
107
+ export { authoringTokenMatches, isLoopbackAddress, startAuthorHttp } from './tools/http.ts'
108
+ export { createLoopbackApp } from './http/app.ts'
109
+ export { routeCodes, RouteError, type RouteCode } from './http/route-codes.ts'
110
+ export { diagnosticLayout, diagnosticUrl } from './tools/diagnostics.ts'
111
+ export { RegistryError, createAppRegistry, listedApp, type AppSummary, type ListedApp } from './apps/registry.ts'
112
+ export { heat, type HeatSample, type HeatSnapshot, type HeatValue } from './host/heat.ts'
113
+ export { ActivityError, readActivity, recordOpen, type Activity, type ActivityApp } from './host/activity.ts'
114
+ export { monogram } from './apps/monogram.ts'
115
+ export { createOwnerReads, DEFAULT_OWNER_ROW_CAP, reloadView } from './owner/read.ts'
116
+ export { ThemeError, themeCodes, type ThemeCode } from './theme/codes.ts'
117
+ export {
118
+ appThemeCss,
119
+ appThemePin,
120
+ customThemeId,
121
+ customThemePath,
122
+ themeLayout,
123
+ } from './theme/layout.ts'
124
+ export { parseThemeCss } from './theme/parse.ts'
125
+ export { requiredThemeTokens, themeTokens, type ThemeToken } from './theme/tokens.ts'
126
+ export { resolveFirstPaint, firstPaintStyle, type FirstPaint } from './theme/paint.ts'
127
+ export { createThemePins, type AppPin, type PaletteList } from './theme/pin.ts'
128
+ export { CredentialError, credentialCodes, type CredentialCode } from './credentials/codes.ts'
129
+ export { createFileCredentials } from './credentials/file.ts'
130
+ export {
131
+ createCredentials,
132
+ credentialSourceKinds,
133
+ type BuiltinJsonCredentialSource,
134
+ type CredentialSource,
135
+ type CredentialSourceKind,
136
+ } from './credentials/create.ts'
137
+ export {
138
+ admitCredentialName,
139
+ emptyCredentials,
140
+ type CredentialListing,
141
+ type CredentialProvider,
142
+ } from './credentials/provider.ts'
143
+ export { ConfigError, configCodes, type ConfigCode } from './host/codes.ts'
144
+ export {
145
+ DEFAULT_PORT_BOUND,
146
+ hostThemes,
147
+ probeBrain,
148
+ resolveHostConfig,
149
+ writeHostPolicy,
150
+ type HostPolicy,
151
+ type HostSeed,
152
+ type HostTheme,
153
+ type PortBound,
154
+ } from './host/config.ts'
155
+ export {
156
+ allocateHostPort,
157
+ HOST_PORT_PROBE_SPAN,
158
+ isHostPortFree,
159
+ isPortInUseError,
160
+ PortInUseError,
161
+ } from './host/port.ts'
162
+ export {
163
+ defaultRuntimeRoot,
164
+ homeCredentialsPath,
165
+ homeDir,
166
+ homeLayout,
167
+ homeThemesDir,
168
+ hostAppsDir,
169
+ hostTrashDir,
170
+ hostAuthoringToken,
171
+ hostAppLogPath,
172
+ hostConfigPath,
173
+ hostLayout,
174
+ hostMcpPath,
175
+ } from './host/layout.ts'
176
+ export { createHostLog, DEFAULT_HOST_LOG_BYTES, type HostLog } from './host/log.ts'
177
+ export { loadMcpServers, mcpConfigEnv, mcpReferenceSources } from './host/mcp.ts'
178
+ export { createHost, type HostSession } from './host/session.ts'
179
+ export {
180
+ ensureRuntimeAppsLayout,
181
+ startupSamples,
182
+ startupSeedMarker,
183
+ type StartupSeedResult,
184
+ } from './apps/startup-seed.ts'
185
+ export {
186
+ platformBundled,
187
+ platformImportMap,
188
+ platformLayout,
189
+ platformModuleAllowed,
190
+ platformModules,
191
+ platformRuntimePath,
192
+ platformSdkPath,
193
+ platformVendorPath,
194
+ type PlatformModule,
195
+ type PlatformSide,
196
+ } from './compile/allowlist.ts'
197
+ export { CompileError, compileCodes, type CompileCode } from './compile/codes.ts'
198
+ export { loadBackend, type LoadedBackend } from './compile/load-backend.ts'
199
+ export { reviewApp, reviewNoticeCodes, type AppReview, type ReviewNotice, type ReviewNoticeCode } from './compile/review.ts'
200
+ export { renderRunnerDocument } from './compile/runner.ts'
201
+ export { compilePanelStylesheet } from './compile/sheet.ts'
@@ -0,0 +1,21 @@
1
+ /** Codes the installer emits. Callers match `code`. */
2
+
3
+ export const installCodes = ['install-denied', 'install-failed'] as const
4
+
5
+ export type InstallCode = (typeof installCodes)[number]
6
+
7
+ /** A denied spec or a failed install. The message names the next step. */
8
+ export class InstallError extends Error {
9
+ readonly code: InstallCode
10
+
11
+ /**
12
+ * @param code - one of {@link installCodes}
13
+ * @param message - human text; not the match key
14
+ * @param options - optional `cause`
15
+ */
16
+ constructor(code: InstallCode, message: string, options?: { cause?: unknown }) {
17
+ super(message, options)
18
+ this.name = 'InstallError'
19
+ this.code = code
20
+ }
21
+ }