gibil 0.1.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/LICENSE +20 -0
- package/README.md +94 -0
- package/dist/index.js +1714 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/paths.ts","../src/utils/auth.ts","../src/cli/index.ts","../src/utils/logger.ts","../src/providers/hetzner.ts","../src/ssh/keys.ts","../src/ssh/exec.ts","../src/config/cloud-init.ts","../src/config/parser.ts","../src/utils/store.ts","../src/utils/random.ts","../src/cli/commands/create.ts","../src/utils/validate.ts","../src/cli/commands/ssh.ts","../src/cli/commands/run.ts","../src/cli/commands/destroy.ts","../src/cli/commands/list.ts","../src/cli/commands/extend.ts","../src/cli/commands/exec-script.ts","../src/cli/commands/auth.ts","../src/cli/commands/usage.ts","../src/mcp/server.ts","../src/cli/commands/mcp.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst GIBIL_DIR = join(homedir(), \".gibil\");\n\nexport const paths = {\n root: GIBIL_DIR,\n instances: join(GIBIL_DIR, \"instances\"),\n keys: join(GIBIL_DIR, \"keys\"),\n instanceFile: (name: string) =>\n join(GIBIL_DIR, \"instances\", `${name}.json`),\n keyDir: (name: string) => join(GIBIL_DIR, \"keys\", name),\n privateKey: (name: string) =>\n join(GIBIL_DIR, \"keys\", name, \"id_ed25519\"),\n publicKey: (name: string) =>\n join(GIBIL_DIR, \"keys\", name, \"id_ed25519.pub\"),\n} as const;\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { paths } from \"./paths.js\";\n\n// ── Config file (stored at ~/.gibil/config.json) ──\n\ninterface GibilLocalConfig {\n api_key?: string;\n api_url?: string; // override for dev/testing\n hetzner_token?: string;\n}\n\nconst CONFIG_FILE = join(paths.root, \"config.json\");\n\n// Default API URL — points to Supabase Edge Functions\n// Format: https://<project-ref>.supabase.co/functions/v1\n// Overridable via GIBIL_API_URL env var or config.api_url\nconst DEFAULT_API_URL = \"https://zopdxjruwktjyjunitrv.supabase.co/functions/v1\";\n\nasync function readConfig(): Promise<GibilLocalConfig> {\n if (!existsSync(CONFIG_FILE)) return {};\n const raw = await readFile(CONFIG_FILE, \"utf-8\");\n return JSON.parse(raw) as GibilLocalConfig;\n}\n\nasync function writeConfig(config: GibilLocalConfig): Promise<void> {\n await mkdir(paths.root, { recursive: true });\n await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2));\n}\n\n// ── Public API ──\n\nexport async function saveApiKey(apiKey: string): Promise<void> {\n const config = await readConfig();\n config.api_key = apiKey;\n await writeConfig(config);\n}\n\nexport async function getApiKey(): Promise<string | null> {\n // Env var takes precedence (for CI/agents)\n if (process.env.GIBIL_API_KEY) return process.env.GIBIL_API_KEY;\n const config = await readConfig();\n return config.api_key ?? null;\n}\n\nexport async function clearApiKey(): Promise<void> {\n const config = await readConfig();\n delete config.api_key;\n await writeConfig(config);\n}\n\nexport function getApiUrl(): string {\n return process.env.GIBIL_API_URL ?? DEFAULT_API_URL;\n}\n\nexport async function getApiUrlFromConfig(): Promise<string> {\n if (process.env.GIBIL_API_URL) return process.env.GIBIL_API_URL;\n const config = await readConfig();\n return config.api_url ?? DEFAULT_API_URL;\n}\n\n// ── Hetzner token (stored in ~/.gibil/config.json) ──\n\nexport async function saveHetznerToken(token: string): Promise<void> {\n const config = await readConfig();\n config.hetzner_token = token;\n await writeConfig(config);\n}\n\nexport async function getHetznerToken(): Promise<string | null> {\n // Env var takes precedence\n if (process.env.HETZNER_API_TOKEN) return process.env.HETZNER_API_TOKEN;\n const config = await readConfig();\n return config.hetzner_token ?? null;\n}\n\n// ── API calls ──\n\nexport interface AuthVerifyResponse {\n valid: boolean;\n user: {\n email: string;\n plan: string;\n };\n limits: {\n max_concurrent: number;\n remaining_hours: number;\n };\n}\n\nexport interface UsageResponse {\n plan: string;\n vm_hours_used: number;\n vm_hours_limit: number;\n active_instances: number;\n max_concurrent: number;\n}\n\nexport async function verifyApiKey(apiKey: string): Promise<AuthVerifyResponse> {\n const apiUrl = await getApiUrlFromConfig();\n const res = await fetch(`${apiUrl}/auth-verify`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ api_key: apiKey }),\n });\n\n if (res.status === 401) {\n throw new Error(\"Invalid API key. Get one at https://gibil.dev\");\n }\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API error (${res.status}): ${text}`);\n }\n\n return (await res.json()) as AuthVerifyResponse;\n}\n\nexport async function trackUsage(\n apiKey: string,\n event: \"create\" | \"destroy\",\n instanceName: string,\n serverType?: string,\n): Promise<void> {\n const apiUrl = await getApiUrlFromConfig();\n const res = await fetch(`${apiUrl}/usage-track`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n api_key: apiKey,\n event,\n instance_name: instanceName,\n server_type: serverType,\n }),\n });\n\n if (res.status === 429) {\n throw new Error(\n \"Plan limit reached. Upgrade at https://gibil.dev/pricing\",\n );\n }\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Usage tracking failed (${res.status}): ${text}`);\n }\n}\n\nexport async function fetchUsage(apiKey: string): Promise<UsageResponse> {\n const apiUrl = await getApiUrlFromConfig();\n const res = await fetch(`${apiUrl}/usage-get`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Failed to fetch usage (${res.status}): ${text}`);\n }\n\n return (await res.json()) as UsageResponse;\n}\n","#!/usr/bin/env node\n\n// Load .env if available (dev mode only — dotenv is optional)\ntry {\n await import(\"dotenv/config\");\n} catch {\n // dotenv not available (bundled/published mode) — no-op\n}\n\nimport { Command } from \"commander\";\nimport { registerCreateCommand } from \"./commands/create.js\";\nimport { registerSSHCommand } from \"./commands/ssh.js\";\nimport { registerRunCommand } from \"./commands/run.js\";\nimport { registerDestroyCommand } from \"./commands/destroy.js\";\nimport { registerListCommand } from \"./commands/list.js\";\nimport { registerExtendCommand } from \"./commands/extend.js\";\nimport { registerExecScriptCommand } from \"./commands/exec-script.js\";\nimport { registerAuthCommand } from \"./commands/auth.js\";\nimport { registerUsageCommand } from \"./commands/usage.js\";\nimport { registerMcpCommand } from \"./commands/mcp.js\";\nimport { logger } from \"../utils/logger.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"gibil\")\n .description(\"Ephemeral dev compute for humans and AI agents\")\n .version(\"0.1.0\");\n\n// Register all commands\nregisterCreateCommand(program);\nregisterSSHCommand(program);\nregisterRunCommand(program);\nregisterDestroyCommand(program);\nregisterListCommand(program);\nregisterExtendCommand(program);\nregisterExecScriptCommand(program);\nregisterAuthCommand(program);\nregisterUsageCommand(program);\nregisterMcpCommand(program);\n\nasync function main() {\n try {\n await program.parseAsync(process.argv);\n } catch (error) {\n if (error instanceof Error) {\n logger.error(error.message);\n }\n process.exit(1);\n }\n}\n\nmain();\n","export type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n\nlet currentLevel: LogLevel = \"info\";\nlet jsonMode = false;\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n};\n\nexport function setLogLevel(level: LogLevel): void {\n currentLevel = level;\n}\n\nexport function setJsonMode(enabled: boolean): void {\n jsonMode = enabled;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n if (jsonMode && level !== \"error\") return false;\n return LEVEL_ORDER[level] >= LEVEL_ORDER[currentLevel];\n}\n\nexport const logger = {\n debug(msg: string, ...args: unknown[]) {\n if (shouldLog(\"debug\")) console.debug(`[debug] ${msg}`, ...args);\n },\n info(msg: string, ...args: unknown[]) {\n if (shouldLog(\"info\")) console.log(msg, ...args);\n },\n warn(msg: string, ...args: unknown[]) {\n if (shouldLog(\"warn\")) console.warn(`⚠ ${msg}`, ...args);\n },\n error(msg: string, ...args: unknown[]) {\n if (shouldLog(\"error\")) console.error(`✖ ${msg}`, ...args);\n },\n /** Output JSON to stdout — always works regardless of log level */\n json(data: unknown) {\n console.log(JSON.stringify(data, null, 2));\n },\n};\n","import type {\n CloudProvider,\n HetznerServer,\n HetznerSSHKey,\n HetznerServerCreateRequest,\n} from \"../types/index.js\";\nimport { logger } from \"../utils/logger.js\";\n\nconst API_BASE = \"https://api.hetzner.cloud/v1\";\n\ninterface HetznerApiError {\n error: { message: string; code: string };\n}\n\nexport class HetznerProvider implements CloudProvider {\n private token: string;\n\n private constructor(token: string) {\n this.token = token;\n }\n\n static async create(token?: string): Promise<HetznerProvider> {\n const { getHetznerToken } = await import(\"../utils/auth.js\");\n const t = token ?? await getHetznerToken();\n if (!t) {\n throw new Error(\n \"HETZNER_API_TOKEN is required. Run 'gibil auth setup' or set it in your environment.\",\n );\n }\n return new HetznerProvider(t);\n }\n\n // ── Low-level HTTP ──\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${API_BASE}${path}`;\n logger.debug(`${method} ${url}`);\n\n const res = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.token}`,\n \"Content-Type\": \"application/json\",\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const text = await res.text();\n let errorMsg: string;\n try {\n const parsed = JSON.parse(text) as HetznerApiError;\n errorMsg = parsed.error?.message ?? text;\n } catch {\n errorMsg = text;\n }\n throw new Error(`Hetzner API error (${res.status}): ${errorMsg}`);\n }\n\n // DELETE returns 204 with no body\n if (res.status === 204) return {} as T;\n return (await res.json()) as T;\n }\n\n // ── Server operations ──\n\n async createServer(\n name: string,\n sshKeyId: number,\n userData?: string,\n serverType = \"cax11\",\n location = \"fsn1\",\n ): Promise<HetznerServer> {\n const payload: HetznerServerCreateRequest = {\n name,\n server_type: serverType,\n image: \"ubuntu-24.04\",\n ssh_keys: [sshKeyId],\n labels: { gibil: \"true\", \"gibil-name\": name },\n location,\n };\n if (userData) {\n payload.user_data = userData;\n }\n\n const res = await this.request<{ server: HetznerServer }>(\n \"POST\",\n \"/servers\",\n payload,\n );\n return res.server;\n }\n\n async destroyServer(serverId: number): Promise<void> {\n await this.request(\"DELETE\", `/servers/${serverId}`);\n }\n\n async getServer(serverId: number): Promise<HetznerServer> {\n const res = await this.request<{ server: HetznerServer }>(\n \"GET\",\n `/servers/${serverId}`,\n );\n return res.server;\n }\n\n async listServers(labelSelector = \"gibil=true\"): Promise<HetznerServer[]> {\n const res = await this.request<{ servers: HetznerServer[] }>(\n \"GET\",\n `/servers?label_selector=${encodeURIComponent(labelSelector)}&per_page=50`,\n );\n return res.servers;\n }\n\n async waitForReady(\n serverId: number,\n timeoutMs = 120_000,\n ): Promise<HetznerServer> {\n const start = Date.now();\n const pollInterval = 3_000;\n\n while (Date.now() - start < timeoutMs) {\n const server = await this.getServer(serverId);\n\n if (\n server.status === \"running\" &&\n server.public_net.ipv4.ip !== \"0.0.0.0\"\n ) {\n return server;\n }\n\n logger.debug(\n `Server ${serverId} status: ${server.status}, waiting...`,\n );\n await new Promise((r) => setTimeout(r, pollInterval));\n }\n\n throw new Error(\n `Server ${serverId} did not become ready within ${timeoutMs / 1000}s`,\n );\n }\n\n // ── SSH key operations ──\n\n async createSSHKey(name: string, publicKey: string): Promise<HetznerSSHKey> {\n const res = await this.request<{ ssh_key: HetznerSSHKey }>(\n \"POST\",\n \"/ssh_keys\",\n { name, public_key: publicKey },\n );\n return res.ssh_key;\n }\n\n async deleteSSHKey(keyId: number): Promise<void> {\n await this.request(\"DELETE\", `/ssh_keys/${keyId}`);\n }\n}\n","import { mkdir, rm, readFile, chmod } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { paths } from \"../utils/paths.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport interface KeyPair {\n privateKeyPath: string;\n publicKeyPath: string;\n publicKey: string;\n}\n\n/**\n * Generate an ED25519 key pair for an instance and store it on disk.\n * Uses ssh-keygen directly — Node's crypto generates PEM/DER keys that need\n * conversion to OpenSSH format for Hetzner. ssh-keygen produces the right\n * format natively and is available on all target platforms.\n */\nexport async function generateInstanceKeys(name: string): Promise<KeyPair> {\n const keyDir = paths.keyDir(name);\n\n // Clean up existing keys from a previous failed attempt\n if (existsSync(keyDir)) {\n await rm(keyDir, { recursive: true });\n }\n await mkdir(keyDir, { recursive: true });\n\n const privateKeyPath = paths.privateKey(name);\n const publicKeyPath = paths.publicKey(name);\n\n await execFileAsync(\"ssh-keygen\", [\n \"-t\",\n \"ed25519\",\n \"-f\",\n privateKeyPath,\n \"-N\",\n \"\", // no passphrase\n \"-C\",\n `gibil-${name}`,\n ]);\n\n await chmod(privateKeyPath, 0o600);\n\n const publicKeyContent = await readFile(publicKeyPath, \"utf-8\");\n\n return {\n privateKeyPath,\n publicKeyPath,\n publicKey: publicKeyContent.trim(),\n };\n}\n\n/** Delete keys for an instance */\nexport async function deleteInstanceKeys(name: string): Promise<void> {\n const keyDir = paths.keyDir(name);\n if (existsSync(keyDir)) {\n await rm(keyDir, { recursive: true });\n }\n}\n\n/** Read the private key for an instance */\nexport async function readPrivateKey(name: string): Promise<string> {\n const keyPath = paths.privateKey(name);\n if (!existsSync(keyPath)) {\n throw new Error(`SSH key not found for instance \"${name}\"`);\n }\n return readFile(keyPath, \"utf-8\");\n}\n","import { Client } from \"ssh2\";\nimport { readFile } from \"node:fs/promises\";\nimport type { ExecResult } from \"../types/index.js\";\nimport { paths } from \"../utils/paths.js\";\nimport { logger } from \"../utils/logger.js\";\n\ninterface SSHExecOptions {\n instanceName: string;\n ip: string;\n command: string;\n /** Stream output to stdout/stderr in real-time */\n stream?: boolean;\n /** Connection timeout in ms */\n timeoutMs?: number;\n}\n\n/** Execute a command on a remote instance via SSH */\nexport async function sshExec(opts: SSHExecOptions): Promise<ExecResult> {\n const { instanceName, ip, command, stream = false, timeoutMs = 30_000 } = opts;\n const privateKey = await readFile(paths.privateKey(instanceName), \"utf-8\");\n\n return new Promise<ExecResult>((resolve, reject) => {\n const conn = new Client();\n let stdout = \"\";\n let stderr = \"\";\n\n conn\n .on(\"ready\", () => {\n logger.debug(`SSH connected to ${ip}`);\n conn.exec(command, (err, channel) => {\n if (err) {\n conn.end();\n return reject(err);\n }\n\n channel.on(\"data\", (data: Buffer) => {\n const text = data.toString();\n stdout += text;\n if (stream) process.stdout.write(text);\n });\n\n channel.stderr.on(\"data\", (data: Buffer) => {\n const text = data.toString();\n stderr += text;\n if (stream) process.stderr.write(text);\n });\n\n channel.on(\"close\", (code: number) => {\n conn.end();\n resolve({ stdout, stderr, exitCode: code ?? 0 });\n });\n });\n })\n .on(\"error\", (err: NodeJS.ErrnoException) => {\n let hint = \"\";\n if (err.code === \"ECONNREFUSED\") {\n hint = \" (instance may have been destroyed or is still booting)\";\n } else if (err.code === \"EHOSTUNREACH\") {\n hint = \" (IP unreachable — instance may not be running)\";\n } else if (err.code === \"ETIMEDOUT\") {\n hint = \" (connection timed out — check if instance is running with 'gibil list')\";\n }\n reject(\n new Error(`SSH connection to ${ip} failed: ${err.message}${hint}`),\n );\n })\n .connect({\n host: ip,\n port: 22,\n username: \"root\",\n privateKey,\n readyTimeout: timeoutMs,\n // Retry-friendly: don't reject on first attempt\n hostVerifier: () => true,\n // Forward local SSH agent for commit signing\n agent: process.env.SSH_AUTH_SOCK,\n agentForward: true,\n });\n });\n}\n\n/** Wait for SSH to become available on a new server */\nexport async function waitForSSH(\n instanceName: string,\n ip: string,\n timeoutMs = 120_000,\n): Promise<void> {\n const start = Date.now();\n const retryInterval = 5_000;\n\n while (Date.now() - start < timeoutMs) {\n try {\n await sshExec({\n instanceName,\n ip,\n command: \"echo ready\",\n timeoutMs: 10_000,\n });\n return; // SSH is ready\n } catch {\n logger.debug(`SSH not ready on ${ip}, retrying...`);\n await new Promise((r) => setTimeout(r, retryInterval));\n }\n }\n\n throw new Error(`SSH did not become available on ${ip} within ${timeoutMs / 1000}s`);\n}\n","import type { GibilConfig, GitIdentity } from \"../types/index.js\";\n\ninterface CloudInitOptions {\n repo?: string;\n config?: GibilConfig;\n ttlMinutes?: number;\n githubToken?: string;\n gitIdentity?: GitIdentity;\n}\n\n/** Generate a cloud-init user-data script for a new server */\nexport function generateCloudInit(opts: CloudInitOptions): string {\n const { repo, config, ttlMinutes, githubToken, gitIdentity } = opts;\n const lines: string[] = [\n \"#!/bin/bash\",\n \"set -euo pipefail\",\n \"\",\n \"# ── Gibil cloud-init ──\",\n 'export HOME=/root',\n 'export DEBIAN_FRONTEND=noninteractive',\n ];\n\n // Inject GitHub token for private repo cloning\n if (githubToken) {\n lines.push(`export GITHUB_TOKEN=${shellEscape(githubToken)}`);\n }\n\n lines.push(\n \"\",\n \"# Base packages\",\n \"apt-get update -qq\",\n \"apt-get install -y -qq git curl wget build-essential unzip > /dev/null 2>&1\",\n \"\",\n \"# Install GitHub CLI\",\n 'if ! type gh > /dev/null 2>&1; then',\n ' curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg 2>/dev/null',\n ' chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg',\n ' ARCH=$(dpkg --print-architecture)',\n ' echo \"deb [arch=${ARCH} signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" > /etc/apt/sources.list.d/github-cli.list',\n ' apt-get update -qq && apt-get install -y -qq gh > /dev/null 2>&1',\n 'fi',\n \"\",\n );\n\n // Install runtime based on config image\n const image = config?.image ?? \"node:20\";\n lines.push(...runtimeInstallScript(image));\n\n // Install Docker if services are defined\n if (config?.services && config.services.length > 0) {\n lines.push(...dockerInstallScript());\n lines.push(\"\");\n // Start services\n for (const svc of config.services) {\n lines.push(...serviceStartScript(svc));\n }\n }\n\n // Set environment variables\n if (config?.env) {\n lines.push(\"# Environment variables\");\n for (const [key, value] of Object.entries(config.env)) {\n lines.push(`export ${key}=${shellEscape(value)}`);\n lines.push(`echo 'export ${key}=${shellEscape(value)}' >> /root/.bashrc`);\n }\n lines.push(\"\");\n }\n\n // Always configure git identity — whether or not a repo is cloned\n lines.push(\"# Configure git\");\n if (gitIdentity) {\n lines.push(`git config --global user.email ${shellEscape(gitIdentity.email)}`);\n lines.push(`git config --global user.name ${shellEscape(gitIdentity.name)}`);\n if (gitIdentity.signingKey) {\n lines.push(`git config --global gpg.format ssh`);\n lines.push(`git config --global user.signingkey ${shellEscape('key::' + gitIdentity.signingKey)}`);\n lines.push(`git config --global commit.gpgsign true`);\n lines.push(`git config --global tag.gpgsign true`);\n lines.push(`mkdir -p /root/.ssh`);\n lines.push(`echo ${shellEscape(gitIdentity.email + ' ' + gitIdentity.signingKey)} > /root/.ssh/allowed_signers`);\n lines.push(`git config --global gpg.ssh.allowedSignersFile /root/.ssh/allowed_signers`);\n }\n } else {\n lines.push(\"git config --global user.email 'gibil@bot.dev'\");\n lines.push(\"git config --global user.name 'Gibil Bot'\");\n }\n lines.push(\"\");\n\n // Clone repo\n if (repo) {\n\n // Build clone URL — use GITHUB_TOKEN for private repos if available\n const repoMatch = repo.match(/github\\.com\\/([^/]+\\/[^/.]+)/);\n lines.push(\"# Clone repository\");\n lines.push(`cd /root`);\n if (repoMatch) {\n // Use token-authenticated URL for GitHub repos when GITHUB_TOKEN is set\n lines.push('if [ -n \"${GITHUB_TOKEN:-}\" ]; then');\n lines.push(` CLONE_URL=\"https://x-access-token:\\${GITHUB_TOKEN}@github.com/${repoMatch[1]}.git\"`);\n lines.push(\"else\");\n lines.push(` CLONE_URL=${shellEscape(repo)}`);\n lines.push(\"fi\");\n lines.push(`timeout 300 git clone \"\\$CLONE_URL\" /root/project || { echo \"Git clone failed or timed out\"; exit 1; }`);\n } else {\n lines.push(`timeout 300 git clone ${shellEscape(repo)} /root/project || { echo \"Git clone failed or timed out\"; exit 1; }`);\n }\n lines.push(`cd /root/project`);\n lines.push(\"\");\n\n // Set up gh auth and rewrite remote for push access\n lines.push('if [ -n \"${GITHUB_TOKEN:-}\" ]; then');\n lines.push(' echo \"${GITHUB_TOKEN}\" | gh auth login --with-token 2>/dev/null || true');\n if (repoMatch) {\n lines.push(` git -C /root/project remote set-url origin \"https://x-access-token:\\${GITHUB_TOKEN}@github.com/${repoMatch[1]}.git\"`);\n }\n lines.push(\"fi\");\n lines.push(\"\");\n }\n\n // TTL auto-destroy\n if (ttlMinutes && ttlMinutes > 0) {\n lines.push(\"# Auto-destroy after TTL\");\n lines.push(`echo \"shutdown -h now\" | at now + ${ttlMinutes} minutes 2>/dev/null || true`);\n lines.push(\n `(sleep ${ttlMinutes * 60} && shutdown -h now) &`,\n );\n lines.push(\"\");\n }\n\n // Signal that base infrastructure is ready (runtime + services installed)\n lines.push(\"# Signal that infrastructure is ready\");\n lines.push(\"touch /root/.gibil-ready\");\n lines.push('echo \"Gibil infrastructure ready\"');\n lines.push(\"\");\n\n // Run tasks only when a repo was cloned — tasks assume a project context\n if (repo && config?.tasks && config.tasks.length > 0) {\n lines.push(\"# Run project tasks\");\n lines.push(\"cd /root/project\");\n for (const task of config.tasks) {\n lines.push(`echo '▶ Running task: '${shellEscape(task.name)}`);\n lines.push(`if ! ${task.command}; then`);\n lines.push(` echo '✗ Task failed: '${shellEscape(task.name)}`);\n lines.push(` touch /root/.gibil-tasks-failed`);\n lines.push(`fi`);\n }\n lines.push(\"\");\n lines.push(\"# Signal tasks complete\");\n lines.push(\"if [ ! -f /root/.gibil-tasks-failed ]; then\");\n lines.push(\" touch /root/.gibil-tasks-done\");\n lines.push(' echo \"Gibil tasks complete\"');\n lines.push(\"else\");\n lines.push(' echo \"Gibil tasks finished with errors\"');\n lines.push(\"fi\");\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction runtimeInstallScript(image: string): string[] {\n const lines: string[] = [];\n\n if (image.startsWith(\"node:\")) {\n const version = image.split(\":\")[1] ?? \"20\";\n lines.push(\"# Install Node.js\");\n lines.push(\n `curl -fsSL https://deb.nodesource.com/setup_${version}.x | bash - > /dev/null 2>&1`,\n );\n lines.push(\"apt-get install -y -qq nodejs > /dev/null 2>&1\");\n lines.push(\"npm install -g pnpm@latest > /dev/null 2>&1\");\n lines.push(\"\");\n } else if (image.startsWith(\"python:\")) {\n const version = image.split(\":\")[1] ?? \"3.12\";\n lines.push(\"# Install Python\");\n lines.push(\n `apt-get install -y -qq python${version} python3-pip python3-venv > /dev/null 2>&1`,\n );\n lines.push(\"\");\n } else if (image.startsWith(\"go:\")) {\n const version = image.split(\":\")[1] ?? \"1.22\";\n lines.push(\"# Install Go\");\n lines.push(\n `wget -q https://go.dev/dl/go${version}.linux-amd64.tar.gz -O /tmp/go.tar.gz`,\n );\n lines.push(\"tar -C /usr/local -xzf /tmp/go.tar.gz\");\n lines.push('export PATH=$PATH:/usr/local/go/bin');\n lines.push('echo \"export PATH=\\\\$PATH:/usr/local/go/bin\" >> /root/.bashrc');\n lines.push(\"\");\n } else {\n // Default: install Node.js 20\n lines.push(\"# Install Node.js (default)\");\n lines.push(\n \"curl -fsSL https://deb.nodesource.com/setup_20.x | bash - > /dev/null 2>&1\",\n );\n lines.push(\"apt-get install -y -qq nodejs > /dev/null 2>&1\");\n lines.push(\"npm install -g pnpm@latest > /dev/null 2>&1\");\n lines.push(\"\");\n }\n\n return lines;\n}\n\nfunction dockerInstallScript(): string[] {\n return [\n \"# Install Docker\",\n \"curl -fsSL https://get.docker.com | sh > /dev/null 2>&1\",\n \"systemctl enable docker --now\",\n ];\n}\n\nfunction serviceStartScript(svc: {\n name: string;\n image: string;\n port?: number;\n env?: Record<string, string>;\n}): string[] {\n const lines: string[] = [];\n lines.push(`# Start service: ${svc.name.replace(/[^a-zA-Z0-9_-]/g, \"\")}`);\n\n const safeName = svc.name.replace(/[^a-zA-Z0-9_-]/g, \"\");\n let dockerCmd = `docker run -d --name ${safeName}`;\n\n if (svc.port) {\n dockerCmd += ` -p ${svc.port}:${svc.port}`;\n }\n\n if (svc.env) {\n for (const [key, value] of Object.entries(svc.env)) {\n dockerCmd += ` -e ${key}=${shellEscape(value)}`;\n }\n }\n\n dockerCmd += ` ${svc.image}`;\n lines.push(dockerCmd);\n lines.push(\"\");\n\n return lines;\n}\n\nfunction shellEscape(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { GibilConfig } from \"../types/index.js\";\n\nconst CONFIG_FILENAME = \".gibil.yml\";\n\n/** Parse a .gibil.yml config from a file path or directory.\n * Accepts:\n * - A direct .yml file path: \"test-projects/nextjs/.gibil.yml\"\n * - A directory containing .gibil.yml: \"test-projects/nextjs\"\n */\nexport async function parseConfig(pathOrDir: string): Promise<GibilConfig | null> {\n let configPath: string;\n\n if (existsSync(pathOrDir) && statSync(pathOrDir).isFile()) {\n configPath = pathOrDir;\n } else {\n configPath = join(pathOrDir, CONFIG_FILENAME);\n }\n\n if (!existsSync(configPath)) return null;\n\n const raw = await readFile(configPath, \"utf-8\");\n const parsed = parseYaml(raw);\n\n return validateConfig(parsed);\n}\n\n/** Parse a YAML string directly */\nexport function parseConfigString(yamlContent: string): GibilConfig {\n const parsed = parseYaml(yamlContent);\n return validateConfig(parsed);\n}\n\nfunction validateConfig(raw: unknown): GibilConfig {\n if (!raw || typeof raw !== \"object\") {\n throw new Error(\"Invalid .gibil.yml: must be a YAML object\");\n }\n\n const config = raw as Record<string, unknown>;\n const result: GibilConfig = {};\n\n if (typeof config.name === \"string\") result.name = config.name;\n if (typeof config.image === \"string\") result.image = config.image;\n if (typeof config.server_type === \"string\")\n result.server_type = config.server_type;\n if (typeof config.location === \"string\") result.location = config.location;\n\n if (Array.isArray(config.services)) {\n result.services = config.services.map((s: unknown) => {\n const svc = s as Record<string, unknown>;\n if (typeof svc.name !== \"string\" || typeof svc.image !== \"string\") {\n throw new Error(\n \"Each service must have a 'name' and 'image' field\",\n );\n }\n return {\n name: svc.name,\n image: svc.image,\n port: typeof svc.port === \"number\" ? svc.port : undefined,\n env: validateEnvRecord(svc.env, `service \"${svc.name}\"`),\n };\n });\n }\n\n if (Array.isArray(config.tasks)) {\n result.tasks = config.tasks.map((t: unknown) => {\n const task = t as Record<string, unknown>;\n if (typeof task.name !== \"string\" || typeof task.command !== \"string\") {\n throw new Error(\n \"Each task must have a 'name' and 'command' field\",\n );\n }\n return { name: task.name, command: task.command };\n });\n }\n\n if (config.env !== undefined) {\n result.env = validateEnvRecord(config.env, \"top-level\");\n }\n\n return result;\n}\n\n/** Validate that an env block is a Record<string, string>, coercing numbers/booleans */\nfunction validateEnvRecord(\n env: unknown,\n context: string,\n): Record<string, string> | undefined {\n if (env === undefined || env === null) return undefined;\n if (typeof env !== \"object\" || Array.isArray(env)) {\n throw new Error(`env in ${context} must be a key-value object`);\n }\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(env as Record<string, unknown>)) {\n if (typeof value === \"string\") {\n result[key] = value;\n } else if (typeof value === \"number\" || typeof value === \"boolean\") {\n result[key] = String(value);\n } else {\n throw new Error(\n `env.${key} in ${context} must be a string, number, or boolean — got ${typeof value}`,\n );\n }\n }\n return Object.keys(result).length > 0 ? result : undefined;\n}\n","import { readFile, writeFile, mkdir, rm, readdir } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { paths } from \"./paths.js\";\nimport type { InstanceMetadata } from \"../types/index.js\";\n\n/**\n * InstanceStore manages instance metadata on disk.\n * Accepts an optional base dir for testability; defaults to ~/.gibil.\n */\nexport class InstanceStore {\n private instancesDir: string;\n private keysDir: string;\n\n constructor(baseDir?: string) {\n const root = baseDir ?? paths.root;\n this.instancesDir = join(root, \"instances\");\n this.keysDir = join(root, \"keys\");\n }\n\n async ensureDirectories(): Promise<void> {\n await mkdir(this.instancesDir, { recursive: true });\n await mkdir(this.keysDir, { recursive: true });\n }\n\n private instanceFile(name: string): string {\n return join(this.instancesDir, `${name}.json`);\n }\n\n async save(meta: InstanceMetadata): Promise<void> {\n await this.ensureDirectories();\n await writeFile(this.instanceFile(meta.name), JSON.stringify(meta, null, 2));\n }\n\n async load(name: string): Promise<InstanceMetadata | null> {\n const filePath = this.instanceFile(name);\n if (!existsSync(filePath)) return null;\n const raw = await readFile(filePath, \"utf-8\");\n return JSON.parse(raw) as InstanceMetadata;\n }\n\n async loadOrThrow(name: string): Promise<InstanceMetadata> {\n const meta = await this.load(name);\n if (!meta) {\n throw new Error(\n `Instance \"${name}\" not found. Run \"gibil list\" to see active instances.`,\n );\n }\n return meta;\n }\n\n /** Load and verify the instance hasn't expired */\n async loadActiveOrThrow(name: string): Promise<InstanceMetadata> {\n const meta = await this.loadOrThrow(name);\n if (new Date() > new Date(meta.expiresAt)) {\n throw new Error(\n `Instance \"${name}\" has expired (TTL was ${meta.ttlMinutes}m). Run \"gibil destroy ${name}\" to clean up.`,\n );\n }\n return meta;\n }\n\n async delete(name: string): Promise<void> {\n const filePath = this.instanceFile(name);\n if (existsSync(filePath)) {\n await rm(filePath);\n }\n }\n\n async list(): Promise<InstanceMetadata[]> {\n await this.ensureDirectories();\n const files = await readdir(this.instancesDir);\n const instances: InstanceMetadata[] = [];\n for (const file of files) {\n if (!file.endsWith(\".json\")) continue;\n const name = file.replace(\".json\", \"\");\n const meta = await this.load(name);\n if (meta) instances.push(meta);\n }\n return instances;\n }\n}\n\n// ── Default singleton for use in CLI commands ──\n\nconst defaultStore = new InstanceStore();\n\nexport const ensureDirectories = () => defaultStore.ensureDirectories();\nexport const saveInstance = (meta: InstanceMetadata) => defaultStore.save(meta);\nexport const loadInstance = (name: string) => defaultStore.load(name);\nexport const loadInstanceOrThrow = (name: string) => defaultStore.loadOrThrow(name);\nexport const loadActiveInstanceOrThrow = (name: string) => defaultStore.loadActiveOrThrow(name);\nexport const deleteInstance = (name: string) => defaultStore.delete(name);\nexport const listInstances = () => defaultStore.list();\n","import { randomBytes } from \"node:crypto\";\n\n/** Generate a short random suffix like \"a3f1b2\" */\nexport function randomSuffix(length = 6): string {\n return randomBytes(Math.ceil(length / 2))\n .toString(\"hex\")\n .slice(0, length);\n}\n\n/** Generate a default instance name like \"gibil-a3f1b2\" */\nexport function defaultInstanceName(): string {\n return `gibil-${randomSuffix()}`;\n}\n\n/** Generate a fleet ID */\nexport function fleetId(): string {\n return `fleet-${randomSuffix(8)}`;\n}\n","import { Command } from \"commander\";\nimport { HetznerProvider } from \"../../providers/hetzner.js\";\nimport { generateInstanceKeys, deleteInstanceKeys } from \"../../ssh/keys.js\";\nimport { waitForSSH } from \"../../ssh/exec.js\";\nimport { generateCloudInit } from \"../../config/cloud-init.js\";\nimport { parseConfig, parseConfigString } from \"../../config/parser.js\";\nimport { saveInstance } from \"../../utils/store.js\";\nimport { defaultInstanceName, fleetId } from \"../../utils/random.js\";\nimport { paths } from \"../../utils/paths.js\";\nimport { logger, setJsonMode } from \"../../utils/logger.js\";\nimport { validateInstanceName, parsePositiveInt } from \"../../utils/validate.js\";\nimport { getApiKey, verifyApiKey, trackUsage } from \"../../utils/auth.js\";\nimport { execSync } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport type { InstanceMetadata, InstanceOutput, GibilConfig, GitIdentity } from \"../../types/index.js\";\n\n/** Read the user's local git identity for commit signing on VMs */\nfunction getLocalGitIdentity(): GitIdentity | undefined {\n try {\n const name = execSync(\"git config user.name\", { encoding: \"utf-8\" }).trim();\n const email = execSync(\"git config user.email\", { encoding: \"utf-8\" }).trim();\n if (!name || !email) return undefined;\n let signingKey: string | undefined;\n try {\n const format = execSync(\"git config gpg.format\", { encoding: \"utf-8\" }).trim();\n if (format === \"ssh\") {\n const keyPath = execSync(\"git config user.signingkey\", { encoding: \"utf-8\" }).trim();\n if (keyPath) {\n // Read the public key — could be a path or inline key\n try {\n signingKey = readFileSync(keyPath, \"utf-8\").trim();\n } catch {\n // Already an inline key (starts with ssh-)\n if (keyPath.startsWith(\"ssh-\") || keyPath.startsWith(\"key::\")) {\n signingKey = keyPath.replace(/^key::/, \"\");\n }\n }\n }\n }\n } catch {\n // No signing config — that's fine\n }\n return { name, email, signingKey };\n } catch {\n return undefined;\n }\n}\n\ninterface CreateOptions {\n name?: string;\n fleet?: string;\n repo?: string;\n json?: boolean;\n ttl?: string;\n config?: string;\n serverType?: string;\n location?: string;\n quiet?: boolean;\n}\n\nasync function createSingleInstance(\n provider: HetznerProvider,\n name: string,\n opts: {\n repo?: string;\n ttlMinutes: number;\n config?: GibilConfig | null;\n serverType?: string;\n location?: string;\n fleetId?: string;\n },\n): Promise<InstanceMetadata> {\n // 1. Generate SSH key pair\n logger.info(` Generating SSH keys for \"${name}\"...`);\n const keys = await generateInstanceKeys(name);\n\n let sshKey: { id: number } | undefined;\n try {\n // 2. Upload public key to Hetzner\n logger.info(` Uploading SSH key to Hetzner...`);\n sshKey = await provider.createSSHKey(`gibil-${name}`, keys.publicKey);\n\n // 3. Generate cloud-init script\n const gitIdentity = getLocalGitIdentity();\n const userData = generateCloudInit({\n repo: opts.repo,\n config: opts.config ?? undefined,\n ttlMinutes: opts.ttlMinutes,\n githubToken: process.env.GITHUB_TOKEN,\n gitIdentity,\n });\n\n // 4. Create server\n logger.info(` Creating server...`);\n const server = await provider.createServer(\n name,\n sshKey.id,\n userData,\n opts.config?.server_type ?? opts.serverType,\n opts.config?.location ?? opts.location,\n );\n\n // 5. Wait for server to be running\n logger.info(` Waiting for server to be ready...`);\n const readyServer = await provider.waitForReady(server.id);\n const ip = readyServer.public_net.ipv4.ip;\n\n // 6. Save instance metadata\n const now = new Date();\n const meta: InstanceMetadata = {\n name,\n serverId: server.id,\n ip,\n sshKeyId: sshKey.id,\n keyPath: paths.privateKey(name),\n status: \"running\",\n createdAt: now.toISOString(),\n ttlMinutes: opts.ttlMinutes,\n expiresAt: new Date(\n now.getTime() + opts.ttlMinutes * 60_000,\n ).toISOString(),\n repo: opts.repo,\n fleetId: opts.fleetId,\n gitIdentity,\n };\n await saveInstance(meta);\n\n // 7. Wait for SSH to become available\n logger.info(` Waiting for SSH on ${ip}...`);\n await waitForSSH(name, ip);\n\n logger.info(` ✓ Instance \"${name}\" is ready at ${ip}`);\n return meta;\n } catch (error) {\n // Cleanup on failure — log warnings rather than silently swallowing\n logger.error(`Failed to create instance \"${name}\", cleaning up...`);\n await deleteInstanceKeys(name).catch((e) =>\n logger.warn(`Could not clean up SSH keys: ${e instanceof Error ? e.message : String(e)}`),\n );\n if (sshKey) {\n const keyId = sshKey.id;\n await provider.deleteSSHKey(keyId).catch((e) =>\n logger.warn(`Could not delete Hetzner SSH key ${keyId}: ${e instanceof Error ? e.message : String(e)}`),\n );\n }\n throw error;\n }\n}\n\nfunction metaToOutput(meta: InstanceMetadata): InstanceOutput {\n const ttlRemaining = Math.max(\n 0,\n Math.floor((new Date(meta.expiresAt).getTime() - Date.now()) / 1000),\n );\n return {\n name: meta.name,\n ip: meta.ip,\n ssh: `ssh -i ${meta.keyPath} -o StrictHostKeyChecking=no root@${meta.ip}`,\n status: meta.status,\n ttl_remaining: ttlRemaining,\n created_at: meta.createdAt,\n fleet_id: meta.fleetId,\n };\n}\n\n/**\n * Fetch .gibil.yml from a GitHub repo URL before cloning.\n * Supports: https://github.com/user/repo and https://github.com/user/repo.git\n */\nasync function fetchRepoConfig(repoUrl: string): Promise<GibilConfig | null> {\n // Convert GitHub URL to raw content URL\n const match = repoUrl.match(\n /github\\.com\\/([^/]+)\\/([^/.]+)/,\n );\n if (!match) {\n logger.debug(`Cannot fetch config from non-GitHub repo: ${repoUrl}`);\n return null;\n }\n\n const [, owner, repo] = match;\n const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/HEAD/.gibil.yml`;\n\n logger.debug(`Fetching config from ${rawUrl}`);\n try {\n const res = await fetch(rawUrl, { signal: AbortSignal.timeout(10_000) });\n if (!res.ok) {\n logger.debug(`No .gibil.yml found in repo (${res.status})`);\n return null;\n }\n const content = await res.text();\n return parseConfigString(content);\n } catch {\n logger.debug(\"Failed to fetch repo config, continuing without it\");\n return null;\n }\n}\n\nexport function registerCreateCommand(program: Command): void {\n program\n .command(\"create\")\n .description(\"Spin up one or more ephemeral dev machines\")\n .option(\"-n, --name <name>\", \"Instance name\")\n .option(\n \"-f, --fleet <count>\",\n \"Number of instances to create in parallel\",\n )\n .option(\"-r, --repo <git-url>\", \"Git repository to clone on startup\")\n .option(\"--json\", \"Output instance info as JSON\")\n .option(\"--ttl <minutes>\", \"Auto-destroy after N minutes\", \"60\")\n .option(\"-c, --config <path>\", \"Path to .gibil.yml config\")\n .option(\"--server-type <type>\", \"Hetzner server type (e.g. cpx11, cpx21)\")\n .option(\"--location <loc>\", \"Hetzner location (e.g. fsn1, nbg1)\")\n .option(\"-q, --quiet\", \"Suppress non-essential output\")\n .action(async (opts: CreateOptions) => {\n if (opts.json) setJsonMode(true);\n\n // Validate inputs before touching the API\n const ttlMinutes = parsePositiveInt(opts.ttl ?? \"60\", \"TTL\");\n if (ttlMinutes > 10080) {\n throw new Error(\"TTL cannot exceed 7 days (10080 minutes).\");\n }\n const fleetCount = parsePositiveInt(opts.fleet ?? \"1\", \"Fleet count\");\n if (fleetCount > 20) {\n throw new Error(\"Fleet size cannot exceed 20. Contact support for higher limits.\");\n }\n if (opts.name) validateInstanceName(opts.name);\n\n // Parse config:\n // 1. Explicit --config flag: use that file\n // 2. --repo given: fetch .gibil.yml from the repo (ignore local cwd)\n // 3. No repo: auto-detect .gibil.yml in cwd\n let config: GibilConfig | null = null;\n if (opts.config) {\n config = await parseConfig(opts.config);\n } else if (opts.repo) {\n config = await fetchRepoConfig(opts.repo);\n } else {\n config = await parseConfig(process.cwd());\n }\n\n // ── Auth: verify API key if logged in ──\n // Auth is optional — verifies plan limits and tracks usage.\n // Hetzner token always comes from local env (HETZNER_API_TOKEN).\n const apiKey = await getApiKey();\n if (apiKey) {\n logger.info(\"Verifying API key...\");\n const auth = await verifyApiKey(apiKey);\n logger.info(` Authenticated as ${auth.user.email} (${auth.user.plan})`);\n }\n\n const provider = await HetznerProvider.create();\n\n if (fleetCount === 1) {\n // Single instance\n const name = opts.name ?? defaultInstanceName();\n logger.info(`Creating instance \"${name}\"...`);\n\n const meta = await createSingleInstance(provider, name, {\n repo: opts.repo,\n ttlMinutes,\n config,\n serverType: opts.serverType,\n location: opts.location,\n });\n\n // Track usage if authenticated\n if (apiKey) {\n await trackUsage(apiKey, \"create\", meta.name, opts.serverType).catch((e) =>\n logger.debug(`Usage tracking failed: ${e instanceof Error ? e.message : String(e)}`),\n );\n }\n\n if (opts.json) {\n logger.json(metaToOutput(meta));\n } else {\n logger.info(\"\");\n logger.info(`Instance ready:`);\n logger.info(` Name: ${meta.name}`);\n logger.info(` IP: ${meta.ip}`);\n logger.info(\n ` SSH: ssh -i ${meta.keyPath} -o StrictHostKeyChecking=no root@${meta.ip}`,\n );\n logger.info(` TTL: ${ttlMinutes} minutes`);\n logger.info(\"\");\n logger.info(`Quick connect: gibil ssh ${meta.name}`);\n }\n } else {\n // Fleet mode\n const fId = fleetId();\n const baseName = opts.name ?? \"gibil\";\n logger.info(`Creating fleet \"${fId}\" with ${fleetCount} instances...`);\n\n const names = Array.from({ length: fleetCount }, (_, i) =>\n `${baseName}-${i + 1}-${fId.slice(6)}`,\n );\n\n const results = await Promise.allSettled(\n names.map((name) =>\n createSingleInstance(provider, name, {\n repo: opts.repo,\n ttlMinutes,\n config,\n serverType: opts.serverType,\n location: opts.location,\n fleetId: fId,\n }),\n ),\n );\n\n const succeeded: InstanceMetadata[] = [];\n const failed: string[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result.status === \"fulfilled\") {\n succeeded.push(result.value);\n } else {\n failed.push(\n `${names[i]}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`,\n );\n }\n }\n\n // Track usage for all successful instances\n if (apiKey) {\n await Promise.all(\n succeeded.map((m) =>\n trackUsage(apiKey, \"create\", m.name, opts.serverType).catch((e) =>\n logger.debug(`Usage tracking failed for ${m.name}: ${e instanceof Error ? e.message : String(e)}`),\n ),\n ),\n );\n }\n\n if (opts.json) {\n logger.json({\n fleet_id: fId,\n instances: succeeded.map(metaToOutput),\n errors: failed,\n });\n } else {\n logger.info(\"\");\n logger.info(\n `Fleet \"${fId}\": ${succeeded.length}/${fleetCount} instances created`,\n );\n for (const meta of succeeded) {\n logger.info(` ✓ ${meta.name} → ${meta.ip}`);\n }\n for (const err of failed) {\n logger.error(` ✗ ${err}`);\n }\n }\n }\n });\n}\n","const INSTANCE_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$/;\n\n/** Validate and sanitize an instance name */\nexport function validateInstanceName(name: string): string {\n if (!INSTANCE_NAME_REGEX.test(name)) {\n throw new Error(\n `Invalid instance name \"${name}\". Names must be 1-63 chars, start with alphanumeric, and contain only [a-zA-Z0-9_-].`,\n );\n }\n return name;\n}\n\n/** Parse and validate a positive integer from CLI input */\nexport function parsePositiveInt(value: string, label: string): number {\n const parsed = parseInt(value, 10);\n if (isNaN(parsed) || parsed <= 0) {\n throw new Error(`${label} must be a positive integer, got \"${value}\"`);\n }\n return parsed;\n}\n","import { Command } from \"commander\";\nimport { spawn } from \"node:child_process\";\nimport { loadActiveInstanceOrThrow } from \"../../utils/store.js\";\n\nexport function registerSSHCommand(program: Command): void {\n program\n .command(\"ssh <name>\")\n .description(\"SSH into a running ephemeral machine\")\n .action(async (name: string) => {\n const meta = await loadActiveInstanceOrThrow(name);\n\n const sshArgs = [\n \"-A\",\n \"-i\",\n meta.keyPath,\n \"-o\",\n \"StrictHostKeyChecking=no\",\n \"-o\",\n \"UserKnownHostsFile=/dev/null\",\n \"-o\",\n \"LogLevel=ERROR\",\n `root@${meta.ip}`,\n ];\n\n const proc = spawn(\"ssh\", sshArgs, {\n stdio: \"inherit\",\n });\n\n proc.on(\"exit\", (code) => {\n process.exit(code ?? 0);\n });\n });\n}\n","import { Command } from \"commander\";\nimport { sshExec } from \"../../ssh/exec.js\";\nimport { loadActiveInstanceOrThrow } from \"../../utils/store.js\";\nimport { logger, setJsonMode } from \"../../utils/logger.js\";\n\ninterface RunOptions {\n json?: boolean;\n}\n\nexport function registerRunCommand(program: Command): void {\n program\n .command(\"run <name> <command...>\")\n .description(\"Execute a command on a running instance\")\n .option(\"--json\", \"Output result as JSON\")\n .action(async (name: string, commandParts: string[], opts: RunOptions) => {\n if (opts.json) setJsonMode(true);\n\n const meta = await loadActiveInstanceOrThrow(name);\n const command = commandParts.join(\" \");\n\n logger.info(`Running on \"${name}\" (${meta.ip}): ${command}`);\n\n const result = await sshExec({\n instanceName: name,\n ip: meta.ip,\n command,\n stream: !opts.json,\n });\n\n if (opts.json) {\n logger.json({\n instance: name,\n command,\n stdout: result.stdout,\n stderr: result.stderr,\n exit_code: result.exitCode,\n });\n } else if (result.exitCode !== 0) {\n logger.error(`Command exited with code ${result.exitCode}`);\n }\n\n process.exit(result.exitCode ?? 1);\n });\n}\n","import { Command } from \"commander\";\nimport { HetznerProvider } from \"../../providers/hetzner.js\";\nimport { deleteInstanceKeys } from \"../../ssh/keys.js\";\nimport {\n loadInstanceOrThrow,\n deleteInstance,\n listInstances,\n} from \"../../utils/store.js\";\nimport { logger, setJsonMode } from \"../../utils/logger.js\";\nimport { getApiKey, trackUsage } from \"../../utils/auth.js\";\n\ninterface DestroyOptions {\n all?: boolean;\n json?: boolean;\n}\n\nasync function destroySingle(\n provider: HetznerProvider,\n name: string,\n): Promise<void> {\n const meta = await loadInstanceOrThrow(name);\n\n logger.info(`Destroying instance \"${name}\" (server ${meta.serverId})...`);\n\n // Delete server from Hetzner\n try {\n await provider.destroyServer(meta.serverId);\n } catch (error) {\n logger.warn(\n `Could not delete server ${meta.serverId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Delete SSH key from Hetzner\n try {\n await provider.deleteSSHKey(meta.sshKeyId);\n } catch (error) {\n logger.warn(\n `Could not delete SSH key ${meta.sshKeyId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Clean up local files\n await deleteInstanceKeys(name);\n await deleteInstance(name);\n\n // Track destroy event if authenticated\n const apiKey = await getApiKey();\n if (apiKey) {\n await trackUsage(apiKey, \"destroy\", name).catch((e) =>\n logger.warn(`Usage tracking failed (billing may be inaccurate): ${e instanceof Error ? e.message : String(e)}`),\n );\n }\n\n logger.info(` ✓ Instance \"${name}\" destroyed`);\n}\n\nexport function registerDestroyCommand(program: Command): void {\n program\n .command(\"destroy [name]\")\n .description(\"Destroy a running ephemeral machine\")\n .option(\"-a, --all\", \"Destroy all gibil instances\")\n .option(\"--json\", \"Output result as JSON\")\n .action(async (name: string | undefined, opts: DestroyOptions) => {\n if (opts.json) setJsonMode(true);\n\n if (opts.all) {\n // Check local state first — no provider needed if nothing to destroy\n const instances = await listInstances();\n if (instances.length === 0) {\n if (opts.json) {\n logger.json({ destroyed: [], failed: [] });\n } else {\n logger.info(\"No instances to destroy.\");\n }\n return;\n }\n\n // Only create provider when we actually need to delete servers\n const provider = await HetznerProvider.create();\n\n logger.info(`Destroying ${instances.length} instance(s)...`);\n\n const results = await Promise.allSettled(\n instances.map((inst) => destroySingle(provider, inst.name)),\n );\n\n const destroyed: string[] = [];\n const failed: string[] = [];\n\n for (let i = 0; i < results.length; i++) {\n if (results[i].status === \"fulfilled\") {\n destroyed.push(instances[i].name);\n } else {\n const reason = (results[i] as PromiseRejectedResult).reason;\n failed.push(\n `${instances[i].name}: ${reason instanceof Error ? reason.message : String(reason)}`,\n );\n }\n }\n\n if (opts.json) {\n logger.json({ destroyed, failed });\n } else {\n logger.info(\n `\\n${destroyed.length} destroyed, ${failed.length} failed`,\n );\n }\n } else {\n if (!name) {\n logger.error(\n 'Specify an instance name or use --all. Run \"gibil list\" to see instances.',\n );\n process.exit(1);\n }\n\n const provider = await HetznerProvider.create();\n await destroySingle(provider, name);\n\n if (opts.json) {\n logger.json({ destroyed: [name] });\n }\n }\n });\n}\n","import { Command } from \"commander\";\nimport { listInstances } from \"../../utils/store.js\";\nimport { logger, setJsonMode } from \"../../utils/logger.js\";\nimport type { InstanceOutput } from \"../../types/index.js\";\n\ninterface ListOptions {\n json?: boolean;\n}\n\nexport function registerListCommand(program: Command): void {\n program\n .command(\"list\")\n .alias(\"ls\")\n .description(\"List all active gibil instances\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (opts: ListOptions) => {\n if (opts.json) setJsonMode(true);\n\n const instances = await listInstances();\n\n if (instances.length === 0) {\n if (opts.json) {\n logger.json({ instances: [] });\n } else {\n logger.info(\"No active instances.\");\n }\n return;\n }\n\n const outputs: InstanceOutput[] = instances.map((meta) => {\n const ttlRemaining = Math.max(\n 0,\n Math.floor(\n (new Date(meta.expiresAt).getTime() - Date.now()) / 1000,\n ),\n );\n return {\n name: meta.name,\n ip: meta.ip,\n ssh: `ssh -i ${meta.keyPath} -o StrictHostKeyChecking=no root@${meta.ip}`,\n status: meta.status,\n ttl_remaining: ttlRemaining,\n created_at: meta.createdAt,\n fleet_id: meta.fleetId,\n };\n });\n\n if (opts.json) {\n logger.json({ instances: outputs });\n return;\n }\n\n // Table output\n logger.info(\n `${\"NAME\".padEnd(30)} ${\"IP\".padEnd(18)} ${\"STATUS\".padEnd(12)} ${\"TTL\".padEnd(10)} ${\"AGE\".padEnd(10)}`,\n );\n logger.info(\"─\".repeat(80));\n\n for (const inst of outputs) {\n const ttl = formatDuration(inst.ttl_remaining);\n const age = formatAge(inst.created_at);\n logger.info(\n `${inst.name.padEnd(30)} ${inst.ip.padEnd(18)} ${inst.status.padEnd(12)} ${ttl.padEnd(10)} ${age.padEnd(10)}`,\n );\n }\n\n logger.info(`\\n${outputs.length} instance(s)`);\n });\n}\n\nfunction formatDuration(seconds: number): string {\n if (seconds <= 0) return \"expired\";\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n if (m >= 60) {\n const h = Math.floor(m / 60);\n return `${h}h ${m % 60}m`;\n }\n return `${m}m ${s}s`;\n}\n\nfunction formatAge(createdAt: string): string {\n const diffMs = Date.now() - new Date(createdAt).getTime();\n const seconds = Math.floor(diffMs / 1000);\n return formatDuration(seconds);\n}\n","import { Command } from \"commander\";\nimport { sshExec } from \"../../ssh/exec.js\";\nimport {\n loadActiveInstanceOrThrow,\n saveInstance,\n} from \"../../utils/store.js\";\nimport { logger, setJsonMode } from \"../../utils/logger.js\";\n\ninterface ExtendOptions {\n ttl: string;\n json?: boolean;\n}\n\nexport function registerExtendCommand(program: Command): void {\n program\n .command(\"extend <name>\")\n .description(\"Extend the TTL of a running instance\")\n .requiredOption(\"--ttl <minutes>\", \"New TTL in minutes from now\")\n .option(\"--json\", \"Output result as JSON\")\n .action(async (name: string, opts: ExtendOptions) => {\n if (opts.json) setJsonMode(true);\n\n const meta = await loadActiveInstanceOrThrow(name);\n const ttlMinutes = parseInt(opts.ttl, 10);\n\n if (isNaN(ttlMinutes) || ttlMinutes <= 0) {\n logger.error(\"TTL must be a positive number of minutes\");\n process.exit(1);\n }\n\n // Kill existing shutdown timer and set a new one\n await sshExec({\n instanceName: name,\n ip: meta.ip,\n command: [\n // Kill any existing shutdown timers\n \"pkill -f 'sleep.*shutdown' || true\",\n // Set new timer\n `(sleep ${ttlMinutes * 60} && shutdown -h now) &`,\n ].join(\" && \"),\n });\n\n // Update local metadata\n const newExpiry = new Date(\n Date.now() + ttlMinutes * 60_000,\n ).toISOString();\n meta.ttlMinutes = ttlMinutes;\n meta.expiresAt = newExpiry;\n await saveInstance(meta);\n\n if (opts.json) {\n logger.json({\n name: meta.name,\n ttl_minutes: ttlMinutes,\n expires_at: newExpiry,\n });\n } else {\n logger.info(\n `✓ Extended \"${name}\" TTL to ${ttlMinutes} minutes (expires ${newExpiry})`,\n );\n }\n });\n}\n","import { Command } from \"commander\";\nimport { readFile } from \"node:fs/promises\";\nimport { randomBytes } from \"node:crypto\";\nimport { sshExec } from \"../../ssh/exec.js\";\nimport { loadActiveInstanceOrThrow } from \"../../utils/store.js\";\nimport { logger, setJsonMode } from \"../../utils/logger.js\";\n\ninterface ExecScriptOptions {\n json?: boolean;\n}\n\nexport function registerExecScriptCommand(program: Command): void {\n program\n .command(\"exec <name>\")\n .description(\"Upload and run a local script on an instance\")\n .requiredOption(\"-s, --script <path>\", \"Path to local script\")\n .option(\"--json\", \"Output result as JSON\")\n .action(async (name: string, opts: ExecScriptOptions & { script: string }) => {\n if (opts.json) setJsonMode(true);\n\n const meta = await loadActiveInstanceOrThrow(name);\n const scriptContent = await readFile(opts.script, \"utf-8\");\n\n logger.info(\n `Uploading and running script \"${opts.script}\" on \"${name}\"...`,\n );\n\n // Use base64 encoding to safely transfer script content — avoids\n // heredoc delimiter injection and special character issues entirely.\n const encoded = Buffer.from(scriptContent).toString(\"base64\");\n const remoteScript = `/tmp/gibil-script-${randomBytes(4).toString(\"hex\")}.sh`;\n\n const result = await sshExec({\n instanceName: name,\n ip: meta.ip,\n command: `echo '${encoded}' | base64 -d > ${remoteScript} && chmod +x ${remoteScript} && ${remoteScript}; EXIT=$?; rm -f ${remoteScript}; exit $EXIT`,\n stream: !opts.json,\n });\n\n if (opts.json) {\n logger.json({\n instance: name,\n script: opts.script,\n stdout: result.stdout,\n stderr: result.stderr,\n exit_code: result.exitCode,\n });\n } else if (result.exitCode !== 0) {\n logger.error(`Script exited with code ${result.exitCode}`);\n }\n\n process.exit(result.exitCode ?? 1);\n });\n}\n","import { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport {\n saveApiKey,\n getApiKey,\n clearApiKey,\n verifyApiKey,\n saveHetznerToken,\n} from \"../../utils/auth.js\";\nimport { logger, setJsonMode } from \"../../utils/logger.js\";\n\n/** Prompt the user for input (works in TTY terminals) */\nfunction prompt(question: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr, // stderr so stdout stays clean for --json\n });\n\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nexport function registerAuthCommand(program: Command): void {\n const auth = program\n .command(\"auth\")\n .description(\"Manage authentication\");\n\n // ── gibil auth login ──\n auth\n .command(\"login\")\n .description(\"Log in with your Gibil API key\")\n .option(\"--key <api-key>\", \"API key (or enter interactively)\")\n .option(\"--json\", \"Output result as JSON\")\n .action(async (opts: { key?: string; json?: boolean }) => {\n if (opts.json) setJsonMode(true);\n\n let apiKey = opts.key ?? process.env.GIBIL_API_KEY;\n\n if (!apiKey) {\n apiKey = await prompt(\"Enter your API key: \");\n }\n\n if (!apiKey) {\n logger.error(\"No API key provided.\");\n process.exit(1);\n }\n\n // Validate key format\n if (!apiKey.startsWith(\"pk_\")) {\n logger.error('Invalid key format. API keys start with \"pk_\".');\n process.exit(1);\n }\n\n // Verify with the API\n logger.info(\"Verifying API key...\");\n try {\n const result = await verifyApiKey(apiKey);\n await saveApiKey(apiKey);\n\n if (opts.json) {\n logger.json({\n authenticated: true,\n email: result.user.email,\n plan: result.user.plan,\n });\n } else {\n logger.info(`✓ Logged in as ${result.user.email}`);\n logger.info(` Plan: ${result.user.plan}`);\n logger.info(\n ` Limits: ${result.limits.max_concurrent} concurrent VMs, ${result.limits.remaining_hours}h remaining`,\n );\n }\n } catch (error) {\n logger.error(\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n\n // ── gibil auth setup ──\n auth\n .command(\"setup\")\n .description(\"Configure Hetzner API token (stored in ~/.gibil/config.json)\")\n .option(\"--token <token>\", \"Hetzner API token (or enter interactively)\")\n .action(async (opts: { token?: string }) => {\n let token = opts.token;\n\n if (!token) {\n token = await prompt(\"Enter your Hetzner API token: \");\n }\n\n if (!token) {\n logger.error(\"No token provided.\");\n process.exit(1);\n }\n\n // Quick validation — try listing servers\n try {\n const res = await fetch(\"https://api.hetzner.cloud/v1/servers\", {\n headers: { Authorization: `Bearer ${token}` },\n });\n const data = (await res.json()) as { error?: { message: string } };\n if (data.error) {\n logger.error(`Invalid token: ${data.error.message}`);\n process.exit(1);\n }\n } catch {\n logger.error(\"Could not verify token with Hetzner API.\");\n process.exit(1);\n }\n\n await saveHetznerToken(token);\n logger.info(\"✓ Hetzner token saved to ~/.gibil/config.json\");\n });\n\n // ── gibil auth logout ──\n auth\n .command(\"logout\")\n .description(\"Clear stored API key\")\n .action(async () => {\n await clearApiKey();\n logger.info(\"✓ Logged out. API key removed from ~/.gibil/config.json\");\n });\n\n // ── gibil auth status ──\n auth\n .command(\"status\")\n .description(\"Show current authentication status\")\n .option(\"--json\", \"Output result as JSON\")\n .action(async (opts: { json?: boolean }) => {\n if (opts.json) setJsonMode(true);\n\n const apiKey = await getApiKey();\n\n if (!apiKey) {\n if (opts.json) {\n logger.json({ authenticated: false });\n } else {\n logger.info('Not logged in. Run \"gibil auth login\" to authenticate.');\n }\n return;\n }\n\n try {\n const result = await verifyApiKey(apiKey);\n if (opts.json) {\n logger.json({\n authenticated: true,\n email: result.user.email,\n plan: result.user.plan,\n limits: result.limits,\n });\n } else {\n logger.info(`✓ Authenticated as ${result.user.email}`);\n logger.info(` Plan: ${result.user.plan}`);\n logger.info(\n ` Concurrent VMs: ${result.limits.max_concurrent}`,\n );\n logger.info(\n ` Hours remaining: ${result.limits.remaining_hours}`,\n );\n }\n } catch {\n if (opts.json) {\n logger.json({ authenticated: false, error: \"Key verification failed\" });\n } else {\n logger.error(\n 'Stored API key is invalid. Run \"gibil auth login\" to re-authenticate.',\n );\n }\n }\n });\n}\n","import { Command } from \"commander\";\nimport { getApiKey, fetchUsage } from \"../../utils/auth.js\";\nimport { logger, setJsonMode } from \"../../utils/logger.js\";\n\nexport function registerUsageCommand(program: Command): void {\n program\n .command(\"usage\")\n .description(\"Show current month's usage and plan limits\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (opts: { json?: boolean }) => {\n if (opts.json) setJsonMode(true);\n\n const apiKey = await getApiKey();\n if (!apiKey) {\n logger.error('Not logged in. Run \"gibil auth login\" first.');\n process.exit(1);\n }\n\n try {\n const usage = await fetchUsage(apiKey);\n\n if (opts.json) {\n logger.json(usage);\n } else {\n const hoursPercent = Math.round(\n (usage.vm_hours_used / usage.vm_hours_limit) * 100,\n );\n logger.info(`Plan: ${usage.plan}`);\n logger.info(\n `VM hours: ${usage.vm_hours_used.toFixed(1)} / ${usage.vm_hours_limit}h (${hoursPercent}%)`,\n );\n logger.info(\n `Active instances: ${usage.active_instances} / ${usage.max_concurrent}`,\n );\n\n if (hoursPercent > 80) {\n logger.warn(\n \"Running low on hours. Upgrade at https://gibil.dev/pricing\",\n );\n }\n }\n } catch (error) {\n logger.error(\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n}\n","#!/usr/bin/env node\n\n/**\n * Gibil MCP Server — gives Claude transparent file + command access to remote VMs.\n *\n * Tools exposed:\n * vm_bash — run any command on the VM\n * vm_read — read a file from the VM\n * vm_write — write a file on the VM\n * vm_ls — list directory contents on the VM\n * vm_grep — search file contents on the VM\n *\n * Usage:\n * gibil mcp <instance-name>\n *\n * Or in Claude Code MCP config:\n * { \"command\": \"gibil\", \"args\": [\"mcp\", \"my-vm\"] }\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { sshExec } from \"../ssh/exec.js\";\nimport { loadActiveInstanceOrThrow } from \"../utils/store.js\";\nimport type { InstanceMetadata } from \"../types/index.js\";\n\nlet instance: InstanceMetadata;\n\nfunction exec(command: string, timeoutMs = 30_000) {\n return sshExec({\n instanceName: instance.name,\n ip: instance.ip,\n command,\n stream: false,\n timeoutMs,\n });\n}\n\nexport async function startMcpServer(instanceName: string) {\n // Validate instance exists and is running\n instance = await loadActiveInstanceOrThrow(instanceName);\n\n // Ensure git identity is configured on the VM (fallback if cloud-init failed)\n if (instance.gitIdentity) {\n const { name, email, signingKey } = instance.gitIdentity;\n const cmds = [\n `git config --global user.name '${name.replace(/'/g, \"'\\\\''\")}'`,\n `git config --global user.email '${email.replace(/'/g, \"'\\\\''\")}'`,\n ];\n if (signingKey) {\n cmds.push(\n `git config --global gpg.format ssh`,\n `git config --global user.signingkey 'key::${signingKey.replace(/'/g, \"'\\\\''\")}'`,\n `git config --global commit.gpgsign true`,\n );\n }\n exec(cmds.join(\" && \")).catch(() => {});\n }\n\n const server = new McpServer({\n name: `gibil-${instanceName}`,\n version: \"0.1.0\",\n });\n\n // ── vm_bash: run any command ──\n server.tool(\n \"vm_bash\",\n \"Run a shell command on the remote VM. Use for: builds, tests, git, package managers, any shell operation.\",\n {\n command: z.string().describe(\"Shell command to execute\"),\n working_dir: z\n .string()\n .optional()\n .describe(\"Working directory (default: /root/project)\"),\n timeout_ms: z\n .number()\n .optional()\n .describe(\"Timeout in ms (default: 30000)\"),\n },\n async ({ command, working_dir, timeout_ms }) => {\n const cwd = working_dir ?? \"/root/project\";\n const result = await exec(\n `cd ${cwd} 2>/dev/null || cd /root && ${command}`,\n timeout_ms ?? 30_000,\n );\n const output = [result.stdout, result.stderr].filter(Boolean).join(\"\\n\");\n return {\n content: [\n {\n type: \"text\" as const,\n text: output || \"(no output)\",\n },\n ],\n isError: result.exitCode !== 0,\n };\n },\n );\n\n // ── vm_read: read a file ──\n server.tool(\n \"vm_read\",\n \"Read a file from the remote VM. Returns the file contents.\",\n {\n path: z.string().describe(\"Absolute path on the VM (e.g. /root/project/src/app.ts)\"),\n offset: z.number().optional().describe(\"Start at line N (1-based)\"),\n limit: z.number().optional().describe(\"Max lines to return\"),\n },\n async ({ path, offset, limit }) => {\n let cmd = `cat -n '${path.replace(/'/g, \"'\\\\''\")}'`;\n if (offset && limit) {\n cmd = `sed -n '${offset},${offset + limit - 1}p' '${path.replace(/'/g, \"'\\\\''\")}' | cat -n`;\n } else if (offset) {\n cmd = `tail -n +${offset} '${path.replace(/'/g, \"'\\\\''\")}' | cat -n`;\n } else if (limit) {\n cmd = `head -n ${limit} '${path.replace(/'/g, \"'\\\\''\")}' | cat -n`;\n }\n\n const result = await exec(cmd);\n if (result.exitCode !== 0) {\n return {\n content: [{ type: \"text\" as const, text: `Error: ${result.stderr}` }],\n isError: true,\n };\n }\n return {\n content: [{ type: \"text\" as const, text: result.stdout }],\n };\n },\n );\n\n // ── vm_write: write/create a file ──\n server.tool(\n \"vm_write\",\n \"Write content to a file on the remote VM. Creates parent directories if needed. Overwrites existing files.\",\n {\n path: z.string().describe(\"Absolute path on the VM\"),\n content: z.string().describe(\"File content to write\"),\n },\n async ({ path, content }) => {\n // Base64 encode to avoid any escaping issues\n const encoded = Buffer.from(content).toString(\"base64\");\n const cmd = `mkdir -p \"$(dirname '${path.replace(/'/g, \"'\\\\''\")}')\" && echo '${encoded}' | base64 -d > '${path.replace(/'/g, \"'\\\\''\")}'`;\n\n const result = await exec(cmd);\n if (result.exitCode !== 0) {\n return {\n content: [{ type: \"text\" as const, text: `Error: ${result.stderr}` }],\n isError: true,\n };\n }\n return {\n content: [{ type: \"text\" as const, text: `Wrote ${path}` }],\n };\n },\n );\n\n // ── vm_ls: list directory ──\n server.tool(\n \"vm_ls\",\n \"List files and directories on the remote VM.\",\n {\n path: z\n .string()\n .optional()\n .describe(\"Directory path (default: /root/project)\"),\n glob: z\n .string()\n .optional()\n .describe(\"Glob pattern to filter (e.g. '**/*.ts')\"),\n },\n async ({ path, glob }) => {\n const dir = path ?? \"/root/project\";\n let cmd: string;\n if (glob) {\n // Use find with pattern matching\n cmd = `cd '${dir.replace(/'/g, \"'\\\\''\")}' && find . -path './${glob}' -type f 2>/dev/null | sort | head -200`;\n } else {\n cmd = `ls -la '${dir.replace(/'/g, \"'\\\\''\")}'`;\n }\n\n const result = await exec(cmd);\n if (result.exitCode !== 0) {\n return {\n content: [{ type: \"text\" as const, text: `Error: ${result.stderr}` }],\n isError: true,\n };\n }\n return {\n content: [{ type: \"text\" as const, text: result.stdout }],\n };\n },\n );\n\n // ── vm_grep: search file contents ──\n server.tool(\n \"vm_grep\",\n \"Search for a pattern in files on the remote VM. Uses ripgrep if available, falls back to grep.\",\n {\n pattern: z.string().describe(\"Regex pattern to search for\"),\n path: z\n .string()\n .optional()\n .describe(\"Directory or file to search in (default: /root/project)\"),\n include: z\n .string()\n .optional()\n .describe(\"File glob to include (e.g. '*.ts')\"),\n },\n async ({ pattern, path, include }) => {\n const dir = path ?? \"/root/project\";\n const safePattern = pattern.replace(/'/g, \"'\\\\''\");\n const safeDir = dir.replace(/'/g, \"'\\\\''\");\n\n // Try rg first, fall back to grep -rn\n let cmd: string;\n if (include) {\n const safeInclude = include.replace(/'/g, \"'\\\\''\");\n cmd = `cd '${safeDir}' && (rg -n --glob '${safeInclude}' '${safePattern}' 2>/dev/null || grep -rn --include='${safeInclude}' '${safePattern}' .) | head -100`;\n } else {\n cmd = `cd '${safeDir}' && (rg -n '${safePattern}' 2>/dev/null || grep -rn '${safePattern}' .) | head -100`;\n }\n\n const result = await exec(cmd);\n // grep returns exit 1 for \"no matches\" — not an error\n const output = result.stdout || \"(no matches)\";\n return {\n content: [{ type: \"text\" as const, text: output }],\n };\n },\n );\n\n // Start the server on stdio\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","import { Command } from \"commander\";\nimport { startMcpServer } from \"../../mcp/server.js\";\nimport { logger } from \"../../utils/logger.js\";\n\nexport function registerMcpCommand(program: Command): void {\n program\n .command(\"mcp <name>\")\n .description(\n \"Start an MCP server for a running instance (used by Claude Code)\",\n )\n .action(async (name: string) => {\n try {\n await startMcpServer(name);\n } catch (error) {\n logger.error(\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n}\n"],"mappings":"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","names":["join","getHetznerToken","mkdir","readFile","existsSync","readFile","readFile","readFile","existsSync","join","readFile","writeFile","mkdir","rm","existsSync","join","program","program","program","program","program","program","readFile","randomBytes","program","readFile","randomBytes","program","program","program"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gibil",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Ephemeral dev compute for humans and AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gibil": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"build:check": "tsc --noEmit",
|
|
15
|
+
"dev": "tsx src/cli/index.ts",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"lint": "tsc --noEmit",
|
|
19
|
+
"prepublishOnly": "pnpm build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"gibil",
|
|
23
|
+
"ephemeral",
|
|
24
|
+
"compute",
|
|
25
|
+
"cli",
|
|
26
|
+
"ai-agent"
|
|
27
|
+
],
|
|
28
|
+
"author": "Alex Mouradian",
|
|
29
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
32
|
+
"commander": "^14.0.3",
|
|
33
|
+
"dotenv": "^17.3.1",
|
|
34
|
+
"ssh2": "^1.17.0",
|
|
35
|
+
"yaml": "^2.8.2",
|
|
36
|
+
"zod": "^4.3.6"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^25.5.0",
|
|
40
|
+
"@types/ssh2": "^1.15.5",
|
|
41
|
+
"tsup": "^8.5.1",
|
|
42
|
+
"tsx": "^4.21.0",
|
|
43
|
+
"typescript": "^5.9.3",
|
|
44
|
+
"vitest": "^4.1.0"
|
|
45
|
+
}
|
|
46
|
+
}
|