ohwow 0.1.13 → 0.3.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.
- package/LICENSE +17 -17
- package/README.md +202 -138
- package/dist/api.d.ts +91 -0
- package/dist/api.js +2 -0
- package/dist/index.d.ts +1 -90
- package/dist/index.js +1983 -596
- package/dist/mcp-server/index.js +64 -49
- package/dist/migrations/001-data-plane-tables.sql +1 -1
- package/dist/migrations/002-agents-table.sql +1 -1
- package/dist/migrations/017-openclaw-call-logs.sql +15 -0
- package/dist/migrations/038-peer-queue-tracking.sql +5 -0
- package/dist/migrations/039-task-delegation.sql +5 -0
- package/dist/migrations/040-self-improvement-tables.sql +109 -0
- package/dist/migrations/041-session-metadata.sql +3 -0
- package/dist/migrations/042-memory-sync.sql +45 -0
- package/dist/migrations/043-multi-connection.sql +18 -0
- package/dist/migrations/044-multi-connection-fixes.sql +32 -0
- package/dist/migrations/045-task-state.sql +21 -0
- package/dist/migrations/046-state-changelog.sql +18 -0
- package/dist/migrations/047-outbound-queue.sql +9 -0
- package/dist/migrations/048-tasks-column-alignment.sql +37 -0
- package/dist/migrations/049-agents-column-alignment.sql +25 -0
- package/dist/migrations/050-workflows-trigger-alignment.sql +25 -0
- package/dist/migrations/051-execution-engine-tables.sql +132 -0
- package/dist/migrations/052-a2a-rate-limit-alignment.sql +8 -0
- package/dist/migrations/053-llm-cache.sql +20 -0
- package/dist/migrations/054-task-checkpoints.sql +13 -0
- package/dist/migrations/055-resource-usage.sql +12 -0
- package/dist/migrations/056-sandbox-tables.sql +35 -0
- package/dist/migrations/057-persona-soul.sql +20 -0
- package/dist/web/assets/index-DkZG6rzJ.css +1 -0
- package/dist/web/assets/{index-D6DkPUkA.js → index-fXgVWRQU.js} +28 -28
- package/dist/web/index.html +2 -2
- package/package.json +59 -14
- package/dist/web/assets/index-DSojxPLI.css +0 -1
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
MIT License
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Copyright (c) 2026 Jesus David Oñoro Delgado
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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,224 +1,288 @@
|
|
|
1
1
|
# ohwow
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://www.npmjs.com/package/ohwow)
|
|
5
|
+
[](https://github.com/ohwow-fun/ohwow/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/ohwow)
|
|
7
|
+
[](https://discord.gg/WUGnGqceeY)
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
**An AI team that runs on your laptop. Agents that learn your patterns, message your customers, automate your operations, and actually understand what your business needs right now. Free. Open source. Your data stays on your machine.**
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<img src="docs/demo.gif" alt="ohwow demo" width="800">
|
|
13
|
+
</p>
|
|
10
14
|
|
|
11
15
|
```bash
|
|
12
16
|
npm install ohwow -g
|
|
17
|
+
ohwow
|
|
13
18
|
```
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
Running in under 5 minutes. A setup wizard connects to [Ollama](https://ollama.com), picks a model, and builds an AI team for your business type.
|
|
16
21
|
|
|
17
|
-
|
|
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
|
+
---
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
## What a day looks like with ohwow
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
ohwow
|
|
27
|
-
```
|
|
26
|
+
**7:45 AM** — You open your laptop. ohwow noticed you always start around this time. Your morning briefing is ready: "3 tasks completed overnight. 1 needs your eye (a customer contract with unusual terms). 2 leads replied to yesterday's outreach. Your Sales Agent's follow-up is queued for 9am."
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
**8:00 AM** — You say: "What should I focus on today?" ohwow knows you're at growth stage 2 (Attract). It responds: "You're over-indexing on content creation this week. Your growth stage needs more outreach. Your Sales Agent has 3 warm leads that haven't been contacted. Want me to draft follow-ups?"
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
**8:15 AM** — You approve the follow-ups. The Sales Agent sends personalized WhatsApp messages to each lead, referencing their specific context from your CRM. No templates. Each message reads like you wrote it.
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
**10:30 AM** — You're deep in product work. A customer emails with a support question. ohwow detects it, routes it to the Support Agent, which drafts a response. Because your cognitive load is high (3 open approvals, deep work mode), it batches the notification instead of interrupting. You'll see it at your next natural break.
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
**2:00 PM** — ohwow notices your energy dips after lunch (it learned this from 30 days of watching your activity patterns). It holds the complex research report that just completed, knowing you'll evaluate it better in the morning. Instead, it surfaces a simple approval that takes 10 seconds.
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
**4:00 PM** — A lead responds positively on WhatsApp. ohwow's Sales Agent wants to send a pricing proposal. It could auto-send (it's earned that trust after 47 successful outreach tasks with 94% approval), but the amount is above the auto-approve threshold. You get a one-line notification: "Ready to send pricing to Acme Corp. $4,500/mo proposal. Approve?"
|
|
38
37
|
|
|
39
|
-
|
|
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
|
|
38
|
+
**6:15 PM** — You close your laptop. ohwow stops all non-critical notifications. Your agents keep working: the Content Writer drafts tomorrow's blog post, the Researcher compiles a competitive analysis you asked for this morning. Results will be waiting when you open up tomorrow.
|
|
48
39
|
|
|
49
|
-
|
|
40
|
+
**End of day**: 12 tasks completed across 4 agents. 3 leads contacted. 1 support ticket resolved. 1 blog draft ready. You spent 20 minutes on approvals. The rest was your work.
|
|
50
41
|
|
|
51
|
-
|
|
42
|
+
---
|
|
52
43
|
|
|
53
|
-
|
|
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 |
|
|
44
|
+
## How it actually works
|
|
61
45
|
|
|
62
|
-
|
|
46
|
+
### Talk to it like a person
|
|
63
47
|
|
|
64
|
-
The
|
|
48
|
+
The orchestrator understands natural language with 150+ tools behind it:
|
|
65
49
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
50
|
+
| What you say | What happens |
|
|
51
|
+
|---|---|
|
|
52
|
+
| "Follow up with everyone who opened my last email" | Finds contacts with email opens, drafts personalized follow-ups, sends via WhatsApp or email |
|
|
53
|
+
| "Research our top 5 competitors and create a comparison" | Launches the Research Agent with deep web search, compiles findings into a structured report |
|
|
54
|
+
| "Schedule outreach every weekday at 9am" | Creates a recurring schedule. The agent picks leads from your CRM, personalizes messages, and sends them on time |
|
|
55
|
+
| "Open Figma and export the hero banner as PNG" | Takes over your desktop, opens the app, navigates to the file, exports it, saves to your folder |
|
|
56
|
+
| "How's the business doing?" | Shows your eudaimonia score (workspace flourishing), task stats, lead pipeline, agent efficiency, and growth stage guidance |
|
|
57
|
+
| "Create a project for the website redesign" | Creates a Kanban board, suggests task breakdown, assigns agents to each phase |
|
|
58
|
+
| "Send a WhatsApp to the team: launching Friday" | Sends through your connected WhatsApp. No API needed. Uses your actual WhatsApp number |
|
|
59
|
+
| "What should I focus on this week?" | Analyzes your growth stage, current work balance, goal velocity, and team capacity. Gives specific, actionable recommendations |
|
|
74
60
|
|
|
75
|
-
|
|
61
|
+
### Your agents learn and improve
|
|
76
62
|
|
|
77
|
-
|
|
63
|
+
ohwow agents aren't stateless. They remember what worked and what didn't:
|
|
78
64
|
|
|
79
|
-
|
|
65
|
+
- After the Content Writer produces 50 blog posts, it knows your brand voice, preferred structure, and which topics get approved immediately vs revised
|
|
66
|
+
- After the Sales Agent sends 200 outreach messages, it knows which opening lines get responses and which get ignored
|
|
67
|
+
- After the Researcher completes 30 deep dives, it knows which sources you trust and how detailed you want the analysis
|
|
80
68
|
|
|
81
|
-
|
|
69
|
+
This isn't "fine-tuning." It's persistent memory, skill synthesis, and pattern mining that builds real expertise over time. Each agent develops its own character: some become meticulous researchers, others become fast-moving closers.
|
|
82
70
|
|
|
83
|
-
|
|
71
|
+
### It knows your business stage
|
|
84
72
|
|
|
85
|
-
|
|
73
|
+
A startup just launching needs different work than an established company scaling. ohwow knows the difference:
|
|
86
74
|
|
|
87
|
-
|
|
|
88
|
-
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
| "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 |
|
|
75
|
+
| Growth Stage | Revenue | What ohwow prioritizes |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| **Launch** | $0-$1K | Selling. Not building more features. Not researching competitors. Getting first customers |
|
|
78
|
+
| **Attract** | $1K-$10K | Lead flow. Content that brings people in. One channel, mastered |
|
|
79
|
+
| **Systemize** | $10K-$25K | Automating what works. SOPs. Reducing founder dependency |
|
|
80
|
+
| **Focus** | $25K-$50K | Cutting the bottom 20%. Serving ideal customers. Unit economics |
|
|
81
|
+
| **Expand** | $50K-$100K | Second product or market. Validated before scaled |
|
|
82
|
+
| **Structure** | $250K-$500K | Delegation. The founder stops doing execution work |
|
|
97
83
|
|
|
98
|
-
|
|
84
|
+
When you ask "what should I focus on?", the answer comes from practical wisdom about your specific stage, not generic advice.
|
|
99
85
|
|
|
100
|
-
|
|
86
|
+
### It respects that you're human
|
|
101
87
|
|
|
102
|
-
|
|
88
|
+
Most productivity tools treat you like a machine that processes tasks. ohwow doesn't.
|
|
103
89
|
|
|
104
|
-
|
|
90
|
+
- **It learns your rhythm.** Morning person? Complex approvals arrive at 9am. Afternoon slump? Only quick tasks surface after 2pm.
|
|
91
|
+
- **It detects overload.** When you have too many open decisions, it stops adding work and starts batching, prioritizing, and filtering.
|
|
92
|
+
- **It respects boundaries.** If you always stop responding at 6pm, ohwow stops notifying. No setting required. It learns by watching.
|
|
93
|
+
- **It suggests rest.** After a week of sprinting, it shifts work toward reflective tasks and away from high-pressure action items.
|
|
105
94
|
|
|
106
|
-
|
|
95
|
+
---
|
|
107
96
|
|
|
108
|
-
|
|
97
|
+
## Real scenarios
|
|
109
98
|
|
|
110
|
-
###
|
|
99
|
+
### For a solo e-commerce founder
|
|
111
100
|
|
|
112
|
-
|
|
101
|
+
You sell handmade candles on Shopify. You spend 3 hours a day on customer emails, social media, and supplier follow-ups.
|
|
113
102
|
|
|
114
|
-
|
|
103
|
+
With ohwow: the Support Agent handles customer questions via WhatsApp (you approve the first few, then trust builds and it handles most autonomously). The Content Writer creates daily Instagram captions and weekly blog posts about your craft. The Sales Agent follows up with wholesale leads from your last trade show. You focus on making candles.
|
|
115
104
|
|
|
116
|
-
|
|
105
|
+
### For a SaaS founder with a small team
|
|
117
106
|
|
|
118
|
-
|
|
119
|
-
- **Text-to-Speech**: VoiceboxTTS (local), Piper (local), or OpenAI TTS (cloud fallback)
|
|
107
|
+
You and 2 engineers are building a developer tool. Marketing, support, and sales fall on you.
|
|
120
108
|
|
|
121
|
-
|
|
109
|
+
With ohwow: agents handle lead outreach, support ticket triage, competitive research, and content creation. The system knows you're at stage 2 (Attract) and pushes outreach over feature building. When a lead responds on WhatsApp, the Sales Agent drafts a response referencing their specific use case from your CRM. Your team reviews agent output in the dashboard. Trust builds per domain: the Content Writer runs autonomously, the Sales Agent still needs approval for pricing discussions.
|
|
122
110
|
|
|
123
|
-
|
|
111
|
+
### For an agency managing multiple clients
|
|
124
112
|
|
|
125
|
-
|
|
113
|
+
You run a marketing agency with 10 clients. Each needs weekly reports, social posts, and campaign analysis.
|
|
126
114
|
|
|
127
|
-
|
|
115
|
+
With ohwow: each client gets dedicated agents. The Researcher pulls analytics. The Content Writer drafts social posts in each client's brand voice. The Reporter compiles weekly performance reports. Agents learn each client's preferences separately. Desktop automation exports graphics from Canva, screenshots from analytics dashboards. Multi-device mesh lets your office Mac Mini run overnight while you review results on your MacBook in the morning.
|
|
128
116
|
|
|
129
|
-
|
|
117
|
+
### For a privacy-focused business
|
|
130
118
|
|
|
131
|
-
|
|
119
|
+
You handle sensitive customer data and can't use cloud AI platforms.
|
|
132
120
|
|
|
133
|
-
|
|
121
|
+
With ohwow: everything runs locally with Ollama. No data leaves your machine. No API calls to external LLMs if you don't want them. Local SQLite database. Local model inference. WhatsApp messages stay on your device. You get full AI automation with zero cloud dependency.
|
|
134
122
|
|
|
135
|
-
|
|
123
|
+
---
|
|
136
124
|
|
|
137
|
-
|
|
125
|
+
## Features
|
|
138
126
|
|
|
139
|
-
|
|
127
|
+
| Category | What you get |
|
|
128
|
+
|---|---|
|
|
129
|
+
| **AI Agents** | 48 pre-built agents across 6 business types. Persistent memory. Tool mastery. Real expertise over time |
|
|
130
|
+
| **Orchestrator** | 150+ tools. Natural language interface. Growth-stage-aware recommendations |
|
|
131
|
+
| **Desktop Control** | Full macOS automation: mouse, keyboard, screen capture. Your agents operate any app on your computer |
|
|
132
|
+
| **Messaging** | WhatsApp + Telegram built in. Uses your real number. Auto-routing to the right agent |
|
|
133
|
+
| **Browser** | Local Chromium automation: navigate, click, fill forms, screenshot, extract data |
|
|
134
|
+
| **CRM** | Contacts, pipeline, events, analytics. All stored locally. Agents use it to personalize outreach |
|
|
135
|
+
| **Scheduling** | Cron schedules + smart timing based on opportunity detection |
|
|
136
|
+
| **Workflows** | Multi-step automation graphs. Parallel execution. Conditions and branching |
|
|
137
|
+
| **Multi-device** | Zero-config mesh across your machines. Tasks route to the best device. Self-healing if one goes down |
|
|
138
|
+
| **Physical I/O** | Connect Arduino, ESP32, sensors, actuators. Your office becomes smart |
|
|
139
|
+
| **MCP** | External tool integration via Model Context Protocol |
|
|
140
|
+
| **A2A** | Agent-to-Agent protocol for connecting with other AI systems |
|
|
141
|
+
| **Cognitive Architecture** | 7-layer philosophical system powering everything under the hood |
|
|
142
|
+
|
|
143
|
+
## Use with Claude Code
|
|
144
|
+
|
|
145
|
+
ohwow works as a Claude Code plugin. 25 tools become available in your Claude conversations.
|
|
140
146
|
|
|
141
|
-
|
|
147
|
+
```bash
|
|
148
|
+
claude plugin install ohwow-fun/ohwow
|
|
149
|
+
```
|
|
142
150
|
|
|
143
|
-
|
|
151
|
+
Or add to any project's `.mcp.json`:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"mcpServers": {
|
|
156
|
+
"ohwow": {
|
|
157
|
+
"command": "npx",
|
|
158
|
+
"args": ["-y", "ohwow", "mcp-server"]
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
144
163
|
|
|
145
|
-
|
|
164
|
+
**Example**: "Research the top 5 competitors in our space, add the findings to the knowledge base, then create a contact for each CEO." Claude uses ohwow's research, knowledge, and CRM tools seamlessly.
|
|
146
165
|
|
|
147
|
-
|
|
166
|
+
## Desktop Control
|
|
148
167
|
|
|
149
|
-
|
|
168
|
+
Your agents aren't trapped in a chat window. They operate your computer.
|
|
150
169
|
|
|
151
|
-
|
|
170
|
+
```
|
|
171
|
+
You: "Fill out the Q1 expense report in Google Sheets using last month's receipts folder"
|
|
172
|
+
|
|
173
|
+
ohwow:
|
|
174
|
+
1. Opens Finder, navigates to receipts
|
|
175
|
+
2. Opens Google Sheets in Chrome
|
|
176
|
+
3. Reads each receipt, types amounts into the right cells
|
|
177
|
+
4. Double-checks totals
|
|
178
|
+
5. Done. You were making coffee the whole time.
|
|
179
|
+
```
|
|
152
180
|
|
|
153
|
-
|
|
181
|
+
The agent takes a screenshot, analyzes it with a vision model, decides the next action, executes it, then screenshots again to see the result. This perception/reasoning/action loop repeats until the task is done.
|
|
154
182
|
|
|
155
|
-
|
|
183
|
+
**Safety first.** Desktop control requires explicit permission. Emergency stop halts everything instantly. Dangerous actions (Terminal, system settings) trigger additional approval.
|
|
156
184
|
|
|
157
|
-
|
|
185
|
+
## Multi-device Mesh
|
|
158
186
|
|
|
159
|
-
|
|
187
|
+
```
|
|
188
|
+
Mac Mini (home office) MacBook (on the go) Raspberry Pi (warehouse)
|
|
189
|
+
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
|
|
190
|
+
│ WhatsApp Agent │◄─mesh──►│ Research Agent │◄─mesh──►│ Temp Sensor │
|
|
191
|
+
│ Desktop Tasks │ │ Browser Auto │ │ Motion Detect │
|
|
192
|
+
│ Overnight Work │ │ Mobile Review │ │ Inventory Scan │
|
|
193
|
+
└────────────────┘ └────────────────┘ └────────────────┘
|
|
194
|
+
```
|
|
160
195
|
|
|
161
|
-
|
|
196
|
+
Zero configuration. Devices discover each other on your network. Tasks go to the best device for the job. If your Mac Mini discovers that a lead's website is down, your MacBook knows instantly. If one device goes offline, another takes over its responsibilities.
|
|
162
197
|
|
|
163
|
-
|
|
198
|
+
## Why ohwow?
|
|
164
199
|
|
|
165
|
-
|
|
200
|
+
| | Zapier | n8n | Make | **ohwow** |
|
|
201
|
+
|---|---|---|---|---|
|
|
202
|
+
| Runs on your machine | No | Self-host option | No | **Yes, always** |
|
|
203
|
+
| Desktop control | No | No | No | **Yes, with safety guards** |
|
|
204
|
+
| AI agents with real memory | No | Partial | No | **Yes, agents build expertise** |
|
|
205
|
+
| Cost to start | $20+/mo | Free (self-host) | $10+/mo | **$0 with Ollama** |
|
|
206
|
+
| Pre-built business agents | No | No | No | **48 agents, 6 business types** |
|
|
207
|
+
| WhatsApp + Telegram | Plugin | Plugin | Plugin | **Built in** |
|
|
208
|
+
| CRM + contacts | No | No | No | **Built in** |
|
|
209
|
+
| Multi-device mesh | No | No | No | **Zero-config** |
|
|
210
|
+
| Knows your growth stage | No | No | No | **Yes, adapts priorities** |
|
|
211
|
+
| Respects your energy/stress | No | No | No | **Yes, bio-aware** |
|
|
212
|
+
| Your data stays local | No | Optional | No | **Always** |
|
|
166
213
|
|
|
167
|
-
##
|
|
214
|
+
## Cloud (optional)
|
|
168
215
|
|
|
169
|
-
Connect to ohwow.fun
|
|
216
|
+
Connect to [ohwow.fun](https://ohwow.fun) for cloud features on top of the free local runtime:
|
|
170
217
|
|
|
171
|
-
**
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
-
|
|
218
|
+
- **Phone dispatch**: assign tasks to your computer from your phone
|
|
219
|
+
- **OAuth integrations**: Gmail, Slack, and more
|
|
220
|
+
- **AI site generator**: with hosting and custom domains
|
|
221
|
+
- **Fleet dashboard**: manage all your devices from one place
|
|
222
|
+
- **Collective intelligence**: visualize what your agents have learned
|
|
223
|
+
- **Work ontology dashboard**: eudaimonia score, growth stage guidance
|
|
177
224
|
|
|
178
|
-
|
|
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
|
|
225
|
+
All local features work without cloud. See [pricing](https://ohwow.fun/pricing) for plans starting at $29/mo.
|
|
185
226
|
|
|
186
|
-
|
|
227
|
+
## Under the Hood
|
|
187
228
|
|
|
188
|
-
|
|
229
|
+
For those who want to understand the architecture: ohwow is built on a seven-layer philosophical cognitive architecture. Each layer addresses a distinct dimension of intelligence, grounded in philosophy from Aristotle to modern cognitive science.
|
|
189
230
|
|
|
190
|
-
|
|
231
|
+
```
|
|
232
|
+
Layer 7: BIOS — Respects human biology (energy, stress, boundaries)
|
|
233
|
+
Layer 6: Symbiosis — Models human-AI collaboration (trust, handoffs, mutual learning)
|
|
234
|
+
Layer 5: Soul — Tracks identity for agents and humans (values, blind spots, growth)
|
|
235
|
+
Persona — Real-time behavioral profiling (circadian, load, communication style)
|
|
236
|
+
Layer 4: Mesh — Distributed consciousness across devices (propagation, shared body, self-healing)
|
|
237
|
+
Layer 3: Work — Purpose-driven execution (growth stage wisdom, work classification, flourishing)
|
|
238
|
+
Layer 2: Body — Physical + digital embodiment (organs, nervous systems, affordances, PID controllers)
|
|
239
|
+
Layer 1: Brain — Cognitive cycle: perceive → deliberate → act (prediction, reflection, dialectic)
|
|
240
|
+
Layer 0: Runtime — Orchestrator, engine, 150+ tools, DB, API, scheduling, messaging
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
115+ modules. 28,000+ lines. Every philosophical concept is load-bearing architecture: remove any one and the system degrades measurably. See [ARCHITECTURE.md](./ARCHITECTURE.md) for the full technical breakdown.
|
|
191
244
|
|
|
192
|
-
##
|
|
245
|
+
## Self-hosting
|
|
246
|
+
|
|
247
|
+
| Command | What it does |
|
|
248
|
+
|---|---|
|
|
249
|
+
| `ohwow` | Start the TUI (default) |
|
|
250
|
+
| `ohwow --daemon` | Start daemon in foreground (for systemd/launchd/Docker) |
|
|
251
|
+
| `ohwow stop` | Stop the daemon |
|
|
252
|
+
| `ohwow status` | Check daemon status |
|
|
253
|
+
| `ohwow logs` | Tail daemon logs |
|
|
193
254
|
|
|
194
|
-
|
|
255
|
+
Docker:
|
|
195
256
|
|
|
196
257
|
```bash
|
|
197
|
-
ohwow
|
|
258
|
+
docker run -d --name ohwow -p 7700:7700 -v ~/.ohwow:/root/.ohwow ohwow
|
|
198
259
|
```
|
|
199
260
|
|
|
200
|
-
|
|
261
|
+
## Requirements
|
|
201
262
|
|
|
202
|
-
|
|
263
|
+
- Node.js 20+
|
|
264
|
+
- [Ollama](https://ollama.com) for local models
|
|
265
|
+
- Optional: Anthropic API key (for Claude models)
|
|
266
|
+
- Optional: Playwright browsers (`npx playwright install chromium`) for browser automation
|
|
267
|
+
- Optional: macOS Accessibility permission for desktop control
|
|
203
268
|
|
|
204
|
-
|
|
269
|
+
## Troubleshooting
|
|
205
270
|
|
|
206
|
-
|
|
|
207
|
-
|
|
208
|
-
| `
|
|
209
|
-
|
|
|
210
|
-
|
|
|
211
|
-
|
|
|
271
|
+
| Problem | Solution |
|
|
272
|
+
|---------|----------|
|
|
273
|
+
| `better-sqlite3` build fails | Install build tools: `xcode-select --install` (macOS), `sudo apt install build-essential python3` (Linux) |
|
|
274
|
+
| Ollama not detected | Ensure Ollama is running (`ollama serve`) and accessible at `http://localhost:11434` |
|
|
275
|
+
| Port 7700 in use | Set `OHWOW_PORT=7701` or any free port |
|
|
276
|
+
| WhatsApp QR expired | Restart ohwow and scan the new QR within 60 seconds |
|
|
212
277
|
|
|
213
|
-
##
|
|
278
|
+
## Community
|
|
214
279
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
| Any Ollama model | Local |
|
|
280
|
+
- [Discord](https://discord.gg/WUGnGqceeY)
|
|
281
|
+
- [Contributing](./CONTRIBUTING.md)
|
|
282
|
+
- [Architecture](./ARCHITECTURE.md)
|
|
283
|
+
- [Security](./SECURITY.md)
|
|
284
|
+
- Email: ogsus@ohwow.fun
|
|
221
285
|
|
|
222
286
|
## License
|
|
223
287
|
|
|
224
|
-
|
|
288
|
+
[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};
|