zubo 0.1.3 → 0.1.5

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/.dockerignore ADDED
@@ -0,0 +1,12 @@
1
+ node_modules
2
+ dist
3
+ desktop
4
+ site
5
+ .env
6
+ .env.*
7
+ .zubo
8
+ .git
9
+ .DS_Store
10
+ *.db
11
+ *.db-wal
12
+ *.db-shm
package/Dockerfile ADDED
@@ -0,0 +1,24 @@
1
+ # Zubo — Dockerfile
2
+ # Runs the Zubo AI agent using the official Bun runtime image.
3
+ # Persistent data (config, database, memory, skills) lives in /root/.zubo
4
+ # and should be mounted as a volume.
5
+
6
+ FROM oven/bun:1
7
+
8
+ WORKDIR /app
9
+
10
+ # Install dependencies first (layer cache optimisation)
11
+ COPY package.json bun.lock ./
12
+ RUN bun install --frozen-lockfile
13
+
14
+ # Copy the rest of the project
15
+ COPY . .
16
+
17
+ # Webchat dashboard port
18
+ EXPOSE 8787
19
+
20
+ # Persistent storage mount point — config.json, zubo.db, workspace, logs, etc.
21
+ VOLUME /root/.zubo
22
+
23
+ # Start the agent
24
+ CMD ["bun", "run", "src/index.ts", "start"]
@@ -0,0 +1,20 @@
1
+ # Zubo — Docker Compose
2
+ # Usage:
3
+ # 1. Copy .env.example to .env and fill in your API keys
4
+ # 2. docker compose up -d
5
+ # 3. Open http://localhost:8787 for the webchat dashboard
6
+
7
+ services:
8
+ zubo:
9
+ build: .
10
+ container_name: zubo
11
+ restart: unless-stopped
12
+
13
+ ports:
14
+ - "8787:8787" # Webchat dashboard
15
+
16
+ volumes:
17
+ - ~/.zubo:/root/.zubo # Persistent data (config, DB, memory, skills, logs)
18
+
19
+ env_file:
20
+ - .env # API keys: ANTHROPIC_API_KEY, OPENAI_API_KEY, TELEGRAM_BOT_TOKEN, etc.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zubo",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Your AI agent that never forgets. Persistent memory, 20+ tools, 6 channels, 11+ LLM providers — runs entirely on your machine.",
5
5
  "license": "MIT",
6
6
  "author": "thomaskanze",
@@ -4,18 +4,18 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Getting Started — Zubo Docs</title>
7
- <meta name="description" content="Get started with Zubo, a self-hosted AI agent with persistent semantic memory, multi-channel presence, 35+ tools, sub-agent delegation, and workflow automation.">
7
+ <meta name="description" content="Get started with Zubo, a self-hosted AI agent with persistent semantic memory, multi-channel presence, 20+ tools, sub-agent delegation, and workflow automation.">
8
8
  <meta name="theme-color" content="#060608">
9
9
  <link rel="canonical" href="https://zubo.bot/docs/">
10
10
  <meta property="og:title" content="Getting Started — Zubo Docs">
11
- <meta property="og:description" content="Get started with Zubo, a self-hosted AI agent with persistent semantic memory, multi-channel presence, 35+ tools, sub-agent delegation, and workflow automation.">
11
+ <meta property="og:description" content="Get started with Zubo, a self-hosted AI agent with persistent semantic memory, multi-channel presence, 20+ tools, sub-agent delegation, and workflow automation.">
12
12
  <meta property="og:type" content="article">
13
13
  <meta property="og:url" content="https://zubo.bot/docs/">
14
14
  <meta property="og:image" content="https://zubo.bot/og-image.png">
15
15
  <meta property="og:site_name" content="Zubo">
16
16
  <meta name="twitter:card" content="summary_large_image">
17
17
  <meta name="twitter:title" content="Getting Started — Zubo Docs">
18
- <meta name="twitter:description" content="Get started with Zubo, a self-hosted AI agent with persistent semantic memory, 35+ tools, and workflow automation.">
18
+ <meta name="twitter:description" content="Get started with Zubo, a self-hosted AI agent with persistent semantic memory, 20+ tools, and workflow automation.">
19
19
  <meta name="twitter:image" content="https://zubo.bot/og-image.png">
20
20
  <meta name="twitter:creator" content="@thomaskanze">
21
21
  <link rel="preconnect" href="https://fonts.googleapis.com">
@@ -87,7 +87,7 @@
87
87
  <h1>Getting Started with Zubo</h1>
88
88
 
89
89
  <p>
90
- Zubo is a self-hosted AI agent with persistent semantic memory, multi-channel presence across Telegram, Discord, Slack, WhatsApp, Signal, and Web Chat, over 35 smart built-in tools, sub-agent delegation, and workflow automation. It runs entirely on your machine &mdash; your data never leaves your infrastructure. Whether you need a personal assistant that remembers everything, an automation hub that connects your services, or an AI copilot embedded in every chat platform you use, Zubo handles it from a single process with a single configuration file.
90
+ Zubo is a self-hosted AI agent with persistent semantic memory, multi-channel presence across Telegram, Discord, Slack, WhatsApp, Signal, and Web Chat, 20+ smart built-in tools, sub-agent delegation, and workflow automation. It runs entirely on your machine &mdash; your data never leaves your infrastructure. Whether you need a personal assistant that remembers everything, an automation hub that connects your services, or an AI copilot embedded in every chat platform you use, Zubo handles it from a single process with a single configuration file.
91
91
  </p>
92
92
 
93
93
  <!-- ================================================================ -->
@@ -125,7 +125,7 @@
125
125
 
126
126
  <h3>Tools</h3>
127
127
  <ul>
128
- <li><strong>35+ built-in tools</strong> including shell command execution, file I/O (read, write, list, search), web search (DuckDuckGo, Brave, Google), HTTP requests, memory read/write/search, secrets management, and cron job scheduling.</li>
128
+ <li><strong>20+ built-in tools</strong> including shell command execution, file I/O (read, write, list, search), web search (DuckDuckGo, Brave, Google), HTTP requests, memory read/write/search, secrets management, and cron job scheduling.</li>
129
129
  <li>Tools are permission-gated with three levels: <code>safe</code>, <code>sensitive</code>, and <code>dangerous</code>. Dangerous operations require explicit confirmation tokens.</li>
130
130
  </ul>
131
131
 
@@ -200,21 +200,69 @@
200
200
  <!-- ================================================================ -->
201
201
  <h2 id="installation">Installation</h2>
202
202
 
203
+ <p>Choose the installation method that fits your platform.</p>
204
+
205
+ <h3 id="install-mac-linux">macOS &amp; Linux</h3>
206
+
203
207
  <p>Zubo requires <strong>Bun v1.0+</strong> as its runtime. If you do not have Bun installed, visit <a href="https://bun.sh" target="_blank" rel="noopener">bun.sh</a> and run the one-line installer.</p>
204
208
 
205
- <h3>Step 1: Install Zubo</h3>
209
+ <p><strong>One-liner (recommended):</strong></p>
210
+
211
+ <pre><code>curl -fsSL https://zubo.bot/install.sh | bash</code></pre>
206
212
 
207
- <p>Install Zubo globally using Bun (recommended) or npm:</p>
213
+ <p>This installs Bun (if needed), installs Zubo globally, and runs the setup wizard automatically.</p>
214
+
215
+ <p><strong>Manual install:</strong></p>
208
216
 
209
217
  <pre><code># Using Bun (recommended)
210
218
  bun add -g zubo
211
219
 
212
220
  # Or using npm
213
- npm i -g zubo</code></pre>
221
+ npm i -g zubo
222
+
223
+ # Then run the setup wizard
224
+ zubo setup</code></pre>
225
+
226
+ <h3 id="install-windows">Windows (via WSL)</h3>
227
+
228
+ <p>Zubo runs on Windows through the Windows Subsystem for Linux (WSL). This gives you full compatibility with zero workarounds.</p>
229
+
230
+ <p><strong>Step 1:</strong> Install WSL (requires Windows 10 version 2004+ or Windows 11):</p>
231
+
232
+ <pre><code>wsl --install</code></pre>
233
+
234
+ <p>Restart your computer when prompted. This installs Ubuntu by default.</p>
235
+
236
+ <p><strong>Step 2:</strong> Open the Ubuntu terminal from the Start Menu and install Zubo:</p>
237
+
238
+ <pre><code>curl -fsSL https://zubo.bot/install.sh | bash</code></pre>
239
+
240
+ <p>That's it. Zubo runs inside WSL with full access to all features. The web dashboard is accessible from your Windows browser at <code>http://localhost:3000</code>.</p>
241
+
242
+ <h3 id="install-docker">Docker</h3>
243
+
244
+ <p>Run Zubo as a container with no local dependencies.</p>
245
+
246
+ <pre><code># Clone the repo
247
+ git clone https://github.com/apwn/zubo.git &amp;&amp; cd zubo
248
+
249
+ # Copy the example env and add your API key
250
+ cp .env.example .env
251
+
252
+ # Start Zubo
253
+ docker compose up -d</code></pre>
254
+
255
+ <p>The container exposes port <code>8787</code> for the web dashboard and mounts <code>~/.zubo</code> for persistent data. Stop with <code>docker compose down</code>.</p>
256
+
257
+ <p>To configure, edit <code>.env</code> or mount your own <code>config.json</code>:</p>
258
+
259
+ <pre><code>volumes:
260
+ - ~/.zubo:/root/.zubo
261
+ - ./my-config.json:/root/.zubo/config.json</code></pre>
214
262
 
215
- <p>This places the <code>zubo</code> binary on your PATH, making it available from any terminal session.</p>
263
+ <h3 id="setup-wizard">The Setup Wizard</h3>
216
264
 
217
- <h3>Step 2: Run the Setup Wizard</h3>
265
+ <p>After installing (on any platform), run the setup wizard:</p>
218
266
 
219
267
  <pre><code>zubo setup</code></pre>
220
268
 
@@ -228,7 +276,7 @@ npm i -g zubo</code></pre>
228
276
  <li>Create a default <code>SYSTEM.md</code> system prompt that you can customize later.</li>
229
277
  </ul>
230
278
 
231
- <h3>Step 3: Start Zubo</h3>
279
+ <h3 id="starting">Starting Zubo</h3>
232
280
 
233
281
  <pre><code># Start in foreground (logs to stdout)
234
282
  zubo start
package/site/index.html CHANGED
@@ -3,19 +3,19 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Zubo — The AI Agent That Never Forgets</title>
7
- <meta name="description" content="Zubo is a self-hosted AI agent with persistent memory, multi-channel presence, smart tools, and sub-agent delegation. Open source. Runs locally. No cloud required.">
6
+ <title>Zubo — Your AI Agent. Your Machine. Your Rules.</title>
7
+ <meta name="description" content="Open-source AI agent that runs on your machine. Persistent memory, 20+ tools, 6 messaging channels, 11+ LLM providers. One install, works everywhere.">
8
8
  <meta name="theme-color" content="#060608">
9
9
  <link rel="canonical" href="https://zubo.bot/">
10
- <meta property="og:title" content="Zubo — The AI Agent That Never Forgets">
11
- <meta property="og:description" content="Self-hosted AI agent with persistent memory, multi-channel presence, and 20+ smart tools. Open source, runs locally.">
10
+ <meta property="og:title" content="Zubo — Your AI Agent. Your Machine. Your Rules.">
11
+ <meta property="og:description" content="Open-source AI agent that runs on your machine. Persistent memory, 20+ tools, works across Telegram, Discord, Slack, WhatsApp, and more. One install, zero cloud.">
12
12
  <meta property="og:type" content="website">
13
13
  <meta property="og:url" content="https://zubo.bot/">
14
14
  <meta property="og:image" content="https://zubo.bot/og-image.png">
15
15
  <meta property="og:site_name" content="Zubo">
16
16
  <meta name="twitter:card" content="summary_large_image">
17
- <meta name="twitter:title" content="Zubo — The AI Agent That Never Forgets">
18
- <meta name="twitter:description" content="Self-hosted AI agent with persistent memory, multi-channel presence, and 20+ smart tools. Open source, runs locally.">
17
+ <meta name="twitter:title" content="Zubo — Your AI Agent. Your Machine. Your Rules.">
18
+ <meta name="twitter:description" content="Open-source AI agent that runs on your machine. Persistent memory, 20+ tools, works across Telegram, Discord, Slack, WhatsApp, and more. One install, zero cloud.">
19
19
  <meta name="twitter:image" content="https://zubo.bot/og-image.png">
20
20
  <meta name="twitter:creator" content="@thomaskanze">
21
21
  <link rel="preconnect" href="https://fonts.googleapis.com">
@@ -74,12 +74,12 @@
74
74
 
75
75
  <h1 class="hero-title">
76
76
  <span class="hero-line">Your AI Agent.</span>
77
- <span class="hero-line"><span class="gradient-text">Infinite Memory.</span></span>
77
+ <span class="hero-line"><span class="gradient-text">Your Machine.</span></span>
78
78
  </h1>
79
79
 
80
80
  <p class="hero-subtitle">
81
- Zubo runs on your machine with persistent semantic memory, multi-channel presence,
82
- 20+ smart tools, and sub-agent delegation. No cloud. No compromise. No forgetting.
81
+ Zubo runs on your machine, remembers everything you tell it, works across all your
82
+ messaging apps, and connects to your favorite tools. No cloud. No subscriptions. No forgetting.
83
83
  </p>
84
84
 
85
85
  <div class="hero-actions">
@@ -231,8 +231,8 @@
231
231
  <div class="container">
232
232
  <div class="section-header reveal">
233
233
  <span class="section-badge">Capabilities</span>
234
- <h2 class="section-title">Everything you need.<br><span class="gradient-text">Nothing you don't.</span></h2>
235
- <p class="section-subtitle">Zubo ships with powerful features out of the box &mdash; memory, tools, scheduling, multi-channel support, and more. No plugins needed to get started.</p>
234
+ <h2 class="section-title">One agent.<br><span class="gradient-text">Endless capabilities.</span></h2>
235
+ <p class="section-subtitle">Memory, tools, scheduling, multi-channel support &mdash; everything works out of the box. No plugins required.</p>
236
236
  </div>
237
237
 
238
238
  <div class="bento reveal-stagger">
@@ -272,7 +272,7 @@
272
272
  <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/><circle cx="12" cy="16" r="1"/></svg>
273
273
  </div>
274
274
  <h3>100% Private</h3>
275
- <p>Runs entirely on your machine. SQLite database, local vector store. Your conversations never leave your hardware. Set spending limits and track costs per model &mdash; full transparency, zero surprises.</p>
275
+ <p>Runs entirely on your machine. SQLite database, local vector store. Your conversations never leave your hardware. Your data stays yours.</p>
276
276
  </div>
277
277
 
278
278
  <!-- Row 2 -->
@@ -280,8 +280,8 @@
280
280
  <div class="bento-icon">
281
281
  <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
282
282
  </div>
283
- <h3>Always Watching</h3>
284
- <p>Schedule tasks in plain English &mdash; 'every weekday at 9am' just works. Cron jobs, proactive triggers, and automated workflows. Zubo monitors, alerts, and acts on your behalf &mdash; even when you're away.</p>
283
+ <h3>Runs on Autopilot</h3>
284
+ <p>Schedule tasks in plain English &mdash; &ldquo;every weekday at 9am&rdquo; just works. Zubo monitors, alerts, and acts on your behalf &mdash; even when you're away.</p>
285
285
  </div>
286
286
 
287
287
  <div class="bento-card tilt-card" data-feature="extensible">
@@ -297,7 +297,7 @@
297
297
  <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="7.5 4.21 12 6.81 16.5 4.21"/><polyline points="7.5 19.79 7.5 14.6 3 12"/><polyline points="21 12 16.5 14.6 16.5 19.79"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
298
298
  </div>
299
299
  <h3>Any LLM, Your Choice</h3>
300
- <p>Works with Anthropic, OpenAI, Google Gemini, Ollama, Groq, Together, OpenRouter, DeepSeek, xAI, Fireworks, LM Studio, and any OpenAI-compatible provider. Smart routing automatically sends simple queries to fast, cheap models &mdash; saving you money without sacrificing quality.</p>
300
+ <p>11+ providers including Anthropic, OpenAI, Gemini, Ollama, Groq, DeepSeek, and more. Smart routing sends simple queries to fast, cheap models automatically &mdash; so you save money without thinking about it.</p>
301
301
  <div class="bento-models">
302
302
  <span class="model-tag">Claude</span>
303
303
  <span class="model-tag">GPT-4o</span>
@@ -440,86 +440,179 @@
440
440
  <h2 class="section-title">Running in <span class="gradient-text">60 seconds</span>.</h2>
441
441
  </div>
442
442
 
443
- <div class="qs-terminal glow-border reveal">
444
- <div class="qs-chrome">
445
- <div class="terminal-dots">
446
- <span class="td td-r"></span>
447
- <span class="td td-y"></span>
448
- <span class="td td-g"></span>
449
- </div>
450
- <div class="qs-tabs">
451
- <button class="qs-tab active" data-tab="one-liner">One-liner</button>
452
- <button class="qs-tab" data-tab="bun">bun</button>
453
- <button class="qs-tab" data-tab="npm">npm</button>
443
+ <!-- OS toggle -->
444
+ <div class="os-toggle reveal">
445
+ <button class="os-btn active" data-os="mac" onclick="switchOS('mac')">
446
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
447
+ macOS / Linux
448
+ </button>
449
+ <button class="os-btn" data-os="windows" onclick="switchOS('windows')">
450
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M0 3.449L9.75 2.1v9.451H0m10.949-9.602L24 0v11.4H10.949M0 12.6h9.75v9.451L0 20.699M10.949 12.6H24V24l-12.9-1.801"/></svg>
451
+ Windows
452
+ </button>
453
+ <button class="os-btn" data-os="docker" onclick="switchOS('docker')">
454
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.186.186 0 00-.185.186v1.887c0 .102.083.185.185.185zm-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.186.186 0 00-.185.185v1.888c0 .102.082.185.185.186zm0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.186.186 0 00-.185.185v1.887c0 .102.082.186.185.186zm-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.186.186 0 00-.185.185v1.887c0 .102.083.186.185.186zm-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.186.186 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.186.186.186zm5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.186v1.887c0 .102.082.185.185.185zm-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.186v1.887c0 .102.083.185.185.185zm-2.964 0h2.119a.186.186 0 00.185-.185V9.006a.186.186 0 00-.185-.186H5.136a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185zm-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185zM23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.687 11.687 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.33 12.33 0 003.544-1.372 10.078 10.078 0 002.506-2.053c1.076-1.223 1.718-2.58 2.198-3.794h.19c1.18 0 1.907-.482 2.31-.882.265-.26.478-.57.625-.916l.087-.252z"/></svg>
455
+ Docker
456
+ </button>
457
+ </div>
458
+
459
+ <!-- macOS / Linux terminal -->
460
+ <div class="os-content active" data-os-content="mac">
461
+ <div class="qs-terminal glow-border reveal">
462
+ <div class="qs-chrome">
463
+ <div class="terminal-dots">
464
+ <span class="td td-r"></span>
465
+ <span class="td td-y"></span>
466
+ <span class="td td-g"></span>
467
+ </div>
468
+ <div class="qs-tabs">
469
+ <button class="qs-tab active" data-tab="one-liner">One-liner</button>
470
+ <button class="qs-tab" data-tab="bun">bun</button>
471
+ <button class="qs-tab" data-tab="npm">npm</button>
472
+ </div>
473
+ <div class="qs-chrome-spacer"></div>
474
+ </div>
475
+ <div class="qs-body">
476
+ <div class="qs-pane active" id="qs-one-liner">
477
+ <div class="qs-line">
478
+ <span class="qs-comment"># Install and launch in one shot</span>
479
+ </div>
480
+ <div class="qs-line qs-cmd">
481
+ <span class="qs-prompt">$</span>
482
+ <span class="qs-text">curl -fsSL https://zubo.bot/install.sh | bash</span>
483
+ <button class="copy-btn" data-copy="curl -fsSL https://zubo.bot/install.sh | bash" aria-label="Copy command">
484
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
485
+ </button>
486
+ </div>
487
+ </div>
488
+ <div class="qs-pane" id="qs-bun">
489
+ <div class="qs-line">
490
+ <span class="qs-comment"># Install Zubo</span>
491
+ </div>
492
+ <div class="qs-line qs-cmd">
493
+ <span class="qs-prompt">$</span>
494
+ <span class="qs-text">bun add -g zubo</span>
495
+ <button class="copy-btn" data-copy="bun add -g zubo" aria-label="Copy command">
496
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
497
+ </button>
498
+ </div>
499
+ <div class="qs-line qs-spacer"></div>
500
+ <div class="qs-line">
501
+ <span class="qs-comment"># Meet your new agent</span>
502
+ </div>
503
+ <div class="qs-line qs-cmd">
504
+ <span class="qs-prompt">$</span>
505
+ <span class="qs-text">zubo setup</span>
506
+ <button class="copy-btn" data-copy="zubo setup" aria-label="Copy command">
507
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
508
+ </button>
509
+ </div>
510
+ </div>
511
+ <div class="qs-pane" id="qs-npm">
512
+ <div class="qs-line">
513
+ <span class="qs-comment"># Install Zubo</span>
514
+ </div>
515
+ <div class="qs-line qs-cmd">
516
+ <span class="qs-prompt">$</span>
517
+ <span class="qs-text">npm i -g zubo</span>
518
+ <button class="copy-btn" data-copy="npm i -g zubo" aria-label="Copy command">
519
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
520
+ </button>
521
+ </div>
522
+ <div class="qs-line qs-spacer"></div>
523
+ <div class="qs-line">
524
+ <span class="qs-comment"># Meet your new agent</span>
525
+ </div>
526
+ <div class="qs-line qs-cmd">
527
+ <span class="qs-prompt">$</span>
528
+ <span class="qs-text">zubo setup</span>
529
+ <button class="copy-btn" data-copy="zubo setup" aria-label="Copy command">
530
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
531
+ </button>
532
+ </div>
533
+ </div>
454
534
  </div>
455
- <div class="qs-chrome-spacer"></div>
456
535
  </div>
457
- <div class="qs-body">
458
- <!-- one-liner tab -->
459
- <div class="qs-pane active" id="qs-one-liner">
460
- <div class="qs-line">
461
- <span class="qs-comment"># Install and launch in one shot</span>
462
- </div>
463
- <div class="qs-line qs-cmd">
464
- <span class="qs-prompt">$</span>
465
- <span class="qs-text">curl -fsSL https://zubo.bot/install.sh | bash</span>
466
- <button class="copy-btn" data-copy="curl -fsSL https://zubo.bot/install.sh | bash" aria-label="Copy command">
467
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
468
- </button>
536
+ <p class="qs-note reveal">Works on macOS &amp; Linux. The installer sets up Bun and everything else for you.</p>
537
+ </div>
538
+
539
+ <!-- Windows terminal -->
540
+ <div class="os-content" data-os-content="windows">
541
+ <div class="qs-terminal glow-border reveal">
542
+ <div class="qs-chrome">
543
+ <div class="terminal-dots">
544
+ <span class="td td-r"></span>
545
+ <span class="td td-y"></span>
546
+ <span class="td td-g"></span>
469
547
  </div>
548
+ <div class="terminal-title">PowerShell</div>
549
+ <div class="qs-chrome-spacer"></div>
470
550
  </div>
471
- <!-- bun tab -->
472
- <div class="qs-pane" id="qs-bun">
551
+ <div class="qs-body">
473
552
  <div class="qs-line">
474
- <span class="qs-comment"># Install Zubo</span>
553
+ <span class="qs-comment"># Install WSL (one-time, requires restart)</span>
475
554
  </div>
476
555
  <div class="qs-line qs-cmd">
477
- <span class="qs-prompt">$</span>
478
- <span class="qs-text">bun add -g zubo</span>
479
- <button class="copy-btn" data-copy="bun add -g zubo" aria-label="Copy command">
556
+ <span class="qs-prompt">&gt;</span>
557
+ <span class="qs-text">wsl --install</span>
558
+ <button class="copy-btn" data-copy="wsl --install" aria-label="Copy command">
480
559
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
481
560
  </button>
482
561
  </div>
483
562
  <div class="qs-line qs-spacer"></div>
484
563
  <div class="qs-line">
485
- <span class="qs-comment"># Meet your new agent</span>
564
+ <span class="qs-comment"># Then open WSL and install Zubo</span>
486
565
  </div>
487
566
  <div class="qs-line qs-cmd">
488
567
  <span class="qs-prompt">$</span>
489
- <span class="qs-text">zubo setup</span>
490
- <button class="copy-btn" data-copy="zubo setup" aria-label="Copy command">
568
+ <span class="qs-text">curl -fsSL https://zubo.bot/install.sh | bash</span>
569
+ <button class="copy-btn" data-copy="curl -fsSL https://zubo.bot/install.sh | bash" aria-label="Copy command">
491
570
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
492
571
  </button>
493
572
  </div>
494
573
  </div>
495
- <!-- npm tab -->
496
- <div class="qs-pane" id="qs-npm">
574
+ </div>
575
+ <p class="qs-note reveal">Runs inside WSL (Windows Subsystem for Linux). Full performance, native experience.</p>
576
+ </div>
577
+
578
+ <!-- Docker terminal -->
579
+ <div class="os-content" data-os-content="docker">
580
+ <div class="qs-terminal glow-border reveal">
581
+ <div class="qs-chrome">
582
+ <div class="terminal-dots">
583
+ <span class="td td-r"></span>
584
+ <span class="td td-y"></span>
585
+ <span class="td td-g"></span>
586
+ </div>
587
+ <div class="terminal-title">Terminal</div>
588
+ <div class="qs-chrome-spacer"></div>
589
+ </div>
590
+ <div class="qs-body">
497
591
  <div class="qs-line">
498
- <span class="qs-comment"># Install Zubo</span>
592
+ <span class="qs-comment"># Clone and start with Docker</span>
499
593
  </div>
500
594
  <div class="qs-line qs-cmd">
501
595
  <span class="qs-prompt">$</span>
502
- <span class="qs-text">npm i -g zubo</span>
503
- <button class="copy-btn" data-copy="npm i -g zubo" aria-label="Copy command">
596
+ <span class="qs-text">git clone https://github.com/apwn/zubo.git &amp;&amp; cd zubo</span>
597
+ <button class="copy-btn" data-copy="git clone https://github.com/apwn/zubo.git && cd zubo" aria-label="Copy command">
504
598
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
505
599
  </button>
506
600
  </div>
507
601
  <div class="qs-line qs-spacer"></div>
508
602
  <div class="qs-line">
509
- <span class="qs-comment"># Meet your new agent</span>
603
+ <span class="qs-comment"># Launch Zubo in a container</span>
510
604
  </div>
511
605
  <div class="qs-line qs-cmd">
512
606
  <span class="qs-prompt">$</span>
513
- <span class="qs-text">zubo setup</span>
514
- <button class="copy-btn" data-copy="zubo setup" aria-label="Copy command">
607
+ <span class="qs-text">docker compose up -d</span>
608
+ <button class="copy-btn" data-copy="docker compose up -d" aria-label="Copy command">
515
609
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
516
610
  </button>
517
611
  </div>
518
612
  </div>
519
613
  </div>
614
+ <p class="qs-note reveal">Works on any OS with Docker. Dashboard at <strong>localhost:8787</strong>.</p>
520
615
  </div>
521
-
522
- <p class="qs-note reveal">Works on macOS &amp; Linux. The installer sets up Bun and everything else for you.</p>
523
616
  </div>
524
617
  </section>
525
618
 
@@ -552,8 +645,8 @@
552
645
  </div>
553
646
  <div class="container">
554
647
  <div class="cta-content reveal">
555
- <h2>Ready to build your <span class="gradient-text">perfect AI agent</span>?</h2>
556
- <p>Get started in seconds. Free forever. No credit card needed.</p>
648
+ <h2>Ready to meet your <span class="gradient-text">new AI agent</span>?</h2>
649
+ <p>One install. Free and open source. You only pay for the LLM API you choose.</p>
557
650
  <div class="cta-actions">
558
651
  <a href="#get-started" class="btn btn-primary btn-xl">
559
652
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
@@ -616,7 +709,7 @@
616
709
  "@context": "https://schema.org",
617
710
  "@type": "SoftwareApplication",
618
711
  "name": "Zubo",
619
- "description": "A self-hosted AI agent with persistent semantic memory, multi-channel presence, 35+ smart tools, sub-agent delegation, and workflow automation. Open source, runs locally.",
712
+ "description": "Open-source AI agent with persistent semantic memory, multi-channel presence, 20+ smart tools, sub-agent delegation, and workflow automation. Runs locally on your machine.",
620
713
  "url": "https://zubo.bot",
621
714
  "applicationCategory": "DeveloperApplication",
622
715
  "operatingSystem": "macOS, Linux, Windows",
package/site/script.js CHANGED
@@ -3,6 +3,16 @@
3
3
  Vanilla JS, zero dependencies
4
4
  ============================================ */
5
5
 
6
+ /* OS toggle — must be global for inline onclick handlers */
7
+ function switchOS(os) {
8
+ document.querySelectorAll('.os-btn').forEach(function (b) { b.classList.remove('active'); });
9
+ document.querySelectorAll('.os-content').forEach(function (c) { c.classList.remove('active'); });
10
+ var btn = document.querySelector('.os-btn[data-os="' + os + '"]');
11
+ if (btn) btn.classList.add('active');
12
+ var content = document.querySelector('[data-os-content="' + os + '"]');
13
+ if (content) content.classList.add('active');
14
+ }
15
+
6
16
  (function () {
7
17
  'use strict';
8
18
 
@@ -231,14 +241,13 @@
231
241
  });
232
242
 
233
243
  /* ------------------------------------------
234
- 6. Quick Start tab switching
244
+ 6. Quick Start tab switching (within macOS/Linux)
235
245
  ------------------------------------------ */
236
246
  document.querySelectorAll('.qs-tab').forEach(function (tab) {
237
247
  tab.addEventListener('click', function () {
238
- // Deactivate all tabs and panes
239
- document.querySelectorAll('.qs-tab').forEach(function (t) { t.classList.remove('active'); });
240
- document.querySelectorAll('.qs-pane').forEach(function (p) { p.classList.remove('active'); });
241
- // Activate clicked tab and matching pane
248
+ var parent = tab.closest('.os-content') || document;
249
+ parent.querySelectorAll('.qs-tab').forEach(function (t) { t.classList.remove('active'); });
250
+ parent.querySelectorAll('.qs-pane').forEach(function (p) { p.classList.remove('active'); });
242
251
  tab.classList.add('active');
243
252
  var paneId = 'qs-' + tab.getAttribute('data-tab');
244
253
  var pane = document.getElementById(paneId);
package/site/style.css CHANGED
@@ -986,6 +986,44 @@ h1, h2, h3, h4,
986
986
  color: #c4b5fd;
987
987
  }
988
988
  .qs-chrome-spacer { flex: 1; }
989
+
990
+ /* OS toggle */
991
+ .os-toggle {
992
+ display: flex;
993
+ justify-content: center;
994
+ gap: 6px;
995
+ margin-bottom: 24px;
996
+ }
997
+ .os-btn {
998
+ display: inline-flex;
999
+ align-items: center;
1000
+ gap: 8px;
1001
+ background: rgba(255,255,255,0.04);
1002
+ border: 1px solid var(--border);
1003
+ border-radius: 10px;
1004
+ padding: 10px 20px;
1005
+ font-family: var(--font);
1006
+ font-size: 14px;
1007
+ font-weight: 500;
1008
+ color: var(--text-muted);
1009
+ cursor: pointer;
1010
+ transition: all 0.25s var(--ease);
1011
+ }
1012
+ .os-btn:hover {
1013
+ color: var(--text-secondary);
1014
+ border-color: rgba(124, 58, 237, 0.2);
1015
+ background: rgba(255,255,255,0.06);
1016
+ }
1017
+ .os-btn.active {
1018
+ background: rgba(124, 58, 237, 0.12);
1019
+ border-color: rgba(124, 58, 237, 0.35);
1020
+ color: #c4b5fd;
1021
+ }
1022
+ .os-btn svg { opacity: 0.7; }
1023
+ .os-btn.active svg { opacity: 1; }
1024
+ .os-content { display: none; }
1025
+ .os-content.active { display: block; }
1026
+
989
1027
  .qs-body {
990
1028
  padding: 28px 28px 32px;
991
1029
  font-family: var(--mono);
@@ -1,5 +1,11 @@
1
1
  const API = "https://gmail.googleapis.com/gmail/v1/users/me";
2
2
 
3
+ /** RFC 2047 encode a header value if it contains non-ASCII characters (e.g. emojis) */
4
+ function mimeEncode(value: string): string {
5
+ if (/^[\x20-\x7E]*$/.test(value)) return value;
6
+ return `=?UTF-8?B?${Buffer.from(value, "utf-8").toString("base64")}?=`;
7
+ }
8
+
3
9
  async function getToken(): Promise<string> {
4
10
  const Zubo = (globalThis as any).Zubo;
5
11
  if (Zubo?.getGoogleToken) return Zubo.getGoogleToken();
@@ -80,7 +86,7 @@ export default async function (input: Record<string, unknown>): Promise<string>
80
86
  case "send": {
81
87
  if (!to) return JSON.stringify({ error: "to is required" });
82
88
  if (!subject) return JSON.stringify({ error: "subject is required" });
83
- const raw = Buffer.from(`To: ${to}\r\nSubject: ${subject}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n${body || ""}`).toString("base64url");
89
+ const raw = Buffer.from(`To: ${to}\r\nSubject: ${mimeEncode(subject)}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n${body || ""}`).toString("base64url");
84
90
  const res = await fetch(`${API}/messages/send`, {
85
91
  method: "POST", headers,
86
92
  body: JSON.stringify({ raw }),
@@ -106,7 +112,7 @@ export default async function (input: Record<string, unknown>): Promise<string>
106
112
  const replyTo = getHeader("From");
107
113
  const subj = getHeader("Subject").startsWith("Re:") ? getHeader("Subject") : `Re: ${getHeader("Subject")}`;
108
114
  const msgId = getHeader("Message-ID");
109
- const raw = Buffer.from(`To: ${replyTo}\r\nSubject: ${subj}\r\nIn-Reply-To: ${msgId}\r\nReferences: ${msgId}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n${body}`).toString("base64url");
115
+ const raw = Buffer.from(`To: ${replyTo}\r\nSubject: ${mimeEncode(subj)}\r\nIn-Reply-To: ${msgId}\r\nReferences: ${msgId}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n${body}`).toString("base64url");
110
116
  const res = await fetch(`${API}/messages/send`, {
111
117
  method: "POST", headers,
112
118
  body: JSON.stringify({ raw, threadId: origData.threadId }),
@@ -37,11 +37,17 @@ function validateUrl(raw: string): void {
37
37
  export default async function (input: Record<string, unknown>): Promise<string> {
38
38
  const url = input.url as string;
39
39
  const method = ((input.method as string) || "GET").toUpperCase();
40
- const headers = (input.headers as Record<string, string>) || {};
40
+ const rawHeaders = (input.headers as Record<string, string>) || {};
41
41
  const body = input.body as string | undefined;
42
42
 
43
43
  validateUrl(url);
44
44
 
45
+ // Strip non-ASCII characters from header values (HTTP/1.1 requires ASCII)
46
+ const headers: Record<string, string> = {};
47
+ for (const [key, value] of Object.entries(rawHeaders)) {
48
+ headers[key] = String(value).replace(/[^\x20-\x7E]/g, "");
49
+ }
50
+
45
51
  const opts: RequestInit = {
46
52
  method,
47
53
  headers: {