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.
Files changed (2) hide show
  1. package/oc-tui.js +93 -14
  2. 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: "Disable auto-update" });
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.autoUpdate) {
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.updateAvail) {
622
- statusParts.push(CYAN + "UPDATE" + RST);
623
- } else if (pitem.deployed) {
624
- statusParts.push(GRAY + "ok" + RST);
625
- } else {
626
- statusParts.push(RED + "missing" + RST);
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 commitStr = pitem.localHead ? (GRAY + pitem.localHead.substring(0, 7) + RST) : (GRAY + "---" + RST);
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 + " " + commitStr + RST, isSelected);
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.autoUpdate) autoCount++; else manualCount++;
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; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-hub",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "TUI launcher for OpenCode - project switcher and plugin manager with oc command",
5
5
  "main": "plugin.js",
6
6
  "type": "module",