${escapeHtml(viewModel.header.title)}
${escapeHtml(viewModel.header.subtitle)}
const vscode = require('vscode'); const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); const { chooseLaunchWorkspace, describeProviderState, findCommandPath, isPathInsideWorkspace, parseProfileFile, resolveCommandCheckPath, } = require('./state'); const { buildControlCenterViewModel } = require('./presentation'); const OPENCLAUDE_REPO_URL = 'https://github.com/Gitlawb/openclaude'; const OPENCLAUDE_SETUP_URL = 'https://github.com/Gitlawb/openclaude/blob/main/README.md#quick-start'; const PROFILE_FILE_NAME = '.openclaude-profile.json'; function escapeHtml(value) { return String(value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } async function isCommandAvailable(command, launchCwd) { return Boolean(findCommandPath(command, { cwd: launchCwd })); } function getExecutableFromCommand(command) { const normalized = String(command || '').trim(); if (!normalized) { return ''; } const doubleQuotedMatch = normalized.match(/^"([^"]+)"/); if (doubleQuotedMatch) { return doubleQuotedMatch[1]; } const singleQuotedMatch = normalized.match(/^'([^']+)'/); if (singleQuotedMatch) { return singleQuotedMatch[1]; } return normalized.split(/\s+/)[0]; } function getWorkspacePaths() { return (vscode.workspace.workspaceFolders || []).map(folder => folder.uri.fsPath); } function getActiveWorkspacePath() { const editor = vscode.window.activeTextEditor; if (!editor || editor.document.uri.scheme !== 'file') { return null; } const workspaceFolder = vscode.workspace.getWorkspaceFolder(editor.document.uri); return workspaceFolder ? workspaceFolder.uri.fsPath : null; } function getActiveFilePath() { const editor = vscode.window.activeTextEditor; if (!editor || editor.document.uri.scheme !== 'file') { return null; } return editor.document.uri.fsPath || null; } function resolveLaunchTargets({ activeFilePath, workspacePath, workspaceSourceLabel, executable } = {}) { const activeFileDirectory = isPathInsideWorkspace(activeFilePath, workspacePath) ? path.dirname(activeFilePath) : null; const normalizedExecutable = String(executable || '').trim(); const commandPath = normalizedExecutable ? resolveCommandCheckPath(normalizedExecutable, workspacePath) : null; const relativeCommandRequiresWorkspaceRoot = Boolean( workspacePath && commandPath && !path.isAbsolute(normalizedExecutable), ); if (relativeCommandRequiresWorkspaceRoot) { return { projectAwareCwd: workspacePath, projectAwareCwdLabel: workspacePath, projectAwareSourceLabel: 'workspace root (required by relative launch command)', workspaceRootCwd: workspacePath, workspaceRootCwdLabel: workspacePath, launchActionsShareTarget: true, launchActionsShareTargetReason: 'relative-launch-command', }; } if (activeFileDirectory) { return { projectAwareCwd: activeFileDirectory, projectAwareCwdLabel: activeFileDirectory, projectAwareSourceLabel: 'active file directory', workspaceRootCwd: workspacePath || null, workspaceRootCwdLabel: workspacePath || 'No workspace open', launchActionsShareTarget: false, launchActionsShareTargetReason: null, }; } if (workspacePath) { return { projectAwareCwd: workspacePath, projectAwareCwdLabel: workspacePath, projectAwareSourceLabel: workspaceSourceLabel || 'workspace root', workspaceRootCwd: workspacePath, workspaceRootCwdLabel: workspacePath, launchActionsShareTarget: true, launchActionsShareTargetReason: null, }; } return { projectAwareCwd: null, projectAwareCwdLabel: 'VS Code default terminal cwd', projectAwareSourceLabel: 'VS Code default terminal cwd', workspaceRootCwd: null, workspaceRootCwdLabel: 'No workspace open', launchActionsShareTarget: false, launchActionsShareTargetReason: null, }; } function resolveLaunchWorkspace() { return chooseLaunchWorkspace({ activeWorkspacePath: getActiveWorkspacePath(), workspacePaths: getWorkspacePaths(), }); } function getWorkspaceSourceLabel(source) { switch (source) { case 'active-workspace': return 'active editor workspace'; case 'first-workspace': return 'first workspace folder'; default: return 'no workspace open'; } } function getProviderSourceLabel(source) { switch (source) { case 'profile': return 'saved profile'; case 'env': return 'environment'; case 'shim': return 'launch setting'; default: return 'unknown'; } } function readWorkspaceProfile(profilePath) { if (!profilePath || !fs.existsSync(profilePath)) { return { profile: null, statusLabel: 'Missing', statusHint: `${PROFILE_FILE_NAME} not found in the workspace root`, filePath: null, }; } try { const raw = fs.readFileSync(profilePath, 'utf8'); const profile = parseProfileFile(raw); if (!profile) { return { profile: null, statusLabel: 'Invalid', statusHint: `${profilePath} has invalid JSON or an unsupported profile`, filePath: profilePath, }; } return { profile, statusLabel: 'Found', statusHint: profilePath, filePath: profilePath, }; } catch (error) { return { profile: null, statusLabel: 'Unreadable', statusHint: `${profilePath} (${error instanceof Error ? error.message : 'read failed'})`, filePath: profilePath, }; } } async function collectControlCenterState() { const configured = vscode.workspace.getConfiguration('openclaude'); const launchCommand = configured.get('launchCommand', 'openclaude'); const terminalName = configured.get('terminalName', 'OpenClaude'); const shimEnabled = configured.get('useOpenAIShim', false); const executable = getExecutableFromCommand(launchCommand); const launchWorkspace = resolveLaunchWorkspace(); const workspaceFolder = launchWorkspace.workspacePath; const workspaceSourceLabel = getWorkspaceSourceLabel(launchWorkspace.source); const launchTargets = resolveLaunchTargets({ activeFilePath: getActiveFilePath(), workspacePath: workspaceFolder, workspaceSourceLabel, executable, }); const installed = await isCommandAvailable(executable, launchTargets.projectAwareCwd); const profilePath = workspaceFolder ? path.join(workspaceFolder, PROFILE_FILE_NAME) : null; const profileState = workspaceFolder ? readWorkspaceProfile(profilePath) : { profile: null, statusLabel: 'No workspace', statusHint: 'Open a workspace folder to detect a saved profile', filePath: null, }; const providerState = describeProviderState({ shimEnabled, env: process.env, profile: profileState.profile, }); return { installed, executable, launchCommand, terminalName, shimEnabled, workspaceFolder, workspaceSourceLabel, launchCwd: launchTargets.projectAwareCwd, launchCwdLabel: launchTargets.projectAwareCwdLabel, launchCwdSourceLabel: launchTargets.projectAwareSourceLabel, workspaceRootCwd: launchTargets.workspaceRootCwd, workspaceRootCwdLabel: launchTargets.workspaceRootCwdLabel, launchActionsShareTarget: launchTargets.launchActionsShareTarget, launchActionsShareTargetReason: launchTargets.launchActionsShareTargetReason, canLaunchInWorkspaceRoot: Boolean(workspaceFolder), profileStatusLabel: profileState.statusLabel, profileStatusHint: profileState.statusHint, workspaceProfilePath: profileState.filePath, providerState, providerSourceLabel: getProviderSourceLabel(providerState.source), }; } async function launchOpenClaude(options = {}) { const { requireWorkspace = false } = options; const configured = vscode.workspace.getConfiguration('openclaude'); const launchCommand = configured.get('launchCommand', 'openclaude'); const terminalName = configured.get('terminalName', 'OpenClaude'); const shimEnabled = configured.get('useOpenAIShim', false); const executable = getExecutableFromCommand(launchCommand); const launchWorkspace = resolveLaunchWorkspace(); if (requireWorkspace && !launchWorkspace.workspacePath) { await vscode.window.showWarningMessage( 'Open a workspace folder before using Launch in Workspace Root.', ); return; } const launchTargets = resolveLaunchTargets({ activeFilePath: getActiveFilePath(), workspacePath: launchWorkspace.workspacePath, workspaceSourceLabel: getWorkspaceSourceLabel(launchWorkspace.source), executable, }); const targetCwd = requireWorkspace ? launchTargets.workspaceRootCwd : launchTargets.projectAwareCwd; const installed = await isCommandAvailable(executable, targetCwd); if (!installed) { const action = await vscode.window.showErrorMessage( `OpenClaude command not found: ${executable}. Install it with: npm install -g @gitlawb/openclaude`, 'Open Setup Guide', 'Open Repository', ); if (action === 'Open Setup Guide') { await vscode.env.openExternal(vscode.Uri.parse(OPENCLAUDE_SETUP_URL)); } else if (action === 'Open Repository') { await vscode.env.openExternal(vscode.Uri.parse(OPENCLAUDE_REPO_URL)); } return; } const env = {}; if (shimEnabled) { env.CLAUDE_CODE_USE_OPENAI = '1'; } const terminalOptions = { name: terminalName, env, }; if (targetCwd) { terminalOptions.cwd = targetCwd; } const terminal = vscode.window.createTerminal(terminalOptions); terminal.show(true); terminal.sendText(launchCommand, true); } async function openWorkspaceProfile() { const state = await collectControlCenterState(); if (!state.workspaceProfilePath) { await vscode.window.showInformationMessage( `No ${PROFILE_FILE_NAME} file was found for the current workspace.`, ); return; } const document = await vscode.workspace.openTextDocument( vscode.Uri.file(state.workspaceProfilePath), ); await vscode.window.showTextDocument(document, { preview: false }); } function getToneClass(tone) { switch (tone) { case 'accent': return 'tone-accent'; case 'positive': return 'tone-positive'; case 'warning': return 'tone-warning'; case 'critical': return 'tone-critical'; default: return 'tone-neutral'; } } function renderHeaderBadge(badge) { return `
${escapeHtml(viewModel.header.subtitle)}