@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.
- package/bin/nah +28 -0
- package/package.json +45 -0
- 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();
|