├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc.yaml
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── README.md
├── electron-builder.yml
├── litellm_uuid.txt
├── package-lock.json
├── package.json
├── resources
├── icon.png
└── old_icon.png
├── screenshots
├── Screenshot_1.png
├── Screenshot_2.png
└── cover_art.png
└── src
├── index.html
├── main.js
├── preload.js
├── renderer.js
└── style.css
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | out
4 | .gitignore
5 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['eslint:recommended', '@electron-toolkit', '@electron-toolkit/eslint-config-prettier']
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | out
4 | *.log*
5 | litellm_uuid.txt
6 |
7 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | out
2 | dist
3 | pnpm-lock.yaml
4 | LICENSE.md
5 | tsconfig.json
6 | tsconfig.*.json
7 |
--------------------------------------------------------------------------------
/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | singleQuote: true
2 | semi: false
3 | printWidth: 100
4 | trailingComma: none
5 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["dbaeumer.vscode-eslint"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug Main Process",
6 | "type": "node",
7 | "request": "launch",
8 | "cwd": "${workspaceRoot}",
9 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
10 | "windows": {
11 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
12 | },
13 | "runtimeArgs": ["--sourcemap"],
14 | "env": {
15 | "REMOTE_DEBUGGING_PORT": "9222"
16 | }
17 | },
18 | {
19 | "name": "Debug Renderer Process",
20 | "port": 9222,
21 | "request": "attach",
22 | "type": "chrome",
23 | "webRoot": "${workspaceFolder}/src/renderer",
24 | "timeout": 60000,
25 | "presentation": {
26 | "hidden": true
27 | }
28 | }
29 | ],
30 | "compounds": [
31 | {
32 | "name": "Debug All",
33 | "configurations": ["Debug Main Process", "Debug Renderer Process"],
34 | "presentation": {
35 | "order": 1
36 | }
37 | }
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[typescript]": {
3 | "editor.defaultFormatter": "esbenp.prettier-vscode"
4 | },
5 | "[javascript]": {
6 | "editor.defaultFormatter": "esbenp.prettier-vscode"
7 | },
8 | "[json]": {
9 | "editor.defaultFormatter": "esbenp.prettier-vscode"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Electerpreter
2 |
3 | A minimal Electron application to run and display the output of open-interpreter
4 | this is a work in progress and was intended to be my entry for the lablab.ai open-interpreter 24hour hackathon
5 |
6 | link to entry here: https://lablab.ai/event/open-interpreter-hackathon/big-full-stack-gamers/electerpreter-open-interpreter-electron-app
7 |
8 | 
9 |
10 | ## Features
11 |
12 | - Runs open-interpreter within an electron app
13 | - Displays the output of open-interpreter
14 | - Allows for the user to send an input to open-interpreter
15 | - Uses Open-Interpreter to automatically execute code that the llm comes up with
16 |
17 | ## TODO
18 |
19 | - [ ] have a spefic argument to send to open-interpreter so that it knows that we are using the electron app and not the cli may make it easier to use
20 | - [ ] a way to change the arguments in the spawned process of open-interpreter
21 |
22 | ## Screenshots
23 |
24 | 
25 | 
26 |
27 | ## Recommended IDE Setup
28 |
29 | - [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
30 |
31 | ## Project Setup
32 |
33 | ### Install
34 |
35 | ```bash
36 | $ npm install
37 | ```
38 |
39 | ### Development
40 |
41 | ```bash
42 | $ npm run dev
43 | ```
44 |
45 | ### Build
46 |
47 | ```bash
48 | # For windows
49 | $ npm run build:win
50 |
51 | # For macOS
52 | $ npm run build:mac
53 |
54 | # For Linux
55 | $ npm run build:linux
56 | ```
57 |
58 | ## Note
59 |
60 | if you want to use this application it is currently setup to run the open-interpreter cli with the specific aruements i use for testing which are
61 |
62 | ```bash
63 | $ open-interpreter --context_window 2042 -y
64 | ```
65 |
66 | this can be changed on line 13 of the preload.js file, you will need to set your environment variable OPENAI_API_KEY
--------------------------------------------------------------------------------
/electron-builder.yml:
--------------------------------------------------------------------------------
1 | appId: com.electron.app
2 | productName: open-execute
3 | directories:
4 | buildResources: build
5 | files:
6 | - '!**/.vscode/*'
7 | - '!{.eslintignore,.eslintrc.js,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
8 | asarUnpack:
9 | - resources/**
10 | win:
11 | executableName: open-execute
12 | nsis:
13 | artifactName: ${name}-${version}-setup.${ext}
14 | shortcutName: ${productName}
15 | uninstallDisplayName: ${productName}
16 | createDesktopShortcut: always
17 | mac:
18 | entitlementsInherit: build/entitlements.mac.plist
19 | extendInfo:
20 | - NSCameraUsageDescription: Application requests access to the device's camera.
21 | - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
22 | - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
23 | - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
24 | notarize: false
25 | dmg:
26 | artifactName: ${name}-${version}.${ext}
27 | linux:
28 | target:
29 | - AppImage
30 | - snap
31 | - deb
32 | maintainer: electronjs.org
33 | category: Utility
34 | appImage:
35 | artifactName: ${name}-${version}.${ext}
36 | npmRebuild: false
37 | publish:
38 | provider: generic
39 | url: https://example.com/auto-updates
40 |
--------------------------------------------------------------------------------
/litellm_uuid.txt:
--------------------------------------------------------------------------------
1 | 7e0fcc89-793a-442b-b05b-253baab4150f
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "open-execute",
3 | "version": "1.0.0",
4 | "description": "A electron app to open and execute open-interpreter queries",
5 | "main": "./src/main.js",
6 | "author": "Rowan wood",
7 | "homepage": "https://www.electronjs.org",
8 | "scripts": {
9 | "format": "prettier --write .",
10 | "lint": "eslint --ext .js .",
11 | "dev": "electron .",
12 | "postinstall": "electron-builder install-app-deps",
13 | "build:win": "electron-builder --win --config",
14 | "build:mac": "electron-builder --mac --config",
15 | "build:linux": "electron-builder --linux --config"
16 | },
17 | "dependencies": {
18 | "@electron-toolkit/preload": "^2.0.0",
19 | "@electron-toolkit/utils": "^2.0.0"
20 | },
21 | "devDependencies": {
22 | "@electron-toolkit/eslint-config": "^1.0.1",
23 | "@electron-toolkit/eslint-config-prettier": "^1.0.1",
24 | "electron": "^25.6.0",
25 | "electron-builder": "^24.6.3",
26 | "electron-vite": "^1.0.28",
27 | "eslint": "^8.47.0",
28 | "prettier": "^3.0.2"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/resources/icon.png
--------------------------------------------------------------------------------
/resources/old_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/resources/old_icon.png
--------------------------------------------------------------------------------
/screenshots/Screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/screenshots/Screenshot_1.png
--------------------------------------------------------------------------------
/screenshots/Screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/screenshots/Screenshot_2.png
--------------------------------------------------------------------------------
/screenshots/cover_art.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/screenshots/cover_art.png
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electerpreter
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | // Modules to control application life and create native browser window
2 | const { app, shell, BrowserWindow } = require('electron')
3 | const path = require('path')
4 | const { electronApp, optimizer } = require('@electron-toolkit/utils')
5 |
6 | function createWindow() {
7 | // Create the browser window.
8 | const mainWindow = new BrowserWindow({
9 | width: 900,
10 | height: 670,
11 | show: false,
12 | autoHideMenuBar: true,
13 | ...(process.platform === 'linux'
14 | ? {
15 | icon: path.join(__dirname, '../resources/icon.png')
16 | }
17 | : {}),
18 | webPreferences: {
19 | preload: path.join(__dirname, 'preload.js'),
20 | // sandbox: true,
21 | nodeIntegration: true,
22 | contentSecurityPolicy: `
23 | default-src 'none' 'self' 'unsafe-inline';
24 | `,
25 | }
26 | })
27 |
28 | mainWindow.webContents.openDevTools()
29 |
30 | mainWindow.on('ready-to-show', () => {
31 | mainWindow.show()
32 | })
33 |
34 | mainWindow.webContents.setWindowOpenHandler((details) => {
35 | shell.openExternal(details.url)
36 | return { action: 'deny' }
37 | })
38 |
39 | // and load the index.html of the app.
40 | mainWindow.loadFile(path.join(__dirname, 'index.html'))
41 | }
42 |
43 | // This method will be called when Electron has finished
44 | // initialization and is ready to create browser windows.
45 | // Some APIs can only be used after this event occurs.
46 | app.whenReady().then(() => {
47 | // Set app user model id for windows
48 | electronApp.setAppUserModelId('com.electron')
49 |
50 | // Default open or close DevTools by F12 in development
51 | // and ignore CommandOrControl + R in production.
52 | // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
53 | app.on('browser-window-created', (_, window) => {
54 | optimizer.watchWindowShortcuts(window)
55 | })
56 |
57 | createWindow()
58 |
59 | app.on('activate', function () {
60 | // On macOS it's common to re-create a window in the app when the
61 | // dock icon is clicked and there are no other windows open.
62 | if (BrowserWindow.getAllWindows().length === 0) createWindow()
63 | })
64 | })
65 |
66 | // Quit when all windows are closed, except on macOS. There, it's common
67 | // for applications and their menu bar to stay active until the user quits
68 | // explicitly with Cmd + Q.
69 | app.on('window-all-closed', function () {
70 | if (process.platform !== 'darwin') {
71 | app.quit()
72 | }
73 | })
74 |
75 | // In this file you can include the rest of your app's specific main process
76 | // code. You can also put them in separate files and require them here.
77 |
78 | // mainWindow.addEventListner('message', (event) => {
79 | // if (event.source === window){
80 | // console.log("from preload", event.data)
81 |
82 | // if (event.data === 'ready') {
83 | // console.log('interpreter is ready')
84 | // }
85 | // }
86 | // })
--------------------------------------------------------------------------------
/src/preload.js:
--------------------------------------------------------------------------------
1 | // All of the Node.js APIs are available in the preload process.
2 | // It has the same sandbox as a Chrome extension.
3 | const { contextBridge } = require('electron')
4 | const { electronAPI } = require('@electron-toolkit/preload')
5 | const {spawn} = require('node:child_process')
6 | const { create } = require('domain');
7 | const { decode } = require('node:punycode');
8 |
9 | // Set the PYTHONIOENCODING environment variable to UTF-8
10 | process.env.PYTHONIOENCODING = 'UTF-8';
11 |
12 | function createOpenInterpreterProcess() {
13 | const createProcess = spawn('interpreter', ['--context_window', '2042', '-y'],{
14 | shell: true,
15 | stdio: ['pipe', 'pipe', 'pipe'],
16 | env: process.env,
17 | // cwd: process.cwd(),
18 | // windowsHide: true,
19 | detached: false,
20 |
21 | })
22 |
23 | createProcess.stdout.on('data', (data) => {
24 | // log the data
25 | console.log(`stdout: ${data}`)
26 | // send message to renderer
27 | // add the word 'bot-stdount' to the beginning of the message so we can tell the difference between bot output and other output
28 | window.postMessage('bot-stdout' + data, '*')
29 | })
30 | createProcess.stderr.on('data', (data) => {
31 | data = data.toString()
32 | console.error(`stderr: ${data}`)
33 | if (data.includes('Downloading')) {
34 | // get numbers before %
35 | const percent = data.match(/\d+(?=%)/)[0]
36 | console.log(percent)
37 | window.postMessage('Downloading', '*')
38 | }
39 | })
40 |
41 | // get anything being piped out of the process
42 | createProcess.stdio[1].on('data', (data) => {
43 | console.log(`stdio[1]: ${data}`)
44 | })
45 |
46 | // get anything being piped into the process
47 | createProcess.stdio[0].on('data', (data) => {
48 | console.log(`stdio[0]: ${data}`)
49 | })
50 |
51 | createProcess.on('close', (code) => {
52 | console.log(`child process exited with code ${code}`)
53 | })
54 | createProcess.on('error', (err) => {
55 | console.error(err)
56 | })
57 |
58 | // send messages to the process
59 | window.addEventListener('message', (event) => {
60 | if (event.source === window) {
61 | if (event.data !== 'ready' && event.data !== 'command' && event.data !== 'Downloading') {
62 | // and event.data doesnt degin with 'bot-stdout'
63 | if (event.data.startsWith('bot-stdout')) {
64 | if (event.data.includes('Would you like to run this code? (y/n)')) {
65 | console.log('we have some code to run')
66 | // just run it *test*
67 | // createProcess.stdio[1].write('y')
68 | }
69 | // send message to renderer
70 | // window.postMessage('bot-stdout' + event.data, '*')
71 | }
72 | if (event.data.startsWith('user-input')) {
73 | // remove 'user-input' from the beginning of the message
74 | const message = event.data.replace('user-input', '')
75 | createProcess.stdio[0].write(event.data + '\n')
76 | // add output
77 | // addOutput(message)
78 | }
79 | // decodedData = new TextDecoder('utf-8').decode(event.data)
80 | // console.log("sent decoded data: ", decodedData)
81 | }
82 | }
83 | })
84 | }
85 |
86 | createOpenInterpreterProcess()
87 |
88 |
89 | // Custom APIs for renderer
90 | const api = {}
91 |
92 | // Use `contextBridge` APIs to expose Electron APIs to
93 | // renderer only if context isolation is enabled, otherwise
94 | // just add to the DOM global.
95 | if (process.contextIsolated) {
96 | try {
97 | contextBridge.exposeInMainWorld('electron', electronAPI)
98 | contextBridge.exposeInMainWorld('api', api)
99 | } catch (error) {
100 | console.error(error)
101 | }
102 | } else {
103 | // window.electron = electronAPI
104 | window.api = api
105 | }
106 |
--------------------------------------------------------------------------------
/src/renderer.js:
--------------------------------------------------------------------------------
1 | // This file is required by the index.html file and will
2 | // be executed in the renderer process for that window.
3 | // No Node.js APIs are available in this process because
4 | // `nodeIntegration` is turned off. Use `preload.js` to
5 | // selectively enable features needed in the rendering
6 | ;(async () => {
7 | var isReady = false
8 |
9 | window.addEventListener('DOMContentLoaded', () => {
10 | outputcontent = document.getElementById('interpreter-output')
11 | submitbutton = document.getElementById('prompt-submit')
12 | input = document.getElementById('prompt-input')
13 |
14 | // event listener for enter key when input is focused and submit button is not disabled
15 | input.addEventListener('keydown', (event) => {
16 | if (event.key === 'Enter' && !submitbutton.disabled) {
17 | submitbutton.click()
18 | }
19 | })
20 |
21 | submitbutton.addEventListener('click', () => {
22 | // send input to python
23 | // at the begining of the message add 'user-input' so we can tell the difference between user input and other output
24 | window.postMessage('user-input ' + input.value, '*')
25 | // window.postMessage('command', '*')
26 | // clear input
27 | addInput(input.value)
28 | input.value = ''
29 | })
30 | })
31 |
32 |
33 |
34 | var output
35 |
36 | function addOutput(message) {
37 | if (message.trim() !== '') { // Check if the message is not empty or only whitespace
38 | outputcontent.innerHTML += `Open-Interpreter: ${message}
`;
39 | // scroll to the bottom of the container
40 | const lastElement = outputcontent.lastElementChild;
41 | if (lastElement) {
42 | lastElement.scrollIntoView({ behavior: 'smooth', block: 'end' });
43 | }
44 | }
45 | }
46 |
47 | function addInput(message) {
48 | if (message.trim() !== '') { // Check if the message is not empty or only whitespace
49 | outputcontent.innerHTML += `Your Prompt: ${message}
`;
50 | // scroll to the bottom of the container
51 | const lastElement = outputcontent.lastElementChild;
52 | if (lastElement) {
53 | lastElement.scrollIntoView({ behavior: 'smooth', block: 'end' });
54 | }
55 | }
56 | }
57 |
58 | window.addEventListener('message', (event) => {
59 | if (event.data.startsWith('bot-stdout')) {
60 | // remove 'bot-stdout' from the beginning of the message
61 | const message = event.data.replace('bot-stdout', '')
62 | // add output
63 | console.log("message", message)
64 | addOutput(message)
65 | }
66 | if (event.data !== 'ready') {
67 | // check type of event.data
68 | if (typeof event.data === 'string') {
69 | console.log('string')
70 | console.log("sent event data: ",event.data)
71 | } else if (typeof event.data === 'object') {
72 | console.log('object')
73 | const textDecoder = new TextDecoder('utf-8');
74 | const decodedData = textDecoder.decode(event.data);
75 | output = decodedData
76 | console.log("sent event data: ",decodedData)
77 | } else {
78 | console.log('other')
79 | console.log("other event data: ",event.data)
80 | }
81 | }
82 | })
83 | })()
84 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | /* Add styles here to customize the appearance of your app */
2 | body {
3 | display: flex;
4 | flex-direction: column;
5 | font-family:
6 | Roboto,
7 | -apple-system,
8 | BlinkMacSystemFont,
9 | 'Helvetica Neue',
10 | 'Segoe UI',
11 | 'Oxygen',
12 | 'Ubuntu',
13 | 'Cantarell',
14 | 'Open Sans',
15 | sans-serif;
16 | color: #86a5b1;
17 | background-color: #2f3241;
18 | overflow: hidden;
19 | }
20 |
21 | /* less shit scroll bars */
22 |
23 | ::-webkit-scrollbar {
24 | width: 5px;
25 | height: 5px;
26 | }
27 |
28 | /* Track */
29 | ::-webkit-scrollbar-track {
30 | margin-top: 20px;
31 | margin-bottom: 50px;
32 | box-shadow: inset 0 0 5px grey;
33 | border-radius: 10px;
34 | }
35 |
36 | /* Handle */
37 | ::-webkit-scrollbar-thumb {
38 | background: rgb(4, 249, 4);
39 | border-radius: 10px;
40 | }
41 |
42 | #output-container {
43 | display: flex;
44 | flex-direction: column;
45 | background-color: rgb(79, 79, 80);
46 | border: 1px solid black;
47 | width: 100%;
48 | height: calc(100vh - 80px);
49 | border-radius: 5px;
50 | overflow-y: scroll;
51 | overflow-x: hidden;
52 | }
53 |
54 |
55 | .bot {
56 | background-color: black;
57 | color: white;
58 | border: 1px solid white;
59 | border-radius: 5px;
60 | margin: 10px;
61 | padding: 5px;
62 | }
63 |
64 | .user {
65 | background-color: #000f58;
66 | color: white;
67 | border: 1px solid #86a5b1;
68 | border-radius: 5px;
69 | margin: 10px;
70 | padding: 5px;
71 | }
72 |
73 | #container {
74 | position: relative;
75 | }
76 |
77 | #prompt-container {
78 | display: flex;
79 | position: absolute;
80 | bottom: 20px;
81 | flex-direction: row;
82 | justify-content: center;
83 | align-items: center;
84 | width: 100vw;
85 | }
86 |
87 | #prompt-input {
88 | /* modern style input box */
89 | height: 30px;
90 | width: 80vw;
91 | }
92 |
93 | #prompt-submit {
94 | width: 50px;
95 | height: 30px;
96 | border-radius: 2px;
97 | background-color: green;
98 | color: black;
99 | }
100 |
101 | * {
102 | padding: 0;
103 | margin: 0;
104 | }
105 |
106 | ul {
107 | list-style: none;
108 | }
109 |
110 | code {
111 | font-weight: 600;
112 | padding: 3px 5px;
113 | border-radius: 2px;
114 | background-color: #26282e;
115 | font-family:
116 | ui-monospace,
117 | SFMono-Regular,
118 | SF Mono,
119 | Menlo,
120 | Consolas,
121 | Liberation Mono,
122 | monospace;
123 | font-size: 85%;
124 | }
125 |
126 | a {
127 | color: #9feaf9;
128 | font-weight: 600;
129 | cursor: pointer;
130 | text-decoration: none;
131 | outline: none;
132 | }
133 |
134 | a:hover {
135 | border-bottom: 1px solid;
136 | }
137 |
138 | .container {
139 | flex: 1;
140 | display: flex;
141 | flex-direction: column;
142 | max-width: 900px;
143 | margin: 0 auto;
144 | padding: 15px 30px 0 30px;
145 | box-sizing: border-box;
146 | }
147 |
148 |
149 | @media (min-width: 840px) {
150 | .container {
151 | width: 100%;
152 | }
153 | }
--------------------------------------------------------------------------------