@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 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
- console.log(dim(' Follow live progress with: mindos logs\n'));
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)}`);
@@ -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
- export async function waitForHttp(port, { retries = 60, intervalMs = 2000, label = 'service' } = {}) {
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
- const phases = [
60
- { after: 0, msg: 'installing dependencies' },
61
- { after: 15, msg: 'building app' },
62
- { after: 60, msg: 'still building (first run takes a while)' },
63
- ];
64
- let currentPhase = -1;
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 phase hint
85
- const secs = (Date.now() - start) / 1000;
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(` — ${hint} (${elapsed()})`));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geminilight/mindos",
3
- "version": "0.5.16",
3
+ "version": "0.5.17",
4
4
  "description": "MindOS — Human-Agent Collaborative Mind System. Local-first knowledge base that syncs your mind to all AI Agents via MCP.",
5
5
  "keywords": [
6
6
  "mindos",
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'));