* feat: integrate GitHub OAuth device flow across runtimes Add GitHub OAuth device flow endpoints across runtimes Introduce GitHubSettings UI panel and sidebar entry Persist GitHub auth state in per-runtime storage * feat: add GitHub PR status and PR description generation Show PR status for the current branch in the Git view Generate a pull request description from the diff between base and head Expose prStatus, prCreate, and prMerge APIs in web and desktop clients * feat: add GitHub PR ready for review Add API to mark pull requests as ready for review Show a Ready button for draft PRs and reflect status in UI Handle token expiration and GraphQL errors when marking ready
51 lines
1.5 KiB
JavaScript
51 lines
1.5 KiB
JavaScript
const DEVICE_CODE_URL = 'https://github.com/login/device/code';
|
|
const ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token';
|
|
const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
|
|
|
|
const encodeForm = (params) => {
|
|
const body = new URLSearchParams();
|
|
for (const [key, value] of Object.entries(params)) {
|
|
if (value == null) continue;
|
|
body.set(key, String(value));
|
|
}
|
|
return body.toString();
|
|
};
|
|
|
|
async function postForm(url, params) {
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
Accept: 'application/json',
|
|
},
|
|
body: encodeForm(params),
|
|
});
|
|
|
|
const payload = await response.json().catch(() => null);
|
|
if (!response.ok) {
|
|
const message = payload?.error_description || payload?.error || response.statusText;
|
|
const error = new Error(message || 'GitHub request failed');
|
|
error.status = response.status;
|
|
error.payload = payload;
|
|
throw error;
|
|
}
|
|
return payload;
|
|
}
|
|
|
|
export async function startDeviceFlow({ clientId, scope }) {
|
|
return postForm(DEVICE_CODE_URL, {
|
|
client_id: clientId,
|
|
scope,
|
|
});
|
|
}
|
|
|
|
export async function exchangeDeviceCode({ clientId, deviceCode }) {
|
|
// GitHub returns 200 with {error: 'authorization_pending'|...} for non-success states.
|
|
const payload = await postForm(ACCESS_TOKEN_URL, {
|
|
client_id: clientId,
|
|
device_code: deviceCode,
|
|
grant_type: DEVICE_GRANT_TYPE,
|
|
});
|
|
return payload;
|
|
}
|