scripts / install-python.js
const { spawn } = require("child_process");
const fs = require("fs");
const path = require("path");
const REPO_URL = "https://github.com/twjackysu/TWSEMCPServer.git";
const CLONE_DIR = "TWStockMCPServer";
const VENV_DIR = "venv";
function log(msg) {
console.log(`[TW-Stock Install] ${msg}`);
}
function exec(cmd, args, options = {}) {
return new Promise((resolve, reject) => {
const stdio = options.stdio || "inherit";
log(`Running: ${cmd} ${args.join(" ")}`);
const proc = spawn(cmd, args, { stdio, ...options });
proc.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Command failed with code ${code}`));
}
});
proc.on("error", reject);
});
}
async function commandExists(cmd) {
try {
await exec(cmd, ["--version"], { stdio: "pipe" });
return true;
} catch (e) {
return false;
}
}
async function cloneRepo() {
if (fs.existsSync(CLONE_DIR)) {
log(`${CLONE_DIR} already exists, skipping clone`);
return;
}
log(`Cloning ${REPO_URL}...`);
await exec("git", ["clone", REPO_URL, CLONE_DIR]);
log("Clone completed");
}
function patchPyprojectToml() {
const pyprojectPath = path.join(CLONE_DIR, "pyproject.toml");
if (!fs.existsSync(pyprojectPath)) {
log("pyproject.toml not found, skipping patch");
return;
}
let content = fs.readFileSync(pyprojectPath, "utf-8");
// Check if already patched
if (content.includes("[tool.setuptools]")) {
log("pyproject.toml already has setuptools config, skipping patch");
return;
}
// Add setuptools package discovery configuration
const patch = `
[tool.setuptools]
packages = ["prompts", "tools", "utils"]
[tool.setuptools.package-dir]
"" = "."
`;
content += patch;
fs.writeFileSync(pyprojectPath, content, "utf-8");
log("Patched pyproject.toml with setuptools configuration");
}
async function findPython() {
const candidates = ["python3.13", "python3.12", "python3.11", "python3", "python"];
for (const py of candidates) {
if (await commandExists(py)) {
log(`Found Python: ${py}`);
return py;
}
}
throw new Error("Python not found. Please install Python 3.11 or later.");
}
async function setupVenvWithUV(pythonCmd) {
const venvPath = path.join(VENV_DIR);
if (fs.existsSync(venvPath)) {
log("venv already exists, skipping creation");
return;
}
log("Creating venv with uv...");
await exec("uv", ["venv", "--python", pythonCmd, venvPath], { cwd: CLONE_DIR });
log("Installing dependencies with uv...");
const pythonPath = process.platform === "win32"
? path.join(venvPath, "Scripts", "python.exe")
: path.join(venvPath, "bin", "python");
// Patch pyproject.toml before installing
patchPyprojectToml();
await exec("uv", ["pip", "install", "-e", "."], {
cwd: CLONE_DIR,
env: {
...process.env,
VIRTUAL_ENV: path.resolve(venvPath),
PATH: `${path.dirname(pythonPath)}${path.delimiter}${process.env.PATH}`
}
});
log("Dependencies installed successfully with uv");
}
async function setupVenvWithPip(pythonCmd) {
const venvPath = path.join(VENV_DIR);
if (fs.existsSync(venvPath)) {
log("venv already exists, skipping creation");
return;
}
log("Creating venv with python...");
await exec(pythonCmd, ["-m", "venv", venvPath], { cwd: CLONE_DIR });
const isWindows = process.platform === "win32";
const pipPath = isWindows
? path.join(venvPath, "Scripts", "pip")
: path.join(venvPath, "bin", "pip");
// Patch pyproject.toml before installing
patchPyprojectToml();
log("Installing dependencies with pip...");
await exec(pipPath, ["install", "-e", "."], { cwd: CLONE_DIR });
log("Dependencies installed successfully with pip");
}
async function main() {
try {
log("Starting Python environment setup...");
log("");
// Clone repository
await cloneRepo();
// Patch pyproject.toml to fix setuptools package discovery
patchPyprojectToml();
// Find Python
const pythonCmd = await findPython();
// Try uv first, fall back to pip
const hasUV = await commandExists("uv");
if (hasUV) {
log("Using uv for faster installation");
await setupVenvWithUV(pythonCmd);
} else {
log("uv not found, using standard pip");
await setupVenvWithPip(pythonCmd);
}
log("");
log("=".repeat(60));
log("Setup completed successfully!");
log("=".repeat(60));
log("");
log("The Python virtual environment is ready at:");
log(` ${path.join(CLONE_DIR, VENV_DIR)}`);
log("");
log("To activate it manually (optional):");
if (process.platform === "win32") {
log(` ${path.join(CLONE_DIR, VENV_DIR, "Scripts", "activate")}`);
} else {
log(` source ${path.join(CLONE_DIR, VENV_DIR, "bin", "activate")}`);
}
log("");
log("The plugin will automatically use this venv when you start LM Studio.");
log("");
} catch (error) {
console.error("");
console.error("[TW-Stock Install] Error:", error.message);
console.error("");
console.error("Manual setup instructions:");
console.error(" 1. git clone https://github.com/twjackysu/TWSEMCPServer.git");
console.error(" 2. cd TWStockMCPServer");
console.error(" 3. Add [tool.setuptools] section to pyproject.toml:");
console.error(" [tool.setuptools]");
console.error(" packages = [\"prompts\", \"tools\", \"utils\"]");
console.error(" 4. python -m venv venv");
if (process.platform === "win32") {
console.error(" 5. venv\\Scripts\\activate");
} else {
console.error(" 5. source venv/bin/activate");
}
console.error(" 6. pip install -e .");
console.error("");
process.exit(1);
}
}
main();scripts / install-python.js
const { spawn } = require("child_process");
const fs = require("fs");
const path = require("path");
const REPO_URL = "https://github.com/twjackysu/TWSEMCPServer.git";
const CLONE_DIR = "TWStockMCPServer";
const VENV_DIR = "venv";
function log(msg) {
console.log(`[TW-Stock Install] ${msg}`);
}
function exec(cmd, args, options = {}) {
return new Promise((resolve, reject) => {
const stdio = options.stdio || "inherit";
log(`Running: ${cmd} ${args.join(" ")}`);
const proc = spawn(cmd, args, { stdio, ...options });
proc.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Command failed with code ${code}`));
}
});
proc.on("error", reject);
});
}
async function commandExists(cmd) {
try {
await exec(cmd, ["--version"], { stdio: "pipe" });
return true;
} catch (e) {
return false;
}
}
async function cloneRepo() {
if (fs.existsSync(CLONE_DIR)) {
log(`${CLONE_DIR} already exists, skipping clone`);
return;
}
log(`Cloning ${REPO_URL}...`);
await exec("git", ["clone", REPO_URL, CLONE_DIR]);
log("Clone completed");
}
function patchPyprojectToml() {
const pyprojectPath = path.join(CLONE_DIR, "pyproject.toml");
if (!fs.existsSync(pyprojectPath)) {
log("pyproject.toml not found, skipping patch");
return;
}
let content = fs.readFileSync(pyprojectPath, "utf-8");
// Check if already patched
if (content.includes("[tool.setuptools]")) {
log("pyproject.toml already has setuptools config, skipping patch");
return;
}
// Add setuptools package discovery configuration
const patch = `
[tool.setuptools]
packages = ["prompts", "tools", "utils"]
[tool.setuptools.package-dir]
"" = "."
`;
content += patch;
fs.writeFileSync(pyprojectPath, content, "utf-8");
log("Patched pyproject.toml with setuptools configuration");
}
async function findPython() {
const candidates = ["python3.13", "python3.12", "python3.11", "python3", "python"];
for (const py of candidates) {
if (await commandExists(py)) {
log(`Found Python: ${py}`);
return py;
}
}
throw new Error("Python not found. Please install Python 3.11 or later.");
}
async function setupVenvWithUV(pythonCmd) {
const venvPath = path.join(VENV_DIR);
if (fs.existsSync(venvPath)) {
log("venv already exists, skipping creation");
return;
}
log("Creating venv with uv...");
await exec("uv", ["venv", "--python", pythonCmd, venvPath], { cwd: CLONE_DIR });
log("Installing dependencies with uv...");
const pythonPath = process.platform === "win32"
? path.join(venvPath, "Scripts", "python.exe")
: path.join(venvPath, "bin", "python");
// Patch pyproject.toml before installing
patchPyprojectToml();
await exec("uv", ["pip", "install", "-e", "."], {
cwd: CLONE_DIR,
env: {
...process.env,
VIRTUAL_ENV: path.resolve(venvPath),
PATH: `${path.dirname(pythonPath)}${path.delimiter}${process.env.PATH}`
}
});
log("Dependencies installed successfully with uv");
}
async function setupVenvWithPip(pythonCmd) {
const venvPath = path.join(VENV_DIR);
if (fs.existsSync(venvPath)) {
log("venv already exists, skipping creation");
return;
}
log("Creating venv with python...");
await exec(pythonCmd, ["-m", "venv", venvPath], { cwd: CLONE_DIR });
const isWindows = process.platform === "win32";
const pipPath = isWindows
? path.join(venvPath, "Scripts", "pip")
: path.join(venvPath, "bin", "pip");
// Patch pyproject.toml before installing
patchPyprojectToml();
log("Installing dependencies with pip...");
await exec(pipPath, ["install", "-e", "."], { cwd: CLONE_DIR });
log("Dependencies installed successfully with pip");
}
async function main() {
try {
log("Starting Python environment setup...");
log("");
// Clone repository
await cloneRepo();
// Patch pyproject.toml to fix setuptools package discovery
patchPyprojectToml();
// Find Python
const pythonCmd = await findPython();
// Try uv first, fall back to pip
const hasUV = await commandExists("uv");
if (hasUV) {
log("Using uv for faster installation");
await setupVenvWithUV(pythonCmd);
} else {
log("uv not found, using standard pip");
await setupVenvWithPip(pythonCmd);
}
log("");
log("=".repeat(60));
log("Setup completed successfully!");
log("=".repeat(60));
log("");
log("The Python virtual environment is ready at:");
log(` ${path.join(CLONE_DIR, VENV_DIR)}`);
log("");
log("To activate it manually (optional):");
if (process.platform === "win32") {
log(` ${path.join(CLONE_DIR, VENV_DIR, "Scripts", "activate")}`);
} else {
log(` source ${path.join(CLONE_DIR, VENV_DIR, "bin", "activate")}`);
}
log("");
log("The plugin will automatically use this venv when you start LM Studio.");
log("");
} catch (error) {
console.error("");
console.error("[TW-Stock Install] Error:", error.message);
console.error("");
console.error("Manual setup instructions:");
console.error(" 1. git clone https://github.com/twjackysu/TWSEMCPServer.git");
console.error(" 2. cd TWStockMCPServer");
console.error(" 3. Add [tool.setuptools] section to pyproject.toml:");
console.error(" [tool.setuptools]");
console.error(" packages = [\"prompts\", \"tools\", \"utils\"]");
console.error(" 4. python -m venv venv");
if (process.platform === "win32") {
console.error(" 5. venv\\Scripts\\activate");
} else {
console.error(" 5. source venv/bin/activate");
}
console.error(" 6. pip install -e .");
console.error("");
process.exit(1);
}
}
main();