-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathpostinstall.js
More file actions
160 lines (140 loc) · 4.62 KB
/
postinstall.js
File metadata and controls
160 lines (140 loc) · 4.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env node
// postinstall.js downloads the pre-built lightning-mcp-server binary for the
// current platform and architecture from GitHub Releases. This follows the
// same pattern used by esbuild, turbo, and other Go/Rust projects distributed
// via npm.
//
// The binary is placed in bin/ so the npm "bin" entry in package.json can
// reference it directly, making `npx lightning-mcp-server` work out of the
// box.
const https = require("https");
const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const os = require("os");
// PLATFORM_MAP translates Node.js platform/arch identifiers to the Go build
// system naming convention used by the release workflow.
const PLATFORM_MAP = {
"darwin-arm64": "darwin-arm64",
"darwin-x64": "darwin-amd64",
"linux-arm64": "linux-arm64",
"linux-x64": "linux-amd64",
"win32-x64": "windows-amd64",
};
// REPO is the GitHub repository that hosts the release binaries.
const REPO = "lightninglabs/lightning-agent-tools";
// BINARY_NAME is the name of the binary inside the release tarball.
const BINARY_NAME = "lightning-mcp-server";
// getPackageVersion reads the version from package.json to determine which
// GitHub release tag to download from.
function getPackageVersion() {
const pkg = JSON.parse(
fs.readFileSync(path.join(__dirname, "package.json"), "utf8"),
);
return pkg.version;
}
// getPlatformKey returns the platform-architecture key for the current
// system, e.g. "darwin-arm64" or "linux-x64".
function getPlatformKey() {
const platform = os.platform();
const arch = os.arch();
return `${platform}-${arch}`;
}
// getBinaryPath returns the file path where the downloaded binary will be
// placed within the package's bin directory.
function getBinaryPath() {
const ext = os.platform() === "win32" ? ".exe" : "";
return path.join(__dirname, "bin", `${BINARY_NAME}${ext}`);
}
// fetchBuffer follows HTTP redirects (GitHub Releases redirects to a CDN)
// and returns the final response body as a buffer.
function fetchBuffer(url) {
return new Promise((resolve, reject) => {
const opts = { headers: { "User-Agent": "npm-postinstall" } };
https.get(url, opts, (res) => {
if (res.statusCode >= 300 && res.statusCode < 400 &&
res.headers.location) {
fetchBuffer(res.headers.location)
.then(resolve, reject);
return;
}
if (res.statusCode !== 200) {
reject(new Error(
`Download failed: HTTP ${res.statusCode} ` +
`from ${url}`,
));
return;
}
const chunks = [];
res.on("data", (chunk) => chunks.push(chunk));
res.on("end", () => resolve(Buffer.concat(chunks)));
res.on("error", reject);
}).on("error", reject);
});
}
// extractTarGz extracts a .tar.gz buffer to the specified directory using
// the system tar command. The binary is expected at the root of the archive.
function extractTarGz(buffer, destDir) {
const tmpFile = path.join(
os.tmpdir(), `${BINARY_NAME}-${Date.now()}.tar.gz`,
);
fs.writeFileSync(tmpFile, buffer);
try {
execSync(`tar xzf "${tmpFile}" -C "${destDir}"`, {
stdio: "pipe",
});
} finally {
fs.unlinkSync(tmpFile);
}
}
// main downloads and installs the correct binary for the current platform.
async function main() {
const platformKey = getPlatformKey();
const goTarget = PLATFORM_MAP[platformKey];
if (!goTarget) {
console.error(
`Unsupported platform: ${platformKey}. ` +
`Supported: ${Object.keys(PLATFORM_MAP).join(", ")}`,
);
console.error(
"You can build from source instead:\n" +
" cd mcp-server && make build",
);
process.exit(1);
}
const version = getPackageVersion();
const tag = `v${version}`;
const assetName = `${BINARY_NAME}-${goTarget}.tar.gz`;
const url =
`https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
console.log(
`Downloading ${BINARY_NAME} ${tag} for ${goTarget}...`,
);
try {
const buffer = await fetchBuffer(url);
// Ensure the bin directory exists before extracting.
const binDir = path.join(__dirname, "bin");
fs.mkdirSync(binDir, { recursive: true });
extractTarGz(buffer, binDir);
// Make the binary executable on Unix systems.
const binaryPath = getBinaryPath();
if (os.platform() !== "win32") {
fs.chmodSync(binaryPath, 0o755);
}
console.log(`Installed ${BINARY_NAME} to ${binaryPath}`);
} catch (err) {
console.error(
`Failed to download ${BINARY_NAME}: ${err.message}`,
);
console.error("");
console.error("You can build from source instead:");
console.error(" cd mcp-server && make build");
console.error("");
console.error(
"Or download from: " +
`https://github.com/${REPO}/releases/tag/${tag}`,
);
process.exit(1);
}
}
main();