@fractary/faber-cli 1.3.2 → 1.3.4
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/README.md +101 -4
- package/dist/commands/auth/index.d.ts +13 -0
- package/dist/commands/auth/index.d.ts.map +1 -0
- package/dist/commands/auth/index.js +336 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/lib/github-app-auth.d.ts +122 -0
- package/dist/lib/github-app-auth.d.ts.map +1 -0
- package/dist/lib/github-app-auth.js +294 -0
- package/dist/lib/github-app-setup.d.ts +142 -0
- package/dist/lib/github-app-setup.d.ts.map +1 -0
- package/dist/lib/github-app-setup.js +365 -0
- package/dist/lib/repo-client.d.ts +22 -2
- package/dist/lib/repo-client.d.ts.map +1 -1
- package/dist/lib/repo-client.js +52 -8
- package/dist/lib/sdk-config-adapter.d.ts +52 -0
- package/dist/lib/sdk-config-adapter.d.ts.map +1 -1
- package/dist/lib/sdk-config-adapter.js +167 -6
- package/dist/types/config.d.ts +12 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/github-manifest.d.ts +39 -0
- package/dist/utils/github-manifest.d.ts.map +1 -0
- package/dist/utils/github-manifest.js +84 -0
- package/package.json +4 -2
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub App Authentication Module
|
|
3
|
+
*
|
|
4
|
+
* Provides JWT generation, installation token exchange, and token caching
|
|
5
|
+
* for GitHub App authentication in FABER CLI.
|
|
6
|
+
*/
|
|
7
|
+
import jwt from 'jsonwebtoken';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
/**
|
|
12
|
+
* Private Key Loader
|
|
13
|
+
*
|
|
14
|
+
* Loads private keys from file path or environment variable
|
|
15
|
+
*/
|
|
16
|
+
export class PrivateKeyLoader {
|
|
17
|
+
/**
|
|
18
|
+
* Load private key from configured sources.
|
|
19
|
+
* Priority: env var > file path
|
|
20
|
+
*
|
|
21
|
+
* @param config - GitHub App configuration
|
|
22
|
+
* @returns The private key content
|
|
23
|
+
* @throws Error if private key cannot be loaded
|
|
24
|
+
*/
|
|
25
|
+
static async load(config) {
|
|
26
|
+
// Try environment variable first (priority)
|
|
27
|
+
if (config.private_key_env_var) {
|
|
28
|
+
const envValue = process.env[config.private_key_env_var];
|
|
29
|
+
if (envValue) {
|
|
30
|
+
try {
|
|
31
|
+
// Decode base64-encoded key
|
|
32
|
+
const decoded = Buffer.from(envValue, 'base64').toString('utf-8');
|
|
33
|
+
if (PrivateKeyLoader.validate(decoded)) {
|
|
34
|
+
return decoded;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Invalid base64, fall through to file path
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Try file path
|
|
43
|
+
if (config.private_key_path) {
|
|
44
|
+
try {
|
|
45
|
+
// Expand ~ to home directory
|
|
46
|
+
const expandedPath = config.private_key_path.startsWith('~')
|
|
47
|
+
? config.private_key_path.replace('~', os.homedir())
|
|
48
|
+
: config.private_key_path;
|
|
49
|
+
const resolvedPath = path.resolve(expandedPath);
|
|
50
|
+
const key = await fs.readFile(resolvedPath, 'utf-8');
|
|
51
|
+
if (PrivateKeyLoader.validate(key)) {
|
|
52
|
+
return key;
|
|
53
|
+
}
|
|
54
|
+
throw new Error('Invalid private key format. Expected PEM-encoded RSA private key');
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error instanceof Error && error.message.includes('ENOENT')) {
|
|
58
|
+
throw new Error(`GitHub App private key not found at '${config.private_key_path}'. ` +
|
|
59
|
+
`Check 'private_key_path' in config or set ${config.private_key_env_var || 'GITHUB_APP_PRIVATE_KEY'} env var`);
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
throw new Error('GitHub App private key not found. ' +
|
|
65
|
+
"Configure 'private_key_path' in .fractary/settings.json or set GITHUB_APP_PRIVATE_KEY env var");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate private key format.
|
|
69
|
+
*
|
|
70
|
+
* @param key - The private key content
|
|
71
|
+
* @returns true if valid PEM format
|
|
72
|
+
*/
|
|
73
|
+
static validate(key) {
|
|
74
|
+
// Check for PEM format (RSA or PKCS#8)
|
|
75
|
+
const trimmed = key.trim();
|
|
76
|
+
return ((trimmed.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
|
|
77
|
+
trimmed.endsWith('-----END RSA PRIVATE KEY-----')) ||
|
|
78
|
+
(trimmed.startsWith('-----BEGIN PRIVATE KEY-----') &&
|
|
79
|
+
trimmed.endsWith('-----END PRIVATE KEY-----')));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* GitHub App Authentication
|
|
84
|
+
*
|
|
85
|
+
* Handles JWT generation, installation token exchange, and caching
|
|
86
|
+
*/
|
|
87
|
+
export class GitHubAppAuth {
|
|
88
|
+
constructor(config) {
|
|
89
|
+
this.cache = new Map();
|
|
90
|
+
this.refreshPromise = null;
|
|
91
|
+
this.config = config;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get a valid installation token.
|
|
95
|
+
* Returns cached token if still valid, otherwise generates new one.
|
|
96
|
+
*
|
|
97
|
+
* @returns Installation access token
|
|
98
|
+
*/
|
|
99
|
+
async getToken() {
|
|
100
|
+
const cacheKey = this.config.installation_id;
|
|
101
|
+
const cached = this.cache.get(cacheKey);
|
|
102
|
+
if (cached && !this.isExpired(cached) && !this.isExpiringSoon(cached)) {
|
|
103
|
+
return cached.token;
|
|
104
|
+
}
|
|
105
|
+
// If token is expiring soon but still valid, trigger background refresh
|
|
106
|
+
if (cached && !this.isExpired(cached) && this.isExpiringSoon(cached)) {
|
|
107
|
+
this.triggerBackgroundRefresh();
|
|
108
|
+
return cached.token;
|
|
109
|
+
}
|
|
110
|
+
// Token expired or missing, must refresh synchronously
|
|
111
|
+
return this.refreshToken();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Force refresh the token.
|
|
115
|
+
*
|
|
116
|
+
* @returns New installation access token
|
|
117
|
+
*/
|
|
118
|
+
async refreshToken() {
|
|
119
|
+
// Deduplicate concurrent refresh requests
|
|
120
|
+
if (this.refreshPromise) {
|
|
121
|
+
return this.refreshPromise;
|
|
122
|
+
}
|
|
123
|
+
this.refreshPromise = this.doRefresh();
|
|
124
|
+
try {
|
|
125
|
+
return await this.refreshPromise;
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
this.refreshPromise = null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if token needs refresh (within 5 minutes of expiration).
|
|
133
|
+
*
|
|
134
|
+
* @returns true if token should be refreshed
|
|
135
|
+
*/
|
|
136
|
+
isTokenExpiringSoon() {
|
|
137
|
+
const cached = this.cache.get(this.config.installation_id);
|
|
138
|
+
return cached ? this.isExpiringSoon(cached) : true;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Validate the configuration and private key.
|
|
142
|
+
*
|
|
143
|
+
* @throws Error if configuration is invalid
|
|
144
|
+
*/
|
|
145
|
+
async validate() {
|
|
146
|
+
// Validate required fields
|
|
147
|
+
if (!this.config.id) {
|
|
148
|
+
throw new Error("GitHub App ID is required. Configure 'app.id' in .fractary/settings.json");
|
|
149
|
+
}
|
|
150
|
+
if (!this.config.installation_id) {
|
|
151
|
+
throw new Error("GitHub App Installation ID is required. Configure 'app.installation_id' in .fractary/settings.json");
|
|
152
|
+
}
|
|
153
|
+
// Validate private key can be loaded
|
|
154
|
+
await PrivateKeyLoader.load(this.config);
|
|
155
|
+
// Attempt to generate JWT to validate key
|
|
156
|
+
await this.generateJWT();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Perform the actual token refresh
|
|
160
|
+
*/
|
|
161
|
+
async doRefresh() {
|
|
162
|
+
const jwtToken = await this.generateJWT();
|
|
163
|
+
const installationToken = await this.exchangeForInstallationToken(jwtToken);
|
|
164
|
+
// Cache the token
|
|
165
|
+
this.cache.set(this.config.installation_id, {
|
|
166
|
+
token: installationToken.token,
|
|
167
|
+
expires_at: new Date(installationToken.expires_at),
|
|
168
|
+
installation_id: this.config.installation_id,
|
|
169
|
+
});
|
|
170
|
+
return installationToken.token;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Generate a JWT for GitHub App authentication
|
|
174
|
+
*/
|
|
175
|
+
async generateJWT() {
|
|
176
|
+
const privateKey = await PrivateKeyLoader.load(this.config);
|
|
177
|
+
const now = Math.floor(Date.now() / 1000);
|
|
178
|
+
const payload = {
|
|
179
|
+
iat: now - 60, // Issued 60 seconds ago to allow for clock drift
|
|
180
|
+
exp: now + GitHubAppAuth.JWT_EXPIRY_SECONDS,
|
|
181
|
+
iss: this.config.id,
|
|
182
|
+
};
|
|
183
|
+
try {
|
|
184
|
+
return jwt.sign(payload, privateKey, { algorithm: 'RS256' });
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (error instanceof Error) {
|
|
188
|
+
throw new Error(`Failed to generate JWT: ${error.message}`);
|
|
189
|
+
}
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Exchange JWT for installation access token
|
|
195
|
+
*/
|
|
196
|
+
async exchangeForInstallationToken(jwtToken) {
|
|
197
|
+
const url = `${GitHubAppAuth.GITHUB_API_URL}/app/installations/${this.config.installation_id}/access_tokens`;
|
|
198
|
+
let response;
|
|
199
|
+
try {
|
|
200
|
+
response = await fetch(url, {
|
|
201
|
+
method: 'POST',
|
|
202
|
+
headers: {
|
|
203
|
+
Accept: 'application/vnd.github+json',
|
|
204
|
+
Authorization: `Bearer ${jwtToken}`,
|
|
205
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
if (error instanceof Error) {
|
|
211
|
+
throw new Error(`Failed to connect to GitHub API: ${error.message}`);
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
if (!response.ok) {
|
|
216
|
+
const errorBody = await response.text().catch(() => 'Unknown error');
|
|
217
|
+
if (response.status === 401) {
|
|
218
|
+
throw new Error('Failed to authenticate with GitHub App. Verify App ID and private key are correct.');
|
|
219
|
+
}
|
|
220
|
+
if (response.status === 404) {
|
|
221
|
+
throw new Error(`GitHub App installation not found (ID: ${this.config.installation_id}). ` +
|
|
222
|
+
'Verify the Installation ID is correct and the app is installed.');
|
|
223
|
+
}
|
|
224
|
+
if (response.status === 403) {
|
|
225
|
+
// Check for rate limiting
|
|
226
|
+
const rateLimitRemaining = response.headers.get('x-ratelimit-remaining');
|
|
227
|
+
const rateLimitReset = response.headers.get('x-ratelimit-reset');
|
|
228
|
+
if (rateLimitRemaining === '0' && rateLimitReset) {
|
|
229
|
+
const resetTime = new Date(parseInt(rateLimitReset) * 1000);
|
|
230
|
+
const secondsUntilReset = Math.ceil((resetTime.getTime() - Date.now()) / 1000);
|
|
231
|
+
throw new Error(`GitHub API rate limited. Retry after ${secondsUntilReset} seconds.`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
throw new Error(`Failed to get installation token: ${response.status} ${errorBody}`);
|
|
235
|
+
}
|
|
236
|
+
return response.json();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Check if token is expired
|
|
240
|
+
*/
|
|
241
|
+
isExpired(cached) {
|
|
242
|
+
return cached.expires_at.getTime() <= Date.now();
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Check if token is expiring soon
|
|
246
|
+
*/
|
|
247
|
+
isExpiringSoon(cached) {
|
|
248
|
+
return cached.expires_at.getTime() - Date.now() < GitHubAppAuth.REFRESH_THRESHOLD_MS;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Trigger background token refresh (non-blocking)
|
|
252
|
+
*/
|
|
253
|
+
triggerBackgroundRefresh() {
|
|
254
|
+
if (this.refreshPromise) {
|
|
255
|
+
return; // Already refreshing
|
|
256
|
+
}
|
|
257
|
+
// Fire and forget - errors logged but not thrown
|
|
258
|
+
this.refreshToken().catch(error => {
|
|
259
|
+
console.error('[GitHubAppAuth] Background token refresh failed:', error.message);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Token refresh threshold (5 minutes before expiration)
|
|
264
|
+
GitHubAppAuth.REFRESH_THRESHOLD_MS = 5 * 60 * 1000;
|
|
265
|
+
// JWT validity period (10 minutes max for GitHub)
|
|
266
|
+
GitHubAppAuth.JWT_EXPIRY_SECONDS = 600;
|
|
267
|
+
// GitHub API base URL
|
|
268
|
+
GitHubAppAuth.GITHUB_API_URL = 'https://api.github.com';
|
|
269
|
+
/**
|
|
270
|
+
* Static Token Provider
|
|
271
|
+
*
|
|
272
|
+
* Simple provider for static PAT tokens
|
|
273
|
+
*/
|
|
274
|
+
export class StaticTokenProvider {
|
|
275
|
+
constructor(token) {
|
|
276
|
+
this.token = token;
|
|
277
|
+
}
|
|
278
|
+
async getToken() {
|
|
279
|
+
return this.token;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* GitHub App Token Provider
|
|
284
|
+
*
|
|
285
|
+
* Provider that uses GitHubAppAuth for dynamic token generation
|
|
286
|
+
*/
|
|
287
|
+
export class GitHubAppTokenProvider {
|
|
288
|
+
constructor(auth) {
|
|
289
|
+
this.auth = auth;
|
|
290
|
+
}
|
|
291
|
+
async getToken() {
|
|
292
|
+
return this.auth.getToken();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub App Manifest Flow for Automated Setup
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for creating GitHub Apps via the App Manifest flow,
|
|
5
|
+
* which simplifies setup from 15+ manual steps to a single CLI command.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for generating an app manifest
|
|
9
|
+
*/
|
|
10
|
+
export interface ManifestConfig {
|
|
11
|
+
organization: string;
|
|
12
|
+
repository: string;
|
|
13
|
+
appName?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* GitHub App Manifest structure
|
|
17
|
+
* @see https://docs.github.com/en/apps/sharing-github-apps/registering-a-github-app-from-a-manifest
|
|
18
|
+
*/
|
|
19
|
+
export interface GitHubAppManifest {
|
|
20
|
+
name: string;
|
|
21
|
+
url: string;
|
|
22
|
+
hook_attributes: {
|
|
23
|
+
url: string;
|
|
24
|
+
};
|
|
25
|
+
redirect_url?: string;
|
|
26
|
+
callback_urls?: string[];
|
|
27
|
+
setup_url?: string;
|
|
28
|
+
description: string;
|
|
29
|
+
public: boolean;
|
|
30
|
+
default_permissions: {
|
|
31
|
+
contents: 'read' | 'write';
|
|
32
|
+
issues: 'read' | 'write';
|
|
33
|
+
pull_requests: 'read' | 'write';
|
|
34
|
+
metadata: 'read';
|
|
35
|
+
};
|
|
36
|
+
default_events: string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Response from GitHub App Manifest conversion API
|
|
40
|
+
* @see https://docs.github.com/en/rest/apps/apps#create-a-github-app-from-a-manifest
|
|
41
|
+
*/
|
|
42
|
+
export interface ManifestConversionResponse {
|
|
43
|
+
id: number;
|
|
44
|
+
slug: string;
|
|
45
|
+
node_id: string;
|
|
46
|
+
owner: {
|
|
47
|
+
login: string;
|
|
48
|
+
id: number;
|
|
49
|
+
};
|
|
50
|
+
name: string;
|
|
51
|
+
description: string;
|
|
52
|
+
external_url: string;
|
|
53
|
+
html_url: string;
|
|
54
|
+
created_at: string;
|
|
55
|
+
updated_at: string;
|
|
56
|
+
permissions: {
|
|
57
|
+
contents?: string;
|
|
58
|
+
issues?: string;
|
|
59
|
+
metadata?: string;
|
|
60
|
+
pull_requests?: string;
|
|
61
|
+
};
|
|
62
|
+
events: string[];
|
|
63
|
+
installations_count: number;
|
|
64
|
+
client_id: string;
|
|
65
|
+
client_secret: string;
|
|
66
|
+
webhook_secret: string | null;
|
|
67
|
+
pem: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* App credentials extracted from manifest conversion
|
|
71
|
+
*/
|
|
72
|
+
export interface AppCredentials {
|
|
73
|
+
id: string;
|
|
74
|
+
installation_id: string;
|
|
75
|
+
private_key: string;
|
|
76
|
+
app_slug: string;
|
|
77
|
+
app_name: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Generate GitHub App manifest with FABER's required permissions
|
|
81
|
+
*/
|
|
82
|
+
export declare function generateAppManifest(config: ManifestConfig): GitHubAppManifest;
|
|
83
|
+
/**
|
|
84
|
+
* Generate HTML content for manifest submission
|
|
85
|
+
*
|
|
86
|
+
* The GitHub App Manifest flow requires POSTing a form to GitHub.
|
|
87
|
+
* Since CLI cannot POST directly, we generate an HTML file that the user
|
|
88
|
+
* opens in their browser and clicks to submit.
|
|
89
|
+
*
|
|
90
|
+
* @param manifest - The app manifest to submit
|
|
91
|
+
* @param organization - GitHub organization name
|
|
92
|
+
* @returns HTML content ready to save to file
|
|
93
|
+
*/
|
|
94
|
+
export declare function generateManifestHtml(manifest: GitHubAppManifest, organization: string): string;
|
|
95
|
+
/**
|
|
96
|
+
* Generate GitHub App creation URL (legacy - prefer generateManifestHtml)
|
|
97
|
+
*
|
|
98
|
+
* @deprecated Use generateManifestHtml() instead for proper manifest flow
|
|
99
|
+
*/
|
|
100
|
+
export declare function getManifestCreationUrl(manifest: GitHubAppManifest): string;
|
|
101
|
+
/**
|
|
102
|
+
* Exchange manifest code for app credentials
|
|
103
|
+
*
|
|
104
|
+
* @param code - The code from the GitHub redirect URL
|
|
105
|
+
* @returns App credentials from GitHub
|
|
106
|
+
* @throws Error if code is invalid or API request fails
|
|
107
|
+
*/
|
|
108
|
+
export declare function exchangeCodeForCredentials(code: string): Promise<ManifestConversionResponse>;
|
|
109
|
+
/**
|
|
110
|
+
* Validate app credentials from manifest conversion
|
|
111
|
+
*
|
|
112
|
+
* @param response - The manifest conversion response
|
|
113
|
+
* @throws Error if response is invalid
|
|
114
|
+
*/
|
|
115
|
+
export declare function validateAppCredentials(response: ManifestConversionResponse): void;
|
|
116
|
+
/**
|
|
117
|
+
* Fetch installation ID for the app in the specified organization
|
|
118
|
+
*
|
|
119
|
+
* @param appId - The GitHub App ID
|
|
120
|
+
* @param privateKey - The app's private key in PEM format
|
|
121
|
+
* @param organization - The GitHub organization name
|
|
122
|
+
* @returns The installation ID
|
|
123
|
+
* @throws Error if installation not found or authentication fails
|
|
124
|
+
*/
|
|
125
|
+
export declare function getInstallationId(appId: string, privateKey: string, organization: string): Promise<string>;
|
|
126
|
+
/**
|
|
127
|
+
* Save private key to secure location
|
|
128
|
+
*
|
|
129
|
+
* @param privateKey - The private key content
|
|
130
|
+
* @param organization - The organization name (for filename)
|
|
131
|
+
* @returns Path to the saved private key file
|
|
132
|
+
* @throws Error if file cannot be saved
|
|
133
|
+
*/
|
|
134
|
+
export declare function savePrivateKey(privateKey: string, organization: string): Promise<string>;
|
|
135
|
+
/**
|
|
136
|
+
* Format permissions for display
|
|
137
|
+
*
|
|
138
|
+
* @param manifest - The app manifest
|
|
139
|
+
* @returns Formatted permissions string
|
|
140
|
+
*/
|
|
141
|
+
export declare function formatPermissionsDisplay(manifest: GitHubAppManifest): string;
|
|
142
|
+
//# sourceMappingURL=github-app-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-app-setup.d.ts","sourceRoot":"","sources":["../../src/lib/github-app-setup.ts"],"names":[],"mappings":"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"}
|