mirror of
https://github.com/cline/cline.git
synced 2026-09-11 16:42:40 +08:00
Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe2a27fe25 | ||
|
|
480bc37c6b | ||
|
|
9e0d6661a5 | ||
|
|
4badb2eb5a | ||
|
|
9ec4e1ffde | ||
|
|
195fdea95c | ||
|
|
5d48e59276 | ||
|
|
2fdc285cb5 | ||
|
|
52c7b8f94e | ||
|
|
b21717b009 | ||
|
|
db70cd5a70 | ||
|
|
267f6cd4ca | ||
|
|
a3b9bffc05 | ||
|
|
ec291b6ee2 | ||
|
|
5c1b15dd8c | ||
|
|
e38ee596bf | ||
|
|
7b92356ae1 | ||
|
|
af1936ee2b | ||
|
|
baa83821c4 | ||
|
|
e0880de85e | ||
|
|
4cd7d90b9e | ||
|
|
97a8b3c55b | ||
|
|
eeda58c8ac | ||
|
|
4dabfee1a6 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
gRPC over vscode message bus to make messaging better
|
||||
Generated
+164
-36
@@ -1,20 +1,22 @@
|
||||
{
|
||||
"name": "claude-dev",
|
||||
"version": "3.12.0",
|
||||
"version": "3.12.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-dev",
|
||||
"version": "3.12.0",
|
||||
"version": "3.12.3",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/bedrock-sdk": "^0.12.4",
|
||||
"@anthropic-ai/sdk": "^0.37.0",
|
||||
"@anthropic-ai/vertex-sdk": "^0.6.4",
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.758.0",
|
||||
"@bufbuild/protobuf": "^2.2.5",
|
||||
"@google-cloud/vertexai": "^1.9.3",
|
||||
"@google/generative-ai": "^0.18.0",
|
||||
"@grpc/grpc-js": "^1.9.15",
|
||||
"@mistralai/mistralai": "^1.5.0",
|
||||
"@modelcontextprotocol/sdk": "^1.7.0",
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
@@ -79,15 +81,18 @@
|
||||
"@vscode/test-cli": "^0.0.9",
|
||||
"@vscode/test-electron": "^2.4.0",
|
||||
"chai": "^4.3.10",
|
||||
"chalk": "^5.3.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^8.57.0",
|
||||
"husky": "^9.1.7",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.3.3",
|
||||
"protoc-gen-ts": "^0.8.7",
|
||||
"proxyquire": "^2.1.3",
|
||||
"should": "^13.2.3",
|
||||
"sinon": "^19.0.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-proto": "^2.6.1",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3957,6 +3962,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@bufbuild/protobuf": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.5.tgz",
|
||||
"integrity": "sha512-/g5EzJifw5GF8aren8wZ/G5oMuPoGeS6MQD3ca8ddcvdXR5UELUfdTZITCGNhNXynY/AYl3Z4plmxdj/tRl/hQ==",
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/@changesets/apply-release-plan": {
|
||||
"version": "7.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.8.tgz",
|
||||
@@ -5566,6 +5577,7 @@
|
||||
"version": "1.9.15",
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz",
|
||||
"integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.7.8",
|
||||
"@types/node": ">=12.12.47"
|
||||
@@ -9919,6 +9931,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/case-anything": {
|
||||
"version": "2.1.13",
|
||||
"resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz",
|
||||
"integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.13"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mesqueeb"
|
||||
}
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "4.3.10",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz",
|
||||
@@ -9938,35 +9963,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chardet": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
||||
@@ -10612,6 +10620,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"detect-libc": "bin/detect-libc.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/devtools-protocol": {
|
||||
"version": "0.0.1342118",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1342118.tgz",
|
||||
@@ -10722,6 +10743,16 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dprint-node": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz",
|
||||
"integrity": "sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-libc": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/duck": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz",
|
||||
@@ -11192,6 +11223,23 @@
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/glob-parent": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
@@ -11240,6 +11288,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
|
||||
@@ -13659,6 +13720,36 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-symbols/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-symbols/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz",
|
||||
@@ -14768,19 +14859,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ora/node_modules/chalk": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ora/node_modules/emoji-regex": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
|
||||
@@ -15366,6 +15444,20 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/protoc-gen-ts": {
|
||||
"version": "0.8.7",
|
||||
"resolved": "https://registry.npmjs.org/protoc-gen-ts/-/protoc-gen-ts-0.8.7.tgz",
|
||||
"integrity": "sha512-jr4VJey2J9LVYCV7EVyVe53g1VMw28cCmYJhBe5e3YX5wiyiDwgxWxeDf9oTqAe4P1bN/YGAkW2jhlH8LohwiQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"protoc-gen-ts": "protoc-gen-ts.js"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://www.buymeacoffee.com/thesayyn"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -16970,6 +17062,42 @@
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-poet": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.11.0.tgz",
|
||||
"integrity": "sha512-r5AGF8vvb+GjBsnqiTqbLhN1/U2FJt6BI+k0dfCrkKzWvUhNlwMmq9nDHuucHs45LomgHjZPvYj96dD3JawjJA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"dprint-node": "^1.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-proto": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-2.7.0.tgz",
|
||||
"integrity": "sha512-BGHjse2wTOeswOqnnPKinpxmbaRd882so/e1En6ww59YMG7AO9Kg4vPpJcbVfrpBixPRDqHafXD/RDyd2T99GA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.0.0",
|
||||
"case-anything": "^2.1.13",
|
||||
"ts-poet": "^6.7.0",
|
||||
"ts-proto-descriptors": "2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"protoc-gen-ts_proto": "protoc-gen-ts_proto"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-proto-descriptors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-2.0.0.tgz",
|
||||
"integrity": "sha512-wHcTH3xIv11jxgkX5OyCSFfw27agpInAd6yh89hKG6zqIXnjW9SYqSER2CVQxdPj4czeOhGagNvZBEbJPy7qkw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
|
||||
@@ -290,6 +290,7 @@
|
||||
"watch:esbuild:test": "IS_TEST=true node esbuild.js --watch",
|
||||
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
|
||||
"package": "npm run build:webview && npm run check-types && npm run lint && node esbuild.js --production",
|
||||
"protos": "node proto/build-proto.js && prettier src/shared/proto --write && prettier src/core/controller --write",
|
||||
"package:test": "IS_TEST=true npm run build:webview:test && npm run check-types && npm run lint && IS_TEST=true node esbuild.js --production",
|
||||
"build:webview:test": "cd webview-ui && IS_TEST=true npm run build",
|
||||
"watch:test": "IS_TEST=true npm-run-all -p watch:tsc watch:esbuild:test",
|
||||
@@ -334,15 +335,18 @@
|
||||
"@vscode/test-cli": "^0.0.9",
|
||||
"@vscode/test-electron": "^2.4.0",
|
||||
"chai": "^4.3.10",
|
||||
"chalk": "^5.3.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^8.57.0",
|
||||
"husky": "^9.1.7",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.3.3",
|
||||
"protoc-gen-ts": "^0.8.7",
|
||||
"proxyquire": "^2.1.3",
|
||||
"should": "^13.2.3",
|
||||
"sinon": "^19.0.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-proto": "^2.6.1",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -350,8 +354,10 @@
|
||||
"@anthropic-ai/sdk": "^0.37.0",
|
||||
"@anthropic-ai/vertex-sdk": "^0.6.4",
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.758.0",
|
||||
"@bufbuild/protobuf": "^2.2.5",
|
||||
"@google-cloud/vertexai": "^1.9.3",
|
||||
"@google/generative-ai": "^0.18.0",
|
||||
"@grpc/grpc-js": "^1.9.15",
|
||||
"@mistralai/mistralai": "^1.5.0",
|
||||
"@modelcontextprotocol/sdk": "^1.7.0",
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package cline;
|
||||
|
||||
import "common.proto";
|
||||
|
||||
service BrowserService {
|
||||
rpc getBrowserConnectionInfo(EmptyRequest) returns (BrowserConnectionInfo);
|
||||
}
|
||||
|
||||
message BrowserConnectionInfo {
|
||||
bool is_connected = 1;
|
||||
bool is_remote = 2;
|
||||
string host = 3; // Optional, may be empty
|
||||
}
|
||||
Executable
+157
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as fs from "fs/promises"
|
||||
import * as path from "path"
|
||||
import { execSync } from "child_process"
|
||||
import { globby } from "globby"
|
||||
import chalk from "chalk"
|
||||
|
||||
// Get script directory and root directory
|
||||
const SCRIPT_DIR = path.dirname(new URL(import.meta.url).pathname)
|
||||
const ROOT_DIR = path.resolve(SCRIPT_DIR, "..")
|
||||
|
||||
async function main() {
|
||||
console.log(chalk.bold.blue("Starting Protocol Buffer code generation..."))
|
||||
|
||||
// Check if protoc is installed
|
||||
try {
|
||||
const options = { stdio: "ignore" }
|
||||
execSync("protoc --version", options)
|
||||
} catch (error) {
|
||||
console.warn(chalk.yellow("Warning: protoc is not installed. Skipping proto generation."))
|
||||
console.warn(chalk.yellow("To install Protocol Buffers compiler, visit: https://grpc.io/docs/protoc-installation/"))
|
||||
process.exit(0) // Exit with success as requested
|
||||
}
|
||||
|
||||
// Check if ts-proto plugin is available
|
||||
const TS_PROTO_PLUGIN = path.join(ROOT_DIR, "node_modules", ".bin", "protoc-gen-ts_proto")
|
||||
try {
|
||||
await fs.access(TS_PROTO_PLUGIN)
|
||||
} catch (error) {
|
||||
console.error(chalk.red("Error: ts-proto plugin not found at"), TS_PROTO_PLUGIN)
|
||||
console.error(chalk.red('Please run "npm install" to install the required dependencies.'))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Define output directories
|
||||
const TS_OUT_DIR = path.join(ROOT_DIR, "src", "shared", "proto")
|
||||
|
||||
// Create output directory if it doesn't exist
|
||||
await fs.mkdir(TS_OUT_DIR, { recursive: true })
|
||||
|
||||
// Clean up existing generated files
|
||||
console.log(chalk.cyan("Cleaning up existing generated TypeScript files..."))
|
||||
const existingFiles = await globby("**/*.ts", { cwd: TS_OUT_DIR })
|
||||
for (const file of existingFiles) {
|
||||
await fs.unlink(path.join(TS_OUT_DIR, file))
|
||||
}
|
||||
|
||||
// Process all proto files
|
||||
console.log(chalk.cyan("Processing proto files from"), SCRIPT_DIR)
|
||||
const protoFiles = await globby("**/*.proto", { cwd: SCRIPT_DIR })
|
||||
|
||||
for (const protoFile of protoFiles) {
|
||||
console.log(chalk.cyan(`Generating TypeScript code for ${protoFile}...`))
|
||||
|
||||
// Build the protoc command with proper path handling for cross-platform
|
||||
const protocCommand = [
|
||||
"protoc",
|
||||
`--plugin=protoc-gen-ts_proto="${TS_PROTO_PLUGIN}"`,
|
||||
`--ts_proto_out="${TS_OUT_DIR}"`,
|
||||
"--ts_proto_opt=outputServices=generic-definitions,env=node,esModuleInterop=true,useDate=false,useOptionals=messages",
|
||||
`--proto_path="${SCRIPT_DIR}"`,
|
||||
`"${path.join(SCRIPT_DIR, protoFile)}"`,
|
||||
].join(" ")
|
||||
|
||||
try {
|
||||
const execOptions = {
|
||||
stdio: "inherit",
|
||||
}
|
||||
execSync(protocCommand, execOptions)
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`Error generating TypeScript for ${protoFile}:`), error)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(chalk.green("Protocol Buffer code generation completed successfully."))
|
||||
console.log(chalk.green(`TypeScript files generated in: ${TS_OUT_DIR}`))
|
||||
|
||||
// Generate method registration files
|
||||
await generateMethodRegistrations()
|
||||
|
||||
// Make the script executable
|
||||
try {
|
||||
await fs.chmod(path.join(SCRIPT_DIR, "build-proto.js"), 0o755)
|
||||
} catch (error) {
|
||||
console.warn(chalk.yellow("Warning: Could not make script executable:"), error)
|
||||
}
|
||||
}
|
||||
|
||||
async function generateMethodRegistrations() {
|
||||
console.log(chalk.cyan("Generating method registration files..."))
|
||||
|
||||
const serviceDirs = [
|
||||
path.join(ROOT_DIR, "src", "core", "controller", "browser"),
|
||||
// Add more service directories here as needed
|
||||
]
|
||||
|
||||
for (const serviceDir of serviceDirs) {
|
||||
try {
|
||||
await fs.access(serviceDir)
|
||||
} catch (error) {
|
||||
console.log(chalk.gray(`Skipping ${serviceDir} - directory does not exist`))
|
||||
continue
|
||||
}
|
||||
|
||||
const serviceName = path.basename(serviceDir)
|
||||
const registryFile = path.join(serviceDir, "methods.ts")
|
||||
|
||||
console.log(chalk.cyan(`Generating method registrations for ${serviceName}...`))
|
||||
|
||||
// Get all TypeScript files in the service directory
|
||||
const files = await globby("*.ts", { cwd: serviceDir })
|
||||
|
||||
// Filter out index.ts and methods.ts
|
||||
const implementationFiles = files.filter((file) => file !== "index.ts" && file !== "methods.ts")
|
||||
|
||||
// Create the output file with header
|
||||
let content = `// AUTO-GENERATED FILE - DO NOT MODIFY DIRECTLY
|
||||
// Generated by proto/build-proto.js
|
||||
|
||||
// Import all method implementations
|
||||
import { registerMethod } from "./index"\n`
|
||||
|
||||
// Add imports for all implementation files
|
||||
for (const file of implementationFiles) {
|
||||
const baseName = path.basename(file, ".ts")
|
||||
content += `import { ${baseName} } from "./${baseName}"\n`
|
||||
}
|
||||
|
||||
// Add registration function
|
||||
content += `\n// Register all ${serviceName} service methods
|
||||
export function registerAllMethods(): void {
|
||||
\t// Register each method with the registry\n`
|
||||
|
||||
// Add registration statements
|
||||
for (const file of implementationFiles) {
|
||||
const baseName = path.basename(file, ".ts")
|
||||
content += `\tregisterMethod("${baseName}", ${baseName})\n`
|
||||
}
|
||||
|
||||
// Close the function
|
||||
content += `}`
|
||||
|
||||
// Write the file
|
||||
await fs.writeFile(registryFile, content)
|
||||
console.log(chalk.green(`Generated ${registryFile}`))
|
||||
}
|
||||
|
||||
console.log(chalk.green("Method registration files generated successfully."))
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
main().catch((error) => {
|
||||
console.error(chalk.red("Error:"), error)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package cline;
|
||||
|
||||
message Metadata {
|
||||
}
|
||||
|
||||
message EmptyRequest {
|
||||
Metadata metadata = 1;
|
||||
}
|
||||
|
||||
message Empty {
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { BrowserConnectionInfo } from "../../../shared/proto/browser"
|
||||
import { EmptyRequest } from "../../../shared/proto/common"
|
||||
import { Controller } from "../index"
|
||||
import { getAllExtensionState } from "../../storage/state"
|
||||
|
||||
/**
|
||||
* Get information about the current browser connection
|
||||
* @param controller The controller instance
|
||||
* @param request The request message
|
||||
* @returns The browser connection info
|
||||
*/
|
||||
export async function getBrowserConnectionInfo(controller: Controller, request: EmptyRequest): Promise<BrowserConnectionInfo> {
|
||||
try {
|
||||
// Get browser settings from extension state
|
||||
const { browserSettings } = await getAllExtensionState(controller.context)
|
||||
|
||||
// Check if there's an active browser session by using the controller's handleWebviewMessage approach
|
||||
// This is similar to what's done in controller/index.ts for the "getBrowserConnectionInfo" message
|
||||
if (controller.task?.browserSession) {
|
||||
// Access the browser session through the controller's task property
|
||||
// Using indexer notation to access private property
|
||||
const browserSession = controller.task.browserSession
|
||||
const connectionInfo = browserSession.getConnectionInfo()
|
||||
|
||||
// Convert from BrowserSession.BrowserConnectionInfo to proto.BrowserConnectionInfo
|
||||
return {
|
||||
isConnected: connectionInfo.isConnected,
|
||||
isRemote: connectionInfo.isRemote,
|
||||
host: connectionInfo.host || "", // Ensure host is never undefined
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to browser settings if no active browser session
|
||||
return {
|
||||
isConnected: false,
|
||||
isRemote: !!browserSettings.remoteBrowserEnabled,
|
||||
host: browserSettings.remoteBrowserHost || "",
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error("Error getting browser connection info:", error)
|
||||
return {
|
||||
isConnected: false,
|
||||
isRemote: false,
|
||||
host: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { createServiceRegistry, ServiceMethodHandler } from "../grpc-service"
|
||||
import { registerAllMethods } from "./methods"
|
||||
|
||||
// Create browser service registry
|
||||
const browserService = createServiceRegistry("browser")
|
||||
|
||||
// Export the method handler type and registration function
|
||||
export type BrowserMethodHandler = ServiceMethodHandler
|
||||
export const registerMethod = browserService.registerMethod
|
||||
|
||||
// Export the request handler
|
||||
export const handleBrowserServiceRequest = browserService.handleRequest
|
||||
|
||||
// Register all browser methods
|
||||
registerAllMethods()
|
||||
@@ -0,0 +1,12 @@
|
||||
// AUTO-GENERATED FILE - DO NOT MODIFY DIRECTLY
|
||||
// Generated by proto/build-proto.js
|
||||
|
||||
// Import all method implementations
|
||||
import { registerMethod } from "./index"
|
||||
import { getBrowserConnectionInfo } from "./getBrowserConnectionInfo"
|
||||
|
||||
// Register all browser service methods
|
||||
export function registerAllMethods(): void {
|
||||
// Register each method with the registry
|
||||
registerMethod("getBrowserConnectionInfo", getBrowserConnectionInfo)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Controller } from "./index"
|
||||
import { handleBrowserServiceRequest } from "./browser/index"
|
||||
import { ExtensionMessage } from "../../shared/ExtensionMessage"
|
||||
|
||||
/**
|
||||
* Handles gRPC requests from the webview
|
||||
*/
|
||||
export class GrpcHandler {
|
||||
constructor(private controller: Controller) {}
|
||||
|
||||
/**
|
||||
* Handle a gRPC request from the webview
|
||||
* @param service The service name
|
||||
* @param method The method name
|
||||
* @param message The request message
|
||||
* @param requestId The request ID for response correlation
|
||||
* @returns The response message or error
|
||||
*/
|
||||
async handleRequest(
|
||||
service: string,
|
||||
method: string,
|
||||
message: any,
|
||||
requestId: string,
|
||||
): Promise<{
|
||||
message?: any
|
||||
error?: string
|
||||
request_id: string
|
||||
}> {
|
||||
try {
|
||||
// Handle BrowserService requests
|
||||
if (service === "cline.BrowserService") {
|
||||
return {
|
||||
message: await handleBrowserServiceRequest(this.controller, method, message),
|
||||
request_id: requestId,
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unknown service: ${service}`)
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
request_id: requestId,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a gRPC request from the webview
|
||||
* @param controller The controller instance
|
||||
* @param request The gRPC request
|
||||
*/
|
||||
export async function handleGrpcRequest(
|
||||
controller: Controller,
|
||||
request: {
|
||||
service: string
|
||||
method: string
|
||||
message: any
|
||||
request_id: string
|
||||
},
|
||||
) {
|
||||
try {
|
||||
const grpcHandler = new GrpcHandler(controller)
|
||||
const response = await grpcHandler.handleRequest(request.service, request.method, request.message, request.request_id)
|
||||
|
||||
// Send the response back to the webview
|
||||
await controller.postMessageToWebview({
|
||||
type: "grpc_response",
|
||||
grpc_response: response,
|
||||
})
|
||||
} catch (error) {
|
||||
// Send error response
|
||||
await controller.postMessageToWebview({
|
||||
type: "grpc_response",
|
||||
grpc_response: {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
request_id: request.request_id,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Controller } from "./index"
|
||||
|
||||
/**
|
||||
* Generic type for service method handlers
|
||||
*/
|
||||
export type ServiceMethodHandler = (controller: Controller, message: any) => Promise<any>
|
||||
|
||||
/**
|
||||
* Generic service registry for gRPC services
|
||||
*/
|
||||
export class ServiceRegistry {
|
||||
private serviceName: string
|
||||
private methodRegistry: Record<string, ServiceMethodHandler> = {}
|
||||
|
||||
/**
|
||||
* Create a new service registry
|
||||
* @param serviceName The name of the service (used for logging)
|
||||
*/
|
||||
constructor(serviceName: string) {
|
||||
this.serviceName = serviceName
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a method handler
|
||||
* @param methodName The name of the method to register
|
||||
* @param handler The handler function for the method
|
||||
*/
|
||||
registerMethod(methodName: string, handler: ServiceMethodHandler): void {
|
||||
this.methodRegistry[methodName] = handler
|
||||
console.log(`Registered ${this.serviceName} method: ${methodName}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a service request
|
||||
* @param controller The controller instance
|
||||
* @param method The method name
|
||||
* @param message The request message
|
||||
* @returns The response message
|
||||
*/
|
||||
async handleRequest(controller: Controller, method: string, message: any): Promise<any> {
|
||||
const handler = this.methodRegistry[method]
|
||||
|
||||
if (!handler) {
|
||||
throw new Error(`Unknown ${this.serviceName} method: ${method}`)
|
||||
}
|
||||
|
||||
return handler(controller, message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a service registry factory function
|
||||
* @param serviceName The name of the service
|
||||
* @returns An object with register and handle functions
|
||||
*/
|
||||
export function createServiceRegistry(serviceName: string) {
|
||||
const registry = new ServiceRegistry(serviceName)
|
||||
|
||||
return {
|
||||
registerMethod: (methodName: string, handler: ServiceMethodHandler) => registry.registerMethod(methodName, handler),
|
||||
|
||||
handleRequest: (controller: Controller, method: string, message: any) =>
|
||||
registry.handleRequest(controller, method, message),
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { setTimeout as setTimeoutPromise } from "node:timers/promises"
|
||||
import pWaitFor from "p-wait-for"
|
||||
import * as path from "path"
|
||||
import * as vscode from "vscode"
|
||||
import { handleGrpcRequest } from "./grpc-handler"
|
||||
import { buildApiHandler } from "../../api"
|
||||
import { cleanupLegacyCheckpoints } from "../../integrations/checkpoints/CheckpointMigration"
|
||||
import { downloadTask } from "../../integrations/misc/export-markdown"
|
||||
@@ -56,7 +57,7 @@ export class Controller {
|
||||
private postMessage: (message: ExtensionMessage) => Thenable<boolean> | undefined
|
||||
|
||||
private disposables: vscode.Disposable[] = []
|
||||
private task?: Task
|
||||
task?: Task
|
||||
workspaceTracker: WorkspaceTracker
|
||||
mcpHub: McpHub
|
||||
accountService: ClineAccountService
|
||||
@@ -299,36 +300,6 @@ export class Controller {
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
break
|
||||
case "getBrowserConnectionInfo":
|
||||
try {
|
||||
// Get the current browser session from Cline if it exists
|
||||
if (this.task?.browserSession) {
|
||||
const connectionInfo = this.task.browserSession.getConnectionInfo()
|
||||
await this.postMessageToWebview({
|
||||
type: "browserConnectionInfo",
|
||||
isConnected: connectionInfo.isConnected,
|
||||
isRemote: connectionInfo.isRemote,
|
||||
host: connectionInfo.host,
|
||||
})
|
||||
} else {
|
||||
// If no active browser session, just return the settings
|
||||
const { browserSettings } = await getAllExtensionState(this.context)
|
||||
await this.postMessageToWebview({
|
||||
type: "browserConnectionInfo",
|
||||
isConnected: false,
|
||||
isRemote: !!browserSettings.remoteBrowserEnabled,
|
||||
host: browserSettings.remoteBrowserHost,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error getting browser connection info:", error)
|
||||
await this.postMessageToWebview({
|
||||
type: "browserConnectionInfo",
|
||||
isConnected: false,
|
||||
isRemote: false,
|
||||
})
|
||||
}
|
||||
break
|
||||
case "testBrowserConnection":
|
||||
try {
|
||||
const { browserSettings } = await getAllExtensionState(this.context)
|
||||
@@ -910,6 +881,12 @@ export class Controller {
|
||||
}
|
||||
break
|
||||
}
|
||||
case "grpc_request": {
|
||||
if (message.grpc_request) {
|
||||
await handleGrpcRequest(this, message.grpc_request)
|
||||
}
|
||||
break
|
||||
}
|
||||
// Add more switch case statements here as more webview message commands
|
||||
// are created within the webview context (i.e. inside media/main.js)
|
||||
}
|
||||
|
||||
@@ -43,12 +43,12 @@ export interface ExtensionMessage {
|
||||
| "totalTasksSize"
|
||||
| "addToInput"
|
||||
| "browserConnectionResult"
|
||||
| "browserConnectionInfo"
|
||||
| "detectedChromePath"
|
||||
| "scrollToSettings"
|
||||
| "browserRelaunchResult"
|
||||
| "relativePathsResponse" // Handles single and multiple path responses
|
||||
| "fileSearchResults"
|
||||
| "grpc_response" // New type for gRPC responses
|
||||
text?: string
|
||||
paths?: (string | null)[] // Used for relativePathsResponse
|
||||
action?:
|
||||
@@ -109,6 +109,11 @@ export interface ExtensionMessage {
|
||||
error?: string
|
||||
}
|
||||
tab?: McpViewTab
|
||||
grpc_response?: {
|
||||
message?: any // JSON serialized protobuf message
|
||||
request_id: string // Same ID as the request
|
||||
error?: string // Optional error message
|
||||
}
|
||||
}
|
||||
|
||||
export type Invoke = "sendMessage" | "primaryButtonClick" | "secondaryButtonClick"
|
||||
@@ -236,12 +241,6 @@ export type BrowserActionResult = {
|
||||
currentMousePosition?: string
|
||||
}
|
||||
|
||||
export interface BrowserConnectionInfo {
|
||||
isConnected: boolean
|
||||
isRemote: boolean
|
||||
host?: string
|
||||
}
|
||||
|
||||
export interface ClineAskUseMcpServer {
|
||||
serverName: string
|
||||
type: "use_mcp_tool" | "access_mcp_resource"
|
||||
|
||||
@@ -74,13 +74,13 @@ export interface WebviewMessage {
|
||||
| "requestTotalTasksSize"
|
||||
| "relaunchChromeDebugMode"
|
||||
| "taskFeedback"
|
||||
| "getBrowserConnectionInfo"
|
||||
| "getDetectedChromePath"
|
||||
| "detectedChromePath"
|
||||
| "scrollToSettings"
|
||||
| "getRelativePaths" // Handles single and multiple URI resolution
|
||||
| "searchFiles"
|
||||
| "toggleFavoriteModel"
|
||||
| "grpc_request"
|
||||
// | "relaunchChromeDebugMode"
|
||||
text?: string
|
||||
uris?: string[] // Used for getRelativePaths
|
||||
@@ -117,6 +117,12 @@ export interface WebviewMessage {
|
||||
query?: string
|
||||
// For toggleFavoriteModel
|
||||
modelId?: string
|
||||
grpc_request?: {
|
||||
service: string
|
||||
method: string
|
||||
message: any // JSON serialized protobuf message
|
||||
request_id: string // For correlating requests and responses
|
||||
}
|
||||
}
|
||||
|
||||
export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse"
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.7.0
|
||||
// protoc v6.30.1
|
||||
// source: browser.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"
|
||||
import { EmptyRequest } from "./common"
|
||||
|
||||
export const protobufPackage = "cline"
|
||||
|
||||
export interface BrowserConnectionInfo {
|
||||
isConnected: boolean
|
||||
isRemote: boolean
|
||||
/** Optional, may be empty */
|
||||
host: string
|
||||
}
|
||||
|
||||
function createBaseBrowserConnectionInfo(): BrowserConnectionInfo {
|
||||
return { isConnected: false, isRemote: false, host: "" }
|
||||
}
|
||||
|
||||
export const BrowserConnectionInfo: MessageFns<BrowserConnectionInfo> = {
|
||||
encode(message: BrowserConnectionInfo, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.isConnected !== false) {
|
||||
writer.uint32(8).bool(message.isConnected)
|
||||
}
|
||||
if (message.isRemote !== false) {
|
||||
writer.uint32(16).bool(message.isRemote)
|
||||
}
|
||||
if (message.host !== "") {
|
||||
writer.uint32(26).string(message.host)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): BrowserConnectionInfo {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
|
||||
let end = length === undefined ? reader.len : reader.pos + length
|
||||
const message = createBaseBrowserConnectionInfo()
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32()
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 8) {
|
||||
break
|
||||
}
|
||||
|
||||
message.isConnected = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 16) {
|
||||
break
|
||||
}
|
||||
|
||||
message.isRemote = reader.bool()
|
||||
continue
|
||||
}
|
||||
case 3: {
|
||||
if (tag !== 26) {
|
||||
break
|
||||
}
|
||||
|
||||
message.host = reader.string()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
}
|
||||
reader.skip(tag & 7)
|
||||
}
|
||||
return message
|
||||
},
|
||||
|
||||
fromJSON(object: any): BrowserConnectionInfo {
|
||||
return {
|
||||
isConnected: isSet(object.isConnected) ? globalThis.Boolean(object.isConnected) : false,
|
||||
isRemote: isSet(object.isRemote) ? globalThis.Boolean(object.isRemote) : false,
|
||||
host: isSet(object.host) ? globalThis.String(object.host) : "",
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: BrowserConnectionInfo): unknown {
|
||||
const obj: any = {}
|
||||
if (message.isConnected !== false) {
|
||||
obj.isConnected = message.isConnected
|
||||
}
|
||||
if (message.isRemote !== false) {
|
||||
obj.isRemote = message.isRemote
|
||||
}
|
||||
if (message.host !== "") {
|
||||
obj.host = message.host
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<BrowserConnectionInfo>, I>>(base?: I): BrowserConnectionInfo {
|
||||
return BrowserConnectionInfo.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<BrowserConnectionInfo>, I>>(object: I): BrowserConnectionInfo {
|
||||
const message = createBaseBrowserConnectionInfo()
|
||||
message.isConnected = object.isConnected ?? false
|
||||
message.isRemote = object.isRemote ?? false
|
||||
message.host = object.host ?? ""
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
export type BrowserServiceDefinition = typeof BrowserServiceDefinition
|
||||
export const BrowserServiceDefinition = {
|
||||
name: "BrowserService",
|
||||
fullName: "cline.BrowserService",
|
||||
methods: {
|
||||
getBrowserConnectionInfo: {
|
||||
name: "getBrowserConnectionInfo",
|
||||
requestType: EmptyRequest,
|
||||
requestStream: false,
|
||||
responseType: BrowserConnectionInfo,
|
||||
responseStream: false,
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined
|
||||
|
||||
export type DeepPartial<T> = T extends Builtin
|
||||
? T
|
||||
: T extends globalThis.Array<infer U>
|
||||
? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {}
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never
|
||||
export type Exact<P, I extends P> = P extends Builtin
|
||||
? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never }
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined
|
||||
}
|
||||
|
||||
export interface MessageFns<T> {
|
||||
encode(message: T, writer?: BinaryWriter): BinaryWriter
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): T
|
||||
fromJSON(object: any): T
|
||||
toJSON(message: T): unknown
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.7.0
|
||||
// protoc v6.30.1
|
||||
// source: common.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"
|
||||
|
||||
export const protobufPackage = "cline"
|
||||
|
||||
export interface Metadata {}
|
||||
|
||||
export interface EmptyRequest {
|
||||
metadata?: Metadata | undefined
|
||||
}
|
||||
|
||||
export interface Empty {}
|
||||
|
||||
function createBaseMetadata(): Metadata {
|
||||
return {}
|
||||
}
|
||||
|
||||
export const Metadata: MessageFns<Metadata> = {
|
||||
encode(_: Metadata, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
return writer
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Metadata {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
|
||||
let end = length === undefined ? reader.len : reader.pos + length
|
||||
const message = createBaseMetadata()
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32()
|
||||
switch (tag >>> 3) {
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
}
|
||||
reader.skip(tag & 7)
|
||||
}
|
||||
return message
|
||||
},
|
||||
|
||||
fromJSON(_: any): Metadata {
|
||||
return {}
|
||||
},
|
||||
|
||||
toJSON(_: Metadata): unknown {
|
||||
const obj: any = {}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<Metadata>, I>>(base?: I): Metadata {
|
||||
return Metadata.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<Metadata>, I>>(_: I): Metadata {
|
||||
const message = createBaseMetadata()
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseEmptyRequest(): EmptyRequest {
|
||||
return { metadata: undefined }
|
||||
}
|
||||
|
||||
export const EmptyRequest: MessageFns<EmptyRequest> = {
|
||||
encode(message: EmptyRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.metadata !== undefined) {
|
||||
Metadata.encode(message.metadata, writer.uint32(10).fork()).join()
|
||||
}
|
||||
return writer
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): EmptyRequest {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
|
||||
let end = length === undefined ? reader.len : reader.pos + length
|
||||
const message = createBaseEmptyRequest()
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32()
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 10) {
|
||||
break
|
||||
}
|
||||
|
||||
message.metadata = Metadata.decode(reader, reader.uint32())
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
}
|
||||
reader.skip(tag & 7)
|
||||
}
|
||||
return message
|
||||
},
|
||||
|
||||
fromJSON(object: any): EmptyRequest {
|
||||
return { metadata: isSet(object.metadata) ? Metadata.fromJSON(object.metadata) : undefined }
|
||||
},
|
||||
|
||||
toJSON(message: EmptyRequest): unknown {
|
||||
const obj: any = {}
|
||||
if (message.metadata !== undefined) {
|
||||
obj.metadata = Metadata.toJSON(message.metadata)
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<EmptyRequest>, I>>(base?: I): EmptyRequest {
|
||||
return EmptyRequest.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<EmptyRequest>, I>>(object: I): EmptyRequest {
|
||||
const message = createBaseEmptyRequest()
|
||||
message.metadata =
|
||||
object.metadata !== undefined && object.metadata !== null ? Metadata.fromPartial(object.metadata) : undefined
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseEmpty(): Empty {
|
||||
return {}
|
||||
}
|
||||
|
||||
export const Empty: MessageFns<Empty> = {
|
||||
encode(_: Empty, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
return writer
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Empty {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
|
||||
let end = length === undefined ? reader.len : reader.pos + length
|
||||
const message = createBaseEmpty()
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32()
|
||||
switch (tag >>> 3) {
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break
|
||||
}
|
||||
reader.skip(tag & 7)
|
||||
}
|
||||
return message
|
||||
},
|
||||
|
||||
fromJSON(_: any): Empty {
|
||||
return {}
|
||||
},
|
||||
|
||||
toJSON(_: Empty): unknown {
|
||||
const obj: any = {}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<Empty>, I>>(base?: I): Empty {
|
||||
return Empty.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<Empty>, I>>(_: I): Empty {
|
||||
const message = createBaseEmpty()
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined
|
||||
|
||||
export type DeepPartial<T> = T extends Builtin
|
||||
? T
|
||||
: T extends globalThis.Array<infer U>
|
||||
? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {}
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never
|
||||
export type Exact<P, I extends P> = P extends Builtin
|
||||
? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never }
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined
|
||||
}
|
||||
|
||||
export interface MessageFns<T> {
|
||||
encode(message: T, writer?: BinaryWriter): BinaryWriter
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): T
|
||||
fromJSON(object: any): T
|
||||
toJSON(message: T): unknown
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T
|
||||
}
|
||||
Generated
+23
-13
@@ -27,7 +27,8 @@
|
||||
"react-use": "^17.6.0",
|
||||
"react-virtuoso": "^4.12.3",
|
||||
"rehype-highlight": "^7.0.1",
|
||||
"styled-components": "^6.1.15"
|
||||
"styled-components": "^6.1.15",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.17.0",
|
||||
@@ -40,6 +41,7 @@
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/vscode-webview": "^1.57.5",
|
||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||
"@vitest/coverage-v8": "^3.0.9",
|
||||
@@ -3539,6 +3541,13 @@
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
|
||||
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
|
||||
"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",
|
||||
@@ -6936,18 +6945,6 @@
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/mermaid/node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark": {
|
||||
"version": "2.11.4",
|
||||
"resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz",
|
||||
@@ -8836,6 +8833,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vfile": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
"react-use": "^17.6.0",
|
||||
"react-virtuoso": "^4.12.3",
|
||||
"rehype-highlight": "^7.0.1",
|
||||
"styled-components": "^6.1.15"
|
||||
"styled-components": "^6.1.15",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.17.0",
|
||||
@@ -46,6 +47,7 @@
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/vscode-webview": "^1.57.5",
|
||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||
"@vitest/coverage-v8": "^3.0.9",
|
||||
|
||||
@@ -3,6 +3,8 @@ import { useEffect, useRef, useState } from "react"
|
||||
import styled from "styled-components"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { vscode } from "@/utils/vscode"
|
||||
import { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
|
||||
import { BrowserServiceClient } from "../../services/grpc-client"
|
||||
|
||||
interface ConnectionInfo {
|
||||
isConnected: boolean
|
||||
@@ -21,29 +23,25 @@ export const BrowserSettingsMenu = () => {
|
||||
})
|
||||
const popoverRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Get actual connection info from the browser session
|
||||
// Get actual connection info from the browser session using gRPC
|
||||
useEffect(() => {
|
||||
// Request connection info when component mounts
|
||||
vscode.postMessage({
|
||||
type: "getBrowserConnectionInfo",
|
||||
})
|
||||
|
||||
// Listen for connection info updates
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const message = event.data
|
||||
if (message.type === "browserConnectionInfo") {
|
||||
// Function to fetch connection info
|
||||
;(async () => {
|
||||
try {
|
||||
console.log("[DEBUG] SENDING BROWSER CONNECTION INFO REQUEST")
|
||||
const info = await BrowserServiceClient.getBrowserConnectionInfo({})
|
||||
console.log("[DEBUG] GOT BROWSER REPLY:", info, typeof info)
|
||||
setConnectionInfo({
|
||||
isConnected: message.isConnected,
|
||||
isRemote: message.isRemote,
|
||||
host: message.host,
|
||||
isConnected: info.isConnected,
|
||||
isRemote: info.isRemote,
|
||||
host: info.host,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error fetching browser connection info:", error)
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
window.addEventListener("message", handleMessage)
|
||||
return () => {
|
||||
window.removeEventListener("message", handleMessage)
|
||||
}
|
||||
// No need for message event listeners anymore!
|
||||
}, [browserSettings.remoteBrowserHost, browserSettings.remoteBrowserEnabled])
|
||||
|
||||
// Close popover when clicking outside
|
||||
@@ -84,11 +82,22 @@ export const BrowserSettingsMenu = () => {
|
||||
const toggleInfoPopover = () => {
|
||||
setShowInfoPopover(!showInfoPopover)
|
||||
|
||||
// Request updated connection info when opening the popover
|
||||
// Request updated connection info when opening the popover using gRPC
|
||||
if (!showInfoPopover) {
|
||||
vscode.postMessage({
|
||||
type: "getBrowserConnectionInfo",
|
||||
})
|
||||
const fetchConnectionInfo = async () => {
|
||||
try {
|
||||
const info = await BrowserServiceClient.getBrowserConnectionInfo({})
|
||||
setConnectionInfo({
|
||||
isConnected: info.isConnected,
|
||||
isRemote: info.isRemote,
|
||||
host: info.host,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error fetching browser connection info:", error)
|
||||
}
|
||||
}
|
||||
|
||||
fetchConnectionInfo()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,19 +121,27 @@ export const BrowserSettingsMenu = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Check connection status every second to keep icon in sync
|
||||
// Check connection status every second to keep icon in sync using gRPC
|
||||
useEffect(() => {
|
||||
// Function to fetch connection info
|
||||
const fetchConnectionInfo = async () => {
|
||||
try {
|
||||
const info = await BrowserServiceClient.getBrowserConnectionInfo({})
|
||||
setConnectionInfo({
|
||||
isConnected: info.isConnected,
|
||||
isRemote: info.isRemote,
|
||||
host: info.host,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error fetching browser connection info:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// Request connection info immediately
|
||||
vscode.postMessage({
|
||||
type: "getBrowserConnectionInfo",
|
||||
})
|
||||
fetchConnectionInfo()
|
||||
|
||||
// Set up interval to refresh every second
|
||||
const intervalId = setInterval(() => {
|
||||
vscode.postMessage({
|
||||
type: "getBrowserConnectionInfo",
|
||||
})
|
||||
}, 1000)
|
||||
const intervalId = setInterval(fetchConnectionInfo, 1000)
|
||||
|
||||
return () => clearInterval(intervalId)
|
||||
}, [])
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { vscode } from "../utils/vscode"
|
||||
import { v4 as uuidv4 } from "uuid"
|
||||
import { BrowserServiceDefinition } from "@shared/proto/browser"
|
||||
import { EmptyRequest } from "@shared/proto/common"
|
||||
|
||||
// Generic type for any protobuf service definition
|
||||
type ProtoService = {
|
||||
name: string
|
||||
fullName: string
|
||||
methods: {
|
||||
[key: string]: {
|
||||
name: string
|
||||
requestType: any
|
||||
responseType: any
|
||||
requestStream: boolean
|
||||
responseStream: boolean
|
||||
options: any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define a generic type that extracts method signatures from a service definition
|
||||
type GrpcClientType<T extends ProtoService> = {
|
||||
[K in keyof T["methods"]]: (
|
||||
request: InstanceType<T["methods"][K]["requestType"]>,
|
||||
) => Promise<InstanceType<T["methods"][K]["responseType"]>>
|
||||
}
|
||||
|
||||
// Create a client for any protobuf service with inferred types
|
||||
function createGrpcClient<T extends ProtoService>(service: T): GrpcClientType<T> {
|
||||
const client = {} as GrpcClientType<T>
|
||||
|
||||
// For each method in the service
|
||||
Object.values(service.methods).forEach((method) => {
|
||||
// Create a function that matches the method signature
|
||||
client[method.name as keyof GrpcClientType<T>] = ((request: any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestId = uuidv4()
|
||||
|
||||
// Set up one-time listener for this specific request
|
||||
const handleResponse = (event: MessageEvent) => {
|
||||
const message = event.data
|
||||
if (message.type === "grpc_response" && message.grpc_response?.request_id === requestId) {
|
||||
// Remove listener once we get our response
|
||||
window.removeEventListener("message", handleResponse)
|
||||
|
||||
if (message.grpc_response.error) {
|
||||
reject(new Error(message.grpc_response.error))
|
||||
} else {
|
||||
// Convert JSON back to protobuf message
|
||||
const responseType = method.responseType
|
||||
const response = responseType.fromJSON(message.grpc_response.message)
|
||||
console.log("[DEBUG] grpc-client sending response:", response)
|
||||
resolve(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", handleResponse)
|
||||
|
||||
let encodedRequest = "{}"
|
||||
if (Object.keys(request).length !== 0) {
|
||||
encodedRequest = request.toJSON()
|
||||
console.log("[DEBUG] request is", request, encodedRequest)
|
||||
}
|
||||
|
||||
// Send the request
|
||||
vscode.postMessage({
|
||||
type: "grpc_request",
|
||||
grpc_request: {
|
||||
service: service.fullName,
|
||||
method: method.name,
|
||||
message: encodedRequest, // Convert protobuf to JSON
|
||||
request_id: requestId,
|
||||
},
|
||||
})
|
||||
})
|
||||
}) as any
|
||||
})
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// Create the Browser Service Client singleton with inferred types
|
||||
// No need for manual interface definition - types are inferred from the service definition
|
||||
const BrowserServiceClient = createGrpcClient(BrowserServiceDefinition)
|
||||
|
||||
// Export the Browser Service Client as a static object
|
||||
export { BrowserServiceClient }
|
||||
Reference in New Issue
Block a user