diff --git a/.claude/skills/time/SKILL.md b/.claude/skills/time/SKILL.md index 3544458..8386400 100644 --- a/.claude/skills/time/SKILL.md +++ b/.claude/skills/time/SKILL.md @@ -125,5 +125,5 @@ Only when the human says so: `gh variable set TIME_MODE --body live`. The daily With a Cloudflare API token, each venture also gets a backend: its own Worker (`time-`, code in `.github/time/cloudflare/worker.js`), its own D1 database and Workers AI, serving the venture's Pages folder under its own domain. The page reaches it through `time.js` (written next to `index.html`; the build never edits it): `Time.save` / `Time.load` for share links, `Time.ai` for a small model. Every call degrades to null, so a product still works on Pages alone and in the headless checks. - Token: Cloudflare → My Profile → API Tokens → Create Token (custom) with Account · Workers Scripts · Edit, Account · D1 · Edit, Account · Workers AI · Read, and, for a domain, Zone · Workers Routes · Edit and Zone · DNS · Edit on that zone. Then `gh secret set CLOUDFLARE_API_TOKEN` and `gh secret set CLOUDFLARE_ACCOUNT_ID` (the account id is in the dashboard URL). - Domain: `gh variable set TIME_VENTURE_DOMAIN --body example.com` (a zone on that account) puts each venture at `.example.com`. Without it, ventures live at `time-..workers.dev`. -- Optional: `TIME_VENTURE_AI_MODEL` (default `@cf/meta/llama-3.1-8b-instruct`). +- Optional: `TIME_VENTURE_AI_MODEL` (default `@cf/google/gemma-4-26b-a4b-it`). - The doctor checks the token. Existing ventures get backends a couple per tick, and a new `worker.js` (from Time's update) redeploys every venture the same way. A Cloudflare failure never holds a venture: it launches on Pages and the next tick tries again. diff --git a/.github/time/cloudflare.py b/.github/time/cloudflare.py index 17278a7..63c66d8 100644 --- a/.github/time/cloudflare.py +++ b/.github/time/cloudflare.py @@ -29,7 +29,7 @@ HERE = Path(__file__).resolve().parent WORKER = HERE / "cloudflare" / "worker.js" CLIENT = HERE / "cloudflare" / "time.js" COMPAT = "2026-09-01" -AI_MODEL = "@cf/meta/llama-3.1-8b-instruct" +AI_MODEL = "@cf/google/gemma-4-26b-a4b-it" def env(name: str, default: str = "") -> str: diff --git a/.github/time/cloudflare/test_worker.mjs b/.github/time/cloudflare/test_worker.mjs index 7a04c76..09a3b98 100644 --- a/.github/time/cloudflare/test_worker.mjs +++ b/.github/time/cloudflare/test_worker.mjs @@ -78,6 +78,12 @@ assert.equal((await post("/api/ai", { prompt: "three" }, "4.4.4.4")).status, 200 assert.equal((await post("/api/ai", { prompt: "over the day" }, "5.5.5.5")).status, 429); assert.equal(prompts.length, 3); +// newer models answer in the OpenAI shape +const openai = { ...env, DB: new FakeD1(), AI: { async run() { return { choices: [{ message: { content: "from choices" } }] }; } } }; +r = await worker.fetch(new Request("https://mdecks.yolo.lol/api/ai", { method: "POST", body: JSON.stringify({ prompt: "hi" }), + headers: { "cf-connecting-ip": "9.9.9.9" } }), openai); +assert.deepEqual(await r.json(), { text: "from choices" }); + // CORS: this domain and the Pages origin, nobody else r = await call("/api/health", { headers: { origin: "https://o.github.io" } }); assert.equal(r.headers.get("access-control-allow-origin"), "https://o.github.io"); diff --git a/.github/time/cloudflare/worker.js b/.github/time/cloudflare/worker.js index 91c8d18..13fe499 100644 --- a/.github/time/cloudflare/worker.js +++ b/.github/time/cloudflare/worker.js @@ -80,7 +80,9 @@ async function api(req, env, url, h) { const messages = [...(body.system ? [{ role: "system", content: String(body.system).slice(0, MAX_PROMPT) }] : []), { role: "user", content: prompt }]; const out = await env.AI.run(env.AI_MODEL, { messages, max_tokens: 512 }); - return json({ text: out.response ?? "" }, 200, h); + // older models answer { response }, newer ones the OpenAI shape { choices: [{ message: { content } }] } + const text = out.response ?? out.choices?.[0]?.message?.content ?? ""; + return json({ text: typeof text === "string" ? text : JSON.stringify(text) }, 200, h); } return json({ error: "no such endpoint" }, 404, h); } diff --git a/CHANGELOG.md b/CHANGELOG.md index f6b482a..22cf9f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - The Venice worker's shell no longer sees secrets: its environment drops tokens and keys, its checkout keeps no token in `.git/config`, and the workflows check out without persisted credentials. ### Added +- Venture backends use Gemma 4 (`@cf/google/gemma-4-26b-a4b-it`) by default and read both the older `{response}` and the OpenAI-style answer from Workers AI (the first default had been retired). - Ventures can be backed by Cloudflare: with `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`, each venture gets its own Worker, D1 database and Workers AI, and lives at `.` (or workers.dev) while its page stays on GitHub Pages. The page's `time.js` gives share links and a small model, and degrades to null so products still work without it. Existing ventures are backfilled a couple per tick; a new worker redeploys every venture; the doctor checks the token. - Venture loop launches products, not pages: a launch round where board seats write plans and one synthesizes `LAUNCH.md` with attribution; launch pages where the working product fills the first screen under a slim header with one call to action, then pricing and honest limits, in a look that follows the product; early-access signups through a GitHub issue form, thanked on their issues; deterministic and headless-Chrome checks with fix rounds and an andon; a 7-day decision against success and kill lines; a studio index at `/ventures/`. - Start a venture whenever you like: Actions → venture → Run workflow → "Start a new venture now" (`gh workflow run venture.yml -f start=true`); the schedule still starts one a day.