opencode-studio-server 1.16.2 → 1.16.4
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/index.js +199 -11
- package/package.json +1 -1
- package/server.log +1 -0
package/index.js
CHANGED
|
@@ -84,6 +84,7 @@ app.use(cors({
|
|
|
84
84
|
}));
|
|
85
85
|
app.use(bodyParser.json({ limit: '50mb' }));
|
|
86
86
|
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
|
87
|
+
app.use(bodyParser.text({ type: ['text/*', 'application/yaml'], limit: '50mb' }));
|
|
87
88
|
|
|
88
89
|
const HOME_DIR = os.homedir();
|
|
89
90
|
const STUDIO_CONFIG_PATH = path.join(HOME_DIR, '.config', 'opencode-studio', 'studio.json');
|
|
@@ -1615,15 +1616,37 @@ app.delete('/api/auth/profiles/:provider/all', (req, res) => {
|
|
|
1615
1616
|
|
|
1616
1617
|
const authCfg = loadAuthConfig() || {};
|
|
1617
1618
|
if (authCfg[provider]) {
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
delete authCfg[key];
|
|
1619
|
+
const paths = getPaths();
|
|
1620
|
+
const allPaths = paths.candidates;
|
|
1621
|
+
if (paths.current && !allPaths.includes(paths.current)) {
|
|
1622
|
+
allPaths.push(paths.current);
|
|
1623
1623
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1624
|
+
|
|
1625
|
+
allPaths.forEach(p => {
|
|
1626
|
+
const ap = path.join(path.dirname(p), 'auth.json');
|
|
1627
|
+
if (fs.existsSync(ap)) {
|
|
1628
|
+
try {
|
|
1629
|
+
const cfg = JSON.parse(fs.readFileSync(ap, 'utf8'));
|
|
1630
|
+
let modified = false;
|
|
1631
|
+
|
|
1632
|
+
if (provider === 'google') {
|
|
1633
|
+
if (cfg.google) { delete cfg.google; modified = true; }
|
|
1634
|
+
if (cfg['google.antigravity']) { delete cfg['google.antigravity']; modified = true; }
|
|
1635
|
+
if (cfg['google.gemini']) { delete cfg['google.gemini']; modified = true; }
|
|
1636
|
+
} else if (cfg[provider]) {
|
|
1637
|
+
delete cfg[provider];
|
|
1638
|
+
modified = true;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
if (modified) {
|
|
1642
|
+
console.log(`[Auth] Removing ${provider} (all) from ${ap}`);
|
|
1643
|
+
atomicWriteFileSync(ap, JSON.stringify(cfg, null, 2));
|
|
1644
|
+
}
|
|
1645
|
+
} catch (e) {
|
|
1646
|
+
console.error(`[Auth] Failed to update ${ap}:`, e.message);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1627
1650
|
}
|
|
1628
1651
|
|
|
1629
1652
|
const metadata = loadPoolMetadata();
|
|
@@ -1692,9 +1715,51 @@ app.delete('/api/auth/profiles/:provider/:name', (req, res) => {
|
|
|
1692
1715
|
}
|
|
1693
1716
|
|
|
1694
1717
|
if (changed) {
|
|
1695
|
-
const
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1718
|
+
const paths = getPaths();
|
|
1719
|
+
const allPaths = paths.candidates;
|
|
1720
|
+
if (paths.current && !allPaths.includes(paths.current)) {
|
|
1721
|
+
allPaths.push(paths.current);
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
allPaths.forEach(p => {
|
|
1725
|
+
const ap = path.join(path.dirname(p), 'auth.json');
|
|
1726
|
+
if (fs.existsSync(ap)) {
|
|
1727
|
+
try {
|
|
1728
|
+
const cfg = JSON.parse(fs.readFileSync(ap, 'utf8'));
|
|
1729
|
+
let modified = false;
|
|
1730
|
+
|
|
1731
|
+
if (provider === 'google') {
|
|
1732
|
+
if (cfg.google?.email === name || cfg['google.antigravity']?.email === name || cfg['google.gemini']?.email === name) {
|
|
1733
|
+
delete cfg.google;
|
|
1734
|
+
delete cfg['google.antigravity'];
|
|
1735
|
+
delete cfg['google.gemini'];
|
|
1736
|
+
modified = true;
|
|
1737
|
+
}
|
|
1738
|
+
} else if (cfg[provider]) {
|
|
1739
|
+
const creds = cfg[provider];
|
|
1740
|
+
let matches = false;
|
|
1741
|
+
if (creds.email === name) matches = true;
|
|
1742
|
+
else if (creds.accountId === name) matches = true;
|
|
1743
|
+
else if (provider === 'openai' && creds.access) {
|
|
1744
|
+
const decoded = decodeJWT(creds.access);
|
|
1745
|
+
if (decoded && decoded['https://api.openai.com/profile']?.email === name) matches = true;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
if (matches) {
|
|
1749
|
+
delete cfg[provider];
|
|
1750
|
+
modified = true;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
if (modified) {
|
|
1755
|
+
console.log(`[Auth] Removing credentials from ${ap}`);
|
|
1756
|
+
atomicWriteFileSync(ap, JSON.stringify(cfg, null, 2));
|
|
1757
|
+
}
|
|
1758
|
+
} catch (e) {
|
|
1759
|
+
console.error(`[Auth] Failed to update ${ap}:`, e.message);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1698
1763
|
}
|
|
1699
1764
|
|
|
1700
1765
|
const metadata = loadPoolMetadata();
|
|
@@ -2575,6 +2640,129 @@ app.post('/api/proxy/config', (req, res) => {
|
|
|
2575
2640
|
res.json({ success: true });
|
|
2576
2641
|
});
|
|
2577
2642
|
|
|
2643
|
+
const PROXY_CONFIG_PATH = path.join(HOME_DIR, '.config', 'opencode-studio', 'cliproxy.yaml');
|
|
2644
|
+
|
|
2645
|
+
app.get('/api/management/config.yaml', (req, res) => {
|
|
2646
|
+
console.log('GET config.yaml hit');
|
|
2647
|
+
if (!fs.existsSync(PROXY_CONFIG_PATH)) {
|
|
2648
|
+
console.log('Config file not found:', PROXY_CONFIG_PATH);
|
|
2649
|
+
return res.send("");
|
|
2650
|
+
}
|
|
2651
|
+
console.log('Sending config file:', PROXY_CONFIG_PATH);
|
|
2652
|
+
const content = fs.readFileSync(PROXY_CONFIG_PATH, 'utf8');
|
|
2653
|
+
res.send(content);
|
|
2654
|
+
});
|
|
2655
|
+
|
|
2656
|
+
app.put('/api/management/config.yaml', (req, res) => {
|
|
2657
|
+
fs.writeFileSync(PROXY_CONFIG_PATH, req.body);
|
|
2658
|
+
res.json({ ok: true, changed: [] });
|
|
2659
|
+
});
|
|
2660
|
+
|
|
2661
|
+
app.get('/api/management/api-keys', (req, res) => {
|
|
2662
|
+
if (!fs.existsSync(PROXY_CONFIG_PATH)) return res.json({ "api-keys": [] });
|
|
2663
|
+
const content = fs.readFileSync(PROXY_CONFIG_PATH, 'utf8');
|
|
2664
|
+
const yaml = require('js-yaml');
|
|
2665
|
+
try {
|
|
2666
|
+
const doc = yaml.load(content) || {};
|
|
2667
|
+
const keys = [];
|
|
2668
|
+
if (doc['management-key']) keys.push(doc['management-key']);
|
|
2669
|
+
if (Array.isArray(doc['api-keys'])) keys.push(...doc['api-keys']);
|
|
2670
|
+
res.json({ "api-keys": [...new Set(keys)].filter(k => k) });
|
|
2671
|
+
} catch (e) {
|
|
2672
|
+
res.json({ "api-keys": [] });
|
|
2673
|
+
}
|
|
2674
|
+
});
|
|
2675
|
+
|
|
2676
|
+
app.put('/api/management/api-keys', (req, res) => {
|
|
2677
|
+
const keys = req.body;
|
|
2678
|
+
if (!fs.existsSync(PROXY_CONFIG_PATH)) return res.status(404).json({ error: "Config not found" });
|
|
2679
|
+
const content = fs.readFileSync(PROXY_CONFIG_PATH, 'utf8');
|
|
2680
|
+
const yaml = require('js-yaml');
|
|
2681
|
+
try {
|
|
2682
|
+
const doc = yaml.load(content) || {};
|
|
2683
|
+
doc['api-keys'] = keys;
|
|
2684
|
+
if (keys.length > 0) doc['management-key'] = keys[0];
|
|
2685
|
+
|
|
2686
|
+
fs.writeFileSync(PROXY_CONFIG_PATH, yaml.dump(doc));
|
|
2687
|
+
res.json({ status: "ok" });
|
|
2688
|
+
} catch (e) {
|
|
2689
|
+
res.status(500).json({ error: e.message });
|
|
2690
|
+
}
|
|
2691
|
+
});
|
|
2692
|
+
|
|
2693
|
+
app.get('/api/management/logs', (req, res) => {
|
|
2694
|
+
if (!fs.existsSync(LOG_DIR)) return res.json({ lines: [], "line-count": 0, "latest-timestamp": Date.now() });
|
|
2695
|
+
const logFiles = fs.readdirSync(LOG_DIR).filter(f => f.endsWith('.log'));
|
|
2696
|
+
if (logFiles.length === 0) return res.json({ lines: [], "line-count": 0, "latest-timestamp": Date.now() });
|
|
2697
|
+
|
|
2698
|
+
const latest = logFiles.map(f => ({ name: f, time: fs.statSync(path.join(LOG_DIR, f)).mtime.getTime() }))
|
|
2699
|
+
.sort((a, b) => b.time - a.time)[0];
|
|
2700
|
+
|
|
2701
|
+
const content = fs.readFileSync(path.join(LOG_DIR, latest.name), 'utf8');
|
|
2702
|
+
const lines = content.split('\n').slice(-1000);
|
|
2703
|
+
res.json({ lines, "line-count": lines.length, "latest-timestamp": latest.time });
|
|
2704
|
+
});
|
|
2705
|
+
|
|
2706
|
+
app.get('/api/management/usage', (req, res) => {
|
|
2707
|
+
res.json({
|
|
2708
|
+
usage: {
|
|
2709
|
+
total_requests: 0,
|
|
2710
|
+
success_count: 0,
|
|
2711
|
+
failure_count: 0,
|
|
2712
|
+
total_tokens: 0,
|
|
2713
|
+
requests_by_day: {},
|
|
2714
|
+
requests_by_hour: {},
|
|
2715
|
+
tokens_by_day: {},
|
|
2716
|
+
tokens_by_hour: {},
|
|
2717
|
+
apis: {},
|
|
2718
|
+
failed_requests: 0
|
|
2719
|
+
}
|
|
2720
|
+
});
|
|
2721
|
+
});
|
|
2722
|
+
|
|
2723
|
+
app.get('/api/management/logging-to-file', (req, res) => {
|
|
2724
|
+
if (!fs.existsSync(PROXY_CONFIG_PATH)) return res.json({ "logging-to-file": false });
|
|
2725
|
+
try {
|
|
2726
|
+
const content = fs.readFileSync(PROXY_CONFIG_PATH, 'utf8');
|
|
2727
|
+
const yaml = require('js-yaml');
|
|
2728
|
+
const doc = yaml.load(content) || {};
|
|
2729
|
+
res.json({ "logging-to-file": !!doc['logging-to-file'] });
|
|
2730
|
+
} catch (e) {
|
|
2731
|
+
res.json({ "logging-to-file": false });
|
|
2732
|
+
}
|
|
2733
|
+
});
|
|
2734
|
+
|
|
2735
|
+
app.patch('/api/management/logging-to-file', (req, res) => {
|
|
2736
|
+
if (!fs.existsSync(PROXY_CONFIG_PATH)) return res.status(404).json({ error: "Config not found" });
|
|
2737
|
+
const enabled = req.body.value;
|
|
2738
|
+
const content = fs.readFileSync(PROXY_CONFIG_PATH, 'utf8');
|
|
2739
|
+
const yaml = require('js-yaml');
|
|
2740
|
+
try {
|
|
2741
|
+
const doc = yaml.load(content) || {};
|
|
2742
|
+
doc['logging-to-file'] = enabled;
|
|
2743
|
+
fs.writeFileSync(PROXY_CONFIG_PATH, yaml.dump(doc));
|
|
2744
|
+
res.json({ status: "ok" });
|
|
2745
|
+
} catch (e) {
|
|
2746
|
+
res.status(500).json({ error: e.message });
|
|
2747
|
+
}
|
|
2748
|
+
});
|
|
2749
|
+
|
|
2750
|
+
app.delete('/api/management/logs', (req, res) => {
|
|
2751
|
+
if (!fs.existsSync(LOG_DIR)) return res.json({ success: true, removed: 0 });
|
|
2752
|
+
const logFiles = fs.readdirSync(LOG_DIR).filter(f => f.endsWith('.log'));
|
|
2753
|
+
let removed = 0;
|
|
2754
|
+
try {
|
|
2755
|
+
logFiles.forEach(f => {
|
|
2756
|
+
const filePath = path.join(LOG_DIR, f);
|
|
2757
|
+
fs.unlinkSync(filePath);
|
|
2758
|
+
removed++;
|
|
2759
|
+
});
|
|
2760
|
+
res.json({ success: true, removed });
|
|
2761
|
+
} catch (e) {
|
|
2762
|
+
res.status(500).json({ error: e.message });
|
|
2763
|
+
}
|
|
2764
|
+
});
|
|
2765
|
+
|
|
2578
2766
|
app.get('/api/profiles', (req, res) => {
|
|
2579
2767
|
res.json(profileManager.listProfiles());
|
|
2580
2768
|
});
|
package/package.json
CHANGED
package/server.log
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Acceso denegado.
|