mirror of
https://github.com/cline/cline.git
synced 2026-09-04 11:44:01 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 264e076621 | |||
| ba482c1d37 | |||
| 872123bfe4 | |||
| 6bf80959f0 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Include optional telemtry to help cline improve
|
||||
@@ -82,6 +82,8 @@ jobs:
|
||||
env:
|
||||
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
||||
OVSX_PAT: ${{ secrets.OVSX_PAT }}
|
||||
POSTHOG_PROJECT_API_KEY: ${{ secrets.POSTHOG_PROJECT_API_KEY }}
|
||||
POSTHOG_INSTANCE_ADDRESS: ${{ secrets.POSTHOG_INSTANCE_ADDRESS }}
|
||||
run: |
|
||||
# Required to generate the .vsix
|
||||
vsce package --out "cline-${{ steps.get_version.outputs.version }}.vsix"
|
||||
|
||||
+7
-1
@@ -7,4 +7,10 @@ tmp
|
||||
|
||||
.DS_Store
|
||||
|
||||
pnpm-lock.yaml
|
||||
pnpm-lock.yaml
|
||||
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ Cline Bot Inc. ("Cline," "we," "our," and/or "us") values the privacy of individ
|
||||
- Your data is only sent to your chosen AI provider (e.g., Anthropic, OpenAI) when you explicitly request assistance
|
||||
- All processing happens locally on your machine
|
||||
- API keys are stored securely in VS Code's built-in settings storage
|
||||
- Telemetry is collected anonymously via PostHog if the user opts in
|
||||
|
||||
## Information We Process
|
||||
|
||||
@@ -43,7 +44,7 @@ Cline functions solely as a client-side VS Code extension that facilitates commu
|
||||
|
||||
- All operations happen on your local machine
|
||||
- No central servers or data collection
|
||||
- No telemetry or usage statistics gathered
|
||||
- No telemetry or usage statistics gathered unless the user opts in
|
||||
- No account creation required
|
||||
|
||||
2. **API Key Security**:
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const esbuild = require("esbuild")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const dotenv = require("dotenv")
|
||||
// Load environment variables from .env.local
|
||||
dotenv.config({ path: ".env" })
|
||||
|
||||
const production = process.argv.includes("--production")
|
||||
const watch = process.argv.includes("--watch")
|
||||
@@ -78,6 +81,10 @@ const extensionConfig = {
|
||||
platform: "node",
|
||||
outfile: "dist/extension.js",
|
||||
external: ["vscode"],
|
||||
define: {
|
||||
"process.env.POSTHOG_PROJECT_API_KEY": JSON.stringify(process.env.POSTHOG_PROJECT_API_KEY || ""),
|
||||
"process.env.POSTHOG_INSTANCE_ADDRESS": JSON.stringify(process.env.POSTHOG_INSTANCE_ADDRESS || ""),
|
||||
},
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
Generated
+29
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "claude-dev",
|
||||
"version": "3.4.3",
|
||||
"version": "3.4.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-dev",
|
||||
"version": "3.4.3",
|
||||
"version": "3.4.6",
|
||||
"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",
|
||||
@@ -63,6 +64,7 @@
|
||||
"@vscode/test-cli": "^0.0.9",
|
||||
"@vscode/test-electron": "^2.4.0",
|
||||
"chai": "^4.3.10",
|
||||
"dotenv": "^16.4.7",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^8.57.0",
|
||||
"husky": "^9.1.7",
|
||||
@@ -7648,6 +7650,19 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.4.7",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
|
||||
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/duck": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz",
|
||||
@@ -11606,6 +11621,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": false,
|
||||
"description": "Enables extension to post anonymous error tracking & usage data to help improve the product."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,6 +233,7 @@
|
||||
"@vscode/test-cli": "^0.0.9",
|
||||
"@vscode/test-electron": "^2.4.0",
|
||||
"chai": "^4.3.10",
|
||||
"dotenv": "^16.4.7",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^8.57.0",
|
||||
"husky": "^9.1.7",
|
||||
@@ -268,6 +274,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",
|
||||
|
||||
@@ -59,6 +59,7 @@ import { formatResponse } from "./prompts/responses"
|
||||
import { addUserInstructions, SYSTEM_PROMPT } from "./prompts/system"
|
||||
import { getNextTruncationRange, getTruncatedMessages } from "./sliding-window"
|
||||
import { ClineProvider, GlobalFileNames } from "./webview/ClineProvider"
|
||||
import posthog from "../services/analytics/PostHogClient"
|
||||
|
||||
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution
|
||||
|
||||
@@ -148,6 +149,16 @@ export class Cline {
|
||||
} else {
|
||||
throw new Error("Either historyItem or task/images must be provided")
|
||||
}
|
||||
// capture start of thread with the state at the beginning
|
||||
posthog.capture({
|
||||
event: "cline created",
|
||||
properties: {
|
||||
taskId: this.taskId,
|
||||
isHistory: !!historyItem,
|
||||
chatMode: this.chatSettings.mode,
|
||||
hasImages: !!images,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
updateBrowserSettings(browserSettings: BrowserSettings) {
|
||||
@@ -3265,6 +3276,27 @@ export class Cline {
|
||||
this.consecutiveMistakeCount++
|
||||
}
|
||||
|
||||
posthog.capture({
|
||||
event: "message sent",
|
||||
properties: {
|
||||
taskId: this.taskId,
|
||||
chatMode: this.chatSettings.mode,
|
||||
apiConversationHistoryCount: this.apiConversationHistory.length,
|
||||
userMessageCount: this.apiConversationHistory.filter((m) => m.role === "user").length,
|
||||
assistantMessageCount: this.apiConversationHistory.filter((m) => m.role === "assistant").length,
|
||||
clineMessageCount: this.clineMessages.length,
|
||||
textMessageCount: this.clineMessages.filter((m) => !!m.text).length,
|
||||
askMessageCount: this.clineMessages.filter((m) => !!m.ask).length,
|
||||
sayMessageCount: this.clineMessages.filter((m) => !!m.say).length,
|
||||
reasoningMessageCount: this.clineMessages.filter((m) => !!m.reasoning).length,
|
||||
partialMessageCount: this.clineMessages.filter((m) => !!m.partial).length,
|
||||
consecutiveMistakeCount: this.consecutiveMistakeCount,
|
||||
consecutiveAutoApprovedRequestsCount: this.consecutiveAutoApprovedRequestsCount,
|
||||
toolUseCount: this.clineMessages.filter((m) => m.say === "tool").length,
|
||||
checkpointsCount: this.clineMessages.filter((m) => m.say === "checkpoint_created").length,
|
||||
},
|
||||
})
|
||||
|
||||
const recDidEndLoop = await this.recursivelyMakeClineRequests(this.userMessageContent)
|
||||
didEndLoop = recDidEndLoop
|
||||
} else {
|
||||
|
||||
@@ -94,6 +94,7 @@ type GlobalStateKey =
|
||||
| "requestyModelId"
|
||||
| "togetherModelId"
|
||||
| "mcpMarketplaceCatalog"
|
||||
| "hideTelemetryOptIn"
|
||||
|
||||
export const GlobalFileNames = {
|
||||
apiConversationHistory: "api_conversation_history.json",
|
||||
@@ -365,8 +366,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${webview.cspSource}; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} https: data:; script-src 'nonce-${nonce}';">
|
||||
<link rel="stylesheet" type="text/css" href="${stylesUri}">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; connect-src https://*.posthog.com; font-src ${webview.cspSource}; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} https: data:; script-src 'nonce-${nonce}' https://*.posthog.com;"> <link rel="stylesheet" type="text/css" href="${stylesUri}">
|
||||
<link href="${codiconsUri}" rel="stylesheet" />
|
||||
<title>Cline</title>
|
||||
</head>
|
||||
@@ -839,6 +839,12 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
)
|
||||
break
|
||||
}
|
||||
case "toggleTelemetryOptin": {
|
||||
await vscode.workspace.getConfiguration().update("cline.enableTelemetry", true, true)
|
||||
await this.updateGlobalState("hideTelemetryOptIn", 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)
|
||||
}
|
||||
@@ -1604,6 +1610,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
||||
userInfo,
|
||||
authToken,
|
||||
mcpMarketplaceEnabled,
|
||||
hideTelemetryOptIn,
|
||||
} = await this.getState()
|
||||
|
||||
return {
|
||||
@@ -1621,8 +1628,11 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
||||
browserSettings,
|
||||
chatSettings,
|
||||
isLoggedIn: !!authToken,
|
||||
advancedSettings: vscode.workspace.getConfiguration("cline"),
|
||||
vscMachineId: vscode.env.machineId,
|
||||
userInfo,
|
||||
mcpMarketplaceEnabled,
|
||||
hideTelemetryOptIn: hideTelemetryOptIn ?? false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1729,6 +1739,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
||||
previousModeModelInfo,
|
||||
qwenApiLine,
|
||||
liteLlmApiKey,
|
||||
hideTelemetryOptIn,
|
||||
] = await Promise.all([
|
||||
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
||||
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
||||
@@ -1780,6 +1791,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
||||
this.getGlobalState("previousModeModelInfo") as Promise<ModelInfo | undefined>,
|
||||
this.getGlobalState("qwenApiLine") as Promise<string | undefined>,
|
||||
this.getSecret("liteLlmApiKey") as Promise<string | undefined>,
|
||||
this.getGlobalState("hideTelemetryOptIn") as Promise<boolean | undefined>,
|
||||
])
|
||||
|
||||
let apiProvider: ApiProvider
|
||||
@@ -1857,6 +1869,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
|
||||
previousModeModelId,
|
||||
previousModeModelInfo,
|
||||
mcpMarketplaceEnabled,
|
||||
hideTelemetryOptIn,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-1
@@ -7,6 +7,7 @@ import { Logger } from "./services/logging/Logger"
|
||||
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 posthog from "./services/analytics/PostHogClient"
|
||||
|
||||
/*
|
||||
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
|
||||
@@ -57,6 +58,22 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
}),
|
||||
)
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidChangeConfiguration(async (e) => {
|
||||
if (e.affectsConfiguration("cline")) {
|
||||
Logger.log("Configuration changed")
|
||||
await sidebarProvider.postStateToWebview()
|
||||
const config = vscode.workspace.getConfiguration("cline")
|
||||
// we use optIn and optOut because we want to keep posthog active for feature flags
|
||||
if (config.get("enableTelemetry")) {
|
||||
posthog.optIn()
|
||||
} else {
|
||||
posthog.optOut()
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
const openClineInNewTab = async () => {
|
||||
Logger.log("Opening Cline in new tab")
|
||||
// (this example uses webviewProvider activation event which is necessary to deserialize cached webview, but since we use retainContextWhenHidden, we don't need to use that event)
|
||||
@@ -187,6 +204,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
|
||||
// This method is called when your extension is deactivated
|
||||
export function deactivate() {
|
||||
export async function deactivate() {
|
||||
Logger.log("Cline extension deactivated")
|
||||
await posthog.shutdown()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
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 constructor() {
|
||||
this.client = new PostHog("phc_5WnLHpYyC30Bsb7VSJ6DzcPXZ34JSF08DJLyM7svZ15", {
|
||||
host: "https://us.i.posthog.com",
|
||||
enableExceptionAutocapture: true,
|
||||
})
|
||||
}
|
||||
|
||||
public static getInstance(): PostHogClient {
|
||||
if (!PostHogClient.instance) {
|
||||
PostHogClient.instance = new PostHogClient()
|
||||
}
|
||||
return PostHogClient.instance
|
||||
}
|
||||
|
||||
public optIn(): void {
|
||||
this.client.identify({ distinctId: this.distinctId })
|
||||
this.client.optIn()
|
||||
}
|
||||
|
||||
public optOut(): void {
|
||||
this.client.optOut()
|
||||
}
|
||||
|
||||
public capture(event: { event: string; properties?: any }): void {
|
||||
this.client.capture({ distinctId: this.distinctId, event: event.event, properties: event.properties })
|
||||
}
|
||||
|
||||
public async shutdown(): Promise<void> {
|
||||
await this.client.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
// Export a single instance
|
||||
export default PostHogClient.getInstance()
|
||||
@@ -0,0 +1,19 @@
|
||||
export interface ClineConfiguration {
|
||||
vsCodeLmModelSelector?: {
|
||||
vendor?: string
|
||||
family?: string
|
||||
}
|
||||
mcp: {
|
||||
mode: "full" | "server-use-only" | "off"
|
||||
}
|
||||
enableCheckpoints: boolean
|
||||
enableTelemetry: boolean
|
||||
}
|
||||
|
||||
export const DEFAULT_ADVANCED_SETTINGS: ClineConfiguration = {
|
||||
mcp: {
|
||||
mode: "full",
|
||||
},
|
||||
enableCheckpoints: true,
|
||||
enableTelemetry: false,
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import { BrowserSettings } from "./BrowserSettings"
|
||||
import { ChatSettings } from "./ChatSettings"
|
||||
import { HistoryItem } from "./HistoryItem"
|
||||
import { McpServer, McpMarketplaceCatalog, McpMarketplaceItem, McpDownloadResponse } from "./mcp"
|
||||
import { ClineConfiguration } from "./AdvancedSettings"
|
||||
import { WorkspaceConfiguration } from "vscode"
|
||||
|
||||
// webview will hold state
|
||||
export interface ExtensionMessage {
|
||||
@@ -75,6 +77,9 @@ export interface ExtensionState {
|
||||
chatSettings: ChatSettings
|
||||
isLoggedIn: boolean
|
||||
platform: Platform
|
||||
advancedSettings: WorkspaceConfiguration | ClineConfiguration
|
||||
vscMachineId: string
|
||||
hideTelemetryOptIn: boolean
|
||||
userInfo?: {
|
||||
displayName: string | null
|
||||
email: string | null
|
||||
|
||||
@@ -51,6 +51,7 @@ export interface WebviewMessage {
|
||||
| "showMcpView"
|
||||
| "fetchLatestMcpServersFromHub"
|
||||
| "updateMcpTimeout"
|
||||
| "toggleTelemetryOptin"
|
||||
// | "relaunchChromeDebugMode"
|
||||
text?: string
|
||||
disabled?: boolean
|
||||
|
||||
Generated
+66
@@ -15,6 +15,7 @@
|
||||
"fuse.js": "^7.0.0",
|
||||
"fzf": "^0.5.2",
|
||||
"mermaid": "^11.4.1",
|
||||
"posthog-js": "^1.223.3",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -37,6 +38,7 @@
|
||||
"@types/node": "^20.x",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/vscode": "^1.84.0",
|
||||
"@types/vscode-webview": "^1.57.5",
|
||||
"jsdom": "^25.0.1",
|
||||
"vitest": "^2.1.9"
|
||||
@@ -4285,6 +4287,16 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rrweb/types": {
|
||||
"version": "2.0.0-alpha.17",
|
||||
"resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.0.0-alpha.17.tgz",
|
||||
"integrity": "sha512-AfDTVUuCyCaIG0lTSqYtrZqJX39ZEYzs4fYKnexhQ+id+kbZIpIJtaut5cto6dWZbB3SEe4fW0o90Po3LvTmfg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"rrweb-snapshot": "^2.0.0-alpha.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||
@@ -5329,6 +5341,13 @@
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/vscode": {
|
||||
"version": "1.97.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.97.0.tgz",
|
||||
"integrity": "sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/vscode-webview": {
|
||||
"version": "1.57.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.5.tgz",
|
||||
@@ -10201,6 +10220,12 @@
|
||||
"bser": "2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.4.8",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
|
||||
"integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
||||
@@ -16628,6 +16653,37 @@
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/posthog-js": {
|
||||
"version": "1.223.3",
|
||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.223.3.tgz",
|
||||
"integrity": "sha512-ZQTc17M21IzkQmECJa2Xjont4tZrvIn252uGT3sTfmahTqZoW4j+kBj4eOJt9SNR6hOheFNkg7MSiI/rA6FaDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-js": "^3.38.1",
|
||||
"fflate": "^0.4.8",
|
||||
"preact": "^10.19.3",
|
||||
"web-vitals": "^4.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@rrweb/types": "2.0.0-alpha.17"
|
||||
}
|
||||
},
|
||||
"node_modules/posthog-js/node_modules/web-vitals": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz",
|
||||
"integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.26.2",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.2.tgz",
|
||||
"integrity": "sha512-0gNmv4qpS9HaN3+40CLBAnKe0ZfyE4ZWo5xKlC1rVrr0ckkEvJvAQqKaHANdFKsGstoxrY4AItZ7kZSGVoVjgg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -17700,6 +17756,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rrweb-snapshot": {
|
||||
"version": "2.0.0-alpha.18",
|
||||
"resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-2.0.0-alpha.18.tgz",
|
||||
"integrity": "sha512-hBHZL/NfgQX6wO1D9mpwqFu1NJPpim+moIcKhFEjVTZVRUfCln+LOugRc4teVTCISYHN8Cw5e2iNTWCSm+SkoA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"postcss": "^8.4.38"
|
||||
}
|
||||
},
|
||||
"node_modules/rtl-css-js": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"fuse.js": "^7.0.0",
|
||||
"fzf": "^0.5.2",
|
||||
"mermaid": "^11.4.1",
|
||||
"posthog-js": "^1.223.3",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -60,6 +61,7 @@
|
||||
"@types/node": "^20.x",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/vscode": "^1.84.0",
|
||||
"@types/vscode-webview": "^1.57.5",
|
||||
"jsdom": "^25.0.1",
|
||||
"vitest": "^2.1.9"
|
||||
|
||||
@@ -12,6 +12,14 @@
|
||||
const rewire = require("rewire")
|
||||
const defaults = rewire("react-scripts/scripts/build.js")
|
||||
const config = defaults.__get__("config")
|
||||
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin")
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
const dotenv = require("dotenv")
|
||||
const webpack = require("webpack")
|
||||
|
||||
// Load environment variables from root .env.local
|
||||
dotenv.config({ path: "../.env" })
|
||||
|
||||
/* Modifying Webpack Configuration for 'shared' dir
|
||||
This section uses Rewire to modify Create React App's webpack configuration without ejecting. Rewire allows us to inject and alter the internal build scripts of CRA at runtime. This allows us to maintain a flexible project structure that keeps shared code outside the webview-ui/src directory, while still adhering to CRA's security model that typically restricts imports to within src/.
|
||||
@@ -21,12 +29,19 @@ Before, we would just import types from shared dir and specifying include (and a
|
||||
- Imports from the shared directory must use full paths relative to the src directory, without file extensions.
|
||||
- Example: import { someFunction } from '../../src/shared/utils/helpers'
|
||||
*/
|
||||
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin")
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
|
||||
// Get all files in the shared directory
|
||||
const sharedDir = path.resolve(__dirname, "..", "..", "src", "shared")
|
||||
|
||||
config.plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
"process.env": {
|
||||
POSTHOG_PROJECT_API_KEY: JSON.stringify(process.env.POSTHOG_PROJECT_API_KEY || ""),
|
||||
POSTHOG_INSTANCE_ADDRESS: JSON.stringify(process.env.POSTHOG_INSTANCE_ADDRESS || ""),
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
function getAllFiles(dir) {
|
||||
let files = []
|
||||
fs.readdirSync(dir).forEach((file) => {
|
||||
|
||||
+14
-1
@@ -9,14 +9,17 @@ import AccountView from "./components/account/AccountView"
|
||||
import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext"
|
||||
import { vscode } from "./utils/vscode"
|
||||
import McpView from "./components/mcp/McpView"
|
||||
import posthog from "posthog-js"
|
||||
|
||||
const AppContent = () => {
|
||||
const { didHydrateState, showWelcome, shouldShowAnnouncement } = useExtensionState()
|
||||
const { didHydrateState, showWelcome, shouldShowAnnouncement, advancedSettings, vscMachineId, hideTelemetryOptIn } =
|
||||
useExtensionState()
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
const [showHistory, setShowHistory] = useState(false)
|
||||
const [showMcp, setShowMcp] = useState(false)
|
||||
const [showAccount, setShowAccount] = useState(false)
|
||||
const [showAnnouncement, setShowAnnouncement] = useState(false)
|
||||
const telemetryEnabled = advancedSettings.enableTelemetry
|
||||
|
||||
const handleMessage = useCallback((e: MessageEvent) => {
|
||||
const message: ExtensionMessage = e.data
|
||||
@@ -60,6 +63,15 @@ const AppContent = () => {
|
||||
|
||||
useEvent("message", handleMessage)
|
||||
|
||||
useEffect(() => {
|
||||
if (telemetryEnabled) {
|
||||
posthog.identify(vscMachineId)
|
||||
posthog.opt_in_capturing()
|
||||
} else {
|
||||
posthog.opt_out_capturing()
|
||||
}
|
||||
}, [telemetryEnabled, vscMachineId])
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldShowAnnouncement) {
|
||||
setShowAnnouncement(true)
|
||||
@@ -93,6 +105,7 @@ const AppContent = () => {
|
||||
hideAnnouncement={() => {
|
||||
setShowAnnouncement(false)
|
||||
}}
|
||||
hideTelemetryOptIn={hideTelemetryOptIn}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import debounce from "debounce"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useDeepCompareEffect, useEvent, useMount } from "react-use"
|
||||
@@ -26,17 +26,19 @@ import BrowserSessionRow from "./BrowserSessionRow"
|
||||
import ChatRow from "./ChatRow"
|
||||
import ChatTextArea from "./ChatTextArea"
|
||||
import TaskHeader from "./TaskHeader"
|
||||
import TelemetryOptin from "./TelemetryOptin"
|
||||
|
||||
interface ChatViewProps {
|
||||
isHidden: boolean
|
||||
showAnnouncement: boolean
|
||||
hideAnnouncement: () => void
|
||||
showHistoryView: () => void
|
||||
hideTelemetryOptIn: boolean
|
||||
}
|
||||
|
||||
export const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images
|
||||
|
||||
const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryView }: ChatViewProps) => {
|
||||
const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryView, hideTelemetryOptIn }: ChatViewProps) => {
|
||||
const { version, clineMessages: messages, taskHistory, apiConfiguration } = useExtensionState()
|
||||
|
||||
//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined
|
||||
@@ -790,6 +792,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
paddingBottom: "10px",
|
||||
}}>
|
||||
{showAnnouncement && <Announcement version={version} hideAnnouncement={hideAnnouncement} />}
|
||||
{!hideTelemetryOptIn && <TelemetryOptin />}
|
||||
<div style={{ padding: "0 20px", flexShrink: 0 }}>
|
||||
<h2>What can I do for you?</h2>
|
||||
<p>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useState, useEffect } from "react"
|
||||
import { getAsVar, VSC_INACTIVE_SELECTION_BACKGROUND } from "../../utils/vscStyles"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
|
||||
const boxStyles = {
|
||||
backgroundColor: getAsVar(VSC_INACTIVE_SELECTION_BACKGROUND),
|
||||
borderRadius: "3px",
|
||||
padding: "12px 16px",
|
||||
margin: "5px 15px 5px 15px",
|
||||
position: "relative" as const,
|
||||
flexShrink: 0,
|
||||
minHeight: "120px",
|
||||
display: "flex",
|
||||
flexDirection: "column" as const,
|
||||
justifyContent: "center" as const,
|
||||
}
|
||||
|
||||
const TelemetryOptin = () => {
|
||||
const [showThankYou, setShowThankYou] = useState(false)
|
||||
const [isVisible, setIsVisible] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (showThankYou) {
|
||||
const fadeTimer = setTimeout(() => {
|
||||
setIsVisible(false)
|
||||
}, 1500)
|
||||
|
||||
const closeTimer = setTimeout(() => {
|
||||
vscode.postMessage({ type: "toggleTelemetryOptin", bool: true })
|
||||
}, 2000)
|
||||
|
||||
return () => {
|
||||
clearTimeout(fadeTimer)
|
||||
clearTimeout(closeTimer)
|
||||
}
|
||||
}
|
||||
}, [showThankYou])
|
||||
|
||||
const handleOptIn = () => {
|
||||
setShowThankYou(true)
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
vscode.postMessage({ type: "toggleTelemetryOptin", bool: false })
|
||||
}
|
||||
|
||||
if (showThankYou) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
...boxStyles,
|
||||
opacity: isVisible ? 1 : 0,
|
||||
transition: "opacity 0.5s ease-out",
|
||||
textAlign: "center",
|
||||
}}>
|
||||
<h3 style={{ margin: "0" }}>Thank you for helping improve Cline!</h3>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={boxStyles}>
|
||||
<h3 style={{ margin: "0 0 8px" }}>Help Improve Cline</h3>
|
||||
<p style={{ margin: "0 0 12px" }}>
|
||||
Would you like to help make Cline better by sending anonymous error reports and usage data? No personal or project
|
||||
information will be collected. You can change this setting anytime in{" "}
|
||||
<VSCodeLink onClick={() => vscode.postMessage({ type: "openExtensionSettings" })}>VS Code preferences</VSCodeLink>
|
||||
.{" "}
|
||||
<VSCodeLink href="https://github.com/cline/cline/blob/main/docs/PRIVACY.md" style={{ display: "inline" }}>
|
||||
Learn more
|
||||
</VSCodeLink>
|
||||
</p>
|
||||
<div style={{ display: "flex", gap: "8px" }}>
|
||||
<VSCodeButton appearance="primary" onClick={handleOptIn}>
|
||||
Opt In
|
||||
</VSCodeButton>
|
||||
<VSCodeButton appearance="secondary" onClick={handleCancel}>
|
||||
Cancel
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(TelemetryOptin)
|
||||
@@ -9,6 +9,7 @@ import { convertTextMateToHljs } from "../utils/textMateToHljs"
|
||||
import { vscode } from "../utils/vscode"
|
||||
import { DEFAULT_BROWSER_SETTINGS } from "../../../src/shared/BrowserSettings"
|
||||
import { DEFAULT_CHAT_SETTINGS } from "../../../src/shared/ChatSettings"
|
||||
import { DEFAULT_ADVANCED_SETTINGS } from "../../../src/shared/AdvancedSettings"
|
||||
|
||||
interface ExtensionStateContextType extends ExtensionState {
|
||||
didHydrateState: boolean
|
||||
@@ -39,6 +40,9 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
chatSettings: DEFAULT_CHAT_SETTINGS,
|
||||
isLoggedIn: false,
|
||||
platform: DEFAULT_PLATFORM,
|
||||
vscMachineId: "",
|
||||
advancedSettings: DEFAULT_ADVANCED_SETTINGS,
|
||||
hideTelemetryOptIn: false,
|
||||
})
|
||||
const [didHydrateState, setDidHydrateState] = useState(false)
|
||||
const [showWelcome, setShowWelcome] = useState(false)
|
||||
|
||||
@@ -4,11 +4,17 @@ import "./index.css"
|
||||
import App from "./App"
|
||||
import reportWebVitals from "./reportWebVitals"
|
||||
import "../../node_modules/@vscode/codicons/dist/codicon.css"
|
||||
import { PostHogProvider } from "posthog-js/react"
|
||||
|
||||
const apiKey = process.env.POSTHOG_PROJECT_API_KEY || ""
|
||||
const instanceAddress = process.env.POSTHOG_INSTANCE_ADDRESS || ""
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
<PostHogProvider apiKey={apiKey} options={{ api_host: instanceAddress, capture_pageview: false }}>
|
||||
<App />
|
||||
</PostHogProvider>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user