sproutkit-code 1.15.0 → 1.16.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 +44 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -407,7 +407,7 @@ var require_db = __commonJS({
|
|
|
407
407
|
} catch {
|
|
408
408
|
}
|
|
409
409
|
}
|
|
410
|
-
for (const col of ["receptionist_id TEXT", "receptionist_url TEXT", "receptionist_price_pence INTEGER", "receptionist_line TEXT"]) {
|
|
410
|
+
for (const col of ["receptionist_id TEXT", "receptionist_url TEXT", "receptionist_price_pence INTEGER", "receptionist_line TEXT", "front_desk_chat INTEGER NOT NULL DEFAULT 0"]) {
|
|
411
411
|
try {
|
|
412
412
|
db2.exec(`ALTER TABLE leads ADD COLUMN ${col}`);
|
|
413
413
|
} catch {
|
|
@@ -31510,7 +31510,7 @@ var require_analytics_runtime = __commonJS({
|
|
|
31510
31510
|
"../desktop/dist/main/lib/analytics-runtime.js"(exports2) {
|
|
31511
31511
|
"use strict";
|
|
31512
31512
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
31513
|
-
exports2.TRACK_SNIPPET = exports2.API_STATS_JS = exports2.API_TRACK_JS = void 0;
|
|
31513
|
+
exports2.FRONT_DESK_SNIPPET = exports2.TRACK_SNIPPET = exports2.API_STATS_JS = exports2.API_TRACK_JS = void 0;
|
|
31514
31514
|
exports2.API_TRACK_JS = `import { list, put } from "@vercel/blob";
|
|
31515
31515
|
import { createHash, createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
31516
31516
|
|
|
@@ -31876,6 +31876,7 @@ export default async function handler(req, res) {
|
|
|
31876
31876
|
}, true);
|
|
31877
31877
|
})();
|
|
31878
31878
|
</script>`;
|
|
31879
|
+
exports2.FRONT_DESK_SNIPPET = `<script src="${process.env.SPROUTKIT_API_BASE_URL ?? "https://sproutkit.app"}/front-desk.js" defer></script>`;
|
|
31879
31880
|
}
|
|
31880
31881
|
});
|
|
31881
31882
|
|
|
@@ -81633,6 +81634,7 @@ var require_builder = __commonJS({
|
|
|
81633
81634
|
exports2.deployStandaloneBooking = deployStandaloneBooking2;
|
|
81634
81635
|
exports2.handoverSite = handoverSite2;
|
|
81635
81636
|
exports2.addEditorToSite = addEditorToSite2;
|
|
81637
|
+
exports2.redeployAsIs = redeployAsIs;
|
|
81636
81638
|
var sdk_1 = __importDefault(require_sdk());
|
|
81637
81639
|
var axios_1 = __importDefault(require_axios());
|
|
81638
81640
|
var crypto_1 = require("crypto");
|
|
@@ -82402,7 +82404,7 @@ ${prompt}` : prompt;
|
|
|
82402
82404
|
onProgress("Deploying to Vercel...");
|
|
82403
82405
|
const indexHtml = `<!DOCTYPE html>
|
|
82404
82406
|
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${lead.name}</title></head>
|
|
82405
|
-
<body style="margin:0">${bannerFor(lead)}<div id="root"></div><script type="module" src="/src/main.jsx"></script>${analytics_runtime_1.TRACK_SNIPPET}</body></html>`;
|
|
82407
|
+
<body style="margin:0">${bannerFor(lead)}<div id="root"></div><script type="module" src="/src/main.jsx"></script>${analytics_runtime_1.TRACK_SNIPPET}${analytics_runtime_1.FRONT_DESK_SNIPPET}</body></html>`;
|
|
82406
82408
|
const mainJsx = `import { StrictMode } from 'react';
|
|
82407
82409
|
import { createRoot } from 'react-dom/client';
|
|
82408
82410
|
import App from './App.jsx';
|
|
@@ -82633,7 +82635,7 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
82633
82635
|
{ source: "/book/", destination: "/book/index.html" }
|
|
82634
82636
|
]
|
|
82635
82637
|
});
|
|
82636
|
-
const indexHtmlCms = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${lead.name}</title></head><body style="margin:0">${bannerFor(lead)}<div id="root"></div><script type="module" src="/src/main.jsx"></script>${analytics_runtime_1.TRACK_SNIPPET}</body></html>`;
|
|
82638
|
+
const indexHtmlCms = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${lead.name}</title></head><body style="margin:0">${bannerFor(lead)}<div id="root"></div><script type="module" src="/src/main.jsx"></script>${analytics_runtime_1.TRACK_SNIPPET}${analytics_runtime_1.FRONT_DESK_SNIPPET}</body></html>`;
|
|
82637
82639
|
const mainJsxCms = `import { StrictMode } from 'react';
|
|
82638
82640
|
import { createRoot } from 'react-dom/client';
|
|
82639
82641
|
import App from './App.jsx';
|
|
@@ -82712,7 +82714,7 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
82712
82714
|
snapshot[appJsxIndex] = { ...snapshot[appJsxIndex], data: modifiedApp };
|
|
82713
82715
|
saveSnapshot(lead.id, snapshot);
|
|
82714
82716
|
const files = snapshot.map((f) => ({ file: f.file, data: f.data, encoding: f.encoding }));
|
|
82715
|
-
const indexHtml = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${lead.name}</title></head><body style="margin:0">${bannerFor(lead)}<div id="root"></div><script type="module" src="/src/main.jsx"></script>${analytics_runtime_1.TRACK_SNIPPET}</body></html>`;
|
|
82717
|
+
const indexHtml = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${lead.name}</title></head><body style="margin:0">${bannerFor(lead)}<div id="root"></div><script type="module" src="/src/main.jsx"></script>${analytics_runtime_1.TRACK_SNIPPET}${analytics_runtime_1.FRONT_DESK_SNIPPET}</body></html>`;
|
|
82716
82718
|
const mainJsx = `import { StrictMode } from 'react';
|
|
82717
82719
|
import { createRoot } from 'react-dom/client';
|
|
82718
82720
|
import App from './App.jsx';
|
|
@@ -83015,7 +83017,7 @@ export default defineConfig({ plugins: [react()] });`;
|
|
|
83015
83017
|
deps["@vercel/blob"] = "^0.23.4";
|
|
83016
83018
|
deps.bcryptjs = "^2.4.3";
|
|
83017
83019
|
}
|
|
83018
|
-
const indexHtml = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${lead.name}</title></head><body style="margin:0">${bannerFor(lead)}<div id="root"></div><script type="module" src="/src/main.jsx"></script>${analytics_runtime_1.TRACK_SNIPPET}</body></html>`;
|
|
83020
|
+
const indexHtml = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${lead.name}</title></head><body style="margin:0">${bannerFor(lead)}<div id="root"></div><script type="module" src="/src/main.jsx"></script>${analytics_runtime_1.TRACK_SNIPPET}${analytics_runtime_1.FRONT_DESK_SNIPPET}</body></html>`;
|
|
83019
83021
|
files.push({ file: "index.html", data: indexHtml, encoding: "utf-8" }, {
|
|
83020
83022
|
file: "src/main.jsx",
|
|
83021
83023
|
data: `import { StrictMode } from 'react';
|
|
@@ -83206,6 +83208,22 @@ Your previous reply was unusable (${lastErr}). Follow the reply format exactly.`
|
|
|
83206
83208
|
}
|
|
83207
83209
|
throw new Error(`Couldn't connect this site to the editor (${lastErr}). Try again \u2014 nothing was changed.`);
|
|
83208
83210
|
}
|
|
83211
|
+
async function redeployAsIs(args) {
|
|
83212
|
+
const { lead } = args;
|
|
83213
|
+
if (lead.booking_url) {
|
|
83214
|
+
throw new Error("This site has online booking, which a redeploy would remove. Skipped to keep bookings working.");
|
|
83215
|
+
}
|
|
83216
|
+
const uid = (0, db_1.getDb)().get("SELECT site_uid FROM leads WHERE id = ?", [lead.id])?.site_uid ?? null;
|
|
83217
|
+
return redeployWithoutBanner({
|
|
83218
|
+
lead,
|
|
83219
|
+
vercelToken: args.vercelToken,
|
|
83220
|
+
blobToken: args.blobToken,
|
|
83221
|
+
hasCms: !!lead.admin_url,
|
|
83222
|
+
adminPassword: lead.admin_password ?? null,
|
|
83223
|
+
siteUid: uid,
|
|
83224
|
+
onProgress: args.onProgress
|
|
83225
|
+
});
|
|
83226
|
+
}
|
|
83209
83227
|
function hasBookLink(code) {
|
|
83210
83228
|
return /(href\s*=\s*["'`{]*\s*["'`]?\/book\/?["'`}]?)|(location(?:\.href)?\s*=\s*["'`]\/book["'`])/i.test(code);
|
|
83211
83229
|
}
|
|
@@ -84495,13 +84513,14 @@ ${line}`;
|
|
|
84495
84513
|
lines.splice(at + 1, 0, "", line);
|
|
84496
84514
|
return lines.join("\n");
|
|
84497
84515
|
}
|
|
84498
|
-
function withReceptionist(message, url, demoNumber, businessName) {
|
|
84499
|
-
|
|
84500
|
-
|
|
84516
|
+
function withReceptionist(message, url, demoNumber, businessName, chatOn) {
|
|
84517
|
+
void demoNumber;
|
|
84518
|
+
message = message.replace(/\{front_desk\}/g, "{receptionist}");
|
|
84519
|
+
if (!url)
|
|
84501
84520
|
return message.replace(/\n?[^\n]*\{receptionist\}[^\n]*/g, "");
|
|
84502
84521
|
if (message.includes("{receptionist}"))
|
|
84503
|
-
return message.replace(/\{receptionist\}/g,
|
|
84504
|
-
const line = `I've also set up
|
|
84522
|
+
return message.replace(/\{receptionist\}/g, url);
|
|
84523
|
+
const line = `I've also set up a virtual front desk for ${businessName ?? "you"}: it answers your calls when you can't${chatOn ? ", and questions on your website" : ""}, 24/7. You can try it here: ${url}`;
|
|
84505
84524
|
const lines = message.split("\n");
|
|
84506
84525
|
const at = lines.findIndex((l) => /https?:\/\//.test(l));
|
|
84507
84526
|
if (at < 0)
|
|
@@ -84715,6 +84734,7 @@ var require_receptionist = __commonJS({
|
|
|
84715
84734
|
exports2.listAll = listAll;
|
|
84716
84735
|
exports2.activateReceptionist = activateReceptionist;
|
|
84717
84736
|
exports2.forwardingSheet = forwardingSheet;
|
|
84737
|
+
exports2.setChat = setChat;
|
|
84718
84738
|
exports2.removeReceptionist = removeReceptionist;
|
|
84719
84739
|
var db_1 = require_db();
|
|
84720
84740
|
var agency_1 = require_agency();
|
|
@@ -84841,11 +84861,14 @@ ${source.slice(0, 6e4)}`
|
|
|
84841
84861
|
const { config, ownerEmail } = await buildConfig(leadId);
|
|
84842
84862
|
if (extra)
|
|
84843
84863
|
config.extra = extra;
|
|
84864
|
+
const site = (0, db_1.getDb)().get("SELECT site_url, domain_choice, domain_status FROM leads WHERE id = ?", [leadId]);
|
|
84865
|
+
const siteHosts = [site?.site_url, site?.domain_status === "bought" ? site.domain_choice : null].filter((h) => !!h);
|
|
84844
84866
|
const r = await api("POST", "/api/receptionists", {
|
|
84845
84867
|
leadRef: String(leadId),
|
|
84846
84868
|
config,
|
|
84847
84869
|
ownerEmail,
|
|
84848
|
-
prospectPhones: config.phone ? [config.phone] : []
|
|
84870
|
+
prospectPhones: config.phone ? [config.phone] : [],
|
|
84871
|
+
siteHosts
|
|
84849
84872
|
});
|
|
84850
84873
|
(0, db_1.getDb)().run("UPDATE leads SET receptionist_id = ?, receptionist_url = ?, receptionist_line = ?, receptionist_price_pence = COALESCE(receptionist_price_pence, 4900) WHERE id = ?", [r.receptionist.id, r.trialUrl, r.demoNumber, leadId]);
|
|
84851
84874
|
return { trialUrl: r.trialUrl, id: r.receptionist.id, demoNumber: r.demoNumber };
|
|
@@ -84878,6 +84901,10 @@ ${source.slice(0, 6e4)}`
|
|
|
84878
84901
|
}
|
|
84879
84902
|
return lines.join("\n");
|
|
84880
84903
|
}
|
|
84904
|
+
async function setChat(id, leadId, on) {
|
|
84905
|
+
await api("PATCH", `/api/receptionists/${id}`, { chatEnabled: on });
|
|
84906
|
+
(0, db_1.getDb)().run("UPDATE leads SET front_desk_chat = ? WHERE id = ?", [on ? 1 : 0, leadId]);
|
|
84907
|
+
}
|
|
84881
84908
|
async function removeReceptionist(id, leadId) {
|
|
84882
84909
|
await api("DELETE", `/api/receptionists/${id}`);
|
|
84883
84910
|
(0, db_1.getDb)().run("UPDATE leads SET receptionist_id = NULL, receptionist_url = NULL WHERE id = ?", [leadId]);
|
|
@@ -98408,12 +98435,12 @@ If you'd rather not hear from me again, just reply and let me know.`;
|
|
|
98408
98435
|
}
|
|
98409
98436
|
case "prepare_outreach": {
|
|
98410
98437
|
const explicit = input.ids;
|
|
98411
|
-
const rows = explicit && explicit.length > 0 ? db2.all(`SELECT id, name, phone, email, address, city, site_url, lead_kind, booking_platform, booking_url, pitch_video_url, receptionist_url, receptionist_line FROM leads WHERE id IN (${explicit.map(() => "?").join(",")})`, explicit) : db2.all(
|
|
98438
|
+
const rows = explicit && explicit.length > 0 ? db2.all(`SELECT id, name, phone, email, address, city, site_url, lead_kind, booking_platform, booking_url, pitch_video_url, receptionist_url, receptionist_line, front_desk_chat FROM leads WHERE id IN (${explicit.map(() => "?").join(",")})`, explicit) : db2.all(
|
|
98412
98439
|
// Eligible = we have something to show them: either a demo site
|
|
98413
98440
|
// we built (site_url) OR a booking page we built (booking_url).
|
|
98414
98441
|
// Booking leads never get a site_url, so requiring it silently
|
|
98415
98442
|
// excluded every one-shot booking customer.
|
|
98416
|
-
"SELECT id, name, phone, email, address, city, site_url, lead_kind, booking_platform, booking_url, pitch_video_url, receptionist_url, receptionist_line FROM leads WHERE status IN ('site_built','discovered') AND ((site_url IS NOT NULL AND site_url != '') OR (booking_url IS NOT NULL AND booking_url != '')) ORDER BY score DESC NULLS LAST, id DESC",
|
|
98443
|
+
"SELECT id, name, phone, email, address, city, site_url, lead_kind, booking_platform, booking_url, pitch_video_url, receptionist_url, receptionist_line, front_desk_chat FROM leads WHERE status IN ('site_built','discovered') AND ((site_url IS NOT NULL AND site_url != '') OR (booking_url IS NOT NULL AND booking_url != '')) ORDER BY score DESC NULLS LAST, id DESC",
|
|
98417
98444
|
[]
|
|
98418
98445
|
);
|
|
98419
98446
|
const eligible = rows.filter((r) => r.site_url || r.booking_url);
|
|
@@ -98447,7 +98474,7 @@ If you'd rather not hear from me again, just reply and let me know.`;
|
|
|
98447
98474
|
let receptionistUrl = r.receptionist_url ?? null;
|
|
98448
98475
|
let receptionistLine = r.receptionist_line ?? null;
|
|
98449
98476
|
if (!isBookingLead && !receptionistUrl && r.site_url && getDecryptedSetting("receptionist_auto") === "1") {
|
|
98450
|
-
ctx.emitProgress?.({ channel: "outreach:progress", payload: { msg: `Setting up ${r.name}
|
|
98477
|
+
ctx.emitProgress?.({ channel: "outreach:progress", payload: { msg: `Setting up Front Desk for ${r.name}\u2026` } });
|
|
98451
98478
|
try {
|
|
98452
98479
|
const t = await (0, receptionist_1.createTrial)(r.id);
|
|
98453
98480
|
receptionistUrl = t.trialUrl;
|
|
@@ -98456,7 +98483,7 @@ If you'd rather not hear from me again, just reply and let me know.`;
|
|
|
98456
98483
|
receptionistUrl = null;
|
|
98457
98484
|
}
|
|
98458
98485
|
}
|
|
98459
|
-
const message = (0, pitch_text_1.withVideo)((0, pitch_text_1.withReceptionist)((isBookingLead ? bookingTemplate : template).replace(/\{name\}/g, r.name).replace(/\{site_url\}/g, (isBookingLead ? r.booking_url : r.site_url) ?? r.site_url ?? "").replace(/\{booking_how\}/g, r.booking_platform || "WhatsApp/phone"), isBookingLead ? null : receptionistUrl, isBookingLead ? null : receptionistLine, r.name), isBookingLead ? null : videoUrl);
|
|
98486
|
+
const message = (0, pitch_text_1.withVideo)((0, pitch_text_1.withReceptionist)((isBookingLead ? bookingTemplate : template).replace(/\{name\}/g, r.name).replace(/\{site_url\}/g, (isBookingLead ? r.booking_url : r.site_url) ?? r.site_url ?? "").replace(/\{booking_how\}/g, r.booking_platform || "WhatsApp/phone"), isBookingLead ? null : receptionistUrl, isBookingLead ? null : receptionistLine, r.name, !!r.front_desk_chat), isBookingLead ? null : videoUrl);
|
|
98460
98487
|
const e164 = toE164(r.phone ?? "");
|
|
98461
98488
|
if (!r.email && smtp && (preferEmail || !e164)) {
|
|
98462
98489
|
ctx.emitProgress?.({
|
|
@@ -100467,7 +100494,7 @@ var require_package2 = __commonJS({
|
|
|
100467
100494
|
"package.json"(exports2, module2) {
|
|
100468
100495
|
module2.exports = {
|
|
100469
100496
|
name: "sproutkit-code",
|
|
100470
|
-
version: "1.
|
|
100497
|
+
version: "1.16.0",
|
|
100471
100498
|
description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
|
|
100472
100499
|
bin: {
|
|
100473
100500
|
sproutkit: "./dist/index.js"
|