vibespot 1.5.0 → 1.5.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibespot",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "AI-powered HubSpot CMS landing page builder — vibe coding & React converter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,10 +20,13 @@
20
20
  "test": "vitest run",
21
21
  "test:watch": "vitest",
22
22
  "prepublishOnly": "npm run build",
23
- "contact-monitor": "tsx scripts/contact-monitor.ts"
23
+ "contact-monitor": "tsx scripts/contact-monitor.ts",
24
+ "docker:publish": "scripts/docker-publish.sh",
25
+ "docker:publish:multi": "scripts/docker-publish.sh --multi-arch",
26
+ "docker:smoke": "scripts/docker-publish.sh --smoke --dry-run"
24
27
  },
25
28
  "dependencies": {
26
- "@anthropic-ai/sdk": "^0.92.0",
29
+ "@anthropic-ai/sdk": "^0.96.0",
27
30
  "@clack/prompts": "^1.3.0",
28
31
  "@codemirror/lang-css": "^6.3.1",
29
32
  "@codemirror/lang-html": "^6.4.11",
@@ -1300,20 +1300,60 @@ testimonial carousel, and a minimal footer with social links.</code></pre>
1300
1300
  ============================================================ -->
1301
1301
  <div class="doc-section" id="docker-deployment">
1302
1302
  <h2 id="docker-deployment-heading">Docker Deployment <a href="#docker-deployment" class="doc-anchor">#</a></h2>
1303
- <p>Run vibeSpot as a containerised service for your team. The Docker image bundles the server, UI, and all dependencies &mdash; just add an AI API key.</p>
1303
+ <p>Run vibeSpot as a containerised service for your team. The Docker image bundles the server, UI, and all dependencies &mdash; just add an AI API key. The image is published to the GitHub Container Registry and is <strong>public</strong>, so no login or repository checkout is required to pull it.</p>
1304
+
1305
+ <pre><code>docker pull ghcr.io/borismichel/vibespot:latest</code></pre>
1306
+ <p>Tags: <code>latest</code> (most recent release), <code>1.5.0</code> / <code>1.5</code> (specific version), <code>main</code> (latest commit, may be unstable). Pin a version for production.</p>
1304
1307
 
1305
1308
  <h3 id="docker-quick-start">Quick Start (LAN / VPN) <a href="#docker-quick-start" class="doc-anchor">#</a></h3>
1306
- <pre><code>cp .env.example .env
1307
- # Edit .env &mdash; set at least one AI API key (e.g. ANTHROPIC_API_KEY)
1308
- docker compose up -d</code></pre>
1309
- <p>Open <code>http://&lt;host-ip&gt;:4200</code> in a browser. All AI keys, engine selection, and HubSpot credentials are configured via the <code>.env</code> file &mdash; no interactive setup needed.</p>
1309
+ <p>Run the public image directly &mdash; no repository or compose file needed:</p>
1310
+ <pre><code>docker run -d --name vibespot \
1311
+ -p 4200:4200 \
1312
+ -e VIBESPOT_AI_ENGINE=anthropic-api \
1313
+ -e VIBESPOT_AGENTIC_MODE=true \
1314
+ -e ANTHROPIC_API_KEY=sk-ant-... \
1315
+ -v vibespot-config:/home/vibespot/.vibespot \
1316
+ -v vibespot-themes:/home/vibespot/vibespot-themes \
1317
+ ghcr.io/borismichel/vibespot:latest</code></pre>
1318
+ <p>Open <code>http://&lt;host-ip&gt;:4200</code> in a browser. Swap the engine and key for whichever provider you use. The two volumes keep config and generated themes across restarts.</p>
1310
1319
 
1311
1320
  <h3 id="docker-https">HTTPS with Caddy <a href="#docker-https" class="doc-anchor">#</a></h3>
1312
- <p>For public or semi-public deployments, activate the Caddy reverse proxy profile. Caddy auto-provisions a TLS certificate from Let&rsquo;s Encrypt.</p>
1313
- <pre><code>cp .env.example .env
1314
- # Set VIBESPOT_DOMAIN=vibespot.example.com and your AI key
1315
- docker compose --profile https up -d</code></pre>
1316
- <p>Caddy binds ports 80 and 443. Point your DNS A record at the host, and HTTPS works automatically.</p>
1321
+ <p>For public or semi-public deployments, put a <a href="https://caddyserver.com/" target="_blank">Caddy</a> reverse proxy in front to auto-provision a Let&rsquo;s Encrypt TLS certificate. Save a <code>docker-compose.yml</code> and <code>Caddyfile</code> in a directory, then run <code>docker compose up -d</code>.</p>
1322
+ <pre><code># docker-compose.yml
1323
+ services:
1324
+ vibespot:
1325
+ image: ghcr.io/borismichel/vibespot:latest
1326
+ restart: unless-stopped
1327
+ environment:
1328
+ VIBESPOT_AI_ENGINE: anthropic-api
1329
+ VIBESPOT_AGENTIC_MODE: "true"
1330
+ ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
1331
+ volumes:
1332
+ - vibespot-config:/home/vibespot/.vibespot
1333
+ - vibespot-themes:/home/vibespot/vibespot-themes
1334
+ expose: ["4200"]
1335
+ caddy:
1336
+ image: caddy:2-alpine
1337
+ restart: unless-stopped
1338
+ ports: ["80:80", "443:443"]
1339
+ volumes:
1340
+ - ./Caddyfile:/etc/caddy/Caddyfile:ro
1341
+ - caddy-data:/data
1342
+ depends_on: [vibespot]
1343
+ volumes:
1344
+ vibespot-config:
1345
+ vibespot-themes:
1346
+ caddy-data:</code></pre>
1347
+ <pre><code># Caddyfile (set your real domain)
1348
+ vibespot.example.com {
1349
+ @websockets {
1350
+ header Connection *Upgrade*
1351
+ header Upgrade websocket
1352
+ }
1353
+ reverse_proxy @websockets vibespot:4200
1354
+ reverse_proxy vibespot:4200
1355
+ }</code></pre>
1356
+ <p>Caddy binds ports 80 and 443. Point your DNS A record at the host, and HTTPS works automatically. The <code>@websockets</code> rule is required &mdash; the chat and generation pipeline run over a persistent WebSocket.</p>
1317
1357
 
1318
1358
  <h3 id="docker-env-vars">Environment Variables <a href="#docker-env-vars" class="doc-anchor">#</a></h3>
1319
1359
  <p>The Docker image reads all configuration from environment variables. The <code>.env.example</code> file documents every option.</p>
@@ -1324,7 +1364,6 @@ docker compose --profile https up -d</code></pre>
1324
1364
  </thead>
1325
1365
  <tbody>
1326
1366
  <tr><td><code>VIBESPOT_PORT</code></td><td><code>4200</code></td><td>Port the server listens on inside the container</td></tr>
1327
- <tr><td><code>VIBESPOT_DOMAIN</code></td><td>&mdash;</td><td>Public domain for the Caddy HTTPS profile</td></tr>
1328
1367
  <tr><td><code>VIBESPOT_NO_OPEN</code></td><td><code>1</code></td><td>Suppress auto-open browser (set automatically in Docker)</td></tr>
1329
1368
  <tr><td><code>VIBESPOT_AI_ENGINE</code></td><td>&mdash;</td><td>Default engine: <code>anthropic-api</code>, <code>openai-api</code>, <code>gemini-api</code>, <code>langdock-api</code></td></tr>
1330
1369
  <tr><td><code>VIBESPOT_AGENTIC_MODE</code></td><td>&mdash;</td><td>Set <code>true</code> to enable the multi-stage agentic pipeline</td></tr>
@@ -1348,9 +1387,9 @@ docker compose --profile https up -d</code></pre>
1348
1387
  <tr><td><code>vibespot-themes</code></td><td><code>/home/vibespot/vibespot-themes</code></td><td>Generated HubSpot themes</td></tr>
1349
1388
  </tbody>
1350
1389
  </table>
1351
- <p>To back up:</p>
1352
- <pre><code>docker compose cp vibespot:/home/vibespot/.vibespot ./backup-config
1353
- docker compose cp vibespot:/home/vibespot/vibespot-themes ./backup-themes</code></pre>
1390
+ <p>Mount the themes volume at exactly <code>/home/vibespot/vibespot-themes</code> &mdash; that is where the app writes generated themes. Without it, themes are lost when the container is recreated. To back up:</p>
1391
+ <pre><code>docker cp vibespot:/home/vibespot/.vibespot ./backup-config
1392
+ docker cp vibespot:/home/vibespot/vibespot-themes ./backup-themes</code></pre>
1354
1393
 
1355
1394
  <h3 id="docker-reverse-proxy">Reverse Proxy &amp; Kubernetes <a href="#docker-reverse-proxy" class="doc-anchor">#</a></h3>
1356
1395
  <p>If you already have a reverse proxy (nginx, Traefik, k8s Ingress), skip the Caddy profile and point your proxy at port 4200. Key requirements:</p>
@@ -1360,8 +1399,8 @@ docker compose cp vibespot:/home/vibespot/vibespot-themes ./backup-themes</code>
1360
1399
  <li><strong>No buffering</strong> &mdash; for streaming AI responses, disable proxy buffering.</li>
1361
1400
  </ul>
1362
1401
  <div class="doc-callout doc-callout--tip">
1363
- <div class="doc-callout__label">&#128214; Full guide</div>
1364
- <p>For nginx configs, Kubernetes manifests, security considerations, and troubleshooting, see the complete <a href="https://github.com/borismichel/vibespot/blob/main/docs/docker-deployment.md" target="_blank">Docker Deployment Guide</a>.</p>
1402
+ <div class="doc-callout__label">&#9888;&#65039; Single instance &amp; auth</div>
1403
+ <p>vibeSpot keeps active sessions in memory &mdash; run a single instance (no horizontal scaling without sticky sessions). It runs as a non-root user and has <strong>no built-in authentication</strong>. For internet-facing deployments, put an authenticating proxy (OAuth2 Proxy, Authelia, Cloudflare Access) in front. Persistence is filesystem-only on the named volumes &mdash; there is no database to run.</p>
1365
1404
  </div>
1366
1405
  </div>
1367
1406
 
Binary file
package/ui/settings.js CHANGED
@@ -156,8 +156,43 @@ function renderAITab(body, data) {
156
156
  }
157
157
  section.appendChild(selectEl);
158
158
 
159
- // Model selector
159
+ // Langdock provider selector — shown only when Langdock is the active engine
160
160
  const activeEngine = config.aiEngine || (env.availableEngines.length > 0 ? env.availableEngines[0] : null);
161
+ const langdockProvider = config.langdockProvider || "anthropic";
162
+
163
+ if (activeEngine === "langdock-api") {
164
+ const providerRow = el("div", "settings__model-row");
165
+ const providerLabel = el("span", "settings__card-label");
166
+ providerLabel.textContent = "Provider";
167
+ providerRow.appendChild(providerLabel);
168
+
169
+ const providerSelect = el("select", "settings__model-select");
170
+ const providers = [
171
+ { id: "anthropic", label: "Anthropic (Claude)" },
172
+ { id: "openai", label: "OpenAI (GPT)" },
173
+ { id: "google", label: "Google (Gemini)" },
174
+ { id: "mistral", label: "Mistral" },
175
+ ];
176
+ for (const p of providers) {
177
+ const opt = document.createElement("option");
178
+ opt.value = p.id;
179
+ opt.textContent = p.label;
180
+ if (p.id === langdockProvider) opt.selected = true;
181
+ providerSelect.appendChild(opt);
182
+ }
183
+ providerSelect.addEventListener("change", async () => {
184
+ await fetch("/api/settings", {
185
+ method: "POST",
186
+ headers: { "Content-Type": "application/json" },
187
+ body: JSON.stringify({ langdockProvider: providerSelect.value }),
188
+ });
189
+ refreshSettings();
190
+ });
191
+ providerRow.appendChild(providerSelect);
192
+ section.appendChild(providerRow);
193
+ }
194
+
195
+ // Model selector
161
196
  if (activeEngine) {
162
197
  const modelRow = el("div", "settings__model-row");
163
198
  const modelLabel = el("span", "settings__card-label");
@@ -433,7 +468,10 @@ function renderAICapabilitiesSection(activeEngine, config) {
433
468
  section.appendChild(desc("Advanced model features. Some are configurable directly; some auto-engage based on the active engine."));
434
469
 
435
470
  // Engine classification — different engines have different feature surfaces.
436
- const isAnthropicAPI = activeEngine === "anthropic-api" || activeEngine === "claude-oauth";
471
+ // Langdock inherits capabilities from the selected upstream provider.
472
+ const ldProv = (config.langdockProvider || "anthropic");
473
+ const isLangdockAnthropic = activeEngine === "langdock-api" && ldProv === "anthropic";
474
+ const isAnthropicAPI = activeEngine === "anthropic-api" || activeEngine === "claude-oauth" || isLangdockAnthropic;
437
475
  const isClaudeCode = activeEngine === "claude-code";
438
476
  const isAnthropicAny = isAnthropicAPI || isClaudeCode;
439
477
 
@@ -1473,14 +1511,38 @@ function getModelsForEngine(engine) {
1473
1511
  { id: "gpt-5.4-nano", label: "GPT-5.4 Nano" },
1474
1512
  { id: "codex-mini-latest", label: "Codex Mini (latest)" },
1475
1513
  ];
1476
- case "langdock-api":
1477
- return [
1478
- { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 (default)" },
1479
- { id: "claude-opus-4-7", label: "Claude Opus 4.7" },
1480
- { id: "claude-opus-4-6", label: "Claude Opus 4.6" },
1481
- { id: "claude-sonnet-4-5", label: "Claude Sonnet 4.5" },
1482
- { id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5" },
1483
- ];
1514
+ case "langdock-api": {
1515
+ const ldProvider = (settingsData && settingsData.config && settingsData.config.langdockProvider) || "anthropic";
1516
+ const ldModels = {
1517
+ anthropic: [
1518
+ { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 (default)" },
1519
+ { id: "claude-opus-4-7", label: "Claude Opus 4.7" },
1520
+ { id: "claude-opus-4-6", label: "Claude Opus 4.6" },
1521
+ { id: "claude-sonnet-4-5", label: "Claude Sonnet 4.5" },
1522
+ { id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5" },
1523
+ ],
1524
+ openai: [
1525
+ { id: "gpt-4.1", label: "GPT-4.1 (default)" },
1526
+ { id: "gpt-4.1-mini", label: "GPT-4.1 Mini" },
1527
+ { id: "gpt-4.1-nano", label: "GPT-4.1 Nano" },
1528
+ { id: "gpt-4o", label: "GPT-4o" },
1529
+ { id: "o3-mini", label: "o3 Mini" },
1530
+ ],
1531
+ google: [
1532
+ { id: "gemini-2.5-pro", label: "Gemini 2.5 Pro (default)" },
1533
+ { id: "gemini-2.5-flash", label: "Gemini 2.5 Flash" },
1534
+ { id: "gemini-2.0-flash", label: "Gemini 2.0 Flash" },
1535
+ ],
1536
+ mistral: [
1537
+ { id: "mistral-large-latest", label: "Mistral Large (default)" },
1538
+ { id: "mistral-medium-latest", label: "Mistral Medium" },
1539
+ { id: "mistral-small-latest", label: "Mistral Small" },
1540
+ { id: "codestral-latest", label: "Codestral" },
1541
+ { id: "pixtral-large-latest", label: "Pixtral Large" },
1542
+ ],
1543
+ };
1544
+ return ldModels[ldProvider] || ldModels.anthropic;
1545
+ }
1484
1546
  default:
1485
1547
  return [];
1486
1548
  }
@@ -1495,7 +1557,11 @@ function getCurrentModel(engine, config) {
1495
1557
  case "codex-cli": return config.codexCliModel || "gpt-5.5";
1496
1558
  case "gemini-cli": return config.geminiCliModel || "gemini-2.5-pro";
1497
1559
  case "gemini-api": return config.geminiApiModel || "gemini-2.5-pro";
1498
- case "langdock-api": return config.langdockApiModel || "claude-sonnet-4-6";
1560
+ case "langdock-api": {
1561
+ if (config.langdockApiModel) return config.langdockApiModel;
1562
+ const defaults = { anthropic: "claude-sonnet-4-6", openai: "gpt-4.1", google: "gemini-2.5-pro", mistral: "mistral-large-latest" };
1563
+ return defaults[config.langdockProvider || "anthropic"] || "claude-sonnet-4-6";
1564
+ }
1499
1565
  default: return null;
1500
1566
  }
1501
1567
  }