Back to Articles

Accomplish: Building a Privacy-First Desktop AI Agent With Electron and TypeScript

[ View on GitHub ]

Accomplish: Building a Privacy-First Desktop AI Agent With Electron and TypeScript

Hook

Most AI agents send your file contents to remote servers for processing. Accomplish took the opposite bet: what if everything stayed on your machine, and you approved every action before execution?

Context

The AI agent landscape has exploded with tools promising to automate workflows, manage files, and handle tedious tasks. But there's a fundamental tension: the more powerful an AI agent becomes, the more access it needs to your data. Tools like GitHub Copilot Workspace, Devin, and various ChatGPT plugins all follow a similar pattern—send your context to remote servers, process it there, and return results. This works, but it creates uncomfortable privacy tradeoffs. Your API keys, sensitive documents, and proprietary code all leave your machine.

Accomplish emerged as a direct response to this architecture. Rather than building another cloud-based agent, the team created an Electron desktop application that runs entirely locally. It integrates with 15+ LLM providers (OpenAI, Anthropic, Google, xAI) or runs completely offline using Ollama or LM Studio. The agent can manage files, create documents, and automate browser tasks—but every action requires explicit user approval, and all processing happens on your hardware. It's a different philosophy: transparency and control over seamless convenience, local-first over cloud-native.

Technical Insight

Accomplish's architecture centers on three key technical decisions that shape how it operates: Electron-based local execution, a provider-agnostic LLM abstraction layer, and an action approval workflow that intercepts every agent operation.

The Electron foundation is critical here. Unlike browser extensions or web apps, a desktop application gets deeper OS-level access—file system operations, process management, and native integrations. Accomplish uses TypeScript throughout, leveraging Electron's main/renderer process separation to isolate AI decision-making from UI rendering. The main process handles all file operations and system calls, while the renderer process manages the user interface and approval dialogs. This separation ensures that even if the renderer is compromised, file system access remains gated.

The LLM provider abstraction is where Accomplish differentiates itself from single-vendor tools. Rather than hardcoding OpenAI's API, they built an adapter pattern that normalizes requests across providers. Here's a simplified example of how this might look:

interface LLMProvider {
  name: string;
  complete(prompt: string, options: CompletionOptions): Promise<LLMResponse>;
  validateApiKey(key: string): Promise<boolean>;
}

class OpenAIProvider implements LLMProvider {
  name = 'OpenAI';
  
  async complete(prompt: string, options: CompletionOptions): Promise<LLMResponse> {
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${options.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: options.model || 'gpt-4',
        messages: [{ role: 'user', content: prompt }],
        temperature: options.temperature || 0.7
      })
    });
    
    const data = await response.json();
    return {
      text: data.choices[0].message.content,
      tokens: data.usage.total_tokens,
      provider: this.name
    };
  }
  
  async validateApiKey(key: string): Promise<boolean> {
    // Validation logic
    return true;
  }
}

class OllamaProvider implements LLMProvider {
  name = 'Ollama';
  
  async complete(prompt: string, options: CompletionOptions): Promise<LLMResponse> {
    // Local model inference, no API key needed
    const response = await fetch('http://localhost:11434/api/generate', {
      method: 'POST',
      body: JSON.stringify({
        model: options.model || 'llama2',
        prompt: prompt
      })
    });
    
    // Parse streaming response
    const data = await response.json();
    return {
      text: data.response,
      tokens: 0, // Ollama doesn't report tokens the same way
      provider: this.name
    };
  }
  
  async validateApiKey(key: string): Promise<boolean> {
    return true; // No key needed for local models
  }
}

This abstraction means users can switch from OpenAI to Anthropic to a local Llama model without the application logic changing. It's provider-agnostic by design, which matters when you're trying to avoid vendor lock-in.

The action approval system is perhaps the most interesting architectural piece. Most AI agents execute actions automatically—if the LLM decides to delete a file, it happens immediately. Accomplish inverts this with a middleware pattern that intercepts every proposed action, presents it to the user for approval, and only executes on confirmation. The skill learning system builds on this: when users approve a sequence of actions, they can save it as a reusable skill. Think of it as recording a macro, but defined by AI-generated actions rather than recorded keystrokes.

Folder-level access controls add another security layer. Users explicitly grant the agent access to specific directories. If the AI tries to read or write outside those bounds, the action is automatically rejected. This is implemented through a permissions manifest that's checked before any file system call executes. It's reminiscent of how mobile apps request permissions, but applied to desktop AI automation.

Gotcha

The BYOK (Bring Your Own Key) model is both Accomplish's strength and its Achilles heel. On one hand, you're not locked into a subscription and you control your data. On the other hand, you're now responsible for managing API keys across multiple providers, understanding token costs, and troubleshooting when a provider changes its API. Non-technical users will struggle here. If you don't know what an API key is or how to get one from OpenAI's dashboard, the onboarding experience will be frustrating. There's no "sign up and go" flow—you need to bring your own infrastructure.

The desktop-only constraint is also limiting in 2024. Many knowledge workers are increasingly mobile, using iPads or working across multiple devices. Accomplish has no mobile app, no web interface, and no sync mechanism. If you define a useful skill on your work laptop, it doesn't automatically appear on your home desktop. You're locked to wherever you installed the app. For a tool focused on productivity automation, this feels like a significant gap. Additionally, at version 0.5.17, you'll encounter rough edges. The skill library is limited compared to mature automation platforms like Zapier or n8n. Error messages may be cryptic, especially when dealing with LLM provider issues, and the UI, while functional, lacks the polish of commercial products.

Verdict

Use Accomplish if you're a developer or privacy-conscious professional who wants desktop automation without sending sensitive data to third-party servers, you're comfortable managing API keys and understanding LLM pricing models, and you value transparency—seeing and approving every action before execution. It's particularly strong for file management workflows, document generation tasks, and scenarios where you need to keep proprietary information local. Skip it if you need mobile access or cross-device sync, you want a zero-config SaaS experience with no API key management, or you require a mature ecosystem with hundreds of pre-built integrations. It's too technical for non-developers and too desktop-focused for mobile-first workflows. But for engineers who want an AI coworker that respects their privacy and runs entirely under their control, Accomplish is one of the few options that gets the architecture right.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/accomplish-ai-accomplish.svg)](https://starlog.is/api/badge-click/developer-tools/accomplish-ai-accomplish)