kly 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kly might be problematic. Click here for more details.
- package/dist/ai/storage.mjs +3 -2
- package/dist/ai/storage.mjs.map +1 -1
- package/dist/bin/bin-registry-DOBspniG.mjs +3 -0
- package/dist/bin/kly.mjs +3217 -2430
- package/dist/bin/kly.mjs.map +1 -1
- package/dist/bin/{permissions-extractor-BfUPS0Tr.mjs → permissions-extractor-ySRyhQut.mjs} +4 -2
- package/dist/bin/permissions-extractor-ySRyhQut.mjs.map +1 -0
- package/dist/bin/remote-tQeFZX9o.mjs +3 -0
- package/dist/cli.mjs +4 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/define-app.d.mts.map +1 -1
- package/dist/define-app.mjs +35 -28
- package/dist/define-app.mjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/mcp/schema-converter.d.mts.map +1 -1
- package/dist/mcp/schema-converter.mjs +2 -1
- package/dist/mcp/schema-converter.mjs.map +1 -1
- package/dist/mcp/server.d.mts.map +1 -1
- package/dist/mcp/server.mjs +7 -6
- package/dist/mcp/server.mjs.map +1 -1
- package/dist/permissions/index.mjs +8 -10
- package/dist/permissions/index.mjs.map +1 -1
- package/dist/sandbox/bundled-executor.d.mts.map +1 -1
- package/dist/sandbox/bundled-executor.mjs +67 -10
- package/dist/sandbox/bundled-executor.mjs.map +1 -1
- package/dist/sandbox/ipc-client.mjs +2 -0
- package/dist/sandbox/ipc-client.mjs.map +1 -1
- package/dist/shared/constants.mjs +5 -1
- package/dist/shared/constants.mjs.map +1 -1
- package/dist/shared/errors.mjs +21 -0
- package/dist/shared/errors.mjs.map +1 -0
- package/dist/types.d.mts +7 -6
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/ui/components/confirm.d.mts.map +1 -1
- package/dist/ui/components/confirm.mjs +4 -8
- package/dist/ui/components/confirm.mjs.map +1 -1
- package/dist/ui/components/form.d.mts.map +1 -1
- package/dist/ui/components/form.mjs +12 -26
- package/dist/ui/components/form.mjs.map +1 -1
- package/dist/ui/components/input.d.mts.map +1 -1
- package/dist/ui/components/input.mjs +3 -7
- package/dist/ui/components/input.mjs.map +1 -1
- package/dist/ui/{utils/output.d.mts → components/log.d.mts} +22 -2
- package/dist/ui/components/log.d.mts.map +1 -0
- package/dist/ui/components/log.mjs +92 -0
- package/dist/ui/components/log.mjs.map +1 -0
- package/dist/ui/components/prompts.d.mts +1 -0
- package/dist/ui/components/select.d.mts.map +1 -1
- package/dist/ui/components/select.mjs +5 -8
- package/dist/ui/components/select.mjs.map +1 -1
- package/dist/ui/components/table.d.mts +1 -1
- package/dist/ui/components/table.d.mts.map +1 -1
- package/dist/ui/components/table.mjs +19 -19
- package/dist/ui/components/table.mjs.map +1 -1
- package/dist/ui/index.d.mts +3 -2
- package/dist/ui/utils/cancel.mjs +17 -0
- package/dist/ui/utils/cancel.mjs.map +1 -0
- package/dist/ui/utils/colors.d.mts +3 -3
- package/dist/ui/utils/colors.d.mts.map +1 -1
- package/dist/ui/utils/colors.mjs +21 -33
- package/dist/ui/utils/colors.mjs.map +1 -1
- package/package.json +14 -12
- package/dist/bin/permissions-extractor-BfUPS0Tr.mjs.map +0 -1
- package/dist/ui/utils/output.d.mts.map +0 -1
- package/dist/ui/utils/output.mjs +0 -42
- package/dist/ui/utils/output.mjs.map +0 -1
- /package/dist/bin/{config-builder-D5EtwOB3.mjs → config-builder-VSUaGlaV.mjs} +0 -0
- /package/dist/bin/{launcher-Ex3ynZdE.mjs → launcher-DD6vpEFb.mjs} +0 -0
- /package/dist/bin/{permissions-C_WgoA3t.mjs → permissions-D7-M2lRi.mjs} +0 -0
package/dist/bin/kly.mjs.map
CHANGED
|
@@ -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","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 /** Working directory where `kly run` was invoked */\n invokeDir: 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 /** Working directory where `kly run` was invoked */\n invokeDir: 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, invokeDir, sandboxConfig, allowApiKey } =\n options;\n\n // Initialize sandbox manager\n await SandboxManager.initialize(sandboxConfig);\n\n // Resolve paths\n const absoluteScriptPath = resolve(process.cwd(), scriptPath);\n const _scriptDir = absoluteScriptPath.substring(\n 0,\n absoluteScriptPath.lastIndexOf(\"/\"),\n );\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 cwd: _scriptDir, // Set working directory to script's directory for module resolution\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 invokeDir,\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/config\"), // KLY config directory\n join(homedir(), \".kly/permissions.json\"), // Permissions file\n join(homedir(), \".kly/kly.sum\"), // Integrity checksums\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/config\"), // KLY config directory\n join(homedir(), \".kly/permissions.json\"), // Permissions file (prevent reading)\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 allowPty: true, // Enable pseudo-terminal support for interactive prompts\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: convention candidates > main field\n *\n * We prioritize convention-based source files over package.json main field\n * because remote apps are run from source, not from built artifacts.\n */\nexport function resolveEntryPoint(repoPath: string): string | null {\n // First, try convention candidates (source files)\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 // Fallback to package.json main field\n const pkgPath = join(repoPath, \"package.json\");\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, ignore\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 // Ensure remote app directory is accessible in sandbox\n // This is needed for module resolution and file access\n if (!sandboxConfig.filesystem.allowWrite.includes(repoPath)) {\n sandboxConfig.filesystem.allowWrite.push(repoPath);\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 invokeDir: process.cwd(), // Capture where kly run was invoked\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 // Capture the working directory where kly run was invoked\n const invokeDir = process.cwd();\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 invokeDir,\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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"}
|
|
1
|
+
{"version":3,"file":"kly.mjs","names":["exitCode: number","request: IPCRequest","colorMap: Record<AnsiColor, (text: string) => string>","text","lines: string[]","output","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: SelectOption<LLMProvider>[]","lines: string[]","parts: string[]","baseURL","model","baseURL: string | undefined","modelOptions: SelectOption<string>[]","bins: Record<string, string>","error","cachedRegistry: BinRegistryData | null","error","error","registered: string[]","skipped: string[]","errors: string[]","error","entry: BinRegistryEntry","pathLine: string","error","provider: Provider","subpath: string | undefined","PROVIDER_ALIASES: Record<string, Provider>","execAsync","error","stat: Stats","error","autoRegisterBins","PROVIDER_DOMAINS: Record<RepoRef[\"provider\"], string>","verifyResult: \"ok\" | \"mismatch\" | \"new\"","result: IntegrityCheckResult","getAppIdentifier","extractAppPermissions","formatPermissionsSummary","sandboxConfig:\n | import(\"@anthropic-ai/sandbox-runtime\").SandboxRuntimeConfig\n | null","checkApiKeyPermission","buildSandboxConfig","getAppSandboxConfig","launchSandbox","args","runRemote","rawIsCancel","options: ResourceProviderOptions","error","modelConfig: ModelConfigResponse | null","result: Record<string, unknown>","command","initMessage: SandboxInitMessage","executionResult: ExecutionCompleteMessage | null","error","denyRead: string[]","allowWrite: string[]","allowedDomains: string[]","sandboxConfig: SandboxRuntimeConfig","p","allowedDomains: string[]","allowWrite: string[]","denyRead: string[]","p","summary: string[]","sandboxConfig: SandboxRuntimeConfig"],"sources":["../../src/shared/errors.ts","../../src/ui/utils/cancel.ts","../../src/ui/utils/tty.ts","../../src/shared/constants.ts","../../src/sandbox/ipc-client.ts","../../src/shared/runtime-mode.ts","../../src/ui/components/confirm.ts","../../src/ui/utils/colors.ts","../../src/ui/components/log.ts","../../src/ui/components/password.ts","../../src/ui/components/prompts.ts","../../src/ui/components/select.ts","../../src/ui/components/table.ts","../../src/ui/components/text.ts","../../src/ai/models-dev.ts","../../src/ai/provider-config.ts","../../src/ai/storage.ts","../../src/ai/types.ts","../../src/ai/models-command.ts","../../src/bin-registry/bin-detector.ts","../../src/bin-registry/registry-manager.ts","../../src/bin-registry/shim-generator.ts","../../src/bin-registry/auto-register.ts","../../src/bin-registry/path-manager.ts","../../src/remote/parser.ts","../../src/remote/cache.ts","../../src/remote/fetcher.ts","../../src/remote/integrity.ts","../../src/remote/lockfile.ts","../../src/remote/resolver.ts","../../src/remote/update-checker.ts","../../src/remote/index.ts","../../src/bin-registry/commands.ts","../../src/shared/ipc-protocol.ts","../../src/host/resource-provider.ts","../../src/host/launcher.ts","../../src/permissions/index.ts","../../src/permissions/cli.ts","../../src/permissions/config-builder.ts","../../src/permissions/extract.ts","../../src/permissions/unified-prompt.ts","../../bin/kly.ts"],"sourcesContent":["export class ExitError extends Error {\n constructor(\n message: string,\n public exitCode: number = 1,\n ) {\n super(message);\n this.name = \"ExitError\";\n }\n}\n\n/**\n * Used for user cancellation - not an error, just a graceful exit\n */\nexport class ExitWarning extends Error {\n constructor(message: string = \"Operation cancelled\") {\n super(message);\n this.name = \"ExitWarning\";\n }\n}\n","import * as p from \"@clack/prompts\";\nimport { ExitWarning } from \"../../shared/errors\";\n\n/**\n * Handle cancel action for prompts\n * If the value is a cancel symbol, throws ExitWarning\n * Otherwise returns the value\n */\nexport function handleCancel<T>(value: T | symbol): T {\n if (p.isCancel(value)) {\n throw new ExitWarning();\n }\n return value;\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","/**\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 * Exit codes\n */\nexport const EXIT_CODES = {\n /** User cancelled operation (similar to SIGINT) */\n CANCELLED: 130,\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","import { TIMEOUTS } from \"../shared/constants\";\nimport { ExitWarning } from \"../shared/errors\";\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 if (response.cancelled) {\n reject(new ExitWarning(response.error));\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","/**\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","import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { handleCancel } from \"../utils/cancel\";\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 p.log.warn(\n `[MCP] 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 return handleCancel(result);\n}\n","import * as colors from \"xycolors\";\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 * Color mapping for formatText function\n */\nconst colorMap: Record<AnsiColor, (text: string) => string> = {\n red: colors.red,\n green: colors.green,\n yellow: colors.yellow,\n blue: colors.blue,\n magenta: colors.magenta,\n cyan: colors.cyan,\n white: colors.white,\n gray: colors.gray,\n};\n\n/**\n * Format text with xycolors\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 result = colorMap[options.color](result);\n }\n\n // Apply styles\n if (options?.bold) result = colors.bold(result);\n if (options?.dim) result = colors.dim(result);\n if (options?.italic) result = colors.italic(result);\n if (options?.underline) result = colors.underline(result);\n\n return result;\n}\n\nexport { colors };\n","import * as p from \"@clack/prompts\";\nimport { colors } from \"../utils/colors\";\n\n/**\n * Log utilities for consistent CLI output\n *\n * Uses @clack/prompts log functions for styled output\n */\nexport const log = {\n /**\n * Display an info message\n *\n * @example\n * ```typescript\n * log.info(\"Processing files...\");\n * ```\n */\n info(message: string): void {\n p.log.info(message);\n },\n\n /**\n * Display a success message\n *\n * @example\n * ```typescript\n * log.success(\"Build completed successfully!\");\n * ```\n */\n success(message: string): void {\n p.log.success(message);\n },\n\n /**\n * Display a step message\n *\n * @example\n * ```typescript\n * log.step(\"Installing dependencies\");\n * ```\n */\n step(message: string): void {\n p.log.step(message);\n },\n\n /**\n * Display a warning message\n *\n * @example\n * ```typescript\n * log.warn(\"Config file not found, using defaults\");\n * ```\n */\n warn(message: string): void {\n p.log.warn(message);\n },\n\n /**\n * Display a general message\n *\n * @example\n * ```typescript\n * log.message(\"Welcome to the CLI!\");\n * ```\n */\n message(message: string): void {\n p.log.message(message);\n },\n};\n\n/**\n * Output a result to the console\n *\n * @param result - The result to display (string, object, etc.)\n *\n * @example\n * ```typescript\n * output(\"Hello, world!\");\n * output({ name: \"John\", age: 30 });\n * ```\n */\nexport function output(result: unknown): void {\n if (result === undefined || result === null) {\n return;\n }\n\n if (typeof result === \"string\") {\n p.log.message(result);\n } else {\n p.log.message(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 *\n * @example\n * ```typescript\n * error(\"Failed to load config\", [\n * \"Check if config.json exists\",\n * \"Verify JSON syntax\"\n * ]);\n * ```\n */\nexport function error(message: string, suggestions?: string[]): void {\n p.log.error(message);\n\n if (suggestions?.length) {\n p.log.message(\"\");\n p.log.message(colors.dim(\"Suggestions:\"));\n for (const suggestion of suggestions) {\n p.log.message(` ${colors.dim(\"•\")} ${suggestion}`);\n }\n }\n}\n\n/**\n * Display help text\n *\n * @param content - Help text content\n *\n * @example\n * ```typescript\n * help(\"Usage: myapp <command> [options]\");\n * ```\n */\nexport function help(content: string): void {\n p.log.message(content);\n}\n\n/**\n * Display an intro message at the start of your CLI\n *\n * @example\n * ```typescript\n * intro(\"Welcome to my-cli v1.0.0\");\n * ```\n */\nexport function intro(message?: string): void {\n p.intro(message);\n}\n\n/**\n * Display an outro message at the end of your CLI\n *\n * @example\n * ```typescript\n * outro(\"Thanks for using my-cli!\");\n * ```\n */\nexport function outro(message?: string): void {\n p.outro(message);\n}\n\n/**\n * Display a cancellation message and optionally exit\n *\n * @example\n * ```typescript\n * cancel(\"Operation cancelled\");\n * ```\n */\nexport function cancel(message?: string): void {\n p.cancel(message);\n}\n\n/**\n * Check if a value is a cancel symbol\n *\n * @example\n * ```typescript\n * const result = await text({ message: \"Enter name\" });\n * if (isCancel(result)) {\n * cancel(\"Cancelled\");\n * process.exit(0);\n * }\n * ```\n */\nexport function isCancel(value: unknown): value is symbol {\n return p.isCancel(value);\n}\n\n/**\n * Add a note/box with styled content\n *\n * @example\n * ```typescript\n * note(\"npm install my-package\", \"Next steps\");\n * ```\n */\nexport function note(message: string, title?: string): void {\n p.note(message, title);\n}\n","import * as p from \"@clack/prompts\";\nimport { handleCancel } from \"../utils/cancel\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Configuration for password component\n */\nexport interface PasswordConfig {\n /** Prompt message */\n prompt: string;\n /** Mask character (default: •) */\n mask?: string;\n /** Validation function */\n validate?: (value: string | undefined) => string | Error | undefined;\n}\n\n/**\n * Prompt for password input with masked characters\n *\n * @example\n * ```typescript\n * const secret = await password({\n * prompt: \"Enter your API key\",\n * validate: (value) => {\n * if (value.length < 8) return \"Password must be at least 8 characters\";\n * }\n * });\n * ```\n */\nexport async function password(config: PasswordConfig): Promise<string> {\n // Non-TTY fallback: throw error (passwords should never have defaults)\n if (!isTTY()) {\n if (process.env.KLY_MCP_MODE === \"true\") {\n throw new Error(\n \"Password input not available in MCP mode. Sensitive credentials should be provided via environment variables or the tool's inputSchema.\",\n );\n }\n\n throw new Error(\n \"Password input not available in non-TTY mode. Please provide credentials via environment variables.\",\n );\n }\n\n const result = await p.password({\n message: config.prompt,\n mask: config.mask ?? \"•\",\n validate: config.validate,\n });\n\n return handleCancel(result);\n}\n","/**\n * Raw prompt utilities for scenarios that need manual cancel handling.\n * These are thin wrappers around @clack/prompts that don't auto-exit on cancel.\n * Use these when you need to return error responses instead of exiting (e.g., IPC handlers).\n */\nimport * as p from \"@clack/prompts\";\n\n// Re-export isCancel for manual cancel detection\nexport const isCancel = p.isCancel;\n\n/**\n * Raw text input - does NOT auto-exit on cancel\n * Returns the cancel symbol if user cancels, handle it manually\n */\nexport async function rawText(config: {\n message: string;\n defaultValue?: string;\n placeholder?: string;\n validate?: (value: string | undefined) => string | Error | undefined;\n}): Promise<string | symbol> {\n return p.text(config);\n}\n\n/**\n * Raw select - does NOT auto-exit on cancel\n * Returns the cancel symbol if user cancels, handle it manually\n */\nexport async function rawSelect<T>(config: {\n message: string;\n options: Array<{\n label: string;\n value: T;\n hint?: string;\n disabled?: boolean;\n }>;\n}): Promise<T | symbol> {\n // Use type assertion to handle clack's complex Option type\n return p.select({\n message: config.message,\n options: config.options as Parameters<typeof p.select>[0][\"options\"],\n }) as Promise<T | symbol>;\n}\n\n/**\n * Raw confirm - does NOT auto-exit on cancel\n * Returns the cancel symbol if user cancels, handle it manually\n */\nexport async function rawConfirm(config: {\n message: string;\n initialValue?: boolean;\n}): Promise<boolean | symbol> {\n return p.confirm(config);\n}\n\n/**\n * Raw multiselect - does NOT auto-exit on cancel\n * Returns the cancel symbol if user cancels, handle it manually\n */\nexport async function rawMultiselect<T>(config: {\n message: string;\n options: Array<{\n label: string;\n value: T;\n hint?: string;\n }>;\n required?: boolean;\n}): Promise<T[] | symbol> {\n // Use type assertion to handle clack's complex Option type\n return p.multiselect({\n message: config.message,\n options: config.options as Parameters<typeof p.multiselect>[0][\"options\"],\n required: config.required,\n }) as Promise<T[] | symbol>;\n}\n\n/**\n * Raw password input - does NOT auto-exit on cancel\n * Returns the cancel symbol if user cancels, handle it manually\n */\nexport async function rawPassword(config: {\n message: string;\n mask?: string;\n validate?: (value: string | undefined) => string | Error | undefined;\n}): Promise<string | symbol> {\n return p.password(config);\n}\n","import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { handleCancel } from \"../utils/cancel\";\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 ...(opt.disabled !== undefined && { disabled: opt.disabled }),\n }));\n\n const result = await p.select({\n message: config.prompt ?? \"Select an option\",\n options: mappedOptions,\n });\n\n return handleCancel(result) as T;\n}\n","import { colors } from \"../utils/colors\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Render table title lines\n */\nfunction _renderTitle(title: string | undefined, styled: boolean): string[] {\n if (!title) return [];\n return [\"\", styled ? colors.bold(title) : title, \"\"];\n}\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 colors.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 lines.push(..._renderTitle(title, true));\n\n // Header row\n if (showHeader) {\n const headerCells = columns.map((col, i) => {\n const text = colors.cyan(colors.bold(col.header));\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(colors.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(colors.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 lines.push(..._renderTitle(title, false));\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\" ? colors.green(\"✓ Active\") : colors.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 * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { handleCancel } from \"../utils/cancel\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Configuration for text component (compatible with @clack/prompts text)\n */\nexport interface TextConfig {\n /** Prompt message */\n message: string;\n /** Default value */\n defaultValue?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Validation function */\n validate?: (value: string | undefined) => string | Error | undefined;\n}\n\n/**\n * Prompt for text input (compatible with @clack/prompts text API)\n *\n * @example\n * ```typescript\n * const name = await text({\n * message: \"What's your name?\",\n * defaultValue: \"Anonymous\",\n * validate: (value) => {\n * if (!value) return \"Name is required\";\n * }\n * });\n * ```\n */\nexport async function text(config: TextConfig): Promise<string> {\n // Sandbox mode: use IPC to request input from host\n if (isSandbox()) {\n return sendIPCRequest<string>(\"prompt:input\", {\n prompt: config.message,\n defaultValue: config.defaultValue,\n placeholder: config.placeholder,\n });\n }\n\n // Non-TTY fallback: return default or throw\n if (!isTTY()) {\n if (config.defaultValue !== undefined) {\n return config.defaultValue;\n }\n\n // Provide MCP-specific error message\n if (isMCP()) {\n throw new Error(\n `Interactive input not available in MCP mode. All parameters must be defined in the tool's inputSchema. Missing parameter: ${config.message}`,\n );\n }\n\n throw new Error(\n \"Interactive input not available in non-TTY mode. Please provide all required arguments.\",\n );\n }\n\n const result = await p.text({\n message: config.message,\n defaultValue: config.defaultValue,\n placeholder: config.placeholder,\n validate: config.validate,\n });\n\n return handleCancel(result);\n}\n","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 { error } from \"../ui\";\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 (err) {\n error(`Failed to parse config file: ${err}`);\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 { ExitWarning } from \"../shared/errors\";\nimport {\n colors,\n confirm,\n intro,\n note,\n outro,\n password,\n type SelectOption,\n select,\n text,\n} from \"../ui\";\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: SelectOption<LLMProvider>[] = [\n {\n value: \"openai\",\n name: \"OpenAI\",\n description: getProviderDescription(\"openai\"),\n },\n {\n value: \"anthropic\",\n name: \"Anthropic\",\n description: getProviderDescription(\"anthropic\"),\n },\n {\n value: \"google\",\n name: \"Google\",\n description: getProviderDescription(\"google\"),\n },\n {\n value: \"deepseek\",\n name: \"DeepSeek\",\n description: getProviderDescription(\"deepseek\"),\n },\n { value: \"groq\", name: \"Groq\", description: getProviderDescription(\"groq\") },\n {\n value: \"mistral\",\n name: \"Mistral\",\n description: getProviderDescription(\"mistral\"),\n },\n {\n value: \"cohere\",\n name: \"Cohere\",\n description: getProviderDescription(\"cohere\"),\n },\n {\n value: \"ollama\",\n name: \"Ollama\",\n description: getProviderDescription(\"ollama\"),\n },\n {\n value: \"openai-compatible\",\n name: \"OpenAI Compatible\",\n description: \"Custom endpoint\",\n },\n];\n\n/**\n * Main entry point for `kly models` command\n */\nexport async function modelsCommand(): Promise<void> {\n intro(colors.bgCyan(colors.black(\" kly models \")));\n\n const models = listModels();\n\n const action = await select<string>({\n prompt: \"What would you like to do?\",\n options: [\n { value: \"list\", name: \"List configured models\" },\n { value: \"add\", name: \"Add a new model\" },\n {\n value: \"switch\",\n name: \"Switch current model\",\n disabled: models.length === 0,\n },\n {\n value: \"remove\",\n name: \"Remove a model\",\n disabled: models.length === 0,\n },\n ],\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 outro(colors.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 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 ? colors.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}${colors.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 += ` ${colors.dim(metadata)}`;\n }\n }\n }\n\n lines.push(line);\n }\n\n 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 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 const provider = await select<LLMProvider>({\n prompt: \"Select a provider:\",\n options: PROVIDER_OPTIONS,\n });\n\n // Get provider-specific configuration\n const config = await getProviderConfig(provider);\n\n saveModelConfig(name, config);\n\n note(\n `Model '${colors.cyan(name)}' configured with ${getProviderDisplayName(provider)}`,\n colors.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 note(\"No models configured\");\n return;\n }\n\n const modelName = await select<string>({\n prompt: \"Select a model:\",\n options: models.map((m) => ({\n value: m.name,\n name: m.name,\n description: `${getProviderDisplayName(m.config.provider)} - ${m.config.model || DEFAULT_MODELS[m.config.provider]}`,\n })),\n });\n\n setCurrentModel(modelName);\n\n note(`Switched to '${colors.cyan(modelName)}'`, colors.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 note(\"No models configured\");\n return;\n }\n\n const modelName = await select<string>({\n prompt: \"Select a model to remove:\",\n options: models.map((m) => ({\n value: m.name,\n name: m.name,\n description: `${getProviderDisplayName(m.config.provider)}`,\n })),\n });\n\n const confirmed = await confirm(\n `Are you sure you want to remove '${modelName}'?`,\n );\n\n if (!confirmed) {\n throw new ExitWarning();\n }\n\n removeModelConfig(modelName);\n\n note(`Removed '${colors.cyan(modelName)}'`, colors.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 text({\n message: \"Ollama base URL:\",\n placeholder: \"http://localhost:11434\",\n defaultValue: \"http://localhost:11434\",\n });\n\n const model = await text({\n message: \"Model name:\",\n placeholder: defaultModel,\n defaultValue: defaultModel,\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 password({\n prompt: `Enter your ${getProviderDisplayName(provider)} API key:`,\n validate: (value) => {\n if (!value) return \"API key is required\";\n return undefined;\n },\n });\n\n // Ask for optional base URL\n const customBaseURL = await confirm(\n \"Do you want to specify a custom base URL?\",\n false,\n );\n\n let baseURL: string | undefined;\n\n if (customBaseURL) {\n const defaultURL = getDefaultBaseURL(provider);\n const baseURLInput = await text({\n message: \"Base URL:\",\n placeholder: defaultURL || \"\",\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: SelectOption<string>[] = availableModels\n .slice(0, 10)\n .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 name: m.name || m.id,\n description: 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 name: `Use default (${defaultModel})`,\n description: \"Recommended\",\n });\n modelOptions.push({\n value: \"__custom__\",\n name: \"Enter custom model name\",\n description: \"Advanced\",\n });\n\n const selectedModel = await select<string>({\n prompt: \"Select a model:\",\n options: modelOptions,\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 confirm(\n `Use default model (${defaultModel})?`,\n true,\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 text({\n message: \"Model name:\",\n placeholder: defaultModel,\n defaultValue: defaultModel,\n });\n\n return modelInput || defaultModel;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { log } from \"../ui\";\nimport type { BinDetectionResult } from \"./types\";\n\nexport function detectBins(projectPath: string): BinDetectionResult {\n const pkgPath = join(projectPath, \"package.json\");\n\n if (!existsSync(pkgPath)) {\n return {\n hasBin: false,\n bins: {},\n projectName: \"unknown\",\n projectVersion: \"0.0.0\",\n };\n }\n\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n const projectName = pkg.name || \"unknown\";\n const projectVersion = pkg.version || \"0.0.0\";\n\n if (!pkg.bin) {\n return {\n hasBin: false,\n bins: {},\n projectName,\n projectVersion,\n };\n }\n\n // Handle two formats:\n // 1. String: { \"bin\": \"./cli.js\" } - command name = package name\n // 2. Object: { \"bin\": { \"mycmd\": \"./cli.js\", \"other\": \"./other.js\" } }\n\n let bins: Record<string, string> = {};\n\n if (typeof pkg.bin === \"string\") {\n // Single bin, use package name as command\n bins[projectName] = pkg.bin;\n } else if (typeof pkg.bin === \"object\" && pkg.bin !== null) {\n // Multiple bins\n bins = { ...pkg.bin };\n }\n\n return {\n hasBin: Object.keys(bins).length > 0,\n bins,\n projectName,\n projectVersion,\n };\n } catch (error) {\n log.warn(`Failed to parse package.json: ${error}`);\n return {\n hasBin: false,\n bins: {},\n projectName: \"unknown\",\n projectVersion: \"0.0.0\",\n };\n }\n}\n\nexport function isCommandAvailable(commandName: string): boolean {\n // Check if command already exists in system\n // Use `which` or `command -v` to check\n try {\n const { spawnSync } = require(\"node:child_process\");\n const result = spawnSync(\"command\", [\"-v\", commandName], {\n shell: true,\n encoding: \"utf-8\",\n });\n return result.status === 0;\n } catch {\n return false;\n }\n}\n","import {\n existsSync,\n mkdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport yaml from \"js-yaml\";\nimport { log } from \"../ui\";\nimport type { BinRegistryData, BinRegistryEntry } from \"./types\";\n\nlet cachedRegistry: BinRegistryData | null = null;\nlet cacheMtime = 0;\n\nexport function getRegistryPath(): string {\n return join(homedir(), \".kly\", \"bin-registry.yaml\");\n}\n\nexport function readRegistry(): BinRegistryData {\n const registryPath = getRegistryPath();\n\n if (existsSync(registryPath)) {\n const currentMtime = statSync(registryPath).mtimeMs;\n\n if (cachedRegistry && cacheMtime === currentMtime) {\n return cachedRegistry;\n }\n\n cacheMtime = currentMtime;\n }\n\n if (!existsSync(registryPath)) {\n return {\n registryVersion: 1,\n commands: {},\n };\n }\n\n try {\n const content = readFileSync(registryPath, \"utf-8\");\n const data = yaml.load(content) as BinRegistryData;\n\n if (!data || typeof data !== \"object\") {\n throw new Error(\"Invalid registry format\");\n }\n\n data.commands = data.commands || {};\n data.registryVersion = data.registryVersion || 1;\n\n cachedRegistry = data;\n return data;\n } catch (error) {\n log.warn(`Failed to read bin registry: ${error}`);\n return {\n registryVersion: 1,\n commands: {},\n };\n }\n}\n\nexport function writeRegistry(data: BinRegistryData): void {\n const registryPath = getRegistryPath();\n\n mkdirSync(dirname(registryPath), { recursive: true });\n\n const header =\n \"# kly bin registry - Tracks globally registered commands\\n\" +\n \"# This file is auto-generated and auto-updated\\n\\n\";\n\n const yamlContent = yaml.dump(data, {\n indent: 2,\n lineWidth: 100,\n noRefs: true,\n sortKeys: true,\n });\n\n writeFileSync(registryPath, header + yamlContent, \"utf-8\");\n\n // Update cache\n cachedRegistry = data;\n if (existsSync(registryPath)) {\n cacheMtime = statSync(registryPath).mtimeMs;\n }\n}\n\nexport function getCommand(commandName: string): BinRegistryEntry | null {\n const registry = readRegistry();\n return registry.commands[commandName] || null;\n}\n\nexport function addCommand(commandName: string, entry: BinRegistryEntry): void {\n const registry = readRegistry();\n registry.commands[commandName] = entry;\n writeRegistry(registry);\n}\n\nexport function removeCommand(commandName: string): boolean {\n const registry = readRegistry();\n\n if (!registry.commands[commandName]) {\n return false;\n }\n\n delete registry.commands[commandName];\n writeRegistry(registry);\n return true;\n}\n\nexport function updateLastUsed(commandName: string): void {\n const registry = readRegistry();\n\n if (registry.commands[commandName]) {\n registry.commands[commandName].lastUsed = new Date().toISOString();\n writeRegistry(registry);\n }\n}\n\nexport function listCommands(): Array<\n BinRegistryEntry & { commandName: string }\n> {\n const registry = readRegistry();\n return Object.entries(registry.commands).map(([name, entry]) => ({\n ...entry,\n commandName: name,\n }));\n}\n","import {\n chmodSync,\n existsSync,\n mkdirSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { log } from \"../ui\";\nimport type { BinRegistryEntry } from \"./types\";\n\nexport function getShimDir(): string {\n return join(homedir(), \".kly\", \"bin\");\n}\n\nexport function generateShimPath(commandName: string): string {\n return join(getShimDir(), commandName);\n}\n\nexport function createShim(\n commandName: string,\n entry: BinRegistryEntry,\n): string {\n const shimPath = generateShimPath(commandName);\n\n // Ensure directory exists\n mkdirSync(dirname(shimPath), { recursive: true });\n\n const shimContent = generateShimContent(commandName, entry);\n\n // Write shim file\n writeFileSync(shimPath, shimContent, \"utf-8\");\n\n // Make executable (chmod +x)\n try {\n chmodSync(shimPath, 0o755);\n } catch (error) {\n log.warn(`Failed to make shim executable: ${error}`);\n }\n\n return shimPath;\n}\n\nfunction generateShimContent(\n commandName: string,\n entry: BinRegistryEntry,\n): string {\n const timestamp = new Date().toISOString();\n const sourceDisplay =\n entry.type === \"remote\" ? entry.remoteRef : entry.localPath;\n\n if (entry.type === \"remote\") {\n return `#!/usr/bin/env bun\n\n// Auto-generated shim for kly-registered command\n// DO NOT EDIT - This file is managed by kly\n//\n// Command: ${commandName}\n// Source: ${sourceDisplay}\n// Registered: ${timestamp}\n\nimport { spawn } from \"node:child_process\";\n\nasync function main() {\n const args = process.argv.slice(2);\n\n // Use kly run to ensure caching, updates, and integrity checks\n const klyArgs = [\"run\", \"${entry.remoteRef}\", \"--\", ...args];\n\n const kly = spawn(\"kly\", klyArgs, {\n stdio: \"inherit\",\n cwd: process.cwd(),\n env: process.env,\n });\n\n kly.on(\"exit\", (code) => {\n process.exit(code ?? 0);\n });\n\n kly.on(\"error\", (err) => {\n console.error(\\`Failed to execute kly: \\${err.message}\\`);\n process.exit(1);\n });\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n`;\n }\n\n return `#!/usr/bin/env bun\n\n// Auto-generated shim for kly-registered command\n// DO NOT EDIT - This file is managed by kly\n//\n// Command: ${commandName}\n// Source: ${sourceDisplay}\n// Registered: ${timestamp}\n\nimport { resolve } from \"node:path\";\nimport { spawn } from \"node:child_process\";\n\nasync function main() {\n const args = process.argv.slice(2);\n\n // Run directly from local source\n const sourcePath = \"${entry.localPath}\";\n const binPath = \"${entry.binPath}\";\n const absoluteBinPath = resolve(sourcePath, binPath);\n\n const proc = spawn(\"bun\", [\"run\", absoluteBinPath, ...args], {\n stdio: \"inherit\",\n cwd: process.cwd(),\n env: process.env,\n });\n\n proc.on(\"exit\", (code) => {\n process.exit(code ?? 0);\n });\n\n proc.on(\"error\", (err) => {\n console.error(\\`Failed to execute command: \\${err.message}\\`);\n process.exit(1);\n });\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n`;\n}\n\nexport function removeShim(commandName: string): boolean {\n const shimPath = generateShimPath(commandName);\n\n try {\n if (existsSync(shimPath)) {\n rmSync(shimPath);\n return true;\n }\n return false;\n } catch (error) {\n log.warn(`Failed to remove shim: ${error}`);\n return false;\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { ExitWarning } from \"../shared/errors\";\nimport { log } from \"../ui\";\nimport { detectBins, isCommandAvailable } from \"./bin-detector\";\nimport { addCommand, getCommand, removeCommand } from \"./registry-manager\";\nimport { createShim, removeShim } from \"./shim-generator\";\nimport type {\n BinDetectionResult,\n BinRegistryEntry,\n UnregisterOptions,\n} from \"./types\";\n\n/**\n * Calculate content hash for a project directory\n * Used to detect changes in local projects\n */\nfunction calculateProjectHash(projectPath: string): string {\n const hash = createHash(\"sha256\");\n\n // Hash package.json and main source files\n const files = [\n \"package.json\",\n \"src\", // Will recursively hash\n \"bin\",\n ];\n\n for (const file of files) {\n const filePath = join(projectPath, file);\n try {\n const stat = statSync(filePath);\n if (stat.isFile()) {\n const content = readFileSync(filePath);\n hash.update(content);\n } else if (stat.isDirectory()) {\n // Recursively hash directory contents\n const dirFiles = readdirSync(filePath, { recursive: true });\n for (const subFile of dirFiles) {\n const subPath = join(filePath, subFile as string);\n try {\n if (statSync(subPath).isFile()) {\n hash.update(readFileSync(subPath));\n }\n } catch {\n // Skip files that can't be read\n }\n }\n }\n } catch {\n // Skip missing files\n }\n }\n\n return `sha256-${hash.digest(\"hex\")}`;\n}\n\n/**\n * Auto-register bin commands from a project\n * Called after successful execution of a kly app\n */\nexport async function autoRegisterBins(\n projectPath: string,\n options: {\n type: \"local\" | \"remote\";\n remoteRef?: string;\n force?: boolean;\n skipConfirm?: boolean;\n },\n): Promise<{ registered: string[]; skipped: string[]; errors: string[] }> {\n const detection = detectBins(projectPath);\n\n if (!detection.hasBin) {\n return { registered: [], skipped: [], errors: [] };\n }\n\n const binEntries = Object.entries(detection.bins);\n const registered: string[] = [];\n const skipped: string[] = [];\n const errors: string[] = [];\n\n // Show what will be registered\n if (!options.skipConfirm) {\n log.info(`\\nThis project provides ${binEntries.length} command(s):`);\n for (const [cmdName, binPath] of binEntries) {\n log.message(` • ${cmdName} (${binPath})`);\n }\n\n const shouldRegister = await p.confirm({\n message: \"Would you like to register these commands globally?\",\n });\n\n if (p.isCancel(shouldRegister) || !shouldRegister) {\n return {\n registered: [],\n skipped: binEntries.map(([name]) => name),\n errors: [],\n };\n }\n }\n\n // Register each command\n for (const [commandName, binPath] of binEntries) {\n try {\n await registerSingleCommand(commandName, {\n projectPath,\n binPath,\n detection,\n ...options,\n });\n registered.push(commandName);\n log.success(`Registered: ${commandName}`);\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n errors.push(`${commandName}: ${errorMsg}`);\n log.warn(`Failed to register ${commandName}: ${errorMsg}`);\n }\n }\n\n if (registered.length > 0) {\n log.info(\"\\nCommands are now available globally!\");\n log.message(\"Make sure ~/.kly/bin is in your PATH.\");\n log.message(\"Run 'kly install --setup-path' to configure automatically.\");\n }\n\n return { registered, skipped, errors };\n}\n\nasync function registerSingleCommand(\n commandName: string,\n options: {\n projectPath: string;\n binPath: string;\n detection: BinDetectionResult;\n type: \"local\" | \"remote\";\n remoteRef?: string;\n force?: boolean;\n },\n): Promise<void> {\n const { projectPath, binPath, detection, type, remoteRef, force } = options;\n\n // Check if command already exists\n const existing = getCommand(commandName);\n\n if (existing && !force) {\n // Check if it's the same project (update scenario)\n const isSameProject =\n (type === \"local\" && existing.localPath === projectPath) ||\n (type === \"remote\" && existing.remoteRef === remoteRef);\n\n if (isSameProject) {\n // Update existing registration\n log.step(`Updating registration for ${commandName}`);\n } else {\n // Conflict with different project\n const shouldOverride = await p.confirm({\n message: `Command '${commandName}' is already registered by ${existing.projectName}. Override?`,\n initialValue: false,\n });\n\n if (p.isCancel(shouldOverride) || !shouldOverride) {\n throw new ExitWarning(\"Registration cancelled\");\n }\n }\n }\n\n // Check if command exists in system (not from kly)\n if (!existing && isCommandAvailable(commandName)) {\n log.warn(`Warning: '${commandName}' already exists in your system`);\n const shouldContinue = await p.confirm({\n message: \"This may shadow the existing command. Continue?\",\n initialValue: false,\n });\n\n if (p.isCancel(shouldContinue) || !shouldContinue) {\n throw new ExitWarning(\"Registration cancelled\");\n }\n }\n\n // Calculate content hash (for local projects)\n const contentHash =\n type === \"local\" ? calculateProjectHash(projectPath) : null;\n\n // Create registry entry\n const entry: BinRegistryEntry = {\n type,\n remoteRef: type === \"remote\" ? remoteRef! : null,\n localPath: type === \"local\" ? projectPath : null,\n binPath,\n shimPath: \"\", // Will be set after shim creation\n projectName: detection.projectName,\n projectVersion: detection.projectVersion,\n registeredAt: new Date().toISOString(),\n lastUsed: new Date().toISOString(),\n contentHash,\n };\n\n // Create shim script\n const shimPath = createShim(commandName, entry);\n entry.shimPath = shimPath;\n\n // Save to registry\n addCommand(commandName, entry);\n}\n\n/**\n * Unregister a command\n */\nexport async function unregisterCommand(\n commandName: string,\n options: UnregisterOptions = {},\n): Promise<boolean> {\n const existing = getCommand(commandName);\n\n if (!existing && !options.force) {\n log.warn(`Command '${commandName}' is not registered`);\n return false;\n }\n\n if (!options.skipConfirm && existing) {\n const shouldUnregister = await p.confirm({\n message: `Unregister '${commandName}' from ${existing.projectName}?`,\n });\n\n if (p.isCancel(shouldUnregister) || !shouldUnregister) {\n log.message(\"Cancelled\");\n return false;\n }\n }\n\n // Remove shim\n removeShim(commandName);\n\n // Remove from registry\n removeCommand(commandName);\n\n log.success(`Unregistered: ${commandName}`);\n return true;\n}\n\n/**\n * Check if local project needs re-registration (content changed)\n */\nexport function shouldReregisterLocal(\n commandName: string,\n projectPath: string,\n): boolean {\n const existing = getCommand(commandName);\n\n if (!existing || existing.type !== \"local\") {\n return false;\n }\n\n if (existing.localPath !== projectPath) {\n return false;\n }\n\n const currentHash = calculateProjectHash(projectPath);\n return currentHash !== existing.contentHash;\n}\n","import { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { log } from \"../ui\";\n\nexport function getShellConfigFile(): string | null {\n const home = homedir();\n const shell = process.env.SHELL || \"\";\n\n if (shell.includes(\"zsh\")) {\n return join(home, \".zshrc\");\n }\n if (shell.includes(\"bash\")) {\n const bashrc = join(home, \".bashrc\");\n const profile = join(home, \".bash_profile\");\n return existsSync(bashrc) ? bashrc : profile;\n }\n if (shell.includes(\"fish\")) {\n return join(home, \".config\", \"fish\", \"config.fish\");\n }\n\n return null;\n}\n\nexport function isKlyBinInPath(): boolean {\n const path = process.env.PATH || \"\";\n const klyBin = join(homedir(), \".kly\", \"bin\");\n return path.split(\":\").includes(klyBin);\n}\n\nexport async function setupPath(): Promise<boolean> {\n if (isKlyBinInPath()) {\n log.success(\"~/.kly/bin is already in your PATH\");\n return true;\n }\n\n const configFile = getShellConfigFile();\n\n if (!configFile) {\n log.warn(\"Could not detect shell configuration file\");\n p.log.info(\"\\nManually add this to your shell config:\");\n p.log.message(' export PATH=\"$HOME/.kly/bin:$PATH\"');\n return false;\n }\n\n p.log.info(`\\nDetected shell config: ${configFile}`);\n\n const shouldAdd = await p.confirm({\n message: \"Add ~/.kly/bin to PATH in your shell config?\",\n });\n\n if (p.isCancel(shouldAdd) || !shouldAdd) {\n p.log.message(\"Cancelled\");\n return false;\n }\n\n try {\n const shell = process.env.SHELL || \"\";\n let pathLine: string;\n\n if (shell.includes(\"fish\")) {\n pathLine = \"\\n# Added by kly\\nset -gx PATH $HOME/.kly/bin $PATH\\n\";\n } else {\n pathLine = '\\n# Added by kly\\nexport PATH=\"$HOME/.kly/bin:$PATH\"\\n';\n }\n\n // Check if already added (avoid duplicates)\n if (existsSync(configFile)) {\n const content = readFileSync(configFile, \"utf-8\");\n if (content.includes(\"# Added by kly\")) {\n log.success(\"~/.kly/bin is already configured in your shell\");\n return true;\n }\n }\n\n appendFileSync(configFile, pathLine, \"utf-8\");\n\n log.success(`Added to ${configFile}`);\n p.log.info(\"\\nRestart your shell or run:\");\n p.log.message(` source ${configFile}`);\n\n return true;\n } catch (error) {\n log.warn(`Failed to update ${configFile}: ${error}`);\n return false;\n }\n}\n\nexport function checkPathSetup(): void {\n if (!isKlyBinInPath()) {\n log.warn(\"~/.kly/bin is not in your PATH\");\n p.log.message(\"Run 'kly install --setup-path' to configure automatically\");\n }\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Provider, RepoRef } from \"./types\";\n\n/**\n * Provider aliases (giget-style)\n */\nconst PROVIDER_ALIASES: Record<string, Provider> = {\n gh: \"github\",\n github: \"github\",\n gitlab: \"gitlab\",\n bitbucket: \"bitbucket\",\n sourcehut: \"sourcehut\",\n};\n\n/**\n * Parse various remote formats into RepoRef\n *\n * Supported formats (giget-style):\n * - gh:user/repo\n * - gh:user/repo#branch\n * - gh:user/repo/subpath\n * - gh:user/repo/subpath#branch\n * - gitlab:user/repo\n * - bitbucket:user/repo\n * - sourcehut:user/repo\n *\n * Legacy formats (backward compatible):\n * - user/repo\n * - user/repo@branch\n * - github.com/user/repo\n * - https://github.com/user/repo\n */\nexport function parseRemoteRef(input: string): RepoRef | null {\n let normalized = input.trim();\n let provider: Provider = \"github\";\n let ref = \"main\";\n let subpath: string | undefined;\n\n // First, remove https:// or http:// prefix if present\n normalized = normalized.replace(/^https?:\\/\\//, \"\");\n\n // Check for provider prefix (gh:, gitlab:, etc.)\n const providerMatch = normalized.match(/^([a-z]+):/);\n if (providerMatch?.[1]) {\n const alias = providerMatch[1];\n const matchedProvider = PROVIDER_ALIASES[alias];\n if (matchedProvider) {\n provider = matchedProvider;\n normalized = normalized.slice(providerMatch[0].length);\n }\n }\n\n // Remove github.com/ prefix (legacy support)\n normalized = normalized.replace(/^github\\.com\\//, \"\");\n\n // Extract ref using # (giget-style: user/repo#ref)\n const hashIndex = normalized.indexOf(\"#\");\n if (hashIndex !== -1) {\n ref = normalized.slice(hashIndex + 1);\n normalized = normalized.slice(0, hashIndex);\n } else {\n // Fallback to @ for backward compatibility (user/repo@ref)\n const atIndex = normalized.indexOf(\"@\");\n if (atIndex !== -1) {\n ref = normalized.slice(atIndex + 1);\n normalized = normalized.slice(0, atIndex);\n }\n }\n\n // Remove trailing .git (after ref extraction)\n normalized = normalized.replace(/\\.git$/, \"\");\n\n // Parse owner/repo/subpath\n const parts = normalized.split(\"/\");\n if (parts.length < 2) {\n return null;\n }\n\n const [owner, repo, ...subpathParts] = 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 // Extract subpath if present\n if (subpathParts.length > 0) {\n subpath = subpathParts.join(\"/\");\n }\n\n return { provider, owner, repo, ref, subpath };\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 provider domain name\n */\nfunction getProviderDomain(provider: Provider): string {\n switch (provider) {\n case \"github\":\n return \"github.com\";\n case \"gitlab\":\n return \"gitlab.com\";\n case \"bitbucket\":\n return \"bitbucket.org\";\n case \"sourcehut\":\n return \"sr.ht\";\n }\n}\n\n/**\n * Get the cache path for a specific repo ref\n */\nexport function getRepoCachePath(ref: RepoRef): string {\n const domain = getProviderDomain(ref.provider);\n const basePath = join(getCacheDir(), domain, ref.owner, ref.repo, ref.ref);\n\n // Include subpath in cache key if present\n if (ref.subpath) {\n return join(basePath, ref.subpath);\n }\n\n return basePath;\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 * Get the git repository URL for a given provider\n */\nfunction getRepoUrl(ref: RepoRef): string {\n switch (ref.provider) {\n case \"github\":\n return `https://github.com/${ref.owner}/${ref.repo}.git`;\n case \"gitlab\":\n return `https://gitlab.com/${ref.owner}/${ref.repo}.git`;\n case \"bitbucket\":\n return `https://bitbucket.org/${ref.owner}/${ref.repo}.git`;\n case \"sourcehut\":\n return `https://git.sr.ht/~${ref.owner}/${ref.repo}`;\n }\n}\n\n/**\n * Clone a repository to cache\n */\nexport async function cloneRepo(ref: RepoRef): Promise<void> {\n const repoUrl = getRepoUrl(ref);\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.provider}:${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, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport yaml from \"js-yaml\";\nimport { log } from \"../ui\";\nimport type { LockfileData, LockfileRepoRecord } from \"./types\";\n\n/**\n * Get the path to the global lockfile\n */\nexport function getLockfilePath(): string {\n return join(homedir(), \".kly\", \"kly.lock.yaml\");\n}\n\n/**\n * Read the lockfile, creating an empty one if it doesn't exist\n */\nexport function readLockfile(): LockfileData {\n const lockfilePath = getLockfilePath();\n\n if (!existsSync(lockfilePath)) {\n return {\n lockfileVersion: 1,\n repositories: {},\n };\n }\n\n try {\n const content = readFileSync(lockfilePath, \"utf-8\");\n const data = yaml.load(content) as LockfileData;\n\n // Validate basic structure\n if (!data || typeof data !== \"object\") {\n throw new Error(\"Invalid lockfile format\");\n }\n\n // Ensure repositories field exists\n if (!data.repositories || typeof data.repositories !== \"object\") {\n data.repositories = {};\n }\n\n // Set lockfile version if missing\n if (!data.lockfileVersion) {\n data.lockfileVersion = 1;\n }\n\n return data;\n } catch (error) {\n log.warn(\n `Failed to read lockfile (${error instanceof Error ? error.message : \"unknown error\"}), creating new one`,\n );\n return {\n lockfileVersion: 1,\n repositories: {},\n };\n }\n}\n\n/**\n * Write the lockfile to disk\n */\nexport function writeLockfile(data: LockfileData): void {\n const lockfilePath = getLockfilePath();\n\n // Ensure directory exists\n mkdirSync(dirname(lockfilePath), { recursive: true });\n\n // Add header comment\n const header =\n \"# kly.lock.yaml - Unified version and security tracking\\n\" +\n \"# This file is auto-generated and auto-updated\\n\" +\n \"#\\n\" +\n \"# Format:\\n\" +\n \"# lockfileVersion: 1\\n\" +\n \"# repositories:\\n\" +\n \"# github.com/owner/repo@ref:\\n\" +\n \"# commitSha: abc123... # Git commit SHA\\n\" +\n \"# lastChecked: 2025-12-28... # Last update check\\n\" +\n \"# lastUpdated: 2025-12-27... # Last clone/update\\n\" +\n \"# integrityHash: sha384-xxx... # Code content hash\\n\" +\n \"# trusted: true # User trust status\\n\" +\n \"# trustedAt: 2025-12-27... # When trusted\\n\" +\n \"\\n\";\n\n const yamlContent = yaml.dump(data, {\n indent: 2,\n lineWidth: 100,\n noRefs: true,\n sortKeys: true,\n });\n\n writeFileSync(lockfilePath, header + yamlContent, \"utf-8\");\n}\n\n/**\n * Get a specific repository record from the lockfile\n */\nexport function getRepoRecord(url: string): LockfileRepoRecord | undefined {\n const lockfile = readLockfile();\n return lockfile.repositories[url];\n}\n\n/**\n * Update or add version information for a repository\n */\nexport function updateRepoRecord(\n url: string,\n commitSha: string,\n isUpdate = false,\n): void {\n const lockfile = readLockfile();\n const now = new Date().toISOString();\n const existing = lockfile.repositories[url];\n\n lockfile.repositories[url] = {\n // Version info\n commitSha,\n lastChecked: now,\n lastUpdated: isUpdate ? now : (existing?.lastUpdated ?? now),\n // Preserve security info if it exists\n integrityHash: existing?.integrityHash,\n trusted: existing?.trusted,\n trustedAt: existing?.trustedAt,\n };\n\n writeLockfile(lockfile);\n}\n\n/**\n * Update security information for a repository\n */\nexport function updateSecurityInfo(\n url: string,\n integrityHash: string,\n trusted: boolean,\n): void {\n const lockfile = readLockfile();\n const existing = lockfile.repositories[url];\n const now = new Date().toISOString();\n\n if (!existing) {\n // If no version info exists, create minimal record\n lockfile.repositories[url] = {\n commitSha: \"\",\n lastChecked: now,\n lastUpdated: now,\n integrityHash,\n trusted,\n trustedAt: now,\n };\n } else {\n // Update existing record\n lockfile.repositories[url] = {\n ...existing,\n integrityHash,\n trusted,\n trustedAt: now,\n };\n }\n\n writeLockfile(lockfile);\n}\n\n/**\n * Get integrity hash for a repository\n */\nexport function getIntegrityHash(url: string): string | undefined {\n const record = getRepoRecord(url);\n return record?.integrityHash;\n}\n\n/**\n * Check if a repository is trusted\n */\nexport function isTrusted(url: string): boolean {\n const record = getRepoRecord(url);\n return record?.trusted ?? false;\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: convention candidates > main field\n *\n * We prioritize convention-based source files over package.json main field\n * because remote apps are run from source, not from built artifacts.\n *\n * @param repoPath - The root path of the cloned repository\n * @param subpath - Optional subpath within the repository to search for entry point\n */\nexport function resolveEntryPoint(\n repoPath: string,\n subpath?: string,\n): string | null {\n // If subpath is specified, search within that subpath\n const searchPath = subpath ? join(repoPath, subpath) : repoPath;\n\n // First, try convention candidates (source files)\n for (const candidate of ENTRY_CANDIDATES) {\n const candidatePath = join(searchPath, candidate);\n if (existsSync(candidatePath)) {\n // Return relative path from repo root\n return subpath ? join(subpath, candidate) : candidate;\n }\n }\n\n // Fallback to package.json main field\n const pkgPath = join(searchPath, \"package.json\");\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(searchPath, pkg.main);\n if (existsSync(mainPath)) {\n // Return relative path from repo root\n return subpath ? join(subpath, pkg.main) : pkg.main;\n }\n }\n } catch {\n // Invalid package.json, ignore\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 { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { log, output, select } from \"../ui\";\nimport { isTTY } from \"../ui/utils/tty\";\nimport { updateRepoRecord } from \"./lockfile\";\nimport type {\n CacheMetadata,\n RepoRef,\n UpdateCheckResult,\n UpdateChoice,\n} from \"./types\";\n\nconst execAsync = promisify(exec);\n\n/**\n * Determine if we should check for updates for this ref\n * Skip for tags and commit SHAs (immutable refs)\n */\nexport function shouldCheckForUpdates(ref: RepoRef): boolean {\n // Skip if it looks like a semver tag (v1.0.0, v2.3.1, 1.0.0, etc.)\n if (/^v?\\d+\\.\\d+/.test(ref.ref)) {\n return false;\n }\n\n // Skip if it's a commit SHA (7-40 hex characters)\n if (/^[a-f0-9]{7,40}$/i.test(ref.ref)) {\n return false;\n }\n\n // Check for branch refs (everything else)\n return true;\n}\n\n/**\n * Get the git repository URL for git ls-remote\n */\nfunction getGitRemoteUrl(ref: RepoRef): string {\n switch (ref.provider) {\n case \"github\":\n return `https://github.com/${ref.owner}/${ref.repo}.git`;\n case \"gitlab\":\n return `https://gitlab.com/${ref.owner}/${ref.repo}.git`;\n case \"bitbucket\":\n return `https://bitbucket.org/${ref.owner}/${ref.repo}.git`;\n case \"sourcehut\":\n return `https://git.sr.ht/~${ref.owner}/${ref.repo}`;\n }\n}\n\n/**\n * Get the remote commit SHA for a specific ref using git ls-remote\n * Returns null if network request fails\n */\nexport async function getRemoteCommitSha(ref: RepoRef): Promise<string | null> {\n try {\n const remoteUrl = getGitRemoteUrl(ref);\n const { stdout } = await execAsync(\n `git ls-remote ${remoteUrl} ${ref.ref}`,\n { timeout: 10000 }, // 10s timeout\n );\n\n // Output format: \"abc123def456...\\trefs/heads/main\" or \"abc123def456...\\tHEAD\"\n const sha = stdout.trim().split(/\\s+/)[0];\n return sha || null;\n } catch {\n // Network error or ref doesn't exist - return null to allow fallback to cache\n return null;\n }\n}\n\n/**\n * Get the compare URL for viewing changes between commits\n */\nfunction getCompareUrl(ref: RepoRef, from: string, to: string): string {\n const fromShort = from.slice(0, 12);\n const toShort = to.slice(0, 12);\n\n switch (ref.provider) {\n case \"github\":\n return `https://github.com/${ref.owner}/${ref.repo}/compare/${fromShort}...${toShort}`;\n case \"gitlab\":\n return `https://gitlab.com/${ref.owner}/${ref.repo}/-/compare/${fromShort}...${toShort}`;\n case \"bitbucket\":\n return `https://bitbucket.org/${ref.owner}/${ref.repo}/branches/compare/${toShort}..${fromShort}`;\n case \"sourcehut\":\n return `https://git.sr.ht/~${ref.owner}/${ref.repo}/log/${ref.ref}`;\n }\n}\n\n/**\n * Prompt user to choose what to do when an update is available\n */\nexport async function promptUserForUpdate(\n ref: RepoRef,\n localSha: string,\n remoteSha: string,\n): Promise<UpdateChoice> {\n const repoName = `${ref.provider}:${ref.owner}/${ref.repo}#${ref.ref}`;\n\n output(`📦 Update available for ${repoName}`);\n output(`Local: ${localSha.slice(0, 12)}`);\n output(`Remote: ${remoteSha.slice(0, 12)}`);\n output(`View changes: ${getCompareUrl(ref, localSha, remoteSha)}`);\n\n const choice = await select<UpdateChoice>({\n prompt: \"What would you like to do?\",\n options: [\n {\n name: \"Update and run\",\n value: \"update\",\n description: \"Download the latest version and run it\",\n },\n {\n name: \"Use current version\",\n value: \"use-current\",\n description: \"Run the cached version\",\n },\n {\n name: \"Cancel\",\n value: \"cancel\",\n description: \"Exit without running\",\n },\n ],\n });\n\n return choice;\n}\n\n/**\n * Check if an update is available for a cached repository\n * Returns update check result with user's choice\n */\nexport async function checkForUpdates(\n ref: RepoRef,\n metadata: CacheMetadata,\n): Promise<UpdateCheckResult> {\n // Use same URL format as in remote/index.ts\n const domain =\n ref.provider === \"github\"\n ? \"github.com\"\n : ref.provider === \"gitlab\"\n ? \"gitlab.com\"\n : ref.provider === \"bitbucket\"\n ? \"bitbucket.org\"\n : \"sr.ht\";\n const url = `${domain}/${ref.owner}/${ref.repo}@${ref.ref}`;\n const localSha = metadata.commitSha;\n\n // Skip update check for immutable refs (tags, commit SHAs)\n if (!shouldCheckForUpdates(ref)) {\n return {\n hasUpdate: false,\n localSha,\n shouldUpdate: false,\n skipCheck: true,\n };\n }\n\n // Try to get remote commit SHA\n const remoteSha = await getRemoteCommitSha(ref);\n\n // Network error - fallback to using cache with warning\n if (!remoteSha) {\n if (isTTY()) {\n log.warn(\n `⚠️ Unable to check for updates (network error), using cached version`,\n );\n }\n return {\n hasUpdate: false,\n localSha,\n shouldUpdate: false,\n skipCheck: true,\n };\n }\n\n // No update available - SHAs match\n if (remoteSha === localSha) {\n // Update lastChecked in lockfile\n updateRepoRecord(url, localSha, false);\n\n return {\n hasUpdate: false,\n localSha,\n remoteSha,\n shouldUpdate: false,\n };\n }\n\n // Update available - prompt user if in TTY mode\n if (!isTTY()) {\n // Non-interactive mode - log warning but use cached version\n log.warn(\n `Update available for ${ref.owner}/${ref.repo}@${ref.ref} ` +\n `(${localSha.slice(0, 12)} → ${remoteSha.slice(0, 12)}). ` +\n `Using cached version. Run 'kly run ${url} --force' to update.`,\n );\n return {\n hasUpdate: true,\n localSha,\n remoteSha,\n shouldUpdate: false,\n };\n }\n\n // Interactive mode - ask user\n const choice = await promptUserForUpdate(ref, localSha, remoteSha);\n\n switch (choice) {\n case \"cancel\":\n // User cancelled - return with shouldUpdate: false but don't treat as error\n return {\n hasUpdate: true,\n localSha,\n remoteSha,\n shouldUpdate: false,\n skipCheck: false,\n };\n\n case \"update\":\n // User chose to update\n return {\n hasUpdate: true,\n localSha,\n remoteSha,\n shouldUpdate: true,\n };\n\n case \"use-current\":\n // Update lastChecked in lockfile (user explicitly chose current version)\n updateRepoRecord(url, localSha, false);\n\n return {\n hasUpdate: true,\n localSha,\n remoteSha,\n shouldUpdate: false,\n };\n }\n}\n","import { join } from \"node:path\";\nimport { ENV_VARS } from \"../shared/constants\";\nimport { ExitError, ExitWarning } from \"../shared/errors\";\nimport { confirm, error, log, output } from \"../ui\";\nimport { checkCache, invalidateCache, writeMetadata } from \"./cache\";\nimport { cloneRepo, getCommitSha, installDependencies } from \"./fetcher\";\nimport { calculateRepoHash } from \"./integrity\";\nimport {\n getIntegrityHash,\n updateRepoRecord,\n updateSecurityInfo,\n} from \"./lockfile\";\nimport { getRepoCachePath, parseRemoteRef } from \"./parser\";\nimport {\n checkEnvVars,\n readKlyConfig,\n resolveEntryPoint,\n validateVersion,\n} from \"./resolver\";\nimport type { IntegrityCheckResult, RepoRef, RunRemoteOptions } from \"./types\";\nimport { checkForUpdates } from \"./update-checker\";\n\n/** Current kly CLI version */\nconst KLY_VERSION = __VERSION__;\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 // 2.5. Check for updates if cache is valid\n let needsUpdate = false;\n if (cacheResult.valid && !options.force && !options.skipUpdateCheck) {\n const updateResult = await checkForUpdates(ref, cacheResult.metadata!);\n\n if (updateResult.hasUpdate && updateResult.shouldUpdate) {\n // User chose to update\n needsUpdate = true;\n log.step(`Updating ${ref.owner}/${ref.repo}@${ref.ref}...`);\n } else if (updateResult.hasUpdate && !updateResult.shouldUpdate) {\n // User chose to use current version or cancelled\n if (updateResult.skipCheck === false) {\n // User explicitly cancelled - exit\n throw new ExitWarning(\"Cancelled\");\n }\n // Otherwise, continue with cached version\n }\n }\n\n if (!cacheResult.valid || options.force || needsUpdate) {\n // 3. Clone repository\n if (options.force && cacheResult.exists) {\n log.step(`Refreshing ${ref.owner}/${ref.repo}@${ref.ref}...`);\n invalidateCache(ref);\n } else {\n log.step(`Fetching ${ref.owner}/${ref.repo}@${ref.ref}...`);\n }\n\n await cloneRepo(ref);\n\n // 4. Resolve entry point\n const entryPoint = resolveEntryPoint(repoPath, ref.subpath);\n if (!entryPoint) {\n throw new Error(\n `No entry point found in ${ref.provider}:${ref.owner}/${ref.repo}${ref.subpath ? `/${ref.subpath}` : \"\"}. Set \"main\" in package.json or create index.ts`,\n );\n }\n\n // 5. Install dependencies\n if (!options.skipInstall) {\n log.step(\"Installing dependencies...\");\n await installDependencies(repoPath);\n }\n\n // 5.5. Auto-register bin commands (if any)\n if (!options.skipRegister) {\n const { autoRegisterBins } = await import(\"../bin-registry\");\n await autoRegisterBins(repoPath, {\n type: \"remote\",\n remoteRef: formatRepoUrl(ref),\n skipConfirm: false, // Always ask for remote projects\n });\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 // Update lockfile (use consistent URL format)\n const url = formatRepoUrl(ref);\n updateRepoRecord(url, commitSha, true);\n\n log.success(\"Ready!\");\n }\n\n // 7. Integrity verification\n if (!options.skipIntegrityCheck) {\n const integrityResult = await verifyIntegrity(ref, repoPath);\n\n if (!integrityResult.proceedWithExecution) {\n throw new ExitError(\n \"Execution cancelled due to integrity verification failure\",\n );\n }\n }\n\n // 8. Validate and execute\n await executeApp(ref, repoPath, options.args ?? [], options.mcp ?? false);\n}\n\n/**\n * Provider to domain mapping\n */\nconst PROVIDER_DOMAINS: Record<RepoRef[\"provider\"], string> = {\n github: \"github.com\",\n gitlab: \"gitlab.com\",\n bitbucket: \"bitbucket.org\",\n sourcehut: \"sr.ht\",\n};\n\n/**\n * Format a repository reference as a URL string for lockfile\n */\nfunction formatRepoUrl(ref: RepoRef): string {\n const domain = PROVIDER_DOMAINS[ref.provider];\n const base = `${domain}/${ref.owner}/${ref.repo}@${ref.ref}`;\n return ref.subpath ? `${base}/${ref.subpath}` : base;\n}\n\n/**\n * Get the web URL for a repository (for viewing in browser)\n */\nfunction getRepoWebUrl(\n ref: RepoRef,\n path: \"tree\" | \"commits\" = \"tree\",\n): string {\n switch (ref.provider) {\n case \"github\":\n return `https://github.com/${ref.owner}/${ref.repo}/${path}/${ref.ref}`;\n case \"gitlab\":\n return `https://gitlab.com/${ref.owner}/${ref.repo}/-/${path}/${ref.ref}`;\n case \"bitbucket\":\n return `https://bitbucket.org/${ref.owner}/${ref.repo}/src/${ref.ref}`;\n case \"sourcehut\":\n return `https://git.sr.ht/~${ref.owner}/${ref.repo}/tree/${ref.ref}`;\n }\n}\n\n/**\n * Verify repository integrity using lockfile\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 = formatRepoUrl(ref);\n\n log.step(\"Verifying code integrity...\");\n\n // Calculate repository hash\n const hash = calculateRepoHash(repoPath);\n output(`Hash: ${hash.slice(0, 20)}...`);\n\n // Check against lockfile\n const existingHash = getIntegrityHash(url);\n\n // Determine verification result\n let verifyResult: \"ok\" | \"mismatch\" | \"new\";\n if (!existingHash) {\n verifyResult = \"new\";\n } else if (existingHash === hash) {\n verifyResult = \"ok\";\n } else {\n verifyResult = \"mismatch\";\n }\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 log.success(\"Integrity verified\");\n return { proceedWithExecution: true, result };\n }\n\n case \"new\": {\n // First time running this version\n log.warn(\"SECURITY NOTICE: First time running this tool\");\n output(\"This code has not been verified before.\");\n output(\"Please review the source code before proceeding:\");\n output(getRepoWebUrl(ref));\n\n const shouldTrust = await confirm(\n \"Do you trust this code and want to proceed?\",\n );\n\n if (shouldTrust) {\n updateSecurityInfo(url, hash, true);\n log.success(\"Code trusted and added to lockfile\");\n return { proceedWithExecution: true, result };\n }\n\n output(\"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 result.expectedHash = existingHash;\n\n log.warn(\"SECURITY WARNING: Code has been modified!\");\n output(\"The code for this tool has changed since you last ran it.\");\n output(\"This could indicate:\");\n output(\" - A supply chain attack (code tampering)\");\n output(\" - Maintainer account compromise\");\n output(\" - Git history rewrite\");\n\n output(`Expected hash: ${result.expectedHash?.slice(0, 40)}...`);\n output(`Current hash: ${hash.slice(0, 40)}...`);\n\n output(\"Recommended actions:\");\n output(\" 1. Check the repository for official announcements\");\n output(\" 2. Contact the maintainer\");\n output(\" 3. Review code changes carefully\");\n output(` 4. Visit: ${getRepoWebUrl(ref, \"commits\")}`);\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 lockfile with new hash?\",\n false,\n );\n\n if (shouldUpdate) {\n updateSecurityInfo(url, hash, true);\n log.success(\"Lockfile updated with new hash\");\n }\n\n return { proceedWithExecution: true, result };\n }\n\n output(\"Execution cancelled for safety\");\n return { proceedWithExecution: false, result };\n }\n\n default: {\n // Should never reach here, but TypeScript requires it\n 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 log.warn(`Required environment variables not set: ${missing.join(\", \")}`);\n }\n }\n\n // Resolve entry point\n const entryPoint = resolveEntryPoint(repoPath, ref.subpath);\n if (!entryPoint) {\n throw new Error(\n `Cannot resolve entry point for ${ref.provider}:${ref.owner}/${ref.repo}`,\n );\n }\n\n const absoluteEntryPath = join(repoPath, entryPoint);\n\n // Set remote ref environment variable for permission tracking\n const remoteRef = formatRepoUrl(ref);\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 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 output(\"This app requests the following permissions:\");\n for (const item of summary) {\n output(item);\n }\n }\n }\n\n // Check permissions based on what's declared\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 throw new ExitError(\"Permission denied: API key access rejected\");\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 throw new ExitError(\n \"Permission denied: Sandbox configuration rejected\",\n );\n }\n }\n\n // Ensure remote app directory is accessible in sandbox\n // This is needed for module resolution and file access\n if (!sandboxConfig.filesystem.allowWrite.includes(repoPath)) {\n sandboxConfig.filesystem.allowWrite.push(repoPath);\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 log.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 invokeDir: process.cwd(), // Capture where kly run was invoked\n sandboxConfig,\n allowApiKey,\n });\n\n if (result.error) {\n error(result.error);\n }\n\n if (result.exitCode !== 0) {\n throw new ExitError(\"\", 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","import { resolve } from \"node:path\";\nimport { isRemoteRef } from \"../remote\";\nimport { ExitError } from \"../shared/errors\";\nimport { log } from \"../ui\";\nimport {\n autoRegisterBins,\n checkPathSetup,\n detectBins,\n listCommands,\n setupPath,\n unregisterCommand,\n} from \".\";\n\nexport async function installCommand(args: string[]): Promise<void> {\n const target = args[0];\n\n if (!target) {\n throw new ExitError(\n \"Missing target\\nUsage: kly install <file|user/repo[@ref]>\\n or: kly install --setup-path\",\n );\n }\n\n if (target === \"--setup-path\") {\n await setupPath();\n return;\n }\n\n // Install is essentially \"run + force register\"\n if (isRemoteRef(target)) {\n const { runRemote } = await import(\"../remote\");\n await runRemote(target, {\n args: args.slice(1),\n skipRegister: false, // Ensure registration happens\n });\n } else {\n // For local projects, just link them directly\n const absolutePath = resolve(process.cwd(), target);\n const detection = detectBins(absolutePath);\n\n if (!detection.hasBin) {\n log.warn(\"No bin field found in package.json\");\n log.message(\"This project cannot be installed as a command\");\n return;\n }\n\n log.step(`Installing ${detection.projectName}...`);\n\n await autoRegisterBins(absolutePath, {\n type: \"local\",\n force: true,\n skipConfirm: false, // Ask user for confirmation\n });\n\n log.success(\"Installed successfully!\");\n }\n}\n\nexport async function uninstallCommand(args: string[]): Promise<void> {\n const commandName = args[0];\n\n if (!commandName) {\n throw new ExitError(\n \"Missing command name\\nUsage: kly uninstall <command-name>\",\n );\n }\n\n await unregisterCommand(commandName, { skipConfirm: false });\n}\n\nexport async function linkCommand(args: string[]): Promise<void> {\n // Link is for local development - like npm link\n const targetPath = args[0] || process.cwd();\n const absolutePath = resolve(process.cwd(), targetPath);\n\n const detection = detectBins(absolutePath);\n\n if (!detection.hasBin) {\n throw new ExitError(\"No bin field found in package.json\");\n }\n\n log.step(`Linking ${detection.projectName}...`);\n\n await autoRegisterBins(absolutePath, {\n type: \"local\",\n force: true,\n skipConfirm: true, // Link is explicit, no need to ask\n });\n\n log.success(\"Linked successfully!\");\n}\n\nexport async function listCommand(): Promise<void> {\n const commands = listCommands();\n\n if (commands.length === 0) {\n log.message(\"No commands registered\");\n log.info(\"\\nRun 'kly install <target>' to register commands\");\n return;\n }\n\n log.info(`\\nRegistered commands (${commands.length}):\\n`);\n\n for (const cmd of commands) {\n const source = cmd.type === \"remote\" ? cmd.remoteRef : cmd.localPath;\n\n log.message(\n ` ${cmd.commandName.padEnd(20)} ${cmd.projectName}@${cmd.projectVersion}`,\n );\n log.message(` ${\" \".repeat(20)} ${source}`);\n log.message(\"\");\n }\n\n checkPathSetup();\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 /** Working directory where `kly run` was invoked */\n invokeDir: 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 /** True if this is a user cancellation, not an error */\n cancelled?: boolean;\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 { getCurrentModelConfig, listModels } from \"../ai/storage\";\nimport type {\n IPCRequest,\n IPCResponse,\n ModelConfigResponse,\n ModelInfoResponse,\n} from \"../shared/ipc-protocol\";\nimport {\n colors,\n error,\n log,\n output,\n rawConfirm,\n rawIsCancel,\n rawMultiselect,\n rawSelect,\n rawText,\n} from \"../ui\";\n\nexport interface ResourceProviderOptions {\n appId: string;\n allowApiKey: boolean;\n sandboxConfig: SandboxRuntimeConfig;\n}\n\n/**\n * Helper to handle raw prompt results and convert to IPC responses\n */\nfunction _handleRawPromptResult<T>(\n requestId: string,\n result: T | symbol,\n): IPCResponse<T> {\n if (rawIsCancel(result)) {\n return _cancelledResponse(requestId);\n }\n\n return {\n type: \"response\",\n id: requestId,\n success: true,\n data: result,\n };\n}\n\n/**\n * Helper to create a cancelled IPC response\n */\nfunction _cancelledResponse<T>(requestId: string): IPCResponse<T> {\n return {\n type: \"response\",\n id: requestId,\n success: false,\n error: \"Operation cancelled\",\n cancelled: true,\n };\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 log.info(`${prefix} ${message}`);\n break;\n case \"warn\":\n log.warn(`${prefix} ${message}`);\n break;\n case \"error\":\n 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 rawText({\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 return _handleRawPromptResult(requestId, result);\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 rawSelect({\n message: payload.prompt,\n options: mappedOptions,\n });\n\n return _handleRawPromptResult(requestId, result as string);\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 rawConfirm({\n message: payload.message,\n initialValue: payload.defaultValue,\n });\n\n return _handleRawPromptResult(requestId, result);\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 rawMultiselect({\n message: payload.prompt,\n options: mappedOptions,\n required: payload.required,\n });\n\n return _handleRawPromptResult(requestId, result as string[]);\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 output(colors.bold(payload.title));\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 rawConfirm({\n message: label,\n initialValue: field.defaultValue as boolean | undefined,\n });\n\n if (rawIsCancel(value)) {\n return _cancelledResponse(requestId);\n }\n\n result[field.name] = value;\n } else if (field.type === \"enum\" && field.enumValues?.length) {\n const value = await rawSelect({\n message: label,\n options: field.enumValues.map((v) => ({\n label: v,\n value: v,\n })),\n });\n\n if (rawIsCancel(value)) {\n return _cancelledResponse(requestId);\n }\n\n result[field.name] = value;\n } else if (field.type === \"number\") {\n const strValue = await rawText({\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 (rawIsCancel(strValue)) {\n return _cancelledResponse(requestId);\n }\n\n result[field.name] = Number.parseFloat(strValue);\n } else {\n const value = await rawText({\n message: label,\n defaultValue: field.defaultValue as string | undefined,\n });\n\n if (rawIsCancel(value)) {\n return _cancelledResponse(requestId);\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 { log } from \"../ui\";\nimport { createResourceProvider } from \"./resource-provider\";\n\nexport interface LaunchOptions {\n scriptPath: string;\n args: string[];\n appId: string;\n /** Working directory where `kly run` was invoked */\n invokeDir: 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, invokeDir, sandboxConfig, allowApiKey } =\n options;\n\n // Initialize sandbox manager\n await SandboxManager.initialize(sandboxConfig);\n\n // Resolve paths\n const absoluteScriptPath = resolve(process.cwd(), scriptPath);\n const _scriptDir = absoluteScriptPath.substring(\n 0,\n absoluteScriptPath.lastIndexOf(\"/\"),\n );\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 log.warn(\n \"Sandboxing is not supported on this platform. Running without OS-level isolation.\",\n );\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 cwd: _scriptDir, // Set working directory to script's directory for module resolution\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 invokeDir,\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","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 { error, log, output, 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 (err) {\n error(`Failed to parse permissions file: ${err}`);\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 error(\n `Permission required: App \"${appName}\" (${appId}) wants to access your API keys.`,\n );\n error(\n \"Set KLY_TRUST_ALL=true environment variable to grant access in non-interactive mode.\",\n );\n return false;\n }\n\n output(`App \"${appName}\" is requesting access to your API keys.`);\n output(`Source: ${appId}`);\n output(\"This will allow the app to use your configured LLM models.\");\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 error(`Sandbox permission required for: \"${appName}\" (${appId})`);\n 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 output(`🔐 Sandbox Permission Request from: ${appName}`);\n\n // Ask for filesystem read permissions\n output(\"📂 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 output(\"📝 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 output(\"🌐 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 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 log.success(\"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, log, 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 log.info(\"No permissions granted yet.\");\n return;\n }\n\n log.info(\"📋 Granted Permissions:\");\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\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 log.info(\"No permissions to revoke.\");\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 log.success(\"Permission revoked.\");\n } else {\n log.warn(\"Cancelled.\");\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 log.success(\"All permissions cleared.\");\n } else {\n log.warn(\"Cancelled.\");\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/config\"), // KLY config directory\n join(homedir(), \".kly/permissions.json\"), // Permissions file\n join(homedir(), \".kly/kly.sum\"), // Integrity checksums\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/config\"), // KLY config directory\n join(homedir(), \".kly/permissions.json\"), // Permissions file (prevent reading)\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 allowPty: true, // Enable pseudo-terminal support for interactive prompts\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\";\nimport { log } from \"../ui\";\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 (err) {\n // If extraction fails, we'll ask for permissions interactively\n log.warn(\n `Could not extract permissions from ${scriptPath}: ${err instanceof Error ? err.message : String(err)}`,\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 { error, output, 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 error(\n `Permission required: App \"${appName}\" (${appId}) requests permissions.`,\n );\n 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 output(`App \"${appName}\" requests the following permissions:`);\n\n // Format and display permissions\n const summary = formatPermissionsSummary(appPermissions);\n for (const line of summary) {\n output(` ${line}`);\n }\n\n output(`Source: ${appId}`);\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","#!/usr/bin/env bun\nimport { dirname, resolve } from \"node:path\";\nimport type { SandboxRuntimeConfig } from \"@anthropic-ai/sandbox-runtime\";\nimport { modelsCommand } from \"../src/ai/models-command\";\nimport {\n autoRegisterBins,\n detectBins,\n getCommand,\n shouldReregisterLocal,\n} from \"../src/bin-registry\";\nimport {\n installCommand,\n linkCommand,\n listCommand,\n uninstallCommand,\n} from \"../src/bin-registry/commands\";\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\";\nimport { EXIT_CODES } from \"../src/shared/constants\";\nimport { ExitError, ExitWarning } from \"../src/shared/errors\";\nimport { cancel, colors, error, intro, log, outro } from \"../src/ui\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nasync function main() {\n intro(\n `${colors.bgHex(\"#dc7702\")(colors.black(` Kly ${colors.italic(__VERSION__)} `))}`,\n );\n\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 === \"install\") {\n await installCommand(args.slice(1));\n return;\n }\n\n if (command === \"uninstall\") {\n await uninstallCommand(args.slice(1));\n return;\n }\n\n if (command === \"link\") {\n await linkCommand(args.slice(1));\n return;\n }\n\n if (command === \"list\" || command === \"ls\") {\n await listCommand();\n return;\n }\n\n if (command === \"run\") {\n const target = args[1];\n if (!target) {\n throw new ExitError(\n \"Missing file path or remote reference\\nUsage: kly run <file|user/repo[@ref]>\",\n );\n }\n\n // Check for flags\n const forceIndex = args.indexOf(\"--force\");\n const force = forceIndex !== -1;\n const noUpdateCheckIndex = args.indexOf(\"--no-update-check\");\n const skipUpdateCheck = noUpdateCheckIndex !== -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\n .slice(2)\n .filter((arg) => arg !== \"--force\" && arg !== \"--no-update-check\");\n\n if (isRemoteRef(target)) {\n await runRemote(target, { args: appArgs, force, skipUpdateCheck });\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 throw new ExitError(\n \"Missing file path or remote reference\\nUsage: kly mcp <file|user/repo[@ref]>\",\n );\n }\n\n // Check for flags\n const forceIndex = args.indexOf(\"--force\");\n const force = forceIndex !== -1;\n const noUpdateCheckIndex = args.indexOf(\"--no-update-check\");\n const skipUpdateCheck = noUpdateCheckIndex !== -1;\n\n if (isRemoteRef(target)) {\n await runRemote(target, { args: [], force, skipUpdateCheck, mcp: true });\n } else {\n await runFileAsMcp(target);\n }\n return;\n }\n\n throw new ExitError(\n `Unknown command: ${command}\\nRun \"kly --help\" for usage`,\n );\n}\n\nasync function runFile(filePath: string, appArgs: string[]) {\n const absolutePath = resolve(process.cwd(), filePath);\n // Capture the working directory where kly run was invoked\n const invokeDir = process.cwd();\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 const allowed = await requestUnifiedPermission(\n appId,\n appPermissions,\n sandboxConfig,\n );\n\n if (!allowed) {\n throw new ExitError(\"Permission denied\");\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 invokeDir,\n sandboxConfig,\n allowApiKey,\n });\n\n if (result.error) {\n error(result.error);\n }\n\n if (result.exitCode === 0) {\n // Auto-register bin commands for local projects\n const projectPath = dirname(absolutePath);\n const detection = detectBins(projectPath);\n\n if (detection.hasBin) {\n // Check if any commands need re-registration (local project code changed)\n let needsUpdate = false;\n for (const [cmdName] of Object.entries(detection.bins)) {\n if (shouldReregisterLocal(cmdName, projectPath)) {\n needsUpdate = true;\n break;\n }\n }\n\n if (needsUpdate) {\n // Auto-update without asking\n await autoRegisterBins(projectPath, {\n type: \"local\",\n force: true,\n skipConfirm: true,\n });\n } else {\n // Check if this is the first time (not registered yet)\n const firstBinCmd = Object.keys(detection.bins)[0];\n if (firstBinCmd) {\n const existing = getCommand(firstBinCmd);\n\n if (!existing) {\n // First time - ask user\n await autoRegisterBins(projectPath, {\n type: \"local\",\n skipConfirm: false,\n });\n }\n }\n }\n }\n }\n\n if (result.exitCode === EXIT_CODES.CANCELLED) {\n // User cancelled in sandbox - propagate as ExitWarning (no message since sandbox already showed it)\n throw new ExitWarning(\"\");\n }\n\n if (result.exitCode !== 0) {\n throw new ExitError(\"\", 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 log.message(`Usage:\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 install <target> Install a Kly app as global command\n uninstall <cmd> Uninstall a registered global command\n link [path] Link a local project as global command\n list List all registered global commands\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 --no-update-check Skip checking for remote updates\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 kly install user/awesome-cli\n kly link ./my-tool\n kly list\n kly install --setup-path`);\n}\n\nfunction showVersion() {\n log.message(__VERSION__);\n}\n\nmain()\n .then(() => {\n outro(`ヾ( ̄▽ ̄)Bye~`);\n process.exit(0);\n })\n .catch((err) => {\n // Check for ExitWarning (user cancellation - graceful exit)\n const isExitWarning =\n err instanceof ExitWarning || err?.name === \"ExitWarning\";\n if (isExitWarning) {\n if (err.message) {\n cancel(err.message);\n }\n process.exit(0);\n }\n\n // Check for ExitError\n const isExitError = err instanceof ExitError || err?.name === \"ExitError\";\n const exitCode = isExitError ? (err.exitCode ?? 1) : 1;\n\n const message = typeof err === \"string\" ? err : err?.message || String(err);\n if (message) {\n error(message);\n }\n\n process.exit(exitCode);\n });\n"],"mappings":"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"}
|