@mobius-os/mobius 0.3.7 → 0.3.9
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 +1 -1
- package/src/aimux.ts +37 -5
package/package.json
CHANGED
package/src/aimux.ts
CHANGED
|
@@ -88,12 +88,25 @@ async function pythonForAimux(onProgress?: (p: InstallProgress) => void): Promis
|
|
|
88
88
|
// 解压到 ~/.mobius/python-bundle/ 后用 `<python> -m aimux` 运行,彻底绕开宿主机
|
|
89
89
|
// 系统 python(如被精简掉 ensurepip 的容器镜像)。aimux 全部依赖为纯 Python,
|
|
90
90
|
// 故三平台可共用同一套打包产物,分别按 arch 发布到 CDN。
|
|
91
|
-
const BUNDLE_VER = '
|
|
91
|
+
const BUNDLE_VER = '2'
|
|
92
|
+
const BUNDLE_AIMUX_VERSION = '0.1.21'
|
|
92
93
|
const bundleDir = () => path.join(mobiusHome(), 'python-bundle')
|
|
93
94
|
const bundlePython = () => WIN
|
|
94
95
|
? path.join(bundleDir(), 'python', 'python.exe')
|
|
95
96
|
: path.join(bundleDir(), 'python', 'bin', 'python3')
|
|
96
97
|
|
|
98
|
+
/** Persistent child-process diagnostics. Never include the JWT in this file. */
|
|
99
|
+
export const aimuxLogPath = () => path.join(mobiusHome(), 'aimux.log')
|
|
100
|
+
|
|
101
|
+
function appendAimuxLog(write: { queue: Promise<void> }, text: string): void {
|
|
102
|
+
write.queue = write.queue
|
|
103
|
+
.then(async () => {
|
|
104
|
+
await fs.mkdir(mobiusHome(), { recursive: true })
|
|
105
|
+
await fs.appendFile(aimuxLogPath(), text, 'utf8')
|
|
106
|
+
})
|
|
107
|
+
.catch(() => {})
|
|
108
|
+
}
|
|
109
|
+
|
|
97
110
|
/** 当前平台对应的内置运行时包名;mac-arm64 走 mac-x64(Rosetta 2)。 */
|
|
98
111
|
export function bundleArch(): string | null {
|
|
99
112
|
const { platform, arch } = process
|
|
@@ -107,8 +120,19 @@ export function bundleArch(): string | null {
|
|
|
107
120
|
export const bundleBaseUrl = () => (process.env.MOBIUS_TUI_PYTHON_BUNDLE_URL || 'https://serve.nutshellai.cn/publish/auto/mobius-tui').replace(/\/$/, '')
|
|
108
121
|
export const bundleUrl = (arch: string) => `${bundleBaseUrl()}/mobius-python-${arch}-v${BUNDLE_VER}.zip`
|
|
109
122
|
|
|
123
|
+
export function bundleHealthCheckCode(platform: NodeJS.Platform = process.platform): string {
|
|
124
|
+
const imports = platform === 'win32'
|
|
125
|
+
? 'import aimux, aimux.bridge_client, win32_setctime'
|
|
126
|
+
: 'import aimux, aimux.bridge_client'
|
|
127
|
+
return `${imports}; assert aimux.__version__ == '${BUNDLE_AIMUX_VERSION}'`
|
|
128
|
+
}
|
|
129
|
+
|
|
110
130
|
function bundleReady(): boolean {
|
|
111
|
-
return existsSync(bundlePython()) && spawnSync(
|
|
131
|
+
return existsSync(bundlePython()) && spawnSync(
|
|
132
|
+
bundlePython(),
|
|
133
|
+
['-c', bundleHealthCheckCode()],
|
|
134
|
+
{ stdio: 'ignore', windowsHide: true },
|
|
135
|
+
).status === 0
|
|
112
136
|
}
|
|
113
137
|
|
|
114
138
|
async function downloadBundle(arch: string, onProgress?: (p: InstallProgress) => void): Promise<{ ok: boolean; error?: string; zipPath?: string }> {
|
|
@@ -288,9 +312,13 @@ export class AimuxSupervisor {
|
|
|
288
312
|
this.child = child
|
|
289
313
|
this.startHeartbeat()
|
|
290
314
|
let tail = '' // 缓存 aimux 最近输出, 进程异常退出时带进状态行, 便于诊断(code=1 不再是黑盒)
|
|
315
|
+
const logWrite = { queue: Promise.resolve() }
|
|
316
|
+
appendAimuxLog(logWrite, `\n===== AIMUX start ${new Date().toISOString()} =====\n`)
|
|
317
|
+
appendAimuxLog(logWrite, `server=${server} identifier=${identifier} platform=${process.platform} arch=${process.arch}\n`)
|
|
291
318
|
const classify = (buf: Buffer) => {
|
|
292
319
|
const text = buf.toString('utf8')
|
|
293
320
|
tail = (tail + text).slice(-4000)
|
|
321
|
+
appendAimuxLog(logWrite, text)
|
|
294
322
|
if (!this.bridgeConnected && /connected|registered|event stream|heartbeat|sse/i.test(text)) {
|
|
295
323
|
onStatus({ state: 'starting', phase: 'heartbeat', detail: 'AIMUX 已启动,等待 bridge 心跳确认…', identifier })
|
|
296
324
|
} else if (/connection (refused|reset|closed|error)|failed to connect|unauthorized|forbidden|token.*invalid/i.test(text)) {
|
|
@@ -298,15 +326,19 @@ export class AimuxSupervisor {
|
|
|
298
326
|
}
|
|
299
327
|
}
|
|
300
328
|
child.stdout?.on('data', classify); child.stderr?.on('data', classify)
|
|
301
|
-
child.on('error', e =>
|
|
329
|
+
child.on('error', e => {
|
|
330
|
+
appendAimuxLog(logWrite, `\n[spawn error] ${e.stack || e.message}\n`)
|
|
331
|
+
onStatus({ state: 'failed', phase: 'retrying', detail: `AIMUX 启动失败: ${e.message} · 日志: ${aimuxLogPath()}`, identifier })
|
|
332
|
+
})
|
|
302
333
|
child.on('exit', code => {
|
|
303
334
|
if (this.child !== child) return
|
|
304
335
|
this.child = null
|
|
305
336
|
this.stopHeartbeat()
|
|
306
337
|
if (this.stopping) { onStatus({ state: 'stopped', phase: 'idle', detail: 'AIMUX 已停止', identifier }); return }
|
|
338
|
+
appendAimuxLog(logWrite, `\n===== AIMUX exit code=${code} ${new Date().toISOString()} =====\n`)
|
|
307
339
|
const reason = code !== 0 && tail.trim()
|
|
308
|
-
? `AIMUX 进程退出(code=${code}): ${tail.trim().split(/[\r\n]+/).filter(Boolean).slice(-3).join(' ⏎ ').slice(-
|
|
309
|
-
: `AIMUX 进程退出(code=${code}
|
|
340
|
+
? `AIMUX 进程退出(code=${code}): ${tail.trim().split(/[\r\n]+/).filter(Boolean).slice(-3).join(' ⏎ ').slice(-220)} · 日志: ${aimuxLogPath()}`
|
|
341
|
+
: `AIMUX 进程退出(code=${code}) · 日志: ${aimuxLogPath()}`
|
|
310
342
|
this.scheduleReconnect(reason)
|
|
311
343
|
})
|
|
312
344
|
}
|