@squadbase/vite-server 0.1.9-dev.87dd3f7 → 0.1.9-dev.a120137

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 (44) hide show
  1. package/dist/cli/index.js +73157 -60061
  2. package/dist/connectors/asana.js +15 -2
  3. package/dist/connectors/aws-billing.d.ts +5 -0
  4. package/dist/connectors/aws-billing.js +29843 -0
  5. package/dist/connectors/azure-sql.d.ts +5 -0
  6. package/dist/connectors/azure-sql.js +657 -0
  7. package/dist/connectors/clickup.d.ts +5 -0
  8. package/dist/connectors/clickup.js +850 -0
  9. package/dist/connectors/freshdesk.d.ts +5 -0
  10. package/dist/connectors/freshdesk.js +842 -0
  11. package/dist/connectors/freshsales.d.ts +5 -0
  12. package/dist/connectors/freshsales.js +867 -0
  13. package/dist/connectors/freshservice.d.ts +5 -0
  14. package/dist/connectors/freshservice.js +813 -0
  15. package/dist/connectors/github.d.ts +5 -0
  16. package/dist/connectors/github.js +963 -0
  17. package/dist/connectors/gmail-oauth.js +15 -2
  18. package/dist/connectors/gmail.js +23 -14
  19. package/dist/connectors/google-audit-log.js +25 -14
  20. package/dist/connectors/google-calendar-oauth.js +18 -2
  21. package/dist/connectors/google-calendar.js +40 -26
  22. package/dist/connectors/google-docs.js +18 -2
  23. package/dist/connectors/google-drive.js +15 -2
  24. package/dist/connectors/google-search-console-oauth.d.ts +5 -0
  25. package/dist/connectors/google-search-console-oauth.js +923 -0
  26. package/dist/connectors/google-sheets.js +18 -2
  27. package/dist/connectors/google-slides.js +18 -2
  28. package/dist/connectors/jdbc.d.ts +5 -0
  29. package/dist/connectors/jdbc.js +21097 -0
  30. package/dist/connectors/monday.d.ts +5 -0
  31. package/dist/connectors/monday.js +853 -0
  32. package/dist/connectors/oracle.d.ts +5 -0
  33. package/dist/connectors/oracle.js +665 -0
  34. package/dist/connectors/semrush.d.ts +5 -0
  35. package/dist/connectors/semrush.js +812 -0
  36. package/dist/connectors/sqlserver.d.ts +5 -0
  37. package/dist/connectors/sqlserver.js +656 -0
  38. package/dist/connectors/supabase.d.ts +5 -0
  39. package/dist/connectors/supabase.js +582 -0
  40. package/dist/connectors/tiktok-ads.js +15 -2
  41. package/dist/index.js +73218 -60122
  42. package/dist/main.js +73212 -60116
  43. package/dist/vite-plugin.js +73118 -60022
  44. package/package.json +60 -2
@@ -0,0 +1,963 @@
1
+ // ../connectors/src/parameter-definition.ts
2
+ var ParameterDefinition = class {
3
+ slug;
4
+ name;
5
+ description;
6
+ envVarBaseKey;
7
+ type;
8
+ secret;
9
+ required;
10
+ constructor(config) {
11
+ this.slug = config.slug;
12
+ this.name = config.name;
13
+ this.description = config.description;
14
+ this.envVarBaseKey = config.envVarBaseKey;
15
+ this.type = config.type;
16
+ this.secret = config.secret;
17
+ this.required = config.required;
18
+ }
19
+ /**
20
+ * Get the parameter value from a ConnectorConnectionObject.
21
+ */
22
+ getValue(connection2) {
23
+ const param = connection2.parameters.find(
24
+ (p) => p.parameterSlug === this.slug
25
+ );
26
+ if (!param || param.value == null) {
27
+ throw new Error(
28
+ `Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
29
+ );
30
+ }
31
+ return param.value;
32
+ }
33
+ /**
34
+ * Try to get the parameter value. Returns undefined if not found (for optional params).
35
+ */
36
+ tryGetValue(connection2) {
37
+ const param = connection2.parameters.find(
38
+ (p) => p.parameterSlug === this.slug
39
+ );
40
+ if (!param || param.value == null) return void 0;
41
+ return param.value;
42
+ }
43
+ };
44
+
45
+ // ../connectors/src/connectors/github/parameters.ts
46
+ var parameters = {
47
+ personalAccessToken: new ParameterDefinition({
48
+ slug: "personal-access-token",
49
+ name: "GitHub Personal Access Token",
50
+ description: "Personal Access Token (Classic \u2014 `ghp_\u2026` \u2014 or fine-grained \u2014 `github_pat_\u2026`). Generate one at https://github.com/settings/tokens. The token must have the scopes / repository access required for the data you want to read (e.g. `repo` for private repositories, `read:org` for org metadata).",
51
+ envVarBaseKey: "GITHUB_PAT",
52
+ type: "text",
53
+ secret: true,
54
+ required: true
55
+ }),
56
+ baseUrl: new ParameterDefinition({
57
+ slug: "base-url",
58
+ name: "GitHub API Base URL",
59
+ description: "Optional. Override the API base URL for GitHub Enterprise Server (e.g. `https://github.example.com/api/v3`). Leave empty to use the public API at `https://api.github.com`.",
60
+ envVarBaseKey: "GITHUB_API_BASE_URL",
61
+ type: "text",
62
+ secret: false,
63
+ required: false
64
+ })
65
+ };
66
+ var DEFAULT_BASE_URL = "https://api.github.com";
67
+
68
+ // ../connectors/src/connectors/github/sdk/index.ts
69
+ function createClient(params) {
70
+ const token = params[parameters.personalAccessToken.slug];
71
+ if (!token) {
72
+ throw new Error(
73
+ `github: missing required parameter: ${parameters.personalAccessToken.slug}`
74
+ );
75
+ }
76
+ const baseUrlParam = params[parameters.baseUrl.slug]?.trim();
77
+ const baseUrl = baseUrlParam && baseUrlParam.replace(/\/+$/, "") || DEFAULT_BASE_URL;
78
+ function authHeaders(extra) {
79
+ const headers = new Headers(extra);
80
+ headers.set("Authorization", `Bearer ${token}`);
81
+ headers.set("Accept", "application/vnd.github+json");
82
+ headers.set("X-GitHub-Api-Version", "2022-11-28");
83
+ if (!headers.has("User-Agent")) {
84
+ headers.set("User-Agent", "squadbase-connectors-github");
85
+ }
86
+ return headers;
87
+ }
88
+ async function assertOk(res, label) {
89
+ if (!res.ok) {
90
+ const body = await res.text().catch(() => "(unreadable body)");
91
+ throw new Error(
92
+ `github ${label}: ${res.status} ${res.statusText} \u2014 ${body}`
93
+ );
94
+ }
95
+ }
96
+ function buildQuery(base) {
97
+ const search = new URLSearchParams();
98
+ for (const [key, value] of Object.entries(base)) {
99
+ if (value !== void 0) search.set(key, String(value));
100
+ }
101
+ const qs = search.toString();
102
+ return qs ? `?${qs}` : "";
103
+ }
104
+ return {
105
+ request(path2, init) {
106
+ const url = `${baseUrl}${path2.startsWith("/") ? "" : "/"}${path2}`;
107
+ const headers = authHeaders(
108
+ init?.headers
109
+ );
110
+ if (!headers.has("Content-Type") && init?.body !== void 0) {
111
+ headers.set("Content-Type", "application/json");
112
+ }
113
+ return fetch(url, { ...init, headers });
114
+ },
115
+ async getAuthenticatedUser() {
116
+ const res = await fetch(`${baseUrl}/user`, {
117
+ method: "GET",
118
+ headers: authHeaders()
119
+ });
120
+ await assertOk(res, "getAuthenticatedUser");
121
+ return await res.json();
122
+ },
123
+ async listAuthenticatedRepos(options) {
124
+ const qs = buildQuery({
125
+ visibility: options?.visibility,
126
+ affiliation: options?.affiliation,
127
+ type: options?.type,
128
+ sort: options?.sort,
129
+ direction: options?.direction,
130
+ per_page: options?.per_page,
131
+ page: options?.page
132
+ });
133
+ const res = await fetch(`${baseUrl}/user/repos${qs}`, {
134
+ method: "GET",
135
+ headers: authHeaders()
136
+ });
137
+ await assertOk(res, "listAuthenticatedRepos");
138
+ return await res.json();
139
+ },
140
+ async listOrgRepos(org, options) {
141
+ const qs = buildQuery({
142
+ type: options?.type,
143
+ sort: options?.sort,
144
+ direction: options?.direction,
145
+ per_page: options?.per_page,
146
+ page: options?.page
147
+ });
148
+ const res = await fetch(
149
+ `${baseUrl}/orgs/${encodeURIComponent(org)}/repos${qs}`,
150
+ { method: "GET", headers: authHeaders() }
151
+ );
152
+ await assertOk(res, "listOrgRepos");
153
+ return await res.json();
154
+ },
155
+ async getRepo(owner, repo) {
156
+ const res = await fetch(
157
+ `${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`,
158
+ { method: "GET", headers: authHeaders() }
159
+ );
160
+ await assertOk(res, "getRepo");
161
+ return await res.json();
162
+ },
163
+ async listIssues(owner, repo, options) {
164
+ const qs = buildQuery({
165
+ state: options?.state,
166
+ labels: options?.labels,
167
+ sort: options?.sort,
168
+ direction: options?.direction,
169
+ since: options?.since,
170
+ assignee: options?.assignee,
171
+ creator: options?.creator,
172
+ mentioned: options?.mentioned,
173
+ milestone: options?.milestone !== void 0 ? String(options.milestone) : void 0,
174
+ per_page: options?.per_page,
175
+ page: options?.page
176
+ });
177
+ const res = await fetch(
178
+ `${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues${qs}`,
179
+ { method: "GET", headers: authHeaders() }
180
+ );
181
+ await assertOk(res, "listIssues");
182
+ return await res.json();
183
+ },
184
+ async getIssue(owner, repo, issueNumber) {
185
+ const res = await fetch(
186
+ `${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`,
187
+ { method: "GET", headers: authHeaders() }
188
+ );
189
+ await assertOk(res, "getIssue");
190
+ return await res.json();
191
+ },
192
+ async createIssue(owner, repo, issueData) {
193
+ const headers = authHeaders();
194
+ headers.set("Content-Type", "application/json");
195
+ const res = await fetch(
196
+ `${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues`,
197
+ { method: "POST", headers, body: JSON.stringify(issueData) }
198
+ );
199
+ await assertOk(res, "createIssue");
200
+ return await res.json();
201
+ },
202
+ async createIssueComment(owner, repo, issueNumber, body) {
203
+ const headers = authHeaders();
204
+ headers.set("Content-Type", "application/json");
205
+ const res = await fetch(
206
+ `${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments`,
207
+ { method: "POST", headers, body: JSON.stringify({ body }) }
208
+ );
209
+ await assertOk(res, "createIssueComment");
210
+ return await res.json();
211
+ },
212
+ async listPullRequests(owner, repo, options) {
213
+ const qs = buildQuery({
214
+ state: options?.state,
215
+ head: options?.head,
216
+ base: options?.base,
217
+ sort: options?.sort,
218
+ direction: options?.direction,
219
+ per_page: options?.per_page,
220
+ page: options?.page
221
+ });
222
+ const res = await fetch(
223
+ `${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls${qs}`,
224
+ { method: "GET", headers: authHeaders() }
225
+ );
226
+ await assertOk(res, "listPullRequests");
227
+ return await res.json();
228
+ },
229
+ async getPullRequest(owner, repo, pullNumber) {
230
+ const res = await fetch(
231
+ `${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${pullNumber}`,
232
+ { method: "GET", headers: authHeaders() }
233
+ );
234
+ await assertOk(res, "getPullRequest");
235
+ return await res.json();
236
+ },
237
+ async listCommits(owner, repo, options) {
238
+ const qs = buildQuery({
239
+ sha: options?.sha,
240
+ path: options?.path,
241
+ author: options?.author,
242
+ since: options?.since,
243
+ until: options?.until,
244
+ per_page: options?.per_page,
245
+ page: options?.page
246
+ });
247
+ const res = await fetch(
248
+ `${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits${qs}`,
249
+ { method: "GET", headers: authHeaders() }
250
+ );
251
+ await assertOk(res, "listCommits");
252
+ return await res.json();
253
+ },
254
+ async searchRepositories(q, options) {
255
+ const qs = buildQuery({
256
+ q,
257
+ sort: options?.sort,
258
+ order: options?.order,
259
+ per_page: options?.per_page,
260
+ page: options?.page
261
+ });
262
+ const res = await fetch(`${baseUrl}/search/repositories${qs}`, {
263
+ method: "GET",
264
+ headers: authHeaders()
265
+ });
266
+ await assertOk(res, "searchRepositories");
267
+ return await res.json();
268
+ },
269
+ async searchIssues(q, options) {
270
+ const qs = buildQuery({
271
+ q,
272
+ sort: options?.sort,
273
+ order: options?.order,
274
+ per_page: options?.per_page,
275
+ page: options?.page
276
+ });
277
+ const res = await fetch(`${baseUrl}/search/issues${qs}`, {
278
+ method: "GET",
279
+ headers: authHeaders()
280
+ });
281
+ await assertOk(res, "searchIssues");
282
+ return await res.json();
283
+ }
284
+ };
285
+ }
286
+
287
+ // ../connectors/src/connector-onboarding.ts
288
+ var ConnectorOnboarding = class {
289
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
290
+ connectionSetupInstructions;
291
+ /** Phase 2: Data overview instructions */
292
+ dataOverviewInstructions;
293
+ constructor(config) {
294
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
295
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
296
+ }
297
+ getConnectionSetupPrompt(language) {
298
+ return this.connectionSetupInstructions?.[language] ?? null;
299
+ }
300
+ getDataOverviewInstructions(language) {
301
+ return this.dataOverviewInstructions[language];
302
+ }
303
+ };
304
+
305
+ // ../connectors/src/connector-tool.ts
306
+ var ConnectorTool = class {
307
+ name;
308
+ description;
309
+ inputSchema;
310
+ outputSchema;
311
+ _execute;
312
+ constructor(config) {
313
+ this.name = config.name;
314
+ this.description = config.description;
315
+ this.inputSchema = config.inputSchema;
316
+ this.outputSchema = config.outputSchema;
317
+ this._execute = config.execute;
318
+ }
319
+ createTool(connections, config) {
320
+ return {
321
+ description: this.description,
322
+ inputSchema: this.inputSchema,
323
+ outputSchema: this.outputSchema,
324
+ execute: (input) => this._execute(input, connections, config)
325
+ };
326
+ }
327
+ };
328
+
329
+ // ../connectors/src/connector-plugin.ts
330
+ var ConnectorPlugin = class _ConnectorPlugin {
331
+ slug;
332
+ authType;
333
+ name;
334
+ description;
335
+ iconUrl;
336
+ parameters;
337
+ releaseFlag;
338
+ proxyPolicy;
339
+ experimentalAttributes;
340
+ categories;
341
+ onboarding;
342
+ systemPrompt;
343
+ tools;
344
+ query;
345
+ checkConnection;
346
+ constructor(config) {
347
+ this.slug = config.slug;
348
+ this.authType = config.authType;
349
+ this.name = config.name;
350
+ this.description = config.description;
351
+ this.iconUrl = config.iconUrl;
352
+ this.parameters = config.parameters;
353
+ this.releaseFlag = config.releaseFlag;
354
+ this.proxyPolicy = config.proxyPolicy;
355
+ this.experimentalAttributes = config.experimentalAttributes;
356
+ this.categories = config.categories ?? [];
357
+ this.onboarding = config.onboarding;
358
+ this.systemPrompt = config.systemPrompt;
359
+ this.tools = config.tools;
360
+ this.query = config.query;
361
+ this.checkConnection = config.checkConnection;
362
+ }
363
+ get connectorKey() {
364
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
365
+ }
366
+ /**
367
+ * Create tools for connections that belong to this connector.
368
+ * Filters connections by connectorKey internally.
369
+ * Returns tools keyed as `${connectorKey}_${toolName}`.
370
+ */
371
+ createTools(connections, config, opts) {
372
+ const myConnections = connections.filter(
373
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
374
+ );
375
+ const result = {};
376
+ for (const t of Object.values(this.tools)) {
377
+ const tool = t.createTool(myConnections, config);
378
+ const originalToModelOutput = tool.toModelOutput;
379
+ result[`${this.connectorKey}_${t.name}`] = {
380
+ ...tool,
381
+ toModelOutput: async (options) => {
382
+ if (!originalToModelOutput) {
383
+ return opts.truncateOutput(options.output);
384
+ }
385
+ const modelOutput = await originalToModelOutput(options);
386
+ if (modelOutput.type === "text" || modelOutput.type === "json") {
387
+ return opts.truncateOutput(modelOutput.value);
388
+ }
389
+ return modelOutput;
390
+ }
391
+ };
392
+ }
393
+ return result;
394
+ }
395
+ static deriveKey(slug, authType) {
396
+ if (authType) return `${slug}-${authType}`;
397
+ const LEGACY_NULL_AUTH_TYPE_MAP = {
398
+ // user-password
399
+ "postgresql": "user-password",
400
+ "mysql": "user-password",
401
+ "clickhouse": "user-password",
402
+ "kintone": "user-password",
403
+ "squadbase-db": "user-password",
404
+ // service-account
405
+ "snowflake": "service-account",
406
+ "bigquery": "service-account",
407
+ "google-analytics": "service-account",
408
+ "google-calendar": "service-account",
409
+ "aws-athena": "service-account",
410
+ "redshift": "service-account",
411
+ // api-key
412
+ "databricks": "api-key",
413
+ "dbt": "api-key",
414
+ "airtable": "api-key",
415
+ "openai": "api-key",
416
+ "gemini": "api-key",
417
+ "anthropic": "api-key",
418
+ "wix-store": "api-key"
419
+ };
420
+ const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
421
+ if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
422
+ return slug;
423
+ }
424
+ };
425
+
426
+ // ../connectors/src/auth-types.ts
427
+ var AUTH_TYPES = {
428
+ OAUTH: "oauth",
429
+ API_KEY: "api-key",
430
+ JWT: "jwt",
431
+ SERVICE_ACCOUNT: "service-account",
432
+ PAT: "pat",
433
+ USER_PASSWORD: "user-password"
434
+ };
435
+
436
+ // ../connectors/src/connectors/github/setup.ts
437
+ var githubOnboarding = new ConnectorOnboarding({
438
+ dataOverviewInstructions: {
439
+ en: `1. Call github_request with GET /user to confirm credentials and discover the authenticated account (login, name, plan).
440
+ 2. Call github_request with GET /user/repos?per_page=10&sort=updated to sample repositories the token can see. For organisation-owned repos, use GET /orgs/{org}/repos.
441
+ 3. Pick a representative repo and inspect its activity:
442
+ - GET /repos/{owner}/{repo}/issues?state=open&per_page=10 \u2014 recent open issues (the GitHub API treats pull requests as issues; filter with the \`pull_request\` field if you want only issues).
443
+ - GET /repos/{owner}/{repo}/pulls?state=open&per_page=10 \u2014 open pull requests.
444
+ - GET /repos/{owner}/{repo}/commits?per_page=10 \u2014 recent commits on the default branch.
445
+ 4. For broad discovery, use the search endpoints (GET /search/repositories?q=..., /search/issues?q=..., /search/code?q=...). These are heavily rate-limited (30 req/min for authenticated users), so use them sparingly.
446
+ 5. Pagination is page-based: \`?page=1&per_page=100\` (max 100). The \`Link\` response header carries \`rel="next"\` / \`rel="last"\` URLs when more results exist.`,
447
+ ja: `1. github_request \u3067 GET /user \u3092\u547C\u3073\u51FA\u3057\u3001\u8A8D\u8A3C\u60C5\u5831\u306E\u78BA\u8A8D\u3068\u8A8D\u8A3C\u6E08\u307F\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u60C5\u5831\uFF08login, name, plan\uFF09\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002
448
+ 2. github_request \u3067 GET /user/repos?per_page=10&sort=updated \u3092\u547C\u3073\u51FA\u3057\u3066\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30EA\u30DD\u30B8\u30C8\u30EA\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u3057\u307E\u3059\u3002Organization \u6240\u6709\u306E\u30EA\u30DD\u30B8\u30C8\u30EA\u306F GET /orgs/{org}/repos \u3092\u4F7F\u7528\u3057\u307E\u3059\u3002
449
+ 3. \u4EE3\u8868\u7684\u306A\u30EA\u30DD\u30B8\u30C8\u30EA\u3092\u9078\u3073\u3001\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u3092\u78BA\u8A8D\u3057\u307E\u3059:
450
+ - GET /repos/{owner}/{repo}/issues?state=open&per_page=10 \u2014 \u76F4\u8FD1\u306E\u30AA\u30FC\u30D7\u30F3\u30A4\u30B7\u30E5\u30FC\uFF08GitHub API \u306F\u30D7\u30EB\u30EA\u30AF\u30A8\u30B9\u30C8\u3082\u30A4\u30B7\u30E5\u30FC\u6271\u3044\u3057\u307E\u3059\u3002\u30A4\u30B7\u30E5\u30FC\u306E\u307F\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F \`pull_request\` \u30D5\u30A3\u30FC\u30EB\u30C9\u3067\u9664\u5916\uFF09\u3002
451
+ - GET /repos/{owner}/{repo}/pulls?state=open&per_page=10 \u2014 \u30AA\u30FC\u30D7\u30F3\u306A\u30D7\u30EB\u30EA\u30AF\u30A8\u30B9\u30C8\u4E00\u89A7\u3002
452
+ - GET /repos/{owner}/{repo}/commits?per_page=10 \u2014 \u30C7\u30D5\u30A9\u30EB\u30C8\u30D6\u30E9\u30F3\u30C1\u306E\u76F4\u8FD1\u30B3\u30DF\u30C3\u30C8\u3002
453
+ 4. \u6A2A\u65AD\u7684\u306B\u63A2\u7D22\u3059\u308B\u5834\u5408\u306F\u30B5\u30FC\u30C1\u7CFB\uFF08GET /search/repositories?q=..., /search/issues?q=..., /search/code?q=...\uFF09\u3092\u4F7F\u7528\u3057\u307E\u3059\u304C\u3001\u8A8D\u8A3C\u30E6\u30FC\u30B6\u30FC\u3067\u308230 req/\u5206\u306E\u53B3\u3057\u3044\u30EC\u30FC\u30C8\u5236\u9650\u304C\u3042\u308B\u305F\u3081\u547C\u3073\u51FA\u3057\u306F\u63A7\u3048\u3081\u306B\u3002
454
+ 5. \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u306F1\u59CB\u307E\u308A\u306E \`page\` \u3068 \`per_page\`\uFF08\u6700\u5927100\uFF09\u3002\`Link\` \u30EC\u30B9\u30DD\u30F3\u30B9\u30D8\u30C3\u30C0\u306E \`rel="next"\` / \`rel="last"\` \u304B\u3089\u6B21URL\u3092\u53D6\u5F97\u3067\u304D\u307E\u3059\u3002`
455
+ }
456
+ });
457
+
458
+ // ../connectors/src/connectors/github/tools/request.ts
459
+ import { z } from "zod";
460
+ var REQUEST_TIMEOUT_MS = 6e4;
461
+ function resolveBaseUrl(connectionBaseUrl) {
462
+ const trimmed = connectionBaseUrl?.trim();
463
+ return trimmed && trimmed.replace(/\/+$/, "") || DEFAULT_BASE_URL;
464
+ }
465
+ var inputSchema = z.object({
466
+ toolUseIntent: z.string().optional().describe(
467
+ "Brief description of what you intend to accomplish with this tool call"
468
+ ),
469
+ connectionId: z.string().describe("ID of the GitHub connection to use"),
470
+ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe(
471
+ "HTTP method. GET for reading, POST for creating, PATCH for partial updates, PUT for replacement (e.g. starring), DELETE for removal."
472
+ ),
473
+ path: z.string().describe(
474
+ "API path (e.g., '/user', '/repos/{owner}/{repo}/issues', '/search/repositories?q=topic:react'). Append query parameters such as '?state=open&per_page=100&page=1'."
475
+ ),
476
+ body: z.union([
477
+ z.record(z.string(), z.unknown()),
478
+ z.array(z.unknown())
479
+ ]).optional().describe(
480
+ 'Request body (JSON). Example creating an issue: { "title": "...", "body": "...", "labels": ["bug"] }.'
481
+ )
482
+ });
483
+ var outputSchema = z.discriminatedUnion("success", [
484
+ z.object({
485
+ success: z.literal(true),
486
+ status: z.number(),
487
+ data: z.union([
488
+ z.record(z.string(), z.unknown()),
489
+ z.array(z.unknown())
490
+ ])
491
+ }),
492
+ z.object({
493
+ success: z.literal(false),
494
+ error: z.string()
495
+ })
496
+ ]);
497
+ var requestTool = new ConnectorTool({
498
+ name: "request",
499
+ description: `Send authenticated requests to the GitHub REST API.
500
+ Authentication uses the Personal Access Token via \`Authorization: Bearer <PAT>\` (handled automatically). Both Classic (\`ghp_\u2026\`) and fine-grained (\`github_pat_\u2026\`) tokens work \u2014 the scopes / repository access of the token determine what data is reachable.
501
+
502
+ Provide the API path relative to the base URL (default \`https://api.github.com\`; can be overridden per-connection for GitHub Enterprise Server).
503
+
504
+ Common endpoints:
505
+ - GET /user \u2014 Authenticated user (auth probe)
506
+ - GET /user/repos?sort=updated \u2014 Repos accessible to the token
507
+ - GET /orgs/{org}/repos \u2014 Repos owned by an organisation
508
+ - GET /repos/{owner}/{repo} \u2014 Repository metadata
509
+ - GET /repos/{owner}/{repo}/branches \u2014 Branches
510
+ - GET /repos/{owner}/{repo}/commits \u2014 Commits on the default branch (\`?sha=branch\` to switch)
511
+ - GET /repos/{owner}/{repo}/contents/{path} \u2014 File or directory contents
512
+ - GET /repos/{owner}/{repo}/issues?state=open \u2014 Issues (note: pull requests are returned too \u2014 they have a \`pull_request\` field; filter on it to separate)
513
+ - GET /repos/{owner}/{repo}/issues/{number} \u2014 Single issue / PR
514
+ - POST /repos/{owner}/{repo}/issues \u2014 Create an issue (body: { title, body, labels })
515
+ - PATCH /repos/{owner}/{repo}/issues/{number} \u2014 Update an issue (close: { "state": "closed" })
516
+ - GET /repos/{owner}/{repo}/issues/{number}/comments \u2014 List issue comments
517
+ - POST /repos/{owner}/{repo}/issues/{number}/comments \u2014 Add a comment
518
+ - GET /repos/{owner}/{repo}/pulls?state=open \u2014 Pull requests
519
+ - GET /repos/{owner}/{repo}/pulls/{number} \u2014 Single PR (includes mergeability)
520
+ - GET /repos/{owner}/{repo}/pulls/{number}/files \u2014 Files changed in a PR
521
+ - GET /repos/{owner}/{repo}/pulls/{number}/reviews \u2014 Reviews on a PR
522
+ - GET /repos/{owner}/{repo}/actions/runs \u2014 GitHub Actions workflow runs
523
+ - GET /repos/{owner}/{repo}/releases \u2014 Releases
524
+ - GET /search/repositories?q=topic:react \u2014 Repository search
525
+ - GET /search/issues?q=is:pr+repo:foo/bar \u2014 Issue / PR search
526
+ - GET /search/code?q=...+repo:foo/bar \u2014 Code search
527
+ - GET /rate_limit \u2014 Inspect remaining quota
528
+
529
+ Pagination: 1-indexed \`page\` + \`per_page\` (max 100). The \`Link\` response header exposes \`rel="next"\`/\`rel="last"\` URLs.
530
+
531
+ Rate limits: 5,000 req/hr on REST endpoints, 30 req/min on /search/*. Inspect \`X-RateLimit-Remaining\` to throttle.`,
532
+ inputSchema,
533
+ outputSchema,
534
+ async execute({ connectionId, method, path: path2, body }, connections) {
535
+ const connection2 = connections.find((c) => c.id === connectionId);
536
+ if (!connection2) {
537
+ return {
538
+ success: false,
539
+ error: `Connection ${connectionId} not found`
540
+ };
541
+ }
542
+ console.log(
543
+ `[connector-request] github/${connection2.name}: ${method} ${path2}`
544
+ );
545
+ try {
546
+ const token = parameters.personalAccessToken.getValue(connection2);
547
+ const baseUrl = resolveBaseUrl(parameters.baseUrl.tryGetValue(connection2));
548
+ const trimmedPath = path2.trim().replace(/^\/+/, "/");
549
+ const url = `${baseUrl}${trimmedPath.startsWith("/") ? "" : "/"}${trimmedPath}`;
550
+ const controller = new AbortController();
551
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
552
+ try {
553
+ const response = await fetch(url, {
554
+ method,
555
+ headers: {
556
+ Authorization: `Bearer ${token}`,
557
+ Accept: "application/vnd.github+json",
558
+ "X-GitHub-Api-Version": "2022-11-28",
559
+ "User-Agent": "squadbase-connectors-github",
560
+ "Content-Type": "application/json"
561
+ },
562
+ body: body ? JSON.stringify(body) : void 0,
563
+ signal: controller.signal
564
+ });
565
+ const text = await response.text();
566
+ const data = text ? JSON.parse(text) : {};
567
+ if (!response.ok) {
568
+ const errObj = !Array.isArray(data) ? data : {};
569
+ const message = typeof errObj.message === "string" && errObj.message || typeof errObj.error === "string" && errObj.error;
570
+ const errors = errObj.errors;
571
+ const fieldDetail = Array.isArray(errors) && errors.length > 0 ? ` \u2014 ${errors[0]?.message ?? JSON.stringify(errors[0])}` : "";
572
+ const errorMessage = message ? `${message}${fieldDetail}` : `HTTP ${response.status} ${response.statusText}`;
573
+ return { success: false, error: String(errorMessage) };
574
+ }
575
+ return { success: true, status: response.status, data };
576
+ } finally {
577
+ clearTimeout(timeout);
578
+ }
579
+ } catch (err) {
580
+ const msg = err instanceof Error ? err.message : String(err);
581
+ return { success: false, error: msg };
582
+ }
583
+ }
584
+ });
585
+
586
+ // ../connectors/src/connectors/github/index.ts
587
+ var tools = { request: requestTool };
588
+ var githubConnector = new ConnectorPlugin({
589
+ slug: "github",
590
+ authType: AUTH_TYPES.PAT,
591
+ name: "GitHub",
592
+ description: "Connect to GitHub for repository, issue, pull request, commit, and search data via Personal Access Token (Classic or fine-grained).",
593
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/2flOAufkhDrLKuOQqEVS3/1f9dcf145680ef77aef149709263e2d4/github-icon.svg",
594
+ parameters,
595
+ releaseFlag: { dev1: true, dev2: false, prod: false },
596
+ categories: ["productivity"],
597
+ onboarding: githubOnboarding,
598
+ systemPrompt: {
599
+ en: `### Tools
600
+
601
+ - \`github_request\`: The only way to call the GitHub REST API. Use it to read repositories, issues, pull requests, commits, comments, releases, GitHub Actions runs, organisation metadata, and the search endpoints. Authentication (\`Authorization: Bearer <PAT>\`) and required headers (\`Accept: application/vnd.github+json\`, \`X-GitHub-Api-Version: 2022-11-28\`, \`User-Agent\`) are configured automatically. Both Classic (\`ghp_\u2026\`) and fine-grained (\`github_pat_\u2026\`) tokens work.
602
+
603
+ ### Cloning Repositories for Deep Code Exploration
604
+
605
+ \`github_request\` plus \`/repos/{owner}/{repo}/contents/{path}\` is fine for reading a few files. When the task requires deep exploration \u2014 reading many files, searching across the codebase with ripgrep / grep, or inspecting many commits \u2014 prefer \`git clone\` via the bash tool. Follow these rules **strictly**:
606
+
607
+ 1. **Always clone OUTSIDE the dashboard project.** The current working directory is the user's Vite-based dashboard project, which is itself a git repository. Cloning into it (or any subdirectory of it) would pollute the dashboard's git index, risk accidental commits, and create nested-repo confusion. Always clone into a path outside the project \u2014 use \`$(mktemp -d)\`, \`/tmp/<unique-name>\`, or \`~/.cache/<unique-name>\`.
608
+ 2. **NEVER commit, push, or otherwise write to the cloned repo.** This is a strictly read-only flow. Do not run \`git commit\`, \`git push\`, \`git tag\`, \`git rebase\`, or any other write operation inside the cloned working tree. The connector is for reading source; if the task requires writing back to GitHub (creating an issue, posting a comment, etc.), use \`github_request\` \u2014 never \`git push\`.
609
+ 3. **Authenticate via HTTPS using the PAT inline.** Run \`git clone https://<TOKEN>@github.com/<owner>/<repo>.git <target-dir>\`. Avoid logging the URL with the token interpolated; redact when echoing commands.
610
+ 4. **Prefer shallow clones.** Use \`--depth=1 --single-branch\` unless full history is specifically required.
611
+ 5. **Clean up when done.** Remove the cloned directory (\`rm -rf <target-dir>\`) once exploration is complete.
612
+
613
+ ### Business Logic
614
+
615
+ The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
616
+
617
+ SDK methods (client created via \`connection(connectionId)\`):
618
+ - \`client.request(path, init?)\` \u2014 low-level authenticated fetch (default base \`https://api.github.com\`, overridable per-connection for GitHub Enterprise Server)
619
+ - \`client.getAuthenticatedUser()\` \u2014 calling user (auth probe)
620
+ - \`client.listAuthenticatedRepos(options?)\` / \`client.listOrgRepos(org, options?)\` \u2014 repos
621
+ - \`client.getRepo(owner, repo)\` \u2014 single repo
622
+ - \`client.listIssues(owner, repo, options?)\` / \`client.getIssue(owner, repo, n)\` / \`client.createIssue(owner, repo, data)\` \u2014 issues
623
+ - \`client.createIssueComment(owner, repo, n, body)\` \u2014 add a comment to an issue or PR
624
+ - \`client.listPullRequests(owner, repo, options?)\` / \`client.getPullRequest(owner, repo, n)\` \u2014 pull requests
625
+ - \`client.listCommits(owner, repo, options?)\` \u2014 commits
626
+ - \`client.searchRepositories(q, options?)\` / \`client.searchIssues(q, options?)\` \u2014 search (rate-limited 30 req/min)
627
+
628
+ \`\`\`ts
629
+ import type { Context } from "hono";
630
+ import { connection } from "@squadbase/vite-server/connectors/github";
631
+
632
+ const gh = connection("<connectionId>");
633
+
634
+ export default async function handler(c: Context) {
635
+ const { owner, repo } = await c.req.json<{ owner: string; repo: string }>();
636
+ const issues = await gh.listIssues(owner, repo, {
637
+ state: "open",
638
+ sort: "updated",
639
+ direction: "desc",
640
+ per_page: 100,
641
+ });
642
+ // GitHub returns PRs in the issues endpoint \u2014 filter them out.
643
+ const onlyIssues = issues.filter((i) => !("pull_request" in i));
644
+ return c.json({ issues: onlyIssues });
645
+ }
646
+ \`\`\`
647
+
648
+ ### GitHub REST API Reference
649
+
650
+ - Base URL: \`https://api.github.com\` (overridable per-connection for GitHub Enterprise Server, e.g. \`https://github.example.com/api/v3\`)
651
+ - Authentication: \`Authorization: Bearer <PAT>\`. Required headers (handled automatically): \`Accept: application/vnd.github+json\`, \`X-GitHub-Api-Version: 2022-11-28\`, \`User-Agent\`.
652
+ - Token scopes determine reachable data: e.g. \`repo\` for private repositories, \`read:org\` for organisation metadata. Fine-grained tokens additionally restrict by repository.
653
+ - Pagination: 1-indexed \`page\` + \`per_page\` (max 100). The \`Link\` response header carries \`rel="next"\` / \`rel="last"\` URLs.
654
+ - Rate limits: 5,000 req/hr for authenticated REST endpoints, 30 req/min for \`/search/*\`. Inspect \`X-RateLimit-Remaining\` / \`X-RateLimit-Reset\`.
655
+ - Caveat: \`/repos/{owner}/{repo}/issues\` returns pull requests too. They include a \`pull_request\` object \u2014 filter on it (or call \`/repos/{owner}/{repo}/pulls\` directly).
656
+
657
+ #### Resource Endpoints
658
+
659
+ **User & Orgs**
660
+ - GET \`/user\` \u2014 Authenticated user
661
+ - GET \`/users/{username}\` \u2014 Public profile
662
+ - GET \`/orgs/{org}\` \u2014 Org metadata
663
+ - GET \`/orgs/{org}/members\` \u2014 Org members
664
+
665
+ **Repositories**
666
+ - GET \`/user/repos?sort=updated\` \u2014 Repos visible to the token
667
+ - GET \`/orgs/{org}/repos\` \u2014 Org repos
668
+ - GET \`/repos/{owner}/{repo}\` \u2014 Repo metadata
669
+ - GET \`/repos/{owner}/{repo}/branches\` \u2014 Branches
670
+ - GET \`/repos/{owner}/{repo}/contents/{path}\` \u2014 File or directory
671
+ - GET \`/repos/{owner}/{repo}/topics\` \u2014 Topics
672
+
673
+ **Issues**
674
+ - GET \`/repos/{owner}/{repo}/issues?state=open\` \u2014 Issues (includes PRs \u2014 see caveat)
675
+ - GET \`/repos/{owner}/{repo}/issues/{number}\` \u2014 Single issue
676
+ - POST \`/repos/{owner}/{repo}/issues\` \u2014 Create
677
+ - PATCH \`/repos/{owner}/{repo}/issues/{number}\` \u2014 Update (close: \`{ "state": "closed" }\`)
678
+ - GET \`/repos/{owner}/{repo}/issues/{number}/comments\` \u2014 Comments
679
+ - POST \`/repos/{owner}/{repo}/issues/{number}/comments\` \u2014 Add comment
680
+
681
+ **Pull requests**
682
+ - GET \`/repos/{owner}/{repo}/pulls?state=open\` \u2014 PRs
683
+ - GET \`/repos/{owner}/{repo}/pulls/{number}\` \u2014 Single PR (includes \`mergeable\`, \`mergeable_state\`)
684
+ - GET \`/repos/{owner}/{repo}/pulls/{number}/files\` \u2014 Files changed
685
+ - GET \`/repos/{owner}/{repo}/pulls/{number}/commits\` \u2014 Commits in PR
686
+ - GET \`/repos/{owner}/{repo}/pulls/{number}/reviews\` \u2014 Reviews
687
+
688
+ **Commits & Releases**
689
+ - GET \`/repos/{owner}/{repo}/commits?sha=branch\` \u2014 Commits
690
+ - GET \`/repos/{owner}/{repo}/commits/{ref}\` \u2014 Single commit (with files / stats)
691
+ - GET \`/repos/{owner}/{repo}/releases\` \u2014 Releases
692
+ - GET \`/repos/{owner}/{repo}/releases/latest\` \u2014 Latest release
693
+
694
+ **Actions**
695
+ - GET \`/repos/{owner}/{repo}/actions/runs\` \u2014 Workflow runs
696
+ - GET \`/repos/{owner}/{repo}/actions/workflows\` \u2014 Workflows
697
+
698
+ **Search (rate-limited)**
699
+ - GET \`/search/repositories?q=topic:react+language:typescript\` \u2014 Repos
700
+ - GET \`/search/issues?q=is:open+is:pr+repo:foo/bar\` \u2014 Issues / PRs
701
+ - GET \`/search/code?q=...+repo:foo/bar\` \u2014 Code (token must have \`repo\` scope)
702
+ - GET \`/search/users?q=...\` \u2014 Users
703
+
704
+ **Quota**
705
+ - GET \`/rate_limit\` \u2014 Inspect remaining quota across REST / GraphQL / search`,
706
+ ja: `### \u30C4\u30FC\u30EB
707
+
708
+ - \`github_request\`: GitHub REST API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30EA\u30DD\u30B8\u30C8\u30EA\u3001Issue\u3001Pull Request\u3001\u30B3\u30DF\u30C3\u30C8\u3001\u30B3\u30E1\u30F3\u30C8\u3001Release\u3001GitHub Actions \u306E\u30E9\u30F3\u3001\u7D44\u7E54\u30E1\u30BF\u60C5\u5831\u3001\u30B5\u30FC\u30C1\u7CFB\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306E\u53D6\u5F97\u30FB\u66F4\u65B0\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08\`Authorization: Bearer <PAT>\`\uFF09\u3068\u5FC5\u9808\u30D8\u30C3\u30C0\uFF08\`Accept: application/vnd.github+json\`\u3001\`X-GitHub-Api-Version: 2022-11-28\`\u3001\`User-Agent\`\uFF09\u306F\u81EA\u52D5\u3067\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002Classic\uFF08\`ghp_\u2026\`\uFF09/ fine-grained\uFF08\`github_pat_\u2026\`\uFF09\u3069\u3061\u3089\u306E PAT \u3082\u5229\u7528\u53EF\u80FD\u3067\u3059\u3002
709
+
710
+ ### \u6DF1\u3044\u30B3\u30FC\u30C9\u63A2\u7D22\u306E\u305F\u3081\u306E\u30EA\u30DD\u30B8\u30C8\u30EA\u30AF\u30ED\u30FC\u30F3
711
+
712
+ \u6570\u30D5\u30A1\u30A4\u30EB\u3092\u8AAD\u3080\u7A0B\u5EA6\u306A\u3089 \`github_request\` \u3068 \`/repos/{owner}/{repo}/contents/{path}\` \u3067\u5341\u5206\u3067\u3059\u3002\u591A\u6570\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u8AAD\u3080\u3001ripgrep / grep \u3067\u6A2A\u65AD\u691C\u7D22\u3059\u308B\u3001\u8907\u6570\u30B3\u30DF\u30C3\u30C8\u3092\u8ABF\u3079\u308B\u306A\u3069 **\u6DF1\u3044\u63A2\u7D22\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F** bash \u30C4\u30FC\u30EB\u7D4C\u7531\u3067 \`git clone\` \u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002\u4EE5\u4E0B\u306E\u30EB\u30FC\u30EB\u3092 **\u53B3\u5B88** \u3059\u308B\u3053\u3068\uFF1A
713
+
714
+ 1. **\u5FC5\u305A\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u5916\u306B\u30AF\u30ED\u30FC\u30F3\u3059\u308B\u3053\u3068\u3002** \u73FE\u5728\u306E\u4F5C\u696D\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u30E6\u30FC\u30B6\u30FC\u306E Vite \u30D9\u30FC\u30B9\u306E\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\uFF08\u305D\u308C\u81EA\u4F53\u304C git \u30EA\u30DD\u30B8\u30C8\u30EA\uFF09\u3067\u3059\u3002\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u5185\uFF08\u3042\u308B\u3044\u306F\u305D\u306E\u30B5\u30D6\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\uFF09\u306B\u30AF\u30ED\u30FC\u30F3\u3059\u308B\u3068\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u306E git \u306E\u72B6\u614B\u3092\u6C5A\u67D3\u3057\u3001\u8AA4\u30B3\u30DF\u30C3\u30C8\u3084\u30CD\u30B9\u30C8\u3057\u305F\u30EA\u30DD\u30B8\u30C8\u30EA\u306E\u6DF7\u4E71\u306E\u539F\u56E0\u306B\u306A\u308A\u307E\u3059\u3002\u5FC5\u305A\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u5916\u306E\u30D1\u30B9\uFF08\`$(mktemp -d)\`, \`/tmp/<unique-name>\`, \`~/.cache/<unique-name>\` \u306A\u3069\uFF09\u306B\u30AF\u30ED\u30FC\u30F3\u3057\u3066\u304F\u3060\u3055\u3044\u3002
715
+ 2. **\u7D76\u5BFE\u306B commit / push / \u66F8\u304D\u8FBC\u307F\u64CD\u4F5C\u3092\u3057\u306A\u3044\u3053\u3068\u3002** \u3053\u308C\u306F\u53B3\u5BC6\u306B read-only \u306E\u30D5\u30ED\u30FC\u3067\u3059\u3002\u30AF\u30ED\u30FC\u30F3\u3057\u305F\u4F5C\u696D\u30C4\u30EA\u30FC\u5185\u3067 \`git commit\` / \`git push\` / \`git tag\` / \`git rebase\` \u306A\u3069\u306E\u66F8\u304D\u8FBC\u307F\u64CD\u4F5C\u3092\u884C\u308F\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002GitHub \u3078\u306E\u66F8\u304D\u8FBC\u307F\u304C\u5FC5\u8981\u306A\u5834\u5408\uFF08Issue \u4F5C\u6210\u3001\u30B3\u30E1\u30F3\u30C8\u6295\u7A3F\u7B49\uFF09\u306F \`github_request\` \u3092\u4F7F\u7528\u3057\u3001\`git push\` \u3067\u884C\u308F\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
716
+ 3. **HTTPS \u8A8D\u8A3C\u3067 PAT \u3092\u30A4\u30F3\u30E9\u30A4\u30F3\u3067\u6E21\u3059\u3053\u3068\u3002** \`git clone https://<TOKEN>@github.com/<owner>/<repo>.git <target-dir>\` \u306E\u3088\u3046\u306B\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002\u30C8\u30FC\u30AF\u30F3\u5165\u308A\u306E URL \u3092\u305D\u306E\u307E\u307E\u30ED\u30B0\u306B\u51FA\u529B\u3057\u306A\u3044\u3088\u3046\u3001\u30B3\u30DE\u30F3\u30C9\u3092\u30A8\u30B3\u30FC\u3059\u308B\u969B\u306F\u30DE\u30B9\u30AF\u3057\u3066\u304F\u3060\u3055\u3044\u3002
717
+ 4. **shallow clone \u3092\u512A\u5148\u3059\u308B\u3053\u3068\u3002** \u5C65\u6B74\u304C\u7279\u306B\u5FC5\u8981\u3067\u306A\u3044\u9650\u308A \`--depth=1 --single-branch\` \u3092\u4F7F\u7528\u3057\u307E\u3059\u3002
718
+ 5. **\u4F5C\u696D\u5F8C\u306F\u524A\u9664\u3059\u308B\u3053\u3068\u3002** \u63A2\u7D22\u304C\u7D42\u308F\u3063\u305F\u3089\u30AF\u30ED\u30FC\u30F3\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\uFF08\`rm -rf <target-dir>\`\uFF09\u3057\u3066\u304F\u3060\u3055\u3044\u3002
719
+
720
+ ### Business Logic
721
+
722
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
723
+
724
+ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
725
+ - \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch\uFF08\u65E2\u5B9A base \`https://api.github.com\`\u3001\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u5358\u4F4D\u3067 GitHub Enterprise Server \u7528\u306B\u4E0A\u66F8\u304D\u53EF\u80FD\uFF09
726
+ - \`client.getAuthenticatedUser()\` \u2014 \u8A8D\u8A3C\u30E6\u30FC\u30B6\u30FC\uFF08\u758E\u901A\u78BA\u8A8D\uFF09
727
+ - \`client.listAuthenticatedRepos(options?)\` / \`client.listOrgRepos(org, options?)\` \u2014 \u30EA\u30DD\u30B8\u30C8\u30EA
728
+ - \`client.getRepo(owner, repo)\` \u2014 \u5358\u4E00\u30EA\u30DD\u30B8\u30C8\u30EA
729
+ - \`client.listIssues(owner, repo, options?)\` / \`client.getIssue(owner, repo, n)\` / \`client.createIssue(owner, repo, data)\` \u2014 Issue
730
+ - \`client.createIssueComment(owner, repo, n, body)\` \u2014 Issue / PR \u306B\u30B3\u30E1\u30F3\u30C8\u3092\u8FFD\u52A0
731
+ - \`client.listPullRequests(owner, repo, options?)\` / \`client.getPullRequest(owner, repo, n)\` \u2014 Pull Request
732
+ - \`client.listCommits(owner, repo, options?)\` \u2014 \u30B3\u30DF\u30C3\u30C8
733
+ - \`client.searchRepositories(q, options?)\` / \`client.searchIssues(q, options?)\` \u2014 \u30B5\u30FC\u30C1\uFF0830 req/\u5206\u306E\u5236\u9650\uFF09
734
+
735
+ \`\`\`ts
736
+ import type { Context } from "hono";
737
+ import { connection } from "@squadbase/vite-server/connectors/github";
738
+
739
+ const gh = connection("<connectionId>");
740
+
741
+ export default async function handler(c: Context) {
742
+ const { owner, repo } = await c.req.json<{ owner: string; repo: string }>();
743
+ const issues = await gh.listIssues(owner, repo, {
744
+ state: "open",
745
+ sort: "updated",
746
+ direction: "desc",
747
+ per_page: 100,
748
+ });
749
+ // GitHub \u306E issues \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306F PR \u3082\u8FD4\u3059\u305F\u3081\u9664\u5916\u3057\u307E\u3059\u3002
750
+ const onlyIssues = issues.filter((i) => !("pull_request" in i));
751
+ return c.json({ issues: onlyIssues });
752
+ }
753
+ \`\`\`
754
+
755
+ ### GitHub REST API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
756
+
757
+ - \u30D9\u30FC\u30B9URL: \`https://api.github.com\`\uFF08GitHub Enterprise Server \u306E\u5834\u5408\u306F\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u5358\u4F4D\u3067\u4E0A\u66F8\u304D\u53EF\u80FD\u3001\u4F8B: \`https://github.example.com/api/v3\`\uFF09
758
+ - \u8A8D\u8A3C: \`Authorization: Bearer <PAT>\`\u3002\u5FC5\u9808\u30D8\u30C3\u30C0\uFF08\u81EA\u52D5\u4ED8\u4E0E\uFF09: \`Accept: application/vnd.github+json\`\u3001\`X-GitHub-Api-Version: 2022-11-28\`\u3001\`User-Agent\`\u3002
759
+ - \u30C8\u30FC\u30AF\u30F3\u306E\u30B9\u30B3\u30FC\u30D7\u6B21\u7B2C\u3067\u53D6\u5F97\u3067\u304D\u308B\u30C7\u30FC\u30BF\u304C\u6C7A\u307E\u308A\u307E\u3059\uFF08\u4F8B: \u30D7\u30E9\u30A4\u30D9\u30FC\u30C8\u30EA\u30DD\u306F \`repo\`\u3001Org \u30E1\u30BF\u60C5\u5831\u306F \`read:org\`\uFF09\u3002fine-grained \u30C8\u30FC\u30AF\u30F3\u306F\u3055\u3089\u306B\u30EA\u30DD\u30B8\u30C8\u30EA\u5358\u4F4D\u3067\u5236\u9650\u3055\u308C\u307E\u3059\u3002
760
+ - \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3: 1\u59CB\u307E\u308A\u306E \`page\` \u3068 \`per_page\`\uFF08\u6700\u5927100\uFF09\u3002\`Link\` \u30EC\u30B9\u30DD\u30F3\u30B9\u30D8\u30C3\u30C0\u306E \`rel="next"\` / \`rel="last"\` \u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002
761
+ - \u30EC\u30FC\u30C8\u5236\u9650: \u8A8D\u8A3C\u6E08\u307F REST \u306F 5,000 req/\u6642\u3001\`/search/*\` \u306F 30 req/\u5206\u3002\`X-RateLimit-Remaining\` / \`X-RateLimit-Reset\` \u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
762
+ - \u6CE8\u610F\u70B9: \`/repos/{owner}/{repo}/issues\` \u306F Pull Request \u3082\u8FD4\u3057\u307E\u3059\u3002\u30EC\u30B9\u30DD\u30F3\u30B9\u306B \`pull_request\` \u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u5834\u5408\u306F PR \u3067\u3059\u3002Issue \u306E\u307F\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F\u305D\u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u3067\u9664\u5916\u3059\u308B\u304B\u3001\`/repos/{owner}/{repo}/pulls\` \u3092\u76F4\u63A5\u5229\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002
763
+
764
+ #### \u30EA\u30BD\u30FC\u30B9\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
765
+
766
+ **\u30E6\u30FC\u30B6\u30FC & \u7D44\u7E54**
767
+ - GET \`/user\` \u2014 \u8A8D\u8A3C\u30E6\u30FC\u30B6\u30FC
768
+ - GET \`/users/{username}\` \u2014 \u516C\u958B\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB
769
+ - GET \`/orgs/{org}\` \u2014 \u7D44\u7E54\u30E1\u30BF\u60C5\u5831
770
+ - GET \`/orgs/{org}/members\` \u2014 \u7D44\u7E54\u30E1\u30F3\u30D0\u30FC
771
+
772
+ **\u30EA\u30DD\u30B8\u30C8\u30EA**
773
+ - GET \`/user/repos?sort=updated\` \u2014 \u30C8\u30FC\u30AF\u30F3\u3067\u53C2\u7167\u53EF\u80FD\u306A\u30EA\u30DD\u30B8\u30C8\u30EA
774
+ - GET \`/orgs/{org}/repos\` \u2014 \u7D44\u7E54\u6240\u6709\u306E\u30EA\u30DD\u30B8\u30C8\u30EA
775
+ - GET \`/repos/{owner}/{repo}\` \u2014 \u30EA\u30DD\u30B8\u30C8\u30EA\u306E\u30E1\u30BF\u60C5\u5831
776
+ - GET \`/repos/{owner}/{repo}/branches\` \u2014 \u30D6\u30E9\u30F3\u30C1\u4E00\u89A7
777
+ - GET \`/repos/{owner}/{repo}/contents/{path}\` \u2014 \u30D5\u30A1\u30A4\u30EB / \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA
778
+ - GET \`/repos/{owner}/{repo}/topics\` \u2014 \u30C8\u30D4\u30C3\u30AF
779
+
780
+ **Issue**
781
+ - GET \`/repos/{owner}/{repo}/issues?state=open\` \u2014 Issue \u4E00\u89A7\uFF08PR \u3092\u542B\u3080\u70B9\u306B\u6CE8\u610F\uFF09
782
+ - GET \`/repos/{owner}/{repo}/issues/{number}\` \u2014 \u5358\u4E00 Issue
783
+ - POST \`/repos/{owner}/{repo}/issues\` \u2014 \u4F5C\u6210
784
+ - PATCH \`/repos/{owner}/{repo}/issues/{number}\` \u2014 \u66F4\u65B0\uFF08\u30AF\u30ED\u30FC\u30BA: \`{ "state": "closed" }\`\uFF09
785
+ - GET \`/repos/{owner}/{repo}/issues/{number}/comments\` \u2014 \u30B3\u30E1\u30F3\u30C8\u4E00\u89A7
786
+ - POST \`/repos/{owner}/{repo}/issues/{number}/comments\` \u2014 \u30B3\u30E1\u30F3\u30C8\u8FFD\u52A0
787
+
788
+ **Pull Request**
789
+ - GET \`/repos/{owner}/{repo}/pulls?state=open\` \u2014 PR \u4E00\u89A7
790
+ - GET \`/repos/{owner}/{repo}/pulls/{number}\` \u2014 \u5358\u4E00 PR\uFF08\`mergeable\`, \`mergeable_state\` \u3092\u542B\u3080\uFF09
791
+ - GET \`/repos/{owner}/{repo}/pulls/{number}/files\` \u2014 \u5909\u66F4\u30D5\u30A1\u30A4\u30EB
792
+ - GET \`/repos/{owner}/{repo}/pulls/{number}/commits\` \u2014 PR \u5185\u306E\u30B3\u30DF\u30C3\u30C8
793
+ - GET \`/repos/{owner}/{repo}/pulls/{number}/reviews\` \u2014 \u30EC\u30D3\u30E5\u30FC
794
+
795
+ **\u30B3\u30DF\u30C3\u30C8 & \u30EA\u30EA\u30FC\u30B9**
796
+ - GET \`/repos/{owner}/{repo}/commits?sha=branch\` \u2014 \u30B3\u30DF\u30C3\u30C8\u4E00\u89A7
797
+ - GET \`/repos/{owner}/{repo}/commits/{ref}\` \u2014 \u5358\u4E00\u30B3\u30DF\u30C3\u30C8\uFF08\u5DEE\u5206\u30FB\u7D71\u8A08\u3042\u308A\uFF09
798
+ - GET \`/repos/{owner}/{repo}/releases\` \u2014 \u30EA\u30EA\u30FC\u30B9\u4E00\u89A7
799
+ - GET \`/repos/{owner}/{repo}/releases/latest\` \u2014 \u6700\u65B0\u30EA\u30EA\u30FC\u30B9
800
+
801
+ **Actions**
802
+ - GET \`/repos/{owner}/{repo}/actions/runs\` \u2014 \u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u306E\u5B9F\u884C
803
+ - GET \`/repos/{owner}/{repo}/actions/workflows\` \u2014 \u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u5B9A\u7FA9
804
+
805
+ **\u30B5\u30FC\u30C1\uFF08\u30EC\u30FC\u30C8\u5236\u9650\u3042\u308A\uFF09**
806
+ - GET \`/search/repositories?q=topic:react+language:typescript\` \u2014 \u30EA\u30DD\u30B8\u30C8\u30EA
807
+ - GET \`/search/issues?q=is:open+is:pr+repo:foo/bar\` \u2014 Issue / PR
808
+ - GET \`/search/code?q=...+repo:foo/bar\` \u2014 \u30B3\u30FC\u30C9\uFF08\u30C8\u30FC\u30AF\u30F3\u306B \`repo\` \u30B9\u30B3\u30FC\u30D7\u304C\u5FC5\u8981\uFF09
809
+ - GET \`/search/users?q=...\` \u2014 \u30E6\u30FC\u30B6\u30FC
810
+
811
+ **\u30AF\u30A9\u30FC\u30BF**
812
+ - GET \`/rate_limit\` \u2014 REST / GraphQL / search \u306E\u6B8B\u30EC\u30FC\u30C8\u78BA\u8A8D`
813
+ },
814
+ tools
815
+ });
816
+
817
+ // src/connectors/create-connector-sdk.ts
818
+ import { readFileSync } from "fs";
819
+ import path from "path";
820
+
821
+ // src/connector-client/env.ts
822
+ function resolveEnvVar(entry, key, connectionId) {
823
+ const envVarName = entry.envVars[key];
824
+ if (!envVarName) {
825
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
826
+ }
827
+ const value = process.env[envVarName];
828
+ if (!value) {
829
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
830
+ }
831
+ return value;
832
+ }
833
+ function resolveEnvVarOptional(entry, key) {
834
+ const envVarName = entry.envVars[key];
835
+ if (!envVarName) return void 0;
836
+ return process.env[envVarName] || void 0;
837
+ }
838
+
839
+ // src/connector-client/proxy-fetch.ts
840
+ import { getContext } from "hono/context-storage";
841
+ import { getCookie } from "hono/cookie";
842
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
843
+ function normalizeHeaders(input) {
844
+ const out = {};
845
+ if (!input) return out;
846
+ new Headers(input).forEach((value, key) => {
847
+ out[key] = value;
848
+ });
849
+ return out;
850
+ }
851
+ function createSandboxProxyFetch(connectionId) {
852
+ return async (input, init) => {
853
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
854
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
855
+ if (!token || !sandboxId) {
856
+ throw new Error(
857
+ "Connection proxy is not configured. Please check your deployment settings."
858
+ );
859
+ }
860
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
861
+ const originalMethod = init?.method ?? "GET";
862
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
863
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
864
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
865
+ return fetch(proxyUrl, {
866
+ method: "POST",
867
+ headers: {
868
+ "Content-Type": "application/json",
869
+ Authorization: `Bearer ${token}`
870
+ },
871
+ body: JSON.stringify({
872
+ url: originalUrl,
873
+ method: originalMethod,
874
+ headers: normalizeHeaders(init?.headers),
875
+ body: originalBody
876
+ })
877
+ });
878
+ };
879
+ }
880
+ function createDeployedAppProxyFetch(connectionId) {
881
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
882
+ if (!projectId) {
883
+ throw new Error(
884
+ "Connection proxy is not configured. Please check your deployment settings."
885
+ );
886
+ }
887
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
888
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
889
+ return async (input, init) => {
890
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
891
+ const originalMethod = init?.method ?? "GET";
892
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
893
+ const c = getContext();
894
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
895
+ if (!appSession) {
896
+ throw new Error(
897
+ "No authentication method available for connection proxy."
898
+ );
899
+ }
900
+ return fetch(proxyUrl, {
901
+ method: "POST",
902
+ headers: {
903
+ "Content-Type": "application/json",
904
+ Authorization: `Bearer ${appSession}`
905
+ },
906
+ body: JSON.stringify({
907
+ url: originalUrl,
908
+ method: originalMethod,
909
+ headers: normalizeHeaders(init?.headers),
910
+ body: originalBody
911
+ })
912
+ });
913
+ };
914
+ }
915
+ function createProxyFetch(connectionId) {
916
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
917
+ return createSandboxProxyFetch(connectionId);
918
+ }
919
+ return createDeployedAppProxyFetch(connectionId);
920
+ }
921
+
922
+ // src/connectors/create-connector-sdk.ts
923
+ function loadConnectionsSync() {
924
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
925
+ try {
926
+ const raw = readFileSync(filePath, "utf-8");
927
+ return JSON.parse(raw);
928
+ } catch {
929
+ return {};
930
+ }
931
+ }
932
+ function createConnectorSdk(plugin, createClient2) {
933
+ return (connectionId) => {
934
+ const connections = loadConnectionsSync();
935
+ const entry = connections[connectionId];
936
+ if (!entry) {
937
+ throw new Error(
938
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
939
+ );
940
+ }
941
+ if (entry.connector.slug !== plugin.slug) {
942
+ throw new Error(
943
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
944
+ );
945
+ }
946
+ const params = {};
947
+ for (const param of Object.values(plugin.parameters)) {
948
+ if (param.required) {
949
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
950
+ } else {
951
+ const val = resolveEnvVarOptional(entry, param.slug);
952
+ if (val !== void 0) params[param.slug] = val;
953
+ }
954
+ }
955
+ return createClient2(params, createProxyFetch(connectionId));
956
+ };
957
+ }
958
+
959
+ // src/connectors/entries/github.ts
960
+ var connection = createConnectorSdk(githubConnector, createClient);
961
+ export {
962
+ connection
963
+ };