@geminilight/mindos 0.5.16 → 0.5.17
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/cli.js +2 -3
- package/bin/lib/gateway.js +69 -17
- package/package.json +1 -1
- package/scripts/setup.js +1 -1
package/bin/cli.js
CHANGED
|
@@ -242,8 +242,7 @@ const commands = {
|
|
|
242
242
|
// Do NOT call start() here — kickstart -k would kill the just-started process,
|
|
243
243
|
// causing a port-conflict race condition with KeepAlive restart loops.
|
|
244
244
|
console.log(dim(' (First run may take a few minutes to install dependencies and build the app.)'));
|
|
245
|
-
|
|
246
|
-
const ready = await waitForHttp(Number(webPort), { retries: 60, intervalMs: 2000, label: 'Web UI' });
|
|
245
|
+
const ready = await waitForHttp(Number(webPort), { retries: 60, intervalMs: 2000, label: 'Web UI', logFile: LOG_PATH });
|
|
247
246
|
if (!ready) {
|
|
248
247
|
console.error(red('\n✘ Service started but Web UI did not become ready in time.'));
|
|
249
248
|
console.error(dim(' Check logs with: mindos logs\n'));
|
|
@@ -616,7 +615,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
616
615
|
const webPort = updateConfig.port ?? 3456;
|
|
617
616
|
const mcpPort = updateConfig.mcpPort ?? 8781;
|
|
618
617
|
console.log(dim(' (Waiting for Web UI to come back up — first run after update includes a rebuild...)'));
|
|
619
|
-
const ready = await waitForHttp(Number(webPort), { retries: 60, intervalMs: 2000, label: 'Web UI' });
|
|
618
|
+
const ready = await waitForHttp(Number(webPort), { retries: 60, intervalMs: 2000, label: 'Web UI', logFile: LOG_PATH });
|
|
620
619
|
if (ready) {
|
|
621
620
|
const localIP = getLocalIP();
|
|
622
621
|
console.log(`\n${'─'.repeat(53)}`);
|
package/bin/lib/gateway.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync, statSync, renameSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync, statSync, renameSync, unlinkSync, openSync, readSync, closeSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { MINDOS_DIR, LOG_PATH, CLI_PATH, NODE_BIN, CONFIG_PATH } from './constants.js';
|
|
@@ -50,18 +50,76 @@ export async function waitForPortFree(port, { retries = 30, intervalMs = 500 } =
|
|
|
50
50
|
return !(await isPortInUse(port));
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Parse a log line to extract a user-friendly status hint.
|
|
55
|
+
* Returns null if the line isn't interesting enough to show.
|
|
56
|
+
* @internal Exported for testing only.
|
|
57
|
+
*/
|
|
58
|
+
export function parseLogHint(line) {
|
|
59
|
+
const l = line.trim();
|
|
60
|
+
if (!l) return null;
|
|
61
|
+
// npm install progress
|
|
62
|
+
if (/added \d+ packages/i.test(l)) return 'dependencies installed';
|
|
63
|
+
// MCP-specific must come before generic "Installing" match
|
|
64
|
+
if (/Installing MCP dependencies/i.test(l)) return 'installing MCP…';
|
|
65
|
+
if (/Installing app dependencies/i.test(l)) return 'installing dependencies…';
|
|
66
|
+
if (/Updating app dependencies/i.test(l)) return 'updating dependencies…';
|
|
67
|
+
// next build progress
|
|
68
|
+
if (/Building MindOS/i.test(l)) return 'building app…';
|
|
69
|
+
if (/Creating.*optimized.*production/i.test(l)) return 'building app…';
|
|
70
|
+
if (/Compil/i.test(l)) return 'compiling…';
|
|
71
|
+
if (/Collecting page data/i.test(l)) return 'collecting page data…';
|
|
72
|
+
if (/Generating static pages/i.test(l)) return 'generating pages…';
|
|
73
|
+
if (/Finalizing page optimization/i.test(l)) return 'optimizing…';
|
|
74
|
+
if (/[○●◐λƒ]\s+\/\S+.*\d+(\.\d+)?\s*kB/i.test(l)) return 'bundling routes…';
|
|
75
|
+
// next start / ready
|
|
76
|
+
if (/▲ Next\.js/i.test(l)) return 'starting server…';
|
|
77
|
+
if (/Ready in/i.test(l)) return 'starting server…';
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function waitForHttp(port, { retries = 60, intervalMs = 2000, label = 'service', logFile = null } = {}) {
|
|
54
82
|
const start = Date.now();
|
|
55
83
|
const elapsed = () => {
|
|
56
84
|
const s = Math.round((Date.now() - start) / 1000);
|
|
57
85
|
return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m${String(s % 60).padStart(2, '0')}s`;
|
|
58
86
|
};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
|
|
88
|
+
// Track log file position for incremental reads
|
|
89
|
+
let logOffset = 0;
|
|
90
|
+
let lastHint = 'starting…';
|
|
91
|
+
if (logFile) {
|
|
92
|
+
try {
|
|
93
|
+
logOffset = statSync(logFile).size; // start from current end
|
|
94
|
+
} catch { /* file may not exist yet */ }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Read new lines from logFile and update lastHint */
|
|
98
|
+
function updateHintFromLog() {
|
|
99
|
+
if (!logFile) return;
|
|
100
|
+
try {
|
|
101
|
+
const st = statSync(logFile);
|
|
102
|
+
// File was truncated or rotated — reset offset
|
|
103
|
+
if (st.size < logOffset) logOffset = 0;
|
|
104
|
+
if (st.size <= logOffset) return;
|
|
105
|
+
// Read new chunk (at most 4KB to avoid large reads)
|
|
106
|
+
const readSize = Math.min(st.size - logOffset, 4096);
|
|
107
|
+
const buf = Buffer.alloc(readSize);
|
|
108
|
+
const fd = openSync(logFile, 'r');
|
|
109
|
+
try {
|
|
110
|
+
readSync(fd, buf, 0, readSize, logOffset);
|
|
111
|
+
} finally {
|
|
112
|
+
closeSync(fd);
|
|
113
|
+
}
|
|
114
|
+
logOffset = logOffset + readSize;
|
|
115
|
+
const newLines = buf.toString('utf-8').split('\n');
|
|
116
|
+
// Walk lines in order, keep the last interesting hint
|
|
117
|
+
for (const line of newLines) {
|
|
118
|
+
const hint = parseLogHint(line);
|
|
119
|
+
if (hint) lastHint = hint;
|
|
120
|
+
}
|
|
121
|
+
} catch { /* log file may not exist yet or be rotated */ }
|
|
122
|
+
}
|
|
65
123
|
|
|
66
124
|
for (let i = 0; i < retries; i++) {
|
|
67
125
|
try {
|
|
@@ -74,24 +132,18 @@ export async function waitForHttp(port, { retries = 60, intervalMs = 2000, label
|
|
|
74
132
|
req.end();
|
|
75
133
|
});
|
|
76
134
|
if (ok) {
|
|
77
|
-
// Clear line and print success
|
|
78
135
|
process.stdout.write(`\r\x1b[K`);
|
|
79
136
|
process.stdout.write(` ${green('\u2714')} ${label} ready ${dim(`(${elapsed()})`)}\n`);
|
|
80
137
|
return true;
|
|
81
138
|
}
|
|
82
139
|
} catch { /* not ready yet */ }
|
|
83
140
|
|
|
84
|
-
// Update
|
|
85
|
-
|
|
86
|
-
const nextPhase = phases.reduce((idx, p, i) => secs >= p.after ? i : idx, -1);
|
|
87
|
-
if (nextPhase !== currentPhase) {
|
|
88
|
-
currentPhase = nextPhase;
|
|
89
|
-
}
|
|
90
|
-
const hint = currentPhase >= 0 ? phases[currentPhase].msg : '';
|
|
141
|
+
// Update hint from real log output
|
|
142
|
+
updateHintFromLog();
|
|
91
143
|
|
|
92
144
|
// Rewrite the status line in place
|
|
93
145
|
process.stdout.write(`\r\x1b[K`);
|
|
94
|
-
process.stdout.write(cyan(` ⏳ Waiting for ${label}`) + dim(` — ${
|
|
146
|
+
process.stdout.write(cyan(` ⏳ Waiting for ${label}`) + dim(` — ${lastHint} (${elapsed()})`));
|
|
95
147
|
|
|
96
148
|
await new Promise(r => setTimeout(r, intervalMs));
|
|
97
149
|
}
|
package/package.json
CHANGED
package/scripts/setup.js
CHANGED
|
@@ -902,7 +902,7 @@ async function startGuiSetup() {
|
|
|
902
902
|
|
|
903
903
|
// Wait for the server to be ready (120s timeout)
|
|
904
904
|
const { waitForHttp } = await import('../bin/lib/gateway.js');
|
|
905
|
-
const ready = await waitForHttp(usePort, { retries: 120, intervalMs: 1000, label: 'MindOS' });
|
|
905
|
+
const ready = await waitForHttp(usePort, { retries: 120, intervalMs: 1000, label: 'MindOS', logFile: LOG_PATH });
|
|
906
906
|
|
|
907
907
|
if (!ready) {
|
|
908
908
|
write(c.red('\n✘ Server failed to start.\n'));
|