opencode-studio-server 1.3.9 → 1.4.1
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 +72 -11
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -7,6 +7,9 @@ const os = require('os');
|
|
|
7
7
|
const crypto = require('crypto');
|
|
8
8
|
const { spawn, exec } = require('child_process');
|
|
9
9
|
|
|
10
|
+
const pkg = require('./package.json');
|
|
11
|
+
const SERVER_VERSION = pkg.version;
|
|
12
|
+
|
|
10
13
|
// Atomic file write: write to temp file then rename to prevent corruption
|
|
11
14
|
const atomicWriteFileSync = (filePath, data, options = 'utf8') => {
|
|
12
15
|
const dir = path.dirname(filePath);
|
|
@@ -219,9 +222,7 @@ function processLogLine(line) {
|
|
|
219
222
|
}
|
|
220
223
|
}
|
|
221
224
|
|
|
222
|
-
|
|
223
|
-
setupLogWatcher();
|
|
224
|
-
importExistingAuth();
|
|
225
|
+
|
|
225
226
|
|
|
226
227
|
let pendingActionMemory = null;
|
|
227
228
|
|
|
@@ -436,7 +437,7 @@ const saveConfig = (config) => {
|
|
|
436
437
|
atomicWriteFileSync(configPath, JSON.stringify(config, null, 2));
|
|
437
438
|
};
|
|
438
439
|
|
|
439
|
-
app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
|
|
440
|
+
app.get('/api/health', (req, res) => res.json({ status: 'ok', version: SERVER_VERSION }));
|
|
440
441
|
|
|
441
442
|
app.post('/api/shutdown', (req, res) => {
|
|
442
443
|
res.json({ success: true });
|
|
@@ -445,6 +446,56 @@ app.post('/api/shutdown', (req, res) => {
|
|
|
445
446
|
|
|
446
447
|
app.get('/api/paths', (req, res) => res.json(getPaths()));
|
|
447
448
|
|
|
449
|
+
app.get('/api/debug/auth', (req, res) => {
|
|
450
|
+
const paths = getPaths();
|
|
451
|
+
const studio = loadStudioConfig();
|
|
452
|
+
const activePlugin = studio.activeGooglePlugin;
|
|
453
|
+
|
|
454
|
+
// Check auth.json in all candidate locations
|
|
455
|
+
const authLocations = [];
|
|
456
|
+
paths.candidates.forEach(p => {
|
|
457
|
+
const ap = path.join(path.dirname(p), 'auth.json');
|
|
458
|
+
authLocations.push({
|
|
459
|
+
path: ap,
|
|
460
|
+
exists: fs.existsSync(ap),
|
|
461
|
+
keys: fs.existsSync(ap) ? Object.keys(JSON.parse(fs.readFileSync(ap, 'utf8'))) : []
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// Check current auth.json
|
|
466
|
+
if (paths.current) {
|
|
467
|
+
const ap = path.join(path.dirname(paths.current), 'auth.json');
|
|
468
|
+
if (!authLocations.some(l => l.path === ap)) {
|
|
469
|
+
authLocations.push({
|
|
470
|
+
path: ap,
|
|
471
|
+
exists: fs.existsSync(ap),
|
|
472
|
+
keys: fs.existsSync(ap) ? Object.keys(JSON.parse(fs.readFileSync(ap, 'utf8'))) : []
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Check profile directories
|
|
478
|
+
const namespaces = ['google', 'google.gemini', 'google.antigravity', 'openai', 'anthropic'];
|
|
479
|
+
const profileDirs = {};
|
|
480
|
+
namespaces.forEach(ns => {
|
|
481
|
+
const dir = path.join(AUTH_PROFILES_DIR, ns);
|
|
482
|
+
profileDirs[ns] = {
|
|
483
|
+
path: dir,
|
|
484
|
+
exists: fs.existsSync(dir),
|
|
485
|
+
profiles: fs.existsSync(dir) ? fs.readdirSync(dir).filter(f => f.endsWith('.json')) : []
|
|
486
|
+
};
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
res.json({
|
|
490
|
+
configPath: paths.current,
|
|
491
|
+
activeGooglePlugin: activePlugin,
|
|
492
|
+
activeProfiles: studio.activeProfiles || {},
|
|
493
|
+
authLocations,
|
|
494
|
+
profileDirs,
|
|
495
|
+
authProfilesDir: AUTH_PROFILES_DIR
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
|
|
448
499
|
app.post('/api/paths', (req, res) => {
|
|
449
500
|
const { configPath } = req.body;
|
|
450
501
|
const studioConfig = loadStudioConfig();
|
|
@@ -476,9 +527,11 @@ const getSkillDir = () => {
|
|
|
476
527
|
app.get('/api/skills', (req, res) => {
|
|
477
528
|
const sd = getSkillDir();
|
|
478
529
|
if (!sd || !fs.existsSync(sd)) return res.json([]);
|
|
530
|
+
const studio = loadStudioConfig();
|
|
531
|
+
const disabledSkills = studio.disabledSkills || [];
|
|
479
532
|
const skills = fs.readdirSync(sd, { withFileTypes: true })
|
|
480
533
|
.filter(e => e.isDirectory() && fs.existsSync(path.join(sd, e.name, 'SKILL.md')))
|
|
481
|
-
.map(e => ({ name: e.name, path: path.join(sd, e.name, 'SKILL.md'), enabled: !e.name
|
|
534
|
+
.map(e => ({ name: e.name, path: path.join(sd, e.name, 'SKILL.md'), enabled: !disabledSkills.includes(e.name) }));
|
|
482
535
|
res.json(skills);
|
|
483
536
|
});
|
|
484
537
|
|
|
@@ -538,9 +591,13 @@ app.get('/api/plugins', (req, res) => {
|
|
|
538
591
|
const pd = getPluginDir();
|
|
539
592
|
const cp = getConfigPath();
|
|
540
593
|
const cr = cp ? path.dirname(cp) : null;
|
|
594
|
+
const studio = loadStudioConfig();
|
|
595
|
+
const disabledPlugins = studio.disabledPlugins || [];
|
|
541
596
|
const plugins = [];
|
|
542
|
-
const add = (name, p,
|
|
543
|
-
if (!plugins.some(pl => pl.name === name))
|
|
597
|
+
const add = (name, p, type = 'file') => {
|
|
598
|
+
if (!plugins.some(pl => pl.name === name)) {
|
|
599
|
+
plugins.push({ name, path: p, type, enabled: !disabledPlugins.includes(name) });
|
|
600
|
+
}
|
|
544
601
|
};
|
|
545
602
|
|
|
546
603
|
if (pd && fs.existsSync(pd)) {
|
|
@@ -549,9 +606,9 @@ app.get('/api/plugins', (req, res) => {
|
|
|
549
606
|
const st = fs.lstatSync(fp);
|
|
550
607
|
if (st.isDirectory()) {
|
|
551
608
|
const j = path.join(fp, 'index.js'), t = path.join(fp, 'index.ts');
|
|
552
|
-
if (fs.existsSync(j) || fs.existsSync(t)) add(e.name, fs.existsSync(j) ? j : t);
|
|
609
|
+
if (fs.existsSync(j) || fs.existsSync(t)) add(e.name, fs.existsSync(j) ? j : t, 'file');
|
|
553
610
|
} else if ((st.isFile() || st.isSymbolicLink()) && /\.(js|ts)$/.test(e.name)) {
|
|
554
|
-
add(e.name.replace(/\.(js|ts)$/, ''), fp);
|
|
611
|
+
add(e.name.replace(/\.(js|ts)$/, ''), fp, 'file');
|
|
555
612
|
}
|
|
556
613
|
});
|
|
557
614
|
}
|
|
@@ -559,14 +616,14 @@ app.get('/api/plugins', (req, res) => {
|
|
|
559
616
|
if (cr && fs.existsSync(cr)) {
|
|
560
617
|
['oh-my-opencode', 'superpowers', 'opencode-gemini-auth'].forEach(n => {
|
|
561
618
|
const fp = path.join(cr, n);
|
|
562
|
-
if (fs.existsSync(fp) && fs.statSync(fp).isDirectory()) add(n, fp);
|
|
619
|
+
if (fs.existsSync(fp) && fs.statSync(fp).isDirectory()) add(n, fp, 'file');
|
|
563
620
|
});
|
|
564
621
|
}
|
|
565
622
|
|
|
566
623
|
const cfg = loadConfig();
|
|
567
624
|
if (cfg && Array.isArray(cfg.plugin)) {
|
|
568
625
|
cfg.plugin.forEach(n => {
|
|
569
|
-
if (!n.includes('/') && !n.includes('\\') && !/\.(js|ts)$/.test(n)) add(n, 'npm');
|
|
626
|
+
if (!n.includes('/') && !n.includes('\\') && !/\.(js|ts)$/.test(n)) add(n, 'npm', 'npm');
|
|
570
627
|
});
|
|
571
628
|
}
|
|
572
629
|
res.json(plugins);
|
|
@@ -2194,4 +2251,8 @@ app.post('/api/presets/:id/apply', (req, res) => {
|
|
|
2194
2251
|
res.json({ success: true });
|
|
2195
2252
|
});
|
|
2196
2253
|
|
|
2254
|
+
// Start watcher on server start
|
|
2255
|
+
setupLogWatcher();
|
|
2256
|
+
importExistingAuth();
|
|
2257
|
+
|
|
2197
2258
|
app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));
|