src / setup.ts
import fs from "fs";
import path from "path";
import { spawn } from "child_process";
const PLUGIN_DIR = process.cwd();
const VENDOR_DIR = path.join(PLUGIN_DIR, "vendor");
const REPO_DIR = path.join(VENDOR_DIR, "tradingview-mcp");
const VENV_DIR = path.join(VENDOR_DIR, "venv");
const IS_WIN = process.platform === "win32";
const PYTHON_BIN = IS_WIN ? path.join(VENV_DIR, "Scripts", "python.exe") : path.join(VENV_DIR, "bin", "python");
const PIP_BIN = IS_WIN ? path.join(VENV_DIR, "Scripts", "pip.exe") : path.join(VENV_DIR, "bin", "pip");
export function getRepoDir() { return REPO_DIR; }
export function getVenvDir() { return VENV_DIR; }
function spawnAsync(command: string, args: string[], options: any = {}): Promise<void> {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { ...options, stdio: "ignore" });
child.on("close", (code) => {
if (code === 0) resolve();
else reject(new Error(`${command} exited with code ${code}`));
});
child.on("error", reject);
});
}
export async function ensureSetup(onStatus: (msg: string) => void) {
if (!fs.existsSync(VENDOR_DIR)) {
fs.mkdirSync(VENDOR_DIR, { recursive: true });
}
if (!fs.existsSync(REPO_DIR)) {
onStatus("Cloning tradingview-mcp repository...");
await spawnAsync("git", ["clone", "https://github.com/atilaahmettaner/tradingview-mcp", REPO_DIR]);
} else {
onStatus("Updating tradingview-mcp repository...");
await spawnAsync("git", ["-C", REPO_DIR, "pull"]).catch(() => {});
}
if (!fs.existsSync(VENV_DIR)) {
onStatus("Creating Python virtual environment...");
const pythonCmd = IS_WIN ? "python" : "python3";
await spawnAsync(pythonCmd, ["-m", "venv", VENV_DIR]);
}
const readyMarker = path.join(VENV_DIR, ".ready");
if (!fs.existsSync(readyMarker)) {
onStatus("Installing Python dependencies (this may take a few minutes)...");
await spawnAsync(PYTHON_BIN, ["-m", "pip", "install", "--upgrade", "pip"]);
await spawnAsync(PIP_BIN, ["install", "."], { cwd: REPO_DIR });
fs.writeFileSync(readyMarker, new Date().toISOString());
}
onStatus("Setup complete.");
}
src / setup.ts
import fs from "fs";
import path from "path";
import { spawn } from "child_process";
const PLUGIN_DIR = process.cwd();
const VENDOR_DIR = path.join(PLUGIN_DIR, "vendor");
const REPO_DIR = path.join(VENDOR_DIR, "tradingview-mcp");
const VENV_DIR = path.join(VENDOR_DIR, "venv");
const IS_WIN = process.platform === "win32";
const PYTHON_BIN = IS_WIN ? path.join(VENV_DIR, "Scripts", "python.exe") : path.join(VENV_DIR, "bin", "python");
const PIP_BIN = IS_WIN ? path.join(VENV_DIR, "Scripts", "pip.exe") : path.join(VENV_DIR, "bin", "pip");
export function getRepoDir() { return REPO_DIR; }
export function getVenvDir() { return VENV_DIR; }
function spawnAsync(command: string, args: string[], options: any = {}): Promise<void> {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { ...options, stdio: "ignore" });
child.on("close", (code) => {
if (code === 0) resolve();
else reject(new Error(`${command} exited with code ${code}`));
});
child.on("error", reject);
});
}
export async function ensureSetup(onStatus: (msg: string) => void) {
if (!fs.existsSync(VENDOR_DIR)) {
fs.mkdirSync(VENDOR_DIR, { recursive: true });
}
if (!fs.existsSync(REPO_DIR)) {
onStatus("Cloning tradingview-mcp repository...");
await spawnAsync("git", ["clone", "https://github.com/atilaahmettaner/tradingview-mcp", REPO_DIR]);
} else {
onStatus("Updating tradingview-mcp repository...");
await spawnAsync("git", ["-C", REPO_DIR, "pull"]).catch(() => {});
}
if (!fs.existsSync(VENV_DIR)) {
onStatus("Creating Python virtual environment...");
const pythonCmd = IS_WIN ? "python" : "python3";
await spawnAsync(pythonCmd, ["-m", "venv", VENV_DIR]);
}
const readyMarker = path.join(VENV_DIR, ".ready");
if (!fs.existsSync(readyMarker)) {
onStatus("Installing Python dependencies (this may take a few minutes)...");
await spawnAsync(PYTHON_BIN, ["-m", "pip", "install", "--upgrade", "pip"]);
await spawnAsync(PIP_BIN, ["install", "."], { cwd: REPO_DIR });
fs.writeFileSync(readyMarker, new Date().toISOString());
}
onStatus("Setup complete.");
}