sproutkit-code 0.9.0 → 0.11.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/dist/index.js +139 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -187,6 +187,10 @@ var require_db = __commonJS({
|
|
|
187
187
|
db2.exec("ALTER TABLE leads ADD COLUMN dead_at TEXT");
|
|
188
188
|
} catch {
|
|
189
189
|
}
|
|
190
|
+
try {
|
|
191
|
+
db2.exec("ALTER TABLE leads ADD COLUMN site_uid TEXT");
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
190
194
|
}
|
|
191
195
|
function getDb2() {
|
|
192
196
|
return db2;
|
|
@@ -32728,6 +32732,12 @@ var require_cms_runtime = __commonJS({
|
|
|
32728
32732
|
exports2.hashPassword = hashPassword;
|
|
32729
32733
|
exports2.generateSessionSecret = generateSessionSecret;
|
|
32730
32734
|
exports2.API_CONTENT_JS = `import { list, put } from "@vercel/blob";
|
|
32735
|
+
|
|
32736
|
+
// Per-site Blob namespace so many CMS sites share one store (one token per
|
|
32737
|
+
// agency) without overwriting each other's content. Empty when SITE_ID unset \u2192
|
|
32738
|
+
// legacy sites keep the flat content.json key, so nothing deployed breaks.
|
|
32739
|
+
const PFX = process.env.SITE_ID ? "sites/" + process.env.SITE_ID + "/" : "";
|
|
32740
|
+
const CONTENT_KEY = PFX + "content.json";
|
|
32731
32741
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
32732
32742
|
|
|
32733
32743
|
// Runs on Node.js (not Edge) because @vercel/blob's undici dependency
|
|
@@ -32795,8 +32805,8 @@ function sanitise(raw) {
|
|
|
32795
32805
|
|
|
32796
32806
|
async function readContent() {
|
|
32797
32807
|
try {
|
|
32798
|
-
const { blobs } = await list({ prefix:
|
|
32799
|
-
const target = blobs.find((b) => b.pathname ===
|
|
32808
|
+
const { blobs } = await list({ prefix: CONTENT_KEY, limit: 1 });
|
|
32809
|
+
const target = blobs.find((b) => b.pathname === CONTENT_KEY);
|
|
32800
32810
|
if (!target) return SAFE_DEFAULTS;
|
|
32801
32811
|
const res = await fetch(target.url);
|
|
32802
32812
|
if (!res.ok) return SAFE_DEFAULTS;
|
|
@@ -32864,7 +32874,7 @@ export default async function handler(req, res) {
|
|
|
32864
32874
|
body = raw ? JSON.parse(raw) : null;
|
|
32865
32875
|
}
|
|
32866
32876
|
const clean = sanitise(body);
|
|
32867
|
-
await put(
|
|
32877
|
+
await put(CONTENT_KEY, JSON.stringify(clean), {
|
|
32868
32878
|
access: "public",
|
|
32869
32879
|
addRandomSuffix: false,
|
|
32870
32880
|
allowOverwrite: true,
|
|
@@ -32988,6 +32998,10 @@ export default function handler(req, res) {
|
|
|
32988
32998
|
|
|
32989
32999
|
export const config = { runtime: "nodejs", api: { bodyParser: false } };
|
|
32990
33000
|
|
|
33001
|
+
// Per-site namespace so uploaded photos don't collide across sites sharing one
|
|
33002
|
+
// Blob store. Empty when SITE_ID unset (legacy sites keep flat photos/ path).
|
|
33003
|
+
const PFX = process.env.SITE_ID ? "sites/" + process.env.SITE_ID + "/" : "";
|
|
33004
|
+
|
|
32991
33005
|
async function verifySession(req) {
|
|
32992
33006
|
const cookie = req.headers.cookie || "";
|
|
32993
33007
|
const match = cookie.match(/sk_admin=([^;]+)/);
|
|
@@ -33036,7 +33050,7 @@ export default async function handler(req, res) {
|
|
|
33036
33050
|
res.status(413).json({ error: "too_large" });
|
|
33037
33051
|
return;
|
|
33038
33052
|
}
|
|
33039
|
-
const name = "photos/" + Date.now() + "-" + Math.random().toString(36).slice(2, 8) + ".webp";
|
|
33053
|
+
const name = PFX + "photos/" + Date.now() + "-" + Math.random().toString(36).slice(2, 8) + ".webp";
|
|
33040
33054
|
const blob = await put(name, buf, {
|
|
33041
33055
|
access: "public",
|
|
33042
33056
|
addRandomSuffix: false,
|
|
@@ -33804,6 +33818,13 @@ import { createHmac, timingSafeEqual } from "node:crypto";
|
|
|
33804
33818
|
|
|
33805
33819
|
export const config = { runtime: "nodejs" };
|
|
33806
33820
|
|
|
33821
|
+
// Per-site Blob namespace: every site isolates its files under sites/<id>/ so
|
|
33822
|
+
// many booking sites can share ONE Blob store (one token per agency) without
|
|
33823
|
+
// overwriting each other. Empty when SITE_ID is unset \u2192 older sites keep
|
|
33824
|
+
// reading/writing the legacy flat keys, so nothing already deployed breaks.
|
|
33825
|
+
const PFX = process.env.SITE_ID ? "sites/" + process.env.SITE_ID + "/" : "";
|
|
33826
|
+
const CONFIG_KEY = PFX + "booking-config.json";
|
|
33827
|
+
|
|
33807
33828
|
const SAFE_DEFAULTS = {
|
|
33808
33829
|
archetype: "enquiry",
|
|
33809
33830
|
services: [],
|
|
@@ -33856,8 +33877,8 @@ function sanitise(raw) {
|
|
|
33856
33877
|
|
|
33857
33878
|
async function readConfig() {
|
|
33858
33879
|
try {
|
|
33859
|
-
const { blobs } = await list({ prefix:
|
|
33860
|
-
const target = blobs.find((b) => b.pathname ===
|
|
33880
|
+
const { blobs } = await list({ prefix: CONFIG_KEY, limit: 1 });
|
|
33881
|
+
const target = blobs.find((b) => b.pathname === CONFIG_KEY);
|
|
33861
33882
|
if (!target) return SAFE_DEFAULTS;
|
|
33862
33883
|
const res = await fetch(target.url);
|
|
33863
33884
|
if (!res.ok) return SAFE_DEFAULTS;
|
|
@@ -33912,7 +33933,7 @@ export default async function handler(req, res) {
|
|
|
33912
33933
|
body = raw ? JSON.parse(raw) : null;
|
|
33913
33934
|
}
|
|
33914
33935
|
const clean = sanitise(body);
|
|
33915
|
-
await put(
|
|
33936
|
+
await put(CONFIG_KEY, JSON.stringify(clean), {
|
|
33916
33937
|
access: "public", addRandomSuffix: false, allowOverwrite: true,
|
|
33917
33938
|
contentType: "application/json",
|
|
33918
33939
|
});
|
|
@@ -33930,6 +33951,10 @@ import { createHmac, timingSafeEqual } from "node:crypto";
|
|
|
33930
33951
|
|
|
33931
33952
|
export const config = { runtime: "nodejs" };
|
|
33932
33953
|
|
|
33954
|
+
// Per-site namespace (see booking-config.js). Empty when SITE_ID unset.
|
|
33955
|
+
const PFX = process.env.SITE_ID ? "sites/" + process.env.SITE_ID + "/" : "";
|
|
33956
|
+
const BOOKINGS_KEY = PFX + "bookings.jsonl";
|
|
33957
|
+
|
|
33933
33958
|
function verifySession(req) {
|
|
33934
33959
|
const cookie = req.headers.cookie || "";
|
|
33935
33960
|
const match = cookie.match(/sk_admin=([^;]+)/);
|
|
@@ -33959,8 +33984,8 @@ function readBody(req) {
|
|
|
33959
33984
|
|
|
33960
33985
|
async function readAllBookings() {
|
|
33961
33986
|
try {
|
|
33962
|
-
const { blobs } = await list({ prefix:
|
|
33963
|
-
const target = blobs.find((b) => b.pathname ===
|
|
33987
|
+
const { blobs } = await list({ prefix: BOOKINGS_KEY, limit: 1 });
|
|
33988
|
+
const target = blobs.find((b) => b.pathname === BOOKINGS_KEY);
|
|
33964
33989
|
if (!target) return [];
|
|
33965
33990
|
const res = await fetch(target.url);
|
|
33966
33991
|
if (!res.ok) return [];
|
|
@@ -33975,7 +34000,7 @@ async function readAllBookings() {
|
|
|
33975
34000
|
|
|
33976
34001
|
async function writeAllBookings(bookings) {
|
|
33977
34002
|
const text = bookings.map((b) => JSON.stringify(b)).join("\\n");
|
|
33978
|
-
await put(
|
|
34003
|
+
await put(BOOKINGS_KEY, text, {
|
|
33979
34004
|
access: "public", addRandomSuffix: false, allowOverwrite: true,
|
|
33980
34005
|
contentType: "application/x-ndjson",
|
|
33981
34006
|
});
|
|
@@ -79485,7 +79510,9 @@ var require_builder = __commonJS({
|
|
|
79485
79510
|
exports2.deployStandaloneBooking = deployStandaloneBooking2;
|
|
79486
79511
|
var sdk_1 = __importDefault(require_sdk());
|
|
79487
79512
|
var axios_1 = __importDefault(require_axios());
|
|
79513
|
+
var crypto_1 = require("crypto");
|
|
79488
79514
|
var runtime_1 = require_runtime();
|
|
79515
|
+
var db_1 = require_db();
|
|
79489
79516
|
var llm_1 = require_llm();
|
|
79490
79517
|
var fs3 = __importStar(require("fs"));
|
|
79491
79518
|
var path3 = __importStar(require("path"));
|
|
@@ -79493,6 +79520,15 @@ var require_builder = __commonJS({
|
|
|
79493
79520
|
var cms_template_1 = require_cms_template();
|
|
79494
79521
|
var cms_runtime_1 = require_cms_runtime();
|
|
79495
79522
|
var booking_runtime_1 = require_booking_runtime();
|
|
79523
|
+
function siteUidFor(leadId) {
|
|
79524
|
+
const db2 = (0, db_1.getDb)();
|
|
79525
|
+
const row = db2.get("SELECT site_uid FROM leads WHERE id = ?", [leadId]);
|
|
79526
|
+
if (row?.site_uid)
|
|
79527
|
+
return row.site_uid;
|
|
79528
|
+
const uid = (0, crypto_1.randomUUID)();
|
|
79529
|
+
db2.run("UPDATE leads SET site_uid = ? WHERE id = ?", [uid, leadId]);
|
|
79530
|
+
return uid;
|
|
79531
|
+
}
|
|
79496
79532
|
var STOCK_HOSTS = [
|
|
79497
79533
|
"unsplash.com",
|
|
79498
79534
|
"images.unsplash.com",
|
|
@@ -80058,6 +80094,7 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
80058
80094
|
devDependencies: { "@vitejs/plugin-react": "^4.3.1", vite: "^5.4.2" }
|
|
80059
80095
|
}) : packageJson;
|
|
80060
80096
|
if (includeCms) {
|
|
80097
|
+
const cmsSiteUid = siteUidFor(lead.id);
|
|
80061
80098
|
onProgress(reuseAdminPassword ? "Reusing existing credentials\u2026" : "Generating admin credentials\u2026");
|
|
80062
80099
|
const adminPassword = reuseAdminPassword ?? (0, cms_runtime_1.generateFriendlyPassword)();
|
|
80063
80100
|
const adminHash = await (0, cms_runtime_1.hashPassword)(adminPassword);
|
|
@@ -80092,7 +80129,7 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
80092
80129
|
onProgress("Seeding CMS content\u2026");
|
|
80093
80130
|
try {
|
|
80094
80131
|
const { put } = await Promise.resolve().then(() => __importStar(require_dist5()));
|
|
80095
|
-
await put(
|
|
80132
|
+
await put(`sites/${cmsSiteUid}/content.json`, JSON.stringify(initialContent), {
|
|
80096
80133
|
access: "public",
|
|
80097
80134
|
addRandomSuffix: false,
|
|
80098
80135
|
allowOverwrite: true,
|
|
@@ -80107,6 +80144,9 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
80107
80144
|
onProgress("Setting CMS env vars\u2026");
|
|
80108
80145
|
await setProjectEnv(projectName, vercelToken, {
|
|
80109
80146
|
BLOB_READ_WRITE_TOKEN: blobToken,
|
|
80147
|
+
// Namespaces this site's Blob files so it never collides with other
|
|
80148
|
+
// sites sharing the same store/token.
|
|
80149
|
+
SITE_ID: cmsSiteUid,
|
|
80110
80150
|
ADMIN_PASSWORD_HASH: adminHash,
|
|
80111
80151
|
SESSION_SECRET: sessionSecret,
|
|
80112
80152
|
BUSINESS_NAME: lead.name
|
|
@@ -80224,12 +80264,16 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
80224
80264
|
throw new Error("Couldn't fetch src/App.jsx from the existing deployment. Try Regenerate website first, then Redeploy CMS.");
|
|
80225
80265
|
}
|
|
80226
80266
|
onProgress("Refreshing CMS env vars\u2026");
|
|
80227
|
-
|
|
80267
|
+
const existingUid = (0, db_1.getDb)().get("SELECT site_uid FROM leads WHERE id = ?", [lead.id])?.site_uid;
|
|
80268
|
+
const cmsEnv = {
|
|
80228
80269
|
BLOB_READ_WRITE_TOKEN: blobToken,
|
|
80229
80270
|
ADMIN_PASSWORD_HASH: adminHash,
|
|
80230
80271
|
SESSION_SECRET: sessionSecret,
|
|
80231
80272
|
BUSINESS_NAME: lead.name
|
|
80232
|
-
}
|
|
80273
|
+
};
|
|
80274
|
+
if (existingUid)
|
|
80275
|
+
cmsEnv.SITE_ID = existingUid;
|
|
80276
|
+
await setProjectEnv(projectName, vercelToken, cmsEnv);
|
|
80233
80277
|
onProgress("Deploying\u2026");
|
|
80234
80278
|
const res = await postVercelDeployment({
|
|
80235
80279
|
name: projectName,
|
|
@@ -80293,10 +80337,12 @@ createRoot(document.getElementById('root')).render(<StrictMode><App /></StrictMo
|
|
|
80293
80337
|
import react from '@vitejs/plugin-react';
|
|
80294
80338
|
export default defineConfig({ plugins: [react()] });`;
|
|
80295
80339
|
files.push({ file: "index.html", data: indexHtml, encoding: "utf-8" }, { file: "src/main.jsx", data: mainJsx, encoding: "utf-8" }, { file: "vite.config.js", data: viteConfig, encoding: "utf-8" });
|
|
80340
|
+
const bookingExistingUid = (0, db_1.getDb)().get("SELECT site_uid FROM leads WHERE id = ?", [lead.id])?.site_uid;
|
|
80341
|
+
const bookingKey = bookingExistingUid ? `sites/${bookingExistingUid}/booking-config.json` : "booking-config.json";
|
|
80296
80342
|
onProgress("Seeding booking config into Blob\u2026");
|
|
80297
80343
|
try {
|
|
80298
80344
|
const { put } = await Promise.resolve().then(() => __importStar(require_dist5()));
|
|
80299
|
-
await put(
|
|
80345
|
+
await put(bookingKey, JSON.stringify(bookingConfig), {
|
|
80300
80346
|
access: "public",
|
|
80301
80347
|
addRandomSuffix: false,
|
|
80302
80348
|
allowOverwrite: true,
|
|
@@ -80307,19 +80353,25 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
80307
80353
|
throw new Error(`Failed to seed booking config: ${e.message}`);
|
|
80308
80354
|
}
|
|
80309
80355
|
onProgress("Setting booking env vars\u2026");
|
|
80310
|
-
|
|
80356
|
+
const bookingEnv = {
|
|
80311
80357
|
RESEND_API_KEY: resendKey,
|
|
80312
80358
|
OWNER_EMAIL: ownerEmail,
|
|
80313
80359
|
BUSINESS_NAME: lead.name
|
|
80314
|
-
}
|
|
80360
|
+
};
|
|
80361
|
+
if (bookingExistingUid)
|
|
80362
|
+
bookingEnv.SITE_ID = bookingExistingUid;
|
|
80363
|
+
await setProjectEnv(projectName, vercelToken, bookingEnv);
|
|
80315
80364
|
if (lead.admin_password) {
|
|
80316
80365
|
onProgress("Refreshing admin session env\u2026");
|
|
80317
80366
|
const adminHash = await (0, cms_runtime_1.hashPassword)(lead.admin_password);
|
|
80318
|
-
|
|
80367
|
+
const adminEnv = {
|
|
80319
80368
|
BLOB_READ_WRITE_TOKEN: blobToken,
|
|
80320
80369
|
ADMIN_PASSWORD_HASH: adminHash,
|
|
80321
80370
|
SESSION_SECRET: (0, cms_runtime_1.generateSessionSecret)()
|
|
80322
|
-
}
|
|
80371
|
+
};
|
|
80372
|
+
if (bookingExistingUid)
|
|
80373
|
+
adminEnv.SITE_ID = bookingExistingUid;
|
|
80374
|
+
await setProjectEnv(projectName, vercelToken, adminEnv);
|
|
80323
80375
|
}
|
|
80324
80376
|
const packageJsonBooking = JSON.stringify({
|
|
80325
80377
|
name: "sprout-site",
|
|
@@ -80378,9 +80430,10 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
80378
80430
|
const slug = lead.name.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").slice(0, 26);
|
|
80379
80431
|
const projectName = `sprout-book-${lead.id}-${slug}`;
|
|
80380
80432
|
onProgress("Seeding booking config\u2026");
|
|
80433
|
+
const standaloneSiteUid = siteUidFor(lead.id);
|
|
80381
80434
|
try {
|
|
80382
80435
|
const { put } = await Promise.resolve().then(() => __importStar(require_dist5()));
|
|
80383
|
-
await put(
|
|
80436
|
+
await put(`sites/${standaloneSiteUid}/booking-config.json`, JSON.stringify(bookingConfig), {
|
|
80384
80437
|
access: "public",
|
|
80385
80438
|
addRandomSuffix: false,
|
|
80386
80439
|
allowOverwrite: true,
|
|
@@ -80395,7 +80448,8 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
80395
80448
|
RESEND_API_KEY: resendKey,
|
|
80396
80449
|
OWNER_EMAIL: ownerEmail,
|
|
80397
80450
|
BUSINESS_NAME: lead.name,
|
|
80398
|
-
BLOB_READ_WRITE_TOKEN: blobToken
|
|
80451
|
+
BLOB_READ_WRITE_TOKEN: blobToken,
|
|
80452
|
+
SITE_ID: standaloneSiteUid
|
|
80399
80453
|
});
|
|
80400
80454
|
const files = [
|
|
80401
80455
|
{ file: "index.html", data: booking_runtime_1.BOOK_INDEX_HTML, encoding: "utf-8" },
|
|
@@ -82451,12 +82505,53 @@ Return this exact shape:
|
|
|
82451
82505
|
}
|
|
82452
82506
|
});
|
|
82453
82507
|
|
|
82508
|
+
// ../desktop/dist/main/lib/vercel-store.js
|
|
82509
|
+
var require_vercel_store = __commonJS({
|
|
82510
|
+
"../desktop/dist/main/lib/vercel-store.js"(exports2) {
|
|
82511
|
+
"use strict";
|
|
82512
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
82513
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
82514
|
+
};
|
|
82515
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
82516
|
+
exports2.createBlobStore = createBlobStore2;
|
|
82517
|
+
var axios_1 = __importDefault(require_axios());
|
|
82518
|
+
async function createBlobStore2(args) {
|
|
82519
|
+
const name = (args.name || "sproutkit").slice(0, 70);
|
|
82520
|
+
const url = "https://api.vercel.com/storage/stores/blob" + (args.teamId ? `?teamId=${encodeURIComponent(args.teamId)}` : "");
|
|
82521
|
+
try {
|
|
82522
|
+
const res = await axios_1.default.post(url, { name }, {
|
|
82523
|
+
headers: {
|
|
82524
|
+
Authorization: `Bearer ${args.vercelToken}`,
|
|
82525
|
+
"Content-Type": "application/json"
|
|
82526
|
+
},
|
|
82527
|
+
timeout: 3e4
|
|
82528
|
+
});
|
|
82529
|
+
const store = res.data?.store ?? {};
|
|
82530
|
+
const storeId = store.id || store.storeId;
|
|
82531
|
+
return {
|
|
82532
|
+
ok: true,
|
|
82533
|
+
storeId,
|
|
82534
|
+
dashboardUrl: "https://vercel.com/dashboard/stores"
|
|
82535
|
+
};
|
|
82536
|
+
} catch (e) {
|
|
82537
|
+
const err = e;
|
|
82538
|
+
const status = err.response?.status;
|
|
82539
|
+
const msg = err.response?.data?.error?.message ?? err.message;
|
|
82540
|
+
if (status === 409 || /already|exists|conflict/i.test(msg)) {
|
|
82541
|
+
return { ok: false, alreadyExists: true, error: msg };
|
|
82542
|
+
}
|
|
82543
|
+
return { ok: false, error: msg };
|
|
82544
|
+
}
|
|
82545
|
+
}
|
|
82546
|
+
}
|
|
82547
|
+
});
|
|
82548
|
+
|
|
82454
82549
|
// package.json
|
|
82455
82550
|
var require_package = __commonJS({
|
|
82456
82551
|
"package.json"(exports2, module2) {
|
|
82457
82552
|
module2.exports = {
|
|
82458
82553
|
name: "sproutkit-code",
|
|
82459
|
-
version: "0.
|
|
82554
|
+
version: "0.11.0",
|
|
82460
82555
|
description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
|
|
82461
82556
|
bin: {
|
|
82462
82557
|
sproutkit: "./dist/index.js"
|
|
@@ -82503,6 +82598,7 @@ var autoMode = require_auto_mode();
|
|
|
82503
82598
|
var cms = require_cms_provision();
|
|
82504
82599
|
var bookingPreset = require_booking_preset();
|
|
82505
82600
|
var builder = require_builder();
|
|
82601
|
+
var vercelStore = require_vercel_store();
|
|
82506
82602
|
var models = require_models();
|
|
82507
82603
|
var llm = require_llm();
|
|
82508
82604
|
var initDatabase = db.initDatabase;
|
|
@@ -82514,6 +82610,7 @@ var provisionCmsForLead = cms.provisionCmsForLead;
|
|
|
82514
82610
|
var generatePreset = bookingPreset.generatePreset;
|
|
82515
82611
|
var deployBookingOnly = builder.deployBookingOnly;
|
|
82516
82612
|
var deployStandaloneBooking = builder.deployStandaloneBooking;
|
|
82613
|
+
var createBlobStore = vercelStore.createBlobStore;
|
|
82517
82614
|
var refreshModels = models.refreshModels;
|
|
82518
82615
|
var refreshIfStale = models.refreshIfStale;
|
|
82519
82616
|
var listCachedModels = models.listCachedModels;
|
|
@@ -82975,6 +83072,25 @@ async function runOnboarding(force = false) {
|
|
|
82975
83072
|
}
|
|
82976
83073
|
|
|
82977
83074
|
// src/invoke-ipc.ts
|
|
83075
|
+
async function requireBlobToken(vercelToken, emit, leadId) {
|
|
83076
|
+
const existing = getSetting("blob_token");
|
|
83077
|
+
if (existing) return existing;
|
|
83078
|
+
emit("bookings:progress", { leadId, msg: "No Blob token yet \u2014 creating a Blob store for you\u2026" });
|
|
83079
|
+
const created = await createBlobStore({ vercelToken, name: "sproutkit" });
|
|
83080
|
+
if (created.ok) {
|
|
83081
|
+
throw new Error(
|
|
83082
|
+
"Created a Vercel Blob store for you \u2713 \u2014 one last one-time step: open " + (created.dashboardUrl || "https://vercel.com/dashboard/stores") + " , open the 'sproutkit' store, copy its BLOB_READ_WRITE_TOKEN, then run 'sproutkit setup' and paste it. You only do this once \u2014 it's reused for every booking site."
|
|
83083
|
+
);
|
|
83084
|
+
}
|
|
83085
|
+
if (created.alreadyExists) {
|
|
83086
|
+
throw new Error(
|
|
83087
|
+
"You already have a Blob store. Open https://vercel.com/dashboard/stores , copy its BLOB_READ_WRITE_TOKEN, and run 'sproutkit setup' to paste it (one-time \u2014 reused for all sites)."
|
|
83088
|
+
);
|
|
83089
|
+
}
|
|
83090
|
+
throw new Error(
|
|
83091
|
+
`Booking needs a Vercel Blob token and I couldn't auto-create a store (${created.error ?? "unknown"}). Create one at https://vercel.com/dashboard/stores (Create \u2192 Blob), copy BLOB_READ_WRITE_TOKEN, then 'sproutkit setup'.`
|
|
83092
|
+
);
|
|
83093
|
+
}
|
|
82978
83094
|
function makeInvokeIpc(emit) {
|
|
82979
83095
|
return async (channel, payload) => {
|
|
82980
83096
|
if (channel === "auto:build:start") {
|
|
@@ -83026,11 +83142,10 @@ function makeInvokeIpc(emit) {
|
|
|
83026
83142
|
if (!lead) throw new Error("Lead not found");
|
|
83027
83143
|
const anthropicKey = getSetting("anthropic_key");
|
|
83028
83144
|
const vercelToken = getSetting("vercel_token");
|
|
83029
|
-
const blobToken = getSetting("blob_token");
|
|
83030
83145
|
const resendKey = getSetting("resend_key");
|
|
83031
83146
|
if (!anthropicKey) throw new Error("Anthropic API key not set");
|
|
83032
83147
|
if (!vercelToken) throw new Error("Vercel token not set");
|
|
83033
|
-
|
|
83148
|
+
const blobToken = await requireBlobToken(vercelToken, emit, p.leadId);
|
|
83034
83149
|
emit("bookings:progress", { leadId: p.leadId, msg: "Designing the booking form\u2026" });
|
|
83035
83150
|
const leadInfo = lead;
|
|
83036
83151
|
const config = await generatePreset({
|
|
@@ -83068,10 +83183,9 @@ function makeInvokeIpc(emit) {
|
|
|
83068
83183
|
const lead = db2.get("SELECT * FROM leads WHERE id = ?", [p.leadId]);
|
|
83069
83184
|
if (!lead) throw new Error("Lead not found");
|
|
83070
83185
|
const vercelToken = getSetting("vercel_token");
|
|
83071
|
-
const blobToken = getSetting("blob_token");
|
|
83072
83186
|
const resendKey = getSetting("resend_key");
|
|
83073
83187
|
if (!vercelToken) throw new Error("Vercel token not set");
|
|
83074
|
-
|
|
83188
|
+
const blobToken = await requireBlobToken(vercelToken, emit, p.leadId);
|
|
83075
83189
|
emit("bookings:progress", { leadId: p.leadId, msg: "Designing the booking form\u2026" });
|
|
83076
83190
|
const leadInfo = lead;
|
|
83077
83191
|
const config = await generatePreset({
|