@mohou/host 1.0.21 → 1.0.22
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.
- package/package.json +6 -6
- package/src/host/config.ts +6 -0
- package/src/http/bind.ts +4 -4
- package/src/http/reply.ts +5 -1
- package/src/http/updates.ts +20 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mohou/host",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"react-dom": "^19.2.0",
|
|
50
50
|
"tailwindcss": "^4.3.3",
|
|
51
51
|
"tw-animate-css": "^1.4.0",
|
|
52
|
-
"@mohou/app-view": "^1.0.
|
|
53
|
-
"@mohou/
|
|
54
|
-
"@mohou/runtime-provider": "^1.0.
|
|
55
|
-
"@mohou/
|
|
56
|
-
"@mohou/ui": "^1.0.
|
|
52
|
+
"@mohou/app-view": "^1.0.22",
|
|
53
|
+
"@mohou/contract": "^1.0.22",
|
|
54
|
+
"@mohou/runtime-provider": "^1.0.22",
|
|
55
|
+
"@mohou/mcp-client": "^1.0.22",
|
|
56
|
+
"@mohou/ui": "^1.0.22"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/better-sqlite3": "^9.6.0",
|
package/src/host/config.ts
CHANGED
|
@@ -25,6 +25,12 @@ export interface HostPolicy {
|
|
|
25
25
|
}
|
|
26
26
|
/** Absent, or `default`, means the builtin library. Not required to boot. */
|
|
27
27
|
defaultWorkbenchId?: string
|
|
28
|
+
/**
|
|
29
|
+
* Registry for this product's own update check and install, when the owner wants a mirror.
|
|
30
|
+
* Absent or empty follows the install prefix's build-time value. Our own npm children only:
|
|
31
|
+
* `~/.npmrc` and an app's own dependency installs are untouched.
|
|
32
|
+
*/
|
|
33
|
+
updateRegistry?: string
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
/** Seed numbers and names. Host policy, not a locked product value. */
|
package/src/http/bind.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { readVendorFile } from './ports.ts'
|
|
|
15
15
|
import type { LoopbackPorts } from './ports.ts'
|
|
16
16
|
import { authoringTokenMatches } from './guard.ts'
|
|
17
17
|
import { probeBrain, type HostPolicy } from '../host/config.ts'
|
|
18
|
-
import { checkPackageUpdate, discardUpdateResult, readUpdateResult, readUpdateSource, stagePackageUpdate } from './updates.ts'
|
|
18
|
+
import { checkPackageUpdate, discardUpdateResult, readUpdateResult, readUpdateSource, stagePackageUpdate, updateEnv } from './updates.ts'
|
|
19
19
|
import { McpError } from '@mohou/mcp-client'
|
|
20
20
|
|
|
21
21
|
import { admitMcpText } from '../host/mcp-import.ts'
|
|
@@ -147,16 +147,16 @@ export function bindLoopback(input: {
|
|
|
147
147
|
subscribeHost: listener => input.author.hostEvents.subscribe(listener),
|
|
148
148
|
subscribeFrames: listener => forwardFrames(input.author.appEvents, listener),
|
|
149
149
|
subscribeApp: (appId, since, listener) => input.author.appEvents.subscribe(appId, since, listener),
|
|
150
|
-
updateSource: () => Promise.resolve(readUpdateSource()),
|
|
150
|
+
updateSource: () => Promise.resolve(readUpdateSource(updateEnv(input.policy()))),
|
|
151
151
|
checkUpdate: async () => {
|
|
152
152
|
const attempt = readUpdateResult(input.runtimeRoot)
|
|
153
153
|
return {
|
|
154
|
-
...await checkPackageUpdate(),
|
|
154
|
+
...await checkPackageUpdate(undefined, updateEnv(input.policy())),
|
|
155
155
|
...attempt === undefined ? {} : { lastAttempt: attempt },
|
|
156
156
|
}
|
|
157
157
|
},
|
|
158
158
|
installUpdate: (version) => {
|
|
159
|
-
stagePackageUpdate(version)
|
|
159
|
+
stagePackageUpdate(version, updateEnv(input.policy()))
|
|
160
160
|
if (input.restart === undefined) return Promise.resolve()
|
|
161
161
|
const restart = input.restart
|
|
162
162
|
setTimeout(() => {
|
package/src/http/reply.ts
CHANGED
|
@@ -30,11 +30,15 @@ export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
30
30
|
export function mergePolicy(current: unknown, body: Record<string, unknown>): Record<string, unknown> {
|
|
31
31
|
const base = isRecord(current) ? current : {}
|
|
32
32
|
const runtimeRoot = typeof base.runtimeRoot === 'string' ? base.runtimeRoot : undefined
|
|
33
|
+
const { updateRegistry: rawRegistry, ...rest } = body
|
|
34
|
+
const updateRegistry = typeof rawRegistry === 'string' ? rawRegistry.trim() : undefined
|
|
33
35
|
const provider = isRecord(body.runtimeProvider) ? body.runtimeProvider : undefined
|
|
34
36
|
const previous = isRecord(base.runtimeProvider) ? base.runtimeProvider : undefined
|
|
35
37
|
return {
|
|
36
38
|
...base,
|
|
37
|
-
...
|
|
39
|
+
...rest,
|
|
40
|
+
// An empty string means "the packaged default". A non-string never clobbers the stored value.
|
|
41
|
+
...updateRegistry === undefined ? {} : { updateRegistry },
|
|
38
42
|
...runtimeRoot === undefined ? {} : { runtimeRoot },
|
|
39
43
|
...provider === undefined || previous === undefined ? {} : {
|
|
40
44
|
runtimeProvider: {
|
package/src/http/updates.ts
CHANGED
|
@@ -5,6 +5,12 @@ import path from 'node:path'
|
|
|
5
5
|
import { homePackagesDir, hostUpdateResultPath } from '../host/layout.ts'
|
|
6
6
|
import { aboutInfo } from './ports.ts'
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Ask the install channel once. A dev tree with no prefix still asks the registry and cannot install.
|
|
10
|
+
* The budget is host policy, not a locked number: a mirror's first response is often slow.
|
|
11
|
+
*/
|
|
12
|
+
export const updateCheckTimeoutMs = 8_000
|
|
13
|
+
|
|
8
14
|
/** Why an install did not finish. The panel owns the wording of each code. */
|
|
9
15
|
export const updateFailureCodes = ['timeout', 'exit', 'prepare', 'closed', 'leftover', 'verify', 'boot'] as const
|
|
10
16
|
|
|
@@ -91,7 +97,7 @@ export async function checkPackageUpdate(
|
|
|
91
97
|
const name = prefix === undefined ? about.name : '@mohou/shell'
|
|
92
98
|
try {
|
|
93
99
|
const response = await fetch(`${registry.replace(/\/$/, '')}/${name}/latest`, {
|
|
94
|
-
signal: AbortSignal.timeout(
|
|
100
|
+
signal: AbortSignal.timeout(updateCheckTimeoutMs),
|
|
95
101
|
})
|
|
96
102
|
if (!response.ok) return {
|
|
97
103
|
...empty,
|
|
@@ -109,7 +115,8 @@ export async function checkPackageUpdate(
|
|
|
109
115
|
channel: prefix?.channel ?? 'registry',
|
|
110
116
|
...prefix === undefined ? {} : { registry: prefix.registry ?? 'https://registry.npmjs.org' },
|
|
111
117
|
installable: prefix !== undefined,
|
|
112
|
-
|
|
118
|
+
// Newer only. A mirror syncs late, and an older `latest` is not an update.
|
|
119
|
+
updateAvailable: latest !== null && compareVersion(latest, about.current) > 0,
|
|
113
120
|
}
|
|
114
121
|
} catch (error) {
|
|
115
122
|
return {
|
|
@@ -122,10 +129,16 @@ export async function checkPackageUpdate(
|
|
|
122
129
|
}
|
|
123
130
|
}
|
|
124
131
|
|
|
132
|
+
export function updateEnv(policy: { readonly updateRegistry?: string }, env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
133
|
+
const registry = policy.updateRegistry?.trim()
|
|
134
|
+
return registry === undefined || registry.length === 0 ? env : { ...env, MINI_APP_NPM_REGISTRY: registry }
|
|
135
|
+
}
|
|
136
|
+
|
|
125
137
|
/**
|
|
126
138
|
* Where this install would update from. Reads the install prefix; asks no registry, so a
|
|
127
139
|
* panel can show the source while the network is slow or down.
|
|
128
|
-
* @param env - process environment; `MINI_APP_TARBALL_DIR` overrides the package folder
|
|
140
|
+
* @param env - process environment; `MINI_APP_TARBALL_DIR` overrides the package folder, and
|
|
141
|
+
* `MINI_APP_NPM_REGISTRY` overrides the prefix's registry (that is how the policy reaches us)
|
|
129
142
|
* @param cwd - directory the sidecar runs in
|
|
130
143
|
*/
|
|
131
144
|
export function readUpdateSource(env: NodeJS.ProcessEnv = process.env, cwd = process.cwd()): UpdateSource {
|
|
@@ -272,10 +285,13 @@ function readPrefixUpdate(cwd: string, env: NodeJS.ProcessEnv): PrefixUpdate | u
|
|
|
272
285
|
const packages = Object.keys(parsed.dependencies ?? {}).filter(name => name.startsWith('@mohou/'))
|
|
273
286
|
const override = env.MINI_APP_TARBALL_DIR
|
|
274
287
|
const tarballDir = override !== undefined && override.length > 0 ? override : parsed.mohou?.tarballDir
|
|
288
|
+
const registry = env.MINI_APP_NPM_REGISTRY?.trim()
|
|
275
289
|
return {
|
|
276
290
|
dir,
|
|
277
291
|
channel,
|
|
278
|
-
...
|
|
292
|
+
...registry !== undefined && registry.length > 0
|
|
293
|
+
? { registry }
|
|
294
|
+
: typeof parsed.mohou?.registry === 'string' ? { registry: parsed.mohou.registry } : {},
|
|
279
295
|
...tarballDir === undefined ? {} : { tarballDir },
|
|
280
296
|
packages: packages.length === 0 ? ['@mohou/shell'] : packages,
|
|
281
297
|
}
|