@rtorr/nah 1.0.7

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.
Files changed (3) hide show
  1. package/bin/nah +28 -0
  2. package/package.json +45 -0
  3. package/scripts/install.js +176 -0
package/bin/nah ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("child_process");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+
7
+ const binaryName = process.platform === "win32" ? "nah.exe" : "nah";
8
+ const binaryPath = path.join(__dirname, binaryName);
9
+
10
+ if (!fs.existsSync(binaryPath)) {
11
+ console.error("nah binary not found. Try reinstalling the package:");
12
+ console.error(" npm uninstall -g nah-cli && npm install -g nah-cli");
13
+ process.exit(1);
14
+ }
15
+
16
+ const child = spawn(binaryPath, process.argv.slice(2), {
17
+ stdio: "inherit",
18
+ env: process.env,
19
+ });
20
+
21
+ child.on("error", (err) => {
22
+ console.error(`Failed to execute nah: ${err.message}`);
23
+ process.exit(1);
24
+ });
25
+
26
+ child.on("close", (code) => {
27
+ process.exit(code ?? 0);
28
+ });
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@rtorr/nah",
3
+ "version": "1.0.7",
4
+ "description": "Native Application Host - Manage native applications, NAKs, profiles, and launch contracts",
5
+ "keywords": [
6
+ "native",
7
+ "application",
8
+ "host",
9
+ "cli",
10
+ "nak",
11
+ "sdk",
12
+ "runtime"
13
+ ],
14
+ "author": "rtorr",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/rtorr/nah.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/rtorr/nah/issues"
22
+ },
23
+ "homepage": "https://github.com/rtorr/nah#readme",
24
+ "bin": {
25
+ "nah": "bin/nah"
26
+ },
27
+ "scripts": {
28
+ "postinstall": "node scripts/install.js"
29
+ },
30
+ "engines": {
31
+ "node": ">=14"
32
+ },
33
+ "files": [
34
+ "bin",
35
+ "scripts"
36
+ ],
37
+ "os": [
38
+ "darwin",
39
+ "linux"
40
+ ],
41
+ "cpu": [
42
+ "x64",
43
+ "arm64"
44
+ ]
45
+ }
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+
3
+ const https = require("https");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { execSync } = require("child_process");
7
+ const zlib = require("zlib");
8
+
9
+ const PACKAGE_VERSION = require("../package.json").version;
10
+ const GITHUB_REPO = "rtorr/nah";
11
+
12
+ const PLATFORM_MAP = {
13
+ "darwin-x64": "darwin-x64",
14
+ "darwin-arm64": "darwin-arm64",
15
+ "linux-x64": "linux-x64",
16
+ "linux-arm64": "linux-arm64",
17
+ };
18
+
19
+ function getPlatformKey() {
20
+ const platform = process.platform;
21
+ const arch = process.arch;
22
+ return `${platform}-${arch}`;
23
+ }
24
+
25
+ function getBinaryName() {
26
+ return process.platform === "win32" ? "nah.exe" : "nah";
27
+ }
28
+
29
+ function getDownloadUrl(platformKey) {
30
+ const binaryName = getBinaryName();
31
+ return `https://github.com/${GITHUB_REPO}/releases/download/v${PACKAGE_VERSION}/nah-${platformKey}.tar.gz`;
32
+ }
33
+
34
+ function downloadFile(url) {
35
+ return new Promise((resolve, reject) => {
36
+ const follow = (url, redirects = 0) => {
37
+ if (redirects > 5) {
38
+ reject(new Error("Too many redirects"));
39
+ return;
40
+ }
41
+
42
+ https
43
+ .get(url, (response) => {
44
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
45
+ follow(response.headers.location, redirects + 1);
46
+ return;
47
+ }
48
+
49
+ if (response.statusCode !== 200) {
50
+ reject(new Error(`Failed to download: HTTP ${response.statusCode}`));
51
+ return;
52
+ }
53
+
54
+ const chunks = [];
55
+ response.on("data", (chunk) => chunks.push(chunk));
56
+ response.on("end", () => resolve(Buffer.concat(chunks)));
57
+ response.on("error", reject);
58
+ })
59
+ .on("error", reject);
60
+ };
61
+
62
+ follow(url);
63
+ });
64
+ }
65
+
66
+ function extractTarGz(buffer, destDir) {
67
+ return new Promise((resolve, reject) => {
68
+ const gunzip = zlib.createGunzip();
69
+ const chunks = [];
70
+
71
+ gunzip.on("data", (chunk) => chunks.push(chunk));
72
+ gunzip.on("end", () => {
73
+ const tarData = Buffer.concat(chunks);
74
+ extractTar(tarData, destDir);
75
+ resolve();
76
+ });
77
+ gunzip.on("error", reject);
78
+
79
+ gunzip.end(buffer);
80
+ });
81
+ }
82
+
83
+ function extractTar(tarData, destDir) {
84
+ // Simple tar extraction - handles standard POSIX tar format
85
+ let offset = 0;
86
+
87
+ while (offset < tarData.length) {
88
+ // Read header (512 bytes)
89
+ const header = tarData.slice(offset, offset + 512);
90
+ offset += 512;
91
+
92
+ // Check for end of archive (two zero blocks)
93
+ if (header.every((b) => b === 0)) {
94
+ break;
95
+ }
96
+
97
+ // Parse header
98
+ const name = header.slice(0, 100).toString("utf8").replace(/\0/g, "").trim();
99
+ const sizeOctal = header.slice(124, 136).toString("utf8").replace(/\0/g, "").trim();
100
+ const size = parseInt(sizeOctal, 8) || 0;
101
+ const typeFlag = header[156];
102
+
103
+ if (!name) {
104
+ break;
105
+ }
106
+
107
+ // Calculate padded size (512-byte blocks)
108
+ const paddedSize = Math.ceil(size / 512) * 512;
109
+
110
+ // Type: '0' or '\0' = regular file, '5' = directory
111
+ if (typeFlag === 48 || typeFlag === 0) {
112
+ // Regular file
113
+ const filePath = path.join(destDir, path.basename(name));
114
+ const fileData = tarData.slice(offset, offset + size);
115
+ fs.writeFileSync(filePath, fileData);
116
+
117
+ // Make executable if it's the nah binary
118
+ if (path.basename(name) === "nah") {
119
+ fs.chmodSync(filePath, 0o755);
120
+ }
121
+ }
122
+
123
+ offset += paddedSize;
124
+ }
125
+ }
126
+
127
+ async function main() {
128
+ const platformKey = getPlatformKey();
129
+ const supportedPlatform = PLATFORM_MAP[platformKey];
130
+
131
+ if (!supportedPlatform) {
132
+ console.error(`Unsupported platform: ${platformKey}`);
133
+ console.error(`Supported platforms: ${Object.keys(PLATFORM_MAP).join(", ")}`);
134
+ process.exit(1);
135
+ }
136
+
137
+ const binDir = path.join(__dirname, "..", "bin");
138
+ const binaryPath = path.join(binDir, getBinaryName());
139
+
140
+ // Check if binary already exists
141
+ if (fs.existsSync(binaryPath)) {
142
+ console.log("nah binary already installed");
143
+ return;
144
+ }
145
+
146
+ const url = getDownloadUrl(supportedPlatform);
147
+ console.log(`Downloading nah v${PACKAGE_VERSION} for ${platformKey}...`);
148
+
149
+ try {
150
+ const buffer = await downloadFile(url);
151
+ console.log("Extracting...");
152
+
153
+ // Ensure bin directory exists
154
+ if (!fs.existsSync(binDir)) {
155
+ fs.mkdirSync(binDir, { recursive: true });
156
+ }
157
+
158
+ await extractTarGz(buffer, binDir);
159
+
160
+ // Verify the binary exists
161
+ if (!fs.existsSync(binaryPath)) {
162
+ throw new Error("Binary not found after extraction");
163
+ }
164
+
165
+ console.log(`nah v${PACKAGE_VERSION} installed successfully`);
166
+ } catch (error) {
167
+ console.error(`Failed to install nah: ${error.message}`);
168
+ console.error("");
169
+ console.error("You can try installing manually:");
170
+ console.error(` 1. Download from: ${url}`);
171
+ console.error(` 2. Extract to: ${binDir}`);
172
+ process.exit(1);
173
+ }
174
+ }
175
+
176
+ main();