flowmind 1.5.6 → 1.5.7
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/CHANGELOG.md +2 -1
- package/core/update-notifier.js +54 -1
- package/package.json +1 -1
- package/tui/app.jsx +44 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [1.5.
|
|
3
|
+
## [1.5.7] - 2026-07-02
|
|
4
4
|
|
|
5
5
|
### Added
|
|
6
6
|
- Added a `check-update` script and CLI command so already installed users can quickly see whether a newer npm release is available
|
|
7
|
+
- TUI now checks npm for a newer release after startup and shows a non-blocking update reminder banner when one is available
|
|
7
8
|
|
|
8
9
|
### Fixed
|
|
9
10
|
- TUI and dashboard now degrade gracefully on narrow or very small terminals instead of crashing on resize
|
package/core/update-notifier.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const { execSync } = require('child_process');
|
|
1
|
+
const { execFile, execSync } = require('child_process');
|
|
2
|
+
const { promisify } = require('util');
|
|
2
3
|
|
|
3
4
|
function compareVersions(currentVersion, latestVersion) {
|
|
4
5
|
const current = parseVersion(currentVersion);
|
|
@@ -23,6 +24,16 @@ function getLatestVersion(packageName = 'flowmind') {
|
|
|
23
24
|
return execSync(`npm view ${packageName} version`, { encoding: 'utf-8' }).trim();
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
async function getLatestVersionAsync(packageName = 'flowmind') {
|
|
28
|
+
const run = promisify(execFile);
|
|
29
|
+
const result = await run('npm', ['view', packageName, 'version'], {
|
|
30
|
+
encoding: 'utf-8',
|
|
31
|
+
timeout: 2500,
|
|
32
|
+
maxBuffer: 1024 * 64
|
|
33
|
+
});
|
|
34
|
+
return String(result).trim();
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
function isGlobalInstall(packageJsonPath, packageName = 'flowmind') {
|
|
27
38
|
try {
|
|
28
39
|
const globalRoot = execSync('npm root -g', { encoding: 'utf-8' }).trim();
|
|
@@ -65,10 +76,52 @@ function getUpdateStatus({
|
|
|
65
76
|
};
|
|
66
77
|
}
|
|
67
78
|
|
|
79
|
+
async function getUpdateStatusAsync({
|
|
80
|
+
packageName = 'flowmind',
|
|
81
|
+
currentVersion = '0.0.0',
|
|
82
|
+
packageJsonPath = ''
|
|
83
|
+
} = {}) {
|
|
84
|
+
const [latestVersion, globalInstall] = await Promise.all([
|
|
85
|
+
getLatestVersionAsync(packageName),
|
|
86
|
+
isGlobalInstallAsync(packageJsonPath, packageName)
|
|
87
|
+
]);
|
|
88
|
+
const comparison = compareVersions(currentVersion, latestVersion);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
packageName,
|
|
92
|
+
currentVersion,
|
|
93
|
+
latestVersion,
|
|
94
|
+
comparison,
|
|
95
|
+
globalInstall,
|
|
96
|
+
installCommand: buildInstallCommand({
|
|
97
|
+
packageName,
|
|
98
|
+
version: latestVersion,
|
|
99
|
+
globalInstall
|
|
100
|
+
})
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function isGlobalInstallAsync(packageJsonPath, packageName = 'flowmind') {
|
|
105
|
+
try {
|
|
106
|
+
const run = promisify(execFile);
|
|
107
|
+
const result = await run('npm', ['root', '-g'], {
|
|
108
|
+
encoding: 'utf-8',
|
|
109
|
+
timeout: 2500,
|
|
110
|
+
maxBuffer: 1024 * 16
|
|
111
|
+
});
|
|
112
|
+
const globalRoot = String(result).trim();
|
|
113
|
+
return String(packageJsonPath || '').startsWith(globalRoot) || String(packageJsonPath || '').includes(`${globalRoot}/${packageName}`);
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
68
119
|
module.exports = {
|
|
69
120
|
buildInstallCommand,
|
|
70
121
|
compareVersions,
|
|
71
122
|
getLatestVersion,
|
|
123
|
+
getLatestVersionAsync,
|
|
72
124
|
getUpdateStatus,
|
|
125
|
+
getUpdateStatusAsync,
|
|
73
126
|
isGlobalInstall
|
|
74
127
|
};
|
package/package.json
CHANGED
package/tui/app.jsx
CHANGED
|
@@ -5,6 +5,7 @@ const ChatPanel = require('./components/ChatPanel.jsx');
|
|
|
5
5
|
const StatusBar = require('./components/StatusBar.jsx');
|
|
6
6
|
const { formatResultText } = require('./format-result');
|
|
7
7
|
const { getTuiLayout, MIN_COLUMNS, MIN_ROWS } = require('./layout');
|
|
8
|
+
const { getUpdateStatusAsync } = require('../core/update-notifier');
|
|
8
9
|
|
|
9
10
|
class TuiErrorBoundary extends React.Component {
|
|
10
11
|
constructor(props) {
|
|
@@ -41,6 +42,7 @@ function App({ flowmind, asciiMode = false }) {
|
|
|
41
42
|
const MAX_MESSAGES = 60;
|
|
42
43
|
const [messages, setMessages] = React.useState([]);
|
|
43
44
|
const [isProcessing, setIsProcessing] = React.useState(false);
|
|
45
|
+
const [updateNotice, setUpdateNotice] = React.useState(null);
|
|
44
46
|
const [focusPanel, setFocusPanel] = React.useState('chat'); // 'chat' | 'sidebar'
|
|
45
47
|
const [terminalSize, setTerminalSize] = React.useState(() => ({
|
|
46
48
|
columns: Number(process.stdout?.columns) || 0,
|
|
@@ -56,6 +58,39 @@ function App({ flowmind, asciiMode = false }) {
|
|
|
56
58
|
return () => { mountedRef.current = false; };
|
|
57
59
|
}, []);
|
|
58
60
|
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
let cancelled = false;
|
|
63
|
+
|
|
64
|
+
async function checkForUpdates() {
|
|
65
|
+
try {
|
|
66
|
+
const status = await getUpdateStatusAsync({
|
|
67
|
+
packageName: 'flowmind',
|
|
68
|
+
currentVersion: require('../package.json').version,
|
|
69
|
+
packageJsonPath: require.resolve('../package.json')
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (cancelled || !mountedRef.current) return;
|
|
73
|
+
if (status.comparison < 0) {
|
|
74
|
+
setUpdateNotice({
|
|
75
|
+
currentVersion: status.currentVersion,
|
|
76
|
+
latestVersion: status.latestVersion,
|
|
77
|
+
installCommand: status.installCommand
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
if (!cancelled && mountedRef.current) {
|
|
82
|
+
setUpdateNotice(null);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
checkForUpdates();
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
cancelled = true;
|
|
91
|
+
};
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
59
94
|
React.useEffect(() => {
|
|
60
95
|
const updateSize = () => {
|
|
61
96
|
if (!mountedRef.current) return;
|
|
@@ -149,6 +184,15 @@ function App({ flowmind, asciiMode = false }) {
|
|
|
149
184
|
return (
|
|
150
185
|
React.createElement(TuiErrorBoundary, null,
|
|
151
186
|
React.createElement(Box, { flexDirection: 'column', width: '100%', height: '100%' },
|
|
187
|
+
updateNotice && React.createElement(Box, {
|
|
188
|
+
borderStyle: 'single',
|
|
189
|
+
borderColor: 'yellow',
|
|
190
|
+
paddingX: 1,
|
|
191
|
+
marginBottom: 1
|
|
192
|
+
},
|
|
193
|
+
React.createElement(Text, { color: 'yellow' }, `Update available: ${updateNotice.currentVersion} → ${updateNotice.latestVersion}`),
|
|
194
|
+
React.createElement(Text, { color: 'gray' }, ` Run ${updateNotice.installCommand}`)
|
|
195
|
+
),
|
|
152
196
|
React.createElement(Box, { flexDirection: 'row', flexGrow: 1 },
|
|
153
197
|
React.createElement(Sidebar, { flowmind: flowmind, width: layout.sidebarWidth, onSkillSelect: handleSkillSelect, focused: focusPanel === 'sidebar', asciiMode: asciiMode }),
|
|
154
198
|
React.createElement(ChatPanel, {
|