yo-moss-ai 0.0.11 → 0.0.13
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/bin/bootstrap-deps.mjs +166 -54
- package/bin/runtime-manifest.json +1 -1
- package/bin/yo-moss-ai.mjs +3 -1
- package/dist/assets/esp-flasher-CLKzE7JE.js +2 -2
- package/dist/assets/index-CZ_NROJp.js +8 -8
- package/dist-server/camera.mjs +1 -1
- package/dist-server/ffmpeg.mjs +1 -1
- package/dist-server/index.mjs +1 -1
- package/dist-server/ir-import-validate.mjs +1 -1
- package/dist-server/mcp/backoff.mjs +1 -1
- package/dist-server/mcp/bridge-manager.mjs +1 -1
- package/dist-server/mcp/builtin-tools.mjs +1 -1
- package/dist-server/mcp/dedupe-servers.mjs +1 -1
- package/dist-server/mcp/doctor.mjs +1 -1
- package/dist-server/mcp/fixtures/echo-mcp.mjs +1 -1
- package/dist-server/mcp/fixtures/hotkey-mcp.mjs +1 -1
- package/dist-server/mcp/fixtures/onvif-mcp.mjs +1 -1
- package/dist-server/mcp/fixtures/shell-mcp.mjs +1 -1
- package/dist-server/mcp/hotkey-agent-docs.mjs +1 -1
- package/dist-server/mcp/hotkey-driver.mjs +1 -1
- package/dist-server/mcp/hotkey-handlers.mjs +1 -1
- package/dist-server/mcp/hotkey-import-validate.mjs +1 -1
- package/dist-server/mcp/hotkey-store.mjs +1 -1
- package/dist-server/mcp/http-client.mjs +1 -1
- package/dist-server/mcp/http-pipe.mjs +1 -1
- package/dist-server/mcp/local-probe.mjs +1 -1
- package/dist-server/mcp/marketplace.mjs +1 -1
- package/dist-server/mcp/onvif-handlers.mjs +1 -1
- package/dist-server/mcp/parse-server-json.mjs +1 -1
- package/dist-server/mcp/paths.mjs +1 -1
- package/dist-server/mcp/routes.mjs +1 -1
- package/dist-server/mcp/schema.mjs +1 -1
- package/dist-server/mcp/settings.mjs +1 -1
- package/dist-server/mcp/shell-approval.mjs +1 -1
- package/dist-server/mcp/shell-policy.mjs +1 -1
- package/dist-server/mcp/shell-trash.mjs +1 -1
- package/dist-server/mcp/stdio-pipe.mjs +1 -1
- package/dist-server/mcp/store.mjs +1 -1
- package/dist-server/mcp/task-notify.mjs +1 -1
- package/dist-server/mcp/ws-connect.mjs +1 -1
- package/dist-server/media-cache.mjs +1 -1
- package/package.json +1 -1
package/bin/bootstrap-deps.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { spawn } from 'node:child_process'
|
|
|
7
7
|
import { pipeline } from 'node:stream/promises'
|
|
8
8
|
|
|
9
9
|
export const PROGRESS_BAR_WIDTH = 28
|
|
10
|
+
export const MEDIA_PACKAGE_NAME = 'yo-moss-ai-media'
|
|
10
11
|
|
|
11
12
|
export function mediaCacheDir() {
|
|
12
13
|
return process.env.MOSS_MEDIA_ROOT || path.join(os.homedir(), '.moss-desktop', 'media')
|
|
@@ -24,15 +25,29 @@ export function formatProgressBar(label, ratio, width = PROGRESS_BAR_WIDTH) {
|
|
|
24
25
|
return `${label} [${bar}] ${pct}%`
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
/** 终端单行刷新;非 TTY(部分 npx 环境)按步进换行,避免长时间无输出 */
|
|
29
|
+
export function createProgressReporter(label) {
|
|
30
|
+
let lastPct = -1
|
|
31
|
+
return (ratio) => {
|
|
32
|
+
const pct = Math.min(100, Math.max(0, Math.round(ratio * 100)))
|
|
33
|
+
const line = formatProgressBar(label, ratio)
|
|
34
|
+
if (process.stdout.isTTY) {
|
|
35
|
+
process.stdout.write(`\r\x1b[2K${line}`)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
if (lastPct < 0 || pct >= lastPct + 10 || pct === 100) {
|
|
39
|
+
console.log(line)
|
|
40
|
+
lastPct = pct
|
|
41
|
+
}
|
|
42
|
+
}
|
|
29
43
|
}
|
|
30
44
|
|
|
45
|
+
/** @deprecated 单文件 CDN 对大体积 npm 包不可靠,保留仅供测试/兜底 */
|
|
31
46
|
export function cdnUrls(version, relativePath) {
|
|
32
47
|
const encoded = relativePath.split('/').map((part) => encodeURIComponent(part)).join('/')
|
|
33
48
|
return [
|
|
34
|
-
`https://cdn.jsdelivr.net/npm
|
|
35
|
-
`https://unpkg.com
|
|
49
|
+
`https://cdn.jsdelivr.net/npm/${MEDIA_PACKAGE_NAME}@${version}/${encoded}`,
|
|
50
|
+
`https://unpkg.com/${MEDIA_PACKAGE_NAME}@${version}/${encoded}`,
|
|
36
51
|
]
|
|
37
52
|
}
|
|
38
53
|
|
|
@@ -90,14 +105,41 @@ export function fileReady(filePath, expectedSize) {
|
|
|
90
105
|
}
|
|
91
106
|
}
|
|
92
107
|
|
|
93
|
-
function
|
|
108
|
+
export function dirByteSize(dir) {
|
|
109
|
+
if (!fs.existsSync(dir)) return 0
|
|
110
|
+
let total = 0
|
|
111
|
+
for (const name of fs.readdirSync(dir)) {
|
|
112
|
+
const full = path.join(dir, name)
|
|
113
|
+
const stat = fs.statSync(full)
|
|
114
|
+
if (stat.isDirectory()) total += dirByteSize(full)
|
|
115
|
+
else total += stat.size
|
|
116
|
+
}
|
|
117
|
+
return total
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function copyTree(src, dest) {
|
|
121
|
+
fs.mkdirSync(dest, { recursive: true })
|
|
122
|
+
for (const name of fs.readdirSync(src)) {
|
|
123
|
+
if (name.startsWith('.')) continue
|
|
124
|
+
const from = path.join(src, name)
|
|
125
|
+
const to = path.join(dest, name)
|
|
126
|
+
if (fs.statSync(from).isDirectory()) copyTree(from, to)
|
|
127
|
+
else fs.copyFileSync(from, to)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function runNpmInstall(cwd, extraArgs = []) {
|
|
94
132
|
return new Promise((resolve, reject) => {
|
|
95
133
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
96
|
-
const child = spawn(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
134
|
+
const child = spawn(
|
|
135
|
+
npmCmd,
|
|
136
|
+
['install', '--omit=dev', '--no-audit', '--no-fund', '--loglevel=error', ...extraArgs],
|
|
137
|
+
{
|
|
138
|
+
cwd,
|
|
139
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
140
|
+
shell: process.platform === 'win32',
|
|
141
|
+
},
|
|
142
|
+
)
|
|
101
143
|
let errText = ''
|
|
102
144
|
child.stderr.on('data', (chunk) => {
|
|
103
145
|
errText += String(chunk)
|
|
@@ -109,7 +151,84 @@ function runNpmInstall(cwd) {
|
|
|
109
151
|
})
|
|
110
152
|
}
|
|
111
153
|
|
|
112
|
-
|
|
154
|
+
/**
|
|
155
|
+
* 通过 npm registry 安装 yo-moss-ai-media 整包(比 CDN 单文件拉取可靠)。
|
|
156
|
+
*/
|
|
157
|
+
export const NPM_REGISTRIES = [
|
|
158
|
+
'https://registry.npmjs.org',
|
|
159
|
+
'https://registry.npmmirror.com',
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
export async function installMediaBundleFromNpm(
|
|
163
|
+
version,
|
|
164
|
+
cacheDir,
|
|
165
|
+
{ totalBytes = 0, onProgress, installImpl } = {},
|
|
166
|
+
) {
|
|
167
|
+
const staging = path.join(cacheDir, '.npm-staging')
|
|
168
|
+
fs.rmSync(staging, { recursive: true, force: true })
|
|
169
|
+
fs.mkdirSync(staging, { recursive: true })
|
|
170
|
+
fs.writeFileSync(
|
|
171
|
+
path.join(staging, 'package.json'),
|
|
172
|
+
`${JSON.stringify({
|
|
173
|
+
name: 'yo-moss-ai-media-staging',
|
|
174
|
+
private: true,
|
|
175
|
+
dependencies: { [MEDIA_PACKAGE_NAME]: version },
|
|
176
|
+
}, null, 2)}\n`,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
const expected = totalBytes > 0 ? totalBytes : 56_000_000
|
|
180
|
+
let tick = 0
|
|
181
|
+
const poll = setInterval(() => {
|
|
182
|
+
tick += 1
|
|
183
|
+
const size = dirByteSize(path.join(staging, 'node_modules', MEDIA_PACKAGE_NAME))
|
|
184
|
+
if (size > 0) {
|
|
185
|
+
onProgress?.(Math.min(0.92, size / expected))
|
|
186
|
+
} else {
|
|
187
|
+
// npm 解压前 node_modules 为空,用轻量动画避免长时间 0%
|
|
188
|
+
onProgress?.(0.02 + (tick % 6) * 0.012)
|
|
189
|
+
}
|
|
190
|
+
}, 350)
|
|
191
|
+
|
|
192
|
+
let lastError
|
|
193
|
+
try {
|
|
194
|
+
for (const registry of NPM_REGISTRIES) {
|
|
195
|
+
try {
|
|
196
|
+
const installer = installImpl || ((cwd) => runNpmInstall(cwd, ['--registry', registry]))
|
|
197
|
+
await installer(staging)
|
|
198
|
+
lastError = null
|
|
199
|
+
break
|
|
200
|
+
} catch (err) {
|
|
201
|
+
lastError = err
|
|
202
|
+
fs.rmSync(path.join(staging, 'node_modules'), { recursive: true, force: true })
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (lastError) {
|
|
206
|
+
throw new Error(`运行资源自动安装失败(${MEDIA_PACKAGE_NAME}@${version}),请检查网络后重试 npx yo-moss-ai`)
|
|
207
|
+
}
|
|
208
|
+
} finally {
|
|
209
|
+
clearInterval(poll)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const pkgRoot = path.join(staging, 'node_modules', MEDIA_PACKAGE_NAME)
|
|
213
|
+
if (!fs.existsSync(pkgRoot)) {
|
|
214
|
+
throw new Error(`运行资源自动安装失败(${MEDIA_PACKAGE_NAME}@${version}),请检查网络后重试 npx yo-moss-ai`)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
onProgress?.(0.95)
|
|
218
|
+
for (const dir of ['models', 'textures', 'stl']) {
|
|
219
|
+
const from = path.join(pkgRoot, dir)
|
|
220
|
+
if (!fs.existsSync(from)) continue
|
|
221
|
+
copyTree(from, path.join(cacheDir, dir))
|
|
222
|
+
}
|
|
223
|
+
fs.rmSync(staging, { recursive: true, force: true })
|
|
224
|
+
onProgress?.(1)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export async function ensureRuntimeNpmDeps(
|
|
228
|
+
npmDeps,
|
|
229
|
+
depsRoot = runtimeDepsRoot(),
|
|
230
|
+
{ installImpl = runNpmInstall, onProgress } = {},
|
|
231
|
+
) {
|
|
113
232
|
fs.mkdirSync(depsRoot, { recursive: true })
|
|
114
233
|
const pkgPath = path.join(depsRoot, 'package.json')
|
|
115
234
|
const deps = Object.fromEntries(npmDeps.map((item) => [item.name, item.version]))
|
|
@@ -128,7 +247,21 @@ export async function ensureRuntimeNpmDeps(npmDeps, depsRoot = runtimeDepsRoot()
|
|
|
128
247
|
if (fs.existsSync(nutEntry)) return
|
|
129
248
|
}
|
|
130
249
|
|
|
131
|
-
|
|
250
|
+
let tick = 0
|
|
251
|
+
const poll = onProgress
|
|
252
|
+
? setInterval(() => {
|
|
253
|
+
tick += 1
|
|
254
|
+
const size = dirByteSize(path.join(depsRoot, 'node_modules'))
|
|
255
|
+
if (size > 0) onProgress(Math.min(0.95, size / 80_000_000))
|
|
256
|
+
else onProgress(0.02 + (tick % 6) * 0.012)
|
|
257
|
+
}, 350)
|
|
258
|
+
: null
|
|
259
|
+
try {
|
|
260
|
+
await installImpl(depsRoot)
|
|
261
|
+
} finally {
|
|
262
|
+
if (poll) clearInterval(poll)
|
|
263
|
+
}
|
|
264
|
+
onProgress?.(1)
|
|
132
265
|
fs.writeFileSync(marker, nextMarker)
|
|
133
266
|
}
|
|
134
267
|
|
|
@@ -164,8 +297,8 @@ export async function bootstrapRuntimeDeps(opts) {
|
|
|
164
297
|
version,
|
|
165
298
|
cacheDir = mediaCacheDir(),
|
|
166
299
|
depsRoot = runtimeDepsRoot(),
|
|
167
|
-
|
|
168
|
-
installNpmDeps = (npmDeps) => ensureRuntimeNpmDeps(npmDeps, depsRoot),
|
|
300
|
+
installMediaBundle = installMediaBundleFromNpm,
|
|
301
|
+
installNpmDeps = (npmDeps, onProgress) => ensureRuntimeNpmDeps(npmDeps, depsRoot, { onProgress }),
|
|
169
302
|
} = opts
|
|
170
303
|
|
|
171
304
|
const distDir = path.join(root, 'dist')
|
|
@@ -195,57 +328,36 @@ export async function bootstrapRuntimeDeps(opts) {
|
|
|
195
328
|
console.log('')
|
|
196
329
|
if (needMedia) {
|
|
197
330
|
const mb = (totalBytes / 1024 / 1024).toFixed(0)
|
|
198
|
-
console.log(
|
|
331
|
+
console.log(`首次运行:正在下载 UI 资源(约 ${mb} MB),完成后自动启动…`)
|
|
199
332
|
} else if (needNpm) {
|
|
200
|
-
console.log('
|
|
333
|
+
console.log('首次运行:正在安装扩展组件,完成后自动启动…')
|
|
201
334
|
}
|
|
202
335
|
|
|
203
336
|
if (needMedia) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
continue
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const fileSize = file.size || 0
|
|
216
|
-
let fileDone = 0
|
|
217
|
-
let lastError
|
|
218
|
-
for (const url of cdnUrls(mediaVersion, file.path)) {
|
|
219
|
-
try {
|
|
220
|
-
await downloadFile(url, dest, (chunkLen) => {
|
|
221
|
-
fileDone += chunkLen
|
|
222
|
-
const current = doneBytes + Math.min(fileDone, fileSize || fileDone)
|
|
223
|
-
renderBar('下载资源', totalBytes ? current / totalBytes : 1)
|
|
224
|
-
})
|
|
225
|
-
lastError = null
|
|
226
|
-
break
|
|
227
|
-
} catch (err) {
|
|
228
|
-
lastError = err
|
|
229
|
-
fileDone = 0
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (lastError) throw new Error(`无法下载 ${file.path}: ${lastError.message}`)
|
|
337
|
+
const reportMedia = createProgressReporter('下载资源')
|
|
338
|
+
reportMedia(0.02)
|
|
339
|
+
await installMediaBundle(mediaVersion, cacheDir, {
|
|
340
|
+
totalBytes,
|
|
341
|
+
onProgress: reportMedia,
|
|
342
|
+
})
|
|
343
|
+
if (process.stdout.isTTY) process.stdout.write('\n')
|
|
344
|
+
else console.log(formatProgressBar('下载资源', 1))
|
|
233
345
|
|
|
234
|
-
|
|
235
|
-
|
|
346
|
+
if (!mediaCacheReady(mediaFiles, cacheDir)) {
|
|
347
|
+
throw new Error('资源校验失败,请删除 ~/.moss-desktop/media 文件夹后重试')
|
|
236
348
|
}
|
|
237
|
-
process.stdout.write('\n')
|
|
238
349
|
}
|
|
239
350
|
|
|
240
351
|
if (needNpm) {
|
|
241
|
-
|
|
352
|
+
const reportNpm = createProgressReporter('安装扩展依赖')
|
|
353
|
+
reportNpm(0.02)
|
|
242
354
|
try {
|
|
243
|
-
await installNpmDeps(npmDeps)
|
|
244
|
-
|
|
245
|
-
|
|
355
|
+
await installNpmDeps(npmDeps, reportNpm)
|
|
356
|
+
if (process.stdout.isTTY) process.stdout.write('\n')
|
|
357
|
+
else console.log(formatProgressBar('安装扩展依赖', 1))
|
|
246
358
|
} catch (err) {
|
|
247
|
-
process.stdout.write('\n')
|
|
248
|
-
console.warn('
|
|
359
|
+
if (process.stdout.isTTY) process.stdout.write('\n')
|
|
360
|
+
console.warn('扩展组件安装失败,快捷键/媒体键可能不可用:', err.message)
|
|
249
361
|
}
|
|
250
362
|
}
|
|
251
363
|
|
package/bin/yo-moss-ai.mjs
CHANGED
|
@@ -28,11 +28,13 @@ if (!fs.existsSync(server)) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
console.log(`yo-moss-ai${pkgVersion ? ` v${pkgVersion}` : ''}`)
|
|
31
|
+
if (typeof process.stdout.write === 'function') process.stdout.write('')
|
|
31
32
|
|
|
32
33
|
try {
|
|
33
34
|
await bootstrapRuntimeDeps({ root, version: pkg.version })
|
|
34
35
|
} catch (err) {
|
|
35
|
-
console.error('\n
|
|
36
|
+
console.error('\n启动准备失败:', err?.message || err)
|
|
37
|
+
console.error('请检查网络连接后重新运行:npx yo-moss-ai')
|
|
36
38
|
process.exit(1)
|
|
37
39
|
}
|
|
38
40
|
|