@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.
@@ -303,7 +305,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
303
305
  /**
304
306
  * Create tools for connections that belong to this connector.
305
307
  * Filters connections by connectorKey internally.
306
- * Returns tools keyed as `${connectorKey}_${toolName}`.
308
+ * Returns tools keyed as `connector_${connectorKey}_${toolName}`.
307
309
  */
308
310
  createTools(connections, config, opts) {
309
311
  const myConnections = connections.filter(
@@ -313,7 +315,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
313
315
  for (const t of Object.values(this.tools)) {
314
316
  const tool = t.createTool(myConnections, config);
315
317
  const originalToModelOutput = tool.toModelOutput;
316
- result[`${this.connectorKey}_${t.name}`] = {
318
+ result[`connector_${this.connectorKey}_${t.name}`] = {
317
319
  ...tool,
318
320
  toModelOutput: async (options) => {
319
321
  if (!originalToModelOutput) {
@@ -369,19 +371,34 @@ async function runSetupFlow(flow, params, ctx, config) {
369
371
  };
370
372
  let state = flow.initialState();
371
373
  let answerIdx = 0;
374
+ const pendingParameterUpdates = [];
372
375
  for (const step of flow.steps) {
373
376
  const ans = ctx.answers[answerIdx];
374
377
  if (ans && ans.questionSlug === step.slug) {
375
378
  state = step.applyAnswer(state, ans.answer);
379
+ if (step.toParameterUpdates) {
380
+ pendingParameterUpdates.push(...step.toParameterUpdates(state));
381
+ }
376
382
  answerIdx += 1;
377
383
  continue;
378
384
  }
385
+ const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
379
386
  if (step.type === "text") {
387
+ if (step.fetchOptions) {
388
+ const options2 = await step.fetchOptions(state, runtime);
389
+ if (options2.length === 0) {
390
+ continue;
391
+ }
392
+ }
380
393
  return {
381
394
  type: "nextQuestion",
382
395
  questionSlug: step.slug,
383
396
  question: step.question[ctx.language],
384
- questionType: "text"
397
+ questionType: "text",
398
+ allowFreeText: resolvedAllowFreeText,
399
+ ...pendingParameterUpdates.length > 0 && {
400
+ parameterUpdates: pendingParameterUpdates
401
+ }
385
402
  };
386
403
  }
387
404
  const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
@@ -393,11 +410,21 @@ async function runSetupFlow(flow, params, ctx, config) {
393
410
  questionSlug: step.slug,
394
411
  question: step.question[ctx.language],
395
412
  questionType: step.type,
396
- options
413
+ options,
414
+ allowFreeText: resolvedAllowFreeText,
415
+ ...pendingParameterUpdates.length > 0 && {
416
+ parameterUpdates: pendingParameterUpdates
417
+ }
397
418
  };
398
419
  }
399
420
  const dataInvestigationResult = await flow.finalize(state, runtime);
400
- return { type: "fulfilled", dataInvestigationResult };
421
+ return {
422
+ type: "fulfilled",
423
+ dataInvestigationResult,
424
+ ...pendingParameterUpdates.length > 0 && {
425
+ parameterUpdates: pendingParameterUpdates
426
+ }
427
+ };
401
428
  }
402
429
  async function resolveSetupSelection(params) {
403
430
  const { selected, allSentinel, fetchAll, limit } = params;
@@ -420,7 +447,7 @@ var tableauOnboarding = new ConnectorOnboarding({
420
447
  connectionSetupInstructions: {
421
448
  en: `Follow these steps to verify the Tableau PAT connection.
422
449
 
423
- 1. Call \`tableau_request\` with \`method: "GET"\` and \`path: "/sites/{siteId}/projects?pageSize=5"\` to list a few projects on the signed-in site
450
+ 1. Call \`connector_tableau_request\` with \`method: "GET"\` and \`path: "/sites/{siteId}/projects?pageSize=5"\` to list a few projects on the signed-in site
424
451
  2. If the call fails with HTTP 401, ask the user to confirm the PAT name/secret and that the Site Content URL matches the site where the PAT was issued (Tableau Cloud sites are case-sensitive)
425
452
 
426
453
  #### Constraints
@@ -428,7 +455,7 @@ var tableauOnboarding = new ConnectorOnboarding({
428
455
  - The literal placeholder \`{siteId}\` in the path is automatically replaced with the session's site ID \u2014 do NOT hardcode the ID`,
429
456
  ja: `Tableau PAT \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u306F\u4EE5\u4E0B\u306E\u624B\u9806\u3067\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
430
457
 
431
- 1. \`tableau_request\` \u3092 \`method: "GET"\`\u3001\`path: "/sites/{siteId}/projects?pageSize=5"\` \u3067\u547C\u3073\u51FA\u3057\u3001\u30B5\u30A4\u30F3\u30A4\u30F3\u5148\u30B5\u30A4\u30C8\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u3044\u304F\u3064\u304B\u53D6\u5F97\u3059\u308B
458
+ 1. \`connector_tableau_request\` \u3092 \`method: "GET"\`\u3001\`path: "/sites/{siteId}/projects?pageSize=5"\` \u3067\u547C\u3073\u51FA\u3057\u3001\u30B5\u30A4\u30F3\u30A4\u30F3\u5148\u30B5\u30A4\u30C8\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u3044\u304F\u3064\u304B\u53D6\u5F97\u3059\u308B
432
459
  2. HTTP 401 \u3067\u5931\u6557\u3057\u305F\u5834\u5408\u306F\u3001PAT \u540D/\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8\u304A\u3088\u3073 Site Content URL \u304C PAT \u767A\u884C\u30B5\u30A4\u30C8\u3068\u4E00\u81F4\u3057\u3066\u3044\u308B\u304B\u3092\u30E6\u30FC\u30B6\u30FC\u306B\u78BA\u8A8D\u3059\u308B\u3088\u3046\u4F1D\u3048\u308B\uFF08Tableau Cloud \u306E\u30B5\u30A4\u30C8\u540D\u306F\u5927\u6587\u5B57\u5C0F\u6587\u5B57\u3092\u533A\u5225\uFF09
433
460
 
434
461
  #### \u5236\u7D04
@@ -436,14 +463,14 @@ var tableauOnboarding = new ConnectorOnboarding({
436
463
  - \u30D1\u30B9\u5185\u306E\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC \`{siteId}\` \u306F\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u30B5\u30A4\u30C8 ID \u3067\u81EA\u52D5\u7F6E\u63DB\u3055\u308C\u307E\u3059 \u2014 ID \u3092\u30CF\u30FC\u30C9\u30B3\u30FC\u30C9\u3057\u306A\u3044\u3053\u3068`
437
464
  },
438
465
  dataOverviewInstructions: {
439
- en: `1. Call tableau_request with GET /sites/{siteId}/projects to list projects
440
- 2. Call tableau_request with GET /sites/{siteId}/workbooks?pageSize=20 to list workbooks
441
- 3. For a target workbook, call tableau_request with GET /sites/{siteId}/workbooks/{workbookId}/views to list its views
442
- 4. Call tableau_request with GET /sites/{siteId}/datasources?pageSize=20 to list published data sources`,
443
- ja: `1. tableau_request \u3067 GET /sites/{siteId}/projects \u3092\u547C\u3073\u51FA\u3057\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97
444
- 2. tableau_request \u3067 GET /sites/{siteId}/workbooks?pageSize=20 \u3092\u547C\u3073\u51FA\u3057\u3001\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u4E00\u89A7\u3092\u53D6\u5F97
445
- 3. \u5BFE\u8C61\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u306B\u3064\u3044\u3066 tableau_request \u3067 GET /sites/{siteId}/workbooks/{workbookId}/views \u3092\u547C\u3073\u51FA\u3057\u3001\u30D3\u30E5\u30FC\u4E00\u89A7\u3092\u53D6\u5F97
446
- 4. tableau_request \u3067 GET /sites/{siteId}/datasources?pageSize=20 \u3092\u547C\u3073\u51FA\u3057\u3001\u516C\u958B\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u4E00\u89A7\u3092\u53D6\u5F97`
466
+ en: `1. Call connector_tableau_request with GET /sites/{siteId}/projects to list projects
467
+ 2. Call connector_tableau_request with GET /sites/{siteId}/workbooks?pageSize=20 to list workbooks
468
+ 3. For a target workbook, call connector_tableau_request with GET /sites/{siteId}/workbooks/{workbookId}/views to list its views
469
+ 4. Call connector_tableau_request with GET /sites/{siteId}/datasources?pageSize=20 to list published data sources`,
470
+ ja: `1. connector_tableau_request \u3067 GET /sites/{siteId}/projects \u3092\u547C\u3073\u51FA\u3057\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97
471
+ 2. connector_tableau_request \u3067 GET /sites/{siteId}/workbooks?pageSize=20 \u3092\u547C\u3073\u51FA\u3057\u3001\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u4E00\u89A7\u3092\u53D6\u5F97
472
+ 3. \u5BFE\u8C61\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u306B\u3064\u3044\u3066 connector_tableau_request \u3067 GET /sites/{siteId}/workbooks/{workbookId}/views \u3092\u547C\u3073\u51FA\u3057\u3001\u30D3\u30E5\u30FC\u4E00\u89A7\u3092\u53D6\u5F97
473
+ 4. connector_tableau_request \u3067 GET /sites/{siteId}/datasources?pageSize=20 \u3092\u547C\u3073\u51FA\u3057\u3001\u516C\u958B\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u4E00\u89A7\u3092\u53D6\u5F97`
447
474
  }
448
475
  });
449
476
 
@@ -467,7 +494,11 @@ async function tableauProxyApiFetch(proxyFetch, params, path2, init) {
467
494
 
468
495
  // ../connectors/src/connectors/tableau/setup-flow.ts
469
496
  var ALL_PROJECTS = "__ALL_PROJECTS__";
497
+ var ALL_WORKBOOKS = "__ALL_WORKBOOKS__";
470
498
  var TABLEAU_SETUP_MAX_PROJECTS = 10;
499
+ var MAX_WORKBOOKS_DETAIL = 10;
500
+ var MAX_VIEWS_PER_WORKBOOK = 5;
501
+ var MAX_SAMPLE_ROWS = 5;
471
502
  var PAGE_SIZE = 100;
472
503
  async function listAllProjects(rt) {
473
504
  const all = [];
@@ -516,6 +547,57 @@ async function listProjectResources(rt, resource) {
516
547
  }
517
548
  return all;
518
549
  }
550
+ async function listViewsForWorkbook(rt, workbookId) {
551
+ const res = await tableauProxyApiFetch(
552
+ rt.config.proxyFetch,
553
+ rt.params,
554
+ `/sites/{siteId}/workbooks/${encodeURIComponent(workbookId)}/views`
555
+ );
556
+ if (!res.ok) return [];
557
+ const data = await res.json();
558
+ return data.views?.view ?? [];
559
+ }
560
+ async function fetchViewDataSample(rt, viewId) {
561
+ try {
562
+ const res = await tableauProxyApiFetch(
563
+ rt.config.proxyFetch,
564
+ rt.params,
565
+ `/sites/{siteId}/views/${encodeURIComponent(viewId)}/data`
566
+ );
567
+ if (!res.ok) return null;
568
+ const csv = await res.text();
569
+ const lines = csv.trim().split("\n").filter(Boolean);
570
+ if (lines.length === 0) return null;
571
+ const columns = parseCsvLine(lines[0]);
572
+ const rows = lines.slice(1, 1 + MAX_SAMPLE_ROWS).map(parseCsvLine);
573
+ return { columns, rows };
574
+ } catch {
575
+ return null;
576
+ }
577
+ }
578
+ function parseCsvLine(line) {
579
+ const result = [];
580
+ let current = "";
581
+ let inQuotes = false;
582
+ for (let i = 0; i < line.length; i++) {
583
+ const ch = line[i];
584
+ if (ch === '"') {
585
+ if (inQuotes && line[i + 1] === '"') {
586
+ current += '"';
587
+ i++;
588
+ } else {
589
+ inQuotes = !inQuotes;
590
+ }
591
+ } else if (ch === "," && !inQuotes) {
592
+ result.push(current.trim());
593
+ current = "";
594
+ } else {
595
+ current += ch;
596
+ }
597
+ }
598
+ result.push(current.trim());
599
+ return result;
600
+ }
519
601
  var tableauSetupFlow = {
520
602
  initialState: () => ({}),
521
603
  steps: [
@@ -538,6 +620,39 @@ var tableauSetupFlow = {
538
620
  ];
539
621
  },
540
622
  applyAnswer: (state, answer) => ({ ...state, projects: answer })
623
+ },
624
+ {
625
+ slug: "workbooks",
626
+ type: "multiSelect",
627
+ question: {
628
+ ja: "\u5206\u6790\u5BFE\u8C61\u306E\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u9078\u629E\u53EF\uFF09",
629
+ en: "Select the workbooks to analyze (multi-select allowed)"
630
+ },
631
+ async fetchOptions(state, rt) {
632
+ if (!state.projects?.length) return [];
633
+ const allProjects = await listAllProjects(rt);
634
+ const targetIds = await resolveSetupSelection({
635
+ selected: state.projects,
636
+ allSentinel: ALL_PROJECTS,
637
+ fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
638
+ limit: TABLEAU_SETUP_MAX_PROJECTS
639
+ });
640
+ const targetSet = new Set(targetIds);
641
+ const workbooks = await listProjectResources(rt, "workbooks");
642
+ const options = workbooks.filter((wb) => wb.id && wb.project?.id && targetSet.has(wb.project.id)).map((wb) => ({
643
+ value: wb.id,
644
+ label: wb.name ?? wb.id ?? "(unknown)"
645
+ }));
646
+ if (options.length === 0) return [];
647
+ return [
648
+ {
649
+ value: ALL_WORKBOOKS,
650
+ label: rt.language === "ja" ? "\u3059\u3079\u3066\u306E\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF" : "All workbooks"
651
+ },
652
+ ...options
653
+ ];
654
+ },
655
+ applyAnswer: (state, answer) => ({ ...state, workbooks: answer })
541
656
  }
542
657
  ],
543
658
  async finalize(state, rt) {
@@ -546,25 +661,33 @@ var tableauSetupFlow = {
546
661
  }
547
662
  const allProjects = await listAllProjects(rt);
548
663
  const projectById = new Map(allProjects.map((p) => [p.id, p]));
549
- const targetIds = await resolveSetupSelection({
664
+ const targetProjectIds = await resolveSetupSelection({
550
665
  selected: state.projects,
551
666
  allSentinel: ALL_PROJECTS,
552
667
  fetchAll: async () => allProjects.map((p) => p.id).filter((id) => id),
553
668
  limit: TABLEAU_SETUP_MAX_PROJECTS
554
669
  });
555
670
  const sections = ["## Tableau", ""];
556
- if (!targetIds.length) {
671
+ if (!targetProjectIds.length) {
557
672
  sections.push("_No projects selected._", "");
558
673
  return sections.join("\n");
559
674
  }
560
- const [workbooks, datasources] = await Promise.all([
675
+ const [allWorkbooks, datasources] = await Promise.all([
561
676
  listProjectResources(rt, "workbooks"),
562
677
  listProjectResources(rt, "datasources")
563
678
  ]);
679
+ const projectIdSet = new Set(targetProjectIds);
680
+ const targetWorkbooks = await resolveSetupSelection({
681
+ selected: state.workbooks ?? [],
682
+ allSentinel: ALL_WORKBOOKS,
683
+ fetchAll: async () => allWorkbooks.filter((wb) => wb.id && wb.project?.id && projectIdSet.has(wb.project.id)).map((wb) => wb.id).filter(Boolean),
684
+ limit: MAX_WORKBOOKS_DETAIL
685
+ });
686
+ const targetWorkbookSet = new Set(targetWorkbooks);
564
687
  const workbooksByProject = /* @__PURE__ */ new Map();
565
- for (const wb of workbooks) {
688
+ for (const wb of allWorkbooks) {
566
689
  const pid = wb.project?.id;
567
- if (!pid) continue;
690
+ if (!pid || !projectIdSet.has(pid)) continue;
568
691
  const bucket = workbooksByProject.get(pid) ?? [];
569
692
  bucket.push(wb);
570
693
  workbooksByProject.set(pid, bucket);
@@ -572,32 +695,78 @@ var tableauSetupFlow = {
572
695
  const datasourcesByProject = /* @__PURE__ */ new Map();
573
696
  for (const ds of datasources) {
574
697
  const pid = ds.project?.id;
575
- if (!pid) continue;
698
+ if (!pid || !projectIdSet.has(pid)) continue;
576
699
  const bucket = datasourcesByProject.get(pid) ?? [];
577
700
  bucket.push(ds);
578
701
  datasourcesByProject.set(pid, bucket);
579
702
  }
580
- for (const id of targetIds) {
581
- const project = projectById.get(id);
582
- const name = project?.name ?? id;
583
- sections.push(`### Project: ${name}`, "", `- id: \`${id}\``);
584
- const projectWorkbooks = workbooksByProject.get(id) ?? [];
585
- sections.push(`- Workbooks (${projectWorkbooks.length}):`);
586
- for (const wb of projectWorkbooks.slice(0, 25)) {
587
- sections.push(` - ${wb.name ?? wb.id ?? "(unknown)"}`);
588
- }
589
- if (projectWorkbooks.length > 25) {
590
- sections.push(` - \u2026and ${projectWorkbooks.length - 25} more`);
703
+ const allDiscoveredColumns = [];
704
+ for (const pid of targetProjectIds) {
705
+ const project = projectById.get(pid);
706
+ sections.push(`### Project: ${project?.name ?? pid}`, "");
707
+ const projectWorkbooks = workbooksByProject.get(pid) ?? [];
708
+ for (const wb of projectWorkbooks) {
709
+ if (!wb.id) continue;
710
+ const isDetailed = targetWorkbookSet.has(wb.id);
711
+ sections.push(`#### Workbook: ${wb.name ?? wb.id}`, "");
712
+ if (!isDetailed) continue;
713
+ const views = await listViewsForWorkbook(rt, wb.id);
714
+ if (views.length === 0) {
715
+ sections.push("_No views found._", "");
716
+ continue;
717
+ }
718
+ for (const view of views.slice(0, MAX_VIEWS_PER_WORKBOOK)) {
719
+ if (!view.id) continue;
720
+ sections.push(`##### View: ${view.name ?? view.id}`, "");
721
+ const sample = await fetchViewDataSample(rt, view.id);
722
+ if (!sample || sample.columns.length === 0) {
723
+ sections.push("_Data not available._", "");
724
+ continue;
725
+ }
726
+ allDiscoveredColumns.push(...sample.columns);
727
+ sections.push(`| ${sample.columns.join(" | ")} |`);
728
+ sections.push(`|${sample.columns.map(() => "---").join("|")}|`);
729
+ for (const row of sample.rows) {
730
+ const cells = row.map((c) => c.replace(/\|/g, "\\|"));
731
+ sections.push(`| ${cells.join(" | ")} |`);
732
+ }
733
+ sections.push("");
734
+ }
591
735
  }
592
- const projectDatasources = datasourcesByProject.get(id) ?? [];
593
- sections.push(`- Datasources (${projectDatasources.length}):`);
594
- for (const ds of projectDatasources.slice(0, 25)) {
595
- sections.push(` - ${ds.name ?? ds.id ?? "(unknown)"}`);
736
+ const projectDatasources = datasourcesByProject.get(pid) ?? [];
737
+ if (projectDatasources.length > 0) {
738
+ sections.push(`#### Datasources (${projectDatasources.length})`, "");
739
+ for (const ds of projectDatasources.slice(0, 25)) {
740
+ sections.push(`- ${ds.name ?? ds.id ?? "(unknown)"}`);
741
+ }
742
+ sections.push("");
596
743
  }
597
- if (projectDatasources.length > 25) {
598
- sections.push(` - \u2026and ${projectDatasources.length - 25} more`);
744
+ }
745
+ if (allDiscoveredColumns.length > 0) {
746
+ const unique = [...new Set(allDiscoveredColumns.map((c) => c.toLowerCase()))];
747
+ const themes = [];
748
+ const hasPattern = (keywords) => unique.some((c) => keywords.some((k) => c.includes(k)));
749
+ if (hasPattern(["revenue", "sales", "amount", "price", "cost", "profit", "\u58F2\u4E0A", "\u91D1\u984D", "\u5229\u76CA"]))
750
+ themes.push("Revenue & profitability analysis (trends, breakdown by segment)");
751
+ if (hasPattern(["date", "month", "year", "quarter", "day", "\u65E5\u4ED8", "\u6708", "\u5E74", "\u671F"]))
752
+ themes.push("Time-series trend analysis (YoY, MoM comparisons)");
753
+ if (hasPattern(["region", "country", "city", "state", "\u5730\u57DF", "\u56FD", "\u90FD\u5E02", "\u5E02"]))
754
+ themes.push("Geographic/regional performance comparison");
755
+ if (hasPattern(["product", "category", "item", "sku", "\u5546\u54C1", "\u30AB\u30C6\u30B4\u30EA"]))
756
+ themes.push("Product/category mix analysis");
757
+ if (hasPattern(["customer", "user", "account", "\u9867\u5BA2", "\u30E6\u30FC\u30B6\u30FC"]))
758
+ themes.push("Customer segmentation and behavior analysis");
759
+ if (hasPattern(["quantity", "count", "volume", "\u6570\u91CF", "\u4EF6\u6570"]))
760
+ themes.push("Volume and demand pattern analysis");
761
+ if (hasPattern(["rate", "ratio", "percentage", "%", "\u7387"]))
762
+ themes.push("KPI ratio tracking and benchmarking");
763
+ if (themes.length > 0) {
764
+ sections.push("### Suggested Analysis Themes", "");
765
+ for (const theme of themes) {
766
+ sections.push(`- ${theme}`);
767
+ }
768
+ sections.push("");
599
769
  }
600
- sections.push("");
601
770
  }
602
771
  return sections.join("\n");
603
772
  }
@@ -607,27 +776,36 @@ var tableauSetupFlow = {
607
776
  import { z } from "zod";
608
777
  var DEFAULT_API_VERSION3 = "3.28";
609
778
  var REQUEST_TIMEOUT_MS = 6e4;
610
- async function fetchTableauSession(connectionId) {
611
- const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
612
- const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
613
- if (!token || !sandboxId) {
614
- throw new Error(
615
- "Tableau session manager is not configured. Missing INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL or INTERNAL_SQUADBASE_SANDBOX_ID."
616
- );
779
+ var cachedToken = null;
780
+ async function getProxyToken(config) {
781
+ if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
782
+ return cachedToken.token;
617
783
  }
618
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
619
- const url = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/tableau-session`;
784
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
620
785
  const res = await fetch(url, {
621
786
  method: "POST",
622
- headers: { Authorization: `Bearer ${token}` }
787
+ headers: {
788
+ "Content-Type": "application/json",
789
+ "x-api-key": config.appApiKey,
790
+ "project-id": config.projectId
791
+ },
792
+ body: JSON.stringify({
793
+ sandboxId: config.sandboxId,
794
+ issuedBy: "coding-agent"
795
+ })
623
796
  });
624
797
  if (!res.ok) {
625
- const errBody = await res.text().catch(() => res.statusText);
798
+ const errorText = await res.text().catch(() => res.statusText);
626
799
  throw new Error(
627
- `Failed to fetch Tableau session from backend-api (HTTP ${res.status}): ${errBody}`
800
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
628
801
  );
629
802
  }
630
- return await res.json();
803
+ const data = await res.json();
804
+ cachedToken = {
805
+ token: data.token,
806
+ expiresAt: new Date(data.expiresAt).getTime()
807
+ };
808
+ return data.token;
631
809
  }
632
810
  function buildBaseUrl(serverUrl, apiVersion) {
633
811
  return `${serverUrl.replace(/\/$/, "")}/api/${apiVersion}`;
@@ -669,7 +847,7 @@ All paths are relative to {serverUrl}/api/{apiVersion}. Use the literal placehol
669
847
  Accept and Content-Type headers default to application/json so list responses come back as JSON instead of Tableau's default XML.`,
670
848
  inputSchema,
671
849
  outputSchema,
672
- async execute({ connectionId, method, path: path2, queryParams, body }, connections) {
850
+ async execute({ connectionId, method, path: path2, queryParams, body }, connections, config) {
673
851
  const connection2 = connections.find((c) => c.id === connectionId);
674
852
  if (!connection2) {
675
853
  return {
@@ -684,50 +862,42 @@ Accept and Content-Type headers default to application/json so list responses co
684
862
  const serverUrl = parameters.serverUrl.getValue(connection2);
685
863
  const apiVersion = parameters.apiVersion.tryGetValue(connection2) || DEFAULT_API_VERSION3;
686
864
  const trimmedPath = path2.trim().replace(/^([^/])/, "/$1");
687
- const queryString = queryParams ? `?${new URLSearchParams(queryParams).toString()}` : "";
688
865
  const baseUrl = buildBaseUrl(serverUrl, apiVersion);
866
+ let url = `${baseUrl}${trimmedPath}`;
867
+ if (queryParams) {
868
+ url += `?${new URLSearchParams(queryParams).toString()}`;
869
+ }
870
+ const token = await getProxyToken(config.oauthProxy);
871
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
689
872
  const controller = new AbortController();
690
873
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
691
874
  try {
692
- let attempt = 0;
693
- while (true) {
694
- const session = await fetchTableauSession(connectionId);
695
- const resolvedPath = trimmedPath.replace(
696
- /\{siteId\}/g,
697
- session.siteId
698
- );
699
- const url = `${baseUrl}${resolvedPath}${queryString}`;
700
- const init = {
875
+ const response = await fetch(proxyUrl, {
876
+ method: "POST",
877
+ headers: {
878
+ "Content-Type": "application/json",
879
+ Authorization: `Bearer ${token}`
880
+ },
881
+ body: JSON.stringify({
882
+ url,
701
883
  method,
702
- headers: {
703
- "X-Tableau-Auth": session.authToken,
704
- Accept: "application/json",
705
- "Content-Type": "application/json"
706
- },
707
- signal: controller.signal
708
- };
709
- if (body !== void 0) {
710
- init.body = JSON.stringify(body);
711
- }
712
- const response = await fetch(url, init);
713
- const text = await response.text();
714
- const data = text ? (() => {
715
- try {
716
- return JSON.parse(text);
717
- } catch {
718
- return text;
719
- }
720
- })() : null;
721
- if (response.status === 401 && attempt === 0) {
722
- attempt++;
723
- continue;
724
- }
725
- if (!response.ok) {
726
- const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
727
- return { success: false, error: errorMessage };
884
+ ...body !== void 0 ? { body } : {}
885
+ }),
886
+ signal: controller.signal
887
+ });
888
+ const text = await response.text();
889
+ const data = text ? (() => {
890
+ try {
891
+ return JSON.parse(text);
892
+ } catch {
893
+ return text;
728
894
  }
729
- return { success: true, status: response.status, data };
895
+ })() : null;
896
+ if (!response.ok) {
897
+ const errorMessage = data && typeof data === "object" && "error" in data ? JSON.stringify(data.error) : typeof data === "string" && data ? data : `HTTP ${response.status} ${response.statusText}`;
898
+ return { success: false, error: errorMessage };
730
899
  }
900
+ return { success: true, status: response.status, data };
731
901
  } finally {
732
902
  clearTimeout(timeout);
733
903
  }
@@ -753,7 +923,7 @@ var tableauConnector = new ConnectorPlugin({
753
923
  systemPrompt: {
754
924
  en: `### Tools
755
925
 
756
- - \`tableau_request\`: The only way to call the Tableau REST API. Use it for projects, workbooks, views, data sources, users, and permissions. The \`X-Tableau-Auth\` token is issued and managed centrally by the Squadbase backend so concurrent processes (chat & deployed app) share a single PAT sign-in. Use the literal \`{siteId}\` placeholder in the path \u2014 it is replaced with the session's site ID automatically.
926
+ - \`connector_tableau_request\`: The only way to call the Tableau REST API. Use it for projects, workbooks, views, data sources, users, and permissions. The \`X-Tableau-Auth\` token is issued and managed centrally by the Squadbase backend so concurrent processes (chat & deployed app) share a single PAT sign-in. Use the literal \`{siteId}\` placeholder in the path \u2014 it is replaced with the session's site ID automatically.
757
927
 
758
928
  ### Business Logic
759
929
 
@@ -810,7 +980,7 @@ export default async function handler(c: Context) {
810
980
  - Combine: \`filter=ownerName:eq:alice,tags:in:[finance]\``,
811
981
  ja: `### \u30C4\u30FC\u30EB
812
982
 
813
- - \`tableau_request\`: Tableau REST API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u3001\u30D3\u30E5\u30FC\u3001\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3001\u30E6\u30FC\u30B6\u30FC\u3001\u6A29\u9650\u306A\u3069\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\`X-Tableau-Auth\` \u30C8\u30FC\u30AF\u30F3\u306F Squadbase \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u3067\u4E00\u5143\u7BA1\u7406\u3055\u308C\u3001\u8907\u6570\u30D7\u30ED\u30BB\u30B9 (\u30C1\u30E3\u30C3\u30C8\u30FB\u30C7\u30D7\u30ED\u30A4\u6E08\u307F\u30A2\u30D7\u30EA) \u3067\u540C\u3058 PAT \u30B5\u30A4\u30F3\u30A4\u30F3\u3092\u5171\u6709\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306B\u306F \`{siteId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044 \u2014 \u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u30B5\u30A4\u30C8 ID \u3067\u81EA\u52D5\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002
983
+ - \`connector_tableau_request\`: Tableau REST API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30EF\u30FC\u30AF\u30D6\u30C3\u30AF\u3001\u30D3\u30E5\u30FC\u3001\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3001\u30E6\u30FC\u30B6\u30FC\u3001\u6A29\u9650\u306A\u3069\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\`X-Tableau-Auth\` \u30C8\u30FC\u30AF\u30F3\u306F Squadbase \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u3067\u4E00\u5143\u7BA1\u7406\u3055\u308C\u3001\u8907\u6570\u30D7\u30ED\u30BB\u30B9 (\u30C1\u30E3\u30C3\u30C8\u30FB\u30C7\u30D7\u30ED\u30A4\u6E08\u307F\u30A2\u30D7\u30EA) \u3067\u540C\u3058 PAT \u30B5\u30A4\u30F3\u30A4\u30F3\u3092\u5171\u6709\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306B\u306F \`{siteId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044 \u2014 \u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u30B5\u30A4\u30C8 ID \u3067\u81EA\u52D5\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002
814
984
 
815
985
  ### Business Logic
816
986