@squadbase/vite-server 0.1.17-dev.a9ddcfa → 0.1.17-dev.e4768d1

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 (80) hide show
  1. package/dist/cli/index.js +8941 -3700
  2. package/dist/connectors/airtable-oauth.js +87 -23
  3. package/dist/connectors/airtable.js +130 -23
  4. package/dist/connectors/amplitude.js +47 -23
  5. package/dist/connectors/anthropic.js +41 -17
  6. package/dist/connectors/asana.js +76 -25
  7. package/dist/connectors/attio.js +69 -28
  8. package/dist/connectors/aws-billing.js +47 -23
  9. package/dist/connectors/azure-sql.js +322 -25
  10. package/dist/connectors/backlog-api-key.js +79 -30
  11. package/dist/connectors/clickup.js +89 -25
  12. package/dist/connectors/cosmosdb.js +51 -27
  13. package/dist/connectors/customerio.js +47 -23
  14. package/dist/connectors/dbt.js +725 -40
  15. package/dist/connectors/freshdesk.js +121 -23
  16. package/dist/connectors/freshsales.js +47 -23
  17. package/dist/connectors/freshservice.js +47 -23
  18. package/dist/connectors/gamma.js +54 -30
  19. package/dist/connectors/gemini.js +41 -17
  20. package/dist/connectors/github.js +51 -27
  21. package/dist/connectors/gmail-oauth.js +42 -23
  22. package/dist/connectors/gmail.js +43 -19
  23. package/dist/connectors/google-ads.js +47 -23
  24. package/dist/connectors/google-analytics-oauth.js +191 -40
  25. package/dist/connectors/google-analytics.js +529 -111
  26. package/dist/connectors/google-audit-log.js +43 -19
  27. package/dist/connectors/google-calendar-oauth.js +100 -30
  28. package/dist/connectors/google-calendar.js +100 -26
  29. package/dist/connectors/google-docs.js +42 -23
  30. package/dist/connectors/google-drive.js +64 -23
  31. package/dist/connectors/google-search-console-oauth.js +165 -32
  32. package/dist/connectors/google-sheets.js +142 -199
  33. package/dist/connectors/google-slides.js +42 -23
  34. package/dist/connectors/grafana.js +84 -25
  35. package/dist/connectors/hackernews.d.ts +5 -0
  36. package/dist/connectors/hackernews.js +909 -0
  37. package/dist/connectors/hubspot-oauth.js +73 -22
  38. package/dist/connectors/hubspot.js +64 -24
  39. package/dist/connectors/influxdb.js +47 -23
  40. package/dist/connectors/intercom-oauth.js +104 -25
  41. package/dist/connectors/intercom.js +51 -27
  42. package/dist/connectors/jdbc.js +76 -25
  43. package/dist/connectors/jira-api-key.js +107 -26
  44. package/dist/connectors/kintone-api-token.js +105 -33
  45. package/dist/connectors/kintone.js +182 -26
  46. package/dist/connectors/linear.js +93 -27
  47. package/dist/connectors/linkedin-ads.js +80 -29
  48. package/dist/connectors/mailchimp-oauth.js +45 -21
  49. package/dist/connectors/mailchimp.js +45 -21
  50. package/dist/connectors/meta-ads-oauth.js +72 -29
  51. package/dist/connectors/meta-ads.js +74 -29
  52. package/dist/connectors/mixpanel.js +47 -23
  53. package/dist/connectors/monday.js +48 -24
  54. package/dist/connectors/mongodb.js +47 -23
  55. package/dist/connectors/notion-oauth.js +92 -24
  56. package/dist/connectors/notion.js +144 -26
  57. package/dist/connectors/openai.js +41 -17
  58. package/dist/connectors/oracle.js +316 -26
  59. package/dist/connectors/outlook-oauth.js +53 -34
  60. package/dist/connectors/powerbi-oauth.js +45 -26
  61. package/dist/connectors/salesforce.js +150 -24
  62. package/dist/connectors/semrush.js +45 -21
  63. package/dist/connectors/sentry.js +75 -25
  64. package/dist/connectors/shopify-oauth.js +75 -23
  65. package/dist/connectors/shopify.js +47 -23
  66. package/dist/connectors/sqlserver.js +322 -25
  67. package/dist/connectors/stripe-api-key.js +105 -30
  68. package/dist/connectors/stripe-oauth.js +102 -32
  69. package/dist/connectors/supabase.js +315 -21
  70. package/dist/connectors/tableau.js +54 -30
  71. package/dist/connectors/tiktok-ads.js +76 -31
  72. package/dist/connectors/wix-store.js +47 -23
  73. package/dist/connectors/x.d.ts +5 -0
  74. package/dist/connectors/x.js +951 -0
  75. package/dist/connectors/zendesk-oauth.js +87 -25
  76. package/dist/connectors/zendesk.js +51 -27
  77. package/dist/index.js +9005 -3709
  78. package/dist/main.js +9005 -3709
  79. package/dist/vite-plugin.js +8939 -3698
  80. package/package.json +12 -4
@@ -1,7 +1,12 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __esm = (fn, res) => function __init() {
4
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
3
+ var __esm = (fn, res, err) => function __init() {
4
+ if (err) throw err[0];
5
+ try {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ } catch (e) {
8
+ throw err = [e], e;
9
+ }
5
10
  };
6
11
  var __export = (target, all) => {
7
12
  for (var name in all)
@@ -334,10 +339,16 @@ async function runOracleQuery(parsed, sql, options = {}) {
334
339
  outFormat: oracledb.OUT_FORMAT_OBJECT,
335
340
  // Bound by the connector's own row cap, but keep the driver from
336
341
  // streaming arbitrarily large result sets.
337
- maxRows: 5e3
342
+ maxRows: 5e3,
343
+ // oracledb defaults to autoCommit=false, which silently rolls back
344
+ // DML when the connection closes.
345
+ autoCommit: true
338
346
  }
339
347
  );
340
- return { rows: result.rows ?? [] };
348
+ return {
349
+ rows: result.rows ?? [],
350
+ rowsAffected: result.rowsAffected
351
+ };
341
352
  } finally {
342
353
  try {
343
354
  await connection2.close();
@@ -508,6 +519,15 @@ var ConnectorPlugin = class _ConnectorPlugin {
508
519
  * `runSetupFlow` from `setup-flow.ts`.
509
520
  */
510
521
  setup;
522
+ /**
523
+ * 統一 introspection。SQL DB の `information_schema` / `SHOW` / `DESCRIBE` や SaaS の
524
+ * メタデータ API といった取得経路の差を吸収し、tables / columns / PK / FK を共通型
525
+ * {@link IntrospectedSchema} へ正規化して返す。取得経路は {@link IntrospectContext} で
526
+ * 抽象化され、SQL DB は `ctx.query`、SaaS は `ctx.proxyFetch` + `ctx.params` を使う。
527
+ * `ctx.profile === true` のときは L2 プロファイル(行数・NULL 率等)も best-effort で埋める。
528
+ * coding-agent の `DataContextIndex` がデータ地図を構築する際に使う。
529
+ */
530
+ introspect;
511
531
  /**
512
532
  * Opt-out of the default "verify before save" behavior on connection
513
533
  * creation. The backend invokes `checkConnection` synchronously while
@@ -522,6 +542,14 @@ var ConnectorPlugin = class _ConnectorPlugin {
522
542
  * the default verify-on-create behavior without opt-in.
523
543
  */
524
544
  skipConnectionCheckOnCreate;
545
+ /**
546
+ * 決定論的な「立ち上げ survey」レシピ(ルールベース prewarm 基盤)。実装した connector は、LLM を使わずに
547
+ * read-only で自分を survey する probe 列(テーブル/列メタデータ → 小サンプル)を宣言する。coding-agent の
548
+ * recon driver がこれを読み、connector ツールの `execute` を直接呼んで結果を seed にする。未実装の connector は
549
+ * driver 側で safely skip される(その connection は listConnections のみ seed)。標準 `information_schema` を
550
+ * 持つ SQL connector は `makeStandardSqlSurveyProbes` を使える。**probe は必ず read-only**。
551
+ */
552
+ surveyProbes;
525
553
  constructor(config) {
526
554
  this.slug = config.slug;
527
555
  this.authType = config.authType;
@@ -539,7 +567,9 @@ var ConnectorPlugin = class _ConnectorPlugin {
539
567
  this.query = config.query;
540
568
  this.checkConnection = config.checkConnection;
541
569
  this.setup = config.setup;
570
+ this.introspect = config.introspect;
542
571
  this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
572
+ this.surveyProbes = config.surveyProbes;
543
573
  }
544
574
  get connectorKey() {
545
575
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -547,7 +577,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
547
577
  /**
548
578
  * Create tools for connections that belong to this connector.
549
579
  * Filters connections by connectorKey internally.
550
- * Returns tools keyed as `${connectorKey}_${toolName}`.
580
+ * Returns tools keyed as `connector_${connectorKey}_${toolName}`.
551
581
  */
552
582
  createTools(connections, config, opts) {
553
583
  const myConnections = connections.filter(
@@ -557,7 +587,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
557
587
  for (const t of Object.values(this.tools)) {
558
588
  const tool = t.createTool(myConnections, config);
559
589
  const originalToModelOutput = tool.toModelOutput;
560
- result[`${this.connectorKey}_${t.name}`] = {
590
+ result[`connector_${this.connectorKey}_${t.name}`] = {
561
591
  ...tool,
562
592
  toModelOutput: async (options) => {
563
593
  if (!originalToModelOutput) {
@@ -577,25 +607,25 @@ var ConnectorPlugin = class _ConnectorPlugin {
577
607
  if (authType) return `${slug}-${authType}`;
578
608
  const LEGACY_NULL_AUTH_TYPE_MAP = {
579
609
  // user-password
580
- "postgresql": "user-password",
581
- "mysql": "user-password",
582
- "clickhouse": "user-password",
583
- "kintone": "user-password",
610
+ postgresql: "user-password",
611
+ mysql: "user-password",
612
+ clickhouse: "user-password",
613
+ kintone: "user-password",
584
614
  "squadbase-db": "user-password",
585
615
  // service-account
586
- "snowflake": "service-account",
587
- "bigquery": "service-account",
616
+ snowflake: "service-account",
617
+ bigquery: "service-account",
588
618
  "google-analytics": "service-account",
589
619
  "google-calendar": "service-account",
590
620
  "aws-athena": "service-account",
591
- "redshift": "service-account",
621
+ redshift: "service-account",
592
622
  // api-key
593
- "databricks": "api-key",
594
- "dbt": "api-key",
595
- "airtable": "api-key",
596
- "openai": "api-key",
597
- "gemini": "api-key",
598
- "anthropic": "api-key",
623
+ databricks: "api-key",
624
+ dbt: "api-key",
625
+ airtable: "api-key",
626
+ openai: "api-key",
627
+ gemini: "api-key",
628
+ anthropic: "api-key",
599
629
  "wix-store": "api-key"
600
630
  };
601
631
  const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
@@ -604,6 +634,80 @@ var ConnectorPlugin = class _ConnectorPlugin {
604
634
  }
605
635
  };
606
636
 
637
+ // ../connectors/src/survey-probe.ts
638
+ function rowsOf(output) {
639
+ if (!output || typeof output !== "object") return [];
640
+ const o = output;
641
+ if (o.success === false) return [];
642
+ return Array.isArray(o.rows) ? o.rows : [];
643
+ }
644
+ function pickString(row, candidates) {
645
+ for (const key of candidates) {
646
+ const v = row[key] ?? row[key.toUpperCase()] ?? row[key.toLowerCase()];
647
+ if (typeof v === "string" && v.length > 0) return v;
648
+ }
649
+ return void 0;
650
+ }
651
+ var TABLE_NAME_FIELDS = [
652
+ "table_name",
653
+ "name",
654
+ "tableName",
655
+ "tab_name",
656
+ "Table",
657
+ "table"
658
+ ];
659
+ function makeQuoteIdent(opts) {
660
+ if (opts.bracket) {
661
+ return (name) => `[${name.replaceAll("]", "]]")}]`;
662
+ }
663
+ const q = opts.quote ?? '"';
664
+ return (name) => `${q}${name.replaceAll(q, q + q)}${q}`;
665
+ }
666
+ function sampleSql(ref, limit) {
667
+ return `SELECT * FROM (SELECT * FROM ${ref}) AS _sq LIMIT ${limit}`;
668
+ }
669
+ function distinctTables(prior, max) {
670
+ const names = [];
671
+ const seen = /* @__PURE__ */ new Set();
672
+ for (const row of rowsOf(prior[0]?.output)) {
673
+ const name = pickString(row, TABLE_NAME_FIELDS);
674
+ if (!name || seen.has(name)) continue;
675
+ seen.add(name);
676
+ names.push(name);
677
+ if (names.length >= max) break;
678
+ }
679
+ return names;
680
+ }
681
+ function makeOracleSurveyProbes(opts) {
682
+ const queryTool = opts?.queryTool ?? "executeQuery";
683
+ const sampleLimit = opts?.sampleLimit ?? 5;
684
+ const maxSampleTables = opts?.maxSampleTables ?? 4;
685
+ const quoteIdent = makeQuoteIdent({ quote: '"' });
686
+ return (_connectionId, prior) => {
687
+ if (prior.length === 0) {
688
+ return [
689
+ {
690
+ tool: queryTool,
691
+ input: {
692
+ toolUseIntent: "Survey table/column metadata",
693
+ sql: "SELECT table_name, column_name, data_type FROM user_tab_columns ORDER BY table_name, column_id FETCH FIRST 1000 ROWS ONLY"
694
+ }
695
+ }
696
+ ];
697
+ }
698
+ if (prior.length === 1) {
699
+ return distinctTables(prior, maxSampleTables).map((name) => ({
700
+ tool: queryTool,
701
+ input: {
702
+ toolUseIntent: `Sample ${name}`,
703
+ sql: sampleSql(quoteIdent(name), sampleLimit)
704
+ }
705
+ }));
706
+ }
707
+ return [];
708
+ };
709
+ }
710
+
607
711
  // ../connectors/src/setup-flow.ts
608
712
  async function runSetupFlow(flow, params, ctx, config) {
609
713
  const runtime = {
@@ -674,6 +778,58 @@ async function resolveSetupSelection(params) {
674
778
  return resolved.slice(0, limit);
675
779
  }
676
780
 
781
+ // ../connectors/src/introspect-concurrency.ts
782
+ async function mapWithConcurrency(items, limit, fn) {
783
+ const results = new Array(items.length);
784
+ let next = 0;
785
+ const workers = Array.from(
786
+ { length: Math.min(Math.max(limit, 1), items.length) },
787
+ async () => {
788
+ for (; ; ) {
789
+ const i = next++;
790
+ if (i >= items.length) return;
791
+ results[i] = await fn(items[i]);
792
+ }
793
+ }
794
+ );
795
+ await Promise.all(workers);
796
+ return results;
797
+ }
798
+
799
+ // ../connectors/src/introspect-profile.ts
800
+ var DEFAULT_MAX_TABLES = 200;
801
+ var DEFAULT_CONCURRENCY = 5;
802
+ function dq(ident) {
803
+ return `"${ident.replace(/"/g, '""')}"`;
804
+ }
805
+ var quoteAnsi = (schema, name) => [...schema.split("."), name].map(dq).join(".");
806
+ async function withRowCounts(ctx, schemaPromise, quoteRef, opts = {}) {
807
+ const schema = await schemaPromise;
808
+ if (!ctx.profile || !ctx.query) return schema;
809
+ return attachRowCounts(ctx.query, schema, quoteRef, opts);
810
+ }
811
+ async function attachRowCounts(run, schema, quoteRef, opts = {}) {
812
+ const targets = schema.tables.slice(0, opts.maxTables ?? DEFAULT_MAX_TABLES);
813
+ await mapWithConcurrency(
814
+ targets,
815
+ opts.concurrency ?? DEFAULT_CONCURRENCY,
816
+ async (table) => {
817
+ try {
818
+ const { rows } = await run(
819
+ `SELECT COUNT(*) AS row_count FROM ${quoteRef(table.schema, table.name)}`
820
+ );
821
+ const first = rows[0] ? Object.values(rows[0])[0] : void 0;
822
+ const rowCount = Number(first);
823
+ if (Number.isFinite(rowCount)) {
824
+ table.profile = { ...table.profile ?? {}, rowCount };
825
+ }
826
+ } catch {
827
+ }
828
+ }
829
+ );
830
+ return schema;
831
+ }
832
+
677
833
  // ../connectors/src/auth-types.ts
678
834
  var AUTH_TYPES = {
679
835
  OAUTH: "oauth",
@@ -684,6 +840,63 @@ var AUTH_TYPES = {
684
840
  USER_PASSWORD: "user-password"
685
841
  };
686
842
 
843
+ // ../connectors/src/introspect-information-schema.ts
844
+ function pickCI(row, key) {
845
+ if (key in row) return String(row[key] ?? "");
846
+ const lower = key.toLowerCase();
847
+ for (const k of Object.keys(row)) {
848
+ if (k.toLowerCase() === lower) return String(row[k] ?? "");
849
+ }
850
+ return "";
851
+ }
852
+ function assembleIntrospectedSchema(input) {
853
+ const { colRows, pkRows = [], fkRows = [] } = input;
854
+ const pkSet = /* @__PURE__ */ new Set();
855
+ for (const r of pkRows) {
856
+ pkSet.add(
857
+ `${pickCI(r, "table_schema")}.${pickCI(r, "table_name")}.${pickCI(r, "column_name")}`
858
+ );
859
+ }
860
+ const fkMap = /* @__PURE__ */ new Map();
861
+ for (const r of fkRows) {
862
+ fkMap.set(
863
+ `${pickCI(r, "table_schema")}.${pickCI(r, "table_name")}.${pickCI(r, "column_name")}`,
864
+ {
865
+ schema: pickCI(r, "foreign_table_schema"),
866
+ table: pickCI(r, "foreign_table_name"),
867
+ column: pickCI(r, "foreign_column_name")
868
+ }
869
+ );
870
+ }
871
+ const tableMap = /* @__PURE__ */ new Map();
872
+ for (const r of colRows) {
873
+ const schema = pickCI(r, "table_schema");
874
+ const name = pickCI(r, "table_name");
875
+ const key = `${schema}.${name}`;
876
+ let table = tableMap.get(key);
877
+ if (!table) {
878
+ table = {
879
+ schema,
880
+ name,
881
+ type: pickCI(r, "table_type").toUpperCase() === "VIEW" ? "view" : "table",
882
+ columns: []
883
+ };
884
+ tableMap.set(key, table);
885
+ }
886
+ const colKey = `${schema}.${name}.${pickCI(r, "column_name")}`;
887
+ const column = {
888
+ name: pickCI(r, "column_name"),
889
+ dataType: pickCI(r, "data_type"),
890
+ nullable: pickCI(r, "is_nullable").toUpperCase() === "YES"
891
+ };
892
+ if (pkSet.has(colKey)) column.isPrimaryKey = true;
893
+ const fk = fkMap.get(colKey);
894
+ if (fk) column.foreignKey = fk;
895
+ table.columns.push(column);
896
+ }
897
+ return { tables: [...tableMap.values()] };
898
+ }
899
+
687
900
  // ../connectors/src/lib/unwrap-sample-limit.ts
688
901
  var STRICT_WRAPPER_RE = /^\s*SELECT\s+\*\s+FROM\s+\(([\s\S]+)\)\s+AS\s+\w+\s+LIMIT\s+(\d+)\s*;?\s*$/i;
689
902
  var TRAILING_LIMIT_RE = /\s+LIMIT\s+(\d+)\s*;?\s*$/i;
@@ -706,12 +919,12 @@ init_oracle_runner();
706
919
  // ../connectors/src/connectors/oracle/setup.ts
707
920
  var oracleOnboarding = new ConnectorOnboarding({
708
921
  dataOverviewInstructions: {
709
- en: `1. Use oracle_executeQuery to confirm the version: \`SELECT BANNER FROM V$VERSION\`
922
+ en: `1. Use connector_oracle_executeQuery to confirm the version: \`SELECT BANNER FROM V$VERSION\`
710
923
  2. Identify the current schema (Oracle treats users as schemas): \`SELECT USER FROM DUAL\`
711
924
  3. List user tables: \`SELECT TABLE_NAME FROM USER_TABLES\`. To explore other schemas the connection has access to, use \`ALL_TABLES\` filtered by \`OWNER\`.
712
925
  4. For key tables, fetch column info: \`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = UPPER('xxx')\` (Oracle stores unquoted identifiers as upper-case).
713
926
  5. Sample up to 3 tables. Oracle has no \`LIMIT\` keyword: use \`FETCH FIRST n ROWS ONLY\` or \`ROWNUM\`: \`SELECT * FROM <table> FETCH FIRST 5 ROWS ONLY\``,
714
- ja: `1. oracle_executeQuery \u3067\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u78BA\u8A8D: \`SELECT BANNER FROM V$VERSION\`
927
+ ja: `1. connector_oracle_executeQuery \u3067\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u78BA\u8A8D: \`SELECT BANNER FROM V$VERSION\`
715
928
  2. \u73FE\u5728\u306E\u30B9\u30AD\u30FC\u30DE\uFF08Oracle\u3067\u306F\u30E6\u30FC\u30B6\u30FC\uFF1D\u30B9\u30AD\u30FC\u30DE\uFF09\u3092\u78BA\u8A8D: \`SELECT USER FROM DUAL\`
716
929
  3. \u30E6\u30FC\u30B6\u30FC\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u3092\u53D6\u5F97: \`SELECT TABLE_NAME FROM USER_TABLES\`\u3002\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u4ED6\u30B9\u30AD\u30FC\u30DE\u3092\u53C2\u7167\u3059\u308B\u5834\u5408\u306F \`ALL_TABLES\` \u3092 \`OWNER\` \u3067\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\u3002
717
930
  4. \u4E3B\u8981\u30C6\u30FC\u30D6\u30EB\u306E\u30AB\u30E9\u30E0\u60C5\u5831\u3092\u53D6\u5F97: \`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = UPPER('xxx')\`\uFF08Oracle \u306F\u30AF\u30A9\u30FC\u30C8\u306A\u3057\u8B58\u5225\u5B50\u3092\u5927\u6587\u5B57\u3067\u4FDD\u5B58\uFF09\u3002
@@ -840,6 +1053,20 @@ var oracleSetupFlow = {
840
1053
  fetchAll: () => fetchTableAndViewNames(rt.params, owner),
841
1054
  limit: ORACLE_SETUP_MAX_TABLES
842
1055
  });
1056
+ let rowCounts = /* @__PURE__ */ new Map();
1057
+ try {
1058
+ const countRows = await runOracleSetupQuery(
1059
+ rt.params,
1060
+ `SELECT TABLE_NAME, NUM_ROWS FROM ALL_TABLES WHERE OWNER = ${quoteLiteral(owner)}`
1061
+ );
1062
+ rowCounts = new Map(
1063
+ countRows.filter((r) => r["NUM_ROWS"] != null).map((r) => [
1064
+ String(r["TABLE_NAME"] ?? ""),
1065
+ Number(r["NUM_ROWS"])
1066
+ ])
1067
+ );
1068
+ } catch {
1069
+ }
843
1070
  const typeRows = targetTables.length > 0 ? await runOracleSetupQuery(
844
1071
  rt.params,
845
1072
  `SELECT OBJECT_NAME, OBJECT_TYPE FROM ALL_OBJECTS
@@ -869,7 +1096,9 @@ var oracleSetupFlow = {
869
1096
  AND TABLE_NAME = ${quoteLiteral(table)}
870
1097
  ORDER BY COLUMN_ID`
871
1098
  );
872
- sections.push(`#### ${heading}: ${table}`, "");
1099
+ const rowCount = rowCounts.get(table);
1100
+ const rowCountSuffix = typeof rowCount === "number" ? ` (~${rowCount.toLocaleString()} rows)` : "";
1101
+ sections.push(`#### ${heading}: ${table}${rowCountSuffix}`, "");
873
1102
  sections.push("| Column | Type | Nullable | Default |");
874
1103
  sections.push("|--------|------|----------|---------|");
875
1104
  for (const c of cols) {
@@ -888,6 +1117,58 @@ var oracleSetupFlow = {
888
1117
  }
889
1118
  };
890
1119
 
1120
+ // ../connectors/src/connectors/oracle/introspect.ts
1121
+ var OWNER_NOT_IN = [...INTERNAL_OWNERS].map((o) => `'${o}'`).join(", ");
1122
+ function ownerFilter(ownerCol) {
1123
+ return `${ownerCol} NOT IN (${OWNER_NOT_IN}) AND ${ownerCol} NOT LIKE 'APEX_%'`;
1124
+ }
1125
+ async function introspectOracle(run) {
1126
+ const [{ rows: colRows }, { rows: pkRows }, { rows: fkRows }] = await Promise.all([
1127
+ // 1) 全カラム(型 / nullable)+ テーブル種別(TABLE / VIEW)
1128
+ run(
1129
+ `SELECT c.owner AS table_schema, c.table_name AS table_name,
1130
+ c.column_name AS column_name, c.data_type AS data_type,
1131
+ CASE WHEN c.nullable = 'Y' THEN 'YES' ELSE 'NO' END AS is_nullable,
1132
+ c.column_id AS ordinal_position,
1133
+ CASE WHEN o.object_type = 'VIEW' THEN 'VIEW' ELSE 'BASE TABLE' END AS table_type
1134
+ FROM all_tab_columns c
1135
+ JOIN all_objects o
1136
+ ON o.owner = c.owner AND o.object_name = c.table_name
1137
+ AND o.object_type IN ('TABLE', 'VIEW')
1138
+ WHERE ${ownerFilter("c.owner")}
1139
+ ORDER BY c.owner, c.table_name, c.column_id`
1140
+ ),
1141
+ // 2) 主キー(CONSTRAINT_TYPE = 'P')
1142
+ run(
1143
+ `SELECT ac.owner AS table_schema, ac.table_name AS table_name,
1144
+ acc.column_name AS column_name
1145
+ FROM all_constraints ac
1146
+ JOIN all_cons_columns acc
1147
+ ON acc.owner = ac.owner AND acc.constraint_name = ac.constraint_name
1148
+ WHERE ac.constraint_type = 'P'
1149
+ AND ${ownerFilter("ac.owner")}`
1150
+ ),
1151
+ // 3) 外部キー(CONSTRAINT_TYPE = 'R')。参照元 → 参照先を position で突き合わせる。
1152
+ run(
1153
+ `SELECT ac.owner AS table_schema, ac.table_name AS table_name,
1154
+ acc.column_name AS column_name,
1155
+ rac.owner AS foreign_table_schema, rac.table_name AS foreign_table_name,
1156
+ racc.column_name AS foreign_column_name
1157
+ FROM all_constraints ac
1158
+ JOIN all_cons_columns acc
1159
+ ON acc.owner = ac.owner AND acc.constraint_name = ac.constraint_name
1160
+ JOIN all_constraints rac
1161
+ ON rac.owner = ac.r_owner AND rac.constraint_name = ac.r_constraint_name
1162
+ JOIN all_cons_columns racc
1163
+ ON racc.owner = rac.owner AND racc.constraint_name = rac.constraint_name
1164
+ AND racc.position = acc.position
1165
+ WHERE ac.constraint_type = 'R'
1166
+ AND ${ownerFilter("ac.owner")}`
1167
+ )
1168
+ ]);
1169
+ return assembleIntrospectedSchema({ colRows, pkRows, fkRows });
1170
+ }
1171
+
891
1172
  // ../connectors/src/connectors/oracle/tools/execute-query.ts
892
1173
  import { z } from "zod";
893
1174
  init_oracle_runner();
@@ -907,6 +1188,9 @@ var outputSchema = z.discriminatedUnion("success", [
907
1188
  z.object({
908
1189
  success: z.literal(true),
909
1190
  rowCount: z.number(),
1191
+ rowsAffected: z.number().optional().describe(
1192
+ "Affected rows for INSERT/UPDATE/DELETE; undefined for SELECT"
1193
+ ),
910
1194
  truncated: z.boolean(),
911
1195
  rows: z.array(z.record(z.string(), z.unknown()))
912
1196
  }),
@@ -921,7 +1205,8 @@ var executeQueryTool = new ConnectorTool({
921
1205
  Use for: schema exploration via \`USER_TABLES\` / \`USER_TAB_COLUMNS\` / \`ALL_TABLES\`, data sampling, and analytical queries.
922
1206
  Oracle uses \`FETCH FIRST n ROWS ONLY\` (12c+) or \`ROWNUM\` for row limiting \u2014 there is no \`LIMIT\` keyword.
923
1207
  Unquoted identifiers are stored upper-case (\`SELECT * FROM employees\` resolves to \`EMPLOYEES\`).
924
- Do NOT terminate statements with a semicolon; the driver rejects trailing terminators.`,
1208
+ Do NOT terminate statements with a semicolon; the driver rejects trailing terminators.
1209
+ For INSERT/UPDATE/DELETE, \`rowCount\` is 0 and \`rows\` is empty (rowCount counts returned rows, not affected rows) \u2014 this is normal, not a failure; check \`rowsAffected\` for the number of affected rows.`,
925
1210
  inputSchema,
926
1211
  outputSchema,
927
1212
  async execute({ connectionId, sql }, connections) {
@@ -949,13 +1234,15 @@ Do NOT terminate statements with a semicolon; the driver rejects trailing termin
949
1234
  }
950
1235
  try {
951
1236
  const cleanSql = sql.replace(/;\s*$/, "");
952
- const { rows } = await runOracleQuery(parsed, cleanSql, {
1237
+ const { rows, rowsAffected } = await runOracleQuery(parsed, cleanSql, {
953
1238
  tunnelParams: connectionParamsToRecord(connection2)
954
1239
  });
955
1240
  const truncated = rows.length > MAX_ROWS;
956
1241
  return {
957
1242
  success: true,
958
1243
  rowCount: Math.min(rows.length, MAX_ROWS),
1244
+ // oracledb sets rowsAffected only for DML; undefined for SELECT.
1245
+ rowsAffected,
959
1246
  truncated,
960
1247
  rows: rows.slice(0, MAX_ROWS)
961
1248
  };
@@ -983,7 +1270,7 @@ var oracleConnector = new ConnectorPlugin({
983
1270
  systemPrompt: {
984
1271
  en: `### Tools
985
1272
 
986
- - \`oracle_executeQuery\`: Executes a SQL query against an Oracle Database and returns rows. Use it for schema exploration via \`USER_TABLES\` / \`USER_TAB_COLUMNS\` / \`ALL_TABLES\` and for sampling data. See the SQL Reference below for Oracle-specific syntax.
1273
+ - \`connector_oracle_executeQuery\`: Executes a SQL query against an Oracle Database and returns rows. Use it for schema exploration via \`USER_TABLES\` / \`USER_TAB_COLUMNS\` / \`ALL_TABLES\` and for sampling data. See the SQL Reference below for Oracle-specific syntax.
987
1274
 
988
1275
  ### Business Logic
989
1276
 
@@ -1002,7 +1289,7 @@ The business logic type for this connector is "sql".
1002
1289
  - Row-limit compatibility: the platform's server-logic schema inference may wrap your query as \`SELECT * FROM (<inner>) AS _sq LIMIT N\`. Oracle has no \`LIMIT\` keyword, so the connector detects this exact wrapper at \`query()\` time, executes \`<inner>\` directly, and slices the first N rows in JS. You do not need to handle this \u2014 but do not author your own \`LIMIT\` clauses; use \`FETCH FIRST N ROWS ONLY\`, \`OFFSET m ROWS FETCH NEXT N ROWS ONLY\`, or \`ROWNUM\` filters.`,
1003
1290
  ja: `### \u30C4\u30FC\u30EB
1004
1291
 
1005
- - \`oracle_executeQuery\`: Oracle Database \u306B\u5BFE\u3057\u3066 SQL \u3092\u5B9F\u884C\u3057\u3001\u884C\u30C7\u30FC\u30BF\u3092\u8FD4\u3057\u307E\u3059\u3002\`USER_TABLES\` / \`USER_TAB_COLUMNS\` / \`ALL_TABLES\` \u3092\u4F7F\u3063\u305F\u30B9\u30AD\u30FC\u30DE\u63A2\u7D22\u3084\u30C7\u30FC\u30BF\u306E\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u306B\u4F7F\u3044\u307E\u3059\u3002Oracle \u56FA\u6709\u306E\u69CB\u6587\u306F\u4E0B\u90E8\u306E\u300CSQL \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1292
+ - \`connector_oracle_executeQuery\`: Oracle Database \u306B\u5BFE\u3057\u3066 SQL \u3092\u5B9F\u884C\u3057\u3001\u884C\u30C7\u30FC\u30BF\u3092\u8FD4\u3057\u307E\u3059\u3002\`USER_TABLES\` / \`USER_TAB_COLUMNS\` / \`ALL_TABLES\` \u3092\u4F7F\u3063\u305F\u30B9\u30AD\u30FC\u30DE\u63A2\u7D22\u3084\u30C7\u30FC\u30BF\u306E\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u306B\u4F7F\u3044\u307E\u3059\u3002Oracle \u56FA\u6709\u306E\u69CB\u6587\u306F\u4E0B\u90E8\u306E\u300CSQL \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1006
1293
 
1007
1294
  ### Business Logic
1008
1295
 
@@ -1021,6 +1308,9 @@ The business logic type for this connector is "sql".
1021
1308
  - \u884C\u6570\u5236\u9650\u306E\u4E92\u63DB\u6027: \u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u306E server-logic \u30B9\u30AD\u30FC\u30DE\u63A8\u8AD6\u306F\u3001\u30AF\u30A8\u30EA\u3092 \`SELECT * FROM (<inner>) AS _sq LIMIT N\` \u306E\u5F62\u3067\u30E9\u30C3\u30D7\u3057\u3066\u304F\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059\u3002Oracle \u306B\u306F \`LIMIT\` \u30AD\u30FC\u30EF\u30FC\u30C9\u304C\u5B58\u5728\u3057\u306A\u3044\u305F\u3081\u3001\u30B3\u30CD\u30AF\u30BF\u306F \`query()\` \u5185\u3067\u3053\u306E\u30E9\u30C3\u30D1\u3092\u691C\u51FA\u3057\u3001\`<inner>\` \u3092\u305D\u306E\u307E\u307E\u5B9F\u884C\u3057\u3066 JS \u5074\u3067\u5148\u982D N \u884C\u306B\u5207\u308A\u8A70\u3081\u307E\u3059\u3002\u5229\u7528\u8005\u5074\u3067\u5BFE\u51E6\u3059\u308B\u5FC5\u8981\u306F\u3042\u308A\u307E\u305B\u3093\u304C\u3001\u81EA\u5206\u3067\u66F8\u304F SQL \u3067\u306F \`LIMIT\` \u3092\u4F7F\u308F\u305A \`FETCH FIRST N ROWS ONLY\` / \`OFFSET m ROWS FETCH NEXT N ROWS ONLY\` / \`ROWNUM\` \u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002`
1022
1309
  },
1023
1310
  tools,
1311
+ // 決定論的な立ち上げ survey(ルールベース prewarm)。Oracle は USER_TAB_COLUMNS でメタ取得。
1312
+ surveyProbes: makeOracleSurveyProbes(),
1313
+ introspect: (ctx) => withRowCounts(ctx, introspectOracle(ctx.query), quoteAnsi),
1024
1314
  setup: (params, ctx, config) => runSetupFlow(oracleSetupFlow, params, ctx, config),
1025
1315
  async checkConnection(params, _config) {
1026
1316
  return checkOracleConnection(