create-volt 0.49.0 → 0.50.0
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/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/templates/blog/server.js +28 -20
- package/templates/default/server.js +28 -20
- package/templates/docs/server.js +28 -20
- package/templates/starter/server.js +28 -20
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable changes to `create-volt` are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.50.0] - 2026-06-30
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Config editor AI falls back to the hosted gateway.** With no local provider
|
|
11
|
+
key, `/setup/ai` routes through the voltjs.com gateway via `VOLT_AI_TOKEN`
|
|
12
|
+
(free-capped, then pay-as-you-go on the host's key); a local key still wins
|
|
13
|
+
(BYO). Clear error when neither is set. The gateway now honors the client's
|
|
14
|
+
stream preference, so the editor gets a normal JSON response.
|
|
15
|
+
|
|
7
16
|
## [0.49.0] - 2026-06-29
|
|
8
17
|
|
|
9
18
|
### Added
|
|
@@ -657,6 +666,7 @@ All notable changes to `create-volt` are documented here. The format follows
|
|
|
657
666
|
watching and full-page hot reload. Supports `--skip-install` and `--force`,
|
|
658
667
|
and auto-detects npm / pnpm / yarn / bun for the install step.
|
|
659
668
|
|
|
669
|
+
[0.50.0]: https://github.com/MIR-2025/volt/releases/tag/v0.50.0
|
|
660
670
|
[0.49.0]: https://github.com/MIR-2025/volt/releases/tag/v0.49.0
|
|
661
671
|
[0.48.3]: https://github.com/MIR-2025/volt/releases/tag/v0.48.3
|
|
662
672
|
[0.48.2]: https://github.com/MIR-2025/volt/releases/tag/v0.48.2
|
package/package.json
CHANGED
package/templates/blog/server.js
CHANGED
|
@@ -344,9 +344,10 @@ function startSetup() {
|
|
|
344
344
|
}
|
|
345
345
|
return;
|
|
346
346
|
}
|
|
347
|
-
// --- AI proxy for the in-config editor (RTEPro).
|
|
348
|
-
//
|
|
349
|
-
//
|
|
347
|
+
// --- AI proxy for the in-config editor (RTEPro). Uses a local provider key
|
|
348
|
+
// (BYO) when set; otherwise falls back to the voltjs.com gateway via
|
|
349
|
+
// VOLT_AI_TOKEN (free-capped, then pay-as-you-go on the host's key). The
|
|
350
|
+
// key/token never reaches the browser. ---
|
|
350
351
|
if (req.method === "POST" && p === "/setup/ai") {
|
|
351
352
|
let cbody = "";
|
|
352
353
|
req.on("data", (c) => (cbody += c));
|
|
@@ -357,23 +358,30 @@ function startSetup() {
|
|
|
357
358
|
const body = JSON.parse(cbody || "{}");
|
|
358
359
|
const provider = body._provider || env.AI_PROVIDER || "anthropic";
|
|
359
360
|
delete body._provider;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
} else
|
|
376
|
-
|
|
361
|
+
const localKey = { anthropic: env.ANTHROPIC_API_KEY, openai: env.OPENAI_API_KEY, gemini: env.GEMINI_API_KEY }[provider];
|
|
362
|
+
let url, headers, payload = body;
|
|
363
|
+
if (localKey) {
|
|
364
|
+
if (provider === "anthropic") {
|
|
365
|
+
url = "https://api.anthropic.com/v1/messages";
|
|
366
|
+
headers = { "x-api-key": localKey, "anthropic-version": "2023-06-01", "content-type": "application/json" };
|
|
367
|
+
} else if (provider === "openai") {
|
|
368
|
+
url = "https://api.openai.com/v1/chat/completions";
|
|
369
|
+
headers = { authorization: "Bearer " + localKey, "content-type": "application/json" };
|
|
370
|
+
} else if (provider === "gemini") {
|
|
371
|
+
const model = body.model || "gemini-2.0-flash";
|
|
372
|
+
delete body.model;
|
|
373
|
+
url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${localKey}`;
|
|
374
|
+
headers = { "content-type": "application/json" };
|
|
375
|
+
} else throw new Error("unknown AI provider: " + provider);
|
|
376
|
+
} else if (env.VOLT_AI_TOKEN) {
|
|
377
|
+
// no local key → host gateway (free-capped, then pay-as-you-go)
|
|
378
|
+
url = env.VOLT_AI_GATEWAY || "https://voltjs.com/api/ai";
|
|
379
|
+
headers = { authorization: "Bearer " + env.VOLT_AI_TOKEN, "content-type": "application/json" };
|
|
380
|
+
payload = { messages: body.messages, system: body.system, max_tokens: body.max_tokens, model: body.model };
|
|
381
|
+
} else {
|
|
382
|
+
throw new Error("No AI key in .env and no VOLT_AI_TOKEN — add a provider key, or a gateway token to use the hosted tier.");
|
|
383
|
+
}
|
|
384
|
+
const r = await fetch(url, { method: "POST", headers, body: JSON.stringify(payload) });
|
|
377
385
|
const text = await r.text();
|
|
378
386
|
res.statusCode = r.status;
|
|
379
387
|
res.setHeader("Content-Type", r.headers.get("content-type") || "application/json");
|
|
@@ -344,9 +344,10 @@ function startSetup() {
|
|
|
344
344
|
}
|
|
345
345
|
return;
|
|
346
346
|
}
|
|
347
|
-
// --- AI proxy for the in-config editor (RTEPro).
|
|
348
|
-
//
|
|
349
|
-
//
|
|
347
|
+
// --- AI proxy for the in-config editor (RTEPro). Uses a local provider key
|
|
348
|
+
// (BYO) when set; otherwise falls back to the voltjs.com gateway via
|
|
349
|
+
// VOLT_AI_TOKEN (free-capped, then pay-as-you-go on the host's key). The
|
|
350
|
+
// key/token never reaches the browser. ---
|
|
350
351
|
if (req.method === "POST" && p === "/setup/ai") {
|
|
351
352
|
let cbody = "";
|
|
352
353
|
req.on("data", (c) => (cbody += c));
|
|
@@ -357,23 +358,30 @@ function startSetup() {
|
|
|
357
358
|
const body = JSON.parse(cbody || "{}");
|
|
358
359
|
const provider = body._provider || env.AI_PROVIDER || "anthropic";
|
|
359
360
|
delete body._provider;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
} else
|
|
376
|
-
|
|
361
|
+
const localKey = { anthropic: env.ANTHROPIC_API_KEY, openai: env.OPENAI_API_KEY, gemini: env.GEMINI_API_KEY }[provider];
|
|
362
|
+
let url, headers, payload = body;
|
|
363
|
+
if (localKey) {
|
|
364
|
+
if (provider === "anthropic") {
|
|
365
|
+
url = "https://api.anthropic.com/v1/messages";
|
|
366
|
+
headers = { "x-api-key": localKey, "anthropic-version": "2023-06-01", "content-type": "application/json" };
|
|
367
|
+
} else if (provider === "openai") {
|
|
368
|
+
url = "https://api.openai.com/v1/chat/completions";
|
|
369
|
+
headers = { authorization: "Bearer " + localKey, "content-type": "application/json" };
|
|
370
|
+
} else if (provider === "gemini") {
|
|
371
|
+
const model = body.model || "gemini-2.0-flash";
|
|
372
|
+
delete body.model;
|
|
373
|
+
url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${localKey}`;
|
|
374
|
+
headers = { "content-type": "application/json" };
|
|
375
|
+
} else throw new Error("unknown AI provider: " + provider);
|
|
376
|
+
} else if (env.VOLT_AI_TOKEN) {
|
|
377
|
+
// no local key → host gateway (free-capped, then pay-as-you-go)
|
|
378
|
+
url = env.VOLT_AI_GATEWAY || "https://voltjs.com/api/ai";
|
|
379
|
+
headers = { authorization: "Bearer " + env.VOLT_AI_TOKEN, "content-type": "application/json" };
|
|
380
|
+
payload = { messages: body.messages, system: body.system, max_tokens: body.max_tokens, model: body.model };
|
|
381
|
+
} else {
|
|
382
|
+
throw new Error("No AI key in .env and no VOLT_AI_TOKEN — add a provider key, or a gateway token to use the hosted tier.");
|
|
383
|
+
}
|
|
384
|
+
const r = await fetch(url, { method: "POST", headers, body: JSON.stringify(payload) });
|
|
377
385
|
const text = await r.text();
|
|
378
386
|
res.statusCode = r.status;
|
|
379
387
|
res.setHeader("Content-Type", r.headers.get("content-type") || "application/json");
|
package/templates/docs/server.js
CHANGED
|
@@ -344,9 +344,10 @@ function startSetup() {
|
|
|
344
344
|
}
|
|
345
345
|
return;
|
|
346
346
|
}
|
|
347
|
-
// --- AI proxy for the in-config editor (RTEPro).
|
|
348
|
-
//
|
|
349
|
-
//
|
|
347
|
+
// --- AI proxy for the in-config editor (RTEPro). Uses a local provider key
|
|
348
|
+
// (BYO) when set; otherwise falls back to the voltjs.com gateway via
|
|
349
|
+
// VOLT_AI_TOKEN (free-capped, then pay-as-you-go on the host's key). The
|
|
350
|
+
// key/token never reaches the browser. ---
|
|
350
351
|
if (req.method === "POST" && p === "/setup/ai") {
|
|
351
352
|
let cbody = "";
|
|
352
353
|
req.on("data", (c) => (cbody += c));
|
|
@@ -357,23 +358,30 @@ function startSetup() {
|
|
|
357
358
|
const body = JSON.parse(cbody || "{}");
|
|
358
359
|
const provider = body._provider || env.AI_PROVIDER || "anthropic";
|
|
359
360
|
delete body._provider;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
} else
|
|
376
|
-
|
|
361
|
+
const localKey = { anthropic: env.ANTHROPIC_API_KEY, openai: env.OPENAI_API_KEY, gemini: env.GEMINI_API_KEY }[provider];
|
|
362
|
+
let url, headers, payload = body;
|
|
363
|
+
if (localKey) {
|
|
364
|
+
if (provider === "anthropic") {
|
|
365
|
+
url = "https://api.anthropic.com/v1/messages";
|
|
366
|
+
headers = { "x-api-key": localKey, "anthropic-version": "2023-06-01", "content-type": "application/json" };
|
|
367
|
+
} else if (provider === "openai") {
|
|
368
|
+
url = "https://api.openai.com/v1/chat/completions";
|
|
369
|
+
headers = { authorization: "Bearer " + localKey, "content-type": "application/json" };
|
|
370
|
+
} else if (provider === "gemini") {
|
|
371
|
+
const model = body.model || "gemini-2.0-flash";
|
|
372
|
+
delete body.model;
|
|
373
|
+
url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${localKey}`;
|
|
374
|
+
headers = { "content-type": "application/json" };
|
|
375
|
+
} else throw new Error("unknown AI provider: " + provider);
|
|
376
|
+
} else if (env.VOLT_AI_TOKEN) {
|
|
377
|
+
// no local key → host gateway (free-capped, then pay-as-you-go)
|
|
378
|
+
url = env.VOLT_AI_GATEWAY || "https://voltjs.com/api/ai";
|
|
379
|
+
headers = { authorization: "Bearer " + env.VOLT_AI_TOKEN, "content-type": "application/json" };
|
|
380
|
+
payload = { messages: body.messages, system: body.system, max_tokens: body.max_tokens, model: body.model };
|
|
381
|
+
} else {
|
|
382
|
+
throw new Error("No AI key in .env and no VOLT_AI_TOKEN — add a provider key, or a gateway token to use the hosted tier.");
|
|
383
|
+
}
|
|
384
|
+
const r = await fetch(url, { method: "POST", headers, body: JSON.stringify(payload) });
|
|
377
385
|
const text = await r.text();
|
|
378
386
|
res.statusCode = r.status;
|
|
379
387
|
res.setHeader("Content-Type", r.headers.get("content-type") || "application/json");
|
|
@@ -370,9 +370,10 @@ function startSetup() {
|
|
|
370
370
|
}
|
|
371
371
|
return;
|
|
372
372
|
}
|
|
373
|
-
// --- AI proxy for the in-config editor (RTEPro).
|
|
374
|
-
//
|
|
375
|
-
//
|
|
373
|
+
// --- AI proxy for the in-config editor (RTEPro). Uses a local provider key
|
|
374
|
+
// (BYO) when set; otherwise falls back to the voltjs.com gateway via
|
|
375
|
+
// VOLT_AI_TOKEN (free-capped, then pay-as-you-go on the host's key). The
|
|
376
|
+
// key/token never reaches the browser. ---
|
|
376
377
|
if (req.method === "POST" && p === "/setup/ai") {
|
|
377
378
|
let cbody = "";
|
|
378
379
|
req.on("data", (c) => (cbody += c));
|
|
@@ -383,23 +384,30 @@ function startSetup() {
|
|
|
383
384
|
const body = JSON.parse(cbody || "{}");
|
|
384
385
|
const provider = body._provider || env.AI_PROVIDER || "anthropic";
|
|
385
386
|
delete body._provider;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
} else
|
|
402
|
-
|
|
387
|
+
const localKey = { anthropic: env.ANTHROPIC_API_KEY, openai: env.OPENAI_API_KEY, gemini: env.GEMINI_API_KEY }[provider];
|
|
388
|
+
let url, headers, payload = body;
|
|
389
|
+
if (localKey) {
|
|
390
|
+
if (provider === "anthropic") {
|
|
391
|
+
url = "https://api.anthropic.com/v1/messages";
|
|
392
|
+
headers = { "x-api-key": localKey, "anthropic-version": "2023-06-01", "content-type": "application/json" };
|
|
393
|
+
} else if (provider === "openai") {
|
|
394
|
+
url = "https://api.openai.com/v1/chat/completions";
|
|
395
|
+
headers = { authorization: "Bearer " + localKey, "content-type": "application/json" };
|
|
396
|
+
} else if (provider === "gemini") {
|
|
397
|
+
const model = body.model || "gemini-2.0-flash";
|
|
398
|
+
delete body.model;
|
|
399
|
+
url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${localKey}`;
|
|
400
|
+
headers = { "content-type": "application/json" };
|
|
401
|
+
} else throw new Error("unknown AI provider: " + provider);
|
|
402
|
+
} else if (env.VOLT_AI_TOKEN) {
|
|
403
|
+
// no local key → host gateway (free-capped, then pay-as-you-go)
|
|
404
|
+
url = env.VOLT_AI_GATEWAY || "https://voltjs.com/api/ai";
|
|
405
|
+
headers = { authorization: "Bearer " + env.VOLT_AI_TOKEN, "content-type": "application/json" };
|
|
406
|
+
payload = { messages: body.messages, system: body.system, max_tokens: body.max_tokens, model: body.model };
|
|
407
|
+
} else {
|
|
408
|
+
throw new Error("No AI key in .env and no VOLT_AI_TOKEN — add a provider key, or a gateway token to use the hosted tier.");
|
|
409
|
+
}
|
|
410
|
+
const r = await fetch(url, { method: "POST", headers, body: JSON.stringify(payload) });
|
|
403
411
|
const text = await r.text();
|
|
404
412
|
res.statusCode = r.status;
|
|
405
413
|
res.setHeader("Content-Type", r.headers.get("content-type") || "application/json");
|