@quatrain/git-github 1.0.1-beta.0

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.
@@ -0,0 +1,29 @@
1
+ import { AbstractGitForgeAdapter, AuthStatus, CommitFile, FileTreeNode, ForgeContext, ForgeType, PrCreateOptions, PrListOptions, PrMergeOptions, PullRequest, ReleaseCreateOptions, RepoCreateOptions, RepoDetails } from "@quatrain/git";
2
+ export declare class GithubAdapter extends AbstractGitForgeAdapter {
3
+ readonly forgeType: ForgeType;
4
+ private readonly baseUrl;
5
+ private token?;
6
+ private httpClient;
7
+ constructor(context: ForgeContext);
8
+ isCliAvailable(): Promise<boolean>;
9
+ getCliInstallHint(): string;
10
+ private getHeaders;
11
+ authStatus(): Promise<AuthStatus>;
12
+ whoami(): Promise<string>;
13
+ repoView(): Promise<RepoDetails>;
14
+ repoCreate(options: RepoCreateOptions): Promise<RepoDetails>;
15
+ repoClone(target: string, destination?: string): Promise<void>;
16
+ prList(options?: PrListOptions): Promise<PullRequest[]>;
17
+ prView(prNumber: number): Promise<PullRequest>;
18
+ prCreate(options: PrCreateOptions): Promise<PullRequest>;
19
+ prCheckout(prNumber: number): Promise<void>;
20
+ prMerge(prNumber: number, options?: PrMergeOptions): Promise<void>;
21
+ releaseCreate(options: ReleaseCreateOptions): Promise<void>;
22
+ fetchFileTree(branch?: string): Promise<FileTreeNode[]>;
23
+ downloadBlob(sha: string): Promise<string>;
24
+ pull(branch?: string): Promise<void>;
25
+ push(files: CommitFile[], message: string, branch?: string): Promise<void>;
26
+ createBranch(branchName: string, fromBranch?: string): Promise<void>;
27
+ passthrough(args: string[]): Promise<number>;
28
+ }
29
+ //# sourceMappingURL=GithubAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GithubAdapter.d.ts","sourceRoot":"","sources":["../src/GithubAdapter.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,uBAAuB,EACvB,UAAU,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,eAAe,EACf,aAAa,EACb,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACZ,MAAM,eAAe,CAAC;AAGvB,qBAAa,aAAc,SAAQ,uBAAuB;IACxD,SAAgB,SAAS,EAAE,SAAS,CAAY;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,UAAU,CAAmB;gBAEzB,OAAO,EAAE,YAAY;IAepB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IASxC,iBAAiB,IAAI,MAAM;IAIlC,OAAO,CAAC,UAAU;IAYL,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IA0CjC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAMzB,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IA4ChC,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC;IAuB5D,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9D,MAAM,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA0CvD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA0C9C,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IA6CxD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBlE,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B3D,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIvD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK1C,IAAI,CAAC,MAAM,GAAE,MAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5C,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBlF,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,GAAE,MAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB5E,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAQ1D"}
@@ -0,0 +1,416 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GithubAdapter = void 0;
4
+ const git_1 = require("@quatrain/git");
5
+ const GithubHttpClient_1 = require("./GithubHttpClient");
6
+ class GithubAdapter extends git_1.AbstractGitForgeAdapter {
7
+ forgeType = "github";
8
+ baseUrl;
9
+ token;
10
+ httpClient;
11
+ constructor(context) {
12
+ super(context);
13
+ const host = context.host || "github.com";
14
+ this.baseUrl = host === "github.com" ? "https://api.github.com" : `https://${host}/api/v3`;
15
+ this.token = context.token || process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
16
+ this.httpClient = new GithubHttpClient_1.GithubHttpClient({
17
+ host: context.host,
18
+ token: this.token,
19
+ owner: context.owner,
20
+ repo: context.repo,
21
+ branch: context.branch || "main",
22
+ });
23
+ }
24
+ async isCliAvailable() {
25
+ try {
26
+ const res = await this.spawnSafe("which", ["gh"]);
27
+ return res.exitCode === 0 && res.stdout.length > 0;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ getCliInstallHint() {
34
+ return "GitHub CLI (gh) recommended: brew install gh (macOS) or sudo apt install gh (Linux)";
35
+ }
36
+ getHeaders() {
37
+ const headers = {
38
+ Accept: "application/vnd.github.v3+json",
39
+ "User-Agent": "Quatrain-Git-Client",
40
+ };
41
+ if (this.token) {
42
+ headers["Authorization"] = `token ${this.token}`;
43
+ }
44
+ return headers;
45
+ }
46
+ async authStatus() {
47
+ if (await this.isCliAvailable()) {
48
+ try {
49
+ const res = await this.spawnSafe("gh", ["auth", "status"]);
50
+ const full = res.stdout + res.stderr;
51
+ const loggedIn = res.exitCode === 0 || full.includes("Logged in to");
52
+ const userMatch = full.match(/account ([^ \n(]+)/);
53
+ if (loggedIn) {
54
+ return {
55
+ loggedIn: true,
56
+ user: userMatch ? userMatch[1] : undefined,
57
+ host: this.context.host || "github.com",
58
+ tokenType: "gh-cli",
59
+ };
60
+ }
61
+ }
62
+ catch {
63
+ }
64
+ }
65
+ try {
66
+ const res = await fetch(`${this.baseUrl}/user`, { headers: this.getHeaders() });
67
+ if (res.ok) {
68
+ const user = (await res.json());
69
+ return {
70
+ loggedIn: true,
71
+ user: user.login,
72
+ host: this.context.host || "github.com",
73
+ tokenType: "Bearer/Token",
74
+ };
75
+ }
76
+ return {
77
+ loggedIn: false,
78
+ host: this.context.host || "github.com",
79
+ error: `HTTP ${res.status}: ${res.statusText}`,
80
+ };
81
+ }
82
+ catch (e) {
83
+ return { loggedIn: false, host: this.context.host || "github.com", error: e.message };
84
+ }
85
+ }
86
+ async whoami() {
87
+ const status = await this.authStatus();
88
+ return status.user || "unauthenticated";
89
+ }
90
+ async repoView() {
91
+ if (await this.isCliAvailable()) {
92
+ const res = await this.spawnSafe("gh", [
93
+ "repo",
94
+ "view",
95
+ "--json",
96
+ "name,nameWithOwner,owner,defaultBranchRef,isPrivate,url,sshUrl",
97
+ ]);
98
+ if (res.exitCode === 0 && res.stdout) {
99
+ const data = JSON.parse(res.stdout);
100
+ return {
101
+ name: data.name,
102
+ fullName: data.nameWithOwner,
103
+ owner: data.owner?.login || this.context.owner,
104
+ defaultBranch: data.defaultBranchRef?.name || "main",
105
+ isPrivate: data.isPrivate,
106
+ cloneUrl: data.url + ".git",
107
+ sshUrl: data.sshUrl,
108
+ webUrl: data.url,
109
+ };
110
+ }
111
+ }
112
+ const owner = this.context.owner;
113
+ const repo = this.context.repo;
114
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}`, { headers: this.getHeaders() });
115
+ if (!res.ok) {
116
+ throw new Error(`Failed to fetch repo ${owner}/${repo}: HTTP ${res.status}`);
117
+ }
118
+ const data = (await res.json());
119
+ return {
120
+ name: data.name,
121
+ fullName: data.full_name,
122
+ owner: data.owner?.login || owner,
123
+ defaultBranch: data.default_branch || "main",
124
+ isPrivate: data.private,
125
+ cloneUrl: data.clone_url,
126
+ sshUrl: data.ssh_url,
127
+ webUrl: data.html_url,
128
+ description: data.description,
129
+ };
130
+ }
131
+ async repoCreate(options) {
132
+ if (await this.isCliAvailable()) {
133
+ const args = ["repo", "create", options.name, options.isPrivate ? "--private" : "--public"];
134
+ if (options.description)
135
+ args.push("--description", options.description);
136
+ const res = await this.spawnSafe("gh", args);
137
+ if (res.exitCode === 0)
138
+ return this.repoView();
139
+ }
140
+ const endpoint = options.org ? `${this.baseUrl}/orgs/${options.org}/repos` : `${this.baseUrl}/user/repos`;
141
+ const res = await fetch(endpoint, {
142
+ method: "POST",
143
+ headers: this.getHeaders(),
144
+ body: JSON.stringify({
145
+ name: options.name,
146
+ private: options.isPrivate !== false,
147
+ description: options.description || "",
148
+ }),
149
+ });
150
+ if (!res.ok)
151
+ throw new Error(`Failed to create repository: HTTP ${res.status}`);
152
+ return this.repoView();
153
+ }
154
+ async repoClone(target, destination) {
155
+ const args = ["clone", target.startsWith("http") ? target : `https://github.com/${target}.git`];
156
+ if (destination)
157
+ args.push(destination);
158
+ const res = await this.spawnSafe("git", args);
159
+ if (res.exitCode !== 0)
160
+ throw new Error(`git clone failed: ${res.stderr}`);
161
+ }
162
+ async prList(options) {
163
+ if (await this.isCliAvailable()) {
164
+ const args = ["pr", "list", "--json", "number,title,state,author,headRefName,baseRefName,url,updatedAt"];
165
+ if (options?.state)
166
+ args.push("--state", options.state);
167
+ if (options?.limit)
168
+ args.push("--limit", options.limit.toString());
169
+ const res = await this.spawnSafe("gh", args);
170
+ if (res.exitCode === 0 && res.stdout) {
171
+ const list = JSON.parse(res.stdout);
172
+ return list.map((item) => ({
173
+ number: item.number,
174
+ title: item.title,
175
+ state: item.state?.toLowerCase() === "merged" ? "merged" : item.state?.toLowerCase() === "closed" ? "closed" : "open",
176
+ author: item.author?.login || "unknown",
177
+ sourceBranch: item.headRefName,
178
+ targetBranch: item.baseRefName,
179
+ url: item.url,
180
+ updatedAt: item.updatedAt,
181
+ }));
182
+ }
183
+ }
184
+ const owner = this.context.owner;
185
+ const repo = this.context.repo;
186
+ const state = options?.state || "open";
187
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/pulls?state=${state}`, {
188
+ headers: this.getHeaders(),
189
+ });
190
+ if (!res.ok)
191
+ throw new Error(`Failed to list PRs: HTTP ${res.status}`);
192
+ const list = (await res.json());
193
+ return list.map((item) => ({
194
+ number: item.number,
195
+ title: item.title,
196
+ state: item.merged_at ? "merged" : item.state === "closed" ? "closed" : "open",
197
+ author: item.user?.login || "unknown",
198
+ sourceBranch: item.head?.ref,
199
+ targetBranch: item.base?.ref,
200
+ url: item.html_url,
201
+ updatedAt: item.updated_at,
202
+ }));
203
+ }
204
+ async prView(prNumber) {
205
+ if (await this.isCliAvailable()) {
206
+ const res = await this.spawnSafe("gh", [
207
+ "pr",
208
+ "view",
209
+ prNumber.toString(),
210
+ "--json",
211
+ "number,title,state,author,headRefName,baseRefName,url",
212
+ ]);
213
+ if (res.exitCode === 0 && res.stdout) {
214
+ const item = JSON.parse(res.stdout);
215
+ return {
216
+ number: item.number,
217
+ title: item.title,
218
+ state: item.state?.toLowerCase() === "merged" ? "merged" : item.state?.toLowerCase() === "closed" ? "closed" : "open",
219
+ author: item.author?.login || "unknown",
220
+ sourceBranch: item.headRefName,
221
+ targetBranch: item.baseRefName,
222
+ url: item.url,
223
+ };
224
+ }
225
+ }
226
+ const owner = this.context.owner;
227
+ const repo = this.context.repo;
228
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/pulls/${prNumber}`, {
229
+ headers: this.getHeaders(),
230
+ });
231
+ if (!res.ok)
232
+ throw new Error(`Failed to fetch PR #${prNumber}: HTTP ${res.status}`);
233
+ const item = (await res.json());
234
+ return {
235
+ number: item.number,
236
+ title: item.title,
237
+ state: item.merged_at ? "merged" : item.state === "closed" ? "closed" : "open",
238
+ author: item.user?.login || "unknown",
239
+ sourceBranch: item.head?.ref,
240
+ targetBranch: item.base?.ref,
241
+ url: item.html_url,
242
+ };
243
+ }
244
+ async prCreate(options) {
245
+ if (await this.isCliAvailable()) {
246
+ const args = ["pr", "create", "--title", options.title];
247
+ if (options.body)
248
+ args.push("--body", options.body);
249
+ if (options.base)
250
+ args.push("--base", options.base);
251
+ if (options.head)
252
+ args.push("--head", options.head);
253
+ const res = await this.spawnSafe("gh", args);
254
+ if (res.exitCode === 0) {
255
+ const list = await this.prList({ limit: 1 });
256
+ return list[0];
257
+ }
258
+ }
259
+ const owner = this.context.owner;
260
+ const repo = this.context.repo;
261
+ let head = options.head;
262
+ if (!head) {
263
+ const branchRes = await this.spawnSafe("git", ["branch", "--show-current"]);
264
+ head = branchRes.stdout || "main";
265
+ }
266
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/pulls`, {
267
+ method: "POST",
268
+ headers: this.getHeaders(),
269
+ body: JSON.stringify({
270
+ title: options.title,
271
+ body: options.body || "",
272
+ base: options.base || "main",
273
+ head,
274
+ }),
275
+ });
276
+ if (!res.ok)
277
+ throw new Error(`Failed to create PR: HTTP ${res.status} - ${await res.text()}`);
278
+ const item = (await res.json());
279
+ return {
280
+ number: item.number,
281
+ title: item.title,
282
+ state: "open",
283
+ author: item.user?.login || "unknown",
284
+ sourceBranch: item.head?.ref,
285
+ targetBranch: item.base?.ref,
286
+ url: item.html_url,
287
+ };
288
+ }
289
+ async prCheckout(prNumber) {
290
+ if (await this.isCliAvailable()) {
291
+ const res = await this.spawnSafe("gh", ["pr", "checkout", prNumber.toString()]);
292
+ if (res.exitCode === 0)
293
+ return;
294
+ }
295
+ await this.spawnSafe("git", ["fetch", "origin", `pull/${prNumber}/head:pr-${prNumber}`]);
296
+ await this.spawnSafe("git", ["checkout", `pr-${prNumber}`]);
297
+ }
298
+ async prMerge(prNumber, options) {
299
+ if (await this.isCliAvailable()) {
300
+ const args = ["pr", "merge", prNumber.toString()];
301
+ if (options?.method === "squash")
302
+ args.push("--squash");
303
+ else if (options?.method === "rebase")
304
+ args.push("--rebase");
305
+ else
306
+ args.push("--merge");
307
+ if (options?.deleteBranch)
308
+ args.push("--delete-branch");
309
+ const res = await this.spawnSafe("gh", args);
310
+ if (res.exitCode === 0)
311
+ return;
312
+ }
313
+ const owner = this.context.owner;
314
+ const repo = this.context.repo;
315
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/pulls/${prNumber}/merge`, {
316
+ method: "PUT",
317
+ headers: this.getHeaders(),
318
+ body: JSON.stringify({
319
+ merge_method: options?.method || "merge",
320
+ }),
321
+ });
322
+ if (!res.ok)
323
+ throw new Error(`Failed to merge PR #${prNumber}: HTTP ${res.status}`);
324
+ }
325
+ async releaseCreate(options) {
326
+ if (await this.isCliAvailable()) {
327
+ const args = ["release", "create", options.tag];
328
+ if (options.title)
329
+ args.push("--title", options.title);
330
+ if (options.notes)
331
+ args.push("--notes", options.notes);
332
+ if (options.draft)
333
+ args.push("--draft");
334
+ if (options.prerelease)
335
+ args.push("--prerelease");
336
+ const res = await this.spawnSafe("gh", args);
337
+ if (res.exitCode === 0)
338
+ return;
339
+ }
340
+ const owner = this.context.owner;
341
+ const repo = this.context.repo;
342
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/releases`, {
343
+ method: "POST",
344
+ headers: this.getHeaders(),
345
+ body: JSON.stringify({
346
+ tag_name: options.tag,
347
+ name: options.title || options.tag,
348
+ body: options.notes || "",
349
+ draft: !!options.draft,
350
+ prerelease: !!options.prerelease,
351
+ }),
352
+ });
353
+ if (!res.ok)
354
+ throw new Error(`Failed to create release: HTTP ${res.status}`);
355
+ }
356
+ async fetchFileTree(branch) {
357
+ return this.httpClient.fetchFileTree(branch);
358
+ }
359
+ async downloadBlob(sha) {
360
+ return this.httpClient.downloadBlob(sha);
361
+ }
362
+ async pull(branch = "main") {
363
+ const owner = this.context.owner;
364
+ const repo = this.context.repo;
365
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/branches/${branch}`, {
366
+ headers: this.getHeaders(),
367
+ });
368
+ if (!res.ok)
369
+ throw new Error(`Failed to pull branch ${branch}: HTTP ${res.status}`);
370
+ }
371
+ async push(files, message, branch = "main") {
372
+ const owner = this.context.owner;
373
+ const repo = this.context.repo;
374
+ for (const file of files) {
375
+ await fetch(`${this.baseUrl}/repos/${owner}/${repo}/contents/${file.path}`, {
376
+ method: "PUT",
377
+ headers: this.getHeaders(),
378
+ body: JSON.stringify({
379
+ branch,
380
+ message,
381
+ content: Buffer.from(file.content).toString("base64"),
382
+ }),
383
+ });
384
+ }
385
+ }
386
+ async createBranch(branchName, fromBranch = "main") {
387
+ const owner = this.context.owner;
388
+ const repo = this.context.repo;
389
+ const refRes = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/git/ref/heads/${fromBranch}`, {
390
+ headers: this.getHeaders(),
391
+ });
392
+ if (!refRes.ok)
393
+ throw new Error(`Failed to get ref for ${fromBranch}`);
394
+ const refData = (await refRes.json());
395
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/git/refs`, {
396
+ method: "POST",
397
+ headers: this.getHeaders(),
398
+ body: JSON.stringify({
399
+ ref: `refs/heads/${branchName}`,
400
+ sha: refData.object.sha,
401
+ }),
402
+ });
403
+ if (!res.ok)
404
+ throw new Error(`Failed to create branch ${branchName}: HTTP ${res.status}`);
405
+ }
406
+ async passthrough(args) {
407
+ if (await this.isCliAvailable()) {
408
+ const res = await this.spawnSafe("gh", args);
409
+ return res.exitCode;
410
+ }
411
+ console.error(`[GithubAdapter] ${this.getCliInstallHint()}`);
412
+ return 1;
413
+ }
414
+ }
415
+ exports.GithubAdapter = GithubAdapter;
416
+ //# sourceMappingURL=GithubAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GithubAdapter.js","sourceRoot":"","sources":["../src/GithubAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { AbstractGitHttpClient, FileTreeNode } from "@quatrain/git";
2
+ export interface GithubHttpClientConfig {
3
+ token?: string;
4
+ host?: string;
5
+ owner: string;
6
+ repo: string;
7
+ branch?: string;
8
+ }
9
+ export declare class GithubHttpClient extends AbstractGitHttpClient {
10
+ private token?;
11
+ private host;
12
+ private owner;
13
+ private repo;
14
+ private branch;
15
+ private baseUrl;
16
+ constructor(config: GithubHttpClientConfig);
17
+ private getHeaders;
18
+ fetchFileTree(branch?: string): Promise<FileTreeNode[]>;
19
+ downloadBlob(sha: string): Promise<string>;
20
+ }
21
+ //# sourceMappingURL=GithubHttpClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GithubHttpClient.d.ts","sourceRoot":"","sources":["../src/GithubHttpClient.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEpE,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,gBAAiB,SAAQ,qBAAqB;IACzD,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,sBAAsB;IAU1C,OAAO,CAAC,UAAU;IAcZ,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAuBvD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAcjD"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GithubHttpClient = void 0;
4
+ const git_1 = require("@quatrain/git");
5
+ class GithubHttpClient extends git_1.AbstractGitHttpClient {
6
+ token;
7
+ host;
8
+ owner;
9
+ repo;
10
+ branch;
11
+ baseUrl;
12
+ constructor(config) {
13
+ super();
14
+ this.token = config.token;
15
+ this.host = config.host || "github.com";
16
+ this.owner = config.owner;
17
+ this.repo = config.repo;
18
+ this.branch = config.branch || "main";
19
+ this.baseUrl = this.host === "github.com" ? "https://api.github.com" : `https://${this.host}/api/v3`;
20
+ }
21
+ getHeaders() {
22
+ const headers = {
23
+ Accept: "application/vnd.github.v3+json",
24
+ "User-Agent": "Quatrain-Git-Client",
25
+ };
26
+ if (this.token) {
27
+ headers["Authorization"] = `token ${this.token}`;
28
+ }
29
+ return headers;
30
+ }
31
+ async fetchFileTree(branch) {
32
+ const targetBranch = branch || this.branch;
33
+ const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/git/trees/${targetBranch}?recursive=1`;
34
+ const res = await fetch(url, { headers: this.getHeaders() });
35
+ if (!res.ok) {
36
+ const errBody = await res.text();
37
+ throw new Error(`GitHub API error ${res.status}: ${errBody}`);
38
+ }
39
+ const data = (await res.json());
40
+ const tree = data.tree || [];
41
+ return tree.map((item) => ({
42
+ path: item.path,
43
+ type: item.type === "blob" ? "blob" : item.type === "tree" ? "tree" : item.type,
44
+ sha: item.sha,
45
+ size: item.size,
46
+ }));
47
+ }
48
+ async downloadBlob(sha) {
49
+ const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/git/blobs/${sha}`;
50
+ const headers = {
51
+ ...this.getHeaders(),
52
+ Accept: "application/vnd.github.v3.raw",
53
+ };
54
+ const res = await fetch(url, { headers });
55
+ if (!res.ok) {
56
+ throw new Error(`Failed to download blob ${sha}: status ${res.status}`);
57
+ }
58
+ return res.text();
59
+ }
60
+ }
61
+ exports.GithubHttpClient = GithubHttpClient;
62
+ //# sourceMappingURL=GithubHttpClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GithubHttpClient.js","sourceRoot":"","sources":["../src/GithubHttpClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from "./GithubAdapter";
2
+ export * from "./GithubHttpClient";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./GithubAdapter"), exports);
18
+ __exportStar(require("./GithubHttpClient"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAMA,kDAAgC;AAChC,qDAAmC"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@quatrain/git-github",
3
+ "version": "1.0.1-beta.0",
4
+ "license": "AGPL-3.0-only",
5
+ "description": "Quatrain Core GitHub adapter with bi-modal CLI (gh) and HTTP REST API engine",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bun": "src/index.ts",
9
+ "files": [
10
+ "LICENSE.md",
11
+ "src/",
12
+ "dist/",
13
+ "README.md"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/Quatrain/Core.git",
18
+ "directory": "packages/git-github"
19
+ },
20
+ "author": "Quatrain Développement SAS <developers@quatrain.com>",
21
+ "dependencies": {
22
+ "@quatrain/core": "^1.2.20",
23
+ "@quatrain/git": "^1.0.1-beta.0"
24
+ },
25
+ "devDependencies": {
26
+ "@tsconfig/recommended": "^1.0.1",
27
+ "@types/node": "^22.10.1",
28
+ "typescript": "^5.2.2"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "wbuild": "tsc --watch"
33
+ }
34
+ }
@@ -0,0 +1,449 @@
1
+ // packages/git-github/src/GithubAdapter.ts
2
+ /**
3
+ * @quatrain/git-github - GithubAdapter
4
+ * @description Bi-modal GitHub adapter (gh CLI <-> GitHub REST API)
5
+ * @license AGPL-3.0-only
6
+ */
7
+
8
+ import {
9
+ AbstractGitForgeAdapter,
10
+ AuthStatus,
11
+ CommitFile,
12
+ FileTreeNode,
13
+ ForgeContext,
14
+ ForgeType,
15
+ PrCreateOptions,
16
+ PrListOptions,
17
+ PrMergeOptions,
18
+ PullRequest,
19
+ ReleaseCreateOptions,
20
+ RepoCreateOptions,
21
+ RepoDetails,
22
+ } from "@quatrain/git";
23
+ import { GithubHttpClient } from "./GithubHttpClient";
24
+
25
+ export class GithubAdapter extends AbstractGitForgeAdapter {
26
+ public readonly forgeType: ForgeType = "github";
27
+ private readonly baseUrl: string;
28
+ private token?: string;
29
+ private httpClient: GithubHttpClient;
30
+
31
+ constructor(context: ForgeContext) {
32
+ super(context);
33
+ const host = context.host || "github.com";
34
+ this.baseUrl = host === "github.com" ? "https://api.github.com" : `https://${host}/api/v3`;
35
+ this.token = context.token || process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
36
+
37
+ this.httpClient = new GithubHttpClient({
38
+ host: context.host,
39
+ token: this.token,
40
+ owner: context.owner,
41
+ repo: context.repo,
42
+ branch: context.branch || "main",
43
+ });
44
+ }
45
+
46
+ public async isCliAvailable(): Promise<boolean> {
47
+ try {
48
+ const res = await this.spawnSafe("which", ["gh"]);
49
+ return res.exitCode === 0 && res.stdout.length > 0;
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ public getCliInstallHint(): string {
56
+ return "GitHub CLI (gh) recommended: brew install gh (macOS) or sudo apt install gh (Linux)";
57
+ }
58
+
59
+ private getHeaders(): Record<string, string> {
60
+ const headers: Record<string, string> = {
61
+ Accept: "application/vnd.github.v3+json",
62
+ "User-Agent": "Quatrain-Git-Client",
63
+ };
64
+ if (this.token) {
65
+ headers["Authorization"] = `token ${this.token}`;
66
+ }
67
+ return headers;
68
+ }
69
+
70
+ // --- Authentication ---
71
+ public async authStatus(): Promise<AuthStatus> {
72
+ if (await this.isCliAvailable()) {
73
+ try {
74
+ const res = await this.spawnSafe("gh", ["auth", "status"]);
75
+ const full = res.stdout + res.stderr;
76
+ const loggedIn = res.exitCode === 0 || full.includes("Logged in to");
77
+ const userMatch = full.match(/account ([^ \n(]+)/);
78
+ if (loggedIn) {
79
+ return {
80
+ loggedIn: true,
81
+ user: userMatch ? userMatch[1] : undefined,
82
+ host: this.context.host || "github.com",
83
+ tokenType: "gh-cli",
84
+ };
85
+ }
86
+ } catch {
87
+ // Fallback
88
+ }
89
+ }
90
+
91
+ // HTTP Fallback
92
+ try {
93
+ const res = await fetch(`${this.baseUrl}/user`, { headers: this.getHeaders() });
94
+ if (res.ok) {
95
+ const user = (await res.json()) as any;
96
+ return {
97
+ loggedIn: true,
98
+ user: user.login,
99
+ host: this.context.host || "github.com",
100
+ tokenType: "Bearer/Token",
101
+ };
102
+ }
103
+ return {
104
+ loggedIn: false,
105
+ host: this.context.host || "github.com",
106
+ error: `HTTP ${res.status}: ${res.statusText}`,
107
+ };
108
+ } catch (e: any) {
109
+ return { loggedIn: false, host: this.context.host || "github.com", error: e.message };
110
+ }
111
+ }
112
+
113
+ public async whoami(): Promise<string> {
114
+ const status = await this.authStatus();
115
+ return status.user || "unauthenticated";
116
+ }
117
+
118
+ // --- Repositories ---
119
+ public async repoView(): Promise<RepoDetails> {
120
+ if (await this.isCliAvailable()) {
121
+ const res = await this.spawnSafe("gh", [
122
+ "repo",
123
+ "view",
124
+ "--json",
125
+ "name,nameWithOwner,owner,defaultBranchRef,isPrivate,url,sshUrl",
126
+ ]);
127
+ if (res.exitCode === 0 && res.stdout) {
128
+ const data = JSON.parse(res.stdout);
129
+ return {
130
+ name: data.name,
131
+ fullName: data.nameWithOwner,
132
+ owner: data.owner?.login || this.context.owner,
133
+ defaultBranch: data.defaultBranchRef?.name || "main",
134
+ isPrivate: data.isPrivate,
135
+ cloneUrl: data.url + ".git",
136
+ sshUrl: data.sshUrl,
137
+ webUrl: data.url,
138
+ };
139
+ }
140
+ }
141
+
142
+ // HTTP Fallback
143
+ const owner = this.context.owner;
144
+ const repo = this.context.repo;
145
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}`, { headers: this.getHeaders() });
146
+ if (!res.ok) {
147
+ throw new Error(`Failed to fetch repo ${owner}/${repo}: HTTP ${res.status}`);
148
+ }
149
+ const data = (await res.json()) as any;
150
+ return {
151
+ name: data.name,
152
+ fullName: data.full_name,
153
+ owner: data.owner?.login || owner,
154
+ defaultBranch: data.default_branch || "main",
155
+ isPrivate: data.private,
156
+ cloneUrl: data.clone_url,
157
+ sshUrl: data.ssh_url,
158
+ webUrl: data.html_url,
159
+ description: data.description,
160
+ };
161
+ }
162
+
163
+ public async repoCreate(options: RepoCreateOptions): Promise<RepoDetails> {
164
+ if (await this.isCliAvailable()) {
165
+ const args = ["repo", "create", options.name, options.isPrivate ? "--private" : "--public"];
166
+ if (options.description) args.push("--description", options.description);
167
+ const res = await this.spawnSafe("gh", args);
168
+ if (res.exitCode === 0) return this.repoView();
169
+ }
170
+
171
+ // HTTP Fallback
172
+ const endpoint = options.org ? `${this.baseUrl}/orgs/${options.org}/repos` : `${this.baseUrl}/user/repos`;
173
+ const res = await fetch(endpoint, {
174
+ method: "POST",
175
+ headers: this.getHeaders(),
176
+ body: JSON.stringify({
177
+ name: options.name,
178
+ private: options.isPrivate !== false,
179
+ description: options.description || "",
180
+ }),
181
+ });
182
+ if (!res.ok) throw new Error(`Failed to create repository: HTTP ${res.status}`);
183
+ return this.repoView();
184
+ }
185
+
186
+ public async repoClone(target: string, destination?: string): Promise<void> {
187
+ const args = ["clone", target.startsWith("http") ? target : `https://github.com/${target}.git`];
188
+ if (destination) args.push(destination);
189
+ const res = await this.spawnSafe("git", args);
190
+ if (res.exitCode !== 0) throw new Error(`git clone failed: ${res.stderr}`);
191
+ }
192
+
193
+ // --- Pull Requests ---
194
+ public async prList(options?: PrListOptions): Promise<PullRequest[]> {
195
+ if (await this.isCliAvailable()) {
196
+ const args = ["pr", "list", "--json", "number,title,state,author,headRefName,baseRefName,url,updatedAt"];
197
+ if (options?.state) args.push("--state", options.state);
198
+ if (options?.limit) args.push("--limit", options.limit.toString());
199
+ const res = await this.spawnSafe("gh", args);
200
+ if (res.exitCode === 0 && res.stdout) {
201
+ const list = JSON.parse(res.stdout);
202
+ return list.map((item: any) => ({
203
+ number: item.number,
204
+ title: item.title,
205
+ state: item.state?.toLowerCase() === "merged" ? "merged" : item.state?.toLowerCase() === "closed" ? "closed" : "open",
206
+ author: item.author?.login || "unknown",
207
+ sourceBranch: item.headRefName,
208
+ targetBranch: item.baseRefName,
209
+ url: item.url,
210
+ updatedAt: item.updatedAt,
211
+ }));
212
+ }
213
+ }
214
+
215
+ // HTTP Fallback
216
+ const owner = this.context.owner;
217
+ const repo = this.context.repo;
218
+ const state = options?.state || "open";
219
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/pulls?state=${state}`, {
220
+ headers: this.getHeaders(),
221
+ });
222
+ if (!res.ok) throw new Error(`Failed to list PRs: HTTP ${res.status}`);
223
+ const list = (await res.json()) as any[];
224
+ return list.map((item: any) => ({
225
+ number: item.number,
226
+ title: item.title,
227
+ state: item.merged_at ? "merged" : item.state === "closed" ? "closed" : "open",
228
+ author: item.user?.login || "unknown",
229
+ sourceBranch: item.head?.ref,
230
+ targetBranch: item.base?.ref,
231
+ url: item.html_url,
232
+ updatedAt: item.updated_at,
233
+ }));
234
+ }
235
+
236
+ public async prView(prNumber: number): Promise<PullRequest> {
237
+ if (await this.isCliAvailable()) {
238
+ const res = await this.spawnSafe("gh", [
239
+ "pr",
240
+ "view",
241
+ prNumber.toString(),
242
+ "--json",
243
+ "number,title,state,author,headRefName,baseRefName,url",
244
+ ]);
245
+ if (res.exitCode === 0 && res.stdout) {
246
+ const item = JSON.parse(res.stdout);
247
+ return {
248
+ number: item.number,
249
+ title: item.title,
250
+ state: item.state?.toLowerCase() === "merged" ? "merged" : item.state?.toLowerCase() === "closed" ? "closed" : "open",
251
+ author: item.author?.login || "unknown",
252
+ sourceBranch: item.headRefName,
253
+ targetBranch: item.baseRefName,
254
+ url: item.url,
255
+ };
256
+ }
257
+ }
258
+
259
+ // HTTP Fallback
260
+ const owner = this.context.owner;
261
+ const repo = this.context.repo;
262
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/pulls/${prNumber}`, {
263
+ headers: this.getHeaders(),
264
+ });
265
+ if (!res.ok) throw new Error(`Failed to fetch PR #${prNumber}: HTTP ${res.status}`);
266
+ const item = (await res.json()) as any;
267
+ return {
268
+ number: item.number,
269
+ title: item.title,
270
+ state: item.merged_at ? "merged" : item.state === "closed" ? "closed" : "open",
271
+ author: item.user?.login || "unknown",
272
+ sourceBranch: item.head?.ref,
273
+ targetBranch: item.base?.ref,
274
+ url: item.html_url,
275
+ };
276
+ }
277
+
278
+ public async prCreate(options: PrCreateOptions): Promise<PullRequest> {
279
+ if (await this.isCliAvailable()) {
280
+ const args = ["pr", "create", "--title", options.title];
281
+ if (options.body) args.push("--body", options.body);
282
+ if (options.base) args.push("--base", options.base);
283
+ if (options.head) args.push("--head", options.head);
284
+ const res = await this.spawnSafe("gh", args);
285
+ if (res.exitCode === 0) {
286
+ const list = await this.prList({ limit: 1 });
287
+ return list[0];
288
+ }
289
+ }
290
+
291
+ // HTTP Fallback
292
+ const owner = this.context.owner;
293
+ const repo = this.context.repo;
294
+ let head = options.head;
295
+ if (!head) {
296
+ const branchRes = await this.spawnSafe("git", ["branch", "--show-current"]);
297
+ head = branchRes.stdout || "main";
298
+ }
299
+
300
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/pulls`, {
301
+ method: "POST",
302
+ headers: this.getHeaders(),
303
+ body: JSON.stringify({
304
+ title: options.title,
305
+ body: options.body || "",
306
+ base: options.base || "main",
307
+ head,
308
+ }),
309
+ });
310
+ if (!res.ok) throw new Error(`Failed to create PR: HTTP ${res.status} - ${await res.text()}`);
311
+ const item = (await res.json()) as any;
312
+ return {
313
+ number: item.number,
314
+ title: item.title,
315
+ state: "open",
316
+ author: item.user?.login || "unknown",
317
+ sourceBranch: item.head?.ref,
318
+ targetBranch: item.base?.ref,
319
+ url: item.html_url,
320
+ };
321
+ }
322
+
323
+ public async prCheckout(prNumber: number): Promise<void> {
324
+ if (await this.isCliAvailable()) {
325
+ const res = await this.spawnSafe("gh", ["pr", "checkout", prNumber.toString()]);
326
+ if (res.exitCode === 0) return;
327
+ }
328
+ await this.spawnSafe("git", ["fetch", "origin", `pull/${prNumber}/head:pr-${prNumber}`]);
329
+ await this.spawnSafe("git", ["checkout", `pr-${prNumber}`]);
330
+ }
331
+
332
+ public async prMerge(prNumber: number, options?: PrMergeOptions): Promise<void> {
333
+ if (await this.isCliAvailable()) {
334
+ const args = ["pr", "merge", prNumber.toString()];
335
+ if (options?.method === "squash") args.push("--squash");
336
+ else if (options?.method === "rebase") args.push("--rebase");
337
+ else args.push("--merge");
338
+ if (options?.deleteBranch) args.push("--delete-branch");
339
+ const res = await this.spawnSafe("gh", args);
340
+ if (res.exitCode === 0) return;
341
+ }
342
+
343
+ const owner = this.context.owner;
344
+ const repo = this.context.repo;
345
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/pulls/${prNumber}/merge`, {
346
+ method: "PUT",
347
+ headers: this.getHeaders(),
348
+ body: JSON.stringify({
349
+ merge_method: options?.method || "merge",
350
+ }),
351
+ });
352
+ if (!res.ok) throw new Error(`Failed to merge PR #${prNumber}: HTTP ${res.status}`);
353
+ }
354
+
355
+ // --- Releases ---
356
+ public async releaseCreate(options: ReleaseCreateOptions): Promise<void> {
357
+ if (await this.isCliAvailable()) {
358
+ const args = ["release", "create", options.tag];
359
+ if (options.title) args.push("--title", options.title);
360
+ if (options.notes) args.push("--notes", options.notes);
361
+ if (options.draft) args.push("--draft");
362
+ if (options.prerelease) args.push("--prerelease");
363
+ const res = await this.spawnSafe("gh", args);
364
+ if (res.exitCode === 0) return;
365
+ }
366
+
367
+ const owner = this.context.owner;
368
+ const repo = this.context.repo;
369
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/releases`, {
370
+ method: "POST",
371
+ headers: this.getHeaders(),
372
+ body: JSON.stringify({
373
+ tag_name: options.tag,
374
+ name: options.title || options.tag,
375
+ body: options.notes || "",
376
+ draft: !!options.draft,
377
+ prerelease: !!options.prerelease,
378
+ }),
379
+ });
380
+ if (!res.ok) throw new Error(`Failed to create release: HTTP ${res.status}`);
381
+ }
382
+
383
+ // --- File Sync & Modaka Support ---
384
+ public async fetchFileTree(branch?: string): Promise<FileTreeNode[]> {
385
+ return this.httpClient.fetchFileTree(branch);
386
+ }
387
+
388
+ public async downloadBlob(sha: string): Promise<string> {
389
+ return this.httpClient.downloadBlob(sha);
390
+ }
391
+
392
+ // --- Core Code Repository Adapter Compatibility ---
393
+ public async pull(branch: string = "main"): Promise<void> {
394
+ const owner = this.context.owner;
395
+ const repo = this.context.repo;
396
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/branches/${branch}`, {
397
+ headers: this.getHeaders(),
398
+ });
399
+ if (!res.ok) throw new Error(`Failed to pull branch ${branch}: HTTP ${res.status}`);
400
+ }
401
+
402
+ public async push(files: CommitFile[], message: string, branch: string = "main"): Promise<void> {
403
+ const owner = this.context.owner;
404
+ const repo = this.context.repo;
405
+
406
+ // Direct tree / commit API via HTTP
407
+ for (const file of files) {
408
+ await fetch(`${this.baseUrl}/repos/${owner}/${repo}/contents/${file.path}`, {
409
+ method: "PUT",
410
+ headers: this.getHeaders(),
411
+ body: JSON.stringify({
412
+ branch,
413
+ message,
414
+ content: Buffer.from(file.content).toString("base64"),
415
+ }),
416
+ });
417
+ }
418
+ }
419
+
420
+ public async createBranch(branchName: string, fromBranch: string = "main"): Promise<void> {
421
+ const owner = this.context.owner;
422
+ const repo = this.context.repo;
423
+
424
+ const refRes = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/git/ref/heads/${fromBranch}`, {
425
+ headers: this.getHeaders(),
426
+ });
427
+ if (!refRes.ok) throw new Error(`Failed to get ref for ${fromBranch}`);
428
+ const refData = (await refRes.json()) as any;
429
+
430
+ const res = await fetch(`${this.baseUrl}/repos/${owner}/${repo}/git/refs`, {
431
+ method: "POST",
432
+ headers: this.getHeaders(),
433
+ body: JSON.stringify({
434
+ ref: `refs/heads/${branchName}`,
435
+ sha: refData.object.sha,
436
+ }),
437
+ });
438
+ if (!res.ok) throw new Error(`Failed to create branch ${branchName}: HTTP ${res.status}`);
439
+ }
440
+
441
+ public async passthrough(args: string[]): Promise<number> {
442
+ if (await this.isCliAvailable()) {
443
+ const res = await this.spawnSafe("gh", args);
444
+ return res.exitCode;
445
+ }
446
+ console.error(`[GithubAdapter] ${this.getCliInstallHint()}`);
447
+ return 1;
448
+ }
449
+ }
@@ -0,0 +1,87 @@
1
+ // packages/git-github/src/GithubHttpClient.ts
2
+ /**
3
+ * @quatrain/git-github - GithubHttpClient
4
+ * @description Lightweight HTTP-based GitHub Git client for Web/WebView/Edge environments
5
+ * @license AGPL-3.0-only
6
+ */
7
+
8
+ import { AbstractGitHttpClient, FileTreeNode } from "@quatrain/git";
9
+
10
+ export interface GithubHttpClientConfig {
11
+ token?: string;
12
+ host?: string;
13
+ owner: string;
14
+ repo: string;
15
+ branch?: string;
16
+ }
17
+
18
+ export class GithubHttpClient extends AbstractGitHttpClient {
19
+ private token?: string;
20
+ private host: string;
21
+ private owner: string;
22
+ private repo: string;
23
+ private branch: string;
24
+ private baseUrl: string;
25
+
26
+ constructor(config: GithubHttpClientConfig) {
27
+ super();
28
+ this.token = config.token;
29
+ this.host = config.host || "github.com";
30
+ this.owner = config.owner;
31
+ this.repo = config.repo;
32
+ this.branch = config.branch || "main";
33
+ this.baseUrl = this.host === "github.com" ? "https://api.github.com" : `https://${this.host}/api/v3`;
34
+ }
35
+
36
+ private getHeaders(): Record<string, string> {
37
+ const headers: Record<string, string> = {
38
+ Accept: "application/vnd.github.v3+json",
39
+ "User-Agent": "Quatrain-Git-Client",
40
+ };
41
+ if (this.token) {
42
+ headers["Authorization"] = `token ${this.token}`;
43
+ }
44
+ return headers;
45
+ }
46
+
47
+ /**
48
+ * Fetches the repository file tree recursively
49
+ */
50
+ async fetchFileTree(branch?: string): Promise<FileTreeNode[]> {
51
+ const targetBranch = branch || this.branch;
52
+ const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/git/trees/${targetBranch}?recursive=1`;
53
+ const res = await fetch(url, { headers: this.getHeaders() });
54
+
55
+ if (!res.ok) {
56
+ const errBody = await res.text();
57
+ throw new Error(`GitHub API error ${res.status}: ${errBody}`);
58
+ }
59
+
60
+ const data = (await res.json()) as any;
61
+ const tree = data.tree || [];
62
+ return tree.map((item: any) => ({
63
+ path: item.path,
64
+ type: item.type === "blob" ? "blob" : item.type === "tree" ? "tree" : item.type,
65
+ sha: item.sha,
66
+ size: item.size,
67
+ }));
68
+ }
69
+
70
+ /**
71
+ * Downloads a file's raw content by its blob SHA
72
+ */
73
+ async downloadBlob(sha: string): Promise<string> {
74
+ const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/git/blobs/${sha}`;
75
+ const headers = {
76
+ ...this.getHeaders(),
77
+ Accept: "application/vnd.github.v3.raw",
78
+ };
79
+ const res = await fetch(url, { headers });
80
+
81
+ if (!res.ok) {
82
+ throw new Error(`Failed to download blob ${sha}: status ${res.status}`);
83
+ }
84
+
85
+ return res.text();
86
+ }
87
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ // packages/git-github/src/index.ts
2
+ /**
3
+ * @quatrain/git-github - Main Entrypoint
4
+ * @license AGPL-3.0-only
5
+ */
6
+
7
+ export * from "./GithubAdapter";
8
+ export * from "./GithubHttpClient";