opencode-studio-server 1.9.5 → 1.9.8

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.
Files changed (2) hide show
  1. package/index.js +111 -5
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -17,10 +17,24 @@ const atomicWriteFileSync = (filePath, data, options = 'utf8') => {
17
17
  const tempPath = path.join(dir, `.${path.basename(filePath)}.${crypto.randomBytes(6).toString('hex')}.tmp`);
18
18
  try {
19
19
  fs.writeFileSync(tempPath, data, options);
20
- fs.renameSync(tempPath, filePath);
20
+ // Retry rename for Windows file locking issues
21
+ let retries = 5;
22
+ while (retries > 0) {
23
+ try {
24
+ fs.renameSync(tempPath, filePath);
25
+ break;
26
+ } catch (e) {
27
+ if (retries === 1) throw e;
28
+ retries--;
29
+ // Synchronous delay
30
+ const start = Date.now();
31
+ while (Date.now() - start < 50) {}
32
+ }
33
+ }
21
34
  } catch (err) {
22
- // Clean up temp file if rename fails
23
- try { fs.unlinkSync(tempPath); } catch {}
35
+ if (fs.existsSync(tempPath)) {
36
+ try { fs.unlinkSync(tempPath); } catch (e) {}
37
+ }
24
38
  throw err;
25
39
  }
26
40
  };
@@ -276,6 +290,10 @@ function loadStudioConfig() {
276
290
  activeGooglePlugin: 'gemini',
277
291
  availableGooglePlugins: [],
278
292
  presets: [],
293
+ cooldownRules: [
294
+ { name: "Opus 4.5 (Antigravity)", duration: 4 * 60 * 60 * 1000 }, // 4 hours
295
+ { name: "Gemini 3 Pro (Antigravity)", duration: 24 * 60 * 60 * 1000 } // 24 hours
296
+ ],
279
297
  pluginModels: {
280
298
  gemini: {
281
299
  "gemini-3-pro-preview": {
@@ -632,6 +650,36 @@ app.post('/api/restore', (req, res) => {
632
650
  }
633
651
  });
634
652
 
653
+ app.get('/api/cooldowns', (req, res) => {
654
+ const studio = loadStudioConfig();
655
+ res.json(studio.cooldownRules || []);
656
+ });
657
+
658
+ app.post('/api/cooldowns', (req, res) => {
659
+ const { name, duration } = req.body;
660
+ if (!name || typeof duration !== 'number') return res.status(400).json({ error: 'Invalid name or duration' });
661
+ const studio = loadStudioConfig();
662
+ if (!studio.cooldownRules) studio.cooldownRules = [];
663
+
664
+ // Update if exists, else push
665
+ const idx = studio.cooldownRules.findIndex(r => r.name === name);
666
+ if (idx >= 0) studio.cooldownRules[idx] = { name, duration };
667
+ else studio.cooldownRules.push({ name, duration });
668
+
669
+ saveStudioConfig(studio);
670
+ res.json(studio.cooldownRules);
671
+ });
672
+
673
+ app.delete('/api/cooldowns/:name', (req, res) => {
674
+ const { name } = req.params;
675
+ const studio = loadStudioConfig();
676
+ if (studio.cooldownRules) {
677
+ studio.cooldownRules = studio.cooldownRules.filter(r => r.name !== name);
678
+ saveStudioConfig(studio);
679
+ }
680
+ res.json(studio.cooldownRules || []);
681
+ });
682
+
635
683
  const DROPBOX_CLIENT_ID = 'your-dropbox-app-key';
636
684
  const GDRIVE_CLIENT_ID = 'your-google-client-id';
637
685
 
@@ -1342,6 +1390,7 @@ app.get('/api/auth/providers', (req, res) => {
1342
1390
  });
1343
1391
 
1344
1392
  app.get('/api/auth', (req, res) => {
1393
+ importCurrentGoogleAuthToPool();
1345
1394
  syncAntigravityPool();
1346
1395
  const authCfg = loadAuthConfig() || {};
1347
1396
  const studio = loadStudioConfig();
@@ -1814,6 +1863,55 @@ function listAntigravityAccounts() {
1814
1863
  }));
1815
1864
  }
1816
1865
 
1866
+ function importCurrentGoogleAuthToPool() {
1867
+ const studio = loadStudioConfig();
1868
+ // Only applies if Antigravity is active
1869
+ if (studio.activeGooglePlugin !== 'antigravity') return;
1870
+
1871
+ const authCfg = loadAuthConfig();
1872
+ if (!authCfg || !authCfg.google || !authCfg.google.email) return;
1873
+
1874
+ const namespace = 'google.antigravity';
1875
+ const profileDir = path.join(AUTH_PROFILES_DIR, namespace);
1876
+ if (!fs.existsSync(profileDir)) fs.mkdirSync(profileDir, { recursive: true });
1877
+
1878
+ const email = authCfg.google.email;
1879
+ const profilePath = path.join(profileDir, `${email}.json`);
1880
+
1881
+ // Check if we need to sync (new account or updated tokens)
1882
+ let shouldSync = true;
1883
+ if (fs.existsSync(profilePath)) {
1884
+ try {
1885
+ const current = JSON.parse(fs.readFileSync(profilePath, 'utf8'));
1886
+ if (current.access_token === authCfg.google.access_token) {
1887
+ shouldSync = false;
1888
+ }
1889
+ } catch {
1890
+ // Corrupt file, overwrite
1891
+ }
1892
+ }
1893
+
1894
+ if (shouldSync) {
1895
+ console.log(`[Auth] Syncing Google login for ${email} to Antigravity pool.`);
1896
+ atomicWriteFileSync(profilePath, JSON.stringify(authCfg.google, null, 2));
1897
+
1898
+ const metadata = loadPoolMetadata();
1899
+ if (!metadata[namespace]) metadata[namespace] = {};
1900
+
1901
+ // Only update metadata if it doesn't exist
1902
+ if (!metadata[namespace][email]) {
1903
+ metadata[namespace][email] = {
1904
+ email: email,
1905
+ createdAt: Date.now(),
1906
+ lastUsed: Date.now(),
1907
+ usageCount: 0,
1908
+ imported: true
1909
+ };
1910
+ savePoolMetadata(metadata);
1911
+ }
1912
+ }
1913
+ }
1914
+
1817
1915
  function syncAntigravityPool() {
1818
1916
  const accounts = listAntigravityAccounts();
1819
1917
  const namespace = 'google.antigravity';
@@ -2159,7 +2257,15 @@ app.post('/api/auth/pool/limit', (req, res) => {
2159
2257
  // PUT /api/auth/pool/:name/cooldown - Mark account as in cooldown
2160
2258
  app.put('/api/auth/pool/:name/cooldown', (req, res) => {
2161
2259
  const { name } = req.params;
2162
- const { duration = 3600000, provider = 'google' } = req.body; // default 1 hour
2260
+ let { duration, provider = 'google', rule } = req.body;
2261
+
2262
+ if (rule) {
2263
+ const studio = loadStudioConfig();
2264
+ const r = (studio.cooldownRules || []).find(cr => cr.name === rule);
2265
+ if (r) duration = r.duration;
2266
+ }
2267
+
2268
+ if (!duration) duration = 3600000; // default 1 hour
2163
2269
 
2164
2270
  const activePlugin = getActiveGooglePlugin();
2165
2271
  const namespace = provider === 'google'
@@ -2886,7 +2992,7 @@ app.post('/api/presets/:id/apply', (req, res) => {
2886
2992
 
2887
2993
  // Start watcher on server start
2888
2994
  function startServer() {
2889
- setupLogWatcher();
2995
+ // setupLogWatcher(); // Disabled as per user request (manual switching only)
2890
2996
  importExistingAuth();
2891
2997
  app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));
2892
2998
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-studio-server",
3
- "version": "1.9.5",
3
+ "version": "1.9.8",
4
4
  "description": "Backend server for OpenCode Studio - manages opencode configurations",
5
5
  "main": "index.js",
6
6
  "bin": {