@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,6 +1,11 @@
1
1
  var __getOwnPropNames = Object.getOwnPropertyNames;
2
- var __esm = (fn, res) => function __init() {
3
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
2
+ var __esm = (fn, res, err) => function __init() {
3
+ if (err) throw err[0];
4
+ try {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ } catch (e) {
7
+ throw err = [e], e;
8
+ }
4
9
  };
5
10
 
6
11
  // ../connectors/src/parameter-definition.ts
@@ -148,6 +153,15 @@ var ConnectorPlugin = class _ConnectorPlugin {
148
153
  * `runSetupFlow` from `setup-flow.ts`.
149
154
  */
150
155
  setup;
156
+ /**
157
+ * 統一 introspection。SQL DB の `information_schema` / `SHOW` / `DESCRIBE` や SaaS の
158
+ * メタデータ API といった取得経路の差を吸収し、tables / columns / PK / FK を共通型
159
+ * {@link IntrospectedSchema} へ正規化して返す。取得経路は {@link IntrospectContext} で
160
+ * 抽象化され、SQL DB は `ctx.query`、SaaS は `ctx.proxyFetch` + `ctx.params` を使う。
161
+ * `ctx.profile === true` のときは L2 プロファイル(行数・NULL 率等)も best-effort で埋める。
162
+ * coding-agent の `DataContextIndex` がデータ地図を構築する際に使う。
163
+ */
164
+ introspect;
151
165
  /**
152
166
  * Opt-out of the default "verify before save" behavior on connection
153
167
  * creation. The backend invokes `checkConnection` synchronously while
@@ -162,6 +176,14 @@ var ConnectorPlugin = class _ConnectorPlugin {
162
176
  * the default verify-on-create behavior without opt-in.
163
177
  */
164
178
  skipConnectionCheckOnCreate;
179
+ /**
180
+ * 決定論的な「立ち上げ survey」レシピ(ルールベース prewarm 基盤)。実装した connector は、LLM を使わずに
181
+ * read-only で自分を survey する probe 列(テーブル/列メタデータ → 小サンプル)を宣言する。coding-agent の
182
+ * recon driver がこれを読み、connector ツールの `execute` を直接呼んで結果を seed にする。未実装の connector は
183
+ * driver 側で safely skip される(その connection は listConnections のみ seed)。標準 `information_schema` を
184
+ * 持つ SQL connector は `makeStandardSqlSurveyProbes` を使える。**probe は必ず read-only**。
185
+ */
186
+ surveyProbes;
165
187
  constructor(config) {
166
188
  this.slug = config.slug;
167
189
  this.authType = config.authType;
@@ -179,7 +201,9 @@ var ConnectorPlugin = class _ConnectorPlugin {
179
201
  this.query = config.query;
180
202
  this.checkConnection = config.checkConnection;
181
203
  this.setup = config.setup;
204
+ this.introspect = config.introspect;
182
205
  this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
206
+ this.surveyProbes = config.surveyProbes;
183
207
  }
184
208
  get connectorKey() {
185
209
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -187,7 +211,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
187
211
  /**
188
212
  * Create tools for connections that belong to this connector.
189
213
  * Filters connections by connectorKey internally.
190
- * Returns tools keyed as `${connectorKey}_${toolName}`.
214
+ * Returns tools keyed as `connector_${connectorKey}_${toolName}`.
191
215
  */
192
216
  createTools(connections, config, opts) {
193
217
  const myConnections = connections.filter(
@@ -197,7 +221,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
197
221
  for (const t of Object.values(this.tools)) {
198
222
  const tool = t.createTool(myConnections, config);
199
223
  const originalToModelOutput = tool.toModelOutput;
200
- result[`${this.connectorKey}_${t.name}`] = {
224
+ result[`connector_${this.connectorKey}_${t.name}`] = {
201
225
  ...tool,
202
226
  toModelOutput: async (options) => {
203
227
  if (!originalToModelOutput) {
@@ -217,25 +241,25 @@ var ConnectorPlugin = class _ConnectorPlugin {
217
241
  if (authType) return `${slug}-${authType}`;
218
242
  const LEGACY_NULL_AUTH_TYPE_MAP = {
219
243
  // user-password
220
- "postgresql": "user-password",
221
- "mysql": "user-password",
222
- "clickhouse": "user-password",
223
- "kintone": "user-password",
244
+ postgresql: "user-password",
245
+ mysql: "user-password",
246
+ clickhouse: "user-password",
247
+ kintone: "user-password",
224
248
  "squadbase-db": "user-password",
225
249
  // service-account
226
- "snowflake": "service-account",
227
- "bigquery": "service-account",
250
+ snowflake: "service-account",
251
+ bigquery: "service-account",
228
252
  "google-analytics": "service-account",
229
253
  "google-calendar": "service-account",
230
254
  "aws-athena": "service-account",
231
- "redshift": "service-account",
255
+ redshift: "service-account",
232
256
  // api-key
233
- "databricks": "api-key",
234
- "dbt": "api-key",
235
- "airtable": "api-key",
236
- "openai": "api-key",
237
- "gemini": "api-key",
238
- "anthropic": "api-key",
257
+ databricks: "api-key",
258
+ dbt: "api-key",
259
+ airtable: "api-key",
260
+ openai: "api-key",
261
+ gemini: "api-key",
262
+ anthropic: "api-key",
239
263
  "wix-store": "api-key"
240
264
  };
241
265
  const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
@@ -478,10 +502,10 @@ var airtableOauthOnboarding = new ConnectorOnboarding({
478
502
  - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
479
503
  },
480
504
  dataOverviewInstructions: {
481
- en: `1. Call airtable-oauth_request with GET /meta/bases/{baseId}/tables to list tables and their fields
482
- 2. Call airtable-oauth_request with GET /{baseId}/{tableIdOrName}?maxRecords=5 to sample records from key tables`,
483
- ja: `1. airtable-oauth_request \u3067 GET /meta/bases/{baseId}/tables \u3092\u547C\u3073\u51FA\u3057\u3001\u30C6\u30FC\u30D6\u30EB\u3068\u30D5\u30A3\u30FC\u30EB\u30C9\u306E\u4E00\u89A7\u3092\u53D6\u5F97
484
- 2. airtable-oauth_request \u3067 GET /{baseId}/{tableIdOrName}?maxRecords=5 \u3092\u547C\u3073\u51FA\u3057\u3001\u4E3B\u8981\u30C6\u30FC\u30D6\u30EB\u306E\u30EC\u30B3\u30FC\u30C9\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0`
505
+ en: `1. Call connector_airtable-oauth_request with GET /meta/bases/{baseId}/tables to list tables and their fields
506
+ 2. Call connector_airtable-oauth_request with GET /{baseId}/{tableIdOrName}?maxRecords=5 to sample records from key tables`,
507
+ ja: `1. connector_airtable-oauth_request \u3067 GET /meta/bases/{baseId}/tables \u3092\u547C\u3073\u51FA\u3057\u3001\u30C6\u30FC\u30D6\u30EB\u3068\u30D5\u30A3\u30FC\u30EB\u30C9\u306E\u4E00\u89A7\u3092\u53D6\u5F97
508
+ 2. connector_airtable-oauth_request \u3067 GET /{baseId}/{tableIdOrName}?maxRecords=5 \u3092\u547C\u3073\u51FA\u3057\u3001\u4E3B\u8981\u30C6\u30FC\u30D6\u30EB\u306E\u30EC\u30B3\u30FC\u30C9\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0`
485
509
  }
486
510
  });
487
511
 
@@ -517,6 +541,26 @@ async function listTables(proxyFetch, baseId) {
517
541
  const data = await res.json();
518
542
  return data.tables ?? [];
519
543
  }
544
+ async function listSampleRecords(proxyFetch, baseId, tableId) {
545
+ try {
546
+ const res = await apiFetch(
547
+ proxyFetch,
548
+ `/${baseId}/${encodeURIComponent(tableId)}?maxRecords=3`
549
+ );
550
+ if (!res.ok) return [];
551
+ const data = await res.json();
552
+ return data.records ?? [];
553
+ } catch {
554
+ return [];
555
+ }
556
+ }
557
+ function formatCell(val) {
558
+ if (val == null) return "";
559
+ if (typeof val === "string") return val.length > 40 ? `${val.slice(0, 40)}\u2026` : val;
560
+ if (Array.isArray(val)) return `[${val.length} items]`;
561
+ if (typeof val === "object") return JSON.stringify(val).slice(0, 40);
562
+ return String(val);
563
+ }
520
564
  var airtableOauthSetupFlow = {
521
565
  initialState: () => ({}),
522
566
  steps: [
@@ -588,6 +632,26 @@ var airtableOauthSetupFlow = {
588
632
  sections.push(`| ${f.name} | ${f.type} | ${description || "-"} |`);
589
633
  }
590
634
  sections.push("");
635
+ const records = await listSampleRecords(
636
+ rt.config.proxyFetch,
637
+ baseId,
638
+ table.id
639
+ );
640
+ if (records.length > 0) {
641
+ const sampleFields = (table.fields ?? []).slice(0, 5).map((f) => f.name);
642
+ if (sampleFields.length > 0) {
643
+ sections.push(`##### Sample records (${records.length})`, "");
644
+ sections.push(`| ${sampleFields.join(" | ")} |`);
645
+ sections.push(`| ${sampleFields.map(() => "---").join(" | ")} |`);
646
+ for (const record of records) {
647
+ const cells = sampleFields.map(
648
+ (name) => formatCell(record.fields?.[name]).replace(/\|/g, "\\|")
649
+ );
650
+ sections.push(`| ${cells.join(" | ")} |`);
651
+ }
652
+ sections.push("");
653
+ }
654
+ }
591
655
  }
592
656
  return sections.join("\n");
593
657
  }
@@ -617,7 +681,7 @@ var airtableOauthConnector = new ConnectorPlugin({
617
681
  systemPrompt: {
618
682
  en: `### Tools
619
683
 
620
- - \`airtable-oauth_request\`: The only way to call the Airtable REST API. Use it to list tables, query/create/update/delete records. Authentication is configured automatically via OAuth. The {baseId} placeholder in paths is automatically replaced with the configured default base ID.
684
+ - \`connector_airtable-oauth_request\`: The only way to call the Airtable REST API. Use it to list tables, query/create/update/delete records. Authentication is configured automatically via OAuth. The {baseId} placeholder in paths is automatically replaced with the configured default base ID.
621
685
 
622
686
  ### Airtable API Reference
623
687
 
@@ -665,7 +729,7 @@ const recordsData = await records.json();
665
729
  \`\`\``,
666
730
  ja: `### \u30C4\u30FC\u30EB
667
731
 
668
- - \`airtable-oauth_request\`: Airtable REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u306E\u53D6\u5F97\u3001\u30EC\u30B3\u30FC\u30C9\u306E\u691C\u7D22\u30FB\u4F5C\u6210\u30FB\u66F4\u65B0\u30FB\u524A\u9664\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002OAuth\u7D4C\u7531\u3067\u8A8D\u8A3C\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{baseId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u8A2D\u5B9A\u6E08\u307F\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30D9\u30FC\u30B9ID\u3067\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002
732
+ - \`connector_airtable-oauth_request\`: Airtable REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u306E\u53D6\u5F97\u3001\u30EC\u30B3\u30FC\u30C9\u306E\u691C\u7D22\u30FB\u4F5C\u6210\u30FB\u66F4\u65B0\u30FB\u524A\u9664\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002OAuth\u7D4C\u7531\u3067\u8A8D\u8A3C\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{baseId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u8A2D\u5B9A\u6E08\u307F\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30D9\u30FC\u30B9ID\u3067\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002
669
733
 
670
734
  ### Airtable API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
671
735
 
@@ -1,6 +1,11 @@
1
1
  var __getOwnPropNames = Object.getOwnPropertyNames;
2
- var __esm = (fn, res) => function __init() {
3
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
2
+ var __esm = (fn, res, err) => function __init() {
3
+ if (err) throw err[0];
4
+ try {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ } catch (e) {
7
+ throw err = [e], e;
8
+ }
4
9
  };
5
10
 
6
11
  // ../connectors/src/parameter-definition.ts
@@ -278,6 +283,15 @@ var ConnectorPlugin = class _ConnectorPlugin {
278
283
  * `runSetupFlow` from `setup-flow.ts`.
279
284
  */
280
285
  setup;
286
+ /**
287
+ * 統一 introspection。SQL DB の `information_schema` / `SHOW` / `DESCRIBE` や SaaS の
288
+ * メタデータ API といった取得経路の差を吸収し、tables / columns / PK / FK を共通型
289
+ * {@link IntrospectedSchema} へ正規化して返す。取得経路は {@link IntrospectContext} で
290
+ * 抽象化され、SQL DB は `ctx.query`、SaaS は `ctx.proxyFetch` + `ctx.params` を使う。
291
+ * `ctx.profile === true` のときは L2 プロファイル(行数・NULL 率等)も best-effort で埋める。
292
+ * coding-agent の `DataContextIndex` がデータ地図を構築する際に使う。
293
+ */
294
+ introspect;
281
295
  /**
282
296
  * Opt-out of the default "verify before save" behavior on connection
283
297
  * creation. The backend invokes `checkConnection` synchronously while
@@ -292,6 +306,14 @@ var ConnectorPlugin = class _ConnectorPlugin {
292
306
  * the default verify-on-create behavior without opt-in.
293
307
  */
294
308
  skipConnectionCheckOnCreate;
309
+ /**
310
+ * 決定論的な「立ち上げ survey」レシピ(ルールベース prewarm 基盤)。実装した connector は、LLM を使わずに
311
+ * read-only で自分を survey する probe 列(テーブル/列メタデータ → 小サンプル)を宣言する。coding-agent の
312
+ * recon driver がこれを読み、connector ツールの `execute` を直接呼んで結果を seed にする。未実装の connector は
313
+ * driver 側で safely skip される(その connection は listConnections のみ seed)。標準 `information_schema` を
314
+ * 持つ SQL connector は `makeStandardSqlSurveyProbes` を使える。**probe は必ず read-only**。
315
+ */
316
+ surveyProbes;
295
317
  constructor(config) {
296
318
  this.slug = config.slug;
297
319
  this.authType = config.authType;
@@ -309,7 +331,9 @@ var ConnectorPlugin = class _ConnectorPlugin {
309
331
  this.query = config.query;
310
332
  this.checkConnection = config.checkConnection;
311
333
  this.setup = config.setup;
334
+ this.introspect = config.introspect;
312
335
  this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
336
+ this.surveyProbes = config.surveyProbes;
313
337
  }
314
338
  get connectorKey() {
315
339
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -317,7 +341,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
317
341
  /**
318
342
  * Create tools for connections that belong to this connector.
319
343
  * Filters connections by connectorKey internally.
320
- * Returns tools keyed as `${connectorKey}_${toolName}`.
344
+ * Returns tools keyed as `connector_${connectorKey}_${toolName}`.
321
345
  */
322
346
  createTools(connections, config, opts) {
323
347
  const myConnections = connections.filter(
@@ -327,7 +351,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
327
351
  for (const t of Object.values(this.tools)) {
328
352
  const tool = t.createTool(myConnections, config);
329
353
  const originalToModelOutput = tool.toModelOutput;
330
- result[`${this.connectorKey}_${t.name}`] = {
354
+ result[`connector_${this.connectorKey}_${t.name}`] = {
331
355
  ...tool,
332
356
  toModelOutput: async (options) => {
333
357
  if (!originalToModelOutput) {
@@ -347,25 +371,25 @@ var ConnectorPlugin = class _ConnectorPlugin {
347
371
  if (authType) return `${slug}-${authType}`;
348
372
  const LEGACY_NULL_AUTH_TYPE_MAP = {
349
373
  // user-password
350
- "postgresql": "user-password",
351
- "mysql": "user-password",
352
- "clickhouse": "user-password",
353
- "kintone": "user-password",
374
+ postgresql: "user-password",
375
+ mysql: "user-password",
376
+ clickhouse: "user-password",
377
+ kintone: "user-password",
354
378
  "squadbase-db": "user-password",
355
379
  // service-account
356
- "snowflake": "service-account",
357
- "bigquery": "service-account",
380
+ snowflake: "service-account",
381
+ bigquery: "service-account",
358
382
  "google-analytics": "service-account",
359
383
  "google-calendar": "service-account",
360
384
  "aws-athena": "service-account",
361
- "redshift": "service-account",
385
+ redshift: "service-account",
362
386
  // api-key
363
- "databricks": "api-key",
364
- "dbt": "api-key",
365
- "airtable": "api-key",
366
- "openai": "api-key",
367
- "gemini": "api-key",
368
- "anthropic": "api-key",
387
+ databricks: "api-key",
388
+ dbt: "api-key",
389
+ airtable: "api-key",
390
+ openai: "api-key",
391
+ gemini: "api-key",
392
+ anthropic: "api-key",
369
393
  "wix-store": "api-key"
370
394
  };
371
395
  const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
@@ -457,10 +481,10 @@ var AUTH_TYPES = {
457
481
  // ../connectors/src/connectors/airtable/setup.ts
458
482
  var airtableOnboarding = new ConnectorOnboarding({
459
483
  dataOverviewInstructions: {
460
- en: `1. Call airtable_request with GET meta/bases/{baseId}/tables to list tables and their fields
461
- 2. Call airtable_request with GET {baseId}/{tableIdOrName}?maxRecords=5 to sample records from key tables`,
462
- ja: `1. airtable_request \u3067 GET meta/bases/{baseId}/tables \u3092\u547C\u3073\u51FA\u3057\u3001\u30C6\u30FC\u30D6\u30EB\u3068\u30D5\u30A3\u30FC\u30EB\u30C9\u306E\u4E00\u89A7\u3092\u53D6\u5F97
463
- 2. airtable_request \u3067 GET {baseId}/{tableIdOrName}?maxRecords=5 \u3092\u547C\u3073\u51FA\u3057\u3001\u4E3B\u8981\u30C6\u30FC\u30D6\u30EB\u306E\u30EC\u30B3\u30FC\u30C9\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0`
484
+ en: `1. Call connector_airtable_request with GET meta/bases/{baseId}/tables to list tables and their fields
485
+ 2. Call connector_airtable_request with GET {baseId}/{tableIdOrName}?maxRecords=5 to sample records from key tables`,
486
+ ja: `1. connector_airtable_request \u3067 GET meta/bases/{baseId}/tables \u3092\u547C\u3073\u51FA\u3057\u3001\u30C6\u30FC\u30D6\u30EB\u3068\u30D5\u30A3\u30FC\u30EB\u30C9\u306E\u4E00\u89A7\u3092\u53D6\u5F97
487
+ 2. connector_airtable_request \u3067 GET {baseId}/{tableIdOrName}?maxRecords=5 \u3092\u547C\u3073\u51FA\u3057\u3001\u4E3B\u8981\u30C6\u30FC\u30D6\u30EB\u306E\u30EC\u30B3\u30FC\u30C9\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0`
464
488
  }
465
489
  });
466
490
 
@@ -498,6 +522,26 @@ async function listTables(params, baseId) {
498
522
  const data = await res.json();
499
523
  return data.tables ?? [];
500
524
  }
525
+ async function listSampleRecords(params, baseId, tableId) {
526
+ try {
527
+ const res = await apiFetch(
528
+ params,
529
+ `/${baseId}/${encodeURIComponent(tableId)}?maxRecords=3`
530
+ );
531
+ if (!res.ok) return [];
532
+ const data = await res.json();
533
+ return data.records ?? [];
534
+ } catch {
535
+ return [];
536
+ }
537
+ }
538
+ function formatCell(val) {
539
+ if (val == null) return "";
540
+ if (typeof val === "string") return val.length > 40 ? `${val.slice(0, 40)}\u2026` : val;
541
+ if (Array.isArray(val)) return `[${val.length} items]`;
542
+ if (typeof val === "object") return JSON.stringify(val).slice(0, 40);
543
+ return String(val);
544
+ }
501
545
  var airtableSetupFlow = {
502
546
  initialState: () => ({}),
503
547
  steps: [
@@ -569,11 +613,61 @@ var airtableSetupFlow = {
569
613
  sections.push(`| ${f.name} | ${f.type} | ${description || "-"} |`);
570
614
  }
571
615
  sections.push("");
616
+ const records = await listSampleRecords(rt.params, baseId, table.id);
617
+ if (records.length > 0) {
618
+ const sampleFields = (table.fields ?? []).slice(0, 5).map((f) => f.name);
619
+ if (sampleFields.length > 0) {
620
+ sections.push(`##### Sample records (${records.length})`, "");
621
+ sections.push(`| ${sampleFields.join(" | ")} |`);
622
+ sections.push(`| ${sampleFields.map(() => "---").join(" | ")} |`);
623
+ for (const record of records) {
624
+ const cells = sampleFields.map(
625
+ (name) => formatCell(record.fields?.[name]).replace(/\|/g, "\\|")
626
+ );
627
+ sections.push(`| ${cells.join(" | ")} |`);
628
+ }
629
+ sections.push("");
630
+ }
631
+ }
572
632
  }
573
633
  return sections.join("\n");
574
634
  }
575
635
  };
576
636
 
637
+ // ../connectors/src/connectors/airtable/introspect.ts
638
+ async function introspectAirtable(ctx) {
639
+ const baseId = ctx.params[parameters.baseId.slug];
640
+ if (!baseId) return { tables: [] };
641
+ const res = await apiFetch(ctx.params, `/meta/bases/${baseId}/tables`);
642
+ if (!res.ok) {
643
+ throw new Error(`airtable: meta/tables failed (${res.status})`);
644
+ }
645
+ const data = await res.json();
646
+ const atTables = data.tables ?? [];
647
+ const tableNameById = new Map(atTables.map((t) => [t.id, t.name]));
648
+ const tables = atTables.map((t) => {
649
+ const columns = (t.fields ?? []).map((f) => {
650
+ const column = {
651
+ name: f.name,
652
+ dataType: f.type,
653
+ nullable: true
654
+ };
655
+ if (f.id === t.primaryFieldId) column.isPrimaryKey = true;
656
+ const linkedId = f.options?.linkedTableId;
657
+ if (f.type === "multipleRecordLinks" && linkedId) {
658
+ column.foreignKey = {
659
+ schema: baseId,
660
+ table: tableNameById.get(linkedId) ?? linkedId,
661
+ column: ""
662
+ };
663
+ }
664
+ return column;
665
+ });
666
+ return { schema: baseId, name: t.name, type: "table", columns };
667
+ });
668
+ return { tables };
669
+ }
670
+
577
671
  // ../connectors/src/connectors/airtable/tools/request.ts
578
672
  import { z } from "zod";
579
673
  var BASE_URL3 = "https://api.airtable.com/v0/";
@@ -660,7 +754,7 @@ var airtableConnector = new ConnectorPlugin({
660
754
  systemPrompt: {
661
755
  en: `### Tools
662
756
 
663
- - \`airtable_request\`: The only way to call the Airtable REST API. Use it to list tables, read records, and create/update records. The \`{baseId}\` placeholder in the path is automatically replaced. See the Airtable API Reference below for available endpoints and query parameters.
757
+ - \`connector_airtable_request\`: The only way to call the Airtable REST API. Use it to list tables, read records, and create/update records. The \`{baseId}\` placeholder in the path is automatically replaced. See the Airtable API Reference below for available endpoints and query parameters.
664
758
 
665
759
  ### Business Logic
666
760
 
@@ -719,7 +813,7 @@ export default async function handler(c: Context) {
719
813
  - If the response contains an \`offset\`, fetch the next page by appending \`?offset={offset}\` to the next request`,
720
814
  ja: `### \u30C4\u30FC\u30EB
721
815
 
722
- - \`airtable_request\`: Airtable REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u306E\u53D6\u5F97\u3001\u30EC\u30B3\u30FC\u30C9\u306E\u8AAD\u307F\u53D6\u308A\u3001\u30EC\u30B3\u30FC\u30C9\u306E\u4F5C\u6210\u30FB\u66F4\u65B0\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E \`{baseId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u306F\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002\u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3068\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u306F\u4E0B\u90E8\u306E\u300CAirtable API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
816
+ - \`connector_airtable_request\`: Airtable REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u306E\u53D6\u5F97\u3001\u30EC\u30B3\u30FC\u30C9\u306E\u8AAD\u307F\u53D6\u308A\u3001\u30EC\u30B3\u30FC\u30C9\u306E\u4F5C\u6210\u30FB\u66F4\u65B0\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E \`{baseId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u306F\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002\u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3068\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u306F\u4E0B\u90E8\u306E\u300CAirtable API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
723
817
 
724
818
  ### Business Logic
725
819
 
@@ -778,6 +872,19 @@ export default async function handler(c: Context) {
778
872
  - \u30EC\u30B9\u30DD\u30F3\u30B9\u306B \`offset\` \u304C\u542B\u307E\u308C\u308B\u5834\u5408\u3001\u6B21\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u306B \`?offset={offset}\` \u3092\u8FFD\u52A0\u3057\u3066\u6B21\u306E\u30DA\u30FC\u30B8\u3092\u53D6\u5F97\u3057\u307E\u3059`
779
873
  },
780
874
  tools,
875
+ // 決定論的な立ち上げ survey(ルールベース prewarm)。テーブル一覧(フィールド定義込み)を 1 GET で取得。
876
+ // `{baseId}` は request ツールが自動置換する。read-only。
877
+ surveyProbes: (_connectionId, prior) => prior.length === 0 ? [
878
+ {
879
+ tool: "request",
880
+ input: {
881
+ method: "GET",
882
+ path: "meta/bases/{baseId}/tables",
883
+ toolUseIntent: "List Airtable tables"
884
+ }
885
+ }
886
+ ] : [],
887
+ introspect: (ctx) => introspectAirtable(ctx),
781
888
  setup: (params, ctx, config) => runSetupFlow(airtableSetupFlow, params, ctx, config),
782
889
  async checkConnection(params, _config) {
783
890
  const apiKey = params[parameters.apiKey.slug];
@@ -1,6 +1,11 @@
1
1
  var __getOwnPropNames = Object.getOwnPropertyNames;
2
- var __esm = (fn, res) => function __init() {
3
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
2
+ var __esm = (fn, res, err) => function __init() {
3
+ if (err) throw err[0];
4
+ try {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ } catch (e) {
7
+ throw err = [e], e;
8
+ }
4
9
  };
5
10
 
6
11
  // ../connectors/src/parameter-definition.ts
@@ -238,6 +243,15 @@ var ConnectorPlugin = class _ConnectorPlugin {
238
243
  * `runSetupFlow` from `setup-flow.ts`.
239
244
  */
240
245
  setup;
246
+ /**
247
+ * 統一 introspection。SQL DB の `information_schema` / `SHOW` / `DESCRIBE` や SaaS の
248
+ * メタデータ API といった取得経路の差を吸収し、tables / columns / PK / FK を共通型
249
+ * {@link IntrospectedSchema} へ正規化して返す。取得経路は {@link IntrospectContext} で
250
+ * 抽象化され、SQL DB は `ctx.query`、SaaS は `ctx.proxyFetch` + `ctx.params` を使う。
251
+ * `ctx.profile === true` のときは L2 プロファイル(行数・NULL 率等)も best-effort で埋める。
252
+ * coding-agent の `DataContextIndex` がデータ地図を構築する際に使う。
253
+ */
254
+ introspect;
241
255
  /**
242
256
  * Opt-out of the default "verify before save" behavior on connection
243
257
  * creation. The backend invokes `checkConnection` synchronously while
@@ -252,6 +266,14 @@ var ConnectorPlugin = class _ConnectorPlugin {
252
266
  * the default verify-on-create behavior without opt-in.
253
267
  */
254
268
  skipConnectionCheckOnCreate;
269
+ /**
270
+ * 決定論的な「立ち上げ survey」レシピ(ルールベース prewarm 基盤)。実装した connector は、LLM を使わずに
271
+ * read-only で自分を survey する probe 列(テーブル/列メタデータ → 小サンプル)を宣言する。coding-agent の
272
+ * recon driver がこれを読み、connector ツールの `execute` を直接呼んで結果を seed にする。未実装の connector は
273
+ * driver 側で safely skip される(その connection は listConnections のみ seed)。標準 `information_schema` を
274
+ * 持つ SQL connector は `makeStandardSqlSurveyProbes` を使える。**probe は必ず read-only**。
275
+ */
276
+ surveyProbes;
255
277
  constructor(config) {
256
278
  this.slug = config.slug;
257
279
  this.authType = config.authType;
@@ -269,7 +291,9 @@ var ConnectorPlugin = class _ConnectorPlugin {
269
291
  this.query = config.query;
270
292
  this.checkConnection = config.checkConnection;
271
293
  this.setup = config.setup;
294
+ this.introspect = config.introspect;
272
295
  this.skipConnectionCheckOnCreate = config.skipConnectionCheckOnCreate;
296
+ this.surveyProbes = config.surveyProbes;
273
297
  }
274
298
  get connectorKey() {
275
299
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -277,7 +301,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
277
301
  /**
278
302
  * Create tools for connections that belong to this connector.
279
303
  * Filters connections by connectorKey internally.
280
- * Returns tools keyed as `${connectorKey}_${toolName}`.
304
+ * Returns tools keyed as `connector_${connectorKey}_${toolName}`.
281
305
  */
282
306
  createTools(connections, config, opts) {
283
307
  const myConnections = connections.filter(
@@ -287,7 +311,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
287
311
  for (const t of Object.values(this.tools)) {
288
312
  const tool = t.createTool(myConnections, config);
289
313
  const originalToModelOutput = tool.toModelOutput;
290
- result[`${this.connectorKey}_${t.name}`] = {
314
+ result[`connector_${this.connectorKey}_${t.name}`] = {
291
315
  ...tool,
292
316
  toModelOutput: async (options) => {
293
317
  if (!originalToModelOutput) {
@@ -307,25 +331,25 @@ var ConnectorPlugin = class _ConnectorPlugin {
307
331
  if (authType) return `${slug}-${authType}`;
308
332
  const LEGACY_NULL_AUTH_TYPE_MAP = {
309
333
  // user-password
310
- "postgresql": "user-password",
311
- "mysql": "user-password",
312
- "clickhouse": "user-password",
313
- "kintone": "user-password",
334
+ postgresql: "user-password",
335
+ mysql: "user-password",
336
+ clickhouse: "user-password",
337
+ kintone: "user-password",
314
338
  "squadbase-db": "user-password",
315
339
  // service-account
316
- "snowflake": "service-account",
317
- "bigquery": "service-account",
340
+ snowflake: "service-account",
341
+ bigquery: "service-account",
318
342
  "google-analytics": "service-account",
319
343
  "google-calendar": "service-account",
320
344
  "aws-athena": "service-account",
321
- "redshift": "service-account",
345
+ redshift: "service-account",
322
346
  // api-key
323
- "databricks": "api-key",
324
- "dbt": "api-key",
325
- "airtable": "api-key",
326
- "openai": "api-key",
327
- "gemini": "api-key",
328
- "anthropic": "api-key",
347
+ databricks: "api-key",
348
+ dbt: "api-key",
349
+ airtable: "api-key",
350
+ openai: "api-key",
351
+ gemini: "api-key",
352
+ anthropic: "api-key",
329
353
  "wix-store": "api-key"
330
354
  };
331
355
  const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
@@ -413,13 +437,13 @@ var AUTH_TYPES = {
413
437
  var amplitudeOnboarding = new ConnectorOnboarding({
414
438
  dataOverviewInstructions: {
415
439
  en: `1. Check the connection's region parameter: use https://analytics.eu.amplitude.com for EU, https://amplitude.com for US (default)
416
- 2. Call amplitude_request with GET {baseUrl}/api/2/events/list to list available event types
417
- 3. Call amplitude_request with GET {baseUrl}/api/2/export?start=YYYYMMDDTHH&end=YYYYMMDDTHH to export raw event data for a time range
440
+ 2. Call connector_amplitude_request with GET {baseUrl}/api/2/events/list to list available event types
441
+ 3. Call connector_amplitude_request with GET {baseUrl}/api/2/export?start=YYYYMMDDTHH&end=YYYYMMDDTHH to export raw event data for a time range
418
442
  4. Use GET {baseUrl}/api/2/usersearch?user=QUERY and GET {baseUrl}/api/2/useractivity?user=AMPLITUDE_ID to explore user-level data
419
443
  NOTE: The Dashboard REST API endpoints (events/segmentation, funnels, retention, etc.) require a paid Growth or Enterprise plan. If you get a 403 Forbidden error, fall back to the Export API (/api/2/export) to retrieve raw events and perform aggregation in code instead.`,
420
444
  ja: `1. \u63A5\u7D9A\u306Eregion\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u78BA\u8A8D\uFF1AEU\u306E\u5834\u5408\u306F https://analytics.eu.amplitude.com\u3001US\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\uFF09\u306E\u5834\u5408\u306F https://amplitude.com \u3092\u4F7F\u7528
421
- 2. amplitude_request \u3067 GET {baseUrl}/api/2/events/list \u3092\u547C\u3073\u51FA\u3057\u3001\u5229\u7528\u53EF\u80FD\u306A\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u4E00\u89A7\u3092\u53D6\u5F97
422
- 3. amplitude_request \u3067 GET {baseUrl}/api/2/export?start=YYYYMMDDTHH&end=YYYYMMDDTHH \u3092\u547C\u3073\u51FA\u3057\u3001\u6307\u5B9A\u671F\u9593\u306E\u751F\u30A4\u30D9\u30F3\u30C8\u30C7\u30FC\u30BF\u3092\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8
445
+ 2. connector_amplitude_request \u3067 GET {baseUrl}/api/2/events/list \u3092\u547C\u3073\u51FA\u3057\u3001\u5229\u7528\u53EF\u80FD\u306A\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u4E00\u89A7\u3092\u53D6\u5F97
446
+ 3. connector_amplitude_request \u3067 GET {baseUrl}/api/2/export?start=YYYYMMDDTHH&end=YYYYMMDDTHH \u3092\u547C\u3073\u51FA\u3057\u3001\u6307\u5B9A\u671F\u9593\u306E\u751F\u30A4\u30D9\u30F3\u30C8\u30C7\u30FC\u30BF\u3092\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8
423
447
  4. GET {baseUrl}/api/2/usersearch?user=QUERY \u3084 GET {baseUrl}/api/2/useractivity?user=AMPLITUDE_ID \u3067\u30E6\u30FC\u30B6\u30FC\u30EC\u30D9\u30EB\u306E\u30C7\u30FC\u30BF\u3092\u63A2\u7D22
424
448
  \u6CE8\u610F: Dashboard REST API\uFF08events/segmentation\u3001funnels\u3001retention\u7B49\uFF09\u306F\u6709\u6599\u306EGrowth\u307E\u305F\u306FEnterprise\u30D7\u30E9\u30F3\u304C\u5FC5\u8981\u3067\u3059\u3002403 Forbidden\u30A8\u30E9\u30FC\u304C\u8FD4\u3055\u308C\u305F\u5834\u5408\u306F\u3001Export API\uFF08/api/2/export\uFF09\u3067\u751F\u30A4\u30D9\u30F3\u30C8\u3092\u53D6\u5F97\u3057\u3001\u30B3\u30FC\u30C9\u4E0A\u3067\u96C6\u8A08\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002`
425
449
  }
@@ -645,7 +669,7 @@ var amplitudeConnector = new ConnectorPlugin({
645
669
  systemPrompt: {
646
670
  en: `### Tools
647
671
 
648
- - \`amplitude_request\`: The only way to call the Amplitude REST API. Use it for event segmentation, listing event types, user search, user activity, and data export. Authentication (Basic auth with API Key + Secret Key) is configured automatically. Provide the full URL including query parameters \u2014 the base URL varies by endpoint.
672
+ - \`connector_amplitude_request\`: The only way to call the Amplitude REST API. Use it for event segmentation, listing event types, user search, user activity, and data export. Authentication (Basic auth with API Key + Secret Key) is configured automatically. Provide the full URL including query parameters \u2014 the base URL varies by endpoint.
649
673
 
650
674
  ### Business Logic
651
675
 
@@ -709,7 +733,7 @@ IMPORTANT: Always start with the endpoints available on all plans. If you need a
709
733
  - \`g\` \u2014 Group by property`,
710
734
  ja: `### \u30C4\u30FC\u30EB
711
735
 
712
- - \`amplitude_request\`: Amplitude REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30A4\u30D9\u30F3\u30C8\u30BB\u30B0\u30E1\u30F3\u30C6\u30FC\u30B7\u30E7\u30F3\u3001\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u4E00\u89A7\u3001\u30E6\u30FC\u30B6\u30FC\u691C\u7D22\u3001\u30E6\u30FC\u30B6\u30FC\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u3001\u30C7\u30FC\u30BF\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08API Key + Secret Key\u306B\u3088\u308BBasic\u8A8D\u8A3C\uFF09\u306F\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u542B\u3080\u5B8C\u5168\u306AURL\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044 \u2014 \u30D9\u30FC\u30B9URL\u306F\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306B\u3088\u3063\u3066\u7570\u306A\u308A\u307E\u3059\u3002
736
+ - \`connector_amplitude_request\`: Amplitude REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30A4\u30D9\u30F3\u30C8\u30BB\u30B0\u30E1\u30F3\u30C6\u30FC\u30B7\u30E7\u30F3\u3001\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u4E00\u89A7\u3001\u30E6\u30FC\u30B6\u30FC\u691C\u7D22\u3001\u30E6\u30FC\u30B6\u30FC\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u3001\u30C7\u30FC\u30BF\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08API Key + Secret Key\u306B\u3088\u308BBasic\u8A8D\u8A3C\uFF09\u306F\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u542B\u3080\u5B8C\u5168\u306AURL\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044 \u2014 \u30D9\u30FC\u30B9URL\u306F\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306B\u3088\u3063\u3066\u7570\u306A\u308A\u307E\u3059\u3002
713
737
 
714
738
  ### Business Logic
715
739