diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89343869..cda37a24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -525,6 +525,18 @@ importers: specifier: ^6.1.13 version: 6.1.13 + provider/excel-vba: + dependencies: + '@openctx/provider': + specifier: workspace:* + version: link:../../lib/provider + jszip: + specifier: ^3.10.1 + version: 3.10.1 + xlsx: + specifier: ^0.18.5 + version: 0.18.5 + provider/github: dependencies: '@octokit/core': @@ -6959,6 +6971,11 @@ packages: engines: {node: '>= 10.0.0'} dev: true + /adler-32@1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + dev: false + /agent-base@5.1.1: resolution: {integrity: sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==} engines: {node: '>= 6.0.0'} @@ -7526,6 +7543,14 @@ packages: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} dev: false + /cfb@1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + dev: false + /chai@4.3.10: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} @@ -7750,6 +7775,11 @@ packages: - '@lezer/common' dev: false + /codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + dev: false + /collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} dev: false @@ -7925,7 +7955,6 @@ packages: /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true /cosmiconfig@8.2.0: resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==} @@ -7937,6 +7966,12 @@ packages: path-type: 4.0.0 dev: true + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: false + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -9217,6 +9252,11 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + /frac@1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + dev: false + /fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: false @@ -9924,7 +9964,6 @@ packages: /immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - dev: true /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -10368,7 +10407,6 @@ packages: /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -10677,7 +10715,6 @@ packages: pako: 1.0.11 readable-stream: 2.3.8 setimmediate: 1.0.5 - dev: true /jwa@1.4.1: resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} @@ -10845,7 +10882,6 @@ packages: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} dependencies: immediate: 3.0.6 - dev: true /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} @@ -12145,7 +12181,6 @@ packages: /pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - dev: true /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} @@ -12551,7 +12586,6 @@ packages: /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true /process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} @@ -12964,7 +12998,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} @@ -13452,7 +13485,6 @@ packages: /setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - dev: true /setprototypeof@1.1.0: resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} @@ -13618,6 +13650,13 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true + /ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + dependencies: + frac: 1.1.2 + dev: false + /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true @@ -13723,7 +13762,6 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: true /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -15018,6 +15056,16 @@ packages: stackback: 0.0.2 dev: true + /wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + dev: false + + /word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + dev: false + /wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: true @@ -15110,6 +15158,20 @@ packages: optional: true dev: true + /xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + dev: false + /xml2js@0.5.0: resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} engines: {node: '>=4.0.0'} diff --git a/provider/excel-vba/README.md b/provider/excel-vba/README.md new file mode 100644 index 00000000..b878311a --- /dev/null +++ b/provider/excel-vba/README.md @@ -0,0 +1,25 @@ +# Excel VBA context provider for OpenCtx + +This is a context provider for [OpenCtx](https://openctx.org) that lets you @-mention Excel files (`.xlsm`) and use their VBA scripts as context in code AI tools. + +**Status:** experimental + +## Usage + +Add the following to your settings in any OpenCtx client: + +```json +"openctx.providers": { + // ...other providers... + "https://openctx.org/npm/@openctx/provider-excel-vba": { + // TODO(sqs) + } +}, +``` + + +## Development + +- [Source code](https://sourcegraph.com/github.com/sourcegraph/openctx/-/tree/provider/excel-vba) +- [Docs](https://openctx.org/docs/providers/excel-vba) +- License: Apache 2.0 diff --git a/provider/excel-vba/extract_vba_code.py b/provider/excel-vba/extract_vba_code.py new file mode 100644 index 00000000..dc8c64f5 --- /dev/null +++ b/provider/excel-vba/extract_vba_code.py @@ -0,0 +1,18 @@ +import olefile +import sys + +def extract_vba_code(file_path): + vba_code = "" + try: + with olefile.OleFileIO(file_path) as ole: + for stream_name in ole.listdir(): + if stream_name[0] == 'VBA': # VBA code streams + vba_code += ole.openstream(stream_name).read().decode('utf-8', errors='ignore') + except Exception as e: + print(f"Error: {e}") + return vba_code + +# Example usage +file_path = sys.argv[1] +code = extract_vba_code(file_path) +print(code) \ No newline at end of file diff --git a/provider/excel-vba/index.ts b/provider/excel-vba/index.ts new file mode 100644 index 00000000..c790b453 --- /dev/null +++ b/provider/excel-vba/index.ts @@ -0,0 +1,83 @@ +import { execSync } from 'child_process' +import type { + ItemsParams, + ItemsResult, + MentionsParams, + MentionsResult, + MetaResult, + Provider, +} from '@openctx/provider' +import { readFile, readdir, writeFile } from 'fs/promises' +import JSZip from 'jszip' + +/** Settings for the Google Docs OpenCtx provider. */ +export type Settings = { + path: string +} + +/** + * An [OpenCtx](https://openctx.org) provider that brings Google Docs context to code AI and + * editors. + */ +const excelVBA: Provider = { + meta(): MetaResult { + return { name: 'Excel VBA', mentions: {} } + }, + + async mentions(params: MentionsParams, settings: Settings): Promise { + const files = (await readdir(settings.path, { recursive: true })).filter( + name => !name.startsWith('~$') && name.endsWith('.xlsm') + ) + return (files ?? []).map((file: any) => ({ + title: file, + uri: `file://${settings.path}/${file}`, + })) + }, + + async items(params: ItemsParams, settings: Settings): Promise { + if (!params.mention) { + return [] + } + + const modules = await extractVBAModules(params.mention.uri.replace(/^file:\/\//, '')) + + return [ + { + title: params.mention.title, + url: params.mention.uri, + ai: { + content: modules.join('\n\n'), + }, + }, + ] + }, +} + +async function extractVBAModules(filePath: string): Promise { + // Read the .xlsm file as a zip archive + const data = await readFile(filePath) + const zip = await JSZip.loadAsync(data) + + // Get the entries in the zip file + const vbaModules: string[] = [] + zip.forEach(async (relPath, zipEntry) => { + if (relPath.endsWith('/vbaProject.bin')) { + // Read the vbaProject.bin file + const vbaProjectBin = await zipEntry.async('nodebuffer') + + const tmpFile = '/tmp/TMP1.ole' + await writeFile(tmpFile, vbaProjectBin) + + // exec extract_vba_code.py + const stdout = execSync( + 'python /Users/sqs/src/github.com/sourcegraph/openctx/provider/excel-vba/extract_vba_code.py ' + + tmpFile + ) + vbaModules.push(stdout.toString()) + } + }) + + return vbaModules +} + +export default excelVBA diff --git a/provider/excel-vba/package.json b/provider/excel-vba/package.json new file mode 100644 index 00000000..17765354 --- /dev/null +++ b/provider/excel-vba/package.json @@ -0,0 +1,29 @@ +{ + "name": "@openctx/provider-excel-vba", + "version": "0.0.10", + "description": "Excel VBA context for code AI and editors (OpenCtx provider)", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/sourcegraph/openctx", + "directory": "provider/excel-vba" + }, + "type": "module", + "main": "dist/bundle.js", + "types": "dist/index.d.ts", + "files": [ + "dist/bundle.js", + "dist/index.d.ts" + ], + "sideEffects": false, + "scripts": { + "bundle": "tsc --build && esbuild --log-level=error --platform=node --bundle --format=esm --outfile=dist/bundle.js index.ts", + "prepublishOnly": "tsc --build --clean && npm run --silent bundle", + "test": "vitest" + }, + "dependencies": { + "@openctx/provider": "workspace:*", + "jszip": "^3.10.1", + "xlsx": "^0.18.5" + } +} diff --git a/provider/excel-vba/tsconfig.json b/provider/excel-vba/tsconfig.json new file mode 100644 index 00000000..4ff7ab81 --- /dev/null +++ b/provider/excel-vba/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../.config/tsconfig.base.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "dist", + "lib": ["ESNext"], + }, + "include": ["*.ts"], + "exclude": ["dist", "vitest.config.ts"], + "references": [{ "path": "../../lib/provider" }], +} diff --git a/provider/excel-vba/vitest.config.ts b/provider/excel-vba/vitest.config.ts new file mode 100644 index 00000000..abed6b21 --- /dev/null +++ b/provider/excel-vba/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({}) diff --git a/tsconfig.json b/tsconfig.json index 442280c3..2fabd774 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,6 +27,7 @@ { "path": "client/vscode/test/integration" }, { "path": "client/web-playground" }, { "path": "provider/devdocs" }, + { "path": "provider/excel-vba" }, { "path": "provider/google-docs" }, { "path": "provider/hello-world" }, { "path": "provider/links" }, diff --git a/web/content/docs/providers/excel-vba.mdx b/web/content/docs/providers/excel-vba.mdx new file mode 100644 index 00000000..ee501ea0 --- /dev/null +++ b/web/content/docs/providers/excel-vba.mdx @@ -0,0 +1,8 @@ +export const info = { + title: 'Excel VBA', + group: 'providers', +} + +import Readme from '../../../../provider/excel-vba/README.md' + +