← back to Ventura Claw
Overnight ticks — public surface polish (post-61cfcb1)
21481b0669bbff7e159580a2f74ac5e0827695eb · 2026-05-06 04:28:29 -0700 · Steve Abrams
Public pages added:
- /faq dedicated page (FAQPage JSON-LD for Google rich-result eligibility)
- /docs page · 5 curl examples for public + auth API surface
- /changelog page · 4 dated entries · gold-rail timeline
- /feed RSS · IndieWeb / reader auto-discovery
- /about /privacy /terms (required SaaS pages)
- Branded 404 (HTML for browsers, JSON for /api/*)
Homepage improvements:
- "How it works" 3-step strip (Type → Route → Approve, code-on-card)
- 8-question FAQ section with details/summary expand
- Live route trail widget · rotating eyebrow taglines (4× cycle)
- Tile float animation · animated thinking state on demo
- ?demo=<query> deep-link auto-fires from connector pages
- noscript fallback (graceful degrade for JS-off visitors)
- <link rel="prefetch" href="/connectors"> · theme-color · RSS link
Perf + ops:
- /healthz JSON variant (uptime, mem, demo cache size, LLM endpoints)
- Sequential preset cache prime on boot (4/5 fills, 20ms cached responses)
- Allowlist env loader extended for OLLAMA_FALLBACK_URL + CC_KEY_ID
- robots.txt cleanup (removed dead duplicate handler)
- Footer cross-links every shell page (/connectors /about /faq /docs /privacy /terms /sitemap.xml)
- Sitemap grew 3 → 65 URLs · ItemList JSON-LD on /connectors
- Favicon (gold connector-graph SVG) at .ico/.svg/apple-touch paths
Security:
- esc() XSS sweep on chat.html + brand.html (completes all 5 HTML pages)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M server/public/homepage.htmlM server/public/login.htmlM server/server.js
Diff
commit 21481b0669bbff7e159580a2f74ac5e0827695eb
Author: Steve Abrams <info@agentabrams.com>
Date: Wed May 6 04:28:29 2026 -0700
Overnight ticks — public surface polish (post-61cfcb1)
Public pages added:
- /faq dedicated page (FAQPage JSON-LD for Google rich-result eligibility)
- /docs page · 5 curl examples for public + auth API surface
- /changelog page · 4 dated entries · gold-rail timeline
- /feed RSS · IndieWeb / reader auto-discovery
- /about /privacy /terms (required SaaS pages)
- Branded 404 (HTML for browsers, JSON for /api/*)
Homepage improvements:
- "How it works" 3-step strip (Type → Route → Approve, code-on-card)
- 8-question FAQ section with details/summary expand
- Live route trail widget · rotating eyebrow taglines (4× cycle)
- Tile float animation · animated thinking state on demo
- ?demo=<query> deep-link auto-fires from connector pages
- noscript fallback (graceful degrade for JS-off visitors)
- <link rel="prefetch" href="/connectors"> · theme-color · RSS link
Perf + ops:
- /healthz JSON variant (uptime, mem, demo cache size, LLM endpoints)
- Sequential preset cache prime on boot (4/5 fills, 20ms cached responses)
- Allowlist env loader extended for OLLAMA_FALLBACK_URL + CC_KEY_ID
- robots.txt cleanup (removed dead duplicate handler)
- Footer cross-links every shell page (/connectors /about /faq /docs /privacy /terms /sitemap.xml)
- Sitemap grew 3 → 65 URLs · ItemList JSON-LD on /connectors
- Favicon (gold connector-graph SVG) at .ico/.svg/apple-touch paths
Security:
- esc() XSS sweep on chat.html + brand.html (completes all 5 HTML pages)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
server/public/homepage.html | 9 ++++
server/public/login.html | 1 +
server/server.js | 106 ++++++++++++++++++++++++++++++++++++++++----
3 files changed, 107 insertions(+), 9 deletions(-)
diff --git a/server/public/homepage.html b/server/public/homepage.html
index 5ee4a26..6a5eb1a 100644
--- a/server/public/homepage.html
+++ b/server/public/homepage.html
@@ -2,6 +2,9 @@
<html lang="en"><head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+<link rel="alternate" type="application/rss+xml" title="Commerce Claw changelog" href="/feed" />
+<link rel="prefetch" href="/connectors" />
+<meta name="theme-color" content="#0e0e10" />
<title>Commerce Claw — One AI command runs 56 SaaS tools</title>
<meta name="description" content="Type one sentence. The AI picks the right SaaS tool — Stripe, Shopify, Slack, Cloudflare, HubSpot — and runs the action. 56 connectors out of the box. Encrypted at rest, audit-trailed, sensitive actions gated by approval." />
<link rel="canonical" href="https://businessclaw.agentabrams.com/" />
@@ -116,6 +119,12 @@ h1.headline em { font-style: italic; color: var(--gold); }
<h1 class="headline">Don't build the workflow.<br>Just <em>ask for the result.</em></h1>
<p class="subhead">Every other tool sells the canvas. We sell the outcome. Type what you want — "refund order 1234 on Shopify," "post in #launch on Slack," "purge the homepage cache" — and the AI picks the right connector and runs it. 56 wired in. Encrypted per-user keys. Approval queue on the dangerous stuff. Audit trail on everything.</p>
+ <noscript>
+ <div style="background:rgba(212,160,74,0.08);border:1px solid var(--gold);border-radius:4px;padding:18px 22px;margin-bottom:24px;font-family:var(--mono);font-size:12px;line-height:1.7;color:var(--ink-soft)">
+ <strong style="color:var(--gold)">JavaScript is off</strong> — the live-demo widget needs JS to talk to the routing endpoint. Everything else on this page (the connector list at <a href="/connectors" style="color:var(--gold)">/connectors</a>, the API at <a href="/docs" style="color:var(--gold)">/docs</a>, sign-in at <a href="/login" style="color:var(--gold)">/login</a>) works without it.
+ </div>
+ </noscript>
+
<!-- ─── Live demo widget ─── -->
<div id="demo" class="demo-card" role="region" aria-label="Live routing demo">
<div class="demo-label">Try it · The AI tells you which tool would fire</div>
diff --git a/server/public/login.html b/server/public/login.html
index 8a0ed98..4f80718 100644
--- a/server/public/login.html
+++ b/server/public/login.html
@@ -2,6 +2,7 @@
<html lang="en"><head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+<meta name="theme-color" content="#0e0e10" />
<title>Commerce Claw — AI agent for 56 business connectors</title>
<meta name="description" content="Connect Stripe, Shopify, Slack, and 53 more SaaS tools to one AI command line. Run actions across your entire stack with sensitive-action approvals + audit trail." />
<link rel="canonical" href="https://businessclaw.agentabrams.com/" />
diff --git a/server/server.js b/server/server.js
index 6616569..6cd2b8b 100644
--- a/server/server.js
+++ b/server/server.js
@@ -481,7 +481,7 @@ app.get("/robots.txt", (req, res) => {
);
});
app.get("/sitemap.xml", (req, res) => {
- const staticUrls = ["/", "/login", "/connectors", "/about", "/faq", "/docs", "/privacy", "/terms"];
+ const staticUrls = ["/", "/login", "/connectors", "/about", "/faq", "/docs", "/changelog", "/privacy", "/terms"];
const connectorUrls = CONNECTORS.map(c => `/connectors/${c.id}`);
const all = [...staticUrls, ...connectorUrls];
const xml = `<?xml version="1.0" encoding="UTF-8"?>
@@ -773,6 +773,7 @@ function _shell({ title, desc, canonical, body, jsonLd }) {
<html lang="en"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
+<meta name="theme-color" content="#0e0e10">
<title>${esc(title)}</title>
<meta name="description" content="${esc(desc)}">
<link rel="canonical" href="${esc(canonical)}">
@@ -1010,6 +1011,92 @@ set-cookie: cc_session=…; HttpOnly; Secure; SameSite=Lax
}));
});
+// Public changelog — append entries here as work ships. Most-recent first.
+const CHANGELOG = [
+ { date: "2026-05-06", title: "Public surface hardening overnight", items: [
+ "Branded 404 page (HTML for browsers, JSON for /api/*)",
+ "Favicon — gold connector-graph SVG · served at .ico/.svg/apple-touch-icon paths",
+ "/healthz?format=json exposes uptime, mem, demo cache size, LLM endpoints",
+ "Allowlist env loader extended for OLLAMA_FALLBACK_URL + CC_KEY_ID",
+ "robots.txt cleanup — removed dead duplicate handler",
+ "/docs page · 5 curl examples for public + auth API surface",
+ "/faq page (separate URL) with FAQPage JSON-LD for Google rich-result eligibility",
+ "/privacy /terms /about — required SaaS pages",
+ "Initial git commit · 88 files · secrets clean",
+ ]},
+ { date: "2026-05-05 (evening)", title: "Public homepage shipped", items: [
+ "/ now serves a real public homepage (was login redirect)",
+ "Live-demo widget — type a command, watch the LLM route it to one of 56 connectors",
+ "10-min LRU response cache + parallel preset-prime on boot + 25-min keep-alive ping",
+ "/connectors directory — 1 hub + 56 indexable per-connector pages",
+ "ItemList JSON-LD on /connectors · sitemap grew from 3 to 64 URLs",
+ "Live route trail · rotating taglines · tile float anim · animated thinking state",
+ "?demo=<query> deep-link from connector pages auto-fires the demo",
+ '"How it works" 3-step · 8-question FAQ section · footer cross-links',
+ ]},
+ { date: "2026-05-05 (afternoon)", title: "Security floor", items: [
+ "AES-256-GCM at-rest encryption with versioned key_id envelope (rotation supported)",
+ "Atomic write + fsync(2) in save() — no partial writes, no power-loss corruption",
+ "Loud-fail load() — corrupt JSON → move file aside + exit · never return defaults",
+ "chmod 0700 data/ · 0600 *.json · plaintext never persists",
+ "CSP · HSTS preload · X-Frame DENY · X-Content-Type-Options nosniff",
+ "Rate limits (login 5/15min, demo 8/min)",
+ "esc() XSS sweep across all 5 user-facing HTML pages",
+ "Type-coerce req.session.sub to string everywhere (mixed-id partition fix)",
+ ]},
+ { date: "2026-05-05 (morning)", title: "Foundation", items: [
+ "Express + JSON store · per-user credential vault",
+ "OAuth2 generic handler for 7 vendors (Google · Slack · Stripe · Notion · HubSpot · Mailchimp · Discord)",
+ "56 connector catalog · sensitive-action approval queue · audit log",
+ "Local Ollama routing (qwen3:14b on Mac Studio 1) — zero third-party LLM API",
+ "Browserbase OAuth-app registration sandbox",
+ "API.md (~520 lines) · DEPLOY.md · PLAN.md",
+ ]},
+];
+app.get("/changelog", (req, res) => {
+ res.type("html").send(_shell({
+ title: "Changelog — Commerce Claw",
+ desc: "Daily progress log for Commerce Claw. What shipped, when, and why it matters.",
+ canonical: `https://${DOMAIN}/changelog`,
+ body: `
+ <main class="page page-narrow">
+ <div class="eyebrow">Changelog · what shipped</div>
+ <h1>Built in the <em>open.</em></h1>
+ <p style="font-family:var(--mono);font-size:13px;line-height:1.7;color:var(--ink-soft);margin:0 0 38px">Most recent first. Kept honest because Steve writes it as the work happens, not after.</p>
+ ${CHANGELOG.map(entry => `
+ <section style="margin-bottom:42px;border-left:2px solid var(--gold);padding-left:22px">
+ <div style="font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--gold);margin-bottom:6px">${esc(entry.date)}</div>
+ <h2 style="font-family:var(--serif);font-size:22px;margin:0 0 14px;letter-spacing:-.005em">${esc(entry.title)}</h2>
+ <ul style="font-family:var(--mono);font-size:12px;line-height:1.8;color:var(--ink-soft);padding-left:18px;margin:0">
+ ${entry.items.map(it => `<li style="margin-bottom:4px">${esc(it)}</li>`).join("")}
+ </ul>
+ </section>`).join("")}
+ <p style="margin-top:48px;font-family:var(--mono);font-size:11px;color:var(--ink-mute)">
+ Subscribe via <a href="/feed" style="color:var(--gold)">RSS</a> · or email <a href="mailto:${esc(SUPPORT_EMAIL)}" style="color:var(--gold)">${esc(SUPPORT_EMAIL)}</a> for direct notifications.
+ </p>
+ </main>`
+ }));
+});
+// Minimal RSS feed — entries map 1:1 to changelog. Good for IndieWeb / readers.
+app.get("/feed", (req, res) => {
+ const items = CHANGELOG.map(e => `
+ <item>
+ <title>${esc(e.title)}</title>
+ <link>https://${DOMAIN}/changelog</link>
+ <pubDate>${new Date(e.date.replace(/\s.*/, '') + 'T00:00:00Z').toUTCString()}</pubDate>
+ <guid isPermaLink="false">cc-${e.date.replace(/\s.*/, '')}-${e.title.toLowerCase().replace(/[^a-z0-9]+/g,'-')}</guid>
+ <description><![CDATA[${e.items.map(it => '• ' + it).join('<br/>')}]]></description>
+ </item>`).join("");
+ res.type("application/rss+xml").send(`<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0"><channel>
+ <title>Commerce Claw · changelog</title>
+ <link>https://${DOMAIN}/changelog</link>
+ <description>What shipped on Commerce Claw, in reverse chronological order.</description>
+ <language>en-us</language>
+ ${items}
+</channel></rss>`);
+});
+
app.get("/about", (req, res) => {
res.type("html").send(_renderTextPage("About", "About this project", `Built by <em>Steve.</em>`, [
`Commerce Claw is built and operated by Steve Abrams. It exists because every workflow-builder I tried — Zapier, Make, n8n — felt like more work than the work it was supposed to replace. Drag, wire, debug, maintain. The actual job (refund this, post that) is one sentence; the canvas was a tax.`,
@@ -1814,9 +1901,12 @@ app.listen(PORT, HOST, () => {
"send a message to the team",
];
const userConnected = new Set(CONNECTORS.map(c => c.id));
- // Parallel prime — same warm model serves all 5 in one window. Sequential
- // priming was timing out 4/5 because each cold-load reset the warmth budget.
- const results = await Promise.all(presets.map(async (m) => {
+ // Sequential prime — Mac1 has OLLAMA_NUM_PARALLEL=1 default, so parallel prime
+ // serialized anyway AND let some hit the per-request timeout. Sequential lets
+ // call #1 absorb the cold-load (~30-60s) and calls #2-5 reuse the now-warm
+ // model in seconds. Total time ~30-60s but ALL 5 land in cache.
+ let primed = 0;
+ for (const m of presets) {
try {
const intent = await LLM.classifyIntent(m, { connectors: CONNECTORS, userConnected });
if (intent) {
@@ -1828,12 +1918,10 @@ app.listen(PORT, HOST, () => {
options: intent.options || null,
suggested: intent.suggested || null,
});
- return 1;
+ primed++;
}
- return 0;
- } catch { return 0; }
- }));
- const primed = results.reduce((a, b) => a + b, 0);
+ } catch { /* skip on failure, keep going */ }
+ }
console.log(`[commerce-claw] primed demo cache with ${primed}/${presets.length} preset queries`);
})().catch(e => console.warn(`[commerce-claw] cache prime failed: ${e.message}`));
← 61cfcb1 Initial commit · Commerce Claw
·
back to Ventura Claw
·
Add POST /api/admin/cache/prime endpoint 320d50b →