santui 0.1.8 → 0.2.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 +101 -20
- package/package.json +2 -6
- package/install.js +0 -141
package/index.js
CHANGED
|
@@ -1,34 +1,115 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { spawn } = require('child_process');
|
|
3
|
+
const { spawn, execSync } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const http = require('http');
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
10
|
+
const version = pkg.version;
|
|
11
|
+
const repo = 'sonyarianto/santui';
|
|
8
12
|
const binaryName = process.platform === 'win32' ? 'santui.exe' : 'santui';
|
|
9
13
|
const binaryPath = path.join(__dirname, binaryName);
|
|
10
14
|
|
|
11
|
-
//
|
|
12
|
-
const fallbackPath = path.join(__dirname, '..', '.bin', binaryName);
|
|
15
|
+
// ── Download helpers ──
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
function getTarget() {
|
|
18
|
+
const os = process.platform;
|
|
19
|
+
const arch = process.arch;
|
|
20
|
+
if (arch !== 'x64' && arch !== 'arm64') die(`Unsupported architecture: ${arch}`);
|
|
21
|
+
if (os === 'win32') return 'x86_64-pc-windows-msvc';
|
|
22
|
+
if (os === 'darwin') {
|
|
23
|
+
if (arch === 'arm64') return 'aarch64-apple-darwin';
|
|
24
|
+
die('Intel Mac (x64) is not supported yet. Build from source instead:\n git clone https://github.com/sonyarianto/santui.git && cd santui && cargo build --workspace');
|
|
25
|
+
}
|
|
26
|
+
if (os === 'linux') return 'x86_64-unknown-linux-gnu';
|
|
27
|
+
die(`Unsupported platform: ${os}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getArchiveExt() { return process.platform === 'win32' ? 'zip' : 'tar.gz'; }
|
|
15
31
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
32
|
+
function download(url, dest) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const file = fs.createWriteStream(dest);
|
|
35
|
+
const protocol = url.startsWith('https') ? https : http;
|
|
36
|
+
protocol.get(url, (res) => {
|
|
37
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
38
|
+
file.close(); fs.unlinkSync(dest);
|
|
39
|
+
return download(res.headers.location, dest).then(resolve).catch(reject);
|
|
40
|
+
}
|
|
41
|
+
if (res.statusCode !== 200) { file.close(); fs.unlinkSync(dest); return reject(new Error(`HTTP ${res.statusCode}`)); }
|
|
42
|
+
res.pipe(file);
|
|
43
|
+
file.on('finish', () => { file.close(); resolve(); });
|
|
44
|
+
}).on('error', (err) => { file.close(); fs.unlinkSync(dest, () => {}); reject(err); });
|
|
45
|
+
});
|
|
19
46
|
}
|
|
20
47
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
48
|
+
function die(msg) { console.error(msg); process.exit(1); }
|
|
49
|
+
|
|
50
|
+
async function downloadBinary() {
|
|
51
|
+
const target = getTarget();
|
|
52
|
+
const ext = getArchiveExt();
|
|
53
|
+
const archiveUrl = `https://github.com/${repo}/releases/download/v${version}/santui-${target}.${ext}`;
|
|
54
|
+
|
|
55
|
+
console.error(`Downloading Santui v${version} (${target})...`);
|
|
56
|
+
console.error(` ${archiveUrl}`);
|
|
57
|
+
|
|
58
|
+
const tmpDir = fs.mkdtempSync(path.join(__dirname, 'tmp-'));
|
|
59
|
+
const archivePath = path.join(tmpDir, `santui.${ext}`);
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await download(archiveUrl, archivePath);
|
|
63
|
+
console.error(' Extracting...');
|
|
64
|
+
|
|
65
|
+
if (ext === 'zip') {
|
|
66
|
+
execSync(`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${tmpDir}' -Force"`, { stdio: 'pipe' });
|
|
67
|
+
const files = fs.readdirSync(tmpDir);
|
|
68
|
+
const exeFile = files.find(f => f.endsWith('.exe'));
|
|
69
|
+
if (!exeFile) throw new Error('santui.exe not found in archive');
|
|
70
|
+
fs.copyFileSync(path.join(tmpDir, exeFile), binaryPath);
|
|
71
|
+
} else {
|
|
72
|
+
execSync(`tar xzf '${archivePath}' -C '${tmpDir}'`, { stdio: 'pipe' });
|
|
73
|
+
const extracted = path.join(tmpDir, binaryName);
|
|
74
|
+
if (fs.existsSync(extracted)) {
|
|
75
|
+
fs.copyFileSync(extracted, binaryPath);
|
|
76
|
+
} else {
|
|
77
|
+
const items = fs.readdirSync(tmpDir);
|
|
78
|
+
let found = false;
|
|
79
|
+
for (const item of items) {
|
|
80
|
+
const itemPath = path.join(tmpDir, item);
|
|
81
|
+
if (fs.statSync(itemPath).isDirectory()) {
|
|
82
|
+
const sub = path.join(itemPath, binaryName);
|
|
83
|
+
if (fs.existsSync(sub)) { fs.copyFileSync(sub, binaryPath); found = true; break; }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!found) throw new Error(`${binaryName} not found in archive`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (process.platform !== 'win32') execSync(`chmod +x '${binaryPath}'`, { stdio: 'pipe' });
|
|
91
|
+
console.error(' [OK] Santui binary downloaded');
|
|
92
|
+
} catch (err) {
|
|
93
|
+
die(`Failed to download Santui: ${err.message}`);
|
|
94
|
+
} finally {
|
|
95
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Main ──
|
|
100
|
+
|
|
101
|
+
(async () => {
|
|
102
|
+
// Download binary on first run if missing
|
|
103
|
+
if (!fs.existsSync(binaryPath)) {
|
|
104
|
+
await downloadBinary();
|
|
105
|
+
}
|
|
26
106
|
|
|
27
|
-
|
|
28
|
-
process.
|
|
29
|
-
|
|
107
|
+
// Spawn the binary with all passed arguments
|
|
108
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
109
|
+
stdio: 'inherit',
|
|
110
|
+
windowsHide: false,
|
|
111
|
+
});
|
|
30
112
|
|
|
31
|
-
child.on('
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
113
|
+
child.on('close', (code) => process.exit(code));
|
|
114
|
+
child.on('error', (err) => die(`Failed to start Santui: ${err.message}`));
|
|
115
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "santui",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Your terminal home base — a modern TUI launcher for plugins",
|
|
5
5
|
"homepage": "https://github.com/sonyarianto/santui",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,14 +9,10 @@
|
|
|
9
9
|
"url": "git+https://github.com/sonyarianto/santui.git"
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
|
12
|
-
"santui": "
|
|
13
|
-
},
|
|
14
|
-
"scripts": {
|
|
15
|
-
"postinstall": "node install.js"
|
|
12
|
+
"santui": "index.js"
|
|
16
13
|
},
|
|
17
14
|
"files": [
|
|
18
15
|
"index.js",
|
|
19
|
-
"install.js",
|
|
20
16
|
"README.md"
|
|
21
17
|
],
|
|
22
18
|
"engines": {
|
package/install.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
const https = require('https');
|
|
2
|
-
const http = require('http');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { createWriteStream, readFileSync, writeFileSync } = require('fs');
|
|
6
|
-
const { execSync } = require('child_process');
|
|
7
|
-
|
|
8
|
-
const pkg = JSON.parse(readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
9
|
-
const version = pkg.version;
|
|
10
|
-
const repo = 'sonyarianto/santui';
|
|
11
|
-
|
|
12
|
-
// Map OS + arch → Rust target triple
|
|
13
|
-
function getTarget() {
|
|
14
|
-
const os = process.platform;
|
|
15
|
-
const arch = process.arch;
|
|
16
|
-
|
|
17
|
-
if (arch !== 'x64' && arch !== 'arm64') {
|
|
18
|
-
console.error(`Unsupported architecture: ${arch}`);
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (os === 'win32') {
|
|
23
|
-
return 'x86_64-pc-windows-msvc';
|
|
24
|
-
}
|
|
25
|
-
if (os === 'darwin') {
|
|
26
|
-
if (arch === 'arm64') return 'aarch64-apple-darwin';
|
|
27
|
-
console.error('Intel Mac (x64) is not supported yet. Build from source instead:');
|
|
28
|
-
console.error(' git clone https://github.com/sonyarianto/santui.git && cd santui && cargo build --workspace');
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
if (os === 'linux') {
|
|
32
|
-
return 'x86_64-unknown-linux-gnu';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
console.error(`Unsupported platform: ${os}`);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function getArchiveExt() {
|
|
40
|
-
return process.platform === 'win32' ? 'zip' : 'tar.gz';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function download(url, dest) {
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
const file = createWriteStream(dest);
|
|
46
|
-
const protocol = url.startsWith('https') ? https : http;
|
|
47
|
-
|
|
48
|
-
protocol.get(url, (response) => {
|
|
49
|
-
// Handle redirects (GitHub)
|
|
50
|
-
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
51
|
-
file.close();
|
|
52
|
-
fs.unlinkSync(dest);
|
|
53
|
-
return download(response.headers.location, dest).then(resolve).catch(reject);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (response.statusCode !== 200) {
|
|
57
|
-
file.close();
|
|
58
|
-
fs.unlinkSync(dest);
|
|
59
|
-
return reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
response.pipe(file);
|
|
63
|
-
file.on('finish', () => {
|
|
64
|
-
file.close();
|
|
65
|
-
resolve();
|
|
66
|
-
});
|
|
67
|
-
}).on('error', (err) => {
|
|
68
|
-
file.close();
|
|
69
|
-
fs.unlinkSync(dest, () => {});
|
|
70
|
-
reject(err);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function install() {
|
|
76
|
-
const target = getTarget();
|
|
77
|
-
const ext = getArchiveExt();
|
|
78
|
-
const archiveUrl = `https://github.com/${repo}/releases/download/v${version}/santui-${target}.${ext}`;
|
|
79
|
-
const binaryName = process.platform === 'win32' ? 'santui.exe' : 'santui';
|
|
80
|
-
const destPath = path.join(__dirname, binaryName);
|
|
81
|
-
|
|
82
|
-
console.log(`Downloading Santui v${version} (${target})...`);
|
|
83
|
-
console.log(` ${archiveUrl}`);
|
|
84
|
-
|
|
85
|
-
const tmpDir = fs.mkdtempSync(path.join(__dirname, 'tmp-'));
|
|
86
|
-
const archivePath = path.join(tmpDir, `santui.${ext}`);
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
// Download archive
|
|
90
|
-
await download(archiveUrl, archivePath);
|
|
91
|
-
|
|
92
|
-
// Extract
|
|
93
|
-
console.log(' Extracting...');
|
|
94
|
-
if (ext === 'zip') {
|
|
95
|
-
execSync(`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${tmpDir}' -Force"`, { stdio: 'pipe' });
|
|
96
|
-
// Find the exe in the extracted folder
|
|
97
|
-
const files = fs.readdirSync(tmpDir);
|
|
98
|
-
const exeFile = files.find(f => f.endsWith('.exe'));
|
|
99
|
-
if (!exeFile) throw new Error('santui.exe not found in archive');
|
|
100
|
-
fs.copyFileSync(path.join(tmpDir, exeFile), destPath);
|
|
101
|
-
} else {
|
|
102
|
-
execSync(`tar xzf '${archivePath}' -C '${tmpDir}'`, { stdio: 'pipe' });
|
|
103
|
-
// Binary is at root of archive (tar czf ... -C staging .)
|
|
104
|
-
const extractedBinary = path.join(tmpDir, binaryName);
|
|
105
|
-
if (fs.existsSync(extractedBinary)) {
|
|
106
|
-
fs.copyFileSync(extractedBinary, destPath);
|
|
107
|
-
} else {
|
|
108
|
-
// Fallback: check subdirectory (staging/)
|
|
109
|
-
const items = fs.readdirSync(tmpDir);
|
|
110
|
-
let found = false;
|
|
111
|
-
for (const item of items) {
|
|
112
|
-
const itemPath = path.join(tmpDir, item);
|
|
113
|
-
if (fs.statSync(itemPath).isDirectory()) {
|
|
114
|
-
const subBinary = path.join(itemPath, binaryName);
|
|
115
|
-
if (fs.existsSync(subBinary)) {
|
|
116
|
-
fs.copyFileSync(subBinary, destPath);
|
|
117
|
-
found = true;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (!found) throw new Error(`${binaryName} not found in archive`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Make executable on Unix
|
|
127
|
-
if (process.platform !== 'win32') {
|
|
128
|
-
execSync(`chmod +x '${destPath}'`, { stdio: 'pipe' });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
console.log(' [OK] Binary installed');
|
|
132
|
-
} catch (err) {
|
|
133
|
-
console.error(` [FAIL] ${err.message}`);
|
|
134
|
-
process.exit(1);
|
|
135
|
-
} finally {
|
|
136
|
-
// Cleanup temp
|
|
137
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
install();
|