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