From b9c12317df446310dd16a00f6749aa3bc8ea1253 Mon Sep 17 00:00:00 2001 From: Keegan Carruthers-Smith Date: Thu, 27 Jun 2024 15:15:28 +0200 Subject: [PATCH] vscode-lib: expose vscode API via global Currently vscode doesn't support dynamic imports, so it isn't possible for a javascript provider to opt-in to more advanced user experience via the vscode APIs. Until we can more easily opt-in to vscode support, we expose the vscode API via node's global object. We want to see what sort of APIs get used from vscode in providers, and will then likely evolve a first class editor abstraction. --- client/vscode-lib/package.json | 2 +- client/vscode-lib/src/controller.ts | 3 ++ client/vscode-lib/src/global.ts | 23 +++++++++++++ pnpm-lock.yaml | 3 ++ provider/linear-issues/index.ts | 52 +++++++++++++++++++++++++++++ provider/linear-issues/package.json | 7 ++-- 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 client/vscode-lib/src/global.ts diff --git a/client/vscode-lib/package.json b/client/vscode-lib/package.json index 419f0ec5..0ebf41c2 100644 --- a/client/vscode-lib/package.json +++ b/client/vscode-lib/package.json @@ -1,6 +1,6 @@ { "name": "@openctx/vscode-lib", - "version": "0.0.11", + "version": "0.0.12", "description": "OpenCtx library for VS Code extensions", "license": "Apache-2.0", "repository": { diff --git a/client/vscode-lib/src/controller.ts b/client/vscode-lib/src/controller.ts index 27be9867..1400a3bf 100644 --- a/client/vscode-lib/src/controller.ts +++ b/client/vscode-lib/src/controller.ts @@ -9,6 +9,7 @@ import type { ImportedProviderConfiguration } from '@openctx/client/src/configur import { type Observable, combineLatest, from, isObservable, map, mergeMap, of } from 'rxjs' import * as vscode from 'vscode' import { getClientConfiguration } from './configuration.js' +import { initializeOpenCtxGlobal } from './global.js' import { type ExtensionApiForTesting, createApiForTesting } from './testing.js' import { createCodeLensProvider } from './ui/editor/codeLens.js' import { createHoverProvider } from './ui/editor/hover.js' @@ -61,6 +62,8 @@ export function createController({ apiForTesting: ExtensionApiForTesting disposable: vscode.Disposable } { + initializeOpenCtxGlobal() + const disposables: vscode.Disposable[] = [] const globalConfigurationChanges = observeWorkspaceConfigurationChanges('openctx') diff --git a/client/vscode-lib/src/global.ts b/client/vscode-lib/src/global.ts new file mode 100644 index 00000000..498ad94e --- /dev/null +++ b/client/vscode-lib/src/global.ts @@ -0,0 +1,23 @@ +import * as vscode from 'vscode' + +// dynamic imports don't work in node + vscode due to +// https://github.com/microsoft/vscode-loader/issues/36 +// +// So vscode-lib sets a global so providers can optionally access vscode APIs. + +interface Global { + openctx?: { + vscode?: typeof vscode + } +} + +export function initializeOpenCtxGlobal() { + initializeGlobal(global as Global) +} + +function initializeGlobal(global: Global) { + if (!global.openctx) { + global.openctx = { vscode } + } + global.openctx.vscode = vscode +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89343869..b732e8ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -627,6 +627,9 @@ importers: '@types/server-destroy': specifier: ^1.0.3 version: 1.0.3 + '@types/vscode': + specifier: ^1.85.0 + version: 1.85.0 provider/links: dependencies: diff --git a/provider/linear-issues/index.ts b/provider/linear-issues/index.ts index 12cad314..31f686ee 100644 --- a/provider/linear-issues/index.ts +++ b/provider/linear-issues/index.ts @@ -124,6 +124,11 @@ async function getAccessToken(settings: Settings): Promise { return userCredentials.access_token } + const vscodeAccessToken = await getAccessTokenLinearConnect() + if (vscodeAccessToken) { + return vscodeAccessToken + } + throw new Error( 'must provide a Linear user credentials path in the `userCredentialsPath` settings field or an accessToken in the linearClientOptions' ) @@ -157,6 +162,53 @@ async function linearApiRequest( return json } +const LINEAR_AUTHENTICATION_EXTENSION_ID = 'linear.linear-connect' +const LINEAR_AUTHENTICATION_PROVIDER_ID = 'linear' +const LINEAR_AUTHENTICATION_SCOPES = ['read'] + +function vscodeAPI() { + // dynamic imports don't work in node + vscode due + // https://github.com/microsoft/vscode-loader/issues/36 vscode-lib however + // will sneakily set the vscode import to a global for us. + if (!('openctx' in global)) { + return undefined + } + const openctx = (global as any).openctx + if (!openctx.vscode) { + return undefined + } + return openctx.vscode as typeof import('vscode') +} + +async function getAccessTokenLinearConnect(): Promise { + const vscode = vscodeAPI() + if (!vscode) { + return undefined + } + + const ext = vscode.extensions.getExtension(LINEAR_AUTHENTICATION_EXTENSION_ID) + if (!ext) { + vscode.window.showWarningMessage( + 'Cody requires the Linear Connect extension to be installed and activated.' + ) + await vscode.commands.executeCommand('workbench.extensions.action.showExtensionsWithIds', [ + [LINEAR_AUTHENTICATION_EXTENSION_ID], + ]) + } + + const session = await vscode.authentication.getSession( + LINEAR_AUTHENTICATION_PROVIDER_ID, + LINEAR_AUTHENTICATION_SCOPES, + { createIfNone: true } + ) + + if (!session) { + throw new Error(`We weren't able to log you into Linear when trying to open the issue.`) + } + + return session.accessToken +} + function parseIssueIDFromURL(urlStr: string): string | undefined { const url = new URL(urlStr) if (!url.hostname.endsWith('linear.app')) { diff --git a/provider/linear-issues/package.json b/provider/linear-issues/package.json index be284056..36d39c0e 100644 --- a/provider/linear-issues/package.json +++ b/provider/linear-issues/package.json @@ -1,6 +1,6 @@ { "name": "@openctx/provider-linear-issues", - "version": "0.0.3", + "version": "0.0.4", "description": "Linear Issues context for code AI and editors (OpenCtx provider)", "license": "Apache-2.0", "repository": { @@ -19,7 +19,7 @@ "scripts": { "build": "tsc --build", "bundle:watch": "pnpm run bundle --watch", - "bundle": "esbuild --main-fields=module,main --log-level=error --platform=node --bundle --format=esm --outfile=dist/bundle.js index.ts", + "bundle": "esbuild --main-fields=module,main --external:vscode --log-level=error --platform=node --bundle --format=esm --outfile=dist/bundle.js index.ts", "prepublishOnly": "tsc --build --clean && pnpm run --silent bundle", "test": "vitest", "auth": "node --no-warnings=ExperimentalWarning --es-module-specifier-resolution=node --loader ts-node/esm/transpile-only auth.ts" @@ -32,6 +32,7 @@ "server-destroy": "^1.0.1" }, "devDependencies": { - "@types/server-destroy": "^1.0.3" + "@types/server-destroy": "^1.0.3", + "@types/vscode": "^1.85.0" } }