extension.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import * as vscode from "vscode"
  2. import * as dotenvx from "@dotenvx/dotenvx"
  3. import * as path from "path"
  4. // Load environment variables from .env file
  5. try {
  6. // Specify path to .env file in the project root directory
  7. const envPath = path.join(__dirname, "..", ".env")
  8. dotenvx.config({ path: envPath })
  9. } catch (e) {
  10. // Silently handle environment loading errors
  11. console.warn("Failed to load environment variables:", e)
  12. }
  13. import { CloudService } from "@roo-code/cloud"
  14. import { TelemetryService, PostHogTelemetryClient } from "@roo-code/telemetry"
  15. import "./utils/path" // Necessary to have access to String.prototype.toPosix.
  16. import { createOutputChannelLogger, createDualLogger } from "./utils/outputChannelLogger"
  17. import { Package } from "./shared/package"
  18. import { formatLanguage } from "./shared/language"
  19. import { ContextProxy } from "./core/config/ContextProxy"
  20. import { ClineProvider } from "./core/webview/ClineProvider"
  21. import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider"
  22. import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry"
  23. import { McpServerManager } from "./services/mcp/McpServerManager"
  24. import { CodeIndexManager } from "./services/code-index/manager"
  25. import { MdmService } from "./services/mdm/MdmService"
  26. import { migrateSettings } from "./utils/migrateSettings"
  27. import { autoImportSettings } from "./utils/autoImportSettings"
  28. import { API } from "./extension/api"
  29. import {
  30. handleUri,
  31. registerCommands,
  32. registerCodeActions,
  33. registerTerminalActions,
  34. CodeActionProvider,
  35. } from "./activate"
  36. import { initializeI18n } from "./i18n"
  37. /**
  38. * Built using https://github.com/microsoft/vscode-webview-ui-toolkit
  39. *
  40. * Inspired by:
  41. * - https://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/default/weather-webview
  42. * - https://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/frameworks/hello-world-react-cra
  43. */
  44. let outputChannel: vscode.OutputChannel
  45. let extensionContext: vscode.ExtensionContext
  46. // This method is called when your extension is activated.
  47. // Your extension is activated the very first time the command is executed.
  48. export async function activate(context: vscode.ExtensionContext) {
  49. extensionContext = context
  50. outputChannel = vscode.window.createOutputChannel(Package.outputChannel)
  51. context.subscriptions.push(outputChannel)
  52. outputChannel.appendLine(`${Package.name} extension activated - ${JSON.stringify(Package)}`)
  53. // Migrate old settings to new
  54. await migrateSettings(context, outputChannel)
  55. // Initialize telemetry service.
  56. const telemetryService = TelemetryService.createInstance()
  57. try {
  58. telemetryService.register(new PostHogTelemetryClient())
  59. } catch (error) {
  60. console.warn("Failed to register PostHogTelemetryClient:", error)
  61. }
  62. // Create logger for cloud services
  63. const cloudLogger = createDualLogger(createOutputChannelLogger(outputChannel))
  64. // Initialize Roo Code Cloud service.
  65. await CloudService.createInstance(context, {
  66. stateChanged: () => ClineProvider.getVisibleInstance()?.postStateToWebview(),
  67. log: cloudLogger,
  68. })
  69. // Initialize MDM service
  70. const mdmService = await MdmService.createInstance(cloudLogger)
  71. // Initialize i18n for internationalization support
  72. initializeI18n(context.globalState.get("language") ?? formatLanguage(vscode.env.language))
  73. // Initialize terminal shell execution handlers.
  74. TerminalRegistry.initialize()
  75. // Get default commands from configuration.
  76. const defaultCommands = vscode.workspace.getConfiguration(Package.name).get<string[]>("allowedCommands") || []
  77. // Initialize global state if not already set.
  78. if (!context.globalState.get("allowedCommands")) {
  79. context.globalState.update("allowedCommands", defaultCommands)
  80. }
  81. const contextProxy = await ContextProxy.getInstance(context)
  82. const codeIndexManager = CodeIndexManager.getInstance(context)
  83. try {
  84. await codeIndexManager?.initialize(contextProxy)
  85. } catch (error) {
  86. outputChannel.appendLine(
  87. `[CodeIndexManager] Error during background CodeIndexManager configuration/indexing: ${error.message || error}`,
  88. )
  89. }
  90. const provider = new ClineProvider(context, outputChannel, "sidebar", contextProxy, codeIndexManager, mdmService)
  91. TelemetryService.instance.setProvider(provider)
  92. if (codeIndexManager) {
  93. context.subscriptions.push(codeIndexManager)
  94. }
  95. context.subscriptions.push(
  96. vscode.window.registerWebviewViewProvider(ClineProvider.sideBarId, provider, {
  97. webviewOptions: { retainContextWhenHidden: true },
  98. }),
  99. )
  100. // Auto-import configuration if specified in settings
  101. try {
  102. await autoImportSettings(outputChannel, {
  103. providerSettingsManager: provider.providerSettingsManager,
  104. contextProxy: provider.contextProxy,
  105. customModesManager: provider.customModesManager,
  106. })
  107. } catch (error) {
  108. outputChannel.appendLine(
  109. `[AutoImport] Error during auto-import: ${error instanceof Error ? error.message : String(error)}`,
  110. )
  111. }
  112. registerCommands({ context, outputChannel, provider })
  113. /**
  114. * We use the text document content provider API to show the left side for diff
  115. * view by creating a virtual document for the original content. This makes it
  116. * readonly so users know to edit the right side if they want to keep their changes.
  117. *
  118. * This API allows you to create readonly documents in VSCode from arbitrary
  119. * sources, and works by claiming an uri-scheme for which your provider then
  120. * returns text contents. The scheme must be provided when registering a
  121. * provider and cannot change afterwards.
  122. *
  123. * Note how the provider doesn't create uris for virtual documents - its role
  124. * is to provide contents given such an uri. In return, content providers are
  125. * wired into the open document logic so that providers are always considered.
  126. *
  127. * https://code.visualstudio.com/api/extension-guides/virtual-documents
  128. */
  129. const diffContentProvider = new (class implements vscode.TextDocumentContentProvider {
  130. provideTextDocumentContent(uri: vscode.Uri): string {
  131. return Buffer.from(uri.query, "base64").toString("utf-8")
  132. }
  133. })()
  134. context.subscriptions.push(
  135. vscode.workspace.registerTextDocumentContentProvider(DIFF_VIEW_URI_SCHEME, diffContentProvider),
  136. )
  137. context.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))
  138. // Register code actions provider.
  139. context.subscriptions.push(
  140. vscode.languages.registerCodeActionsProvider({ pattern: "**/*" }, new CodeActionProvider(), {
  141. providedCodeActionKinds: CodeActionProvider.providedCodeActionKinds,
  142. }),
  143. )
  144. registerCodeActions(context)
  145. registerTerminalActions(context)
  146. // Allows other extensions to activate once Roo is ready.
  147. vscode.commands.executeCommand(`${Package.name}.activationCompleted`)
  148. // Implements the `RooCodeAPI` interface.
  149. const socketPath = process.env.ROO_CODE_IPC_SOCKET_PATH
  150. const enableLogging = typeof socketPath === "string"
  151. // Watch the core files and automatically reload the extension host.
  152. if (process.env.NODE_ENV === "development") {
  153. const pattern = "**/*.ts"
  154. const watchPaths = [
  155. { path: context.extensionPath, name: "extension" },
  156. { path: path.join(context.extensionPath, "../packages/types"), name: "types" },
  157. { path: path.join(context.extensionPath, "../packages/telemetry"), name: "telemetry" },
  158. { path: path.join(context.extensionPath, "../packages/cloud"), name: "cloud" },
  159. ]
  160. console.log(
  161. `♻️♻️♻️ Core auto-reloading is ENABLED. Watching for changes in: ${watchPaths.map(({ name }) => name).join(", ")}`,
  162. )
  163. watchPaths.forEach(({ path: watchPath, name }) => {
  164. const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(watchPath, pattern))
  165. watcher.onDidChange((uri) => {
  166. console.log(`♻️ ${name} file changed: ${uri.fsPath}. Reloading host…`)
  167. vscode.commands.executeCommand("workbench.action.reloadWindow")
  168. })
  169. context.subscriptions.push(watcher)
  170. })
  171. }
  172. return new API(outputChannel, provider, socketPath, enableLogging)
  173. }
  174. // This method is called when your extension is deactivated.
  175. export async function deactivate() {
  176. outputChannel.appendLine(`${Package.name} extension deactivated`)
  177. await McpServerManager.cleanup(extensionContext)
  178. TelemetryService.instance.shutdown()
  179. TerminalRegistry.cleanup()
  180. }