Skip to content

擴充入口:activate 啟動流程

源码版本v4.0.10

職責

activate() 是 VSCode 擴充在使用者首次觸發擴充功能時被呼叫的入口,寫在 apps/vscode/src/extension.ts。Cline 把整個啟動過程在這裡拆成五步:先把平台相關的 HostProvider 裝好,然後清理上一版本遺留的儲存格式,再把 VSCode 自己的儲存匯出到共享檔案儲存,接著跑一遍跨平台初始化 (initialize),最後才註冊 VSCode 專屬的命令、視圖、URI 處理器和 CodeAction Provider。整個函式以 createClineAPI 收尾,回傳外部使用者能直接呼叫的極簡 API 物件。

它存在的意義是把「擴充怎麼被 VSCode 拉起來」這件平台相關的事,和「Cline 自己怎麼初始化」這件跨平台的事隔開。common.ts 裡的 initialize() 同時被 VSCode、CLI、JetBrains 三個 host 共用 (initialize:34),extension.ts 只負責把 VSCode 特有的 context、命令、URI 等接進來。所以你在讀 extension.ts 時,看到的幾乎都是 vscode.commands.registerCommand(...) 這種註冊動作,真正的服務裝配都在 initialize()Controller 建構函式裡。

設計動機

  • 平台抽象先於一切:setupHostProvider 必須先跑 (setupHostProvider:73),因為後續所有服務都透過 HostProvider.get() 拿 webview、終端、回呼 URL。VSCode 特有的實作 (VscodeWebviewProvider、VscodeTerminalManager 等) 在這裡被注入。
  • 遷移先於匯出:cleanupLegacyVSCodeStorageexportVSCodeStorageToSharedFiles 之前跑 (cleanupLegacyVSCodeStorage:78),先把 workspace→global、task history→檔案、custom instructions→rules 這幾類歷史髒資料清掉,再匯出乾淨的 state 到 ~/.cline/data/,讓其它平台能讀到一致的狀態。
  • 共享初始化抽到 common.ts:跨平台服務 (StateManager、ErrorService、PostHog、ClineTempManager、FileContextTracker 等) 都在 initialize() 裡拉起 (external services:56),VSCode 不重複造一遍。這樣 CLI 和 JetBrains 復用同一套啟動邏輯。
  • webview 用 retainContextWhenHidden:registerWebviewViewProviderretainContextWhenHidden: true (webview retain:124),讓 sidebar 關閉時 React 狀態不丟,下次開啟不用重渲染。代價是記憶體常駐,但 Cline 的對話 context 值得這點開銷。
  • 命令分兩層註冊:VSCode 命令先從 ExtensionRegistryInfo.commands 拿 ID 常量 (ExtensionRegistryInfo:130),保證 package.json 和程式碼裡的 command ID 一致。Dev 模式額外載入 dev/commands/tasks 裡的除錯命令 (dev commands:193)。
  • URI handler 兜底重試:handleUri 處理深鏈時,如果 sidebar 還沒初始化好,先調 openClineSidebarForTaskUri 再處理一次 (task uri retry:178),防止首次安裝時 sidebar 初始化和深鏈到達之間的競態。

關鍵檔案

  • activate:68 — 整個擴充入口,五步啟動流程都在這裡。
  • setupHostProvider call:73 — 第一步,裝配 VSCode 版 HostProvider (webview、diff、comment review、terminal、callback URL)。
  • cleanupLegacyVSCodeStorage:78 — 第二步,清理 workspace→global、task history→file、custom instructions→rules 等歷史格式。
  • exportVSCodeStorageToSharedFiles:84 — 第三步,把 VSCode 原生儲存匯出到 ~/.cline/data/ 共享檔案儲存,供其它平台讀取。
  • initialize call:88 — 第四步,跑 common.ts 的跨平台初始化,回傳 VscodeWebviewProvider
  • registerWebviewViewProvider:124 — 把 sidebar webview 註冊到 VSCode,啟用 retainContextWhenHidden
  • registerUriHandler:187 — 註冊 URI handler,處理 vscode://extension.id/cline/task/... 這種深鏈。
  • registerCodeActionsProvider:248 — 註冊 CodeAction Provider,在編輯器錯誤燈泡上掛「Add to Cline / Explain / Improve / Fix with Cline」四個動作。
  • setupHostProvider:602 — 裝配 HostProvider 的實際函式,把 VscodeWebviewProvider、VscodeDiffViewProvider、VscodeTerminalManager 等工廠函式傳進去。
  • createClineAPI:553 — 回傳外部 API,提供 startNewTasksendMessagepressPrimaryButtonpressSecondaryButton 四個方法。
  • initialize:34 — 跨平台初始化主體,StateManager、PostHog、ErrorService、syncWorker 都在這裡拉起。
  • deactivate:699 — 反啟用時調 tearDown() + dispose comment review controller。

資料流

啟動順序在 activate 頂部一眼可見,核心是「先遷移後初始化,先共享後平台」:

typescript
// apps/vscode/src/extension.ts
// 1. Set up HostProvider for VSCode
setupHostProvider(context)

// 2. Clean up legacy data patterns within VSCode's native storage.
await cleanupLegacyVSCodeStorage(context)

// 3. One-time export of VSCode's native storage to shared file-backed stores.
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath
const storageContext = createStorageContext({ workspacePath })
await exportVSCodeStorageToSharedFiles(context, storageContext)

// 4. Register services and perform common initialization
const webview = (await initialize(storageContext)) as VscodeWebviewProvider

這段在 activation sequence:73 附近。initialize() 內部 (createWebviewProvider:63) 呼叫 HostProvider.get().createWebviewProvider(),工廠方法回傳一個 VscodeWebviewProvider 實例,這個實例在構造時同步 new Controller(context),Controller 建構函式裡又拉起 StateManager、AuthService、McpHub、BannerService 等一長串依賴。所以 initialize() 回傳時,Controller 已經準備好接 webview 訊息了。回到 activate() 後面那段才註冊命令、URI handler、CodeAction Provider,這些註冊動作最終都把請求轉給 webview.controllerWebviewProvider.getInstance()

邊界與失敗

  • StateManager 初始化失敗不致命:initialize() 用 try/catch 包住 StateManager.initialize,失敗時彈 ERROR 訊息但繼續往下走 (stateManager try:45),因為儲存在記憶體裡仍然可用,只是沒法持久化。
  • task URI 與 sidebar 初始化競態:URI handler 先嘗試處理 task 深鏈,失敗時先 openClineSidebarForTaskUri 等 sidebar 可見再重試一次 (task uri retry:178),最多等 3 秒。
  • dev 模式額外命令非同步載入:IS_DEV 分支裡用動態 import("./dev/commands/tasks") 載入除錯命令 (dev import:193),失敗只記日誌,不影響主流程。
  • secrets 跨視窗同步:監聽 storageContext.secrets.onDidChange,當 cline:clineAccountId 變化時,新值代表另一視窗登入、空值代表另一視窗登出 (secrets listener:533),讓多視窗登入狀態保持一致。
  • ripgrep 路徑探測:VSCode 1.122 把 ripgrep 從 @vscode/ripgrep 遷到 @vscode/ripgrep-universal,getBinaryLocation 按新佈局先探,失敗再回退到舊路徑 (ripgrep path probe:685)。
  • deactivate 調 tearDown:deactivate() 調 common.tstearDown() (tearDown:153),按順序 dispose PostHog、telemetry、ErrorService、featureFlags、WebviewProvider、syncWorker、HookProcessRegistry、ClineTempManager,避免留下殭屍程序。

小結

activate() 把 VSCode 平台特性和 Cline 業務邏輯拆得很乾淨:VSCode 這邊只管註冊命令、視圖、URI handler、CodeAction,真正的服務裝配都委託給 common.tsinitialize() 和 Controller 建構函式。這種分層讓 CLI 和 JetBrains 能復用同一套初始化程式碼。要繼續看初始化後 Controller 怎麼裝配依賴、怎麼把 webview 和 Task 串起來,轉 /startup/controller;看 webview 和擴充之間怎麼用 gRPC-over-postMessage 通信,轉 /startup/webview-bridge

對照官方資料:Cline 文件 · README