@sylphx/flow 2.15.1 → 2.15.3
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 +13 -0
- package/package.json +1 -1
- package/src/services/auto-upgrade.ts +136 -52
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @sylphx/flow
|
|
2
2
|
|
|
3
|
+
## 2.15.3 (2025-12-17)
|
|
4
|
+
|
|
5
|
+
### ⚡️ Performance
|
|
6
|
+
|
|
7
|
+
- **auto-upgrade:** simplify - no TTL, always background check ([5621a21](https://github.com/SylphxAI/flow/commit/5621a21ab2a3eeb54f3fecadd32c19269bd22312))
|
|
8
|
+
- **auto-upgrade:** cache target current version too ([229a400](https://github.com/SylphxAI/flow/commit/229a4002bde6da3540c014eb39e8a941e072a4db))
|
|
9
|
+
|
|
10
|
+
## 2.15.2 (2025-12-17)
|
|
11
|
+
|
|
12
|
+
### ⚡️ Performance
|
|
13
|
+
|
|
14
|
+
- **auto-upgrade:** non-blocking version check with cache ([0550e44](https://github.com/SylphxAI/flow/commit/0550e44ea9b471ae07b2ea13c196354a7a32a605))
|
|
15
|
+
|
|
3
16
|
## 2.15.1 (2025-12-17)
|
|
4
17
|
|
|
5
18
|
### Improvements
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylphx/flow",
|
|
3
|
-
"version": "2.15.
|
|
3
|
+
"version": "2.15.3",
|
|
4
4
|
"description": "One CLI to rule them all. Unified orchestration layer for Claude Code, OpenCode, Cursor and all AI development tools. Auto-detection, auto-installation, auto-upgrade.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-Upgrade Service
|
|
3
|
-
*
|
|
3
|
+
* Non-blocking version check with cache
|
|
4
|
+
* Checks in background, shows result on next run
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import { exec } from 'node:child_process';
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
7
9
|
import fs from 'node:fs/promises';
|
|
10
|
+
import os from 'node:os';
|
|
8
11
|
import path from 'node:path';
|
|
9
12
|
import { fileURLToPath } from 'node:url';
|
|
10
13
|
import { promisify } from 'node:util';
|
|
@@ -16,6 +19,15 @@ import { TargetInstaller } from './target-installer.js';
|
|
|
16
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
20
|
const __dirname = path.dirname(__filename);
|
|
18
21
|
|
|
22
|
+
// Version info file (stores last background check result)
|
|
23
|
+
const VERSION_FILE = path.join(os.homedir(), '.sylphx-flow', 'versions.json');
|
|
24
|
+
|
|
25
|
+
interface VersionInfo {
|
|
26
|
+
flowLatest?: string;
|
|
27
|
+
targetLatest?: Record<string, string>;
|
|
28
|
+
targetCurrent?: Record<string, string>;
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
const execAsync = promisify(exec);
|
|
20
32
|
|
|
21
33
|
export interface UpgradeStatus {
|
|
@@ -43,80 +55,152 @@ export class AutoUpgrade {
|
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @param targetId - Optional target CLI ID to check for upgrades
|
|
48
|
-
* @returns Upgrade status indicating what needs upgrading
|
|
58
|
+
* Read version info from last background check
|
|
49
59
|
*/
|
|
50
|
-
async
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
private async readVersionInfo(): Promise<VersionInfo | null> {
|
|
61
|
+
try {
|
|
62
|
+
if (!existsSync(VERSION_FILE)) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const data = await fs.readFile(VERSION_FILE, 'utf-8');
|
|
66
|
+
return JSON.parse(data);
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Write version info
|
|
74
|
+
*/
|
|
75
|
+
private async writeVersionInfo(info: VersionInfo): Promise<void> {
|
|
76
|
+
try {
|
|
77
|
+
const dir = path.dirname(VERSION_FILE);
|
|
78
|
+
await fs.mkdir(dir, { recursive: true });
|
|
79
|
+
await fs.writeFile(VERSION_FILE, JSON.stringify(info, null, 2));
|
|
80
|
+
} catch {
|
|
81
|
+
// Silent fail
|
|
82
|
+
}
|
|
62
83
|
}
|
|
63
84
|
|
|
64
85
|
/**
|
|
65
|
-
*
|
|
86
|
+
* Get current Flow version from package.json (instant, local file)
|
|
66
87
|
*/
|
|
67
|
-
private async
|
|
88
|
+
private async getCurrentFlowVersion(): Promise<string> {
|
|
68
89
|
try {
|
|
69
|
-
// Get current version from package.json
|
|
70
90
|
const packageJsonPath = path.join(__dirname, '..', '..', 'package.json');
|
|
71
91
|
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Get latest version from npm
|
|
75
|
-
const { stdout } = await execAsync('npm view @sylphx/flow version');
|
|
76
|
-
const latestVersion = stdout.trim();
|
|
77
|
-
|
|
78
|
-
if (currentVersion !== latestVersion) {
|
|
79
|
-
return { current: currentVersion, latest: latestVersion };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return null;
|
|
92
|
+
return packageJson.version;
|
|
83
93
|
} catch {
|
|
84
|
-
return
|
|
94
|
+
return 'unknown';
|
|
85
95
|
}
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
/**
|
|
89
|
-
* Check
|
|
99
|
+
* Check for available upgrades (instant, reads from last background check)
|
|
100
|
+
* Background check runs every time for fresh data next run
|
|
90
101
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
async checkForUpgrades(targetId?: string): Promise<UpgradeStatus> {
|
|
103
|
+
const info = await this.readVersionInfo();
|
|
104
|
+
const currentVersion = await this.getCurrentFlowVersion();
|
|
105
|
+
|
|
106
|
+
// Trigger background check for next run (non-blocking, every time)
|
|
107
|
+
this.checkInBackground(targetId);
|
|
108
|
+
|
|
109
|
+
// No previous check = no upgrade info yet
|
|
110
|
+
if (!info) {
|
|
111
|
+
return {
|
|
112
|
+
flowNeedsUpgrade: false,
|
|
113
|
+
targetNeedsUpgrade: false,
|
|
114
|
+
flowVersion: null,
|
|
115
|
+
targetVersion: null,
|
|
116
|
+
};
|
|
97
117
|
}
|
|
98
118
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
119
|
+
// Check if Flow needs upgrade
|
|
120
|
+
const flowVersion =
|
|
121
|
+
info.flowLatest && info.flowLatest !== currentVersion
|
|
122
|
+
? { current: currentVersion, latest: info.flowLatest }
|
|
123
|
+
: null;
|
|
124
|
+
|
|
125
|
+
// Check if target needs upgrade
|
|
126
|
+
let targetVersion: { current: string; latest: string } | null = null;
|
|
127
|
+
if (targetId && info.targetLatest?.[targetId] && info.targetCurrent?.[targetId]) {
|
|
128
|
+
const current = info.targetCurrent[targetId];
|
|
129
|
+
const latest = info.targetLatest[targetId];
|
|
130
|
+
if (current !== latest) {
|
|
131
|
+
targetVersion = { current, latest };
|
|
105
132
|
}
|
|
106
|
-
|
|
133
|
+
}
|
|
107
134
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
135
|
+
return {
|
|
136
|
+
flowNeedsUpgrade: !!flowVersion,
|
|
137
|
+
targetNeedsUpgrade: !!targetVersion,
|
|
138
|
+
flowVersion,
|
|
139
|
+
targetVersion,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
111
142
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Check versions in background (non-blocking)
|
|
145
|
+
* Runs every time, updates info for next run
|
|
146
|
+
*/
|
|
147
|
+
private checkInBackground(targetId?: string): void {
|
|
148
|
+
// Fire and forget - don't await
|
|
149
|
+
this.performBackgroundCheck(targetId).catch(() => {
|
|
150
|
+
// Silent fail
|
|
151
|
+
});
|
|
152
|
+
}
|
|
115
153
|
|
|
116
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Perform the actual version check (called in background)
|
|
156
|
+
*/
|
|
157
|
+
private async performBackgroundCheck(targetId?: string): Promise<void> {
|
|
158
|
+
const oldInfo = await this.readVersionInfo();
|
|
159
|
+
|
|
160
|
+
const newInfo: VersionInfo = {
|
|
161
|
+
targetLatest: oldInfo?.targetLatest || {},
|
|
162
|
+
targetCurrent: oldInfo?.targetCurrent || {},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Check Flow version from npm (with timeout)
|
|
166
|
+
try {
|
|
167
|
+
const { stdout } = await execAsync('npm view @sylphx/flow version', { timeout: 5000 });
|
|
168
|
+
newInfo.flowLatest = stdout.trim();
|
|
117
169
|
} catch {
|
|
118
|
-
|
|
170
|
+
// Keep old value if check fails
|
|
171
|
+
newInfo.flowLatest = oldInfo?.flowLatest;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check target version from npm and local (with timeout)
|
|
175
|
+
if (targetId) {
|
|
176
|
+
const installation = this.targetInstaller.getInstallationInfo(targetId);
|
|
177
|
+
if (installation) {
|
|
178
|
+
// Check latest version from npm
|
|
179
|
+
try {
|
|
180
|
+
const { stdout } = await execAsync(`npm view ${installation.package} version`, {
|
|
181
|
+
timeout: 5000,
|
|
182
|
+
});
|
|
183
|
+
newInfo.targetLatest = newInfo.targetLatest || {};
|
|
184
|
+
newInfo.targetLatest[targetId] = stdout.trim();
|
|
185
|
+
} catch {
|
|
186
|
+
// Keep old value
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Check current installed version (local command)
|
|
190
|
+
try {
|
|
191
|
+
const { stdout } = await execAsync(installation.checkCommand, { timeout: 5000 });
|
|
192
|
+
const match = stdout.match(/v?(\d+\.\d+\.\d+)/);
|
|
193
|
+
if (match) {
|
|
194
|
+
newInfo.targetCurrent = newInfo.targetCurrent || {};
|
|
195
|
+
newInfo.targetCurrent[targetId] = match[1];
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
// Keep old value
|
|
199
|
+
}
|
|
200
|
+
}
|
|
119
201
|
}
|
|
202
|
+
|
|
203
|
+
await this.writeVersionInfo(newInfo);
|
|
120
204
|
}
|
|
121
205
|
|
|
122
206
|
/**
|