zubo 0.1.20 → 0.1.21
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/package.json +1 -1
- package/site/docs/agents.html +2 -2
- package/site/docs/api.html +2 -2
- package/site/docs/index.html +5 -5
- package/site/docs/integrations.html +3 -3
- package/site/docs/marketplace.html +9 -9
- package/site/docs/security.html +4 -4
- package/site/docs/skills.html +1 -1
- package/site/index.html +4 -4
- package/site/install.sh +11 -5
- package/src/channels/dashboard.html.ts +84 -52
- package/src/channels/webchat.ts +16 -16
- package/src/llm/claude-code.ts +1 -2
- package/src/llm/codex.ts +1 -2
- package/src/setup-web.html.ts +9 -9
- package/src/setup.ts +6 -6
- package/src/tools/executor.ts +2 -2
- package/src/tools/mcp-registry.ts +1 -1
package/package.json
CHANGED
package/site/docs/agents.html
CHANGED
|
@@ -89,11 +89,11 @@
|
|
|
89
89
|
<p>By default, Zubo operates as a single agent that handles all messages across every connected channel using a unified session identified as "owner." This default agent has access to all registered tools — both built-in tools and any user-installed skills — and its personality and behavior are defined by the system prompt stored at <code>~/.zubo/workspace/SYSTEM.md</code>.</p>
|
|
90
90
|
<p>For many use cases, the default agent is all you need. It can search the web, manage your calendar, write code, read and write memory, and use any skill you install. Custom agents become valuable when you want to restrict tool access, provide specialized instructions, or build multi-step workflows.</p>
|
|
91
91
|
|
|
92
|
-
<h2>
|
|
92
|
+
<h2>Personality</h2>
|
|
93
93
|
<p>The system prompt defines your agent's personality, rules, and background knowledge. There are two ways to customize it:</p>
|
|
94
94
|
<ul>
|
|
95
95
|
<li><strong>Edit the file directly</strong> — Open <code>~/.zubo/workspace/SYSTEM.md</code> in any text editor and modify it.</li>
|
|
96
|
-
<li><strong>Use the dashboard</strong> — Navigate to the
|
|
96
|
+
<li><strong>Use the dashboard</strong> — Navigate to the Personality panel in the web dashboard and edit it there. Changes are saved to <code>SYSTEM.md</code> automatically.</li>
|
|
97
97
|
</ul>
|
|
98
98
|
<p>Here is an example <code>SYSTEM.md</code> that defines a personalized assistant:</p>
|
|
99
99
|
<pre><code># System Prompt
|
package/site/docs/api.html
CHANGED
|
@@ -200,7 +200,7 @@ file: <document></code></pre>
|
|
|
200
200
|
"Status": "running"
|
|
201
201
|
}</code></pre>
|
|
202
202
|
|
|
203
|
-
<h3>
|
|
203
|
+
<h3>Personality</h3>
|
|
204
204
|
<pre><code>GET /api/dashboard/system</code></pre>
|
|
205
205
|
<p>Retrieve the current system prompt.</p>
|
|
206
206
|
<pre><code>PUT /api/dashboard/system
|
|
@@ -583,7 +583,7 @@ Content-Type: application/json
|
|
|
583
583
|
<pre><code>GET /api/dashboard/channel-status</code></pre>
|
|
584
584
|
<p>Returns the configuration and connection status for each channel (webchat, Telegram, Discord, Slack, WhatsApp, Signal). Each channel reports whether it is configured and whether it is currently enabled.</p>
|
|
585
585
|
|
|
586
|
-
<h3>
|
|
586
|
+
<h3>API Keys Management</h3>
|
|
587
587
|
<pre><code>GET /api/dashboard/secrets</code></pre>
|
|
588
588
|
<p>List all stored secrets. Values are masked in the response for security.</p>
|
|
589
589
|
<pre><code>GET /api/dashboard/secrets/:name</code></pre>
|
package/site/docs/index.html
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<a href="/docs/conversations">Conversation History</a>
|
|
70
70
|
<a href="/docs/webhooks">Webhooks</a>
|
|
71
71
|
<a href="/docs/workflows">Visual Workflows</a>
|
|
72
|
-
<a href="/docs/marketplace">
|
|
72
|
+
<a href="/docs/marketplace">Extensions Marketplace</a>
|
|
73
73
|
</div>
|
|
74
74
|
</div>
|
|
75
75
|
<div class="docs-sidebar-section">
|
|
@@ -300,10 +300,10 @@ docker compose up -d</code></pre>
|
|
|
300
300
|
|
|
301
301
|
<p>The wizard walks you through 4 steps:</p>
|
|
302
302
|
<ul>
|
|
303
|
-
<li><strong>Step 1:
|
|
303
|
+
<li><strong>Step 1: AI Provider</strong> — choose from 15 supported providers (Anthropic, OpenAI, Ollama, Groq, Together, OpenRouter, DeepSeek, xAI, MiniMax, Fireworks, Cerebras, LM Studio, Claude Code, Codex, or any OpenAI-compatible endpoint) and enter your API key. Optionally configure a fallback provider for automatic failover.</li>
|
|
304
304
|
<li><strong>Step 2: Channels</strong> — enable any combination of Telegram, Discord, Slack, WhatsApp, Signal, and Email. Enter the required tokens and credentials for each. Web Chat is always on and requires no configuration.</li>
|
|
305
305
|
<li><strong>Step 3: Personalization</strong> — set your agent's name and describe its personality. This shapes how Zubo talks to you across all channels.</li>
|
|
306
|
-
<li><strong>Step 4: Smart
|
|
306
|
+
<li><strong>Step 4: Smart Cost Savings</strong> — optionally configure a fast model (e.g., Groq) for simple queries. Smart routing automatically detects low-complexity messages and routes them to the cheaper, faster model, saving 50–80% on costs without sacrificing quality for complex tasks.</li>
|
|
307
307
|
</ul>
|
|
308
308
|
<p>The wizard also creates the <code>~/.zubo</code> directory and all required subdirectories, generates your <code>~/.zubo/config.json</code> file, and downloads the all-MiniLM-L6-v2 embedding model (~80 MB) for local semantic memory.</p>
|
|
309
309
|
|
|
@@ -624,7 +624,7 @@ zubo start --daemon</code></pre>
|
|
|
624
624
|
<li><strong>Use <code>zubo start --daemon</code></strong> for always-on operation. Zubo writes a PID file and rotates logs automatically.</li>
|
|
625
625
|
<li><strong>Check <code>zubo status</code> and <code>zubo logs</code></strong> for troubleshooting. The status command shows uptime, memory usage, connected channels, and pending scheduled jobs.</li>
|
|
626
626
|
<li><strong>Back up your data</strong> with <code>zubo export</code> for a full JSON export, or rely on the automatic daily SQLite backups in <code>~/.zubo/workspace/backups/</code>.</li>
|
|
627
|
-
<li><strong>Enable smart
|
|
627
|
+
<li><strong>Enable smart cost savings</strong> to automatically send simple queries to fast, cheap models. Set <code>smartRouting.enabled</code> to <code>true</code> and configure a <code>fastProvider</code> (e.g., Groq) alongside your primary. This can cut costs significantly.</li>
|
|
628
628
|
<li><strong>Set a budget</strong> to avoid surprise bills: <code>zubo config set budget.dailyLimit 5</code>. Cost tracking is always active in the dashboard under Analytics → Costs.</li>
|
|
629
629
|
<li><strong>Configure failover providers</strong> so your agent stays available even if your primary LLM provider has an outage. For example, set Anthropic as primary and Ollama as failover for fully offline operation when the cloud is down.</li>
|
|
630
630
|
<li><strong>Keep skills small and focused</strong> — one skill per task. This makes them easier to test, share, and debug.</li>
|
|
@@ -644,7 +644,7 @@ zubo start --daemon</code></pre>
|
|
|
644
644
|
<li><a href="/docs/conversations"><strong>Conversation History</strong></a> — unified cross-channel history with FTS5 search, dashboard browsing, and API access for searching and analyzing past conversations across all channels.</li>
|
|
645
645
|
<li><a href="/docs/webhooks"><strong>Webhooks</strong></a> — create webhook endpoints for GitHub, Stripe, CI/CD, and any external service. HMAC signature verification, prompt templates with <code>{{payload}}</code> substitution, and dashboard management.</li>
|
|
646
646
|
<li><a href="/docs/workflows"><strong>Visual Workflows</strong></a> — build multi-step automations with the drag-and-drop workflow builder. Step types (tool, agent, condition, message, delay), triggers (manual, cron, webhook), and template variables.</li>
|
|
647
|
-
<li><a href="/docs/marketplace"><strong>
|
|
647
|
+
<li><a href="/docs/marketplace"><strong>Extensions Marketplace</strong></a> — browse, install, and manage MCP servers from the official registry. One-click installation with automatic tool registration.</li>
|
|
648
648
|
<li><a href="/docs/integrations"><strong>Integrations</strong></a> — connect Zubo to GitHub, Google Workspace, Notion, Linear, Jira, and more. <a href="/docs/integrations#oauth">OAuth 2.0 authentication</a> with automatic token refresh, API key setup, and usage examples.</li>
|
|
649
649
|
<li><a href="/docs/security"><strong>Security & Auth</strong></a> — harden your Zubo instance: API key management, tool permission levels, confirmation tokens, rate limiting, file access restrictions, and network security.</li>
|
|
650
650
|
<li><a href="/docs/api"><strong>API Reference</strong></a> — complete HTTP API documentation for programmatic access: endpoints, request/response formats, authentication, and WebSocket streaming.</li>
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
<p>There are three ways to connect a service:</p>
|
|
102
102
|
<ol>
|
|
103
103
|
<li><strong>Chat:</strong> Tell Zubo directly — for example, <code>"Connect GitHub with token ghp_abc123"</code>. Zubo will store the secret and install the skill pack automatically.</li>
|
|
104
|
-
<li><strong>Dashboard:</strong> Navigate to Settings →
|
|
104
|
+
<li><strong>Dashboard:</strong> Navigate to Settings → API Keys. Enter the secret name and value, then Zubo detects the associated integration and installs the skills.</li>
|
|
105
105
|
<li><strong>CLI:</strong> Run <code>zubo start</code>, then use the <code>connect_service</code> tool programmatically to provide the credentials.</li>
|
|
106
106
|
</ol>
|
|
107
107
|
<p>When you connect a service, Zubo performs the following steps:</p>
|
|
@@ -384,11 +384,11 @@ zubo mcp-serve</code></pre>
|
|
|
384
384
|
</tbody>
|
|
385
385
|
</table>
|
|
386
386
|
|
|
387
|
-
<h2 id="managing-secrets">Managing
|
|
387
|
+
<h2 id="managing-secrets">Managing API Keys</h2>
|
|
388
388
|
<p>All integration secrets are managed through the same unified secret system.</p>
|
|
389
389
|
<h3>Dashboard</h3>
|
|
390
390
|
<ul>
|
|
391
|
-
<li>Navigate to Settings →
|
|
391
|
+
<li>Navigate to Settings → API Keys</li>
|
|
392
392
|
<li>Values are displayed as <code>••••••••</code> by default</li>
|
|
393
393
|
<li>Click <strong>“Reveal”</strong> to show the actual secret value</li>
|
|
394
394
|
<li>Click <strong>“Edit”</strong> to update a secret with a new value</li>
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>
|
|
6
|
+
<title>Extensions Marketplace — Zubo Docs</title>
|
|
7
7
|
<meta name="description" content="Browse, install, and manage MCP servers from the built-in marketplace. Extend Zubo with community-built tools, one-click installs, and automatic configuration.">
|
|
8
8
|
<meta name="theme-color" content="#060608">
|
|
9
9
|
<link rel="canonical" href="https://zubo.bot/docs/marketplace">
|
|
10
|
-
<meta property="og:title" content="
|
|
10
|
+
<meta property="og:title" content="Extensions Marketplace — Zubo Docs">
|
|
11
11
|
<meta property="og:description" content="Browse, install, and manage MCP servers from the built-in marketplace. Extend Zubo with community-built tools, one-click installs, and automatic configuration.">
|
|
12
12
|
<meta property="og:type" content="website">
|
|
13
13
|
<meta property="og:url" content="https://zubo.bot/docs/marketplace">
|
|
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="
|
|
17
|
+
<meta name="twitter:title" content="Extensions Marketplace — Zubo Docs">
|
|
18
18
|
<meta name="twitter:description" content="Browse, install, and manage MCP servers from the built-in marketplace. Extend Zubo with community-built tools, one-click installs, and automatic configuration.">
|
|
19
19
|
<meta name="twitter:image" content="https://zubo.bot/og-image.png">
|
|
20
20
|
<meta name="twitter:creator" content="@thomaskanze">
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
<a href="/docs/conversations">Conversation History</a>
|
|
68
68
|
<a href="/docs/webhooks">Webhooks</a>
|
|
69
69
|
<a href="/docs/workflows">Visual Workflows</a>
|
|
70
|
-
<a href="/docs/marketplace" class="active">
|
|
70
|
+
<a href="/docs/marketplace" class="active">Extensions Marketplace</a>
|
|
71
71
|
</div>
|
|
72
72
|
</div>
|
|
73
73
|
<div class="docs-sidebar-section">
|
|
@@ -89,12 +89,12 @@
|
|
|
89
89
|
</aside>
|
|
90
90
|
|
|
91
91
|
<main class="docs-content">
|
|
92
|
-
<div class="docs-breadcrumb"><a href="/">Home</a><span>/</span><a href="/docs/">Docs</a><span>/</span>
|
|
92
|
+
<div class="docs-breadcrumb"><a href="/">Home</a><span>/</span><a href="/docs/">Docs</a><span>/</span>Extensions Marketplace</div>
|
|
93
93
|
|
|
94
|
-
<h1>
|
|
94
|
+
<h1>Extensions Marketplace</h1>
|
|
95
95
|
|
|
96
96
|
<p>
|
|
97
|
-
The
|
|
97
|
+
The Extensions Marketplace lets you browse, install, and manage MCP (Model Context Protocol) servers from the official registry directly through the Zubo dashboard or API. Instead of manually configuring MCP server commands and arguments, you can discover servers from the community, install them with one click, and have their tools automatically registered in Zubo.
|
|
98
98
|
</p>
|
|
99
99
|
|
|
100
100
|
<!-- ================================================================ -->
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
<h3>Via the Dashboard</h3>
|
|
108
108
|
|
|
109
109
|
<ol>
|
|
110
|
-
<li>Open the web dashboard and navigate to the <strong>
|
|
110
|
+
<li>Open the web dashboard and navigate to the <strong>Extensions</strong> panel in the sidebar.</li>
|
|
111
111
|
<li>Click the <strong>Marketplace</strong> tab to view the available servers from the registry.</li>
|
|
112
112
|
<li>Use the search bar to filter servers by name or keyword (e.g., "filesystem", "database", "github").</li>
|
|
113
113
|
<li>Each server card shows the name, description, author, tool count, and install status.</li>
|
|
@@ -291,7 +291,7 @@ Content-Type: application/json
|
|
|
291
291
|
"itemListElement": [
|
|
292
292
|
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://zubo.bot/" },
|
|
293
293
|
{ "@type": "ListItem", "position": 2, "name": "Docs", "item": "https://zubo.bot/docs/" },
|
|
294
|
-
{ "@type": "ListItem", "position": 3, "name": "
|
|
294
|
+
{ "@type": "ListItem", "position": 3, "name": "Extensions Marketplace", "item": "https://zubo.bot/docs/marketplace" }
|
|
295
295
|
]
|
|
296
296
|
}
|
|
297
297
|
</script>
|
package/site/docs/security.html
CHANGED
|
@@ -169,18 +169,18 @@
|
|
|
169
169
|
|
|
170
170
|
<h2 id="secret-management">Secret Management</h2>
|
|
171
171
|
<p>Secrets (API keys, tokens, passwords) are stored securely in the local SQLite <code>secrets</code> table. The agent can use secrets by name but <strong>never sees their actual values</strong> in conversation context.</p>
|
|
172
|
-
<h3>Storing
|
|
172
|
+
<h3>Storing API Keys</h3>
|
|
173
173
|
<p>There are three ways to store a secret:</p>
|
|
174
174
|
<ul>
|
|
175
175
|
<li><strong>Chat:</strong> Tell Zubo directly — <code>"Store my GitHub token: ghp_abc123xyz"</code></li>
|
|
176
176
|
<li><strong>Tool:</strong> The <code>secret_set</code> tool can be called programmatically</li>
|
|
177
|
-
<li><strong>Dashboard:</strong> Navigate to Settings →
|
|
177
|
+
<li><strong>Dashboard:</strong> Navigate to Settings → API Keys and use the form</li>
|
|
178
178
|
</ul>
|
|
179
|
-
<h3>Accessing
|
|
179
|
+
<h3>Accessing API Keys in Skills</h3>
|
|
180
180
|
<p>User-installed skills can access secrets via environment variables:</p>
|
|
181
181
|
<pre><code>const token = process.env.ZUBO_SECRET_GITHUB_TOKEN;</code></pre>
|
|
182
182
|
<p>Only secrets explicitly declared in the skill manifest are passed to the skill process.</p>
|
|
183
|
-
<h3>Managing
|
|
183
|
+
<h3>Managing API Keys</h3>
|
|
184
184
|
<ul>
|
|
185
185
|
<li><strong>Dashboard:</strong> View masked values (<code>••••••••</code>), click “Reveal” to show the actual value, click “Edit” to update, or “Delete” to remove</li>
|
|
186
186
|
<li><strong>Naming:</strong> Secret names must match the pattern <code>[a-z0-9_]+</code> (lowercase alphanumeric and underscores only)</li>
|
package/site/docs/skills.html
CHANGED
|
@@ -162,7 +162,7 @@ export default async function (input: Record<string, unknown>): Promise<
|
|
|
162
162
|
<li><strong>Cannot import from Zubo internals</strong> — skills run in a sandboxed subprocess and have no access to Zubo's core modules, database, or session state</li>
|
|
163
163
|
</ul>
|
|
164
164
|
|
|
165
|
-
<h2>Accessing
|
|
165
|
+
<h2>Accessing API Keys</h2>
|
|
166
166
|
<p>Skills often need API keys or credentials to call external services. Zubo provides a secure way to pass secrets to skill handlers via environment variables:</p>
|
|
167
167
|
<pre><code>export default async function (input: Record<string, unknown>): Promise<string> {
|
|
168
168
|
const apiKey = process.env.ZUBO_SECRET_WEATHER_API_KEY;
|
package/site/index.html
CHANGED
|
@@ -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>Zubo — Your AI Agent. Your Machine. Your Rules.</title>
|
|
7
|
-
<meta name="description" content="Open-source AI agent that runs on your machine. One command to install, one file to configure. Persistent memory, 25+ tools, 7 messaging channels, 12+
|
|
7
|
+
<meta name="description" content="Open-source AI agent that runs on your machine. One command to install, one file to configure. Persistent memory, 25+ tools, 7 messaging channels, 12+ AI providers. Extension compatible. Zero complexity.">
|
|
8
8
|
<meta name="theme-color" content="#060608">
|
|
9
9
|
<link rel="canonical" href="https://zubo.bot/">
|
|
10
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, 25+ tools, works across Telegram, Discord, Slack, WhatsApp, Email, and more.
|
|
11
|
+
<meta property="og:description" content="Open-source AI agent that runs on your machine. Persistent memory, 25+ tools, works across Telegram, Discord, Slack, WhatsApp, Email, and more. Extension compatible. 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
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, 25+ tools, works across Telegram, Discord, Slack, WhatsApp, Email, and more.
|
|
18
|
+
<meta name="twitter:description" content="Open-source AI agent that runs on your machine. Persistent memory, 25+ tools, works across Telegram, Discord, Slack, WhatsApp, Email, and more. Extension compatible. 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">
|
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
<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="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
|
|
265
265
|
</div>
|
|
266
266
|
<h3>25+ Smart Tools</h3>
|
|
267
|
-
<p>Web search, file operations, code interpreter, image generation, APIs, webhooks, and more. Knowledge graph memory,
|
|
267
|
+
<p>Web search, file operations, code interpreter, image generation, APIs, webhooks, and more. Knowledge graph memory, extension support, and sub-agent delegation for complex tasks.</p>
|
|
268
268
|
</div>
|
|
269
269
|
|
|
270
270
|
<div class="bento-card tilt-card" data-feature="privacy">
|
package/site/install.sh
CHANGED
|
@@ -29,7 +29,9 @@ ARCH="$(uname -m)"
|
|
|
29
29
|
case "$OS" in
|
|
30
30
|
Linux*) PLATFORM="linux" ;;
|
|
31
31
|
Darwin*) PLATFORM="darwin" ;;
|
|
32
|
-
*)
|
|
32
|
+
MINGW*|MSYS*|CYGWIN*)
|
|
33
|
+
fail "Windows detected. Use WSL (Windows Subsystem for Linux) to run Zubo:\n\n ${DIM}wsl --install${RESET}\n Then run this installer inside WSL." ;;
|
|
34
|
+
*) fail "Unsupported OS: $OS. Zubo supports macOS, Linux, and Windows (via WSL)." ;;
|
|
33
35
|
esac
|
|
34
36
|
|
|
35
37
|
case "$ARCH" in
|
|
@@ -45,7 +47,7 @@ if command -v bun &>/dev/null; then
|
|
|
45
47
|
BUN_VERSION=$(bun --version 2>/dev/null || echo "unknown")
|
|
46
48
|
ok "Bun already installed (v${BUN_VERSION})"
|
|
47
49
|
else
|
|
48
|
-
info "Installing Bun runtime..."
|
|
50
|
+
info "Installing Bun (a fast JavaScript runtime Zubo needs)..."
|
|
49
51
|
curl -fsSL https://bun.sh/install | bash
|
|
50
52
|
|
|
51
53
|
# Source the updated profile so bun is on PATH
|
|
@@ -55,7 +57,7 @@ else
|
|
|
55
57
|
if command -v bun &>/dev/null; then
|
|
56
58
|
ok "Bun installed (v$(bun --version))"
|
|
57
59
|
else
|
|
58
|
-
fail "Bun installation failed.
|
|
60
|
+
fail "Bun installation failed. Visit https://bun.sh for manual install instructions."
|
|
59
61
|
fi
|
|
60
62
|
fi
|
|
61
63
|
|
|
@@ -78,9 +80,13 @@ else
|
|
|
78
80
|
# Bun global bin might not be on PATH yet
|
|
79
81
|
ZUBO_BIN="${HOME}/.bun/bin/zubo"
|
|
80
82
|
if [ -f "$ZUBO_BIN" ]; then
|
|
81
|
-
warn "Zubo installed but
|
|
83
|
+
warn "Zubo installed but your terminal can't find it yet."
|
|
82
84
|
echo ""
|
|
83
|
-
echo -e "
|
|
85
|
+
echo -e " Run this command, then restart your terminal:"
|
|
86
|
+
echo ""
|
|
87
|
+
echo -e " ${BOLD}echo 'export PATH=\"\$HOME/.bun/bin:\$PATH\"' >> ~/.bashrc && source ~/.bashrc${RESET}"
|
|
88
|
+
echo ""
|
|
89
|
+
echo -e " ${DIM}(On macOS with zsh, use ~/.zshrc instead of ~/.bashrc)${RESET}"
|
|
84
90
|
echo ""
|
|
85
91
|
else
|
|
86
92
|
fail "Zubo binary not found after install"
|
|
@@ -928,7 +928,7 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
928
928
|
<span class="nav-svg-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg></span> Dashboard
|
|
929
929
|
</a>
|
|
930
930
|
<a href="#memory" onclick="showPanel('memory')">
|
|
931
|
-
<span class="nav-svg-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2C8.5 2 6 4.5 6 7c0 1.5.5 2.8 1.4 3.8C6.5 12 6 13.5 6 15c0 3.5 2.5 7 6 7s6-3.5 6-7c0-1.5-.5-3-1.4-4.2C17.5 9.8 18 8.5 18 7c0-2.5-2.5-5-6-5z"/><path d="M9 10h6"/><path d="M9 14h6"/></svg></span>
|
|
931
|
+
<span class="nav-svg-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2C8.5 2 6 4.5 6 7c0 1.5.5 2.8 1.4 3.8C6.5 12 6 13.5 6 15c0 3.5 2.5 7 6 7s6-3.5 6-7c0-1.5-.5-3-1.4-4.2C17.5 9.8 18 8.5 18 7c0-2.5-2.5-5-6-5z"/><path d="M9 10h6"/><path d="M9 14h6"/></svg></span> Knowledge
|
|
932
932
|
</a>
|
|
933
933
|
<a href="#skills" onclick="showPanel('skills')">
|
|
934
934
|
<span class="nav-svg-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg></span> Skills
|
|
@@ -940,7 +940,7 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
940
940
|
<span class="nav-svg-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 16.98h1.67c2.49 0 4.5-2.01 4.5-4.49 0-2.48-2.01-4.49-4.5-4.49h-.16C19.17 5.35 16.68 3 13.67 3 11.23 3 9.14 4.56 8.34 6.78c-.3-.05-.6-.08-.91-.08-2.76 0-5 2.24-5 5s2.24 5 5 5h1.48"/><polyline points="12 13 12 21"/><polyline points="9 18 12 21 15 18"/></svg></span> Webhooks
|
|
941
941
|
</a>
|
|
942
942
|
<a href="#mcp" onclick="showPanel('mcp')">
|
|
943
|
-
<span class="nav-svg-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 12h4"/><path d="M14 12h4"/><circle cx="8" cy="12" r="1"/><circle cx="16" cy="12" r="1"/></svg></span>
|
|
943
|
+
<span class="nav-svg-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 12h4"/><path d="M14 12h4"/><circle cx="8" cy="12" r="1"/><circle cx="16" cy="12" r="1"/></svg></span> Extensions
|
|
944
944
|
</a>
|
|
945
945
|
<div class="sidebar-divider"></div>
|
|
946
946
|
<div class="sidebar-section">Settings</div>
|
|
@@ -989,11 +989,11 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
989
989
|
<div class="suggestion-chips-group">
|
|
990
990
|
<div class="chip-row">
|
|
991
991
|
<button class="suggestion-chip" onclick="useSuggestion(this)">What can you do?</button>
|
|
992
|
-
<button class="suggestion-chip" onclick="useSuggestion(this)">
|
|
992
|
+
<button class="suggestion-chip" onclick="useSuggestion(this)">Set a reminder for tomorrow</button>
|
|
993
993
|
</div>
|
|
994
994
|
<div class="chip-row">
|
|
995
|
-
<button class="suggestion-chip" onclick="useSuggestion(this)">
|
|
996
|
-
<button class="suggestion-chip" onclick="useSuggestion(this)">
|
|
995
|
+
<button class="suggestion-chip" onclick="useSuggestion(this)">Help me write an email</button>
|
|
996
|
+
<button class="suggestion-chip" onclick="useSuggestion(this)">Explain something to me</button>
|
|
997
997
|
</div>
|
|
998
998
|
</div>
|
|
999
999
|
</div>
|
|
@@ -1099,6 +1099,9 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1099
1099
|
<!-- MEMORY PANEL -->
|
|
1100
1100
|
<div id="panel-memory" class="panel">
|
|
1101
1101
|
<div class="panel-body">
|
|
1102
|
+
<div style="margin-bottom:20px;">
|
|
1103
|
+
<p class="settings-desc" style="margin-bottom:0;">Everything Zubo remembers about you and your conversations. Edit the persistent memory file directly, or search through individual memory chunks below.</p>
|
|
1104
|
+
</div>
|
|
1102
1105
|
<div class="editor-wrap">
|
|
1103
1106
|
<div class="editor-toolbar">
|
|
1104
1107
|
<button class="btn btn-primary" onclick="saveMemory()">Save MEMORY.md</button>
|
|
@@ -1122,6 +1125,9 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1122
1125
|
<!-- SKILLS PANEL (with Browse/Registry tab) -->
|
|
1123
1126
|
<div id="panel-skills" class="panel">
|
|
1124
1127
|
<div class="panel-body">
|
|
1128
|
+
<div style="margin-bottom:16px;">
|
|
1129
|
+
<p class="settings-desc" style="margin-bottom:0;">Skills are custom capabilities you can add to Zubo. Browse the community registry to install new ones, or build your own in TypeScript.</p>
|
|
1130
|
+
</div>
|
|
1125
1131
|
<div class="tab-bar" id="skills-tabs">
|
|
1126
1132
|
<button class="tab active" onclick="switchTab('skills','installed')">Installed</button>
|
|
1127
1133
|
<button class="tab" onclick="switchTab('skills','browse')">Browse Registry</button>
|
|
@@ -1206,6 +1212,9 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1206
1212
|
<!-- WORKFLOWS PANEL -->
|
|
1207
1213
|
<div id="panel-workflows" class="panel">
|
|
1208
1214
|
<div class="panel-body">
|
|
1215
|
+
<div style="margin-bottom:16px;">
|
|
1216
|
+
<p class="settings-desc" style="margin-bottom:0;">Automate multi-step tasks by chaining actions together. Use pre-built recipes, ask Zubo to create one in chat, or build visually with drag-and-drop.</p>
|
|
1217
|
+
</div>
|
|
1209
1218
|
<div class="tab-bar" id="workflows-tabs">
|
|
1210
1219
|
<button class="tab active" onclick="switchTab('workflows','recipes')">Recipes</button>
|
|
1211
1220
|
<button class="tab" onclick="switchTab('workflows','custom')">Custom Workflows</button>
|
|
@@ -1305,6 +1314,9 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1305
1314
|
<!-- MCP MARKETPLACE PANEL -->
|
|
1306
1315
|
<div id="panel-mcp" class="panel">
|
|
1307
1316
|
<div class="panel-body">
|
|
1317
|
+
<div style="margin-bottom:16px;">
|
|
1318
|
+
<p class="settings-desc" style="margin-bottom:0;">Extensions use the <strong>MCP (Model Context Protocol)</strong> standard to give Zubo new tools — like accessing files, databases, GitHub, and more. Browse the marketplace or add your own.</p>
|
|
1319
|
+
</div>
|
|
1308
1320
|
<div class="tab-bar" id="mcp-tabs">
|
|
1309
1321
|
<button class="tab active" onclick="switchTab('mcp','installed')">Installed</button>
|
|
1310
1322
|
<button class="tab" onclick="switchTab('mcp','marketplace')">Marketplace</button>
|
|
@@ -1314,22 +1326,22 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1314
1326
|
<div id="mcp-installed-list" style="display:flex;flex-direction:column;gap:12px;"></div>
|
|
1315
1327
|
<div id="mcp-installed-empty" class="empty-state-card" style="display:none;">
|
|
1316
1328
|
<div class="empty-icon"><svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 12h4"/><path d="M14 12h4"/></svg></div>
|
|
1317
|
-
<h4>No
|
|
1318
|
-
<p>Browse the marketplace to add powerful
|
|
1329
|
+
<h4>No extensions installed</h4>
|
|
1330
|
+
<p>Browse the marketplace to add powerful extensions.</p>
|
|
1319
1331
|
<button class="btn btn-primary" onclick="switchTab('mcp','marketplace')">Browse Marketplace</button>
|
|
1320
1332
|
</div>
|
|
1321
1333
|
</div>
|
|
1322
1334
|
|
|
1323
1335
|
<div class="tab-content" id="mcp-tab-marketplace">
|
|
1324
1336
|
<div class="search-bar">
|
|
1325
|
-
<input id="mcp-marketplace-search" type="text" placeholder="Search
|
|
1337
|
+
<input id="mcp-marketplace-search" type="text" placeholder="Search extensions (e.g. filesystem, github, database...)" onkeydown="if(event.key==='Enter')searchMcpMarketplace()">
|
|
1326
1338
|
<button class="btn btn-primary" onclick="searchMcpMarketplace()">Search</button>
|
|
1327
1339
|
</div>
|
|
1328
1340
|
<div class="mcp-marketplace-grid" id="mcp-marketplace-results"></div>
|
|
1329
1341
|
<div id="mcp-marketplace-empty" class="empty-state-card">
|
|
1330
1342
|
<div class="empty-icon"><svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></div>
|
|
1331
|
-
<h4>Discover
|
|
1332
|
-
<p>Search the
|
|
1343
|
+
<h4>Discover Extensions</h4>
|
|
1344
|
+
<p>Search the marketplace to find and install extensions.</p>
|
|
1333
1345
|
</div>
|
|
1334
1346
|
</div>
|
|
1335
1347
|
</div>
|
|
@@ -1398,12 +1410,12 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1398
1410
|
<button class="tab active" onclick="switchTab('settings','general')">General</button>
|
|
1399
1411
|
<button class="tab" onclick="switchTab('settings','providers')">Providers</button>
|
|
1400
1412
|
<button class="tab" onclick="switchTab('settings','channels')">Channels</button>
|
|
1401
|
-
<button class="tab" onclick="switchTab('settings','mcp')">
|
|
1402
|
-
<button class="tab" onclick="switchTab('settings','routing')">
|
|
1413
|
+
<button class="tab" onclick="switchTab('settings','mcp')">Extensions</button>
|
|
1414
|
+
<button class="tab" onclick="switchTab('settings','routing')">Cost Savings</button>
|
|
1403
1415
|
<button class="tab" onclick="switchTab('settings','data')">Data</button>
|
|
1404
|
-
<button class="tab" onclick="switchTab('settings','secrets')">
|
|
1405
|
-
<button class="tab" onclick="switchTab('settings','system')">
|
|
1406
|
-
<button class="tab" onclick="switchTab('settings','cron')">
|
|
1416
|
+
<button class="tab" onclick="switchTab('settings','secrets')">API Keys</button>
|
|
1417
|
+
<button class="tab" onclick="switchTab('settings','system')">Personality</button>
|
|
1418
|
+
<button class="tab" onclick="switchTab('settings','cron')">Scheduled Tasks</button>
|
|
1407
1419
|
<button class="tab" onclick="switchTab('settings','logs')">Logs</button>
|
|
1408
1420
|
<button class="tab" onclick="switchTab('settings','privacy')">Privacy</button>
|
|
1409
1421
|
<button class="tab" onclick="switchTab('settings','budget')">Budget</button>
|
|
@@ -1413,11 +1425,11 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1413
1425
|
<!-- General Tab -->
|
|
1414
1426
|
<div class="tab-content active" id="settings-tab-general">
|
|
1415
1427
|
<div class="settings-section">
|
|
1416
|
-
<h3 class="settings-title" data-tooltip="
|
|
1417
|
-
<p class="settings-desc">
|
|
1428
|
+
<h3 class="settings-title" data-tooltip="Choose which AI powers your agent">AI Model</h3>
|
|
1429
|
+
<p class="settings-desc">Choose which AI service and model your agent uses for conversations.</p>
|
|
1418
1430
|
<div class="settings-grid">
|
|
1419
1431
|
<div class="settings-field">
|
|
1420
|
-
<label class="settings-label" data-tooltip="
|
|
1432
|
+
<label class="settings-label" data-tooltip="AI service (e.g. Anthropic, OpenAI)" for="settings-provider">Provider</label>
|
|
1421
1433
|
<select id="settings-provider" class="settings-select" onchange="onProviderChange()"></select>
|
|
1422
1434
|
</div>
|
|
1423
1435
|
<div class="settings-field">
|
|
@@ -1433,11 +1445,11 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1433
1445
|
</div>
|
|
1434
1446
|
|
|
1435
1447
|
<div class="settings-section">
|
|
1436
|
-
<h3 class="settings-title" data-tooltip="
|
|
1437
|
-
<p class="settings-desc">How often
|
|
1448
|
+
<h3 class="settings-title" data-tooltip="How often Zubo checks for tasks">Background Check Interval</h3>
|
|
1449
|
+
<p class="settings-desc">How often Zubo checks for reminders, scheduled tasks, and updates. Default: every 30 minutes.</p>
|
|
1438
1450
|
<div class="settings-grid">
|
|
1439
1451
|
<div class="settings-field">
|
|
1440
|
-
<label class="settings-label" data-tooltip="Minutes between
|
|
1452
|
+
<label class="settings-label" data-tooltip="Minutes between checks" for="settings-heartbeat">Interval (minutes)</label>
|
|
1441
1453
|
<input id="settings-heartbeat" type="number" class="settings-input" min="1" max="1440" step="1" placeholder="30">
|
|
1442
1454
|
</div>
|
|
1443
1455
|
</div>
|
|
@@ -1456,8 +1468,8 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1456
1468
|
<!-- Providers Tab -->
|
|
1457
1469
|
<div class="tab-content" id="settings-tab-providers">
|
|
1458
1470
|
<div class="settings-section">
|
|
1459
|
-
<h3 class="settings-title">
|
|
1460
|
-
<p class="settings-desc">Configure AI
|
|
1471
|
+
<h3 class="settings-title">AI Providers</h3>
|
|
1472
|
+
<p class="settings-desc">Configure AI services. Set one as active for your agent to use.</p>
|
|
1461
1473
|
<div id="providers-list" style="display:flex;flex-direction:column;gap:12px;"></div>
|
|
1462
1474
|
<div style="margin-top:20px;">
|
|
1463
1475
|
<button class="btn btn-primary" onclick="showAddProviderForm()">Add Provider</button>
|
|
@@ -1468,22 +1480,23 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1468
1480
|
<div class="settings-field">
|
|
1469
1481
|
<label class="settings-label" for="new-provider-name">Provider</label>
|
|
1470
1482
|
<select id="new-provider-name" class="settings-select" onchange="onNewProviderSelect()">
|
|
1471
|
-
<option value="">-- Select --</option>
|
|
1472
|
-
<option value="anthropic">Anthropic</option>
|
|
1473
|
-
<option value="openai">OpenAI</option>
|
|
1474
|
-
<option value="groq">Groq</option>
|
|
1475
|
-
<option value="together">Together</option>
|
|
1476
|
-
<option value="openrouter">OpenRouter</option>
|
|
1483
|
+
<option value="">-- Select a provider --</option>
|
|
1484
|
+
<option value="anthropic">Anthropic (Claude)</option>
|
|
1485
|
+
<option value="openai">OpenAI (GPT)</option>
|
|
1486
|
+
<option value="groq">Groq (fast, free tier)</option>
|
|
1487
|
+
<option value="together">Together AI</option>
|
|
1488
|
+
<option value="openrouter">OpenRouter (many models)</option>
|
|
1477
1489
|
<option value="deepseek">DeepSeek</option>
|
|
1478
1490
|
<option value="xai">xAI (Grok)</option>
|
|
1479
|
-
<option value="ollama">Ollama (
|
|
1480
|
-
<option value="lmstudio">LM Studio (
|
|
1481
|
-
<option value="custom">Custom (
|
|
1491
|
+
<option value="ollama">Ollama (free, runs locally)</option>
|
|
1492
|
+
<option value="lmstudio">LM Studio (free, runs locally)</option>
|
|
1493
|
+
<option value="custom">Custom (advanced)</option>
|
|
1482
1494
|
</select>
|
|
1483
1495
|
</div>
|
|
1484
1496
|
<div class="settings-field">
|
|
1485
1497
|
<label class="settings-label" for="new-provider-key">API Key</label>
|
|
1486
1498
|
<input id="new-provider-key" type="password" class="settings-input" placeholder="sk-...">
|
|
1499
|
+
<span id="provider-key-help" class="settings-hint" style="font-size:11px;color:var(--text-faint);margin-top:4px;display:none;"></span>
|
|
1487
1500
|
</div>
|
|
1488
1501
|
<div class="settings-field">
|
|
1489
1502
|
<label class="settings-label" for="new-provider-model">Model</label>
|
|
@@ -1532,14 +1545,14 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1532
1545
|
<!-- MCP Tab -->
|
|
1533
1546
|
<div class="tab-content" id="settings-tab-mcp">
|
|
1534
1547
|
<div class="settings-section">
|
|
1535
|
-
<h3 class="settings-title">
|
|
1536
|
-
<p class="settings-desc">
|
|
1548
|
+
<h3 class="settings-title">Extensions</h3>
|
|
1549
|
+
<p class="settings-desc">Extensions give Zubo new abilities like accessing files, databases, and APIs. Install from the marketplace or add manually.</p>
|
|
1537
1550
|
<div id="mcp-servers-list" style="display:flex;flex-direction:column;gap:12px;"></div>
|
|
1538
1551
|
<div style="margin-top:20px;">
|
|
1539
|
-
<button class="btn btn-primary" onclick="showAddMcpForm()">Add
|
|
1552
|
+
<button class="btn btn-primary" onclick="showAddMcpForm()">Add Extension</button>
|
|
1540
1553
|
</div>
|
|
1541
1554
|
<div id="mcp-add-form" style="display:none;margin-top:16px;padding:16px;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);">
|
|
1542
|
-
<h4 style="margin-bottom:12px;font-family:var(--display);font-weight:600;">Add
|
|
1555
|
+
<h4 style="margin-bottom:12px;font-family:var(--display);font-weight:600;">Add Extension</h4>
|
|
1543
1556
|
<div class="settings-grid">
|
|
1544
1557
|
<div class="settings-field">
|
|
1545
1558
|
<label class="settings-label" for="mcp-name">Name</label>
|
|
@@ -1570,8 +1583,8 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1570
1583
|
<!-- Routing Tab -->
|
|
1571
1584
|
<div class="tab-content" id="settings-tab-routing">
|
|
1572
1585
|
<div class="settings-section">
|
|
1573
|
-
<h3 class="settings-title" data-tooltip="
|
|
1574
|
-
<p class="settings-desc">
|
|
1586
|
+
<h3 class="settings-title" data-tooltip="Use a cheaper AI for simple questions">Smart Cost Savings</h3>
|
|
1587
|
+
<p class="settings-desc">Use a cheaper, faster AI for simple questions and your main AI for complex ones. Saves money without sacrificing quality.</p>
|
|
1575
1588
|
<div class="settings-grid">
|
|
1576
1589
|
<div class="settings-field">
|
|
1577
1590
|
<label class="settings-label" for="sr-enabled">Enabled</label>
|
|
@@ -1615,8 +1628,8 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1615
1628
|
<!-- Secrets Tab -->
|
|
1616
1629
|
<div class="tab-content" id="settings-tab-secrets">
|
|
1617
1630
|
<div class="settings-section" style="max-width:700px;">
|
|
1618
|
-
<h3 class="settings-title">
|
|
1619
|
-
<p class="settings-desc">Manage API keys
|
|
1631
|
+
<h3 class="settings-title">API Keys & Credentials</h3>
|
|
1632
|
+
<p class="settings-desc">Manage API keys for integrations. Your keys are encrypted and stored securely on your device — Zubo never sends them anywhere.</p>
|
|
1620
1633
|
<div style="display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap;">
|
|
1621
1634
|
<button class="btn btn-primary" onclick="showAddSecretForm()">Add Secret</button>
|
|
1622
1635
|
<button class="btn btn-ghost" onclick="loadSecrets()">Refresh</button>
|
|
@@ -1648,6 +1661,7 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1648
1661
|
|
|
1649
1662
|
<!-- System Prompt Tab -->
|
|
1650
1663
|
<div class="tab-content" id="settings-tab-system">
|
|
1664
|
+
<p class="settings-desc" style="margin-bottom:12px;">Customize your agent's personality and instructions. This controls how Zubo thinks and responds. Be careful — incorrect changes may cause errors.</p>
|
|
1651
1665
|
<div class="editor-wrap" style="min-height:calc(100vh - 220px);">
|
|
1652
1666
|
<div class="editor-toolbar">
|
|
1653
1667
|
<button class="btn btn-primary" onclick="saveSystem()">Save</button>
|
|
@@ -1728,7 +1742,7 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1728
1742
|
<div class="cards" id="budget-summary-cards"></div>
|
|
1729
1743
|
<div class="settings-section" style="margin-top:24px;">
|
|
1730
1744
|
<h3 class="settings-title">Budget Limits</h3>
|
|
1731
|
-
<p class="settings-desc">Set spending limits to control costs.
|
|
1745
|
+
<p class="settings-desc">Set spending limits to control costs. Zubo will stop responding when limits are reached — you can raise them anytime. Typical usage: ~100 messages/day costs $0.50–$2.00 depending on your AI model.</p>
|
|
1732
1746
|
<div class="settings-grid">
|
|
1733
1747
|
<div class="settings-field">
|
|
1734
1748
|
<label class="settings-label" for="budget-daily">Daily Limit (USD)</label>
|
|
@@ -1907,7 +1921,7 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1907
1921
|
<script>
|
|
1908
1922
|
// --- Panel routing ---
|
|
1909
1923
|
var panelNames = ['agent','history','dashboard','memory','skills','workflows','webhooks','mcp','integrations','settings'];
|
|
1910
|
-
var panelTitles = { agent:'Chat', history:'History', dashboard:'Dashboard', memory:'
|
|
1924
|
+
var panelTitles = { agent:'Chat', history:'History', dashboard:'Dashboard', memory:'Knowledge', skills:'Skills', workflows:'Workflows', webhooks:'Webhooks', mcp:'Extensions', integrations:'Integrations', settings:'Settings' };
|
|
1911
1925
|
|
|
1912
1926
|
// Legacy panel name mapping (old names -> new names + tab)
|
|
1913
1927
|
var legacyPanelMap = {
|
|
@@ -3790,10 +3804,28 @@ function onNewProviderSelect() {
|
|
|
3790
3804
|
document.getElementById('new-provider-url-field').style.display = showUrl ? '' : 'none';
|
|
3791
3805
|
// Local providers don't need API key
|
|
3792
3806
|
var keyInput = document.getElementById('new-provider-key');
|
|
3807
|
+
var helpEl = document.getElementById('provider-key-help');
|
|
3793
3808
|
if (name === 'ollama' || name === 'lmstudio') {
|
|
3794
|
-
keyInput.placeholder = '
|
|
3809
|
+
keyInput.placeholder = 'Not needed for local models';
|
|
3810
|
+
helpEl.style.display = 'none';
|
|
3795
3811
|
} else {
|
|
3796
3812
|
keyInput.placeholder = 'sk-...';
|
|
3813
|
+
// Show where to get the API key
|
|
3814
|
+
var keyLinks = {
|
|
3815
|
+
anthropic: 'console.anthropic.com/settings/keys',
|
|
3816
|
+
openai: 'platform.openai.com/api-keys',
|
|
3817
|
+
groq: 'console.groq.com/keys',
|
|
3818
|
+
together: 'api.together.xyz/settings/api-keys',
|
|
3819
|
+
openrouter: 'openrouter.ai/keys',
|
|
3820
|
+
deepseek: 'platform.deepseek.com/api_keys',
|
|
3821
|
+
xai: 'console.x.ai'
|
|
3822
|
+
};
|
|
3823
|
+
if (keyLinks[name]) {
|
|
3824
|
+
helpEl.textContent = 'Get your key at ' + keyLinks[name];
|
|
3825
|
+
helpEl.style.display = 'block';
|
|
3826
|
+
} else {
|
|
3827
|
+
helpEl.style.display = 'none';
|
|
3828
|
+
}
|
|
3797
3829
|
}
|
|
3798
3830
|
}
|
|
3799
3831
|
|
|
@@ -3857,7 +3889,7 @@ function loadMcpServers() {
|
|
|
3857
3889
|
if (!servers.length) {
|
|
3858
3890
|
var empty = document.createElement('div');
|
|
3859
3891
|
empty.className = 'empty-state';
|
|
3860
|
-
empty.textContent = 'No
|
|
3892
|
+
empty.textContent = 'No extensions configured. Add one to extend Zubo with new tools.';
|
|
3861
3893
|
list.appendChild(empty);
|
|
3862
3894
|
return;
|
|
3863
3895
|
}
|
|
@@ -3921,7 +3953,7 @@ function restartMcpServer(name) {
|
|
|
3921
3953
|
}
|
|
3922
3954
|
|
|
3923
3955
|
function removeMcpServer(name) {
|
|
3924
|
-
if (!confirm('Remove
|
|
3956
|
+
if (!confirm('Remove extension "' + name + '"? This will disconnect it.')) return;
|
|
3925
3957
|
api('/mcp/servers/' + encodeURIComponent(name), { method: 'DELETE' }).then(function(data) {
|
|
3926
3958
|
if (data.ok) {
|
|
3927
3959
|
toast(name + ' removed');
|
|
@@ -4363,19 +4395,19 @@ function renderCmdResults(query) {
|
|
|
4363
4395
|
var subTabs = [
|
|
4364
4396
|
{ title: 'Analytics', action: function() { showPanel('dashboard'); switchTab('dashboard','analytics'); } },
|
|
4365
4397
|
{ title: 'Performance', action: function() { showPanel('dashboard'); switchTab('dashboard','performance'); } },
|
|
4366
|
-
{ title: '
|
|
4367
|
-
{ title: '
|
|
4398
|
+
{ title: 'Personality', action: function() { showPanel('settings'); switchTab('settings','system'); } },
|
|
4399
|
+
{ title: 'Scheduled Tasks', action: function() { showPanel('settings'); switchTab('settings','cron'); } },
|
|
4368
4400
|
{ title: 'Logs', action: function() { showPanel('settings'); switchTab('settings','logs'); } },
|
|
4369
4401
|
{ title: 'Privacy & Data', action: function() { showPanel('settings'); switchTab('settings','privacy'); } },
|
|
4370
4402
|
{ title: 'Budget', action: function() { showPanel('settings'); switchTab('settings','budget'); } },
|
|
4371
|
-
{ title: '
|
|
4403
|
+
{ title: 'API Keys', action: function() { showPanel('settings'); switchTab('settings','secrets'); } },
|
|
4372
4404
|
{ title: 'Channels', action: function() { showPanel('settings'); switchTab('settings','channels'); } },
|
|
4373
4405
|
{ title: 'Providers', action: function() { showPanel('settings'); switchTab('settings','providers'); } },
|
|
4374
|
-
{ title: '
|
|
4375
|
-
{ title: '
|
|
4406
|
+
{ title: 'Extensions', action: function() { showPanel('settings'); switchTab('settings','mcp'); } },
|
|
4407
|
+
{ title: 'Cost Savings', action: function() { showPanel('settings'); switchTab('settings','routing'); } },
|
|
4376
4408
|
{ title: 'Browse Registry', action: function() { showPanel('skills'); switchTab('skills','browse'); } },
|
|
4377
4409
|
{ title: 'Visual Builder', action: function() { showPanel('workflows'); switchTab('workflows','visual'); } },
|
|
4378
|
-
{ title: '
|
|
4410
|
+
{ title: 'Extensions Marketplace', action: function() { showPanel('mcp'); switchTab('mcp','marketplace'); } },
|
|
4379
4411
|
{ title: 'Email Digests', action: function() { showPanel('settings'); switchTab('settings','digests'); } },
|
|
4380
4412
|
];
|
|
4381
4413
|
subTabs.forEach(function(st) { items.push({ name: st.title.toLowerCase(), title: st.title, action: st.action }); });
|
|
@@ -4516,7 +4548,7 @@ function clearChatMessages() {
|
|
|
4516
4548
|
subtext.textContent = 'Ask me anything, or try a suggestion below';
|
|
4517
4549
|
var chipsGroup = document.createElement('div');
|
|
4518
4550
|
chipsGroup.className = 'suggestion-chips-group';
|
|
4519
|
-
var rows = [['What can you do?','
|
|
4551
|
+
var rows = [['What can you do?','Set a reminder for tomorrow'],['Help me write an email','Explain something to me']];
|
|
4520
4552
|
rows.forEach(function(rowLabels) {
|
|
4521
4553
|
var row = document.createElement('div');
|
|
4522
4554
|
row.className = 'chip-row';
|
package/src/channels/webchat.ts
CHANGED
|
@@ -18,22 +18,22 @@ function escapeHtml(s: string): string {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/** Convert raw error messages to user-friendly messages. Prevents leaking internal details. */
|
|
21
|
-
function friendlyError(err: any): string {
|
|
22
|
-
const msg = err?.message ?? String(err);
|
|
23
|
-
if (msg.includes("401") || msg.includes("Unauthorized") || msg.includes("invalid"))
|
|
24
|
-
return "
|
|
25
|
-
if (msg.includes("429") || msg.includes("rate limit"))
|
|
26
|
-
return "
|
|
27
|
-
if (msg.includes("404") || msg.includes("not found"))
|
|
28
|
-
return "
|
|
29
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed") || msg.includes("Connection refused"))
|
|
30
|
-
return "
|
|
31
|
-
if (msg.includes("timed out") || msg.includes("timeout"))
|
|
32
|
-
return "
|
|
33
|
-
if (msg.includes("context") || msg.includes("too long"))
|
|
34
|
-
return "
|
|
35
|
-
return "Something went wrong.
|
|
36
|
-
}
|
|
21
|
+
function friendlyError(err: any): string {
|
|
22
|
+
const msg = err?.message ?? String(err);
|
|
23
|
+
if (msg.includes("401") || msg.includes("Unauthorized") || msg.includes("invalid"))
|
|
24
|
+
return "Authentication failed. Check your API key in Settings > API Keys.";
|
|
25
|
+
if (msg.includes("429") || msg.includes("rate limit"))
|
|
26
|
+
return "Too many messages too quickly. Wait a moment and try again.";
|
|
27
|
+
if (msg.includes("404") || msg.includes("not found"))
|
|
28
|
+
return "The AI model wasn't found. Check your model name in Settings > AI Model.";
|
|
29
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed") || msg.includes("Connection refused"))
|
|
30
|
+
return "Can't reach the AI service. Check your internet connection or try again.";
|
|
31
|
+
if (msg.includes("timed out") || msg.includes("timeout"))
|
|
32
|
+
return "The request took too long. The AI service may be busy — try again in a moment.";
|
|
33
|
+
if (msg.includes("context") || msg.includes("too long"))
|
|
34
|
+
return "Your message is too long. Try splitting it into shorter questions.";
|
|
35
|
+
return "Something went wrong. Try again, or check Settings if this keeps happening.";
|
|
36
|
+
}
|
|
37
37
|
|
|
38
38
|
/** Add security headers to all HTTP responses */
|
|
39
39
|
function addSecurityHeaders(res: Response): Response {
|
package/src/llm/claude-code.ts
CHANGED
|
@@ -11,7 +11,7 @@ export class ClaudeCodeProvider implements LlmProvider {
|
|
|
11
11
|
model: string;
|
|
12
12
|
contextWindow = 200_000;
|
|
13
13
|
|
|
14
|
-
constructor(model: string = "
|
|
14
|
+
constructor(model: string = "default") {
|
|
15
15
|
this.model = model;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -55,7 +55,6 @@ export class ClaudeCodeProvider implements LlmProvider {
|
|
|
55
55
|
const prompt = parts.join("\n\n");
|
|
56
56
|
|
|
57
57
|
const args = ["claude", "-p", prompt, "--output-format", "json"];
|
|
58
|
-
if (this.model) args.push("--model", this.model);
|
|
59
58
|
|
|
60
59
|
try {
|
|
61
60
|
const proc = Bun.spawn(args, {
|
package/src/llm/codex.ts
CHANGED
|
@@ -11,7 +11,7 @@ export class CodexProvider implements LlmProvider {
|
|
|
11
11
|
model: string;
|
|
12
12
|
contextWindow = 200_000;
|
|
13
13
|
|
|
14
|
-
constructor(model: string = "
|
|
14
|
+
constructor(model: string = "default") {
|
|
15
15
|
this.model = model;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -52,7 +52,6 @@ export class CodexProvider implements LlmProvider {
|
|
|
52
52
|
const prompt = parts.join("\n\n");
|
|
53
53
|
|
|
54
54
|
const args = ["codex", "exec"];
|
|
55
|
-
if (this.model) args.push("--model", this.model);
|
|
56
55
|
args.push(prompt);
|
|
57
56
|
|
|
58
57
|
try {
|
package/src/setup-web.html.ts
CHANGED
|
@@ -588,10 +588,10 @@ export const SETUP_WIZARD_HTML = `<!DOCTYPE html>
|
|
|
588
588
|
</div>
|
|
589
589
|
|
|
590
590
|
<div class="stepper" id="stepper">
|
|
591
|
-
<div class="stepper-item active" data-step="1"><div class="stepper-dot">1</div><span>Provider</span></div>
|
|
591
|
+
<div class="stepper-item active" data-step="1"><div class="stepper-dot">1</div><span>AI Provider</span></div>
|
|
592
592
|
<div class="stepper-item" data-step="2"><div class="stepper-dot">2</div><span>Channels</span></div>
|
|
593
|
-
<div class="stepper-item" data-step="3"><div class="stepper-dot">3</div><span>
|
|
594
|
-
<div class="stepper-item" data-step="4"><div class="stepper-dot">4</div><span>
|
|
593
|
+
<div class="stepper-item" data-step="3"><div class="stepper-dot">3</div><span>Personalize</span></div>
|
|
594
|
+
<div class="stepper-item" data-step="4"><div class="stepper-dot">4</div><span>Cost Savings</span></div>
|
|
595
595
|
</div>
|
|
596
596
|
|
|
597
597
|
<div class="step-panel" id="stepPanel">
|
|
@@ -725,8 +725,8 @@ function prevStep() {
|
|
|
725
725
|
|
|
726
726
|
// ── Step 1: Provider ──
|
|
727
727
|
function renderProvider() {
|
|
728
|
-
var html = '<h3 class="step-title">
|
|
729
|
-
html += '<p class="step-desc">Choose the AI
|
|
728
|
+
var html = '<h3 class="step-title">AI Provider</h3>';
|
|
729
|
+
html += '<p class="step-desc">Choose the AI service that powers your agent.</p>';
|
|
730
730
|
html += '<div class="provider-grid">';
|
|
731
731
|
PROVIDERS.forEach(function(p) {
|
|
732
732
|
html += '<div class="provider-card' + (state.activeProvider === p.id ? ' selected' : '') + '" data-pid="' + p.id + '">';
|
|
@@ -978,7 +978,7 @@ function collectProviderData() {
|
|
|
978
978
|
if (!p) return;
|
|
979
979
|
|
|
980
980
|
if (p.cli) {
|
|
981
|
-
var model =
|
|
981
|
+
var model = 'default';
|
|
982
982
|
state.providers[p.id] = { model: model };
|
|
983
983
|
} else if (p.local) {
|
|
984
984
|
var m = document.getElementById('model')?.value || p.defaultModel;
|
|
@@ -1006,7 +1006,7 @@ function collectProviderData() {
|
|
|
1006
1006
|
var fb = PROVIDERS.find(function(x) { return x.id === state.fallbackProvider; });
|
|
1007
1007
|
if (fb) {
|
|
1008
1008
|
if (fb.cli) {
|
|
1009
|
-
var fbm =
|
|
1009
|
+
var fbm = 'default';
|
|
1010
1010
|
state.providers[fb.id] = { model: fbm };
|
|
1011
1011
|
} else if (fb.local) {
|
|
1012
1012
|
var fbModel = document.getElementById('fb_model')?.value || fb.defaultModel;
|
|
@@ -1149,9 +1149,9 @@ function collectAgentData() {
|
|
|
1149
1149
|
|
|
1150
1150
|
// ── Step 4: Smart Routing ──
|
|
1151
1151
|
function renderRouting() {
|
|
1152
|
-
var html = '<h3 class="step-title">Smart
|
|
1152
|
+
var html = '<h3 class="step-title">Smart Cost Savings</h3>';
|
|
1153
1153
|
html += '<div class="routing-card">';
|
|
1154
|
-
html += '<p>
|
|
1154
|
+
html += '<p>Use a cheaper, faster AI for simple questions (greetings, one-liners) and your main AI for complex tasks. This can save 50-80% on costs.</p>';
|
|
1155
1155
|
|
|
1156
1156
|
var hasSecondProvider = state.failover.length > 0;
|
|
1157
1157
|
|
package/src/setup.ts
CHANGED
|
@@ -350,12 +350,12 @@ const PROVIDER_OPTIONS: ProviderOption[] = [
|
|
|
350
350
|
console.log(` ${DIM}npm install -g @anthropic-ai/claude-code${RESET}\n`);
|
|
351
351
|
const cont = await prompt(" Press Enter after installing, or 'skip' to continue anyway: ");
|
|
352
352
|
if (cont.toLowerCase() === "skip") {
|
|
353
|
-
return { name: "claude-code", config: { model: "
|
|
353
|
+
return { name: "claude-code", config: { model: "default" } };
|
|
354
354
|
}
|
|
355
355
|
const recheck = Bun.spawnSync(["which", "claude"], { stdout: "pipe", stderr: "pipe" });
|
|
356
356
|
if (recheck.exitCode !== 0) {
|
|
357
357
|
warn("Still not found. Config saved — install 'claude' CLI before starting.");
|
|
358
|
-
return { name: "claude-code", config: { model: "
|
|
358
|
+
return { name: "claude-code", config: { model: "default" } };
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
361
|
ok("'claude' CLI found");
|
|
@@ -380,7 +380,7 @@ const PROVIDER_OPTIONS: ProviderOption[] = [
|
|
|
380
380
|
warn("Still not authenticated. Run 'claude' in a terminal to log in before starting Zubo.");
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
|
-
return { name: "claude-code", config: { model: "
|
|
383
|
+
return { name: "claude-code", config: { model: "default" } };
|
|
384
384
|
},
|
|
385
385
|
},
|
|
386
386
|
{
|
|
@@ -394,12 +394,12 @@ const PROVIDER_OPTIONS: ProviderOption[] = [
|
|
|
394
394
|
console.log(` ${DIM}npm install -g @openai/codex${RESET}\n`);
|
|
395
395
|
const cont = await prompt(" Press Enter after installing, or 'skip' to continue anyway: ");
|
|
396
396
|
if (cont.toLowerCase() === "skip") {
|
|
397
|
-
return { name: "codex", config: { model: "
|
|
397
|
+
return { name: "codex", config: { model: "default" } };
|
|
398
398
|
}
|
|
399
399
|
const recheck = Bun.spawnSync(["which", "codex"], { stdout: "pipe", stderr: "pipe" });
|
|
400
400
|
if (recheck.exitCode !== 0) {
|
|
401
401
|
warn("Still not found. Config saved — install 'codex' CLI before starting.");
|
|
402
|
-
return { name: "codex", config: { model: "
|
|
402
|
+
return { name: "codex", config: { model: "default" } };
|
|
403
403
|
}
|
|
404
404
|
}
|
|
405
405
|
ok("'codex' CLI found");
|
|
@@ -412,7 +412,7 @@ const PROVIDER_OPTIONS: ProviderOption[] = [
|
|
|
412
412
|
} else {
|
|
413
413
|
ok("Codex ready");
|
|
414
414
|
}
|
|
415
|
-
return { name: "codex", config: { model: "
|
|
415
|
+
return { name: "codex", config: { model: "default" } };
|
|
416
416
|
},
|
|
417
417
|
},
|
|
418
418
|
{
|
package/src/tools/executor.ts
CHANGED
|
@@ -121,7 +121,7 @@ export async function executeTool(
|
|
|
121
121
|
logger.warn(`Tool blocked by allowedTools: ${name}`);
|
|
122
122
|
return {
|
|
123
123
|
tool_use_id: toolUseId,
|
|
124
|
-
content: `Error:
|
|
124
|
+
content: `Error: The '${name}' feature is not available right now.`,
|
|
125
125
|
is_error: true,
|
|
126
126
|
};
|
|
127
127
|
}
|
|
@@ -141,7 +141,7 @@ export async function executeTool(
|
|
|
141
141
|
logger.warn(`Tool denied: ${name}`);
|
|
142
142
|
return {
|
|
143
143
|
tool_use_id: toolUseId,
|
|
144
|
-
content: `Error:
|
|
144
|
+
content: `Error: The '${name}' feature is not permitted. You can change permissions in Settings.`,
|
|
145
145
|
is_error: true,
|
|
146
146
|
};
|
|
147
147
|
}
|
|
@@ -46,7 +46,7 @@ export async function searchRegistry(
|
|
|
46
46
|
if (cached) return cached;
|
|
47
47
|
|
|
48
48
|
try {
|
|
49
|
-
const url = `${BASE_URL}/servers?
|
|
49
|
+
const url = `${BASE_URL}/servers?search=${encodeURIComponent(query)}&limit=${limit}`;
|
|
50
50
|
const res = await fetch(url);
|
|
51
51
|
if (!res.ok) {
|
|
52
52
|
logger.warn(`MCP registry search failed: ${res.status} ${res.statusText}`);
|