opencode-hub 1.0.6 → 1.0.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.
- package/oc-tui.js +93 -14
- package/package.json +1 -1
package/oc-tui.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Database } from "bun:sqlite";
|
|
3
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from "fs";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, unlinkSync } from "fs";
|
|
4
4
|
import { execSync } from "child_process";
|
|
5
5
|
import { join, dirname } from "path";
|
|
6
6
|
import { homedir } from "os";
|
|
@@ -20,6 +20,30 @@ var PLUGINS_DIR = join(CONFIG_DIR, "plugin");
|
|
|
20
20
|
// Folder name helper: <creator>/<repo-name> to avoid collisions
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
22
|
|
|
23
|
+
function loadNpmPlugins() {
|
|
24
|
+
var ocPath = join(CONFIG_DIR, "opencode.json");
|
|
25
|
+
if (!existsSync(ocPath)) return [];
|
|
26
|
+
try {
|
|
27
|
+
var raw = readFileSync(ocPath, "utf-8");
|
|
28
|
+
var stripped = raw.replace(/\/\/[^\n]*/g, "");
|
|
29
|
+
var oc = JSON.parse(stripped);
|
|
30
|
+
var plugins = oc.plugin || [];
|
|
31
|
+
return plugins
|
|
32
|
+
.filter(function(p) { return typeof p === "string"; })
|
|
33
|
+
.map(function(p) {
|
|
34
|
+
var name = p.replace(/@[^@\/]+$/, "") || p;
|
|
35
|
+
var version = "";
|
|
36
|
+
try {
|
|
37
|
+
var pkgPath = join(CONFIG_DIR, "node_modules", name, "package.json");
|
|
38
|
+
if (existsSync(pkgPath)) {
|
|
39
|
+
version = JSON.parse(readFileSync(pkgPath, "utf-8")).version || "";
|
|
40
|
+
}
|
|
41
|
+
} catch {}
|
|
42
|
+
return { name: name, version: version, raw: p };
|
|
43
|
+
});
|
|
44
|
+
} catch { return []; }
|
|
45
|
+
}
|
|
46
|
+
|
|
23
47
|
function getFolderName(plugin) {
|
|
24
48
|
var match = (plugin.url || "").match(/github\.com\/([^\/]+)\/([^\/\.]+)/);
|
|
25
49
|
if (match) return match[1] + "/" + plugin.name;
|
|
@@ -210,10 +234,13 @@ function buildPluginList() {
|
|
|
210
234
|
var remoteHead = "";
|
|
211
235
|
var subject = "";
|
|
212
236
|
var updateAvail = false;
|
|
237
|
+
var latestTag = "";
|
|
238
|
+
var enabled = p.enabled !== false;
|
|
213
239
|
|
|
214
240
|
if (installed) {
|
|
215
241
|
localHead = gitText(["git", "rev-parse", "HEAD"], dir);
|
|
216
242
|
subject = gitText(["git", "log", "-1", "--format=%s"], dir);
|
|
243
|
+
latestTag = gitText(["git", "describe", "--tags", "--abbrev=0"], dir);
|
|
217
244
|
}
|
|
218
245
|
|
|
219
246
|
list.push({
|
|
@@ -221,10 +248,12 @@ function buildPluginList() {
|
|
|
221
248
|
folderName: folderName,
|
|
222
249
|
url: p.url,
|
|
223
250
|
autoUpdate: p.autoUpdate !== false,
|
|
251
|
+
enabled: enabled,
|
|
224
252
|
installed: installed,
|
|
225
253
|
deployed: deployed,
|
|
226
254
|
localHead: localHead,
|
|
227
255
|
remoteHead: remoteHead,
|
|
256
|
+
latestTag: latestTag,
|
|
228
257
|
subject: subject,
|
|
229
258
|
updateAvail: updateAvail,
|
|
230
259
|
hasBuild: !!(p.build || p.bundle),
|
|
@@ -279,6 +308,10 @@ function runPluginUpdate(pluginItem) {
|
|
|
279
308
|
try { execSync(repo.install.join(" "), { cwd: dir, timeout: 120000, stdio: "ignore" }); }
|
|
280
309
|
catch (e) { return "Install failed"; }
|
|
281
310
|
}
|
|
311
|
+
if (repo.postInstall) {
|
|
312
|
+
try { execSync(repo.postInstall.join(" "), { cwd: dir, timeout: 120000, stdio: "ignore" }); }
|
|
313
|
+
catch (e) { return "Post-install failed"; }
|
|
314
|
+
}
|
|
282
315
|
if (repo.build) {
|
|
283
316
|
try { execSync(repo.build.join(" "), { cwd: dir, timeout: 120000, stdio: "ignore" }); }
|
|
284
317
|
catch (e) { return "Build failed"; }
|
|
@@ -334,6 +367,7 @@ function showCur() { process.stderr.write(E + "?25h"); }
|
|
|
334
367
|
|
|
335
368
|
var items = buildList();
|
|
336
369
|
var pluginItems = buildPluginList();
|
|
370
|
+
var npmPluginItems = loadNpmPlugins();
|
|
337
371
|
var cursor = 0;
|
|
338
372
|
var pcursor = 0; // plugin page cursor
|
|
339
373
|
var mode = "list"; // "list" | "actions" | "input" | "pactions"
|
|
@@ -380,16 +414,22 @@ function getActions(item) {
|
|
|
380
414
|
|
|
381
415
|
function getPluginActions(pitem) {
|
|
382
416
|
var a = [];
|
|
417
|
+
if (!pitem.enabled) {
|
|
418
|
+
a.push({ key: "enable-plugin", label: "Enable plugin" });
|
|
419
|
+
a.push({ key: "cancel", label: "Cancel" });
|
|
420
|
+
return a;
|
|
421
|
+
}
|
|
383
422
|
if (pitem.updateAvail || !pitem.deployed) {
|
|
384
423
|
a.push({ key: "update", label: "Update now" });
|
|
385
424
|
}
|
|
386
425
|
if (pitem.autoUpdate) {
|
|
387
|
-
a.push({ key: "disable-auto", label: "
|
|
426
|
+
a.push({ key: "disable-auto", label: "Set to manual update" });
|
|
388
427
|
} else {
|
|
389
428
|
a.push({ key: "enable-auto", label: "Enable auto-update" });
|
|
390
429
|
}
|
|
391
430
|
a.push({ key: "force-update", label: "Force rebuild & deploy" });
|
|
392
431
|
a.push({ key: "commits", label: "Select specific commit (Downgrade)" });
|
|
432
|
+
a.push({ key: "disable-plugin", label: "Disable plugin" });
|
|
393
433
|
a.push({ key: "cancel", label: "Cancel" });
|
|
394
434
|
return a;
|
|
395
435
|
}
|
|
@@ -613,23 +653,29 @@ function buildPluginItem(pushBody, i, pitem, nameW, cols, isSelected) {
|
|
|
613
653
|
var nameStyle = sel ? (BOLD + WHITE) : DIM;
|
|
614
654
|
|
|
615
655
|
var statusParts = [];
|
|
616
|
-
if (pitem.
|
|
656
|
+
if (!pitem.enabled) {
|
|
657
|
+
statusParts.push(RED + "disabled" + RST);
|
|
658
|
+
} else if (pitem.autoUpdate) {
|
|
617
659
|
statusParts.push(GREEN + "auto" + RST);
|
|
618
660
|
} else {
|
|
619
661
|
statusParts.push(YELLOW + "manual" + RST);
|
|
620
662
|
}
|
|
621
|
-
if (pitem.
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
663
|
+
if (pitem.enabled) {
|
|
664
|
+
if (pitem.updateAvail) {
|
|
665
|
+
statusParts.push(CYAN + "UPDATE" + RST);
|
|
666
|
+
} else if (pitem.deployed) {
|
|
667
|
+
statusParts.push(GRAY + "ok" + RST);
|
|
668
|
+
} else {
|
|
669
|
+
statusParts.push(RED + "missing" + RST);
|
|
670
|
+
}
|
|
627
671
|
}
|
|
628
672
|
|
|
629
673
|
var statusStr = statusParts.join(GRAY + " | " + RST);
|
|
630
|
-
var
|
|
674
|
+
var versionStr = pitem.latestTag
|
|
675
|
+
? (GRAY + pitem.latestTag + RST)
|
|
676
|
+
: (pitem.localHead ? (GRAY + pitem.localHead.substring(0, 7) + RST) : (GRAY + "---" + RST));
|
|
631
677
|
|
|
632
|
-
pushBody(" " + bg + arrow + nameStyle + pad(trunc(pitem.name, nameW), nameW) + RST + bg + " " + statusStr + " " +
|
|
678
|
+
pushBody(" " + bg + arrow + nameStyle + pad(trunc(pitem.name, nameW), nameW) + RST + bg + " " + statusStr + " " + versionStr + RST, isSelected);
|
|
633
679
|
|
|
634
680
|
if (sel) {
|
|
635
681
|
var subInfo = GRAY + " " + trunc(pitem.subject || pitem.url, cols - 10) + RST;
|
|
@@ -687,15 +733,18 @@ function buildPlugins(pushBody, pushFoot, cols, barW) {
|
|
|
687
733
|
return;
|
|
688
734
|
}
|
|
689
735
|
|
|
690
|
-
var autoCount = 0, manualCount = 0, updateCount = 0;
|
|
736
|
+
var autoCount = 0, manualCount = 0, updateCount = 0, disabledCount = 0;
|
|
691
737
|
for (var p of pluginItems) {
|
|
692
|
-
if (p.
|
|
738
|
+
if (!p.enabled) disabledCount++;
|
|
739
|
+
else if (p.autoUpdate) autoCount++; else manualCount++;
|
|
693
740
|
if (p.updateAvail) updateCount++;
|
|
694
741
|
}
|
|
695
742
|
|
|
696
743
|
pushBody(" " + MAGENTA + "#" + GRAY + " Plugins " +
|
|
697
744
|
DIM + "(" + autoCount + " auto, " + manualCount + " manual" +
|
|
745
|
+
(disabledCount > 0 ? ", " + RED + disabledCount + " disabled" + DIM : "") +
|
|
698
746
|
(updateCount > 0 ? ", " + CYAN + updateCount + " updates" + DIM : "") +
|
|
747
|
+
(npmPluginItems.length > 0 ? ", " + GRAY + npmPluginItems.length + " npm" + DIM : "") +
|
|
699
748
|
")" + RST, false);
|
|
700
749
|
|
|
701
750
|
if (!pluginFetched) {
|
|
@@ -706,6 +755,16 @@ function buildPlugins(pushBody, pushFoot, cols, barW) {
|
|
|
706
755
|
buildPluginItem(pushBody, i, pluginItems[i], nameW, cols, i === pcursor);
|
|
707
756
|
}
|
|
708
757
|
|
|
758
|
+
if (npmPluginItems.length > 0) {
|
|
759
|
+
pushBody("", false);
|
|
760
|
+
pushBody(" " + MAGENTA + "#" + GRAY + " npm plugins" + RST, false);
|
|
761
|
+
for (var ni = 0; ni < npmPluginItems.length; ni++) {
|
|
762
|
+
var np = npmPluginItems[ni];
|
|
763
|
+
var nvstr = np.version ? (GRAY + "v" + np.version + RST) : (GRAY + "not installed" + RST);
|
|
764
|
+
pushBody(" " + DIM + np.name + RST + " " + nvstr, false);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
709
768
|
pushBody("", false);
|
|
710
769
|
|
|
711
770
|
if (message) {
|
|
@@ -754,7 +813,7 @@ function render() {
|
|
|
754
813
|
pushHead("");
|
|
755
814
|
pushHead(" " + BOLD + CYAN + " OpenCode" + RST + GRAY + " Launcher" + RST);
|
|
756
815
|
pushHead(" " + GRAY + "-".repeat(barW) + RST);
|
|
757
|
-
var showPluginsTab = pluginItems.length > 0;
|
|
816
|
+
var showPluginsTab = pluginItems.length > 0 || npmPluginItems.length > 0;
|
|
758
817
|
var projTab = page === "projects" ? (BOLD + WHITE + BG_SEL + " Projects " + RST) : (GRAY + " Projects " + RST);
|
|
759
818
|
var plugTab = showPluginsTab ? (page === "plugins" ? (BOLD + WHITE + BG_SEL + " Plugins " + RST) : (GRAY + " Plugins " + RST)) : "";
|
|
760
819
|
pushHead(" " + projTab + (showPluginsTab ? " " + plugTab + " " + DIM + "<- ->" + RST : ""));
|
|
@@ -937,6 +996,26 @@ function handlePluginKey(key) {
|
|
|
937
996
|
flash(pitem.name + ": auto-update " + (newVal ? "ON" : "OFF"));
|
|
938
997
|
mode = "list";
|
|
939
998
|
}
|
|
999
|
+
else if (action === "disable-plugin") {
|
|
1000
|
+
var plugins = loadPlugins();
|
|
1001
|
+
var match = plugins.find(function(r) { return r.name === pitem.name; });
|
|
1002
|
+
if (match) { match.enabled = false; savePlugins(plugins); }
|
|
1003
|
+
var deployedPath = join(PLUGINS_DIR, pitem.pluginFile);
|
|
1004
|
+
if (existsSync(deployedPath)) { try { unlinkSync(deployedPath); } catch {} }
|
|
1005
|
+
pluginItems = buildPluginList();
|
|
1006
|
+
if (pcursor >= pluginItems.length) pcursor = Math.max(0, pluginItems.length - 1);
|
|
1007
|
+
flash(pitem.name + " disabled. Restart OpenCode to unload.");
|
|
1008
|
+
mode = "list";
|
|
1009
|
+
}
|
|
1010
|
+
else if (action === "enable-plugin") {
|
|
1011
|
+
var plugins = loadPlugins();
|
|
1012
|
+
var match = plugins.find(function(r) { return r.name === pitem.name; });
|
|
1013
|
+
if (match) { delete match.enabled; savePlugins(plugins); }
|
|
1014
|
+
pluginItems = buildPluginList();
|
|
1015
|
+
if (pcursor >= pluginItems.length) pcursor = Math.max(0, pluginItems.length - 1);
|
|
1016
|
+
flash(pitem.name + " enabled. Use Update to deploy.");
|
|
1017
|
+
mode = "list";
|
|
1018
|
+
}
|
|
940
1019
|
else if (action === "commits") {
|
|
941
1020
|
var dir = join(REPOS_DIR, pitem.folderName);
|
|
942
1021
|
if (!existsSync(dir)) { flash("Not installed locally yet"); mode = "list"; return; }
|