@squadbase/vite-server 0.1.9-dev.87dd3f7 → 0.1.9-dev.a57a0ac
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.
- package/dist/cli/index.js +74937 -61829
- package/dist/connectors/asana.js +15 -2
- package/dist/connectors/aws-billing.d.ts +5 -0
- package/dist/connectors/aws-billing.js +29843 -0
- package/dist/connectors/azure-sql.d.ts +5 -0
- package/dist/connectors/azure-sql.js +657 -0
- package/dist/connectors/clickup.d.ts +5 -0
- package/dist/connectors/clickup.js +850 -0
- package/dist/connectors/freshdesk.d.ts +5 -0
- package/dist/connectors/freshdesk.js +842 -0
- package/dist/connectors/freshsales.d.ts +5 -0
- package/dist/connectors/freshsales.js +867 -0
- package/dist/connectors/freshservice.d.ts +5 -0
- package/dist/connectors/freshservice.js +813 -0
- package/dist/connectors/github.d.ts +5 -0
- package/dist/connectors/github.js +963 -0
- package/dist/connectors/gmail-oauth.js +15 -2
- package/dist/connectors/gmail.js +23 -14
- package/dist/connectors/google-audit-log.js +25 -14
- package/dist/connectors/google-calendar-oauth.js +18 -2
- package/dist/connectors/google-calendar.js +40 -26
- package/dist/connectors/google-docs.js +18 -2
- package/dist/connectors/google-drive.js +15 -2
- package/dist/connectors/google-search-console-oauth.d.ts +5 -0
- package/dist/connectors/google-search-console-oauth.js +954 -0
- package/dist/connectors/google-sheets.js +18 -2
- package/dist/connectors/google-slides.js +18 -2
- package/dist/connectors/jdbc.d.ts +5 -0
- package/dist/connectors/jdbc.js +21097 -0
- package/dist/connectors/monday.d.ts +5 -0
- package/dist/connectors/monday.js +853 -0
- package/dist/connectors/oracle.d.ts +5 -0
- package/dist/connectors/oracle.js +665 -0
- package/dist/connectors/semrush.d.ts +5 -0
- package/dist/connectors/semrush.js +812 -0
- package/dist/connectors/sqlserver.d.ts +5 -0
- package/dist/connectors/sqlserver.js +656 -0
- package/dist/connectors/supabase.d.ts +5 -0
- package/dist/connectors/supabase.js +582 -0
- package/dist/connectors/tiktok-ads.js +15 -2
- package/dist/index.js +73506 -60398
- package/dist/main.js +73500 -60392
- package/dist/vite-plugin.js +73405 -60297
- 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: true, prod: true },
|
|
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
|
+
};
|