htn-tunnel 0.2.0 → 0.2.2

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