( token: string, owner: string, repo: string, title: string, body: string, head: string, base: string )
| 427 | } |
| 428 | |
| 429 | export async function createPullRequest( |
| 430 | token: string, |
| 431 | owner: string, |
| 432 | repo: string, |
| 433 | title: string, |
| 434 | body: string, |
| 435 | head: string, |
| 436 | base: string |
| 437 | ) { |
| 438 | const res = await ghFetch(`/repos/${owner}/${repo}/pulls`, token, { |
| 439 | method: 'POST', |
| 440 | body: JSON.stringify({ title, body, head, base }), |
| 441 | headers: { 'Content-Type': 'application/json' }, |
| 442 | }); |
| 443 | if (!res.ok) { |
| 444 | const err = await res.json().catch(() => ({ message: res.statusText })); |
| 445 | if (res.status === 403) { |
| 446 | if (err.message?.includes('Resource not accessible')) |
| 447 | throw new Error(`PERMISSION_DENIED: ${err.message}`); |
| 448 | if (err.message?.includes('rate limit') || err.message?.includes('abuse')) |
| 449 | throw new Error(`RATE_LIMITED: ${err.message}`); |
| 450 | } |
| 451 | if (res.status === 401) |
| 452 | throw new Error(`PERMISSION_DENIED: Token expired or invalid (401 Unauthorized)`); |
| 453 | if (res.status === 422) { |
| 454 | throw new Error(`PR_VALIDATION: ${err.message || 'Validation error. The branch may have no changes relative to base.'}`); |
| 455 | } |
| 456 | throw new Error(err.message || `HTTP ${res.status}`); |
| 457 | } |
| 458 | return res.json(); |
| 459 | } |
| 460 | |
| 461 | export async function requestPRReview( |
| 462 | token: string, |
no test coverage detected