From f53768612b1e2268697b5444e502032ef9f3fb3c Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 23 May 2023 23:45:12 -0400 Subject: copying from old repo --- extension/src/README.md | 77 +++++ extension/src/activation/activate.ts | 71 ++++ extension/src/activation/environmentSetup.ts | 226 +++++++++++++ extension/src/activation/languageClient.ts | 115 +++++++ extension/src/bridge.ts | 292 +++++++++++++++++ extension/src/commands.ts | 223 +++++++++++++ extension/src/continueIdeClient.ts | 338 +++++++++++++++++++ extension/src/debugPanel.ts | 378 ++++++++++++++++++++++ extension/src/decorations.ts | 313 ++++++++++++++++++ extension/src/extension.ts | 37 +++ extension/src/lang-server/codeLens.ts | 99 ++++++ extension/src/languages/index.d.ts | 13 + extension/src/languages/index.ts | 19 ++ extension/src/languages/javascript/index.ts | 16 + extension/src/languages/notImplemented.ts | 10 + extension/src/languages/python/index.ts | 74 +++++ extension/src/suggestions.ts | 311 ++++++++++++++++++ extension/src/telemetry.ts | 51 +++ extension/src/terminal/snoopers.ts | 133 ++++++++ extension/src/terminal/terminalEmulator.ts | 140 ++++++++ extension/src/test/runTest.ts | 23 ++ extension/src/test/suite/extension.test.ts | 16 + extension/src/test/suite/index.ts | 38 +++ extension/src/test/suite/terminalEmulator.test.ts | 28 ++ extension/src/test/suite/util.test.ts | 18 ++ extension/src/util/util.ts | 115 +++++++ extension/src/util/vscode.ts | 152 +++++++++ 27 files changed, 3326 insertions(+) create mode 100644 extension/src/README.md create mode 100644 extension/src/activation/activate.ts create mode 100644 extension/src/activation/environmentSetup.ts create mode 100644 extension/src/activation/languageClient.ts create mode 100644 extension/src/bridge.ts create mode 100644 extension/src/commands.ts create mode 100644 extension/src/continueIdeClient.ts create mode 100644 extension/src/debugPanel.ts create mode 100644 extension/src/decorations.ts create mode 100644 extension/src/extension.ts create mode 100644 extension/src/lang-server/codeLens.ts create mode 100644 extension/src/languages/index.d.ts create mode 100644 extension/src/languages/index.ts create mode 100644 extension/src/languages/javascript/index.ts create mode 100644 extension/src/languages/notImplemented.ts create mode 100644 extension/src/languages/python/index.ts create mode 100644 extension/src/suggestions.ts create mode 100644 extension/src/telemetry.ts create mode 100644 extension/src/terminal/snoopers.ts create mode 100644 extension/src/terminal/terminalEmulator.ts create mode 100644 extension/src/test/runTest.ts create mode 100644 extension/src/test/suite/extension.test.ts create mode 100644 extension/src/test/suite/index.ts create mode 100644 extension/src/test/suite/terminalEmulator.test.ts create mode 100644 extension/src/test/suite/util.test.ts create mode 100644 extension/src/util/util.ts create mode 100644 extension/src/util/vscode.ts (limited to 'extension/src') diff --git a/extension/src/README.md b/extension/src/README.md new file mode 100644 index 00000000..bb10f5c8 --- /dev/null +++ b/extension/src/README.md @@ -0,0 +1,77 @@ +# Continue VS Code Extension README + +## How to get started with development + +1. Clone the `continue` repo + +2. Open a VS Code window with the `continue` repo + +3. Package and then start the FastAPI server by following instructions outlined in `package/server/README.md` + +4. Open the `extension` sub-directory of the repo in a second VS Code window + +5. Run `npm install` + +6. Run `npm run clientgen` + +7. Run `cd react-app` + +8. Run `npm run build` + +9. Run `cd ..` to return to `extension` directory + +10. Then run `npm run compile` + +7. Open `src/activate.ts` file (or any TypeScript file) + +7. Press `F5` on your keyboard to start `Run and Debug` mode + +8. `cmd+shift+p` to look at developer console and select Continue commands + +9. Every time you make changes to the code, you need to run `npm run compile` + +10. If you run into a "command not found" error, try running `npm run rebuild` and then `npm run compile` + +## Alternative: Install a packaged version + +You should always have a packaged version installed in VSCode, because when Continue is broken you'll want a stable version to help you debug. There are four key commands in the `package.json`: + +1. `npm run package` will create a .vsix file in the `build/` folder that can then be installed. It is this same file that you can share with others who want to try the extension. + +2. `npm run install-extension` will install the extension to VSCode. You should then see it in your installed extensions in the VSCode sidebar. + +3. `npm run uninstall` will uninstall the extension. You don't always have to do this thanks to the reinstall command, but can be useful when you want to do so manually. + +4. `npm run reinstall` will go through the entire process of uninstalling the existing installed extension, rebuilding, and then installing the new version. You shouldn't be doing this every time you make a change to the extension, but rather when there is some significant update that you would like to make available to yourself (or if you happen to be debugging something which is specific to the packaged extension). + +## Background + +- `src/bridge.ts`: connects this VS Code Extension to our Python backend that interacts with GPT-3 +- `src/debugPanel.ts`: contains the HTML for the full window on the right (used for investigation) +- `src/DebugViewProvider.ts`: contains the HTML for the bottom left panel +- `src/extension.ts`: entry point into the extension, where all of the commands / views are registered (activate function is what happens when you start extension) +- `media/main.js`: handles messages sent from the extension to the webview (bottom left) +- `media/debugPanel.js`: loaded by right window + +## Features + +- `List 10 things that might be wrong` button +- `Write a unit test to reproduce bug` button +- Highlight a code range + `Find Suspicious Code` button +- `Suggest Fix` button +- A fix suggestion shown to you + `Make Edit` button +- Write a docstring for the current function +- Ask a question about your codebase +- Move up / down to the closest suggestion + +## Commands + +- "Write a docstring for the current function" command (windows: `ctrl+alt+l`, mac: `shift+cmd+l`) +- "Open Debug Panel" command +- "Ask a question from input box" command (windows: `ctrl+alt+j`, mac: `shift+cmd+j`) +- "Open Captured Terminal" command +- "Ask a question from webview" command (what context is it given?) +- "Create Terminal" command ??? +- "Suggestion Down" command (windows: `shift+ctrl+down`, mac: `shift+ctrl+down`) +- "Suggestion Up" command (windows: `shift+ctrl+up`, mac: `shift+ctrl+up`) +- "Accept Suggestion" command (windows: `shift+ctrl+enter`, mac: `shift+ctrl+enter`) diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts new file mode 100644 index 00000000..a0aa560b --- /dev/null +++ b/extension/src/activation/activate.ts @@ -0,0 +1,71 @@ +import * as vscode from "vscode"; +import { registerAllCommands } from "../commands"; +import { registerAllCodeLensProviders } from "../lang-server/codeLens"; +import { sendTelemetryEvent, TelemetryEvent } from "../telemetry"; +import { getExtensionUri } from "../util/vscode"; +import * as path from "path"; +// import { openCapturedTerminal } from "../terminal/terminalEmulator"; +import IdeProtocolClient from "../continueIdeClient"; +import { getContinueServerUrl } from "../bridge"; + +export let extensionContext: vscode.ExtensionContext | undefined = undefined; + +export let ideProtocolClient: IdeProtocolClient | undefined = undefined; + +export function activateExtension( + context: vscode.ExtensionContext, + showTutorial: boolean +) { + sendTelemetryEvent(TelemetryEvent.ExtensionActivated); + + registerAllCodeLensProviders(context); + registerAllCommands(context); + + let serverUrl = getContinueServerUrl(); + + ideProtocolClient = new IdeProtocolClient( + serverUrl.replace("http", "ws") + "/ide/ws", + context + ); + + if (showTutorial && false) { + Promise.all([ + vscode.workspace + .openTextDocument( + path.join(getExtensionUri().fsPath, "examples/python/sum.py") + ) + .then((document) => + vscode.window.showTextDocument(document, { + preview: false, + viewColumn: vscode.ViewColumn.One, + }) + ), + + vscode.workspace + .openTextDocument( + path.join(getExtensionUri().fsPath, "examples/python/main.py") + ) + .then((document) => + vscode.window + .showTextDocument(document, { + preview: false, + viewColumn: vscode.ViewColumn.One, + }) + .then((editor) => { + editor.revealRange( + new vscode.Range(0, 0, 0, 0), + vscode.TextEditorRevealType.InCenter + ); + }) + ), + ]).then(() => { + ideProtocolClient?.openNotebook(); + }); + } else { + // ideProtocolClient?.openNotebook().then(() => { + // // openCapturedTerminal(); + // }); + } + + extensionContext = context; +} diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts new file mode 100644 index 00000000..4816b4b1 --- /dev/null +++ b/extension/src/activation/environmentSetup.ts @@ -0,0 +1,226 @@ +import { getExtensionUri } from "../util/vscode"; +const util = require("util"); +const exec = util.promisify(require("child_process").exec); +const { spawn } = require("child_process"); +import * as path from "path"; +import * as fs from "fs"; +import rebuild from "@electron/rebuild"; +import * as vscode from "vscode"; +import { getContinueServerUrl } from "../bridge"; +import fetch from "node-fetch"; + +async function runCommand(cmd: string): Promise<[string, string | undefined]> { + var stdout: any = ""; + var stderr: any = ""; + try { + var { stdout, stderr } = await exec(cmd); + } catch (e: any) { + stderr = e.stderr; + stdout = e.stdout; + } + if (stderr === "") { + stderr = undefined; + } + if (typeof stdout === "undefined") { + stdout = ""; + } + + 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 + + var [stdout, stderr] = await runCommand("python3 --version"); + let pythonCmd = "python3"; + if (stderr) { + // If not, first see if python3 is aliased to python + var [stdout, stderr] = await runCommand("python --version"); + if ( + (typeof stderr === "undefined" || stderr === "") && + stdout.split(" ")[1][0] === "3" + ) { + // Python3 is aliased to python + pythonCmd = "python"; + } else { + // Python doesn't exist at all + console.log("Python3 not found, downloading..."); + await downloadPython3(); + } + } + let pipCmd = pythonCmd.endsWith("3") ? "pip3" : "pip"; + + let activateCmd = "source env/bin/activate"; + let pipUpgradeCmd = `${pipCmd} install --upgrade pip`; + if (process.platform == "win32") { + activateCmd = ".\\env\\Scripts\\activate"; + pipUpgradeCmd = `${pythonCmd} -m pip install --upgrade pip`; + } + + let command = `cd ${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); + } + console.log( + "Successfully set up python env at ", + getExtensionUri().fsPath + "/scripts/env" + ); + + await startContinuePythonServer(); +} + +function readEnvFile(path: string) { + if (!fs.existsSync(path)) { + return {}; + } + let envFile = fs.readFileSync(path, "utf8"); + + let env: { [key: string]: string } = {}; + envFile.split("\n").forEach((line) => { + let [key, value] = line.split("="); + if (typeof key === "undefined" || typeof value === "undefined") { + return; + } + env[key] = value.replace(/"/g, ""); + }); + return env; +} + +function writeEnvFile(path: string, key: string, value: string) { + if (!fs.existsSync(path)) { + fs.writeFileSync(path, `${key}="${value}"`); + return; + } + + let env = readEnvFile(path); + env[key] = value; + + let newEnvFile = ""; + for (let key in env) { + newEnvFile += `${key}="${env[key]}"\n`; + } + fs.writeFileSync(path, newEnvFile); +} + +export async function startContinuePythonServer() { + // 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"); + if (response.status === 200) { + console.log("Continue python server already running"); + return; + } + } catch (e) { + console.log("Error checking for existing server", e); + } + + let activateCmd = "source env/bin/activate"; + let pythonCmd = "python3"; + if (process.platform == "win32") { + activateCmd = ".\\env\\Scripts\\activate"; + pythonCmd = "python"; + } + + let command = `cd ${path.join( + 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"); +} + +async function installNodeModules() { + console.log("Rebuilding node-pty for Continue extension..."); + await rebuild({ + buildPath: getExtensionUri().fsPath, // Folder containing node_modules + electronVersion: "19.1.8", + onlyModules: ["node-pty"], + }); + console.log("Successfully rebuilt node-pty"); +} + +export function isPythonEnvSetup(): boolean { + let pathToEnvCfg = getExtensionUri().fsPath + "/scripts/env/pyvenv.cfg"; + 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; + let command: string = ""; + let pythonCmd = "python3"; + if (os === "darwin") { + throw new Error("python3 not found"); + } else if (os === "linux") { + command = + "sudo apt update && upgrade && sudo apt install python3 python3-pip"; + } else if (os === "win32") { + command = + "wget -O python_installer.exe https://www.python.org/ftp/python/3.11.3/python-3.11.3-amd64.exe && python_installer.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0"; + pythonCmd = "python"; + } + + var [stdout, stderr] = await runCommand(command); + if (stderr) { + throw new Error(stderr); + } + console.log("Successfully downloaded python3"); + + return pythonCmd; +} diff --git a/extension/src/activation/languageClient.ts b/extension/src/activation/languageClient.ts new file mode 100644 index 00000000..5b0bd612 --- /dev/null +++ b/extension/src/activation/languageClient.ts @@ -0,0 +1,115 @@ +/** + * If we wanted to run or use another language server from our extension, this is how we would do it. + */ + +import * as path from "path"; +import { workspace, ExtensionContext, extensions } from "vscode"; + +import { + DefinitionParams, + LanguageClient, + LanguageClientOptions, + ServerOptions, + StateChangeEvent, + TransportKind, + State, +} from "vscode-languageclient/node"; +import { getExtensionUri } from "../util/vscode"; + +let client: LanguageClient; + +export async function startLanguageClient(context: ExtensionContext) { + let pythonLS = startPythonLanguageServer(context); + pythonLS.start(); +} + +export async function makeRequest(method: string, param: any): Promise { + if (!client) { + return; + } else if (client.state === State.Starting) { + return new Promise((resolve, reject) => { + let stateListener = client.onDidChangeState((e: StateChangeEvent) => { + if (e.newState === State.Running) { + stateListener.dispose(); + resolve(client.sendRequest(method, param)); + } else if (e.newState === State.Stopped) { + stateListener.dispose(); + reject(new Error("Language server stopped unexpectedly")); + } + }); + }); + } else { + return client.sendRequest(method, param); + } +} + +export function deactivate(): Thenable | undefined { + if (!client) { + return undefined; + } + return client.stop(); +} + +function startPythonLanguageServer(context: ExtensionContext): LanguageClient { + let extensionPath = getExtensionUri().fsPath; + const command = `cd ${path.join( + extensionPath, + "scripts" + )} && source env/bin/activate.fish && python -m pyls`; + const serverOptions: ServerOptions = { + command: command, + args: ["-vv"], + }; + const clientOptions: LanguageClientOptions = { + documentSelector: ["python"], + synchronize: { + configurationSection: "pyls", + }, + }; + return new LanguageClient(command, serverOptions, clientOptions); +} + +async function startPylance(context: ExtensionContext) { + let pylance = extensions.getExtension("ms-python.vscode-pylance"); + await pylance?.activate(); + if (!pylance) { + return; + } + let { path: lsPath } = await pylance.exports.languageServerFolder(); + + // The server is implemented in node + let serverModule = context.asAbsolutePath(lsPath); + // The debug options for the server + // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging + let debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + let serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { + module: serverModule, + transport: TransportKind.ipc, + options: debugOptions, + }, + }; + + // Options to control the language client + let clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{ scheme: "file", language: "python" }], + synchronize: { + // Notify the server about file changes to '.clientrc files contained in the workspace + fileEvents: workspace.createFileSystemWatcher("**/.clientrc"), + }, + }; + + // Create the language client and start the client. + client = new LanguageClient( + "languageServerExample", + "Language Server Example", + serverOptions, + clientOptions + ); + return client; +} diff --git a/extension/src/bridge.ts b/extension/src/bridge.ts new file mode 100644 index 00000000..3e21a205 --- /dev/null +++ b/extension/src/bridge.ts @@ -0,0 +1,292 @@ +import fetch from "node-fetch"; +import * as path from "path"; +import * as vscode from "vscode"; +import { + Configuration, + DebugApi, + RangeInFile, + SerializedDebugContext, + UnittestApi, +} from "./client"; +import { convertSingleToDoubleQuoteJSON } from "./util/util"; +import { getExtensionUri } from "./util/vscode"; +const axios = require("axios").default; +const util = require("util"); +const exec = util.promisify(require("child_process").exec); + +const configuration = new Configuration({ + basePath: get_api_url(), + fetchApi: fetch as any, + middleware: [ + { + pre: async (context) => { + // If there is a SerializedDebugContext in the body, add the files for the filesystem + context.init.body; + + // Add the VS Code Machine Code Header + context.init.headers = { + ...context.init.headers, + "x-vsc-machine-id": vscode.env.machineId, + }; + }, + }, + ], +}); +export const debugApi = new DebugApi(configuration); +export const unittestApi = new UnittestApi(configuration); + +function get_python_path() { + return path.join(getExtensionUri().fsPath, ".."); +} + +export function get_api_url() { + let extensionUri = getExtensionUri(); + let configFile = path.join(extensionUri.fsPath, "config/config.json"); + let config = require(configFile); + + if (config.API_URL) { + return config.API_URL; + } + return "http://localhost:8000"; +} +const API_URL = get_api_url(); + +export function getContinueServerUrl() { + return ( + vscode.workspace.getConfiguration("continue").get("serverUrl") || + "http://localhost:8000" + ); +} + +function build_python_command(cmd: string): string { + return `cd ${get_python_path()} && source env/bin/activate && ${cmd}`; +} + +function listToCmdLineArgs(list: string[]): string { + return list.map((el) => `"$(echo "${el}")"`).join(" "); +} + +export async function runPythonScript( + scriptName: string, + args: string[] +): Promise { + // TODO: Need to make sure that the path to poetry is in the PATH and that it is installed in the first place. Realistically also need to install npm in some cases. + const command = `export PATH="$PATH:/opt/homebrew/bin" && cd ${path.join( + getExtensionUri().fsPath, + "scripts" + )} && source env/bin/activate && python3 ${scriptName} ${listToCmdLineArgs( + args + )}`; + + const { stdout, stderr } = await exec(command); + + try { + let jsonString = stdout.substring( + stdout.indexOf("{"), + stdout.lastIndexOf("}") + 1 + ); + jsonString = convertSingleToDoubleQuoteJSON(jsonString); + return JSON.parse(jsonString); + } catch (e) { + if (stderr) { + throw new Error(stderr); + } else { + throw new Error("Failed to parse JSON: " + e); + } + } +} + +function parseStdout( + stdout: string, + key: string, + until_end: boolean = false +): string { + const prompt = `${key}=`; + let lines = stdout.split("\n"); + + let value: string = ""; + for (let i = 0; i < lines.length; i++) { + if (lines[i].startsWith(prompt)) { + if (until_end) { + return lines.slice(i).join("\n").substring(prompt.length); + } else { + return lines[i].substring(prompt.length); + } + } + } + return ""; +} + +export async function askQuestion( + question: string, + workspacePath: string +): Promise<{ answer: string; range: vscode.Range; filename: string }> { + const command = build_python_command( + `python3 ${path.join( + get_python_path(), + "ask.py" + )} ask ${workspacePath} "${question}"` + ); + + const { stdout, stderr } = await exec(command); + if (stderr) { + throw new Error(stderr); + } + // Use the output + const answer = parseStdout(stdout, "Answer"); + const filename = parseStdout(stdout, "Filename"); + const startLineno = parseInt(parseStdout(stdout, "Start lineno")); + const endLineno = parseInt(parseStdout(stdout, "End lineno")); + const range = new vscode.Range( + new vscode.Position(startLineno, 0), + new vscode.Position(endLineno, 0) + ); + if (answer && filename && startLineno && endLineno) { + return { answer, filename, range }; + } else { + throw new Error("Error: No answer found"); + } +} + +export async function apiRequest( + endpoint: string, + options: { + method?: string; + query?: { [key: string]: any }; + body?: { [key: string]: any }; + } +): Promise { + let defaults = { + method: "GET", + query: {}, + body: {}, + }; + options = Object.assign(defaults, options); // Second takes over first + if (endpoint.startsWith("/")) endpoint = endpoint.substring(1); + console.log("API request: ", options.body); + + let resp; + try { + resp = await axios({ + method: options.method, + url: `${API_URL}/${endpoint}`, + data: options.body, + params: options.query, + headers: { + "x-vsc-machine-id": vscode.env.machineId, + }, + }); + } catch (err) { + console.log("Error: ", err); + throw err; + } + + return resp.data; +} + +// Write a docstring for the most specific function or class at the current line in the given file +export async function writeDocstringForFunction( + filename: string, + position: vscode.Position +): Promise<{ lineno: number; docstring: string }> { + let resp = await apiRequest("docstring/forline", { + query: { + filecontents: ( + await vscode.workspace.fs.readFile(vscode.Uri.file(filename)) + ).toString(), + lineno: position.line.toString(), + }, + }); + + const lineno = resp.lineno; + const docstring = resp.completion; + if (lineno && docstring) { + return { lineno, docstring }; + } else { + throw new Error("Error: No docstring returned"); + } +} + +export async function findSuspiciousCode( + ctx: SerializedDebugContext +): Promise { + if (!ctx.traceback) return []; + let files = await getFileContents( + getFilenamesFromPythonStacktrace(ctx.traceback) + ); + let resp = await debugApi.findSusCodeEndpointDebugFindPost({ + findBody: { + traceback: ctx.traceback, + description: ctx.description, + filesystem: files, + }, + }); + let ranges = resp.response; + if ( + ranges.length <= 1 && + ctx.traceback && + ctx.traceback.includes("AssertionError") + ) { + let parsed_traceback = + await debugApi.parseTracebackEndpointDebugParseTracebackGet({ + traceback: ctx.traceback, + }); + let last_frame = parsed_traceback.frames[0]; + if (!last_frame) return []; + ranges = ( + await runPythonScript("build_call_graph.py", [ + last_frame.filepath, + last_frame.lineno.toString(), + last_frame._function, + ]) + ).value; + } + + return ranges; +} + +export async function writeUnitTestForFunction( + filename: string, + position: vscode.Position +): Promise { + let resp = await apiRequest("unittest/forline", { + method: "POST", + body: { + filecontents: ( + await vscode.workspace.fs.readFile(vscode.Uri.file(filename)) + ).toString(), + lineno: position.line, + userid: vscode.env.machineId, + }, + }); + + return resp.completion; +} + +async function getFileContents( + files: string[] +): Promise<{ [key: string]: string }> { + let contents = await Promise.all( + files.map(async (file: string) => { + return ( + await vscode.workspace.fs.readFile(vscode.Uri.file(file)) + ).toString(); + }) + ); + let fileContents: { [key: string]: string } = {}; + for (let i = 0; i < files.length; i++) { + fileContents[files[i]] = contents[i]; + } + return fileContents; +} + +function getFilenamesFromPythonStacktrace(traceback: string): string[] { + let filenames: string[] = []; + for (let line of traceback.split("\n")) { + let match = line.match(/File "(.*)", line/); + if (match) { + filenames.push(match[1]); + } + } + return filenames; +} diff --git a/extension/src/commands.ts b/extension/src/commands.ts new file mode 100644 index 00000000..18f08e31 --- /dev/null +++ b/extension/src/commands.ts @@ -0,0 +1,223 @@ +import * as vscode from "vscode"; +import { + decorationManager, + showAnswerInTextEditor, + showGutterSpinner, + writeAndShowUnitTest, +} from "./decorations"; +import { + acceptSuggestionCommand, + rejectSuggestionCommand, + suggestionDownCommand, + suggestionUpCommand, +} from "./suggestions"; +import * as bridge from "./bridge"; +import { debugPanelWebview, setupDebugPanel } from "./debugPanel"; +// import { openCapturedTerminal } from "./terminal/terminalEmulator"; +import { getRightViewColumn } from "./util/vscode"; +import { + findSuspiciousCode, + runPythonScript, + writeUnitTestForFunction, +} from "./bridge"; +import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; +import { getLanguageLibrary } from "./languages"; +import { SerializedDebugContext } from "./client"; +import { addFileSystemToDebugContext } from "./util/util"; +import { ideProtocolClient } from "./activation/activate"; + +// Copy everything over from extension.ts +const commandsMap: { [command: string]: (...args: any) => any } = { + "continue.askQuestion": (data: any, webviewView: vscode.WebviewView) => { + if (!vscode.workspace.workspaceFolders) { + return; + } + + answerQuestion( + data.question, + vscode.workspace.workspaceFolders[0].uri.fsPath, + webviewView.webview + ); + }, + "continue.askQuestionFromInput": () => { + vscode.window + .showInputBox({ placeHolder: "Ask away!" }) + .then((question) => { + if (!question || !vscode.workspace.workspaceFolders) { + return; + } + + sendTelemetryEvent(TelemetryEvent.UniversalPromptQuery, { + query: question, + }); + + answerQuestion( + question, + vscode.workspace.workspaceFolders[0].uri.fsPath + ); + }); + }, + "continue.suggestionDown": suggestionDownCommand, + "continue.suggestionUp": suggestionUpCommand, + "continue.acceptSuggestion": acceptSuggestionCommand, + "continue.rejectSuggestion": rejectSuggestionCommand, + "continue.openDebugPanel": () => { + ideProtocolClient?.openNotebook(); + }, + "continue.focusContinueInput": async () => { + if (!debugPanelWebview) { + await ideProtocolClient?.openNotebook(); + } + debugPanelWebview?.postMessage({ + type: "focusContinueInput", + }); + }, + "continue.openCapturedTerminal": () => { + // Happens in webview resolution function + // openCapturedTerminal(); + }, + "continue.findSuspiciousCode": async ( + debugContext: SerializedDebugContext + ) => { + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Finding suspicious code", + cancellable: false, + }, + async (progress, token) => { + let suspiciousCode = await findSuspiciousCode(debugContext); + debugContext.rangesInFiles = suspiciousCode; + let { filesystem } = addFileSystemToDebugContext(debugContext); + debugPanelWebview?.postMessage({ + type: "findSuspiciousCode", + codeLocations: suspiciousCode, + filesystem, + }); + } + ); + }, + "continue.debugTest": async (fileAndFunctionSpecifier: string) => { + sendTelemetryEvent(TelemetryEvent.AutoDebugThisTest); + let editor = vscode.window.activeTextEditor; + if (editor) editor.document.save(); + let { stdout } = await runPythonScript("run_unit_test.py", [ + fileAndFunctionSpecifier, + ]); + let traceback = getLanguageLibrary( + fileAndFunctionSpecifier.split("::")[0] + ).parseFirstStacktrace(stdout); + if (!traceback) { + vscode.window.showInformationMessage("The test passes!"); + return; + } + vscode.commands.executeCommand("continue.openDebugPanel").then(() => { + setTimeout(() => { + debugPanelWebview?.postMessage({ + type: "traceback", + value: traceback, + }); + }, 500); + }); + }, +}; + +const textEditorCommandsMap: { [command: string]: (...args: any) => {} } = { + "continue.writeUnitTest": async (editor: vscode.TextEditor) => { + let position = editor.selection.active; + + let gutterSpinnerKey = showGutterSpinner(editor, position.line); + try { + let test = await writeUnitTestForFunction( + editor.document.fileName, + position + ); + writeAndShowUnitTest(editor.document.fileName, test); + } catch { + } finally { + decorationManager.deleteDecoration(gutterSpinnerKey); + } + }, + "continue.writeDocstring": async (editor: vscode.TextEditor, _) => { + sendTelemetryEvent(TelemetryEvent.GenerateDocstring); + let gutterSpinnerKey = showGutterSpinner( + editor, + editor.selection.active.line + ); + + const { lineno, docstring } = await bridge.writeDocstringForFunction( + editor.document.fileName, + editor.selection.active + ); + // Can't use the edit given above after an async call + editor.edit((edit) => { + edit.insert(new vscode.Position(lineno, 0), docstring); + decorationManager.deleteDecoration(gutterSpinnerKey); + }); + }, +}; + +export function registerAllCommands(context: vscode.ExtensionContext) { + for (const [command, callback] of Object.entries(commandsMap)) { + context.subscriptions.push( + vscode.commands.registerCommand(command, callback) + ); + } + + for (const [command, callback] of Object.entries(textEditorCommandsMap)) { + context.subscriptions.push( + vscode.commands.registerTextEditorCommand(command, callback) + ); + } +} + +async function answerQuestion( + question: string, + workspacePath: string, + webview: vscode.Webview | undefined = undefined +) { + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Anwering question...", + cancellable: false, + }, + async (progress, token) => { + try { + let resp = await bridge.askQuestion(question, workspacePath); + // Send the answer back to the webview + if (webview) { + webview.postMessage({ + type: "answerQuestion", + answer: resp.answer, + }); + } + showAnswerInTextEditor(resp.filename, resp.range, resp.answer); + } catch (error: any) { + if (webview) { + webview.postMessage({ + type: "answerQuestion", + answer: error, + }); + } + } + } + ); +} + +// async function suggestFixForAllWorkspaceProblems() { +// Something like this, just figure out the loops for diagnostics vs problems +// let problems = vscode.languages.getDiagnostics(); +// let codeSuggestions = await Promise.all(problems.map((problem) => { +// return bridge.suggestFixForProblem(problem[0].fsPath, problem[1]); +// })); +// for (const [uri, diagnostics] of problems) { +// for (let i = 0; i < diagnostics.length; i++) { +// let diagnostic = diagnostics[i]; +// let suggestedCode = codeSuggestions[i]; +// // If you're going to do this for a bunch of files at once, it will show the unsaved icon in the tab +// // BUT it would be better to have a single window to review all edits +// showSuggestion(uri.fsPath, diagnostic.range, suggestedCode) +// } +// } +// } diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts new file mode 100644 index 00000000..6c65415f --- /dev/null +++ b/extension/src/continueIdeClient.ts @@ -0,0 +1,338 @@ +// import { ShowSuggestionRequest } from "../schema/ShowSuggestionRequest"; +import { showSuggestion, SuggestionRanges } from "./suggestions"; +import { openEditorAndRevealRange, getRightViewColumn } from "./util/vscode"; +import { FileEdit } from "../schema/FileEdit"; +import { RangeInFile } from "../schema/RangeInFile"; +import * as vscode from "vscode"; +import { + acceptSuggestionCommand, + rejectSuggestionCommand, +} 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"); + +class IdeProtocolClient { + private _ws: WebSocket | null = null; + private _panels: Map = new Map(); + private readonly _serverUrl: string; + 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)); + }); + // Setup listeners for any file changes in open editors + vscode.workspace.onDidChangeTextDocument((event) => { + if (this._makingEdit === 0) { + let fileEdits: FileEditWithFullContents[] = event.contentChanges.map( + (change) => { + return { + fileEdit: { + filepath: event.document.uri.fsPath, + range: { + start: { + line: change.range.start.line, + character: change.range.start.character, + }, + end: { + line: change.range.end.line, + character: change.range.end.character, + }, + }, + replacement: change.text, + }, + fileContents: event.document.getText(), + }; + } + ); + this.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 { + 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 { + 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) { + case "highlightedCode": + this.send("highlightedCode", { + highlightedCode: this.getHighlightedCode(), + }); + break; + case "workspaceDirectory": + this.send("workspaceDirectory", { + workspaceDirectory: this.getWorkspaceDirectory(), + }); + case "openFiles": + this.send("openFiles", { + openFiles: this.getOpenFiles(), + }); + break; + case "readFile": + this.send("readFile", { + contents: this.readFile(message.filepath), + }); + break; + case "editFile": + let fileEdit = await this.editFile(message.edit); + this.send("editFile", { + fileEdit, + }); + break; + case "saveFile": + this.saveFile(message.filepath); + break; + case "setFileOpen": + this.openFile(message.filepath); + // TODO: Close file + break; + case "openNotebook": + case "connected": + break; + default: + throw Error("Unknown message type:" + message.messageType); + } + } + getWorkspaceDirectory() { + return vscode.workspace.workspaceFolders![0].uri.fsPath; + } + + // ------------------------------------ // + // On message handlers + + showSuggestion(edit: FileEdit) { + // showSuggestion already exists + showSuggestion( + edit.filepath, + new vscode.Range( + edit.range.start.line, + edit.range.start.character, + edit.range.end.line, + edit.range.end.character + ), + edit.replacement + ); + } + + openFile(filepath: string) { + // vscode has a builtin open/get open files + openEditorAndRevealRange(filepath, undefined, vscode.ViewColumn.One); + } + + // ------------------------------------ // + // Initiate Request + + closeNotebook(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; + console.log("SESSION ID", sessionId); + + let column = getRightViewColumn(); + const panel = vscode.window.createWebviewPanel( + "continue.debugPanelView", + "Continue", + column, + { + enableScripts: true, + retainContextWhenHidden: true, + } + ); + + // And set its HTML content + panel.webview.html = setupDebugPanel(panel, this._context, sessionId); + + this._panels.set(sessionId, panel); + } + + acceptRejectSuggestion(accept: boolean, key: SuggestionRanges) { + if (accept) { + acceptSuggestionCommand(key); + } else { + rejectSuggestionCommand(key); + } + } + + // ------------------------------------ // + // Respond to request + + getOpenFiles(): string[] { + return vscode.window.visibleTextEditors + .filter((editor) => { + return !( + editor.document.uri.fsPath.endsWith("/1") || + (editor.document.languageId === "plaintext" && + editor.document.getText() === + "accessible-buffer-accessible-buffer-") + ); + }) + .map((editor) => { + return editor.document.uri.fsPath; + }); + } + + saveFile(filepath: string) { + vscode.window.visibleTextEditors.forEach((editor) => { + if (editor.document.uri.fsPath === filepath) { + editor.document.save(); + } + }); + } + + readFile(filepath: string): string { + let contents: string | undefined; + vscode.window.visibleTextEditors.forEach((editor) => { + if (editor.document.uri.fsPath === filepath) { + contents = editor.document.getText(); + } + }); + if (!contents) { + contents = fs.readFileSync(filepath, "utf-8"); + } + return contents; + } + + editFile(edit: FileEdit): Promise { + return new Promise((resolve, reject) => { + openEditorAndRevealRange( + edit.filepath, + undefined, + vscode.ViewColumn.One + ).then((editor) => { + let range = new vscode.Range( + edit.range.start.line, + edit.range.start.character, + edit.range.end.line, + edit.range.end.character + 1 + ); + editor.edit((editBuilder) => { + this._makingEdit += 2; // editBuilder.replace takes 2 edits: delete and insert + editBuilder.replace(range, edit.replacement); + resolve({ + fileEdit: edit, + fileContents: editor.document.getText(), + }); + }); + }); + }); + } + + getHighlightedCode(): RangeInFile[] { + // TODO + let rangeInFiles: RangeInFile[] = []; + vscode.window.visibleTextEditors.forEach((editor) => { + editor.selections.forEach((selection) => { + if (!selection.isEmpty) { + rangeInFiles.push({ + filepath: editor.document.uri.fsPath, + range: { + start: { + line: selection.start.line, + character: selection.start.character, + }, + end: { + line: selection.end.line, + character: selection.end.character, + }, + }, + }); + } + }); + }); + return rangeInFiles; + } + + runCommand(command: string) { + vscode.window.terminals[0].sendText(command, true); + // But need to know when it's done executing... + } +} + +export default IdeProtocolClient; diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts new file mode 100644 index 00000000..66829836 --- /dev/null +++ b/extension/src/debugPanel.ts @@ -0,0 +1,378 @@ +import * as vscode from "vscode"; +import { + debugApi, + getContinueServerUrl, + runPythonScript, + unittestApi, +} from "./bridge"; +import { writeAndShowUnitTest } from "./decorations"; +import { showSuggestion } from "./suggestions"; +import { getLanguageLibrary } from "./languages"; +import { + getExtensionUri, + getNonce, + openEditorAndRevealRange, +} from "./util/vscode"; +import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; +import { RangeInFile, SerializedDebugContext } from "./client"; +import { addFileSystemToDebugContext } from "./util/util"; + +class StreamManager { + private _fullText: string = ""; + private _insertionPoint: vscode.Position | undefined; + + private _addToEditor(update: string) { + let editor = + vscode.window.activeTextEditor || vscode.window.visibleTextEditors[0]; + + if (typeof this._insertionPoint === "undefined") { + if (editor?.selection.isEmpty) { + this._insertionPoint = editor?.selection.active; + } else { + this._insertionPoint = editor?.selection.end; + } + } + editor?.edit((editBuilder) => { + if (this._insertionPoint) { + editBuilder.insert(this._insertionPoint, update); + this._insertionPoint = this._insertionPoint.translate( + Array.from(update.matchAll(/\n/g)).length, + update.length + ); + } + }); + } + + public closeStream() { + this._fullText = ""; + this._insertionPoint = undefined; + this._codeBlockStatus = "closed"; + this._pendingBackticks = 0; + } + + private _codeBlockStatus: "open" | "closed" | "language-descriptor" = + "closed"; + private _pendingBackticks: number = 0; + public onStreamUpdate(update: string) { + let textToInsert = ""; + for (let i = 0; i < update.length; i++) { + switch (this._codeBlockStatus) { + case "closed": + if (update[i] === "`" && this._fullText.endsWith("``")) { + this._codeBlockStatus = "language-descriptor"; + } + break; + case "language-descriptor": + if (update[i] === " " || update[i] === "\n") { + this._codeBlockStatus = "open"; + } + break; + case "open": + if (update[i] === "`") { + if (this._fullText.endsWith("``")) { + this._codeBlockStatus = "closed"; + this._pendingBackticks = 0; + } else { + this._pendingBackticks += 1; + } + } else { + textToInsert += "`".repeat(this._pendingBackticks) + update[i]; + this._pendingBackticks = 0; + } + break; + } + this._fullText += update[i]; + } + this._addToEditor(textToInsert); + } +} + +let streamManager = new StreamManager(); + +export let debugPanelWebview: vscode.Webview | undefined; +export function setupDebugPanel( + panel: vscode.WebviewPanel, + context: vscode.ExtensionContext | undefined, + sessionId: string +): string { + debugPanelWebview = panel.webview; + panel.onDidDispose(() => { + debugPanelWebview = undefined; + }); + + let extensionUri = getExtensionUri(); + let scriptUri: string; + let styleMainUri: string; + + const isProduction = true; // context?.extensionMode === vscode.ExtensionMode.Development; + if (!isProduction) { + scriptUri = "http://localhost:5173/src/main.tsx"; + styleMainUri = "http://localhost:5173/src/main.css"; + } else { + scriptUri = debugPanelWebview + .asWebviewUri( + vscode.Uri.joinPath(extensionUri, "react-app/dist/assets/index.js") + ) + .toString(); + styleMainUri = debugPanelWebview + .asWebviewUri( + vscode.Uri.joinPath(extensionUri, "react-app/dist/assets/index.css") + ) + .toString(); + } + + const nonce = getNonce(); + + vscode.window.onDidChangeTextEditorSelection((e) => { + if (e.selections[0].isEmpty) { + return; + } + + let rangeInFile: RangeInFile = { + range: e.selections[0], + filepath: e.textEditor.document.fileName, + }; + let filesystem = { + [rangeInFile.filepath]: e.textEditor.document.getText(), + }; + panel.webview.postMessage({ + type: "highlightedCode", + rangeInFile, + filesystem, + }); + + panel.webview.postMessage({ + type: "workspacePath", + value: vscode.workspace.workspaceFolders?.[0].uri.fsPath, + }); + }); + + panel.webview.onDidReceiveMessage(async (data) => { + switch (data.type) { + case "onLoad": { + panel.webview.postMessage({ + type: "onLoad", + vscMachineId: vscode.env.machineId, + apiUrl: getContinueServerUrl(), + sessionId, + }); + break; + } + case "listTenThings": { + sendTelemetryEvent(TelemetryEvent.GenerateIdeas); + let resp = await debugApi.listtenDebugListPost({ + serializedDebugContext: data.debugContext, + }); + panel.webview.postMessage({ + type: "listTenThings", + value: resp.completion, + }); + break; + } + case "suggestFix": { + let completion: string; + let codeSelection = data.debugContext.rangesInFiles?.at(0); + if (codeSelection) { + completion = ( + await debugApi.inlineDebugInlinePost({ + inlineBody: { + filecontents: await vscode.workspace.fs + .readFile(vscode.Uri.file(codeSelection.filepath)) + .toString(), + startline: codeSelection.range.start.line, + endline: codeSelection.range.end.line, + traceback: data.debugContext.traceback, + }, + }) + ).completion; + } else if (data.debugContext.traceback) { + completion = ( + await debugApi.suggestionDebugSuggestionGet({ + traceback: data.debugContext.traceback, + }) + ).completion; + } else { + break; + } + panel.webview.postMessage({ + type: "suggestFix", + value: completion, + }); + break; + } + case "findSuspiciousCode": { + let traceback = getLanguageLibrary(".py").parseFirstStacktrace( + data.debugContext.traceback + ); + if (traceback === undefined) return; + vscode.commands.executeCommand( + "continue.findSuspiciousCode", + data.debugContext + ); + break; + } + case "queryEmbeddings": { + let { results } = await runPythonScript("index.py query", [ + data.query, + 2, + vscode.workspace.workspaceFolders?.[0].uri.fsPath, + ]); + panel.webview.postMessage({ + type: "queryEmbeddings", + results, + }); + break; + } + case "openFile": { + openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One); + break; + } + case "streamUpdate": { + // Write code at the position of the cursor + streamManager.onStreamUpdate(data.update); + break; + } + case "closeStream": { + streamManager.closeStream(); + break; + } + case "explainCode": { + sendTelemetryEvent(TelemetryEvent.ExplainCode); + let debugContext: SerializedDebugContext = addFileSystemToDebugContext( + data.debugContext + ); + let resp = await debugApi.explainDebugExplainPost({ + serializedDebugContext: debugContext, + }); + panel.webview.postMessage({ + type: "explainCode", + value: resp.completion, + }); + break; + } + case "withProgress": { + // This message allows withProgress to be used in the webview + if (data.done) { + // Will be caught in the listener created below + break; + } + let title = data.title; + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title, + cancellable: false, + }, + async () => { + return new Promise((resolve, reject) => { + let listener = panel.webview.onDidReceiveMessage(async (data) => { + if ( + data.type === "withProgress" && + data.done && + data.title === title + ) { + listener.dispose(); + resolve(); + } + }); + }); + } + ); + break; + } + case "makeEdit": { + sendTelemetryEvent(TelemetryEvent.SuggestFix); + let suggestedEdits = data.edits; + + if ( + typeof suggestedEdits === "undefined" || + suggestedEdits.length === 0 + ) { + vscode.window.showInformationMessage( + "Continue couldn't find a fix for this error." + ); + return; + } + + for (let i = 0; i < suggestedEdits.length; i++) { + let edit = suggestedEdits[i]; + await showSuggestion( + edit.filepath, + new vscode.Range( + edit.range.start.line, + edit.range.start.character, + edit.range.end.line, + edit.range.end.character + ), + edit.replacement + ); + } + break; + } + case "generateUnitTest": { + sendTelemetryEvent(TelemetryEvent.CreateTest); + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Generating Unit Test...", + cancellable: false, + }, + async () => { + for (let i = 0; i < data.debugContext.rangesInFiles?.length; i++) { + let codeSelection = data.debugContext.rangesInFiles?.at(i); + if ( + codeSelection && + codeSelection.filepath && + codeSelection.range + ) { + try { + let filecontents = ( + await vscode.workspace.fs.readFile( + vscode.Uri.file(codeSelection.filepath) + ) + ).toString(); + let resp = + await unittestApi.failingtestUnittestFailingtestPost({ + failingTestBody: { + fp: { + filecontents, + lineno: codeSelection.range.end.line, + }, + description: data.debugContext.description || "", + }, + }); + + if (resp.completion) { + let decorationKey = await writeAndShowUnitTest( + codeSelection.filepath, + resp.completion + ); + break; + } + } catch {} + } + } + } + ); + + break; + } + } + }); + + return ` + + + + + + + + Continue + + +
+ + + `; +} diff --git a/extension/src/decorations.ts b/extension/src/decorations.ts new file mode 100644 index 00000000..456f0c10 --- /dev/null +++ b/extension/src/decorations.ts @@ -0,0 +1,313 @@ +import * as vscode from "vscode"; +import { getRightViewColumn, getTestFile } from "./util/vscode"; +import * as path from "path"; +import { getLanguageLibrary } from "./languages"; + +export function showAnswerInTextEditor( + filename: string, + range: vscode.Range, + answer: string +) { + vscode.workspace.openTextDocument(vscode.Uri.file(filename)).then((doc) => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + + // Open file, reveal range, show decoration + vscode.window.showTextDocument(doc).then((new_editor) => { + new_editor.revealRange( + new vscode.Range(range.end, range.end), + vscode.TextEditorRevealType.InCenter + ); + + let decorationType = vscode.window.createTextEditorDecorationType({ + after: { + contentText: answer + "\n", + color: "rgb(0, 255, 0, 0.8)", + }, + backgroundColor: "rgb(0, 255, 0, 0.2)", + }); + new_editor.setDecorations(decorationType, [range]); + vscode.window.showInformationMessage("Answer found!"); + + // Remove decoration when user moves cursor + vscode.window.onDidChangeTextEditorSelection((e) => { + if ( + e.textEditor === new_editor && + e.selections[0].active.line !== range.end.line + ) { + new_editor.setDecorations(decorationType, []); + } + }); + }); + }); +} + +type DecorationKey = { + editorUri: string; + options: vscode.DecorationOptions; + decorationType: vscode.TextEditorDecorationType; +}; + +class DecorationManager { + private editorToDecorations = new Map< + string, + Map + >(); + + constructor() { + vscode.window.onDidChangeVisibleTextEditors((editors) => { + for (const editor of editors) { + if (editor.document.isClosed) { + this.editorToDecorations.delete(editor.document.uri.toString()); + } + } + }); + } + + private rerenderDecorations( + editorUri: string, + decorationType: vscode.TextEditorDecorationType + ) { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + + const decorationTypes = this.editorToDecorations.get(editorUri); + if (!decorationTypes) { + return; + } + + const decorations = decorationTypes.get(decorationType); + if (!decorations) { + return; + } + + editor.setDecorations(decorationType, decorations); + } + + addDecoration(key: DecorationKey) { + let decorationTypes = this.editorToDecorations.get(key.editorUri); + if (!decorationTypes) { + decorationTypes = new Map(); + decorationTypes.set(key.decorationType, [key.options]); + this.editorToDecorations.set(key.editorUri, decorationTypes); + } + + const decorations = decorationTypes.get(key.decorationType); + if (!decorations) { + decorationTypes.set(key.decorationType, [key.options]); + } else { + decorations.push(key.options); + } + this.rerenderDecorations(key.editorUri, key.decorationType); + } + + deleteDecoration(key: DecorationKey) { + let decorationTypes = this.editorToDecorations.get(key.editorUri); + if (!decorationTypes) { + return; + } + + let decorations = decorationTypes?.get(key.decorationType); + if (!decorations) { + return; + } + + decorations = decorations.filter((decOpts) => decOpts !== key.options); + decorationTypes.set(key.decorationType, decorations); + this.rerenderDecorations(key.editorUri, key.decorationType); + } + + deleteAllDecorations(editorUri: string) { + let decorationTypes = this.editorToDecorations.get(editorUri)?.keys(); + if (!decorationTypes) { + return; + } + this.editorToDecorations.delete(editorUri); + for (let decorationType of decorationTypes) { + this.rerenderDecorations(editorUri, decorationType); + } + } +} + +export const decorationManager = new DecorationManager(); + +function constructBaseKey( + editor: vscode.TextEditor, + lineno: number, + decorationType?: vscode.TextEditorDecorationType +): DecorationKey { + return { + editorUri: editor.document.uri.toString(), + options: { + range: new vscode.Range(lineno, 0, lineno, 0), + }, + decorationType: + decorationType || vscode.window.createTextEditorDecorationType({}), + }; +} + +const gutterSpinnerDecorationType = + vscode.window.createTextEditorDecorationType({ + gutterIconPath: vscode.Uri.file( + path.join(__dirname, "..", "media", "spinner.gif") + ), + gutterIconSize: "contain", + }); + +export function showGutterSpinner( + editor: vscode.TextEditor, + lineno: number +): DecorationKey { + const key = constructBaseKey(editor, lineno, gutterSpinnerDecorationType); + decorationManager.addDecoration(key); + + return key; +} + +export function showLintMessage( + editor: vscode.TextEditor, + lineno: number, + msg: string +): DecorationKey { + const key = constructBaseKey(editor, lineno); + key.decorationType = vscode.window.createTextEditorDecorationType({ + after: { + contentText: "Linting error", + color: "rgb(255, 0, 0, 0.6)", + }, + gutterIconPath: vscode.Uri.file( + path.join(__dirname, "..", "media", "error.png") + ), + gutterIconSize: "contain", + }); + key.options.hoverMessage = msg; + decorationManager.addDecoration(key); + return key; +} + +export function highlightCode( + editor: vscode.TextEditor, + range: vscode.Range, + removeOnClick: boolean = true +): DecorationKey { + const decorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: "rgb(255, 255, 0, 0.1)", + }); + const key = { + editorUri: editor.document.uri.toString(), + options: { + range, + }, + decorationType, + }; + decorationManager.addDecoration(key); + + if (removeOnClick) { + vscode.window.onDidChangeTextEditorSelection((e) => { + if (e.textEditor === editor) { + decorationManager.deleteDecoration(key); + } + }); + } + + return key; +} + +// Show unit test +const pythonImportDistinguisher = (line: string): boolean => { + if (line.startsWith("from") || line.startsWith("import")) { + return true; + } + return false; +}; +const javascriptImportDistinguisher = (line: string): boolean => { + if (line.startsWith("import")) { + return true; + } + return false; +}; +const importDistinguishersMap: { + [fileExtension: string]: (line: string) => boolean; +} = { + js: javascriptImportDistinguisher, + ts: javascriptImportDistinguisher, + py: pythonImportDistinguisher, +}; +function getImportsFromFileString( + fileString: string, + importDistinguisher: (line: string) => boolean +): Set { + let importLines = new Set(); + for (let line of fileString.split("\n")) { + if (importDistinguisher(line)) { + importLines.add(line); + } + } + return importLines; +} +function removeRedundantLinesFrom( + fileContents: string, + linesToRemove: Set +): string { + let fileLines = fileContents.split("\n"); + fileLines = fileLines.filter((line: string) => { + return !linesToRemove.has(line); + }); + return fileLines.join("\n"); +} + +export async function writeAndShowUnitTest( + filename: string, + test: string +): Promise { + return new Promise((resolve, reject) => { + let testFilename = getTestFile(filename, true); + vscode.workspace.openTextDocument(testFilename).then((doc) => { + let fileContent = doc.getText(); + let fileEmpty = fileContent.trim() === ""; + let existingImportLines = getImportsFromFileString( + fileContent, + importDistinguishersMap[doc.fileName.split(".").at(-1) || ".py"] + ); + + // Remove redundant imports, make sure pytest is there + test = removeRedundantLinesFrom(test, existingImportLines); + test = + (fileEmpty + ? `${getLanguageLibrary(".py").writeImport( + testFilename, + filename + )}\nimport pytest\n\n` + : "\n\n") + + test.trim() + + "\n"; + + vscode.window + .showTextDocument(doc, getRightViewColumn()) + .then((editor) => { + let lastLine = editor.document.lineAt(editor.document.lineCount - 1); + let testRange = new vscode.Range( + lastLine.range.end, + new vscode.Position( + test.split("\n").length + lastLine.range.end.line, + 0 + ) + ); + editor + .edit((edit) => { + edit.insert(lastLine.range.end, test); + return true; + }) + .then((success) => { + if (!success) reject("Failed to insert test"); + let key = highlightCode(editor, testRange); + resolve(key); + }); + }); + }); + }); +} diff --git a/extension/src/extension.ts b/extension/src/extension.ts new file mode 100644 index 00000000..e0b94278 --- /dev/null +++ b/extension/src/extension.ts @@ -0,0 +1,37 @@ +/** + * This is the entry point for the extension. + */ + +import * as vscode from "vscode"; +import { + setupExtensionEnvironment, + isPythonEnvSetup, + startContinuePythonServer, +} from "./activation/environmentSetup"; + +async function dynamicImportAndActivate( + context: vscode.ExtensionContext, + showTutorial: boolean +) { + const { activateExtension } = await import("./activation/activate"); + activateExtension(context, showTutorial); +} + +export function activate(context: vscode.ExtensionContext) { + // Only show progress if we have to setup + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Setting up Continue extension...", + cancellable: false, + }, + async () => { + if (isPythonEnvSetup()) { + await startContinuePythonServer(); + } else { + await setupExtensionEnvironment(); + } + dynamicImportAndActivate(context, true); + } + ); +} diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts new file mode 100644 index 00000000..2a362b62 --- /dev/null +++ b/extension/src/lang-server/codeLens.ts @@ -0,0 +1,99 @@ +import * as vscode from "vscode"; +import { getLanguageLibrary } from "../languages"; +import { editorToSuggestions } from "../suggestions"; + +class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { + public provideCodeLenses( + document: vscode.TextDocument, + token: vscode.CancellationToken + ): vscode.CodeLens[] | Thenable { + let suggestions = editorToSuggestions.get(document.uri.toString()); + if (!suggestions) { + return []; + } + + let codeLenses: vscode.CodeLens[] = []; + for (let suggestion of suggestions) { + let range = new vscode.Range( + suggestion.oldRange.start, + suggestion.newRange.end + ); + codeLenses.push( + new vscode.CodeLens(range, { + title: "Accept", + command: "continue.acceptSuggestion", + arguments: [suggestion], + }), + new vscode.CodeLens(range, { + title: "Reject", + command: "continue.rejectSuggestion", + arguments: [suggestion], + }) + ); + } + + return codeLenses; + } + + onDidChangeCodeLenses?: vscode.Event | undefined; + + constructor(emitter?: vscode.EventEmitter) { + if (emitter) { + this.onDidChangeCodeLenses = emitter.event; + this.onDidChangeCodeLenses(() => { + if (vscode.window.activeTextEditor) { + this.provideCodeLenses( + vscode.window.activeTextEditor.document, + new vscode.CancellationTokenSource().token + ); + } + }); + } + } +} + +class PytestCodeLensProvider implements vscode.CodeLensProvider { + public provideCodeLenses( + document: vscode.TextDocument, + token: vscode.CancellationToken + ): vscode.CodeLens[] | Thenable { + let codeLenses: vscode.CodeLens[] = []; + let lineno = 1; + let languageLibrary = getLanguageLibrary(document.fileName); + for (let line of document.getText().split("\n")) { + if ( + languageLibrary.lineIsFunctionDef(line) && + languageLibrary.parseFunctionDefForName(line).startsWith("test_") + ) { + let functionToTest = languageLibrary.parseFunctionDefForName(line); + let fileAndFunctionNameSpecifier = + document.fileName + "::" + functionToTest; + codeLenses.push( + new vscode.CodeLens(new vscode.Range(lineno, 0, lineno, 1), { + title: "Debug This Test", + command: "continue.debugTest", + arguments: [fileAndFunctionNameSpecifier], + }) + ); + } + lineno++; + } + + return codeLenses; + } +} + +const allCodeLensProviders: { [langauge: string]: vscode.CodeLensProvider[] } = + { + python: [new SuggestionsCodeLensProvider(), new PytestCodeLensProvider()], + }; + +export function registerAllCodeLensProviders(context: vscode.ExtensionContext) { + for (let language in allCodeLensProviders) { + for (let codeLensProvider of allCodeLensProviders[language]) { + context.subscriptions.push( + vscode.languages.registerCodeLensProvider(language, codeLensProvider) + ); + } + } +} diff --git a/extension/src/languages/index.d.ts b/extension/src/languages/index.d.ts new file mode 100644 index 00000000..be7ddfbc --- /dev/null +++ b/extension/src/languages/index.d.ts @@ -0,0 +1,13 @@ +export interface LanguageLibrary { + language: string; + fileExtensions: string[]; + parseFirstStacktrace: (stdout: string) => string | undefined; + lineIsFunctionDef: (line: string) => boolean; + parseFunctionDefForName: (line: string) => string; + lineIsComment: (line: string) => boolean; + writeImport: ( + sourcePath: string, + pathToImport: string, + namesToImport?: string[] | undefined + ) => string; +} diff --git a/extension/src/languages/index.ts b/extension/src/languages/index.ts new file mode 100644 index 00000000..31d73a0b --- /dev/null +++ b/extension/src/languages/index.ts @@ -0,0 +1,19 @@ +import pythonLanguageLibrary from "./python"; +import javascriptLanguageLibrary from "./javascript"; +import { LanguageLibrary } from "./index.d"; + +export const languageLibraries: LanguageLibrary[] = [ + pythonLanguageLibrary, + javascriptLanguageLibrary, +]; + +export function getLanguageLibrary(filepath: string): LanguageLibrary { + for (let languageLibrary of languageLibraries) { + for (let fileExtension of languageLibrary.fileExtensions) { + if (filepath.endsWith(fileExtension)) { + return languageLibrary; + } + } + } + throw new Error(`No language library found for file ${filepath}`); +} diff --git a/extension/src/languages/javascript/index.ts b/extension/src/languages/javascript/index.ts new file mode 100644 index 00000000..1c21a2fc --- /dev/null +++ b/extension/src/languages/javascript/index.ts @@ -0,0 +1,16 @@ +import { LanguageLibrary } from "../index.d"; +import { notImplemented } from "../notImplemented"; + +const NI = (propertyName: string) => notImplemented(propertyName, "javascript"); + +const javascriptLangaugeLibrary: LanguageLibrary = { + language: "javascript", + fileExtensions: [".js", ".jsx", ".ts", ".tsx"], + parseFirstStacktrace: NI("parseFirstStacktrace"), + lineIsFunctionDef: NI("lineIsFunctionDef"), + parseFunctionDefForName: NI("parseFunctionDefForName"), + lineIsComment: NI("lineIsComment"), + writeImport: NI("writeImport"), +}; + +export default javascriptLangaugeLibrary; diff --git a/extension/src/languages/notImplemented.ts b/extension/src/languages/notImplemented.ts new file mode 100644 index 00000000..bbba2382 --- /dev/null +++ b/extension/src/languages/notImplemented.ts @@ -0,0 +1,10 @@ +export function notImplemented( + propertyName: string, + langauge: string +): (...args: any[]) => never { + return (...args: any[]) => { + throw new Error( + `Property ${propertyName} not implemented for language ${langauge}.` + ); + }; +} diff --git a/extension/src/languages/python/index.ts b/extension/src/languages/python/index.ts new file mode 100644 index 00000000..50282b45 --- /dev/null +++ b/extension/src/languages/python/index.ts @@ -0,0 +1,74 @@ +import path = require("path"); +import { LanguageLibrary } from "../index.d"; + +const tracebackStart = "Traceback (most recent call last):"; +const tracebackEnd = (buf: string): string | undefined => { + let lines = buf + .split("\n") + .filter((line: string) => line.trim() !== "~~^~~") + .filter((line: string) => line.trim() !== ""); + for (let i = 0; i < lines.length; i++) { + if ( + lines[i].startsWith(" File") && + i + 2 < lines.length && + lines[i + 2][0] !== " " + ) { + return lines.slice(0, i + 3).join("\n"); + } + } + return undefined; +}; + +function parseFirstStacktrace(stdout: string): string | undefined { + let startIdx = stdout.indexOf(tracebackStart); + if (startIdx < 0) return undefined; + stdout = stdout.substring(startIdx); + return tracebackEnd(stdout); +} + +function lineIsFunctionDef(line: string): boolean { + return line.startsWith("def "); +} + +function parseFunctionDefForName(line: string): string { + return line.split("def ")[1].split("(")[0]; +} + +function lineIsComment(line: string): boolean { + return line.trim().startsWith("#"); +} + +function writeImport( + sourcePath: string, + pathToImport: string, + namesToImport: string[] | undefined = undefined +): string { + let segs = path.relative(sourcePath, pathToImport).split(path.sep); + let importFrom = ""; + for (let seg of segs) { + if (seg === "..") { + importFrom = "." + importFrom; + } else { + if (!importFrom.endsWith(".")) { + importFrom += "."; + } + importFrom += seg.split(".").slice(0, -1).join("."); + } + } + + return `from ${importFrom} import ${ + namesToImport ? namesToImport.join(", ") : "*" + }`; +} + +const pythonLangaugeLibrary: LanguageLibrary = { + language: "python", + fileExtensions: [".py"], + parseFirstStacktrace, + lineIsFunctionDef, + parseFunctionDefForName, + lineIsComment, + writeImport, +}; + +export default pythonLangaugeLibrary; diff --git a/extension/src/suggestions.ts b/extension/src/suggestions.ts new file mode 100644 index 00000000..c66fad86 --- /dev/null +++ b/extension/src/suggestions.ts @@ -0,0 +1,311 @@ +import * as vscode from "vscode"; +import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; +import { openEditorAndRevealRange } from "./util/vscode"; +import { translate, readFileAtRange } from "./util/vscode"; + +export interface SuggestionRanges { + oldRange: vscode.Range; + newRange: vscode.Range; + newSelected: boolean; +} + +/* Keyed by editor.document.uri.toString() */ +export const editorToSuggestions: Map< + string, // URI of file + SuggestionRanges[] +> = new Map(); +export let currentSuggestion: Map = new Map(); // Map from editor URI to index of current SuggestionRanges in editorToSuggestions + +// When tab is reopened, rerender the decorations: +vscode.window.onDidChangeActiveTextEditor((editor) => { + if (!editor) return; + rerenderDecorations(editor.document.uri.toString()); +}); +vscode.workspace.onDidOpenTextDocument((doc) => { + rerenderDecorations(doc.uri.toString()); +}); + +let newDecorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: "rgb(0, 255, 0, 0.1)", + isWholeLine: true, +}); +let oldDecorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: "rgb(255, 0, 0, 0.1)", + isWholeLine: true, + cursor: "pointer", +}); +let newSelDecorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: "rgb(0, 255, 0, 0.25)", + isWholeLine: true, + after: { + contentText: "Press ctrl+shift+enter to accept", + margin: "0 0 0 1em", + }, +}); +let oldSelDecorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: "rgb(255, 0, 0, 0.25)", + isWholeLine: true, + after: { + contentText: "Press ctrl+shift+enter to reject", + margin: "0 0 0 1em", + }, +}); + +export function rerenderDecorations(editorUri: string) { + let suggestions = editorToSuggestions.get(editorUri); + let idx = currentSuggestion.get(editorUri); + let editor = vscode.window.visibleTextEditors.find( + (editor) => editor.document.uri.toString() === editorUri + ); + if (!suggestions || !editor) return; + + let olds: vscode.Range[] = [], + news: vscode.Range[] = [], + oldSels: vscode.Range[] = [], + newSels: vscode.Range[] = []; + for (let i = 0; i < suggestions.length; i++) { + let suggestion = suggestions[i]; + if (typeof idx != "undefined" && idx === i) { + if (suggestion.newSelected) { + olds.push(suggestion.oldRange); + newSels.push(suggestion.newRange); + } else { + oldSels.push(suggestion.oldRange); + news.push(suggestion.newRange); + } + } else { + olds.push(suggestion.oldRange); + news.push(suggestion.newRange); + } + } + editor.setDecorations(oldDecorationType, olds); + editor.setDecorations(newDecorationType, news); + editor.setDecorations(oldSelDecorationType, oldSels); + editor.setDecorations(newSelDecorationType, newSels); + + // Reveal the range in the editor + if (idx === undefined) return; + editor.revealRange( + suggestions[idx].newRange, + vscode.TextEditorRevealType.Default + ); +} + +export function suggestionDownCommand() { + let editor = vscode.window.activeTextEditor; + if (!editor) return; + let editorUri = editor.document.uri.toString(); + let suggestions = editorToSuggestions.get(editorUri); + let idx = currentSuggestion.get(editorUri); + if (!suggestions || idx === undefined) return; + + let suggestion = suggestions[idx]; + if (!suggestion.newSelected) { + suggestion.newSelected = true; + } else if (idx + 1 < suggestions.length) { + currentSuggestion.set(editorUri, idx + 1); + } else return; + rerenderDecorations(editorUri); +} + +export function suggestionUpCommand() { + let editor = vscode.window.activeTextEditor; + if (!editor) return; + let editorUri = editor.document.uri.toString(); + let suggestions = editorToSuggestions.get(editorUri); + let idx = currentSuggestion.get(editorUri); + if (!suggestions || idx === undefined) return; + + let suggestion = suggestions[idx]; + if (suggestion.newSelected) { + suggestion.newSelected = false; + } else if (idx > 0) { + currentSuggestion.set(editorUri, idx - 1); + } else return; + rerenderDecorations(editorUri); +} + +type SuggestionSelectionOption = "old" | "new" | "selected"; +function selectSuggestion( + accept: SuggestionSelectionOption, + key: SuggestionRanges | null = null +) { + let editor = vscode.window.activeTextEditor; + if (!editor) return; + let editorUri = editor.document.uri.toString(); + let suggestions = editorToSuggestions.get(editorUri); + + if (!suggestions) return; + + let idx: number | undefined; + if (key) { + // Use the key to find a specific suggestion + for (let i = 0; i < suggestions.length; i++) { + if ( + suggestions[i].newRange === key.newRange && + suggestions[i].oldRange === key.oldRange + ) { + // Don't include newSelected in the comparison, because it can change + idx = i; + break; + } + } + } else { + // Otherwise, use the current suggestion + idx = currentSuggestion.get(editorUri); + } + if (idx === undefined) return; + + let [suggestion] = suggestions.splice(idx, 1); + + var rangeToDelete: vscode.Range; + switch (accept) { + case "old": + rangeToDelete = suggestion.newRange; + break; + case "new": + rangeToDelete = suggestion.oldRange; + break; + case "selected": + rangeToDelete = suggestion.newSelected + ? suggestion.oldRange + : suggestion.newRange; + } + + rangeToDelete = new vscode.Range( + rangeToDelete.start, + new vscode.Position(rangeToDelete.end.line + 1, 0) + ); + editor.edit((edit) => { + edit.delete(rangeToDelete); + }); + + // Shift the below suggestions up + let linesToShift = rangeToDelete.end.line - rangeToDelete.start.line; + for (let below of suggestions) { + // Assumes there should be no crossover between suggestions. Might want to enforce this. + if ( + below.oldRange.union(below.newRange).start.line > + suggestion.oldRange.union(suggestion.newRange).start.line + ) { + below.oldRange = translate(below.oldRange, -linesToShift); + below.newRange = translate(below.newRange, -linesToShift); + } + } + + if (suggestions.length === 0) { + currentSuggestion.delete(editorUri); + } else { + currentSuggestion.set(editorUri, Math.min(idx, suggestions.length - 1)); + } + rerenderDecorations(editorUri); +} + +export function acceptSuggestionCommand(key: SuggestionRanges | null = null) { + sendTelemetryEvent(TelemetryEvent.SuggestionAccepted); + selectSuggestion("selected", key); +} + +export async function rejectSuggestionCommand( + key: SuggestionRanges | null = null +) { + sendTelemetryEvent(TelemetryEvent.SuggestionRejected); + selectSuggestion("old", key); +} + +export async function showSuggestion( + editorFilename: string, + range: vscode.Range, + suggestion: string +): Promise { + let existingCode = await readFileAtRange( + new vscode.Range(range.start, range.end), + editorFilename + ); + + // If any of the outside lines are the same, don't repeat them in the suggestion + let slines = suggestion.split("\n"); + let elines = existingCode.split("\n"); + let linesRemovedBefore = 0; + let linesRemovedAfter = 0; + while (slines.length > 0 && elines.length > 0 && slines[0] === elines[0]) { + slines.shift(); + elines.shift(); + linesRemovedBefore++; + } + + while ( + slines.length > 0 && + elines.length > 0 && + slines[slines.length - 1] === elines[elines.length - 1] + ) { + slines.pop(); + elines.pop(); + linesRemovedAfter++; + } + + suggestion = slines.join("\n"); + if (suggestion === "") return Promise.resolve(false); // Don't even make a suggestion if they are exactly the same + + range = new vscode.Range( + new vscode.Position(range.start.line + linesRemovedBefore, 0), + new vscode.Position( + range.end.line - linesRemovedAfter, + elines.at(-1)?.length || 0 + ) + ); + + let editor = await openEditorAndRevealRange(editorFilename, range); + if (!editor) return Promise.resolve(false); + + return new Promise((resolve, reject) => { + editor! + .edit((edit) => { + if (range.end.line + 1 >= editor.document.lineCount) { + suggestion = "\n" + suggestion; + } + edit.insert( + new vscode.Position(range.end.line + 1, 0), + suggestion + "\n" + ); + }) + .then( + (success) => { + if (success) { + let suggestionRange = new vscode.Range( + new vscode.Position(range.end.line + 1, 0), + new vscode.Position( + range.end.line + suggestion.split("\n").length, + 0 + ) + ); + + const filename = editor!.document.uri.toString(); + if (editorToSuggestions.has(filename)) { + let suggestions = editorToSuggestions.get(filename)!; + suggestions.push({ + oldRange: range, + newRange: suggestionRange, + newSelected: true, + }); + editorToSuggestions.set(filename, suggestions); + currentSuggestion.set(filename, suggestions.length - 1); + } else { + editorToSuggestions.set(filename, [ + { + oldRange: range, + newRange: suggestionRange, + newSelected: true, + }, + ]); + currentSuggestion.set(filename, 0); + } + + rerenderDecorations(filename); + } + resolve(success); + }, + (reason) => reject(reason) + ); + }); +} diff --git a/extension/src/telemetry.ts b/extension/src/telemetry.ts new file mode 100644 index 00000000..ea71a545 --- /dev/null +++ b/extension/src/telemetry.ts @@ -0,0 +1,51 @@ +import * as Segment from "@segment/analytics-node"; +import * as vscode from "vscode"; + +// Setup Segment +const SEGMENT_WRITE_KEY = "57yy2uYXH2bwMuy7djm9PorfFlYqbJL1"; +const analytics = new Segment.Analytics({ writeKey: SEGMENT_WRITE_KEY }); +analytics.identify({ + userId: vscode.env.machineId, + // traits: { + // name: "Michael Bolton", + // email: "mbolton@example.com", + // createdAt: new Date("2014-06-14T02:00:19.467Z"), + // }, +}); + +// Enum of telemetry events +export enum TelemetryEvent { + // Extension has been activated + ExtensionActivated = "ExtensionActivated", + // Suggestion has been accepted + SuggestionAccepted = "SuggestionAccepted", + // Suggestion has been rejected + SuggestionRejected = "SuggestionRejected", + // Queried universal prompt + UniversalPromptQuery = "UniversalPromptQuery", + // `Explain Code` button clicked + ExplainCode = "ExplainCode", + // `Generate Ideas` button clicked + GenerateIdeas = "GenerateIdeas", + // `Suggest Fix` button clicked + SuggestFix = "SuggestFix", + // `Create Test` button clicked + CreateTest = "CreateTest", + // `AutoDebug This Test` button clicked + AutoDebugThisTest = "AutoDebugThisTest", + // Command run to generate docstring + GenerateDocstring = "GenerateDocstring", +} + +export function sendTelemetryEvent( + event: TelemetryEvent, + properties?: Record +) { + if (!vscode.env.isTelemetryEnabled) return; + + analytics.track({ + event, + userId: vscode.env.machineId, + properties, + }); +} \ No newline at end of file diff --git a/extension/src/terminal/snoopers.ts b/extension/src/terminal/snoopers.ts new file mode 100644 index 00000000..a1f8993c --- /dev/null +++ b/extension/src/terminal/snoopers.ts @@ -0,0 +1,133 @@ +export abstract class TerminalSnooper { + abstract onData(data: string): void; + abstract onWrite(data: string): void; + callback: (data: T) => void; + + constructor(callback: (data: T) => void) { + this.callback = callback; + } +} + +function stripAnsi(data: string) { + const pattern = [ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", + ].join("|"); + + let regex = new RegExp(pattern, "g"); + return data.replace(regex, ""); +} + +export class CommandCaptureSnooper extends TerminalSnooper { + stdinBuffer = ""; + cursorPos = 0; + stdoutHasInterrupted = false; + + static RETURN_KEY = "\r"; + static DEL_KEY = "\x7F"; + static UP_KEY = "\x1B[A"; + static DOWN_KEY = "\x1B[B"; + static RIGHT_KEY = "\x1B[C"; + static LEFT_KEY = "\x1B[D"; + static CONTROL_KEYS = new Set([ + CommandCaptureSnooper.RETURN_KEY, + CommandCaptureSnooper.DEL_KEY, + CommandCaptureSnooper.UP_KEY, + CommandCaptureSnooper.DOWN_KEY, + CommandCaptureSnooper.RIGHT_KEY, + CommandCaptureSnooper.LEFT_KEY, + ]); + + private _cursorLeft() { + this.cursorPos = Math.max(0, this.cursorPos - 1); + } + private _cursorRight() { + this.cursorPos = Math.min(this.stdinBuffer.length, this.cursorPos + 1); + } + // Known issue: This does not handle autocomplete. + // Would be preferable to find a way that didn't require this all, just parsing by command prompt + // but that has it's own challenges + private handleControlKey(data: string): void { + switch (data) { + case CommandCaptureSnooper.DEL_KEY: + this.stdinBuffer = + this.stdinBuffer.slice(0, this.cursorPos - 1) + + this.stdinBuffer.slice(this.cursorPos); + this._cursorLeft(); + break; + case CommandCaptureSnooper.RETURN_KEY: + this.callback(this.stdinBuffer); + this.stdinBuffer = ""; + break; + case CommandCaptureSnooper.UP_KEY: + case CommandCaptureSnooper.DOWN_KEY: + this.stdinBuffer = ""; + break; + case CommandCaptureSnooper.RIGHT_KEY: + this._cursorRight(); + break; + case CommandCaptureSnooper.LEFT_KEY: + this._cursorLeft(); + break; + } + } + + onWrite(data: string): void { + if (CommandCaptureSnooper.CONTROL_KEYS.has(data)) { + this.handleControlKey(data); + } else { + this.stdinBuffer = + this.stdinBuffer.substring(0, this.cursorPos) + + data + + this.stdinBuffer.substring(this.cursorPos); + this._cursorRight(); + } + } + + onData(data: string): void {} +} + +export class PythonTracebackSnooper extends TerminalSnooper { + static tracebackStart = "Traceback (most recent call last):"; + tracebackBuffer = ""; + + static tracebackEnd = (buf: string): string | undefined => { + let lines = buf.split("\n"); + for (let i = 0; i < lines.length; i++) { + if ( + lines[i].startsWith(" File") && + i + 2 < lines.length && + lines[i + 2][0] != " " + ) { + return lines.slice(0, i + 3).join("\n"); + } + } + return undefined; + }; + override onWrite(data: string): void {} + override onData(data: string): void { + let strippedData = stripAnsi(data); + // Strip fully blank and squiggle lines + strippedData = strippedData + .split("\n") + .filter((line) => line.trim().length > 0 && line.trim() !== "~~^~~") + .join("\n"); + // Snoop for traceback + let idx = strippedData.indexOf(PythonTracebackSnooper.tracebackStart); + if (idx >= 0) { + this.tracebackBuffer = strippedData.substr(idx); + } else if (this.tracebackBuffer.length > 0) { + this.tracebackBuffer += "\n" + strippedData; + } + // End of traceback, send to webview + if (this.tracebackBuffer.length > 0) { + let wholeTraceback = PythonTracebackSnooper.tracebackEnd( + this.tracebackBuffer + ); + if (wholeTraceback) { + this.callback(wholeTraceback); + this.tracebackBuffer = ""; + } + } + } +} diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts new file mode 100644 index 00000000..ba860b24 --- /dev/null +++ b/extension/src/terminal/terminalEmulator.ts @@ -0,0 +1,140 @@ +// /* Terminal emulator - commented because node-pty is causing problems. */ + +// import * as vscode from "vscode"; +// import pty = require("node-pty"); +// import os = require("os"); +// import { extensionContext } from "../activation/activate"; +// import { debugPanelWebview } from "../debugPanel"; // Need to consider having multiple panels, where to store this state. +// import { +// CommandCaptureSnooper, +// PythonTracebackSnooper, +// TerminalSnooper, +// } from "./snoopers"; + +// export function tracebackToWebviewAction(traceback: string) { +// if (debugPanelWebview) { +// debugPanelWebview.postMessage({ +// type: "traceback", +// value: traceback, +// }); +// } else { +// vscode.commands +// .executeCommand("continue.openDebugPanel", extensionContext) +// .then(() => { +// // TODO: Waiting for the webview to load, but should add a hook to the onLoad message event. Same thing in autodebugTest command in commands.ts +// setTimeout(() => { +// debugPanelWebview?.postMessage({ +// type: "traceback", +// value: traceback, +// }); +// }, 500); +// }); +// } +// } + +// const DEFAULT_SNOOPERS = [ +// new PythonTracebackSnooper(tracebackToWebviewAction), +// new CommandCaptureSnooper((data: string) => { +// if (data.trim().startsWith("pytest ")) { +// let fileAndFunctionSpecifier = data.split(" ")[1]; +// vscode.commands.executeCommand( +// "continue.debugTest", +// fileAndFunctionSpecifier +// ); +// } +// }), +// ]; + +// // Whenever a user opens a terminal, replace it with ours +// vscode.window.onDidOpenTerminal((terminal) => { +// if (terminal.name != "Continue") { +// terminal.dispose(); +// openCapturedTerminal(); +// } +// }); + +// function getDefaultShell(): string { +// if (process.platform !== "win32") { +// return os.userInfo().shell; +// } +// switch (process.platform) { +// case "win32": +// return process.env.COMSPEC || "cmd.exe"; +// // case "darwin": +// // return process.env.SHELL || "/bin/zsh"; +// // default: +// // return process.env.SHELL || "/bin/sh"; +// } +// } + +// function getRootDir(): string | undefined { +// var isWindows = os.platform() === "win32"; +// let cwd = isWindows ? process.env.USERPROFILE : process.env.HOME; +// if ( +// vscode.workspace.workspaceFolders && +// vscode.workspace.workspaceFolders.length > 0 +// ) { +// cwd = vscode.workspace.workspaceFolders[0].uri.fsPath; +// } +// return cwd; +// } + +// export function openCapturedTerminal( +// snoopers: TerminalSnooper[] = DEFAULT_SNOOPERS +// ) { +// // If there is another existing, non-Continue terminal, delete it +// let terminals = vscode.window.terminals; +// for (let i = 0; i < terminals.length; i++) { +// if (terminals[i].name != "Continue") { +// terminals[i].dispose(); +// } +// } + +// let env = { ...(process.env as any) }; +// if (os.platform() !== "win32") { +// env["PATH"] += ":" + ["/opt/homebrew/bin", "/opt/homebrew/sbin"].join(":"); +// } + +// var ptyProcess = pty.spawn(getDefaultShell(), [], { +// name: "xterm-256color", +// cols: 160, // TODO: Get size of vscode terminal, and change with resize +// rows: 26, +// cwd: getRootDir(), +// env, +// useConpty: true, +// }); + +// const writeEmitter = new vscode.EventEmitter(); + +// ptyProcess.onData((data: any) => { +// // Let each of the snoopers see the new data +// for (let snooper of snoopers) { +// snooper.onData(data); +// } + +// // Pass data through to terminal +// writeEmitter.fire(data); +// }); +// process.on("exit", () => ptyProcess.kill()); + +// const newPty: vscode.Pseudoterminal = { +// onDidWrite: writeEmitter.event, +// open: () => {}, +// close: () => {}, +// handleInput: (data) => { +// for (let snooper of snoopers) { +// snooper.onWrite(data); +// } +// ptyProcess.write(data); +// }, +// }; +// const terminal = vscode.window.createTerminal({ +// name: "Continue", +// pty: newPty, +// }); +// terminal.show(); + +// setTimeout(() => { +// ptyProcess.write("clear\r"); +// }, 500); +// } diff --git a/extension/src/test/runTest.ts b/extension/src/test/runTest.ts new file mode 100644 index 00000000..27b3ceb2 --- /dev/null +++ b/extension/src/test/runTest.ts @@ -0,0 +1,23 @@ +import * as path from 'path'; + +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, '../../'); + + // 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); + } +} + +main(); diff --git a/extension/src/test/suite/extension.test.ts b/extension/src/test/suite/extension.test.ts new file mode 100644 index 00000000..890820b2 --- /dev/null +++ b/extension/src/test/suite/extension.test.ts @@ -0,0 +1,16 @@ +import { test, describe } from "mocha"; +import * as assert from "assert"; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from "vscode"; +// import * as myExtension from '../../extension'; + +describe("Extension Test Suite", () => { + vscode.window.showInformationMessage("Start all tests."); + + test("Sample test", () => { + assert.strictEqual(-1, [1, 2, 3].indexOf(5)); + assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + }); +}); diff --git a/extension/src/test/suite/index.ts b/extension/src/test/suite/index.ts new file mode 100644 index 00000000..772a0152 --- /dev/null +++ b/extension/src/test/suite/index.ts @@ -0,0 +1,38 @@ +import * as path from "path"; +import * as Mocha from "mocha"; +import * as glob from "glob"; + +export function run(): Promise { + // Create the mocha test + const mocha = new Mocha({ + ui: "tdd", + color: true, + }); + + const testsRoot = path.resolve(__dirname, ".."); + + return new Promise((c, e) => { + glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { + if (err) { + return e(err); + } + + // Add files to the test suite + files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); + + try { + // Run the mocha test + mocha.run((failures: any) => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } else { + c(); + } + }); + } catch (err) { + console.error(err); + e(err); + } + }); + }); +} diff --git a/extension/src/test/suite/terminalEmulator.test.ts b/extension/src/test/suite/terminalEmulator.test.ts new file mode 100644 index 00000000..c4c159a4 --- /dev/null +++ b/extension/src/test/suite/terminalEmulator.test.ts @@ -0,0 +1,28 @@ +import { test, describe } from "mocha"; +import * as assert from "assert"; +import { PythonTracebackSnooper } from "../../terminal/snoopers"; + +suite("Snoopers", () => { + suite("PythonTracebackSnooper", () => { + test("should detect traceback given all at once", async () => { + let traceback = `Traceback (most recent call last): + File "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", line 10, in + sum(first, second) + File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum + return a + b + TypeError: unsupported operand type(s) for +: 'int' and 'str'`; + let returnedTraceback = await new Promise((resolve) => { + let callback = (data: string) => { + resolve(data); + }; + let snooper = new PythonTracebackSnooper(callback); + snooper.onData(traceback); + }); + assert( + returnedTraceback === traceback, + "Detected \n" + returnedTraceback + ); + }); + test("should detect traceback given in chunks", () => {}); + }); +}); diff --git a/extension/src/test/suite/util.test.ts b/extension/src/test/suite/util.test.ts new file mode 100644 index 00000000..0ba1473b --- /dev/null +++ b/extension/src/test/suite/util.test.ts @@ -0,0 +1,18 @@ +import { test, describe } from "mocha"; +import * as assert from "assert"; +import { convertSingleToDoubleQuoteJSON } from "../../util/util"; + +describe("utils.ts", () => { + test("convertSingleToDoubleQuoteJson", () => { + let pairs = [ + [`{'a': 'b'}`, `{"a": "b"}`], + [`{'a': "b", "c": 'd'}`, `{"a": "b", "c": "d"}`], + [`{'a': '\\'"'}`, `{"a": "'\\""}`], + ]; + for (let pair of pairs) { + let result = convertSingleToDoubleQuoteJSON(pair[0]); + assert(result === pair[1]); + JSON.parse(result); + } + }); +}); diff --git a/extension/src/util/util.ts b/extension/src/util/util.ts new file mode 100644 index 00000000..d33593e1 --- /dev/null +++ b/extension/src/util/util.ts @@ -0,0 +1,115 @@ +import { RangeInFile, SerializedDebugContext } from "../client"; +import * as fs from "fs"; + +function charIsEscapedAtIndex(index: number, str: string): boolean { + if (index === 0) return false; + if (str[index - 1] !== "\\") return false; + return !charIsEscapedAtIndex(index - 1, str); +} + +export function convertSingleToDoubleQuoteJSON(json: string): string { + const singleQuote = "'"; + const doubleQuote = '"'; + const isQuote = (char: string) => + char === doubleQuote || char === singleQuote; + + let newJson = ""; + let insideString = false; + let enclosingQuoteType = doubleQuote; + for (let i = 0; i < json.length; i++) { + if (insideString) { + if (json[i] === enclosingQuoteType && !charIsEscapedAtIndex(i, json)) { + // Close string with a double quote + insideString = false; + newJson += doubleQuote; + } else if (json[i] === singleQuote) { + if (charIsEscapedAtIndex(i, json)) { + // Unescape single quote + newJson = newJson.slice(0, -1); + } + newJson += singleQuote; + } else if (json[i] === doubleQuote) { + if (!charIsEscapedAtIndex(i, json)) { + // Escape double quote + newJson += "\\"; + } + newJson += doubleQuote; + } else { + newJson += json[i]; + } + } else { + if (isQuote(json[i])) { + insideString = true; + enclosingQuoteType = json[i]; + newJson += doubleQuote; + } else { + newJson += json[i]; + } + } + } + + return newJson; +} + +export async function readRangeInFile( + rangeInFile: RangeInFile +): Promise { + const range = rangeInFile.range; + return new Promise((resolve, reject) => { + fs.readFile(rangeInFile.filepath, (err, data) => { + if (err) { + reject(err); + } else { + let lines = data.toString().split("\n"); + if (range.start.line === range.end.line) { + resolve( + lines[rangeInFile.range.start.line].slice( + rangeInFile.range.start.character, + rangeInFile.range.end.character + ) + ); + } else { + let firstLine = lines[range.start.line].slice(range.start.character); + let lastLine = lines[range.end.line].slice(0, range.end.character); + let middleLines = lines.slice(range.start.line + 1, range.end.line); + resolve([firstLine, ...middleLines, lastLine].join("\n")); + } + } + }); + }); +} + +export function codeSelectionsToVirtualFileSystem( + codeSelections: RangeInFile[] +): { + [filepath: string]: string; +} { + let virtualFileSystem: { [filepath: string]: string } = {}; + for (let cs of codeSelections) { + if (!cs.filepath) continue; + if (cs.filepath in virtualFileSystem) continue; + let content = fs.readFileSync(cs.filepath, "utf8"); + virtualFileSystem[cs.filepath] = content; + } + return virtualFileSystem; +} + +export function addFileSystemToDebugContext( + ctx: SerializedDebugContext +): SerializedDebugContext { + ctx.filesystem = codeSelectionsToVirtualFileSystem(ctx.rangesInFiles); + return ctx; +} + +export function debounced(delay: number, fn: Function) { + let timerId: NodeJS.Timeout | null; + return function (...args: any[]) { + if (timerId) { + clearTimeout(timerId); + } + timerId = setTimeout(() => { + fn(...args); + timerId = null; + }, delay); + }; +} diff --git a/extension/src/util/vscode.ts b/extension/src/util/vscode.ts new file mode 100644 index 00000000..4eab98a7 --- /dev/null +++ b/extension/src/util/vscode.ts @@ -0,0 +1,152 @@ +import * as vscode from "vscode"; +import * as path from "path"; +import * as fs from "fs"; + +export function translate(range: vscode.Range, lines: number): vscode.Range { + return new vscode.Range( + range.start.line + lines, + range.start.character, + range.end.line + lines, + range.end.character + ); +} + +export function getNonce() { + let text = ""; + const possible = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} + +export function getTestFile( + filename: string, + createFile: boolean = false +): string { + let basename = path.basename(filename).split(".")[0]; + switch (path.extname(filename)) { + case ".py": + basename += "_test"; + break; + case ".js": + case ".jsx": + case ".ts": + case ".tsx": + basename += ".test"; + break; + default: + basename += "_test"; + } + + const directory = path.join(path.dirname(filename), "tests"); + const testFilename = path.join(directory, basename + path.extname(filename)); + + // Optionally, create the file if it doesn't exist + if (createFile && !fs.existsSync(testFilename)) { + if (!fs.existsSync(directory)) { + fs.mkdirSync(directory); + } + fs.writeFileSync(testFilename, ""); + } + + return testFilename; +} + +export function getExtensionUri(): vscode.Uri { + return vscode.extensions.getExtension("Continue.continue")!.extensionUri; +} + +export function getViewColumnOfFile( + filepath: string +): vscode.ViewColumn | undefined { + for (let tabGroup of vscode.window.tabGroups.all) { + for (let tab of tabGroup.tabs) { + if ( + (tab?.input as any)?.uri && + (tab.input as any).uri.fsPath === filepath + ) { + return tabGroup.viewColumn; + } + } + } + return undefined; +} + +export function getRightViewColumn(): vscode.ViewColumn { + // When you want to place in the rightmost panel if there is already more than one, otherwise use Beside + let column = vscode.ViewColumn.Beside; + let columnOrdering = [ + vscode.ViewColumn.One, + vscode.ViewColumn.Beside, + vscode.ViewColumn.Two, + vscode.ViewColumn.Three, + vscode.ViewColumn.Four, + vscode.ViewColumn.Five, + vscode.ViewColumn.Six, + vscode.ViewColumn.Seven, + vscode.ViewColumn.Eight, + vscode.ViewColumn.Nine, + ]; + for (let tabGroup of vscode.window.tabGroups.all) { + if ( + columnOrdering.indexOf(tabGroup.viewColumn) > + columnOrdering.indexOf(column) + ) { + column = tabGroup.viewColumn; + } + } + return column; +} + +export async function readFileAtRange( + range: vscode.Range, + filepath: string +): Promise { + return new Promise((resolve, reject) => { + fs.readFile(filepath, (err, data) => { + if (err) { + reject(err); + } else { + let lines = data.toString().split("\n"); + if (range.isSingleLine) { + resolve( + lines[range.start.line].slice( + range.start.character, + range.end.character + ) + ); + } else { + let firstLine = lines[range.start.line].slice(range.start.character); + let lastLine = lines[range.end.line].slice(0, range.end.character); + let middleLines = lines.slice(range.start.line + 1, range.end.line); + resolve([firstLine, ...middleLines, lastLine].join("\n")); + } + } + }); + }); +} + +export function openEditorAndRevealRange( + editorFilename: string, + range?: vscode.Range, + viewColumn?: vscode.ViewColumn +): Promise { + return new Promise((resolve, _) => { + // Check if the editor is already open + vscode.workspace.openTextDocument(editorFilename).then((doc) => { + vscode.window + .showTextDocument( + doc, + getViewColumnOfFile(editorFilename) || viewColumn + ) + .then((editor) => { + if (range) { + editor.revealRange(range); + } + resolve(editor); + }); + }); + }); +} -- cgit v1.2.3-70-g09d2 From d3db75ee7e2483f86ba5eb8856dd59aaadfae285 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sat, 27 May 2023 23:33:47 -0400 Subject: posthog setup in react app --- extension/react-app/package-lock.json | 39 ++++++++++++++++++++++++++++ extension/react-app/package.json | 1 + extension/react-app/src/main.tsx | 25 ++++++++++++------ extension/src/activation/environmentSetup.ts | 4 +-- 4 files changed, 59 insertions(+), 10 deletions(-) (limited to 'extension/src') 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/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( - - , -) + + + + +); diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 4816b4b1..1e921c18 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -59,7 +59,7 @@ async function setupPythonEnv() { } let pipCmd = pythonCmd.endsWith("3") ? "pip3" : "pip"; - let activateCmd = "source env/bin/activate"; + let activateCmd = ". env/bin/activate"; let pipUpgradeCmd = `${pipCmd} install --upgrade pip`; if (process.platform == "win32") { activateCmd = ".\\env\\Scripts\\activate"; @@ -147,7 +147,7 @@ export async function startContinuePythonServer() { console.log("Error checking for existing server", e); } - let activateCmd = "source env/bin/activate"; + let activateCmd = ". env/bin/activate"; let pythonCmd = "python3"; if (process.platform == "win32") { activateCmd = ".\\env\\Scripts\\activate"; -- cgit v1.2.3-70-g09d2 From 64643e87e9af8b98945614119179e45fc95281e4 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 28 May 2023 15:11:46 -0400 Subject: First load fail error fixed --- extension/package-lock.json | 5 +-- extension/package.json | 4 +-- extension/scripts/install_from_source.py | 50 +++++++++++++++++++--------- extension/src/activation/environmentSetup.ts | 5 ++- extension/src/continueIdeClient.ts | 26 +++++++++------ 5 files changed, 56 insertions(+), 34 deletions(-) (limited to 'extension/src') diff --git a/extension/package-lock.json b/extension/package-lock.json index faa07b57..822b908e 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,13 @@ { "name": "continue", - "version": "0.0.3", + "version": "0.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.3", + "version": "0.0.4", + "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", "@reduxjs/toolkit": "^1.9.3", diff --git a/extension/package.json b/extension/package.json index 40d56fb7..f5771ee6 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.3", + "version": "0.0.4", "publisher": "Continue", "engines": { "vscode": "^1.74.0" @@ -149,7 +149,7 @@ "scripts": { "vscode:prepublish": "npm run esbuild-base -- --minify", "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node", - "esbuild": "npm run esbuild-base -- --sourcemap", + "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", diff --git a/extension/scripts/install_from_source.py b/extension/scripts/install_from_source.py index 4fe903ed..28f382b0 100644 --- a/extension/scripts/install_from_source.py +++ b/extension/scripts/install_from_source.py @@ -1,43 +1,61 @@ +import os import subprocess def run(cmd: str): - return subprocess.run(cmd, shell=True, capture_output=True) + return subprocess.run(cmd, shell=True, capture_output=False) def main(): # Check for Python and Node - we won't install them, but will warn - out, err1 = run("python --version") - out, err2 = run("python3 --version") - if err1 and err2: + 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 - out, err = run("node --version") - if err: + 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 - out, err = run("npm --version") - if err: + 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 - out, err = run("poetry --version") - if err: + 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 - out, err = run("cd ../../continuedev; poetry run typegen") + resp = run("cd ../../continuedev; poetry run typegen") - out, err = run( - "cd ..; npm i; cd react-app; npm i; cd ..; npm run full-package\r y\r npm run install-extension") + resp = run( + "cd ..; npm i; cd react-app; npm i; cd ..; npm run full-package") - if err: - print("Error installing the extension. Please try again.") - print("This was the error: ", err) + 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.") diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 1e921c18..93a471ff 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -143,9 +143,7 @@ export async function startContinuePythonServer() { console.log("Continue python server already running"); return; } - } catch (e) { - console.log("Error checking for existing server", e); - } + } catch (e) {} let activateCmd = ". env/bin/activate"; let pythonCmd = "python3"; @@ -162,6 +160,7 @@ export async function startContinuePythonServer() { // exec(command); let child = spawn(command, { shell: true, + detached: true, }); child.stdout.on("data", (data: any) => { console.log(`stdout: ${data}`); diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 6c65415f..35eb668d 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -66,18 +66,22 @@ class IdeProtocolClient { } 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); + if (this._ws === null || this._ws.readyState !== WebSocket.OPEN) { + let ws = new WebSocket(this._serverUrl); + ws.onclose = () => { + this._ws = null; }; - }); + ws.on("message", (data: any) => { + this.handleMessage(JSON.parse(data)); + }); + this._ws = ws; + + return new Promise((resolve, reject) => { + ws.addEventListener("open", () => { + resolve(null); + }); + }); + } } async startCore() { -- cgit v1.2.3-70-g09d2 From e02da4c8fada20b7e6bdd80d257a868bbd6b0d0f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 28 May 2023 19:16:10 -0400 Subject: websockets through vscode messaging for codespaces --- extension/react-app/src/hooks/useWebsocket.ts | 133 ++++++++++++++++++++++---- extension/src/debugPanel.ts | 95 ++++++++++++++++++ 2 files changed, 207 insertions(+), 21 deletions(-) (limited to 'extension/src') diff --git a/extension/react-app/src/hooks/useWebsocket.ts b/extension/react-app/src/hooks/useWebsocket.ts index 147172bd..6e8e68fa 100644 --- a/extension/react-app/src/hooks/useWebsocket.ts +++ b/extension/react-app/src/hooks/useWebsocket.ts @@ -1,13 +1,100 @@ import React, { useEffect, useState } from "react"; import { RootStore } from "../redux/store"; import { useSelector } from "react-redux"; +import { postVscMessage } from "../vscode"; + +abstract class Messenger { + abstract send(data: string): void; +} + +class VscodeMessenger extends Messenger { + url: string; + + constructor( + url: string, + onMessage: (message: { data: any }) => void, + onOpen: (messenger: Messenger) => void, + onClose: (messenger: Messenger) => void + ) { + super(); + this.url = url; + window.addEventListener("message", (event: any) => { + switch (event.data.type) { + case "websocketForwardingMessage": + onMessage(event.data); + break; + case "websocketForwardingOpen": + onOpen(this); + break; + case "websocketForwardingClose": + onClose(this); + break; + } + }); + + postVscMessage("websocketForwardingOpen", { url: this.url }); + } + + send(data: string) { + postVscMessage("websocketForwardingMessage", { + message: data, + url: this.url, + }); + } +} + +class WebsocketMessenger extends Messenger { + websocket: WebSocket; + constructor( + websocket: WebSocket, + onMessage: (message: { data: any }) => void, + onOpen: (messenger: Messenger) => void, + onClose: (messenger: Messenger) => void + ) { + super(); + this.websocket = websocket; + + websocket.addEventListener("close", () => { + onClose(this); + }); + + websocket.addEventListener("open", () => { + onOpen(this); + }); + + websocket.addEventListener("message", (event) => { + onMessage(event.data); + }); + } + + static async connect( + url: string, + sessionId: string, + onMessage: (message: { data: any }) => void, + onOpen: (messenger: Messenger) => void, + onClose: (messenger: Messenger) => void + ): Promise { + const ws = new WebSocket(url); + + return new Promise((resolve, reject) => { + ws.addEventListener("open", () => { + resolve(new WebsocketMessenger(ws, onMessage, onOpen, onClose)); + }); + }); + } + + send(data: string) { + this.websocket.send(JSON.stringify(data)); + } +} function useContinueWebsocket( serverUrl: string, - onMessage: (message: { data: any }) => void + onMessage: (message: { data: any }) => void, + useVscodeMessagePassing: boolean = true ) { const sessionId = useSelector((state: RootStore) => state.config.sessionId); - const [websocket, setWebsocket] = useState(undefined); + const [websocket, setWebsocket] = useState(undefined); async function connect() { while (!sessionId) { @@ -15,32 +102,36 @@ function useContinueWebsocket( } console.log("Creating websocket", sessionId); + console.log("Using vscode message passing", useVscodeMessagePassing); - const wsUrl = - serverUrl.replace("http", "ws") + - "/notebook/ws?session_id=" + - encodeURIComponent(sessionId); - - const ws = new WebSocket(wsUrl); - setWebsocket(ws); + const onClose = (messenger: Messenger) => { + console.log("Websocket closed"); + setWebsocket(undefined); + }; - // Set up callbacks - ws.onopen = () => { + const onOpen = (messenger: Messenger) => { console.log("Websocket opened"); - ws.send(JSON.stringify({ sessionId })); + messenger.send(JSON.stringify({ sessionId })); }; - ws.onmessage = (msg) => { - onMessage(msg); - console.log("Got message", msg); - }; + const url = + serverUrl.replace("http", "ws") + + "/notebook/ws?session_id=" + + encodeURIComponent(sessionId); - ws.onclose = (msg) => { - console.log("Websocket closed"); - setWebsocket(undefined); - }; + const messenger: Messenger = useVscodeMessagePassing + ? new VscodeMessenger(url, onMessage, onOpen, onClose) + : await WebsocketMessenger.connect( + url, + sessionId, + onMessage, + onOpen, + onClose + ); + + setWebsocket(messenger); - return ws; + return messenger; } async function getConnection() { diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 66829836..4192595c 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,46 @@ 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.onmessage = (event) => { + this._onMessage(event.data); + }; + this._ws.onclose = () => { + this._onClose(); + }; + this._ws.onopen = () => { + this._onOpen(); + }; + } + + public send(message: string) { + this._ws.send(message); + } + + public close() { + this._ws.close(); + } +} + let streamManager = new StreamManager(); export let debugPanelWebview: vscode.Webview | undefined; @@ -147,6 +188,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": { @@ -158,6 +232,27 @@ export function setupDebugPanel( }); 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": { sendTelemetryEvent(TelemetryEvent.GenerateIdeas); let resp = await debugApi.listtenDebugListPost({ -- cgit v1.2.3-70-g09d2 From dd5b9f6b7f08f178d6034a57f97faea38442eb0a Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 31 May 2023 16:13:01 -0400 Subject: checkpoint! protocol reform and it works now --- continuedev/src/continuedev/core/agent.py | 34 ++-- continuedev/src/continuedev/core/env.py | 4 + continuedev/src/continuedev/core/main.py | 14 +- continuedev/src/continuedev/core/policy.py | 17 +- continuedev/src/continuedev/core/sdk.py | 13 +- continuedev/src/continuedev/libs/steps/chroma.py | 2 +- .../src/continuedev/libs/steps/core/core.py | 33 ++-- .../src/continuedev/libs/steps/draft/dlt.py | 2 +- continuedev/src/continuedev/libs/steps/main.py | 20 +- .../src/continuedev/libs/steps/migration.py | 2 +- continuedev/src/continuedev/libs/steps/nate.py | 6 +- continuedev/src/continuedev/libs/steps/pytest.py | 2 +- continuedev/src/continuedev/libs/steps/ty.py | 2 +- continuedev/src/continuedev/server/ide.py | 50 ++--- continuedev/src/continuedev/server/main.py | 2 +- continuedev/src/continuedev/server/notebook.py | 207 +++++++-------------- .../src/continuedev/server/notebook_protocol.py | 28 +++ .../src/continuedev/server/session_manager.py | 101 ++++++++++ extension/package-lock.json | 19 ++ extension/package.json | 7 +- .../src/hooks/ContinueNotebookClientProtocol.ts | 13 ++ extension/react-app/src/hooks/messenger.ts | 108 +++++++++++ .../src/hooks/useContinueNotebookProtocol.ts | 49 +++++ extension/react-app/src/hooks/useWebsocket.ts | 171 +++-------------- extension/react-app/src/hooks/vscodeMessenger.ts | 68 +++++++ extension/react-app/src/tabs/notebook.tsx | 70 +++---- extension/react-app/src/vscode/index.ts | 1 + extension/react-app/tsconfig.json | 2 +- .../scripts/continuedev-0.1.0-py3-none-any.whl | Bin 53104 -> 56070 bytes extension/src/activation/activate.ts | 4 +- extension/src/activation/environmentSetup.ts | 104 +++++++---- extension/src/commands.ts | 4 +- extension/src/continueIdeClient.ts | 146 ++++----------- extension/src/debugPanel.ts | 26 ++- extension/src/extension.ts | 7 +- extension/src/test/runTest.ts | 30 +-- extension/src/util/messenger.ts | 108 +++++++++++ 37 files changed, 864 insertions(+), 612 deletions(-) create mode 100644 continuedev/src/continuedev/server/notebook_protocol.py create mode 100644 continuedev/src/continuedev/server/session_manager.py create mode 100644 extension/react-app/src/hooks/ContinueNotebookClientProtocol.ts create mode 100644 extension/react-app/src/hooks/messenger.ts create mode 100644 extension/react-app/src/hooks/useContinueNotebookProtocol.ts create mode 100644 extension/react-app/src/hooks/vscodeMessenger.ts create mode 100644 extension/src/util/messenger.ts (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/agent.py b/continuedev/src/continuedev/core/agent.py index 329e3d4c..7f7466a2 100644 --- a/continuedev/src/continuedev/core/agent.py +++ b/continuedev/src/continuedev/core/agent.py @@ -13,7 +13,6 @@ from .sdk import ContinueSDK class Agent(ContinueBaseModel): - llm: LLM policy: Policy ide: AbstractIdeProtocolServer history: History = History.from_empty() @@ -31,27 +30,24 @@ class Agent(ContinueBaseModel): def get_full_state(self) -> FullState: return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue) - def on_update(self, callback: Callable[["FullState"], None]): + def on_update(self, callback: Coroutine["FullState", None, None]): """Subscribe to changes to state""" self._on_update_callbacks.append(callback) - def update_subscribers(self): + async def update_subscribers(self): full_state = self.get_full_state() for callback in self._on_update_callbacks: - callback(full_state) - - def __get_step_params(self, step: "Step"): - return ContinueSDK(agent=self, llm=self.llm.with_system_message(step.system_message)) + await callback(full_state) def give_user_input(self, input: str, index: int): - self._user_input_queue.post(index, input) + self._user_input_queue.post(str(index), input) async def wait_for_user_input(self) -> str: self._active = False - self.update_subscribers() - user_input = await self._user_input_queue.get(self.history.current_index) + await self.update_subscribers() + user_input = await self._user_input_queue.get(str(self.history.current_index)) self._active = True - self.update_subscribers() + await self.update_subscribers() return user_input _manual_edits_buffer: List[FileEditWithFullContents] = [] @@ -62,9 +58,9 @@ class Agent(ContinueBaseModel): current_step = self.history.get_current().step self.history.step_back() if issubclass(current_step.__class__, ReversibleStep): - await current_step.reverse(self.__get_step_params(current_step)) + await current_step.reverse(ContinueSDK(self)) - self.update_subscribers() + await self.update_subscribers() except Exception as e: print(e) @@ -94,17 +90,17 @@ class Agent(ContinueBaseModel): # Run step self._step_depth += 1 - observation = await step(self.__get_step_params(step)) + observation = await step(ContinueSDK(self)) self._step_depth -= 1 # Add observation to history self.history.get_current().observation = observation # Update its description - step._set_description(await step.describe(self.llm)) + step._set_description(await step.describe(ContinueSDK(self))) # Call all subscribed callbacks - self.update_subscribers() + await self.update_subscribers() return observation @@ -138,7 +134,7 @@ class Agent(ContinueBaseModel): # Doing this so active can make it to the frontend after steps are done. But want better state syncing tools for callback in self._on_update_callbacks: - callback(None) + await callback(None) async def run_from_observation(self, observation: Observation): next_step = self.policy.next(self.history) @@ -158,7 +154,7 @@ class Agent(ContinueBaseModel): async def accept_user_input(self, user_input: str): self._main_user_input_queue.append(user_input) - self.update_subscribers() + await self.update_subscribers() if len(self._main_user_input_queue) > 1: return @@ -167,7 +163,7 @@ class Agent(ContinueBaseModel): # Just run the step that takes user input, and # then up to the policy to decide how to deal with it. self._main_user_input_queue.pop(0) - self.update_subscribers() + await self.update_subscribers() await self.run_from_step(UserInputStep(user_input=user_input)) while len(self._main_user_input_queue) > 0: diff --git a/continuedev/src/continuedev/core/env.py b/continuedev/src/continuedev/core/env.py index 6267ed60..edd3297c 100644 --- a/continuedev/src/continuedev/core/env.py +++ b/continuedev/src/continuedev/core/env.py @@ -8,6 +8,10 @@ def get_env_var(var_name: str): def save_env_var(var_name: str, var_value: str): + if not os.path.exists('.env'): + with open('.env', 'w') as f: + f.write(f'{var_name}="{var_value}"\n') + return with open('.env', 'r') as f: lines = f.readlines() with open('.env', 'w') as f: diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 51fcd299..6be5139b 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -72,7 +72,7 @@ class ContinueSDK: pass -class SequentialStep: +class Models: pass @@ -94,7 +94,7 @@ class Step(ContinueBaseModel): class Config: copy_on_model_validation = False - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: if self._description is not None: return self._description return "Running step: " + self.name @@ -135,6 +135,16 @@ class Step(ContinueBaseModel): return SequentialStep(steps=steps) +class SequentialStep(Step): + steps: list[Step] + hide: bool = True + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + for step in self.steps: + observation = await sdk.run_step(step) + return observation + + class ValidatorObservation(Observation): passed: bool observation: Observation diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index 07101576..c0ba0f4f 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -1,20 +1,16 @@ from typing import List, Tuple, Type - -from ..models.main import ContinueBaseModel - from ..libs.steps.ty import CreatePipelineStep from .main import Step, Validator, History, Policy from .observation import Observation, TracebackObservation, UserInputObservation from ..libs.steps.main import EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep, FasterEditHighlightedCodeStep, StarCoderEditHighlightedCodeStep from ..libs.steps.nate import WritePytestsStep, CreateTableStep -from ..libs.steps.chroma import AnswerQuestionChroma, EditFileChroma +# from ..libs.steps.chroma import AnswerQuestionChroma, EditFileChroma from ..libs.steps.continue_step import ContinueStepStep class DemoPolicy(Policy): ran_code_last: bool = False - cmd: str def next(self, history: History) -> Step: observation = history.last_observation() @@ -26,18 +22,15 @@ class DemoPolicy(Policy): return CreatePipelineStep() elif "/table" in observation.user_input: return CreateTableStep(sql_str=" ".join(observation.user_input.split(" ")[1:])) - elif "/ask" in observation.user_input: - return AnswerQuestionChroma(question=" ".join(observation.user_input.split(" ")[1:])) - elif "/edit" in observation.user_input: - return EditFileChroma(request=" ".join(observation.user_input.split(" ")[1:])) + # elif "/ask" in observation.user_input: + # return AnswerQuestionChroma(question=" ".join(observation.user_input.split(" ")[1:])) + # elif "/edit" in observation.user_input: + # return EditFileChroma(request=" ".join(observation.user_input.split(" ")[1:])) elif "/step" in observation.user_input: return ContinueStepStep(prompt=" ".join(observation.user_input.split(" ")[1:])) return StarCoderEditHighlightedCodeStep(user_input=observation.user_input) state = history.get_current() - if state is None or not self.ran_code_last: - self.ran_code_last = True - return RunCodeStep(cmd=self.cmd) if observation is not None and isinstance(observation, TracebackObservation): self.ran_code_last = False diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 750b335d..6ae0be04 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -4,6 +4,7 @@ from ..models.filesystem_edit import FileSystemEdit, AddFile, DeleteFile, AddDir from ..models.filesystem import RangeInFile from ..libs.llm import LLM from ..libs.llm.hf_inference_api import HuggingFaceInferenceAPI +from ..libs.llm.openai import OpenAI from .observation import Observation from ..server.ide_protocol import AbstractIdeProtocolServer from .main import History, Step @@ -29,20 +30,20 @@ class Models: 'HUGGING_FACE_TOKEN', 'Please enter your Hugging Face token') return HuggingFaceInferenceAPI(api_key=api_key) + async def gpt35(self): + api_key = await self.sdk.get_user_secret( + 'OPENAI_API_KEY', 'Please enter your OpenAI API key') + return OpenAI(api_key=api_key, default_model="gpt-3.5-turbo") + class ContinueSDK: """The SDK provided as parameters to a step""" - llm: LLM ide: AbstractIdeProtocolServer steps: ContinueSDKSteps models: Models __agent: Agent - def __init__(self, agent: Agent, llm: Union[LLM, None] = None): - if llm is None: - self.llm = agent.llm - else: - self.llm = llm + def __init__(self, agent: Agent): self.ide = agent.ide self.__agent = agent self.steps = ContinueSDKSteps(self) diff --git a/continuedev/src/continuedev/libs/steps/chroma.py b/continuedev/src/continuedev/libs/steps/chroma.py index f13a2bab..39424c5c 100644 --- a/continuedev/src/continuedev/libs/steps/chroma.py +++ b/continuedev/src/continuedev/libs/steps/chroma.py @@ -40,7 +40,7 @@ class AnswerQuestionChroma(Step): Here is the answer:""") - answer = sdk.llm.complete(prompt) + answer = (await sdk.models.gpt35()).complete(prompt) print(answer) self._answer = answer diff --git a/continuedev/src/continuedev/libs/steps/core/core.py b/continuedev/src/continuedev/libs/steps/core/core.py index 0338d635..14b3cb80 100644 --- a/continuedev/src/continuedev/libs/steps/core/core.py +++ b/continuedev/src/continuedev/libs/steps/core/core.py @@ -4,27 +4,18 @@ from textwrap import dedent from typing import Coroutine, List, Union from ...llm.prompt_utils import MarkdownStyleEncoderDecoder -from ...util.traceback_parsers import parse_python_traceback - from ....models.filesystem_edit import EditDiff, FileEditWithFullContents, FileSystemEdit from ....models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents -from ...llm import LLM from ....core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation -from ....core.main import Step +from ....core.main import Step, SequentialStep class ContinueSDK: pass -class SequentialStep(Step): - steps: list[Step] - hide: bool = True - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - for step in self.steps: - observation = await sdk.run_step(step) - return observation +class Models: + pass class ReversibleStep(Step): @@ -52,7 +43,7 @@ def ShellCommandsStep(Step): cwd: str | None = None name: str = "Run Shell Commands" - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return "\n".join(self.cmds) async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: @@ -81,13 +72,13 @@ class EditCodeStep(Step): _prompt: Union[str, None] = None _completion: Union[str, None] = None - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: if self._edit_diffs is None: return "Editing files: " + ", ".join(map(lambda rif: rif.filepath, self.range_in_files)) elif len(self._edit_diffs) == 0: return "No edits made" else: - return llm.complete(dedent(f"""{self._prompt}{self._completion} + return (await models.gpt35()).complete(dedent(f"""{self._prompt}{self._completion} Maximally concise summary of changes in bullet points (can use markdown): """)) @@ -102,7 +93,7 @@ class EditCodeStep(Step): code_string = enc_dec.encode() prompt = self.prompt.format(code=code_string) - completion = sdk.llm.complete(prompt) + completion = (await sdk.models.gpt35()).complete(prompt) # Temporarily doing this to generate description. self._prompt = prompt @@ -127,7 +118,7 @@ class EditFileStep(Step): prompt: str hide: bool = True - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return "Editing file: " + self.filepath async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: @@ -145,7 +136,7 @@ class ManualEditStep(ReversibleStep): hide: bool = True - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return "Manual edit step" # TODO - only handling FileEdit here, but need all other types of FileSystemEdits # Also requires the merge_file_edit function @@ -181,7 +172,7 @@ class UserInputStep(Step): name: str = "User Input" hide: bool = True - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return self.user_input async def run(self, sdk: ContinueSDK) -> Coroutine[UserInputObservation, None, None]: @@ -194,7 +185,7 @@ class WaitForUserInputStep(Step): _description: Union[str, None] = None - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return self.prompt async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: @@ -207,7 +198,7 @@ class WaitForUserConfirmationStep(Step): prompt: str name: str = "Waiting for user confirmation" - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return self.prompt async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: diff --git a/continuedev/src/continuedev/libs/steps/draft/dlt.py b/continuedev/src/continuedev/libs/steps/draft/dlt.py index 5ba5692a..460aa0cc 100644 --- a/continuedev/src/continuedev/libs/steps/draft/dlt.py +++ b/continuedev/src/continuedev/libs/steps/draft/dlt.py @@ -10,7 +10,7 @@ class SetupPipelineStep(Step): api_description: str # e.g. "I want to load data from the weatherapi.com API" async def run(self, sdk: ContinueSDK): - source_name = sdk.llm.complete( + source_name = (await sdk.models.gpt35()).complete( f"Write a snake_case name for the data source described by {self.api_description}: ").strip() filename = f'{source_name}.py' diff --git a/continuedev/src/continuedev/libs/steps/main.py b/continuedev/src/continuedev/libs/steps/main.py index c8a85800..70c0d4b8 100644 --- a/continuedev/src/continuedev/libs/steps/main.py +++ b/continuedev/src/continuedev/libs/steps/main.py @@ -11,7 +11,7 @@ from ...core.observation import Observation, TextObservation, TracebackObservati from ..llm.prompt_utils import MarkdownStyleEncoderDecoder from textwrap import dedent from ...core.main import Step -from ...core.sdk import ContinueSDK +from ...core.sdk import ContinueSDK, Models from ...core.observation import Observation import subprocess from .core.core import EditCodeStep @@ -20,7 +20,7 @@ from .core.core import EditCodeStep class RunCodeStep(Step): cmd: str - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return f"Ran command: `{self.cmd}`" async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: @@ -59,7 +59,7 @@ class RunCommandStep(Step): name: str = "Run command" _description: str = None - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: if self._description is not None: return self._description return self.cmd @@ -125,7 +125,7 @@ class FasterEditHighlightedCodeStep(Step): Here is the description of changes to make: """) - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return "Editing highlighted code" async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: @@ -154,7 +154,7 @@ class FasterEditHighlightedCodeStep(Step): for rif in rif_with_contents: rif_dict[rif.filepath] = rif.contents - completion = sdk.llm.complete(prompt) + completion = (await sdk.models.gpt35()).complete(prompt) # Temporarily doing this to generate description. self._prompt = prompt @@ -215,10 +215,10 @@ class FasterEditHighlightedCodeStep(Step): class StarCoderEditHighlightedCodeStep(Step): user_input: str - hide = True + hide = False _prompt: str = "{code}{user_request}" - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return "Editing highlighted code" async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: @@ -271,7 +271,7 @@ This is the user request: This is the code after being changed to perfectly satisfy the user request: """) - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return "Editing highlighted code" async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: @@ -293,7 +293,7 @@ This is the code after being changed to perfectly satisfy the user request: class FindCodeStep(Step): prompt: str - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return "Finding code" async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: @@ -307,7 +307,7 @@ class UserInputStep(Step): class SolveTracebackStep(Step): traceback: Traceback - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + async def describe(self, models: Models) -> Coroutine[str, None, None]: return f"```\n{self.traceback.full_traceback}\n```" async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: diff --git a/continuedev/src/continuedev/libs/steps/migration.py b/continuedev/src/continuedev/libs/steps/migration.py index f044a60f..7b70422d 100644 --- a/continuedev/src/continuedev/libs/steps/migration.py +++ b/continuedev/src/continuedev/libs/steps/migration.py @@ -15,7 +15,7 @@ class MigrationStep(Step): recent_edits = await sdk.ide.get_recent_edits(self.edited_file) recent_edits_string = "\n\n".join( map(lambda x: x.to_string(), recent_edits)) - description = await sdk.llm.complete(f"{recent_edits_string}\n\nGenerate a short description of the migration made in the above changes:\n") + description = await (await sdk.models.gpt35()).complete(f"{recent_edits_string}\n\nGenerate a short description of the migration made in the above changes:\n") await sdk.run_step(RunCommandStep(cmd=f"cd libs && poetry run alembic revision --autogenerate -m {description}")) migration_file = f"libs/alembic/versions/{?}.py" contents = await sdk.ide.readFile(migration_file) diff --git a/continuedev/src/continuedev/libs/steps/nate.py b/continuedev/src/continuedev/libs/steps/nate.py index a0e728e5..2f84e9d7 100644 --- a/continuedev/src/continuedev/libs/steps/nate.py +++ b/continuedev/src/continuedev/libs/steps/nate.py @@ -45,7 +45,7 @@ Here are additional instructions: Here is a complete set of pytest unit tests: """) - # tests = sdk.llm.complete(prompt) + # tests = (await sdk.models.gpt35()).complete(prompt) tests = ''' import pytest @@ -169,9 +169,9 @@ export class Order { tracking_number: string; }''' time.sleep(2) - # orm_entity = sdk.llm.complete( + # orm_entity = (await sdk.models.gpt35()).complete( # f"{self.sql_str}\n\nWrite a TypeORM entity called {entity_name} for this table, importing as necessary:") - # sdk.llm.complete("What is the name of the entity?") + # (await sdk.models.gpt35()).complete("What is the name of the entity?") await sdk.apply_filesystem_edit(AddFile(filepath=f"/Users/natesesti/Desktop/continue/extension/examples/python/MyProject/src/entity/{entity_name}.ts", content=orm_entity)) await sdk.ide.setFileOpen(f"/Users/natesesti/Desktop/continue/extension/examples/python/MyProject/src/entity/{entity_name}.ts", True) diff --git a/continuedev/src/continuedev/libs/steps/pytest.py b/continuedev/src/continuedev/libs/steps/pytest.py index b4e6dfd2..2e83ae2d 100644 --- a/continuedev/src/continuedev/libs/steps/pytest.py +++ b/continuedev/src/continuedev/libs/steps/pytest.py @@ -33,5 +33,5 @@ class WritePytestsStep(Step): Here is a complete set of pytest unit tests: """) - tests = sdk.llm.complete(prompt) + tests = (await sdk.models.gpt35()).complete(prompt) await sdk.apply_filesystem_edit(AddFile(filepath=path, content=tests)) diff --git a/continuedev/src/continuedev/libs/steps/ty.py b/continuedev/src/continuedev/libs/steps/ty.py index 5ff03f04..9dde7c86 100644 --- a/continuedev/src/continuedev/libs/steps/ty.py +++ b/continuedev/src/continuedev/libs/steps/ty.py @@ -18,7 +18,7 @@ class SetupPipelineStep(Step): api_description: str # e.g. "I want to load data from the weatherapi.com API" async def run(self, sdk: ContinueSDK): - # source_name = sdk.llm.complete( + # source_name = (await sdk.models.gpt35()).complete( # f"Write a snake_case name for the data source described by {self.api_description}: ").strip() filename = f'/Users/natesesti/Desktop/continue/extension/examples/python/{source_name}.py' diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index dd1dc463..50296841 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -1,5 +1,6 @@ # This is a separate server from server/main.py import asyncio +import json import os from typing import Any, Dict, List, Type, TypeVar, Union import uuid @@ -90,31 +91,33 @@ class IdeProtocolServer(AbstractIdeProtocolServer): def __init__(self, session_manager: SessionManager): self.session_manager = session_manager - async def _send_json(self, data: Any): - await self.websocket.send_json(data) + async def _send_json(self, message_type: str, data: Any): + await self.websocket.send_json({ + "messageType": message_type, + "data": data + }) async def _receive_json(self, message_type: str) -> Any: return await self.sub_queue.get(message_type) async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: - await self._send_json(data) + await self._send_json(message_type, data) resp = await self._receive_json(message_type) return resp_model.parse_obj(resp) - async def handle_json(self, data: Any): - t = data["messageType"] - if t == "openNotebook": + async def handle_json(self, message_type: str, data: Any): + if message_type == "openNotebook": await self.openNotebook() - elif t == "setFileOpen": + elif message_type == "setFileOpen": await self.setFileOpen(data["filepath"], data["open"]) - elif t == "fileEdits": + elif message_type == "fileEdits": fileEdits = list( map(lambda d: FileEditWithFullContents.parse_obj(d), data["fileEdits"])) self.onFileEdits(fileEdits) - elif t in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory"]: - self.sub_queue.post(t, data) + elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory"]: + self.sub_queue.post(message_type, data) else: - raise ValueError("Unknown message type", t) + raise ValueError("Unknown message type", message_type) # ------------------------------- # # Request actions in IDE, doesn't matter which Session @@ -123,24 +126,21 @@ class IdeProtocolServer(AbstractIdeProtocolServer): async def setFileOpen(self, filepath: str, open: bool = True): # Agent needs access to this. - await self.websocket.send_json({ - "messageType": "setFileOpen", + await self._send_json("setFileOpen", { "filepath": filepath, "open": open }) async def openNotebook(self): session_id = self.session_manager.new_session(self) - await self._send_json({ - "messageType": "openNotebook", + await self._send_json("openNotebook", { "sessionId": session_id }) async def showSuggestionsAndWait(self, suggestions: List[FileEdit]) -> bool: ids = [str(uuid.uuid4()) for _ in suggestions] for i in range(len(suggestions)): - self._send_json({ - "messageType": "showSuggestion", + self._send_json("showSuggestion", { "suggestion": suggestions[i], "suggestionId": ids[i] }) @@ -210,8 +210,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): async def saveFile(self, filepath: str): """Save a file""" - await self._send_json({ - "messageType": "saveFile", + await self._send_json("saveFile", { "filepath": filepath }) @@ -293,10 +292,17 @@ ideProtocolServer = IdeProtocolServer(session_manager) async def websocket_endpoint(websocket: WebSocket): await websocket.accept() print("Accepted websocket connection from, ", websocket.client) - await websocket.send_json({"messageType": "connected"}) + await websocket.send_json({"messageType": "connected", "data": {}}) ideProtocolServer.websocket = websocket while True: - data = await websocket.receive_json() - await ideProtocolServer.handle_json(data) + message = await websocket.receive_text() + message = json.loads(message) + + if "messageType" not in message or "data" not in message: + continue + message_type = message["messageType"] + data = message["data"] + + await ideProtocolServer.handle_json(message_type, data) await websocket.close() diff --git a/continuedev/src/continuedev/server/main.py b/continuedev/src/continuedev/server/main.py index 11ad1d8f..e87d5fa9 100644 --- a/continuedev/src/continuedev/server/main.py +++ b/continuedev/src/continuedev/server/main.py @@ -32,7 +32,7 @@ args = parser.parse_args() def run_server(): - uvicorn.run(app, host="0.0.0.0", port=args.port, log_config="logging.ini") + uvicorn.run(app, host="0.0.0.0", port=args.port) if __name__ == "__main__": diff --git a/continuedev/src/continuedev/server/notebook.py b/continuedev/src/continuedev/server/notebook.py index c5dcea31..edb61a45 100644 --- a/continuedev/src/continuedev/server/notebook.py +++ b/continuedev/src/continuedev/server/notebook.py @@ -1,18 +1,12 @@ -from fastapi import FastAPI, Depends, Header, WebSocket, APIRouter -from typing import Any, Dict, List, Union -from uuid import uuid4 +import json +from fastapi import Depends, Header, WebSocket, APIRouter +from typing import Any, Type, TypeVar, Union from pydantic import BaseModel from uvicorn.main import Server -from ..models.filesystem_edit import FileEditWithFullContents -from ..core.policy import DemoPolicy -from ..core.main import FullState, History, Step -from ..core.agent import Agent -from ..libs.steps.nate import ImplementAbstractMethodStep -from ..core.observation import Observation -from ..libs.llm.openai import OpenAI -from .ide_protocol import AbstractIdeProtocolServer -from ..core.env import get_env_var +from .session_manager import SessionManager, session_manager, Session +from .notebook_protocol import AbstractNotebookProtocolServer +from ..libs.util.queue import AsyncSubscriptionQueue import asyncio import nest_asyncio nest_asyncio.apply() @@ -36,160 +30,99 @@ class AppStatus: Server.handle_exit = AppStatus.handle_exit -class Session: - session_id: str - agent: Agent - ws: Union[WebSocket, None] - - def __init__(self, session_id: str, agent: Agent): - self.session_id = session_id - self.agent = agent - self.ws = None - - -class DemoAgent(Agent): - first_seen: bool = False - cumulative_edit_string = "" - - def handle_manual_edits(self, edits: List[FileEditWithFullContents]): - for edit in edits: - self.cumulative_edit_string += edit.fileEdit.replacement - self._manual_edits_buffer.append(edit) - # Note that you're storing a lot of unecessary data here. Can compress into EditDiffs on the spot, and merge. - # self._manual_edits_buffer = merge_file_edit(self._manual_edits_buffer, edit) - # FOR DEMO PURPOSES - if edit.fileEdit.filepath.endswith("filesystem.py") and "List" in self.cumulative_edit_string and ":" in edit.fileEdit.replacement: - self.cumulative_edit_string = "" - asyncio.create_task(self.run_from_step( - ImplementAbstractMethodStep())) - - -class SessionManager: - sessions: Dict[str, Session] = {} - _event_loop: Union[asyncio.BaseEventLoop, None] = None - - def get_session(self, session_id: str) -> Session: - if session_id not in self.sessions: - raise KeyError("Session ID not recognized") - return self.sessions[session_id] - - def new_session(self, ide: AbstractIdeProtocolServer) -> str: - cmd = "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py" - agent = DemoAgent(llm=OpenAI(api_key=get_env_var("OPENAI_API_KEY")), - policy=DemoPolicy(cmd=cmd), ide=ide) - session_id = str(uuid4()) - session = Session(session_id=session_id, agent=agent) - self.sessions[session_id] = session +def session(x_continue_session_id: str = Header("anonymous")) -> Session: + return session_manager.get_session(x_continue_session_id) - def on_update(state: FullState): - session_manager.send_ws_data(session_id, { - "messageType": "state", - "state": agent.get_full_state().dict() - }) - agent.on_update(on_update) - asyncio.create_task(agent.run_policy()) - return session_id +def websocket_session(session_id: str) -> Session: + return session_manager.get_session(session_id) - def remove_session(self, session_id: str): - del self.sessions[session_id] - def register_websocket(self, session_id: str, ws: WebSocket): - self.sessions[session_id].ws = ws - print("Registered websocket for session", session_id) +T = TypeVar("T", bound=BaseModel) - def send_ws_data(self, session_id: str, data: Any): - if self.sessions[session_id].ws is None: - print(f"Session {session_id} has no websocket") - return +# You should probably abstract away the websocket stuff into a separate class - async def a(): - await self.sessions[session_id].ws.send_json(data) - # Run coroutine in background - if self._event_loop is None or self._event_loop.is_closed(): - self._event_loop = asyncio.new_event_loop() - self._event_loop.run_until_complete(a()) - self._event_loop.close() - else: - self._event_loop.run_until_complete(a()) - self._event_loop.close() +class NotebookProtocolServer(AbstractNotebookProtocolServer): + websocket: WebSocket + session: Session + sub_queue: AsyncSubscriptionQueue = AsyncSubscriptionQueue() + def __init__(self, session: Session): + self.session = session -session_manager = SessionManager() + async def _send_json(self, data: Any): + await self.websocket.send_json(data) + async def _receive_json(self, message_type: str) -> Any: + return await self.sub_queue.get(message_type) -def session(x_continue_session_id: str = Header("anonymous")) -> Session: - return session_manager.get_session(x_continue_session_id) + async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: + await self._send_json(data) + resp = await self._receive_json(message_type) + return resp_model.parse_obj(resp) + def handle_json(self, message_type: str, data: Any): + try: + if message_type == "main_input": + self.on_main_input(data["input"]) + elif message_type == "step_user_input": + self.on_step_user_input(data["input"], data["index"]) + elif message_type == "refinement_input": + self.on_refinement_input(data["input"], data["index"]) + elif message_type == "reverse_to_index": + self.on_reverse_to_index(data["index"]) + except Exception as e: + print(e) -def websocket_session(session_id: str) -> Session: - return session_manager.get_session(session_id) + async def send_state_update(self): + state = self.session.agent.get_full_state().dict() + await self._send_json({ + "messageType": "state_update", + "state": state + }) + def on_main_input(self, input: str): + # Do something with user input + asyncio.create_task(self.session.agent.accept_user_input(input)) -class StartSessionBody(BaseModel): - config_file_path: Union[str, None] + def on_reverse_to_index(self, index: int): + # Reverse the history to the given index + asyncio.create_task(self.session.agent.reverse_to_index(index)) + def on_step_user_input(self, input: str, index: int): + asyncio.create_task( + self.session.agent.give_user_input(input, index)) -class StartSessionResp(BaseModel): - session_id: str + def on_refinement_input(self, input: str, index: int): + asyncio.create_task( + self.session.agent.accept_refinement_input(input, index)) @router.websocket("/ws") async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(websocket_session)): await websocket.accept() + print("Session started") session_manager.register_websocket(session.session_id, websocket) - data = await websocket.receive_text() + protocol = NotebookProtocolServer(session) + protocol.websocket = websocket + # Update any history that may have happened before connection - await websocket.send_json({ - "messageType": "state", - "state": session_manager.get_session(session.session_id).agent.get_full_state().dict() - }) - print("Session started", data) + await protocol.send_state_update() + while AppStatus.should_exit is False: - data = await websocket.receive_json() - print("Received data", data) + message = await websocket.receive_json() + print("Received message", message) + if type(message) is str: + message = json.loads(message) - if "messageType" not in data: + if "messageType" not in message or "data" not in message: continue - messageType = data["messageType"] + message_type = message["messageType"] + data = message["data"] - try: - if messageType == "main_input": - # Do something with user input - asyncio.create_task( - session.agent.accept_user_input(data["value"])) - elif messageType == "step_user_input": - asyncio.create_task( - session.agent.give_user_input(data["value"], data["index"])) - elif messageType == "refinement_input": - asyncio.create_task( - session.agent.accept_refinement_input(data["value"], data["index"])) - elif messageType == "reverse": - # Reverse the history to the given index - asyncio.create_task( - session.agent.reverse_to_index(data["index"])) - except Exception as e: - print(e) + protocol.handle_json(message_type, data) print("Closing websocket") await websocket.close() - - -@router.post("/run") -def request_run(step: Step, session=Depends(session)): - """Tell an agent to take a specific action.""" - asyncio.create_task(session.agent.run_from_step(step)) - return "Success" - - -@router.get("/history") -def get_history(session=Depends(session)) -> History: - return session.agent.history - - -@router.post("/observation") -def post_observation(observation: Observation, session=Depends(session)): - asyncio.create_task(session.agent.run_from_observation(observation)) - return "Success" diff --git a/continuedev/src/continuedev/server/notebook_protocol.py b/continuedev/src/continuedev/server/notebook_protocol.py new file mode 100644 index 00000000..c2be82e0 --- /dev/null +++ b/continuedev/src/continuedev/server/notebook_protocol.py @@ -0,0 +1,28 @@ +from typing import Any +from abc import ABC, abstractmethod + + +class AbstractNotebookProtocolServer(ABC): + @abstractmethod + async def handle_json(self, data: Any): + """Handle a json message""" + + @abstractmethod + def on_main_input(self, input: str): + """Called when the user inputs something""" + + @abstractmethod + def on_reverse_to_index(self, index: int): + """Called when the user requests reverse to a previous index""" + + @abstractmethod + def on_refinement_input(self, input: str, index: int): + """Called when the user inputs a refinement""" + + @abstractmethod + def on_step_user_input(self, input: str, index: int): + """Called when the user inputs a step""" + + @abstractmethod + async def send_state_update(self, state: dict): + """Send a state update to the client""" diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py new file mode 100644 index 00000000..b48c21b7 --- /dev/null +++ b/continuedev/src/continuedev/server/session_manager.py @@ -0,0 +1,101 @@ +from fastapi import WebSocket +from typing import Any, Dict, List, Union +from uuid import uuid4 + +from ..models.filesystem_edit import FileEditWithFullContents +from ..core.policy import DemoPolicy +from ..core.main import FullState +from ..core.agent import Agent +from ..libs.steps.nate import ImplementAbstractMethodStep +from .ide_protocol import AbstractIdeProtocolServer +import asyncio +import nest_asyncio +nest_asyncio.apply() + + +class Session: + session_id: str + agent: Agent + ws: Union[WebSocket, None] + + def __init__(self, session_id: str, agent: Agent): + self.session_id = session_id + self.agent = agent + self.ws = None + + +class DemoAgent(Agent): + first_seen: bool = False + cumulative_edit_string = "" + + def handle_manual_edits(self, edits: List[FileEditWithFullContents]): + for edit in edits: + self.cumulative_edit_string += edit.fileEdit.replacement + self._manual_edits_buffer.append(edit) + # Note that you're storing a lot of unecessary data here. Can compress into EditDiffs on the spot, and merge. + # self._manual_edits_buffer = merge_file_edit(self._manual_edits_buffer, edit) + # FOR DEMO PURPOSES + if edit.fileEdit.filepath.endswith("filesystem.py") and "List" in self.cumulative_edit_string and ":" in edit.fileEdit.replacement: + self.cumulative_edit_string = "" + asyncio.create_task(self.run_from_step( + ImplementAbstractMethodStep())) + + +class SessionManager: + sessions: Dict[str, Session] = {} + _event_loop: Union[asyncio.BaseEventLoop, None] = None + + def get_session(self, session_id: str) -> Session: + if session_id not in self.sessions: + raise KeyError("Session ID not recognized") + return self.sessions[session_id] + + def new_session(self, ide: AbstractIdeProtocolServer) -> str: + agent = DemoAgent(policy=DemoPolicy(), ide=ide) + session_id = str(uuid4()) + session = Session(session_id=session_id, agent=agent) + self.sessions[session_id] = session + + async def on_update(state: FullState): + await session_manager.send_ws_data(session_id, "state_update", { + "state": agent.get_full_state().dict() + }) + + agent.on_update(on_update) + asyncio.create_task(agent.run_policy()) + return session_id + + def remove_session(self, session_id: str): + del self.sessions[session_id] + + def register_websocket(self, session_id: str, ws: WebSocket): + self.sessions[session_id].ws = ws + print("Registered websocket for session", session_id) + + async def send_ws_data(self, session_id: str, message_type: str, data: Any): + if self.sessions[session_id].ws is None: + print(f"Session {session_id} has no websocket") + return + + async def a(): + await self.sessions[session_id].ws.send_json({ + "messageType": message_type, + "data": data + }) + + # Run coroutine in background + await self.sessions[session_id].ws.send_json({ + "messageType": message_type, + "data": data + }) + return + if self._event_loop is None or self._event_loop.is_closed(): + self._event_loop = asyncio.new_event_loop() + self._event_loop.run_until_complete(a()) + self._event_loop.close() + else: + self._event_loop.run_until_complete(a()) + self._event_loop.close() + + +session_manager = SessionManager() diff --git a/extension/package-lock.json b/extension/package-lock.json index 20ac24be..04af09d3 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -28,6 +28,7 @@ "@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", @@ -2027,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", @@ -9246,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", diff --git a/extension/package.json b/extension/package.json index dc0192c3..c96655a9 100644 --- a/extension/package.json +++ b/extension/package.json @@ -148,7 +148,7 @@ }, "scripts": { "vscode:prepublish": "npm run esbuild-base -- --minify", - "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node", + "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 ./", @@ -160,9 +160,9 @@ "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 && mkdir -p ./build && vsce package --out ./build && chmod 777 ./build/continue-0.0.2.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 && chmod 777 ./build/continue-0.0.5.vsix && 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", + "install-extension": "code --install-extension ./build/continue-0.0.5.vsix", "uninstall": "code --uninstall-extension .continue", "reinstall": "rm -rf ./build && npm run package && npm run uninstall && npm run install-extension" }, @@ -173,6 +173,7 @@ "@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", diff --git a/extension/react-app/src/hooks/ContinueNotebookClientProtocol.ts b/extension/react-app/src/hooks/ContinueNotebookClientProtocol.ts new file mode 100644 index 00000000..75fd7373 --- /dev/null +++ b/extension/react-app/src/hooks/ContinueNotebookClientProtocol.ts @@ -0,0 +1,13 @@ +abstract class AbstractContinueNotebookClientProtocol { + 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 AbstractContinueNotebookClientProtocol; 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; +} + +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 { + 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/useContinueNotebookProtocol.ts b/extension/react-app/src/hooks/useContinueNotebookProtocol.ts new file mode 100644 index 00000000..d5ffbf09 --- /dev/null +++ b/extension/react-app/src/hooks/useContinueNotebookProtocol.ts @@ -0,0 +1,49 @@ +import AbstractContinueNotebookClientProtocol from "./ContinueNotebookClientProtocol"; +// import { Messenger, WebsocketMessenger } from "../../../src/util/messenger"; +import { Messenger, WebsocketMessenger } from "./messenger"; +import { VscodeMessenger } from "./vscodeMessenger"; + +class ContinueNotebookClientProtocol extends AbstractContinueNotebookClientProtocol { + messenger: Messenger; + // Server URL must contain the session ID param + serverUrlWithSessionId: string; + + constructor( + serverUrlWithSessionId: string, + useVscodeMessagePassing: boolean = false + ) { + 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 ContinueNotebookClientProtocol; diff --git a/extension/react-app/src/hooks/useWebsocket.ts b/extension/react-app/src/hooks/useWebsocket.ts index 6e8e68fa..b98be577 100644 --- a/extension/react-app/src/hooks/useWebsocket.ts +++ b/extension/react-app/src/hooks/useWebsocket.ts @@ -1,158 +1,39 @@ import React, { useEffect, useState } from "react"; import { RootStore } from "../redux/store"; import { useSelector } from "react-redux"; +import ContinueNotebookClientProtocol from "./useContinueNotebookProtocol"; import { postVscMessage } from "../vscode"; -abstract class Messenger { - abstract send(data: string): void; -} - -class VscodeMessenger extends Messenger { - url: string; - - constructor( - url: string, - onMessage: (message: { data: any }) => void, - onOpen: (messenger: Messenger) => void, - onClose: (messenger: Messenger) => void - ) { - super(); - this.url = url; - window.addEventListener("message", (event: any) => { - switch (event.data.type) { - case "websocketForwardingMessage": - onMessage(event.data); - break; - case "websocketForwardingOpen": - onOpen(this); - break; - case "websocketForwardingClose": - onClose(this); - break; - } - }); - - postVscMessage("websocketForwardingOpen", { url: this.url }); - } - - send(data: string) { - postVscMessage("websocketForwardingMessage", { - message: data, - url: this.url, - }); - } -} - -class WebsocketMessenger extends Messenger { - websocket: WebSocket; - constructor( - websocket: WebSocket, - onMessage: (message: { data: any }) => void, - onOpen: (messenger: Messenger) => void, - onClose: (messenger: Messenger) => void - ) { - super(); - this.websocket = websocket; - - websocket.addEventListener("close", () => { - onClose(this); - }); - - websocket.addEventListener("open", () => { - onOpen(this); - }); - - websocket.addEventListener("message", (event) => { - onMessage(event.data); - }); - } - - static async connect( - url: string, - sessionId: string, - onMessage: (message: { data: any }) => void, - onOpen: (messenger: Messenger) => void, - onClose: (messenger: Messenger) => void - ): Promise { - const ws = new WebSocket(url); - - return new Promise((resolve, reject) => { - ws.addEventListener("open", () => { - resolve(new WebsocketMessenger(ws, onMessage, onOpen, onClose)); - }); - }); - } - - send(data: string) { - this.websocket.send(JSON.stringify(data)); - } -} - -function useContinueWebsocket( - serverUrl: string, - onMessage: (message: { data: any }) => void, - useVscodeMessagePassing: boolean = true -) { +function useContinueNotebookProtocol(useVscodeMessagePassing: boolean = false) { const sessionId = useSelector((state: RootStore) => state.config.sessionId); - const [websocket, setWebsocket] = useState(undefined); + const serverHttpUrl = useSelector((state: RootStore) => state.config.apiUrl); + const [client, setClient] = useState< + ContinueNotebookClientProtocol | 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); - console.log("Using vscode message passing", useVscodeMessagePassing); - - const onClose = (messenger: Messenger) => { - console.log("Websocket closed"); - setWebsocket(undefined); - }; - - const onOpen = (messenger: Messenger) => { - console.log("Websocket opened"); - messenger.send(JSON.stringify({ sessionId })); - }; - - const url = - serverUrl.replace("http", "ws") + + const serverUrlWithSessionId = + serverHttpUrl.replace("http", "ws") + "/notebook/ws?session_id=" + encodeURIComponent(sessionId); - const messenger: Messenger = useVscodeMessagePassing - ? new VscodeMessenger(url, onMessage, onOpen, onClose) - : await WebsocketMessenger.connect( - url, - sessionId, - onMessage, - onOpen, - onClose - ); - - setWebsocket(messenger); - - return messenger; - } - - 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]); - - return { send }; + console.log("Creating websocket", serverUrlWithSessionId); + console.log("Using vscode message passing", useVscodeMessagePassing); + const newClient = new ContinueNotebookClientProtocol( + serverUrlWithSessionId, + useVscodeMessagePassing + ); + setClient(newClient); + }, [sessionId, serverHttpUrl]); + + return client; } -export default useContinueWebsocket; +export default useContinueNotebookProtocol; diff --git a/extension/react-app/src/hooks/vscodeMessenger.ts b/extension/react-app/src/hooks/vscodeMessenger.ts new file mode 100644 index 00000000..746c4302 --- /dev/null +++ b/extension/react-app/src/hooks/vscodeMessenger.ts @@ -0,0 +1,68 @@ +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") { + if (event.data.message.messageType === messageType) { + callback(event.data.message.data); + } + } + }); + } + + onMessage(callback: (messageType: string, data: any) => void): void { + window.addEventListener("message", (event: any) => { + if (event.data.type === "websocketForwardingMessage") { + callback(event.data.message.messageType, event.data.message.data); + } + }); + } + + sendAndReceive(messageType: string, data: any): Promise { + return new Promise((resolve) => { + const handler = (event: any) => { + if (event.data.type === "websocketForwardingMessage") { + if (event.data.message.messageType === messageType) { + window.removeEventListener("message", handler); + resolve(event.data.message.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/tabs/notebook.tsx b/extension/react-app/src/tabs/notebook.tsx index a9c69c5b..02c9ff31 100644 --- a/extension/react-app/src/tabs/notebook.tsx +++ b/extension/react-app/src/tabs/notebook.tsx @@ -14,6 +14,7 @@ import StepContainer from "../components/StepContainer"; import { useSelector } from "react-redux"; import { RootStore } from "../redux/store"; import useContinueWebsocket from "../hooks/useWebsocket"; +import useContinueNotebookProtocol from "../hooks/useWebsocket"; let TopNotebookDiv = styled.div` display: grid; @@ -33,8 +34,6 @@ interface NotebookProps { } function Notebook(props: NotebookProps) { - const serverUrl = useSelector((state: RootStore) => state.config.apiUrl); - const [waitingForSteps, setWaitingForSteps] = useState(false); const [userInputQueue, setUserInputQueue] = useState([]); const [history, setHistory] = useState(); @@ -157,30 +156,17 @@ function Notebook(props: NotebookProps) { // } 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 = useContinueNotebookProtocol(); - // 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(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,20 @@ 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 ( + {typeof client === "undefined" && ( + <> + +

Server disconnected

+ + )} {history?.timeline.map((node: HistoryNode, index: number) => { return ( 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); }} /> ); 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/scripts/continuedev-0.1.0-py3-none-any.whl b/extension/scripts/continuedev-0.1.0-py3-none-any.whl index d1483db9..2019c904 100644 Binary files a/extension/scripts/continuedev-0.1.0-py3-none-any.whl and b/extension/scripts/continuedev-0.1.0-py3-none-any.whl differ diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index a0aa560b..712ffe13 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 ); diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 93a471ff..ad6ac71b 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -28,18 +28,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 +47,77 @@ async function setupPythonEnv() { } } let pipCmd = pythonCmd.endsWith("3") ? "pip3" : "pip"; + return [pythonCmd, pipCmd]; +} +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 envBinActivatePath = 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", + "bin", + "activate" + ); + return fs.existsSync(envBinActivatePath); +} + +async function setupPythonEnv() { + console.log("Setting up python env for Continue extension..."); + + // 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,29 +154,19 @@ 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; @@ -152,15 +180,18 @@ export async function startContinuePythonServer() { pythonCmd = "python"; } + // let command = `cd ${path.join( + // getExtensionUri().fsPath, + // "scripts" + // )} && ${activateCmd} && cd env/lib/python3.11/site-packages && ${pythonCmd} -m continuedev.server.main`; let command = `cd ${path.join( getExtensionUri().fsPath, "scripts" )} && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`; try { // exec(command); - let child = spawn(command, { + const child = spawn(command, { shell: true, - detached: true, }); child.stdout.on("data", (data: any) => { console.log(`stdout: ${data}`); @@ -194,11 +225,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..aeeb4b4f 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.openNotebook(); }, "continue.focusContinueInput": async () => { if (!debugPanelWebview) { - await ideProtocolClient?.openNotebook(); + await ideProtocolClient.openNotebook(); } debugPanelWebview?.postMessage({ type: "focusContinueInput", diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 35eb668d..477d1420 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 = new Map(); - private readonly _serverUrl: string; - private readonly _context: vscode.ExtensionContext; + private messenger: WebsocketMessenger | null = null; + private panels: Map = 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,125 +56,52 @@ class IdeProtocolClient { }; } ); - this.send("fileEdits", { fileEdits }); + this.messenger?.send("fileEdits", { fileEdits }); } else { this._makingEdit--; } }); } - async isConnected() { - if (this._ws === null || this._ws.readyState !== WebSocket.OPEN) { - let ws = new WebSocket(this._serverUrl); - ws.onclose = () => { - this._ws = null; - }; - ws.on("message", (data: any) => { - this.handleMessage(JSON.parse(data)); - }); - this._ws = ws; - - return new Promise((resolve, reject) => { - ws.addEventListener("open", () => { - 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 { - 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 { - 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 "connected": break; default: - throw Error("Unknown message type:" + message.messageType); + throw Error("Unknown message type:" + messageType); } } getWorkspaceDirectory() { @@ -209,17 +134,20 @@ class IdeProtocolClient { // Initiate Request closeNotebook(sessionId: string) { - this._panels.get(sessionId)?.dispose(); - this._panels.delete(sessionId); + 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; + if (this.messenger === null) { + console.log("MESSENGER IS NULL"); + } + const resp = await this.messenger?.sendAndReceive("openNotebook", {}); + const sessionId = resp.sessionId; console.log("SESSION ID", sessionId); - let column = getRightViewColumn(); + const column = getRightViewColumn(); const panel = vscode.window.createWebviewPanel( "continue.debugPanelView", "Continue", @@ -231,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 4192595c..a295085f 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -16,7 +16,6 @@ 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 = ""; @@ -108,15 +107,15 @@ class WebsocketConnection { this._onOpen = onOpen; this._onClose = onClose; - this._ws.onmessage = (event) => { + this._ws.addEventListener("message", (event) => { this._onMessage(event.data); - }; - this._ws.onclose = () => { + }); + this._ws.addEventListener("close", () => { this._onClose(); - }; - this._ws.onopen = () => { + }); + this._ws.addEventListener("open", () => { this._onOpen(); - }; + }); } public send(message: string) { @@ -230,6 +229,19 @@ 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; } 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; +} + +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 { + 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); + } +} -- cgit v1.2.3-70-g09d2 From a4e9e7764ee42a743dbfbaedb520cc70daa23ec4 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 31 May 2023 20:27:52 -0400 Subject: fixing up vscodemessenger --- continuedev/src/continuedev/server/main.py | 3 +- continuedev/src/continuedev/server/notebook.py | 14 ++--- .../src/continuedev/server/session_manager.py | 15 ------ extension/package-lock.json | 4 +- extension/package.json | 4 +- .../src/hooks/useContinueNotebookProtocol.ts | 2 +- extension/react-app/src/hooks/useWebsocket.ts | 2 +- extension/react-app/src/hooks/vscodeMessenger.ts | 14 +++-- .../scripts/continuedev-0.1.0-py3-none-any.whl | Bin 56070 -> 56086 bytes extension/src/activation/environmentSetup.ts | 59 +++++++++++---------- extension/src/debugPanel.ts | 5 ++ logging.ini | 27 ---------- logging.yaml | 30 +++++++++++ 13 files changed, 91 insertions(+), 88 deletions(-) delete mode 100644 logging.ini create mode 100644 logging.yaml (limited to 'extension/src') diff --git a/continuedev/src/continuedev/server/main.py b/continuedev/src/continuedev/server/main.py index e87d5fa9..1977bfdd 100644 --- a/continuedev/src/continuedev/server/main.py +++ b/continuedev/src/continuedev/server/main.py @@ -32,7 +32,8 @@ args = parser.parse_args() def run_server(): - uvicorn.run(app, host="0.0.0.0", port=args.port) + uvicorn.run(app, host="0.0.0.0", port=args.port, + log_config="logging.yaml") if __name__ == "__main__": diff --git a/continuedev/src/continuedev/server/notebook.py b/continuedev/src/continuedev/server/notebook.py index edb61a45..ab9211a8 100644 --- a/continuedev/src/continuedev/server/notebook.py +++ b/continuedev/src/continuedev/server/notebook.py @@ -51,14 +51,17 @@ class NotebookProtocolServer(AbstractNotebookProtocolServer): def __init__(self, session: Session): self.session = session - async def _send_json(self, data: Any): - await self.websocket.send_json(data) + async def _send_json(self, message_type: str, data: Any): + await self.websocket.send_json({ + "message_type": message_type, + "data": data + }) async def _receive_json(self, message_type: str) -> Any: return await self.sub_queue.get(message_type) async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: - await self._send_json(data) + await self._send_json(message_type, data) resp = await self._receive_json(message_type) return resp_model.parse_obj(resp) @@ -77,8 +80,7 @@ class NotebookProtocolServer(AbstractNotebookProtocolServer): async def send_state_update(self): state = self.session.agent.get_full_state().dict() - await self._send_json({ - "messageType": "state_update", + await self._send_json("state_update", { "state": state }) @@ -112,7 +114,7 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we await protocol.send_state_update() while AppStatus.should_exit is False: - message = await websocket.receive_json() + message = await websocket.receive_text() print("Received message", message) if type(message) is str: message = json.loads(message) diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py index b48c21b7..c5715034 100644 --- a/continuedev/src/continuedev/server/session_manager.py +++ b/continuedev/src/continuedev/server/session_manager.py @@ -77,25 +77,10 @@ class SessionManager: print(f"Session {session_id} has no websocket") return - async def a(): - await self.sessions[session_id].ws.send_json({ - "messageType": message_type, - "data": data - }) - - # Run coroutine in background await self.sessions[session_id].ws.send_json({ "messageType": message_type, "data": data }) - return - if self._event_loop is None or self._event_loop.is_closed(): - self._event_loop = asyncio.new_event_loop() - self._event_loop.run_until_complete(a()) - self._event_loop.close() - else: - self._event_loop.run_until_complete(a()) - self._event_loop.close() session_manager = SessionManager() diff --git a/extension/package-lock.json b/extension/package-lock.json index 04af09d3..cd956286 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.5", + "version": "0.0.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.5", + "version": "0.0.7", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index c96655a9..1a8d9004 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.5", + "version": "0.0.7", "publisher": "Continue", "engines": { "vscode": "^1.74.0" @@ -162,7 +162,7 @@ "test": "node ./out/test/runTest.js", "package": "cp ./config/prod_config.json ./config/config.json && mkdir -p ./build && vsce package --out ./build && chmod 777 ./build/continue-0.0.5.vsix && 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.5.vsix", + "install-extension": "code --install-extension ./build/continue-0.0.6.vsix", "uninstall": "code --uninstall-extension .continue", "reinstall": "rm -rf ./build && npm run package && npm run uninstall && npm run install-extension" }, diff --git a/extension/react-app/src/hooks/useContinueNotebookProtocol.ts b/extension/react-app/src/hooks/useContinueNotebookProtocol.ts index d5ffbf09..b785cc84 100644 --- a/extension/react-app/src/hooks/useContinueNotebookProtocol.ts +++ b/extension/react-app/src/hooks/useContinueNotebookProtocol.ts @@ -10,7 +10,7 @@ class ContinueNotebookClientProtocol extends AbstractContinueNotebookClientProto constructor( serverUrlWithSessionId: string, - useVscodeMessagePassing: boolean = false + useVscodeMessagePassing: boolean ) { super(); this.serverUrlWithSessionId = serverUrlWithSessionId; diff --git a/extension/react-app/src/hooks/useWebsocket.ts b/extension/react-app/src/hooks/useWebsocket.ts index b98be577..016fa17d 100644 --- a/extension/react-app/src/hooks/useWebsocket.ts +++ b/extension/react-app/src/hooks/useWebsocket.ts @@ -4,7 +4,7 @@ import { useSelector } from "react-redux"; import ContinueNotebookClientProtocol from "./useContinueNotebookProtocol"; import { postVscMessage } from "../vscode"; -function useContinueNotebookProtocol(useVscodeMessagePassing: boolean = false) { +function useContinueNotebookProtocol(useVscodeMessagePassing: boolean = true) { const sessionId = useSelector((state: RootStore) => state.config.sessionId); const serverHttpUrl = useSelector((state: RootStore) => state.config.apiUrl); const [client, setClient] = useState< diff --git a/extension/react-app/src/hooks/vscodeMessenger.ts b/extension/react-app/src/hooks/vscodeMessenger.ts index 746c4302..e330db57 100644 --- a/extension/react-app/src/hooks/vscodeMessenger.ts +++ b/extension/react-app/src/hooks/vscodeMessenger.ts @@ -21,8 +21,10 @@ export class VscodeMessenger extends Messenger { onMessageType(messageType: string, callback: (data: object) => void): void { window.addEventListener("message", (event: any) => { if (event.data.type === "websocketForwardingMessage") { - if (event.data.message.messageType === messageType) { - callback(event.data.message.data); + console.log("VS CODE SENT DATA: ", event.data); + const data = JSON.parse(event.data.data); + if (data.messageType === messageType) { + callback(data.data); } } }); @@ -31,7 +33,8 @@ export class VscodeMessenger extends Messenger { onMessage(callback: (messageType: string, data: any) => void): void { window.addEventListener("message", (event: any) => { if (event.data.type === "websocketForwardingMessage") { - callback(event.data.message.messageType, event.data.message.data); + const data = JSON.parse(event.data.data); + callback(data.messageType, data.data); } }); } @@ -40,9 +43,10 @@ export class VscodeMessenger extends Messenger { return new Promise((resolve) => { const handler = (event: any) => { if (event.data.type === "websocketForwardingMessage") { - if (event.data.message.messageType === messageType) { + const data = JSON.parse(event.data.data); + if (data.messageType === messageType) { window.removeEventListener("message", handler); - resolve(event.data.message.data); + resolve(data.data); } } }; 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 2019c904..8a35e0ec 100644 Binary files a/extension/scripts/continuedev-0.1.0-py3-none-any.whl and b/extension/scripts/continuedev-0.1.0-py3-none-any.whl differ diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index ad6ac71b..db2c5523 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 { @@ -61,14 +62,16 @@ function getActivateUpgradeCommands(pythonCmd: string, pipCmd: string) { } function checkEnvExists() { - const envBinActivatePath = path.join( + const envBinPath = path.join( getExtensionUri().fsPath, "scripts", "env", - "bin", - "activate" + "bin" + ); + return ( + fs.existsSync(envBinPath + "/activate") && + fs.existsSync(envBinPath + "/pip") ); - return fs.existsSync(envBinActivatePath); } async function setupPythonEnv() { @@ -180,34 +183,34 @@ export async function startContinuePythonServer() { pythonCmd = "python"; } - // let command = `cd ${path.join( - // getExtensionUri().fsPath, - // "scripts" - // )} && ${activateCmd} && cd env/lib/python3.11/site-packages && ${pythonCmd} -m continuedev.server.main`; let command = `cd ${path.join( getExtensionUri().fsPath, "scripts" )} && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`; - try { - // exec(command); - 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}`); - }); - 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() { diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index a295085f..da29a52c 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 = ""; @@ -119,6 +120,9 @@ class WebsocketConnection { } public send(message: string) { + if (typeof message !== "string") { + message = JSON.stringify(message); + } this._ws.send(message); } @@ -190,6 +194,7 @@ export function setupDebugPanel( async function connectWebsocket(url: string) { return new Promise((resolve, reject) => { const onMessage = (message: any) => { + console.log("websocket message", message); panel.webview.postMessage({ type: "websocketForwardingMessage", url, diff --git a/logging.ini b/logging.ini deleted file mode 100644 index 5b478619..00000000 --- a/logging.ini +++ /dev/null @@ -1,27 +0,0 @@ -[loggers] -keys=root - -[handlers] -keys=logfile,logconsole - -[formatters] -keys=logformatter - -[logger_root] -level=INFO -handlers=logfile, logconsole - -[formatter_logformatter] -format=[%(asctime)s.%(msecs)03d] %(levelname)s [%(thread)d] - %(message)s - -[handler_logfile] -class=handlers.RotatingFileHandler -level=INFO -args=('logfile.log','a') -formatter=logformatter - -[handler_logconsole] -class=handlers.logging.StreamHandler -level=INFO -args=() -formatter=logformatter \ No newline at end of file diff --git a/logging.yaml b/logging.yaml new file mode 100644 index 00000000..391041ef --- /dev/null +++ b/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 -- cgit v1.2.3-70-g09d2 From ea5d50af9ba84242c25e82069d86c08ac039e543 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 1 Jun 2023 00:09:19 -0400 Subject: Polishing for dlt codespace and !config! --- continuedev/src/continuedev/core/config.py | 29 ++++++++++++++++++++ continuedev/src/continuedev/core/policy.py | 9 +++++-- continuedev/src/continuedev/core/sdk.py | 17 ++++++++++++ .../src/continuedev/libs/steps/draft/dlt.py | 4 +-- continuedev/src/continuedev/libs/steps/main.py | 20 ++++++++++++++ .../src/continuedev/libs/steps/steps_on_startup.py | 30 +++++++++++++++++++++ continuedev/src/continuedev/server/main.py | 8 ++++-- continuedev/src/continuedev/server/notebook.py | 2 +- extension/react-app/src/hooks/vscodeMessenger.ts | 1 - .../scripts/continuedev-0.1.0-py3-none-any.whl | Bin 56086 -> 57363 bytes extension/src/activation/activate.ts | 6 ++--- extension/src/activation/environmentSetup.ts | 2 ++ 12 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 continuedev/src/continuedev/core/config.py create mode 100644 continuedev/src/continuedev/libs/steps/steps_on_startup.py (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py new file mode 100644 index 00000000..e62f0e4f --- /dev/null +++ b/continuedev/src/continuedev/core/config.py @@ -0,0 +1,29 @@ +import json +import os +from pydantic import BaseModel +from typing import List, Optional, Dict +import yaml + + +class ContinueConfig(BaseModel): + """ + A pydantic class for the continue config file. + """ + steps_on_startup: Optional[Dict[str, Dict]] = {} + server_url: Optional[str] = None + + +def load_config(config_file: str) -> ContinueConfig: + """ + Load the config file and return a ContinueConfig object. + """ + _, ext = os.path.splitext(config_file) + if ext == '.yaml': + with open(config_file, 'r') as f: + config_dict = yaml.safe_load(f) + elif ext == '.json': + with open(config_file, 'r') as f: + config_dict = json.load(f) + else: + raise ValueError(f'Unknown config file extension: {ext}') + return ContinueConfig(**config_dict) diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index c0ba0f4f..9f68515f 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -1,9 +1,10 @@ from typing import List, Tuple, Type -from ..libs.steps.ty import CreatePipelineStep +from ..libs.steps.steps_on_startup import StepsOnStartupStep +from ..libs.steps.draft.dlt import CreatePipelineStep from .main import Step, Validator, History, Policy from .observation import Observation, TracebackObservation, UserInputObservation -from ..libs.steps.main import EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep, FasterEditHighlightedCodeStep, StarCoderEditHighlightedCodeStep +from ..libs.steps.main import EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep, FasterEditHighlightedCodeStep, StarCoderEditHighlightedCodeStep, MessageStep, EmptyStep from ..libs.steps.nate import WritePytestsStep, CreateTableStep # from ..libs.steps.chroma import AnswerQuestionChroma, EditFileChroma from ..libs.steps.continue_step import ContinueStepStep @@ -13,6 +14,10 @@ class DemoPolicy(Policy): ran_code_last: bool = False def next(self, history: History) -> Step: + # At the very start, run initial Steps spcecified in the config + if history.get_current() is None: + return MessageStep(message="Welcome to Continue!") >> StepsOnStartupStep() + observation = history.last_observation() if observation is not None and isinstance(observation, UserInputObservation): # This could be defined with ObservationTypePolicy. Ergonomics not right though. diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 6ae0be04..4d82a1ae 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -1,5 +1,7 @@ import os from typing import Coroutine, Union + +from .config import ContinueConfig, load_config from ..models.filesystem_edit import FileSystemEdit, AddFile, DeleteFile, AddDirectory, DeleteDirectory from ..models.filesystem import RangeInFile from ..libs.llm import LLM @@ -106,3 +108,18 @@ class ContinueSDK: val = (await self.run_step(WaitForUserInputStep(prompt=prompt))).text save_env_var(env_var, val) return val + + async def get_config(self) -> ContinueConfig: + dir = await self.ide.getWorkspaceDirectory() + yaml_path = os.path.join(dir, 'continue.yaml') + json_path = os.path.join(dir, 'continue.json') + if os.path.exists(yaml_path): + return load_config(yaml_path) + elif os.path.exists(json_path): + return load_config(json_path) + else: + return ContinueConfig() + + def set_loading_message(self, message: str): + # self.__agent.set_loading_message(message) + raise NotImplementedError() diff --git a/continuedev/src/continuedev/libs/steps/draft/dlt.py b/continuedev/src/continuedev/libs/steps/draft/dlt.py index 460aa0cc..778ced1d 100644 --- a/continuedev/src/continuedev/libs/steps/draft/dlt.py +++ b/continuedev/src/continuedev/libs/steps/draft/dlt.py @@ -2,7 +2,7 @@ from textwrap import dedent from ....models.filesystem_edit import AddFile from ....core.main import Step from ....core.sdk import ContinueSDK -from ..main import WaitForUserInputStep +from ..core.core import WaitForUserInputStep class SetupPipelineStep(Step): @@ -77,6 +77,6 @@ class CreatePipelineStep(Step): async def run(self, sdk: ContinueSDK): await sdk.run_step( WaitForUserInputStep(prompt="What API do you want to load data from?") >> - SetupPipelineStep() >> + SetupPipelineStep(api_description="WeatherAPI.com API") >> ValidatePipelineStep() ) diff --git a/continuedev/src/continuedev/libs/steps/main.py b/continuedev/src/continuedev/libs/steps/main.py index 70c0d4b8..d31db0eb 100644 --- a/continuedev/src/continuedev/libs/steps/main.py +++ b/continuedev/src/continuedev/libs/steps/main.py @@ -331,3 +331,23 @@ class SolveTracebackStep(Step): await sdk.run_step(EditCodeStep( range_in_files=range_in_files, prompt=prompt)) return None + + +class MessageStep(Step): + message: str + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return self.message + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + pass + + +class EmptyStep(Step): + hide: bool = True + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return "" + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + pass diff --git a/continuedev/src/continuedev/libs/steps/steps_on_startup.py b/continuedev/src/continuedev/libs/steps/steps_on_startup.py new file mode 100644 index 00000000..fd1eb8f0 --- /dev/null +++ b/continuedev/src/continuedev/libs/steps/steps_on_startup.py @@ -0,0 +1,30 @@ + + +from ...core.main import ContinueSDK, Models, Step +from .main import UserInputStep +from .draft.dlt import CreatePipelineStep + + +step_name_to_step_class = { + "UserInputStep": UserInputStep, + "CreatePipelineStep": CreatePipelineStep +} + + +class StepsOnStartupStep(Step): + hide: bool = True + + async def describe(self, models: Models): + return "Running steps on startup" + + async def run(self, sdk: ContinueSDK): + steps_descriptions = (await sdk.get_config()).steps_on_startup + + for step_name, step_params in steps_descriptions.items(): + try: + step = step_name_to_step_class[step_name](**step_params) + except: + print( + f"Incorrect parameters for step {step_name}. Parameters provided were: {step_params}") + continue + await sdk.run_step(step) diff --git a/continuedev/src/continuedev/server/main.py b/continuedev/src/continuedev/server/main.py index 1977bfdd..1ffe1450 100644 --- a/continuedev/src/continuedev/server/main.py +++ b/continuedev/src/continuedev/server/main.py @@ -1,3 +1,4 @@ +import os from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from .ide import router as ide_router @@ -32,8 +33,11 @@ args = parser.parse_args() def run_server(): - uvicorn.run(app, host="0.0.0.0", port=args.port, - log_config="logging.yaml") + if os.path.exists("logging.yaml"): + uvicorn.run(app, host="0.0.0.0", port=args.port, + log_config="logging.yaml") + else: + uvicorn.run(app, host="0.0.0.0", port=args.port) if __name__ == "__main__": diff --git a/continuedev/src/continuedev/server/notebook.py b/continuedev/src/continuedev/server/notebook.py index ab9211a8..9ca510dd 100644 --- a/continuedev/src/continuedev/server/notebook.py +++ b/continuedev/src/continuedev/server/notebook.py @@ -53,7 +53,7 @@ class NotebookProtocolServer(AbstractNotebookProtocolServer): async def _send_json(self, message_type: str, data: Any): await self.websocket.send_json({ - "message_type": message_type, + "messageType": message_type, "data": data }) diff --git a/extension/react-app/src/hooks/vscodeMessenger.ts b/extension/react-app/src/hooks/vscodeMessenger.ts index e330db57..ba19586b 100644 --- a/extension/react-app/src/hooks/vscodeMessenger.ts +++ b/extension/react-app/src/hooks/vscodeMessenger.ts @@ -21,7 +21,6 @@ export class VscodeMessenger extends Messenger { onMessageType(messageType: string, callback: (data: object) => void): void { window.addEventListener("message", (event: any) => { if (event.data.type === "websocketForwardingMessage") { - console.log("VS CODE SENT DATA: ", event.data); const data = JSON.parse(event.data.data); if (data.messageType === messageType) { callback(data.data); 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 8a35e0ec..6cda6f2d 100644 Binary files a/extension/scripts/continuedev-0.1.0-py3-none-any.whl and b/extension/scripts/continuedev-0.1.0-py3-none-any.whl differ diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 712ffe13..f8f3c65a 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -62,9 +62,9 @@ export function activateExtension( ideProtocolClient?.openNotebook(); }); } else { - // ideProtocolClient?.openNotebook().then(() => { - // // openCapturedTerminal(); - // }); + ideProtocolClient.openNotebook().then(() => { + // openCapturedTerminal(); + }); } extensionContext = context; diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index db2c5523..419d32ed 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -77,6 +77,8 @@ function checkEnvExists() { 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( -- cgit v1.2.3-70-g09d2 From da4647f2788a7b6d1e3b6ec76665052210311385 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 1 Jun 2023 16:04:03 -0400 Subject: fixing up CreatePipelineStep --- continuedev/src/continuedev/core/agent.py | 5 +++-- continuedev/src/continuedev/core/main.py | 13 ++++++++++++- continuedev/src/continuedev/core/sdk.py | 2 +- continuedev/src/continuedev/libs/steps/core/core.py | 2 +- continuedev/src/continuedev/libs/steps/draft/dlt.py | 14 +++++++++----- continuedev/src/continuedev/libs/steps/main.py | 2 +- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- extension/src/debugPanel.ts | 1 - 9 files changed, 30 insertions(+), 15 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/agent.py b/continuedev/src/continuedev/core/agent.py index 7f7466a2..0dba6122 100644 --- a/continuedev/src/continuedev/core/agent.py +++ b/continuedev/src/continuedev/core/agent.py @@ -94,10 +94,11 @@ class Agent(ContinueBaseModel): self._step_depth -= 1 # Add observation to history - self.history.get_current().observation = observation + self.history.get_last_at_depth( + self._step_depth, include_current=True).observation = observation # Update its description - step._set_description(await step.describe(ContinueSDK(self))) + step._set_description(await step.describe(ContinueSDK(self).models)) # Call all subscribed callbacks await self.update_subscribers() diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 6be5139b..a2336671 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -27,6 +27,17 @@ class History(ContinueBaseModel): return None return self.timeline[self.current_index] + def get_last_at_depth(self, depth: int, include_current: bool = False) -> Union[HistoryNode, None]: + i = self.current_index if include_current else self.current_index - 1 + while i >= 0: + if self.timeline[i].depth == depth and type(self.timeline[i].step).__name__ != "ManualEditStep": + return self.timeline[i] + i -= 1 + return None + + def get_last_at_same_depth(self) -> Union[HistoryNode, None]: + return self.get_last_at_depth(self.get_current().depth) + def remove_current_and_substeps(self): self.timeline.pop(self.current_index) while self.get_current() is not None and self.get_current().depth > 0: @@ -51,7 +62,7 @@ class History(ContinueBaseModel): self.current_index -= 1 def last_observation(self) -> Union[Observation, None]: - state = self.get_current() + state = self.get_last_at_same_depth() if state is None: return None return state.observation diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 4d82a1ae..b9b20422 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -74,7 +74,7 @@ class ContinueSDK: async def run(self, commands: List[str] | str, cwd: str = None): commands = commands if isinstance(commands, List) else [commands] - return await self.run_step(ShellCommandsStep(commands=commands, cwd=cwd)) + return await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd)) async def edit_file(self, filename: str, prompt: str): filepath = await self._ensure_absolute_path(filename) diff --git a/continuedev/src/continuedev/libs/steps/core/core.py b/continuedev/src/continuedev/libs/steps/core/core.py index 14b3cb80..1761cbfd 100644 --- a/continuedev/src/continuedev/libs/steps/core/core.py +++ b/continuedev/src/continuedev/libs/steps/core/core.py @@ -38,7 +38,7 @@ class FileSystemEditStep(ReversibleStep): # Where and when should file saves happen? -def ShellCommandsStep(Step): +class ShellCommandsStep(Step): cmds: List[str] cwd: str | None = None name: str = "Run Shell Commands" diff --git a/continuedev/src/continuedev/libs/steps/draft/dlt.py b/continuedev/src/continuedev/libs/steps/draft/dlt.py index 778ced1d..94ca2323 100644 --- a/continuedev/src/continuedev/libs/steps/draft/dlt.py +++ b/continuedev/src/continuedev/libs/steps/draft/dlt.py @@ -1,4 +1,6 @@ from textwrap import dedent + +from ....core.observation import DictObservation from ....models.filesystem_edit import AddFile from ....core.main import Step from ....core.sdk import ContinueSDK @@ -19,7 +21,7 @@ class SetupPipelineStep(Step): 'python3 -m venv env', 'source env/bin/activate', 'pip install dlt', - 'dlt init {source_name} duckdb', + f'dlt init {source_name} duckdb', 'Y', 'pip install -r requirements.txt' ]) @@ -31,15 +33,15 @@ class SetupPipelineStep(Step): ) # wait for user to put API key in secrets.toml - await sdk.ide.setFileOpen(".dlt/secrets.toml") + await sdk.ide.setFileOpen(await sdk.ide.getWorkspaceDirectory() + "/.dlt/secrets.toml") await sdk.wait_for_user_confirmation("Please add the API key to the `secrets.toml` file and then press `Continue`") - return {"source_name": source_name} + return DictObservation(values={"source_name": source_name}) class ValidatePipelineStep(Step): async def run(self, sdk: ContinueSDK): - source_name = sdk.history.last_observation()["source_name"] + source_name = sdk.history.last_observation().values["source_name"] filename = f'{source_name}.py' # test that the API call works @@ -68,7 +70,9 @@ class ValidatePipelineStep(Step): for row in rows: print(row) ''') - await sdk.apply_filesystem_edit(AddFile(filepath='query.py', content=tables_query_code)) + + query_filename = (await sdk.ide.getWorkspaceDirectory()) + "/query.py" + await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code)) await sdk.run('env/bin/python3 query.py') diff --git a/continuedev/src/continuedev/libs/steps/main.py b/continuedev/src/continuedev/libs/steps/main.py index d31db0eb..6a7f14c7 100644 --- a/continuedev/src/continuedev/libs/steps/main.py +++ b/continuedev/src/continuedev/libs/steps/main.py @@ -340,7 +340,7 @@ class MessageStep(Step): return self.message async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - pass + return TextObservation(text=self.message) class EmptyStep(Step): diff --git a/extension/package-lock.json b/extension/package-lock.json index 8662ef49..ef3a619b 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.9", + "version": "0.0.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.9", + "version": "0.0.10", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index f84585fb..6658f77a 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.9", + "version": "0.0.10", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index da29a52c..87c33da1 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -194,7 +194,6 @@ export function setupDebugPanel( async function connectWebsocket(url: string) { return new Promise((resolve, reject) => { const onMessage = (message: any) => { - console.log("websocket message", message); panel.webview.postMessage({ type: "websocketForwardingMessage", url, -- cgit v1.2.3-70-g09d2 From 5ce90853586fcfaa7d795e2593aaaecce4bbed49 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 1 Jun 2023 19:03:10 -0400 Subject: polishing dlt recipe --- continuedev/src/continuedev/core/agent.py | 13 ++++++++---- .../src/continuedev/libs/steps/core/core.py | 3 ++- .../src/continuedev/libs/steps/draft/dlt.py | 24 ++++++++++++++++++++-- continuedev/src/continuedev/libs/steps/main.py | 1 + .../react-app/src/components/StepContainer.tsx | 2 ++ extension/react-app/src/components/index.ts | 2 +- extension/src/activation/environmentSetup.ts | 6 +++--- 7 files changed, 40 insertions(+), 11 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/agent.py b/continuedev/src/continuedev/core/agent.py index 0dba6122..cf5c9781 100644 --- a/continuedev/src/continuedev/core/agent.py +++ b/continuedev/src/continuedev/core/agent.py @@ -10,6 +10,7 @@ from ..models.main import ContinueBaseModel from .main import Policy, History, FullState, Step, HistoryNode from ..libs.steps.core.core import ReversibleStep, ManualEditStep, UserInputStep from .sdk import ContinueSDK +import asyncio class Agent(ContinueBaseModel): @@ -88,6 +89,9 @@ class Agent(ContinueBaseModel): self.history.add_node(HistoryNode( step=step, observation=None, depth=self._step_depth)) + # Call all subscribed callbacks + await self.update_subscribers() + # Run step self._step_depth += 1 observation = await step(ContinueSDK(self)) @@ -98,10 +102,11 @@ class Agent(ContinueBaseModel): self._step_depth, include_current=True).observation = observation # Update its description - step._set_description(await step.describe(ContinueSDK(self).models)) - - # Call all subscribed callbacks - await self.update_subscribers() + async def update_description(): + step._set_description(await step.describe(ContinueSDK(self).models)) + # Update subscribers with new description + await self.update_subscribers() + asyncio.create_task(update_description()) return observation diff --git a/continuedev/src/continuedev/libs/steps/core/core.py b/continuedev/src/continuedev/libs/steps/core/core.py index 1761cbfd..9a5d54f0 100644 --- a/continuedev/src/continuedev/libs/steps/core/core.py +++ b/continuedev/src/continuedev/libs/steps/core/core.py @@ -44,7 +44,8 @@ class ShellCommandsStep(Step): name: str = "Run Shell Commands" async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "\n".join(self.cmds) + cmds_str = "\n".join(self.cmds) + return (await models.gpt35()).complete(f"{cmds_str}\n\nSummarize what was done in these shell commands, using markdown bullet points:") async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: cwd = await sdk.ide.getWorkspaceDirectory() if self.cwd is None else self.cwd diff --git a/continuedev/src/continuedev/libs/steps/draft/dlt.py b/continuedev/src/continuedev/libs/steps/draft/dlt.py index 94ca2323..73762327 100644 --- a/continuedev/src/continuedev/libs/steps/draft/dlt.py +++ b/continuedev/src/continuedev/libs/steps/draft/dlt.py @@ -1,16 +1,27 @@ from textwrap import dedent +from ....core.sdk import Models + from ....core.observation import DictObservation from ....models.filesystem_edit import AddFile from ....core.main import Step from ....core.sdk import ContinueSDK from ..core.core import WaitForUserInputStep +from ..main import MessageStep class SetupPipelineStep(Step): + hide: bool = True + name: str = "Setup dlt Pipeline" api_description: str # e.g. "I want to load data from the weatherapi.com API" + async def describe(self, models: Models): + return dedent(f"""\ + This step will create a new dlt pipeline that loads data from an API, as per your request: + {self.api_description} + """) + async def run(self, sdk: ContinueSDK): source_name = (await sdk.models.gpt35()).complete( f"Write a snake_case name for the data source described by {self.api_description}: ").strip() @@ -34,12 +45,11 @@ class SetupPipelineStep(Step): # wait for user to put API key in secrets.toml await sdk.ide.setFileOpen(await sdk.ide.getWorkspaceDirectory() + "/.dlt/secrets.toml") - await sdk.wait_for_user_confirmation("Please add the API key to the `secrets.toml` file and then press `Continue`") + await sdk.wait_for_user_confirmation("If this service requires an API key, please add it to the `secrets.toml` file and then press `Continue`") return DictObservation(values={"source_name": source_name}) class ValidatePipelineStep(Step): - async def run(self, sdk: ContinueSDK): source_name = sdk.history.last_observation().values["source_name"] filename = f'{source_name}.py' @@ -77,9 +87,19 @@ class ValidatePipelineStep(Step): class CreatePipelineStep(Step): + hide: bool = True async def run(self, sdk: ContinueSDK): await sdk.run_step( + MessageStep(message=dedent("""\ + This recipe will walk you through the process of creating a dlt pipeline for your chosen data source. With the help of Continue, you will: + - Create a Python virtual environment with dlt installed + - Run `dlt init` to generate a pipeline template + - Write the code to call the API + - Add any required API keys to the `secrets.toml` file + - Test that the API call works + - Load the data into a local DuckDB instance + - Write a query to view the data""")) >> WaitForUserInputStep(prompt="What API do you want to load data from?") >> SetupPipelineStep(api_description="WeatherAPI.com API") >> ValidatePipelineStep() diff --git a/continuedev/src/continuedev/libs/steps/main.py b/continuedev/src/continuedev/libs/steps/main.py index 6a7f14c7..c70d5c2c 100644 --- a/continuedev/src/continuedev/libs/steps/main.py +++ b/continuedev/src/continuedev/libs/steps/main.py @@ -334,6 +334,7 @@ class SolveTracebackStep(Step): class MessageStep(Step): + name: str = "Message" message: str async def describe(self, models: Models) -> Coroutine[str, None, None]: diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 36b3d99a..ec601ea7 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 }>` 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/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 419d32ed..170426e1 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -66,11 +66,11 @@ function checkEnvExists() { getExtensionUri().fsPath, "scripts", "env", - "bin" + process.platform == "win32" ? "Scripts" : "bin" ); return ( - fs.existsSync(envBinPath + "/activate") && - fs.existsSync(envBinPath + "/pip") + fs.existsSync(path.join(envBinPath, "activate")) && + fs.existsSync(path.join(envBinPath, "pip")) ); } -- cgit v1.2.3-70-g09d2 From 5fb7e9931eef72eeb83405edfade978c556c2689 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 1 Jun 2023 23:47:38 -0400 Subject: Edits and braindumps in docs --- docs/docs/concepts/autopilot.md | 20 ++++-- docs/docs/concepts/core.md | 14 ++-- docs/docs/concepts/gui.md | 15 ++-- docs/docs/concepts/history.md | 4 +- docs/docs/concepts/ide.md | 6 +- docs/docs/concepts/llm.md | 4 +- docs/docs/concepts/policy.md | 4 +- docs/docs/concepts/recipe.md | 4 +- docs/docs/concepts/sdk.md | 6 +- docs/docs/concepts/step.md | 6 +- docs/docs/getting-started.md | 27 +++---- docs/docs/how-continue-works.md | 18 +++-- docs/docs/install.md | 13 ++-- docs/docs/intro.md | 18 +++-- docs/docusaurus.config.js | 80 ++++++++++----------- docs/src/components/HomepageFeatures/index.js | 35 ++++----- docs/static/img/continue-architecture.png | Bin 136666 -> 1023648 bytes extension/README.md | 2 +- .../react-app/src/components/CodeMultiselect.tsx | 2 +- .../react-app/src/components/VSCodeFileLink.tsx | 4 +- extension/react-app/src/tabs/chat/MessageDiv.tsx | 2 +- extension/scripts/README.md | 2 +- extension/src/README.md | 14 ++-- 23 files changed, 153 insertions(+), 147 deletions(-) (limited to 'extension/src') diff --git a/docs/docs/concepts/autopilot.md b/docs/docs/concepts/autopilot.md index 5073f7cb..f3ed6fc9 100644 --- a/docs/docs/concepts/autopilot.md +++ b/docs/docs/concepts/autopilot.md @@ -8,13 +8,23 @@ The **autopilot** is the main loop, completing steps and then deciding the next ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** +The Autopilot class is the center of Continue. Every step is initiated from the Autopilot, which provides it with a ContinueSDK. + +- Records history +- Allows reversal +- Injects SDK +- Has policy to decide what step to take next +- Accepts user input and acts on it +- Main event loop +- Contains main classes that are provided through the SDK, including LLM, History, IDE + +--- - An autopilot takes user input from the React app - You can see this happening in `server/notebook.py` - It basically queues user inputs, pops off the most recent, runs that as a "UserInputStep", uses its Policy to run other steps until the next step is None, and then pops off the next user input. When nothing left, just waits for more - `Autopilot` contains the - - History - - LLM - - Policy - - IDE \ No newline at end of file + - History + - LLM + - Policy + - IDE diff --git a/docs/docs/concepts/core.md b/docs/docs/concepts/core.md index 766dbca0..e9757b36 100644 --- a/docs/docs/concepts/core.md +++ b/docs/docs/concepts/core.md @@ -3,16 +3,16 @@ **TODO: Better explain in one sentence what this is and what its purpose is** :::info -The **Continue Core** connects the [SDK](./sdk.md) and [GUI](./gui.md) with the [IDE](./ide.md) (i.e. in VS Code, GitHub Codespaces, a web browser text editor, etc), enabling the steps to make changes to your code and accelerate your software development workflows +The **Continue Core** holds the main event loop, responsible for connecting [IDE](./ide.md) (i.e. in VS Code, GitHub Codespaces, a web browser text editor, etc), [SDK](./sdk.md), and [GUI](./gui.md), and deciding which steps to take next. ::: ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** +I tried a rewrite of the info box above. The core doesn't really mean that much except for maybe the Autopilot class and the small set of classes in core.py, including History, Step, Policy mostly. Maybe best referred to as a set of abstractions. Autopilot runs the main event loop, basically queueing user input and asking the policy what to do next, and injecting the SDK, and recording history. I suppose you could also say it includes the protocols, in which case you might say "connects IDE and GUI through the SDK", though lots of 3-letter acronyms going on here. Notes below are correct. - The `Core` includes - - IDE protocol - - GUI protocol - - SDK - - Autopilot -- There is a two-way sync between an IDE and the GUI that happens through Core \ No newline at end of file + - IDE protocol + - GUI protocol + - SDK + - Autopilot +- There is a two-way sync between an IDE and the GUI that happens through Core diff --git a/docs/docs/concepts/gui.md b/docs/docs/concepts/gui.md index 7ba16a63..ff99839f 100644 --- a/docs/docs/concepts/gui.md +++ b/docs/docs/concepts/gui.md @@ -2,20 +2,17 @@ **TODO: Make sure codebase aligns with this terminology** -**TODO: Explain in one sentence what this is and what its purpose is** - :::info -The **Continue GUI** enables you to guide steps and makes everything transparent, so you can review all steps that were automated, giving you the opportunity to undo and rerun any that ran incorrectly +The **Continue GUI** lets you transparently review every automated step, providing the opportunity to undo and rerun any that ran incorrectly. ::: - ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** +GUI displays every step taken by Continue in a way that lets you easily review, reverse, refine, re-run. Provides a natural language prompt where you can request edits in natural language or initiate recipes with slash commands. Communicates with the Continue server via GUI Protocol. Is a React app, which will eventually be published as a standalone npm package, importable as a simple React component. - **From GUI to Core** - - Natural language instructions from the developer - - Hover / clicked on a step - - Other user input + - Natural language instructions from the developer + - Hover / clicked on a step + - Other user input - **From Core to GUI** - - Updates to state (e.g. a new step) \ No newline at end of file + - Updates to state (e.g. a new step) diff --git a/docs/docs/concepts/history.md b/docs/docs/concepts/history.md index bf72d2b4..7f028e14 100644 --- a/docs/docs/concepts/history.md +++ b/docs/docs/concepts/history.md @@ -8,6 +8,6 @@ The **history** is the ordered record of all past steps ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** +History stores a list ("timeline") of HistoryNodes. Each HistoryNode contains the Step with parameters, the Observation returned by the step, and the depth of the step (just so we can understand the tree structure, and what called what, and display as a tree if we wanted). History has a current_index, which can be smaller than the length of the timeline if some steps have been reversed (in which case GUI displays them with lower opacity). History class mostly responsible for maintaining order of history during reversals, grabbing most recent steps, or other things that should be bottlenecked through reliable access methods. -- What step data and metadata is stored in the history? \ No newline at end of file +- What step data and metadata is stored in the history? diff --git a/docs/docs/concepts/ide.md b/docs/docs/concepts/ide.md index f7ff2429..dc20518e 100644 --- a/docs/docs/concepts/ide.md +++ b/docs/docs/concepts/ide.md @@ -8,10 +8,10 @@ The **IDE** is the text editor where you manually edit your code. ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** +SDK provides "IDEProtocol" class so that steps can interact with VS Code, etc... in an IDE-agnostic way. Communicates with editor through websockets protocol. All that's needed to make continue work with a new IDE/editor is to implement the protocol on the side of the editor. - `ide_protocol.py` is just the abstract version of what is implemented in `ide.py`, and `main.py` runs both `notebook.py` and `ide.py` as a single FastAPI server. This is the entry point to the Continue server, and acts as a bridge between IDE and React app -- extension directory contains 1. The VS Code extension, whose code is in `extension/src`, with `extension.ts` being the entry point, and 2. the Continue React app, in the `extension/react-app` folder. This is displayed in the sidebar of VSCode, but is designed to work with any IDE that implements the protocol as is done in `extension/src/continueIdeClient.ts`. +- extension directory contains 1. The VS Code extension, whose code is in `extension/src`, with `extension.ts` being the entry point, and 2. the Continue React app, in the `extension/react-app` folder. This is displayed in the sidebar of VS Code, but is designed to work with any IDE that implements the protocol as is done in `extension/src/continueIdeClient.ts`. ## Supported IDEs @@ -95,4 +95,4 @@ Apply a file edit ### saveFile -Save a file \ No newline at end of file +Save a file diff --git a/docs/docs/concepts/llm.md b/docs/docs/concepts/llm.md index fb3b2a33..6e5fccc1 100644 --- a/docs/docs/concepts/llm.md +++ b/docs/docs/concepts/llm.md @@ -8,7 +8,7 @@ An **LLM** is short for Large Language Model, which includes models like GPT-4, ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** +Just a class with a "complete" method. Right now have HuggingFaceInferenceAPI and OpenAI subclasses. Need credentials as of now. Different models useful in different places, so we provide easy access to multiple of them, right now just gpt3.5 and starcoder, but can add others super easily. - `LLM` is the large language model that can be used in steps to automate that require some judgement based on context (e.g. generating code based on docs, explaining an error given a stack trace, etc) - Steps and recipes are implemented with specific models @@ -20,4 +20,4 @@ An **LLM** is short for Large Language Model, which includes models like GPT-4, ### `gpt-turbo-3.5` -### `StarCoder` \ No newline at end of file +### `StarCoder` diff --git a/docs/docs/concepts/policy.md b/docs/docs/concepts/policy.md index 79dbca56..ea515673 100644 --- a/docs/docs/concepts/policy.md +++ b/docs/docs/concepts/policy.md @@ -8,7 +8,7 @@ A **policy** is decides what step to run next and is associated with a [autopilo ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** +A relic of my original plan that ended up being the place to define slash commands, the command run on startup, and other weird stuff that you might want to inject after certain other steps. This may be the place where "hooks" turn out to be implemented. Much of this may be configurable through `continue.json/yaml` config file (this is where steps that run on GUI opening are currently configured.). Simply takes the history and returns a single step to run next. Can return None if no step to take next. Then user input will kick it off again eventually. Autopilot has a single policy that it follows, so definitely a global/user-configurable type of thing. - The Policy is where slash commands are defined -- The Policy is a global thing, so probably something we'll want to make user-configurable if we don't significantly change it \ No newline at end of file +- The Policy is a global thing, so probably something we'll want to make user-configurable if we don't significantly change it diff --git a/docs/docs/concepts/recipe.md b/docs/docs/concepts/recipe.md index 05d5eb0b..da2b6264 100644 --- a/docs/docs/concepts/recipe.md +++ b/docs/docs/concepts/recipe.md @@ -8,8 +8,8 @@ A **recipe** is an ordered sequence of [steps](./step.md) that are intended to a ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** +When enough steps are strung together they become a recipe. Can kick off with slash command, can share/download somehow. - Although technically just a step itself, since they also subclass the Step class, recipes differentiate themselves from normal steps by ending their name with `Recipe` by - Technically, everything is a step since everything subclasses the step class. Steps can be composed together. Once steps are composed into a workflow that developers use and share with others, that step is called a recipe and, by convention, it ends with Recipe to signal this -- Actually just a step that is composed of only other steps / recipes. \ No newline at end of file +- Actually just a step that is composed of only other steps / recipes. diff --git a/docs/docs/concepts/sdk.md b/docs/docs/concepts/sdk.md index aab97f5f..30da2e79 100644 --- a/docs/docs/concepts/sdk.md +++ b/docs/docs/concepts/sdk.md @@ -8,8 +8,6 @@ The **Continue SDK** gives you all the tools you need to automate software devel ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** - - The ContinueSDK has a `run_step` method, which allows Steps to be composable - The reason you want to run it with `run_step` instead of creating a Step and calling `step.run(...)` is so Continue can automatically keep track of the order of all steps run, and allow for reversibility, etc... - The ContinueSDK also contains functions for very common steps, like `edit_file`, `add_file`, `run` (to run shell commands), and a few others @@ -21,7 +19,7 @@ The **Continue SDK** gives you all the tools you need to automate software devel ### `sdk.ide` -`sdk.ide` is an instance of the class `AbstractIdeProtocolServer`, which contains all the methods you might need to interact with the IDE. This includes things like reading, editing, saving, and opening files as well as getting the workspace directory, displaying suggestions, and more. The goal is to have an IDE agnostic way of interacting with IDEs, so that Steps are portable across VSCode, Sublime, Code, or any other editor you use. +`sdk.ide` is an instance of the class `AbstractIdeProtocolServer`, which contains all the methods you might need to interact with the IDE. This includes things like reading, editing, saving, and opening files as well as getting the workspace directory, displaying suggestions, and more. The goal is to have an IDE agnostic way of interacting with IDEs, so that Steps are portable across VS Code, Sublime, Code, or any other editor you use. ### `sdk.models` @@ -61,4 +59,4 @@ The below methods are all just shorthand for very common steps. ### `get_user_secret` -`get_user_secret` will retrieve a secret from the local .env file or, if it doesn't exist, prompt the user to enter the secret, then store this in the .env file. \ No newline at end of file +`get_user_secret` will retrieve a secret from the local .env file or, if it doesn't exist, prompt the user to enter the secret before moving on. diff --git a/docs/docs/concepts/step.md b/docs/docs/concepts/step.md index 61761344..b92e6faf 100644 --- a/docs/docs/concepts/step.md +++ b/docs/docs/concepts/step.md @@ -8,8 +8,6 @@ A **step** is a simple action that the LLM should take as part of a sequence tha ## Details -**TODO: Nate to brain dump anything important to know and Ty to shape into paragraphs** - - A `Step` is a Pydantic class inheriting from `Step` - Steps implement the `run` method, which takes a ContinueSDK as its only parameter - The ContinueSDK gives all the utilities you need to easily write recipes (Steps) @@ -60,7 +58,7 @@ Create and run an alembic migration #### Parameters -- `edited_file`: +- `edited_file`: ### WritePytestsStep @@ -68,4 +66,4 @@ Write unit tests for this file. #### Parameters -- for_filepath (required): the path of the file that unit tests should be created for \ No newline at end of file +- for_filepath (required): the path of the file that unit tests should be created for diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md index bec9961c..39becd1a 100644 --- a/docs/docs/getting-started.md +++ b/docs/docs/getting-started.md @@ -2,15 +2,15 @@ ## GitHub Codespaces Demo -**TODO: Add `Open in GitHub Codespaces` badge here** +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/continuedev/continue-codespaces-demo?quickstart=1) 1. Click the `Open in GitHub Codespaces` badge above :::tip - We don't want to waste your time with install and env setup before you try Continue, so we set up a GitHub Codespaces dev container for you, which **won’t cost you anything**. If you are using GitHub Free for personal accounts, you can [use Codespaces for 120 hours per month for free](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces#monthly-included-storage-and-core-hours-for-personal-accounts) +We don't want to waste your time with install and env setup before you try Continue, so we set up a GitHub Codespace for you, which **won’t cost you anything**. If you are using GitHub Free for personal accounts, you can [use Codespaces for 120 hours per month for free](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces#monthly-included-storage-and-core-hours-for-personal-accounts) ::: -2. Select the `Create new codespace` button and wait a couple minutes for it to launch and then install the Continue extension. It should look like this one it is complete: +2. Select the `Create new codespace` button and wait a couple minutes while it launches and installs the Continue extension. Once complete, it should look like this: **TODO: Insert an image of Continue when it has opened** @@ -19,9 +19,9 @@ **TODO: Design and set up Pandas stuff scenario in codespaces** 4. There are a few recipes you should also try -a. In the first directory, try out X recipe -b. In the second directory, try out Y recipe -c. In the third directory, try out Z recipe + a. In the first directory, try out X recipe + b. In the second directory, try out Y recipe + c. In the third directory, try out Z recipe - database migrations - something super simple (libaries) @@ -31,10 +31,13 @@ c. In the third directory, try out Z recipe ## Next Steps -If you would prefer to use Continue locally, we reccommend installing `Continue` packaged as a VS Code extension as described [here](./install.md). +If you're ready to use Continue locally, install `Continue` packaged as a VS Code extension, as described [here](./install.md). -Otherwise, if you would like to continue to use Continue on GitHub Codespaces, then you should now go through the walkthroughs: -- How to [use the GUI](./walkthroughs/use-the-gui.md) -- How to [use a recipe](./walkthroughs/use-a-recipe.md) -- How to [create a recipe](./walkthroughs/create-a-recipe.md) -- How to [share a recipe](./walkthroughs/share-a-recipe.md) \ No newline at end of file +If you'd like to continue exploring in GitHub Codespaces, you can learn more with our walkthroughs: + +How to... + +- [Use the GUI](./walkthroughs/use-the-gui.md) +- [Invoke a recipe](./walkthroughs/use-a-recipe.md) +- [Create a recipe](./walkthroughs/create-a-recipe.md) +- [Share a recipe](./walkthroughs/share-a-recipe.md) diff --git a/docs/docs/how-continue-works.md b/docs/docs/how-continue-works.md index e032c7a4..32bf6347 100644 --- a/docs/docs/how-continue-works.md +++ b/docs/docs/how-continue-works.md @@ -2,17 +2,15 @@ ![Continue Architecture Diagram](/img/continue-architecture.png) -**TODO: Rename notebook protocol in this diagram** - ## Overview -The `Continue` library consists of a [SDK](./concepts/sdk.md), a [GUI](./concepts/gui.md), and a [Core](./concepts/core.md) that brings everything together. +The `Continue` library consists of an [SDK](./concepts/sdk.md), a [GUI](./concepts/gui.md), and a [Core](./concepts/core.md) that brings everything together. -The [SDK](./concepts/sdk.md) gives you access to tools (e.g. open a directory, edit a file, call a model, etc), which you can use when defining how a step should work and composing them with other steps. +The [SDK](./concepts/sdk.md) gives you access to the tools (e.g. open a directory, edit a file, call a model, etc.) needed to define steps that integrate LLMs into your IDE. -The [GUI](./concepts/gui.md) enables you to guide steps and makes everything transparent, so you can review all steps that were automated, giving you the opportunity to undo and rerun any that ran incorrectly. +The [GUI](./concepts/gui.md) lets you transparently review every automated step, providing the opportunity to undo and rerun any that ran incorrectly. -The [Core](./concepts/core.md) connects the SDK and GUI with the IDE (i.e. in VS Code, a web browser, etc), enabling the steps to make changes to your code and accelerate your software development workflows. +The [Core](./concepts/core.md) holds the main event loop, responsible for connecting IDE, SDK, and GUI and deciding which steps to take next. ## Details @@ -20,9 +18,9 @@ The [Core](./concepts/core.md) connects the SDK and GUI with the IDE (i.e. in VS - Continue connects any code editor (primarily VS Code right now) to a server (the Continue server) that can take actions in the editor in accordance with defined recipes at the request of a user through the GUI - What this looks like: - - The Continue VS Code extension runs the ContinueIdeProtocol, launches the Continue Python server in the background, and opens the Continue GUI in a side-panel. - - The Continue server is the brain, communication center, and source of truth, interacting with VS Code through the ContinueIdeProtocol and with the GUI through the NotebookProtocol. - - Communication between the extension and GUI happens through the Continue server. + - The Continue VS Code extension runs the ContinueIdeProtocol, launches the Continue Python server in the background, and opens the Continue GUI in a side-panel. + - The Continue server is the brain, communication center, and source of truth, interacting with VS Code through the ContinueIdeProtocol and with the GUI through the NotebookProtocol. + - Communication between the extension and GUI happens through the Continue server. - When you type a natural language command in the GUI, this is sent to the Continue server, where the `Autopilot` class takes action, potentially using the ContinueIdeProtocol to request actions be taken in the IDE, and then updates the GUI to display the new history. - `core` directory contains major concepts - This includes Autopilot, Policy, SDK (all in their own files so far) @@ -36,4 +34,4 @@ The [Core](./concepts/core.md) connects the SDK and GUI with the IDE (i.e. in VS - `models` contains all the Pydantic models and `generate_json_schema.py`, a script that converts them to JSONSchema .json files in `schema/json` - `server` runs the servers that communicate with a) the React app (`notebook.py`) and b) the IDE (`ide.py`) - `ide_protocol.py` is just the abstract version of what is implemented in `ide.py`, and `main.py` runs both `notebook.py` and `ide.py` as a single FastAPI server. This is the entry point to the Continue server, and acts as a bridge between IDE and React app -- We use OpenAPI/JSONSchema to define types so that it's really easy to bring them across language barriers. Use Pydantic types, then run `poetry run typegen` from the root of continuedev folder to generate JSONSchema json files in the `schema/json` folder. Then `npm run typegen` from the extension folder generates the types that are used within the extension. \ No newline at end of file +- We use OpenAPI/JSONSchema to define types so that it's really easy to bring them across language barriers. Use Pydantic types, then run `poetry run typegen` from the root of continuedev folder to generate JSONSchema json files in the `schema/json` folder. Then `npm run typegen` from the extension folder generates the types that are used within the extension. diff --git a/docs/docs/install.md b/docs/docs/install.md index b9916b73..6dce5da4 100644 --- a/docs/docs/install.md +++ b/docs/docs/install.md @@ -20,8 +20,11 @@ Please follow the [README instructions in the repo](https://github.com/continued ## Next steps -Now that you have installed the VS Code extension, you should go through the walkthroughs: -- How to [use the GUI](./walkthroughs/use-the-gui.md) -- How to [use a recipe](./walkthroughs/use-a-recipe.md) -- How to [create a recipe](./walkthroughs/create-a-recipe.md) -- How to [share a recipe](./walkthroughs/share-a-recipe.md) \ No newline at end of file +Now that you have installed the VS Code extension, you can learn more with our walkthroughs: + +How to... + +- [Use the GUI](./walkthroughs/use-the-gui.md) +- [Invoke a recipe](./walkthroughs/use-a-recipe.md) +- [Create a recipe](./walkthroughs/create-a-recipe.md) +- [Share a recipe](./walkthroughs/share-a-recipe.md) diff --git a/docs/docs/intro.md b/docs/docs/intro.md index e9f1dffe..5d73a256 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -2,21 +2,19 @@ ![continue-cover-logo](/img/continue-cover-logo.png) -**TODO: Nate's review of this page** +## Quickstart + +1. Try out `Continue` in the [GitHub Codespaces Demo](./getting-started.md) +2. Install `Continue` packaged as a [VS Code extension](./install.md) ## What is `Continue`? -**`Continue` is the open-source library for accelerating your use of LLMs while coding.** +**`Continue` is the open-source library for accelerating software development with language models** -You define the scenarios where Large Language Models ([LLMs](./concepts/llm.md)) like GPT-4 and StarCoder should act as an autopilot that helps you complete software development tasks. You use [recipes](./concepts/recipe.md) created by others to automate more steps in your development workflows. If a [recipe](./concepts/recipe.md) does not exist or work exactly like you want, you can use the [Continue SDK](./concepts/sdk.md) to create custom [steps](./concepts/step.md) and compose them into personalized [recipes](./concepts/recipe.md). Whether you are using a [recipe](./concepts/recipe.md) created by yourself or someone else, you can also review, reverse, and rerun [steps](./concepts/step.md) with the [Continue GUI](./concepts/gui.md), which helps you guide the work done by LLMs and learn when to use and trust them. +You define the scenarios where Large Language Models ([LLMs](./concepts/llm.md)) like GPT-4 and StarCoder should act as an autopilot, helping you complete software development tasks. You use [recipes](./concepts/recipe.md) created by others to automate more steps in your workflows. If a [recipe](./concepts/recipe.md) does not exist or work exactly like you want, you can use the [Continue SDK](./concepts/sdk.md) to create custom [steps](./concepts/step.md) and compose them into personalized [recipes](./concepts/recipe.md). Whether you are using a [recipe](./concepts/recipe.md) created by yourself or someone else, you can review, reverse, and rerun [steps](./concepts/step.md) with the [Continue GUI](./concepts/gui.md), which helps you guide the work done by LLMs and learn when to use and trust them. ## Why do developers use `Continue`? -Many developers have begun to use models like [GPT-4](https://openai.com/research/gpt-4) through [ChatGPT](https://openai.com/blog/chatgpt) while coding; however, this is quite a painful experience because of how much manual copy, paste, and editing is required to construct context for LLMs and then incorporate the generations from LLMs. Many other developers prefer to use open source models or work at organizations where they are unable to use ChatGPT, so they are using [StarCoder](https://huggingface.co/blog/starcoder) [Chat](https://huggingface.co/chat/) and are running into the same issues. - -`Continue` eliminates the manual copy, paste, and editing required when using LLMs while coding. This accelerates how developers build, ship, and maintain software, while giving them the control to define when LLMs should take actions and the confidence to trust LLMs. In short, it enables developers to do what they have always done: work together to create better and better abstractions that make it easier and easier to automate the repetitive work that people want computers to do. +Many developers have begun to use models like [GPT-4](https://openai.com/research/gpt-4) through [ChatGPT](https://openai.com/blog/chatgpt) while coding; however, the experience is painful because of how much manual copying, pasting, and editing is required to supply them with context and transfer the generated solutions to your codebase. `Continue` eliminates this pain by deeply integrating LLMs into your IDE. -## Getting started - -1. Try out `Continue` in the [GitHub Codespaces Demo](./getting-started.md) -2. Install `Continue` packaged as a [VS Code extension](./install.md) \ No newline at end of file +`Continue` accelerates how developers build, ship, and maintain software, while giving them the control to define when LLMs should take actions and the confidence to trust LLMs. In short, it enables developers to do what they have always done: work together to create better and better abstractions that make it easier and easier to automate the repetitive work that people want computers to do. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 7ca00f8d..c2991841 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -1,49 +1,49 @@ // @ts-check // Note: type annotations allow type checking and IDEs autocompletion -const lightCodeTheme = require('prism-react-renderer/themes/github'); -const darkCodeTheme = require('prism-react-renderer/themes/dracula'); +const lightCodeTheme = require("prism-react-renderer/themes/github"); +const darkCodeTheme = require("prism-react-renderer/themes/dracula"); /** @type {import('@docusaurus/types').Config} */ const config = { - title: 'Continue', - tagline: 'the open-source library for accelerating your use of LLMs while coding', - favicon: 'img/favicon.ico', + title: "Continue", + tagline: + "the open-source library for accelerating software development with language models", + favicon: "img/favicon.ico", // Set the production url of your site here - url: 'https://continue.dev', + url: "https://continue.dev", // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' - baseUrl: '/', + baseUrl: "/", // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these. - organizationName: 'continuedev', // Usually your GitHub org/user name. - projectName: 'continue', // Usually your repo name. + organizationName: "continuedev", // Usually your GitHub org/user name. + projectName: "continue", // Usually your repo name. - onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'warn', + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "warn", // Even if you don't use internalization, you can use this field to set useful // metadata like html lang. For example, if your site is Chinese, you may want // to replace "en" with "zh-Hans". i18n: { - defaultLocale: 'en', - locales: ['en'], + defaultLocale: "en", + locales: ["en"], }, presets: [ [ - 'classic', + "classic", /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { - sidebarPath: require.resolve('./sidebars.js'), - editUrl: - 'https://github.com/continuedev/continue/', + sidebarPath: require.resolve("./sidebars.js"), + editUrl: "https://github.com/continuedev/continue/", }, theme: { - customCss: require.resolve('./src/css/custom.css'), + customCss: require.resolve("./src/css/custom.css"), }, }), ], @@ -53,54 +53,54 @@ const config = { /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ // Replace with your project's social card - image: 'img/docusaurus-social-card.jpg', + image: "img/docusaurus-social-card.jpg", navbar: { - title: 'Continue', + title: "Continue", logo: { - alt: 'My Site Logo', - src: 'img/logo.svg', + alt: "My Site Logo", + src: "img/logo.svg", }, items: [ { - type: 'docSidebar', - sidebarId: 'docsSidebar', - position: 'left', - label: 'Docs', + type: "docSidebar", + sidebarId: "docsSidebar", + position: "left", + label: "Docs", }, { - href: 'https://github.com/continuedev/continue', - label: 'GitHub', - position: 'right', + href: "https://github.com/continuedev/continue", + label: "GitHub", + position: "right", }, ], }, footer: { - style: 'dark', + style: "dark", links: [ { - title: 'Docs', + title: "Docs", items: [ { - label: 'Introduction', - to: '/docs/intro', + label: "Introduction", + to: "/docs/intro", }, ], }, { - title: 'Community', + title: "Community", items: [ { - label: 'Twitter', - href: 'https://twitter.com/continuedev', + label: "Twitter", + href: "https://twitter.com/continuedev", }, ], }, { - title: 'More', + title: "More", items: [ { - label: 'GitHub', - href: 'https://github.com/continuedev/continue', + label: "GitHub", + href: "https://github.com/continuedev/continue", }, ], }, @@ -114,4 +114,4 @@ const config = { }), }; -module.exports = config; \ No newline at end of file +module.exports = config; diff --git a/docs/src/components/HomepageFeatures/index.js b/docs/src/components/HomepageFeatures/index.js index 764ca891..0c5d8272 100644 --- a/docs/src/components/HomepageFeatures/index.js +++ b/docs/src/components/HomepageFeatures/index.js @@ -1,43 +1,44 @@ -import React from 'react'; -import clsx from 'clsx'; -import styles from './styles.module.css'; +import React from "react"; +import clsx from "clsx"; +import styles from "./styles.module.css"; const FeatureList = [ { - title: 'Define the scenarios where LLMs should step into accelerate you', - Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, + title: "Tell LLMs when to step in", + Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default, description: ( <> - Enable LLMs to be an autopilot for parts of your software development tasks - by leveraging recipes created by others in the workflows you use when coding + Seamlessly put your repetitive software development tasks on autopilot + by leveraging recipes created by others ), }, { - title: 'Create your own workflows to show LLMs exactly what to do', - Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, + title: "Write your own recipes", + Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default, description: ( <> - Use the Continue SDK to create your own custom steps and compose them into - personalized recipes, so that using LLMs seamlessly fits into your workflows + Use the Continue SDK to create your own custom steps and compose them + into personalized recipes, guiding LLMs through common sequences of + tasks ), }, { - title: 'Guide the steps taken by LLMs to learn when to use and trust them', - Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, + title: "Wield LLMs with confidence", + Svg: require("@site/static/img/undraw_docusaurus_react.svg").default, description: ( <> - Use the Continue GUI to review, reverse, and rerun steps or even entire recipes, - incorporating LLMs with confidence into your software development workflows + Use the Continue GUI to review, reverse, and rerun steps or even entire + recipes, allowing you to build trust in LLMs ), }, ]; -function Feature({Svg, title, description}) { +function Feature({ Svg, title, description }) { return ( -
+
diff --git a/docs/static/img/continue-architecture.png b/docs/static/img/continue-architecture.png index 7a38c904..58366d8f 100644 Binary files a/docs/static/img/continue-architecture.png and b/docs/static/img/continue-architecture.png differ diff --git a/extension/README.md b/extension/README.md index 0a003145..df158440 100644 --- a/extension/README.md +++ b/extension/README.md @@ -29,7 +29,7 @@ Once Continue has code snippets to work with, it can generate a fix. Just click ### Stacktrace Parsing -Any stacktrace that appears in your VSCode terminal will be caught by us so we can immediately begin the debugging process. For small bugs that you might have quickly solved, we'll just speed up the process to be nearly instantaneous. +Any stacktrace that appears in your VS Code terminal will be caught by us so we can immediately begin the debugging process. For small bugs that you might have quickly solved, we'll just speed up the process to be nearly instantaneous. ### Generate Unit Tests and Docstrings diff --git a/extension/react-app/src/components/CodeMultiselect.tsx b/extension/react-app/src/components/CodeMultiselect.tsx index 626ae42f..c0ab9400 100644 --- a/extension/react-app/src/components/CodeMultiselect.tsx +++ b/extension/react-app/src/components/CodeMultiselect.tsx @@ -135,7 +135,7 @@ function formatFileRange( )} (lines ${rangeInFile.range.start.line + 1}-${ rangeInFile.range.end.line + 1 })`; - // +1 because VSCode Ranges are 0-indexed + // +1 because VS Code Ranges are 0-indexed } //#endregion diff --git a/extension/react-app/src/components/VSCodeFileLink.tsx b/extension/react-app/src/components/VSCodeFileLink.tsx index 6219654d..12ce5af8 100644 --- a/extension/react-app/src/components/VSCodeFileLink.tsx +++ b/extension/react-app/src/components/VSCodeFileLink.tsx @@ -1,7 +1,7 @@ import React from "react"; import { postVscMessage } from "../vscode"; -function VSCodeFileLink(props: { path: string; text?: string }) { +function VS CodeFileLink(props: { path: string; text?: string }) { return ( requirements.txt` whenever you add a new requirement. diff --git a/extension/src/README.md b/extension/src/README.md index bb10f5c8..76b96ea0 100644 --- a/extension/src/README.md +++ b/extension/src/README.md @@ -22,23 +22,23 @@ 10. Then run `npm run compile` -7. Open `src/activate.ts` file (or any TypeScript file) +11. Open `src/activate.ts` file (or any TypeScript file) -7. Press `F5` on your keyboard to start `Run and Debug` mode +12. Press `F5` on your keyboard to start `Run and Debug` mode -8. `cmd+shift+p` to look at developer console and select Continue commands +13. `cmd+shift+p` to look at developer console and select Continue commands -9. Every time you make changes to the code, you need to run `npm run compile` +14. Every time you make changes to the code, you need to run `npm run compile` -10. If you run into a "command not found" error, try running `npm run rebuild` and then `npm run compile` +15. If you run into a "command not found" error, try running `npm run rebuild` and then `npm run compile` ## Alternative: Install a packaged version -You should always have a packaged version installed in VSCode, because when Continue is broken you'll want a stable version to help you debug. There are four key commands in the `package.json`: +You should always have a packaged version installed in VS Code, because when Continue is broken you'll want a stable version to help you debug. There are four key commands in the `package.json`: 1. `npm run package` will create a .vsix file in the `build/` folder that can then be installed. It is this same file that you can share with others who want to try the extension. -2. `npm run install-extension` will install the extension to VSCode. You should then see it in your installed extensions in the VSCode sidebar. +2. `npm run install-extension` will install the extension to VS Code. You should then see it in your installed extensions in the VS Code sidebar. 3. `npm run uninstall` will uninstall the extension. You don't always have to do this thanks to the reinstall command, but can be useful when you want to do so manually. -- cgit v1.2.3-70-g09d2 From ad86eff7b06f0bfbed3b1cb362d83ec6a4348e45 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 2 Jun 2023 01:05:59 -0400 Subject: notebook -> gui --- continuedev/src/continuedev/core/agent.py | 186 --------------- continuedev/src/continuedev/core/autopilot.py | 186 +++++++++++++++ continuedev/src/continuedev/plugins/__init__.py | 26 -- continuedev/src/continuedev/plugins/load.py | 21 -- .../src/continuedev/plugins/policy/__init__.py | 4 - .../src/continuedev/plugins/policy/hookspecs.py | 10 - .../continuedev/plugins/policy/libs/__init__.py | 0 .../continuedev/plugins/policy/libs/alternate.py | 20 -- .../src/continuedev/plugins/step/__init__.py | 4 - .../src/continuedev/plugins/step/hookspecs.py | 15 -- .../src/continuedev/plugins/step/libs/__init__.py | 0 .../continuedev/plugins/step/libs/hello_world.py | 9 - continuedev/src/continuedev/server/gui.py | 130 ++++++++++ continuedev/src/continuedev/server/gui_protocol.py | 28 +++ continuedev/src/continuedev/server/ide.py | 16 +- continuedev/src/continuedev/server/ide_protocol.py | 12 +- continuedev/src/continuedev/server/main.py | 4 +- continuedev/src/continuedev/server/notebook.py | 130 ---------- .../src/continuedev/server/notebook_protocol.py | 28 --- docs/docs/concepts/agent.md | 8 - docs/docs/concepts/autopilot.md | 30 +++ docs/docs/concepts/gui.md | 4 +- docs/docs/concepts/ide.md | 14 +- docs/docs/how-continue-works.md | 37 +++ extension/react-app/src/App.tsx | 6 +- .../src/hooks/ContinueGUIClientProtocol.ts | 13 + .../src/hooks/ContinueNotebookClientProtocol.ts | 13 - .../react-app/src/hooks/useContinueGUIProtocol.ts | 49 ++++ .../src/hooks/useContinueNotebookProtocol.ts | 49 ---- extension/react-app/src/hooks/useWebsocket.ts | 16 +- extension/react-app/src/tabs/gui.tsx | 265 +++++++++++++++++++++ extension/react-app/src/tabs/notebook.tsx | 265 --------------------- extension/src/activation/activate.ts | 4 +- extension/src/commands.ts | 4 +- extension/src/continueIdeClient.ts | 8 +- 35 files changed, 782 insertions(+), 832 deletions(-) delete mode 100644 continuedev/src/continuedev/core/agent.py create mode 100644 continuedev/src/continuedev/core/autopilot.py delete mode 100644 continuedev/src/continuedev/plugins/__init__.py delete mode 100644 continuedev/src/continuedev/plugins/load.py delete mode 100644 continuedev/src/continuedev/plugins/policy/__init__.py delete mode 100644 continuedev/src/continuedev/plugins/policy/hookspecs.py delete mode 100644 continuedev/src/continuedev/plugins/policy/libs/__init__.py delete mode 100644 continuedev/src/continuedev/plugins/policy/libs/alternate.py delete mode 100644 continuedev/src/continuedev/plugins/step/__init__.py delete mode 100644 continuedev/src/continuedev/plugins/step/hookspecs.py delete mode 100644 continuedev/src/continuedev/plugins/step/libs/__init__.py delete mode 100644 continuedev/src/continuedev/plugins/step/libs/hello_world.py create mode 100644 continuedev/src/continuedev/server/gui.py create mode 100644 continuedev/src/continuedev/server/gui_protocol.py delete mode 100644 continuedev/src/continuedev/server/notebook.py delete mode 100644 continuedev/src/continuedev/server/notebook_protocol.py delete mode 100644 docs/docs/concepts/agent.md create mode 100644 docs/docs/concepts/autopilot.md create mode 100644 docs/docs/how-continue-works.md create mode 100644 extension/react-app/src/hooks/ContinueGUIClientProtocol.ts delete mode 100644 extension/react-app/src/hooks/ContinueNotebookClientProtocol.ts create mode 100644 extension/react-app/src/hooks/useContinueGUIProtocol.ts delete mode 100644 extension/react-app/src/hooks/useContinueNotebookProtocol.ts create mode 100644 extension/react-app/src/tabs/gui.tsx delete mode 100644 extension/react-app/src/tabs/notebook.tsx (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/agent.py b/continuedev/src/continuedev/core/agent.py deleted file mode 100644 index 6e920ab4..00000000 --- a/continuedev/src/continuedev/core/agent.py +++ /dev/null @@ -1,186 +0,0 @@ -import traceback -import time -from typing import Callable, Coroutine, List -from ..models.filesystem_edit import FileEditWithFullContents -from ..libs.llm import LLM -from .observation import Observation -from ..server.ide_protocol import AbstractIdeProtocolServer -from ..libs.util.queue import AsyncSubscriptionQueue -from ..models.main import ContinueBaseModel -from .main import Policy, History, FullState, Step, HistoryNode -from ..libs.steps.core.core import ReversibleStep, ManualEditStep, UserInputStep -from ..libs.util.telemetry import capture_event -from .sdk import ContinueSDK -import asyncio - - -class Autopilot(ContinueBaseModel): - policy: Policy - ide: AbstractIdeProtocolServer - history: History = History.from_empty() - _on_update_callbacks: List[Callable[[FullState], None]] = [] - - _active: bool = False - _should_halt: bool = False - _main_user_input_queue: List[str] = [] - - _user_input_queue = AsyncSubscriptionQueue() - - class Config: - arbitrary_types_allowed = True - - def get_full_state(self) -> FullState: - return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue) - - def on_update(self, callback: Coroutine["FullState", None, None]): - """Subscribe to changes to state""" - self._on_update_callbacks.append(callback) - - async def update_subscribers(self): - full_state = self.get_full_state() - for callback in self._on_update_callbacks: - await callback(full_state) - - def give_user_input(self, input: str, index: int): - self._user_input_queue.post(str(index), input) - - async def wait_for_user_input(self) -> str: - self._active = False - await self.update_subscribers() - user_input = await self._user_input_queue.get(str(self.history.current_index)) - self._active = True - await self.update_subscribers() - return user_input - - _manual_edits_buffer: List[FileEditWithFullContents] = [] - - async def reverse_to_index(self, index: int): - try: - while self.history.get_current_index() >= index: - current_step = self.history.get_current().step - self.history.step_back() - if issubclass(current_step.__class__, ReversibleStep): - await current_step.reverse(ContinueSDK(self)) - - await self.update_subscribers() - except Exception as e: - print(e) - - def handle_manual_edits(self, edits: List[FileEditWithFullContents]): - for edit in edits: - self._manual_edits_buffer.append(edit) - # TODO: You're storing a lot of unecessary data here. Can compress into EditDiffs on the spot, and merge. - # self._manual_edits_buffer = merge_file_edit(self._manual_edits_buffer, edit) - - def handle_traceback(self, traceback: str): - raise NotImplementedError - - _step_depth: int = 0 - - async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: - capture_event( - 'step run', {'step_name': step.name, 'params': step.dict()}) - - if not is_future_step: - # Check manual edits buffer, clear out if needed by creating a ManualEditStep - if len(self._manual_edits_buffer) > 0: - manualEditsStep = ManualEditStep.from_sequence( - self._manual_edits_buffer) - self._manual_edits_buffer = [] - await self._run_singular_step(manualEditsStep) - - # Update history - do this first so we get top-first tree ordering - self.history.add_node(HistoryNode( - step=step, observation=None, depth=self._step_depth)) - - # Call all subscribed callbacks - await self.update_subscribers() - - # Run step - self._step_depth += 1 - observation = await step(ContinueSDK(self)) - self._step_depth -= 1 - - # Add observation to history - self.history.get_last_at_depth( - self._step_depth, include_current=True).observation = observation - - # Update its description - async def update_description(): - step._set_description(await step.describe(ContinueSDK(self).models)) - # Update subscribers with new description - await self.update_subscribers() - asyncio.create_task(update_description()) - - return observation - - async def run_from_step(self, step: "Step"): - # if self._active: - # raise RuntimeError("Autopilot is already running") - self._active = True - - next_step = step - is_future_step = False - while not (next_step is None or self._should_halt): - try: - if is_future_step: - # If future step, then we are replaying and need to delete the step from history so it can be replaced - self.history.remove_current_and_substeps() - - observation = await self._run_singular_step(next_step, is_future_step) - if next_step := self.policy.next(self.history): - is_future_step = False - elif next_step := self.history.take_next_step(): - is_future_step = True - else: - next_step = None - - except Exception as e: - print( - f"Error while running step: \n{''.join(traceback.format_tb(e.__traceback__))}\n{e}") - next_step = None - - self._active = False - - # Doing this so active can make it to the frontend after steps are done. But want better state syncing tools - for callback in self._on_update_callbacks: - await callback(None) - - async def run_from_observation(self, observation: Observation): - next_step = self.policy.next(self.history) - await self.run_from_step(next_step) - - async def run_policy(self): - first_step = self.policy.next(self.history) - await self.run_from_step(first_step) - - async def _request_halt(self): - if self._active: - self._should_halt = True - while self._active: - time.sleep(0.1) - self._should_halt = False - return None - - async def accept_user_input(self, user_input: str): - self._main_user_input_queue.append(user_input) - await self.update_subscribers() - - if len(self._main_user_input_queue) > 1: - return - - # await self._request_halt() - # Just run the step that takes user input, and - # then up to the policy to decide how to deal with it. - self._main_user_input_queue.pop(0) - await self.update_subscribers() - await self.run_from_step(UserInputStep(user_input=user_input)) - - while len(self._main_user_input_queue) > 0: - await self.run_from_step(UserInputStep( - user_input=self._main_user_input_queue.pop(0))) - - async def accept_refinement_input(self, user_input: str, index: int): - await self._request_halt() - await self.reverse_to_index(index) - await self.run_from_step(UserInputStep(user_input=user_input)) diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py new file mode 100644 index 00000000..6e920ab4 --- /dev/null +++ b/continuedev/src/continuedev/core/autopilot.py @@ -0,0 +1,186 @@ +import traceback +import time +from typing import Callable, Coroutine, List +from ..models.filesystem_edit import FileEditWithFullContents +from ..libs.llm import LLM +from .observation import Observation +from ..server.ide_protocol import AbstractIdeProtocolServer +from ..libs.util.queue import AsyncSubscriptionQueue +from ..models.main import ContinueBaseModel +from .main import Policy, History, FullState, Step, HistoryNode +from ..libs.steps.core.core import ReversibleStep, ManualEditStep, UserInputStep +from ..libs.util.telemetry import capture_event +from .sdk import ContinueSDK +import asyncio + + +class Autopilot(ContinueBaseModel): + policy: Policy + ide: AbstractIdeProtocolServer + history: History = History.from_empty() + _on_update_callbacks: List[Callable[[FullState], None]] = [] + + _active: bool = False + _should_halt: bool = False + _main_user_input_queue: List[str] = [] + + _user_input_queue = AsyncSubscriptionQueue() + + class Config: + arbitrary_types_allowed = True + + def get_full_state(self) -> FullState: + return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue) + + def on_update(self, callback: Coroutine["FullState", None, None]): + """Subscribe to changes to state""" + self._on_update_callbacks.append(callback) + + async def update_subscribers(self): + full_state = self.get_full_state() + for callback in self._on_update_callbacks: + await callback(full_state) + + def give_user_input(self, input: str, index: int): + self._user_input_queue.post(str(index), input) + + async def wait_for_user_input(self) -> str: + self._active = False + await self.update_subscribers() + user_input = await self._user_input_queue.get(str(self.history.current_index)) + self._active = True + await self.update_subscribers() + return user_input + + _manual_edits_buffer: List[FileEditWithFullContents] = [] + + async def reverse_to_index(self, index: int): + try: + while self.history.get_current_index() >= index: + current_step = self.history.get_current().step + self.history.step_back() + if issubclass(current_step.__class__, ReversibleStep): + await current_step.reverse(ContinueSDK(self)) + + await self.update_subscribers() + except Exception as e: + print(e) + + def handle_manual_edits(self, edits: List[FileEditWithFullContents]): + for edit in edits: + self._manual_edits_buffer.append(edit) + # TODO: You're storing a lot of unecessary data here. Can compress into EditDiffs on the spot, and merge. + # self._manual_edits_buffer = merge_file_edit(self._manual_edits_buffer, edit) + + def handle_traceback(self, traceback: str): + raise NotImplementedError + + _step_depth: int = 0 + + async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: + capture_event( + 'step run', {'step_name': step.name, 'params': step.dict()}) + + if not is_future_step: + # Check manual edits buffer, clear out if needed by creating a ManualEditStep + if len(self._manual_edits_buffer) > 0: + manualEditsStep = ManualEditStep.from_sequence( + self._manual_edits_buffer) + self._manual_edits_buffer = [] + await self._run_singular_step(manualEditsStep) + + # Update history - do this first so we get top-first tree ordering + self.history.add_node(HistoryNode( + step=step, observation=None, depth=self._step_depth)) + + # Call all subscribed callbacks + await self.update_subscribers() + + # Run step + self._step_depth += 1 + observation = await step(ContinueSDK(self)) + self._step_depth -= 1 + + # Add observation to history + self.history.get_last_at_depth( + self._step_depth, include_current=True).observation = observation + + # Update its description + async def update_description(): + step._set_description(await step.describe(ContinueSDK(self).models)) + # Update subscribers with new description + await self.update_subscribers() + asyncio.create_task(update_description()) + + return observation + + async def run_from_step(self, step: "Step"): + # if self._active: + # raise RuntimeError("Autopilot is already running") + self._active = True + + next_step = step + is_future_step = False + while not (next_step is None or self._should_halt): + try: + if is_future_step: + # If future step, then we are replaying and need to delete the step from history so it can be replaced + self.history.remove_current_and_substeps() + + observation = await self._run_singular_step(next_step, is_future_step) + if next_step := self.policy.next(self.history): + is_future_step = False + elif next_step := self.history.take_next_step(): + is_future_step = True + else: + next_step = None + + except Exception as e: + print( + f"Error while running step: \n{''.join(traceback.format_tb(e.__traceback__))}\n{e}") + next_step = None + + self._active = False + + # Doing this so active can make it to the frontend after steps are done. But want better state syncing tools + for callback in self._on_update_callbacks: + await callback(None) + + async def run_from_observation(self, observation: Observation): + next_step = self.policy.next(self.history) + await self.run_from_step(next_step) + + async def run_policy(self): + first_step = self.policy.next(self.history) + await self.run_from_step(first_step) + + async def _request_halt(self): + if self._active: + self._should_halt = True + while self._active: + time.sleep(0.1) + self._should_halt = False + return None + + async def accept_user_input(self, user_input: str): + self._main_user_input_queue.append(user_input) + await self.update_subscribers() + + if len(self._main_user_input_queue) > 1: + return + + # await self._request_halt() + # Just run the step that takes user input, and + # then up to the policy to decide how to deal with it. + self._main_user_input_queue.pop(0) + await self.update_subscribers() + await self.run_from_step(UserInputStep(user_input=user_input)) + + while len(self._main_user_input_queue) > 0: + await self.run_from_step(UserInputStep( + user_input=self._main_user_input_queue.pop(0))) + + async def accept_refinement_input(self, user_input: str, index: int): + await self._request_halt() + await self.reverse_to_index(index) + await self.run_from_step(UserInputStep(user_input=user_input)) diff --git a/continuedev/src/continuedev/plugins/__init__.py b/continuedev/src/continuedev/plugins/__init__.py deleted file mode 100644 index 0ce6d079..00000000 --- a/continuedev/src/continuedev/plugins/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import List -import pluggy -from .step import hookspecs -from .step.libs import hello_world - -builtin_libs = [hello_world] - -def get_plugin_manager(use_plugins: List[str]) -> pluggy.PluginManager: - pm = pluggy.PluginManager("continue.step") - pm.add_hookspecs(hookspecs) - pm.load_setuptools_entrypoints("continue.step") - - # Only use plugins that are specified in the config file - for plugin, name in pm.list_name_plugin(): - if name not in use_plugins: - pm.set_blocked(plugin) - - # Print warning if plugin not found - for name in use_plugins: - if not pm.has_plugin(name): - print(f"Plugin {name} not found.") - - for lib in builtin_libs: - pm.register(lib) - - return pm \ No newline at end of file diff --git a/continuedev/src/continuedev/plugins/load.py b/continuedev/src/continuedev/plugins/load.py deleted file mode 100644 index adbaad09..00000000 --- a/continuedev/src/continuedev/plugins/load.py +++ /dev/null @@ -1,21 +0,0 @@ -def load_validator_plugin(config: ValidatorPluginConfig) -> Validator: - if config.name == "continue.tb_validator": - return PythonTracebackValidator(config.cmd, config.cwd) - elif config.name == "continue.pytest_validator": - return PytestValidator(cwd=config.cwd) - else: - raise KeyError("Unknown validator plugin name") - -def load_llm_plugin(config: LLMPluginConfig) -> LLM: - if config.provider == "openai": - return OpenAI(api_key=config.api_key) - else: - raise KeyError("Unknown LLM provider: " + config.provider) - -def load_policy_plugin(config: PolicyPluginConfig) -> Policy: - if config.name == "continue.random_policy": - return RandomPolicy() - elif config.name == "continue.dfs_policy": - return DFSPolicy() - else: - raise KeyError("Unknown policy plugin name") \ No newline at end of file diff --git a/continuedev/src/continuedev/plugins/policy/__init__.py b/continuedev/src/continuedev/plugins/policy/__init__.py deleted file mode 100644 index b9722bae..00000000 --- a/continuedev/src/continuedev/plugins/policy/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -import pluggy - -hookimpl = pluggy.HookimplMarker("continue.policy") -"""Marker to be imported and used in plugins (and for own implementations)""" \ No newline at end of file diff --git a/continuedev/src/continuedev/plugins/policy/hookspecs.py b/continuedev/src/continuedev/plugins/policy/hookspecs.py deleted file mode 100644 index abe932d3..00000000 --- a/continuedev/src/continuedev/plugins/policy/hookspecs.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import List, Tuple -import pluggy -from ...libs.policy import Policy, Step - -hookspec = pluggy.HookspecMarker("continue.policy") - -class PolicyPlugin(Policy): - @hookspec - def next(self) -> Step: - """Get the next step to run""" \ No newline at end of file diff --git a/continuedev/src/continuedev/plugins/policy/libs/__init__.py b/continuedev/src/continuedev/plugins/policy/libs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/continuedev/src/continuedev/plugins/policy/libs/alternate.py b/continuedev/src/continuedev/plugins/policy/libs/alternate.py deleted file mode 100644 index 3087c059..00000000 --- a/continuedev/src/continuedev/plugins/policy/libs/alternate.py +++ /dev/null @@ -1,20 +0,0 @@ -from plugins import policy -from ....core.main import History, Step - - -class AlternatingPolicy: - """A Policy that alternates between two steps.""" - - def __init__(self, first: Step, second: Step): - self.first = first - self.second = second - self.last_was_first = False - - @policy.hookimpl - def next(self, history: History) -> Step: - if self.last_was_first: - self.last_was_first = False - return self.second - else: - self.last_was_first = True - return self.first diff --git a/continuedev/src/continuedev/plugins/step/__init__.py b/continuedev/src/continuedev/plugins/step/__init__.py deleted file mode 100644 index e6d8cd3b..00000000 --- a/continuedev/src/continuedev/plugins/step/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -import pluggy - -hookimpl = pluggy.HookimplMarker("continue.step") -"""Marker to be imported and used in plugins (and for own implementations)""" \ No newline at end of file diff --git a/continuedev/src/continuedev/plugins/step/hookspecs.py b/continuedev/src/continuedev/plugins/step/hookspecs.py deleted file mode 100644 index a5714fc5..00000000 --- a/continuedev/src/continuedev/plugins/step/hookspecs.py +++ /dev/null @@ -1,15 +0,0 @@ -from typing import Coroutine -import pluggy -from ...core.main import Step -from ...core.observation import Observation -from ...core.sdk import ContinueSDK - -hookspec = pluggy.HookspecMarker("continue.step") - -# Perhaps Actions should be generic about what their inputs must be. - - -class StepPlugin(Step): - @hookspec - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - """Run""" diff --git a/continuedev/src/continuedev/plugins/step/libs/__init__.py b/continuedev/src/continuedev/plugins/step/libs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/continuedev/src/continuedev/plugins/step/libs/hello_world.py b/continuedev/src/continuedev/plugins/step/libs/hello_world.py deleted file mode 100644 index 72255bfd..00000000 --- a/continuedev/src/continuedev/plugins/step/libs/hello_world.py +++ /dev/null @@ -1,9 +0,0 @@ -from ....plugins import step -from ....libs.steps import ContinueSDK - - -class HelloWorldStep: - """A Step that prints "Hello World!".""" - @step.hookimpl - def run(sdk: ContinueSDK): - print("Hello World!") diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py new file mode 100644 index 00000000..3d1a5a82 --- /dev/null +++ b/continuedev/src/continuedev/server/gui.py @@ -0,0 +1,130 @@ +import json +from fastapi import Depends, Header, WebSocket, APIRouter +from typing import Any, Type, TypeVar, Union +from pydantic import BaseModel +from uvicorn.main import Server + +from .session_manager import SessionManager, session_manager, Session +from .gui_protocol import AbstractGUIProtocolServer +from ..libs.util.queue import AsyncSubscriptionQueue +import asyncio +import nest_asyncio +nest_asyncio.apply() + +router = APIRouter(prefix="/gui", tags=["gui"]) + +# Graceful shutdown by closing websockets +original_handler = Server.handle_exit + + +class AppStatus: + should_exit = False + + @staticmethod + def handle_exit(*args, **kwargs): + AppStatus.should_exit = True + print("Shutting down") + original_handler(*args, **kwargs) + + +Server.handle_exit = AppStatus.handle_exit + + +def session(x_continue_session_id: str = Header("anonymous")) -> Session: + return session_manager.get_session(x_continue_session_id) + + +def websocket_session(session_id: str) -> Session: + return session_manager.get_session(session_id) + + +T = TypeVar("T", bound=BaseModel) + +# You should probably abstract away the websocket stuff into a separate class + + +class GUIProtocolServer(AbstractGUIProtocolServer): + websocket: WebSocket + session: Session + sub_queue: AsyncSubscriptionQueue = AsyncSubscriptionQueue() + + def __init__(self, session: Session): + self.session = session + + async def _send_json(self, message_type: str, data: Any): + await self.websocket.send_json({ + "messageType": message_type, + "data": data + }) + + async def _receive_json(self, message_type: str) -> Any: + return await self.sub_queue.get(message_type) + + async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: + await self._send_json(message_type, data) + resp = await self._receive_json(message_type) + return resp_model.parse_obj(resp) + + def handle_json(self, message_type: str, data: Any): + try: + if message_type == "main_input": + self.on_main_input(data["input"]) + elif message_type == "step_user_input": + self.on_step_user_input(data["input"], data["index"]) + elif message_type == "refinement_input": + self.on_refinement_input(data["input"], data["index"]) + elif message_type == "reverse_to_index": + self.on_reverse_to_index(data["index"]) + except Exception as e: + print(e) + + async def send_state_update(self): + state = self.session.autopilot.get_full_state().dict() + await self._send_json("state_update", { + "state": state + }) + + def on_main_input(self, input: str): + # Do something with user input + asyncio.create_task(self.session.autopilot.accept_user_input(input)) + + def on_reverse_to_index(self, index: int): + # Reverse the history to the given index + asyncio.create_task(self.session.autopilot.reverse_to_index(index)) + + def on_step_user_input(self, input: str, index: int): + asyncio.create_task( + self.session.autopilot.give_user_input(input, index)) + + def on_refinement_input(self, input: str, index: int): + asyncio.create_task( + self.session.autopilot.accept_refinement_input(input, index)) + + +@router.websocket("/ws") +async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(websocket_session)): + await websocket.accept() + + print("Session started") + session_manager.register_websocket(session.session_id, websocket) + protocol = GUIProtocolServer(session) + protocol.websocket = websocket + + # Update any history that may have happened before connection + await protocol.send_state_update() + + while AppStatus.should_exit is False: + message = await websocket.receive_text() + print("Received message", message) + if type(message) is str: + message = json.loads(message) + + if "messageType" not in message or "data" not in message: + continue + message_type = message["messageType"] + data = message["data"] + + protocol.handle_json(message_type, data) + + print("Closing websocket") + await websocket.close() diff --git a/continuedev/src/continuedev/server/gui_protocol.py b/continuedev/src/continuedev/server/gui_protocol.py new file mode 100644 index 00000000..e32d80ef --- /dev/null +++ b/continuedev/src/continuedev/server/gui_protocol.py @@ -0,0 +1,28 @@ +from typing import Any +from abc import ABC, abstractmethod + + +class AbstractGUIProtocolServer(ABC): + @abstractmethod + async def handle_json(self, data: Any): + """Handle a json message""" + + @abstractmethod + def on_main_input(self, input: str): + """Called when the user inputs something""" + + @abstractmethod + def on_reverse_to_index(self, index: int): + """Called when the user requests reverse to a previous index""" + + @abstractmethod + def on_refinement_input(self, input: str, index: int): + """Called when the user inputs a refinement""" + + @abstractmethod + def on_step_user_input(self, input: str, index: int): + """Called when the user inputs a step""" + + @abstractmethod + async def send_state_update(self, state: dict): + """Send a state update to the client""" diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 32f0b3ba..71017ce0 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -12,7 +12,7 @@ from ..models.filesystem import FileSystem, RangeInFile, EditDiff, RealFileSyste from ..models.main import Traceback from ..models.filesystem_edit import AddDirectory, AddFile, DeleteDirectory, DeleteFile, FileSystemEdit, FileEdit, FileEditWithFullContents, RenameDirectory, RenameFile, SequentialFileSystemEdit from pydantic import BaseModel -from .notebook import SessionManager, session_manager +from .gui import SessionManager, session_manager from .ide_protocol import AbstractIdeProtocolServer @@ -106,8 +106,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer): return resp_model.parse_obj(resp) async def handle_json(self, message_type: str, data: Any): - if message_type == "openNotebook": - await self.openNotebook() + if message_type == "openGUI": + await self.openGUI() elif message_type == "setFileOpen": await self.setFileOpen(data["filepath"], data["open"]) elif message_type == "fileEdits": @@ -131,9 +131,9 @@ class IdeProtocolServer(AbstractIdeProtocolServer): "open": open }) - async def openNotebook(self): + async def openGUI(self): session_id = self.session_manager.new_session(self) - await self._send_json("openNotebook", { + await self._send_json("openGUI", { "sessionId": session_id }) @@ -148,7 +148,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): self._receive_json(ShowSuggestionResponse) for i in range(len(suggestions)) ]) # WORKING ON THIS FLOW HERE. Fine now to just await for response, instead of doing something fancy with a "waiting" state on the autopilot. - # Just need connect the suggestionId to the IDE (and the notebook) + # Just need connect the suggestionId to the IDE (and the gui) return any([r.accepted for r in responses]) # ------------------------------- # @@ -168,11 +168,11 @@ class IdeProtocolServer(AbstractIdeProtocolServer): # Access to Autopilot (so SessionManager) pass - def onCloseNotebook(self, session_id: str): + def onCloseGUI(self, session_id: str): # Accesss to SessionManager pass - def onOpenNotebookRequest(self): + def onOpenGUIRequest(self): pass def onFileEdits(self, edits: List[FileEditWithFullContents]): diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 15d019b4..4f505e80 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -24,8 +24,8 @@ class AbstractIdeProtocolServer(ABC): """Set whether a file is open""" @abstractmethod - async def openNotebook(self): - """Open a notebook""" + async def openGUI(self): + """Open a GUI""" @abstractmethod async def showSuggestionsAndWait(self, suggestions: List[FileEdit]) -> bool: @@ -44,12 +44,12 @@ class AbstractIdeProtocolServer(ABC): """Called when a file system update is received""" @abstractmethod - def onCloseNotebook(self, session_id: str): - """Called when a notebook is closed""" + def onCloseGUI(self, session_id: str): + """Called when a GUI is closed""" @abstractmethod - def onOpenNotebookRequest(self): - """Called when a notebook is requested to be opened""" + def onOpenGUIRequest(self): + """Called when a GUI is requested to be opened""" @abstractmethod async def getOpenFiles(self) -> List[str]: diff --git a/continuedev/src/continuedev/server/main.py b/continuedev/src/continuedev/server/main.py index 1ffe1450..7b7124de 100644 --- a/continuedev/src/continuedev/server/main.py +++ b/continuedev/src/continuedev/server/main.py @@ -2,14 +2,14 @@ import os from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from .ide import router as ide_router -from .notebook import router as notebook_router +from .gui import router as gui_router import uvicorn import argparse app = FastAPI() app.include_router(ide_router) -app.include_router(notebook_router) +app.include_router(gui_router) # Add CORS support app.add_middleware( diff --git a/continuedev/src/continuedev/server/notebook.py b/continuedev/src/continuedev/server/notebook.py deleted file mode 100644 index 8ebe2853..00000000 --- a/continuedev/src/continuedev/server/notebook.py +++ /dev/null @@ -1,130 +0,0 @@ -import json -from fastapi import Depends, Header, WebSocket, APIRouter -from typing import Any, Type, TypeVar, Union -from pydantic import BaseModel -from uvicorn.main import Server - -from .session_manager import SessionManager, session_manager, Session -from .notebook_protocol import AbstractNotebookProtocolServer -from ..libs.util.queue import AsyncSubscriptionQueue -import asyncio -import nest_asyncio -nest_asyncio.apply() - -router = APIRouter(prefix="/notebook", tags=["notebook"]) - -# Graceful shutdown by closing websockets -original_handler = Server.handle_exit - - -class AppStatus: - should_exit = False - - @staticmethod - def handle_exit(*args, **kwargs): - AppStatus.should_exit = True - print("Shutting down") - original_handler(*args, **kwargs) - - -Server.handle_exit = AppStatus.handle_exit - - -def session(x_continue_session_id: str = Header("anonymous")) -> Session: - return session_manager.get_session(x_continue_session_id) - - -def websocket_session(session_id: str) -> Session: - return session_manager.get_session(session_id) - - -T = TypeVar("T", bound=BaseModel) - -# You should probably abstract away the websocket stuff into a separate class - - -class NotebookProtocolServer(AbstractNotebookProtocolServer): - websocket: WebSocket - session: Session - sub_queue: AsyncSubscriptionQueue = AsyncSubscriptionQueue() - - def __init__(self, session: Session): - self.session = session - - async def _send_json(self, message_type: str, data: Any): - await self.websocket.send_json({ - "messageType": message_type, - "data": data - }) - - async def _receive_json(self, message_type: str) -> Any: - return await self.sub_queue.get(message_type) - - async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: - await self._send_json(message_type, data) - resp = await self._receive_json(message_type) - return resp_model.parse_obj(resp) - - def handle_json(self, message_type: str, data: Any): - try: - if message_type == "main_input": - self.on_main_input(data["input"]) - elif message_type == "step_user_input": - self.on_step_user_input(data["input"], data["index"]) - elif message_type == "refinement_input": - self.on_refinement_input(data["input"], data["index"]) - elif message_type == "reverse_to_index": - self.on_reverse_to_index(data["index"]) - except Exception as e: - print(e) - - async def send_state_update(self): - state = self.session.autopilot.get_full_state().dict() - await self._send_json("state_update", { - "state": state - }) - - def on_main_input(self, input: str): - # Do something with user input - asyncio.create_task(self.session.autopilot.accept_user_input(input)) - - def on_reverse_to_index(self, index: int): - # Reverse the history to the given index - asyncio.create_task(self.session.autopilot.reverse_to_index(index)) - - def on_step_user_input(self, input: str, index: int): - asyncio.create_task( - self.session.autopilot.give_user_input(input, index)) - - def on_refinement_input(self, input: str, index: int): - asyncio.create_task( - self.session.autopilot.accept_refinement_input(input, index)) - - -@router.websocket("/ws") -async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(websocket_session)): - await websocket.accept() - - print("Session started") - session_manager.register_websocket(session.session_id, websocket) - protocol = NotebookProtocolServer(session) - protocol.websocket = websocket - - # Update any history that may have happened before connection - await protocol.send_state_update() - - while AppStatus.should_exit is False: - message = await websocket.receive_text() - print("Received message", message) - if type(message) is str: - message = json.loads(message) - - if "messageType" not in message or "data" not in message: - continue - message_type = message["messageType"] - data = message["data"] - - protocol.handle_json(message_type, data) - - print("Closing websocket") - await websocket.close() diff --git a/continuedev/src/continuedev/server/notebook_protocol.py b/continuedev/src/continuedev/server/notebook_protocol.py deleted file mode 100644 index c2be82e0..00000000 --- a/continuedev/src/continuedev/server/notebook_protocol.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Any -from abc import ABC, abstractmethod - - -class AbstractNotebookProtocolServer(ABC): - @abstractmethod - async def handle_json(self, data: Any): - """Handle a json message""" - - @abstractmethod - def on_main_input(self, input: str): - """Called when the user inputs something""" - - @abstractmethod - def on_reverse_to_index(self, index: int): - """Called when the user requests reverse to a previous index""" - - @abstractmethod - def on_refinement_input(self, input: str, index: int): - """Called when the user inputs a refinement""" - - @abstractmethod - def on_step_user_input(self, input: str, index: int): - """Called when the user inputs a step""" - - @abstractmethod - async def send_state_update(self, state: dict): - """Send a state update to the client""" diff --git a/docs/docs/concepts/agent.md b/docs/docs/concepts/agent.md deleted file mode 100644 index bbcc6f57..00000000 --- a/docs/docs/concepts/agent.md +++ /dev/null @@ -1,8 +0,0 @@ -# Autopilot - -`Autopilot` contains the - -- History -- LLM -- Policy -- IDE diff --git a/docs/docs/concepts/autopilot.md b/docs/docs/concepts/autopilot.md new file mode 100644 index 00000000..71090eb0 --- /dev/null +++ b/docs/docs/concepts/autopilot.md @@ -0,0 +1,30 @@ +# Autopilot + +**TODO: Better explain in one sentence what this is and what its purpose is** + +:::info +The **autopilot** is the main loop, completing steps and then deciding the next step and repeating +::: + +## Details + +The Autopilot class is the center of Continue. Every step is initiated from the Autopilot, which provides it with a ContinueSDK. + +- Records history +- Allows reversal +- Injects SDK +- Has policy to decide what step to take next +- Accepts user input and acts on it +- Main event loop +- Contains main classes that are provided through the SDK, including LLM, History, IDE + +--- + +- An autopilot takes user input from the React app +- You can see this happening in `server/gui.py` +- It basically queues user inputs, pops off the most recent, runs that as a "UserInputStep", uses its Policy to run other steps until the next step is None, and then pops off the next user input. When nothing left, just waits for more +- `Autopilot` contains the + - History + - LLM + - Policy + - IDE diff --git a/docs/docs/concepts/gui.md b/docs/docs/concepts/gui.md index dfdc2a7a..f9cff697 100644 --- a/docs/docs/concepts/gui.md +++ b/docs/docs/concepts/gui.md @@ -3,11 +3,11 @@ The `GUI` enables you to guide steps and makes everything transparent, so you can review all steps that were automated, giving you the opportunity to undo and rerun any that ran incorrectly. **From GUI to Core** + - Natural language instructions from the developer - Hover / clicked on a step - Other user input **From Core to GUI** -- Updates to state (e.g. a new step) -**Q: do we call this the Continue GUI or Notebook?** \ No newline at end of file +- Updates to state (e.g. a new step) diff --git a/docs/docs/concepts/ide.md b/docs/docs/concepts/ide.md index 980b589d..4684c362 100644 --- a/docs/docs/concepts/ide.md +++ b/docs/docs/concepts/ide.md @@ -24,9 +24,9 @@ Get the workspace directory Set whether a file is open -### openNotebook +### openGUI -Open a notebook +Open a gui ### showSuggestionsAndWait @@ -44,13 +44,13 @@ Called when a traceback is received Called when a file system update is received -### onCloseNotebook +### onCloseGUI -Called when a notebook is closed +Called when a gui is closed -### onOpenNotebookRequest +### onOpenGUIRequest -Called when a notebook is requested to be opened +Called when a gui is requested to be opened ### getOpenFiles @@ -78,4 +78,4 @@ Apply a file edit ### saveFile -Save a file \ No newline at end of file +Save a file diff --git a/docs/docs/how-continue-works.md b/docs/docs/how-continue-works.md new file mode 100644 index 00000000..e6648cbc --- /dev/null +++ b/docs/docs/how-continue-works.md @@ -0,0 +1,37 @@ +# How `Continue` works + +![Continue Architecture Diagram](/img/continue-architecture.png) + +## Overview + +The `Continue` library consists of an [SDK](./concepts/sdk.md), a [GUI](./concepts/gui.md), and a [Core](./concepts/core.md) that brings everything together. + +The [SDK](./concepts/sdk.md) gives you access to the tools (e.g. open a directory, edit a file, call a model, etc.) needed to define steps that integrate LLMs into your IDE. + +The [GUI](./concepts/gui.md) lets you transparently review every automated step, providing the opportunity to undo and rerun any that ran incorrectly. + +The [Core](./concepts/core.md) holds the main event loop, responsible for connecting IDE, SDK, and GUI and deciding which steps to take next. + +## Details + +**TODO: Refactor all of this and make it fit with language above** + +- Continue connects any code editor (primarily VS Code right now) to a server (the Continue server) that can take actions in the editor in accordance with defined recipes at the request of a user through the GUI +- What this looks like: + - The Continue VS Code extension runs the ContinueIdeProtocol, launches the Continue Python server in the background, and opens the Continue GUI in a side-panel. + - The Continue server is the brain, communication center, and source of truth, interacting with VS Code through the ContinueIdeProtocol and with the GUI through the GUIProtocol. + - Communication between the extension and GUI happens through the Continue server. + - When you type a natural language command in the GUI, this is sent to the Continue server, where the `Autopilot` class takes action, potentially using the ContinueIdeProtocol to request actions be taken in the IDE, and then updates the GUI to display the new history. +- `core` directory contains major concepts + - This includes Autopilot, Policy, SDK (all in their own files so far) + - It also includes `main.py`, which contains History, HistoryNode, Step, and others + - You'll find `env.py` here too, which is a common place to load environment variables, which can then be imported from here +- `libs` contains misc. stuff +- `llm` for language model utilities +- `steps` for builtin Continue steps +- `util` for very misc. stuff +- `chroma` for chroma code that deals with codebase embeddings +- `models` contains all the Pydantic models and `generate_json_schema.py`, a script that converts them to JSONSchema .json files in `schema/json` +- `server` runs the servers that communicate with a) the React app (`gui.py`) and b) the IDE (`ide.py`) +- `ide_protocol.py` is just the abstract version of what is implemented in `ide.py`, and `main.py` runs both `gui.py` and `ide.py` as a single FastAPI server. This is the entry point to the Continue server, and acts as a bridge between IDE and React app +- We use OpenAPI/JSONSchema to define types so that it's really easy to bring them across language barriers. Use Pydantic types, then run `poetry run typegen` from the root of continuedev folder to generate JSONSchema json files in the `schema/json` folder. Then `npm run typegen` from the extension folder generates the types that are used within the extension. 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() { , - title: "Notebook", + element: , + title: "GUI", }, // { element: , title: "Debug Panel" }, // { element: , title: "Welcome" }, 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/ContinueNotebookClientProtocol.ts b/extension/react-app/src/hooks/ContinueNotebookClientProtocol.ts deleted file mode 100644 index 75fd7373..00000000 --- a/extension/react-app/src/hooks/ContinueNotebookClientProtocol.ts +++ /dev/null @@ -1,13 +0,0 @@ -abstract class AbstractContinueNotebookClientProtocol { - 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 AbstractContinueNotebookClientProtocol; 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/useContinueNotebookProtocol.ts b/extension/react-app/src/hooks/useContinueNotebookProtocol.ts deleted file mode 100644 index b785cc84..00000000 --- a/extension/react-app/src/hooks/useContinueNotebookProtocol.ts +++ /dev/null @@ -1,49 +0,0 @@ -import AbstractContinueNotebookClientProtocol from "./ContinueNotebookClientProtocol"; -// import { Messenger, WebsocketMessenger } from "../../../src/util/messenger"; -import { Messenger, WebsocketMessenger } from "./messenger"; -import { VscodeMessenger } from "./vscodeMessenger"; - -class ContinueNotebookClientProtocol extends AbstractContinueNotebookClientProtocol { - 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 ContinueNotebookClientProtocol; diff --git a/extension/react-app/src/hooks/useWebsocket.ts b/extension/react-app/src/hooks/useWebsocket.ts index 016fa17d..e762666f 100644 --- a/extension/react-app/src/hooks/useWebsocket.ts +++ b/extension/react-app/src/hooks/useWebsocket.ts @@ -1,15 +1,15 @@ import React, { useEffect, useState } from "react"; import { RootStore } from "../redux/store"; import { useSelector } from "react-redux"; -import ContinueNotebookClientProtocol from "./useContinueNotebookProtocol"; +import ContinueGUIClientProtocol from "./useContinueGUIProtocol"; import { postVscMessage } from "../vscode"; -function useContinueNotebookProtocol(useVscodeMessagePassing: boolean = true) { +function useContinueGUIProtocol(useVscodeMessagePassing: boolean = true) { const sessionId = useSelector((state: RootStore) => state.config.sessionId); const serverHttpUrl = useSelector((state: RootStore) => state.config.apiUrl); - const [client, setClient] = useState< - ContinueNotebookClientProtocol | undefined - >(undefined); + const [client, setClient] = useState( + undefined + ); useEffect(() => { if (!sessionId || !serverHttpUrl) { @@ -22,12 +22,12 @@ function useContinueNotebookProtocol(useVscodeMessagePassing: boolean = true) { const serverUrlWithSessionId = serverHttpUrl.replace("http", "ws") + - "/notebook/ws?session_id=" + + "/gui/ws?session_id=" + encodeURIComponent(sessionId); console.log("Creating websocket", serverUrlWithSessionId); console.log("Using vscode message passing", useVscodeMessagePassing); - const newClient = new ContinueNotebookClientProtocol( + const newClient = new ContinueGUIClientProtocol( serverUrlWithSessionId, useVscodeMessagePassing ); @@ -36,4 +36,4 @@ function useContinueNotebookProtocol(useVscodeMessagePassing: boolean = true) { return client; } -export default useContinueNotebookProtocol; +export default useContinueGUIProtocol; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx new file mode 100644 index 00000000..5ddddbfc --- /dev/null +++ b/extension/react-app/src/tabs/gui.tsx @@ -0,0 +1,265 @@ +import styled from "styled-components"; +import { + Button, + defaultBorderRadius, + vscBackground, + MainTextInput, + Loader, +} from "../components"; +import ContinueButton from "../components/ContinueButton"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { History } from "../../../schema/History"; +import { HistoryNode } from "../../../schema/HistoryNode"; +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 TopGUIDiv = styled.div` + display: grid; + grid-template-columns: 1fr; +`; + +let UserInputQueueItem = styled.div` + border-radius: ${defaultBorderRadius}; + color: gray; + padding: 8px; + margin: 8px; + text-align: center; +`; + +interface GUIProps { + firstObservation?: any; +} + +function GUI(props: GUIProps) { + const [waitingForSteps, setWaitingForSteps] = useState(false); + const [userInputQueue, setUserInputQueue] = useState([]); + const [history, setHistory] = useState(); + // { + // timeline: [ + // { + // step: { + // name: "RunCodeStep", + // cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py", + // description: + // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`", + // }, + // output: [ + // { + // traceback: { + // frames: [ + // { + // filepath: + // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", + // lineno: 7, + // function: "", + // code: "print(sum(first, second))", + // }, + // ], + // message: "unsupported operand type(s) for +: 'int' and 'str'", + // error_type: + // ' ^^^^^^^^^^^^^^^^^^\n File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n return a + b\n ~~^~~\nTypeError', + // full_traceback: + // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'", + // }, + // }, + // null, + // ], + // }, + // { + // step: { + // name: "EditCodeStep", + // range_in_files: [ + // { + // filepath: + // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", + // range: { + // start: { + // line: 0, + // character: 0, + // }, + // end: { + // line: 6, + // character: 25, + // }, + // }, + // }, + // ], + // prompt: + // "I ran into this problem with my Python code:\n\n Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'\n\n Below are the files that might need to be fixed:\n\n {code}\n\n This is what the code should be in order to avoid the problem:\n", + // description: + // "Editing files: /Users/natesesti/Desktop/continue/extension/examples/python/main.py", + // }, + // output: [ + // null, + // { + // reversible: true, + // actions: [ + // { + // reversible: true, + // filesystem: {}, + // filepath: + // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", + // range: { + // start: { + // line: 0, + // character: 0, + // }, + // end: { + // line: 6, + // character: 25, + // }, + // }, + // replacement: + // "\nfrom sum import sum\n\nfirst = 1\nsecond = 2\n\nprint(sum(first, second))", + // }, + // ], + // }, + // ], + // }, + // { + // step: { + // name: "SolveTracebackStep", + // traceback: { + // frames: [ + // { + // filepath: + // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", + // lineno: 7, + // function: "", + // code: "print(sum(first, second))", + // }, + // ], + // message: "unsupported operand type(s) for +: 'int' and 'str'", + // error_type: + // ' ^^^^^^^^^^^^^^^^^^\n File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n return a + b\n ~~^~~\nTypeError', + // full_traceback: + // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'", + // }, + // description: "Running step: SolveTracebackStep", + // }, + // output: [null, null], + // }, + // { + // step: { + // name: "RunCodeStep", + // cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py", + // description: + // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`", + // }, + // output: [null, null], + // }, + // ], + // current_index: 0, + // } as any + // ); + + const client = useContinueGUIProtocol(); + + 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(null); + + useEffect(() => { + if (mainTextInputRef.current) { + mainTextInputRef.current.focus(); + let handler = (event: any) => { + if (event.data.type === "focusContinueInput") { + mainTextInputRef.current?.focus(); + } + }; + window.addEventListener("message", handler); + return () => { + window.removeEventListener("message", handler); + }; + } + }, [mainTextInputRef]); + + const onMainTextInput = () => { + if (mainTextInputRef.current) { + if (!client) return; + let input = mainTextInputRef.current.value; + setWaitingForSteps(true); + client.sendMainInput(input); + setUserInputQueue((queue) => { + return [...queue, input]; + }); + mainTextInputRef.current.value = ""; + mainTextInputRef.current.style.height = ""; + } + }; + + const onStepUserInput = (input: string, index: number) => { + if (!client) return; + console.log("Sending step user input", input, index); + client.sendStepUserInput(input, index); + }; + + // const iterations = useSelector(selectIterations); + return ( + + {typeof client === "undefined" && ( + <> + +

Server disconnected

+ + )} + {history?.timeline.map((node: HistoryNode, index: number) => { + return ( + { + onStepUserInput(input, index); + }} + inFuture={index > history?.current_index} + historyNode={node} + onRefinement={(input: string) => { + client?.sendRefinementInput(input, index); + }} + onReverse={() => { + client?.reverseToIndex(index); + }} + /> + ); + })} + {waitingForSteps && } + +
+ {userInputQueue.map((input) => { + return {input}; + })} +
+ + { + if (e.key === "Enter") { + onMainTextInput(); + e.stopPropagation(); + e.preventDefault(); + } + }} + rows={1} + onChange={() => { + let textarea = mainTextInputRef.current!; + textarea.style.height = ""; /* Reset the height*/ + textarea.style.height = + Math.min(textarea.scrollHeight - 15, 500) + "px"; + }} + > + +
+ ); +} + +export default GUI; diff --git a/extension/react-app/src/tabs/notebook.tsx b/extension/react-app/src/tabs/notebook.tsx deleted file mode 100644 index 02c9ff31..00000000 --- a/extension/react-app/src/tabs/notebook.tsx +++ /dev/null @@ -1,265 +0,0 @@ -import styled from "styled-components"; -import { - Button, - defaultBorderRadius, - vscBackground, - MainTextInput, - Loader, -} from "../components"; -import ContinueButton from "../components/ContinueButton"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { History } from "../../../schema/History"; -import { HistoryNode } from "../../../schema/HistoryNode"; -import StepContainer from "../components/StepContainer"; -import { useSelector } from "react-redux"; -import { RootStore } from "../redux/store"; -import useContinueWebsocket from "../hooks/useWebsocket"; -import useContinueNotebookProtocol from "../hooks/useWebsocket"; - -let TopNotebookDiv = styled.div` - display: grid; - grid-template-columns: 1fr; -`; - -let UserInputQueueItem = styled.div` - border-radius: ${defaultBorderRadius}; - color: gray; - padding: 8px; - margin: 8px; - text-align: center; -`; - -interface NotebookProps { - firstObservation?: any; -} - -function Notebook(props: NotebookProps) { - const [waitingForSteps, setWaitingForSteps] = useState(false); - const [userInputQueue, setUserInputQueue] = useState([]); - const [history, setHistory] = useState(); - // { - // timeline: [ - // { - // step: { - // name: "RunCodeStep", - // cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py", - // description: - // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`", - // }, - // output: [ - // { - // traceback: { - // frames: [ - // { - // filepath: - // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", - // lineno: 7, - // function: "", - // code: "print(sum(first, second))", - // }, - // ], - // message: "unsupported operand type(s) for +: 'int' and 'str'", - // error_type: - // ' ^^^^^^^^^^^^^^^^^^\n File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n return a + b\n ~~^~~\nTypeError', - // full_traceback: - // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'", - // }, - // }, - // null, - // ], - // }, - // { - // step: { - // name: "EditCodeStep", - // range_in_files: [ - // { - // filepath: - // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", - // range: { - // start: { - // line: 0, - // character: 0, - // }, - // end: { - // line: 6, - // character: 25, - // }, - // }, - // }, - // ], - // prompt: - // "I ran into this problem with my Python code:\n\n Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'\n\n Below are the files that might need to be fixed:\n\n {code}\n\n This is what the code should be in order to avoid the problem:\n", - // description: - // "Editing files: /Users/natesesti/Desktop/continue/extension/examples/python/main.py", - // }, - // output: [ - // null, - // { - // reversible: true, - // actions: [ - // { - // reversible: true, - // filesystem: {}, - // filepath: - // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", - // range: { - // start: { - // line: 0, - // character: 0, - // }, - // end: { - // line: 6, - // character: 25, - // }, - // }, - // replacement: - // "\nfrom sum import sum\n\nfirst = 1\nsecond = 2\n\nprint(sum(first, second))", - // }, - // ], - // }, - // ], - // }, - // { - // step: { - // name: "SolveTracebackStep", - // traceback: { - // frames: [ - // { - // filepath: - // "/Users/natesesti/Desktop/continue/extension/examples/python/main.py", - // lineno: 7, - // function: "", - // code: "print(sum(first, second))", - // }, - // ], - // message: "unsupported operand type(s) for +: 'int' and 'str'", - // error_type: - // ' ^^^^^^^^^^^^^^^^^^\n File "/Users/natesesti/Desktop/continue/extension/examples/python/sum.py", line 2, in sum\n return a + b\n ~~^~~\nTypeError', - // full_traceback: - // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'", - // }, - // description: "Running step: SolveTracebackStep", - // }, - // output: [null, null], - // }, - // { - // step: { - // name: "RunCodeStep", - // cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py", - // description: - // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`", - // }, - // output: [null, null], - // }, - // ], - // current_index: 0, - // } as any - // ); - - const client = useContinueNotebookProtocol(); - - 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(null); - - useEffect(() => { - if (mainTextInputRef.current) { - mainTextInputRef.current.focus(); - let handler = (event: any) => { - if (event.data.type === "focusContinueInput") { - mainTextInputRef.current?.focus(); - } - }; - window.addEventListener("message", handler); - return () => { - window.removeEventListener("message", handler); - }; - } - }, [mainTextInputRef]); - - const onMainTextInput = () => { - if (mainTextInputRef.current) { - if (!client) return; - let input = mainTextInputRef.current.value; - setWaitingForSteps(true); - client.sendMainInput(input); - setUserInputQueue((queue) => { - return [...queue, input]; - }); - mainTextInputRef.current.value = ""; - mainTextInputRef.current.style.height = ""; - } - }; - - const onStepUserInput = (input: string, index: number) => { - if (!client) return; - console.log("Sending step user input", input, index); - client.sendStepUserInput(input, index); - }; - - // const iterations = useSelector(selectIterations); - return ( - - {typeof client === "undefined" && ( - <> - -

Server disconnected

- - )} - {history?.timeline.map((node: HistoryNode, index: number) => { - return ( - { - onStepUserInput(input, index); - }} - inFuture={index > history?.current_index} - historyNode={node} - onRefinement={(input: string) => { - client?.sendRefinementInput(input, index); - }} - onReverse={() => { - client?.reverseToIndex(index); - }} - /> - ); - })} - {waitingForSteps && } - -
- {userInputQueue.map((input) => { - return {input}; - })} -
- - { - if (e.key === "Enter") { - onMainTextInput(); - e.stopPropagation(); - e.preventDefault(); - } - }} - rows={1} - onChange={() => { - let textarea = mainTextInputRef.current!; - textarea.style.height = ""; /* Reset the height*/ - textarea.style.height = - Math.min(textarea.scrollHeight - 15, 500) + "px"; - }} - > - -
- ); -} - -export default Notebook; diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index f8f3c65a..40def480 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -59,10 +59,10 @@ export function activateExtension( }) ), ]).then(() => { - ideProtocolClient?.openNotebook(); + ideProtocolClient?.openGUI(); }); } else { - ideProtocolClient.openNotebook().then(() => { + ideProtocolClient.openGUI().then(() => { // openCapturedTerminal(); }); } diff --git a/extension/src/commands.ts b/extension/src/commands.ts index aeeb4b4f..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 477d1420..ab890801 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -97,7 +97,7 @@ class IdeProtocolClient { this.openFile(data.filepath); // TODO: Close file break; - case "openNotebook": + case "openGUI": case "connected": break; default: @@ -133,17 +133,17 @@ class IdeProtocolClient { // ------------------------------------ // // Initiate Request - closeNotebook(sessionId: string) { + closeGUI(sessionId: string) { this.panels.get(sessionId)?.dispose(); this.panels.delete(sessionId); } - async openNotebook() { + async openGUI() { console.log("OPENING NOTEBOOK"); if (this.messenger === null) { console.log("MESSENGER IS NULL"); } - const resp = await this.messenger?.sendAndReceive("openNotebook", {}); + const resp = await this.messenger?.sendAndReceive("openGUI", {}); const sessionId = resp.sessionId; console.log("SESSION ID", sessionId); -- cgit v1.2.3-70-g09d2 From 5e1216968b4bb1d67438e9d1b329932c5d55daab Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 2 Jun 2023 12:40:06 -0400 Subject: minor --- continuedev/src/continuedev/libs/steps/draft/dlt.py | 9 +++++++++ continuedev/src/continuedev/libs/steps/main.py | 4 +++- extension/src/continueIdeClient.ts | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/libs/steps/draft/dlt.py b/continuedev/src/continuedev/libs/steps/draft/dlt.py index 73762327..f3417c9d 100644 --- a/continuedev/src/continuedev/libs/steps/draft/dlt.py +++ b/continuedev/src/continuedev/libs/steps/draft/dlt.py @@ -50,6 +50,15 @@ class SetupPipelineStep(Step): class ValidatePipelineStep(Step): + + async def describe(self, models: Models): + return dedent("""\ + This step will validate that your dlt pipeline is working as expected: + - Test that the API call works + - Load the data into a local DuckDB instance + - Write a query to view the data + """) + async def run(self, sdk: ContinueSDK): source_name = sdk.history.last_observation().values["source_name"] filename = f'{source_name}.py' diff --git a/continuedev/src/continuedev/libs/steps/main.py b/continuedev/src/continuedev/libs/steps/main.py index c70d5c2c..aefbe084 100644 --- a/continuedev/src/continuedev/libs/steps/main.py +++ b/continuedev/src/continuedev/libs/steps/main.py @@ -218,8 +218,10 @@ class StarCoderEditHighlightedCodeStep(Step): hide = False _prompt: str = "{code}{user_request}" + _prompt_and_completion: str = "" + async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "Editing highlighted code" + return (await models.gpt35()).complete(f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points:") async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: range_in_files = await sdk.ide.getHighlightedCode() diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index ab890801..03e5fbc5 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -139,7 +139,7 @@ class IdeProtocolClient { } async openGUI() { - console.log("OPENING NOTEBOOK"); + console.log("OPENING GUI"); if (this.messenger === null) { console.log("MESSENGER IS NULL"); } -- cgit v1.2.3-70-g09d2 From 20a388db56d380d5520bf90ccb596fbbb2eae468 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 2 Jun 2023 17:54:17 -0400 Subject: openFile fix for codespaces --- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- extension/src/util/vscode.ts | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'extension/src') diff --git a/extension/package-lock.json b/extension/package-lock.json index 647e3aa2..15ea6428 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.13", + "version": "0.0.14", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.13", + "version": "0.0.14", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 1219ca8e..2d83a58c 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.13", + "version": "0.0.14", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/src/util/vscode.ts b/extension/src/util/vscode.ts index 4eab98a7..59e1ae8e 100644 --- a/extension/src/util/vscode.ts +++ b/extension/src/util/vscode.ts @@ -135,6 +135,9 @@ export function openEditorAndRevealRange( ): Promise { return new Promise((resolve, _) => { // Check if the editor is already open + if (editorFilename.startsWith("file://")) { + editorFilename = editorFilename.slice(7); + } vscode.workspace.openTextDocument(editorFilename).then((doc) => { vscode.window .showTextDocument( -- cgit v1.2.3-70-g09d2 From fcc3249323a30839d13c7fb256a8d967a8417bbf Mon Sep 17 00:00:00 2001 From: Nate Sesti <33237525+sestinj@users.noreply.github.com> Date: Fri, 2 Jun 2023 23:08:14 +0000 Subject: Fixed codespaces showTextDocument error --- extension/src/util/vscode.ts | 46 ++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'extension/src') diff --git a/extension/src/util/vscode.ts b/extension/src/util/vscode.ts index 59e1ae8e..a76b53c7 100644 --- a/extension/src/util/vscode.ts +++ b/extension/src/util/vscode.ts @@ -128,28 +128,40 @@ export async function readFileAtRange( }); } +let showTextDocumentInProcess = false; + export function openEditorAndRevealRange( editorFilename: string, range?: vscode.Range, viewColumn?: vscode.ViewColumn ): Promise { return new Promise((resolve, _) => { - // Check if the editor is already open - if (editorFilename.startsWith("file://")) { - editorFilename = editorFilename.slice(7); - } - vscode.workspace.openTextDocument(editorFilename).then((doc) => { - vscode.window - .showTextDocument( - doc, - getViewColumnOfFile(editorFilename) || viewColumn - ) - .then((editor) => { - if (range) { - editor.revealRange(range); - } - resolve(editor); - }); - }); + vscode.workspace.openTextDocument(editorFilename).then(async (doc) => { + try { + // An error is thrown mysteriously if you open two documents in parallel, hence this + while (showTextDocumentInProcess) { + await new Promise((resolve) => { + setInterval(() => { + resolve(null); + }, 200); + }) + } + showTextDocumentInProcess = true; + vscode.window + .showTextDocument( + doc, + getViewColumnOfFile(editorFilename) || viewColumn + ) + .then((editor) => { + if (range) { + editor.revealRange(range); + } + resolve(editor); + showTextDocumentInProcess = false; + }) + } catch (err) { + console.log(err); + } + }); }); } -- cgit v1.2.3-70-g09d2 From 22f6e4a01aed7955f608fcaa2198dc7da7902f3e Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sat, 3 Jun 2023 11:33:05 -0400 Subject: API tokens set through global Vsc settings --- continuedev/src/continuedev/core/sdk.py | 20 +------------ continuedev/src/continuedev/server/ide.py | 35 ++++++++++------------ continuedev/src/continuedev/server/ide_protocol.py | 4 +++ continuedev/src/continuedev/steps/core/core.py | 1 + continuedev/src/continuedev/steps/main.py | 2 +- extension/package.json | 10 +++++++ .../react-app/src/components/StepContainer.tsx | 10 +++---- extension/src/continueIdeClient.ts | 27 +++++++++++++++++ 8 files changed, 64 insertions(+), 45 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 5d0f03fe..5ae471c4 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -11,7 +11,6 @@ from .observation import Observation from ..server.ide_protocol import AbstractIdeProtocolServer from .main import History, Step from ..steps.core.core import * -from .env import get_env_var, make_sure_env_exists class Autopilot: @@ -105,24 +104,7 @@ class ContinueSDK: return await self.run_step(FileSystemEditStep(edit=DeleteDirectory(path=path))) async def get_user_secret(self, env_var: str, prompt: str) -> str: - make_sure_env_exists() - - val = None - while val is None: - try: - val = get_env_var(env_var) - if val is not None: - return val - except: - pass - server_dir = os.getcwd() - env_path = os.path.join(server_dir, ".env") - await self.ide.setFileOpen(env_path) - await self.append_to_file(env_path, f'\n{env_var}=""') - await self.run_step(WaitForUserConfirmationStep(prompt=prompt)) - val = get_env_var(env_var) - - return val + return await self.ide.getUserSecret(env_var) async def get_config(self) -> ContinueConfig: dir = await self.ide.getWorkspaceDirectory() diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 71017ce0..eec5b607 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -40,46 +40,42 @@ Server.handle_exit = AppStatus.handle_exit class FileEditsUpdate(BaseModel): - messageType: str = "fileEdits" fileEdits: List[FileEditWithFullContents] class OpenFilesResponse(BaseModel): - messageType: str = "openFiles" openFiles: List[str] class HighlightedCodeResponse(BaseModel): - messageType: str = "highlightedCode" highlightedCode: List[RangeInFile] class ShowSuggestionRequest(BaseModel): - messageType: str = "showSuggestion" suggestion: FileEdit class ShowSuggestionResponse(BaseModel): - messageType: str = "showSuggestion" suggestion: FileEdit accepted: bool class ReadFileResponse(BaseModel): - messageType: str = "readFile" contents: str class EditFileResponse(BaseModel): - messageType: str = "editFile" fileEdit: FileEditWithFullContents class WorkspaceDirectoryResponse(BaseModel): - messageType: str = "workspaceDirectory" workspaceDirectory: str +class GetUserSecretResponse(BaseModel): + value: str + + T = TypeVar("T", bound=BaseModel) @@ -114,7 +110,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): fileEdits = list( map(lambda d: FileEditWithFullContents.parse_obj(d), data["fileEdits"])) self.onFileEdits(fileEdits) - elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory"]: + elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret"]: self.sub_queue.post(message_type, data) else: raise ValueError("Unknown message type", message_type) @@ -183,31 +179,31 @@ class IdeProtocolServer(AbstractIdeProtocolServer): # Request information. Session doesn't matter. async def getOpenFiles(self) -> List[str]: - resp = await self._send_and_receive_json({ - "messageType": "openFiles" - }, OpenFilesResponse, "openFiles") + resp = await self._send_and_receive_json({}, OpenFilesResponse, "openFiles") return resp.openFiles async def getWorkspaceDirectory(self) -> str: - resp = await self._send_and_receive_json({ - "messageType": "workspaceDirectory" - }, WorkspaceDirectoryResponse, "workspaceDirectory") + resp = await self._send_and_receive_json({}, WorkspaceDirectoryResponse, "workspaceDirectory") return resp.workspaceDirectory async def getHighlightedCode(self) -> List[RangeInFile]: - resp = await self._send_and_receive_json({ - "messageType": "highlightedCode" - }, HighlightedCodeResponse, "highlightedCode") + resp = await self._send_and_receive_json({}, HighlightedCodeResponse, "highlightedCode") return resp.highlightedCode async def readFile(self, filepath: str) -> str: """Read a file""" resp = await self._send_and_receive_json({ - "messageType": "readFile", "filepath": filepath }, ReadFileResponse, "readFile") return resp.contents + async def getUserSecret(self, key: str) -> str: + """Get a user secret""" + resp = await self._send_and_receive_json({ + "key": key + }, GetUserSecretResponse, "getUserSecret") + return resp.value + async def saveFile(self, filepath: str): """Save a file""" await self._send_json("saveFile", { @@ -222,7 +218,6 @@ class IdeProtocolServer(AbstractIdeProtocolServer): async def editFile(self, edit: FileEdit) -> FileEditWithFullContents: """Edit a file""" resp = await self._send_and_receive_json({ - "messageType": "editFile", "edit": edit.dict() }, EditFileResponse, "editFile") return resp.fileEdit diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 4f505e80..8f155415 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -78,3 +78,7 @@ class AbstractIdeProtocolServer(ABC): @abstractmethod async def saveFile(self, filepath: str): """Save a file""" + + @abstractmethod + async def getUserSecret(self, key: str): + """Get a user secret""" diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index e54a9a21..0f513f3e 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -205,4 +205,5 @@ class WaitForUserConfirmationStep(Step): async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: self._description = self.prompt resp = await sdk.wait_for_user_input() + self.hide = True return TextObservation(text=resp) diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py index bb720b20..dfb4f3be 100644 --- a/continuedev/src/continuedev/steps/main.py +++ b/continuedev/src/continuedev/steps/main.py @@ -190,7 +190,7 @@ class FasterEditHighlightedCodeStep(Step): class StarCoderEditHighlightedCodeStep(Step): user_input: str - name: str = "Editing code" + name: str = "Editing Code" hide = False _prompt: str = "{code}{user_request}" diff --git a/extension/package.json b/extension/package.json index 13086954..cc8e18c4 100644 --- a/extension/package.json +++ b/extension/package.json @@ -39,6 +39,16 @@ "type": "string", "default": "http://localhost:8000", "description": "The URL of the Continue server to use." + }, + "continue.OPENAI_API_KEY": { + "type": "string", + "default": "", + "description": "The OpenAI API key to use for code generation." + }, + "continue.HUGGING_FACE_TOKEN": { + "type": "string", + "default": "", + "description": "The Hugging Face API token to use for code generation." } } }, diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 5e979b34..fd29f21b 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -114,12 +114,12 @@ function StepContainer(props: StepContainerProps) { hidden={props.historyNode.step.hide as any} > setOpen((prev) => !prev)} + className="m-2 overflow-hidden" + // onClick={() => setOpen((prev) => !prev)} > -

+

{open ? ( ) : ( @@ -127,14 +127,14 @@ function StepContainer(props: StepContainerProps) { )} {props.historyNode.step.name as any}:

- { e.stopPropagation(); props.onReverse(); }} > - + */}
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 03e5fbc5..a5a1c5dc 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -74,6 +74,12 @@ class IdeProtocolClient { this.messenger?.send("workspaceDirectory", { workspaceDirectory: this.getWorkspaceDirectory(), }); + break; + case "getUserSecret": + this.messenger?.send("getUserSecret", { + value: await this.getUserSecret(data.key), + }); + break; case "openFiles": this.messenger?.send("openFiles", { openFiles: this.getOpenFiles(), @@ -130,6 +136,27 @@ class IdeProtocolClient { openEditorAndRevealRange(filepath, undefined, vscode.ViewColumn.One); } + async getUserSecret(key: string) { + // Check if secret already exists in VS Code settings (global) + let secret = vscode.workspace.getConfiguration("continue").get(key); + if (secret && secret !== "") return secret; + + // If not, ask user for secret + while (typeof secret === "undefined" || secret === "") { + secret = await vscode.window.showInputBox({ + prompt: `Enter secret for ${key}`, + password: true, + }); + } + + // Add secret to VS Code settings + vscode.workspace + .getConfiguration("continue") + .update(key, secret, vscode.ConfigurationTarget.Global); + + return secret; + } + // ------------------------------------ // // Initiate Request -- cgit v1.2.3-70-g09d2 From 897279d79739711ad75e18ccb409e1671aa159f7 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 4 Jun 2023 18:04:39 -0400 Subject: docusaurus link fixing --- continuedev/pyproject.toml | 1 - continuedev/src/continuedev/core/sdk.py | 2 +- docs/docusaurus.config.js | 4 ++-- docs/src/pages/index.js | 26 +++++++++++---------- extension/DEV_README.md | 2 +- extension/README.md | 2 +- extension/package-lock.json | 4 ++-- extension/package.json | 24 +++++++++++++------ .../scripts/continuedev-0.1.1-py3-none-any.whl | Bin 55076 -> 55610 bytes extension/src/README.md | 2 +- extension/src/activation/activate.ts | 3 +++ extension/src/commands.ts | 4 ++-- extension/src/debugPanel.ts | 13 ++++++++++- extension/src/terminal/terminalEmulator.ts | 2 +- 14 files changed, 57 insertions(+), 32 deletions(-) (limited to 'extension/src') diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml index 83a287c8..631742ec 100644 --- a/continuedev/pyproject.toml +++ b/continuedev/pyproject.toml @@ -18,7 +18,6 @@ nest-asyncio = "^1.5.6" websockets = "^11.0.2" urllib3 = "1.26.15" gpt-index = "^0.6.8" -setuptools = "^67.7.2" posthog = "^3.0.1" [tool.poetry.scripts] diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index de14ee3c..5bd77d11 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -95,7 +95,7 @@ class ContinueSDK(AbstractContinueSDK): async def add_file(self, filename: str, content: str | None): filepath = await self._ensure_absolute_path(filename) - return await self.run_step(FileSystemEditStep(edit=AddFile(filename=filename, content=content))) + return await self.run_step(FileSystemEditStep(edit=AddFile(filepath=filepath, content=content))) async def delete_file(self, filename: str): filepath = await self._ensure_absolute_path(filename) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index c2991841..1730332a 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -15,7 +15,7 @@ const config = { url: "https://continue.dev", // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' - baseUrl: "/", + baseUrl: "/docs", // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these. @@ -82,7 +82,7 @@ const config = { items: [ { label: "Introduction", - to: "/docs/intro", + to: "/docs/docs/intro", }, ], }, diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js index e974e52a..cbe05d60 100644 --- a/docs/src/pages/index.js +++ b/docs/src/pages/index.js @@ -1,23 +1,24 @@ -import React from 'react'; -import clsx from 'clsx'; -import Link from '@docusaurus/Link'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import Layout from '@theme/Layout'; -import HomepageFeatures from '@site/src/components/HomepageFeatures'; +import React from "react"; +import clsx from "clsx"; +import Link from "@docusaurus/Link"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import Layout from "@theme/Layout"; +import HomepageFeatures from "@site/src/components/HomepageFeatures"; -import styles from './index.module.css'; +import styles from "./index.module.css"; function HomepageHeader() { - const {siteConfig} = useDocusaurusContext(); + const { siteConfig } = useDocusaurusContext(); return ( -
+

{siteConfig.title}

{siteConfig.tagline}

+ to="/docs/docs/getting-started" + > GitHub Codespaces Demo
@@ -27,11 +28,12 @@ function HomepageHeader() { } export default function Home() { - const {siteConfig} = useDocusaurusContext(); + const { siteConfig } = useDocusaurusContext(); return ( + description="Documentation for the `Continue` library" + >
diff --git a/extension/DEV_README.md b/extension/DEV_README.md index 7049da45..dd02bf59 100644 --- a/extension/DEV_README.md +++ b/extension/DEV_README.md @@ -3,5 +3,5 @@ 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`. +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.openContinueGUI` 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/README.md b/extension/README.md index 7fa8022b..12e25417 100644 --- a/extension/README.md +++ b/extension/README.md @@ -6,7 +6,7 @@ The Continue VS Code extension lets you make edits with natural langauge, ask qu ## Getting Started -Get started by opening the command pallet with cmd+shift+p and then selecting Continue: Open Debug Panel. +Get started by opening the command pallet with cmd+shift+p and then selecting Continue: Open Continue GUI. To test a few common recipes, open a blank python file and try the following: diff --git a/extension/package-lock.json b/extension/package-lock.json index ed140937..b923a2b2 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.19", + "version": "0.0.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.19", + "version": "0.0.20", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index dd15157d..87c78b75 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.19", + "version": "0.0.20", "publisher": "Continue", "engines": { "vscode": "^1.74.0" @@ -23,7 +23,8 @@ "Other" ], "activationEvents": [ - "onStartupFinished" + "onStartupFinished", + "onView:continueGUIView" ], "main": "./out/extension.js", "contributes": { @@ -59,9 +60,9 @@ "title": "Write a docstring for the current function" }, { - "command": "continue.openDebugPanel", + "command": "continue.openContinueGUI", "category": "Continue", - "title": "Open Debug Panel" + "title": "Open Continue GUI" }, { "command": "continue.askQuestionFromInput", @@ -149,9 +150,18 @@ "menus": { "view/title": [ { - "command": "continue.openDebugPanel", + "command": "continue.openContinueGUI", "group": "navigation", - "when": "view == continue.debugView" + "when": "view == continue.continueGUIView" + } + ] + }, + "views": { + "explorer": [ + { + "type": "webview", + "id": "continue.continueGUIView", + "name": "Continue GUI" } ] } @@ -171,7 +181,7 @@ "lint": "eslint src --ext ts", "test": "node ./out/test/runTest.js", "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.1-py3-none-any.whl ../extension/scripts/continuedev-0.1.1-py3-none-any.whl && cd ../extension && npm run typegen && npm run clientgen && cd react-app && npm run build && cd .. && npm run package", + "full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.1-py3-none-any.whl ../extension/scripts/continuedev-0.1.1-py3-none-any.whl && cd ../extension && npm install && npm run typegen && npm run clientgen && cd react-app && npm install && npm run build && cd .. && npm run package", "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" diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl index 043b0c06..5bd3ea7d 100644 Binary files a/extension/scripts/continuedev-0.1.1-py3-none-any.whl and b/extension/scripts/continuedev-0.1.1-py3-none-any.whl differ diff --git a/extension/src/README.md b/extension/src/README.md index 76b96ea0..9fd73f9f 100644 --- a/extension/src/README.md +++ b/extension/src/README.md @@ -67,7 +67,7 @@ You should always have a packaged version installed in VS Code, because when Con ## Commands - "Write a docstring for the current function" command (windows: `ctrl+alt+l`, mac: `shift+cmd+l`) -- "Open Debug Panel" command +- "Open Continue GUI" command - "Ask a question from input box" command (windows: `ctrl+alt+j`, mac: `shift+cmd+j`) - "Open Captured Terminal" command - "Ask a question from webview" command (what context is it given?) diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 40def480..293ee26c 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -7,6 +7,7 @@ import * as path from "path"; // import { openCapturedTerminal } from "../terminal/terminalEmulator"; import IdeProtocolClient from "../continueIdeClient"; import { getContinueServerUrl } from "../bridge"; +import { setupDebugPanel } from "../debugPanel"; export let extensionContext: vscode.ExtensionContext | undefined = undefined; @@ -21,6 +22,8 @@ export function activateExtension( registerAllCodeLensProviders(context); registerAllCommands(context); + // vscode.window.registerWebviewViewProvider("continue.continueGUIView", setupDebugPanel); + let serverUrl = getContinueServerUrl(); ideProtocolClient = new IdeProtocolClient( diff --git a/extension/src/commands.ts b/extension/src/commands.ts index f0c1744b..c98cd3c3 100644 --- a/extension/src/commands.ts +++ b/extension/src/commands.ts @@ -61,7 +61,7 @@ const commandsMap: { [command: string]: (...args: any) => any } = { "continue.suggestionUp": suggestionUpCommand, "continue.acceptSuggestion": acceptSuggestionCommand, "continue.rejectSuggestion": rejectSuggestionCommand, - "continue.openDebugPanel": () => { + "continue.openContinueGUI": () => { ideProtocolClient.openGUI(); }, "continue.focusContinueInput": async () => { @@ -111,7 +111,7 @@ const commandsMap: { [command: string]: (...args: any) => any } = { vscode.window.showInformationMessage("The test passes!"); return; } - vscode.commands.executeCommand("continue.openDebugPanel").then(() => { + vscode.commands.executeCommand("continue.openContinueGUI").then(() => { setTimeout(() => { debugPanelWebview?.postMessage({ type: "traceback", diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 87c33da1..7407faf4 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -135,7 +135,7 @@ let streamManager = new StreamManager(); export let debugPanelWebview: vscode.Webview | undefined; export function setupDebugPanel( - panel: vscode.WebviewPanel, + panel: vscode.WebviewPanel | vscode.WebviewView, context: vscode.ExtensionContext | undefined, sessionId: string ): string { @@ -487,3 +487,14 @@ export function setupDebugPanel( `; } + +// class ContinueGUIWebviewViewProvider implements vscode.WebviewViewProvider { +// public static readonly viewType = "continue.continueGUIView"; +// resolveWebviewView( +// webviewView: vscode.WebviewView, +// context: vscode.WebviewViewResolveContext, +// token: vscode.CancellationToken +// ): void | Thenable { +// setupDebugPanel(webviewView, context, sessionId); +// } +// } diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index ba860b24..6cf65970 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -19,7 +19,7 @@ // }); // } else { // vscode.commands -// .executeCommand("continue.openDebugPanel", extensionContext) +// .executeCommand("continue.openContinueGUI", extensionContext) // .then(() => { // // TODO: Waiting for the webview to load, but should add a hook to the onLoad message event. Same thing in autodebugTest command in commands.ts // setTimeout(() => { -- cgit v1.2.3-70-g09d2 From 61ef38efa40e6ef61efa375de5daea4306b635d9 Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Mon, 5 Jun 2023 08:53:48 +0200 Subject: outcomes of work session --- README.md | 1 + continuedev/README.md | 15 +- continuedev/pyproject.toml | 1 - continuedev/src/continuedev/core/abstract_sdk.py | 6 +- continuedev/src/continuedev/core/sdk.py | 4 +- continuedev/src/continuedev/steps/core/core.py | 4 +- docs/docs/catalog.md | 4 +- docs/docs/walkthroughs/create-a-recipe.md | 6 + docs/docs/walkthroughs/use-a-recipe.md | 8 +- extension/DEV_README.md | 2 + extension/package-lock.json | 2781 ++++++++++++++++++-- extension/package.json | 7 + .../scripts/continuedev-0.1.1-py3-none-any.whl | Bin 55076 -> 55604 bytes extension/src/README.md | 42 +- extension/src/activation/environmentSetup.ts | 2 +- 15 files changed, 2636 insertions(+), 247 deletions(-) (limited to 'extension/src') diff --git a/README.md b/README.md index 573c6da5..92570126 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ cd continue/extension/scripts && python3 install_from_source.py - [Continue GUI README](./extension/react-app/): learn about the React app that lets users interact with the server and is placed adjacent to the text editor in any suppported IDE - [Schema README](./schema): learn about the JSON Schema types generated from Pydantic models, which we use across the `continuedev/` and `extension/` directories - [Continue Docs README](./docs): learn how our [docs](https://continue.dev/docs) are written and built +- Add steps for development README mention here # License diff --git a/continuedev/README.md b/continuedev/README.md index e27cf3ae..b7967ddd 100644 --- a/continuedev/README.md +++ b/continuedev/README.md @@ -6,13 +6,14 @@ Continue is a Python library for automating repetitive sequences of software dev ## Continue Server -The Continue server acts as a bridge between the Continue React app and your IDE, running your recipes and acting on the codebase. Start it by running the following commands: - -- `cd continuedev` -- Make sure packages are installed with `poetry install` -- `poetry shell` -- `cd ..` -- `python3 -m continuedev.src.continuedev.server.main` +The Continue server acts as a bridge between the Continue React app and your IDE, running your recipes and acting on the codebase. + +Start it by running the following commands: +1. `cd continuedev` +2. Make sure packages are installed with `poetry install` +3. `poetry shell` +4. `cd ..` +5. `python3 -m continuedev.src.continuedev.server.main` ## Scripts diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml index 83a287c8..631742ec 100644 --- a/continuedev/pyproject.toml +++ b/continuedev/pyproject.toml @@ -18,7 +18,6 @@ nest-asyncio = "^1.5.6" websockets = "^11.0.2" urllib3 = "1.26.15" gpt-index = "^0.6.8" -setuptools = "^67.7.2" posthog = "^3.0.1" [tool.poetry.scripts] diff --git a/continuedev/src/continuedev/core/abstract_sdk.py b/continuedev/src/continuedev/core/abstract_sdk.py index 9278f873..1c800875 100644 --- a/continuedev/src/continuedev/core/abstract_sdk.py +++ b/continuedev/src/continuedev/core/abstract_sdk.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Coroutine, List +from typing import Coroutine, List, Union from .config import ContinueConfig from ..models.filesystem_edit import FileSystemEdit @@ -45,7 +45,7 @@ class AbstractContinueSDK(ABC): pass @abstractmethod - async def run(self, commands: List[str] | str, cwd: str = None): + async def run(self, commands: Union[List[str], str], cwd: str = None): pass @abstractmethod @@ -57,7 +57,7 @@ class AbstractContinueSDK(ABC): pass @abstractmethod - async def add_file(self, filename: str, content: str | None): + async def add_file(self, filename: str, content: Union[str, None]): pass @abstractmethod diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index de14ee3c..f9f3679e 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -73,7 +73,7 @@ class ContinueSDK(AbstractContinueSDK): async def wait_for_user_confirmation(self, prompt: str): return await self.run_step(WaitForUserConfirmationStep(prompt=prompt)) - async def run(self, commands: List[str] | str, cwd: str = None): + async def run(self, commands: Union[List[str], str], cwd: str = None): commands = commands if isinstance(commands, List) else [commands] return await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd)) @@ -93,7 +93,7 @@ class ContinueSDK(AbstractContinueSDK): file_edit = FileEdit.from_append(filepath, previous_content, content) await self.ide.applyFileSystemEdit(file_edit) - async def add_file(self, filename: str, content: str | None): + async def add_file(self, filename: str, content: Union[str, None]): filepath = await self._ensure_absolute_path(filename) return await self.run_step(FileSystemEditStep(edit=AddFile(filename=filename, content=content))) diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index d7f7a307..fdcd9837 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -40,10 +40,10 @@ class FileSystemEditStep(ReversibleStep): class ShellCommandsStep(Step): cmds: List[str] - cwd: str | None = None + cwd: Union[str, None] = None name: str = "Run Shell Commands" - _err_text: str | None = None + _err_text: Union[str, None] = None async def describe(self, models: Models) -> Coroutine[str, None, None]: if self._err_text is not None: diff --git a/docs/docs/catalog.md b/docs/docs/catalog.md index 25b1214d..b9e223e2 100644 --- a/docs/docs/catalog.md +++ b/docs/docs/catalog.md @@ -2,7 +2,7 @@ ## Steps -**TODO: better explain each step and link to them** +**TODO: better explain each step and link to them, find all in steps folder** #### EditCodeStep @@ -30,4 +30,4 @@ Type `/ask` and ask any question about your whole codebase. The Chroma embedding ## Recipes -**TODO: add each recipe, explain them, and link to them** \ No newline at end of file +**TODO: add each recipe, explain them, and link to them, find all in recipes folder** \ No newline at end of file diff --git a/docs/docs/walkthroughs/create-a-recipe.md b/docs/docs/walkthroughs/create-a-recipe.md index 6a32906a..60bfe9a8 100644 --- a/docs/docs/walkthroughs/create-a-recipe.md +++ b/docs/docs/walkthroughs/create-a-recipe.md @@ -11,8 +11,14 @@ Points to include - How to create recipe - Using models +1. Create a recipe here + +continue/continuedev/src/continuedev/recipes + ## 1. Create a step + + ### a. Start by creating a subclass of Step You should first consider what will be the parameters of your recipe. These are defined as attributes in the step, as with `input_file_path: str` below diff --git a/docs/docs/walkthroughs/use-a-recipe.md b/docs/docs/walkthroughs/use-a-recipe.md index a5be199e..dbe8901b 100644 --- a/docs/docs/walkthroughs/use-a-recipe.md +++ b/docs/docs/walkthroughs/use-a-recipe.md @@ -10,9 +10,15 @@ All of the recipes are located in this part of the codebase here At the moment, we only support editing the file open and focused in the code editor, so you need to make sure that is +user input step +demopolicy decides what to do +default step, starcoder to edit the current open file + ## Use its slash command to start the recipe -Every recipe can be triggered by the user using its slash command. For example, `/comment` will trigger +Every recipe can be triggered by the user using its slash command. For example, `/comment` will trigger. + +add to the if / else with `/` commands ## Review the steps and take any required manual actions diff --git a/extension/DEV_README.md b/extension/DEV_README.md index 7049da45..4df0e926 100644 --- a/extension/DEV_README.md +++ b/extension/DEV_README.md @@ -5,3 +5,5 @@ 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). + +1. Open Continue \ No newline at end of file diff --git a/extension/package-lock.json b/extension/package-lock.json index ed140937..c7c67fdc 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -13,11 +13,16 @@ "@reduxjs/toolkit": "^1.9.3", "@segment/analytics-node": "^0.0.1-beta.16", "@styled-icons/heroicons-outline": "^10.47.0", + "@vitejs/plugin-react-swc": "^3.3.2", "axios": "^1.2.5", "highlight.js": "^11.7.0", "octokit": "^2.0.11", + "posthog-js": "^1.63.3", + "react-markdown": "^8.0.7", "react-redux": "^8.0.5", "strip-ansi": "^7.0.1", + "tailwindcss": "^3.3.2", + "vite": "^4.3.9", "vscode-languageclient": "^8.0.2", "ws": "^8.13.0" }, @@ -27,6 +32,8 @@ "@types/mocha": "^10.0.1", "@types/node": "16.x", "@types/node-fetch": "^2.6.2", + "@types/react-dom": "^18.2.4", + "@types/styled-components": "^5.1.26", "@types/vscode": "^1.74.0", "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.45.0", @@ -44,6 +51,17 @@ "vscode": "^1.74.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", @@ -461,7 +479,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -477,7 +494,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -493,7 +509,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -509,7 +524,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -525,7 +539,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -541,7 +554,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -557,7 +569,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -573,7 +584,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -589,7 +599,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -605,7 +614,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -621,7 +629,6 @@ "cpu": [ "loong64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -637,7 +644,6 @@ "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -653,7 +659,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -669,7 +674,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -685,7 +689,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -701,7 +704,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -717,7 +719,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -733,7 +734,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -749,7 +749,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -765,7 +764,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -781,7 +779,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -797,7 +794,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -871,7 +867,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "peer": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -885,7 +880,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "peer": true, "engines": { "node": ">=6.0.0" } @@ -894,7 +888,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "peer": true, "engines": { "node": ">=6.0.0" } @@ -902,14 +895,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "peer": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.18", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -918,8 +909,7 @@ "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "peer": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jsdevtools/ono": { "version": "7.1.3", @@ -1048,7 +1038,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1061,7 +1050,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -1070,7 +1058,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1838,6 +1825,189 @@ "styled-components": ">=4.1.0 <6" } }, + "node_modules/@swc/core": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.62.tgz", + "integrity": "sha512-J58hWY+/G8vOr4J6ZH9hLg0lMSijZtqIIf4HofZezGog/pVX6sJyBJ40dZ1ploFkDIlWTWvJyqtpesBKS73gkQ==", + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.62", + "@swc/core-darwin-x64": "1.3.62", + "@swc/core-linux-arm-gnueabihf": "1.3.62", + "@swc/core-linux-arm64-gnu": "1.3.62", + "@swc/core-linux-arm64-musl": "1.3.62", + "@swc/core-linux-x64-gnu": "1.3.62", + "@swc/core-linux-x64-musl": "1.3.62", + "@swc/core-win32-arm64-msvc": "1.3.62", + "@swc/core-win32-ia32-msvc": "1.3.62", + "@swc/core-win32-x64-msvc": "1.3.62" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.62.tgz", + "integrity": "sha512-MmGilibITz68LEje6vJlKzc2gUUSgzvB3wGLSjEORikTNeM7P8jXVxE4A8fgZqDeudJUm9HVWrxCV+pHDSwXhA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.62.tgz", + "integrity": "sha512-Xl93MMB3sCWVlYWuQIB+v6EQgzoiuQYK5tNt9lsHoIEVu2zLdkQjae+5FUHZb1VYqCXIiWcULFfVz0R4Sjb7JQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.62.tgz", + "integrity": "sha512-nJsp6O7kCtAjTTMcIjVB0g5y1JNiYAa5q630eiwrnaHUusEFoANDdORI3Z9vXeikMkng+6yIv9/V8Rb093xLjQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.62.tgz", + "integrity": "sha512-XGsV93vpUAopDt5y6vPwbK1Nc/MlL55L77bAZUPIiosWD1cWWPHNtNSpriE6+I+JiMHe0pqtfS/SSTk6ZkFQVw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.62.tgz", + "integrity": "sha512-ESUmJjSlTTkoBy9dMG49opcNn8BmviqStMhwyeD1G8XRnmRVCZZgoBOKdvCXmJhw8bQXDhZumeaTUB+OFUKVXg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.62.tgz", + "integrity": "sha512-wnHJkt3ZBrax3SFnUHDcncG6mrSg9ZZjMhQV9Mc3JL1x1s1Gy9rGZCoBNnV/BUZWTemxIBcQbANRSDut/WO+9A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.62.tgz", + "integrity": "sha512-9oRbuTC/VshB66Rgwi3pTq3sPxSTIb8k9L1vJjES+dDMKa29DAjPtWCXG/pyZ00ufpFZgkGEuAHH5uqUcr1JQg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.62.tgz", + "integrity": "sha512-zv14vlF2VRrxS061XkfzGjCYnOrEo5glKJjLK5PwUKysIoVrx/L8nAbFxjkX5cObdlyoqo+ekelyBPAO+4bS0w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.62.tgz", + "integrity": "sha512-8MC/PZQSsOP2iA/81tAfNRqMWyEqTS/8zKUI67vPuLvpx6NAjRn3E9qBv7iFqH79iqZNzqSMo3awnLrKZyFbcw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.62.tgz", + "integrity": "sha512-GJSmUJ95HKHZXAxiuPUmrcm/S3ivQvEzXhOZaIqYBIwUsm02vFZkClsV7eIKzWjso1t0+I/8MjrnUNaSWqh1rQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -1879,6 +2049,14 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/glob": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz", @@ -1889,6 +2067,14 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -1936,6 +2122,14 @@ "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" }, + "node_modules/@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -1948,6 +2142,11 @@ "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, "node_modules/@types/node": { "version": "16.18.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", @@ -1998,6 +2197,15 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", + "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "devOptional": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -2017,6 +2225,22 @@ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, + "node_modules/@types/styled-components": { + "version": "5.1.26", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", + "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -2224,6 +2448,17 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.2.tgz", + "integrity": "sha512-VJFWY5sfoZerQRvJrh518h3AcQt6f/yTuWn4/TRB+dqmYU0NX1qz7qM5Wfd+gOQqUzQW4gxKqKN3KpE/P3+zrA==", + "dependencies": { + "@swc/core": "^1.3.61" + }, + "peerDependencies": { + "vite": "^4" + } + }, "node_modules/@vscode/test-electron": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.2.tgz", @@ -2381,14 +2616,12 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -2427,6 +2660,11 @@ "node": ">= 6" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2489,6 +2727,15 @@ "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==", "peer": true }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2544,7 +2791,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } @@ -2602,7 +2848,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -2788,6 +3033,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, "node_modules/camelize": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", @@ -2824,6 +3077,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -2872,7 +3134,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, "funding": [ { "type": "individual", @@ -2899,7 +3160,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -3056,6 +3316,15 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -3215,6 +3484,17 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", @@ -3271,6 +3551,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -3356,6 +3648,14 @@ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", @@ -3364,11 +3664,15 @@ "node": ">=8" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, "engines": { "node": ">=0.3.1" } @@ -3385,6 +3689,11 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3585,7 +3894,6 @@ "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" @@ -3897,6 +4205,11 @@ "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", "dev": true }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3945,7 +4258,6 @@ "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -3961,7 +4273,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -3991,7 +4302,6 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -4005,6 +4315,11 @@ "pend": "~1.2.0" } }, + "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/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -4045,7 +4360,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4187,7 +4501,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -4247,8 +4560,7 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/gauge": { "version": "4.0.4", @@ -4363,7 +4675,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -4464,7 +4775,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -4497,6 +4807,15 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -4715,6 +5034,11 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, "node_modules/inquirer": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", @@ -4771,7 +5095,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -4779,11 +5102,43 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -4800,7 +5155,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -4825,7 +5179,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -4893,6 +5246,14 @@ "node": ">=6" } }, + "node_modules/jiti": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", + "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-sdsl": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", @@ -4906,8 +5267,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "peer": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -5109,6 +5469,14 @@ "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -5131,6 +5499,19 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, "node_modules/linkify-it": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", @@ -5191,7 +5572,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5337,6 +5717,74 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", @@ -5363,16 +5811,435 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "engines": { "node": ">= 8" } }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -5663,6 +6530,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5678,7 +6553,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -5842,7 +6716,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5888,11 +6761,18 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", @@ -6122,62 +7002,231 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", + "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" } }, - "node_modules/path-to-regexp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", - "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, "engines": { - "node": ">=8" + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, "engines": { - "node": ">=8.6" + "node": ">=12.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "peer": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/posthog-js": { + "version": "1.63.3", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.63.3.tgz", + "integrity": "sha512-Ob2x1ENxx/mWAdhLhc6ouKAph9LwmitBy7M+hiaFoS0T9g7OVL4lYdrJbODbYV+HX/HeO3bsnQwYO1QLPWm/YA==", + "dependencies": { + "fflate": "^0.4.1" + } }, "node_modules/prebuild-install": { "version": "7.1.1", @@ -6252,6 +7301,30 @@ "node": ">=10" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/property-information": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz", + "integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -6294,7 +7367,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -6384,6 +7456,36 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, "node_modules/react-redux": { "version": "8.0.5", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", @@ -6434,6 +7536,14 @@ "node": ">=0.8" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -6459,7 +7569,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -6506,6 +7615,35 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6519,6 +7657,22 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz", "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", @@ -6568,7 +7722,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -6607,6 +7760,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.1.tgz", + "integrity": "sha512-ybRdFVHOoljGEFILHLd2g/qateqUdjE6YS41WXq4p3C/WwD3xtWxV4FYWETA1u9TeXQc5K8L8zHE5d/scOvrOQ==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -6620,7 +7788,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -6654,6 +7821,17 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -6861,6 +8039,23 @@ "node": ">= 10" } }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", @@ -6949,6 +8144,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-object": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.1.tgz", + "integrity": "sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, "node_modules/styled-components": { "version": "5.3.9", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.9.tgz", @@ -7000,6 +8203,54 @@ "node": ">=4" } }, + "node_modules/sucrase": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", + "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7011,6 +8262,54 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", + "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tar": { "version": "6.1.13", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", @@ -7107,7 +8406,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "dependencies": { "any-promise": "^1.0.0" } @@ -7116,7 +8414,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -7165,7 +8462,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -7196,6 +8492,29 @@ "tree-kill": "cli.js" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -7306,16 +8625,45 @@ "dependencies": { "@lukeed/csprng": "^1.0.0" }, - "engines": { - "node": ">=8" + "engines": { + "node": ">=8" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", - "dev": true - }, "node_modules/unique-filename": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", @@ -7338,6 +8686,78 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universal-github-app-jwt": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz", @@ -7416,6 +8836,98 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", + "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "dependencies": { + "esbuild": "^0.17.5", + "postcss": "^8.4.23", + "rollup": "^3.21.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, "node_modules/vsce": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.15.0.tgz", @@ -7740,6 +9252,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -7815,6 +9335,11 @@ } }, "dependencies": { + "@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" + }, "@babel/code-frame": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", @@ -8139,154 +9664,132 @@ "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": { @@ -8338,7 +9841,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "peer": true, "requires": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -8348,26 +9850,22 @@ "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "peer": true + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" }, "@jridgewell/set-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "peer": true + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" }, "@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "peer": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "@jridgewell/trace-mapping": { "version": "0.3.18", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "peer": true, "requires": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -8376,8 +9874,7 @@ "@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "peer": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" } } }, @@ -8466,7 +9963,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -8475,14 +9971,12 @@ "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" }, "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -9073,6 +10567,83 @@ "@babel/runtime": "^7.19.0" } }, + "@swc/core": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.62.tgz", + "integrity": "sha512-J58hWY+/G8vOr4J6ZH9hLg0lMSijZtqIIf4HofZezGog/pVX6sJyBJ40dZ1ploFkDIlWTWvJyqtpesBKS73gkQ==", + "requires": { + "@swc/core-darwin-arm64": "1.3.62", + "@swc/core-darwin-x64": "1.3.62", + "@swc/core-linux-arm-gnueabihf": "1.3.62", + "@swc/core-linux-arm64-gnu": "1.3.62", + "@swc/core-linux-arm64-musl": "1.3.62", + "@swc/core-linux-x64-gnu": "1.3.62", + "@swc/core-linux-x64-musl": "1.3.62", + "@swc/core-win32-arm64-msvc": "1.3.62", + "@swc/core-win32-ia32-msvc": "1.3.62", + "@swc/core-win32-x64-msvc": "1.3.62" + } + }, + "@swc/core-darwin-arm64": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.62.tgz", + "integrity": "sha512-MmGilibITz68LEje6vJlKzc2gUUSgzvB3wGLSjEORikTNeM7P8jXVxE4A8fgZqDeudJUm9HVWrxCV+pHDSwXhA==", + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.62.tgz", + "integrity": "sha512-Xl93MMB3sCWVlYWuQIB+v6EQgzoiuQYK5tNt9lsHoIEVu2zLdkQjae+5FUHZb1VYqCXIiWcULFfVz0R4Sjb7JQ==", + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.62.tgz", + "integrity": "sha512-nJsp6O7kCtAjTTMcIjVB0g5y1JNiYAa5q630eiwrnaHUusEFoANDdORI3Z9vXeikMkng+6yIv9/V8Rb093xLjQ==", + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.62.tgz", + "integrity": "sha512-XGsV93vpUAopDt5y6vPwbK1Nc/MlL55L77bAZUPIiosWD1cWWPHNtNSpriE6+I+JiMHe0pqtfS/SSTk6ZkFQVw==", + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.62.tgz", + "integrity": "sha512-ESUmJjSlTTkoBy9dMG49opcNn8BmviqStMhwyeD1G8XRnmRVCZZgoBOKdvCXmJhw8bQXDhZumeaTUB+OFUKVXg==", + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.62.tgz", + "integrity": "sha512-wnHJkt3ZBrax3SFnUHDcncG6mrSg9ZZjMhQV9Mc3JL1x1s1Gy9rGZCoBNnV/BUZWTemxIBcQbANRSDut/WO+9A==", + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.62.tgz", + "integrity": "sha512-9oRbuTC/VshB66Rgwi3pTq3sPxSTIb8k9L1vJjES+dDMKa29DAjPtWCXG/pyZ00ufpFZgkGEuAHH5uqUcr1JQg==", + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.62.tgz", + "integrity": "sha512-zv14vlF2VRrxS061XkfzGjCYnOrEo5glKJjLK5PwUKysIoVrx/L8nAbFxjkX5cObdlyoqo+ekelyBPAO+4bS0w==", + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.62.tgz", + "integrity": "sha512-8MC/PZQSsOP2iA/81tAfNRqMWyEqTS/8zKUI67vPuLvpx6NAjRn3E9qBv7iFqH79iqZNzqSMo3awnLrKZyFbcw==", + "optional": true + }, + "@swc/core-win32-x64-msvc": { + "version": "1.3.62", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.62.tgz", + "integrity": "sha512-GJSmUJ95HKHZXAxiuPUmrcm/S3ivQvEzXhOZaIqYBIwUsm02vFZkClsV7eIKzWjso1t0+I/8MjrnUNaSWqh1rQ==", + "optional": true + }, "@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -9108,6 +10679,14 @@ "@types/responselike": "^1.0.0" } }, + "@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "requires": { + "@types/ms": "*" + } + }, "@types/glob": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz", @@ -9118,6 +10697,14 @@ "@types/node": "*" } }, + "@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "requires": { + "@types/unist": "*" + } + }, "@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -9165,6 +10752,14 @@ "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" }, + "@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", + "requires": { + "@types/unist": "*" + } + }, "@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -9177,6 +10772,11 @@ "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, "@types/node": { "version": "16.18.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", @@ -9226,6 +10826,15 @@ "csstype": "^3.0.2" } }, + "@types/react-dom": { + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", + "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "devOptional": true, + "requires": { + "@types/react": "*" + } + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -9245,6 +10854,22 @@ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, + "@types/styled-components": { + "version": "5.1.26", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", + "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, + "@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, "@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -9363,6 +10988,14 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@vitejs/plugin-react-swc": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.2.tgz", + "integrity": "sha512-VJFWY5sfoZerQRvJrh518h3AcQt6f/yTuWn4/TRB+dqmYU0NX1qz7qM5Wfd+gOQqUzQW4gxKqKN3KpE/P3+zrA==", + "requires": { + "@swc/core": "^1.3.61" + } + }, "@vscode/test-electron": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.2.tgz", @@ -9471,14 +11104,12 @@ "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -9510,6 +11141,11 @@ } } }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -9566,6 +11202,11 @@ "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==", "peer": true }, + "bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -9600,8 +11241,7 @@ "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, "bl": { "version": "4.1.0", @@ -9655,7 +11295,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -9787,6 +11426,11 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, "camelize": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", @@ -9811,6 +11455,11 @@ "supports-color": "^7.1.0" } }, + "character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -9850,7 +11499,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -9866,7 +11514,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -9982,6 +11629,11 @@ "delayed-stream": "~1.0.0" } }, + "comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" + }, "commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -10107,6 +11759,11 @@ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, "csstype": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", @@ -10142,6 +11799,14 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, + "decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "requires": { + "character-entities": "^2.0.0" + } + }, "decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -10202,16 +11867,25 @@ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, "detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" }, "dir-glob": { "version": "3.0.1", @@ -10222,6 +11896,11 @@ "path-type": "^4.0.0" } }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -10385,7 +12064,6 @@ "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", @@ -10625,6 +12303,11 @@ } } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -10666,7 +12349,6 @@ "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -10679,7 +12361,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -10708,7 +12389,6 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, "requires": { "reusify": "^1.0.4" } @@ -10722,6 +12402,11 @@ "pend": "~1.2.0" } }, + "fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -10752,7 +12437,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -10842,7 +12526,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "optional": true }, "fstream": { @@ -10885,8 +12568,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "gauge": { "version": "4.0.4", @@ -10988,7 +12670,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "requires": { "is-glob": "^4.0.3" } @@ -11049,7 +12730,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -11070,6 +12750,11 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==" + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -11229,6 +12914,11 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, "inquirer": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", @@ -11278,16 +12968,27 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "requires": { "binary-extensions": "^2.0.0" } }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -11298,7 +12999,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -11316,8 +13016,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-path-inside": { "version": "3.0.3", @@ -11364,6 +13063,11 @@ "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", "dev": true }, + "jiti": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", + "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==" + }, "js-sdsl": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", @@ -11373,8 +13077,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "peer": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "4.1.0", @@ -11536,6 +13239,11 @@ "json-buffer": "3.0.1" } }, + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -11552,6 +13260,16 @@ "type-check": "~0.4.0" } }, + "lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, "linkify-it": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", @@ -11600,7 +13318,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -11715,6 +13432,58 @@ } } }, + "mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + } + }, + "mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "requires": { + "@types/mdast": "^3.0.0" + } + }, "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", @@ -11740,14 +13509,223 @@ "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "requires": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==" + }, + "micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==" + }, + "micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==" + }, + "micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "requires": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -11962,6 +13940,11 @@ } } }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -11977,7 +13960,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "requires": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -12096,8 +14078,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "normalize-url": { "version": "6.1.0", @@ -12127,8 +14108,12 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" }, "object-inspect": { "version": "1.12.3", @@ -12310,6 +14295,11 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, "path-to-regexp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", @@ -12328,16 +14318,99 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==" + }, + "postcss": { + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "dependencies": { + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + } + } + }, + "postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + } + }, + "postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "requires": { + "postcss-selector-parser": "^6.0.11" + } + }, + "postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, "postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "peer": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "posthog-js": { + "version": "1.63.3", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.63.3.tgz", + "integrity": "sha512-Ob2x1ENxx/mWAdhLhc6ouKAph9LwmitBy7M+hiaFoS0T9g7OVL4lYdrJbODbYV+HX/HeO3bsnQwYO1QLPWm/YA==", + "requires": { + "fflate": "^0.4.1" + } }, "prebuild-install": { "version": "7.1.1", @@ -12391,6 +14464,28 @@ "retry": "^0.12.0" } }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "property-information": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz", + "integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==" + }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -12423,8 +14518,7 @@ "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, "quick-lru": { "version": "5.1.1", @@ -12484,6 +14578,28 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "requires": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + } + }, "react-redux": { "version": "8.0.5", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", @@ -12506,6 +14622,14 @@ "mute-stream": "~0.0.4" } }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -12533,7 +14657,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "requires": { "picomatch": "^2.2.1" } @@ -12569,6 +14692,27 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, + "remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -12579,6 +14723,16 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz", "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" }, + "resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "requires": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, "resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", @@ -12615,8 +14769,7 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, "rimraf": { "version": "3.0.2", @@ -12641,6 +14794,14 @@ } } }, + "rollup": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.1.tgz", + "integrity": "sha512-ybRdFVHOoljGEFILHLd2g/qateqUdjE6YS41WXq4p3C/WwD3xtWxV4FYWETA1u9TeXQc5K8L8zHE5d/scOvrOQ==", + "requires": { + "fsevents": "~2.3.2" + } + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -12651,7 +14812,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "requires": { "queue-microtask": "^1.2.2" } @@ -12673,6 +14833,14 @@ } } }, + "sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "requires": { + "mri": "^1.1.0" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -12809,6 +14977,16 @@ "socks": "^2.6.2" } }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" + }, "spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", @@ -12877,6 +15055,14 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "style-to-object": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.1.tgz", + "integrity": "sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==", + "requires": { + "inline-style-parser": "0.1.1" + } + }, "styled-components": { "version": "5.3.9", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.9.tgz", @@ -12912,6 +15098,40 @@ } } }, + "sucrase": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", + "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12920,6 +15140,41 @@ "has-flag": "^4.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "tailwindcss": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", + "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "requires": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + } + }, "tar": { "version": "6.1.13", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", @@ -13004,7 +15259,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "requires": { "any-promise": "^1.0.0" } @@ -13013,7 +15267,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "requires": { "thenify": ">= 3.1.0 < 4" } @@ -13053,7 +15306,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } @@ -13075,6 +15327,21 @@ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, + "trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" + }, + "trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" + }, + "ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -13164,6 +15431,27 @@ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, + "unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "requires": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" + } + } + }, "unique-filename": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", @@ -13180,6 +15468,54 @@ "imurmurhash": "^0.1.4" } }, + "unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==" + }, + "unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + } + }, + "unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + } + }, "universal-github-app-jwt": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz", @@ -13250,6 +15586,48 @@ "dev": true, "peer": true }, + "uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "requires": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + } + }, + "vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + } + }, + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + }, + "vite": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", + "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "requires": { + "esbuild": "^0.17.5", + "fsevents": "~2.3.2", + "postcss": "^8.4.23", + "rollup": "^3.21.0" + } + }, "vsce": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.15.0.tgz", @@ -13497,6 +15875,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==" + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/extension/package.json b/extension/package.json index dd15157d..a3b82c1b 100644 --- a/extension/package.json +++ b/extension/package.json @@ -182,6 +182,8 @@ "@types/mocha": "^10.0.1", "@types/node": "16.x", "@types/node-fetch": "^2.6.2", + "@types/react-dom": "^18.2.4", + "@types/styled-components": "^5.1.26", "@types/vscode": "^1.74.0", "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.45.0", @@ -200,11 +202,16 @@ "@reduxjs/toolkit": "^1.9.3", "@segment/analytics-node": "^0.0.1-beta.16", "@styled-icons/heroicons-outline": "^10.47.0", + "@vitejs/plugin-react-swc": "^3.3.2", "axios": "^1.2.5", "highlight.js": "^11.7.0", "octokit": "^2.0.11", + "posthog-js": "^1.63.3", + "react-markdown": "^8.0.7", "react-redux": "^8.0.5", "strip-ansi": "^7.0.1", + "tailwindcss": "^3.3.2", + "vite": "^4.3.9", "vscode-languageclient": "^8.0.2", "ws": "^8.13.0" } diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl index 043b0c06..c9e40ce3 100644 Binary files a/extension/scripts/continuedev-0.1.1-py3-none-any.whl and b/extension/scripts/continuedev-0.1.1-py3-none-any.whl differ diff --git a/extension/src/README.md b/extension/src/README.md index 76b96ea0..126a094a 100644 --- a/extension/src/README.md +++ b/extension/src/README.md @@ -2,47 +2,31 @@ ## How to get started with development -1. Clone the `continue` repo +1. Clone the Continue repo -2. Open a VS Code window with the `continue` repo +2. Open a VS Code window with the `continue` directory as your workspace -3. Package and then start the FastAPI server by following instructions outlined in `package/server/README.md` +3. Package and then start the FastAPI server by following instructions outlined in the `Continue Server` section of the `continuedev/README.md` -4. Open the `extension` sub-directory of the repo in a second VS Code window +4. Open a VS Code window with the `extension` directory as your workspace -5. Run `npm install` +5. From `continue/extension`, run `npm install` -6. Run `npm run clientgen` +6. Run `npm run full-package` -7. Run `cd react-app` +7. Open `src/activation/activate.ts` file (or any TypeScript file) -8. Run `npm run build` +8. Press `F5` on your keyboard to start `Run and Debug` mode -9. Run `cd ..` to return to `extension` directory +9. `cmd+shift+p` to look at developer console and select Continue commands -10. Then run `npm run compile` +10. Every time you make changes to the code, you need to run `npm run compile` -11. Open `src/activate.ts` file (or any TypeScript file) +11. If you run into a "command not found" error, try running `npm run rebuild` and then `npm run compile` -12. Press `F5` on your keyboard to start `Run and Debug` mode +## Alternative: Install from source -13. `cmd+shift+p` to look at developer console and select Continue commands - -14. Every time you make changes to the code, you need to run `npm run compile` - -15. If you run into a "command not found" error, try running `npm run rebuild` and then `npm run compile` - -## Alternative: Install a packaged version - -You should always have a packaged version installed in VS Code, because when Continue is broken you'll want a stable version to help you debug. There are four key commands in the `package.json`: - -1. `npm run package` will create a .vsix file in the `build/` folder that can then be installed. It is this same file that you can share with others who want to try the extension. - -2. `npm run install-extension` will install the extension to VS Code. You should then see it in your installed extensions in the VS Code sidebar. - -3. `npm run uninstall` will uninstall the extension. You don't always have to do this thanks to the reinstall command, but can be useful when you want to do so manually. - -4. `npm run reinstall` will go through the entire process of uninstalling the existing installed extension, rebuilding, and then installing the new version. You shouldn't be doing this every time you make a change to the extension, but rather when there is some significant update that you would like to make available to yourself (or if you happen to be debugging something which is specific to the packaged extension). +Update: directions to root README ## Background diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 170426e1..21f867b1 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -88,7 +88,7 @@ async function setupPythonEnv() { const createEnvCommand = [ `cd ${path.join(getExtensionUri().fsPath, "scripts")}`, `${pythonCmd} -m venv env`, - ].join(" && "); + ].join(" ; "); // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again) while (true) { -- cgit v1.2.3-70-g09d2 From db0c493850d73c9d05f96ce1c5a04fc318c77422 Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Mon, 5 Jun 2023 09:26:40 +0200 Subject: README changes --- README.md | 23 +++++------------------ extension/src/README.md | 2 +- 2 files changed, 6 insertions(+), 19 deletions(-) (limited to 'extension/src') diff --git a/README.md b/README.md index 92570126..6429c715 100644 --- a/README.md +++ b/README.md @@ -25,22 +25,9 @@ Learn how to try the GitHub Codespaces Demo [here](https://continue.dev/docs/get Learn how to install locally in VS Code [here](https://continue.dev/docs/install) -## Walkthroughs - -- [Use the GUI](https://continue.dev/docs/walkthroughs/use-the-gui.md) -- [Use a recipe](https://continue.dev/docs/walkthroughs/use-a-recipe.md) -- [Create a recipe](https://continue.dev/docs/walkthroughs/create-a-recipe.md) -- [Share a recipe](https://continue.dev/docs/walkthroughs/share-a-recipe.md) - ## How to contribute -### Option 1: Create a recipe and share it with the community - -Follow [these steps](https://continue.dev/docs/walkthroughs/share-a-recipe.md) to share a recipe you have created :) - -### Option 2: Open a [new GitHub Issue](https://github.com/continuedev/continue/issues/new) or comment on [an existing one](https://github.com/continuedev/continue/issues) - -Let us know what you would like to contribute and we will help you make it happen! +Open a [new GitHub Issue](https://github.com/continuedev/continue/issues/new) or comment on [an existing one](https://github.com/continuedev/continue/issues). Let us know what you would like to contribute there, and we will help you make it happen! ## Install from source @@ -68,13 +55,13 @@ cd continue/extension/scripts && python3 install_from_source.py # Understanding the codebase -- [Continue Server README](./continuedev): learn about the core of Continue, which can be downloaded as a [PyPI package](https://pypi.org/project/continuedev/) -- [VS Code Extension README](./extension/src): learn about the capabilities of our extension—the first implementation of Continue's IDE Protocol—which makes it possible to use use Continue in VS Code and GitHub Codespaces +- [Continue Server README](./continuedev/README.md): learn about the core of Continue, which can be downloaded as a [PyPI package](https://pypi.org/project/continuedev/) +- [VS Code Extension README](./extension/README.md): learn about the capabilities of our extension—the first implementation of Continue's IDE Protocol—which makes it possible to use use Continue in VS Code and GitHub Codespaces - [Continue GUI README](./extension/react-app/): learn about the React app that lets users interact with the server and is placed adjacent to the text editor in any suppported IDE - [Schema README](./schema): learn about the JSON Schema types generated from Pydantic models, which we use across the `continuedev/` and `extension/` directories - [Continue Docs README](./docs): learn how our [docs](https://continue.dev/docs) are written and built -- Add steps for development README mention here +- [How to debug the VS Code Extension README](./extension/src/README.md): learn how to set up the VS Code extension, so you can debug it # License -[Apache 2.0 © 2023 Continue Dev, Inc.](./LICENSE) +[Apache 2.0 © 2023 Continue Dev, Inc.](./LICENSE) \ No newline at end of file diff --git a/extension/src/README.md b/extension/src/README.md index 126a094a..8523ec86 100644 --- a/extension/src/README.md +++ b/extension/src/README.md @@ -1,6 +1,6 @@ # Continue VS Code Extension README -## How to get started with development +## How to debug the VS Code Extension 1. Clone the Continue repo -- cgit v1.2.3-70-g09d2 From 5f3d975cb3bef13769cb19ff3f604fe42cc9ee5d Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Mon, 5 Jun 2023 22:54:08 +0200 Subject: fixing windows install issue --- extension/scripts/continuedev-0.1.1-py3-none-any.whl | Bin 0 -> 59915 bytes extension/src/activation/environmentSetup.ts | 10 +++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 extension/scripts/continuedev-0.1.1-py3-none-any.whl (limited to 'extension/src') diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl new file mode 100644 index 00000000..e9d03c6e Binary files /dev/null and b/extension/scripts/continuedev-0.1.1-py3-none-any.whl differ diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 21f867b1..2410dcca 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -14,7 +14,7 @@ async function runCommand(cmd: string): Promise<[string, string | undefined]> { var stdout: any = ""; var stderr: any = ""; try { - var { stdout, stderr } = await exec(cmd); + var { stdout, stderr } = await exec(cmd, {'shell':'powershell.exe'}); } catch (e: any) { stderr = e.stderr; stdout = e.stdout; @@ -70,7 +70,7 @@ function checkEnvExists() { ); return ( fs.existsSync(path.join(envBinPath, "activate")) && - fs.existsSync(path.join(envBinPath, "pip")) + fs.existsSync(path.join(envBinPath, process.platform == "win32" ? "pip.exe" : "pip")) ); } @@ -88,7 +88,11 @@ async function setupPythonEnv() { const createEnvCommand = [ `cd ${path.join(getExtensionUri().fsPath, "scripts")}`, `${pythonCmd} -m venv env`, - ].join(" ; "); + ].join("; "); + + const [here, something] = await runCommand(`cd ${path.join(getExtensionUri().fsPath, "scripts")}`); + const [here1, something1] = await runCommand('cd c:\\Users\\Ty\\Documents\\continuedev\\continue\\extension\\scripts; python -m venv env'); + // console.log('cd c:\\Users\\Ty\\Documents\\continuedev\\continue\\extension\\scripts; c:\\Program` Files\\Python310\\python.exe -m venv env'); // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again) while (true) { -- cgit v1.2.3-70-g09d2 From 60eaf08df63b77ce31ce8afaa77fdd6b357c8a8a Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 6 Jun 2023 00:21:15 -0400 Subject: many design improvements --- continuedev/src/continuedev/core/autopilot.py | 34 +++++++--- continuedev/src/continuedev/core/main.py | 30 ++++++--- continuedev/src/continuedev/core/policy.py | 7 +- continuedev/src/continuedev/core/sdk.py | 21 ++++-- .../recipes/CreatePipelineRecipe/main.py | 2 +- .../recipes/CreatePipelineRecipe/steps.py | 62 +++++++++++++----- continuedev/src/continuedev/server/ide.py | 11 ++++ continuedev/src/continuedev/server/ide_protocol.py | 8 +++ continuedev/src/continuedev/steps/core/core.py | 27 ++++---- extension/package.json | 4 +- extension/react-app/src/components/DebugPanel.tsx | 15 ++--- .../react-app/src/components/StepContainer.tsx | 52 ++++++++------- extension/react-app/src/components/index.ts | 11 +++- extension/react-app/src/tabs/chat/MessageDiv.tsx | 1 - extension/react-app/src/tabs/gui.tsx | 74 +++++++++++++++++----- extension/src/continueIdeClient.ts | 39 +++++++++++- 16 files changed, 286 insertions(+), 112 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index db06c975..d55e521b 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -1,13 +1,13 @@ import traceback import time -from typing import Callable, Coroutine, List +from typing import Any, Callable, Coroutine, Dict, List from ..models.filesystem_edit import FileEditWithFullContents from ..libs.llm import LLM from .observation import Observation, InternalErrorObservation from ..server.ide_protocol import AbstractIdeProtocolServer from ..libs.util.queue import AsyncSubscriptionQueue from ..models.main import ContinueBaseModel -from .main import Policy, History, FullState, Step, HistoryNode +from .main import Context, ContinueCustomException, Policy, History, FullState, Step, HistoryNode from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep from ..libs.util.telemetry import capture_event from .sdk import ContinueSDK @@ -18,6 +18,7 @@ class Autopilot(ContinueBaseModel): policy: Policy ide: AbstractIdeProtocolServer history: History = History.from_empty() + context: Context = Context() _on_update_callbacks: List[Callable[[FullState], None]] = [] _active: bool = False @@ -26,6 +27,10 @@ class Autopilot(ContinueBaseModel): _user_input_queue = AsyncSubscriptionQueue() + @property + def continue_sdk(self) -> ContinueSDK: + return ContinueSDK(self) + class Config: arbitrary_types_allowed = True @@ -60,7 +65,7 @@ class Autopilot(ContinueBaseModel): current_step = self.history.get_current().step self.history.step_back() if issubclass(current_step.__class__, ReversibleStep): - await current_step.reverse(ContinueSDK(self)) + await current_step.reverse(self.continue_sdk) await self.update_subscribers() except Exception as e: @@ -105,7 +110,16 @@ class Autopilot(ContinueBaseModel): self._step_depth += 1 try: - observation = await step(ContinueSDK(self)) + observation = await step(self.continue_sdk) + except ContinueCustomException as e: + # Attach an InternalErrorObservation to the step and unhide it. + error_string = e.message + print( + f"\n{error_string}\n{e}") + + observation = InternalErrorObservation( + error=error_string) + step.hide = False except Exception as e: # Attach an InternalErrorObservation to the step and unhide it. error_string = '\n\n'.join( @@ -125,11 +139,13 @@ class Autopilot(ContinueBaseModel): await self.update_subscribers() # Update its description - async def update_description(): - step._set_description(await step.describe(ContinueSDK(self).models)) - # Update subscribers with new description - await self.update_subscribers() - asyncio.create_task(update_description()) + if step.description is None: + async def update_description(): + step.description = await step.describe(self.continue_sdk.models) + # Update subscribers with new description + await self.update_subscribers() + + asyncio.create_task(update_description()) return observation diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index b2b97bae..17b30e96 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -105,7 +105,7 @@ class Policy(ContinueBaseModel): class Step(ContinueBaseModel): name: str = None hide: bool = False - _description: Union[str, None] = None + description: Union[str, None] = None system_message: Union[str, None] = None @@ -113,17 +113,14 @@ class Step(ContinueBaseModel): copy_on_model_validation = False async def describe(self, models: Models) -> Coroutine[str, None, None]: - if self._description is not None: - return self._description + if self.description is not None: + return self.description return "Running step: " + self.name - def _set_description(self, description: str): - self._description = description - def dict(self, *args, **kwargs): d = super().dict(*args, **kwargs) - if self._description is not None: - d["description"] = self._description + if self.description is not None: + d["description"] = self.description else: d["description"] = "`Description loading...`" return d @@ -173,4 +170,21 @@ class Validator(Step): raise NotImplementedError +class Context: + key_value: Dict[str, str] = {} + + def set(self, key: str, value: str): + self.key_value[key] = value + + def get(self, key: str) -> str: + return self.key_value[key] + + +class ContinueCustomException(Exception): + message: str + + def __init__(self, message: str): + self.message = message + + HistoryNode.update_forward_refs() diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index e65b6c9d..4934497d 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -17,9 +17,10 @@ class DemoPolicy(Policy): def next(self, history: History) -> Step: # At the very start, run initial Steps spcecified in the config if history.get_current() is None: - return (MessageStep(message="Welcome to Continue!") >> - SetupContinueWorkspaceStep() >> - CreateCodebaseIndexChroma() >> + return ( + MessageStep(name="Welcome to Continue!", message="") >> + # SetupContinueWorkspaceStep() >> + # CreateCodebaseIndexChroma() >> StepsOnStartupStep()) observation = history.get_current().observation diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 8317a3d1..690949f1 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -11,7 +11,7 @@ from ..libs.llm.hf_inference_api import HuggingFaceInferenceAPI from ..libs.llm.openai import OpenAI from .observation import Observation from ..server.ide_protocol import AbstractIdeProtocolServer -from .main import History, Step +from .main import Context, ContinueCustomException, History, Step from ..steps.core.core import * @@ -44,6 +44,7 @@ class ContinueSDK(AbstractContinueSDK): ide: AbstractIdeProtocolServer steps: ContinueSDKSteps models: Models + context: Context __autopilot: Autopilot def __init__(self, autopilot: Autopilot): @@ -51,6 +52,7 @@ class ContinueSDK(AbstractContinueSDK): self.__autopilot = autopilot self.steps = ContinueSDKSteps(self) self.models = Models(self) + self.context = autopilot.context @property def history(self) -> History: @@ -64,8 +66,8 @@ class ContinueSDK(AbstractContinueSDK): async def run_step(self, step: Step) -> Coroutine[Observation, None, None]: return await self.__autopilot._run_singular_step(step) - async def apply_filesystem_edit(self, edit: FileSystemEdit): - return await self.run_step(FileSystemEditStep(edit=edit)) + async def apply_filesystem_edit(self, edit: FileSystemEdit, name: str = None, description: str = None): + return await self.run_step(FileSystemEditStep(edit=edit, description=description, **({'name': name} if name else {}))) async def wait_for_user_input(self) -> str: return await self.__autopilot.wait_for_user_input() @@ -73,18 +75,20 @@ class ContinueSDK(AbstractContinueSDK): async def wait_for_user_confirmation(self, prompt: str): return await self.run_step(WaitForUserConfirmationStep(prompt=prompt)) - async def run(self, commands: Union[List[str], str], cwd: str = None): + async def run(self, commands: Union[List[str], str], cwd: str = None, name: str = None, description: str = None): commands = commands if isinstance(commands, List) else [commands] - return await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd)) + return await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd, description=description, **({'name': name} if name else {}))) - async def edit_file(self, filename: str, prompt: str): + async def edit_file(self, filename: str, prompt: str, name: str = None, description: str = None): filepath = await self._ensure_absolute_path(filename) await self.ide.setFileOpen(filepath) contents = await self.ide.readFile(filepath) await self.run_step(EditCodeStep( range_in_files=[RangeInFile.from_entire_file(filepath, contents)], - prompt=f'Here is the code before:\n\n{{code}}\n\nHere is the user request:\n\n{prompt}\n\nHere is the code edited to perfectly solve the user request:\n\n' + prompt=f'Here is the code before:\n\n{{code}}\n\nHere is the user request:\n\n{prompt}\n\nHere is the code edited to perfectly solve the user request:\n\n', + description=description, + **({'name': name} if name else {}) )) async def append_to_file(self, filename: str, content: str): @@ -126,3 +130,6 @@ class ContinueSDK(AbstractContinueSDK): def set_loading_message(self, message: str): # self.__autopilot.set_loading_message(message) raise NotImplementedError() + + def raise_exception(self, message: str): + raise ContinueCustomException(message) diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py index 55c25da4..1206db0e 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py @@ -12,7 +12,7 @@ class CreatePipelineRecipe(Step): async def run(self, sdk: ContinueSDK): text_observation = await sdk.run_step( - MessageStep(message=dedent("""\ + MessageStep(name="Building your first dlt pipeline", message=dedent("""\ This recipe will walk you through the process of creating a dlt pipeline for your chosen data source. With the help of Continue, you will: - Create a Python virtual environment with dlt installed - Run `dlt init` to generate a pipeline template diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index ef5e3b43..3c8277c0 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -1,8 +1,12 @@ +import os +import subprocess from textwrap import dedent +from ...models.main import Range +from ...models.filesystem import RangeInFile from ...steps.main import MessageStep from ...core.sdk import Models -from ...core.observation import DictObservation +from ...core.observation import DictObservation, InternalErrorObservation from ...models.filesystem_edit import AddFile from ...core.main import Step from ...core.sdk import ContinueSDK @@ -33,45 +37,69 @@ class SetupPipelineStep(Step): f'dlt init {source_name} duckdb', 'Y', 'pip install -r requirements.txt' - ]) - + ], description=dedent(f"""\ + Running the following commands: + - `python3 -m venv env`: Create a Python virtual environment + - `source env/bin/activate`: Activate the virtual environment + - `pip install dlt`: Install dlt + - `dlt init {source_name} duckdb`: Create a new dlt pipeline called {source_name} that loads data into a local DuckDB instance + - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline""")) + + await sdk.wait_for_user_confirmation("Wait for the commands to finish running, then press `Continue`") # editing the resource function to call the requested API + await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 30, 0)), "#00ff0022") + await sdk.edit_file( filename=filename, - prompt=f'Edit the resource function to call the API described by this: {self.api_description}' + prompt=f'Edit the resource function to call the API described by this: {self.api_description}', + name="Edit the resource function to call the API" ) # wait for user to put API key in secrets.toml await sdk.ide.setFileOpen(await sdk.ide.getWorkspaceDirectory() + "/.dlt/secrets.toml") await sdk.wait_for_user_confirmation("If this service requires an API key, please add it to the `secrets.toml` file and then press `Continue`") - return DictObservation(values={"source_name": source_name}) + + sdk.context.set("source_name", source_name) class ValidatePipelineStep(Step): hide: bool = True async def run(self, sdk: ContinueSDK): - source_name = sdk.history.last_observation().values["source_name"] + workspace_dir = await sdk.ide.getWorkspaceDirectory() + source_name = sdk.context.get("source_name") filename = f'{source_name}.py' - await sdk.run_step(MessageStep(message=dedent("""\ - This step will validate that your dlt pipeline is working as expected: - - Test that the API call works - - Load the data into a local DuckDB instance - - Write a query to view the data - """))) + # await sdk.run_step(MessageStep(name="Validate the pipeline", message=dedent("""\ + # Next, we will validate that your dlt pipeline is working as expected: + # - Test that the API call works + # - Load the data into a local DuckDB instance + # - Write a query to view the data + # """))) # test that the API call works - await sdk.run(f'python3 {filename}') + + p = subprocess.run( + ['python3', f'{filename}'], capture_output=True, text=True, cwd=workspace_dir) + err = p.stderr + + # If it fails, return the error + if err is not None and err != "": + sdk.raise_exception( + f"Error while running pipeline. Fix the resource function in {filename} and rerun this step: \n\n" + err) + + await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API") # remove exit() from the main main function await sdk.edit_file( filename=filename, - prompt='Remove exit() from the main function' + prompt='Remove exit() from the main function', + name="Remove early exit() from main function", + description="Remove the `exit()` call from the main function in the pipeline file so that the data is loaded into DuckDB" ) # load the data into the DuckDB instance - await sdk.run(f'python3 {filename}') + await sdk.run(f'python3 {filename}', name="Load data into DuckDB", description=f"Running python3 {filename} to load data into DuckDB") table_name = f"{source_name}.{source_name}_resource" tables_query_code = dedent(f'''\ @@ -89,5 +117,5 @@ class ValidatePipelineStep(Step): ''') query_filename = (await sdk.ide.getWorkspaceDirectory()) + "/query.py" - await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code)) - await sdk.run('env/bin/python3 query.py') + await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code), name="Add query.py file", description="Adding a file called `query.py` to the workspace that will run a test query on the DuckDB instance") + await sdk.run('env/bin/python3 query.py', name="Run test query", description="Running `env/bin/python3 query.py` to test that the data was loaded into DuckDB as expected") diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index eec5b607..073e1dba 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -133,6 +133,17 @@ class IdeProtocolServer(AbstractIdeProtocolServer): "sessionId": session_id }) + async def highlightCode(self, range_in_file: RangeInFile, color: str): + await self._send_json("highlightCode", { + "rangeInFile": range_in_file.dict(), + "color": color + }) + + async def runCommand(self, command: str): + await self._send_json("runCommand", { + "command": command + }) + async def showSuggestionsAndWait(self, suggestions: List[FileEdit]) -> bool: ids = [str(uuid.uuid4()) for _ in suggestions] for i in range(len(suggestions)): diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 8f155415..f42de68f 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -82,3 +82,11 @@ class AbstractIdeProtocolServer(ABC): @abstractmethod async def getUserSecret(self, key: str): """Get a user secret""" + + @abstractmethod + async def highlightCode(self, range_in_file: RangeInFile, color: str): + """Highlight code""" + + @abstractmethod + async def runCommand(self, command: str): + """Run a command""" diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index ad468595..99786b00 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -55,18 +55,21 @@ class ShellCommandsStep(Step): async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: cwd = await sdk.ide.getWorkspaceDirectory() if self.cwd is None else self.cwd - process = subprocess.Popen( - '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd) + for cmd in self.cmds: + await sdk.ide.runCommand(cmd) - stdin_input = "\n".join(self.cmds) - out, err = process.communicate(stdin_input.encode()) + # process = subprocess.Popen( + # '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd) - # If it fails, return the error - if err is not None and err != "": - self._err_text = err - return TextObservation(text=err) + # stdin_input = "\n".join(self.cmds) + # out, err = process.communicate(stdin_input.encode()) - return None + # # If it fails, return the error + # if err is not None and err != "": + # self._err_text = err + # return TextObservation(text=err) + + # return None class EditCodeStep(Step): @@ -197,10 +200,10 @@ class WaitForUserInputStep(Step): if self._response is None: return self.prompt else: - return self.prompt + "\n\n" + self._response + return f"{self.prompt}\n\n`{self._response}`" async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - self._description = self.prompt + self.description = self.prompt resp = await sdk.wait_for_user_input() self._response = resp return TextObservation(text=resp) @@ -214,7 +217,7 @@ class WaitForUserConfirmationStep(Step): return self.prompt async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - self._description = self.prompt + self.description = self.prompt resp = await sdk.wait_for_user_input() self.hide = True return TextObservation(text=resp) diff --git a/extension/package.json b/extension/package.json index 7bd48f98..c834f402 100644 --- a/extension/package.json +++ b/extension/package.json @@ -42,12 +42,12 @@ "description": "The URL of the Continue server to use." }, "continue.OPENAI_API_KEY": { - "type": "string", + "type": "password", "default": "", "description": "The OpenAI API key to use for code generation." }, "continue.HUGGING_FACE_TOKEN": { - "type": "string", + "type": "password", "default": "", "description": "The Hugging Face API token to use for code generation." } diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx index 9dacc624..11ec2fe2 100644 --- a/extension/react-app/src/components/DebugPanel.tsx +++ b/extension/react-app/src/components/DebugPanel.tsx @@ -9,7 +9,7 @@ import { } from "../redux/slices/configSlice"; import { setHighlightedCode } from "../redux/slices/miscSlice"; import { updateFileSystem } from "../redux/slices/debugContexSlice"; -import { buttonColor, defaultBorderRadius, vscBackground } from "."; +import { defaultBorderRadius, secondaryDark, vscBackground } from "."; interface DebugPanelProps { tabs: { element: React.ReactElement; @@ -19,14 +19,15 @@ interface DebugPanelProps { const GradientContainer = styled.div` // Uncomment to get gradient border - background: linear-gradient( + /* background: linear-gradient( 101.79deg, #12887a 0%, #87245c 37.64%, #e12637 65.98%, #ffb215 110.45% - ); + ); */ /* padding: 10px; */ + background-color: ${secondaryDark}; margin: 0; height: 100%; /* border: 1px solid white; */ @@ -36,11 +37,8 @@ const GradientContainer = styled.div` const MainDiv = styled.div` height: 100%; border-radius: ${defaultBorderRadius}; - overflow-y: scroll; - scrollbar-gutter: stable both-edges; scrollbar-base-color: transparent; - /* background: ${vscBackground}; */ - background-color: #1e1e1ede; + background-color: ${vscBackground}; `; const TabBar = styled.div<{ numTabs: number }>` @@ -105,9 +103,6 @@ function DebugPanel(props: DebugPanelProps) { { if (e.key === "Enter") { diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index a5a1c5dc..25287d32 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -96,6 +96,12 @@ class IdeProtocolClient { fileEdit, }); break; + case "highlightCode": + this.highlightCode(data.rangeInFile, data.color); + break; + case "runCommand": + this.runCommand(data.command); + break; case "saveFile": this.saveFile(data.filepath); break; @@ -117,6 +123,28 @@ class IdeProtocolClient { // ------------------------------------ // // On message handlers + async highlightCode(rangeInFile: RangeInFile, color: string) { + const range = new vscode.Range( + rangeInFile.range.start.line, + rangeInFile.range.start.character, + rangeInFile.range.end.line, + rangeInFile.range.end.character + ); + const editor = await openEditorAndRevealRange( + rangeInFile.filepath, + range, + vscode.ViewColumn.One + ); + if (editor) { + editor.setDecorations( + vscode.window.createTextEditorDecorationType({ + backgroundColor: color, + }), + [range] + ); + } + } + showSuggestion(edit: FileEdit) { // showSuggestion already exists showSuggestion( @@ -289,7 +317,16 @@ class IdeProtocolClient { } runCommand(command: string) { - vscode.window.terminals[0].sendText(command, true); + if (vscode.window.terminals.length === 0) { + const terminal = vscode.window.createTerminal(); + terminal.show(); + terminal.sendText("bash", true); + terminal.sendText(command, true); + return; + } + const terminal = vscode.window.terminals[0]; + terminal.show(); + terminal.sendText(command, true); // But need to know when it's done executing... } } -- cgit v1.2.3-70-g09d2 From 9f33cac01eef7cbe15dafb4bd51666195f120d69 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 6 Jun 2023 00:23:56 -0400 Subject: cleaning up with win32 check for powershell --- extension/src/activation/environmentSetup.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'extension/src') diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 2410dcca..bc071461 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -5,7 +5,6 @@ const { spawn } = require("child_process"); import * as path from "path"; import * as fs from "fs"; import rebuild from "@electron/rebuild"; -import * as vscode from "vscode"; import { getContinueServerUrl } from "../bridge"; import fetch from "node-fetch"; @@ -14,7 +13,9 @@ async function runCommand(cmd: string): Promise<[string, string | undefined]> { var stdout: any = ""; var stderr: any = ""; try { - var { stdout, stderr } = await exec(cmd, {'shell':'powershell.exe'}); + var { stdout, stderr } = await exec(cmd, { + shell: process.platform === "win32" ? "powershell.exe" : undefined, + }); } catch (e: any) { stderr = e.stderr; stdout = e.stdout; @@ -70,7 +71,9 @@ function checkEnvExists() { ); return ( fs.existsSync(path.join(envBinPath, "activate")) && - fs.existsSync(path.join(envBinPath, process.platform == "win32" ? "pip.exe" : "pip")) + fs.existsSync( + path.join(envBinPath, process.platform == "win32" ? "pip.exe" : "pip") + ) ); } @@ -90,10 +93,6 @@ async function setupPythonEnv() { `${pythonCmd} -m venv env`, ].join("; "); - const [here, something] = await runCommand(`cd ${path.join(getExtensionUri().fsPath, "scripts")}`); - const [here1, something1] = await runCommand('cd c:\\Users\\Ty\\Documents\\continuedev\\continue\\extension\\scripts; python -m venv env'); - // console.log('cd c:\\Users\\Ty\\Documents\\continuedev\\continue\\extension\\scripts; c:\\Program` Files\\Python310\\python.exe -m venv env'); - // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again) while (true) { const [, stderr] = await runCommand(createEnvCommand); -- cgit v1.2.3-70-g09d2 From 881718c4c7f58837a8a208930e7d2c69b9433fd7 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 6 Jun 2023 17:50:50 -0400 Subject: trying to reliably capture terminal output in vsc --- continuedev/src/continuedev/core/autopilot.py | 4 +- continuedev/src/continuedev/core/main.py | 4 +- continuedev/src/continuedev/core/observation.py | 1 + continuedev/src/continuedev/core/sdk.py | 8 +- .../recipes/CreatePipelineRecipe/main.py | 2 +- .../recipes/CreatePipelineRecipe/steps.py | 17 +- continuedev/src/continuedev/server/ide.py | 12 +- continuedev/src/continuedev/server/ide_protocol.py | 2 +- continuedev/src/continuedev/steps/core/core.py | 6 +- extension/esbuild.mjs | 14 + extension/package-lock.json | 1132 +------------------- extension/package.json | 5 +- .../react-app/src/components/StepContainer.tsx | 12 +- .../react-app/src/components/ToggleErrorDiv.tsx | 41 + extension/react-app/src/tabs/gui.tsx | 4 + extension/src/continueIdeClient.ts | 24 +- extension/src/terminal/terminalEmulator.ts | 301 +++--- 17 files changed, 277 insertions(+), 1312 deletions(-) create mode 100644 extension/esbuild.mjs create mode 100644 extension/react-app/src/components/ToggleErrorDiv.tsx (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index d55e521b..b227570e 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -118,7 +118,7 @@ class Autopilot(ContinueBaseModel): f"\n{error_string}\n{e}") observation = InternalErrorObservation( - error=error_string) + error=error_string, title=e.title) step.hide = False except Exception as e: # Attach an InternalErrorObservation to the step and unhide it. @@ -128,7 +128,7 @@ class Autopilot(ContinueBaseModel): f"Error while running step: \n{error_string}\n{e}") observation = InternalErrorObservation( - error=error_string) + error=error_string, title=e.__repr__()) step.hide = False self._step_depth -= 1 diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 17b30e96..33e25c93 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -181,10 +181,12 @@ class Context: class ContinueCustomException(Exception): + title: str message: str - def __init__(self, message: str): + def __init__(self, message: str, title: str = "Error while running step:"): self.message = message + self.title = title HistoryNode.update_forward_refs() diff --git a/continuedev/src/continuedev/core/observation.py b/continuedev/src/continuedev/core/observation.py index b6117236..126cf19e 100644 --- a/continuedev/src/continuedev/core/observation.py +++ b/continuedev/src/continuedev/core/observation.py @@ -36,4 +36,5 @@ class TextObservation(Observation): class InternalErrorObservation(Observation): + title: str error: str diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 690949f1..f4aa2b35 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -75,9 +75,9 @@ class ContinueSDK(AbstractContinueSDK): async def wait_for_user_confirmation(self, prompt: str): return await self.run_step(WaitForUserConfirmationStep(prompt=prompt)) - async def run(self, commands: Union[List[str], str], cwd: str = None, name: str = None, description: str = None): + async def run(self, commands: Union[List[str], str], cwd: str = None, name: str = None, description: str = None) -> Coroutine[str, None, None]: commands = commands if isinstance(commands, List) else [commands] - return await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd, description=description, **({'name': name} if name else {}))) + return (await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd, description=description, **({'name': name} if name else {})))).text async def edit_file(self, filename: str, prompt: str, name: str = None, description: str = None): filepath = await self._ensure_absolute_path(filename) @@ -131,5 +131,5 @@ class ContinueSDK(AbstractContinueSDK): # self.__autopilot.set_loading_message(message) raise NotImplementedError() - def raise_exception(self, message: str): - raise ContinueCustomException(message) + def raise_exception(self, message: str, title: str): + raise ContinueCustomException(message, title) diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py index 1206db0e..428ac9cc 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py @@ -22,7 +22,7 @@ class CreatePipelineRecipe(Step): - Load the data into a local DuckDB instance - Write a query to view the data""")) >> WaitForUserInputStep( - prompt="What API do you want to load data from?") + prompt="What API do you want to load data from? (e.g. weatherapi.com, chess.com)") ) await sdk.run_step( SetupPipelineStep(api_description=text_observation.text) >> diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index 3c8277c0..9bee4c95 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -34,8 +34,7 @@ class SetupPipelineStep(Step): 'python3 -m venv env', 'source env/bin/activate', 'pip install dlt', - f'dlt init {source_name} duckdb', - 'Y', + f'dlt init {source_name} duckdb\n\rY', 'pip install -r requirements.txt' ], description=dedent(f"""\ Running the following commands: @@ -43,9 +42,8 @@ class SetupPipelineStep(Step): - `source env/bin/activate`: Activate the virtual environment - `pip install dlt`: Install dlt - `dlt init {source_name} duckdb`: Create a new dlt pipeline called {source_name} that loads data into a local DuckDB instance - - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline""")) + - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline"""), name="Setup Python environment") - await sdk.wait_for_user_confirmation("Wait for the commands to finish running, then press `Continue`") # editing the resource function to call the requested API await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 30, 0)), "#00ff0022") @@ -78,17 +76,12 @@ class ValidatePipelineStep(Step): # """))) # test that the API call works - - p = subprocess.run( - ['python3', f'{filename}'], capture_output=True, text=True, cwd=workspace_dir) - err = p.stderr + output = await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API") # If it fails, return the error - if err is not None and err != "": + if "Traceback" in output: sdk.raise_exception( - f"Error while running pipeline. Fix the resource function in {filename} and rerun this step: \n\n" + err) - - await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API") + title="Error while running pipeline.\nFix the resource function in {filename} and rerun this step", description=output) # remove exit() from the main main function await sdk.edit_file( diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 073e1dba..007eb2b4 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -76,6 +76,10 @@ class GetUserSecretResponse(BaseModel): value: str +class RunCommandResponse(BaseModel): + output: str + + T = TypeVar("T", bound=BaseModel) @@ -110,7 +114,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): fileEdits = list( map(lambda d: FileEditWithFullContents.parse_obj(d), data["fileEdits"])) self.onFileEdits(fileEdits) - elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret"]: + elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret", "runCommand"]: self.sub_queue.post(message_type, data) else: raise ValueError("Unknown message type", message_type) @@ -139,10 +143,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer): "color": color }) - async def runCommand(self, command: str): - await self._send_json("runCommand", { - "command": command - }) + async def runCommand(self, command: str) -> str: + return (await self._send_and_receive_json({"command": command}, RunCommandResponse, "runCommand")).output async def showSuggestionsAndWait(self, suggestions: List[FileEdit]) -> bool: ids = [str(uuid.uuid4()) for _ in suggestions] diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index f42de68f..4622d6ff 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -88,5 +88,5 @@ class AbstractIdeProtocolServer(ABC): """Highlight code""" @abstractmethod - async def runCommand(self, command: str): + async def runCommand(self, command: str) -> str: """Run a command""" diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 99786b00..c6dc7c04 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -56,7 +56,9 @@ class ShellCommandsStep(Step): cwd = await sdk.ide.getWorkspaceDirectory() if self.cwd is None else self.cwd for cmd in self.cmds: - await sdk.ide.runCommand(cmd) + output = await sdk.ide.runCommand(cmd) + + return TextObservation(text=output) # process = subprocess.Popen( # '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd) @@ -205,7 +207,7 @@ class WaitForUserInputStep(Step): async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: self.description = self.prompt resp = await sdk.wait_for_user_input() - self._response = resp + self.description = f"{self.prompt}\n\n`{resp}`" return TextObservation(text=resp) diff --git a/extension/esbuild.mjs b/extension/esbuild.mjs new file mode 100644 index 00000000..bc1b3e5f --- /dev/null +++ b/extension/esbuild.mjs @@ -0,0 +1,14 @@ +import * as esbuild from "esbuild"; +// esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node +await esbuild.build({ + entryPoints: ["src/extension.ts"], + bundle: true, + outfile: "out/extension.js", + external: ["vscode"], + format: "cjs", + platform: "node", + sourcemap: true, + loader: { + ".node": "file", + }, +}); diff --git a/extension/package-lock.json b/extension/package-lock.json index fa09527e..a20be756 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -16,11 +16,10 @@ "@vitejs/plugin-react-swc": "^3.3.2", "axios": "^1.2.5", "highlight.js": "^11.7.0", - "octokit": "^2.0.11", "posthog-js": "^1.63.3", "react-markdown": "^8.0.7", "react-redux": "^8.0.5", - "strip-ansi": "^7.0.1", + "strip-ansi": "^7.1.0", "tailwindcss": "^3.3.2", "vite": "^4.3.9", "vscode-languageclient": "^8.0.2", @@ -1120,457 +1119,6 @@ "npm": ">=5.0.0" } }, - "node_modules/@octokit/app": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/@octokit/app/-/app-13.1.2.tgz", - "integrity": "sha512-Kf+h5sa1SOI33hFsuHvTsWj1jUrjp1x4MuiJBq7U/NicfEGa6nArPUoDnyfP/YTmcQ5cQ5yvOgoIBkbwPg6kzQ==", - "dependencies": { - "@octokit/auth-app": "^4.0.8", - "@octokit/auth-unauthenticated": "^3.0.0", - "@octokit/core": "^4.0.0", - "@octokit/oauth-app": "^4.0.7", - "@octokit/plugin-paginate-rest": "^6.0.0", - "@octokit/types": "^9.0.0", - "@octokit/webhooks": "^10.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/app/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/app/node_modules/@octokit/plugin-paginate-rest": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz", - "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==", - "dependencies": { - "@octokit/types": "^9.0.0" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=4" - } - }, - "node_modules/@octokit/app/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/auth-app": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-4.0.8.tgz", - "integrity": "sha512-miI7y9FfS/fL1bSPsDaAfCGSxQ04iGLyisI2GA8N7P6eB6AkCOt+F1XXapJKRnAubQubvYF0dqxoTZYyKk93NQ==", - "dependencies": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", - "@types/lru-cache": "^5.1.0", - "deprecation": "^2.3.1", - "lru-cache": "^6.0.0", - "universal-github-app-jwt": "^1.1.1", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-app": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.5.tgz", - "integrity": "sha512-UPX1su6XpseaeLVCi78s9droxpGtBWIgz9XhXAx9VXabksoF0MyI5vaa1zo1njyYt6VaAjFisC2A2Wchcu2WmQ==", - "dependencies": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "@types/btoa-lite": "^1.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/auth-oauth-device": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.3.tgz", - "integrity": "sha512-KPTx5nMntKjNZzzltO3X4T68v22rd7Cp/TcLJXQE2U8aXPcZ9LFuww9q9Q5WUNSu3jwi3lRwzfkPguRfz1R8Vg==", - "dependencies": { - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^8.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-user": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.0.tgz", - "integrity": "sha512-TC2Mj8NkSy9uAnZLYX+FKB/IH6uDe+qYNzHzH8l13JlzsrTE7GKkcqtXdSGGN4tncyROAB4/KS5rDPRCEnWHlA==", - "dependencies": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^8.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-token": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz", - "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==", - "dependencies": { - "@octokit/types": "^9.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-token/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/auth-token/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/auth-unauthenticated": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.4.tgz", - "integrity": "sha512-AT74XGBylcLr4lmUp1s6mjSUgphGdlse21Qjtv5DzpX1YOl5FXKwvNcZWESdhyBbpDT8VkVyLFqa/7a7eqpPNw==", - "dependencies": { - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/core": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz", - "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==", - "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/core/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/endpoint": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.4.tgz", - "integrity": "sha512-hXJP43VT2IrUxBCNIahta8qawpIzLvCjHLCuDDsdIPbd6+jPwsc3KGl/kdQ37mLd+sdiJm6c9qKI7k5CjE0Z9A==", - "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/endpoint/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/graphql": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz", - "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==", - "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/graphql/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/oauth-app": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-4.2.0.tgz", - "integrity": "sha512-gyGclT77RQMkVUEW3YBeAKY+LBSc5u3eC9Wn/Uwt3WhuKuu9mrV18EnNpDqmeNll+mdV02yyBROU29Tlili6gg==", - "dependencies": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/auth-unauthenticated": "^3.0.0", - "@octokit/core": "^4.0.0", - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@types/aws-lambda": "^8.10.83", - "fromentries": "^1.3.1", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/oauth-authorization-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz", - "integrity": "sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/oauth-methods": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-2.0.4.tgz", - "integrity": "sha512-RDSa6XL+5waUVrYSmOlYROtPq0+cfwppP4VaQY/iIei3xlFb0expH6YNsxNrZktcLhJWSpm9uzeom+dQrXlS3A==", - "dependencies": { - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", - "btoa-lite": "^1.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", - "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", - "dependencies": { - "@octokit/types": "^8.0.0" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=4" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz", - "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==", - "dependencies": { - "@octokit/types": "^8.1.1", - "deprecation": "^2.3.1" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-retry": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-4.0.4.tgz", - "integrity": "sha512-d7qGFLR3AH+WbNEDUvBPgMc7wRCxU40FZyNXFFqs8ISw75ZYS5/P3ScggzU13dCoY0aywYDxKugGstQTwNgppA==", - "dependencies": { - "@octokit/types": "^9.0.0", - "bottleneck": "^2.15.3" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/plugin-throttling": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-4.3.2.tgz", - "integrity": "sha512-ZaCK599h3tzcoy0Jtdab95jgmD7X9iAk59E2E7hYKCAmnURaI4WpzwL9vckImilybUGrjY1JOWJapDs2N2D3vw==", - "dependencies": { - "@octokit/types": "^8.0.0", - "bottleneck": "^2.15.3" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": "^4.0.0" - } - }, - "node_modules/@octokit/request": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz", - "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==", - "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "node_modules/@octokit/request-error/node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "dependencies": { - "@octokit/openapi-types": "^16.0.0" - } - }, - "node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, - "node_modules/@octokit/webhooks": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-10.7.0.tgz", - "integrity": "sha512-zZBbQMpXXnK/ki/utrFG/TuWv9545XCSLibfDTxrYqR1PmU6zel02ebTOrA7t5XIGHzlEOc/NgISUIBUe7pMFA==", - "dependencies": { - "@octokit/request-error": "^3.0.0", - "@octokit/webhooks-methods": "^3.0.0", - "@octokit/webhooks-types": "6.10.0", - "aggregate-error": "^3.1.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/webhooks-methods": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-3.0.2.tgz", - "integrity": "sha512-Vlnv5WBscf07tyAvfDbp7pTkMZUwk7z7VwEF32x6HqI+55QRwBTcT+D7DDjZXtad/1dU9E32x0HmtDlF9VIRaQ==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/webhooks-types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-6.10.0.tgz", - "integrity": "sha512-lDNv83BeEyxxukdQ0UttiUXawk9+6DkdjjFtm2GFED+24IQhTVaoSbwV9vWWKONyGLzRmCQqZmoEWkDhkEmPlw==" - }, "node_modules/@openapitools/openapi-generator-cli": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.6.0.tgz", @@ -2028,16 +1576,6 @@ "node": ">= 6" } }, - "node_modules/@types/aws-lambda": { - "version": "8.10.109", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.109.tgz", - "integrity": "sha512-/ME92FneNyXQzrAfcnQQlW1XkCZGPDlpi2ao1MJwecN+6SbeonKeggU8eybv1DfKli90FAVT1MlIZVXfwVuCyg==" - }, - "node_modules/@types/btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg==" - }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -2095,14 +1633,6 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -2117,11 +1647,6 @@ "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", "dev": true }, - "node_modules/@types/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" - }, "node_modules/@types/mdast": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", @@ -2760,11 +2285,6 @@ } ] }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -2830,11 +2350,6 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2861,11 +2376,6 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "node_modules/btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==" - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -2898,11 +2408,6 @@ "node": "*" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, "node_modules/buffer-indexof-polyfill": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", @@ -3643,11 +3148,6 @@ "node": ">= 0.6" } }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3787,14 +3287,6 @@ "wcwidth": ">=1.0.1" } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4443,25 +3935,6 @@ "node": ">= 6" } }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -5201,14 +4674,6 @@ "node": ">=8" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -5410,40 +4875,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", - "dependencies": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keytar": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", @@ -6782,24 +6213,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/octokit": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/octokit/-/octokit-2.0.11.tgz", - "integrity": "sha512-Ivjapy5RXWvJfmZe0BvfMM2gnNi39rjheZV/s3SjICb7gfl83JWPDmBERe4f/l2czdRnj4NVIn4YO7Q737oLCg==", - "dependencies": { - "@octokit/app": "^13.1.1", - "@octokit/core": "^4.0.4", - "@octokit/oauth-app": "^4.0.6", - "@octokit/plugin-paginate-rest": "^5.0.0", - "@octokit/plugin-rest-endpoint-methods": "^6.0.0", - "@octokit/plugin-retry": "^4.0.3", - "@octokit/plugin-throttling": "^4.0.1", - "@octokit/types": "^8.0.0" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -7836,6 +7249,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, "funding": [ { "type": "github", @@ -8119,9 +7533,9 @@ } }, "node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -8758,20 +8172,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/universal-github-app-jwt": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz", - "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==", - "dependencies": { - "@types/jsonwebtoken": "^9.0.0", - "jsonwebtoken": "^9.0.0" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -10018,394 +9418,6 @@ "node-fetch": "^2.6.1" } }, - "@octokit/app": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/@octokit/app/-/app-13.1.2.tgz", - "integrity": "sha512-Kf+h5sa1SOI33hFsuHvTsWj1jUrjp1x4MuiJBq7U/NicfEGa6nArPUoDnyfP/YTmcQ5cQ5yvOgoIBkbwPg6kzQ==", - "requires": { - "@octokit/auth-app": "^4.0.8", - "@octokit/auth-unauthenticated": "^3.0.0", - "@octokit/core": "^4.0.0", - "@octokit/oauth-app": "^4.0.7", - "@octokit/plugin-paginate-rest": "^6.0.0", - "@octokit/types": "^9.0.0", - "@octokit/webhooks": "^10.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/plugin-paginate-rest": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz", - "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==", - "requires": { - "@octokit/types": "^9.0.0" - } - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/auth-app": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-4.0.8.tgz", - "integrity": "sha512-miI7y9FfS/fL1bSPsDaAfCGSxQ04iGLyisI2GA8N7P6eB6AkCOt+F1XXapJKRnAubQubvYF0dqxoTZYyKk93NQ==", - "requires": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", - "@types/lru-cache": "^5.1.0", - "deprecation": "^2.3.1", - "lru-cache": "^6.0.0", - "universal-github-app-jwt": "^1.1.1", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-oauth-app": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.5.tgz", - "integrity": "sha512-UPX1su6XpseaeLVCi78s9droxpGtBWIgz9XhXAx9VXabksoF0MyI5vaa1zo1njyYt6VaAjFisC2A2Wchcu2WmQ==", - "requires": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "@types/btoa-lite": "^1.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/auth-oauth-device": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.3.tgz", - "integrity": "sha512-KPTx5nMntKjNZzzltO3X4T68v22rd7Cp/TcLJXQE2U8aXPcZ9LFuww9q9Q5WUNSu3jwi3lRwzfkPguRfz1R8Vg==", - "requires": { - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^8.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-oauth-user": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.0.tgz", - "integrity": "sha512-TC2Mj8NkSy9uAnZLYX+FKB/IH6uDe+qYNzHzH8l13JlzsrTE7GKkcqtXdSGGN4tncyROAB4/KS5rDPRCEnWHlA==", - "requires": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^8.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-token": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz", - "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==", - "requires": { - "@octokit/types": "^9.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/auth-unauthenticated": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.4.tgz", - "integrity": "sha512-AT74XGBylcLr4lmUp1s6mjSUgphGdlse21Qjtv5DzpX1YOl5FXKwvNcZWESdhyBbpDT8VkVyLFqa/7a7eqpPNw==", - "requires": { - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/core": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz", - "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==", - "requires": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/endpoint": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.4.tgz", - "integrity": "sha512-hXJP43VT2IrUxBCNIahta8qawpIzLvCjHLCuDDsdIPbd6+jPwsc3KGl/kdQ37mLd+sdiJm6c9qKI7k5CjE0Z9A==", - "requires": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/graphql": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz", - "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==", - "requires": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/oauth-app": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-4.2.0.tgz", - "integrity": "sha512-gyGclT77RQMkVUEW3YBeAKY+LBSc5u3eC9Wn/Uwt3WhuKuu9mrV18EnNpDqmeNll+mdV02yyBROU29Tlili6gg==", - "requires": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/auth-unauthenticated": "^3.0.0", - "@octokit/core": "^4.0.0", - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@types/aws-lambda": "^8.10.83", - "fromentries": "^1.3.1", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/oauth-authorization-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz", - "integrity": "sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg==" - }, - "@octokit/oauth-methods": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-2.0.4.tgz", - "integrity": "sha512-RDSa6XL+5waUVrYSmOlYROtPq0+cfwppP4VaQY/iIei3xlFb0expH6YNsxNrZktcLhJWSpm9uzeom+dQrXlS3A==", - "requires": { - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", - "btoa-lite": "^1.0.0" - } - }, - "@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "@octokit/plugin-paginate-rest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", - "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", - "requires": { - "@octokit/types": "^8.0.0" - } - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz", - "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==", - "requires": { - "@octokit/types": "^8.1.1", - "deprecation": "^2.3.1" - } - }, - "@octokit/plugin-retry": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-4.0.4.tgz", - "integrity": "sha512-d7qGFLR3AH+WbNEDUvBPgMc7wRCxU40FZyNXFFqs8ISw75ZYS5/P3ScggzU13dCoY0aywYDxKugGstQTwNgppA==", - "requires": { - "@octokit/types": "^9.0.0", - "bottleneck": "^2.15.3" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/plugin-throttling": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-4.3.2.tgz", - "integrity": "sha512-ZaCK599h3tzcoy0Jtdab95jgmD7X9iAk59E2E7hYKCAmnURaI4WpzwL9vckImilybUGrjY1JOWJapDs2N2D3vw==", - "requires": { - "@octokit/types": "^8.0.0", - "bottleneck": "^2.15.3" - } - }, - "@octokit/request": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz", - "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==", - "requires": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^8.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "requires": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==" - }, - "@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", - "requires": { - "@octokit/openapi-types": "^16.0.0" - } - } - } - }, - "@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "requires": { - "@octokit/openapi-types": "^14.0.0" - } - }, - "@octokit/webhooks": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-10.7.0.tgz", - "integrity": "sha512-zZBbQMpXXnK/ki/utrFG/TuWv9545XCSLibfDTxrYqR1PmU6zel02ebTOrA7t5XIGHzlEOc/NgISUIBUe7pMFA==", - "requires": { - "@octokit/request-error": "^3.0.0", - "@octokit/webhooks-methods": "^3.0.0", - "@octokit/webhooks-types": "6.10.0", - "aggregate-error": "^3.1.0" - } - }, - "@octokit/webhooks-methods": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-3.0.2.tgz", - "integrity": "sha512-Vlnv5WBscf07tyAvfDbp7pTkMZUwk7z7VwEF32x6HqI+55QRwBTcT+D7DDjZXtad/1dU9E32x0HmtDlF9VIRaQ==" - }, - "@octokit/webhooks-types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-6.10.0.tgz", - "integrity": "sha512-lDNv83BeEyxxukdQ0UttiUXawk9+6DkdjjFtm2GFED+24IQhTVaoSbwV9vWWKONyGLzRmCQqZmoEWkDhkEmPlw==" - }, "@openapitools/openapi-generator-cli": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.6.0.tgz", @@ -10658,16 +9670,6 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, - "@types/aws-lambda": { - "version": "8.10.109", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.109.tgz", - "integrity": "sha512-/ME92FneNyXQzrAfcnQQlW1XkCZGPDlpi2ao1MJwecN+6SbeonKeggU8eybv1DfKli90FAVT1MlIZVXfwVuCyg==" - }, - "@types/btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg==" - }, "@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -10725,14 +9727,6 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "@types/jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==", - "requires": { - "@types/node": "*" - } - }, "@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -10747,11 +9741,6 @@ "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", "dev": true }, - "@types/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" - }, "@types/mdast": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", @@ -11217,11 +10206,6 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, - "before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, "big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -11277,11 +10261,6 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, - "bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -11305,11 +10284,6 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==" - }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -11325,11 +10299,6 @@ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, "buffer-indexof-polyfill": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", @@ -11862,11 +10831,6 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, "dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -11970,14 +10934,6 @@ "wcwidth": ">=1.0.1" } }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -12488,11 +11444,6 @@ "mime-types": "^2.1.12" } }, - "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==" - }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -13030,11 +11981,6 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, "is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -13183,36 +12129,6 @@ "universalify": "^2.0.0" } }, - "jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", - "requires": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "keytar": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", @@ -14121,21 +13037,6 @@ "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, - "octokit": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/octokit/-/octokit-2.0.11.tgz", - "integrity": "sha512-Ivjapy5RXWvJfmZe0BvfMM2gnNi39rjheZV/s3SjICb7gfl83JWPDmBERe4f/l2czdRnj4NVIn4YO7Q737oLCg==", - "requires": { - "@octokit/app": "^13.1.1", - "@octokit/core": "^4.0.4", - "@octokit/oauth-app": "^4.0.6", - "@octokit/plugin-paginate-rest": "^5.0.0", - "@octokit/plugin-rest-endpoint-methods": "^6.0.0", - "@octokit/plugin-retry": "^4.0.3", - "@octokit/plugin-throttling": "^4.0.1", - "@octokit/types": "^8.0.0" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -14844,7 +13745,8 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -15042,9 +13944,9 @@ } }, "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "requires": { "ansi-regex": "^6.0.1" } @@ -15516,20 +14418,6 @@ "unist-util-is": "^5.0.0" } }, - "universal-github-app-jwt": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz", - "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==", - "requires": { - "@types/jsonwebtoken": "^9.0.0", - "jsonwebtoken": "^9.0.0" - } - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/extension/package.json b/extension/package.json index c834f402..598ae778 100644 --- a/extension/package.json +++ b/extension/package.json @@ -169,7 +169,7 @@ "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": "rm -rf ./out && node esbuild.mjs", "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", @@ -215,11 +215,10 @@ "@vitejs/plugin-react-swc": "^3.3.2", "axios": "^1.2.5", "highlight.js": "^11.7.0", - "octokit": "^2.0.11", "posthog-js": "^1.63.3", "react-markdown": "^8.0.7", "react-redux": "^8.0.5", - "strip-ansi": "^7.0.1", + "strip-ansi": "^7.1.0", "tailwindcss": "^3.3.2", "vite": "^4.3.9", "vscode-languageclient": "^8.0.2", diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index dab5a752..a150e370 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -22,6 +22,7 @@ import { HistoryNode } from "../../../schema/HistoryNode"; import ReactMarkdown from "react-markdown"; import ContinueButton from "./ContinueButton"; import InputAndButton from "./InputAndButton"; +import ToggleErrorDiv from "./ToggleErrorDiv"; interface StepContainerProps { historyNode: HistoryNode; @@ -170,13 +171,10 @@ function StepContainer(props: StepContainerProps) { )} {props.historyNode.observation?.error ? ( - <> - Error while running step: -
-
-                {props.historyNode.observation.error as string}
-              
- + ) : ( {props.historyNode.step.description as any} diff --git a/extension/react-app/src/components/ToggleErrorDiv.tsx b/extension/react-app/src/components/ToggleErrorDiv.tsx new file mode 100644 index 00000000..69112ef7 --- /dev/null +++ b/extension/react-app/src/components/ToggleErrorDiv.tsx @@ -0,0 +1,41 @@ +import React, { useState } from "react"; +import styled from "styled-components"; +import { defaultBorderRadius } from "."; + +// Should be a toggleable div with red border and light red background that displays a main message and detail inside + +interface ToggleErrorDivProps { + title: string; + error: string; +} + +const TopDiv = styled.div` + border: 1px solid red; + background-color: #ff000020; + padding: 8px; + + border-radius: ${defaultBorderRadius}; + cursor: pointer; +`; + +const ToggleErrorDiv = (props: ToggleErrorDivProps) => { + const [open, setOpen] = useState(false); + return ( + { + setOpen(!open); + }} + > +
+
+

+ {open ? "▼" : "▶"} {props.title} +

+
+
+ {open &&
{props.error}
} +
+ ); +}; + +export default ToggleErrorDiv; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index c66172a9..308dfd57 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -43,6 +43,10 @@ function GUI(props: GUIProps) { // description: // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`", // }, + // observation: { + // error: + // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'", + // }, // output: [ // { // traceback: { diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 25287d32..e84602f0 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -12,6 +12,7 @@ import { debugPanelWebview, setupDebugPanel } from "./debugPanel"; import { FileEditWithFullContents } from "../schema/FileEditWithFullContents"; import fs = require("fs"); import { WebsocketMessenger } from "./util/messenger"; +import { CapturedTerminal } from "./terminal/terminalEmulator"; class IdeProtocolClient { private messenger: WebsocketMessenger | null = null; @@ -100,7 +101,9 @@ class IdeProtocolClient { this.highlightCode(data.rangeInFile, data.color); break; case "runCommand": - this.runCommand(data.command); + this.messenger?.send("runCommand", { + output: await this.runCommand(data.command), + }); break; case "saveFile": this.saveFile(data.filepath); @@ -316,18 +319,15 @@ class IdeProtocolClient { return rangeInFiles; } - runCommand(command: string) { - if (vscode.window.terminals.length === 0) { - const terminal = vscode.window.createTerminal(); - terminal.show(); - terminal.sendText("bash", true); - terminal.sendText(command, true); - return; + private continueTerminal: CapturedTerminal | undefined; + + async runCommand(command: string) { + if (!this.continueTerminal) { + this.continueTerminal = new CapturedTerminal("Continue"); } - const terminal = vscode.window.terminals[0]; - terminal.show(); - terminal.sendText(command, true); - // But need to know when it's done executing... + + this.continueTerminal.show(); + return await this.continueTerminal.runCommand(command); } } diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index 6cf65970..8974b7e3 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -1,140 +1,161 @@ -// /* Terminal emulator - commented because node-pty is causing problems. */ - -// import * as vscode from "vscode"; -// import pty = require("node-pty"); -// import os = require("os"); -// import { extensionContext } from "../activation/activate"; -// import { debugPanelWebview } from "../debugPanel"; // Need to consider having multiple panels, where to store this state. -// import { -// CommandCaptureSnooper, -// PythonTracebackSnooper, -// TerminalSnooper, -// } from "./snoopers"; - -// export function tracebackToWebviewAction(traceback: string) { -// if (debugPanelWebview) { -// debugPanelWebview.postMessage({ -// type: "traceback", -// value: traceback, -// }); -// } else { -// vscode.commands -// .executeCommand("continue.openContinueGUI", extensionContext) -// .then(() => { -// // TODO: Waiting for the webview to load, but should add a hook to the onLoad message event. Same thing in autodebugTest command in commands.ts -// setTimeout(() => { -// debugPanelWebview?.postMessage({ -// type: "traceback", -// value: traceback, -// }); -// }, 500); -// }); -// } -// } - -// const DEFAULT_SNOOPERS = [ -// new PythonTracebackSnooper(tracebackToWebviewAction), -// new CommandCaptureSnooper((data: string) => { -// if (data.trim().startsWith("pytest ")) { -// let fileAndFunctionSpecifier = data.split(" ")[1]; -// vscode.commands.executeCommand( -// "continue.debugTest", -// fileAndFunctionSpecifier -// ); -// } -// }), -// ]; - -// // Whenever a user opens a terminal, replace it with ours -// vscode.window.onDidOpenTerminal((terminal) => { -// if (terminal.name != "Continue") { -// terminal.dispose(); -// openCapturedTerminal(); -// } -// }); - -// function getDefaultShell(): string { -// if (process.platform !== "win32") { -// return os.userInfo().shell; -// } -// switch (process.platform) { -// case "win32": -// return process.env.COMSPEC || "cmd.exe"; -// // case "darwin": -// // return process.env.SHELL || "/bin/zsh"; -// // default: -// // return process.env.SHELL || "/bin/sh"; -// } -// } - -// function getRootDir(): string | undefined { -// var isWindows = os.platform() === "win32"; -// let cwd = isWindows ? process.env.USERPROFILE : process.env.HOME; -// if ( -// vscode.workspace.workspaceFolders && -// vscode.workspace.workspaceFolders.length > 0 -// ) { -// cwd = vscode.workspace.workspaceFolders[0].uri.fsPath; -// } -// return cwd; -// } - -// export function openCapturedTerminal( -// snoopers: TerminalSnooper[] = DEFAULT_SNOOPERS -// ) { -// // If there is another existing, non-Continue terminal, delete it -// let terminals = vscode.window.terminals; -// for (let i = 0; i < terminals.length; i++) { -// if (terminals[i].name != "Continue") { -// terminals[i].dispose(); -// } -// } - -// let env = { ...(process.env as any) }; -// if (os.platform() !== "win32") { -// env["PATH"] += ":" + ["/opt/homebrew/bin", "/opt/homebrew/sbin"].join(":"); -// } - -// var ptyProcess = pty.spawn(getDefaultShell(), [], { -// name: "xterm-256color", -// cols: 160, // TODO: Get size of vscode terminal, and change with resize -// rows: 26, -// cwd: getRootDir(), -// env, -// useConpty: true, -// }); - -// const writeEmitter = new vscode.EventEmitter(); - -// ptyProcess.onData((data: any) => { -// // Let each of the snoopers see the new data -// for (let snooper of snoopers) { -// snooper.onData(data); -// } - -// // Pass data through to terminal -// writeEmitter.fire(data); -// }); -// process.on("exit", () => ptyProcess.kill()); - -// const newPty: vscode.Pseudoterminal = { -// onDidWrite: writeEmitter.event, -// open: () => {}, -// close: () => {}, -// handleInput: (data) => { -// for (let snooper of snoopers) { -// snooper.onWrite(data); -// } -// ptyProcess.write(data); -// }, -// }; -// const terminal = vscode.window.createTerminal({ -// name: "Continue", -// pty: newPty, -// }); -// terminal.show(); - -// setTimeout(() => { -// ptyProcess.write("clear\r"); -// }, 500); -// } +/* Terminal emulator - commented because node-pty is causing problems. */ + +import * as vscode from "vscode"; +import os = require("os"); +import stripAnsi from "strip-ansi"; + +function loadNativeModule(id: string): T | null { + try { + return require(`${vscode.env.appRoot}/node_modules.asar/${id}`); + } catch (err) { + // ignore + } + + try { + return require(`${vscode.env.appRoot}/node_modules/${id}`); + } catch (err) { + // ignore + } + + return null; +} + +const pty = loadNativeModule("node-pty"); + +function getDefaultShell(): string { + if (process.platform !== "win32") { + return os.userInfo().shell; + } + switch (process.platform) { + case "win32": + return process.env.COMSPEC || "cmd.exe"; + // case "darwin": + // return process.env.SHELL || "/bin/zsh"; + // default: + // return process.env.SHELL || "/bin/sh"; + } +} + +function getRootDir(): string | undefined { + const isWindows = os.platform() === "win32"; + let cwd = isWindows ? process.env.USERPROFILE : process.env.HOME; + if ( + vscode.workspace.workspaceFolders && + vscode.workspace.workspaceFolders.length > 0 + ) { + cwd = vscode.workspace.workspaceFolders[0].uri.fsPath; + } + return cwd; +} + +export class CapturedTerminal { + private readonly terminal: vscode.Terminal; + private readonly shellCmd: string; + private readonly ptyProcess: any; + + private shellPrompt: string | undefined = undefined; + private dataBuffer: string = ""; + + private onDataListeners: ((data: string) => void)[] = []; + + show() { + this.terminal.show(); + } + + private commandQueue: [string, (output: string) => void][] = []; + private hasRunCommand: boolean = false; + + private async waitForCommandToFinish() { + return new Promise((resolve, reject) => { + this.onDataListeners.push((data: any) => { + const strippedData = stripAnsi(data); + this.dataBuffer += strippedData; + const lines = this.dataBuffer.split("\n"); + if ( + lines.length > 0 && + lines[lines.length - 1].includes("bash-") && + lines[lines.length - 1].trim().endsWith("$") + ) { + resolve(this.dataBuffer); + this.dataBuffer = ""; + this.onDataListeners = []; + } + }); + }); + } + + async runCommand(command: string): Promise { + if (!this.hasRunCommand) { + this.hasRunCommand = true; + // Let the first bash- prompt appear and let python env be opened + await this.waitForCommandToFinish(); + } + + if (this.commandQueue.length === 0) { + return new Promise(async (resolve, reject) => { + this.commandQueue.push([command, resolve]); + + while (this.commandQueue.length > 0) { + const [command, resolve] = this.commandQueue.shift()!; + + this.terminal.sendText(command); + resolve(await this.waitForCommandToFinish()); + } + }); + } else { + return new Promise((resolve, reject) => { + this.commandQueue.push([command, resolve]); + }); + } + } + + private readonly writeEmitter: vscode.EventEmitter; + + constructor(terminalName: string) { + this.shellCmd = "bash"; // getDefaultShell(); + + const env = { ...(process.env as any) }; + if (os.platform() !== "win32") { + env.PATH += `:${["/opt/homebrew/bin", "/opt/homebrew/sbin"].join(":")}`; + } + + // Create the pseudo terminal + this.ptyProcess = pty.spawn(this.shellCmd, [], { + name: "xterm-256color", + cols: 160, // TODO: Get size of vscode terminal, and change with resize + rows: 26, + cwd: getRootDir(), + env, + useConpty: true, + }); + + this.writeEmitter = new vscode.EventEmitter(); + + this.ptyProcess.onData((data: any) => { + // Pass data through to terminal + this.writeEmitter.fire(data); + + for (let listener of this.onDataListeners) { + listener(data); + } + }); + + process.on("exit", () => this.ptyProcess.kill()); + + const newPty: vscode.Pseudoterminal = { + onDidWrite: this.writeEmitter.event, + open: () => {}, + close: () => {}, + handleInput: (data) => { + this.ptyProcess.write(data); + }, + }; + + // Create and clear the terminal + this.terminal = vscode.window.createTerminal({ + name: terminalName, + pty: newPty, + }); + this.terminal.show(); + } +} -- cgit v1.2.3-70-g09d2 From 6ebb5088a1363d4de8b9d2e6abaa02c49ee90f05 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 7 Jun 2023 01:08:34 -0400 Subject: dlt stuff --- continuedev/src/continuedev/core/autopilot.py | 20 ++++++- continuedev/src/continuedev/core/main.py | 17 ++++-- continuedev/src/continuedev/core/policy.py | 8 +-- continuedev/src/continuedev/core/sdk.py | 4 +- .../recipes/CreatePipelineRecipe/steps.py | 64 +++++++++++++++++----- extension/package-lock.json | 4 +- extension/package.json | 2 +- .../react-app/src/components/StepContainer.tsx | 22 +++++--- extension/react-app/src/components/index.ts | 8 ++- extension/react-app/src/tabs/gui.tsx | 17 +++--- extension/src/continueIdeClient.ts | 1 + extension/src/terminal/terminalEmulator.ts | 5 +- 12 files changed, 124 insertions(+), 48 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index b227570e..5a6bd2e7 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -83,9 +83,9 @@ class Autopilot(ContinueBaseModel): _step_depth: int = 0 async def retry_at_index(self, index: int): - last_step = self.history.pop_last_step() + step = self.history.timeline[index].step.copy() await self.update_subscribers() - await self._run_singular_step(last_step) + await self._run_singular_step(step) async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: capture_event( @@ -119,7 +119,17 @@ class Autopilot(ContinueBaseModel): observation = InternalErrorObservation( error=error_string, title=e.title) + + # Reveal this step, but hide all of the following steps (its substeps) step.hide = False + i = self.history.get_current_index() + while self.history.timeline[i].step.name != step.name: + self.history.timeline[i].step.hide = True + i -= 1 + + if e.with_step is not None: + await self._run_singular_step(e.with_step) + except Exception as e: # Attach an InternalErrorObservation to the step and unhide it. error_string = '\n\n'.join( @@ -129,7 +139,13 @@ class Autopilot(ContinueBaseModel): observation = InternalErrorObservation( error=error_string, title=e.__repr__()) + + # Reveal this step, but hide all of the following steps (its substeps) step.hide = False + i = self.history.get_current_index() + while self.history.timeline[i].step.name != step.name: + self.history.timeline[i].step.hide = True + i -= 1 self._step_depth -= 1 diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 33e25c93..37d80de3 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -67,11 +67,16 @@ class History(ContinueBaseModel): return None return state.observation - def pop_last_step(self) -> Union[HistoryNode, None]: - if self.current_index < 0: + def pop_step(self, index: int = None) -> Union[HistoryNode, None]: + index = index if index is not None else self.current_index + if index < 0 or self.current_index < 0: return None - node = self.timeline.pop(self.current_index) - self.current_index -= 1 + + node = self.timeline.pop(index) + + if index <= self.current_index: + self.current_index -= 1 + return node.step @classmethod @@ -183,10 +188,12 @@ class Context: class ContinueCustomException(Exception): title: str message: str + with_step: Union[Step, None] - def __init__(self, message: str, title: str = "Error while running step:"): + def __init__(self, message: str, title: str = "Error while running step:", with_step: Union[Step, None] = None): self.message = message self.title = title + self.with_step = with_step HistoryNode.update_forward_refs() diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index 4934497d..91ae3c83 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -18,10 +18,10 @@ class DemoPolicy(Policy): # At the very start, run initial Steps spcecified in the config if history.get_current() is None: return ( - MessageStep(name="Welcome to Continue!", message="") >> - # SetupContinueWorkspaceStep() >> - # CreateCodebaseIndexChroma() >> - StepsOnStartupStep()) + # MessageStep(name="Welcome to Continue!", message="") >> + # SetupContinueWorkspaceStep() >> + # CreateCodebaseIndexChroma() >> + StepsOnStartupStep()) observation = history.get_current().observation if observation is not None and isinstance(observation, UserInputObservation): diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index f4aa2b35..76caef02 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -131,5 +131,5 @@ class ContinueSDK(AbstractContinueSDK): # self.__autopilot.set_loading_message(message) raise NotImplementedError() - def raise_exception(self, message: str, title: str): - raise ContinueCustomException(message, title) + def raise_exception(self, message: str, title: str, with_step: Union[Step, None] = None): + raise ContinueCustomException(message, title, with_step) diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index 9bee4c95..c32ae923 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -1,16 +1,19 @@ import os import subprocess from textwrap import dedent +import time from ...models.main import Range from ...models.filesystem import RangeInFile from ...steps.main import MessageStep from ...core.sdk import Models from ...core.observation import DictObservation, InternalErrorObservation -from ...models.filesystem_edit import AddFile +from ...models.filesystem_edit import AddFile, FileEdit from ...core.main import Step from ...core.sdk import ContinueSDK +AI_ASSISTED_STRING = "(✨ AI-Assisted ✨)" + class SetupPipelineStep(Step): hide: bool = True @@ -25,6 +28,8 @@ class SetupPipelineStep(Step): """) async def run(self, sdk: ContinueSDK): + sdk.context.set("api_description", self.api_description) + source_name = (await sdk.models.gpt35()).complete( f"Write a snake_case name for the data source described by {self.api_description}: ").strip() filename = f'{source_name}.py' @@ -45,14 +50,17 @@ class SetupPipelineStep(Step): - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline"""), name="Setup Python environment") # editing the resource function to call the requested API - await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 30, 0)), "#00ff0022") + await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 29, 0)), "#00ff0022") + # sdk.set_loading_message("Writing code to call the API...") await sdk.edit_file( filename=filename, - prompt=f'Edit the resource function to call the API described by this: {self.api_description}', - name="Edit the resource function to call the API" + prompt=f'Edit the resource function to call the API described by this: {self.api_description}. Do not move or remove the exit() call in __main__.', + name=f"Edit the resource function to call the API {AI_ASSISTED_STRING}" ) + time.sleep(1) + # wait for user to put API key in secrets.toml await sdk.ide.setFileOpen(await sdk.ide.getWorkspaceDirectory() + "/.dlt/secrets.toml") await sdk.wait_for_user_confirmation("If this service requires an API key, please add it to the `secrets.toml` file and then press `Continue`") @@ -76,20 +84,50 @@ class ValidatePipelineStep(Step): # """))) # test that the API call works - output = await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API") + output = await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running `python3 {filename}` to test loading data from the API") # If it fails, return the error if "Traceback" in output: + output = "Traceback" + output.split("Traceback")[-1] + file_content = await sdk.ide.readFile(os.path.join(workspace_dir, filename)) + suggestion = (await sdk.models.gpt35()).complete(dedent(f"""\ + ```python + {file_content} + ``` + This above code is a dlt pipeline that loads data from an API. The function with the @resource decorator is responsible for calling the API and returning the data. While attempting to run the pipeline, the following error occurred: + + ```ascii + {output} + ``` + + This is a brief summary of the error followed by a suggestion on how it can be fixed by editing the resource function:""")) + + api_documentation_url = (await sdk.models.gpt35()).complete(dedent(f"""\ + The API I am trying to call is the '{sdk.context.get('api_description')}'. I tried calling it in the @resource function like this: + ```python + {file_content} + ``` + What is the URL for the API documentation that will help me learn how to make this call? Please format in markdown so I can click the link.""")) + sdk.raise_exception( - title="Error while running pipeline.\nFix the resource function in {filename} and rerun this step", description=output) + title=f"Error while running pipeline.\nFix the resource function in {filename} and rerun this step", message=output, with_step=MessageStep(name=f"Suggestion to solve error {AI_ASSISTED_STRING}", message=dedent(f"""\ + {suggestion} + + {api_documentation_url} + + After you've fixed the code, click the retry button at the top of the Validate Pipeline step above."""))) # remove exit() from the main main function - await sdk.edit_file( - filename=filename, - prompt='Remove exit() from the main function', - name="Remove early exit() from main function", - description="Remove the `exit()` call from the main function in the pipeline file so that the data is loaded into DuckDB" - ) + await sdk.run_step(MessageStep(name="Remove early exit() from main function", message="Remove the early exit() from the main function now that we are done testing and want the pipeline to load the data into DuckDB.")) + + contents = await sdk.ide.readFile(os.path.join(workspace_dir, filename)) + replacement = "\n".join( + list(filter(lambda line: line.strip() != "exit()", contents.split("\n")))) + await sdk.ide.applyFileSystemEdit(FileEdit( + filepath=os.path.join(workspace_dir, filename), + replacement=replacement, + range=Range.from_entire_file(contents) + )) # load the data into the DuckDB instance await sdk.run(f'python3 {filename}', name="Load data into DuckDB", description=f"Running python3 {filename} to load data into DuckDB") @@ -109,6 +147,6 @@ class ValidatePipelineStep(Step): print(row) ''') - query_filename = (await sdk.ide.getWorkspaceDirectory()) + "/query.py" + query_filename = os.path.join(workspace_dir, "query.py") await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code), name="Add query.py file", description="Adding a file called `query.py` to the workspace that will run a test query on the DuckDB instance") await sdk.run('env/bin/python3 query.py', name="Run test query", description="Running `env/bin/python3 query.py` to test that the data was loaded into DuckDB as expected") diff --git a/extension/package-lock.json b/extension/package-lock.json index a20be756..0b0e063b 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.20", + "version": "0.0.23", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.20", + "version": "0.0.23", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 598ae778..c979a435 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.20", + "version": "0.0.23", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index a150e370..8ea54325 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -50,8 +50,9 @@ const StepContainerDiv = styled.div<{ open: boolean }>` /* padding: 8px; */ `; -const HeaderDiv = styled.div` - background-color: ${vscBackgroundTransparent}; +const HeaderDiv = styled.div<{ error: boolean }>` + background-color: ${(props) => + props.error ? "#522" : vscBackgroundTransparent}; display: grid; grid-template-columns: 1fr auto; align-items: center; @@ -124,17 +125,23 @@ function StepContainer(props: StepContainerProps) { > setOpen((prev) => !prev)} > - +

{open ? ( ) : ( )} - {props.historyNode.step.name as any} + {props.historyNode.observation?.title || + (props.historyNode.step.name as any)}

{/* { @@ -171,10 +178,9 @@ function StepContainer(props: StepContainerProps) { )} {props.historyNode.observation?.error ? ( - +
+              {props.historyNode.observation.error as string}
+            
) : ( {props.historyNode.step.description as any} diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts index ac5faa41..4966f3e8 100644 --- a/extension/react-app/src/components/index.ts +++ b/extension/react-app/src/components/index.ts @@ -98,17 +98,21 @@ export const Loader = styled.div` export const GradientBorder = styled.div<{ borderWidth?: string; borderRadius?: string; + borderColor?: string; }>` border-radius: ${(props) => props.borderRadius || "0"}; padding-top: ${(props) => props.borderWidth || "1px"}; padding-bottom: ${(props) => props.borderWidth || "1px"}; - background: linear-gradient( + background: ${(props) => + props.borderColor + ? props.borderColor + : `linear-gradient( 101.79deg, #12887a 0%, #87245c 37.64%, #e12637 65.98%, #ffb215 110.45% - ); + )`}; `; export const MainContainerWithBorder = styled.div<{ borderWidth?: string }>` diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 308dfd57..5c75579b 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -44,6 +44,7 @@ function GUI(props: GUIProps) { // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`", // }, // observation: { + // title: "ERROR FOUND", // error: // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'", // }, @@ -228,9 +229,9 @@ function GUI(props: GUIProps) { setUserInputQueue((queue) => { return [...queue, input]; }); - mainTextInputRef.current.value = ""; - mainTextInputRef.current.style.height = ""; } + mainTextInputRef.current.value = ""; + mainTextInputRef.current.style.height = ""; } setWaitingForSteps(true); @@ -307,13 +308,15 @@ function GUI(props: GUIProps) { }} rows={1} onChange={() => { - let textarea = mainTextInputRef.current!; + const textarea = mainTextInputRef.current!; textarea.style.height = ""; /* Reset the height*/ - textarea.style.height = - Math.min(textarea.scrollHeight - 15, 500) + "px"; + textarea.style.height = `${Math.min( + textarea.scrollHeight - 15, + 500 + )}px`; }} - >
- + /> + ); } diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index e84602f0..bbaf5f08 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -142,6 +142,7 @@ class IdeProtocolClient { editor.setDecorations( vscode.window.createTextEditorDecorationType({ backgroundColor: color, + isWholeLine: true, }), [range] ); diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index 8974b7e3..67b47e2f 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -73,8 +73,9 @@ export class CapturedTerminal { const lines = this.dataBuffer.split("\n"); if ( lines.length > 0 && - lines[lines.length - 1].includes("bash-") && - lines[lines.length - 1].trim().endsWith("$") + (lines[lines.length - 1].includes("bash-") || + lines[lines.length - 1].includes("(main)")) && + lines[lines.length - 1].includes("$") ) { resolve(this.dataBuffer); this.dataBuffer = ""; -- cgit v1.2.3-70-g09d2 From bcfaac2491b5888a425f3cc46ff7efa5048e9c79 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 12 Jun 2023 00:11:45 -0700 Subject: patches --- .../recipes/DeployPipelineAirflowRecipe/main.py | 2 +- .../recipes/DeployPipelineAirflowRecipe/steps.py | 2 +- .../src/continuedev/steps/steps_on_startup.py | 5 +++-- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- .../scripts/continuedev-0.1.1-py3-none-any.whl | Bin 59335 -> 69544 bytes extension/src/terminal/terminalEmulator.ts | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/recipes/DeployPipelineAirflowRecipe/main.py b/continuedev/src/continuedev/recipes/DeployPipelineAirflowRecipe/main.py index fbd6e11d..f3601c2d 100644 --- a/continuedev/src/continuedev/recipes/DeployPipelineAirflowRecipe/main.py +++ b/continuedev/src/continuedev/recipes/DeployPipelineAirflowRecipe/main.py @@ -4,7 +4,7 @@ from ...steps.input.nl_multiselect import NLMultiselectStep from ...core.main import Step from ...core.sdk import ContinueSDK from ...steps.core.core import WaitForUserInputStep -from ...steps.main import MessageStep +from ...steps.core.core import MessageStep from .steps import SetupPipelineStep, DeployAirflowStep diff --git a/continuedev/src/continuedev/recipes/DeployPipelineAirflowRecipe/steps.py b/continuedev/src/continuedev/recipes/DeployPipelineAirflowRecipe/steps.py index ce910252..97e16e82 100644 --- a/continuedev/src/continuedev/recipes/DeployPipelineAirflowRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/DeployPipelineAirflowRecipe/steps.py @@ -6,7 +6,7 @@ import time from ...steps.core.core import WaitForUserInputStep from ...models.main import Range from ...models.filesystem import RangeInFile -from ...steps.main import MessageStep +from ...steps.core.core import MessageStep from ...core.sdk import Models from ...core.observation import DictObservation, InternalErrorObservation from ...models.filesystem_edit import AddFile, FileEdit diff --git a/continuedev/src/continuedev/steps/steps_on_startup.py b/continuedev/src/continuedev/steps/steps_on_startup.py index fbdbbcff..2586eb03 100644 --- a/continuedev/src/continuedev/steps/steps_on_startup.py +++ b/continuedev/src/continuedev/steps/steps_on_startup.py @@ -2,14 +2,15 @@ from ..core.main import ContinueSDK, Models, Step from .main import UserInputStep from ..recipes.CreatePipelineRecipe.main import CreatePipelineRecipe from ..recipes.DeployPipelineAirflowRecipe.main import DeployPipelineAirflowRecipe - +from ..recipes.DDtoBQRecipe.main import DDtoBQRecipe from ..recipes.AddTransformRecipe.main import AddTransformRecipe step_name_to_step_class = { "UserInputStep": UserInputStep, "CreatePipelineRecipe": CreatePipelineRecipe, "DeployPipelineAirflowRecipe": DeployPipelineAirflowRecipe, - "AddTransformRecipe": AddTransformRecipe + "AddTransformRecipe": AddTransformRecipe, + "DDtoBQRecipe": DDtoBQRecipe } diff --git a/extension/package-lock.json b/extension/package-lock.json index 061b6342..c86d3955 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.25", + "version": "0.0.28", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.25", + "version": "0.0.28", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 66ade224..052fe6ff 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.25", + "version": "0.0.28", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl index 4c89d23e..c5a66516 100644 Binary files a/extension/scripts/continuedev-0.1.1-py3-none-any.whl and b/extension/scripts/continuedev-0.1.1-py3-none-any.whl differ diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index 67b47e2f..b3031baf 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -74,7 +74,7 @@ export class CapturedTerminal { if ( lines.length > 0 && (lines[lines.length - 1].includes("bash-") || - lines[lines.length - 1].includes("(main)")) && + lines[lines.length - 1].includes(") $ ")) && lines[lines.length - 1].includes("$") ) { resolve(this.dataBuffer); -- cgit v1.2.3-70-g09d2 From 40ba9eaf82a1386ccacf5046c072df3d131d5284 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 12 Jun 2023 10:50:33 -0700 Subject: calculate diff and highlight changes --- continuedev/poetry.lock | 34 +++-- continuedev/pyproject.toml | 1 + continuedev/src/continuedev/core/sdk.py | 3 + .../src/continuedev/libs/util/calculate_diff.py | 152 +++++++++++++++++++++ .../src/continuedev/libs/util/copy_codebase.py | 38 +----- .../src/continuedev/models/filesystem_edit.py | 4 +- continuedev/src/continuedev/steps/chat.py | 12 ++ continuedev/src/continuedev/steps/react.py | 33 +++-- extension/package.json | 80 ----------- extension/react-app/src/components/CodeBlock.tsx | 1 + .../react-app/src/components/StepContainer.tsx | 23 +++- extension/react-app/src/tabs/chat/MessageDiv.tsx | 4 +- extension/react-app/src/tabs/gui.tsx | 20 ++- extension/src/continueIdeClient.ts | 31 ++++- extension/src/decorations.ts | 19 ++- 15 files changed, 298 insertions(+), 157 deletions(-) create mode 100644 continuedev/src/continuedev/libs/util/calculate_diff.py (limited to 'extension/src') diff --git a/continuedev/poetry.lock b/continuedev/poetry.lock index 857a7c99..4aedce87 100644 --- a/continuedev/poetry.lock +++ b/continuedev/poetry.lock @@ -344,6 +344,21 @@ typing-inspect = ">=0.4.0" [package.extras] dev = ["flake8", "hypothesis", "ipython", "mypy (>=0.710)", "portray", "pytest (>=6.2.3)", "simplejson", "types-dataclasses"] +[[package]] +name = "diff-match-patch" +version = "20230430" +description = "Diff Match and Patch" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "diff-match-patch-20230430.tar.gz", hash = "sha256:953019cdb9c9d2c9e47b5b12bcff3cf4746fc4598eb406076fa1fc27e6a1f15c"}, + {file = "diff_match_patch-20230430-py3-none-any.whl", hash = "sha256:dce43505fb7b1b317de7195579388df0746d90db07015ed47a85e5e44930ef93"}, +] + +[package.extras] +dev = ["attribution (==1.6.2)", "black (==23.3.0)", "flit (==3.8.0)", "mypy (==1.2.0)", "ufmt (==2.1.0)", "usort (==1.0.6)"] + [[package]] name = "fastapi" version = "0.95.1" @@ -1251,23 +1266,6 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "setuptools" -version = "67.7.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - [[package]] name = "six" version = "1.16.0" @@ -1739,4 +1737,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "9f9254c954b7948c49debba86bc81a4a9c3f50694424f5940d0058725b1bf0fb" +content-hash = "0f5f759bac0e44a1fbcc9babeccdea8688ea2226a4bae7a13858542ae03a3228" diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml index 631742ec..7315e79d 100644 --- a/continuedev/pyproject.toml +++ b/continuedev/pyproject.toml @@ -7,6 +7,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.9" +diff-match-patch = "^20230430" fastapi = "^0.95.1" typer = "^0.7.0" openai = "^0.27.5" diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index a94b5026..2849b0c8 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -149,3 +149,6 @@ class ContinueSDK(AbstractContinueSDK): history_context.append(ChatMessage( content=f"The following code is highlighted:\n```\n{code}\n```", role="user")) return history_context + + async def update_ui(self): + await self.__autopilot.update_subscribers() diff --git a/continuedev/src/continuedev/libs/util/calculate_diff.py b/continuedev/src/continuedev/libs/util/calculate_diff.py new file mode 100644 index 00000000..d778891b --- /dev/null +++ b/continuedev/src/continuedev/libs/util/calculate_diff.py @@ -0,0 +1,152 @@ +import difflib +from typing import List +from ...models.main import Position, Range +from ...models.filesystem import FileEdit +from diff_match_patch import diff_match_patch + + +def calculate_diff_match_patch(filepath: str, original: str, updated: str) -> List[FileEdit]: + dmp = diff_match_patch() + diffs = dmp.diff_main(original, updated) + dmp.diff_cleanupSemantic(diffs) + + replacements = [] + + current_index = 0 + deleted_length = 0 + + for diff in diffs: + if diff[0] == diff_match_patch.DIFF_EQUAL: + current_index += len(diff[1]) + deleted_length = 0 + elif diff[0] == diff_match_patch.DIFF_INSERT: + current_index += deleted_length + replacements.append((current_index, current_index, diff[1])) + current_index += len(diff[1]) + deleted_length = 0 + elif diff[0] == diff_match_patch.DIFF_DELETE: + replacements.append( + (current_index, current_index + len(diff[1]), '')) + deleted_length += len(diff[1]) + elif diff[0] == diff_match_patch.DIFF_REPLACE: + replacements.append( + (current_index, current_index + len(diff[1]), '')) + current_index += deleted_length + replacements.append((current_index, current_index, diff[2])) + current_index += len(diff[2]) + deleted_length = 0 + + return [FileEdit(filepath=filepath, range=Range.from_indices(original, r[0], r[1]), replacement=r[2]) for r in replacements] + + +def calculate_diff(filepath: str, original: str, updated: str) -> List[FileEdit]: + s = difflib.SequenceMatcher(None, original, updated) + offset = 0 # The indices are offset by previous deletions/insertions + edits = [] + for tag, i1, i2, j1, j2 in s.get_opcodes(): + i1, i2, j1, j2 = i1 + offset, i2 + offset, j1 + offset, j2 + offset + replacement = updated[j1:j2] + if tag == "equal": + pass + elif tag == "delete": + edits.append(FileEdit.from_deletion( + filepath, Range.from_indices(original, i1, i2))) + offset -= i2 - i1 + elif tag == "insert": + edits.append(FileEdit.from_insertion( + filepath, Position.from_index(original, i1), replacement)) + offset += j2 - j1 + elif tag == "replace": + edits.append(FileEdit(filepath=filepath, range=Range.from_indices( + original, i1, i2), replacement=replacement)) + offset += (j2 - j1) - (i2 - i1) + else: + raise Exception("Unexpected difflib.SequenceMatcher tag: " + tag) + + return edits + + +def calculate_diff2(filepath: str, original: str, updated: str) -> List[FileEdit]: + edits = [] + max_iterations = 1000 + i = 0 + while not original == updated: + # TODO - For some reason it can't handle a single newline at the end of the file? + s = difflib.SequenceMatcher(None, original, updated) + opcodes = s.get_opcodes() + for edit_index in range(len(opcodes)): + tag, i1, i2, j1, j2 = s.get_opcodes()[edit_index] + replacement = updated[j1:j2] + if tag == "equal": + continue + elif tag == "delete": + edits.append(FileEdit.from_deletion( + filepath, Range.from_indices(original, i1, i2))) + elif tag == "insert": + edits.append(FileEdit.from_insertion( + filepath, Position.from_index(original, i1), replacement)) + elif tag == "replace": + edits.append(FileEdit(filepath=filepath, range=Range.from_indices( + original, i1, i2), replacement=replacement)) + else: + raise Exception( + "Unexpected difflib.SequenceMatcher tag: " + tag) + break + + original = apply_edit_to_str(original, edits[-1]) + + i += 1 + if i > max_iterations: + raise Exception("Max iterations reached") + + return edits + + +def read_range_in_str(s: str, r: Range) -> str: + lines = s.splitlines()[r.start.line:r.end.line + 1] + if len(lines) == 0: + return "" + + lines[0] = lines[0][r.start.character:] + lines[-1] = lines[-1][:r.end.character + 1] + return "\n".join(lines) + + +def apply_edit_to_str(s: str, edit: FileEdit) -> str: + original = read_range_in_str(s, edit.range) + + # Split lines and deal with some edge cases (could obviously be nicer) + lines = s.splitlines() + if s.startswith("\n"): + lines.insert(0, "") + if s.endswith("\n"): + lines.append("") + + if len(lines) == 0: + lines = [""] + + end = Position(line=edit.range.end.line, + character=edit.range.end.character) + if edit.range.end.line == len(lines) and edit.range.end.character == 0: + end = Position(line=edit.range.end.line - 1, + character=len(lines[min(len(lines) - 1, edit.range.end.line - 1)])) + + before_lines = lines[:edit.range.start.line] + after_lines = lines[end.line + 1:] + between_str = lines[min(len(lines) - 1, edit.range.start.line)][:edit.range.start.character] + \ + edit.replacement + \ + lines[min(len(lines) - 1, end.line)][end.character + 1:] + + new_range = Range( + start=edit.range.start, + end=Position( + line=edit.range.start.line + + len(edit.replacement.splitlines()) - 1, + character=edit.range.start.character + + len(edit.replacement.splitlines() + [-1]) if edit.replacement != "" else 0 + ) + ) + + lines = before_lines + between_str.splitlines() + after_lines + return "\n".join(lines) diff --git a/continuedev/src/continuedev/libs/util/copy_codebase.py b/continuedev/src/continuedev/libs/util/copy_codebase.py index af957a34..97143faf 100644 --- a/continuedev/src/continuedev/libs/util/copy_codebase.py +++ b/continuedev/src/continuedev/libs/util/copy_codebase.py @@ -3,13 +3,12 @@ from pathlib import Path from typing import Iterable, List, Union from watchdog.observers import Observer from watchdog.events import PatternMatchingEventHandler -from ..models.main import FileEdit, DeleteDirectory, DeleteFile, AddDirectory, AddFile, FileSystemEdit, Position, Range, RenameFile, RenameDirectory, SequentialFileSystemEdit -from ..models.filesystem import FileSystem -from ..libs.main import Autopilot -from ..libs.map_path import map_path -from ..libs.steps.main import ManualEditAction +from ...models.main import FileEdit, DeleteDirectory, DeleteFile, AddDirectory, AddFile, FileSystemEdit, RenameFile, RenameDirectory, SequentialFileSystemEdit +from ...models.filesystem import FileSystem +from ...core.autopilot import Autopilot +from .map_path import map_path +from ...core.sdk import ManualEditStep import shutil -import difflib def create_copy(orig_root: str, copy_root: str = None, ignore: Iterable[str] = []): @@ -36,33 +35,6 @@ def create_copy(orig_root: str, copy_root: str = None, ignore: Iterable[str] = [ os.symlink(child, map_path(child)) -def calculate_diff(filepath: str, original: str, updated: str) -> List[FileEdit]: - s = difflib.SequenceMatcher(None, original, updated) - offset = 0 # The indices are offset by previous deletions/insertions - edits = [] - for tag, i1, i2, j1, j2 in s.get_opcodes(): - i1, i2, j1, j2 = i1 + offset, i2 + offset, j1 + offset, j2 + offset - replacement = updated[j1:j2] - if tag == "equal": - pass - elif tag == "delete": - edits.append(FileEdit.from_deletion( - filepath, Range.from_indices(original, i1, i2))) - offset -= i2 - i1 - elif tag == "insert": - edits.append(FileEdit.from_insertion( - filepath, Position.from_index(original, i1), replacement)) - offset += j2 - j1 - elif tag == "replace": - edits.append(FileEdit(filepath, Range.from_indices( - original, i1, i2), replacement)) - offset += (j2 - j1) - (i2 + i1) - else: - raise Exception("Unexpected difflib.SequenceMatcher tag: " + tag) - - return edits - - # The whole usage of watchdog here should only be specific to RealFileSystem, you want to have a different "Observer" class for VirtualFileSystem, which would depend on being sent notifications class CopyCodebaseEventHandler(PatternMatchingEventHandler): def __init__(self, ignore_directories: List[str], ignore_patterns: List[str], autopilot: Autopilot, orig_root: str, copy_root: str, filesystem: FileSystem): diff --git a/continuedev/src/continuedev/models/filesystem_edit.py b/continuedev/src/continuedev/models/filesystem_edit.py index 8e74b819..b06ca2b3 100644 --- a/continuedev/src/continuedev/models/filesystem_edit.py +++ b/continuedev/src/continuedev/models/filesystem_edit.py @@ -30,8 +30,8 @@ class FileEdit(AtomicFileSystemEdit): return FileEdit(map_path(self.filepath, orig_root, copy_root), self.range, self.replacement) @staticmethod - def from_deletion(filepath: str, start: Position, end: Position) -> "FileEdit": - return FileEdit(filepath, Range(start, end), "") + def from_deletion(filepath: str, range: Range) -> "FileEdit": + return FileEdit(filepath=filepath, range=range, replacement="") @staticmethod def from_insertion(filepath: str, position: Position, content: str) -> "FileEdit": diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py index 80065c24..56e49223 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -7,6 +7,18 @@ from .core.core import MessageStep class SimpleChatStep(Step): user_input: str + name: str = "Chat" async def run(self, sdk: ContinueSDK): +<<<<<<< Updated upstream self.description = sdk.models.gpt35.complete(self.user_input, with_history=await sdk.get_chat_context()) +======= + # TODO: With history + self.description = "" + for chunk in sdk.models.gpt35.stream_chat([{"role": "user", "content": self.user_input}]): + self.description += chunk + await sdk.update_ui() + + self.name = sdk.models.gpt35.complete( + f"Write a short title for the following chat message: {self.description}").strip() +>>>>>>> Stashed changes diff --git a/continuedev/src/continuedev/steps/react.py b/continuedev/src/continuedev/steps/react.py index 6b6024ce..d98b41c6 100644 --- a/continuedev/src/continuedev/steps/react.py +++ b/continuedev/src/continuedev/steps/react.py @@ -1,5 +1,9 @@ from textwrap import dedent +<<<<<<< Updated upstream from typing import List, Union +======= +from typing import List, Tuple +>>>>>>> Stashed changes from ..core.main import Step from ..core.sdk import ContinueSDK from .core.core import MessageStep @@ -7,31 +11,42 @@ from .core.core import MessageStep class NLDecisionStep(Step): user_input: str +<<<<<<< Updated upstream steps: List[Step] hide: bool = True default_step: Union[Step, None] = None +======= + steps: List[Tuple[Step, str]] + + hide: bool = True +>>>>>>> Stashed changes async def run(self, sdk: ContinueSDK): step_descriptions = "\n".join([ - f"- {step.name}: {step.description}" + f"- {step[0].name}: {step[1]}" for step in self.steps ]) prompt = dedent(f"""\ - The following steps are available, in the format "- [step name]: [step description]": - {step_descriptions} - - The user gave the following input: - - {self.user_input} - - Select the step which should be taken next. Say only the name of the selected step:""") + The following steps are available, in the format "- [step name]: [step description]": + {step_descriptions} + + The user gave the following input: + + {self.user_input} + + Select the step which should be taken next to satisfy the user input. Say only the name of the selected step. You must choose one:""") resp = sdk.models.gpt35.complete(prompt).lower() step_to_run = None for step in self.steps: +<<<<<<< Updated upstream if step.name.lower() in resp: step_to_run = step +======= + if step[0].name.lower() in resp: + step_to_run = step[0] +>>>>>>> Stashed changes step_to_run = step_to_run or self.default_step or self.steps[0] diff --git a/extension/package.json b/extension/package.json index 1d2fd995..8ee8cb4c 100644 --- a/extension/package.json +++ b/extension/package.json @@ -54,93 +54,13 @@ } }, "commands": [ - { - "command": "continue.writeDocstring", - "category": "Continue", - "title": "Write a docstring for the current function" - }, { "command": "continue.openContinueGUI", "category": "Continue", "title": "Open Continue GUI" - }, - { - "command": "continue.askQuestionFromInput", - "Category": "Continue", - "title": "Ask a question from input box" - }, - { - "command": "continue.openCapturedTerminal", - "Category": "Continue", - "title": "Open Captured Terminal" - }, - { - "command": "continue.askQuestion", - "Category": "Continue", - "title": "Ask a question from webview" - }, - { - "command": "continue.createTerminal", - "category": "Continue", - "title": "Create Terminal" - }, - { - "command": "continue.debugTest", - "category": "Continue", - "title": "Debug Test" - }, - { - "command": "continue.suggestionDown", - "category": "Continue", - "title": "Suggestion Down" - }, - { - "command": "continue.suggestionUp", - "category": "Continue", - "title": "Suggestion Up" - }, - { - "command": "continue.acceptSuggestion", - "category": "Continue", - "title": "Accept Suggestion" - }, - { - "command": "continue.rejectSuggestion", - "category": "Continue", - "title": "Reject Suggestion" - }, - { - "command": "continue.writeUnitTest", - "title": "Write Unit Test", - "category": "Continue" - }, - { - "command": "continue.findSuspiciousCode", - "title": "Find Suspicious Code", - "category": "Continue" - }, - { - "command": "continue.focusContinueInput", - "title": "Focus Continue Input", - "category": "Continue" } ], "keybindings": [ - { - "command": "continue.suggestionDown", - "mac": "shift+ctrl+down", - "key": "shift+ctrl+down" - }, - { - "command": "continue.suggestionUp", - "mac": "shift+ctrl+up", - "key": "shift+ctrl+up" - }, - { - "command": "continue.acceptSuggestion", - "mac": "shift+ctrl+enter", - "key": "shift+ctrl+enter" - }, { "command": "continue.focusContinueInput", "mac": "cmd+k", diff --git a/extension/react-app/src/components/CodeBlock.tsx b/extension/react-app/src/components/CodeBlock.tsx index e0336554..eedae3fb 100644 --- a/extension/react-app/src/components/CodeBlock.tsx +++ b/extension/react-app/src/components/CodeBlock.tsx @@ -11,6 +11,7 @@ const StyledPre = styled.pre` border: 1px solid gray; border-radius: ${defaultBorderRadius}; background-color: ${vscBackground}; + padding: 8px; `; const StyledCode = styled.code` diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 8ea54325..fb0143b5 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -84,6 +84,15 @@ const OnHoverDiv = styled.div` animation: ${appear} 0.3s ease-in-out; `; +const MarkdownPre = styled.pre` + background-color: ${secondaryDark}; + padding: 10px; + border-radius: ${defaultBorderRadius}; + border: 0.5px solid white; +`; + +const MarkdownCode = styled.code``; + function StepContainer(props: StepContainerProps) { const [open, setOpen] = useState( typeof props.open === "undefined" ? true : props.open @@ -182,7 +191,19 @@ function StepContainer(props: StepContainerProps) { {props.historyNode.observation.error as string} ) : ( - + { + return ( + + ); + }, + }} + > {props.historyNode.step.description as any} )} diff --git a/extension/react-app/src/tabs/chat/MessageDiv.tsx b/extension/react-app/src/tabs/chat/MessageDiv.tsx index 1d7bb5f5..3543dd93 100644 --- a/extension/react-app/src/tabs/chat/MessageDiv.tsx +++ b/extension/react-app/src/tabs/chat/MessageDiv.tsx @@ -58,7 +58,9 @@ function MessageDiv(props: ChatMessage) { }, [richContent, isStreaming]); useEffect(() => { - setRichContent([{props.content}]); + setRichContent([ + , + ]); }, [props.content]); return ( diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 5c75579b..9f7e651f 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -41,7 +41,7 @@ function GUI(props: GUIProps) { // name: "Waiting for user input", // cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py", // description: - // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`", + // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py` and ```\nprint(sum(first, second))\n```\n- Testing\n- Testing 2\n- Testing 3", // }, // observation: { // title: "ERROR FOUND", @@ -92,7 +92,7 @@ function GUI(props: GUIProps) { // prompt: // "I ran into this problem with my Python code:\n\n Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in \n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'\n\n Below are the files that might need to be fixed:\n\n {code}\n\n This is what the code should be in order to avoid the problem:\n", // description: - // "Editing files: /Users/natesesti/Desktop/continue/extension/examples/python/main.py", + // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py` and\n```python\nprint(sum(first, second))\n```\n- Testing\n- Testing 2\n- Testing 3", // }, // output: [ // null, @@ -154,22 +154,30 @@ function GUI(props: GUIProps) { // output: [null, null], // }, // ], - // current_index: 0, + // current_index: 3, // } as any); const topGuiDivRef = useRef(null); const client = useContinueGUIProtocol(); + const [scrollTimeout, setScrollTimeout] = useState( + null + ); const scrollToBottom = useCallback(() => { + if (scrollTimeout) { + clearTimeout(scrollTimeout); + } + // Debounced smooth scroll to bottom of screen if (topGuiDivRef.current) { - setTimeout(() => { + const timeout = setTimeout(() => { window.scrollTo({ top: window.outerHeight, behavior: "smooth", }); - }, 100); + }, 200); + setScrollTimeout(timeout); } - }, [topGuiDivRef.current]); + }, [topGuiDivRef.current, scrollTimeout]); useEffect(() => { console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate); diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index bbaf5f08..c395ae0e 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -13,6 +13,7 @@ import { FileEditWithFullContents } from "../schema/FileEditWithFullContents"; import fs = require("fs"); import { WebsocketMessenger } from "./util/messenger"; import { CapturedTerminal } from "./terminal/terminalEmulator"; +import { decorationManager } from "./decorations"; class IdeProtocolClient { private messenger: WebsocketMessenger | null = null; @@ -281,8 +282,36 @@ class IdeProtocolClient { edit.range.start.line, edit.range.start.character, edit.range.end.line, - edit.range.end.character + 1 + edit.range.end.character ); + const decorationKey = + edit.replacement === "" + ? { + editorUri: editor.document.uri.fsPath, + options: { + range: new vscode.Range( + new vscode.Position(range.start.line, 0), + new vscode.Position(range.end.line + 1, 0) + ), + // after: { + // contentText: "Removed", + // }, + }, + decorationType: vscode.window.createTextEditorDecorationType({ + backgroundColor: "rgba(255, 0, 0, 0.2)", + }), + } + : { + editorUri: editor.document.uri.fsPath, + options: { + range, + }, + decorationType: vscode.window.createTextEditorDecorationType({ + backgroundColor: "rgba(66, 105, 55, 1.0)", + isWholeLine: true, + }), + }; + decorationManager.addDecoration(decorationKey); editor.edit((editBuilder) => { this._makingEdit += 2; // editBuilder.replace takes 2 edits: delete and insert editBuilder.replace(range, edit.replacement); diff --git a/extension/src/decorations.ts b/extension/src/decorations.ts index 456f0c10..d2c94135 100644 --- a/extension/src/decorations.ts +++ b/extension/src/decorations.ts @@ -94,15 +94,22 @@ class DecorationManager { decorationTypes = new Map(); decorationTypes.set(key.decorationType, [key.options]); this.editorToDecorations.set(key.editorUri, decorationTypes); - } - - const decorations = decorationTypes.get(key.decorationType); - if (!decorations) { - decorationTypes.set(key.decorationType, [key.options]); } else { - decorations.push(key.options); + const decorations = decorationTypes.get(key.decorationType); + if (!decorations) { + decorationTypes.set(key.decorationType, [key.options]); + } else { + decorations.push(key.options); + } } + this.rerenderDecorations(key.editorUri, key.decorationType); + + vscode.window.onDidChangeTextEditorSelection((event) => { + if (event.textEditor.document.fileName === key.editorUri) { + this.deleteAllDecorations(key.editorUri); + } + }); } deleteDecoration(key: DecorationKey) { -- cgit v1.2.3-70-g09d2 From af350f5e70f20d14c361684e361b1e64e5e0b2c3 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 12 Jun 2023 12:21:26 -0700 Subject: cleaner highlighting (from server side) --- .../src/continuedev/server/session_manager.py | 1 + continuedev/src/continuedev/steps/core/core.py | 45 +++++++++++++++++++--- continuedev/src/continuedev/steps/main.py | 28 +++++++++++--- extension/src/continueIdeClient.ts | 31 +-------------- 4 files changed, 65 insertions(+), 40 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py index 0dbfaf38..ebea08a5 100644 --- a/continuedev/src/continuedev/server/session_manager.py +++ b/continuedev/src/continuedev/server/session_manager.py @@ -28,6 +28,7 @@ class DemoAutopilot(Autopilot): cumulative_edit_string = "" def handle_manual_edits(self, edits: List[FileEditWithFullContents]): + return for edit in edits: self.cumulative_edit_string += edit.fileEdit.replacement self._manual_edits_buffer.append(edit) diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 57689f19..4288ffd2 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -3,8 +3,10 @@ import os import subprocess from textwrap import dedent from typing import Coroutine, List, Union -from ...libs.llm.prompt_utils import MarkdownStyleEncoderDecoder +from ...models.main import Range +from ...libs.util.calculate_diff import calculate_diff2, apply_edit_to_str +from ...libs.llm.prompt_utils import MarkdownStyleEncoderDecoder from ...models.filesystem_edit import EditDiff, FileEdit, FileEditWithFullContents, FileSystemEdit from ...models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents from ...core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation @@ -149,7 +151,11 @@ class Gpt35EditCodeStep(Step): _prompt_and_completion: str = "" async def describe(self, models: Models) -> Coroutine[str, None, None]: - return models.gpt35.complete(f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points:") + description = models.gpt35.complete( + f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points. Be concise and only mention changes made to the commit before, not prefix or suffix:") + self.name = models.gpt35.complete( + f"Write a short title for this description: {description}") + return description async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: rif_with_contents = [] @@ -174,11 +180,40 @@ class Gpt35EditCodeStep(Step): self._prompt_and_completion += prompt + completion - await sdk.ide.applyFileSystemEdit( - FileEdit(filepath=rif.filepath, range=rif.range, replacement=completion)) - await sdk.ide.saveFile(rif.filepath) + # Calculate diff, open file, apply edits, and highlight changed lines + edits = calculate_diff2( + rif.filepath, rif.contents, completion.removesuffix("\n")) + await sdk.ide.setFileOpen(rif.filepath) + lines_to_highlight = set() + for edit in edits: + edit.range.start.line += rif.range.start.line + edit.range.start.character += rif.range.start.character + edit.range.end.line += rif.range.start.line + edit.range.end.character += rif.range.start.character if edit.range.end.line == 0 else 0 + + for line in range(edit.range.start.line, edit.range.end.line + 1): + lines_to_highlight.add(line) + + await sdk.ide.applyFileSystemEdit(edit) + + current_start = None + last_line = None + for line in sorted(list(lines_to_highlight)): + if current_start is None: + current_start = line + elif line != last_line + 1: + await sdk.ide.highlightCode(RangeInFile(filepath=edit.filepath, range=Range.from_shorthand(current_start, 0, last_line, 0))) + current_start = line + + last_line = line + + if current_start is not None: + await sdk.ide.highlightCode(RangeInFile(filepath=edit.filepath, range=Range.from_shorthand(current_start, 0, last_line, 0))) + + await sdk.ide.saveFile(rif.filepath) + class EditFileStep(Step): filepath: str diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py index 24335b4f..9634c726 100644 --- a/continuedev/src/continuedev/steps/main.py +++ b/continuedev/src/continuedev/steps/main.py @@ -16,6 +16,7 @@ from ..core.sdk import ContinueSDK, Models from ..core.observation import Observation import subprocess from .core.core import Gpt35EditCodeStep +from ..libs.util.calculate_diff import calculate_diff2 class SetupContinueWorkspaceStep(Step): @@ -216,7 +217,8 @@ class StarCoderEditHighlightedCodeStep(Step): async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: range_in_files = await sdk.ide.getHighlightedCode() - if len(range_in_files) == 0: + found_highlighted_code = len(range_in_files) > 0 + if not found_highlighted_code: # Get the full contents of all open files files = await sdk.ide.getOpenFiles() contents = {} @@ -239,15 +241,29 @@ class StarCoderEditHighlightedCodeStep(Step): for rif in rif_with_contents: prompt = self._prompt.format( code=rif.contents, user_request=self.user_input) - completion = str(sdk.models.starcoder.complete(prompt)) + + if found_highlighted_code: + full_file_contents = await sdk.ide.readFile(rif.filepath) + segs = full_file_contents.split(rif.contents) + prompt = f"{segs[0]}{segs[1]}" + prompt + + completion = str((await sdk.models.starcoder()).complete(prompt)) eot_token = "<|endoftext|>" - if completion.endswith(eot_token): - completion = completion[:completion.rindex(eot_token)] + completion = completion.removesuffix(eot_token) + + if found_highlighted_code: + rif.contents = segs[0] + rif.contents + segs[1] + completion = segs[0] + completion + segs[1] self._prompt_and_completion += prompt + completion - await sdk.ide.applyFileSystemEdit( - FileEdit(filepath=rif.filepath, range=rif.range, replacement=completion)) + edits = calculate_diff2( + rif.filepath, rif.contents, completion.removesuffix("\n")) + for edit in edits: + await sdk.ide.applyFileSystemEdit(edit) + + # await sdk.ide.applyFileSystemEdit( + # FileEdit(filepath=rif.filepath, range=rif.range, replacement=completion)) await sdk.ide.saveFile(rif.filepath) await sdk.ide.setFileOpen(rif.filepath) diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index c395ae0e..035778a5 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -278,40 +278,13 @@ class IdeProtocolClient { undefined, vscode.ViewColumn.One ).then((editor) => { - let range = new vscode.Range( + const range = new vscode.Range( edit.range.start.line, edit.range.start.character, edit.range.end.line, edit.range.end.character ); - const decorationKey = - edit.replacement === "" - ? { - editorUri: editor.document.uri.fsPath, - options: { - range: new vscode.Range( - new vscode.Position(range.start.line, 0), - new vscode.Position(range.end.line + 1, 0) - ), - // after: { - // contentText: "Removed", - // }, - }, - decorationType: vscode.window.createTextEditorDecorationType({ - backgroundColor: "rgba(255, 0, 0, 0.2)", - }), - } - : { - editorUri: editor.document.uri.fsPath, - options: { - range, - }, - decorationType: vscode.window.createTextEditorDecorationType({ - backgroundColor: "rgba(66, 105, 55, 1.0)", - isWholeLine: true, - }), - }; - decorationManager.addDecoration(decorationKey); + editor.edit((editBuilder) => { this._makingEdit += 2; // editBuilder.replace takes 2 edits: delete and insert editBuilder.replace(range, edit.replacement); -- cgit v1.2.3-70-g09d2 From ffca2e64cdaf7236d678c38dd4d496e9923ebf7b Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 12 Jun 2023 14:54:51 -0700 Subject: fixing feedback on dlt recipes --- continuedev/src/continuedev/core/main.py | 2 +- .../src/continuedev/recipes/AddTransformRecipe/main.py | 2 +- .../src/continuedev/recipes/AddTransformRecipe/steps.py | 15 +++++---------- continuedev/src/continuedev/steps/core/core.py | 2 +- extension/react-app/src/components/StepContainer.tsx | 2 +- extension/src/continueIdeClient.ts | 2 +- 6 files changed, 10 insertions(+), 15 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 3053e5a1..81aaaf2e 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -147,7 +147,7 @@ class Step(ContinueBaseModel): if self.description is not None: d["description"] = self.description else: - d["description"] = "`Description loading...`" + d["description"] = "`Step in progress...`" return d @validator("name", pre=True, always=True) diff --git a/continuedev/src/continuedev/recipes/AddTransformRecipe/main.py b/continuedev/src/continuedev/recipes/AddTransformRecipe/main.py index e9a998e3..fdd343f5 100644 --- a/continuedev/src/continuedev/recipes/AddTransformRecipe/main.py +++ b/continuedev/src/continuedev/recipes/AddTransformRecipe/main.py @@ -19,7 +19,7 @@ class AddTransformRecipe(Step): - Run the pipeline and view the transformed data in a Streamlit app"""), name="Add transformation to a dlt pipeline") >> SetUpChessPipelineStep() >> WaitForUserInputStep( - prompt="How do you want to transform the Chess.com API data before loading it? For example, you could use the `python-chess` library to decode the moves or filter out certain games") + prompt="How do you want to transform the Chess.com API data before loading it? For example, you could filter out games that ended in a draw.") ) await sdk.run_step( AddTransformStep( diff --git a/continuedev/src/continuedev/recipes/AddTransformRecipe/steps.py b/continuedev/src/continuedev/recipes/AddTransformRecipe/steps.py index 7bb0fc23..f042424c 100644 --- a/continuedev/src/continuedev/recipes/AddTransformRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/AddTransformRecipe/steps.py @@ -31,12 +31,11 @@ class SetUpChessPipelineStep(Step): 'pip install -r requirements.txt', 'pip install pandas streamlit' # Needed for the pipeline show step later ], name="Set up Python environment", description=dedent(f"""\ - Running the following commands: - - `python3 -m venv env`: Create a Python virtual environment - - `source env/bin/activate`: Activate the virtual environment - - `pip install dlt`: Install dlt - - `dlt init chess duckdb`: Create a new dlt pipeline called "chess" that loads data into a local DuckDB instance - - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline""")) + - Create a Python virtual environment: `python3 -m venv env` + - Activate the virtual environment: `source env/bin/activate` + - Install dlt: `pip install dlt` + - Create a new dlt pipeline called "chess" that loads data into a local DuckDB instance: `dlt init chess duckdb` + - Install the Python dependencies for the pipeline: `pip install -r requirements.txt`""")) class AddTransformStep(Step): @@ -58,10 +57,6 @@ class AddTransformStep(Step): # Open the file and highlight the function to be edited await sdk.ide.setFileOpen(abs_filepath) - await sdk.ide.highlightCode(range_in_file=RangeInFile( - filepath=abs_filepath, - range=Range.from_shorthand(47, 0, 51, 0) - )) with open(os.path.join(os.path.dirname(__file__), 'dlt_transform_docs.md')) as f: dlt_transform_docs = f.read() diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 53df65cc..392339c6 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -88,7 +88,7 @@ class ShellCommandsStep(Step): This is a brief summary of the error followed by a suggestion on how it can be fixed:"""), with_history=sdk.chat_context) sdk.raise_exception( - title="Error while running query", message=output, with_step=MessageStep(name=f"Suggestion to solve error {AI_ASSISTED_STRING}", message=suggestion) + title="Error while running query", message=output, with_step=MessageStep(name=f"Suggestion to solve error {AI_ASSISTED_STRING}", message=f"{suggestion}\n\nYou can click the retry button on the failed step to try again.") ) return TextObservation(text=output) diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 8ea54325..3408053b 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -169,7 +169,7 @@ function StepContainer(props: StepContainerProps) {
+              
                 Step Details:
                 
{JSON.stringify(props.historyNode.step, null, 2)} diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index bbaf5f08..42671ade 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -176,7 +176,7 @@ class IdeProtocolClient { // If not, ask user for secret while (typeof secret === "undefined" || secret === "") { secret = await vscode.window.showInputBox({ - prompt: `Enter secret for ${key}`, + prompt: `Enter secret for ${key}. You can edit this later in the Continue VS Code settings.`, password: true, }); } -- cgit v1.2.3-70-g09d2 From 1e3acec89e9d91758a719b5540b0e11d811e725d Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 13 Jun 2023 21:35:38 -0700 Subject: fixed diff application and highlighting --- continuedev/src/continuedev/libs/llm/openai.py | 2 +- continuedev/src/continuedev/models/main.py | 14 +++++- continuedev/src/continuedev/steps/core/core.py | 70 +++++++++++++------------- extension/src/continueIdeClient.ts | 22 +++++--- 4 files changed, 64 insertions(+), 44 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/libs/llm/openai.py b/continuedev/src/continuedev/libs/llm/openai.py index 39c0b69f..6f620ba0 100644 --- a/continuedev/src/continuedev/libs/llm/openai.py +++ b/continuedev/src/continuedev/libs/llm/openai.py @@ -34,7 +34,7 @@ class OpenAI(LLM): return tiktoken.encoding_for_model(self.default_model) def count_tokens(self, text: str): - return len(self.__encoding_for_model.encode(text)) + return len(self.__encoding_for_model.encode(text, disallowed_special=())) def __prune_chat_history(self, chat_history: List[ChatMessage], max_tokens: int, tokens_for_completion: int): tokens = tokens_for_completion diff --git a/continuedev/src/continuedev/models/main.py b/continuedev/src/continuedev/models/main.py index 02c44aae..fceba284 100644 --- a/continuedev/src/continuedev/models/main.py +++ b/continuedev/src/continuedev/models/main.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import List, Union +from typing import List, Union, Tuple from pydantic import BaseModel, root_validator from functools import total_ordering @@ -61,6 +61,18 @@ class Range(BaseModel): def is_empty(self) -> bool: return self.start == self.end + def indices_in_string(self, string: str) -> Tuple[int, int]: + """Get the start and end indicees of this range in the string""" + lines = string.splitlines() + if len(lines) == 0: + return (0, 0) + + start_index = sum( + [len(line) + 1 for line in lines[:self.start.line]]) + self.start.character + end_index = sum( + [len(line) + 1 for line in lines[:self.end.line]]) + self.end.character + return (start_index, end_index) + def overlaps_with(self, other: "Range") -> bool: return not (self.end < other.start or self.start > other.end) diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index ff498b9b..f1fb229e 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -5,12 +5,13 @@ from textwrap import dedent from typing import Coroutine, List, Union from ...models.main import Range -from ...libs.util.calculate_diff import calculate_diff2, apply_edit_to_str +from ...libs.util.calculate_diff import calculate_diff2, apply_edit_to_str, line_by_line_diff from ...libs.llm.prompt_utils import MarkdownStyleEncoderDecoder from ...models.filesystem_edit import EditDiff, FileEdit, FileEditWithFullContents, FileSystemEdit from ...models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents from ...core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation from ...core.main import Step, SequentialStep +import difflib class ContinueSDK: @@ -135,7 +136,7 @@ class DefaultModelEditCodeStep(Step): return a + b <|endoftext|> - Now complete the real thing. Do NOT rewrite the prefix or suffix. + Now complete the real thing. Do NOT rewrite the prefix or suffix. You are only to write the code that goes in "commit_after". {file_prefix} @@ -159,6 +160,7 @@ class DefaultModelEditCodeStep(Step): async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: self.name = self.user_input + await sdk.update_ui() rif_with_contents = [] for range_in_file in self.range_in_files: @@ -171,48 +173,46 @@ class DefaultModelEditCodeStep(Step): rif_dict[rif.filepath] = rif.contents for rif in rif_with_contents: + await sdk.ide.setFileOpen(rif.filepath) + full_file_contents = await sdk.ide.readFile(rif.filepath) - segs = full_file_contents.split(rif.contents) + start_index, end_index = rif.range.indices_in_string( + full_file_contents) + segs = [full_file_contents[:start_index], + full_file_contents[end_index:]] + prompt = self._prompt.format( code=rif.contents, user_request=self.user_input, file_prefix=segs[0], file_suffix=segs[1]) - completion = str(sdk.models.default.complete(prompt)) + completion = str(sdk.models.default.complete(prompt, with_history=await sdk.get_chat_context())) eot_token = "<|endoftext|>" completion = completion.removesuffix(eot_token) self._prompt_and_completion += prompt + completion - # Calculate diff, open file, apply edits, and highlight changed lines - edits = calculate_diff2( - rif.filepath, rif.contents, completion.removesuffix("\n")) - - await sdk.ide.setFileOpen(rif.filepath) - - lines_to_highlight = set() - for edit in edits: - edit.range.start.line += rif.range.start.line - edit.range.start.character += rif.range.start.character - edit.range.end.line += rif.range.start.line - edit.range.end.character += rif.range.start.character if edit.range.end.line == 0 else 0 - - for line in range(edit.range.start.line, edit.range.end.line + 1 + len(edit.replacement.splitlines()) - (edit.range.end.line - edit.range.start.line + 1)): - lines_to_highlight.add(line) - - await sdk.ide.applyFileSystemEdit(edit) - - current_start = None - last_line = None - for line in sorted(list(lines_to_highlight)): - if current_start is None: - current_start = line - elif line != last_line + 1: - await sdk.ide.highlightCode(RangeInFile(filepath=edit.filepath, range=Range.from_shorthand(current_start, 0, last_line, 0))) - current_start = line - - last_line = line - - if current_start is not None: - await sdk.ide.highlightCode(RangeInFile(filepath=edit.filepath, range=Range.from_shorthand(current_start, 0, last_line, 0))) + diff = list(difflib.ndiff(rif.contents.splitlines( + keepends=True), completion.splitlines(keepends=True))) + + lines_to_highlight = [] + index = 0 + for line in diff: + if line.startswith("-"): + pass + elif line.startswith("+"): + lines_to_highlight.append(index + rif.range.start.line) + index += 1 + elif line.startswith(" "): + index += 1 + + + await sdk.ide.applyFileSystemEdit(FileEdit( + filepath=rif.filepath, + range=rif.range, + replacement=completion + )) + + for line in lines_to_highlight: + await sdk.ide.highlightCode(RangeInFile(filepath=rif.filepath, range=Range.from_shorthand(line, 0, line, 0))) await sdk.ide.saveFile(rif.filepath) diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 92af6b10..782219dc 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -140,12 +140,20 @@ class IdeProtocolClient { vscode.ViewColumn.One ); if (editor) { - editor.setDecorations( - vscode.window.createTextEditorDecorationType({ - backgroundColor: color, - isWholeLine: true, - }), - [range] + const decorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: color, + isWholeLine: true, + }); + editor.setDecorations(decorationType, [range]); + + // Listen for changes to cursor position + const cursorDisposable = vscode.window.onDidChangeTextEditorSelection( + (event) => { + if (event.textEditor.document.uri.fsPath === rangeInFile.filepath) { + cursorDisposable.dispose(); + editor.setDecorations(decorationType, []); + } + } ); } } @@ -282,7 +290,7 @@ class IdeProtocolClient { edit.range.start.line, edit.range.start.character, edit.range.end.line, - edit.range.end.character + edit.range.end.character + 1 ); editor.edit((editBuilder) => { -- cgit v1.2.3-70-g09d2 From 9100f116d0c28b231c0b746f60b8d59e00546456 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 13 Jun 2023 23:45:49 -0700 Subject: Small fixes --- .../src/continuedev/recipes/CreatePipelineRecipe/steps.py | 2 +- continuedev/src/continuedev/steps/feedback.py | 6 ++++++ extension/react-app/src/components/TextDialog.tsx | 10 +++++++++- extension/src/continueIdeClient.ts | 6 +++++- 4 files changed, 21 insertions(+), 3 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index 88e27d2a..096b41c6 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -50,7 +50,7 @@ class SetupPipelineStep(Step): # editing the resource function to call the requested API resource_function_range = Range.from_shorthand(15, 0, 29, 0) - await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=resource_function_range)) + await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=resource_function_range), "#ffa50033") # sdk.set_loading_message("Writing code to call the API...") await sdk.edit_file( diff --git a/continuedev/src/continuedev/steps/feedback.py b/continuedev/src/continuedev/steps/feedback.py index bf119ab0..96081756 100644 --- a/continuedev/src/continuedev/steps/feedback.py +++ b/continuedev/src/continuedev/steps/feedback.py @@ -1,3 +1,5 @@ +from typing import Coroutine +from ..core.main import Models from ..core.main import Step from ..core.sdk import ContinueSDK from ..libs.util.telemetry import capture_event @@ -5,6 +7,10 @@ from ..libs.util.telemetry import capture_event class FeedbackStep(Step): user_input: str + name = "Thanks for your feedback!" + + async def describe(self, models: Models): + return f"`{self.user_input}`\n\nWe'll see your feedback and make improvements as soon as possible. If you'd like to directly email us, you can send an email to [nate@continue.dev](mailto:nate@continue.dev?subject=Feedback%20On%20Continue)." async def run(self, sdk: ContinueSDK): capture_event("feedback", {"feedback": self.user_input}) diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx index 6b335e00..167e12cf 100644 --- a/extension/react-app/src/components/TextDialog.tsx +++ b/extension/react-app/src/components/TextDialog.tsx @@ -47,7 +47,15 @@ const TextDialog = (props: {

Thanks for your feedback. We'll get back to you soon!

- +
); diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 782219dc..3308068a 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -121,7 +121,11 @@ class IdeProtocolClient { } } getWorkspaceDirectory() { - return vscode.workspace.workspaceFolders![0].uri.fsPath; + if (!vscode.workspace.workspaceFolders) { + // Return the home directory + return process.env.HOME || process.env.USERPROFILE || "/"; + } + return vscode.workspace.workspaceFolders[0].uri.fsPath; } // ------------------------------------ // -- cgit v1.2.3-70-g09d2 From 7b08dfd5af073dfe5b12f52d3427ae03c4313ea6 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 14 Jun 2023 14:34:42 -0700 Subject: feedback with unique id, patch --- continuedev/src/continuedev/core/autopilot.py | 4 +- continuedev/src/continuedev/libs/util/telemetry.py | 4 +- continuedev/src/continuedev/server/ide.py | 30 +++- continuedev/src/continuedev/server/ide_protocol.py | 4 + continuedev/src/continuedev/steps/feedback.py | 5 +- extension/package-lock.json | 4 +- extension/package.json | 2 +- extension/react-app/src/tabs/gui.tsx | 194 +++++++++++---------- .../scripts/continuedev-0.1.1-py3-none-any.whl | Bin 0 -> 81413 bytes extension/src/continueIdeClient.ts | 9 + 10 files changed, 153 insertions(+), 103 deletions(-) create mode 100644 extension/scripts/continuedev-0.1.1-py3-none-any.whl (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 0874bbc5..703a73af 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -102,8 +102,8 @@ class Autopilot(ContinueBaseModel): await self.update_subscribers() async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: - capture_event( - 'step run', {'step_name': step.name, 'params': step.dict()}) + capture_event(self.continue_sdk.ide.unique_id, 'step run', { + 'step_name': step.name, 'params': step.dict()}) if not is_future_step: # Check manual edits buffer, clear out if needed by creating a ManualEditStep diff --git a/continuedev/src/continuedev/libs/util/telemetry.py b/continuedev/src/continuedev/libs/util/telemetry.py index d6345c25..03ec93c6 100644 --- a/continuedev/src/continuedev/libs/util/telemetry.py +++ b/continuedev/src/continuedev/libs/util/telemetry.py @@ -6,7 +6,7 @@ posthog = Posthog('phc_JS6XFROuNbhJtVCEdTSYk6gl5ArRrTNMpCcguAXlSPs', host='https://app.posthog.com') -def capture_event(event_name, event_properties): +def capture_event(unique_id: str, event_name, event_properties): config = load_config('.continue/config.json') if config.allow_anonymous_telemetry: - posthog.capture("not distinct :(", event_name, event_properties) + posthog.capture(unique_id, event_name, event_properties) diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index f4ea1071..c53149d8 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -81,9 +81,29 @@ class RunCommandResponse(BaseModel): output: str +class UniqueIdResponse(BaseModel): + uniqueId: str + + T = TypeVar("T", bound=BaseModel) +class cached_property_no_none: + def __init__(self, func): + self.func = func + + def __get__(self, instance, owner): + if instance is None: + return self + value = self.func(instance) + if value is not None: + setattr(instance, self.func.__name__, value) + return value + + def __repr__(self): + return f"" + + class IdeProtocolServer(AbstractIdeProtocolServer): websocket: WebSocket session_manager: SessionManager @@ -115,7 +135,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): fileEdits = list( map(lambda d: FileEditWithFullContents.parse_obj(d), data["fileEdits"])) self.onFileEdits(fileEdits) - elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret", "runCommand"]: + elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret", "runCommand", "uniqueId"]: self.sub_queue.post(message_type, data) else: raise ValueError("Unknown message type", message_type) @@ -200,10 +220,18 @@ class IdeProtocolServer(AbstractIdeProtocolServer): resp = await self._send_and_receive_json({}, WorkspaceDirectoryResponse, "workspaceDirectory") return resp.workspaceDirectory + async def get_unique_id(self) -> str: + resp = await self._send_and_receive_json({}, UniqueIdResponse, "uniqueId") + return resp.uniqueId + @cached_property def workspace_directory(self) -> str: return asyncio.run(self.getWorkspaceDirectory()) + @cached_property_no_none + def unique_id(self) -> str: + return asyncio.run(self.get_unique_id()) + async def getHighlightedCode(self) -> List[RangeInFile]: resp = await self._send_and_receive_json({}, HighlightedCodeResponse, "highlightedCode") return resp.highlightedCode diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index a937ad75..1d98f4a1 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -94,3 +94,7 @@ class AbstractIdeProtocolServer(ABC): @abstractproperty def workspace_directory(self) -> str: """Get the workspace directory""" + + @abstractproperty + def unique_id(self) -> str: + """Get a unique ID for this IDE""" diff --git a/continuedev/src/continuedev/steps/feedback.py b/continuedev/src/continuedev/steps/feedback.py index 96081756..6f6a9b15 100644 --- a/continuedev/src/continuedev/steps/feedback.py +++ b/continuedev/src/continuedev/steps/feedback.py @@ -10,7 +10,8 @@ class FeedbackStep(Step): name = "Thanks for your feedback!" async def describe(self, models: Models): - return f"`{self.user_input}`\n\nWe'll see your feedback and make improvements as soon as possible. If you'd like to directly email us, you can send an email to [nate@continue.dev](mailto:nate@continue.dev?subject=Feedback%20On%20Continue)." + return f"`{self.user_input}`\n\nWe'll see your feedback and make improvements as soon as possible. If you'd like to directly email us, you can contact [nate@continue.dev](mailto:nate@continue.dev?subject=Feedback%20On%20Continue)." async def run(self, sdk: ContinueSDK): - capture_event("feedback", {"feedback": self.user_input}) + capture_event(sdk.ide.unique_id, "feedback", + {"feedback": self.user_input}) diff --git a/extension/package-lock.json b/extension/package-lock.json index 6edc38f9..afa8ab2d 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.36", + "version": "0.0.38", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.36", + "version": "0.0.38", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 91d687ab..4b86f4ea 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.36", + "version": "0.0.38", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 55c2b763..70bab352 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -21,6 +21,11 @@ import { import ComboBox from "../components/ComboBox"; import TextDialog from "../components/TextDialog"; +const MainDiv = styled.div` + display: grid; + grid-template-rows: 1fr auto; +`; + let TopGUIDiv = styled.div` display: grid; grid-template-columns: 1fr; @@ -39,10 +44,11 @@ const TopBar = styled.div` display: flex; flex-direction: row; gap: 8px; - justify-content: center; + justify-content: right; padding: 8px; align-items: center; - border-bottom: 0.1px solid gray; + margin-top: 8px; + border-top: 0.1px solid gray; `; interface GUIProps { @@ -294,101 +300,103 @@ function GUI(props: GUIProps) { setShowFeedbackDialog(false); }} > - { - if (e.key === "Enter" && e.ctrlKey) { - onMainTextInput(); - } - }} - > - -
- - Continue Docs - - - - { - // Set dialog open - setShowFeedbackDialog(true); - }} - > - Feedback - - - { - client?.sendClear(); + + { + if (e.key === "Enter" && e.ctrlKey) { + onMainTextInput(); + } + }} + > + {typeof client === "undefined" && ( + <> + +

+ Trying to reconnect with server... +

+ + )} + {history?.timeline.map((node: HistoryNode, index: number) => { + return ( + { + onStepUserInput(input, index); + }} + inFuture={index > history?.current_index} + historyNode={node} + onRefinement={(input: string) => { + client?.sendRefinementInput(input, index); + }} + onReverse={() => { + client?.reverseToIndex(index); + }} + onRetry={() => { + client?.retryAtIndex(index); + setWaitingForSteps(true); + }} + onDelete={() => { + client?.deleteAtIndex(index); + }} + /> + ); + })} + {waitingForSteps && } + +
+ {userInputQueue.map((input) => { + return {input}; + })} +
+ + { + onMainTextInput(); + e.stopPropagation(); + e.preventDefault(); }} - style={{ padding: "3px" }} - > - Clear History - -
- + onInputValueChange={() => {}} + items={availableSlashCommands} + /> + - {typeof client === "undefined" && ( - <> - -

- Trying to reconnect with server... -

- - )} - {history?.timeline.map((node: HistoryNode, index: number) => { - return ( - { - onStepUserInput(input, index); + + + + Continue Docs + + + + { + // Set dialog open + setShowFeedbackDialog(true); }} - inFuture={index > history?.current_index} - historyNode={node} - onRefinement={(input: string) => { - client?.sendRefinementInput(input, index); - }} - onReverse={() => { - client?.reverseToIndex(index); - }} - onRetry={() => { - client?.retryAtIndex(index); - setWaitingForSteps(true); - }} - onDelete={() => { - client?.deleteAtIndex(index); + > + Feedback + + + { + client?.sendClear(); }} - /> - ); - })} - {waitingForSteps && } - -
- {userInputQueue.map((input) => { - return {input}; - })} -
- - { - onMainTextInput(); - e.stopPropagation(); - e.preventDefault(); - }} - onInputValueChange={() => {}} - items={availableSlashCommands} - /> - - + style={{ padding: "3px" }} + > + Clear History + +
+
+ + ); } diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl new file mode 100644 index 00000000..b3e8229a Binary files /dev/null and b/extension/scripts/continuedev-0.1.1-py3-none-any.whl differ diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 3308068a..16941c70 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -77,6 +77,11 @@ class IdeProtocolClient { workspaceDirectory: this.getWorkspaceDirectory(), }); break; + case "uniqueId": + this.messenger?.send("uniqueId", { + uniqueId: this.getUniqueId(), + }); + break; case "getUserSecret": this.messenger?.send("getUserSecret", { value: await this.getUserSecret(data.key), @@ -128,6 +133,10 @@ class IdeProtocolClient { return vscode.workspace.workspaceFolders[0].uri.fsPath; } + getUniqueId() { + return vscode.env.machineId; + } + // ------------------------------------ // // On message handlers -- cgit v1.2.3-70-g09d2 From 0c33de59e472b53ea2a4d9aadde391b5b10343d4 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 15 Jun 2023 08:57:55 -0700 Subject: left panel --- extension/package.json | 30 +++++++++++++++----- extension/src/activation/activate.ts | 55 +++++++++++------------------------- extension/src/continueIdeClient.ts | 28 ++++-------------- extension/src/debugPanel.ts | 34 ++++++++++++++-------- 4 files changed, 67 insertions(+), 80 deletions(-) (limited to 'extension/src') diff --git a/extension/package.json b/extension/package.json index 4b86f4ea..402f37a6 100644 --- a/extension/package.json +++ b/extension/package.json @@ -20,7 +20,19 @@ "vscode": "^1.74.0" }, "categories": [ - "Other" + "Other", + "Education", + "Machine Learning" + ], + "keywords": [ + "openai", + "chatbot", + "chatgpt", + "autocomplete", + "llm", + "ai", + "assistant", + "chat" ], "activationEvents": [ "onStartupFinished", @@ -68,20 +80,24 @@ } ], "menus": { - "view/title": [ + "view/title": [] + }, + "viewsContainers": { + "activitybar": [ { - "command": "continue.openContinueGUI", - "group": "navigation", - "when": "view == continue.continueGUIView" + "id": "continue", + "title": "Continue", + "icon": "media/continue-gradient.png" } ] }, "views": { - "explorer": [ + "continue": [ { "type": "webview", "id": "continue.continueGUIView", - "name": "Continue GUI" + "name": "Continue GUI", + "visibility": "visible" } ] } diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 293ee26c..135a8ec7 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -7,7 +7,7 @@ import * as path from "path"; // import { openCapturedTerminal } from "../terminal/terminalEmulator"; import IdeProtocolClient from "../continueIdeClient"; import { getContinueServerUrl } from "../bridge"; -import { setupDebugPanel } from "../debugPanel"; +import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel"; export let extensionContext: vscode.ExtensionContext | undefined = undefined; @@ -31,44 +31,21 @@ export function activateExtension( context ); - if (showTutorial && false) { - Promise.all([ - vscode.workspace - .openTextDocument( - path.join(getExtensionUri().fsPath, "examples/python/sum.py") - ) - .then((document) => - vscode.window.showTextDocument(document, { - preview: false, - viewColumn: vscode.ViewColumn.One, - }) - ), - - vscode.workspace - .openTextDocument( - path.join(getExtensionUri().fsPath, "examples/python/main.py") - ) - .then((document) => - vscode.window - .showTextDocument(document, { - preview: false, - viewColumn: vscode.ViewColumn.One, - }) - .then((editor) => { - editor.revealRange( - new vscode.Range(0, 0, 0, 0), - vscode.TextEditorRevealType.InCenter - ); - }) - ), - ]).then(() => { - ideProtocolClient?.openGUI(); - }); - } else { - ideProtocolClient.openGUI().then(() => { - // openCapturedTerminal(); - }); - } + // Setup the left panel + (async () => { + const sessionId = await ideProtocolClient.getSessionId(); + const provider = new ContinueGUIWebviewViewProvider(sessionId); + + context.subscriptions.push( + vscode.window.registerWebviewViewProvider( + "continue.continueGUIView", + provider, + { + webviewOptions: { retainContextWhenHidden: true }, + } + ) + ); + })(); extensionContext = context; } diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 16941c70..c879c682 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -17,7 +17,6 @@ import { decorationManager } from "./decorations"; class IdeProtocolClient { private messenger: WebsocketMessenger | null = null; - private panels: Map = new Map(); private readonly context: vscode.ExtensionContext; private _makingEdit = 0; @@ -214,35 +213,18 @@ class IdeProtocolClient { // ------------------------------------ // // Initiate Request - closeGUI(sessionId: string) { - this.panels.get(sessionId)?.dispose(); - this.panels.delete(sessionId); + async openGUI(asRightWebviewPanel: boolean = false) { + // Open the webview panel } - async openGUI() { - console.log("OPENING GUI"); + async getSessionId(): Promise { 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); - - const column = getRightViewColumn(); - const panel = vscode.window.createWebviewPanel( - "continue.debugPanelView", - "Continue", - column, - { - enableScripts: true, - retainContextWhenHidden: true, - } - ); - - // And set its HTML content - panel.webview.html = setupDebugPanel(panel, this.context, sessionId); - - this.panels.set(sessionId, panel); + console.log("New Continue session with ID: ", sessionId); + return sessionId; } acceptRejectSuggestion(accept: boolean, key: SuggestionRanges) { diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 7407faf4..bb98eb46 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -136,7 +136,6 @@ let streamManager = new StreamManager(); export let debugPanelWebview: vscode.Webview | undefined; export function setupDebugPanel( panel: vscode.WebviewPanel | vscode.WebviewView, - context: vscode.ExtensionContext | undefined, sessionId: string ): string { debugPanelWebview = panel.webview; @@ -165,6 +164,11 @@ export function setupDebugPanel( .toString(); } + panel.webview.options = { + enableScripts: true, + localResourceRoots: [vscode.Uri.joinPath(extensionUri, "react-app/dist")], + }; + const nonce = getNonce(); vscode.window.onDidChangeTextEditorSelection((e) => { @@ -488,13 +492,21 @@ export function setupDebugPanel( `; } -// class ContinueGUIWebviewViewProvider implements vscode.WebviewViewProvider { -// public static readonly viewType = "continue.continueGUIView"; -// resolveWebviewView( -// webviewView: vscode.WebviewView, -// context: vscode.WebviewViewResolveContext, -// token: vscode.CancellationToken -// ): void | Thenable { -// setupDebugPanel(webviewView, context, sessionId); -// } -// } +export class ContinueGUIWebviewViewProvider + implements vscode.WebviewViewProvider +{ + public static readonly viewType = "continue.continueGUIView"; + private readonly sessionId: string; + + constructor(sessionId: string) { + this.sessionId = sessionId; + } + + resolveWebviewView( + webviewView: vscode.WebviewView, + _context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ): void | Thenable { + webviewView.webview.html = setupDebugPanel(webviewView, this.sessionId); + } +} -- cgit v1.2.3-70-g09d2 From 9cf110602621f80d0971f698ee40e7d73d0fa2b7 Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Thu, 15 Jun 2023 11:35:17 -0700 Subject: adds /edit and /explain commands --- continuedev/src/continuedev/core/config.py | 28 +++++----------------- continuedev/src/continuedev/core/policy.py | 8 +------ .../continuedev/libs/util/step_name_to_steps.py | 18 ++++---------- extension/package.json | 1 - extension/react-app/src/components/ComboBox.tsx | 2 +- extension/src/README.md | 3 +-- 6 files changed, 14 insertions(+), 46 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py index 8ebdc145..a1c58399 100644 --- a/continuedev/src/continuedev/core/config.py +++ b/continuedev/src/continuedev/core/config.py @@ -27,37 +27,21 @@ class ContinueConfig(BaseModel): # description="Write pytest unit tests for the current file", # step_name="WritePytestsRecipe", # params=??) - - SlashCommand( - name="dlt", - description="Create a dlt pipeline", - step_name="CreatePipelineRecipe", - ), SlashCommand( - name="ddtobq", - description="Adjust a dlt pipeline to load data into BigQuery", - step_name="DDtoBQRecipe", + name="edit", + description="Edit code in the current file or the highlighted code", + step_name="EditHighlightedCodeStep", ), SlashCommand( - name="addtransform", - description="Add transforms to the chess.com API dlt pipeline.", - step_name="AddTransformRecipe", - ), - SlashCommand( - name="deployairflow", - description="Deploy a dlt pipeline to Airflow", - step_name="DeployPipelineAirflowRecipe", + name="explain", + description="Reply to instructions or a question with previous steps as context", + step_name="SimpleChatStep", ), SlashCommand( name="comment", description="Write comments for the current file or highlighted code", step_name="CommentCodeStep", ), - SlashCommand( - name="pytest", - description="Write pytest unit tests for the current file", - step_name="WritePytestsRecipe", - ), SlashCommand( name="feedback", description="Send feedback to improve Continue", diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index 9fcda882..255f598d 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -26,7 +26,7 @@ class DemoPolicy(Policy): # At the very start, run initial Steps spcecified in the config if history.get_current() is None: return ( - MessageStep(name="Welcome to Continue!", message="You can type a question or instructions for a file edit in the text box. If you highlight code, edits will be localized to the highlighted range. Otherwise, the currently open file is taken as context. If you type '/', you can see the list of available slash commands.") >> + MessageStep(name="Welcome to Continue!", message="Type '/' to see the list of available slash commands. If you highlight code, edits and explanations will be localized to the highlighted range. Otherwise, the currently open file is used. In both cases, the code is combined with the previous steps to construct the context.") >> # SetupContinueWorkspaceStep() >> # CreateCodebaseIndexChroma() >> StepsOnStartupStep()) @@ -45,12 +45,6 @@ class DemoPolicy(Policy): params["user_input"] = after_command return get_step_from_name(slash_command.step_name, params) - if "/ask" in user_input: - return AnswerQuestionChroma(question=" ".join(user_input.split(" ")[1:])) - elif "/edit" in user_input: - return EditFileChroma(request=" ".join(user_input.split(" ")[1:])) - elif "/step" in user_input: - return ContinueStepStep(prompt=" ".join(user_input.split(" ")[1:])) # return EditHighlightedCodeStep(user_input=user_input) return NLDecisionStep(user_input=user_input, steps=[ (EditHighlightedCodeStep(user_input=user_input), diff --git a/continuedev/src/continuedev/libs/util/step_name_to_steps.py b/continuedev/src/continuedev/libs/util/step_name_to_steps.py index fd1aab5b..4dd9c430 100644 --- a/continuedev/src/continuedev/libs/util/step_name_to_steps.py +++ b/continuedev/src/continuedev/libs/util/step_name_to_steps.py @@ -2,23 +2,15 @@ from typing import Dict from ...core.main import Step from ...steps.core.core import UserInputStep -from ...recipes.CreatePipelineRecipe.main import CreatePipelineRecipe -from ...recipes.DDtoBQRecipe.main import DDtoBQRecipe -from ...recipes.DeployPipelineAirflowRecipe.main import DeployPipelineAirflowRecipe -from ...recipes.DDtoBQRecipe.main import DDtoBQRecipe -from ...recipes.AddTransformRecipe.main import AddTransformRecipe -from ...recipes.WritePytestsRecipe.main import WritePytestsRecipe +from ...steps.main import EditHighlightedCodeStep +from ...steps.chat import SimpleChatStep from ...steps.comment_code import CommentCodeStep from ...steps.feedback import FeedbackStep step_name_to_step_class = { "UserInputStep": UserInputStep, - "CreatePipelineRecipe": CreatePipelineRecipe, - "DDtoBQRecipe": DDtoBQRecipe, - "DeployPipelineAirflowRecipe": DeployPipelineAirflowRecipe, - "AddTransformRecipe": AddTransformRecipe, - "DDtoBQRecipe": DDtoBQRecipe, - "WritePytestsRecipe": WritePytestsRecipe, + "EditHighlightedCodeStep": EditHighlightedCodeStep, + "SimpleChatStep": SimpleChatStep, "CommentCodeStep": CommentCodeStep, "FeedbackStep": FeedbackStep, } @@ -30,4 +22,4 @@ def get_step_from_name(step_name: str, params: Dict) -> Step: except: print( f"Incorrect parameters for step {step_name}. Parameters provided were: {params}") - raise + raise \ No newline at end of file diff --git a/extension/package.json b/extension/package.json index 13cc92a2..c41c3565 100644 --- a/extension/package.json +++ b/extension/package.json @@ -111,7 +111,6 @@ "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", "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", diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 11d04974..ace0605e 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -105,7 +105,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { { onMainTextInput(); @@ -365,6 +367,14 @@ function GUI(props: GUIProps) { ); diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 77010241..32726c86 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -59,6 +59,9 @@ export function activateExtension( name: "Continue", }); terminal.dispose(); + if (!ideProtocolClient.continueTerminal) { + ideProtocolClient.continueTerminal = capturedTerminal; + } }); // If any terminals are open to start, replace them @@ -77,6 +80,9 @@ export function activateExtension( } ); terminal.dispose(); + if (!ideProtocolClient.continueTerminal) { + ideProtocolClient.continueTerminal = capturedTerminal; + } }); extensionContext = context; diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index a889d3dc..9a93a4ef 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -323,7 +323,7 @@ class IdeProtocolClient { return rangeInFiles; } - private continueTerminal: CapturedTerminal | undefined; + public continueTerminal: CapturedTerminal | undefined; async runCommand(command: string) { if (!this.continueTerminal || this.continueTerminal.isClosed()) { diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index 9d90b331..ff2060f8 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -76,7 +76,8 @@ export class CapturedTerminal { (lines.length > 0 && (last_line.includes("bash-") || last_line.includes(") $ ")) && last_line.includes("$")) || - (last_line.includes("]> ") && last_line.includes(") [")) + (last_line.includes("]> ") && last_line.includes(") [")) || + (last_line.includes(" (") && last_line.includes(")>")) ); } @@ -98,7 +99,7 @@ export class CapturedTerminal { if (!this.hasRunCommand) { this.hasRunCommand = true; // Let the first bash- prompt appear and let python env be opened - await this.waitForCommandToFinish(); + // await this.waitForCommandToFinish(); } if (this.commandQueue.length === 0) { @@ -154,7 +155,7 @@ export class CapturedTerminal { // Create the pseudo terminal this.ptyProcess = pty.spawn(this.shellCmd, [], { name: "xterm-256color", - cols: 160, // TODO: Get size of vscode terminal, and change with resize + cols: 250, // No way to get the size of VS Code terminal, or listen to resize, so make it just bigger than most conceivable VS Code widths rows: 26, cwd: getRootDir(), env, -- cgit v1.2.3-70-g09d2 From b740c5bf216b63dbb2ffa88f8385e9795aa57403 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 16 Jun 2023 14:34:30 -0700 Subject: Capturing command line prompt --- extension/package-lock.json | 4 +- extension/package.json | 2 +- extension/src/terminal/terminalEmulator.ts | 97 ++++++++++++++++++++++++++---- extension/src/util/lcs.ts | 30 +++++++++ 4 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 extension/src/util/lcs.ts (limited to 'extension/src') diff --git a/extension/package-lock.json b/extension/package-lock.json index eac8edf2..86c816e0 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.45", + "version": "0.0.47", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.45", + "version": "0.0.47", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 8f7da94d..3a3927f3 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.45", + "version": "0.0.47", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index ff2060f8..8e49737e 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -3,6 +3,7 @@ import * as vscode from "vscode"; import os = require("os"); import stripAnsi from "strip-ansi"; +import { longestCommonSubsequence } from "../util/lcs"; function loadNativeModule(id: string): T | null { try { @@ -71,13 +72,20 @@ export class CapturedTerminal { private dataEndsInPrompt(strippedData: string): boolean { const lines = strippedData.split("\n"); - const last_line = lines[lines.length - 1]; + const lastLine = lines[lines.length - 1]; + return ( - (lines.length > 0 && - (last_line.includes("bash-") || last_line.includes(") $ ")) && - last_line.includes("$")) || - (last_line.includes("]> ") && last_line.includes(") [")) || - (last_line.includes(" (") && last_line.includes(")>")) + lines.length > 0 && + (((lastLine.includes("bash-") || lastLine.includes(") $ ")) && + lastLine.includes("$")) || + (lastLine.includes("]> ") && lastLine.includes(") [")) || + (lastLine.includes(" (") && lastLine.includes(")>")) || + (typeof this.commandPromptString !== "undefined" && + (lastLine.includes(this.commandPromptString) || + this.commandPromptString.length - + longestCommonSubsequence(lastLine, this.commandPromptString) + .length < + 3))) ); } @@ -96,12 +104,6 @@ export class CapturedTerminal { } async runCommand(command: string): Promise { - if (!this.hasRunCommand) { - this.hasRunCommand = true; - // Let the first bash- prompt appear and let python env be opened - // await this.waitForCommandToFinish(); - } - if (this.commandQueue.length === 0) { return new Promise(async (resolve, reject) => { this.commandQueue.push([command, resolve]); @@ -109,8 +111,12 @@ export class CapturedTerminal { while (this.commandQueue.length > 0) { const [command, resolve] = this.commandQueue.shift()!; + // Refresh the command prompt string every time in case it changes + await this.refreshCommandPromptString(); + this.terminal.sendText(command); - resolve(await this.waitForCommandToFinish()); + const output = await this.waitForCommandToFinish(); + resolve(output); } }); } else { @@ -138,6 +144,24 @@ export class CapturedTerminal { } } + private runningClearToGetPrompt: boolean = false; + private seenClear: boolean = false; + private commandPromptString: string | undefined = undefined; + private resolveMeWhenCommandPromptStringFound: + | ((_: unknown) => void) + | undefined = undefined; + + private async refreshCommandPromptString(): Promise { + // Sends a message that will be received by the terminal to get the command prompt string, see the onData method below in constructor. + this.runningClearToGetPrompt = true; + this.terminal.sendText("echo"); + const promise = new Promise((resolve, reject) => { + this.resolveMeWhenCommandPromptStringFound = resolve; + }); + await promise; + return this.commandPromptString; + } + constructor( options: { name: string } & Partial, onCommandOutput?: (output: string) => void @@ -165,7 +189,54 @@ export class CapturedTerminal { this.writeEmitter = new vscode.EventEmitter(); this.ptyProcess.onData((data: any) => { + if (this.runningClearToGetPrompt) { + if ( + stripAnsi(data) + .split("\n") + .flatMap((line) => line.split("\r")) + .find((line) => line.trim() === "echo") !== undefined + ) { + this.seenClear = true; + return; + } else if (this.seenClear) { + const strippedLines = stripAnsi(data) + .split("\r") + .filter( + (line) => + line.trim().length > 0 && + line.trim() !== "%" && + line.trim() !== "⏎" + ); + const lastLine = strippedLines[strippedLines.length - 1] || ""; + const lines = lastLine + .split("\n") + .filter( + (line) => + line.trim().length > 0 && + line.trim() !== "%" && + line.trim() !== "⏎" + ); + const commandPromptString = (lines[lines.length - 1] || "").trim(); + if ( + commandPromptString.length > 0 && + !commandPromptString.includes("echo") + ) { + this.runningClearToGetPrompt = false; + this.seenClear = false; + this.commandPromptString = commandPromptString; + console.log( + "Found command prompt string: " + this.commandPromptString + ); + if (this.resolveMeWhenCommandPromptStringFound) { + this.resolveMeWhenCommandPromptStringFound(undefined); + } + } + return; + } + } + // Pass data through to terminal + data = data.replace("⏎", ""); this.writeEmitter.fire(data); this.splitByCommandsListener(data); diff --git a/extension/src/util/lcs.ts b/extension/src/util/lcs.ts new file mode 100644 index 00000000..17ea63f9 --- /dev/null +++ b/extension/src/util/lcs.ts @@ -0,0 +1,30 @@ +export function longestCommonSubsequence(a: string, b: string) { + const lengths: number[][] = []; + for (let i = 0; i <= a.length; i++) { + lengths[i] = []; + for (let j = 0; j <= b.length; j++) { + if (i === 0 || j === 0) { + lengths[i][j] = 0; + } else if (a[i - 1] === b[j - 1]) { + lengths[i][j] = lengths[i - 1][j - 1] + 1; + } else { + lengths[i][j] = Math.max(lengths[i - 1][j], lengths[i][j - 1]); + } + } + } + let result = ""; + let x = a.length; + let y = b.length; + while (x !== 0 && y !== 0) { + if (lengths[x][y] === lengths[x - 1][y]) { + x--; + } else if (lengths[x][y] === lengths[x][y - 1]) { + y--; + } else { + result = a[x - 1] + result; + x--; + y--; + } + } + return result; +} -- cgit v1.2.3-70-g09d2 From 5a6fa9ae784c5942f1e37c7a0ae3acdd6337c459 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 16 Jun 2023 23:42:10 -0700 Subject: polishing --- continuedev/src/continuedev/core/config.py | 15 ++++++++++-- continuedev/src/continuedev/core/sdk.py | 3 +++ .../src/continuedev/libs/llm/proxy_server.py | 2 +- .../continuedev/libs/util/step_name_to_steps.py | 2 ++ continuedev/src/continuedev/steps/clear_history.py | 10 ++++++++ continuedev/src/continuedev/steps/core/core.py | 2 ++ extension/package-lock.json | 4 ++-- extension/package.json | 8 +++---- extension/react-app/src/components/ComboBox.tsx | 21 ++++++++++++++++ .../src/components/HeaderButtonWithText.tsx | 4 +--- .../react-app/src/components/StepContainer.tsx | 1 - extension/react-app/src/components/TextDialog.tsx | 28 ++++++++++++++++++++-- extension/react-app/src/tabs/gui.tsx | 3 +++ extension/src/commands.ts | 5 +--- extension/src/continueIdeClient.ts | 2 +- 15 files changed, 90 insertions(+), 20 deletions(-) create mode 100644 continuedev/src/continuedev/steps/clear_history.py (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py index 8f703758..1ee3a7f8 100644 --- a/continuedev/src/continuedev/core/config.py +++ b/continuedev/src/continuedev/core/config.py @@ -52,6 +52,11 @@ class ContinueConfig(BaseModel): name="feedback", description="Send feedback to improve Continue", step_name="FeedbackStep", + ), + SlashCommand( + name="clear", + description="Clear step history", + step_name="ClearHistoryStep", ) ] on_traceback: Optional[List[OnTracebackSteps]] = [ @@ -68,10 +73,16 @@ def load_config(config_file: str) -> ContinueConfig: _, ext = os.path.splitext(config_file) if ext == '.yaml': with open(config_file, 'r') as f: - config_dict = yaml.safe_load(f) + try: + config_dict = yaml.safe_load(f) + except: + return ContinueConfig() elif ext == '.json': with open(config_file, 'r') as f: - config_dict = json.load(f) + try: + config_dict = json.load(f) + except: + return ContinueConfig() else: raise ValueError(f'Unknown config file extension: {ext}') return ContinueConfig(**config_dict) diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 7639d010..4ab2f027 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -192,3 +192,6 @@ class ContinueSDK(AbstractContinueSDK): async def update_ui(self): await self.__autopilot.update_subscribers() + + async def clear_history(self): + await self.__autopilot.clear_history() diff --git a/continuedev/src/continuedev/libs/llm/proxy_server.py b/continuedev/src/continuedev/libs/llm/proxy_server.py index 93f2d48a..dd0d3aca 100644 --- a/continuedev/src/continuedev/libs/llm/proxy_server.py +++ b/continuedev/src/continuedev/libs/llm/proxy_server.py @@ -18,7 +18,7 @@ CHAT_MODELS = { } # SERVER_URL = "http://127.0.0.1:8080" -SERVER_URL = "https://proxy-server-l6vsfbzhba-uc.a.run.app" +SERVER_URL = "https://proxy-server-l6vsfbzhba-uw.a.run.app" class ProxyServer(LLM): diff --git a/continuedev/src/continuedev/libs/util/step_name_to_steps.py b/continuedev/src/continuedev/libs/util/step_name_to_steps.py index 2c4474af..f431f317 100644 --- a/continuedev/src/continuedev/libs/util/step_name_to_steps.py +++ b/continuedev/src/continuedev/libs/util/step_name_to_steps.py @@ -11,6 +11,7 @@ from ...recipes.CreatePipelineRecipe.main import CreatePipelineRecipe from ...recipes.DDtoBQRecipe.main import DDtoBQRecipe from ...recipes.DeployPipelineAirflowRecipe.main import DeployPipelineAirflowRecipe from ...steps.on_traceback import DefaultOnTracebackStep +from ...steps.clear_history import ClearHistoryStep # This mapping is used to convert from string in ContinueConfig json to corresponding Step class. # Used for example in slash_commands and steps_on_startup @@ -25,6 +26,7 @@ step_name_to_step_class = { "DDtoBQRecipe": DDtoBQRecipe, "DeployPipelineAirflowRecipe": DeployPipelineAirflowRecipe, "DefaultOnTracebackStep": DefaultOnTracebackStep, + "ClearHistoryStep": ClearHistoryStep, } diff --git a/continuedev/src/continuedev/steps/clear_history.py b/continuedev/src/continuedev/steps/clear_history.py new file mode 100644 index 00000000..a875c6d3 --- /dev/null +++ b/continuedev/src/continuedev/steps/clear_history.py @@ -0,0 +1,10 @@ +from ..core.main import Step +from ..core.sdk import ContinueSDK + + +class ClearHistoryStep(Step): + name: str = "Clear History" + hide: bool = True + + async def run(self, sdk: ContinueSDK): + await sdk.clear_history() diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 59af5f38..330f60ed 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -186,6 +186,8 @@ class DefaultModelEditCodeStep(Step): completion = completion.removesuffix(eot_token) # Remove tags and If it accidentally includes prefix or suffix, remove it + if completion.strip().startswith("```"): + completion = completion.strip().removeprefix("```").removesuffix("```") completion = completion.replace("", "").replace("", "").replace( "", "").replace("", "").replace("", "") completion = completion.removeprefix(segs[0]) diff --git a/extension/package-lock.json b/extension/package-lock.json index 86c816e0..92df6a4f 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.47", + "version": "0.0.50", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.47", + "version": "0.0.50", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 3a3927f3..a875c82d 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.47", + "version": "0.0.50", "publisher": "Continue", "engines": { "vscode": "^1.74.0" @@ -86,8 +86,8 @@ "activitybar": [ { "id": "continue", - "title": "Continue", - "icon": "media/continue-gradient.png" + "title": "Continue ", + "icon": "react-app/dist/continue_arrow.png" } ] }, @@ -96,7 +96,7 @@ { "type": "webview", "id": "continue.continueGUIView", - "name": ")", + "name": " )", "visibility": "visible" } ] diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 2b140567..29f8a53b 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -68,6 +68,9 @@ interface ComboBoxProps { } const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { + const [history, setHistory] = React.useState([]); + // The position of the current command you are typing now, so the one that will be appended to history once you press enter + const [positionInHistory, setPositionInHistory] = React.useState(0); const [items, setItems] = React.useState(props.items); const { isOpen, @@ -113,9 +116,27 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { (event.nativeEvent as any).preventDownshiftDefault = true; if (props.onEnter) props.onEnter(event); setInputValue(""); + const value = event.currentTarget.value; + if (value !== "") { + setPositionInHistory(history.length + 1); + setHistory([...history, value]); + } } else if (event.key === "Tab" && items.length > 0) { setInputValue(items[0].name); event.preventDefault(); + } else if (event.key === "ArrowUp" && !isOpen) { + if (positionInHistory == 0) return; + setInputValue(history[positionInHistory - 1]); + setPositionInHistory((prev) => prev - 1); + } else if (event.key === "ArrowDown" && !isOpen) { + if (positionInHistory >= history.length - 1) { + setInputValue(""); + } else { + setInputValue(history[positionInHistory + 1]); + } + setPositionInHistory((prev) => + Math.min(prev + 1, history.length) + ); } }, ref: ref as any, diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx index c4f22211..f9483f0f 100644 --- a/extension/react-app/src/components/HeaderButtonWithText.tsx +++ b/extension/react-app/src/components/HeaderButtonWithText.tsx @@ -15,9 +15,7 @@ const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => { style={{ padding: "3px" }} onMouseEnter={() => setHover(true)} onMouseLeave={() => { - setTimeout(() => { - setHover(false); - }, 100); + setHover(false); }} onClick={props.onClick} > diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 480f517f..7adbd7c2 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -63,7 +63,6 @@ const HeaderDiv = styled.div<{ error: boolean }>` const ContentDiv = styled.div` padding: 8px; padding-left: 16px; - background-color: ${vscBackground}; `; const OnHoverDiv = styled.div` diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx index 167e12cf..31a385d5 100644 --- a/extension/react-app/src/components/TextDialog.tsx +++ b/extension/react-app/src/components/TextDialog.tsx @@ -1,5 +1,5 @@ // Write a component that displays a dialog box with a text field and a button. -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import styled from "styled-components"; import { Button, buttonColor, secondaryDark, vscBackground } from "."; @@ -28,6 +28,10 @@ const TextArea = styled.textarea` padding: 8px; outline: 1px solid black; font-family: Arial, Helvetica, sans-serif; + + &:focus { + outline: 1px solid orange; + } `; const P = styled.p` @@ -38,19 +42,39 @@ const P = styled.p` const TextDialog = (props: { showDialog: boolean; onEnter: (text: string) => void; + onClose: () => void; }) => { const [text, setText] = useState(""); const textAreaRef = React.createRef(); + useEffect(() => { + if (textAreaRef.current) { + textAreaRef.current.focus(); + } + }, [props.showDialog]); + return (
- ); -} - -export default AdditionalContextTab; diff --git a/extension/react-app/src/tabs/chat/MessageDiv.tsx b/extension/react-app/src/tabs/chat/MessageDiv.tsx deleted file mode 100644 index 3543dd93..00000000 --- a/extension/react-app/src/tabs/chat/MessageDiv.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useEffect } from "react"; -import { ChatMessage } from "../../redux/store"; -import styled from "styled-components"; -import { - buttonColor, - defaultBorderRadius, - secondaryDark, -} from "../../components"; -import VSCodeFileLink from "../../components/VSCodeFileLink"; -import ReactMarkdown from "react-markdown"; -import "../../highlight/dark.min.css"; -import hljs from "highlight.js"; -import { useSelector } from "react-redux"; -import { selectIsStreaming } from "../../redux/selectors/chatSelectors"; - -const Container = styled.div` - padding-left: 8px; - padding-right: 8px; - border-radius: 8px; - margin: 3px; - width: fit-content; - max-width: 75%; - overflow-y: scroll; - word-wrap: break-word; - -ms-word-wrap: break-word; - height: fit-content; - overflow: hidden; - background-color: ${(props) => { - if (props.role === "user") { - return buttonColor; - } else { - return secondaryDark; - } - }}; - float: ${(props) => { - if (props.role === "user") { - return "right"; - } else { - return "left"; - } - }}; - display: block; - - & pre { - border: 1px solid gray; - border-radius: ${defaultBorderRadius}; - } -`; - -function MessageDiv(props: ChatMessage) { - const [richContent, setRichContent] = React.useState([]); - const isStreaming = useSelector(selectIsStreaming); - - useEffect(() => { - if (!isStreaming) { - hljs.highlightAll(); - } - }, [richContent, isStreaming]); - - useEffect(() => { - setRichContent([ - , - ]); - }, [props.content]); - - return ( - <> -
- {richContent} -
- - ); -} - -export default MessageDiv; diff --git a/extension/react-app/src/tabs/chat/index.tsx b/extension/react-app/src/tabs/chat/index.tsx deleted file mode 100644 index a93ad4f9..00000000 --- a/extension/react-app/src/tabs/chat/index.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { selectChatMessages } from "../../redux/selectors/chatSelectors"; -import MessageDiv from "./MessageDiv"; -import styled from "styled-components"; -import { addMessage, setIsStreaming } from "../../redux/slices/chatSlice"; -import { AnyAction, Dispatch } from "@reduxjs/toolkit"; -import { closeStream, streamUpdate } from "../../redux/slices/chatSlice"; -import { ChatMessage, RootStore } from "../../redux/store"; -import { postVscMessage, vscRequest } from "../../vscode"; -import { defaultBorderRadius, Loader } from "../../components"; -import { selectHighlightedCode } from "../../redux/selectors/miscSelectors"; -import { readRangeInVirtualFileSystem } from "../../util"; -import { selectDebugContext } from "../../redux/selectors/debugContextSelectors"; - -let textEntryBarHeight = "30px"; - -const ChatContainer = styled.div` - display: grid; - grid-template-rows: 1fr auto; - height: 100%; -`; - -const BottomDiv = styled.div` - display: grid; - grid-template-rows: auto auto; -`; - -const BottomButton = styled.button( - (props: { active: boolean }) => ` - font-size: 10px; - border: none; - color: white; - margin-right: 4px; - cursor: pointer; - background-color: ${props.active ? "black" : "gray"}; - border-radius: ${defaultBorderRadius}; - padding: 8px; -` -); - -const TextEntryBar = styled.input` - height: ${textEntryBarHeight}; - border-bottom-left-radius: ${defaultBorderRadius}; - border-bottom-right-radius: ${defaultBorderRadius}; - padding: 8px; - border: 1px solid white; - background-color: black; - color: white; - outline: none; -`; - -function ChatTab() { - const dispatch = useDispatch(); - const chatMessages = useSelector(selectChatMessages); - const isStreaming = useSelector((state: RootStore) => state.chat.isStreaming); - const baseUrl = useSelector((state: RootStore) => state.config.apiUrl); - const debugContext = useSelector(selectDebugContext); - - const [includeHighlightedCode, setIncludeHighlightedCode] = useState(true); - const [writeToEditor, setWriteToEditor] = useState(false); - const [waitingForResponse, setWaitingForResponse] = useState(false); - - const highlightedCode = useSelector(selectHighlightedCode); - - const streamToStateThunk = useCallback( - (dispatch: Dispatch, getResponse: () => Promise) => { - let streamToCursor = writeToEditor; - getResponse().then((resp) => { - setWaitingForResponse(false); - if (resp.body) { - resp.body.pipeTo( - new WritableStream({ - write(chunk) { - let update = new TextDecoder("utf-8").decode(chunk); - dispatch(streamUpdate(update)); - if (streamToCursor) { - postVscMessage("streamUpdate", { update }); - } - }, - close() { - dispatch(closeStream()); - if (streamToCursor) { - postVscMessage("closeStream", null); - } - }, - }) - ); - } - }); - }, - [writeToEditor] - ); - - const compileHiddenChatMessages = useCallback(async () => { - let messages: ChatMessage[] = []; - if ( - includeHighlightedCode && - highlightedCode?.filepath !== undefined && - highlightedCode?.range !== undefined && - debugContext.filesystem[highlightedCode.filepath] !== undefined - ) { - let fileContents = readRangeInVirtualFileSystem( - highlightedCode, - debugContext.filesystem - ); - if (fileContents) { - messages.push({ - role: "user", - content: fileContents, - }); - } - } else { - // Similarity search over workspace - let data = await vscRequest("queryEmbeddings", { - query: chatMessages[chatMessages.length - 1].content, - }); - let codeContextMessages = data.results.map( - (result: { id: string; document: string }) => { - let msg: ChatMessage = { - role: "user", - content: `File: ${result.id} \n ${result.document}`, - }; - return msg; - } - ); - codeContextMessages.push({ - role: "user", - content: - "Use the above code to help you answer the question below. Answer in asterisk bullet points, and give the full path whenever you reference files.", - }); - messages.push(...codeContextMessages); - } - - let systemMsgContent = writeToEditor - ? "Respond only with the exact code requested, no additional text." - : "Use the above code to help you answer the question below. Respond in markdown if using bullets or other special formatting, being sure to specify language for code blocks."; - - messages.push({ - role: "system", - content: systemMsgContent, - }); - return messages; - }, [highlightedCode, chatMessages, includeHighlightedCode, writeToEditor]); - - useEffect(() => { - if ( - chatMessages.length > 0 && - chatMessages[chatMessages.length - 1].role === "user" && - !isStreaming - ) { - dispatch(setIsStreaming(true)); - streamToStateThunk(dispatch, async () => { - if (chatMessages.length === 0) { - return new Promise((resolve, _) => resolve(new Response())); - } - let hiddenChatMessages = await compileHiddenChatMessages(); - let augmentedMessages = [ - ...chatMessages.slice(0, -1), - ...hiddenChatMessages, - chatMessages[chatMessages.length - 1], - ]; - console.log(augmentedMessages); - // The autogenerated client can't handle streams, so have to go raw - return fetch(`${baseUrl}/chat/complete`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - messages: augmentedMessages, - }), - }); - }); - } - }, [chatMessages, dispatch, isStreaming, highlightedCode]); - - const chatMessagesDiv = useRef(null); - useEffect(() => { - // Scroll to bottom - let interval = setInterval(() => { - if (chatMessagesDiv.current && !waitingForResponse) { - chatMessagesDiv.current.scrollTop += Math.max( - 4, - 0.05 * chatMessagesDiv.current.scrollHeight - - chatMessagesDiv.current.clientHeight - - chatMessagesDiv.current.scrollTop - ); - if ( - chatMessagesDiv.current.scrollTop >= - chatMessagesDiv.current.scrollHeight - - chatMessagesDiv.current.clientHeight - ) { - clearInterval(interval); - } - } - }, 10); - }, [chatMessages, chatMessagesDiv, waitingForResponse]); - - return ( - -
-

Chat

-
-
- {chatMessages.length > 0 ? ( - chatMessages.map((message, idx) => { - return ; - }) - ) : ( -

- You can ask questions about your codebase or ask for code written - directly in the editor. -

- )} - {waitingForResponse && } -
-
- - -
-
- {/*

- Highlighted code is automatically included in your chat message. -

*/} - { - setWriteToEditor(!writeToEditor); - }} - > - {writeToEditor ? "Writing to editor" : "Write to editor"} - - - { - setIncludeHighlightedCode(!includeHighlightedCode); - }} - > - {includeHighlightedCode - ? "Including highlighted code" - : "Automatically finding relevant code"} - -
-
- { - if (e.key === "Enter" && e.currentTarget.value !== "") { - console.log("Sending message", e.currentTarget.value); - dispatch( - addMessage({ content: e.currentTarget.value, role: "user" }) - ); - (e.target as any).value = ""; - setWaitingForResponse(true); - } - }} - > -
-
- ); -} - -export default ChatTab; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx deleted file mode 100644 index b6a18dc8..00000000 --- a/extension/react-app/src/tabs/gui.tsx +++ /dev/null @@ -1,486 +0,0 @@ -import styled from "styled-components"; -import { defaultBorderRadius } from "../components"; -import Loader from "../components/Loader"; -import ContinueButton from "../components/ContinueButton"; -import { FullState, HighlightedRangeContext } from "../../../schema/FullState"; -import { useCallback, useEffect, useRef, useState, useContext } from "react"; -import { History } from "../../../schema/History"; -import { HistoryNode } from "../../../schema/HistoryNode"; -import StepContainer from "../components/StepContainer"; -import { GUIClientContext } from "../App"; -import { - BookOpen, - ChatBubbleOvalLeftEllipsis, - Trash, -} from "@styled-icons/heroicons-outline"; -import ComboBox from "../components/ComboBox"; -import TextDialog from "../components/TextDialog"; -import HeaderButtonWithText from "../components/HeaderButtonWithText"; -import ReactSwitch from "react-switch"; -import { usePostHog } from "posthog-js/react"; -import { useSelector } from "react-redux"; -import { RootStore } from "../redux/store"; -import LoadingCover from "../components/LoadingCover"; -import { postVscMessage } from "../vscode"; -import UserInputContainer from "../components/UserInputContainer"; -import Onboarding from "../components/Onboarding"; - -const TopGUIDiv = styled.div` - overflow: hidden; -`; - -const UserInputQueueItem = styled.div` - border-radius: ${defaultBorderRadius}; - color: gray; - padding: 8px; - margin: 8px; - text-align: center; -`; - -const Footer = styled.footer<{ dataSwitchChecked: boolean }>` - display: flex; - flex-direction: row; - gap: 8px; - justify-content: right; - padding: 8px; - align-items: center; - margin-top: 8px; - border-top: 0.1px solid gray; - background-color: ${(props) => - props.dataSwitchChecked ? "#12887a33" : "transparent"}; -`; - -interface GUIProps { - firstObservation?: any; -} - -function GUI(props: GUIProps) { - const client = useContext(GUIClientContext); - const posthog = usePostHog(); - const vscMachineId = useSelector( - (state: RootStore) => state.config.vscMachineId - ); - const [dataSwitchChecked, setDataSwitchChecked] = useState(false); - const dataSwitchOn = useSelector( - (state: RootStore) => state.config.dataSwitchOn - ); - - useEffect(() => { - if (typeof dataSwitchOn !== "undefined") { - setDataSwitchChecked(dataSwitchOn); - } - }, [dataSwitchOn]); - - const [usingFastModel, setUsingFastModel] = useState(false); - const [waitingForSteps, setWaitingForSteps] = useState(false); - const [userInputQueue, setUserInputQueue] = useState([]); - const [highlightedRanges, setHighlightedRanges] = useState< - HighlightedRangeContext[] - >([]); - const [addingHighlightedCode, setAddingHighlightedCode] = useState(false); - const [availableSlashCommands, setAvailableSlashCommands] = useState< - { name: string; description: string }[] - >([]); - const [pinned, setPinned] = useState(false); - const [showDataSharingInfo, setShowDataSharingInfo] = useState(false); - const [stepsOpen, setStepsOpen] = useState([ - true, - true, - true, - true, - ]); - const [history, setHistory] = useState({ - timeline: [ - { - step: { - name: "Welcome to Continue", - hide: false, - description: `- Highlight code and ask a question or give instructions -- Use \`cmd+k\` (Mac) / \`ctrl+k\` (Windows) to open Continue -- Use \`cmd+shift+e\` / \`ctrl+shift+e\` to open file Explorer -- Add your own OpenAI API key to VS Code Settings with \`cmd+,\` -- Use slash commands when you want fine-grained control -- Past steps are included as part of the context by default`, - system_message: null, - chat_context: [], - manage_own_chat_context: false, - message: "", - }, - depth: 0, - deleted: false, - active: false, - }, - ], - current_index: 3, - } as any); - - const [showFeedbackDialog, setShowFeedbackDialog] = useState(false); - const [feedbackDialogMessage, setFeedbackDialogMessage] = useState(""); - - const topGuiDivRef = useRef(null); - - const [scrollTimeout, setScrollTimeout] = useState( - null - ); - const scrollToBottom = useCallback(() => { - if (scrollTimeout) { - clearTimeout(scrollTimeout); - } - // Debounced smooth scroll to bottom of screen - if (topGuiDivRef.current) { - const timeout = setTimeout(() => { - window.scrollTo({ - top: topGuiDivRef.current!.offsetHeight, - behavior: "smooth", - }); - }, 200); - setScrollTimeout(timeout); - } - }, [topGuiDivRef.current, scrollTimeout]); - - useEffect(() => { - const listener = (e: any) => { - // Cmd + i to toggle fast model - if (e.key === "i" && e.metaKey && e.shiftKey) { - setUsingFastModel((prev) => !prev); - // Cmd + backspace to stop currently running step - } else if ( - e.key === "Backspace" && - e.metaKey && - typeof history?.current_index !== "undefined" && - history.timeline[history.current_index]?.active - ) { - client?.deleteAtIndex(history.current_index); - } - }; - window.addEventListener("keydown", listener); - - return () => { - window.removeEventListener("keydown", listener); - }; - }, [client, history]); - - useEffect(() => { - client?.onStateUpdate((state: FullState) => { - // Scroll only if user is at very bottom of the window. - setUsingFastModel(state.default_model === "gpt-3.5-turbo"); - const shouldScrollToBottom = - topGuiDivRef.current && - topGuiDivRef.current?.offsetHeight - window.scrollY < 100; - - const waitingForSteps = - state.active && - state.history.current_index < state.history.timeline.length && - state.history.timeline[state.history.current_index] && - state.history.timeline[ - state.history.current_index - ].step.description?.trim() === ""; - - setWaitingForSteps(waitingForSteps); - setHistory(state.history); - setHighlightedRanges(state.highlighted_ranges); - setUserInputQueue(state.user_input_queue); - setAddingHighlightedCode(state.adding_highlighted_code); - setAvailableSlashCommands( - state.slash_commands.map((c: any) => { - return { - name: `/${c.name}`, - description: c.description, - }; - }) - ); - setStepsOpen((prev) => { - const nextStepsOpen = [...prev]; - for ( - let i = nextStepsOpen.length; - i < state.history.timeline.length; - i++ - ) { - nextStepsOpen.push(true); - } - return nextStepsOpen; - }); - - if (shouldScrollToBottom) { - scrollToBottom(); - } - }); - }, [client]); - - useEffect(() => { - scrollToBottom(); - }, [waitingForSteps]); - - const mainTextInputRef = useRef(null); - - const deleteContextItems = useCallback( - (indices: number[]) => { - client?.deleteContextAtIndices(indices); - }, - [client] - ); - - const onMainTextInput = (event?: any) => { - if (mainTextInputRef.current) { - let input = (mainTextInputRef.current as any).inputValue; - // cmd+enter to /edit - if (event?.metaKey) { - input = `/edit ${input}`; - } - (mainTextInputRef.current as any).setInputValue(""); - if (!client) return; - - setWaitingForSteps(true); - - if ( - history && - history.current_index >= 0 && - history.current_index < history.timeline.length - ) { - if ( - history.timeline[history.current_index]?.step.name === - "Waiting for user input" - ) { - if (input.trim() === "") return; - onStepUserInput(input, history!.current_index); - return; - } else if ( - history.timeline[history.current_index]?.step.name === - "Waiting for user confirmation" - ) { - onStepUserInput("ok", history!.current_index); - return; - } - } - if (input.trim() === "") return; - - client.sendMainInput(input); - setUserInputQueue((queue) => { - return [...queue, input]; - }); - } - }; - - const onStepUserInput = (input: string, index: number) => { - if (!client) return; - console.log("Sending step user input", input, index); - client.sendStepUserInput(input, index); - }; - - // const iterations = useSelector(selectIterations); - return ( - <> - -
- - ); -} - -export default GUI; diff --git a/extension/react-app/src/tabs/main.tsx b/extension/react-app/src/tabs/main.tsx deleted file mode 100644 index a8b3300d..00000000 --- a/extension/react-app/src/tabs/main.tsx +++ /dev/null @@ -1,189 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { H3, TextArea, Button, Pre, Loader } from "../components"; -import styled from "styled-components"; -import { postVscMessage, withProgress } from "../vscode"; -import { useDebugContextValue } from "../redux/hooks"; -import CodeMultiselect from "../components/CodeMultiselect"; -import { useSelector } from "react-redux"; -import { selectDebugContext } from "../redux/selectors/debugContextSelectors"; -import { useDispatch } from "react-redux"; -import { updateValue } from "../redux/slices/debugContexSlice"; -import { setWorkspacePath } from "../redux/slices/configSlice"; -import { SerializedDebugContext } from "../../../src/client"; -import { useEditCache } from "../util/editCache"; -import { useApi } from "../util/api"; - -const ButtonDiv = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - gap: 4px; - margin: 4px; - flex-wrap: wrap; - - & button { - flex-grow: 1; - } -`; - -function MainTab(props: any) { - const dispatch = useDispatch(); - - const [suggestion, setSuggestion] = useState(""); - const [traceback, setTraceback] = useDebugContextValue("traceback", ""); - const [selectedRanges, setSelectedRanges] = useDebugContextValue( - "rangesInFiles", - [] - ); - - const editCache = useEditCache(); - const { debugApi } = useApi(); - - const [responseLoading, setResponseLoading] = useState(false); - - let debugContext = useSelector(selectDebugContext); - - useEffect(() => { - editCache.preloadEdit(debugContext); - }, [debugContext]); - - function postVscMessageWithDebugContext( - type: string, - overrideDebugContext: SerializedDebugContext | null = null - ) { - postVscMessage(type, { - debugContext: overrideDebugContext || debugContext, - }); - } - - function launchFindSuspiciousCode(newTraceback: string) { - // setTraceback's effects don't occur immediately, so we have to add it to the debug context manually - let updatedDebugContext = { - ...debugContext, - traceback: newTraceback, - }; - postVscMessageWithDebugContext("findSuspiciousCode", updatedDebugContext); - postVscMessageWithDebugContext("preloadEdit", updatedDebugContext); - } - - useEffect(() => { - const eventListener = (event: any) => { - switch (event.data.type) { - case "suggestFix": - case "explainCode": - case "listTenThings": - setSuggestion(event.data.value); - setResponseLoading(false); - break; - case "traceback": - setTraceback(event.data.value); - launchFindSuspiciousCode(event.data.value); - break; - case "workspacePath": - dispatch(setWorkspacePath(event.data.value)); - break; - } - }; - window.addEventListener("message", eventListener); - - return () => window.removeEventListener("message", eventListener); - }, [debugContext, selectedRanges]); - - return ( -
-

Debug Panel

- -

Code Sections

- - -

Bug Description

- - -

Stack Trace

- - - - - - - - - - - - - - -

-
- ); -} - -export default MainTab; diff --git a/extension/react-app/src/tabs/welcome.tsx b/extension/react-app/src/tabs/welcome.tsx deleted file mode 100644 index c29d260a..00000000 --- a/extension/react-app/src/tabs/welcome.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from "react"; - -function WelcomeTab() { - return ( -
-

Welcome to Continue

- -

- Learn more in the{" "} - - Continue User Guide - {" "} -

-

Send Nate or Ty your feedback:

-

1. What excites you about Continue?

-

2. What did you struggle with when using Continue?

-

3. How do you wish Continue worked?

-
- ); -} - -export default WelcomeTab; diff --git a/extension/src/bridge.ts b/extension/src/bridge.ts index 92ba4044..55c4cc3b 100644 --- a/extension/src/bridge.ts +++ b/extension/src/bridge.ts @@ -4,14 +4,11 @@ import * as vscode from "vscode"; import { Configuration, DebugApi, - RangeInFile, - SerializedDebugContext, UnittestApi, } from "./client"; import { convertSingleToDoubleQuoteJSON } from "./util/util"; import { getExtensionUri } from "./util/vscode"; import { extensionContext } from "./activation/activate"; -const axios = require("axios").default; const util = require("util"); const exec = util.promisify(require("child_process").exec); @@ -36,21 +33,16 @@ const configuration = new Configuration({ export const debugApi = new DebugApi(configuration); export const unittestApi = new UnittestApi(configuration); -function get_python_path() { - return path.join(getExtensionUri().fsPath, ".."); -} - export function get_api_url() { - let extensionUri = getExtensionUri(); - let configFile = path.join(extensionUri.fsPath, "config/config.json"); - let config = require(configFile); + const extensionUri = getExtensionUri(); + const configFile = path.join(extensionUri.fsPath, "config/config.json"); + const config = require(configFile); if (config.API_URL) { return config.API_URL; } return "http://localhost:65432"; } -const API_URL = get_api_url(); export function getContinueServerUrl() { // If in debug mode, always use 8001 @@ -58,7 +50,7 @@ export function getContinueServerUrl() { extensionContext && extensionContext.extensionMode === vscode.ExtensionMode.Development ) { - return "http://localhost:8001"; + // return "http://localhost:8001"; } return ( vscode.workspace.getConfiguration("continue").get("serverUrl") || @@ -66,10 +58,6 @@ export function getContinueServerUrl() { ); } -function build_python_command(cmd: string): string { - return `cd ${get_python_path()} && source env/bin/activate && ${cmd}`; -} - function listToCmdLineArgs(list: string[]): string { return list.map((el) => `"$(echo "${el}")"`).join(" "); } @@ -103,198 +91,3 @@ export async function runPythonScript( } } } - -function parseStdout( - stdout: string, - key: string, - until_end: boolean = false -): string { - const prompt = `${key}=`; - let lines = stdout.split("\n"); - - let value: string = ""; - for (let i = 0; i < lines.length; i++) { - if (lines[i].startsWith(prompt)) { - if (until_end) { - return lines.slice(i).join("\n").substring(prompt.length); - } else { - return lines[i].substring(prompt.length); - } - } - } - return ""; -} - -export async function askQuestion( - question: string, - workspacePath: string -): Promise<{ answer: string; range: vscode.Range; filename: string }> { - const command = build_python_command( - `python3 ${path.join( - get_python_path(), - "ask.py" - )} ask ${workspacePath} "${question}"` - ); - - const { stdout, stderr } = await exec(command); - if (stderr) { - throw new Error(stderr); - } - // Use the output - const answer = parseStdout(stdout, "Answer"); - const filename = parseStdout(stdout, "Filename"); - const startLineno = parseInt(parseStdout(stdout, "Start lineno")); - const endLineno = parseInt(parseStdout(stdout, "End lineno")); - const range = new vscode.Range( - new vscode.Position(startLineno, 0), - new vscode.Position(endLineno, 0) - ); - if (answer && filename && startLineno && endLineno) { - return { answer, filename, range }; - } else { - throw new Error("Error: No answer found"); - } -} - -export async function apiRequest( - endpoint: string, - options: { - method?: string; - query?: { [key: string]: any }; - body?: { [key: string]: any }; - } -): Promise { - let defaults = { - method: "GET", - query: {}, - body: {}, - }; - options = Object.assign(defaults, options); // Second takes over first - if (endpoint.startsWith("/")) endpoint = endpoint.substring(1); - console.log("API request: ", options.body); - - let resp; - try { - resp = await axios({ - method: options.method, - url: `${API_URL}/${endpoint}`, - data: options.body, - params: options.query, - headers: { - "x-vsc-machine-id": vscode.env.machineId, - }, - }); - } catch (err) { - console.log("Error: ", err); - throw err; - } - - return resp.data; -} - -// Write a docstring for the most specific function or class at the current line in the given file -export async function writeDocstringForFunction( - filename: string, - position: vscode.Position -): Promise<{ lineno: number; docstring: string }> { - let resp = await apiRequest("docstring/forline", { - query: { - filecontents: ( - await vscode.workspace.fs.readFile(vscode.Uri.file(filename)) - ).toString(), - lineno: position.line.toString(), - }, - }); - - const lineno = resp.lineno; - const docstring = resp.completion; - if (lineno && docstring) { - return { lineno, docstring }; - } else { - throw new Error("Error: No docstring returned"); - } -} - -export async function findSuspiciousCode( - ctx: SerializedDebugContext -): Promise { - if (!ctx.traceback) return []; - let files = await getFileContents( - getFilenamesFromPythonStacktrace(ctx.traceback) - ); - let resp = await debugApi.findSusCodeEndpointDebugFindPost({ - findBody: { - traceback: ctx.traceback, - description: ctx.description, - filesystem: files, - }, - }); - let ranges = resp.response; - if ( - ranges.length <= 1 && - ctx.traceback && - ctx.traceback.includes("AssertionError") - ) { - let parsed_traceback = - await debugApi.parseTracebackEndpointDebugParseTracebackGet({ - traceback: ctx.traceback, - }); - let last_frame = parsed_traceback.frames[0]; - if (!last_frame) return []; - ranges = ( - await runPythonScript("build_call_graph.py", [ - last_frame.filepath, - last_frame.lineno.toString(), - last_frame._function, - ]) - ).value; - } - - return ranges; -} - -export async function writeUnitTestForFunction( - filename: string, - position: vscode.Position -): Promise { - let resp = await apiRequest("unittest/forline", { - method: "POST", - body: { - filecontents: ( - await vscode.workspace.fs.readFile(vscode.Uri.file(filename)) - ).toString(), - lineno: position.line, - userid: vscode.env.machineId, - }, - }); - - return resp.completion; -} - -async function getFileContents( - files: string[] -): Promise<{ [key: string]: string }> { - let contents = await Promise.all( - files.map(async (file: string) => { - return ( - await vscode.workspace.fs.readFile(vscode.Uri.file(file)) - ).toString(); - }) - ); - let fileContents: { [key: string]: string } = {}; - for (let i = 0; i < files.length; i++) { - fileContents[files[i]] = contents[i]; - } - return fileContents; -} - -function getFilenamesFromPythonStacktrace(traceback: string): string[] { - let filenames: string[] = []; - for (let line of traceback.split("\n")) { - let match = line.match(/File "(.*)", line/); - if (match) { - filenames.push(match[1]); - } - } - return filenames; -} -- cgit v1.2.3-70-g09d2 From 93bf9cdd82765665b481b64be7d7896fb04690ad Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 12 Jul 2023 15:19:57 -0700 Subject: purging more --- extension/react-app/index.html | 2 +- extension/react-app/public/vite.svg | 1 - extension/react-app/src/TestPage.tsx | 33 --- extension/react-app/src/assets/Hubot-Sans.woff2 | Bin 165932 -> 0 bytes extension/react-app/src/assets/Mona-Sans.woff2 | Bin 133748 -> 0 bytes extension/react-app/src/assets/react.svg | 1 - .../react-app/src/components/CodeMultiselect.tsx | 276 --------------------- extension/react-app/src/highlight/dark.min.css | 53 ---- extension/react-app/src/util/api.ts | 43 ---- extension/react-app/src/util/editCache.ts | 89 ------- extension/react-app/src/util/index.ts | 27 -- extension/src/commands.ts | 92 +------ 12 files changed, 2 insertions(+), 615 deletions(-) delete mode 100644 extension/react-app/public/vite.svg delete mode 100644 extension/react-app/src/TestPage.tsx delete mode 100644 extension/react-app/src/assets/Hubot-Sans.woff2 delete mode 100644 extension/react-app/src/assets/Mona-Sans.woff2 delete mode 100644 extension/react-app/src/assets/react.svg delete mode 100644 extension/react-app/src/components/CodeMultiselect.tsx delete mode 100644 extension/react-app/src/highlight/dark.min.css delete mode 100644 extension/react-app/src/util/api.ts delete mode 100644 extension/react-app/src/util/editCache.ts delete mode 100644 extension/react-app/src/util/index.ts (limited to 'extension/src') diff --git a/extension/react-app/index.html b/extension/react-app/index.html index e0d1c840..043c307e 100644 --- a/extension/react-app/index.html +++ b/extension/react-app/index.html @@ -2,7 +2,7 @@ - + Vite + React + TS diff --git a/extension/react-app/public/vite.svg b/extension/react-app/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/extension/react-app/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/extension/react-app/src/TestPage.tsx b/extension/react-app/src/TestPage.tsx deleted file mode 100644 index d104980b..00000000 --- a/extension/react-app/src/TestPage.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from "react"; -import styled from "styled-components"; - -const SideBySideDiv = styled.div` - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: 1fr; - grid-template-areas: "left right"; -`; - -const LeftDiv = styled.div` - grid-area: left; -`; - -const RightDiv = styled.div` - grid-area: right; -`; - -function TestPage() { - return ( -
-

Continue

- - -

Left

-
- -

Right

-
-
-
- ); -} diff --git a/extension/react-app/src/assets/Hubot-Sans.woff2 b/extension/react-app/src/assets/Hubot-Sans.woff2 deleted file mode 100644 index 5089fc47..00000000 Binary files a/extension/react-app/src/assets/Hubot-Sans.woff2 and /dev/null differ diff --git a/extension/react-app/src/assets/Mona-Sans.woff2 b/extension/react-app/src/assets/Mona-Sans.woff2 deleted file mode 100644 index 8208a500..00000000 Binary files a/extension/react-app/src/assets/Mona-Sans.woff2 and /dev/null differ diff --git a/extension/react-app/src/assets/react.svg b/extension/react-app/src/assets/react.svg deleted file mode 100644 index 6c87de9b..00000000 --- a/extension/react-app/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/extension/react-app/src/components/CodeMultiselect.tsx b/extension/react-app/src/components/CodeMultiselect.tsx deleted file mode 100644 index c0ab9400..00000000 --- a/extension/react-app/src/components/CodeMultiselect.tsx +++ /dev/null @@ -1,276 +0,0 @@ -import React, { useEffect, useState } from "react"; -import styled from "styled-components"; -import { Button, buttonColor, defaultBorderRadius, secondaryDark } from "."; -import { useSelector } from "react-redux"; -import { - selectDebugContext, - selectAllRangesInFiles, - selectRangesMask, -} from "../redux/selectors/debugContextSelectors"; -import "../highlight/dark.min.css"; -import hljs from "highlight.js"; -import { postVscMessage } from "../vscode"; -import { RootStore } from "../redux/store"; -import { useDispatch } from "react-redux"; -import { - addRangeInFile, - deleteRangeInFileAt, - toggleSelectionAt, - updateFileSystem, -} from "../redux/slices/debugContexSlice"; -import { RangeInFile } from "../../../src/client"; -import { readRangeInVirtualFileSystem } from "../util"; - -//#region Styled Components - -const MultiSelectContainer = styled.div` - border-radius: ${defaultBorderRadius}; - padding: 4px; - display: flex; - flex-direction: column; - gap: 4px; -`; - -const MultiSelectHeader = styled.div` - display: flex; - justify-content: space-between; - align-items: left; - border-bottom: 1px solid gray; - padding-left: 4px; - padding-right: 4px; - & p { - overflow-wrap: break-word; - word-wrap: break-word; - -ms-wrap-flow: break-word; - overflow: hidden; - } -`; - -const MultiSelectOption = styled.div` - border-radius: ${defaultBorderRadius}; - padding-top: 4px; - cursor: pointer; - background-color: ${secondaryDark}; -`; - -const DeleteSelectedRangeButton = styled(Button)` - align-self: right; - padding: 0px; - margin-top: 0; - aspect-ratio: 1/1; - height: 28px; -`; - -const ToggleHighlightButton = styled(Button)` - display: grid; - justify-content: center; - align-items: center; - grid-template-columns: 30px 1fr; - margin-left: 20px; - order: 1; - width: fit-content; -`; - -//#endregion - -//#region Path Formatting - -const filenameToLanguageMap: any = { - py: "python", - js: "javascript", - ts: "typescript", - html: "html", - css: "css", - java: "java", - c: "c", - cpp: "cpp", - cs: "csharp", - go: "go", - rb: "ruby", - rs: "rust", - swift: "swift", - php: "php", - scala: "scala", - kt: "kotlin", - dart: "dart", - hs: "haskell", - lua: "lua", - pl: "perl", - r: "r", - sql: "sql", - vb: "vb", - xml: "xml", - yaml: "yaml", -}; - -function filenameToLanguage(filename: string): string { - const extension = filename.split(".").pop(); - if (extension === undefined) { - return ""; - } - return filenameToLanguageMap[extension] || ""; -} - -function formatPathRelativeToWorkspace( - path: string, - workspacePath: string | undefined -) { - if (workspacePath === undefined) { - return path; - } - if (path.startsWith(workspacePath)) { - return path.substring(workspacePath.length + 1); - } else { - return path; - } -} - -function formatFileRange( - rangeInFile: RangeInFile, - workspacePath: string | undefined -) { - return `${formatPathRelativeToWorkspace( - rangeInFile.filepath, - workspacePath - )} (lines ${rangeInFile.range.start.line + 1}-${ - rangeInFile.range.end.line + 1 - })`; - // +1 because VS Code Ranges are 0-indexed -} - -//#endregion - -function CodeMultiselect(props: {}) { - // State - const [highlightLocked, setHighlightLocked] = useState(true); - - // Redux - const dispatch = useDispatch(); - const workspacePath = useSelector( - (state: RootStore) => state.config.workspacePath - ); - const debugContext = useSelector(selectDebugContext); - const rangesInFiles = useSelector(selectAllRangesInFiles); - const rangesInFilesMask = useSelector(selectRangesMask); - - useEffect(() => { - let eventListener = (event: any) => { - switch (event.data.type) { - case "highlightedCode": - if (!highlightLocked) { - dispatch( - addRangeInFile({ - rangeInFile: event.data.rangeInFile, - canUpdateLast: true, - }) - ); - dispatch(updateFileSystem(event.data.filesystem)); - } - break; - case "findSuspiciousCode": - for (let c of event.data.codeLocations) { - dispatch(addRangeInFile({ rangeInFile: c, canUpdateLast: false })); - } - dispatch(updateFileSystem(event.data.filesystem)); - postVscMessage("listTenThings", { debugContext }); - break; - } - }; - window.addEventListener("message", eventListener); - return () => window.removeEventListener("message", eventListener); - }, [debugContext, highlightLocked]); - - useEffect(() => { - hljs.highlightAll(); - }, [rangesInFiles]); - - return ( - - {rangesInFiles.map((range: RangeInFile, index: number) => { - return ( - { - dispatch(toggleSelectionAt(index)); - }} - > - -

- {formatFileRange(range, workspacePath)} -

- dispatch(deleteRangeInFileAt(index))} - > - x - -
-
-              
-                {readRangeInVirtualFileSystem(range, debugContext.filesystem)}
-              
-            
-
- ); - })} - {rangesInFiles.length === 0 && ( - <> -

Highlight relevant code in the editor.

- - )} - { - setHighlightLocked(!highlightLocked); - }} - > - {highlightLocked ? ( - <> - - - {" "} - Enable Highlight - - ) : ( - <> - - - {" "} - Disable Highlight - - )} - -
- ); -} - -export default CodeMultiselect; diff --git a/extension/react-app/src/highlight/dark.min.css b/extension/react-app/src/highlight/dark.min.css deleted file mode 100644 index 9268d7c9..00000000 --- a/extension/react-app/src/highlight/dark.min.css +++ /dev/null @@ -1,53 +0,0 @@ -pre code.hljs { - display: block; - overflow-x: auto; - padding: 1em; -} -code.hljs { - padding: 3px 5px; -} -.hljs { - color: #ddd; - background: #252526; -} -.hljs-keyword, -.hljs-link, -.hljs-literal, -.hljs-section, -.hljs-selector-tag { - color: #fff; -} -.hljs-addition, -.hljs-attribute, -.hljs-built_in, -.hljs-bullet, -.hljs-name, -.hljs-string, -.hljs-symbol, -.hljs-template-tag, -.hljs-template-variable, -.hljs-title, -.hljs-type, -.hljs-variable { - color: #d88; -} -.hljs-comment, -.hljs-deletion, -.hljs-meta, -.hljs-quote { - color: #979797; -} -.hljs-doctag, -.hljs-keyword, -.hljs-literal, -.hljs-name, -.hljs-section, -.hljs-selector-tag, -.hljs-strong, -.hljs-title, -.hljs-type { - font-weight: 700; -} -.hljs-emphasis { - font-style: italic; -} diff --git a/extension/react-app/src/util/api.ts b/extension/react-app/src/util/api.ts deleted file mode 100644 index bdec1d20..00000000 --- a/extension/react-app/src/util/api.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - Configuration, - DebugApi, - UnittestApi, - ChatApi, -} from "../../../src/client"; -import { useSelector } from "react-redux"; -import { useEffect, useState } from "react"; -import { RootStore } from "../redux/store"; - -export function useApi() { - const apiUrl = useSelector((state: RootStore) => state.config.apiUrl); - const vscMachineId = useSelector( - (state: RootStore) => state.config.vscMachineId - ); - const [debugApi, setDebugApi] = useState(); - const [unittestApi, setUnittestApi] = useState(); - const [chatApi, setChatApi] = useState(); - - useEffect(() => { - if (apiUrl && vscMachineId) { - let config = new Configuration({ - basePath: apiUrl, - fetchApi: fetch, - middleware: [ - { - pre: async (context) => { - context.init.headers = { - ...context.init.headers, - "x-vsc-machine-id": vscMachineId, - }; - }, - }, - ], - }); - setDebugApi(new DebugApi(config)); - setUnittestApi(new UnittestApi(config)); - setChatApi(new ChatApi(config)); - } - }, [apiUrl, vscMachineId]); - - return { debugApi, unittestApi, chatApi }; -} diff --git a/extension/react-app/src/util/editCache.ts b/extension/react-app/src/util/editCache.ts deleted file mode 100644 index b8071127..00000000 --- a/extension/react-app/src/util/editCache.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { useApi } from "../util/api"; -import { FileEdit, SerializedDebugContext } from "../../../src/client"; -import { useCallback, useEffect, useState } from "react"; - -export function useEditCache() { - const { debugApi } = useApi(); - - const fetchNewEdit = useCallback( - async (debugContext: SerializedDebugContext) => { - return ( - await debugApi?.editEndpointDebugEditPost({ - serializedDebugContext: debugContext, - }) - )?.completion; - }, - [debugApi] - ); - - const [editCache, setEditCache] = useState(new EditCache(fetchNewEdit)); - - useEffect(() => { - setEditCache(new EditCache(fetchNewEdit)); - }, [fetchNewEdit]); - - return editCache; -} - -/** - * Stores preloaded edits, invalidating based off of debug context changes - */ -class EditCache { - private _lastDebugContext: SerializedDebugContext | undefined; - private _cachedEdits: FileEdit[] | undefined; - private _fetchNewEdit: ( - debugContext: SerializedDebugContext - ) => Promise; - private _debounceTimer: NodeJS.Timeout | undefined; - - private _debugContextChanged(debugContext: SerializedDebugContext): boolean { - if (!this._lastDebugContext) { - return true; - } - - return ( - JSON.stringify(this._lastDebugContext) !== JSON.stringify(debugContext) - ); - } - - private _debugContextComplete(debugContext: SerializedDebugContext): boolean { - return debugContext.rangesInFiles.length > 0; - } - - public async preloadEdit(debugContext: SerializedDebugContext) { - if (this._debounceTimer) { - clearTimeout(this._debounceTimer); - } - if ( - this._debugContextComplete(debugContext) && - this._debugContextChanged(debugContext) - ) { - this._debounceTimer = setTimeout(async () => { - console.log("Preloading edits"); - this._cachedEdits = await this._fetchNewEdit(debugContext); - this._lastDebugContext = debugContext; - }, 200); - } - } - - public async getEdit( - debugContext: SerializedDebugContext - ): Promise { - if (this._debugContextChanged(debugContext)) { - console.log("Cache miss"); - this._cachedEdits = await this._fetchNewEdit(debugContext); - } else { - console.log("Cache hit"); - } - - return this._cachedEdits!; - } - - constructor( - fetchNewEdit: ( - debugContext: SerializedDebugContext - ) => Promise - ) { - this._fetchNewEdit = fetchNewEdit; - } -} diff --git a/extension/react-app/src/util/index.ts b/extension/react-app/src/util/index.ts deleted file mode 100644 index 458f9d95..00000000 --- a/extension/react-app/src/util/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { RangeInFile } from "../../../src/client"; - -export function readRangeInVirtualFileSystem( - rangeInFile: RangeInFile, - filesystem: { [filepath: string]: string } -): string | undefined { - const range = rangeInFile.range; - - let data = filesystem[rangeInFile.filepath]; - if (data === undefined) { - console.log("File not found"); - return undefined; - } else { - let lines = data.toString().split("\n"); - if (range.start.line === range.end.line) { - return lines[rangeInFile.range.start.line].slice( - rangeInFile.range.start.character, - rangeInFile.range.end.character - ); - } else { - let firstLine = lines[range.start.line].slice(range.start.character); - let lastLine = lines[range.end.line].slice(0, range.end.character); - let middleLines = lines.slice(range.start.line + 1, range.end.line); - return [firstLine, ...middleLines, lastLine].join("\n"); - } - } -} diff --git a/extension/src/commands.ts b/extension/src/commands.ts index ffb67ab5..0b002549 100644 --- a/extension/src/commands.ts +++ b/extension/src/commands.ts @@ -21,35 +21,6 @@ import { ideProtocolClient } from "./activation/activate"; // Copy everything over from extension.ts const commandsMap: { [command: string]: (...args: any) => any } = { - "continue.askQuestion": (data: any, webviewView: vscode.WebviewView) => { - if (!vscode.workspace.workspaceFolders) { - return; - } - - answerQuestion( - data.question, - vscode.workspace.workspaceFolders[0].uri.fsPath, - webviewView.webview - ); - }, - "continue.askQuestionFromInput": () => { - vscode.window - .showInputBox({ placeHolder: "Ask away!" }) - .then((question) => { - if (!question || !vscode.workspace.workspaceFolders) { - return; - } - - sendTelemetryEvent(TelemetryEvent.UniversalPromptQuery, { - query: question, - }); - - answerQuestion( - question, - vscode.workspace.workspaceFolders[0].uri.fsPath - ); - }); - }, "continue.suggestionDown": suggestionDownCommand, "continue.suggestionUp": suggestionUpCommand, "continue.acceptSuggestion": acceptSuggestionCommand, @@ -73,27 +44,6 @@ const commandsMap: { [command: string]: (...args: any) => any } = { if (text) { ideProtocolClient.sendMainUserInput(text); } - vscode.commands.executeCommand("continue.continueGUIView.focus"); - }, -}; - -const textEditorCommandsMap: { [command: string]: (...args: any) => {} } = { - "continue.writeDocstring": async (editor: vscode.TextEditor, _) => { - sendTelemetryEvent(TelemetryEvent.GenerateDocstring); - let gutterSpinnerKey = showGutterSpinner( - editor, - editor.selection.active.line - ); - - const { lineno, docstring } = await bridge.writeDocstringForFunction( - editor.document.fileName, - editor.selection.active - ); - // Can't use the edit given above after an async call - editor.edit((edit) => { - edit.insert(new vscode.Position(lineno, 0), docstring); - decorationManager.deleteDecoration(gutterSpinnerKey); - }); }, }; @@ -103,44 +53,4 @@ export function registerAllCommands(context: vscode.ExtensionContext) { vscode.commands.registerCommand(command, callback) ); } - - for (const [command, callback] of Object.entries(textEditorCommandsMap)) { - context.subscriptions.push( - vscode.commands.registerTextEditorCommand(command, callback) - ); - } -} - -async function answerQuestion( - question: string, - workspacePath: string, - webview: vscode.Webview | undefined = undefined -) { - vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: "Anwering question...", - cancellable: false, - }, - async (progress, token) => { - try { - let resp = await bridge.askQuestion(question, workspacePath); - // Send the answer back to the webview - if (webview) { - webview.postMessage({ - type: "answerQuestion", - answer: resp.answer, - }); - } - showAnswerInTextEditor(resp.filename, resp.range, resp.answer); - } catch (error: any) { - if (webview) { - webview.postMessage({ - type: "answerQuestion", - answer: error, - }); - } - } - } - ); -} +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 69a1bc9cff896f492a122d87def4632696ce59b2 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 12 Jul 2023 16:10:53 -0700 Subject: catch error at top level when activating extension --- extension/src/activation/activate.ts | 18 ++++++------------ extension/src/extension.ts | 13 ++++++++----- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'extension/src') diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index fe72eaf6..559caf44 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -2,11 +2,9 @@ import * as vscode from "vscode"; import { registerAllCommands } from "../commands"; import { registerAllCodeLensProviders } from "../lang-server/codeLens"; import { sendTelemetryEvent, TelemetryEvent } from "../telemetry"; -// import { openCapturedTerminal } from "../terminal/terminalEmulator"; import IdeProtocolClient from "../continueIdeClient"; import { getContinueServerUrl } from "../bridge"; -import { CapturedTerminal } from "../terminal/terminalEmulator"; -import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel"; +import { ContinueGUIWebviewViewProvider } from "../debugPanel"; import { getExtensionVersion, startContinuePythonServer, @@ -37,6 +35,7 @@ export async function activateExtension(context: vscode.ExtensionContext) { }) .catch((e) => console.log("Error checking for extension updates: ", e)); + // Start the Python server await new Promise((resolve, reject) => { vscode.window.withProgress( { @@ -52,19 +51,19 @@ export async function activateExtension(context: vscode.ExtensionContext) { ); }); + // Register commands and providers sendTelemetryEvent(TelemetryEvent.ExtensionActivated); registerAllCodeLensProviders(context); registerAllCommands(context); + // Initialize IDE Protocol Client, then call "openGUI" const serverUrl = getContinueServerUrl(); - ideProtocolClient = new IdeProtocolClient( `${serverUrl.replace("http", "ws")}/ide/ws`, context ); - // Setup the left panel - (async () => { + { const sessionIdPromise = await ideProtocolClient.getSessionId(); const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise); @@ -77,10 +76,5 @@ export async function activateExtension(context: vscode.ExtensionContext) { } ) ); - })(); - // All opened terminals should be replaced by our own terminal - // vscode.window.onDidOpenTerminal((terminal) => {}); - - // If any terminals are open to start, replace them - // vscode.window.terminals.forEach((terminal) => {} + } } diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 1f9f7914..6959ec05 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -3,14 +3,17 @@ */ import * as vscode from "vscode"; -import { - isPythonEnvSetup, - startContinuePythonServer, -} from "./activation/environmentSetup"; async function dynamicImportAndActivate(context: vscode.ExtensionContext) { const { activateExtension } = await import("./activation/activate"); - await activateExtension(context); + try { + await activateExtension(context); + } catch (e) { + console.log("Error activating extension: ", e); + vscode.window.showInformationMessage( + "Error activating the Continue extension." + ); + } } export function activate(context: vscode.ExtensionContext) { -- cgit v1.2.3-70-g09d2 From b3ab5bda368fcae690837f9ce8062dc7f17c6472 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 12 Jul 2023 16:41:58 -0700 Subject: getVisibleFiles --- continuedev/src/continuedev/core/autopilot.py | 2 ++ continuedev/src/continuedev/core/sdk.py | 10 +++++----- .../src/continuedev/recipes/TemplateRecipe/main.py | 4 ++-- .../src/continuedev/recipes/WritePytestsRecipe/main.py | 2 +- continuedev/src/continuedev/server/ide.py | 8 +++++++- continuedev/src/continuedev/server/ide_protocol.py | 4 ++++ continuedev/src/continuedev/steps/main.py | 18 ++++-------------- continuedev/src/continuedev/steps/welcome.py | 2 +- extension/react-app/src/components/StepContainer.tsx | 4 ++-- extension/src/bridge.ts | 2 +- extension/src/continueIdeClient.ts | 11 +++++++++++ 11 files changed, 40 insertions(+), 27 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index ac00e4f0..1b074435 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -208,6 +208,8 @@ class Autopilot(ContinueBaseModel): async def delete_at_index(self, index: int): self.history.timeline[index].step.hide = True self.history.timeline[index].deleted = True + self.history.timeline[index].active = False + await self.update_subscribers() async def delete_context_at_indices(self, indices: List[int]): diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index a3441ad9..aa2d8892 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -204,14 +204,14 @@ class ContinueSDK(AbstractContinueSDK): preface = "The following code is highlighted" + # If no higlighted ranges, use first file as context if len(highlighted_code) == 0: preface = "The following file is open" - # Get the full contents of all open files - files = await self.ide.getOpenFiles() - if len(files) > 0: - content = await self.ide.readFile(files[0]) + visible_files = await self.ide.getVisibleFiles() + if len(visible_files) > 0: + content = await self.ide.readFile(visible_files[0]) highlighted_code = [ - RangeInFileWithContents.from_entire_file(files[0], content)] + RangeInFileWithContents.from_entire_file(visible_files[0], content)] for rif in highlighted_code: msg = ChatMessage(content=f"{preface} ({rif.filepath}):\n```\n{rif.contents}\n```", diff --git a/continuedev/src/continuedev/recipes/TemplateRecipe/main.py b/continuedev/src/continuedev/recipes/TemplateRecipe/main.py index 94675725..16132cfd 100644 --- a/continuedev/src/continuedev/recipes/TemplateRecipe/main.py +++ b/continuedev/src/continuedev/recipes/TemplateRecipe/main.py @@ -20,8 +20,8 @@ class TemplateRecipe(Step): # The code executed when the recipe is run async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - open_files = await sdk.ide.getOpenFiles() + visible_files = await sdk.ide.getVisibleFiles() await sdk.edit_file( - filename=open_files[0], + filename=visible_files[0], prompt=f"Append a statement to print `Hello, {self.name}!` at the end of the file." ) diff --git a/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py b/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py index 6e1244b3..c7a65fa6 100644 --- a/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py +++ b/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py @@ -14,7 +14,7 @@ class WritePytestsRecipe(Step): async def run(self, sdk: ContinueSDK): if self.for_filepath is None: - self.for_filepath = (await sdk.ide.getOpenFiles())[0] + self.for_filepath = (await sdk.ide.getVisibleFiles())[0] filename = os.path.basename(self.for_filepath) dirname = os.path.dirname(self.for_filepath) diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 400ad740..4645b49e 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -52,6 +52,8 @@ class FileEditsUpdate(BaseModel): class OpenFilesResponse(BaseModel): openFiles: List[str] +class VisibleFilesResponse(BaseModel): + visibleFiles: List[str] class HighlightedCodeResponse(BaseModel): highlightedCode: List[RangeInFile] @@ -180,7 +182,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): self.onMainUserInput(data["input"]) elif message_type == "deleteAtIndex": self.onDeleteAtIndex(data["index"]) - elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "getUserSecret", "runCommand", "uniqueId"]: + elif message_type in ["highlightedCode", "openFiles", "visibleFiles", "readFile", "editFile", "getUserSecret", "runCommand", "uniqueId"]: self.sub_queue.post(message_type, data) elif message_type == "workspaceDirectory": self.workspace_directory = data["workspaceDirectory"] @@ -302,6 +304,10 @@ class IdeProtocolServer(AbstractIdeProtocolServer): async def getOpenFiles(self) -> List[str]: resp = await self._send_and_receive_json({}, OpenFilesResponse, "openFiles") return resp.openFiles + + async def getVisibleFiles(self) -> List[str]: + resp = await self._send_and_receive_json({}, VisibleFilesResponse, "visibleFiles") + return resp.visibleFiles async def get_unique_id(self) -> str: resp = await self._send_and_receive_json({}, UniqueIdResponse, "uniqueId") diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 69cb6c10..2783dc61 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -51,6 +51,10 @@ class AbstractIdeProtocolServer(ABC): async def getOpenFiles(self) -> List[str]: """Get a list of open files""" + @abstractmethod + async def getVisibleFiles(self) -> List[str]: + """Get a list of visible files""" + @abstractmethod async def getHighlightedCode(self) -> List[RangeInFile]: """Get a list of highlighted code""" diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py index e6ef9281..ce7cbc60 100644 --- a/continuedev/src/continuedev/steps/main.py +++ b/continuedev/src/continuedev/steps/main.py @@ -99,8 +99,8 @@ class FasterEditHighlightedCodeStep(Step): async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: range_in_files = await sdk.get_code_context(only_editing=True) if len(range_in_files) == 0: - # Get the full contents of all open files - files = await sdk.ide.getOpenFiles() + # Get the full contents of all visible files + files = await sdk.ide.getVisibleFiles() contents = {} for file in files: contents[file] = await sdk.ide.readFile(file) @@ -191,8 +191,8 @@ class StarCoderEditHighlightedCodeStep(Step): range_in_files = await sdk.get_code_context(only_editing=True) found_highlighted_code = len(range_in_files) > 0 if not found_highlighted_code: - # Get the full contents of all open files - files = await sdk.ide.getOpenFiles() + # Get the full contents of all visible files + files = await sdk.ide.getVisibleFiles() contents = {} for file in files: contents[file] = await sdk.ide.readFile(file) @@ -275,16 +275,6 @@ class EditHighlightedCodeStep(Step): await sdk.run_step(DefaultModelEditCodeStep(user_input=self.user_input, range_in_files=range_in_files)) -class FindCodeStep(Step): - prompt: str - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "Finding code" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - return await sdk.ide.getOpenFiles() - - class UserInputStep(Step): user_input: str diff --git a/continuedev/src/continuedev/steps/welcome.py b/continuedev/src/continuedev/steps/welcome.py index 32ebc3ba..2dece649 100644 --- a/continuedev/src/continuedev/steps/welcome.py +++ b/continuedev/src/continuedev/steps/welcome.py @@ -29,4 +29,4 @@ class WelcomeStep(Step): - Ask about how the class works, how to write it in another language, etc. \"\"\""""))) - await sdk.ide.setFileOpen(filepath=filepath) + # await sdk.ide.setFileOpen(filepath=filepath) diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index d480c565..d1a8a46a 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -158,7 +158,7 @@ function StepContainer(props: StepContainerProps) { >
diff --git a/extension/src/bridge.ts b/extension/src/bridge.ts index 55c4cc3b..7e6398be 100644 --- a/extension/src/bridge.ts +++ b/extension/src/bridge.ts @@ -50,7 +50,7 @@ export function getContinueServerUrl() { extensionContext && extensionContext.extensionMode === vscode.ExtensionMode.Development ) { - // return "http://localhost:8001"; + return "http://localhost:8001"; } return ( vscode.workspace.getConfiguration("continue").get("serverUrl") || diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 304c592b..b728833f 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -131,6 +131,11 @@ class IdeProtocolClient { openFiles: this.getOpenFiles(), }); break; + case "visibleFiles": + messenger.send("visibleFiles", { + visibleFiles: this.getVisibleFiles(), + }); + break; case "readFile": messenger.send("readFile", { contents: this.readFile(data.filepath), @@ -330,6 +335,12 @@ class IdeProtocolClient { }); } + getVisibleFiles(): string[] { + return vscode.window.visibleTextEditors.map((editor) => { + return editor.document.uri.fsPath; + }); + } + saveFile(filepath: string) { vscode.window.visibleTextEditors.forEach((editor) => { if (editor.document.uri.fsPath === filepath) { -- cgit v1.2.3-70-g09d2 From 391764f1371dab06af30a29e10a826a516b69bb3 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 12 Jul 2023 21:53:06 -0700 Subject: persist state and reconnect automatically --- continuedev/src/continuedev/core/autopilot.py | 17 +++- continuedev/src/continuedev/libs/constants/main.py | 6 ++ continuedev/src/continuedev/libs/util/paths.py | 17 ++++ continuedev/src/continuedev/server/gui.py | 10 +- continuedev/src/continuedev/server/ide.py | 25 +++-- continuedev/src/continuedev/server/ide_protocol.py | 10 +- continuedev/src/continuedev/server/main.py | 16 ++- .../src/continuedev/server/session_manager.py | 41 ++++++-- docs/docs/concepts/ide.md | 4 +- extension/react-app/src/components/ComboBox.tsx | 1 - extension/react-app/src/hooks/messenger.ts | 10 -- extension/react-app/src/pages/gui.tsx | 1 - extension/src/activation/activate.ts | 2 +- extension/src/continueIdeClient.ts | 62 ++++++++--- extension/src/debugPanel.ts | 113 ++++----------------- extension/src/util/messenger.ts | 10 ++ 16 files changed, 192 insertions(+), 153 deletions(-) create mode 100644 continuedev/src/continuedev/libs/constants/main.py create mode 100644 continuedev/src/continuedev/libs/util/paths.py (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 1b074435..e1c8a076 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -1,13 +1,13 @@ from functools import cached_property import traceback import time -from typing import Any, Callable, Coroutine, Dict, List +from typing import Any, Callable, Coroutine, Dict, List, Union import os from aiohttp import ClientPayloadError +from pydantic import root_validator from ..models.filesystem import RangeInFileWithContents from ..models.filesystem_edit import FileEditWithFullContents -from ..libs.llm import LLM from .observation import Observation, InternalErrorObservation from ..server.ide_protocol import AbstractIdeProtocolServer from ..libs.util.queue import AsyncSubscriptionQueue @@ -16,7 +16,6 @@ from .main import Context, ContinueCustomException, HighlightedRangeContext, Pol from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep from ..libs.util.telemetry import capture_event from .sdk import ContinueSDK -import asyncio from ..libs.util.step_name_to_steps import get_step_from_name from ..libs.util.traceback_parsers import get_python_traceback, get_javascript_traceback from openai import error as openai_errors @@ -46,6 +45,7 @@ class Autopilot(ContinueBaseModel): ide: AbstractIdeProtocolServer history: History = History.from_empty() context: Context = Context() + full_state: Union[FullState, None] = None _on_update_callbacks: List[Callable[[FullState], None]] = [] _active: bool = False @@ -63,8 +63,15 @@ class Autopilot(ContinueBaseModel): arbitrary_types_allowed = True keep_untouched = (cached_property,) + @root_validator(pre=True) + def fill_in_values(cls, values): + full_state: FullState = values.get('full_state') + if full_state is not None: + values['history'] = full_state.history + return values + def get_full_state(self) -> FullState: - return FullState( + full_state = FullState( history=self.history, active=self._active, user_input_queue=self._main_user_input_queue, @@ -73,6 +80,8 @@ class Autopilot(ContinueBaseModel): slash_commands=self.get_available_slash_commands(), adding_highlighted_code=self._adding_highlighted_code, ) + self.full_state = full_state + return full_state def get_available_slash_commands(self) -> List[Dict]: custom_commands = list(map(lambda x: { diff --git a/continuedev/src/continuedev/libs/constants/main.py b/continuedev/src/continuedev/libs/constants/main.py new file mode 100644 index 00000000..96eb6e69 --- /dev/null +++ b/continuedev/src/continuedev/libs/constants/main.py @@ -0,0 +1,6 @@ +## PATHS ## + +CONTINUE_GLOBAL_FOLDER = ".continue" +CONTINUE_SESSIONS_FOLDER = "sessions" +CONTINUE_SERVER_FOLDER = "server" + diff --git a/continuedev/src/continuedev/libs/util/paths.py b/continuedev/src/continuedev/libs/util/paths.py new file mode 100644 index 00000000..fddef887 --- /dev/null +++ b/continuedev/src/continuedev/libs/util/paths.py @@ -0,0 +1,17 @@ +import os + +from ..constants.main import CONTINUE_SESSIONS_FOLDER, CONTINUE_GLOBAL_FOLDER, CONTINUE_SERVER_FOLDER + +def getGlobalFolderPath(): + return os.path.join(os.path.expanduser("~"), CONTINUE_GLOBAL_FOLDER) + + + +def getSessionsFolderPath(): + return os.path.join(getGlobalFolderPath(), CONTINUE_SESSIONS_FOLDER) + +def getServerFolderPath(): + return os.path.join(getGlobalFolderPath(), CONTINUE_SERVER_FOLDER) + +def getSessionFilePath(session_id: str): + return os.path.join(getSessionsFolderPath(), f"{session_id}.json") \ No newline at end of file diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 21089f30..8f6f68f6 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -31,12 +31,12 @@ class AppStatus: Server.handle_exit = AppStatus.handle_exit -def session(x_continue_session_id: str = Header("anonymous")) -> Session: - return session_manager.get_session(x_continue_session_id) +async def session(x_continue_session_id: str = Header("anonymous")) -> Session: + return await session_manager.get_session(x_continue_session_id) -def websocket_session(session_id: str) -> Session: - return session_manager.get_session(session_id) +async def websocket_session(session_id: str) -> Session: + return await session_manager.get_session(session_id) T = TypeVar("T", bound=BaseModel) @@ -199,4 +199,6 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we print("Closing gui websocket") if websocket.client_state != WebSocketState.DISCONNECTED: await websocket.close() + + session_manager.persist_session(session.session_id) session_manager.remove_session(session.session_id) diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 4645b49e..12a21f19 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -52,9 +52,11 @@ class FileEditsUpdate(BaseModel): class OpenFilesResponse(BaseModel): openFiles: List[str] + class VisibleFilesResponse(BaseModel): visibleFiles: List[str] + class HighlightedCodeResponse(BaseModel): highlightedCode: List[RangeInFile] @@ -115,6 +117,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): websocket: WebSocket session_manager: SessionManager sub_queue: AsyncSubscriptionQueue = AsyncSubscriptionQueue() + session_id: Union[str, None] = None def __init__(self, session_manager: SessionManager, websocket: WebSocket): self.websocket = websocket @@ -132,8 +135,6 @@ class IdeProtocolServer(AbstractIdeProtocolServer): continue message_type = message["messageType"] data = message["data"] - # if message_type == "openGUI": - # await self.openGUI() if message_type == "workspaceDirectory": self.workspace_directory = data["workspaceDirectory"] break @@ -158,8 +159,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer): return resp_model.parse_obj(resp) async def handle_json(self, message_type: str, data: Any): - if message_type == "openGUI": - await self.openGUI() + if message_type == "getSessionId": + await self.getSessionId() elif message_type == "setFileOpen": await self.setFileOpen(data["filepath"], data["open"]) elif message_type == "setSuggestionsLocked": @@ -217,9 +218,10 @@ class IdeProtocolServer(AbstractIdeProtocolServer): "locked": locked }) - async def openGUI(self): - session_id = self.session_manager.new_session(self) - await self._send_json("openGUI", { + async def getSessionId(self): + session_id = self.session_manager.new_session( + self, self.session_id).session_id + await self._send_json("getSessionId", { "sessionId": session_id }) @@ -304,7 +306,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): async def getOpenFiles(self) -> List[str]: resp = await self._send_and_receive_json({}, OpenFilesResponse, "openFiles") return resp.openFiles - + async def getVisibleFiles(self) -> List[str]: resp = await self._send_and_receive_json({}, VisibleFilesResponse, "visibleFiles") return resp.visibleFiles @@ -416,7 +418,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): @router.websocket("/ws") -async def websocket_endpoint(websocket: WebSocket): +async def websocket_endpoint(websocket: WebSocket, session_id: str = None): try: await websocket.accept() print("Accepted websocket connection from, ", websocket.client) @@ -434,6 +436,9 @@ async def websocket_endpoint(websocket: WebSocket): ideProtocolServer.handle_json(message_type, data)) ideProtocolServer = IdeProtocolServer(session_manager, websocket) + ideProtocolServer.session_id = session_id + if session_id is not None: + session_manager.registered_ides[session_id] = ideProtocolServer other_msgs = await ideProtocolServer.initialize() for other_msg in other_msgs: @@ -454,3 +459,5 @@ async def websocket_endpoint(websocket: WebSocket): finally: if websocket.client_state != WebSocketState.DISCONNECTED: await websocket.close() + + session_manager.registered_ides.pop(ideProtocolServer.session_id) diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 2783dc61..2f78cf0e 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -1,5 +1,6 @@ -from typing import Any, List +from typing import Any, List, Union from abc import ABC, abstractmethod, abstractproperty +from fastapi import WebSocket from ..models.main import Traceback from ..models.filesystem_edit import FileEdit, FileSystemEdit, EditDiff @@ -7,6 +8,9 @@ from ..models.filesystem import RangeInFile, RangeInFileWithContents class AbstractIdeProtocolServer(ABC): + websocket: WebSocket + session_id: Union[str, None] + @abstractmethod async def handle_json(self, data: Any): """Handle a json message""" @@ -24,8 +28,8 @@ class AbstractIdeProtocolServer(ABC): """Set whether suggestions are locked""" @abstractmethod - async def openGUI(self): - """Open a GUI""" + async def getSessionId(self): + """Get a new session ID""" @abstractmethod async def showSuggestionsAndWait(self, suggestions: List[FileEdit]) -> bool: diff --git a/continuedev/src/continuedev/server/main.py b/continuedev/src/continuedev/server/main.py index f4d82903..aa093853 100644 --- a/continuedev/src/continuedev/server/main.py +++ b/continuedev/src/continuedev/server/main.py @@ -4,7 +4,8 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from .ide import router as ide_router from .gui import router as gui_router -import logging +from .session_manager import session_manager +import atexit import uvicorn import argparse @@ -44,5 +45,16 @@ def run_server(): uvicorn.run(app, host="0.0.0.0", port=args.port) +def cleanup(): + print("Cleaning up sessions") + for session_id in session_manager.sessions: + session_manager.persist_session(session_id) + + +atexit.register(cleanup) if __name__ == "__main__": - run_server() + try: + run_server() + except Exception as e: + cleanup() + raise e diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py index 7147dcfa..fb8ac386 100644 --- a/continuedev/src/continuedev/server/session_manager.py +++ b/continuedev/src/continuedev/server/session_manager.py @@ -1,9 +1,12 @@ -from asyncio import BaseEventLoop +import os from fastapi import WebSocket from typing import Any, Dict, List, Union from uuid import uuid4 +import json +from ..libs.util.paths import getSessionFilePath, getSessionsFolderPath from ..models.filesystem_edit import FileEditWithFullContents +from ..libs.constants.main import CONTINUE_SESSIONS_FOLDER from ..core.policy import DemoPolicy from ..core.main import FullState from ..core.autopilot import Autopilot @@ -39,17 +42,35 @@ class DemoAutopilot(Autopilot): class SessionManager: sessions: Dict[str, Session] = {} + # Mapping of session_id to IDE, where the IDE is still alive + registered_ides: Dict[str, AbstractIdeProtocolServer] = {} - def get_session(self, session_id: str) -> Session: + async def get_session(self, session_id: str) -> Session: if session_id not in self.sessions: + # Check then whether it is persisted by listing all files in the sessions folder + # And only if the IDE is still alive + sessions_folder = getSessionsFolderPath() + session_files = os.listdir(sessions_folder) + if f"{session_id}.json" in session_files and session_id in self.registered_ides: + if self.registered_ides[session_id].session_id is not None: + return self.new_session(self.registered_ides[session_id], session_id=session_id) + raise KeyError("Session ID not recognized", session_id) return self.sessions[session_id] - def new_session(self, ide: AbstractIdeProtocolServer) -> str: - autopilot = DemoAutopilot(policy=DemoPolicy(), ide=ide) - session_id = str(uuid4()) + def new_session(self, ide: AbstractIdeProtocolServer, session_id: Union[str, None] = None) -> Session: + full_state = None + if session_id is not None and os.path.exists(getSessionFilePath(session_id)): + with open(getSessionFilePath(session_id), "r") as f: + full_state = FullState(**json.load(f)) + + autopilot = DemoAutopilot( + policy=DemoPolicy(), ide=ide, full_state=full_state) + session_id = session_id or str(uuid4()) + ide.session_id = session_id session = Session(session_id=session_id, autopilot=autopilot) self.sessions[session_id] = session + self.registered_ides[session_id] = ide async def on_update(state: FullState): await session_manager.send_ws_data(session_id, "state_update", { @@ -58,11 +79,19 @@ class SessionManager: autopilot.on_update(on_update) create_async_task(autopilot.run_policy()) - return session_id + return session def remove_session(self, session_id: str): del self.sessions[session_id] + def persist_session(self, session_id: str): + """Save the session's FullState as a json file""" + full_state = self.sessions[session_id].autopilot.get_full_state() + if not os.path.exists(getSessionsFolderPath()): + os.mkdir(getSessionsFolderPath()) + with open(getSessionFilePath(session_id), "w") as f: + json.dump(full_state.dict(), f) + def register_websocket(self, session_id: str, ws: WebSocket): self.sessions[session_id].ws = ws print("Registered websocket for session", session_id) diff --git a/docs/docs/concepts/ide.md b/docs/docs/concepts/ide.md index dc7b9e23..bd31481b 100644 --- a/docs/docs/concepts/ide.md +++ b/docs/docs/concepts/ide.md @@ -41,9 +41,9 @@ Get the workspace directory Set whether a file is open -### openGUI +### getSessionId -Open a gui +Get a new session ID ### showSuggestionsAndWait diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index ac994b0a..7d6541c7 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -331,7 +331,6 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { ) { (event.nativeEvent as any).preventDownshiftDefault = true; } else if (event.key === "ArrowUp") { - console.log("OWJFOIJO"); if (positionInHistory == 0) return; else if ( positionInHistory == history.length && diff --git a/extension/react-app/src/hooks/messenger.ts b/extension/react-app/src/hooks/messenger.ts index e2a0bab8..00ce1fbb 100644 --- a/extension/react-app/src/hooks/messenger.ts +++ b/extension/react-app/src/hooks/messenger.ts @@ -1,6 +1,3 @@ -// console.log("Websocket import"); -// const WebSocket = require("ws"); - export abstract class Messenger { abstract send(messageType: string, data: object): void; @@ -28,13 +25,6 @@ export class WebsocketMessenger extends Messenger { 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); diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index b9382bd1..4ff260fa 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -262,7 +262,6 @@ function GUI(props: GUIProps) { const onStepUserInput = (input: string, index: number) => { if (!client) return; - console.log("Sending step user input", input, index); client.sendStepUserInput(input, index); }; diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 559caf44..cd885b12 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -56,7 +56,7 @@ export async function activateExtension(context: vscode.ExtensionContext) { registerAllCodeLensProviders(context); registerAllCommands(context); - // Initialize IDE Protocol Client, then call "openGUI" + // Initialize IDE Protocol Client const serverUrl = getContinueServerUrl(); ideProtocolClient = new IdeProtocolClient( `${serverUrl.replace("http", "ws")}/ide/ws`, diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index b728833f..4c1fdf1e 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -1,10 +1,9 @@ -// import { ShowSuggestionRequest } from "../schema/ShowSuggestionRequest"; import { editorSuggestionsLocked, showSuggestion as showSuggestionInEditor, SuggestionRanges, } from "./suggestions"; -import { openEditorAndRevealRange, getRightViewColumn } from "./util/vscode"; +import { openEditorAndRevealRange } from "./util/vscode"; import { FileEdit } from "../schema/FileEdit"; import { RangeInFile } from "../schema/RangeInFile"; import * as vscode from "vscode"; @@ -15,8 +14,6 @@ import { import { FileEditWithFullContents } from "../schema/FileEditWithFullContents"; import fs = require("fs"); import { WebsocketMessenger } from "./util/messenger"; -import * as path from "path"; -import * as os from "os"; import { diffManager } from "./diffs"; class IdeProtocolClient { @@ -27,17 +24,54 @@ class IdeProtocolClient { private _highlightDebounce: NodeJS.Timeout | null = null; - constructor(serverUrl: string, context: vscode.ExtensionContext) { - this.context = context; + private _lastReloadTime: number = 16; + private _reconnectionTimeouts: NodeJS.Timeout[] = []; + + private _sessionId: string | null = null; + private _serverUrl: string; - let messenger = new WebsocketMessenger(serverUrl); + private _newWebsocketMessenger() { + const requestUrl = + this._serverUrl + + (this._sessionId ? `?session_id=${this._sessionId}` : ""); + const messenger = new WebsocketMessenger(requestUrl); this.messenger = messenger; - messenger.onClose(() => { + + const reconnect = () => { + console.log("Trying to reconnect IDE protocol websocket..."); this.messenger = null; + + // Exponential backoff to reconnect + this._reconnectionTimeouts.forEach((to) => clearTimeout(to)); + + const timeout = setTimeout(() => { + if (this.messenger?.websocket?.readyState === 1) { + return; + } + this._newWebsocketMessenger(); + }, this._lastReloadTime); + + this._reconnectionTimeouts.push(timeout); + this._lastReloadTime = Math.min(2 * this._lastReloadTime, 5000); + }; + messenger.onOpen(() => { + this._reconnectionTimeouts.forEach((to) => clearTimeout(to)); + }); + messenger.onClose(() => { + reconnect(); + }); + messenger.onError(() => { + reconnect(); }); messenger.onMessage((messageType, data, messenger) => { this.handleMessage(messageType, data, messenger); }); + } + + constructor(serverUrl: string, context: vscode.ExtensionContext) { + this.context = context; + this._serverUrl = serverUrl; + this._newWebsocketMessenger(); // Setup listeners for any file changes in open editors // vscode.workspace.onDidChangeTextDocument((event) => { @@ -171,7 +205,7 @@ class IdeProtocolClient { case "showDiff": this.showDiff(data.filepath, data.replacement, data.step_index); break; - case "openGUI": + case "getSessionId": case "connected": break; default: @@ -284,10 +318,6 @@ class IdeProtocolClient { // ------------------------------------ // // Initiate Request - async openGUI(asRightWebviewPanel: boolean = false) { - // Open the webview panel - } - async getSessionId(): Promise { await new Promise((resolve, reject) => { // Repeatedly try to connect to the server @@ -303,10 +333,10 @@ class IdeProtocolClient { } }, 1000); }); - const resp = await this.messenger?.sendAndReceive("openGUI", {}); - const sessionId = resp.sessionId; + const resp = await this.messenger?.sendAndReceive("getSessionId", {}); // console.log("New Continue session with ID: ", sessionId); - return sessionId; + this._sessionId = resp.sessionId; + return resp.sessionId; } acceptRejectSuggestion(accept: boolean, key: SuggestionRanges) { diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 487bbedf..5e1689d1 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -8,76 +8,6 @@ import { import { RangeInFile } from "./client"; const WebSocket = require("ws"); -class StreamManager { - private _fullText: string = ""; - private _insertionPoint: vscode.Position | undefined; - - private _addToEditor(update: string) { - let editor = - vscode.window.activeTextEditor || vscode.window.visibleTextEditors[0]; - - if (typeof this._insertionPoint === "undefined") { - if (editor?.selection.isEmpty) { - this._insertionPoint = editor?.selection.active; - } else { - this._insertionPoint = editor?.selection.end; - } - } - editor?.edit((editBuilder) => { - if (this._insertionPoint) { - editBuilder.insert(this._insertionPoint, update); - this._insertionPoint = this._insertionPoint.translate( - Array.from(update.matchAll(/\n/g)).length, - update.length - ); - } - }); - } - - public closeStream() { - this._fullText = ""; - this._insertionPoint = undefined; - this._codeBlockStatus = "closed"; - this._pendingBackticks = 0; - } - - private _codeBlockStatus: "open" | "closed" | "language-descriptor" = - "closed"; - private _pendingBackticks: number = 0; - public onStreamUpdate(update: string) { - let textToInsert = ""; - for (let i = 0; i < update.length; i++) { - switch (this._codeBlockStatus) { - case "closed": - if (update[i] === "`" && this._fullText.endsWith("``")) { - this._codeBlockStatus = "language-descriptor"; - } - break; - case "language-descriptor": - if (update[i] === " " || update[i] === "\n") { - this._codeBlockStatus = "open"; - } - break; - case "open": - if (update[i] === "`") { - if (this._fullText.endsWith("``")) { - this._codeBlockStatus = "closed"; - this._pendingBackticks = 0; - } else { - this._pendingBackticks += 1; - } - } else { - textToInsert += "`".repeat(this._pendingBackticks) + update[i]; - this._pendingBackticks = 0; - } - break; - } - this._fullText += update[i]; - } - this._addToEditor(textToInsert); - } -} - let websocketConnections: { [url: string]: WebsocketConnection | undefined } = {}; @@ -127,8 +57,6 @@ class WebsocketConnection { } } -let streamManager = new StreamManager(); - export let debugPanelWebview: vscode.Webview | undefined; export function setupDebugPanel( panel: vscode.WebviewPanel | vscode.WebviewView, @@ -147,10 +75,7 @@ export function setupDebugPanel( .toString(); const isProduction = true; // context?.extensionMode === vscode.ExtensionMode.Development; - if (!isProduction) { - scriptUri = "http://localhost:5173/src/main.tsx"; - styleMainUri = "http://localhost:5173/src/main.css"; - } else { + if (isProduction) { scriptUri = debugPanelWebview .asWebviewUri( vscode.Uri.joinPath(extensionUri, "react-app/dist/assets/index.js") @@ -161,6 +86,9 @@ export function setupDebugPanel( vscode.Uri.joinPath(extensionUri, "react-app/dist/assets/index.css") ) .toString(); + } else { + scriptUri = "http://localhost:5173/src/main.tsx"; + styleMainUri = "http://localhost:5173/src/main.css"; } panel.webview.options = { @@ -175,11 +103,11 @@ export function setupDebugPanel( return; } - let rangeInFile: RangeInFile = { + const rangeInFile: RangeInFile = { range: e.selections[0], filepath: e.textEditor.document.fileName, }; - let filesystem = { + const filesystem = { [rangeInFile.filepath]: e.textEditor.document.getText(), }; panel.webview.postMessage({ @@ -217,13 +145,19 @@ export function setupDebugPanel( url, }); }; - const connection = new WebsocketConnection( - url, - onMessage, - onOpen, - onClose - ); - websocketConnections[url] = connection; + try { + const connection = new WebsocketConnection( + url, + onMessage, + onOpen, + onClose + ); + websocketConnections[url] = connection; + resolve(null); + } catch (e) { + console.log("Caught it!: ", e); + reject(e); + } }); } @@ -292,15 +226,6 @@ export function setupDebugPanel( openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One); break; } - case "streamUpdate": { - // Write code at the position of the cursor - streamManager.onStreamUpdate(data.update); - break; - } - case "closeStream": { - streamManager.closeStream(); - break; - } case "withProgress": { // This message allows withProgress to be used in the webview if (data.done) { diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index b1df161b..7fd71ddd 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -15,6 +15,8 @@ export abstract class Messenger { abstract onOpen(callback: () => void): void; abstract onClose(callback: () => void): void; + + abstract onError(callback: () => void): void; abstract sendAndReceive(messageType: string, data: any): Promise; } @@ -26,6 +28,7 @@ export class WebsocketMessenger extends Messenger { } = {}; private onOpenListeners: (() => void)[] = []; private onCloseListeners: (() => void)[] = []; + private onErrorListeners: (() => void)[] = []; private serverUrl: string; _newWebsocket(): WebSocket { @@ -43,6 +46,9 @@ export class WebsocketMessenger extends Messenger { for (const listener of this.onCloseListeners) { this.onClose(listener); } + for (const listener of this.onErrorListeners) { + this.onError(listener); + } for (const messageType in this.onMessageListeners) { for (const listener of this.onMessageListeners[messageType]) { this.onMessageType(messageType, listener); @@ -151,4 +157,8 @@ export class WebsocketMessenger extends Messenger { onClose(callback: () => void): void { this.websocket.addEventListener("close", callback); } + + onError(callback: () => void): void { + this.websocket.addEventListener("error", callback); + } } -- cgit v1.2.3-70-g09d2 From 699a74250fd4cf91af930ff63077aeb81f74856f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 13 Jul 2023 09:55:09 -0700 Subject: show react immediately --- extension/src/activation/activate.ts | 72 ++++++++++++++++++------------------ extension/src/bridge.ts | 6 +-- 2 files changed, 38 insertions(+), 40 deletions(-) (limited to 'extension/src') diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index cd885b12..b03282e5 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -35,46 +35,48 @@ export async function activateExtension(context: vscode.ExtensionContext) { }) .catch((e) => console.log("Error checking for extension updates: ", e)); - // Start the Python server - await new Promise((resolve, reject) => { - vscode.window.withProgress( + const sessionIdPromise = (async () => { + // Start the Python server + await new Promise((resolve, reject) => { + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: + "Starting Continue Server... (it may take a minute to download Python packages)", + cancellable: false, + }, + async (progress, token) => { + await startContinuePythonServer(); + resolve(null); + } + ); + }); + + // Initialize IDE Protocol Client + const serverUrl = getContinueServerUrl(); + ideProtocolClient = new IdeProtocolClient( + `${serverUrl.replace("http", "ws")}/ide/ws`, + context + ); + + return ideProtocolClient.getSessionId(); + })(); + + // Register the webview + const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise); + + context.subscriptions.push( + vscode.window.registerWebviewViewProvider( + "continue.continueGUIView", + provider, { - location: vscode.ProgressLocation.Notification, - title: - "Starting Continue Server... (it may take a minute to download Python packages)", - cancellable: false, - }, - async (progress, token) => { - await startContinuePythonServer(); - resolve(null); + webviewOptions: { retainContextWhenHidden: true }, } - ); - }); + ) + ); // Register commands and providers sendTelemetryEvent(TelemetryEvent.ExtensionActivated); registerAllCodeLensProviders(context); registerAllCommands(context); - - // Initialize IDE Protocol Client - const serverUrl = getContinueServerUrl(); - ideProtocolClient = new IdeProtocolClient( - `${serverUrl.replace("http", "ws")}/ide/ws`, - context - ); - - { - const sessionIdPromise = await ideProtocolClient.getSessionId(); - const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise); - - context.subscriptions.push( - vscode.window.registerWebviewViewProvider( - "continue.continueGUIView", - provider, - { - webviewOptions: { retainContextWhenHidden: true }, - } - ) - ); - } } diff --git a/extension/src/bridge.ts b/extension/src/bridge.ts index 7e6398be..d614ace4 100644 --- a/extension/src/bridge.ts +++ b/extension/src/bridge.ts @@ -1,11 +1,7 @@ import fetch from "node-fetch"; import * as path from "path"; import * as vscode from "vscode"; -import { - Configuration, - DebugApi, - UnittestApi, -} from "./client"; +import { Configuration, DebugApi, UnittestApi } from "./client"; import { convertSingleToDoubleQuoteJSON } from "./util/util"; import { getExtensionUri } from "./util/vscode"; import { extensionContext } from "./activation/activate"; -- cgit v1.2.3-70-g09d2 From 01419c661430f6d100f086513876201cf9e0f0f6 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 13 Jul 2023 14:50:10 -0700 Subject: diff editor infer filepath, codelens in middle --- continuedev/src/continuedev/steps/core/core.py | 98 ++++++++++++++------------ extension/src/diffs.ts | 65 ++++++++++++++--- extension/src/lang-server/codeLens.ts | 8 ++- 3 files changed, 114 insertions(+), 57 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 5ea95104..787da316 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -486,58 +486,64 @@ Please output the code to be inserted at the cursor in order to fulfill the user completion_lines_covered = 0 repeating_file_suffix = False line_below_highlighted_range = file_suffix.lstrip().split("\n")[0] - async for chunk in model_to_use.stream_chat(messages, temperature=0, max_tokens=max_tokens): - # Stop early if it is repeating the file_suffix or the step was deleted - if repeating_file_suffix: - break - if sdk.current_step_was_deleted(): - return - # Accumulate lines - if "content" not in chunk: - continue - chunk = chunk["content"] - chunk_lines = chunk.split("\n") - chunk_lines[0] = unfinished_line + chunk_lines[0] - if chunk.endswith("\n"): - unfinished_line = "" - chunk_lines.pop() # because this will be an empty string - else: - unfinished_line = chunk_lines.pop() - - # Deal with newly accumulated lines - for i in range(len(chunk_lines)): - # Trailing whitespace doesn't matter - chunk_lines[i] = chunk_lines[i].rstrip() - chunk_lines[i] = common_whitespace + chunk_lines[i] - - # Lines that should signify the end of generation - if self.is_end_line(chunk_lines[i]): - break - # Lines that should be ignored, like the <> tags - elif self.line_to_be_ignored(chunk_lines[i], completion_lines_covered == 0): - continue - # Check if we are currently just copying the prefix - elif (lines_of_prefix_copied > 0 or completion_lines_covered == 0) and lines_of_prefix_copied < len(file_prefix.splitlines()) and chunk_lines[i] == full_file_contents_lines[lines_of_prefix_copied]: - # This is a sketchy way of stopping it from repeating the file_prefix. Is a bug if output happens to have a matching line - lines_of_prefix_copied += 1 - continue - # Because really short lines might be expected to be repeated, this is only a !heuristic! - # Stop when it starts copying the file_suffix - elif chunk_lines[i].strip() == line_below_highlighted_range.strip() and len(chunk_lines[i].strip()) > 4 and not (len(original_lines_below_previous_blocks) > 0 and chunk_lines[i].strip() == original_lines_below_previous_blocks[0].strip()): - repeating_file_suffix = True + generator = model_to_use.stream_chat( + messages, temperature=0, max_tokens=max_tokens) + + try: + async for chunk in generator: + # Stop early if it is repeating the file_suffix or the step was deleted + if repeating_file_suffix: break + if sdk.current_step_was_deleted(): + return - # If none of the above, insert the line! - if False: - await handle_generated_line(chunk_lines[i]) + # Accumulate lines + if "content" not in chunk: + continue + chunk = chunk["content"] + chunk_lines = chunk.split("\n") + chunk_lines[0] = unfinished_line + chunk_lines[0] + if chunk.endswith("\n"): + unfinished_line = "" + chunk_lines.pop() # because this will be an empty string + else: + unfinished_line = chunk_lines.pop() + + # Deal with newly accumulated lines + for i in range(len(chunk_lines)): + # Trailing whitespace doesn't matter + chunk_lines[i] = chunk_lines[i].rstrip() + chunk_lines[i] = common_whitespace + chunk_lines[i] + + # Lines that should signify the end of generation + if self.is_end_line(chunk_lines[i]): + break + # Lines that should be ignored, like the <> tags + elif self.line_to_be_ignored(chunk_lines[i], completion_lines_covered == 0): + continue + # Check if we are currently just copying the prefix + elif (lines_of_prefix_copied > 0 or completion_lines_covered == 0) and lines_of_prefix_copied < len(file_prefix.splitlines()) and chunk_lines[i] == full_file_contents_lines[lines_of_prefix_copied]: + # This is a sketchy way of stopping it from repeating the file_prefix. Is a bug if output happens to have a matching line + lines_of_prefix_copied += 1 + continue + # Because really short lines might be expected to be repeated, this is only a !heuristic! + # Stop when it starts copying the file_suffix + elif chunk_lines[i].strip() == line_below_highlighted_range.strip() and len(chunk_lines[i].strip()) > 4 and not (len(original_lines_below_previous_blocks) > 0 and chunk_lines[i].strip() == original_lines_below_previous_blocks[0].strip()): + repeating_file_suffix = True + break - lines.append(chunk_lines[i]) - completion_lines_covered += 1 - current_line_in_file += 1 + # If none of the above, insert the line! + if False: + await handle_generated_line(chunk_lines[i]) - await sendDiffUpdate(lines + [common_whitespace if unfinished_line.startswith("<") else (common_whitespace + unfinished_line)], sdk) + lines.append(chunk_lines[i]) + completion_lines_covered += 1 + current_line_in_file += 1 + await sendDiffUpdate(lines + [common_whitespace if unfinished_line.startswith("<") else (common_whitespace + unfinished_line)], sdk) + finally: + await generator.aclose() # Add the unfinished line if unfinished_line != "" and not self.line_to_be_ignored(unfinished_line, completion_lines_covered == 0) and not self.is_end_line(unfinished_line): unfinished_line = common_whitespace + unfinished_line diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index 28089fc6..910c30f2 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -9,6 +9,7 @@ interface DiffInfo { newFilepath: string; editor?: vscode.TextEditor; step_index: number; + range: vscode.Range; } export const DIFF_DIRECTORY = path.join(os.homedir(), ".continue", "diffs"); @@ -18,6 +19,10 @@ class DiffManager { // Doing this because virtual files are read-only private diffs: Map = new Map(); + diffAtNewFilepath(newFilepath: string): DiffInfo | undefined { + return this.diffs.get(newFilepath); + } + private setupDirectory() { // Make sure the diff directory exists if (!fs.existsSync(DIFF_DIRECTORY)) { @@ -35,6 +40,10 @@ class DiffManager { return filepath.replace(/\\/g, "_").replace(/\//g, "_"); } + private getNewFilepath(originalFilepath: string): string { + return path.join(DIFF_DIRECTORY, this.escapeFilepath(originalFilepath)); + } + private openDiffEditor( originalFilepath: string, newFilepath: string @@ -103,18 +112,28 @@ class DiffManager { this.setupDirectory(); // Create or update existing diff - const newFilepath = path.join( - DIFF_DIRECTORY, - this.escapeFilepath(originalFilepath) - ); + const newFilepath = this.getNewFilepath(originalFilepath); fs.writeFileSync(newFilepath, newContent); // Open the diff editor if this is a new diff if (!this.diffs.has(newFilepath)) { + // Figure out the first line that is different + const oldContent = fs.readFileSync(originalFilepath).toString("utf-8"); + let line = 0; + const newLines = newContent.split("\n"); + const oldLines = oldContent.split("\n"); + for (let i = 0; i < newLines.length && i < oldLines.length; i++) { + if (newLines[i] !== oldLines[i]) { + line = i; + break; + } + } + const diffInfo: DiffInfo = { originalFilepath, newFilepath, step_index, + range: new vscode.Range(line, 0, line + 1, 0), }; this.diffs.set(newFilepath, diffInfo); } @@ -139,10 +158,38 @@ class DiffManager { fs.unlinkSync(diffInfo.newFilepath); } + private inferNewFilepath() { + const activeEditorPath = + vscode.window.activeTextEditor?.document.uri.fsPath; + if (activeEditorPath && path.dirname(activeEditorPath) === DIFF_DIRECTORY) { + return activeEditorPath; + } + const visibleEditors = vscode.window.visibleTextEditors.map( + (editor) => editor.document.uri.fsPath + ); + for (const editorPath of visibleEditors) { + if (path.dirname(editorPath) === DIFF_DIRECTORY) { + for (const otherEditorPath of visibleEditors) { + if ( + path.dirname(otherEditorPath) !== DIFF_DIRECTORY && + this.getNewFilepath(otherEditorPath) === editorPath + ) { + return editorPath; + } + } + } + } + + if (this.diffs.size === 1) { + return Array.from(this.diffs.keys())[0]; + } + return undefined; + } + acceptDiff(newFilepath?: string) { - // If no newFilepath is provided and there is only one in the dictionary, use that - if (!newFilepath && this.diffs.size === 1) { - newFilepath = Array.from(this.diffs.keys())[0]; + // When coming from a keyboard shortcut, we have to infer the newFilepath from visible text editors + if (!newFilepath) { + newFilepath = this.inferNewFilepath(); } if (!newFilepath) { console.log("No newFilepath provided to accept the diff"); @@ -170,8 +217,8 @@ class DiffManager { rejectDiff(newFilepath?: string) { // If no newFilepath is provided and there is only one in the dictionary, use that - if (!newFilepath && this.diffs.size === 1) { - newFilepath = Array.from(this.diffs.keys())[0]; + if (!newFilepath) { + newFilepath = this.inferNewFilepath(); } if (!newFilepath) { console.log( diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 778b98dc..5800a00e 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import { editorToSuggestions, editorSuggestionsLocked } from "../suggestions"; import * as path from "path"; import * as os from "os"; -import { DIFF_DIRECTORY } from "../diffs"; +import { DIFF_DIRECTORY, diffManager } from "../diffs"; class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { public provideCodeLenses( document: vscode.TextDocument, @@ -53,7 +53,11 @@ class DiffViewerCodeLensProvider implements vscode.CodeLensProvider { ): vscode.CodeLens[] | Thenable { if (path.dirname(document.uri.fsPath) === DIFF_DIRECTORY) { const codeLenses: vscode.CodeLens[] = []; - const range = new vscode.Range(0, 0, 1, 0); + let range = new vscode.Range(0, 0, 1, 0); + const diffInfo = diffManager.diffAtNewFilepath(document.uri.fsPath); + if (diffInfo) { + range = diffInfo.range; + } codeLenses.push( new vscode.CodeLens(range, { title: "Accept ✅ (⌘⇧↩)", -- cgit v1.2.3-70-g09d2 From 39cd2ef27d6ed439b00a9edec4a487343ff1c2c9 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 14 Jul 2023 03:24:46 -0700 Subject: warn of large highlighted ranges, cmd+k->m --- continuedev/src/continuedev/core/policy.py | 2 +- continuedev/src/continuedev/steps/help.py | 2 +- extension/package-lock.json | 4 +- extension/package.json | 6 +- extension/react-app/src/components/ComboBox.tsx | 5 + extension/react-app/src/components/Onboarding.tsx | 1 + extension/react-app/src/components/PillButton.tsx | 167 +++++++++++++--------- extension/react-app/src/pages/gui.tsx | 27 ++-- extension/src/commands.ts | 17 ++- extension/src/lang-server/codeLens.ts | 4 +- 10 files changed, 141 insertions(+), 94 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index 59ea78b1..bc897357 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -59,7 +59,7 @@ class DemoPolicy(Policy): return ( MessageStep(name="Welcome to Continue", message=dedent("""\ - Highlight code and ask a question or give instructions - - Use `cmd+k` (Mac) / `ctrl+k` (Windows) to open Continue + - Use `cmd+m` (Mac) / `ctrl+m` (Windows) to open Continue - Use `/help` to ask questions about how to use Continue""")) >> WelcomeStep() >> # SetupContinueWorkspaceStep() >> diff --git a/continuedev/src/continuedev/steps/help.py b/continuedev/src/continuedev/steps/help.py index 2dc3647c..ba1e6087 100644 --- a/continuedev/src/continuedev/steps/help.py +++ b/continuedev/src/continuedev/steps/help.py @@ -19,7 +19,7 @@ help = dedent("""\ Continue passes all of the sections of code you highlight, the code above and below the to-be edited highlighted code section, and all previous steps above input box as context to the LLM. - You can use cmd+k (Mac) / ctrl+k (Windows) to open Continue. You can use cmd+shift+e / ctrl+shift+e to open file Explorer. You can add your own OpenAI API key to VS Code Settings with `cmd+,` + You can use cmd+m (Mac) / ctrl+m (Windows) to open Continue. You can use cmd+shift+e / ctrl+shift+e to open file Explorer. You can add your own OpenAI API key to VS Code Settings with `cmd+,` If Continue is stuck loading, try using `cmd+shift+p` to open the command palette, search "Reload Window", and then select it. This will reload VS Code and Continue and often fixes issues. diff --git a/extension/package-lock.json b/extension/package-lock.json index 9d5c73e1..a79dd6b4 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.163", + "version": "0.0.164", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.163", + "version": "0.0.164", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 2b0f6b94..de1f395d 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.163", + "version": "0.0.164", "publisher": "Continue", "engines": { "vscode": "^1.67.0" @@ -111,8 +111,8 @@ "keybindings": [ { "command": "continue.focusContinueInput", - "mac": "cmd+k", - "key": "ctrl+k" + "mac": "cmd+m", + "key": "ctrl+m" }, { "command": "continue.suggestionDown", diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 73db33ca..bd0d59b5 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -228,6 +228,11 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { )} */} {highlightedCodeSections.map((section, idx) => ( 4000 && section.editing + ? "Editing such a large range may be slow" + : undefined + } editing={section.editing} pinned={section.pinned} index={idx} diff --git a/extension/react-app/src/components/Onboarding.tsx b/extension/react-app/src/components/Onboarding.tsx index 6bfb0ccd..231c1e93 100644 --- a/extension/react-app/src/components/Onboarding.tsx +++ b/extension/react-app/src/components/Onboarding.tsx @@ -109,6 +109,7 @@ const Onboarding = () => { paddingBottom: "50px", textAlign: "center", cursor: "pointer", + whiteSpace: "nowrap", }} > ` } `; +const CircleDiv = styled.div` + position: absolute; + top: -10px; + right: -10px; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: red; + color: white; + display: flex; + align-items: center; + justify-content: center; + padding: 2px; +`; + interface PillButtonProps { onHover?: (arg0: boolean) => void; onDelete?: () => void; @@ -55,6 +68,7 @@ interface PillButtonProps { index: number; editing: boolean; pinned: boolean; + warning?: string; } const PillButton = (props: PillButtonProps) => { @@ -63,75 +77,96 @@ const PillButton = (props: PillButtonProps) => { return ( <> - + + {props.editing + ? "Editing this range (with rest of file as context)" + : "Edit this range"} + + Delete + {props.warning && ( + <> + + + + + {props.warning} - { - if (props.onDelete) { - props.onDelete(); - } - }} - > - - - + )} - {props.title} - - - {props.editing - ? "Editing this range (with rest of file as context)" - : "Edit this range"} - - Delete +
); }; diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index 4ff260fa..57cebac3 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -95,11 +95,8 @@ function GUI(props: GUIProps) { name: "Welcome to Continue", hide: false, description: `- Highlight code and ask a question or give instructions -- Use \`cmd+k\` (Mac) / \`ctrl+k\` (Windows) to open Continue -- Use \`cmd+shift+e\` / \`ctrl+shift+e\` to open file Explorer -- Add your own OpenAI API key to VS Code Settings with \`cmd+,\` -- Use slash commands when you want fine-grained control -- Past steps are included as part of the context by default`, + - Use \`cmd+m\` (Mac) / \`ctrl+m\` (Windows) to open Continue + - Use \`/help\` to ask questions about how to use Continue`, system_message: null, chat_context: [], manage_own_chat_context: false, @@ -269,15 +266,17 @@ function GUI(props: GUIProps) { return ( <> - { - client?.sendMainInput(`/feedback ${text}`); - setShowFeedbackDialog(false); - }} - onClose={() => { - setShowFeedbackDialog(false); - }} - message={feedbackDialogMessage} /> + { + client?.sendMainInput(`/feedback ${text}`); + setShowFeedbackDialog(false); + }} + onClose={() => { + setShowFeedbackDialog(false); + }} + message={feedbackDialogMessage} + /> any } = { "continue.suggestionDown": suggestionDownCommand, @@ -30,10 +32,15 @@ const commandsMap: { [command: string]: (...args: any) => any } = { "continue.acceptAllSuggestions": acceptAllSuggestionsCommand, "continue.rejectAllSuggestions": rejectAllSuggestionsCommand, "continue.focusContinueInput": async () => { - vscode.commands.executeCommand("continue.continueGUIView.focus"); - debugPanelWebview?.postMessage({ - type: "focusContinueInput", - }); + if (focusedOnContinueInput) { + vscode.commands.executeCommand("workbench.action.focusActiveEditorGroup"); + } else { + vscode.commands.executeCommand("continue.continueGUIView.focus"); + debugPanelWebview?.postMessage({ + type: "focusContinueInput", + }); + } + focusedOnContinueInput = !focusedOnContinueInput; }, "continue.quickTextEntry": async () => { const text = await vscode.window.showInputBox({ @@ -53,4 +60,4 @@ export function registerAllCommands(context: vscode.ExtensionContext) { vscode.commands.registerCommand(command, callback) ); } -} \ No newline at end of file +} diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 5800a00e..1cfef5d5 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -60,12 +60,12 @@ class DiffViewerCodeLensProvider implements vscode.CodeLensProvider { } codeLenses.push( new vscode.CodeLens(range, { - title: "Accept ✅ (⌘⇧↩)", + title: "Accept All ✅ (⌘⇧↩)", command: "continue.acceptDiff", arguments: [document.uri.fsPath], }), new vscode.CodeLens(range, { - title: "Reject ❌ (⌘⇧⌫)", + title: "Reject All ❌ (⌘⇧⌫)", command: "continue.rejectDiff", arguments: [document.uri.fsPath], }) -- cgit v1.2.3-70-g09d2 From b19076ddb6d11acb5ffd54046d9e5cad549c00c1 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 14 Jul 2023 11:01:06 -0700 Subject: command m reliable toggle --- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- extension/react-app/src/components/ComboBox.tsx | 4 ++++ extension/src/commands.ts | 5 ++++- extension/src/debugPanel.ts | 5 +++++ extension/src/diffs.ts | 21 ++++++++++++--------- 6 files changed, 28 insertions(+), 13 deletions(-) (limited to 'extension/src') diff --git a/extension/package-lock.json b/extension/package-lock.json index a79dd6b4..12aa27c9 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.164", + "version": "0.0.165", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.164", + "version": "0.0.165", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index de1f395d..05bd4d84 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.164", + "version": "0.0.165", "publisher": "Continue", "engines": { "vscode": "^1.67.0" diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index bd0d59b5..5d9b5109 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -12,6 +12,7 @@ import PillButton from "./PillButton"; import HeaderButtonWithText from "./HeaderButtonWithText"; import { DocumentPlus } from "@styled-icons/heroicons-outline"; import { HighlightedRangeContext } from "../../../schema/FullState"; +import { postVscMessage } from "../vscode"; // #region styled components const mainInputFontSize = 13; @@ -297,6 +298,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { // setShowContextDropdown(target.value.endsWith("@")); }, + onBlur: (e) => { + postVscMessage("blurContinueInput", {}); + }, onKeyDown: (event) => { if (event.key === "Enter" && event.shiftKey) { // Prevent Downshift's default 'Enter' behavior. diff --git a/extension/src/commands.ts b/extension/src/commands.ts index 0025340a..888f01ed 100644 --- a/extension/src/commands.ts +++ b/extension/src/commands.ts @@ -16,11 +16,14 @@ import { import { acceptDiffCommand, rejectDiffCommand } from "./diffs"; import * as bridge from "./bridge"; import { debugPanelWebview } from "./debugPanel"; -import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; import { ideProtocolClient } from "./activation/activate"; let focusedOnContinueInput = false; +export const setFocusedOnContinueInput = (value: boolean) => { + focusedOnContinueInput = value; +}; + // Copy everything over from extension.ts const commandsMap: { [command: string]: (...args: any) => any } = { "continue.suggestionDown": suggestionDownCommand, diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 5e1689d1..dd24a8d8 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -6,6 +6,7 @@ import { openEditorAndRevealRange, } from "./util/vscode"; import { RangeInFile } from "./client"; +import { setFocusedOnContinueInput } from "./commands"; const WebSocket = require("ws"); let websocketConnections: { [url: string]: WebsocketConnection | undefined } = @@ -226,6 +227,10 @@ export function setupDebugPanel( openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One); break; } + case "blurContinueInput": { + setFocusedOnContinueInput(false); + break; + } case "withProgress": { // This message allows withProgress to be used in the webview if (data.done) { diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index 910c30f2..37943de4 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -104,6 +104,17 @@ class DiffManager { return editor; } + private _findFirstDifferentLine(contentA: string, contentB: string): number { + const linesA = contentA.split("\n"); + const linesB = contentB.split("\n"); + for (let i = 0; i < linesA.length && i < linesB.length; i++) { + if (linesA[i] !== linesB[i]) { + return i; + } + } + return 0; + } + writeDiff( originalFilepath: string, newContent: string, @@ -119,15 +130,7 @@ class DiffManager { if (!this.diffs.has(newFilepath)) { // Figure out the first line that is different const oldContent = fs.readFileSync(originalFilepath).toString("utf-8"); - let line = 0; - const newLines = newContent.split("\n"); - const oldLines = oldContent.split("\n"); - for (let i = 0; i < newLines.length && i < oldLines.length; i++) { - if (newLines[i] !== oldLines[i]) { - line = i; - break; - } - } + const line = this._findFirstDifferentLine(oldContent, newContent); const diffInfo: DiffInfo = { originalFilepath, -- cgit v1.2.3-70-g09d2 From c5102b0997baa81ce544514d6b5b4d5a2eae804f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 14 Jul 2023 13:45:10 -0700 Subject: insidious client_state vs application_state err --- continuedev/src/continuedev/core/autopilot.py | 2 ++ continuedev/src/continuedev/server/gui.py | 2 +- continuedev/src/continuedev/server/ide.py | 2 +- continuedev/src/continuedev/steps/core/core.py | 9 ++++++++- extension/react-app/src/components/ComboBox.tsx | 9 ++++++++- extension/react-app/src/components/StepContainer.tsx | 9 ++++++--- extension/src/continueIdeClient.ts | 8 ++++++-- extension/src/diffs.ts | 2 +- extension/src/util/messenger.ts | 2 +- 9 files changed, 34 insertions(+), 11 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index e1c8a076..82439f49 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -37,6 +37,8 @@ def get_error_title(e: Exception) -> str: return "The request failed. Please check your internet connection and try again. If this issue persists, you can use our API key for free by going to VS Code settings and changing the value of continue.OPENAI_API_KEY to \"\"" elif isinstance(e, openai_errors.InvalidRequestError): return 'Your API key does not have access to GPT-4. You can use ours for free by going to VS Code settings and changing the value of continue.OPENAI_API_KEY to ""' + elif e.__str__().startswith("Cannot connect to host"): + return "The request failed. Please check your internet connection and try again." return e.__str__() or e.__repr__() diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 238273b2..9a411fbe 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -53,7 +53,7 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.session = session async def _send_json(self, message_type: str, data: Any): - if self.websocket.client_state == WebSocketState.DISCONNECTED: + if self.websocket.application_state == WebSocketState.DISCONNECTED: return await self.websocket.send_json({ "messageType": message_type, diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 73cce201..7875c94d 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -149,7 +149,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): return other_msgs async def _send_json(self, message_type: str, data: Any): - if self.websocket.client_state == WebSocketState.DISCONNECTED: + if self.websocket.application_state == WebSocketState.DISCONNECTED: return await self.websocket.send_json({ "messageType": message_type, diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 787da316..75f8e460 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -9,7 +9,7 @@ from ...libs.llm.prompt_utils import MarkdownStyleEncoderDecoder from ...models.filesystem_edit import EditDiff, FileEdit, FileEditWithFullContents, FileSystemEdit from ...models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents from ...core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation -from ...core.main import ChatMessage, Step, SequentialStep +from ...core.main import ChatMessage, ContinueCustomException, Step, SequentialStep from ...libs.util.count_tokens import MAX_TOKENS_FOR_MODEL, DEFAULT_MAX_TOKENS from ...libs.util.dedent import dedent_and_get_common_whitespace import difflib @@ -608,6 +608,13 @@ Please output the code to be inserted at the cursor in order to fulfill the user rif_dict[rif.filepath] = rif.contents for rif in rif_with_contents: + # If the file doesn't exist, ask them to save it first + if not os.path.exists(rif.filepath): + message = f"The file {rif.filepath} does not exist. Please save it first." + raise ContinueCustomException( + title=message, message=message + ) + await sdk.ide.setFileOpen(rif.filepath) await sdk.ide.setSuggestionsLocked(rif.filepath, True) await self.stream_rif(rif, sdk) diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 5d9b5109..754c9445 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -169,6 +169,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { useImperativeHandle(ref, () => downshiftProps, [downshiftProps]); const [metaKeyPressed, setMetaKeyPressed] = useState(false); + const [focused, setFocused] = useState(false); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Meta") { @@ -298,7 +299,11 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { // setShowContextDropdown(target.value.endsWith("@")); }, + onFocus: (e) => { + setFocused(true); + }, onBlur: (e) => { + setFocused(false); postVscMessage("blurContinueInput", {}); }, onKeyDown: (event) => { @@ -374,7 +379,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
{highlightedCodeSections.length === 0 && (downshiftProps.inputValue?.startsWith("/edit") || - (metaKeyPressed && downshiftProps.inputValue?.length > 0)) && ( + (focused && + metaKeyPressed && + downshiftProps.inputValue?.length > 0)) && (
Inserting at cursor
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 6fa4ba13..14e9b854 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -253,9 +253,12 @@ function StepContainer(props: StepContainerProps) { )} {props.historyNode.observation?.error ? ( -
-              {props.historyNode.observation.error as string}
-            
+
+ View Traceback +
+                {props.historyNode.observation.error as string}
+              
+
) : ( void): void; abstract onClose(callback: () => void): void; - + abstract onError(callback: () => void): void; abstract sendAndReceive(messageType: string, data: any): Promise; -- cgit v1.2.3-70-g09d2 From 98085ebc2eb39569db89a34c8d820a5d0bfe8f84 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 14 Jul 2023 17:40:16 -0700 Subject: fixed config explanation, don't read terminals --- continuedev/src/continuedev/steps/open_config.py | 4 ++-- extension/src/continueIdeClient.ts | 26 ++++++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/steps/open_config.py b/continuedev/src/continuedev/steps/open_config.py index 87f03e9f..af55a95a 100644 --- a/continuedev/src/continuedev/steps/open_config.py +++ b/continuedev/src/continuedev/steps/open_config.py @@ -14,10 +14,10 @@ class OpenConfigStep(Step): "custom_commands": [ { "name": "test", - "description": "Write unit tests like I do for the highlighted code" + "description": "Write unit tests like I do for the highlighted code", "prompt": "Write a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated." } - ], + ] ``` `"name"` is the command you will type. `"description"` is the description displayed in the slash command menu. diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 6dd117d3..2c96763d 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -15,6 +15,7 @@ import { FileEditWithFullContents } from "../schema/FileEditWithFullContents"; import fs = require("fs"); import { WebsocketMessenger } from "./util/messenger"; import { diffManager } from "./diffs"; +import path = require("path"); class IdeProtocolClient { private messenger: WebsocketMessenger | null = null; @@ -350,25 +351,28 @@ class IdeProtocolClient { // ------------------------------------ // // Respond to request + private editorIsTerminal(editor: vscode.TextEditor) { + return ( + !!path.basename(editor.document.uri.fsPath).match(/\d/) || + (editor.document.languageId === "plaintext" && + editor.document.getText() === "accessible-buffer-accessible-buffer-") + ); + } + getOpenFiles(): string[] { return vscode.window.visibleTextEditors - .filter((editor) => { - return !( - editor.document.uri.fsPath.endsWith("/1") || - (editor.document.languageId === "plaintext" && - editor.document.getText() === - "accessible-buffer-accessible-buffer-") - ); - }) + .filter((editor) => !this.editorIsTerminal(editor)) .map((editor) => { return editor.document.uri.fsPath; }); } getVisibleFiles(): string[] { - return vscode.window.visibleTextEditors.map((editor) => { - return editor.document.uri.fsPath; - }); + return vscode.window.visibleTextEditors + .filter((editor) => !this.editorIsTerminal(editor)) + .map((editor) => { + return editor.document.uri.fsPath; + }); } saveFile(filepath: string) { -- cgit v1.2.3-70-g09d2 From abe77c56abd7aea66fa85bd1257f76dc2d435a15 Mon Sep 17 00:00:00 2001 From: sestinj Date: Sat, 15 Jul 2023 14:35:52 -0700 Subject: fix vscode uri parsing for diff --- extension/src/diffs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'extension/src') diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts index d04f9bdb..db6a6490 100644 --- a/extension/src/diffs.ts +++ b/extension/src/diffs.ts @@ -56,7 +56,7 @@ class DiffManager { return undefined; } - const rightUri = vscode.Uri.parse(newFilepath); + const rightUri = vscode.Uri.file(newFilepath); const leftUri = vscode.Uri.file(originalFilepath); const title = "Continue Diff"; console.log( -- cgit v1.2.3-70-g09d2 From 45ee33f7fd84c0bc49d35d9d1a7a3a8e9f6addd7 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sat, 15 Jul 2023 15:06:32 -0700 Subject: use correct label for meta key --- extension/react-app/src/components/ComboBox.tsx | 3 ++- .../react-app/src/components/StepContainer.tsx | 7 +++++- extension/react-app/src/util/index.ts | 17 +++++++++++-- extension/src/diffs.ts | 3 ++- extension/src/lang-server/codeLens.ts | 7 +++--- extension/src/util/util.ts | 29 ++++++++++++++++++++++ 6 files changed, 58 insertions(+), 8 deletions(-) (limited to 'extension/src') diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 754c9445..f11e07af 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -13,6 +13,7 @@ import HeaderButtonWithText from "./HeaderButtonWithText"; import { DocumentPlus } from "@styled-icons/heroicons-outline"; import { HighlightedRangeContext } from "../../../schema/FullState"; import { postVscMessage } from "../vscode"; +import { getMetaKeyLabel } from "../util"; // #region styled components const mainInputFontSize = 13; @@ -286,7 +287,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
-
Collect Data -
+
*/} { // Show the dialog diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index f97cf846..6da79cdc 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -262,6 +262,7 @@ export function setupDebugPanel( + Continue -- cgit v1.2.3-70-g09d2 From e788f27ae90ef6b8ff2c5df2cd1ed6e526cb920a Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 24 Jul 2023 20:02:43 -0700 Subject: Removing unnecessary config.jsons --- extension/config/dev_config.json | 3 -- extension/config/prod_config.json | 3 -- extension/src/bridge.ts | 73 --------------------------------------- 3 files changed, 79 deletions(-) delete mode 100644 extension/config/dev_config.json delete mode 100644 extension/config/prod_config.json (limited to 'extension/src') diff --git a/extension/config/dev_config.json b/extension/config/dev_config.json deleted file mode 100644 index b4b9993e..00000000 --- a/extension/config/dev_config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "API_URL": "http://localhost:65432" -} diff --git a/extension/config/prod_config.json b/extension/config/prod_config.json deleted file mode 100644 index dc0d0797..00000000 --- a/extension/config/prod_config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "API_URL": "https://basin.dev" -} diff --git a/extension/src/bridge.ts b/extension/src/bridge.ts index d614ace4..0d665826 100644 --- a/extension/src/bridge.ts +++ b/extension/src/bridge.ts @@ -1,44 +1,5 @@ -import fetch from "node-fetch"; -import * as path from "path"; import * as vscode from "vscode"; -import { Configuration, DebugApi, UnittestApi } from "./client"; -import { convertSingleToDoubleQuoteJSON } from "./util/util"; -import { getExtensionUri } from "./util/vscode"; import { extensionContext } from "./activation/activate"; -const util = require("util"); -const exec = util.promisify(require("child_process").exec); - -const configuration = new Configuration({ - basePath: get_api_url(), - fetchApi: fetch as any, - middleware: [ - { - pre: async (context) => { - // If there is a SerializedDebugContext in the body, add the files for the filesystem - context.init.body; - - // Add the VS Code Machine Code Header - context.init.headers = { - ...context.init.headers, - "x-vsc-machine-id": vscode.env.machineId, - }; - }, - }, - ], -}); -export const debugApi = new DebugApi(configuration); -export const unittestApi = new UnittestApi(configuration); - -export function get_api_url() { - const extensionUri = getExtensionUri(); - const configFile = path.join(extensionUri.fsPath, "config/config.json"); - const config = require(configFile); - - if (config.API_URL) { - return config.API_URL; - } - return "http://localhost:65432"; -} export function getContinueServerUrl() { // If in debug mode, always use 8001 @@ -53,37 +14,3 @@ export function getContinueServerUrl() { "http://localhost:65432" ); } - -function listToCmdLineArgs(list: string[]): string { - return list.map((el) => `"$(echo "${el}")"`).join(" "); -} - -export async function runPythonScript( - scriptName: string, - args: string[] -): Promise { - // TODO: Need to make sure that the path to poetry is in the PATH and that it is installed in the first place. Realistically also need to install npm in some cases. - const command = `export PATH="$PATH:/opt/homebrew/bin" && cd ${path.join( - getExtensionUri().fsPath, - "scripts" - )} && source env/bin/activate && python3 ${scriptName} ${listToCmdLineArgs( - args - )}`; - - const { stdout, stderr } = await exec(command); - - try { - let jsonString = stdout.substring( - stdout.indexOf("{"), - stdout.lastIndexOf("}") + 1 - ); - jsonString = convertSingleToDoubleQuoteJSON(jsonString); - return JSON.parse(jsonString); - } catch (e) { - if (stderr) { - throw new Error(stderr); - } else { - throw new Error("Failed to parse JSON: " + e); - } - } -} -- cgit v1.2.3-70-g09d2 From 361e0d70983a7c2c51a5d0585df814c4db810ce0 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 25 Jul 2023 00:14:54 -0700 Subject: hot fixes --- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- extension/react-app/src/pages/gui.tsx | 2 +- extension/src/activation/activate.ts | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'extension/src') diff --git a/extension/package-lock.json b/extension/package-lock.json index 2558c9c2..e8cc7b3e 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.191", + "version": "0.0.193", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.191", + "version": "0.0.193", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index f1add025..45a45d1e 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.191", + "version": "0.0.193", "publisher": "Continue", "engines": { "vscode": "^1.67.0" diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index a42e38f4..5d893de9 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -205,7 +205,7 @@ function GUI(props: GUIProps) { setWaitingForSteps(waitingForSteps); setHistory(state.history); - setSelectedContextItems(state.selected_context_items); + setSelectedContextItems(state.selected_context_items || []); setUserInputQueue(state.user_input_queue); setAddingHighlightedCode(state.adding_highlighted_code); setAvailableSlashCommands( diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index a1d88a31..3a370132 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -76,8 +76,9 @@ export async function activateExtension(context: vscode.ExtensionContext) { return await ideProtocolClient.getSessionId(); })(); + const sessionId = await sessionIdPromise; // Register Continue GUI as sidebar webview, and beging a new session - const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise); + const provider = new ContinueGUIWebviewViewProvider(sessionId); context.subscriptions.push( vscode.window.registerWebviewViewProvider( -- cgit v1.2.3-70-g09d2 From 7edf95f0530fb65069049c996f56f7159a3cafa3 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 25 Jul 2023 13:55:32 -0700 Subject: more reliably install meilisearch --- .../src/continuedev/server/meilisearch_server.py | 39 +++++++++++++++++----- extension/src/activation/activate.ts | 3 +- 2 files changed, 31 insertions(+), 11 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/server/meilisearch_server.py b/continuedev/src/continuedev/server/meilisearch_server.py index 419f081f..232b6243 100644 --- a/continuedev/src/continuedev/server/meilisearch_server.py +++ b/continuedev/src/continuedev/server/meilisearch_server.py @@ -1,19 +1,44 @@ import os +import shutil import subprocess import meilisearch from ..libs.util.paths import getServerFolderPath -def check_meilisearch_installed() -> bool: +def ensure_meilisearch_installed(): """ Checks if MeiliSearch is installed. """ - serverPath = getServerFolderPath() meilisearchPath = os.path.join(serverPath, "meilisearch") + dumpsPath = os.path.join(serverPath, "dumps") + dataMsPath = os.path.join(serverPath, "data.ms") + + paths = [meilisearchPath, dumpsPath, dataMsPath] - return os.path.exists(meilisearchPath) + existing_paths = set() + non_existing_paths = set() + for path in paths: + if os.path.exists(path): + existing_paths.add(path) + else: + non_existing_paths.add(path) + + if len(non_existing_paths) > 0: + # Clear the meilisearch binary + if meilisearchPath in existing_paths: + os.remove(meilisearchPath) + non_existing_paths.remove(meilisearchPath) + + # Clear the existing directories + for p in existing_paths: + shutil.rmtree(p, ignore_errors=True) + + # Download MeiliSearch + print("Downloading MeiliSearch...") + subprocess.run( + f"curl -L https://install.meilisearch.com | sh", shell=True, check=True, cwd=serverPath) def check_meilisearch_running() -> bool: @@ -42,12 +67,8 @@ def start_meilisearch(): serverPath = getServerFolderPath() - # Check if MeiliSearch is installed - if not check_meilisearch_installed(): - # Download MeiliSearch - print("Downloading MeiliSearch...") - subprocess.run( - f"curl -L https://install.meilisearch.com | sh", shell=True, check=True, cwd=serverPath) + # Check if MeiliSearch is installed, if not download + ensure_meilisearch_installed() # Check if MeiliSearch is running if not check_meilisearch_running(): diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 3a370132..a1d88a31 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -76,9 +76,8 @@ export async function activateExtension(context: vscode.ExtensionContext) { return await ideProtocolClient.getSessionId(); })(); - const sessionId = await sessionIdPromise; // Register Continue GUI as sidebar webview, and beging a new session - const provider = new ContinueGUIWebviewViewProvider(sessionId); + const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise); context.subscriptions.push( vscode.window.registerWebviewViewProvider( -- cgit v1.2.3-70-g09d2 From 861a873f7ecf455b9c7833040b2a8163e369e062 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 25 Jul 2023 22:26:09 -0700 Subject: checkpoint. Somewhat working, just a bit slow, probably some blocking meilisearch calls still happening --- continuedev/poetry.lock | 105 +++++------------------------- continuedev/pyproject.toml | 1 + continuedev/src/continuedev/server/gui.py | 4 +- continuedev/src/continuedev/server/ide.py | 6 +- extension/react-app/src/main.tsx | 7 +- extension/src/activation/activate.ts | 3 +- extension/src/continueIdeClient.ts | 3 +- extension/src/debugPanel.ts | 2 + extension/src/util/messenger.ts | 9 ++- 9 files changed, 40 insertions(+), 100 deletions(-) (limited to 'extension/src') diff --git a/continuedev/poetry.lock b/continuedev/poetry.lock index b67128fd..6644bf6f 100644 --- a/continuedev/poetry.lock +++ b/continuedev/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "aiohttp" version = "3.8.4" description = "Async http client/server framework (asyncio)" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -113,7 +112,6 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -128,7 +126,6 @@ frozenlist = ">=1.1.0" name = "anthropic" version = "0.3.4" description = "Client library for the anthropic API" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -148,7 +145,6 @@ typing-extensions = ">=4.1.1,<5" name = "anyio" version = "3.6.2" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = false python-versions = ">=3.6.2" files = [ @@ -169,7 +165,6 @@ trio = ["trio (>=0.16,<0.22)"] name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -181,7 +176,6 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -200,7 +194,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "backoff" version = "2.2.1" description = "Function decoration for backoff and retry" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -212,7 +205,6 @@ files = [ name = "boltons" version = "23.0.0" description = "When they're not builtins, they're boltons." -category = "main" optional = false python-versions = "*" files = [ @@ -224,7 +216,6 @@ files = [ name = "camel-converter" version = "3.0.2" description = "Converts a string from snake case to camel case or camel case to snake case" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -242,7 +233,6 @@ pydantic = ["pydantic (>=1.8.2)"] name = "certifi" version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -254,7 +244,6 @@ files = [ name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = "*" files = [ @@ -331,7 +320,6 @@ pycparser = "*" name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -416,7 +404,6 @@ files = [ name = "chevron" version = "0.14.0" description = "Mustache templating language renderer" -category = "main" optional = false python-versions = "*" files = [ @@ -428,7 +415,6 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -443,7 +429,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -455,7 +440,6 @@ files = [ name = "cryptography" version = "41.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -501,7 +485,6 @@ test-randomorder = ["pytest-randomly"] name = "dataclasses-json" version = "0.5.7" description = "Easily serialize dataclasses to and from JSON" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -521,7 +504,6 @@ dev = ["flake8", "hypothesis", "ipython", "mypy (>=0.710)", "portray", "pytest ( name = "deprecated" version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -539,7 +521,6 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] name = "diff-match-patch" version = "20230430" description = "Diff Match and Patch" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -554,7 +535,6 @@ dev = ["attribution (==1.6.2)", "black (==23.3.0)", "flit (==3.8.0)", "mypy (==1 name = "directory-tree" version = "0.0.3.1" description = "Utility Package that Displays out the Tree Structure of a Particular Directory." -category = "main" optional = false python-versions = "*" files = [ @@ -569,7 +549,6 @@ dev = ["pytest (>=3.7)"] name = "distro" version = "1.8.0" description = "Distro - an OS platform information API" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -581,7 +560,6 @@ files = [ name = "fastapi" version = "0.95.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -603,7 +581,6 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6 name = "frozenlist" version = "1.3.3" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -683,11 +660,20 @@ files = [ {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, ] +[[package]] +name = "func-timeout" +version = "4.3.5" +description = "Python module which allows you to specify timeouts when calling any existing function. Also provides support for stoppable-threads" +optional = false +python-versions = "*" +files = [ + {file = "func_timeout-4.3.5.tar.gz", hash = "sha256:74cd3c428ec94f4edfba81f9b2f14904846d5ffccc27c92433b8b5939b5575dd"}, +] + [[package]] name = "gpt-index" version = "0.6.8" description = "Interface between LLMs and your data" -category = "main" optional = false python-versions = "*" files = [ @@ -709,7 +695,6 @@ tiktoken = "*" name = "greenlet" version = "2.0.2" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ @@ -783,7 +768,6 @@ test = ["objgraph", "psutil"] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -795,7 +779,6 @@ files = [ name = "httpcore" version = "0.17.3" description = "A minimal low-level HTTP client." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -807,17 +790,16 @@ files = [ anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" +sniffio = "==1.*" [package.extras] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "httpx" version = "0.24.1" description = "The next generation HTTP client." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -833,15 +815,14 @@ sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -853,7 +834,6 @@ files = [ name = "importlib-resources" version = "6.0.0" description = "Read resources from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -872,7 +852,6 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "jsonref" version = "1.1.0" description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -884,7 +863,6 @@ files = [ name = "jsonschema" version = "4.17.3" description = "An implementation of JSON Schema validation for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -906,7 +884,6 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "langchain" version = "0.0.171" description = "Building applications with LLMs through composability" -category = "main" optional = false python-versions = ">=3.8.1,<4.0" files = [ @@ -943,7 +920,6 @@ qdrant = ["qdrant-client (>=1.1.2,<2.0.0)"] name = "marshmallow" version = "3.19.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -964,7 +940,6 @@ tests = ["pytest", "pytz", "simplejson"] name = "marshmallow-enum" version = "1.5.1" description = "Enum field for Marshmallow" -category = "main" optional = false python-versions = "*" files = [ @@ -979,7 +954,6 @@ marshmallow = ">=2.0.0" name = "meilisearch" version = "0.28.1" description = "The python client for Meilisearch API." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -995,7 +969,6 @@ requests = "*" name = "monotonic" version = "1.6" description = "An implementation of time.monotonic() for Python 2 & < 3.3" -category = "main" optional = false python-versions = "*" files = [ @@ -1007,7 +980,6 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1091,7 +1063,6 @@ files = [ name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1103,7 +1074,6 @@ files = [ name = "nest-asyncio" version = "1.5.6" description = "Patch asyncio to allow nested event loops" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1115,7 +1085,6 @@ files = [ name = "numexpr" version = "2.8.4" description = "Fast numerical expression evaluator for NumPy" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1158,7 +1127,6 @@ numpy = ">=1.13.3" name = "numpy" version = "1.24.3" description = "Fundamental package for array computing in Python" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1196,7 +1164,6 @@ files = [ name = "openai" version = "0.27.6" description = "Python client library for the OpenAI API" -category = "main" optional = false python-versions = ">=3.7.1" files = [ @@ -1211,7 +1178,7 @@ tqdm = "*" [package.extras] datalib = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -dev = ["black (>=21.6b0,<22.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"] +dev = ["black (>=21.6b0,<22.0)", "pytest (==6.*)", "pytest-asyncio", "pytest-mock"] embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "scipy", "tenacity (>=8.0.1)"] wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"] @@ -1219,7 +1186,6 @@ wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1 name = "openapi-schema-pydantic" version = "1.2.4" description = "OpenAPI (v3) specification schema as pydantic class" -category = "main" optional = false python-versions = ">=3.6.1" files = [ @@ -1234,7 +1200,6 @@ pydantic = ">=1.8.2" name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1246,7 +1211,6 @@ files = [ name = "pandas" version = "2.0.1" description = "Powerful data structures for data analysis, time series, and statistics" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1314,7 +1278,6 @@ xml = ["lxml (>=4.6.3)"] name = "pkgutil-resolve-name" version = "1.3.10" description = "Resolve a name to an object." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1326,7 +1289,6 @@ files = [ name = "posthog" version = "3.0.1" description = "Integrate PostHog into any python application." -category = "main" optional = false python-versions = "*" files = [ @@ -1350,7 +1312,6 @@ test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint" name = "psutil" version = "5.9.5" description = "Cross-platform lib for process and system monitoring in Python." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1377,7 +1338,6 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1389,7 +1349,6 @@ files = [ name = "pydantic" version = "1.10.7" description = "Data validation and settings management using python type hints" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1442,7 +1401,6 @@ email = ["email-validator (>=1.0.3)"] name = "pygithub" version = "1.59.0" description = "Use the full Github API v3" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1460,7 +1418,6 @@ requests = ">=2.14.0" name = "pyjwt" version = "2.8.0" description = "JSON Web Token implementation in Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1481,7 +1438,6 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1508,7 +1464,6 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "pyrsistent" version = "0.19.3" description = "Persistent/Functional/Immutable data structures" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1545,7 +1500,6 @@ files = [ name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1560,7 +1514,6 @@ six = ">=1.5" name = "python-dotenv" version = "1.0.0" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1575,7 +1528,6 @@ cli = ["click (>=5.0)"] name = "pytz" version = "2023.3" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" files = [ @@ -1587,7 +1539,6 @@ files = [ name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1637,7 +1588,6 @@ files = [ name = "regex" version = "2023.5.5" description = "Alternative regular expression module, to replace re." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1735,7 +1685,6 @@ files = [ name = "requests" version = "2.29.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1757,7 +1706,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1769,7 +1717,6 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1781,7 +1728,6 @@ files = [ name = "sqlalchemy" version = "2.0.13" description = "Database Abstraction Library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1829,7 +1775,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""} typing-extensions = ">=4.2.0" [package.extras] @@ -1859,7 +1805,6 @@ sqlcipher = ["sqlcipher3-binary"] name = "starlette" version = "0.26.1" description = "The little ASGI library that shines." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1878,7 +1823,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam name = "tenacity" version = "8.2.2" description = "Retry code until it succeeds" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1893,7 +1837,6 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"] name = "tiktoken" version = "0.4.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1939,7 +1882,6 @@ blobfile = ["blobfile (>=2)"] name = "tokenizers" version = "0.13.3" description = "Fast and Customizable Tokenizers" -category = "main" optional = false python-versions = "*" files = [ @@ -1994,7 +1936,6 @@ testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] name = "tqdm" version = "4.65.0" description = "Fast, Extensible Progress Meter" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2015,7 +1956,6 @@ telegram = ["requests"] name = "typer" version = "0.7.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2036,7 +1976,6 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. name = "typing-extensions" version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2048,7 +1987,6 @@ files = [ name = "typing-inspect" version = "0.8.0" description = "Runtime inspection utilities for typing module." -category = "main" optional = false python-versions = "*" files = [ @@ -2064,7 +2002,6 @@ typing-extensions = ">=3.7.4" name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" -category = "main" optional = false python-versions = ">=2" files = [ @@ -2076,7 +2013,6 @@ files = [ name = "urllib3" version = "1.26.15" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -2093,7 +2029,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "uvicorn" version = "0.21.1" description = "The lightning-fast ASGI server." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2112,7 +2047,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", name = "websockets" version = "11.0.2" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2192,7 +2126,6 @@ files = [ name = "wrapt" version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -2277,7 +2210,6 @@ files = [ name = "yarl" version = "1.9.2" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2365,7 +2297,6 @@ multidict = ">=4.0" name = "zipp" version = "3.16.2" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2380,4 +2311,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "83880c8de27a4094fa8e695bd8db5d4053fc2d61ffc028f09cfa097f315c35f2" +content-hash = "53ad0b6fa45f00fe53fbd2a90845ae1647e0be22b0b52dfe55996f2c9a6faa38" diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml index 64d5e90c..ad5657b1 100644 --- a/continuedev/pyproject.toml +++ b/continuedev/pyproject.toml @@ -29,6 +29,7 @@ chevron = "^0.14.0" psutil = "^5.9.5" meilisearch = "^0.28.1" pygithub = "^1.59.0" +func-timeout = "^4.3.5" [tool.poetry.scripts] typegen = "src.continuedev.models.generate_json_schema:main" diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index fa203c28..c0957395 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -61,12 +61,12 @@ class GUIProtocolServer(AbstractGUIProtocolServer): "data": data }) - async def _receive_json(self, message_type: str, timeout: int = 5) -> Any: + async def _receive_json(self, message_type: str, timeout: int = 20) -> Any: try: return await asyncio.wait_for(self.sub_queue.get(message_type), timeout=timeout) except asyncio.TimeoutError: raise Exception( - "GUI Protocol _receive_json timed out after 5 seconds") + "GUI Protocol _receive_json timed out after 20 seconds") async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: await self._send_json(message_type, data) diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index d6a28c92..3401cbac 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -139,6 +139,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): continue message_type = message["messageType"] data = message["data"] + print("Received message while initializing", message_type) if message_type == "workspaceDirectory": self.workspace_directory = data["workspaceDirectory"] elif message_type == "uniqueId": @@ -153,17 +154,18 @@ class IdeProtocolServer(AbstractIdeProtocolServer): async def _send_json(self, message_type: str, data: Any): if self.websocket.application_state == WebSocketState.DISCONNECTED: return + print("Sending IDE message: ", message_type) await self.websocket.send_json({ "messageType": message_type, "data": data }) - async def _receive_json(self, message_type: str, timeout: int = 5) -> Any: + async def _receive_json(self, message_type: str, timeout: int = 20) -> Any: try: return await asyncio.wait_for(self.sub_queue.get(message_type), timeout=timeout) except asyncio.TimeoutError: raise Exception( - "IDE Protocol _receive_json timed out after 5 seconds") + "IDE Protocol _receive_json timed out after 20 seconds", message_type) async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: await self._send_json(message_type, data) diff --git a/extension/react-app/src/main.tsx b/extension/react-app/src/main.tsx index a76bced6..1776490c 100644 --- a/extension/react-app/src/main.tsx +++ b/extension/react-app/src/main.tsx @@ -8,12 +8,11 @@ import "./index.css"; import posthog from "posthog-js"; import { PostHogProvider } from "posthog-js/react"; +console.log("Starting React"); + posthog.init("phc_JS6XFROuNbhJtVCEdTSYk6gl5ArRrTNMpCcguAXlSPs", { api_host: "https://app.posthog.com", - session_recording: { - // WARNING: Only enable this if you understand the security implications - recordCrossOriginIframes: true, - } as any, + disable_session_recording: true, }); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index a1d88a31..356d0256 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -17,7 +17,7 @@ export let ideProtocolClient: IdeProtocolClient; export async function activateExtension(context: vscode.ExtensionContext) { extensionContext = context; - + console.log("Using Continue version: ", getExtensionVersion()); // Before anything else, check whether this is an out-of-date version of the extension // Do so by grabbing the package.json off of the GitHub respository for now. fetch(PACKAGE_JSON_RAW_GITHUB_URL) @@ -67,6 +67,7 @@ export async function activateExtension(context: vscode.ExtensionContext) { }, 2000); }); + console.log("Continue server started"); // Initialize IDE Protocol Client const serverUrl = getContinueServerUrl(); ideProtocolClient = new IdeProtocolClient( diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 802afc1d..d92a829d 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -399,8 +399,9 @@ class IdeProtocolClient { } }, 1000); }); + console.log("Getting session ID"); const resp = await this.messenger?.sendAndReceive("getSessionId", {}); - // console.log("New Continue session with ID: ", sessionId); + console.log("New Continue session with ID: ", resp.sessionId); this._sessionId = resp.sessionId; return resp.sessionId; } diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 6da79cdc..6dcb588a 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -166,11 +166,13 @@ export function setupDebugPanel( switch (data.type) { case "onLoad": { let sessionId: string; + console.log("Running onLoad"); if (typeof sessionIdPromise === "string") { sessionId = sessionIdPromise; } else { sessionId = await sessionIdPromise; } + console.log("Done with onLoad: ", sessionId); panel.webview.postMessage({ type: "onLoad", vscMachineId: vscode.env.machineId, diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index 3044898e..e28040f5 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -39,6 +39,7 @@ export class WebsocketMessenger extends Messenger { // var WebSocket = require("ws"); // } + console.log("Creating websocket at: ", this.serverUrl); const newWebsocket = new WebSocket(this.serverUrl); for (const listener of this.onOpenListeners) { this.onOpen(listener); @@ -105,12 +106,14 @@ export class WebsocketMessenger extends Messenger { send(messageType: string, data: object) { const payload = JSON.stringify({ messageType, data }); if (this.websocket.readyState === this.websocket.OPEN) { + console.log("websocket is open, sending message: ", messageType); this.websocket.send(payload); } else { - if (this.websocket.readyState !== this.websocket.CONNECTING) { - this.websocket = this._newWebsocket(); - } + console.log("websocket is not open, creating new websocket", messageType); + this.websocket = this._newWebsocket(); + this.websocket.addEventListener("open", () => { + console.log("websocket is open, resending message: ", messageType); this.websocket.send(payload); }); } -- cgit v1.2.3-70-g09d2 From 932925ccc90096c7869797faa00b95c8ac1eff0e Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 25 Jul 2023 23:46:34 -0700 Subject: correctly ignore directories in file context prov --- continuedev/src/continuedev/plugins/context_providers/file.py | 7 +++++-- extension/src/util/messenger.ts | 3 --- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'extension/src') diff --git a/continuedev/src/continuedev/plugins/context_providers/file.py b/continuedev/src/continuedev/plugins/context_providers/file.py index fc0af7ba..6222ec6a 100644 --- a/continuedev/src/continuedev/plugins/context_providers/file.py +++ b/continuedev/src/continuedev/plugins/context_providers/file.py @@ -21,7 +21,7 @@ class FileContextProvider(ContextProvider): title = "file" workspace_dir: str - ignore_patterns: List[str] = list(map(lambda folder: f"**/{folder}", [ + ignore_patterns: List[str] = [ ".git", ".vscode", ".idea", @@ -35,7 +35,10 @@ class FileContextProvider(ContextProvider): "target", "out", "bin", - ])) + ".pytest_cache", + ".vscode-test", + ".continue", + ] async def provide_context_items(self) -> List[ContextItem]: filepaths = [] diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index e28040f5..be4233b2 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -106,14 +106,11 @@ export class WebsocketMessenger extends Messenger { send(messageType: string, data: object) { const payload = JSON.stringify({ messageType, data }); if (this.websocket.readyState === this.websocket.OPEN) { - console.log("websocket is open, sending message: ", messageType); this.websocket.send(payload); } else { - console.log("websocket is not open, creating new websocket", messageType); this.websocket = this._newWebsocket(); this.websocket.addEventListener("open", () => { - console.log("websocket is open, resending message: ", messageType); this.websocket.send(payload); }); } -- cgit v1.2.3-70-g09d2 From 03da423e5abdf32c5bf9755ffd2e1c7be98e6454 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 25 Jul 2023 23:51:55 -0700 Subject: messenger don't create new websocket if connecting --- extension/src/util/messenger.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'extension/src') diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index be4233b2..dc59a593 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -108,7 +108,9 @@ export class WebsocketMessenger extends Messenger { if (this.websocket.readyState === this.websocket.OPEN) { this.websocket.send(payload); } else { - this.websocket = this._newWebsocket(); + if (this.websocket.readyState !== this.websocket.CONNECTING) { + this.websocket = this._newWebsocket(); + } this.websocket.addEventListener("open", () => { this.websocket.send(payload); -- cgit v1.2.3-70-g09d2