openfox 0.1.0 → 0.2.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.
Files changed (61) hide show
  1. package/dist/agent-defaults/builder.agent.md +38 -0
  2. package/dist/agent-defaults/code-reviewer.agent.md +21 -0
  3. package/dist/agent-defaults/debugger.agent.md +21 -0
  4. package/dist/agent-defaults/planner.agent.md +35 -0
  5. package/dist/agent-defaults/test-generator.agent.md +22 -0
  6. package/dist/agent-defaults/verifier.agent.md +41 -0
  7. package/dist/{chunk-FF6Z2MC3.js → chunk-5ZUU23XF.js} +18 -1
  8. package/dist/{chunk-IVHCPQO2.js → chunk-ARFK3RZW.js} +1 -1
  9. package/dist/{chunk-IKAWIOXM.js → chunk-FUUYYDVG.js} +387 -73
  10. package/dist/{chunk-YQ36AK5A.js → chunk-JFQEATRC.js} +623 -327
  11. package/dist/{chunk-43SWNGYW.js → chunk-L475ND2Z.js} +9 -9
  12. package/dist/{chunk-6MOGZMGX.js → chunk-L4XDA3V7.js} +2 -2
  13. package/dist/{chunk-EI46G5WO.js → chunk-MKAHOQBI.js} +10 -10
  14. package/dist/cli/dev.js +1 -1
  15. package/dist/cli/index.js +1 -1
  16. package/dist/{config-6GJ4SVFH.js → config-BMXSDOZB.js} +3 -3
  17. package/dist/{init-UVZDOHMX.js → init-PLRTRSDC.js} +6 -6
  18. package/dist/{protocol-BJsm88OW.d.ts → protocol-BFFz3WM0.d.ts} +14 -3
  19. package/dist/{provider-LCE6WZA6.js → provider-ETVY64A2.js} +5 -5
  20. package/dist/{serve-27DZK34K.js → serve-EOKIXTWM.js} +8 -8
  21. package/dist/server/index.d.ts +1 -1
  22. package/dist/server/index.js +5 -5
  23. package/dist/shared/index.d.ts +2 -2
  24. package/dist/shared/index.js +1 -1
  25. package/dist/{tools-R4YQAYUF.js → tools-QMIIDJTV.js} +7 -5
  26. package/dist/web/assets/index-CAD7Iv_d.js +122 -0
  27. package/dist/web/assets/{index-BSlSWZrM.css → index-DHmZmhnR.css} +1 -1
  28. package/dist/web/index.html +2 -2
  29. package/package.json +34 -16
  30. package/dist/branch.api-WDHKZJS6.js.map +0 -1
  31. package/dist/chunk-26NRL3HX.js.map +0 -1
  32. package/dist/chunk-43SWNGYW.js.map +0 -1
  33. package/dist/chunk-4Z2OZ6NX.js.map +0 -1
  34. package/dist/chunk-6MOGZMGX.js.map +0 -1
  35. package/dist/chunk-AF7XS4OI.js.map +0 -1
  36. package/dist/chunk-EI46G5WO.js.map +0 -1
  37. package/dist/chunk-FF6Z2MC3.js.map +0 -1
  38. package/dist/chunk-IKAWIOXM.js.map +0 -1
  39. package/dist/chunk-IVHCPQO2.js.map +0 -1
  40. package/dist/chunk-MTX5Y3V3.js.map +0 -1
  41. package/dist/chunk-N2XYX4ZA.js.map +0 -1
  42. package/dist/chunk-QY7BMXWT.js.map +0 -1
  43. package/dist/chunk-U62ZECSQ.js.map +0 -1
  44. package/dist/chunk-YQ36AK5A.js.map +0 -1
  45. package/dist/cli/dev.js.map +0 -1
  46. package/dist/cli/index.js.map +0 -1
  47. package/dist/config-6GJ4SVFH.js.map +0 -1
  48. package/dist/init-UVZDOHMX.js.map +0 -1
  49. package/dist/paths-4BPXWB2W.js.map +0 -1
  50. package/dist/project-creator-CWPKRIKI.js.map +0 -1
  51. package/dist/provider-LCE6WZA6.js.map +0 -1
  52. package/dist/serve-27DZK34K.js.map +0 -1
  53. package/dist/server/index.js.map +0 -1
  54. package/dist/settings-D5O3TMLD.js.map +0 -1
  55. package/dist/shared/index.js.map +0 -1
  56. package/dist/tools-R4YQAYUF.js.map +0 -1
  57. package/dist/web/assets/index-BMNDCsbJ.js +0 -120
  58. /package/dist/command-defaults/{defaults/commit-push.command.md → commit-push.command.md} +0 -0
  59. /package/dist/command-defaults/{defaults/init.command.md → init.command.md} +0 -0
  60. /package/dist/command-defaults/{defaults/test-ui.command.md → test-ui.command.md} +0 -0
  61. /package/dist/skill-defaults/{defaults/playwright-cli.skill.md → playwright-cli.skill.md} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/dev.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runCli } from './main.js'\nimport { logger } from '../server/utils/logger.js'\nrunCli({ mode: 'development' }).catch((error) => {\n logger.error('CLI fatal error', { error: error instanceof Error ? error.message : String(error) })\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;AAGA,OAAO,EAAE,MAAM,cAAc,CAAC,EAAE,MAAM,CAAC,UAAU;AAC/C,SAAO,MAAM,mBAAmB,EAAE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,CAAC;AACjG,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runCli } from './main.js'\nimport { logger } from '../server/utils/logger.js'\nrunCli({ mode: 'production' }).catch((error) => {\n logger.error('CLI fatal error', { error: error instanceof Error ? error.message : String(error) })\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;AAGA,OAAO,EAAE,MAAM,aAAa,CAAC,EAAE,MAAM,CAAC,UAAU;AAC9C,SAAO,MAAM,mBAAmB,EAAE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,CAAC;AACjG,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/init.ts"],"sourcesContent":["import { select, text, spinner, outro, confirm } from '@clack/prompts'\nimport { detectBackend, detectModel } from '../server/llm/index.js'\nimport { saveGlobalConfig, addProvider, type GlobalConfig } from './config.js'\nimport type { Mode } from './main.js'\nimport type { ProviderBackend } from '../shared/types.js'\n\nconst LLM_OPTIONS = [\n { value: 'http://localhost:8000', label: 'http://localhost:8000' },\n { value: 'http://localhost:11434', label: 'http://localhost:11434' },\n { value: 'http://localhost:8080', label: 'http://localhost:8080' },\n { value: 'other', label: 'Other...' },\n]\n\nfunction createBaseConfig(): GlobalConfig {\n return {\n providers: [],\n activeProviderId: undefined,\n server: { port: 10369, host: '127.0.0.1', openBrowser: true },\n logging: { level: 'info' as const },\n database: { path: '' },\n workspace: { workdir: process.cwd() },\n }\n}\n\n/**\n * Prompt user for network accessibility preference\n * Returns '127.0.0.1' for localhost only, '0.0.0.0' for network access\n */\nasync function promptNetworkAccessibility(): Promise<string> {\n const networkChoice = await select({\n message: 'How should OpenFox be accessible?',\n options: [\n { value: 'localhost', label: 'Secure (localhost only)' },\n { value: 'network', label: 'Accessible from local network (phone, tablet, etc.)' },\n ],\n })\n \n // Default to secure if user skips\n if (networkChoice === Symbol.for('clack:cancel')) {\n return '127.0.0.1'\n }\n \n return networkChoice === 'network' ? '0.0.0.0' : '127.0.0.1'\n}\n\n/**\n * Run the init wizard with optional existing config\n * If existingConfig is provided, offer to preserve providers\n */\nexport async function runInitWithSelect(mode: Mode, existingConfig?: GlobalConfig): Promise<void> {\n // If existing config, ask about preserving providers\n let preserveProviders = false\n let config: GlobalConfig\n \n if (existingConfig && existingConfig.providers.length > 0) {\n console.log('\\nCurrent configuration detected:')\n console.log(` Providers: ${existingConfig.providers.length}`)\n existingConfig.providers.forEach((p, i) => {\n console.log(` ${i + 1}. ${p.name} (${p.url}) - ${p.model}`)\n })\n console.log(` Server: ${existingConfig.server.host}`)\n \n const keepChoice = await confirm({\n message: 'Keep existing providers?',\n initialValue: true,\n })\n \n if (keepChoice === true) {\n preserveProviders = true\n config = {\n ...existingConfig,\n server: { ...existingConfig.server }, // Copy server settings\n }\n console.log('\\nPreserving existing providers...\\n')\n } else {\n console.log('\\nStarting fresh configuration...\\n')\n config = createBaseConfig()\n }\n } else {\n config = createBaseConfig()\n }\n \n // Only run LLM setup if not preserving providers\n if (!preserveProviders) {\n const s = spinner()\n s.start('Searching for local LLM...')\n \n // Try smart defaults in parallel while showing spinner\n const detected = await Promise.all(\n LLM_OPTIONS.filter(o => o.value !== 'other').map(async ({ value: url }) => {\n try {\n const [backend, model] = await Promise.all([\n detectBackend(url, undefined, true),\n detectModel(url, 1, true),\n ])\n if (backend !== 'unknown' && model) {\n return { url, backend, model }\n }\n } catch {\n // Silent fail\n }\n return null\n })\n )\n \n const found = detected.find(r => r !== null)\n if (found) {\n s.stop(`✓ Found ${found.backend} (${found.model})`)\n config = addProvider(config, {\n name: 'Default',\n url: found.url,\n\n backend: found.backend as ProviderBackend,\n maxContext: 200000,\n isActive: true,\n })\n } else {\n s.stop('✗ No LLM server detected')\n \n const selection = await select({\n message: 'Select your LLM server:',\n options: LLM_OPTIONS,\n })\n \n let url: string\n if (String(selection) === 'other') {\n const textValue = await text({\n message: 'LLM Server URL (don\\'t include /v1)',\n placeholder: 'http://localhost:8000',\n initialValue: 'http://localhost:8000',\n validate: (value) => {\n if (!value || value.length === 0) return 'URL is required'\n if (!value.startsWith('http')) return 'Must start with http://'\n },\n })\n url = String(textValue)\n } else {\n url = String(selection)\n }\n \n // Test connection\n const s2 = spinner()\n s2.start(`Testing connection to ${url}...`)\n \n try {\n const backend = await detectBackend(url)\n const model = await detectModel(url)\n \n s2.stop(`Connected to ${backend}${model ? ' (' + model + ')' : ''}`)\n \n config = addProvider(config, {\n name: 'Default',\n url,\n backend: backend as ProviderBackend,\n maxContext: 200000,\n isActive: true,\n })\n // Set the default model selection\n const { setDefaultModelSelection } = await import('./config.js')\n config = setDefaultModelSelection(config, config.providers[config.providers.length - 1]!.id, model ?? 'auto')\n } catch {\n s2.stop('Server isn\\'t available')\n \n const choice = await select({\n message: 'Continue with this URL or retry?',\n options: [\n { value: 'continue', label: 'Continue' },\n { value: 'retry', label: 'Retry' },\n { value: 'change', label: 'Select different server' },\n ],\n })\n \n if (choice === 'retry') {\n return runInitWithSelect(mode, existingConfig) // Recurse to retry\n } else if (choice === 'change') {\n return runInitWithSelect(mode, existingConfig) // Recurse to change selection\n }\n // choice === 'continue' - save anyway with auto backend\n config = addProvider(config, {\n name: 'Default',\n url,\n backend: 'auto',\n maxContext: 200000,\n isActive: true,\n })\n // Set the default model selection\n const { setDefaultModelSelection } = await import('./config.js')\n config = setDefaultModelSelection(config, config.providers[config.providers.length - 1]!.id, 'auto')\n }\n }\n }\n \n // Ask about network accessibility\n const host = await promptNetworkAccessibility()\n config.server.host = host\n \n // Ask about workspace directory\n const workdirChoice = await text({\n message: 'Workspace directory for new projects',\n placeholder: 'Directory where new projects will be created',\n initialValue: config.workspace?.workdir || process.cwd(),\n validate: (value) => {\n if (!value || value.length === 0) return 'Workspace directory is required'\n },\n })\n // Normalize: remove trailing slash to prevent double slashes in paths\n config.workspace = { workdir: String(workdirChoice).replace(/\\/$/, '') }\n \n // If a provider was added and no default model selection exists, set it\n if (config.providers.length > 0 && !config.defaultModelSelection) {\n const { setDefaultModelSelection } = await import('./config.js')\n config = setDefaultModelSelection(config, config.providers[0]!.id, 'auto')\n }\n \n // Save the configuration\n await saveGlobalConfig(mode, config)\n \n outro('Configuration saved!')\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server/utils/project-creator.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises'\nimport { execSync } from 'node:child_process'\nimport { join } from 'node:path'\nimport { createProject as createProjectDb } from '../db/projects.js'\nimport type { Project } from '../../shared/types.js'\n\n/**\n * Validate a project name for safe directory creation.\n * Only allows alphanumeric characters, hyphens, underscores, and dots.\n */\nexport function validateProjectName(name: string): { valid: true } | { valid: false; error: string } {\n if (!name || name.trim() === '') {\n return { valid: false, error: 'Project name cannot be empty' }\n }\n \n // Check for valid characters (alphanumeric, hyphens, underscores, dots)\n const validPattern = /^[a-zA-Z0-9._-]+$/\n if (!validPattern.test(name)) {\n return { \n valid: false, \n error: 'Project name can only contain letters, numbers, hyphens, underscores, and dots' \n }\n }\n \n // Check for path traversal attempts\n if (name.includes('/') || name.includes('\\\\') || name.includes('..')) {\n return { valid: false, error: 'Project name cannot contain path separators' }\n }\n \n return { valid: true }\n}\n\n/**\n * Check if a directory already exists at the given path\n */\nexport async function directoryExists(path: string): Promise<boolean> {\n try {\n const { access } = await import('node:fs/promises')\n const { constants } = await import('node:fs')\n await access(path, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Create a new project directory with git initialization.\n * \n * @param projectName - The name of the project (will be prefixed with workdir)\n * @param workdir - The base directory where the project will be created\n * @returns The created project\n * @throws Error if creation fails\n */\nexport async function createDirectoryWithGit(projectName: string, workdir: string): Promise<Project> {\n // Validate project name\n const validation = validateProjectName(projectName)\n if (!validation.valid) {\n throw new Error(validation.error)\n }\n \n // Build full path\n const fullPath = join(workdir, projectName)\n \n // Check if directory already exists\n const exists = await directoryExists(fullPath)\n if (exists) {\n throw new Error(`Directory already exists: ${fullPath}. Please choose a different name or use the existing directory from Browse Projects.`)\n }\n \n // Create directory recursively\n try {\n await mkdir(fullPath, { recursive: true })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n throw new Error(`Failed to create directory: ${errorMessage}`)\n }\n \n // Initialize git repository\n try {\n execSync('git init', { cwd: fullPath, stdio: 'pipe' })\n } catch (error) {\n // Clean up the directory if git init fails\n const { rm } = await import('node:fs/promises')\n await rm(fullPath, { recursive: true, force: true })\n \n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n throw new Error(`Failed to initialize git repository: ${errorMessage}`)\n }\n \n // Register project in database\n const project = createProjectDb(projectName, fullPath)\n \n return project\n}\n"],"mappings":";;;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,gBAAgB;AACzB,SAAS,YAAY;AAQd,SAAS,oBAAoB,MAAiE;AACnG,MAAI,CAAC,QAAQ,KAAK,KAAK,MAAM,IAAI;AAC/B,WAAO,EAAE,OAAO,OAAO,OAAO,+BAA+B;AAAA,EAC/D;AAGA,QAAM,eAAe;AACrB,MAAI,CAAC,aAAa,KAAK,IAAI,GAAG;AAC5B,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,GAAG;AACpE,WAAO,EAAE,OAAO,OAAO,OAAO,8CAA8C;AAAA,EAC9E;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAKA,eAAsB,gBAAgB,MAAgC;AACpE,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,aAAkB;AAClD,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,IAAS;AAC5C,UAAM,OAAO,MAAM,UAAU,IAAI;AACjC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,eAAsB,uBAAuB,aAAqB,SAAmC;AAEnG,QAAM,aAAa,oBAAoB,WAAW;AAClD,MAAI,CAAC,WAAW,OAAO;AACrB,UAAM,IAAI,MAAM,WAAW,KAAK;AAAA,EAClC;AAGA,QAAM,WAAW,KAAK,SAAS,WAAW;AAG1C,QAAM,SAAS,MAAM,gBAAgB,QAAQ;AAC7C,MAAI,QAAQ;AACV,UAAM,IAAI,MAAM,6BAA6B,QAAQ,sFAAsF;AAAA,EAC7I;AAGA,MAAI;AACF,UAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,EAC3C,SAAS,OAAO;AACd,UAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,UAAM,IAAI,MAAM,+BAA+B,YAAY,EAAE;AAAA,EAC/D;AAGA,MAAI;AACF,aAAS,YAAY,EAAE,KAAK,UAAU,OAAO,OAAO,CAAC;AAAA,EACvD,SAAS,OAAO;AAEd,UAAM,EAAE,GAAG,IAAI,MAAM,OAAO,aAAkB;AAC9C,UAAM,GAAG,UAAU,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAEnD,UAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,UAAM,IAAI,MAAM,wCAAwC,YAAY,EAAE;AAAA,EACxE;AAGA,QAAM,UAAU,cAAgB,aAAa,QAAQ;AAErD,SAAO;AACT;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/provider.ts"],"sourcesContent":["import { select, text, password, spinner, log, outro, isCancel, cancel } from '@clack/prompts'\nimport { detectBackend, detectModel } from '../server/llm/index.js'\nimport { fetchAvailableModelsFromBackend } from '../server/provider-manager.js'\nimport {\n loadGlobalConfig,\n saveGlobalConfig,\n addProvider,\n removeProvider,\n activateProvider,\n getActiveProvider,\n} from './config.js'\nimport type { Mode } from './main.js'\nimport type { ProviderBackend } from '../shared/types.js'\n\nconst BACKEND_OPTIONS = [\n { value: 'auto', label: 'Auto-detect' },\n { value: 'vllm', label: 'vLLM' },\n { value: 'sglang', label: 'SGLang' },\n { value: 'ollama', label: 'Ollama' },\n { value: 'llamacpp', label: 'llama.cpp' },\n { value: 'openai', label: 'OpenAI' },\n { value: 'anthropic', label: 'Anthropic' },\n]\n\nexport function printProviderHelp(): void {\n console.log(`\nProvider Management Commands:\n\n openfox provider add Add a new provider\n openfox provider list List all configured providers\n openfox provider use Switch to a different provider\n openfox provider remove Remove a provider\n`)\n}\n\nexport async function runProviderAdd(mode: Mode): Promise<void> {\n const config = await loadGlobalConfig(mode)\n\n // Provider name\n const name = await text({\n message: 'Provider name:',\n placeholder: 'My Local vLLM',\n validate: (value) => {\n if (!value || value.length === 0) return 'Name is required'\n if (config.providers.some(p => p.name === value)) return 'Provider with this name already exists'\n },\n })\n if (isCancel(name)) {\n cancel('Cancelled')\n return\n }\n\n // URL with examples\n log.info(`URL examples:\n vLLM/SGLang: http://localhost:8000\n Ollama: http://localhost:11434\n llama.cpp: http://localhost:8080\n OpenAI: https://api.openai.com\n Anthropic: https://api.anthropic.com\n \n (Don't include /v1 - it's added automatically)`)\n \n const url = await text({\n message: 'API URL:',\n placeholder: 'http://localhost:8000',\n initialValue: 'http://localhost:8000',\n validate: (value) => {\n if (!value || value.length === 0) return 'URL is required'\n if (!value.startsWith('http')) return 'Must start with http:// or https://'\n },\n })\n if (isCancel(url)) {\n cancel('Cancelled')\n return\n }\n\n // Backend\n const backend = await select({\n message: 'Backend type:',\n options: BACKEND_OPTIONS,\n })\n if (isCancel(backend)) {\n cancel('Cancelled')\n return\n }\n\n // Model selection - fetch available models from backend\n let selectedModel: string\n const s = spinner()\n s.start(`Fetching available models from ${url}...`)\n\n let availableModels: string[] = []\n let detectedModel: string | null = null\n\n try {\n // Detect backend first if needed\n let detectedBackend = backend as string\n if (backend === 'auto') {\n detectedBackend = await detectBackend(url as string)\n }\n\n // Fetch available models\n availableModels = await fetchAvailableModelsFromBackend(url as string)\n s.stop(`Found ${availableModels.length} model(s)`)\n } catch {\n s.stop('⚠ Could not fetch models, will use auto-detect')\n }\n\n if (availableModels.length > 0) {\n // Show dropdown of available models\n const modelChoice = await select({\n message: 'Select model:',\n options: availableModels.map(m => ({\n value: m,\n label: m.split('/').pop() ?? m,\n hint: m,\n })),\n })\n if (isCancel(modelChoice)) {\n cancel('Cancelled')\n return\n }\n selectedModel = modelChoice as string\n } else {\n // Fall back to text input\n const model = await text({\n message: 'Model name (or \"auto\" to detect):',\n placeholder: 'auto',\n initialValue: 'auto',\n })\n if (isCancel(model)) {\n cancel('Cancelled')\n return\n }\n selectedModel = model as string\n\n // Try to detect model if auto\n if (selectedModel === 'auto') {\n const detectSpinner = spinner()\n detectSpinner.start('Detecting model...')\n try {\n detectedModel = await detectModel(url as string)\n if (detectedModel) {\n selectedModel = detectedModel\n detectSpinner.stop(`Detected: ${detectedModel}`)\n } else {\n detectSpinner.stop('Could not detect model, will use auto')\n }\n } catch {\n detectSpinner.stop('Detection failed, will use auto')\n }\n }\n }\n\n // API Key (optional)\n let apiKey: string | undefined\n if (backend === 'openai' || backend === 'anthropic') {\n const key = await password({\n message: 'API Key:',\n })\n if (isCancel(key)) {\n cancel('Cancelled')\n return\n }\n apiKey = key || undefined\n } else {\n const needsKey = await select({\n message: 'Does this provider require an API key?',\n options: [\n { value: 'no', label: 'No' },\n { value: 'yes', label: 'Yes' },\n ],\n })\n if (isCancel(needsKey)) {\n cancel('Cancelled')\n return\n }\n if (needsKey === 'yes') {\n const key = await password({\n message: 'API Key:',\n })\n if (isCancel(key)) {\n cancel('Cancelled')\n return\n }\n apiKey = key || undefined\n }\n }\n\n // Test connection\n const testSpinner = spinner()\n testSpinner.start(`Testing connection to ${url}...`)\n\n let finalBackend = backend as string\n let finalDetectedModel: string | null = null\n\n try {\n if (backend === 'auto') {\n finalBackend = await detectBackend(url as string)\n }\n if (selectedModel === 'auto') {\n finalDetectedModel = await detectModel(url as string) ?? 'auto'\n }\n testSpinner.stop(`✓ Connected to ${finalBackend}${finalDetectedModel !== 'auto' ? ` (${finalDetectedModel})` : ''}`)\n } catch {\n testSpinner.stop('⚠ Could not connect to provider')\n const continueAnyway = await select({\n message: 'Provider is not reachable. Save anyway?',\n options: [\n { value: 'yes', label: 'Yes, save anyway' },\n { value: 'no', label: 'No, cancel' },\n ],\n })\n if (isCancel(continueAnyway) || continueAnyway === 'no') {\n cancel('Cancelled')\n return\n }\n }\n\n // Make active?\n const makeActive = config.providers.length === 0 || await (async () => {\n const choice = await select({\n message: 'Make this the active provider?',\n options: [\n { value: 'yes', label: 'Yes' },\n { value: 'no', label: 'No' },\n ],\n })\n if (isCancel(choice)) return false\n return choice === 'yes'\n })()\n\n let newConfig = addProvider(config, {\n name: name as string,\n url: url as string,\n backend: finalBackend as ProviderBackend,\n apiKey,\n maxContext: 200000,\n isActive: makeActive as boolean,\n })\n\n // If making active, set the default model selection\n if (makeActive) {\n const { setDefaultModelSelection } = await import('./config.js')\n newConfig = setDefaultModelSelection(newConfig, newConfig.providers[newConfig.providers.length - 1]!.id, selectedModel)\n }\n\n await saveGlobalConfig(mode, newConfig)\n outro(`✓ Provider \"${name}\" added${makeActive ? ' and activated' : ''}`)\n}\n\nexport async function runProviderList(mode: Mode): Promise<void> {\n const config = await loadGlobalConfig(mode)\n\n if (config.providers.length === 0) {\n log.warn('No providers configured. Run `openfox provider add` to add one.')\n return\n }\n\n console.log('\\nConfigured providers:\\n')\n console.log(' NAME URL MODEL BACKEND')\n console.log(' ────────────────────────────────────────────────────────────────────────────────')\n\n for (const provider of config.providers) {\n const marker = provider.isActive ? '▸' : ' '\n const name = provider.name.padEnd(18)\n const url = provider.url.padEnd(32)\n const model = (provider.model || 'auto').padEnd(18)\n const backend = provider.backend\n\n console.log(`${marker} ${name} ${url} ${model} ${backend}`)\n }\n\n console.log('')\n}\n\nexport async function runProviderUse(mode: Mode): Promise<void> {\n const config = await loadGlobalConfig(mode)\n\n if (config.providers.length === 0) {\n log.warn('No providers configured. Run `openfox provider add` to add one.')\n return\n }\n\n if (config.providers.length === 1) {\n log.info('Only one provider configured, already active.')\n return\n }\n\n const activeProvider = getActiveProvider(config)\n\n const choice = await select({\n message: 'Select provider to activate:',\n options: config.providers.map(p => ({\n value: p.id,\n label: `${p.name}${p.isActive ? ' (current)' : ''}`,\n hint: `${p.url} - ${p.model}`,\n })),\n initialValue: activeProvider?.id,\n })\n\n if (isCancel(choice)) {\n cancel('Cancelled')\n return\n }\n\n if (choice === activeProvider?.id) {\n log.info('Already the active provider.')\n return\n }\n\n const newConfig = activateProvider(config, choice as string)\n await saveGlobalConfig(mode, newConfig)\n\n const activated = newConfig.providers.find(p => p.id === choice)\n outro(`✓ Now using \"${activated?.name}\"`)\n}\n\nexport async function runProviderRemove(mode: Mode): Promise<void> {\n const config = await loadGlobalConfig(mode)\n\n if (config.providers.length === 0) {\n log.warn('No providers configured.')\n return\n }\n\n const choice = await select({\n message: 'Select provider to remove:',\n options: config.providers.map(p => ({\n value: p.id,\n label: `${p.name}${p.isActive ? ' (active)' : ''}`,\n hint: p.url,\n })),\n })\n\n if (isCancel(choice)) {\n cancel('Cancelled')\n return\n }\n\n const providerToRemove = config.providers.find(p => p.id === choice)\n\n if (providerToRemove?.isActive && config.providers.length > 1) {\n const confirm = await select({\n message: `\"${providerToRemove.name}\" is the active provider. Remove it anyway?`,\n options: [\n { value: 'yes', label: 'Yes, remove and activate next provider' },\n { value: 'no', label: 'No, cancel' },\n ],\n })\n if (isCancel(confirm) || confirm === 'no') {\n cancel('Cancelled')\n return\n }\n }\n\n const newConfig = removeProvider(config, choice as string)\n await saveGlobalConfig(mode, newConfig)\n\n outro(`✓ Removed \"${providerToRemove?.name}\"`)\n}\n\nexport async function runProviderCommand(mode: Mode, subcommand?: string): Promise<void> {\n switch (subcommand) {\n case 'add':\n await runProviderAdd(mode)\n break\n case 'list':\n case 'ls':\n await runProviderList(mode)\n break\n case 'use':\n case 'switch':\n await runProviderUse(mode)\n break\n case 'remove':\n case 'rm':\n case 'delete':\n await runProviderRemove(mode)\n break\n default:\n printProviderHelp()\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server/utils/network.ts","../src/cli/serve.ts"],"sourcesContent":["import os from 'node:os'\nimport { statSync } from 'node:fs'\n\nexport interface NetworkInterface {\n ip: string\n family: 'IPv4' | 'IPv6'\n name: string\n}\n\nexport function getNetworkInterfaces(): NetworkInterface[] {\n const interfaces = os.networkInterfaces()\n const result: NetworkInterface[] = []\n\n for (const [name, addresses] of Object.entries(interfaces)) {\n if (!addresses) continue\n\n for (const addr of addresses) {\n if (addr.family === 'IPv4') {\n result.push({\n ip: addr.address,\n family: 'IPv4',\n name,\n })\n }\n }\n }\n\n return result\n}\n\nexport function getValidIPv4Addresses(): string[] {\n const interfaces = getNetworkInterfaces()\n \n // Filter out loopback and internal addresses\n const valid = interfaces\n .filter(addr => {\n // Exclude loopback\n if (addr.ip.startsWith('127.')) return false\n // Exclude link-local\n if (addr.ip.startsWith('169.254.')) return false\n // Exclude unique local addresses (fc00::/7 for IPv6, but we're only looking at IPv4)\n return true\n })\n .map(addr => addr.ip)\n\n // Sort: prefer eth*, wlan*, then others\n const sorted = valid.sort((a, b) => {\n const aInterface = interfaces.find(i => i.ip === a)?.name || ''\n const bInterface = interfaces.find(i => i.ip === b)?.name || ''\n \n // Prefer eth* interfaces\n if (aInterface.startsWith('eth') && !bInterface.startsWith('eth')) return -1\n if (!aInterface.startsWith('eth') && bInterface.startsWith('eth')) return 1\n \n // Then prefer wlan* interfaces\n if (aInterface.startsWith('wlan') && !bInterface.startsWith('wlan')) return -1\n if (!aInterface.startsWith('wlan') && bInterface.startsWith('wlan')) return 1\n \n return 0\n })\n\n return sorted\n}\n\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0KB'\n \n const KB = 1024\n const MB = KB * 1024\n const GB = MB * 1024\n \n if (bytes >= GB) {\n return `${(bytes / GB).toFixed(1)}GB`\n } else if (bytes >= MB) {\n return `${(bytes / MB).toFixed(1)}MB`\n } else {\n return `${Math.round(bytes / KB)}KB`\n }\n}\n\nexport function getDatabaseSize(databasePath: string): string {\n try {\n const stats = statSync(databasePath)\n return formatFileSize(stats.size)\n } catch {\n return '0KB'\n }\n}\n\nexport function displayStartupBanner(config: {\n host: string\n port: number\n databasePath: string\n configPath: string\n}): void {\n const { host, port, databasePath, configPath } = config\n const isLocalhost = host === '127.0.0.1'\n \n console.log('\\n🦊 OpenFox v0.1.0\\n')\n \n if (isLocalhost) {\n console.log(` 🌐 Server: http://localhost:${port}`)\n console.log(' 🔒 Access: Localhost only')\n } else {\n const ips = getValidIPv4Addresses()\n \n if (ips.length === 0) {\n console.log(` 🌐 Server: http://0.0.0.0:${port}`)\n console.warn(' ⚠️ Warning: No valid network interfaces detected')\n } else {\n console.log(' 🌐 Server:')\n for (const ip of ips) {\n console.log(` • http://${ip}:${port}`)\n }\n console.log(' 🌍 Access: Local network')\n }\n }\n \n const size = getDatabaseSize(databasePath)\n console.log(` 💾 Database: ${databasePath} (${size})`)\n console.log(` ⚙️ Config: ${configPath}`)\n console.log('\\n💡 Tip: Press Ctrl+C to stop the server\\n')\n}\n","import { createServer } from '../server/index.js'\nimport { loadConfig } from '../server/config.js'\nimport { logger } from '../server/utils/logger.js'\nimport { displayStartupBanner } from '../server/utils/network.js'\nimport { loadGlobalConfig, getActiveProvider, getDefaultModel } from './config.js'\nimport { getDatabasePath, getGlobalConfigPath, ensureDataDirExists } from './paths.js'\nimport open from 'open'\nimport type { Mode } from './main.js'\nimport type { LlmBackend } from '../shared/types.js'\n\nexport interface ServeOptions {\n mode: Mode\n port?: number\n openBrowser?: boolean\n}\n\nexport async function runServe(options: ServeOptions): Promise<void> {\n const { mode, port, openBrowser } = options\n \n // Ensure data directory exists before starting server\n await ensureDataDirExists(mode)\n \n const globalConfig = await loadGlobalConfig(mode)\n const activeProvider = getActiveProvider(globalConfig)\n const env = loadConfig()\n \n // Environment variables take precedence over global config file\n // This allows CLI overrides and e2e test configuration to work properly\n const envBackend = env.llm.backend\n const envModel = env.llm.model\n const envUrl = env.llm.baseUrl\n \n // Only use env values if they're not the defaults (meaning they were explicitly set)\n const isEnvBackendExplicit = envBackend !== 'auto'\n const isEnvModelExplicit = envModel !== 'qwen3.5-122b-int4-autoround' // default in config.ts\n const isEnvUrlExplicit = envUrl !== 'http://localhost:8000/v1' // default in config.ts\n \n // Get provider values with fallbacks\n const providerUrl = activeProvider?.url ?? envUrl\n const defaultModel = getDefaultModel(globalConfig) ?? envModel\n const providerBackend = (activeProvider?.backend ?? envBackend) as LlmBackend | 'auto'\n \n const merged = {\n ...env,\n llm: { \n ...env.llm, \n baseUrl: isEnvUrlExplicit ? envUrl : providerUrl,\n model: isEnvModelExplicit ? envModel : defaultModel,\n backend: isEnvBackendExplicit ? envBackend : providerBackend,\n },\n server: { \n ...env.server, \n port: port ?? (mode === 'development' ? 10469 : env.server.port),\n host: env.server.host ?? globalConfig.server.host ?? '127.0.0.1',\n openBrowser: openBrowser ?? globalConfig.server.openBrowser,\n },\n database: {\n // Use env OPENFOX_DB_PATH if explicitly set (e.g., \":memory:\" for tests), otherwise use standard path\n path: env.database.path !== './openfox.db' ? env.database.path : getDatabasePath(mode),\n },\n logging: {\n level: globalConfig.logging?.level ?? 'info' as const,\n },\n mode,\n // Pass providers for the server to use\n providers: globalConfig.providers,\n activeProviderId: globalConfig.activeProviderId,\n activeWorkflowId: globalConfig.activeWorkflowId,\n // Workdir precedence: .env override → global config → process.cwd()\n // Normalize: remove trailing slash to prevent double slashes in paths\n workdir: (process.env['OPENFOX_WORKDIR'] ?? globalConfig.workspace?.workdir ?? process.cwd()).replace(/\\/$/, ''),\n }\n \n await createServer(merged)\n \n // Display startup banner\n displayStartupBanner({\n host: merged.server.host,\n port: merged.server.port,\n databasePath: merged.database.path,\n configPath: getGlobalConfigPath(mode),\n })\n \n if (merged.server.openBrowser) {\n open(`http://${merged.server.host === '127.0.0.1' ? 'localhost' : merged.server.host}:${merged.server.port}`).catch(() => {\n logger.warn('Could not open browser automatically')\n })\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}