rskim 0.7.0 → 0.9.4
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/bin/skim +244 -0
- package/package.json +27 -16
- package/scripts/postinstall.js +327 -0
- package/bin/darwin/arm64/skim +0 -0
- package/bin/darwin/x64/skim +0 -0
- package/bin/linux/arm64/skim +0 -0
- package/bin/linux/x64/skim +0 -0
- package/bin/skim.js +0 -93
- package/bin/win32/x64/skim.exe +0 -0
package/bin/skim
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync, execSync } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Cached result for musl detection (null = not yet computed)
|
|
9
|
+
*/
|
|
10
|
+
let _muslCached = null;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Detect if running on musl-based Linux (Alpine, Void, etc.)
|
|
14
|
+
* Result is cached at module level since it cannot change during execution.
|
|
15
|
+
*
|
|
16
|
+
* ARCHITECTURE NOTE: This function is intentionally duplicated in scripts/postinstall.js.
|
|
17
|
+
* Postinstall scripts run before node_modules is fully available, so that script
|
|
18
|
+
* must be self-contained with no external dependencies. Do not extract to a shared
|
|
19
|
+
* module - it would break postinstall execution.
|
|
20
|
+
*/
|
|
21
|
+
function isMusl() {
|
|
22
|
+
if (_muslCached !== null) {
|
|
23
|
+
return _muslCached;
|
|
24
|
+
}
|
|
25
|
+
_muslCached = detectMusl();
|
|
26
|
+
return _muslCached;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Internal musl detection logic
|
|
31
|
+
*/
|
|
32
|
+
function detectMusl() {
|
|
33
|
+
if (process.platform !== 'linux') {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check for Alpine Linux (most common musl distro)
|
|
38
|
+
try {
|
|
39
|
+
const osRelease = fs.readFileSync('/etc/os-release', 'utf8');
|
|
40
|
+
if (osRelease.includes('Alpine')) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
// File doesn't exist, continue checking
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check ldd output for musl
|
|
48
|
+
try {
|
|
49
|
+
const lddOutput = execSync('ldd --version 2>&1 || true', { encoding: 'utf8' });
|
|
50
|
+
if (lddOutput.includes('musl')) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// ldd not available, continue checking
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for musl dynamic linker
|
|
58
|
+
try {
|
|
59
|
+
const files = fs.readdirSync('/lib');
|
|
60
|
+
if (files.some(f => f.startsWith('ld-musl'))) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// /lib doesn't exist or not readable
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get the platform-specific package name
|
|
72
|
+
*/
|
|
73
|
+
function getPlatformPackage() {
|
|
74
|
+
const platform = process.platform;
|
|
75
|
+
const arch = process.arch;
|
|
76
|
+
const musl = isMusl();
|
|
77
|
+
|
|
78
|
+
const packages = {
|
|
79
|
+
'darwin-arm64': '@rskim/cli-darwin-arm64',
|
|
80
|
+
'darwin-x64': '@rskim/cli-darwin-x64',
|
|
81
|
+
'linux-arm64': musl ? '@rskim/cli-linux-arm64-musl' : '@rskim/cli-linux-arm64',
|
|
82
|
+
'linux-x64': musl ? '@rskim/cli-linux-x64-musl' : '@rskim/cli-linux-x64',
|
|
83
|
+
'win32-x64': '@rskim/cli-win32-x64',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const key = `${platform}-${arch}`;
|
|
87
|
+
return packages[key];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Find installed platform package binary using walktree algorithm.
|
|
92
|
+
*
|
|
93
|
+
* Walks up the directory tree looking for node_modules containing the platform package.
|
|
94
|
+
* This approach is package-manager agnostic and works with npm, pnpm, yarn (classic & berry),
|
|
95
|
+
* bun, and arbitrary monorepo nesting depths.
|
|
96
|
+
*/
|
|
97
|
+
function findPlatformBinary(packageName) {
|
|
98
|
+
if (!packageName) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const binaryName = process.platform === 'win32' ? 'skim.exe' : 'skim';
|
|
103
|
+
let currentDir = __dirname;
|
|
104
|
+
|
|
105
|
+
// Walk up the directory tree looking for node_modules
|
|
106
|
+
for (let i = 0; i < 10; i++) {
|
|
107
|
+
const candidate = path.join(currentDir, 'node_modules', packageName, binaryName);
|
|
108
|
+
if (fs.existsSync(candidate)) {
|
|
109
|
+
return candidate;
|
|
110
|
+
}
|
|
111
|
+
const parent = path.dirname(currentDir);
|
|
112
|
+
if (parent === currentDir) break; // Hit filesystem root
|
|
113
|
+
currentDir = parent;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Find fallback binary in local bin directory (from postinstall)
|
|
121
|
+
*/
|
|
122
|
+
function findFallbackBinary() {
|
|
123
|
+
const binaryName = process.platform === 'win32' ? 'skim.exe' : 'skim';
|
|
124
|
+
const fallbackPath = path.join(__dirname, binaryName);
|
|
125
|
+
|
|
126
|
+
if (fs.existsSync(fallbackPath)) {
|
|
127
|
+
return fallbackPath;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get the binary path, with helpful error messages
|
|
135
|
+
*/
|
|
136
|
+
function getBinaryPath() {
|
|
137
|
+
const platform = process.platform;
|
|
138
|
+
const arch = process.arch;
|
|
139
|
+
const musl = isMusl();
|
|
140
|
+
|
|
141
|
+
// Get the platform-specific package
|
|
142
|
+
const packageName = getPlatformPackage();
|
|
143
|
+
|
|
144
|
+
if (!packageName) {
|
|
145
|
+
console.error(`ERROR: Platform ${platform}-${arch} is not supported.`);
|
|
146
|
+
console.error('');
|
|
147
|
+
console.error('Supported platforms:');
|
|
148
|
+
console.error(' - darwin-arm64 (macOS Apple Silicon)');
|
|
149
|
+
console.error(' - darwin-x64 (macOS Intel)');
|
|
150
|
+
console.error(' - linux-arm64 (Linux ARM64, glibc)');
|
|
151
|
+
console.error(' - linux-arm64-musl (Linux ARM64, Alpine/musl)');
|
|
152
|
+
console.error(' - linux-x64 (Linux x64, glibc)');
|
|
153
|
+
console.error(' - linux-x64-musl (Linux x64, Alpine/musl)');
|
|
154
|
+
console.error(' - win32-x64 (Windows x64)');
|
|
155
|
+
console.error('');
|
|
156
|
+
console.error('Workaround: Install via Cargo instead:');
|
|
157
|
+
console.error(' cargo install rskim');
|
|
158
|
+
console.error('');
|
|
159
|
+
console.error('Report issues: https://github.com/dean0x/skim/issues');
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Try to find the platform package binary
|
|
164
|
+
let binaryPath = findPlatformBinary(packageName);
|
|
165
|
+
|
|
166
|
+
// Fallback to local binary (from postinstall)
|
|
167
|
+
if (!binaryPath) {
|
|
168
|
+
binaryPath = findFallbackBinary();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!binaryPath) {
|
|
172
|
+
console.error(`ERROR: Could not find skim binary.`);
|
|
173
|
+
console.error('');
|
|
174
|
+
console.error(`Expected platform package: ${packageName}`);
|
|
175
|
+
if (musl) {
|
|
176
|
+
console.error('Detected: musl-based Linux (Alpine, Void, etc.)');
|
|
177
|
+
}
|
|
178
|
+
console.error('');
|
|
179
|
+
console.error('The platform-specific package may have failed to install.');
|
|
180
|
+
console.error('This can happen with npm\'s optional dependencies.');
|
|
181
|
+
console.error('');
|
|
182
|
+
console.error('Try reinstalling:');
|
|
183
|
+
console.error(' npm uninstall -g rskim && npm install -g rskim');
|
|
184
|
+
console.error('');
|
|
185
|
+
console.error('Or install via Cargo:');
|
|
186
|
+
console.error(' cargo install rskim');
|
|
187
|
+
console.error('');
|
|
188
|
+
console.error('Report issues: https://github.com/dean0x/skim/issues');
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Verify binary is executable
|
|
193
|
+
try {
|
|
194
|
+
const testResult = spawnSync(binaryPath, ['--version'], {
|
|
195
|
+
timeout: 5000,
|
|
196
|
+
stdio: 'pipe',
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (testResult.error) {
|
|
200
|
+
const errorMsg = testResult.error.message || '';
|
|
201
|
+
|
|
202
|
+
console.error('ERROR: Binary exists but cannot be executed.');
|
|
203
|
+
console.error(`Path: ${binaryPath}`);
|
|
204
|
+
console.error(`Error: ${errorMsg}`);
|
|
205
|
+
console.error('');
|
|
206
|
+
|
|
207
|
+
if (errorMsg.includes('ENOENT') || errorMsg.includes('libc') || errorMsg.includes('GLIBC')) {
|
|
208
|
+
console.error('This appears to be a C library compatibility issue.');
|
|
209
|
+
if (musl) {
|
|
210
|
+
console.error('You are on musl-based Linux but may have a glibc binary.');
|
|
211
|
+
} else {
|
|
212
|
+
console.error('You may be on Alpine/musl but have a glibc binary.');
|
|
213
|
+
}
|
|
214
|
+
console.error('');
|
|
215
|
+
console.error('For Alpine Linux:');
|
|
216
|
+
console.error(' apk add cargo');
|
|
217
|
+
console.error(' cargo install rskim');
|
|
218
|
+
} else {
|
|
219
|
+
console.error('Workaround: Install via Cargo:');
|
|
220
|
+
console.error(' cargo install rskim');
|
|
221
|
+
}
|
|
222
|
+
console.error('');
|
|
223
|
+
console.error('Report issues: https://github.com/dean0x/skim/issues');
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
} catch {
|
|
227
|
+
// Non-fatal, proceed with execution
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return binaryPath;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Main execution
|
|
234
|
+
const binaryPath = getBinaryPath();
|
|
235
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
236
|
+
stdio: 'inherit',
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (result.error) {
|
|
240
|
+
console.error(`ERROR: Failed to execute skim: ${result.error.message}`);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
process.exit(result.status || 0);
|
package/package.json
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rskim",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Smart code reader - streaming code transformation for AI agents",
|
|
5
|
-
"bin": {
|
|
6
|
-
"skim": "./bin/skim.js"
|
|
7
|
-
},
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/dean0x/skim.git"
|
|
11
|
-
},
|
|
12
5
|
"keywords": [
|
|
13
6
|
"code",
|
|
14
7
|
"ast",
|
|
@@ -18,17 +11,35 @@
|
|
|
18
11
|
],
|
|
19
12
|
"author": "Skim Contributors",
|
|
20
13
|
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/dean0x/skim.git"
|
|
17
|
+
},
|
|
21
18
|
"bugs": {
|
|
22
19
|
"url": "https://github.com/dean0x/skim/issues"
|
|
23
20
|
},
|
|
24
21
|
"homepage": "https://github.com/dean0x/skim#readme",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
22
|
+
"bin": {
|
|
23
|
+
"skim": "bin/skim",
|
|
24
|
+
"rskim": "bin/skim"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin",
|
|
28
|
+
"scripts"
|
|
29
29
|
],
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
"scripts": {
|
|
31
|
+
"postinstall": "node scripts/postinstall.js"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=16"
|
|
35
|
+
},
|
|
36
|
+
"optionalDependencies": {
|
|
37
|
+
"@rskim/cli-darwin-arm64": "0.9.4",
|
|
38
|
+
"@rskim/cli-darwin-x64": "0.9.4",
|
|
39
|
+
"@rskim/cli-linux-arm64": "0.9.4",
|
|
40
|
+
"@rskim/cli-linux-x64": "0.9.4",
|
|
41
|
+
"@rskim/cli-linux-arm64-musl": "0.9.4",
|
|
42
|
+
"@rskim/cli-linux-x64-musl": "0.9.4",
|
|
43
|
+
"@rskim/cli-win32-x64": "0.9.4"
|
|
44
|
+
}
|
|
34
45
|
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Postinstall script for rskim npm package.
|
|
4
|
+
*
|
|
5
|
+
* This script runs after `npm install` to ensure the binary is available.
|
|
6
|
+
* It downloads the platform-specific binary as a fallback if the optional
|
|
7
|
+
* dependency failed to install (common with npm's optional deps).
|
|
8
|
+
*
|
|
9
|
+
* IMPORTANT: This script only uses Node.js built-in modules (no dependencies).
|
|
10
|
+
*/
|
|
11
|
+
const { execSync, spawn } = require('child_process');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const https = require('https');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detect if running on musl-based Linux
|
|
19
|
+
*
|
|
20
|
+
* ARCHITECTURE NOTE: This function is intentionally duplicated in bin/skim.
|
|
21
|
+
* Postinstall scripts run before node_modules is fully available, so this script
|
|
22
|
+
* must be self-contained with no external dependencies. Do not extract to a shared
|
|
23
|
+
* module - it would break postinstall execution.
|
|
24
|
+
*/
|
|
25
|
+
function isMusl() {
|
|
26
|
+
if (process.platform !== 'linux') {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const osRelease = fs.readFileSync('/etc/os-release', 'utf8');
|
|
32
|
+
if (osRelease.includes('Alpine')) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
// Continue checking
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const lddOutput = execSync('ldd --version 2>&1 || true', { encoding: 'utf8' });
|
|
41
|
+
if (lddOutput.includes('musl')) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
// Continue checking
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const files = fs.readdirSync('/lib');
|
|
50
|
+
if (files.some(f => f.startsWith('ld-musl'))) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Not musl
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get platform-specific package name
|
|
62
|
+
*/
|
|
63
|
+
function getPlatformPackage() {
|
|
64
|
+
const platform = process.platform;
|
|
65
|
+
const arch = process.arch;
|
|
66
|
+
const musl = isMusl();
|
|
67
|
+
|
|
68
|
+
const packages = {
|
|
69
|
+
'darwin-arm64': '@rskim/cli-darwin-arm64',
|
|
70
|
+
'darwin-x64': '@rskim/cli-darwin-x64',
|
|
71
|
+
'linux-arm64': musl ? '@rskim/cli-linux-arm64-musl' : '@rskim/cli-linux-arm64',
|
|
72
|
+
'linux-x64': musl ? '@rskim/cli-linux-x64-musl' : '@rskim/cli-linux-x64',
|
|
73
|
+
'win32-x64': '@rskim/cli-win32-x64',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return packages[`${platform}-${arch}`];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if platform package is already installed using walktree algorithm.
|
|
81
|
+
*
|
|
82
|
+
* Walks up the directory tree looking for node_modules containing the platform package.
|
|
83
|
+
* This approach is package-manager agnostic and works with npm, pnpm, yarn (classic & berry),
|
|
84
|
+
* bun, and arbitrary monorepo nesting depths.
|
|
85
|
+
*/
|
|
86
|
+
function isPlatformPackageInstalled(packageName) {
|
|
87
|
+
const binaryName = process.platform === 'win32' ? 'skim.exe' : 'skim';
|
|
88
|
+
let currentDir = __dirname;
|
|
89
|
+
|
|
90
|
+
// Walk up the directory tree looking for node_modules
|
|
91
|
+
for (let i = 0; i < 10; i++) {
|
|
92
|
+
const candidate = path.join(currentDir, 'node_modules', packageName, binaryName);
|
|
93
|
+
if (fs.existsSync(candidate)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
const parent = path.dirname(currentDir);
|
|
97
|
+
if (parent === currentDir) break; // Hit filesystem root
|
|
98
|
+
currentDir = parent;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if fallback binary already exists
|
|
106
|
+
*/
|
|
107
|
+
function isFallbackInstalled() {
|
|
108
|
+
const binaryName = process.platform === 'win32' ? 'skim.exe' : 'skim';
|
|
109
|
+
const fallbackPath = path.join(__dirname, '..', 'bin', binaryName);
|
|
110
|
+
return fs.existsSync(fallbackPath);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Trusted domains for redirect validation.
|
|
115
|
+
* Only follow redirects to these domains to prevent redirect attacks.
|
|
116
|
+
*/
|
|
117
|
+
const TRUSTED_DOMAINS = [
|
|
118
|
+
'registry.npmjs.org',
|
|
119
|
+
'registry.yarnpkg.com',
|
|
120
|
+
'registry.npmmirror.com',
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Validate that a URL is within trusted domains
|
|
125
|
+
*/
|
|
126
|
+
function isUrlTrusted(urlString) {
|
|
127
|
+
try {
|
|
128
|
+
const url = new URL(urlString);
|
|
129
|
+
return TRUSTED_DOMAINS.some(domain => url.hostname === domain || url.hostname.endsWith('.' + domain));
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Download file with redirect handling
|
|
137
|
+
*/
|
|
138
|
+
function downloadToFile(url, destPath, maxRedirects = 5) {
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
if (maxRedirects <= 0) {
|
|
141
|
+
reject(new Error('Too many redirects'));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const file = fs.createWriteStream(destPath);
|
|
146
|
+
|
|
147
|
+
https.get(url, (response) => {
|
|
148
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
149
|
+
file.close();
|
|
150
|
+
fs.unlinkSync(destPath);
|
|
151
|
+
|
|
152
|
+
// Validate redirect URL stays within trusted domains
|
|
153
|
+
const redirectUrl = response.headers.location;
|
|
154
|
+
if (!isUrlTrusted(redirectUrl)) {
|
|
155
|
+
reject(new Error(`Redirect to untrusted domain blocked: ${redirectUrl}`));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
downloadToFile(redirectUrl, destPath, maxRedirects - 1)
|
|
160
|
+
.then(resolve)
|
|
161
|
+
.catch(reject);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (response.statusCode !== 200) {
|
|
166
|
+
file.close();
|
|
167
|
+
fs.unlinkSync(destPath);
|
|
168
|
+
reject(new Error(`HTTP ${response.statusCode}: ${url}`));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
response.pipe(file);
|
|
173
|
+
file.on('finish', () => {
|
|
174
|
+
file.close();
|
|
175
|
+
resolve();
|
|
176
|
+
});
|
|
177
|
+
file.on('error', (err) => {
|
|
178
|
+
file.close();
|
|
179
|
+
fs.unlinkSync(destPath);
|
|
180
|
+
reject(err);
|
|
181
|
+
});
|
|
182
|
+
}).on('error', (err) => {
|
|
183
|
+
file.close();
|
|
184
|
+
try { fs.unlinkSync(destPath); } catch {}
|
|
185
|
+
reject(err);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get the npm registry tarball URL for a package
|
|
192
|
+
* Uses the abbreviated metadata endpoint (/{package}/{version}) for efficiency.
|
|
193
|
+
*/
|
|
194
|
+
async function getPackageTarballUrl(packageName, version) {
|
|
195
|
+
return new Promise((resolve, reject) => {
|
|
196
|
+
// Use abbreviated metadata endpoint - returns only the specific version data
|
|
197
|
+
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/${encodeURIComponent(version)}`;
|
|
198
|
+
|
|
199
|
+
https.get(url, (response) => {
|
|
200
|
+
if (response.statusCode !== 200) {
|
|
201
|
+
reject(new Error(`Failed to fetch package info: HTTP ${response.statusCode}`));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let data = '';
|
|
206
|
+
response.on('data', chunk => data += chunk);
|
|
207
|
+
response.on('end', () => {
|
|
208
|
+
try {
|
|
209
|
+
const versionData = JSON.parse(data);
|
|
210
|
+
if (!versionData.dist || !versionData.dist.tarball) {
|
|
211
|
+
reject(new Error(`Tarball URL not found for ${packageName}@${version}`));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
resolve(versionData.dist.tarball);
|
|
215
|
+
} catch (e) {
|
|
216
|
+
reject(new Error(`Failed to parse package info: ${e.message}`));
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}).on('error', reject);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Extract binary from npm tarball using system tar command
|
|
225
|
+
*/
|
|
226
|
+
async function extractBinaryFromTarball(tarballUrl, destPath) {
|
|
227
|
+
const binaryName = process.platform === 'win32' ? 'skim.exe' : 'skim';
|
|
228
|
+
|
|
229
|
+
// Create a temporary directory for extraction
|
|
230
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rskim-'));
|
|
231
|
+
const tarballPath = path.join(tempDir, 'package.tgz');
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
// Download the tarball
|
|
235
|
+
await downloadToFile(tarballUrl, tarballPath);
|
|
236
|
+
|
|
237
|
+
// Extract using system tar command
|
|
238
|
+
// The tarball contains: package/<binaryName>
|
|
239
|
+
await new Promise((resolve, reject) => {
|
|
240
|
+
const tarArgs = ['-xzf', tarballPath, '-C', tempDir];
|
|
241
|
+
const tar = spawn('tar', tarArgs, { stdio: 'inherit' });
|
|
242
|
+
tar.on('close', (code) => {
|
|
243
|
+
if (code === 0) {
|
|
244
|
+
resolve();
|
|
245
|
+
} else {
|
|
246
|
+
reject(new Error(`tar exited with code ${code}`));
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
tar.on('error', reject);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Find the extracted binary (it will be in package/<binaryName>)
|
|
253
|
+
const extractedBinary = path.join(tempDir, 'package', binaryName);
|
|
254
|
+
if (!fs.existsSync(extractedBinary)) {
|
|
255
|
+
throw new Error(`Binary not found in tarball: ${binaryName}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Ensure destination directory exists
|
|
259
|
+
const destDir = path.dirname(destPath);
|
|
260
|
+
if (!fs.existsSync(destDir)) {
|
|
261
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Copy to destination
|
|
265
|
+
fs.copyFileSync(extractedBinary, destPath);
|
|
266
|
+
|
|
267
|
+
// Make executable (Unix only)
|
|
268
|
+
if (process.platform !== 'win32') {
|
|
269
|
+
fs.chmodSync(destPath, 0o755);
|
|
270
|
+
}
|
|
271
|
+
} finally {
|
|
272
|
+
// Cleanup temp directory
|
|
273
|
+
try {
|
|
274
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
275
|
+
} catch {
|
|
276
|
+
// Ignore cleanup errors
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Main postinstall logic
|
|
283
|
+
*/
|
|
284
|
+
async function main() {
|
|
285
|
+
const packageName = getPlatformPackage();
|
|
286
|
+
|
|
287
|
+
if (!packageName) {
|
|
288
|
+
// Unsupported platform, wrapper will show error at runtime
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Check if platform package is already installed
|
|
293
|
+
if (isPlatformPackageInstalled(packageName)) {
|
|
294
|
+
// Already installed via optional dependency
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Check if fallback binary already exists
|
|
299
|
+
if (isFallbackInstalled()) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Read our version from package.json
|
|
304
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
305
|
+
const version = pkgJson.version;
|
|
306
|
+
|
|
307
|
+
console.log(`rskim: Downloading ${packageName}@${version}...`);
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
const tarballUrl = await getPackageTarballUrl(packageName, version);
|
|
311
|
+
const binaryName = process.platform === 'win32' ? 'skim.exe' : 'skim';
|
|
312
|
+
const destPath = path.join(__dirname, '..', 'bin', binaryName);
|
|
313
|
+
|
|
314
|
+
await extractBinaryFromTarball(tarballUrl, destPath);
|
|
315
|
+
console.log('rskim: Binary downloaded successfully');
|
|
316
|
+
} catch (error) {
|
|
317
|
+
// Non-fatal - wrapper will show helpful error at runtime
|
|
318
|
+
console.warn(`rskim: Failed to download binary: ${error.message}`);
|
|
319
|
+
console.warn('rskim: You may need to install via: cargo install rskim');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Run main and handle errors gracefully
|
|
324
|
+
main().catch(error => {
|
|
325
|
+
// Non-fatal - wrapper will handle missing binary
|
|
326
|
+
console.warn(`rskim postinstall: ${error.message}`);
|
|
327
|
+
});
|
package/bin/darwin/arm64/skim
DELETED
|
Binary file
|
package/bin/darwin/x64/skim
DELETED
|
Binary file
|
package/bin/linux/arm64/skim
DELETED
|
Binary file
|
package/bin/linux/x64/skim
DELETED
|
Binary file
|
package/bin/skim.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const { spawnSync } = require('child_process');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
|
|
6
|
-
function getBinaryPath() {
|
|
7
|
-
const platform = process.platform;
|
|
8
|
-
const arch = process.arch;
|
|
9
|
-
|
|
10
|
-
// Define supported platforms and their binary paths
|
|
11
|
-
const supportedPlatforms = {
|
|
12
|
-
'linux-x64': 'bin/linux/x64/skim',
|
|
13
|
-
'linux-arm64': 'bin/linux/arm64/skim',
|
|
14
|
-
'darwin-x64': 'bin/darwin/x64/skim',
|
|
15
|
-
'darwin-arm64': 'bin/darwin/arm64/skim',
|
|
16
|
-
'win32-x64': 'bin/win32/x64/skim.exe',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const platformKey = `${platform}-${arch}`;
|
|
20
|
-
const binaryRelativePath = supportedPlatforms[platformKey];
|
|
21
|
-
|
|
22
|
-
// Check if platform is supported
|
|
23
|
-
if (!binaryRelativePath) {
|
|
24
|
-
console.error(`ERROR: Platform ${platform}-${arch} is not supported by the npm package.`);
|
|
25
|
-
console.error('');
|
|
26
|
-
console.error('Supported platforms:');
|
|
27
|
-
Object.keys(supportedPlatforms).forEach(p => {
|
|
28
|
-
console.error(` - ${p}`);
|
|
29
|
-
});
|
|
30
|
-
console.error('');
|
|
31
|
-
console.error('Workaround: Install via Cargo instead:');
|
|
32
|
-
console.error(' cargo install rskim');
|
|
33
|
-
console.error('');
|
|
34
|
-
console.error('If you believe this platform should be supported, please report:');
|
|
35
|
-
console.error(' https://github.com/dean0x/skim/issues');
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Check if binary exists in package
|
|
40
|
-
const binaryPath = path.join(__dirname, '..', binaryRelativePath);
|
|
41
|
-
if (!fs.existsSync(binaryPath)) {
|
|
42
|
-
console.error('ERROR: Binary missing from npm package');
|
|
43
|
-
console.error(`Expected location: ${binaryPath}`);
|
|
44
|
-
console.error(`Platform: ${platformKey}`);
|
|
45
|
-
console.error('');
|
|
46
|
-
console.error('This is a packaging bug. Please report:');
|
|
47
|
-
console.error(' https://github.com/dean0x/skim/issues');
|
|
48
|
-
console.error('');
|
|
49
|
-
console.error('Workaround: Install via Cargo instead:');
|
|
50
|
-
console.error(' cargo install rskim');
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Quick check if binary is executable (catches glibc/musl issues)
|
|
55
|
-
try {
|
|
56
|
-
const testResult = spawnSync(binaryPath, ['--version'], {
|
|
57
|
-
timeout: 5000,
|
|
58
|
-
stdio: 'pipe'
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
if (testResult.error) {
|
|
62
|
-
console.error('ERROR: Binary exists but cannot be executed');
|
|
63
|
-
console.error(`Reason: ${testResult.error.message}`);
|
|
64
|
-
console.error('');
|
|
65
|
-
|
|
66
|
-
if (testResult.error.code === 'ENOENT' || testResult.error.message.includes('libc')) {
|
|
67
|
-
console.error('This may be a C library incompatibility issue.');
|
|
68
|
-
console.error('Common cause: Alpine Linux (musl) vs glibc binary mismatch');
|
|
69
|
-
console.error('');
|
|
70
|
-
console.error('If you are using Alpine Linux, install via Cargo instead:');
|
|
71
|
-
console.error(' apk add cargo');
|
|
72
|
-
console.error(' cargo install rskim');
|
|
73
|
-
} else {
|
|
74
|
-
console.error('Workaround: Install via Cargo instead:');
|
|
75
|
-
console.error(' cargo install rskim');
|
|
76
|
-
}
|
|
77
|
-
console.error('');
|
|
78
|
-
console.error('For more help, see: https://github.com/dean0x/skim/issues');
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
} catch (e) {
|
|
82
|
-
// Non-fatal, just proceed
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return binaryPath;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const binary = getBinaryPath();
|
|
89
|
-
const result = spawnSync(binary, process.argv.slice(2), {
|
|
90
|
-
stdio: 'inherit',
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
process.exit(result.status || 0);
|
package/bin/win32/x64/skim.exe
DELETED
|
Binary file
|