mcp-server-wework 0.1.0 → 0.3.0
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/index.js +43 -27
- package/package.json +1 -1
- package/postinstall.js +105 -95
- package/update_check.js +63 -27
package/index.js
CHANGED
|
@@ -3,9 +3,11 @@ const fs = require("fs");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const { spawn } = require("child_process");
|
|
5
5
|
|
|
6
|
+
const { installBinary } = require("./postinstall");
|
|
6
7
|
const { startUpdateCheck } = require("./update_check");
|
|
7
8
|
|
|
8
|
-
const exe =
|
|
9
|
+
const exe =
|
|
10
|
+
process.platform === "win32" ? "mcp-server-wework.exe" : "mcp-server-wework";
|
|
9
11
|
const binPath = path.join(__dirname, exe);
|
|
10
12
|
|
|
11
13
|
const PKG = (() => {
|
|
@@ -16,34 +18,48 @@ const PKG = (() => {
|
|
|
16
18
|
}
|
|
17
19
|
})();
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
21
|
+
async function main() {
|
|
22
|
+
if (!fs.existsSync(binPath)) {
|
|
23
|
+
await installBinary();
|
|
24
|
+
}
|
|
23
25
|
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
26
|
+
if (!fs.existsSync(binPath)) {
|
|
27
|
+
console.error(`Binary not found: ${binPath}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
31
|
+
if (process.platform !== "win32") {
|
|
32
|
+
try {
|
|
33
|
+
fs.chmodSync(binPath, 0o755);
|
|
34
|
+
} catch {}
|
|
35
|
+
}
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
const child = spawn(binPath, process.argv.slice(2), {
|
|
38
|
+
stdio: "inherit",
|
|
39
|
+
windowsHide: true,
|
|
40
|
+
});
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
// Best-effort update notice (cached + short timeout).
|
|
43
|
+
// Aborts on command exit so it never delays shutdown.
|
|
44
|
+
const updateAbort = new AbortController();
|
|
45
|
+
startUpdateCheck({
|
|
46
|
+
installedVersion: PKG && PKG.version,
|
|
47
|
+
signal: updateAbort.signal,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
child.on("exit", (code) => {
|
|
51
|
+
try {
|
|
52
|
+
updateAbort.abort();
|
|
53
|
+
} catch {}
|
|
54
|
+
process.exitCode = code == null ? 1 : code;
|
|
55
|
+
});
|
|
56
|
+
child.on("error", (err) => {
|
|
57
|
+
console.error(err.message);
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
main().catch((err) => {
|
|
63
|
+
console.error(`mcp-server-wework: failed to install binary: ${err.message}`);
|
|
64
|
+
process.exit(1);
|
|
49
65
|
});
|
package/package.json
CHANGED
package/postinstall.js
CHANGED
|
@@ -17,7 +17,12 @@ const PLATFORM_ENV = "MCP_SERVER_WEWORK_PLATFORM";
|
|
|
17
17
|
function httpGet(url, { headers } = {}) {
|
|
18
18
|
return new Promise((resolve, reject) => {
|
|
19
19
|
const req = https.get(url, { headers }, (res) => {
|
|
20
|
-
if (
|
|
20
|
+
if (
|
|
21
|
+
res.statusCode &&
|
|
22
|
+
res.statusCode >= 300 &&
|
|
23
|
+
res.statusCode < 400 &&
|
|
24
|
+
res.headers.location
|
|
25
|
+
) {
|
|
21
26
|
resolve(httpGet(res.headers.location, { headers }));
|
|
22
27
|
return;
|
|
23
28
|
}
|
|
@@ -65,108 +70,113 @@ function parseChecksums(text) {
|
|
|
65
70
|
return map;
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
async function installBinary() {
|
|
74
|
+
const platformRaw = process.env[PLATFORM_ENV] || process.platform;
|
|
75
|
+
const platform = platformRaw === "win32" ? "windows" : platformRaw;
|
|
76
|
+
if (!["darwin", "linux", "windows"].includes(platform)) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"mcp-server-wework: npm install supports macOS (darwin), Linux, and Windows only",
|
|
79
|
+
);
|
|
80
|
+
}
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
const pkg = JSON.parse(
|
|
83
|
+
fs.readFileSync(path.join(__dirname, "package.json"), "utf8"),
|
|
84
|
+
);
|
|
85
|
+
const version = process.env[VERSION_ENV] || pkg.version || "";
|
|
86
|
+
if (!version) {
|
|
87
|
+
throw new Error("postinstall: could not determine version");
|
|
88
|
+
}
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
process.exit(1);
|
|
100
|
-
}
|
|
90
|
+
const detectedArch =
|
|
91
|
+
process.arch === "x64"
|
|
92
|
+
? "amd64"
|
|
93
|
+
: process.arch === "arm64"
|
|
94
|
+
? "arm64"
|
|
95
|
+
: process.arch === "arm"
|
|
96
|
+
? "armv7"
|
|
97
|
+
: process.arch;
|
|
98
|
+
const arch = process.env[ARCH_ENV] || detectedArch;
|
|
99
|
+
if (!["amd64", "arm64", "armv7"].includes(arch)) {
|
|
100
|
+
throw new Error(`Unsupported arch: ${arch}`);
|
|
101
|
+
}
|
|
102
|
+
if (platform === "windows" && arch === "armv7") {
|
|
103
|
+
throw new Error(`Unsupported Windows arch: ${arch}`);
|
|
104
|
+
}
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
const assetName = `${BIN}_${version}_${platform}_${arch}.tar.gz`;
|
|
107
|
+
const baseOverride = process.env[BASE_URL_ENV];
|
|
108
|
+
const bases = baseOverride
|
|
109
|
+
? [baseOverride]
|
|
110
|
+
: [
|
|
111
|
+
`https://github.com/${OWNER}/${REPO}/releases/download/${version}`,
|
|
112
|
+
`https://github.com/${OWNER}/${REPO}/releases/download/v${version}`,
|
|
113
|
+
];
|
|
114
|
+
const headers = { "User-Agent": `${REPO}-postinstall` };
|
|
115
|
+
const outDir = __dirname;
|
|
116
|
+
const exe = platform === "windows" ? `${BIN}.exe` : BIN;
|
|
117
|
+
const binPath = path.join(outDir, exe);
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
let tarGz = null;
|
|
123
|
-
let baseUsed = "";
|
|
124
|
-
let lastErr = null;
|
|
125
|
-
for (const base of bases) {
|
|
126
|
-
const url = `${base}/${assetName}`;
|
|
127
|
-
console.log(`postinstall: downloading ${assetName} from ${url}`);
|
|
128
|
-
try {
|
|
129
|
-
tarGz = await httpGet(url, { headers });
|
|
130
|
-
baseUsed = base;
|
|
131
|
-
break;
|
|
132
|
-
} catch (e) {
|
|
133
|
-
lastErr = e;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (!tarGz) throw lastErr || new Error("failed to download binary");
|
|
119
|
+
if (fs.existsSync(binPath)) {
|
|
120
|
+
try {
|
|
121
|
+
fs.chmodSync(binPath, 0o755);
|
|
122
|
+
} catch {}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
137
125
|
|
|
138
|
-
|
|
126
|
+
let tarGz = null;
|
|
127
|
+
let baseUsed = "";
|
|
128
|
+
let lastErr = null;
|
|
129
|
+
for (const base of bases) {
|
|
130
|
+
const url = `${base}/${assetName}`;
|
|
131
|
+
console.log(`postinstall: downloading ${assetName} from ${url}`);
|
|
139
132
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const sumExpected = checksums.get(assetName);
|
|
144
|
-
if (!sumExpected) throw new Error("asset not in checksums.txt");
|
|
145
|
-
const sumActual = sha256(tarGz);
|
|
146
|
-
if (sumActual.toLowerCase() !== sumExpected.toLowerCase()) throw new Error("checksum mismatch");
|
|
147
|
-
console.log("postinstall: checksum OK");
|
|
133
|
+
tarGz = await httpGet(url, { headers });
|
|
134
|
+
baseUsed = base;
|
|
135
|
+
break;
|
|
148
136
|
} catch (e) {
|
|
149
|
-
|
|
137
|
+
lastErr = e;
|
|
150
138
|
}
|
|
139
|
+
}
|
|
140
|
+
if (!tarGz) throw lastErr || new Error("failed to download binary");
|
|
151
141
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
142
|
+
// checksum (best effort)
|
|
143
|
+
try {
|
|
144
|
+
const checksumsUrl = `${baseUsed}/checksums.txt`;
|
|
145
|
+
const checksumsBuf = await httpGet(checksumsUrl, { headers });
|
|
146
|
+
const checksums = parseChecksums(checksumsBuf.toString("utf8"));
|
|
147
|
+
const sumExpected = checksums.get(assetName);
|
|
148
|
+
if (!sumExpected) throw new Error("asset not in checksums.txt");
|
|
149
|
+
const sumActual = sha256(tarGz);
|
|
150
|
+
if (sumActual.toLowerCase() !== sumExpected.toLowerCase())
|
|
151
|
+
throw new Error("checksum mismatch");
|
|
152
|
+
console.log("postinstall: checksum OK");
|
|
153
|
+
} catch (e) {
|
|
154
|
+
console.warn(`postinstall: checksum skipped/failed: ${e.message}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// extract only the binary into npm directory (archive contains binary at root)
|
|
158
|
+
const tmpFile = path.join(os.tmpdir(), `${REPO}-${Date.now()}.tar.gz`);
|
|
159
|
+
fs.writeFileSync(tmpFile, tarGz);
|
|
160
|
+
const tarRes = spawnSync("tar", ["-xzf", tmpFile, "-C", outDir, exe], {
|
|
161
|
+
stdio: "inherit",
|
|
162
|
+
});
|
|
163
|
+
if (tarRes.status !== 0) {
|
|
164
|
+
throw new Error("postinstall: failed to extract binary");
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
fs.chmodSync(binPath, 0o755);
|
|
168
|
+
} catch {}
|
|
169
|
+
try {
|
|
170
|
+
fs.unlinkSync(tmpFile);
|
|
171
|
+
} catch {}
|
|
172
|
+
console.log(`postinstall: installed ${exe} to ${outDir}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (require.main === module) {
|
|
176
|
+
installBinary().catch((err) => {
|
|
169
177
|
console.error(`postinstall error: ${err.message}`);
|
|
170
178
|
process.exit(1);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = { installBinary };
|
package/update_check.js
CHANGED
|
@@ -29,7 +29,8 @@ function getCacheFile() {
|
|
|
29
29
|
|
|
30
30
|
if (process.platform === "win32") {
|
|
31
31
|
const base = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
32
|
-
if (!base)
|
|
32
|
+
if (!base)
|
|
33
|
+
return path.join(home, "AppData", "Local", CACHE_DIR_NAME, "update.json");
|
|
33
34
|
return path.join(base, CACHE_DIR_NAME, "update.json");
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -64,24 +65,28 @@ function fetchLatestVersion(packageName, timeoutMs, signal) {
|
|
|
64
65
|
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
|
|
65
66
|
|
|
66
67
|
return new Promise((resolve, reject) => {
|
|
67
|
-
const req = https.get(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
res.on("data", (c) => chunks.push(c));
|
|
76
|
-
res.on("end", () => {
|
|
77
|
-
try {
|
|
78
|
-
const json = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
79
|
-
resolve(typeof json.version === "string" ? json.version : "");
|
|
80
|
-
} catch (e) {
|
|
81
|
-
reject(e);
|
|
68
|
+
const req = https.get(
|
|
69
|
+
url,
|
|
70
|
+
{ headers: { "User-Agent": `${DEFAULT_PACKAGE_NAME}-update-check` } },
|
|
71
|
+
(res) => {
|
|
72
|
+
if (res.statusCode !== 200) {
|
|
73
|
+
reject(new Error(`GET ${url} -> ${res.statusCode}`));
|
|
74
|
+
res.resume();
|
|
75
|
+
return;
|
|
82
76
|
}
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
|
|
78
|
+
const chunks = [];
|
|
79
|
+
res.on("data", (c) => chunks.push(c));
|
|
80
|
+
res.on("end", () => {
|
|
81
|
+
try {
|
|
82
|
+
const json = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
83
|
+
resolve(typeof json.version === "string" ? json.version : "");
|
|
84
|
+
} catch (e) {
|
|
85
|
+
reject(e);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
);
|
|
85
90
|
|
|
86
91
|
req.on("error", reject);
|
|
87
92
|
req.setTimeout(timeoutMs, () => req.destroy(new Error("timeout")));
|
|
@@ -91,7 +96,11 @@ function fetchLatestVersion(packageName, timeoutMs, signal) {
|
|
|
91
96
|
req.destroy(new Error("aborted"));
|
|
92
97
|
return;
|
|
93
98
|
}
|
|
94
|
-
signal.addEventListener(
|
|
99
|
+
signal.addEventListener(
|
|
100
|
+
"abort",
|
|
101
|
+
() => req.destroy(new Error("aborted")),
|
|
102
|
+
{ once: true },
|
|
103
|
+
);
|
|
95
104
|
}
|
|
96
105
|
});
|
|
97
106
|
}
|
|
@@ -107,13 +116,22 @@ function normalizeVersion(version) {
|
|
|
107
116
|
function printNotice({ packageName, installed, latest, updateCommand }) {
|
|
108
117
|
const prefix = `${packageName}:`;
|
|
109
118
|
if (installed && latest) {
|
|
110
|
-
console.error(
|
|
119
|
+
console.error(
|
|
120
|
+
`${prefix} an update is available (installed ${installed}, latest ${latest}). Update with: ${updateCommand}`,
|
|
121
|
+
);
|
|
111
122
|
return;
|
|
112
123
|
}
|
|
113
|
-
console.error(
|
|
124
|
+
console.error(
|
|
125
|
+
`${prefix} an update is available. Update with: ${updateCommand}`,
|
|
126
|
+
);
|
|
114
127
|
}
|
|
115
128
|
|
|
116
|
-
async function runUpdateCheck({
|
|
129
|
+
async function runUpdateCheck({
|
|
130
|
+
packageName,
|
|
131
|
+
installedVersion,
|
|
132
|
+
updateCommand,
|
|
133
|
+
signal,
|
|
134
|
+
}) {
|
|
117
135
|
if (isDisabled()) return;
|
|
118
136
|
if (!process.stderr.isTTY) return;
|
|
119
137
|
|
|
@@ -121,8 +139,12 @@ async function runUpdateCheck({ packageName, installedVersion, updateCommand, si
|
|
|
121
139
|
if (!installed) return;
|
|
122
140
|
|
|
123
141
|
const cache = readCache() || {};
|
|
124
|
-
const lastChecked = Number.isFinite(cache.lastChecked)
|
|
125
|
-
|
|
142
|
+
const lastChecked = Number.isFinite(cache.lastChecked)
|
|
143
|
+
? cache.lastChecked
|
|
144
|
+
: 0;
|
|
145
|
+
const lastNotified = Number.isFinite(cache.lastNotified)
|
|
146
|
+
? cache.lastNotified
|
|
147
|
+
: 0;
|
|
126
148
|
const cachedLatest = typeof cache.latest === "string" ? cache.latest : "";
|
|
127
149
|
|
|
128
150
|
const now = Date.now();
|
|
@@ -131,14 +153,23 @@ async function runUpdateCheck({ packageName, installedVersion, updateCommand, si
|
|
|
131
153
|
const latestCached = normalizeVersion(cachedLatest);
|
|
132
154
|
if (latestCached && semver.gt(latestCached, installed)) {
|
|
133
155
|
if (!lastNotified || now - lastNotified >= UPDATE_CHECK_INTERVAL_MS) {
|
|
134
|
-
printNotice({
|
|
156
|
+
printNotice({
|
|
157
|
+
packageName,
|
|
158
|
+
installed,
|
|
159
|
+
latest: latestCached,
|
|
160
|
+
updateCommand,
|
|
161
|
+
});
|
|
135
162
|
writeCache({ ...cache, lastNotified: now });
|
|
136
163
|
}
|
|
137
164
|
}
|
|
138
165
|
return;
|
|
139
166
|
}
|
|
140
167
|
|
|
141
|
-
const latestRaw = await fetchLatestVersion(
|
|
168
|
+
const latestRaw = await fetchLatestVersion(
|
|
169
|
+
packageName,
|
|
170
|
+
UPDATE_CHECK_TIMEOUT_MS,
|
|
171
|
+
signal,
|
|
172
|
+
);
|
|
142
173
|
const latest = normalizeVersion(latestRaw);
|
|
143
174
|
|
|
144
175
|
writeCache({ lastChecked: now, lastNotified, latest: latest || latestRaw });
|
|
@@ -158,7 +189,12 @@ function startUpdateCheck({
|
|
|
158
189
|
signal,
|
|
159
190
|
} = {}) {
|
|
160
191
|
// Fire-and-forget; never block MCP server startup.
|
|
161
|
-
runUpdateCheck({
|
|
192
|
+
runUpdateCheck({
|
|
193
|
+
packageName,
|
|
194
|
+
installedVersion,
|
|
195
|
+
updateCommand,
|
|
196
|
+
signal,
|
|
197
|
+
}).catch(() => {});
|
|
162
198
|
}
|
|
163
199
|
|
|
164
200
|
module.exports = {
|