swarmhack-cli 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/README.md +103 -0
- package/bin/swarmhack.js +93 -0
- package/index.js +137 -0
- package/package.json +49 -0
- package/scripts/build.js +110 -0
- package/scripts/pack-local.js +56 -0
- package/scripts/postinstall.js +156 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @prancer/swarmhack
|
|
2
|
+
|
|
3
|
+
Neural swarm-based penetration testing framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @prancer/swarmhack
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @prancer/swarmhack --help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## CLI Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Run SQL injection scan
|
|
21
|
+
swarmhack spawn --agents sqli \
|
|
22
|
+
--target "http://example.com" \
|
|
23
|
+
--customer "your-customer" \
|
|
24
|
+
--token "your-token"
|
|
25
|
+
|
|
26
|
+
# Run multiple agents
|
|
27
|
+
swarmhack spawn --agents sqli,xss,csrf \
|
|
28
|
+
--target "http://example.com" \
|
|
29
|
+
--customer "your-customer" \
|
|
30
|
+
--token "your-token"
|
|
31
|
+
|
|
32
|
+
# List available agents
|
|
33
|
+
swarmhack agents list
|
|
34
|
+
|
|
35
|
+
# Check system health
|
|
36
|
+
swarmhack doctor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Node.js API
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
const swarmhack = require('@prancer/swarmhack');
|
|
43
|
+
|
|
44
|
+
// Run a scan
|
|
45
|
+
const results = await swarmhack.scan({
|
|
46
|
+
target: 'http://example.com',
|
|
47
|
+
agents: ['sqli', 'xss'],
|
|
48
|
+
customer: 'your-customer',
|
|
49
|
+
token: 'your-token',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
console.log(results);
|
|
53
|
+
|
|
54
|
+
// Check version
|
|
55
|
+
const version = await swarmhack.version();
|
|
56
|
+
console.log(version);
|
|
57
|
+
|
|
58
|
+
// Run any command
|
|
59
|
+
const result = await swarmhack.run(['spawn', '--help']);
|
|
60
|
+
console.log(result.stdout);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Supported Platforms
|
|
64
|
+
|
|
65
|
+
| Platform | Architecture |
|
|
66
|
+
|----------|--------------|
|
|
67
|
+
| Linux | x64, arm64 |
|
|
68
|
+
| macOS | x64, arm64 |
|
|
69
|
+
| Windows | x64 |
|
|
70
|
+
|
|
71
|
+
## Docker Alternative
|
|
72
|
+
|
|
73
|
+
If npm installation fails, use Docker:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
docker run --rm \
|
|
77
|
+
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
78
|
+
-v $(pwd)/reports:/app/reports \
|
|
79
|
+
prancer/swarmhack:0.1.0 \
|
|
80
|
+
spawn --agents sqli --target "http://example.com" \
|
|
81
|
+
--customer "your-customer" --token "your-token"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Available Agents
|
|
85
|
+
|
|
86
|
+
| Agent | Description |
|
|
87
|
+
|-------|-------------|
|
|
88
|
+
| `crawler` | Web crawling and discovery |
|
|
89
|
+
| `sqli` | SQL injection detection |
|
|
90
|
+
| `xss` | Cross-site scripting |
|
|
91
|
+
| `csrf` | CSRF vulnerabilities |
|
|
92
|
+
| `idor` | Insecure direct object reference |
|
|
93
|
+
| `auth_bypass` | Authentication bypass |
|
|
94
|
+
| `cmdi` | Command injection |
|
|
95
|
+
|
|
96
|
+
## Requirements
|
|
97
|
+
|
|
98
|
+
- Node.js 16+
|
|
99
|
+
- Prancer Portal account (for `--token` and `--customer`)
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
package/bin/swarmhack.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SwarmHack CLI Wrapper
|
|
5
|
+
* Executes the native binary with all arguments passed through
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
// Determine binary path based on platform
|
|
14
|
+
function getBinaryPath() {
|
|
15
|
+
const platform = os.platform();
|
|
16
|
+
const arch = os.arch();
|
|
17
|
+
|
|
18
|
+
let binaryName = 'swarmhack';
|
|
19
|
+
if (platform === 'win32') {
|
|
20
|
+
binaryName = 'swarmhack.exe';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check multiple locations
|
|
24
|
+
const locations = [
|
|
25
|
+
// Installed in package
|
|
26
|
+
path.join(__dirname, '..', 'native', `${platform}-${arch}`, binaryName),
|
|
27
|
+
// Fallback to generic
|
|
28
|
+
path.join(__dirname, '..', 'native', binaryName),
|
|
29
|
+
// Development: in release folder
|
|
30
|
+
path.join(__dirname, '..', '..', binaryName),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
for (const loc of locations) {
|
|
34
|
+
if (fs.existsSync(loc)) {
|
|
35
|
+
return loc;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Not found - show helpful error
|
|
40
|
+
console.error(`
|
|
41
|
+
SwarmHack binary not found for ${platform}-${arch}
|
|
42
|
+
|
|
43
|
+
This could mean:
|
|
44
|
+
1. The postinstall script failed to download the binary
|
|
45
|
+
2. Your platform is not supported yet
|
|
46
|
+
|
|
47
|
+
Supported platforms:
|
|
48
|
+
- linux-x64
|
|
49
|
+
- linux-arm64
|
|
50
|
+
- darwin-x64 (macOS Intel)
|
|
51
|
+
- darwin-arm64 (macOS Apple Silicon)
|
|
52
|
+
- win32-x64 (Windows)
|
|
53
|
+
|
|
54
|
+
To manually install:
|
|
55
|
+
1. Download binary from: https://github.com/prancer-io/swarmhack/releases
|
|
56
|
+
2. Place in: ${path.join(__dirname, '..', 'native', `${platform}-${arch}`)}
|
|
57
|
+
3. Make executable: chmod +x swarmhack
|
|
58
|
+
|
|
59
|
+
Or use Docker instead:
|
|
60
|
+
docker run --rm prancer/swarmhack:0.1.0 --help
|
|
61
|
+
`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Get binary path
|
|
66
|
+
const binaryPath = getBinaryPath();
|
|
67
|
+
|
|
68
|
+
// Spawn the binary with all arguments
|
|
69
|
+
const args = process.argv.slice(2);
|
|
70
|
+
const child = spawn(binaryPath, args, {
|
|
71
|
+
stdio: 'inherit',
|
|
72
|
+
env: {
|
|
73
|
+
...process.env,
|
|
74
|
+
// Ensure proper terminal handling
|
|
75
|
+
TERM: process.env.TERM || 'xterm-256color',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Forward exit code
|
|
80
|
+
child.on('close', (code) => {
|
|
81
|
+
process.exit(code || 0);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
child.on('error', (err) => {
|
|
85
|
+
if (err.code === 'ENOENT') {
|
|
86
|
+
console.error(`Error: Binary not found at ${binaryPath}`);
|
|
87
|
+
} else if (err.code === 'EACCES') {
|
|
88
|
+
console.error(`Error: Binary not executable. Run: chmod +x ${binaryPath}`);
|
|
89
|
+
} else {
|
|
90
|
+
console.error(`Error executing SwarmHack: ${err.message}`);
|
|
91
|
+
}
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|
package/index.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SwarmHack Node.js API
|
|
3
|
+
* Programmatic interface for running SwarmHack scans
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { spawn } = require('child_process');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get path to SwarmHack binary
|
|
13
|
+
*/
|
|
14
|
+
function getBinaryPath() {
|
|
15
|
+
const platform = os.platform();
|
|
16
|
+
const arch = os.arch();
|
|
17
|
+
const binaryName = platform === 'win32' ? 'swarmhack.exe' : 'swarmhack';
|
|
18
|
+
|
|
19
|
+
const locations = [
|
|
20
|
+
path.join(__dirname, 'native', `${platform}-${arch}`, binaryName),
|
|
21
|
+
path.join(__dirname, 'native', binaryName),
|
|
22
|
+
path.join(__dirname, '..', binaryName),
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
for (const loc of locations) {
|
|
26
|
+
if (fs.existsSync(loc)) {
|
|
27
|
+
return loc;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
throw new Error(`SwarmHack binary not found for ${platform}-${arch}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Run SwarmHack with arguments
|
|
36
|
+
* @param {string[]} args - Command line arguments
|
|
37
|
+
* @param {object} options - Spawn options
|
|
38
|
+
* @returns {Promise<{code: number, stdout: string, stderr: string}>}
|
|
39
|
+
*/
|
|
40
|
+
function run(args = [], options = {}) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const binaryPath = getBinaryPath();
|
|
43
|
+
const stdout = [];
|
|
44
|
+
const stderr = [];
|
|
45
|
+
|
|
46
|
+
const child = spawn(binaryPath, args, {
|
|
47
|
+
...options,
|
|
48
|
+
env: { ...process.env, ...options.env },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
child.stdout?.on('data', (data) => stdout.push(data));
|
|
52
|
+
child.stderr?.on('data', (data) => stderr.push(data));
|
|
53
|
+
|
|
54
|
+
child.on('close', (code) => {
|
|
55
|
+
resolve({
|
|
56
|
+
code: code || 0,
|
|
57
|
+
stdout: Buffer.concat(stdout).toString(),
|
|
58
|
+
stderr: Buffer.concat(stderr).toString(),
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
child.on('error', reject);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Run a security scan
|
|
68
|
+
* @param {object} config - Scan configuration
|
|
69
|
+
* @param {string} config.target - Target URL
|
|
70
|
+
* @param {string[]} config.agents - Agents to run (sqli, xss, etc.)
|
|
71
|
+
* @param {string} config.customer - Prancer customer ID
|
|
72
|
+
* @param {string} config.token - Prancer API token
|
|
73
|
+
* @param {string} config.output - Output file path
|
|
74
|
+
* @returns {Promise<object>} - Scan results
|
|
75
|
+
*/
|
|
76
|
+
async function scan(config) {
|
|
77
|
+
const args = ['spawn'];
|
|
78
|
+
|
|
79
|
+
if (config.target) args.push('--target', config.target);
|
|
80
|
+
if (config.agents) args.push('--agents', config.agents.join(','));
|
|
81
|
+
if (config.customer) args.push('--customer', config.customer);
|
|
82
|
+
if (config.token) args.push('--token', config.token);
|
|
83
|
+
if (config.output) args.push('--output', config.output);
|
|
84
|
+
if (config.timeout) args.push('--timeout', String(config.timeout));
|
|
85
|
+
|
|
86
|
+
args.push('--format', 'json');
|
|
87
|
+
|
|
88
|
+
const result = await run(args);
|
|
89
|
+
|
|
90
|
+
if (result.code !== 0) {
|
|
91
|
+
throw new Error(`Scan failed: ${result.stderr || result.stdout}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Try to parse JSON output
|
|
95
|
+
try {
|
|
96
|
+
return JSON.parse(result.stdout);
|
|
97
|
+
} catch {
|
|
98
|
+
return { raw: result.stdout };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get SwarmHack version
|
|
104
|
+
*/
|
|
105
|
+
async function version() {
|
|
106
|
+
const result = await run(['--version']);
|
|
107
|
+
return result.stdout.trim();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Check if SwarmHack is installed and working
|
|
112
|
+
*/
|
|
113
|
+
async function doctor() {
|
|
114
|
+
try {
|
|
115
|
+
const binaryPath = getBinaryPath();
|
|
116
|
+
const result = await run(['doctor']);
|
|
117
|
+
return {
|
|
118
|
+
installed: true,
|
|
119
|
+
binaryPath,
|
|
120
|
+
healthy: result.code === 0,
|
|
121
|
+
output: result.stdout,
|
|
122
|
+
};
|
|
123
|
+
} catch (err) {
|
|
124
|
+
return {
|
|
125
|
+
installed: false,
|
|
126
|
+
error: err.message,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = {
|
|
132
|
+
run,
|
|
133
|
+
scan,
|
|
134
|
+
version,
|
|
135
|
+
doctor,
|
|
136
|
+
getBinaryPath,
|
|
137
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "swarmhack-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SwarmHack - Neural swarm-based penetration testing framework",
|
|
5
|
+
"author": "Prancer <support@prancer.io>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/prancer-io/swarmhack"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://prancer.io",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"security",
|
|
14
|
+
"pentesting",
|
|
15
|
+
"vulnerability-scanner",
|
|
16
|
+
"sql-injection",
|
|
17
|
+
"xss",
|
|
18
|
+
"web-security",
|
|
19
|
+
"ocsf"
|
|
20
|
+
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"swarmhack": "./bin/swarmhack.js"
|
|
23
|
+
},
|
|
24
|
+
"main": "index.js",
|
|
25
|
+
"files": [
|
|
26
|
+
"bin/",
|
|
27
|
+
"scripts/",
|
|
28
|
+
"index.js",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"postinstall": "node scripts/postinstall.js",
|
|
33
|
+
"test": "node bin/swarmhack.js --version"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=16.0.0"
|
|
37
|
+
},
|
|
38
|
+
"os": [
|
|
39
|
+
"linux",
|
|
40
|
+
"darwin",
|
|
41
|
+
"win32"
|
|
42
|
+
],
|
|
43
|
+
"cpu": [
|
|
44
|
+
"x64",
|
|
45
|
+
"arm64"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {},
|
|
48
|
+
"devDependencies": {}
|
|
49
|
+
}
|
package/scripts/build.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SwarmHack npm Package Build Script
|
|
5
|
+
* Builds binaries for all platforms and packages for npm
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const VERSION = require('../package.json').version;
|
|
13
|
+
const ROOT = path.join(__dirname, '..');
|
|
14
|
+
const PROJECT_ROOT = path.join(ROOT, '..', '..');
|
|
15
|
+
|
|
16
|
+
// Build targets
|
|
17
|
+
const TARGETS = [
|
|
18
|
+
{ platform: 'linux', arch: 'x64', rust_target: 'x86_64-unknown-linux-gnu' },
|
|
19
|
+
{ platform: 'linux', arch: 'arm64', rust_target: 'aarch64-unknown-linux-gnu' },
|
|
20
|
+
{ platform: 'darwin', arch: 'x64', rust_target: 'x86_64-apple-darwin' },
|
|
21
|
+
{ platform: 'darwin', arch: 'arm64', rust_target: 'aarch64-apple-darwin' },
|
|
22
|
+
{ platform: 'win32', arch: 'x64', rust_target: 'x86_64-pc-windows-msvc' },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
function exec(cmd, options = {}) {
|
|
26
|
+
console.log(`$ ${cmd}`);
|
|
27
|
+
try {
|
|
28
|
+
return execSync(cmd, { stdio: 'inherit', cwd: PROJECT_ROOT, ...options });
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error(`Command failed: ${cmd}`);
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildTarget(target) {
|
|
36
|
+
console.log(`\n=== Building ${target.platform}-${target.arch} ===\n`);
|
|
37
|
+
|
|
38
|
+
const nativeDir = path.join(ROOT, 'native', `${target.platform}-${target.arch}`);
|
|
39
|
+
fs.mkdirSync(nativeDir, { recursive: true });
|
|
40
|
+
|
|
41
|
+
const binaryName = target.platform === 'win32' ? 'swarmhack.exe' : 'swarmhack';
|
|
42
|
+
const binaryPath = path.join(nativeDir, binaryName);
|
|
43
|
+
|
|
44
|
+
// Build with cross-compilation
|
|
45
|
+
try {
|
|
46
|
+
exec(`cross build --release --target ${target.rust_target}`);
|
|
47
|
+
|
|
48
|
+
const srcBinary = path.join(
|
|
49
|
+
PROJECT_ROOT,
|
|
50
|
+
'target',
|
|
51
|
+
target.rust_target,
|
|
52
|
+
'release',
|
|
53
|
+
binaryName
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
fs.copyFileSync(srcBinary, binaryPath);
|
|
57
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
58
|
+
console.log(`✓ Built: ${binaryPath}`);
|
|
59
|
+
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.warn(`⚠ Skipping ${target.rust_target}: ${err.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildLocal() {
|
|
66
|
+
console.log('\n=== Building for current platform ===\n');
|
|
67
|
+
|
|
68
|
+
const platform = process.platform;
|
|
69
|
+
const arch = process.arch;
|
|
70
|
+
|
|
71
|
+
const nativeDir = path.join(ROOT, 'native', `${platform}-${arch}`);
|
|
72
|
+
fs.mkdirSync(nativeDir, { recursive: true });
|
|
73
|
+
|
|
74
|
+
const binaryName = platform === 'win32' ? 'swarmhack.exe' : 'swarmhack';
|
|
75
|
+
|
|
76
|
+
exec('cargo build --release');
|
|
77
|
+
|
|
78
|
+
const srcBinary = path.join(PROJECT_ROOT, 'target', 'release', binaryName);
|
|
79
|
+
const destBinary = path.join(nativeDir, binaryName);
|
|
80
|
+
|
|
81
|
+
fs.copyFileSync(srcBinary, destBinary);
|
|
82
|
+
fs.chmodSync(destBinary, 0o755);
|
|
83
|
+
|
|
84
|
+
console.log(`✓ Built: ${destBinary}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function main() {
|
|
88
|
+
const args = process.argv.slice(2);
|
|
89
|
+
|
|
90
|
+
if (args.includes('--all')) {
|
|
91
|
+
// Build all platforms (requires cross)
|
|
92
|
+
console.log('Building all platforms...');
|
|
93
|
+
for (const target of TARGETS) {
|
|
94
|
+
try {
|
|
95
|
+
buildTarget(target);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error(`Failed to build ${target.platform}-${target.arch}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
// Build for current platform only
|
|
102
|
+
buildLocal();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log(`\n✓ Build complete! Version: ${VERSION}\n`);
|
|
106
|
+
console.log('To publish:');
|
|
107
|
+
console.log(' cd npm && npm publish --access public\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pack local binary for npm
|
|
5
|
+
* Use this when you already have a built binary
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
const ROOT = path.join(__dirname, '..');
|
|
13
|
+
const RELEASE_DIR = path.join(ROOT, '..');
|
|
14
|
+
|
|
15
|
+
function main() {
|
|
16
|
+
const platform = os.platform();
|
|
17
|
+
const arch = os.arch();
|
|
18
|
+
const binaryName = platform === 'win32' ? 'swarmhack.exe' : 'swarmhack';
|
|
19
|
+
|
|
20
|
+
// Source: release/swarmhack
|
|
21
|
+
const srcBinary = path.join(RELEASE_DIR, 'swarmhack');
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(srcBinary)) {
|
|
24
|
+
console.error(`Binary not found: ${srcBinary}`);
|
|
25
|
+
console.error('Build first with: cargo build --release && cp target/release/swarmhack release/');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Destination: npm/native/<platform>-<arch>/swarmhack
|
|
30
|
+
const nativeDir = path.join(ROOT, 'native', `${platform}-${arch}`);
|
|
31
|
+
fs.mkdirSync(nativeDir, { recursive: true });
|
|
32
|
+
|
|
33
|
+
const destBinary = path.join(nativeDir, binaryName);
|
|
34
|
+
fs.copyFileSync(srcBinary, destBinary);
|
|
35
|
+
fs.chmodSync(destBinary, 0o755);
|
|
36
|
+
|
|
37
|
+
console.log(`✓ Packed binary for ${platform}-${arch}`);
|
|
38
|
+
console.log(` Source: ${srcBinary}`);
|
|
39
|
+
console.log(` Dest: ${destBinary}`);
|
|
40
|
+
console.log();
|
|
41
|
+
console.log('Package structure:');
|
|
42
|
+
console.log(' npm/');
|
|
43
|
+
console.log(' ├── package.json');
|
|
44
|
+
console.log(' ├── bin/swarmhack.js');
|
|
45
|
+
console.log(' ├── index.js');
|
|
46
|
+
console.log(` └── native/${platform}-${arch}/${binaryName}`);
|
|
47
|
+
console.log();
|
|
48
|
+
console.log('To test locally:');
|
|
49
|
+
console.log(' cd npm && npm link');
|
|
50
|
+
console.log(' swarmhack --version');
|
|
51
|
+
console.log();
|
|
52
|
+
console.log('To publish:');
|
|
53
|
+
console.log(' cd npm && npm publish --access public');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main();
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SwarmHack Postinstall Script
|
|
5
|
+
* Downloads the appropriate binary for the current platform
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const https = require('https');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const { execSync } = require('child_process');
|
|
13
|
+
|
|
14
|
+
const VERSION = '0.1.0';
|
|
15
|
+
const GITHUB_REPO = 'prancer-io/swarmhack';
|
|
16
|
+
const BASE_URL = `https://github.com/${GITHUB_REPO}/releases/download/v${VERSION}`;
|
|
17
|
+
|
|
18
|
+
// Platform mapping
|
|
19
|
+
const PLATFORM_MAP = {
|
|
20
|
+
'linux-x64': 'swarmhack-linux-x64',
|
|
21
|
+
'linux-arm64': 'swarmhack-linux-arm64',
|
|
22
|
+
'darwin-x64': 'swarmhack-darwin-x64',
|
|
23
|
+
'darwin-arm64': 'swarmhack-darwin-arm64',
|
|
24
|
+
'win32-x64': 'swarmhack-windows-x64.exe',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function getPlatformKey() {
|
|
28
|
+
const platform = os.platform();
|
|
29
|
+
const arch = os.arch();
|
|
30
|
+
return `${platform}-${arch}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getBinaryName() {
|
|
34
|
+
const platform = os.platform();
|
|
35
|
+
return platform === 'win32' ? 'swarmhack.exe' : 'swarmhack';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function downloadFile(url, dest) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const file = fs.createWriteStream(dest);
|
|
41
|
+
|
|
42
|
+
const request = https.get(url, (response) => {
|
|
43
|
+
// Handle redirects (GitHub releases use redirects)
|
|
44
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
45
|
+
file.close();
|
|
46
|
+
fs.unlinkSync(dest);
|
|
47
|
+
return downloadFile(response.headers.location, dest).then(resolve).catch(reject);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (response.statusCode !== 200) {
|
|
51
|
+
file.close();
|
|
52
|
+
fs.unlinkSync(dest);
|
|
53
|
+
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
response.pipe(file);
|
|
58
|
+
file.on('finish', () => {
|
|
59
|
+
file.close();
|
|
60
|
+
resolve();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
request.on('error', (err) => {
|
|
65
|
+
file.close();
|
|
66
|
+
fs.unlinkSync(dest);
|
|
67
|
+
reject(err);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
request.setTimeout(60000, () => {
|
|
71
|
+
request.destroy();
|
|
72
|
+
reject(new Error('Download timeout'));
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function main() {
|
|
78
|
+
const platformKey = getPlatformKey();
|
|
79
|
+
const remoteBinary = PLATFORM_MAP[platformKey];
|
|
80
|
+
|
|
81
|
+
if (!remoteBinary) {
|
|
82
|
+
console.log(`
|
|
83
|
+
╔════════════════════════════════════════════════════════════╗
|
|
84
|
+
║ SwarmHack: Platform not supported ║
|
|
85
|
+
╠════════════════════════════════════════════════════════════╣
|
|
86
|
+
║ Your platform: ${platformKey.padEnd(40)}║
|
|
87
|
+
║ ║
|
|
88
|
+
║ Supported platforms: ║
|
|
89
|
+
║ - linux-x64, linux-arm64 ║
|
|
90
|
+
║ - darwin-x64, darwin-arm64 ║
|
|
91
|
+
║ - win32-x64 ║
|
|
92
|
+
║ ║
|
|
93
|
+
║ Alternative: Use Docker ║
|
|
94
|
+
║ docker run prancer/swarmhack:${VERSION} --help ║
|
|
95
|
+
╚════════════════════════════════════════════════════════════╝
|
|
96
|
+
`);
|
|
97
|
+
// Don't fail install - just warn
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const nativeDir = path.join(__dirname, '..', 'native', platformKey);
|
|
102
|
+
const binaryPath = path.join(nativeDir, getBinaryName());
|
|
103
|
+
|
|
104
|
+
// Skip if already exists
|
|
105
|
+
if (fs.existsSync(binaryPath)) {
|
|
106
|
+
console.log(`SwarmHack binary already exists: ${binaryPath}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Create directory
|
|
111
|
+
fs.mkdirSync(nativeDir, { recursive: true });
|
|
112
|
+
|
|
113
|
+
const url = `${BASE_URL}/${remoteBinary}`;
|
|
114
|
+
|
|
115
|
+
console.log(`
|
|
116
|
+
╔════════════════════════════════════════════════════════════╗
|
|
117
|
+
║ SwarmHack: Downloading binary... ║
|
|
118
|
+
╠════════════════════════════════════════════════════════════╣
|
|
119
|
+
║ Version: ${VERSION.padEnd(48)}║
|
|
120
|
+
║ Platform: ${platformKey.padEnd(47)}║
|
|
121
|
+
╚════════════════════════════════════════════════════════════╝
|
|
122
|
+
`);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
await downloadFile(url, binaryPath);
|
|
126
|
+
|
|
127
|
+
// Make executable on Unix
|
|
128
|
+
if (os.platform() !== 'win32') {
|
|
129
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log(`✓ SwarmHack installed successfully!`);
|
|
133
|
+
console.log(` Binary: ${binaryPath}`);
|
|
134
|
+
console.log(`\n Run: npx swarmhack --help\n`);
|
|
135
|
+
|
|
136
|
+
} catch (err) {
|
|
137
|
+
console.error(`
|
|
138
|
+
╔════════════════════════════════════════════════════════════╗
|
|
139
|
+
║ SwarmHack: Download failed ║
|
|
140
|
+
╠════════════════════════════════════════════════════════════╣
|
|
141
|
+
║ ${err.message.substring(0, 56).padEnd(56)}║
|
|
142
|
+
║ ║
|
|
143
|
+
║ Manual installation: ║
|
|
144
|
+
║ 1. Download from GitHub releases ║
|
|
145
|
+
║ 2. Place binary in: native/${platformKey}/ ║
|
|
146
|
+
║ 3. Make executable: chmod +x swarmhack ║
|
|
147
|
+
║ ║
|
|
148
|
+
║ Or use Docker: ║
|
|
149
|
+
║ docker run prancer/swarmhack:${VERSION} --help ║
|
|
150
|
+
╚════════════════════════════════════════════════════════════╝
|
|
151
|
+
`);
|
|
152
|
+
// Don't fail the install
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
main().catch(console.error);
|