runbay 0.1.0 → 0.1.2
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/README.en.md +97 -0
- package/README.md +67 -30
- package/out/main/index.js +59 -29
- package/package.json +6 -2
package/README.en.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="build/icon.png" alt="RunBay" width="96" />
|
|
4
|
+
|
|
5
|
+
# RunBay
|
|
6
|
+
|
|
7
|
+
**Not an installer. A launcher.**
|
|
8
|
+
|
|
9
|
+
[Français](README.md) · **English**
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
Package managers are great at installing things. They are terrible at showing you what you **already** have, letting you run it, and telling you what's out of date. **RunBay** fixes that: it finds every app and CLI you installed from the command line — **npm, pnpm, yarn, bun, scoop** — and puts them in one fast, modern window.
|
|
14
|
+
|
|
15
|
+
## 🖥️ The interface
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
## ✨ What it does
|
|
20
|
+
|
|
21
|
+
- **Detects** everything installed through the package managers actually present on your machine — nothing invented, RunBay reads what your managers already know
|
|
22
|
+
- **Launches** any of them in one click — interactive CLIs open in a real console
|
|
23
|
+
- **Updates** only when a newer version actually exists — with *Update all*, and *ignore this version* when the nag gets old
|
|
24
|
+
- **Uninstalls** with a real confirmation dialog, never an accidental click
|
|
25
|
+
- **Desktop shortcuts** for the tools you use every day
|
|
26
|
+
- **Detail panel**: description, homepage, exposed binaries, install location
|
|
27
|
+
- **Tray mode**: close the window and RunBay keeps scanning quietly, then notifies you when something can be updated
|
|
28
|
+
- **Light and dark themes**, collapsible sidebar, instant search, per-source colors
|
|
29
|
+
|
|
30
|
+
## 🔄 RunBay updates itself
|
|
31
|
+
|
|
32
|
+
When a newer version lands on npm, a yellow dot appears in the app. One click: it reinstalls and restarts on its own. No more remembering `npm update -g`.
|
|
33
|
+
|
|
34
|
+
## 📦 Supported sources
|
|
35
|
+
|
|
36
|
+
| Source | Detect | Launch | Uninstall | Update |
|
|
37
|
+
|---|---|---|---|---|
|
|
38
|
+
| npm (global) | ✅ | ✅ | ✅ | ✅ |
|
|
39
|
+
| pnpm (global) | ✅ | ✅ | ✅ | ✅ |
|
|
40
|
+
| yarn v1 (global) | ✅ | ✅ | ✅ | ✅ |
|
|
41
|
+
| bun (global) | ✅ | ✅ | ✅ | ✅ |
|
|
42
|
+
| scoop | ✅ | ✅ | ✅ | ✅ |
|
|
43
|
+
|
|
44
|
+
## ⚙️ Installation
|
|
45
|
+
|
|
46
|
+
> **Prerequisite: [Node.js](https://nodejs.org) must be installed** (LTS recommended). This is required — without Node.js, RunBay can neither install nor run.
|
|
47
|
+
|
|
48
|
+
RunBay ships through npm (no installer, no SmartScreen):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install -g runbay
|
|
52
|
+
runbay
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or try it once, without installing anything:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx runbay
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 🔧 Daily use
|
|
62
|
+
|
|
63
|
+
Select an app to open its detail — version, source, install location, exposed binaries — then launch it, create a shortcut, or uninstall it.
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
Right-click opens a menu grouped by intent: *Run*, *Manage*.
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
The **Options** button holds every setting: theme, startup, tray, welcome guide — and updating RunBay itself.
|
|
72
|
+
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
## 🧩 Why "not an installer"
|
|
76
|
+
|
|
77
|
+
An installer adds things to your machine. A launcher gets you *to* the things you already have. RunBay never installs anything for you — it reads what npm, pnpm, yarn, bun and scoop already know, and turns that list into a place you can actually work: launch, update, remove, shortcut. The command line stays your install tool; RunBay is your run tool.
|
|
78
|
+
|
|
79
|
+
## 👨💻 Development
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install
|
|
83
|
+
npm run dev # the app with hot reload
|
|
84
|
+
npm run typecheck # type checking
|
|
85
|
+
npm run build # production bundles
|
|
86
|
+
npm run package:win # Windows installer (electron-builder)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Each source is an isolated **provider** (`src/main/providers/`): adding a new one means adding one file that implements the `Provider` interface.
|
|
90
|
+
|
|
91
|
+
## ☕ Support
|
|
92
|
+
|
|
93
|
+
[Buy Me a Coffee — skroproduction](https://buymeacoffee.com/skroproduction)
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT
|
package/README.md
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="build/icon.png" alt="RunBay" width="96" />
|
|
4
|
+
|
|
1
5
|
# RunBay
|
|
2
6
|
|
|
3
|
-
**
|
|
7
|
+
**Pas un installateur. Un lanceur.**
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
**Français** · [English](README.en.md)
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
</div>
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
npm install -g runbay # then start it with: runbay
|
|
11
|
-
```
|
|
13
|
+
Les gestionnaires de paquets installent très bien les choses. Ils sont terribles pour vous montrer ce que vous avez **déjà**, vous permettre de le lancer, et vous dire ce qui n'est pas à jour. **RunBay** corrige ça : il détecte toutes les apps et CLI installées en ligne de commande — **npm, pnpm, yarn, bun, scoop** — et les réunit dans une seule fenêtre, rapide et moderne.
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## 🖥️ L'interface
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
npx runbay
|
|
17
|
-
```
|
|
17
|
+

|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
## ✨ Fonctionnalités
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
- **Détection** de tout ce qui est installé via les gestionnaires réellement présents sur votre machine — rien d'inventé, RunBay lit ce que vos gestionnaires savent déjà
|
|
22
|
+
- **Lancement** en un clic — les CLI interactifs s'ouvrent dans une vraie console
|
|
23
|
+
- **Mises à jour** uniquement quand une version plus récente existe vraiment — avec *Update all*, et *ignorer cette version* quand la notif devient insistante
|
|
24
|
+
- **Désinstallation** avec un vrai dialogue de confirmation, jamais un clic de trop
|
|
25
|
+
- **Raccourcis bureau** pour les outils que vous utilisez tous les jours
|
|
26
|
+
- **Panneau de détail** : description, page d'accueil, binaires exposés, emplacement d'installation
|
|
27
|
+
- **Mode tray** : fermez la fenêtre, RunBay continue de scanner discrètement et vous prévient quand quelque chose peut être mis à jour
|
|
28
|
+
- **Thèmes clair et sombre**, barre latérale repliable, recherche instantanée, couleurs par source
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
- ▶ **Launches** any of them in one click — interactive CLIs open in a real console
|
|
25
|
-
- ⬆ **Updates** only when a newer version actually exists — with *Update all*, and *ignore this version* when you don't want the nag
|
|
26
|
-
- 🗑 **Uninstalls** with a real confirmation dialog, never an accidental click
|
|
27
|
-
- 📌 **Desktop shortcuts** for the tools you use every day
|
|
28
|
-
- 🧰 **Detail panel**: description, homepage, exposed binaries, install location
|
|
29
|
-
- 🕐 **Tray mode**: close the window and RunBay keeps scanning quietly, then notifies you when something can be updated
|
|
30
|
-
- 🌗 Light and dark themes, collapsible sidebar, search, per-source colors
|
|
30
|
+
## 🔄 RunBay se met à jour tout seul
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Quand une nouvelle version arrive sur npm, un point jaune apparaît dans l'app. Un clic : réinstallation et relance automatiques. Plus besoin de penser à `npm update -g`.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
## 📦 Sources supportées
|
|
35
|
+
|
|
36
|
+
| Source | Détection | Lancement | Désinstallation | Mise à jour |
|
|
35
37
|
|---|---|---|---|---|
|
|
36
38
|
| npm (global) | ✅ | ✅ | ✅ | ✅ |
|
|
37
39
|
| pnpm (global) | ✅ | ✅ | ✅ | ✅ |
|
|
@@ -39,22 +41,57 @@ RunBay **updates itself**: when a newer version lands on npm, a yellow dot appea
|
|
|
39
41
|
| bun (global) | ✅ | ✅ | ✅ | ✅ |
|
|
40
42
|
| scoop | ✅ | ✅ | ✅ | ✅ |
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
## ⚙️ Installation
|
|
45
|
+
|
|
46
|
+
> **Prérequis : [Node.js](https://nodejs.org) doit être installé** (version LTS recommandée). C'est indispensable — sans Node.js, RunBay ne peut ni s'installer ni se lancer.
|
|
47
|
+
|
|
48
|
+
RunBay se distribue via npm (pas d'installeur, pas de SmartScreen) :
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install -g runbay
|
|
52
|
+
runbay
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Ou pour essayer une seule fois, sans rien installer :
|
|
43
56
|
|
|
44
|
-
|
|
57
|
+
```bash
|
|
58
|
+
npx runbay
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 🔧 L'usage au quotidien
|
|
62
|
+
|
|
63
|
+
Sélectionnez une app pour ouvrir son détail — version, source, emplacement, binaires exposés — puis lancez, créez un raccourci, désinstallez.
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
Le clic droit ouvre un menu classé par intention : *Run*, *Manage*.
|
|
45
68
|
|
|
46
|
-
|
|
69
|
+

|
|
47
70
|
|
|
48
|
-
|
|
71
|
+
Le bouton **Options** regroupe les réglages : thème, démarrage, tray, guide de bienvenue — et la mise à jour de RunBay lui-même.
|
|
72
|
+
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
## 🧩 Pourquoi « pas un installateur »
|
|
76
|
+
|
|
77
|
+
Un installateur ajoute des choses à votre machine. Un lanceur vous *amène* à ce que vous avez déjà. RunBay n'installe jamais rien à votre place — il lit ce que npm, pnpm, yarn, bun et scoop savent déjà, et transforme cette liste en un endroit où l'on peut travailler : lancer, mettre à jour, retirer, créer un raccourci. La ligne de commande reste votre outil d'installation ; RunBay est votre outil de lancement.
|
|
78
|
+
|
|
79
|
+
## 👨💻 Développement
|
|
49
80
|
|
|
50
81
|
```bash
|
|
51
82
|
npm install
|
|
52
|
-
npm run dev #
|
|
53
|
-
npm run typecheck #
|
|
54
|
-
npm run build #
|
|
55
|
-
npm run package:win #
|
|
83
|
+
npm run dev # l'app avec rechargement à chaud
|
|
84
|
+
npm run typecheck # vérification des types
|
|
85
|
+
npm run build # bundles de production
|
|
86
|
+
npm run package:win # installeur Windows (electron-builder)
|
|
56
87
|
```
|
|
57
88
|
|
|
89
|
+
Chaque source est un **provider** isolé (`src/main/providers/`) : ajouter une nouvelle source = un seul fichier implémentant l'interface `Provider`.
|
|
90
|
+
|
|
91
|
+
## ☕ Support
|
|
92
|
+
|
|
93
|
+
[Buy Me a Coffee — skroproduction](https://buymeacoffee.com/skroproduction)
|
|
94
|
+
|
|
58
95
|
## License
|
|
59
96
|
|
|
60
97
|
MIT
|
package/out/main/index.js
CHANGED
|
@@ -56,7 +56,7 @@ function cachedIcon(key, candidates) {
|
|
|
56
56
|
}
|
|
57
57
|
async function resolve(key, candidates) {
|
|
58
58
|
const cacheDir = node_path.join(electron.app.getPath("userData"), "icon-cache");
|
|
59
|
-
const cacheFile = node_path.join(cacheDir, node_crypto.createHash("sha1").update(key).digest("hex"));
|
|
59
|
+
const cacheFile = node_path.join(cacheDir, node_crypto.createHash("sha1").update(`v2:${key}`).digest("hex"));
|
|
60
60
|
if (node_fs.existsSync(cacheFile)) return toDataUrl(node_fs.readFileSync(cacheFile));
|
|
61
61
|
for (const url of await candidates()) {
|
|
62
62
|
const buf = await download(url);
|
|
@@ -80,6 +80,7 @@ async function iconCandidates(homepage) {
|
|
|
80
80
|
if (host === "github.com" || host === "www.github.com") {
|
|
81
81
|
const repo = url.pathname.match(/^\/([^/]+)\/([^/]+)/);
|
|
82
82
|
if (!repo) return [];
|
|
83
|
+
const candidates = [`https://github.com/${repo[1]}/${repo[2]}.png`];
|
|
83
84
|
try {
|
|
84
85
|
const res = await fetch(`https://api.github.com/repos/${repo[1]}/${repo[2]}`, {
|
|
85
86
|
headers: { "user-agent": "runbay" },
|
|
@@ -87,11 +88,11 @@ async function iconCandidates(homepage) {
|
|
|
87
88
|
});
|
|
88
89
|
if (res.ok) {
|
|
89
90
|
const json = await res.json();
|
|
90
|
-
if (json.owner?.avatar_url)
|
|
91
|
+
if (json.owner?.avatar_url) candidates.push(json.owner.avatar_url);
|
|
91
92
|
}
|
|
92
93
|
} catch {
|
|
93
94
|
}
|
|
94
|
-
return
|
|
95
|
+
return candidates;
|
|
95
96
|
}
|
|
96
97
|
return [`https://${host}/favicon.ico`, `https://www.google.com/s2/favicons?domain=${host}&sz=64`];
|
|
97
98
|
}
|
|
@@ -631,7 +632,6 @@ async function scanAll(onBatch, onStatus) {
|
|
|
631
632
|
const REGISTRY = process.env.RUNBAY_REGISTRY ?? "https://registry.npmjs.org";
|
|
632
633
|
const PKG = "runbay";
|
|
633
634
|
const SIM_CMD = process.env.RUNBAY_SELFUPDATE_CMD;
|
|
634
|
-
const SIMULATED = !!SIM_CMD;
|
|
635
635
|
let available = null;
|
|
636
636
|
function selfUpdateStatus() {
|
|
637
637
|
return available;
|
|
@@ -657,46 +657,76 @@ async function checkSelfUpdate() {
|
|
|
657
657
|
}
|
|
658
658
|
return available;
|
|
659
659
|
}
|
|
660
|
-
async function
|
|
660
|
+
async function globalRoot() {
|
|
661
661
|
try {
|
|
662
|
-
|
|
663
|
-
return electron.app.getAppPath().toLowerCase().startsWith(root.toLowerCase());
|
|
662
|
+
return (await run("npm", ["root", "-g"], { shim: true, timeout: 3e4 })).trim();
|
|
664
663
|
} catch {
|
|
665
|
-
return
|
|
664
|
+
return null;
|
|
666
665
|
}
|
|
667
666
|
}
|
|
667
|
+
async function installedGlobally() {
|
|
668
|
+
const root = await globalRoot();
|
|
669
|
+
return !!root && electron.app.getAppPath().toLowerCase().startsWith(root.toLowerCase());
|
|
670
|
+
}
|
|
671
|
+
function runCommand(command) {
|
|
672
|
+
return new Promise((resolve2, reject) => {
|
|
673
|
+
const child = node_child_process.spawn(command, { shell: true, windowsHide: true });
|
|
674
|
+
const timer = setTimeout(() => {
|
|
675
|
+
child.kill();
|
|
676
|
+
reject(new Error("npm timed out"));
|
|
677
|
+
}, 5 * 6e4);
|
|
678
|
+
child.on("error", reject);
|
|
679
|
+
child.on("close", (code) => {
|
|
680
|
+
clearTimeout(timer);
|
|
681
|
+
if (code === 0) resolve2();
|
|
682
|
+
else reject(new Error(`npm exited with code ${code}`));
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
}
|
|
668
686
|
async function applySelfUpdate() {
|
|
669
687
|
if (!available) return { state: "error", message: "No update pending" };
|
|
670
688
|
const target = available.latest;
|
|
671
689
|
const registryFlag = REGISTRY === "https://registry.npmjs.org" ? "" : ` --registry "${REGISTRY}"`;
|
|
672
|
-
|
|
673
|
-
|
|
690
|
+
if (SIM_CMD) {
|
|
691
|
+
try {
|
|
692
|
+
await runCommand(SIM_CMD);
|
|
693
|
+
} catch (err) {
|
|
694
|
+
return { state: "error", message: err instanceof Error ? err.message : String(err) };
|
|
695
|
+
}
|
|
696
|
+
available = null;
|
|
697
|
+
return { state: "done", message: `Updated to ${target} — restart to apply` };
|
|
698
|
+
}
|
|
699
|
+
if (!await installedGlobally()) {
|
|
674
700
|
return { state: "manual", message: `npm install -g ${PKG}@latest` };
|
|
675
701
|
}
|
|
702
|
+
const root = await globalRoot();
|
|
703
|
+
const shim = node_path.join(node_path.dirname(root), `${PKG}.cmd`);
|
|
704
|
+
const script = node_path.join(electron.app.getPath("userData"), "update.cmd");
|
|
705
|
+
const log = node_path.join(electron.app.getPath("userData"), "update.log");
|
|
706
|
+
const install = `call npm install -g ${PKG}@latest${registryFlag}`;
|
|
707
|
+
const body = [
|
|
708
|
+
"@echo off",
|
|
709
|
+
"ping -n 5 127.0.0.1 >nul",
|
|
710
|
+
// le temps que l'app se ferme et relâche ses fichiers
|
|
711
|
+
`${install} > "${log}" 2>&1`,
|
|
712
|
+
"if errorlevel 1 (",
|
|
713
|
+
" ping -n 31 127.0.0.1 >nul",
|
|
714
|
+
// le registre met parfois une minute à propager une publication prouvée
|
|
715
|
+
`${install} --prefer-online >> "${log}" 2>&1`,
|
|
716
|
+
")",
|
|
717
|
+
`start "" "${shim}"`,
|
|
718
|
+
'del "%~f0"',
|
|
719
|
+
""
|
|
720
|
+
].join("\r\n");
|
|
676
721
|
try {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
const timer = setTimeout(() => {
|
|
680
|
-
child.kill();
|
|
681
|
-
reject(new Error("npm timed out"));
|
|
682
|
-
}, 5 * 6e4);
|
|
683
|
-
child.on("error", reject);
|
|
684
|
-
child.on("close", (code) => {
|
|
685
|
-
clearTimeout(timer);
|
|
686
|
-
if (code === 0) resolve2();
|
|
687
|
-
else reject(new Error(`npm exited with code ${code}`));
|
|
688
|
-
});
|
|
689
|
-
});
|
|
722
|
+
node_fs.writeFileSync(script, body);
|
|
723
|
+
node_child_process.spawn(script, { detached: true, stdio: "ignore", windowsHide: true }).unref();
|
|
690
724
|
} catch (err) {
|
|
691
725
|
return { state: "error", message: err instanceof Error ? err.message : String(err) };
|
|
692
726
|
}
|
|
693
727
|
available = null;
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
electron.app.relaunch();
|
|
697
|
-
electron.app.quit();
|
|
698
|
-
}, 1500);
|
|
699
|
-
return { state: "done", message: `Updated to ${target} — restarting…` };
|
|
728
|
+
setTimeout(() => electron.app.quit(), 1500);
|
|
729
|
+
return { state: "done", message: `Updating to ${target} — restarting…` };
|
|
700
730
|
}
|
|
701
731
|
let scanning = false;
|
|
702
732
|
let mainHooks = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runbay",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Not an installer. A launcher. Detect, launch, update and uninstall apps installed from the command line (npm, pnpm, yarn, bun, scoop) — in one modern window.",
|
|
5
5
|
"main": "./out/main/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -39,7 +39,11 @@
|
|
|
39
39
|
"bun",
|
|
40
40
|
"scoop",
|
|
41
41
|
"electron",
|
|
42
|
-
"windows"
|
|
42
|
+
"windows",
|
|
43
|
+
"desktop-app",
|
|
44
|
+
"developer-tools",
|
|
45
|
+
"self-update",
|
|
46
|
+
"tray"
|
|
43
47
|
],
|
|
44
48
|
"author": "Jerem (https://github.com/10skro)",
|
|
45
49
|
"license": "MIT",
|