kly 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"kly.mjs","names":["cachedData: ModelsDevData","PROVIDER_ID_MAP: Partial<Record<LLMProvider, string>>","caps: string[]","PROVIDER_CONFIGS: Partial<Record<LLMProvider, ProviderConfig>>","CONFIG_DIR","DEFAULT_MODELS: Record<LLMProvider, string>","PROVIDER_OPTIONS: Array<{\n value: LLMProvider;\n label: string;\n hint?: string;\n}>","lines: string[]","parts: string[]","confirm","baseURL","model","baseURL: string | undefined","options: ResourceProviderOptions","modelConfig: ModelConfigResponse | null","p","result: Record<string, unknown>","command","initMessage: SandboxInitMessage","executionResult: ExecutionCompleteMessage | null","request: IPCRequest","p","p","pc","pc","lines: string[]","output","denyRead: string[]","allowWrite: string[]","allowedDomains: string[]","sandboxConfig: SandboxRuntimeConfig","allowedDomains: string[]","allowWrite: string[]","denyRead: string[]","summary: string[]","appName","stat: Stats","result: IntegrityCheckResult","getAppIdentifier","checkApiKeyPermission","getAppSandboxConfig","launchSandbox","sandboxConfig: SandboxRuntimeConfig"],"sources":["../../src/ai/models-dev.ts","../../src/ai/provider-config.ts","../../src/ai/storage.ts","../../src/ai/types.ts","../../src/ai/models-command.ts","../../src/shared/ipc-protocol.ts","../../src/host/resource-provider.ts","../../src/host/launcher.ts","../../src/shared/constants.ts","../../src/shared/runtime-mode.ts","../../src/ui/utils/tty.ts","../../src/sandbox/ipc-client.ts","../../src/ui/components/confirm.ts","../../src/ui/components/select.ts","../../src/ui/utils/colors.ts","../../src/ui/components/table.ts","../../src/ui/utils/output.ts","../../src/permissions/index.ts","../../src/permissions/cli.ts","../../src/permissions/config-builder.ts","../../src/permissions/extract.ts","../../src/permissions/unified-prompt.ts","../../src/remote/parser.ts","../../src/remote/cache.ts","../../src/remote/fetcher.ts","../../src/remote/integrity.ts","../../src/remote/resolver.ts","../../src/remote/sumfile.ts","../../src/remote/index.ts","../../bin/kly.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { LLMProvider } from \"./types\";\n\nconst MODELS_DEV_API = \"https://models.dev/api.json\";\nconst CACHE_FILE = join(homedir(), \".kly\", \"models-cache.json\");\nconst CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours\n\n/**\n * Model data from models.dev\n */\nexport interface ModelInfo {\n id: string;\n name: string;\n family?: string;\n // Capabilities\n attachment?: boolean;\n reasoning?: boolean;\n tool_call?: boolean;\n structured_output?: boolean;\n temperature?: boolean;\n // Pricing (per million tokens)\n cost?: {\n input?: number;\n output?: number;\n cache_write?: number;\n cache_read?: number;\n };\n // Limits\n limit?: {\n context?: number;\n output?: number;\n };\n // Metadata\n knowledge?: string;\n release?: string;\n release_date?: string;\n last_updated?: string;\n modalities?: {\n input?: string[];\n output?: string[];\n };\n open_weights?: boolean;\n}\n\n/**\n * Provider data from models.dev\n */\nexport interface ProviderInfo {\n id: string;\n name: string;\n api?: string;\n env?: string;\n npm?: string;\n doc?: string;\n models: Record<string, ModelInfo>;\n}\n\n/**\n * models.dev API response\n */\nexport interface ModelsDevData {\n providers: Record<string, ProviderInfo>;\n timestamp: number;\n}\n\n/**\n * Fetch models.dev data with caching\n */\nexport async function fetchModelsDevData(\n forceRefresh = false,\n): Promise<ModelsDevData | null> {\n // Try to load from cache first\n if (!forceRefresh && existsSync(CACHE_FILE)) {\n try {\n const cached = JSON.parse(readFileSync(CACHE_FILE, \"utf-8\"));\n const age = Date.now() - cached.timestamp;\n\n if (age < CACHE_TTL) {\n return cached;\n }\n } catch (_error) {\n // Cache file corrupted, continue to fetch\n }\n }\n\n // Fetch fresh data\n try {\n const response = await fetch(MODELS_DEV_API);\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n const cachedData: ModelsDevData = {\n providers: data,\n timestamp: Date.now(),\n };\n\n // Save to cache\n try {\n writeFileSync(CACHE_FILE, JSON.stringify(cachedData, null, 2), \"utf-8\");\n } catch (_error) {\n // Ignore cache write errors\n }\n\n return cachedData;\n } catch (_error) {\n // Network error, try to return stale cache\n if (existsSync(CACHE_FILE)) {\n try {\n return JSON.parse(readFileSync(CACHE_FILE, \"utf-8\"));\n } catch {\n // Ignore\n }\n }\n return null;\n }\n}\n\n/**\n * Map our provider IDs to models.dev IDs\n */\nconst PROVIDER_ID_MAP: Partial<Record<LLMProvider, string>> = {\n openai: \"openai\",\n anthropic: \"anthropic\",\n google: \"google\",\n deepseek: \"deepseek\",\n groq: \"groq\",\n mistral: \"mistral\",\n cohere: \"cohere\",\n ollama: \"ollama\",\n};\n\n/**\n * Get provider info by our internal provider ID\n */\nexport function getProviderInfo(\n data: ModelsDevData,\n provider: LLMProvider,\n): ProviderInfo | null {\n const modelsDevId = PROVIDER_ID_MAP[provider];\n if (!modelsDevId) return null;\n\n return data.providers[modelsDevId] || null;\n}\n\n/**\n * Get all models for a provider\n */\nexport function getProviderModels(\n data: ModelsDevData,\n provider: LLMProvider,\n): ModelInfo[] {\n const providerInfo = getProviderInfo(data, provider);\n if (!providerInfo) return [];\n\n return Object.values(providerInfo.models);\n}\n\n/**\n * Get model info by ID\n */\nexport function getModelInfo(\n data: ModelsDevData,\n provider: LLMProvider,\n modelId: string,\n): ModelInfo | null {\n const providerInfo = getProviderInfo(data, provider);\n if (!providerInfo) return null;\n\n return providerInfo.models[modelId] || null;\n}\n\n/**\n * Format price for display\n */\nexport function formatPrice(pricePerMillion: number | undefined): string {\n if (pricePerMillion === undefined) return \"N/A\";\n if (pricePerMillion === 0) return \"Free\";\n\n // Format to 2 decimal places, remove trailing zeros and decimal point\n return pricePerMillion.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n\n/**\n * Format capabilities for display\n */\nexport function formatCapabilities(model: ModelInfo): string[] {\n const caps: string[] = [];\n\n if (model.tool_call) caps.push(\"Tools\");\n if (model.reasoning) caps.push(\"Reasoning\");\n if (model.structured_output) caps.push(\"JSON\");\n if (model.attachment) caps.push(\"Files\");\n\n return caps;\n}\n\n/**\n * Get logo URL for a provider\n */\nexport function getProviderLogoURL(provider: LLMProvider): string {\n const modelsDevId = PROVIDER_ID_MAP[provider];\n if (!modelsDevId) return \"\";\n\n return `https://models.dev/logos/${modelsDevId}.svg`;\n}\n","import type { LLMProvider } from \"./types\";\n\n/**\n * Provider configuration\n * Data sourced from https://models.dev/api.json\n */\ninterface ProviderConfig {\n baseURL?: string;\n docURL?: string;\n description?: string;\n}\n\n/**\n * Provider configurations\n * Based on https://models.dev/api.json\n */\nconst PROVIDER_CONFIGS: Partial<Record<LLMProvider, ProviderConfig>> = {\n openai: {\n docURL: \"https://platform.openai.com/docs\",\n description: \"OpenAI's GPT models\",\n },\n anthropic: {\n docURL: \"https://docs.anthropic.com\",\n description: \"Anthropic's Claude models\",\n },\n google: {\n docURL: \"https://ai.google.dev/docs\",\n description: \"Google's Gemini models\",\n },\n deepseek: {\n docURL: \"https://platform.deepseek.com/docs\",\n description: \"DeepSeek's AI models\",\n },\n groq: {\n baseURL: \"https://api.groq.com/openai/v1\",\n docURL: \"https://console.groq.com/docs\",\n description: \"Ultra-fast LLM inference\",\n },\n mistral: {\n docURL: \"https://docs.mistral.ai\",\n description: \"Mistral AI models\",\n },\n cohere: {\n baseURL: \"https://api.cohere.ai/v1\",\n docURL: \"https://docs.cohere.com\",\n description: \"Cohere's language models\",\n },\n ollama: {\n baseURL: \"http://localhost:11434/v1\",\n docURL: \"https://ollama.ai\",\n description: \"Local AI models\",\n },\n};\n\n/**\n * Get default base URL for a provider\n */\nexport function getDefaultBaseURL(provider: LLMProvider): string | undefined {\n return PROVIDER_CONFIGS[provider]?.baseURL;\n}\n\n/**\n * Get documentation URL for a provider\n */\nexport function getProviderDocURL(provider: LLMProvider): string | undefined {\n return PROVIDER_CONFIGS[provider]?.docURL;\n}\n\n/**\n * Get provider description\n */\nexport function getProviderDescription(\n provider: LLMProvider,\n): string | undefined {\n return PROVIDER_CONFIGS[provider]?.description;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { LLMConfig, LLMProvider } from \"./types\";\n\nconst CONFIG_DIR = join(homedir(), \".kly\");\nconst CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\n\nexport interface KlyConfig {\n currentModel?: string;\n models: Record<string, LLMConfig>;\n}\n\n/**\n * Ensure config directory exists\n */\nfunction ensureConfigDir(): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\n/**\n * Load configuration from ~/.kly/config.json\n */\nexport function loadConfig(): KlyConfig {\n ensureConfigDir();\n\n if (!existsSync(CONFIG_FILE)) {\n return { models: {} };\n }\n\n try {\n const content = readFileSync(CONFIG_FILE, \"utf-8\");\n return JSON.parse(content);\n } catch (error) {\n console.error(\"Failed to parse config file:\", error);\n return { models: {} };\n }\n}\n\n/**\n * Save configuration to ~/.kly/config.json\n */\nexport function saveConfig(config: KlyConfig): void {\n ensureConfigDir();\n writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n/**\n * Get current active model configuration\n */\nexport function getCurrentModelConfig(): LLMConfig | null {\n const config = loadConfig();\n\n if (!config.currentModel) {\n return null;\n }\n\n return config.models[config.currentModel] || null;\n}\n\n/**\n * Set a model as current\n */\nexport function setCurrentModel(modelName: string): void {\n const config = loadConfig();\n\n if (!config.models[modelName]) {\n throw new Error(`Model '${modelName}' not found in config`);\n }\n\n config.currentModel = modelName;\n saveConfig(config);\n}\n\n/**\n * Add or update a model configuration\n */\nexport function saveModelConfig(\n modelName: string,\n modelConfig: LLMConfig,\n): void {\n const config = loadConfig();\n\n config.models[modelName] = modelConfig;\n\n // Set as current if it's the first model\n if (!config.currentModel) {\n config.currentModel = modelName;\n }\n\n saveConfig(config);\n}\n\n/**\n * Remove a model configuration\n */\nexport function removeModelConfig(modelName: string): void {\n const config = loadConfig();\n\n delete config.models[modelName];\n\n // Clear current if it was removed\n if (config.currentModel === modelName) {\n config.currentModel = undefined;\n }\n\n saveConfig(config);\n}\n\n/**\n * List all configured models\n */\nexport function listModels(): Array<{\n name: string;\n config: LLMConfig;\n isCurrent: boolean;\n}> {\n const config = loadConfig();\n\n return Object.entries(config.models).map(([name, modelConfig]) => ({\n name,\n config: modelConfig,\n isCurrent: name === config.currentModel,\n }));\n}\n\n/**\n * Get provider display name\n */\nexport function getProviderDisplayName(provider: LLMProvider): string {\n const displayNames: Record<LLMProvider, string> = {\n openai: \"OpenAI\",\n anthropic: \"Anthropic\",\n google: \"Google\",\n deepseek: \"DeepSeek\",\n ollama: \"Ollama\",\n groq: \"Groq\",\n mistral: \"Mistral\",\n cohere: \"Cohere\",\n \"openai-compatible\": \"OpenAI Compatible\",\n };\n return displayNames[provider];\n}\n","/**\n * Supported LLM providers\n */\nexport type LLMProvider =\n | \"openai\"\n | \"anthropic\"\n | \"google\"\n | \"deepseek\"\n | \"ollama\"\n | \"groq\"\n | \"mistral\"\n | \"cohere\"\n | \"openai-compatible\"; // For custom OpenAI-compatible endpoints\n\n/**\n * LLM provider configuration\n */\nexport interface LLMConfig {\n provider: LLMProvider;\n apiKey?: string; // Optional for Ollama (local)\n baseURL?: string;\n model?: string;\n}\n\n/**\n * Default models for each provider\n * Based on https://models.dev/ (2025-12)\n */\nexport const DEFAULT_MODELS: Record<LLMProvider, string> = {\n openai: \"gpt-4o-mini\", // Fast, cheap ($0.15/$0.60 per 1M tokens)\n anthropic: \"claude-3-5-sonnet-20241022\", // Best balance ($3/$15 per 1M tokens)\n google: \"gemini-2.5-flash\", // Fast, cheap ($0.07/$0.30 per 1M tokens)\n deepseek: \"deepseek-v3\", // Cost-effective ($0.27/$0.41 per 1M tokens)\n ollama: \"llama3.2\", // Free, local\n groq: \"llama-3.3-70b-versatile\", // Ultra-fast inference\n mistral: \"mistral-large-2411\", // Latest Mistral\n cohere: \"command-r-plus\", // Enhanced reasoning\n \"openai-compatible\": \"gpt-4o-mini\", // Fallback\n};\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport {\n fetchModelsDevData,\n formatCapabilities,\n formatPrice,\n getModelInfo,\n getProviderModels,\n type ModelInfo,\n} from \"./models-dev\";\nimport { getDefaultBaseURL, getProviderDescription } from \"./provider-config\";\nimport {\n getProviderDisplayName,\n listModels,\n removeModelConfig,\n saveModelConfig,\n setCurrentModel,\n} from \"./storage\";\nimport type { LLMProvider } from \"./types\";\nimport { DEFAULT_MODELS } from \"./types\";\n\nconst PROVIDER_OPTIONS: Array<{\n value: LLMProvider;\n label: string;\n hint?: string;\n}> = [\n {\n value: \"openai\",\n label: \"OpenAI\",\n hint: getProviderDescription(\"openai\"),\n },\n {\n value: \"anthropic\",\n label: \"Anthropic\",\n hint: getProviderDescription(\"anthropic\"),\n },\n {\n value: \"google\",\n label: \"Google\",\n hint: getProviderDescription(\"google\"),\n },\n {\n value: \"deepseek\",\n label: \"DeepSeek\",\n hint: getProviderDescription(\"deepseek\"),\n },\n { value: \"groq\", label: \"Groq\", hint: getProviderDescription(\"groq\") },\n {\n value: \"mistral\",\n label: \"Mistral\",\n hint: getProviderDescription(\"mistral\"),\n },\n {\n value: \"cohere\",\n label: \"Cohere\",\n hint: getProviderDescription(\"cohere\"),\n },\n {\n value: \"ollama\",\n label: \"Ollama\",\n hint: getProviderDescription(\"ollama\"),\n },\n {\n value: \"openai-compatible\",\n label: \"OpenAI Compatible\",\n hint: \"Custom endpoint\",\n },\n];\n\n/**\n * Main entry point for `kly models` command\n */\nexport async function modelsCommand(): Promise<void> {\n clack.intro(pc.bgCyan(pc.black(\" kly models \")));\n\n const models = listModels();\n\n const action = (await clack.select({\n message: \"What would you like to do?\",\n options: [\n { value: \"list\", label: \"List configured models\" },\n { value: \"add\", label: \"Add a new model\" },\n {\n value: \"switch\",\n label: \"Switch current model\",\n disabled: models.length === 0,\n },\n {\n value: \"remove\",\n label: \"Remove a model\",\n disabled: models.length === 0,\n },\n ],\n })) as string;\n\n if (clack.isCancel(action)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n switch (action) {\n case \"list\":\n await listAction();\n break;\n case \"add\":\n await addAction();\n break;\n case \"switch\":\n await switchAction();\n break;\n case \"remove\":\n await removeAction();\n break;\n }\n\n clack.outro(pc.green(\"Done!\"));\n}\n\n/**\n * List all configured models\n */\nasync function listAction(): Promise<void> {\n const models = listModels();\n\n if (models.length === 0) {\n clack.note(\n \"No models configured yet.\\nRun 'kly models' and select 'Add a new model'\",\n );\n return;\n }\n\n // Try to fetch models.dev data for enhanced info\n const modelsData = await fetchModelsDevData();\n\n const lines: string[] = [];\n for (const model of models) {\n const current = model.isCurrent ? pc.green(\"✓ \") : \" \";\n const provider = getProviderDisplayName(model.config.provider);\n const modelName =\n model.config.model || DEFAULT_MODELS[model.config.provider];\n\n let line = `${current}${pc.cyan(model.name)} - ${provider} (${modelName})`;\n\n // Add pricing and capabilities if available\n if (modelsData) {\n const modelInfo = getModelInfo(\n modelsData,\n model.config.provider,\n modelName,\n );\n\n if (modelInfo) {\n const metadata = formatModelMetadata(modelInfo);\n if (metadata) {\n line += ` ${pc.dim(metadata)}`;\n }\n }\n }\n\n lines.push(line);\n }\n\n clack.note(lines.join(\"\\n\"), \"Configured models:\");\n}\n\n/**\n * Format model metadata (pricing and capabilities) for display\n */\nfunction formatModelMetadata(modelInfo: ModelInfo): string {\n const parts: string[] = [];\n\n // Add pricing\n if (\n modelInfo.cost?.input !== undefined &&\n modelInfo.cost?.output !== undefined\n ) {\n parts.push(\n `[$${formatPrice(modelInfo.cost.input)}/$${formatPrice(modelInfo.cost.output)} per 1M]`,\n );\n }\n\n // Add capabilities\n const caps = formatCapabilities(modelInfo);\n if (caps.length > 0) {\n parts.push(`[${caps.join(\", \")}]`);\n }\n\n return parts.join(\" \");\n}\n\n/**\n * Add a new model configuration\n */\nasync function addAction(): Promise<void> {\n const name = await clack.text({\n message: \"Enter a name for this model configuration:\",\n placeholder: \"my-openai\",\n validate: (value) => {\n if (!value) return \"Name is required\";\n if (listModels().some((m) => m.name === value)) {\n return \"A model with this name already exists\";\n }\n return undefined;\n },\n });\n\n if (clack.isCancel(name)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n const provider = (await clack.select({\n message: \"Select a provider:\",\n options: PROVIDER_OPTIONS,\n })) as LLMProvider;\n\n if (clack.isCancel(provider)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n // Get provider-specific configuration\n const config = await getProviderConfig(provider);\n\n saveModelConfig(name, config);\n\n clack.note(\n `Model '${pc.cyan(name)}' configured with ${getProviderDisplayName(provider)}`,\n pc.green(\"Success!\"),\n );\n}\n\n/**\n * Switch to a different model\n */\nasync function switchAction(): Promise<void> {\n const models = listModels();\n\n if (models.length === 0) {\n clack.note(\"No models configured\");\n return;\n }\n\n const modelName = (await clack.select({\n message: \"Select a model:\",\n options: models.map((m) => ({\n value: m.name,\n label: m.name,\n hint: `${getProviderDisplayName(m.config.provider)} - ${m.config.model || DEFAULT_MODELS[m.config.provider]}`,\n })),\n })) as string;\n\n if (clack.isCancel(modelName)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n setCurrentModel(modelName);\n\n clack.note(`Switched to '${pc.cyan(modelName)}'`, pc.green(\"Success!\"));\n}\n\n/**\n * Remove a model configuration\n */\nasync function removeAction(): Promise<void> {\n const models = listModels();\n\n if (models.length === 0) {\n clack.note(\"No models configured\");\n return;\n }\n\n const modelName = (await clack.select({\n message: \"Select a model to remove:\",\n options: models.map((m) => ({\n value: m.name,\n label: m.name,\n hint: `${getProviderDisplayName(m.config.provider)}`,\n })),\n })) as string;\n\n if (clack.isCancel(modelName)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n const confirm = (await clack.confirm({\n message: `Are you sure you want to remove '${modelName}'?`,\n })) as boolean;\n\n if (clack.isCancel(confirm) || !confirm) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n removeModelConfig(modelName);\n\n clack.note(`Removed '${pc.cyan(modelName)}'`, pc.green(\"Success!\"));\n}\n\n/**\n * Get provider-specific configuration\n */\nasync function getProviderConfig(provider: LLMProvider): Promise<{\n provider: LLMProvider;\n apiKey?: string;\n baseURL?: string;\n model?: string;\n}> {\n const defaultModel = DEFAULT_MODELS[provider];\n\n // Ollama doesn't need API key\n if (provider === \"ollama\") {\n const baseURL = (await clack.text({\n message: \"Ollama base URL:\",\n placeholder: \"http://localhost:11434\",\n defaultValue: \"http://localhost:11434\",\n })) as string;\n\n if (clack.isCancel(baseURL)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n const model = (await clack.text({\n message: \"Model name:\",\n placeholder: defaultModel,\n defaultValue: defaultModel,\n })) as string;\n\n if (clack.isCancel(model)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return {\n provider,\n baseURL: baseURL || \"http://localhost:11434\",\n model: model || defaultModel,\n };\n }\n\n // Other providers need API key\n const apiKey = (await clack.password({\n message: `Enter your ${getProviderDisplayName(provider)} API key:`,\n validate: (value) => {\n if (!value) return \"API key is required\";\n return undefined;\n },\n })) as string;\n\n if (clack.isCancel(apiKey)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n // Ask for optional base URL\n const customBaseURL = (await clack.confirm({\n message: \"Do you want to specify a custom base URL?\",\n initialValue: false,\n })) as boolean;\n\n if (clack.isCancel(customBaseURL)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n let baseURL: string | undefined;\n\n if (customBaseURL) {\n const defaultURL = getDefaultBaseURL(provider);\n const baseURLInput = (await clack.text({\n message: \"Base URL:\",\n placeholder: defaultURL || \"\",\n })) as string;\n\n if (clack.isCancel(baseURLInput)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n baseURL = baseURLInput || undefined;\n }\n\n // Try to fetch models from models.dev and let user select\n const model = await selectModelForProvider(provider, defaultModel);\n\n return {\n provider,\n apiKey,\n baseURL,\n model,\n };\n}\n\n/**\n * Let user select a model for a provider\n */\nasync function selectModelForProvider(\n provider: LLMProvider,\n defaultModel: string,\n): Promise<string | undefined> {\n const modelsData = await fetchModelsDevData();\n\n // If we have models.dev data and models are available, show selection\n if (modelsData) {\n const availableModels = getProviderModels(modelsData, provider);\n\n if (availableModels.length > 0) {\n return await selectFromModelList(availableModels, defaultModel);\n }\n }\n\n // Fallback to simple confirm/input\n return await selectModelWithInput(defaultModel);\n}\n\n/**\n * Show model selection list with pricing and capabilities\n */\nasync function selectFromModelList(\n availableModels: ModelInfo[],\n defaultModel: string,\n): Promise<string | undefined> {\n const modelOptions = availableModels.slice(0, 10).map((m) => {\n const parts: string[] = [];\n\n // Add pricing if available\n if (m.cost?.input !== undefined && m.cost?.output !== undefined) {\n parts.push(\n `$${formatPrice(m.cost.input)}/$${formatPrice(m.cost.output)} per 1M`,\n );\n }\n\n // Add capabilities\n const caps = formatCapabilities(m);\n if (caps.length > 0) {\n parts.push(caps.join(\", \"));\n }\n\n return {\n value: m.id,\n label: m.name || m.id,\n hint: parts.length > 0 ? parts.join(\" • \") : \"No info available\",\n };\n });\n\n // Add option to use default or enter custom\n modelOptions.push({\n value: \"__default__\",\n label: `Use default (${defaultModel})`,\n hint: \"Recommended\",\n });\n modelOptions.push({\n value: \"__custom__\",\n label: \"Enter custom model name\",\n hint: \"Advanced\",\n });\n\n const selectedModel = (await clack.select({\n message: \"Select a model:\",\n options: modelOptions,\n })) as string;\n\n if (clack.isCancel(selectedModel)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n if (selectedModel === \"__default__\") {\n return undefined; // Use default\n }\n\n if (selectedModel === \"__custom__\") {\n return await promptForModelName(defaultModel);\n }\n\n return selectedModel;\n}\n\n/**\n * Simple model selection via confirm + input\n */\nasync function selectModelWithInput(\n defaultModel: string,\n): Promise<string | undefined> {\n const useDefault = (await clack.confirm({\n message: `Use default model (${defaultModel})?`,\n initialValue: true,\n })) as boolean;\n\n if (clack.isCancel(useDefault)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n if (useDefault) {\n return undefined;\n }\n\n return await promptForModelName(defaultModel);\n}\n\n/**\n * Prompt user to enter a custom model name\n */\nasync function promptForModelName(defaultModel: string): Promise<string> {\n const modelInput = (await clack.text({\n message: \"Model name:\",\n placeholder: defaultModel,\n defaultValue: defaultModel,\n })) as string;\n\n if (clack.isCancel(modelInput)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return modelInput || defaultModel;\n}\n","import type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport type { ModelConfig } from \"../types\";\n\n/**\n * Message sent from Host to Sandbox on initialization\n */\nexport interface SandboxInitMessage {\n type: \"init\";\n scriptPath: string;\n args: string[];\n appId: string;\n permissions: {\n allowApiKey: boolean;\n sandboxConfig: SandboxRuntimeConfig;\n };\n}\n\n/**\n * Request types sent from Sandbox to Host\n */\nexport type IPCRequest =\n | {\n type: \"getModelConfig\";\n id: string;\n payload: { name?: string };\n }\n | {\n type: \"listModels\";\n id: string;\n payload: Record<string, never>;\n }\n | {\n type: \"log\";\n id: string;\n payload: { level: \"info\" | \"warn\" | \"error\"; message: string };\n }\n | {\n type: \"prompt:input\";\n id: string;\n payload: {\n prompt: string;\n defaultValue?: string;\n placeholder?: string;\n maxLength?: number;\n };\n }\n | {\n type: \"prompt:select\";\n id: string;\n payload: {\n prompt: string;\n options: Array<{\n name: string;\n description?: string;\n value: string;\n }>;\n };\n }\n | {\n type: \"prompt:confirm\";\n id: string;\n payload: {\n message: string;\n defaultValue?: boolean;\n };\n }\n | {\n type: \"prompt:multiselect\";\n id: string;\n payload: {\n prompt: string;\n options: Array<{\n name: string;\n description?: string;\n value: string;\n }>;\n required?: boolean;\n };\n }\n | {\n type: \"prompt:form\";\n id: string;\n payload: {\n title?: string;\n fields: Array<{\n name: string;\n label: string;\n type: \"string\" | \"number\" | \"boolean\" | \"enum\";\n required?: boolean;\n defaultValue?: unknown;\n description?: string;\n enumValues?: string[];\n }>;\n };\n };\n\n/**\n * Response types sent from Host to Sandbox\n */\nexport type IPCResponse<T = unknown> =\n | {\n type: \"response\";\n id: string;\n success: true;\n data: T;\n }\n | {\n type: \"response\";\n id: string;\n success: false;\n error: string;\n };\n\n/**\n * Model info response (without sensitive data)\n */\nexport interface ModelInfoResponse {\n name: string;\n provider: string;\n model?: string;\n isCurrent: boolean;\n}\n\n/**\n * Model config response (with sensitive data like API keys)\n */\nexport interface ModelConfigResponse extends ModelConfig {\n provider: string;\n model?: string;\n apiKey?: string;\n baseURL?: string;\n}\n\n/**\n * Message sent from Sandbox to Host when execution completes\n */\nexport interface ExecutionCompleteMessage {\n type: \"complete\";\n success: boolean;\n result?: unknown;\n error?: string;\n}\n\n/**\n * Type guard for IPC messages\n */\nexport function isIPCRequest(msg: unknown): msg is IPCRequest {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n \"id\" in msg &&\n typeof msg.id === \"string\"\n );\n}\n\nexport function isIPCResponse(msg: unknown): msg is IPCResponse {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"response\" &&\n \"id\" in msg &&\n typeof msg.id === \"string\"\n );\n}\n\nexport function isSandboxInitMessage(msg: unknown): msg is SandboxInitMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"init\"\n );\n}\n\nexport function isExecutionCompleteMessage(\n msg: unknown,\n): msg is ExecutionCompleteMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"complete\"\n );\n}\n","import type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { getCurrentModelConfig, listModels } from \"../ai/storage\";\nimport type {\n IPCRequest,\n IPCResponse,\n ModelConfigResponse,\n ModelInfoResponse,\n} from \"../shared/ipc-protocol\";\n\nexport interface ResourceProviderOptions {\n appId: string;\n allowApiKey: boolean;\n sandboxConfig: SandboxRuntimeConfig;\n}\n\n/**\n * Resource Provider - Host-side IPC server\n * Handles resource requests from the sandboxed child process\n * Enforces permissions and provides controlled access to sensitive resources\n */\nexport class ResourceProvider {\n constructor(private options: ResourceProviderOptions) {}\n\n /**\n * Handle an IPC request from sandbox\n */\n async handle(request: IPCRequest): Promise<IPCResponse> {\n try {\n switch (request.type) {\n case \"listModels\":\n return this.handleListModels(request.id);\n\n case \"getModelConfig\":\n return this.handleGetModelConfig(request.id, request.payload.name);\n\n case \"log\":\n return this.handleLog(\n request.id,\n request.payload.level,\n request.payload.message,\n );\n\n case \"prompt:input\":\n return this.handlePromptInput(request.id, request.payload);\n\n case \"prompt:select\":\n return this.handlePromptSelect(request.id, request.payload);\n\n case \"prompt:confirm\":\n return this.handlePromptConfirm(request.id, request.payload);\n\n case \"prompt:multiselect\":\n return this.handlePromptMultiselect(request.id, request.payload);\n\n case \"prompt:form\":\n return this.handlePromptForm(request.id, request.payload);\n\n default: {\n const unknownRequest = request as { type: string; id: string };\n return {\n type: \"response\",\n id: unknownRequest.id,\n success: false,\n error: `Unknown request type: ${unknownRequest.type}`,\n };\n }\n }\n } catch (error) {\n return {\n type: \"response\",\n id: request.id,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * Handle: List available models (no permission required)\n */\n private handleListModels(\n requestId: string,\n ): IPCResponse<ModelInfoResponse[]> {\n const models = listModels();\n const response: ModelInfoResponse[] = models.map((m) => ({\n name: m.name,\n provider: m.config.provider,\n model: m.config.model,\n isCurrent: m.isCurrent,\n }));\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: response,\n };\n }\n\n /**\n * Handle: Get model config with API key (requires permission)\n */\n private handleGetModelConfig(\n requestId: string,\n name?: string,\n ): IPCResponse<ModelConfigResponse | null> {\n // Check permission\n if (!this.options.allowApiKey) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Permission denied: API key access not allowed for this app\",\n };\n }\n\n // Get model config\n let modelConfig: ModelConfigResponse | null = null;\n\n if (name) {\n const models = listModels();\n const found = models.find((m) => m.name === name);\n if (found) {\n modelConfig = {\n provider: found.config.provider,\n model: found.config.model,\n apiKey: found.config.apiKey,\n baseURL: found.config.baseURL,\n };\n }\n } else {\n const current = getCurrentModelConfig();\n if (current) {\n modelConfig = {\n provider: current.provider,\n model: current.model,\n apiKey: current.apiKey,\n baseURL: current.baseURL,\n };\n }\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: modelConfig,\n };\n }\n\n /**\n * Handle: Log message (for debugging)\n */\n private handleLog(\n requestId: string,\n level: \"info\" | \"warn\" | \"error\",\n message: string,\n ): IPCResponse<void> {\n const prefix = `[Sandbox:${this.options.appId}]`;\n\n switch (level) {\n case \"info\":\n console.log(`${prefix} ${message}`);\n break;\n case \"warn\":\n console.warn(`${prefix} ${message}`);\n break;\n case \"error\":\n console.error(`${prefix} ${message}`);\n break;\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: undefined,\n };\n }\n\n /**\n * Handle: Interactive input prompt\n */\n private async handlePromptInput(\n requestId: string,\n payload: {\n prompt: string;\n defaultValue?: string;\n placeholder?: string;\n maxLength?: number;\n },\n ): Promise<IPCResponse<string>> {\n const result = await p.text({\n message: payload.prompt,\n defaultValue: payload.defaultValue,\n placeholder: payload.placeholder,\n validate: payload.maxLength\n ? (value) => {\n if (value && value.length > payload.maxLength!) {\n return `Input must be ${payload.maxLength} characters or less`;\n }\n return undefined;\n }\n : undefined,\n });\n\n if (p.isCancel(result)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result,\n };\n }\n\n /**\n * Handle: Interactive select prompt\n */\n private async handlePromptSelect(\n requestId: string,\n payload: {\n prompt: string;\n options: Array<{\n name: string;\n description?: string;\n value: string;\n }>;\n },\n ): Promise<IPCResponse<string>> {\n const mappedOptions = payload.options.map((opt) => ({\n label: opt.name,\n value: opt.value as unknown,\n ...(opt.description && { hint: opt.description }),\n }));\n\n const result = await p.select({\n message: payload.prompt,\n options: mappedOptions,\n });\n\n if (p.isCancel(result)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result as string,\n };\n }\n\n /**\n * Handle: Interactive confirm prompt\n */\n private async handlePromptConfirm(\n requestId: string,\n payload: {\n message: string;\n defaultValue?: boolean;\n },\n ): Promise<IPCResponse<boolean>> {\n const result = await p.confirm({\n message: payload.message,\n initialValue: payload.defaultValue,\n });\n\n if (p.isCancel(result)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result,\n };\n }\n\n /**\n * Handle: Interactive multiselect prompt\n */\n private async handlePromptMultiselect(\n requestId: string,\n payload: {\n prompt: string;\n options: Array<{\n name: string;\n description?: string;\n value: string;\n }>;\n required?: boolean;\n },\n ): Promise<IPCResponse<string[]>> {\n const mappedOptions = payload.options.map((opt) => ({\n label: opt.name,\n value: opt.value as unknown,\n ...(opt.description && { hint: opt.description }),\n }));\n\n const result = await p.multiselect({\n message: payload.prompt,\n options: mappedOptions,\n required: payload.required,\n });\n\n if (p.isCancel(result)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result as string[],\n };\n }\n\n /**\n * Handle: Interactive form prompt\n */\n private async handlePromptForm(\n requestId: string,\n payload: {\n title?: string;\n fields: Array<{\n name: string;\n label: string;\n type: \"string\" | \"number\" | \"boolean\" | \"enum\";\n required?: boolean;\n defaultValue?: unknown;\n description?: string;\n enumValues?: string[];\n }>;\n },\n ): Promise<IPCResponse<Record<string, unknown>>> {\n const result: Record<string, unknown> = {};\n\n if (payload.title) {\n console.log(`\\n${pc.bold(payload.title)}\\n`);\n }\n\n for (const field of payload.fields) {\n const label = field.description\n ? `${field.label} (${field.description})`\n : field.label;\n\n if (field.type === \"boolean\") {\n const value = await p.confirm({\n message: label,\n initialValue: field.defaultValue as boolean | undefined,\n });\n\n if (p.isCancel(value)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n result[field.name] = value;\n } else if (field.type === \"enum\" && field.enumValues?.length) {\n const value = await p.select({\n message: label,\n options: field.enumValues.map((v) => ({\n label: v,\n value: v,\n })),\n });\n\n if (p.isCancel(value)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n result[field.name] = value;\n } else if (field.type === \"number\") {\n const strValue = await p.text({\n message: label,\n defaultValue: field.defaultValue?.toString(),\n validate: (value) => {\n if (value && Number.isNaN(Number.parseFloat(value))) {\n return \"Please enter a valid number\";\n }\n return undefined;\n },\n });\n\n if (p.isCancel(strValue)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n result[field.name] = Number.parseFloat(strValue);\n } else {\n const value = await p.text({\n message: label,\n defaultValue: field.defaultValue as string | undefined,\n });\n\n if (p.isCancel(value)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n result[field.name] = value;\n }\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result,\n };\n }\n}\n\n/**\n * Factory function to create a resource provider\n */\nexport function createResourceProvider(\n options: ResourceProviderOptions,\n): ResourceProvider {\n return new ResourceProvider(options);\n}\n","import { spawn } from \"node:child_process\";\nimport { resolve } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { SandboxManager } from \"@anthropic-ai/sandbox-runtime\";\nimport type {\n ExecutionCompleteMessage,\n SandboxInitMessage,\n} from \"../shared/ipc-protocol\";\nimport {\n isExecutionCompleteMessage,\n isIPCRequest,\n} from \"../shared/ipc-protocol\";\nimport { createResourceProvider } from \"./resource-provider\";\n\nexport interface LaunchOptions {\n scriptPath: string;\n args: string[];\n appId: string;\n sandboxConfig: SandboxRuntimeConfig;\n allowApiKey: boolean;\n}\n\nexport interface LaunchResult {\n exitCode: number;\n result?: unknown;\n error?: string;\n}\n\n/**\n * Launch a user script in a sandboxed child process\n * This is the Host-side launcher that:\n * 1. Spawns a child process with IPC\n * 2. Applies OS-level sandboxing\n * 3. Handles IPC communication for resource access\n * 4. Returns execution result\n */\nexport async function launchSandbox(\n options: LaunchOptions,\n): Promise<LaunchResult> {\n const { scriptPath, args, appId, sandboxConfig, allowApiKey } = options;\n\n // Initialize sandbox manager\n await SandboxManager.initialize(sandboxConfig);\n\n // Resolve paths\n const absoluteScriptPath = resolve(process.cwd(), scriptPath);\n\n // Find executor path based on current location\n // Development: src/host/launcher.ts -> src/sandbox/executor.ts\n // Production: dist/bin/kly.mjs (embedded) -> dist/sandbox/bundled-executor.mjs\n // __dirname from dist/bin/kly.mjs is dist/bin, so ../sandbox resolves to dist/sandbox\n // Use bundled-executor to avoid circular dependency issues\n const executorPath = resolve(__dirname, \"../sandbox/bundled-executor.mjs\");\n\n // Show sandbox info\n if (!SandboxManager.isSandboxingEnabled()) {\n console.warn(\"⚠️ Sandboxing is not supported on this platform.\");\n console.warn(\" Running without OS-level isolation.\");\n } else {\n console.log(\"🔒 Sandbox Configuration:\");\n console.log(\n ` Read denied: ${sandboxConfig.filesystem.denyRead.length} paths`,\n );\n console.log(\n ` Write allowed: ${sandboxConfig.filesystem.allowWrite.length} paths`,\n );\n console.log(\n ` Network: ${sandboxConfig.network.allowedDomains.join(\", \") || \"none\"}`,\n );\n console.log(\"\");\n }\n\n // Create the command to run in sandbox\n const command = `bun run ${executorPath}`;\n const wrappedCommand = await SandboxManager.wrapWithSandbox(command);\n\n // Spawn child process with IPC and inherited stdio for interactive input support\n // Using 'inherit' for stdin/stdout/stderr allows the child to directly access the TTY\n // This enables raw mode for interactive prompts (select, input, etc.)\n const child = spawn(wrappedCommand, {\n shell: true,\n stdio: [\"inherit\", \"inherit\", \"inherit\", \"ipc\"], // Inherit to support raw mode\n env: {\n ...process.env,\n KLY_SANDBOX_MODE: \"true\",\n },\n });\n\n // Create resource provider for handling IPC requests\n const resourceProvider = createResourceProvider({\n appId,\n allowApiKey,\n sandboxConfig,\n });\n\n // Handle IPC messages from sandbox\n child.on(\"message\", async (message: unknown) => {\n if (isIPCRequest(message)) {\n const response = await resourceProvider.handle(message);\n child.send(response);\n }\n });\n\n // Send initialization message to sandbox\n const initMessage: SandboxInitMessage = {\n type: \"init\",\n scriptPath: absoluteScriptPath,\n args,\n appId,\n permissions: {\n allowApiKey,\n sandboxConfig,\n },\n };\n child.send(initMessage);\n\n // Wait for execution to complete\n return new Promise((resolve, reject) => {\n let executionResult: ExecutionCompleteMessage | null = null;\n\n child.on(\"message\", (message: unknown) => {\n if (isExecutionCompleteMessage(message)) {\n executionResult = message;\n }\n });\n\n child.on(\"error\", (error) => {\n reject(new Error(`Sandbox process error: ${error.message}`));\n });\n\n child.on(\"exit\", (code) => {\n if (executionResult) {\n resolve({\n exitCode: code ?? 0,\n result: executionResult.result,\n error: executionResult.error,\n });\n } else {\n resolve({\n exitCode: code ?? 1,\n error: code !== 0 ? `Process exited with code ${code}` : undefined,\n });\n }\n });\n });\n}\n","/**\n * Centralized constants for the KLY project\n * Prevents magic strings and improves maintainability\n */\n\n/**\n * Environment variable names used throughout the application\n */\nexport const ENV_VARS = {\n SANDBOX_MODE: \"KLY_SANDBOX_MODE\",\n MCP_MODE: \"KLY_MCP_MODE\",\n PROGRAMMATIC: \"KLY_PROGRAMMATIC\",\n TRUST_ALL: \"KLY_TRUST_ALL\",\n LOCAL_REF: \"KLY_LOCAL_REF\",\n REMOTE_REF: \"KLY_REMOTE_REF\",\n} as const;\n\n/**\n * File and directory paths used for configuration and caching\n */\nexport const PATHS = {\n CONFIG_DIR: \".kly\",\n META_FILE: \".kly-meta.json\",\n PERMISSIONS_FILE: \"permissions.json\",\n CONFIG_FILE: \"config.json\",\n} as const;\n\n/**\n * Timeout values in milliseconds\n */\nexport const TIMEOUTS = {\n /** Standard IPC request timeout (30 seconds) */\n IPC_REQUEST: 30_000,\n /** Long-running IPC request timeout (60 seconds) */\n IPC_LONG_REQUEST: 60_000,\n} as const;\n\n/**\n * LLM API domains for network permission configuration\n */\nexport const LLM_API_DOMAINS = [\n \"api.openai.com\",\n \"*.anthropic.com\",\n \"generativelanguage.googleapis.com\",\n \"api.deepseek.com\",\n] as const;\n","/**\n * Runtime mode detection utilities\n * Centralizes environment variable checks for different execution modes\n */\n\nimport type { RuntimeMode } from \"../types\";\nimport { ENV_VARS } from \"./constants\";\n\n/**\n * Check if running in sandbox mode\n * Sandbox mode: Isolated child process with restricted permissions\n */\nexport function isSandbox(): boolean {\n return process.env[ENV_VARS.SANDBOX_MODE] === \"true\";\n}\n\n/**\n * Check if running in MCP (Model Context Protocol) mode\n * MCP mode: Running as an MCP server for Claude Desktop integration\n */\nexport function isMCP(): boolean {\n return process.env[ENV_VARS.MCP_MODE] === \"true\";\n}\n\n/**\n * Check if running in programmatic mode\n * Programmatic mode: Imported as a library in another application\n */\nexport function isProgrammatic(): boolean {\n return process.env[ENV_VARS.PROGRAMMATIC] === \"true\";\n}\n\n/**\n * Check if running with trust all flag\n * Trust all: Skip permission prompts (for testing/automation)\n */\nexport function isTrustAll(): boolean {\n return process.env[ENV_VARS.TRUST_ALL] === \"true\";\n}\n\n/**\n * Get local reference environment variable\n */\nexport function getLocalRef(): string | undefined {\n return process.env[ENV_VARS.LOCAL_REF];\n}\n\n/**\n * Get remote reference environment variable\n */\nexport function getRemoteRef(): string | undefined {\n return process.env[ENV_VARS.REMOTE_REF];\n}\n\n/**\n * Detect the current runtime mode\n * This is the canonical implementation that should be used throughout the app\n */\nexport function detectMode(): RuntimeMode {\n // Sandbox mode: Running inside sandboxed child process\n if (isSandbox()) {\n return \"cli\"; // Sandbox always runs in CLI mode\n }\n\n // MCP mode: Check for MCP environment variable\n if (isMCP()) {\n return \"mcp\";\n }\n\n // Programmatic mode: Check for explicit flag\n if (isProgrammatic()) {\n return \"programmatic\";\n }\n\n // CLI mode: Running a .ts file directly with bun\n const scriptPath = process.argv[1] ?? \"\";\n const isDirectRun = scriptPath.endsWith(\".ts\") || scriptPath.endsWith(\".js\");\n if (isDirectRun) {\n return \"cli\";\n }\n\n // Default to programmatic (e.g., when imported as a module)\n return \"programmatic\";\n}\n","/**\n * Check if we're in a TTY environment\n * Returns false in CI or non-interactive environments\n */\nexport function isTTY(): boolean {\n return Boolean(\n process.stdout.isTTY && process.stdin.isTTY && !process.env.CI,\n );\n}\n","import { TIMEOUTS } from \"../shared/constants\";\nimport type { IPCRequest, IPCResponse } from \"../shared/ipc-protocol\";\n\n/**\n * Send an IPC request to the host and wait for response\n * Used by UI components and other sandbox code to communicate with the host process\n */\nexport async function sendIPCRequest<T>(\n type: IPCRequest[\"type\"],\n payload: unknown,\n): Promise<T> {\n if (!process.send) {\n throw new Error(\"IPC not available - not running in sandbox mode\");\n }\n\n return new Promise((resolve, reject) => {\n const requestId = `${type}-${Date.now()}-${Math.random()}`;\n\n const request: IPCRequest = {\n type,\n id: requestId,\n payload,\n } as IPCRequest;\n\n // Set up response listener\n const responseHandler = (message: unknown) => {\n if (\n typeof message === \"object\" &&\n message !== null &&\n \"type\" in message &&\n message.type === \"response\" &&\n \"id\" in message &&\n message.id === requestId\n ) {\n process.off(\"message\", responseHandler);\n\n const response = message as IPCResponse<T>;\n if (response.success) {\n resolve(response.data);\n } else {\n reject(new Error(response.error));\n }\n }\n };\n\n process.on(\"message\", responseHandler);\n\n // Send request\n if (!process.send!(request)) {\n // Send failed immediately\n process.off(\"message\", responseHandler);\n reject(new Error(\"Failed to send IPC message\"));\n return;\n }\n\n // Timeout for long-running requests (prompts, etc.)\n setTimeout(() => {\n process.off(\"message\", responseHandler);\n reject(new Error(`IPC request timeout: ${type}`));\n }, TIMEOUTS.IPC_LONG_REQUEST);\n });\n}\n","import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Simplified confirm function\n *\n * @example\n * ```typescript\n * const proceed = await confirm(\"Continue?\", true);\n * ```\n */\nexport async function confirm(\n message: string,\n defaultValue = false,\n): Promise<boolean> {\n // Sandbox mode: use IPC to request confirm from host\n if (isSandbox()) {\n return sendIPCRequest<boolean>(\"prompt:confirm\", { message, defaultValue });\n }\n\n if (!isTTY()) {\n // In MCP mode, warn about using default value for confirmation\n if (isMCP()) {\n console.warn(\n `[MCP Warning] Interactive confirmation not available. Using default value (${defaultValue}) for: ${message}`,\n );\n }\n return defaultValue;\n }\n\n const result = await p.confirm({\n message,\n initialValue: defaultValue,\n });\n\n if (p.isCancel(result)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return result;\n}\n","import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Option for select menus\n */\nexport interface SelectOption<T = string> {\n /** Display name */\n name: string;\n /** Optional description shown below name */\n description?: string;\n /** Value returned when selected */\n value: T;\n /** Disable this option */\n disabled?: boolean;\n}\n\n/**\n * Configuration for select component\n */\nexport interface SelectConfig<T> {\n /** Options to choose from */\n options: SelectOption<T>[];\n /** Prompt message */\n prompt?: string;\n}\n\n/**\n * Show a selection menu and wait for user choice\n *\n * @example\n * ```typescript\n * const color = await select({\n * options: [\n * { name: \"Red\", value: \"red\" },\n * { name: \"Blue\", value: \"blue\", description: \"Ocean color\" },\n * ],\n * prompt: \"Pick a color\"\n * });\n * ```\n */\nexport async function select<T = string>(config: SelectConfig<T>): Promise<T> {\n // Sandbox mode: use IPC to request select from host\n if (isSandbox()) {\n return sendIPCRequest<T>(\"prompt:select\", {\n prompt: config.prompt ?? \"Select an option\",\n options: config.options,\n });\n }\n\n // Non-TTY fallback: auto-select first option or throw in MCP mode\n if (!isTTY()) {\n // In MCP mode, interactive selection is not allowed\n if (isMCP()) {\n throw new Error(\n `Interactive selection not available in MCP mode. All parameters must be defined in the tool's inputSchema. Selection prompt: ${config.prompt}`,\n );\n }\n\n const firstOption = config.options[0];\n if (!firstOption) {\n throw new Error(\"No options provided\");\n }\n return firstOption.value;\n }\n\n // Map to @clack/prompts Option format\n // Using type assertion due to complex Option<T> conditional types\n const mappedOptions = config.options.map((opt) => ({\n label: opt.name,\n value: opt.value as unknown,\n ...(opt.description && { hint: opt.description }),\n }));\n\n const result = await p.select({\n message: config.prompt ?? \"Select an option\",\n options: mappedOptions,\n });\n\n if (p.isCancel(result)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return result as T;\n}\n","import pc from \"picocolors\";\n\n/**\n * Default color theme for UI components (hex values for reference)\n */\nexport const theme = {\n // Brand colors\n primary: \"#3b82f6\", // Blue\n success: \"#10b981\", // Green\n warning: \"#f59e0b\", // Orange\n error: \"#ef4444\", // Red\n info: \"#06b6d4\", // Cyan\n\n // UI colors\n background: \"#161b22\", // Dark gray\n surface: \"#1e293b\", // Lighter gray\n border: \"#30363d\", // Border gray\n text: \"#c9d1d9\", // Light text\n textDim: \"#8b949e\", // Dimmed text\n textBright: \"#ffffff\", // Bright text\n\n // Interactive states\n focused: \"#3b82f6\", // Blue\n selected: \"#3b82f6\", // Blue\n hover: \"#334155\", // Lighter gray\n disabled: \"#6e7681\", // Muted gray\n} as const;\n\nexport type AnsiColor =\n | \"red\"\n | \"green\"\n | \"yellow\"\n | \"blue\"\n | \"magenta\"\n | \"cyan\"\n | \"white\"\n | \"gray\";\n\n/**\n * Format text with picocolors\n */\nexport function formatText(\n text: string,\n options?: {\n color?: AnsiColor;\n bold?: boolean;\n dim?: boolean;\n italic?: boolean;\n underline?: boolean;\n },\n): string {\n let result = text;\n\n // Apply color first\n if (options?.color) {\n switch (options.color) {\n case \"red\":\n result = pc.red(result);\n break;\n case \"green\":\n result = pc.green(result);\n break;\n case \"yellow\":\n result = pc.yellow(result);\n break;\n case \"blue\":\n result = pc.blue(result);\n break;\n case \"magenta\":\n result = pc.magenta(result);\n break;\n case \"cyan\":\n result = pc.cyan(result);\n break;\n case \"white\":\n result = pc.white(result);\n break;\n case \"gray\":\n result = pc.gray(result);\n break;\n }\n }\n\n // Apply styles\n if (options?.bold) result = pc.bold(result);\n if (options?.dim) result = pc.dim(result);\n if (options?.italic) result = pc.italic(result);\n if (options?.underline) result = pc.underline(result);\n\n return result;\n}\n\n// Re-export picocolors for direct usage\nexport { pc };\n","import { formatText, pc } from \"../utils/colors\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Column alignment options\n */\nexport type ColumnAlign = \"left\" | \"center\" | \"right\";\n\n/**\n * Column definition for table\n */\nexport interface TableColumn<T = Record<string, unknown>> {\n /** Column key in the data object */\n key: keyof T;\n /** Display header text */\n header: string;\n /** Column alignment (default: left) */\n align?: ColumnAlign;\n /** Fixed column width (auto-calculated if not specified) */\n width?: number;\n /** Custom formatter function for cell values */\n formatter?: (value: unknown, row: T) => string;\n}\n\n/**\n * Configuration for table component\n */\nexport interface TableConfig<T = Record<string, unknown>> {\n /** Column definitions */\n columns: TableColumn<T>[];\n /** Data rows */\n rows: T[];\n /** Show header row (default: true) */\n showHeader?: boolean;\n /** Show borders (default: true in TTY mode) */\n showBorders?: boolean;\n /** Table title (optional) */\n title?: string;\n}\n\n/**\n * Align text within a given width\n */\nfunction alignText(text: string, width: number, align: ColumnAlign): string {\n const textLength = stripAnsi(text).length;\n const padding = Math.max(0, width - textLength);\n\n switch (align) {\n case \"right\":\n return \" \".repeat(padding) + text;\n case \"center\": {\n const leftPad = Math.floor(padding / 2);\n const rightPad = padding - leftPad;\n return \" \".repeat(leftPad) + text + \" \".repeat(rightPad);\n }\n default:\n return text + \" \".repeat(padding);\n }\n}\n\n/**\n * Strip ANSI escape codes from string for length calculation\n */\nfunction stripAnsi(str: string): string {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI codes regex\n return str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\n/**\n * Calculate column widths based on content\n */\nfunction calculateColumnWidths<T>(\n columns: TableColumn<T>[],\n rows: T[],\n showHeader: boolean,\n): number[] {\n return columns.map((col) => {\n // Use fixed width if specified\n if (col.width !== undefined) {\n return col.width;\n }\n\n // Calculate max width from header and data\n let maxWidth = showHeader ? col.header.length : 0;\n\n for (const row of rows) {\n const value = row[col.key];\n const formatted = col.formatter\n ? col.formatter(value, row)\n : String(value ?? \"\");\n const length = stripAnsi(formatted).length;\n maxWidth = Math.max(maxWidth, length);\n }\n\n return maxWidth;\n });\n}\n\n/**\n * Format a single cell value\n */\nfunction formatCell<T>(value: unknown, row: T, column: TableColumn<T>): string {\n if (column.formatter) {\n return column.formatter(value, row);\n }\n\n if (value === null || value === undefined) {\n return pc.dim(\"-\");\n }\n\n return String(value);\n}\n\n/**\n * Render table in TTY mode with borders and styling\n */\nfunction renderTTY<T>(config: TableConfig<T>): string {\n const {\n columns,\n rows,\n showHeader = true,\n showBorders = true,\n title,\n } = config;\n const lines: string[] = [];\n\n // Calculate column widths\n const widths = calculateColumnWidths(columns, rows, showHeader);\n\n // Title\n if (title) {\n lines.push(\"\");\n lines.push(formatText(`${title}`, { bold: true }));\n lines.push(\"\");\n }\n\n // Header row\n if (showHeader) {\n const headerCells = columns.map((col, i) => {\n const text = formatText(col.header, { bold: true, color: \"cyan\" });\n return alignText(text, widths[i]!, col.align ?? \"left\");\n });\n\n lines.push(headerCells.join(\" \"));\n\n // Header separator\n if (showBorders) {\n const separatorParts = widths.map((w) => \"─\".repeat(w));\n lines.push(pc.gray(separatorParts.join(\"─\")));\n }\n }\n\n // Data rows\n for (const row of rows) {\n const cells = columns.map((col, i) => {\n const value = row[col.key];\n const formatted = formatCell(value, row, col);\n return alignText(formatted, widths[i]!, col.align ?? \"left\");\n });\n\n lines.push(cells.join(\" \"));\n }\n\n // Bottom border (optional)\n if (showBorders && rows.length > 0) {\n const separatorParts = widths.map((w) => \"─\".repeat(w));\n lines.push(pc.gray(separatorParts.join(\"─\")));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Render table in non-TTY mode (plain text)\n */\nfunction renderPlain<T>(config: TableConfig<T>): string {\n const { columns, rows, showHeader = true, title } = config;\n const lines: string[] = [];\n\n // Calculate column widths\n const widths = calculateColumnWidths(columns, rows, showHeader);\n\n // Title\n if (title) {\n lines.push(\"\");\n lines.push(title);\n lines.push(\"\");\n }\n\n // Header row\n if (showHeader) {\n const headerCells = columns.map((col, i) =>\n alignText(col.header, widths[i]!, col.align ?? \"left\"),\n );\n lines.push(headerCells.join(\" \"));\n\n // Separator\n const separator = columns.map((_, i) => \"-\".repeat(widths[i]!)).join(\" \");\n lines.push(separator);\n }\n\n // Data rows\n for (const row of rows) {\n const cells = columns.map((col, i) => {\n const value = row[col.key];\n const formatted = formatCell(value, row, col);\n return alignText(stripAnsi(formatted), widths[i]!, col.align ?? \"left\");\n });\n lines.push(cells.join(\" \"));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Display a table with columns and rows\n *\n * @example\n * ```typescript\n * table({\n * title: \"Users\",\n * columns: [\n * { key: \"name\", header: \"Name\" },\n * { key: \"age\", header: \"Age\", align: \"right\" },\n * { key: \"status\", header: \"Status\", formatter: (val) =>\n * val === \"active\" ? pc.green(\"✓ Active\") : pc.red(\"✗ Inactive\")\n * },\n * ],\n * rows: [\n * { name: \"Alice\", age: 25, status: \"active\" },\n * { name: \"Bob\", age: 30, status: \"inactive\" },\n * ],\n * });\n * ```\n */\nexport function table<T = Record<string, unknown>>(\n config: TableConfig<T>,\n): void {\n const output = isTTY() ? renderTTY(config) : renderPlain(config);\n console.log(output);\n}\n","import { formatText } from \"./colors\";\n\n/**\n * Output a result to the console\n *\n * @param result - The result to display (string, object, etc.)\n */\nexport function output(result: unknown): void {\n if (result === undefined || result === null) {\n return;\n }\n\n if (typeof result === \"string\") {\n console.log(result);\n } else {\n console.log(JSON.stringify(result, null, 2));\n }\n}\n\n/**\n * Display an error message with optional suggestions\n *\n * @param message - Error message\n * @param suggestions - Optional suggestions for fixing the error\n */\nexport function error(message: string, suggestions?: string[]): void {\n console.error(\n `${formatText(\"Error:\", { color: \"red\", bold: true })} ${message}`,\n );\n\n if (suggestions?.length) {\n console.error(\"\");\n console.error(formatText(\"Suggestions:\", { dim: true }));\n for (const suggestion of suggestions) {\n console.error(` ${formatText(\"•\", { dim: true })} ${suggestion}`);\n }\n }\n}\n\n/**\n * Display help text\n *\n * @param content - Help text content\n */\nexport function help(content: string): void {\n console.log(content);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { PATHS } from \"../shared/constants\";\nimport { getLocalRef, getRemoteRef, isTrustAll } from \"../shared/runtime-mode\";\nimport { select } from \"../ui\";\nimport { isTTY } from \"../ui/utils/tty\";\n\nconst CONFIG_DIR = join(homedir(), PATHS.CONFIG_DIR);\nconst PERMISSIONS_FILE = join(CONFIG_DIR, PATHS.PERMISSIONS_FILE);\n\n/**\n * Permission record for an app\n * Only \"always\" choices are stored\n */\ninterface PermissionRecord {\n /** When the permission was granted */\n timestamp: string;\n /** User's choice: always \"always\" (only stored choice) */\n choice: \"always\";\n /** Sandbox configuration (optional, for sandboxed execution) */\n sandboxConfig?: SandboxRuntimeConfig;\n}\n\n/**\n * Permissions configuration\n */\ninterface PermissionsConfig {\n /** Trusted apps with their permissions */\n trustedApps: Record<string, PermissionRecord>;\n}\n\n/**\n * Get app identifier from script path\n */\nexport function getAppIdentifier(): string {\n // Check for explicit local file reference (set by kly run command)\n const localRef = getLocalRef();\n if (localRef) {\n return localRef;\n }\n\n // Remote app (from environment variable set by remote loader)\n const remoteRef = getRemoteRef();\n if (remoteRef) {\n return remoteRef;\n }\n\n // Fallback to script path for direct execution\n const scriptPath = process.argv[1] ?? \"\";\n if (scriptPath.startsWith(\"/\") || scriptPath.startsWith(\"C:\\\\\")) {\n return `local:${scriptPath}`;\n }\n\n // Default to script path\n return scriptPath || \"unknown\";\n}\n\n/**\n * Get friendly app name for display\n */\nexport function getAppName(appId: string): string {\n if (appId.startsWith(\"local:\")) {\n const path = appId.slice(6);\n const parts = path.split(\"/\");\n return parts[parts.length - 1] || path;\n }\n\n if (appId.startsWith(\"github.com/\")) {\n const parts = appId.split(\"/\");\n return parts.slice(1, 3).join(\"/\");\n }\n\n return appId;\n}\n\n/**\n * Ensure permissions config directory exists\n */\nfunction ensurePermissionsDir(): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\n/**\n * Load permissions configuration\n */\nexport function loadPermissions(): PermissionsConfig {\n ensurePermissionsDir();\n\n if (!existsSync(PERMISSIONS_FILE)) {\n return { trustedApps: {} };\n }\n\n try {\n const content = readFileSync(PERMISSIONS_FILE, \"utf-8\");\n return JSON.parse(content);\n } catch (error) {\n console.error(\"Failed to parse permissions file:\", error);\n return { trustedApps: {} };\n }\n}\n\n/**\n * Save permissions configuration\n */\nexport function savePermissions(config: PermissionsConfig): void {\n ensurePermissionsDir();\n writeFileSync(PERMISSIONS_FILE, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n/**\n * Request permission from user with interactive prompt\n */\nasync function requestPermission(\n appId: string,\n appName: string,\n): Promise<boolean> {\n // Check if running in TTY mode\n if (!isTTY()) {\n console.error(\n `\\nPermission required: App \"${appName}\" (${appId}) wants to access your API keys.`,\n );\n console.error(\n \"Set KLY_TRUST_ALL=true environment variable to grant access in non-interactive mode.\",\n );\n return false;\n }\n\n console.log(\"\");\n console.log(`App \"${appName}\" is requesting access to your API keys.`);\n console.log(`Source: ${appId}`);\n console.log(\"\");\n console.log(\"This will allow the app to use your configured LLM models.\");\n console.log(\"\");\n\n const choice = await select({\n prompt: \"Do you want to allow this?\",\n options: [\n {\n name: \"Allow once\",\n value: \"once\",\n description: \"Allow for this session only\",\n },\n {\n name: \"Always allow\",\n value: \"always\",\n description: \"Remember this choice for future runs\",\n },\n { name: \"Cancel\", value: \"cancel\", description: \"Cancel and exit\" },\n ],\n });\n\n // Cancel - don't save, just reject\n if (choice === \"cancel\") {\n return false;\n }\n\n // Always - save to config\n if (choice === \"always\") {\n const config = loadPermissions();\n config.trustedApps[appId] = {\n timestamp: new Date().toISOString(),\n choice: \"always\",\n };\n savePermissions(config);\n return true;\n }\n\n // Once - don't save, just allow for this run\n return true;\n}\n\n/**\n * Check if an app has permission to access API keys\n * If not, prompt user for permission (in interactive mode)\n */\nexport async function checkApiKeyPermission(appId: string): Promise<boolean> {\n // Allow bypass via environment variable (for CI/automation)\n if (isTrustAll()) {\n return true;\n }\n\n // Check stored permissions\n const config = loadPermissions();\n const record = config.trustedApps[appId];\n\n // If record exists, it's always \"always allow\" (we only store grants)\n if (record && record.choice === \"always\") {\n return true;\n }\n\n // No stored permission - need to request\n const appName = getAppName(appId);\n return await requestPermission(appId, appName);\n}\n\n/**\n * Revoke permission for an app\n */\nexport function revokePermission(appId: string): void {\n const config = loadPermissions();\n delete config.trustedApps[appId];\n savePermissions(config);\n}\n\n/**\n * List all granted permissions\n * Only \"always allow\" permissions are stored\n */\nexport function listPermissions(): Array<{\n appId: string;\n appName: string;\n timestamp: string;\n choice: string;\n}> {\n const config = loadPermissions();\n\n return Object.entries(config.trustedApps).map(([appId, record]) => ({\n appId,\n appName: getAppName(appId),\n timestamp: record.timestamp,\n choice: record.choice,\n }));\n}\n\n/**\n * Request sandbox configuration from user interactively\n * Returns SandboxRuntimeConfig directly (no conversion needed)\n */\nasync function requestSandboxConfig(\n appId: string,\n appName: string,\n): Promise<SandboxRuntimeConfig | null> {\n if (!isTTY()) {\n console.error(`\\nSandbox permission required for: \"${appName}\" (${appId})`);\n console.error(\n \"Set KLY_TRUST_ALL=true environment variable to run without sandboxing in non-interactive mode.\",\n );\n return null;\n }\n\n const homeDir = homedir();\n const currentDir = process.cwd();\n\n console.log(\"\");\n console.log(`🔐 Sandbox Permission Request from: ${appName}`);\n console.log(\"\");\n\n // Ask for filesystem read permissions\n console.log(\"📂 Filesystem Read Access:\");\n const fsReadChoice = await select({\n prompt: \"Which files should be denied for reading?\",\n options: [\n {\n name: \"Sensitive only\",\n value: \"sensitive\",\n description: \"Deny access to ~/.kly, ~/.ssh, ~/.aws, etc.\",\n },\n {\n name: \"All home directory\",\n value: \"all-home\",\n description: \"Deny access to entire home directory\",\n },\n {\n name: \"None (allow all)\",\n value: \"none\",\n description: \"No read restrictions (except ~/.kly)\",\n },\n ],\n });\n\n // Always deny reading sensitive directories (hardcoded for security)\n let denyRead: string[] = [join(homeDir, \".kly\")];\n\n if (fsReadChoice === \"sensitive\") {\n denyRead = [\n join(homeDir, \".kly\"),\n join(homeDir, \".ssh\"),\n join(homeDir, \".aws\"),\n join(homeDir, \".gnupg\"),\n ];\n } else if (fsReadChoice === \"all-home\") {\n denyRead = [homeDir];\n }\n // Note: Even if user chooses \"none\", .kly is still protected\n\n // Ask for filesystem write permissions\n console.log(\"\");\n console.log(\"📝 Filesystem Write Access:\");\n const fsWriteChoice = await select({\n prompt: \"Which directories should be allowed for writing?\",\n options: [\n {\n name: \"None\",\n value: \"none\",\n description: \"No write access\",\n },\n {\n name: \"Current directory only\",\n value: \"current\",\n description: `Allow write to ${currentDir}`,\n },\n {\n name: \"Temporary directory\",\n value: \"temp\",\n description: \"Allow write to system temp directory\",\n },\n ],\n });\n\n let allowWrite: string[] = [];\n if (fsWriteChoice === \"current\") {\n allowWrite = [currentDir];\n } else if (fsWriteChoice === \"temp\") {\n const tmpdir = process.env.TMPDIR || process.env.TEMP || \"/tmp\";\n allowWrite = [tmpdir];\n }\n\n // Always deny writing to sensitive directories (hardcoded for security)\n const denyWrite = [\n join(homeDir, \".kly\"), // KLY config and permissions\n join(homeDir, \".ssh\"), // SSH keys\n join(homeDir, \".aws\"), // AWS credentials\n join(homeDir, \".gnupg\"), // GPG keys\n ];\n\n // Ask for network permissions\n console.log(\"\");\n console.log(\"🌐 Network Access:\");\n const networkChoice = await select({\n prompt: \"Which network access should be allowed?\",\n options: [\n {\n name: \"None\",\n value: \"none\",\n description: \"No network access\",\n },\n {\n name: \"LLM APIs only\",\n value: \"llm-apis\",\n description: \"OpenAI, Anthropic, Google AI\",\n },\n {\n name: \"Common APIs\",\n value: \"common\",\n description: \"LLM + GitHub, npm, etc.\",\n },\n {\n name: \"All domains\",\n value: \"all\",\n description: \"Allow all network access\",\n },\n ],\n });\n\n let allowedDomains: string[] = [];\n if (networkChoice === \"llm-apis\") {\n allowedDomains = [\n \"api.openai.com\",\n \"*.anthropic.com\",\n \"generativelanguage.googleapis.com\",\n ];\n } else if (networkChoice === \"common\") {\n allowedDomains = [\n \"api.openai.com\",\n \"*.anthropic.com\",\n \"generativelanguage.googleapis.com\",\n \"*.github.com\",\n \"registry.npmjs.org\",\n ];\n } else if (networkChoice === \"all\") {\n allowedDomains = [\"*\"];\n }\n\n // Ask how long to remember this choice\n console.log(\"\");\n const duration = await select({\n prompt: \"How long should these permissions last?\",\n options: [\n {\n name: \"One time only\",\n value: \"once\",\n description: \"Ask again next time\",\n },\n {\n name: \"Always allow\",\n value: \"always\",\n description: \"Remember for this app\",\n },\n {\n name: \"Cancel\",\n value: \"cancel\",\n description: \"Cancel and exit\",\n },\n ],\n });\n\n // Cancel - don't save, just reject\n if (duration === \"cancel\") {\n return null;\n }\n\n // Construct SandboxRuntimeConfig directly\n const sandboxConfig: SandboxRuntimeConfig = {\n network: {\n allowedDomains,\n deniedDomains: [],\n },\n filesystem: {\n denyRead,\n allowWrite,\n denyWrite,\n },\n };\n\n // Save permission record only if \"always\"\n if (duration === \"always\") {\n const config = loadPermissions();\n config.trustedApps[appId] = {\n sandboxConfig,\n timestamp: new Date().toISOString(),\n choice: \"always\",\n };\n savePermissions(config);\n }\n\n console.log(\"\");\n console.log(\"✅ Sandbox permissions granted!\");\n return sandboxConfig;\n}\n\n/**\n * Get sandbox configuration for an app\n * Returns SandboxRuntimeConfig directly (no conversion needed)\n *\n * @param appId - App identifier\n * @returns SandboxRuntimeConfig or null if denied\n */\nexport async function getAppSandboxConfig(\n appId: string,\n): Promise<SandboxRuntimeConfig | null> {\n const homeDir = homedir();\n\n // Check for trust-all bypass (for automation)\n if (isTrustAll()) {\n // Even in trust-all mode, protect sensitive directories\n return {\n network: { allowedDomains: [\"*\"], deniedDomains: [] },\n filesystem: {\n denyRead: [join(homeDir, \".kly\")], // ALWAYS deny reading KLY config\n allowWrite: [\"*\"],\n denyWrite: [\n join(homeDir, \".kly\"), // KLY config and permissions\n join(homeDir, \".ssh\"), // SSH keys\n join(homeDir, \".aws\"), // AWS credentials\n join(homeDir, \".gnupg\"), // GPG keys\n ],\n },\n };\n }\n\n const config = loadPermissions();\n const record = config.trustedApps[appId];\n\n // If permission already granted as \"always\", return cached config\n if (record?.choice === \"always\" && record.sandboxConfig) {\n return record.sandboxConfig;\n }\n\n // Request new sandbox permissions\n const appName = getAppName(appId);\n return await requestSandboxConfig(appId, appName);\n}\n\n/**\n * Clear all permissions\n */\nexport function clearAllPermissions(): void {\n savePermissions({ trustedApps: {} });\n}\n","import { confirm, output, select, table } from \"../ui\";\nimport { clearAllPermissions, listPermissions, revokePermission } from \".\";\n\n/**\n * Permissions management CLI\n */\nexport async function permissionsCommand(): Promise<void> {\n const action = await select({\n prompt: \"Permissions Management\",\n options: [\n {\n name: \"List permissions\",\n value: \"list\",\n description: \"View all granted permissions\",\n },\n {\n name: \"Revoke permission\",\n value: \"revoke\",\n description: \"Remove permission for a specific app\",\n },\n {\n name: \"Clear all\",\n value: \"clear\",\n description: \"Remove all permissions\",\n },\n ],\n });\n\n switch (action) {\n case \"list\":\n await listPermissionsAction();\n break;\n case \"revoke\":\n await revokePermissionAction();\n break;\n case \"clear\":\n await clearAllPermissionsAction();\n break;\n }\n}\n\n/**\n * List all permissions\n */\nasync function listPermissionsAction(): Promise<void> {\n const permissions = listPermissions();\n\n if (permissions.length === 0) {\n output(\"\\nNo permissions granted yet.\\n\");\n return;\n }\n\n output(\"\\n📋 Granted Permissions:\\n\");\n\n table({\n columns: [\n { key: \"app\", header: \"App\" },\n { key: \"grantedAt\", header: \"Granted At\" },\n ],\n rows: permissions.map((p) => ({\n app: p.appName,\n grantedAt: new Date(p.timestamp).toLocaleString(),\n })),\n });\n\n output(\"\");\n}\n\n/**\n * Revoke permission for a specific app\n */\nasync function revokePermissionAction(): Promise<void> {\n const permissions = listPermissions();\n\n if (permissions.length === 0) {\n output(\"\\nNo permissions to revoke.\\n\");\n return;\n }\n\n const appId = await select({\n prompt: \"Select app to revoke permission:\",\n options: permissions.map((p) => ({\n name: p.appName,\n value: p.appId,\n description: \"Always allowed\",\n })),\n });\n\n const confirmed = await confirm(\n \"Are you sure you want to revoke this permission?\",\n );\n\n if (confirmed) {\n revokePermission(appId);\n output(\"\\n✅ Permission revoked.\\n\");\n } else {\n output(\"\\n❌ Cancelled.\\n\");\n }\n}\n\n/**\n * Clear all permissions\n */\nasync function clearAllPermissionsAction(): Promise<void> {\n const confirmed = await confirm(\n \"Are you sure you want to clear ALL permissions?\",\n );\n\n if (confirmed) {\n clearAllPermissions();\n output(\"\\n✅ All permissions cleared.\\n\");\n } else {\n output(\"\\n❌ Cancelled.\\n\");\n }\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { LLM_API_DOMAINS } from \"../shared/constants\";\nimport type { AppPermissions } from \"../types\";\n\n/**\n * Always protected paths (never allow write, some deny read)\n */\nconst PROTECTED_PATHS = {\n alwaysDenyWrite: [\n join(homedir(), \".kly\"), // KLY config and permissions\n join(homedir(), \".ssh\"), // SSH keys\n join(homedir(), \".aws\"), // AWS credentials\n join(homedir(), \".gnupg\"), // GPG keys\n ],\n alwaysDenyRead: [\n join(homedir(), \".kly\"), // KLY config (prevent reading permissions.json)\n ],\n};\n\n/**\n * Build a complete SandboxRuntimeConfig from declared app permissions\n *\n * This merges:\n * 1. Default safe configuration\n * 2. Automatic LLM domains (if apiKeys: true)\n * 3. User-declared sandbox config\n * 4. Mandatory protections (always applied)\n *\n * @param permissions - Declared app permissions\n * @returns Complete sandbox configuration ready for SandboxManager\n */\nexport function buildSandboxConfig(\n permissions: AppPermissions | undefined,\n): SandboxRuntimeConfig {\n const currentDir = process.cwd();\n\n // Start with defaults\n let allowedDomains: string[] = [];\n let allowWrite: string[] = [currentDir]; // Allow current directory by default\n let denyRead: string[] = [...PROTECTED_PATHS.alwaysDenyRead];\n\n // If apiKeys requested, add LLM domains automatically\n if (permissions?.apiKeys) {\n allowedDomains = [...LLM_API_DOMAINS];\n }\n\n // Merge user-declared sandbox config\n if (permissions?.sandbox) {\n const userSandbox = permissions.sandbox;\n\n // Merge network config\n if (userSandbox.network?.allowedDomains) {\n allowedDomains = [\n ...allowedDomains,\n ...userSandbox.network.allowedDomains,\n ];\n }\n\n // Merge filesystem config\n if (userSandbox.filesystem) {\n if (userSandbox.filesystem.allowWrite) {\n // Replace default with user's choice\n allowWrite = userSandbox.filesystem.allowWrite;\n }\n\n if (userSandbox.filesystem.denyRead) {\n // Add user's deny list\n denyRead = [...denyRead, ...userSandbox.filesystem.denyRead];\n }\n }\n }\n\n // Build final config\n const config: SandboxRuntimeConfig = {\n network: {\n allowedDomains,\n deniedDomains: [],\n },\n filesystem: {\n denyRead,\n allowWrite,\n denyWrite: PROTECTED_PATHS.alwaysDenyWrite, // Always protected\n },\n };\n\n return config;\n}\n\n/**\n * Get a human-readable summary of permissions for display\n * Only shows special/non-default permissions\n */\nexport function formatPermissionsSummary(\n permissions: AppPermissions | undefined,\n): string[] {\n const summary: string[] = [];\n\n // Always show API Keys if requested (special permission)\n if (permissions?.apiKeys) {\n summary.push(\"• API Keys access (to call LLM APIs)\");\n }\n\n const config = buildSandboxConfig(permissions);\n const currentDir = process.cwd();\n\n // Network - only show if non-default (not empty)\n if (config.network.allowedDomains.length > 0) {\n if (config.network.allowedDomains.includes(\"*\")) {\n summary.push(\"• Network: All domains\");\n } else {\n const domains = config.network.allowedDomains.slice(0, 3).join(\", \");\n const more =\n config.network.allowedDomains.length > 3\n ? ` +${config.network.allowedDomains.length - 3} more`\n : \"\";\n summary.push(`• Network: ${domains}${more}`);\n }\n }\n\n // Filesystem - only show if custom (not just current directory)\n const hasCustomWrite =\n config.filesystem.allowWrite.length > 1 ||\n (config.filesystem.allowWrite.length === 1 &&\n config.filesystem.allowWrite[0] !== currentDir);\n\n if (hasCustomWrite) {\n const dirs = config.filesystem.allowWrite\n .map((p) => (p === currentDir ? \"current directory\" : p))\n .slice(0, 2)\n .join(\", \");\n const more =\n config.filesystem.allowWrite.length > 2\n ? ` +${config.filesystem.allowWrite.length - 2} more`\n : \"\";\n summary.push(`• Filesystem write: ${dirs}${more}`);\n }\n\n // Show custom filesystem read restrictions if declared\n if (permissions?.sandbox?.filesystem?.denyRead) {\n summary.push(\n `• Filesystem read denied: ${permissions.sandbox.filesystem.denyRead.length} path(s)`,\n );\n }\n\n return summary;\n}\n","import { ENV_VARS } from \"../shared/constants\";\nimport type { AppPermissions } from \"../types\";\n\n/**\n * Extract permissions from a user's app script\n * This runs in the host process BEFORE launching the sandbox\n *\n * @param scriptPath - Absolute path to the user's script\n * @returns Declared permissions or undefined if not specified\n */\nexport async function extractAppPermissions(\n scriptPath: string,\n): Promise<AppPermissions | undefined> {\n try {\n // Set environment to prevent auto-execution\n const prevMode = process.env[ENV_VARS.PROGRAMMATIC];\n process.env[ENV_VARS.PROGRAMMATIC] = \"true\";\n\n // Import the script (will call defineApp but not auto-run)\n const module = await import(scriptPath);\n\n // Restore environment\n if (prevMode === undefined) {\n delete process.env[ENV_VARS.PROGRAMMATIC];\n } else {\n process.env[ENV_VARS.PROGRAMMATIC] = prevMode;\n }\n\n // Get the app instance\n const app = module.default;\n\n if (!app || !app.definition) {\n return undefined;\n }\n\n // Return declared permissions\n return app.definition.permissions;\n } catch (error) {\n // If extraction fails, we'll ask for permissions interactively\n console.warn(\n `Warning: Could not extract permissions from ${scriptPath}:`,\n error instanceof Error ? error.message : String(error),\n );\n return undefined;\n }\n}\n","import type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { isTrustAll } from \"../shared/runtime-mode\";\nimport type { AppPermissions } from \"../types\";\nimport { select } from \"../ui\";\nimport { isTTY } from \"../ui/utils/tty\";\nimport { formatPermissionsSummary } from \"./config-builder\";\nimport { getAppName, loadPermissions, savePermissions } from \"./index\";\n\n/**\n * Check if permissions require user prompt\n * Only prompt for special permissions (API keys, custom network/filesystem)\n * Default permissions (current directory write, no network) are auto-granted\n */\nfunction needsPermissionPrompt(\n permissions: AppPermissions | undefined,\n sandboxConfig: SandboxRuntimeConfig,\n): boolean {\n // If API keys requested, always prompt\n if (permissions?.apiKeys) {\n return true;\n }\n\n // If custom network access requested (not empty), prompt\n if (sandboxConfig.network.allowedDomains.length > 0) {\n return true;\n }\n\n // If custom filesystem permissions declared, prompt\n if (permissions?.sandbox?.filesystem) {\n return true;\n }\n\n // Otherwise, use default permissions silently\n return false;\n}\n\n/**\n * Request permission with a single unified prompt\n * Shows all requested permissions at once\n *\n * @param appId - App identifier\n * @param appPermissions - Declared permissions from app\n * @param sandboxConfig - Generated sandbox configuration\n * @returns true if allowed, false if cancelled\n */\nexport async function requestUnifiedPermission(\n appId: string,\n appPermissions: AppPermissions | undefined,\n sandboxConfig: SandboxRuntimeConfig,\n): Promise<boolean> {\n // Check for bypass\n if (isTrustAll()) {\n return true;\n }\n\n // Check stored permissions\n const config = loadPermissions();\n const record = config.trustedApps[appId];\n\n if (record && record.choice === \"always\") {\n // Already granted\n return true;\n }\n\n // Check if this app needs permission prompt\n // Default permissions (current dir write, no network) don't need prompt\n if (!needsPermissionPrompt(appPermissions, sandboxConfig)) {\n return true; // Auto-grant default permissions\n }\n\n // Check if running in TTY mode\n if (!isTTY()) {\n const appName = getAppName(appId);\n console.error(\n `\\nPermission required: App \"${appName}\" (${appId}) requests permissions.`,\n );\n console.error(\n \"Set KLY_TRUST_ALL=true environment variable to grant access in non-interactive mode.\",\n );\n return false;\n }\n\n // Show unified prompt\n const appName = getAppName(appId);\n console.log(\"\");\n console.log(`App \"${appName}\" requests the following permissions:`);\n console.log(\"\");\n\n // Format and display permissions\n const summary = formatPermissionsSummary(appPermissions);\n for (const line of summary) {\n console.log(` ${line}`);\n }\n\n console.log(\"\");\n console.log(`Source: ${appId}`);\n console.log(\"\");\n\n // Ask user\n const choice = await select({\n prompt: \"Do you want to allow this?\",\n options: [\n {\n name: \"Allow once\",\n value: \"once\",\n description: \"Allow for this session only\",\n },\n {\n name: \"Always allow\",\n value: \"always\",\n description: \"Remember this choice for future runs\",\n },\n {\n name: \"Cancel\",\n value: \"cancel\",\n description: \"Cancel and exit\",\n },\n ],\n });\n\n // Handle choice\n if (choice === \"cancel\") {\n return false;\n }\n\n if (choice === \"always\") {\n // Save permission\n config.trustedApps[appId] = {\n timestamp: new Date().toISOString(),\n choice: \"always\",\n sandboxConfig,\n };\n savePermissions(config);\n }\n\n // \"once\" or \"always\" - both allow execution\n return true;\n}\n\n/**\n * Check if app needs permission check\n * Returns stored sandbox config if \"always\" was granted\n */\nexport function checkStoredPermission(\n appId: string,\n): SandboxRuntimeConfig | null {\n if (isTrustAll()) {\n return null; // Will use default config\n }\n\n const config = loadPermissions();\n const record = config.trustedApps[appId];\n\n if (record?.choice === \"always\" && record.sandboxConfig) {\n return record.sandboxConfig;\n }\n\n return null;\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { RepoRef } from \"./types\";\n\n/**\n * Parse various remote formats into RepoRef\n *\n * Supported formats:\n * - user/repo\n * - user/repo@v1.0.0\n * - user/repo@branch\n * - github.com/user/repo\n * - github.com/user/repo@ref\n * - https://github.com/user/repo\n */\nexport function parseRemoteRef(input: string): RepoRef | null {\n let normalized = input.trim();\n\n // Remove https:// or http:// prefix\n normalized = normalized.replace(/^https?:\\/\\//, \"\");\n\n // Remove github.com/ prefix\n normalized = normalized.replace(/^github\\.com\\//, \"\");\n\n // Extract ref if present (user/repo@ref or user/repo.git@ref)\n let ref = \"main\";\n const atIndex = normalized.indexOf(\"@\");\n if (atIndex !== -1) {\n ref = normalized.slice(atIndex + 1);\n normalized = normalized.slice(0, atIndex);\n }\n\n // Remove trailing .git (after @ extraction)\n normalized = normalized.replace(/\\.git$/, \"\");\n\n // Parse owner/repo\n const parts = normalized.split(\"/\");\n if (parts.length !== 2) {\n return null;\n }\n\n const [owner, repo] = parts;\n\n // Validate owner and repo names\n if (\n !owner ||\n !repo ||\n !isValidGitHubName(owner) ||\n !isValidGitHubName(repo)\n ) {\n return null;\n }\n\n return { owner, repo, ref };\n}\n\n/**\n * Check if a string is a valid GitHub username or repo name\n */\nfunction isValidGitHubName(name: string): boolean {\n // GitHub names: alphanumeric, hyphens, no consecutive hyphens, no start/end with hyphen\n return (\n /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(name) ||\n /^[a-zA-Z0-9]$/.test(name)\n );\n}\n\n/**\n * Get the kly cache directory\n */\nexport function getCacheDir(): string {\n return join(homedir(), \".kly\", \"cache\");\n}\n\n/**\n * Get the cache path for a specific repo ref\n */\nexport function getRepoCachePath(ref: RepoRef): string {\n return join(getCacheDir(), \"github.com\", ref.owner, ref.repo, ref.ref);\n}\n\n/**\n * Check if an input looks like a remote reference (vs local file path)\n */\nexport function isRemoteRef(input: string): boolean {\n // Local paths start with ./ ../ / or contain backslash (Windows)\n if (\n input.startsWith(\"./\") ||\n input.startsWith(\"../\") ||\n input.startsWith(\"/\") ||\n input.includes(\"\\\\\")\n ) {\n return false;\n }\n\n // Check if it parses as a valid remote ref\n return parseRemoteRef(input) !== null;\n}\n","import {\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { getRepoCachePath } from \"./parser\";\nimport type { CacheCheckResult, CacheMetadata, RepoRef } from \"./types\";\n\nconst META_FILENAME = \".kly-meta.json\";\n\n/**\n * Check if cache exists and is valid\n */\nexport function checkCache(ref: RepoRef): CacheCheckResult {\n const cachePath = getRepoCachePath(ref);\n const metaPath = join(cachePath, META_FILENAME);\n\n if (!existsSync(cachePath)) {\n return {\n exists: false,\n valid: false,\n reason: \"Cache directory does not exist\",\n };\n }\n\n if (!existsSync(metaPath)) {\n return { exists: true, valid: false, reason: \"Cache metadata missing\" };\n }\n\n try {\n const metadata = JSON.parse(\n readFileSync(metaPath, \"utf-8\"),\n ) as CacheMetadata;\n\n // Check if entry point still exists\n const entryPath = join(cachePath, metadata.entryPoint);\n if (!existsSync(entryPath)) {\n return {\n exists: true,\n valid: false,\n metadata,\n reason: \"Entry point file missing\",\n };\n }\n\n // Check if dependencies were installed\n if (!metadata.dependenciesInstalled) {\n return {\n exists: true,\n valid: false,\n metadata,\n reason: \"Dependencies not installed\",\n };\n }\n\n return { exists: true, valid: true, metadata };\n } catch {\n return { exists: true, valid: false, reason: \"Invalid cache metadata\" };\n }\n}\n\n/**\n * Read cache metadata\n */\nexport function readMetadata(ref: RepoRef): CacheMetadata | null {\n const cachePath = getRepoCachePath(ref);\n const metaPath = join(cachePath, META_FILENAME);\n\n if (!existsSync(metaPath)) {\n return null;\n }\n\n try {\n return JSON.parse(readFileSync(metaPath, \"utf-8\")) as CacheMetadata;\n } catch {\n return null;\n }\n}\n\n/**\n * Write cache metadata\n */\nexport function writeMetadata(ref: RepoRef, metadata: CacheMetadata): void {\n const cachePath = getRepoCachePath(ref);\n const metaPath = join(cachePath, META_FILENAME);\n\n mkdirSync(dirname(metaPath), { recursive: true });\n writeFileSync(metaPath, JSON.stringify(metadata, null, 2));\n}\n\n/**\n * Remove cached repository\n */\nexport function invalidateCache(ref: RepoRef): void {\n const cachePath = getRepoCachePath(ref);\n\n if (existsSync(cachePath)) {\n rmSync(cachePath, { recursive: true, force: true });\n }\n}\n\n/**\n * Clear all cache\n */\nexport function clearAllCache(): void {\n const { getCacheDir } = require(\"./parser\");\n const cacheDir = getCacheDir();\n\n if (existsSync(cacheDir)) {\n rmSync(cacheDir, { recursive: true, force: true });\n }\n}\n","import { exec } from \"node:child_process\";\nimport { existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { getRepoCachePath } from \"./parser\";\nimport type { RepoRef } from \"./types\";\n\nconst execAsync = promisify(exec);\n\n/**\n * Clone a repository to cache\n */\nexport async function cloneRepo(ref: RepoRef): Promise<void> {\n const repoUrl = `https://github.com/${ref.owner}/${ref.repo}.git`;\n const targetPath = getRepoCachePath(ref);\n\n // Remove existing cache if any\n if (existsSync(targetPath)) {\n rmSync(targetPath, { recursive: true, force: true });\n }\n\n // Ensure parent directory exists\n mkdirSync(dirname(targetPath), { recursive: true });\n\n // Shallow clone specific ref\n try {\n await execAsync(\n `git clone --depth 1 --branch ${ref.ref} ${repoUrl} \"${targetPath}\"`,\n {\n timeout: 60000, // 60s timeout\n },\n );\n } catch (error) {\n // If branch clone fails, try without --branch (for default branch)\n if (ref.ref === \"main\") {\n try {\n await execAsync(`git clone --depth 1 ${repoUrl} \"${targetPath}\"`, {\n timeout: 60000,\n });\n return;\n } catch {\n // Fall through to original error\n }\n }\n throw new Error(\n `Failed to clone ${ref.owner}/${ref.repo}@${ref.ref}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Install dependencies using bun\n */\nexport async function installDependencies(repoPath: string): Promise<void> {\n const pkgPath = `${repoPath}/package.json`;\n\n if (!existsSync(pkgPath)) {\n // No package.json, skip install\n return;\n }\n\n try {\n await execAsync(\"bun install\", {\n cwd: repoPath,\n timeout: 120000, // 120s timeout\n });\n } catch (error) {\n throw new Error(\n `Failed to install dependencies: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Get the commit SHA of a cloned repo\n */\nexport async function getCommitSha(repoPath: string): Promise<string> {\n try {\n const { stdout } = await execAsync(\"git rev-parse HEAD\", {\n cwd: repoPath,\n timeout: 5000,\n });\n return stdout.trim();\n } catch {\n return \"unknown\";\n }\n}\n","import { createHash } from \"node:crypto\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n type Stats,\n statSync,\n} from \"node:fs\";\nimport { join, relative } from \"node:path\";\n\n/**\n * Directories and files to ignore when calculating repository hash\n */\nconst IGNORE_PATTERNS = [\n \".git\",\n \"node_modules\",\n \"dist\",\n \"build\",\n \"coverage\",\n \".kly\",\n \".kly-meta.json\",\n \".DS_Store\",\n \"*.log\",\n];\n\n/**\n * File extensions to include in hash calculation\n */\nconst SOURCE_EXTENSIONS = [\n \".ts\",\n \".js\",\n \".tsx\",\n \".jsx\",\n \".json\",\n \".md\",\n \".txt\",\n];\n\n/**\n * Calculate integrity hash for a cloned repository\n * Uses SHA-384 (consistent with browser Subresource Integrity)\n *\n * Hash includes:\n * - All source code files (sorted by path)\n * - File paths (for structure verification)\n * - Lock file (bun.lockb) if present\n *\n * @param repoPath - Absolute path to the repository\n * @param algorithm - Hash algorithm (default: sha384)\n * @returns Hash in format \"sha384-base64...\"\n */\nexport function calculateRepoHash(\n repoPath: string,\n algorithm: \"sha256\" | \"sha384\" | \"sha512\" = \"sha384\",\n): string {\n const hash = createHash(algorithm);\n\n // 1. Collect all source files\n const files = collectSourceFiles(repoPath);\n\n // 2. Sort by relative path (ensures consistent ordering)\n files.sort();\n\n // 3. Hash each file (path + content)\n for (const file of files) {\n const relativePath = relative(repoPath, file);\n const content = readFileSync(file);\n\n // Include file path in hash (detects file moves/renames)\n hash.update(`FILE:${relativePath}\\n`);\n hash.update(content);\n hash.update(\"\\n\");\n }\n\n // 4. Include lock file if present (dependency integrity)\n const lockFile = join(repoPath, \"bun.lockb\");\n if (existsSync(lockFile)) {\n hash.update(\"LOCK:bun.lockb\\n\");\n hash.update(readFileSync(lockFile));\n hash.update(\"\\n\");\n }\n\n // 5. Generate base64 digest\n const digest = hash.digest(\"base64\");\n return `${algorithm}-${digest}`;\n}\n\n/**\n * Recursively collect all source files in a directory\n *\n * @param dir - Directory to scan\n * @param results - Accumulator for file paths\n * @returns Array of absolute file paths\n */\nfunction collectSourceFiles(dir: string, results: string[] = []): string[] {\n if (!existsSync(dir)) {\n return results;\n }\n\n try {\n const entries = readdirSync(dir);\n\n for (const entry of entries) {\n // Skip ignored patterns\n if (shouldIgnore(entry)) {\n continue;\n }\n\n const fullPath = join(dir, entry);\n let stat: Stats;\n\n try {\n stat = statSync(fullPath);\n } catch {\n // Skip files we can't stat (permission issues, symlinks, etc.)\n continue;\n }\n\n if (stat.isDirectory()) {\n collectSourceFiles(fullPath, results);\n } else if (stat.isFile() && shouldIncludeFile(entry)) {\n results.push(fullPath);\n }\n }\n } catch {\n // Skip directories we can't read\n }\n\n return results;\n}\n\n/**\n * Check if a file/directory should be ignored\n */\nfunction shouldIgnore(name: string): boolean {\n for (const pattern of IGNORE_PATTERNS) {\n if (pattern.startsWith(\"*\")) {\n // Wildcard pattern (e.g., *.log)\n const ext = pattern.slice(1);\n if (name.endsWith(ext)) {\n return true;\n }\n } else if (name === pattern) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a file should be included in hash calculation\n */\nfunction shouldIncludeFile(name: string): boolean {\n return SOURCE_EXTENSIONS.some((ext) => name.endsWith(ext));\n}\n\n/**\n * Parse a hash string into its components\n *\n * @param hashString - Hash in format \"sha384-base64...\"\n * @returns Parsed components or null if invalid\n */\nexport function parseHashString(\n hashString: string,\n): { algorithm: string; digest: string } | null {\n const match = hashString.match(/^(sha256|sha384|sha512)-(.+)$/);\n if (!match) {\n return null;\n }\n\n return {\n algorithm: match[1] as string,\n digest: match[2] as string,\n };\n}\n\n/**\n * Compare two hash strings for equality\n *\n * @param hash1 - First hash\n * @param hash2 - Second hash\n * @returns true if hashes match\n */\nexport function compareHashes(hash1: string, hash2: string): boolean {\n const parsed1 = parseHashString(hash1);\n const parsed2 = parseHashString(hash2);\n\n if (!parsed1 || !parsed2) {\n return false;\n }\n\n // Must use same algorithm\n if (parsed1.algorithm !== parsed2.algorithm) {\n return false;\n }\n\n return parsed1.digest === parsed2.digest;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { KlyConfig } from \"./types\";\n\n/**\n * Entry point candidates to search for (in order)\n */\nconst ENTRY_CANDIDATES = [\n \"index.ts\",\n \"main.ts\",\n \"src/index.ts\",\n \"src/main.ts\",\n \"app.ts\",\n];\n\n/**\n * Resolve entry point for a kly app\n * Priority: main field > convention candidates\n */\nexport function resolveEntryPoint(repoPath: string): string | null {\n const pkgPath = join(repoPath, \"package.json\");\n\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\n // Check main field (standard npm field)\n if (pkg.main && (pkg.main.endsWith(\".ts\") || pkg.main.endsWith(\".js\"))) {\n const mainPath = join(repoPath, pkg.main);\n if (existsSync(mainPath)) {\n return pkg.main; // Return relative path\n }\n }\n } catch {\n // Invalid package.json, continue to candidates\n }\n }\n\n // Try convention candidates\n for (const candidate of ENTRY_CANDIDATES) {\n const candidatePath = join(repoPath, candidate);\n if (existsSync(candidatePath)) {\n return candidate; // Return relative path\n }\n }\n\n return null;\n}\n\n/**\n * Read kly configuration from package.json\n */\nexport function readKlyConfig(repoPath: string): KlyConfig | null {\n const pkgPath = join(repoPath, \"package.json\");\n\n if (!existsSync(pkgPath)) {\n return null;\n }\n\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.kly ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Check if current kly version satisfies the required version\n * Simple semver check (supports >=x.y.z format)\n */\nexport function validateVersion(required: string, current: string): boolean {\n // Parse >=x.y.z format\n const reqMatch = required.match(/^>=?\\s*(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!reqMatch) {\n // Unknown format, skip validation\n return true;\n }\n\n const curMatch = current.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!curMatch) {\n return true;\n }\n\n const reqMajor = Number(reqMatch[1]);\n const reqMinor = Number(reqMatch[2]);\n const reqPatch = Number(reqMatch[3]);\n const curMajor = Number(curMatch[1]);\n const curMinor = Number(curMatch[2]);\n const curPatch = Number(curMatch[3]);\n\n // Compare versions\n if (curMajor > reqMajor) return true;\n if (curMajor < reqMajor) return false;\n if (curMinor > reqMinor) return true;\n if (curMinor < reqMinor) return false;\n return curPatch >= reqPatch;\n}\n\n/**\n * Check required environment variables\n * Returns list of missing variables\n */\nexport function checkEnvVars(required: string[]): string[] {\n return required.filter((name) => !process.env[name]);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Sum file entry representing a trusted code hash\n */\nexport interface SumEntry {\n /** Full URL: github.com/owner/repo@ref */\n url: string;\n /** Integrity hash: sha384-base64... */\n hash: string;\n /** Unix timestamp when first trusted */\n timestamp: number;\n /** Whether user explicitly trusted this code */\n trusted: boolean;\n}\n\n/**\n * Verification result\n */\nexport type VerifyResult = \"ok\" | \"mismatch\" | \"new\";\n\n/**\n * Get the path to kly.sum file\n */\nexport function getSumFilePath(): string {\n return join(homedir(), \".kly\", \"kly.sum\");\n}\n\n/**\n * Manager for kly.sum file (integrity verification database)\n *\n * File format (one entry per line):\n * github.com/owner/repo@ref sha384-hash timestamp trusted|untrusted\n *\n * Example:\n * github.com/jack/weather@v1.0.0 sha384-oqVuAfXRKap7fdgc... 1704067200 trusted\n */\nexport class SumFileManager {\n private entries: Map<string, SumEntry> = new Map();\n private sumFilePath: string;\n private dirty = false;\n\n constructor(sumFilePath?: string) {\n this.sumFilePath = sumFilePath || getSumFilePath();\n this.load();\n }\n\n /**\n * Load entries from kly.sum file\n */\n private load(): void {\n if (!existsSync(this.sumFilePath)) {\n return;\n }\n\n try {\n const content = readFileSync(this.sumFilePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n\n const entry = this.parseLine(trimmed);\n if (entry) {\n this.entries.set(entry.url, entry);\n }\n }\n } catch (error) {\n console.warn(`Warning: Failed to load kly.sum: ${error}`);\n }\n }\n\n /**\n * Parse a single line from kly.sum\n */\n private parseLine(line: string): SumEntry | null {\n // Format: url hash timestamp trusted|untrusted\n const parts = line.split(/\\s+/);\n\n if (parts.length < 4) {\n return null;\n }\n\n const [url, hash, timestampStr, trustedStr] = parts;\n\n if (!url || !hash || !timestampStr || !trustedStr) {\n return null;\n }\n\n const timestamp = Number(timestampStr);\n if (Number.isNaN(timestamp)) {\n return null;\n }\n\n return {\n url,\n hash,\n timestamp,\n trusted: trustedStr === \"trusted\",\n };\n }\n\n /**\n * Format an entry for writing to file\n */\n private formatEntry(entry: SumEntry): string {\n return `${entry.url} ${entry.hash} ${entry.timestamp} ${entry.trusted ? \"trusted\" : \"untrusted\"}`;\n }\n\n /**\n * Save entries to kly.sum file\n */\n private save(): void {\n if (!this.dirty) {\n return;\n }\n\n try {\n // Ensure directory exists\n const dir = dirname(this.sumFilePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Sort entries by URL for consistent formatting\n const sortedEntries = Array.from(this.entries.values()).sort((a, b) =>\n a.url.localeCompare(b.url),\n );\n\n // Build file content\n const lines = [\n \"# kly.sum - Integrity verification database\",\n \"# Format: url hash timestamp trusted|untrusted\",\n \"# DO NOT EDIT THIS FILE MANUALLY\",\n \"\",\n ...sortedEntries.map((entry) => this.formatEntry(entry)),\n ];\n\n writeFileSync(this.sumFilePath, `${lines.join(\"\\n\")}\\n`, \"utf-8\");\n this.dirty = false;\n } catch (error) {\n console.error(`Error: Failed to save kly.sum: ${error}`);\n throw error;\n }\n }\n\n /**\n * Verify a repository's integrity hash\n *\n * @param url - Full URL (e.g., \"github.com/owner/repo@ref\")\n * @param hash - Calculated hash to verify\n * @returns \"ok\" if matches, \"mismatch\" if different, \"new\" if first time\n */\n public verify(url: string, hash: string): VerifyResult {\n const entry = this.entries.get(url);\n\n if (!entry) {\n return \"new\";\n }\n\n if (entry.hash === hash) {\n return \"ok\";\n }\n\n return \"mismatch\";\n }\n\n /**\n * Add or update an entry in kly.sum\n *\n * @param url - Full URL\n * @param hash - Integrity hash\n * @param trusted - Whether user explicitly trusted this code\n */\n public add(url: string, hash: string, trusted = false): void {\n this.entries.set(url, {\n url,\n hash,\n timestamp: Math.floor(Date.now() / 1000),\n trusted,\n });\n\n this.dirty = true;\n this.save();\n }\n\n /**\n * Update an existing entry's hash (e.g., user approved new version)\n *\n * @param url - Full URL\n * @param hash - New hash\n * @param trusted - Whether user explicitly trusted this update\n */\n public update(url: string, hash: string, trusted = false): void {\n const existing = this.entries.get(url);\n\n this.entries.set(url, {\n url,\n hash,\n timestamp: existing?.timestamp ?? Math.floor(Date.now() / 1000),\n trusted,\n });\n\n this.dirty = true;\n this.save();\n }\n\n /**\n * Remove an entry from kly.sum\n *\n * @param url - Full URL to remove\n * @returns true if removed, false if not found\n */\n public remove(url: string): boolean {\n const existed = this.entries.delete(url);\n\n if (existed) {\n this.dirty = true;\n this.save();\n }\n\n return existed;\n }\n\n /**\n * Get an entry by URL\n *\n * @param url - Full URL\n * @returns Entry if found, undefined otherwise\n */\n public get(url: string): SumEntry | undefined {\n return this.entries.get(url);\n }\n\n /**\n * Get all entries\n *\n * @returns Array of all sum entries\n */\n public getAll(): SumEntry[] {\n return Array.from(this.entries.values());\n }\n\n /**\n * Clear all entries (for testing or reset)\n */\n public clear(): void {\n this.entries.clear();\n this.dirty = true;\n this.save();\n }\n\n /**\n * Get statistics about the sum file\n */\n public getStats(): {\n total: number;\n trusted: number;\n untrusted: number;\n } {\n const entries = Array.from(this.entries.values());\n\n return {\n total: entries.length,\n trusted: entries.filter((e) => e.trusted).length,\n untrusted: entries.filter((e) => !e.trusted).length,\n };\n }\n}\n","import { join } from \"node:path\";\nimport { ENV_VARS } from \"../shared/constants\";\nimport { confirm } from \"../ui\";\nimport { checkCache, invalidateCache, writeMetadata } from \"./cache\";\nimport { cloneRepo, getCommitSha, installDependencies } from \"./fetcher\";\nimport { calculateRepoHash } from \"./integrity\";\nimport { getRepoCachePath, parseRemoteRef } from \"./parser\";\nimport {\n checkEnvVars,\n readKlyConfig,\n resolveEntryPoint,\n validateVersion,\n} from \"./resolver\";\nimport { SumFileManager } from \"./sumfile\";\nimport type { IntegrityCheckResult, RepoRef, RunRemoteOptions } from \"./types\";\n\n/** Current kly CLI version */\nconst KLY_VERSION = \"0.1.0\";\n\n/**\n * Run a remote GitHub repository as a kly app\n */\nexport async function runRemote(\n input: string,\n options: RunRemoteOptions = {},\n): Promise<void> {\n // 1. Parse input\n const ref = parseRemoteRef(input);\n if (!ref) {\n throw new Error(`Invalid remote reference: ${input}`);\n }\n\n const repoPath = getRepoCachePath(ref);\n\n // 2. Check cache\n const cacheResult = checkCache(ref);\n\n if (!cacheResult.valid || options.force) {\n // 3. Clone repository\n if (options.force && cacheResult.exists) {\n console.log(`Refreshing ${ref.owner}/${ref.repo}@${ref.ref}...`);\n invalidateCache(ref);\n } else {\n console.log(`Fetching ${ref.owner}/${ref.repo}@${ref.ref}...`);\n }\n\n await cloneRepo(ref);\n\n // 4. Resolve entry point\n const entryPoint = resolveEntryPoint(repoPath);\n if (!entryPoint) {\n throw new Error(\n `No entry point found in ${ref.owner}/${ref.repo}. Set \"main\" in package.json or create index.ts`,\n );\n }\n\n // 5. Install dependencies\n if (!options.skipInstall) {\n console.log(\"Installing dependencies...\");\n await installDependencies(repoPath);\n }\n\n // 6. Write metadata\n const commitSha = await getCommitSha(repoPath);\n writeMetadata(ref, {\n commitSha,\n cachedAt: new Date().toISOString(),\n entryPoint,\n dependenciesInstalled: !options.skipInstall,\n });\n\n console.log(\"Ready!\\n\");\n }\n\n // 7. Integrity verification\n if (!options.skipIntegrityCheck) {\n const integrityResult = await verifyIntegrity(ref, repoPath);\n\n if (!integrityResult.proceedWithExecution) {\n console.error(\n \"\\n❌ Execution cancelled due to integrity verification failure\",\n );\n process.exit(1);\n }\n }\n\n // 8. Validate and execute\n await executeApp(ref, repoPath, options.args ?? [], options.mcp ?? false);\n}\n\n/**\n * Verify repository integrity using kly.sum\n *\n * @param ref - Repository reference\n * @param repoPath - Local path to repository\n * @returns Object with integrity check result and whether to proceed with execution\n */\nasync function verifyIntegrity(\n ref: RepoRef,\n repoPath: string,\n): Promise<{ proceedWithExecution: boolean; result: IntegrityCheckResult }> {\n const url = `github.com/${ref.owner}/${ref.repo}@${ref.ref}`;\n\n console.log(\"\\n🔐 Verifying code integrity...\");\n\n // Calculate repository hash\n const hash = calculateRepoHash(repoPath);\n console.log(` Hash: ${hash.slice(0, 20)}...`);\n\n // Check against kly.sum\n const sumManager = new SumFileManager();\n const verifyResult = sumManager.verify(url, hash);\n\n const result: IntegrityCheckResult = {\n status: verifyResult,\n hash,\n requiresTrust: verifyResult !== \"ok\",\n };\n\n switch (verifyResult) {\n case \"ok\": {\n // Hash matches - code is trusted\n console.log(\" ✓ Integrity verified\\n\");\n return { proceedWithExecution: true, result };\n }\n\n case \"new\": {\n // First time running this version\n console.log(\"\\n⚠️ SECURITY NOTICE: First time running this tool\\n\");\n console.log(\" This code has not been verified before.\");\n console.log(\" Please review the source code before proceeding:\");\n console.log(\n ` https://github.com/${ref.owner}/${ref.repo}/tree/${ref.ref}\\n`,\n );\n\n const shouldTrust = await confirm(\n \"Do you trust this code and want to proceed?\",\n );\n\n if (shouldTrust) {\n sumManager.add(url, hash, true);\n console.log(\" ✓ Code trusted and added to kly.sum\\n\");\n return { proceedWithExecution: true, result };\n }\n\n console.log(\"\\n User declined to trust the code\");\n return { proceedWithExecution: false, result };\n }\n\n case \"mismatch\": {\n // Hash doesn't match - code has changed!\n const existingEntry = sumManager.get(url);\n result.expectedHash = existingEntry?.hash;\n\n console.log(\"\\n🚨 SECURITY WARNING: Code has been modified!\\n\");\n console.log(\n \" The code for this tool has changed since you last ran it.\",\n );\n console.log(\" This could indicate:\");\n console.log(\" - A supply chain attack (code tampering)\");\n console.log(\" - Maintainer account compromise\");\n console.log(\" - Git history rewrite\\n\");\n\n console.log(\n \" Expected hash:\",\n `${result.expectedHash?.slice(0, 40)}...`,\n );\n console.log(\" Current hash: \", `${hash.slice(0, 40)}...\\n`);\n\n console.log(\" Recommended actions:\");\n console.log(\" 1. Check GitHub for official announcements\");\n console.log(\" 2. Contact the maintainer\");\n console.log(\" 3. Review code changes carefully\");\n console.log(\n ` 4. Visit: https://github.com/${ref.owner}/${ref.repo}/commits/${ref.ref}\\n`,\n );\n\n const shouldProceed = await confirm(\n \"⚠️ Proceed anyway? (NOT RECOMMENDED)\",\n false,\n );\n\n if (shouldProceed) {\n const shouldUpdate = await confirm(\n \"Update kly.sum with new hash?\",\n false,\n );\n\n if (shouldUpdate) {\n sumManager.update(url, hash, true);\n console.log(\" ✓ kly.sum updated with new hash\\n\");\n }\n\n return { proceedWithExecution: true, result };\n }\n\n console.log(\"\\n Execution cancelled for safety\");\n return { proceedWithExecution: false, result };\n }\n\n default: {\n // Should never reach here, but TypeScript requires it\n console.error(\"Unknown verification result\");\n return { proceedWithExecution: false, result };\n }\n }\n}\n\n/**\n * Execute the kly app\n */\nasync function executeApp(\n ref: RepoRef,\n repoPath: string,\n args: string[],\n mcp: boolean,\n): Promise<void> {\n // Read config and validate\n const config = readKlyConfig(repoPath);\n\n if (config?.version) {\n if (!validateVersion(config.version, KLY_VERSION)) {\n throw new Error(\n `This app requires kly ${config.version}, but you have ${KLY_VERSION}`,\n );\n }\n }\n\n // Check required env vars\n if (config?.env && config.env.length > 0) {\n const missing = checkEnvVars(config.env);\n if (missing.length > 0) {\n console.warn(\n `Warning: Required environment variables not set: ${missing.join(\", \")}`,\n );\n }\n }\n\n // Resolve entry point\n const entryPoint = resolveEntryPoint(repoPath);\n if (!entryPoint) {\n throw new Error(`Cannot resolve entry point for ${ref.owner}/${ref.repo}`);\n }\n\n const absoluteEntryPath = join(repoPath, entryPoint);\n\n // Set remote ref environment variable for permission tracking\n const remoteRef = `github.com/${ref.owner}/${ref.repo}`;\n const prevRemoteRef = process.env[ENV_VARS.REMOTE_REF];\n process.env[ENV_VARS.REMOTE_REF] = remoteRef;\n\n try {\n // Dynamic imports to avoid circular dependencies\n const { getAppIdentifier, checkApiKeyPermission, getAppSandboxConfig } =\n await import(\"../permissions\");\n const { launchSandbox } = await import(\"../host/launcher\");\n\n const appId = getAppIdentifier();\n\n // Check permissions\n console.log(\"🔐 Checking permissions...\");\n const allowApiKey = await checkApiKeyPermission(appId);\n\n if (!allowApiKey) {\n console.error(\"❌ Permission denied: API key access rejected\");\n process.exit(1);\n }\n\n // Get sandbox configuration\n const sandboxConfig = await getAppSandboxConfig(appId);\n\n if (!sandboxConfig) {\n console.error(\"❌ Permission denied: Sandbox configuration rejected\");\n process.exit(1);\n }\n\n // Handle MCP mode\n if (mcp) {\n // For MCP mode, we still need special handling\n // For now, just run directly without sandbox\n console.warn(\n \"⚠️ MCP mode with remote repos not yet fully supported in new architecture\",\n );\n process.env[ENV_VARS.MCP_MODE] = \"true\";\n process.argv = [\"bun\", absoluteEntryPath];\n await import(absoluteEntryPath);\n return;\n }\n\n // Launch in sandbox\n const result = await launchSandbox({\n scriptPath: absoluteEntryPath,\n args,\n appId,\n sandboxConfig,\n allowApiKey,\n });\n\n if (result.error) {\n console.error(`\\n❌ Error: ${result.error}`);\n }\n\n if (result.exitCode !== 0) {\n process.exit(result.exitCode);\n }\n } finally {\n // Restore environment\n if (prevRemoteRef === undefined) {\n delete process.env[ENV_VARS.REMOTE_REF];\n } else {\n process.env[ENV_VARS.REMOTE_REF] = prevRemoteRef;\n }\n }\n}\n\nexport { clearAllCache, invalidateCache } from \"./cache\";\n// Re-export utilities\nexport { isRemoteRef, parseRemoteRef } from \"./parser\";\nexport type {\n CacheMetadata,\n KlyConfig,\n RepoRef,\n RunRemoteOptions,\n} from \"./types\";\n","#!/usr/bin/env bun\nimport { resolve } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { modelsCommand } from \"../src/ai/models-command\";\nimport { launchSandbox } from \"../src/host/launcher\";\nimport { getAppIdentifier } from \"../src/permissions\";\nimport { permissionsCommand } from \"../src/permissions/cli\";\nimport { buildSandboxConfig } from \"../src/permissions/config-builder\";\nimport { extractAppPermissions } from \"../src/permissions/extract\";\nimport {\n checkStoredPermission,\n requestUnifiedPermission,\n} from \"../src/permissions/unified-prompt\";\nimport { isRemoteRef, runRemote } from \"../src/remote\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nasync function main() {\n if (!command || command === \"--help\" || command === \"-h\") {\n showHelp();\n return;\n }\n\n if (command === \"--version\" || command === \"-v\") {\n showVersion();\n return;\n }\n\n if (command === \"models\") {\n await modelsCommand();\n return;\n }\n\n if (command === \"permissions\") {\n await permissionsCommand();\n return;\n }\n\n if (command === \"run\") {\n const target = args[1];\n if (!target) {\n console.error(\"Error: Missing file path or remote reference\");\n console.error(\"Usage: kly run <file|user/repo[@ref]>\");\n process.exit(1);\n }\n\n // Check for --force flag\n const forceIndex = args.indexOf(\"--force\");\n const force = forceIndex !== -1;\n\n // Find -- separator for app arguments\n const dashDashIndex = args.indexOf(\"--\");\n const appArgs =\n dashDashIndex !== -1\n ? args.slice(dashDashIndex + 1)\n : args.slice(2).filter((arg) => arg !== \"--force\");\n\n if (isRemoteRef(target)) {\n await runRemote(target, { args: appArgs, force });\n } else {\n await runFile(target, appArgs);\n }\n return;\n }\n\n if (command === \"mcp\") {\n const target = args[1];\n if (!target) {\n console.error(\"Error: Missing file path or remote reference\");\n console.error(\"Usage: kly mcp <file|user/repo[@ref]>\");\n process.exit(1);\n }\n\n // Check for --force flag\n const forceIndex = args.indexOf(\"--force\");\n const force = forceIndex !== -1;\n\n if (isRemoteRef(target)) {\n await runRemote(target, { args: [], force, mcp: true });\n } else {\n await runFileAsMcp(target);\n }\n return;\n }\n\n console.error(`Unknown command: ${command}`);\n console.error('Run \"kly --help\" for usage');\n process.exit(1);\n}\n\nasync function runFile(filePath: string, appArgs: string[]) {\n const absolutePath = resolve(process.cwd(), filePath);\n\n // Set local file identifier for permission tracking\n const prevLocalRef = process.env.KLY_LOCAL_REF;\n process.env.KLY_LOCAL_REF = `local:${absolutePath}`;\n\n try {\n // Get app identifier\n const appId = getAppIdentifier();\n\n // Check if permission already granted\n const storedConfig = checkStoredPermission(appId);\n let sandboxConfig: SandboxRuntimeConfig;\n let allowApiKey = false;\n\n if (!storedConfig) {\n // Extract declared permissions from app\n const appPermissions = await extractAppPermissions(absolutePath);\n\n // Build complete sandbox config (with auto-LLM domains if apiKeys: true)\n sandboxConfig = buildSandboxConfig(appPermissions);\n\n // Show unified permission prompt\n console.log(\"🔐 Checking permissions...\");\n const allowed = await requestUnifiedPermission(\n appId,\n appPermissions,\n sandboxConfig,\n );\n\n if (!allowed) {\n console.error(\"❌ Permission denied\");\n process.exit(1);\n }\n\n // Set API key access based on declared permissions\n allowApiKey = appPermissions?.apiKeys ?? false;\n } else {\n // Permission already granted, use stored config\n sandboxConfig = storedConfig;\n // We need to re-extract to determine if apiKeys was requested\n const appPermissions = await extractAppPermissions(absolutePath);\n allowApiKey = appPermissions?.apiKeys ?? false;\n }\n\n // Launch in sandbox\n const result = await launchSandbox({\n scriptPath: absolutePath,\n args: appArgs,\n appId,\n sandboxConfig,\n allowApiKey,\n });\n\n if (result.error) {\n console.error(`\\n❌ Error: ${result.error}`);\n }\n\n if (result.exitCode !== 0) {\n process.exit(result.exitCode);\n }\n } finally {\n // Restore environment\n if (prevLocalRef === undefined) {\n delete process.env.KLY_LOCAL_REF;\n } else {\n process.env.KLY_LOCAL_REF = prevLocalRef;\n }\n }\n}\n\nasync function runFileAsMcp(filePath: string) {\n const absolutePath = resolve(process.cwd(), filePath);\n\n // Set MCP mode environment variable\n process.env.KLY_MCP_MODE = \"true\";\n\n // Modify process.argv\n process.argv = [\"bun\", absolutePath];\n\n // Dynamic import triggers defineApp's auto-execution in MCP mode\n await import(absolutePath);\n}\n\nfunction showHelp() {\n console.log(`\nkly - Command Line AI\n\nUsage:\n kly <command> [options]\n\nCommands:\n models Manage LLM model configurations\n permissions Manage app permissions\n run <target> Run a Kly app\n mcp <target> Start an MCP server for a Kly app\n\nTarget can be:\n ./file.ts Local file\n user/repo GitHub repo (main branch)\n user/repo@v1.0.0 GitHub repo at specific tag\n user/repo@branch GitHub repo at specific branch\n\nOptions:\n --force Force re-fetch remote repo (ignore cache)\n --help, -h Show help\n --version, -v Show version\n\nExamples:\n kly models\n kly permissions\n kly run ./my-tool.ts\n kly run ./my-tool.ts --name=World\n kly run user/weather-app\n kly run user/weather-app@v1.0.0\n kly run user/weather-app -- --city=Beijing\n kly mcp ./my-tool.ts\n kly mcp user/weather-app\n`);\n}\n\nfunction showVersion() {\n console.log(\"0.1.0\");\n}\n\nmain().catch((err) => {\n console.error(err.message || err);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;AAKA,MAAM,iBAAiB;AACvB,MAAM,aAAa,KAAK,SAAS,EAAE,QAAQ,oBAAoB;AAC/D,MAAM,YAAY,OAAU,KAAK;;;;AA+DjC,eAAsB,mBACpB,eAAe,OACgB;AAE/B,KAAI,CAAC,gBAAgB,WAAW,WAAW,CACzC,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,aAAa,YAAY,QAAQ,CAAC;AAG5D,MAFY,KAAK,KAAK,GAAG,OAAO,YAEtB,UACR,QAAO;UAEF,QAAQ;AAMnB,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,eAAe;AAC5C,MAAI,CAAC,SAAS,GACZ,QAAO;EAIT,MAAMA,aAA4B;GAChC,WAFW,MAAM,SAAS,MAAM;GAGhC,WAAW,KAAK,KAAK;GACtB;AAGD,MAAI;AACF,iBAAc,YAAY,KAAK,UAAU,YAAY,MAAM,EAAE,EAAE,QAAQ;WAChE,QAAQ;AAIjB,SAAO;UACA,QAAQ;AAEf,MAAI,WAAW,WAAW,CACxB,KAAI;AACF,UAAO,KAAK,MAAM,aAAa,YAAY,QAAQ,CAAC;UAC9C;AAIV,SAAO;;;;;;AAOX,MAAMC,kBAAwD;CAC5D,QAAQ;CACR,WAAW;CACX,QAAQ;CACR,UAAU;CACV,MAAM;CACN,SAAS;CACT,QAAQ;CACR,QAAQ;CACT;;;;AAKD,SAAgB,gBACd,MACA,UACqB;CACrB,MAAM,cAAc,gBAAgB;AACpC,KAAI,CAAC,YAAa,QAAO;AAEzB,QAAO,KAAK,UAAU,gBAAgB;;;;;AAMxC,SAAgB,kBACd,MACA,UACa;CACb,MAAM,eAAe,gBAAgB,MAAM,SAAS;AACpD,KAAI,CAAC,aAAc,QAAO,EAAE;AAE5B,QAAO,OAAO,OAAO,aAAa,OAAO;;;;;AAM3C,SAAgB,aACd,MACA,UACA,SACkB;CAClB,MAAM,eAAe,gBAAgB,MAAM,SAAS;AACpD,KAAI,CAAC,aAAc,QAAO;AAE1B,QAAO,aAAa,OAAO,YAAY;;;;;AAMzC,SAAgB,YAAY,iBAA6C;AACvE,KAAI,oBAAoB,OAAW,QAAO;AAC1C,KAAI,oBAAoB,EAAG,QAAO;AAGlC,QAAO,gBAAgB,QAAQ,EAAE,CAAC,QAAQ,UAAU,GAAG;;;;;AAMzD,SAAgB,mBAAmB,OAA4B;CAC7D,MAAMC,OAAiB,EAAE;AAEzB,KAAI,MAAM,UAAW,MAAK,KAAK,QAAQ;AACvC,KAAI,MAAM,UAAW,MAAK,KAAK,YAAY;AAC3C,KAAI,MAAM,kBAAmB,MAAK,KAAK,OAAO;AAC9C,KAAI,MAAM,WAAY,MAAK,KAAK,QAAQ;AAExC,QAAO;;;;;;;;;ACrLT,MAAMC,mBAAiE;CACrE,QAAQ;EACN,QAAQ;EACR,aAAa;EACd;CACD,WAAW;EACT,QAAQ;EACR,aAAa;EACd;CACD,QAAQ;EACN,QAAQ;EACR,aAAa;EACd;CACD,UAAU;EACR,QAAQ;EACR,aAAa;EACd;CACD,MAAM;EACJ,SAAS;EACT,QAAQ;EACR,aAAa;EACd;CACD,SAAS;EACP,QAAQ;EACR,aAAa;EACd;CACD,QAAQ;EACN,SAAS;EACT,QAAQ;EACR,aAAa;EACd;CACD,QAAQ;EACN,SAAS;EACT,QAAQ;EACR,aAAa;EACd;CACF;;;;AAKD,SAAgB,kBAAkB,UAA2C;AAC3E,QAAO,iBAAiB,WAAW;;;;;AAarC,SAAgB,uBACd,UACoB;AACpB,QAAO,iBAAiB,WAAW;;;;;ACrErC,MAAMC,eAAa,KAAK,SAAS,EAAE,OAAO;AAC1C,MAAM,cAAc,KAAKA,cAAY,cAAc;;;;AAUnD,SAAS,kBAAwB;AAC/B,KAAI,CAAC,WAAWA,aAAW,CACzB,WAAUA,cAAY,EAAE,WAAW,MAAM,CAAC;;;;;AAO9C,SAAgB,aAAwB;AACtC,kBAAiB;AAEjB,KAAI,CAAC,WAAW,YAAY,CAC1B,QAAO,EAAE,QAAQ,EAAE,EAAE;AAGvB,KAAI;EACF,MAAM,UAAU,aAAa,aAAa,QAAQ;AAClD,SAAO,KAAK,MAAM,QAAQ;UACnB,OAAO;AACd,UAAQ,MAAM,gCAAgC,MAAM;AACpD,SAAO,EAAE,QAAQ,EAAE,EAAE;;;;;;AAOzB,SAAgB,WAAW,QAAyB;AAClD,kBAAiB;AACjB,eAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,EAAE,EAAE,QAAQ;;;;;AAMtE,SAAgB,wBAA0C;CACxD,MAAM,SAAS,YAAY;AAE3B,KAAI,CAAC,OAAO,aACV,QAAO;AAGT,QAAO,OAAO,OAAO,OAAO,iBAAiB;;;;;AAM/C,SAAgB,gBAAgB,WAAyB;CACvD,MAAM,SAAS,YAAY;AAE3B,KAAI,CAAC,OAAO,OAAO,WACjB,OAAM,IAAI,MAAM,UAAU,UAAU,uBAAuB;AAG7D,QAAO,eAAe;AACtB,YAAW,OAAO;;;;;AAMpB,SAAgB,gBACd,WACA,aACM;CACN,MAAM,SAAS,YAAY;AAE3B,QAAO,OAAO,aAAa;AAG3B,KAAI,CAAC,OAAO,aACV,QAAO,eAAe;AAGxB,YAAW,OAAO;;;;;AAMpB,SAAgB,kBAAkB,WAAyB;CACzD,MAAM,SAAS,YAAY;AAE3B,QAAO,OAAO,OAAO;AAGrB,KAAI,OAAO,iBAAiB,UAC1B,QAAO,eAAe;AAGxB,YAAW,OAAO;;;;;AAMpB,SAAgB,aAIb;CACD,MAAM,SAAS,YAAY;AAE3B,QAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,KAAK,CAAC,MAAM,kBAAkB;EACjE;EACA,QAAQ;EACR,WAAW,SAAS,OAAO;EAC5B,EAAE;;;;;AAML,SAAgB,uBAAuB,UAA+B;AAYpE,QAXkD;EAChD,QAAQ;EACR,WAAW;EACX,QAAQ;EACR,UAAU;EACV,QAAQ;EACR,MAAM;EACN,SAAS;EACT,QAAQ;EACR,qBAAqB;EACtB,CACmB;;;;;;;;;ACnHtB,MAAaC,iBAA8C;CACzD,QAAQ;CACR,WAAW;CACX,QAAQ;CACR,UAAU;CACV,QAAQ;CACR,MAAM;CACN,SAAS;CACT,QAAQ;CACR,qBAAqB;CACtB;;;;ACjBD,MAAMC,mBAID;CACH;EACE,OAAO;EACP,OAAO;EACP,MAAM,uBAAuB,SAAS;EACvC;CACD;EACE,OAAO;EACP,OAAO;EACP,MAAM,uBAAuB,YAAY;EAC1C;CACD;EACE,OAAO;EACP,OAAO;EACP,MAAM,uBAAuB,SAAS;EACvC;CACD;EACE,OAAO;EACP,OAAO;EACP,MAAM,uBAAuB,WAAW;EACzC;CACD;EAAE,OAAO;EAAQ,OAAO;EAAQ,MAAM,uBAAuB,OAAO;EAAE;CACtE;EACE,OAAO;EACP,OAAO;EACP,MAAM,uBAAuB,UAAU;EACxC;CACD;EACE,OAAO;EACP,OAAO;EACP,MAAM,uBAAuB,SAAS;EACvC;CACD;EACE,OAAO;EACP,OAAO;EACP,MAAM,uBAAuB,SAAS;EACvC;CACD;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACF;;;;AAKD,eAAsB,gBAA+B;AACnD,OAAM,MAAM,GAAG,OAAO,GAAG,MAAM,eAAe,CAAC,CAAC;CAEhD,MAAM,SAAS,YAAY;CAE3B,MAAM,SAAU,MAAM,MAAM,OAAO;EACjC,SAAS;EACT,SAAS;GACP;IAAE,OAAO;IAAQ,OAAO;IAA0B;GAClD;IAAE,OAAO;IAAO,OAAO;IAAmB;GAC1C;IACE,OAAO;IACP,OAAO;IACP,UAAU,OAAO,WAAW;IAC7B;GACD;IACE,OAAO;IACP,OAAO;IACP,UAAU,OAAO,WAAW;IAC7B;GACF;EACF,CAAC;AAEF,KAAI,MAAM,SAAS,OAAO,EAAE;AAC1B,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;AAGjB,SAAQ,QAAR;EACE,KAAK;AACH,SAAM,YAAY;AAClB;EACF,KAAK;AACH,SAAM,WAAW;AACjB;EACF,KAAK;AACH,SAAM,cAAc;AACpB;EACF,KAAK;AACH,SAAM,cAAc;AACpB;;AAGJ,OAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;;;;;AAMhC,eAAe,aAA4B;CACzC,MAAM,SAAS,YAAY;AAE3B,KAAI,OAAO,WAAW,GAAG;AACvB,QAAM,KACJ,2EACD;AACD;;CAIF,MAAM,aAAa,MAAM,oBAAoB;CAE7C,MAAMC,QAAkB,EAAE;AAC1B,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,UAAU,MAAM,YAAY,GAAG,MAAM,KAAK,GAAG;EACnD,MAAM,WAAW,uBAAuB,MAAM,OAAO,SAAS;EAC9D,MAAM,YACJ,MAAM,OAAO,SAAS,eAAe,MAAM,OAAO;EAEpD,IAAI,OAAO,GAAG,UAAU,GAAG,KAAK,MAAM,KAAK,CAAC,KAAK,SAAS,IAAI,UAAU;AAGxE,MAAI,YAAY;GACd,MAAM,YAAY,aAChB,YACA,MAAM,OAAO,UACb,UACD;AAED,OAAI,WAAW;IACb,MAAM,WAAW,oBAAoB,UAAU;AAC/C,QAAI,SACF,SAAQ,IAAI,GAAG,IAAI,SAAS;;;AAKlC,QAAM,KAAK,KAAK;;AAGlB,OAAM,KAAK,MAAM,KAAK,KAAK,EAAE,qBAAqB;;;;;AAMpD,SAAS,oBAAoB,WAA8B;CACzD,MAAMC,QAAkB,EAAE;AAG1B,KACE,UAAU,MAAM,UAAU,UAC1B,UAAU,MAAM,WAAW,OAE3B,OAAM,KACJ,KAAK,YAAY,UAAU,KAAK,MAAM,CAAC,IAAI,YAAY,UAAU,KAAK,OAAO,CAAC,UAC/E;CAIH,MAAM,OAAO,mBAAmB,UAAU;AAC1C,KAAI,KAAK,SAAS,EAChB,OAAM,KAAK,IAAI,KAAK,KAAK,KAAK,CAAC,GAAG;AAGpC,QAAO,MAAM,KAAK,IAAI;;;;;AAMxB,eAAe,YAA2B;CACxC,MAAM,OAAO,MAAM,MAAM,KAAK;EAC5B,SAAS;EACT,aAAa;EACb,WAAW,UAAU;AACnB,OAAI,CAAC,MAAO,QAAO;AACnB,OAAI,YAAY,CAAC,MAAM,MAAM,EAAE,SAAS,MAAM,CAC5C,QAAO;;EAIZ,CAAC;AAEF,KAAI,MAAM,SAAS,KAAK,EAAE;AACxB,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;CAGjB,MAAM,WAAY,MAAM,MAAM,OAAO;EACnC,SAAS;EACT,SAAS;EACV,CAAC;AAEF,KAAI,MAAM,SAAS,SAAS,EAAE;AAC5B,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;AAMjB,iBAAgB,MAFD,MAAM,kBAAkB,SAAS,CAEnB;AAE7B,OAAM,KACJ,UAAU,GAAG,KAAK,KAAK,CAAC,oBAAoB,uBAAuB,SAAS,IAC5E,GAAG,MAAM,WAAW,CACrB;;;;;AAMH,eAAe,eAA8B;CAC3C,MAAM,SAAS,YAAY;AAE3B,KAAI,OAAO,WAAW,GAAG;AACvB,QAAM,KAAK,uBAAuB;AAClC;;CAGF,MAAM,YAAa,MAAM,MAAM,OAAO;EACpC,SAAS;EACT,SAAS,OAAO,KAAK,OAAO;GAC1B,OAAO,EAAE;GACT,OAAO,EAAE;GACT,MAAM,GAAG,uBAAuB,EAAE,OAAO,SAAS,CAAC,KAAK,EAAE,OAAO,SAAS,eAAe,EAAE,OAAO;GACnG,EAAE;EACJ,CAAC;AAEF,KAAI,MAAM,SAAS,UAAU,EAAE;AAC7B,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;AAGjB,iBAAgB,UAAU;AAE1B,OAAM,KAAK,gBAAgB,GAAG,KAAK,UAAU,CAAC,IAAI,GAAG,MAAM,WAAW,CAAC;;;;;AAMzE,eAAe,eAA8B;CAC3C,MAAM,SAAS,YAAY;AAE3B,KAAI,OAAO,WAAW,GAAG;AACvB,QAAM,KAAK,uBAAuB;AAClC;;CAGF,MAAM,YAAa,MAAM,MAAM,OAAO;EACpC,SAAS;EACT,SAAS,OAAO,KAAK,OAAO;GAC1B,OAAO,EAAE;GACT,OAAO,EAAE;GACT,MAAM,GAAG,uBAAuB,EAAE,OAAO,SAAS;GACnD,EAAE;EACJ,CAAC;AAEF,KAAI,MAAM,SAAS,UAAU,EAAE;AAC7B,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;CAGjB,MAAMC,YAAW,MAAM,MAAM,QAAQ,EACnC,SAAS,oCAAoC,UAAU,KACxD,CAAC;AAEF,KAAI,MAAM,SAASA,UAAQ,IAAI,CAACA,WAAS;AACvC,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;AAGjB,mBAAkB,UAAU;AAE5B,OAAM,KAAK,YAAY,GAAG,KAAK,UAAU,CAAC,IAAI,GAAG,MAAM,WAAW,CAAC;;;;;AAMrE,eAAe,kBAAkB,UAK9B;CACD,MAAM,eAAe,eAAe;AAGpC,KAAI,aAAa,UAAU;EACzB,MAAMC,YAAW,MAAM,MAAM,KAAK;GAChC,SAAS;GACT,aAAa;GACb,cAAc;GACf,CAAC;AAEF,MAAI,MAAM,SAASA,UAAQ,EAAE;AAC3B,SAAM,OAAO,sBAAsB;AACnC,WAAQ,KAAK,EAAE;;EAGjB,MAAMC,UAAS,MAAM,MAAM,KAAK;GAC9B,SAAS;GACT,aAAa;GACb,cAAc;GACf,CAAC;AAEF,MAAI,MAAM,SAASA,QAAM,EAAE;AACzB,SAAM,OAAO,sBAAsB;AACnC,WAAQ,KAAK,EAAE;;AAGjB,SAAO;GACL;GACA,SAASD,aAAW;GACpB,OAAOC,WAAS;GACjB;;CAIH,MAAM,SAAU,MAAM,MAAM,SAAS;EACnC,SAAS,cAAc,uBAAuB,SAAS,CAAC;EACxD,WAAW,UAAU;AACnB,OAAI,CAAC,MAAO,QAAO;;EAGtB,CAAC;AAEF,KAAI,MAAM,SAAS,OAAO,EAAE;AAC1B,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;CAIjB,MAAM,gBAAiB,MAAM,MAAM,QAAQ;EACzC,SAAS;EACT,cAAc;EACf,CAAC;AAEF,KAAI,MAAM,SAAS,cAAc,EAAE;AACjC,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;CAGjB,IAAIC;AAEJ,KAAI,eAAe;EACjB,MAAM,aAAa,kBAAkB,SAAS;EAC9C,MAAM,eAAgB,MAAM,MAAM,KAAK;GACrC,SAAS;GACT,aAAa,cAAc;GAC5B,CAAC;AAEF,MAAI,MAAM,SAAS,aAAa,EAAE;AAChC,SAAM,OAAO,sBAAsB;AACnC,WAAQ,KAAK,EAAE;;AAGjB,YAAU,gBAAgB;;CAI5B,MAAM,QAAQ,MAAM,uBAAuB,UAAU,aAAa;AAElE,QAAO;EACL;EACA;EACA;EACA;EACD;;;;;AAMH,eAAe,uBACb,UACA,cAC6B;CAC7B,MAAM,aAAa,MAAM,oBAAoB;AAG7C,KAAI,YAAY;EACd,MAAM,kBAAkB,kBAAkB,YAAY,SAAS;AAE/D,MAAI,gBAAgB,SAAS,EAC3B,QAAO,MAAM,oBAAoB,iBAAiB,aAAa;;AAKnE,QAAO,MAAM,qBAAqB,aAAa;;;;;AAMjD,eAAe,oBACb,iBACA,cAC6B;CAC7B,MAAM,eAAe,gBAAgB,MAAM,GAAG,GAAG,CAAC,KAAK,MAAM;EAC3D,MAAMJ,QAAkB,EAAE;AAG1B,MAAI,EAAE,MAAM,UAAU,UAAa,EAAE,MAAM,WAAW,OACpD,OAAM,KACJ,IAAI,YAAY,EAAE,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,KAAK,OAAO,CAAC,SAC9D;EAIH,MAAM,OAAO,mBAAmB,EAAE;AAClC,MAAI,KAAK,SAAS,EAChB,OAAM,KAAK,KAAK,KAAK,KAAK,CAAC;AAG7B,SAAO;GACL,OAAO,EAAE;GACT,OAAO,EAAE,QAAQ,EAAE;GACnB,MAAM,MAAM,SAAS,IAAI,MAAM,KAAK,MAAM,GAAG;GAC9C;GACD;AAGF,cAAa,KAAK;EAChB,OAAO;EACP,OAAO,gBAAgB,aAAa;EACpC,MAAM;EACP,CAAC;AACF,cAAa,KAAK;EAChB,OAAO;EACP,OAAO;EACP,MAAM;EACP,CAAC;CAEF,MAAM,gBAAiB,MAAM,MAAM,OAAO;EACxC,SAAS;EACT,SAAS;EACV,CAAC;AAEF,KAAI,MAAM,SAAS,cAAc,EAAE;AACjC,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;AAGjB,KAAI,kBAAkB,cACpB;AAGF,KAAI,kBAAkB,aACpB,QAAO,MAAM,mBAAmB,aAAa;AAG/C,QAAO;;;;;AAMT,eAAe,qBACb,cAC6B;CAC7B,MAAM,aAAc,MAAM,MAAM,QAAQ;EACtC,SAAS,sBAAsB,aAAa;EAC5C,cAAc;EACf,CAAC;AAEF,KAAI,MAAM,SAAS,WAAW,EAAE;AAC9B,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;AAGjB,KAAI,WACF;AAGF,QAAO,MAAM,mBAAmB,aAAa;;;;;AAM/C,eAAe,mBAAmB,cAAuC;CACvE,MAAM,aAAc,MAAM,MAAM,KAAK;EACnC,SAAS;EACT,aAAa;EACb,cAAc;EACf,CAAC;AAEF,KAAI,MAAM,SAAS,WAAW,EAAE;AAC9B,QAAM,OAAO,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;AAGjB,QAAO,cAAc;;;;;;;;ACrXvB,SAAgB,aAAa,KAAiC;AAC5D,QACE,OAAO,QAAQ,YACf,QAAQ,QACR,UAAU,OACV,QAAQ,OACR,OAAO,IAAI,OAAO;;AAwBtB,SAAgB,2BACd,KACiC;AACjC,QACE,OAAO,QAAQ,YACf,QAAQ,QACR,UAAU,OACV,IAAI,SAAS;;;;;;;;;;ACjKjB,IAAa,mBAAb,MAA8B;CAC5B,YAAY,AAAQK,SAAkC;EAAlC;;;;;CAKpB,MAAM,OAAO,SAA2C;AACtD,MAAI;AACF,WAAQ,QAAQ,MAAhB;IACE,KAAK,aACH,QAAO,KAAK,iBAAiB,QAAQ,GAAG;IAE1C,KAAK,iBACH,QAAO,KAAK,qBAAqB,QAAQ,IAAI,QAAQ,QAAQ,KAAK;IAEpE,KAAK,MACH,QAAO,KAAK,UACV,QAAQ,IACR,QAAQ,QAAQ,OAChB,QAAQ,QAAQ,QACjB;IAEH,KAAK,eACH,QAAO,KAAK,kBAAkB,QAAQ,IAAI,QAAQ,QAAQ;IAE5D,KAAK,gBACH,QAAO,KAAK,mBAAmB,QAAQ,IAAI,QAAQ,QAAQ;IAE7D,KAAK,iBACH,QAAO,KAAK,oBAAoB,QAAQ,IAAI,QAAQ,QAAQ;IAE9D,KAAK,qBACH,QAAO,KAAK,wBAAwB,QAAQ,IAAI,QAAQ,QAAQ;IAElE,KAAK,cACH,QAAO,KAAK,iBAAiB,QAAQ,IAAI,QAAQ,QAAQ;IAE3D,SAAS;KACP,MAAM,iBAAiB;AACvB,YAAO;MACL,MAAM;MACN,IAAI,eAAe;MACnB,SAAS;MACT,OAAO,yBAAyB,eAAe;MAChD;;;WAGE,OAAO;AACd,UAAO;IACL,MAAM;IACN,IAAI,QAAQ;IACZ,SAAS;IACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC9D;;;;;;CAOL,AAAQ,iBACN,WACkC;AASlC,SAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAZa,YAAY,CACkB,KAAK,OAAO;IACvD,MAAM,EAAE;IACR,UAAU,EAAE,OAAO;IACnB,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE;IACd,EAAE;GAOF;;;;;CAMH,AAAQ,qBACN,WACA,MACyC;AAEzC,MAAI,CAAC,KAAK,QAAQ,YAChB,QAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,OAAO;GACR;EAIH,IAAIC,cAA0C;AAE9C,MAAI,MAAM;GAER,MAAM,QADS,YAAY,CACN,MAAM,MAAM,EAAE,SAAS,KAAK;AACjD,OAAI,MACF,eAAc;IACZ,UAAU,MAAM,OAAO;IACvB,OAAO,MAAM,OAAO;IACpB,QAAQ,MAAM,OAAO;IACrB,SAAS,MAAM,OAAO;IACvB;SAEE;GACL,MAAM,UAAU,uBAAuB;AACvC,OAAI,QACF,eAAc;IACZ,UAAU,QAAQ;IAClB,OAAO,QAAQ;IACf,QAAQ,QAAQ;IAChB,SAAS,QAAQ;IAClB;;AAIL,SAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAAM;GACP;;;;;CAMH,AAAQ,UACN,WACA,OACA,SACmB;EACnB,MAAM,SAAS,YAAY,KAAK,QAAQ,MAAM;AAE9C,UAAQ,OAAR;GACE,KAAK;AACH,YAAQ,IAAI,GAAG,OAAO,GAAG,UAAU;AACnC;GACF,KAAK;AACH,YAAQ,KAAK,GAAG,OAAO,GAAG,UAAU;AACpC;GACF,KAAK;AACH,YAAQ,MAAM,GAAG,OAAO,GAAG,UAAU;AACrC;;AAGJ,SAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAAM;GACP;;;;;CAMH,MAAc,kBACZ,WACA,SAM8B;EAC9B,MAAM,SAAS,MAAMC,MAAE,KAAK;GAC1B,SAAS,QAAQ;GACjB,cAAc,QAAQ;GACtB,aAAa,QAAQ;GACrB,UAAU,QAAQ,aACb,UAAU;AACT,QAAI,SAAS,MAAM,SAAS,QAAQ,UAClC,QAAO,iBAAiB,QAAQ,UAAU;OAI9C;GACL,CAAC;AAEF,MAAIA,MAAE,SAAS,OAAO,CACpB,QAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,OAAO;GACR;AAGH,SAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAAM;GACP;;;;;CAMH,MAAc,mBACZ,WACA,SAQ8B;EAC9B,MAAM,gBAAgB,QAAQ,QAAQ,KAAK,SAAS;GAClD,OAAO,IAAI;GACX,OAAO,IAAI;GACX,GAAI,IAAI,eAAe,EAAE,MAAM,IAAI,aAAa;GACjD,EAAE;EAEH,MAAM,SAAS,MAAMA,MAAE,OAAO;GAC5B,SAAS,QAAQ;GACjB,SAAS;GACV,CAAC;AAEF,MAAIA,MAAE,SAAS,OAAO,CACpB,QAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,OAAO;GACR;AAGH,SAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAAM;GACP;;;;;CAMH,MAAc,oBACZ,WACA,SAI+B;EAC/B,MAAM,SAAS,MAAMA,MAAE,QAAQ;GAC7B,SAAS,QAAQ;GACjB,cAAc,QAAQ;GACvB,CAAC;AAEF,MAAIA,MAAE,SAAS,OAAO,CACpB,QAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,OAAO;GACR;AAGH,SAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAAM;GACP;;;;;CAMH,MAAc,wBACZ,WACA,SASgC;EAChC,MAAM,gBAAgB,QAAQ,QAAQ,KAAK,SAAS;GAClD,OAAO,IAAI;GACX,OAAO,IAAI;GACX,GAAI,IAAI,eAAe,EAAE,MAAM,IAAI,aAAa;GACjD,EAAE;EAEH,MAAM,SAAS,MAAMA,MAAE,YAAY;GACjC,SAAS,QAAQ;GACjB,SAAS;GACT,UAAU,QAAQ;GACnB,CAAC;AAEF,MAAIA,MAAE,SAAS,OAAO,CACpB,QAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,OAAO;GACR;AAGH,SAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAAM;GACP;;;;;CAMH,MAAc,iBACZ,WACA,SAY+C;EAC/C,MAAMC,SAAkC,EAAE;AAE1C,MAAI,QAAQ,MACV,SAAQ,IAAI,KAAK,GAAG,KAAK,QAAQ,MAAM,CAAC,IAAI;AAG9C,OAAK,MAAM,SAAS,QAAQ,QAAQ;GAClC,MAAM,QAAQ,MAAM,cAChB,GAAG,MAAM,MAAM,IAAI,MAAM,YAAY,KACrC,MAAM;AAEV,OAAI,MAAM,SAAS,WAAW;IAC5B,MAAM,QAAQ,MAAMD,MAAE,QAAQ;KAC5B,SAAS;KACT,cAAc,MAAM;KACrB,CAAC;AAEF,QAAIA,MAAE,SAAS,MAAM,CACnB,QAAO;KACL,MAAM;KACN,IAAI;KACJ,SAAS;KACT,OAAO;KACR;AAGH,WAAO,MAAM,QAAQ;cACZ,MAAM,SAAS,UAAU,MAAM,YAAY,QAAQ;IAC5D,MAAM,QAAQ,MAAMA,MAAE,OAAO;KAC3B,SAAS;KACT,SAAS,MAAM,WAAW,KAAK,OAAO;MACpC,OAAO;MACP,OAAO;MACR,EAAE;KACJ,CAAC;AAEF,QAAIA,MAAE,SAAS,MAAM,CACnB,QAAO;KACL,MAAM;KACN,IAAI;KACJ,SAAS;KACT,OAAO;KACR;AAGH,WAAO,MAAM,QAAQ;cACZ,MAAM,SAAS,UAAU;IAClC,MAAM,WAAW,MAAMA,MAAE,KAAK;KAC5B,SAAS;KACT,cAAc,MAAM,cAAc,UAAU;KAC5C,WAAW,UAAU;AACnB,UAAI,SAAS,OAAO,MAAM,OAAO,WAAW,MAAM,CAAC,CACjD,QAAO;;KAIZ,CAAC;AAEF,QAAIA,MAAE,SAAS,SAAS,CACtB,QAAO;KACL,MAAM;KACN,IAAI;KACJ,SAAS;KACT,OAAO;KACR;AAGH,WAAO,MAAM,QAAQ,OAAO,WAAW,SAAS;UAC3C;IACL,MAAM,QAAQ,MAAMA,MAAE,KAAK;KACzB,SAAS;KACT,cAAc,MAAM;KACrB,CAAC;AAEF,QAAIA,MAAE,SAAS,MAAM,CACnB,QAAO;KACL,MAAM;KACN,IAAI;KACJ,SAAS;KACT,OAAO;KACR;AAGH,WAAO,MAAM,QAAQ;;;AAIzB,SAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAAM;GACP;;;;;;AAOL,SAAgB,uBACd,SACkB;AAClB,QAAO,IAAI,iBAAiB,QAAQ;;;;;;;;;;;;;AC3atC,eAAsB,cACpB,SACuB;CACvB,MAAM,EAAE,YAAY,cAAM,OAAO,eAAe,gBAAgB;AAGhE,OAAM,eAAe,WAAW,cAAc;CAG9C,MAAM,qBAAqB,QAAQ,QAAQ,KAAK,EAAE,WAAW;CAO7D,MAAM,eAAe,QAAQ,WAAW,kCAAkC;AAG1E,KAAI,CAAC,eAAe,qBAAqB,EAAE;AACzC,UAAQ,KAAK,oDAAoD;AACjE,UAAQ,KAAK,yCAAyC;QACjD;AACL,UAAQ,IAAI,4BAA4B;AACxC,UAAQ,IACN,mBAAmB,cAAc,WAAW,SAAS,OAAO,QAC7D;AACD,UAAQ,IACN,qBAAqB,cAAc,WAAW,WAAW,OAAO,QACjE;AACD,UAAQ,IACN,eAAe,cAAc,QAAQ,eAAe,KAAK,KAAK,IAAI,SACnE;AACD,UAAQ,IAAI,GAAG;;CAIjB,MAAME,YAAU,WAAW;CAM3B,MAAM,QAAQ,MALS,MAAM,eAAe,gBAAgBA,UAAQ,EAKhC;EAClC,OAAO;EACP,OAAO;GAAC;GAAW;GAAW;GAAW;GAAM;EAC/C,KAAK;GACH,GAAG,QAAQ;GACX,kBAAkB;GACnB;EACF,CAAC;CAGF,MAAM,mBAAmB,uBAAuB;EAC9C;EACA;EACA;EACD,CAAC;AAGF,OAAM,GAAG,WAAW,OAAO,YAAqB;AAC9C,MAAI,aAAa,QAAQ,EAAE;GACzB,MAAM,WAAW,MAAM,iBAAiB,OAAO,QAAQ;AACvD,SAAM,KAAK,SAAS;;GAEtB;CAGF,MAAMC,cAAkC;EACtC,MAAM;EACN,YAAY;EACZ;EACA;EACA,aAAa;GACX;GACA;GACD;EACF;AACD,OAAM,KAAK,YAAY;AAGvB,QAAO,IAAI,SAAS,WAAS,WAAW;EACtC,IAAIC,kBAAmD;AAEvD,QAAM,GAAG,YAAY,YAAqB;AACxC,OAAI,2BAA2B,QAAQ,CACrC,mBAAkB;IAEpB;AAEF,QAAM,GAAG,UAAU,UAAU;AAC3B,0BAAO,IAAI,MAAM,0BAA0B,MAAM,UAAU,CAAC;IAC5D;AAEF,QAAM,GAAG,SAAS,SAAS;AACzB,OAAI,gBACF,WAAQ;IACN,UAAU,QAAQ;IAClB,QAAQ,gBAAgB;IACxB,OAAO,gBAAgB;IACxB,CAAC;OAEF,WAAQ;IACN,UAAU,QAAQ;IAClB,OAAO,SAAS,IAAI,4BAA4B,SAAS;IAC1D,CAAC;IAEJ;GACF;;;;;;;;;;;;ACxIJ,MAAa,WAAW;CACtB,cAAc;CACd,UAAU;CACV,cAAc;CACd,WAAW;CACX,WAAW;CACX,YAAY;CACb;;;;AAKD,MAAa,QAAQ;CACnB,YAAY;CACZ,WAAW;CACX,kBAAkB;CAClB,aAAa;CACd;;;;AAKD,MAAa,WAAW;CAEtB,aAAa;CAEb,kBAAkB;CACnB;;;;AAKD,MAAa,kBAAkB;CAC7B;CACA;CACA;CACA;CACD;;;;;;;;ACjCD,SAAgB,YAAqB;AACnC,QAAO,QAAQ,IAAI,SAAS,kBAAkB;;;;;;AAOhD,SAAgB,QAAiB;AAC/B,QAAO,QAAQ,IAAI,SAAS,cAAc;;;;;;AAe5C,SAAgB,aAAsB;AACpC,QAAO,QAAQ,IAAI,SAAS,eAAe;;;;;AAM7C,SAAgB,cAAkC;AAChD,QAAO,QAAQ,IAAI,SAAS;;;;;AAM9B,SAAgB,eAAmC;AACjD,QAAO,QAAQ,IAAI,SAAS;;;;;;;;;AC/C9B,SAAgB,QAAiB;AAC/B,QAAO,QACL,QAAQ,OAAO,SAAS,QAAQ,MAAM,SAAS,CAAC,QAAQ,IAAI,GAC7D;;;;;;;;;ACAH,eAAsB,eACpB,MACA,SACY;AACZ,KAAI,CAAC,QAAQ,KACX,OAAM,IAAI,MAAM,kDAAkD;AAGpE,QAAO,IAAI,SAAS,WAAS,WAAW;EACtC,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ;EAExD,MAAMC,UAAsB;GAC1B;GACA,IAAI;GACJ;GACD;EAGD,MAAM,mBAAmB,YAAqB;AAC5C,OACE,OAAO,YAAY,YACnB,YAAY,QACZ,UAAU,WACV,QAAQ,SAAS,cACjB,QAAQ,WACR,QAAQ,OAAO,WACf;AACA,YAAQ,IAAI,WAAW,gBAAgB;IAEvC,MAAM,WAAW;AACjB,QAAI,SAAS,QACX,WAAQ,SAAS,KAAK;QAEtB,QAAO,IAAI,MAAM,SAAS,MAAM,CAAC;;;AAKvC,UAAQ,GAAG,WAAW,gBAAgB;AAGtC,MAAI,CAAC,QAAQ,KAAM,QAAQ,EAAE;AAE3B,WAAQ,IAAI,WAAW,gBAAgB;AACvC,0BAAO,IAAI,MAAM,6BAA6B,CAAC;AAC/C;;AAIF,mBAAiB;AACf,WAAQ,IAAI,WAAW,gBAAgB;AACvC,0BAAO,IAAI,MAAM,wBAAwB,OAAO,CAAC;KAChD,SAAS,iBAAiB;GAC7B;;;;;;;;;;;;;AC/CJ,eAAsB,QACpB,SACA,eAAe,OACG;AAElB,KAAI,WAAW,CACb,QAAO,eAAwB,kBAAkB;EAAE;EAAS;EAAc,CAAC;AAG7E,KAAI,CAAC,OAAO,EAAE;AAEZ,MAAI,OAAO,CACT,SAAQ,KACN,8EAA8E,aAAa,SAAS,UACrG;AAEH,SAAO;;CAGT,MAAM,SAAS,MAAMC,MAAE,QAAQ;EAC7B;EACA,cAAc;EACf,CAAC;AAEF,KAAIA,MAAE,SAAS,OAAO,EAAE;AACtB,QAAE,OAAO,sBAAsB;AAC/B,UAAQ,KAAK,EAAE;;AAGjB,QAAO;;;;;;;;;;;;;;;;;;;ACCT,eAAsB,OAAmB,QAAqC;AAE5E,KAAI,WAAW,CACb,QAAO,eAAkB,iBAAiB;EACxC,QAAQ,OAAO,UAAU;EACzB,SAAS,OAAO;EACjB,CAAC;AAIJ,KAAI,CAAC,OAAO,EAAE;AAEZ,MAAI,OAAO,CACT,OAAM,IAAI,MACR,gIAAgI,OAAO,SACxI;EAGH,MAAM,cAAc,OAAO,QAAQ;AACnC,MAAI,CAAC,YACH,OAAM,IAAI,MAAM,sBAAsB;AAExC,SAAO,YAAY;;CAKrB,MAAM,gBAAgB,OAAO,QAAQ,KAAK,SAAS;EACjD,OAAO,IAAI;EACX,OAAO,IAAI;EACX,GAAI,IAAI,eAAe,EAAE,MAAM,IAAI,aAAa;EACjD,EAAE;CAEH,MAAM,SAAS,MAAMC,MAAE,OAAO;EAC5B,SAAS,OAAO,UAAU;EAC1B,SAAS;EACV,CAAC;AAEF,KAAIA,MAAE,SAAS,OAAO,EAAE;AACtB,QAAE,OAAO,sBAAsB;AAC/B,UAAQ,KAAK,EAAE;;AAGjB,QAAO;;;;;;;;AC7CT,SAAgB,WACd,MACA,SAOQ;CACR,IAAI,SAAS;AAGb,KAAI,SAAS,MACX,SAAQ,QAAQ,OAAhB;EACE,KAAK;AACH,YAASC,KAAG,IAAI,OAAO;AACvB;EACF,KAAK;AACH,YAASA,KAAG,MAAM,OAAO;AACzB;EACF,KAAK;AACH,YAASA,KAAG,OAAO,OAAO;AAC1B;EACF,KAAK;AACH,YAASA,KAAG,KAAK,OAAO;AACxB;EACF,KAAK;AACH,YAASA,KAAG,QAAQ,OAAO;AAC3B;EACF,KAAK;AACH,YAASA,KAAG,KAAK,OAAO;AACxB;EACF,KAAK;AACH,YAASA,KAAG,MAAM,OAAO;AACzB;EACF,KAAK;AACH,YAASA,KAAG,KAAK,OAAO;AACxB;;AAKN,KAAI,SAAS,KAAM,UAASA,KAAG,KAAK,OAAO;AAC3C,KAAI,SAAS,IAAK,UAASA,KAAG,IAAI,OAAO;AACzC,KAAI,SAAS,OAAQ,UAASA,KAAG,OAAO,OAAO;AAC/C,KAAI,SAAS,UAAW,UAASA,KAAG,UAAU,OAAO;AAErD,QAAO;;;;;;;;AC9CT,SAAS,UAAU,MAAc,OAAe,OAA4B;CAC1E,MAAM,aAAa,UAAU,KAAK,CAAC;CACnC,MAAM,UAAU,KAAK,IAAI,GAAG,QAAQ,WAAW;AAE/C,SAAQ,OAAR;EACE,KAAK,QACH,QAAO,IAAI,OAAO,QAAQ,GAAG;EAC/B,KAAK,UAAU;GACb,MAAM,UAAU,KAAK,MAAM,UAAU,EAAE;GACvC,MAAM,WAAW,UAAU;AAC3B,UAAO,IAAI,OAAO,QAAQ,GAAG,OAAO,IAAI,OAAO,SAAS;;EAE1D,QACE,QAAO,OAAO,IAAI,OAAO,QAAQ;;;;;;AAOvC,SAAS,UAAU,KAAqB;AAEtC,QAAO,IAAI,QAAQ,mBAAmB,GAAG;;;;;AAM3C,SAAS,sBACP,SACA,MACA,YACU;AACV,QAAO,QAAQ,KAAK,QAAQ;AAE1B,MAAI,IAAI,UAAU,OAChB,QAAO,IAAI;EAIb,IAAI,WAAW,aAAa,IAAI,OAAO,SAAS;AAEhD,OAAK,MAAM,OAAO,MAAM;GACtB,MAAM,QAAQ,IAAI,IAAI;GAItB,MAAM,SAAS,UAHG,IAAI,YAClB,IAAI,UAAU,OAAO,IAAI,GACzB,OAAO,SAAS,GAAG,CACY,CAAC;AACpC,cAAW,KAAK,IAAI,UAAU,OAAO;;AAGvC,SAAO;GACP;;;;;AAMJ,SAAS,WAAc,OAAgB,KAAQ,QAAgC;AAC7E,KAAI,OAAO,UACT,QAAO,OAAO,UAAU,OAAO,IAAI;AAGrC,KAAI,UAAU,QAAQ,UAAU,OAC9B,QAAOC,KAAG,IAAI,IAAI;AAGpB,QAAO,OAAO,MAAM;;;;;AAMtB,SAAS,UAAa,QAAgC;CACpD,MAAM,EACJ,SACA,MACA,aAAa,MACb,cAAc,MACd,UACE;CACJ,MAAMC,QAAkB,EAAE;CAG1B,MAAM,SAAS,sBAAsB,SAAS,MAAM,WAAW;AAG/D,KAAI,OAAO;AACT,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,WAAW,GAAG,SAAS,EAAE,MAAM,MAAM,CAAC,CAAC;AAClD,QAAM,KAAK,GAAG;;AAIhB,KAAI,YAAY;EACd,MAAM,cAAc,QAAQ,KAAK,KAAK,MAAM;AAE1C,UAAO,UADM,WAAW,IAAI,QAAQ;IAAE,MAAM;IAAM,OAAO;IAAQ,CAAC,EAC3C,OAAO,IAAK,IAAI,SAAS,OAAO;IACvD;AAEF,QAAM,KAAK,YAAY,KAAK,IAAI,CAAC;AAGjC,MAAI,aAAa;GACf,MAAM,iBAAiB,OAAO,KAAK,MAAM,IAAI,OAAO,EAAE,CAAC;AACvD,SAAM,KAAKD,KAAG,KAAK,eAAe,KAAK,IAAI,CAAC,CAAC;;;AAKjD,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,QAAQ,KAAK,KAAK,MAAM;GACpC,MAAM,QAAQ,IAAI,IAAI;AAEtB,UAAO,UADW,WAAW,OAAO,KAAK,IAAI,EACjB,OAAO,IAAK,IAAI,SAAS,OAAO;IAC5D;AAEF,QAAM,KAAK,MAAM,KAAK,IAAI,CAAC;;AAI7B,KAAI,eAAe,KAAK,SAAS,GAAG;EAClC,MAAM,iBAAiB,OAAO,KAAK,MAAM,IAAI,OAAO,EAAE,CAAC;AACvD,QAAM,KAAKA,KAAG,KAAK,eAAe,KAAK,IAAI,CAAC,CAAC;;AAG/C,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,YAAe,QAAgC;CACtD,MAAM,EAAE,SAAS,MAAM,aAAa,MAAM,UAAU;CACpD,MAAMC,QAAkB,EAAE;CAG1B,MAAM,SAAS,sBAAsB,SAAS,MAAM,WAAW;AAG/D,KAAI,OAAO;AACT,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,MAAM;AACjB,QAAM,KAAK,GAAG;;AAIhB,KAAI,YAAY;EACd,MAAM,cAAc,QAAQ,KAAK,KAAK,MACpC,UAAU,IAAI,QAAQ,OAAO,IAAK,IAAI,SAAS,OAAO,CACvD;AACD,QAAM,KAAK,YAAY,KAAK,IAAI,CAAC;EAGjC,MAAM,YAAY,QAAQ,KAAK,GAAG,MAAM,IAAI,OAAO,OAAO,GAAI,CAAC,CAAC,KAAK,IAAI;AACzE,QAAM,KAAK,UAAU;;AAIvB,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,QAAQ,KAAK,KAAK,MAAM;GACpC,MAAM,QAAQ,IAAI,IAAI;AAEtB,UAAO,UAAU,UADC,WAAW,OAAO,KAAK,IAAI,CACR,EAAE,OAAO,IAAK,IAAI,SAAS,OAAO;IACvE;AACF,QAAM,KAAK,MAAM,KAAK,IAAI,CAAC;;AAG7B,QAAO,MAAM,KAAK,KAAK;;;;;;;;;;;;;;;;;;;;;;;AAwBzB,SAAgB,MACd,QACM;CACN,MAAMC,WAAS,OAAO,GAAG,UAAU,OAAO,GAAG,YAAY,OAAO;AAChE,SAAQ,IAAIA,SAAO;;;;;;;;;;ACxOrB,SAAgB,OAAO,QAAuB;AAC5C,KAAI,WAAW,UAAa,WAAW,KACrC;AAGF,KAAI,OAAO,WAAW,SACpB,SAAQ,IAAI,OAAO;KAEnB,SAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;;;;;ACNhD,MAAM,aAAa,KAAK,SAAS,EAAE,MAAM,WAAW;AACpD,MAAM,mBAAmB,KAAK,YAAY,MAAM,iBAAiB;;;;AA0BjE,SAAgB,mBAA2B;CAEzC,MAAM,WAAW,aAAa;AAC9B,KAAI,SACF,QAAO;CAIT,MAAM,YAAY,cAAc;AAChC,KAAI,UACF,QAAO;CAIT,MAAM,aAAa,QAAQ,KAAK,MAAM;AACtC,KAAI,WAAW,WAAW,IAAI,IAAI,WAAW,WAAW,OAAO,CAC7D,QAAO,SAAS;AAIlB,QAAO,cAAc;;;;;AAMvB,SAAgB,WAAW,OAAuB;AAChD,KAAI,MAAM,WAAW,SAAS,EAAE;EAC9B,MAAM,OAAO,MAAM,MAAM,EAAE;EAC3B,MAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,SAAO,MAAM,MAAM,SAAS,MAAM;;AAGpC,KAAI,MAAM,WAAW,cAAc,CAEjC,QADc,MAAM,MAAM,IAAI,CACjB,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;AAGpC,QAAO;;;;;AAMT,SAAS,uBAA6B;AACpC,KAAI,CAAC,WAAW,WAAW,CACzB,WAAU,YAAY,EAAE,WAAW,MAAM,CAAC;;;;;AAO9C,SAAgB,kBAAqC;AACnD,uBAAsB;AAEtB,KAAI,CAAC,WAAW,iBAAiB,CAC/B,QAAO,EAAE,aAAa,EAAE,EAAE;AAG5B,KAAI;EACF,MAAM,UAAU,aAAa,kBAAkB,QAAQ;AACvD,SAAO,KAAK,MAAM,QAAQ;UACnB,OAAO;AACd,UAAQ,MAAM,qCAAqC,MAAM;AACzD,SAAO,EAAE,aAAa,EAAE,EAAE;;;;;;AAO9B,SAAgB,gBAAgB,QAAiC;AAC/D,uBAAsB;AACtB,eAAc,kBAAkB,KAAK,UAAU,QAAQ,MAAM,EAAE,EAAE,QAAQ;;;;;AAM3E,eAAe,kBACb,OACA,SACkB;AAElB,KAAI,CAAC,OAAO,EAAE;AACZ,UAAQ,MACN,+BAA+B,QAAQ,KAAK,MAAM,kCACnD;AACD,UAAQ,MACN,uFACD;AACD,SAAO;;AAGT,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,QAAQ,QAAQ,0CAA0C;AACtE,SAAQ,IAAI,WAAW,QAAQ;AAC/B,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,6DAA6D;AACzE,SAAQ,IAAI,GAAG;CAEf,MAAM,SAAS,MAAM,OAAO;EAC1B,QAAQ;EACR,SAAS;GACP;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IAAE,MAAM;IAAU,OAAO;IAAU,aAAa;IAAmB;GACpE;EACF,CAAC;AAGF,KAAI,WAAW,SACb,QAAO;AAIT,KAAI,WAAW,UAAU;EACvB,MAAM,SAAS,iBAAiB;AAChC,SAAO,YAAY,SAAS;GAC1B,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,QAAQ;GACT;AACD,kBAAgB,OAAO;AACvB,SAAO;;AAIT,QAAO;;;;;;AAOT,eAAsB,sBAAsB,OAAiC;AAE3E,KAAI,YAAY,CACd,QAAO;CAKT,MAAM,SADS,iBAAiB,CACV,YAAY;AAGlC,KAAI,UAAU,OAAO,WAAW,SAC9B,QAAO;AAKT,QAAO,MAAM,kBAAkB,OADf,WAAW,MAAM,CACa;;;;;AAMhD,SAAgB,iBAAiB,OAAqB;CACpD,MAAM,SAAS,iBAAiB;AAChC,QAAO,OAAO,YAAY;AAC1B,iBAAgB,OAAO;;;;;;AAOzB,SAAgB,kBAKb;CACD,MAAM,SAAS,iBAAiB;AAEhC,QAAO,OAAO,QAAQ,OAAO,YAAY,CAAC,KAAK,CAAC,OAAO,aAAa;EAClE;EACA,SAAS,WAAW,MAAM;EAC1B,WAAW,OAAO;EAClB,QAAQ,OAAO;EAChB,EAAE;;;;;;AAOL,eAAe,qBACb,OACA,SACsC;AACtC,KAAI,CAAC,OAAO,EAAE;AACZ,UAAQ,MAAM,uCAAuC,QAAQ,KAAK,MAAM,GAAG;AAC3E,UAAQ,MACN,iGACD;AACD,SAAO;;CAGT,MAAM,UAAU,SAAS;CACzB,MAAM,aAAa,QAAQ,KAAK;AAEhC,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,uCAAuC,UAAU;AAC7D,SAAQ,IAAI,GAAG;AAGf,SAAQ,IAAI,6BAA6B;CACzC,MAAM,eAAe,MAAM,OAAO;EAChC,QAAQ;EACR,SAAS;GACP;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACF;EACF,CAAC;CAGF,IAAIC,WAAqB,CAAC,KAAK,SAAS,OAAO,CAAC;AAEhD,KAAI,iBAAiB,YACnB,YAAW;EACT,KAAK,SAAS,OAAO;EACrB,KAAK,SAAS,OAAO;EACrB,KAAK,SAAS,OAAO;EACrB,KAAK,SAAS,SAAS;EACxB;UACQ,iBAAiB,WAC1B,YAAW,CAAC,QAAQ;AAKtB,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,8BAA8B;CAC1C,MAAM,gBAAgB,MAAM,OAAO;EACjC,QAAQ;EACR,SAAS;GACP;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa,kBAAkB;IAChC;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACF;EACF,CAAC;CAEF,IAAIC,aAAuB,EAAE;AAC7B,KAAI,kBAAkB,UACpB,cAAa,CAAC,WAAW;UAChB,kBAAkB,OAE3B,cAAa,CADE,QAAQ,IAAI,UAAU,QAAQ,IAAI,QAAQ,OACpC;CAIvB,MAAM,YAAY;EAChB,KAAK,SAAS,OAAO;EACrB,KAAK,SAAS,OAAO;EACrB,KAAK,SAAS,OAAO;EACrB,KAAK,SAAS,SAAS;EACxB;AAGD,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,qBAAqB;CACjC,MAAM,gBAAgB,MAAM,OAAO;EACjC,QAAQ;EACR,SAAS;GACP;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACF;EACF,CAAC;CAEF,IAAIC,iBAA2B,EAAE;AACjC,KAAI,kBAAkB,WACpB,kBAAiB;EACf;EACA;EACA;EACD;UACQ,kBAAkB,SAC3B,kBAAiB;EACf;EACA;EACA;EACA;EACA;EACD;UACQ,kBAAkB,MAC3B,kBAAiB,CAAC,IAAI;AAIxB,SAAQ,IAAI,GAAG;CACf,MAAM,WAAW,MAAM,OAAO;EAC5B,QAAQ;EACR,SAAS;GACP;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACF;EACF,CAAC;AAGF,KAAI,aAAa,SACf,QAAO;CAIT,MAAMC,gBAAsC;EAC1C,SAAS;GACP;GACA,eAAe,EAAE;GAClB;EACD,YAAY;GACV;GACA;GACA;GACD;EACF;AAGD,KAAI,aAAa,UAAU;EACzB,MAAM,SAAS,iBAAiB;AAChC,SAAO,YAAY,SAAS;GAC1B;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,QAAQ;GACT;AACD,kBAAgB,OAAO;;AAGzB,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,iCAAiC;AAC7C,QAAO;;;;;;;;;AAUT,eAAsB,oBACpB,OACsC;CACtC,MAAM,UAAU,SAAS;AAGzB,KAAI,YAAY,CAEd,QAAO;EACL,SAAS;GAAE,gBAAgB,CAAC,IAAI;GAAE,eAAe,EAAE;GAAE;EACrD,YAAY;GACV,UAAU,CAAC,KAAK,SAAS,OAAO,CAAC;GACjC,YAAY,CAAC,IAAI;GACjB,WAAW;IACT,KAAK,SAAS,OAAO;IACrB,KAAK,SAAS,OAAO;IACrB,KAAK,SAAS,OAAO;IACrB,KAAK,SAAS,SAAS;IACxB;GACF;EACF;CAIH,MAAM,SADS,iBAAiB,CACV,YAAY;AAGlC,KAAI,QAAQ,WAAW,YAAY,OAAO,cACxC,QAAO,OAAO;AAKhB,QAAO,MAAM,qBAAqB,OADlB,WAAW,MAAM,CACgB;;;;;AAMnD,SAAgB,sBAA4B;AAC1C,iBAAgB,EAAE,aAAa,EAAE,EAAE,CAAC;;;;;;;;AC3dtC,eAAsB,qBAAoC;AAsBxD,SArBe,MAAM,OAAO;EAC1B,QAAQ;EACR,SAAS;GACP;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACF;EACF,CAAC,EAEF;EACE,KAAK;AACH,SAAM,uBAAuB;AAC7B;EACF,KAAK;AACH,SAAM,wBAAwB;AAC9B;EACF,KAAK;AACH,SAAM,2BAA2B;AACjC;;;;;;AAON,eAAe,wBAAuC;CACpD,MAAM,cAAc,iBAAiB;AAErC,KAAI,YAAY,WAAW,GAAG;AAC5B,SAAO,kCAAkC;AACzC;;AAGF,QAAO,8BAA8B;AAErC,OAAM;EACJ,SAAS,CACP;GAAE,KAAK;GAAO,QAAQ;GAAO,EAC7B;GAAE,KAAK;GAAa,QAAQ;GAAc,CAC3C;EACD,MAAM,YAAY,KAAK,OAAO;GAC5B,KAAK,EAAE;GACP,WAAW,IAAI,KAAK,EAAE,UAAU,CAAC,gBAAgB;GAClD,EAAE;EACJ,CAAC;AAEF,QAAO,GAAG;;;;;AAMZ,eAAe,yBAAwC;CACrD,MAAM,cAAc,iBAAiB;AAErC,KAAI,YAAY,WAAW,GAAG;AAC5B,SAAO,gCAAgC;AACvC;;CAGF,MAAM,QAAQ,MAAM,OAAO;EACzB,QAAQ;EACR,SAAS,YAAY,KAAK,OAAO;GAC/B,MAAM,EAAE;GACR,OAAO,EAAE;GACT,aAAa;GACd,EAAE;EACJ,CAAC;AAMF,KAJkB,MAAM,QACtB,mDACD,EAEc;AACb,mBAAiB,MAAM;AACvB,SAAO,4BAA4B;OAEnC,QAAO,mBAAmB;;;;;AAO9B,eAAe,4BAA2C;AAKxD,KAJkB,MAAM,QACtB,kDACD,EAEc;AACb,uBAAqB;AACrB,SAAO,iCAAiC;OAExC,QAAO,mBAAmB;;;;;;;;ACvG9B,MAAM,kBAAkB;CACtB,iBAAiB;EACf,KAAK,SAAS,EAAE,OAAO;EACvB,KAAK,SAAS,EAAE,OAAO;EACvB,KAAK,SAAS,EAAE,OAAO;EACvB,KAAK,SAAS,EAAE,SAAS;EAC1B;CACD,gBAAgB,CACd,KAAK,SAAS,EAAE,OAAO,CACxB;CACF;;;;;;;;;;;;;AAcD,SAAgB,mBACd,aACsB;CACtB,MAAM,aAAa,QAAQ,KAAK;CAGhC,IAAIC,iBAA2B,EAAE;CACjC,IAAIC,aAAuB,CAAC,WAAW;CACvC,IAAIC,WAAqB,CAAC,GAAG,gBAAgB,eAAe;AAG5D,KAAI,aAAa,QACf,kBAAiB,CAAC,GAAG,gBAAgB;AAIvC,KAAI,aAAa,SAAS;EACxB,MAAM,cAAc,YAAY;AAGhC,MAAI,YAAY,SAAS,eACvB,kBAAiB,CACf,GAAG,gBACH,GAAG,YAAY,QAAQ,eACxB;AAIH,MAAI,YAAY,YAAY;AAC1B,OAAI,YAAY,WAAW,WAEzB,cAAa,YAAY,WAAW;AAGtC,OAAI,YAAY,WAAW,SAEzB,YAAW,CAAC,GAAG,UAAU,GAAG,YAAY,WAAW,SAAS;;;AAkBlE,QAZqC;EACnC,SAAS;GACP;GACA,eAAe,EAAE;GAClB;EACD,YAAY;GACV;GACA;GACA,WAAW,gBAAgB;GAC5B;EACF;;;;;;AASH,SAAgB,yBACd,aACU;CACV,MAAMC,UAAoB,EAAE;AAG5B,KAAI,aAAa,QACf,SAAQ,KAAK,uCAAuC;CAGtD,MAAM,SAAS,mBAAmB,YAAY;CAC9C,MAAM,aAAa,QAAQ,KAAK;AAGhC,KAAI,OAAO,QAAQ,eAAe,SAAS,EACzC,KAAI,OAAO,QAAQ,eAAe,SAAS,IAAI,CAC7C,SAAQ,KAAK,yBAAyB;MACjC;EACL,MAAM,UAAU,OAAO,QAAQ,eAAe,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK;EACpE,MAAM,OACJ,OAAO,QAAQ,eAAe,SAAS,IACnC,KAAK,OAAO,QAAQ,eAAe,SAAS,EAAE,SAC9C;AACN,UAAQ,KAAK,cAAc,UAAU,OAAO;;AAUhD,KAJE,OAAO,WAAW,WAAW,SAAS,KACrC,OAAO,WAAW,WAAW,WAAW,KACvC,OAAO,WAAW,WAAW,OAAO,YAEpB;EAClB,MAAM,OAAO,OAAO,WAAW,WAC5B,KAAK,MAAO,MAAM,aAAa,sBAAsB,EAAG,CACxD,MAAM,GAAG,EAAE,CACX,KAAK,KAAK;EACb,MAAM,OACJ,OAAO,WAAW,WAAW,SAAS,IAClC,KAAK,OAAO,WAAW,WAAW,SAAS,EAAE,SAC7C;AACN,UAAQ,KAAK,uBAAuB,OAAO,OAAO;;AAIpD,KAAI,aAAa,SAAS,YAAY,SACpC,SAAQ,KACN,6BAA6B,YAAY,QAAQ,WAAW,SAAS,OAAO,UAC7E;AAGH,QAAO;;;;;;;;;;;;ACxIT,eAAsB,sBACpB,YACqC;AACrC,KAAI;EAEF,MAAM,WAAW,QAAQ,IAAI,SAAS;AACtC,UAAQ,IAAI,SAAS,gBAAgB;EAGrC,MAAM,SAAS,MAAM,OAAO;AAG5B,MAAI,aAAa,OACf,QAAO,QAAQ,IAAI,SAAS;MAE5B,SAAQ,IAAI,SAAS,gBAAgB;EAIvC,MAAM,MAAM,OAAO;AAEnB,MAAI,CAAC,OAAO,CAAC,IAAI,WACf;AAIF,SAAO,IAAI,WAAW;UACf,OAAO;AAEd,UAAQ,KACN,+CAA+C,WAAW,IAC1D,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CACvD;AACD;;;;;;;;;;;AC9BJ,SAAS,sBACP,aACA,eACS;AAET,KAAI,aAAa,QACf,QAAO;AAIT,KAAI,cAAc,QAAQ,eAAe,SAAS,EAChD,QAAO;AAIT,KAAI,aAAa,SAAS,WACxB,QAAO;AAIT,QAAO;;;;;;;;;;;AAYT,eAAsB,yBACpB,OACA,gBACA,eACkB;AAElB,KAAI,YAAY,CACd,QAAO;CAIT,MAAM,SAAS,iBAAiB;CAChC,MAAM,SAAS,OAAO,YAAY;AAElC,KAAI,UAAU,OAAO,WAAW,SAE9B,QAAO;AAKT,KAAI,CAAC,sBAAsB,gBAAgB,cAAc,CACvD,QAAO;AAIT,KAAI,CAAC,OAAO,EAAE;EACZ,MAAMC,YAAU,WAAW,MAAM;AACjC,UAAQ,MACN,+BAA+BA,UAAQ,KAAK,MAAM,yBACnD;AACD,UAAQ,MACN,uFACD;AACD,SAAO;;CAIT,MAAM,UAAU,WAAW,MAAM;AACjC,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,QAAQ,QAAQ,uCAAuC;AACnE,SAAQ,IAAI,GAAG;CAGf,MAAM,UAAU,yBAAyB,eAAe;AACxD,MAAK,MAAM,QAAQ,QACjB,SAAQ,IAAI,KAAK,OAAO;AAG1B,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,WAAW,QAAQ;AAC/B,SAAQ,IAAI,GAAG;CAGf,MAAM,SAAS,MAAM,OAAO;EAC1B,QAAQ;EACR,SAAS;GACP;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACD;IACE,MAAM;IACN,OAAO;IACP,aAAa;IACd;GACF;EACF,CAAC;AAGF,KAAI,WAAW,SACb,QAAO;AAGT,KAAI,WAAW,UAAU;AAEvB,SAAO,YAAY,SAAS;GAC1B,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,QAAQ;GACR;GACD;AACD,kBAAgB,OAAO;;AAIzB,QAAO;;;;;;AAOT,SAAgB,sBACd,OAC6B;AAC7B,KAAI,YAAY,CACd,QAAO;CAIT,MAAM,SADS,iBAAiB,CACV,YAAY;AAElC,KAAI,QAAQ,WAAW,YAAY,OAAO,cACxC,QAAO,OAAO;AAGhB,QAAO;;;;;;;;;;;;;;;;AC9IT,SAAgB,eAAe,OAA+B;CAC5D,IAAI,aAAa,MAAM,MAAM;AAG7B,cAAa,WAAW,QAAQ,gBAAgB,GAAG;AAGnD,cAAa,WAAW,QAAQ,kBAAkB,GAAG;CAGrD,IAAI,MAAM;CACV,MAAM,UAAU,WAAW,QAAQ,IAAI;AACvC,KAAI,YAAY,IAAI;AAClB,QAAM,WAAW,MAAM,UAAU,EAAE;AACnC,eAAa,WAAW,MAAM,GAAG,QAAQ;;AAI3C,cAAa,WAAW,QAAQ,UAAU,GAAG;CAG7C,MAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,KAAI,MAAM,WAAW,EACnB,QAAO;CAGT,MAAM,CAAC,OAAO,QAAQ;AAGtB,KACE,CAAC,SACD,CAAC,QACD,CAAC,kBAAkB,MAAM,IACzB,CAAC,kBAAkB,KAAK,CAExB,QAAO;AAGT,QAAO;EAAE;EAAO;EAAM;EAAK;;;;;AAM7B,SAAS,kBAAkB,MAAuB;AAEhD,QACE,2CAA2C,KAAK,KAAK,IACrD,gBAAgB,KAAK,KAAK;;;;;AAO9B,SAAgB,cAAsB;AACpC,QAAO,KAAK,SAAS,EAAE,QAAQ,QAAQ;;;;;AAMzC,SAAgB,iBAAiB,KAAsB;AACrD,QAAO,KAAK,aAAa,EAAE,cAAc,IAAI,OAAO,IAAI,MAAM,IAAI,IAAI;;;;;AAMxE,SAAgB,YAAY,OAAwB;AAElD,KACE,MAAM,WAAW,KAAK,IACtB,MAAM,WAAW,MAAM,IACvB,MAAM,WAAW,IAAI,IACrB,MAAM,SAAS,KAAK,CAEpB,QAAO;AAIT,QAAO,eAAe,MAAM,KAAK;;;;;;aCxFS;AAG5C,MAAM,gBAAgB;;;;AAKtB,SAAgB,WAAW,KAAgC;CACzD,MAAM,YAAY,iBAAiB,IAAI;CACvC,MAAM,WAAW,KAAK,WAAW,cAAc;AAE/C,KAAI,CAAC,WAAW,UAAU,CACxB,QAAO;EACL,QAAQ;EACR,OAAO;EACP,QAAQ;EACT;AAGH,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO;EAAE,QAAQ;EAAM,OAAO;EAAO,QAAQ;EAA0B;AAGzE,KAAI;EACF,MAAM,WAAW,KAAK,MACpB,aAAa,UAAU,QAAQ,CAChC;AAID,MAAI,CAAC,WADa,KAAK,WAAW,SAAS,WAAW,CAC5B,CACxB,QAAO;GACL,QAAQ;GACR,OAAO;GACP;GACA,QAAQ;GACT;AAIH,MAAI,CAAC,SAAS,sBACZ,QAAO;GACL,QAAQ;GACR,OAAO;GACP;GACA,QAAQ;GACT;AAGH,SAAO;GAAE,QAAQ;GAAM,OAAO;GAAM;GAAU;SACxC;AACN,SAAO;GAAE,QAAQ;GAAM,OAAO;GAAO,QAAQ;GAA0B;;;;;;AAyB3E,SAAgB,cAAc,KAAc,UAA+B;CAEzE,MAAM,WAAW,KADC,iBAAiB,IAAI,EACN,cAAc;AAE/C,WAAU,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,eAAc,UAAU,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC;;;;;AAM5D,SAAgB,gBAAgB,KAAoB;CAClD,MAAM,YAAY,iBAAiB,IAAI;AAEvC,KAAI,WAAW,UAAU,CACvB,QAAO,WAAW;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;;;;;aChGX;AAG5C,MAAM,YAAY,UAAU,KAAK;;;;AAKjC,eAAsB,UAAU,KAA6B;CAC3D,MAAM,UAAU,sBAAsB,IAAI,MAAM,GAAG,IAAI,KAAK;CAC5D,MAAM,aAAa,iBAAiB,IAAI;AAGxC,KAAI,WAAW,WAAW,CACxB,QAAO,YAAY;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AAItD,WAAU,QAAQ,WAAW,EAAE,EAAE,WAAW,MAAM,CAAC;AAGnD,KAAI;AACF,QAAM,UACJ,gCAAgC,IAAI,IAAI,GAAG,QAAQ,IAAI,WAAW,IAClE,EACE,SAAS,KACV,CACF;UACM,OAAO;AAEd,MAAI,IAAI,QAAQ,OACd,KAAI;AACF,SAAM,UAAU,uBAAuB,QAAQ,IAAI,WAAW,IAAI,EAChE,SAAS,KACV,CAAC;AACF;UACM;AAIV,QAAM,IAAI,MACR,mBAAmB,IAAI,MAAM,GAAG,IAAI,KAAK,GAAG,IAAI,IAAI,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAC/G;;;;;;AAOL,eAAsB,oBAAoB,UAAiC;AAGzE,KAAI,CAAC,WAFW,GAAG,SAAS,eAEJ,CAEtB;AAGF,KAAI;AACF,QAAM,UAAU,eAAe;GAC7B,KAAK;GACL,SAAS;GACV,CAAC;UACK,OAAO;AACd,QAAM,IAAI,MACR,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAC1F;;;;;;AAOL,eAAsB,aAAa,UAAmC;AACpE,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,UAAU,sBAAsB;GACvD,KAAK;GACL,SAAS;GACV,CAAC;AACF,SAAO,OAAO,MAAM;SACd;AACN,SAAO;;;;;;;;;ACvEX,MAAM,kBAAkB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AAKD,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;;;;;AAeD,SAAgB,kBACd,UACA,YAA4C,UACpC;CACR,MAAM,OAAO,WAAW,UAAU;CAGlC,MAAM,QAAQ,mBAAmB,SAAS;AAG1C,OAAM,MAAM;AAGZ,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,SAAS,UAAU,KAAK;EAC7C,MAAM,UAAU,aAAa,KAAK;AAGlC,OAAK,OAAO,QAAQ,aAAa,IAAI;AACrC,OAAK,OAAO,QAAQ;AACpB,OAAK,OAAO,KAAK;;CAInB,MAAM,WAAW,KAAK,UAAU,YAAY;AAC5C,KAAI,WAAW,SAAS,EAAE;AACxB,OAAK,OAAO,mBAAmB;AAC/B,OAAK,OAAO,aAAa,SAAS,CAAC;AACnC,OAAK,OAAO,KAAK;;AAKnB,QAAO,GAAG,UAAU,GADL,KAAK,OAAO,SAAS;;;;;;;;;AAWtC,SAAS,mBAAmB,KAAa,UAAoB,EAAE,EAAY;AACzE,KAAI,CAAC,WAAW,IAAI,CAClB,QAAO;AAGT,KAAI;EACF,MAAM,UAAU,YAAY,IAAI;AAEhC,OAAK,MAAM,SAAS,SAAS;AAE3B,OAAI,aAAa,MAAM,CACrB;GAGF,MAAM,WAAW,KAAK,KAAK,MAAM;GACjC,IAAIC;AAEJ,OAAI;AACF,WAAO,SAAS,SAAS;WACnB;AAEN;;AAGF,OAAI,KAAK,aAAa,CACpB,oBAAmB,UAAU,QAAQ;YAC5B,KAAK,QAAQ,IAAI,kBAAkB,MAAM,CAClD,SAAQ,KAAK,SAAS;;SAGpB;AAIR,QAAO;;;;;AAMT,SAAS,aAAa,MAAuB;AAC3C,MAAK,MAAM,WAAW,gBACpB,KAAI,QAAQ,WAAW,IAAI,EAAE;EAE3B,MAAM,MAAM,QAAQ,MAAM,EAAE;AAC5B,MAAI,KAAK,SAAS,IAAI,CACpB,QAAO;YAEA,SAAS,QAClB,QAAO;AAGX,QAAO;;;;;AAMT,SAAS,kBAAkB,MAAuB;AAChD,QAAO,kBAAkB,MAAM,QAAQ,KAAK,SAAS,IAAI,CAAC;;;;;;;;AClJ5D,MAAM,mBAAmB;CACvB;CACA;CACA;CACA;CACA;CACD;;;;;AAMD,SAAgB,kBAAkB,UAAiC;CACjE,MAAM,UAAU,KAAK,UAAU,eAAe;AAE9C,KAAI,WAAW,QAAQ,CACrB,KAAI;EACF,MAAM,MAAM,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC;AAGtD,MAAI,IAAI,SAAS,IAAI,KAAK,SAAS,MAAM,IAAI,IAAI,KAAK,SAAS,MAAM,GAEnE;OAAI,WADa,KAAK,UAAU,IAAI,KAAK,CACjB,CACtB,QAAO,IAAI;;SAGT;AAMV,MAAK,MAAM,aAAa,iBAEtB,KAAI,WADkB,KAAK,UAAU,UAAU,CAClB,CAC3B,QAAO;AAIX,QAAO;;;;;AAMT,SAAgB,cAAc,UAAoC;CAChE,MAAM,UAAU,KAAK,UAAU,eAAe;AAE9C,KAAI,CAAC,WAAW,QAAQ,CACtB,QAAO;AAGT,KAAI;AAEF,SADY,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC,CAC3C,OAAO;SACZ;AACN,SAAO;;;;;;;AAQX,SAAgB,gBAAgB,UAAkB,SAA0B;CAE1E,MAAM,WAAW,SAAS,MAAM,6BAA6B;AAC7D,KAAI,CAAC,SAEH,QAAO;CAGT,MAAM,WAAW,QAAQ,MAAM,uBAAuB;AACtD,KAAI,CAAC,SACH,QAAO;CAGT,MAAM,WAAW,OAAO,SAAS,GAAG;CACpC,MAAM,WAAW,OAAO,SAAS,GAAG;CACpC,MAAM,WAAW,OAAO,SAAS,GAAG;CACpC,MAAM,WAAW,OAAO,SAAS,GAAG;CACpC,MAAM,WAAW,OAAO,SAAS,GAAG;CACpC,MAAM,WAAW,OAAO,SAAS,GAAG;AAGpC,KAAI,WAAW,SAAU,QAAO;AAChC,KAAI,WAAW,SAAU,QAAO;AAChC,KAAI,WAAW,SAAU,QAAO;AAChC,KAAI,WAAW,SAAU,QAAO;AAChC,QAAO,YAAY;;;;;;AAOrB,SAAgB,aAAa,UAA8B;AACzD,QAAO,SAAS,QAAQ,SAAS,CAAC,QAAQ,IAAI,MAAM;;;;;;;;AC9EtD,SAAgB,iBAAyB;AACvC,QAAO,KAAK,SAAS,EAAE,QAAQ,UAAU;;;;;;;;;;;AAY3C,IAAa,iBAAb,MAA4B;CAC1B,AAAQ,0BAAiC,IAAI,KAAK;CAClD,AAAQ;CACR,AAAQ,QAAQ;CAEhB,YAAY,aAAsB;AAChC,OAAK,cAAc,eAAe,gBAAgB;AAClD,OAAK,MAAM;;;;;CAMb,AAAQ,OAAa;AACnB,MAAI,CAAC,WAAW,KAAK,YAAY,CAC/B;AAGF,MAAI;GAEF,MAAM,QADU,aAAa,KAAK,aAAa,QAAQ,CACjC,MAAM,KAAK;AAEjC,QAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,UAAU,KAAK,MAAM;AAG3B,QAAI,CAAC,WAAW,QAAQ,WAAW,IAAI,CACrC;IAGF,MAAM,QAAQ,KAAK,UAAU,QAAQ;AACrC,QAAI,MACF,MAAK,QAAQ,IAAI,MAAM,KAAK,MAAM;;WAG/B,OAAO;AACd,WAAQ,KAAK,oCAAoC,QAAQ;;;;;;CAO7D,AAAQ,UAAU,MAA+B;EAE/C,MAAM,QAAQ,KAAK,MAAM,MAAM;AAE/B,MAAI,MAAM,SAAS,EACjB,QAAO;EAGT,MAAM,CAAC,KAAK,MAAM,cAAc,cAAc;AAE9C,MAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,WACrC,QAAO;EAGT,MAAM,YAAY,OAAO,aAAa;AACtC,MAAI,OAAO,MAAM,UAAU,CACzB,QAAO;AAGT,SAAO;GACL;GACA;GACA;GACA,SAAS,eAAe;GACzB;;;;;CAMH,AAAQ,YAAY,OAAyB;AAC3C,SAAO,GAAG,MAAM,IAAI,GAAG,MAAM,KAAK,GAAG,MAAM,UAAU,GAAG,MAAM,UAAU,YAAY;;;;;CAMtF,AAAQ,OAAa;AACnB,MAAI,CAAC,KAAK,MACR;AAGF,MAAI;GAEF,MAAM,MAAM,QAAQ,KAAK,YAAY;AACrC,OAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;GASrC,MAAM,QAAQ;IACZ;IACA;IACA;IACA;IACA,GAVoB,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC,CAAC,MAAM,GAAG,MAC/D,EAAE,IAAI,cAAc,EAAE,IAAI,CAC3B,CAQkB,KAAK,UAAU,KAAK,YAAY,MAAM,CAAC;IACzD;AAED,iBAAc,KAAK,aAAa,GAAG,MAAM,KAAK,KAAK,CAAC,KAAK,QAAQ;AACjE,QAAK,QAAQ;WACN,OAAO;AACd,WAAQ,MAAM,kCAAkC,QAAQ;AACxD,SAAM;;;;;;;;;;CAWV,AAAO,OAAO,KAAa,MAA4B;EACrD,MAAM,QAAQ,KAAK,QAAQ,IAAI,IAAI;AAEnC,MAAI,CAAC,MACH,QAAO;AAGT,MAAI,MAAM,SAAS,KACjB,QAAO;AAGT,SAAO;;;;;;;;;CAUT,AAAO,IAAI,KAAa,MAAc,UAAU,OAAa;AAC3D,OAAK,QAAQ,IAAI,KAAK;GACpB;GACA;GACA,WAAW,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;GACxC;GACD,CAAC;AAEF,OAAK,QAAQ;AACb,OAAK,MAAM;;;;;;;;;CAUb,AAAO,OAAO,KAAa,MAAc,UAAU,OAAa;EAC9D,MAAM,WAAW,KAAK,QAAQ,IAAI,IAAI;AAEtC,OAAK,QAAQ,IAAI,KAAK;GACpB;GACA;GACA,WAAW,UAAU,aAAa,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;GAC/D;GACD,CAAC;AAEF,OAAK,QAAQ;AACb,OAAK,MAAM;;;;;;;;CASb,AAAO,OAAO,KAAsB;EAClC,MAAM,UAAU,KAAK,QAAQ,OAAO,IAAI;AAExC,MAAI,SAAS;AACX,QAAK,QAAQ;AACb,QAAK,MAAM;;AAGb,SAAO;;;;;;;;CAST,AAAO,IAAI,KAAmC;AAC5C,SAAO,KAAK,QAAQ,IAAI,IAAI;;;;;;;CAQ9B,AAAO,SAAqB;AAC1B,SAAO,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC;;;;;CAM1C,AAAO,QAAc;AACnB,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ;AACb,OAAK,MAAM;;;;;CAMb,AAAO,WAIL;EACA,MAAM,UAAU,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC;AAEjD,SAAO;GACL,OAAO,QAAQ;GACf,SAAS,QAAQ,QAAQ,MAAM,EAAE,QAAQ,CAAC;GAC1C,WAAW,QAAQ,QAAQ,MAAM,CAAC,EAAE,QAAQ,CAAC;GAC9C;;;;;;aC3QuD;;AAW5D,MAAM,cAAc;;;;AAKpB,eAAsB,UACpB,OACA,UAA4B,EAAE,EACf;CAEf,MAAM,MAAM,eAAe,MAAM;AACjC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,6BAA6B,QAAQ;CAGvD,MAAM,WAAW,iBAAiB,IAAI;CAGtC,MAAM,cAAc,WAAW,IAAI;AAEnC,KAAI,CAAC,YAAY,SAAS,QAAQ,OAAO;AAEvC,MAAI,QAAQ,SAAS,YAAY,QAAQ;AACvC,WAAQ,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK;AAChE,mBAAgB,IAAI;QAEpB,SAAQ,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK;AAGhE,QAAM,UAAU,IAAI;EAGpB,MAAM,aAAa,kBAAkB,SAAS;AAC9C,MAAI,CAAC,WACH,OAAM,IAAI,MACR,2BAA2B,IAAI,MAAM,GAAG,IAAI,KAAK,iDAClD;AAIH,MAAI,CAAC,QAAQ,aAAa;AACxB,WAAQ,IAAI,6BAA6B;AACzC,SAAM,oBAAoB,SAAS;;AAKrC,gBAAc,KAAK;GACjB,WAFgB,MAAM,aAAa,SAAS;GAG5C,2BAAU,IAAI,MAAM,EAAC,aAAa;GAClC;GACA,uBAAuB,CAAC,QAAQ;GACjC,CAAC;AAEF,UAAQ,IAAI,WAAW;;AAIzB,KAAI,CAAC,QAAQ,oBAGX;MAAI,EAFoB,MAAM,gBAAgB,KAAK,SAAS,EAEvC,sBAAsB;AACzC,WAAQ,MACN,gEACD;AACD,WAAQ,KAAK,EAAE;;;AAKnB,OAAM,WAAW,KAAK,UAAU,QAAQ,QAAQ,EAAE,EAAE,QAAQ,OAAO,MAAM;;;;;;;;;AAU3E,eAAe,gBACb,KACA,UAC0E;CAC1E,MAAM,MAAM,cAAc,IAAI,MAAM,GAAG,IAAI,KAAK,GAAG,IAAI;AAEvD,SAAQ,IAAI,mCAAmC;CAG/C,MAAM,OAAO,kBAAkB,SAAS;AACxC,SAAQ,IAAI,YAAY,KAAK,MAAM,GAAG,GAAG,CAAC,KAAK;CAG/C,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,eAAe,WAAW,OAAO,KAAK,KAAK;CAEjD,MAAMC,SAA+B;EACnC,QAAQ;EACR;EACA,eAAe,iBAAiB;EACjC;AAED,SAAQ,cAAR;EACE,KAAK;AAEH,WAAQ,IAAI,4BAA4B;AACxC,UAAO;IAAE,sBAAsB;IAAM;IAAQ;EAG/C,KAAK;AAEH,WAAQ,IAAI,wDAAwD;AACpE,WAAQ,IAAI,6CAA6C;AACzD,WAAQ,IAAI,sDAAsD;AAClE,WAAQ,IACN,yBAAyB,IAAI,MAAM,GAAG,IAAI,KAAK,QAAQ,IAAI,IAAI,IAChE;AAMD,OAJoB,MAAM,QACxB,8CACD,EAEgB;AACf,eAAW,IAAI,KAAK,MAAM,KAAK;AAC/B,YAAQ,IAAI,2CAA2C;AACvD,WAAO;KAAE,sBAAsB;KAAM;KAAQ;;AAG/C,WAAQ,IAAI,uCAAuC;AACnD,UAAO;IAAE,sBAAsB;IAAO;IAAQ;EAGhD,KAAK;AAGH,UAAO,eADe,WAAW,IAAI,IAAI,EACJ;AAErC,WAAQ,IAAI,mDAAmD;AAC/D,WAAQ,IACN,+DACD;AACD,WAAQ,IAAI,0BAA0B;AACtC,WAAQ,IAAI,8CAA8C;AAC1D,WAAQ,IAAI,qCAAqC;AACjD,WAAQ,IAAI,6BAA6B;AAEzC,WAAQ,IACN,qBACA,GAAG,OAAO,cAAc,MAAM,GAAG,GAAG,CAAC,KACtC;AACD,WAAQ,IAAI,qBAAqB,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC,OAAO;AAE7D,WAAQ,IAAI,0BAA0B;AACtC,WAAQ,IAAI,gDAAgD;AAC5D,WAAQ,IAAI,+BAA+B;AAC3C,WAAQ,IAAI,sCAAsC;AAClD,WAAQ,IACN,mCAAmC,IAAI,MAAM,GAAG,IAAI,KAAK,WAAW,IAAI,IAAI,IAC7E;AAOD,OALsB,MAAM,QAC1B,yCACA,MACD,EAEkB;AAMjB,QALqB,MAAM,QACzB,iCACA,MACD,EAEiB;AAChB,gBAAW,OAAO,KAAK,MAAM,KAAK;AAClC,aAAQ,IAAI,uCAAuC;;AAGrD,WAAO;KAAE,sBAAsB;KAAM;KAAQ;;AAG/C,WAAQ,IAAI,sCAAsC;AAClD,UAAO;IAAE,sBAAsB;IAAO;IAAQ;EAGhD;AAEE,WAAQ,MAAM,8BAA8B;AAC5C,UAAO;IAAE,sBAAsB;IAAO;IAAQ;;;;;;AAQpD,eAAe,WACb,KACA,UACA,QACA,KACe;CAEf,MAAM,SAAS,cAAc,SAAS;AAEtC,KAAI,QAAQ,SACV;MAAI,CAAC,gBAAgB,OAAO,SAAS,YAAY,CAC/C,OAAM,IAAI,MACR,yBAAyB,OAAO,QAAQ,iBAAiB,cAC1D;;AAKL,KAAI,QAAQ,OAAO,OAAO,IAAI,SAAS,GAAG;EACxC,MAAM,UAAU,aAAa,OAAO,IAAI;AACxC,MAAI,QAAQ,SAAS,EACnB,SAAQ,KACN,oDAAoD,QAAQ,KAAK,KAAK,GACvE;;CAKL,MAAM,aAAa,kBAAkB,SAAS;AAC9C,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,kCAAkC,IAAI,MAAM,GAAG,IAAI,OAAO;CAG5E,MAAM,oBAAoB,KAAK,UAAU,WAAW;CAGpD,MAAM,YAAY,cAAc,IAAI,MAAM,GAAG,IAAI;CACjD,MAAM,gBAAgB,QAAQ,IAAI,SAAS;AAC3C,SAAQ,IAAI,SAAS,cAAc;AAEnC,KAAI;EAEF,MAAM,EAAE,sCAAkB,gDAAuB,+CAC/C,MAAM,OAAO;EACf,MAAM,EAAE,mCAAkB,MAAM,OAAO;EAEvC,MAAM,QAAQC,oBAAkB;AAGhC,UAAQ,IAAI,6BAA6B;EACzC,MAAM,cAAc,MAAMC,wBAAsB,MAAM;AAEtD,MAAI,CAAC,aAAa;AAChB,WAAQ,MAAM,+CAA+C;AAC7D,WAAQ,KAAK,EAAE;;EAIjB,MAAM,gBAAgB,MAAMC,sBAAoB,MAAM;AAEtD,MAAI,CAAC,eAAe;AAClB,WAAQ,MAAM,sDAAsD;AACpE,WAAQ,KAAK,EAAE;;AAIjB,MAAI,KAAK;AAGP,WAAQ,KACN,6EACD;AACD,WAAQ,IAAI,SAAS,YAAY;AACjC,WAAQ,OAAO,CAAC,OAAO,kBAAkB;AACzC,SAAM,OAAO;AACb;;EAIF,MAAM,SAAS,MAAMC,gBAAc;GACjC,YAAY;GACZ;GACA;GACA;GACA;GACD,CAAC;AAEF,MAAI,OAAO,MACT,SAAQ,MAAM,cAAc,OAAO,QAAQ;AAG7C,MAAI,OAAO,aAAa,EACtB,SAAQ,KAAK,OAAO,SAAS;WAEvB;AAER,MAAI,kBAAkB,OACpB,QAAO,QAAQ,IAAI,SAAS;MAE5B,SAAQ,IAAI,SAAS,cAAc;;;;;;ACvSzC,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;AAClC,MAAM,UAAU,KAAK;AAErB,eAAe,OAAO;AACpB,KAAI,CAAC,WAAW,YAAY,YAAY,YAAY,MAAM;AACxD,YAAU;AACV;;AAGF,KAAI,YAAY,eAAe,YAAY,MAAM;AAC/C,eAAa;AACb;;AAGF,KAAI,YAAY,UAAU;AACxB,QAAM,eAAe;AACrB;;AAGF,KAAI,YAAY,eAAe;AAC7B,QAAM,oBAAoB;AAC1B;;AAGF,KAAI,YAAY,OAAO;EACrB,MAAM,SAAS,KAAK;AACpB,MAAI,CAAC,QAAQ;AACX,WAAQ,MAAM,+CAA+C;AAC7D,WAAQ,MAAM,wCAAwC;AACtD,WAAQ,KAAK,EAAE;;EAKjB,MAAM,QADa,KAAK,QAAQ,UAAU,KACb;EAG7B,MAAM,gBAAgB,KAAK,QAAQ,KAAK;EACxC,MAAM,UACJ,kBAAkB,KACd,KAAK,MAAM,gBAAgB,EAAE,GAC7B,KAAK,MAAM,EAAE,CAAC,QAAQ,QAAQ,QAAQ,UAAU;AAEtD,MAAI,YAAY,OAAO,CACrB,OAAM,UAAU,QAAQ;GAAE,MAAM;GAAS;GAAO,CAAC;MAEjD,OAAM,QAAQ,QAAQ,QAAQ;AAEhC;;AAGF,KAAI,YAAY,OAAO;EACrB,MAAM,SAAS,KAAK;AACpB,MAAI,CAAC,QAAQ;AACX,WAAQ,MAAM,+CAA+C;AAC7D,WAAQ,MAAM,wCAAwC;AACtD,WAAQ,KAAK,EAAE;;EAKjB,MAAM,QADa,KAAK,QAAQ,UAAU,KACb;AAE7B,MAAI,YAAY,OAAO,CACrB,OAAM,UAAU,QAAQ;GAAE,MAAM,EAAE;GAAE;GAAO,KAAK;GAAM,CAAC;MAEvD,OAAM,aAAa,OAAO;AAE5B;;AAGF,SAAQ,MAAM,oBAAoB,UAAU;AAC5C,SAAQ,MAAM,+BAA6B;AAC3C,SAAQ,KAAK,EAAE;;AAGjB,eAAe,QAAQ,UAAkB,SAAmB;CAC1D,MAAM,eAAe,QAAQ,QAAQ,KAAK,EAAE,SAAS;CAGrD,MAAM,eAAe,QAAQ,IAAI;AACjC,SAAQ,IAAI,gBAAgB,SAAS;AAErC,KAAI;EAEF,MAAM,QAAQ,kBAAkB;EAGhC,MAAM,eAAe,sBAAsB,MAAM;EACjD,IAAIC;EACJ,IAAI,cAAc;AAElB,MAAI,CAAC,cAAc;GAEjB,MAAM,iBAAiB,MAAM,sBAAsB,aAAa;AAGhE,mBAAgB,mBAAmB,eAAe;AAGlD,WAAQ,IAAI,6BAA6B;AAOzC,OAAI,CANY,MAAM,yBACpB,OACA,gBACA,cACD,EAEa;AACZ,YAAQ,MAAM,sBAAsB;AACpC,YAAQ,KAAK,EAAE;;AAIjB,iBAAc,gBAAgB,WAAW;SACpC;AAEL,mBAAgB;AAGhB,kBADuB,MAAM,sBAAsB,aAAa,GAClC,WAAW;;EAI3C,MAAM,SAAS,MAAM,cAAc;GACjC,YAAY;GACZ,MAAM;GACN;GACA;GACA;GACD,CAAC;AAEF,MAAI,OAAO,MACT,SAAQ,MAAM,cAAc,OAAO,QAAQ;AAG7C,MAAI,OAAO,aAAa,EACtB,SAAQ,KAAK,OAAO,SAAS;WAEvB;AAER,MAAI,iBAAiB,OACnB,QAAO,QAAQ,IAAI;MAEnB,SAAQ,IAAI,gBAAgB;;;AAKlC,eAAe,aAAa,UAAkB;CAC5C,MAAM,eAAe,QAAQ,QAAQ,KAAK,EAAE,SAAS;AAGrD,SAAQ,IAAI,eAAe;AAG3B,SAAQ,OAAO,CAAC,OAAO,aAAa;AAGpC,OAAM,OAAO;;AAGf,SAAS,WAAW;AAClB,SAAQ,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCZ;;AAGF,SAAS,cAAc;AACrB,SAAQ,IAAI,QAAQ;;AAGtB,MAAM,CAAC,OAAO,QAAQ;AACpB,SAAQ,MAAM,IAAI,WAAW,IAAI;AACjC,SAAQ,KAAK,EAAE;EACf"}
1
+ {"version":3,"file":"kly.mjs","names":["cachedData: ModelsDevData","PROVIDER_ID_MAP: Partial<Record<LLMProvider, string>>","caps: string[]","PROVIDER_CONFIGS: Partial<Record<LLMProvider, ProviderConfig>>","CONFIG_DIR","DEFAULT_MODELS: Record<LLMProvider, string>","PROVIDER_OPTIONS: Array<{\n value: LLMProvider;\n label: string;\n hint?: string;\n}>","lines: string[]","parts: string[]","confirm","baseURL","model","baseURL: string | undefined","options: ResourceProviderOptions","modelConfig: ModelConfigResponse | null","p","result: Record<string, unknown>","command","initMessage: SandboxInitMessage","executionResult: ExecutionCompleteMessage | null","request: IPCRequest","p","p","pc","pc","lines: string[]","output","denyRead: string[]","allowWrite: string[]","allowedDomains: string[]","sandboxConfig: SandboxRuntimeConfig","allowedDomains: string[]","allowWrite: string[]","denyRead: string[]","summary: string[]","appName","stat: Stats","result: IntegrityCheckResult","getAppIdentifier","extractAppPermissions","formatPermissionsSummary","sandboxConfig:\n | import(\"@anthropic-ai/sandbox-runtime\").SandboxRuntimeConfig\n | null","checkApiKeyPermission","buildSandboxConfig","getAppSandboxConfig","launchSandbox","sandboxConfig: SandboxRuntimeConfig"],"sources":["../../src/ai/models-dev.ts","../../src/ai/provider-config.ts","../../src/ai/storage.ts","../../src/ai/types.ts","../../src/ai/models-command.ts","../../src/shared/ipc-protocol.ts","../../src/host/resource-provider.ts","../../src/host/launcher.ts","../../src/shared/constants.ts","../../src/shared/runtime-mode.ts","../../src/ui/utils/tty.ts","../../src/sandbox/ipc-client.ts","../../src/ui/components/confirm.ts","../../src/ui/components/select.ts","../../src/ui/utils/colors.ts","../../src/ui/components/table.ts","../../src/ui/utils/output.ts","../../src/permissions/index.ts","../../src/permissions/cli.ts","../../src/permissions/config-builder.ts","../../src/permissions/extract.ts","../../src/permissions/unified-prompt.ts","../../src/remote/parser.ts","../../src/remote/cache.ts","../../src/remote/fetcher.ts","../../src/remote/integrity.ts","../../src/remote/resolver.ts","../../src/remote/sumfile.ts","../../src/remote/index.ts","../../bin/kly.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { LLMProvider } from \"./types\";\n\nconst MODELS_DEV_API = \"https://models.dev/api.json\";\nconst CACHE_FILE = join(homedir(), \".kly\", \"models-cache.json\");\nconst CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours\n\n/**\n * Model data from models.dev\n */\nexport interface ModelInfo {\n id: string;\n name: string;\n family?: string;\n // Capabilities\n attachment?: boolean;\n reasoning?: boolean;\n tool_call?: boolean;\n structured_output?: boolean;\n temperature?: boolean;\n // Pricing (per million tokens)\n cost?: {\n input?: number;\n output?: number;\n cache_write?: number;\n cache_read?: number;\n };\n // Limits\n limit?: {\n context?: number;\n output?: number;\n };\n // Metadata\n knowledge?: string;\n release?: string;\n release_date?: string;\n last_updated?: string;\n modalities?: {\n input?: string[];\n output?: string[];\n };\n open_weights?: boolean;\n}\n\n/**\n * Provider data from models.dev\n */\nexport interface ProviderInfo {\n id: string;\n name: string;\n api?: string;\n env?: string;\n npm?: string;\n doc?: string;\n models: Record<string, ModelInfo>;\n}\n\n/**\n * models.dev API response\n */\nexport interface ModelsDevData {\n providers: Record<string, ProviderInfo>;\n timestamp: number;\n}\n\n/**\n * Fetch models.dev data with caching\n */\nexport async function fetchModelsDevData(\n forceRefresh = false,\n): Promise<ModelsDevData | null> {\n // Try to load from cache first\n if (!forceRefresh && existsSync(CACHE_FILE)) {\n try {\n const cached = JSON.parse(readFileSync(CACHE_FILE, \"utf-8\"));\n const age = Date.now() - cached.timestamp;\n\n if (age < CACHE_TTL) {\n return cached;\n }\n } catch (_error) {\n // Cache file corrupted, continue to fetch\n }\n }\n\n // Fetch fresh data\n try {\n const response = await fetch(MODELS_DEV_API);\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n const cachedData: ModelsDevData = {\n providers: data,\n timestamp: Date.now(),\n };\n\n // Save to cache\n try {\n writeFileSync(CACHE_FILE, JSON.stringify(cachedData, null, 2), \"utf-8\");\n } catch (_error) {\n // Ignore cache write errors\n }\n\n return cachedData;\n } catch (_error) {\n // Network error, try to return stale cache\n if (existsSync(CACHE_FILE)) {\n try {\n return JSON.parse(readFileSync(CACHE_FILE, \"utf-8\"));\n } catch {\n // Ignore\n }\n }\n return null;\n }\n}\n\n/**\n * Map our provider IDs to models.dev IDs\n */\nconst PROVIDER_ID_MAP: Partial<Record<LLMProvider, string>> = {\n openai: \"openai\",\n anthropic: \"anthropic\",\n google: \"google\",\n deepseek: \"deepseek\",\n groq: \"groq\",\n mistral: \"mistral\",\n cohere: \"cohere\",\n ollama: \"ollama\",\n};\n\n/**\n * Get provider info by our internal provider ID\n */\nexport function getProviderInfo(\n data: ModelsDevData,\n provider: LLMProvider,\n): ProviderInfo | null {\n const modelsDevId = PROVIDER_ID_MAP[provider];\n if (!modelsDevId) return null;\n\n return data.providers[modelsDevId] || null;\n}\n\n/**\n * Get all models for a provider\n */\nexport function getProviderModels(\n data: ModelsDevData,\n provider: LLMProvider,\n): ModelInfo[] {\n const providerInfo = getProviderInfo(data, provider);\n if (!providerInfo) return [];\n\n return Object.values(providerInfo.models);\n}\n\n/**\n * Get model info by ID\n */\nexport function getModelInfo(\n data: ModelsDevData,\n provider: LLMProvider,\n modelId: string,\n): ModelInfo | null {\n const providerInfo = getProviderInfo(data, provider);\n if (!providerInfo) return null;\n\n return providerInfo.models[modelId] || null;\n}\n\n/**\n * Format price for display\n */\nexport function formatPrice(pricePerMillion: number | undefined): string {\n if (pricePerMillion === undefined) return \"N/A\";\n if (pricePerMillion === 0) return \"Free\";\n\n // Format to 2 decimal places, remove trailing zeros and decimal point\n return pricePerMillion.toFixed(2).replace(/\\.?0+$/, \"\");\n}\n\n/**\n * Format capabilities for display\n */\nexport function formatCapabilities(model: ModelInfo): string[] {\n const caps: string[] = [];\n\n if (model.tool_call) caps.push(\"Tools\");\n if (model.reasoning) caps.push(\"Reasoning\");\n if (model.structured_output) caps.push(\"JSON\");\n if (model.attachment) caps.push(\"Files\");\n\n return caps;\n}\n\n/**\n * Get logo URL for a provider\n */\nexport function getProviderLogoURL(provider: LLMProvider): string {\n const modelsDevId = PROVIDER_ID_MAP[provider];\n if (!modelsDevId) return \"\";\n\n return `https://models.dev/logos/${modelsDevId}.svg`;\n}\n","import type { LLMProvider } from \"./types\";\n\n/**\n * Provider configuration\n * Data sourced from https://models.dev/api.json\n */\ninterface ProviderConfig {\n baseURL?: string;\n docURL?: string;\n description?: string;\n}\n\n/**\n * Provider configurations\n * Based on https://models.dev/api.json\n */\nconst PROVIDER_CONFIGS: Partial<Record<LLMProvider, ProviderConfig>> = {\n openai: {\n docURL: \"https://platform.openai.com/docs\",\n description: \"OpenAI's GPT models\",\n },\n anthropic: {\n docURL: \"https://docs.anthropic.com\",\n description: \"Anthropic's Claude models\",\n },\n google: {\n docURL: \"https://ai.google.dev/docs\",\n description: \"Google's Gemini models\",\n },\n deepseek: {\n docURL: \"https://platform.deepseek.com/docs\",\n description: \"DeepSeek's AI models\",\n },\n groq: {\n baseURL: \"https://api.groq.com/openai/v1\",\n docURL: \"https://console.groq.com/docs\",\n description: \"Ultra-fast LLM inference\",\n },\n mistral: {\n docURL: \"https://docs.mistral.ai\",\n description: \"Mistral AI models\",\n },\n cohere: {\n baseURL: \"https://api.cohere.ai/v1\",\n docURL: \"https://docs.cohere.com\",\n description: \"Cohere's language models\",\n },\n ollama: {\n baseURL: \"http://localhost:11434/v1\",\n docURL: \"https://ollama.ai\",\n description: \"Local AI models\",\n },\n};\n\n/**\n * Get default base URL for a provider\n */\nexport function getDefaultBaseURL(provider: LLMProvider): string | undefined {\n return PROVIDER_CONFIGS[provider]?.baseURL;\n}\n\n/**\n * Get documentation URL for a provider\n */\nexport function getProviderDocURL(provider: LLMProvider): string | undefined {\n return PROVIDER_CONFIGS[provider]?.docURL;\n}\n\n/**\n * Get provider description\n */\nexport function getProviderDescription(\n provider: LLMProvider,\n): string | undefined {\n return PROVIDER_CONFIGS[provider]?.description;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { LLMConfig, LLMProvider } from \"./types\";\n\nconst CONFIG_DIR = join(homedir(), \".kly\");\nconst CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\n\nexport interface KlyConfig {\n currentModel?: string;\n models: Record<string, LLMConfig>;\n}\n\n/**\n * Ensure config directory exists\n */\nfunction ensureConfigDir(): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\n/**\n * Load configuration from ~/.kly/config.json\n */\nexport function loadConfig(): KlyConfig {\n ensureConfigDir();\n\n if (!existsSync(CONFIG_FILE)) {\n return { models: {} };\n }\n\n try {\n const content = readFileSync(CONFIG_FILE, \"utf-8\");\n return JSON.parse(content);\n } catch (error) {\n console.error(\"Failed to parse config file:\", error);\n return { models: {} };\n }\n}\n\n/**\n * Save configuration to ~/.kly/config.json\n */\nexport function saveConfig(config: KlyConfig): void {\n ensureConfigDir();\n writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n/**\n * Get current active model configuration\n */\nexport function getCurrentModelConfig(): LLMConfig | null {\n const config = loadConfig();\n\n if (!config.currentModel) {\n return null;\n }\n\n return config.models[config.currentModel] || null;\n}\n\n/**\n * Set a model as current\n */\nexport function setCurrentModel(modelName: string): void {\n const config = loadConfig();\n\n if (!config.models[modelName]) {\n throw new Error(`Model '${modelName}' not found in config`);\n }\n\n config.currentModel = modelName;\n saveConfig(config);\n}\n\n/**\n * Add or update a model configuration\n */\nexport function saveModelConfig(\n modelName: string,\n modelConfig: LLMConfig,\n): void {\n const config = loadConfig();\n\n config.models[modelName] = modelConfig;\n\n // Set as current if it's the first model\n if (!config.currentModel) {\n config.currentModel = modelName;\n }\n\n saveConfig(config);\n}\n\n/**\n * Remove a model configuration\n */\nexport function removeModelConfig(modelName: string): void {\n const config = loadConfig();\n\n delete config.models[modelName];\n\n // Clear current if it was removed\n if (config.currentModel === modelName) {\n config.currentModel = undefined;\n }\n\n saveConfig(config);\n}\n\n/**\n * List all configured models\n */\nexport function listModels(): Array<{\n name: string;\n config: LLMConfig;\n isCurrent: boolean;\n}> {\n const config = loadConfig();\n\n return Object.entries(config.models).map(([name, modelConfig]) => ({\n name,\n config: modelConfig,\n isCurrent: name === config.currentModel,\n }));\n}\n\n/**\n * Get provider display name\n */\nexport function getProviderDisplayName(provider: LLMProvider): string {\n const displayNames: Record<LLMProvider, string> = {\n openai: \"OpenAI\",\n anthropic: \"Anthropic\",\n google: \"Google\",\n deepseek: \"DeepSeek\",\n ollama: \"Ollama\",\n groq: \"Groq\",\n mistral: \"Mistral\",\n cohere: \"Cohere\",\n \"openai-compatible\": \"OpenAI Compatible\",\n };\n return displayNames[provider];\n}\n","/**\n * Supported LLM providers\n */\nexport type LLMProvider =\n | \"openai\"\n | \"anthropic\"\n | \"google\"\n | \"deepseek\"\n | \"ollama\"\n | \"groq\"\n | \"mistral\"\n | \"cohere\"\n | \"openai-compatible\"; // For custom OpenAI-compatible endpoints\n\n/**\n * LLM provider configuration\n */\nexport interface LLMConfig {\n provider: LLMProvider;\n apiKey?: string; // Optional for Ollama (local)\n baseURL?: string;\n model?: string;\n}\n\n/**\n * Default models for each provider\n * Based on https://models.dev/ (2025-12)\n */\nexport const DEFAULT_MODELS: Record<LLMProvider, string> = {\n openai: \"gpt-4o-mini\", // Fast, cheap ($0.15/$0.60 per 1M tokens)\n anthropic: \"claude-3-5-sonnet-20241022\", // Best balance ($3/$15 per 1M tokens)\n google: \"gemini-2.5-flash\", // Fast, cheap ($0.07/$0.30 per 1M tokens)\n deepseek: \"deepseek-v3\", // Cost-effective ($0.27/$0.41 per 1M tokens)\n ollama: \"llama3.2\", // Free, local\n groq: \"llama-3.3-70b-versatile\", // Ultra-fast inference\n mistral: \"mistral-large-2411\", // Latest Mistral\n cohere: \"command-r-plus\", // Enhanced reasoning\n \"openai-compatible\": \"gpt-4o-mini\", // Fallback\n};\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport {\n fetchModelsDevData,\n formatCapabilities,\n formatPrice,\n getModelInfo,\n getProviderModels,\n type ModelInfo,\n} from \"./models-dev\";\nimport { getDefaultBaseURL, getProviderDescription } from \"./provider-config\";\nimport {\n getProviderDisplayName,\n listModels,\n removeModelConfig,\n saveModelConfig,\n setCurrentModel,\n} from \"./storage\";\nimport type { LLMProvider } from \"./types\";\nimport { DEFAULT_MODELS } from \"./types\";\n\nconst PROVIDER_OPTIONS: Array<{\n value: LLMProvider;\n label: string;\n hint?: string;\n}> = [\n {\n value: \"openai\",\n label: \"OpenAI\",\n hint: getProviderDescription(\"openai\"),\n },\n {\n value: \"anthropic\",\n label: \"Anthropic\",\n hint: getProviderDescription(\"anthropic\"),\n },\n {\n value: \"google\",\n label: \"Google\",\n hint: getProviderDescription(\"google\"),\n },\n {\n value: \"deepseek\",\n label: \"DeepSeek\",\n hint: getProviderDescription(\"deepseek\"),\n },\n { value: \"groq\", label: \"Groq\", hint: getProviderDescription(\"groq\") },\n {\n value: \"mistral\",\n label: \"Mistral\",\n hint: getProviderDescription(\"mistral\"),\n },\n {\n value: \"cohere\",\n label: \"Cohere\",\n hint: getProviderDescription(\"cohere\"),\n },\n {\n value: \"ollama\",\n label: \"Ollama\",\n hint: getProviderDescription(\"ollama\"),\n },\n {\n value: \"openai-compatible\",\n label: \"OpenAI Compatible\",\n hint: \"Custom endpoint\",\n },\n];\n\n/**\n * Main entry point for `kly models` command\n */\nexport async function modelsCommand(): Promise<void> {\n clack.intro(pc.bgCyan(pc.black(\" kly models \")));\n\n const models = listModels();\n\n const action = (await clack.select({\n message: \"What would you like to do?\",\n options: [\n { value: \"list\", label: \"List configured models\" },\n { value: \"add\", label: \"Add a new model\" },\n {\n value: \"switch\",\n label: \"Switch current model\",\n disabled: models.length === 0,\n },\n {\n value: \"remove\",\n label: \"Remove a model\",\n disabled: models.length === 0,\n },\n ],\n })) as string;\n\n if (clack.isCancel(action)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n switch (action) {\n case \"list\":\n await listAction();\n break;\n case \"add\":\n await addAction();\n break;\n case \"switch\":\n await switchAction();\n break;\n case \"remove\":\n await removeAction();\n break;\n }\n\n clack.outro(pc.green(\"Done!\"));\n}\n\n/**\n * List all configured models\n */\nasync function listAction(): Promise<void> {\n const models = listModels();\n\n if (models.length === 0) {\n clack.note(\n \"No models configured yet.\\nRun 'kly models' and select 'Add a new model'\",\n );\n return;\n }\n\n // Try to fetch models.dev data for enhanced info\n const modelsData = await fetchModelsDevData();\n\n const lines: string[] = [];\n for (const model of models) {\n const current = model.isCurrent ? pc.green(\"✓ \") : \" \";\n const provider = getProviderDisplayName(model.config.provider);\n const modelName =\n model.config.model || DEFAULT_MODELS[model.config.provider];\n\n let line = `${current}${pc.cyan(model.name)} - ${provider} (${modelName})`;\n\n // Add pricing and capabilities if available\n if (modelsData) {\n const modelInfo = getModelInfo(\n modelsData,\n model.config.provider,\n modelName,\n );\n\n if (modelInfo) {\n const metadata = formatModelMetadata(modelInfo);\n if (metadata) {\n line += ` ${pc.dim(metadata)}`;\n }\n }\n }\n\n lines.push(line);\n }\n\n clack.note(lines.join(\"\\n\"), \"Configured models:\");\n}\n\n/**\n * Format model metadata (pricing and capabilities) for display\n */\nfunction formatModelMetadata(modelInfo: ModelInfo): string {\n const parts: string[] = [];\n\n // Add pricing\n if (\n modelInfo.cost?.input !== undefined &&\n modelInfo.cost?.output !== undefined\n ) {\n parts.push(\n `[$${formatPrice(modelInfo.cost.input)}/$${formatPrice(modelInfo.cost.output)} per 1M]`,\n );\n }\n\n // Add capabilities\n const caps = formatCapabilities(modelInfo);\n if (caps.length > 0) {\n parts.push(`[${caps.join(\", \")}]`);\n }\n\n return parts.join(\" \");\n}\n\n/**\n * Add a new model configuration\n */\nasync function addAction(): Promise<void> {\n const name = await clack.text({\n message: \"Enter a name for this model configuration:\",\n placeholder: \"my-openai\",\n validate: (value) => {\n if (!value) return \"Name is required\";\n if (listModels().some((m) => m.name === value)) {\n return \"A model with this name already exists\";\n }\n return undefined;\n },\n });\n\n if (clack.isCancel(name)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n const provider = (await clack.select({\n message: \"Select a provider:\",\n options: PROVIDER_OPTIONS,\n })) as LLMProvider;\n\n if (clack.isCancel(provider)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n // Get provider-specific configuration\n const config = await getProviderConfig(provider);\n\n saveModelConfig(name, config);\n\n clack.note(\n `Model '${pc.cyan(name)}' configured with ${getProviderDisplayName(provider)}`,\n pc.green(\"Success!\"),\n );\n}\n\n/**\n * Switch to a different model\n */\nasync function switchAction(): Promise<void> {\n const models = listModels();\n\n if (models.length === 0) {\n clack.note(\"No models configured\");\n return;\n }\n\n const modelName = (await clack.select({\n message: \"Select a model:\",\n options: models.map((m) => ({\n value: m.name,\n label: m.name,\n hint: `${getProviderDisplayName(m.config.provider)} - ${m.config.model || DEFAULT_MODELS[m.config.provider]}`,\n })),\n })) as string;\n\n if (clack.isCancel(modelName)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n setCurrentModel(modelName);\n\n clack.note(`Switched to '${pc.cyan(modelName)}'`, pc.green(\"Success!\"));\n}\n\n/**\n * Remove a model configuration\n */\nasync function removeAction(): Promise<void> {\n const models = listModels();\n\n if (models.length === 0) {\n clack.note(\"No models configured\");\n return;\n }\n\n const modelName = (await clack.select({\n message: \"Select a model to remove:\",\n options: models.map((m) => ({\n value: m.name,\n label: m.name,\n hint: `${getProviderDisplayName(m.config.provider)}`,\n })),\n })) as string;\n\n if (clack.isCancel(modelName)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n const confirm = (await clack.confirm({\n message: `Are you sure you want to remove '${modelName}'?`,\n })) as boolean;\n\n if (clack.isCancel(confirm) || !confirm) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n removeModelConfig(modelName);\n\n clack.note(`Removed '${pc.cyan(modelName)}'`, pc.green(\"Success!\"));\n}\n\n/**\n * Get provider-specific configuration\n */\nasync function getProviderConfig(provider: LLMProvider): Promise<{\n provider: LLMProvider;\n apiKey?: string;\n baseURL?: string;\n model?: string;\n}> {\n const defaultModel = DEFAULT_MODELS[provider];\n\n // Ollama doesn't need API key\n if (provider === \"ollama\") {\n const baseURL = (await clack.text({\n message: \"Ollama base URL:\",\n placeholder: \"http://localhost:11434\",\n defaultValue: \"http://localhost:11434\",\n })) as string;\n\n if (clack.isCancel(baseURL)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n const model = (await clack.text({\n message: \"Model name:\",\n placeholder: defaultModel,\n defaultValue: defaultModel,\n })) as string;\n\n if (clack.isCancel(model)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return {\n provider,\n baseURL: baseURL || \"http://localhost:11434\",\n model: model || defaultModel,\n };\n }\n\n // Other providers need API key\n const apiKey = (await clack.password({\n message: `Enter your ${getProviderDisplayName(provider)} API key:`,\n validate: (value) => {\n if (!value) return \"API key is required\";\n return undefined;\n },\n })) as string;\n\n if (clack.isCancel(apiKey)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n // Ask for optional base URL\n const customBaseURL = (await clack.confirm({\n message: \"Do you want to specify a custom base URL?\",\n initialValue: false,\n })) as boolean;\n\n if (clack.isCancel(customBaseURL)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n let baseURL: string | undefined;\n\n if (customBaseURL) {\n const defaultURL = getDefaultBaseURL(provider);\n const baseURLInput = (await clack.text({\n message: \"Base URL:\",\n placeholder: defaultURL || \"\",\n })) as string;\n\n if (clack.isCancel(baseURLInput)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n baseURL = baseURLInput || undefined;\n }\n\n // Try to fetch models from models.dev and let user select\n const model = await selectModelForProvider(provider, defaultModel);\n\n return {\n provider,\n apiKey,\n baseURL,\n model,\n };\n}\n\n/**\n * Let user select a model for a provider\n */\nasync function selectModelForProvider(\n provider: LLMProvider,\n defaultModel: string,\n): Promise<string | undefined> {\n const modelsData = await fetchModelsDevData();\n\n // If we have models.dev data and models are available, show selection\n if (modelsData) {\n const availableModels = getProviderModels(modelsData, provider);\n\n if (availableModels.length > 0) {\n return await selectFromModelList(availableModels, defaultModel);\n }\n }\n\n // Fallback to simple confirm/input\n return await selectModelWithInput(defaultModel);\n}\n\n/**\n * Show model selection list with pricing and capabilities\n */\nasync function selectFromModelList(\n availableModels: ModelInfo[],\n defaultModel: string,\n): Promise<string | undefined> {\n const modelOptions = availableModels.slice(0, 10).map((m) => {\n const parts: string[] = [];\n\n // Add pricing if available\n if (m.cost?.input !== undefined && m.cost?.output !== undefined) {\n parts.push(\n `$${formatPrice(m.cost.input)}/$${formatPrice(m.cost.output)} per 1M`,\n );\n }\n\n // Add capabilities\n const caps = formatCapabilities(m);\n if (caps.length > 0) {\n parts.push(caps.join(\", \"));\n }\n\n return {\n value: m.id,\n label: m.name || m.id,\n hint: parts.length > 0 ? parts.join(\" • \") : \"No info available\",\n };\n });\n\n // Add option to use default or enter custom\n modelOptions.push({\n value: \"__default__\",\n label: `Use default (${defaultModel})`,\n hint: \"Recommended\",\n });\n modelOptions.push({\n value: \"__custom__\",\n label: \"Enter custom model name\",\n hint: \"Advanced\",\n });\n\n const selectedModel = (await clack.select({\n message: \"Select a model:\",\n options: modelOptions,\n })) as string;\n\n if (clack.isCancel(selectedModel)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n if (selectedModel === \"__default__\") {\n return undefined; // Use default\n }\n\n if (selectedModel === \"__custom__\") {\n return await promptForModelName(defaultModel);\n }\n\n return selectedModel;\n}\n\n/**\n * Simple model selection via confirm + input\n */\nasync function selectModelWithInput(\n defaultModel: string,\n): Promise<string | undefined> {\n const useDefault = (await clack.confirm({\n message: `Use default model (${defaultModel})?`,\n initialValue: true,\n })) as boolean;\n\n if (clack.isCancel(useDefault)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n if (useDefault) {\n return undefined;\n }\n\n return await promptForModelName(defaultModel);\n}\n\n/**\n * Prompt user to enter a custom model name\n */\nasync function promptForModelName(defaultModel: string): Promise<string> {\n const modelInput = (await clack.text({\n message: \"Model name:\",\n placeholder: defaultModel,\n defaultValue: defaultModel,\n })) as string;\n\n if (clack.isCancel(modelInput)) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return modelInput || defaultModel;\n}\n","import type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport type { ModelConfig } from \"../types\";\n\n/**\n * Message sent from Host to Sandbox on initialization\n */\nexport interface SandboxInitMessage {\n type: \"init\";\n scriptPath: string;\n args: string[];\n appId: string;\n permissions: {\n allowApiKey: boolean;\n sandboxConfig: SandboxRuntimeConfig;\n };\n}\n\n/**\n * Request types sent from Sandbox to Host\n */\nexport type IPCRequest =\n | {\n type: \"getModelConfig\";\n id: string;\n payload: { name?: string };\n }\n | {\n type: \"listModels\";\n id: string;\n payload: Record<string, never>;\n }\n | {\n type: \"log\";\n id: string;\n payload: { level: \"info\" | \"warn\" | \"error\"; message: string };\n }\n | {\n type: \"prompt:input\";\n id: string;\n payload: {\n prompt: string;\n defaultValue?: string;\n placeholder?: string;\n maxLength?: number;\n };\n }\n | {\n type: \"prompt:select\";\n id: string;\n payload: {\n prompt: string;\n options: Array<{\n name: string;\n description?: string;\n value: string;\n }>;\n };\n }\n | {\n type: \"prompt:confirm\";\n id: string;\n payload: {\n message: string;\n defaultValue?: boolean;\n };\n }\n | {\n type: \"prompt:multiselect\";\n id: string;\n payload: {\n prompt: string;\n options: Array<{\n name: string;\n description?: string;\n value: string;\n }>;\n required?: boolean;\n };\n }\n | {\n type: \"prompt:form\";\n id: string;\n payload: {\n title?: string;\n fields: Array<{\n name: string;\n label: string;\n type: \"string\" | \"number\" | \"boolean\" | \"enum\";\n required?: boolean;\n defaultValue?: unknown;\n description?: string;\n enumValues?: string[];\n }>;\n };\n };\n\n/**\n * Response types sent from Host to Sandbox\n */\nexport type IPCResponse<T = unknown> =\n | {\n type: \"response\";\n id: string;\n success: true;\n data: T;\n }\n | {\n type: \"response\";\n id: string;\n success: false;\n error: string;\n };\n\n/**\n * Model info response (without sensitive data)\n */\nexport interface ModelInfoResponse {\n name: string;\n provider: string;\n model?: string;\n isCurrent: boolean;\n}\n\n/**\n * Model config response (with sensitive data like API keys)\n */\nexport interface ModelConfigResponse extends ModelConfig {\n provider: string;\n model?: string;\n apiKey?: string;\n baseURL?: string;\n}\n\n/**\n * Message sent from Sandbox to Host when execution completes\n */\nexport interface ExecutionCompleteMessage {\n type: \"complete\";\n success: boolean;\n result?: unknown;\n error?: string;\n}\n\n/**\n * Type guard for IPC messages\n */\nexport function isIPCRequest(msg: unknown): msg is IPCRequest {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n \"id\" in msg &&\n typeof msg.id === \"string\"\n );\n}\n\nexport function isIPCResponse(msg: unknown): msg is IPCResponse {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"response\" &&\n \"id\" in msg &&\n typeof msg.id === \"string\"\n );\n}\n\nexport function isSandboxInitMessage(msg: unknown): msg is SandboxInitMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"init\"\n );\n}\n\nexport function isExecutionCompleteMessage(\n msg: unknown,\n): msg is ExecutionCompleteMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"complete\"\n );\n}\n","import type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { getCurrentModelConfig, listModels } from \"../ai/storage\";\nimport type {\n IPCRequest,\n IPCResponse,\n ModelConfigResponse,\n ModelInfoResponse,\n} from \"../shared/ipc-protocol\";\n\nexport interface ResourceProviderOptions {\n appId: string;\n allowApiKey: boolean;\n sandboxConfig: SandboxRuntimeConfig;\n}\n\n/**\n * Resource Provider - Host-side IPC server\n * Handles resource requests from the sandboxed child process\n * Enforces permissions and provides controlled access to sensitive resources\n */\nexport class ResourceProvider {\n constructor(private options: ResourceProviderOptions) {}\n\n /**\n * Handle an IPC request from sandbox\n */\n async handle(request: IPCRequest): Promise<IPCResponse> {\n try {\n switch (request.type) {\n case \"listModels\":\n return this.handleListModels(request.id);\n\n case \"getModelConfig\":\n return this.handleGetModelConfig(request.id, request.payload.name);\n\n case \"log\":\n return this.handleLog(\n request.id,\n request.payload.level,\n request.payload.message,\n );\n\n case \"prompt:input\":\n return this.handlePromptInput(request.id, request.payload);\n\n case \"prompt:select\":\n return this.handlePromptSelect(request.id, request.payload);\n\n case \"prompt:confirm\":\n return this.handlePromptConfirm(request.id, request.payload);\n\n case \"prompt:multiselect\":\n return this.handlePromptMultiselect(request.id, request.payload);\n\n case \"prompt:form\":\n return this.handlePromptForm(request.id, request.payload);\n\n default: {\n const unknownRequest = request as { type: string; id: string };\n return {\n type: \"response\",\n id: unknownRequest.id,\n success: false,\n error: `Unknown request type: ${unknownRequest.type}`,\n };\n }\n }\n } catch (error) {\n return {\n type: \"response\",\n id: request.id,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * Handle: List available models (no permission required)\n */\n private handleListModels(\n requestId: string,\n ): IPCResponse<ModelInfoResponse[]> {\n const models = listModels();\n const response: ModelInfoResponse[] = models.map((m) => ({\n name: m.name,\n provider: m.config.provider,\n model: m.config.model,\n isCurrent: m.isCurrent,\n }));\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: response,\n };\n }\n\n /**\n * Handle: Get model config with API key (requires permission)\n */\n private handleGetModelConfig(\n requestId: string,\n name?: string,\n ): IPCResponse<ModelConfigResponse | null> {\n // Check permission\n if (!this.options.allowApiKey) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Permission denied: API key access not allowed for this app\",\n };\n }\n\n // Get model config\n let modelConfig: ModelConfigResponse | null = null;\n\n if (name) {\n const models = listModels();\n const found = models.find((m) => m.name === name);\n if (found) {\n modelConfig = {\n provider: found.config.provider,\n model: found.config.model,\n apiKey: found.config.apiKey,\n baseURL: found.config.baseURL,\n };\n }\n } else {\n const current = getCurrentModelConfig();\n if (current) {\n modelConfig = {\n provider: current.provider,\n model: current.model,\n apiKey: current.apiKey,\n baseURL: current.baseURL,\n };\n }\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: modelConfig,\n };\n }\n\n /**\n * Handle: Log message (for debugging)\n */\n private handleLog(\n requestId: string,\n level: \"info\" | \"warn\" | \"error\",\n message: string,\n ): IPCResponse<void> {\n const prefix = `[Sandbox:${this.options.appId}]`;\n\n switch (level) {\n case \"info\":\n console.log(`${prefix} ${message}`);\n break;\n case \"warn\":\n console.warn(`${prefix} ${message}`);\n break;\n case \"error\":\n console.error(`${prefix} ${message}`);\n break;\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: undefined,\n };\n }\n\n /**\n * Handle: Interactive input prompt\n */\n private async handlePromptInput(\n requestId: string,\n payload: {\n prompt: string;\n defaultValue?: string;\n placeholder?: string;\n maxLength?: number;\n },\n ): Promise<IPCResponse<string>> {\n const result = await p.text({\n message: payload.prompt,\n defaultValue: payload.defaultValue,\n placeholder: payload.placeholder,\n validate: payload.maxLength\n ? (value) => {\n if (value && value.length > payload.maxLength!) {\n return `Input must be ${payload.maxLength} characters or less`;\n }\n return undefined;\n }\n : undefined,\n });\n\n if (p.isCancel(result)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result,\n };\n }\n\n /**\n * Handle: Interactive select prompt\n */\n private async handlePromptSelect(\n requestId: string,\n payload: {\n prompt: string;\n options: Array<{\n name: string;\n description?: string;\n value: string;\n }>;\n },\n ): Promise<IPCResponse<string>> {\n const mappedOptions = payload.options.map((opt) => ({\n label: opt.name,\n value: opt.value as unknown,\n ...(opt.description && { hint: opt.description }),\n }));\n\n const result = await p.select({\n message: payload.prompt,\n options: mappedOptions,\n });\n\n if (p.isCancel(result)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result as string,\n };\n }\n\n /**\n * Handle: Interactive confirm prompt\n */\n private async handlePromptConfirm(\n requestId: string,\n payload: {\n message: string;\n defaultValue?: boolean;\n },\n ): Promise<IPCResponse<boolean>> {\n const result = await p.confirm({\n message: payload.message,\n initialValue: payload.defaultValue,\n });\n\n if (p.isCancel(result)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result,\n };\n }\n\n /**\n * Handle: Interactive multiselect prompt\n */\n private async handlePromptMultiselect(\n requestId: string,\n payload: {\n prompt: string;\n options: Array<{\n name: string;\n description?: string;\n value: string;\n }>;\n required?: boolean;\n },\n ): Promise<IPCResponse<string[]>> {\n const mappedOptions = payload.options.map((opt) => ({\n label: opt.name,\n value: opt.value as unknown,\n ...(opt.description && { hint: opt.description }),\n }));\n\n const result = await p.multiselect({\n message: payload.prompt,\n options: mappedOptions,\n required: payload.required,\n });\n\n if (p.isCancel(result)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result as string[],\n };\n }\n\n /**\n * Handle: Interactive form prompt\n */\n private async handlePromptForm(\n requestId: string,\n payload: {\n title?: string;\n fields: Array<{\n name: string;\n label: string;\n type: \"string\" | \"number\" | \"boolean\" | \"enum\";\n required?: boolean;\n defaultValue?: unknown;\n description?: string;\n enumValues?: string[];\n }>;\n },\n ): Promise<IPCResponse<Record<string, unknown>>> {\n const result: Record<string, unknown> = {};\n\n if (payload.title) {\n console.log(`\\n${pc.bold(payload.title)}\\n`);\n }\n\n for (const field of payload.fields) {\n const label = field.description\n ? `${field.label} (${field.description})`\n : field.label;\n\n if (field.type === \"boolean\") {\n const value = await p.confirm({\n message: label,\n initialValue: field.defaultValue as boolean | undefined,\n });\n\n if (p.isCancel(value)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n result[field.name] = value;\n } else if (field.type === \"enum\" && field.enumValues?.length) {\n const value = await p.select({\n message: label,\n options: field.enumValues.map((v) => ({\n label: v,\n value: v,\n })),\n });\n\n if (p.isCancel(value)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n result[field.name] = value;\n } else if (field.type === \"number\") {\n const strValue = await p.text({\n message: label,\n defaultValue: field.defaultValue?.toString(),\n validate: (value) => {\n if (value && Number.isNaN(Number.parseFloat(value))) {\n return \"Please enter a valid number\";\n }\n return undefined;\n },\n });\n\n if (p.isCancel(strValue)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n result[field.name] = Number.parseFloat(strValue);\n } else {\n const value = await p.text({\n message: label,\n defaultValue: field.defaultValue as string | undefined,\n });\n\n if (p.isCancel(value)) {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled by user\",\n };\n }\n\n result[field.name] = value;\n }\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result,\n };\n }\n}\n\n/**\n * Factory function to create a resource provider\n */\nexport function createResourceProvider(\n options: ResourceProviderOptions,\n): ResourceProvider {\n return new ResourceProvider(options);\n}\n","import { spawn } from \"node:child_process\";\nimport { resolve } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { SandboxManager } from \"@anthropic-ai/sandbox-runtime\";\nimport type {\n ExecutionCompleteMessage,\n SandboxInitMessage,\n} from \"../shared/ipc-protocol\";\nimport {\n isExecutionCompleteMessage,\n isIPCRequest,\n} from \"../shared/ipc-protocol\";\nimport { createResourceProvider } from \"./resource-provider\";\n\nexport interface LaunchOptions {\n scriptPath: string;\n args: string[];\n appId: string;\n sandboxConfig: SandboxRuntimeConfig;\n allowApiKey: boolean;\n}\n\nexport interface LaunchResult {\n exitCode: number;\n result?: unknown;\n error?: string;\n}\n\n/**\n * Launch a user script in a sandboxed child process\n * This is the Host-side launcher that:\n * 1. Spawns a child process with IPC\n * 2. Applies OS-level sandboxing\n * 3. Handles IPC communication for resource access\n * 4. Returns execution result\n */\nexport async function launchSandbox(\n options: LaunchOptions,\n): Promise<LaunchResult> {\n const { scriptPath, args, appId, sandboxConfig, allowApiKey } = options;\n\n // Initialize sandbox manager\n await SandboxManager.initialize(sandboxConfig);\n\n // Resolve paths\n const absoluteScriptPath = resolve(process.cwd(), scriptPath);\n\n // Find executor path based on current location\n // Development: src/host/launcher.ts -> src/sandbox/executor.ts\n // Production: dist/bin/kly.mjs (embedded) -> dist/sandbox/bundled-executor.mjs\n // __dirname from dist/bin/kly.mjs is dist/bin, so ../sandbox resolves to dist/sandbox\n // Use bundled-executor to avoid circular dependency issues\n const executorPath = resolve(__dirname, \"../sandbox/bundled-executor.mjs\");\n\n // Show sandbox info\n if (!SandboxManager.isSandboxingEnabled()) {\n console.warn(\"⚠️ Sandboxing is not supported on this platform.\");\n console.warn(\" Running without OS-level isolation.\");\n } else {\n console.log(\"🔒 Sandbox Configuration:\");\n console.log(\n ` Read denied: ${sandboxConfig.filesystem.denyRead.length} paths`,\n );\n console.log(\n ` Write allowed: ${sandboxConfig.filesystem.allowWrite.length} paths`,\n );\n console.log(\n ` Network: ${sandboxConfig.network.allowedDomains.join(\", \") || \"none\"}`,\n );\n console.log(\"\");\n }\n\n // Create the command to run in sandbox\n const command = `bun run ${executorPath}`;\n const wrappedCommand = await SandboxManager.wrapWithSandbox(command);\n\n // Spawn child process with IPC and inherited stdio for interactive input support\n // Using 'inherit' for stdin/stdout/stderr allows the child to directly access the TTY\n // This enables raw mode for interactive prompts (select, input, etc.)\n const child = spawn(wrappedCommand, {\n shell: true,\n stdio: [\"inherit\", \"inherit\", \"inherit\", \"ipc\"], // Inherit to support raw mode\n env: {\n ...process.env,\n KLY_SANDBOX_MODE: \"true\",\n },\n });\n\n // Create resource provider for handling IPC requests\n const resourceProvider = createResourceProvider({\n appId,\n allowApiKey,\n sandboxConfig,\n });\n\n // Handle IPC messages from sandbox\n child.on(\"message\", async (message: unknown) => {\n if (isIPCRequest(message)) {\n const response = await resourceProvider.handle(message);\n child.send(response);\n }\n });\n\n // Send initialization message to sandbox\n const initMessage: SandboxInitMessage = {\n type: \"init\",\n scriptPath: absoluteScriptPath,\n args,\n appId,\n permissions: {\n allowApiKey,\n sandboxConfig,\n },\n };\n child.send(initMessage);\n\n // Wait for execution to complete\n return new Promise((resolve, reject) => {\n let executionResult: ExecutionCompleteMessage | null = null;\n\n child.on(\"message\", (message: unknown) => {\n if (isExecutionCompleteMessage(message)) {\n executionResult = message;\n }\n });\n\n child.on(\"error\", (error) => {\n reject(new Error(`Sandbox process error: ${error.message}`));\n });\n\n child.on(\"exit\", (code) => {\n if (executionResult) {\n resolve({\n exitCode: code ?? 0,\n result: executionResult.result,\n error: executionResult.error,\n });\n } else {\n resolve({\n exitCode: code ?? 1,\n error: code !== 0 ? `Process exited with code ${code}` : undefined,\n });\n }\n });\n });\n}\n","/**\n * Centralized constants for the KLY project\n * Prevents magic strings and improves maintainability\n */\n\n/**\n * Environment variable names used throughout the application\n */\nexport const ENV_VARS = {\n SANDBOX_MODE: \"KLY_SANDBOX_MODE\",\n MCP_MODE: \"KLY_MCP_MODE\",\n PROGRAMMATIC: \"KLY_PROGRAMMATIC\",\n TRUST_ALL: \"KLY_TRUST_ALL\",\n LOCAL_REF: \"KLY_LOCAL_REF\",\n REMOTE_REF: \"KLY_REMOTE_REF\",\n} as const;\n\n/**\n * File and directory paths used for configuration and caching\n */\nexport const PATHS = {\n CONFIG_DIR: \".kly\",\n META_FILE: \".kly-meta.json\",\n PERMISSIONS_FILE: \"permissions.json\",\n CONFIG_FILE: \"config.json\",\n} as const;\n\n/**\n * Timeout values in milliseconds\n */\nexport const TIMEOUTS = {\n /** Standard IPC request timeout (30 seconds) */\n IPC_REQUEST: 30_000,\n /** Long-running IPC request timeout (60 seconds) */\n IPC_LONG_REQUEST: 60_000,\n} as const;\n\n/**\n * LLM API domains for network permission configuration\n */\nexport const LLM_API_DOMAINS = [\n \"api.openai.com\",\n \"*.anthropic.com\",\n \"generativelanguage.googleapis.com\",\n \"api.deepseek.com\",\n] as const;\n","/**\n * Runtime mode detection utilities\n * Centralizes environment variable checks for different execution modes\n */\n\nimport type { RuntimeMode } from \"../types\";\nimport { ENV_VARS } from \"./constants\";\n\n/**\n * Check if running in sandbox mode\n * Sandbox mode: Isolated child process with restricted permissions\n */\nexport function isSandbox(): boolean {\n return process.env[ENV_VARS.SANDBOX_MODE] === \"true\";\n}\n\n/**\n * Check if running in MCP (Model Context Protocol) mode\n * MCP mode: Running as an MCP server for Claude Desktop integration\n */\nexport function isMCP(): boolean {\n return process.env[ENV_VARS.MCP_MODE] === \"true\";\n}\n\n/**\n * Check if running in programmatic mode\n * Programmatic mode: Imported as a library in another application\n */\nexport function isProgrammatic(): boolean {\n return process.env[ENV_VARS.PROGRAMMATIC] === \"true\";\n}\n\n/**\n * Check if running with trust all flag\n * Trust all: Skip permission prompts (for testing/automation)\n */\nexport function isTrustAll(): boolean {\n return process.env[ENV_VARS.TRUST_ALL] === \"true\";\n}\n\n/**\n * Get local reference environment variable\n */\nexport function getLocalRef(): string | undefined {\n return process.env[ENV_VARS.LOCAL_REF];\n}\n\n/**\n * Get remote reference environment variable\n */\nexport function getRemoteRef(): string | undefined {\n return process.env[ENV_VARS.REMOTE_REF];\n}\n\n/**\n * Detect the current runtime mode\n * This is the canonical implementation that should be used throughout the app\n */\nexport function detectMode(): RuntimeMode {\n // Sandbox mode: Running inside sandboxed child process\n if (isSandbox()) {\n return \"cli\"; // Sandbox always runs in CLI mode\n }\n\n // MCP mode: Check for MCP environment variable\n if (isMCP()) {\n return \"mcp\";\n }\n\n // Programmatic mode: Check for explicit flag\n if (isProgrammatic()) {\n return \"programmatic\";\n }\n\n // CLI mode: Running a .ts file directly with bun\n const scriptPath = process.argv[1] ?? \"\";\n const isDirectRun = scriptPath.endsWith(\".ts\") || scriptPath.endsWith(\".js\");\n if (isDirectRun) {\n return \"cli\";\n }\n\n // Default to programmatic (e.g., when imported as a module)\n return \"programmatic\";\n}\n","/**\n * Check if we're in a TTY environment\n * Returns false in CI or non-interactive environments\n */\nexport function isTTY(): boolean {\n return Boolean(\n process.stdout.isTTY && process.stdin.isTTY && !process.env.CI,\n );\n}\n","import { TIMEOUTS } from \"../shared/constants\";\nimport type { IPCRequest, IPCResponse } from \"../shared/ipc-protocol\";\n\n/**\n * Send an IPC request to the host and wait for response\n * Used by UI components and other sandbox code to communicate with the host process\n */\nexport async function sendIPCRequest<T>(\n type: IPCRequest[\"type\"],\n payload: unknown,\n): Promise<T> {\n if (!process.send) {\n throw new Error(\"IPC not available - not running in sandbox mode\");\n }\n\n return new Promise((resolve, reject) => {\n const requestId = `${type}-${Date.now()}-${Math.random()}`;\n\n const request: IPCRequest = {\n type,\n id: requestId,\n payload,\n } as IPCRequest;\n\n // Set up response listener\n const responseHandler = (message: unknown) => {\n if (\n typeof message === \"object\" &&\n message !== null &&\n \"type\" in message &&\n message.type === \"response\" &&\n \"id\" in message &&\n message.id === requestId\n ) {\n process.off(\"message\", responseHandler);\n\n const response = message as IPCResponse<T>;\n if (response.success) {\n resolve(response.data);\n } else {\n reject(new Error(response.error));\n }\n }\n };\n\n process.on(\"message\", responseHandler);\n\n // Send request\n if (!process.send!(request)) {\n // Send failed immediately\n process.off(\"message\", responseHandler);\n reject(new Error(\"Failed to send IPC message\"));\n return;\n }\n\n // Timeout for long-running requests (prompts, etc.)\n setTimeout(() => {\n process.off(\"message\", responseHandler);\n reject(new Error(`IPC request timeout: ${type}`));\n }, TIMEOUTS.IPC_LONG_REQUEST);\n });\n}\n","import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Simplified confirm function\n *\n * @example\n * ```typescript\n * const proceed = await confirm(\"Continue?\", true);\n * ```\n */\nexport async function confirm(\n message: string,\n defaultValue = false,\n): Promise<boolean> {\n // Sandbox mode: use IPC to request confirm from host\n if (isSandbox()) {\n return sendIPCRequest<boolean>(\"prompt:confirm\", { message, defaultValue });\n }\n\n if (!isTTY()) {\n // In MCP mode, warn about using default value for confirmation\n if (isMCP()) {\n console.warn(\n `[MCP Warning] Interactive confirmation not available. Using default value (${defaultValue}) for: ${message}`,\n );\n }\n return defaultValue;\n }\n\n const result = await p.confirm({\n message,\n initialValue: defaultValue,\n });\n\n if (p.isCancel(result)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return result;\n}\n","import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Option for select menus\n */\nexport interface SelectOption<T = string> {\n /** Display name */\n name: string;\n /** Optional description shown below name */\n description?: string;\n /** Value returned when selected */\n value: T;\n /** Disable this option */\n disabled?: boolean;\n}\n\n/**\n * Configuration for select component\n */\nexport interface SelectConfig<T> {\n /** Options to choose from */\n options: SelectOption<T>[];\n /** Prompt message */\n prompt?: string;\n}\n\n/**\n * Show a selection menu and wait for user choice\n *\n * @example\n * ```typescript\n * const color = await select({\n * options: [\n * { name: \"Red\", value: \"red\" },\n * { name: \"Blue\", value: \"blue\", description: \"Ocean color\" },\n * ],\n * prompt: \"Pick a color\"\n * });\n * ```\n */\nexport async function select<T = string>(config: SelectConfig<T>): Promise<T> {\n // Sandbox mode: use IPC to request select from host\n if (isSandbox()) {\n return sendIPCRequest<T>(\"prompt:select\", {\n prompt: config.prompt ?? \"Select an option\",\n options: config.options,\n });\n }\n\n // Non-TTY fallback: auto-select first option or throw in MCP mode\n if (!isTTY()) {\n // In MCP mode, interactive selection is not allowed\n if (isMCP()) {\n throw new Error(\n `Interactive selection not available in MCP mode. All parameters must be defined in the tool's inputSchema. Selection prompt: ${config.prompt}`,\n );\n }\n\n const firstOption = config.options[0];\n if (!firstOption) {\n throw new Error(\"No options provided\");\n }\n return firstOption.value;\n }\n\n // Map to @clack/prompts Option format\n // Using type assertion due to complex Option<T> conditional types\n const mappedOptions = config.options.map((opt) => ({\n label: opt.name,\n value: opt.value as unknown,\n ...(opt.description && { hint: opt.description }),\n }));\n\n const result = await p.select({\n message: config.prompt ?? \"Select an option\",\n options: mappedOptions,\n });\n\n if (p.isCancel(result)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return result as T;\n}\n","import pc from \"picocolors\";\n\n/**\n * Default color theme for UI components (hex values for reference)\n */\nexport const theme = {\n // Brand colors\n primary: \"#3b82f6\", // Blue\n success: \"#10b981\", // Green\n warning: \"#f59e0b\", // Orange\n error: \"#ef4444\", // Red\n info: \"#06b6d4\", // Cyan\n\n // UI colors\n background: \"#161b22\", // Dark gray\n surface: \"#1e293b\", // Lighter gray\n border: \"#30363d\", // Border gray\n text: \"#c9d1d9\", // Light text\n textDim: \"#8b949e\", // Dimmed text\n textBright: \"#ffffff\", // Bright text\n\n // Interactive states\n focused: \"#3b82f6\", // Blue\n selected: \"#3b82f6\", // Blue\n hover: \"#334155\", // Lighter gray\n disabled: \"#6e7681\", // Muted gray\n} as const;\n\nexport type AnsiColor =\n | \"red\"\n | \"green\"\n | \"yellow\"\n | \"blue\"\n | \"magenta\"\n | \"cyan\"\n | \"white\"\n | \"gray\";\n\n/**\n * Format text with picocolors\n */\nexport function formatText(\n text: string,\n options?: {\n color?: AnsiColor;\n bold?: boolean;\n dim?: boolean;\n italic?: boolean;\n underline?: boolean;\n },\n): string {\n let result = text;\n\n // Apply color first\n if (options?.color) {\n switch (options.color) {\n case \"red\":\n result = pc.red(result);\n break;\n case \"green\":\n result = pc.green(result);\n break;\n case \"yellow\":\n result = pc.yellow(result);\n break;\n case \"blue\":\n result = pc.blue(result);\n break;\n case \"magenta\":\n result = pc.magenta(result);\n break;\n case \"cyan\":\n result = pc.cyan(result);\n break;\n case \"white\":\n result = pc.white(result);\n break;\n case \"gray\":\n result = pc.gray(result);\n break;\n }\n }\n\n // Apply styles\n if (options?.bold) result = pc.bold(result);\n if (options?.dim) result = pc.dim(result);\n if (options?.italic) result = pc.italic(result);\n if (options?.underline) result = pc.underline(result);\n\n return result;\n}\n\n// Re-export picocolors for direct usage\nexport { pc };\n","import { formatText, pc } from \"../utils/colors\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Column alignment options\n */\nexport type ColumnAlign = \"left\" | \"center\" | \"right\";\n\n/**\n * Column definition for table\n */\nexport interface TableColumn<T = Record<string, unknown>> {\n /** Column key in the data object */\n key: keyof T;\n /** Display header text */\n header: string;\n /** Column alignment (default: left) */\n align?: ColumnAlign;\n /** Fixed column width (auto-calculated if not specified) */\n width?: number;\n /** Custom formatter function for cell values */\n formatter?: (value: unknown, row: T) => string;\n}\n\n/**\n * Configuration for table component\n */\nexport interface TableConfig<T = Record<string, unknown>> {\n /** Column definitions */\n columns: TableColumn<T>[];\n /** Data rows */\n rows: T[];\n /** Show header row (default: true) */\n showHeader?: boolean;\n /** Show borders (default: true in TTY mode) */\n showBorders?: boolean;\n /** Table title (optional) */\n title?: string;\n}\n\n/**\n * Align text within a given width\n */\nfunction alignText(text: string, width: number, align: ColumnAlign): string {\n const textLength = stripAnsi(text).length;\n const padding = Math.max(0, width - textLength);\n\n switch (align) {\n case \"right\":\n return \" \".repeat(padding) + text;\n case \"center\": {\n const leftPad = Math.floor(padding / 2);\n const rightPad = padding - leftPad;\n return \" \".repeat(leftPad) + text + \" \".repeat(rightPad);\n }\n default:\n return text + \" \".repeat(padding);\n }\n}\n\n/**\n * Strip ANSI escape codes from string for length calculation\n */\nfunction stripAnsi(str: string): string {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI codes regex\n return str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\n/**\n * Calculate column widths based on content\n */\nfunction calculateColumnWidths<T>(\n columns: TableColumn<T>[],\n rows: T[],\n showHeader: boolean,\n): number[] {\n return columns.map((col) => {\n // Use fixed width if specified\n if (col.width !== undefined) {\n return col.width;\n }\n\n // Calculate max width from header and data\n let maxWidth = showHeader ? col.header.length : 0;\n\n for (const row of rows) {\n const value = row[col.key];\n const formatted = col.formatter\n ? col.formatter(value, row)\n : String(value ?? \"\");\n const length = stripAnsi(formatted).length;\n maxWidth = Math.max(maxWidth, length);\n }\n\n return maxWidth;\n });\n}\n\n/**\n * Format a single cell value\n */\nfunction formatCell<T>(value: unknown, row: T, column: TableColumn<T>): string {\n if (column.formatter) {\n return column.formatter(value, row);\n }\n\n if (value === null || value === undefined) {\n return pc.dim(\"-\");\n }\n\n return String(value);\n}\n\n/**\n * Render table in TTY mode with borders and styling\n */\nfunction renderTTY<T>(config: TableConfig<T>): string {\n const {\n columns,\n rows,\n showHeader = true,\n showBorders = true,\n title,\n } = config;\n const lines: string[] = [];\n\n // Calculate column widths\n const widths = calculateColumnWidths(columns, rows, showHeader);\n\n // Title\n if (title) {\n lines.push(\"\");\n lines.push(formatText(`${title}`, { bold: true }));\n lines.push(\"\");\n }\n\n // Header row\n if (showHeader) {\n const headerCells = columns.map((col, i) => {\n const text = formatText(col.header, { bold: true, color: \"cyan\" });\n return alignText(text, widths[i]!, col.align ?? \"left\");\n });\n\n lines.push(headerCells.join(\" \"));\n\n // Header separator\n if (showBorders) {\n const separatorParts = widths.map((w) => \"─\".repeat(w));\n lines.push(pc.gray(separatorParts.join(\"─\")));\n }\n }\n\n // Data rows\n for (const row of rows) {\n const cells = columns.map((col, i) => {\n const value = row[col.key];\n const formatted = formatCell(value, row, col);\n return alignText(formatted, widths[i]!, col.align ?? \"left\");\n });\n\n lines.push(cells.join(\" \"));\n }\n\n // Bottom border (optional)\n if (showBorders && rows.length > 0) {\n const separatorParts = widths.map((w) => \"─\".repeat(w));\n lines.push(pc.gray(separatorParts.join(\"─\")));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Render table in non-TTY mode (plain text)\n */\nfunction renderPlain<T>(config: TableConfig<T>): string {\n const { columns, rows, showHeader = true, title } = config;\n const lines: string[] = [];\n\n // Calculate column widths\n const widths = calculateColumnWidths(columns, rows, showHeader);\n\n // Title\n if (title) {\n lines.push(\"\");\n lines.push(title);\n lines.push(\"\");\n }\n\n // Header row\n if (showHeader) {\n const headerCells = columns.map((col, i) =>\n alignText(col.header, widths[i]!, col.align ?? \"left\"),\n );\n lines.push(headerCells.join(\" \"));\n\n // Separator\n const separator = columns.map((_, i) => \"-\".repeat(widths[i]!)).join(\" \");\n lines.push(separator);\n }\n\n // Data rows\n for (const row of rows) {\n const cells = columns.map((col, i) => {\n const value = row[col.key];\n const formatted = formatCell(value, row, col);\n return alignText(stripAnsi(formatted), widths[i]!, col.align ?? \"left\");\n });\n lines.push(cells.join(\" \"));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Display a table with columns and rows\n *\n * @example\n * ```typescript\n * table({\n * title: \"Users\",\n * columns: [\n * { key: \"name\", header: \"Name\" },\n * { key: \"age\", header: \"Age\", align: \"right\" },\n * { key: \"status\", header: \"Status\", formatter: (val) =>\n * val === \"active\" ? pc.green(\"✓ Active\") : pc.red(\"✗ Inactive\")\n * },\n * ],\n * rows: [\n * { name: \"Alice\", age: 25, status: \"active\" },\n * { name: \"Bob\", age: 30, status: \"inactive\" },\n * ],\n * });\n * ```\n */\nexport function table<T = Record<string, unknown>>(\n config: TableConfig<T>,\n): void {\n const output = isTTY() ? renderTTY(config) : renderPlain(config);\n console.log(output);\n}\n","import { formatText } from \"./colors\";\n\n/**\n * Output a result to the console\n *\n * @param result - The result to display (string, object, etc.)\n */\nexport function output(result: unknown): void {\n if (result === undefined || result === null) {\n return;\n }\n\n if (typeof result === \"string\") {\n console.log(result);\n } else {\n console.log(JSON.stringify(result, null, 2));\n }\n}\n\n/**\n * Display an error message with optional suggestions\n *\n * @param message - Error message\n * @param suggestions - Optional suggestions for fixing the error\n */\nexport function error(message: string, suggestions?: string[]): void {\n console.error(\n `${formatText(\"Error:\", { color: \"red\", bold: true })} ${message}`,\n );\n\n if (suggestions?.length) {\n console.error(\"\");\n console.error(formatText(\"Suggestions:\", { dim: true }));\n for (const suggestion of suggestions) {\n console.error(` ${formatText(\"•\", { dim: true })} ${suggestion}`);\n }\n }\n}\n\n/**\n * Display help text\n *\n * @param content - Help text content\n */\nexport function help(content: string): void {\n console.log(content);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { PATHS } from \"../shared/constants\";\nimport { getLocalRef, getRemoteRef, isTrustAll } from \"../shared/runtime-mode\";\nimport { select } from \"../ui\";\nimport { isTTY } from \"../ui/utils/tty\";\n\nconst CONFIG_DIR = join(homedir(), PATHS.CONFIG_DIR);\nconst PERMISSIONS_FILE = join(CONFIG_DIR, PATHS.PERMISSIONS_FILE);\n\n/**\n * Permission record for an app\n * Only \"always\" choices are stored\n */\ninterface PermissionRecord {\n /** When the permission was granted */\n timestamp: string;\n /** User's choice: always \"always\" (only stored choice) */\n choice: \"always\";\n /** Sandbox configuration (optional, for sandboxed execution) */\n sandboxConfig?: SandboxRuntimeConfig;\n}\n\n/**\n * Permissions configuration\n */\ninterface PermissionsConfig {\n /** Trusted apps with their permissions */\n trustedApps: Record<string, PermissionRecord>;\n}\n\n/**\n * Get app identifier from script path\n */\nexport function getAppIdentifier(): string {\n // Check for explicit local file reference (set by kly run command)\n const localRef = getLocalRef();\n if (localRef) {\n return localRef;\n }\n\n // Remote app (from environment variable set by remote loader)\n const remoteRef = getRemoteRef();\n if (remoteRef) {\n return remoteRef;\n }\n\n // Fallback to script path for direct execution\n const scriptPath = process.argv[1] ?? \"\";\n if (scriptPath.startsWith(\"/\") || scriptPath.startsWith(\"C:\\\\\")) {\n return `local:${scriptPath}`;\n }\n\n // Default to script path\n return scriptPath || \"unknown\";\n}\n\n/**\n * Get friendly app name for display\n */\nexport function getAppName(appId: string): string {\n if (appId.startsWith(\"local:\")) {\n const path = appId.slice(6);\n const parts = path.split(\"/\");\n return parts[parts.length - 1] || path;\n }\n\n if (appId.startsWith(\"github.com/\")) {\n const parts = appId.split(\"/\");\n return parts.slice(1, 3).join(\"/\");\n }\n\n return appId;\n}\n\n/**\n * Ensure permissions config directory exists\n */\nfunction ensurePermissionsDir(): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\n/**\n * Load permissions configuration\n */\nexport function loadPermissions(): PermissionsConfig {\n ensurePermissionsDir();\n\n if (!existsSync(PERMISSIONS_FILE)) {\n return { trustedApps: {} };\n }\n\n try {\n const content = readFileSync(PERMISSIONS_FILE, \"utf-8\");\n return JSON.parse(content);\n } catch (error) {\n console.error(\"Failed to parse permissions file:\", error);\n return { trustedApps: {} };\n }\n}\n\n/**\n * Save permissions configuration\n */\nexport function savePermissions(config: PermissionsConfig): void {\n ensurePermissionsDir();\n writeFileSync(PERMISSIONS_FILE, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n/**\n * Request permission from user with interactive prompt\n */\nasync function requestPermission(\n appId: string,\n appName: string,\n): Promise<boolean> {\n // Check if running in TTY mode\n if (!isTTY()) {\n console.error(\n `\\nPermission required: App \"${appName}\" (${appId}) wants to access your API keys.`,\n );\n console.error(\n \"Set KLY_TRUST_ALL=true environment variable to grant access in non-interactive mode.\",\n );\n return false;\n }\n\n console.log(\"\");\n console.log(`App \"${appName}\" is requesting access to your API keys.`);\n console.log(`Source: ${appId}`);\n console.log(\"\");\n console.log(\"This will allow the app to use your configured LLM models.\");\n console.log(\"\");\n\n const choice = await select({\n prompt: \"Do you want to allow this?\",\n options: [\n {\n name: \"Allow once\",\n value: \"once\",\n description: \"Allow for this session only\",\n },\n {\n name: \"Always allow\",\n value: \"always\",\n description: \"Remember this choice for future runs\",\n },\n { name: \"Cancel\", value: \"cancel\", description: \"Cancel and exit\" },\n ],\n });\n\n // Cancel - don't save, just reject\n if (choice === \"cancel\") {\n return false;\n }\n\n // Always - save to config\n if (choice === \"always\") {\n const config = loadPermissions();\n config.trustedApps[appId] = {\n timestamp: new Date().toISOString(),\n choice: \"always\",\n };\n savePermissions(config);\n return true;\n }\n\n // Once - don't save, just allow for this run\n return true;\n}\n\n/**\n * Check if an app has permission to access API keys\n * If not, prompt user for permission (in interactive mode)\n */\nexport async function checkApiKeyPermission(appId: string): Promise<boolean> {\n // Allow bypass via environment variable (for CI/automation)\n if (isTrustAll()) {\n return true;\n }\n\n // Check stored permissions\n const config = loadPermissions();\n const record = config.trustedApps[appId];\n\n // If record exists, it's always \"always allow\" (we only store grants)\n if (record && record.choice === \"always\") {\n return true;\n }\n\n // No stored permission - need to request\n const appName = getAppName(appId);\n return await requestPermission(appId, appName);\n}\n\n/**\n * Revoke permission for an app\n */\nexport function revokePermission(appId: string): void {\n const config = loadPermissions();\n delete config.trustedApps[appId];\n savePermissions(config);\n}\n\n/**\n * List all granted permissions\n * Only \"always allow\" permissions are stored\n */\nexport function listPermissions(): Array<{\n appId: string;\n appName: string;\n timestamp: string;\n choice: string;\n}> {\n const config = loadPermissions();\n\n return Object.entries(config.trustedApps).map(([appId, record]) => ({\n appId,\n appName: getAppName(appId),\n timestamp: record.timestamp,\n choice: record.choice,\n }));\n}\n\n/**\n * Request sandbox configuration from user interactively\n * Returns SandboxRuntimeConfig directly (no conversion needed)\n */\nasync function requestSandboxConfig(\n appId: string,\n appName: string,\n): Promise<SandboxRuntimeConfig | null> {\n if (!isTTY()) {\n console.error(`\\nSandbox permission required for: \"${appName}\" (${appId})`);\n console.error(\n \"Set KLY_TRUST_ALL=true environment variable to run without sandboxing in non-interactive mode.\",\n );\n return null;\n }\n\n const homeDir = homedir();\n const currentDir = process.cwd();\n\n console.log(\"\");\n console.log(`🔐 Sandbox Permission Request from: ${appName}`);\n console.log(\"\");\n\n // Ask for filesystem read permissions\n console.log(\"📂 Filesystem Read Access:\");\n const fsReadChoice = await select({\n prompt: \"Which files should be denied for reading?\",\n options: [\n {\n name: \"Sensitive only\",\n value: \"sensitive\",\n description: \"Deny access to ~/.kly, ~/.ssh, ~/.aws, etc.\",\n },\n {\n name: \"All home directory\",\n value: \"all-home\",\n description: \"Deny access to entire home directory\",\n },\n {\n name: \"None (allow all)\",\n value: \"none\",\n description: \"No read restrictions (except ~/.kly)\",\n },\n ],\n });\n\n // Always deny reading sensitive directories (hardcoded for security)\n let denyRead: string[] = [join(homeDir, \".kly\")];\n\n if (fsReadChoice === \"sensitive\") {\n denyRead = [\n join(homeDir, \".kly\"),\n join(homeDir, \".ssh\"),\n join(homeDir, \".aws\"),\n join(homeDir, \".gnupg\"),\n ];\n } else if (fsReadChoice === \"all-home\") {\n denyRead = [homeDir];\n }\n // Note: Even if user chooses \"none\", .kly is still protected\n\n // Ask for filesystem write permissions\n console.log(\"\");\n console.log(\"📝 Filesystem Write Access:\");\n const fsWriteChoice = await select({\n prompt: \"Which directories should be allowed for writing?\",\n options: [\n {\n name: \"None\",\n value: \"none\",\n description: \"No write access\",\n },\n {\n name: \"Current directory only\",\n value: \"current\",\n description: `Allow write to ${currentDir}`,\n },\n {\n name: \"Temporary directory\",\n value: \"temp\",\n description: \"Allow write to system temp directory\",\n },\n ],\n });\n\n let allowWrite: string[] = [];\n if (fsWriteChoice === \"current\") {\n allowWrite = [currentDir];\n } else if (fsWriteChoice === \"temp\") {\n const tmpdir = process.env.TMPDIR || process.env.TEMP || \"/tmp\";\n allowWrite = [tmpdir];\n }\n\n // Always deny writing to sensitive directories (hardcoded for security)\n const denyWrite = [\n join(homeDir, \".kly\"), // KLY config and permissions\n join(homeDir, \".ssh\"), // SSH keys\n join(homeDir, \".aws\"), // AWS credentials\n join(homeDir, \".gnupg\"), // GPG keys\n ];\n\n // Ask for network permissions\n console.log(\"\");\n console.log(\"🌐 Network Access:\");\n const networkChoice = await select({\n prompt: \"Which network access should be allowed?\",\n options: [\n {\n name: \"None\",\n value: \"none\",\n description: \"No network access\",\n },\n {\n name: \"LLM APIs only\",\n value: \"llm-apis\",\n description: \"OpenAI, Anthropic, Google AI\",\n },\n {\n name: \"Common APIs\",\n value: \"common\",\n description: \"LLM + GitHub, npm, etc.\",\n },\n {\n name: \"All domains\",\n value: \"all\",\n description: \"Allow all network access\",\n },\n ],\n });\n\n let allowedDomains: string[] = [];\n if (networkChoice === \"llm-apis\") {\n allowedDomains = [\n \"api.openai.com\",\n \"*.anthropic.com\",\n \"generativelanguage.googleapis.com\",\n ];\n } else if (networkChoice === \"common\") {\n allowedDomains = [\n \"api.openai.com\",\n \"*.anthropic.com\",\n \"generativelanguage.googleapis.com\",\n \"*.github.com\",\n \"registry.npmjs.org\",\n ];\n } else if (networkChoice === \"all\") {\n allowedDomains = [\"*\"];\n }\n\n // Ask how long to remember this choice\n console.log(\"\");\n const duration = await select({\n prompt: \"How long should these permissions last?\",\n options: [\n {\n name: \"One time only\",\n value: \"once\",\n description: \"Ask again next time\",\n },\n {\n name: \"Always allow\",\n value: \"always\",\n description: \"Remember for this app\",\n },\n {\n name: \"Cancel\",\n value: \"cancel\",\n description: \"Cancel and exit\",\n },\n ],\n });\n\n // Cancel - don't save, just reject\n if (duration === \"cancel\") {\n return null;\n }\n\n // Construct SandboxRuntimeConfig directly\n const sandboxConfig: SandboxRuntimeConfig = {\n network: {\n allowedDomains,\n deniedDomains: [],\n },\n filesystem: {\n denyRead,\n allowWrite,\n denyWrite,\n },\n };\n\n // Save permission record only if \"always\"\n if (duration === \"always\") {\n const config = loadPermissions();\n config.trustedApps[appId] = {\n sandboxConfig,\n timestamp: new Date().toISOString(),\n choice: \"always\",\n };\n savePermissions(config);\n }\n\n console.log(\"\");\n console.log(\"✅ Sandbox permissions granted!\");\n return sandboxConfig;\n}\n\n/**\n * Get sandbox configuration for an app\n * Returns SandboxRuntimeConfig directly (no conversion needed)\n *\n * @param appId - App identifier\n * @returns SandboxRuntimeConfig or null if denied\n */\nexport async function getAppSandboxConfig(\n appId: string,\n): Promise<SandboxRuntimeConfig | null> {\n const homeDir = homedir();\n\n // Check for trust-all bypass (for automation)\n if (isTrustAll()) {\n // Even in trust-all mode, protect sensitive directories\n return {\n network: { allowedDomains: [\"*\"], deniedDomains: [] },\n filesystem: {\n denyRead: [join(homeDir, \".kly\")], // ALWAYS deny reading KLY config\n allowWrite: [\"*\"],\n denyWrite: [\n join(homeDir, \".kly\"), // KLY config and permissions\n join(homeDir, \".ssh\"), // SSH keys\n join(homeDir, \".aws\"), // AWS credentials\n join(homeDir, \".gnupg\"), // GPG keys\n ],\n },\n };\n }\n\n const config = loadPermissions();\n const record = config.trustedApps[appId];\n\n // If permission already granted as \"always\", return cached config\n if (record?.choice === \"always\" && record.sandboxConfig) {\n return record.sandboxConfig;\n }\n\n // Request new sandbox permissions\n const appName = getAppName(appId);\n return await requestSandboxConfig(appId, appName);\n}\n\n/**\n * Clear all permissions\n */\nexport function clearAllPermissions(): void {\n savePermissions({ trustedApps: {} });\n}\n","import { confirm, output, select, table } from \"../ui\";\nimport { clearAllPermissions, listPermissions, revokePermission } from \".\";\n\n/**\n * Permissions management CLI\n */\nexport async function permissionsCommand(): Promise<void> {\n const action = await select({\n prompt: \"Permissions Management\",\n options: [\n {\n name: \"List permissions\",\n value: \"list\",\n description: \"View all granted permissions\",\n },\n {\n name: \"Revoke permission\",\n value: \"revoke\",\n description: \"Remove permission for a specific app\",\n },\n {\n name: \"Clear all\",\n value: \"clear\",\n description: \"Remove all permissions\",\n },\n ],\n });\n\n switch (action) {\n case \"list\":\n await listPermissionsAction();\n break;\n case \"revoke\":\n await revokePermissionAction();\n break;\n case \"clear\":\n await clearAllPermissionsAction();\n break;\n }\n}\n\n/**\n * List all permissions\n */\nasync function listPermissionsAction(): Promise<void> {\n const permissions = listPermissions();\n\n if (permissions.length === 0) {\n output(\"\\nNo permissions granted yet.\\n\");\n return;\n }\n\n output(\"\\n📋 Granted Permissions:\\n\");\n\n table({\n columns: [\n { key: \"app\", header: \"App\" },\n { key: \"grantedAt\", header: \"Granted At\" },\n ],\n rows: permissions.map((p) => ({\n app: p.appName,\n grantedAt: new Date(p.timestamp).toLocaleString(),\n })),\n });\n\n output(\"\");\n}\n\n/**\n * Revoke permission for a specific app\n */\nasync function revokePermissionAction(): Promise<void> {\n const permissions = listPermissions();\n\n if (permissions.length === 0) {\n output(\"\\nNo permissions to revoke.\\n\");\n return;\n }\n\n const appId = await select({\n prompt: \"Select app to revoke permission:\",\n options: permissions.map((p) => ({\n name: p.appName,\n value: p.appId,\n description: \"Always allowed\",\n })),\n });\n\n const confirmed = await confirm(\n \"Are you sure you want to revoke this permission?\",\n );\n\n if (confirmed) {\n revokePermission(appId);\n output(\"\\n✅ Permission revoked.\\n\");\n } else {\n output(\"\\n❌ Cancelled.\\n\");\n }\n}\n\n/**\n * Clear all permissions\n */\nasync function clearAllPermissionsAction(): Promise<void> {\n const confirmed = await confirm(\n \"Are you sure you want to clear ALL permissions?\",\n );\n\n if (confirmed) {\n clearAllPermissions();\n output(\"\\n✅ All permissions cleared.\\n\");\n } else {\n output(\"\\n❌ Cancelled.\\n\");\n }\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { LLM_API_DOMAINS } from \"../shared/constants\";\nimport type { AppPermissions } from \"../types\";\n\n/**\n * Always protected paths (never allow write, some deny read)\n */\nconst PROTECTED_PATHS = {\n alwaysDenyWrite: [\n join(homedir(), \".kly\"), // KLY config and permissions\n join(homedir(), \".ssh\"), // SSH keys\n join(homedir(), \".aws\"), // AWS credentials\n join(homedir(), \".gnupg\"), // GPG keys\n ],\n alwaysDenyRead: [\n join(homedir(), \".kly\"), // KLY config (prevent reading permissions.json)\n ],\n};\n\n/**\n * Resolve filesystem path with special marker support\n *\n * Special markers:\n * - \"*\": User's home directory (allows access to all non-sensitive files)\n * - Absolute paths: Used as-is\n *\n * @param path - Path to resolve (may contain special markers)\n * @returns Resolved absolute path, or undefined if path is undefined\n */\nfunction resolveFilesystemPath(path: string | undefined): string | undefined {\n if (!path) return undefined;\n if (path === \"*\") {\n // Allow access to user's home directory (sensitive paths are always protected)\n return homedir();\n }\n return path;\n}\n\n/**\n * Build a complete SandboxRuntimeConfig from declared app permissions\n *\n * This merges:\n * 1. Default safe configuration\n * 2. Automatic LLM domains (if apiKeys: true)\n * 3. User-declared sandbox config (with special marker support)\n * 4. Mandatory protections (always applied)\n *\n * Special markers in filesystem paths:\n * - \"*\": Allows access to all files in user's home directory (except sensitive paths)\n *\n * @param permissions - Declared app permissions\n * @returns Complete sandbox configuration ready for SandboxManager\n */\nexport function buildSandboxConfig(\n permissions: AppPermissions | undefined,\n): SandboxRuntimeConfig {\n const currentDir = process.cwd();\n\n // Start with defaults\n let allowedDomains: string[] = [];\n let allowWrite: string[] = [currentDir]; // Allow current directory by default\n let denyRead: string[] = [...PROTECTED_PATHS.alwaysDenyRead];\n\n // If apiKeys requested, add LLM domains automatically\n if (permissions?.apiKeys) {\n allowedDomains = [...LLM_API_DOMAINS];\n }\n\n // Merge user-declared sandbox config\n if (permissions?.sandbox) {\n const userSandbox = permissions.sandbox;\n\n // Merge network config\n if (userSandbox.network?.allowedDomains) {\n const domains = userSandbox.network.allowedDomains.filter(\n (d): d is string => d !== undefined,\n );\n allowedDomains = [...allowedDomains, ...domains];\n }\n\n // Merge filesystem config\n if (userSandbox.filesystem) {\n if (userSandbox.filesystem.allowWrite) {\n // Replace default with user's choice, resolving special markers\n allowWrite = userSandbox.filesystem.allowWrite\n .map(resolveFilesystemPath)\n .filter((p): p is string => p !== undefined);\n }\n\n if (userSandbox.filesystem.denyRead) {\n // Add user's deny list, resolving special markers\n const deniedPaths = userSandbox.filesystem.denyRead\n .map(resolveFilesystemPath)\n .filter((p): p is string => p !== undefined);\n denyRead = [...denyRead, ...deniedPaths];\n }\n }\n }\n\n // Build final config\n const config: SandboxRuntimeConfig = {\n network: {\n allowedDomains,\n deniedDomains: [],\n },\n filesystem: {\n denyRead,\n allowWrite,\n denyWrite: PROTECTED_PATHS.alwaysDenyWrite, // Always protected\n },\n };\n\n return config;\n}\n\n/**\n * Get a human-readable summary of permissions for display\n * Only shows special/non-default permissions\n */\nexport function formatPermissionsSummary(\n permissions: AppPermissions | undefined,\n): string[] {\n const summary: string[] = [];\n\n // Always show API Keys if requested (special permission)\n if (permissions?.apiKeys) {\n summary.push(\"• API Keys access (to call LLM APIs)\");\n }\n\n const config = buildSandboxConfig(permissions);\n const currentDir = process.cwd();\n\n // Network - only show if non-default (not empty)\n if (config.network.allowedDomains.length > 0) {\n if (config.network.allowedDomains.includes(\"*\")) {\n summary.push(\"• Network: All domains\");\n } else {\n const domains = config.network.allowedDomains.slice(0, 3).join(\", \");\n const more =\n config.network.allowedDomains.length > 3\n ? ` +${config.network.allowedDomains.length - 3} more`\n : \"\";\n summary.push(`• Network: ${domains}${more}`);\n }\n }\n\n // Filesystem - only show if custom (not just current directory)\n const hasCustomWrite =\n config.filesystem.allowWrite.length > 1 ||\n (config.filesystem.allowWrite.length === 1 &&\n config.filesystem.allowWrite[0] !== currentDir);\n\n if (hasCustomWrite) {\n // Check if home directory is allowed (via \"*\" marker)\n const homeDir = homedir();\n const allowsHomeDir = config.filesystem.allowWrite.includes(homeDir);\n\n if (allowsHomeDir) {\n summary.push(\"• Filesystem write: All non-sensitive directories\");\n } else {\n const dirs = config.filesystem.allowWrite\n .map((p) => (p === currentDir ? \"current directory\" : p))\n .slice(0, 2)\n .join(\", \");\n const more =\n config.filesystem.allowWrite.length > 2\n ? ` +${config.filesystem.allowWrite.length - 2} more`\n : \"\";\n summary.push(`• Filesystem write: ${dirs}${more}`);\n }\n }\n\n // Show custom filesystem read restrictions if declared\n if (permissions?.sandbox?.filesystem?.denyRead) {\n summary.push(\n `• Filesystem read denied: ${permissions.sandbox.filesystem.denyRead.length} path(s)`,\n );\n }\n\n return summary;\n}\n","import { ENV_VARS } from \"../shared/constants\";\nimport type { AppPermissions } from \"../types\";\n\n/**\n * Extract permissions from a user's app script\n * This runs in the host process BEFORE launching the sandbox\n *\n * @param scriptPath - Absolute path to the user's script\n * @returns Declared permissions or undefined if not specified\n */\nexport async function extractAppPermissions(\n scriptPath: string,\n): Promise<AppPermissions | undefined> {\n try {\n // Set environment to prevent auto-execution\n const prevMode = process.env[ENV_VARS.PROGRAMMATIC];\n process.env[ENV_VARS.PROGRAMMATIC] = \"true\";\n\n // Import the script (will call defineApp but not auto-run)\n const module = await import(scriptPath);\n\n // Restore environment\n if (prevMode === undefined) {\n delete process.env[ENV_VARS.PROGRAMMATIC];\n } else {\n process.env[ENV_VARS.PROGRAMMATIC] = prevMode;\n }\n\n // Get the app instance\n const app = module.default;\n\n if (!app || !app.definition) {\n return undefined;\n }\n\n // Return declared permissions\n return app.definition.permissions;\n } catch (error) {\n // If extraction fails, we'll ask for permissions interactively\n console.warn(\n `Warning: Could not extract permissions from ${scriptPath}:`,\n error instanceof Error ? error.message : String(error),\n );\n return undefined;\n }\n}\n","import type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { isTrustAll } from \"../shared/runtime-mode\";\nimport type { AppPermissions } from \"../types\";\nimport { select } from \"../ui\";\nimport { isTTY } from \"../ui/utils/tty\";\nimport { formatPermissionsSummary } from \"./config-builder\";\nimport { getAppName, loadPermissions, savePermissions } from \"./index\";\n\n/**\n * Check if permissions require user prompt\n * Only prompt for special permissions (API keys, custom network/filesystem)\n * Default permissions (current directory write, no network) are auto-granted\n */\nfunction needsPermissionPrompt(\n permissions: AppPermissions | undefined,\n sandboxConfig: SandboxRuntimeConfig,\n): boolean {\n // If API keys requested, always prompt\n if (permissions?.apiKeys) {\n return true;\n }\n\n // If custom network access requested (not empty), prompt\n if (sandboxConfig.network.allowedDomains.length > 0) {\n return true;\n }\n\n // If custom filesystem permissions declared, prompt\n if (permissions?.sandbox?.filesystem) {\n return true;\n }\n\n // Otherwise, use default permissions silently\n return false;\n}\n\n/**\n * Request permission with a single unified prompt\n * Shows all requested permissions at once\n *\n * @param appId - App identifier\n * @param appPermissions - Declared permissions from app\n * @param sandboxConfig - Generated sandbox configuration\n * @returns true if allowed, false if cancelled\n */\nexport async function requestUnifiedPermission(\n appId: string,\n appPermissions: AppPermissions | undefined,\n sandboxConfig: SandboxRuntimeConfig,\n): Promise<boolean> {\n // Check for bypass\n if (isTrustAll()) {\n return true;\n }\n\n // Check stored permissions\n const config = loadPermissions();\n const record = config.trustedApps[appId];\n\n if (record && record.choice === \"always\") {\n // Already granted\n return true;\n }\n\n // Check if this app needs permission prompt\n // Default permissions (current dir write, no network) don't need prompt\n if (!needsPermissionPrompt(appPermissions, sandboxConfig)) {\n return true; // Auto-grant default permissions\n }\n\n // Check if running in TTY mode\n if (!isTTY()) {\n const appName = getAppName(appId);\n console.error(\n `\\nPermission required: App \"${appName}\" (${appId}) requests permissions.`,\n );\n console.error(\n \"Set KLY_TRUST_ALL=true environment variable to grant access in non-interactive mode.\",\n );\n return false;\n }\n\n // Show unified prompt\n const appName = getAppName(appId);\n console.log(\"\");\n console.log(`App \"${appName}\" requests the following permissions:`);\n console.log(\"\");\n\n // Format and display permissions\n const summary = formatPermissionsSummary(appPermissions);\n for (const line of summary) {\n console.log(` ${line}`);\n }\n\n console.log(\"\");\n console.log(`Source: ${appId}`);\n console.log(\"\");\n\n // Ask user\n const choice = await select({\n prompt: \"Do you want to allow this?\",\n options: [\n {\n name: \"Allow once\",\n value: \"once\",\n description: \"Allow for this session only\",\n },\n {\n name: \"Always allow\",\n value: \"always\",\n description: \"Remember this choice for future runs\",\n },\n {\n name: \"Cancel\",\n value: \"cancel\",\n description: \"Cancel and exit\",\n },\n ],\n });\n\n // Handle choice\n if (choice === \"cancel\") {\n return false;\n }\n\n if (choice === \"always\") {\n // Save permission\n config.trustedApps[appId] = {\n timestamp: new Date().toISOString(),\n choice: \"always\",\n sandboxConfig,\n };\n savePermissions(config);\n }\n\n // \"once\" or \"always\" - both allow execution\n return true;\n}\n\n/**\n * Check if app needs permission check\n * Returns stored sandbox config if \"always\" was granted\n */\nexport function checkStoredPermission(\n appId: string,\n): SandboxRuntimeConfig | null {\n if (isTrustAll()) {\n return null; // Will use default config\n }\n\n const config = loadPermissions();\n const record = config.trustedApps[appId];\n\n if (record?.choice === \"always\" && record.sandboxConfig) {\n return record.sandboxConfig;\n }\n\n return null;\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { RepoRef } from \"./types\";\n\n/**\n * Parse various remote formats into RepoRef\n *\n * Supported formats:\n * - user/repo\n * - user/repo@v1.0.0\n * - user/repo@branch\n * - github.com/user/repo\n * - github.com/user/repo@ref\n * - https://github.com/user/repo\n */\nexport function parseRemoteRef(input: string): RepoRef | null {\n let normalized = input.trim();\n\n // Remove https:// or http:// prefix\n normalized = normalized.replace(/^https?:\\/\\//, \"\");\n\n // Remove github.com/ prefix\n normalized = normalized.replace(/^github\\.com\\//, \"\");\n\n // Extract ref if present (user/repo@ref or user/repo.git@ref)\n let ref = \"main\";\n const atIndex = normalized.indexOf(\"@\");\n if (atIndex !== -1) {\n ref = normalized.slice(atIndex + 1);\n normalized = normalized.slice(0, atIndex);\n }\n\n // Remove trailing .git (after @ extraction)\n normalized = normalized.replace(/\\.git$/, \"\");\n\n // Parse owner/repo\n const parts = normalized.split(\"/\");\n if (parts.length !== 2) {\n return null;\n }\n\n const [owner, repo] = parts;\n\n // Validate owner and repo names\n if (\n !owner ||\n !repo ||\n !isValidGitHubName(owner) ||\n !isValidGitHubName(repo)\n ) {\n return null;\n }\n\n return { owner, repo, ref };\n}\n\n/**\n * Check if a string is a valid GitHub username or repo name\n */\nfunction isValidGitHubName(name: string): boolean {\n // GitHub names: alphanumeric, hyphens, no consecutive hyphens, no start/end with hyphen\n return (\n /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(name) ||\n /^[a-zA-Z0-9]$/.test(name)\n );\n}\n\n/**\n * Get the kly cache directory\n */\nexport function getCacheDir(): string {\n return join(homedir(), \".kly\", \"cache\");\n}\n\n/**\n * Get the cache path for a specific repo ref\n */\nexport function getRepoCachePath(ref: RepoRef): string {\n return join(getCacheDir(), \"github.com\", ref.owner, ref.repo, ref.ref);\n}\n\n/**\n * Check if an input looks like a remote reference (vs local file path)\n */\nexport function isRemoteRef(input: string): boolean {\n // Local paths start with ./ ../ / or contain backslash (Windows)\n if (\n input.startsWith(\"./\") ||\n input.startsWith(\"../\") ||\n input.startsWith(\"/\") ||\n input.includes(\"\\\\\")\n ) {\n return false;\n }\n\n // Check if it parses as a valid remote ref\n return parseRemoteRef(input) !== null;\n}\n","import {\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { getRepoCachePath } from \"./parser\";\nimport type { CacheCheckResult, CacheMetadata, RepoRef } from \"./types\";\n\nconst META_FILENAME = \".kly-meta.json\";\n\n/**\n * Check if cache exists and is valid\n */\nexport function checkCache(ref: RepoRef): CacheCheckResult {\n const cachePath = getRepoCachePath(ref);\n const metaPath = join(cachePath, META_FILENAME);\n\n if (!existsSync(cachePath)) {\n return {\n exists: false,\n valid: false,\n reason: \"Cache directory does not exist\",\n };\n }\n\n if (!existsSync(metaPath)) {\n return { exists: true, valid: false, reason: \"Cache metadata missing\" };\n }\n\n try {\n const metadata = JSON.parse(\n readFileSync(metaPath, \"utf-8\"),\n ) as CacheMetadata;\n\n // Check if entry point still exists\n const entryPath = join(cachePath, metadata.entryPoint);\n if (!existsSync(entryPath)) {\n return {\n exists: true,\n valid: false,\n metadata,\n reason: \"Entry point file missing\",\n };\n }\n\n // Check if dependencies were installed\n if (!metadata.dependenciesInstalled) {\n return {\n exists: true,\n valid: false,\n metadata,\n reason: \"Dependencies not installed\",\n };\n }\n\n return { exists: true, valid: true, metadata };\n } catch {\n return { exists: true, valid: false, reason: \"Invalid cache metadata\" };\n }\n}\n\n/**\n * Read cache metadata\n */\nexport function readMetadata(ref: RepoRef): CacheMetadata | null {\n const cachePath = getRepoCachePath(ref);\n const metaPath = join(cachePath, META_FILENAME);\n\n if (!existsSync(metaPath)) {\n return null;\n }\n\n try {\n return JSON.parse(readFileSync(metaPath, \"utf-8\")) as CacheMetadata;\n } catch {\n return null;\n }\n}\n\n/**\n * Write cache metadata\n */\nexport function writeMetadata(ref: RepoRef, metadata: CacheMetadata): void {\n const cachePath = getRepoCachePath(ref);\n const metaPath = join(cachePath, META_FILENAME);\n\n mkdirSync(dirname(metaPath), { recursive: true });\n writeFileSync(metaPath, JSON.stringify(metadata, null, 2));\n}\n\n/**\n * Remove cached repository\n */\nexport function invalidateCache(ref: RepoRef): void {\n const cachePath = getRepoCachePath(ref);\n\n if (existsSync(cachePath)) {\n rmSync(cachePath, { recursive: true, force: true });\n }\n}\n\n/**\n * Clear all cache\n */\nexport function clearAllCache(): void {\n const { getCacheDir } = require(\"./parser\");\n const cacheDir = getCacheDir();\n\n if (existsSync(cacheDir)) {\n rmSync(cacheDir, { recursive: true, force: true });\n }\n}\n","import { exec } from \"node:child_process\";\nimport { existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { getRepoCachePath } from \"./parser\";\nimport type { RepoRef } from \"./types\";\n\nconst execAsync = promisify(exec);\n\n/**\n * Clone a repository to cache\n */\nexport async function cloneRepo(ref: RepoRef): Promise<void> {\n const repoUrl = `https://github.com/${ref.owner}/${ref.repo}.git`;\n const targetPath = getRepoCachePath(ref);\n\n // Remove existing cache if any\n if (existsSync(targetPath)) {\n rmSync(targetPath, { recursive: true, force: true });\n }\n\n // Ensure parent directory exists\n mkdirSync(dirname(targetPath), { recursive: true });\n\n // Shallow clone specific ref\n try {\n await execAsync(\n `git clone --depth 1 --branch ${ref.ref} ${repoUrl} \"${targetPath}\"`,\n {\n timeout: 60000, // 60s timeout\n },\n );\n } catch (error) {\n // If branch clone fails, try without --branch (for default branch)\n if (ref.ref === \"main\") {\n try {\n await execAsync(`git clone --depth 1 ${repoUrl} \"${targetPath}\"`, {\n timeout: 60000,\n });\n return;\n } catch {\n // Fall through to original error\n }\n }\n throw new Error(\n `Failed to clone ${ref.owner}/${ref.repo}@${ref.ref}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Install dependencies using bun\n */\nexport async function installDependencies(repoPath: string): Promise<void> {\n const pkgPath = `${repoPath}/package.json`;\n\n if (!existsSync(pkgPath)) {\n // No package.json, skip install\n return;\n }\n\n try {\n await execAsync(\"bun install\", {\n cwd: repoPath,\n timeout: 120000, // 120s timeout\n });\n } catch (error) {\n throw new Error(\n `Failed to install dependencies: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Get the commit SHA of a cloned repo\n */\nexport async function getCommitSha(repoPath: string): Promise<string> {\n try {\n const { stdout } = await execAsync(\"git rev-parse HEAD\", {\n cwd: repoPath,\n timeout: 5000,\n });\n return stdout.trim();\n } catch {\n return \"unknown\";\n }\n}\n","import { createHash } from \"node:crypto\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n type Stats,\n statSync,\n} from \"node:fs\";\nimport { join, relative } from \"node:path\";\n\n/**\n * Directories and files to ignore when calculating repository hash\n */\nconst IGNORE_PATTERNS = [\n \".git\",\n \"node_modules\",\n \"dist\",\n \"build\",\n \"coverage\",\n \".kly\",\n \".kly-meta.json\",\n \".DS_Store\",\n \"*.log\",\n];\n\n/**\n * File extensions to include in hash calculation\n */\nconst SOURCE_EXTENSIONS = [\n \".ts\",\n \".js\",\n \".tsx\",\n \".jsx\",\n \".json\",\n \".md\",\n \".txt\",\n];\n\n/**\n * Calculate integrity hash for a cloned repository\n * Uses SHA-384 (consistent with browser Subresource Integrity)\n *\n * Hash includes:\n * - All source code files (sorted by path)\n * - File paths (for structure verification)\n * - Lock file (bun.lockb) if present\n *\n * @param repoPath - Absolute path to the repository\n * @param algorithm - Hash algorithm (default: sha384)\n * @returns Hash in format \"sha384-base64...\"\n */\nexport function calculateRepoHash(\n repoPath: string,\n algorithm: \"sha256\" | \"sha384\" | \"sha512\" = \"sha384\",\n): string {\n const hash = createHash(algorithm);\n\n // 1. Collect all source files\n const files = collectSourceFiles(repoPath);\n\n // 2. Sort by relative path (ensures consistent ordering)\n files.sort();\n\n // 3. Hash each file (path + content)\n for (const file of files) {\n const relativePath = relative(repoPath, file);\n const content = readFileSync(file);\n\n // Include file path in hash (detects file moves/renames)\n hash.update(`FILE:${relativePath}\\n`);\n hash.update(content);\n hash.update(\"\\n\");\n }\n\n // 4. Include lock file if present (dependency integrity)\n const lockFile = join(repoPath, \"bun.lockb\");\n if (existsSync(lockFile)) {\n hash.update(\"LOCK:bun.lockb\\n\");\n hash.update(readFileSync(lockFile));\n hash.update(\"\\n\");\n }\n\n // 5. Generate base64 digest\n const digest = hash.digest(\"base64\");\n return `${algorithm}-${digest}`;\n}\n\n/**\n * Recursively collect all source files in a directory\n *\n * @param dir - Directory to scan\n * @param results - Accumulator for file paths\n * @returns Array of absolute file paths\n */\nfunction collectSourceFiles(dir: string, results: string[] = []): string[] {\n if (!existsSync(dir)) {\n return results;\n }\n\n try {\n const entries = readdirSync(dir);\n\n for (const entry of entries) {\n // Skip ignored patterns\n if (shouldIgnore(entry)) {\n continue;\n }\n\n const fullPath = join(dir, entry);\n let stat: Stats;\n\n try {\n stat = statSync(fullPath);\n } catch {\n // Skip files we can't stat (permission issues, symlinks, etc.)\n continue;\n }\n\n if (stat.isDirectory()) {\n collectSourceFiles(fullPath, results);\n } else if (stat.isFile() && shouldIncludeFile(entry)) {\n results.push(fullPath);\n }\n }\n } catch {\n // Skip directories we can't read\n }\n\n return results;\n}\n\n/**\n * Check if a file/directory should be ignored\n */\nfunction shouldIgnore(name: string): boolean {\n for (const pattern of IGNORE_PATTERNS) {\n if (pattern.startsWith(\"*\")) {\n // Wildcard pattern (e.g., *.log)\n const ext = pattern.slice(1);\n if (name.endsWith(ext)) {\n return true;\n }\n } else if (name === pattern) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a file should be included in hash calculation\n */\nfunction shouldIncludeFile(name: string): boolean {\n return SOURCE_EXTENSIONS.some((ext) => name.endsWith(ext));\n}\n\n/**\n * Parse a hash string into its components\n *\n * @param hashString - Hash in format \"sha384-base64...\"\n * @returns Parsed components or null if invalid\n */\nexport function parseHashString(\n hashString: string,\n): { algorithm: string; digest: string } | null {\n const match = hashString.match(/^(sha256|sha384|sha512)-(.+)$/);\n if (!match) {\n return null;\n }\n\n return {\n algorithm: match[1] as string,\n digest: match[2] as string,\n };\n}\n\n/**\n * Compare two hash strings for equality\n *\n * @param hash1 - First hash\n * @param hash2 - Second hash\n * @returns true if hashes match\n */\nexport function compareHashes(hash1: string, hash2: string): boolean {\n const parsed1 = parseHashString(hash1);\n const parsed2 = parseHashString(hash2);\n\n if (!parsed1 || !parsed2) {\n return false;\n }\n\n // Must use same algorithm\n if (parsed1.algorithm !== parsed2.algorithm) {\n return false;\n }\n\n return parsed1.digest === parsed2.digest;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { KlyConfig } from \"./types\";\n\n/**\n * Entry point candidates to search for (in order)\n */\nconst ENTRY_CANDIDATES = [\n \"index.ts\",\n \"main.ts\",\n \"src/index.ts\",\n \"src/main.ts\",\n \"app.ts\",\n];\n\n/**\n * Resolve entry point for a kly app\n * Priority: main field > convention candidates\n */\nexport function resolveEntryPoint(repoPath: string): string | null {\n const pkgPath = join(repoPath, \"package.json\");\n\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\n // Check main field (standard npm field)\n if (pkg.main && (pkg.main.endsWith(\".ts\") || pkg.main.endsWith(\".js\"))) {\n const mainPath = join(repoPath, pkg.main);\n if (existsSync(mainPath)) {\n return pkg.main; // Return relative path\n }\n }\n } catch {\n // Invalid package.json, continue to candidates\n }\n }\n\n // Try convention candidates\n for (const candidate of ENTRY_CANDIDATES) {\n const candidatePath = join(repoPath, candidate);\n if (existsSync(candidatePath)) {\n return candidate; // Return relative path\n }\n }\n\n return null;\n}\n\n/**\n * Read kly configuration from package.json\n */\nexport function readKlyConfig(repoPath: string): KlyConfig | null {\n const pkgPath = join(repoPath, \"package.json\");\n\n if (!existsSync(pkgPath)) {\n return null;\n }\n\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.kly ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Check if current kly version satisfies the required version\n * Simple semver check (supports >=x.y.z format)\n */\nexport function validateVersion(required: string, current: string): boolean {\n // Parse >=x.y.z format\n const reqMatch = required.match(/^>=?\\s*(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!reqMatch) {\n // Unknown format, skip validation\n return true;\n }\n\n const curMatch = current.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!curMatch) {\n return true;\n }\n\n const reqMajor = Number(reqMatch[1]);\n const reqMinor = Number(reqMatch[2]);\n const reqPatch = Number(reqMatch[3]);\n const curMajor = Number(curMatch[1]);\n const curMinor = Number(curMatch[2]);\n const curPatch = Number(curMatch[3]);\n\n // Compare versions\n if (curMajor > reqMajor) return true;\n if (curMajor < reqMajor) return false;\n if (curMinor > reqMinor) return true;\n if (curMinor < reqMinor) return false;\n return curPatch >= reqPatch;\n}\n\n/**\n * Check required environment variables\n * Returns list of missing variables\n */\nexport function checkEnvVars(required: string[]): string[] {\n return required.filter((name) => !process.env[name]);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Sum file entry representing a trusted code hash\n */\nexport interface SumEntry {\n /** Full URL: github.com/owner/repo@ref */\n url: string;\n /** Integrity hash: sha384-base64... */\n hash: string;\n /** Unix timestamp when first trusted */\n timestamp: number;\n /** Whether user explicitly trusted this code */\n trusted: boolean;\n}\n\n/**\n * Verification result\n */\nexport type VerifyResult = \"ok\" | \"mismatch\" | \"new\";\n\n/**\n * Get the path to kly.sum file\n */\nexport function getSumFilePath(): string {\n return join(homedir(), \".kly\", \"kly.sum\");\n}\n\n/**\n * Manager for kly.sum file (integrity verification database)\n *\n * File format (one entry per line):\n * github.com/owner/repo@ref sha384-hash timestamp trusted|untrusted\n *\n * Example:\n * github.com/jack/weather@v1.0.0 sha384-oqVuAfXRKap7fdgc... 1704067200 trusted\n */\nexport class SumFileManager {\n private entries: Map<string, SumEntry> = new Map();\n private sumFilePath: string;\n private dirty = false;\n\n constructor(sumFilePath?: string) {\n this.sumFilePath = sumFilePath || getSumFilePath();\n this.load();\n }\n\n /**\n * Load entries from kly.sum file\n */\n private load(): void {\n if (!existsSync(this.sumFilePath)) {\n return;\n }\n\n try {\n const content = readFileSync(this.sumFilePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n\n const entry = this.parseLine(trimmed);\n if (entry) {\n this.entries.set(entry.url, entry);\n }\n }\n } catch (error) {\n console.warn(`Warning: Failed to load kly.sum: ${error}`);\n }\n }\n\n /**\n * Parse a single line from kly.sum\n */\n private parseLine(line: string): SumEntry | null {\n // Format: url hash timestamp trusted|untrusted\n const parts = line.split(/\\s+/);\n\n if (parts.length < 4) {\n return null;\n }\n\n const [url, hash, timestampStr, trustedStr] = parts;\n\n if (!url || !hash || !timestampStr || !trustedStr) {\n return null;\n }\n\n const timestamp = Number(timestampStr);\n if (Number.isNaN(timestamp)) {\n return null;\n }\n\n return {\n url,\n hash,\n timestamp,\n trusted: trustedStr === \"trusted\",\n };\n }\n\n /**\n * Format an entry for writing to file\n */\n private formatEntry(entry: SumEntry): string {\n return `${entry.url} ${entry.hash} ${entry.timestamp} ${entry.trusted ? \"trusted\" : \"untrusted\"}`;\n }\n\n /**\n * Save entries to kly.sum file\n */\n private save(): void {\n if (!this.dirty) {\n return;\n }\n\n try {\n // Ensure directory exists\n const dir = dirname(this.sumFilePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Sort entries by URL for consistent formatting\n const sortedEntries = Array.from(this.entries.values()).sort((a, b) =>\n a.url.localeCompare(b.url),\n );\n\n // Build file content\n const lines = [\n \"# kly.sum - Integrity verification database\",\n \"# Format: url hash timestamp trusted|untrusted\",\n \"# DO NOT EDIT THIS FILE MANUALLY\",\n \"\",\n ...sortedEntries.map((entry) => this.formatEntry(entry)),\n ];\n\n writeFileSync(this.sumFilePath, `${lines.join(\"\\n\")}\\n`, \"utf-8\");\n this.dirty = false;\n } catch (error) {\n console.error(`Error: Failed to save kly.sum: ${error}`);\n throw error;\n }\n }\n\n /**\n * Verify a repository's integrity hash\n *\n * @param url - Full URL (e.g., \"github.com/owner/repo@ref\")\n * @param hash - Calculated hash to verify\n * @returns \"ok\" if matches, \"mismatch\" if different, \"new\" if first time\n */\n public verify(url: string, hash: string): VerifyResult {\n const entry = this.entries.get(url);\n\n if (!entry) {\n return \"new\";\n }\n\n if (entry.hash === hash) {\n return \"ok\";\n }\n\n return \"mismatch\";\n }\n\n /**\n * Add or update an entry in kly.sum\n *\n * @param url - Full URL\n * @param hash - Integrity hash\n * @param trusted - Whether user explicitly trusted this code\n */\n public add(url: string, hash: string, trusted = false): void {\n this.entries.set(url, {\n url,\n hash,\n timestamp: Math.floor(Date.now() / 1000),\n trusted,\n });\n\n this.dirty = true;\n this.save();\n }\n\n /**\n * Update an existing entry's hash (e.g., user approved new version)\n *\n * @param url - Full URL\n * @param hash - New hash\n * @param trusted - Whether user explicitly trusted this update\n */\n public update(url: string, hash: string, trusted = false): void {\n const existing = this.entries.get(url);\n\n this.entries.set(url, {\n url,\n hash,\n timestamp: existing?.timestamp ?? Math.floor(Date.now() / 1000),\n trusted,\n });\n\n this.dirty = true;\n this.save();\n }\n\n /**\n * Remove an entry from kly.sum\n *\n * @param url - Full URL to remove\n * @returns true if removed, false if not found\n */\n public remove(url: string): boolean {\n const existed = this.entries.delete(url);\n\n if (existed) {\n this.dirty = true;\n this.save();\n }\n\n return existed;\n }\n\n /**\n * Get an entry by URL\n *\n * @param url - Full URL\n * @returns Entry if found, undefined otherwise\n */\n public get(url: string): SumEntry | undefined {\n return this.entries.get(url);\n }\n\n /**\n * Get all entries\n *\n * @returns Array of all sum entries\n */\n public getAll(): SumEntry[] {\n return Array.from(this.entries.values());\n }\n\n /**\n * Clear all entries (for testing or reset)\n */\n public clear(): void {\n this.entries.clear();\n this.dirty = true;\n this.save();\n }\n\n /**\n * Get statistics about the sum file\n */\n public getStats(): {\n total: number;\n trusted: number;\n untrusted: number;\n } {\n const entries = Array.from(this.entries.values());\n\n return {\n total: entries.length,\n trusted: entries.filter((e) => e.trusted).length,\n untrusted: entries.filter((e) => !e.trusted).length,\n };\n }\n}\n","import { join } from \"node:path\";\nimport { ENV_VARS } from \"../shared/constants\";\nimport { confirm } from \"../ui\";\nimport { checkCache, invalidateCache, writeMetadata } from \"./cache\";\nimport { cloneRepo, getCommitSha, installDependencies } from \"./fetcher\";\nimport { calculateRepoHash } from \"./integrity\";\nimport { getRepoCachePath, parseRemoteRef } from \"./parser\";\nimport {\n checkEnvVars,\n readKlyConfig,\n resolveEntryPoint,\n validateVersion,\n} from \"./resolver\";\nimport { SumFileManager } from \"./sumfile\";\nimport type { IntegrityCheckResult, RepoRef, RunRemoteOptions } from \"./types\";\n\n/** Current kly CLI version */\nconst KLY_VERSION = \"0.1.0\";\n\n/**\n * Run a remote GitHub repository as a kly app\n */\nexport async function runRemote(\n input: string,\n options: RunRemoteOptions = {},\n): Promise<void> {\n // 1. Parse input\n const ref = parseRemoteRef(input);\n if (!ref) {\n throw new Error(`Invalid remote reference: ${input}`);\n }\n\n const repoPath = getRepoCachePath(ref);\n\n // 2. Check cache\n const cacheResult = checkCache(ref);\n\n if (!cacheResult.valid || options.force) {\n // 3. Clone repository\n if (options.force && cacheResult.exists) {\n console.log(`Refreshing ${ref.owner}/${ref.repo}@${ref.ref}...`);\n invalidateCache(ref);\n } else {\n console.log(`Fetching ${ref.owner}/${ref.repo}@${ref.ref}...`);\n }\n\n await cloneRepo(ref);\n\n // 4. Resolve entry point\n const entryPoint = resolveEntryPoint(repoPath);\n if (!entryPoint) {\n throw new Error(\n `No entry point found in ${ref.owner}/${ref.repo}. Set \"main\" in package.json or create index.ts`,\n );\n }\n\n // 5. Install dependencies\n if (!options.skipInstall) {\n console.log(\"Installing dependencies...\");\n await installDependencies(repoPath);\n }\n\n // 6. Write metadata\n const commitSha = await getCommitSha(repoPath);\n writeMetadata(ref, {\n commitSha,\n cachedAt: new Date().toISOString(),\n entryPoint,\n dependenciesInstalled: !options.skipInstall,\n });\n\n console.log(\"Ready!\\n\");\n }\n\n // 7. Integrity verification\n if (!options.skipIntegrityCheck) {\n const integrityResult = await verifyIntegrity(ref, repoPath);\n\n if (!integrityResult.proceedWithExecution) {\n console.error(\n \"\\n❌ Execution cancelled due to integrity verification failure\",\n );\n process.exit(1);\n }\n }\n\n // 8. Validate and execute\n await executeApp(ref, repoPath, options.args ?? [], options.mcp ?? false);\n}\n\n/**\n * Verify repository integrity using kly.sum\n *\n * @param ref - Repository reference\n * @param repoPath - Local path to repository\n * @returns Object with integrity check result and whether to proceed with execution\n */\nasync function verifyIntegrity(\n ref: RepoRef,\n repoPath: string,\n): Promise<{ proceedWithExecution: boolean; result: IntegrityCheckResult }> {\n const url = `github.com/${ref.owner}/${ref.repo}@${ref.ref}`;\n\n console.log(\"\\n🔐 Verifying code integrity...\");\n\n // Calculate repository hash\n const hash = calculateRepoHash(repoPath);\n console.log(` Hash: ${hash.slice(0, 20)}...`);\n\n // Check against kly.sum\n const sumManager = new SumFileManager();\n const verifyResult = sumManager.verify(url, hash);\n\n const result: IntegrityCheckResult = {\n status: verifyResult,\n hash,\n requiresTrust: verifyResult !== \"ok\",\n };\n\n switch (verifyResult) {\n case \"ok\": {\n // Hash matches - code is trusted\n console.log(\" ✓ Integrity verified\\n\");\n return { proceedWithExecution: true, result };\n }\n\n case \"new\": {\n // First time running this version\n console.log(\"\\n⚠️ SECURITY NOTICE: First time running this tool\\n\");\n console.log(\" This code has not been verified before.\");\n console.log(\" Please review the source code before proceeding:\");\n console.log(\n ` https://github.com/${ref.owner}/${ref.repo}/tree/${ref.ref}\\n`,\n );\n\n const shouldTrust = await confirm(\n \"Do you trust this code and want to proceed?\",\n );\n\n if (shouldTrust) {\n sumManager.add(url, hash, true);\n console.log(\" ✓ Code trusted and added to kly.sum\\n\");\n return { proceedWithExecution: true, result };\n }\n\n console.log(\"\\n User declined to trust the code\");\n return { proceedWithExecution: false, result };\n }\n\n case \"mismatch\": {\n // Hash doesn't match - code has changed!\n const existingEntry = sumManager.get(url);\n result.expectedHash = existingEntry?.hash;\n\n console.log(\"\\n🚨 SECURITY WARNING: Code has been modified!\\n\");\n console.log(\n \" The code for this tool has changed since you last ran it.\",\n );\n console.log(\" This could indicate:\");\n console.log(\" - A supply chain attack (code tampering)\");\n console.log(\" - Maintainer account compromise\");\n console.log(\" - Git history rewrite\\n\");\n\n console.log(\n \" Expected hash:\",\n `${result.expectedHash?.slice(0, 40)}...`,\n );\n console.log(\" Current hash: \", `${hash.slice(0, 40)}...\\n`);\n\n console.log(\" Recommended actions:\");\n console.log(\" 1. Check GitHub for official announcements\");\n console.log(\" 2. Contact the maintainer\");\n console.log(\" 3. Review code changes carefully\");\n console.log(\n ` 4. Visit: https://github.com/${ref.owner}/${ref.repo}/commits/${ref.ref}\\n`,\n );\n\n const shouldProceed = await confirm(\n \"⚠️ Proceed anyway? (NOT RECOMMENDED)\",\n false,\n );\n\n if (shouldProceed) {\n const shouldUpdate = await confirm(\n \"Update kly.sum with new hash?\",\n false,\n );\n\n if (shouldUpdate) {\n sumManager.update(url, hash, true);\n console.log(\" ✓ kly.sum updated with new hash\\n\");\n }\n\n return { proceedWithExecution: true, result };\n }\n\n console.log(\"\\n Execution cancelled for safety\");\n return { proceedWithExecution: false, result };\n }\n\n default: {\n // Should never reach here, but TypeScript requires it\n console.error(\"Unknown verification result\");\n return { proceedWithExecution: false, result };\n }\n }\n}\n\n/**\n * Execute the kly app\n */\nasync function executeApp(\n ref: RepoRef,\n repoPath: string,\n args: string[],\n mcp: boolean,\n): Promise<void> {\n // Read config and validate\n const config = readKlyConfig(repoPath);\n\n if (config?.version) {\n if (!validateVersion(config.version, KLY_VERSION)) {\n throw new Error(\n `This app requires kly ${config.version}, but you have ${KLY_VERSION}`,\n );\n }\n }\n\n // Check required env vars\n if (config?.env && config.env.length > 0) {\n const missing = checkEnvVars(config.env);\n if (missing.length > 0) {\n console.warn(\n `Warning: Required environment variables not set: ${missing.join(\", \")}`,\n );\n }\n }\n\n // Resolve entry point\n const entryPoint = resolveEntryPoint(repoPath);\n if (!entryPoint) {\n throw new Error(`Cannot resolve entry point for ${ref.owner}/${ref.repo}`);\n }\n\n const absoluteEntryPath = join(repoPath, entryPoint);\n\n // Set remote ref environment variable for permission tracking\n const remoteRef = `github.com/${ref.owner}/${ref.repo}`;\n const prevRemoteRef = process.env[ENV_VARS.REMOTE_REF];\n process.env[ENV_VARS.REMOTE_REF] = remoteRef;\n\n try {\n // Dynamic imports to avoid circular dependencies\n const { getAppIdentifier, checkApiKeyPermission, getAppSandboxConfig } =\n await import(\"../permissions\");\n const { launchSandbox } = await import(\"../host/launcher\");\n const { buildSandboxConfig, formatPermissionsSummary } = await import(\n \"../permissions/config-builder\"\n );\n const { extractAppPermissions } = await import(\"./permissions-extractor\");\n\n const appId = getAppIdentifier();\n\n // Extract declared permissions from the app\n console.log(\"📋 Reading app permissions...\");\n const declaredPermissions = await extractAppPermissions(absoluteEntryPath);\n\n // Show permission summary if declared\n if (declaredPermissions) {\n const summary = formatPermissionsSummary(declaredPermissions);\n if (summary.length > 0) {\n console.log(\"\\nThis app requests the following permissions:\");\n for (const item of summary) {\n console.log(item);\n }\n console.log(\"\");\n }\n }\n\n // Check permissions based on what's declared\n console.log(\"🔐 Checking permissions...\");\n\n let allowApiKey = false;\n let sandboxConfig:\n | import(\"@anthropic-ai/sandbox-runtime\").SandboxRuntimeConfig\n | null = null;\n\n // Only ask for API key permission if the app declares it needs it\n if (declaredPermissions?.apiKeys) {\n allowApiKey = await checkApiKeyPermission(appId);\n\n if (!allowApiKey) {\n console.error(\"❌ Permission denied: API key access rejected\");\n process.exit(1);\n }\n }\n\n // Build sandbox config from declared permissions, or ask interactively\n if (declaredPermissions) {\n // Use declared permissions to build sandbox config\n sandboxConfig = buildSandboxConfig(declaredPermissions);\n } else {\n // Fall back to interactive prompt if no permissions declared\n sandboxConfig = await getAppSandboxConfig(appId);\n\n if (!sandboxConfig) {\n console.error(\"❌ Permission denied: Sandbox configuration rejected\");\n process.exit(1);\n }\n }\n\n // Handle MCP mode\n if (mcp) {\n // For MCP mode, we still need special handling\n // For now, just run directly without sandbox\n console.warn(\n \"⚠️ MCP mode with remote repos not yet fully supported in new architecture\",\n );\n process.env[ENV_VARS.MCP_MODE] = \"true\";\n process.argv = [\"bun\", absoluteEntryPath];\n await import(absoluteEntryPath);\n return;\n }\n\n // Launch in sandbox\n const result = await launchSandbox({\n scriptPath: absoluteEntryPath,\n args,\n appId,\n sandboxConfig,\n allowApiKey,\n });\n\n if (result.error) {\n console.error(`\\n❌ Error: ${result.error}`);\n }\n\n if (result.exitCode !== 0) {\n process.exit(result.exitCode);\n }\n } finally {\n // Restore environment\n if (prevRemoteRef === undefined) {\n delete process.env[ENV_VARS.REMOTE_REF];\n } else {\n process.env[ENV_VARS.REMOTE_REF] = prevRemoteRef;\n }\n }\n}\n\nexport { clearAllCache, invalidateCache } from \"./cache\";\n// Re-export utilities\nexport { isRemoteRef, parseRemoteRef } from \"./parser\";\nexport type {\n CacheMetadata,\n KlyConfig,\n RepoRef,\n RunRemoteOptions,\n} from \"./types\";\n","#!/usr/bin/env bun\nimport { resolve } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { modelsCommand } from \"../src/ai/models-command\";\nimport { launchSandbox } from \"../src/host/launcher\";\nimport { getAppIdentifier } from \"../src/permissions\";\nimport { permissionsCommand } from \"../src/permissions/cli\";\nimport { buildSandboxConfig } from \"../src/permissions/config-builder\";\nimport { extractAppPermissions } from \"../src/permissions/extract\";\nimport {\n checkStoredPermission,\n requestUnifiedPermission,\n} from \"../src/permissions/unified-prompt\";\nimport { isRemoteRef, runRemote } from \"../src/remote\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nasync function main() {\n if (!command || command === \"--help\" || command === \"-h\") {\n showHelp();\n return;\n }\n\n if (command === \"--version\" || command === \"-v\") {\n showVersion();\n return;\n }\n\n if (command === \"models\") {\n await modelsCommand();\n return;\n }\n\n if (command === \"permissions\") {\n await permissionsCommand();\n return;\n }\n\n if (command === \"run\") {\n const target = args[1];\n if (!target) {\n console.error(\"Error: Missing file path or remote reference\");\n console.error(\"Usage: kly run <file|user/repo[@ref]>\");\n process.exit(1);\n }\n\n // Check for --force flag\n const forceIndex = args.indexOf(\"--force\");\n const force = forceIndex !== -1;\n\n // Find -- separator for app arguments\n const dashDashIndex = args.indexOf(\"--\");\n const appArgs =\n dashDashIndex !== -1\n ? args.slice(dashDashIndex + 1)\n : args.slice(2).filter((arg) => arg !== \"--force\");\n\n if (isRemoteRef(target)) {\n await runRemote(target, { args: appArgs, force });\n } else {\n await runFile(target, appArgs);\n }\n return;\n }\n\n if (command === \"mcp\") {\n const target = args[1];\n if (!target) {\n console.error(\"Error: Missing file path or remote reference\");\n console.error(\"Usage: kly mcp <file|user/repo[@ref]>\");\n process.exit(1);\n }\n\n // Check for --force flag\n const forceIndex = args.indexOf(\"--force\");\n const force = forceIndex !== -1;\n\n if (isRemoteRef(target)) {\n await runRemote(target, { args: [], force, mcp: true });\n } else {\n await runFileAsMcp(target);\n }\n return;\n }\n\n console.error(`Unknown command: ${command}`);\n console.error('Run \"kly --help\" for usage');\n process.exit(1);\n}\n\nasync function runFile(filePath: string, appArgs: string[]) {\n const absolutePath = resolve(process.cwd(), filePath);\n\n // Set local file identifier for permission tracking\n const prevLocalRef = process.env.KLY_LOCAL_REF;\n process.env.KLY_LOCAL_REF = `local:${absolutePath}`;\n\n try {\n // Get app identifier\n const appId = getAppIdentifier();\n\n // Check if permission already granted\n const storedConfig = checkStoredPermission(appId);\n let sandboxConfig: SandboxRuntimeConfig;\n let allowApiKey = false;\n\n if (!storedConfig) {\n // Extract declared permissions from app\n const appPermissions = await extractAppPermissions(absolutePath);\n\n // Build complete sandbox config (with auto-LLM domains if apiKeys: true)\n sandboxConfig = buildSandboxConfig(appPermissions);\n\n // Show unified permission prompt\n console.log(\"🔐 Checking permissions...\");\n const allowed = await requestUnifiedPermission(\n appId,\n appPermissions,\n sandboxConfig,\n );\n\n if (!allowed) {\n console.error(\"❌ Permission denied\");\n process.exit(1);\n }\n\n // Set API key access based on declared permissions\n allowApiKey = appPermissions?.apiKeys ?? false;\n } else {\n // Permission already granted, use stored config\n sandboxConfig = storedConfig;\n // We need to re-extract to determine if apiKeys was requested\n const appPermissions = await extractAppPermissions(absolutePath);\n allowApiKey = appPermissions?.apiKeys ?? false;\n }\n\n // Launch in sandbox\n const result = await launchSandbox({\n scriptPath: absolutePath,\n args: appArgs,\n appId,\n sandboxConfig,\n allowApiKey,\n });\n\n if (result.error) {\n console.error(`\\n❌ Error: ${result.error}`);\n }\n\n if (result.exitCode !== 0) {\n process.exit(result.exitCode);\n }\n } finally {\n // Restore environment\n if (prevLocalRef === undefined) {\n delete process.env.KLY_LOCAL_REF;\n } else {\n process.env.KLY_LOCAL_REF = prevLocalRef;\n }\n }\n}\n\nasync function runFileAsMcp(filePath: string) {\n const absolutePath = resolve(process.cwd(), filePath);\n\n // Set MCP mode environment variable\n process.env.KLY_MCP_MODE = \"true\";\n\n // Modify process.argv\n process.argv = [\"bun\", absolutePath];\n\n // Dynamic import triggers defineApp's auto-execution in MCP mode\n await import(absolutePath);\n}\n\nfunction showHelp() {\n console.log(`\nkly - Command Line AI\n\nUsage:\n kly <command> [options]\n\nCommands:\n models Manage LLM model configurations\n permissions Manage app permissions\n run <target> Run a Kly app\n mcp <target> Start an MCP server for a Kly app\n\nTarget can be:\n ./file.ts Local file\n user/repo GitHub repo (main branch)\n user/repo@v1.0.0 GitHub repo at specific tag\n user/repo@branch GitHub repo at specific branch\n\nOptions:\n --force Force re-fetch remote repo (ignore cache)\n --help, -h Show help\n --version, -v Show version\n\nExamples:\n kly models\n kly permissions\n kly run ./my-tool.ts\n kly run ./my-tool.ts --name=World\n kly run user/weather-app\n kly run user/weather-app@v1.0.0\n kly run user/weather-app -- --city=Beijing\n kly mcp ./my-tool.ts\n kly mcp user/weather-app\n`);\n}\n\nfunction showVersion() {\n console.log(\"0.1.0\");\n}\n\nmain().catch((err) => {\n console.error(err.message || err);\n process.exit(1);\n});\n"],"mappings":"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"}