local-operator-ui 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Damian Tran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,306 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="./resources/local-operator-icon-2-dark-clear.png">
3
+ <source media="(prefers-color-scheme: light)" srcset="./resources/local-operator-icon-2-light-clear.png">
4
+ <img alt="Shows a black Local Operator Logo in light color mode and a white one in dark color mode."
5
+ src="./resources/local-operator-icon-2-light-clear.png">
6
+ </picture>
7
+
8
+ <h1 align="center">Local Operator: AI Agent Assistants On Your Device</h1>
9
+ <div align="center">
10
+ <h2>🤖 Your Personal Assistant that Gets Things Done with Python</h2>
11
+ <p><i>Real-time code execution on your device through natural conversation</i></p>
12
+ </div>
13
+
14
+ **<span style="color: #38C96A">Local Operator</span>** empowers you to run Python code safely on your own machine through an intuitive chat interface. The AI agent:
15
+
16
+ 🎯 **Plans & Executes** - Breaks down complex goals into manageable steps and executes them with precision.
17
+
18
+ 🔒 **Prioritizes Security** - Built-in safety checks by independent AI review and user confirmations keep your system protected
19
+
20
+ 🌐 **Flexible Deployment** - Run completely locally with Ollama models or leverage cloud providers like OpenAI
21
+
22
+ 🔧 **Problem Solving** - Intelligently handles errors and roadblocks by adapting approaches and finding alternative solutions
23
+
24
+ This project is proudly open source under the MIT license. We believe AI tools should be accessible to everyone, given their transformative impact on productivity. Your contributions and feedback help make this vision a reality!
25
+
26
+ > "Democratizing AI-powered productivity, one conversation at a time."
27
+
28
+ <div align="center">
29
+ <a href="https://github.com/damianvtran/local-operator">Agent Backend</a> •
30
+ <a href="https://local-operator.com">Learn More</a> •
31
+ <a href="https://github.com/damianvtran/local-operator/tree/main/examples/notebooks">Examples</a>
32
+ </div>
33
+
34
+ ## 💡 Overview
35
+
36
+ The Local Operator UI is a user interface for managing and interacting with the Local Operator agent environment. It is built using Electron, React, and TypeScript, leveraging modern web technologies for a rich and responsive user experience.
37
+
38
+ 👉 For the agent environment CLI and Server backend, see the [Local Operator GitHub repository](https://github.com/damianvtran/local-operator).
39
+
40
+ ## 🚀 Getting Started
41
+
42
+ ### Prerequisites
43
+
44
+ Before you begin, ensure you have the following installed:
45
+
46
+ - **Node.js**: Version specified in `.nvmrc`. It's recommended to use [nvm](https://github.com/nvm-sh/nvm) for managing Node.js versions.
47
+ - **yarn**: Installable via `npm install -g yarn`.
48
+ - **Local Operator Backend**: The UI connects to the Local Operator backend API. See the [Local Operator GitHub repository](https://github.com/damianvtran/local-operator) for installation instructions.
49
+
50
+ ### Installation
51
+
52
+ **Clone the repository:**
53
+
54
+ ```bash
55
+ git clone https://github.com/local-operator/local-operator-ui.git
56
+ cd local-operator-ui
57
+ ```
58
+
59
+ **Install dependencies:**
60
+
61
+ ```bash
62
+ yarn install
63
+ ```
64
+
65
+ **Configure environment variables:**
66
+
67
+ Copy the `.env.template` file to `.env` and update the values as needed:
68
+
69
+ ```bash
70
+ cp .env.template .env
71
+ ```
72
+
73
+ The main environment variable is:
74
+
75
+ - `VITE_LOCAL_OPERATOR_API_URL`: URL of the Local Operator backend API (default: `http://localhost:1111`)
76
+
77
+ ### Development
78
+
79
+ For local development, follow these steps:
80
+
81
+ **Start the development server:**
82
+
83
+ ```bash
84
+ yarn dev
85
+ ```
86
+
87
+ This command will start the Electron application in development mode, with hot reloading enabled for both the main process and the renderer process.
88
+
89
+ ### Production
90
+
91
+ To build and run the application in production mode:
92
+
93
+ **Build and start the application:**
94
+
95
+ ```bash
96
+ yarn start
97
+ ```
98
+
99
+ This command will run the pre-build script and then start the Electron application in production mode, loading the compiled assets.
100
+
101
+ ## 🏗️ Project Structure
102
+
103
+ The project follows a modular architecture with clear separation of concerns:
104
+
105
+ ```
106
+ local-operator-ui/
107
+ ├── resources/ # Application resources (icons, images)
108
+ ├── src/ # Source code
109
+ │ ├── main/ # Electron main process
110
+ │ │ └── index.ts # Main process entry point
111
+ │ ├── preload/ # Electron preload scripts
112
+ │ │ ├── index.d.ts # Type definitions
113
+ │ │ └── index.ts # Preload script
114
+ │ └── renderer/ # Renderer process (React application)
115
+ │ ├── index.html # HTML template
116
+ │ └── src/ # React application source
117
+ │ ├── api/ # API clients and utilities
118
+ │ │ ├── query-client.ts # React Query client setup
119
+ │ │ └── local-operator/ # Local Operator API client
120
+ │ │ ├── agents-api.ts # Agents API endpoints
121
+ │ │ ├── chat-api.ts # Chat API endpoints
122
+ │ │ ├── config-api.ts # Config API endpoints
123
+ │ │ ├── credentials-api.ts # Credentials API endpoints
124
+ │ │ ├── health-api.ts # Health API endpoints
125
+ │ │ ├── jobs-api.ts # Jobs API endpoints
126
+ │ │ ├── types.ts # API type definitions
127
+ │ │ └── index.ts # API client exports
128
+ │ ├── assets/ # Static assets
129
+ │ ├── components/ # React components
130
+ │ │ ├── agents/ # Agent management components
131
+ │ │ ├── chat/ # Chat interface components
132
+ │ │ ├── common/ # Shared/common components
133
+ │ │ ├── navigation/ # Navigation components
134
+ │ │ └── settings/ # Settings components
135
+ │ ├── hooks/ # Custom React hooks
136
+ │ ├── store/ # State management (Zustand)
137
+ │ ├── app.tsx # Main application component
138
+ │ ├── config.ts # Application configuration
139
+ │ ├── main.tsx # Application entry point
140
+ │ ├── theme.ts # MUI theme configuration
141
+ │ └── vite-env.d.ts # Vite environment type definitions
142
+ ├── .env.template # Environment variables template
143
+ ├── biome.json # Biome configuration
144
+ ├── electron.vite.config.js # Electron Vite configuration
145
+ ├── eslint.config.js # ESLint configuration
146
+ ├── package.json # Project dependencies and scripts
147
+ ├── tsconfig.json # TypeScript configuration
148
+ └── README.md # Project documentation
149
+ ```
150
+
151
+ ### Key Directories
152
+
153
+ - **src/main**: Contains the Electron main process code
154
+ - **src/preload**: Contains the Electron preload scripts
155
+ - **src/renderer**: Contains the React application code
156
+ - **api**: API clients for communicating with the Local Operator backend
157
+ - **components**: React components organized by feature
158
+ - **hooks**: Custom React hooks for data fetching and state management
159
+ - **store**: Global state management using Zustand
160
+
161
+ ## 🛠️ Technology Stack
162
+
163
+ The Local Operator UI is built with the following technologies:
164
+
165
+ ### Core Technologies
166
+
167
+ - **Electron**: Cross-platform desktop application framework
168
+ - **React**: UI library for building component-based interfaces
169
+ - **TypeScript**: Typed superset of JavaScript for improved developer experience
170
+ - **Vite**: Modern frontend build tool
171
+
172
+ ### UI Framework and Styling
173
+
174
+ - **Material UI (MUI)**: React component library implementing Google's Material Design
175
+ - **Styled Components**: CSS-in-JS library for component styling
176
+ - **Emotion**: CSS-in-JS library used by MUI
177
+ - **FontAwesome**: Icon library
178
+
179
+ ### State Management and Data Fetching
180
+
181
+ - **Zustand**: Lightweight state management library
182
+ - **React Query**: Data fetching and caching library
183
+ - **Zod**: TypeScript-first schema validation
184
+
185
+ ### Development Tools
186
+
187
+ - **Biome**: Fast linter and formatter for JavaScript and TypeScript
188
+ - **ESLint**: JavaScript and TypeScript linter
189
+ - **TypeScript**: Static type checking
190
+
191
+ ## ✨ Features
192
+
193
+ The Local Operator UI provides a comprehensive interface for interacting with AI agents:
194
+
195
+ ### Chat Interface
196
+
197
+ - Real-time chat with AI agents
198
+ - Markdown rendering for code blocks and formatted text
199
+ - Syntax highlighting for code snippets
200
+ - Message history and conversation management
201
+
202
+ ### Agent Management
203
+
204
+ - Create, update, and delete AI agents
205
+ - Configure agent settings:
206
+ - General settings (name, description, model)
207
+ - Chat settings (temperature, top_p, etc.)
208
+ - Security settings (security prompt, execution permissions)
209
+
210
+ ### Settings
211
+
212
+ - System prompt configuration
213
+ - API credentials management
214
+ - Application configuration
215
+
216
+ ### API Integration
217
+
218
+ - Seamless integration with the Local Operator backend API
219
+ - Real-time status updates for long-running operations
220
+ - Error handling and retry mechanisms
221
+
222
+ ## 🧪 Testing
223
+
224
+ The project uses Jest for testing. To run tests:
225
+
226
+ ```bash
227
+ yarn test
228
+ ```
229
+
230
+ ## 🔧 Linting and Formatting
231
+
232
+ The project uses Biome for linting and formatting. To lint and format the code:
233
+
234
+ ```bash
235
+ # Check for linting issues
236
+ yarn lint
237
+
238
+ # Fix linting issues
239
+ yarn lint:fix
240
+
241
+ # Format code
242
+ yarn format
243
+
244
+ # Fix formatting issues
245
+ yarn format:fix
246
+ ```
247
+
248
+ ## 🤝 Contributing
249
+
250
+ Contributions are welcome! Here's how you can contribute:
251
+
252
+ 1. Fork the repository
253
+ 2. Create a new branch (`git checkout -b feature/your-feature-name`)
254
+ 3. Make your changes
255
+ 4. Run tests and linting (`yarn test && yarn lint`)
256
+ 5. Commit your changes (`git commit -m 'Add some feature'`)
257
+ 6. Push to the branch (`git push origin feature/your-feature-name`)
258
+ 7. Open a Pull Request
259
+
260
+ Please ensure your code follows the project's coding standards:
261
+
262
+ - Use TypeScript for all new code
263
+ - Follow the existing code style and patterns
264
+ - Write tests for new features
265
+ - Update documentation as needed
266
+
267
+ ### Code Style Guidelines
268
+
269
+ - Use `type` instead of `interface` for type definitions
270
+ - Use kebab-case for file names and PascalCase for component names
271
+ - Use named exports instead of default exports
272
+ - Include JSDoc documentation where appropriate
273
+ - Follow Biome linting conventions
274
+
275
+ ## 🐛 Troubleshooting
276
+
277
+ ### Common Issues
278
+
279
+ #### Application fails to connect to the backend
280
+
281
+ - Ensure the Local Operator backend is running
282
+ - Check that the `VITE_LOCAL_OPERATOR_API_URL` environment variable is set correctly
283
+ - Verify network connectivity between the UI and the backend
284
+
285
+ #### Development server crashes
286
+
287
+ - Check the console for error messages
288
+ - Ensure all dependencies are installed correctly
289
+ - Try clearing the node_modules folder and reinstalling dependencies
290
+
291
+ #### UI rendering issues
292
+
293
+ - Check for console errors in the developer tools
294
+ - Ensure you're using a compatible version of Node.js
295
+ - Try restarting the development server
296
+
297
+ ### Getting Help
298
+
299
+ If you encounter issues not covered here, please:
300
+
301
+ 1. Check the [GitHub Issues](https://github.com/local-operator/local-operator-ui/issues) for similar problems
302
+ 2. Open a new issue if your problem hasn't been reported
303
+
304
+ ## 📄 License
305
+
306
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const vm = require("vm");
5
+ const v8 = require("v8");
6
+ const Module = require("module");
7
+ v8.setFlagsFromString("--no-lazy");
8
+ v8.setFlagsFromString("--no-flush-bytecode");
9
+ const FLAG_HASH_OFFSET = 12;
10
+ const SOURCE_HASH_OFFSET = 8;
11
+ let dummyBytecode;
12
+ function setFlagHashHeader(bytecodeBuffer) {
13
+ if (!dummyBytecode) {
14
+ const script = new vm.Script("", {
15
+ produceCachedData: true
16
+ });
17
+ dummyBytecode = script.createCachedData();
18
+ }
19
+ dummyBytecode.slice(FLAG_HASH_OFFSET, FLAG_HASH_OFFSET + 4).copy(bytecodeBuffer, FLAG_HASH_OFFSET);
20
+ };
21
+ function getSourceHashHeader(bytecodeBuffer) {
22
+ return bytecodeBuffer.slice(SOURCE_HASH_OFFSET, SOURCE_HASH_OFFSET + 4);
23
+ };
24
+ function buffer2Number(buffer) {
25
+ let ret = 0;
26
+ ret |= buffer[3] << 24;
27
+ ret |= buffer[2] << 16;
28
+ ret |= buffer[1] << 8;
29
+ ret |= buffer[0];
30
+ return ret;
31
+ };
32
+ Module._extensions[".jsc"] = Module._extensions[".cjsc"] = function (module, filename) {
33
+ const bytecodeBuffer = fs.readFileSync(filename);
34
+ if (!Buffer.isBuffer(bytecodeBuffer)) {
35
+ throw new Error("BytecodeBuffer must be a buffer object.");
36
+ }
37
+ setFlagHashHeader(bytecodeBuffer);
38
+ const length = buffer2Number(getSourceHashHeader(bytecodeBuffer));
39
+ let dummyCode = "";
40
+ if (length > 1) {
41
+ dummyCode = "\"" + "\u200b".repeat(length - 2) + "\"";
42
+ }
43
+ const script = new vm.Script(dummyCode, {
44
+ filename: filename,
45
+ lineOffset: 0,
46
+ displayErrors: true,
47
+ cachedData: bytecodeBuffer
48
+ });
49
+ if (script.cachedDataRejected) {
50
+ throw new Error("Invalid or incompatible cached data (cachedDataRejected)");
51
+ }
52
+ const require = function (id) {
53
+ return module.require(id);
54
+ };
55
+ require.resolve = function (request, options) {
56
+ return Module._resolveFilename(request, module, false, options);
57
+ };
58
+ if (process.mainModule) {
59
+ require.main = process.mainModule;
60
+ }
61
+ require.extensions = Module._extensions;
62
+ require.cache = Module._cache;
63
+ const compiledWrapper = script.runInThisContext({
64
+ filename: filename,
65
+ lineOffset: 0,
66
+ columnOffset: 0,
67
+ displayErrors: true
68
+ });
69
+ const dirname = path.dirname(filename);
70
+ const args = [module.exports, require, module, filename, dirname, process, global];
71
+ return compiledWrapper.apply(module.exports, args);
72
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ require("./bytecode-loader.cjs");
3
+ require("./index.jsc");
Binary file
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const vm = require("vm");
5
+ const v8 = require("v8");
6
+ const Module = require("module");
7
+ v8.setFlagsFromString("--no-lazy");
8
+ v8.setFlagsFromString("--no-flush-bytecode");
9
+ const FLAG_HASH_OFFSET = 12;
10
+ const SOURCE_HASH_OFFSET = 8;
11
+ let dummyBytecode;
12
+ function setFlagHashHeader(bytecodeBuffer) {
13
+ if (!dummyBytecode) {
14
+ const script = new vm.Script("", {
15
+ produceCachedData: true
16
+ });
17
+ dummyBytecode = script.createCachedData();
18
+ }
19
+ dummyBytecode.slice(FLAG_HASH_OFFSET, FLAG_HASH_OFFSET + 4).copy(bytecodeBuffer, FLAG_HASH_OFFSET);
20
+ };
21
+ function getSourceHashHeader(bytecodeBuffer) {
22
+ return bytecodeBuffer.slice(SOURCE_HASH_OFFSET, SOURCE_HASH_OFFSET + 4);
23
+ };
24
+ function buffer2Number(buffer) {
25
+ let ret = 0;
26
+ ret |= buffer[3] << 24;
27
+ ret |= buffer[2] << 16;
28
+ ret |= buffer[1] << 8;
29
+ ret |= buffer[0];
30
+ return ret;
31
+ };
32
+ Module._extensions[".jsc"] = Module._extensions[".cjsc"] = function (module, filename) {
33
+ const bytecodeBuffer = fs.readFileSync(filename);
34
+ if (!Buffer.isBuffer(bytecodeBuffer)) {
35
+ throw new Error("BytecodeBuffer must be a buffer object.");
36
+ }
37
+ setFlagHashHeader(bytecodeBuffer);
38
+ const length = buffer2Number(getSourceHashHeader(bytecodeBuffer));
39
+ let dummyCode = "";
40
+ if (length > 1) {
41
+ dummyCode = "\"" + "\u200b".repeat(length - 2) + "\"";
42
+ }
43
+ const script = new vm.Script(dummyCode, {
44
+ filename: filename,
45
+ lineOffset: 0,
46
+ displayErrors: true,
47
+ cachedData: bytecodeBuffer
48
+ });
49
+ if (script.cachedDataRejected) {
50
+ throw new Error("Invalid or incompatible cached data (cachedDataRejected)");
51
+ }
52
+ const require = function (id) {
53
+ return module.require(id);
54
+ };
55
+ require.resolve = function (request, options) {
56
+ return Module._resolveFilename(request, module, false, options);
57
+ };
58
+ if (process.mainModule) {
59
+ require.main = process.mainModule;
60
+ }
61
+ require.extensions = Module._extensions;
62
+ require.cache = Module._cache;
63
+ const compiledWrapper = script.runInThisContext({
64
+ filename: filename,
65
+ lineOffset: 0,
66
+ columnOffset: 0,
67
+ displayErrors: true
68
+ });
69
+ const dirname = path.dirname(filename);
70
+ const args = [module.exports, require, module, filename, dirname, process, global];
71
+ return compiledWrapper.apply(module.exports, args);
72
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ require("./bytecode-loader.cjs");
3
+ require("./index.jsc");
Binary file