@elf5/periscope 1.0.65 → 1.0.70
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/cli.js +34 -40
- package/dist/cli.js.map +2 -2
- package/dist/commands/base-command.d.ts.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/index.js +34 -40
- package/dist/index.js.map +2 -2
- package/dist/lib/config-manager.d.ts +1 -0
- package/dist/lib/config-manager.d.ts.map +1 -1
- package/package.json +9 -11
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/lib/readline-instance.ts", "../src/lib/telemetry.ts", "../src/lib/process-lifecycle.ts", "../package.json", "../src/lib/msal-auth-manager.ts", "../src/lib/logger.ts", "../src/lib/cache-utils.ts", "../src/lib/msal-cache-plugin.ts", "../src/lib/auth-callback-server.ts", "../src/lib/auth-types.ts", "../src/lib/auth0-auth-manager.ts", "../node_modules/@elf-5/periscope-api-client/dist/Generated/PeriscopeApiClient.js", "../src/lib/server-config.ts", "../src/lib/client.ts", "../src/lib/secure-memory.ts", "../src/lib/tunnel-manager.ts", "../src/lib/ssh-key-manager.ts", "../src/lib/interactive-utils.ts", "../src/lib/tunnel-utils.ts", "../src/lib/error-classifier.ts", "../src/lib/request-monitor.ts", "../src/lib/config-manager.ts", "../src/commands/tunnel.ts", "../src/commands/base-command.ts", "../src/lib/terms.ts", "../src/commands/config.ts", "../src/commands/status.ts", "../src/commands/auth.ts", "../src/commands/user.ts", "../src/interactive.ts"],
|
|
4
|
-
"sourcesContent": ["import * as readline from 'readline';\nimport chalk from 'chalk';\n\n// Module-level readline interface that can be shared across commands\nlet readlineInstance: readline.Interface | null = null;\n\nexport function getReadlineInterface(): readline.Interface {\n if (!readlineInstance) {\n // Auto-initialize if not already done\n initializeReadline();\n }\n return readlineInstance!;\n}\n\nexport function initializeReadline(\n isInteractive: boolean = false,\n completer?: readline.Completer\n): void {\n if (readlineInstance) {\n return; // Already initialized, do nothing\n }\n\n readlineInstance = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: isInteractive ? chalk.cyan('periscope> ') : undefined,\n completer,\n });\n}\n\nexport function closeReadline(): void {\n if (readlineInstance) {\n readlineInstance.close();\n readlineInstance = null;\n }\n}\n\nexport function isReadlineActive(): boolean {\n return readlineInstance !== null;\n}\n", "/**\n * Application Insights telemetry module for the Periscope CLI.\n *\n * Provides crash/exception tracking and event telemetry.\n * Graceful no-op when not initialized (no connection string configured).\n * Connection string is fetched post-auth from the server via /api/configuration/telemetry.\n */\n\nimport type { TelemetryClient } from 'applicationinsights';\nimport * as os from 'os';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\n\nlet client: TelemetryClient | null = null;\nlet initialized = false;\nlet userEmail: string | null = null;\n\nfunction getCliVersion(): string {\n try {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(\n fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8')\n );\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Initialize Application Insights telemetry.\n * Must be called with a connection string from the server configuration.\n * Safe to call multiple times \u2014 subsequent calls are ignored.\n *\n * @returns true if telemetry was initialized, false if skipped\n */\nexport async function initTelemetry(\n connectionString: string\n): Promise<boolean> {\n if (initialized) return false;\n if (!connectionString) return false;\n\n try {\n // Set cloud role name via OpenTelemetry env var (v3 SDK ignores classic context.tags)\n process.env.OTEL_SERVICE_NAME = 'periscope-cli';\n\n // Dynamic import \u2014 required because esbuild ESM output doesn't support require()\n const appInsights = await import('applicationinsights');\n\n appInsights.default\n .setup(connectionString)\n .setAutoCollectRequests(false)\n .setAutoCollectPerformance(false, false)\n .setAutoCollectExceptions(false) // We handle exceptions ourselves for proper flush-before-exit\n .setAutoCollectDependencies(true)\n .setAutoCollectConsole(false)\n .setAutoCollectPreAggregatedMetrics(false)\n .setUseDiskRetryCaching(false) // CLI is ephemeral \u2014 disk cache won't help\n .start();\n\n client = appInsights.default.defaultClient;\n\n if (client) {\n const version = getCliVersion();\n\n // Common properties attached to every telemetry item\n client.commonProperties = {\n cliVersion: version,\n nodeVersion: process.version,\n platform: os.platform(),\n arch: os.arch(),\n };\n\n initialized = true;\n return true;\n }\n } catch {\n // Telemetry init failure should never break the CLI\n client = null;\n }\n\n return false;\n}\n\n/**\n * Track an exception in Application Insights.\n * No-op if telemetry is not initialized.\n */\nexport function trackException(\n error: unknown,\n properties?: Record<string, string>\n): void {\n if (!client) return;\n\n const exception = error instanceof Error ? error : new Error(String(error));\n\n // Caller-provided 'email' in properties takes precedence over module-level userEmail\n const enriched = userEmail ? { email: userEmail, ...properties } : properties;\n\n client.trackException({\n exception,\n properties: enriched,\n });\n}\n\n/**\n * Track a custom event in Application Insights.\n * No-op if telemetry is not initialized.\n */\nexport function trackEvent(\n name: string,\n properties?: Record<string, string>\n): void {\n if (!client) return;\n\n // Caller-provided 'email' in properties takes precedence over module-level userEmail\n const enriched = userEmail ? { email: userEmail, ...properties } : properties;\n\n client.trackEvent({ name, properties: enriched });\n}\n\n/**\n * Store the authenticated user's email for enrichment of subsequent telemetry events.\n * Safe to call before or after initTelemetry.\n */\nexport function setUserContext(email?: string): void {\n if (email) {\n userEmail = email;\n }\n}\n\n/**\n * Flush all buffered telemetry to Application Insights.\n * Returns a Promise that resolves when flush completes or times out.\n * No-op (resolves immediately) if telemetry is not initialized.\n */\nexport async function flushTelemetry(): Promise<void> {\n if (!client) return;\n\n try {\n await Promise.race([\n client.flush(),\n new Promise<void>(resolve => setTimeout(resolve, 5000)),\n ]);\n } catch {\n // Best-effort \u2014 don't let flush failures prevent exit\n }\n}\n\n/**\n * Flush buffered telemetry and dispose the SDK so its background timers\n * don't keep the process alive. Call this before process exit.\n */\nexport async function shutdownTelemetry(): Promise<void> {\n await flushTelemetry();\n if (client) {\n try {\n const appInsights = await import('applicationinsights');\n appInsights.dispose();\n } catch {\n // Best-effort\n }\n client = null;\n initialized = false;\n userEmail = null;\n }\n}\n", "/**\n * Centralized process exit handling.\n *\n * All exit paths should call gracefulExit() instead of process.exit()\n * to ensure telemetry is flushed, the SDK is disposed, and readline\n * is closed before the process terminates.\n */\n\nimport { closeReadline } from './readline-instance.js';\nimport { shutdownTelemetry } from './telemetry.js';\n\n/**\n * Cleanly shut down and exit the process.\n * Flushes + disposes telemetry, closes readline, then exits.\n */\nexport async function gracefulExit(code: number = 0): Promise<never> {\n await shutdownTelemetry();\n closeReadline();\n process.exit(code);\n}\n", "{\n \"name\": \"@elf5/periscope\",\n \"version\": \"1.0.65\",\n \"description\": \"CLI client for Periscope SSH tunnel server\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"bin\": {\n \"periscope\": \"./dist/cli.js\"\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"build:tsc\": \"tsc\",\n \"dev\": \"tsc --watch\",\n \"clean\": \"rimraf dist\",\n \"prepublishOnly\": \"npm run clean && npm run build\",\n \"start\": \"node dist/cli.js\",\n \"cli\": \"npm run build && node dist/cli.js\",\n \"test\": \"npm run test:unit && npm run test:offline\",\n \"test:watch\": \"vitest --config vitest.config.unit.js\",\n \"test:ui\": \"vitest --ui --config vitest.config.unit.js\",\n \"test:unit\": \"vitest run --coverage --config vitest.config.unit.js\",\n \"test:offline\": \"vitest run --config vitest.config.integration.js\",\n \"test:e2e\": \"vitest run --config vitest.config.e2e.js\",\n \"test:ci\": \"npm run test && npm run test:e2e\",\n \"format\": \"prettier --write src/**/*.ts\",\n \"format:check\": \"prettier --check src/**/*.ts\",\n \"lint\": \"eslint src\",\n \"lint:fix\": \"eslint src --fix\",\n \"test-server\": \"npx tsx src/__tests__/e2e/test-server.ts\",\n \"prepare\": \"husky\"\n },\n \"keywords\": [\n \"ssh\",\n \"tunnel\",\n \"cli\",\n \"periscope\",\n \"port-forwarding\"\n ],\n \"author\": \"Elf 5\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"email\": \"support@elf5.com\"\n },\n \"homepage\": \"https://elf5.com\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"overrides\": {\n \"glob\": \"^11.0.0\",\n \"uuid\": \"^10.0.0\"\n },\n \"dependencies\": {\n \"@azure/msal-node\": \"^2.6.6\",\n \"@microsoft/dev-tunnels-connections\": \"^1.2.1\",\n \"@microsoft/dev-tunnels-contracts\": \"^1.2.1\",\n \"@microsoft/dev-tunnels-management\": \"^1.2.1\",\n \"@microsoft/dev-tunnels-ssh\": \"^3.12.5\",\n \"@microsoft/dev-tunnels-ssh-keys\": \"^3.12.5\",\n \"@microsoft/dev-tunnels-ssh-tcp\": \"^3.12.5\",\n \"applicationinsights\": \"^3.13.0\",\n \"axios\": \"^1.7.2\",\n \"chalk\": \"^5.3.0\",\n \"commander\": \"^12.0.0\",\n \"dotenv\": \"^16.4.5\",\n \"open\": \"^10.0.3\",\n \"openid-client\": \"^6.8.2\"\n },\n \"lint-staged\": {\n \"*.ts\": [\n \"prettier --write\",\n \"eslint --fix\"\n ]\n },\n \"devDependencies\": {\n \"@elf-5/periscope-api-client\": \"^1.0.129\",\n \"@types/node\": \"^20.14.0\",\n \"@typescript-eslint/eslint-plugin\": \"^8.41.0\",\n \"@typescript-eslint/parser\": \"^8.41.0\",\n \"@vitest/coverage-v8\": \"^3.2.4\",\n \"@vitest/ui\": \"^3.2.4\",\n \"esbuild\": \"^0.27.3\",\n \"eslint\": \"^9.34.0\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"globals\": \"^16.3.0\",\n \"husky\": \"^9.1.7\",\n \"lint-staged\": \"^16.2.7\",\n \"prettier\": \"^3.8.1\",\n \"rimraf\": \"^6.0.0\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.4.5\",\n \"vitest\": \"^3.2.4\",\n \"yocto-queue\": \"^1.2.1\"\n }\n}\n", "import {\n PublicClientApplication,\n LogLevel,\n AuthorizationCodeRequest,\n} from '@azure/msal-node';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as http from 'http';\nimport * as crypto from 'crypto';\nimport open from 'open';\nimport { log } from './logger.js';\nimport { getCacheDir, writeSecureFile } from './cache-utils.js';\nimport { createMsalCachePlugin, clearMsalCache } from './msal-cache-plugin.js';\nimport { listenForAuthCode } from './auth-callback-server.js';\nimport {\n isTokenExpired,\n type AuthToken,\n type IAuthManager,\n type PromptValue,\n} from './auth-types.js';\n\nexport interface MSALConfig {\n clientId: string;\n authority: string;\n scopes: string[];\n}\n\nexport class MsalAuthManager implements IAuthManager {\n private static readonly TOKEN_CACHE_FILE = 'token-cache.json';\n private msalApp: PublicClientApplication | null = null;\n private config: MSALConfig | null = null;\n\n constructor(config?: MSALConfig) {\n if (config) {\n this.config = config;\n this.initializeMSAL();\n }\n }\n\n private initializeMSAL(): void {\n if (!this.config) {\n throw new Error('MSAL configuration is required');\n }\n\n const msalConfig = {\n auth: {\n clientId: this.config.clientId,\n authority: this.config.authority,\n // B2C requires knownAuthorities to include the B2C login domain\n knownAuthorities: MsalAuthManager.extractKnownAuthorities(\n this.config.authority\n ),\n },\n cache: {\n cachePlugin: createMsalCachePlugin(),\n },\n system: {\n loggerOptions: {\n loggerCallback(loglevel: LogLevel, message: string) {\n // Only log errors in production\n if (loglevel === LogLevel.Error) {\n log.error('MSAL Error:', message);\n }\n },\n piiLoggingEnabled: false,\n logLevel: LogLevel.Error,\n },\n },\n };\n\n this.msalApp = new PublicClientApplication(msalConfig);\n }\n\n /**\n * Extract knownAuthorities from an authority URL for non-standard authority domains.\n * B2C (*.b2clogin.com) and Entra External ID (*.ciamlogin.com) must be listed in knownAuthorities.\n */\n private static extractKnownAuthorities(authority: string): string[] {\n try {\n const url = new URL(authority);\n if (\n url.hostname.includes('.b2clogin.com') ||\n url.hostname.includes('.ciamlogin.com')\n ) {\n return [url.hostname];\n }\n } catch {\n // Not a valid URL \u2014 no known authorities needed\n }\n return [];\n }\n\n /**\n * Authenticate user using device code flow\n * This will display a device code and open the browser for authentication\n */\n async authenticate(): Promise<AuthToken> {\n if (!this.msalApp || !this.config) {\n throw new Error(\n 'MSAL not initialized. Please configure authentication first.'\n );\n }\n\n try {\n // Try to get token silently first (from cache)\n const cachedToken = this.getCachedToken();\n if (cachedToken && !isTokenExpired(cachedToken.expiresOn)) {\n return cachedToken;\n }\n\n // Use device code flow for CLI authentication\n const deviceCodeRequest = {\n scopes: this.config.scopes,\n deviceCodeCallback: (response: {\n verificationUri: string;\n userCode: string;\n }) => {\n log.blank();\n log.info('To authenticate, use a web browser to open the page:');\n log.info(` ${response.verificationUri}`);\n log.blank();\n log.info('And enter the code:');\n log.info(` ${response.userCode}`);\n log.blank();\n log.info('Opening browser automatically...');\n log.blank();\n\n // Open the browser automatically\n open(response.verificationUri).catch(() => {\n log.warn(\n 'Failed to open browser automatically. Please open the URL manually.'\n );\n });\n },\n };\n\n const response =\n await this.msalApp.acquireTokenByDeviceCode(deviceCodeRequest);\n\n if (!response) {\n throw new Error('Failed to acquire authentication token');\n }\n\n const authToken: AuthToken = {\n accessToken: response.accessToken,\n expiresOn: response.expiresOn || new Date(Date.now() + 3600000), // Default 1 hour\n // Note: MSAL node may not provide refresh token in all flows\n };\n\n // Cache the token securely\n this.cacheToken(authToken);\n\n return authToken;\n } catch (error) {\n throw new Error(\n `Authentication failed: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n /**\n * Authenticate user using interactive browser flow with prompt control\n * This opens the system browser for authentication and handles the redirect\n */\n async authenticateInteractive(\n prompt: PromptValue = 'select_account'\n ): Promise<AuthToken> {\n if (!this.msalApp || !this.config) {\n throw new Error(\n 'MSAL not initialized. Please configure authentication first.'\n );\n }\n\n try {\n // For 'none' prompt, try silent authentication first\n if (prompt === 'none') {\n try {\n // Get all cached accounts\n const accounts = await this.msalApp.getAllAccounts();\n if (accounts && accounts.length > 0) {\n const silentRequest = {\n scopes: this.config.scopes,\n account: accounts[0], // Use the first available account\n };\n const silentResponse =\n await this.msalApp.acquireTokenSilent(silentRequest);\n\n if (silentResponse) {\n const authToken: AuthToken = {\n accessToken: silentResponse.accessToken,\n expiresOn:\n silentResponse.expiresOn || new Date(Date.now() + 3600000),\n };\n this.cacheToken(authToken);\n return authToken;\n }\n }\n } catch {\n // Silent authentication failed\n throw new Error(\n 'Silent authentication failed. User interaction required.'\n );\n }\n }\n\n // Use interactive browser flow\n log.info(\n `\\nStarting browser authentication with prompt behavior: ${prompt}`\n );\n\n // Set up local server for redirect handling\n const redirectPort = await this.getAvailablePort();\n const redirectUri = `http://localhost:${redirectPort}`;\n\n // Generate PKCE challenge\n const pkceCodes = {\n challenge: '',\n verifier: '',\n };\n\n // Create cryptographically random verifier\n pkceCodes.verifier = crypto.randomBytes(32).toString('base64url');\n pkceCodes.challenge = crypto\n .createHash('sha256')\n .update(pkceCodes.verifier)\n .digest('base64url');\n\n // Build the authorization URL\n const authCodeUrlParameters = {\n scopes: this.config.scopes,\n redirectUri,\n codeChallenge: pkceCodes.challenge,\n codeChallengeMethod: 'S256' as const,\n prompt: prompt as 'login' | 'none' | 'consent' | 'select_account', // MSAL expects specific prompt values\n responseMode: 'query' as const,\n };\n\n const authCodeUrl = await this.msalApp.getAuthCodeUrl(\n authCodeUrlParameters\n );\n\n // Set up local server to handle redirect\n const result = await listenForAuthCode(redirectPort, authCodeUrl);\n\n if (!result) {\n throw new Error('Failed to receive authorization code');\n }\n\n // Exchange authorization code for token\n const tokenRequest: AuthorizationCodeRequest = {\n code: result.code,\n scopes: this.config.scopes,\n redirectUri,\n codeVerifier: pkceCodes.verifier,\n };\n\n const response = await this.msalApp.acquireTokenByCode(tokenRequest);\n\n if (!response) {\n throw new Error('Failed to acquire authentication token');\n }\n\n const authToken: AuthToken = {\n accessToken: response.accessToken,\n expiresOn: response.expiresOn || new Date(Date.now() + 3600000),\n };\n\n // Cache the token securely\n this.cacheToken(authToken);\n\n return authToken;\n } catch (error) {\n throw new Error(\n `Browser authentication failed: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n /**\n * Get an available port for the redirect server\n */\n private async getAvailablePort(): Promise<number> {\n return new Promise(resolve => {\n const server = http.createServer();\n server.listen(0, '127.0.0.1', () => {\n const address = server.address();\n const port =\n address && typeof address !== 'string' ? address.port : 3000;\n server.close(() => resolve(port));\n });\n });\n }\n\n /**\n * Try to get a valid token without user interaction.\n * Checks the simple cache first, then attempts MSAL silent refresh.\n * Returns null if no valid token is available.\n */\n async tryGetValidTokenSilently(): Promise<string | null> {\n // Fast check: valid token in simple cache?\n const cachedToken = this.getCachedToken();\n if (cachedToken && !isTokenExpired(cachedToken.expiresOn)) {\n const ttlMin = Math.round(\n (cachedToken.expiresOn.getTime() - Date.now()) / 60_000\n );\n log.debug(`Using cached MSAL token (expires in ${ttlMin}m)`);\n return cachedToken.accessToken;\n }\n\n // Try silent refresh via MSAL (uses refresh token from persistent cache)\n if (this.msalApp && this.config) {\n try {\n const accounts = await this.msalApp.getAllAccounts();\n if (accounts.length > 0) {\n log.debug('Attempting silent token refresh...');\n const silentResult = await this.msalApp.acquireTokenSilent({\n account: accounts[0],\n scopes: this.config.scopes,\n });\n\n if (silentResult) {\n const refreshedToken: AuthToken = {\n accessToken: silentResult.accessToken,\n expiresOn:\n silentResult.expiresOn || new Date(Date.now() + 3600000),\n };\n this.cacheToken(refreshedToken);\n const ttlMin = Math.round(\n (refreshedToken.expiresOn.getTime() - Date.now()) / 60_000\n );\n log.debug(`Token silently refreshed (expires in ${ttlMin}m)`);\n return refreshedToken.accessToken;\n }\n }\n } catch (error) {\n log.debug(\n 'Silent token refresh failed, falling back to interactive auth:',\n error\n );\n }\n }\n\n return null;\n }\n\n /**\n * Get a valid access token, refreshing if necessary\n */\n async getValidToken(): Promise<string> {\n const token = await this.tryGetValidTokenSilently();\n if (token) {\n return token;\n }\n\n // Fallback: full interactive authentication\n const newToken = await this.authenticate();\n return newToken.accessToken;\n }\n\n /**\n * Check if user is currently authenticated with a valid token\n */\n isAuthenticated(): boolean {\n const cachedToken = this.getCachedToken();\n return cachedToken !== null && !isTokenExpired(cachedToken.expiresOn);\n }\n\n /**\n * Clear all cached authentication data\n */\n logout(): void {\n try {\n const tokenCachePath = this.getTokenCachePath();\n if (fs.existsSync(tokenCachePath)) {\n fs.unlinkSync(tokenCachePath);\n }\n } catch (error) {\n log.error('Error clearing token cache during logout:', error);\n }\n\n // Clear the MSAL persistent cache (refresh tokens, accounts)\n clearMsalCache();\n }\n\n private getCachedToken(): AuthToken | null {\n try {\n const tokenCachePath = this.getTokenCachePath();\n if (fs.existsSync(tokenCachePath)) {\n const content = fs.readFileSync(tokenCachePath, 'utf-8');\n const data = JSON.parse(content);\n return {\n accessToken: data.accessToken,\n expiresOn: new Date(data.expiresOn),\n refreshToken: data.refreshToken,\n };\n }\n } catch (error) {\n log.debug('Failed to load cached token:', error);\n }\n return null;\n }\n\n private cacheToken(token: AuthToken): void {\n try {\n const tokenCachePath = this.getTokenCachePath();\n const tokenData = {\n accessToken: token.accessToken,\n expiresOn: token.expiresOn.toISOString(),\n refreshToken: token.refreshToken,\n };\n\n writeSecureFile(tokenCachePath, JSON.stringify(tokenData, null, 2));\n } catch (error) {\n log.error('Failed to cache token:', error);\n }\n }\n\n private getTokenCachePath(): string {\n return path.join(getCacheDir(), MsalAuthManager.TOKEN_CACHE_FILE);\n }\n}\n", "import chalk from 'chalk';\nimport { createRequire } from 'module';\n\nexport enum LogLevel {\n ERROR = 0,\n WARN = 1,\n INFO = 2,\n DEBUG = 3,\n TRACE = 4,\n}\n\nexport interface LoggerConfig {\n level: LogLevel;\n prefix?: string;\n timestamp?: boolean;\n}\n\nexport class Logger {\n private config: LoggerConfig;\n\n constructor(config: LoggerConfig = { level: LogLevel.INFO }) {\n this.config = config;\n }\n\n /**\n * Set the current log level\n */\n setLevel(level: LogLevel): void {\n this.config.level = level;\n }\n\n /**\n * Get the current log level\n */\n getLevel(): LogLevel {\n return this.config.level;\n }\n\n /**\n * Check if a log level should be output\n */\n private shouldLog(level: LogLevel): boolean {\n return level <= this.config.level;\n }\n\n /**\n * Format an argument for logging\n * Stack traces are only included at DEBUG level or higher\n */\n private formatArg(arg: unknown): string {\n if (arg instanceof Error) {\n // Only include stack trace at DEBUG level or above\n if (this.shouldLog(LogLevel.DEBUG) && arg.stack) {\n return `${arg.message}\\n${arg.stack}`;\n }\n return arg.message;\n } else if (typeof arg === 'object' && arg !== null) {\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n } else {\n return String(arg);\n }\n }\n\n /**\n * Format the log message with optional timestamp and prefix\n */\n private formatMessage(\n level: LogLevel,\n message: string,\n ...args: unknown[]\n ): string {\n let formatted = message;\n\n // Apply arguments if provided\n if (args.length > 0) {\n formatted =\n message + ' ' + args.map(arg => this.formatArg(arg)).join(' ');\n }\n\n // Add timestamp if enabled\n if (this.config.timestamp) {\n const timestamp = new Date().toISOString();\n formatted = `[${timestamp}] ${formatted}`;\n }\n\n // Add prefix if provided\n if (this.config.prefix) {\n formatted = `[${this.config.prefix}] ${formatted}`;\n }\n\n return formatted;\n }\n\n /**\n * Error level logging (always shown unless level is below ERROR)\n */\n error(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.ERROR)) {\n const formatted = this.formatMessage(LogLevel.ERROR, message, ...args);\n console.error(chalk.red('\u274C ' + formatted));\n }\n }\n\n /**\n * Warning level logging\n */\n warn(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.WARN)) {\n const formatted = this.formatMessage(LogLevel.WARN, message, ...args);\n console.warn(chalk.yellow('\u26A0\uFE0F ' + formatted));\n }\n }\n\n /**\n * Info level logging (user-facing information)\n */\n info(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.INFO)) {\n const formatted = this.formatMessage(LogLevel.INFO, message, ...args);\n console.log(chalk.cyan('\u2139\uFE0F ' + formatted));\n }\n }\n\n /**\n * Success logging (special case of info)\n */\n success(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.INFO)) {\n const formatted = this.formatMessage(LogLevel.INFO, message, ...args);\n console.log(chalk.green('\u2705 ' + formatted));\n }\n }\n\n /**\n * Debug level logging (internal operations, verbose)\n */\n debug(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.DEBUG)) {\n const formatted = this.formatMessage(LogLevel.DEBUG, message, ...args);\n console.log(chalk.gray('\uD83D\uDD0D ' + formatted));\n }\n }\n\n /**\n * Trace level logging (very detailed, internal state)\n */\n trace(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.TRACE)) {\n const formatted = this.formatMessage(LogLevel.TRACE, message, ...args);\n console.log(chalk.dim('\uD83D\uDD2C ' + formatted));\n }\n }\n\n /**\n * Raw output without formatting (for CLI output that shouldn't be logged)\n */\n raw(message: string, ...args: unknown[]): void {\n if (args.length > 0) {\n console.log(message, ...args);\n } else {\n console.log(message);\n }\n }\n\n /**\n * Print a blank line without any icon or prefix.\n */\n blank(): void {\n console.log('');\n }\n\n /**\n * Print a section header: blank line + bold text, no icon.\n * Use instead of log.info('\\nSection Title:') for section headings.\n */\n header(text: string): void {\n console.log('');\n console.log(chalk.bold(text));\n }\n\n /**\n * Print a horizontal separator line without any icon or prefix.\n */\n separator(length = 40): void {\n console.log('\u2500'.repeat(length));\n }\n\n /**\n * Create a child logger with additional prefix\n */\n child(prefix: string): Logger {\n const childPrefix = this.config.prefix\n ? `${this.config.prefix}:${prefix}`\n : prefix;\n return new Logger({\n ...this.config,\n prefix: childPrefix,\n });\n }\n}\n\n// Global logger instance\nlet globalLogger: Logger | null = null;\n\n/**\n * Get or create the global logger instance\n */\nexport function getLogger(): Logger {\n if (!globalLogger) {\n // Default to INFO level, can be configured via environment or config\n const logLevel = getLogLevelFromEnv();\n globalLogger = new Logger({\n level: logLevel,\n timestamp: false, // Disable timestamps by default for CLI\n });\n }\n return globalLogger;\n}\n\n/**\n * Get log level from environment variable or config\n */\nfunction getLogLevelFromEnv(): LogLevel {\n let envLevel = process.env.PERISCOPE_LOG_LEVEL?.toUpperCase();\n\n // If not in env, try to get from config file.\n // Uses createRequire to synchronously load config-manager and avoid the\n // circular dependency at module load time (config-manager imports logger).\n // By the time getLogLevelFromEnv() runs, config-manager is already in the cache.\n if (!envLevel) {\n try {\n const require = createRequire(import.meta.url);\n const { ConfigManager } = require('./config-manager.js');\n const config = ConfigManager.load();\n envLevel = config.logLevel?.toUpperCase();\n } catch {\n // Ignore config loading errors during logger initialization\n }\n }\n\n switch (envLevel) {\n case 'ERROR':\n return LogLevel.ERROR;\n case 'WARN':\n case 'WARNING':\n return LogLevel.WARN;\n case 'INFO':\n return LogLevel.INFO;\n case 'DEBUG':\n return LogLevel.DEBUG;\n case 'TRACE':\n return LogLevel.TRACE;\n default:\n return LogLevel.INFO; // Default level\n }\n}\n\n// Export convenience functions that use the global logger\nexport const log = {\n error: (message: string, ...args: unknown[]) =>\n getLogger().error(message, ...args),\n warn: (message: string, ...args: unknown[]) =>\n getLogger().warn(message, ...args),\n info: (message: string, ...args: unknown[]) =>\n getLogger().info(message, ...args),\n success: (message: string, ...args: unknown[]) =>\n getLogger().success(message, ...args),\n debug: (message: string, ...args: unknown[]) =>\n getLogger().debug(message, ...args),\n trace: (message: string, ...args: unknown[]) =>\n getLogger().trace(message, ...args),\n raw: (message: string, ...args: unknown[]) =>\n getLogger().raw(message, ...args),\n blank: () => getLogger().blank(),\n header: (text: string) => getLogger().header(text),\n separator: (length?: number) => getLogger().separator(length),\n};\n", "import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\n\nconst CACHE_SUBDIR = '.periscope';\n\n/**\n * Get the cache directory path, computed at runtime to respect HOME env var overrides.\n */\nexport function getCacheDir(): string {\n return path.join(os.homedir(), CACHE_SUBDIR);\n}\n\n/**\n * Ensure the cache directory exists, creating it if necessary.\n */\nexport function ensureCacheDir(): void {\n const cacheDir = getCacheDir();\n if (!fs.existsSync(cacheDir)) {\n fs.mkdirSync(cacheDir, { recursive: true });\n }\n}\n\n/**\n * Write data to a file with owner-only permissions (0o600).\n * Creates the cache directory if it doesn't exist.\n */\nexport function writeSecureFile(filePath: string, data: string): void {\n ensureCacheDir();\n fs.writeFileSync(filePath, data, { mode: 0o600 });\n}\n", "import * as fs from 'fs';\nimport * as path from 'path';\nimport { log } from './logger.js';\nimport { getCacheDir, writeSecureFile } from './cache-utils.js';\n\nconst MSAL_CACHE_FILE = 'msal-cache.json';\n\n/**\n * Get the MSAL cache file path, computed at runtime to respect HOME env var overrides in tests.\n */\nexport function getMsalCacheFilePath(): string {\n return path.join(getCacheDir(), MSAL_CACHE_FILE);\n}\n\n/**\n * Create an MSAL ICachePlugin that persists the full MSAL token cache\n * (including refresh tokens and account info) to ~/.periscope/msal-cache.json.\n *\n * Uses structural typing to match ICachePlugin from @azure/msal-common\n * without importing it directly.\n */\nexport function createMsalCachePlugin() {\n return {\n async beforeCacheAccess(cacheContext: {\n tokenCache: { deserialize: (cache: string) => void };\n }): Promise<void> {\n const cachePath = getMsalCacheFilePath();\n try {\n if (fs.existsSync(cachePath)) {\n const data = fs.readFileSync(cachePath, 'utf-8');\n cacheContext.tokenCache.deserialize(data);\n }\n } catch (error) {\n log.debug('Failed to read MSAL cache from disk:', error);\n }\n },\n\n async afterCacheAccess(cacheContext: {\n cacheHasChanged: boolean;\n tokenCache: { serialize: () => string };\n }): Promise<void> {\n if (cacheContext.cacheHasChanged) {\n try {\n const cachePath = getMsalCacheFilePath();\n const data = cacheContext.tokenCache.serialize();\n writeSecureFile(cachePath, data);\n } catch (error) {\n log.error('Failed to write MSAL cache to disk:', error);\n }\n }\n },\n };\n}\n\n/**\n * Delete the MSAL cache file from disk. Called during logout.\n */\nexport function clearMsalCache(): void {\n try {\n const cachePath = getMsalCacheFilePath();\n if (fs.existsSync(cachePath)) {\n fs.unlinkSync(cachePath);\n }\n } catch (error) {\n log.error('Failed to clear MSAL cache:', error);\n }\n}\n", "/**\n * Shared local HTTP callback server for OAuth redirect flows.\n * Used by both MsalAuthManager and Auth0AuthManager.\n */\n\nimport * as http from 'http';\nimport open from 'open';\nimport { log } from './logger.js';\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\nexport interface AuthCallbackResult {\n /** The authorization code from the callback */\n code: string;\n /** The full raw URL path from the callback (preserves state for CSRF validation) */\n fullPath: string;\n}\n\n/**\n * Start a local HTTP server to listen for an OAuth authorization code callback.\n *\n * - On error callback: shows sanitized error page, resolves null.\n * - On code callback with matching state: shows success page, resolves with code + full path.\n * - On code callback with stale state: shows retry page, redirects to authUrl, keeps waiting.\n * - On any other request: 302 redirects to authUrl (no script injection).\n * - Times out after 5 minutes.\n *\n * @param port - Port to listen on (must be pre-registered as allowed callback URL)\n * @param authUrl - Authorization URL to redirect the browser to\n * @param expectedState - Expected OAuth state parameter to reject stale callbacks\n */\nexport function listenForAuthCode(\n port: number,\n authUrl: string,\n expectedState?: string\n): Promise<AuthCallbackResult | null> {\n return new Promise((resolve, reject) => {\n let timeoutHandle: ReturnType<typeof setTimeout>;\n\n const done = (result: AuthCallbackResult | null) => {\n clearTimeout(timeoutHandle);\n server.close();\n resolve(result);\n };\n\n const server = http.createServer((req, res) => {\n const url = new URL(req.url || '', `http://localhost:${port}`);\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n\n if (error) {\n const safeError = escapeHtml(error);\n const safeDesc = escapeHtml(\n url.searchParams.get('error_description') || ''\n );\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px;\">\n <h1>Authentication Failed</h1>\n <p>Error: ${safeError}</p>\n <p>${safeDesc}</p>\n <p>You can close this window.</p>\n </body>\n </html>\n `);\n done(null);\n return;\n }\n\n if (code) {\n // Reject stale callbacks from previous auth attempts\n const callbackState = url.searchParams.get('state');\n if (expectedState && callbackState !== expectedState) {\n log.debug(\n `Ignoring stale callback (state mismatch: expected ${expectedState.slice(0, 8)}\u2026, got ${callbackState?.slice(0, 8)}\u2026)`\n );\n // Show a page that redirects to the current auth URL\n const safeLocation = authUrl.replace(/[\\r\\n]/g, '');\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <title>Authentication - Retry</title>\n </head>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px;\">\n <h1>Session Expired</h1>\n <p>This callback was from a previous login attempt. Redirecting...</p>\n <script>window.location.href = ${JSON.stringify(safeLocation)};</script>\n </body>\n </html>\n `);\n return; // Keep server running, wait for the correct callback\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <title>Authentication Successful</title>\n </head>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px;\">\n <h1>✓ Authentication Successful</h1>\n <p>You can close this window and return to the terminal.</p>\n <script>window.setTimeout(() => window.close(), 2000);</script>\n </body>\n </html>\n `);\n done({ code, fullPath: url.pathname + url.search });\n return;\n }\n\n // Not a callback \u2014 use HTTP 302 redirect (avoids injecting URL into script).\n // Strip CRLF to prevent HTTP response splitting if authUrl is ever untrusted.\n const safeLocation = authUrl.replace(/[\\r\\n]/g, '');\n res.writeHead(302, { Location: safeLocation });\n res.end();\n });\n\n // Reject on EADDRINUSE so callers get an actionable error rather than a 5-minute hang.\n server.on('error', (err: NodeJS.ErrnoException) => {\n clearTimeout(timeoutHandle);\n server.close();\n if (err.code === 'EADDRINUSE') {\n reject(\n new Error(\n `Port ${port} is already in use. Another authentication flow may be in progress.`\n )\n );\n } else {\n log.warn(`Auth callback server error: ${err.message}`);\n resolve(null);\n }\n });\n\n server.listen(port, '127.0.0.1', () => {\n log.blank();\n log.info('Opening browser for authentication...');\n log.info(\"If the browser doesn't open automatically, visit:\");\n log.info(` http://localhost:${port}`);\n log.blank();\n\n open(authUrl).catch(() => {\n log.warn(\n 'Failed to open browser automatically. Please open the URL manually.'\n );\n });\n });\n\n // Timeout after 5 minutes\n timeoutHandle = setTimeout(() => done(null), 5 * 60 * 1000);\n });\n}\n", "/**\n * Shared authentication types used by all auth provider implementations.\n */\n\n/** Buffer (60s) to avoid using tokens that are about to expire mid-request */\nconst TOKEN_EXPIRY_BUFFER_MS = 60_000;\n\n/** Returns true if the token expiry time is within the safety buffer. */\nexport function isTokenExpired(expiresAt: Date): boolean {\n return expiresAt <= new Date(Date.now() + TOKEN_EXPIRY_BUFFER_MS);\n}\n\nexport interface AuthToken {\n accessToken: string;\n expiresOn: Date;\n refreshToken?: string;\n}\n\nexport type PromptValue = 'select_account' | 'login' | 'consent' | 'none';\n\n/**\n * Common interface for authentication managers.\n * Implemented by MsalAuthManager (Entra) and Auth0AuthManager (Auth0).\n */\nexport interface IAuthManager {\n authenticate(): Promise<AuthToken>;\n authenticateInteractive(prompt?: PromptValue): Promise<AuthToken>;\n tryGetValidTokenSilently(): Promise<string | null>;\n getValidToken(): Promise<string>;\n isAuthenticated(): boolean;\n logout(): void;\n}\n", "import * as client from 'openid-client';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport open from 'open';\nimport { log } from './logger.js';\nimport { getCacheDir, writeSecureFile } from './cache-utils.js';\nimport { listenForAuthCode } from './auth-callback-server.js';\nimport {\n isTokenExpired,\n type AuthToken,\n type IAuthManager,\n type PromptValue,\n} from './auth-types.js';\n\nexport interface Auth0Config {\n /** Auth0 authority URL, e.g. https://periscope.auth0.com/ */\n authority: string;\n /** Auth0 application client ID */\n clientId: string;\n /** OAuth2 scopes to request */\n scopes: string[];\n /** Auth0 API audience identifier (required for JWT access tokens) */\n audience?: string;\n}\n\ninterface Auth0CachedTokens {\n accessToken: string;\n refreshToken?: string;\n idToken?: string;\n expiresAt: string; // ISO date string\n}\n\n/**\n * Auth0 authentication manager using openid-client (RFC-compliant OIDC).\n * Supports authorization code + PKCE browser flow (interactive) and\n * device code flow (headless/fallback), plus token refresh.\n */\nexport class Auth0AuthManager implements IAuthManager {\n private static readonly TOKEN_CACHE_FILE = 'auth0-token-cache.json';\n private static readonly REDIRECT_PORT = 19836;\n\n private oidcConfig: client.Configuration | null = null;\n private config: Auth0Config | null = null;\n\n constructor(config?: Auth0Config) {\n if (config) {\n this.config = config;\n }\n }\n\n /**\n * Discover OIDC endpoints and initialize the openid-client Configuration.\n * Lazy-loaded on first use.\n */\n private async ensureOidcConfig(): Promise<client.Configuration> {\n if (this.oidcConfig) return this.oidcConfig;\n if (!this.config) {\n throw new Error('Auth0 configuration is required');\n }\n\n const serverUrl = new URL(this.config.authority);\n this.oidcConfig = await client.discovery(\n serverUrl,\n this.config.clientId,\n undefined,\n client.None()\n );\n return this.oidcConfig;\n }\n\n /**\n * Authenticate using the device code flow (RFC 8628).\n * Displays a code + URL, opens the browser, and polls for completion.\n */\n async authenticate(): Promise<AuthToken> {\n const oidcConfig = await this.ensureOidcConfig();\n\n const parameters: Record<string, string> = {\n scope: this.getScopes(),\n };\n if (this.config?.audience) {\n parameters.audience = this.config.audience;\n }\n\n const deviceResponse = await client.initiateDeviceAuthorization(\n oidcConfig,\n parameters\n );\n\n const verificationUrl =\n deviceResponse.verification_uri_complete ||\n deviceResponse.verification_uri;\n\n log.blank();\n log.info(\n 'To authenticate, open the page below and confirm the code matches:'\n );\n log.info(` ${verificationUrl}`);\n log.blank();\n log.info(`Your code: ${deviceResponse.user_code}`);\n log.blank();\n log.info('Opening browser automatically...');\n log.blank();\n\n open(verificationUrl).catch(() => {\n log.warn(\n 'Failed to open browser automatically. Please open the URL manually.'\n );\n });\n\n const tokenResponse = await client.pollDeviceAuthorizationGrant(\n oidcConfig,\n deviceResponse\n );\n\n return this.processTokenResponse(tokenResponse);\n }\n\n /**\n * Authenticate using the interactive browser flow (Authorization Code + PKCE).\n * Opens the browser directly to Auth0 Universal Login \u2014 user enters email,\n * receives OTP, enters it, and is redirected back. Single code, no device confirmation.\n */\n async authenticateInteractive(_prompt?: PromptValue): Promise<AuthToken> {\n const oidcConfig = await this.ensureOidcConfig();\n\n // Generate PKCE code verifier + challenge\n const codeVerifier = client.randomPKCECodeVerifier();\n const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);\n const state = client.randomState();\n\n // Use a fixed port so the callback URL can be registered in Auth0\n const port = Auth0AuthManager.REDIRECT_PORT;\n const redirectUri = `http://localhost:${port}/callback`;\n\n // Build authorization URL\n const parameters: Record<string, string> = {\n redirect_uri: redirectUri,\n scope: this.getScopes(),\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state,\n };\n if (this.config?.audience) {\n parameters.audience = this.config.audience;\n }\n\n const authUrl = client.buildAuthorizationUrl(oidcConfig, parameters);\n\n // Start local server and wait for the authorization code callback.\n log.debug('Waiting for Auth0 callback on port', port);\n const result = await listenForAuthCode(port, authUrl.href, state);\n\n if (!result) {\n throw new Error('Failed to receive authorization code from Auth0');\n }\n\n log.debug('Received authorization code, exchanging for tokens...');\n log.debug('Callback fullPath:', result.fullPath);\n\n // Use the full callback path from the server (preserves state for CSRF validation)\n const callbackUrl = new URL(result.fullPath, `http://localhost:${port}`);\n\n try {\n const tokenResponse = await client.authorizationCodeGrant(\n oidcConfig,\n callbackUrl,\n {\n pkceCodeVerifier: codeVerifier,\n expectedState: state,\n idTokenExpected: true,\n }\n );\n\n log.debug('Token exchange successful');\n return this.processTokenResponse(tokenResponse);\n } catch (error) {\n log.debug('Token exchange failed:', error);\n if (error instanceof Error && error.cause) {\n log.debug('Token exchange error cause:', error.cause);\n }\n throw error;\n }\n }\n\n /**\n * Try to get a valid token without user interaction.\n * Checks cache first, then tries refresh token.\n */\n async tryGetValidTokenSilently(): Promise<string | null> {\n const cached = this.getCachedTokens();\n if (!cached) return null;\n\n // Check if access token is still valid\n const expiresAt = new Date(cached.expiresAt);\n if (!isTokenExpired(expiresAt)) {\n const ttlMin = Math.round((expiresAt.getTime() - Date.now()) / 60_000);\n log.debug(`Using cached Auth0 token (expires in ${ttlMin}m)`);\n return cached.accessToken;\n }\n\n // Try refresh\n if (cached.refreshToken) {\n try {\n log.debug('Attempting Auth0 token refresh...');\n const token = await this.refreshAccessToken(cached.refreshToken);\n const ttlMin = Math.round(\n (token.expiresOn.getTime() - Date.now()) / 60_000\n );\n log.debug(`Auth0 token refreshed successfully (expires in ${ttlMin}m)`);\n return token.accessToken;\n } catch (error) {\n log.debug('Auth0 token refresh failed:', error);\n }\n } else {\n log.debug(\n 'No refresh token available \u2014 cannot silently refresh. Re-authentication required.'\n );\n }\n\n return null;\n }\n\n /**\n * Get a valid access token, refreshing or re-authenticating if necessary.\n *\n * Falls back to the device code flow (not browser PKCE) so this method can be\n * called in headless environments (e.g., mid-tunnel token refresh). Callers that\n * need browser-based re-authentication should call authenticateInteractive() directly.\n */\n async getValidToken(): Promise<string> {\n const token = await this.tryGetValidTokenSilently();\n if (token) return token;\n\n // Fallback: device code flow (headless-safe, works without a browser redirect port)\n const newToken = await this.authenticate();\n return newToken.accessToken;\n }\n\n /**\n * Check if a valid cached token exists.\n */\n isAuthenticated(): boolean {\n const cached = this.getCachedTokens();\n if (!cached) return false;\n return !isTokenExpired(new Date(cached.expiresAt));\n }\n\n /**\n * Clear all cached Auth0 token data.\n */\n logout(): void {\n try {\n const cachePath = this.getTokenCachePath();\n if (fs.existsSync(cachePath)) {\n fs.unlinkSync(cachePath);\n }\n } catch (error) {\n log.error('Error clearing Auth0 token cache during logout:', error);\n }\n }\n\n /**\n * Use a refresh token to obtain a new access token.\n */\n private async refreshAccessToken(refreshToken: string): Promise<AuthToken> {\n const oidcConfig = await this.ensureOidcConfig();\n const tokenResponse = await client.refreshTokenGrant(\n oidcConfig,\n refreshToken\n );\n return this.processTokenResponse(tokenResponse);\n }\n\n /**\n * Process a token endpoint response into our AuthToken format and cache it.\n */\n private processTokenResponse(\n response: client.TokenEndpointResponse & client.TokenEndpointResponseHelpers\n ): AuthToken {\n const expiresIn = response.expiresIn();\n const expiresOn = expiresIn\n ? new Date(Date.now() + expiresIn * 1000)\n : new Date(Date.now() + 3600000); // Default 1 hour\n\n if (!response.refresh_token) {\n log.debug(\n 'Auth0 token response did not include a refresh token. Token refresh will not be available when the access token expires.'\n );\n }\n\n const authToken: AuthToken = {\n accessToken: response.access_token,\n expiresOn,\n refreshToken: response.refresh_token,\n };\n\n // Cache the tokens\n this.cacheTokens({\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n idToken: response.id_token,\n expiresAt: expiresOn.toISOString(),\n });\n\n return authToken;\n }\n\n private getScopes(): string {\n if (!this.config?.scopes?.length) {\n return 'openid profile email offline_access';\n }\n // Ensure offline_access is included for refresh tokens\n const scopes = new Set(this.config.scopes);\n scopes.add('offline_access');\n return [...scopes].join(' ');\n }\n\n private getCachedTokens(): Auth0CachedTokens | null {\n try {\n const cachePath = this.getTokenCachePath();\n if (fs.existsSync(cachePath)) {\n const content = fs.readFileSync(cachePath, 'utf-8');\n const parsed = JSON.parse(content) as Partial<Auth0CachedTokens>;\n // Guard against a corrupt or schema-mismatched cache file that would\n // produce a \"Bearer undefined\" Authorization header downstream.\n if (!parsed.accessToken || !parsed.expiresAt) return null;\n return parsed as Auth0CachedTokens;\n }\n } catch (error) {\n log.debug('Failed to load Auth0 cached tokens:', error);\n }\n return null;\n }\n\n private cacheTokens(tokens: Auth0CachedTokens): void {\n try {\n const cachePath = this.getTokenCachePath();\n writeSecureFile(cachePath, JSON.stringify(tokens, null, 2));\n } catch (error) {\n log.error('Failed to cache Auth0 tokens:', error);\n }\n }\n\n private getTokenCachePath(): string {\n return path.join(getCacheDir(), Auth0AuthManager.TOKEN_CACHE_FILE);\n }\n}\n", "//----------------------\n// <auto-generated>\n// Generated using the NSwag toolchain v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)\n// </auto-generated>\n//----------------------\n/* tslint:disable */\n/* eslint-disable */\n// ReSharper disable InconsistentNaming\nexport var PeriscopeApi;\n(function (PeriscopeApi) {\n class PeriscopeApiClient {\n constructor(baseUrl, http) {\n this.jsonParseReviver = undefined;\n this.http = http ? http : window;\n this.baseUrl = baseUrl ?? \"\";\n }\n /**\n * @param clientVersion (optional)\n * @return OK\n */\n configuration(clientVersion) {\n let url_ = this.baseUrl + \"/api/configuration?\";\n if (clientVersion === null)\n throw new Error(\"The parameter 'clientVersion' cannot be null.\");\n else if (clientVersion !== undefined)\n url_ += \"clientVersion=\" + encodeURIComponent(\"\" + clientVersion) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processConfiguration(_response);\n });\n }\n processConfiguration(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 503) {\n return response.text().then((_responseText) => {\n return throwException(\"Service Unavailable\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n telemetryGET() {\n let url_ = this.baseUrl + \"/api/configuration/telemetry\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processTelemetryGET(_response);\n });\n }\n processTelemetryGET(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 401) {\n return response.text().then((_responseText) => {\n let result401 = null;\n result401 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Unauthorized\", status, _responseText, _headers, result401);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n submitFeedback(body) {\n let url_ = this.baseUrl + \"/api/feedback\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processSubmitFeedback(_response);\n });\n }\n processSubmitFeedback(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n telemetryGET2() {\n let url_ = this.baseUrl + \"/api/test/telemetry\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {}\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processTelemetryGET2(_response);\n });\n }\n processTelemetryGET2(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n telemetryDELETE() {\n let url_ = this.baseUrl + \"/api/test/telemetry\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"DELETE\",\n headers: {}\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processTelemetryDELETE(_response);\n });\n }\n processTelemetryDELETE(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n licenseConfigPOST(body) {\n let url_ = this.baseUrl + \"/api/test/license-config\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processLicenseConfigPOST(_response);\n });\n }\n processLicenseConfigPOST(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n licenseConfigDELETE() {\n let url_ = this.baseUrl + \"/api/test/license-config\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"DELETE\",\n headers: {}\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processLicenseConfigDELETE(_response);\n });\n }\n processLicenseConfigDELETE(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n getCurrentUser() {\n let url_ = this.baseUrl + \"/api/user\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processGetCurrentUser(_response);\n });\n }\n processGetCurrentUser(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 404) {\n return response.text().then((_responseText) => {\n let result404 = null;\n result404 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Not Found\", status, _responseText, _headers, result404);\n });\n }\n else if (status === 500) {\n return response.text().then((_responseText) => {\n return throwException(\"Internal Server Error\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n getSshCredentials() {\n let url_ = this.baseUrl + \"/api/user/ssh-credentials\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processGetSshCredentials(_response);\n });\n }\n processGetSshCredentials(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status === 500) {\n return response.text().then((_responseText) => {\n return throwException(\"Internal Server Error\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n validateSshHostKey(body) {\n let url_ = this.baseUrl + \"/api/user/ssh/validate-host-key\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processValidateSshHostKey(_response);\n });\n }\n processValidateSshHostKey(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422 = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n registerPublicKey(body) {\n let url_ = this.baseUrl + \"/api/user/public-key\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processRegisterPublicKey(_response);\n });\n }\n processRegisterPublicKey(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status === 500) {\n return response.text().then((_responseText) => {\n return throwException(\"Internal Server Error\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n getTermsStatus() {\n let url_ = this.baseUrl + \"/api/user/terms-status\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processGetTermsStatus(_response);\n });\n }\n processGetTermsStatus(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n acceptTerms(body) {\n let url_ = this.baseUrl + \"/api/user/accept-terms\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processAcceptTerms(_response);\n });\n }\n processAcceptTerms(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n updateUserSlug(body) {\n let url_ = this.baseUrl + \"/api/user/slug\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processUpdateUserSlug(_response);\n });\n }\n processUpdateUserSlug(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409 = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n }\n else if (status === 500) {\n return response.text().then((_responseText) => {\n return throwException(\"Internal Server Error\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n }\n PeriscopeApi.PeriscopeApiClient = PeriscopeApiClient;\n class ApiException extends Error {\n constructor(message, status, response, headers, result) {\n super();\n this.isApiException = true;\n this.message = message;\n this.status = status;\n this.response = response;\n this.headers = headers;\n this.result = result;\n }\n static isApiException(obj) {\n return obj.isApiException === true;\n }\n }\n PeriscopeApi.ApiException = ApiException;\n function throwException(message, status, response, headers, result) {\n throw new ApiException(message, status, response, headers, result);\n }\n})(PeriscopeApi || (PeriscopeApi = {}));\n", "/**\n * Centralized cache for the server's /api/configuration response.\n *\n * Gate 1 of CLI initialization: fetched once (unauthenticated) and\n * consumed by MSAL auth, telemetry init, and display commands.\n * Eliminates duplicate GET /api/configuration requests.\n */\n\nimport { PeriscopeApi } from '@elf-5/periscope-api-client';\n\nlet cachedConfig: PeriscopeApi.PeriscopeConfigDto | null = null;\nlet fetchPromise: Promise<PeriscopeApi.PeriscopeConfigDto> | null = null;\n\n/**\n * Get the server configuration, fetching it at most once per CLI invocation.\n * Concurrent calls share the same in-flight request.\n *\n * @param serverUrl - The Periscope server base URL\n * @returns The cached server configuration DTO\n */\nexport async function getServerConfig(\n serverUrl: string\n): Promise<PeriscopeApi.PeriscopeConfigDto> {\n if (cachedConfig) return cachedConfig;\n\n // Deduplicate concurrent calls\n if (fetchPromise) return fetchPromise;\n\n fetchPromise = fetchServerConfig(serverUrl);\n try {\n cachedConfig = await fetchPromise;\n return cachedConfig;\n } finally {\n fetchPromise = null;\n }\n}\n\n/**\n * Get the cached config without fetching. Returns null if not yet fetched.\n */\nexport function getCachedServerConfig(): PeriscopeApi.PeriscopeConfigDto | null {\n return cachedConfig;\n}\n\n/**\n * Clear the cached config. Useful for testing.\n */\nexport function clearServerConfigCache(): void {\n cachedConfig = null;\n fetchPromise = null;\n}\n\nasync function fetchServerConfig(\n serverUrl: string\n): Promise<PeriscopeApi.PeriscopeConfigDto> {\n const client = new PeriscopeApi.PeriscopeApiClient(serverUrl, {\n fetch: (url: string | URL, init?: RequestInit) => fetch(url, init),\n });\n return client.configuration(undefined); // clientVersion is optional\n}\n", "import { PeriscopeConfig } from './config-manager.js';\nimport type { AuthToken, IAuthManager, PromptValue } from './auth-types.js';\nimport { MsalAuthManager } from './msal-auth-manager.js';\nimport { Auth0AuthManager } from './auth0-auth-manager.js';\nimport { PeriscopeApi } from '@elf-5/periscope-api-client';\nimport { getLogger } from './logger.js';\nimport { getServerConfig } from './server-config.js';\nimport * as https from 'https';\nimport * as fs from 'fs';\n\n// Re-export the official types from the API client for convenience\nexport type UserInfo = PeriscopeApi.UserDto;\n\n/**\n * Account status values returned by the server.\n * Mirrors the UserStatus enum in Periscope.Shared.\n */\nexport const AccountStatus = {\n PENDING_APPROVAL: 'PendingApproval',\n ACTIVE: 'Active',\n REJECTED: 'Rejected',\n INACTIVE: 'Inactive',\n} as const;\n\n// Define only the interfaces we need that aren't in the API client\nexport interface HealthStatus {\n healthy: boolean;\n version?: string;\n uptime?: number;\n}\n\nexport class PeriscopeClient {\n private authManager: IAuthManager | null = null;\n private apiClient: PeriscopeApi.PeriscopeApiClient | null = null;\n private logger = getLogger().child('PeriscopeClient');\n private httpsAgent: https.Agent | null = null;\n private authInitPromise: Promise<void> | null = null;\n\n constructor(private config: PeriscopeConfig) {\n if (!config.serverUrl) {\n throw new Error('Server URL is required');\n }\n\n // Create HTTPS agent with custom CA certificate if provided\n if (config.caCertPath) {\n try {\n const caCert = fs.readFileSync(config.caCertPath);\n this.httpsAgent = new https.Agent({\n ca: caCert,\n });\n this.logger.debug(\n `Using custom CA certificate from: ${config.caCertPath}`\n );\n } catch (error) {\n this.logger.error(\n `Failed to load CA certificate from ${config.caCertPath}:`,\n error\n );\n throw new Error(\n `Failed to load CA certificate: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n }\n\n /**\n * Perform fetch with SSL configuration\n */\n private async fetchWithOptions(\n url: string | URL,\n init?: RequestInit\n ): Promise<Response> {\n const options: RequestInit & { agent?: https.Agent } = { ...init };\n\n // Add HTTPS agent with custom CA if configured\n if (this.httpsAgent && url.toString().startsWith('https://')) {\n options.agent = this.httpsAgent;\n }\n\n return fetch(url, options);\n }\n\n /**\n * Initialize the auth manager with configuration from the API.\n * Selects MSAL (Entra) or Auth0 based on the server's authProvider field.\n * Uses a cached promise to prevent duplicate initialization attempts.\n */\n private async initializeAuth(): Promise<void> {\n // Already initialized\n if (this.authManager) {\n return;\n }\n\n // Reuse in-flight initialization to prevent duplicate requests\n if (this.authInitPromise) {\n return this.authInitPromise;\n }\n\n this.authInitPromise = this.doInitializeAuth();\n try {\n await this.authInitPromise;\n } finally {\n // Clear the promise so future calls can retry if this one failed\n this.authInitPromise = null;\n }\n }\n\n /**\n * Internal auth initialization logic.\n * Uses the centralized ServerConfig cache to avoid duplicate /api/configuration requests.\n */\n private async doInitializeAuth(): Promise<void> {\n const serverConfig = await getServerConfig(this.config.serverUrl!);\n\n if (!serverConfig.clientId || !serverConfig.authority) {\n throw new Error('Incomplete auth configuration received from server');\n }\n\n const isAuth0 = serverConfig.authProvider?.toLowerCase() === 'auth0';\n\n if (isAuth0) {\n this.authManager = new Auth0AuthManager({\n authority: serverConfig.authority,\n clientId: serverConfig.clientId,\n scopes: serverConfig.scopes || ['openid', 'profile', 'email'],\n audience: serverConfig.audience,\n });\n this.logger.debug('Auth0 auth manager initialized from server config');\n } else {\n if (!serverConfig.scopes) {\n throw new Error('Incomplete MSAL configuration received from server');\n }\n this.authManager = new MsalAuthManager({\n clientId: serverConfig.clientId,\n authority: serverConfig.authority,\n scopes: serverConfig.scopes,\n });\n this.logger.debug('MSAL auth manager initialized from server config');\n }\n }\n\n /**\n * Create a custom fetch implementation that includes authentication headers.\n * Dynamically fetches a fresh token on each request via authManager so that\n * long-lived API clients (e.g., during tunnel reconnection) automatically\n * pick up refreshed tokens instead of replaying an expired one.\n */\n private createAuthenticatedFetch(token: string) {\n return {\n fetch: async (\n url: string | URL,\n init?: RequestInit\n ): Promise<Response> => {\n const headers = new Headers(init?.headers);\n\n // Get a fresh token if authManager is available, otherwise use the initial token.\n // tryGetValidTokenSilently() returns the cached token if still valid, or silently\n // refreshes it. If refresh fails (e.g., expired refresh token), throw so the caller\n // gets a clear auth error rather than triggering interactive auth in a background\n // context (e.g., tunnel reconnect).\n let currentToken: string;\n if (this.authManager) {\n const silentToken = await this.authManager.tryGetValidTokenSilently();\n if (!silentToken) {\n throw new Error(\n 'Authentication expired. Please run `periscope auth login` to re-authenticate.'\n );\n }\n currentToken = silentToken;\n } else {\n currentToken = token;\n }\n\n headers.set('Authorization', `Bearer ${currentToken}`);\n headers.set('Content-Type', 'application/json');\n headers.set('Accept', 'application/json');\n\n const requestInit: RequestInit = {\n ...init,\n headers,\n };\n\n this.logger.debug(`Making authenticated request to: ${url}`);\n this.logger.trace('Using Bearer token: [present]');\n\n const response = await this.fetchWithOptions(url, requestInit);\n\n this.logger.debug(`Response status: ${response.status}`);\n\n return response;\n },\n };\n }\n\n /**\n * Create an unauthenticated fetch implementation for public endpoints\n */\n private createUnauthenticatedFetch() {\n return {\n fetch: async (\n url: string | URL,\n init?: RequestInit\n ): Promise<Response> => {\n const headers = new Headers(init?.headers);\n\n // Set standard headers without authentication\n headers.set('Content-Type', 'application/json');\n headers.set('Accept', 'application/json');\n\n const requestInit: RequestInit = {\n ...init,\n headers,\n };\n\n this.logger.debug(`Making unauthenticated request to: ${url}`);\n\n const response = await this.fetchWithOptions(url, requestInit);\n\n this.logger.debug(`Response status: ${response.status}`);\n\n return response;\n },\n };\n }\n\n /**\n * Initialize the API client with authentication token\n */\n private async initializeApiClient(token: string): Promise<boolean> {\n try {\n if (!this.config.serverUrl) {\n throw new Error('Server URL is required');\n }\n\n // Create authenticated fetch implementation\n const authenticatedFetch = this.createAuthenticatedFetch(token);\n\n // Create the API client with base URL and authenticated fetch\n this.apiClient = new PeriscopeApi.PeriscopeApiClient(\n this.config.serverUrl,\n authenticatedFetch\n );\n\n this.logger.debug('Periscope API client initialized successfully');\n this.logger.debug('API base URL:', this.config.serverUrl);\n this.logger.debug('Auth token:', token ? 'Present' : 'Missing');\n\n return true;\n } catch (error) {\n this.logger.error('Failed to initialize Periscope API client:', error);\n return false;\n }\n }\n\n /**\n * Check if API client is initialized and ready\n */\n private async isApiClientReady(): Promise<boolean> {\n if (this.apiClient) {\n return true;\n }\n\n // Try to initialize from existing auth\n await this.ensureApiClientInitialized();\n return this.apiClient !== null;\n }\n\n /**\n * Authenticate and ensure we have a valid token\n */\n async authenticate(): Promise<AuthToken> {\n // Initialize auth manager if not already done\n await this.initializeAuth();\n\n if (!this.authManager) {\n throw new Error('Failed to initialize auth configuration');\n }\n\n const authResult = await this.authManager.authenticate();\n\n // Initialize API client with the token\n await this.initializeApiClient(authResult.accessToken);\n\n return authResult;\n }\n\n /**\n * Authenticate using interactive flow with prompt control\n */\n async authenticateInteractive(prompt?: PromptValue): Promise<AuthToken> {\n // Initialize auth manager if not already done\n await this.initializeAuth();\n\n if (!this.authManager) {\n throw new Error('Failed to initialize auth configuration');\n }\n\n const authResult = await this.authManager.authenticateInteractive(prompt);\n\n // Initialize API client with the token\n await this.initializeApiClient(authResult.accessToken);\n\n return authResult;\n }\n\n /**\n * Check if currently authenticated.\n *\n * Checks in order:\n * 1. Already initialized authManager with valid token\n * 2. Cached token on disk (without needing server config)\n * 3. Initialize auth from server and check\n *\n * This layered approach allows the CLI to work even when the server\n * doesn't provide auth configuration (e.g., test environments).\n */\n async isAuthenticated(): Promise<boolean> {\n // 1. Check already-initialized authManager\n if (this.authManager?.isAuthenticated()) {\n await this.ensureApiClientInitialized();\n return true;\n }\n\n // 2. Check for cached token on disk (works without server config)\n if (await this.tryInitializeFromCachedToken()) {\n // Initialize the full auth manager so code that uses this.authManager\n // (e.g., getTermsStatus, acceptTerms) works correctly.\n try {\n await this.initializeAuth();\n } catch (error) {\n // Server config unavailable \u2014 apiClient still has the cached token.\n // authManager will remain null; methods that need it handle this gracefully.\n this.logger.debug('Auth init failed after cached token login:', error);\n }\n return true;\n }\n\n // 3. Try full auth initialization from server (enables silent refresh)\n try {\n await this.initializeAuth();\n if (this.authManager) {\n // tryGetValidTokenSilently() checks simple cache and attempts\n // silent refresh using persisted refresh tokens\n const token = await this.authManager.tryGetValidTokenSilently();\n if (token) {\n await this.initializeApiClient(token);\n return true;\n }\n }\n } catch (error) {\n // Distinguish between network errors (should throw) and auth issues (return false)\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const isNetworkError =\n errorMessage.includes('ECONNREFUSED') ||\n errorMessage.includes('ENOTFOUND') ||\n errorMessage.includes('ETIMEDOUT') ||\n errorMessage.includes('ECONNRESET') ||\n errorMessage.includes('fetch failed') ||\n errorMessage.toLowerCase().includes('network');\n\n if (isNetworkError) {\n // Throw a clear error so commands can display a helpful message\n // Include the server URL so users know which server failed\n throw new Error(\n `Server is unreachable (${this.config.serverUrl}): ${errorMessage}`\n );\n }\n\n this.logger.debug('Auth initialization failed during auth check:', error);\n }\n\n return false;\n }\n\n /**\n * @deprecated Use isAuthenticated() instead. This alias exists for backwards compatibility.\n */\n async isAuthenticatedAsync(): Promise<boolean> {\n return this.isAuthenticated();\n }\n\n /**\n * Try to initialize from a cached token file without needing server config.\n * Checks both MSAL and Auth0 token caches.\n * Returns true if a valid cached token was found and API client initialized.\n *\n * NOTE: Config-less managers are used only to read the cache and extract\n * a still-valid access token. They are NOT stored as this.authManager\n * because they lack server config needed for re-authentication and\n * token refresh. The full authManager is set later via initializeAuth().\n */\n private async tryInitializeFromCachedToken(): Promise<boolean> {\n // Try MSAL cache first\n try {\n const msalAuthManager = new MsalAuthManager();\n if (msalAuthManager.isAuthenticated()) {\n const token = await msalAuthManager.getValidToken();\n await this.initializeApiClient(token);\n this.logger.debug('Initialized API client from cached MSAL token');\n return true;\n }\n } catch {\n // MSAL cache not available or invalid\n }\n\n // Try Auth0 cache\n try {\n const auth0AuthManager = new Auth0AuthManager();\n if (auth0AuthManager.isAuthenticated()) {\n const token = await auth0AuthManager.getValidToken();\n await this.initializeApiClient(token);\n this.logger.debug('Initialized API client from cached Auth0 token');\n return true;\n }\n } catch {\n // Auth0 cache not available or invalid\n }\n\n return false;\n }\n\n /**\n * Ensure API client is initialized if we have valid authentication.\n */\n private async ensureApiClientInitialized(): Promise<void> {\n if (this.apiClient || !this.authManager) {\n return;\n }\n\n try {\n const token = await this.authManager.getValidToken();\n await this.initializeApiClient(token);\n } catch (error) {\n this.logger.debug('Failed to initialize API client:', error);\n }\n }\n\n /**\n * Logout and clear authentication data.\n * Clears both MSAL and Auth0 caches to ensure a clean slate.\n */\n async logout(): Promise<void> {\n // Initialize auth if needed to ensure proper logout\n try {\n await this.initializeAuth();\n } catch (error) {\n this.logger.warn('Failed to initialize auth for logout:', error);\n }\n\n if (this.authManager) {\n this.authManager.logout();\n }\n\n // Also clear the other provider's cache in case the user switches providers\n try {\n if (!(this.authManager instanceof MsalAuthManager)) {\n new MsalAuthManager().logout();\n }\n } catch {\n // Ignore errors clearing MSAL cache\n }\n try {\n if (!(this.authManager instanceof Auth0AuthManager)) {\n new Auth0AuthManager().logout();\n }\n } catch {\n // Ignore errors clearing Auth0 cache\n }\n\n this.apiClient = null;\n }\n\n // API Methods - these use the real API client\n\n /**\n * Get auth configuration from server (unauthenticated endpoint)\n */\n async getAuthConfig(): Promise<PeriscopeApi.PeriscopeConfigDto> {\n if (!this.config.serverUrl) {\n throw new Error('Server URL is required to fetch auth configuration');\n }\n\n try {\n // Create an unauthenticated API client for this public endpoint\n const unauthenticatedFetch = this.createUnauthenticatedFetch();\n const unauthenticatedClient = new PeriscopeApi.PeriscopeApiClient(\n this.config.serverUrl,\n unauthenticatedFetch\n );\n\n this.logger.debug(\n `Getting auth configuration from ${this.config.serverUrl}`\n );\n const authConfig = await unauthenticatedClient.configuration(undefined); // clientVersion is optional\n this.logger.debug('Successfully fetched auth configuration from server');\n\n return authConfig;\n } catch (error) {\n this.logger.debug(\n 'Failed to fetch auth configuration from server:',\n error\n );\n throw new Error(\n `Failed to fetch auth configuration: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n async checkHealth(): Promise<HealthStatus> {\n if (!this.config.serverUrl) {\n throw new Error('Server URL is required');\n }\n\n const response = await this.fetchWithOptions(\n `${this.config.serverUrl}/healthz`\n );\n\n if (!response.ok) {\n return { healthy: false };\n }\n\n const body = (await response.json()) as { status?: string };\n return { healthy: body.status === 'Healthy' };\n }\n\n /**\n * Register the client's SSH public key with the server.\n * Returns the normalized public key string accepted by the server.\n */\n async registerPublicKey(publicKey: string): Promise<string> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n const response = await this.apiClient.registerPublicKey({ publicKey });\n if (response.publicKey == null) {\n throw new Error(\n 'Server did not return the registered public key. The server may be outdated.'\n );\n }\n return response.publicKey;\n }\n\n async getSSHCredentials(): Promise<PeriscopeApi.SshCredentialsDto> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n try {\n this.logger.debug('Fetching SSH credentials...');\n const sshCredentials = await this.apiClient.getSshCredentials();\n this.logger.debug(\n `SSH credentials fetched for user '${sshCredentials.email ?? 'unknown'}'`\n );\n return sshCredentials;\n } catch (error) {\n this.logger.warn('Failed to fetch SSH credentials:', error);\n throw error;\n }\n }\n\n /**\n * Get current user information\n */\n async getCurrentUser(): Promise<PeriscopeApi.UserDto> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n try {\n this.logger.debug('Fetching current user from Periscope server...');\n const user = await this.apiClient.getCurrentUser();\n this.logger.debug(\n 'Successfully fetched current user from Periscope server'\n );\n return user;\n } catch (error) {\n this.logger.warn(\n 'Failed to fetch current user from Periscope server:',\n error\n );\n throw error;\n }\n }\n\n /**\n * Get current user's account status from the server.\n * Returns the status string (e.g., \"Active\", \"PendingApproval\").\n * Throws if the status cannot be determined (fail-closed).\n */\n async getUserStatus(): Promise<string> {\n const user = await this.getCurrentUser();\n if (!user.status) {\n throw new Error('Server did not return account status');\n }\n return user.status;\n }\n\n /**\n * Get current user's Terms of Service acceptance status.\n * Uses direct fetch since these endpoints may not yet be in the generated API client.\n */\n async getTermsStatus(): Promise<TermsStatus> {\n if (!(await this.isApiClientReady())) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n if (!this.authManager) {\n throw new Error(\n 'Auth manager not initialized. Cannot check terms status.'\n );\n }\n\n const token = await this.authManager.getValidToken();\n const response = await this.fetchWithOptions(\n `${this.config.serverUrl}/api/user/terms-status`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to get terms status: ${response.status} ${response.statusText}`\n );\n }\n\n return (await response.json()) as TermsStatus;\n }\n\n /**\n * Accept the Terms of Service for the current user.\n */\n async acceptTerms(termsVersion: string): Promise<void> {\n if (!(await this.isApiClientReady())) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n if (!this.authManager) {\n throw new Error('Auth manager not initialized. Cannot accept terms.');\n }\n\n const token = await this.authManager.getValidToken();\n const response = await this.fetchWithOptions(\n `${this.config.serverUrl}/api/user/accept-terms`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ termsVersion }),\n }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to accept terms: ${response.status} ${response.statusText}`\n );\n }\n }\n\n /**\n * Update the user's slug for tunnel namespacing (ELF-166).\n * Requires authentication.\n */\n async updateSlug(request: { slug: string }): Promise<void> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n try {\n await this.apiClient.updateUserSlug(request);\n } catch (error: unknown) {\n // Re-throw with enhanced error info for 409 conflicts\n if (\n error &&\n typeof error === 'object' &&\n 'status' in error &&\n error.status === 409\n ) {\n throw Object.assign(new Error('Slug already in use'), {\n response: { status: 409 },\n });\n }\n throw error;\n }\n }\n\n /**\n * Validate the SSH host key received during the SSH handshake (ELF-198).\n * Sends the raw SSH wire-format public key bytes (base64-encoded) to the server,\n * which compares them against its own key using constant-time comparison.\n * Throws ApiException with status 422 if the key does not match (potential MITM).\n */\n async validateSshHostKey(keyBytes: string): Promise<void> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n await this.apiClient.validateSshHostKey({ keyBytes });\n }\n\n /**\n * Submit user feedback to create a Linear issue.\n * Requires authentication.\n */\n async submitFeedback(message: string): Promise<FeedbackResponse> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n try {\n this.logger.debug('Submitting feedback...');\n const response = await this.apiClient.submitFeedback({\n message,\n source: 'cli',\n });\n this.logger.debug('Feedback submitted successfully');\n return response;\n } catch (error) {\n this.logger.warn('Failed to submit feedback:', error);\n throw error;\n }\n }\n\n /**\n * Get the Application Insights connection string from the authenticated telemetry endpoint.\n * Returns null if not authenticated or if the server has no connection string configured.\n */\n async getTelemetryConnectionString(): Promise<string | null> {\n if (!(await this.isApiClientReady())) {\n return null;\n }\n try {\n const config = await this.apiClient!.telemetryGET();\n return config.applicationInsightsConnectionString ?? null;\n } catch (error) {\n this.logger.debug('Failed to fetch telemetry connection string:', error);\n return null;\n }\n }\n\n /**\n * Get the configuration object\n */\n getConfig(): PeriscopeConfig {\n return this.config;\n }\n}\n\n/**\n * Terms of Service status response from server\n */\nexport interface TermsStatus {\n accepted: boolean;\n termsVersion: string | null;\n acceptedAt: string | null;\n currentVersion: string;\n}\n\n/**\n * Feedback response from server\n */\nexport interface FeedbackResponse {\n success?: boolean;\n message?: string | undefined;\n}\n", "/**\n * Secure memory utilities for cleanup and process-lifecycle management.\n */\n\nimport { getLogger } from './logger.js';\nimport { isReadlineActive } from './readline-instance.js';\n\n// Define interface for tunnel manager cleanup\ninterface TunnelManagerLike {\n stopAll(): Promise<void>;\n}\n\n// Global tunnel manager registry for cleanup\nconst activeTunnelManagers = new Set<TunnelManagerLike>();\n\nexport function registerTunnelManager(tunnelManager: TunnelManagerLike): void {\n activeTunnelManagers.add(tunnelManager);\n}\n\nexport function unregisterTunnelManager(\n tunnelManager: TunnelManagerLike\n): void {\n activeTunnelManagers.delete(tunnelManager);\n}\n\nasync function stopActiveTunnelManagers(): Promise<void> {\n const logger = getLogger().child('SecureCleanup');\n const managers = [...activeTunnelManagers];\n activeTunnelManagers.clear();\n for (const tunnelManager of managers) {\n try {\n await tunnelManager.stopAll();\n } catch (error) {\n logger.error('Error during tunnel cleanup:', error);\n }\n }\n}\n\nlet setupDone = false;\n\n/**\n * Utility function to ensure cleanup happens even if process exits unexpectedly.\n * Idempotent \u2014 safe to call multiple times; handlers are registered only once.\n */\nexport function setupSecureCleanup(): void {\n if (setupDone) return;\n setupDone = true;\n\n let isCleaningUp = false;\n const logger = getLogger().child('SecureCleanup');\n\n const cleanup = async () => {\n if (isCleaningUp) return;\n isCleaningUp = true;\n logger.info('Cleaning up...');\n await stopActiveTunnelManagers();\n };\n\n // Dynamic import of gracefulExit to avoid circular deps at module load\n const exit = async (code: number) => {\n const { gracefulExit } = await import('./process-lifecycle.js');\n await gracefulExit(code);\n };\n\n // Handle interrupt signal - only if not in interactive mode with an active readline.\n // If readline has been closed (e.g. after a tunnel wizard in interactive mode),\n // there is no readline-level SIGINT handler left, so we must handle it here.\n process.on('SIGINT', async () => {\n if (process.env.PERISCOPE_INTERACTIVE === 'true' && isReadlineActive()) {\n // Readline is live \u2014 let the readline-level handler in interactive.ts take over\n return;\n }\n\n logger.info('Shutting down gracefully...');\n await cleanup();\n await exit(0);\n });\n\n // Handle termination signal\n process.on('SIGTERM', async () => {\n logger.info('Shutting down gracefully...');\n await cleanup();\n await exit(0);\n });\n\n // Handle uncaught exceptions\n process.on('uncaughtException', async error => {\n // ObjectDisposedError from SSH pipe flush on a disposed SshStream is\n // expected during connection loss (see request-monitor.ts).\n // The reconnection logic in tunnel-manager.ts handles recovery;\n // crashing the process would prevent it from running.\n if (isSshChannelDisposedError(error)) {\n logger.warn(\n `SSH channel disposed (connection lost) \u2014 reconnection will be attempted: ${error.message}`\n );\n return;\n }\n\n logger.error('Uncaught exception:', error);\n try {\n const { trackException } = await import('./telemetry.js');\n trackException(error, { source: 'uncaughtException' });\n } catch {\n // Best-effort\n }\n await cleanup();\n await exit(1);\n });\n\n // Handle unhandled promise rejections\n process.on('unhandledRejection', async (reason, promise) => {\n logger.error('Unhandled rejection at:', promise, 'reason:', reason);\n try {\n const { trackException } = await import('./telemetry.js');\n const error =\n reason instanceof Error ? reason : new Error(String(reason));\n trackException(error, { source: 'unhandledRejection' });\n } catch {\n // Best-effort\n }\n await cleanup();\n await exit(1);\n });\n}\n\n/**\n * Manual cleanup function that can be called by interactive mode\n */\nexport async function performSecureCleanup(): Promise<void> {\n const logger = getLogger().child('SecureCleanup');\n logger.info('Performing secure cleanup...');\n await stopActiveTunnelManagers();\n}\n\n/**\n * Detect ObjectDisposedError from disposed SSH channels.\n * These surface as uncaught exceptions when pipe() flushes data to a\n * disposed SshStream. They are non-fatal because the session onClosed/\n * onDisconnected handlers in tunnel-manager.ts trigger reconnection.\n *\n * Uses error.name check to avoid coupling this module to the SSH library.\n */\nexport function isSshChannelDisposedError(error: Error): boolean {\n return error.name === 'ObjectDisposedError';\n}\n", "import { PeriscopeApi } from '@elf-5/periscope-api-client';\nimport { PeriscopeClient } from './client.js';\nimport {\n setupSecureCleanup,\n registerTunnelManager,\n unregisterTunnelManager,\n} from './secure-memory.js';\nimport {\n SshClientSession,\n SshSessionConfiguration,\n SshClientCredentials,\n SshDisconnectReason,\n SshAuthenticationType,\n KeyPair,\n} from '@microsoft/dev-tunnels-ssh';\nimport { SshKeyManager, SshKeyNotFoundError } from './ssh-key-manager.js';\nimport { log } from './logger.js';\nimport { exitOrThrow, isInteractiveMode } from './interactive-utils.js';\nimport { displayTunnelInfo } from './tunnel-utils.js';\nimport {\n SshClient,\n PortForwardingService,\n} from '@microsoft/dev-tunnels-ssh-tcp';\nimport { ErrorClassifier, ErrorType } from './error-classifier.js';\nimport { setupRequestMonitor } from './request-monitor.js';\nimport * as net from 'net';\n\n// Constants for tunnel operations\nconst PORT_CHECK_TIMEOUT_MS = 2000;\n// TODO(ELF-124): Make port monitoring interval configurable via environment variable\nconst PORT_MONITORING_INTERVAL_MS = 3000;\nconst DEFAULT_SSH_TUNNEL_PORT = 2222;\n\n// Timeout for SSH host key validation API call.\n// If the HTTPS endpoint is unreachable mid-handshake we want a fast failure\n// rather than an indefinite hang awaiting e.authenticationPromise.\nconst SSH_HOST_KEY_VALIDATION_TIMEOUT_MS = 10_000;\n\n// Constants for SSH reconnection with exponential backoff\nconst RECONNECT_INITIAL_DELAY_MS = 1000;\nconst RECONNECT_MAX_DELAY_MS = 30000;\nconst RECONNECT_BACKOFF_MULTIPLIER = 2;\nconst RECONNECT_MAX_ATTEMPTS = 10;\n\n/**\n * Represents an active tunnel's information (ephemeral, not persisted)\n */\nexport interface TunnelInfo {\n name: string;\n clientPort: number;\n sshTunnelPort: number;\n isConnected: boolean;\n /** Wildcard hostname from server config (e.g., \"develop.elf5.com\") */\n wildcardHostname?: string;\n /** URL separator from server config (e.g., \"-\" or \".\") */\n urlSeparator?: string;\n /** SSH host for tunnel connections (from server config) */\n sshHost?: string;\n /** Local host override for Host header rewriting (e.g., \"myapp.local:3000\") */\n localHost?: string;\n /** Scheme used by the local service (\"http\" or \"https\"). Defaults to \"http\" on server. */\n localScheme?: string;\n /** User's unique 6-character slug for tunnel namespace isolation (ELF-166) */\n slug?: string;\n}\n\nexport class TunnelManager {\n private activeTunnels: Map<string, SshClientSession> = new Map();\n private sshClients: Map<string, SshClient> = new Map();\n private retryIntervals: Map<string, NodeJS.Timeout> = new Map();\n // Maps tunnel name to tunnel info for tracking local state\n private tunnelInfoMap: Map<string, TunnelInfo> = new Map();\n // Tracks active reconnection attempts per tunnel to prevent concurrent reconnects\n private reconnecting: Map<string, boolean> = new Map();\n // Tracks pending reconnection timeouts so they can be cancelled on cleanup\n private reconnectTimeouts: Map<string, NodeJS.Timeout> = new Map();\n // Tracks tunnels being intentionally closed by the client so onClosed handler\n // can distinguish client-initiated closes from server rejections\n private intentionalDisconnects: Set<string> = new Set();\n private clientConfig: ReturnType<PeriscopeClient['getConfig']>;\n // SSH key pair loaded once per TunnelManager instance (set in connect()).\n // Reusing the same KeyPair across reconnects avoids repeated disk reads and\n // ensures consistent auth if the key file is rotated while a tunnel is active.\n private sshKeyPair: KeyPair | null = null;\n\n constructor(private client: PeriscopeClient) {\n this.clientConfig = this.client.getConfig();\n if (!this.clientConfig.serverUrl) {\n log.error('No server URL configured for tunnel manager');\n throw new Error('Server URL is not configured');\n }\n // Setup secure cleanup for private keys\n setupSecureCleanup();\n // Register this tunnel manager for cleanup\n registerTunnelManager(this);\n }\n\n /**\n * Check if a local port is listening\n */\n private async isPortListening(\n port: number,\n host: string = 'localhost'\n ): Promise<boolean> {\n return new Promise(resolve => {\n const socket = new net.Socket();\n\n socket.setTimeout(PORT_CHECK_TIMEOUT_MS);\n socket.on('connect', () => {\n socket.destroy();\n resolve(true);\n });\n\n socket.on('timeout', () => {\n socket.destroy();\n resolve(false);\n });\n\n socket.on('error', () => {\n resolve(false);\n });\n\n socket.connect(port, host);\n });\n }\n\n /**\n * Start monitoring for local service availability and manage tunnel connection\n * TODO(ELF-122): Add connection queuing/debouncing to prevent race conditions\n * during rapid port state changes\n */\n private startPortMonitoring(tunnelInfo: TunnelInfo): void {\n const tunnelName = tunnelInfo.name;\n const localPort = tunnelInfo.clientPort;\n\n // Clear any existing interval\n if (this.retryIntervals.has(tunnelName)) {\n clearInterval(this.retryIntervals.get(tunnelName));\n }\n\n // Track the last known state to detect transitions\n let lastServiceState: boolean | null = null;\n let isReconnecting = false;\n\n const interval = setInterval(async () => {\n try {\n const currentServiceState = await this.isPortListening(localPort);\n const tunnelIsActive = this.activeTunnels.has(tunnelName);\n\n // Handle state transitions\n if (\n lastServiceState !== null &&\n lastServiceState !== currentServiceState\n ) {\n if (\n currentServiceState &&\n !tunnelIsActive &&\n !isReconnecting &&\n !this.reconnecting.get(tunnelName)\n ) {\n // Service came back online and tunnel is not active - reconnect\n log.debug(\n `Local service started on port ${localPort} - reconnecting tunnel`\n );\n isReconnecting = true;\n try {\n await this.createSSHConnection(tunnelInfo);\n } catch (error) {\n log.error(\n `Failed to reconnect tunnel: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n isReconnecting = false;\n } else if (!currentServiceState && tunnelIsActive) {\n // Service went offline and tunnel is active - disconnect\n log.debug(\n `Local service stopped on port ${localPort} - disconnecting tunnel`\n );\n await this.disconnectTunnel(tunnelName);\n }\n } else if (lastServiceState === null) {\n // Initial state logging\n if (currentServiceState) {\n log.debug(\n `Local service detected on port ${localPort} - tunnel active`\n );\n } else {\n log.debug(\n `No local service on port ${localPort} - tunnel will connect when service starts`\n );\n if (tunnelIsActive) {\n await this.disconnectTunnel(tunnelName);\n }\n }\n }\n\n lastServiceState = currentServiceState;\n } catch (error) {\n log.error(\n `Error in port monitoring: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }, PORT_MONITORING_INTERVAL_MS);\n\n this.retryIntervals.set(tunnelName, interval);\n }\n\n /**\n * Disconnect tunnel without stopping the monitoring\n */\n private async disconnectTunnel(tunnelName: string): Promise<void> {\n const session = this.activeTunnels.get(tunnelName);\n if (session) {\n this.intentionalDisconnects.add(tunnelName);\n await session.close(\n SshDisconnectReason.byApplication,\n 'Local service unavailable'\n );\n this.activeTunnels.delete(tunnelName);\n }\n\n const sshClient = this.sshClients.get(tunnelName);\n if (sshClient) {\n sshClient.dispose();\n this.sshClients.delete(tunnelName);\n }\n\n // Update tunnel info to reflect disconnected state\n const tunnelInfo = this.tunnelInfoMap.get(tunnelName);\n if (tunnelInfo) {\n tunnelInfo.isConnected = false;\n }\n }\n\n /**\n * Handle unexpected server disconnection by attempting reconnection with exponential backoff.\n * Only triggers when the disconnect was NOT initiated by the client (e.g., server restart).\n */\n private async handleServerDisconnect(tunnelName: string): Promise<void> {\n // Prevent concurrent reconnection attempts for the same tunnel\n if (this.reconnecting.get(tunnelName)) {\n log.debug(`Reconnection already in progress for tunnel '${tunnelName}'`);\n return;\n }\n\n const tunnelInfo = this.tunnelInfoMap.get(tunnelName);\n if (!tunnelInfo) {\n log.debug(\n `Tunnel '${tunnelName}' no longer tracked, skipping reconnection`\n );\n return;\n }\n\n // Clean up the dead session/client before reconnecting\n this.activeTunnels.delete(tunnelName);\n const sshClient = this.sshClients.get(tunnelName);\n if (sshClient) {\n sshClient.dispose();\n this.sshClients.delete(tunnelName);\n }\n tunnelInfo.isConnected = false;\n\n this.reconnecting.set(tunnelName, true);\n let attempt = 0;\n let delay = RECONNECT_INITIAL_DELAY_MS;\n\n const attemptReconnect = async (): Promise<void> => {\n // Check if tunnel was stopped while we were waiting\n if (\n !this.tunnelInfoMap.has(tunnelName) ||\n !this.reconnecting.get(tunnelName)\n ) {\n log.debug(`Reconnection cancelled for tunnel '${tunnelName}'`);\n this.reconnecting.delete(tunnelName);\n return;\n }\n\n attempt++;\n log.info(\n `Reconnecting tunnel '${tunnelName}' (attempt ${attempt}/${RECONNECT_MAX_ATTEMPTS})...`\n );\n\n // Check if local service is still running before attempting SSH reconnection\n const isServiceRunning = await this.isPortListening(\n tunnelInfo.clientPort\n );\n if (!isServiceRunning) {\n log.info(\n `Local service on port ${tunnelInfo.clientPort} is not running, deferring reconnection to port monitor`\n );\n this.reconnecting.delete(tunnelName);\n return;\n }\n\n try {\n await this.createSSHConnection(tunnelInfo);\n\n // Check if tunnel was disconnected while createSSHConnection was in progress\n if (\n !this.reconnecting.get(tunnelName) ||\n !this.tunnelInfoMap.has(tunnelName)\n ) {\n log.debug(\n `Tunnel '${tunnelName}' was disconnected during reconnection, cleaning up`\n );\n const newSession = this.activeTunnels.get(tunnelName);\n if (newSession) {\n this.intentionalDisconnects.add(tunnelName);\n await newSession.close(SshDisconnectReason.byApplication);\n this.activeTunnels.delete(tunnelName);\n }\n const newClient = this.sshClients.get(tunnelName);\n if (newClient) {\n newClient.dispose();\n this.sshClients.delete(tunnelName);\n }\n this.reconnecting.delete(tunnelName);\n return;\n }\n\n log.success(`Tunnel '${tunnelName}' reconnected successfully`);\n this.reconnecting.delete(tunnelName);\n this.reconnectTimeouts.delete(tunnelName);\n return;\n } catch (error) {\n const classified = ErrorClassifier.classify(error, 'tunnel-reconnect');\n const message =\n classified.type === ErrorType.NETWORK\n ? 'server unreachable (may still be restarting)'\n : classified.userMessage;\n log.warn(\n `Reconnection attempt ${attempt}/${RECONNECT_MAX_ATTEMPTS} for tunnel '${tunnelName}': ${message}`\n );\n }\n\n if (attempt >= RECONNECT_MAX_ATTEMPTS) {\n log.error(\n `Failed to reconnect tunnel '${tunnelName}' after ${RECONNECT_MAX_ATTEMPTS} attempts. Port monitor will retry when server becomes available.`\n );\n this.reconnecting.delete(tunnelName);\n this.reconnectTimeouts.delete(tunnelName);\n return;\n }\n\n // Schedule next attempt with exponential backoff\n delay = Math.min(\n delay * RECONNECT_BACKOFF_MULTIPLIER,\n RECONNECT_MAX_DELAY_MS\n );\n log.debug(`Next reconnection attempt for '${tunnelName}' in ${delay}ms`);\n\n const timeout = setTimeout(() => {\n attemptReconnect();\n }, delay);\n this.reconnectTimeouts.set(tunnelName, timeout);\n };\n\n // Start first reconnection attempt after initial delay\n log.warn(\n `Server connection lost for tunnel '${tunnelName}', will attempt reconnection...`\n );\n const timeout = setTimeout(() => {\n attemptReconnect();\n }, delay);\n this.reconnectTimeouts.set(tunnelName, timeout);\n }\n\n /**\n * Establishes SSH connection with authentication\n * TODO(ELF-123): Add configurable timeout for SSH connection establishment\n */\n private async createSSHConnection(tunnelInfo: TunnelInfo): Promise<void> {\n const name = tunnelInfo.name;\n\n // Get server config (wildcard hostname, slug, SSH host, port) from server\n const sshCredentials = await this.client.getSSHCredentials();\n\n // Store server config from credentials for URL construction and SSH connection\n if (sshCredentials.wildcardHostname) {\n tunnelInfo.wildcardHostname = sshCredentials.wildcardHostname;\n tunnelInfo.urlSeparator = sshCredentials.urlSeparator || '.';\n }\n if (sshCredentials.serverPort) {\n tunnelInfo.sshTunnelPort = sshCredentials.serverPort;\n }\n // Store user slug for URL construction (ELF-166)\n if (sshCredentials.slug) {\n tunnelInfo.slug = sshCredentials.slug;\n }\n\n // Load the client's locally-generated SSH key pair (ELF-184).\n // Use the KeyPair cached in connect() \u2014 avoids repeated disk reads on reconnect.\n if (!this.sshKeyPair) {\n throw new Error(\n 'SSH key pair not loaded. Call connect() before createSSHConnection().'\n );\n }\n const keyPair = this.sshKeyPair;\n\n // Re-register the public key on every connection attempt (including reconnects).\n // The server may have restarted and lost its in-memory or persistent record of\n // the key. Re-registering is idempotent on a persistent DB and essential on\n // in-memory DBs where server restarts wipe all state.\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n await this.client.registerPublicKey(publicKey);\n\n // Create SSH session configuration with port forwarding service\n const sessionConfig = new SshSessionConfiguration();\n sessionConfig.addService(PortForwardingService);\n const sshClient = new SshClient(sessionConfig);\n\n // Determine SSH hostname: use server-provided sshHost if available,\n // otherwise fall back to the API hostname (SMBs may reuse the same CNAME for HTTP + SSH)\n const baseHostname = new URL(this.clientConfig.serverUrl!).hostname;\n const serverHostname = sshCredentials.sshHost || baseHostname;\n tunnelInfo.sshHost = serverHostname;\n const serverPort = tunnelInfo.sshTunnelPort;\n\n log.debug(`Connecting to SSH server: ${serverHostname}:${serverPort}`);\n const session = await sshClient.openSession(serverHostname, serverPort);\n\n // Server authentication \u2014 verify host key against the server via API (ELF-198).\n // The CLI sends the raw SSH wire-format key bytes it received during the handshake\n // to POST /api/user/ssh/validate-host-key; the server compares them against its own\n // key using constant-time comparison. Any failure blocks the connection: a 422 means\n // key mismatch (potential MITM); any other error means the key could not be verified.\n session.onAuthenticating(e => {\n log.trace(`Server authentication event - Type: ${e.authenticationType}`);\n const serverIdentity = {\n identity: { isAuthenticated: true, name: 'server' },\n };\n\n if (e.authenticationType !== SshAuthenticationType.serverPublicKey) {\n e.authenticationPromise = Promise.reject(\n new Error(\n `Unexpected server authentication type: ${e.authenticationType}`\n )\n );\n return;\n }\n\n if (!e.publicKey) {\n e.authenticationPromise = Promise.reject(\n new Error('Server did not present a public key during SSH handshake')\n );\n return;\n }\n\n const pk = e.publicKey;\n log.trace(`Server public key algorithm: ${pk.keyAlgorithmName}`);\n\n e.authenticationPromise = (async () => {\n const keyBytes = await pk.getPublicKeyBytes(pk.keyAlgorithmName);\n if (!keyBytes) {\n throw new Error('Failed to read server public key bytes');\n }\n const base64KeyBytes = Buffer.from(keyBytes).toString('base64');\n\n try {\n let timeoutId: NodeJS.Timeout;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () =>\n reject(\n new Error(\n `timed out after ${SSH_HOST_KEY_VALIDATION_TIMEOUT_MS / 1000} s`\n )\n ),\n SSH_HOST_KEY_VALIDATION_TIMEOUT_MS\n );\n });\n try {\n await Promise.race([\n this.client.validateSshHostKey(base64KeyBytes),\n timeoutPromise,\n ]);\n } finally {\n clearTimeout(timeoutId!);\n }\n log.trace('SSH host key verified via server API');\n } catch (error) {\n if (\n PeriscopeApi.ApiException.isApiException(error) &&\n error.status === 422\n ) {\n throw new Error(\n 'SSH host key verification failed: the key received during the SSH handshake ' +\n 'does not match the server key. This may indicate a MITM attack. ' +\n 'Contact your administrator if the server was recently updated.'\n );\n }\n // Re-throw all other errors \u2014 without a successful validation we cannot\n // confirm the server's identity, so the connection is refused.\n throw new Error(\n `SSH host key validation failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n return serverIdentity;\n })();\n });\n\n const serverAuthenticated = await session.authenticateServer();\n if (!serverAuthenticated) {\n throw new Error('Server authentication failed');\n }\n\n // SSH username format: {email}:{subdomain}:{clientPort}[:{localScheme}][:{localHost}]\n // Email is used as the unique identifier (display names may not be unique).\n // The server uses this to configure the SSH reverse tunnel to forward to the correct client port.\n // Optional localScheme (only included when \"https\") and localHost configure YARP proxying.\n // The server detects localScheme by checking if the next segment is \"http\" or \"https\".\n let username = `${sshCredentials.email}:${tunnelInfo.name}:${tunnelInfo.clientPort}`;\n if (tunnelInfo.localScheme === 'https') {\n username += `:https`;\n if (tunnelInfo.localHost) {\n username += `:${tunnelInfo.localHost}`;\n }\n } else if (tunnelInfo.localHost) {\n username += `:${tunnelInfo.localHost}`;\n }\n const credentials: SshClientCredentials = {\n username,\n publicKeys: [keyPair],\n };\n\n const clientAuthenticated = await session.authenticateClient(credentials);\n if (!clientAuthenticated) {\n throw new Error(\n \"SSH key rejected by server. Run 'periscope user key generate' to re-register your key.\"\n );\n }\n\n // Store active session and client using tunnel name as key\n this.activeTunnels.set(name, session);\n this.sshClients.set(name, sshClient);\n\n // Hook request monitoring to log incoming HTTP requests in the CLI\n setupRequestMonitor(session, name);\n\n // Listen for server-initiated disconnects (e.g., duplicate subdomain rejection, server restart).\n // The server may close the session after auth succeeds if tunnel registration fails,\n // or the session may close if the periscope server restarts.\n session.onClosed(e => {\n tunnelInfo.isConnected = false;\n\n // Client-initiated close (disconnect/disconnectTunnel/stopAll) \u2014 no reconnection\n if (this.intentionalDisconnects.has(name)) {\n this.intentionalDisconnects.delete(name);\n return;\n }\n\n if (e.reason === SshDisconnectReason.byApplication && e.message) {\n // Server explicitly rejected the tunnel (e.g., duplicate subdomain)\n log.error(`Server rejected tunnel '${name}': ${e.message}`);\n // Clean up and exit \u2014 this is a fatal error, not recoverable\n this.intentionalDisconnects.add(name);\n this.disconnect(name)\n .then(() => {\n exitOrThrow(1);\n })\n .catch(() => {\n exitOrThrow(1);\n });\n return;\n }\n\n // Unexpected close \u2014 attempt reconnection (e.g., server crash/restart)\n if (e.reason !== SshDisconnectReason.none) {\n log.warn(\n `Tunnel '${name}' disconnected unexpectedly: ${e.message || e.reason}`\n );\n }\n this.handleServerDisconnect(name);\n });\n\n // Handle transport-level disconnection (TCP connection lost)\n session.onDisconnected(() => {\n if (!tunnelInfo.isConnected) {\n // Already handled by onClosed or intentional disconnect\n return;\n }\n tunnelInfo.isConnected = false;\n log.warn(`Tunnel '${name}' transport disconnected`);\n this.handleServerDisconnect(name);\n });\n\n // Update tunnel info to reflect connected state\n tunnelInfo.isConnected = true;\n\n if (isInteractiveMode()) {\n displayTunnelInfo(tunnelInfo, this.clientConfig.serverUrl!, {\n isInteractive: true,\n tunnelName: name,\n showBackgroundStatus: true,\n });\n } else {\n displayTunnelInfo(tunnelInfo, this.clientConfig.serverUrl!);\n }\n\n return;\n }\n\n /**\n * Connect to establish an ephemeral SSH tunnel\n * @param name - Unique name for this tunnel\n * @param localPort - Local port to forward through the tunnel\n * @param localHost - Optional Host header override for local service\n * @param localScheme - Optional scheme for local service (\"http\" or \"https\")\n * @param sshKeyPath - Optional path to SSH private key file\n */\n async connect(\n name: string,\n localPort: number,\n localHost?: string,\n localScheme?: string,\n sshKeyPath?: string\n ): Promise<TunnelInfo> {\n try {\n // Validate required parameters\n if (!name) {\n throw new Error('Tunnel name is required');\n }\n // Validate tunnel name format - must not contain characters that break SSH username parsing\n // SSH username format is: {username}:{subdomain}:{clientPort}\n if (!/^[a-zA-Z0-9_-]+$/.test(name)) {\n throw new Error(\n 'Tunnel name must contain only alphanumeric characters, hyphens, and underscores'\n );\n }\n if (!localPort || localPort <= 0 || localPort > 65535) {\n throw new Error('Valid local port is required (1-65535)');\n }\n // Validate localHost before it is forwarded as the HTTP Host header by YARP.\n // Whitelist: hostname chars, dots, hyphens, colons (port/IPv6), square brackets (IPv6).\n // Colons are intentionally allowed: the server rejoins parts[3..] so \"host:port\"\n // round-trips correctly (e.g. \"myapp.local:3000\").\n if (localHost !== undefined && !/^[a-zA-Z0-9.\\-:[\\]]+$/.test(localHost)) {\n throw new Error(\n 'localHost contains invalid characters (allowed: alphanumeric, hyphens, dots, colons, square brackets)'\n );\n }\n if (\n localScheme !== undefined &&\n localScheme !== 'http' &&\n localScheme !== 'https'\n ) {\n throw new Error('localScheme must be \"http\" or \"https\"');\n }\n\n // Load SSH key once for the lifetime of this connect() call (and all reconnects).\n // Caching avoids repeated disk reads during exponential-backoff reconnection and\n // ensures consistent auth if the key file is rotated while a tunnel is active.\n // Note: sshKeyPath is only honoured on the first connect() call; subsequent calls\n // (e.g. reconnects) reuse the cached KeyPair regardless of the sshKeyPath argument.\n if (!this.sshKeyPair) {\n this.sshKeyPair = await SshKeyManager.loadKeyPair(\n sshKeyPath ?? this.clientConfig.sshKeyPath\n );\n }\n\n // Create tunnel info (ephemeral, not persisted to server)\n const tunnelInfo: TunnelInfo = {\n name,\n clientPort: localPort,\n sshTunnelPort: DEFAULT_SSH_TUNNEL_PORT,\n isConnected: false,\n localHost,\n localScheme,\n };\n\n // Store tunnel info locally\n this.tunnelInfoMap.set(name, tunnelInfo);\n\n log.info(\n `Establishing tunnel '${name}' for local port ${localPort} on ${this.clientConfig.serverUrl}`\n );\n\n // Check if local service is available\n const isServiceRunning = await this.isPortListening(localPort);\n\n if (!isServiceRunning) {\n log.warn(`Local service not detected on port ${localPort}`);\n log.warn(\n 'Waiting for service - tunnel will connect when service becomes available'\n );\n\n // Start monitoring - it will establish SSH connection when service comes online\n this.startPortMonitoring(tunnelInfo);\n\n // Return tunnel info - monitoring is running and will connect when ready\n return tunnelInfo;\n }\n\n log.debug(\n `Local service detected on port ${localPort} - establishing tunnel connection`\n );\n\n // Establish SSH connection\n await this.createSSHConnection(tunnelInfo);\n\n // Start monitoring for local service throughout tunnel lifecycle\n this.startPortMonitoring(tunnelInfo);\n\n return tunnelInfo;\n } catch (error) {\n // Remove tunnel info on error\n this.tunnelInfoMap.delete(name);\n\n // Re-throw sentinel errors so callers can handle them by type.\n if (error instanceof SshKeyNotFoundError) {\n throw error;\n }\n\n throw new Error(\n `Failed to establish tunnel: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n /**\n * Get list of active tunnels managed by this CLI session\n */\n getActiveTunnels(): TunnelInfo[] {\n return Array.from(this.tunnelInfoMap.values());\n }\n\n /**\n * Disconnect the SSH connection for a tunnel\n */\n async disconnect(name: string): Promise<void> {\n // Cancel any pending reconnection attempts\n this.reconnecting.delete(name);\n const reconnectTimeout = this.reconnectTimeouts.get(name);\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n this.reconnectTimeouts.delete(name);\n }\n\n // Stop port monitoring\n const interval = this.retryIntervals.get(name);\n if (interval) {\n clearInterval(interval);\n this.retryIntervals.delete(name);\n }\n\n // Stop the SSH tunnel if active\n const session = this.activeTunnels.get(name);\n if (session) {\n this.intentionalDisconnects.add(name);\n await session.close(\n SshDisconnectReason.byApplication,\n 'Tunnel stopped by user'\n );\n this.activeTunnels.delete(name);\n }\n\n // Dispose SSH client if active\n const sshClient = this.sshClients.get(name);\n if (sshClient) {\n sshClient.dispose();\n this.sshClients.delete(name);\n }\n\n // Remove tunnel info\n this.tunnelInfoMap.delete(name);\n\n log.info(`Disconnected tunnel '${name}'`);\n }\n\n async stopAll(): Promise<void> {\n // Cancel all pending reconnection attempts\n for (const [, timeout] of this.reconnectTimeouts) {\n clearTimeout(timeout);\n }\n this.reconnectTimeouts.clear();\n this.reconnecting.clear();\n\n // Stop all port monitoring\n for (const [, interval] of this.retryIntervals) {\n clearInterval(interval);\n }\n this.retryIntervals.clear();\n\n // Close all active SSH sessions\n for (const [name, session] of this.activeTunnels) {\n this.intentionalDisconnects.add(name);\n await session.close(\n SshDisconnectReason.byApplication,\n 'All tunnels stopped'\n );\n }\n this.activeTunnels.clear();\n // Don't clear intentionalDisconnects here \u2014 let onClosed handlers\n // consume them individually to avoid a race where the handler fires\n // after the set is cleared and mistakenly triggers reconnection.\n\n // Dispose all SSH clients\n for (const [, sshClient] of this.sshClients) {\n sshClient.dispose();\n }\n this.sshClients.clear();\n\n this.sshKeyPair = null;\n\n // Clear tunnel info\n this.tunnelInfoMap.clear();\n }\n\n /**\n * Cleanup and unregister this tunnel manager\n */\n async dispose(): Promise<void> {\n await this.stopAll();\n unregisterTunnelManager(this);\n }\n}\n", "import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { log } from './logger.js';\nimport {\n SshAlgorithms,\n PublicKeyAlgorithm,\n KeyPair,\n} from '@microsoft/dev-tunnels-ssh';\nimport {\n exportPrivateKey,\n exportPublicKey,\n importKeyFile,\n} from '@microsoft/dev-tunnels-ssh-keys';\n\n// KeyFormat const enum values from @microsoft/dev-tunnels-ssh-keys@3.6.x.\n// Inlined to avoid runtime import of a const enum from a CJS package (esbuild limitation).\n// If these values change after a library upgrade, generateKeyPair() will produce keys in the\n// wrong format. The unit test asserting \"-----BEGIN PRIVATE KEY-----\" output will catch a\n// silent breakage.\n//\n// NOTE: KeyFormat.OpenSsh (6) is declared in keyFormat.d.ts but NOT implemented in the\n// runtime (no formatter registered for 6). Using Default (0) exports private keys as PKCS#8\n// (\"-----BEGIN PRIVATE KEY-----\"), which importKeyFile() can load via format auto-detection.\n// Migrate to 6 if a future library version implements OpenSsh export.\nconst KEY_FORMAT_SSH = 1; // KeyFormat.Ssh \u2014 \"ecdsa-sha2-nistp256 AAAA...\" public key\nconst KEY_FORMAT_PRIVATE = 0; // KeyFormat.Default \u2014 \"-----BEGIN PRIVATE KEY-----\" (PKCS#8)\n\nconst DEFAULT_KEY_DIR = path.join(os.homedir(), '.periscope');\nconst DEFAULT_PRIVATE_KEY_FILE = 'id_ecdsa';\n\nexport interface SshKeyInfo {\n exists: boolean;\n path: string;\n pubKeyPath: string;\n}\n\n/**\n * Thrown by SshKeyManager.loadKeyPair() when the private key file does not exist.\n * Caught by TunnelCommand to trigger the missing-key setup wizard.\n */\nexport class SshKeyNotFoundError extends Error {\n constructor(keyPath: string) {\n super(\n `SSH key not found at ${keyPath}. ` +\n `Run 'periscope user key generate' to generate and register a key.`\n );\n this.name = 'SshKeyNotFoundError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SshKeyManager {\n /**\n * Returns the default SSH private key path.\n */\n static getDefaultKeyPath(): string {\n return path.join(DEFAULT_KEY_DIR, DEFAULT_PRIVATE_KEY_FILE);\n }\n\n /**\n * Generates a new ECDSA P-256 key pair and saves to disk.\n * Warns before overwriting an existing key \u2014 callers should confirm with the user\n * before calling this on an already-registered key path.\n */\n static async generateKeyPair(keyPath?: string): Promise<KeyPair> {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n const publicKeyPath = `${privateKeyPath}.pub`;\n\n if (fs.existsSync(privateKeyPath)) {\n log.warn(\n `Overwriting existing SSH key at ${privateKeyPath}. ` +\n 'Any active tunnels using the old key will need to reconnect after re-login.'\n );\n }\n\n // Ensure directory exists (mkdirSync with recursive is a no-op if it already exists)\n fs.mkdirSync(path.dirname(privateKeyPath), { recursive: true });\n\n // Generate ECDSA P-256 key pair using the dev-tunnels SSH library\n const algorithms = SshAlgorithms.publicKey as Record<\n string,\n PublicKeyAlgorithm | null\n >;\n const algorithm = algorithms['ecdsaSha2Nistp256'];\n if (!algorithm) {\n throw new Error('ECDSA P-256 algorithm not available');\n }\n const keyPair = await algorithm.generateKeyPair();\n\n // Export and save private key (PKCS#8 format: -----BEGIN PRIVATE KEY-----)\n // The private key is stored unencrypted (null passphrase). File mode 0o600 restricts\n // read access to the owner, but this does not protect against backup exfiltration or\n // shared filesystems. Use `--key <path>` with an independently-secured key if stronger\n // protection is required.\n const privateKeyStr = await exportPrivateKey(\n keyPair,\n null,\n KEY_FORMAT_PRIVATE\n );\n\n // Export and save public key (SSH format: ecdsa-sha2-nistp256 AAAA...)\n const publicKeyStr = await exportPublicKey(keyPair, KEY_FORMAT_SSH);\n\n // Write to temp files then rename to avoid leaving a corrupted or mismatched key pair\n // on disk if the process is killed (e.g. SIGKILL) between the two writes. fs.renameSync\n // is atomic on POSIX systems; on Windows it may overwrite but is still safer than a\n // partial writeFileSync.\n const tmpPrivatePath = `${privateKeyPath}.tmp`;\n const tmpPublicPath = `${publicKeyPath}.tmp`;\n try {\n fs.writeFileSync(tmpPrivatePath, privateKeyStr, { mode: 0o600 });\n fs.writeFileSync(tmpPublicPath, publicKeyStr, { mode: 0o644 });\n fs.renameSync(tmpPrivatePath, privateKeyPath);\n fs.renameSync(tmpPublicPath, publicKeyPath);\n } catch (err) {\n // Best-effort cleanup of temp files on failure\n for (const tmp of [tmpPrivatePath, tmpPublicPath]) {\n try {\n fs.unlinkSync(tmp);\n } catch {\n /* already renamed or never created */\n }\n }\n throw err;\n }\n\n log.debug(`SSH key pair generated at ${privateKeyPath}`);\n return keyPair;\n }\n\n /**\n * Loads an existing key pair from disk.\n * Throws if the key file does not exist.\n */\n static async loadKeyPair(keyPath?: string): Promise<KeyPair> {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n\n if (!fs.existsSync(privateKeyPath)) {\n throw new SshKeyNotFoundError(privateKeyPath);\n }\n\n try {\n return await importKeyFile(privateKeyPath, null);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // importKeyFile throws a generic crypto error for passphrase-protected keys.\n // Surface a more actionable message rather than a raw library trace.\n throw new Error(\n `Failed to load SSH key at ${privateKeyPath}: ${msg}. ` +\n 'If the key is passphrase-protected, use an unprotected key or specify ' +\n 'an alternative key path with --key.'\n );\n }\n }\n\n /**\n * Ensures a key pair exists, generating one if not.\n * Returns the loaded key pair.\n */\n static async ensureKeyPair(keyPath?: string): Promise<KeyPair> {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n\n if (!fs.existsSync(privateKeyPath)) {\n log.info('Generating SSH key pair...');\n return this.generateKeyPair(keyPath);\n }\n\n return this.loadKeyPair(keyPath);\n }\n\n /**\n * Exports the public key from an in-memory KeyPair in SSH wire format.\n * Use this after generateKeyPair() or ensureKeyPair() to avoid a redundant disk read.\n */\n static async exportPublicKey(keyPair: KeyPair): Promise<string> {\n return (await exportPublicKey(keyPair, KEY_FORMAT_SSH)).trim();\n }\n\n /**\n * Returns the public key string in SSH format (e.g. \"ecdsa-sha2-nistp256 AAAA...\").\n * Reads from the .pub file if present; otherwise imports the private key to derive it.\n */\n static async getPublicKeyString(keyPath?: string): Promise<string> {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n const publicKeyPath = `${privateKeyPath}.pub`;\n\n if (fs.existsSync(publicKeyPath)) {\n return fs.readFileSync(publicKeyPath, 'utf-8').trim();\n }\n\n // Derive from private key\n const keyPair = await this.loadKeyPair(keyPath);\n return (await exportPublicKey(keyPair, KEY_FORMAT_SSH)).trim();\n }\n\n /**\n * Returns info about the current SSH key (path, existence).\n */\n static getKeyInfo(keyPath?: string): SshKeyInfo {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n return {\n exists: fs.existsSync(privateKeyPath),\n path: privateKeyPath,\n pubKeyPath: `${privateKeyPath}.pub`,\n };\n }\n}\n", "/**\n * Utility functions for interactive mode handling\n */\nimport { log } from './logger.js';\n\n/**\n * Check if we're currently running in interactive mode\n */\nexport function isInteractiveMode(): boolean {\n return process.env.PERISCOPE_INTERACTIVE === 'true';\n}\n\n/**\n * Exit or throw error based on context\n * In interactive mode, throws an error instead of calling process.exit\n *\n * Note: telemetry is flushed in the signal handlers (secure-memory.ts)\n * and the main CLI exit paths (cli.ts), not here. This function is sync\n * and cannot await an async flush.\n */\nexport function exitOrThrow(code: number, message?: string): never {\n if (isInteractiveMode()) {\n throw new Error(message || `Command failed with exit code ${code}`);\n } else {\n if (message) {\n log.error(message);\n }\n process.exit(code);\n }\n}\n", "import { log } from './logger.js';\n\n/**\n * Helper function to display tunnel information consistently\n */\nexport function displayTunnelInfo(\n tunnel: {\n id?: string | number;\n clientPort?: number;\n name?: string;\n remoteURL?: string;\n sshTunnelPort?: number;\n wildcardHostname?: string;\n urlSeparator?: string;\n sshHost?: string;\n slug?: string;\n },\n serverUrl: string,\n options?: {\n isInteractive?: boolean;\n tunnelName?: string;\n showBackgroundStatus?: boolean;\n showAsListItem?: boolean;\n }\n): void {\n const {\n isInteractive = false,\n tunnelName,\n showBackgroundStatus = false,\n showAsListItem = false,\n } = options || {};\n\n // Use SSH host from tunnel info if available, otherwise fall back to API hostname\n const serverHostname = tunnel.sshHost || new URL(serverUrl).hostname;\n\n // Use the remote URL from the server, or construct one from wildcard hostname config\n // Priority: 1) explicit remoteURL, 2) wildcardHostname from server, 3) fallback to serverUrl hostname\n let remoteUrl: string;\n if (tunnel.remoteURL) {\n remoteUrl = tunnel.remoteURL;\n } else if (tunnel.name && tunnel.wildcardHostname) {\n // Use server's wildcard config: {subdomain}-{slug}{separator}{wildcardHostname}\n // ELF-166: Server appends slug to tunnel name for namespace isolation\n const separator = tunnel.urlSeparator || '.';\n const fullSubdomain = tunnel.slug\n ? `${tunnel.name}-${tunnel.slug}`\n : tunnel.name;\n remoteUrl = `https://${fullSubdomain}${separator}${tunnel.wildcardHostname}`;\n } else if (tunnel.name) {\n // Fallback to server hostname (legacy behavior)\n const fullSubdomain = tunnel.slug\n ? `${tunnel.name}-${tunnel.slug}`\n : tunnel.name;\n remoteUrl = `https://${fullSubdomain}.${serverHostname}`;\n } else {\n remoteUrl = 'N/A';\n }\n\n // Show success message for interactive mode\n if (isInteractive && tunnelName) {\n log.success(`Tunnel connected: ${tunnelName}`);\n }\n\n if (showAsListItem) {\n // List format with bullet point and name\n log.info(` Local: localhost:${tunnel.clientPort || 'N/A'}`);\n log.info(` Server: ${serverHostname}:${tunnel.sshTunnelPort || 443}`);\n log.info(` \uD83C\uDF10 Remote URL: ${remoteUrl}`);\n log.info('\u2500'.repeat(60));\n } else {\n // Single tunnel format\n log.info(`Local: localhost:${tunnel.clientPort || 'N/A'}`);\n log.info(`Server: ${serverHostname}:${tunnel.sshTunnelPort || 443}`);\n log.info(`Remote URL: ${remoteUrl}`);\n }\n\n // Show background status for interactive mode\n if (showBackgroundStatus) {\n log.info('Status: Running in background');\n }\n}\n", "/**\n * Error classification system for enhanced error handling\n */\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * HTTP status code constants\n */\nconst HTTP_STATUS = {\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n REQUEST_TIMEOUT: 408,\n RATE_LIMITED: 429,\n INTERNAL_SERVER_ERROR: 500,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n CLOUDFLARE_ERROR: 522,\n ORIGIN_UNREACHABLE: 523,\n TIMEOUT: 524,\n} as const;\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\n/**\n * Common HTTP error response interface\n */\ninterface HttpErrorResponse {\n status?: number;\n statusCode?: number;\n code?: number | string;\n response?: {\n status?: number;\n statusCode?: number;\n };\n message?: string;\n}\n\n/**\n * Type-safe error object with known properties\n */\ntype ErrorWithProperties = Error & Partial<HttpErrorResponse>;\n\n// ============================================================================\n// Exported Types and Enums\n// ============================================================================\n\nexport enum ErrorType {\n AUTHENTICATION = 'authentication',\n AUTHORIZATION = 'authorization',\n NETWORK = 'network',\n VALIDATION = 'validation',\n SERVER = 'server',\n TUNNEL = 'tunnel',\n UNKNOWN = 'unknown',\n}\n\nexport enum ErrorSeverity {\n LOW = 'low',\n MEDIUM = 'medium',\n HIGH = 'high',\n CRITICAL = 'critical',\n}\n\nexport interface ClassifiedError {\n type: ErrorType;\n severity: ErrorSeverity;\n httpStatus?: number;\n isRetryable: boolean;\n userMessage: string;\n suggestedActions: string[];\n technicalDetails?: string;\n}\n\n// ============================================================================\n// Main Error Classifier Class\n// ============================================================================\n\n/**\n * Enhanced error classifier with granular categorization\n */\nexport class ErrorClassifier {\n /**\n * Classify an error based on various signals\n */\n static classify(error: unknown, context?: string): ClassifiedError {\n const errorMessage = this.extractMessage(error).toLowerCase();\n const httpStatus = this.extractHttpStatus(error);\n\n // Authentication errors\n if (this.isAuthenticationError(errorMessage, httpStatus)) {\n return this.createAuthenticationError(errorMessage, httpStatus);\n }\n\n // Authorization errors\n if (this.isAuthorizationError(errorMessage, httpStatus)) {\n return this.createAuthorizationError(errorMessage, httpStatus);\n }\n\n // Network errors\n if (this.isNetworkError(errorMessage, httpStatus)) {\n return this.createNetworkError(errorMessage, httpStatus);\n }\n\n // Validation errors\n if (this.isValidationError(errorMessage, httpStatus)) {\n return this.createValidationError(errorMessage, context);\n }\n\n // Server errors\n if (this.isServerError(errorMessage, httpStatus)) {\n return this.createServerError(errorMessage, httpStatus);\n }\n\n // Tunnel-specific errors\n if (this.isTunnelError(errorMessage, context)) {\n return this.createTunnelError(errorMessage, context);\n }\n\n // Default unknown error\n return this.createUnknownError(errorMessage, httpStatus);\n }\n\n /**\n * Extract message from various error formats\n */\n private static extractMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (error && typeof error === 'object') {\n const err = error as { message?: string };\n if (typeof err.message === 'string') {\n return err.message;\n }\n }\n return String(error);\n }\n\n private static extractHttpStatus(error: unknown): number | undefined {\n if (error && typeof error === 'object') {\n const err = error as ErrorWithProperties;\n\n // Check for numeric status codes in common properties\n if (typeof err.status === 'number') return err.status;\n if (typeof err.statusCode === 'number') return err.statusCode;\n if (typeof err.response?.status === 'number') return err.response.status;\n if (typeof err.response?.statusCode === 'number')\n return err.response.statusCode;\n if (typeof err.code === 'number') return err.code;\n\n // Extract from error message using regex\n // TODO(ELF-125): Improve regex to prevent false positives (e.g., \"line 404\", \"port 500\")\n const message = err.message || '';\n const statusMatch = message.match(/\\b([4-5]\\d{2})\\b/);\n if (statusMatch) {\n const statusCode = parseInt(statusMatch[1], 10);\n if (statusCode >= 400 && statusCode < 600) {\n return statusCode;\n }\n }\n }\n return undefined;\n }\n\n private static isAuthenticationError(\n message: string,\n status?: number\n ): boolean {\n const authKeywords = [\n 'authentication failed',\n 'token expired',\n 'invalid token',\n 'token invalid',\n 'authentication required',\n 'login required',\n 'session expired',\n 'unauthorized',\n 'not authenticated',\n 'invalid credentials',\n ];\n\n return (\n status === HTTP_STATUS.UNAUTHORIZED ||\n authKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isAuthorizationError(\n message: string,\n status?: number\n ): boolean {\n const authzKeywords = [\n 'forbidden',\n 'access denied',\n 'insufficient permissions',\n 'not authorized',\n 'permission denied',\n 'scope required',\n 'accountpendingapproval',\n 'maxusersexceeded',\n 'user limit reached',\n ];\n\n return (\n status === HTTP_STATUS.FORBIDDEN ||\n authzKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isNetworkError(message: string, status?: number): boolean {\n const networkKeywords = [\n 'connection timeout',\n 'network error',\n 'connection refused',\n 'connection reset',\n 'dns lookup failed',\n 'host not found',\n 'enotfound',\n 'econnrefused',\n 'econnreset',\n 'etimedout',\n 'socket hang up',\n 'network unreachable',\n 'rate limited',\n 'too many requests',\n 'request rate exceeded',\n 'fetch failed',\n 'server is unreachable',\n 'failed to fetch',\n ];\n\n const networkStatuses = [\n HTTP_STATUS.REQUEST_TIMEOUT,\n HTTP_STATUS.RATE_LIMITED,\n HTTP_STATUS.BAD_GATEWAY,\n HTTP_STATUS.SERVICE_UNAVAILABLE,\n HTTP_STATUS.GATEWAY_TIMEOUT,\n HTTP_STATUS.CLOUDFLARE_ERROR,\n HTTP_STATUS.ORIGIN_UNREACHABLE,\n HTTP_STATUS.TIMEOUT,\n ];\n\n return (\n (status !== undefined &&\n networkStatuses.includes(status as (typeof networkStatuses)[number])) ||\n networkKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isValidationError(message: string, status?: number): boolean {\n const validationKeywords = [\n 'validation error',\n 'invalid input',\n 'bad request',\n 'malformed',\n 'invalid format',\n 'missing required',\n 'invalid parameter',\n ];\n\n return (\n status === HTTP_STATUS.BAD_REQUEST ||\n validationKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isServerError(message: string, status?: number): boolean {\n const serverKeywords = [\n 'internal server error',\n 'server error',\n 'service unavailable',\n 'database error',\n 'configuration error',\n ];\n\n return (\n (status !== undefined && status >= 500 && status < 600) ||\n serverKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isTunnelError(message: string, context?: string): boolean {\n const tunnelKeywords = [\n 'tunnel',\n 'ssh connection',\n 'port forwarding',\n 'local port',\n 'remote port',\n 'address already in use',\n 'port already in use',\n 'bind failed',\n 'eaddrinuse',\n 'port is not available',\n 'connection already exists',\n 'failed to establish tunnel',\n ];\n\n // Pattern for \"Port X is already in use\" format\n const portInUsePattern = /port\\s+\\d+\\s+is\\s+already\\s+in\\s+use/i;\n\n const isTunnelContext = context?.toLowerCase().includes('tunnel');\n\n return (\n isTunnelContext ||\n tunnelKeywords.some(keyword => message.includes(keyword)) ||\n portInUsePattern.test(message)\n );\n }\n\n private static createAuthenticationError(\n message: string,\n status?: number\n ): ClassifiedError {\n const isTokenExpired = message.includes('expired');\n const isInvalidToken =\n message.includes('invalid') && message.includes('token');\n\n return {\n type: ErrorType.AUTHENTICATION,\n severity: ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: true,\n userMessage: isTokenExpired\n ? 'Your session has expired. Please log in again.'\n : isInvalidToken\n ? 'Your authentication token is invalid. Please log in again.'\n : 'Authentication required. Please log in.',\n suggestedActions: [\n 'Run: periscope auth login',\n 'Check status: periscope status',\n ],\n technicalDetails: isTokenExpired\n ? 'Token expiration detected'\n : isInvalidToken\n ? 'Invalid token format or signature'\n : 'Authentication credentials missing or invalid',\n };\n }\n\n private static createAuthorizationError(\n message: string,\n status?: number\n ): ClassifiedError {\n const isPendingApproval =\n message.includes('accountpendingapproval') ||\n message.includes('pending approval');\n\n const isMaxUsersExceeded =\n message.includes('maxusersexceeded') ||\n message.includes('user limit reached');\n\n if (isMaxUsersExceeded) {\n return {\n type: ErrorType.AUTHORIZATION,\n severity: ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: false,\n userMessage:\n 'Your organization has reached its maximum user limit. No new accounts can be created.',\n suggestedActions: [\n 'Contact your administrator to increase the user limit or remove inactive users',\n 'Check your license details with your administrator',\n ],\n technicalDetails: 'Server returned MaxUsersExceeded (HTTP 403)',\n };\n }\n\n return {\n type: ErrorType.AUTHORIZATION,\n severity: ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: false,\n userMessage: isPendingApproval\n ? 'Your account is pending approval by an administrator.'\n : 'Access denied. You do not have permission to perform this action.',\n suggestedActions: isPendingApproval\n ? [\n 'Wait for an administrator to approve your account',\n 'Check your account status: periscope status',\n ]\n : [\n 'Check your account permissions',\n 'Contact your administrator if you believe you should have access',\n 'Verify you are using the correct account: periscope status',\n ],\n technicalDetails: isPendingApproval\n ? 'Account status is PendingApproval'\n : 'Insufficient permissions for the requested operation',\n };\n }\n\n private static createNetworkError(\n message: string,\n status?: number\n ): ClassifiedError {\n const isTimeout = message.includes('timeout');\n const isConnectionRefused =\n message.includes('refused') || message.includes('econnrefused');\n const isDnsError = message.includes('enotfound') || message.includes('dns');\n const isServerUnreachable =\n message.includes('server is unreachable') ||\n message.includes('fetch failed');\n const isRateLimited =\n status === HTTP_STATUS.RATE_LIMITED ||\n message.includes('rate limited') ||\n message.includes('too many requests');\n\n // Extract server URL from error message if present (format: \"Server is unreachable (https://...): ...\")\n const urlMatch = message.match(/\\((https?:\\/\\/[^)]+)\\)/);\n const serverUrl = urlMatch ? urlMatch[1] : null;\n\n // Build user message with server URL context when available\n let userMessage: string;\n if (isServerUnreachable) {\n userMessage = serverUrl\n ? `Cannot connect to server at ${serverUrl}. Please check your network connection.`\n : 'Cannot connect to server. Please check your network connection and server URL.';\n } else if (isRateLimited) {\n userMessage =\n 'Too many requests. Please wait a few minutes and try again.';\n } else if (isDnsError) {\n userMessage = serverUrl\n ? `Cannot resolve server address for ${serverUrl}. Please check the server URL.`\n : 'Cannot resolve server address. Please check the server URL.';\n } else if (isConnectionRefused) {\n userMessage = serverUrl\n ? `Connection refused by ${serverUrl}. The server may be down or unreachable.`\n : 'Connection refused. The server may be down or unreachable.';\n } else if (isTimeout) {\n userMessage = 'Request timed out. Please try again.';\n } else {\n userMessage = 'Network error. Please check your connection.';\n }\n\n return {\n type: ErrorType.NETWORK,\n severity: isTimeout\n ? ErrorSeverity.MEDIUM\n : isRateLimited\n ? ErrorSeverity.LOW\n : ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: true,\n userMessage,\n suggestedActions: isRateLimited\n ? [\n 'Wait a few minutes before trying again',\n 'Reduce the frequency of requests',\n 'Check if you have exceeded the rate limit',\n ]\n : [\n 'Check your internet connection',\n 'Verify the server URL is correct: periscope config get',\n 'Check network connectivity and firewall settings',\n ],\n technicalDetails: isDnsError\n ? 'DNS resolution failed'\n : isConnectionRefused\n ? 'Connection refused by server'\n : isTimeout\n ? 'Request timeout'\n : isRateLimited\n ? 'API rate limit exceeded'\n : 'Network connectivity issue',\n };\n }\n\n private static createValidationError(\n message: string,\n context?: string\n ): ClassifiedError {\n return {\n type: ErrorType.VALIDATION,\n severity: ErrorSeverity.LOW,\n httpStatus: HTTP_STATUS.BAD_REQUEST,\n isRetryable: false,\n userMessage: 'Invalid input. Please check your command parameters.',\n suggestedActions: [\n 'Check the command parameters and try again',\n 'Review the command documentation: periscope --help',\n 'Ensure all required parameters are provided',\n ],\n technicalDetails: `Input validation failed${context ? ` in context: ${context}` : ''}: ${message}`,\n };\n }\n\n private static createServerError(\n message: string,\n status?: number\n ): ClassifiedError {\n const isServiceUnavailable =\n status === HTTP_STATUS.SERVICE_UNAVAILABLE ||\n message.includes('unavailable');\n\n return {\n type: ErrorType.SERVER,\n severity: ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: isServiceUnavailable,\n userMessage: isServiceUnavailable\n ? 'Server is temporarily unavailable. Please try again later.'\n : 'Server error. Please try again or contact support.',\n suggestedActions: [\n 'Try again in a few minutes',\n 'Check server status or contact support if the problem persists',\n ],\n technicalDetails: isServiceUnavailable\n ? 'Service temporarily unavailable'\n : 'Internal server error',\n };\n }\n\n private static createTunnelError(\n message: string,\n context?: string\n ): ClassifiedError {\n const portInUsePattern = /port\\s+\\d+\\s+is\\s+already\\s+in\\s+use/i;\n const isPortError =\n (message.includes('port') &&\n (message.includes('use') || message.includes('bound'))) ||\n portInUsePattern.test(message);\n const isAddressInUse =\n message.includes('address already in use') ||\n message.includes('eaddrinuse');\n const isLocalServiceError =\n message.includes('local') && message.includes('service');\n const isBindError = message.includes('bind failed');\n const isConnectionFailed =\n message.includes('failed to establish tunnel') ||\n message.includes('ssh connection');\n\n return {\n type: ErrorType.TUNNEL,\n severity:\n isPortError || isAddressInUse || isBindError\n ? ErrorSeverity.HIGH\n : ErrorSeverity.MEDIUM,\n isRetryable: !(isPortError || isAddressInUse || isBindError),\n userMessage:\n isAddressInUse || isPortError\n ? 'Port is already in use. Try a different local port.'\n : isBindError\n ? 'Failed to bind to port. Check port availability and permissions.'\n : isLocalServiceError\n ? 'Local service is not running.'\n : isConnectionFailed\n ? 'Failed to establish SSH connection to tunnel server.'\n : 'Tunnel operation failed.',\n suggestedActions: [\n 'Try using a different local port',\n 'Verify local port is not already in use: netstat -tlnp | grep :PORT',\n 'Check tunnel connection: periscope connect <name> --target http://localhost:<port>',\n 'Ensure local service is running on the specified port',\n ],\n technicalDetails: `${\n isAddressInUse\n ? 'Address/port already in use (EADDRINUSE)'\n : isPortError\n ? 'Port conflict detected'\n : isBindError\n ? 'Port binding failed'\n : isLocalServiceError\n ? 'Local service not available'\n : isConnectionFailed\n ? 'SSH tunnel connection failed'\n : 'Tunnel operation failed'\n }${context ? ` in context: ${context}` : ''}`,\n };\n }\n\n private static createUnknownError(\n message: string,\n httpStatus?: number\n ): ClassifiedError {\n return {\n type: ErrorType.UNKNOWN,\n severity: ErrorSeverity.MEDIUM,\n httpStatus,\n isRetryable: true,\n userMessage: 'An unexpected error occurred. Please try again.',\n suggestedActions: [\n 'Try the operation again',\n 'Check the command documentation: periscope --help',\n 'Contact support if the issue persists',\n ],\n technicalDetails: `Unclassified error occurred: ${message}`,\n };\n }\n}\n", "import { Duplex } from 'stream';\n\nimport chalk from 'chalk';\n\nimport { SshClientSession } from '@microsoft/dev-tunnels-ssh';\nimport { PortForwardingService } from '@microsoft/dev-tunnels-ssh-tcp';\n\nimport { ConfigManager } from './config-manager.js';\nimport { log } from './logger.js';\nimport { isSshChannelDisposedError } from './secure-memory.js';\n\nconst HTTP_METHODS =\n /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|CONNECT|TRACE)\\s+(\\S+)/i;\nconst MAX_PEEK_BYTES = 256;\n\n/**\n * Parse an HTTP request line from a buffer and log it.\n * Returns true if a request line was found and logged.\n */\nexport function parseAndLogRequestLine(\n chunk: Buffer,\n _tunnelName: string\n): boolean {\n try {\n const str = chunk.toString(\n 'utf8',\n 0,\n Math.min(chunk.length, MAX_PEEK_BYTES)\n );\n const firstLine = str.split('\\r\\n')[0] || str.split('\\n')[0];\n const match = firstLine?.match(HTTP_METHODS);\n if (match) {\n log.raw(chalk.dim(` \u2190 ${match[1].toUpperCase()} ${match[2]}`));\n return true;\n }\n } catch {\n // Never let logging break the data path\n }\n return false;\n}\n\n/**\n * Intercept push() on a Duplex stream to peek at every data chunk\n * for HTTP request lines. This captures all requests on keep-alive\n * connections where multiple requests share a single stream.\n * Data flows through unchanged \u2014 this only observes, never modifies\n * or consumes data.\n */\nexport function monitorStream(stream: Duplex, tunnelName: string): void {\n const originalPush = stream.push.bind(stream);\n\n stream.push = function (chunk: unknown, encoding?: BufferEncoding): boolean {\n if (chunk != null) {\n try {\n const buf = Buffer.isBuffer(chunk)\n ? chunk\n : Buffer.from(chunk as string);\n parseAndLogRequestLine(buf, tunnelName);\n } catch {\n // Never interfere with data flow\n }\n }\n return originalPush(chunk, encoding);\n } as typeof stream.push;\n}\n\n/**\n * Hook into the PortForwardingService to log incoming HTTP requests.\n * Call this after SSH authentication succeeds.\n */\nexport function setupRequestMonitor(\n session: SshClientSession,\n tunnelName: string\n): void {\n const config = ConfigManager.load();\n const monitorEnabled = config.showRequestLog !== false;\n\n try {\n const pfs = session.activateService(PortForwardingService);\n pfs.onForwardedPortConnecting(e => {\n if (e.isIncoming) {\n // Guard against ObjectDisposedError when the SSH channel is disposed\n // while pipe() still has buffered data to flush. Without this handler\n // the error surfaces as an uncaught exception and crashes the process.\n // Reconnection is handled by session.onClosed/onDisconnected in\n // tunnel-manager.ts.\n e.stream.on('error', (err: Error) => {\n if (isSshChannelDisposedError(err)) {\n log.debug(\n `SSH stream closed for tunnel '${tunnelName}': ${err.message}`\n );\n } else {\n log.debug(\n `SSH stream error for tunnel '${tunnelName}': ${err.message}`\n );\n }\n });\n\n if (monitorEnabled) {\n monitorStream(e.stream, tunnelName);\n }\n }\n // Outgoing (client-initiated) streams don't pipe through the same\n // SshStream path and aren't subject to the disposal race.\n });\n log.debug(\n monitorEnabled\n ? `Request monitor active for tunnel '${tunnelName}'`\n : `Stream error handler active for tunnel '${tunnelName}' (request logging disabled)`\n );\n } catch (error) {\n log.debug(\n `Could not activate request monitor: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n}\n", "import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport * as dotenv from 'dotenv';\nimport { log } from './logger.js';\n\nexport interface PeriscopeConfig {\n serverUrl?: string;\n logLevel?: string; // ERROR, WARN, INFO, DEBUG, TRACE\n caCertPath?: string; // Path to custom CA certificate file (PEM format) for self-signed or corporate certs\n showRequestLog?: boolean; // Show live HTTP request log during tunnel sessions (default: true)\n sshKeyPath?: string; // Path to SSH private key file (default: ~/.periscope/id_ecdsa)\n allowExternalKey?: boolean; // When true, wizard offers option to use an existing key (enterprise feature, default: false)\n // Note: MSAL configuration is now fetched from the API\n}\n\nexport class ConfigManager {\n private static readonly CONFIG_DIR = path.join(os.homedir(), '.periscope');\n private static readonly CONFIG_FILE = 'config.json';\n private static testConfig: PeriscopeConfig | null = null;\n\n static getConfigPath(): string {\n return path.join(this.CONFIG_DIR, this.CONFIG_FILE);\n }\n\n /**\n * Set a test configuration that bypasses file system operations\n * Only use this in tests!\n */\n static setTestConfig(config: PeriscopeConfig | null): void {\n if (process.env.NODE_ENV !== 'test' && !process.env.VITEST) {\n throw new Error('setTestConfig can only be used in test environment');\n }\n this.testConfig = config;\n }\n\n /**\n * Load configuration from file, environment variables, and .env file\n * Priority order: environment variables > .env file > config file\n */\n static load(): PeriscopeConfig {\n // If test config is set, use it as the base instead of file\n let config: PeriscopeConfig = {};\n\n if (this.testConfig !== null) {\n // In test mode, use test config as base\n config = { ...this.testConfig };\n } else {\n // Load .env file if it exists (only in non-test mode)\n this.loadDotEnv();\n\n // Start with config file\n try {\n const configPath = this.getConfigPath();\n if (fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, 'utf-8');\n config = JSON.parse(content);\n }\n } catch (error) {\n log.debug('Failed to load config:', error);\n }\n }\n\n // Override with environment variables (always applies)\n config = this.mergeEnvironmentVariables(config);\n\n return config;\n }\n\n /**\n * Load .env file from current working directory or project root\n */\n private static loadDotEnv(): void {\n // Try loading .env from current working directory\n const cwd = process.cwd();\n const envPaths = [\n path.join(cwd, '.env'),\n path.join(cwd, '.env.local'),\n // Also try parent directories up to 3 levels\n path.join(cwd, '..', '.env'),\n path.join(cwd, '..', '..', '.env'),\n path.join(cwd, '..', '..', '..', '.env'),\n ];\n\n for (const envPath of envPaths) {\n if (fs.existsSync(envPath)) {\n dotenv.config({ path: envPath });\n break; // Use the first .env file found\n }\n }\n }\n\n /**\n * Merge environment variables into configuration\n */\n private static mergeEnvironmentVariables(\n config: PeriscopeConfig\n ): PeriscopeConfig {\n const env = process.env;\n\n // Main configuration\n if (env.PERISCOPE_SERVER_URL) {\n config.serverUrl = env.PERISCOPE_SERVER_URL;\n }\n if (env.PERISCOPE_LOG_LEVEL) {\n config.logLevel = env.PERISCOPE_LOG_LEVEL;\n }\n if (env.PERISCOPE_CA_CERT_PATH) {\n config.caCertPath = env.PERISCOPE_CA_CERT_PATH;\n }\n if (env.PERISCOPE_SHOW_REQUEST_LOG !== undefined) {\n config.showRequestLog = env.PERISCOPE_SHOW_REQUEST_LOG !== 'false';\n }\n if (env.PERISCOPE_SSH_KEY_PATH) {\n config.sshKeyPath = env.PERISCOPE_SSH_KEY_PATH;\n }\n if (env.PERISCOPE_ALLOW_EXTERNAL_KEY !== undefined) {\n config.allowExternalKey = env.PERISCOPE_ALLOW_EXTERNAL_KEY === 'true';\n }\n\n // MSAL configuration is now fetched from the API, no environment variables needed\n\n return config;\n }\n\n static save(config: PeriscopeConfig): void {\n // If in test mode, just update the test config\n if (this.testConfig !== null) {\n this.testConfig = { ...config };\n return;\n }\n\n try {\n // Ensure config directory exists\n if (!fs.existsSync(this.CONFIG_DIR)) {\n fs.mkdirSync(this.CONFIG_DIR, { recursive: true });\n }\n\n // Write config file\n const configPath = this.getConfigPath();\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2));\n\n // Set appropriate permissions (readable/writable by owner only)\n fs.chmodSync(configPath, 0o600);\n } catch (error) {\n throw new Error(\n `Failed to save config: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n}\n", "import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport { TunnelManager } from '../lib/tunnel-manager.js';\nimport { SshKeyManager, SshKeyNotFoundError } from '../lib/ssh-key-manager.js';\nimport { log } from '../lib/logger.js';\nimport { trackEvent } from '../lib/telemetry.js';\nimport {\n getReadlineInterface,\n closeReadline,\n} from '../lib/readline-instance.js';\nimport { exitOrThrow } from '../lib/interactive-utils.js';\nimport { ConfigManager } from '../lib/config-manager.js';\nimport { BaseCommand } from './base-command.js';\nimport type { PeriscopeClient } from '../lib/client.js';\n\n/** Wraps errors thrown during SSH key generation or registration. */\nclass SshKeySetupError extends Error {\n constructor(public override readonly cause: unknown) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n super(`SSH key setup failed: ${msg}`);\n this.name = 'SshKeySetupError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class TunnelCommand extends BaseCommand {\n /** Action constants for type-safe error handling */\n static readonly Action = {\n CONNECT: 'connect to tunnel',\n KEY_SETUP: 'set up SSH key',\n } as const;\n\n /**\n * Wizard invoked when no local SSH key is found.\n * By default, auto-generates a new key pair without prompting. When\n * allowExternalKey is true (future enterprise feature), presents a menu\n * that also allows providing a path to an existing key.\n *\n * Returns the resolved key path to use for the retry, or null if the user\n * cancels. Throws SshKeySetupError if key generation or registration fails.\n */\n private static async handleMissingKeyWizard(\n client: PeriscopeClient,\n customKeyPath?: string\n ): Promise<string | null> {\n if (!process.stdin.isTTY) {\n log.error(\n 'No SSH key found. Run `periscope user key generate` to generate and register one, ' +\n 'or provide an existing key path with --key.'\n );\n return null; // non-interactive; can't prompt user\n }\n\n const defaultPath = SshKeyManager.getDefaultKeyPath();\n const config = ConfigManager.load();\n\n // When allowExternalKey is enabled (enterprise), show the full menu.\n // Otherwise auto-generate without prompting.\n if (config.allowExternalKey) {\n return this.handleMissingKeyMenu(client, customKeyPath, defaultPath);\n }\n\n // Default path: auto-generate key without prompting\n const targetPath = customKeyPath ?? defaultPath;\n log.blank();\n log.info(`No SSH key found. Generating new key at ${targetPath}...`);\n try {\n const keyPair = await SshKeyManager.generateKeyPair(targetPath);\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n await client.registerPublicKey(publicKey);\n } catch (err) {\n throw new SshKeySetupError(err);\n }\n log.success('SSH key generated and registered successfully.');\n if (targetPath !== defaultPath) {\n config.sshKeyPath = targetPath;\n ConfigManager.save(config);\n log.info(`Key path saved to configuration.`);\n }\n return targetPath;\n }\n\n /**\n * Full menu wizard for missing SSH key. Only shown when allowExternalKey\n * is enabled in configuration. Allows generating a new key or providing\n * a path to an existing one.\n */\n private static async handleMissingKeyMenu(\n client: PeriscopeClient,\n customKeyPath: string | undefined,\n defaultPath: string\n ): Promise<string | null> {\n log.blank();\n log.warn('No SSH key found for tunnel authentication.');\n log.blank();\n log.info('Options:');\n log.info(\n ` 1. Generate a new SSH key at ${customKeyPath ?? defaultPath} (recommended)`\n );\n log.info(' 2. Use an existing SSH key from a different path');\n log.info(' 3. Cancel');\n\n const rl = getReadlineInterface();\n const choice = await new Promise<string>(resolve => {\n rl.question('Enter your choice [default: 1]: ', answer => {\n resolve(answer.trim() || '1');\n });\n });\n\n if (choice === '1') {\n const targetPath = customKeyPath ?? defaultPath;\n log.info(`Generating SSH key at ${targetPath}...`);\n try {\n const keyPair = await SshKeyManager.generateKeyPair(targetPath);\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n await client.registerPublicKey(publicKey);\n } catch (err) {\n throw new SshKeySetupError(err);\n }\n log.success('SSH key generated and registered successfully.');\n if (targetPath !== defaultPath) {\n const config = ConfigManager.load();\n config.sshKeyPath = targetPath;\n ConfigManager.save(config);\n log.info(`Key path saved to configuration.`);\n }\n return targetPath;\n } else if (choice === '2') {\n const existingPath = await new Promise<string>(resolve => {\n rl.question('Enter the full path to your SSH private key: ', answer => {\n resolve(answer.trim());\n });\n });\n if (!existingPath) {\n log.error('No path provided.');\n return null;\n }\n const expandedPath = existingPath.replace(/^~(?=\\/|$)/, os.homedir());\n if (!fs.existsSync(expandedPath)) {\n log.error(`No key file found at ${expandedPath}.`);\n return null;\n }\n log.info(`Registering public key from ${expandedPath}...`);\n try {\n const publicKey = await SshKeyManager.getPublicKeyString(expandedPath);\n await client.registerPublicKey(publicKey);\n } catch (err) {\n throw new SshKeySetupError(err);\n }\n log.success('SSH key registered successfully.');\n const config = ConfigManager.load();\n config.sshKeyPath = expandedPath;\n ConfigManager.save(config);\n log.info(`Key path saved to configuration.`);\n return expandedPath;\n } else if (choice === '3') {\n log.info('Cancelled.');\n return null;\n } else {\n log.error(`Invalid choice: \"${choice}\". Please enter 1, 2, or 3.`);\n return null;\n }\n }\n\n static parseTarget(target: string): {\n port: number;\n localHost?: string;\n localScheme?: string;\n } {\n let url: URL;\n try {\n url = new URL(target);\n } catch {\n throw new Error(\n `Invalid --target \"${target}\". Expected a URL like http://localhost:3000 or https://myapp.local:8443`\n );\n }\n if (url.pathname !== '/' || url.search !== '') {\n throw new Error(\n `--target must not include a path or query string (e.g., use \"http://localhost:3000\" not \"${target}\")`\n );\n }\n const scheme = url.protocol.slice(0, -1); // strip trailing ':'\n if (scheme !== 'http' && scheme !== 'https') {\n throw new Error(\n `Unsupported scheme \"${scheme}\" in --target. Only \"http\" and \"https\" are currently supported.`\n );\n }\n const hostname = url.hostname;\n const port = url.port\n ? parseInt(url.port, 10)\n : scheme === 'https'\n ? 443\n : 80;\n // Only send localScheme when it's https (http is the server default)\n const localScheme = scheme === 'https' ? 'https' : undefined;\n // Only set localHost when not loopback \u2014 server already defaults to localhost:{port}.\n // URL.hostname preserves IPv6 brackets: new URL('http://[::1]:3000').hostname === '[::1]'.\n const isLoopback =\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '[::1]';\n let localHost: string | undefined;\n if (!isLoopback) {\n const isStandardPort =\n (scheme === 'http' && port === 80) ||\n (scheme === 'https' && port === 443);\n localHost = isStandardPort ? hostname : `${hostname}:${port}`;\n }\n return { port, localHost, localScheme };\n }\n\n static async connect(name: string, target?: string, sshKeyPath?: string) {\n const { port, localHost, localScheme } = target\n ? this.parseTarget(target)\n : { port: 80, localHost: undefined, localScheme: undefined };\n\n log.info(`Establishing tunnel '${name}' for local port ${port}...`);\n if (localHost) {\n log.info(`Host header override: ${localHost}`);\n }\n\n try {\n const { client } = await this.setupClient(\n `Establishing tunnel '${name}'...`\n );\n const tunnelManager = new TunnelManager(client);\n\n let resolvedKeyPath = sshKeyPath;\n try {\n // Establish SSH tunnel; pass sshKeyPath so --key overrides the configured\n // path for this session without mutating the shared config object.\n await tunnelManager.connect(\n name,\n port,\n localHost,\n localScheme,\n resolvedKeyPath\n );\n } catch (innerError) {\n if (innerError instanceof SshKeyNotFoundError) {\n // No local key found \u2014 run wizard to generate or locate one, then retry.\n const wizardResult = await this.handleMissingKeyWizard(\n client,\n sshKeyPath\n );\n if (wizardResult === null) {\n closeReadline();\n exitOrThrow(1);\n }\n resolvedKeyPath = wizardResult;\n // Release stdin before the retry so the SSH library can attach its own\n // listeners without the dangling readline instance intercepting input.\n // If this retry also throws SshKeyNotFoundError (e.g. key unreadable\n // after generation), let it propagate to the outer catch as a normal\n // connect error. Re-entering the wizard here would risk an infinite loop.\n closeReadline();\n await tunnelManager.connect(\n name,\n port,\n localHost,\n localScheme,\n resolvedKeyPath\n );\n } else {\n throw innerError;\n }\n }\n\n trackEvent('tunnel_connect', {\n tunnelName: name,\n localPort: port.toString(),\n });\n\n // Keep process alive to maintain SSH connection until terminated.\n // Shutdown is handled by setupSecureCleanup() which calls stopAll()\n // on all registered TunnelManagers when SIGINT/SIGTERM is received.\n const keepAlive = setInterval(() => {}, 1000 * 60 * 60);\n process.once('exit', () => clearInterval(keepAlive));\n\n // Block until the process is about to exit (signal handlers call process.exit)\n await new Promise<void>(() => {});\n } catch (error) {\n if (error instanceof SshKeySetupError) {\n closeReadline();\n this.handleError(TunnelCommand.Action.KEY_SETUP, error.cause);\n } else {\n this.handleError(TunnelCommand.Action.CONNECT, error);\n }\n }\n }\n}\n", "import { PeriscopeClient, AccountStatus } from '../lib/client.js';\nimport { ConfigManager } from '../lib/config-manager.js';\nimport { exitOrThrow } from '../lib/interactive-utils.js';\nimport { log } from '../lib/logger.js';\nimport { PromptValue } from '../lib/auth-types.js';\nimport { getReadlineInterface } from '../lib/readline-instance.js';\nimport { ErrorClassifier } from '../lib/error-classifier.js';\nimport { TERMS_VERSION, TERMS_TEXT } from '../lib/terms.js';\nimport {\n initTelemetry,\n trackEvent,\n trackException,\n setUserContext,\n} from '../lib/telemetry.js';\n\nexport abstract class BaseCommand {\n /**\n * Common error handler for all commands\n * Uses ErrorClassifier for structured error handling based on HTTP status codes and error patterns\n * @param action - Action constant from the command's Actions object (e.g., TunnelCommand.Action.CREATE)\n * @param error - The error to handle\n */\n protected static handleError(action: string, error: unknown): never {\n log.debug(`handleError(${action}):`, error);\n\n const classified = ErrorClassifier.classify(error, action);\n\n log.debug(`Classified as: ${classified.type}`);\n\n trackException(error, {\n action,\n errorType: classified.type,\n userMessage: classified.userMessage,\n });\n\n // Display the user-friendly message from the classifier\n log.error(classified.userMessage);\n\n // Show troubleshooting tips for actionable errors\n if (classified.suggestedActions.length > 0) {\n log.blank();\n log.info('Troubleshooting:');\n classified.suggestedActions.forEach(tip => log.info(` \u2022 ${tip}`));\n }\n\n exitOrThrow(1);\n }\n\n /**\n * Display a warning that the account is pending approval.\n */\n protected static warnPendingApproval(): void {\n log.warn('Your account is pending approval by an administrator.');\n log.warn('You will be notified when your account has been approved.');\n log.warn('Most commands will be unavailable until your account is active.');\n }\n\n /**\n * Core authentication logic.\n * Device code auth is hidden behind PERISCOPE_ENABLE_DEVICE_CODE=true for internal use.\n * Returns the account status string if available.\n */\n static async authenticateWithChoice(\n client: PeriscopeClient,\n prompt: PromptValue = 'select_account'\n ): Promise<{ status: string | null; method: string }> {\n log.info('Authentication required...');\n\n const deviceCodeEnabled =\n process.env.PERISCOPE_ENABLE_DEVICE_CODE === 'true';\n let useBrowser = true;\n\n if (deviceCodeEnabled) {\n const rl = getReadlineInterface();\n useBrowser = await new Promise<boolean>((resolve, reject) => {\n log.blank();\n log.info('Choose authentication method:');\n log.info('1. Browser authentication (recommended)');\n log.info('2. Device code authentication');\n try {\n rl.question('Enter your choice (1 or 2) [default: 1]: ', answer =>\n resolve((answer.trim() || '1') === '1')\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n log.info(\n useBrowser\n ? `Starting browser authentication (${prompt})...`\n : 'Starting device code authentication...'\n );\n\n const authResult = useBrowser\n ? await client.authenticateInteractive(prompt)\n : await client.authenticate();\n\n log.success('Authentication successful!');\n log.success('Successfully authenticated');\n log.info(` Token expires: ${authResult.expiresOn.toLocaleString()}`);\n log.info(\n useBrowser\n ? ` Authentication method: Browser (${prompt})`\n : ' Authentication method: Device Code'\n );\n\n const method = useBrowser ? 'browser' : 'device_code';\n\n // Check account status after authentication (best-effort \u2014 don't crash on failure)\n try {\n const status = await client.getUserStatus();\n if (status === AccountStatus.PENDING_APPROVAL) {\n log.blank();\n this.warnPendingApproval();\n }\n return { status, method };\n } catch {\n log.debug('Could not determine account status after authentication');\n return { status: null, method };\n }\n }\n\n /**\n * Load config, validate server URL, init telemetry, and create client.\n * Shared entry point for all command paths that need a PeriscopeClient.\n * Accepts an optional pre-loaded config to avoid double-loading.\n */\n protected static async initClient(\n existingConfig?: ReturnType<typeof ConfigManager.load>\n ): Promise<{\n client: PeriscopeClient;\n config: ReturnType<typeof ConfigManager.load>;\n }> {\n const config = existingConfig ?? ConfigManager.load();\n\n if (!config.serverUrl) {\n log.error(\n 'Server URL not configured. Run: periscope config set --server <url>'\n );\n exitOrThrow(1);\n }\n\n return { client: new PeriscopeClient(config), config };\n }\n\n /**\n * Check client version compatibility with the server.\n * Blocks on incompatible, warns on outdated, silent on compatible/null.\n */\n private static async checkVersionCompatibility(\n serverUrl: string\n ): Promise<void> {\n try {\n // Get package version\n const packageJson = await import('../../package.json', {\n with: { type: 'json' },\n });\n const clientVersion = packageJson.default.version;\n\n // Fetch server config with clientVersion parameter\n const response = await fetch(\n `${serverUrl}/api/configuration?clientVersion=${encodeURIComponent(clientVersion)}`\n );\n\n if (!response.ok) {\n // Fail-open: if endpoint doesn't exist or errors, proceed silently\n return;\n }\n\n const config = (await response.json()) as {\n compatibilityStatus?: string | null;\n compatibilityMessage?: string | null;\n minimumVersion?: string | null;\n recommendedVersion?: string | null;\n latestVersion?: string | null;\n };\n\n // Check compatibility status\n if (config.compatibilityStatus === 'incompatible') {\n log.blank();\n log.error('CLIENT VERSION INCOMPATIBLE');\n log.blank();\n log.error(\n config.compatibilityMessage ||\n `Your client version (${clientVersion}) is incompatible with this server.`\n );\n log.error(`Minimum required version: ${config.minimumVersion}`);\n log.error(`Latest version: ${config.latestVersion}`);\n log.blank();\n log.error('Please upgrade:');\n log.error(' npm update -g @elf5/periscope');\n exitOrThrow(1);\n } else if (config.compatibilityStatus === 'outdated') {\n log.blank();\n log.warn(\n config.compatibilityMessage ||\n `Your client version (${clientVersion}) is outdated.`\n );\n log.warn(`Recommended version: ${config.recommendedVersion}`);\n log.warn(`Latest version: ${config.latestVersion}`);\n log.warn('Consider upgrading: npm update -g @elf5/periscope');\n log.blank();\n }\n // 'compatible' or null: proceed silently\n } catch (error) {\n // Fail-open: network errors or missing endpoint don't block the CLI\n log.debug('Version compatibility check failed:', error);\n }\n }\n\n /**\n * Common setup for all commands - handles config loading and authentication\n */\n static async setupClient(actionDescription?: string): Promise<{\n client: PeriscopeClient;\n config: ReturnType<typeof ConfigManager.load>;\n }> {\n const { client, config } = await this.initClient();\n\n // Check version compatibility (fail-open: errors don't block)\n await this.checkVersionCompatibility(config.serverUrl!);\n\n // Check authentication; authenticateWithChoice returns the account status and auth method\n let status: string | null = null;\n let authMethod: string | null = null;\n if (!(await client.isAuthenticated())) {\n ({ status, method: authMethod } =\n await this.authenticateWithChoice(client));\n if (actionDescription) {\n log.info(actionDescription);\n }\n }\n\n // Fetch user (and derive status) if not already known (fail-closed: let errors propagate)\n let cachedUser: Awaited<ReturnType<typeof client.getCurrentUser>> | null =\n null;\n if (status === null) {\n cachedUser = await client.getCurrentUser();\n if (!cachedUser.status) {\n throw new Error('Server did not return account status');\n }\n status = cachedUser.status;\n } else {\n // status came from authenticateWithChoice; fetch user so telemetry can be enriched\n try {\n cachedUser = await client.getCurrentUser();\n } catch {\n // non-critical\n }\n }\n\n // Initialize telemetry and enrich with user context (best-effort, post-auth)\n try {\n const cs = await client.getTelemetryConnectionString();\n if (cs) await initTelemetry(cs);\n setUserContext(cachedUser?.email);\n // Re-emit auth_login now that telemetry is initialized (was a no-op when fired in authenticateWithChoice)\n if (authMethod) trackEvent('auth_login', { method: authMethod });\n } catch {\n // Non-critical \u2014 telemetry works without user context\n }\n\n // Block commands for pending accounts\n if (status === AccountStatus.PENDING_APPROVAL) {\n log.blank();\n this.warnPendingApproval();\n log.blank();\n log.info('Available commands while pending:');\n log.info(\n ' periscope status Show server, auth, and SSH key status'\n );\n log.info(' periscope auth logout Sign out');\n exitOrThrow(1);\n }\n\n // Check Terms of Service acceptance\n await this.ensureTermsAccepted(client);\n\n return { client, config };\n }\n\n /**\n * Check if user has accepted the current Terms of Service.\n * If not, display embedded TOS and prompt for acceptance.\n */\n private static async ensureTermsAccepted(\n client: PeriscopeClient\n ): Promise<void> {\n try {\n const termsStatus = await client.getTermsStatus();\n\n if (termsStatus.accepted) {\n return; // Already accepted current version\n }\n\n // Display TOS and prompt\n log.blank();\n log.info('Welcome to Periscope!');\n log.blank();\n log.info(\n 'Before you get started, please review and accept our Terms of Service.'\n );\n log.blank();\n console.log(TERMS_TEXT);\n log.blank();\n\n const accepted = await this.promptTermsAcceptance();\n\n if (!accepted) {\n log.error('You must accept the Terms of Service to use Periscope.');\n exitOrThrow(1);\n }\n\n // Record acceptance on server\n await client.acceptTerms(TERMS_VERSION);\n log.success('Terms of Service accepted.');\n log.blank();\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n // Skip gracefully when terms can't be checked:\n // - Server doesn't support terms endpoints yet (404)\n // - Auth manager not initialized (cached-token-only mode)\n if (\n errorMessage.includes('404') ||\n errorMessage.includes('Auth manager not initialized')\n ) {\n return;\n }\n throw error;\n }\n }\n\n /**\n * Prompt user to accept or decline Terms of Service.\n */\n private static async promptTermsAcceptance(): Promise<boolean> {\n const rl = getReadlineInterface();\n return new Promise(resolve => {\n rl.question('Do you accept the Terms of Service? [y/N] ', answer => {\n resolve(answer.trim().toLowerCase() === 'y');\n });\n });\n }\n}\n", "/**\n * Embedded Terms of Service for Periscope CLI.\n * This is the beta version - a simplified TOS displayed inline in the terminal.\n * See ELF-139 for future: hosted TOS pages, versioning/re-acceptance, etc.\n */\n\nexport const TERMS_VERSION = '2026-02-14';\n\nexport const TERMS_TEXT = `\nPERISCOPE TERMS OF SERVICE\nVersion: ${TERMS_VERSION}\n\nCopyright (c) 2024-2026 Elf 5. All rights reserved.\n\nBy using Periscope (\"the Service\"), you agree to the following terms:\n\n1. ACCEPTANCE\n By accessing or using Periscope, you agree to be bound by these Terms\n of Service. If you do not agree, you may not use the Service.\n\n2. BETA PROGRAM\n IMPORTANT: Periscope is currently in beta. By participating, you acknowledge:\n - Beta products may contain bugs, errors, or incomplete features\n - We may collect feedback and usage data to improve our products\n - Beta access is provided \"as is\" without warranties of any kind\n - We reserve the right to modify or terminate the beta at any time without notice\n - The service may experience downtime, interruptions, or changes during the beta period\n\n3. SERVICE DESCRIPTION\n Periscope provides secure SSH tunnel services that allow you to expose\n local development services through publicly accessible URLs.\n\n4. ACCOUNT SECURITY\n Periscope uses email-based passcode authentication. You are responsible for:\n - Maintaining the security of the email address associated with your account\n - Not sharing authentication passcodes sent to your email\n - Promptly notifying us if you receive unexpected passcodes or suspect unauthorized access\n - Ensuring your email address remains current and accessible\n - All activities that occur using passcodes sent to your email address\n\n We are not responsible for unauthorized access resulting from compromise of your\n email account, sharing of passcodes, or use of an insecure or shared email address.\n\n5. ACCEPTABLE USE\n You agree to use Periscope only for lawful purposes. You shall not:\n - Use the Service to transmit harmful, illegal, or offensive content\n - Violate any applicable laws or regulations\n - Infringe upon the rights of others\n - Distribute malicious software or engage in harmful activities\n - Attempt to gain unauthorized access to other systems through tunnels\n - Use the Service to circumvent network security policies\n - Share your credentials or tunnel access with unauthorized parties\n - Interfere with the proper functioning of our services\n\n6. DATA AND PRIVACY\n - Tunnel traffic passes through Periscope infrastructure\n - We collect minimal usage data (tunnel names, connection times, user identity)\n - We do not inspect or store the content of your tunnel traffic\n - Your authentication data is managed by your identity provider\n - Your privacy is important to us. Our collection and use of personal information\n is governed by our Privacy Policy\n\n Data Access and Disclosure:\n We reserve the right to access, preserve, and disclose your account information\n and data if required by law or if we believe such action is necessary to:\n (a) comply with legal process or government requests; (b) enforce these Terms;\n (c) respond to claims of violation of third-party rights; or (d) protect the\n rights, property, or safety of Elf 5, our users, or the public.\n\n7. DISCLAIMERS AND LIMITATIONS\n Disclaimer of Warranties:\n Our services are provided \"as is\" and \"as available\" without warranties of any kind,\n either express or implied, including but not limited to warranties of merchantability,\n fitness for a particular purpose, or non-infringement.\n\n Limitation of Liability:\n To the maximum extent permitted by law, Elf 5 shall not be liable for any indirect,\n incidental, special, consequential, or punitive damages, or any loss of profits or\n revenues, whether arising from:\n - Your use or inability to use our services\n - Services, applications, or data you expose through Periscope tunnels\n - Unauthorized access to your tunnels or services due to your configuration choices\n - Security vulnerabilities in services you make accessible through our platform\n - Data breaches or exposure of sensitive information through tunnels you create\n - Any actions taken by third parties who access services through your tunnels\n\n User Responsibility:\n You are solely responsible for the security, configuration, and content of any\n services you expose through Periscope. You acknowledge that creating publicly\n accessible tunnels to local services carries inherent security risks, and you\n assume all such risks.\n\n Liability Cap:\n In no event shall Elf 5's total aggregate liability exceed the greater of:\n (i) $100 USD or (ii) the total fees paid by you to Elf 5 in the twelve (12)\n months immediately preceding the claim.\n\n8. TERMINATION\n We may terminate or suspend your access to the Service at any time, with or\n without cause or notice. Upon termination, your right to use our services will\n cease immediately.\n\n9. CHANGES TO TERMS\n We reserve the right to modify these Terms at any time. We will notify you of\n any changes by updating the \"Version\" date. Your continued use of our services\n after such modifications constitutes acceptance of the updated Terms.\n\nFor questions, contact: hello@elf5.com\n`;\n", "import { ConfigManager } from '../lib/config-manager.js';\nimport { log } from '../lib/logger.js';\nimport { exitOrThrow } from '../lib/interactive-utils.js';\nimport { BaseCommand } from './base-command.js';\n\nexport class ConfigCommand extends BaseCommand {\n /** Action constants for type-safe error handling */\n static readonly Action = {\n SET: 'update configuration',\n SHOW: 'load configuration',\n } as const;\n\n static async set(options: { server?: string; requestLog?: string }) {\n try {\n const config = ConfigManager.load();\n let hasChanges = false;\n\n // Check if any options were provided\n if (!options.server && options.requestLog === undefined) {\n log.warn('No configuration options provided.');\n log.header('Available configuration options:');\n log.info(' --server <url> Set the Periscope server URL');\n log.info(\n ' --request-log <bool> Show live HTTP request log during tunnel sessions (true/false)'\n );\n log.header('Examples:');\n log.info(' periscope config set --server https://periscope.elf5.com');\n log.info(' periscope config set --request-log false');\n log.blank();\n log.info('To view current configuration, use: periscope config show');\n exitOrThrow(1);\n }\n\n if (options.server) {\n // Validate URL\n try {\n new URL(options.server);\n config.serverUrl = options.server;\n log.success(`Server URL set to: ${options.server}`);\n hasChanges = true;\n } catch {\n // URL validation is a simple user input error - keep message simple\n log.error('Invalid server URL format');\n exitOrThrow(1);\n }\n }\n\n if (options.requestLog !== undefined) {\n config.showRequestLog = options.requestLog !== 'false';\n log.success(`Request log set to: ${config.showRequestLog}`);\n hasChanges = true;\n }\n\n if (hasChanges) {\n ConfigManager.save(config);\n log.info('Configuration saved successfully');\n }\n } catch (error) {\n this.handleError(ConfigCommand.Action.SET, error);\n }\n }\n\n static async show() {\n try {\n const config = ConfigManager.load();\n\n log.header('Periscope Configuration:');\n log.separator(50);\n\n // Main configuration\n ConfigCommand.showConfigValue(\n 'Server URL ',\n config.serverUrl,\n 'PERISCOPE_SERVER_URL'\n );\n ConfigCommand.showConfigValue(\n 'Request Log ',\n String(config.showRequestLog ?? true),\n 'PERISCOPE_SHOW_REQUEST_LOG'\n );\n\n log.info('Config file: ' + ConfigManager.getConfigPath());\n log.separator(50);\n } catch (error) {\n this.handleError(ConfigCommand.Action.SHOW, error);\n }\n }\n\n private static showConfigValue(\n label: string,\n value: string | undefined,\n envVar: string\n ): void {\n const envValue = process.env[envVar];\n const isFromEnv = envValue !== undefined && envValue !== '';\n const displayValue = value || 'Not configured';\n const source = isFromEnv ? ' (from env)' : '';\n\n log.info(`${label}: ${displayValue}${source}`);\n }\n}\n", "import { AccountStatus } from '../lib/client.js';\nimport { ConfigManager } from '../lib/config-manager.js';\nimport { log } from '../lib/logger.js';\nimport { getServerConfig } from '../lib/server-config.js';\nimport { SshKeyManager } from '../lib/ssh-key-manager.js';\nimport { trackEvent } from '../lib/telemetry.js';\nimport { BaseCommand } from './base-command.js';\n\nexport class StatusCommand extends BaseCommand {\n static readonly Action = {\n CHECK: 'check status',\n } as const;\n\n static async check() {\n try {\n const config = ConfigManager.load();\n\n log.header('Periscope Status:');\n log.separator();\n\n if (!config.serverUrl) {\n log.info('Server URL: Not configured');\n log.info('Run: periscope config set --server <server-url>');\n log.separator();\n return;\n }\n\n log.info('Server URL: ' + config.serverUrl);\n\n const { client } = await this.initClient(config);\n\n // Server health\n try {\n const health = await client.checkHealth();\n log.info(\n 'Server: ' + (health.healthy ? '\u25CF Healthy' : '\u25CF Unhealthy')\n );\n if (health.version) {\n log.info('Server Version: ' + health.version);\n }\n } catch {\n log.info('Server: \u25CF Unreachable');\n }\n\n // Client version\n const packageJson = await import('../../package.json', {\n with: { type: 'json' },\n });\n log.info('Client Version: ' + packageJson.default.version);\n\n // Auth provider\n try {\n const serverConfig = await getServerConfig(config.serverUrl);\n log.info('Auth Provider: ' + (serverConfig.authProvider || 'Default'));\n } catch {\n // Skip if server unreachable\n }\n\n const isAuthenticated = await client.isAuthenticated();\n log.info(\n 'Auth: ' +\n (isAuthenticated ? '\u25CF Authenticated' : '\u25CF Not authenticated')\n );\n\n // SSH key local status \u2014 shown regardless of auth state\n const keyInfo = SshKeyManager.getKeyInfo(config.sshKeyPath);\n log.info(\n 'SSH Key: ' +\n (keyInfo.exists ? `\u25CF Present (${keyInfo.path})` : '\u25CF Not generated')\n );\n if (!keyInfo.exists) {\n log.info('Run: periscope user key generate');\n }\n\n if (isAuthenticated) {\n // Account status\n let isPendingApproval = false;\n try {\n const accountStatus = await client.getUserStatus();\n if (accountStatus) {\n isPendingApproval =\n accountStatus === AccountStatus.PENDING_APPROVAL;\n const statusDisplay = isPendingApproval\n ? '\u23F3 Pending Approval'\n : accountStatus === AccountStatus.ACTIVE\n ? '\u25CF Active'\n : accountStatus;\n log.info('Account: ' + statusDisplay);\n }\n } catch {\n // Skip on older server versions\n }\n\n // Slug, tunnel format, and server-side key metadata\n try {\n const sshCreds = await client.getSSHCredentials();\n if (sshCreds.slug) {\n log.info('User Slug: ' + sshCreds.slug);\n log.info(\n 'Tunnel Format: {name}-' +\n sshCreds.slug +\n '.' +\n (sshCreds.wildcardHostname || 'domain')\n );\n }\n if (keyInfo.exists && sshCreds.keyGeneratedAt) {\n const registeredDate = new Date(sshCreds.keyGeneratedAt);\n if (!isNaN(registeredDate.getTime())) {\n log.info('Key Registered: ' + registeredDate.toLocaleString());\n }\n }\n } catch {\n // Skip if SSH creds unavailable (older server or pending approval)\n }\n\n if (isPendingApproval) {\n log.blank();\n log.warn('Your account is pending approval by an administrator.');\n log.warn(\n 'Most commands will be unavailable until your account is active.'\n );\n }\n } else {\n log.info('Run: periscope auth login');\n }\n\n log.info(\n 'Request Log: ' +\n (config.showRequestLog !== false ? '\u25CF Enabled' : '\u25CB Disabled')\n );\n trackEvent('status_check', { authenticated: isAuthenticated.toString() });\n log.separator();\n } catch (error) {\n this.handleError(StatusCommand.Action.CHECK, error);\n }\n }\n}\n", "import { ConfigManager } from '../lib/config-manager.js';\nimport { PromptValue } from '../lib/auth-types.js';\nimport { log } from '../lib/logger.js';\nimport { SshKeyManager } from '../lib/ssh-key-manager.js';\nimport { trackEvent } from '../lib/telemetry.js';\nimport { BaseCommand } from './base-command.js';\n\nexport class AuthCommand extends BaseCommand {\n /** Action constants for type-safe error handling */\n static readonly Action = {\n LOGIN: 'authenticate',\n LOGOUT: 'logout',\n } as const;\n\n static async login(\n options: {\n prompt?: PromptValue;\n } = {}\n ) {\n log.info('Initializing authentication...');\n\n try {\n const { client, config } = await this.initClient();\n\n // Force authentication even if already authenticated\n log.debug('Starting authentication...');\n await this.authenticateWithChoice(client, options.prompt);\n log.debug('Authentication complete');\n\n // Ensure SSH key pair exists (ELF-184): generate if first login, reuse if already present.\n // Register the public key with the server so the server can authenticate SSH connections.\n // SSH key setup is fatal: a missing or unregistered key means tunnel connections\n // will fail with a cryptic SSH auth rejection. Better to surface the error now.\n // Export directly from the returned KeyPair \u2014 avoids a redundant disk read and\n // a TOCTOU window where the .pub file could be replaced between generation and read.\n log.debug('Ensuring SSH key pair...');\n const keyPair = await SshKeyManager.ensureKeyPair(config.sshKeyPath);\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n log.debug('Registering SSH public key with server...');\n await client.registerPublicKey(publicKey);\n log.success('SSH key registered with server.');\n } catch (error) {\n log.debug('Login failed with error:', error);\n this.handleError(AuthCommand.Action.LOGIN, error);\n }\n }\n\n static async logout() {\n log.info('Clearing authentication data...');\n\n try {\n const config = ConfigManager.load();\n\n if (!config.serverUrl) {\n log.warn('No server URL configured - nothing to clear');\n return;\n }\n\n const { client } = await this.initClient(config);\n await client.logout();\n\n log.success('Logged out successfully');\n log.success('Authentication data cleared');\n trackEvent('auth_logout');\n } catch (error) {\n this.handleError(AuthCommand.Action.LOGOUT, error);\n }\n }\n}\n", "import { log } from '../lib/logger.js';\nimport { trackEvent } from '../lib/telemetry.js';\nimport { SshKeyManager } from '../lib/ssh-key-manager.js';\nimport { BaseCommand } from './base-command.js';\n\nexport class UserCommand extends BaseCommand {\n /** Action constants for type-safe error handling */\n static readonly Action = {\n KEY_GENERATE: 'generate SSH key',\n UPDATE_SLUG: 'update user slug',\n } as const;\n\n /**\n * Generate a new SSH key pair and register it with the server.\n * Overwrites any existing key at the configured path.\n */\n static async keyGenerate() {\n try {\n const { client, config } = await this.setupClient(\n 'Generating SSH key...'\n );\n\n log.info('Generating new ECDSA P-256 SSH key pair...');\n // Export directly from the returned KeyPair \u2014 avoids a redundant disk read.\n const keyPair = await SshKeyManager.generateKeyPair(config.sshKeyPath);\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n await client.registerPublicKey(publicKey);\n\n const keyInfo = SshKeyManager.getKeyInfo(config.sshKeyPath);\n log.success('SSH key generated and registered.');\n log.info('Private key:', keyInfo.path);\n log.info('Public key:', keyInfo.pubKeyPath);\n\n trackEvent('user_key_generate');\n } catch (error) {\n this.handleError(UserCommand.Action.KEY_GENERATE, error);\n }\n }\n\n /**\n * Update the user's slug for tunnel namespacing (delegates to server update).\n */\n static async updateSlug(newSlug: string) {\n if (!/^[a-zA-Z]{6}$/.test(newSlug)) {\n log.error('Invalid slug format');\n log.info('Slug must be exactly 6 alphabetic characters (a-z, A-Z)');\n log.info('Example: periscope user slug myslug');\n return;\n }\n\n try {\n const { client } = await this.setupClient();\n\n const normalizedSlug = newSlug.toLowerCase();\n log.info(`Updating user slug to: ${normalizedSlug}`);\n await client.updateSlug({ slug: normalizedSlug });\n\n log.success('Slug updated successfully!');\n log.info(`Your tunnels will now use: {name}-${normalizedSlug}.{domain}`);\n\n trackEvent('auth_update_slug');\n } catch (error) {\n this.handleError(UserCommand.Action.UPDATE_SLUG, error);\n }\n }\n}\n", "import readline from 'readline';\nimport { TunnelCommand } from './commands/tunnel.js';\nimport { ConfigCommand } from './commands/config.js';\nimport { StatusCommand } from './commands/status.js';\nimport { AuthCommand } from './commands/auth.js';\nimport { UserCommand } from './commands/user.js';\n\nimport { log } from './lib/logger.js';\nimport { performSecureCleanup } from './lib/secure-memory.js';\nimport {\n initializeReadline,\n closeReadline,\n getReadlineInterface,\n} from './lib/readline-instance.js';\nimport { gracefulExit } from './lib/process-lifecycle.js';\n\ninterface ConfigOptions {\n server?: string;\n requestLog?: string;\n}\n\nexport class InteractiveMode {\n private static instance: InteractiveMode | null = null;\n\n constructor() {\n InteractiveMode.instance = this;\n\n // Initialize the shared readline instance for interactive mode\n initializeReadline(true, line => this.tabCompleter(line));\n\n this.setupLineHandler();\n this.setupSignalHandling();\n }\n\n private get rl(): readline.Interface {\n return getReadlineInterface();\n }\n\n private setupLineHandler() {\n this.rl.on('line', line => this.handleCommand(line.trim()));\n }\n\n /**\n * Tab completion for interactive mode.\n * Maps the command tree so Tab expands to the next token at each level.\n */\n private tabCompleter(line: string): [string[], string] {\n const trimmed = line.trimEnd();\n const parts = trimmed.split(' ');\n\n const topLevel = [\n 'auth',\n 'config',\n 'connect',\n 'status',\n 'user',\n 'help',\n 'clear',\n 'exit',\n 'quit',\n ];\n\n let candidates: string[];\n\n if (parts.length <= 1) {\n candidates = topLevel;\n } else {\n switch (parts[0]) {\n case 'auth':\n candidates = ['auth login', 'auth logout'];\n break;\n case 'config':\n if (parts[1] === 'set' && parts.length >= 3) {\n candidates = ['config set --server', 'config set --request-log'];\n } else {\n candidates = ['config show', 'config set'];\n }\n break;\n case 'user':\n if (parts[1] === 'key') {\n candidates = ['user key generate'];\n } else {\n candidates = ['user slug', 'user key'];\n }\n break;\n default:\n return [[], line];\n }\n }\n\n const hits = candidates.filter(c => c.startsWith(trimmed));\n return [hits, line];\n }\n\n private setupSignalHandling() {\n // Handle CTRL-C in interactive mode (readline level)\n this.rl.on('SIGINT', async () => {\n log.info('Exiting interactive mode...');\n await this.close();\n await gracefulExit(0);\n });\n }\n\n async start() {\n // Set environment variable to indicate interactive mode\n process.env.PERISCOPE_INTERACTIVE = 'true';\n\n log.header('\uD83D\uDD2D Periscope Interactive CLI');\n log.info('Type \"help\" for available commands or \"exit\" to quit.');\n log.blank();\n\n this.rl.prompt();\n }\n\n private async handleCommand(input: string) {\n if (!input) {\n this.rl.prompt();\n return;\n }\n\n const [mainCommand, ...args] = input.split(' ');\n\n try {\n switch (mainCommand.toLowerCase()) {\n case 'help':\n this.showHelp();\n break;\n\n case 'exit':\n case 'quit':\n log.raw('Goodbye! \uD83D\uDC4B');\n process.exit(0);\n break;\n\n case 'auth':\n await this.handleAuthCommand(args);\n break;\n\n case 'config':\n await this.handleConfigCommand(args);\n break;\n\n case 'connect':\n await this.handleConnectCommand(args);\n break;\n\n case 'status':\n await StatusCommand.check();\n break;\n\n case 'user':\n await this.handleUserCommand(args);\n break;\n\n case 'clear':\n console.clear();\n break;\n\n default:\n log.error(`Unknown command: ${mainCommand}`);\n log.info('Type \"help\" for available commands.');\n break;\n }\n } catch (error) {\n log.error(\n `Error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n\n this.rl.prompt();\n }\n\n private showHelp() {\n log.header('Available Commands:');\n log.separator(50);\n\n log.info('Authentication:');\n log.info(' auth login - Authenticate (choose method)');\n log.info(' auth logout - Clear authentication data');\n\n log.header('User:');\n log.info(' user slug <new-slug> - Update user slug (6 letters)');\n log.info(\n ' user key generate - Generate and register a new SSH key'\n );\n\n log.header('Configuration:');\n log.info(' config show - Show current configuration');\n log.info(' config set --server <url> - Set server URL');\n log.info(\n ' config set --request-log <bool> - Show request log (true/false)'\n );\n\n log.header('Tunnels:');\n log.info(\n ' connect <name> [--target <target>] [--key <path>] - Establish SSH tunnel'\n );\n\n log.header('General:');\n log.info(\n ' status - Show server, auth, and SSH key status'\n );\n log.info(' clear - Clear the screen');\n log.info(' help - Show this help message');\n log.info(' exit, quit - Exit interactive mode');\n\n log.separator(50);\n log.info('Use Tab for command completion');\n }\n\n private async handleAuthCommand(args: string[]) {\n const [subCommand] = args;\n\n // Pause readline interface before running commands that might use ora spinner\n switch (subCommand) {\n case 'login':\n await AuthCommand.login();\n break;\n\n case 'logout':\n await AuthCommand.logout();\n break;\n\n default:\n log.error('Invalid auth command. Available: login, logout');\n break;\n }\n }\n\n private async handleUserCommand(args: string[]) {\n const [subCommand, ...options] = args;\n\n switch (subCommand) {\n case 'slug': {\n const newSlug = options[0];\n if (!newSlug) {\n log.error('Please provide a new slug.');\n log.info('Usage: user slug <new-slug>');\n break;\n }\n await UserCommand.updateSlug(newSlug);\n break;\n }\n\n case 'key': {\n const keySubCommand = options[0];\n if (keySubCommand === 'generate') {\n await UserCommand.keyGenerate();\n } else {\n log.error('Invalid key command. Available: generate');\n log.info('Usage: user key generate');\n }\n break;\n }\n\n default:\n log.error('Invalid user command. Available: slug, key');\n break;\n }\n }\n\n private async handleConfigCommand(args: string[]) {\n const [subCommand, ...options] = args;\n\n switch (subCommand) {\n case 'show':\n await ConfigCommand.show();\n break;\n\n case 'set': {\n // Parse options for config set\n const configOptions: ConfigOptions = {};\n for (let i = 0; i < options.length; i += 2) {\n const option = options[i];\n const value = options[i + 1];\n\n switch (option) {\n case '--server':\n case '-s':\n configOptions.server = value;\n break;\n case '--request-log':\n configOptions.requestLog = value;\n break;\n }\n }\n\n await ConfigCommand.set(configOptions);\n break;\n }\n\n default:\n log.error('Invalid config command. Available: show, set');\n break;\n }\n }\n\n private async handleConnectCommand(args: string[]) {\n // First argument is the tunnel name (required)\n if (args.length === 0) {\n log.error('Please specify a tunnel name.');\n log.info('Usage: connect <name> [--target <target>] [--key <path>]');\n return;\n }\n\n const name = args[0];\n let target: string | undefined;\n let sshKeyPath: string | undefined;\n\n // Parse remaining options\n for (let i = 1; i < args.length; i += 2) {\n const option = args[i];\n const value = args[i + 1];\n\n switch (option) {\n case '--target':\n target = value;\n break;\n case '--key':\n sshKeyPath = value;\n break;\n }\n }\n\n await TunnelCommand.connect(name, target, sshKeyPath);\n }\n\n /**\n * Trigger a new prompt (useful for background operations)\n */\n public static triggerPrompt(): void {\n if (InteractiveMode.instance) {\n const rl = getReadlineInterface();\n if (rl) {\n rl.prompt();\n }\n }\n }\n\n async close() {\n InteractiveMode.instance = null;\n\n // Close the shared readline instance\n closeReadline();\n\n // Ensure secure cleanup happens\n await performSecureCleanup();\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import * as readline from 'readline';\nimport chalk from 'chalk';\n\n// Module-level readline interface that can be shared across commands\nlet readlineInstance: readline.Interface | null = null;\n\nexport function getReadlineInterface(): readline.Interface {\n if (!readlineInstance) {\n // Auto-initialize if not already done\n initializeReadline();\n }\n return readlineInstance!;\n}\n\nexport function initializeReadline(\n isInteractive: boolean = false,\n completer?: readline.Completer\n): void {\n if (readlineInstance) {\n return; // Already initialized, do nothing\n }\n\n readlineInstance = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: isInteractive ? chalk.cyan('periscope> ') : undefined,\n completer,\n });\n}\n\nexport function closeReadline(): void {\n if (readlineInstance) {\n readlineInstance.close();\n readlineInstance = null;\n }\n}\n\nexport function isReadlineActive(): boolean {\n return readlineInstance !== null;\n}\n", "/**\n * Application Insights telemetry module for the Periscope CLI.\n *\n * Provides crash/exception tracking and event telemetry.\n * Graceful no-op when not initialized (no connection string configured).\n * Connection string is fetched post-auth from the server via /api/configuration/telemetry.\n */\n\nimport type { TelemetryClient } from 'applicationinsights';\nimport * as os from 'os';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\n\nlet client: TelemetryClient | null = null;\nlet initialized = false;\nlet userEmail: string | null = null;\n\nfunction getCliVersion(): string {\n try {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(\n fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8')\n );\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Initialize Application Insights telemetry.\n * Must be called with a connection string from the server configuration.\n * Safe to call multiple times \u2014 subsequent calls are ignored.\n *\n * @returns true if telemetry was initialized, false if skipped\n */\nexport async function initTelemetry(\n connectionString: string\n): Promise<boolean> {\n if (initialized) return false;\n if (!connectionString) return false;\n\n try {\n // Set cloud role name via OpenTelemetry env var (v3 SDK ignores classic context.tags)\n process.env.OTEL_SERVICE_NAME = 'periscope-cli';\n\n // Dynamic import \u2014 required because esbuild ESM output doesn't support require()\n const appInsights = await import('applicationinsights');\n\n appInsights.default\n .setup(connectionString)\n .setAutoCollectRequests(false)\n .setAutoCollectPerformance(false, false)\n .setAutoCollectExceptions(false) // We handle exceptions ourselves for proper flush-before-exit\n .setAutoCollectDependencies(true)\n .setAutoCollectConsole(false)\n .setAutoCollectPreAggregatedMetrics(false)\n .setUseDiskRetryCaching(false) // CLI is ephemeral \u2014 disk cache won't help\n .start();\n\n client = appInsights.default.defaultClient;\n\n if (client) {\n const version = getCliVersion();\n\n // Common properties attached to every telemetry item\n client.commonProperties = {\n cliVersion: version,\n nodeVersion: process.version,\n platform: os.platform(),\n arch: os.arch(),\n };\n\n initialized = true;\n return true;\n }\n } catch {\n // Telemetry init failure should never break the CLI\n client = null;\n }\n\n return false;\n}\n\n/**\n * Track an exception in Application Insights.\n * No-op if telemetry is not initialized.\n */\nexport function trackException(\n error: unknown,\n properties?: Record<string, string>\n): void {\n if (!client) return;\n\n const exception = error instanceof Error ? error : new Error(String(error));\n\n // Caller-provided 'email' in properties takes precedence over module-level userEmail\n const enriched = userEmail ? { email: userEmail, ...properties } : properties;\n\n client.trackException({\n exception,\n properties: enriched,\n });\n}\n\n/**\n * Track a custom event in Application Insights.\n * No-op if telemetry is not initialized.\n */\nexport function trackEvent(\n name: string,\n properties?: Record<string, string>\n): void {\n if (!client) return;\n\n // Caller-provided 'email' in properties takes precedence over module-level userEmail\n const enriched = userEmail ? { email: userEmail, ...properties } : properties;\n\n client.trackEvent({ name, properties: enriched });\n}\n\n/**\n * Store the authenticated user's email for enrichment of subsequent telemetry events.\n * Safe to call before or after initTelemetry.\n */\nexport function setUserContext(email?: string): void {\n if (email) {\n userEmail = email;\n }\n}\n\n/**\n * Flush all buffered telemetry to Application Insights.\n * Returns a Promise that resolves when flush completes or times out.\n * No-op (resolves immediately) if telemetry is not initialized.\n */\nexport async function flushTelemetry(): Promise<void> {\n if (!client) return;\n\n try {\n await Promise.race([\n client.flush(),\n new Promise<void>(resolve => setTimeout(resolve, 5000)),\n ]);\n } catch {\n // Best-effort \u2014 don't let flush failures prevent exit\n }\n}\n\n/**\n * Flush buffered telemetry and dispose the SDK so its background timers\n * don't keep the process alive. Call this before process exit.\n */\nexport async function shutdownTelemetry(): Promise<void> {\n await flushTelemetry();\n if (client) {\n try {\n const appInsights = await import('applicationinsights');\n appInsights.dispose();\n } catch {\n // Best-effort\n }\n client = null;\n initialized = false;\n userEmail = null;\n }\n}\n", "/**\n * Centralized process exit handling.\n *\n * All exit paths should call gracefulExit() instead of process.exit()\n * to ensure telemetry is flushed, the SDK is disposed, and readline\n * is closed before the process terminates.\n */\n\nimport { closeReadline } from './readline-instance.js';\nimport { shutdownTelemetry } from './telemetry.js';\n\n/**\n * Cleanly shut down and exit the process.\n * Flushes + disposes telemetry, closes readline, then exits.\n */\nexport async function gracefulExit(code: number = 0): Promise<never> {\n await shutdownTelemetry();\n closeReadline();\n process.exit(code);\n}\n", "{\n \"name\": \"@elf5/periscope\",\n \"version\": \"1.0.70\",\n \"description\": \"CLI client for Periscope SSH tunnel server\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"bin\": {\n \"periscope\": \"./dist/cli.js\"\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"build:tsc\": \"tsc\",\n \"dev\": \"tsc --watch\",\n \"clean\": \"rimraf dist\",\n \"prepublishOnly\": \"npm run clean && npm run build\",\n \"start\": \"node dist/cli.js\",\n \"cli\": \"npm run build && node dist/cli.js\",\n \"test\": \"npm run test:unit && npm run test:offline\",\n \"test:watch\": \"vitest --config vitest.config.unit.js\",\n \"test:ui\": \"vitest --ui --config vitest.config.unit.js\",\n \"test:unit\": \"vitest run --coverage --config vitest.config.unit.js\",\n \"test:offline\": \"vitest run --config vitest.config.integration.js\",\n \"test:e2e\": \"vitest run --config vitest.config.e2e.js\",\n \"test:ci\": \"npm run test && npm run test:e2e\",\n \"format\": \"prettier --write src/**/*.ts\",\n \"format:check\": \"prettier --check src/**/*.ts\",\n \"lint\": \"eslint src\",\n \"lint:fix\": \"eslint src --fix\",\n \"test-server\": \"npx tsx src/__tests__/e2e/test-server.ts\",\n \"prepare\": \"husky\"\n },\n \"keywords\": [\n \"ssh\",\n \"tunnel\",\n \"cli\",\n \"periscope\",\n \"port-forwarding\"\n ],\n \"author\": \"Elf 5\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"email\": \"support@elf5.com\"\n },\n \"homepage\": \"https://elf5.com\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"overrides\": {\n \"glob\": \"^11.0.0\",\n \"uuid\": \"^10.0.0\"\n },\n \"lint-staged\": {\n \"*.ts\": [\n \"prettier --write\",\n \"eslint --fix\"\n ]\n },\n \"devDependencies\": {\n \"@azure/msal-node\": \"^2.6.6\",\n \"@microsoft/dev-tunnels-connections\": \"^1.2.1\",\n \"@microsoft/dev-tunnels-contracts\": \"^1.2.1\",\n \"@microsoft/dev-tunnels-management\": \"^1.2.1\",\n \"@microsoft/dev-tunnels-ssh\": \"^3.12.5\",\n \"@microsoft/dev-tunnels-ssh-keys\": \"^3.12.5\",\n \"@microsoft/dev-tunnels-ssh-tcp\": \"^3.12.5\",\n \"applicationinsights\": \"^3.13.0\",\n \"axios\": \"^1.7.2\",\n \"chalk\": \"^5.3.0\",\n \"commander\": \"^12.0.0\",\n \"dotenv\": \"^16.4.5\",\n \"open\": \"^10.0.3\",\n \"openid-client\": \"^6.8.2\",\n \"@elf-5/periscope-api-client\": \"^1.0.129\",\n \"@types/node\": \"^20.14.0\",\n \"@typescript-eslint/eslint-plugin\": \"^8.41.0\",\n \"@typescript-eslint/parser\": \"^8.41.0\",\n \"@vitest/coverage-v8\": \"^3.2.4\",\n \"@vitest/ui\": \"^3.2.4\",\n \"esbuild\": \"^0.27.3\",\n \"eslint\": \"^9.34.0\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"globals\": \"^16.3.0\",\n \"husky\": \"^9.1.7\",\n \"lint-staged\": \"^16.2.7\",\n \"prettier\": \"^3.8.1\",\n \"rimraf\": \"^6.0.0\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.4.5\",\n \"vitest\": \"^3.2.4\",\n \"yocto-queue\": \"^1.2.1\"\n }\n}\n", "import {\n PublicClientApplication,\n LogLevel,\n AuthorizationCodeRequest,\n} from '@azure/msal-node';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as http from 'http';\nimport * as crypto from 'crypto';\nimport open from 'open';\nimport { log } from './logger.js';\nimport { getCacheDir, writeSecureFile } from './cache-utils.js';\nimport { createMsalCachePlugin, clearMsalCache } from './msal-cache-plugin.js';\nimport { listenForAuthCode } from './auth-callback-server.js';\nimport {\n isTokenExpired,\n type AuthToken,\n type IAuthManager,\n type PromptValue,\n} from './auth-types.js';\n\nexport interface MSALConfig {\n clientId: string;\n authority: string;\n scopes: string[];\n}\n\nexport class MsalAuthManager implements IAuthManager {\n private static readonly TOKEN_CACHE_FILE = 'token-cache.json';\n private msalApp: PublicClientApplication | null = null;\n private config: MSALConfig | null = null;\n\n constructor(config?: MSALConfig) {\n if (config) {\n this.config = config;\n this.initializeMSAL();\n }\n }\n\n private initializeMSAL(): void {\n if (!this.config) {\n throw new Error('MSAL configuration is required');\n }\n\n const msalConfig = {\n auth: {\n clientId: this.config.clientId,\n authority: this.config.authority,\n // B2C requires knownAuthorities to include the B2C login domain\n knownAuthorities: MsalAuthManager.extractKnownAuthorities(\n this.config.authority\n ),\n },\n cache: {\n cachePlugin: createMsalCachePlugin(),\n },\n system: {\n loggerOptions: {\n loggerCallback(loglevel: LogLevel, message: string) {\n // Only log errors in production\n if (loglevel === LogLevel.Error) {\n log.error('MSAL Error:', message);\n }\n },\n piiLoggingEnabled: false,\n logLevel: LogLevel.Error,\n },\n },\n };\n\n this.msalApp = new PublicClientApplication(msalConfig);\n }\n\n /**\n * Extract knownAuthorities from an authority URL for non-standard authority domains.\n * B2C (*.b2clogin.com) and Entra External ID (*.ciamlogin.com) must be listed in knownAuthorities.\n */\n private static extractKnownAuthorities(authority: string): string[] {\n try {\n const url = new URL(authority);\n if (\n url.hostname.includes('.b2clogin.com') ||\n url.hostname.includes('.ciamlogin.com')\n ) {\n return [url.hostname];\n }\n } catch {\n // Not a valid URL \u2014 no known authorities needed\n }\n return [];\n }\n\n /**\n * Authenticate user using device code flow\n * This will display a device code and open the browser for authentication\n */\n async authenticate(): Promise<AuthToken> {\n if (!this.msalApp || !this.config) {\n throw new Error(\n 'MSAL not initialized. Please configure authentication first.'\n );\n }\n\n try {\n // Try to get token silently first (from cache)\n const cachedToken = this.getCachedToken();\n if (cachedToken && !isTokenExpired(cachedToken.expiresOn)) {\n return cachedToken;\n }\n\n // Use device code flow for CLI authentication\n const deviceCodeRequest = {\n scopes: this.config.scopes,\n deviceCodeCallback: (response: {\n verificationUri: string;\n userCode: string;\n }) => {\n log.blank();\n log.info('To authenticate, use a web browser to open the page:');\n log.info(` ${response.verificationUri}`);\n log.blank();\n log.info('And enter the code:');\n log.info(` ${response.userCode}`);\n log.blank();\n log.info('Opening browser automatically...');\n log.blank();\n\n // Open the browser automatically\n open(response.verificationUri).catch(() => {\n log.warn(\n 'Failed to open browser automatically. Please open the URL manually.'\n );\n });\n },\n };\n\n const response =\n await this.msalApp.acquireTokenByDeviceCode(deviceCodeRequest);\n\n if (!response) {\n throw new Error('Failed to acquire authentication token');\n }\n\n const authToken: AuthToken = {\n accessToken: response.accessToken,\n expiresOn: response.expiresOn || new Date(Date.now() + 3600000), // Default 1 hour\n // Note: MSAL node may not provide refresh token in all flows\n };\n\n // Cache the token securely\n this.cacheToken(authToken);\n\n return authToken;\n } catch (error) {\n throw new Error(\n `Authentication failed: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n /**\n * Authenticate user using interactive browser flow with prompt control\n * This opens the system browser for authentication and handles the redirect\n */\n async authenticateInteractive(\n prompt: PromptValue = 'select_account'\n ): Promise<AuthToken> {\n if (!this.msalApp || !this.config) {\n throw new Error(\n 'MSAL not initialized. Please configure authentication first.'\n );\n }\n\n try {\n // For 'none' prompt, try silent authentication first\n if (prompt === 'none') {\n try {\n // Get all cached accounts\n const accounts = await this.msalApp.getAllAccounts();\n if (accounts && accounts.length > 0) {\n const silentRequest = {\n scopes: this.config.scopes,\n account: accounts[0], // Use the first available account\n };\n const silentResponse =\n await this.msalApp.acquireTokenSilent(silentRequest);\n\n if (silentResponse) {\n const authToken: AuthToken = {\n accessToken: silentResponse.accessToken,\n expiresOn:\n silentResponse.expiresOn || new Date(Date.now() + 3600000),\n };\n this.cacheToken(authToken);\n return authToken;\n }\n }\n } catch {\n // Silent authentication failed\n throw new Error(\n 'Silent authentication failed. User interaction required.'\n );\n }\n }\n\n // Use interactive browser flow\n log.info(\n `\\nStarting browser authentication with prompt behavior: ${prompt}`\n );\n\n // Set up local server for redirect handling\n const redirectPort = await this.getAvailablePort();\n const redirectUri = `http://localhost:${redirectPort}`;\n\n // Generate PKCE challenge\n const pkceCodes = {\n challenge: '',\n verifier: '',\n };\n\n // Create cryptographically random verifier\n pkceCodes.verifier = crypto.randomBytes(32).toString('base64url');\n pkceCodes.challenge = crypto\n .createHash('sha256')\n .update(pkceCodes.verifier)\n .digest('base64url');\n\n // Build the authorization URL\n const authCodeUrlParameters = {\n scopes: this.config.scopes,\n redirectUri,\n codeChallenge: pkceCodes.challenge,\n codeChallengeMethod: 'S256' as const,\n prompt: prompt as 'login' | 'none' | 'consent' | 'select_account', // MSAL expects specific prompt values\n responseMode: 'query' as const,\n };\n\n const authCodeUrl = await this.msalApp.getAuthCodeUrl(\n authCodeUrlParameters\n );\n\n // Set up local server to handle redirect\n const result = await listenForAuthCode(redirectPort, authCodeUrl);\n\n if (!result) {\n throw new Error('Failed to receive authorization code');\n }\n\n // Exchange authorization code for token\n const tokenRequest: AuthorizationCodeRequest = {\n code: result.code,\n scopes: this.config.scopes,\n redirectUri,\n codeVerifier: pkceCodes.verifier,\n };\n\n const response = await this.msalApp.acquireTokenByCode(tokenRequest);\n\n if (!response) {\n throw new Error('Failed to acquire authentication token');\n }\n\n const authToken: AuthToken = {\n accessToken: response.accessToken,\n expiresOn: response.expiresOn || new Date(Date.now() + 3600000),\n };\n\n // Cache the token securely\n this.cacheToken(authToken);\n\n return authToken;\n } catch (error) {\n throw new Error(\n `Browser authentication failed: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n /**\n * Get an available port for the redirect server\n */\n private async getAvailablePort(): Promise<number> {\n return new Promise(resolve => {\n const server = http.createServer();\n server.listen(0, '127.0.0.1', () => {\n const address = server.address();\n const port =\n address && typeof address !== 'string' ? address.port : 3000;\n server.close(() => resolve(port));\n });\n });\n }\n\n /**\n * Try to get a valid token without user interaction.\n * Checks the simple cache first, then attempts MSAL silent refresh.\n * Returns null if no valid token is available.\n */\n async tryGetValidTokenSilently(): Promise<string | null> {\n // Fast check: valid token in simple cache?\n const cachedToken = this.getCachedToken();\n if (cachedToken && !isTokenExpired(cachedToken.expiresOn)) {\n const ttlMin = Math.round(\n (cachedToken.expiresOn.getTime() - Date.now()) / 60_000\n );\n log.debug(`Using cached MSAL token (expires in ${ttlMin}m)`);\n return cachedToken.accessToken;\n }\n\n // Try silent refresh via MSAL (uses refresh token from persistent cache)\n if (this.msalApp && this.config) {\n try {\n const accounts = await this.msalApp.getAllAccounts();\n if (accounts.length > 0) {\n log.debug('Attempting silent token refresh...');\n const silentResult = await this.msalApp.acquireTokenSilent({\n account: accounts[0],\n scopes: this.config.scopes,\n });\n\n if (silentResult) {\n const refreshedToken: AuthToken = {\n accessToken: silentResult.accessToken,\n expiresOn:\n silentResult.expiresOn || new Date(Date.now() + 3600000),\n };\n this.cacheToken(refreshedToken);\n const ttlMin = Math.round(\n (refreshedToken.expiresOn.getTime() - Date.now()) / 60_000\n );\n log.debug(`Token silently refreshed (expires in ${ttlMin}m)`);\n return refreshedToken.accessToken;\n }\n }\n } catch (error) {\n log.debug(\n 'Silent token refresh failed, falling back to interactive auth:',\n error\n );\n }\n }\n\n return null;\n }\n\n /**\n * Get a valid access token, refreshing if necessary\n */\n async getValidToken(): Promise<string> {\n const token = await this.tryGetValidTokenSilently();\n if (token) {\n return token;\n }\n\n // Fallback: full interactive authentication\n const newToken = await this.authenticate();\n return newToken.accessToken;\n }\n\n /**\n * Check if user is currently authenticated with a valid token\n */\n isAuthenticated(): boolean {\n const cachedToken = this.getCachedToken();\n return cachedToken !== null && !isTokenExpired(cachedToken.expiresOn);\n }\n\n /**\n * Clear all cached authentication data\n */\n logout(): void {\n try {\n const tokenCachePath = this.getTokenCachePath();\n if (fs.existsSync(tokenCachePath)) {\n fs.unlinkSync(tokenCachePath);\n }\n } catch (error) {\n log.error('Error clearing token cache during logout:', error);\n }\n\n // Clear the MSAL persistent cache (refresh tokens, accounts)\n clearMsalCache();\n }\n\n private getCachedToken(): AuthToken | null {\n try {\n const tokenCachePath = this.getTokenCachePath();\n if (fs.existsSync(tokenCachePath)) {\n const content = fs.readFileSync(tokenCachePath, 'utf-8');\n const data = JSON.parse(content);\n return {\n accessToken: data.accessToken,\n expiresOn: new Date(data.expiresOn),\n refreshToken: data.refreshToken,\n };\n }\n } catch (error) {\n log.debug('Failed to load cached token:', error);\n }\n return null;\n }\n\n private cacheToken(token: AuthToken): void {\n try {\n const tokenCachePath = this.getTokenCachePath();\n const tokenData = {\n accessToken: token.accessToken,\n expiresOn: token.expiresOn.toISOString(),\n refreshToken: token.refreshToken,\n };\n\n writeSecureFile(tokenCachePath, JSON.stringify(tokenData, null, 2));\n } catch (error) {\n log.error('Failed to cache token:', error);\n }\n }\n\n private getTokenCachePath(): string {\n return path.join(getCacheDir(), MsalAuthManager.TOKEN_CACHE_FILE);\n }\n}\n", "import chalk from 'chalk';\nimport { createRequire } from 'module';\n\nexport enum LogLevel {\n ERROR = 0,\n WARN = 1,\n INFO = 2,\n DEBUG = 3,\n TRACE = 4,\n}\n\nexport interface LoggerConfig {\n level: LogLevel;\n prefix?: string;\n timestamp?: boolean;\n}\n\nexport class Logger {\n private config: LoggerConfig;\n\n constructor(config: LoggerConfig = { level: LogLevel.INFO }) {\n this.config = config;\n }\n\n /**\n * Set the current log level\n */\n setLevel(level: LogLevel): void {\n this.config.level = level;\n }\n\n /**\n * Get the current log level\n */\n getLevel(): LogLevel {\n return this.config.level;\n }\n\n /**\n * Check if a log level should be output\n */\n private shouldLog(level: LogLevel): boolean {\n return level <= this.config.level;\n }\n\n /**\n * Format an argument for logging\n * Stack traces are only included at DEBUG level or higher\n */\n private formatArg(arg: unknown): string {\n if (arg instanceof Error) {\n // Only include stack trace at DEBUG level or above\n if (this.shouldLog(LogLevel.DEBUG) && arg.stack) {\n return `${arg.message}\\n${arg.stack}`;\n }\n return arg.message;\n } else if (typeof arg === 'object' && arg !== null) {\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n } else {\n return String(arg);\n }\n }\n\n /**\n * Format the log message with optional timestamp and prefix\n */\n private formatMessage(\n level: LogLevel,\n message: string,\n ...args: unknown[]\n ): string {\n let formatted = message;\n\n // Apply arguments if provided\n if (args.length > 0) {\n formatted =\n message + ' ' + args.map(arg => this.formatArg(arg)).join(' ');\n }\n\n // Add timestamp if enabled\n if (this.config.timestamp) {\n const timestamp = new Date().toISOString();\n formatted = `[${timestamp}] ${formatted}`;\n }\n\n // Add prefix if provided\n if (this.config.prefix) {\n formatted = `[${this.config.prefix}] ${formatted}`;\n }\n\n return formatted;\n }\n\n /**\n * Error level logging (always shown unless level is below ERROR)\n */\n error(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.ERROR)) {\n const formatted = this.formatMessage(LogLevel.ERROR, message, ...args);\n console.error(chalk.red('\u274C ' + formatted));\n }\n }\n\n /**\n * Warning level logging\n */\n warn(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.WARN)) {\n const formatted = this.formatMessage(LogLevel.WARN, message, ...args);\n console.warn(chalk.yellow('\u26A0\uFE0F ' + formatted));\n }\n }\n\n /**\n * Info level logging (user-facing information)\n */\n info(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.INFO)) {\n const formatted = this.formatMessage(LogLevel.INFO, message, ...args);\n console.log(chalk.cyan('\u2139\uFE0F ' + formatted));\n }\n }\n\n /**\n * Success logging (special case of info)\n */\n success(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.INFO)) {\n const formatted = this.formatMessage(LogLevel.INFO, message, ...args);\n console.log(chalk.green('\u2705 ' + formatted));\n }\n }\n\n /**\n * Debug level logging (internal operations, verbose)\n */\n debug(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.DEBUG)) {\n const formatted = this.formatMessage(LogLevel.DEBUG, message, ...args);\n console.log(chalk.gray('\uD83D\uDD0D ' + formatted));\n }\n }\n\n /**\n * Trace level logging (very detailed, internal state)\n */\n trace(message: string, ...args: unknown[]): void {\n if (this.shouldLog(LogLevel.TRACE)) {\n const formatted = this.formatMessage(LogLevel.TRACE, message, ...args);\n console.log(chalk.dim('\uD83D\uDD2C ' + formatted));\n }\n }\n\n /**\n * Raw output without formatting (for CLI output that shouldn't be logged)\n */\n raw(message: string, ...args: unknown[]): void {\n if (args.length > 0) {\n console.log(message, ...args);\n } else {\n console.log(message);\n }\n }\n\n /**\n * Print a blank line without any icon or prefix.\n */\n blank(): void {\n console.log('');\n }\n\n /**\n * Print a section header: blank line + bold text, no icon.\n * Use instead of log.info('\\nSection Title:') for section headings.\n */\n header(text: string): void {\n console.log('');\n console.log(chalk.bold(text));\n }\n\n /**\n * Print a horizontal separator line without any icon or prefix.\n */\n separator(length = 40): void {\n console.log('\u2500'.repeat(length));\n }\n\n /**\n * Create a child logger with additional prefix\n */\n child(prefix: string): Logger {\n const childPrefix = this.config.prefix\n ? `${this.config.prefix}:${prefix}`\n : prefix;\n return new Logger({\n ...this.config,\n prefix: childPrefix,\n });\n }\n}\n\n// Global logger instance\nlet globalLogger: Logger | null = null;\n\n/**\n * Get or create the global logger instance\n */\nexport function getLogger(): Logger {\n if (!globalLogger) {\n // Default to INFO level, can be configured via environment or config\n const logLevel = getLogLevelFromEnv();\n globalLogger = new Logger({\n level: logLevel,\n timestamp: false, // Disable timestamps by default for CLI\n });\n }\n return globalLogger;\n}\n\n/**\n * Get log level from environment variable or config\n */\nfunction getLogLevelFromEnv(): LogLevel {\n let envLevel = process.env.PERISCOPE_LOG_LEVEL?.toUpperCase();\n\n // If not in env, try to get from config file.\n // Uses createRequire to synchronously load config-manager and avoid the\n // circular dependency at module load time (config-manager imports logger).\n // By the time getLogLevelFromEnv() runs, config-manager is already in the cache.\n if (!envLevel) {\n try {\n const require = createRequire(import.meta.url);\n const { ConfigManager } = require('./config-manager.js');\n const config = ConfigManager.load();\n envLevel = config.logLevel?.toUpperCase();\n } catch {\n // Ignore config loading errors during logger initialization\n }\n }\n\n switch (envLevel) {\n case 'ERROR':\n return LogLevel.ERROR;\n case 'WARN':\n case 'WARNING':\n return LogLevel.WARN;\n case 'INFO':\n return LogLevel.INFO;\n case 'DEBUG':\n return LogLevel.DEBUG;\n case 'TRACE':\n return LogLevel.TRACE;\n default:\n return LogLevel.INFO; // Default level\n }\n}\n\n// Export convenience functions that use the global logger\nexport const log = {\n error: (message: string, ...args: unknown[]) =>\n getLogger().error(message, ...args),\n warn: (message: string, ...args: unknown[]) =>\n getLogger().warn(message, ...args),\n info: (message: string, ...args: unknown[]) =>\n getLogger().info(message, ...args),\n success: (message: string, ...args: unknown[]) =>\n getLogger().success(message, ...args),\n debug: (message: string, ...args: unknown[]) =>\n getLogger().debug(message, ...args),\n trace: (message: string, ...args: unknown[]) =>\n getLogger().trace(message, ...args),\n raw: (message: string, ...args: unknown[]) =>\n getLogger().raw(message, ...args),\n blank: () => getLogger().blank(),\n header: (text: string) => getLogger().header(text),\n separator: (length?: number) => getLogger().separator(length),\n};\n", "import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\n\nconst CACHE_SUBDIR = '.periscope';\n\n/**\n * Get the cache directory path, computed at runtime to respect HOME env var overrides.\n */\nexport function getCacheDir(): string {\n return path.join(os.homedir(), CACHE_SUBDIR);\n}\n\n/**\n * Ensure the cache directory exists, creating it if necessary.\n */\nexport function ensureCacheDir(): void {\n const cacheDir = getCacheDir();\n if (!fs.existsSync(cacheDir)) {\n fs.mkdirSync(cacheDir, { recursive: true });\n }\n}\n\n/**\n * Write data to a file with owner-only permissions (0o600).\n * Creates the cache directory if it doesn't exist.\n */\nexport function writeSecureFile(filePath: string, data: string): void {\n ensureCacheDir();\n fs.writeFileSync(filePath, data, { mode: 0o600 });\n}\n", "import * as fs from 'fs';\nimport * as path from 'path';\nimport { log } from './logger.js';\nimport { getCacheDir, writeSecureFile } from './cache-utils.js';\n\nconst MSAL_CACHE_FILE = 'msal-cache.json';\n\n/**\n * Get the MSAL cache file path, computed at runtime to respect HOME env var overrides in tests.\n */\nexport function getMsalCacheFilePath(): string {\n return path.join(getCacheDir(), MSAL_CACHE_FILE);\n}\n\n/**\n * Create an MSAL ICachePlugin that persists the full MSAL token cache\n * (including refresh tokens and account info) to ~/.periscope/msal-cache.json.\n *\n * Uses structural typing to match ICachePlugin from @azure/msal-common\n * without importing it directly.\n */\nexport function createMsalCachePlugin() {\n return {\n async beforeCacheAccess(cacheContext: {\n tokenCache: { deserialize: (cache: string) => void };\n }): Promise<void> {\n const cachePath = getMsalCacheFilePath();\n try {\n if (fs.existsSync(cachePath)) {\n const data = fs.readFileSync(cachePath, 'utf-8');\n cacheContext.tokenCache.deserialize(data);\n }\n } catch (error) {\n log.debug('Failed to read MSAL cache from disk:', error);\n }\n },\n\n async afterCacheAccess(cacheContext: {\n cacheHasChanged: boolean;\n tokenCache: { serialize: () => string };\n }): Promise<void> {\n if (cacheContext.cacheHasChanged) {\n try {\n const cachePath = getMsalCacheFilePath();\n const data = cacheContext.tokenCache.serialize();\n writeSecureFile(cachePath, data);\n } catch (error) {\n log.error('Failed to write MSAL cache to disk:', error);\n }\n }\n },\n };\n}\n\n/**\n * Delete the MSAL cache file from disk. Called during logout.\n */\nexport function clearMsalCache(): void {\n try {\n const cachePath = getMsalCacheFilePath();\n if (fs.existsSync(cachePath)) {\n fs.unlinkSync(cachePath);\n }\n } catch (error) {\n log.error('Failed to clear MSAL cache:', error);\n }\n}\n", "/**\n * Shared local HTTP callback server for OAuth redirect flows.\n * Used by both MsalAuthManager and Auth0AuthManager.\n */\n\nimport * as http from 'http';\nimport open from 'open';\nimport { log } from './logger.js';\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\nexport interface AuthCallbackResult {\n /** The authorization code from the callback */\n code: string;\n /** The full raw URL path from the callback (preserves state for CSRF validation) */\n fullPath: string;\n}\n\n/**\n * Start a local HTTP server to listen for an OAuth authorization code callback.\n *\n * - On error callback: shows sanitized error page, resolves null.\n * - On code callback with matching state: shows success page, resolves with code + full path.\n * - On code callback with stale state: shows retry page, redirects to authUrl, keeps waiting.\n * - On any other request: 302 redirects to authUrl (no script injection).\n * - Times out after 5 minutes.\n *\n * @param port - Port to listen on (must be pre-registered as allowed callback URL)\n * @param authUrl - Authorization URL to redirect the browser to\n * @param expectedState - Expected OAuth state parameter to reject stale callbacks\n */\nexport function listenForAuthCode(\n port: number,\n authUrl: string,\n expectedState?: string\n): Promise<AuthCallbackResult | null> {\n return new Promise((resolve, reject) => {\n let timeoutHandle: ReturnType<typeof setTimeout>;\n\n const done = (result: AuthCallbackResult | null) => {\n clearTimeout(timeoutHandle);\n server.close();\n resolve(result);\n };\n\n const server = http.createServer((req, res) => {\n const url = new URL(req.url || '', `http://localhost:${port}`);\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n\n if (error) {\n const safeError = escapeHtml(error);\n const safeDesc = escapeHtml(\n url.searchParams.get('error_description') || ''\n );\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px;\">\n <h1>Authentication Failed</h1>\n <p>Error: ${safeError}</p>\n <p>${safeDesc}</p>\n <p>You can close this window.</p>\n </body>\n </html>\n `);\n done(null);\n return;\n }\n\n if (code) {\n // Reject stale callbacks from previous auth attempts\n const callbackState = url.searchParams.get('state');\n if (expectedState && callbackState !== expectedState) {\n log.debug(\n `Ignoring stale callback (state mismatch: expected ${expectedState.slice(0, 8)}\u2026, got ${callbackState?.slice(0, 8)}\u2026)`\n );\n // Show a page that redirects to the current auth URL\n const safeLocation = authUrl.replace(/[\\r\\n]/g, '');\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <title>Authentication - Retry</title>\n </head>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px;\">\n <h1>Session Expired</h1>\n <p>This callback was from a previous login attempt. Redirecting...</p>\n <script>window.location.href = ${JSON.stringify(safeLocation)};</script>\n </body>\n </html>\n `);\n return; // Keep server running, wait for the correct callback\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <title>Authentication Successful</title>\n </head>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px;\">\n <h1>✓ Authentication Successful</h1>\n <p>You can close this window and return to the terminal.</p>\n <script>window.setTimeout(() => window.close(), 2000);</script>\n </body>\n </html>\n `);\n done({ code, fullPath: url.pathname + url.search });\n return;\n }\n\n // Not a callback \u2014 use HTTP 302 redirect (avoids injecting URL into script).\n // Strip CRLF to prevent HTTP response splitting if authUrl is ever untrusted.\n const safeLocation = authUrl.replace(/[\\r\\n]/g, '');\n res.writeHead(302, { Location: safeLocation });\n res.end();\n });\n\n // Reject on EADDRINUSE so callers get an actionable error rather than a 5-minute hang.\n server.on('error', (err: NodeJS.ErrnoException) => {\n clearTimeout(timeoutHandle);\n server.close();\n if (err.code === 'EADDRINUSE') {\n reject(\n new Error(\n `Port ${port} is already in use. Another authentication flow may be in progress.`\n )\n );\n } else {\n log.warn(`Auth callback server error: ${err.message}`);\n resolve(null);\n }\n });\n\n server.listen(port, '127.0.0.1', () => {\n log.blank();\n log.info('Opening browser for authentication...');\n log.info(\"If the browser doesn't open automatically, visit:\");\n log.info(` http://localhost:${port}`);\n log.blank();\n\n open(authUrl).catch(() => {\n log.warn(\n 'Failed to open browser automatically. Please open the URL manually.'\n );\n });\n });\n\n // Timeout after 5 minutes\n timeoutHandle = setTimeout(() => done(null), 5 * 60 * 1000);\n });\n}\n", "/**\n * Shared authentication types used by all auth provider implementations.\n */\n\n/** Buffer (60s) to avoid using tokens that are about to expire mid-request */\nconst TOKEN_EXPIRY_BUFFER_MS = 60_000;\n\n/** Returns true if the token expiry time is within the safety buffer. */\nexport function isTokenExpired(expiresAt: Date): boolean {\n return expiresAt <= new Date(Date.now() + TOKEN_EXPIRY_BUFFER_MS);\n}\n\nexport interface AuthToken {\n accessToken: string;\n expiresOn: Date;\n refreshToken?: string;\n}\n\nexport type PromptValue = 'select_account' | 'login' | 'consent' | 'none';\n\n/**\n * Common interface for authentication managers.\n * Implemented by MsalAuthManager (Entra) and Auth0AuthManager (Auth0).\n */\nexport interface IAuthManager {\n authenticate(): Promise<AuthToken>;\n authenticateInteractive(prompt?: PromptValue): Promise<AuthToken>;\n tryGetValidTokenSilently(): Promise<string | null>;\n getValidToken(): Promise<string>;\n isAuthenticated(): boolean;\n logout(): void;\n}\n", "import * as client from 'openid-client';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport open from 'open';\nimport { log } from './logger.js';\nimport { getCacheDir, writeSecureFile } from './cache-utils.js';\nimport { listenForAuthCode } from './auth-callback-server.js';\nimport {\n isTokenExpired,\n type AuthToken,\n type IAuthManager,\n type PromptValue,\n} from './auth-types.js';\n\nexport interface Auth0Config {\n /** Auth0 authority URL, e.g. https://periscope.auth0.com/ */\n authority: string;\n /** Auth0 application client ID */\n clientId: string;\n /** OAuth2 scopes to request */\n scopes: string[];\n /** Auth0 API audience identifier (required for JWT access tokens) */\n audience?: string;\n}\n\ninterface Auth0CachedTokens {\n accessToken: string;\n refreshToken?: string;\n idToken?: string;\n expiresAt: string; // ISO date string\n}\n\n/**\n * Auth0 authentication manager using openid-client (RFC-compliant OIDC).\n * Supports authorization code + PKCE browser flow (interactive) and\n * device code flow (headless/fallback), plus token refresh.\n */\nexport class Auth0AuthManager implements IAuthManager {\n private static readonly TOKEN_CACHE_FILE = 'auth0-token-cache.json';\n private static readonly REDIRECT_PORT = 19836;\n\n private oidcConfig: client.Configuration | null = null;\n private config: Auth0Config | null = null;\n\n constructor(config?: Auth0Config) {\n if (config) {\n this.config = config;\n }\n }\n\n /**\n * Discover OIDC endpoints and initialize the openid-client Configuration.\n * Lazy-loaded on first use.\n */\n private async ensureOidcConfig(): Promise<client.Configuration> {\n if (this.oidcConfig) return this.oidcConfig;\n if (!this.config) {\n throw new Error('Auth0 configuration is required');\n }\n\n const serverUrl = new URL(this.config.authority);\n this.oidcConfig = await client.discovery(\n serverUrl,\n this.config.clientId,\n undefined,\n client.None()\n );\n return this.oidcConfig;\n }\n\n /**\n * Authenticate using the device code flow (RFC 8628).\n * Displays a code + URL, opens the browser, and polls for completion.\n */\n async authenticate(): Promise<AuthToken> {\n const oidcConfig = await this.ensureOidcConfig();\n\n const parameters: Record<string, string> = {\n scope: this.getScopes(),\n };\n if (this.config?.audience) {\n parameters.audience = this.config.audience;\n }\n\n const deviceResponse = await client.initiateDeviceAuthorization(\n oidcConfig,\n parameters\n );\n\n const verificationUrl =\n deviceResponse.verification_uri_complete ||\n deviceResponse.verification_uri;\n\n log.blank();\n log.info(\n 'To authenticate, open the page below and confirm the code matches:'\n );\n log.info(` ${verificationUrl}`);\n log.blank();\n log.info(`Your code: ${deviceResponse.user_code}`);\n log.blank();\n log.info('Opening browser automatically...');\n log.blank();\n\n open(verificationUrl).catch(() => {\n log.warn(\n 'Failed to open browser automatically. Please open the URL manually.'\n );\n });\n\n const tokenResponse = await client.pollDeviceAuthorizationGrant(\n oidcConfig,\n deviceResponse\n );\n\n return this.processTokenResponse(tokenResponse);\n }\n\n /**\n * Authenticate using the interactive browser flow (Authorization Code + PKCE).\n * Opens the browser directly to Auth0 Universal Login \u2014 user enters email,\n * receives OTP, enters it, and is redirected back. Single code, no device confirmation.\n */\n async authenticateInteractive(_prompt?: PromptValue): Promise<AuthToken> {\n const oidcConfig = await this.ensureOidcConfig();\n\n // Generate PKCE code verifier + challenge\n const codeVerifier = client.randomPKCECodeVerifier();\n const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);\n const state = client.randomState();\n\n // Use a fixed port so the callback URL can be registered in Auth0\n const port = Auth0AuthManager.REDIRECT_PORT;\n const redirectUri = `http://localhost:${port}/callback`;\n\n // Build authorization URL\n const parameters: Record<string, string> = {\n redirect_uri: redirectUri,\n scope: this.getScopes(),\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state,\n };\n if (this.config?.audience) {\n parameters.audience = this.config.audience;\n }\n\n const authUrl = client.buildAuthorizationUrl(oidcConfig, parameters);\n\n // Start local server and wait for the authorization code callback.\n log.debug('Waiting for Auth0 callback on port', port);\n const result = await listenForAuthCode(port, authUrl.href, state);\n\n if (!result) {\n throw new Error('Failed to receive authorization code from Auth0');\n }\n\n log.debug('Received authorization code, exchanging for tokens...');\n log.debug('Callback fullPath:', result.fullPath);\n\n // Use the full callback path from the server (preserves state for CSRF validation)\n const callbackUrl = new URL(result.fullPath, `http://localhost:${port}`);\n\n try {\n const tokenResponse = await client.authorizationCodeGrant(\n oidcConfig,\n callbackUrl,\n {\n pkceCodeVerifier: codeVerifier,\n expectedState: state,\n idTokenExpected: true,\n }\n );\n\n log.debug('Token exchange successful');\n return this.processTokenResponse(tokenResponse);\n } catch (error) {\n log.debug('Token exchange failed:', error);\n if (error instanceof Error && error.cause) {\n log.debug('Token exchange error cause:', error.cause);\n }\n throw error;\n }\n }\n\n /**\n * Try to get a valid token without user interaction.\n * Checks cache first, then tries refresh token.\n */\n async tryGetValidTokenSilently(): Promise<string | null> {\n const cached = this.getCachedTokens();\n if (!cached) return null;\n\n // Check if access token is still valid\n const expiresAt = new Date(cached.expiresAt);\n if (!isTokenExpired(expiresAt)) {\n const ttlMin = Math.round((expiresAt.getTime() - Date.now()) / 60_000);\n log.debug(`Using cached Auth0 token (expires in ${ttlMin}m)`);\n return cached.accessToken;\n }\n\n // Try refresh\n if (cached.refreshToken) {\n try {\n log.debug('Attempting Auth0 token refresh...');\n const token = await this.refreshAccessToken(cached.refreshToken);\n const ttlMin = Math.round(\n (token.expiresOn.getTime() - Date.now()) / 60_000\n );\n log.debug(`Auth0 token refreshed successfully (expires in ${ttlMin}m)`);\n return token.accessToken;\n } catch (error) {\n log.debug('Auth0 token refresh failed:', error);\n }\n } else {\n log.debug(\n 'No refresh token available \u2014 cannot silently refresh. Re-authentication required.'\n );\n }\n\n return null;\n }\n\n /**\n * Get a valid access token, refreshing or re-authenticating if necessary.\n *\n * Falls back to the device code flow (not browser PKCE) so this method can be\n * called in headless environments (e.g., mid-tunnel token refresh). Callers that\n * need browser-based re-authentication should call authenticateInteractive() directly.\n */\n async getValidToken(): Promise<string> {\n const token = await this.tryGetValidTokenSilently();\n if (token) return token;\n\n // Fallback: device code flow (headless-safe, works without a browser redirect port)\n const newToken = await this.authenticate();\n return newToken.accessToken;\n }\n\n /**\n * Check if a valid cached token exists.\n */\n isAuthenticated(): boolean {\n const cached = this.getCachedTokens();\n if (!cached) return false;\n return !isTokenExpired(new Date(cached.expiresAt));\n }\n\n /**\n * Clear all cached Auth0 token data.\n */\n logout(): void {\n try {\n const cachePath = this.getTokenCachePath();\n if (fs.existsSync(cachePath)) {\n fs.unlinkSync(cachePath);\n }\n } catch (error) {\n log.error('Error clearing Auth0 token cache during logout:', error);\n }\n }\n\n /**\n * Use a refresh token to obtain a new access token.\n */\n private async refreshAccessToken(refreshToken: string): Promise<AuthToken> {\n const oidcConfig = await this.ensureOidcConfig();\n const tokenResponse = await client.refreshTokenGrant(\n oidcConfig,\n refreshToken\n );\n return this.processTokenResponse(tokenResponse);\n }\n\n /**\n * Process a token endpoint response into our AuthToken format and cache it.\n */\n private processTokenResponse(\n response: client.TokenEndpointResponse & client.TokenEndpointResponseHelpers\n ): AuthToken {\n const expiresIn = response.expiresIn();\n const expiresOn = expiresIn\n ? new Date(Date.now() + expiresIn * 1000)\n : new Date(Date.now() + 3600000); // Default 1 hour\n\n if (!response.refresh_token) {\n log.debug(\n 'Auth0 token response did not include a refresh token. Token refresh will not be available when the access token expires.'\n );\n }\n\n const authToken: AuthToken = {\n accessToken: response.access_token,\n expiresOn,\n refreshToken: response.refresh_token,\n };\n\n // Cache the tokens\n this.cacheTokens({\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n idToken: response.id_token,\n expiresAt: expiresOn.toISOString(),\n });\n\n return authToken;\n }\n\n private getScopes(): string {\n if (!this.config?.scopes?.length) {\n return 'openid profile email offline_access';\n }\n // Ensure offline_access is included for refresh tokens\n const scopes = new Set(this.config.scopes);\n scopes.add('offline_access');\n return [...scopes].join(' ');\n }\n\n private getCachedTokens(): Auth0CachedTokens | null {\n try {\n const cachePath = this.getTokenCachePath();\n if (fs.existsSync(cachePath)) {\n const content = fs.readFileSync(cachePath, 'utf-8');\n const parsed = JSON.parse(content) as Partial<Auth0CachedTokens>;\n // Guard against a corrupt or schema-mismatched cache file that would\n // produce a \"Bearer undefined\" Authorization header downstream.\n if (!parsed.accessToken || !parsed.expiresAt) return null;\n return parsed as Auth0CachedTokens;\n }\n } catch (error) {\n log.debug('Failed to load Auth0 cached tokens:', error);\n }\n return null;\n }\n\n private cacheTokens(tokens: Auth0CachedTokens): void {\n try {\n const cachePath = this.getTokenCachePath();\n writeSecureFile(cachePath, JSON.stringify(tokens, null, 2));\n } catch (error) {\n log.error('Failed to cache Auth0 tokens:', error);\n }\n }\n\n private getTokenCachePath(): string {\n return path.join(getCacheDir(), Auth0AuthManager.TOKEN_CACHE_FILE);\n }\n}\n", "//----------------------\n// <auto-generated>\n// Generated using the NSwag toolchain v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)\n// </auto-generated>\n//----------------------\n/* tslint:disable */\n/* eslint-disable */\n// ReSharper disable InconsistentNaming\nexport var PeriscopeApi;\n(function (PeriscopeApi) {\n class PeriscopeApiClient {\n constructor(baseUrl, http) {\n this.jsonParseReviver = undefined;\n this.http = http ? http : window;\n this.baseUrl = baseUrl ?? \"\";\n }\n /**\n * @param clientVersion (optional)\n * @return OK\n */\n configuration(clientVersion) {\n let url_ = this.baseUrl + \"/api/configuration?\";\n if (clientVersion === null)\n throw new Error(\"The parameter 'clientVersion' cannot be null.\");\n else if (clientVersion !== undefined)\n url_ += \"clientVersion=\" + encodeURIComponent(\"\" + clientVersion) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processConfiguration(_response);\n });\n }\n processConfiguration(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 503) {\n return response.text().then((_responseText) => {\n return throwException(\"Service Unavailable\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n telemetryGET() {\n let url_ = this.baseUrl + \"/api/configuration/telemetry\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processTelemetryGET(_response);\n });\n }\n processTelemetryGET(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 401) {\n return response.text().then((_responseText) => {\n let result401 = null;\n result401 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Unauthorized\", status, _responseText, _headers, result401);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n submitFeedback(body) {\n let url_ = this.baseUrl + \"/api/feedback\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processSubmitFeedback(_response);\n });\n }\n processSubmitFeedback(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n telemetryGET2() {\n let url_ = this.baseUrl + \"/api/test/telemetry\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {}\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processTelemetryGET2(_response);\n });\n }\n processTelemetryGET2(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n telemetryDELETE() {\n let url_ = this.baseUrl + \"/api/test/telemetry\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"DELETE\",\n headers: {}\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processTelemetryDELETE(_response);\n });\n }\n processTelemetryDELETE(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n licenseConfigPOST(body) {\n let url_ = this.baseUrl + \"/api/test/license-config\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processLicenseConfigPOST(_response);\n });\n }\n processLicenseConfigPOST(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n licenseConfigDELETE() {\n let url_ = this.baseUrl + \"/api/test/license-config\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"DELETE\",\n headers: {}\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processLicenseConfigDELETE(_response);\n });\n }\n processLicenseConfigDELETE(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n getCurrentUser() {\n let url_ = this.baseUrl + \"/api/user\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processGetCurrentUser(_response);\n });\n }\n processGetCurrentUser(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 404) {\n return response.text().then((_responseText) => {\n let result404 = null;\n result404 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Not Found\", status, _responseText, _headers, result404);\n });\n }\n else if (status === 500) {\n return response.text().then((_responseText) => {\n return throwException(\"Internal Server Error\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n getSshCredentials() {\n let url_ = this.baseUrl + \"/api/user/ssh-credentials\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processGetSshCredentials(_response);\n });\n }\n processGetSshCredentials(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status === 500) {\n return response.text().then((_responseText) => {\n return throwException(\"Internal Server Error\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n validateSshHostKey(body) {\n let url_ = this.baseUrl + \"/api/user/ssh/validate-host-key\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processValidateSshHostKey(_response);\n });\n }\n processValidateSshHostKey(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n }\n else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422 = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n registerPublicKey(body) {\n let url_ = this.baseUrl + \"/api/user/public-key\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processRegisterPublicKey(_response);\n });\n }\n processRegisterPublicKey(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status === 500) {\n return response.text().then((_responseText) => {\n return throwException(\"Internal Server Error\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @return OK\n */\n getTermsStatus() {\n let url_ = this.baseUrl + \"/api/user/terms-status\";\n url_ = url_.replace(/[?&]$/, \"\");\n let options_ = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processGetTermsStatus(_response);\n });\n }\n processGetTermsStatus(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n acceptTerms(body) {\n let url_ = this.baseUrl + \"/api/user/accept-terms\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processAcceptTerms(_response);\n });\n }\n processAcceptTerms(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n /**\n * @param body (optional)\n * @return OK\n */\n updateUserSlug(body) {\n let url_ = this.baseUrl + \"/api/user/slug\";\n url_ = url_.replace(/[?&]$/, \"\");\n const content_ = JSON.stringify(body);\n let options_ = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n return this.http.fetch(url_, options_).then((_response) => {\n return this.processUpdateUserSlug(_response);\n });\n }\n processUpdateUserSlug(response) {\n const status = response.status;\n let _headers = {};\n if (response.headers && response.headers.forEach) {\n response.headers.forEach((v, k) => _headers[k] = v);\n }\n ;\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200 = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return result200;\n });\n }\n else if (status === 400) {\n return response.text().then((_responseText) => {\n let result400 = null;\n result400 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Bad Request\", status, _responseText, _headers, result400);\n });\n }\n else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409 = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver);\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n }\n else if (status === 500) {\n return response.text().then((_responseText) => {\n return throwException(\"Internal Server Error\", status, _responseText, _headers);\n });\n }\n else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null);\n }\n }\n PeriscopeApi.PeriscopeApiClient = PeriscopeApiClient;\n class ApiException extends Error {\n constructor(message, status, response, headers, result) {\n super();\n this.isApiException = true;\n this.message = message;\n this.status = status;\n this.response = response;\n this.headers = headers;\n this.result = result;\n }\n static isApiException(obj) {\n return obj.isApiException === true;\n }\n }\n PeriscopeApi.ApiException = ApiException;\n function throwException(message, status, response, headers, result) {\n throw new ApiException(message, status, response, headers, result);\n }\n})(PeriscopeApi || (PeriscopeApi = {}));\n", "/**\n * Centralized cache for the server's /api/configuration response.\n *\n * Gate 1 of CLI initialization: fetched once (unauthenticated) and\n * consumed by MSAL auth, telemetry init, and display commands.\n * Eliminates duplicate GET /api/configuration requests.\n */\n\nimport { PeriscopeApi } from '@elf-5/periscope-api-client';\n\nlet cachedConfig: PeriscopeApi.PeriscopeConfigDto | null = null;\nlet fetchPromise: Promise<PeriscopeApi.PeriscopeConfigDto> | null = null;\n\n/**\n * Get the server configuration, fetching it at most once per CLI invocation.\n * Concurrent calls share the same in-flight request.\n *\n * @param serverUrl - The Periscope server base URL\n * @returns The cached server configuration DTO\n */\nexport async function getServerConfig(\n serverUrl: string\n): Promise<PeriscopeApi.PeriscopeConfigDto> {\n if (cachedConfig) return cachedConfig;\n\n // Deduplicate concurrent calls\n if (fetchPromise) return fetchPromise;\n\n fetchPromise = fetchServerConfig(serverUrl);\n try {\n cachedConfig = await fetchPromise;\n return cachedConfig;\n } finally {\n fetchPromise = null;\n }\n}\n\n/**\n * Get the cached config without fetching. Returns null if not yet fetched.\n */\nexport function getCachedServerConfig(): PeriscopeApi.PeriscopeConfigDto | null {\n return cachedConfig;\n}\n\n/**\n * Clear the cached config. Useful for testing.\n */\nexport function clearServerConfigCache(): void {\n cachedConfig = null;\n fetchPromise = null;\n}\n\nasync function fetchServerConfig(\n serverUrl: string\n): Promise<PeriscopeApi.PeriscopeConfigDto> {\n const client = new PeriscopeApi.PeriscopeApiClient(serverUrl, {\n fetch: (url: string | URL, init?: RequestInit) => fetch(url, init),\n });\n return client.configuration(undefined); // clientVersion is optional\n}\n", "import { PeriscopeConfig } from './config-manager.js';\nimport type { AuthToken, IAuthManager, PromptValue } from './auth-types.js';\nimport { MsalAuthManager } from './msal-auth-manager.js';\nimport { Auth0AuthManager } from './auth0-auth-manager.js';\nimport { PeriscopeApi } from '@elf-5/periscope-api-client';\nimport { getLogger } from './logger.js';\nimport { getServerConfig } from './server-config.js';\nimport * as https from 'https';\nimport * as fs from 'fs';\n\n// Re-export the official types from the API client for convenience\nexport type UserInfo = PeriscopeApi.UserDto;\n\n/**\n * Account status values returned by the server.\n * Mirrors the UserStatus enum in Periscope.Shared.\n */\nexport const AccountStatus = {\n PENDING_APPROVAL: 'PendingApproval',\n ACTIVE: 'Active',\n REJECTED: 'Rejected',\n INACTIVE: 'Inactive',\n} as const;\n\n// Define only the interfaces we need that aren't in the API client\nexport interface HealthStatus {\n healthy: boolean;\n version?: string;\n uptime?: number;\n}\n\nexport class PeriscopeClient {\n private authManager: IAuthManager | null = null;\n private apiClient: PeriscopeApi.PeriscopeApiClient | null = null;\n private logger = getLogger().child('PeriscopeClient');\n private httpsAgent: https.Agent | null = null;\n private authInitPromise: Promise<void> | null = null;\n\n constructor(private config: PeriscopeConfig) {\n if (!config.serverUrl) {\n throw new Error('Server URL is required');\n }\n\n // Create HTTPS agent with custom CA certificate if provided\n if (config.caCertPath) {\n try {\n const caCert = fs.readFileSync(config.caCertPath);\n this.httpsAgent = new https.Agent({\n ca: caCert,\n });\n this.logger.debug(\n `Using custom CA certificate from: ${config.caCertPath}`\n );\n } catch (error) {\n this.logger.error(\n `Failed to load CA certificate from ${config.caCertPath}:`,\n error\n );\n throw new Error(\n `Failed to load CA certificate: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n }\n\n /**\n * Perform fetch with SSL configuration\n */\n private async fetchWithOptions(\n url: string | URL,\n init?: RequestInit\n ): Promise<Response> {\n const options: RequestInit & { agent?: https.Agent } = { ...init };\n\n // Add HTTPS agent with custom CA if configured\n if (this.httpsAgent && url.toString().startsWith('https://')) {\n options.agent = this.httpsAgent;\n }\n\n return fetch(url, options);\n }\n\n /**\n * Initialize the auth manager with configuration from the API.\n * Selects MSAL (Entra) or Auth0 based on the server's authProvider field.\n * Uses a cached promise to prevent duplicate initialization attempts.\n */\n private async initializeAuth(): Promise<void> {\n // Already initialized\n if (this.authManager) {\n return;\n }\n\n // Reuse in-flight initialization to prevent duplicate requests\n if (this.authInitPromise) {\n return this.authInitPromise;\n }\n\n this.authInitPromise = this.doInitializeAuth();\n try {\n await this.authInitPromise;\n } finally {\n // Clear the promise so future calls can retry if this one failed\n this.authInitPromise = null;\n }\n }\n\n /**\n * Internal auth initialization logic.\n * Uses the centralized ServerConfig cache to avoid duplicate /api/configuration requests.\n */\n private async doInitializeAuth(): Promise<void> {\n const serverConfig = await getServerConfig(this.config.serverUrl!);\n\n if (!serverConfig.clientId || !serverConfig.authority) {\n throw new Error('Incomplete auth configuration received from server');\n }\n\n const isAuth0 = serverConfig.authProvider?.toLowerCase() === 'auth0';\n\n if (isAuth0) {\n this.authManager = new Auth0AuthManager({\n authority: serverConfig.authority,\n clientId: serverConfig.clientId,\n scopes: serverConfig.scopes || ['openid', 'profile', 'email'],\n audience: serverConfig.audience,\n });\n this.logger.debug('Auth0 auth manager initialized from server config');\n } else {\n if (!serverConfig.scopes) {\n throw new Error('Incomplete MSAL configuration received from server');\n }\n this.authManager = new MsalAuthManager({\n clientId: serverConfig.clientId,\n authority: serverConfig.authority,\n scopes: serverConfig.scopes,\n });\n this.logger.debug('MSAL auth manager initialized from server config');\n }\n }\n\n /**\n * Create a custom fetch implementation that includes authentication headers.\n * Dynamically fetches a fresh token on each request via authManager so that\n * long-lived API clients (e.g., during tunnel reconnection) automatically\n * pick up refreshed tokens instead of replaying an expired one.\n */\n private createAuthenticatedFetch(token: string) {\n return {\n fetch: async (\n url: string | URL,\n init?: RequestInit\n ): Promise<Response> => {\n const headers = new Headers(init?.headers);\n\n // Get a fresh token if authManager is available, otherwise use the initial token.\n // tryGetValidTokenSilently() returns the cached token if still valid, or silently\n // refreshes it. If refresh fails (e.g., expired refresh token), throw so the caller\n // gets a clear auth error rather than triggering interactive auth in a background\n // context (e.g., tunnel reconnect).\n let currentToken: string;\n if (this.authManager) {\n const silentToken = await this.authManager.tryGetValidTokenSilently();\n if (!silentToken) {\n throw new Error(\n 'Authentication expired. Please run `periscope auth login` to re-authenticate.'\n );\n }\n currentToken = silentToken;\n } else {\n currentToken = token;\n }\n\n headers.set('Authorization', `Bearer ${currentToken}`);\n headers.set('Content-Type', 'application/json');\n headers.set('Accept', 'application/json');\n\n const requestInit: RequestInit = {\n ...init,\n headers,\n };\n\n this.logger.debug(`Making authenticated request to: ${url}`);\n this.logger.trace('Using Bearer token: [present]');\n\n const response = await this.fetchWithOptions(url, requestInit);\n\n this.logger.debug(`Response status: ${response.status}`);\n\n return response;\n },\n };\n }\n\n /**\n * Create an unauthenticated fetch implementation for public endpoints\n */\n private createUnauthenticatedFetch() {\n return {\n fetch: async (\n url: string | URL,\n init?: RequestInit\n ): Promise<Response> => {\n const headers = new Headers(init?.headers);\n\n // Set standard headers without authentication\n headers.set('Content-Type', 'application/json');\n headers.set('Accept', 'application/json');\n\n const requestInit: RequestInit = {\n ...init,\n headers,\n };\n\n this.logger.debug(`Making unauthenticated request to: ${url}`);\n\n const response = await this.fetchWithOptions(url, requestInit);\n\n this.logger.debug(`Response status: ${response.status}`);\n\n return response;\n },\n };\n }\n\n /**\n * Initialize the API client with authentication token\n */\n private async initializeApiClient(token: string): Promise<boolean> {\n try {\n if (!this.config.serverUrl) {\n throw new Error('Server URL is required');\n }\n\n // Create authenticated fetch implementation\n const authenticatedFetch = this.createAuthenticatedFetch(token);\n\n // Create the API client with base URL and authenticated fetch\n this.apiClient = new PeriscopeApi.PeriscopeApiClient(\n this.config.serverUrl,\n authenticatedFetch\n );\n\n this.logger.debug('Periscope API client initialized successfully');\n this.logger.debug('API base URL:', this.config.serverUrl);\n this.logger.debug('Auth token:', token ? 'Present' : 'Missing');\n\n return true;\n } catch (error) {\n this.logger.error('Failed to initialize Periscope API client:', error);\n return false;\n }\n }\n\n /**\n * Check if API client is initialized and ready\n */\n private async isApiClientReady(): Promise<boolean> {\n if (this.apiClient) {\n return true;\n }\n\n // Try to initialize from existing auth\n await this.ensureApiClientInitialized();\n return this.apiClient !== null;\n }\n\n /**\n * Authenticate and ensure we have a valid token\n */\n async authenticate(): Promise<AuthToken> {\n // Initialize auth manager if not already done\n await this.initializeAuth();\n\n if (!this.authManager) {\n throw new Error('Failed to initialize auth configuration');\n }\n\n const authResult = await this.authManager.authenticate();\n\n // Initialize API client with the token\n await this.initializeApiClient(authResult.accessToken);\n\n return authResult;\n }\n\n /**\n * Authenticate using interactive flow with prompt control\n */\n async authenticateInteractive(prompt?: PromptValue): Promise<AuthToken> {\n // Initialize auth manager if not already done\n await this.initializeAuth();\n\n if (!this.authManager) {\n throw new Error('Failed to initialize auth configuration');\n }\n\n const authResult = await this.authManager.authenticateInteractive(prompt);\n\n // Initialize API client with the token\n await this.initializeApiClient(authResult.accessToken);\n\n return authResult;\n }\n\n /**\n * Check if currently authenticated.\n *\n * Checks in order:\n * 1. Already initialized authManager with valid token\n * 2. Cached token on disk (without needing server config)\n * 3. Initialize auth from server and check\n *\n * This layered approach allows the CLI to work even when the server\n * doesn't provide auth configuration (e.g., test environments).\n */\n async isAuthenticated(): Promise<boolean> {\n // 1. Check already-initialized authManager\n if (this.authManager?.isAuthenticated()) {\n await this.ensureApiClientInitialized();\n return true;\n }\n\n // 2. Check for cached token on disk (works without server config)\n if (await this.tryInitializeFromCachedToken()) {\n // Initialize the full auth manager so code that uses this.authManager\n // (e.g., getTermsStatus, acceptTerms) works correctly.\n try {\n await this.initializeAuth();\n } catch (error) {\n // Server config unavailable \u2014 apiClient still has the cached token.\n // authManager will remain null; methods that need it handle this gracefully.\n this.logger.debug('Auth init failed after cached token login:', error);\n }\n return true;\n }\n\n // 3. Try full auth initialization from server (enables silent refresh)\n try {\n await this.initializeAuth();\n if (this.authManager) {\n // tryGetValidTokenSilently() checks simple cache and attempts\n // silent refresh using persisted refresh tokens\n const token = await this.authManager.tryGetValidTokenSilently();\n if (token) {\n await this.initializeApiClient(token);\n return true;\n }\n }\n } catch (error) {\n // Distinguish between network errors (should throw) and auth issues (return false)\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const isNetworkError =\n errorMessage.includes('ECONNREFUSED') ||\n errorMessage.includes('ENOTFOUND') ||\n errorMessage.includes('ETIMEDOUT') ||\n errorMessage.includes('ECONNRESET') ||\n errorMessage.includes('fetch failed') ||\n errorMessage.toLowerCase().includes('network');\n\n if (isNetworkError) {\n // Throw a clear error so commands can display a helpful message\n // Include the server URL so users know which server failed\n throw new Error(\n `Server is unreachable (${this.config.serverUrl}): ${errorMessage}`\n );\n }\n\n this.logger.debug('Auth initialization failed during auth check:', error);\n }\n\n return false;\n }\n\n /**\n * @deprecated Use isAuthenticated() instead. This alias exists for backwards compatibility.\n */\n async isAuthenticatedAsync(): Promise<boolean> {\n return this.isAuthenticated();\n }\n\n /**\n * Try to initialize from a cached token file without needing server config.\n * Checks both MSAL and Auth0 token caches.\n * Returns true if a valid cached token was found and API client initialized.\n *\n * NOTE: Config-less managers are used only to read the cache and extract\n * a still-valid access token. They are NOT stored as this.authManager\n * because they lack server config needed for re-authentication and\n * token refresh. The full authManager is set later via initializeAuth().\n */\n private async tryInitializeFromCachedToken(): Promise<boolean> {\n // Try MSAL cache first\n try {\n const msalAuthManager = new MsalAuthManager();\n if (msalAuthManager.isAuthenticated()) {\n const token = await msalAuthManager.getValidToken();\n await this.initializeApiClient(token);\n this.logger.debug('Initialized API client from cached MSAL token');\n return true;\n }\n } catch {\n // MSAL cache not available or invalid\n }\n\n // Try Auth0 cache\n try {\n const auth0AuthManager = new Auth0AuthManager();\n if (auth0AuthManager.isAuthenticated()) {\n const token = await auth0AuthManager.getValidToken();\n await this.initializeApiClient(token);\n this.logger.debug('Initialized API client from cached Auth0 token');\n return true;\n }\n } catch {\n // Auth0 cache not available or invalid\n }\n\n return false;\n }\n\n /**\n * Ensure API client is initialized if we have valid authentication.\n */\n private async ensureApiClientInitialized(): Promise<void> {\n if (this.apiClient || !this.authManager) {\n return;\n }\n\n try {\n const token = await this.authManager.getValidToken();\n await this.initializeApiClient(token);\n } catch (error) {\n this.logger.debug('Failed to initialize API client:', error);\n }\n }\n\n /**\n * Logout and clear authentication data.\n * Clears both MSAL and Auth0 caches to ensure a clean slate.\n */\n async logout(): Promise<void> {\n // Initialize auth if needed to ensure proper logout\n try {\n await this.initializeAuth();\n } catch (error) {\n this.logger.warn('Failed to initialize auth for logout:', error);\n }\n\n if (this.authManager) {\n this.authManager.logout();\n }\n\n // Also clear the other provider's cache in case the user switches providers\n try {\n if (!(this.authManager instanceof MsalAuthManager)) {\n new MsalAuthManager().logout();\n }\n } catch {\n // Ignore errors clearing MSAL cache\n }\n try {\n if (!(this.authManager instanceof Auth0AuthManager)) {\n new Auth0AuthManager().logout();\n }\n } catch {\n // Ignore errors clearing Auth0 cache\n }\n\n this.apiClient = null;\n }\n\n // API Methods - these use the real API client\n\n /**\n * Get auth configuration from server (unauthenticated endpoint)\n */\n async getAuthConfig(): Promise<PeriscopeApi.PeriscopeConfigDto> {\n if (!this.config.serverUrl) {\n throw new Error('Server URL is required to fetch auth configuration');\n }\n\n try {\n // Create an unauthenticated API client for this public endpoint\n const unauthenticatedFetch = this.createUnauthenticatedFetch();\n const unauthenticatedClient = new PeriscopeApi.PeriscopeApiClient(\n this.config.serverUrl,\n unauthenticatedFetch\n );\n\n this.logger.debug(\n `Getting auth configuration from ${this.config.serverUrl}`\n );\n const authConfig = await unauthenticatedClient.configuration(undefined); // clientVersion is optional\n this.logger.debug('Successfully fetched auth configuration from server');\n\n return authConfig;\n } catch (error) {\n this.logger.debug(\n 'Failed to fetch auth configuration from server:',\n error\n );\n throw new Error(\n `Failed to fetch auth configuration: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n async checkHealth(): Promise<HealthStatus> {\n if (!this.config.serverUrl) {\n throw new Error('Server URL is required');\n }\n\n const response = await this.fetchWithOptions(\n `${this.config.serverUrl}/healthz`\n );\n\n if (!response.ok) {\n return { healthy: false };\n }\n\n const body = (await response.json()) as { status?: string };\n return { healthy: body.status === 'Healthy' };\n }\n\n /**\n * Register the client's SSH public key with the server.\n * Returns the normalized public key string accepted by the server.\n */\n async registerPublicKey(publicKey: string): Promise<string> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n const response = await this.apiClient.registerPublicKey({ publicKey });\n if (response.publicKey == null) {\n throw new Error(\n 'Server did not return the registered public key. The server may be outdated.'\n );\n }\n return response.publicKey;\n }\n\n async getSSHCredentials(): Promise<PeriscopeApi.SshCredentialsDto> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n try {\n this.logger.debug('Fetching SSH credentials...');\n const sshCredentials = await this.apiClient.getSshCredentials();\n this.logger.debug(\n `SSH credentials fetched for user '${sshCredentials.email ?? 'unknown'}'`\n );\n return sshCredentials;\n } catch (error) {\n this.logger.warn('Failed to fetch SSH credentials:', error);\n throw error;\n }\n }\n\n /**\n * Get current user information\n */\n async getCurrentUser(): Promise<PeriscopeApi.UserDto> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n try {\n this.logger.debug('Fetching current user from Periscope server...');\n const user = await this.apiClient.getCurrentUser();\n this.logger.debug(\n 'Successfully fetched current user from Periscope server'\n );\n return user;\n } catch (error) {\n this.logger.warn(\n 'Failed to fetch current user from Periscope server:',\n error\n );\n throw error;\n }\n }\n\n /**\n * Get current user's account status from the server.\n * Returns the status string (e.g., \"Active\", \"PendingApproval\").\n * Throws if the status cannot be determined (fail-closed).\n */\n async getUserStatus(): Promise<string> {\n const user = await this.getCurrentUser();\n if (!user.status) {\n throw new Error('Server did not return account status');\n }\n return user.status;\n }\n\n /**\n * Get current user's Terms of Service acceptance status.\n * Uses direct fetch since these endpoints may not yet be in the generated API client.\n */\n async getTermsStatus(): Promise<TermsStatus> {\n if (!(await this.isApiClientReady())) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n if (!this.authManager) {\n throw new Error(\n 'Auth manager not initialized. Cannot check terms status.'\n );\n }\n\n const token = await this.authManager.getValidToken();\n const response = await this.fetchWithOptions(\n `${this.config.serverUrl}/api/user/terms-status`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to get terms status: ${response.status} ${response.statusText}`\n );\n }\n\n return (await response.json()) as TermsStatus;\n }\n\n /**\n * Accept the Terms of Service for the current user.\n */\n async acceptTerms(termsVersion: string): Promise<void> {\n if (!(await this.isApiClientReady())) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n if (!this.authManager) {\n throw new Error('Auth manager not initialized. Cannot accept terms.');\n }\n\n const token = await this.authManager.getValidToken();\n const response = await this.fetchWithOptions(\n `${this.config.serverUrl}/api/user/accept-terms`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ termsVersion }),\n }\n );\n\n if (!response.ok) {\n throw new Error(\n `Failed to accept terms: ${response.status} ${response.statusText}`\n );\n }\n }\n\n /**\n * Update the user's slug for tunnel namespacing (ELF-166).\n * Requires authentication.\n */\n async updateSlug(request: { slug: string }): Promise<void> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n try {\n await this.apiClient.updateUserSlug(request);\n } catch (error: unknown) {\n // Re-throw with enhanced error info for 409 conflicts\n if (\n error &&\n typeof error === 'object' &&\n 'status' in error &&\n error.status === 409\n ) {\n throw Object.assign(new Error('Slug already in use'), {\n response: { status: 409 },\n });\n }\n throw error;\n }\n }\n\n /**\n * Validate the SSH host key received during the SSH handshake (ELF-198).\n * Sends the raw SSH wire-format public key bytes (base64-encoded) to the server,\n * which compares them against its own key using constant-time comparison.\n * Throws ApiException with status 422 if the key does not match (potential MITM).\n */\n async validateSshHostKey(keyBytes: string): Promise<void> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n await this.apiClient.validateSshHostKey({ keyBytes });\n }\n\n /**\n * Submit user feedback to create a Linear issue.\n * Requires authentication.\n */\n async submitFeedback(message: string): Promise<FeedbackResponse> {\n if (!(await this.isApiClientReady()) || !this.apiClient) {\n throw new Error('API client not initialized. Please authenticate first.');\n }\n\n try {\n this.logger.debug('Submitting feedback...');\n const response = await this.apiClient.submitFeedback({\n message,\n source: 'cli',\n });\n this.logger.debug('Feedback submitted successfully');\n return response;\n } catch (error) {\n this.logger.warn('Failed to submit feedback:', error);\n throw error;\n }\n }\n\n /**\n * Get the Application Insights connection string from the authenticated telemetry endpoint.\n * Returns null if not authenticated or if the server has no connection string configured.\n */\n async getTelemetryConnectionString(): Promise<string | null> {\n if (!(await this.isApiClientReady())) {\n return null;\n }\n try {\n const config = await this.apiClient!.telemetryGET();\n return config.applicationInsightsConnectionString ?? null;\n } catch (error) {\n this.logger.debug('Failed to fetch telemetry connection string:', error);\n return null;\n }\n }\n\n /**\n * Get the configuration object\n */\n getConfig(): PeriscopeConfig {\n return this.config;\n }\n}\n\n/**\n * Terms of Service status response from server\n */\nexport interface TermsStatus {\n accepted: boolean;\n termsVersion: string | null;\n acceptedAt: string | null;\n currentVersion: string;\n}\n\n/**\n * Feedback response from server\n */\nexport interface FeedbackResponse {\n success?: boolean;\n message?: string | undefined;\n}\n", "/**\n * Secure memory utilities for cleanup and process-lifecycle management.\n */\n\nimport { getLogger } from './logger.js';\nimport { isReadlineActive } from './readline-instance.js';\n\n// Define interface for tunnel manager cleanup\ninterface TunnelManagerLike {\n stopAll(): Promise<void>;\n}\n\n// Global tunnel manager registry for cleanup\nconst activeTunnelManagers = new Set<TunnelManagerLike>();\n\nexport function registerTunnelManager(tunnelManager: TunnelManagerLike): void {\n activeTunnelManagers.add(tunnelManager);\n}\n\nexport function unregisterTunnelManager(\n tunnelManager: TunnelManagerLike\n): void {\n activeTunnelManagers.delete(tunnelManager);\n}\n\nasync function stopActiveTunnelManagers(): Promise<void> {\n const logger = getLogger().child('SecureCleanup');\n const managers = [...activeTunnelManagers];\n activeTunnelManagers.clear();\n for (const tunnelManager of managers) {\n try {\n await tunnelManager.stopAll();\n } catch (error) {\n logger.error('Error during tunnel cleanup:', error);\n }\n }\n}\n\nlet setupDone = false;\n\n/**\n * Utility function to ensure cleanup happens even if process exits unexpectedly.\n * Idempotent \u2014 safe to call multiple times; handlers are registered only once.\n */\nexport function setupSecureCleanup(): void {\n if (setupDone) return;\n setupDone = true;\n\n let isCleaningUp = false;\n const logger = getLogger().child('SecureCleanup');\n\n const cleanup = async () => {\n if (isCleaningUp) return;\n isCleaningUp = true;\n logger.info('Cleaning up...');\n await stopActiveTunnelManagers();\n };\n\n // Dynamic import of gracefulExit to avoid circular deps at module load\n const exit = async (code: number) => {\n const { gracefulExit } = await import('./process-lifecycle.js');\n await gracefulExit(code);\n };\n\n // Handle interrupt signal - only if not in interactive mode with an active readline.\n // If readline has been closed (e.g. after a tunnel wizard in interactive mode),\n // there is no readline-level SIGINT handler left, so we must handle it here.\n process.on('SIGINT', async () => {\n if (process.env.PERISCOPE_INTERACTIVE === 'true' && isReadlineActive()) {\n // Readline is live \u2014 let the readline-level handler in interactive.ts take over\n return;\n }\n\n logger.info('Shutting down gracefully...');\n await cleanup();\n await exit(0);\n });\n\n // Handle termination signal\n process.on('SIGTERM', async () => {\n logger.info('Shutting down gracefully...');\n await cleanup();\n await exit(0);\n });\n\n // Handle uncaught exceptions\n process.on('uncaughtException', async error => {\n // ObjectDisposedError from SSH pipe flush on a disposed SshStream is\n // expected during connection loss (see request-monitor.ts).\n // The reconnection logic in tunnel-manager.ts handles recovery;\n // crashing the process would prevent it from running.\n if (isSshChannelDisposedError(error)) {\n logger.warn(\n `SSH channel disposed (connection lost) \u2014 reconnection will be attempted: ${error.message}`\n );\n return;\n }\n\n logger.error('Uncaught exception:', error);\n try {\n const { trackException } = await import('./telemetry.js');\n trackException(error, { source: 'uncaughtException' });\n } catch {\n // Best-effort\n }\n await cleanup();\n await exit(1);\n });\n\n // Handle unhandled promise rejections\n process.on('unhandledRejection', async (reason, promise) => {\n logger.error('Unhandled rejection at:', promise, 'reason:', reason);\n try {\n const { trackException } = await import('./telemetry.js');\n const error =\n reason instanceof Error ? reason : new Error(String(reason));\n trackException(error, { source: 'unhandledRejection' });\n } catch {\n // Best-effort\n }\n await cleanup();\n await exit(1);\n });\n}\n\n/**\n * Manual cleanup function that can be called by interactive mode\n */\nexport async function performSecureCleanup(): Promise<void> {\n const logger = getLogger().child('SecureCleanup');\n logger.info('Performing secure cleanup...');\n await stopActiveTunnelManagers();\n}\n\n/**\n * Detect ObjectDisposedError from disposed SSH channels.\n * These surface as uncaught exceptions when pipe() flushes data to a\n * disposed SshStream. They are non-fatal because the session onClosed/\n * onDisconnected handlers in tunnel-manager.ts trigger reconnection.\n *\n * Uses error.name check to avoid coupling this module to the SSH library.\n */\nexport function isSshChannelDisposedError(error: Error): boolean {\n return error.name === 'ObjectDisposedError';\n}\n", "import { PeriscopeApi } from '@elf-5/periscope-api-client';\nimport { PeriscopeClient } from './client.js';\nimport {\n setupSecureCleanup,\n registerTunnelManager,\n unregisterTunnelManager,\n} from './secure-memory.js';\nimport {\n SshClientSession,\n SshSessionConfiguration,\n SshClientCredentials,\n SshDisconnectReason,\n SshAuthenticationType,\n KeyPair,\n} from '@microsoft/dev-tunnels-ssh';\nimport { SshKeyManager, SshKeyNotFoundError } from './ssh-key-manager.js';\nimport { log } from './logger.js';\nimport { exitOrThrow, isInteractiveMode } from './interactive-utils.js';\nimport { displayTunnelInfo } from './tunnel-utils.js';\nimport {\n SshClient,\n PortForwardingService,\n} from '@microsoft/dev-tunnels-ssh-tcp';\nimport { ErrorClassifier, ErrorType } from './error-classifier.js';\nimport { setupRequestMonitor } from './request-monitor.js';\nimport * as net from 'net';\n\n// Constants for tunnel operations\nconst PORT_CHECK_TIMEOUT_MS = 2000;\n// TODO(ELF-124): Make port monitoring interval configurable via environment variable\nconst PORT_MONITORING_INTERVAL_MS = 3000;\nconst DEFAULT_SSH_TUNNEL_PORT = 2222;\n\n// Timeout for SSH host key validation API call.\n// If the HTTPS endpoint is unreachable mid-handshake we want a fast failure\n// rather than an indefinite hang awaiting e.authenticationPromise.\nconst SSH_HOST_KEY_VALIDATION_TIMEOUT_MS = 10_000;\n\n// Constants for SSH reconnection with exponential backoff\nconst RECONNECT_INITIAL_DELAY_MS = 1000;\nconst RECONNECT_MAX_DELAY_MS = 30000;\nconst RECONNECT_BACKOFF_MULTIPLIER = 2;\nconst RECONNECT_MAX_ATTEMPTS = 10;\n\n/**\n * Represents an active tunnel's information (ephemeral, not persisted)\n */\nexport interface TunnelInfo {\n name: string;\n clientPort: number;\n sshTunnelPort: number;\n isConnected: boolean;\n /** Wildcard hostname from server config (e.g., \"develop.elf5.com\") */\n wildcardHostname?: string;\n /** URL separator from server config (e.g., \"-\" or \".\") */\n urlSeparator?: string;\n /** SSH host for tunnel connections (from server config) */\n sshHost?: string;\n /** Local host override for Host header rewriting (e.g., \"myapp.local:3000\") */\n localHost?: string;\n /** Scheme used by the local service (\"http\" or \"https\"). Defaults to \"http\" on server. */\n localScheme?: string;\n /** User's unique 6-character slug for tunnel namespace isolation (ELF-166) */\n slug?: string;\n}\n\nexport class TunnelManager {\n private activeTunnels: Map<string, SshClientSession> = new Map();\n private sshClients: Map<string, SshClient> = new Map();\n private retryIntervals: Map<string, NodeJS.Timeout> = new Map();\n // Maps tunnel name to tunnel info for tracking local state\n private tunnelInfoMap: Map<string, TunnelInfo> = new Map();\n // Tracks active reconnection attempts per tunnel to prevent concurrent reconnects\n private reconnecting: Map<string, boolean> = new Map();\n // Tracks pending reconnection timeouts so they can be cancelled on cleanup\n private reconnectTimeouts: Map<string, NodeJS.Timeout> = new Map();\n // Tracks tunnels being intentionally closed by the client so onClosed handler\n // can distinguish client-initiated closes from server rejections\n private intentionalDisconnects: Set<string> = new Set();\n private clientConfig: ReturnType<PeriscopeClient['getConfig']>;\n // SSH key pair loaded once per TunnelManager instance (set in connect()).\n // Reusing the same KeyPair across reconnects avoids repeated disk reads and\n // ensures consistent auth if the key file is rotated while a tunnel is active.\n private sshKeyPair: KeyPair | null = null;\n\n constructor(private client: PeriscopeClient) {\n this.clientConfig = this.client.getConfig();\n if (!this.clientConfig.serverUrl) {\n log.error('No server URL configured for tunnel manager');\n throw new Error('Server URL is not configured');\n }\n // Setup secure cleanup for private keys\n setupSecureCleanup();\n // Register this tunnel manager for cleanup\n registerTunnelManager(this);\n }\n\n /**\n * Check if a local port is listening\n */\n private async isPortListening(\n port: number,\n host: string = 'localhost'\n ): Promise<boolean> {\n return new Promise(resolve => {\n const socket = new net.Socket();\n\n socket.setTimeout(PORT_CHECK_TIMEOUT_MS);\n socket.on('connect', () => {\n socket.destroy();\n resolve(true);\n });\n\n socket.on('timeout', () => {\n socket.destroy();\n resolve(false);\n });\n\n socket.on('error', () => {\n resolve(false);\n });\n\n socket.connect(port, host);\n });\n }\n\n /**\n * Start monitoring for local service availability and manage tunnel connection\n * TODO(ELF-122): Add connection queuing/debouncing to prevent race conditions\n * during rapid port state changes\n */\n private startPortMonitoring(tunnelInfo: TunnelInfo): void {\n const tunnelName = tunnelInfo.name;\n const localPort = tunnelInfo.clientPort;\n\n // Clear any existing interval\n if (this.retryIntervals.has(tunnelName)) {\n clearInterval(this.retryIntervals.get(tunnelName));\n }\n\n // Track the last known state to detect transitions\n let lastServiceState: boolean | null = null;\n let isReconnecting = false;\n\n const interval = setInterval(async () => {\n try {\n const currentServiceState = await this.isPortListening(localPort);\n const tunnelIsActive = this.activeTunnels.has(tunnelName);\n\n // Handle state transitions\n if (\n lastServiceState !== null &&\n lastServiceState !== currentServiceState\n ) {\n if (\n currentServiceState &&\n !tunnelIsActive &&\n !isReconnecting &&\n !this.reconnecting.get(tunnelName)\n ) {\n // Service came back online and tunnel is not active - reconnect\n log.debug(\n `Local service started on port ${localPort} - reconnecting tunnel`\n );\n isReconnecting = true;\n try {\n await this.createSSHConnection(tunnelInfo);\n } catch (error) {\n log.error(\n `Failed to reconnect tunnel: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n isReconnecting = false;\n } else if (!currentServiceState && tunnelIsActive) {\n // Service went offline and tunnel is active - disconnect\n log.debug(\n `Local service stopped on port ${localPort} - disconnecting tunnel`\n );\n await this.disconnectTunnel(tunnelName);\n }\n } else if (lastServiceState === null) {\n // Initial state logging\n if (currentServiceState) {\n log.debug(\n `Local service detected on port ${localPort} - tunnel active`\n );\n } else {\n log.debug(\n `No local service on port ${localPort} - tunnel will connect when service starts`\n );\n if (tunnelIsActive) {\n await this.disconnectTunnel(tunnelName);\n }\n }\n }\n\n lastServiceState = currentServiceState;\n } catch (error) {\n log.error(\n `Error in port monitoring: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }, PORT_MONITORING_INTERVAL_MS);\n\n this.retryIntervals.set(tunnelName, interval);\n }\n\n /**\n * Disconnect tunnel without stopping the monitoring\n */\n private async disconnectTunnel(tunnelName: string): Promise<void> {\n const session = this.activeTunnels.get(tunnelName);\n if (session) {\n this.intentionalDisconnects.add(tunnelName);\n await session.close(\n SshDisconnectReason.byApplication,\n 'Local service unavailable'\n );\n this.activeTunnels.delete(tunnelName);\n }\n\n const sshClient = this.sshClients.get(tunnelName);\n if (sshClient) {\n sshClient.dispose();\n this.sshClients.delete(tunnelName);\n }\n\n // Update tunnel info to reflect disconnected state\n const tunnelInfo = this.tunnelInfoMap.get(tunnelName);\n if (tunnelInfo) {\n tunnelInfo.isConnected = false;\n }\n }\n\n /**\n * Handle unexpected server disconnection by attempting reconnection with exponential backoff.\n * Only triggers when the disconnect was NOT initiated by the client (e.g., server restart).\n */\n private async handleServerDisconnect(tunnelName: string): Promise<void> {\n // Prevent concurrent reconnection attempts for the same tunnel\n if (this.reconnecting.get(tunnelName)) {\n log.debug(`Reconnection already in progress for tunnel '${tunnelName}'`);\n return;\n }\n\n const tunnelInfo = this.tunnelInfoMap.get(tunnelName);\n if (!tunnelInfo) {\n log.debug(\n `Tunnel '${tunnelName}' no longer tracked, skipping reconnection`\n );\n return;\n }\n\n // Clean up the dead session/client before reconnecting\n this.activeTunnels.delete(tunnelName);\n const sshClient = this.sshClients.get(tunnelName);\n if (sshClient) {\n sshClient.dispose();\n this.sshClients.delete(tunnelName);\n }\n tunnelInfo.isConnected = false;\n\n this.reconnecting.set(tunnelName, true);\n let attempt = 0;\n let delay = RECONNECT_INITIAL_DELAY_MS;\n\n const attemptReconnect = async (): Promise<void> => {\n // Check if tunnel was stopped while we were waiting\n if (\n !this.tunnelInfoMap.has(tunnelName) ||\n !this.reconnecting.get(tunnelName)\n ) {\n log.debug(`Reconnection cancelled for tunnel '${tunnelName}'`);\n this.reconnecting.delete(tunnelName);\n return;\n }\n\n attempt++;\n log.info(\n `Reconnecting tunnel '${tunnelName}' (attempt ${attempt}/${RECONNECT_MAX_ATTEMPTS})...`\n );\n\n // Check if local service is still running before attempting SSH reconnection\n const isServiceRunning = await this.isPortListening(\n tunnelInfo.clientPort\n );\n if (!isServiceRunning) {\n log.info(\n `Local service on port ${tunnelInfo.clientPort} is not running, deferring reconnection to port monitor`\n );\n this.reconnecting.delete(tunnelName);\n return;\n }\n\n try {\n await this.createSSHConnection(tunnelInfo);\n\n // Check if tunnel was disconnected while createSSHConnection was in progress\n if (\n !this.reconnecting.get(tunnelName) ||\n !this.tunnelInfoMap.has(tunnelName)\n ) {\n log.debug(\n `Tunnel '${tunnelName}' was disconnected during reconnection, cleaning up`\n );\n const newSession = this.activeTunnels.get(tunnelName);\n if (newSession) {\n this.intentionalDisconnects.add(tunnelName);\n await newSession.close(SshDisconnectReason.byApplication);\n this.activeTunnels.delete(tunnelName);\n }\n const newClient = this.sshClients.get(tunnelName);\n if (newClient) {\n newClient.dispose();\n this.sshClients.delete(tunnelName);\n }\n this.reconnecting.delete(tunnelName);\n return;\n }\n\n log.success(`Tunnel '${tunnelName}' reconnected successfully`);\n this.reconnecting.delete(tunnelName);\n this.reconnectTimeouts.delete(tunnelName);\n return;\n } catch (error) {\n const classified = ErrorClassifier.classify(error, 'tunnel-reconnect');\n const message =\n classified.type === ErrorType.NETWORK\n ? 'server unreachable (may still be restarting)'\n : classified.userMessage;\n log.warn(\n `Reconnection attempt ${attempt}/${RECONNECT_MAX_ATTEMPTS} for tunnel '${tunnelName}': ${message}`\n );\n }\n\n if (attempt >= RECONNECT_MAX_ATTEMPTS) {\n log.error(\n `Failed to reconnect tunnel '${tunnelName}' after ${RECONNECT_MAX_ATTEMPTS} attempts. Port monitor will retry when server becomes available.`\n );\n this.reconnecting.delete(tunnelName);\n this.reconnectTimeouts.delete(tunnelName);\n return;\n }\n\n // Schedule next attempt with exponential backoff\n delay = Math.min(\n delay * RECONNECT_BACKOFF_MULTIPLIER,\n RECONNECT_MAX_DELAY_MS\n );\n log.debug(`Next reconnection attempt for '${tunnelName}' in ${delay}ms`);\n\n const timeout = setTimeout(() => {\n attemptReconnect();\n }, delay);\n this.reconnectTimeouts.set(tunnelName, timeout);\n };\n\n // Start first reconnection attempt after initial delay\n log.warn(\n `Server connection lost for tunnel '${tunnelName}', will attempt reconnection...`\n );\n const timeout = setTimeout(() => {\n attemptReconnect();\n }, delay);\n this.reconnectTimeouts.set(tunnelName, timeout);\n }\n\n /**\n * Establishes SSH connection with authentication\n * TODO(ELF-123): Add configurable timeout for SSH connection establishment\n */\n private async createSSHConnection(tunnelInfo: TunnelInfo): Promise<void> {\n const name = tunnelInfo.name;\n\n // Get server config (wildcard hostname, slug, SSH host, port) from server\n const sshCredentials = await this.client.getSSHCredentials();\n\n // Store server config from credentials for URL construction and SSH connection\n if (sshCredentials.wildcardHostname) {\n tunnelInfo.wildcardHostname = sshCredentials.wildcardHostname;\n tunnelInfo.urlSeparator = sshCredentials.urlSeparator || '.';\n }\n if (sshCredentials.serverPort) {\n tunnelInfo.sshTunnelPort = sshCredentials.serverPort;\n }\n // Store user slug for URL construction (ELF-166)\n if (sshCredentials.slug) {\n tunnelInfo.slug = sshCredentials.slug;\n }\n\n // Load the client's locally-generated SSH key pair (ELF-184).\n // Use the KeyPair cached in connect() \u2014 avoids repeated disk reads on reconnect.\n if (!this.sshKeyPair) {\n throw new Error(\n 'SSH key pair not loaded. Call connect() before createSSHConnection().'\n );\n }\n const keyPair = this.sshKeyPair;\n\n // Re-register the public key on every connection attempt (including reconnects).\n // The server may have restarted and lost its in-memory or persistent record of\n // the key. Re-registering is idempotent on a persistent DB and essential on\n // in-memory DBs where server restarts wipe all state.\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n await this.client.registerPublicKey(publicKey);\n\n // Create SSH session configuration with port forwarding service\n const sessionConfig = new SshSessionConfiguration();\n sessionConfig.addService(PortForwardingService);\n const sshClient = new SshClient(sessionConfig);\n\n // Determine SSH hostname: use server-provided sshHost if available,\n // otherwise fall back to the API hostname (SMBs may reuse the same CNAME for HTTP + SSH)\n const baseHostname = new URL(this.clientConfig.serverUrl!).hostname;\n const serverHostname = sshCredentials.sshHost || baseHostname;\n tunnelInfo.sshHost = serverHostname;\n const serverPort = tunnelInfo.sshTunnelPort;\n\n log.debug(`Connecting to SSH server: ${serverHostname}:${serverPort}`);\n const session = await sshClient.openSession(serverHostname, serverPort);\n\n // Server authentication \u2014 verify host key against the server via API (ELF-198).\n // The CLI sends the raw SSH wire-format key bytes it received during the handshake\n // to POST /api/user/ssh/validate-host-key; the server compares them against its own\n // key using constant-time comparison. Any failure blocks the connection: a 422 means\n // key mismatch (potential MITM); any other error means the key could not be verified.\n session.onAuthenticating(e => {\n log.trace(`Server authentication event - Type: ${e.authenticationType}`);\n const serverIdentity = {\n identity: { isAuthenticated: true, name: 'server' },\n };\n\n if (e.authenticationType !== SshAuthenticationType.serverPublicKey) {\n e.authenticationPromise = Promise.reject(\n new Error(\n `Unexpected server authentication type: ${e.authenticationType}`\n )\n );\n return;\n }\n\n if (!e.publicKey) {\n e.authenticationPromise = Promise.reject(\n new Error('Server did not present a public key during SSH handshake')\n );\n return;\n }\n\n const pk = e.publicKey;\n log.trace(`Server public key algorithm: ${pk.keyAlgorithmName}`);\n\n e.authenticationPromise = (async () => {\n const keyBytes = await pk.getPublicKeyBytes(pk.keyAlgorithmName);\n if (!keyBytes) {\n throw new Error('Failed to read server public key bytes');\n }\n const base64KeyBytes = Buffer.from(keyBytes).toString('base64');\n\n try {\n let timeoutId: NodeJS.Timeout;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () =>\n reject(\n new Error(\n `timed out after ${SSH_HOST_KEY_VALIDATION_TIMEOUT_MS / 1000} s`\n )\n ),\n SSH_HOST_KEY_VALIDATION_TIMEOUT_MS\n );\n });\n try {\n await Promise.race([\n this.client.validateSshHostKey(base64KeyBytes),\n timeoutPromise,\n ]);\n } finally {\n clearTimeout(timeoutId!);\n }\n log.trace('SSH host key verified via server API');\n } catch (error) {\n if (\n PeriscopeApi.ApiException.isApiException(error) &&\n error.status === 422\n ) {\n throw new Error(\n 'SSH host key verification failed: the key received during the SSH handshake ' +\n 'does not match the server key. This may indicate a MITM attack. ' +\n 'Contact your administrator if the server was recently updated.'\n );\n }\n // Re-throw all other errors \u2014 without a successful validation we cannot\n // confirm the server's identity, so the connection is refused.\n throw new Error(\n `SSH host key validation failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n return serverIdentity;\n })();\n });\n\n const serverAuthenticated = await session.authenticateServer();\n if (!serverAuthenticated) {\n throw new Error('Server authentication failed');\n }\n\n // SSH username format: {email}:{subdomain}:{clientPort}[:{localScheme}][:{localHost}]\n // Email is used as the unique identifier (display names may not be unique).\n // The server uses this to configure the SSH reverse tunnel to forward to the correct client port.\n // Optional localScheme (only included when \"https\") and localHost configure YARP proxying.\n // The server detects localScheme by checking if the next segment is \"http\" or \"https\".\n let username = `${sshCredentials.email}:${tunnelInfo.name}:${tunnelInfo.clientPort}`;\n if (tunnelInfo.localScheme === 'https') {\n username += `:https`;\n if (tunnelInfo.localHost) {\n username += `:${tunnelInfo.localHost}`;\n }\n } else if (tunnelInfo.localHost) {\n username += `:${tunnelInfo.localHost}`;\n }\n const credentials: SshClientCredentials = {\n username,\n publicKeys: [keyPair],\n };\n\n const clientAuthenticated = await session.authenticateClient(credentials);\n if (!clientAuthenticated) {\n throw new Error(\n \"SSH key rejected by server. Run 'periscope user key generate' to re-register your key.\"\n );\n }\n\n // Store active session and client using tunnel name as key\n this.activeTunnels.set(name, session);\n this.sshClients.set(name, sshClient);\n\n // Hook request monitoring to log incoming HTTP requests in the CLI\n setupRequestMonitor(session, name);\n\n // Listen for server-initiated disconnects (e.g., duplicate subdomain rejection, server restart).\n // The server may close the session after auth succeeds if tunnel registration fails,\n // or the session may close if the periscope server restarts.\n session.onClosed(e => {\n tunnelInfo.isConnected = false;\n\n // Client-initiated close (disconnect/disconnectTunnel/stopAll) \u2014 no reconnection\n if (this.intentionalDisconnects.has(name)) {\n this.intentionalDisconnects.delete(name);\n return;\n }\n\n if (e.reason === SshDisconnectReason.byApplication && e.message) {\n // Server explicitly rejected the tunnel (e.g., duplicate subdomain)\n log.error(`Server rejected tunnel '${name}': ${e.message}`);\n // Clean up and exit \u2014 this is a fatal error, not recoverable\n this.intentionalDisconnects.add(name);\n this.disconnect(name)\n .then(() => {\n exitOrThrow(1);\n })\n .catch(() => {\n exitOrThrow(1);\n });\n return;\n }\n\n // Unexpected close \u2014 attempt reconnection (e.g., server crash/restart)\n if (e.reason !== SshDisconnectReason.none) {\n log.warn(\n `Tunnel '${name}' disconnected unexpectedly: ${e.message || e.reason}`\n );\n }\n this.handleServerDisconnect(name);\n });\n\n // Handle transport-level disconnection (TCP connection lost)\n session.onDisconnected(() => {\n if (!tunnelInfo.isConnected) {\n // Already handled by onClosed or intentional disconnect\n return;\n }\n tunnelInfo.isConnected = false;\n log.warn(`Tunnel '${name}' transport disconnected`);\n this.handleServerDisconnect(name);\n });\n\n // Update tunnel info to reflect connected state\n tunnelInfo.isConnected = true;\n\n if (isInteractiveMode()) {\n displayTunnelInfo(tunnelInfo, this.clientConfig.serverUrl!, {\n isInteractive: true,\n tunnelName: name,\n showBackgroundStatus: true,\n });\n } else {\n displayTunnelInfo(tunnelInfo, this.clientConfig.serverUrl!);\n }\n\n return;\n }\n\n /**\n * Connect to establish an ephemeral SSH tunnel\n * @param name - Unique name for this tunnel\n * @param localPort - Local port to forward through the tunnel\n * @param localHost - Optional Host header override for local service\n * @param localScheme - Optional scheme for local service (\"http\" or \"https\")\n * @param sshKeyPath - Optional path to SSH private key file\n */\n async connect(\n name: string,\n localPort: number,\n localHost?: string,\n localScheme?: string,\n sshKeyPath?: string\n ): Promise<TunnelInfo> {\n try {\n // Validate required parameters\n if (!name) {\n throw new Error('Tunnel name is required');\n }\n // Validate tunnel name format - must not contain characters that break SSH username parsing\n // SSH username format is: {username}:{subdomain}:{clientPort}\n if (!/^[a-zA-Z0-9_-]+$/.test(name)) {\n throw new Error(\n 'Tunnel name must contain only alphanumeric characters, hyphens, and underscores'\n );\n }\n if (!localPort || localPort <= 0 || localPort > 65535) {\n throw new Error('Valid local port is required (1-65535)');\n }\n // Validate localHost before it is forwarded as the HTTP Host header by YARP.\n // Whitelist: hostname chars, dots, hyphens, colons (port/IPv6), square brackets (IPv6).\n // Colons are intentionally allowed: the server rejoins parts[3..] so \"host:port\"\n // round-trips correctly (e.g. \"myapp.local:3000\").\n if (localHost !== undefined && !/^[a-zA-Z0-9.\\-:[\\]]+$/.test(localHost)) {\n throw new Error(\n 'localHost contains invalid characters (allowed: alphanumeric, hyphens, dots, colons, square brackets)'\n );\n }\n if (\n localScheme !== undefined &&\n localScheme !== 'http' &&\n localScheme !== 'https'\n ) {\n throw new Error('localScheme must be \"http\" or \"https\"');\n }\n\n // Load SSH key once for the lifetime of this connect() call (and all reconnects).\n // Caching avoids repeated disk reads during exponential-backoff reconnection and\n // ensures consistent auth if the key file is rotated while a tunnel is active.\n // Note: sshKeyPath is only honoured on the first connect() call; subsequent calls\n // (e.g. reconnects) reuse the cached KeyPair regardless of the sshKeyPath argument.\n if (!this.sshKeyPair) {\n this.sshKeyPair = await SshKeyManager.loadKeyPair(\n sshKeyPath ?? this.clientConfig.sshKeyPath\n );\n }\n\n // Create tunnel info (ephemeral, not persisted to server)\n const tunnelInfo: TunnelInfo = {\n name,\n clientPort: localPort,\n sshTunnelPort: DEFAULT_SSH_TUNNEL_PORT,\n isConnected: false,\n localHost,\n localScheme,\n };\n\n // Store tunnel info locally\n this.tunnelInfoMap.set(name, tunnelInfo);\n\n log.info(\n `Establishing tunnel '${name}' for local port ${localPort} on ${this.clientConfig.serverUrl}`\n );\n\n // Check if local service is available\n const isServiceRunning = await this.isPortListening(localPort);\n\n if (!isServiceRunning) {\n log.warn(`Local service not detected on port ${localPort}`);\n log.warn(\n 'Waiting for service - tunnel will connect when service becomes available'\n );\n\n // Start monitoring - it will establish SSH connection when service comes online\n this.startPortMonitoring(tunnelInfo);\n\n // Return tunnel info - monitoring is running and will connect when ready\n return tunnelInfo;\n }\n\n log.debug(\n `Local service detected on port ${localPort} - establishing tunnel connection`\n );\n\n // Establish SSH connection\n await this.createSSHConnection(tunnelInfo);\n\n // Start monitoring for local service throughout tunnel lifecycle\n this.startPortMonitoring(tunnelInfo);\n\n return tunnelInfo;\n } catch (error) {\n // Remove tunnel info on error\n this.tunnelInfoMap.delete(name);\n\n // Re-throw sentinel errors so callers can handle them by type.\n if (error instanceof SshKeyNotFoundError) {\n throw error;\n }\n\n throw new Error(\n `Failed to establish tunnel: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n /**\n * Get list of active tunnels managed by this CLI session\n */\n getActiveTunnels(): TunnelInfo[] {\n return Array.from(this.tunnelInfoMap.values());\n }\n\n /**\n * Disconnect the SSH connection for a tunnel\n */\n async disconnect(name: string): Promise<void> {\n // Cancel any pending reconnection attempts\n this.reconnecting.delete(name);\n const reconnectTimeout = this.reconnectTimeouts.get(name);\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n this.reconnectTimeouts.delete(name);\n }\n\n // Stop port monitoring\n const interval = this.retryIntervals.get(name);\n if (interval) {\n clearInterval(interval);\n this.retryIntervals.delete(name);\n }\n\n // Stop the SSH tunnel if active\n const session = this.activeTunnels.get(name);\n if (session) {\n this.intentionalDisconnects.add(name);\n await session.close(\n SshDisconnectReason.byApplication,\n 'Tunnel stopped by user'\n );\n this.activeTunnels.delete(name);\n }\n\n // Dispose SSH client if active\n const sshClient = this.sshClients.get(name);\n if (sshClient) {\n sshClient.dispose();\n this.sshClients.delete(name);\n }\n\n // Remove tunnel info\n this.tunnelInfoMap.delete(name);\n\n log.info(`Disconnected tunnel '${name}'`);\n }\n\n async stopAll(): Promise<void> {\n // Cancel all pending reconnection attempts\n for (const [, timeout] of this.reconnectTimeouts) {\n clearTimeout(timeout);\n }\n this.reconnectTimeouts.clear();\n this.reconnecting.clear();\n\n // Stop all port monitoring\n for (const [, interval] of this.retryIntervals) {\n clearInterval(interval);\n }\n this.retryIntervals.clear();\n\n // Close all active SSH sessions\n for (const [name, session] of this.activeTunnels) {\n this.intentionalDisconnects.add(name);\n await session.close(\n SshDisconnectReason.byApplication,\n 'All tunnels stopped'\n );\n }\n this.activeTunnels.clear();\n // Don't clear intentionalDisconnects here \u2014 let onClosed handlers\n // consume them individually to avoid a race where the handler fires\n // after the set is cleared and mistakenly triggers reconnection.\n\n // Dispose all SSH clients\n for (const [, sshClient] of this.sshClients) {\n sshClient.dispose();\n }\n this.sshClients.clear();\n\n this.sshKeyPair = null;\n\n // Clear tunnel info\n this.tunnelInfoMap.clear();\n }\n\n /**\n * Cleanup and unregister this tunnel manager\n */\n async dispose(): Promise<void> {\n await this.stopAll();\n unregisterTunnelManager(this);\n }\n}\n", "import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { log } from './logger.js';\nimport {\n SshAlgorithms,\n PublicKeyAlgorithm,\n KeyPair,\n} from '@microsoft/dev-tunnels-ssh';\nimport {\n exportPrivateKey,\n exportPublicKey,\n importKeyFile,\n} from '@microsoft/dev-tunnels-ssh-keys';\n\n// KeyFormat const enum values from @microsoft/dev-tunnels-ssh-keys@3.6.x.\n// Inlined to avoid runtime import of a const enum from a CJS package (esbuild limitation).\n// If these values change after a library upgrade, generateKeyPair() will produce keys in the\n// wrong format. The unit test asserting \"-----BEGIN PRIVATE KEY-----\" output will catch a\n// silent breakage.\n//\n// NOTE: KeyFormat.OpenSsh (6) is declared in keyFormat.d.ts but NOT implemented in the\n// runtime (no formatter registered for 6). Using Default (0) exports private keys as PKCS#8\n// (\"-----BEGIN PRIVATE KEY-----\"), which importKeyFile() can load via format auto-detection.\n// Migrate to 6 if a future library version implements OpenSsh export.\nconst KEY_FORMAT_SSH = 1; // KeyFormat.Ssh \u2014 \"ecdsa-sha2-nistp256 AAAA...\" public key\nconst KEY_FORMAT_PRIVATE = 0; // KeyFormat.Default \u2014 \"-----BEGIN PRIVATE KEY-----\" (PKCS#8)\n\nconst DEFAULT_KEY_DIR = path.join(os.homedir(), '.periscope');\nconst DEFAULT_PRIVATE_KEY_FILE = 'id_ecdsa';\n\nexport interface SshKeyInfo {\n exists: boolean;\n path: string;\n pubKeyPath: string;\n}\n\n/**\n * Thrown by SshKeyManager.loadKeyPair() when the private key file does not exist.\n * Caught by TunnelCommand to trigger the missing-key setup wizard.\n */\nexport class SshKeyNotFoundError extends Error {\n constructor(keyPath: string) {\n super(\n `SSH key not found at ${keyPath}. ` +\n `Run 'periscope user key generate' to generate and register a key.`\n );\n this.name = 'SshKeyNotFoundError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class SshKeyManager {\n /**\n * Returns the default SSH private key path.\n */\n static getDefaultKeyPath(): string {\n return path.join(DEFAULT_KEY_DIR, DEFAULT_PRIVATE_KEY_FILE);\n }\n\n /**\n * Generates a new ECDSA P-256 key pair and saves to disk.\n * Warns before overwriting an existing key \u2014 callers should confirm with the user\n * before calling this on an already-registered key path.\n */\n static async generateKeyPair(keyPath?: string): Promise<KeyPair> {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n const publicKeyPath = `${privateKeyPath}.pub`;\n\n if (fs.existsSync(privateKeyPath)) {\n log.warn(\n `Overwriting existing SSH key at ${privateKeyPath}. ` +\n 'Any active tunnels using the old key will need to reconnect after re-login.'\n );\n }\n\n // Ensure directory exists (mkdirSync with recursive is a no-op if it already exists)\n fs.mkdirSync(path.dirname(privateKeyPath), { recursive: true });\n\n // Generate ECDSA P-256 key pair using the dev-tunnels SSH library\n const algorithms = SshAlgorithms.publicKey as Record<\n string,\n PublicKeyAlgorithm | null\n >;\n const algorithm = algorithms['ecdsaSha2Nistp256'];\n if (!algorithm) {\n throw new Error('ECDSA P-256 algorithm not available');\n }\n const keyPair = await algorithm.generateKeyPair();\n\n // Export and save private key (PKCS#8 format: -----BEGIN PRIVATE KEY-----)\n // The private key is stored unencrypted (null passphrase). File mode 0o600 restricts\n // read access to the owner, but this does not protect against backup exfiltration or\n // shared filesystems. Use `--key <path>` with an independently-secured key if stronger\n // protection is required.\n const privateKeyStr = await exportPrivateKey(\n keyPair,\n null,\n KEY_FORMAT_PRIVATE\n );\n\n // Export and save public key (SSH format: ecdsa-sha2-nistp256 AAAA...)\n const publicKeyStr = await exportPublicKey(keyPair, KEY_FORMAT_SSH);\n\n // Write to temp files then rename to avoid leaving a corrupted or mismatched key pair\n // on disk if the process is killed (e.g. SIGKILL) between the two writes. fs.renameSync\n // is atomic on POSIX systems; on Windows it may overwrite but is still safer than a\n // partial writeFileSync.\n const tmpPrivatePath = `${privateKeyPath}.tmp`;\n const tmpPublicPath = `${publicKeyPath}.tmp`;\n try {\n fs.writeFileSync(tmpPrivatePath, privateKeyStr, { mode: 0o600 });\n fs.writeFileSync(tmpPublicPath, publicKeyStr, { mode: 0o644 });\n fs.renameSync(tmpPrivatePath, privateKeyPath);\n fs.renameSync(tmpPublicPath, publicKeyPath);\n } catch (err) {\n // Best-effort cleanup of temp files on failure\n for (const tmp of [tmpPrivatePath, tmpPublicPath]) {\n try {\n fs.unlinkSync(tmp);\n } catch {\n /* already renamed or never created */\n }\n }\n throw err;\n }\n\n log.debug(`SSH key pair generated at ${privateKeyPath}`);\n return keyPair;\n }\n\n /**\n * Loads an existing key pair from disk.\n * Throws if the key file does not exist.\n */\n static async loadKeyPair(keyPath?: string): Promise<KeyPair> {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n\n if (!fs.existsSync(privateKeyPath)) {\n throw new SshKeyNotFoundError(privateKeyPath);\n }\n\n try {\n return await importKeyFile(privateKeyPath, null);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // importKeyFile throws a generic crypto error for passphrase-protected keys.\n // Surface a more actionable message rather than a raw library trace.\n throw new Error(\n `Failed to load SSH key at ${privateKeyPath}: ${msg}. ` +\n 'If the key is passphrase-protected, use an unprotected key or specify ' +\n 'an alternative key path with --key.'\n );\n }\n }\n\n /**\n * Ensures a key pair exists, generating one if not.\n * Returns the loaded key pair.\n */\n static async ensureKeyPair(keyPath?: string): Promise<KeyPair> {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n\n if (!fs.existsSync(privateKeyPath)) {\n log.info('Generating SSH key pair...');\n return this.generateKeyPair(keyPath);\n }\n\n return this.loadKeyPair(keyPath);\n }\n\n /**\n * Exports the public key from an in-memory KeyPair in SSH wire format.\n * Use this after generateKeyPair() or ensureKeyPair() to avoid a redundant disk read.\n */\n static async exportPublicKey(keyPair: KeyPair): Promise<string> {\n return (await exportPublicKey(keyPair, KEY_FORMAT_SSH)).trim();\n }\n\n /**\n * Returns the public key string in SSH format (e.g. \"ecdsa-sha2-nistp256 AAAA...\").\n * Reads from the .pub file if present; otherwise imports the private key to derive it.\n */\n static async getPublicKeyString(keyPath?: string): Promise<string> {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n const publicKeyPath = `${privateKeyPath}.pub`;\n\n if (fs.existsSync(publicKeyPath)) {\n return fs.readFileSync(publicKeyPath, 'utf-8').trim();\n }\n\n // Derive from private key\n const keyPair = await this.loadKeyPair(keyPath);\n return (await exportPublicKey(keyPair, KEY_FORMAT_SSH)).trim();\n }\n\n /**\n * Returns info about the current SSH key (path, existence).\n */\n static getKeyInfo(keyPath?: string): SshKeyInfo {\n const privateKeyPath = keyPath ?? this.getDefaultKeyPath();\n return {\n exists: fs.existsSync(privateKeyPath),\n path: privateKeyPath,\n pubKeyPath: `${privateKeyPath}.pub`,\n };\n }\n}\n", "/**\n * Utility functions for interactive mode handling\n */\nimport { log } from './logger.js';\n\n/**\n * Check if we're currently running in interactive mode\n */\nexport function isInteractiveMode(): boolean {\n return process.env.PERISCOPE_INTERACTIVE === 'true';\n}\n\n/**\n * Exit or throw error based on context\n * In interactive mode, throws an error instead of calling process.exit\n *\n * Note: telemetry is flushed in the signal handlers (secure-memory.ts)\n * and the main CLI exit paths (cli.ts), not here. This function is sync\n * and cannot await an async flush.\n */\nexport function exitOrThrow(code: number, message?: string): never {\n if (isInteractiveMode()) {\n throw new Error(message || `Command failed with exit code ${code}`);\n } else {\n if (message) {\n log.error(message);\n }\n process.exit(code);\n }\n}\n", "import { log } from './logger.js';\n\n/**\n * Helper function to display tunnel information consistently\n */\nexport function displayTunnelInfo(\n tunnel: {\n id?: string | number;\n clientPort?: number;\n name?: string;\n remoteURL?: string;\n sshTunnelPort?: number;\n wildcardHostname?: string;\n urlSeparator?: string;\n sshHost?: string;\n slug?: string;\n },\n serverUrl: string,\n options?: {\n isInteractive?: boolean;\n tunnelName?: string;\n showBackgroundStatus?: boolean;\n showAsListItem?: boolean;\n }\n): void {\n const {\n isInteractive = false,\n tunnelName,\n showBackgroundStatus = false,\n showAsListItem = false,\n } = options || {};\n\n // Use SSH host from tunnel info if available, otherwise fall back to API hostname\n const serverHostname = tunnel.sshHost || new URL(serverUrl).hostname;\n\n // Use the remote URL from the server, or construct one from wildcard hostname config\n // Priority: 1) explicit remoteURL, 2) wildcardHostname from server, 3) fallback to serverUrl hostname\n let remoteUrl: string;\n if (tunnel.remoteURL) {\n remoteUrl = tunnel.remoteURL;\n } else if (tunnel.name && tunnel.wildcardHostname) {\n // Use server's wildcard config: {subdomain}-{slug}{separator}{wildcardHostname}\n // ELF-166: Server appends slug to tunnel name for namespace isolation\n const separator = tunnel.urlSeparator || '.';\n const fullSubdomain = tunnel.slug\n ? `${tunnel.name}-${tunnel.slug}`\n : tunnel.name;\n remoteUrl = `https://${fullSubdomain}${separator}${tunnel.wildcardHostname}`;\n } else if (tunnel.name) {\n // Fallback to server hostname (legacy behavior)\n const fullSubdomain = tunnel.slug\n ? `${tunnel.name}-${tunnel.slug}`\n : tunnel.name;\n remoteUrl = `https://${fullSubdomain}.${serverHostname}`;\n } else {\n remoteUrl = 'N/A';\n }\n\n // Show success message for interactive mode\n if (isInteractive && tunnelName) {\n log.success(`Tunnel connected: ${tunnelName}`);\n }\n\n if (showAsListItem) {\n // List format with bullet point and name\n log.info(` Local: localhost:${tunnel.clientPort || 'N/A'}`);\n log.info(` Server: ${serverHostname}:${tunnel.sshTunnelPort || 443}`);\n log.info(` \uD83C\uDF10 Remote URL: ${remoteUrl}`);\n log.info('\u2500'.repeat(60));\n } else {\n // Single tunnel format\n log.info(`Local: localhost:${tunnel.clientPort || 'N/A'}`);\n log.info(`Server: ${serverHostname}:${tunnel.sshTunnelPort || 443}`);\n log.info(`Remote URL: ${remoteUrl}`);\n }\n\n // Show background status for interactive mode\n if (showBackgroundStatus) {\n log.info('Status: Running in background');\n }\n}\n", "/**\n * Error classification system for enhanced error handling\n */\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * HTTP status code constants\n */\nconst HTTP_STATUS = {\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n REQUEST_TIMEOUT: 408,\n RATE_LIMITED: 429,\n INTERNAL_SERVER_ERROR: 500,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n CLOUDFLARE_ERROR: 522,\n ORIGIN_UNREACHABLE: 523,\n TIMEOUT: 524,\n} as const;\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\n/**\n * Common HTTP error response interface\n */\ninterface HttpErrorResponse {\n status?: number;\n statusCode?: number;\n code?: number | string;\n response?: {\n status?: number;\n statusCode?: number;\n };\n message?: string;\n}\n\n/**\n * Type-safe error object with known properties\n */\ntype ErrorWithProperties = Error & Partial<HttpErrorResponse>;\n\n// ============================================================================\n// Exported Types and Enums\n// ============================================================================\n\nexport enum ErrorType {\n AUTHENTICATION = 'authentication',\n AUTHORIZATION = 'authorization',\n NETWORK = 'network',\n VALIDATION = 'validation',\n SERVER = 'server',\n TUNNEL = 'tunnel',\n UNKNOWN = 'unknown',\n}\n\nexport enum ErrorSeverity {\n LOW = 'low',\n MEDIUM = 'medium',\n HIGH = 'high',\n CRITICAL = 'critical',\n}\n\nexport interface ClassifiedError {\n type: ErrorType;\n severity: ErrorSeverity;\n httpStatus?: number;\n isRetryable: boolean;\n userMessage: string;\n suggestedActions: string[];\n technicalDetails?: string;\n}\n\n// ============================================================================\n// Main Error Classifier Class\n// ============================================================================\n\n/**\n * Enhanced error classifier with granular categorization\n */\nexport class ErrorClassifier {\n /**\n * Classify an error based on various signals\n */\n static classify(error: unknown, context?: string): ClassifiedError {\n const errorMessage = this.extractMessage(error).toLowerCase();\n const httpStatus = this.extractHttpStatus(error);\n\n // Authentication errors\n if (this.isAuthenticationError(errorMessage, httpStatus)) {\n return this.createAuthenticationError(errorMessage, httpStatus);\n }\n\n // Authorization errors\n if (this.isAuthorizationError(errorMessage, httpStatus)) {\n return this.createAuthorizationError(errorMessage, httpStatus);\n }\n\n // Network errors\n if (this.isNetworkError(errorMessage, httpStatus)) {\n return this.createNetworkError(errorMessage, httpStatus);\n }\n\n // Validation errors\n if (this.isValidationError(errorMessage, httpStatus)) {\n return this.createValidationError(errorMessage, context);\n }\n\n // Server errors\n if (this.isServerError(errorMessage, httpStatus)) {\n return this.createServerError(errorMessage, httpStatus);\n }\n\n // Tunnel-specific errors\n if (this.isTunnelError(errorMessage, context)) {\n return this.createTunnelError(errorMessage, context);\n }\n\n // Default unknown error\n return this.createUnknownError(errorMessage, httpStatus);\n }\n\n /**\n * Extract message from various error formats\n */\n private static extractMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (error && typeof error === 'object') {\n const err = error as { message?: string };\n if (typeof err.message === 'string') {\n return err.message;\n }\n }\n return String(error);\n }\n\n private static extractHttpStatus(error: unknown): number | undefined {\n if (error && typeof error === 'object') {\n const err = error as ErrorWithProperties;\n\n // Check for numeric status codes in common properties\n if (typeof err.status === 'number') return err.status;\n if (typeof err.statusCode === 'number') return err.statusCode;\n if (typeof err.response?.status === 'number') return err.response.status;\n if (typeof err.response?.statusCode === 'number')\n return err.response.statusCode;\n if (typeof err.code === 'number') return err.code;\n\n // Extract from error message using regex\n // TODO(ELF-125): Improve regex to prevent false positives (e.g., \"line 404\", \"port 500\")\n const message = err.message || '';\n const statusMatch = message.match(/\\b([4-5]\\d{2})\\b/);\n if (statusMatch) {\n const statusCode = parseInt(statusMatch[1], 10);\n if (statusCode >= 400 && statusCode < 600) {\n return statusCode;\n }\n }\n }\n return undefined;\n }\n\n private static isAuthenticationError(\n message: string,\n status?: number\n ): boolean {\n const authKeywords = [\n 'authentication failed',\n 'token expired',\n 'invalid token',\n 'token invalid',\n 'authentication required',\n 'login required',\n 'session expired',\n 'unauthorized',\n 'not authenticated',\n 'invalid credentials',\n ];\n\n return (\n status === HTTP_STATUS.UNAUTHORIZED ||\n authKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isAuthorizationError(\n message: string,\n status?: number\n ): boolean {\n const authzKeywords = [\n 'forbidden',\n 'access denied',\n 'insufficient permissions',\n 'not authorized',\n 'permission denied',\n 'scope required',\n 'accountpendingapproval',\n 'maxusersexceeded',\n 'user limit reached',\n ];\n\n return (\n status === HTTP_STATUS.FORBIDDEN ||\n authzKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isNetworkError(message: string, status?: number): boolean {\n const networkKeywords = [\n 'connection timeout',\n 'network error',\n 'connection refused',\n 'connection reset',\n 'dns lookup failed',\n 'host not found',\n 'enotfound',\n 'econnrefused',\n 'econnreset',\n 'etimedout',\n 'socket hang up',\n 'network unreachable',\n 'rate limited',\n 'too many requests',\n 'request rate exceeded',\n 'fetch failed',\n 'server is unreachable',\n 'failed to fetch',\n ];\n\n const networkStatuses = [\n HTTP_STATUS.REQUEST_TIMEOUT,\n HTTP_STATUS.RATE_LIMITED,\n HTTP_STATUS.BAD_GATEWAY,\n HTTP_STATUS.SERVICE_UNAVAILABLE,\n HTTP_STATUS.GATEWAY_TIMEOUT,\n HTTP_STATUS.CLOUDFLARE_ERROR,\n HTTP_STATUS.ORIGIN_UNREACHABLE,\n HTTP_STATUS.TIMEOUT,\n ];\n\n return (\n (status !== undefined &&\n networkStatuses.includes(status as (typeof networkStatuses)[number])) ||\n networkKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isValidationError(message: string, status?: number): boolean {\n const validationKeywords = [\n 'validation error',\n 'invalid input',\n 'bad request',\n 'malformed',\n 'invalid format',\n 'missing required',\n 'invalid parameter',\n ];\n\n return (\n status === HTTP_STATUS.BAD_REQUEST ||\n validationKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isServerError(message: string, status?: number): boolean {\n const serverKeywords = [\n 'internal server error',\n 'server error',\n 'service unavailable',\n 'database error',\n 'configuration error',\n ];\n\n return (\n (status !== undefined && status >= 500 && status < 600) ||\n serverKeywords.some(keyword => message.includes(keyword))\n );\n }\n\n private static isTunnelError(message: string, context?: string): boolean {\n const tunnelKeywords = [\n 'tunnel',\n 'ssh connection',\n 'port forwarding',\n 'local port',\n 'remote port',\n 'address already in use',\n 'port already in use',\n 'bind failed',\n 'eaddrinuse',\n 'port is not available',\n 'connection already exists',\n 'failed to establish tunnel',\n ];\n\n // Pattern for \"Port X is already in use\" format\n const portInUsePattern = /port\\s+\\d+\\s+is\\s+already\\s+in\\s+use/i;\n\n const isTunnelContext = context?.toLowerCase().includes('tunnel');\n\n return (\n isTunnelContext ||\n tunnelKeywords.some(keyword => message.includes(keyword)) ||\n portInUsePattern.test(message)\n );\n }\n\n private static createAuthenticationError(\n message: string,\n status?: number\n ): ClassifiedError {\n const isTokenExpired = message.includes('expired');\n const isInvalidToken =\n message.includes('invalid') && message.includes('token');\n\n return {\n type: ErrorType.AUTHENTICATION,\n severity: ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: true,\n userMessage: isTokenExpired\n ? 'Your session has expired. Please log in again.'\n : isInvalidToken\n ? 'Your authentication token is invalid. Please log in again.'\n : 'Authentication required. Please log in.',\n suggestedActions: [\n 'Run: periscope auth login',\n 'Check status: periscope status',\n ],\n technicalDetails: isTokenExpired\n ? 'Token expiration detected'\n : isInvalidToken\n ? 'Invalid token format or signature'\n : 'Authentication credentials missing or invalid',\n };\n }\n\n private static createAuthorizationError(\n message: string,\n status?: number\n ): ClassifiedError {\n const isPendingApproval =\n message.includes('accountpendingapproval') ||\n message.includes('pending approval');\n\n const isMaxUsersExceeded =\n message.includes('maxusersexceeded') ||\n message.includes('user limit reached');\n\n if (isMaxUsersExceeded) {\n return {\n type: ErrorType.AUTHORIZATION,\n severity: ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: false,\n userMessage:\n 'Your organization has reached its maximum user limit. No new accounts can be created.',\n suggestedActions: [\n 'Contact your administrator to increase the user limit or remove inactive users',\n 'Check your license details with your administrator',\n ],\n technicalDetails: 'Server returned MaxUsersExceeded (HTTP 403)',\n };\n }\n\n return {\n type: ErrorType.AUTHORIZATION,\n severity: ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: false,\n userMessage: isPendingApproval\n ? 'Your account is pending approval by an administrator.'\n : 'Access denied. You do not have permission to perform this action.',\n suggestedActions: isPendingApproval\n ? [\n 'Wait for an administrator to approve your account',\n 'Check your account status: periscope status',\n ]\n : [\n 'Check your account permissions',\n 'Contact your administrator if you believe you should have access',\n 'Verify you are using the correct account: periscope status',\n ],\n technicalDetails: isPendingApproval\n ? 'Account status is PendingApproval'\n : 'Insufficient permissions for the requested operation',\n };\n }\n\n private static createNetworkError(\n message: string,\n status?: number\n ): ClassifiedError {\n const isTimeout = message.includes('timeout');\n const isConnectionRefused =\n message.includes('refused') || message.includes('econnrefused');\n const isDnsError = message.includes('enotfound') || message.includes('dns');\n const isServerUnreachable =\n message.includes('server is unreachable') ||\n message.includes('fetch failed');\n const isRateLimited =\n status === HTTP_STATUS.RATE_LIMITED ||\n message.includes('rate limited') ||\n message.includes('too many requests');\n\n // Extract server URL from error message if present (format: \"Server is unreachable (https://...): ...\")\n const urlMatch = message.match(/\\((https?:\\/\\/[^)]+)\\)/);\n const serverUrl = urlMatch ? urlMatch[1] : null;\n\n // Build user message with server URL context when available\n let userMessage: string;\n if (isServerUnreachable) {\n userMessage = serverUrl\n ? `Cannot connect to server at ${serverUrl}. Please check your network connection.`\n : 'Cannot connect to server. Please check your network connection and server URL.';\n } else if (isRateLimited) {\n userMessage =\n 'Too many requests. Please wait a few minutes and try again.';\n } else if (isDnsError) {\n userMessage = serverUrl\n ? `Cannot resolve server address for ${serverUrl}. Please check the server URL.`\n : 'Cannot resolve server address. Please check the server URL.';\n } else if (isConnectionRefused) {\n userMessage = serverUrl\n ? `Connection refused by ${serverUrl}. The server may be down or unreachable.`\n : 'Connection refused. The server may be down or unreachable.';\n } else if (isTimeout) {\n userMessage = 'Request timed out. Please try again.';\n } else {\n userMessage = 'Network error. Please check your connection.';\n }\n\n return {\n type: ErrorType.NETWORK,\n severity: isTimeout\n ? ErrorSeverity.MEDIUM\n : isRateLimited\n ? ErrorSeverity.LOW\n : ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: true,\n userMessage,\n suggestedActions: isRateLimited\n ? [\n 'Wait a few minutes before trying again',\n 'Reduce the frequency of requests',\n 'Check if you have exceeded the rate limit',\n ]\n : [\n 'Check your internet connection',\n 'Verify the server URL is correct: periscope config get',\n 'Check network connectivity and firewall settings',\n ],\n technicalDetails: isDnsError\n ? 'DNS resolution failed'\n : isConnectionRefused\n ? 'Connection refused by server'\n : isTimeout\n ? 'Request timeout'\n : isRateLimited\n ? 'API rate limit exceeded'\n : 'Network connectivity issue',\n };\n }\n\n private static createValidationError(\n message: string,\n context?: string\n ): ClassifiedError {\n return {\n type: ErrorType.VALIDATION,\n severity: ErrorSeverity.LOW,\n httpStatus: HTTP_STATUS.BAD_REQUEST,\n isRetryable: false,\n userMessage: 'Invalid input. Please check your command parameters.',\n suggestedActions: [\n 'Check the command parameters and try again',\n 'Review the command documentation: periscope --help',\n 'Ensure all required parameters are provided',\n ],\n technicalDetails: `Input validation failed${context ? ` in context: ${context}` : ''}: ${message}`,\n };\n }\n\n private static createServerError(\n message: string,\n status?: number\n ): ClassifiedError {\n const isServiceUnavailable =\n status === HTTP_STATUS.SERVICE_UNAVAILABLE ||\n message.includes('unavailable');\n\n return {\n type: ErrorType.SERVER,\n severity: ErrorSeverity.HIGH,\n httpStatus: status,\n isRetryable: isServiceUnavailable,\n userMessage: isServiceUnavailable\n ? 'Server is temporarily unavailable. Please try again later.'\n : 'Server error. Please try again or contact support.',\n suggestedActions: [\n 'Try again in a few minutes',\n 'Check server status or contact support if the problem persists',\n ],\n technicalDetails: isServiceUnavailable\n ? 'Service temporarily unavailable'\n : 'Internal server error',\n };\n }\n\n private static createTunnelError(\n message: string,\n context?: string\n ): ClassifiedError {\n const portInUsePattern = /port\\s+\\d+\\s+is\\s+already\\s+in\\s+use/i;\n const isPortError =\n (message.includes('port') &&\n (message.includes('use') || message.includes('bound'))) ||\n portInUsePattern.test(message);\n const isAddressInUse =\n message.includes('address already in use') ||\n message.includes('eaddrinuse');\n const isLocalServiceError =\n message.includes('local') && message.includes('service');\n const isBindError = message.includes('bind failed');\n const isConnectionFailed =\n message.includes('failed to establish tunnel') ||\n message.includes('ssh connection');\n\n return {\n type: ErrorType.TUNNEL,\n severity:\n isPortError || isAddressInUse || isBindError\n ? ErrorSeverity.HIGH\n : ErrorSeverity.MEDIUM,\n isRetryable: !(isPortError || isAddressInUse || isBindError),\n userMessage:\n isAddressInUse || isPortError\n ? 'Port is already in use. Try a different local port.'\n : isBindError\n ? 'Failed to bind to port. Check port availability and permissions.'\n : isLocalServiceError\n ? 'Local service is not running.'\n : isConnectionFailed\n ? 'Failed to establish SSH connection to tunnel server.'\n : 'Tunnel operation failed.',\n suggestedActions: [\n 'Try using a different local port',\n 'Verify local port is not already in use: netstat -tlnp | grep :PORT',\n 'Check tunnel connection: periscope connect <name> --target http://localhost:<port>',\n 'Ensure local service is running on the specified port',\n ],\n technicalDetails: `${\n isAddressInUse\n ? 'Address/port already in use (EADDRINUSE)'\n : isPortError\n ? 'Port conflict detected'\n : isBindError\n ? 'Port binding failed'\n : isLocalServiceError\n ? 'Local service not available'\n : isConnectionFailed\n ? 'SSH tunnel connection failed'\n : 'Tunnel operation failed'\n }${context ? ` in context: ${context}` : ''}`,\n };\n }\n\n private static createUnknownError(\n message: string,\n httpStatus?: number\n ): ClassifiedError {\n return {\n type: ErrorType.UNKNOWN,\n severity: ErrorSeverity.MEDIUM,\n httpStatus,\n isRetryable: true,\n userMessage: 'An unexpected error occurred. Please try again.',\n suggestedActions: [\n 'Try the operation again',\n 'Check the command documentation: periscope --help',\n 'Contact support if the issue persists',\n ],\n technicalDetails: `Unclassified error occurred: ${message}`,\n };\n }\n}\n", "import { Duplex } from 'stream';\n\nimport chalk from 'chalk';\n\nimport { SshClientSession } from '@microsoft/dev-tunnels-ssh';\nimport { PortForwardingService } from '@microsoft/dev-tunnels-ssh-tcp';\n\nimport { ConfigManager } from './config-manager.js';\nimport { log } from './logger.js';\nimport { isSshChannelDisposedError } from './secure-memory.js';\n\nconst HTTP_METHODS =\n /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|CONNECT|TRACE)\\s+(\\S+)/i;\nconst MAX_PEEK_BYTES = 256;\n\n/**\n * Parse an HTTP request line from a buffer and log it.\n * Returns true if a request line was found and logged.\n */\nexport function parseAndLogRequestLine(\n chunk: Buffer,\n _tunnelName: string\n): boolean {\n try {\n const str = chunk.toString(\n 'utf8',\n 0,\n Math.min(chunk.length, MAX_PEEK_BYTES)\n );\n const firstLine = str.split('\\r\\n')[0] || str.split('\\n')[0];\n const match = firstLine?.match(HTTP_METHODS);\n if (match) {\n log.raw(chalk.dim(` \u2190 ${match[1].toUpperCase()} ${match[2]}`));\n return true;\n }\n } catch {\n // Never let logging break the data path\n }\n return false;\n}\n\n/**\n * Intercept push() on a Duplex stream to peek at every data chunk\n * for HTTP request lines. This captures all requests on keep-alive\n * connections where multiple requests share a single stream.\n * Data flows through unchanged \u2014 this only observes, never modifies\n * or consumes data.\n */\nexport function monitorStream(stream: Duplex, tunnelName: string): void {\n const originalPush = stream.push.bind(stream);\n\n stream.push = function (chunk: unknown, encoding?: BufferEncoding): boolean {\n if (chunk != null) {\n try {\n const buf = Buffer.isBuffer(chunk)\n ? chunk\n : Buffer.from(chunk as string);\n parseAndLogRequestLine(buf, tunnelName);\n } catch {\n // Never interfere with data flow\n }\n }\n return originalPush(chunk, encoding);\n } as typeof stream.push;\n}\n\n/**\n * Hook into the PortForwardingService to log incoming HTTP requests.\n * Call this after SSH authentication succeeds.\n */\nexport function setupRequestMonitor(\n session: SshClientSession,\n tunnelName: string\n): void {\n const config = ConfigManager.load();\n const monitorEnabled = config.showRequestLog !== false;\n\n try {\n const pfs = session.activateService(PortForwardingService);\n pfs.onForwardedPortConnecting(e => {\n if (e.isIncoming) {\n // Guard against ObjectDisposedError when the SSH channel is disposed\n // while pipe() still has buffered data to flush. Without this handler\n // the error surfaces as an uncaught exception and crashes the process.\n // Reconnection is handled by session.onClosed/onDisconnected in\n // tunnel-manager.ts.\n e.stream.on('error', (err: Error) => {\n if (isSshChannelDisposedError(err)) {\n log.debug(\n `SSH stream closed for tunnel '${tunnelName}': ${err.message}`\n );\n } else {\n log.debug(\n `SSH stream error for tunnel '${tunnelName}': ${err.message}`\n );\n }\n });\n\n if (monitorEnabled) {\n monitorStream(e.stream, tunnelName);\n }\n }\n // Outgoing (client-initiated) streams don't pipe through the same\n // SshStream path and aren't subject to the disposal race.\n });\n log.debug(\n monitorEnabled\n ? `Request monitor active for tunnel '${tunnelName}'`\n : `Stream error handler active for tunnel '${tunnelName}' (request logging disabled)`\n );\n } catch (error) {\n log.debug(\n `Could not activate request monitor: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n}\n", "import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport * as dotenv from 'dotenv';\nimport { log } from './logger.js';\n\nexport interface PeriscopeConfig {\n serverUrl?: string;\n logLevel?: string; // ERROR, WARN, INFO, DEBUG, TRACE\n caCertPath?: string; // Path to custom CA certificate file (PEM format) for self-signed or corporate certs\n showRequestLog?: boolean; // Show live HTTP request log during tunnel sessions (default: true)\n sshKeyPath?: string; // Path to SSH private key file (default: ~/.periscope/id_ecdsa)\n allowExternalKey?: boolean; // When true, wizard offers option to use an existing key (enterprise feature, default: false)\n // Note: MSAL configuration is now fetched from the API\n}\n\nexport class ConfigManager {\n static readonly DEFAULT_SERVER_URL = 'https://periscope.elf5.com';\n\n private static readonly CONFIG_DIR = path.join(os.homedir(), '.periscope');\n private static readonly CONFIG_FILE = 'config.json';\n private static testConfig: PeriscopeConfig | null = null;\n\n static getConfigPath(): string {\n return path.join(this.CONFIG_DIR, this.CONFIG_FILE);\n }\n\n /**\n * Set a test configuration that bypasses file system operations\n * Only use this in tests!\n */\n static setTestConfig(config: PeriscopeConfig | null): void {\n if (process.env.NODE_ENV !== 'test' && !process.env.VITEST) {\n throw new Error('setTestConfig can only be used in test environment');\n }\n this.testConfig = config;\n }\n\n /**\n * Load configuration from file, environment variables, and .env file\n * Priority order: environment variables > .env file > config file\n */\n static load(): PeriscopeConfig {\n // If test config is set, use it as the base instead of file\n let config: PeriscopeConfig = {};\n\n if (this.testConfig !== null) {\n // In test mode, use test config as base\n config = { ...this.testConfig };\n } else {\n // Load .env file if it exists (only in non-test mode)\n this.loadDotEnv();\n\n // Start with config file\n try {\n const configPath = this.getConfigPath();\n if (fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, 'utf-8');\n config = JSON.parse(content);\n }\n } catch (error) {\n log.debug('Failed to load config:', error);\n }\n }\n\n // Override with environment variables (always applies)\n config = this.mergeEnvironmentVariables(config);\n\n // Fall back to hosted server if not configured\n if (!config.serverUrl) {\n config.serverUrl = ConfigManager.DEFAULT_SERVER_URL;\n }\n\n return config;\n }\n\n /**\n * Load .env file from current working directory or project root\n */\n private static loadDotEnv(): void {\n // Try loading .env from current working directory\n const cwd = process.cwd();\n const envPaths = [\n path.join(cwd, '.env'),\n path.join(cwd, '.env.local'),\n // Also try parent directories up to 3 levels\n path.join(cwd, '..', '.env'),\n path.join(cwd, '..', '..', '.env'),\n path.join(cwd, '..', '..', '..', '.env'),\n ];\n\n for (const envPath of envPaths) {\n if (fs.existsSync(envPath)) {\n dotenv.config({ path: envPath });\n break; // Use the first .env file found\n }\n }\n }\n\n /**\n * Merge environment variables into configuration\n */\n private static mergeEnvironmentVariables(\n config: PeriscopeConfig\n ): PeriscopeConfig {\n const env = process.env;\n\n // Main configuration\n if (env.PERISCOPE_SERVER_URL) {\n config.serverUrl = env.PERISCOPE_SERVER_URL;\n }\n if (env.PERISCOPE_LOG_LEVEL) {\n config.logLevel = env.PERISCOPE_LOG_LEVEL;\n }\n if (env.PERISCOPE_CA_CERT_PATH) {\n config.caCertPath = env.PERISCOPE_CA_CERT_PATH;\n }\n if (env.PERISCOPE_SHOW_REQUEST_LOG !== undefined) {\n config.showRequestLog = env.PERISCOPE_SHOW_REQUEST_LOG !== 'false';\n }\n if (env.PERISCOPE_SSH_KEY_PATH) {\n config.sshKeyPath = env.PERISCOPE_SSH_KEY_PATH;\n }\n if (env.PERISCOPE_ALLOW_EXTERNAL_KEY !== undefined) {\n config.allowExternalKey = env.PERISCOPE_ALLOW_EXTERNAL_KEY === 'true';\n }\n\n // MSAL configuration is now fetched from the API, no environment variables needed\n\n return config;\n }\n\n static save(config: PeriscopeConfig): void {\n // If in test mode, just update the test config\n if (this.testConfig !== null) {\n this.testConfig = { ...config };\n return;\n }\n\n try {\n // Ensure config directory exists\n if (!fs.existsSync(this.CONFIG_DIR)) {\n fs.mkdirSync(this.CONFIG_DIR, { recursive: true });\n }\n\n // Write config file\n const configPath = this.getConfigPath();\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2));\n\n // Set appropriate permissions (readable/writable by owner only)\n fs.chmodSync(configPath, 0o600);\n } catch (error) {\n throw new Error(\n `Failed to save config: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n}\n", "import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport { TunnelManager } from '../lib/tunnel-manager.js';\nimport { SshKeyManager, SshKeyNotFoundError } from '../lib/ssh-key-manager.js';\nimport { log } from '../lib/logger.js';\nimport { trackEvent } from '../lib/telemetry.js';\nimport {\n getReadlineInterface,\n closeReadline,\n} from '../lib/readline-instance.js';\nimport { exitOrThrow } from '../lib/interactive-utils.js';\nimport { ConfigManager } from '../lib/config-manager.js';\nimport { BaseCommand } from './base-command.js';\nimport type { PeriscopeClient } from '../lib/client.js';\n\n/** Wraps errors thrown during SSH key generation or registration. */\nclass SshKeySetupError extends Error {\n constructor(public override readonly cause: unknown) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n super(`SSH key setup failed: ${msg}`);\n this.name = 'SshKeySetupError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class TunnelCommand extends BaseCommand {\n /** Action constants for type-safe error handling */\n static readonly Action = {\n CONNECT: 'connect to tunnel',\n KEY_SETUP: 'set up SSH key',\n } as const;\n\n /**\n * Wizard invoked when no local SSH key is found.\n * By default, auto-generates a new key pair without prompting. When\n * allowExternalKey is true (future enterprise feature), presents a menu\n * that also allows providing a path to an existing key.\n *\n * Returns the resolved key path to use for the retry, or null if the user\n * cancels. Throws SshKeySetupError if key generation or registration fails.\n */\n private static async handleMissingKeyWizard(\n client: PeriscopeClient,\n customKeyPath?: string\n ): Promise<string | null> {\n if (!process.stdin.isTTY) {\n log.error(\n 'No SSH key found. Run `periscope user key generate` to generate and register one, ' +\n 'or provide an existing key path with --key.'\n );\n return null; // non-interactive; can't prompt user\n }\n\n const defaultPath = SshKeyManager.getDefaultKeyPath();\n const config = ConfigManager.load();\n\n // When allowExternalKey is enabled (enterprise), show the full menu.\n // Otherwise auto-generate without prompting.\n if (config.allowExternalKey) {\n return this.handleMissingKeyMenu(client, customKeyPath, defaultPath);\n }\n\n // Default path: auto-generate key without prompting\n const targetPath = customKeyPath ?? defaultPath;\n log.blank();\n log.info(`No SSH key found. Generating new key at ${targetPath}...`);\n try {\n const keyPair = await SshKeyManager.generateKeyPair(targetPath);\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n await client.registerPublicKey(publicKey);\n } catch (err) {\n throw new SshKeySetupError(err);\n }\n log.success('SSH key generated and registered successfully.');\n if (targetPath !== defaultPath) {\n config.sshKeyPath = targetPath;\n ConfigManager.save(config);\n log.info(`Key path saved to configuration.`);\n }\n return targetPath;\n }\n\n /**\n * Full menu wizard for missing SSH key. Only shown when allowExternalKey\n * is enabled in configuration. Allows generating a new key or providing\n * a path to an existing one.\n */\n private static async handleMissingKeyMenu(\n client: PeriscopeClient,\n customKeyPath: string | undefined,\n defaultPath: string\n ): Promise<string | null> {\n log.blank();\n log.warn('No SSH key found for tunnel authentication.');\n log.blank();\n log.info('Options:');\n log.info(\n ` 1. Generate a new SSH key at ${customKeyPath ?? defaultPath} (recommended)`\n );\n log.info(' 2. Use an existing SSH key from a different path');\n log.info(' 3. Cancel');\n\n const rl = getReadlineInterface();\n const choice = await new Promise<string>(resolve => {\n rl.question('Enter your choice [default: 1]: ', answer => {\n resolve(answer.trim() || '1');\n });\n });\n\n if (choice === '1') {\n const targetPath = customKeyPath ?? defaultPath;\n log.info(`Generating SSH key at ${targetPath}...`);\n try {\n const keyPair = await SshKeyManager.generateKeyPair(targetPath);\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n await client.registerPublicKey(publicKey);\n } catch (err) {\n throw new SshKeySetupError(err);\n }\n log.success('SSH key generated and registered successfully.');\n if (targetPath !== defaultPath) {\n const config = ConfigManager.load();\n config.sshKeyPath = targetPath;\n ConfigManager.save(config);\n log.info(`Key path saved to configuration.`);\n }\n return targetPath;\n } else if (choice === '2') {\n const existingPath = await new Promise<string>(resolve => {\n rl.question('Enter the full path to your SSH private key: ', answer => {\n resolve(answer.trim());\n });\n });\n if (!existingPath) {\n log.error('No path provided.');\n return null;\n }\n const expandedPath = existingPath.replace(/^~(?=\\/|$)/, os.homedir());\n if (!fs.existsSync(expandedPath)) {\n log.error(`No key file found at ${expandedPath}.`);\n return null;\n }\n log.info(`Registering public key from ${expandedPath}...`);\n try {\n const publicKey = await SshKeyManager.getPublicKeyString(expandedPath);\n await client.registerPublicKey(publicKey);\n } catch (err) {\n throw new SshKeySetupError(err);\n }\n log.success('SSH key registered successfully.');\n const config = ConfigManager.load();\n config.sshKeyPath = expandedPath;\n ConfigManager.save(config);\n log.info(`Key path saved to configuration.`);\n return expandedPath;\n } else if (choice === '3') {\n log.info('Cancelled.');\n return null;\n } else {\n log.error(`Invalid choice: \"${choice}\". Please enter 1, 2, or 3.`);\n return null;\n }\n }\n\n static parseTarget(target: string): {\n port: number;\n localHost?: string;\n localScheme?: string;\n } {\n let url: URL;\n try {\n url = new URL(target);\n } catch {\n throw new Error(\n `Invalid --target \"${target}\". Expected a URL like http://localhost:3000 or https://myapp.local:8443`\n );\n }\n if (url.pathname !== '/' || url.search !== '') {\n throw new Error(\n `--target must not include a path or query string (e.g., use \"http://localhost:3000\" not \"${target}\")`\n );\n }\n const scheme = url.protocol.slice(0, -1); // strip trailing ':'\n if (scheme !== 'http' && scheme !== 'https') {\n throw new Error(\n `Unsupported scheme \"${scheme}\" in --target. Only \"http\" and \"https\" are currently supported.`\n );\n }\n const hostname = url.hostname;\n const port = url.port\n ? parseInt(url.port, 10)\n : scheme === 'https'\n ? 443\n : 80;\n // Only send localScheme when it's https (http is the server default)\n const localScheme = scheme === 'https' ? 'https' : undefined;\n // Only set localHost when not loopback \u2014 server already defaults to localhost:{port}.\n // URL.hostname preserves IPv6 brackets: new URL('http://[::1]:3000').hostname === '[::1]'.\n const isLoopback =\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '[::1]';\n let localHost: string | undefined;\n if (!isLoopback) {\n const isStandardPort =\n (scheme === 'http' && port === 80) ||\n (scheme === 'https' && port === 443);\n localHost = isStandardPort ? hostname : `${hostname}:${port}`;\n }\n return { port, localHost, localScheme };\n }\n\n static async connect(name: string, target?: string, sshKeyPath?: string) {\n const { port, localHost, localScheme } = target\n ? this.parseTarget(target)\n : { port: 80, localHost: undefined, localScheme: undefined };\n\n log.info(`Establishing tunnel '${name}' for local port ${port}...`);\n if (localHost) {\n log.info(`Host header override: ${localHost}`);\n }\n\n try {\n const { client } = await this.setupClient(\n `Establishing tunnel '${name}'...`\n );\n const tunnelManager = new TunnelManager(client);\n\n let resolvedKeyPath = sshKeyPath;\n try {\n // Establish SSH tunnel; pass sshKeyPath so --key overrides the configured\n // path for this session without mutating the shared config object.\n await tunnelManager.connect(\n name,\n port,\n localHost,\n localScheme,\n resolvedKeyPath\n );\n } catch (innerError) {\n if (innerError instanceof SshKeyNotFoundError) {\n // No local key found \u2014 run wizard to generate or locate one, then retry.\n const wizardResult = await this.handleMissingKeyWizard(\n client,\n sshKeyPath\n );\n if (wizardResult === null) {\n closeReadline();\n exitOrThrow(1);\n }\n resolvedKeyPath = wizardResult;\n // Release stdin before the retry so the SSH library can attach its own\n // listeners without the dangling readline instance intercepting input.\n // If this retry also throws SshKeyNotFoundError (e.g. key unreadable\n // after generation), let it propagate to the outer catch as a normal\n // connect error. Re-entering the wizard here would risk an infinite loop.\n closeReadline();\n await tunnelManager.connect(\n name,\n port,\n localHost,\n localScheme,\n resolvedKeyPath\n );\n } else {\n throw innerError;\n }\n }\n\n trackEvent('tunnel_connect', {\n tunnelName: name,\n localPort: port.toString(),\n });\n\n // Keep process alive to maintain SSH connection until terminated.\n // Shutdown is handled by setupSecureCleanup() which calls stopAll()\n // on all registered TunnelManagers when SIGINT/SIGTERM is received.\n const keepAlive = setInterval(() => {}, 1000 * 60 * 60);\n process.once('exit', () => clearInterval(keepAlive));\n\n // Block until the process is about to exit (signal handlers call process.exit)\n await new Promise<void>(() => {});\n } catch (error) {\n if (error instanceof SshKeySetupError) {\n closeReadline();\n this.handleError(TunnelCommand.Action.KEY_SETUP, error.cause);\n } else {\n this.handleError(TunnelCommand.Action.CONNECT, error);\n }\n }\n }\n}\n", "import { PeriscopeClient, AccountStatus } from '../lib/client.js';\nimport { ConfigManager } from '../lib/config-manager.js';\nimport { exitOrThrow } from '../lib/interactive-utils.js';\nimport { log } from '../lib/logger.js';\nimport { PromptValue } from '../lib/auth-types.js';\nimport { getReadlineInterface } from '../lib/readline-instance.js';\nimport { ErrorClassifier } from '../lib/error-classifier.js';\nimport { TERMS_VERSION, TERMS_TEXT } from '../lib/terms.js';\nimport {\n initTelemetry,\n trackEvent,\n trackException,\n setUserContext,\n} from '../lib/telemetry.js';\n\nexport abstract class BaseCommand {\n /**\n * Common error handler for all commands\n * Uses ErrorClassifier for structured error handling based on HTTP status codes and error patterns\n * @param action - Action constant from the command's Actions object (e.g., TunnelCommand.Action.CREATE)\n * @param error - The error to handle\n */\n protected static handleError(action: string, error: unknown): never {\n log.debug(`handleError(${action}):`, error);\n\n const classified = ErrorClassifier.classify(error, action);\n\n log.debug(`Classified as: ${classified.type}`);\n\n trackException(error, {\n action,\n errorType: classified.type,\n userMessage: classified.userMessage,\n });\n\n // Display the user-friendly message from the classifier\n log.error(classified.userMessage);\n\n // Show troubleshooting tips for actionable errors\n if (classified.suggestedActions.length > 0) {\n log.blank();\n log.info('Troubleshooting:');\n classified.suggestedActions.forEach(tip => log.info(` \u2022 ${tip}`));\n }\n\n exitOrThrow(1);\n }\n\n /**\n * Display a warning that the account is pending approval.\n */\n protected static warnPendingApproval(): void {\n log.warn('Your account is pending approval by an administrator.');\n log.warn('You will be notified when your account has been approved.');\n log.warn('Most commands will be unavailable until your account is active.');\n }\n\n /**\n * Core authentication logic.\n * Device code auth is hidden behind PERISCOPE_ENABLE_DEVICE_CODE=true for internal use.\n * Returns the account status string if available.\n */\n static async authenticateWithChoice(\n client: PeriscopeClient,\n prompt: PromptValue = 'select_account'\n ): Promise<{ status: string | null; method: string }> {\n log.info('Authentication required...');\n\n const rl = getReadlineInterface();\n const useBrowser = await new Promise<boolean>((resolve, reject) => {\n log.blank();\n log.info('Choose authentication method:');\n log.info('1. Browser (recommended)');\n log.info('2. Device code (for WSL or headless environments)');\n try {\n rl.question('Enter your choice (1 or 2) [default: 1]: ', answer =>\n resolve((answer.trim() || '1') === '1')\n );\n } catch (error) {\n reject(error);\n }\n });\n\n log.info(\n useBrowser\n ? `Starting browser authentication (${prompt})...`\n : 'Starting device code authentication...'\n );\n\n const authResult = useBrowser\n ? await client.authenticateInteractive(prompt)\n : await client.authenticate();\n\n log.success('Authentication successful!');\n log.success('Successfully authenticated');\n log.info(` Token expires: ${authResult.expiresOn.toLocaleString()}`);\n log.info(\n useBrowser\n ? ` Authentication method: Browser (${prompt})`\n : ' Authentication method: Device Code'\n );\n\n const method = useBrowser ? 'browser' : 'device_code';\n\n // Check account status after authentication (best-effort \u2014 don't crash on failure)\n try {\n const status = await client.getUserStatus();\n if (status === AccountStatus.PENDING_APPROVAL) {\n log.blank();\n this.warnPendingApproval();\n }\n return { status, method };\n } catch {\n log.debug('Could not determine account status after authentication');\n return { status: null, method };\n }\n }\n\n /**\n * Load config, validate server URL, init telemetry, and create client.\n * Shared entry point for all command paths that need a PeriscopeClient.\n * Accepts an optional pre-loaded config to avoid double-loading.\n */\n protected static async initClient(\n existingConfig?: ReturnType<typeof ConfigManager.load>\n ): Promise<{\n client: PeriscopeClient;\n config: ReturnType<typeof ConfigManager.load>;\n }> {\n const config = existingConfig ?? ConfigManager.load();\n\n return { client: new PeriscopeClient(config), config };\n }\n\n /**\n * Check client version compatibility with the server.\n * Blocks on incompatible, warns on outdated, silent on compatible/null.\n */\n private static async checkVersionCompatibility(\n serverUrl: string\n ): Promise<void> {\n try {\n // Get package version\n const packageJson = await import('../../package.json', {\n with: { type: 'json' },\n });\n const clientVersion = packageJson.default.version;\n\n // Fetch server config with clientVersion parameter\n const response = await fetch(\n `${serverUrl}/api/configuration?clientVersion=${encodeURIComponent(clientVersion)}`\n );\n\n if (!response.ok) {\n // Fail-open: if endpoint doesn't exist or errors, proceed silently\n return;\n }\n\n const config = (await response.json()) as {\n compatibilityStatus?: string | null;\n compatibilityMessage?: string | null;\n minimumVersion?: string | null;\n recommendedVersion?: string | null;\n latestVersion?: string | null;\n };\n\n // Check compatibility status\n if (config.compatibilityStatus === 'incompatible') {\n log.blank();\n log.error('CLIENT VERSION INCOMPATIBLE');\n log.blank();\n log.error(\n config.compatibilityMessage ||\n `Your client version (${clientVersion}) is incompatible with this server.`\n );\n log.error(`Minimum required version: ${config.minimumVersion}`);\n log.error(`Latest version: ${config.latestVersion}`);\n log.blank();\n log.error('Please upgrade:');\n log.error(' npm update -g @elf5/periscope');\n exitOrThrow(1);\n } else if (config.compatibilityStatus === 'outdated') {\n log.blank();\n log.warn(\n config.compatibilityMessage ||\n `Your client version (${clientVersion}) is outdated.`\n );\n log.warn(`Recommended version: ${config.recommendedVersion}`);\n log.warn(`Latest version: ${config.latestVersion}`);\n log.warn('Consider upgrading: npm update -g @elf5/periscope');\n log.blank();\n }\n // 'compatible' or null: proceed silently\n } catch (error) {\n // Fail-open: network errors or missing endpoint don't block the CLI\n log.debug('Version compatibility check failed:', error);\n }\n }\n\n /**\n * Common setup for all commands - handles config loading and authentication\n */\n static async setupClient(actionDescription?: string): Promise<{\n client: PeriscopeClient;\n config: ReturnType<typeof ConfigManager.load>;\n }> {\n const { client, config } = await this.initClient();\n\n // Check version compatibility (fail-open: errors don't block)\n await this.checkVersionCompatibility(config.serverUrl!);\n\n // Check authentication; authenticateWithChoice returns the account status and auth method\n let status: string | null = null;\n let authMethod: string | null = null;\n if (!(await client.isAuthenticated())) {\n ({ status, method: authMethod } =\n await this.authenticateWithChoice(client));\n if (actionDescription) {\n log.info(actionDescription);\n }\n }\n\n // Fetch user (and derive status) if not already known (fail-closed: let errors propagate)\n let cachedUser: Awaited<ReturnType<typeof client.getCurrentUser>> | null =\n null;\n if (status === null) {\n cachedUser = await client.getCurrentUser();\n if (!cachedUser.status) {\n throw new Error('Server did not return account status');\n }\n status = cachedUser.status;\n } else {\n // status came from authenticateWithChoice; fetch user so telemetry can be enriched\n try {\n cachedUser = await client.getCurrentUser();\n } catch {\n // non-critical\n }\n }\n\n // Initialize telemetry and enrich with user context (best-effort, post-auth)\n try {\n const cs = await client.getTelemetryConnectionString();\n if (cs) await initTelemetry(cs);\n setUserContext(cachedUser?.email);\n // Re-emit auth_login now that telemetry is initialized (was a no-op when fired in authenticateWithChoice)\n if (authMethod) trackEvent('auth_login', { method: authMethod });\n } catch {\n // Non-critical \u2014 telemetry works without user context\n }\n\n // Block commands for pending accounts\n if (status === AccountStatus.PENDING_APPROVAL) {\n log.blank();\n this.warnPendingApproval();\n log.blank();\n log.info('Available commands while pending:');\n log.info(\n ' periscope status Show server, auth, and SSH key status'\n );\n log.info(' periscope auth logout Sign out');\n exitOrThrow(1);\n }\n\n // Check Terms of Service acceptance\n await this.ensureTermsAccepted(client);\n\n return { client, config };\n }\n\n /**\n * Check if user has accepted the current Terms of Service.\n * If not, display embedded TOS and prompt for acceptance.\n */\n private static async ensureTermsAccepted(\n client: PeriscopeClient\n ): Promise<void> {\n try {\n const termsStatus = await client.getTermsStatus();\n\n if (termsStatus.accepted) {\n return; // Already accepted current version\n }\n\n // Display TOS and prompt\n log.blank();\n log.info('Welcome to Periscope!');\n log.blank();\n log.info(\n 'Before you get started, please review and accept our Terms of Service.'\n );\n log.blank();\n console.log(TERMS_TEXT);\n log.blank();\n\n const accepted = await this.promptTermsAcceptance();\n\n if (!accepted) {\n log.error('You must accept the Terms of Service to use Periscope.');\n exitOrThrow(1);\n }\n\n // Record acceptance on server\n await client.acceptTerms(TERMS_VERSION);\n log.success('Terms of Service accepted.');\n log.blank();\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n // Skip gracefully when terms can't be checked:\n // - Server doesn't support terms endpoints yet (404)\n // - Auth manager not initialized (cached-token-only mode)\n if (\n errorMessage.includes('404') ||\n errorMessage.includes('Auth manager not initialized')\n ) {\n return;\n }\n throw error;\n }\n }\n\n /**\n * Prompt user to accept or decline Terms of Service.\n */\n private static async promptTermsAcceptance(): Promise<boolean> {\n const rl = getReadlineInterface();\n return new Promise(resolve => {\n rl.question('Do you accept the Terms of Service? [y/N] ', answer => {\n resolve(answer.trim().toLowerCase() === 'y');\n });\n });\n }\n}\n", "/**\n * Embedded Terms of Service for Periscope CLI.\n * This is the beta version - a simplified TOS displayed inline in the terminal.\n * See ELF-139 for future: hosted TOS pages, versioning/re-acceptance, etc.\n */\n\nexport const TERMS_VERSION = '2026-02-14';\n\nexport const TERMS_TEXT = `\nPERISCOPE TERMS OF SERVICE\nVersion: ${TERMS_VERSION}\n\nCopyright (c) 2024-2026 Elf 5. All rights reserved.\n\nBy using Periscope (\"the Service\"), you agree to the following terms:\n\n1. ACCEPTANCE\n By accessing or using Periscope, you agree to be bound by these Terms\n of Service. If you do not agree, you may not use the Service.\n\n2. BETA PROGRAM\n IMPORTANT: Periscope is currently in beta. By participating, you acknowledge:\n - Beta products may contain bugs, errors, or incomplete features\n - We may collect feedback and usage data to improve our products\n - Beta access is provided \"as is\" without warranties of any kind\n - We reserve the right to modify or terminate the beta at any time without notice\n - The service may experience downtime, interruptions, or changes during the beta period\n\n3. SERVICE DESCRIPTION\n Periscope provides secure SSH tunnel services that allow you to expose\n local development services through publicly accessible URLs.\n\n4. ACCOUNT SECURITY\n Periscope uses email-based passcode authentication. You are responsible for:\n - Maintaining the security of the email address associated with your account\n - Not sharing authentication passcodes sent to your email\n - Promptly notifying us if you receive unexpected passcodes or suspect unauthorized access\n - Ensuring your email address remains current and accessible\n - All activities that occur using passcodes sent to your email address\n\n We are not responsible for unauthorized access resulting from compromise of your\n email account, sharing of passcodes, or use of an insecure or shared email address.\n\n5. ACCEPTABLE USE\n You agree to use Periscope only for lawful purposes. You shall not:\n - Use the Service to transmit harmful, illegal, or offensive content\n - Violate any applicable laws or regulations\n - Infringe upon the rights of others\n - Distribute malicious software or engage in harmful activities\n - Attempt to gain unauthorized access to other systems through tunnels\n - Use the Service to circumvent network security policies\n - Share your credentials or tunnel access with unauthorized parties\n - Interfere with the proper functioning of our services\n\n6. DATA AND PRIVACY\n - Tunnel traffic passes through Periscope infrastructure\n - We collect minimal usage data (tunnel names, connection times, user identity)\n - We do not inspect or store the content of your tunnel traffic\n - Your authentication data is managed by your identity provider\n - Your privacy is important to us. Our collection and use of personal information\n is governed by our Privacy Policy\n\n Data Access and Disclosure:\n We reserve the right to access, preserve, and disclose your account information\n and data if required by law or if we believe such action is necessary to:\n (a) comply with legal process or government requests; (b) enforce these Terms;\n (c) respond to claims of violation of third-party rights; or (d) protect the\n rights, property, or safety of Elf 5, our users, or the public.\n\n7. DISCLAIMERS AND LIMITATIONS\n Disclaimer of Warranties:\n Our services are provided \"as is\" and \"as available\" without warranties of any kind,\n either express or implied, including but not limited to warranties of merchantability,\n fitness for a particular purpose, or non-infringement.\n\n Limitation of Liability:\n To the maximum extent permitted by law, Elf 5 shall not be liable for any indirect,\n incidental, special, consequential, or punitive damages, or any loss of profits or\n revenues, whether arising from:\n - Your use or inability to use our services\n - Services, applications, or data you expose through Periscope tunnels\n - Unauthorized access to your tunnels or services due to your configuration choices\n - Security vulnerabilities in services you make accessible through our platform\n - Data breaches or exposure of sensitive information through tunnels you create\n - Any actions taken by third parties who access services through your tunnels\n\n User Responsibility:\n You are solely responsible for the security, configuration, and content of any\n services you expose through Periscope. You acknowledge that creating publicly\n accessible tunnels to local services carries inherent security risks, and you\n assume all such risks.\n\n Liability Cap:\n In no event shall Elf 5's total aggregate liability exceed the greater of:\n (i) $100 USD or (ii) the total fees paid by you to Elf 5 in the twelve (12)\n months immediately preceding the claim.\n\n8. TERMINATION\n We may terminate or suspend your access to the Service at any time, with or\n without cause or notice. Upon termination, your right to use our services will\n cease immediately.\n\n9. CHANGES TO TERMS\n We reserve the right to modify these Terms at any time. We will notify you of\n any changes by updating the \"Version\" date. Your continued use of our services\n after such modifications constitutes acceptance of the updated Terms.\n\nFor questions, contact: hello@elf5.com\n`;\n", "import { ConfigManager } from '../lib/config-manager.js';\nimport { log } from '../lib/logger.js';\nimport { exitOrThrow } from '../lib/interactive-utils.js';\nimport { BaseCommand } from './base-command.js';\n\nexport class ConfigCommand extends BaseCommand {\n /** Action constants for type-safe error handling */\n static readonly Action = {\n SET: 'update configuration',\n SHOW: 'load configuration',\n } as const;\n\n static async set(options: { server?: string; requestLog?: string }) {\n try {\n const config = ConfigManager.load();\n let hasChanges = false;\n\n // Check if any options were provided\n if (!options.server && options.requestLog === undefined) {\n log.warn('No configuration options provided.');\n log.header('Available configuration options:');\n log.info(' --server <url> Set the Periscope server URL');\n log.info(\n ' --request-log <bool> Show live HTTP request log during tunnel sessions (true/false)'\n );\n log.header('Examples:');\n log.info(' periscope config set --server https://periscope.elf5.com');\n log.info(' periscope config set --request-log false');\n log.blank();\n log.info('To view current configuration, use: periscope config show');\n exitOrThrow(1);\n }\n\n if (options.server) {\n // Validate URL\n try {\n new URL(options.server);\n config.serverUrl = options.server;\n log.success(`Server URL set to: ${options.server}`);\n hasChanges = true;\n } catch {\n // URL validation is a simple user input error - keep message simple\n log.error('Invalid server URL format');\n exitOrThrow(1);\n }\n }\n\n if (options.requestLog !== undefined) {\n config.showRequestLog = options.requestLog !== 'false';\n log.success(`Request log set to: ${config.showRequestLog}`);\n hasChanges = true;\n }\n\n if (hasChanges) {\n ConfigManager.save(config);\n log.info('Configuration saved successfully');\n }\n } catch (error) {\n this.handleError(ConfigCommand.Action.SET, error);\n }\n }\n\n static async show() {\n try {\n const config = ConfigManager.load();\n\n log.header('Periscope Configuration:');\n log.separator(50);\n\n // Main configuration\n ConfigCommand.showConfigValue(\n 'Server URL ',\n config.serverUrl,\n 'PERISCOPE_SERVER_URL',\n ConfigManager.DEFAULT_SERVER_URL\n );\n ConfigCommand.showConfigValue(\n 'Request Log ',\n String(config.showRequestLog ?? true),\n 'PERISCOPE_SHOW_REQUEST_LOG'\n );\n\n log.info('Config file: ' + ConfigManager.getConfigPath());\n log.separator(50);\n } catch (error) {\n this.handleError(ConfigCommand.Action.SHOW, error);\n }\n }\n\n private static showConfigValue(\n label: string,\n value: string | undefined,\n envVar: string,\n defaultValue?: string\n ): void {\n const envValue = process.env[envVar];\n const isFromEnv = envValue !== undefined && envValue !== '';\n const isDefault = !isFromEnv && value === defaultValue;\n const displayValue = value || 'Not configured';\n const source = isFromEnv ? ' (from env)' : isDefault ? ' (default)' : '';\n\n log.info(`${label}: ${displayValue}${source}`);\n }\n}\n", "import { AccountStatus } from '../lib/client.js';\nimport { ConfigManager } from '../lib/config-manager.js';\nimport { log } from '../lib/logger.js';\nimport { getServerConfig } from '../lib/server-config.js';\nimport { SshKeyManager } from '../lib/ssh-key-manager.js';\nimport { trackEvent } from '../lib/telemetry.js';\nimport { BaseCommand } from './base-command.js';\n\nexport class StatusCommand extends BaseCommand {\n static readonly Action = {\n CHECK: 'check status',\n } as const;\n\n static async check() {\n try {\n const config = ConfigManager.load();\n\n log.header('Periscope Status:');\n log.separator();\n\n if (!config.serverUrl) {\n log.info('Server URL: Not configured');\n log.info('Run: periscope config set --server <server-url>');\n log.separator();\n return;\n }\n\n log.info('Server URL: ' + config.serverUrl);\n\n const { client } = await this.initClient(config);\n\n // Server health\n try {\n const health = await client.checkHealth();\n log.info(\n 'Server: ' + (health.healthy ? '\u25CF Healthy' : '\u25CF Unhealthy')\n );\n if (health.version) {\n log.info('Server Version: ' + health.version);\n }\n } catch {\n log.info('Server: \u25CF Unreachable');\n }\n\n // Client version\n const packageJson = await import('../../package.json', {\n with: { type: 'json' },\n });\n log.info('Client Version: ' + packageJson.default.version);\n\n // Auth provider\n try {\n const serverConfig = await getServerConfig(config.serverUrl);\n log.info('Auth Provider: ' + (serverConfig.authProvider || 'Default'));\n } catch {\n // Skip if server unreachable\n }\n\n const isAuthenticated = await client.isAuthenticated();\n log.info(\n 'Auth: ' +\n (isAuthenticated ? '\u25CF Authenticated' : '\u25CF Not authenticated')\n );\n\n // SSH key local status \u2014 shown regardless of auth state\n const keyInfo = SshKeyManager.getKeyInfo(config.sshKeyPath);\n log.info(\n 'SSH Key: ' +\n (keyInfo.exists ? `\u25CF Present (${keyInfo.path})` : '\u25CF Not generated')\n );\n if (!keyInfo.exists) {\n log.info('Run: periscope user key generate');\n }\n\n if (isAuthenticated) {\n // Account status\n let isPendingApproval = false;\n try {\n const accountStatus = await client.getUserStatus();\n if (accountStatus) {\n isPendingApproval =\n accountStatus === AccountStatus.PENDING_APPROVAL;\n const statusDisplay = isPendingApproval\n ? '\u23F3 Pending Approval'\n : accountStatus === AccountStatus.ACTIVE\n ? '\u25CF Active'\n : accountStatus;\n log.info('Account: ' + statusDisplay);\n }\n } catch {\n // Skip on older server versions\n }\n\n // Slug, tunnel format, and server-side key metadata\n try {\n const sshCreds = await client.getSSHCredentials();\n if (sshCreds.slug) {\n log.info('User Slug: ' + sshCreds.slug);\n log.info(\n 'Tunnel Format: {name}-' +\n sshCreds.slug +\n '.' +\n (sshCreds.wildcardHostname || 'domain')\n );\n }\n if (keyInfo.exists && sshCreds.keyGeneratedAt) {\n const registeredDate = new Date(sshCreds.keyGeneratedAt);\n if (!isNaN(registeredDate.getTime())) {\n log.info('Key Registered: ' + registeredDate.toLocaleString());\n }\n }\n } catch {\n // Skip if SSH creds unavailable (older server or pending approval)\n }\n\n if (isPendingApproval) {\n log.blank();\n log.warn('Your account is pending approval by an administrator.');\n log.warn(\n 'Most commands will be unavailable until your account is active.'\n );\n }\n } else {\n log.info('Run: periscope auth login');\n }\n\n log.info(\n 'Request Log: ' +\n (config.showRequestLog !== false ? '\u25CF Enabled' : '\u25CB Disabled')\n );\n trackEvent('status_check', { authenticated: isAuthenticated.toString() });\n log.separator();\n } catch (error) {\n this.handleError(StatusCommand.Action.CHECK, error);\n }\n }\n}\n", "import { ConfigManager } from '../lib/config-manager.js';\nimport { PromptValue } from '../lib/auth-types.js';\nimport { log } from '../lib/logger.js';\nimport { SshKeyManager } from '../lib/ssh-key-manager.js';\nimport { trackEvent } from '../lib/telemetry.js';\nimport { BaseCommand } from './base-command.js';\n\nexport class AuthCommand extends BaseCommand {\n /** Action constants for type-safe error handling */\n static readonly Action = {\n LOGIN: 'authenticate',\n LOGOUT: 'logout',\n } as const;\n\n static async login(\n options: {\n prompt?: PromptValue;\n } = {}\n ) {\n log.info('Initializing authentication...');\n\n try {\n const { client, config } = await this.initClient();\n\n // Force authentication even if already authenticated\n log.debug('Starting authentication...');\n await this.authenticateWithChoice(client, options.prompt);\n log.debug('Authentication complete');\n\n // Ensure SSH key pair exists (ELF-184): generate if first login, reuse if already present.\n // Register the public key with the server so the server can authenticate SSH connections.\n // SSH key setup is fatal: a missing or unregistered key means tunnel connections\n // will fail with a cryptic SSH auth rejection. Better to surface the error now.\n // Export directly from the returned KeyPair \u2014 avoids a redundant disk read and\n // a TOCTOU window where the .pub file could be replaced between generation and read.\n log.debug('Ensuring SSH key pair...');\n const keyPair = await SshKeyManager.ensureKeyPair(config.sshKeyPath);\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n log.debug('Registering SSH public key with server...');\n await client.registerPublicKey(publicKey);\n log.success('SSH key registered with server.');\n } catch (error) {\n log.debug('Login failed with error:', error);\n this.handleError(AuthCommand.Action.LOGIN, error);\n }\n }\n\n static async logout() {\n log.info('Clearing authentication data...');\n\n try {\n const config = ConfigManager.load();\n\n if (!config.serverUrl) {\n log.warn('No server URL configured - nothing to clear');\n return;\n }\n\n const { client } = await this.initClient(config);\n await client.logout();\n\n log.success('Logged out successfully');\n log.success('Authentication data cleared');\n trackEvent('auth_logout');\n } catch (error) {\n this.handleError(AuthCommand.Action.LOGOUT, error);\n }\n }\n}\n", "import { log } from '../lib/logger.js';\nimport { trackEvent } from '../lib/telemetry.js';\nimport { SshKeyManager } from '../lib/ssh-key-manager.js';\nimport { BaseCommand } from './base-command.js';\n\nexport class UserCommand extends BaseCommand {\n /** Action constants for type-safe error handling */\n static readonly Action = {\n KEY_GENERATE: 'generate SSH key',\n UPDATE_SLUG: 'update user slug',\n } as const;\n\n /**\n * Generate a new SSH key pair and register it with the server.\n * Overwrites any existing key at the configured path.\n */\n static async keyGenerate() {\n try {\n const { client, config } = await this.setupClient(\n 'Generating SSH key...'\n );\n\n log.info('Generating new ECDSA P-256 SSH key pair...');\n // Export directly from the returned KeyPair \u2014 avoids a redundant disk read.\n const keyPair = await SshKeyManager.generateKeyPair(config.sshKeyPath);\n const publicKey = await SshKeyManager.exportPublicKey(keyPair);\n await client.registerPublicKey(publicKey);\n\n const keyInfo = SshKeyManager.getKeyInfo(config.sshKeyPath);\n log.success('SSH key generated and registered.');\n log.info('Private key:', keyInfo.path);\n log.info('Public key:', keyInfo.pubKeyPath);\n\n trackEvent('user_key_generate');\n } catch (error) {\n this.handleError(UserCommand.Action.KEY_GENERATE, error);\n }\n }\n\n /**\n * Update the user's slug for tunnel namespacing (delegates to server update).\n */\n static async updateSlug(newSlug: string) {\n if (!/^[a-zA-Z]{6}$/.test(newSlug)) {\n log.error('Invalid slug format');\n log.info('Slug must be exactly 6 alphabetic characters (a-z, A-Z)');\n log.info('Example: periscope user slug myslug');\n return;\n }\n\n try {\n const { client } = await this.setupClient();\n\n const normalizedSlug = newSlug.toLowerCase();\n log.info(`Updating user slug to: ${normalizedSlug}`);\n await client.updateSlug({ slug: normalizedSlug });\n\n log.success('Slug updated successfully!');\n log.info(`Your tunnels will now use: {name}-${normalizedSlug}.{domain}`);\n\n trackEvent('auth_update_slug');\n } catch (error) {\n this.handleError(UserCommand.Action.UPDATE_SLUG, error);\n }\n }\n}\n", "import readline from 'readline';\nimport { TunnelCommand } from './commands/tunnel.js';\nimport { ConfigCommand } from './commands/config.js';\nimport { StatusCommand } from './commands/status.js';\nimport { AuthCommand } from './commands/auth.js';\nimport { UserCommand } from './commands/user.js';\n\nimport { log } from './lib/logger.js';\nimport { performSecureCleanup } from './lib/secure-memory.js';\nimport {\n initializeReadline,\n closeReadline,\n getReadlineInterface,\n} from './lib/readline-instance.js';\nimport { gracefulExit } from './lib/process-lifecycle.js';\n\ninterface ConfigOptions {\n server?: string;\n requestLog?: string;\n}\n\nexport class InteractiveMode {\n private static instance: InteractiveMode | null = null;\n\n constructor() {\n InteractiveMode.instance = this;\n\n // Initialize the shared readline instance for interactive mode\n initializeReadline(true, line => this.tabCompleter(line));\n\n this.setupLineHandler();\n this.setupSignalHandling();\n }\n\n private get rl(): readline.Interface {\n return getReadlineInterface();\n }\n\n private setupLineHandler() {\n this.rl.on('line', line => this.handleCommand(line.trim()));\n }\n\n /**\n * Tab completion for interactive mode.\n * Maps the command tree so Tab expands to the next token at each level.\n */\n private tabCompleter(line: string): [string[], string] {\n const trimmed = line.trimEnd();\n const parts = trimmed.split(' ');\n\n const topLevel = [\n 'auth',\n 'config',\n 'connect',\n 'status',\n 'user',\n 'help',\n 'clear',\n 'exit',\n 'quit',\n ];\n\n let candidates: string[];\n\n if (parts.length <= 1) {\n candidates = topLevel;\n } else {\n switch (parts[0]) {\n case 'auth':\n candidates = ['auth login', 'auth logout'];\n break;\n case 'config':\n if (parts[1] === 'set' && parts.length >= 3) {\n candidates = ['config set --server', 'config set --request-log'];\n } else {\n candidates = ['config show', 'config set'];\n }\n break;\n case 'user':\n if (parts[1] === 'key') {\n candidates = ['user key generate'];\n } else {\n candidates = ['user slug', 'user key'];\n }\n break;\n default:\n return [[], line];\n }\n }\n\n const hits = candidates.filter(c => c.startsWith(trimmed));\n return [hits, line];\n }\n\n private setupSignalHandling() {\n // Handle CTRL-C in interactive mode (readline level)\n this.rl.on('SIGINT', async () => {\n log.info('Exiting interactive mode...');\n await this.close();\n await gracefulExit(0);\n });\n }\n\n async start() {\n // Set environment variable to indicate interactive mode\n process.env.PERISCOPE_INTERACTIVE = 'true';\n\n log.header('\uD83D\uDD2D Periscope Interactive CLI');\n log.info('Type \"help\" for available commands or \"exit\" to quit.');\n log.blank();\n\n this.rl.prompt();\n }\n\n private async handleCommand(input: string) {\n if (!input) {\n this.rl.prompt();\n return;\n }\n\n const [mainCommand, ...args] = input.split(' ');\n\n try {\n switch (mainCommand.toLowerCase()) {\n case 'help':\n this.showHelp();\n break;\n\n case 'exit':\n case 'quit':\n log.raw('Goodbye! \uD83D\uDC4B');\n process.exit(0);\n break;\n\n case 'auth':\n await this.handleAuthCommand(args);\n break;\n\n case 'config':\n await this.handleConfigCommand(args);\n break;\n\n case 'connect':\n await this.handleConnectCommand(args);\n break;\n\n case 'status':\n await StatusCommand.check();\n break;\n\n case 'user':\n await this.handleUserCommand(args);\n break;\n\n case 'clear':\n console.clear();\n break;\n\n default:\n log.error(`Unknown command: ${mainCommand}`);\n log.info('Type \"help\" for available commands.');\n break;\n }\n } catch (error) {\n log.error(\n `Error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n\n this.rl.prompt();\n }\n\n private showHelp() {\n log.header('Available Commands:');\n log.separator(50);\n\n log.info('Authentication:');\n log.info(' auth login - Authenticate (choose method)');\n log.info(' auth logout - Clear authentication data');\n\n log.header('User:');\n log.info(' user slug <new-slug> - Update user slug (6 letters)');\n log.info(\n ' user key generate - Generate and register a new SSH key'\n );\n\n log.header('Configuration:');\n log.info(' config show - Show current configuration');\n log.info(' config set --server <url> - Set server URL');\n log.info(\n ' config set --request-log <bool> - Show request log (true/false)'\n );\n\n log.header('Tunnels:');\n log.info(\n ' connect <name> [--target <target>] [--key <path>] - Establish SSH tunnel'\n );\n\n log.header('General:');\n log.info(\n ' status - Show server, auth, and SSH key status'\n );\n log.info(' clear - Clear the screen');\n log.info(' help - Show this help message');\n log.info(' exit, quit - Exit interactive mode');\n\n log.separator(50);\n log.info('Use Tab for command completion');\n }\n\n private async handleAuthCommand(args: string[]) {\n const [subCommand] = args;\n\n // Pause readline interface before running commands that might use ora spinner\n switch (subCommand) {\n case 'login':\n await AuthCommand.login();\n break;\n\n case 'logout':\n await AuthCommand.logout();\n break;\n\n default:\n log.error('Invalid auth command. Available: login, logout');\n break;\n }\n }\n\n private async handleUserCommand(args: string[]) {\n const [subCommand, ...options] = args;\n\n switch (subCommand) {\n case 'slug': {\n const newSlug = options[0];\n if (!newSlug) {\n log.error('Please provide a new slug.');\n log.info('Usage: user slug <new-slug>');\n break;\n }\n await UserCommand.updateSlug(newSlug);\n break;\n }\n\n case 'key': {\n const keySubCommand = options[0];\n if (keySubCommand === 'generate') {\n await UserCommand.keyGenerate();\n } else {\n log.error('Invalid key command. Available: generate');\n log.info('Usage: user key generate');\n }\n break;\n }\n\n default:\n log.error('Invalid user command. Available: slug, key');\n break;\n }\n }\n\n private async handleConfigCommand(args: string[]) {\n const [subCommand, ...options] = args;\n\n switch (subCommand) {\n case 'show':\n await ConfigCommand.show();\n break;\n\n case 'set': {\n // Parse options for config set\n const configOptions: ConfigOptions = {};\n for (let i = 0; i < options.length; i += 2) {\n const option = options[i];\n const value = options[i + 1];\n\n switch (option) {\n case '--server':\n case '-s':\n configOptions.server = value;\n break;\n case '--request-log':\n configOptions.requestLog = value;\n break;\n }\n }\n\n await ConfigCommand.set(configOptions);\n break;\n }\n\n default:\n log.error('Invalid config command. Available: show, set');\n break;\n }\n }\n\n private async handleConnectCommand(args: string[]) {\n // First argument is the tunnel name (required)\n if (args.length === 0) {\n log.error('Please specify a tunnel name.');\n log.info('Usage: connect <name> [--target <target>] [--key <path>]');\n return;\n }\n\n const name = args[0];\n let target: string | undefined;\n let sshKeyPath: string | undefined;\n\n // Parse remaining options\n for (let i = 1; i < args.length; i += 2) {\n const option = args[i];\n const value = args[i + 1];\n\n switch (option) {\n case '--target':\n target = value;\n break;\n case '--key':\n sshKeyPath = value;\n break;\n }\n }\n\n await TunnelCommand.connect(name, target, sshKeyPath);\n }\n\n /**\n * Trigger a new prompt (useful for background operations)\n */\n public static triggerPrompt(): void {\n if (InteractiveMode.instance) {\n const rl = getReadlineInterface();\n if (rl) {\n rl.prompt();\n }\n }\n }\n\n async close() {\n InteractiveMode.instance = null;\n\n // Close the shared readline instance\n closeReadline();\n\n // Ensure secure cleanup happens\n await performSecureCleanup();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["chalk", "os", "fs", "path", "client", "fs", "path", "http", "open", "config", "require", "ConfigManager", "fs", "path", "safeLocation", "config", "response", "open", "authToken", "fs", "path", "open", "config", "open", "PeriscopeApi", "http", "client", "fs", "config", "gracefulExit", "trackException", "fs", "path", "os", "PortForwardingService", "isTokenExpired", "chalk", "fs", "path", "os", "config", "chalk", "config", "client", "timeout", "PortForwardingService", "fs", "os", "client", "config", "client", "config", "config", "config", "client", "client", "config", "client", "config"]
|
|
7
7
|
}
|