@almadar/integrations 2.0.1 → 2.0.3

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,756 @@
1
+ import { integratorsRegistry } from '@almadar/patterns';
2
+ import { spawn } from 'child_process';
3
+ import { promises } from 'fs';
4
+ import { join } from 'path';
5
+
6
+ // src/core/logger.ts
7
+ var ConsoleLogger = class {
8
+ constructor(level = "info") {
9
+ this.level = level;
10
+ }
11
+ debug(message, meta) {
12
+ if (this.shouldLog("debug")) {
13
+ console.debug(`[DEBUG] ${message}`, meta || "");
14
+ }
15
+ }
16
+ info(message, meta) {
17
+ if (this.shouldLog("info")) {
18
+ console.log(`[INFO] ${message}`, meta || "");
19
+ }
20
+ }
21
+ warn(message, meta) {
22
+ if (this.shouldLog("warn")) {
23
+ console.warn(`[WARN] ${message}`, meta || "");
24
+ }
25
+ }
26
+ error(message, meta) {
27
+ if (this.shouldLog("error")) {
28
+ console.error(`[ERROR] ${message}`, meta || "");
29
+ }
30
+ }
31
+ shouldLog(level) {
32
+ const levels = ["debug", "info", "warn", "error"];
33
+ return levels.indexOf(level) >= levels.indexOf(this.level);
34
+ }
35
+ };
36
+ function validateParams(integration, action, params) {
37
+ const registry = integratorsRegistry.integrators[integration];
38
+ if (!registry) {
39
+ return {
40
+ valid: false,
41
+ errors: [
42
+ {
43
+ param: "integration",
44
+ message: `Unknown integration: ${integration}`
45
+ }
46
+ ]
47
+ };
48
+ }
49
+ const actionDef = registry.actions.find((a) => a.name === action);
50
+ if (!actionDef) {
51
+ return {
52
+ valid: false,
53
+ errors: [{ param: "action", message: `Unknown action: ${action}` }]
54
+ };
55
+ }
56
+ const errors = [];
57
+ for (const paramDef of actionDef.params) {
58
+ if (paramDef.required && !(paramDef.name in params)) {
59
+ errors.push({
60
+ param: paramDef.name,
61
+ message: `Missing required parameter: ${paramDef.name}`
62
+ });
63
+ }
64
+ if (paramDef.name in params) {
65
+ const value = params[paramDef.name];
66
+ const expectedType = paramDef.type;
67
+ const actualType = typeof value;
68
+ if (expectedType === "number" && actualType !== "number") {
69
+ errors.push({
70
+ param: paramDef.name,
71
+ message: `Expected ${expectedType}, got ${actualType}`
72
+ });
73
+ }
74
+ if (expectedType === "string" && actualType !== "string") {
75
+ errors.push({
76
+ param: paramDef.name,
77
+ message: `Expected ${expectedType}, got ${actualType}`
78
+ });
79
+ }
80
+ if (expectedType === "array" && !Array.isArray(value)) {
81
+ errors.push({
82
+ param: paramDef.name,
83
+ message: `Expected array, got ${actualType}`
84
+ });
85
+ }
86
+ if (expectedType === "object" && (actualType !== "object" || Array.isArray(value) || value === null)) {
87
+ errors.push({
88
+ param: paramDef.name,
89
+ message: `Expected object, got ${actualType}`
90
+ });
91
+ }
92
+ }
93
+ }
94
+ return {
95
+ valid: errors.length === 0,
96
+ errors
97
+ };
98
+ }
99
+
100
+ // src/core/retry.ts
101
+ async function withRetry(fn, config) {
102
+ const {
103
+ maxAttempts,
104
+ backoffMs,
105
+ maxBackoffMs = 3e4,
106
+ retryableErrors
107
+ } = config;
108
+ let lastError;
109
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
110
+ try {
111
+ return await fn();
112
+ } catch (error) {
113
+ lastError = error;
114
+ if (error && typeof error === "object" && "code" in error && retryableErrors) {
115
+ const integrationError = error;
116
+ if (!retryableErrors.includes(integrationError.code)) {
117
+ throw error;
118
+ }
119
+ }
120
+ if (attempt === maxAttempts) {
121
+ throw error;
122
+ }
123
+ const delay = Math.min(
124
+ backoffMs * Math.pow(2, attempt - 1),
125
+ maxBackoffMs
126
+ );
127
+ await new Promise((resolve) => setTimeout(resolve, delay));
128
+ }
129
+ }
130
+ throw lastError;
131
+ }
132
+
133
+ // src/core/BaseIntegration.ts
134
+ var BaseIntegration = class {
135
+ constructor(config) {
136
+ this.config = config;
137
+ this.logger = config.logger || new ConsoleLogger();
138
+ }
139
+ /**
140
+ * Validate action params against registry
141
+ */
142
+ validateParams(action, params) {
143
+ return validateParams(this.config.name, action, params);
144
+ }
145
+ /**
146
+ * Handle errors uniformly
147
+ */
148
+ handleError(action, error) {
149
+ this.logger.error(`Integration error in ${this.config.name}.${action}`, {
150
+ error
151
+ });
152
+ const integrationError = error instanceof Error ? error : new Error(String(error));
153
+ return {
154
+ success: false,
155
+ error: integrationError,
156
+ metadata: this.createMetadata(action, 0, 0)
157
+ };
158
+ }
159
+ /**
160
+ * Create metadata for result
161
+ */
162
+ createMetadata(action, duration, retries = 0) {
163
+ return {
164
+ integration: this.config.name,
165
+ action,
166
+ duration,
167
+ retries,
168
+ timestamp: Date.now()
169
+ };
170
+ }
171
+ /**
172
+ * Execute with retry logic
173
+ */
174
+ async executeWithRetry(fn) {
175
+ if (!this.config.retry) {
176
+ return fn();
177
+ }
178
+ return withRetry(fn, {
179
+ maxAttempts: this.config.retry.maxAttempts,
180
+ backoffMs: this.config.retry.backoffMs,
181
+ maxBackoffMs: this.config.retry.maxBackoffMs,
182
+ retryableErrors: [
183
+ "TIMEOUT_ERROR",
184
+ "NETWORK_ERROR",
185
+ "RATE_LIMIT_ERROR"
186
+ ]
187
+ });
188
+ }
189
+ };
190
+
191
+ // src/types.ts
192
+ var IntegrationError = class extends Error {
193
+ constructor(message, code = "UNKNOWN_ERROR", details) {
194
+ super(message);
195
+ this.name = "IntegrationError";
196
+ this.code = code;
197
+ this.details = details;
198
+ }
199
+ toJSON() {
200
+ return {
201
+ name: this.name,
202
+ message: this.message,
203
+ code: this.code,
204
+ integration: this.integration,
205
+ action: this.action,
206
+ details: this.details
207
+ };
208
+ }
209
+ };
210
+
211
+ // src/integrations/github/github-git.ts
212
+ async function execGit(args, cwd, env) {
213
+ return new Promise((resolve, reject) => {
214
+ const proc = spawn("git", args, {
215
+ cwd,
216
+ env: { ...process.env, ...env },
217
+ stdio: "pipe"
218
+ });
219
+ let stdout = "";
220
+ let stderr = "";
221
+ proc.stdout?.on("data", (data) => {
222
+ stdout += data.toString();
223
+ });
224
+ proc.stderr?.on("data", (data) => {
225
+ stderr += data.toString();
226
+ });
227
+ proc.on("close", (code) => {
228
+ if (code === 0) {
229
+ resolve({ stdout, stderr });
230
+ } else {
231
+ reject(
232
+ new IntegrationError(
233
+ `Git command failed: ${args.join(" ")}
234
+ ${stderr}`,
235
+ "SERVICE_ERROR",
236
+ { code, stderr }
237
+ )
238
+ );
239
+ }
240
+ });
241
+ proc.on("error", (error) => {
242
+ reject(
243
+ new IntegrationError(
244
+ `Failed to execute git: ${error.message}`,
245
+ "SERVICE_ERROR",
246
+ { error }
247
+ )
248
+ );
249
+ });
250
+ });
251
+ }
252
+ async function cloneRepo(params, token) {
253
+ const { repoUrl, targetDir, branch, depth = 1 } = params;
254
+ const authUrl = injectTokenIntoUrl(repoUrl, token);
255
+ const args = ["clone"];
256
+ if (depth > 0) {
257
+ args.push("--depth", depth.toString());
258
+ }
259
+ if (branch) {
260
+ args.push("--branch", branch);
261
+ }
262
+ args.push(authUrl, targetDir);
263
+ try {
264
+ await execGit(args, process.cwd());
265
+ await scrubTokenFromRemote(targetDir, repoUrl);
266
+ } catch (error) {
267
+ throw new IntegrationError(
268
+ `Failed to clone repository: ${error instanceof Error ? error.message : String(error)}`,
269
+ "SERVICE_ERROR",
270
+ { repoUrl, error }
271
+ );
272
+ }
273
+ }
274
+ async function createBranch(params, workDir) {
275
+ const { branchName, baseBranch } = params;
276
+ const cwd = params.workDir || workDir;
277
+ try {
278
+ if (baseBranch) {
279
+ await execGit(["checkout", baseBranch], cwd);
280
+ }
281
+ await execGit(["checkout", "-b", branchName], cwd);
282
+ } catch (error) {
283
+ throw new IntegrationError(
284
+ `Failed to create branch: ${error instanceof Error ? error.message : String(error)}`,
285
+ "SERVICE_ERROR",
286
+ { branchName, baseBranch, error }
287
+ );
288
+ }
289
+ }
290
+ async function commit(params, workDir) {
291
+ const { message, files } = params;
292
+ const cwd = params.workDir || workDir;
293
+ try {
294
+ if (files && files.length > 0) {
295
+ await execGit(["add", ...files], cwd);
296
+ } else {
297
+ await execGit(["add", "."], cwd);
298
+ }
299
+ await execGit(["commit", "-m", message], cwd);
300
+ } catch (error) {
301
+ throw new IntegrationError(
302
+ `Failed to commit: ${error instanceof Error ? error.message : String(error)}`,
303
+ "SERVICE_ERROR",
304
+ { message, error }
305
+ );
306
+ }
307
+ }
308
+ async function push(params, workDir, token) {
309
+ const { branchName, force = false } = params;
310
+ const cwd = params.workDir || workDir;
311
+ const protectedBranches = ["main", "master", "production", "prod"];
312
+ if (force && protectedBranches.includes(branchName.toLowerCase())) {
313
+ throw new IntegrationError(
314
+ `Force push to protected branch '${branchName}' is not allowed`,
315
+ "VALIDATION_ERROR"
316
+ );
317
+ }
318
+ try {
319
+ const credHelper = await createTempCredentialHelper(token);
320
+ const args = ["push"];
321
+ if (force) {
322
+ args.push("--force");
323
+ }
324
+ args.push("-u", "origin", branchName);
325
+ await execGit(args, cwd, {
326
+ GIT_ASKPASS: credHelper,
327
+ GIT_TERMINAL_PROMPT: "0"
328
+ });
329
+ await promises.unlink(credHelper);
330
+ } catch (error) {
331
+ throw new IntegrationError(
332
+ `Failed to push: ${error instanceof Error ? error.message : String(error)}`,
333
+ "SERVICE_ERROR",
334
+ { branchName, error }
335
+ );
336
+ }
337
+ }
338
+ function injectTokenIntoUrl(repoUrl, token) {
339
+ const url = new URL(repoUrl);
340
+ url.username = "x-access-token";
341
+ url.password = token;
342
+ return url.toString();
343
+ }
344
+ async function scrubTokenFromRemote(workDir, originalUrl) {
345
+ try {
346
+ const url = new URL(originalUrl);
347
+ url.username = "";
348
+ url.password = "";
349
+ const cleanUrl = url.toString();
350
+ await execGit(["remote", "set-url", "origin", cleanUrl], workDir);
351
+ } catch (error) {
352
+ console.error("Warning: Failed to scrub token from remote URL:", error);
353
+ }
354
+ }
355
+ async function createTempCredentialHelper(token) {
356
+ const tmpDir = process.env.TMPDIR || "/tmp";
357
+ const helperPath = join(tmpDir, `git-cred-${Date.now()}.sh`);
358
+ const script = `#!/bin/sh
359
+ echo "${token}"`;
360
+ await promises.writeFile(helperPath, script, { mode: 448 });
361
+ return helperPath;
362
+ }
363
+ async function getCurrentBranch(workDir) {
364
+ try {
365
+ const { stdout } = await execGit(["branch", "--show-current"], workDir);
366
+ return stdout.trim();
367
+ } catch (error) {
368
+ throw new IntegrationError(
369
+ `Failed to get current branch: ${error instanceof Error ? error.message : String(error)}`,
370
+ "SERVICE_ERROR",
371
+ { error }
372
+ );
373
+ }
374
+ }
375
+ async function hasUncommittedChanges(workDir) {
376
+ try {
377
+ const { stdout } = await execGit(["status", "--porcelain"], workDir);
378
+ return stdout.trim().length > 0;
379
+ } catch (error) {
380
+ throw new IntegrationError(
381
+ `Failed to check git status: ${error instanceof Error ? error.message : String(error)}`,
382
+ "SERVICE_ERROR",
383
+ { error }
384
+ );
385
+ }
386
+ }
387
+
388
+ // src/integrations/github/github-api.ts
389
+ async function githubFetch(endpoint, config, options = {}) {
390
+ const url = `https://api.github.com${endpoint}`;
391
+ const headers = {
392
+ "Authorization": `Bearer ${config.token}`,
393
+ "Accept": "application/vnd.github+json",
394
+ "X-GitHub-Api-Version": "2022-11-28",
395
+ ...options.headers
396
+ };
397
+ try {
398
+ const response = await fetch(url, {
399
+ ...options,
400
+ headers
401
+ });
402
+ const rateLimit = {
403
+ remaining: parseInt(response.headers.get("x-ratelimit-remaining") || "0", 10),
404
+ limit: parseInt(response.headers.get("x-ratelimit-limit") || "5000", 10),
405
+ reset: parseInt(response.headers.get("x-ratelimit-reset") || "0", 10)
406
+ };
407
+ if (!response.ok) {
408
+ const error = await response.json().catch(() => ({ message: response.statusText }));
409
+ if (response.status === 429) {
410
+ throw new IntegrationError(
411
+ "GitHub API rate limit exceeded",
412
+ "RATE_LIMIT_ERROR",
413
+ { rateLimit, error }
414
+ );
415
+ }
416
+ if (response.status === 401 || response.status === 403) {
417
+ throw new IntegrationError(
418
+ `GitHub API authentication failed: ${error.message || response.statusText}`,
419
+ "AUTH_ERROR",
420
+ { status: response.status, error }
421
+ );
422
+ }
423
+ throw new IntegrationError(
424
+ `GitHub API request failed: ${error.message || response.statusText}`,
425
+ "SERVICE_ERROR",
426
+ { status: response.status, error }
427
+ );
428
+ }
429
+ const data = await response.json();
430
+ return { data, rateLimit };
431
+ } catch (error) {
432
+ if (error instanceof IntegrationError) {
433
+ throw error;
434
+ }
435
+ throw new IntegrationError(
436
+ `GitHub API request failed: ${error instanceof Error ? error.message : String(error)}`,
437
+ "NETWORK_ERROR",
438
+ { error }
439
+ );
440
+ }
441
+ }
442
+ async function createPR(params, config) {
443
+ const { title, body, baseBranch, headBranch, draft = false } = params;
444
+ const endpoint = `/repos/${config.owner}/${config.repo}/pulls`;
445
+ const { data } = await githubFetch(endpoint, config, {
446
+ method: "POST",
447
+ body: JSON.stringify({
448
+ title,
449
+ body,
450
+ base: baseBranch,
451
+ head: headBranch,
452
+ draft
453
+ })
454
+ });
455
+ return data;
456
+ }
457
+ async function getPRComments(params, config) {
458
+ const { prNumber } = params;
459
+ const endpoint = `/repos/${config.owner}/${config.repo}/pulls/${prNumber}/comments`;
460
+ const { data } = await githubFetch(endpoint, config);
461
+ return data;
462
+ }
463
+ async function listIssues(params, config) {
464
+ const { state = "open", labels = [], limit = 30 } = params;
465
+ const queryParams = new URLSearchParams({
466
+ state,
467
+ per_page: Math.min(limit, 100).toString()
468
+ });
469
+ if (labels.length > 0) {
470
+ queryParams.append("labels", labels.join(","));
471
+ }
472
+ const endpoint = `/repos/${config.owner}/${config.repo}/issues?${queryParams}`;
473
+ const { data } = await githubFetch(endpoint, config);
474
+ return data;
475
+ }
476
+ async function getIssue(params, config) {
477
+ const { issueNumber } = params;
478
+ const issueEndpoint = `/repos/${config.owner}/${config.repo}/issues/${issueNumber}`;
479
+ const { data: issue } = await githubFetch(issueEndpoint, config);
480
+ const commentsEndpoint = `/repos/${config.owner}/${config.repo}/issues/${issueNumber}/comments`;
481
+ const { data: comments } = await githubFetch(commentsEndpoint, config);
482
+ return { issue, comments };
483
+ }
484
+ async function getRateLimit(config) {
485
+ const endpoint = "/rate_limit";
486
+ const { data } = await githubFetch(endpoint, config);
487
+ return data.rate;
488
+ }
489
+ async function listCommits(config, options) {
490
+ const params = new URLSearchParams();
491
+ if (options?.path) params.set("path", options.path);
492
+ if (options?.perPage) params.set("per_page", options.perPage.toString());
493
+ if (options?.page) params.set("page", options.page.toString());
494
+ const queryStr = params.toString();
495
+ const endpoint = `/repos/${config.owner}/${config.repo}/commits${queryStr ? `?${queryStr}` : ""}`;
496
+ const { data } = await githubFetch(endpoint, config);
497
+ return data.map((item) => ({
498
+ sha: item.sha,
499
+ message: item.commit.message,
500
+ author: item.commit.author,
501
+ stats: item.stats
502
+ }));
503
+ }
504
+ async function getCommitDiff(config, sha) {
505
+ const url = `https://api.github.com/repos/${config.owner}/${config.repo}/commits/${sha}`;
506
+ const response = await fetch(url, {
507
+ headers: {
508
+ Authorization: `Bearer ${config.token}`,
509
+ Accept: "application/vnd.github.diff",
510
+ "X-GitHub-Api-Version": "2022-11-28"
511
+ }
512
+ });
513
+ if (!response.ok) {
514
+ throw new IntegrationError(
515
+ `GitHub API request failed: ${response.statusText}`,
516
+ "SERVICE_ERROR",
517
+ { status: response.status }
518
+ );
519
+ }
520
+ return response.text();
521
+ }
522
+ async function getFileAtCommit(config, path, sha) {
523
+ const endpoint = `/repos/${config.owner}/${config.repo}/contents/${path}?ref=${sha}`;
524
+ try {
525
+ const response = await fetch(`https://api.github.com${endpoint}`, {
526
+ headers: {
527
+ Authorization: `Bearer ${config.token}`,
528
+ Accept: "application/vnd.github.raw+json",
529
+ "X-GitHub-Api-Version": "2022-11-28"
530
+ }
531
+ });
532
+ if (!response.ok) {
533
+ if (response.status === 404) return null;
534
+ throw new Error(`${response.status}: ${response.statusText}`);
535
+ }
536
+ return response.text();
537
+ } catch (error) {
538
+ if (error instanceof IntegrationError) throw error;
539
+ throw new IntegrationError(
540
+ `Failed to get file at commit: ${error instanceof Error ? error.message : String(error)}`,
541
+ "SERVICE_ERROR",
542
+ { path, sha, error }
543
+ );
544
+ }
545
+ }
546
+ function parseRepoUrl(repoUrl) {
547
+ try {
548
+ const url = new URL(repoUrl);
549
+ const pathParts = url.pathname.split("/").filter(Boolean);
550
+ if (pathParts.length < 2) {
551
+ throw new Error("Invalid repository URL format");
552
+ }
553
+ const owner = pathParts[0];
554
+ const repo = pathParts[1].replace(/\.git$/, "");
555
+ return { owner, repo };
556
+ } catch (error) {
557
+ throw new IntegrationError(
558
+ `Failed to parse repository URL: ${repoUrl}`,
559
+ "VALIDATION_ERROR",
560
+ { error }
561
+ );
562
+ }
563
+ }
564
+
565
+ // src/integrations/github/index.ts
566
+ var GitHubIntegration = class extends BaseIntegration {
567
+ constructor(config) {
568
+ super(config);
569
+ this.token = config.env.GITHUB_TOKEN;
570
+ if (!this.token) {
571
+ throw new Error("GITHUB_TOKEN not configured");
572
+ }
573
+ this.owner = config.env.GITHUB_OWNER || "";
574
+ this.repo = config.env.GITHUB_REPO || "";
575
+ this.workDir = config.env.GITHUB_WORK_DIR || process.cwd();
576
+ this.logger.info("GitHub integration initialized", {
577
+ owner: this.owner,
578
+ repo: this.repo
579
+ });
580
+ }
581
+ /**
582
+ * Execute a GitHub action
583
+ */
584
+ async execute(action, params) {
585
+ const validation = this.validateParams(action, params);
586
+ if (!validation.valid) {
587
+ return {
588
+ success: false,
589
+ error: {
590
+ name: "IntegrationError",
591
+ message: "Validation failed",
592
+ code: "VALIDATION_ERROR",
593
+ details: validation.errors
594
+ },
595
+ metadata: this.createMetadata(action, 0)
596
+ };
597
+ }
598
+ const startTime = Date.now();
599
+ let retries = 0;
600
+ try {
601
+ let data;
602
+ switch (action) {
603
+ case "cloneRepo":
604
+ data = await this.executeWithRetry(
605
+ () => this.cloneRepo(params)
606
+ );
607
+ break;
608
+ case "createBranch":
609
+ data = await this.executeWithRetry(
610
+ () => this.createBranch(params)
611
+ );
612
+ break;
613
+ case "commit":
614
+ data = await this.executeWithRetry(
615
+ () => this.commit(params)
616
+ );
617
+ break;
618
+ case "push":
619
+ data = await this.executeWithRetry(
620
+ () => this.push(params)
621
+ );
622
+ break;
623
+ case "createPR":
624
+ data = await this.executeWithRetry(
625
+ () => this.createPR(params)
626
+ );
627
+ break;
628
+ case "getPRComments":
629
+ data = await this.executeWithRetry(
630
+ () => this.getPRComments(params)
631
+ );
632
+ break;
633
+ case "listIssues":
634
+ data = await this.executeWithRetry(
635
+ () => this.listIssues(params)
636
+ );
637
+ break;
638
+ case "getIssue":
639
+ data = await this.executeWithRetry(
640
+ () => this.getIssue(params)
641
+ );
642
+ break;
643
+ default:
644
+ throw new Error(`Unknown GitHub action: ${action}`);
645
+ }
646
+ return {
647
+ success: true,
648
+ data,
649
+ metadata: this.createMetadata(action, Date.now() - startTime, retries)
650
+ };
651
+ } catch (error) {
652
+ return this.handleError(action, error);
653
+ }
654
+ }
655
+ /**
656
+ * Clone a repository
657
+ */
658
+ async cloneRepo(params) {
659
+ this.logger.debug("Cloning repository", { repoUrl: params.repoUrl });
660
+ if (!this.owner || !this.repo) {
661
+ const parsed = parseRepoUrl(params.repoUrl);
662
+ this.owner = parsed.owner;
663
+ this.repo = parsed.repo;
664
+ }
665
+ await cloneRepo(params, this.token);
666
+ return {
667
+ message: `Successfully cloned ${params.repoUrl} to ${params.targetDir}`
668
+ };
669
+ }
670
+ /**
671
+ * Create a branch
672
+ */
673
+ async createBranch(params) {
674
+ this.logger.debug("Creating branch", { branchName: params.branchName });
675
+ await createBranch(params, this.workDir);
676
+ return {
677
+ message: `Successfully created branch: ${params.branchName}`
678
+ };
679
+ }
680
+ /**
681
+ * Commit changes
682
+ */
683
+ async commit(params) {
684
+ this.logger.debug("Committing changes", { message: params.message });
685
+ await commit(params, this.workDir);
686
+ return {
687
+ message: `Successfully committed changes: ${params.message}`
688
+ };
689
+ }
690
+ /**
691
+ * Push branch
692
+ */
693
+ async push(params) {
694
+ this.logger.debug("Pushing branch", { branchName: params.branchName });
695
+ await push(params, this.workDir, this.token);
696
+ return {
697
+ message: `Successfully pushed branch: ${params.branchName}`
698
+ };
699
+ }
700
+ /**
701
+ * Create a pull request
702
+ */
703
+ async createPR(params) {
704
+ this.logger.debug("Creating pull request", { title: params.title });
705
+ const apiConfig = this.getAPIConfig();
706
+ const pr = await createPR(params, apiConfig);
707
+ this.logger.info("Pull request created", { number: pr.number, url: pr.url });
708
+ return pr;
709
+ }
710
+ /**
711
+ * Get PR comments
712
+ */
713
+ async getPRComments(params) {
714
+ this.logger.debug("Getting PR comments", { prNumber: params.prNumber });
715
+ const apiConfig = this.getAPIConfig();
716
+ const comments = await getPRComments(params, apiConfig);
717
+ return { comments };
718
+ }
719
+ /**
720
+ * List issues
721
+ */
722
+ async listIssues(params) {
723
+ this.logger.debug("Listing issues", params);
724
+ const apiConfig = this.getAPIConfig();
725
+ const issues = await listIssues(params, apiConfig);
726
+ return { issues };
727
+ }
728
+ /**
729
+ * Get issue details
730
+ */
731
+ async getIssue(params) {
732
+ this.logger.debug("Getting issue", { issueNumber: params.issueNumber });
733
+ const apiConfig = this.getAPIConfig();
734
+ const result = await getIssue(params, apiConfig);
735
+ return result;
736
+ }
737
+ /**
738
+ * Get API config for GitHub API calls
739
+ */
740
+ getAPIConfig() {
741
+ if (!this.owner || !this.repo) {
742
+ throw new Error(
743
+ "GitHub owner and repo must be configured. Either set GITHUB_OWNER/GITHUB_REPO or clone a repository first."
744
+ );
745
+ }
746
+ return {
747
+ token: this.token,
748
+ owner: this.owner,
749
+ repo: this.repo
750
+ };
751
+ }
752
+ };
753
+
754
+ export { GitHubIntegration, cloneRepo, commit, createBranch, createPR, getCommitDiff, getCurrentBranch, getFileAtCommit, getIssue, getPRComments, getRateLimit, hasUncommittedChanges, listCommits, listIssues, parseRepoUrl, push };
755
+ //# sourceMappingURL=index.js.map
756
+ //# sourceMappingURL=index.js.map