@sylphx/flow 2.15.2 → 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 CHANGED
@@ -1,5 +1,12 @@
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
+
3
10
  ## 2.15.2 (2025-12-17)
4
11
 
5
12
  ### ⚡️ Performance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/flow",
3
- "version": "2.15.2",
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": {
@@ -19,14 +19,13 @@ import { TargetInstaller } from './target-installer.js';
19
19
  const __filename = fileURLToPath(import.meta.url);
20
20
  const __dirname = path.dirname(__filename);
21
21
 
22
- // Cache file for version checks (24 hour TTL)
23
- const CACHE_FILE = path.join(os.homedir(), '.sylphx-flow', 'version-cache.json');
24
- const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
22
+ // Version info file (stores last background check result)
23
+ const VERSION_FILE = path.join(os.homedir(), '.sylphx-flow', 'versions.json');
25
24
 
26
- interface VersionCache {
25
+ interface VersionInfo {
27
26
  flowLatest?: string;
28
27
  targetLatest?: Record<string, string>;
29
- checkedAt: number;
28
+ targetCurrent?: Record<string, string>;
30
29
  }
31
30
 
32
31
  const execAsync = promisify(exec);
@@ -56,14 +55,14 @@ export class AutoUpgrade {
56
55
  }
57
56
 
58
57
  /**
59
- * Read version cache (instant, no network)
58
+ * Read version info from last background check
60
59
  */
61
- private async readCache(): Promise<VersionCache | null> {
60
+ private async readVersionInfo(): Promise<VersionInfo | null> {
62
61
  try {
63
- if (!existsSync(CACHE_FILE)) {
62
+ if (!existsSync(VERSION_FILE)) {
64
63
  return null;
65
64
  }
66
- const data = await fs.readFile(CACHE_FILE, 'utf-8');
65
+ const data = await fs.readFile(VERSION_FILE, 'utf-8');
67
66
  return JSON.parse(data);
68
67
  } catch {
69
68
  return null;
@@ -71,13 +70,13 @@ export class AutoUpgrade {
71
70
  }
72
71
 
73
72
  /**
74
- * Write version cache
73
+ * Write version info
75
74
  */
76
- private async writeCache(cache: VersionCache): Promise<void> {
75
+ private async writeVersionInfo(info: VersionInfo): Promise<void> {
77
76
  try {
78
- const dir = path.dirname(CACHE_FILE);
77
+ const dir = path.dirname(VERSION_FILE);
79
78
  await fs.mkdir(dir, { recursive: true });
80
- await fs.writeFile(CACHE_FILE, JSON.stringify(cache, null, 2));
79
+ await fs.writeFile(VERSION_FILE, JSON.stringify(info, null, 2));
81
80
  } catch {
82
81
  // Silent fail
83
82
  }
@@ -97,18 +96,18 @@ export class AutoUpgrade {
97
96
  }
98
97
 
99
98
  /**
100
- * Check for available upgrades using CACHE (instant, no network)
101
- * Returns cached results from previous background check
99
+ * Check for available upgrades (instant, reads from last background check)
100
+ * Background check runs every time for fresh data next run
102
101
  */
103
102
  async checkForUpgrades(targetId?: string): Promise<UpgradeStatus> {
104
- const cache = await this.readCache();
103
+ const info = await this.readVersionInfo();
105
104
  const currentVersion = await this.getCurrentFlowVersion();
106
105
 
107
- // Trigger background check for next run (non-blocking)
106
+ // Trigger background check for next run (non-blocking, every time)
108
107
  this.checkInBackground(targetId);
109
108
 
110
- // No cache or expired = no upgrade info yet
111
- if (!cache) {
109
+ // No previous check = no upgrade info yet
110
+ if (!info) {
112
111
  return {
113
112
  flowNeedsUpgrade: false,
114
113
  targetNeedsUpgrade: false,
@@ -117,26 +116,19 @@ export class AutoUpgrade {
117
116
  };
118
117
  }
119
118
 
120
- // Check if Flow needs upgrade based on cache
119
+ // Check if Flow needs upgrade
121
120
  const flowVersion =
122
- cache.flowLatest && cache.flowLatest !== currentVersion
123
- ? { current: currentVersion, latest: cache.flowLatest }
121
+ info.flowLatest && info.flowLatest !== currentVersion
122
+ ? { current: currentVersion, latest: info.flowLatest }
124
123
  : null;
125
124
 
126
- // Check if target needs upgrade based on cache
125
+ // Check if target needs upgrade
127
126
  let targetVersion: { current: string; latest: string } | null = null;
128
- if (targetId && cache.targetLatest?.[targetId]) {
129
- const installation = this.targetInstaller.getInstallationInfo(targetId);
130
- if (installation) {
131
- try {
132
- const { stdout } = await execAsync(installation.checkCommand);
133
- const match = stdout.match(/v?(\d+\.\d+\.\d+)/);
134
- if (match && match[1] !== cache.targetLatest[targetId]) {
135
- targetVersion = { current: match[1], latest: cache.targetLatest[targetId] };
136
- }
137
- } catch {
138
- // Silent
139
- }
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 };
140
132
  }
141
133
  }
142
134
 
@@ -150,7 +142,7 @@ export class AutoUpgrade {
150
142
 
151
143
  /**
152
144
  * Check versions in background (non-blocking)
153
- * Updates cache for next run
145
+ * Runs every time, updates info for next run
154
146
  */
155
147
  private checkInBackground(targetId?: string): void {
156
148
  // Fire and forget - don't await
@@ -163,44 +155,52 @@ export class AutoUpgrade {
163
155
  * Perform the actual version check (called in background)
164
156
  */
165
157
  private async performBackgroundCheck(targetId?: string): Promise<void> {
166
- const cache = await this.readCache();
158
+ const oldInfo = await this.readVersionInfo();
167
159
 
168
- // Skip if checked recently (within TTL)
169
- if (cache && Date.now() - cache.checkedAt < CACHE_TTL_MS) {
170
- return;
171
- }
172
-
173
- const newCache: VersionCache = {
174
- checkedAt: Date.now(),
175
- targetLatest: cache?.targetLatest || {},
160
+ const newInfo: VersionInfo = {
161
+ targetLatest: oldInfo?.targetLatest || {},
162
+ targetCurrent: oldInfo?.targetCurrent || {},
176
163
  };
177
164
 
178
165
  // Check Flow version from npm (with timeout)
179
166
  try {
180
167
  const { stdout } = await execAsync('npm view @sylphx/flow version', { timeout: 5000 });
181
- newCache.flowLatest = stdout.trim();
168
+ newInfo.flowLatest = stdout.trim();
182
169
  } catch {
183
- // Keep old cache value if check fails
184
- newCache.flowLatest = cache?.flowLatest;
170
+ // Keep old value if check fails
171
+ newInfo.flowLatest = oldInfo?.flowLatest;
185
172
  }
186
173
 
187
- // Check target version from npm (with timeout)
174
+ // Check target version from npm and local (with timeout)
188
175
  if (targetId) {
189
176
  const installation = this.targetInstaller.getInstallationInfo(targetId);
190
177
  if (installation) {
178
+ // Check latest version from npm
191
179
  try {
192
180
  const { stdout } = await execAsync(`npm view ${installation.package} version`, {
193
181
  timeout: 5000,
194
182
  });
195
- newCache.targetLatest = newCache.targetLatest || {};
196
- newCache.targetLatest[targetId] = stdout.trim();
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
197
  } catch {
198
- // Keep old cache value
198
+ // Keep old value
199
199
  }
200
200
  }
201
201
  }
202
202
 
203
- await this.writeCache(newCache);
203
+ await this.writeVersionInfo(newInfo);
204
204
  }
205
205
 
206
206
  /**