@mohou/host 1.0.20 → 1.0.21
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/http/bind.ts +2 -1
- package/src/http/owner.ts +2 -1
- package/src/http/ports.ts +3 -1
- package/src/http/updates.ts +43 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mohou/host",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
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/
|
|
52
|
+
"@mohou/app-view": "^1.0.21",
|
|
53
|
+
"@mohou/mcp-client": "^1.0.21",
|
|
54
|
+
"@mohou/runtime-provider": "^1.0.21",
|
|
55
|
+
"@mohou/contract": "^1.0.21",
|
|
56
|
+
"@mohou/ui": "^1.0.21"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/better-sqlite3": "^9.6.0",
|
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, stagePackageUpdate } from './updates.ts'
|
|
18
|
+
import { checkPackageUpdate, discardUpdateResult, readUpdateResult, readUpdateSource, stagePackageUpdate } from './updates.ts'
|
|
19
19
|
import { McpError } from '@mohou/mcp-client'
|
|
20
20
|
|
|
21
21
|
import { admitMcpText } from '../host/mcp-import.ts'
|
|
@@ -147,6 +147,7 @@ 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
151
|
checkUpdate: async () => {
|
|
151
152
|
const attempt = readUpdateResult(input.runtimeRoot)
|
|
152
153
|
return {
|
package/src/http/owner.ts
CHANGED
|
@@ -36,9 +36,10 @@ export function mountOwner(app: Hono<HostEnv>, ports: LoopbackPorts): void {
|
|
|
36
36
|
if (denied !== undefined) return denied
|
|
37
37
|
return writeConfig(c, ports)
|
|
38
38
|
})
|
|
39
|
-
app.get(httpLayout.about, c => c.json({
|
|
39
|
+
app.get(httpLayout.about, async c => c.json({
|
|
40
40
|
ok: true,
|
|
41
41
|
...aboutInfo(),
|
|
42
|
+
source: await ports.updateSource(),
|
|
42
43
|
authoring: {
|
|
43
44
|
url: `http://127.0.0.1:${ports.readPolicy().hostPort}${httpLayout.mcp}`,
|
|
44
45
|
token: ports.authoringToken,
|
package/src/http/ports.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { CredentialListing } from '../credentials/provider.ts'
|
|
|
11
11
|
import type { McpCheck, McpEditorServer } from '../host/mcp-editor.ts'
|
|
12
12
|
import type { McpLoadFailure } from '../host/mcp.ts'
|
|
13
13
|
import type { AppPin } from '../theme/pin.ts'
|
|
14
|
-
import type { UpdateCheck } from './updates.ts'
|
|
14
|
+
import type { UpdateCheck, UpdateSource } from './updates.ts'
|
|
15
15
|
|
|
16
16
|
const require = createRequire(import.meta.url)
|
|
17
17
|
|
|
@@ -67,6 +67,8 @@ export interface LoopbackPorts {
|
|
|
67
67
|
subscribeFrames(listener: (event: unknown) => void): () => void
|
|
68
68
|
subscribeApp(appId: string, since: number, listener: (event: unknown) => void): () => void
|
|
69
69
|
checkUpdate(): Promise<UpdateCheck>
|
|
70
|
+
/** Where an update would come from. Reads the prefix; no network. */
|
|
71
|
+
updateSource(): Promise<UpdateSource>
|
|
70
72
|
installUpdate?(version: string): Promise<void>
|
|
71
73
|
/** The panel showed the launcher's last attempt. The record is dropped, not shown twice. */
|
|
72
74
|
ackUpdate?(at: number): Promise<void>
|
package/src/http/updates.ts
CHANGED
|
@@ -32,6 +32,15 @@ export type UpdateAttempt =
|
|
|
32
32
|
readonly at: number
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Where this install updates from. `none` is a source tree with no install prefix,
|
|
37
|
+
* which cannot install anything.
|
|
38
|
+
*/
|
|
39
|
+
export type UpdateSource =
|
|
40
|
+
| { readonly channel: 'registry'; readonly registry: string }
|
|
41
|
+
| { readonly channel: 'tarball'; readonly tarballDir: string }
|
|
42
|
+
| { readonly channel: 'none' }
|
|
43
|
+
|
|
35
44
|
export interface UpdateCheck {
|
|
36
45
|
readonly name: string
|
|
37
46
|
readonly current: string
|
|
@@ -39,6 +48,9 @@ export interface UpdateCheck {
|
|
|
39
48
|
readonly updateAvailable: boolean
|
|
40
49
|
readonly channel?: 'registry' | 'tarball'
|
|
41
50
|
readonly installable?: boolean
|
|
51
|
+
/** Set when the install prefix names one, so a panel can say where an update comes from. */
|
|
52
|
+
readonly registry?: string
|
|
53
|
+
readonly tarballDir?: string
|
|
42
54
|
readonly error?: string
|
|
43
55
|
/** The last install this machine recorded, when one is on disk. */
|
|
44
56
|
readonly lastAttempt?: UpdateAttempt
|
|
@@ -62,12 +74,14 @@ export async function checkPackageUpdate(
|
|
|
62
74
|
if (about.name.length === 0) return { ...empty, error: 'package name is missing' }
|
|
63
75
|
const prefix = readPrefixUpdate(cwd, env)
|
|
64
76
|
if (prefix?.channel === 'tarball') {
|
|
65
|
-
const
|
|
77
|
+
const tarballDir = prefix.tarballDir ?? homePackagesDir(homedir())
|
|
78
|
+
const latest = newestTarball(tarballDir, 'shell')
|
|
66
79
|
return {
|
|
67
80
|
name: '@mohou/shell',
|
|
68
81
|
current: about.current,
|
|
69
82
|
latest,
|
|
70
83
|
channel: 'tarball',
|
|
84
|
+
tarballDir,
|
|
71
85
|
installable: true,
|
|
72
86
|
updateAvailable: latest !== null && compareVersion(latest, about.current) > 0,
|
|
73
87
|
}
|
|
@@ -79,7 +93,13 @@ export async function checkPackageUpdate(
|
|
|
79
93
|
const response = await fetch(`${registry.replace(/\/$/, '')}/${name}/latest`, {
|
|
80
94
|
signal: AbortSignal.timeout(3_000),
|
|
81
95
|
})
|
|
82
|
-
if (!response.ok) return {
|
|
96
|
+
if (!response.ok) return {
|
|
97
|
+
...empty,
|
|
98
|
+
name,
|
|
99
|
+
...prefix === undefined ? {} : { channel: prefix.channel, registry: prefix.registry ?? 'https://registry.npmjs.org' },
|
|
100
|
+
installable: prefix !== undefined,
|
|
101
|
+
error: `package registry returned ${response.status}`,
|
|
102
|
+
}
|
|
83
103
|
const body = await response.json() as { version?: unknown }
|
|
84
104
|
const latest = typeof body.version === 'string' ? body.version : null
|
|
85
105
|
return {
|
|
@@ -87,14 +107,34 @@ export async function checkPackageUpdate(
|
|
|
87
107
|
current: about.current,
|
|
88
108
|
latest,
|
|
89
109
|
channel: prefix?.channel ?? 'registry',
|
|
110
|
+
...prefix === undefined ? {} : { registry: prefix.registry ?? 'https://registry.npmjs.org' },
|
|
90
111
|
installable: prefix !== undefined,
|
|
91
112
|
updateAvailable: latest !== null && latest !== about.current,
|
|
92
113
|
}
|
|
93
114
|
} catch (error) {
|
|
94
|
-
return {
|
|
115
|
+
return {
|
|
116
|
+
...empty,
|
|
117
|
+
name,
|
|
118
|
+
...prefix === undefined ? {} : { channel: prefix.channel, registry: prefix.registry ?? 'https://registry.npmjs.org' },
|
|
119
|
+
installable: prefix !== undefined,
|
|
120
|
+
error: error instanceof Error ? error.message : 'update check failed',
|
|
121
|
+
}
|
|
95
122
|
}
|
|
96
123
|
}
|
|
97
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Where this install would update from. Reads the install prefix; asks no registry, so a
|
|
127
|
+
* 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
|
|
129
|
+
* @param cwd - directory the sidecar runs in
|
|
130
|
+
*/
|
|
131
|
+
export function readUpdateSource(env: NodeJS.ProcessEnv = process.env, cwd = process.cwd()): UpdateSource {
|
|
132
|
+
const prefix = readPrefixUpdate(cwd, env)
|
|
133
|
+
if (prefix === undefined) return { channel: 'none' }
|
|
134
|
+
if (prefix.channel === 'tarball') return { channel: 'tarball', tarballDir: prefix.tarballDir ?? homePackagesDir(homedir()) }
|
|
135
|
+
return { channel: 'registry', registry: prefix.registry ?? 'https://registry.npmjs.org' }
|
|
136
|
+
}
|
|
137
|
+
|
|
98
138
|
/**
|
|
99
139
|
* Read the launcher's record of the last install. A missing, unreadable, or malformed file reads as
|
|
100
140
|
* no attempt: the panel shows nothing rather than a guessed outcome.
|