@term-hub/term-hub 0.1.0
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/assets/icon.icns +0 -0
- package/assets/icon.ico +0 -0
- package/assets/icon.png +0 -0
- package/bin/hub.js +25 -0
- package/bin/term-hub.js +45 -0
- package/lib/resolve.js +59 -0
- package/package.json +42 -0
- package/scripts/postinstall.js +116 -0
- package/scripts/preuninstall.js +27 -0
package/assets/icon.icns
ADDED
|
Binary file
|
package/assets/icon.ico
ADDED
|
Binary file
|
package/assets/icon.png
ADDED
|
Binary file
|
package/bin/hub.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Passthrough to the `hub` CLI (install / status / kill / uninstall). Forwards
|
|
3
|
+
// args + stdio and mirrors the exit code.
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
const { spawn } = require("child_process");
|
|
7
|
+
const { exe } = require("../lib/resolve");
|
|
8
|
+
|
|
9
|
+
let bin;
|
|
10
|
+
try {
|
|
11
|
+
bin = exe("hub");
|
|
12
|
+
} catch (e) {
|
|
13
|
+
console.error(String(e.message || e));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const child = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
|
|
18
|
+
child.on("error", (err) => {
|
|
19
|
+
console.error(`hub: failed to run (${err.message}).`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|
|
22
|
+
child.on("exit", (code, signal) => {
|
|
23
|
+
if (signal) process.kill(process.pid, signal);
|
|
24
|
+
else process.exit(code ?? 0);
|
|
25
|
+
});
|
package/bin/term-hub.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Launch the Terminal Hub GUI. Detaches so the terminal returns immediately.
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { spawn } = require("child_process");
|
|
6
|
+
const { exe } = require("../lib/resolve");
|
|
7
|
+
|
|
8
|
+
let bin;
|
|
9
|
+
try {
|
|
10
|
+
bin = exe("hub-app");
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.error(String(e.message || e));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// On Linux the GUI needs the webkit2gtk runtime; hint if it's obviously missing.
|
|
17
|
+
if (process.platform === "linux") {
|
|
18
|
+
const fs = require("fs");
|
|
19
|
+
const hasWebkit = ["/usr/lib", "/usr/lib/x86_64-linux-gnu", "/usr/lib/aarch64-linux-gnu", "/usr/lib64"].some(
|
|
20
|
+
(d) => {
|
|
21
|
+
try {
|
|
22
|
+
return fs.readdirSync(d).some((f) => f.startsWith("libwebkit2gtk-4.1"));
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
if (!hasWebkit) {
|
|
29
|
+
console.error(
|
|
30
|
+
"Terminal Hub: the webkit2gtk runtime seems missing. If the window doesn't open, install it:\n" +
|
|
31
|
+
" Debian/Ubuntu: sudo apt install libwebkit2gtk-4.1-0\n" +
|
|
32
|
+
" Fedora: sudo dnf install webkit2gtk4.1"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const child = spawn(bin, process.argv.slice(2), {
|
|
38
|
+
stdio: "ignore",
|
|
39
|
+
detached: true,
|
|
40
|
+
});
|
|
41
|
+
child.on("error", (err) => {
|
|
42
|
+
console.error(`Terminal Hub: failed to launch (${err.message}).`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
45
|
+
child.unref(); // let the terminal return; the GUI keeps running
|
package/lib/resolve.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Resolve the prebuilt binary for the host platform/arch.
|
|
2
|
+
//
|
|
3
|
+
// The binaries live in a per-platform optional dependency
|
|
4
|
+
// (`@term-hub/<platform>-<arch>`); npm only installs the one matching the
|
|
5
|
+
// host (via each package's `os`/`cpu` fields), so exactly one is present.
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
const path = require("path");
|
|
9
|
+
|
|
10
|
+
// Platforms with published prebuilt binaries. Windows (win32-x64) is phase 2 —
|
|
11
|
+
// its package/CI are wired but not built yet, so it's intentionally absent here
|
|
12
|
+
// (Windows users get a clear "in progress" message rather than a broken install).
|
|
13
|
+
const SUPPORTED = new Set([
|
|
14
|
+
"darwin-arm64",
|
|
15
|
+
"darwin-x64",
|
|
16
|
+
"linux-x64",
|
|
17
|
+
"linux-arm64",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const KEY = `${process.platform}-${process.arch}`;
|
|
21
|
+
const PKG = `@term-hub/${KEY}`;
|
|
22
|
+
const EXT = process.platform === "win32" ? ".exe" : "";
|
|
23
|
+
|
|
24
|
+
/** Directory holding the prebuilt binaries for this platform, or null. */
|
|
25
|
+
function binDir() {
|
|
26
|
+
try {
|
|
27
|
+
// Resolve the platform package relative to THIS package (works whether
|
|
28
|
+
// installed globally or locally).
|
|
29
|
+
const manifest = require.resolve(`${PKG}/package.json`, { paths: [__dirname] });
|
|
30
|
+
return path.join(path.dirname(manifest), "bin");
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Absolute path to a named binary (e.g. "hub-app", "hub"), or throws a clear error. */
|
|
37
|
+
function exe(name) {
|
|
38
|
+
if (!SUPPORTED.has(KEY)) {
|
|
39
|
+
const windows = process.platform === "win32";
|
|
40
|
+
throw new Error(
|
|
41
|
+
windows
|
|
42
|
+
? `Terminal Hub: Windows support is in progress — no prebuilt binary yet.\n` +
|
|
43
|
+
`Track it: https://github.com/your-org/term-hub#roadmap--vision`
|
|
44
|
+
: `Terminal Hub: no prebuilt binary for ${KEY}.\n` +
|
|
45
|
+
`Supported: ${[...SUPPORTED].join(", ")}.\n` +
|
|
46
|
+
`See: https://github.com/your-org/term-hub#platform-support`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const dir = binDir();
|
|
50
|
+
if (!dir) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Terminal Hub: the binary package ${PKG} isn't installed.\n` +
|
|
53
|
+
`Reinstall with: npm install -g @term-hub/term-hub (optional deps must be allowed)`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return path.join(dir, name + EXT);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { KEY, PKG, SUPPORTED, binDir, exe };
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@term-hub/term-hub",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Terminal Hub — capture every terminal you open and manage them all from one window. One-command cross-platform install (macOS / Linux / Windows).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"terminal",
|
|
7
|
+
"tmux",
|
|
8
|
+
"pty",
|
|
9
|
+
"shell",
|
|
10
|
+
"tui",
|
|
11
|
+
"terminal-manager",
|
|
12
|
+
"tauri"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/your-org/term-hub#readme",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "TODO your name <ag14906@gmail.com>",
|
|
17
|
+
"type": "commonjs",
|
|
18
|
+
"bin": {
|
|
19
|
+
"term-hub": "bin/term-hub.js",
|
|
20
|
+
"hub": "bin/hub.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"bin",
|
|
24
|
+
"lib",
|
|
25
|
+
"scripts",
|
|
26
|
+
"assets"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"postinstall": "node scripts/postinstall.js",
|
|
30
|
+
"preuninstall": "node scripts/preuninstall.js"
|
|
31
|
+
},
|
|
32
|
+
"//": "optionalDependencies: npm installs ONLY the package whose os/cpu matches the host, so a mac user downloads just the darwin binaries, etc. Versions are kept in lockstep with this package and bumped together by CI.",
|
|
33
|
+
"optionalDependencies": {
|
|
34
|
+
"@term-hub/darwin-arm64": "0.1.0",
|
|
35
|
+
"@term-hub/darwin-x64": "0.1.0",
|
|
36
|
+
"@term-hub/linux-x64": "0.1.0",
|
|
37
|
+
"@term-hub/linux-arm64": "0.1.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Register a desktop launcher so Terminal Hub shows up in the OS apps menu
|
|
3
|
+
// (Launchpad / Linux app grid / Start Menu) and is clickable, not just runnable
|
|
4
|
+
// from a terminal. Best-effort: any failure here never fails `npm install`.
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const os = require("os");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
const NAME = "Terminal Hub";
|
|
12
|
+
const pkgDir = path.resolve(__dirname, "..");
|
|
13
|
+
const assets = path.join(pkgDir, "assets");
|
|
14
|
+
|
|
15
|
+
function safe(label, fn) {
|
|
16
|
+
try {
|
|
17
|
+
fn();
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.error(`Terminal Hub: could not register ${label} (${e.message}). You can still run \`term-hub\`.`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Resolve the GUI binary for this platform; if there isn't one, there's nothing
|
|
24
|
+
// to register (e.g. optional deps were skipped, or unsupported platform).
|
|
25
|
+
let appBin = null;
|
|
26
|
+
try {
|
|
27
|
+
const p = require("../lib/resolve").exe("hub-app");
|
|
28
|
+
if (fs.existsSync(p)) appBin = p;
|
|
29
|
+
} catch {
|
|
30
|
+
/* no binary for this platform */
|
|
31
|
+
}
|
|
32
|
+
if (!appBin) process.exit(0);
|
|
33
|
+
|
|
34
|
+
if (process.platform === "darwin") registerMac();
|
|
35
|
+
else if (process.platform === "linux") registerLinux();
|
|
36
|
+
else if (process.platform === "win32") registerWindows();
|
|
37
|
+
|
|
38
|
+
function registerMac() {
|
|
39
|
+
safe("the Applications entry", () => {
|
|
40
|
+
const appDir = path.join(os.homedir(), "Applications", `${NAME}.app`);
|
|
41
|
+
const macos = path.join(appDir, "Contents", "MacOS");
|
|
42
|
+
const res = path.join(appDir, "Contents", "Resources");
|
|
43
|
+
fs.mkdirSync(macos, { recursive: true });
|
|
44
|
+
fs.mkdirSync(res, { recursive: true });
|
|
45
|
+
|
|
46
|
+
const launcher = path.join(macos, "term-hub");
|
|
47
|
+
fs.writeFileSync(launcher, `#!/bin/sh\nexec "${appBin}" "$@"\n`);
|
|
48
|
+
fs.chmodSync(launcher, 0o755);
|
|
49
|
+
|
|
50
|
+
const icns = path.join(assets, "icon.icns");
|
|
51
|
+
if (fs.existsSync(icns)) fs.copyFileSync(icns, path.join(res, "icon.icns"));
|
|
52
|
+
|
|
53
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
54
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
55
|
+
<plist version="1.0"><dict>
|
|
56
|
+
<key>CFBundleName</key><string>${NAME}</string>
|
|
57
|
+
<key>CFBundleDisplayName</key><string>${NAME}</string>
|
|
58
|
+
<key>CFBundleIdentifier</key><string>dev.hub.launcher</string>
|
|
59
|
+
<key>CFBundleExecutable</key><string>term-hub</string>
|
|
60
|
+
<key>CFBundleIconFile</key><string>icon</string>
|
|
61
|
+
<key>CFBundlePackageType</key><string>APPL</string>
|
|
62
|
+
</dict></plist>
|
|
63
|
+
`;
|
|
64
|
+
fs.writeFileSync(path.join(appDir, "Contents", "Info.plist"), plist);
|
|
65
|
+
// A launcher .app created locally (not downloaded) isn't quarantined, so it
|
|
66
|
+
// opens with no Gatekeeper warning.
|
|
67
|
+
console.log(`Terminal Hub: added to ~/Applications — find it in Launchpad/Spotlight.`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function registerLinux() {
|
|
72
|
+
safe("the applications menu entry", () => {
|
|
73
|
+
const appsDir = path.join(os.homedir(), ".local", "share", "applications");
|
|
74
|
+
const iconsDir = path.join(os.homedir(), ".local", "share", "icons");
|
|
75
|
+
fs.mkdirSync(appsDir, { recursive: true });
|
|
76
|
+
fs.mkdirSync(iconsDir, { recursive: true });
|
|
77
|
+
|
|
78
|
+
const png = path.join(assets, "icon.png");
|
|
79
|
+
let icon = "utilities-terminal";
|
|
80
|
+
if (fs.existsSync(png)) {
|
|
81
|
+
fs.copyFileSync(png, path.join(iconsDir, "term-hub.png"));
|
|
82
|
+
icon = "term-hub";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const desktop = `[Desktop Entry]
|
|
86
|
+
Type=Application
|
|
87
|
+
Name=${NAME}
|
|
88
|
+
Comment=Capture and manage all your terminals
|
|
89
|
+
Exec="${appBin}" %U
|
|
90
|
+
Icon=${icon}
|
|
91
|
+
Terminal=false
|
|
92
|
+
Categories=Utility;System;TerminalEmulator;
|
|
93
|
+
`;
|
|
94
|
+
fs.writeFileSync(path.join(appsDir, "term-hub.desktop"), desktop);
|
|
95
|
+
console.log("Terminal Hub: added to your applications menu.");
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function registerWindows() {
|
|
100
|
+
safe("the Start Menu shortcut", () => {
|
|
101
|
+
const { execFileSync } = require("child_process");
|
|
102
|
+
const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
103
|
+
const startMenu = path.join(appData, "Microsoft", "Windows", "Start Menu", "Programs");
|
|
104
|
+
fs.mkdirSync(startMenu, { recursive: true });
|
|
105
|
+
const lnk = path.join(startMenu, `${NAME}.lnk`);
|
|
106
|
+
const ico = path.join(assets, "icon.ico");
|
|
107
|
+
const q = (s) => s.replace(/'/g, "''");
|
|
108
|
+
const ps =
|
|
109
|
+
`$s=(New-Object -ComObject WScript.Shell).CreateShortcut('${q(lnk)}');` +
|
|
110
|
+
`$s.TargetPath='${q(appBin)}';` +
|
|
111
|
+
(fs.existsSync(ico) ? `$s.IconLocation='${q(ico)}';` : "") +
|
|
112
|
+
`$s.Save()`;
|
|
113
|
+
execFileSync("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { stdio: "ignore" });
|
|
114
|
+
console.log("Terminal Hub: added to the Start Menu.");
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Remove the desktop launcher created by postinstall.js on `npm rm -g`.
|
|
3
|
+
// Best-effort; never throws.
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
|
|
10
|
+
const NAME = "Terminal Hub";
|
|
11
|
+
const rm = (p) => {
|
|
12
|
+
try {
|
|
13
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
14
|
+
} catch {
|
|
15
|
+
/* ignore */
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (process.platform === "darwin") {
|
|
20
|
+
rm(path.join(os.homedir(), "Applications", `${NAME}.app`));
|
|
21
|
+
} else if (process.platform === "linux") {
|
|
22
|
+
rm(path.join(os.homedir(), ".local", "share", "applications", "term-hub.desktop"));
|
|
23
|
+
rm(path.join(os.homedir(), ".local", "share", "icons", "term-hub.png"));
|
|
24
|
+
} else if (process.platform === "win32") {
|
|
25
|
+
const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
26
|
+
rm(path.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", `${NAME}.lnk`));
|
|
27
|
+
}
|