sproutkit-code 1.7.0 → 1.9.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 CHANGED
@@ -6,9 +6,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __commonJS = (cb, mod) => function __require() {
10
13
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
14
  };
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
12
19
  var __copyProps = (to, from, except, desc) => {
13
20
  if (from && typeof from === "object" || typeof from === "function") {
14
21
  for (let key of __getOwnPropNames(from))
@@ -29,19 +36,19 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
36
  // ../../node_modules/electron/index.js
30
37
  var require_electron = __commonJS({
31
38
  "../../node_modules/electron/index.js"(exports2, module2) {
32
- var fs3 = require("fs");
33
- var path3 = require("path");
34
- var pathFile = path3.join(__dirname, "path.txt");
39
+ var fs4 = require("fs");
40
+ var path4 = require("path");
41
+ var pathFile = path4.join(__dirname, "path.txt");
35
42
  function getElectronPath() {
36
43
  let executablePath;
37
- if (fs3.existsSync(pathFile)) {
38
- executablePath = fs3.readFileSync(pathFile, "utf-8");
44
+ if (fs4.existsSync(pathFile)) {
45
+ executablePath = fs4.readFileSync(pathFile, "utf-8");
39
46
  }
40
47
  if (process.env.ELECTRON_OVERRIDE_DIST_PATH) {
41
- return path3.join(process.env.ELECTRON_OVERRIDE_DIST_PATH, executablePath || "electron");
48
+ return path4.join(process.env.ELECTRON_OVERRIDE_DIST_PATH, executablePath || "electron");
42
49
  }
43
50
  if (executablePath) {
44
- return path3.join(__dirname, "dist", executablePath);
51
+ return path4.join(__dirname, "dist", executablePath);
45
52
  } else {
46
53
  throw new Error("Electron failed to install correctly, please delete node_modules/electron and try installing again");
47
54
  }
@@ -59,6 +66,7 @@ var require_runtime = __commonJS({
59
66
  };
60
67
  Object.defineProperty(exports2, "__esModule", { value: true });
61
68
  exports2.userDataDir = userDataDir;
69
+ exports2.isSecretSetting = isSecretSetting;
62
70
  exports2.decryptSettingValue = decryptSettingValue;
63
71
  exports2.runningHeadless = runningHeadless;
64
72
  var path_1 = __importDefault(require("path"));
@@ -80,9 +88,21 @@ var require_runtime = __commonJS({
80
88
  }
81
89
  return process.env.SPROUTKIT_DATA_DIR || path_1.default.join(os_1.default.homedir(), ".sproutkit");
82
90
  }
91
+ var SECRET_SETTINGS = /* @__PURE__ */ new Set([
92
+ "anthropic_key",
93
+ "places_key",
94
+ "vercel_token",
95
+ "blob_token",
96
+ "resend_key",
97
+ "report_key",
98
+ "smtp_pass",
99
+ "imap_pass"
100
+ ]);
101
+ function isSecretSetting(key) {
102
+ return SECRET_SETTINGS.has(key);
103
+ }
83
104
  function decryptSettingValue(key, stored) {
84
- const isSecret = key.endsWith("_key") || key.endsWith("_token");
85
- if (!isSecret)
105
+ if (!isSecretSetting(key))
86
106
  return stored;
87
107
  if (!isHeadless && electronSafeStorage) {
88
108
  try {
@@ -107,6 +127,7 @@ var require_db = __commonJS({
107
127
  return mod && mod.__esModule ? mod : { "default": mod };
108
128
  };
109
129
  Object.defineProperty(exports2, "__esModule", { value: true });
130
+ exports2.databasePath = databasePath;
110
131
  exports2.initDatabase = initDatabase2;
111
132
  exports2.getDb = getDb2;
112
133
  var node_sqlite3_wasm_1 = require("node-sqlite3-wasm");
@@ -114,11 +135,14 @@ var require_db = __commonJS({
114
135
  var fs_1 = __importDefault(require("fs"));
115
136
  var runtime_1 = require_runtime();
116
137
  var db2;
138
+ function databasePath() {
139
+ return path_1.default.join((0, runtime_1.userDataDir)(), "data", "sproutio.db");
140
+ }
117
141
  function initDatabase2() {
118
142
  const dataDir = path_1.default.join((0, runtime_1.userDataDir)(), "data");
119
143
  if (!fs_1.default.existsSync(dataDir))
120
144
  fs_1.default.mkdirSync(dataDir, { recursive: true });
121
- db2 = new node_sqlite3_wasm_1.Database(path_1.default.join(dataDir, "sproutio.db"));
145
+ db2 = new node_sqlite3_wasm_1.Database(databasePath());
122
146
  db2.exec(`
123
147
  CREATE TABLE IF NOT EXISTS settings (
124
148
  key TEXT PRIMARY KEY,
@@ -229,6 +253,12 @@ var require_db = __commonJS({
229
253
  // WITHOUT the client's password. Means handing the client full ownership
230
254
  // doesn't cost the agency its reporting.
231
255
  "report_key TEXT",
256
+ // `sproutkit audit` findings: how bad the business's EXISTING site is, and
257
+ // the specific problems to lead outreach with. Distinct from lead_kind's
258
+ // absence-based gaps.
259
+ "site_score INTEGER",
260
+ "site_issues TEXT",
261
+ "audited_at TEXT",
232
262
  // Reply loop: when we last contacted them, and the latest outcome. Kept on
233
263
  // the lead (as well as contact_log) so `followups` is a single cheap query.
234
264
  "last_contacted_at TEXT",
@@ -263,6 +293,28 @@ var require_db = __commonJS({
263
293
  } catch {
264
294
  }
265
295
  }
296
+ for (const col of ["last_email_message_id TEXT", "last_reply_snippet TEXT"]) {
297
+ try {
298
+ db2.exec(`ALTER TABLE leads ADD COLUMN ${col}`);
299
+ } catch {
300
+ }
301
+ }
302
+ db2.exec(`
303
+ CREATE TABLE IF NOT EXISTS schedules (
304
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
305
+ name TEXT NOT NULL,
306
+ action TEXT NOT NULL,
307
+ cadence TEXT NOT NULL DEFAULT 'daily',
308
+ days TEXT,
309
+ at_hour INTEGER NOT NULL DEFAULT 9,
310
+ at_minute INTEGER NOT NULL DEFAULT 0,
311
+ enabled INTEGER NOT NULL DEFAULT 1,
312
+ max_items INTEGER NOT NULL DEFAULT 10,
313
+ last_run_at TEXT,
314
+ last_result TEXT,
315
+ created_at TEXT DEFAULT (datetime('now'))
316
+ );
317
+ `);
266
318
  }
267
319
  function getDb2() {
268
320
  return db2;
@@ -1058,14 +1110,14 @@ var require_url_state_machine = __commonJS({
1058
1110
  return url.replace(/\u0009|\u000A|\u000D/g, "");
1059
1111
  }
1060
1112
  function shortenPath(url) {
1061
- const path3 = url.path;
1062
- if (path3.length === 0) {
1113
+ const path4 = url.path;
1114
+ if (path4.length === 0) {
1063
1115
  return;
1064
1116
  }
1065
- if (url.scheme === "file" && path3.length === 1 && isNormalizedWindowsDriveLetter(path3[0])) {
1117
+ if (url.scheme === "file" && path4.length === 1 && isNormalizedWindowsDriveLetter(path4[0])) {
1066
1118
  return;
1067
1119
  }
1068
- path3.pop();
1120
+ path4.pop();
1069
1121
  }
1070
1122
  function includesCredentials(url) {
1071
1123
  return url.username !== "" || url.password !== "";
@@ -6781,7 +6833,7 @@ var require_lib3 = __commonJS({
6781
6833
  return new fetch2.Promise(function(resolve, reject) {
6782
6834
  const request = new Request2(url, opts);
6783
6835
  const options = getNodeRequestOptions(request);
6784
- const send = (options.protocol === "https:" ? https : http).request;
6836
+ const send2 = (options.protocol === "https:" ? https : http).request;
6785
6837
  const signal = request.signal;
6786
6838
  let response = null;
6787
6839
  const abort = function abort2() {
@@ -6801,7 +6853,7 @@ var require_lib3 = __commonJS({
6801
6853
  abort();
6802
6854
  finalize();
6803
6855
  };
6804
- const req = send(options);
6856
+ const req = send2(options);
6805
6857
  let reqTimeout;
6806
6858
  if (signal) {
6807
6859
  signal.addEventListener("abort", abortAndFinalize);
@@ -10900,14 +10952,14 @@ var require_fileFromPath = __commonJS({
10900
10952
  return "File";
10901
10953
  }
10902
10954
  };
10903
- function createFileFromPath(path3, { mtimeMs, size }, filenameOrOptions, options = {}) {
10955
+ function createFileFromPath(path4, { mtimeMs, size }, filenameOrOptions, options = {}) {
10904
10956
  let filename;
10905
10957
  if ((0, isPlainObject_1.default)(filenameOrOptions)) {
10906
10958
  [options, filename] = [filenameOrOptions, void 0];
10907
10959
  } else {
10908
10960
  filename = filenameOrOptions;
10909
10961
  }
10910
- const file = new FileFromPath({ path: path3, size, lastModified: mtimeMs });
10962
+ const file = new FileFromPath({ path: path4, size, lastModified: mtimeMs });
10911
10963
  if (!filename) {
10912
10964
  filename = file.name;
10913
10965
  }
@@ -10916,14 +10968,14 @@ var require_fileFromPath = __commonJS({
10916
10968
  lastModified: file.lastModified
10917
10969
  });
10918
10970
  }
10919
- function fileFromPathSync(path3, filenameOrOptions, options = {}) {
10920
- const stats = (0, fs_1.statSync)(path3);
10921
- return createFileFromPath(path3, stats, filenameOrOptions, options);
10971
+ function fileFromPathSync(path4, filenameOrOptions, options = {}) {
10972
+ const stats = (0, fs_1.statSync)(path4);
10973
+ return createFileFromPath(path4, stats, filenameOrOptions, options);
10922
10974
  }
10923
10975
  exports2.fileFromPathSync = fileFromPathSync;
10924
- async function fileFromPath(path3, filenameOrOptions, options) {
10925
- const stats = await fs_1.promises.stat(path3);
10926
- return createFileFromPath(path3, stats, filenameOrOptions, options);
10976
+ async function fileFromPath(path4, filenameOrOptions, options) {
10977
+ const stats = await fs_1.promises.stat(path4);
10978
+ return createFileFromPath(path4, stats, filenameOrOptions, options);
10927
10979
  }
10928
10980
  exports2.fileFromPath = fileFromPath;
10929
10981
  }
@@ -10975,13 +11027,13 @@ var require_node_runtime = __commonJS({
10975
11027
  var MultipartBody_1 = require_MultipartBody();
10976
11028
  var web_1 = require("node:stream/web");
10977
11029
  var fileFromPathWarned = false;
10978
- async function fileFromPath(path3, ...args) {
11030
+ async function fileFromPath(path4, ...args) {
10979
11031
  const { fileFromPath: _fileFromPath } = await Promise.resolve().then(() => __importStar(require_fileFromPath()));
10980
11032
  if (!fileFromPathWarned) {
10981
- console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path3)}) instead`);
11033
+ console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path4)}) instead`);
10982
11034
  fileFromPathWarned = true;
10983
11035
  }
10984
- return await _fileFromPath(path3, ...args);
11036
+ return await _fileFromPath(path4, ...args);
10985
11037
  }
10986
11038
  var defaultHttpAgent = new agentkeepalive_1.default({ keepAlive: true, timeout: 5 * 60 * 1e3 });
10987
11039
  var defaultHttpsAgent = new agentkeepalive_1.default.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1e3 });
@@ -11746,29 +11798,29 @@ var require_core = __commonJS({
11746
11798
  defaultIdempotencyKey() {
11747
11799
  return `stainless-node-retry-${uuid4()}`;
11748
11800
  }
11749
- get(path3, opts) {
11750
- return this.methodRequest("get", path3, opts);
11801
+ get(path4, opts) {
11802
+ return this.methodRequest("get", path4, opts);
11751
11803
  }
11752
- post(path3, opts) {
11753
- return this.methodRequest("post", path3, opts);
11804
+ post(path4, opts) {
11805
+ return this.methodRequest("post", path4, opts);
11754
11806
  }
11755
- patch(path3, opts) {
11756
- return this.methodRequest("patch", path3, opts);
11807
+ patch(path4, opts) {
11808
+ return this.methodRequest("patch", path4, opts);
11757
11809
  }
11758
- put(path3, opts) {
11759
- return this.methodRequest("put", path3, opts);
11810
+ put(path4, opts) {
11811
+ return this.methodRequest("put", path4, opts);
11760
11812
  }
11761
- delete(path3, opts) {
11762
- return this.methodRequest("delete", path3, opts);
11813
+ delete(path4, opts) {
11814
+ return this.methodRequest("delete", path4, opts);
11763
11815
  }
11764
- methodRequest(method, path3, opts) {
11816
+ methodRequest(method, path4, opts) {
11765
11817
  return this.request(Promise.resolve(opts).then(async (opts2) => {
11766
11818
  const body = opts2 && (0, uploads_1.isBlobLike)(opts2?.body) ? new DataView(await opts2.body.arrayBuffer()) : opts2?.body instanceof DataView ? opts2.body : opts2?.body instanceof ArrayBuffer ? new DataView(opts2.body) : opts2 && ArrayBuffer.isView(opts2?.body) ? new DataView(opts2.body.buffer) : opts2?.body;
11767
- return { method, path: path3, ...opts2, body };
11819
+ return { method, path: path4, ...opts2, body };
11768
11820
  }));
11769
11821
  }
11770
- getAPIList(path3, Page, opts) {
11771
- return this.requestAPIList(Page, { method: "get", path: path3, ...opts });
11822
+ getAPIList(path4, Page, opts) {
11823
+ return this.requestAPIList(Page, { method: "get", path: path4, ...opts });
11772
11824
  }
11773
11825
  calculateContentLength(body) {
11774
11826
  if (typeof body === "string") {
@@ -11786,10 +11838,10 @@ var require_core = __commonJS({
11786
11838
  return null;
11787
11839
  }
11788
11840
  buildRequest(options, { retryCount = 0 } = {}) {
11789
- const { method, path: path3, query, headers = {} } = options;
11841
+ const { method, path: path4, query, headers = {} } = options;
11790
11842
  const body = ArrayBuffer.isView(options.body) || options.__binaryRequest && typeof options.body === "string" ? options.body : (0, uploads_1.isMultipartBody)(options.body) ? options.body.body : options.body ? JSON.stringify(options.body, null, 2) : null;
11791
11843
  const contentLength = this.calculateContentLength(body);
11792
- const url = this.buildURL(path3, query);
11844
+ const url = this.buildURL(path4, query);
11793
11845
  if ("timeout" in options)
11794
11846
  validatePositiveInteger("timeout", options.timeout);
11795
11847
  const timeout = options.timeout ?? this.timeout;
@@ -11902,8 +11954,8 @@ var require_core = __commonJS({
11902
11954
  const request = this.makeRequest(options, null);
11903
11955
  return new PagePromise(this, request, Page);
11904
11956
  }
11905
- buildURL(path3, query) {
11906
- const url = isAbsoluteURL(path3) ? new URL(path3) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path3.startsWith("/") ? path3.slice(1) : path3));
11957
+ buildURL(path4, query) {
11958
+ const url = isAbsoluteURL(path4) ? new URL(path4) : new URL(this.baseURL + (this.baseURL.endsWith("/") && path4.startsWith("/") ? path4.slice(1) : path4));
11907
11959
  const defaultQuery = this.defaultQuery();
11908
11960
  if (!isEmptyObj(defaultQuery)) {
11909
11961
  query = { ...defaultQuery, ...query };
@@ -13196,9 +13248,9 @@ var require_PromptCachingBetaMessageStream = __commonJS({
13196
13248
  _addPromptCachingBetaMessageParam(message) {
13197
13249
  this.messages.push(message);
13198
13250
  }
13199
- _addPromptCachingBetaMessage(message, emit = true) {
13251
+ _addPromptCachingBetaMessage(message, emit2 = true) {
13200
13252
  this.receivedMessages.push(message);
13201
- if (emit) {
13253
+ if (emit2) {
13202
13254
  this._emit("message", message);
13203
13255
  }
13204
13256
  }
@@ -13820,9 +13872,9 @@ var require_MessageStream = __commonJS({
13820
13872
  _addMessageParam(message) {
13821
13873
  this.messages.push(message);
13822
13874
  }
13823
- _addMessage(message, emit = true) {
13875
+ _addMessage(message, emit2 = true) {
13824
13876
  this.receivedMessages.push(message);
13825
- if (emit) {
13877
+ if (emit2) {
13826
13878
  this._emit("message", message);
13827
13879
  }
13828
13880
  }
@@ -23240,11 +23292,11 @@ var require_mime_types = __commonJS({
23240
23292
  }
23241
23293
  return exts[0];
23242
23294
  }
23243
- function lookup(path3) {
23244
- if (!path3 || typeof path3 !== "string") {
23295
+ function lookup(path4) {
23296
+ if (!path4 || typeof path4 !== "string") {
23245
23297
  return false;
23246
23298
  }
23247
- var extension2 = extname("x." + path3).toLowerCase().substr(1);
23299
+ var extension2 = extname("x." + path4).toLowerCase().substr(1);
23248
23300
  if (!extension2) {
23249
23301
  return false;
23250
23302
  }
@@ -24349,11 +24401,11 @@ var require_form_data = __commonJS({
24349
24401
  "use strict";
24350
24402
  var CombinedStream = require_combined_stream();
24351
24403
  var util = require("util");
24352
- var path3 = require("path");
24404
+ var path4 = require("path");
24353
24405
  var http = require("http");
24354
24406
  var https = require("https");
24355
24407
  var parseUrl = require("url").parse;
24356
- var fs3 = require("fs");
24408
+ var fs4 = require("fs");
24357
24409
  var Stream = require("stream").Stream;
24358
24410
  var crypto2 = require("crypto");
24359
24411
  var mime = require_mime_types();
@@ -24420,7 +24472,7 @@ var require_form_data = __commonJS({
24420
24472
  if (value.end != void 0 && value.end != Infinity && value.start != void 0) {
24421
24473
  callback(null, value.end + 1 - (value.start ? value.start : 0));
24422
24474
  } else {
24423
- fs3.stat(value.path, function(err, stat) {
24475
+ fs4.stat(value.path, function(err, stat) {
24424
24476
  if (err) {
24425
24477
  callback(err);
24426
24478
  return;
@@ -24477,11 +24529,11 @@ var require_form_data = __commonJS({
24477
24529
  FormData2.prototype._getContentDisposition = function(value, options) {
24478
24530
  var filename;
24479
24531
  if (typeof options.filepath === "string") {
24480
- filename = path3.normalize(options.filepath).replace(/\\/g, "/");
24532
+ filename = path4.normalize(options.filepath).replace(/\\/g, "/");
24481
24533
  } else if (options.filename || value && (value.name || value.path)) {
24482
- filename = path3.basename(options.filename || value && (value.name || value.path));
24534
+ filename = path4.basename(options.filename || value && (value.name || value.path));
24483
24535
  } else if (value && value.readable && hasOwn(value, "httpVersion")) {
24484
- filename = path3.basename(value.client._httpMessage.path || "");
24536
+ filename = path4.basename(value.client._httpMessage.path || "");
24485
24537
  }
24486
24538
  if (filename) {
24487
24539
  return 'filename="' + filename + '"';
@@ -25026,7 +25078,7 @@ var require_has_flag = __commonJS({
25026
25078
  var require_supports_color = __commonJS({
25027
25079
  "../../node_modules/supports-color/index.js"(exports2, module2) {
25028
25080
  "use strict";
25029
- var os4 = require("os");
25081
+ var os5 = require("os");
25030
25082
  var tty = require("tty");
25031
25083
  var hasFlag = require_has_flag();
25032
25084
  var { env } = process;
@@ -25074,7 +25126,7 @@ var require_supports_color = __commonJS({
25074
25126
  return min;
25075
25127
  }
25076
25128
  if (process.platform === "win32") {
25077
- const osRelease = os4.release().split(".");
25129
+ const osRelease = os5.release().split(".");
25078
25130
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
25079
25131
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
25080
25132
  }
@@ -26302,7 +26354,7 @@ var require_axios = __commonJS({
26302
26354
  var https = require("https");
26303
26355
  var http2 = require("http2");
26304
26356
  var util = require("util");
26305
- var path3 = require("path");
26357
+ var path4 = require("path");
26306
26358
  var followRedirects = require_follow_redirects();
26307
26359
  var zlib = require("zlib");
26308
26360
  var stream = require("stream");
@@ -27189,9 +27241,9 @@ var require_axios = __commonJS({
27189
27241
  function removeBrackets(key) {
27190
27242
  return utils$1.endsWith(key, "[]") ? key.slice(0, -2) : key;
27191
27243
  }
27192
- function renderKey(path4, key, dots) {
27193
- if (!path4) return key;
27194
- return path4.concat(key).map(function each(token, i) {
27244
+ function renderKey(path5, key, dots) {
27245
+ if (!path5) return key;
27246
+ return path5.concat(key).map(function each(token, i) {
27195
27247
  token = removeBrackets(token);
27196
27248
  return !dots && i ? "[" + token + "]" : token;
27197
27249
  }).join(dots ? "." : "");
@@ -27240,13 +27292,13 @@ var require_axios = __commonJS({
27240
27292
  }
27241
27293
  return value;
27242
27294
  }
27243
- function defaultVisitor(value, key, path4) {
27295
+ function defaultVisitor(value, key, path5) {
27244
27296
  let arr = value;
27245
27297
  if (utils$1.isReactNative(formData) && utils$1.isReactNativeBlob(value)) {
27246
- formData.append(renderKey(path4, key, dots), convertValue(value));
27298
+ formData.append(renderKey(path5, key, dots), convertValue(value));
27247
27299
  return false;
27248
27300
  }
27249
- if (value && !path4 && typeof value === "object") {
27301
+ if (value && !path5 && typeof value === "object") {
27250
27302
  if (utils$1.endsWith(key, "{}")) {
27251
27303
  key = metaTokens ? key : key.slice(0, -2);
27252
27304
  value = JSON.stringify(value);
@@ -27265,7 +27317,7 @@ var require_axios = __commonJS({
27265
27317
  if (isVisitable(value)) {
27266
27318
  return true;
27267
27319
  }
27268
- formData.append(renderKey(path4, key, dots), convertValue(value));
27320
+ formData.append(renderKey(path5, key, dots), convertValue(value));
27269
27321
  return false;
27270
27322
  }
27271
27323
  const stack = [];
@@ -27274,19 +27326,19 @@ var require_axios = __commonJS({
27274
27326
  convertValue,
27275
27327
  isVisitable
27276
27328
  });
27277
- function build(value, path4, depth = 0) {
27329
+ function build(value, path5, depth = 0) {
27278
27330
  if (utils$1.isUndefined(value)) return;
27279
27331
  if (depth > maxDepth) {
27280
27332
  throw new AxiosError("Object is too deeply nested (" + depth + " levels). Max depth: " + maxDepth, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED);
27281
27333
  }
27282
27334
  if (stack.indexOf(value) !== -1) {
27283
- throw Error("Circular reference detected in " + path4.join("."));
27335
+ throw Error("Circular reference detected in " + path5.join("."));
27284
27336
  }
27285
27337
  stack.push(value);
27286
27338
  utils$1.forEach(value, function each(el, key) {
27287
- const result = !(utils$1.isUndefined(el) || el === null) && visitor.call(formData, el, utils$1.isString(key) ? key.trim() : key, path4, exposedHelpers);
27339
+ const result = !(utils$1.isUndefined(el) || el === null) && visitor.call(formData, el, utils$1.isString(key) ? key.trim() : key, path5, exposedHelpers);
27288
27340
  if (result === true) {
27289
- build(el, path4 ? path4.concat(key) : [key], depth + 1);
27341
+ build(el, path5 ? path5.concat(key) : [key], depth + 1);
27290
27342
  }
27291
27343
  });
27292
27344
  stack.pop();
@@ -27474,7 +27526,7 @@ var require_axios = __commonJS({
27474
27526
  };
27475
27527
  function toURLEncodedForm(data, options) {
27476
27528
  return toFormData(data, new platform.classes.URLSearchParams(), {
27477
- visitor: function(value, key, path4, helpers) {
27529
+ visitor: function(value, key, path5, helpers) {
27478
27530
  if (platform.isNode && utils$1.isBuffer(value)) {
27479
27531
  this.append(key, value.toString("base64"));
27480
27532
  return false;
@@ -27502,11 +27554,11 @@ var require_axios = __commonJS({
27502
27554
  return obj;
27503
27555
  }
27504
27556
  function formDataToJSON(formData) {
27505
- function buildPath(path4, value, target, index) {
27506
- let name = path4[index++];
27557
+ function buildPath(path5, value, target, index) {
27558
+ let name = path5[index++];
27507
27559
  if (name === "__proto__") return true;
27508
27560
  const isNumericKey = Number.isFinite(+name);
27509
- const isLast = index >= path4.length;
27561
+ const isLast = index >= path5.length;
27510
27562
  name = !name && utils$1.isArray(target) ? target.length : name;
27511
27563
  if (isLast) {
27512
27564
  if (utils$1.hasOwnProp(target, name)) {
@@ -27519,7 +27571,7 @@ var require_axios = __commonJS({
27519
27571
  if (!utils$1.hasOwnProp(target, name) || !utils$1.isObject(target[name])) {
27520
27572
  target[name] = [];
27521
27573
  }
27522
- const result = buildPath(path4, value, target[name], index);
27574
+ const result = buildPath(path5, value, target[name], index);
27523
27575
  if (result && utils$1.isArray(target[name])) {
27524
27576
  target[name] = arrayToObject(target[name]);
27525
27577
  }
@@ -28889,8 +28941,8 @@ var require_axios = __commonJS({
28889
28941
  }
28890
28942
  if (config.allowedSocketPaths != null) {
28891
28943
  const allowed = Array.isArray(config.allowedSocketPaths) ? config.allowedSocketPaths : [config.allowedSocketPaths];
28892
- const resolvedSocket = path3.resolve(config.socketPath);
28893
- const isAllowed = allowed.some((entry) => typeof entry === "string" && path3.resolve(entry) === resolvedSocket);
28944
+ const resolvedSocket = path4.resolve(config.socketPath);
28945
+ const isAllowed = allowed.some((entry) => typeof entry === "string" && path4.resolve(entry) === resolvedSocket);
28894
28946
  if (!isAllowed) {
28895
28947
  return reject(new AxiosError(`socketPath "${config.socketPath}" is not permitted by allowedSocketPaths`, AxiosError.ERR_BAD_OPTION_VALUE, config));
28896
28948
  }
@@ -29144,14 +29196,14 @@ var require_axios = __commonJS({
29144
29196
  var cookies = platform.hasStandardBrowserEnv ? (
29145
29197
  // Standard browser envs support document.cookie
29146
29198
  {
29147
- write(name, value, expires, path4, domain, secure, sameSite) {
29199
+ write(name, value, expires, path5, domain, secure, sameSite) {
29148
29200
  if (typeof document === "undefined") return;
29149
29201
  const cookie = [`${name}=${encodeURIComponent(value)}`];
29150
29202
  if (utils$1.isNumber(expires)) {
29151
29203
  cookie.push(`expires=${new Date(expires).toUTCString()}`);
29152
29204
  }
29153
- if (utils$1.isString(path4)) {
29154
- cookie.push(`path=${path4}`);
29205
+ if (utils$1.isString(path5)) {
29206
+ cookie.push(`path=${path5}`);
29155
29207
  }
29156
29208
  if (utils$1.isString(domain)) {
29157
29209
  cookie.push(`domain=${domain}`);
@@ -31232,12 +31284,21 @@ var require_agency = __commonJS({
31232
31284
  "../desktop/dist/main/lib/agency.js"(exports2) {
31233
31285
  "use strict";
31234
31286
  Object.defineProperty(exports2, "__esModule", { value: true });
31287
+ exports2.DEFAULT_CURRENCY = exports2.CURRENCIES = void 0;
31235
31288
  exports2.getAgencyProfile = getAgencyProfile2;
31236
31289
  exports2.setAgencyProfile = setAgencyProfile2;
31237
31290
  exports2.agencyConfigured = agencyConfigured2;
31238
31291
  exports2.siteCredit = siteCredit;
31239
31292
  exports2.formatMoney = formatMoney2;
31240
31293
  var db_1 = require_db();
31294
+ exports2.CURRENCIES = [
31295
+ { code: "USD", symbol: "$", label: "US dollar" },
31296
+ { code: "GBP", symbol: "\xA3", label: "Pound sterling" },
31297
+ { code: "EUR", symbol: "\u20AC", label: "Euro" },
31298
+ { code: "CAD", symbol: "CA$", label: "Canadian dollar" },
31299
+ { code: "AUD", symbol: "A$", label: "Australian dollar" }
31300
+ ];
31301
+ exports2.DEFAULT_CURRENCY = "USD";
31241
31302
  var KEYS = {
31242
31303
  name: "agency_name",
31243
31304
  logoPath: "agency_logo_path",
@@ -31283,7 +31344,7 @@ var require_agency = __commonJS({
31283
31344
  bankAccount: read(KEYS.bankAccount),
31284
31345
  bankSortCode: read(KEYS.bankSortCode),
31285
31346
  paymentTerms: read(KEYS.paymentTerms),
31286
- currency: read("agency_currency") ?? "GBP"
31347
+ currency: read("agency_currency") ?? exports2.DEFAULT_CURRENCY
31287
31348
  };
31288
31349
  }
31289
31350
  function setAgencyProfile2(patch) {
@@ -31307,9 +31368,8 @@ var require_agency = __commonJS({
31307
31368
  return { label: "Sproutkit", url: "https://sproutkit.app" };
31308
31369
  return { label: p.name, url: p.website };
31309
31370
  }
31310
- function formatMoney2(pence, currency = "GBP") {
31311
- const symbols = { GBP: "\xA3", USD: "$", EUR: "\u20AC" };
31312
- const sym = symbols[currency] ?? "";
31371
+ function formatMoney2(pence, currency = exports2.DEFAULT_CURRENCY) {
31372
+ const sym = exports2.CURRENCIES.find((c2) => c2.code === currency)?.symbol ?? "";
31313
31373
  const value = (pence / 100).toFixed(2);
31314
31374
  return `${sym}${value}`;
31315
31375
  }
@@ -35805,7 +35865,7 @@ var require_lib4 = __commonJS({
35805
35865
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
35806
35866
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
35807
35867
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
35808
- var __export = (target, all) => {
35868
+ var __export2 = (target, all) => {
35809
35869
  for (var name in all)
35810
35870
  __defProp2(target, name, { get: all[name], enumerable: true });
35811
35871
  };
@@ -35819,7 +35879,7 @@ var require_lib4 = __commonJS({
35819
35879
  };
35820
35880
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
35821
35881
  var src_exports = {};
35822
- __export(src_exports, {
35882
+ __export2(src_exports, {
35823
35883
  isNodeProcess: () => isNodeProcess
35824
35884
  });
35825
35885
  module2.exports = __toCommonJS(src_exports);
@@ -35856,7 +35916,7 @@ var require_version2 = __commonJS({
35856
35916
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
35857
35917
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
35858
35918
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
35859
- var __export = (target, all) => {
35919
+ var __export2 = (target, all) => {
35860
35920
  for (var name in all)
35861
35921
  __defProp2(target, name, { get: all[name], enumerable: true });
35862
35922
  };
@@ -35870,7 +35930,7 @@ var require_version2 = __commonJS({
35870
35930
  };
35871
35931
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
35872
35932
  var version_exports = {};
35873
- __export(version_exports, {
35933
+ __export2(version_exports, {
35874
35934
  version: () => version
35875
35935
  });
35876
35936
  module2.exports = __toCommonJS(version_exports);
@@ -35886,7 +35946,7 @@ var require_exchange_vercel_oidc_token = __commonJS({
35886
35946
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
35887
35947
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
35888
35948
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
35889
- var __export = (target, all) => {
35949
+ var __export2 = (target, all) => {
35890
35950
  for (var name in all)
35891
35951
  __defProp2(target, name, { get: all[name], enumerable: true });
35892
35952
  };
@@ -35900,7 +35960,7 @@ var require_exchange_vercel_oidc_token = __commonJS({
35900
35960
  };
35901
35961
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
35902
35962
  var exchange_vercel_oidc_token_exports = {};
35903
- __export(exchange_vercel_oidc_token_exports, {
35963
+ __export2(exchange_vercel_oidc_token_exports, {
35904
35964
  exchangeVercelOidcToken: () => exchangeVercelOidcToken2
35905
35965
  });
35906
35966
  module2.exports = __toCommonJS(exchange_vercel_oidc_token_exports);
@@ -36025,7 +36085,7 @@ var require_get_context = __commonJS({
36025
36085
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
36026
36086
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
36027
36087
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
36028
- var __export = (target, all) => {
36088
+ var __export2 = (target, all) => {
36029
36089
  for (var name in all)
36030
36090
  __defProp2(target, name, { get: all[name], enumerable: true });
36031
36091
  };
@@ -36039,7 +36099,7 @@ var require_get_context = __commonJS({
36039
36099
  };
36040
36100
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
36041
36101
  var get_context_exports = {};
36042
- __export(get_context_exports, {
36102
+ __export2(get_context_exports, {
36043
36103
  SYMBOL_FOR_REQ_CONTEXT: () => SYMBOL_FOR_REQ_CONTEXT,
36044
36104
  getContext: () => getContext2
36045
36105
  });
@@ -36060,7 +36120,7 @@ var require_get_vercel_oidc_token_sync = __commonJS({
36060
36120
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
36061
36121
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
36062
36122
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
36063
- var __export = (target, all) => {
36123
+ var __export2 = (target, all) => {
36064
36124
  for (var name in all)
36065
36125
  __defProp2(target, name, { get: all[name], enumerable: true });
36066
36126
  };
@@ -36074,7 +36134,7 @@ var require_get_vercel_oidc_token_sync = __commonJS({
36074
36134
  };
36075
36135
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
36076
36136
  var get_vercel_oidc_token_sync_exports = {};
36077
- __export(get_vercel_oidc_token_sync_exports, {
36137
+ __export2(get_vercel_oidc_token_sync_exports, {
36078
36138
  getVercelOidcTokenSync: () => getVercelOidcTokenSync2
36079
36139
  });
36080
36140
  module2.exports = __toCommonJS(get_vercel_oidc_token_sync_exports);
@@ -36099,7 +36159,7 @@ var require_token_error = __commonJS({
36099
36159
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
36100
36160
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
36101
36161
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
36102
- var __export = (target, all) => {
36162
+ var __export2 = (target, all) => {
36103
36163
  for (var name in all)
36104
36164
  __defProp2(target, name, { get: all[name], enumerable: true });
36105
36165
  };
@@ -36113,7 +36173,7 @@ var require_token_error = __commonJS({
36113
36173
  };
36114
36174
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
36115
36175
  var token_error_exports = {};
36116
- __export(token_error_exports, {
36176
+ __export2(token_error_exports, {
36117
36177
  VercelOidcTokenError: () => VercelOidcTokenError
36118
36178
  });
36119
36179
  module2.exports = __toCommonJS(token_error_exports);
@@ -36141,7 +36201,7 @@ var require_errors = __commonJS({
36141
36201
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
36142
36202
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
36143
36203
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
36144
- var __export = (target, all) => {
36204
+ var __export2 = (target, all) => {
36145
36205
  for (var name in all)
36146
36206
  __defProp2(target, name, { get: all[name], enumerable: true });
36147
36207
  };
@@ -36155,7 +36215,7 @@ var require_errors = __commonJS({
36155
36215
  };
36156
36216
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
36157
36217
  var errors_exports = {};
36158
- __export(errors_exports, {
36218
+ __export2(errors_exports, {
36159
36219
  VercelCliError: () => VercelCliError2,
36160
36220
  assertValidCwd: () => assertValidCwd,
36161
36221
  getCliNotFoundMessage: () => getCliNotFoundMessage,
@@ -36294,8 +36354,8 @@ var require_windows = __commonJS({
36294
36354
  "../../node_modules/isexe/windows.js"(exports2, module2) {
36295
36355
  module2.exports = isexe;
36296
36356
  isexe.sync = sync;
36297
- var fs3 = require("fs");
36298
- function checkPathExt(path3, options) {
36357
+ var fs4 = require("fs");
36358
+ function checkPathExt(path4, options) {
36299
36359
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
36300
36360
  if (!pathext) {
36301
36361
  return true;
@@ -36306,25 +36366,25 @@ var require_windows = __commonJS({
36306
36366
  }
36307
36367
  for (var i = 0; i < pathext.length; i++) {
36308
36368
  var p = pathext[i].toLowerCase();
36309
- if (p && path3.substr(-p.length).toLowerCase() === p) {
36369
+ if (p && path4.substr(-p.length).toLowerCase() === p) {
36310
36370
  return true;
36311
36371
  }
36312
36372
  }
36313
36373
  return false;
36314
36374
  }
36315
- function checkStat(stat, path3, options) {
36375
+ function checkStat(stat, path4, options) {
36316
36376
  if (!stat.isSymbolicLink() && !stat.isFile()) {
36317
36377
  return false;
36318
36378
  }
36319
- return checkPathExt(path3, options);
36379
+ return checkPathExt(path4, options);
36320
36380
  }
36321
- function isexe(path3, options, cb) {
36322
- fs3.stat(path3, function(er, stat) {
36323
- cb(er, er ? false : checkStat(stat, path3, options));
36381
+ function isexe(path4, options, cb) {
36382
+ fs4.stat(path4, function(er, stat) {
36383
+ cb(er, er ? false : checkStat(stat, path4, options));
36324
36384
  });
36325
36385
  }
36326
- function sync(path3, options) {
36327
- return checkStat(fs3.statSync(path3), path3, options);
36386
+ function sync(path4, options) {
36387
+ return checkStat(fs4.statSync(path4), path4, options);
36328
36388
  }
36329
36389
  }
36330
36390
  });
@@ -36334,14 +36394,14 @@ var require_mode = __commonJS({
36334
36394
  "../../node_modules/isexe/mode.js"(exports2, module2) {
36335
36395
  module2.exports = isexe;
36336
36396
  isexe.sync = sync;
36337
- var fs3 = require("fs");
36338
- function isexe(path3, options, cb) {
36339
- fs3.stat(path3, function(er, stat) {
36397
+ var fs4 = require("fs");
36398
+ function isexe(path4, options, cb) {
36399
+ fs4.stat(path4, function(er, stat) {
36340
36400
  cb(er, er ? false : checkStat(stat, options));
36341
36401
  });
36342
36402
  }
36343
- function sync(path3, options) {
36344
- return checkStat(fs3.statSync(path3), options);
36403
+ function sync(path4, options) {
36404
+ return checkStat(fs4.statSync(path4), options);
36345
36405
  }
36346
36406
  function checkStat(stat, options) {
36347
36407
  return stat.isFile() && checkMode(stat, options);
@@ -36365,7 +36425,7 @@ var require_mode = __commonJS({
36365
36425
  // ../../node_modules/isexe/index.js
36366
36426
  var require_isexe = __commonJS({
36367
36427
  "../../node_modules/isexe/index.js"(exports2, module2) {
36368
- var fs3 = require("fs");
36428
+ var fs4 = require("fs");
36369
36429
  var core;
36370
36430
  if (process.platform === "win32" || global.TESTING_WINDOWS) {
36371
36431
  core = require_windows();
@@ -36374,7 +36434,7 @@ var require_isexe = __commonJS({
36374
36434
  }
36375
36435
  module2.exports = isexe;
36376
36436
  isexe.sync = sync;
36377
- function isexe(path3, options, cb) {
36437
+ function isexe(path4, options, cb) {
36378
36438
  if (typeof options === "function") {
36379
36439
  cb = options;
36380
36440
  options = {};
@@ -36384,7 +36444,7 @@ var require_isexe = __commonJS({
36384
36444
  throw new TypeError("callback not provided");
36385
36445
  }
36386
36446
  return new Promise(function(resolve, reject) {
36387
- isexe(path3, options || {}, function(er, is) {
36447
+ isexe(path4, options || {}, function(er, is) {
36388
36448
  if (er) {
36389
36449
  reject(er);
36390
36450
  } else {
@@ -36393,7 +36453,7 @@ var require_isexe = __commonJS({
36393
36453
  });
36394
36454
  });
36395
36455
  }
36396
- core(path3, options || {}, function(er, is) {
36456
+ core(path4, options || {}, function(er, is) {
36397
36457
  if (er) {
36398
36458
  if (er.code === "EACCES" || options && options.ignoreErrors) {
36399
36459
  er = null;
@@ -36403,9 +36463,9 @@ var require_isexe = __commonJS({
36403
36463
  cb(er, is);
36404
36464
  });
36405
36465
  }
36406
- function sync(path3, options) {
36466
+ function sync(path4, options) {
36407
36467
  try {
36408
- return core.sync(path3, options || {});
36468
+ return core.sync(path4, options || {});
36409
36469
  } catch (er) {
36410
36470
  if (options && options.ignoreErrors || er.code === "EACCES") {
36411
36471
  return false;
@@ -36421,7 +36481,7 @@ var require_isexe = __commonJS({
36421
36481
  var require_which = __commonJS({
36422
36482
  "../../node_modules/which/which.js"(exports2, module2) {
36423
36483
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
36424
- var path3 = require("path");
36484
+ var path4 = require("path");
36425
36485
  var COLON = isWindows ? ";" : ":";
36426
36486
  var isexe = require_isexe();
36427
36487
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -36459,7 +36519,7 @@ var require_which = __commonJS({
36459
36519
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
36460
36520
  const ppRaw = pathEnv[i];
36461
36521
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
36462
- const pCmd = path3.join(pathPart, cmd);
36522
+ const pCmd = path4.join(pathPart, cmd);
36463
36523
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
36464
36524
  resolve(subStep(p, i, 0));
36465
36525
  });
@@ -36486,7 +36546,7 @@ var require_which = __commonJS({
36486
36546
  for (let i = 0; i < pathEnv.length; i++) {
36487
36547
  const ppRaw = pathEnv[i];
36488
36548
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
36489
- const pCmd = path3.join(pathPart, cmd);
36549
+ const pCmd = path4.join(pathPart, cmd);
36490
36550
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
36491
36551
  for (let j = 0; j < pathExt.length; j++) {
36492
36552
  const cur = p + pathExt[j];
@@ -36534,7 +36594,7 @@ var require_path_key = __commonJS({
36534
36594
  var require_resolveCommand = __commonJS({
36535
36595
  "../../node_modules/cross-spawn/lib/util/resolveCommand.js"(exports2, module2) {
36536
36596
  "use strict";
36537
- var path3 = require("path");
36597
+ var path4 = require("path");
36538
36598
  var which = require_which();
36539
36599
  var getPathKey = require_path_key();
36540
36600
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -36552,7 +36612,7 @@ var require_resolveCommand = __commonJS({
36552
36612
  try {
36553
36613
  resolved = which.sync(parsed.command, {
36554
36614
  path: env[getPathKey({ env })],
36555
- pathExt: withoutPathExt ? path3.delimiter : void 0
36615
+ pathExt: withoutPathExt ? path4.delimiter : void 0
36556
36616
  });
36557
36617
  } catch (e) {
36558
36618
  } finally {
@@ -36561,7 +36621,7 @@ var require_resolveCommand = __commonJS({
36561
36621
  }
36562
36622
  }
36563
36623
  if (resolved) {
36564
- resolved = path3.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
36624
+ resolved = path4.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
36565
36625
  }
36566
36626
  return resolved;
36567
36627
  }
@@ -36615,8 +36675,8 @@ var require_shebang_command = __commonJS({
36615
36675
  if (!match) {
36616
36676
  return null;
36617
36677
  }
36618
- const [path3, argument] = match[0].replace(/#! ?/, "").split(" ");
36619
- const binary = path3.split("/").pop();
36678
+ const [path4, argument] = match[0].replace(/#! ?/, "").split(" ");
36679
+ const binary = path4.split("/").pop();
36620
36680
  if (binary === "env") {
36621
36681
  return argument;
36622
36682
  }
@@ -36629,16 +36689,16 @@ var require_shebang_command = __commonJS({
36629
36689
  var require_readShebang = __commonJS({
36630
36690
  "../../node_modules/cross-spawn/lib/util/readShebang.js"(exports2, module2) {
36631
36691
  "use strict";
36632
- var fs3 = require("fs");
36692
+ var fs4 = require("fs");
36633
36693
  var shebangCommand = require_shebang_command();
36634
36694
  function readShebang(command) {
36635
36695
  const size = 150;
36636
36696
  const buffer = Buffer.alloc(size);
36637
36697
  let fd;
36638
36698
  try {
36639
- fd = fs3.openSync(command, "r");
36640
- fs3.readSync(fd, buffer, 0, size, 0);
36641
- fs3.closeSync(fd);
36699
+ fd = fs4.openSync(command, "r");
36700
+ fs4.readSync(fd, buffer, 0, size, 0);
36701
+ fs4.closeSync(fd);
36642
36702
  } catch (e) {
36643
36703
  }
36644
36704
  return shebangCommand(buffer.toString());
@@ -36651,7 +36711,7 @@ var require_readShebang = __commonJS({
36651
36711
  var require_parse = __commonJS({
36652
36712
  "../../node_modules/cross-spawn/lib/parse.js"(exports2, module2) {
36653
36713
  "use strict";
36654
- var path3 = require("path");
36714
+ var path4 = require("path");
36655
36715
  var resolveCommand = require_resolveCommand();
36656
36716
  var escape = require_escape();
36657
36717
  var readShebang = require_readShebang();
@@ -36676,7 +36736,7 @@ var require_parse = __commonJS({
36676
36736
  const needsShell = !isExecutableRegExp.test(commandFile);
36677
36737
  if (parsed.options.forceShell || needsShell) {
36678
36738
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
36679
- parsed.command = path3.normalize(parsed.command);
36739
+ parsed.command = path4.normalize(parsed.command);
36680
36740
  parsed.command = escape.command(parsed.command);
36681
36741
  parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
36682
36742
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -36772,7 +36832,7 @@ var require_cross_spawn = __commonJS({
36772
36832
  enoent.hookChildProcess(spawned, parsed);
36773
36833
  return spawned;
36774
36834
  }
36775
- function spawnSync4(command, args, options) {
36835
+ function spawnSync5(command, args, options) {
36776
36836
  const parsed = parse(command, args, options);
36777
36837
  const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
36778
36838
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
@@ -36780,7 +36840,7 @@ var require_cross_spawn = __commonJS({
36780
36840
  }
36781
36841
  module2.exports = spawn2;
36782
36842
  module2.exports.spawn = spawn2;
36783
- module2.exports.sync = spawnSync4;
36843
+ module2.exports.sync = spawnSync5;
36784
36844
  module2.exports._parse = parse;
36785
36845
  module2.exports._enoent = enoent;
36786
36846
  }
@@ -36808,7 +36868,7 @@ var require_strip_final_newline = __commonJS({
36808
36868
  var require_npm_run_path = __commonJS({
36809
36869
  "../../node_modules/npm-run-path/index.js"(exports2, module2) {
36810
36870
  "use strict";
36811
- var path3 = require("path");
36871
+ var path4 = require("path");
36812
36872
  var pathKey = require_path_key();
36813
36873
  var npmRunPath = (options) => {
36814
36874
  options = {
@@ -36818,16 +36878,16 @@ var require_npm_run_path = __commonJS({
36818
36878
  ...options
36819
36879
  };
36820
36880
  let previous;
36821
- let cwdPath = path3.resolve(options.cwd);
36881
+ let cwdPath = path4.resolve(options.cwd);
36822
36882
  const result = [];
36823
36883
  while (previous !== cwdPath) {
36824
- result.push(path3.join(cwdPath, "node_modules/.bin"));
36884
+ result.push(path4.join(cwdPath, "node_modules/.bin"));
36825
36885
  previous = cwdPath;
36826
- cwdPath = path3.resolve(cwdPath, "..");
36886
+ cwdPath = path4.resolve(cwdPath, "..");
36827
36887
  }
36828
- const execPathDir = path3.resolve(options.cwd, options.execPath, "..");
36888
+ const execPathDir = path4.resolve(options.cwd, options.execPath, "..");
36829
36889
  result.push(execPathDir);
36830
- return result.concat(options.path).join(path3.delimiter);
36890
+ return result.concat(options.path).join(path4.delimiter);
36831
36891
  };
36832
36892
  module2.exports = npmRunPath;
36833
36893
  module2.exports.default = npmRunPath;
@@ -36837,9 +36897,9 @@ var require_npm_run_path = __commonJS({
36837
36897
  ...options
36838
36898
  };
36839
36899
  const env = { ...options.env };
36840
- const path4 = pathKey({ env });
36841
- options.path = env[path4];
36842
- env[path4] = module2.exports(options);
36900
+ const path5 = pathKey({ env });
36901
+ options.path = env[path5];
36902
+ env[path5] = module2.exports(options);
36843
36903
  return env;
36844
36904
  };
36845
36905
  }
@@ -37521,7 +37581,7 @@ var require_signal_exit = __commonJS({
37521
37581
  emitter.count -= 1;
37522
37582
  };
37523
37583
  module2.exports.unload = unload;
37524
- emit = function emit2(event, code, signal) {
37584
+ emit2 = function emit3(event, code, signal) {
37525
37585
  if (emitter.emitted[event]) {
37526
37586
  return;
37527
37587
  }
@@ -37537,8 +37597,8 @@ var require_signal_exit = __commonJS({
37537
37597
  var listeners = process2.listeners(sig);
37538
37598
  if (listeners.length === emitter.count) {
37539
37599
  unload();
37540
- emit("exit", null, sig);
37541
- emit("afterexit", null, sig);
37600
+ emit2("exit", null, sig);
37601
+ emit2("afterexit", null, sig);
37542
37602
  if (isWin && sig === "SIGHUP") {
37543
37603
  sig = "SIGINT";
37544
37604
  }
@@ -37575,8 +37635,8 @@ var require_signal_exit = __commonJS({
37575
37635
  }
37576
37636
  process2.exitCode = code || /* istanbul ignore next */
37577
37637
  0;
37578
- emit("exit", process2.exitCode, null);
37579
- emit("afterexit", process2.exitCode, null);
37638
+ emit2("exit", process2.exitCode, null);
37639
+ emit2("afterexit", process2.exitCode, null);
37580
37640
  originalProcessReallyExit.call(process2, process2.exitCode);
37581
37641
  };
37582
37642
  originalProcessEmit = process2.emit;
@@ -37586,8 +37646,8 @@ var require_signal_exit = __commonJS({
37586
37646
  process2.exitCode = arg;
37587
37647
  }
37588
37648
  var ret = originalProcessEmit.apply(this, arguments);
37589
- emit("exit", process2.exitCode, null);
37590
- emit("afterexit", process2.exitCode, null);
37649
+ emit2("exit", process2.exitCode, null);
37650
+ emit2("afterexit", process2.exitCode, null);
37591
37651
  return ret;
37592
37652
  } else {
37593
37653
  return originalProcessEmit.apply(this, arguments);
@@ -37600,7 +37660,7 @@ var require_signal_exit = __commonJS({
37600
37660
  var EE;
37601
37661
  var emitter;
37602
37662
  var unload;
37603
- var emit;
37663
+ var emit2;
37604
37664
  var sigListeners;
37605
37665
  var loaded;
37606
37666
  var load;
@@ -37615,7 +37675,7 @@ var require_signal_exit = __commonJS({
37615
37675
  var require_kill = __commonJS({
37616
37676
  "../../node_modules/execa/lib/kill.js"(exports2, module2) {
37617
37677
  "use strict";
37618
- var os4 = require("os");
37678
+ var os5 = require("os");
37619
37679
  var onExit = require_signal_exit();
37620
37680
  var DEFAULT_FORCE_KILL_TIMEOUT = 1e3 * 5;
37621
37681
  var spawnedKill = (kill, signal = "SIGTERM", options = {}) => {
@@ -37639,7 +37699,7 @@ var require_kill = __commonJS({
37639
37699
  return isSigterm(signal) && forceKillAfterTimeout !== false && killResult;
37640
37700
  };
37641
37701
  var isSigterm = (signal) => {
37642
- return signal === os4.constants.signals.SIGTERM || typeof signal === "string" && signal.toUpperCase() === "SIGTERM";
37702
+ return signal === os5.constants.signals.SIGTERM || typeof signal === "string" && signal.toUpperCase() === "SIGTERM";
37643
37703
  };
37644
37704
  var getForceKillAfterTimeout = ({ forceKillAfterTimeout = true }) => {
37645
37705
  if (forceKillAfterTimeout === true) {
@@ -38021,7 +38081,7 @@ var require_command = __commonJS({
38021
38081
  var require_execa = __commonJS({
38022
38082
  "../../node_modules/execa/index.js"(exports2, module2) {
38023
38083
  "use strict";
38024
- var path3 = require("path");
38084
+ var path4 = require("path");
38025
38085
  var childProcess = require("child_process");
38026
38086
  var crossSpawn = require_cross_spawn();
38027
38087
  var stripFinalNewline = require_strip_final_newline();
@@ -38063,7 +38123,7 @@ var require_execa = __commonJS({
38063
38123
  };
38064
38124
  options.env = getEnv(options);
38065
38125
  options.stdio = normalizeStdio(options);
38066
- if (process.platform === "win32" && path3.basename(file, ".exe") === "cmd") {
38126
+ if (process.platform === "win32" && path4.basename(file, ".exe") === "cmd") {
38067
38127
  args.unshift("/q");
38068
38128
  }
38069
38129
  return { file, args, options, parsed };
@@ -38255,7 +38315,7 @@ var require_envpath = __commonJS({
38255
38315
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
38256
38316
  var __getProtoOf2 = Object.getPrototypeOf;
38257
38317
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
38258
- var __export = (target, all) => {
38318
+ var __export2 = (target, all) => {
38259
38319
  for (var name in all)
38260
38320
  __defProp2(target, name, { get: all[name], enumerable: true });
38261
38321
  };
@@ -38277,7 +38337,7 @@ var require_envpath = __commonJS({
38277
38337
  ));
38278
38338
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
38279
38339
  var envpath_exports = {};
38280
- __export(envpath_exports, {
38340
+ __export2(envpath_exports, {
38281
38341
  getEnvPath: () => getEnvPath,
38282
38342
  prependPathEntries: () => prependPathEntries,
38283
38343
  setEnvPath: () => setEnvPath,
@@ -38341,7 +38401,7 @@ var require_errutils = __commonJS({
38341
38401
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
38342
38402
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
38343
38403
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
38344
- var __export = (target, all) => {
38404
+ var __export2 = (target, all) => {
38345
38405
  for (var name in all)
38346
38406
  __defProp2(target, name, { get: all[name], enumerable: true });
38347
38407
  };
@@ -38355,7 +38415,7 @@ var require_errutils = __commonJS({
38355
38415
  };
38356
38416
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
38357
38417
  var errutils_exports = {};
38358
- __export(errutils_exports, {
38418
+ __export2(errutils_exports, {
38359
38419
  getErrorMessage: () => getErrorMessage,
38360
38420
  isMissingPathError: () => isMissingPathError
38361
38421
  });
@@ -38382,7 +38442,7 @@ var require_fsutils = __commonJS({
38382
38442
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
38383
38443
  var __getProtoOf2 = Object.getPrototypeOf;
38384
38444
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
38385
- var __export = (target, all) => {
38445
+ var __export2 = (target, all) => {
38386
38446
  for (var name in all)
38387
38447
  __defProp2(target, name, { get: all[name], enumerable: true });
38388
38448
  };
@@ -38404,7 +38464,7 @@ var require_fsutils = __commonJS({
38404
38464
  ));
38405
38465
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
38406
38466
  var fsutils_exports = {};
38407
- __export(fsutils_exports, {
38467
+ __export2(fsutils_exports, {
38408
38468
  getCanonicalPath: () => getCanonicalPath,
38409
38469
  getCommandBase: () => getCommandBase,
38410
38470
  getDirectoriesBetween: () => getDirectoriesBetween,
@@ -38476,7 +38536,7 @@ var require_safety = __commonJS({
38476
38536
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
38477
38537
  var __getProtoOf2 = Object.getPrototypeOf;
38478
38538
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
38479
- var __export = (target, all) => {
38539
+ var __export2 = (target, all) => {
38480
38540
  for (var name in all)
38481
38541
  __defProp2(target, name, { get: all[name], enumerable: true });
38482
38542
  };
@@ -38498,7 +38558,7 @@ var require_safety = __commonJS({
38498
38558
  ));
38499
38559
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
38500
38560
  var safety_exports = {};
38501
- __export(safety_exports, {
38561
+ __export2(safety_exports, {
38502
38562
  getSkippedNodeModulesReason: () => getSkippedNodeModulesReason,
38503
38563
  getUnsafeDirectoryReason: () => getUnsafeDirectoryReason,
38504
38564
  getUnsafePackageBinReason: () => getUnsafePackageBinReason,
@@ -38644,7 +38704,7 @@ var require_lookup = __commonJS({
38644
38704
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
38645
38705
  var __getProtoOf2 = Object.getPrototypeOf;
38646
38706
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
38647
- var __export = (target, all) => {
38707
+ var __export2 = (target, all) => {
38648
38708
  for (var name in all)
38649
38709
  __defProp2(target, name, { get: all[name], enumerable: true });
38650
38710
  };
@@ -38666,7 +38726,7 @@ var require_lookup = __commonJS({
38666
38726
  ));
38667
38727
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
38668
38728
  var lookup_exports = {};
38669
- __export(lookup_exports, {
38729
+ __export2(lookup_exports, {
38670
38730
  clearCachedCliInvocation: () => clearCachedCliInvocation,
38671
38731
  clearVercelCliLookupCache: () => clearVercelCliLookupCache2,
38672
38732
  findVercelCli: () => findVercelCli2,
@@ -39112,7 +39172,7 @@ var require_exec = __commonJS({
39112
39172
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
39113
39173
  var __getProtoOf2 = Object.getPrototypeOf;
39114
39174
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
39115
- var __export = (target, all) => {
39175
+ var __export2 = (target, all) => {
39116
39176
  for (var name in all)
39117
39177
  __defProp2(target, name, { get: all[name], enumerable: true });
39118
39178
  };
@@ -39134,7 +39194,7 @@ var require_exec = __commonJS({
39134
39194
  ));
39135
39195
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
39136
39196
  var exec_exports = {};
39137
- __export(exec_exports, {
39197
+ __export2(exec_exports, {
39138
39198
  execVercelCli: () => execVercelCli2
39139
39199
  });
39140
39200
  module2.exports = __toCommonJS(exec_exports);
@@ -39225,7 +39285,7 @@ var require_dist2 = __commonJS({
39225
39285
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
39226
39286
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
39227
39287
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
39228
- var __export = (target, all) => {
39288
+ var __export2 = (target, all) => {
39229
39289
  for (var name in all)
39230
39290
  __defProp2(target, name, { get: all[name], enumerable: true });
39231
39291
  };
@@ -39239,7 +39299,7 @@ var require_dist2 = __commonJS({
39239
39299
  };
39240
39300
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
39241
39301
  var src_exports = {};
39242
- __export(src_exports, {
39302
+ __export2(src_exports, {
39243
39303
  VercelCliError: () => import_errors.VercelCliError,
39244
39304
  clearVercelCliLookupCache: () => import_lookup.clearVercelCliLookupCache,
39245
39305
  execVercelCli: () => import_exec.execVercelCli,
@@ -39509,10 +39569,10 @@ var require_util = __commonJS({
39509
39569
  function cloneDef(schema) {
39510
39570
  return mergeDefs(schema._zod.def);
39511
39571
  }
39512
- function getElementAtPath(obj, path3) {
39513
- if (!path3)
39572
+ function getElementAtPath(obj, path4) {
39573
+ if (!path4)
39514
39574
  return obj;
39515
- return path3.reduce((acc, key) => acc?.[key], obj);
39575
+ return path4.reduce((acc, key) => acc?.[key], obj);
39516
39576
  }
39517
39577
  function promiseAllObject(promisesObj) {
39518
39578
  const keys = Object.keys(promisesObj);
@@ -39874,11 +39934,11 @@ var require_util = __commonJS({
39874
39934
  }
39875
39935
  return false;
39876
39936
  }
39877
- function prefixIssues(path3, issues) {
39937
+ function prefixIssues(path4, issues) {
39878
39938
  return issues.map((iss) => {
39879
39939
  var _a;
39880
39940
  (_a = iss).path ?? (_a.path = []);
39881
- iss.path.unshift(path3);
39941
+ iss.path.unshift(path4);
39882
39942
  return iss;
39883
39943
  });
39884
39944
  }
@@ -40088,7 +40148,7 @@ var require_errors2 = __commonJS({
40088
40148
  return issue.message;
40089
40149
  };
40090
40150
  const result = { errors: [] };
40091
- const processError = (error2, path3 = []) => {
40151
+ const processError = (error2, path4 = []) => {
40092
40152
  var _a, _b;
40093
40153
  for (const issue of error2.issues) {
40094
40154
  if (issue.code === "invalid_union" && issue.errors.length) {
@@ -40098,7 +40158,7 @@ var require_errors2 = __commonJS({
40098
40158
  } else if (issue.code === "invalid_element") {
40099
40159
  processError({ issues: issue.issues }, issue.path);
40100
40160
  } else {
40101
- const fullpath = [...path3, ...issue.path];
40161
+ const fullpath = [...path4, ...issue.path];
40102
40162
  if (fullpath.length === 0) {
40103
40163
  result.errors.push(mapper(issue));
40104
40164
  continue;
@@ -40130,8 +40190,8 @@ var require_errors2 = __commonJS({
40130
40190
  }
40131
40191
  function toDotPath(_path) {
40132
40192
  const segs = [];
40133
- const path3 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
40134
- for (const seg of path3) {
40193
+ const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
40194
+ for (const seg of path4) {
40135
40195
  if (typeof seg === "number")
40136
40196
  segs.push(`[${seg}]`);
40137
40197
  else if (typeof seg === "symbol")
@@ -53975,27 +54035,27 @@ var require_schema = __commonJS({
53975
54035
  var require_lib5 = __commonJS({
53976
54036
  "../../node_modules/os-paths/src/lib/index.js"(exports2, module2) {
53977
54037
  "use strict";
53978
- var os4 = require("os");
54038
+ var os5 = require("os");
53979
54039
  var paths = require("path");
53980
54040
  var isWinOS = /^win/i.test(process.platform);
53981
- function normalize_path(path3) {
53982
- return paths.normalize(paths.join(path3, "."));
54041
+ function normalize_path(path4) {
54042
+ return paths.normalize(paths.join(path4, "."));
53983
54043
  }
53984
54044
  var base = () => {
53985
54045
  const { env } = process;
53986
54046
  const object = {};
53987
- object.home = () => normalize_path(os4.homedir ? os4.homedir() : env.HOME);
53988
- object.temp = () => normalize_path(os4.tmpdir ? os4.tmpdir() : env.TMPDIR || env.TEMP || env.TMP);
54047
+ object.home = () => normalize_path(os5.homedir ? os5.homedir() : env.HOME);
54048
+ object.temp = () => normalize_path(os5.tmpdir ? os5.tmpdir() : env.TMPDIR || env.TEMP || env.TMP);
53989
54049
  return object;
53990
54050
  };
53991
54051
  var windows = () => {
53992
54052
  const { env } = process;
53993
54053
  const object = {};
53994
54054
  object.home = () => normalize_path(
53995
- os4.homedir ? os4.homedir() : env.USERPROFILE || paths.join(env.HOMEDRIVE, env.HOMEPATH) || env.HOME
54055
+ os5.homedir ? os5.homedir() : env.USERPROFILE || paths.join(env.HOMEDRIVE, env.HOMEPATH) || env.HOME
53996
54056
  );
53997
54057
  object.temp = () => normalize_path(
53998
- os4.tmpdir ? os4.tmpdir() : env.TEMP || env.TMP || paths.join(env.LOCALAPPDATA || env.SystemRoot || env.windir, "Temp")
54058
+ os5.tmpdir ? os5.tmpdir() : env.TEMP || env.TMP || paths.join(env.LOCALAPPDATA || env.SystemRoot || env.windir, "Temp")
53999
54059
  );
54000
54060
  return object;
54001
54061
  };
@@ -54020,44 +54080,44 @@ var require_lib5 = __commonJS({
54020
54080
  var require_lib6 = __commonJS({
54021
54081
  "../../node_modules/xdg-portable/src/lib/index.js"(exports2, module2) {
54022
54082
  "use strict";
54023
- var path3 = require("path");
54083
+ var path4 = require("path");
54024
54084
  var osPaths = require_lib5();
54025
54085
  var linux = () => {
54026
54086
  const object = {};
54027
- object.cache = () => process.env.XDG_CACHE_HOME || path3.join(osPaths.home() || osPaths.temp(), ".cache");
54028
- object.config = () => process.env.XDG_CONFIG_HOME || path3.join(osPaths.home() || osPaths.temp(), ".config");
54029
- object.data = () => process.env.XDG_DATA_HOME || path3.join(osPaths.home() || osPaths.temp(), ".local", "share");
54087
+ object.cache = () => process.env.XDG_CACHE_HOME || path4.join(osPaths.home() || osPaths.temp(), ".cache");
54088
+ object.config = () => process.env.XDG_CONFIG_HOME || path4.join(osPaths.home() || osPaths.temp(), ".config");
54089
+ object.data = () => process.env.XDG_DATA_HOME || path4.join(osPaths.home() || osPaths.temp(), ".local", "share");
54030
54090
  object.runtime = () => process.env.XDG_RUNTIME_DIR || void 0;
54031
- object.state = () => process.env.XDG_STATE_HOME || path3.join(osPaths.home() || osPaths.temp(), ".local", "state");
54091
+ object.state = () => process.env.XDG_STATE_HOME || path4.join(osPaths.home() || osPaths.temp(), ".local", "state");
54032
54092
  return object;
54033
54093
  };
54034
54094
  var macos = () => {
54035
54095
  const object = {};
54036
- object.cache = () => process.env.XDG_CACHE_HOME || path3.join(path3.join(osPaths.home() || osPaths.temp(), "Library"), "Caches");
54037
- object.config = () => process.env.XDG_CONFIG_HOME || path3.join(path3.join(osPaths.home() || osPaths.temp(), "Library"), "Preferences");
54038
- object.data = () => process.env.XDG_DATA_HOME || path3.join(path3.join(osPaths.home() || osPaths.temp(), "Library"), "Application Support");
54096
+ object.cache = () => process.env.XDG_CACHE_HOME || path4.join(path4.join(osPaths.home() || osPaths.temp(), "Library"), "Caches");
54097
+ object.config = () => process.env.XDG_CONFIG_HOME || path4.join(path4.join(osPaths.home() || osPaths.temp(), "Library"), "Preferences");
54098
+ object.data = () => process.env.XDG_DATA_HOME || path4.join(path4.join(osPaths.home() || osPaths.temp(), "Library"), "Application Support");
54039
54099
  object.runtime = () => process.env.XDG_RUNTIME_DIR || void 0;
54040
- object.state = () => process.env.XDG_STATE_HOME || path3.join(path3.join(osPaths.home() || osPaths.temp(), "Library"), "State");
54100
+ object.state = () => process.env.XDG_STATE_HOME || path4.join(path4.join(osPaths.home() || osPaths.temp(), "Library"), "State");
54041
54101
  return object;
54042
54102
  };
54043
54103
  var windows = () => {
54044
54104
  const object = {};
54045
54105
  object.cache = () => {
54046
- const localAppData = process.env.LOCALAPPDATA || path3.join(osPaths.home() || osPaths.temp(), "AppData", "Local");
54047
- return process.env.XDG_CACHE_HOME || path3.join(localAppData, "xdg.cache");
54106
+ const localAppData = process.env.LOCALAPPDATA || path4.join(osPaths.home() || osPaths.temp(), "AppData", "Local");
54107
+ return process.env.XDG_CACHE_HOME || path4.join(localAppData, "xdg.cache");
54048
54108
  };
54049
54109
  object.config = () => {
54050
- const appData = process.env.APPDATA || path3.join(osPaths.home() || osPaths.temp(), "AppData", "Roaming");
54051
- return process.env.XDG_CONFIG_HOME || path3.join(appData, "xdg.config");
54110
+ const appData = process.env.APPDATA || path4.join(osPaths.home() || osPaths.temp(), "AppData", "Roaming");
54111
+ return process.env.XDG_CONFIG_HOME || path4.join(appData, "xdg.config");
54052
54112
  };
54053
54113
  object.data = () => {
54054
- const appData = process.env.APPDATA || path3.join(osPaths.home() || osPaths.temp(), "AppData", "Roaming");
54055
- return process.env.XDG_DATA_HOME || path3.join(appData, "xdg.data");
54114
+ const appData = process.env.APPDATA || path4.join(osPaths.home() || osPaths.temp(), "AppData", "Roaming");
54115
+ return process.env.XDG_DATA_HOME || path4.join(appData, "xdg.data");
54056
54116
  };
54057
54117
  object.runtime = () => process.env.XDG_RUNTIME_DIR || void 0;
54058
54118
  object.state = () => {
54059
- const localAppData = process.env.LOCALAPPDATA || path3.join(osPaths.home() || osPaths.temp(), "AppData", "Local");
54060
- return process.env.XDG_STATE_HOME || path3.join(localAppData, "xdg.state");
54119
+ const localAppData = process.env.LOCALAPPDATA || path4.join(osPaths.home() || osPaths.temp(), "AppData", "Local");
54120
+ return process.env.XDG_STATE_HOME || path4.join(localAppData, "xdg.state");
54061
54121
  };
54062
54122
  return object;
54063
54123
  };
@@ -54077,7 +54137,7 @@ var require_lib6 = __commonJS({
54077
54137
  const dirs = [];
54078
54138
  dirs.push(extension.config());
54079
54139
  if (process.env.XDG_CONFIG_DIRS) {
54080
- dirs.push(...process.env.XDG_CONFIG_DIRS.split(path3.delimiter));
54140
+ dirs.push(...process.env.XDG_CONFIG_DIRS.split(path4.delimiter));
54081
54141
  }
54082
54142
  return dirs;
54083
54143
  };
@@ -54085,7 +54145,7 @@ var require_lib6 = __commonJS({
54085
54145
  const dirs = [];
54086
54146
  dirs.push(extension.data());
54087
54147
  if (process.env.XDG_DATA_DIRS) {
54088
- dirs.push(...process.env.XDG_DATA_DIRS.split(path3.delimiter));
54148
+ dirs.push(...process.env.XDG_DATA_DIRS.split(path4.delimiter));
54089
54149
  }
54090
54150
  return dirs;
54091
54151
  };
@@ -54102,7 +54162,7 @@ var require_lib6 = __commonJS({
54102
54162
  var require_lib7 = __commonJS({
54103
54163
  "../../node_modules/xdg-app-paths/src/lib/index.js"(exports2, module2) {
54104
54164
  "use strict";
54105
- var path3 = require("path");
54165
+ var path4 = require("path");
54106
54166
  var xdg = require_lib6();
54107
54167
  function normalizeOptions_(options, isolated) {
54108
54168
  if (!isObject(options)) {
@@ -54154,38 +54214,38 @@ var require_lib7 = __commonJS({
54154
54214
  throw new TypeError(`Expected string for "name" argument, got ${typeOf(name_)}`);
54155
54215
  }
54156
54216
  if (!name_) {
54157
- name_ = path3.parse(require.main && require.main.filename || process.execPath).name;
54217
+ name_ = path4.parse(require.main && require.main.filename || process.execPath).name;
54158
54218
  }
54159
54219
  name_ += suffix_ || "";
54160
54220
  XDGAppPaths.$name = () => name_;
54161
54221
  XDGAppPaths.$isolated = () => isolated_;
54162
54222
  XDGAppPaths.cache = (dirOptions = null) => {
54163
54223
  dirOptions = normalizeOptions_(dirOptions, isolated_);
54164
- return path3.join(xdg.cache(), dirOptions.isolated ? name_ : "");
54224
+ return path4.join(xdg.cache(), dirOptions.isolated ? name_ : "");
54165
54225
  };
54166
54226
  XDGAppPaths.config = function(dirOptions = null) {
54167
54227
  dirOptions = normalizeOptions_(dirOptions, isolated_);
54168
- return path3.join(xdg.config(), dirOptions.isolated ? name_ : "");
54228
+ return path4.join(xdg.config(), dirOptions.isolated ? name_ : "");
54169
54229
  };
54170
54230
  XDGAppPaths.data = function(dirOptions = null) {
54171
54231
  dirOptions = normalizeOptions_(dirOptions, isolated_);
54172
- return path3.join(xdg.data(), dirOptions.isolated ? name_ : "");
54232
+ return path4.join(xdg.data(), dirOptions.isolated ? name_ : "");
54173
54233
  };
54174
54234
  XDGAppPaths.runtime = function(dirOptions = null) {
54175
54235
  dirOptions = normalizeOptions_(dirOptions, isolated_);
54176
- return xdg.runtime() ? path3.join(xdg.runtime(), dirOptions.isolated ? name_ : "") : void 0;
54236
+ return xdg.runtime() ? path4.join(xdg.runtime(), dirOptions.isolated ? name_ : "") : void 0;
54177
54237
  };
54178
54238
  XDGAppPaths.state = function(dirOptions = null) {
54179
54239
  dirOptions = normalizeOptions_(dirOptions, isolated_);
54180
- return path3.join(xdg.state(), dirOptions.isolated ? name_ : "");
54240
+ return path4.join(xdg.state(), dirOptions.isolated ? name_ : "");
54181
54241
  };
54182
54242
  XDGAppPaths.configDirs = function(dirOptions = null) {
54183
54243
  dirOptions = normalizeOptions_(dirOptions, isolated_);
54184
- return xdg.configDirs().map((s) => path3.join(s, dirOptions.isolated ? name_ : ""));
54244
+ return xdg.configDirs().map((s) => path4.join(s, dirOptions.isolated ? name_ : ""));
54185
54245
  };
54186
54246
  XDGAppPaths.dataDirs = function(dirOptions = null) {
54187
54247
  dirOptions = normalizeOptions_(dirOptions, isolated_);
54188
- return xdg.dataDirs().map((s) => path3.join(s, dirOptions.isolated ? name_ : ""));
54248
+ return xdg.dataDirs().map((s) => path4.join(s, dirOptions.isolated ? name_ : ""));
54189
54249
  };
54190
54250
  this.fn = XDGAppPaths;
54191
54251
  }
@@ -54479,7 +54539,7 @@ var require_token_io = __commonJS({
54479
54539
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
54480
54540
  var __getProtoOf2 = Object.getPrototypeOf;
54481
54541
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
54482
- var __export = (target, all) => {
54542
+ var __export2 = (target, all) => {
54483
54543
  for (var name in all)
54484
54544
  __defProp2(target, name, { get: all[name], enumerable: true });
54485
54545
  };
@@ -54501,24 +54561,24 @@ var require_token_io = __commonJS({
54501
54561
  ));
54502
54562
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
54503
54563
  var token_io_exports = {};
54504
- __export(token_io_exports, {
54564
+ __export2(token_io_exports, {
54505
54565
  findRootDir: () => findRootDir,
54506
54566
  getUserDataDir: () => getUserDataDir
54507
54567
  });
54508
54568
  module2.exports = __toCommonJS(token_io_exports);
54509
- var import_path3 = __toESM2(require("path"));
54510
- var import_fs3 = __toESM2(require("fs"));
54511
- var import_os4 = __toESM2(require("os"));
54569
+ var import_path4 = __toESM2(require("path"));
54570
+ var import_fs4 = __toESM2(require("fs"));
54571
+ var import_os5 = __toESM2(require("os"));
54512
54572
  var import_token_error = require_token_error();
54513
54573
  function findRootDir() {
54514
54574
  try {
54515
54575
  let dir = process.cwd();
54516
- while (dir !== import_path3.default.dirname(dir)) {
54517
- const pkgPath = import_path3.default.join(dir, ".vercel");
54518
- if (import_fs3.default.existsSync(pkgPath)) {
54576
+ while (dir !== import_path4.default.dirname(dir)) {
54577
+ const pkgPath = import_path4.default.join(dir, ".vercel");
54578
+ if (import_fs4.default.existsSync(pkgPath)) {
54519
54579
  return dir;
54520
54580
  }
54521
- dir = import_path3.default.dirname(dir);
54581
+ dir = import_path4.default.dirname(dir);
54522
54582
  }
54523
54583
  } catch (_e) {
54524
54584
  throw new import_token_error.VercelOidcTokenError(
@@ -54531,11 +54591,11 @@ var require_token_io = __commonJS({
54531
54591
  if (process.env.XDG_DATA_HOME) {
54532
54592
  return process.env.XDG_DATA_HOME;
54533
54593
  }
54534
- switch (import_os4.default.platform()) {
54594
+ switch (import_os5.default.platform()) {
54535
54595
  case "darwin":
54536
- return import_path3.default.join(import_os4.default.homedir(), "Library/Application Support");
54596
+ return import_path4.default.join(import_os5.default.homedir(), "Library/Application Support");
54537
54597
  case "linux":
54538
- return import_path3.default.join(import_os4.default.homedir(), ".local/share");
54598
+ return import_path4.default.join(import_os5.default.homedir(), ".local/share");
54539
54599
  case "win32":
54540
54600
  if (process.env.LOCALAPPDATA) {
54541
54601
  return process.env.LOCALAPPDATA;
@@ -54556,7 +54616,7 @@ var require_oauth = __commonJS({
54556
54616
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
54557
54617
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
54558
54618
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
54559
- var __export = (target, all) => {
54619
+ var __export2 = (target, all) => {
54560
54620
  for (var name in all)
54561
54621
  __defProp2(target, name, { get: all[name], enumerable: true });
54562
54622
  };
@@ -54570,15 +54630,15 @@ var require_oauth = __commonJS({
54570
54630
  };
54571
54631
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
54572
54632
  var oauth_exports = {};
54573
- __export(oauth_exports, {
54633
+ __export2(oauth_exports, {
54574
54634
  processTokenResponse: () => processTokenResponse,
54575
54635
  refreshTokenRequest: () => refreshTokenRequest
54576
54636
  });
54577
54637
  module2.exports = __toCommonJS(oauth_exports);
54578
- var import_os4 = require("os");
54638
+ var import_os5 = require("os");
54579
54639
  var VERCEL_ISSUER = "https://vercel.com";
54580
54640
  var VERCEL_CLI_CLIENT_ID = "cl_HYyOPBNtFMfHhaUn9L4QPfTZz6TP47bp";
54581
- var userAgent = `@vercel/oidc node-${process.version} ${(0, import_os4.platform)()} (${(0, import_os4.arch)()}) ${(0, import_os4.hostname)()}`;
54641
+ var userAgent = `@vercel/oidc node-${process.version} ${(0, import_os5.platform)()} (${(0, import_os5.arch)()}) ${(0, import_os5.hostname)()}`;
54582
54642
  var _tokenEndpoint = null;
54583
54643
  async function getTokenEndpoint() {
54584
54644
  if (_tokenEndpoint) {
@@ -54645,7 +54705,7 @@ var require_auth_errors = __commonJS({
54645
54705
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
54646
54706
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
54647
54707
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
54648
- var __export = (target, all) => {
54708
+ var __export2 = (target, all) => {
54649
54709
  for (var name in all)
54650
54710
  __defProp2(target, name, { get: all[name], enumerable: true });
54651
54711
  };
@@ -54659,7 +54719,7 @@ var require_auth_errors = __commonJS({
54659
54719
  };
54660
54720
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
54661
54721
  var auth_errors_exports = {};
54662
- __export(auth_errors_exports, {
54722
+ __export2(auth_errors_exports, {
54663
54723
  AccessTokenMissingError: () => AccessTokenMissingError2,
54664
54724
  RefreshAccessTokenFailedError: () => RefreshAccessTokenFailedError2
54665
54725
  });
@@ -54694,7 +54754,7 @@ var require_token_util = __commonJS({
54694
54754
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
54695
54755
  var __getProtoOf2 = Object.getPrototypeOf;
54696
54756
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
54697
- var __export = (target, all) => {
54757
+ var __export2 = (target, all) => {
54698
54758
  for (var name in all)
54699
54759
  __defProp2(target, name, { get: all[name], enumerable: true });
54700
54760
  };
@@ -54716,7 +54776,7 @@ var require_token_util = __commonJS({
54716
54776
  ));
54717
54777
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
54718
54778
  var token_util_exports = {};
54719
- __export(token_util_exports, {
54779
+ __export2(token_util_exports, {
54720
54780
  assertVercelOidcTokenResponse: () => assertVercelOidcTokenResponse,
54721
54781
  findProjectInfo: () => findProjectInfo,
54722
54782
  getTokenPayload: () => getTokenPayload,
@@ -54728,8 +54788,8 @@ var require_token_util = __commonJS({
54728
54788
  saveToken: () => saveToken
54729
54789
  });
54730
54790
  module2.exports = __toCommonJS(token_util_exports);
54731
- var path3 = __toESM2(require("path"));
54732
- var fs3 = __toESM2(require("fs"));
54791
+ var path4 = __toESM2(require("path"));
54792
+ var fs4 = __toESM2(require("fs"));
54733
54793
  var import_cli_exec = require_dist2();
54734
54794
  var import_cli_config = require_dist3();
54735
54795
  var import_token_error = require_token_error();
@@ -54853,13 +54913,13 @@ ${stderr}`.trim();
54853
54913
  "Unable to find project root directory. Have you linked your project with `vc link?`"
54854
54914
  );
54855
54915
  }
54856
- const prjPath = path3.join(dir, ".vercel", "project.json");
54857
- if (!fs3.existsSync(prjPath)) {
54916
+ const prjPath = path4.join(dir, ".vercel", "project.json");
54917
+ if (!fs4.existsSync(prjPath)) {
54858
54918
  throw new import_token_error.VercelOidcTokenError(
54859
54919
  "project.json not found, have you linked your project with `vc link?`"
54860
54920
  );
54861
54921
  }
54862
- const prj = JSON.parse(fs3.readFileSync(prjPath, "utf8"));
54922
+ const prj = JSON.parse(fs4.readFileSync(prjPath, "utf8"));
54863
54923
  if (typeof prj.projectId !== "string" && typeof prj.orgId !== "string") {
54864
54924
  throw new TypeError(
54865
54925
  "Expected a string-valued projectId property. Try running `vc link` to re-link your project."
@@ -54874,11 +54934,11 @@ ${stderr}`.trim();
54874
54934
  "Unable to find user data directory. Please reach out to Vercel support."
54875
54935
  );
54876
54936
  }
54877
- const tokenPath = path3.join(dir, "com.vercel.token", `${projectId}.json`);
54937
+ const tokenPath = path4.join(dir, "com.vercel.token", `${projectId}.json`);
54878
54938
  const tokenJson = JSON.stringify(token);
54879
- fs3.mkdirSync(path3.dirname(tokenPath), { mode: 504, recursive: true });
54880
- fs3.writeFileSync(tokenPath, tokenJson);
54881
- fs3.chmodSync(tokenPath, 432);
54939
+ fs4.mkdirSync(path4.dirname(tokenPath), { mode: 504, recursive: true });
54940
+ fs4.writeFileSync(tokenPath, tokenJson);
54941
+ fs4.chmodSync(tokenPath, 432);
54882
54942
  return;
54883
54943
  }
54884
54944
  function loadToken(projectId) {
@@ -54888,11 +54948,11 @@ ${stderr}`.trim();
54888
54948
  "Unable to find user data directory. Please reach out to Vercel support."
54889
54949
  );
54890
54950
  }
54891
- const tokenPath = path3.join(dir, "com.vercel.token", `${projectId}.json`);
54892
- if (!fs3.existsSync(tokenPath)) {
54951
+ const tokenPath = path4.join(dir, "com.vercel.token", `${projectId}.json`);
54952
+ if (!fs4.existsSync(tokenPath)) {
54893
54953
  return null;
54894
54954
  }
54895
- const token = JSON.parse(fs3.readFileSync(tokenPath, "utf8"));
54955
+ const token = JSON.parse(fs4.readFileSync(tokenPath, "utf8"));
54896
54956
  assertVercelOidcTokenResponse(token);
54897
54957
  return token;
54898
54958
  }
@@ -54922,7 +54982,7 @@ var require_token = __commonJS({
54922
54982
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
54923
54983
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
54924
54984
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
54925
- var __export = (target, all) => {
54985
+ var __export2 = (target, all) => {
54926
54986
  for (var name in all)
54927
54987
  __defProp2(target, name, { get: all[name], enumerable: true });
54928
54988
  };
@@ -54936,7 +54996,7 @@ var require_token = __commonJS({
54936
54996
  };
54937
54997
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
54938
54998
  var token_exports = {};
54939
- __export(token_exports, {
54999
+ __export2(token_exports, {
54940
55000
  refreshToken: () => refreshToken
54941
55001
  });
54942
55002
  module2.exports = __toCommonJS(token_exports);
@@ -54990,7 +55050,7 @@ var require_get_vercel_oidc_token_with_refresh = __commonJS({
54990
55050
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
54991
55051
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
54992
55052
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
54993
- var __export = (target, all) => {
55053
+ var __export2 = (target, all) => {
54994
55054
  for (var name in all)
54995
55055
  __defProp2(target, name, { get: all[name], enumerable: true });
54996
55056
  };
@@ -55004,7 +55064,7 @@ var require_get_vercel_oidc_token_with_refresh = __commonJS({
55004
55064
  };
55005
55065
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
55006
55066
  var get_vercel_oidc_token_with_refresh_exports = {};
55007
- __export(get_vercel_oidc_token_with_refresh_exports, {
55067
+ __export2(get_vercel_oidc_token_with_refresh_exports, {
55008
55068
  getVercelOidcToken: () => getVercelOidcToken2
55009
55069
  });
55010
55070
  module2.exports = __toCommonJS(get_vercel_oidc_token_with_refresh_exports);
@@ -59478,7 +59538,7 @@ var require_verify_vercel_oidc_token = __commonJS({
59478
59538
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
59479
59539
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
59480
59540
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
59481
- var __export = (target, all) => {
59541
+ var __export2 = (target, all) => {
59482
59542
  for (var name in all)
59483
59543
  __defProp2(target, name, { get: all[name], enumerable: true });
59484
59544
  };
@@ -59492,7 +59552,7 @@ var require_verify_vercel_oidc_token = __commonJS({
59492
59552
  };
59493
59553
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
59494
59554
  var verify_vercel_oidc_token_exports = {};
59495
- __export(verify_vercel_oidc_token_exports, {
59555
+ __export2(verify_vercel_oidc_token_exports, {
59496
59556
  verifyVercelOidcToken: () => verifyVercelOidcToken2
59497
59557
  });
59498
59558
  module2.exports = __toCommonJS(verify_vercel_oidc_token_exports);
@@ -59601,7 +59661,7 @@ var require_dist4 = __commonJS({
59601
59661
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
59602
59662
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
59603
59663
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
59604
- var __export = (target, all) => {
59664
+ var __export2 = (target, all) => {
59605
59665
  for (var name in all)
59606
59666
  __defProp2(target, name, { get: all[name], enumerable: true });
59607
59667
  };
@@ -59615,7 +59675,7 @@ var require_dist4 = __commonJS({
59615
59675
  };
59616
59676
  var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
59617
59677
  var src_exports = {};
59618
- __export(src_exports, {
59678
+ __export2(src_exports, {
59619
59679
  AccessTokenMissingError: () => import_auth_errors.AccessTokenMissingError,
59620
59680
  RefreshAccessTokenFailedError: () => import_auth_errors.RefreshAccessTokenFailedError,
59621
59681
  exchangeVercelOidcToken: () => import_exchange_vercel_oidc_token.exchangeVercelOidcToken,
@@ -59869,7 +59929,7 @@ var require_lib8 = __commonJS({
59869
59929
  "../../node_modules/async-retry/lib/index.js"(exports2, module2) {
59870
59930
  var retrier = require_retry2();
59871
59931
  function retry(fn, opts) {
59872
- function run(resolve, reject) {
59932
+ function run2(resolve, reject) {
59873
59933
  var options = opts || {};
59874
59934
  var op;
59875
59935
  if (!("randomize" in options)) {
@@ -59904,7 +59964,7 @@ var require_lib8 = __commonJS({
59904
59964
  }
59905
59965
  op.attempt(runAttempt);
59906
59966
  }
59907
- return new Promise(run);
59967
+ return new Promise(run2);
59908
59968
  }
59909
59969
  module2.exports = retry;
59910
59970
  }
@@ -60712,14 +60772,14 @@ var require_util2 = __commonJS({
60712
60772
  }
60713
60773
  const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
60714
60774
  let origin = url.origin != null ? url.origin : `${url.protocol || ""}//${url.hostname || ""}:${port}`;
60715
- let path3 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
60775
+ let path4 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
60716
60776
  if (origin[origin.length - 1] === "/") {
60717
60777
  origin = origin.slice(0, origin.length - 1);
60718
60778
  }
60719
- if (path3 && path3[0] !== "/") {
60720
- path3 = `/${path3}`;
60779
+ if (path4 && path4[0] !== "/") {
60780
+ path4 = `/${path4}`;
60721
60781
  }
60722
- return new URL(`${origin}${path3}`);
60782
+ return new URL(`${origin}${path4}`);
60723
60783
  }
60724
60784
  if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
60725
60785
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -61170,39 +61230,39 @@ var require_diagnostics = __commonJS({
61170
61230
  });
61171
61231
  diagnosticsChannel.channel("undici:client:sendHeaders").subscribe((evt) => {
61172
61232
  const {
61173
- request: { method, path: path3, origin }
61233
+ request: { method, path: path4, origin }
61174
61234
  } = evt;
61175
- debuglog("sending request to %s %s/%s", method, origin, path3);
61235
+ debuglog("sending request to %s %s/%s", method, origin, path4);
61176
61236
  });
61177
61237
  diagnosticsChannel.channel("undici:request:headers").subscribe((evt) => {
61178
61238
  const {
61179
- request: { method, path: path3, origin },
61239
+ request: { method, path: path4, origin },
61180
61240
  response: { statusCode }
61181
61241
  } = evt;
61182
61242
  debuglog(
61183
61243
  "received response to %s %s/%s - HTTP %d",
61184
61244
  method,
61185
61245
  origin,
61186
- path3,
61246
+ path4,
61187
61247
  statusCode
61188
61248
  );
61189
61249
  });
61190
61250
  diagnosticsChannel.channel("undici:request:trailers").subscribe((evt) => {
61191
61251
  const {
61192
- request: { method, path: path3, origin }
61252
+ request: { method, path: path4, origin }
61193
61253
  } = evt;
61194
- debuglog("trailers received from %s %s/%s", method, origin, path3);
61254
+ debuglog("trailers received from %s %s/%s", method, origin, path4);
61195
61255
  });
61196
61256
  diagnosticsChannel.channel("undici:request:error").subscribe((evt) => {
61197
61257
  const {
61198
- request: { method, path: path3, origin },
61258
+ request: { method, path: path4, origin },
61199
61259
  error
61200
61260
  } = evt;
61201
61261
  debuglog(
61202
61262
  "request to %s %s/%s errored - %s",
61203
61263
  method,
61204
61264
  origin,
61205
- path3,
61265
+ path4,
61206
61266
  error.message
61207
61267
  );
61208
61268
  });
@@ -61251,9 +61311,9 @@ var require_diagnostics = __commonJS({
61251
61311
  });
61252
61312
  diagnosticsChannel.channel("undici:client:sendHeaders").subscribe((evt) => {
61253
61313
  const {
61254
- request: { method, path: path3, origin }
61314
+ request: { method, path: path4, origin }
61255
61315
  } = evt;
61256
- debuglog("sending request to %s %s/%s", method, origin, path3);
61316
+ debuglog("sending request to %s %s/%s", method, origin, path4);
61257
61317
  });
61258
61318
  }
61259
61319
  diagnosticsChannel.channel("undici:websocket:open").subscribe((evt) => {
@@ -61316,7 +61376,7 @@ var require_request = __commonJS({
61316
61376
  var kHandler = Symbol("handler");
61317
61377
  var Request2 = class {
61318
61378
  constructor(origin, {
61319
- path: path3,
61379
+ path: path4,
61320
61380
  method,
61321
61381
  body,
61322
61382
  headers,
@@ -61331,11 +61391,11 @@ var require_request = __commonJS({
61331
61391
  expectContinue,
61332
61392
  servername
61333
61393
  }, handler) {
61334
- if (typeof path3 !== "string") {
61394
+ if (typeof path4 !== "string") {
61335
61395
  throw new InvalidArgumentError("path must be a string");
61336
- } else if (path3[0] !== "/" && !(path3.startsWith("http://") || path3.startsWith("https://")) && method !== "CONNECT") {
61396
+ } else if (path4[0] !== "/" && !(path4.startsWith("http://") || path4.startsWith("https://")) && method !== "CONNECT") {
61337
61397
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
61338
- } else if (invalidPathRegex.test(path3)) {
61398
+ } else if (invalidPathRegex.test(path4)) {
61339
61399
  throw new InvalidArgumentError("invalid request path");
61340
61400
  }
61341
61401
  if (typeof method !== "string") {
@@ -61401,7 +61461,7 @@ var require_request = __commonJS({
61401
61461
  this.completed = false;
61402
61462
  this.aborted = false;
61403
61463
  this.upgrade = upgrade || null;
61404
- this.path = query ? buildURL(path3, query) : path3;
61464
+ this.path = query ? buildURL(path4, query) : path4;
61405
61465
  this.origin = origin;
61406
61466
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
61407
61467
  this.blocking = blocking == null ? false : blocking;
@@ -66023,7 +66083,7 @@ var require_client_h1 = __commonJS({
66023
66083
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
66024
66084
  }
66025
66085
  function writeH1(client, request) {
66026
- const { method, path: path3, host, upgrade, blocking, reset } = request;
66086
+ const { method, path: path4, host, upgrade, blocking, reset } = request;
66027
66087
  let { body, headers, contentLength } = request;
66028
66088
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
66029
66089
  if (util.isFormDataLike(body)) {
@@ -66090,7 +66150,7 @@ var require_client_h1 = __commonJS({
66090
66150
  if (blocking) {
66091
66151
  socket[kBlocking] = true;
66092
66152
  }
66093
- let header = `${method} ${path3} HTTP/1.1\r
66153
+ let header = `${method} ${path4} HTTP/1.1\r
66094
66154
  `;
66095
66155
  if (typeof host === "string") {
66096
66156
  header += `host: ${host}\r
@@ -66616,7 +66676,7 @@ var require_client_h2 = __commonJS({
66616
66676
  }
66617
66677
  function writeH2(client, request) {
66618
66678
  const session = client[kHTTP2Session];
66619
- const { method, path: path3, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
66679
+ const { method, path: path4, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
66620
66680
  let { body } = request;
66621
66681
  if (upgrade) {
66622
66682
  util.errorRequest(client, request, new Error("Upgrade not supported for H2"));
@@ -66683,7 +66743,7 @@ var require_client_h2 = __commonJS({
66683
66743
  });
66684
66744
  return true;
66685
66745
  }
66686
- headers[HTTP2_HEADER_PATH] = path3;
66746
+ headers[HTTP2_HEADER_PATH] = path4;
66687
66747
  headers[HTTP2_HEADER_SCHEME] = "https";
66688
66748
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
66689
66749
  if (body && typeof body.read === "function") {
@@ -67036,9 +67096,9 @@ var require_redirect_handler = __commonJS({
67036
67096
  return this.handler.onHeaders(statusCode, headers, resume, statusText);
67037
67097
  }
67038
67098
  const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
67039
- const path3 = search ? `${pathname}${search}` : pathname;
67099
+ const path4 = search ? `${pathname}${search}` : pathname;
67040
67100
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
67041
- this.opts.path = path3;
67101
+ this.opts.path = path4;
67042
67102
  this.opts.origin = origin;
67043
67103
  this.opts.maxRedirections = 0;
67044
67104
  this.opts.query = null;
@@ -68273,10 +68333,10 @@ var require_proxy_agent = __commonJS({
68273
68333
  };
68274
68334
  const {
68275
68335
  origin,
68276
- path: path3 = "/",
68336
+ path: path4 = "/",
68277
68337
  headers = {}
68278
68338
  } = opts;
68279
- opts.path = origin + path3;
68339
+ opts.path = origin + path4;
68280
68340
  if (!("host" in headers) && !("Host" in headers)) {
68281
68341
  const { host } = new URL2(origin);
68282
68342
  headers.host = host;
@@ -70197,20 +70257,20 @@ var require_mock_utils = __commonJS({
70197
70257
  }
70198
70258
  return true;
70199
70259
  }
70200
- function safeUrl(path3) {
70201
- if (typeof path3 !== "string") {
70202
- return path3;
70260
+ function safeUrl(path4) {
70261
+ if (typeof path4 !== "string") {
70262
+ return path4;
70203
70263
  }
70204
- const pathSegments = path3.split("?");
70264
+ const pathSegments = path4.split("?");
70205
70265
  if (pathSegments.length !== 2) {
70206
- return path3;
70266
+ return path4;
70207
70267
  }
70208
70268
  const qp = new URLSearchParams(pathSegments.pop());
70209
70269
  qp.sort();
70210
70270
  return [...pathSegments, qp.toString()].join("?");
70211
70271
  }
70212
- function matchKey(mockDispatch2, { path: path3, method, body, headers }) {
70213
- const pathMatch = matchValue(mockDispatch2.path, path3);
70272
+ function matchKey(mockDispatch2, { path: path4, method, body, headers }) {
70273
+ const pathMatch = matchValue(mockDispatch2.path, path4);
70214
70274
  const methodMatch = matchValue(mockDispatch2.method, method);
70215
70275
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
70216
70276
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -70232,7 +70292,7 @@ var require_mock_utils = __commonJS({
70232
70292
  function getMockDispatch(mockDispatches, key) {
70233
70293
  const basePath = key.query ? buildURL(key.path, key.query) : key.path;
70234
70294
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
70235
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path3 }) => matchValue(safeUrl(path3), resolvedPath));
70295
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path4 }) => matchValue(safeUrl(path4), resolvedPath));
70236
70296
  if (matchedMockDispatches.length === 0) {
70237
70297
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
70238
70298
  }
@@ -70270,9 +70330,9 @@ var require_mock_utils = __commonJS({
70270
70330
  }
70271
70331
  }
70272
70332
  function buildKey(opts) {
70273
- const { path: path3, method, body, headers, query } = opts;
70333
+ const { path: path4, method, body, headers, query } = opts;
70274
70334
  return {
70275
- path: path3,
70335
+ path: path4,
70276
70336
  method,
70277
70337
  body,
70278
70338
  headers,
@@ -70735,10 +70795,10 @@ var require_pending_interceptors_formatter = __commonJS({
70735
70795
  }
70736
70796
  format(pendingInterceptors) {
70737
70797
  const withPrettyHeaders = pendingInterceptors.map(
70738
- ({ method, path: path3, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
70798
+ ({ method, path: path4, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
70739
70799
  Method: method,
70740
70800
  Origin: origin,
70741
- Path: path3,
70801
+ Path: path4,
70742
70802
  "Status code": statusCode,
70743
70803
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
70744
70804
  Invocations: timesInvoked,
@@ -75619,9 +75679,9 @@ var require_util7 = __commonJS({
75619
75679
  }
75620
75680
  }
75621
75681
  }
75622
- function validateCookiePath(path3) {
75623
- for (let i = 0; i < path3.length; ++i) {
75624
- const code = path3.charCodeAt(i);
75682
+ function validateCookiePath(path4) {
75683
+ for (let i = 0; i < path4.length; ++i) {
75684
+ const code = path4.charCodeAt(i);
75625
75685
  if (code < 32 || // exclude CTLs (0-31)
75626
75686
  code === 127 || // DEL
75627
75687
  code === 59) {
@@ -78314,11 +78374,11 @@ var require_undici = __commonJS({
78314
78374
  if (typeof opts.path !== "string") {
78315
78375
  throw new InvalidArgumentError("invalid opts.path");
78316
78376
  }
78317
- let path3 = opts.path;
78377
+ let path4 = opts.path;
78318
78378
  if (!opts.path.startsWith("/")) {
78319
- path3 = `/${path3}`;
78379
+ path4 = `/${path4}`;
78320
78380
  }
78321
- url = new URL(util.parseOrigin(url).origin + path3);
78381
+ url = new URL(util.parseOrigin(url).origin + path4);
78322
78382
  } else {
78323
78383
  if (!opts) {
78324
78384
  opts = typeof url === "object" ? url : {};
@@ -80902,8 +80962,8 @@ var require_builder = __commonJS({
80902
80962
  var db_1 = require_db();
80903
80963
  var llm_1 = require_llm();
80904
80964
  var qa_1 = require_qa();
80905
- var fs3 = __importStar(require("fs"));
80906
- var path3 = __importStar(require("path"));
80965
+ var fs4 = __importStar(require("fs"));
80966
+ var path4 = __importStar(require("path"));
80907
80967
  var models_1 = require_models();
80908
80968
  var cms_template_1 = require_cms_template();
80909
80969
  var vercel_domains_1 = require_vercel_domains();
@@ -81027,44 +81087,44 @@ var require_builder = __commonJS({
81027
81087
  </div>`;
81028
81088
  }
81029
81089
  function snapshotDir(leadId) {
81030
- return path3.join((0, runtime_1.userDataDir)(), "data", "sites", String(leadId));
81090
+ return path4.join((0, runtime_1.userDataDir)(), "data", "sites", String(leadId));
81031
81091
  }
81032
81092
  function saveSnapshot(leadId, files) {
81033
81093
  const dir = snapshotDir(leadId);
81034
81094
  try {
81035
- fs3.mkdirSync(dir, { recursive: true });
81095
+ fs4.mkdirSync(dir, { recursive: true });
81036
81096
  for (const f of files) {
81037
- const target = path3.join(dir, f.file);
81038
- fs3.mkdirSync(path3.dirname(target), { recursive: true });
81097
+ const target = path4.join(dir, f.file);
81098
+ fs4.mkdirSync(path4.dirname(target), { recursive: true });
81039
81099
  if (f.encoding === "base64") {
81040
- fs3.writeFileSync(target, Buffer.from(f.data, "base64"));
81100
+ fs4.writeFileSync(target, Buffer.from(f.data, "base64"));
81041
81101
  } else {
81042
- fs3.writeFileSync(target, f.data, "utf-8");
81102
+ fs4.writeFileSync(target, f.data, "utf-8");
81043
81103
  }
81044
81104
  }
81045
- fs3.writeFileSync(path3.join(dir, ".snapshot.json"), JSON.stringify({ savedAt: Date.now() }));
81105
+ fs4.writeFileSync(path4.join(dir, ".snapshot.json"), JSON.stringify({ savedAt: Date.now() }));
81046
81106
  } catch {
81047
81107
  }
81048
81108
  }
81049
81109
  function loadSnapshot(leadId) {
81050
81110
  const dir = snapshotDir(leadId);
81051
- if (!fs3.existsSync(dir))
81111
+ if (!fs4.existsSync(dir))
81052
81112
  return null;
81053
81113
  const out = [];
81054
81114
  function walk(rel) {
81055
- const abs = path3.join(dir, rel);
81056
- const stat = fs3.statSync(abs);
81115
+ const abs = path4.join(dir, rel);
81116
+ const stat = fs4.statSync(abs);
81057
81117
  if (stat.isDirectory()) {
81058
- for (const child of fs3.readdirSync(abs)) {
81118
+ for (const child of fs4.readdirSync(abs)) {
81059
81119
  if (child.startsWith("."))
81060
81120
  continue;
81061
81121
  walk(rel ? `${rel}/${child}` : child);
81062
81122
  }
81063
81123
  } else if (stat.isFile()) {
81064
81124
  if (/\.(jpg|jpeg|png|gif|webp|avif)$/i.test(rel)) {
81065
- out.push({ file: rel, data: fs3.readFileSync(abs).toString("base64"), encoding: "base64" });
81125
+ out.push({ file: rel, data: fs4.readFileSync(abs).toString("base64"), encoding: "base64" });
81066
81126
  } else {
81067
- out.push({ file: rel, data: fs3.readFileSync(abs, "utf-8"), encoding: "utf-8" });
81127
+ out.push({ file: rel, data: fs4.readFileSync(abs, "utf-8"), encoding: "utf-8" });
81068
81128
  }
81069
81129
  }
81070
81130
  }
@@ -82790,6 +82850,389 @@ var require_booking_detect = __commonJS({
82790
82850
  }
82791
82851
  });
82792
82852
 
82853
+ // ../desktop/dist/main/lib/site-audit.js
82854
+ var require_site_audit = __commonJS({
82855
+ "../desktop/dist/main/lib/site-audit.js"(exports2) {
82856
+ "use strict";
82857
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
82858
+ if (k2 === void 0) k2 = k;
82859
+ var desc = Object.getOwnPropertyDescriptor(m, k);
82860
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
82861
+ desc = { enumerable: true, get: function() {
82862
+ return m[k];
82863
+ } };
82864
+ }
82865
+ Object.defineProperty(o, k2, desc);
82866
+ }) : (function(o, m, k, k2) {
82867
+ if (k2 === void 0) k2 = k;
82868
+ o[k2] = m[k];
82869
+ }));
82870
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
82871
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
82872
+ }) : function(o, v) {
82873
+ o["default"] = v;
82874
+ });
82875
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
82876
+ var ownKeys = function(o) {
82877
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
82878
+ var ar = [];
82879
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
82880
+ return ar;
82881
+ };
82882
+ return ownKeys(o);
82883
+ };
82884
+ return function(mod) {
82885
+ if (mod && mod.__esModule) return mod;
82886
+ var result = {};
82887
+ if (mod != null) {
82888
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
82889
+ }
82890
+ __setModuleDefault(result, mod);
82891
+ return result;
82892
+ };
82893
+ })();
82894
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
82895
+ return mod && mod.__esModule ? mod : { "default": mod };
82896
+ };
82897
+ Object.defineProperty(exports2, "__esModule", { value: true });
82898
+ exports2.auditSite = auditSite;
82899
+ exports2.isRebuildProspect = isRebuildProspect;
82900
+ exports2.headlineIssues = headlineIssues;
82901
+ var axios_1 = __importDefault(require_axios());
82902
+ var tls = __importStar(require("tls"));
82903
+ var url_1 = require("url");
82904
+ var WEIGHT = {
82905
+ critical: 30,
82906
+ major: 15,
82907
+ minor: 6
82908
+ };
82909
+ async function checkTls(hostname) {
82910
+ return new Promise((resolve) => {
82911
+ let settled = false;
82912
+ const done = (r) => {
82913
+ if (settled)
82914
+ return;
82915
+ settled = true;
82916
+ socket.destroy();
82917
+ resolve(r);
82918
+ };
82919
+ const socket = tls.connect({ host: hostname, port: 443, servername: hostname, timeout: 12e3 }, () => {
82920
+ if (!socket.authorized) {
82921
+ done({
82922
+ ok: false,
82923
+ issue: {
82924
+ code: "tls_invalid",
82925
+ label: "The security certificate isn\u2019t valid \u2014 browsers warn visitors before they can see the site",
82926
+ severity: "critical",
82927
+ detail: socket.authorizationError ? String(socket.authorizationError) : void 0
82928
+ }
82929
+ });
82930
+ return;
82931
+ }
82932
+ const cert = socket.getPeerCertificate();
82933
+ if (!cert || !cert.valid_to) {
82934
+ done({ ok: true });
82935
+ return;
82936
+ }
82937
+ const days = Math.round((new Date(cert.valid_to).getTime() - Date.now()) / 864e5);
82938
+ if (days < 0) {
82939
+ done({
82940
+ ok: false,
82941
+ daysToExpiry: days,
82942
+ issue: {
82943
+ code: "tls_expired",
82944
+ label: "The security certificate has expired \u2014 visitors see a full-page warning",
82945
+ severity: "critical",
82946
+ detail: `expired ${Math.abs(days)} days ago`
82947
+ }
82948
+ });
82949
+ return;
82950
+ }
82951
+ if (days <= 14) {
82952
+ done({
82953
+ ok: true,
82954
+ daysToExpiry: days,
82955
+ issue: {
82956
+ code: "tls_expiring",
82957
+ label: "The security certificate expires within two weeks",
82958
+ severity: "major",
82959
+ detail: `${days} days left`
82960
+ }
82961
+ });
82962
+ return;
82963
+ }
82964
+ done({ ok: true, daysToExpiry: days });
82965
+ });
82966
+ socket.on("error", (err) => {
82967
+ const msg = err.message || "";
82968
+ const code = err.code ?? "";
82969
+ let issue;
82970
+ if (/certificate has expired|CERT_HAS_EXPIRED/i.test(msg + code)) {
82971
+ issue = {
82972
+ code: "tls_expired",
82973
+ label: "The security certificate has expired \u2014 visitors see a full-page browser warning before they reach the site",
82974
+ severity: "critical",
82975
+ detail: msg
82976
+ };
82977
+ } else if (/self.signed|SELF_SIGNED/i.test(msg + code)) {
82978
+ issue = {
82979
+ code: "tls_self_signed",
82980
+ label: "The security certificate isn\u2019t from a recognised authority, so browsers warn visitors away",
82981
+ severity: "critical",
82982
+ detail: msg
82983
+ };
82984
+ } else if (/altnames|hostname\/ip does not match|ERR_TLS_CERT_ALTNAME/i.test(msg + code)) {
82985
+ issue = {
82986
+ code: "tls_wrong_host",
82987
+ label: "The security certificate was issued for a different domain, so browsers reject it",
82988
+ severity: "critical",
82989
+ detail: msg
82990
+ };
82991
+ } else if (/ECONNREFUSED|ENOTFOUND|EHOSTUNREACH|ECONNRESET/i.test(msg + code)) {
82992
+ issue = {
82993
+ code: "tls_unavailable",
82994
+ label: "The site has no working HTTPS \u2014 Chrome labels it \u201CNot secure\u201D to every visitor",
82995
+ severity: "critical",
82996
+ detail: msg
82997
+ };
82998
+ } else {
82999
+ issue = {
83000
+ code: "tls_error",
83001
+ label: "Secure connections to the site fail",
83002
+ severity: "major",
83003
+ detail: msg
83004
+ };
83005
+ }
83006
+ done({ ok: false, issue });
83007
+ });
83008
+ socket.on("timeout", () => {
83009
+ done({
83010
+ ok: false,
83011
+ issue: {
83012
+ code: "tls_timeout",
83013
+ label: "Secure connections to the site time out",
83014
+ severity: "major"
83015
+ }
83016
+ });
83017
+ });
83018
+ });
83019
+ }
83020
+ function markupIssues(html, finalUrl) {
83021
+ const issues = [];
83022
+ const head = html.slice(0, 6e4);
83023
+ if (!/<meta[^>]+name=["']?viewport/i.test(head)) {
83024
+ issues.push({
83025
+ code: "not_mobile",
83026
+ label: "Not built for phones \u2014 the page loads zoomed out and has to be pinched to read",
83027
+ severity: "critical"
83028
+ });
83029
+ }
83030
+ const tableCount = (html.match(/<table/gi) ?? []).length;
83031
+ if (tableCount >= 3 || /<table[^>]*>[\s\S]{0,400}<table/i.test(html)) {
83032
+ issues.push({
83033
+ code: "table_layout",
83034
+ label: "Built with table layouts, a technique abandoned around 2010",
83035
+ severity: "major",
83036
+ detail: `${tableCount} tables`
83037
+ });
83038
+ }
83039
+ if (/<(font|center|marquee|blink)\b/i.test(html)) {
83040
+ const tag = html.match(/<(font|center|marquee|blink)\b/i)?.[1]?.toLowerCase();
83041
+ issues.push({
83042
+ code: "obsolete_tags",
83043
+ label: `Uses HTML tags (<${tag}>) that have been removed from the web standard`,
83044
+ severity: "major"
83045
+ });
83046
+ }
83047
+ if (/\.swf\b|application\/x-shockwave-flash|<embed[^>]+flash/i.test(html)) {
83048
+ issues.push({
83049
+ code: "flash",
83050
+ label: "Contains Flash content, which no browser has supported since 2020 \u2014 that part of the site is invisible",
83051
+ severity: "critical"
83052
+ });
83053
+ }
83054
+ const jq = html.match(/jquery[.-]?(\d+)\.(\d+)(?:\.(\d+))?(?:\.min)?\.js/i);
83055
+ if (jq) {
83056
+ const major = Number(jq[1]);
83057
+ if (major < 3) {
83058
+ issues.push({
83059
+ code: "old_jquery",
83060
+ label: "Runs a jQuery version with publicly known security vulnerabilities",
83061
+ severity: "major",
83062
+ detail: `jQuery ${jq[1]}.${jq[2]}${jq[3] ? "." + jq[3] : ""}`
83063
+ });
83064
+ }
83065
+ }
83066
+ const year = (/* @__PURE__ */ new Date()).getFullYear();
83067
+ const years = [...html.matchAll(/(?:©|&copy;|copyright)\s*(?:\d{4}\s*[-–]\s*)?(\d{4})/gi)].map((m) => Number(m[1])).filter((y) => y >= 1995 && y <= year);
83068
+ if (years.length) {
83069
+ const newest = Math.max(...years);
83070
+ const age = year - newest;
83071
+ if (age >= 2) {
83072
+ issues.push({
83073
+ code: "stale_copyright",
83074
+ label: `The footer still says ${newest} \u2014 visitors read that as \u201Cpossibly closed down\u201D`,
83075
+ severity: age >= 5 ? "major" : "minor",
83076
+ detail: `${age} years out of date`
83077
+ });
83078
+ }
83079
+ }
83080
+ if (/under construction|coming soon|website is being (?:built|updated)/i.test(head)) {
83081
+ issues.push({
83082
+ code: "under_construction",
83083
+ label: "Still shows an \u201Cunder construction\u201D placeholder",
83084
+ severity: "critical"
83085
+ });
83086
+ }
83087
+ if (/this domain (?:is|may be) for sale|parked (?:free )?(?:at|by)|godaddy\.com\/domainfind|default web page/i.test(head)) {
83088
+ issues.push({
83089
+ code: "parked",
83090
+ label: "The domain shows a parking page rather than a real website",
83091
+ severity: "critical"
83092
+ });
83093
+ }
83094
+ if (finalUrl.startsWith("https://")) {
83095
+ const insecure = html.match(/(?:src|href)=["']http:\/\/(?!localhost)/gi);
83096
+ if (insecure && insecure.length >= 2) {
83097
+ issues.push({
83098
+ code: "mixed_content",
83099
+ label: "Loads images or scripts insecurely, so browsers block them and parts of the page break",
83100
+ severity: "major",
83101
+ detail: `${insecure.length} insecure references`
83102
+ });
83103
+ }
83104
+ }
83105
+ const title = html.match(/<title[^>]*>([\s\S]{0,200}?)<\/title>/i)?.[1]?.trim();
83106
+ if (!title) {
83107
+ issues.push({
83108
+ code: "no_title",
83109
+ label: "The page has no title, so search results show a bare web address",
83110
+ severity: "major"
83111
+ });
83112
+ } else if (/^(home|untitled|new page \d*|document|index|website)$/i.test(title)) {
83113
+ issues.push({
83114
+ code: "default_title",
83115
+ label: `The page title is still the template default (\u201C${title}\u201D), which is what Google shows`,
83116
+ severity: "major"
83117
+ });
83118
+ }
83119
+ if (!/<meta[^>]+name=["']?description/i.test(head)) {
83120
+ issues.push({
83121
+ code: "no_description",
83122
+ label: "No search description, so Google invents one from whatever text it finds",
83123
+ severity: "minor"
83124
+ });
83125
+ }
83126
+ if (/<frameset|<frame\b/i.test(html)) {
83127
+ issues.push({
83128
+ code: "frames",
83129
+ label: "Built with frames, which search engines cannot index properly",
83130
+ severity: "critical"
83131
+ });
83132
+ }
83133
+ return issues;
83134
+ }
83135
+ async function auditSite(websiteUrl) {
83136
+ const raw = (websiteUrl ?? "").trim();
83137
+ if (!raw)
83138
+ return { url: raw, reachable: false, score: 0, issues: [], error: "No URL" };
83139
+ const withScheme = /^https?:\/\//i.test(raw) ? raw : `https://${raw}`;
83140
+ let hostname;
83141
+ try {
83142
+ hostname = new url_1.URL(withScheme).hostname;
83143
+ } catch {
83144
+ return { url: raw, reachable: false, score: 0, issues: [], error: "Unparseable URL" };
83145
+ }
83146
+ const issues = [];
83147
+ const listedAsHttp = /^http:\/\//i.test(raw);
83148
+ const [tlsResult, page] = await Promise.all([
83149
+ checkTls(hostname).catch(() => ({ ok: true })),
83150
+ fetchPage(withScheme)
83151
+ ]);
83152
+ if (tlsResult.issue)
83153
+ issues.push(tlsResult.issue);
83154
+ if (listedAsHttp && !tlsResult.issue) {
83155
+ issues.push({
83156
+ code: "listed_insecure",
83157
+ label: "The address the business gives out starts with http:// \u2014 visitors following it get a \u201CNot secure\u201D warning",
83158
+ severity: "major"
83159
+ });
83160
+ }
83161
+ if (!page.ok) {
83162
+ return {
83163
+ url: raw,
83164
+ reachable: false,
83165
+ score: scoreOf(issues),
83166
+ issues,
83167
+ error: page.error
83168
+ };
83169
+ }
83170
+ if (page.finalUrl.startsWith("http://") && !tlsResult.issue) {
83171
+ issues.push({
83172
+ code: "no_https_redirect",
83173
+ label: "The site falls back to an unencrypted connection instead of staying secure",
83174
+ severity: "major"
83175
+ });
83176
+ }
83177
+ issues.push(...markupIssues(page.html, page.finalUrl));
83178
+ const htmlKb = Math.round(page.html.length / 1024);
83179
+ if (htmlKb > 600) {
83180
+ issues.push({
83181
+ code: "heavy_page",
83182
+ label: "The page is unusually heavy, so it loads slowly on mobile data",
83183
+ severity: "minor",
83184
+ detail: `${htmlKb} KB of HTML`
83185
+ });
83186
+ }
83187
+ return { url: raw, reachable: true, score: scoreOf(issues), issues, htmlKb };
83188
+ }
83189
+ async function fetchPage(url) {
83190
+ try {
83191
+ const res = await axios_1.default.get(url, {
83192
+ timeout: 2e4,
83193
+ maxRedirects: 5,
83194
+ responseType: "text",
83195
+ // Some hosts serve a stripped page to a bare client.
83196
+ headers: {
83197
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Safari/537.36",
83198
+ Accept: "text/html,application/xhtml+xml"
83199
+ },
83200
+ maxContentLength: 5e6,
83201
+ validateStatus: () => true,
83202
+ // We want to inspect a broken-cert site's markup too; the cert problem is
83203
+ // reported separately by checkTls, so this doesn't hide anything.
83204
+ httpsAgent: void 0
83205
+ });
83206
+ if (res.status >= 400) {
83207
+ return { ok: false, error: `HTTP ${res.status}` };
83208
+ }
83209
+ const html = typeof res.data === "string" ? res.data : String(res.data ?? "");
83210
+ if (!html)
83211
+ return { ok: false, error: "Empty response" };
83212
+ const finalUrl = res.request?.res?.responseUrl || url;
83213
+ return { ok: true, html, finalUrl };
83214
+ } catch (e) {
83215
+ return { ok: false, error: e.message };
83216
+ }
83217
+ }
83218
+ function scoreOf(issues) {
83219
+ const total = issues.reduce((sum, i) => sum + WEIGHT[i.severity], 0);
83220
+ return Math.min(100, total);
83221
+ }
83222
+ function isRebuildProspect(audit) {
83223
+ if (!audit.issues.length)
83224
+ return false;
83225
+ const critical = audit.issues.filter((i) => i.severity === "critical").length;
83226
+ const major = audit.issues.filter((i) => i.severity === "major").length;
83227
+ return critical >= 1 || major >= 2;
83228
+ }
83229
+ function headlineIssues(audit, limit = 3) {
83230
+ const rank = { critical: 0, major: 1, minor: 2 };
83231
+ return [...audit.issues].sort((a, b) => rank[a.severity] - rank[b.severity]).slice(0, limit);
83232
+ }
83233
+ }
83234
+ });
83235
+
82793
83236
  // ../desktop/dist/main/lib/lead-search.js
82794
83237
  var require_lead_search = __commonJS({
82795
83238
  "../desktop/dist/main/lib/lead-search.js"(exports2) {
@@ -82800,11 +83243,14 @@ var require_lead_search = __commonJS({
82800
83243
  var db_1 = require_db();
82801
83244
  var places_1 = require_places();
82802
83245
  var booking_detect_1 = require_booking_detect();
83246
+ var site_audit_1 = require_site_audit();
82803
83247
  async function searchAndSaveLeadsStream(args, onLead) {
82804
83248
  const db2 = (0, db_1.getDb)();
82805
83249
  const cap = args.maxResults ?? 50;
82806
83250
  const seen = /* @__PURE__ */ new Set();
82807
83251
  const bookingMode = args.mode === "no_booking";
83252
+ const auditMode = args.mode === "outdated";
83253
+ const wantsSiteHavers = bookingMode || auditMode;
82808
83254
  let saved = 0;
82809
83255
  let found = 0;
82810
83256
  await (0, places_1.searchPlacesStream)({
@@ -82813,8 +83259,8 @@ var require_lead_search = __commonJS({
82813
83259
  types: args.types,
82814
83260
  center: args.center,
82815
83261
  radiusMeters: args.radiusMeters,
82816
- // Booking mode needs the businesses that DO have a website.
82817
- includeWithWebsite: bookingMode
83262
+ // Booking and audit modes need the businesses that DO have a website.
83263
+ includeWithWebsite: wantsSiteHavers
82818
83264
  }, async (batch) => {
82819
83265
  for (const r of batch) {
82820
83266
  found++;
@@ -82829,7 +83275,19 @@ var require_lead_search = __commonJS({
82829
83275
  let leadKind = "no_website";
82830
83276
  let bookingStatus = null;
82831
83277
  let bookingPlatform = null;
82832
- if (bookingMode) {
83278
+ let siteScore = null;
83279
+ let siteIssues = null;
83280
+ if (auditMode) {
83281
+ if (!r.website)
83282
+ continue;
83283
+ args.onProgress?.(`Auditing ${r.name}'s website\u2026`);
83284
+ const audit = await (0, site_audit_1.auditSite)(r.website);
83285
+ if (!(0, site_audit_1.isRebuildProspect)(audit))
83286
+ continue;
83287
+ siteScore = audit.score;
83288
+ siteIssues = JSON.stringify(audit.issues);
83289
+ leadKind = "outdated";
83290
+ } else if (bookingMode) {
82833
83291
  if (!r.website)
82834
83292
  continue;
82835
83293
  args.onProgress?.(`Checking ${r.name}'s booking setup\u2026`);
@@ -82843,8 +83301,8 @@ var require_lead_search = __commonJS({
82843
83301
  continue;
82844
83302
  }
82845
83303
  db2.run(`INSERT INTO leads
82846
- (place_id, name, type, city, address, phone, website, rating, review_count, lat, lng, score, price_level, business_status, status, lead_kind, booking_status, booking_platform)
82847
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'discovered', ?, ?, ?)`, [
83304
+ (place_id, name, type, city, address, phone, website, rating, review_count, lat, lng, score, price_level, business_status, status, lead_kind, booking_status, booking_platform, site_score, site_issues, audited_at)
83305
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'discovered', ?, ?, ?, ?, ?, ?)`, [
82848
83306
  r.place_id,
82849
83307
  r.name,
82850
83308
  r.type,
@@ -82861,7 +83319,10 @@ var require_lead_search = __commonJS({
82861
83319
  r.business_status,
82862
83320
  leadKind,
82863
83321
  bookingStatus,
82864
- bookingPlatform
83322
+ bookingPlatform,
83323
+ siteScore,
83324
+ siteIssues,
83325
+ siteScore === null ? null : (/* @__PURE__ */ new Date()).toISOString()
82865
83326
  ]);
82866
83327
  const row = db2.get("SELECT id FROM leads WHERE place_id = ?", [r.place_id]);
82867
83328
  saved++;
@@ -83057,8 +83518,8 @@ var require_invoicing = __commonJS({
83057
83518
  exports2.renderInvoicePdf = renderInvoicePdf;
83058
83519
  exports2.renderLetterPdf = renderLetterPdf2;
83059
83520
  var pdfkit_1 = __importDefault(require("pdfkit"));
83060
- var fs3 = __importStar(require("fs"));
83061
- var path3 = __importStar(require("path"));
83521
+ var fs4 = __importStar(require("fs"));
83522
+ var path4 = __importStar(require("path"));
83062
83523
  var db_1 = require_db();
83063
83524
  var runtime_1 = require_runtime();
83064
83525
  var agency_1 = require_agency();
@@ -83108,8 +83569,8 @@ var require_invoicing = __commonJS({
83108
83569
  return prefix + String(n).padStart(3, "0");
83109
83570
  }
83110
83571
  function invoiceDir() {
83111
- const dir = path3.join((0, runtime_1.userDataDir)(), "data", "invoices");
83112
- fs3.mkdirSync(dir, { recursive: true });
83572
+ const dir = path4.join((0, runtime_1.userDataDir)(), "data", "invoices");
83573
+ fs4.mkdirSync(dir, { recursive: true });
83113
83574
  return dir;
83114
83575
  }
83115
83576
  function addDays(iso, days) {
@@ -83186,7 +83647,7 @@ var require_invoicing = __commonJS({
83186
83647
  const accent = hexOrDefault(p.accent, "#556B45");
83187
83648
  const right = doc.page.width - PAGE_MARGIN;
83188
83649
  let leftBottom = PAGE_MARGIN;
83189
- if (p.logoPath && fs3.existsSync(p.logoPath)) {
83650
+ if (p.logoPath && fs4.existsSync(p.logoPath)) {
83190
83651
  try {
83191
83652
  doc.image(p.logoPath, PAGE_MARGIN, PAGE_MARGIN, { fit: [150, 50] });
83192
83653
  leftBottom = PAGE_MARGIN + 50;
@@ -83226,12 +83687,12 @@ var require_invoicing = __commonJS({
83226
83687
  }
83227
83688
  const accent = hexOrDefault(p.accent, "#556B45");
83228
83689
  const items = JSON.parse(invoice.line_items);
83229
- const file = outPath ?? path3.join(invoiceDir(), `${invoice.number}.pdf`);
83690
+ const file = outPath ?? path4.join(invoiceDir(), `${invoice.number}.pdf`);
83230
83691
  const doc = new pdfkit_1.default({ size: "A4", margin: PAGE_MARGIN, info: {
83231
83692
  Title: `Invoice ${invoice.number}`,
83232
83693
  Author: p.name
83233
83694
  } });
83234
- const stream = fs3.createWriteStream(file);
83695
+ const stream = fs4.createWriteStream(file);
83235
83696
  doc.pipe(stream);
83236
83697
  let y = drawLetterhead(doc, p);
83237
83698
  const right = doc.page.width - PAGE_MARGIN;
@@ -83339,15 +83800,15 @@ var require_invoicing = __commonJS({
83339
83800
  if (!p.name) {
83340
83801
  throw new Error("No agency details set \u2014 run `sproutkit brand` first.");
83341
83802
  }
83342
- const dir = path3.join((0, runtime_1.userDataDir)(), "data", "letters");
83343
- fs3.mkdirSync(dir, { recursive: true });
83803
+ const dir = path4.join((0, runtime_1.userDataDir)(), "data", "letters");
83804
+ fs4.mkdirSync(dir, { recursive: true });
83344
83805
  const safe = args.to.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
83345
- const file = args.outPath ?? path3.join(dir, `letter-${safe}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.pdf`);
83806
+ const file = args.outPath ?? path4.join(dir, `letter-${safe}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.pdf`);
83346
83807
  const doc = new pdfkit_1.default({ size: "A4", margin: PAGE_MARGIN, info: {
83347
83808
  Title: args.subject ?? `Letter to ${args.to}`,
83348
83809
  Author: p.name
83349
83810
  } });
83350
- const stream = fs3.createWriteStream(file);
83811
+ const stream = fs4.createWriteStream(file);
83351
83812
  doc.pipe(stream);
83352
83813
  let y = drawLetterhead(doc, p);
83353
83814
  const width = doc.page.width - PAGE_MARGIN * 2;
@@ -83612,8 +84073,8 @@ var require_cookies2 = __commonJS({
83612
84073
  if (urlparts.hostname !== cookie.domain && (cookie.domain.charAt(0) !== "." || ("." + urlparts.hostname).substr(-cookie.domain.length) !== cookie.domain)) {
83613
84074
  return false;
83614
84075
  }
83615
- const path3 = this.getPath(urlparts.pathname);
83616
- if (path3.substr(0, cookie.path.length) !== cookie.path) {
84076
+ const path4 = this.getPath(urlparts.pathname);
84077
+ if (path4.substr(0, cookie.path.length) !== cookie.path) {
83617
84078
  return false;
83618
84079
  }
83619
84080
  if (cookie.secure && urlparts.protocol !== "https:") {
@@ -83671,16 +84132,16 @@ var require_cookies2 = __commonJS({
83671
84132
  * @returns {String} Normalized path
83672
84133
  */
83673
84134
  getPath(pathname) {
83674
- let path3 = (pathname || "/").split("/");
83675
- path3.pop();
83676
- path3 = path3.join("/").trim();
83677
- if (path3.charAt(0) !== "/") {
83678
- path3 = "/" + path3;
84135
+ let path4 = (pathname || "/").split("/");
84136
+ path4.pop();
84137
+ path4 = path4.join("/").trim();
84138
+ if (path4.charAt(0) !== "/") {
84139
+ path4 = "/" + path4;
83679
84140
  }
83680
- if (path3.substr(-1) !== "/") {
83681
- path3 += "/";
84141
+ if (path4.substr(-1) !== "/") {
84142
+ path4 += "/";
83682
84143
  }
83683
- return path3;
84144
+ return path4;
83684
84145
  }
83685
84146
  };
83686
84147
  module2.exports = Cookies;
@@ -84024,12 +84485,12 @@ var require_shared = __commonJS({
84024
84485
  "use strict";
84025
84486
  var urllib = require("url");
84026
84487
  var util = require("util");
84027
- var fs3 = require("fs");
84488
+ var fs4 = require("fs");
84028
84489
  var nmfetch = require_fetch2();
84029
84490
  var errors = require_errors6();
84030
84491
  var dns = require("dns");
84031
84492
  var net = require("net");
84032
- var os4 = require("os");
84493
+ var os5 = require("os");
84033
84494
  var DNS_TTL = 5 * 60 * 1e3;
84034
84495
  var CACHE_CLEANUP_INTERVAL = 30 * 1e3;
84035
84496
  var MAX_CACHE_SIZE = 1e3;
@@ -84040,7 +84501,7 @@ var require_shared = __commonJS({
84040
84501
  };
84041
84502
  var networkInterfaces;
84042
84503
  try {
84043
- networkInterfaces = os4.networkInterfaces();
84504
+ networkInterfaces = os5.networkInterfaces();
84044
84505
  } catch (_err) {
84045
84506
  }
84046
84507
  module2.exports.networkInterfaces = networkInterfaces;
@@ -84452,7 +84913,7 @@ var require_shared = __commonJS({
84452
84913
  callback(err);
84453
84914
  });
84454
84915
  }
84455
- return resolveStream(fs3.createReadStream(content.path), callback);
84916
+ return resolveStream(fs4.createReadStream(content.path), callback);
84456
84917
  }
84457
84918
  }
84458
84919
  if (typeof data[key].content === "string" && !["utf8", "usascii", "ascii"].includes(encoding)) {
@@ -84565,7 +85026,7 @@ var require_shared = __commonJS({
84565
85026
  var require_mime_types2 = __commonJS({
84566
85027
  "../../node_modules/nodemailer/lib/mime-funcs/mime-types.js"(exports2, module2) {
84567
85028
  "use strict";
84568
- var path3 = require("path");
85029
+ var path4 = require("path");
84569
85030
  var defaultMimeType = "application/octet-stream";
84570
85031
  var defaultExtension = "bin";
84571
85032
  var mimeTypes = /* @__PURE__ */ new Map([
@@ -86632,7 +87093,7 @@ var require_mime_types2 = __commonJS({
86632
87093
  if (!filename) {
86633
87094
  return defaultMimeType;
86634
87095
  }
86635
- const parsed = path3.parse(filename);
87096
+ const parsed = path4.parse(filename);
86636
87097
  const extension = (parsed.ext.substr(1) || parsed.name || "").split("?").shift().trim().toLowerCase();
86637
87098
  const value = extensions.has(extension) ? extensions.get(extension) : defaultMimeType;
86638
87099
  if (Array.isArray(value)) {
@@ -88060,7 +88521,7 @@ var require_mime_node = __commonJS({
88060
88521
  "../../node_modules/nodemailer/lib/mime-node/index.js"(exports2, module2) {
88061
88522
  "use strict";
88062
88523
  var crypto2 = require("crypto");
88063
- var fs3 = require("fs");
88524
+ var fs4 = require("fs");
88064
88525
  var punycode = require_punycode();
88065
88526
  var { PassThrough } = require("stream");
88066
88527
  var shared = require_shared();
@@ -88782,7 +89243,7 @@ var require_mime_node = __commonJS({
88782
89243
  });
88783
89244
  return contentStream;
88784
89245
  }
88785
- return fs3.createReadStream(content.path);
89246
+ return fs4.createReadStream(content.path);
88786
89247
  }
88787
89248
  if (content && typeof content.href === "string") {
88788
89249
  if (this.disableUrlAccess) {
@@ -89848,8 +90309,8 @@ var require_dkim = __commonJS({
89848
90309
  var RelaxedBody = require_relaxed_body();
89849
90310
  var sign = require_sign7();
89850
90311
  var { PassThrough } = require("stream");
89851
- var fs3 = require("fs");
89852
- var path3 = require("path");
90312
+ var fs4 = require("fs");
90313
+ var path4 = require("path");
89853
90314
  var crypto2 = require("crypto");
89854
90315
  var DKIM_ALGO = "sha256";
89855
90316
  var MAX_MESSAGE_SIZE = 2 * 1024 * 1024;
@@ -89863,7 +90324,7 @@ var require_dkim = __commonJS({
89863
90324
  this.chunks = [];
89864
90325
  this.chunklen = 0;
89865
90326
  this.readPos = 0;
89866
- this.cachePath = this.cacheDir ? path3.join(this.cacheDir, "message." + Date.now() + "-" + crypto2.randomBytes(14).toString("hex")) : false;
90327
+ this.cachePath = this.cacheDir ? path4.join(this.cacheDir, "message." + Date.now() + "-" + crypto2.randomBytes(14).toString("hex")) : false;
89867
90328
  this.cache = false;
89868
90329
  this.headers = false;
89869
90330
  this.bodyHash = false;
@@ -89883,10 +90344,10 @@ var require_dkim = __commonJS({
89883
90344
  if (!this.cache || !this.cachePath) {
89884
90345
  return;
89885
90346
  }
89886
- fs3.unlink(this.cachePath, () => false);
90347
+ fs4.unlink(this.cachePath, () => false);
89887
90348
  }
89888
90349
  createReadCache() {
89889
- this.cache = fs3.createReadStream(this.cachePath);
90350
+ this.cache = fs4.createReadStream(this.cachePath);
89890
90351
  this.cache.once("error", (err) => {
89891
90352
  this.cleanup();
89892
90353
  this.output.emit("error", err);
@@ -89942,7 +90403,7 @@ var require_dkim = __commonJS({
89942
90403
  }
89943
90404
  createWriteCache() {
89944
90405
  this.output.usingCache = true;
89945
- this.cache = fs3.createWriteStream(this.cachePath);
90406
+ this.cache = fs4.createWriteStream(this.cachePath);
89946
90407
  this.cache.once("error", (err) => {
89947
90408
  this.cleanup();
89948
90409
  this.relaxedBody.unpipe(this.cache);
@@ -90883,7 +91344,7 @@ var require_smtp_connection = __commonJS({
90883
91344
  var { EventEmitter } = require("events");
90884
91345
  var net = require("net");
90885
91346
  var tls = require("tls");
90886
- var os4 = require("os");
91347
+ var os5 = require("os");
90887
91348
  var crypto2 = require("crypto");
90888
91349
  var DataStream = require_data_stream();
90889
91350
  var { PassThrough } = require("stream");
@@ -92334,7 +92795,7 @@ var require_smtp_connection = __commonJS({
92334
92795
  _getHostname() {
92335
92796
  let defaultHostname;
92336
92797
  try {
92337
- defaultHostname = os4.hostname() || "";
92798
+ defaultHostname = os5.hostname() || "";
92338
92799
  } catch (_err) {
92339
92800
  defaultHostname = "localhost";
92340
92801
  }
@@ -95146,13 +95607,13 @@ var require_email = __commonJS({
95146
95607
  secure: cfg.port === 465,
95147
95608
  auth: { user: cfg.user, pass: cfg.pass }
95148
95609
  });
95149
- await transporter.sendMail({
95610
+ const info = await transporter.sendMail({
95150
95611
  from: cfg.from,
95151
95612
  to: args.to,
95152
95613
  subject: args.subject,
95153
95614
  text: args.text
95154
95615
  });
95155
- return { ok: true };
95616
+ return { ok: true, messageId: info.messageId };
95156
95617
  } catch (e) {
95157
95618
  return { ok: false, error: e.message };
95158
95619
  }
@@ -95251,6 +95712,7 @@ var require_chat_tools = __commonJS({
95251
95712
  "use strict";
95252
95713
  Object.defineProperty(exports2, "__esModule", { value: true });
95253
95714
  exports2.TOOLS = void 0;
95715
+ exports2.normaliseChannel = normaliseChannel;
95254
95716
  exports2.runTool = runTool2;
95255
95717
  var db_1 = require_db();
95256
95718
  var runtime_1 = require_runtime();
@@ -95258,6 +95720,7 @@ var require_chat_tools = __commonJS({
95258
95720
  var lead_search_1 = require_lead_search();
95259
95721
  var places_1 = require_places();
95260
95722
  var booking_detect_1 = require_booking_detect();
95723
+ var site_audit_1 = require_site_audit();
95261
95724
  var followup_1 = require_followup();
95262
95725
  var invoicing_1 = require_invoicing();
95263
95726
  var agency_1 = require_agency();
@@ -95358,7 +95821,7 @@ var require_chat_tools = __commonJS({
95358
95821
  },
95359
95822
  {
95360
95823
  name: "prepare_outreach",
95361
- description: "Prepare outreach for one or more leads that have a website built. For each lead it fills in the user's outreach template (name + demo site URL), moves the lead to the 'contacted' column, and returns a ready-to-open messaging link for the user's chosen channel (Messages on Mac, Phone Link on Windows, or WhatsApp). The user opens each message themselves \u2014 messaging apps can't be truly auto-sent \u2014 so this is the 'send the outreach' step after building sites. Use it for 'message everyone I just built', 'send outreach to the ready column', etc.",
95824
+ description: "Send or prepare outreach for one or more leads that have a website built. Fills in the user's outreach template (name + demo site URL) and moves each lead to the 'contacted' column. Behaviour depends on the channel: on 'email' (the default) it ACTUALLY SENDS via the user's SMTP \u2014 report exactly who was emailed; on 'whatsapp'/'sms' it returns a ready-to-open messaging link the user opens themselves, because messaging apps can't be auto-sent. Leads with no address on file get one looked up automatically where possible; any lead we can't reach at all is reported as such rather than skipped silently. Use it for 'message everyone I just built', 'send outreach to the ready column', etc.",
95362
95825
  input_schema: {
95363
95826
  type: "object",
95364
95827
  properties: {
@@ -95369,8 +95832,8 @@ var require_chat_tools = __commonJS({
95369
95832
  },
95370
95833
  channel: {
95371
95834
  type: "string",
95372
- enum: ["whatsapp", "sms"],
95373
- description: "Messaging channel for leads that have a phone: 'whatsapp' opens WhatsApp, 'sms' opens the device's Messages app (Messages on macOS, Phone Link on Windows). Omit to use the saved preference."
95835
+ enum: ["email", "whatsapp", "sms"],
95836
+ description: "Preferred channel. 'email' (default) sends via the user's SMTP \u2014 the only channel that works unattended on every platform \u2014 falling back to a WhatsApp link for leads with no email address. 'whatsapp' opens WhatsApp and 'sms' opens the device's Messages app (Messages on macOS, Phone Link on Windows); both fall back to email when a lead has no phone. Omit to use the saved preference."
95374
95837
  }
95375
95838
  }
95376
95839
  },
@@ -95449,6 +95912,44 @@ var require_chat_tools = __commonJS({
95449
95912
  required: ["id"]
95450
95913
  }
95451
95914
  },
95915
+ {
95916
+ name: "audit_sites",
95917
+ description: "AUDIT existing websites in an area and save the bad ones as rebuild prospects. Unlike find_leads (which looks for businesses with NO website), this finds businesses that DO have a website and checks it for real, nameable faults: expired or missing HTTPS certificates, no mobile support, table layouts, Flash, obsolete jQuery, parked or under-construction pages, stale copyright years. Each prospect is scored (higher = worse) and its specific problems are stored so outreach can cite them. Use this when the user wants to find outdated, insecure or badly built websites to pitch a rebuild against. Costs the same Places lookup as find_leads plus one fetch per site.",
95918
+ input_schema: {
95919
+ type: "object",
95920
+ properties: {
95921
+ location: {
95922
+ type: "string",
95923
+ description: "Town, postcode, area or address to search, e.g. 'Leeds LS1' or 'Shoreditch, London'."
95924
+ },
95925
+ categories: {
95926
+ type: "array",
95927
+ items: { type: "string" },
95928
+ description: "Business types to search, e.g. ['barber shop','cafe','plumber']."
95929
+ },
95930
+ radiusMeters: {
95931
+ type: "number",
95932
+ description: "Search radius in metres. Defaults to 10000."
95933
+ },
95934
+ limit: {
95935
+ type: "number",
95936
+ description: "Maximum prospects to save. Defaults to 25."
95937
+ }
95938
+ },
95939
+ required: ["location", "categories"]
95940
+ }
95941
+ },
95942
+ {
95943
+ name: "audit_one_site",
95944
+ description: "Audit a SINGLE website by URL and report its faults, without saving anything. Use this when the user names a specific site and asks what's wrong with it, or to check a business's site before pitching them.",
95945
+ input_schema: {
95946
+ type: "object",
95947
+ properties: {
95948
+ url: { type: "string", description: "The website to audit, e.g. 'thebarbers.co.uk'." },
95949
+ id: { type: "number", description: "Or a lead id \u2014 audits that lead's website and stores the result." }
95950
+ }
95951
+ }
95952
+ },
95452
95953
  {
95453
95954
  name: "hand_over_site",
95454
95955
  description: "HAND OVER a finished site to the paying client: removes the DEMO/preview banner, optionally attaches the client's own domain, and returns every credential in one place (site URL, /admin panel + password, booking page + its password, and the DNS records the client must set at their registrar). This is the paid-deliverable step \u2014 the banner exists to stop clients using a free demo as their real site, so only run this when the user says the client has paid / signed off / is going live. Requires the lead to already have a built site or booking page.",
@@ -95707,6 +96208,21 @@ It's not fully live yet. We can make it live for \xA3199 \u2014 your own domain,
95707
96208
 
95708
96209
  Interested?`;
95709
96210
  }
96211
+ function normaliseChannel(value) {
96212
+ switch (String(value ?? "").toLowerCase()) {
96213
+ case "email":
96214
+ return "email";
96215
+ case "sms":
96216
+ case "messages_mac":
96217
+ case "phone_link_win":
96218
+ case "manual":
96219
+ return "sms";
96220
+ case "whatsapp":
96221
+ return "whatsapp";
96222
+ default:
96223
+ return null;
96224
+ }
96225
+ }
95710
96226
  function outreachLink(channel, e164, message) {
95711
96227
  if (channel === "whatsapp") {
95712
96228
  return `https://wa.me/${e164.replace(/^\+/, "")}?text=${encodeURIComponent(message)}`;
@@ -95863,10 +96379,11 @@ Interested?`;
95863
96379
  };
95864
96380
  }
95865
96381
  const channelRow = db2.get("SELECT value FROM settings WHERE key = ?", ["outreach_channel"]);
95866
- const requested = String(input.channel ?? "").toLowerCase();
95867
- const channel = requested === "sms" || requested === "whatsapp" ? requested : channelRow?.value || "whatsapp";
96382
+ const channel = normaliseChannel(input.channel) ?? normaliseChannel(channelRow?.value) ?? "email";
95868
96383
  const template = outreachTemplate(db2);
95869
96384
  const bookingTemplate = bookingOutreachTemplate(db2);
96385
+ const preferEmail = channel === "email";
96386
+ const linkChannel = channel === "email" ? "whatsapp" : channel;
95870
96387
  const smtp = (0, email_1.smtpFromSettings)((k) => getDecryptedSetting(k));
95871
96388
  const emailSubject = getDecryptedSetting("outreach_email_subject") || "A free demo website for {name}";
95872
96389
  const prepared = [];
@@ -95875,7 +96392,7 @@ Interested?`;
95875
96392
  const isBookingLead = r.lead_kind === "no_booking";
95876
96393
  const message = (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");
95877
96394
  const e164 = toE164(r.phone ?? "");
95878
- if (!e164 && !r.email && smtp) {
96395
+ if (!r.email && smtp && (preferEmail || !e164)) {
95879
96396
  ctx.emitProgress?.({
95880
96397
  channel: "outreach:progress",
95881
96398
  payload: { msg: `Looking up an email for ${r.name}\u2026` }
@@ -95892,23 +96409,31 @@ Interested?`;
95892
96409
  harvested++;
95893
96410
  }
95894
96411
  }
95895
- if (e164) {
96412
+ const leadEmail = smtp && r.email ? r.email : null;
96413
+ const useEmail = leadEmail !== null && (preferEmail || !e164);
96414
+ if (!useEmail && e164) {
95896
96415
  prepared.push({
95897
96416
  id: r.id,
95898
96417
  name: r.name,
95899
96418
  via: "phone",
95900
96419
  phone: e164,
95901
- url: outreachLink(channel, e164, message),
96420
+ url: outreachLink(linkChannel, e164, message),
95902
96421
  message
95903
96422
  });
95904
- } else if (r.email && smtp) {
96423
+ } else if (useEmail && smtp && leadEmail) {
95905
96424
  const subject = emailSubject.replace(/\{name\}/g, r.name);
95906
- const res = await (0, email_1.sendEmail)(smtp, { to: r.email, subject, text: message });
96425
+ const res = await (0, email_1.sendEmail)(smtp, { to: leadEmail, subject, text: message });
96426
+ if (res.ok && res.messageId) {
96427
+ db2.run("UPDATE leads SET last_email_message_id = ? WHERE id = ?", [
96428
+ res.messageId,
96429
+ r.id
96430
+ ]);
96431
+ }
95907
96432
  prepared.push({
95908
96433
  id: r.id,
95909
96434
  name: r.name,
95910
96435
  via: "email",
95911
- email: r.email,
96436
+ email: leadEmail,
95912
96437
  emailSent: res.ok,
95913
96438
  emailError: res.ok ? void 0 : res.error,
95914
96439
  message
@@ -95922,7 +96447,8 @@ Interested?`;
95922
96447
  message
95923
96448
  });
95924
96449
  }
95925
- const reached = e164 || r.email && smtp && prepared[prepared.length - 1].emailSent;
96450
+ const last = prepared[prepared.length - 1];
96451
+ const reached = last.via === "phone" || last.via === "email" && last.emailSent;
95926
96452
  if (reached) {
95927
96453
  db2.run("UPDATE leads SET status = 'contacted', contacted_at = ? WHERE id = ?", [
95928
96454
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -95931,7 +96457,7 @@ Interested?`;
95931
96457
  (0, followup_1.logContact)({
95932
96458
  leadId: r.id,
95933
96459
  direction: "out",
95934
- channel: e164 ? channel : "email"
96460
+ channel: last.via === "email" ? "email" : linkChannel
95935
96461
  });
95936
96462
  }
95937
96463
  }
@@ -95939,7 +96465,7 @@ Interested?`;
95939
96465
  const emailsSent = prepared.filter((p) => p.via === "email" && p.emailSent).length;
95940
96466
  const emailsFailed = prepared.filter((p) => p.via === "email" && !p.emailSent).length;
95941
96467
  const unreachable = prepared.filter((p) => p.via === "none").length;
95942
- const smtpMissing = eligible.some((r) => !toE164(r.phone ?? "") && r.email) && !smtp;
96468
+ const smtpMissing = !smtp && (preferEmail || eligible.some((r) => !toE164(r.phone ?? "")));
95943
96469
  return {
95944
96470
  ok: true,
95945
96471
  channel,
@@ -95949,8 +96475,8 @@ Interested?`;
95949
96475
  emailsFailed,
95950
96476
  emailsFound: harvested,
95951
96477
  unreachable,
95952
- // Guide the user if email-only leads were skipped for want of SMTP.
95953
- note: smtpMissing ? "Some leads have only an email but SMTP isn't set up \u2014 run 'sproutkit setup' to add SMTP and email them." : void 0,
96478
+ // Guide the user if leads were skipped for want of SMTP.
96479
+ note: smtpMissing ? "Email outreach needs SMTP, which isn't set up yet \u2014 add it in Sproutkit \u2192 Settings \u2192 Email outreach, or run 'sproutkit setup'." : void 0,
95954
96480
  outreach: prepared
95955
96481
  };
95956
96482
  }
@@ -96206,7 +96732,9 @@ Interested?`;
96206
96732
  paid: r.paid_at,
96207
96733
  pdf: r.pdf_path
96208
96734
  })),
96209
- outstanding: (0, agency_1.formatMoney)(outstanding)
96735
+ // Use the agency's currency, not formatMoney's fallback — otherwise a
96736
+ // non-default agency sees the wrong symbol on their own total.
96737
+ outstanding: (0, agency_1.formatMoney)(outstanding, (0, agency_1.getAgencyProfile)().currency)
96210
96738
  };
96211
96739
  }
96212
96740
  case "mark_invoice_paid": {
@@ -96241,6 +96769,77 @@ Interested?`;
96241
96769
  return { ok: false, error: stats.error, business: lead.name };
96242
96770
  return { ok: true, id, business: lead.name, url: base, ...stats.data };
96243
96771
  }
96772
+ case "audit_sites": {
96773
+ const location = String(input.location ?? "").trim();
96774
+ const categories = input.categories ?? [];
96775
+ if (!location || categories.length === 0) {
96776
+ return { ok: false, error: "Give me a location and at least one business category." };
96777
+ }
96778
+ const placesKey = getDecryptedSetting("places_key");
96779
+ if (!placesKey)
96780
+ throw new Error("Google Places API key not set in Settings");
96781
+ ctx.emitProgress?.({
96782
+ channel: "leads:searching",
96783
+ payload: { msg: `Auditing websites in ${location}\u2026` }
96784
+ });
96785
+ const matches = await (0, places_1.geocodeForward)(location, 1);
96786
+ const place = matches[0];
96787
+ const result = await (0, lead_search_1.searchAndSaveLeadsStream)({
96788
+ placesKey,
96789
+ city: location,
96790
+ types: categories,
96791
+ maxResults: Math.min(Number(input.limit ?? 25), 100),
96792
+ center: place ? { lat: place.lat, lng: place.lng } : void 0,
96793
+ radiusMeters: Number(input.radiusMeters ?? 1e4),
96794
+ mode: "outdated"
96795
+ }, (lead) => ctx.emitProgress?.({ channel: "leads:found", payload: { lead } }));
96796
+ ctx.emitProgress?.({ channel: "leads:changed", payload: { reason: "audit_sites" } });
96797
+ const saved = db2.all(`SELECT id, name, website, site_score, site_issues
96798
+ FROM leads
96799
+ WHERE lead_kind = 'outdated' AND audited_at IS NOT NULL
96800
+ ORDER BY audited_at DESC, site_score DESC
96801
+ LIMIT ?`, [Number(input.limit ?? 25)]);
96802
+ return {
96803
+ ok: true,
96804
+ found: result.found,
96805
+ saved: result.saved,
96806
+ prospects: saved.map((r) => ({
96807
+ id: r.id,
96808
+ business: r.name,
96809
+ website: r.website,
96810
+ score: r.site_score,
96811
+ issues: JSON.parse(r.site_issues || "[]").slice(0, 3).map((i) => `${i.severity}: ${i.label}`)
96812
+ }))
96813
+ };
96814
+ }
96815
+ case "audit_one_site": {
96816
+ let url = input.url ?? "";
96817
+ let leadId = null;
96818
+ if (input.id != null) {
96819
+ leadId = Number(input.id);
96820
+ const lead = db2.get("SELECT id, name, website FROM leads WHERE id = ?", [leadId]);
96821
+ if (!lead)
96822
+ throw new Error(`Lead #${leadId} not found`);
96823
+ if (!lead.website)
96824
+ return { ok: false, error: `#${leadId} ${lead.name} has no website on record.` };
96825
+ url = lead.website;
96826
+ }
96827
+ if (!url)
96828
+ return { ok: false, error: "Give me a website URL or a lead id." };
96829
+ const audit = await (0, site_audit_1.auditSite)(url);
96830
+ if (leadId !== null) {
96831
+ db2.run("UPDATE leads SET site_score = ?, site_issues = ?, audited_at = ? WHERE id = ?", [audit.score, JSON.stringify(audit.issues), (/* @__PURE__ */ new Date()).toISOString(), leadId]);
96832
+ }
96833
+ return {
96834
+ ok: true,
96835
+ url: audit.url,
96836
+ reachable: audit.reachable,
96837
+ score: audit.score,
96838
+ worthRebuilding: (0, site_audit_1.isRebuildProspect)(audit),
96839
+ issues: audit.issues,
96840
+ error: audit.error
96841
+ };
96842
+ }
96244
96843
  case "hand_over_site": {
96245
96844
  const id = Number(input.id);
96246
96845
  const lead = db2.get("SELECT id, name, site_url, booking_url FROM leads WHERE id = ?", [id]);
@@ -97269,111 +97868,59 @@ var require_vercel_store = __commonJS({
97269
97868
  }
97270
97869
  });
97271
97870
 
97272
- // package.json
97273
- var require_package2 = __commonJS({
97274
- "package.json"(exports2, module2) {
97275
- module2.exports = {
97276
- name: "sproutkit-code",
97277
- version: "1.7.0",
97278
- description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
97279
- bin: {
97280
- sproutkit: "./dist/index.js"
97281
- },
97282
- type: "commonjs",
97283
- files: [
97284
- "dist",
97285
- "README.md"
97286
- ],
97287
- engines: {
97288
- node: ">=18"
97289
- },
97290
- keywords: [
97291
- "sproutkit",
97292
- "leads",
97293
- "websites",
97294
- "cli",
97295
- "ai"
97296
- ],
97297
- scripts: {
97298
- dev: "tsc && node dist/index.js",
97299
- build: "node build.mjs && chmod +x dist/index.js",
97300
- typecheck: "tsc --noEmit",
97301
- prepublishOnly: "cd ../desktop && npm run build:main && cd ../sproutkit-code && npm run build"
97302
- },
97303
- dependencies: {
97304
- "@anthropic-ai/sdk": "^0.30.1",
97305
- axios: "^1.7.1",
97306
- "node-sqlite3-wasm": "^0.8.20",
97307
- pdfkit: "^0.20.1"
97308
- },
97309
- devDependencies: {
97310
- "@types/node": "^20.14.0",
97311
- typescript: "^5.4.0"
97312
- }
97313
- };
97871
+ // src/engine.ts
97872
+ var db, chat, chatTools, autoMode, cms, bookingPreset, builder, vercelStore, agency, followup, invoicing, models, llm, initDatabase, getDb, chatWithTools, runTool, TOOLS, startAutoJob, cancelAutoJob, provisionCmsForLead, generatePreset, deployBookingOnly, deployStandaloneBooking, handoverSite, createBlobStore, refreshModels, refreshIfStale, listCachedModels, getPreferredModel, getAgencyProfile, setAgencyProfile, agencyConfigured, formatMoney, dueFollowUps, outreachStats, contactHistory, renderLetterPdf, listInvoicesRaw, checkClaudeCodeAuth, llmBackend, claudeBin, CLAUDE_CODE_MODELS, getClaudeCodeModel;
97873
+ var init_engine = __esm({
97874
+ "src/engine.ts"() {
97875
+ "use strict";
97876
+ db = require_db();
97877
+ chat = require_chat();
97878
+ chatTools = require_chat_tools();
97879
+ autoMode = require_auto_mode();
97880
+ cms = require_cms_provision();
97881
+ bookingPreset = require_booking_preset();
97882
+ builder = require_builder();
97883
+ vercelStore = require_vercel_store();
97884
+ agency = require_agency();
97885
+ followup = require_followup();
97886
+ invoicing = require_invoicing();
97887
+ models = require_models();
97888
+ llm = require_llm();
97889
+ initDatabase = db.initDatabase;
97890
+ getDb = db.getDb;
97891
+ chatWithTools = chat.chatWithTools;
97892
+ runTool = chatTools.runTool;
97893
+ TOOLS = chatTools.TOOLS;
97894
+ startAutoJob = autoMode.startAutoJob;
97895
+ cancelAutoJob = autoMode.cancelAutoJob;
97896
+ provisionCmsForLead = cms.provisionCmsForLead;
97897
+ generatePreset = bookingPreset.generatePreset;
97898
+ deployBookingOnly = builder.deployBookingOnly;
97899
+ deployStandaloneBooking = builder.deployStandaloneBooking;
97900
+ handoverSite = builder.handoverSite;
97901
+ createBlobStore = vercelStore.createBlobStore;
97902
+ refreshModels = models.refreshModels;
97903
+ refreshIfStale = models.refreshIfStale;
97904
+ listCachedModels = models.listCachedModels;
97905
+ getPreferredModel = models.getPreferredModel;
97906
+ getAgencyProfile = agency.getAgencyProfile;
97907
+ setAgencyProfile = agency.setAgencyProfile;
97908
+ agencyConfigured = agency.agencyConfigured;
97909
+ formatMoney = agency.formatMoney;
97910
+ dueFollowUps = followup.dueFollowUps;
97911
+ outreachStats = followup.outreachStats;
97912
+ contactHistory = followup.contactHistory;
97913
+ renderLetterPdf = invoicing.renderLetterPdf;
97914
+ listInvoicesRaw = invoicing.listInvoices;
97915
+ checkClaudeCodeAuth = llm.checkClaudeCodeAuth;
97916
+ llmBackend = llm.getBackend;
97917
+ claudeBin = llm.claudeBin;
97918
+ CLAUDE_CODE_MODELS = llm.CLAUDE_CODE_MODELS;
97919
+ getClaudeCodeModel = llm.getClaudeCodeModel;
97314
97920
  }
97315
97921
  });
97316
97922
 
97317
- // src/engine.ts
97318
- var db = require_db();
97319
- var chat = require_chat();
97320
- var chatTools = require_chat_tools();
97321
- var autoMode = require_auto_mode();
97322
- var cms = require_cms_provision();
97323
- var bookingPreset = require_booking_preset();
97324
- var builder = require_builder();
97325
- var vercelStore = require_vercel_store();
97326
- var agency = require_agency();
97327
- var followup = require_followup();
97328
- var invoicing = require_invoicing();
97329
- var models = require_models();
97330
- var llm = require_llm();
97331
- var initDatabase = db.initDatabase;
97332
- var getDb = db.getDb;
97333
- var chatWithTools = chat.chatWithTools;
97334
- var runTool = chatTools.runTool;
97335
- var startAutoJob = autoMode.startAutoJob;
97336
- var cancelAutoJob = autoMode.cancelAutoJob;
97337
- var provisionCmsForLead = cms.provisionCmsForLead;
97338
- var generatePreset = bookingPreset.generatePreset;
97339
- var deployBookingOnly = builder.deployBookingOnly;
97340
- var deployStandaloneBooking = builder.deployStandaloneBooking;
97341
- var handoverSite = builder.handoverSite;
97342
- var createBlobStore = vercelStore.createBlobStore;
97343
- var refreshModels = models.refreshModels;
97344
- var refreshIfStale = models.refreshIfStale;
97345
- var listCachedModels = models.listCachedModels;
97346
- var getPreferredModel = models.getPreferredModel;
97347
- var getAgencyProfile = agency.getAgencyProfile;
97348
- var setAgencyProfile = agency.setAgencyProfile;
97349
- var agencyConfigured = agency.agencyConfigured;
97350
- var formatMoney = agency.formatMoney;
97351
- var dueFollowUps = followup.dueFollowUps;
97352
- var outreachStats = followup.outreachStats;
97353
- var contactHistory = followup.contactHistory;
97354
- var renderLetterPdf = invoicing.renderLetterPdf;
97355
- var listInvoicesRaw = invoicing.listInvoices;
97356
- var checkClaudeCodeAuth = llm.checkClaudeCodeAuth;
97357
- var llmBackend = llm.getBackend;
97358
- var claudeBin = llm.claudeBin;
97359
- var CLAUDE_CODE_MODELS = llm.CLAUDE_CODE_MODELS;
97360
- var getClaudeCodeModel = llm.getClaudeCodeModel;
97361
-
97362
97923
  // src/config.ts
97363
- var REQUIRED_KEYS = [
97364
- { key: "anthropic_key", label: "Anthropic API key", hint: "sk-ant-\u2026 \u2014 powers the chat + site generation", required: true, apiBackendOnly: true },
97365
- { key: "places_key", label: "Google Places API key", hint: "finds businesses, photos, reviews", required: true },
97366
- { key: "vercel_token", label: "Vercel token", hint: "deploys the generated sites", required: true },
97367
- { key: "blob_token", label: "Vercel Blob token", hint: "required for booking sites & CMS (stores form config/submissions)", required: false },
97368
- { key: "resend_key", label: "Resend API key", hint: "optional \u2014 emails the business owner when a booking comes in", required: false },
97369
- // SMTP for email outreach — used per-lead when a business has no phone. All
97370
- // optional; without them, email-only leads are simply skipped.
97371
- { key: "smtp_host", label: "SMTP host", hint: "optional \u2014 for email outreach, e.g. smtp.gmail.com", required: false },
97372
- { key: "smtp_port", label: "SMTP port", hint: "optional \u2014 587 (STARTTLS) or 465 (TLS); default 587", required: false },
97373
- { key: "smtp_user", label: "SMTP username", hint: "optional \u2014 your email login", required: false },
97374
- { key: "smtp_pass", label: "SMTP password", hint: "optional \u2014 password or app-password", required: false },
97375
- { key: "smtp_from", label: "Email 'From'", hint: "optional \u2014 e.g. You <you@domain.com>; defaults to the username", required: false }
97376
- ];
97377
97924
  function getSetting(key) {
97378
97925
  const row = getDb().get("SELECT value FROM settings WHERE key = ?", [key]);
97379
97926
  return row?.value ?? null;
@@ -97398,81 +97945,29 @@ function missingRequiredKeys() {
97398
97945
  return !getSetting(k.key);
97399
97946
  }).map((k) => ({ key: k.key, label: k.label, hint: k.hint }));
97400
97947
  }
97401
-
97402
- // src/license.ts
97403
- var import_os = __toESM(require("os"));
97404
- var import_crypto = require("crypto");
97405
- var GRACE_MS = 7 * 24 * 60 * 60 * 1e3;
97406
- function apiBase() {
97407
- return process.env.SPROUTKIT_API_BASE_URL ?? "https://sproutkit.app";
97408
- }
97409
- function deviceId() {
97410
- const macs = Object.values(import_os.default.networkInterfaces()).flat().filter((i) => !!i && !i.internal).map((i) => i.mac).filter((m) => m && m !== "00:00:00:00:00:00");
97411
- return (0, import_crypto.createHash)("sha256").update(`${macs[0] ?? "unknown"}|${import_os.default.hostname()}`).digest("hex").slice(0, 32);
97412
- }
97413
- async function callValidate(key) {
97414
- try {
97415
- const r = await fetch(`${apiBase()}/api/licenses/validate`, {
97416
- method: "POST",
97417
- headers: { "Content-Type": "application/json" },
97418
- body: JSON.stringify({ key, deviceId: deviceId() }),
97419
- signal: AbortSignal.timeout(8e3)
97420
- });
97421
- if (!r.ok) return null;
97422
- return await r.json();
97423
- } catch {
97424
- return null;
97425
- }
97426
- }
97427
- async function activate(key) {
97428
- const trimmed = key.trim();
97429
- const result = await callValidate(trimmed);
97430
- if (!result) return { status: "missing" };
97431
- if (!result.valid) {
97432
- return {
97433
- status: result.reason === "revoked" ? "revoked" : result.reason === "refunded" ? "refunded" : "missing",
97434
- email: result.email
97435
- };
97436
- }
97437
- setSetting("license_key", trimmed);
97438
- setSetting("license_email", result.email ?? "");
97439
- setSetting("license_validated_at", String(Date.now()));
97440
- return { status: "active", email: result.email };
97441
- }
97442
- async function check() {
97443
- const key = getSetting("license_key");
97444
- if (!key) return { status: "missing" };
97445
- const result = await callValidate(key);
97446
- if (result) {
97447
- if (result.valid) {
97448
- setSetting("license_validated_at", String(Date.now()));
97449
- return { status: "active", email: result.email };
97450
- }
97451
- return {
97452
- status: result.reason === "revoked" ? "revoked" : result.reason === "refunded" ? "refunded" : "missing",
97453
- email: result.email
97454
- };
97948
+ var REQUIRED_KEYS;
97949
+ var init_config = __esm({
97950
+ "src/config.ts"() {
97951
+ "use strict";
97952
+ init_engine();
97953
+ REQUIRED_KEYS = [
97954
+ { key: "anthropic_key", label: "Anthropic API key", hint: "sk-ant-\u2026 \u2014 powers the chat + site generation", required: true, apiBackendOnly: true },
97955
+ { key: "places_key", label: "Google Places API key", hint: "finds businesses, photos, reviews", required: true },
97956
+ { key: "vercel_token", label: "Vercel token", hint: "deploys the generated sites", required: true },
97957
+ { key: "blob_token", label: "Vercel Blob token", hint: "required for booking sites & CMS (stores form config/submissions)", required: false },
97958
+ { key: "resend_key", label: "Resend API key", hint: "optional \u2014 emails the business owner when a booking comes in", required: false },
97959
+ // SMTP for email outreach — used per-lead when a business has no phone. All
97960
+ // optional; without them, email-only leads are simply skipped.
97961
+ { key: "smtp_host", label: "SMTP host", hint: "optional \u2014 for email outreach, e.g. smtp.gmail.com", required: false },
97962
+ { key: "smtp_port", label: "SMTP port", hint: "optional \u2014 587 (STARTTLS) or 465 (TLS); default 587", required: false },
97963
+ { key: "smtp_user", label: "SMTP username", hint: "optional \u2014 your email login", required: false },
97964
+ { key: "smtp_pass", label: "SMTP password", hint: "optional \u2014 password or app-password", required: false },
97965
+ { key: "smtp_from", label: "Email 'From'", hint: "optional \u2014 e.g. You <you@domain.com>; defaults to the username", required: false }
97966
+ ];
97455
97967
  }
97456
- const last = Number(getSetting("license_validated_at") ?? 0);
97457
- if (Date.now() - last < GRACE_MS) return { status: "active" };
97458
- return { status: "offline_grace_expired" };
97459
- }
97968
+ });
97460
97969
 
97461
97970
  // src/tty.ts
97462
- var import_readline = __toESM(require("readline"));
97463
- var useColor = process.stdout.isTTY && !process.env.NO_COLOR;
97464
- var wrap = (code) => (s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
97465
- var c = {
97466
- green: wrap("38;5;65"),
97467
- // moss, matches the brand
97468
- greenBold: wrap("1;38;5;65"),
97469
- dim: wrap("2"),
97470
- bold: wrap("1"),
97471
- red: wrap("31"),
97472
- amber: wrap("33"),
97473
- cyan: wrap("36")
97474
- };
97475
- var rl = null;
97476
97971
  function iface() {
97477
97972
  if (!rl) rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
97478
97973
  return rl;
@@ -97543,124 +98038,718 @@ function onStopRequested(onStop) {
97543
98038
  function say(msg = "") {
97544
98039
  process.stdout.write(msg + "\n");
97545
98040
  }
97546
- var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
97547
- var Status = class {
97548
- timer = null;
97549
- frame = 0;
97550
- label = "";
97551
- stage = "";
97552
- tty = process.stdout.isTTY && useColor;
97553
- start(label) {
97554
- this.label = label;
97555
- this.stage = "";
97556
- if (!this.tty) {
97557
- say(c.cyan(` \u27F3 ${label}`));
97558
- return;
98041
+ var import_readline, useColor, wrap, c, rl, FRAMES, Status, STATE_MARK, StatusBoard;
98042
+ var init_tty = __esm({
98043
+ "src/tty.ts"() {
98044
+ "use strict";
98045
+ import_readline = __toESM(require("readline"));
98046
+ useColor = process.stdout.isTTY && !process.env.NO_COLOR;
98047
+ wrap = (code) => (s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
98048
+ c = {
98049
+ green: wrap("38;5;65"),
98050
+ // moss, matches the brand
98051
+ greenBold: wrap("1;38;5;65"),
98052
+ dim: wrap("2"),
98053
+ bold: wrap("1"),
98054
+ red: wrap("31"),
98055
+ amber: wrap("33"),
98056
+ cyan: wrap("36")
98057
+ };
98058
+ rl = null;
98059
+ FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
98060
+ Status = class {
98061
+ timer = null;
98062
+ frame = 0;
98063
+ label = "";
98064
+ stage = "";
98065
+ tty = process.stdout.isTTY && useColor;
98066
+ start(label) {
98067
+ this.label = label;
98068
+ this.stage = "";
98069
+ if (!this.tty) {
98070
+ say(c.cyan(` \u27F3 ${label}`));
98071
+ return;
98072
+ }
98073
+ this.render();
98074
+ this.timer = setInterval(() => {
98075
+ this.frame = (this.frame + 1) % FRAMES.length;
98076
+ this.render();
98077
+ }, 80);
98078
+ }
98079
+ // Update the current stage (e.g. "Generating site with Claude…").
98080
+ update(stage) {
98081
+ this.stage = stage;
98082
+ if (!this.tty) {
98083
+ say(c.dim(` \u2026 ${stage}`));
98084
+ return;
98085
+ }
98086
+ this.render();
98087
+ }
98088
+ render() {
98089
+ const spin = c.cyan(FRAMES[this.frame]);
98090
+ const stage = this.stage ? c.dim(` \xB7 ${this.stage}`) : "";
98091
+ process.stdout.write(`\r\x1B[2K ${spin} ${this.label}${stage}`);
98092
+ }
98093
+ // Stop with a terminal state, leaving one clean line behind.
98094
+ done(ok, finalLabel) {
98095
+ if (this.timer) {
98096
+ clearInterval(this.timer);
98097
+ this.timer = null;
98098
+ }
98099
+ const label = finalLabel ?? this.label;
98100
+ const mark2 = ok ? c.green("\u2713") : c.red("\u2717");
98101
+ if (this.tty) process.stdout.write(`\r\x1B[2K ${mark2} ${label}
98102
+ `);
98103
+ else say(` ${mark2} ${label}`);
98104
+ }
98105
+ };
98106
+ STATE_MARK = {
98107
+ queued: () => c.dim("\xB7"),
98108
+ building: () => c.cyan(""),
98109
+ // spinner frame filled in at render time
98110
+ built: () => c.green("\u2713"),
98111
+ failed: () => c.red("\u2717"),
98112
+ skipped: () => c.dim("\u2013")
98113
+ };
98114
+ StatusBoard = class {
98115
+ rows = [];
98116
+ frame = 0;
98117
+ timer = null;
98118
+ lastLineCount = 0;
98119
+ tty = process.stdout.isTTY && useColor;
98120
+ // Non-TTY: remember the last printed signature per row so we only log changes.
98121
+ lastSig = /* @__PURE__ */ new Map();
98122
+ header = "";
98123
+ start(header, rows) {
98124
+ this.header = header;
98125
+ this.rows = rows;
98126
+ if (!this.tty) {
98127
+ say(c.cyan(` \u27F3 ${header}`));
98128
+ this.rows.forEach((r) => this.logRowIfChanged(r));
98129
+ return;
98130
+ }
98131
+ say(c.cyan(` \u27F3 ${header}`));
98132
+ this.render();
98133
+ this.timer = setInterval(() => {
98134
+ this.frame = (this.frame + 1) % FRAMES.length;
98135
+ this.render();
98136
+ }, 90);
98137
+ }
98138
+ update(row) {
98139
+ const i = this.rows.findIndex((r) => r.key === row.key);
98140
+ if (i >= 0) this.rows[i] = row;
98141
+ else this.rows.push(row);
98142
+ if (!this.tty) this.logRowIfChanged(row);
98143
+ else this.render();
98144
+ }
98145
+ // Replace the whole set at once (e.g. from a job snapshot).
98146
+ set(rows) {
98147
+ this.rows = rows;
98148
+ if (!this.tty) rows.forEach((r) => this.logRowIfChanged(r));
98149
+ else this.render();
98150
+ }
98151
+ line(r) {
98152
+ const mark2 = r.state === "building" ? c.cyan(FRAMES[this.frame]) : STATE_MARK[r.state]();
98153
+ const detail = r.detail ? c.dim(` \xB7 ${r.detail}`) : "";
98154
+ return ` ${mark2} ${r.label}${detail}`;
98155
+ }
98156
+ logRowIfChanged(r) {
98157
+ const sig = `${r.state}:${r.detail ?? ""}`;
98158
+ if (this.lastSig.get(r.key) === sig) return;
98159
+ this.lastSig.set(r.key, sig);
98160
+ const mark2 = r.state === "built" ? "\u2713" : r.state === "failed" ? "\u2717" : r.state === "skipped" ? "\u2013" : "\u2026";
98161
+ say(` ${mark2} ${r.label}${r.detail ? ` \xB7 ${r.detail}` : ""}`);
98162
+ }
98163
+ render() {
98164
+ if (this.lastLineCount > 0) process.stdout.write(`\x1B[${this.lastLineCount}A`);
98165
+ const out = this.rows.map((r) => `\x1B[2K${this.line(r)}`).join("\n");
98166
+ process.stdout.write(out + "\n");
98167
+ this.lastLineCount = this.rows.length;
98168
+ }
98169
+ stop() {
98170
+ if (this.timer) {
98171
+ clearInterval(this.timer);
98172
+ this.timer = null;
98173
+ }
98174
+ if (this.tty) this.render();
98175
+ }
98176
+ };
98177
+ }
98178
+ });
98179
+
98180
+ // src/desktop-install.ts
98181
+ var desktop_install_exports = {};
98182
+ __export(desktop_install_exports, {
98183
+ desktopInstallPath: () => desktopInstallPath,
98184
+ desktopSupported: () => desktopSupported,
98185
+ installDesktop: () => installDesktop,
98186
+ openDesktop: () => openDesktop,
98187
+ runDesktopCommand: () => runDesktopCommand
98188
+ });
98189
+ function installCandidates() {
98190
+ const home = import_os3.default.homedir();
98191
+ if (process.platform === "darwin") {
98192
+ const override = process.env.SPROUTKIT_DESKTOP_DIR;
98193
+ if (override) return [import_path2.default.join(override, "Sproutkit.app")];
98194
+ return ["/Applications/Sproutkit.app", import_path2.default.join(home, "Applications", "Sproutkit.app")];
98195
+ }
98196
+ if (process.platform === "win32") {
98197
+ const local = process.env.LOCALAPPDATA || import_path2.default.join(home, "AppData", "Local");
98198
+ const progs = [process.env.ProgramFiles, process.env["ProgramFiles(x86)"]].filter(Boolean);
98199
+ return [
98200
+ import_path2.default.join(local, "Programs", "Sproutkit", "Sproutkit.exe"),
98201
+ ...progs.map((p) => import_path2.default.join(p, "Sproutkit", "Sproutkit.exe"))
98202
+ ];
98203
+ }
98204
+ return [];
98205
+ }
98206
+ function desktopSupported() {
98207
+ return process.platform === "darwin" || process.platform === "win32";
98208
+ }
98209
+ function desktopInstallPath() {
98210
+ return installCandidates().find((p) => import_fs2.default.existsSync(p)) ?? null;
98211
+ }
98212
+ function pickAsset(assets) {
98213
+ if (process.platform === "darwin") {
98214
+ const dmgs = assets.filter((a) => a.name.endsWith(".dmg"));
98215
+ return process.arch === "arm64" ? dmgs.find((a) => a.name.includes("arm64")) : dmgs.find((a) => !a.name.includes("arm64"));
98216
+ }
98217
+ if (process.platform === "win32") return assets.find((a) => a.name.endsWith(".exe"));
98218
+ return void 0;
98219
+ }
98220
+ async function latestRelease() {
98221
+ try {
98222
+ const r = await fetch(RELEASES_API, {
98223
+ headers: { Accept: "application/vnd.github+json", "User-Agent": "sproutkit-code" },
98224
+ signal: AbortSignal.timeout(1e4)
98225
+ });
98226
+ if (!r.ok) return { error: `GitHub answered ${r.status}` };
98227
+ const j = await r.json();
98228
+ const asset = pickAsset(j.assets ?? []);
98229
+ if (!asset) return { error: `No ${process.platform}/${process.arch} build in release ${j.tag_name}` };
98230
+ return { version: j.tag_name.replace(/^v/, ""), asset };
98231
+ } catch (e) {
98232
+ return { error: e.message };
98233
+ }
98234
+ }
98235
+ async function download(url, dest, total) {
98236
+ const r = await fetch(url, { redirect: "follow" });
98237
+ if (!r.ok || !r.body) throw new Error(`download failed (${r.status})`);
98238
+ const out = import_fs2.default.createWriteStream(dest);
98239
+ const tty = process.stdout.isTTY;
98240
+ let got = 0;
98241
+ let lastPct = -1;
98242
+ const reader = r.body.getReader();
98243
+ try {
98244
+ while (true) {
98245
+ const { done, value } = await reader.read();
98246
+ if (done) break;
98247
+ got += value.length;
98248
+ if (!out.write(value)) await new Promise((res) => out.once("drain", () => res()));
98249
+ const pct = total ? Math.floor(got / total * 100) : 0;
98250
+ if (tty && pct !== lastPct) {
98251
+ lastPct = pct;
98252
+ const mb = (n) => (n / 1048576).toFixed(0);
98253
+ process.stdout.write(`\r ${c.dim(`Downloading\u2026 ${pct}% (${mb(got)} / ${mb(total)} MB)`)}`);
98254
+ }
97559
98255
  }
97560
- this.render();
97561
- this.timer = setInterval(() => {
97562
- this.frame = (this.frame + 1) % FRAMES.length;
97563
- this.render();
97564
- }, 80);
97565
- }
97566
- // Update the current stage (e.g. "Generating site with Claude…").
97567
- update(stage) {
97568
- this.stage = stage;
97569
- if (!this.tty) {
97570
- say(c.dim(` \u2026 ${stage}`));
97571
- return;
98256
+ } finally {
98257
+ await new Promise((res, rej) => out.end((err) => err ? rej(err) : res()));
98258
+ if (tty) process.stdout.write("\n");
98259
+ }
98260
+ if (total && got !== total) throw new Error(`download incomplete (${got} of ${total} bytes)`);
98261
+ }
98262
+ function run(cmd, args) {
98263
+ const r = (0, import_child_process.spawnSync)(cmd, args, { encoding: "utf-8" });
98264
+ return { ok: r.status === 0, out: `${r.stdout ?? ""}${r.stderr ?? ""}`.trim() };
98265
+ }
98266
+ function canWrite(dir) {
98267
+ try {
98268
+ import_fs2.default.accessSync(dir, import_fs2.default.constants.W_OK);
98269
+ return true;
98270
+ } catch {
98271
+ return false;
98272
+ }
98273
+ }
98274
+ function installMac(dmg) {
98275
+ const mount = import_fs2.default.mkdtempSync(import_path2.default.join(import_os3.default.tmpdir(), "sproutkit-mnt-"));
98276
+ const attach = run("hdiutil", ["attach", dmg, "-nobrowse", "-readonly", "-mountpoint", mount]);
98277
+ if (!attach.ok) throw new Error(`couldn't open the disk image: ${attach.out}`);
98278
+ try {
98279
+ const src = import_path2.default.join(mount, "Sproutkit.app");
98280
+ if (!import_fs2.default.existsSync(src)) throw new Error("Sproutkit.app not found in the disk image");
98281
+ const destDir = process.env.SPROUTKIT_DESKTOP_DIR || (canWrite("/Applications") ? "/Applications" : import_path2.default.join(import_os3.default.homedir(), "Applications"));
98282
+ import_fs2.default.mkdirSync(destDir, { recursive: true });
98283
+ const dest = import_path2.default.join(destDir, "Sproutkit.app");
98284
+ import_fs2.default.rmSync(dest, { recursive: true, force: true });
98285
+ const copy = run("ditto", [src, dest]);
98286
+ if (!copy.ok) throw new Error(`couldn't copy the app: ${copy.out}`);
98287
+ run("xattr", ["-cr", dest]);
98288
+ return dest;
98289
+ } finally {
98290
+ run("hdiutil", ["detach", mount, "-quiet"]);
98291
+ import_fs2.default.rmSync(mount, { recursive: true, force: true });
98292
+ }
98293
+ }
98294
+ function installWindows(exe) {
98295
+ const r = run(exe, ["/S"]);
98296
+ if (!r.ok) throw new Error(`installer exited with an error${r.out ? `: ${r.out}` : ""}`);
98297
+ const p = desktopInstallPath();
98298
+ if (!p) throw new Error("installer finished but Sproutkit.exe wasn't found");
98299
+ return p;
98300
+ }
98301
+ async function installDesktop() {
98302
+ if (!desktopSupported()) {
98303
+ return { ok: false, error: "The desktop app is only available for macOS and Windows." };
98304
+ }
98305
+ say(c.dim(" Finding the latest desktop release\u2026"));
98306
+ const rel = await latestRelease();
98307
+ if ("error" in rel) return { ok: false, error: `Couldn't find a download: ${rel.error}` };
98308
+ const tmp = import_fs2.default.mkdtempSync(import_path2.default.join(import_os3.default.tmpdir(), "sproutkit-dl-"));
98309
+ const file = import_path2.default.join(tmp, rel.asset.name);
98310
+ try {
98311
+ await download(rel.asset.browser_download_url, file, rel.asset.size);
98312
+ say(c.dim(" Installing\u2026"));
98313
+ const dest = process.platform === "darwin" ? installMac(file) : installWindows(file);
98314
+ return { ok: true, path: dest, version: rel.version };
98315
+ } catch (e) {
98316
+ return { ok: false, error: e.message };
98317
+ } finally {
98318
+ import_fs2.default.rmSync(tmp, { recursive: true, force: true });
98319
+ }
98320
+ }
98321
+ function openDesktop() {
98322
+ const p = desktopInstallPath();
98323
+ if (!p) return false;
98324
+ const r = process.platform === "darwin" ? (0, import_child_process.spawnSync)("open", [p], { stdio: "ignore" }) : (0, import_child_process.spawnSync)("cmd", ["/c", "start", "", p], { stdio: "ignore" });
98325
+ return r.status === 0;
98326
+ }
98327
+ async function runDesktopCommand(args) {
98328
+ const existing = desktopInstallPath();
98329
+ if (existing && !args.includes("--update")) {
98330
+ say(c.green(` \u2713 The desktop app is installed (${existing}).`));
98331
+ if (!args.includes("--no-open") && openDesktop()) say(c.dim(" Opening it now."));
98332
+ say(c.dim(" Run 'sproutkit desktop --update' to reinstall the latest version."));
98333
+ return;
98334
+ }
98335
+ const res = await installDesktop();
98336
+ if (!res.ok) {
98337
+ say(c.red(` ${res.error}`));
98338
+ say(c.dim(" You can also download it from https://sproutkit.app/install"));
98339
+ process.exitCode = 1;
98340
+ return;
98341
+ }
98342
+ say(c.green(` \u2713 Sproutkit ${res.version} installed to ${res.path}`));
98343
+ if (!args.includes("--no-open") && openDesktop()) say(c.dim(" Opening it now."));
98344
+ }
98345
+ var import_fs2, import_os3, import_path2, import_child_process, RELEASES_API;
98346
+ var init_desktop_install = __esm({
98347
+ "src/desktop-install.ts"() {
98348
+ "use strict";
98349
+ import_fs2 = __toESM(require("fs"));
98350
+ import_os3 = __toESM(require("os"));
98351
+ import_path2 = __toESM(require("path"));
98352
+ import_child_process = require("child_process");
98353
+ init_tty();
98354
+ RELEASES_API = "https://api.github.com/repos/shaz2k/sproutkit-downloads/releases/latest";
98355
+ }
98356
+ });
98357
+
98358
+ // package.json
98359
+ var require_package2 = __commonJS({
98360
+ "package.json"(exports2, module2) {
98361
+ module2.exports = {
98362
+ name: "sproutkit-code",
98363
+ version: "1.9.0",
98364
+ description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
98365
+ bin: {
98366
+ sproutkit: "./dist/index.js"
98367
+ },
98368
+ type: "commonjs",
98369
+ files: [
98370
+ "dist",
98371
+ "README.md"
98372
+ ],
98373
+ engines: {
98374
+ node: ">=18"
98375
+ },
98376
+ keywords: [
98377
+ "sproutkit",
98378
+ "leads",
98379
+ "websites",
98380
+ "cli",
98381
+ "ai"
98382
+ ],
98383
+ scripts: {
98384
+ dev: "tsc && node dist/index.js",
98385
+ build: "node build.mjs && chmod +x dist/index.js",
98386
+ typecheck: "tsc --noEmit",
98387
+ prepublishOnly: "cd ../desktop && npm run build:main && cd ../sproutkit-code && npm run build"
98388
+ },
98389
+ dependencies: {
98390
+ "@anthropic-ai/sdk": "^0.30.1",
98391
+ axios: "^1.7.1",
98392
+ "node-sqlite3-wasm": "^0.8.20",
98393
+ pdfkit: "^0.20.1"
98394
+ },
98395
+ devDependencies: {
98396
+ "@types/node": "^20.14.0",
98397
+ typescript: "^5.4.0"
98398
+ }
98399
+ };
98400
+ }
98401
+ });
98402
+
98403
+ // src/invoke-ipc.ts
98404
+ async function requireBlobToken(vercelToken, emit2, leadId) {
98405
+ const existing = getSetting("blob_token");
98406
+ if (existing) return existing;
98407
+ emit2("bookings:progress", { leadId, msg: "No Blob token yet \u2014 creating a Blob store for you\u2026" });
98408
+ const created = await createBlobStore({ vercelToken, name: "sproutkit" });
98409
+ if (created.ok) {
98410
+ throw new Error(
98411
+ "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."
98412
+ );
98413
+ }
98414
+ if (created.alreadyExists) {
98415
+ throw new Error(
98416
+ "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)."
98417
+ );
98418
+ }
98419
+ throw new Error(
98420
+ `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'.`
98421
+ );
98422
+ }
98423
+ function makeInvokeIpc(emit2) {
98424
+ return async (channel, payload) => {
98425
+ if (channel === "auto:build:start") {
98426
+ const args = payload;
98427
+ const anthropicKey = getSetting("anthropic_key");
98428
+ const vercelToken = getSetting("vercel_token");
98429
+ const placesKey = getSetting("places_key");
98430
+ if (!anthropicKey) throw new Error("Anthropic API key not set");
98431
+ if (!vercelToken) throw new Error("Vercel token not set");
98432
+ return startAutoJob({
98433
+ leadIds: args.leadIds,
98434
+ concurrency: args.concurrency,
98435
+ anthropicKey,
98436
+ vercelToken,
98437
+ placesKey,
98438
+ emit: emit2
98439
+ });
97572
98440
  }
97573
- this.render();
98441
+ if (channel === "cms:provision") {
98442
+ const leadId = Number(payload);
98443
+ const db2 = getDb();
98444
+ const lead = db2.get("SELECT * FROM leads WHERE id = ?", [leadId]);
98445
+ if (!lead) throw new Error("Lead not found");
98446
+ const placesKey = getSetting("places_key");
98447
+ const vercelToken = getSetting("vercel_token");
98448
+ const licenseKey = getSetting("license_key");
98449
+ const apiBaseUrl = process.env.SPROUTKIT_API_BASE_URL ?? "https://sproutkit.app";
98450
+ if (!vercelToken) throw new Error("Vercel token not set");
98451
+ emit2("cms:progress", { leadId, msg: "Starting\u2026" });
98452
+ const result = await provisionCmsForLead({
98453
+ lead,
98454
+ placesKey,
98455
+ vercelToken,
98456
+ apiBaseUrl,
98457
+ licenseKey,
98458
+ onProgress: (msg) => emit2("cms:progress", { leadId, msg })
98459
+ });
98460
+ db2.run("UPDATE leads SET site_url = ?, status = 'won' WHERE id = ?", [
98461
+ result.publicUrl,
98462
+ leadId
98463
+ ]);
98464
+ emit2("cms:done", { leadId, ok: true, ...result });
98465
+ return result;
98466
+ }
98467
+ if (channel === "bookings:provision") {
98468
+ const p = payload;
98469
+ const db2 = getDb();
98470
+ const lead = db2.get("SELECT * FROM leads WHERE id = ?", [p.leadId]);
98471
+ if (!lead) throw new Error("Lead not found");
98472
+ const anthropicKey = getSetting("anthropic_key");
98473
+ const vercelToken = getSetting("vercel_token");
98474
+ const resendKey = getSetting("resend_key");
98475
+ if (!anthropicKey) throw new Error("Anthropic API key not set");
98476
+ if (!vercelToken) throw new Error("Vercel token not set");
98477
+ const blobToken = await requireBlobToken(vercelToken, emit2, p.leadId);
98478
+ emit2("bookings:progress", { leadId: p.leadId, msg: "Designing the booking form\u2026" });
98479
+ const leadInfo = lead;
98480
+ const config = await generatePreset({
98481
+ anthropicKey,
98482
+ lead: { name: leadInfo.name, type: leadInfo.type, city: leadInfo.city },
98483
+ instructions: p.instructions,
98484
+ archetypeOverride: p.archetypeOverride
98485
+ });
98486
+ const result = await deployBookingOnly({
98487
+ lead,
98488
+ vercelToken,
98489
+ blobToken,
98490
+ resendKey: resendKey || "",
98491
+ ownerEmail: p.ownerEmail ?? "",
98492
+ bookingConfig: config,
98493
+ anthropicKey,
98494
+ onProgress: (msg) => emit2("bookings:progress", { leadId: p.leadId, msg })
98495
+ });
98496
+ db2.run(
98497
+ "UPDATE leads SET booking_url = ?, owner_email = ?, booking_archetype = ?, booking_config = ? WHERE id = ?",
98498
+ [
98499
+ result.bookingUrl,
98500
+ p.ownerEmail ?? "",
98501
+ config.archetype,
98502
+ JSON.stringify(config),
98503
+ p.leadId
98504
+ ]
98505
+ );
98506
+ emit2("bookings:done", { leadId: p.leadId, ...result });
98507
+ return result;
98508
+ }
98509
+ if (channel === "bookings:provision-standalone") {
98510
+ const p = payload;
98511
+ const db2 = getDb();
98512
+ const lead = db2.get("SELECT * FROM leads WHERE id = ?", [p.leadId]);
98513
+ if (!lead) throw new Error("Lead not found");
98514
+ const vercelToken = getSetting("vercel_token");
98515
+ const resendKey = getSetting("resend_key");
98516
+ if (!vercelToken) throw new Error("Vercel token not set");
98517
+ const blobToken = await requireBlobToken(vercelToken, emit2, p.leadId);
98518
+ emit2("bookings:progress", { leadId: p.leadId, msg: "Designing the booking form\u2026" });
98519
+ const leadInfo = lead;
98520
+ const config = await generatePreset({
98521
+ anthropicKey: getSetting("anthropic_key") ?? "",
98522
+ lead: { name: leadInfo.name, type: leadInfo.type, city: leadInfo.city },
98523
+ instructions: p.instructions,
98524
+ archetypeOverride: p.archetypeOverride
98525
+ });
98526
+ const result = await deployStandaloneBooking({
98527
+ lead,
98528
+ vercelToken,
98529
+ blobToken,
98530
+ resendKey: resendKey || "",
98531
+ ownerEmail: p.ownerEmail ?? "",
98532
+ bookingConfig: config,
98533
+ reuseAdminPassword: lead.booking_admin_password ?? null,
98534
+ onProgress: (msg) => emit2("bookings:progress", { leadId: p.leadId, msg })
98535
+ });
98536
+ db2.run(
98537
+ `UPDATE leads SET booking_url = ?, owner_email = ?, booking_archetype = ?, booking_config = ?,
98538
+ booking_admin_url = ?, booking_admin_password = ?,
98539
+ status = CASE WHEN status = 'discovered' THEN 'site_built' ELSE status END
98540
+ WHERE id = ?`,
98541
+ [
98542
+ result.bookingUrl,
98543
+ p.ownerEmail ?? "",
98544
+ config.archetype,
98545
+ JSON.stringify(config),
98546
+ result.admin.url,
98547
+ result.admin.password,
98548
+ p.leadId
98549
+ ]
98550
+ );
98551
+ emit2("bookings:done", { leadId: p.leadId, ...result });
98552
+ return result;
98553
+ }
98554
+ if (channel === "site:handover") {
98555
+ const p = payload;
98556
+ const db2 = getDb();
98557
+ const lead = db2.get("SELECT * FROM leads WHERE id = ?", [p.leadId]);
98558
+ if (!lead) throw new Error("Lead not found");
98559
+ const vercelToken = getSetting("vercel_token");
98560
+ if (!vercelToken) throw new Error("Vercel token not set");
98561
+ return await handoverSite({
98562
+ lead,
98563
+ vercelToken,
98564
+ blobToken: getSetting("blob_token"),
98565
+ domain: p.domain ?? null,
98566
+ onProgress: (msg) => emit2("sites:progress", { leadId: p.leadId, msg })
98567
+ });
98568
+ }
98569
+ throw new Error(`channel not supported in CLI: ${channel}`);
98570
+ };
98571
+ }
98572
+ var init_invoke_ipc = __esm({
98573
+ "src/invoke-ipc.ts"() {
98574
+ "use strict";
98575
+ init_engine();
98576
+ init_config();
97574
98577
  }
97575
- render() {
97576
- const spin = c.cyan(FRAMES[this.frame]);
97577
- const stage = this.stage ? c.dim(` \xB7 ${this.stage}`) : "";
97578
- process.stdout.write(`\r\x1B[2K ${spin} ${this.label}${stage}`);
98578
+ });
98579
+
98580
+ // src/mcp.ts
98581
+ var mcp_exports = {};
98582
+ __export(mcp_exports, {
98583
+ runMcpServer: () => runMcpServer
98584
+ });
98585
+ function send(msg) {
98586
+ process.stdout.write(JSON.stringify(msg) + "\n");
98587
+ }
98588
+ function reply(id, result) {
98589
+ if (id === void 0 || id === null) return;
98590
+ send({ jsonrpc: "2.0", id, result });
98591
+ }
98592
+ function replyError(id, code, message) {
98593
+ if (id === void 0 || id === null) return;
98594
+ send({ jsonrpc: "2.0", id, error: { code, message } });
98595
+ }
98596
+ async function handle(req) {
98597
+ switch (req.method) {
98598
+ case "initialize":
98599
+ reply(req.id, {
98600
+ protocolVersion: PROTOCOL_VERSION,
98601
+ capabilities: { tools: {} },
98602
+ serverInfo: { name: "sproutkit", version: "1.0.0" }
98603
+ });
98604
+ return;
98605
+ case "notifications/initialized":
98606
+ return;
98607
+ case "tools/list":
98608
+ reply(req.id, {
98609
+ tools: TOOLS.map((t) => ({
98610
+ name: t.name,
98611
+ description: t.description,
98612
+ inputSchema: t.input_schema
98613
+ }))
98614
+ });
98615
+ return;
98616
+ case "tools/call": {
98617
+ const name = String(req.params?.name ?? "");
98618
+ const args = req.params?.arguments ?? {};
98619
+ if (!TOOLS.some((t) => t.name === name)) {
98620
+ replyError(req.id, -32602, `Unknown tool: ${name}`);
98621
+ return;
98622
+ }
98623
+ try {
98624
+ const out = await runTool(name, args, {
98625
+ invokeIpc: makeInvokeIpc(emit),
98626
+ emitProgress: (e) => emit(e.channel, e.payload)
98627
+ });
98628
+ reply(req.id, {
98629
+ content: [{ type: "text", text: JSON.stringify(out ?? { ok: true }, null, 2) }]
98630
+ });
98631
+ } catch (e) {
98632
+ reply(req.id, {
98633
+ content: [{ type: "text", text: `Error: ${e.message}` }],
98634
+ isError: true
98635
+ });
98636
+ }
98637
+ return;
98638
+ }
98639
+ default:
98640
+ replyError(req.id, -32601, `Method not found: ${req.method}`);
97579
98641
  }
97580
- // Stop with a terminal state, leaving one clean line behind.
97581
- done(ok, finalLabel) {
97582
- if (this.timer) {
97583
- clearInterval(this.timer);
97584
- this.timer = null;
98642
+ }
98643
+ async function runMcpServer() {
98644
+ process.stdin.setEncoding("utf-8");
98645
+ let buffer = "";
98646
+ process.stdin.on("data", (chunk) => {
98647
+ buffer += chunk;
98648
+ let idx;
98649
+ while ((idx = buffer.indexOf("\n")) !== -1) {
98650
+ const line = buffer.slice(0, idx).trim();
98651
+ buffer = buffer.slice(idx + 1);
98652
+ if (!line) continue;
98653
+ let req;
98654
+ try {
98655
+ req = JSON.parse(line);
98656
+ } catch {
98657
+ process.stderr.write(`[sproutkit-mcp] bad JSON: ${line.slice(0, 200)}
98658
+ `);
98659
+ continue;
98660
+ }
98661
+ void handle(req).catch((e) => {
98662
+ process.stderr.write(`[sproutkit-mcp] handler failed: ${e.message}
98663
+ `);
98664
+ replyError(req.id, -32603, e.message);
98665
+ });
97585
98666
  }
97586
- const label = finalLabel ?? this.label;
97587
- const mark2 = ok ? c.green("\u2713") : c.red("\u2717");
97588
- if (this.tty) process.stdout.write(`\r\x1B[2K ${mark2} ${label}
98667
+ });
98668
+ await new Promise((resolve) => process.stdin.on("end", resolve));
98669
+ }
98670
+ var PROTOCOL_VERSION, emit;
98671
+ var init_mcp = __esm({
98672
+ "src/mcp.ts"() {
98673
+ "use strict";
98674
+ init_engine();
98675
+ init_invoke_ipc();
98676
+ PROTOCOL_VERSION = "2024-11-05";
98677
+ emit = (channel, payload) => {
98678
+ process.stderr.write(`[sproutkit-mcp] ${channel} ${JSON.stringify(payload)}
97589
98679
  `);
97590
- else say(` ${mark2} ${label}`);
98680
+ };
97591
98681
  }
97592
- };
97593
- var STATE_MARK = {
97594
- queued: () => c.dim("\xB7"),
97595
- building: () => c.cyan(""),
97596
- // spinner frame filled in at render time
97597
- built: () => c.green("\u2713"),
97598
- failed: () => c.red("\u2717"),
97599
- skipped: () => c.dim("\u2013")
97600
- };
97601
- var StatusBoard = class {
97602
- rows = [];
97603
- frame = 0;
97604
- timer = null;
97605
- lastLineCount = 0;
97606
- tty = process.stdout.isTTY && useColor;
97607
- // Non-TTY: remember the last printed signature per row so we only log changes.
97608
- lastSig = /* @__PURE__ */ new Map();
97609
- header = "";
97610
- start(header, rows) {
97611
- this.header = header;
97612
- this.rows = rows;
97613
- if (!this.tty) {
97614
- say(c.cyan(` \u27F3 ${header}`));
97615
- this.rows.forEach((r) => this.logRowIfChanged(r));
97616
- return;
98682
+ });
98683
+
98684
+ // src/index.ts
98685
+ init_engine();
98686
+
98687
+ // src/onboarding.ts
98688
+ init_config();
98689
+
98690
+ // src/license.ts
98691
+ var import_os = __toESM(require("os"));
98692
+ var import_crypto = require("crypto");
98693
+ init_config();
98694
+ var GRACE_MS = 7 * 24 * 60 * 60 * 1e3;
98695
+ function apiBase() {
98696
+ return process.env.SPROUTKIT_API_BASE_URL ?? "https://sproutkit.app";
98697
+ }
98698
+ function deviceId() {
98699
+ const macs = Object.values(import_os.default.networkInterfaces()).flat().filter((i) => !!i && !i.internal).map((i) => i.mac).filter((m) => m && m !== "00:00:00:00:00:00");
98700
+ return (0, import_crypto.createHash)("sha256").update(`${macs[0] ?? "unknown"}|${import_os.default.hostname()}`).digest("hex").slice(0, 32);
98701
+ }
98702
+ async function callValidate(key) {
98703
+ try {
98704
+ const r = await fetch(`${apiBase()}/api/licenses/validate`, {
98705
+ method: "POST",
98706
+ headers: { "Content-Type": "application/json" },
98707
+ body: JSON.stringify({ key, deviceId: deviceId() }),
98708
+ signal: AbortSignal.timeout(8e3)
98709
+ });
98710
+ if (!r.ok) return null;
98711
+ return await r.json();
98712
+ } catch {
98713
+ return null;
98714
+ }
98715
+ }
98716
+ async function activate(key) {
98717
+ const trimmed = key.trim();
98718
+ const result = await callValidate(trimmed);
98719
+ if (!result) return { status: "missing" };
98720
+ if (!result.valid) {
98721
+ return {
98722
+ status: result.reason === "revoked" ? "revoked" : result.reason === "refunded" ? "refunded" : result.reason === "expired" ? "expired" : "missing",
98723
+ email: result.email
98724
+ };
98725
+ }
98726
+ setSetting("license_key", trimmed);
98727
+ setSetting("license_email", result.email ?? "");
98728
+ setSetting("license_validated_at", String(Date.now()));
98729
+ return { status: "active", email: result.email };
98730
+ }
98731
+ async function check() {
98732
+ const key = getSetting("license_key");
98733
+ if (!key) return { status: "missing" };
98734
+ const result = await callValidate(key);
98735
+ if (result) {
98736
+ if (result.valid) {
98737
+ setSetting("license_validated_at", String(Date.now()));
98738
+ return { status: "active", email: result.email };
97617
98739
  }
97618
- say(c.cyan(` \u27F3 ${header}`));
97619
- this.render();
97620
- this.timer = setInterval(() => {
97621
- this.frame = (this.frame + 1) % FRAMES.length;
97622
- this.render();
97623
- }, 90);
97624
- }
97625
- update(row) {
97626
- const i = this.rows.findIndex((r) => r.key === row.key);
97627
- if (i >= 0) this.rows[i] = row;
97628
- else this.rows.push(row);
97629
- if (!this.tty) this.logRowIfChanged(row);
97630
- else this.render();
97631
- }
97632
- // Replace the whole set at once (e.g. from a job snapshot).
97633
- set(rows) {
97634
- this.rows = rows;
97635
- if (!this.tty) rows.forEach((r) => this.logRowIfChanged(r));
97636
- else this.render();
97637
- }
97638
- line(r) {
97639
- const mark2 = r.state === "building" ? c.cyan(FRAMES[this.frame]) : STATE_MARK[r.state]();
97640
- const detail = r.detail ? c.dim(` \xB7 ${r.detail}`) : "";
97641
- return ` ${mark2} ${r.label}${detail}`;
97642
- }
97643
- logRowIfChanged(r) {
97644
- const sig = `${r.state}:${r.detail ?? ""}`;
97645
- if (this.lastSig.get(r.key) === sig) return;
97646
- this.lastSig.set(r.key, sig);
97647
- const mark2 = r.state === "built" ? "\u2713" : r.state === "failed" ? "\u2717" : r.state === "skipped" ? "\u2013" : "\u2026";
97648
- say(` ${mark2} ${r.label}${r.detail ? ` \xB7 ${r.detail}` : ""}`);
97649
- }
97650
- render() {
97651
- if (this.lastLineCount > 0) process.stdout.write(`\x1B[${this.lastLineCount}A`);
97652
- const out = this.rows.map((r) => `\x1B[2K${this.line(r)}`).join("\n");
97653
- process.stdout.write(out + "\n");
97654
- this.lastLineCount = this.rows.length;
97655
- }
97656
- stop() {
97657
- if (this.timer) {
97658
- clearInterval(this.timer);
97659
- this.timer = null;
97660
- }
97661
- if (this.tty) this.render();
98740
+ return {
98741
+ status: result.reason === "revoked" ? "revoked" : result.reason === "refunded" ? "refunded" : result.reason === "expired" ? "expired" : "missing",
98742
+ email: result.email
98743
+ };
97662
98744
  }
97663
- };
98745
+ const last = Number(getSetting("license_validated_at") ?? 0);
98746
+ if (Date.now() - last < GRACE_MS) return { status: "active" };
98747
+ return { status: "offline_grace_expired" };
98748
+ }
98749
+
98750
+ // src/onboarding.ts
98751
+ init_engine();
98752
+ init_tty();
97664
98753
 
97665
98754
  // src/skill-install.ts
97666
98755
  var import_fs = __toESM(require("fs"));
@@ -97738,11 +98827,12 @@ function installSkill() {
97738
98827
  }
97739
98828
 
97740
98829
  // src/onboarding.ts
97741
- var import_child_process = require("child_process");
98830
+ init_desktop_install();
98831
+ var import_child_process2 = require("child_process");
97742
98832
  function claudeCliPresent() {
97743
98833
  try {
97744
98834
  const bin = process.env.SPROUTKIT_CLAUDE_BIN || "claude";
97745
- const r = (0, import_child_process.spawnSync)(bin, ["--version"], { stdio: "ignore", timeout: 1e4 });
98835
+ const r = (0, import_child_process2.spawnSync)(bin, ["--version"], { stdio: "ignore", timeout: 1e4 });
97746
98836
  return r.status === 0;
97747
98837
  } catch {
97748
98838
  return false;
@@ -97824,6 +98914,26 @@ async function offerSkillInstall(force) {
97824
98914
  }
97825
98915
  say();
97826
98916
  }
98917
+ async function offerDesktopInstall() {
98918
+ if (!desktopSupported() || desktopInstallPath()) return;
98919
+ say(` ${c.bold("Desktop app")}`);
98920
+ say(c.dim(" The map, pipeline board and Workflows live in the desktop app, and your"));
98921
+ say(c.dim(" terminal leads sync into it. It's about a 100 MB download."));
98922
+ const ans = (await ask(c.green(" Install it now? [Y/n] "))).trim().toLowerCase();
98923
+ if (ans === "n" || ans === "no") {
98924
+ say(c.dim(" Skipped \u2014 install it any time with 'sproutkit desktop'."));
98925
+ say();
98926
+ return;
98927
+ }
98928
+ const res = await installDesktop();
98929
+ if (res.ok) {
98930
+ say(c.green(` \u2713 Sproutkit ${res.version} installed to ${res.path}`));
98931
+ } else {
98932
+ say(c.amber(` Couldn't install the desktop app: ${res.error}`));
98933
+ say(c.dim(" Try again with 'sproutkit desktop', or download it from https://sproutkit.app/install"));
98934
+ }
98935
+ say();
98936
+ }
97827
98937
  async function onboardLicense(force) {
97828
98938
  if (!force) {
97829
98939
  const state = await check();
@@ -97845,7 +98955,7 @@ async function onboardLicense(force) {
97845
98955
  say();
97846
98956
  return true;
97847
98957
  }
97848
- const msg = state.status === "revoked" ? "That licence has been revoked." : state.status === "refunded" ? "That licence was refunded." : "Couldn't validate that key (check it, or your connection).";
98958
+ const msg = state.status === "revoked" ? "That licence has been revoked." : state.status === "refunded" ? "That licence was refunded." : state.status === "expired" ? "That subscription has ended \u2014 renew at https://sproutkit.app/#pricing." : "Couldn't validate that key (check it, or your connection).";
97849
98959
  say(c.red(` ${msg} Try again, or press Enter to quit.`));
97850
98960
  }
97851
98961
  }
@@ -97881,6 +98991,7 @@ async function runOnboarding(force = false) {
97881
98991
  } catch {
97882
98992
  }
97883
98993
  }
98994
+ await offerDesktopInstall();
97884
98995
  const stillMissing = missingRequiredKeys();
97885
98996
  if (stillMissing.length > 0) {
97886
98997
  say(c.amber(` Still missing: ${stillMissing.map((m) => m.label).join(", ")}.`));
@@ -97894,9 +99005,11 @@ async function runOnboarding(force = false) {
97894
99005
  }
97895
99006
 
97896
99007
  // src/update.ts
97897
- var import_child_process2 = require("child_process");
99008
+ var import_child_process3 = require("child_process");
97898
99009
  var import_util = require("util");
97899
- var execFileP = (0, import_util.promisify)(import_child_process2.execFile);
99010
+ init_config();
99011
+ init_tty();
99012
+ var execFileP = (0, import_util.promisify)(import_child_process3.execFile);
97900
99013
  var PKG = "sproutkit-code";
97901
99014
  var CURRENT_VERSION = require_package2().version;
97902
99015
  async function latestOnRegistry() {
@@ -97959,10 +99072,12 @@ async function notifyIfOutdated() {
97959
99072
  }
97960
99073
 
97961
99074
  // src/sync.ts
97962
- var import_os3 = __toESM(require("os"));
97963
- var import_path2 = __toESM(require("path"));
97964
- var import_fs2 = __toESM(require("fs"));
97965
- var COLS = [
99075
+ var import_os4 = __toESM(require("os"));
99076
+ var import_path3 = __toESM(require("path"));
99077
+ var import_fs3 = __toESM(require("fs"));
99078
+ init_engine();
99079
+ init_tty();
99080
+ var WANTED_COLS = [
97966
99081
  "place_id",
97967
99082
  "name",
97968
99083
  "type",
@@ -97981,50 +99096,79 @@ var COLS = [
97981
99096
  "notes",
97982
99097
  "score",
97983
99098
  "price_level",
97984
- "business_status"
99099
+ "business_status",
99100
+ // Deliverables and stage timestamps — without these the desktop board can't
99101
+ // show booking pages, attached domains, delivered status or stalled leads.
99102
+ "booking_url",
99103
+ "custom_domain",
99104
+ "handed_over_at",
99105
+ "replied_at",
99106
+ "dead_at",
99107
+ "email"
99108
+ ];
99109
+ var UPDATE_COLS = [
99110
+ "status",
99111
+ "site_url",
99112
+ "site_built_at",
99113
+ "score",
99114
+ "booking_url",
99115
+ "custom_domain",
99116
+ "handed_over_at",
99117
+ "replied_at",
99118
+ "dead_at",
99119
+ "email"
97985
99120
  ];
99121
+ function columnsOf(db2, table) {
99122
+ const rows = db2.all(`PRAGMA table_info(${table})`);
99123
+ return new Set(rows.map((r) => r.name));
99124
+ }
97986
99125
  function desktopDbPath() {
97987
- const home = import_os3.default.homedir();
99126
+ const home = import_os4.default.homedir();
97988
99127
  const candidates = [];
97989
99128
  if (process.platform === "darwin") {
97990
- candidates.push(import_path2.default.join(home, "Library", "Application Support", "Sproutkit", "data", "sproutio.db"));
99129
+ candidates.push(import_path3.default.join(home, "Library", "Application Support", "Sproutkit", "data", "sproutio.db"));
97991
99130
  } else if (process.platform === "win32") {
97992
- const appData = process.env.APPDATA || import_path2.default.join(home, "AppData", "Roaming");
97993
- candidates.push(import_path2.default.join(appData, "Sproutkit", "data", "sproutio.db"));
99131
+ const appData = process.env.APPDATA || import_path3.default.join(home, "AppData", "Roaming");
99132
+ candidates.push(import_path3.default.join(appData, "Sproutkit", "data", "sproutio.db"));
97994
99133
  } else {
97995
- const cfg = process.env.XDG_CONFIG_HOME || import_path2.default.join(home, ".config");
97996
- candidates.push(import_path2.default.join(cfg, "Sproutkit", "data", "sproutio.db"));
99134
+ const cfg = process.env.XDG_CONFIG_HOME || import_path3.default.join(home, ".config");
99135
+ candidates.push(import_path3.default.join(cfg, "Sproutkit", "data", "sproutio.db"));
97997
99136
  }
97998
- return candidates.find((p) => import_fs2.default.existsSync(p)) ?? null;
99137
+ return candidates.find((p) => import_fs3.default.existsSync(p)) ?? null;
97999
99138
  }
98000
99139
  function syncToDesktop(opts = {}) {
98001
99140
  const dest = desktopDbPath();
98002
99141
  if (!dest) return { ok: false, reason: "no_desktop_db", inserted: 0, updated: 0 };
98003
- const cliLeads = getDb().all(
98004
- `SELECT ${COLS.join(", ")} FROM leads WHERE place_id IS NOT NULL AND place_id != ''`,
98005
- []
98006
- );
98007
- if (cliLeads.length === 0) return { ok: true, inserted: 0, updated: 0 };
98008
99142
  const { Database } = require("node-sqlite3-wasm");
98009
99143
  const ddb = new Database(dest);
98010
99144
  let inserted = 0;
98011
99145
  let updated = 0;
98012
99146
  try {
99147
+ const shared = new Set(
99148
+ [...columnsOf(getDb(), "leads")].filter((n) => columnsOf(ddb, "leads").has(n))
99149
+ );
99150
+ const cols = WANTED_COLS.filter((c2) => shared.has(c2));
99151
+ const updateCols = UPDATE_COLS.filter((c2) => shared.has(c2));
99152
+ const cliLeads = getDb().all(
99153
+ `SELECT ${cols.join(", ")} FROM leads WHERE place_id IS NOT NULL AND place_id != ''`,
99154
+ []
99155
+ );
99156
+ if (cliLeads.length === 0) return { ok: true, inserted: 0, updated: 0 };
98013
99157
  for (const lead of cliLeads) {
98014
99158
  const existing = ddb.get("SELECT id FROM leads WHERE place_id = ?", [lead.place_id]);
98015
99159
  if (existing) {
98016
99160
  ddb.run(
98017
- "UPDATE leads SET status = ?, site_url = ?, site_built_at = ?, score = ? WHERE place_id = ?",
98018
- [lead.status, lead.site_url, lead.site_built_at, lead.score, lead.place_id].map(
99161
+ `UPDATE leads SET ${updateCols.map((k) => `${k} = ?`).join(", ")} WHERE place_id = ?`,
99162
+ [...updateCols.map((k) => lead[k]), lead.place_id].map(
98019
99163
  (v) => v ?? null
98020
99164
  )
98021
99165
  );
98022
99166
  updated++;
98023
99167
  } else {
98024
- const placeholders = COLS.map(() => "?").join(", ");
99168
+ const placeholders = cols.map(() => "?").join(", ");
98025
99169
  ddb.run(
98026
- `INSERT INTO leads (${COLS.join(", ")}) VALUES (${placeholders})`,
98027
- COLS.map((k) => lead[k] ?? null)
99170
+ `INSERT INTO leads (${cols.join(", ")}) VALUES (${placeholders})`,
99171
+ cols.map((k) => lead[k] ?? null)
98028
99172
  );
98029
99173
  inserted++;
98030
99174
  }
@@ -98050,6 +99194,9 @@ function autoSync() {
98050
99194
  }
98051
99195
 
98052
99196
  // src/models-cmd.ts
99197
+ init_engine();
99198
+ init_config();
99199
+ init_tty();
98053
99200
  async function runModelsClaudeCode() {
98054
99201
  const current = getClaudeCodeModel();
98055
99202
  say();
@@ -98122,187 +99269,21 @@ async function runModels() {
98122
99269
  say(c.green(` \u2713 Now using ${models2[idx].display_name ?? models2[idx].id}.`));
98123
99270
  }
98124
99271
 
98125
- // src/invoke-ipc.ts
98126
- async function requireBlobToken(vercelToken, emit, leadId) {
98127
- const existing = getSetting("blob_token");
98128
- if (existing) return existing;
98129
- emit("bookings:progress", { leadId, msg: "No Blob token yet \u2014 creating a Blob store for you\u2026" });
98130
- const created = await createBlobStore({ vercelToken, name: "sproutkit" });
98131
- if (created.ok) {
98132
- throw new Error(
98133
- "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."
98134
- );
98135
- }
98136
- if (created.alreadyExists) {
98137
- throw new Error(
98138
- "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)."
98139
- );
98140
- }
98141
- throw new Error(
98142
- `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'.`
98143
- );
98144
- }
98145
- function makeInvokeIpc(emit) {
98146
- return async (channel, payload) => {
98147
- if (channel === "auto:build:start") {
98148
- const args = payload;
98149
- const anthropicKey = getSetting("anthropic_key");
98150
- const vercelToken = getSetting("vercel_token");
98151
- const placesKey = getSetting("places_key");
98152
- if (!anthropicKey) throw new Error("Anthropic API key not set");
98153
- if (!vercelToken) throw new Error("Vercel token not set");
98154
- return startAutoJob({
98155
- leadIds: args.leadIds,
98156
- concurrency: args.concurrency,
98157
- anthropicKey,
98158
- vercelToken,
98159
- placesKey,
98160
- emit
98161
- });
98162
- }
98163
- if (channel === "cms:provision") {
98164
- const leadId = Number(payload);
98165
- const db2 = getDb();
98166
- const lead = db2.get("SELECT * FROM leads WHERE id = ?", [leadId]);
98167
- if (!lead) throw new Error("Lead not found");
98168
- const placesKey = getSetting("places_key");
98169
- const vercelToken = getSetting("vercel_token");
98170
- const licenseKey = getSetting("license_key");
98171
- const apiBaseUrl = process.env.SPROUTKIT_API_BASE_URL ?? "https://sproutkit.app";
98172
- if (!vercelToken) throw new Error("Vercel token not set");
98173
- emit("cms:progress", { leadId, msg: "Starting\u2026" });
98174
- const result = await provisionCmsForLead({
98175
- lead,
98176
- placesKey,
98177
- vercelToken,
98178
- apiBaseUrl,
98179
- licenseKey,
98180
- onProgress: (msg) => emit("cms:progress", { leadId, msg })
98181
- });
98182
- db2.run("UPDATE leads SET site_url = ?, status = 'won' WHERE id = ?", [
98183
- result.publicUrl,
98184
- leadId
98185
- ]);
98186
- emit("cms:done", { leadId, ok: true, ...result });
98187
- return result;
98188
- }
98189
- if (channel === "bookings:provision") {
98190
- const p = payload;
98191
- const db2 = getDb();
98192
- const lead = db2.get("SELECT * FROM leads WHERE id = ?", [p.leadId]);
98193
- if (!lead) throw new Error("Lead not found");
98194
- const anthropicKey = getSetting("anthropic_key");
98195
- const vercelToken = getSetting("vercel_token");
98196
- const resendKey = getSetting("resend_key");
98197
- if (!anthropicKey) throw new Error("Anthropic API key not set");
98198
- if (!vercelToken) throw new Error("Vercel token not set");
98199
- const blobToken = await requireBlobToken(vercelToken, emit, p.leadId);
98200
- emit("bookings:progress", { leadId: p.leadId, msg: "Designing the booking form\u2026" });
98201
- const leadInfo = lead;
98202
- const config = await generatePreset({
98203
- anthropicKey,
98204
- lead: { name: leadInfo.name, type: leadInfo.type, city: leadInfo.city },
98205
- instructions: p.instructions,
98206
- archetypeOverride: p.archetypeOverride
98207
- });
98208
- const result = await deployBookingOnly({
98209
- lead,
98210
- vercelToken,
98211
- blobToken,
98212
- resendKey: resendKey || "",
98213
- ownerEmail: p.ownerEmail ?? "",
98214
- bookingConfig: config,
98215
- anthropicKey,
98216
- onProgress: (msg) => emit("bookings:progress", { leadId: p.leadId, msg })
98217
- });
98218
- db2.run(
98219
- "UPDATE leads SET booking_url = ?, owner_email = ?, booking_archetype = ?, booking_config = ? WHERE id = ?",
98220
- [
98221
- result.bookingUrl,
98222
- p.ownerEmail ?? "",
98223
- config.archetype,
98224
- JSON.stringify(config),
98225
- p.leadId
98226
- ]
98227
- );
98228
- emit("bookings:done", { leadId: p.leadId, ...result });
98229
- return result;
98230
- }
98231
- if (channel === "bookings:provision-standalone") {
98232
- const p = payload;
98233
- const db2 = getDb();
98234
- const lead = db2.get("SELECT * FROM leads WHERE id = ?", [p.leadId]);
98235
- if (!lead) throw new Error("Lead not found");
98236
- const vercelToken = getSetting("vercel_token");
98237
- const resendKey = getSetting("resend_key");
98238
- if (!vercelToken) throw new Error("Vercel token not set");
98239
- const blobToken = await requireBlobToken(vercelToken, emit, p.leadId);
98240
- emit("bookings:progress", { leadId: p.leadId, msg: "Designing the booking form\u2026" });
98241
- const leadInfo = lead;
98242
- const config = await generatePreset({
98243
- anthropicKey: getSetting("anthropic_key") ?? "",
98244
- lead: { name: leadInfo.name, type: leadInfo.type, city: leadInfo.city },
98245
- instructions: p.instructions,
98246
- archetypeOverride: p.archetypeOverride
98247
- });
98248
- const result = await deployStandaloneBooking({
98249
- lead,
98250
- vercelToken,
98251
- blobToken,
98252
- resendKey: resendKey || "",
98253
- ownerEmail: p.ownerEmail ?? "",
98254
- bookingConfig: config,
98255
- reuseAdminPassword: lead.booking_admin_password ?? null,
98256
- onProgress: (msg) => emit("bookings:progress", { leadId: p.leadId, msg })
98257
- });
98258
- db2.run(
98259
- `UPDATE leads SET booking_url = ?, owner_email = ?, booking_archetype = ?, booking_config = ?,
98260
- booking_admin_url = ?, booking_admin_password = ?,
98261
- status = CASE WHEN status = 'discovered' THEN 'site_built' ELSE status END
98262
- WHERE id = ?`,
98263
- [
98264
- result.bookingUrl,
98265
- p.ownerEmail ?? "",
98266
- config.archetype,
98267
- JSON.stringify(config),
98268
- result.admin.url,
98269
- result.admin.password,
98270
- p.leadId
98271
- ]
98272
- );
98273
- emit("bookings:done", { leadId: p.leadId, ...result });
98274
- return result;
98275
- }
98276
- if (channel === "site:handover") {
98277
- const p = payload;
98278
- const db2 = getDb();
98279
- const lead = db2.get("SELECT * FROM leads WHERE id = ?", [p.leadId]);
98280
- if (!lead) throw new Error("Lead not found");
98281
- const vercelToken = getSetting("vercel_token");
98282
- if (!vercelToken) throw new Error("Vercel token not set");
98283
- return await handoverSite({
98284
- lead,
98285
- vercelToken,
98286
- blobToken: getSetting("blob_token"),
98287
- domain: p.domain ?? null,
98288
- onProgress: (msg) => emit("sites:progress", { leadId: p.leadId, msg })
98289
- });
98290
- }
98291
- throw new Error(`channel not supported in CLI: ${channel}`);
98292
- };
98293
- }
98294
-
98295
99272
  // src/commands.ts
99273
+ init_engine();
99274
+ init_invoke_ipc();
99275
+ init_config();
99276
+ init_tty();
98296
99277
  function makeCtx(opts) {
98297
- const emit = (channel, payload) => {
99278
+ const emit2 = (channel, payload) => {
98298
99279
  opts.onEvent?.(channel, payload);
98299
99280
  if (opts.json) return;
98300
99281
  const p = payload;
98301
99282
  if (p?.msg && opts.status) opts.status.update(p.msg);
98302
99283
  };
98303
99284
  return {
98304
- invokeIpc: makeInvokeIpc(emit),
98305
- emitProgress: (e) => emit(e.channel, e.payload)
99285
+ invokeIpc: makeInvokeIpc(emit2),
99286
+ emitProgress: (e) => emit2(e.channel, e.payload)
98306
99287
  };
98307
99288
  }
98308
99289
  function parseArgs(argv) {
@@ -99146,6 +100127,135 @@ async function cmdReport(argv) {
99146
100127
  process.exitCode = 1;
99147
100128
  }
99148
100129
  }
100130
+ async function cmdAudit(argv) {
100131
+ const { flags, rest } = parseArgs(argv);
100132
+ const json = !!flags.json;
100133
+ const SEV_COLOUR = {
100134
+ critical: c.red,
100135
+ major: c.amber,
100136
+ minor: c.dim
100137
+ };
100138
+ const asId = Number(flags.id ?? rest[0]);
100139
+ const singleUrl = flags.url ? String(flags.url) : void 0;
100140
+ const looksLikeUrl = singleUrl || rest[0] && /\./.test(String(rest[0])) && !flags.categories;
100141
+ if (singleUrl || !Number.isNaN(asId) && rest.length === 1 && !flags.categories || looksLikeUrl) {
100142
+ const status2 = json ? void 0 : new Status();
100143
+ const target = singleUrl ?? (Number.isNaN(asId) ? String(rest[0]) : `lead #${asId}`);
100144
+ if (status2) status2.start(`Auditing ${target}`);
100145
+ try {
100146
+ const r = await runTool(
100147
+ "audit_one_site",
100148
+ Number.isNaN(asId) || singleUrl ? { url: singleUrl ?? String(rest[0]) } : { id: asId },
100149
+ makeCtx({ json, status: status2 })
100150
+ );
100151
+ if (r && r.ok === false) {
100152
+ if (status2) status2.done(false, r.error ?? "Couldn't audit it");
100153
+ if (json) emitJson(r);
100154
+ process.exitCode = 1;
100155
+ return;
100156
+ }
100157
+ if (status2) {
100158
+ status2.done(
100159
+ true,
100160
+ r.issues && r.issues.length ? `${r.issues.length} issue${r.issues.length === 1 ? "" : "s"} \xB7 score ${r.score}` : "No problems found"
100161
+ );
100162
+ }
100163
+ if (json) {
100164
+ emitJson(r);
100165
+ return;
100166
+ }
100167
+ say();
100168
+ say(` ${c.bold(String(r.url))}` + c.dim(` \xB7 score ${r.score}/100`));
100169
+ if (!r.issues?.length) {
100170
+ say(c.dim(" Nothing worth pitching against \u2014 this site is in decent shape."));
100171
+ say();
100172
+ return;
100173
+ }
100174
+ say();
100175
+ for (const i of r.issues) {
100176
+ const tint = SEV_COLOUR[i.severity] ?? c.dim;
100177
+ say(` ${tint("\u25CF")} ${i.label}${i.detail ? c.dim(` (${i.detail})`) : ""}`);
100178
+ }
100179
+ say();
100180
+ say(
100181
+ r.worthRebuilding ? c.green(" Worth approaching \u2014 these are concrete problems you can name.") : c.dim(" Minor only. Probably not worth a rebuild pitch.")
100182
+ );
100183
+ say();
100184
+ } catch (e) {
100185
+ if (status2) status2.done(false, "Audit failed");
100186
+ json ? emitJson({ ok: false, error: e.message }) : say(c.red(" " + e.message));
100187
+ process.exitCode = 1;
100188
+ }
100189
+ return;
100190
+ }
100191
+ const location = rest.join(" ").trim() || (flags.location ? String(flags.location) : "");
100192
+ if (!location) {
100193
+ const err = 'Usage: sproutkit audit "<area>" --categories "barber shop,cafe"\n single site: sproutkit audit <leadId> \xB7 sproutkit audit --url thebarbers.co.uk';
100194
+ json ? emitJson({ ok: false, error: err }) : say(c.red(" " + err));
100195
+ process.exitCode = 1;
100196
+ return;
100197
+ }
100198
+ const categories = flags.categories ? String(flags.categories).split(",").map((t) => t.trim()).filter(Boolean) : [];
100199
+ if (categories.length === 0) {
100200
+ const err = 'Which trades? e.g. --categories "barber shop,cafe,plumber"';
100201
+ json ? emitJson({ ok: false, error: err }) : say(c.red(" " + err));
100202
+ process.exitCode = 1;
100203
+ return;
100204
+ }
100205
+ const status = json ? void 0 : new Status();
100206
+ if (status) status.start(`Auditing websites in ${location}`);
100207
+ try {
100208
+ const r = await runTool(
100209
+ "audit_sites",
100210
+ {
100211
+ location,
100212
+ categories,
100213
+ radiusMeters: flags.radius ? Number(flags.radius) : void 0,
100214
+ limit: flags.limit ? Number(flags.limit) : void 0
100215
+ },
100216
+ makeCtx({ json, status })
100217
+ );
100218
+ if (r && r.ok === false) {
100219
+ if (status) status.done(false, r.error ?? "Audit failed");
100220
+ if (json) emitJson(r);
100221
+ process.exitCode = 1;
100222
+ return;
100223
+ }
100224
+ if (status) status.done(true, `${r.saved} rebuild prospect${r.saved === 1 ? "" : "s"} from ${r.found} checked`);
100225
+ autoSync();
100226
+ if (json) {
100227
+ emitJson(r);
100228
+ return;
100229
+ }
100230
+ const list = r.prospects ?? [];
100231
+ if (!list.length) {
100232
+ say();
100233
+ say(c.dim(` Checked ${r.found} businesses \u2014 none had a site bad enough to pitch against.`));
100234
+ say(c.dim(" That's a good sign for the area; try a different trade or a wider radius."));
100235
+ say();
100236
+ return;
100237
+ }
100238
+ say();
100239
+ say(c.dim(" Worst first \u2014 these are the easiest conversations to start."));
100240
+ say();
100241
+ for (const p of list) {
100242
+ say(` ${c.greenBold("#" + p.id)} ${c.bold(p.business)} ${c.dim(p.website.replace(/^https?:\/\//, ""))}`);
100243
+ say(` ${c.amber("score " + p.score + "/100")}`);
100244
+ for (const issue of p.issues) {
100245
+ const [sev, ...restText] = issue.split(": ");
100246
+ const tint = SEV_COLOUR[sev] ?? c.dim;
100247
+ say(` ${tint("\u25CF")} ${restText.join(": ")}`);
100248
+ }
100249
+ say();
100250
+ }
100251
+ say(c.dim(" Build one: sproutkit build <id> \xB7 see everything: sproutkit audit <id>"));
100252
+ say();
100253
+ } catch (e) {
100254
+ if (status) status.done(false, "Audit failed");
100255
+ json ? emitJson({ ok: false, error: e.message }) : say(c.red(" " + e.message));
100256
+ process.exitCode = 1;
100257
+ }
100258
+ }
99149
100259
  async function cmdRegenerate(argv) {
99150
100260
  const { flags, rest } = parseArgs(argv);
99151
100261
  const json = !!flags.json;
@@ -99336,7 +100446,7 @@ async function cmdBuildAll(argv) {
99336
100446
  detail: l.status === "built" ? l.siteUrl : l.status === "failed" ? l.error : l.message
99337
100447
  });
99338
100448
  await new Promise((resolve) => {
99339
- const emit = (channel, payload) => {
100449
+ const emit2 = (channel, payload) => {
99340
100450
  const snap2 = payload;
99341
100451
  if (channel === "auto:progress" && board) board.set(snap2.leads.map(stateToRow));
99342
100452
  if (channel === "auto:done") {
@@ -99363,7 +100473,7 @@ async function cmdBuildAll(argv) {
99363
100473
  anthropicKey: anthropicKey ?? "",
99364
100474
  vercelToken,
99365
100475
  placesKey: getSetting("places_key"),
99366
- emit
100476
+ emit: emit2
99367
100477
  });
99368
100478
  if (board) {
99369
100479
  board.start(`Building ${leadIds.length} sites (up to ${concurrency} at once)`, snap.leads.map(stateToRow));
@@ -99408,6 +100518,8 @@ When a request is missing information the command needs, ask a SHORT, STRUCTURED
99408
100518
  COMMANDS:
99409
100519
  - sproutkit find "<area>" --categories "<type>,<type>" --json \u2014 find website-less leads
99410
100520
  - sproutkit find "<area>" --categories "\u2026" --booking --json \u2014 find businesses that HAVE a site but take bookings via WhatsApp/phone only (booking-page prospects)
100521
+ - sproutkit audit "<area>" --categories "\u2026" --json \u2014 find businesses whose EXISTING site is insecure/outdated
100522
+ - sproutkit audit <id> | --url <site> --json \u2014 audit one site, list its faults
99411
100523
  - sproutkit list [--status <s>] [--city <c>] [--limit 100] \u2014 formatted table; SHOW IT VERBATIM
99412
100524
  - sproutkit list --json \u2014 same data as JSON, for reading ids only
99413
100525
  - sproutkit build <id> --json [--instructions "..."] [--cms] [--multipage] \u2014 build a demo site (--multipage = real Home/Services/About/Contact pages, not one scroll)
@@ -99429,7 +100541,11 @@ COMMANDS:
99429
100541
  If no command fits the request, say so in one line and name the closest thing you can do. Never invent results.`;
99430
100542
 
99431
100543
  // src/doctor.ts
99432
- var import_child_process3 = require("child_process");
100544
+ init_config();
100545
+ init_engine();
100546
+ init_desktop_install();
100547
+ init_tty();
100548
+ var import_child_process4 = require("child_process");
99433
100549
  function mark(l) {
99434
100550
  return l === "ok" ? c.green("\u2713") : l === "warn" ? c.amber("!") : c.red("\u2717");
99435
100551
  }
@@ -99448,7 +100564,7 @@ async function runDoctor() {
99448
100564
  if (backend === "claude_code") {
99449
100565
  const present = (() => {
99450
100566
  try {
99451
- return (0, import_child_process3.spawnSync)(claudeBin(), ["--version"], { stdio: "ignore", timeout: 1e4 }).status === 0;
100567
+ return (0, import_child_process4.spawnSync)(claudeBin(), ["--version"], { stdio: "ignore", timeout: 1e4 }).status === 0;
99452
100568
  } catch {
99453
100569
  return false;
99454
100570
  }
@@ -99534,6 +100650,17 @@ async function runDoctor() {
99534
100650
  fix: "Run 'sproutkit setup' and say yes to installing the skill, so `claude` can drive Sproutkit."
99535
100651
  }
99536
100652
  );
100653
+ if (desktopSupported()) {
100654
+ const desktop = desktopInstallPath();
100655
+ rows.push(
100656
+ desktop ? { level: "ok", label: "Desktop app", detail: desktop } : {
100657
+ level: "warn",
100658
+ label: "Desktop app",
100659
+ detail: "not installed",
100660
+ fix: "Run 'sproutkit desktop' to install it \u2014 your terminal leads sync into its map and board."
100661
+ }
100662
+ );
100663
+ }
99537
100664
  rows.push(
99538
100665
  commandsInstalled() ? { level: "ok", label: "Slash commands", detail: commandsTargetDir() } : {
99539
100666
  level: "warn",
@@ -99560,12 +100687,13 @@ async function runDoctor() {
99560
100687
  }
99561
100688
 
99562
100689
  // src/index.ts
99563
- var import_child_process4 = require("child_process");
100690
+ init_tty();
100691
+ var import_child_process5 = require("child_process");
99564
100692
  process.env.SPROUTKIT_HEADLESS = "1";
99565
100693
  function claudePresent() {
99566
100694
  try {
99567
100695
  const bin = process.env.SPROUTKIT_CLAUDE_BIN || "claude";
99568
- return (0, import_child_process4.spawnSync)(bin, ["--version"], { stdio: "ignore", timeout: 1e4 }).status === 0;
100696
+ return (0, import_child_process5.spawnSync)(bin, ["--version"], { stdio: "ignore", timeout: 1e4 }).status === 0;
99569
100697
  } catch {
99570
100698
  return false;
99571
100699
  }
@@ -99573,6 +100701,11 @@ function claudePresent() {
99573
100701
  async function main() {
99574
100702
  const arg = process.argv[2];
99575
100703
  initDatabase();
100704
+ if (arg === "mcp") {
100705
+ const { runMcpServer: runMcpServer2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
100706
+ await runMcpServer2();
100707
+ return;
100708
+ }
99576
100709
  if (arg === "update") {
99577
100710
  await runUpdate();
99578
100711
  closeTty();
@@ -99592,11 +100725,17 @@ async function main() {
99592
100725
  const r = syncToDesktop();
99593
100726
  if (!r.ok && r.reason === "no_desktop_db") {
99594
100727
  say(c.dim(" No desktop Sproutkit database found \u2014 nothing to sync to."));
99595
- say(c.dim(" Install the desktop app and run it once to create it."));
100728
+ say(c.dim(" Install it with 'sproutkit desktop' and open it once to create it."));
99596
100729
  }
99597
100730
  closeTty();
99598
100731
  return;
99599
100732
  }
100733
+ if (arg === "desktop") {
100734
+ const { runDesktopCommand: runDesktopCommand2 } = await Promise.resolve().then(() => (init_desktop_install(), desktop_install_exports));
100735
+ await runDesktopCommand2(process.argv.slice(3));
100736
+ closeTty();
100737
+ return;
100738
+ }
99600
100739
  if (arg === "setup") {
99601
100740
  await runOnboarding(true);
99602
100741
  closeTty();
@@ -99607,7 +100746,7 @@ async function main() {
99607
100746
  closeTty();
99608
100747
  return;
99609
100748
  }
99610
- if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "regenerate" || arg === "cms" || arg === "book" || arg === "handover" || arg === "brand" || arg === "invoice" || arg === "letter" || arg === "reply" || arg === "followups" || arg === "report" || arg === "outreach" || arg === "pipeline") {
100749
+ if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "regenerate" || arg === "cms" || arg === "book" || arg === "handover" || arg === "audit" || arg === "brand" || arg === "invoice" || arg === "letter" || arg === "reply" || arg === "followups" || arg === "report" || arg === "outreach" || arg === "pipeline") {
99611
100750
  const subArgs = process.argv.slice(3);
99612
100751
  if (subArgs.includes("--help") || subArgs.includes("-h")) {
99613
100752
  const USAGE = {
@@ -99647,6 +100786,16 @@ async function main() {
99647
100786
  ' --instructions "\u2026" e.g. "45-min slots, add beard trim \xA312"',
99648
100787
  " --email <addr> owner email for booking notifications"
99649
100788
  ],
100789
+ audit: [
100790
+ 'sproutkit audit "<area>" --categories "barber shop,cafe"',
100791
+ " finds businesses that HAVE a website but whose site is costing them",
100792
+ " work \u2014 no HTTPS, unusable on phones, built in 2008 \u2014 and scores it",
100793
+ " --radius <m> search radius (default 10000)",
100794
+ " --limit <n> max prospects to save (default 25)",
100795
+ "",
100796
+ " one site: sproutkit audit <leadId>",
100797
+ " sproutkit audit --url thebarbers.co.uk"
100798
+ ],
99650
100799
  brand: [
99651
100800
  'sproutkit brand --name "Your Studio" --email you@x.com [--vat-rate 20]',
99652
100801
  " your agency identity \u2014 goes on invoices, letters, the site footer",
@@ -99709,7 +100858,7 @@ async function main() {
99709
100858
  }
99710
100859
  const lic2 = await check();
99711
100860
  if (lic2.status !== "active") {
99712
- const msg = lic2.status === "offline_grace_expired" ? "Licence check offline and grace expired \u2014 reconnect and retry." : "No active licence. Run 'sproutkit setup' first.";
100861
+ const msg = lic2.status === "expired" ? "Your Sproutkit subscription has ended. Renew at https://sproutkit.app/#pricing." : lic2.status === "offline_grace_expired" ? "Licence check offline and grace expired \u2014 reconnect and retry." : "No active licence. Run 'sproutkit setup' first.";
99713
100862
  if (subArgs.includes("--json")) process.stdout.write(JSON.stringify({ ok: false, error: msg }) + "\n");
99714
100863
  else say(c.red(` ${msg}`));
99715
100864
  closeTty();
@@ -99724,6 +100873,7 @@ async function main() {
99724
100873
  else if (arg === "cms") await cmdCms(subArgs);
99725
100874
  else if (arg === "book") await cmdBook(subArgs);
99726
100875
  else if (arg === "handover") await cmdHandover(subArgs);
100876
+ else if (arg === "audit") await cmdAudit(subArgs);
99727
100877
  else if (arg === "brand") await cmdBrand(subArgs);
99728
100878
  else if (arg === "invoice") await cmdInvoice(subArgs);
99729
100879
  else if (arg === "letter") await cmdLetter(subArgs);
@@ -99753,6 +100903,7 @@ async function main() {
99753
100903
  say(` ${c.bold("sproutkit setup")} set or update keys, licence, backend`);
99754
100904
  say(` ${c.bold("sproutkit doctor")} check everything is wired up`);
99755
100905
  say(` ${c.bold("sproutkit models")} list Claude models and pick one`);
100906
+ say(` ${c.bold("sproutkit desktop")} install and open the desktop app (--update)`);
99756
100907
  say(` ${c.bold("sproutkit sync")} push your leads into the desktop app`);
99757
100908
  say(` ${c.bold("sproutkit update")} update to the latest version`);
99758
100909
  say(` ${c.bold("sproutkit version")} show the installed version`);
@@ -99772,7 +100923,7 @@ async function main() {
99772
100923
  }
99773
100924
  const lic = await check();
99774
100925
  if (lic.status !== "active") {
99775
- const why = lic.status === "revoked" ? "Your licence has been revoked." : lic.status === "refunded" ? "Your licence was refunded." : lic.status === "offline_grace_expired" ? "Can't reach the licence server and your offline grace period has expired. Reconnect and try again." : "No active licence. Run 'sproutkit setup' to enter your key, or buy one at https://sproutkit.app";
100926
+ const why = lic.status === "revoked" ? "Your licence has been revoked." : lic.status === "refunded" ? "Your licence was refunded." : lic.status === "expired" ? "Your Sproutkit subscription has ended. Renew at https://sproutkit.app/#pricing, then run 'sproutkit setup' with your new key." : lic.status === "offline_grace_expired" ? "Can't reach the licence server and your offline grace period has expired. Reconnect and try again." : "No active licence. Run 'sproutkit setup' to enter your key, or buy one at https://sproutkit.app";
99776
100927
  say(c.red(` ${why}`));
99777
100928
  closeTty();
99778
100929
  return;
@@ -99813,7 +100964,7 @@ async function main() {
99813
100964
  ];
99814
100965
  if (oneShot) claudeArgs.push(oneShot);
99815
100966
  closeTty();
99816
- const child = (0, import_child_process4.spawn)(claudeBin(), claudeArgs, { stdio: "inherit" });
100967
+ const child = (0, import_child_process5.spawn)(claudeBin(), claudeArgs, { stdio: "inherit" });
99817
100968
  child.on("error", (e) => {
99818
100969
  say(c.red(` Couldn't start Claude Code: ${e.message}`));
99819
100970
  process.exitCode = 1;