summaryrefslogtreecommitdiff
path: root/extension
diff options
context:
space:
mode:
Diffstat (limited to 'extension')
-rw-r--r--extension/.vscodeignore16
-rw-r--r--extension/DEV_README.md7
-rw-r--r--extension/LICENSE.txt18
-rw-r--r--extension/README.md21
-rw-r--r--extension/examples/README.md3
-rw-r--r--extension/logging.yaml30
-rw-r--r--extension/media/continue-gradient.pngbin0 -> 13218 bytes
-rw-r--r--extension/package-lock.json598
-rw-r--r--extension/package.json45
-rw-r--r--extension/react-app/README.md3
-rw-r--r--extension/react-app/package-lock.json39
-rw-r--r--extension/react-app/package.json1
-rw-r--r--extension/react-app/src/App.tsx6
-rw-r--r--extension/react-app/src/components/CodeBlock.tsx3
-rw-r--r--extension/react-app/src/components/DebugPanel.tsx4
-rw-r--r--extension/react-app/src/components/StepContainer.tsx31
-rw-r--r--extension/react-app/src/components/index.ts2
-rw-r--r--extension/react-app/src/hooks/ContinueGUIClientProtocol.ts13
-rw-r--r--extension/react-app/src/hooks/messenger.ts108
-rw-r--r--extension/react-app/src/hooks/useContinueGUIProtocol.ts49
-rw-r--r--extension/react-app/src/hooks/useWebsocket.ts82
-rw-r--r--extension/react-app/src/hooks/vscodeMessenger.ts71
-rw-r--r--extension/react-app/src/index.css2
-rw-r--r--extension/react-app/src/main.tsx25
-rw-r--r--extension/react-app/src/tabs/chat/MessageDiv.tsx3
-rw-r--r--extension/react-app/src/tabs/gui.tsx (renamed from extension/react-app/src/tabs/notebook.tsx)90
-rw-r--r--extension/react-app/src/vscode/index.ts1
-rw-r--r--extension/react-app/tsconfig.json2
-rw-r--r--extension/schema/History.d.ts41
-rw-r--r--extension/schema/HistoryNode.d.ts31
-rw-r--r--extension/schema/README.md3
-rw-r--r--extension/scripts/continuedev-0.1.0-py3-none-any.whlbin61142 -> 55662 bytes
-rw-r--r--extension/scripts/install_from_source.py63
-rw-r--r--extension/src/activation/activate.ts12
-rw-r--r--extension/src/activation/environmentSetup.ts154
-rw-r--r--extension/src/commands.ts4
-rw-r--r--extension/src/continueIdeClient.ts150
-rw-r--r--extension/src/debugPanel.ts111
-rw-r--r--extension/src/extension.ts7
-rw-r--r--extension/src/test/runTest.ts30
-rw-r--r--extension/src/util/messenger.ts108
41 files changed, 1597 insertions, 390 deletions
diff --git a/extension/.vscodeignore b/extension/.vscodeignore
index 8fddc39a..c1a09172 100644
--- a/extension/.vscodeignore
+++ b/extension/.vscodeignore
@@ -1,5 +1,6 @@
.vscode/**
.vscode-test/**
+build/**
src/**
.gitignore
.yarnrc
@@ -8,4 +9,17 @@ vsc-extension-quickstart.md
**/.eslintrc.json
**/*.map
**/*.ts
-scripts/env/** \ No newline at end of file
+scripts/env/**
+scripts/.env
+**/.env
+**/env
+**/node_modules
+
+react-app/node_modules
+react-app/public
+react-app/src
+
+**/.pytest_cache
+**/__pycache__
+
+scripts/data \ No newline at end of file
diff --git a/extension/DEV_README.md b/extension/DEV_README.md
new file mode 100644
index 00000000..7049da45
--- /dev/null
+++ b/extension/DEV_README.md
@@ -0,0 +1,7 @@
+# Continue VS Code Extension
+
+This is the Continue VS Code Extension. Its primary jobs are
+
+1. Implement the IDE side of the Continue IDE protocol, allowing a Continue server to interact natively in an IDE. This happens in `src/continueIdeClient.ts`.
+2. Open the Continue React app in a side panel. The React app's source code lives in the `react-app` directory. The panel is opened by the `continue.openDebugPanel` command, as defined in `src/commands.ts`.
+3. Run a Continue server in the background, which connects to both the IDE protocol and the React app. The server is launched in `src/activation/environmentSetup.ts` by calling Python code that lives in `scripts/` (unless extension settings define a server URL other than localhost:8000, in which case the extension will just connect to that).
diff --git a/extension/LICENSE.txt b/extension/LICENSE.txt
index eddcb4e6..a12f2a75 100644
--- a/extension/LICENSE.txt
+++ b/extension/LICENSE.txt
@@ -1,5 +1,13 @@
-/*******************************************************
- * Copyright (C) 2023 Continue <nate@continue.dev>
- *
- * All rights reserved.
- *******************************************************/ \ No newline at end of file
+Copyright 2023 Continue
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. \ No newline at end of file
diff --git a/extension/README.md b/extension/README.md
index df158440..51e95822 100644
--- a/extension/README.md
+++ b/extension/README.md
@@ -1,27 +1,18 @@
-# continue
+# Continue
-Bug report to fix in 1/10th the time.
+Automate software development at the level of tasks, instead of tab-autocompletions. With Continue, you can make large edits with natural language, ask questions of your codebase, and run custom recipes built with our open-source Python library.
-Our debugging assistant automatically handles tedious portions of the debugging process, such as:
-
-- Fault localization
-- Enumerating potential error sources
-- Generating fixes
-- Pulling in outside context
-- Following data flows
-- Parsing tracebacks
-- Generate unit tests
-- Generate docstrings
+Get started by opening the command pallet with `cmd+shift+p` and then selecting `Continue: Open Debug Panel`.
# Features
### Ask a Question
-`cmd+shift+j` to open up a universal search bar, where you can ask questions of your codebase in natural language.
+Ask natural language questions of your codebase, like "Where is the entry point to the VS Code extension?"
-### Fault Localization
+### Edit with Natural Language
-Either manually highlight code snippets you think are suspicious, or let us find them for you.
+Request an edit to the currently open file, for example: "Add CORS headers to this FastAPI server".
### Generate a Fix
diff --git a/extension/examples/README.md b/extension/examples/README.md
new file mode 100644
index 00000000..c95b9220
--- /dev/null
+++ b/extension/examples/README.md
@@ -0,0 +1,3 @@
+# Examples
+
+This folder contains example code used in Continue demos.
diff --git a/extension/logging.yaml b/extension/logging.yaml
new file mode 100644
index 00000000..391041ef
--- /dev/null
+++ b/extension/logging.yaml
@@ -0,0 +1,30 @@
+version: 1
+disable_existing_loggers: False
+formatters:
+ default:
+ (): 'uvicorn.logging.DefaultFormatter'
+ fmt: '%(asctime)s %(levelprefix)-9s %(name)s -: %(message)s'
+ access:
+ (): 'uvicorn.logging.AccessFormatter'
+ fmt: '%(asctime)s %(levelprefix)-9s %(name)s -: %(client_addr)s - "%(request_line)s" %(status_code)s'
+handlers:
+ default:
+ class: logging.StreamHandler
+ formatter: default
+ stream: ext://sys.stderr
+ access:
+ class: logging.StreamHandler
+ formatter: access
+ stream: ext://sys.stdout
+loggers:
+ uvicorn:
+ level: INFO
+ handlers:
+ - default
+ uvicorn.error:
+ level: INFO
+ uvicorn.access:
+ level: INFO
+ propagate: False
+ handlers:
+ - access \ No newline at end of file
diff --git a/extension/media/continue-gradient.png b/extension/media/continue-gradient.png
new file mode 100644
index 00000000..2b382040
--- /dev/null
+++ b/extension/media/continue-gradient.png
Binary files differ
diff --git a/extension/package-lock.json b/extension/package-lock.json
index c8907210..647e3aa2 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,12 +1,13 @@
{
"name": "continue",
- "version": "0.0.1",
+ "version": "0.0.13",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "continue",
- "version": "0.0.1",
+ "version": "0.0.13",
+ "license": "Apache-2.0",
"dependencies": {
"@electron/rebuild": "^3.2.10",
"@reduxjs/toolkit": "^1.9.3",
@@ -27,9 +28,11 @@
"@types/node": "16.x",
"@types/node-fetch": "^2.6.2",
"@types/vscode": "^1.74.0",
+ "@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"@vscode/test-electron": "^2.2.0",
+ "esbuild": "^0.17.19",
"eslint": "^8.28.0",
"glob": "^8.0.3",
"json-schema-to-typescript": "^12.0.0",
@@ -451,6 +454,358 @@
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
"peer": true
},
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
+ "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
+ "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
+ "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
+ "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
+ "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
+ "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
+ "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
+ "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
+ "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
+ "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
+ "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
+ "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
+ "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
+ "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
+ "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
+ "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
+ "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
+ "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
+ "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
+ "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@eslint/eslintrc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
@@ -1673,6 +2028,15 @@
"integrity": "sha512-LyeCIU3jb9d38w0MXFwta9r0Jx23ugujkAxdwLTNCyspdZTKUc43t7ppPbCiPoQ/Ivd/pnDFZrb4hWd45wrsgA==",
"dev": true
},
+ "node_modules/@types/ws": {
+ "version": "8.5.4",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
+ "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz",
@@ -3217,6 +3581,43 @@
"es6-symbol": "^3.1.1"
}
},
+ "node_modules/esbuild": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
+ "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.17.19",
+ "@esbuild/android-arm64": "0.17.19",
+ "@esbuild/android-x64": "0.17.19",
+ "@esbuild/darwin-arm64": "0.17.19",
+ "@esbuild/darwin-x64": "0.17.19",
+ "@esbuild/freebsd-arm64": "0.17.19",
+ "@esbuild/freebsd-x64": "0.17.19",
+ "@esbuild/linux-arm": "0.17.19",
+ "@esbuild/linux-arm64": "0.17.19",
+ "@esbuild/linux-ia32": "0.17.19",
+ "@esbuild/linux-loong64": "0.17.19",
+ "@esbuild/linux-mips64el": "0.17.19",
+ "@esbuild/linux-ppc64": "0.17.19",
+ "@esbuild/linux-riscv64": "0.17.19",
+ "@esbuild/linux-s390x": "0.17.19",
+ "@esbuild/linux-x64": "0.17.19",
+ "@esbuild/netbsd-x64": "0.17.19",
+ "@esbuild/openbsd-x64": "0.17.19",
+ "@esbuild/sunos-x64": "0.17.19",
+ "@esbuild/win32-arm64": "0.17.19",
+ "@esbuild/win32-ia32": "0.17.19",
+ "@esbuild/win32-x64": "0.17.19"
+ }
+ },
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -7734,6 +8135,160 @@
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
"peer": true
},
+ "@esbuild/android-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
+ "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
+ "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
+ "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
+ "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
+ "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
+ "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
+ "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
+ "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
+ "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
+ "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
+ "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-mips64el": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
+ "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ppc64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
+ "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-riscv64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
+ "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-s390x": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
+ "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
+ "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/netbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/openbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/sunos-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
+ "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
+ "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
+ "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
+ "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
+ "dev": true,
+ "optional": true
+ },
"@eslint/eslintrc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
@@ -8701,6 +9256,15 @@
"integrity": "sha512-LyeCIU3jb9d38w0MXFwta9r0Jx23ugujkAxdwLTNCyspdZTKUc43t7ppPbCiPoQ/Ivd/pnDFZrb4hWd45wrsgA==",
"dev": true
},
+ "@types/ws": {
+ "version": "8.5.4",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
+ "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@typescript-eslint/eslint-plugin": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz",
@@ -9817,6 +10381,36 @@
"es6-symbol": "^3.1.1"
}
},
+ "esbuild": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
+ "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
+ "dev": true,
+ "requires": {
+ "@esbuild/android-arm": "0.17.19",
+ "@esbuild/android-arm64": "0.17.19",
+ "@esbuild/android-x64": "0.17.19",
+ "@esbuild/darwin-arm64": "0.17.19",
+ "@esbuild/darwin-x64": "0.17.19",
+ "@esbuild/freebsd-arm64": "0.17.19",
+ "@esbuild/freebsd-x64": "0.17.19",
+ "@esbuild/linux-arm": "0.17.19",
+ "@esbuild/linux-arm64": "0.17.19",
+ "@esbuild/linux-ia32": "0.17.19",
+ "@esbuild/linux-loong64": "0.17.19",
+ "@esbuild/linux-mips64el": "0.17.19",
+ "@esbuild/linux-ppc64": "0.17.19",
+ "@esbuild/linux-riscv64": "0.17.19",
+ "@esbuild/linux-s390x": "0.17.19",
+ "@esbuild/linux-x64": "0.17.19",
+ "@esbuild/netbsd-x64": "0.17.19",
+ "@esbuild/openbsd-x64": "0.17.19",
+ "@esbuild/sunos-x64": "0.17.19",
+ "@esbuild/win32-arm64": "0.17.19",
+ "@esbuild/win32-ia32": "0.17.19",
+ "@esbuild/win32-x64": "0.17.19"
+ }
+ },
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
diff --git a/extension/package.json b/extension/package.json
index d957e071..1219ca8e 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -1,12 +1,20 @@
{
"name": "continue",
+ "icon": "media/continue-gradient.png",
"repository": {
"type": "git",
- "url": ""
+ "url": "https://github.com/continuedev/continue"
},
+ "bugs": {
+ "url": "https://github.com/continuedev/continue/issues",
+ "email": "nate@continue.dev"
+ },
+ "homepage": "https://continue.dev",
+ "license": "Apache-2.0",
"displayName": "Continue",
- "description": "Reduce debugging time by 10x",
- "version": "0.0.1",
+ "pricing": "Free",
+ "description": "Refine code 10x faster",
+ "version": "0.0.13",
"publisher": "Continue",
"engines": {
"vscode": "^1.74.0"
@@ -15,7 +23,7 @@
"Other"
],
"activationEvents": [
- "*"
+ "onStartupFinished"
],
"main": "./out/extension.js",
"contributes": {
@@ -108,21 +116,6 @@
],
"keybindings": [
{
- "command": "continue.writeDocstring",
- "key": "ctrl+alt+l",
- "mac": "shift+cmd+l"
- },
- {
- "command": "continue.writeUnitTest",
- "key": "ctrl+alt+i",
- "mac": "shift+cmd+i"
- },
- {
- "command": "continue.askQuestionFromInput",
- "key": "ctrl+alt+j",
- "mac": "shift+cmd+j"
- },
- {
"command": "continue.suggestionDown",
"mac": "shift+ctrl+down",
"key": "shift+ctrl+down"
@@ -154,19 +147,23 @@
}
},
"scripts": {
+ "vscode:prepublish": "npm run esbuild-base -- --minify",
+ "esbuild-base": "rm -rf ./out && esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node",
+ "esbuild": "rm -rf ./out && npm run esbuild-base -- --sourcemap",
+ "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
+ "test-compile": "tsc -p ./",
"clientgen": "rm -rf src/client/ && npx @openapitools/openapi-generator-cli generate -i ../schema/openapi.json -g typescript-fetch -o src/client/ --additional-properties=supportsES6=true,npmVersion=8.19.2,typescriptThreePlus=true",
"typegen": "node scripts/typegen.js",
- "vscode:prepublish": "npm run compile",
"rebuild": "electron-rebuild -v 19.1.8 node-pty",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"test": "node ./out/test/runTest.js",
- "package": "cp ./config/prod_config.json ./config/config.json && npm run compile && mkdir -p ./build && vsce package --out ./build && chmod 777 ./build/continue-0.0.1.vsix && cp ./config/dev_config.json ./config/config.json",
+ "package": "cp ./config/prod_config.json ./config/config.json && mkdir -p ./build && vsce package --out ./build && cp ./config/dev_config.json ./config/config.json",
"full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.0-py3-none-any.whl ../extension/scripts/continuedev-0.1.0-py3-none-any.whl && cd ../extension && npm run typegen && npm run clientgen && cd react-app && npm run build && cd .. && npm run package",
- "install-extension": "code --install-extension ./build/continue-0.0.1.vsix",
- "uninstall": "code --uninstall-extension Continue.continue",
+ "install-extension": "code --install-extension ./build/continue-0.0.8.vsix",
+ "uninstall": "code --uninstall-extension .continue",
"reinstall": "rm -rf ./build && npm run package && npm run uninstall && npm run install-extension"
},
"devDependencies": {
@@ -176,9 +173,11 @@
"@types/node": "16.x",
"@types/node-fetch": "^2.6.2",
"@types/vscode": "^1.74.0",
+ "@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"@vscode/test-electron": "^2.2.0",
+ "esbuild": "^0.17.19",
"eslint": "^8.28.0",
"glob": "^8.0.3",
"json-schema-to-typescript": "^12.0.0",
diff --git a/extension/react-app/README.md b/extension/react-app/README.md
new file mode 100644
index 00000000..006b6b11
--- /dev/null
+++ b/extension/react-app/README.md
@@ -0,0 +1,3 @@
+# Continue React App
+
+The Continue React app is a notebook-like interface to the Continue server. It allows the user to submit arbitrary text input, then communicates with the server to takes steps, which are displayed as a sequence of editable cells. The React app should sit beside an IDE, as in the VS Code extension.
diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json
index 1ba8cfe8..dbcbc5cc 100644
--- a/extension/react-app/package-lock.json
+++ b/extension/react-app/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"@styled-icons/heroicons-outline": "^10.47.0",
"@types/vscode-webview": "^1.57.1",
+ "posthog-js": "^1.58.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^8.0.5",
@@ -1506,6 +1507,11 @@
"reusify": "^1.0.4"
}
},
+ "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=="
+ },
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -2528,6 +2534,15 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
+ "node_modules/posthog-js": {
+ "version": "1.58.0",
+ "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.58.0.tgz",
+ "integrity": "sha512-PpH/MwjwV6UHDsv78xFvteEWYgY3O/HTKBnotzmkNCDWgsKzNr978B1AKzgtBU2GzBsnwUfuK+u2O6mxRzFSWw==",
+ "dependencies": {
+ "fflate": "^0.4.1",
+ "rrweb-snapshot": "^1.1.14"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -2778,6 +2793,11 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/rrweb-snapshot": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-1.1.14.tgz",
+ "integrity": "sha512-eP5pirNjP5+GewQfcOQY4uBiDnpqxNRc65yKPW0eSoU1XamDfc4M8oqpXGMyUyvLyxFDB0q0+DChuxxiU2FXBQ=="
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -4196,6 +4216,11 @@
"reusify": "^1.0.4"
}
},
+ "fflate": {
+ "version": "0.4.8",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
+ "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="
+ },
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -4803,6 +4828,15 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
+ "posthog-js": {
+ "version": "1.58.0",
+ "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.58.0.tgz",
+ "integrity": "sha512-PpH/MwjwV6UHDsv78xFvteEWYgY3O/HTKBnotzmkNCDWgsKzNr978B1AKzgtBU2GzBsnwUfuK+u2O6mxRzFSWw==",
+ "requires": {
+ "fflate": "^0.4.1",
+ "rrweb-snapshot": "^1.1.14"
+ }
+ },
"prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -4964,6 +4998,11 @@
"fsevents": "~2.3.2"
}
},
+ "rrweb-snapshot": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-1.1.14.tgz",
+ "integrity": "sha512-eP5pirNjP5+GewQfcOQY4uBiDnpqxNRc65yKPW0eSoU1XamDfc4M8oqpXGMyUyvLyxFDB0q0+DChuxxiU2FXBQ=="
+ },
"run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
diff --git a/extension/react-app/package.json b/extension/react-app/package.json
index 3993b030..7d1211de 100644
--- a/extension/react-app/package.json
+++ b/extension/react-app/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"@styled-icons/heroicons-outline": "^10.47.0",
"@types/vscode-webview": "^1.57.1",
+ "posthog-js": "^1.58.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^8.0.5",
diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx
index 0c40ced1..a51541d0 100644
--- a/extension/react-app/src/App.tsx
+++ b/extension/react-app/src/App.tsx
@@ -4,7 +4,7 @@ import { Provider } from "react-redux";
import store from "./redux/store";
import WelcomeTab from "./tabs/welcome";
import ChatTab from "./tabs/chat";
-import Notebook from "./tabs/notebook";
+import GUI from "./tabs/gui";
function App() {
return (
@@ -13,8 +13,8 @@ function App() {
<DebugPanel
tabs={[
{
- element: <Notebook />,
- title: "Notebook",
+ element: <GUI />,
+ title: "GUI",
},
// { element: <MainTab />, title: "Debug Panel" },
// { element: <WelcomeTab />, title: "Welcome" },
diff --git a/extension/react-app/src/components/CodeBlock.tsx b/extension/react-app/src/components/CodeBlock.tsx
index 4c10ab23..e0336554 100644
--- a/extension/react-app/src/components/CodeBlock.tsx
+++ b/extension/react-app/src/components/CodeBlock.tsx
@@ -6,7 +6,8 @@ import { defaultBorderRadius, vscBackground } from ".";
import { Clipboard } from "@styled-icons/heroicons-outline";
const StyledPre = styled.pre`
- overflow: scroll;
+ overflow-y: scroll;
+ word-wrap: normal;
border: 1px solid gray;
border-radius: ${defaultBorderRadius};
background-color: ${vscBackground};
diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx
index ed00571b..9dacc624 100644
--- a/extension/react-app/src/components/DebugPanel.tsx
+++ b/extension/react-app/src/components/DebugPanel.tsx
@@ -36,7 +36,8 @@ const GradientContainer = styled.div`
const MainDiv = styled.div`
height: 100%;
border-radius: ${defaultBorderRadius};
- overflow: scroll;
+ overflow-y: scroll;
+ scrollbar-gutter: stable both-edges;
scrollbar-base-color: transparent;
/* background: ${vscBackground}; */
background-color: #1e1e1ede;
@@ -107,6 +108,7 @@ function DebugPanel(props: DebugPanelProps) {
className={
tab.title === "Chat" ? "overflow-hidden" : "overflow-scroll"
}
+ style={{ scrollbarGutter: "stable both-edges" }}
>
{tab.element}
</div>
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx
index 03649b66..5e979b34 100644
--- a/extension/react-app/src/components/StepContainer.tsx
+++ b/extension/react-app/src/components/StepContainer.tsx
@@ -36,6 +36,8 @@ const MainDiv = styled.div<{ stepDepth: number; inFuture: boolean }>`
animation: ${appear} 0.3s ease-in-out;
/* padding-left: ${(props) => props.stepDepth * 20}px; */
overflow: hidden;
+ margin-left: 0px;
+ margin-right: 0px;
`;
const StepContainerDiv = styled.div<{ open: boolean }>`
@@ -78,6 +80,13 @@ function StepContainer(props: StepContainerProps) {
const [open, setOpen] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const naturalLanguageInputRef = useRef<HTMLTextAreaElement>(null);
+ const userInputRef = useRef<HTMLInputElement>(null);
+
+ useEffect(() => {
+ if (userInputRef?.current) {
+ userInputRef.current.focus();
+ }
+ }, [userInputRef]);
useEffect(() => {
if (isHovered) {
@@ -134,6 +143,7 @@ function StepContainer(props: StepContainerProps) {
{props.historyNode.step.name === "Waiting for user input" && (
<input
+ ref={userInputRef}
className="m-auto p-2 rounded-md border-1 border-solid text-white w-3/4 border-gray-200 bg-vsc-background"
onKeyDown={(e) => {
if (e.key === "Enter") {
@@ -144,6 +154,9 @@ function StepContainer(props: StepContainerProps) {
onSubmit={(ev) => {
props.onUserInput(ev.currentTarget.value);
}}
+ onClick={(e) => {
+ e.stopPropagation();
+ }}
/>
)}
{props.historyNode.step.name === "Waiting for user confirmation" && (
@@ -165,24 +178,6 @@ function StepContainer(props: StepContainerProps) {
/>
</>
)}
-
- {open && (
- <>
- {/* {props.historyNode.observation && (
- <SubContainer title="Error">
- <CodeBlock>Error Here</CodeBlock>
- </SubContainer>
- )} */}
- {/* {props.iterationContext.suggestedChanges.map((sc) => {
- return (
- <SubContainer title="Suggested Change">
- {sc.filepath}
- <CodeBlock>{sc.replacement}</CodeBlock>
- </SubContainer>
- );
- })} */}
- </>
- )}
</StepContainerDiv>
</GradientBorder>
diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts
index e37c97f3..7ba60467 100644
--- a/extension/react-app/src/components/index.ts
+++ b/extension/react-app/src/components/index.ts
@@ -45,7 +45,7 @@ export const Pre = styled.pre`
border-radius: ${defaultBorderRadius};
padding: 8px;
max-height: 150px;
- overflow: scroll;
+ overflow-y: scroll;
margin: 0;
background-color: ${secondaryDark};
border: none;
diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
new file mode 100644
index 00000000..18a91de7
--- /dev/null
+++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
@@ -0,0 +1,13 @@
+abstract class AbstractContinueGUIClientProtocol {
+ abstract sendMainInput(input: string): void;
+
+ abstract reverseToIndex(index: number): void;
+
+ abstract sendRefinementInput(input: string, index: number): void;
+
+ abstract sendStepUserInput(input: string, index: number): void;
+
+ abstract onStateUpdate(state: any): void;
+}
+
+export default AbstractContinueGUIClientProtocol;
diff --git a/extension/react-app/src/hooks/messenger.ts b/extension/react-app/src/hooks/messenger.ts
new file mode 100644
index 00000000..e2a0bab8
--- /dev/null
+++ b/extension/react-app/src/hooks/messenger.ts
@@ -0,0 +1,108 @@
+// console.log("Websocket import");
+// const WebSocket = require("ws");
+
+export abstract class Messenger {
+ abstract send(messageType: string, data: object): void;
+
+ abstract onMessageType(
+ messageType: string,
+ callback: (data: object) => void
+ ): void;
+
+ abstract onMessage(callback: (messageType: string, data: any) => void): void;
+
+ abstract onOpen(callback: () => void): void;
+
+ abstract onClose(callback: () => void): void;
+
+ abstract sendAndReceive(messageType: string, data: any): Promise<any>;
+}
+
+export class WebsocketMessenger extends Messenger {
+ websocket: WebSocket;
+ private onMessageListeners: {
+ [messageType: string]: ((data: object) => void)[];
+ } = {};
+ private onOpenListeners: (() => void)[] = [];
+ private onCloseListeners: (() => void)[] = [];
+ private serverUrl: string;
+
+ _newWebsocket(): WebSocket {
+ // // Dynamic import, because WebSocket is builtin with browser, but not with node. And can't use require in browser.
+ // if (typeof process === "object") {
+ // console.log("Using node");
+ // // process is only available in Node
+ // var WebSocket = require("ws");
+ // }
+
+ const newWebsocket = new WebSocket(this.serverUrl);
+ for (const listener of this.onOpenListeners) {
+ this.onOpen(listener);
+ }
+ for (const listener of this.onCloseListeners) {
+ this.onClose(listener);
+ }
+ for (const messageType in this.onMessageListeners) {
+ for (const listener of this.onMessageListeners[messageType]) {
+ this.onMessageType(messageType, listener);
+ }
+ }
+ return newWebsocket;
+ }
+
+ constructor(serverUrl: string) {
+ super();
+ this.serverUrl = serverUrl;
+ this.websocket = this._newWebsocket();
+ }
+
+ send(messageType: string, data: object) {
+ const payload = JSON.stringify({ messageType, data });
+ if (this.websocket.readyState === this.websocket.OPEN) {
+ this.websocket.send(payload);
+ } else {
+ if (this.websocket.readyState !== this.websocket.CONNECTING) {
+ this.websocket = this._newWebsocket();
+ }
+ this.websocket.addEventListener("open", () => {
+ this.websocket.send(payload);
+ });
+ }
+ }
+
+ sendAndReceive(messageType: string, data: any): Promise<any> {
+ return new Promise((resolve, reject) => {
+ const eventListener = (data: any) => {
+ // THIS ISN"T GETTING CALLED
+ resolve(data);
+ this.websocket.removeEventListener("message", eventListener);
+ };
+ this.onMessageType(messageType, eventListener);
+ this.send(messageType, data);
+ });
+ }
+
+ onMessageType(messageType: string, callback: (data: any) => void): void {
+ this.websocket.addEventListener("message", (event: any) => {
+ const msg = JSON.parse(event.data);
+ if (msg.messageType === messageType) {
+ callback(msg.data);
+ }
+ });
+ }
+
+ onMessage(callback: (messageType: string, data: any) => void): void {
+ this.websocket.addEventListener("message", (event) => {
+ const msg = JSON.parse(event.data);
+ callback(msg.messageType, msg.data);
+ });
+ }
+
+ onOpen(callback: () => void): void {
+ this.websocket.addEventListener("open", callback);
+ }
+
+ onClose(callback: () => void): void {
+ this.websocket.addEventListener("close", callback);
+ }
+}
diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts
new file mode 100644
index 00000000..a3a1d0c9
--- /dev/null
+++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts
@@ -0,0 +1,49 @@
+import AbstractContinueGUIClientProtocol from "./ContinueGUIClientProtocol";
+// import { Messenger, WebsocketMessenger } from "../../../src/util/messenger";
+import { Messenger, WebsocketMessenger } from "./messenger";
+import { VscodeMessenger } from "./vscodeMessenger";
+
+class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {
+ messenger: Messenger;
+ // Server URL must contain the session ID param
+ serverUrlWithSessionId: string;
+
+ constructor(
+ serverUrlWithSessionId: string,
+ useVscodeMessagePassing: boolean
+ ) {
+ super();
+ this.serverUrlWithSessionId = serverUrlWithSessionId;
+ if (useVscodeMessagePassing) {
+ this.messenger = new VscodeMessenger(serverUrlWithSessionId);
+ } else {
+ this.messenger = new WebsocketMessenger(serverUrlWithSessionId);
+ }
+ }
+
+ sendMainInput(input: string) {
+ this.messenger.send("main_input", { input });
+ }
+
+ reverseToIndex(index: number) {
+ this.messenger.send("reverse_to_index", { index });
+ }
+
+ sendRefinementInput(input: string, index: number) {
+ this.messenger.send("refinement_input", { input, index });
+ }
+
+ sendStepUserInput(input: string, index: number) {
+ this.messenger.send("step_user_input", { input, index });
+ }
+
+ onStateUpdate(callback: (state: any) => void) {
+ this.messenger.onMessageType("state_update", (data: any) => {
+ if (data.state) {
+ callback(data.state);
+ }
+ });
+ }
+}
+
+export default ContinueGUIClientProtocol;
diff --git a/extension/react-app/src/hooks/useWebsocket.ts b/extension/react-app/src/hooks/useWebsocket.ts
index 147172bd..e762666f 100644
--- a/extension/react-app/src/hooks/useWebsocket.ts
+++ b/extension/react-app/src/hooks/useWebsocket.ts
@@ -1,67 +1,39 @@
import React, { useEffect, useState } from "react";
import { RootStore } from "../redux/store";
import { useSelector } from "react-redux";
+import ContinueGUIClientProtocol from "./useContinueGUIProtocol";
+import { postVscMessage } from "../vscode";
-function useContinueWebsocket(
- serverUrl: string,
- onMessage: (message: { data: any }) => void
-) {
+function useContinueGUIProtocol(useVscodeMessagePassing: boolean = true) {
const sessionId = useSelector((state: RootStore) => state.config.sessionId);
- const [websocket, setWebsocket] = useState<WebSocket | undefined>(undefined);
+ const serverHttpUrl = useSelector((state: RootStore) => state.config.apiUrl);
+ const [client, setClient] = useState<ContinueGUIClientProtocol | undefined>(
+ undefined
+ );
- async function connect() {
- while (!sessionId) {
- await new Promise((resolve) => setTimeout(resolve, 300));
+ useEffect(() => {
+ if (!sessionId || !serverHttpUrl) {
+ if (useVscodeMessagePassing) {
+ postVscMessage("onLoad", {});
+ }
+ setClient(undefined);
+ return;
}
- console.log("Creating websocket", sessionId);
-
- const wsUrl =
- serverUrl.replace("http", "ws") +
- "/notebook/ws?session_id=" +
+ const serverUrlWithSessionId =
+ serverHttpUrl.replace("http", "ws") +
+ "/gui/ws?session_id=" +
encodeURIComponent(sessionId);
- const ws = new WebSocket(wsUrl);
- setWebsocket(ws);
-
- // Set up callbacks
- ws.onopen = () => {
- console.log("Websocket opened");
- ws.send(JSON.stringify({ sessionId }));
- };
-
- ws.onmessage = (msg) => {
- onMessage(msg);
- console.log("Got message", msg);
- };
-
- ws.onclose = (msg) => {
- console.log("Websocket closed");
- setWebsocket(undefined);
- };
-
- return ws;
- }
-
- async function getConnection() {
- if (!websocket) {
- return await connect();
- }
- return websocket;
- }
-
- async function send(message: object) {
- let ws = await getConnection();
- ws.send(JSON.stringify(message));
- }
-
- useEffect(() => {
- if (!sessionId) {
- return;
- }
- connect();
- }, [sessionId]);
+ console.log("Creating websocket", serverUrlWithSessionId);
+ console.log("Using vscode message passing", useVscodeMessagePassing);
+ const newClient = new ContinueGUIClientProtocol(
+ serverUrlWithSessionId,
+ useVscodeMessagePassing
+ );
+ setClient(newClient);
+ }, [sessionId, serverHttpUrl]);
- return { send };
+ return client;
}
-export default useContinueWebsocket;
+export default useContinueGUIProtocol;
diff --git a/extension/react-app/src/hooks/vscodeMessenger.ts b/extension/react-app/src/hooks/vscodeMessenger.ts
new file mode 100644
index 00000000..ba19586b
--- /dev/null
+++ b/extension/react-app/src/hooks/vscodeMessenger.ts
@@ -0,0 +1,71 @@
+import { postVscMessage } from "../vscode";
+// import { Messenger } from "../../../src/util/messenger";
+import { Messenger } from "./messenger";
+
+export class VscodeMessenger extends Messenger {
+ serverUrl: string;
+
+ constructor(serverUrl: string) {
+ super();
+ this.serverUrl = serverUrl;
+ postVscMessage("websocketForwardingOpen", { url: this.serverUrl });
+ }
+
+ send(messageType: string, data: object) {
+ postVscMessage("websocketForwardingMessage", {
+ message: { messageType, data },
+ url: this.serverUrl,
+ });
+ }
+
+ onMessageType(messageType: string, callback: (data: object) => void): void {
+ window.addEventListener("message", (event: any) => {
+ if (event.data.type === "websocketForwardingMessage") {
+ const data = JSON.parse(event.data.data);
+ if (data.messageType === messageType) {
+ callback(data.data);
+ }
+ }
+ });
+ }
+
+ onMessage(callback: (messageType: string, data: any) => void): void {
+ window.addEventListener("message", (event: any) => {
+ if (event.data.type === "websocketForwardingMessage") {
+ const data = JSON.parse(event.data.data);
+ callback(data.messageType, data.data);
+ }
+ });
+ }
+
+ sendAndReceive(messageType: string, data: any): Promise<any> {
+ return new Promise((resolve) => {
+ const handler = (event: any) => {
+ if (event.data.type === "websocketForwardingMessage") {
+ const data = JSON.parse(event.data.data);
+ if (data.messageType === messageType) {
+ window.removeEventListener("message", handler);
+ resolve(data.data);
+ }
+ }
+ };
+ window.addEventListener("message", handler);
+ this.send(messageType, data);
+ });
+ }
+
+ onOpen(callback: () => void): void {
+ window.addEventListener("message", (event: any) => {
+ if (event.data.type === "websocketForwardingOpen") {
+ callback();
+ }
+ });
+ }
+ onClose(callback: () => void): void {
+ window.addEventListener("message", (event: any) => {
+ if (event.data.type === "websocketForwardingClose") {
+ callback();
+ }
+ });
+ }
+}
diff --git a/extension/react-app/src/index.css b/extension/react-app/src/index.css
index dd38eec3..20599d30 100644
--- a/extension/react-app/src/index.css
+++ b/extension/react-app/src/index.css
@@ -21,7 +21,7 @@
html,
body,
#root {
- height: calc(100% - 7px);
+ height: calc(100%);
}
body {
diff --git a/extension/react-app/src/main.tsx b/extension/react-app/src/main.tsx
index 791f139e..1b94dc82 100644
--- a/extension/react-app/src/main.tsx
+++ b/extension/react-app/src/main.tsx
@@ -1,10 +1,19 @@
-import React from 'react'
-import ReactDOM from 'react-dom/client'
-import App from './App'
-import './index.css'
+import React from "react";
+import ReactDOM from "react-dom/client";
+import App from "./App";
+import "./index.css";
-ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
+import posthog from "posthog-js";
+import { PostHogProvider } from "posthog-js/react";
+
+posthog.init("phc_JS6XFROuNbhJtVCEdTSYk6gl5ArRrTNMpCcguAXlSPs", {
+ api_host: "https://app.posthog.com",
+});
+
+ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
- <App />
- </React.StrictMode>,
-)
+ <PostHogProvider client={posthog}>
+ <App />
+ </PostHogProvider>
+ </React.StrictMode>
+);
diff --git a/extension/react-app/src/tabs/chat/MessageDiv.tsx b/extension/react-app/src/tabs/chat/MessageDiv.tsx
index d7c79721..9bdd8638 100644
--- a/extension/react-app/src/tabs/chat/MessageDiv.tsx
+++ b/extension/react-app/src/tabs/chat/MessageDiv.tsx
@@ -20,7 +20,8 @@ const Container = styled.div`
margin: 3px;
width: fit-content;
max-width: 75%;
- overflow: scroll;
+ overflow-y: scroll;
+ scrollbar-gutter: stable both-edges;
word-wrap: break-word;
-ms-word-wrap: break-word;
height: fit-content;
diff --git a/extension/react-app/src/tabs/notebook.tsx b/extension/react-app/src/tabs/gui.tsx
index a9c69c5b..42ad4ed5 100644
--- a/extension/react-app/src/tabs/notebook.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -14,10 +14,12 @@ import StepContainer from "../components/StepContainer";
import { useSelector } from "react-redux";
import { RootStore } from "../redux/store";
import useContinueWebsocket from "../hooks/useWebsocket";
+import useContinueGUIProtocol from "../hooks/useWebsocket";
-let TopNotebookDiv = styled.div`
+let TopGUIDiv = styled.div`
display: grid;
grid-template-columns: 1fr;
+ overflow: scroll;
`;
let UserInputQueueItem = styled.div`
@@ -28,17 +30,15 @@ let UserInputQueueItem = styled.div`
text-align: center;
`;
-interface NotebookProps {
+interface GUIProps {
firstObservation?: any;
}
-function Notebook(props: NotebookProps) {
- const serverUrl = useSelector((state: RootStore) => state.config.apiUrl);
-
+function GUI(props: GUIProps) {
const [waitingForSteps, setWaitingForSteps] = useState(false);
const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
const [history, setHistory] = useState<History | undefined>();
- // {
+ // {
// timeline: [
// {
// step: {
@@ -154,33 +154,19 @@ function Notebook(props: NotebookProps) {
// },
// ],
// current_index: 0,
- // } as any
- // );
+ // } as any);
- const { send: websocketSend } = useContinueWebsocket(serverUrl, (msg) => {
- let data = JSON.parse(msg.data);
- if (data.messageType === "state") {
- setWaitingForSteps(data.state.active);
- setHistory(data.state.history);
- setUserInputQueue(data.state.user_input_queue);
- }
- });
+ const client = useContinueGUIProtocol();
- // useEffect(() => {
- // (async () => {
- // if (sessionId && props.firstObservation) {
- // let resp = await fetch(serverUrl + "/observation", {
- // method: "POST",
- // headers: new Headers({
- // "x-continue-session-id": sessionId,
- // }),
- // body: JSON.stringify({
- // observation: props.firstObservation,
- // }),
- // });
- // }
- // })();
- // }, [props.firstObservation]);
+ useEffect(() => {
+ console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate);
+ client?.onStateUpdate((state) => {
+ console.log("Received state update: ", state);
+ setWaitingForSteps(state.active);
+ setHistory(state.history);
+ setUserInputQueue(state.user_input_queue);
+ });
+ }, [client]);
const mainTextInputRef = useRef<HTMLTextAreaElement>(null);
@@ -201,14 +187,12 @@ function Notebook(props: NotebookProps) {
const onMainTextInput = () => {
if (mainTextInputRef.current) {
- let value = mainTextInputRef.current.value;
+ if (!client) return;
+ let input = mainTextInputRef.current.value;
setWaitingForSteps(true);
- websocketSend({
- messageType: "main_input",
- value: value,
- });
+ client.sendMainInput(input);
setUserInputQueue((queue) => {
- return [...queue, value];
+ return [...queue, input];
});
mainTextInputRef.current.value = "";
mainTextInputRef.current.style.height = "";
@@ -216,17 +200,22 @@ function Notebook(props: NotebookProps) {
};
const onStepUserInput = (input: string, index: number) => {
+ if (!client) return;
console.log("Sending step user input", input, index);
- websocketSend({
- messageType: "step_user_input",
- value: input,
- index,
- });
+ client.sendStepUserInput(input, index);
};
// const iterations = useSelector(selectIterations);
return (
- <TopNotebookDiv>
+ <TopGUIDiv>
+ {typeof client === "undefined" && (
+ <>
+ <Loader></Loader>
+ <p style={{ textAlign: "center" }}>
+ Trying to reconnect with server...
+ </p>
+ </>
+ )}
{history?.timeline.map((node: HistoryNode, index: number) => {
return (
<StepContainer
@@ -237,17 +226,10 @@ function Notebook(props: NotebookProps) {
inFuture={index > history?.current_index}
historyNode={node}
onRefinement={(input: string) => {
- websocketSend({
- messageType: "refinement_input",
- value: input,
- index,
- });
+ client?.sendRefinementInput(input, index);
}}
onReverse={() => {
- websocketSend({
- messageType: "reverse",
- index,
- });
+ client?.reverseToIndex(index);
}}
/>
);
@@ -278,8 +260,8 @@ function Notebook(props: NotebookProps) {
}}
></MainTextInput>
<ContinueButton onClick={onMainTextInput}></ContinueButton>
- </TopNotebookDiv>
+ </TopGUIDiv>
);
}
-export default Notebook;
+export default GUI;
diff --git a/extension/react-app/src/vscode/index.ts b/extension/react-app/src/vscode/index.ts
index 7e373cd9..0785aa4d 100644
--- a/extension/react-app/src/vscode/index.ts
+++ b/extension/react-app/src/vscode/index.ts
@@ -5,6 +5,7 @@ declare const vscode: any;
export function postVscMessage(type: string, data: any) {
if (typeof vscode === "undefined") {
+ console.log("Unable to send message: vscode is undefined");
return;
}
vscode.postMessage({
diff --git a/extension/react-app/tsconfig.json b/extension/react-app/tsconfig.json
index 3d0a51a8..940a9359 100644
--- a/extension/react-app/tsconfig.json
+++ b/extension/react-app/tsconfig.json
@@ -16,6 +16,6 @@
"noEmit": true,
"jsx": "react-jsx"
},
- "include": ["src"],
+ "include": ["src", "../src/util/messenger.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
diff --git a/extension/schema/History.d.ts b/extension/schema/History.d.ts
new file mode 100644
index 00000000..508deaf0
--- /dev/null
+++ b/extension/schema/History.d.ts
@@ -0,0 +1,41 @@
+/* eslint-disable */
+/**
+ * This file was automatically generated by json-schema-to-typescript.
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
+ * and run json-schema-to-typescript to regenerate this file.
+ */
+
+export type History = History1;
+export type Name = string;
+export type Hide = boolean;
+export type SystemMessage = string;
+export type Depth = number;
+export type Timeline = HistoryNode[];
+export type CurrentIndex = number;
+
+/**
+ * A history of steps taken and their results
+ */
+export interface History1 {
+ timeline: Timeline;
+ current_index: CurrentIndex;
+ [k: string]: unknown;
+}
+/**
+ * A point in history, a list of which make up History
+ */
+export interface HistoryNode {
+ step: Step;
+ observation?: Observation;
+ depth: Depth;
+ [k: string]: unknown;
+}
+export interface Step {
+ name?: Name;
+ hide?: Hide;
+ system_message?: SystemMessage;
+ [k: string]: unknown;
+}
+export interface Observation {
+ [k: string]: unknown;
+}
diff --git a/extension/schema/HistoryNode.d.ts b/extension/schema/HistoryNode.d.ts
new file mode 100644
index 00000000..c1507270
--- /dev/null
+++ b/extension/schema/HistoryNode.d.ts
@@ -0,0 +1,31 @@
+/* eslint-disable */
+/**
+ * This file was automatically generated by json-schema-to-typescript.
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
+ * and run json-schema-to-typescript to regenerate this file.
+ */
+
+export type HistoryNode = HistoryNode1;
+export type Name = string;
+export type Hide = boolean;
+export type SystemMessage = string;
+export type Depth = number;
+
+/**
+ * A point in history, a list of which make up History
+ */
+export interface HistoryNode1 {
+ step: Step;
+ observation?: Observation;
+ depth: Depth;
+ [k: string]: unknown;
+}
+export interface Step {
+ name?: Name;
+ hide?: Hide;
+ system_message?: SystemMessage;
+ [k: string]: unknown;
+}
+export interface Observation {
+ [k: string]: unknown;
+}
diff --git a/extension/schema/README.md b/extension/schema/README.md
new file mode 100644
index 00000000..9c97c0eb
--- /dev/null
+++ b/extension/schema/README.md
@@ -0,0 +1,3 @@
+# Schema
+
+These are files autogenerated by `npm run typegen`. They come originally from the `continuedev` Python package's Pydantic types.
diff --git a/extension/scripts/continuedev-0.1.0-py3-none-any.whl b/extension/scripts/continuedev-0.1.0-py3-none-any.whl
index 15787c59..fd3f48d1 100644
--- a/extension/scripts/continuedev-0.1.0-py3-none-any.whl
+++ b/extension/scripts/continuedev-0.1.0-py3-none-any.whl
Binary files differ
diff --git a/extension/scripts/install_from_source.py b/extension/scripts/install_from_source.py
new file mode 100644
index 00000000..bbb86797
--- /dev/null
+++ b/extension/scripts/install_from_source.py
@@ -0,0 +1,63 @@
+import os
+import subprocess
+
+
+def run(cmd: str):
+ return subprocess.run(cmd, shell=True, capture_output=False)
+
+
+def main():
+ # Check for Python and Node - we won't install them, but will warn
+ resp1 = run("python --version")
+ resp2 = run("python3 --version")
+ if resp1.stderr and resp2.stderr:
+ print("Python is required for Continue but is not installed on your machine. See https://www.python.org/downloads/ to download the latest version, then try again.")
+ return
+
+ resp = run("node --version")
+ if resp.stderr:
+ print("Node is required for Continue but is not installed on your machine. See https://nodejs.org/en/download/ to download the latest version, then try again.")
+ return
+
+ resp = run("npm --version")
+ if resp.stderr:
+ print("NPM is required for Continue but is not installed on your machine. See https://nodejs.org/en/download/ to download the latest version, then try again.")
+ return
+
+ resp = run("poetry --version")
+ if resp.stderr:
+ print("Poetry is required for Continue but is not installed on your machine. See https://python-poetry.org/docs/#installation to download the latest version, then try again.")
+ return
+
+ resp = run("cd ../../continuedev; poetry install; poetry run typegen")
+
+ resp = run(
+ "cd ..; npm i; cd react-app; npm i; cd ..; npm run full-package")
+
+ if resp.stderr:
+ print("Error packaging the extension. Please try again.")
+ print("This was the error: ", resp.stderr)
+ return
+
+ latest = None
+ latest_major = 0
+ latest_minor = 0
+ latest_patch = 0
+ for file in os.listdir("../build"):
+ if file.endswith(".vsix"):
+ version = file.split("-")[1].split(".vsix")[0]
+ major, minor, patch = list(
+ map(lambda x: int(x), version.split(".")))
+ if latest is None or (major >= latest_major and minor >= latest_minor and patch > latest_patch):
+ latest = file
+ latest_major = major
+ latest_minor = minor
+ latest_patch = patch
+
+ resp = run(f"cd ..; code --install-extension ./build/{latest}")
+
+ print("Continue VS Code extension installed successfully. Please restart VS Code to use it.")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts
index a0aa560b..40def480 100644
--- a/extension/src/activation/activate.ts
+++ b/extension/src/activation/activate.ts
@@ -10,7 +10,7 @@ import { getContinueServerUrl } from "../bridge";
export let extensionContext: vscode.ExtensionContext | undefined = undefined;
-export let ideProtocolClient: IdeProtocolClient | undefined = undefined;
+export let ideProtocolClient: IdeProtocolClient;
export function activateExtension(
context: vscode.ExtensionContext,
@@ -24,7 +24,7 @@ export function activateExtension(
let serverUrl = getContinueServerUrl();
ideProtocolClient = new IdeProtocolClient(
- serverUrl.replace("http", "ws") + "/ide/ws",
+ `${serverUrl.replace("http", "ws")}/ide/ws`,
context
);
@@ -59,12 +59,12 @@ export function activateExtension(
})
),
]).then(() => {
- ideProtocolClient?.openNotebook();
+ ideProtocolClient?.openGUI();
});
} else {
- // ideProtocolClient?.openNotebook().then(() => {
- // // openCapturedTerminal();
- // });
+ ideProtocolClient.openGUI().then(() => {
+ // openCapturedTerminal();
+ });
}
extensionContext = context;
diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts
index 4816b4b1..170426e1 100644
--- a/extension/src/activation/environmentSetup.ts
+++ b/extension/src/activation/environmentSetup.ts
@@ -10,6 +10,7 @@ import { getContinueServerUrl } from "../bridge";
import fetch from "node-fetch";
async function runCommand(cmd: string): Promise<[string, string | undefined]> {
+ console.log("Running command: ", cmd);
var stdout: any = "";
var stderr: any = "";
try {
@@ -28,18 +29,7 @@ async function runCommand(cmd: string): Promise<[string, string | undefined]> {
return [stdout, stderr];
}
-async function getPythonCmdAssumingInstalled() {
- const [, stderr] = await runCommand("python3 --version");
- if (stderr) {
- return "python";
- }
- return "python3";
-}
-
-async function setupPythonEnv() {
- console.log("Setting up python env for Continue extension...");
- // First check that python3 is installed
-
+async function getPythonPipCommands() {
var [stdout, stderr] = await runCommand("python3 --version");
let pythonCmd = "python3";
if (stderr) {
@@ -58,28 +48,81 @@ async function setupPythonEnv() {
}
}
let pipCmd = pythonCmd.endsWith("3") ? "pip3" : "pip";
+ return [pythonCmd, pipCmd];
+}
- let activateCmd = "source env/bin/activate";
+function getActivateUpgradeCommands(pythonCmd: string, pipCmd: string) {
+ let activateCmd = ". env/bin/activate";
let pipUpgradeCmd = `${pipCmd} install --upgrade pip`;
if (process.platform == "win32") {
activateCmd = ".\\env\\Scripts\\activate";
pipUpgradeCmd = `${pythonCmd} -m pip install --upgrade pip`;
}
+ return [activateCmd, pipUpgradeCmd];
+}
- let command = `cd ${path.join(
+function checkEnvExists() {
+ const envBinPath = path.join(
getExtensionUri().fsPath,
- "scripts"
- )} && ${pythonCmd} -m venv env && ${activateCmd} && ${pipUpgradeCmd} && ${pipCmd} install -r requirements.txt`;
- var [stdout, stderr] = await runCommand(command);
- if (stderr) {
- throw new Error(stderr);
+ "scripts",
+ "env",
+ process.platform == "win32" ? "Scripts" : "bin"
+ );
+ return (
+ fs.existsSync(path.join(envBinPath, "activate")) &&
+ fs.existsSync(path.join(envBinPath, "pip"))
+ );
+}
+
+async function setupPythonEnv() {
+ console.log("Setting up python env for Continue extension...");
+
+ if (checkEnvExists()) return;
+
+ // Assemble the command to create the env
+ const [pythonCmd, pipCmd] = await getPythonPipCommands();
+ const [activateCmd, pipUpgradeCmd] = getActivateUpgradeCommands(
+ pythonCmd,
+ pipCmd
+ );
+ const createEnvCommand = [
+ `cd ${path.join(getExtensionUri().fsPath, "scripts")}`,
+ `${pythonCmd} -m venv env`,
+ ].join(" && ");
+
+ // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again)
+ while (true) {
+ const [, stderr] = await runCommand(createEnvCommand);
+ if (stderr) {
+ throw new Error(stderr);
+ }
+ if (checkEnvExists()) {
+ break;
+ } else {
+ // Remove the env and try again
+ const removeCommand = `rm -rf ${path.join(
+ getExtensionUri().fsPath,
+ "scripts",
+ "env"
+ )}`;
+ await runCommand(removeCommand);
+ }
}
console.log(
"Successfully set up python env at ",
getExtensionUri().fsPath + "/scripts/env"
);
- await startContinuePythonServer();
+ const installRequirementsCommand = [
+ `cd ${path.join(getExtensionUri().fsPath, "scripts")}`,
+ activateCmd,
+ pipUpgradeCmd,
+ `${pipCmd} install -r requirements.txt`,
+ ].join(" && ");
+ const [, stderr] = await runCommand(installRequirementsCommand);
+ if (stderr) {
+ throw new Error(stderr);
+ }
}
function readEnvFile(path: string) {
@@ -116,38 +159,26 @@ function writeEnvFile(path: string, key: string, value: string) {
}
export async function startContinuePythonServer() {
+ await setupPythonEnv();
+
// Check vscode settings
let serverUrl = getContinueServerUrl();
if (serverUrl !== "http://localhost:8000") {
return;
}
- let envFile = path.join(getExtensionUri().fsPath, "scripts", ".env");
- let openai_api_key: string | undefined =
- readEnvFile(envFile)["OPENAI_API_KEY"];
- while (typeof openai_api_key === "undefined" || openai_api_key === "") {
- openai_api_key = await vscode.window.showInputBox({
- prompt: "Enter your OpenAI API key",
- placeHolder: "Enter your OpenAI API key",
- });
- // Write to .env file
- }
- writeEnvFile(envFile, "OPENAI_API_KEY", openai_api_key);
-
console.log("Starting Continue python server...");
// Check if already running by calling /health
try {
- let response = await fetch(serverUrl + "/health");
+ const response = await fetch(serverUrl + "/health");
if (response.status === 200) {
console.log("Continue python server already running");
return;
}
- } catch (e) {
- console.log("Error checking for existing server", e);
- }
+ } catch (e) {}
- let activateCmd = "source env/bin/activate";
+ let activateCmd = ". env/bin/activate";
let pythonCmd = "python3";
if (process.platform == "win32") {
activateCmd = ".\\env\\Scripts\\activate";
@@ -158,26 +189,30 @@ export async function startContinuePythonServer() {
getExtensionUri().fsPath,
"scripts"
)} && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`;
- try {
- // exec(command);
- let child = spawn(command, {
- shell: true,
- });
- child.stdout.on("data", (data: any) => {
- console.log(`stdout: ${data}`);
- });
- child.stderr.on("data", (data: any) => {
- console.log(`stderr: ${data}`);
- });
- child.on("error", (error: any) => {
- console.log(`error: ${error.message}`);
- });
- } catch (e) {
- console.log("Failed to start Continue python server", e);
- }
- // Sleep for 3 seconds to give the server time to start
- await new Promise((resolve) => setTimeout(resolve, 3000));
- console.log("Successfully started Continue python server");
+
+ return new Promise((resolve, reject) => {
+ try {
+ const child = spawn(command, {
+ shell: true,
+ });
+ child.stdout.on("data", (data: any) => {
+ console.log(`stdout: ${data}`);
+ });
+ child.stderr.on("data", (data: any) => {
+ console.log(`stderr: ${data}`);
+ if (data.includes("Uvicorn running on")) {
+ console.log("Successfully started Continue python server");
+ resolve(null);
+ }
+ });
+ child.on("error", (error: any) => {
+ console.log(`error: ${error.message}`);
+ });
+ } catch (e) {
+ console.log("Failed to start Continue python server", e);
+ reject();
+ }
+ });
}
async function installNodeModules() {
@@ -195,11 +230,6 @@ export function isPythonEnvSetup(): boolean {
return fs.existsSync(path.join(pathToEnvCfg));
}
-export async function setupExtensionEnvironment() {
- console.log("Setting up environment for Continue extension...");
- await Promise.all([setupPythonEnv()]);
-}
-
export async function downloadPython3() {
// Download python3 and return the command to run it (python or python3)
let os = process.platform;
diff --git a/extension/src/commands.ts b/extension/src/commands.ts
index 18f08e31..f0c1744b 100644
--- a/extension/src/commands.ts
+++ b/extension/src/commands.ts
@@ -62,11 +62,11 @@ const commandsMap: { [command: string]: (...args: any) => any } = {
"continue.acceptSuggestion": acceptSuggestionCommand,
"continue.rejectSuggestion": rejectSuggestionCommand,
"continue.openDebugPanel": () => {
- ideProtocolClient?.openNotebook();
+ ideProtocolClient.openGUI();
},
"continue.focusContinueInput": async () => {
if (!debugPanelWebview) {
- await ideProtocolClient?.openNotebook();
+ await ideProtocolClient.openGUI();
}
debugPanelWebview?.postMessage({
type: "focusContinueInput",
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index 6c65415f..03e5fbc5 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -10,30 +10,28 @@ import {
} from "./suggestions";
import { debugPanelWebview, setupDebugPanel } from "./debugPanel";
import { FileEditWithFullContents } from "../schema/FileEditWithFullContents";
-const util = require("util");
-const exec = util.promisify(require("child_process").exec);
-const WebSocket = require("ws");
import fs = require("fs");
+import { WebsocketMessenger } from "./util/messenger";
class IdeProtocolClient {
- private _ws: WebSocket | null = null;
- private _panels: Map<string, vscode.WebviewPanel> = new Map();
- private readonly _serverUrl: string;
- private readonly _context: vscode.ExtensionContext;
+ private messenger: WebsocketMessenger | null = null;
+ private panels: Map<string, vscode.WebviewPanel> = new Map();
+ private readonly context: vscode.ExtensionContext;
private _makingEdit = 0;
constructor(serverUrl: string, context: vscode.ExtensionContext) {
- this._context = context;
- this._serverUrl = serverUrl;
- let ws = new WebSocket(serverUrl);
- this._ws = ws;
- ws.onclose = () => {
- this._ws = null;
- };
- ws.on("message", (data: any) => {
- this.handleMessage(JSON.parse(data));
+ this.context = context;
+
+ let messenger = new WebsocketMessenger(serverUrl);
+ this.messenger = messenger;
+ messenger.onClose(() => {
+ this.messenger = null;
+ });
+ messenger.onMessage((messageType, data) => {
+ this.handleMessage(messageType, data);
});
+
// Setup listeners for any file changes in open editors
vscode.workspace.onDidChangeTextDocument((event) => {
if (this._makingEdit === 0) {
@@ -58,121 +56,52 @@ class IdeProtocolClient {
};
}
);
- this.send("fileEdits", { fileEdits });
+ this.messenger?.send("fileEdits", { fileEdits });
} else {
this._makingEdit--;
}
});
}
- async isConnected() {
- if (this._ws === null) {
- this._ws = new WebSocket(this._serverUrl);
- }
- // On open, return a promise
- if (this._ws!.readyState === WebSocket.OPEN) {
- return;
- }
- return new Promise((resolve, reject) => {
- this._ws!.onopen = () => {
- resolve(null);
- };
- });
- }
-
- async startCore() {
- var { stdout, stderr } = await exec(
- "cd /Users/natesesti/Desktop/continue/continue && poetry shell"
- );
- if (stderr) {
- throw new Error(stderr);
- }
- var { stdout, stderr } = await exec(
- "cd .. && uvicorn continue.src.server.main:app --reload --reload-dir continue"
- );
- if (stderr) {
- throw new Error(stderr);
- }
- var { stdout, stderr } = await exec("python3 -m continue.src.libs.ide");
- if (stderr) {
- throw new Error(stderr);
- }
- }
-
- async send(messageType: string, data: object) {
- await this.isConnected();
- let msg = JSON.stringify({ messageType, ...data });
- this._ws!.send(msg);
- console.log("Sent message", msg);
- }
-
- async receiveMessage(messageType: string): Promise<any> {
- await this.isConnected();
- console.log("Connected to websocket");
- return await new Promise((resolve, reject) => {
- if (!this._ws) {
- reject("Not connected to websocket");
- }
- this._ws!.onmessage = (event: any) => {
- let message = JSON.parse(event.data);
- console.log("RECEIVED MESSAGE", message);
- if (message.messageType === messageType) {
- resolve(message);
- }
- };
- });
- }
-
- async sendAndReceive(message: any, messageType: string): Promise<any> {
- try {
- await this.send(messageType, message);
- let msg = await this.receiveMessage(messageType);
- console.log("Received message", msg);
- return msg;
- } catch (e) {
- console.log("Error sending message", e);
- }
- }
-
- async handleMessage(message: any) {
- switch (message.messageType) {
+ async handleMessage(messageType: string, data: any) {
+ switch (messageType) {
case "highlightedCode":
- this.send("highlightedCode", {
+ this.messenger?.send("highlightedCode", {
highlightedCode: this.getHighlightedCode(),
});
break;
case "workspaceDirectory":
- this.send("workspaceDirectory", {
+ this.messenger?.send("workspaceDirectory", {
workspaceDirectory: this.getWorkspaceDirectory(),
});
case "openFiles":
- this.send("openFiles", {
+ this.messenger?.send("openFiles", {
openFiles: this.getOpenFiles(),
});
break;
case "readFile":
- this.send("readFile", {
- contents: this.readFile(message.filepath),
+ this.messenger?.send("readFile", {
+ contents: this.readFile(data.filepath),
});
break;
case "editFile":
- let fileEdit = await this.editFile(message.edit);
- this.send("editFile", {
+ const fileEdit = await this.editFile(data.edit);
+ this.messenger?.send("editFile", {
fileEdit,
});
break;
case "saveFile":
- this.saveFile(message.filepath);
+ this.saveFile(data.filepath);
break;
case "setFileOpen":
- this.openFile(message.filepath);
+ this.openFile(data.filepath);
// TODO: Close file
break;
- case "openNotebook":
+ case "openGUI":
case "connected":
break;
default:
- throw Error("Unknown message type:" + message.messageType);
+ throw Error("Unknown message type:" + messageType);
}
}
getWorkspaceDirectory() {
@@ -204,18 +133,21 @@ class IdeProtocolClient {
// ------------------------------------ //
// Initiate Request
- closeNotebook(sessionId: string) {
- this._panels.get(sessionId)?.dispose();
- this._panels.delete(sessionId);
+ closeGUI(sessionId: string) {
+ this.panels.get(sessionId)?.dispose();
+ this.panels.delete(sessionId);
}
- async openNotebook() {
- console.log("OPENING NOTEBOOK");
- let resp = await this.sendAndReceive({}, "openNotebook");
- let sessionId = resp.sessionId;
+ async openGUI() {
+ console.log("OPENING GUI");
+ if (this.messenger === null) {
+ console.log("MESSENGER IS NULL");
+ }
+ const resp = await this.messenger?.sendAndReceive("openGUI", {});
+ const sessionId = resp.sessionId;
console.log("SESSION ID", sessionId);
- let column = getRightViewColumn();
+ const column = getRightViewColumn();
const panel = vscode.window.createWebviewPanel(
"continue.debugPanelView",
"Continue",
@@ -227,9 +159,9 @@ class IdeProtocolClient {
);
// And set its HTML content
- panel.webview.html = setupDebugPanel(panel, this._context, sessionId);
+ panel.webview.html = setupDebugPanel(panel, this.context, sessionId);
- this._panels.set(sessionId, panel);
+ this.panels.set(sessionId, panel);
}
acceptRejectSuggestion(accept: boolean, key: SuggestionRanges) {
diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts
index 66829836..87c33da1 100644
--- a/extension/src/debugPanel.ts
+++ b/extension/src/debugPanel.ts
@@ -16,6 +16,7 @@ import {
import { sendTelemetryEvent, TelemetryEvent } from "./telemetry";
import { RangeInFile, SerializedDebugContext } from "./client";
import { addFileSystemToDebugContext } from "./util/util";
+const WebSocket = require("ws");
class StreamManager {
private _fullText: string = "";
@@ -87,6 +88,49 @@ class StreamManager {
}
}
+let websocketConnections: { [url: string]: WebsocketConnection | undefined } =
+ {};
+
+class WebsocketConnection {
+ private _ws: WebSocket;
+ private _onMessage: (message: string) => void;
+ private _onOpen: () => void;
+ private _onClose: () => void;
+
+ constructor(
+ url: string,
+ onMessage: (message: string) => void,
+ onOpen: () => void,
+ onClose: () => void
+ ) {
+ this._ws = new WebSocket(url);
+ this._onMessage = onMessage;
+ this._onOpen = onOpen;
+ this._onClose = onClose;
+
+ this._ws.addEventListener("message", (event) => {
+ this._onMessage(event.data);
+ });
+ this._ws.addEventListener("close", () => {
+ this._onClose();
+ });
+ this._ws.addEventListener("open", () => {
+ this._onOpen();
+ });
+ }
+
+ public send(message: string) {
+ if (typeof message !== "string") {
+ message = JSON.stringify(message);
+ }
+ this._ws.send(message);
+ }
+
+ public close() {
+ this._ws.close();
+ }
+}
+
let streamManager = new StreamManager();
export let debugPanelWebview: vscode.Webview | undefined;
@@ -147,6 +191,39 @@ export function setupDebugPanel(
});
});
+ async function connectWebsocket(url: string) {
+ return new Promise((resolve, reject) => {
+ const onMessage = (message: any) => {
+ panel.webview.postMessage({
+ type: "websocketForwardingMessage",
+ url,
+ data: message,
+ });
+ };
+ const onOpen = () => {
+ panel.webview.postMessage({
+ type: "websocketForwardingOpen",
+ url,
+ });
+ resolve(null);
+ };
+ const onClose = () => {
+ websocketConnections[url] = undefined;
+ panel.webview.postMessage({
+ type: "websocketForwardingClose",
+ url,
+ });
+ };
+ const connection = new WebsocketConnection(
+ url,
+ onMessage,
+ onOpen,
+ onClose
+ );
+ websocketConnections[url] = connection;
+ });
+ }
+
panel.webview.onDidReceiveMessage(async (data) => {
switch (data.type) {
case "onLoad": {
@@ -156,6 +233,40 @@ export function setupDebugPanel(
apiUrl: getContinueServerUrl(),
sessionId,
});
+
+ // // Listen for changes to server URL in settings
+ // vscode.workspace.onDidChangeConfiguration((event) => {
+ // if (event.affectsConfiguration("continue.serverUrl")) {
+ // debugPanelWebview?.postMessage({
+ // type: "onLoad",
+ // vscMachineId: vscode.env.machineId,
+ // apiUrl: getContinueServerUrl(),
+ // sessionId,
+ // });
+ // }
+ // });
+
+ break;
+ }
+
+ case "websocketForwardingOpen": {
+ let url = data.url;
+ if (typeof websocketConnections[url] === "undefined") {
+ await connectWebsocket(url);
+ }
+ break;
+ }
+ case "websocketForwardingMessage": {
+ let url = data.url;
+ let connection = websocketConnections[url];
+ if (typeof connection === "undefined") {
+ await connectWebsocket(url);
+ }
+ connection = websocketConnections[url];
+ if (typeof connection === "undefined") {
+ throw new Error("Failed to connect websocket in VS Code Extension");
+ }
+ connection.send(data.message);
break;
}
case "listTenThings": {
diff --git a/extension/src/extension.ts b/extension/src/extension.ts
index e0b94278..88af0d19 100644
--- a/extension/src/extension.ts
+++ b/extension/src/extension.ts
@@ -4,7 +4,6 @@
import * as vscode from "vscode";
import {
- setupExtensionEnvironment,
isPythonEnvSetup,
startContinuePythonServer,
} from "./activation/environmentSetup";
@@ -26,11 +25,7 @@ export function activate(context: vscode.ExtensionContext) {
cancellable: false,
},
async () => {
- if (isPythonEnvSetup()) {
- await startContinuePythonServer();
- } else {
- await setupExtensionEnvironment();
- }
+ await startContinuePythonServer();
dynamicImportAndActivate(context, true);
}
);
diff --git a/extension/src/test/runTest.ts b/extension/src/test/runTest.ts
index 27b3ceb2..e810ed5b 100644
--- a/extension/src/test/runTest.ts
+++ b/extension/src/test/runTest.ts
@@ -1,23 +1,23 @@
-import * as path from 'path';
+import * as path from "path";
-import { runTests } from '@vscode/test-electron';
+import { runTests } from "@vscode/test-electron";
async function main() {
- try {
- // The folder containing the Extension Manifest package.json
- // Passed to `--extensionDevelopmentPath`
- const extensionDevelopmentPath = path.resolve(__dirname, '../../');
+ try {
+ // The folder containing the Extension Manifest package.json
+ // Passed to `--extensionDevelopmentPath`
+ const extensionDevelopmentPath = path.resolve(__dirname, "../../");
- // The path to test runner
- // Passed to --extensionTestsPath
- const extensionTestsPath = path.resolve(__dirname, './suite/index');
+ // The path to test runner
+ // Passed to --extensionTestsPath
+ const extensionTestsPath = path.resolve(__dirname, "./suite/index");
- // Download VS Code, unzip it and run the integration test
- await runTests({ extensionDevelopmentPath, extensionTestsPath });
- } catch (err) {
- console.error('Failed to run tests');
- process.exit(1);
- }
+ // Download VS Code, unzip it and run the integration test
+ await runTests({ extensionDevelopmentPath, extensionTestsPath });
+ } catch (err) {
+ console.error("Failed to run tests");
+ process.exit(1);
+ }
}
main();
diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts
new file mode 100644
index 00000000..6f8bb29d
--- /dev/null
+++ b/extension/src/util/messenger.ts
@@ -0,0 +1,108 @@
+console.log("Websocket import");
+const WebSocket = require("ws");
+
+export abstract class Messenger {
+ abstract send(messageType: string, data: object): void;
+
+ abstract onMessageType(
+ messageType: string,
+ callback: (data: object) => void
+ ): void;
+
+ abstract onMessage(callback: (messageType: string, data: any) => void): void;
+
+ abstract onOpen(callback: () => void): void;
+
+ abstract onClose(callback: () => void): void;
+
+ abstract sendAndReceive(messageType: string, data: any): Promise<any>;
+}
+
+export class WebsocketMessenger extends Messenger {
+ websocket: WebSocket;
+ private onMessageListeners: {
+ [messageType: string]: ((data: object) => void)[];
+ } = {};
+ private onOpenListeners: (() => void)[] = [];
+ private onCloseListeners: (() => void)[] = [];
+ private serverUrl: string;
+
+ _newWebsocket(): WebSocket {
+ // // Dynamic import, because WebSocket is builtin with browser, but not with node. And can't use require in browser.
+ // if (typeof process === "object") {
+ // console.log("Using node");
+ // // process is only available in Node
+ // var WebSocket = require("ws");
+ // }
+
+ const newWebsocket = new WebSocket(this.serverUrl);
+ for (const listener of this.onOpenListeners) {
+ this.onOpen(listener);
+ }
+ for (const listener of this.onCloseListeners) {
+ this.onClose(listener);
+ }
+ for (const messageType in this.onMessageListeners) {
+ for (const listener of this.onMessageListeners[messageType]) {
+ this.onMessageType(messageType, listener);
+ }
+ }
+ return newWebsocket;
+ }
+
+ constructor(serverUrl: string) {
+ super();
+ this.serverUrl = serverUrl;
+ this.websocket = this._newWebsocket();
+ }
+
+ send(messageType: string, data: object) {
+ const payload = JSON.stringify({ messageType, data });
+ if (this.websocket.readyState === this.websocket.OPEN) {
+ this.websocket.send(payload);
+ } else {
+ if (this.websocket.readyState !== this.websocket.CONNECTING) {
+ this.websocket = this._newWebsocket();
+ }
+ this.websocket.addEventListener("open", () => {
+ this.websocket.send(payload);
+ });
+ }
+ }
+
+ sendAndReceive(messageType: string, data: any): Promise<any> {
+ return new Promise((resolve, reject) => {
+ const eventListener = (data: any) => {
+ // THIS ISN"T GETTING CALLED
+ resolve(data);
+ this.websocket.removeEventListener("message", eventListener);
+ };
+ this.onMessageType(messageType, eventListener);
+ this.send(messageType, data);
+ });
+ }
+
+ onMessageType(messageType: string, callback: (data: any) => void): void {
+ this.websocket.addEventListener("message", (event: any) => {
+ const msg = JSON.parse(event.data);
+ if (msg.messageType === messageType) {
+ callback(msg.data);
+ }
+ });
+ }
+
+ onMessage(callback: (messageType: string, data: any) => void): void {
+ this.websocket.addEventListener("message", (event) => {
+ const msg = JSON.parse(event.data);
+ callback(msg.messageType, msg.data);
+ });
+ }
+
+ onOpen(callback: () => void): void {
+ this.websocket.addEventListener("open", callback);
+ }
+
+ onClose(callback: () => void): void {
+ this.websocket.addEventListener("close", callback);
+ }
+}