trace-mcp 1.15.2 → 1.16.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/dist/cli.js +45 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.js +32 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/scripts/postinstall-app.mjs +107 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trace-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.1",
|
|
4
4
|
"mcpName": "io.github.nikolai-vysotskyi/trace-mcp",
|
|
5
5
|
"description": "Framework-aware code intelligence MCP server — 48+ frameworks, 68 languages",
|
|
6
6
|
"type": "module",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"files": [
|
|
42
42
|
"dist",
|
|
43
43
|
"hooks",
|
|
44
|
+
"scripts",
|
|
44
45
|
"LICENSE",
|
|
45
46
|
"README.md"
|
|
46
47
|
],
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* postinstall hook for `npm install -g trace-mcp`.
|
|
4
|
+
* If the Electron menu bar app is already installed in ~/Applications/,
|
|
5
|
+
* re-download the latest release zip and replace it — so `npm update -g`
|
|
6
|
+
* automatically keeps the GUI app in sync.
|
|
7
|
+
*
|
|
8
|
+
* Runs silently — never fails the install (all errors are swallowed).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import os from 'node:os';
|
|
14
|
+
import https from 'node:https';
|
|
15
|
+
import http from 'node:http';
|
|
16
|
+
import { execSync } from 'node:child_process';
|
|
17
|
+
|
|
18
|
+
const APP_NAME = 'trace-mcp.app';
|
|
19
|
+
const INSTALL_DIR = path.join(os.homedir(), 'Applications');
|
|
20
|
+
const APP_PATH = path.join(INSTALL_DIR, APP_NAME);
|
|
21
|
+
const GITHUB_REPO = 'nikolai-vysotskyi/trace-mcp';
|
|
22
|
+
|
|
23
|
+
// Only run on macOS and only if app is already installed
|
|
24
|
+
if (process.platform !== 'darwin' || !fs.existsSync(APP_PATH)) {
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Simple HTTPS GET that follows redirects and returns the body. */
|
|
29
|
+
function httpGet(url, timeoutMs = 15000) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const doGet = (target, redirects = 0) => {
|
|
32
|
+
if (redirects > 5) { reject(new Error('Too many redirects')); return; }
|
|
33
|
+
const mod = target.startsWith('https') ? https : http;
|
|
34
|
+
mod.get(target, { timeout: timeoutMs, headers: { 'User-Agent': 'trace-mcp', Accept: 'application/vnd.github.v3+json' } }, (res) => {
|
|
35
|
+
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
|
|
36
|
+
doGet(res.headers.location, redirects + 1);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let body = '';
|
|
40
|
+
res.on('data', (chunk) => { body += chunk; });
|
|
41
|
+
res.on('end', () => resolve(body));
|
|
42
|
+
}).on('error', reject).on('timeout', function () { this.destroy(); reject(new Error('timeout')); });
|
|
43
|
+
};
|
|
44
|
+
doGet(url);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Download a file to disk, following redirects. */
|
|
49
|
+
function downloadFile(url, dest, timeoutMs = 60000) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const file = fs.createWriteStream(dest);
|
|
52
|
+
const doGet = (target, redirects = 0) => {
|
|
53
|
+
if (redirects > 5) { reject(new Error('Too many redirects')); return; }
|
|
54
|
+
https.get(target, { timeout: timeoutMs, headers: { 'User-Agent': 'trace-mcp' } }, (res) => {
|
|
55
|
+
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
|
|
56
|
+
doGet(res.headers.location, redirects + 1);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode}`)); return; }
|
|
60
|
+
res.pipe(file);
|
|
61
|
+
file.on('finish', () => { file.close(); resolve(); });
|
|
62
|
+
}).on('error', (err) => { fs.unlink(dest, () => {}); reject(err); });
|
|
63
|
+
};
|
|
64
|
+
doGet(url);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function main() {
|
|
69
|
+
const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
70
|
+
const zipPattern = new RegExp(`trace-mcp.*${arch}\\.zip$`, 'i');
|
|
71
|
+
|
|
72
|
+
// Fetch latest release
|
|
73
|
+
const body = await httpGet(`https://api.github.com/repos/${GITHUB_REPO}/releases/latest`);
|
|
74
|
+
const release = JSON.parse(body);
|
|
75
|
+
if (!release.tag_name || !release.assets) return;
|
|
76
|
+
|
|
77
|
+
const asset = release.assets.find((a) => zipPattern.test(a.name));
|
|
78
|
+
if (!asset) return;
|
|
79
|
+
|
|
80
|
+
// Check if already up to date (compare tag stored in a marker file)
|
|
81
|
+
const markerPath = path.join(INSTALL_DIR, '.trace-mcp-version');
|
|
82
|
+
if (fs.existsSync(markerPath)) {
|
|
83
|
+
const installed = fs.readFileSync(markerPath, 'utf-8').trim();
|
|
84
|
+
if (installed === release.tag_name) return; // already current
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Download zip to temp
|
|
88
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'trace-mcp-update-'));
|
|
89
|
+
const zipPath = path.join(tmpDir, asset.name);
|
|
90
|
+
await downloadFile(asset.browser_download_url, zipPath);
|
|
91
|
+
|
|
92
|
+
// Remove old app and unzip new one
|
|
93
|
+
fs.rmSync(APP_PATH, { recursive: true, force: true });
|
|
94
|
+
execSync(`unzip -q -o "${zipPath}" -d "${INSTALL_DIR}"`, { stdio: 'pipe' });
|
|
95
|
+
|
|
96
|
+
// Write version marker
|
|
97
|
+
fs.writeFileSync(markerPath, release.tag_name, 'utf-8');
|
|
98
|
+
|
|
99
|
+
// Clean up temp
|
|
100
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
101
|
+
|
|
102
|
+
console.log(` trace-mcp app updated to ${release.tag_name}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
main().catch(() => {
|
|
106
|
+
// Never fail the npm install
|
|
107
|
+
});
|