mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-08 22:21:23 +00:00
Add optional opt-in telemetry to help fix bugs and improve product
This commit is contained in:
parent
35a64a3666
commit
568932492b
11 changed files with 233 additions and 17 deletions
|
|
@ -42,8 +42,8 @@ Cline functions solely as a client-side VS Code extension that facilitates commu
|
|||
1. **Local-Only Processing**:
|
||||
|
||||
- All operations happen on your local machine
|
||||
- No central servers or data collection
|
||||
- No telemetry or usage statistics gathered
|
||||
- No central servers or data collection by default
|
||||
- Anonymous telemetry and usage statistics are only collected if you explicitly opt in
|
||||
- No account creation required
|
||||
|
||||
2. **API Key Security**:
|
||||
|
|
@ -73,7 +73,8 @@ When you request assistance:
|
|||
|
||||
- Error logs are processed locally
|
||||
- No automatic error reporting to Cline
|
||||
- You control what information to include when reporting issues
|
||||
- Optional anonymous telemetry and error reporting via PostHog if you opt in
|
||||
- You control what information to include when manually reporting issues
|
||||
|
||||
## Children's Privacy
|
||||
|
||||
|
|
@ -90,6 +91,22 @@ We will post any changes to this policy on our GitHub repository. Significant ch
|
|||
- You can inspect exactly what data is being sent to AI providers
|
||||
- Enterprise users can implement additional access controls through VS Code
|
||||
|
||||
## Telemetry & Usage Statistics
|
||||
|
||||
If you choose to opt in to anonymous telemetry:
|
||||
|
||||
- Basic usage statistics and error reports are collected via PostHog
|
||||
- A stable, anonymous identifier (VS Code's `machineId`) is used to understand unique usage patterns
|
||||
- This identifier is not linked to any personal information
|
||||
- It helps us understand how features are used across sessions
|
||||
- It cannot be used to identify you personally
|
||||
- All data is anonymized and cannot be linked to individual users
|
||||
- No code content or sensitive information is ever included
|
||||
- You can opt out at any time through:
|
||||
- VS Code Settings > Cline > Enable Telemetry
|
||||
- VS Code Settings > Telemetry > Telemetry Level (setting this to anything other than "all" will disable Cline's telemetry)
|
||||
- Collected data helps us improve the extension's functionality and stability
|
||||
|
||||
## Contact Us
|
||||
|
||||
For privacy-related questions or concerns:
|
||||
|
|
|
|||
17
package-lock.json
generated
17
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "claude-dev",
|
||||
"version": "3.4.6",
|
||||
"version": "3.4.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-dev",
|
||||
"version": "3.4.6",
|
||||
"version": "3.4.8",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/bedrock-sdk": "^0.10.2",
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
"os-name": "^6.0.0",
|
||||
"p-wait-for": "^5.0.2",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"posthog-node": "^4.7.0",
|
||||
"puppeteer-chromium-resolver": "^23.0.0",
|
||||
"puppeteer-core": "^23.4.0",
|
||||
"serialize-error": "^11.0.3",
|
||||
|
|
@ -11606,6 +11607,18 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/posthog-node": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-4.7.0.tgz",
|
||||
"integrity": "sha512-RgdUKSW8MfMOkjUa8cYVqWndNjPePNuuxlGbrZC6z1WRBsVc6TdGl8caidmC10RW8mu/BOfmrGbP4cRTo2jARg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
|
|
|
|||
|
|
@ -186,6 +186,11 @@
|
|||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Controls whether the MCP Marketplace is enabled."
|
||||
},
|
||||
"cline.enableTelemetry": {
|
||||
"type": "boolean",
|
||||
"default": null,
|
||||
"markdownDescription": "Allow anonymous usage and error reporting to help improve Cline. No code, prompts, or personal information is ever sent. See our [privacy policy](https://github.com/cline/cline/blob/main/docs/PRIVACY.md) for details."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -268,6 +273,7 @@
|
|||
"os-name": "^6.0.0",
|
||||
"p-wait-for": "^5.0.2",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"posthog-node": "^4.7.0",
|
||||
"puppeteer-chromium-resolver": "^23.0.0",
|
||||
"puppeteer-core": "^23.4.0",
|
||||
"serialize-error": "^11.0.3",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import axios from "axios"
|
||||
import fs from "fs/promises"
|
||||
import os from "os"
|
||||
import crypto from "crypto"
|
||||
import { execa } from "execa"
|
||||
import fs from "fs/promises"
|
||||
import os from "os"
|
||||
import pWaitFor from "p-wait-for"
|
||||
import * as path from "path"
|
||||
import * as vscode from "vscode"
|
||||
|
|
@ -13,26 +13,25 @@ import { openFile, openImage } from "../../integrations/misc/open-file"
|
|||
import { selectImages } from "../../integrations/misc/process-images"
|
||||
import { getTheme } from "../../integrations/theme/getTheme"
|
||||
import WorkspaceTracker from "../../integrations/workspace/WorkspaceTracker"
|
||||
import { McpHub } from "../../services/mcp/McpHub"
|
||||
import { McpDownloadResponse, McpMarketplaceCatalog, McpMarketplaceItem, McpServer } from "../../shared/mcp"
|
||||
import { FirebaseAuthManager, UserInfo } from "../../services/auth/FirebaseAuthManager"
|
||||
import { McpHub } from "../../services/mcp/McpHub"
|
||||
import { ApiProvider, ModelInfo } from "../../shared/api"
|
||||
import { findLast } from "../../shared/array"
|
||||
import { AutoApprovalSettings, DEFAULT_AUTO_APPROVAL_SETTINGS } from "../../shared/AutoApprovalSettings"
|
||||
import { BrowserSettings, DEFAULT_BROWSER_SETTINGS } from "../../shared/BrowserSettings"
|
||||
import { ChatContent } from "../../shared/ChatContent"
|
||||
import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "../../shared/ChatSettings"
|
||||
import { ExtensionMessage, ExtensionState, Platform } from "../../shared/ExtensionMessage"
|
||||
import { HistoryItem } from "../../shared/HistoryItem"
|
||||
import { McpDownloadResponse, McpMarketplaceCatalog, McpServer } from "../../shared/mcp"
|
||||
import { ClineCheckpointRestore, WebviewMessage } from "../../shared/WebviewMessage"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { searchCommits } from "../../utils/git"
|
||||
import { Cline } from "../Cline"
|
||||
import { openMention } from "../mentions"
|
||||
import { getNonce } from "./getNonce"
|
||||
import { getUri } from "./getUri"
|
||||
import { AutoApprovalSettings, DEFAULT_AUTO_APPROVAL_SETTINGS } from "../../shared/AutoApprovalSettings"
|
||||
import { BrowserSettings, DEFAULT_BROWSER_SETTINGS } from "../../shared/BrowserSettings"
|
||||
import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "../../shared/ChatSettings"
|
||||
import { DIFF_VIEW_URI_SCHEME } from "../../integrations/editor/DiffViewProvider"
|
||||
import { searchCommits } from "../../utils/git"
|
||||
import { ChatContent } from "../../shared/ChatContent"
|
||||
import { getShell } from "../../utils/shell"
|
||||
import { telemetryService } from "../../services/telemetry/TelemetryService"
|
||||
|
||||
/*
|
||||
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
|
||||
|
|
@ -280,6 +279,16 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
task,
|
||||
images,
|
||||
)
|
||||
|
||||
// New task started
|
||||
if (telemetryService.isTelemetryEnabled()) {
|
||||
telemetryService.capture({
|
||||
event: "New task started",
|
||||
properties: {
|
||||
apiProvider: apiConfiguration.apiProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async initClineWithHistoryItem(historyItem: HistoryItem) {
|
||||
|
|
@ -829,6 +838,21 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
)
|
||||
break
|
||||
}
|
||||
// telemetry
|
||||
case "openTelemetrySettings": {
|
||||
await vscode.commands.executeCommand(
|
||||
"workbench.action.openSettings",
|
||||
"@ext:saoudrizwan.claude-dev cline.telemetryOptIn",
|
||||
)
|
||||
break
|
||||
}
|
||||
case "telemetryOptIn": {
|
||||
if (message.bool !== undefined) {
|
||||
await vscode.workspace.getConfiguration("cline").update("enableTelemetry", message.bool, true)
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
break
|
||||
}
|
||||
// Add more switch case statements here as more webview message commands
|
||||
// are created within the webview context (i.e. inside media/main.js)
|
||||
}
|
||||
|
|
@ -1594,6 +1618,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
|||
userInfo,
|
||||
authToken,
|
||||
mcpMarketplaceEnabled,
|
||||
telemetryOptIn,
|
||||
} = await this.getState()
|
||||
|
||||
return {
|
||||
|
|
@ -1613,6 +1638,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
|||
isLoggedIn: !!authToken,
|
||||
userInfo,
|
||||
mcpMarketplaceEnabled,
|
||||
telemetryOptIn,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1791,6 +1817,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
|||
.get("reasoningEffort", "medium")
|
||||
|
||||
const mcpMarketplaceEnabled = vscode.workspace.getConfiguration("cline").get<boolean>("mcpMarketplace.enabled", true)
|
||||
const telemetryOptIn = vscode.workspace.getConfiguration("cline").get<boolean | null>("enableTelemetry", null)
|
||||
|
||||
return {
|
||||
apiConfiguration: {
|
||||
|
|
@ -1847,6 +1874,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
|||
previousModeModelId,
|
||||
previousModeModelInfo,
|
||||
mcpMarketplaceEnabled,
|
||||
telemetryOptIn,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { createClineAPI } from "./exports"
|
|||
import "./utils/path" // necessary to have access to String.prototype.toPosix
|
||||
import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider"
|
||||
import assert from "node:assert"
|
||||
import { telemetryService } from "./services/telemetry/TelemetryService"
|
||||
|
||||
/*
|
||||
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
|
||||
|
|
@ -189,6 +190,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
|
||||
// This method is called when your extension is deactivated
|
||||
export function deactivate() {
|
||||
telemetryService.shutdown()
|
||||
Logger.log("Cline extension deactivated")
|
||||
}
|
||||
|
||||
|
|
|
|||
73
src/services/telemetry/TelemetryService.ts
Normal file
73
src/services/telemetry/TelemetryService.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import { PostHog } from "posthog-node"
|
||||
import * as vscode from "vscode"
|
||||
|
||||
class PostHogClient {
|
||||
private static instance: PostHogClient
|
||||
private client: PostHog
|
||||
private distinctId: string = vscode.env.machineId
|
||||
private telemetryEnabled: boolean = false
|
||||
|
||||
private constructor() {
|
||||
this.client = new PostHog("phc_qfOAGxZw2TL5O8p9KYd9ak3bPBFzfjC8fy5L6jNWY7K", {
|
||||
host: "https://us.i.posthog.com",
|
||||
enableExceptionAutocapture: false,
|
||||
})
|
||||
|
||||
// Initialize telemetry state based on user settings
|
||||
this.updateTelemetryState()
|
||||
|
||||
// Listen for settings changes
|
||||
vscode.workspace.onDidChangeConfiguration((e) => {
|
||||
if (e.affectsConfiguration("cline.enableTelemetry") || e.affectsConfiguration("telemetry.telemetryLevel")) {
|
||||
this.updateTelemetryState()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private updateTelemetryState(): void {
|
||||
// First check global telemetry level - telemetry should only be enabled when level is "all"
|
||||
const telemetryLevel = vscode.workspace.getConfiguration("telemetry").get<string>("telemetryLevel", "all")
|
||||
const globalTelemetryEnabled = telemetryLevel === "all"
|
||||
|
||||
// Only check Cline setting if global telemetry is enabled
|
||||
if (globalTelemetryEnabled) {
|
||||
const clineOptIn = vscode.workspace.getConfiguration("cline").get<boolean | null>("enableTelemetry", null)
|
||||
this.telemetryEnabled = clineOptIn === true
|
||||
}
|
||||
|
||||
// Update PostHog client state based on telemetry preference
|
||||
if (this.telemetryEnabled) {
|
||||
this.client.optIn()
|
||||
// console.log("Telemetry enabled")
|
||||
} else {
|
||||
this.client.optOut()
|
||||
// console.log("Telemetry disabled")
|
||||
}
|
||||
}
|
||||
|
||||
public static getInstance(): PostHogClient {
|
||||
if (!PostHogClient.instance) {
|
||||
PostHogClient.instance = new PostHogClient()
|
||||
}
|
||||
return PostHogClient.instance
|
||||
}
|
||||
|
||||
public capture(event: { event: string; properties?: any }): void {
|
||||
// Only send events if telemetry is enabled
|
||||
if (this.telemetryEnabled) {
|
||||
this.client.capture({ distinctId: this.distinctId, event: event.event, properties: event.properties })
|
||||
// console.log("Captured event", { distinctId: this.distinctId, event: event.event, properties: event.properties })
|
||||
}
|
||||
}
|
||||
|
||||
public isTelemetryEnabled(): boolean {
|
||||
return this.telemetryEnabled
|
||||
}
|
||||
|
||||
public async shutdown(): Promise<void> {
|
||||
await this.client.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
// Export a single instance
|
||||
export const telemetryService = PostHogClient.getInstance()
|
||||
|
|
@ -81,6 +81,7 @@ export interface ExtensionState {
|
|||
photoURL: string | null
|
||||
}
|
||||
mcpMarketplaceEnabled?: boolean
|
||||
telemetryOptIn: boolean | null
|
||||
}
|
||||
|
||||
export interface ClineMessage {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ export interface WebviewMessage {
|
|||
| "searchCommits"
|
||||
| "showMcpView"
|
||||
| "fetchLatestMcpServersFromHub"
|
||||
| "telemetryOptIn"
|
||||
| "openTelemetrySettings"
|
||||
// | "relaunchChromeDebugMode"
|
||||
text?: string
|
||||
disabled?: boolean
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import BrowserSessionRow from "./BrowserSessionRow"
|
|||
import ChatRow from "./ChatRow"
|
||||
import ChatTextArea from "./ChatTextArea"
|
||||
import TaskHeader from "./TaskHeader"
|
||||
import TelemetryBanner from "../common/TelemetryBanner"
|
||||
|
||||
interface ChatViewProps {
|
||||
isHidden: boolean
|
||||
|
|
@ -37,7 +38,7 @@ interface ChatViewProps {
|
|||
export const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images
|
||||
|
||||
const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryView }: ChatViewProps) => {
|
||||
const { version, clineMessages: messages, taskHistory, apiConfiguration } = useExtensionState()
|
||||
const { version, clineMessages: messages, taskHistory, apiConfiguration, telemetryOptIn } = useExtensionState()
|
||||
|
||||
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined
|
||||
const task = useMemo(() => messages.at(0), [messages]) // leaving this less safe version here since if the first message is not a task, then the extension is in a bad state and needs to be debugged (see Cline.abort)
|
||||
|
|
@ -789,6 +790,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
flexDirection: "column",
|
||||
paddingBottom: "10px",
|
||||
}}>
|
||||
{telemetryOptIn === null && <TelemetryBanner />}
|
||||
|
||||
{showAnnouncement && <Announcement version={version} hideAnnouncement={hideAnnouncement} />}
|
||||
<div style={{ padding: "0 20px", flexShrink: 0 }}>
|
||||
<h2>What can I do for you?</h2>
|
||||
|
|
|
|||
70
webview-ui/src/components/common/TelemetryBanner.tsx
Normal file
70
webview-ui/src/components/common/TelemetryBanner.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useState } from "react"
|
||||
import styled from "styled-components"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
|
||||
const BannerContainer = styled.div`
|
||||
background-color: var(--vscode-banner-background);
|
||||
padding: 12px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
flex-shrink: 0;
|
||||
`
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
|
||||
& > vscode-button {
|
||||
flex: 1;
|
||||
}
|
||||
`
|
||||
|
||||
const TelemetryBanner = () => {
|
||||
const [hasChosen, setHasChosen] = useState(false)
|
||||
|
||||
const handleAllow = () => {
|
||||
setHasChosen(true)
|
||||
vscode.postMessage({ type: "telemetryOptIn", bool: true })
|
||||
}
|
||||
|
||||
const handleDeny = () => {
|
||||
setHasChosen(true)
|
||||
vscode.postMessage({ type: "telemetryOptIn", bool: false })
|
||||
}
|
||||
|
||||
const handleOpenSettings = () => {
|
||||
vscode.postMessage({ type: "openTelemetrySettings" })
|
||||
}
|
||||
|
||||
return (
|
||||
<BannerContainer>
|
||||
<div>
|
||||
<strong>Help Improve Cline</strong>
|
||||
<div style={{ marginTop: 4 }}>
|
||||
Send anonymous error and usage data to help us fix bugs and improve the extension. No code, prompts, or
|
||||
personal information is ever sent.
|
||||
<div style={{ marginTop: 4 }}>
|
||||
You can always change this in{" "}
|
||||
<VSCodeLink href="#" onClick={handleOpenSettings}>
|
||||
settings
|
||||
</VSCodeLink>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ButtonContainer>
|
||||
<VSCodeButton appearance="primary" onClick={handleAllow} disabled={hasChosen}>
|
||||
Allow
|
||||
</VSCodeButton>
|
||||
<VSCodeButton appearance="secondary" onClick={handleDeny} disabled={hasChosen}>
|
||||
Deny
|
||||
</VSCodeButton>
|
||||
</ButtonContainer>
|
||||
</BannerContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(TelemetryBanner)
|
||||
|
|
@ -39,6 +39,7 @@ export const ExtensionStateContextProvider: React.FC<{
|
|||
chatSettings: DEFAULT_CHAT_SETTINGS,
|
||||
isLoggedIn: false,
|
||||
platform: DEFAULT_PLATFORM,
|
||||
telemetryOptIn: null,
|
||||
})
|
||||
const [didHydrateState, setDidHydrateState] = useState(false)
|
||||
const [showWelcome, setShowWelcome] = useState(false)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue