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.
Files changed (3) hide show
  1. package/index.js +101 -20
  2. package/package.json +2 -6
  3. 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
- // Find the downloaded binary
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
- // Fallback: check node_modules/.bin
12
- const fallbackPath = path.join(__dirname, '..', '.bin', binaryName);
15
+ // ── Download helpers ──
13
16
 
14
- const exe = fs.existsSync(binaryPath) ? binaryPath : fallbackPath;
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
- if (!fs.existsSync(exe)) {
17
- console.error('Santui binary not found. Run `npm install` again or check your installation.');
18
- process.exit(1);
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
- // Spawn the binary with all passed arguments
22
- const child = spawn(exe, process.argv.slice(2), {
23
- stdio: 'inherit',
24
- windowsHide: false,
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
- child.on('close', (code) => {
28
- process.exit(code);
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('error', (err) => {
32
- console.error('Failed to start Santui:', err.message);
33
- process.exit(1);
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.8",
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": "./index.js"
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();