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.
@@ -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
+ }