diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 550072cf..d6ee1510 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -185,9 +185,3 @@ jobs: env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - - - name: Notify to Discord - run: | - curl -X POST ${{ secrets.DISCORD_WEBHOOK_URL }} \ - -H "Content-Type: application/json" \ - -d '{"msg":"VSCode plugin ${{ steps.setupvars.outputs.plugin_version }} is released in ${{ steps.setupvars.outputs.slack_notification_channel }} channel"}' diff --git a/README.md b/README.md index 23d33a6d..fe3f971c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ --- -[![Discord](https://img.shields.io/discord/1037660742440194089?logo=discord&label=Discord&link=https%3A%2F%2Fsmallcloud.ai%2Fdiscord)](https://smallcloud.ai/discord) [![Twitter Follow](https://img.shields.io/twitter/follow/refact_ai)](https://twitter.com/intent/follow?screen_name=refact_ai) ![License](https://img.shields.io/github/license/smallcloudai/refact-vscode) @@ -13,9 +12,8 @@ Refact.ai is the #1 free **open-source** AI Agent on the SWE-bench verified leaderboard. It autonomously handles software engineering tasks end to end. It deeply understands large and complex codebases and integrates with developers’ tools (including MCP), databases, and browsers to automate complex, multi-step tasks. - Integrate the AI Agent with the tools you already use, allowing it to complete tasks end to end while transparently showing every step it takes. -- Deploy Refact.ai on-premise to maintain **100% control over your codebase**. - Access state-of-the-art models like Claude 4 Sonnet, GPT-4.1, 4o, Gemini 2.5 Pro, and more. -- Bring your own key (BYOK) - Use your own API keys for external LLMs. +- Bring your own key (BYOK) or connect local providers. - Stop switching between your IDE and chat—Refact.ai has an integrated chat right in your IDE. - Get free, unlimited, context-aware auto-completion. @@ -39,23 +37,11 @@ We can't say it better than our users: *'Refact.ai understands me better than my - **@tree** - display the workspace directory and files tree. - Create your own **custom system prompt** for a more personalized workflow.![@-commands](https://github.com/user-attachments/assets/28e1db76-3490-4195-a3e0-de30496239a9) -## Refact.ai Agent For Enterprise -Deploying Refact.ai Agent is like adding a 24/7 engineer to your team—one that instantly understands your codebase, adapts to your workflows, accelerates development from day one, and becomes a shared knowledge base for your entire team. +## BYOK/local-only setup -1. **Refact.ai already understands your company's context:** AI Agent captures the unique structure, tools, and workflows of your organization, using your company's databases, documentation, and code architecture to deliver customized solutions. -2. **Gets smarter over time:** With each interaction and feedback, Refact.ai Agent adapts to your organization's needs, becoming more accurate and powerful. -3. **Organizes experience into the knowledge base:** Refact.ai Agent captures and shares knowledge from interactions with each team member. It not only streamlines workflows but also supports faster onboarding and smoother cross-functional collaboration across projects. +Refact runs with BYOK or local model providers configured on your machine. +Open the Refact sidebar, configure at least one provider in Provider Setup, then choose default models. -### Take full control of your AI Agent, tailored to your company: -- **Deploy Refact.ai on-premise:** on your own servers or private cloud. Your data never leaves your control. Telemetry from the plugins goes to your server and nowhere else. You can verify what the code does, it's open source. -- **Fine-tune a model on your codebase:** A fine-tuned code completion model will provide you with more relevant suggestions: it can memorize your coding style, the right way to use your internal APIs, and the tech stack you use. -- **Priority Support:** Our engineers are always available to assist you at every stage, from setup to fine-tuning and beyond. - -**To get a 2-week free trial** for your team, just fill out the form on [our website](https://refact.ai/contact/?utm_source=vscode&utm_medium=marketplace&utm_campaign=enterprise). We'll reach out with more details! -\ -  -\ -  ## Which tasks can Refact.ai help me with? - **Generate code from natural language prompts (even if you make typos)** - Instantly turn ideas into functional code, accelerating development and eliminating the blank-screen problem. @@ -93,6 +79,6 @@ Deploying Refact.ai Agent is like adding a 24/7 engineer to your team—one that - **Create Documentation** - Automate documentation generation to keep knowledge accessible and up to date. - **Generate Docstrings** - Enhance maintainability with clear, structured documentation generated for functions and classes in seconds. -## Join Our Discord Community +## Support & Feedback -Connect with other developers in our [Discord community](https://www.smallcloud.ai/discord). Ask questions, share your opinion, propose new features. +Use GitHub issues or discussions to ask questions, report bugs, or propose features. diff --git a/package.json b/package.json index 31ddbb1e..b0216e33 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "url": "https://github.com/smallcloudai/refact-vscode/issues", "email": "support@smallcloud.tech" }, - "version": "7.0.2", + "version": "8.0.1", "dependencies": { "@types/marked": "^4.0.8", "@types/vscode": "^1.69.0", @@ -27,7 +27,7 @@ "fetch-h2": "^3.0.2", "json5": "^2.2.3", "marked": "^4.0.8", - "refact-chat-js": "^7.0.2", + "refact-chat-js": "^8.0.0", "uuid": "^9.0.1", "vscode-languageclient": "^7.0.0" }, @@ -46,7 +46,7 @@ "glob": "^8.0.3", "mocha": "^10.0.0", "patch-package": "^8.0.0", - "typescript": "^4.7.4" + "typescript": "^5.0.0" }, "engines": { "vscode": "^1.69.0" @@ -128,28 +128,6 @@ "type": "object", "title": "Refact Assistant", "properties": { - "refactai.addressURL": { - "type": "string", - "description": "For Enterprise, put there your company's server address. Your admin should have emailed that to you.\nFor self-hosted, use something like http://127.0.0.1:8008/\nFor inference in public cloud, use \"Refact\" or \"HF\".", - "default": "", - "order": 0 - }, - "refactai.infurl": { - "type": "string", - "deprecationMessage": "The new field is called addressURL. It's about the same, but it has special values like \"HF\" and if it's empty, makes the plugin do nothing, which is better for Enterprise use case to prevent leaks.", - "order": 0 - }, - "refactai.apiKey": { - "type": "string", - "default": "", - "description": "Secret API Key. It's used to authenticate your requests.", - "order": 1 - }, - "codify.apiKey": { - "type": "string", - "deprecationMessage": "Use refactai.apiKey instead.", - "order": 1 - }, "refactai.insecureSSL": { "type": "boolean", "description": "Allow insecure server connections when using SSL, ignore certificate verification errors. Allows you to use self-signed certificates.", @@ -195,7 +173,7 @@ }, "refactai.ast": { "type": "boolean", - "markdownDescription": "Enable context-aware code completion and chat, adds defintion() and references() functions for a chat model, and @definition, @references commands for you to add context manually.\n\nRAG works better with a larger context, available in the [Pro plan](https://refact.ai/pricing/?utm_source=vscode&utm_medium=settings&utm_campaign=plugin) and [for companies](https://refact.ai/enterprise/?utm_source=vscode&utm_medium=settings&utm_campaign=plugin).", + "markdownDescription": "Enable context-aware code completion and chat. This adds definition() and references() functions for chat models, plus @definition and @references commands for adding context manually.", "default": true, "order": 8 }, @@ -207,7 +185,7 @@ }, "refactai.vecdb": { "type": "boolean", - "markdownDescription": "Enable vector database. This adds the ability for a chat model to perform a semantic search in your codebase, and the @workspace command for you to add context manually.\n\nRAG performs better with a larger context, available in the [Pro plan](https://refact.ai/pricing/?utm_source=vscode&utm_medium=settings&utm_campaign=plugin) and [for companies](https://refact.ai/enterprise/?utm_source=vscode&utm_medium=settings&utm_campaign=plugin).", + "markdownDescription": "Enable vector database. This adds semantic search over your codebase and the @workspace command for adding context manually.", "default": true, "order": 11 }, @@ -227,16 +205,6 @@ "description": "Enable experimental features.", "default": false, "order": 14 - }, - "refactai.activeGroup": { - "type": [ - "object", - "null" - ], - "default": null, - "description": "Active selected group in your Team Workspace. Modify via settings.json in your workspace", - "scope": "machine-overridable", - "included": false } } }, @@ -251,21 +219,11 @@ "command": "refactaicmd.closeInlineChat", "title": "Close" }, - { - "command": "refactaicmd.activateToolboxDeprecated", - "title": "Activate Toolbox (deprecated)", - "category": "Refact.ai" - }, { "command": "refactaicmd.activateToolbox", "title": "Activate (opens chat)", "category": "Refact.ai" }, - { - "command": "refactaicmd.login", - "title": "Login", - "category": "Refact.ai" - }, { "command": "refactaicmd.completionManual", "title": "Manual Completion Trigger", @@ -313,10 +271,6 @@ ] }, "keybindings": [ - { - "command": "refactaicmd.activateToolboxDeprecated", - "key": "alt+t" - }, { "command": "refactaicmd.completionManual", "key": "alt+space" @@ -390,13 +344,6 @@ "fontCharacter": "\\e807" } }, - "codify-coin": { - "description": "codify coin", - "default": { - "fontPath": "./assets/codify.woff", - "fontCharacter": "\\e802" - } - }, "codify-chat": { "description": "codify chat", "default": { diff --git a/src/chatTab.ts b/src/chatTab.ts index 1f02d450..ef077f9b 100644 --- a/src/chatTab.ts +++ b/src/chatTab.ts @@ -910,6 +910,27 @@ export class ChatTab { const fontSize = vscode.workspace.getConfiguration().get("editor.fontSize") ?? 12; const scaling = fontSize < 14 ? "90%" : "100%"; + const rawPort = global.rust_binary_blob?.get_port(); + const port = typeof rawPort === "number" && Number.isFinite(rawPort) && rawPort > 0 + ? rawPort + : 0; + const config = { + host: "vscode", + tabbed: isTab, + themeProps: { + accentColor: "gray", + scaling, + }, + features: { + vecdb, + ast, + }, + lspPort: port, + }; + const serializedConfig = JSON.stringify(config).replace( + /<\/script>/gi, + "" + ); const nonce = ChatTab.getNonce(); return ` @@ -938,18 +959,7 @@ export class ChatTab { diff --git a/src/completionProvider.ts b/src/completionProvider.ts index cba23294..91645ad3 100644 --- a/src/completionProvider.ts +++ b/src/completionProvider.ts @@ -2,7 +2,6 @@ import * as vscode from 'vscode'; import * as estate from "./estate"; import * as fetchAPI from "./fetchAPI"; -import * as fetchH2 from 'fetch-h2'; import { FimDebugData, fim } from 'refact-chat-js/dist/events'; @@ -165,10 +164,9 @@ export class MyInlineCompletionProvider implements vscode.InlineCompletionItemPr return ["", -1]; } let de_facto_model = json["model"]; - let serial_number = json["snippet_telemetry_id"]; this.maybeSendFIMData(json); global.status_bar.completion_model_worked(de_facto_model); - return [completion, serial_number]; + return [completion, -1]; } maybeSendFIMData(data: FimDebugData | null) { @@ -192,33 +190,9 @@ export function _extract_extension(feed: estate.ApiFields) } -export async function inline_accepted(serial_number: number) +export async function inline_accepted(_serial_number: number) { - let url = fetchAPI.rust_url("/v1/snippet-accepted"); - if (!url) { - console.log(["Failed to get url for /v1/snippet-accepted"]); - } - const post = JSON.stringify({ - "snippet_telemetry_id": serial_number - }); - const headers = { - "Content-Type": "application/json", - // "Authorization": `Bearer ${apiKey}`, - }; - let req = new fetchH2.Request(url, { - method: "POST", - headers: headers, - body: post, - redirect: "follow", - cache: "no-cache", - referrer: "no-referrer" - }); - - try { - await fetchH2.fetch(req); - } catch (error) { - console.log("failed to post to /v1/snippet-accepted"); - } + return; } diff --git a/src/extension.ts b/src/extension.ts index 8729f055..ac101f4e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,7 +7,6 @@ import * as codeLens from "./codeLens"; import * as interactiveDiff from "./interactiveDiff"; import * as estate from "./estate"; import * as fetchAPI from "./fetchAPI"; -import * as userLogin from "./userLogin"; import * as sidebar from "./sidebar"; import * as launchRust from "./launchRust"; import { RefactConsoleProvider } from './rconsoleProvider'; @@ -186,24 +185,6 @@ async function f1_pressed() pressed_call_chat(); } -async function f1_deprecated() -{ - let editor = vscode.window.activeTextEditor; - if (editor) { - let state = estate.state_of_editor(editor, "f1_pressed"); - if (state && state.get_mode() === Mode.Diff) { - rollback_and_regen(editor); - return; - } - if (state) { - RefactConsoleProvider.open_between_lines(editor); - } - } - // await vscode.commands.executeCommand("refactai-toolbox.focus"); - // await vscode.commands.executeCommand("workbench.action.focusSideBar"); -} - - export async function inline_accepted(this_completion_serial_number: number) { if (typeof this_completion_serial_number === "number") { @@ -262,7 +243,6 @@ export function activate(context: vscode.ExtensionContext) let disposable4 = vscode.commands.registerCommand('refactaicmd.esc', pressed_escape); let disposable5 = vscode.commands.registerCommand('refactaicmd.tab', pressed_tab); let disposable3 = vscode.commands.registerCommand('refactaicmd.activateToolbox', f1_pressed); - let disposable8 = vscode.commands.registerCommand('refactaicmd.activateToolboxDeprecated', f1_deprecated); let disposable13 = vscode.commands.registerCommand('refactaicmd.completionManual', async () => { await vscode.commands.executeCommand('editor.action.inlineSuggest.hide'); await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger'); @@ -278,7 +258,6 @@ export function activate(context: vscode.ExtensionContext) context.subscriptions.push(disposable3); context.subscriptions.push(disposable4); context.subscriptions.push(disposable5); - context.subscriptions.push(disposable8); context.subscriptions.push(disposable13); context.subscriptions.push(disposable6); context.subscriptions.push(toolbar_command_disposable); @@ -308,19 +287,6 @@ export function activate(context: vscode.ExtensionContext) }); context.subscriptions.push(settingsCommand); - let logout = vscode.commands.registerCommand('refactaicmd.logout', async () => { - context.globalState.update('codifyFirstRun', false); - await vscode.workspace.getConfiguration().update('refactai.apiKey', undefined, vscode.ConfigurationTarget.Global); - await vscode.workspace.getConfiguration().update('refactai.addressURL', undefined, vscode.ConfigurationTarget.Global); - await vscode.workspace.getConfiguration().update('codify.apiKey', undefined, vscode.ConfigurationTarget.Global); - await vscode.workspace.getConfiguration().update('refactai.apiKey', undefined, vscode.ConfigurationTarget.Workspace); - await vscode.workspace.getConfiguration().update('refactai.addressURL', undefined, vscode.ConfigurationTarget.Workspace); - await vscode.workspace.getConfiguration().update('codify.apiKey', undefined, vscode.ConfigurationTarget.Workspace); - global.status_bar.choose_color(); - vscode.commands.executeCommand("workbench.action.webview.reloadWebviewAction"); - }); - - context.subscriptions.push(logout); context.subscriptions.push(...statusBar.status_bar_init()); context.subscriptions.push(...estate.estate_init()); @@ -352,12 +318,8 @@ export function activate(context: vscode.ExtensionContext) let config_debounce: NodeJS.Timeout|undefined; vscode.workspace.onDidChangeConfiguration(e => { - // TODO: update commands here? if ( - e.affectsConfiguration("refactai.infurl") || - e.affectsConfiguration("refactai.addressURL") || e.affectsConfiguration("refactai.xDebug") || - e.affectsConfiguration("refactai.apiKey") || e.affectsConfiguration("refactai.insecureSSL") || e.affectsConfiguration("refactai.ast") || e.affectsConfiguration("refactai.astFileLimit") || @@ -375,10 +337,6 @@ export function activate(context: vscode.ExtensionContext) }, 1000); } - if (e.affectsConfiguration("refactai.apiKey") || e.affectsConfiguration("refactai.addressURL")) { - global.side_panel?.handleSettingsChange(); - } - if ( e.affectsConfiguration("refactai.ast") || e.affectsConfiguration("refactai.astFileLimit") || @@ -492,10 +450,6 @@ export async function deactivate(context: vscode.ExtensionContext) export async function status_bar_clicked() { let editor = vscode.window.activeTextEditor; - if (!userLogin.secret_api_key()) { - userLogin.login_message(); - return; - } let selection: string | undefined; if (global.status_bar.ast_limit_hit || global.status_bar.vecdb_limit_hit) { diff --git a/src/fetchAPI.ts b/src/fetchAPI.ts index fbfe2c5d..95a4e276 100644 --- a/src/fetchAPI.ts +++ b/src/fetchAPI.ts @@ -271,12 +271,6 @@ export function rust_url(addthis: string) export function inference_context(third_party: boolean) { - // let modified_url = vscode.workspace.getConfiguration().get('refactai.infurl'); - // if (!modified_url) { - // // Backward compatibility: codify is the old name - // modified_url = vscode.workspace.getConfiguration().get('codify.infurl'); - // } - // in previous versions, it was possible to skip certificate verification return { disconnect: fetchH2.disconnect, disconnectAll: fetchH2.disconnectAll, @@ -342,7 +336,6 @@ export function fetch_code_completion( }); const headers = { "Content-Type": "application/json", - // "Authorization": `Bearer ${apiKey}`, }; let req = new fetchH2.Request(url, { method: "POST", @@ -386,11 +379,6 @@ export function fetch_chat_promise( console.log(["fetch_chat_promise: No rust binary working"]); return [Promise.reject("No rust binary working"), scope, ""]; } - const apiKey = "any-key-will-work"; - if (!apiKey) { - return [Promise.reject("No API key"), "chat", ""]; - } - let ctx = inference_context(third_party); // an empty tools array causes issues @@ -407,7 +395,6 @@ export function fetch_chat_promise( const headers = { "Content-Type": "application/json", - "Authorization": `Bearer ${apiKey}`, }; let req = new fetchH2.Request(url, { diff --git a/src/interactiveDiff.ts b/src/interactiveDiff.ts index 884529d8..0fc2635b 100644 --- a/src/interactiveDiff.ts +++ b/src/interactiveDiff.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import * as vscode from 'vscode'; import * as fetchAPI from "./fetchAPI"; -import * as userLogin from "./userLogin"; import * as Diff from "diff"; // Documentation: https://github.com/kpdecker/jsdiff/ import * as storeVersions from './storeVersions'; import * as estate from './estate'; @@ -42,180 +41,6 @@ export async function on_cursor_moved(editor: vscode.TextEditor, pos: vscode.Pos } } - -// export async function query_diff( -// editor: vscode.TextEditor, -// sensitive_area: vscode.Range, -// model_function: string, -// model_force: string, -// third_party: boolean, -// ) { -// // NOT called from estate switch mode -// let state = estate.state_of_editor(editor, "query_diff"); -// if (!state) { -// return; -// } -// state.showing_diff_for_function = undefined; -// state.showing_diff_for_model = undefined; -// state.showing_diff_thirdparty = third_party; -// state.showing_diff_modif_doc = undefined; - -// let doc = editor.document; -// let access_level = await privacy.get_file_access(doc.fileName); -// if (third_party && access_level < 2) { -// return; -// } -// if (!third_party && access_level < 1) { -// return; -// } - -// let cancellationTokenSource = new vscode.CancellationTokenSource(); -// let cancelToken = cancellationTokenSource.token; -// let request = new fetchAPI.PendingRequest(undefined, cancelToken); - -// await fetchAPI.cancel_all_requests_and_wait_until_finished(); -// if (state.get_mode() !== estate.Mode.Normal) { -// await estate.back_to_normal(state); -// } -// request.cancellationTokenSource = cancellationTokenSource; -// let login = await userLogin.inference_login(); -// if (!login) { return; } -// await fetchAPI.wait_until_all_requests_finished(); -// if (cancelToken.isCancellationRequested) { -// return; -// } -// let file_name = storeVersions.filename_from_document(doc); -// await estate.switch_mode(state, estate.Mode.DiffWait); -// state.diff_lens_pos = sensitive_area.start.line; -// state.showing_diff_for_range = sensitive_area; -// state.diff_load_animation_head = 0; -// state.diff_load_animation_mid = ""; -// animation_start(editor, state); -// codeLens.quick_refresh(); - -// async function _streaming_callback(json: any) -// { -// if (!state) { -// return; -// } -// if (json === undefined) { -// if (state.get_mode() === estate.Mode.DiffWait) { -// await estate.switch_mode(state, estate.Mode.Normal); -// } -// return; -// } -// if (state.get_mode() !== estate.Mode.DiffWait) { -// return; -// } -// if (cancelToken.isCancellationRequested) { -// console.log("diff request is cancelled, new data is coming"); -// if (state.get_mode() === estate.Mode.DiffWait) { -// state.diff_lens_pos = Number.MAX_SAFE_INTEGER; -// codeLens.quick_refresh(); -// await estate.switch_mode(state, estate.Mode.Normal); -// } -// return; -// } else { -// if (json && json["choices"]) { -// // let modif_doc = json["choices"][0]["files"][file_name]; -// // let files = files_from_head_mid_tail(sources, json["choices"][0]["files_head_mid_tail"]); -// let files: { [key: string]: string } = {}; -// let choice0 = json["choices"][0]; -// if ("files_head_mid_tail" in choice0) { -// let files_head_mid_tail = json["choices"][0]["files_head_mid_tail"]; -// for (let key in files_head_mid_tail) { -// let hmt = files_head_mid_tail[key]; -// let original_doc = sources[key]; -// files[key] = original_doc.substring(0, hmt["head"]) + hmt["mid"] + original_doc.substring(original_doc.length-hmt["tail"]); -// if (key === file_name) { -// state.diff_load_animation_head = hmt["head"]; -// state.diff_load_animation_mid = hmt["mid"]; -// } -// } -// } else { -// files = choice0["files"]; -// } -// let modif_doc = files[file_name]; -// // if (feedback) { -// // feedback.results = files; -// // feedback.ts_presented = Date.now(); -// // } -// state.showing_diff_for_function = model_function; -// state.showing_diff_for_model = model_force; -// state.showing_diff_thirdparty = third_party; -// state.showing_diff_modif_doc = modif_doc; -// await estate.switch_mode(state, estate.Mode.DiffWait); -// } -// if (json && json["metering_balance"]) { -// global.user_metering_balance = json["metering_balance"]; -// if (global.side_panel) { -// global.side_panel.update_webview(); -// } -// } -// } -// } - -// async function _streaming_end_callback(any_error: boolean) -// { -// if (!state) { -// return; -// } -// console.log("streaming end callback"); -// if (state.get_mode() === estate.Mode.DiffWait) { -// await estate.switch_mode(state, estate.Mode.Normal); -// await estate.switch_mode(state, estate.Mode.Diff); -// } -// } - -// let whole_doc = doc.getText(); -// let cursor0 = doc.offsetAt(sensitive_area.start); -// let cursor1 = doc.offsetAt(sensitive_area.end); -// let cursors: number[]; -// [whole_doc, cursors] = crlf.cleanup_cr_lf(whole_doc, [cursor0, cursor1]); -// cursor0 = cursors[0]; -// cursor1 = cursors[1]; -// let sources: { [key: string]: string } = {}; -// let no_newline = whole_doc[whole_doc.length-1] !== "\n"; -// if (no_newline) { -// whole_doc += "\n"; -// } -// sources[file_name] = whole_doc; -// let max_tokens = 550; -// let stop_tokens: string[] = []; -// let max_edits = model_function==="diff-atcursor" ? 1 : 10; // the other is "diff-selection" -// let stream = true; - -// request.set_streaming_callback(_streaming_callback, _streaming_end_callback); -// // let feedback = state.data_feedback_candidate; -// // if (feedback) { -// // feedback.sources = sources; -// // feedback.intent = estate.global_intent; -// // feedback.function = model_function; -// // feedback.cursor_file = file_name; -// // feedback.cursor_pos0 = cursor0; -// // feedback.cursor_pos1 = cursor1; -// // feedback.ts_req = Date.now(); -// // } - -// request.supply_stream(...fetchAPI.fetch_api_promise( -// cancelToken, -// "query_diff" + ":" + model_function, // scope -// sources, -// estate.global_intent, -// model_function, -// file_name, -// cursor0, -// cursor1, -// max_tokens, -// max_edits, -// stop_tokens, -// stream, -// model_force, -// third_party, -// )); -// } - - export async function animation_start(editor: vscode.TextEditor, state: estate.StateOfEditor) { // highlight.hl_clear(editor); @@ -593,20 +418,6 @@ export async function like_and_accept(editor: vscode.TextEditor) await editor.document.save(); } - -// export async function query_the_same_thing_again(editor: vscode.TextEditor) -// { -// let state = estate.state_of_editor(editor, "query_the_same_thing_again"); -// if (!state) { -// return; -// } -// if (state.showing_diff_for_range !== undefined && state.showing_diff_for_function !== undefined && state.showing_diff_for_model !== undefined) { -// _remove_decoration(editor); -// query_diff(editor, state.showing_diff_for_range, state.showing_diff_for_function, state.showing_diff_for_model, state.showing_diff_thirdparty); -// } -// } - - export function hands_off_dont_remove_anything(editor: vscode.TextEditor) { // Don't delete anything, user has already started same edit, leave it alone diff --git a/src/launchRust.ts b/src/launchRust.ts index 5c02152e..a7a1f64d 100644 --- a/src/launchRust.ts +++ b/src/launchRust.ts @@ -1,485 +1,455 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import * as vscode from 'vscode'; -import * as fetchH2 from 'fetch-h2'; -import * as userLogin from './userLogin'; -import * as fetchAPI from "./fetchAPI"; -import { join } from 'path'; -import * as lspClient from 'vscode-languageclient/node'; -import * as net from 'net'; -import { register_commands } from './rconsoleCommands'; -import { QuickActionProvider } from './quickProvider'; -import { TeamsGroup } from 'refact-chat-js/dist/events'; - - -const DEBUG_HTTP_PORT = 8001; -const DEBUG_LSP_PORT = 8002; - - -export class RustBinaryBlob { - public asset_path: string; - public cmdline: string[] = []; - public port: number = 0; - public lsp_disposable: vscode.Disposable | undefined = undefined; - public lsp_client: lspClient.LanguageClient | undefined = undefined; - public lsp_socket: net.Socket | undefined = undefined; - public lsp_client_options: lspClient.LanguageClientOptions; - public ping_response: string = ""; - - constructor(asset_path: string) { - this.asset_path = asset_path; - this.lsp_client_options = { - documentSelector: [{ scheme: 'file', language: '*' }], - diagnosticCollectionName: 'RUST LSP', - progressOnInitialization: true, - traceOutputChannel: vscode.window.createOutputChannel('RUST LSP'), - revealOutputChannelOn: lspClient.RevealOutputChannelOn.Error, - }; - } - - public x_debug(): number { - let xdebug = vscode.workspace.getConfiguration().get("refactai.xDebug"); - if (xdebug === undefined || xdebug === null || xdebug === 0 || xdebug === "0" || xdebug === false || xdebug === "false") { - return 0; - } - return 1; - } - - public get_port(): number { - let xdebug = this.x_debug(); - if (xdebug) { - return 8001; - } else { - return this.port; - } - } - - public rust_url(): string { - let xdebug = this.x_debug(); - let port = xdebug ? 8001 : this.port; - if (!port) { - return ""; - } - return "http2://127.0.0.1:" + port.toString() + "/"; - } - - public attemping_to_reach(): string { - let xdebug = this.x_debug(); - if (xdebug) { - return `debug rust binary on ports ${DEBUG_HTTP_PORT} and ${DEBUG_LSP_PORT}`; - } else { - let addr = userLogin.get_address(); - if (addr === "") { - return ""; - } - return `${addr}`; - } - } - - public async settings_changed() { - for (let i = 0; i < 5; i++) { - console.log(`RUST settings changed, attempt to restart ${i + 1}`); - let xdebug = this.x_debug(); - let api_key: string = userLogin.secret_api_key(); - let port: number; - let ping_response: string; - - // const maybe_active_workspace = global.global_context.globalState.get('active_workspace') as Workspace | undefined; - // const active_workspace_id = maybe_active_workspace ? maybe_active_workspace.workspace_id : null; - if (xdebug === 0) { - if (this.lsp_client) { // running - port = this.port; // keep the same port - ping_response = this.ping_response; - } else { - port = Math.floor(Math.random() * 20) + 9080; - ping_response = `ping-${Math.floor(Math.random() * 0x10000000000000000).toString(16)}`; - } - } else { - port = DEBUG_HTTP_PORT; - console.log(`RUST debug is set, don't start the rust binary. Will attempt HTTP port ${DEBUG_HTTP_PORT}, LSP port ${DEBUG_LSP_PORT}`); - console.log("Also, will try to read caps. If that fails, things like lists of available models will be empty."); - this.cmdline = []; - await this.terminate(); // terminate our own - await this.read_caps(); // debugging rust already running, can read here - - await this.fetch_toolbox_config(); - // await register_commands(); - await this.start_lsp_socket(); - return; - } - let url: string = userLogin.get_address(); - if (url === "") { - this.cmdline = []; - await this.terminate(); - return; - } - let plugin_version = vscode.extensions.getExtension("smallcloud.codify")?.packageJSON.version; // codify is the old name of the product, smallcloud is the company - if (!plugin_version) { - plugin_version = "unknown"; - } - - let new_cmdline: string[] = [ - join(this.asset_path, "refact-lsp"), - "--address-url", url, - "--api-key", api_key, - "--ping-message", ping_response, - "--http-port", port.toString(), - "--lsp-stdin-stdout", "1", - "--enduser-client-version", "refact-" + plugin_version + "/vscode-" + vscode.version, - "--basic-telemetry", - ]; - - // if (active_workspace_id !== null && active_workspace_id !== undefined) { - // new_cmdline.push("--active-workspace-id"); - // new_cmdline.push(active_workspace_id.toString()); - // } - - if (vscode.workspace.getConfiguration().get("refactai.vecdb")) { - new_cmdline.push("--vecdb"); - const vecdb_limit = vscode.workspace.getConfiguration().get("refactai.vecdbFileLimit") ?? 15000; - new_cmdline.push(`--vecdb-max-files`); - new_cmdline.push(`${vecdb_limit}`); - } - if (vscode.workspace.getConfiguration().get("refactai.ast")) { - new_cmdline.push("--ast"); - const ast_limit = vscode.workspace.getConfiguration().get("refactai.astFileLimit") ?? 15000; - new_cmdline.push(`--ast-max-files`); - new_cmdline.push(`${ast_limit}`); - } - let insecureSSL = vscode.workspace.getConfiguration().get("refactai.insecureSSL"); - if (insecureSSL) { - new_cmdline.push("--insecure"); - } - let experimental = vscode.workspace.getConfiguration().get("refactai.xperimental"); - if (experimental) { - new_cmdline.push("--experimental"); - } - - let cmdline_existing: string = this.cmdline.join(" "); - let cmdline_new: string = new_cmdline.join(" "); - if (cmdline_existing !== cmdline_new) { - this.cmdline = new_cmdline; - this.port = port; - this.ping_response = ping_response; - await this.launch(); - } - if (this.lsp_disposable !== undefined) { - break; - } - } - global.side_panel?.handleSettingsChange(); - } - - public async launch() { - await this.terminate(); - let xdebug = this.x_debug(); - if (xdebug) { - await this.start_lsp_socket(); - } else { - await this.start_lsp_stdin_stdout(); - } - } - - public stop_lsp() { - let my_lsp_client_copy = this.lsp_client; - if (my_lsp_client_copy) { - console.log("RUST STOP"); - let ts = Date.now(); - my_lsp_client_copy.stop() // will complete in the background, otherwise it might die inside stop() such that execution never reaches even finally() and we don't know how to restart the process :/ - .then(() => { - console.log(`RUST /STOP completed in ${Date.now() - ts}ms`); - }) - .catch((e) => { - console.log(`RUST STOP ERROR e=${e}`); - }) - .finally(() => { - console.log("RUST STOP FINALLY"); - }); - } - this.lsp_dispose(); - } - - public lsp_dispose() { - if (this.lsp_disposable) { - this.lsp_disposable.dispose(); - this.lsp_disposable = undefined; - } - this.lsp_client = undefined; - this.lsp_socket = undefined; - } - - public async terminate() { - this.stop_lsp(); - await fetchH2.disconnectAll(); - global.have_caps = false; - global.status_bar.choose_color(); - } - - public async read_caps() { - try { - let url = this.rust_url(); - if (!url) { - return Promise.reject("read_caps no rust binary working, very strange"); - } - url += "v1/caps"; - let req = new fetchH2.Request(url, { - method: "GET", - redirect: "follow", - cache: "no-cache", - referrer: "no-referrer" - }); - let resp = await fetchH2.fetch(req); - if (resp.status !== 200) { - console.log(["read_caps http status", resp.status]); - return Promise.reject("read_caps bad status"); - } - let json = await resp.json(); - console.log(["successful read_caps", json]); - global.chat_models = Object.keys(json["chat_models"]); - global.chat_default_model = json["chat_default_model"] || ""; - global.have_caps = true; - global.status_bar.set_socket_error(false, ""); - } catch (e) { - global.chat_models = []; - global.have_caps = false; - console.log(["read_caps:", e]); - } - global.status_bar.choose_color(); - fetchAPI.maybe_show_rag_status(); - let current_editor = vscode.window.activeTextEditor; - if (current_editor) { - fetchAPI.lsp_set_active_document(current_editor); - } - - const promptCustomization = await fetchAPI.get_prompt_customization(); - if (promptCustomization && promptCustomization.toolbox_commands) { - await QuickActionProvider.updateActions(promptCustomization.toolbox_commands as Record); - } - } - - public async ping() { - try { - let url = this.rust_url(); - if (!url) { - return Promise.reject("ping no rust binary working, very strange"); - } - url += "v1/ping"; - console.log([url]); - let req = new fetchH2.Request(url, { - method: "GET", - redirect: "follow", - cache: "no-cache", - referrer: "no-referrer", - }); - let resp = await fetchH2.fetch(req, { timeout: 5000 }); - if (resp.status !== 200) { - console.log(["ping http status", resp.status]); - return Promise.reject("ping bad status"); - } - let pong = await resp.text(); - let success = (pong === this.ping_response || pong === this.ping_response + "\n"); - console.log([`pong=${pong}`, `expected ${this.ping_response}`, success]); - return success; - } catch (e) { - console.log(["ping error:", e]); - } - return false; - } - - public async start_lsp_stdin_stdout() { - console.log("RUST start_lsp_stdint_stdout"); - let path = this.cmdline[0]; - let serverOptions: lspClient.ServerOptions; - serverOptions = { - run: { - command: String(path), - args: this.cmdline.slice(1), - transport: lspClient.TransportKind.stdio, - options: { cwd: process.cwd(), detached: false, shell: false } - }, - debug: { - command: String(path), - args: this.cmdline.slice(1), - transport: lspClient.TransportKind.stdio, - options: { cwd: process.cwd(), detached: false, shell: false } - } - }; - this.lsp_client = new lspClient.LanguageClient( - 'RUST LSP', - serverOptions, - this.lsp_client_options - ); - this.lsp_disposable = this.lsp_client.start(); - - console.log(`${logts()} RUST START`); - const somethings_wrong_timeout = 10000; - const startTime = Date.now(); - let started_okay = false; - - const onReadyPromise = this.lsp_client.onReady().then(() => { - started_okay = true; - }); - - try { - while (true) { - const elapsedTime = Date.now() - startTime; - if (started_okay) { - console.log(`${logts()} RUST /START after ${elapsedTime}ms`); - break; - } - if (elapsedTime >= somethings_wrong_timeout) { - throw new Error("timeout"); - } - console.log(`${logts()} RUST waiting...`); - await new Promise(resolve => setTimeout(resolve, 100)); - } - } catch (e) { - console.log(`${logts()} RUST START PROBLEM e=${e}`); - this.lsp_dispose(); - return; - } - - let success = await this.ping(); - if (!success) { - console.log("RUST ping failed"); - this.lsp_dispose(); - return; - } - // At this point we had successful client_info and workspace_folders server to client calls, - // therefore the LSP server is started. - // A little doubt remains about the http port, but it's very likely there's no race. - await this.read_caps(); - await this.fetch_toolbox_config(); - } - - public async start_lsp_socket() { - console.log("RUST start_lsp_socket"); - this.lsp_socket = new net.Socket(); - this.lsp_socket.on('error', (error) => { - console.log("RUST socket error"); - console.log(error); - console.log("RUST /error"); - this.lsp_dispose(); - }); - this.lsp_socket.on('close', () => { - console.log("RUST socket closed"); - this.lsp_dispose(); - }); - this.lsp_socket.on('connect', async () => { - console.log("RUST LSP socket connected"); - this.lsp_client = new lspClient.LanguageClient( - 'Custom rust LSP server', - async () => { - if (this.lsp_socket === undefined) { - return Promise.reject("this.lsp_socket is undefined, that should not happen"); - } - return Promise.resolve({ - reader: this.lsp_socket, - writer: this.lsp_socket - }); - }, - this.lsp_client_options - ); - // client.registerProposedFeatures(); - this.lsp_disposable = this.lsp_client.start(); - console.log(`RUST DEBUG START`); - try { - await this.lsp_client.onReady(); - console.log(`RUST DEBUG /START`); - } catch (e) { - console.log(`RUST DEBUG START PROBLEM e=${e}`); - } - }); - this.lsp_socket.connect(DEBUG_LSP_PORT); - } - - async rag_status() { - try { - let url = this.rust_url(); - if (!url) { - return Promise.reject("rag status no rust binary working, very strange"); - } - url += "v1/rag-status"; - let req = new fetchH2.Request(url, { - method: "GET", - redirect: "follow", - cache: "no-cache", - referrer: "no-referrer", - }); - let resp = await fetchH2.fetch(req, { timeout: 5000 }); - if (resp.status !== 200) { - console.log(["rag status http status", resp.status]); - return Promise.reject("rag status bad status"); - } - let rag_status = await resp.json(); - return rag_status; - } catch (e) { - console.log(["rag status error:", e]); - } - return false; - } - - async fetch_toolbox_config(): Promise { - const rust_url = this.rust_url(); - - if (!rust_url) { - console.log(["fetch_toolbox_config: No rust binary working"]); - return Promise.reject("No rust binary working"); - } - const url = rust_url + "v1/customization"; - - const request = new fetchH2.Request(url, { method: "GET" }); - - const response = await fetchH2.fetch(request, { timeout: 5000 }); - - if (!response.ok) { - console.log([ - "fetch_toolbox_config: Error fetching toolbox config", - response.status, - url, - ]); - return Promise.reject( - `Error fetching toolbox config: [status: ${response.status}] [statusText: ${response.statusText}]` - ); - } - - // TBD: type-guards or some sort of runtime validation - const json = await response.json() as ToolboxConfig; - console.log(["success fetch_toolbox_config", json]); - - global.toolbox_config = json; - await register_commands(); - return json; - } -} - -export type ChatMessageFromLsp = { - role: string; - content: string; -}; - -export type ToolboxCommand = { - description: string; - messages: ChatMessageFromLsp[]; - selection_needed: number[]; - selection_unwanted: boolean; - insert_at_cursor: boolean; -}; - -export type SystemPrompt = { - description: string; - text: string; -}; - -export type ToolboxConfig = { - system_prompts: Record; - toolbox_commands: Record; -}; - -function logts() { - const now = new Date(); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); - const milliseconds = String(now.getMilliseconds()).padStart(3, '0'); - return `${hours}${minutes}${seconds}.${milliseconds}`; -} +/* eslint-disable @typescript-eslint/naming-convention */ +import * as vscode from 'vscode'; +import * as fetchH2 from 'fetch-h2'; +import * as fetchAPI from "./fetchAPI"; +import { join } from 'path'; +import * as lspClient from 'vscode-languageclient/node'; +import * as net from 'net'; +import { register_commands } from './rconsoleCommands'; +import { QuickActionProvider } from './quickProvider'; + + +const DEBUG_HTTP_PORT = 8001; +const DEBUG_LSP_PORT = 8002; + + +export class RustBinaryBlob { + public asset_path: string; + public cmdline: string[] = []; + public port: number = 0; + public lsp_disposable: vscode.Disposable | undefined = undefined; + public lsp_client: lspClient.LanguageClient | undefined = undefined; + public lsp_socket: net.Socket | undefined = undefined; + public lsp_client_options: lspClient.LanguageClientOptions; + public ping_response: string = ""; + + constructor(asset_path: string) { + this.asset_path = asset_path; + this.lsp_client_options = { + documentSelector: [{ scheme: 'file', language: '*' }], + diagnosticCollectionName: 'RUST LSP', + progressOnInitialization: true, + traceOutputChannel: vscode.window.createOutputChannel('RUST LSP'), + revealOutputChannelOn: lspClient.RevealOutputChannelOn.Error, + }; + } + + public x_debug(): number { + let xdebug = vscode.workspace.getConfiguration().get("refactai.xDebug"); + if (xdebug === undefined || xdebug === null || xdebug === 0 || xdebug === "0" || xdebug === false || xdebug === "false") { + return 0; + } + return 1; + } + + public get_port(): number { + let xdebug = this.x_debug(); + if (xdebug) { + return 8001; + } else { + return this.port; + } + } + + public rust_url(): string { + let xdebug = this.x_debug(); + let port = xdebug ? 8001 : this.port; + if (!port) { + return ""; + } + return "http2://127.0.0.1:" + port.toString() + "/"; + } + + public attemping_to_reach(): string { + let xdebug = this.x_debug(); + if (xdebug) { + return `debug rust binary on ports ${DEBUG_HTTP_PORT} and ${DEBUG_LSP_PORT}`; + } + return "local Refact engine"; + } + + public async settings_changed() { + for (let i = 0; i < 5; i++) { + console.log(`RUST settings changed, attempt to restart ${i + 1}`); + let xdebug = this.x_debug(); + let port: number; + let ping_response: string; + + if (xdebug === 0) { + if (this.lsp_client) { // running + port = this.port; // keep the same port + ping_response = this.ping_response; + } else { + port = Math.floor(Math.random() * 20) + 9080; + ping_response = `ping-${Math.floor(Math.random() * 0x10000000000000000).toString(16)}`; + } + } else { + port = DEBUG_HTTP_PORT; + console.log(`RUST debug is set, don't start the rust binary. Will attempt HTTP port ${DEBUG_HTTP_PORT}, LSP port ${DEBUG_LSP_PORT}`); + console.log("Also, will try to read caps. If that fails, things like lists of available models will be empty."); + this.cmdline = []; + await this.terminate(); // terminate our own + await this.read_caps(); // debugging rust already running, can read here + + await this.fetch_toolbox_config(); + // await register_commands(); + await this.start_lsp_socket(); + return; + } + let new_cmdline: string[] = [ + join(this.asset_path, "refact-lsp"), + "--ping-message", ping_response, + "--http-port", port.toString(), + "--lsp-stdin-stdout", "1", + ]; + + if (vscode.workspace.getConfiguration().get("refactai.vecdb")) { + new_cmdline.push("--vecdb"); + const vecdb_limit = vscode.workspace.getConfiguration().get("refactai.vecdbFileLimit") ?? 15000; + new_cmdline.push(`--vecdb-max-files`); + new_cmdline.push(`${vecdb_limit}`); + } + if (vscode.workspace.getConfiguration().get("refactai.ast")) { + new_cmdline.push("--ast"); + const ast_limit = vscode.workspace.getConfiguration().get("refactai.astFileLimit") ?? 15000; + new_cmdline.push(`--ast-max-files`); + new_cmdline.push(`${ast_limit}`); + } + let insecureSSL = vscode.workspace.getConfiguration().get("refactai.insecureSSL"); + if (insecureSSL) { + new_cmdline.push("--insecure"); + } + let experimental = vscode.workspace.getConfiguration().get("refactai.xperimental"); + if (experimental) { + new_cmdline.push("--experimental"); + } + + let cmdline_existing: string = this.cmdline.join(" "); + let cmdline_new: string = new_cmdline.join(" "); + if (cmdline_existing !== cmdline_new) { + this.cmdline = new_cmdline; + this.port = port; + this.ping_response = ping_response; + await this.launch(); + } + if (this.lsp_disposable !== undefined) { + break; + } + } + global.side_panel?.handleSettingsChange(); + } + + public async launch() { + await this.terminate(); + let xdebug = this.x_debug(); + if (xdebug) { + await this.start_lsp_socket(); + } else { + await this.start_lsp_stdin_stdout(); + } + } + + public stop_lsp() { + let my_lsp_client_copy = this.lsp_client; + if (my_lsp_client_copy) { + console.log("RUST STOP"); + let ts = Date.now(); + my_lsp_client_copy.stop() // will complete in the background, otherwise it might die inside stop() such that execution never reaches even finally() and we don't know how to restart the process :/ + .then(() => { + console.log(`RUST /STOP completed in ${Date.now() - ts}ms`); + }) + .catch((e) => { + console.log(`RUST STOP ERROR e=${e}`); + }) + .finally(() => { + console.log("RUST STOP FINALLY"); + }); + } + this.lsp_dispose(); + } + + public lsp_dispose() { + if (this.lsp_disposable) { + this.lsp_disposable.dispose(); + this.lsp_disposable = undefined; + } + this.lsp_client = undefined; + this.lsp_socket = undefined; + } + + public async terminate() { + this.stop_lsp(); + await fetchH2.disconnectAll(); + global.have_caps = false; + global.status_bar.choose_color(); + } + + public async read_caps() { + try { + let url = this.rust_url(); + if (!url) { + return Promise.reject("read_caps no rust binary working, very strange"); + } + url += "v1/caps"; + let req = new fetchH2.Request(url, { + method: "GET", + redirect: "follow", + cache: "no-cache", + referrer: "no-referrer" + }); + let resp = await fetchH2.fetch(req); + if (resp.status !== 200) { + console.log(["read_caps http status", resp.status]); + return Promise.reject("read_caps bad status"); + } + let json = await resp.json(); + console.log(["successful read_caps", json]); + global.chat_models = Object.keys(json["chat_models"]); + global.chat_default_model = json["chat_default_model"] || ""; + global.have_caps = true; + global.status_bar.set_socket_error(false, ""); + } catch (e) { + global.chat_models = []; + global.have_caps = false; + console.log(["read_caps:", e]); + } + global.status_bar.choose_color(); + fetchAPI.maybe_show_rag_status(); + let current_editor = vscode.window.activeTextEditor; + if (current_editor) { + fetchAPI.lsp_set_active_document(current_editor); + } + + const promptCustomization = await fetchAPI.get_prompt_customization(); + if (promptCustomization && promptCustomization.toolbox_commands) { + await QuickActionProvider.updateActions(promptCustomization.toolbox_commands as Record); + } + } + + public async ping() { + try { + let url = this.rust_url(); + if (!url) { + return Promise.reject("ping no rust binary working, very strange"); + } + url += "v1/ping"; + console.log([url]); + let req = new fetchH2.Request(url, { + method: "GET", + redirect: "follow", + cache: "no-cache", + referrer: "no-referrer", + }); + let resp = await fetchH2.fetch(req, { timeout: 5000 }); + if (resp.status !== 200) { + console.log(["ping http status", resp.status]); + return Promise.reject("ping bad status"); + } + let pong = await resp.text(); + let success = (pong === this.ping_response || pong === this.ping_response + "\n"); + console.log([`pong=${pong}`, `expected ${this.ping_response}`, success]); + return success; + } catch (e) { + console.log(["ping error:", e]); + } + return false; + } + + public async start_lsp_stdin_stdout() { + console.log("RUST start_lsp_stdint_stdout"); + let path = this.cmdline[0]; + let serverOptions: lspClient.ServerOptions; + serverOptions = { + run: { + command: String(path), + args: this.cmdline.slice(1), + transport: lspClient.TransportKind.stdio, + options: { cwd: process.cwd(), detached: false, shell: false } + }, + debug: { + command: String(path), + args: this.cmdline.slice(1), + transport: lspClient.TransportKind.stdio, + options: { cwd: process.cwd(), detached: false, shell: false } + } + }; + this.lsp_client = new lspClient.LanguageClient( + 'RUST LSP', + serverOptions, + this.lsp_client_options + ); + this.lsp_disposable = this.lsp_client.start(); + + console.log(`${logts()} RUST START`); + const somethings_wrong_timeout = 10000; + const startTime = Date.now(); + let started_okay = false; + + const onReadyPromise = this.lsp_client.onReady().then(() => { + started_okay = true; + }); + + try { + while (true) { + const elapsedTime = Date.now() - startTime; + if (started_okay) { + console.log(`${logts()} RUST /START after ${elapsedTime}ms`); + break; + } + if (elapsedTime >= somethings_wrong_timeout) { + throw new Error("timeout"); + } + console.log(`${logts()} RUST waiting...`); + await new Promise(resolve => setTimeout(resolve, 100)); + } + } catch (e) { + console.log(`${logts()} RUST START PROBLEM e=${e}`); + this.lsp_dispose(); + return; + } + + let success = await this.ping(); + if (!success) { + console.log("RUST ping failed"); + this.lsp_dispose(); + return; + } + // At this point we had successful client_info and workspace_folders server to client calls, + // therefore the LSP server is started. + // A little doubt remains about the http port, but it's very likely there's no race. + await this.read_caps(); + await this.fetch_toolbox_config(); + } + + public async start_lsp_socket() { + console.log("RUST start_lsp_socket"); + this.lsp_socket = new net.Socket(); + this.lsp_socket.on('error', (error) => { + console.log("RUST socket error"); + console.log(error); + console.log("RUST /error"); + this.lsp_dispose(); + }); + this.lsp_socket.on('close', () => { + console.log("RUST socket closed"); + this.lsp_dispose(); + }); + this.lsp_socket.on('connect', async () => { + console.log("RUST LSP socket connected"); + this.lsp_client = new lspClient.LanguageClient( + 'Custom rust LSP server', + async () => { + if (this.lsp_socket === undefined) { + return Promise.reject("this.lsp_socket is undefined, that should not happen"); + } + return Promise.resolve({ + reader: this.lsp_socket, + writer: this.lsp_socket + }); + }, + this.lsp_client_options + ); + // client.registerProposedFeatures(); + this.lsp_disposable = this.lsp_client.start(); + console.log(`RUST DEBUG START`); + try { + await this.lsp_client.onReady(); + console.log(`RUST DEBUG /START`); + } catch (e) { + console.log(`RUST DEBUG START PROBLEM e=${e}`); + } + }); + this.lsp_socket.connect(DEBUG_LSP_PORT); + } + + async rag_status() { + try { + let url = this.rust_url(); + if (!url) { + return Promise.reject("rag status no rust binary working, very strange"); + } + url += "v1/rag-status"; + let req = new fetchH2.Request(url, { + method: "GET", + redirect: "follow", + cache: "no-cache", + referrer: "no-referrer", + }); + let resp = await fetchH2.fetch(req, { timeout: 5000 }); + if (resp.status !== 200) { + console.log(["rag status http status", resp.status]); + return Promise.reject("rag status bad status"); + } + let rag_status = await resp.json(); + return rag_status; + } catch (e) { + console.log(["rag status error:", e]); + } + return false; + } + + async fetch_toolbox_config(): Promise { + const rust_url = this.rust_url(); + + if (!rust_url) { + console.log(["fetch_toolbox_config: No rust binary working"]); + return Promise.reject("No rust binary working"); + } + const url = rust_url + "v1/customization"; + + const request = new fetchH2.Request(url, { method: "GET" }); + + const response = await fetchH2.fetch(request, { timeout: 5000 }); + + if (!response.ok) { + console.log([ + "fetch_toolbox_config: Error fetching toolbox config", + response.status, + url, + ]); + return Promise.reject( + `Error fetching toolbox config: [status: ${response.status}] [statusText: ${response.statusText}]` + ); + } + + // TBD: type-guards or some sort of runtime validation + const json = await response.json() as ToolboxConfig; + console.log(["success fetch_toolbox_config", json]); + + global.toolbox_config = json; + await register_commands(); + return json; + } +} + +export type ChatMessageFromLsp = { + role: string; + content: string; +}; + +export type ToolboxCommand = { + description: string; + messages: ChatMessageFromLsp[]; + selection_needed: number[]; + selection_unwanted: boolean; + insert_at_cursor: boolean; +}; + +export type SystemPrompt = { + description: string; + text: string; +}; + +export type ToolboxConfig = { + system_prompts: Record; + toolbox_commands: Record; +}; + +function logts() { + const now = new Date(); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + const milliseconds = String(now.getMilliseconds()).padStart(3, '0'); + return `${hours}${minutes}${seconds}.${milliseconds}`; +} diff --git a/src/sidebar.ts b/src/sidebar.ts index 1c8a15cf..983c09d2 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -9,10 +9,8 @@ import { getKeyBindingForChat } from "./getKeybindings"; import { type ChatMessages, fim, - isLogOut, isOpenExternalUrl, updateConfig, - isSetupHost, type FileInfo, setFileInfo, type Snippet, @@ -38,10 +36,7 @@ import { TextDocToolCall, ideSetCodeCompletionModel, ideSetLoginMessage, - ideSetActiveTeamsGroup, - ideClearActiveTeamsGroup, OpenFilePayload, - TeamsGroup, ideTaskDone, ideAskQuestions, ideSwitchToThread @@ -115,9 +110,6 @@ export class PanelWebview implements vscode.WebviewViewProvider { this.js2ts_message = this.js2ts_message.bind(this); this.handleEvents = this.handleEvents.bind(this); - - // Check for bring-your-own-key configuration during initialization - this.checkForBringYourOwnKeyConfig(); this._disposables.push(vscode.window.onDidChangeActiveTextEditor(() => { this.postActiveFileInfo(); @@ -138,9 +130,6 @@ export class PanelWebview implements vscode.WebviewViewProvider { ) { this.handleSettingsChange(); } - if (event.affectsConfiguration("refactai.addressURL")) { - this.checkForBringYourOwnKeyConfig(); - } })); this._disposables.push(vscode.workspace.onDidChangeWorkspaceFolders((event) => { @@ -285,16 +274,15 @@ export class PanelWebview implements vscode.WebviewViewProvider { ?.get("refactai.ast") ?? false; - const apiKey = vscode.workspace.getConfiguration()?.get("refactai.apiKey") ?? ""; - const addressURL = vscode.workspace.getConfiguration()?.get("refactai.addressURL") ?? ""; - const port = global.rust_binary_blob?.get_port() ?? 8001; + const rawPort = global.rust_binary_blob?.get_port(); + const port = typeof rawPort === "number" && Number.isFinite(rawPort) && rawPort > 0 + ? rawPort + : 0; const submitChatWithShiftEnter = vscode.workspace.getConfiguration()?.get("refactai.submitChatWithShiftEnter")?? false; const currentActiveWorkspaceName = this.getActiveWorkspace(); const message = updateConfig({ - apiKey, - addressURL, lspPort: port, shiftEnterToSubmit: submitChatWithShiftEnter, features: {vecdb, ast}, @@ -344,57 +332,8 @@ export class PanelWebview implements vscode.WebviewViewProvider { }); } - /** - * Checks if user has a 'bring-your-own-key' host type configuration and logs them out - * as this option is no longer supported - */ - private async checkForBringYourOwnKeyConfig() { - const hostType = this.context.globalState.get('refactai.hostType'); - const notificationMessage = "The 'bring-your-own-key' login option is no longer available. Your settings have been cleared. Please choose other login option."; - - if (hostType === 'bring-your-own-key') { - vscode.window.showInformationMessage( - notificationMessage, - "Open Settings" - ).then(selection => { - if (selection === "Open Settings") { - vscode.commands.executeCommand("refactaicmd.openSettings"); - } - }); - await this.delete_old_settings(); - - await this.context.globalState.update('refactai.hostType', undefined); - return; - } - - // Fallback for older versions without stored host type - const addressURL = vscode.workspace.getConfiguration()?.get("refactai.addressURL") ?? ""; - const apiKey = vscode.workspace.getConfiguration()?.get("refactai.apiKey") ?? ""; - - const lowerCasedAddressURL = addressURL.toLowerCase(); - if ( - typeof addressURL === 'string' && - !lowerCasedAddressURL.startsWith("http://") && - !lowerCasedAddressURL.startsWith("https://") && - lowerCasedAddressURL.endsWith('.yaml') - ) { - vscode.window.showInformationMessage( - notificationMessage, - "Open Settings" - ).then(selection => { - if (selection === "Open Settings") { - vscode.commands.executeCommand("refactaicmd.openSettings"); - } - }); - - await this.delete_old_settings(); - } - } - public async goto_main() { - await this.checkForBringYourOwnKeyConfig(); - this.address = ""; if (!this._view) { return; @@ -427,20 +366,6 @@ export class PanelWebview implements vscode.WebviewViewProvider { this._view?.webview.postMessage(message); } - public async delete_old_settings() - { - await vscode.workspace.getConfiguration().update('refactai.apiKey', undefined, vscode.ConfigurationTarget.Global); - await vscode.workspace.getConfiguration().update('refactai.addressURL', undefined, vscode.ConfigurationTarget.Global); - await vscode.workspace.getConfiguration().update('codify.apiKey', undefined, vscode.ConfigurationTarget.Global); - if(vscode.workspace.workspaceFolders) { - await vscode.workspace.getConfiguration().update('refactai.apiKey', undefined, vscode.ConfigurationTarget.Workspace); - await vscode.workspace.getConfiguration().update('refactai.addressURL', undefined, vscode.ConfigurationTarget.Workspace); - await vscode.workspace.getConfiguration().update('codify.apiKey', undefined, vscode.ConfigurationTarget.Workspace); - } - - await this.context.globalState.update('refactai.hostType', undefined); - } - public async js2ts_message(data: any) { if (!this._view) { @@ -560,18 +485,6 @@ export class PanelWebview implements vscode.WebviewViewProvider { } } - if (isSetupHost(e)) { - const { host } = e.payload; - await this.context.globalState.update('refactai.hostType', host.type); - await this.delete_old_settings(); - await vscode.workspace.getConfiguration().update('refactai.addressURL', "Refact", vscode.ConfigurationTarget.Global); - await vscode.workspace.getConfiguration().update('refactai.apiKey', host.apiKey, vscode.ConfigurationTarget.Global); - } - - if (isLogOut(e)) { - await this.delete_old_settings(); - } - if (isOpenExternalUrl(e)) { await vscode.env.openExternal(vscode.Uri.parse(e.payload.url)); } @@ -629,14 +542,6 @@ export class PanelWebview implements vscode.WebviewViewProvider { return this.handleSetLoginMessage(e.payload); } - if (ideSetActiveTeamsGroup.match(e)) { - return this.handleSetActiveGroup(e.payload); - } - - if (ideClearActiveTeamsGroup.match(e)) { - return this.handleClearActiveGroup(); - } - if(ideEscapeKeyPressed.match(e)) { return this.handleEscapePressed(e.payload); } @@ -823,23 +728,6 @@ export class PanelWebview implements vscode.WebviewViewProvider { usabilityHints.show_message_from_server('InferenceServer', message); } - async handleSetActiveGroup (group:TeamsGroup) { - await this.context.workspaceState.update( - 'refactai.activeGroup', - group, - ); - console.log(`[DEBUG]: updated locally active group in ./.vscode/settings.json: `, group); - this.handleSettingsChange(); - } - - async handleClearActiveGroup () { - await this.context.workspaceState.update( - 'refactai.activeGroup', - undefined, - ); - this.handleSettingsChange(); - } - async handleEscapePressed(mode: string) { const editor = vscode.window.activeTextEditor; if (!editor) { return; } @@ -1044,10 +932,10 @@ export class PanelWebview implements vscode.WebviewViewProvider { const activeColorTheme = this.getColorTheme(); const vecdb = vscode.workspace.getConfiguration()?.get("refactai.vecdb") ?? false; const ast = vscode.workspace.getConfiguration()?.get("refactai.ast") ?? false; - const apiKey = vscode.workspace.getConfiguration()?.get("refactai.apiKey") ?? ""; - const addressURL = vscode.workspace.getConfiguration()?.get("refactai.addressURL") ?? ""; - const activeTeamsGroup = this.context.workspaceState.get('refactai.activeGroup') ?? null; - const port = global.rust_binary_blob?.get_port() ?? 8001; + const rawPort = global.rust_binary_blob?.get_port(); + const port = typeof rawPort === "number" && Number.isFinite(rawPort) && rawPort > 0 + ? rawPort + : 0; const completeManual = await getKeyBindingForChat("refactaicmd.completionManual"); const shiftEnterToSubmit = vscode.workspace.getConfiguration()?.get("refactai.shiftEnterToSubmit")?? false; @@ -1072,16 +960,11 @@ export class PanelWebview implements vscode.WebviewViewProvider { keyBindings: { completeManual, }, - apiKey, - addressURL, lspPort: port, currentWorkspaceName: currentActiveWorkspaceName, }; const state: Partial = { - teams: { - group: activeTeamsGroup, - }, current_project: {name: vscode.workspace.name ?? ""}, config, }; @@ -1116,6 +999,9 @@ export class PanelWebview implements vscode.WebviewViewProvider { snapshot_received: false, attached_text_files: [], task_widget_expanded: false, + memory_enrichment_user_touched: false, + manual_preview_items: [], + manual_preview_ran: false, }, }, system_prompt: {}, @@ -1147,11 +1033,6 @@ export class PanelWebview implements vscode.WebviewViewProvider { ); const nonce = this.getNonce(); - let existing_address = vscode.workspace.getConfiguration().get("refactai.addressURL"); - if (typeof existing_address !== "string" || (typeof existing_address === "string" && !existing_address.match(/^https?:\/\//))) { - existing_address = ""; - } - const initialState = await this.createInitialState(chat_thread, tabbed); let stringifiedInitialState = JSON.stringify(initialState); stringifiedInitialState = stringifiedInitialState.replace(/\<\/script>/gi, ""); diff --git a/src/statusBar.ts b/src/statusBar.ts index a7dfcfe4..2257aa37 100644 --- a/src/statusBar.ts +++ b/src/statusBar.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ import * as vscode from 'vscode'; -import * as userLogin from "./userLogin"; import * as fetchH2 from 'fetch-h2'; import { RagStatus } from './fetchAPI'; @@ -109,10 +108,6 @@ export class StatusBarMenu { msg += _website_message || _inference_message; } this.menu.tooltip = msg; - } else if (!userLogin.secret_api_key()) { - this.menu.text = `$(account) Refact.ai`; - this.menu.backgroundColor = new vscode.ThemeColor('statusBarItem.errorBackground'); - this.menu.tooltip = _website_message || `Click to login`; } else { this.menu.text = this.rag_status || `$(codify-logo) Refact.ai`; this.menu.backgroundColor = undefined; diff --git a/src/userLogin.ts b/src/userLogin.ts deleted file mode 100644 index f127f5c7..00000000 --- a/src/userLogin.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import * as vscode from 'vscode'; -import * as fetchH2 from 'fetch-h2'; -import * as usabilityHints from "./usabilityHints"; -import * as statusBar from "./statusBar"; - - -export function get_address(): string -{ - let addr1: string|undefined = vscode.workspace.getConfiguration().get("refactai.addressURL"); - let addr2: string|undefined = vscode.workspace.getConfiguration().get("refactai.infurl"); // old name - return addr1 || addr2 || ""; -} - - -export async function login_message() -{ - await vscode.commands.executeCommand('workbench.view.extension.refact-toolbox-pane'); -} - - -export async function welcome_message() -{ - await vscode.commands.executeCommand('workbench.view.extension.refact-toolbox-pane'); - await new Promise(resolve => setTimeout(resolve, 1000)); - await vscode.commands.executeCommand('workbench.view.extension.refact-toolbox-pane'); - let selection = await vscode.window.showInformationMessage("Welcome to Refact.ai!\nConnect to AI inference server in sidebar."); -} - - -export async function account_message(info: string, action: string, url: string) -{ - let selection = await vscode.window.showInformationMessage( - info, - action, - ); - if (selection === action) { - vscode.env.openExternal(vscode.Uri.parse(url)); - } -} - - -export function secret_api_key(): string -{ - let key = vscode.workspace.getConfiguration().get('refactai.apiKey'); - if (!key) { - // Backward compatibility: codify is the old name - key = vscode.workspace.getConfiguration().get('codify.apiKey'); - } - if (!key) { return ""; } - if (typeof key !== 'string') { return ""; } - return key; -} diff --git a/tsconfig.json b/tsconfig.json index 2a1b725e..e12feea8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "ES2021.WeakRef", "DOM", ], + "skipLibCheck": true, "sourceMap": true, "rootDir": "src", "strict": true,