@evident-ai/cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- #!/usr/bin/env node
1
+
2
+ export { }
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
2
 
4
3
  // src/index.ts
5
4
  import { Command } from "commander";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/config.ts","../src/lib/api.ts","../src/lib/keychain.ts","../src/utils/ui.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../src/commands/tunnel.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Evident CLI\n *\n * Run OpenCode locally and connect it to the Evident platform.\n */\n\nimport { Command } from 'commander';\nimport { login } from './commands/login.js';\nimport { logout } from './commands/logout.js';\nimport { whoami } from './commands/whoami.js';\nimport { tunnel } from './commands/tunnel.js';\nimport { setEnvironment, type Environment } from './lib/config.js';\n\nconst program = new Command();\n\nprogram\n .name('evident')\n .description('Run OpenCode locally and connect it to Evident')\n .version('0.1.0')\n .option('-e, --env <environment>', 'Environment to use (local, dev, production)', 'production')\n .hook('preAction', (thisCommand) => {\n const env = thisCommand.opts().env as Environment;\n if (env) {\n setEnvironment(env);\n }\n });\n\n// Login command\nprogram\n .command('login')\n .description('Authenticate with Evident')\n .option('--token', 'Use token-based authentication (for CI/CD)')\n .option('--no-browser', 'Do not open the browser automatically')\n .action(login);\n\n// Logout command\nprogram.command('logout').description('Remove stored credentials').action(logout);\n\n// Whoami command\nprogram.command('whoami').description('Show the currently logged in user').action(whoami);\n\n// Tunnel command\nprogram\n .command('tunnel')\n .description('Establish a tunnel to Evident for Local Mode')\n .option('-s, --sandbox <id>', 'Sandbox ID to connect to')\n .option('-p, --port <port>', 'OpenCode port (default: 4096)', '4096')\n .action((options: { sandbox?: string; port: string }) => {\n tunnel({\n sandbox: options.sandbox,\n port: parseInt(options.port, 10),\n });\n });\n\n// Parse arguments\nprogram.parse();\n","/**\n * Login Command\n *\n * Authenticates the user using OAuth Device Flow.\n * See ADR-0018 for details.\n */\n\nimport open from 'open';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport { api } from '../lib/api.js';\nimport { storeToken } from '../lib/keychain.js';\nimport { printSuccess, printError, blank, waitForEnter, sleep } from '../utils/ui.js';\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n}\n\ninterface TokenPollResponse {\n status: 'pending' | 'complete' | 'expired';\n access_token?: string;\n expires_at?: string;\n user?: {\n id: string;\n email: string;\n };\n}\n\ninterface LoginOptions {\n token?: boolean;\n noBrowser?: boolean;\n}\n\n/**\n * Start device flow authentication\n */\nasync function deviceFlowLogin(options: LoginOptions): Promise<void> {\n // Step 1: Request device code\n let deviceAuth: DeviceAuthResponse;\n try {\n deviceAuth = await api.post<DeviceAuthResponse>('/auth/device');\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError(`Failed to start authentication: ${message}`);\n process.exit(1);\n }\n\n const { device_code, user_code, verification_uri, interval } = deviceAuth;\n\n // Step 2: Display instructions\n blank();\n console.log(chalk.bold('To authenticate, visit:'));\n console.log();\n console.log(` ${chalk.cyan(verification_uri)}`);\n console.log();\n console.log(chalk.bold('And enter this code:'));\n console.log();\n console.log(` ${chalk.yellow.bold(user_code)}`);\n blank();\n\n // Step 3: Open browser (unless --no-browser)\n if (!options.noBrowser) {\n await waitForEnter('Press Enter to open the browser...');\n try {\n await open(verification_uri);\n } catch {\n console.log(chalk.dim('Could not open browser. Please visit the URL manually.'));\n }\n }\n\n // Step 4: Poll for completion\n const spinner = ora('Waiting for authentication...').start();\n\n const pollIntervalMs = (interval || 5) * 1000;\n const maxAttempts = 60; // 5 minutes max at 5s intervals\n let attempts = 0;\n\n while (attempts < maxAttempts) {\n await sleep(pollIntervalMs);\n attempts++;\n\n try {\n const result = await api.post<TokenPollResponse>('/auth/device/token', {\n device_code,\n });\n\n if (result.status === 'complete' && result.access_token && result.user) {\n // Success! Store the token\n await storeToken({\n token: result.access_token,\n user: result.user,\n expiresAt: result.expires_at,\n });\n\n spinner.stop();\n blank();\n printSuccess(`Logged in as ${chalk.bold(result.user.email)}`);\n return;\n }\n\n if (result.status === 'expired') {\n spinner.stop();\n blank();\n printError('Authentication expired. Please try again.');\n process.exit(1);\n }\n\n // Still pending, continue polling\n } catch (error) {\n // Network error, continue polling\n const message = error instanceof Error ? error.message : 'Unknown error';\n spinner.text = `Waiting for authentication... (${message})`;\n }\n }\n\n spinner.stop();\n blank();\n printError('Authentication timed out. Please try again.');\n process.exit(1);\n}\n\n/**\n * Token-based login (for CI/CD)\n */\nasync function tokenLogin(): Promise<void> {\n console.log('Token login mode.');\n console.log('Visit your Evident dashboard to generate a CLI token.');\n blank();\n\n // Read token from stdin\n process.stdout.write('Paste token: ');\n\n const token = await new Promise<string>((resolve) => {\n let data = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk) => {\n data += chunk;\n });\n process.stdin.on('end', () => {\n resolve(data.trim());\n });\n // For TTY, read a single line\n if (process.stdin.isTTY) {\n process.stdin.once('data', (chunk) => {\n process.stdin.pause();\n resolve(chunk.toString().trim());\n });\n process.stdin.resume();\n }\n });\n\n if (!token) {\n printError('No token provided.');\n process.exit(1);\n }\n\n // Validate the token\n const spinner = ora('Validating token...').start();\n\n try {\n interface ValidateResponse {\n user: { id: string; email: string };\n expires_at?: string;\n }\n\n const result = await api.post<ValidateResponse>('/auth/token/validate', { token });\n\n await storeToken({\n token,\n user: result.user,\n expiresAt: result.expires_at,\n });\n\n spinner.stop();\n printSuccess(`Logged in as ${chalk.bold(result.user.email)}`);\n } catch (error) {\n spinner.stop();\n const message = error instanceof Error ? error.message : 'Invalid token';\n printError(`Authentication failed: ${message}`);\n process.exit(1);\n }\n}\n\n/**\n * Login command handler\n */\nexport async function login(options: LoginOptions): Promise<void> {\n if (options.token) {\n await tokenLogin();\n } else {\n await deviceFlowLogin(options);\n }\n}\n","/**\n * CLI Configuration\n *\n * Manages configuration values and file-based credential storage.\n * Supports multiple environments: local, dev, production\n */\n\nimport Conf from 'conf';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// Supported environments\nexport type Environment = 'local' | 'dev' | 'production';\n\n// Configuration schema\ninterface ConfigSchema {\n apiUrl: string;\n tunnelUrl: string;\n}\n\n// Credentials schema (stored separately with stricter permissions)\ninterface CredentialsSchema {\n token?: string;\n user?: {\n id: string;\n email: string;\n };\n expiresAt?: string;\n}\n\n// Environment presets\n// Domain format: {service}.{env}.evident.run (with aliases for production)\nconst environmentPresets: Record<Environment, ConfigSchema> = {\n local: {\n apiUrl: 'http://localhost:3000',\n tunnelUrl: 'ws://localhost:8787',\n },\n dev: {\n apiUrl: 'https://api.dev.evident.run',\n tunnelUrl: 'wss://tunnel.dev.evident.run',\n },\n production: {\n // Production URLs also have aliases: api.evident.run, tunnel.evident.run\n apiUrl: 'https://api.production.evident.run',\n tunnelUrl: 'wss://tunnel.production.evident.run',\n },\n};\n\n// Default to production\nconst defaults: ConfigSchema = environmentPresets.production;\n\n// Current environment (can be set via --env flag or EVIDENT_ENV)\nlet currentEnvironment: Environment = 'production';\n\n/**\n * Set the current environment\n */\nexport function setEnvironment(env: Environment): void {\n currentEnvironment = env;\n}\n\n/**\n * Get the current environment\n */\nexport function getEnvironment(): Environment {\n // Environment variable takes precedence\n const envVar = process.env.EVIDENT_ENV as Environment | undefined;\n if (envVar && environmentPresets[envVar]) {\n return envVar;\n }\n return currentEnvironment;\n}\n\n/**\n * Get configuration for current environment\n */\nfunction getEnvConfig(): ConfigSchema {\n return environmentPresets[getEnvironment()];\n}\n\n// Environment overrides (env vars take highest precedence)\nfunction getApiUrl(): string {\n return process.env.EVIDENT_API_URL ?? getEnvConfig().apiUrl;\n}\n\nfunction getTunnelUrl(): string {\n return process.env.EVIDENT_TUNNEL_URL ?? getEnvConfig().tunnelUrl;\n}\n\n// Configuration store\nconst config = new Conf<ConfigSchema>({\n projectName: 'evident',\n projectSuffix: '',\n defaults,\n});\n\n// Credentials store (separate file with restricted access)\nconst credentials = new Conf<CredentialsSchema>({\n projectName: 'evident',\n projectSuffix: '',\n configName: 'credentials',\n defaults: {},\n});\n\n/**\n * Get the configuration directory path\n */\nexport function getConfigDir(): string {\n // XDG_CONFIG_HOME on Linux, ~/.config on others\n const xdgConfig = process.env.XDG_CONFIG_HOME;\n if (xdgConfig) {\n return join(xdgConfig, 'evident');\n }\n return join(homedir(), '.config', 'evident');\n}\n\n/**\n * Get the API URL\n */\nexport function getApiUrlConfig(): string {\n return getApiUrl();\n}\n\n/**\n * Get the tunnel WebSocket URL\n */\nexport function getTunnelUrlConfig(): string {\n return getTunnelUrl();\n}\n\n/**\n * Get stored credentials\n */\nexport function getCredentials(): CredentialsSchema {\n return {\n token: credentials.get('token'),\n user: credentials.get('user'),\n expiresAt: credentials.get('expiresAt'),\n };\n}\n\n/**\n * Store credentials\n */\nexport function setCredentials(creds: CredentialsSchema): void {\n if (creds.token) credentials.set('token', creds.token);\n if (creds.user) credentials.set('user', creds.user);\n if (creds.expiresAt) credentials.set('expiresAt', creds.expiresAt);\n}\n\n/**\n * Clear stored credentials\n */\nexport function clearCredentials(): void {\n credentials.clear();\n}\n\n/**\n * Check if we have valid credentials\n */\nexport function hasValidCredentials(): boolean {\n const creds = getCredentials();\n\n if (!creds.token) {\n return false;\n }\n\n if (creds.expiresAt) {\n const expiresAt = new Date(creds.expiresAt);\n if (expiresAt < new Date()) {\n return false;\n }\n }\n\n return true;\n}\n\nexport { config, credentials };\n","/**\n * API Client\n *\n * Handles HTTP requests to the Evident backend API.\n */\n\nimport { getApiUrlConfig, getCredentials } from './config.js';\n\ninterface ApiRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n body?: unknown;\n headers?: Record<string, string>;\n authenticated?: boolean;\n}\n\ninterface ApiError {\n message: string;\n statusCode: number;\n error?: string;\n}\n\nexport class ApiClient {\n private baseUrl: string;\n\n constructor(baseUrl?: string) {\n this.baseUrl = baseUrl ?? getApiUrlConfig();\n }\n\n /**\n * Make an API request\n */\n async request<T>(path: string, options: ApiRequestOptions = {}): Promise<T> {\n const { method = 'GET', body, headers = {}, authenticated = false } = options;\n\n const url = `${this.baseUrl}${path}`;\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headers,\n };\n\n // Add authentication header if requested\n if (authenticated) {\n const creds = getCredentials();\n if (!creds.token) {\n throw new Error('Not authenticated. Run `evident login` first.');\n }\n requestHeaders['Authorization'] = `Bearer ${creds.token}`;\n }\n\n const response = await fetch(url, {\n method,\n headers: requestHeaders,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n // Handle errors\n if (!response.ok) {\n let errorData: ApiError;\n try {\n errorData = (await response.json()) as ApiError;\n } catch {\n errorData = {\n message: response.statusText,\n statusCode: response.status,\n };\n }\n\n const error = new Error(errorData.message) as Error & { statusCode: number };\n error.statusCode = response.status;\n throw error;\n }\n\n // Handle empty responses\n const contentType = response.headers.get('Content-Type');\n if (!contentType?.includes('application/json')) {\n return {} as T;\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * GET request\n */\n async get<T>(path: string, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<T> {\n return this.request<T>(path, { ...options, method: 'GET' });\n }\n\n /**\n * POST request\n */\n async post<T>(\n path: string,\n body?: unknown,\n options: Omit<ApiRequestOptions, 'method'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'POST', body });\n }\n\n /**\n * PUT request\n */\n async put<T>(\n path: string,\n body?: unknown,\n options: Omit<ApiRequestOptions, 'method'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'PUT', body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(\n path: string,\n options: Omit<ApiRequestOptions, 'method' | 'body'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'DELETE' });\n }\n}\n\n// Default API client instance\nexport const api = new ApiClient();\n","/**\n * Keychain Storage\n *\n * Provides secure credential storage using the system keychain.\n * Falls back to file-based storage if keychain is unavailable.\n */\n\nimport { getCredentials, setCredentials, clearCredentials } from './config.js';\n\nconst SERVICE_NAME = 'evident-cli';\nconst ACCOUNT_NAME = 'default';\n\n// Dynamic import for keytar (optional dependency)\nasync function getKeytar(): Promise<typeof import('keytar') | null> {\n try {\n return await import('keytar');\n } catch {\n // keytar not available (e.g., missing native dependencies)\n return null;\n }\n}\n\nexport interface StoredCredentials {\n token: string;\n user: {\n id: string;\n email: string;\n };\n expiresAt?: string;\n}\n\n/**\n * Store credentials in the system keychain\n */\nexport async function storeToken(credentials: StoredCredentials): Promise<void> {\n const keytar = await getKeytar();\n\n if (keytar) {\n // Store in system keychain\n await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(credentials));\n } else {\n // Fallback to file-based storage\n setCredentials({\n token: credentials.token,\n user: credentials.user,\n expiresAt: credentials.expiresAt,\n });\n }\n}\n\n/**\n * Retrieve credentials from the system keychain\n */\nexport async function getToken(): Promise<StoredCredentials | null> {\n const keytar = await getKeytar();\n\n if (keytar) {\n // Try system keychain first\n const stored = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);\n if (stored) {\n try {\n return JSON.parse(stored) as StoredCredentials;\n } catch {\n // Invalid JSON, clear it\n await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);\n return null;\n }\n }\n }\n\n // Fallback to file-based storage\n const creds = getCredentials();\n if (creds.token && creds.user) {\n return {\n token: creds.token,\n user: creds.user,\n expiresAt: creds.expiresAt,\n };\n }\n\n return null;\n}\n\n/**\n * Delete credentials from the system keychain\n */\nexport async function deleteToken(): Promise<void> {\n const keytar = await getKeytar();\n\n if (keytar) {\n await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);\n }\n\n // Always clear file-based storage too\n clearCredentials();\n}\n\n/**\n * Check if credentials are valid (not expired)\n */\nexport async function hasValidToken(): Promise<boolean> {\n const credentials = await getToken();\n\n if (!credentials) {\n return false;\n }\n\n if (credentials.expiresAt) {\n const expiresAt = new Date(credentials.expiresAt);\n if (expiresAt < new Date()) {\n return false;\n }\n }\n\n return true;\n}\n","/**\n * CLI UI Utilities\n *\n * Common formatting and display functions.\n */\n\nimport chalk from 'chalk';\n\n/**\n * Format success message\n */\nexport function success(message: string): string {\n return `${chalk.green('✓')} ${message}`;\n}\n\n/**\n * Format error message\n */\nexport function error(message: string): string {\n return `${chalk.red('✗')} ${message}`;\n}\n\n/**\n * Format warning message\n */\nexport function warning(message: string): string {\n return `${chalk.yellow('!')} ${message}`;\n}\n\n/**\n * Format info message\n */\nexport function info(message: string): string {\n return `${chalk.blue('i')} ${message}`;\n}\n\n/**\n * Print success message\n */\nexport function printSuccess(message: string): void {\n console.log(success(message));\n}\n\n/**\n * Print error message\n */\nexport function printError(message: string): void {\n console.error(error(message));\n}\n\n/**\n * Print warning message\n */\nexport function printWarning(message: string): void {\n console.log(warning(message));\n}\n\n/**\n * Print info message\n */\nexport function printInfo(message: string): void {\n console.log(info(message));\n}\n\n/**\n * Format a key-value pair for display\n */\nexport function keyValue(key: string, value: string): string {\n return `${chalk.dim(key + ':')} ${value}`;\n}\n\n/**\n * Print a blank line\n */\nexport function blank(): void {\n console.log();\n}\n\n/**\n * Wait for user to press Enter\n */\nexport function waitForEnter(prompt = 'Press Enter to continue...'): Promise<void> {\n return new Promise((resolve) => {\n process.stdout.write(chalk.dim(prompt));\n\n const handler = (): void => {\n process.stdin.removeListener('data', handler);\n process.stdin.setRawMode?.(false);\n process.stdin.pause();\n console.log();\n resolve();\n };\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode?.(true);\n }\n process.stdin.resume();\n process.stdin.once('data', handler);\n });\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Logout Command\n *\n * Removes stored credentials.\n */\n\nimport { deleteToken, getToken } from '../lib/keychain.js';\nimport { printSuccess, printWarning } from '../utils/ui.js';\n\n/**\n * Logout command handler\n */\nexport async function logout(): Promise<void> {\n const credentials = await getToken();\n\n if (!credentials) {\n printWarning('You are not logged in.');\n return;\n }\n\n await deleteToken();\n printSuccess('Logged out successfully.');\n}\n","/**\n * Whoami Command\n *\n * Displays the currently logged in user.\n */\n\nimport chalk from 'chalk';\nimport { getToken } from '../lib/keychain.js';\nimport { printError, keyValue, blank } from '../utils/ui.js';\n\n/**\n * Whoami command handler\n */\nexport async function whoami(): Promise<void> {\n const credentials = await getToken();\n\n if (!credentials) {\n printError('Not logged in. Run `evident login` to authenticate.');\n process.exit(1);\n }\n\n blank();\n console.log(keyValue('User', chalk.bold(credentials.user.email)));\n console.log(keyValue('User ID', credentials.user.id));\n\n if (credentials.expiresAt) {\n const expiresAt = new Date(credentials.expiresAt);\n const now = new Date();\n\n if (expiresAt < now) {\n console.log(keyValue('Status', chalk.red('Token expired')));\n } else {\n const daysRemaining = Math.ceil(\n (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),\n );\n console.log(keyValue('Expires', `${daysRemaining} days`));\n }\n }\n\n blank();\n}\n","/**\n * Tunnel Command\n *\n * Establishes a WebSocket tunnel to Evident for Local Mode.\n * See ADR-0020 for architecture details.\n */\n\nimport WebSocket from 'ws';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { getToken } from '../lib/keychain.js';\nimport { getTunnelUrlConfig } from '../lib/config.js';\nimport { printError, printWarning, blank, sleep } from '../utils/ui.js';\n\ninterface TunnelOptions {\n sandbox?: string;\n port?: number;\n}\n\ninterface RelayMessage {\n type: 'connected' | 'error' | 'ping' | 'request';\n sandbox_id?: string;\n code?: string;\n message?: string;\n id?: string;\n payload?: {\n method: string;\n path: string;\n headers?: Record<string, string>;\n body?: unknown;\n };\n}\n\ninterface TunnelState {\n connected: boolean;\n sandboxId: string | null;\n reconnectAttempt: number;\n lastActivity: Date;\n}\n\nconst MAX_RECONNECT_DELAY = 30000; // 30 seconds\nconst BASE_RECONNECT_DELAY = 500; // 0.5 seconds\n\n/**\n * Calculate reconnect delay with exponential backoff and jitter\n */\nfunction getReconnectDelay(attempt: number): number {\n const exponentialDelay = BASE_RECONNECT_DELAY * Math.pow(2, attempt);\n const jitter = Math.random() * 1000;\n return Math.min(exponentialDelay + jitter, MAX_RECONNECT_DELAY);\n}\n\n/**\n * Display tunnel status\n */\nfunction displayStatus(state: TunnelState): void {\n console.clear();\n console.log(chalk.bold('Evident Tunnel'));\n console.log(chalk.dim('─'.repeat(50)));\n blank();\n\n if (state.connected) {\n console.log(` ${chalk.green('●')} Status: ${chalk.green('Online')}`);\n console.log(` Sandbox: ${state.sandboxId ?? 'Unknown'}`);\n console.log(` Last activity: ${state.lastActivity.toLocaleTimeString()}`);\n } else {\n console.log(` ${chalk.yellow('○')} Status: ${chalk.yellow('Reconnecting...')}`);\n if (state.reconnectAttempt > 0) {\n console.log(` Attempt: ${state.reconnectAttempt}`);\n }\n }\n\n blank();\n console.log(chalk.dim('Press Ctrl+C to disconnect'));\n}\n\n/**\n * Forward request to local OpenCode instance\n */\nasync function forwardToOpenCode(\n port: number,\n request: { method: string; path: string; headers?: Record<string, string>; body?: unknown },\n): Promise<{ status: number; headers?: Record<string, string>; body?: unknown }> {\n const url = `http://localhost:${port}${request.path}`;\n\n try {\n const response = await fetch(url, {\n method: request.method,\n headers: {\n 'Content-Type': 'application/json',\n ...request.headers,\n },\n body: request.body ? JSON.stringify(request.body) : undefined,\n });\n\n let body: unknown;\n const contentType = response.headers.get('Content-Type');\n if (contentType?.includes('application/json')) {\n body = await response.json();\n } else {\n body = await response.text();\n }\n\n return {\n status: response.status,\n body,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n status: 502,\n body: { error: 'Failed to connect to OpenCode', message },\n };\n }\n}\n\n/**\n * Establish tunnel connection\n */\nasync function connect(\n token: string,\n sandboxId: string | undefined,\n port: number,\n state: TunnelState,\n): Promise<void> {\n const tunnelUrl = getTunnelUrlConfig();\n const url = sandboxId\n ? `${tunnelUrl}/tunnel/${sandboxId}/connect`\n : `${tunnelUrl}/tunnel/default/connect`;\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n ws.on('open', () => {\n state.connected = true;\n state.reconnectAttempt = 0;\n state.lastActivity = new Date();\n displayStatus(state);\n });\n\n ws.on('message', async (data: WebSocket.RawData) => {\n try {\n const message: RelayMessage = JSON.parse(data.toString());\n state.lastActivity = new Date();\n\n switch (message.type) {\n case 'connected':\n state.sandboxId = message.sandbox_id ?? null;\n displayStatus(state);\n break;\n\n case 'error':\n printError(`Tunnel error: ${message.message}`);\n if (message.code === 'unauthorized') {\n ws.close();\n reject(new Error('Unauthorized'));\n }\n break;\n\n case 'ping':\n ws.send(JSON.stringify({ type: 'pong' }));\n break;\n\n case 'request':\n if (message.id && message.payload) {\n const response = await forwardToOpenCode(port, message.payload);\n ws.send(\n JSON.stringify({\n type: 'response',\n id: message.id,\n payload: response,\n }),\n );\n displayStatus(state);\n }\n break;\n }\n } catch (error) {\n console.error('Failed to handle message:', error);\n }\n });\n\n ws.on('close', () => {\n state.connected = false;\n displayStatus(state);\n resolve();\n });\n\n ws.on('error', (error: Error) => {\n state.connected = false;\n displayStatus(state);\n // Don't reject on connection errors, let it reconnect\n console.error(chalk.dim(`Connection error: ${error.message}`));\n });\n\n // Handle Ctrl+C\n const cleanup = (): void => {\n ws.close();\n process.exit(0);\n };\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n });\n}\n\n/**\n * Tunnel command handler\n */\nexport async function tunnel(options: TunnelOptions): Promise<void> {\n // Get credentials\n const credentials = await getToken();\n if (!credentials) {\n printError('Not logged in. Run `evident login` first.');\n process.exit(1);\n }\n\n const port = options.port ?? 4096;\n const sandboxId = options.sandbox;\n\n // Check if OpenCode is running\n const spinner = ora('Checking OpenCode connection...').start();\n\n try {\n const response = await fetch(`http://localhost:${port}/health`);\n if (!response.ok) {\n throw new Error('Health check failed');\n }\n spinner.succeed(`OpenCode detected on port ${port}`);\n } catch {\n spinner.warn(`Could not connect to OpenCode on port ${port}`);\n printWarning('Make sure OpenCode is running before starting the tunnel.');\n blank();\n }\n\n // Connection state\n const state: TunnelState = {\n connected: false,\n sandboxId: null,\n reconnectAttempt: 0,\n lastActivity: new Date(),\n };\n\n // Reconnection loop\n while (true) {\n try {\n await connect(credentials.token, sandboxId, port, state);\n\n // Connection closed, attempt reconnect\n state.reconnectAttempt++;\n const delay = getReconnectDelay(state.reconnectAttempt);\n\n displayStatus(state);\n await sleep(delay);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n\n if (message === 'Unauthorized') {\n printError('Authentication failed. Please run `evident login` again.');\n process.exit(1);\n }\n\n state.reconnectAttempt++;\n const delay = getReconnectDelay(state.reconnectAttempt);\n await sleep(delay);\n }\n }\n}\n"],"mappings":"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","names":["chalk","error","credentials","error","chalk","credentials","chalk","credentials","chalk","chalk","ora","chalk","error","credentials","ora"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/config.ts","../src/lib/api.ts","../src/lib/keychain.ts","../src/utils/ui.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../src/commands/tunnel.ts"],"sourcesContent":["/**\n * Evident CLI\n *\n * Run OpenCode locally and connect it to the Evident platform.\n */\n\nimport { Command } from 'commander';\nimport { login } from './commands/login.js';\nimport { logout } from './commands/logout.js';\nimport { whoami } from './commands/whoami.js';\nimport { tunnel } from './commands/tunnel.js';\nimport { setEnvironment, type Environment } from './lib/config.js';\n\nconst program = new Command();\n\nprogram\n .name('evident')\n .description('Run OpenCode locally and connect it to Evident')\n .version('0.1.0')\n .option('-e, --env <environment>', 'Environment to use (local, dev, production)', 'production')\n .hook('preAction', (thisCommand) => {\n const env = thisCommand.opts().env as Environment;\n if (env) {\n setEnvironment(env);\n }\n });\n\n// Login command\nprogram\n .command('login')\n .description('Authenticate with Evident')\n .option('--token', 'Use token-based authentication (for CI/CD)')\n .option('--no-browser', 'Do not open the browser automatically')\n .action(login);\n\n// Logout command\nprogram.command('logout').description('Remove stored credentials').action(logout);\n\n// Whoami command\nprogram.command('whoami').description('Show the currently logged in user').action(whoami);\n\n// Tunnel command\nprogram\n .command('tunnel')\n .description('Establish a tunnel to Evident for Local Mode')\n .option('-s, --sandbox <id>', 'Sandbox ID to connect to')\n .option('-p, --port <port>', 'OpenCode port (default: 4096)', '4096')\n .action((options: { sandbox?: string; port: string }) => {\n tunnel({\n sandbox: options.sandbox,\n port: parseInt(options.port, 10),\n });\n });\n\n// Parse arguments\nprogram.parse();\n","/**\n * Login Command\n *\n * Authenticates the user using OAuth Device Flow.\n * See ADR-0018 for details.\n */\n\nimport open from 'open';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport { api } from '../lib/api.js';\nimport { storeToken } from '../lib/keychain.js';\nimport { printSuccess, printError, blank, waitForEnter, sleep } from '../utils/ui.js';\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n}\n\ninterface TokenPollResponse {\n status: 'pending' | 'complete' | 'expired';\n access_token?: string;\n expires_at?: string;\n user?: {\n id: string;\n email: string;\n };\n}\n\ninterface LoginOptions {\n token?: boolean;\n noBrowser?: boolean;\n}\n\n/**\n * Start device flow authentication\n */\nasync function deviceFlowLogin(options: LoginOptions): Promise<void> {\n // Step 1: Request device code\n let deviceAuth: DeviceAuthResponse;\n try {\n deviceAuth = await api.post<DeviceAuthResponse>('/auth/device');\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n printError(`Failed to start authentication: ${message}`);\n process.exit(1);\n }\n\n const { device_code, user_code, verification_uri, interval } = deviceAuth;\n\n // Step 2: Display instructions\n blank();\n console.log(chalk.bold('To authenticate, visit:'));\n console.log();\n console.log(` ${chalk.cyan(verification_uri)}`);\n console.log();\n console.log(chalk.bold('And enter this code:'));\n console.log();\n console.log(` ${chalk.yellow.bold(user_code)}`);\n blank();\n\n // Step 3: Open browser (unless --no-browser)\n if (!options.noBrowser) {\n await waitForEnter('Press Enter to open the browser...');\n try {\n await open(verification_uri);\n } catch {\n console.log(chalk.dim('Could not open browser. Please visit the URL manually.'));\n }\n }\n\n // Step 4: Poll for completion\n const spinner = ora('Waiting for authentication...').start();\n\n const pollIntervalMs = (interval || 5) * 1000;\n const maxAttempts = 60; // 5 minutes max at 5s intervals\n let attempts = 0;\n\n while (attempts < maxAttempts) {\n await sleep(pollIntervalMs);\n attempts++;\n\n try {\n const result = await api.post<TokenPollResponse>('/auth/device/token', {\n device_code,\n });\n\n if (result.status === 'complete' && result.access_token && result.user) {\n // Success! Store the token\n await storeToken({\n token: result.access_token,\n user: result.user,\n expiresAt: result.expires_at,\n });\n\n spinner.stop();\n blank();\n printSuccess(`Logged in as ${chalk.bold(result.user.email)}`);\n return;\n }\n\n if (result.status === 'expired') {\n spinner.stop();\n blank();\n printError('Authentication expired. Please try again.');\n process.exit(1);\n }\n\n // Still pending, continue polling\n } catch (error) {\n // Network error, continue polling\n const message = error instanceof Error ? error.message : 'Unknown error';\n spinner.text = `Waiting for authentication... (${message})`;\n }\n }\n\n spinner.stop();\n blank();\n printError('Authentication timed out. Please try again.');\n process.exit(1);\n}\n\n/**\n * Token-based login (for CI/CD)\n */\nasync function tokenLogin(): Promise<void> {\n console.log('Token login mode.');\n console.log('Visit your Evident dashboard to generate a CLI token.');\n blank();\n\n // Read token from stdin\n process.stdout.write('Paste token: ');\n\n const token = await new Promise<string>((resolve) => {\n let data = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk) => {\n data += chunk;\n });\n process.stdin.on('end', () => {\n resolve(data.trim());\n });\n // For TTY, read a single line\n if (process.stdin.isTTY) {\n process.stdin.once('data', (chunk) => {\n process.stdin.pause();\n resolve(chunk.toString().trim());\n });\n process.stdin.resume();\n }\n });\n\n if (!token) {\n printError('No token provided.');\n process.exit(1);\n }\n\n // Validate the token\n const spinner = ora('Validating token...').start();\n\n try {\n interface ValidateResponse {\n user: { id: string; email: string };\n expires_at?: string;\n }\n\n const result = await api.post<ValidateResponse>('/auth/token/validate', { token });\n\n await storeToken({\n token,\n user: result.user,\n expiresAt: result.expires_at,\n });\n\n spinner.stop();\n printSuccess(`Logged in as ${chalk.bold(result.user.email)}`);\n } catch (error) {\n spinner.stop();\n const message = error instanceof Error ? error.message : 'Invalid token';\n printError(`Authentication failed: ${message}`);\n process.exit(1);\n }\n}\n\n/**\n * Login command handler\n */\nexport async function login(options: LoginOptions): Promise<void> {\n if (options.token) {\n await tokenLogin();\n } else {\n await deviceFlowLogin(options);\n }\n}\n","/**\n * CLI Configuration\n *\n * Manages configuration values and file-based credential storage.\n * Supports multiple environments: local, dev, production\n */\n\nimport Conf from 'conf';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// Supported environments\nexport type Environment = 'local' | 'dev' | 'production';\n\n// Configuration schema\ninterface ConfigSchema {\n apiUrl: string;\n tunnelUrl: string;\n}\n\n// Credentials schema (stored separately with stricter permissions)\ninterface CredentialsSchema {\n token?: string;\n user?: {\n id: string;\n email: string;\n };\n expiresAt?: string;\n}\n\n// Environment presets\n// Domain format: {service}.{env}.evident.run (with aliases for production)\nconst environmentPresets: Record<Environment, ConfigSchema> = {\n local: {\n apiUrl: 'http://localhost:3000',\n tunnelUrl: 'ws://localhost:8787',\n },\n dev: {\n apiUrl: 'https://api.dev.evident.run',\n tunnelUrl: 'wss://tunnel.dev.evident.run',\n },\n production: {\n // Production URLs also have aliases: api.evident.run, tunnel.evident.run\n apiUrl: 'https://api.production.evident.run',\n tunnelUrl: 'wss://tunnel.production.evident.run',\n },\n};\n\n// Default to production\nconst defaults: ConfigSchema = environmentPresets.production;\n\n// Current environment (can be set via --env flag or EVIDENT_ENV)\nlet currentEnvironment: Environment = 'production';\n\n/**\n * Set the current environment\n */\nexport function setEnvironment(env: Environment): void {\n currentEnvironment = env;\n}\n\n/**\n * Get the current environment\n */\nexport function getEnvironment(): Environment {\n // Environment variable takes precedence\n const envVar = process.env.EVIDENT_ENV as Environment | undefined;\n if (envVar && environmentPresets[envVar]) {\n return envVar;\n }\n return currentEnvironment;\n}\n\n/**\n * Get configuration for current environment\n */\nfunction getEnvConfig(): ConfigSchema {\n return environmentPresets[getEnvironment()];\n}\n\n// Environment overrides (env vars take highest precedence)\nfunction getApiUrl(): string {\n return process.env.EVIDENT_API_URL ?? getEnvConfig().apiUrl;\n}\n\nfunction getTunnelUrl(): string {\n return process.env.EVIDENT_TUNNEL_URL ?? getEnvConfig().tunnelUrl;\n}\n\n// Configuration store\nconst config = new Conf<ConfigSchema>({\n projectName: 'evident',\n projectSuffix: '',\n defaults,\n});\n\n// Credentials store (separate file with restricted access)\nconst credentials = new Conf<CredentialsSchema>({\n projectName: 'evident',\n projectSuffix: '',\n configName: 'credentials',\n defaults: {},\n});\n\n/**\n * Get the configuration directory path\n */\nexport function getConfigDir(): string {\n // XDG_CONFIG_HOME on Linux, ~/.config on others\n const xdgConfig = process.env.XDG_CONFIG_HOME;\n if (xdgConfig) {\n return join(xdgConfig, 'evident');\n }\n return join(homedir(), '.config', 'evident');\n}\n\n/**\n * Get the API URL\n */\nexport function getApiUrlConfig(): string {\n return getApiUrl();\n}\n\n/**\n * Get the tunnel WebSocket URL\n */\nexport function getTunnelUrlConfig(): string {\n return getTunnelUrl();\n}\n\n/**\n * Get stored credentials\n */\nexport function getCredentials(): CredentialsSchema {\n return {\n token: credentials.get('token'),\n user: credentials.get('user'),\n expiresAt: credentials.get('expiresAt'),\n };\n}\n\n/**\n * Store credentials\n */\nexport function setCredentials(creds: CredentialsSchema): void {\n if (creds.token) credentials.set('token', creds.token);\n if (creds.user) credentials.set('user', creds.user);\n if (creds.expiresAt) credentials.set('expiresAt', creds.expiresAt);\n}\n\n/**\n * Clear stored credentials\n */\nexport function clearCredentials(): void {\n credentials.clear();\n}\n\n/**\n * Check if we have valid credentials\n */\nexport function hasValidCredentials(): boolean {\n const creds = getCredentials();\n\n if (!creds.token) {\n return false;\n }\n\n if (creds.expiresAt) {\n const expiresAt = new Date(creds.expiresAt);\n if (expiresAt < new Date()) {\n return false;\n }\n }\n\n return true;\n}\n\nexport { config, credentials };\n","/**\n * API Client\n *\n * Handles HTTP requests to the Evident backend API.\n */\n\nimport { getApiUrlConfig, getCredentials } from './config.js';\n\ninterface ApiRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n body?: unknown;\n headers?: Record<string, string>;\n authenticated?: boolean;\n}\n\ninterface ApiError {\n message: string;\n statusCode: number;\n error?: string;\n}\n\nexport class ApiClient {\n private baseUrl: string;\n\n constructor(baseUrl?: string) {\n this.baseUrl = baseUrl ?? getApiUrlConfig();\n }\n\n /**\n * Make an API request\n */\n async request<T>(path: string, options: ApiRequestOptions = {}): Promise<T> {\n const { method = 'GET', body, headers = {}, authenticated = false } = options;\n\n const url = `${this.baseUrl}${path}`;\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...headers,\n };\n\n // Add authentication header if requested\n if (authenticated) {\n const creds = getCredentials();\n if (!creds.token) {\n throw new Error('Not authenticated. Run `evident login` first.');\n }\n requestHeaders['Authorization'] = `Bearer ${creds.token}`;\n }\n\n const response = await fetch(url, {\n method,\n headers: requestHeaders,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n // Handle errors\n if (!response.ok) {\n let errorData: ApiError;\n try {\n errorData = (await response.json()) as ApiError;\n } catch {\n errorData = {\n message: response.statusText,\n statusCode: response.status,\n };\n }\n\n const error = new Error(errorData.message) as Error & { statusCode: number };\n error.statusCode = response.status;\n throw error;\n }\n\n // Handle empty responses\n const contentType = response.headers.get('Content-Type');\n if (!contentType?.includes('application/json')) {\n return {} as T;\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * GET request\n */\n async get<T>(path: string, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<T> {\n return this.request<T>(path, { ...options, method: 'GET' });\n }\n\n /**\n * POST request\n */\n async post<T>(\n path: string,\n body?: unknown,\n options: Omit<ApiRequestOptions, 'method'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'POST', body });\n }\n\n /**\n * PUT request\n */\n async put<T>(\n path: string,\n body?: unknown,\n options: Omit<ApiRequestOptions, 'method'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'PUT', body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(\n path: string,\n options: Omit<ApiRequestOptions, 'method' | 'body'> = {},\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: 'DELETE' });\n }\n}\n\n// Default API client instance\nexport const api = new ApiClient();\n","/**\n * Keychain Storage\n *\n * Provides secure credential storage using the system keychain.\n * Falls back to file-based storage if keychain is unavailable.\n */\n\nimport { getCredentials, setCredentials, clearCredentials } from './config.js';\n\nconst SERVICE_NAME = 'evident-cli';\nconst ACCOUNT_NAME = 'default';\n\n// Dynamic import for keytar (optional dependency)\nasync function getKeytar(): Promise<typeof import('keytar') | null> {\n try {\n return await import('keytar');\n } catch {\n // keytar not available (e.g., missing native dependencies)\n return null;\n }\n}\n\nexport interface StoredCredentials {\n token: string;\n user: {\n id: string;\n email: string;\n };\n expiresAt?: string;\n}\n\n/**\n * Store credentials in the system keychain\n */\nexport async function storeToken(credentials: StoredCredentials): Promise<void> {\n const keytar = await getKeytar();\n\n if (keytar) {\n // Store in system keychain\n await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(credentials));\n } else {\n // Fallback to file-based storage\n setCredentials({\n token: credentials.token,\n user: credentials.user,\n expiresAt: credentials.expiresAt,\n });\n }\n}\n\n/**\n * Retrieve credentials from the system keychain\n */\nexport async function getToken(): Promise<StoredCredentials | null> {\n const keytar = await getKeytar();\n\n if (keytar) {\n // Try system keychain first\n const stored = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);\n if (stored) {\n try {\n return JSON.parse(stored) as StoredCredentials;\n } catch {\n // Invalid JSON, clear it\n await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);\n return null;\n }\n }\n }\n\n // Fallback to file-based storage\n const creds = getCredentials();\n if (creds.token && creds.user) {\n return {\n token: creds.token,\n user: creds.user,\n expiresAt: creds.expiresAt,\n };\n }\n\n return null;\n}\n\n/**\n * Delete credentials from the system keychain\n */\nexport async function deleteToken(): Promise<void> {\n const keytar = await getKeytar();\n\n if (keytar) {\n await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);\n }\n\n // Always clear file-based storage too\n clearCredentials();\n}\n\n/**\n * Check if credentials are valid (not expired)\n */\nexport async function hasValidToken(): Promise<boolean> {\n const credentials = await getToken();\n\n if (!credentials) {\n return false;\n }\n\n if (credentials.expiresAt) {\n const expiresAt = new Date(credentials.expiresAt);\n if (expiresAt < new Date()) {\n return false;\n }\n }\n\n return true;\n}\n","/**\n * CLI UI Utilities\n *\n * Common formatting and display functions.\n */\n\nimport chalk from 'chalk';\n\n/**\n * Format success message\n */\nexport function success(message: string): string {\n return `${chalk.green('✓')} ${message}`;\n}\n\n/**\n * Format error message\n */\nexport function error(message: string): string {\n return `${chalk.red('✗')} ${message}`;\n}\n\n/**\n * Format warning message\n */\nexport function warning(message: string): string {\n return `${chalk.yellow('!')} ${message}`;\n}\n\n/**\n * Format info message\n */\nexport function info(message: string): string {\n return `${chalk.blue('i')} ${message}`;\n}\n\n/**\n * Print success message\n */\nexport function printSuccess(message: string): void {\n console.log(success(message));\n}\n\n/**\n * Print error message\n */\nexport function printError(message: string): void {\n console.error(error(message));\n}\n\n/**\n * Print warning message\n */\nexport function printWarning(message: string): void {\n console.log(warning(message));\n}\n\n/**\n * Print info message\n */\nexport function printInfo(message: string): void {\n console.log(info(message));\n}\n\n/**\n * Format a key-value pair for display\n */\nexport function keyValue(key: string, value: string): string {\n return `${chalk.dim(key + ':')} ${value}`;\n}\n\n/**\n * Print a blank line\n */\nexport function blank(): void {\n console.log();\n}\n\n/**\n * Wait for user to press Enter\n */\nexport function waitForEnter(prompt = 'Press Enter to continue...'): Promise<void> {\n return new Promise((resolve) => {\n process.stdout.write(chalk.dim(prompt));\n\n const handler = (): void => {\n process.stdin.removeListener('data', handler);\n process.stdin.setRawMode?.(false);\n process.stdin.pause();\n console.log();\n resolve();\n };\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode?.(true);\n }\n process.stdin.resume();\n process.stdin.once('data', handler);\n });\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Logout Command\n *\n * Removes stored credentials.\n */\n\nimport { deleteToken, getToken } from '../lib/keychain.js';\nimport { printSuccess, printWarning } from '../utils/ui.js';\n\n/**\n * Logout command handler\n */\nexport async function logout(): Promise<void> {\n const credentials = await getToken();\n\n if (!credentials) {\n printWarning('You are not logged in.');\n return;\n }\n\n await deleteToken();\n printSuccess('Logged out successfully.');\n}\n","/**\n * Whoami Command\n *\n * Displays the currently logged in user.\n */\n\nimport chalk from 'chalk';\nimport { getToken } from '../lib/keychain.js';\nimport { printError, keyValue, blank } from '../utils/ui.js';\n\n/**\n * Whoami command handler\n */\nexport async function whoami(): Promise<void> {\n const credentials = await getToken();\n\n if (!credentials) {\n printError('Not logged in. Run `evident login` to authenticate.');\n process.exit(1);\n }\n\n blank();\n console.log(keyValue('User', chalk.bold(credentials.user.email)));\n console.log(keyValue('User ID', credentials.user.id));\n\n if (credentials.expiresAt) {\n const expiresAt = new Date(credentials.expiresAt);\n const now = new Date();\n\n if (expiresAt < now) {\n console.log(keyValue('Status', chalk.red('Token expired')));\n } else {\n const daysRemaining = Math.ceil(\n (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),\n );\n console.log(keyValue('Expires', `${daysRemaining} days`));\n }\n }\n\n blank();\n}\n","/**\n * Tunnel Command\n *\n * Establishes a WebSocket tunnel to Evident for Local Mode.\n * See ADR-0020 for architecture details.\n */\n\nimport WebSocket from 'ws';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { getToken } from '../lib/keychain.js';\nimport { getTunnelUrlConfig } from '../lib/config.js';\nimport { printError, printWarning, blank, sleep } from '../utils/ui.js';\n\ninterface TunnelOptions {\n sandbox?: string;\n port?: number;\n}\n\ninterface RelayMessage {\n type: 'connected' | 'error' | 'ping' | 'request';\n sandbox_id?: string;\n code?: string;\n message?: string;\n id?: string;\n payload?: {\n method: string;\n path: string;\n headers?: Record<string, string>;\n body?: unknown;\n };\n}\n\ninterface TunnelState {\n connected: boolean;\n sandboxId: string | null;\n reconnectAttempt: number;\n lastActivity: Date;\n}\n\nconst MAX_RECONNECT_DELAY = 30000; // 30 seconds\nconst BASE_RECONNECT_DELAY = 500; // 0.5 seconds\n\n/**\n * Calculate reconnect delay with exponential backoff and jitter\n */\nfunction getReconnectDelay(attempt: number): number {\n const exponentialDelay = BASE_RECONNECT_DELAY * Math.pow(2, attempt);\n const jitter = Math.random() * 1000;\n return Math.min(exponentialDelay + jitter, MAX_RECONNECT_DELAY);\n}\n\n/**\n * Display tunnel status\n */\nfunction displayStatus(state: TunnelState): void {\n console.clear();\n console.log(chalk.bold('Evident Tunnel'));\n console.log(chalk.dim('─'.repeat(50)));\n blank();\n\n if (state.connected) {\n console.log(` ${chalk.green('●')} Status: ${chalk.green('Online')}`);\n console.log(` Sandbox: ${state.sandboxId ?? 'Unknown'}`);\n console.log(` Last activity: ${state.lastActivity.toLocaleTimeString()}`);\n } else {\n console.log(` ${chalk.yellow('○')} Status: ${chalk.yellow('Reconnecting...')}`);\n if (state.reconnectAttempt > 0) {\n console.log(` Attempt: ${state.reconnectAttempt}`);\n }\n }\n\n blank();\n console.log(chalk.dim('Press Ctrl+C to disconnect'));\n}\n\n/**\n * Forward request to local OpenCode instance\n */\nasync function forwardToOpenCode(\n port: number,\n request: { method: string; path: string; headers?: Record<string, string>; body?: unknown },\n): Promise<{ status: number; headers?: Record<string, string>; body?: unknown }> {\n const url = `http://localhost:${port}${request.path}`;\n\n try {\n const response = await fetch(url, {\n method: request.method,\n headers: {\n 'Content-Type': 'application/json',\n ...request.headers,\n },\n body: request.body ? JSON.stringify(request.body) : undefined,\n });\n\n let body: unknown;\n const contentType = response.headers.get('Content-Type');\n if (contentType?.includes('application/json')) {\n body = await response.json();\n } else {\n body = await response.text();\n }\n\n return {\n status: response.status,\n body,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n status: 502,\n body: { error: 'Failed to connect to OpenCode', message },\n };\n }\n}\n\n/**\n * Establish tunnel connection\n */\nasync function connect(\n token: string,\n sandboxId: string | undefined,\n port: number,\n state: TunnelState,\n): Promise<void> {\n const tunnelUrl = getTunnelUrlConfig();\n const url = sandboxId\n ? `${tunnelUrl}/tunnel/${sandboxId}/connect`\n : `${tunnelUrl}/tunnel/default/connect`;\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n ws.on('open', () => {\n state.connected = true;\n state.reconnectAttempt = 0;\n state.lastActivity = new Date();\n displayStatus(state);\n });\n\n ws.on('message', async (data: WebSocket.RawData) => {\n try {\n const message: RelayMessage = JSON.parse(data.toString());\n state.lastActivity = new Date();\n\n switch (message.type) {\n case 'connected':\n state.sandboxId = message.sandbox_id ?? null;\n displayStatus(state);\n break;\n\n case 'error':\n printError(`Tunnel error: ${message.message}`);\n if (message.code === 'unauthorized') {\n ws.close();\n reject(new Error('Unauthorized'));\n }\n break;\n\n case 'ping':\n ws.send(JSON.stringify({ type: 'pong' }));\n break;\n\n case 'request':\n if (message.id && message.payload) {\n const response = await forwardToOpenCode(port, message.payload);\n ws.send(\n JSON.stringify({\n type: 'response',\n id: message.id,\n payload: response,\n }),\n );\n displayStatus(state);\n }\n break;\n }\n } catch (error) {\n console.error('Failed to handle message:', error);\n }\n });\n\n ws.on('close', () => {\n state.connected = false;\n displayStatus(state);\n resolve();\n });\n\n ws.on('error', (error: Error) => {\n state.connected = false;\n displayStatus(state);\n // Don't reject on connection errors, let it reconnect\n console.error(chalk.dim(`Connection error: ${error.message}`));\n });\n\n // Handle Ctrl+C\n const cleanup = (): void => {\n ws.close();\n process.exit(0);\n };\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n });\n}\n\n/**\n * Tunnel command handler\n */\nexport async function tunnel(options: TunnelOptions): Promise<void> {\n // Get credentials\n const credentials = await getToken();\n if (!credentials) {\n printError('Not logged in. Run `evident login` first.');\n process.exit(1);\n }\n\n const port = options.port ?? 4096;\n const sandboxId = options.sandbox;\n\n // Check if OpenCode is running\n const spinner = ora('Checking OpenCode connection...').start();\n\n try {\n const response = await fetch(`http://localhost:${port}/health`);\n if (!response.ok) {\n throw new Error('Health check failed');\n }\n spinner.succeed(`OpenCode detected on port ${port}`);\n } catch {\n spinner.warn(`Could not connect to OpenCode on port ${port}`);\n printWarning('Make sure OpenCode is running before starting the tunnel.');\n blank();\n }\n\n // Connection state\n const state: TunnelState = {\n connected: false,\n sandboxId: null,\n reconnectAttempt: 0,\n lastActivity: new Date(),\n };\n\n // Reconnection loop\n while (true) {\n try {\n await connect(credentials.token, sandboxId, port, state);\n\n // Connection closed, attempt reconnect\n state.reconnectAttempt++;\n const delay = getReconnectDelay(state.reconnectAttempt);\n\n displayStatus(state);\n await sleep(delay);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n\n if (message === 'Unauthorized') {\n printError('Authentication failed. Please run `evident login` again.');\n process.exit(1);\n }\n\n state.reconnectAttempt++;\n const delay = getReconnectDelay(state.reconnectAttempt);\n await sleep(delay);\n }\n }\n}\n"],"mappings":"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","names":["chalk","error","credentials","error","chalk","credentials","chalk","credentials","chalk","chalk","ora","chalk","error","credentials","ora"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evident-ai/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Evident CLI - Run OpenCode locally and connect to Evident",
5
5
  "type": "module",
6
6
  "bin": {