mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-21 22:01:41 +00:00
Initial monorepo setup with npm workspaces and dual TypeScript configuration
- Set up npm workspaces for three packages: pi-tui, pi-agent, and pi (pods) - Implemented dual TypeScript configuration: - Root tsconfig.json with path mappings for development and type checking - Package-specific tsconfig.build.json for clean production builds - Configured lockstep versioning with sync script for inter-package dependencies - Added comprehensive documentation for development and publishing workflows - All packages at version 0.5.0 ready for npm publishing
This commit is contained in:
commit
a74c5da112
63 changed files with 14558 additions and 0 deletions
205
packages/pods/src/commands/pods.ts
Normal file
205
packages/pods/src/commands/pods.ts
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
import chalk from "chalk";
|
||||
import { dirname, join } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { addPod, loadConfig, removePod, setActivePod } from "../config.js";
|
||||
import { scpFile, sshExec, sshExecStream } from "../ssh.js";
|
||||
import type { GPU, Pod } from "../types.js";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
/**
|
||||
* List all pods
|
||||
*/
|
||||
export const listPods = () => {
|
||||
const config = loadConfig();
|
||||
const podNames = Object.keys(config.pods);
|
||||
|
||||
if (podNames.length === 0) {
|
||||
console.log("No pods configured. Use 'pi pods setup' to add a pod.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Configured pods:");
|
||||
for (const name of podNames) {
|
||||
const pod = config.pods[name];
|
||||
const isActive = config.active === name;
|
||||
const marker = isActive ? chalk.green("*") : " ";
|
||||
const gpuCount = pod.gpus?.length || 0;
|
||||
const gpuInfo = gpuCount > 0 ? `${gpuCount}x ${pod.gpus[0].name}` : "no GPUs detected";
|
||||
const vllmInfo = pod.vllmVersion ? ` (vLLM: ${pod.vllmVersion})` : "";
|
||||
console.log(`${marker} ${chalk.bold(name)} - ${gpuInfo}${vllmInfo} - ${pod.ssh}`);
|
||||
if (pod.modelsPath) {
|
||||
console.log(` Models: ${pod.modelsPath}`);
|
||||
}
|
||||
if (pod.vllmVersion === "gpt-oss") {
|
||||
console.log(chalk.yellow(` ⚠️ GPT-OSS build - only for GPT-OSS models`));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup a new pod
|
||||
*/
|
||||
export const setupPod = async (
|
||||
name: string,
|
||||
sshCmd: string,
|
||||
options: { mount?: string; modelsPath?: string; vllm?: "release" | "nightly" | "gpt-oss" },
|
||||
) => {
|
||||
// Validate environment variables
|
||||
const hfToken = process.env.HF_TOKEN;
|
||||
const vllmApiKey = process.env.PI_API_KEY;
|
||||
|
||||
if (!hfToken) {
|
||||
console.error(chalk.red("ERROR: HF_TOKEN environment variable is required"));
|
||||
console.error("Get a token from: https://huggingface.co/settings/tokens");
|
||||
console.error("Then run: export HF_TOKEN=your_token_here");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!vllmApiKey) {
|
||||
console.error(chalk.red("ERROR: PI_API_KEY environment variable is required"));
|
||||
console.error("Set an API key: export PI_API_KEY=your_api_key_here");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Determine models path
|
||||
let modelsPath = options.modelsPath;
|
||||
if (!modelsPath && options.mount) {
|
||||
// Extract path from mount command if not explicitly provided
|
||||
// e.g., "mount -t nfs ... /mnt/sfs" -> "/mnt/sfs"
|
||||
const parts = options.mount.split(" ");
|
||||
modelsPath = parts[parts.length - 1];
|
||||
}
|
||||
|
||||
if (!modelsPath) {
|
||||
console.error(chalk.red("ERROR: --models-path is required (or must be extractable from --mount)"));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(chalk.green(`Setting up pod '${name}'...`));
|
||||
console.log(`SSH: ${sshCmd}`);
|
||||
console.log(`Models path: ${modelsPath}`);
|
||||
console.log(
|
||||
`vLLM version: ${options.vllm || "release"} ${options.vllm === "gpt-oss" ? chalk.yellow("(GPT-OSS special build)") : ""}`,
|
||||
);
|
||||
if (options.mount) {
|
||||
console.log(`Mount command: ${options.mount}`);
|
||||
}
|
||||
console.log("");
|
||||
|
||||
// Test SSH connection
|
||||
console.log("Testing SSH connection...");
|
||||
const testResult = await sshExec(sshCmd, "echo 'SSH OK'");
|
||||
if (testResult.exitCode !== 0) {
|
||||
console.error(chalk.red("Failed to connect via SSH"));
|
||||
console.error(testResult.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(chalk.green("✓ SSH connection successful"));
|
||||
|
||||
// Copy setup script
|
||||
console.log("Copying setup script...");
|
||||
const scriptPath = join(__dirname, "../../scripts/pod_setup.sh");
|
||||
const success = await scpFile(sshCmd, scriptPath, "/tmp/pod_setup.sh");
|
||||
if (!success) {
|
||||
console.error(chalk.red("Failed to copy setup script"));
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(chalk.green("✓ Setup script copied"));
|
||||
|
||||
// Build setup command
|
||||
let setupCmd = `bash /tmp/pod_setup.sh --models-path '${modelsPath}' --hf-token '${hfToken}' --vllm-api-key '${vllmApiKey}'`;
|
||||
if (options.mount) {
|
||||
setupCmd += ` --mount '${options.mount}'`;
|
||||
}
|
||||
// Add vLLM version flag
|
||||
const vllmVersion = options.vllm || "release";
|
||||
setupCmd += ` --vllm '${vllmVersion}'`;
|
||||
|
||||
// Run setup script
|
||||
console.log("");
|
||||
console.log(chalk.yellow("Running setup (this will take 2-5 minutes)..."));
|
||||
console.log("");
|
||||
|
||||
// Use forceTTY to preserve colors from apt, pip, etc.
|
||||
const exitCode = await sshExecStream(sshCmd, setupCmd, { forceTTY: true });
|
||||
if (exitCode !== 0) {
|
||||
console.error(chalk.red("\nSetup failed. Check the output above for errors."));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Parse GPU info from setup output
|
||||
console.log("");
|
||||
console.log("Detecting GPU configuration...");
|
||||
const gpuResult = await sshExec(sshCmd, "nvidia-smi --query-gpu=index,name,memory.total --format=csv,noheader");
|
||||
|
||||
const gpus: GPU[] = [];
|
||||
if (gpuResult.exitCode === 0 && gpuResult.stdout) {
|
||||
const lines = gpuResult.stdout.trim().split("\n");
|
||||
for (const line of lines) {
|
||||
const [id, name, memory] = line.split(",").map((s) => s.trim());
|
||||
if (id !== undefined) {
|
||||
gpus.push({
|
||||
id: parseInt(id),
|
||||
name: name || "Unknown",
|
||||
memory: memory || "Unknown",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(chalk.green(`✓ Detected ${gpus.length} GPU(s)`));
|
||||
for (const gpu of gpus) {
|
||||
console.log(` GPU ${gpu.id}: ${gpu.name} (${gpu.memory})`);
|
||||
}
|
||||
|
||||
// Save pod configuration
|
||||
const pod: Pod = {
|
||||
ssh: sshCmd,
|
||||
gpus,
|
||||
models: {},
|
||||
modelsPath,
|
||||
vllmVersion: options.vllm || "release",
|
||||
};
|
||||
|
||||
addPod(name, pod);
|
||||
console.log("");
|
||||
console.log(chalk.green(`✓ Pod '${name}' setup complete and set as active pod`));
|
||||
console.log("");
|
||||
console.log("You can now deploy models with:");
|
||||
console.log(chalk.cyan(` pi start <model> --name <name>`));
|
||||
};
|
||||
|
||||
/**
|
||||
* Switch active pod
|
||||
*/
|
||||
export const switchActivePod = (name: string) => {
|
||||
const config = loadConfig();
|
||||
if (!config.pods[name]) {
|
||||
console.error(chalk.red(`Pod '${name}' not found`));
|
||||
console.log("\nAvailable pods:");
|
||||
for (const podName of Object.keys(config.pods)) {
|
||||
console.log(` ${podName}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
setActivePod(name);
|
||||
console.log(chalk.green(`✓ Switched active pod to '${name}'`));
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a pod from config
|
||||
*/
|
||||
export const removePodCommand = (name: string) => {
|
||||
const config = loadConfig();
|
||||
if (!config.pods[name]) {
|
||||
console.error(chalk.red(`Pod '${name}' not found`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
removePod(name);
|
||||
console.log(chalk.green(`✓ Removed pod '${name}' from configuration`));
|
||||
console.log(chalk.yellow("Note: This only removes the local configuration. The remote pod is not affected."));
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue