sproutkit-code 0.5.1 → 0.6.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 +134 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -80585,6 +80585,7 @@ var require_places = __commonJS({
|
|
|
80585
80585
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
80586
80586
|
exports2.PLACES_COST_PER_REQUEST_GBP = void 0;
|
|
80587
80587
|
exports2.scoreLead = scoreLead;
|
|
80588
|
+
exports2.searchPlacesStream = searchPlacesStream;
|
|
80588
80589
|
exports2.searchPlaces = searchPlaces;
|
|
80589
80590
|
exports2.geocodeForward = geocodeForward;
|
|
80590
80591
|
exports2.geocodeReverse = geocodeReverse;
|
|
@@ -80642,6 +80643,53 @@ var require_places = __commonJS({
|
|
|
80642
80643
|
return { type: args.type, places: [] };
|
|
80643
80644
|
}
|
|
80644
80645
|
}
|
|
80646
|
+
function processRawPlaces(type, places, center, radiusMeters) {
|
|
80647
|
+
const out = [];
|
|
80648
|
+
for (const p of places) {
|
|
80649
|
+
if (p.websiteUri)
|
|
80650
|
+
continue;
|
|
80651
|
+
if (center && radiusMeters && p.location) {
|
|
80652
|
+
const d = haversineMeters(center.lat, center.lng, p.location.latitude, p.location.longitude);
|
|
80653
|
+
if (d > radiusMeters)
|
|
80654
|
+
continue;
|
|
80655
|
+
}
|
|
80656
|
+
const phone = p.nationalPhoneNumber ?? "";
|
|
80657
|
+
const rating = p.rating ?? null;
|
|
80658
|
+
const review_count = p.userRatingCount ?? null;
|
|
80659
|
+
const price_level = p.priceLevel ?? null;
|
|
80660
|
+
const business_status = p.businessStatus ?? null;
|
|
80661
|
+
const has_hours = !!p.regularOpeningHours;
|
|
80662
|
+
out.push({
|
|
80663
|
+
place_id: p.id,
|
|
80664
|
+
name: p.displayName?.text ?? "Unknown",
|
|
80665
|
+
type,
|
|
80666
|
+
address: p.formattedAddress ?? "",
|
|
80667
|
+
phone,
|
|
80668
|
+
website: p.websiteUri ?? null,
|
|
80669
|
+
rating,
|
|
80670
|
+
review_count,
|
|
80671
|
+
lat: p.location?.latitude ?? null,
|
|
80672
|
+
lng: p.location?.longitude ?? null,
|
|
80673
|
+
price_level,
|
|
80674
|
+
business_status,
|
|
80675
|
+
has_hours,
|
|
80676
|
+
score: scoreLead({ rating, review_count, price_level, phone, has_hours, business_status })
|
|
80677
|
+
});
|
|
80678
|
+
}
|
|
80679
|
+
return out;
|
|
80680
|
+
}
|
|
80681
|
+
async function searchPlacesStream(args, onBatch) {
|
|
80682
|
+
await Promise.all(args.types.map(async (type) => {
|
|
80683
|
+
const { places } = await searchOneType({
|
|
80684
|
+
apiKey: args.apiKey,
|
|
80685
|
+
city: args.city,
|
|
80686
|
+
type,
|
|
80687
|
+
center: args.center,
|
|
80688
|
+
radiusMeters: args.radiusMeters
|
|
80689
|
+
});
|
|
80690
|
+
await onBatch(processRawPlaces(type, places, args.center, args.radiusMeters));
|
|
80691
|
+
}));
|
|
80692
|
+
}
|
|
80645
80693
|
async function searchPlaces(args) {
|
|
80646
80694
|
const batches = await Promise.all(args.types.map((type) => searchOneType({
|
|
80647
80695
|
apiKey: args.apiKey,
|
|
@@ -80736,9 +80784,58 @@ var require_lead_search = __commonJS({
|
|
|
80736
80784
|
"../desktop/dist/main/lib/lead-search.js"(exports2) {
|
|
80737
80785
|
"use strict";
|
|
80738
80786
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
80787
|
+
exports2.searchAndSaveLeadsStream = searchAndSaveLeadsStream;
|
|
80739
80788
|
exports2.searchAndSaveLeads = searchAndSaveLeads;
|
|
80740
80789
|
var db_1 = require_db();
|
|
80741
80790
|
var places_1 = require_places();
|
|
80791
|
+
async function searchAndSaveLeadsStream(args, onLead) {
|
|
80792
|
+
const db2 = (0, db_1.getDb)();
|
|
80793
|
+
const cap = args.maxResults ?? 50;
|
|
80794
|
+
const seen = /* @__PURE__ */ new Set();
|
|
80795
|
+
let saved = 0;
|
|
80796
|
+
let found = 0;
|
|
80797
|
+
await (0, places_1.searchPlacesStream)({
|
|
80798
|
+
apiKey: args.placesKey,
|
|
80799
|
+
city: args.city,
|
|
80800
|
+
types: args.types,
|
|
80801
|
+
center: args.center,
|
|
80802
|
+
radiusMeters: args.radiusMeters
|
|
80803
|
+
}, (batch) => {
|
|
80804
|
+
for (const r of batch) {
|
|
80805
|
+
found++;
|
|
80806
|
+
if (saved >= cap)
|
|
80807
|
+
continue;
|
|
80808
|
+
if (seen.has(r.place_id))
|
|
80809
|
+
continue;
|
|
80810
|
+
seen.add(r.place_id);
|
|
80811
|
+
const existing = db2.all("SELECT id FROM leads WHERE place_id = ?", [r.place_id]);
|
|
80812
|
+
if (existing.length > 0)
|
|
80813
|
+
continue;
|
|
80814
|
+
db2.run(`INSERT INTO leads
|
|
80815
|
+
(place_id, name, type, city, address, phone, website, rating, review_count, lat, lng, score, price_level, business_status, status)
|
|
80816
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'discovered')`, [
|
|
80817
|
+
r.place_id,
|
|
80818
|
+
r.name,
|
|
80819
|
+
r.type,
|
|
80820
|
+
args.city,
|
|
80821
|
+
r.address,
|
|
80822
|
+
r.phone,
|
|
80823
|
+
r.website,
|
|
80824
|
+
r.rating,
|
|
80825
|
+
r.review_count,
|
|
80826
|
+
r.lat,
|
|
80827
|
+
r.lng,
|
|
80828
|
+
r.score,
|
|
80829
|
+
r.price_level,
|
|
80830
|
+
r.business_status
|
|
80831
|
+
]);
|
|
80832
|
+
const row = db2.get("SELECT id FROM leads WHERE place_id = ?", [r.place_id]);
|
|
80833
|
+
saved++;
|
|
80834
|
+
onLead({ id: row?.id ?? 0, place_id: r.place_id, name: r.name, type: r.type, city: args.city });
|
|
80835
|
+
}
|
|
80836
|
+
});
|
|
80837
|
+
return { found, saved };
|
|
80838
|
+
}
|
|
80742
80839
|
async function searchAndSaveLeads(args) {
|
|
80743
80840
|
const db2 = (0, db_1.getDb)();
|
|
80744
80841
|
const results = await (0, places_1.searchPlaces)({
|
|
@@ -81106,15 +81203,20 @@ Interested?`;
|
|
|
81106
81203
|
});
|
|
81107
81204
|
const matches = await (0, places_1.geocodeForward)(location, 1);
|
|
81108
81205
|
const place = matches[0];
|
|
81109
|
-
const result = await (0, lead_search_1.
|
|
81110
|
-
|
|
81111
|
-
|
|
81112
|
-
|
|
81113
|
-
|
|
81114
|
-
|
|
81115
|
-
|
|
81116
|
-
|
|
81117
|
-
|
|
81206
|
+
const result = await (0, lead_search_1.searchAndSaveLeadsStream)(
|
|
81207
|
+
{
|
|
81208
|
+
placesKey,
|
|
81209
|
+
// searchPlaces uses `city` as the text query, so keep the user's words.
|
|
81210
|
+
city: location,
|
|
81211
|
+
types: categories,
|
|
81212
|
+
maxResults: Math.min(Number(input.maxResults ?? 25), 100),
|
|
81213
|
+
center: place ? { lat: place.lat, lng: place.lng } : void 0,
|
|
81214
|
+
radiusMeters: Number(input.radiusMeters ?? 3e3)
|
|
81215
|
+
},
|
|
81216
|
+
// Emit each lead the instant it's saved, so the UI/CLI streams results
|
|
81217
|
+
// in rather than waiting for every category to finish.
|
|
81218
|
+
(lead) => ctx.emitProgress?.({ channel: "leads:found", payload: { lead } })
|
|
81219
|
+
);
|
|
81118
81220
|
ctx.emitProgress?.({
|
|
81119
81221
|
channel: "leads:changed",
|
|
81120
81222
|
payload: { reason: "find_leads", ...result }
|
|
@@ -82282,7 +82384,7 @@ var require_package = __commonJS({
|
|
|
82282
82384
|
"package.json"(exports2, module2) {
|
|
82283
82385
|
module2.exports = {
|
|
82284
82386
|
name: "sproutkit-code",
|
|
82285
|
-
version: "0.
|
|
82387
|
+
version: "0.6.0",
|
|
82286
82388
|
description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
|
|
82287
82389
|
bin: {
|
|
82288
82390
|
sproutkit: "./dist/index.js"
|
|
@@ -83244,6 +83346,7 @@ async function runModels() {
|
|
|
83244
83346
|
// src/commands.ts
|
|
83245
83347
|
function makeCtx(opts) {
|
|
83246
83348
|
const emit = (channel, payload) => {
|
|
83349
|
+
opts.onEvent?.(channel, payload);
|
|
83247
83350
|
if (opts.json) return;
|
|
83248
83351
|
const p = payload;
|
|
83249
83352
|
if (p?.msg && opts.status) opts.status.update(p.msg);
|
|
@@ -83287,8 +83390,18 @@ async function cmdFind(argv) {
|
|
|
83287
83390
|
return;
|
|
83288
83391
|
}
|
|
83289
83392
|
const categories = String(flags.categories ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
83290
|
-
|
|
83291
|
-
|
|
83393
|
+
if (!json) say(c.dim(` Searching ${location}${categories.length ? ` for ${categories.join(", ")}` : ""}\u2026`));
|
|
83394
|
+
let streamed = 0;
|
|
83395
|
+
const onEvent = (channel, payload) => {
|
|
83396
|
+
if (json) return;
|
|
83397
|
+
if (channel === "leads:found") {
|
|
83398
|
+
const lead = payload.lead;
|
|
83399
|
+
if (lead) {
|
|
83400
|
+
streamed++;
|
|
83401
|
+
say(` ${c.green("+")} ${c.bold(lead.name)} ${c.dim(`#${lead.id} \xB7 ${lead.type}`)}`);
|
|
83402
|
+
}
|
|
83403
|
+
}
|
|
83404
|
+
};
|
|
83292
83405
|
try {
|
|
83293
83406
|
const result = await runTool(
|
|
83294
83407
|
"find_leads",
|
|
@@ -83298,14 +83411,18 @@ async function cmdFind(argv) {
|
|
|
83298
83411
|
maxResults: flags.max ? Number(flags.max) : void 0,
|
|
83299
83412
|
radiusMeters: flags.radius ? Number(flags.radius) : void 0
|
|
83300
83413
|
},
|
|
83301
|
-
makeCtx({ json,
|
|
83414
|
+
makeCtx({ json, onEvent })
|
|
83302
83415
|
);
|
|
83303
|
-
if (status) status.done(true, `Found ${result.found ?? 0}, saved ${result.saved ?? 0} new`);
|
|
83304
83416
|
autoSync();
|
|
83305
|
-
if (json)
|
|
83306
|
-
|
|
83417
|
+
if (json) {
|
|
83418
|
+
emitJson(result);
|
|
83419
|
+
} else {
|
|
83420
|
+
say();
|
|
83421
|
+
say(c.green(` Saved ${result.saved ?? streamed} new lead${(result.saved ?? streamed) === 1 ? "" : "s"}.`));
|
|
83422
|
+
if (result.note) say(c.dim(" " + result.note));
|
|
83423
|
+
say();
|
|
83424
|
+
}
|
|
83307
83425
|
} catch (e) {
|
|
83308
|
-
if (status) status.done(false, "Find failed");
|
|
83309
83426
|
json ? emitJson({ ok: false, error: e.message }) : say(c.red(" " + e.message));
|
|
83310
83427
|
process.exitCode = 1;
|
|
83311
83428
|
}
|