@backendsystems/nibble 0.1.0 → 0.3.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/bin/install.js CHANGED
@@ -1,124 +1,211 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const os = require('os');
5
- const path = require('path');
6
- const https = require('https');
7
- const { spawnSync } = require('child_process');
8
-
9
- const PROJECT = 'nibble';
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const https = require('https');
7
+ const crypto = require('crypto');
8
+ const { spawnSync } = require('child_process');
9
+
10
+ const PROJECT = 'nibble';
10
11
  const OWNER = 'backendsystems';
11
- const REPO = 'nibble';
12
- const ROOT = path.resolve(__dirname, '..');
13
- const VENDOR_DIR = path.join(ROOT, 'vendor');
14
- const PACKAGE_JSON = require(path.join(ROOT, 'package.json'));
15
-
16
- function mapPlatform() {
17
- const platformMap = {
18
- linux: 'linux',
19
- darwin: 'darwin',
20
- win32: 'windows',
21
- };
22
-
23
- const archMap = {
24
- x64: 'amd64',
25
- arm64: 'arm64',
26
- };
27
-
28
- const osName = platformMap[process.platform];
29
- const arch = archMap[process.arch];
30
- if (!osName || !arch) {
31
- throw new Error(`unsupported platform: ${process.platform}/${process.arch}`);
32
- }
33
-
34
- return { osName, arch };
35
- }
36
-
37
- function download(url, outFile) {
38
- return new Promise((resolve, reject) => {
39
- const req = https.get(url, (res) => {
40
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
41
- res.resume();
42
- return resolve(download(res.headers.location, outFile));
43
- }
44
-
45
- if (res.statusCode !== 200) {
46
- res.resume();
47
- return reject(new Error(`download failed (${res.statusCode}): ${url}`));
48
- }
49
-
50
- const file = fs.createWriteStream(outFile);
51
- res.pipe(file);
52
- file.on('finish', () => file.close(() => resolve()));
53
- file.on('error', reject);
54
- });
55
- req.on('error', reject);
56
- });
57
- }
58
-
59
- function run(cmd, args) {
60
- const result = spawnSync(cmd, args, { stdio: 'inherit' });
61
- if (result.status !== 0) {
62
- throw new Error(`command failed: ${cmd} ${args.join(' ')}`);
63
- }
64
- }
65
-
66
- function installFromArchive(archivePath, osName) {
67
- fs.mkdirSync(VENDOR_DIR, { recursive: true });
68
-
69
- const binName = osName === 'windows' ? `${PROJECT}.exe` : PROJECT;
70
- const destBinary = path.join(VENDOR_DIR, binName);
71
-
72
- if (archivePath.endsWith('.zip')) {
73
- if (process.platform === 'win32') {
74
- run('powershell', [
75
- '-NoProfile',
76
- '-Command',
77
- `Expand-Archive -Path \"${archivePath}\" -DestinationPath \"${VENDOR_DIR}\" -Force`,
78
- ]);
79
- } else {
80
- run('unzip', ['-o', archivePath, '-d', VENDOR_DIR]);
81
- }
82
- } else {
83
- run('tar', ['-xzf', archivePath, '-C', VENDOR_DIR]);
84
- }
85
-
86
- if (!fs.existsSync(destBinary)) {
87
- throw new Error(`binary not found after extraction: ${destBinary}`);
88
- }
89
-
90
- if (process.platform !== 'win32') {
91
- fs.chmodSync(destBinary, 0o755);
92
- }
93
- }
94
-
95
- async function main() {
96
- const { osName, arch } = mapPlatform();
97
- const pkgVersion = PACKAGE_JSON.version.replace(/^v/, '');
98
- const tag = `v${pkgVersion}`;
99
- const assetBase = `${PROJECT}_${osName}_${arch}`;
100
- const ext = 'tar.gz';
101
- const asset = `${assetBase}.${ext}`;
102
- const url = `https://github.com/${OWNER}/${REPO}/releases/download/${tag}/${asset}`;
103
-
104
- const cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nibble-npm-'));
105
- const archivePath = path.join(cacheDir, asset);
106
-
107
- try {
108
- console.log(`Downloading ${asset} from ${url}`);
109
- await download(url, archivePath);
110
- installFromArchive(archivePath, osName);
111
- console.log('Installed nibble binary successfully');
112
- } finally {
113
- if (fs.rmSync) {
114
- fs.rmSync(cacheDir, { recursive: true, force: true });
115
- } else {
116
- fs.rmdirSync(cacheDir, { recursive: true });
117
- }
118
- }
119
- }
120
-
121
- main().catch((err) => {
122
- console.error(`nibble install failed: ${err.message}`);
123
- process.exit(1);
124
- });
12
+ const REPO = 'nibble';
13
+ const ROOT = path.resolve(__dirname, '..');
14
+ const VENDOR_DIR = path.join(ROOT, 'vendor');
15
+ const PACKAGE_JSON = require(path.join(ROOT, 'package.json'));
16
+
17
+ function mapPlatform() {
18
+ const platformMap = {
19
+ linux: 'linux',
20
+ darwin: 'darwin',
21
+ win32: 'windows',
22
+ };
23
+
24
+ const archMap = {
25
+ x64: 'amd64',
26
+ arm64: 'arm64',
27
+ };
28
+
29
+ const osName = platformMap[process.platform];
30
+ const arch = archMap[process.arch];
31
+ if (!osName || !arch) {
32
+ throw new Error(`unsupported platform: ${process.platform}/${process.arch}`);
33
+ }
34
+
35
+ return { osName, arch };
36
+ }
37
+
38
+ function download(url, outFile) {
39
+ return new Promise((resolve, reject) => {
40
+ const req = https.get(url, (res) => {
41
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
42
+ res.resume();
43
+ return resolve(download(res.headers.location, outFile));
44
+ }
45
+
46
+ if (res.statusCode !== 200) {
47
+ res.resume();
48
+ return reject(new Error(`download failed (${res.statusCode}): ${url}`));
49
+ }
50
+
51
+ const file = fs.createWriteStream(outFile);
52
+ res.pipe(file);
53
+ file.on('finish', () => file.close(() => resolve()));
54
+ file.on('error', reject);
55
+ });
56
+ req.on('error', reject);
57
+ });
58
+ }
59
+
60
+ function downloadText(url) {
61
+ return new Promise((resolve, reject) => {
62
+ const req = https.get(url, (res) => {
63
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
64
+ res.resume();
65
+ return resolve(downloadText(res.headers.location));
66
+ }
67
+
68
+ if (res.statusCode !== 200) {
69
+ res.resume();
70
+ return reject(new Error(`download failed (${res.statusCode}): ${url}`));
71
+ }
72
+
73
+ let data = '';
74
+ res.setEncoding('utf8');
75
+ res.on('data', (chunk) => {
76
+ data += chunk;
77
+ });
78
+ res.on('end', () => resolve(data));
79
+ res.on('error', reject);
80
+ });
81
+ req.on('error', reject);
82
+ });
83
+ }
84
+
85
+ function parseChecksums(text) {
86
+ const checksums = new Map();
87
+ for (const line of text.split(/\r?\n/)) {
88
+ const trimmed = line.trim();
89
+ if (!trimmed || trimmed.startsWith('#')) {
90
+ continue;
91
+ }
92
+
93
+ const match = trimmed.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
94
+ if (!match) {
95
+ continue;
96
+ }
97
+ checksums.set(match[2].trim(), match[1].toLowerCase());
98
+ }
99
+ return checksums;
100
+ }
101
+
102
+ function sha256File(filePath) {
103
+ const hash = crypto.createHash('sha256');
104
+ hash.update(fs.readFileSync(filePath));
105
+ return hash.digest('hex');
106
+ }
107
+
108
+ async function verifyArchiveChecksum(urlBase, archivePath, archiveName, version) {
109
+ const checksumCandidates = [
110
+ 'checksums.txt',
111
+ `${PROJECT}_${version}_checksums.txt`,
112
+ ];
113
+
114
+ let checksumFile = '';
115
+ let checksumName = '';
116
+ for (const candidate of checksumCandidates) {
117
+ try {
118
+ checksumFile = await downloadText(`${urlBase}/${candidate}`);
119
+ checksumName = candidate;
120
+ break;
121
+ } catch (err) {
122
+ if (!String(err.message).includes('download failed (404)')) {
123
+ throw err;
124
+ }
125
+ }
126
+ }
127
+
128
+ if (!checksumFile) {
129
+ throw new Error(`no checksum file found (tried: ${checksumCandidates.join(', ')})`);
130
+ }
131
+
132
+ const checksums = parseChecksums(checksumFile);
133
+ const expected = checksums.get(archiveName);
134
+ if (!expected) {
135
+ throw new Error(`checksum for ${archiveName} not found in ${checksumName}`);
136
+ }
137
+
138
+ const actual = sha256File(archivePath);
139
+ if (actual !== expected) {
140
+ throw new Error(`checksum mismatch for ${archiveName}`);
141
+ }
142
+ }
143
+
144
+ function run(cmd, args) {
145
+ const result = spawnSync(cmd, args, { stdio: 'inherit' });
146
+ if (result.status !== 0) {
147
+ throw new Error(`command failed: ${cmd} ${args.join(' ')}`);
148
+ }
149
+ }
150
+
151
+ function installFromArchive(archivePath, osName) {
152
+ fs.mkdirSync(VENDOR_DIR, { recursive: true });
153
+
154
+ const binName = osName === 'windows' ? `${PROJECT}.exe` : PROJECT;
155
+ const destBinary = path.join(VENDOR_DIR, binName);
156
+
157
+ if (archivePath.endsWith('.zip')) {
158
+ if (process.platform === 'win32') {
159
+ run('powershell', [
160
+ '-NoProfile',
161
+ '-Command',
162
+ `Expand-Archive -Path \"${archivePath}\" -DestinationPath \"${VENDOR_DIR}\" -Force`,
163
+ ]);
164
+ } else {
165
+ run('unzip', ['-o', archivePath, '-d', VENDOR_DIR]);
166
+ }
167
+ } else {
168
+ run('tar', ['-xzf', archivePath, '-C', VENDOR_DIR]);
169
+ }
170
+
171
+ if (!fs.existsSync(destBinary)) {
172
+ throw new Error(`binary not found after extraction: ${destBinary}`);
173
+ }
174
+
175
+ if (process.platform !== 'win32') {
176
+ fs.chmodSync(destBinary, 0o755);
177
+ }
178
+ }
179
+
180
+ async function main() {
181
+ const { osName, arch } = mapPlatform();
182
+ const pkgVersion = PACKAGE_JSON.version.replace(/^v/, '');
183
+ const tag = `v${pkgVersion}`;
184
+ const assetBase = `${PROJECT}_${osName}_${arch}`;
185
+ const ext = 'tar.gz';
186
+ const asset = `${assetBase}.${ext}`;
187
+ const urlBase = `https://github.com/${OWNER}/${REPO}/releases/download/${tag}`;
188
+ const url = `${urlBase}/${asset}`;
189
+
190
+ const cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nibble-npm-'));
191
+ const archivePath = path.join(cacheDir, asset);
192
+
193
+ try {
194
+ console.log(`Downloading ${asset} from ${url}`);
195
+ await download(url, archivePath);
196
+ await verifyArchiveChecksum(urlBase, archivePath, asset, pkgVersion);
197
+ installFromArchive(archivePath, osName);
198
+ console.log('Installed nibble binary successfully');
199
+ } finally {
200
+ if (fs.rmSync) {
201
+ fs.rmSync(cacheDir, { recursive: true, force: true });
202
+ } else {
203
+ fs.rmdirSync(cacheDir, { recursive: true });
204
+ }
205
+ }
206
+ }
207
+
208
+ main().catch((err) => {
209
+ console.error(`nibble install failed: ${err.message}`);
210
+ process.exit(1);
211
+ });
package/bin/nibble.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
-
3
- const { spawnSync } = require('child_process');
4
- const path = require('path');
5
-
6
- const binName = process.platform === 'win32' ? 'nibble.exe' : 'nibble';
7
- const binary = path.join(__dirname, '..', 'vendor', binName);
8
-
9
- const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
10
- if (result.error) {
11
- console.error(`Failed to execute nibble binary: ${result.error.message}`);
12
- process.exit(1);
13
- }
14
- process.exit(result.status || 0);
2
+
3
+ const { spawnSync } = require('child_process');
4
+ const path = require('path');
5
+
6
+ const binName = process.platform === 'win32' ? 'nibble.exe' : 'nibble';
7
+ const binary = path.join(__dirname, '..', 'vendor', binName);
8
+
9
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
10
+ if (result.error) {
11
+ console.error(`Failed to execute nibble binary: ${result.error.message}`);
12
+ process.exit(1);
13
+ }
14
+ process.exit(result.status || 0);
package/go-npm.json CHANGED
@@ -1,7 +1,7 @@
1
- {
2
- "bin": "../nibble",
1
+ {
2
+ "bin": "../nibble",
3
3
  "name": "@backendsystems/nibble",
4
- "platforms": ["linux", "darwin", "win32"],
5
- "arch": ["x64", "arm64"],
6
- "version": "0.1.0"
7
- }
4
+ "platforms": ["linux", "darwin", "win32"],
5
+ "arch": ["x64", "arm64"],
6
+ "version": "0.1.0"
7
+ }
package/package.json CHANGED
@@ -1,34 +1,34 @@
1
- {
1
+ {
2
2
  "name": "@backendsystems/nibble",
3
- "version": "0.1.0",
4
- "description": "Fast local network scanner with hardware identification and a terminal UI",
5
- "bin": {
6
- "nibble": "bin/nibble.js"
7
- },
8
- "scripts": {
9
- "postinstall": "node bin/install.js"
10
- },
11
- "repository": {
12
- "type": "git",
3
+ "version": "0.3.1",
4
+ "description": "Fast local network scanner with hardware identification and a terminal UI",
5
+ "bin": {
6
+ "nibble": "bin/nibble.js"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node bin/install.js"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
13
  "url": "https://github.com/backendsystems/nibble"
14
- },
14
+ },
15
15
  "author": "saberd",
16
- "license": "MIT",
17
- "keywords": [
18
- "network",
19
- "scanner",
20
- "arp",
21
- "ports",
22
- "cli",
23
- "tui"
24
- ],
25
- "engines": {
26
- "node": ">=12.0.0"
27
- },
28
- "provenance": true,
29
- "publishConfig": {
30
- "registry": "https://registry.npmjs.org/",
31
- "access": "public",
32
- "provenance": true
33
- }
34
- }
16
+ "license": "MIT",
17
+ "keywords": [
18
+ "network",
19
+ "scanner",
20
+ "arp",
21
+ "ports",
22
+ "cli",
23
+ "tui"
24
+ ],
25
+ "engines": {
26
+ "node": ">=12.0.0"
27
+ },
28
+ "provenance": true,
29
+ "publishConfig": {
30
+ "registry": "https://registry.npmjs.org/",
31
+ "access": "public",
32
+ "provenance": true
33
+ }
34
+ }