htn-tunnel 0.1.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/htn-tunnel.js +56 -0
- package/lib/install.js +135 -0
- package/package.json +32 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Platform detection shim — finds the correct prebuilt binary from
|
|
4
|
+
// @htn-tunnel/<platform> optional dependency and executes it.
|
|
5
|
+
|
|
6
|
+
const { execFileSync } = require("child_process");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const os = require("os");
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
|
|
11
|
+
const PLATFORMS = {
|
|
12
|
+
"darwin:arm64": "@htn-tunnel/darwin-arm64",
|
|
13
|
+
"darwin:x64": "@htn-tunnel/darwin-x64",
|
|
14
|
+
"linux:arm64": "@htn-tunnel/linux-arm64",
|
|
15
|
+
"linux:x64": "@htn-tunnel/linux-x64",
|
|
16
|
+
"win32:arm64": "@htn-tunnel/win32-arm64",
|
|
17
|
+
"win32:x64": "@htn-tunnel/win32-x64",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const platform = os.platform();
|
|
21
|
+
const arch = os.arch();
|
|
22
|
+
const key = `${platform}:${arch}`;
|
|
23
|
+
const pkg = PLATFORMS[key];
|
|
24
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
25
|
+
|
|
26
|
+
if (!pkg) {
|
|
27
|
+
console.error(`htn-tunnel: unsupported platform ${key}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Try 1: resolve from optionalDependencies package
|
|
32
|
+
let binPath;
|
|
33
|
+
try {
|
|
34
|
+
const pkgDir = path.dirname(require.resolve(`${pkg}/package.json`));
|
|
35
|
+
binPath = path.join(pkgDir, "bin", `htn-tunnel${ext}`);
|
|
36
|
+
} catch {
|
|
37
|
+
// Try 2: postinstall may have downloaded binary to our own bin/
|
|
38
|
+
binPath = path.join(__dirname, `htn-tunnel${ext}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!fs.existsSync(binPath)) {
|
|
42
|
+
console.error("htn-tunnel: binary not found. Try reinstalling:");
|
|
43
|
+
console.error(" npm install -g htn-tunnel");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const result = require("child_process").spawnSync(binPath, process.argv.slice(2), {
|
|
49
|
+
stdio: "inherit",
|
|
50
|
+
windowsHide: false,
|
|
51
|
+
});
|
|
52
|
+
process.exit(result.status ?? 1);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.error("htn-tunnel: failed to execute binary:", e.message);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
package/lib/install.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Postinstall fallback — downloads binary from GitHub Releases
|
|
2
|
+
// when optionalDependencies are disabled or unavailable.
|
|
3
|
+
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const os = require("os");
|
|
7
|
+
const https = require("https");
|
|
8
|
+
|
|
9
|
+
const PLATFORMS = {
|
|
10
|
+
"darwin:arm64": "@htn-tunnel/darwin-arm64",
|
|
11
|
+
"darwin:x64": "@htn-tunnel/darwin-x64",
|
|
12
|
+
"linux:arm64": "@htn-tunnel/linux-arm64",
|
|
13
|
+
"linux:x64": "@htn-tunnel/linux-x64",
|
|
14
|
+
"win32:arm64": "@htn-tunnel/win32-arm64",
|
|
15
|
+
"win32:x64": "@htn-tunnel/win32-x64",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const platform = os.platform();
|
|
19
|
+
const arch = os.arch();
|
|
20
|
+
const key = `${platform}:${arch}`;
|
|
21
|
+
const pkg = PLATFORMS[key];
|
|
22
|
+
|
|
23
|
+
// Check if platform package already installed via optionalDependencies
|
|
24
|
+
if (pkg) {
|
|
25
|
+
try {
|
|
26
|
+
require.resolve(`${pkg}/package.json`);
|
|
27
|
+
process.exit(0); // Already installed, nothing to do
|
|
28
|
+
} catch {
|
|
29
|
+
// Not installed — fall through to download
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const version = require("../package.json").version;
|
|
34
|
+
const goArch = arch === "x64" ? "amd64" : arch;
|
|
35
|
+
const goOS = platform === "win32" ? "windows" : platform;
|
|
36
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
37
|
+
const binDir = path.join(__dirname, "..", "bin");
|
|
38
|
+
const binPath = path.join(binDir, `htn-tunnel${ext}`);
|
|
39
|
+
|
|
40
|
+
// Already downloaded
|
|
41
|
+
if (fs.existsSync(binPath) && fs.statSync(binPath).size > 0) {
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const baseUrl = `https://github.com/nhh0718/htn-tunnel/releases/download/v${version}`;
|
|
46
|
+
const archiveName = `htn-tunnel_${version}_${goOS}_${goArch}`;
|
|
47
|
+
const archiveExt = platform === "win32" ? ".zip" : ".tar.gz";
|
|
48
|
+
const url = `${baseUrl}/${archiveName}${archiveExt}`;
|
|
49
|
+
|
|
50
|
+
console.log(`htn-tunnel: downloading v${version} for ${platform}/${arch}...`);
|
|
51
|
+
|
|
52
|
+
function follow(url, cb) {
|
|
53
|
+
https.get(url, (res) => {
|
|
54
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
55
|
+
return follow(res.headers.location, cb);
|
|
56
|
+
}
|
|
57
|
+
cb(res);
|
|
58
|
+
}).on("error", (err) => {
|
|
59
|
+
console.error("htn-tunnel: download failed:", err.message);
|
|
60
|
+
console.error("Install manually: https://github.com/nhh0718/htn-tunnel/releases");
|
|
61
|
+
process.exit(1);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
66
|
+
|
|
67
|
+
follow(url, (res) => {
|
|
68
|
+
if (res.statusCode !== 200) {
|
|
69
|
+
console.error(`htn-tunnel: download failed (HTTP ${res.statusCode})`);
|
|
70
|
+
console.error("Install manually: https://github.com/nhh0718/htn-tunnel/releases");
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const chunks = [];
|
|
75
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
76
|
+
res.on("end", () => {
|
|
77
|
+
const data = Buffer.concat(chunks);
|
|
78
|
+
|
|
79
|
+
if (archiveExt === ".zip") {
|
|
80
|
+
// Simple zip extraction — find the binary entry
|
|
81
|
+
extractZipBinary(data, `htn-tunnel${ext}`, binPath);
|
|
82
|
+
} else {
|
|
83
|
+
// tar.gz extraction
|
|
84
|
+
extractTarGzBinary(data, `htn-tunnel${ext}`, binPath);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fs.chmodSync(binPath, 0o755);
|
|
88
|
+
console.log("htn-tunnel: installed successfully");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
function extractTarGzBinary(gzData, filename, dest) {
|
|
93
|
+
const zlib = require("zlib");
|
|
94
|
+
const tarData = zlib.gunzipSync(gzData);
|
|
95
|
+
|
|
96
|
+
// Simple tar parser — find file entry matching filename
|
|
97
|
+
let offset = 0;
|
|
98
|
+
while (offset < tarData.length) {
|
|
99
|
+
const header = tarData.subarray(offset, offset + 512);
|
|
100
|
+
if (header[0] === 0) break;
|
|
101
|
+
|
|
102
|
+
const name = header.toString("utf8", 0, 100).replace(/\0/g, "");
|
|
103
|
+
const sizeStr = header.toString("utf8", 124, 136).replace(/\0/g, "").trim();
|
|
104
|
+
const size = parseInt(sizeStr, 8) || 0;
|
|
105
|
+
offset += 512;
|
|
106
|
+
|
|
107
|
+
if (name.endsWith(filename)) {
|
|
108
|
+
fs.writeFileSync(dest, tarData.subarray(offset, offset + size));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
offset += Math.ceil(size / 512) * 512;
|
|
112
|
+
}
|
|
113
|
+
throw new Error(`${filename} not found in archive`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function extractZipBinary(zipData, filename, dest) {
|
|
117
|
+
// Simple zip parser — find local file header for the binary
|
|
118
|
+
let offset = 0;
|
|
119
|
+
while (offset < zipData.length - 4) {
|
|
120
|
+
if (zipData.readUInt32LE(offset) !== 0x04034b50) break; // PK\x03\x04
|
|
121
|
+
|
|
122
|
+
const nameLen = zipData.readUInt16LE(offset + 26);
|
|
123
|
+
const extraLen = zipData.readUInt16LE(offset + 28);
|
|
124
|
+
const compSize = zipData.readUInt32LE(offset + 18);
|
|
125
|
+
const name = zipData.toString("utf8", offset + 30, offset + 30 + nameLen);
|
|
126
|
+
|
|
127
|
+
const dataStart = offset + 30 + nameLen + extraLen;
|
|
128
|
+
if (name.endsWith(filename)) {
|
|
129
|
+
fs.writeFileSync(dest, zipData.subarray(dataStart, dataStart + compSize));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
offset = dataStart + compSize;
|
|
133
|
+
}
|
|
134
|
+
throw new Error(`${filename} not found in archive`);
|
|
135
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "htn-tunnel",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Self-hosted tunnel client — expose localhost to the internet",
|
|
5
|
+
"bin": {
|
|
6
|
+
"htn-tunnel": "./bin/htn-tunnel.js"
|
|
7
|
+
},
|
|
8
|
+
"optionalDependencies": {
|
|
9
|
+
"@htn-tunnel/darwin-arm64": "0.1.1",
|
|
10
|
+
"@htn-tunnel/darwin-x64": "0.1.1",
|
|
11
|
+
"@htn-tunnel/linux-arm64": "0.1.1",
|
|
12
|
+
"@htn-tunnel/linux-x64": "0.1.1",
|
|
13
|
+
"@htn-tunnel/win32-arm64": "0.1.1",
|
|
14
|
+
"@htn-tunnel/win32-x64": "0.1.1"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"postinstall": "node lib/install.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"tunnel",
|
|
21
|
+
"ngrok",
|
|
22
|
+
"localhost",
|
|
23
|
+
"expose",
|
|
24
|
+
"dev",
|
|
25
|
+
"htn"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/nhh0718/htn-tunnel"
|
|
31
|
+
}
|
|
32
|
+
}
|