ohwow 0.1.13 → 0.2.0

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.
Files changed (33) hide show
  1. package/LICENSE +17 -17
  2. package/README.md +120 -166
  3. package/dist/api.d.ts +91 -0
  4. package/dist/api.js +2 -0
  5. package/dist/index.d.ts +1 -90
  6. package/dist/index.js +1754 -590
  7. package/dist/mcp-server/index.js +10 -49
  8. package/dist/migrations/001-data-plane-tables.sql +1 -1
  9. package/dist/migrations/002-agents-table.sql +1 -1
  10. package/dist/migrations/017-openclaw-call-logs.sql +15 -0
  11. package/dist/migrations/038-peer-queue-tracking.sql +5 -0
  12. package/dist/migrations/039-task-delegation.sql +5 -0
  13. package/dist/migrations/040-self-improvement-tables.sql +109 -0
  14. package/dist/migrations/041-session-metadata.sql +3 -0
  15. package/dist/migrations/042-memory-sync.sql +45 -0
  16. package/dist/migrations/043-multi-connection.sql +18 -0
  17. package/dist/migrations/044-multi-connection-fixes.sql +32 -0
  18. package/dist/migrations/045-task-state.sql +21 -0
  19. package/dist/migrations/046-state-changelog.sql +18 -0
  20. package/dist/migrations/047-outbound-queue.sql +9 -0
  21. package/dist/migrations/048-tasks-column-alignment.sql +37 -0
  22. package/dist/migrations/049-agents-column-alignment.sql +25 -0
  23. package/dist/migrations/050-workflows-trigger-alignment.sql +25 -0
  24. package/dist/migrations/051-execution-engine-tables.sql +132 -0
  25. package/dist/migrations/052-a2a-rate-limit-alignment.sql +8 -0
  26. package/dist/migrations/053-llm-cache.sql +20 -0
  27. package/dist/migrations/054-task-checkpoints.sql +13 -0
  28. package/dist/migrations/055-resource-usage.sql +12 -0
  29. package/dist/migrations/056-sandbox-tables.sql +35 -0
  30. package/dist/web/assets/{index-DSojxPLI.css → index-1Wga6dwZ.css} +1 -1
  31. package/dist/web/assets/{index-D6DkPUkA.js → index-BHeLTE3N.js} +26 -26
  32. package/dist/web/index.html +2 -2
  33. package/package.json +59 -14
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- Business Source License 1.1
1
+ MIT License
2
2
 
3
- Parameters
3
+ Copyright (c) 2026 Jesus David Oñoro Delgado
4
4
 
5
- Licensor: ohwow
6
- Licensed Work: ohwow runtime (all versions)
7
- Additional Use Grant: You may use the Licensed Work for any purpose, including
8
- production use, EXCEPT for building or offering a product
9
- or service that competes with ohwow.fun or its features.
10
- Change Date: March 2, 2030
11
- Change License: Apache License, Version 2.0
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:
12
11
 
13
- For the full BSL 1.1 license text, see https://mariadb.com/bsl11/
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
14
 
15
- Notice
16
-
17
- The Licensed Work is (c) 2024-2026 ohwow.
18
-
19
- The full text of the Business Source License 1.1 is incorporated by reference
20
- from the URL above. This file states only the parameters specific to this
21
- project. In the event of any conflict, the full license text governs.
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 CHANGED
@@ -1,88 +1,35 @@
1
1
  # ohwow
2
2
 
3
- Send me a message for support at ogsus@ohwow.fun
3
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
+ [![npm version](https://img.shields.io/npm/v/ohwow)](https://www.npmjs.com/package/ohwow)
5
+ [![CI](https://github.com/ohwow-fun/ohwow/actions/workflows/ci.yml/badge.svg)](https://github.com/ohwow-fun/ohwow/actions/workflows/ci.yml)
6
+ [![npm downloads](https://img.shields.io/npm/dm/ohwow)](https://www.npmjs.com/package/ohwow)
7
+ [![Discord](https://img.shields.io/badge/Discord-join-5865F2)](https://discord.gg/WUGnGqceeY)
4
8
 
5
- A free and open-source local AI agent runtime. Full-featured out of the box with [Ollama](https://ollama.com) for local models. Cloud features (sync, OAuth integrations, cloud task dispatch, webhook relay) available with an [ohwow.fun](https://ohwow.fun) subscription.
9
+ **Your AI team that runs on your laptop. Agents that learn, message customers, browse the web, and control your desktop. Costs $0.**
6
10
 
7
- ## Getting Started
11
+ <p align="center">
12
+ <img src="docs/demo.gif" alt="ohwow demo" width="800">
13
+ </p>
8
14
 
9
- ### Install
15
+ ohwow is an open source AI business operating system. A team of AI agents that live on your laptop, handle your CRM, message your customers on WhatsApp, automate your workflows, browse the web, and now control your desktop. All running locally. All free. No cloud required.
10
16
 
11
- ```bash
12
- npm install ohwow -g
13
- ```
14
-
15
- ### Requirements
16
-
17
- - Node.js 20+
18
- - [Ollama](https://ollama.com) for local models
19
- - Optional: Anthropic API key (for Claude models)
20
- - Optional: Playwright browsers (`npx playwright install chromium`) for browser automation
21
- - Optional: C++ compiler may be needed on some platforms for `better-sqlite3`
22
-
23
- ### Launch
17
+ ## Quick Start
24
18
 
25
19
  ```bash
20
+ npm install ohwow -g
26
21
  ohwow
27
22
  ```
28
23
 
29
- ## First Launch
30
-
31
- A setup wizard appears in your terminal. Point it at your Ollama instance to get started. To connect to ohwow.fun cloud, enter your license key (from the ohwow.fun dashboard, under Settings > License).
32
-
33
- Config is saved to `~/.ohwow/config.json`. You only do this once.
34
-
35
- ## What Happens at Startup
36
-
37
- Once configured, the runtime:
24
+ A setup wizard walks you through connecting to [Ollama](https://ollama.com), picking a model, and selecting agents for your business type. Running in under 5 minutes.
38
25
 
39
- 1. Loads config from `~/.ohwow/config.json`
40
- 2. Spawns the daemon process
41
- 3. Initializes the local SQLite database
42
- 4. Starts the execution engine, orchestrator, and model router
43
- 5. Connects messaging channels (WhatsApp, Telegram) if configured
44
- 6. Starts the scheduler, proactive engine, and trigger evaluator
45
- 7. Launches the HTTP server (default port 7700)
46
- 8. Connects to the ohwow.fun control plane (enterprise)
47
- 9. Opens a WebSocket for real-time updates
26
+ <p align="center">
27
+ <img src="docs/onboarding.gif" alt="ohwow onboarding" width="800">
28
+ </p>
48
29
 
49
- From here, agents execute tasks on your hardware using your own API keys. The dashboard sends the work, your machine does the thinking.
30
+ ## Talk to it like a person
50
31
 
51
- ## CLI Commands
52
-
53
- | Command | What it does |
54
- |---|---|
55
- | `ohwow` | Start the TUI (default) |
56
- | `ohwow --daemon` | Start daemon in foreground (for systemd/launchd/Docker) |
57
- | `ohwow stop` | Stop the daemon |
58
- | `ohwow status` | Check daemon status (PID and port) |
59
- | `ohwow logs` | Tail daemon logs |
60
- | `ohwow restart` | Restart the daemon |
61
-
62
- ## TUI
63
-
64
- The terminal UI opens into a chat interface with tab navigation. Use arrow keys or tab to switch between:
65
-
66
- - **Dashboard** — overview of your workspace
67
- - **Agents** — manage agent configs, memory, and capabilities
68
- - **Tasks** — view and manage running/completed tasks
69
- - **Approvals** — review pending items before execution
70
- - **Activity** — live feed of everything happening
71
- - **Automations** — webhook-based automation triggers
72
- - **Contacts** — CRM with leads, customers, partners
73
- - **Settings** — config, connections, license
74
-
75
- Everything you see in the web dashboard is also here, running locally.
76
-
77
- ## Web UI
78
-
79
- The runtime serves a built-in React app at `http://localhost:7700`. Same capabilities as the TUI. Useful if you prefer a graphical interface or want to share access with your team on the local network. Override the port with the `OHWOW_PORT` env var.
80
-
81
- ## Orchestrator
82
-
83
- The orchestrator is a conversational assistant built into the runtime with 40+ tools. Open the Chat tab in the TUI, or use the web UI.
84
-
85
- You can talk to it naturally:
32
+ The orchestrator is a conversational assistant with 150+ tools. Open the Chat tab and speak naturally:
86
33
 
87
34
  | What you say | What happens |
88
35
  |---|---|
@@ -90,135 +37,142 @@ You can talk to it naturally:
90
37
  | "What failed today?" | Lists recent failed tasks with details |
91
38
  | "Schedule outreach every weekday at 9am" | Creates a cron schedule for the agent |
92
39
  | "Send a WhatsApp to the team: launching Friday" | Sends the message through your connected WhatsApp |
40
+ | "Open Figma and export the hero banner as PNG" | Takes over your desktop, opens the app, clicks through menus, saves the file |
93
41
  | "Plan out researching 5 new leads this week" | Creates a multi-step plan with agent assignments, waits for your approval |
94
42
  | "Show me the business pulse" | Returns task stats, contact pipeline, costs, and streaks |
95
43
  | "Create a project for the website redesign" | Creates a project with a Kanban board |
96
- | "Move that task to review" | Moves a task between board columns |
97
-
98
- The orchestrator covers: agents, tasks, projects, CRM (contacts, pipeline, events), scheduling, messaging (WhatsApp + Telegram), A2A connections, goal planning, deep research, analytics, and workflows. It can also switch your TUI tabs if you ask ("go to approvals").
99
44
 
100
45
  ## Features
101
46
 
102
- ### Agent Memory
103
-
104
- After each task, key facts, skills, and feedback are extracted and stored locally. Memories are periodically consolidated to keep context sharp. On future tasks, relevant memories are retrieved via RAG and compiled into the agent's context. Agents improve the more they work. View any agent's memory from the Agents tab.
105
-
106
- ### Browser Automation
107
-
108
- Agents can browse the web using Playwright. Navigation, clicking, form filling, screenshots, and content extraction. The browser launches on first use and runs headless by default. Set `OHWOW_BROWSER_HEADLESS=false` to watch it work.
109
-
110
- ### WhatsApp and Telegram
111
-
112
- Connect WhatsApp through a QR code scan in Settings (no Meta business API needed). Connect Telegram with a bot token. Once connected, incoming messages route to the orchestrator automatically. Your agents can reply, take action, or flag things for your attention. You control which chats are allowed.
113
-
114
- ### Voice
115
-
116
- Full voice pipeline with local and cloud options:
117
-
118
- - **Speech-to-Text**: Voicebox (Whisper via local FastAPI server), WhisperLocal (via Ollama), or WhisperAPI (OpenAI cloud fallback)
119
- - **Text-to-Speech**: VoiceboxTTS (local), Piper (local), or OpenAI TTS (cloud fallback)
120
-
121
- ### A2A Protocol
122
-
123
- Connect to external agents using the [A2A protocol](https://google.github.io/A2A/) over JSON-RPC 2.0. Each agent publishes a card at `/.well-known/agent-card.json` describing its capabilities. Trust levels (`read_only`, `execute`, `autonomous`, `admin`) control what external agents can do. Scopes cover tasks, agents, results, and file access. Managed from the A2A tab or through the orchestrator.
124
-
125
- ### Scheduling and Proactive Engine
126
-
127
- Set agents or workflows to run on cron schedules. Create schedules through conversation ("schedule the analyst every Monday at 8am") or from the Schedules tab.
128
-
129
- The proactive engine runs every 30 minutes and checks for overdue tasks, aging approvals, and idle agents. It generates nudges (suggestions, not auto-executions) so nothing falls through the cracks.
130
-
131
- ### Goal Planning and Approvals
132
-
133
- For complex goals, the orchestrator breaks them into multi-step plans with agent assignments and dependencies. Plans start as drafts. You review the steps, approve or reject, and track execution from the Plans tab. Rejected tasks can retry with your feedback included.
134
-
135
- ### Projects and CRM
136
-
137
- Organize tasks into projects with Kanban boards (backlog, todo, in progress, review, done). The built-in CRM tracks contacts (leads, customers, partners), logs events (calls, emails, meetings), and gives you pipeline analytics. All stored locally.
138
-
139
- ### Automations and Triggers
140
-
141
- Webhook-based automations that fire on external events. Configure field mapping to extract data from incoming payloads and route it to agents or workflows. Managed from the Automations tab.
47
+ | Category | What you get |
48
+ |---|---|
49
+ | AI Agents | 48 pre-built agents across 6 business types, persistent memory, RAG retrieval |
50
+ | Orchestrator | 150+ tools, natural language chat interface for everything |
51
+ | Desktop Control | Full macOS desktop automation: mouse, keyboard, screen capture. Your agents operate any app on your computer. (macOS only) |
52
+ | Messaging | WhatsApp + Telegram built in, auto-routing to agents |
53
+ | Browser | Local Chromium automation (navigate, click, fill, screenshot, extract) |
54
+ | Voice | Local STT/TTS (Whisper, Piper) with cloud fallbacks |
55
+ | CRM | Contacts, pipeline, events, analytics, all stored locally |
56
+ | Scheduling | Cron schedules + proactive nudge engine every 30 minutes |
57
+ | Workflows | DAG-based multi-agent execution graphs with conditions |
58
+ | Multidevice | Zero-config mDNS mesh, automatic task routing across machines |
59
+ | MCP | External tool integration via Model Context Protocol |
60
+ | A2A | Google Agent-to-Agent protocol over JSON-RPC 2.0 |
61
+ | Sandbox | Isolated JavaScript execution (no fs/net/process access) |
142
62
 
143
- ### Workflows
63
+ ## Desktop Control
144
64
 
145
- DAG-based multi-agent execution graphs. Define sequences of agent tasks with conditions and branches. Workflows can be triggered manually, on a schedule, or via automation triggers.
65
+ Your agents aren't trapped inside a chat window. They can use your computer.
146
66
 
147
- ### MCP Servers
67
+ ohwow agents see your screen, move the mouse, type on the keyboard, and operate any macOS app. Not through brittle scripts or accessibility hacks. They look at what's on screen, reason about what to do next, and act. The same way you would, but without getting distracted.
148
68
 
149
- Integrate external tools via the [Model Context Protocol](https://modelcontextprotocol.io/). Supports stdio (subprocess) and HTTP (Streamable HTTP) transports. Authentication via OAuth 2.1, bearer tokens, or API keys. MCP tools are auto-adapted to the Anthropic SDK format and can be assigned per-agent or globally.
69
+ **How it works:** the agent takes a screenshot, analyzes it with a vision model, decides the next action (click, type, scroll, key press), executes it, then takes another screenshot to see the result. This perception/reasoning/action loop repeats until the task is done.
150
70
 
151
- ### Code Sandbox
71
+ ```
72
+ You: "Fill out the Q1 expense report in Google Sheets using last month's receipts folder"
73
+
74
+ ohwow:
75
+ 1. Opens Finder, navigates to the receipts folder
76
+ 2. Opens Google Sheets in Chrome
77
+ 3. Reads each receipt, types the amounts into the right cells
78
+ 4. Double-checks totals
79
+ 5. Done. You were making coffee the whole time.
80
+ ```
152
81
 
153
- Agents can execute JavaScript in an isolated sandbox (Node.js `vm` module). No filesystem, network, or process access. 5-second default timeout, 30-second maximum. Safe globals only (Math, JSON, Date, Array, Object, Map, Set, etc.).
82
+ **Safety first.** Desktop control requires your explicit permission before activating. You can stop the agent at any point. Dangerous actions (typing in Terminal, changing system settings) trigger additional approval. An emergency stop halts everything instantly.
154
83
 
155
- ### Web Search
84
+ **Works with Dispatch.** Connect to [ohwow.fun](https://ohwow.fun) and trigger desktop tasks from your phone. Start a task on the train, your MacBook at home does the work. Check progress from the dashboard.
156
85
 
157
- Agents with web search enabled can search the web during task execution, powered by Anthropic's built-in search tool.
86
+ > Desktop control currently supports macOS only. All other features (agents, chat, browser automation, voice, CRM, scheduling, MCP) work on macOS, Linux, and Windows. See [Windows Setup Guide](docs/windows-setup.md) for details.
158
87
 
159
- ### Local Models with Ollama
88
+ ## Who is this for?
160
89
 
161
- If you run [Ollama](https://ollama.com) locally, the runtime routes lightweight tasks (orchestration, memory extraction) to your local model instead of Claude. The model catalog includes 25+ models across 5 memory tiers with device-aware recommendations. Complex work (planning, agent tasks, browser automation) still goes to Claude. If Ollama goes down, everything falls back to Anthropic automatically.
90
+ - **Solo founders** automating marketing, outreach, and ops with zero budget
91
+ - **Busy operators** who want an AI team that handles the repetitive screen work while they focus on growth
92
+ - **Small teams** that need agents coordinating across devices
93
+ - **Agencies** managing operations for multiple clients
94
+ - **Privacy-conscious businesses** that need AI automation without cloud lock-in
95
+ - **Developers** building custom agents with the orchestrator, MCP tools, and desktop control APIs
162
96
 
163
- ### Offline Mode
97
+ ## Why ohwow?
164
98
 
165
- If ohwow.fun becomes unreachable, the runtime continues with cached agent configs. Tasks still execute, results still store locally. When connectivity returns, everything syncs back up.
99
+ | | Zapier | n8n | Make | OpenClaw | **ohwow** |
100
+ |---|---|---|---|---|---|
101
+ | Runs on your machine | No | Self-host option | No | Yes | **Yes, always** |
102
+ | Desktop control | No | No | No | Yes | **Yes, with safety guards** |
103
+ | AI agents with persistent memory | No | Yes (LangChain nodes) | No | Partial | **Yes, agents learn over time** |
104
+ | Cost to start | $20+/mo | Free (self-host) | $10+/mo | Free | **$0 with Ollama** |
105
+ | Pre-built business agents | No | No | No | No | **48 agents, 6 biz types** |
106
+ | WhatsApp + Telegram | Plugin | Plugin | Plugin | No | **Built in** |
107
+ | CRM + contacts | No | No | No | No | **Built in** |
108
+ | Multi-device mesh | No | No | No | No | **Zero-config** |
109
+ | Browser automation | No | Partial | No | Yes | **Built in (Chromium)** |
110
+ | Workflow DAGs | Yes | Yes | Yes | No | **Yes, with conditions + parallel** |
111
+ | Phone dispatch | No | No | No | No | **Yes, via ohwow.fun** |
112
+ | Your data stays local | No | Optional | No | Yes | **Always** |
166
113
 
167
- ## Connected Mode (Cloud)
114
+ > Comparison as of March 2026. We love what OpenClaw, n8n, and others are building. ohwow focuses on a different problem: a complete AI business runtime, not just a single agent or workflow tool.
168
115
 
169
- Connect to ohwow.fun to add cloud features on top of the free local runtime.
116
+ ## Self-hosting
170
117
 
171
- **Cloud features (require connection):**
172
- - Cloud sync: agent configs sync from your dashboard
173
- - Cloud task dispatch: receive tasks dispatched from the web
174
- - OAuth integrations: Gmail, Slack, and other cloud-based integrations
175
- - Webhook relay: cloud-proxied webhooks for external services
176
- - Heartbeats and health monitoring
118
+ | Command | What it does |
119
+ |---|---|
120
+ | `ohwow` | Start the TUI (default) |
121
+ | `ohwow --daemon` | Start daemon in foreground (for systemd/launchd/Docker) |
122
+ | `ohwow stop` | Stop the daemon |
123
+ | `ohwow status` | Check daemon status (PID and port) |
124
+ | `ohwow logs` | Tail daemon logs |
125
+ | `ohwow restart` | Restart the daemon |
177
126
 
178
- **What stays local (always):**
179
- - Prompts and system instructions
180
- - Agent outputs and full conversations
181
- - Long-term agent memory
182
- - CRM contacts and activity history
183
- - WhatsApp and Telegram message history
184
- - Browser session data and screenshots
127
+ The runtime serves a web UI at `http://localhost:7700`. Override the port with `OHWOW_PORT`.
185
128
 
186
- **How it works:**
129
+ Docker:
187
130
 
188
- Activate with a license key from the ohwow.fun dashboard (Settings > License). The runtime connects to the control plane via long-polling, receives task dispatches, and sends back status updates. Heartbeats confirm the device is online. Each license is locked to a single device. The cloud dashboard gives you a web interface for managing agents, reviewing tasks, and monitoring your workspace without touching the terminal.
131
+ ```bash
132
+ docker run -d --name ohwow -p 7700:7700 -v ~/.ohwow:/root/.ohwow ohwow
133
+ ```
189
134
 
190
- All local features (agents, scheduling, WhatsApp, Telegram, A2A, browser automation, voice, CRM, automations, approvals) work without a cloud connection.
135
+ ## Cloud
191
136
 
192
- ## Headless / Daemon Mode
137
+ Connect to [ohwow.fun](https://ohwow.fun) for cloud features on top of the free local runtime:
193
138
 
194
- For servers, containers, or always-on deployments:
139
+ - **Desktop dispatch** from your phone. Assign tasks to your MacBook while you're out.
140
+ - **OAuth integrations** (Gmail, Slack, and more)
141
+ - **Cloud task dispatch** from the web dashboard or mobile
142
+ - **AI site generator** with hosting and custom domains
143
+ - **Webhook relay** for external services
144
+ - **Fleet management** across devices
195
145
 
196
- ```bash
197
- ohwow --daemon
198
- ```
146
+ All local features work without cloud. See [pricing](https://ohwow.fun/pricing) for plans starting at $29/mo.
199
147
 
200
- Runs the daemon in the foreground (no TUI). Suitable for systemd, launchd, or Docker. Set `OHWOW_HEADLESS=1` as an alternative. The web UI still runs normally. Configure through environment variables.
148
+ ## Requirements
201
149
 
202
- ## Configuration
150
+ - Node.js 20+
151
+ - [Ollama](https://ollama.com) for local models
152
+ - Optional: Anthropic API key (for Claude models)
153
+ - Optional: Playwright browsers (`npx playwright install chromium`) for browser automation
154
+ - Optional: macOS Accessibility permission for desktop control (macOS only)
155
+ - Optional: Visual Studio C++ Build Tools on Windows for `better-sqlite3` native module
203
156
 
204
- Config lives at `~/.ohwow/config.json`. Key environment variables:
157
+ ## Troubleshooting
205
158
 
206
- | Variable | Purpose |
207
- |---|---|
208
- | `OHWOW_PORT` | HTTP server port (default 7700) |
209
- | `OHWOW_HEADLESS` | Set to `1` for headless mode |
210
- | `OHWOW_BROWSER_HEADLESS` | Set to `false` to show the browser |
211
- | `ANTHROPIC_API_KEY` | Anthropic API key for Claude models |
159
+ | Problem | Solution |
160
+ |---------|----------|
161
+ | `better-sqlite3` build fails | Install build tools: `xcode-select --install` (macOS), `sudo apt install build-essential python3` (Linux), or Visual Studio C++ Build Tools (Windows) |
162
+ | Ollama not detected | Ensure Ollama is running (`ollama serve`) and accessible at `http://localhost:11434` |
163
+ | Port 7700 in use | Set `OHWOW_PORT=7701` or any free port |
164
+ | WhatsApp QR expired | Restart ohwow and scan the new QR within 60 seconds |
165
+ | `EACCES` on global install | Use `npm install ohwow -g --prefix ~/.npm-global` or fix npm permissions |
212
166
 
213
- ## Supported Models
167
+ ## Community
214
168
 
215
- | Model | Provider |
216
- |---|---|
217
- | Claude Opus 4.6 | Anthropic |
218
- | Claude Sonnet 4.6 | Anthropic |
219
- | Claude Haiku 4.5 | Anthropic |
220
- | Any Ollama model | Local |
169
+ - [Discord](https://discord.gg/WUGnGqceeY)
170
+ - [Contributing](./CONTRIBUTING.md)
171
+ - [Architecture](./ARCHITECTURE.md)
172
+ - [Security](./SECURITY.md)
173
+ - [Governance](./GOVERNANCE.md)
174
+ - Email: ogsus@ohwow.fun
221
175
 
222
176
  ## License
223
177
 
224
- BSL 1.1 (Business Source License). Free to use, including production. You can't use it to build a competing product. Converts to Apache 2.0 on March 2, 2030. See [LICENSE](./LICENSE) for details.
178
+ [MIT](./LICENSE)
package/dist/api.d.ts ADDED
@@ -0,0 +1,91 @@
1
+ import Database from 'better-sqlite3';
2
+
3
+ /**
4
+ * DatabaseAdapter Interface (Runtime Copy)
5
+ *
6
+ * Abstracts the query builder pattern used by Supabase's PostgREST client.
7
+ * Both the Supabase adapter (cloud) and SQLite adapter (local runtime)
8
+ * implement this interface, allowing all agent services to work with either backend.
9
+ *
10
+ * Mirrors the SupabaseClient chaining API: .from(table).select().eq().single()
11
+ */
12
+ interface DbResult<T> {
13
+ data: T | null;
14
+ error: DbError | null;
15
+ count?: number | null;
16
+ }
17
+ interface DbError {
18
+ message: string;
19
+ code?: string;
20
+ details?: string;
21
+ hint?: string;
22
+ }
23
+ interface FilterBuilder<T> {
24
+ eq(column: string, value: unknown): FilterBuilder<T>;
25
+ neq(column: string, value: unknown): FilterBuilder<T>;
26
+ gt(column: string, value: unknown): FilterBuilder<T>;
27
+ gte(column: string, value: unknown): FilterBuilder<T>;
28
+ lt(column: string, value: unknown): FilterBuilder<T>;
29
+ lte(column: string, value: unknown): FilterBuilder<T>;
30
+ in(column: string, values: unknown[]): FilterBuilder<T>;
31
+ is(column: string, value: null | boolean): FilterBuilder<T>;
32
+ or(filters: string, options?: {
33
+ foreignTable?: string;
34
+ }): FilterBuilder<T>;
35
+ not(column: string, operator: string, value: unknown): FilterBuilder<T>;
36
+ order(column: string, options?: {
37
+ ascending?: boolean;
38
+ }): FilterBuilder<T>;
39
+ limit(count: number): FilterBuilder<T>;
40
+ range(from: number, to: number): FilterBuilder<T>;
41
+ single(): PromiseLike<DbResult<T>>;
42
+ maybeSingle(): PromiseLike<DbResult<T | null>>;
43
+ then<TResult1 = DbResult<T[]>, TResult2 = never>(onfulfilled?: ((value: DbResult<T[]>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
44
+ }
45
+ type SelectBuilder<T> = FilterBuilder<T>;
46
+ interface InsertBuilder<T> {
47
+ select(columns?: string): FilterBuilder<T>;
48
+ then<TResult1 = DbResult<null>, TResult2 = never>(onfulfilled?: ((value: DbResult<null>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
49
+ }
50
+ interface UpdateBuilder<T> extends FilterBuilder<T> {
51
+ eq(column: string, value: unknown): UpdateBuilder<T>;
52
+ neq(column: string, value: unknown): UpdateBuilder<T>;
53
+ select(columns?: string): FilterBuilder<T>;
54
+ }
55
+ type DeleteBuilder<T> = FilterBuilder<T>;
56
+ interface TableBuilder<T = Record<string, unknown>> {
57
+ select(columns?: string, options?: {
58
+ count?: 'exact';
59
+ head?: boolean;
60
+ }): SelectBuilder<T>;
61
+ insert(data: Partial<T> | Partial<T>[]): InsertBuilder<T>;
62
+ update(data: Partial<T>): UpdateBuilder<T>;
63
+ delete(): DeleteBuilder<T>;
64
+ }
65
+ interface DatabaseAdapter {
66
+ from<T = Record<string, unknown>>(table: string): TableBuilder<T>;
67
+ rpc<T = unknown>(fn: string, params?: Record<string, unknown>): PromiseLike<DbResult<T>>;
68
+ }
69
+
70
+ /**
71
+ * SQLite Adapter
72
+ *
73
+ * Implements the DatabaseAdapter interface using better-sqlite3.
74
+ * Translates the SupabaseClient-shaped query builder API into SQL queries
75
+ * against a local SQLite database.
76
+ *
77
+ * Used by the local runtime for local data plane storage.
78
+ */
79
+
80
+ /**
81
+ * Map of registered RPC functions for the SQLite adapter.
82
+ * In Supabase, .rpc() calls server-side Postgres functions.
83
+ * In SQLite, we register JS functions that perform the same logic.
84
+ */
85
+ type RpcHandler = (params: Record<string, unknown>) => unknown;
86
+ interface SqliteAdapterOptions {
87
+ rpcHandlers?: Record<string, RpcHandler>;
88
+ }
89
+ declare function createSqliteAdapter(db: Database.Database, options?: SqliteAdapterOptions): DatabaseAdapter;
90
+
91
+ export { type SqliteAdapterOptions, createSqliteAdapter };
package/dist/api.js ADDED
@@ -0,0 +1,2 @@
1
+ import { createRequire } from 'module'; const require = createRequire(import.meta.url);
2
+ function F(s,e){return{message:s,code:e}}function q(s,e){return{data:s,error:null,count:e}}function f(s,e){return{data:null,error:F(s,e)}}function A(s){let e=s.split(","),i=[],o=[];for(let p of e){let c=p.trim().split(".");if(c.length<3)continue;let l=c[0],t=c[1],r=c.slice(2).join(".");switch(t){case"eq":i.push(`${l} = ?`),o.push(r);break;case"neq":i.push(`${l} != ?`),o.push(r);break;case"gt":i.push(`${l} > ?`),o.push(r);break;case"gte":i.push(`${l} >= ?`),o.push(r);break;case"lt":i.push(`${l} < ?`),o.push(r);break;case"lte":i.push(`${l} <= ?`),o.push(r);break;case"is":r==="null"?i.push(`${l} IS NULL`):r==="true"?i.push(`${l} = 1`):r==="false"&&i.push(`${l} = 0`);break;case"ilike":i.push(`${l} LIKE ? COLLATE NOCASE`),o.push(r.replace(/%25/g,"%"));break;case"like":i.push(`${l} LIKE ?`),o.push(r);break;case"in":{let n=r.replace(/^\(/,"").replace(/\)$/,"").split(",");i.push(`${l} IN (${n.map(()=>"?").join(",")})`),o.push(...n)}break;default:i.push(`${l} ${t} ?`),o.push(r)}}return{sql:`(${i.join(" OR ")})`,params:o}}function O(s){if(s.conditions.length===0)return{sql:"",params:[]};let e=[],i=[];for(let o of s.conditions)e.push(o.sql),i.push(...o.params);return{sql:` WHERE ${e.join(" AND ")}`,params:i}}function B(s){return s.orderClauses.length===0?"":` ORDER BY ${s.orderClauses.join(", ")}`}function C(s){if(s.rangeFrom!==void 0&&s.rangeTo!==void 0)return` LIMIT ${s.rangeTo-s.rangeFrom+1} OFFSET ${s.rangeFrom}`;if(s.limitValue!==void 0){let e=s.offsetValue?` OFFSET ${s.offsetValue}`:"";return` LIMIT ${s.limitValue}${e}`}return""}function S(s){if(!s)return s;let e={...s};for(let[i,o]of Object.entries(e))if(typeof o=="string"&&(o.startsWith("{")&&o.endsWith("}")||o.startsWith("[")&&o.endsWith("]")))try{e[i]=JSON.parse(o)}catch{}return e}function y(s,e,i,o,p,c,l){let t={eq(r,n){return e.conditions.push({sql:`${r} = ?`,params:[n]}),t},neq(r,n){return e.conditions.push({sql:`${r} != ?`,params:[n]}),t},gt(r,n){return e.conditions.push({sql:`${r} > ?`,params:[n]}),t},gte(r,n){return e.conditions.push({sql:`${r} >= ?`,params:[n]}),t},lt(r,n){return e.conditions.push({sql:`${r} < ?`,params:[n]}),t},lte(r,n){return e.conditions.push({sql:`${r} <= ?`,params:[n]}),t},in(r,n){if(n.length===0)e.conditions.push({sql:"1 = 0",params:[]});else{let a=n.map(()=>"?").join(", ");e.conditions.push({sql:`${r} IN (${a})`,params:n})}return t},is(r,n){return n===null?e.conditions.push({sql:`${r} IS NULL`,params:[]}):e.conditions.push({sql:`${r} = ?`,params:[n?1:0]}),t},or(r){let n=A(r);return e.conditions.push(n),t},not(r,n,a){switch(n){case"eq":e.conditions.push({sql:`${r} != ?`,params:[a]});break;case"is":a===null&&e.conditions.push({sql:`${r} IS NOT NULL`,params:[]});break;default:e.conditions.push({sql:`NOT (${r} ${n} ?)`,params:[a]})}return t},order(r,n){let a=n?.ascending===!1?"DESC":"ASC";return e.orderClauses.push(`${r} ${a}`),t},limit(r){return e.limitValue=r,t},range(r,n){return e.rangeFrom=r,e.rangeTo=n,t},single(){return{then(r,n){try{let a=w(s,e,i,o,p,c,l),u=a.data;if(!u||Array.isArray(u)&&u.length===0){let d=f("Row not found","PGRST116");return Promise.resolve(d).then(r,n)}if(Array.isArray(u)&&u.length>1){let d=f("Multiple rows returned","PGRST116");return Promise.resolve(d).then(r,n)}let m=Array.isArray(u)?u[0]:u;return Promise.resolve(q(m,a.count)).then(r,n)}catch(a){let u=f(a.message);return n?Promise.resolve(u).then(r,n):Promise.resolve(u).then(r)}}}},maybeSingle(){return{then(r,n){try{let a=w(s,e,i,o,p,c,l),u=a.data;if(!u||Array.isArray(u)&&u.length===0)return Promise.resolve(q(null,a.count)).then(r,n);let m=Array.isArray(u)?u[0]:u;return Promise.resolve(q(m,a.count)).then(r,n)}catch(a){let u=f(a.message);return Promise.resolve(u).then(r,n)}}}},then(r,n){try{let a=w(s,e,i,o,p,c,l);return Promise.resolve(a).then(r,n)}catch(a){let u=f(a.message);return Promise.resolve(u).then(r,n)}}};return t}function w(s,e,i,o,p,c,l){let t=O(e),r=B(e),n=C(e);switch(i){case"select":{let a=o||"*";if(p?.head===!0&&p?.count==="exact"){let g=`SELECT COUNT(*) as count FROM ${e.table}${t.sql}`,h=s.prepare(g).get(...t.params);return{data:[],error:null,count:h.count}}let m=`SELECT ${a} FROM ${e.table}${t.sql}${r}${n}`,d=s.prepare(m).all(...t.params);d=d.map(g=>S(g));let T=null;if(p?.count==="exact"){let g=`SELECT COUNT(*) as count FROM ${e.table}${t.sql}`;T=s.prepare(g).get(...t.params).count}return{data:d,error:null,count:T}}case"insert":{let a=Array.isArray(c)?c:[c],u=[];for(let m of a){let d=m,T=Object.keys(d),g=T.map(R=>{let k=d[R];return k!==null&&typeof k=="object"?JSON.stringify(k):k}),h=T.map(()=>"?").join(", "),$=`INSERT INTO ${e.table} (${T.join(", ")}) VALUES (${h})`;s.prepare($).run(...g);let b=s.prepare("SELECT last_insert_rowid() as id").get(),E=s.prepare(`SELECT * FROM ${e.table} WHERE rowid = ?`).get(b.id);E&&u.push(S(E))}return{data:u,error:null}}case"update":{let a=l,u=Object.keys(a),m=u.map($=>`${$} = ?`).join(", "),d=u.map($=>{let b=a[$];return b!==null&&typeof b=="object"?JSON.stringify(b):b}),T=`UPDATE ${e.table} SET ${m}${t.sql}`;s.prepare(T).run(...d,...t.params);let g=`SELECT * FROM ${e.table}${t.sql}${r}${n}`,h=s.prepare(g).all(...t.params);return h=h.map($=>S($)),{data:h,error:null}}case"delete":{let a=`DELETE FROM ${e.table}${t.sql}`;return s.prepare(a).run(...t.params),{data:[],error:null}}}}function P(s,e){let i=e?.rpcHandlers??{};return{from(o){return{select(p,c){return y(s,{table:o,conditions:[],orderClauses:[]},"select",p,c)},insert(p){let c={table:o,conditions:[],orderClauses:[]};return{select(t){return y(s,c,"insert",t,void 0,p)},then(t,r){try{let n=w(s,c,"insert",void 0,void 0,p);return Promise.resolve({data:null,error:n.error}).then(t,r)}catch(n){let a=f(n.message);return Promise.resolve(a).then(t,r)}}}},update(p){let c={table:o,conditions:[],orderClauses:[]},t=y(s,c,"update",void 0,void 0,void 0,p);return t.select=r=>y(s,c,"update",r,void 0,void 0,p),t},delete(){return y(s,{table:o,conditions:[],orderClauses:[]},"delete")}}},rpc(o,p){let c=i[o];if(!c)return Promise.resolve(f(`RPC function '${o}' not registered`));try{let l=c(p??{});return Promise.resolve(q(l))}catch(l){return Promise.resolve(f(l.message))}}}}export{P as createSqliteAdapter};
package/dist/index.d.ts CHANGED
@@ -1,91 +1,2 @@
1
- import Database from 'better-sqlite3';
2
1
 
3
- /**
4
- * DatabaseAdapter Interface (Runtime Copy)
5
- *
6
- * Abstracts the query builder pattern used by Supabase's PostgREST client.
7
- * Both the Supabase adapter (cloud) and SQLite adapter (enterprise runtime)
8
- * implement this interface, allowing all agent services to work with either backend.
9
- *
10
- * Mirrors the SupabaseClient chaining API: .from(table).select().eq().single()
11
- */
12
- interface DbResult<T> {
13
- data: T | null;
14
- error: DbError | null;
15
- count?: number | null;
16
- }
17
- interface DbError {
18
- message: string;
19
- code?: string;
20
- details?: string;
21
- hint?: string;
22
- }
23
- interface FilterBuilder<T> {
24
- eq(column: string, value: unknown): FilterBuilder<T>;
25
- neq(column: string, value: unknown): FilterBuilder<T>;
26
- gt(column: string, value: unknown): FilterBuilder<T>;
27
- gte(column: string, value: unknown): FilterBuilder<T>;
28
- lt(column: string, value: unknown): FilterBuilder<T>;
29
- lte(column: string, value: unknown): FilterBuilder<T>;
30
- in(column: string, values: unknown[]): FilterBuilder<T>;
31
- is(column: string, value: null | boolean): FilterBuilder<T>;
32
- or(filters: string, options?: {
33
- foreignTable?: string;
34
- }): FilterBuilder<T>;
35
- not(column: string, operator: string, value: unknown): FilterBuilder<T>;
36
- order(column: string, options?: {
37
- ascending?: boolean;
38
- }): FilterBuilder<T>;
39
- limit(count: number): FilterBuilder<T>;
40
- range(from: number, to: number): FilterBuilder<T>;
41
- single(): PromiseLike<DbResult<T>>;
42
- maybeSingle(): PromiseLike<DbResult<T | null>>;
43
- then<TResult1 = DbResult<T[]>, TResult2 = never>(onfulfilled?: ((value: DbResult<T[]>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
44
- }
45
- type SelectBuilder<T> = FilterBuilder<T>;
46
- interface InsertBuilder<T> {
47
- select(columns?: string): FilterBuilder<T>;
48
- then<TResult1 = DbResult<null>, TResult2 = never>(onfulfilled?: ((value: DbResult<null>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
49
- }
50
- interface UpdateBuilder<T> extends FilterBuilder<T> {
51
- eq(column: string, value: unknown): UpdateBuilder<T>;
52
- neq(column: string, value: unknown): UpdateBuilder<T>;
53
- select(columns?: string): FilterBuilder<T>;
54
- }
55
- type DeleteBuilder<T> = FilterBuilder<T>;
56
- interface TableBuilder<T = Record<string, unknown>> {
57
- select(columns?: string, options?: {
58
- count?: 'exact';
59
- head?: boolean;
60
- }): SelectBuilder<T>;
61
- insert(data: Partial<T> | Partial<T>[]): InsertBuilder<T>;
62
- update(data: Partial<T>): UpdateBuilder<T>;
63
- delete(): DeleteBuilder<T>;
64
- }
65
- interface DatabaseAdapter {
66
- from<T = Record<string, unknown>>(table: string): TableBuilder<T>;
67
- rpc<T = unknown>(fn: string, params?: Record<string, unknown>): PromiseLike<DbResult<T>>;
68
- }
69
-
70
- /**
71
- * SQLite Adapter
72
- *
73
- * Implements the DatabaseAdapter interface using better-sqlite3.
74
- * Translates the SupabaseClient-shaped query builder API into SQL queries
75
- * against a local SQLite database.
76
- *
77
- * Used by the enterprise runtime for local data plane storage.
78
- */
79
-
80
- /**
81
- * Map of registered RPC functions for the SQLite adapter.
82
- * In Supabase, .rpc() calls server-side Postgres functions.
83
- * In SQLite, we register JS functions that perform the same logic.
84
- */
85
- type RpcHandler = (params: Record<string, unknown>) => unknown;
86
- interface SqliteAdapterOptions {
87
- rpcHandlers?: Record<string, RpcHandler>;
88
- }
89
- declare function createSqliteAdapter(db: Database.Database, options?: SqliteAdapterOptions): DatabaseAdapter;
90
-
91
- export { type SqliteAdapterOptions, createSqliteAdapter };
2
+ export { }