opencode-studio-server 1.3.5 → 1.3.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.
Files changed (2) hide show
  1. package/index.js +149 -19
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -681,8 +681,25 @@ app.post('/api/auth/profiles/:provider', (req, res) => {
681
681
  const dir = path.join(AUTH_PROFILES_DIR, namespace);
682
682
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
683
683
 
684
- const profilePath = path.join(dir, `${name || Date.now()}.json`);
684
+ if (!auth[provider]) {
685
+ return res.status(400).json({ error: 'No current auth for provider' });
686
+ }
687
+
688
+ const profileName = name || auth[provider].email || `profile-${Date.now()}`;
689
+ const profilePath = path.join(dir, `${profileName}.json`);
685
690
  atomicWriteFileSync(profilePath, JSON.stringify(auth[provider], null, 2));
691
+
692
+ const metadata = loadPoolMetadata();
693
+ if (!metadata[namespace]) metadata[namespace] = {};
694
+ metadata[namespace][profileName] = {
695
+ ...(metadata[namespace][profileName] || {}),
696
+ email: auth[provider].email || metadata[namespace][profileName]?.email || null,
697
+ createdAt: metadata[namespace][profileName]?.createdAt || Date.now(),
698
+ lastUsed: Date.now(),
699
+ usageCount: metadata[namespace][profileName]?.usageCount || 0
700
+ };
701
+ savePoolMetadata(metadata);
702
+
686
703
  res.json({ success: true, name: path.basename(profilePath, '.json') });
687
704
  });
688
705
 
@@ -714,6 +731,22 @@ app.post('/api/auth/profiles/:provider/:name/activate', (req, res) => {
714
731
  const cp = getConfigPath();
715
732
  const ap = path.join(path.dirname(cp), 'auth.json');
716
733
  atomicWriteFileSync(ap, JSON.stringify(authCfg, null, 2));
734
+
735
+ const metadata = loadPoolMetadata();
736
+ if (!metadata[namespace]) metadata[namespace] = {};
737
+ metadata[namespace][name] = {
738
+ ...(metadata[namespace][name] || {}),
739
+ email: profileData.email || metadata[namespace][name]?.email || null,
740
+ createdAt: metadata[namespace][name]?.createdAt || Date.now(),
741
+ lastUsed: Date.now(),
742
+ usageCount: (metadata[namespace][name]?.usageCount || 0) + 1
743
+ };
744
+ if (!metadata._quota) metadata._quota = {};
745
+ if (!metadata._quota[namespace]) metadata._quota[namespace] = {};
746
+ const today = new Date().toISOString().split('T')[0];
747
+ metadata._quota[namespace][today] = (metadata._quota[namespace][today] || 0) + 1;
748
+ savePoolMetadata(metadata);
749
+
717
750
  res.json({ success: true });
718
751
  });
719
752
 
@@ -726,6 +759,30 @@ app.delete('/api/auth/profiles/:provider/:name', (req, res) => {
726
759
 
727
760
  const profilePath = path.join(AUTH_PROFILES_DIR, namespace, `${name}.json`);
728
761
  if (fs.existsSync(profilePath)) fs.unlinkSync(profilePath);
762
+
763
+ const studio = loadStudioConfig();
764
+ if (studio.activeProfiles && studio.activeProfiles[provider] === name) {
765
+ delete studio.activeProfiles[provider];
766
+ saveStudioConfig(studio);
767
+
768
+ const authCfg = loadAuthConfig() || {};
769
+ delete authCfg[provider];
770
+ if (provider === 'google') {
771
+ const key = activePlugin === 'antigravity' ? 'google.antigravity' : 'google.gemini';
772
+ delete authCfg.google;
773
+ delete authCfg[key];
774
+ }
775
+ const cp = getConfigPath();
776
+ const ap = path.join(path.dirname(cp), 'auth.json');
777
+ atomicWriteFileSync(ap, JSON.stringify(authCfg, null, 2));
778
+ }
779
+
780
+ const metadata = loadPoolMetadata();
781
+ if (metadata[namespace]?.[name]) {
782
+ delete metadata[namespace][name];
783
+ savePoolMetadata(metadata);
784
+ }
785
+
729
786
  res.json({ success: true });
730
787
  });
731
788
 
@@ -770,10 +827,13 @@ app.post('/api/auth/login', (req, res) => {
770
827
  let cmd = 'opencode auth login';
771
828
  if (provider) cmd += ` ${provider}`;
772
829
 
830
+ const cp = getConfigPath();
831
+ const configDir = cp ? path.dirname(cp) : process.cwd();
832
+ const safeDir = configDir.replace(/"/g, '\\"');
773
833
  const platform = process.platform;
774
834
 
775
835
  if (platform === 'win32') {
776
- const terminalCmd = `start "" cmd /c "call ${cmd} || pause"`;
836
+ const terminalCmd = `start "" /d "${safeDir}" cmd /c "call ${cmd} || pause"`;
777
837
  console.log('Executing terminal command:', terminalCmd);
778
838
  exec(terminalCmd, (err) => {
779
839
  if (err) {
@@ -783,7 +843,7 @@ app.post('/api/auth/login', (req, res) => {
783
843
  res.json({ success: true, message: 'Terminal opened', note: 'Complete login in the terminal window' });
784
844
  });
785
845
  } else if (platform === 'darwin') {
786
- const terminalCmd = `osascript -e 'tell application "Terminal" to do script "${cmd}"'`;
846
+ const terminalCmd = `osascript -e 'tell application "Terminal" to do script "cd ${safeDir} && ${cmd}"'`;
787
847
  console.log('Executing terminal command:', terminalCmd);
788
848
  exec(terminalCmd, (err) => {
789
849
  if (err) {
@@ -794,11 +854,11 @@ app.post('/api/auth/login', (req, res) => {
794
854
  });
795
855
  } else {
796
856
  const linuxTerminals = [
797
- { name: 'x-terminal-emulator', cmd: `x-terminal-emulator -e "${cmd}"` },
798
- { name: 'gnome-terminal', cmd: `gnome-terminal -- bash -c "${cmd}; read -p 'Press Enter to close...'"` },
799
- { name: 'konsole', cmd: `konsole -e bash -c "${cmd}; read -p 'Press Enter to close...'"` },
800
- { name: 'xfce4-terminal', cmd: `xfce4-terminal -e "bash -c \\"${cmd}; read -p 'Press Enter to close...'\\"" ` },
801
- { name: 'xterm', cmd: `xterm -e "bash -c '${cmd}; read -p Press_Enter_to_close...'"` }
857
+ { name: 'x-terminal-emulator', cmd: `x-terminal-emulator -e "bash -c 'cd ${safeDir} && ${cmd}'"` },
858
+ { name: 'gnome-terminal', cmd: `gnome-terminal -- bash -c "cd ${safeDir} && ${cmd}; read -p 'Press Enter to close...'"` },
859
+ { name: 'konsole', cmd: `konsole -e bash -c "cd ${safeDir} && ${cmd}; read -p 'Press Enter to close...'"` },
860
+ { name: 'xfce4-terminal', cmd: `xfce4-terminal -e "bash -c \"cd ${safeDir} && ${cmd}; read -p 'Press Enter to close...'\"" ` },
861
+ { name: 'xterm', cmd: `xterm -e "bash -c 'cd ${safeDir} && ${cmd}; read -p Press_Enter_to_close...'"` }
802
862
  ];
803
863
 
804
864
  const tryTerminal = (index) => {
@@ -828,18 +888,53 @@ app.post('/api/auth/login', (req, res) => {
828
888
  }
829
889
  });
830
890
 
891
+
831
892
  app.delete('/api/auth/:provider', (req, res) => {
832
893
  const { provider } = req.params;
833
894
  const authCfg = loadAuthConfig() || {};
834
- delete authCfg[provider];
895
+ const studio = loadStudioConfig();
896
+ const activePlugin = studio.activeGooglePlugin;
897
+
898
+ if (provider === 'google') {
899
+ delete authCfg.google;
900
+ delete authCfg['google.gemini'];
901
+ delete authCfg['google.antigravity'];
902
+
903
+ if (studio.activeProfiles) delete studio.activeProfiles.google;
904
+ saveStudioConfig(studio);
905
+
906
+ const geminiDir = path.join(AUTH_PROFILES_DIR, 'google.gemini');
907
+ const antiDir = path.join(AUTH_PROFILES_DIR, 'google.antigravity');
908
+ [geminiDir, antiDir].forEach(dir => {
909
+ if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true });
910
+ });
911
+
912
+ const metadata = loadPoolMetadata();
913
+ delete metadata['google.gemini'];
914
+ delete metadata['google.antigravity'];
915
+ savePoolMetadata(metadata);
916
+ } else {
917
+ delete authCfg[provider];
918
+ if (studio.activeProfiles) delete studio.activeProfiles[provider];
919
+ saveStudioConfig(studio);
920
+
921
+ const providerDir = path.join(AUTH_PROFILES_DIR, provider);
922
+ if (fs.existsSync(providerDir)) fs.rmSync(providerDir, { recursive: true, force: true });
923
+
924
+ const metadata = loadPoolMetadata();
925
+ delete metadata[provider];
926
+ savePoolMetadata(metadata);
927
+ }
928
+
929
+ if (provider === 'google' && activePlugin) {
930
+ const key = activePlugin === 'antigravity' ? 'google.antigravity' : 'google.gemini';
931
+ delete authCfg[key];
932
+ }
933
+
835
934
  const cp = getConfigPath();
836
935
  const ap = path.join(path.dirname(cp), 'auth.json');
837
936
  atomicWriteFileSync(ap, JSON.stringify(authCfg, null, 2));
838
-
839
- const studio = loadStudioConfig();
840
- if (studio.activeProfiles) delete studio.activeProfiles[provider];
841
- saveStudioConfig(studio);
842
-
937
+
843
938
  res.json({ success: true });
844
939
  });
845
940
 
@@ -892,11 +987,18 @@ function buildAccountPool(provider) {
892
987
  files.forEach(file => {
893
988
  const name = file.replace('.json', '');
894
989
  const meta = providerMeta[name] || {};
895
- const status = name === activeProfile ? 'active' : getAccountStatus(meta, now);
990
+ let profileEmail = null;
991
+ try {
992
+ const raw = fs.readFileSync(path.join(profileDir, file), 'utf8');
993
+ const parsed = JSON.parse(raw);
994
+ profileEmail = parsed?.email || null;
995
+ } catch {}
996
+ let status = getAccountStatus(meta, now);
997
+ if (name === activeProfile && status === 'ready') status = 'active';
896
998
 
897
999
  profiles.push({
898
1000
  name,
899
- email: meta.email || null,
1001
+ email: meta.email || profileEmail || null,
900
1002
  status,
901
1003
  lastUsed: meta.lastUsed || 0,
902
1004
  usageCount: meta.usageCount || 0,
@@ -1022,8 +1124,15 @@ app.post('/api/auth/pool/rotate', (req, res) => {
1022
1124
  if (!metadata[namespace]) metadata[namespace] = {};
1023
1125
  metadata[namespace][next.name] = {
1024
1126
  ...metadata[namespace][next.name],
1025
- lastUsed: now
1127
+ lastUsed: now,
1128
+ usageCount: (metadata[namespace][next.name]?.usageCount || 0) + 1
1026
1129
  };
1130
+
1131
+ if (!metadata._quota) metadata._quota = {};
1132
+ if (!metadata._quota[namespace]) metadata._quota[namespace] = {};
1133
+ const today = new Date().toISOString().split('T')[0];
1134
+ metadata._quota[namespace][today] = (metadata._quota[namespace][today] || 0) + 1;
1135
+
1027
1136
  savePoolMetadata(metadata);
1028
1137
 
1029
1138
  res.json({
@@ -1471,8 +1580,8 @@ app.post('/api/auth/google/start', async (req, res) => {
1471
1580
  const ap = path.join(path.dirname(cp), 'auth.json');
1472
1581
  const authCfg = fs.existsSync(ap) ? JSON.parse(fs.readFileSync(ap, 'utf8')) : {};
1473
1582
 
1474
- const studio = loadStudioConfig();
1475
- const activePlugin = studio.activeGooglePlugin || 'gemini';
1583
+ const studioConfig = loadStudioConfig();
1584
+ const activePlugin = studioConfig.activeGooglePlugin || 'gemini';
1476
1585
  const namespace = activePlugin === 'antigravity' ? 'google.antigravity' : 'google.gemini';
1477
1586
 
1478
1587
  const credentials = {
@@ -1486,6 +1595,27 @@ app.post('/api/auth/google/start', async (req, res) => {
1486
1595
  authCfg[namespace] = credentials;
1487
1596
 
1488
1597
  atomicWriteFileSync(ap, JSON.stringify(authCfg, null, 2));
1598
+
1599
+ const profileDir = path.join(AUTH_PROFILES_DIR, namespace);
1600
+ if (!fs.existsSync(profileDir)) fs.mkdirSync(profileDir, { recursive: true });
1601
+ const profileName = email || `google-${Date.now()}`;
1602
+ const profilePath = path.join(profileDir, `${profileName}.json`);
1603
+ atomicWriteFileSync(profilePath, JSON.stringify(credentials, null, 2));
1604
+
1605
+ const metadata = loadPoolMetadata();
1606
+ if (!metadata[namespace]) metadata[namespace] = {};
1607
+ metadata[namespace][profileName] = {
1608
+ ...(metadata[namespace][profileName] || {}),
1609
+ email: email || null,
1610
+ createdAt: metadata[namespace][profileName]?.createdAt || Date.now(),
1611
+ lastUsed: Date.now(),
1612
+ usageCount: metadata[namespace][profileName]?.usageCount || 0
1613
+ };
1614
+ savePoolMetadata(metadata);
1615
+
1616
+ if (!studioConfig.activeProfiles) studioConfig.activeProfiles = {};
1617
+ studioConfig.activeProfiles.google = profileName;
1618
+ saveStudioConfig(studioConfig);
1489
1619
 
1490
1620
  pendingOAuthState = { ...pendingOAuthState, status: 'success', email };
1491
1621
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-studio-server",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "Backend server for OpenCode Studio - manages opencode configurations",
5
5
  "main": "index.js",
6
6
  "bin": {