@squadbase/vite-server 0.1.17-dev.24af54e → 0.1.17-dev.423ee34

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.
Files changed (77) hide show
  1. package/dist/cli/index.js +4873 -1073
  2. package/dist/connectors/airtable-oauth.js +78 -11
  3. package/dist/connectors/airtable.js +74 -11
  4. package/dist/connectors/amplitude.js +38 -11
  5. package/dist/connectors/anthropic.js +4 -2
  6. package/dist/connectors/asana.js +67 -13
  7. package/dist/connectors/attio.js +60 -16
  8. package/dist/connectors/aws-billing.js +38 -11
  9. package/dist/connectors/azure-sql.js +64 -13
  10. package/dist/connectors/backlog-api-key.js +70 -18
  11. package/dist/connectors/clickup.js +80 -13
  12. package/dist/connectors/cosmosdb.js +42 -15
  13. package/dist/connectors/customerio.js +39 -12
  14. package/dist/connectors/dbt.js +716 -28
  15. package/dist/connectors/freshdesk.js +112 -11
  16. package/dist/connectors/freshsales.js +38 -11
  17. package/dist/connectors/freshservice.js +38 -11
  18. package/dist/connectors/gamma.js +47 -20
  19. package/dist/connectors/gemini.js +4 -2
  20. package/dist/connectors/github.js +42 -15
  21. package/dist/connectors/gmail-oauth.js +38 -13
  22. package/dist/connectors/gmail.js +34 -7
  23. package/dist/connectors/google-ads.js +38 -11
  24. package/dist/connectors/google-analytics-oauth.js +182 -28
  25. package/dist/connectors/google-analytics.js +653 -104
  26. package/dist/connectors/google-audit-log.js +34 -7
  27. package/dist/connectors/google-calendar-oauth.js +91 -18
  28. package/dist/connectors/google-calendar.js +91 -14
  29. package/dist/connectors/google-docs.js +38 -13
  30. package/dist/connectors/google-drive.js +60 -13
  31. package/dist/connectors/google-search-console-oauth.js +156 -20
  32. package/dist/connectors/google-sheets.js +36 -9
  33. package/dist/connectors/google-slides.js +38 -13
  34. package/dist/connectors/grafana.js +75 -13
  35. package/dist/connectors/hubspot-oauth.js +69 -12
  36. package/dist/connectors/hubspot.js +55 -12
  37. package/dist/connectors/influxdb.js +38 -11
  38. package/dist/connectors/intercom-oauth.js +100 -15
  39. package/dist/connectors/intercom.js +42 -15
  40. package/dist/connectors/jdbc.js +36 -9
  41. package/dist/connectors/jira-api-key.js +98 -14
  42. package/dist/connectors/kintone-api-token.js +96 -21
  43. package/dist/connectors/kintone.js +84 -14
  44. package/dist/connectors/linear.js +84 -15
  45. package/dist/connectors/linkedin-ads.js +71 -17
  46. package/dist/connectors/mailchimp-oauth.js +36 -9
  47. package/dist/connectors/mailchimp.js +36 -9
  48. package/dist/connectors/meta-ads-oauth.js +63 -17
  49. package/dist/connectors/meta-ads.js +65 -17
  50. package/dist/connectors/mixpanel.js +38 -11
  51. package/dist/connectors/monday.js +39 -12
  52. package/dist/connectors/mongodb.js +38 -11
  53. package/dist/connectors/notion-oauth.js +88 -14
  54. package/dist/connectors/notion.js +90 -14
  55. package/dist/connectors/openai.js +4 -2
  56. package/dist/connectors/oracle.js +78 -20
  57. package/dist/connectors/outlook-oauth.js +48 -23
  58. package/dist/connectors/powerbi-oauth.js +321 -49
  59. package/dist/connectors/salesforce.js +72 -12
  60. package/dist/connectors/semrush.js +374 -52
  61. package/dist/connectors/sentry.js +66 -13
  62. package/dist/connectors/shopify-oauth.js +71 -13
  63. package/dist/connectors/shopify.js +38 -11
  64. package/dist/connectors/sqlserver.js +64 -13
  65. package/dist/connectors/stripe-api-key.js +96 -18
  66. package/dist/connectors/stripe-oauth.js +98 -22
  67. package/dist/connectors/supabase.js +55 -11
  68. package/dist/connectors/tableau.js +262 -92
  69. package/dist/connectors/tiktok-ads.js +67 -19
  70. package/dist/connectors/wix-store.js +38 -11
  71. package/dist/connectors/zendesk-oauth.js +83 -15
  72. package/dist/connectors/zendesk.js +42 -15
  73. package/dist/index.d.ts +1 -0
  74. package/dist/index.js +4902 -1077
  75. package/dist/main.js +4891 -1071
  76. package/dist/vite-plugin.js +4871 -1071
  77. package/package.json +1 -1
@@ -16,6 +16,7 @@ var init_parameter_definition = __esm({
16
16
  type;
17
17
  secret;
18
18
  required;
19
+ isDeprecated;
19
20
  constructor(config) {
20
21
  this.slug = config.slug;
21
22
  this.name = config.name;
@@ -24,6 +25,7 @@ var init_parameter_definition = __esm({
24
25
  this.type = config.type;
25
26
  this.secret = config.secret;
26
27
  this.required = config.required;
28
+ this.isDeprecated = config.isDeprecated ?? false;
27
29
  }
28
30
  /**
29
31
  * Get the parameter value from a ConnectorConnectionObject.
@@ -295,7 +297,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
295
297
  /**
296
298
  * Create tools for connections that belong to this connector.
297
299
  * Filters connections by connectorKey internally.
298
- * Returns tools keyed as `${connectorKey}_${toolName}`.
300
+ * Returns tools keyed as `connector_${connectorKey}_${toolName}`.
299
301
  */
300
302
  createTools(connections, config, opts) {
301
303
  const myConnections = connections.filter(
@@ -305,7 +307,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
305
307
  for (const t of Object.values(this.tools)) {
306
308
  const tool = t.createTool(myConnections, config);
307
309
  const originalToModelOutput = tool.toModelOutput;
308
- result[`${this.connectorKey}_${t.name}`] = {
310
+ result[`connector_${this.connectorKey}_${t.name}`] = {
309
311
  ...tool,
310
312
  toModelOutput: async (options) => {
311
313
  if (!originalToModelOutput) {
@@ -361,19 +363,34 @@ async function runSetupFlow(flow, params, ctx, config) {
361
363
  };
362
364
  let state = flow.initialState();
363
365
  let answerIdx = 0;
366
+ const pendingParameterUpdates = [];
364
367
  for (const step of flow.steps) {
365
368
  const ans = ctx.answers[answerIdx];
366
369
  if (ans && ans.questionSlug === step.slug) {
367
370
  state = step.applyAnswer(state, ans.answer);
371
+ if (step.toParameterUpdates) {
372
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
373
+ }
368
374
  answerIdx += 1;
369
375
  continue;
370
376
  }
377
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
371
378
  if (step.type === "text") {
379
+ if (step.fetchOptions) {
380
+ const options2 = await step.fetchOptions(state, runtime);
381
+ if (options2.length === 0) {
382
+ continue;
383
+ }
384
+ }
372
385
  return {
373
386
  type: "nextQuestion",
374
387
  questionSlug: step.slug,
375
388
  question: step.question[ctx.language],
376
- questionType: "text"
389
+ questionType: "text",
390
+ allowFreeText: resolvedAllowFreeText,
391
+ ...pendingParameterUpdates.length > 0 && {
392
+ parameterUpdates: pendingParameterUpdates
393
+ }
377
394
  };
378
395
  }
379
396
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -385,11 +402,21 @@ async function runSetupFlow(flow, params, ctx, config) {
385
402
  questionSlug: step.slug,
386
403
  question: step.question[ctx.language],
387
404
  questionType: step.type,
388
- options
405
+ options,
406
+ allowFreeText: resolvedAllowFreeText,
407
+ ...pendingParameterUpdates.length > 0 && {
408
+ parameterUpdates: pendingParameterUpdates
409
+ }
389
410
  };
390
411
  }
391
412
  const dataInvestigationResult = await flow.finalize(state, runtime);
392
- return { type: "fulfilled", dataInvestigationResult };
413
+ return {
414
+ type: "fulfilled",
415
+ dataInvestigationResult,
416
+ ...pendingParameterUpdates.length > 0 && {
417
+ parameterUpdates: pendingParameterUpdates
418
+ }
419
+ };
393
420
  }
394
421
  async function resolveSetupSelection(params) {
395
422
  const { selected, allSentinel, fetchAll, limit } = params;
@@ -593,7 +620,8 @@ var semrushOnboarding = new ConnectorOnboarding({
593
620
  });
594
621
 
595
622
  // ../connectors/src/connectors/semrush/utils.ts
596
- var PROJECTS_BASE_URL = "https://api.semrush.com/management/v1";
623
+ var BASE_URL3 = "https://api.semrush.com";
624
+ var PROJECTS_BASE_URL = `${BASE_URL3}/management/v1`;
597
625
  function projectsApiFetch(params, path2, init) {
598
626
  const apiKey = params[parameters.apiKey.slug];
599
627
  if (!apiKey) {
@@ -610,10 +638,103 @@ function projectsApiFetch(params, path2, init) {
610
638
  if (!headers.has("Accept")) headers.set("Accept", "application/json");
611
639
  return fetch(url.toString(), { ...init, headers });
612
640
  }
641
+ async function reportFetch(params, query) {
642
+ const apiKey = params[parameters.apiKey.slug];
643
+ if (!apiKey) {
644
+ throw new Error(
645
+ `semrush: missing required parameter: ${parameters.apiKey.slug}`
646
+ );
647
+ }
648
+ const url = new URL(`${BASE_URL3}/`);
649
+ for (const [k, v] of Object.entries(query)) {
650
+ url.searchParams.set(k, v);
651
+ }
652
+ url.searchParams.set("key", apiKey);
653
+ const res = await fetch(url.toString());
654
+ const text = await res.text();
655
+ if (text.startsWith("ERROR ")) throw new Error(text.trim());
656
+ return parseSemicolonCsv(text);
657
+ }
658
+ async function backlinksFetch(params, query) {
659
+ const apiKey = params[parameters.apiKey.slug];
660
+ if (!apiKey) {
661
+ throw new Error(
662
+ `semrush: missing required parameter: ${parameters.apiKey.slug}`
663
+ );
664
+ }
665
+ const url = new URL(`${BASE_URL3}/analytics/v1/`);
666
+ for (const [k, v] of Object.entries(query)) {
667
+ url.searchParams.set(k, v);
668
+ }
669
+ url.searchParams.set("key", apiKey);
670
+ const res = await fetch(url.toString());
671
+ const text = await res.text();
672
+ if (text.startsWith("ERROR ")) throw new Error(text.trim());
673
+ return parseSemicolonCsv(text);
674
+ }
675
+ function parseSemicolonCsv(raw) {
676
+ const lines = raw.trim().split("\n").filter(Boolean);
677
+ if (lines.length === 0) return { columns: [], rows: [] };
678
+ const columns = lines[0].split(";");
679
+ const rows = lines.slice(1).map((line) => {
680
+ const values = line.split(";");
681
+ const row = {};
682
+ for (let i = 0; i < columns.length; i++) {
683
+ row[columns[i]] = values[i] ?? "";
684
+ }
685
+ return row;
686
+ });
687
+ return { columns, rows };
688
+ }
613
689
 
614
690
  // ../connectors/src/connectors/semrush/setup-flow.ts
615
691
  var ALL_PROJECTS = "__ALL_PROJECTS__";
616
692
  var SEMRUSH_SETUP_MAX_PROJECTS = 10;
693
+ var SEMRUSH_DATABASES = [
694
+ { value: "us", label: "United States" },
695
+ { value: "uk", label: "United Kingdom" },
696
+ { value: "ca", label: "Canada" },
697
+ { value: "au", label: "Australia" },
698
+ { value: "de", label: "Germany" },
699
+ { value: "fr", label: "France" },
700
+ { value: "es", label: "Spain" },
701
+ { value: "it", label: "Italy" },
702
+ { value: "br", label: "Brazil" },
703
+ { value: "jp", label: "Japan" },
704
+ { value: "in", label: "India" },
705
+ { value: "ru", label: "Russia" },
706
+ { value: "nl", label: "Netherlands" },
707
+ { value: "se", label: "Sweden" },
708
+ { value: "mx", label: "Mexico" },
709
+ { value: "kr", label: "South Korea" },
710
+ { value: "sg", label: "Singapore" },
711
+ { value: "hk", label: "Hong Kong" },
712
+ { value: "tw", label: "Taiwan" }
713
+ ];
714
+ var REPORT_TYPE_LABELS = {
715
+ domain_overview: {
716
+ en: "Domain Overview (rank, traffic, keyword count)",
717
+ ja: "Domain Overview (\u30E9\u30F3\u30AF\u30FB\u30C8\u30E9\u30D5\u30A3\u30C3\u30AF\u30FB\u30AD\u30FC\u30EF\u30FC\u30C9\u6570)"
718
+ },
719
+ organic_search: {
720
+ en: "Organic Search (top organic keywords)",
721
+ ja: "Organic Search (\u4E0A\u4F4D\u30AA\u30FC\u30AC\u30CB\u30C3\u30AF\u30AD\u30FC\u30EF\u30FC\u30C9)"
722
+ },
723
+ paid_search: {
724
+ en: "Paid Search (top paid keywords)",
725
+ ja: "Paid Search (\u4E0A\u4F4D\u6709\u6599\u30AD\u30FC\u30EF\u30FC\u30C9)"
726
+ },
727
+ backlinks: {
728
+ en: "Backlinks (referring domains, backlink count)",
729
+ ja: "Backlinks (\u53C2\u7167\u30C9\u30E1\u30A4\u30F3\u30FB\u88AB\u30EA\u30F3\u30AF\u6570)"
730
+ }
731
+ };
732
+ var REPORT_TYPE_VALUES = [
733
+ "domain_overview",
734
+ "organic_search",
735
+ "paid_search",
736
+ "backlinks"
737
+ ];
617
738
  function projectId(p) {
618
739
  const raw = p.project_id ?? p.id;
619
740
  return raw == null ? "" : String(raw);
@@ -622,7 +743,7 @@ function projectName(p) {
622
743
  return p.project_name ?? p.name ?? p.domain ?? p.url ?? projectId(p);
623
744
  }
624
745
  function projectDomain(p) {
625
- return p.domain ?? p.url ?? "";
746
+ return p.domain ?? p.domain_unicode ?? p.url ?? "";
626
747
  }
627
748
  function projectCreatedAt(p) {
628
749
  return p.created_at ?? p.date_created ?? "";
@@ -630,13 +751,19 @@ function projectCreatedAt(p) {
630
751
  async function fetchProjects(params) {
631
752
  let res;
632
753
  try {
633
- res = await projectsApiFetch(params, "/projects/");
754
+ res = await projectsApiFetch(params, "/projects");
634
755
  } catch (err) {
635
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
756
+ return {
757
+ ok: false,
758
+ error: err instanceof Error ? err.message : String(err)
759
+ };
636
760
  }
637
761
  if (!res.ok) {
638
762
  const body2 = await res.text().catch(() => res.statusText);
639
- return { ok: false, error: `HTTP ${res.status} ${body2 || res.statusText}` };
763
+ return {
764
+ ok: false,
765
+ error: `HTTP ${res.status} ${body2 || res.statusText}`
766
+ };
640
767
  }
641
768
  let body;
642
769
  try {
@@ -650,9 +777,171 @@ async function fetchProjects(params) {
650
777
  const projects = Array.isArray(body) ? body : Array.isArray(body?.projects) ? body.projects : Array.isArray(body?.data) ? body.data : [];
651
778
  return { ok: true, projects };
652
779
  }
780
+ function formatNumber(n) {
781
+ return n.toLocaleString("en-US");
782
+ }
783
+ async function fetchDomainOverview(params, domain, database) {
784
+ const sections = [];
785
+ try {
786
+ const report = await reportFetch(params, {
787
+ type: "domain_ranks",
788
+ domain,
789
+ database
790
+ });
791
+ if (report.rows.length === 0) {
792
+ sections.push("_No data found for this domain._", "");
793
+ return sections;
794
+ }
795
+ const row = report.rows[0];
796
+ sections.push("| Metric | Value |");
797
+ sections.push("|--------|-------|");
798
+ sections.push(
799
+ `| Rank | ${row["Rank"] ?? "-"} |`,
800
+ `| Organic Keywords | ${formatNumber(Number(row["Organic Keywords"]) || 0)} |`,
801
+ `| Organic Traffic | ${formatNumber(Number(row["Organic Traffic"]) || 0)} |`,
802
+ `| Organic Cost | $${formatNumber(Number(row["Organic Cost"]) || 0)} |`,
803
+ `| Adwords Keywords | ${formatNumber(Number(row["Adwords Keywords"]) || 0)} |`,
804
+ `| Adwords Traffic | ${formatNumber(Number(row["Adwords Traffic"]) || 0)} |`,
805
+ `| Adwords Cost | $${formatNumber(Number(row["Adwords Cost"]) || 0)} |`
806
+ );
807
+ sections.push("");
808
+ } catch (err) {
809
+ sections.push(
810
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
811
+ ""
812
+ );
813
+ }
814
+ return sections;
815
+ }
816
+ async function fetchOrganicSearch(params, domain, database) {
817
+ return fetchKeywordReport(params, "domain_organic", domain, database);
818
+ }
819
+ async function fetchPaidSearch(params, domain, database) {
820
+ return fetchKeywordReport(params, "domain_adwords", domain, database);
821
+ }
822
+ async function fetchKeywordReport(params, type, domain, database) {
823
+ const sections = [];
824
+ try {
825
+ const report = await reportFetch(params, {
826
+ type,
827
+ domain,
828
+ database,
829
+ display_limit: "5"
830
+ });
831
+ if (report.rows.length === 0) {
832
+ sections.push("_No data found._", "");
833
+ return sections;
834
+ }
835
+ sections.push(...renderCsvTable(report, 5));
836
+ sections.push("");
837
+ } catch (err) {
838
+ sections.push(
839
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
840
+ ""
841
+ );
842
+ }
843
+ return sections;
844
+ }
845
+ async function fetchBacklinks(params, domain) {
846
+ const sections = [];
847
+ try {
848
+ const report = await backlinksFetch(params, {
849
+ type: "backlinks_overview",
850
+ target: domain,
851
+ target_type: "root_domain"
852
+ });
853
+ if (report.rows.length === 0) {
854
+ sections.push("_No backlink data found._", "");
855
+ return sections;
856
+ }
857
+ const row = report.rows[0];
858
+ sections.push("| Metric | Value |");
859
+ sections.push("|--------|-------|");
860
+ for (const col of report.columns) {
861
+ sections.push(`| ${col} | ${formatNumber(Number(row[col]) || 0)} |`);
862
+ }
863
+ sections.push("");
864
+ } catch (err) {
865
+ sections.push(
866
+ `_Error: ${err instanceof Error ? err.message : String(err)}_`,
867
+ ""
868
+ );
869
+ }
870
+ return sections;
871
+ }
872
+ function renderCsvTable(report, maxRows) {
873
+ const cols = report.columns;
874
+ const rows = report.rows.slice(0, maxRows);
875
+ const lines = [];
876
+ lines.push(`| ${cols.join(" | ")} |`);
877
+ lines.push(`|${cols.map(() => "---").join("|")}|`);
878
+ for (const row of rows) {
879
+ const cells = cols.map((c) => (row[c] ?? "").replace(/\|/g, "\\|"));
880
+ lines.push(`| ${cells.join(" | ")} |`);
881
+ }
882
+ return lines;
883
+ }
653
884
  var semrushSetupFlow = {
654
885
  initialState: () => ({}),
655
886
  steps: [
887
+ {
888
+ slug: "domain",
889
+ type: "select",
890
+ allowFreeText: true,
891
+ question: {
892
+ ja: "\u5206\u6790\u5BFE\u8C61\u306E\u30C9\u30E1\u30A4\u30F3\u3092\u9078\u629E\u307E\u305F\u306F\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
893
+ en: "Select or enter the domain to analyze"
894
+ },
895
+ async fetchOptions(_state, rt) {
896
+ const result = await fetchProjects(rt.params);
897
+ if (!result.ok || result.projects.length === 0) return [];
898
+ const seen = /* @__PURE__ */ new Set();
899
+ const options = [];
900
+ for (const p of result.projects) {
901
+ const d = projectDomain(p);
902
+ if (d && !seen.has(d)) {
903
+ seen.add(d);
904
+ options.push({
905
+ value: d,
906
+ label: `${d} (${projectName(p)})`
907
+ });
908
+ }
909
+ }
910
+ return options;
911
+ },
912
+ applyAnswer: (state, answer) => ({ ...state, domain: answer[0] })
913
+ },
914
+ {
915
+ slug: "database",
916
+ type: "select",
917
+ allowFreeText: false,
918
+ question: {
919
+ ja: "\u30EA\u30FC\u30B8\u30E7\u30F3\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u5BFE\u8C61\u30C9\u30E1\u30A4\u30F3\u306E\u4E3B\u8981\u5E02\u5834\uFF09",
920
+ en: "Select the region (primary market for the target domain)"
921
+ },
922
+ async fetchOptions() {
923
+ return SEMRUSH_DATABASES.map((db) => ({
924
+ value: db.value,
925
+ label: `${db.label} (${db.value})`
926
+ }));
927
+ },
928
+ applyAnswer: (state, answer) => ({ ...state, database: answer[0] })
929
+ },
930
+ {
931
+ slug: "reportTypes",
932
+ type: "multiSelect",
933
+ question: {
934
+ ja: "\u63A2\u7D22\u3059\u308B\u30EC\u30DD\u30FC\u30C8\u7A2E\u5225\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
935
+ en: "Select report types to explore (multi-select allowed)"
936
+ },
937
+ async fetchOptions(_state, rt) {
938
+ return REPORT_TYPE_VALUES.map((value) => ({
939
+ value,
940
+ label: REPORT_TYPE_LABELS[value][rt.language]
941
+ }));
942
+ },
943
+ applyAnswer: (state, answer) => ({ ...state, reportTypes: answer })
944
+ },
656
945
  {
657
946
  slug: "projects",
658
947
  type: "multiSelect",
@@ -669,7 +958,9 @@ var semrushSetupFlow = {
669
958
  const id = projectId(p);
670
959
  if (!id) return null;
671
960
  return { value: id, label: projectName(p) };
672
- }).filter((opt) => opt !== null);
961
+ }).filter(
962
+ (opt) => opt !== null
963
+ );
673
964
  if (options.length === 0) return [];
674
965
  return [
675
966
  {
@@ -683,49 +974,80 @@ var semrushSetupFlow = {
683
974
  }
684
975
  ],
685
976
  async finalize(state, rt) {
686
- const sections = ["## Semrush", ""];
687
- if (!state.projects?.length) {
977
+ if (!state.domain || !state.database || !state.reportTypes) {
978
+ throw new Error("Semrush setup: incomplete state on finalize");
979
+ }
980
+ const domain = state.domain;
981
+ const database = state.database;
982
+ const selected = state.reportTypes.filter(
983
+ (r) => REPORT_TYPE_VALUES.includes(r)
984
+ );
985
+ const sections = [
986
+ "## Semrush",
987
+ "",
988
+ `**Domain:** ${domain}`,
989
+ `**Region:** ${database}`,
990
+ ""
991
+ ];
992
+ for (const reportType of selected) {
688
993
  sections.push(
689
- "_No Semrush Projects API access detected; Standard Analytics reports run by domain/keyword and don't require pre-selection._",
994
+ `### ${REPORT_TYPE_LABELS[reportType].en}`,
690
995
  ""
691
996
  );
692
- return sections.join("\n");
693
- }
694
- const result = await fetchProjects(rt.params);
695
- if (!result.ok) {
696
- throw new Error(
697
- `semrush: listProjects failed on finalize: ${result.error}`
698
- );
699
- }
700
- const projectByIdMap = /* @__PURE__ */ new Map();
701
- for (const p of result.projects) {
702
- const id = projectId(p);
703
- if (id) projectByIdMap.set(id, p);
704
- }
705
- const targetIds = await resolveSetupSelection({
706
- selected: state.projects,
707
- allSentinel: ALL_PROJECTS,
708
- fetchAll: async () => result.projects.map((p) => projectId(p)).filter((id) => id),
709
- limit: SEMRUSH_SETUP_MAX_PROJECTS
710
- });
711
- if (!targetIds.length) {
712
- sections.push("_No projects selected._", "");
713
- return sections.join("\n");
997
+ switch (reportType) {
998
+ case "domain_overview":
999
+ sections.push(
1000
+ ...await fetchDomainOverview(rt.params, domain, database)
1001
+ );
1002
+ break;
1003
+ case "organic_search":
1004
+ sections.push(
1005
+ ...await fetchOrganicSearch(rt.params, domain, database)
1006
+ );
1007
+ break;
1008
+ case "paid_search":
1009
+ sections.push(
1010
+ ...await fetchPaidSearch(rt.params, domain, database)
1011
+ );
1012
+ break;
1013
+ case "backlinks":
1014
+ sections.push(...await fetchBacklinks(rt.params, domain));
1015
+ break;
1016
+ }
714
1017
  }
715
- sections.push("| Project | Domain | Created |");
716
- sections.push("|---------|--------|---------|");
717
- for (const id of targetIds) {
718
- const p = projectByIdMap.get(id);
719
- if (!p) {
720
- sections.push(`| ${id} | - | - |`);
721
- continue;
1018
+ if (state.projects?.length) {
1019
+ const result = await fetchProjects(rt.params);
1020
+ if (result.ok) {
1021
+ const projectByIdMap = /* @__PURE__ */ new Map();
1022
+ for (const p of result.projects) {
1023
+ const id = projectId(p);
1024
+ if (id) projectByIdMap.set(id, p);
1025
+ }
1026
+ const targetIds = await resolveSetupSelection({
1027
+ selected: state.projects,
1028
+ allSentinel: ALL_PROJECTS,
1029
+ fetchAll: async () => result.projects.map((p) => projectId(p)).filter((id) => id),
1030
+ limit: SEMRUSH_SETUP_MAX_PROJECTS
1031
+ });
1032
+ if (targetIds.length > 0) {
1033
+ sections.push("### Projects", "");
1034
+ sections.push("| Project | Domain | Created |");
1035
+ sections.push("|---------|--------|---------|");
1036
+ for (const id of targetIds) {
1037
+ const p = projectByIdMap.get(id);
1038
+ if (!p) {
1039
+ sections.push(`| ${id} | - | - |`);
1040
+ continue;
1041
+ }
1042
+ const name = projectName(p).replace(/\|/g, "\\|");
1043
+ const dom = projectDomain(p).replace(/\|/g, "\\|") || "-";
1044
+ const created = projectCreatedAt(p) || "-";
1045
+ sections.push(`| ${name} | ${dom} | ${created} |`);
1046
+ }
1047
+ sections.push("");
1048
+ }
722
1049
  }
723
- const name = projectName(p).replace(/\|/g, "\\|");
724
- const domain = projectDomain(p).replace(/\|/g, "\\|") || "-";
725
- const created = projectCreatedAt(p) || "-";
726
- sections.push(`| ${name} | ${domain} | ${created} |`);
727
1050
  }
728
- sections.push("");
729
1051
  return sections.join("\n");
730
1052
  }
731
1053
  };
@@ -745,7 +1067,7 @@ var semrushConnector = new ConnectorPlugin({
745
1067
  systemPrompt: {
746
1068
  en: `### Tools
747
1069
 
748
- - \`semrush-api-key_request\`: The only way to call the Semrush API. Use it for the Standard Analytics API (\`/?type=...\` reports), the Trends API (\`/analytics/v1/...\`), and the Projects API (\`/management/v1/...\`). Authentication is handled automatically \u2014 the API key is appended as the \`key\` query parameter; never include it yourself. The Standard Analytics API returns semicolon-separated CSV with the first row as the header (use \`responseFormat="text"\`); the Trends and Projects APIs return JSON (use \`responseFormat="json"\`). Errors from the Standard API are returned as a body that starts with \`ERROR\`, often with HTTP 200 \u2014 the tool surfaces these as \`success: false\` automatically.
1070
+ - \`connector_semrush-api-key_request\`: The only way to call the Semrush API. Use it for the Standard Analytics API (\`/?type=...\` reports), the Trends API (\`/analytics/v1/...\`), and the Projects API (\`/management/v1/...\`). Authentication is handled automatically \u2014 the API key is appended as the \`key\` query parameter; never include it yourself. The Standard Analytics API returns semicolon-separated CSV with the first row as the header (use \`responseFormat="text"\`); the Trends and Projects APIs return JSON (use \`responseFormat="json"\`). Errors from the Standard API are returned as a body that starts with \`ERROR\`, often with HTTP 200 \u2014 the tool surfaces these as \`success: false\` automatically.
749
1071
 
750
1072
  ### Business Logic
751
1073
 
@@ -758,7 +1080,7 @@ SDK methods (client created via \`connection(connectionId)\`):
758
1080
  - \`client.trends(path, query?)\` \u2014 call a Trends API endpoint (\`/analytics/v1/...\`) and return parsed JSON
759
1081
  - \`client.projects(path, init?)\` \u2014 call a Projects API endpoint (\`/management/v1/...\`) and return parsed JSON
760
1082
 
761
- \u{1F6A8} **MANDATORY pre-flight before creating any server logic that calls \`report()\`.** Before authoring a TypeScript handler that invokes \`client.report(...)\` (Standard Analytics \u2014 \`domain_ranks\`, \`domain_organic\`, \`phrase_this\`, etc.), you MUST first call the \`semrush-api-key_request\` tool with \`path: "https://www.semrush.com/users/countapiunits.html"\`, no \`queryParams\`, \`responseFormat: "text"\` to confirm the unit balance is greater than 0. If the balance is 0, **stop, do not create the server logic**, and report the issue to the user (the account owner needs to top up API units in the Semrush console). \`testFetchServerLogic\` will execute the handler and consume units; creating handlers when the balance is 0 produces \`ERROR 132 :: API UNITS BALANCE IS ZERO\` failures across every server logic.
1083
+ \u{1F6A8} **MANDATORY pre-flight before creating any server logic that calls \`report()\`.** Before authoring a TypeScript handler that invokes \`client.report(...)\` (Standard Analytics \u2014 \`domain_ranks\`, \`domain_organic\`, \`phrase_this\`, etc.), you MUST first call the \`connector_semrush-api-key_request\` tool with \`path: "https://www.semrush.com/users/countapiunits.html"\`, no \`queryParams\`, \`responseFormat: "text"\` to confirm the unit balance is greater than 0. If the balance is 0, **stop, do not create the server logic**, and report the issue to the user (the account owner needs to top up API units in the Semrush console). \`testFetchServerLogic\` will execute the handler and consume units; creating handlers when the balance is 0 produces \`ERROR 132 :: API UNITS BALANCE IS ZERO\` failures across every server logic.
762
1084
 
763
1085
  \u{1F6A8} **Always set a small \`display_limit\` explicitly \u2014 never rely on the default.** Semrush computes "required units = display_limit \xD7 per-row cost" up front, and rejects the request with \`ERROR 132 :: API UNITS BALANCE IS ZERO\` when the *estimated* cost exceeds the remaining balance, **even when the actual balance is non-zero** (e.g. balance \u2248 40,950 still fails for \`display_limit=10000\` on costly reports). Default to \`display_limit: "1000"\` (or smaller \u2014 \`"100"\` is plenty for most dashboards), and only increase it when the user explicitly asks for a larger sample AND \`checkUnits()\` confirms enough headroom. If you truly need all rows, paginate with \`display_offset\` in chunks of 1000 instead of bumping \`display_limit\`. Treat \`ERROR 132\` as "request too large for the current balance" first, and only as "balance is literally zero" after re-checking units.
764
1086
 
@@ -848,7 +1170,7 @@ To check remaining API units (free, does NOT consume units), call the request to
848
1170
  - Date strings in historical endpoints must be the 15th of the month (\`YYYYMM15\`)`,
849
1171
  ja: `### \u30C4\u30FC\u30EB
850
1172
 
851
- - \`semrush-api-key_request\`: Semrush API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002Standard Analytics API\uFF08\`/?type=...\` \u5F62\u5F0F\u306E\u30EC\u30DD\u30FC\u30C8\uFF09\u3001Trends API\uFF08\`/analytics/v1/...\`\uFF09\u3001Projects API\uFF08\`/management/v1/...\`\uFF09\u3059\u3079\u3066\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\u306F\u81EA\u52D5\u7684\u306B\u884C\u308F\u308C\u3001API\u30AD\u30FC\u306F \`key\` \u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3068\u3057\u3066\u4ED8\u4E0E\u3055\u308C\u308B\u305F\u3081\u3001\u81EA\u5206\u3067\u542B\u3081\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002Standard Analytics API \u306F1\u884C\u76EE\u304C\u30D8\u30C3\u30C0\u30FC\u306E\u30BB\u30DF\u30B3\u30ED\u30F3\u533A\u5207\u308ACSV\u3092\u8FD4\u3059\u305F\u3081 \`responseFormat="text"\` \u3092\u4F7F\u7528\u3057\u3001Trends/Projects API \u306F JSON \u3092\u8FD4\u3059\u305F\u3081 \`responseFormat="json"\` \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002Standard API \u306E\u30A8\u30E9\u30FC\u306F HTTP 200 \u3067\u3082\u672C\u6587\u304C \`ERROR\` \u3067\u59CB\u307E\u308B\u5F62\u5F0F\u3067\u8FD4\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059\u304C\u3001\u30C4\u30FC\u30EB\u306F\u81EA\u52D5\u7684\u306B \`success: false\` \u3068\u3057\u3066\u8FD4\u3057\u307E\u3059\u3002
1173
+ - \`connector_semrush-api-key_request\`: Semrush API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002Standard Analytics API\uFF08\`/?type=...\` \u5F62\u5F0F\u306E\u30EC\u30DD\u30FC\u30C8\uFF09\u3001Trends API\uFF08\`/analytics/v1/...\`\uFF09\u3001Projects API\uFF08\`/management/v1/...\`\uFF09\u3059\u3079\u3066\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\u306F\u81EA\u52D5\u7684\u306B\u884C\u308F\u308C\u3001API\u30AD\u30FC\u306F \`key\` \u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3068\u3057\u3066\u4ED8\u4E0E\u3055\u308C\u308B\u305F\u3081\u3001\u81EA\u5206\u3067\u542B\u3081\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002Standard Analytics API \u306F1\u884C\u76EE\u304C\u30D8\u30C3\u30C0\u30FC\u306E\u30BB\u30DF\u30B3\u30ED\u30F3\u533A\u5207\u308ACSV\u3092\u8FD4\u3059\u305F\u3081 \`responseFormat="text"\` \u3092\u4F7F\u7528\u3057\u3001Trends/Projects API \u306F JSON \u3092\u8FD4\u3059\u305F\u3081 \`responseFormat="json"\` \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002Standard API \u306E\u30A8\u30E9\u30FC\u306F HTTP 200 \u3067\u3082\u672C\u6587\u304C \`ERROR\` \u3067\u59CB\u307E\u308B\u5F62\u5F0F\u3067\u8FD4\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059\u304C\u3001\u30C4\u30FC\u30EB\u306F\u81EA\u52D5\u7684\u306B \`success: false\` \u3068\u3057\u3066\u8FD4\u3057\u307E\u3059\u3002
852
1174
 
853
1175
  ### Business Logic
854
1176
 
@@ -861,7 +1183,7 @@ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u30
861
1183
  - \`client.trends(path, query?)\` \u2014 Trends API \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\uFF08\`/analytics/v1/...\`\uFF09\u3092\u547C\u3073\u51FA\u3057 JSON \u3092\u8FD4\u3059
862
1184
  - \`client.projects(path, init?)\` \u2014 Projects API \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\uFF08\`/management/v1/...\`\uFF09\u3092\u547C\u3073\u51FA\u3057 JSON \u3092\u8FD4\u3059
863
1185
 
864
- \u{1F6A8} **\`report()\` \u3092\u547C\u3076\u30B5\u30FC\u30D0\u30FC\u30ED\u30B8\u30C3\u30AF\u3092\u4F5C\u6210\u3059\u308B\u524D\u306E\u5FC5\u9808\u30D7\u30EA\u30D5\u30E9\u30A4\u30C8\u3002** Standard Analytics\uFF08\`domain_ranks\`\u3001\`domain_organic\`\u3001\`phrase_this\` \u306A\u3069\uFF09\u306E \`client.report(...)\` \u3092\u542B\u3080 TypeScript \u30CF\u30F3\u30C9\u30E9\u3092\u4F5C\u6210\u3059\u308B\u524D\u306B\u3001\u5FC5\u305A \`semrush-api-key_request\` \u30C4\u30FC\u30EB\u3092 \`path: "https://www.semrush.com/users/countapiunits.html"\`\u3001\`queryParams\` \u7121\u3057\u3001\`responseFormat: "text"\` \u3067\u547C\u3073\u51FA\u3057\u3066\u30E6\u30CB\u30C3\u30C8\u6B8B\u91CF\u304C 0 \u3088\u308A\u5927\u304D\u3044\u3053\u3068\u3092\u78BA\u8A8D\u3059\u308B\u3053\u3068\u3002\u6B8B\u91CF\u304C 0 \u306E\u5834\u5408\u306F **\u30B5\u30FC\u30D0\u30FC\u30ED\u30B8\u30C3\u30AF\u3092\u4F5C\u6210\u305B\u305A\u306B\u505C\u6B62\u3057\u3001\u30E6\u30FC\u30B6\u30FC\u306B\u5831\u544A**\u3059\u308B\uFF08Semrush \u306E\u7BA1\u7406\u30B3\u30F3\u30BD\u30FC\u30EB\u304B\u3089 API \u30E6\u30CB\u30C3\u30C8\u3092\u88DC\u5145\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B\uFF09\u3002\`testFetchServerLogic\` \u306F\u30CF\u30F3\u30C9\u30E9\u3092\u5B9F\u884C\u3057\u3066\u30E6\u30CB\u30C3\u30C8\u3092\u6D88\u8CBB\u3059\u308B\u305F\u3081\u3001\u6B8B\u91CF 0 \u306E\u307E\u307E\u4F5C\u6210\u3059\u308B\u3068\u5168\u30B5\u30FC\u30D0\u30FC\u30ED\u30B8\u30C3\u30AF\u304C \`ERROR 132 :: API UNITS BALANCE IS ZERO\` \u3067\u5931\u6557\u3059\u308B\u3002
1186
+ \u{1F6A8} **\`report()\` \u3092\u547C\u3076\u30B5\u30FC\u30D0\u30FC\u30ED\u30B8\u30C3\u30AF\u3092\u4F5C\u6210\u3059\u308B\u524D\u306E\u5FC5\u9808\u30D7\u30EA\u30D5\u30E9\u30A4\u30C8\u3002** Standard Analytics\uFF08\`domain_ranks\`\u3001\`domain_organic\`\u3001\`phrase_this\` \u306A\u3069\uFF09\u306E \`client.report(...)\` \u3092\u542B\u3080 TypeScript \u30CF\u30F3\u30C9\u30E9\u3092\u4F5C\u6210\u3059\u308B\u524D\u306B\u3001\u5FC5\u305A \`connector_semrush-api-key_request\` \u30C4\u30FC\u30EB\u3092 \`path: "https://www.semrush.com/users/countapiunits.html"\`\u3001\`queryParams\` \u7121\u3057\u3001\`responseFormat: "text"\` \u3067\u547C\u3073\u51FA\u3057\u3066\u30E6\u30CB\u30C3\u30C8\u6B8B\u91CF\u304C 0 \u3088\u308A\u5927\u304D\u3044\u3053\u3068\u3092\u78BA\u8A8D\u3059\u308B\u3053\u3068\u3002\u6B8B\u91CF\u304C 0 \u306E\u5834\u5408\u306F **\u30B5\u30FC\u30D0\u30FC\u30ED\u30B8\u30C3\u30AF\u3092\u4F5C\u6210\u305B\u305A\u306B\u505C\u6B62\u3057\u3001\u30E6\u30FC\u30B6\u30FC\u306B\u5831\u544A**\u3059\u308B\uFF08Semrush \u306E\u7BA1\u7406\u30B3\u30F3\u30BD\u30FC\u30EB\u304B\u3089 API \u30E6\u30CB\u30C3\u30C8\u3092\u88DC\u5145\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B\uFF09\u3002\`testFetchServerLogic\` \u306F\u30CF\u30F3\u30C9\u30E9\u3092\u5B9F\u884C\u3057\u3066\u30E6\u30CB\u30C3\u30C8\u3092\u6D88\u8CBB\u3059\u308B\u305F\u3081\u3001\u6B8B\u91CF 0 \u306E\u307E\u307E\u4F5C\u6210\u3059\u308B\u3068\u5168\u30B5\u30FC\u30D0\u30FC\u30ED\u30B8\u30C3\u30AF\u304C \`ERROR 132 :: API UNITS BALANCE IS ZERO\` \u3067\u5931\u6557\u3059\u308B\u3002
865
1187
 
866
1188
  \u{1F6A8} **\`display_limit\` \u306F\u5FC5\u305A\u660E\u793A\u7684\u306B\u5C0F\u3055\u3044\u5024\u3092\u6307\u5B9A\u3057\u3001\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u983C\u3089\u306A\u3044\u3053\u3068\u3002** Semrush \u306F\u4E8B\u524D\u306B\u300C\u5FC5\u8981\u30E6\u30CB\u30C3\u30C8 = display_limit \xD7 \u884C\u3042\u305F\u308A\u5358\u4FA1\u300D\u3092\u8A08\u7B97\u3057\u3001\u305D\u306E**\u898B\u7A4D\u984D**\u304C\u6B8B\u91CF\u3092\u8D85\u3048\u308B\u3068\u30EA\u30AF\u30A8\u30B9\u30C8\u3092 \`ERROR 132 :: API UNITS BALANCE IS ZERO\` \u3067\u62D2\u5426\u3059\u308B\u3002**\u5B9F\u6B8B\u91CF\u304C\u30BC\u30ED\u3067\u306A\u304F\u3066\u3082**\u8D77\u3053\u308B\uFF08\u4F8B\uFF1A\u6B8B\u91CF\u7D04 40,950 \u3067\u3082\u3001\u30B3\u30B9\u30C8\u306E\u9AD8\u3044\u30EC\u30DD\u30FC\u30C8\u3067 \`display_limit=10000\` \u3060\u3068\u5931\u6557\u3059\u308B\uFF09\u3002\u539F\u5247\u3068\u3057\u3066 \`display_limit: "1000"\`\uFF08\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u7528\u9014\u306A\u3089 \`"100"\` \u3067\u3082\u5341\u5206\uFF09\u3092\u6307\u5B9A\u3057\u3001\u30E6\u30FC\u30B6\u30FC\u304C\u660E\u793A\u7684\u306B\u5927\u304D\u306A\u30B5\u30F3\u30D7\u30EB\u3092\u8981\u6C42\u3057\u3001\u304B\u3064 \`checkUnits()\` \u3067\u6B8B\u91CF\u306B\u5341\u5206\u306A\u4F59\u88D5\u304C\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3067\u304D\u305F\u5834\u5408\u306E\u307F\u5897\u3084\u3059\u3002\u5168\u884C\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F \`display_limit\` \u3092\u4E0A\u3052\u308B\u306E\u3067\u306F\u306A\u304F\u3001\`display_offset\` \u3067 1000 \u4EF6\u523B\u307F\u306E\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u3092\u5B9F\u88C5\u3059\u308B\u3053\u3068\u3002\`ERROR 132\` \u306F\u300C\u6B8B\u91CF\u304C\u6587\u5B57\u901A\u308A\u30BC\u30ED\u300D\u3088\u308A\u5148\u306B\u300C\u73FE\u5728\u306E\u6B8B\u91CF\u306B\u5BFE\u3057\u3066\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u5927\u304D\u3059\u304E\u308B\u300D\u3092\u7591\u3044\u3001\u30E6\u30CB\u30C3\u30C8\u3092\u518D\u78BA\u8A8D\u3057\u3066\u304B\u3089\u5224\u65AD\u3059\u308B\u3053\u3068\u3002
867
1189