@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,365 @@
|
|
|
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
|
+
import jwt from 'jsonwebtoken';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
/**
|
|
12
|
+
* Generate GitHub App manifest with FABER's required permissions
|
|
13
|
+
*/
|
|
14
|
+
export function generateAppManifest(config) {
|
|
15
|
+
const appName = config.appName || `FABER CLI - ${config.organization}`;
|
|
16
|
+
return {
|
|
17
|
+
name: appName,
|
|
18
|
+
url: 'https://github.com/fractary/faber',
|
|
19
|
+
hook_attributes: {
|
|
20
|
+
url: 'https://example.com/webhook', // Required but not used
|
|
21
|
+
},
|
|
22
|
+
redirect_url: 'https://example.com', // Required by GitHub for manifest flow
|
|
23
|
+
description: 'FABER CLI for automated workflow management, issue tracking, and repository operations.',
|
|
24
|
+
public: false,
|
|
25
|
+
default_permissions: {
|
|
26
|
+
contents: 'write',
|
|
27
|
+
issues: 'write',
|
|
28
|
+
pull_requests: 'write',
|
|
29
|
+
metadata: 'read',
|
|
30
|
+
},
|
|
31
|
+
default_events: [], // No webhook events needed
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generate HTML content for manifest submission
|
|
36
|
+
*
|
|
37
|
+
* The GitHub App Manifest flow requires POSTing a form to GitHub.
|
|
38
|
+
* Since CLI cannot POST directly, we generate an HTML file that the user
|
|
39
|
+
* opens in their browser and clicks to submit.
|
|
40
|
+
*
|
|
41
|
+
* @param manifest - The app manifest to submit
|
|
42
|
+
* @param organization - GitHub organization name
|
|
43
|
+
* @returns HTML content ready to save to file
|
|
44
|
+
*/
|
|
45
|
+
export function generateManifestHtml(manifest, organization) {
|
|
46
|
+
const manifestJson = JSON.stringify(manifest, null, 2);
|
|
47
|
+
const targetUrl = organization
|
|
48
|
+
? `https://github.com/organizations/${organization}/settings/apps/new`
|
|
49
|
+
: 'https://github.com/settings/apps/new';
|
|
50
|
+
return `<!DOCTYPE html>
|
|
51
|
+
<html lang="en">
|
|
52
|
+
<head>
|
|
53
|
+
<meta charset="UTF-8">
|
|
54
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
55
|
+
<title>FABER CLI - Create GitHub App</title>
|
|
56
|
+
<style>
|
|
57
|
+
body {
|
|
58
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
59
|
+
max-width: 600px;
|
|
60
|
+
margin: 50px auto;
|
|
61
|
+
padding: 20px;
|
|
62
|
+
background: #f6f8fa;
|
|
63
|
+
}
|
|
64
|
+
.container {
|
|
65
|
+
background: white;
|
|
66
|
+
border-radius: 8px;
|
|
67
|
+
padding: 30px;
|
|
68
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
69
|
+
}
|
|
70
|
+
h1 {
|
|
71
|
+
margin-top: 0;
|
|
72
|
+
color: #24292f;
|
|
73
|
+
}
|
|
74
|
+
.info {
|
|
75
|
+
background: #dff6ff;
|
|
76
|
+
border-left: 4px solid #0969da;
|
|
77
|
+
padding: 12px 16px;
|
|
78
|
+
margin: 20px 0;
|
|
79
|
+
border-radius: 4px;
|
|
80
|
+
}
|
|
81
|
+
button {
|
|
82
|
+
background: #2da44e;
|
|
83
|
+
color: white;
|
|
84
|
+
border: none;
|
|
85
|
+
padding: 12px 24px;
|
|
86
|
+
font-size: 16px;
|
|
87
|
+
border-radius: 6px;
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
width: 100%;
|
|
90
|
+
margin-top: 20px;
|
|
91
|
+
}
|
|
92
|
+
button:hover {
|
|
93
|
+
background: #2c974b;
|
|
94
|
+
}
|
|
95
|
+
.manifest {
|
|
96
|
+
background: #f6f8fa;
|
|
97
|
+
border: 1px solid #d0d7de;
|
|
98
|
+
border-radius: 6px;
|
|
99
|
+
padding: 16px;
|
|
100
|
+
margin: 16px 0;
|
|
101
|
+
overflow-x: auto;
|
|
102
|
+
}
|
|
103
|
+
.manifest pre {
|
|
104
|
+
margin: 0;
|
|
105
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
106
|
+
font-size: 12px;
|
|
107
|
+
}
|
|
108
|
+
.instructions {
|
|
109
|
+
color: #57606a;
|
|
110
|
+
margin: 16px 0;
|
|
111
|
+
line-height: 1.6;
|
|
112
|
+
}
|
|
113
|
+
</style>
|
|
114
|
+
</head>
|
|
115
|
+
<body>
|
|
116
|
+
<div class="container">
|
|
117
|
+
<h1>🔐 Create FABER GitHub App</h1>
|
|
118
|
+
|
|
119
|
+
<div class="info">
|
|
120
|
+
<strong>Step 1 of 2:</strong> Click the button below to create your GitHub App
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="instructions">
|
|
124
|
+
<p>This will create a GitHub App for <strong>${organization}</strong> with the following permissions:</p>
|
|
125
|
+
<ul>
|
|
126
|
+
<li>Contents: Write</li>
|
|
127
|
+
<li>Issues: Write</li>
|
|
128
|
+
<li>Pull Requests: Write</li>
|
|
129
|
+
<li>Metadata: Read</li>
|
|
130
|
+
</ul>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<details>
|
|
134
|
+
<summary style="cursor: pointer; color: #0969da;">Show manifest JSON</summary>
|
|
135
|
+
<div class="manifest">
|
|
136
|
+
<pre>${manifestJson}</pre>
|
|
137
|
+
</div>
|
|
138
|
+
</details>
|
|
139
|
+
|
|
140
|
+
<form action="${targetUrl}" method="post" id="manifestForm">
|
|
141
|
+
<input type="hidden" name="manifest" value='${manifestJson.replace(/'/g, ''')}'>
|
|
142
|
+
<button type="submit">Create GitHub App →</button>
|
|
143
|
+
</form>
|
|
144
|
+
|
|
145
|
+
<div class="instructions" style="margin-top: 20px; font-size: 14px;">
|
|
146
|
+
<p><strong>What happens next:</strong></p>
|
|
147
|
+
<ol>
|
|
148
|
+
<li>GitHub will show you the app details for review</li>
|
|
149
|
+
<li>Click "Create GitHub App" to confirm</li>
|
|
150
|
+
<li>GitHub will redirect you to a URL with a code</li>
|
|
151
|
+
<li>Copy the code and paste it into the CLI</li>
|
|
152
|
+
</ol>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</body>
|
|
156
|
+
</html>`;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Generate GitHub App creation URL (legacy - prefer generateManifestHtml)
|
|
160
|
+
*
|
|
161
|
+
* @deprecated Use generateManifestHtml() instead for proper manifest flow
|
|
162
|
+
*/
|
|
163
|
+
export function getManifestCreationUrl(manifest) {
|
|
164
|
+
// For backward compatibility, return the basic GitHub App creation page
|
|
165
|
+
return 'https://github.com/settings/apps/new';
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Exchange manifest code for app credentials
|
|
169
|
+
*
|
|
170
|
+
* @param code - The code from the GitHub redirect URL
|
|
171
|
+
* @returns App credentials from GitHub
|
|
172
|
+
* @throws Error if code is invalid or API request fails
|
|
173
|
+
*/
|
|
174
|
+
export async function exchangeCodeForCredentials(code) {
|
|
175
|
+
const url = `https://api.github.com/app-manifests/${code}/conversions`;
|
|
176
|
+
let response;
|
|
177
|
+
try {
|
|
178
|
+
response = await fetch(url, {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
headers: {
|
|
181
|
+
Accept: 'application/vnd.github+json',
|
|
182
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (error instanceof Error) {
|
|
188
|
+
throw new Error(`Failed to connect to GitHub API: ${error.message}`);
|
|
189
|
+
}
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
if (!response.ok) {
|
|
193
|
+
const errorBody = await response.text().catch(() => 'Unknown error');
|
|
194
|
+
if (response.status === 404) {
|
|
195
|
+
throw new Error('Invalid or expired code. The code may have already been used or is not valid. ' +
|
|
196
|
+
'Please run "fractary-faber auth setup" again to generate a new URL.');
|
|
197
|
+
}
|
|
198
|
+
if (response.status === 422) {
|
|
199
|
+
throw new Error('Invalid code format. The code should be from the GitHub redirect URL after creating the app.');
|
|
200
|
+
}
|
|
201
|
+
throw new Error(`Failed to exchange code for credentials: ${response.status} ${errorBody}`);
|
|
202
|
+
}
|
|
203
|
+
return (await response.json());
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Validate app credentials from manifest conversion
|
|
207
|
+
*
|
|
208
|
+
* @param response - The manifest conversion response
|
|
209
|
+
* @throws Error if response is invalid
|
|
210
|
+
*/
|
|
211
|
+
export function validateAppCredentials(response) {
|
|
212
|
+
if (!response.id) {
|
|
213
|
+
throw new Error('Invalid response: missing app ID');
|
|
214
|
+
}
|
|
215
|
+
if (!response.pem) {
|
|
216
|
+
throw new Error('Invalid response: missing private key');
|
|
217
|
+
}
|
|
218
|
+
// Validate PEM format
|
|
219
|
+
const trimmed = response.pem.trim();
|
|
220
|
+
const isValidPEM = (trimmed.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
|
|
221
|
+
trimmed.endsWith('-----END RSA PRIVATE KEY-----')) ||
|
|
222
|
+
(trimmed.startsWith('-----BEGIN PRIVATE KEY-----') &&
|
|
223
|
+
trimmed.endsWith('-----END PRIVATE KEY-----'));
|
|
224
|
+
if (!isValidPEM) {
|
|
225
|
+
throw new Error('Invalid response: private key is not in PEM format');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Fetch installation ID for the app in the specified organization
|
|
230
|
+
*
|
|
231
|
+
* @param appId - The GitHub App ID
|
|
232
|
+
* @param privateKey - The app's private key in PEM format
|
|
233
|
+
* @param organization - The GitHub organization name
|
|
234
|
+
* @returns The installation ID
|
|
235
|
+
* @throws Error if installation not found or authentication fails
|
|
236
|
+
*/
|
|
237
|
+
export async function getInstallationId(appId, privateKey, organization) {
|
|
238
|
+
// Generate JWT for app authentication
|
|
239
|
+
const jwtToken = generateJWT(appId, privateKey);
|
|
240
|
+
// Fetch installation for organization
|
|
241
|
+
const url = `https://api.github.com/orgs/${organization}/installation`;
|
|
242
|
+
let response;
|
|
243
|
+
try {
|
|
244
|
+
response = await fetch(url, {
|
|
245
|
+
method: 'GET',
|
|
246
|
+
headers: {
|
|
247
|
+
Accept: 'application/vnd.github+json',
|
|
248
|
+
Authorization: `Bearer ${jwtToken}`,
|
|
249
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
if (error instanceof Error) {
|
|
255
|
+
throw new Error(`Failed to connect to GitHub API: ${error.message}`);
|
|
256
|
+
}
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
if (!response.ok) {
|
|
260
|
+
const errorBody = await response.text().catch(() => 'Unknown error');
|
|
261
|
+
if (response.status === 404) {
|
|
262
|
+
throw new Error(`GitHub App not installed on organization "${organization}". ` +
|
|
263
|
+
`Please install the app on at least one repository in the organization. ` +
|
|
264
|
+
`Visit the app settings to install it.`);
|
|
265
|
+
}
|
|
266
|
+
if (response.status === 401) {
|
|
267
|
+
throw new Error('Failed to authenticate with GitHub App. This should not happen with a newly created app. ' +
|
|
268
|
+
'Please try running the setup command again.');
|
|
269
|
+
}
|
|
270
|
+
throw new Error(`Failed to fetch installation ID: ${response.status} ${errorBody}`);
|
|
271
|
+
}
|
|
272
|
+
const installation = (await response.json());
|
|
273
|
+
return installation.id.toString();
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Generate a JWT for GitHub App authentication
|
|
277
|
+
*
|
|
278
|
+
* @param appId - The GitHub App ID
|
|
279
|
+
* @param privateKey - The app's private key in PEM format
|
|
280
|
+
* @returns JWT token
|
|
281
|
+
*/
|
|
282
|
+
function generateJWT(appId, privateKey) {
|
|
283
|
+
const now = Math.floor(Date.now() / 1000);
|
|
284
|
+
const payload = {
|
|
285
|
+
iat: now - 60, // Issued 60 seconds ago to allow for clock drift
|
|
286
|
+
exp: now + 600, // Expires in 10 minutes (GitHub max)
|
|
287
|
+
iss: appId,
|
|
288
|
+
};
|
|
289
|
+
try {
|
|
290
|
+
return jwt.sign(payload, privateKey, { algorithm: 'RS256' });
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
if (error instanceof Error) {
|
|
294
|
+
throw new Error(`Failed to generate JWT: ${error.message}`);
|
|
295
|
+
}
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Save private key to secure location
|
|
301
|
+
*
|
|
302
|
+
* @param privateKey - The private key content
|
|
303
|
+
* @param organization - The organization name (for filename)
|
|
304
|
+
* @returns Path to the saved private key file
|
|
305
|
+
* @throws Error if file cannot be saved
|
|
306
|
+
*/
|
|
307
|
+
export async function savePrivateKey(privateKey, organization) {
|
|
308
|
+
const homeDir = os.homedir();
|
|
309
|
+
const githubDir = path.join(homeDir, '.github');
|
|
310
|
+
// Ensure .github directory exists with restricted permissions
|
|
311
|
+
try {
|
|
312
|
+
await fs.mkdir(githubDir, { recursive: true, mode: 0o700 });
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
if (error instanceof Error) {
|
|
316
|
+
throw new Error(`Failed to create .github directory: ${error.message}`);
|
|
317
|
+
}
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
// Generate key filename
|
|
321
|
+
const keyFileName = `faber-${organization}.pem`;
|
|
322
|
+
const keyPath = path.join(githubDir, keyFileName);
|
|
323
|
+
// Save private key with restricted permissions
|
|
324
|
+
try {
|
|
325
|
+
await fs.writeFile(keyPath, privateKey, { mode: 0o600 });
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
if (error instanceof Error) {
|
|
329
|
+
throw new Error(`Failed to write private key file: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
// Verify file was created with correct permissions (Unix only)
|
|
334
|
+
try {
|
|
335
|
+
const stats = await fs.stat(keyPath);
|
|
336
|
+
const mode = stats.mode & 0o777;
|
|
337
|
+
if (process.platform !== 'win32' && mode !== 0o600) {
|
|
338
|
+
console.warn(`Warning: Private key file permissions are ${mode.toString(8)}, ` +
|
|
339
|
+
`expected 0600. Please restrict access with: chmod 600 ${keyPath}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// Ignore stat errors
|
|
344
|
+
}
|
|
345
|
+
return keyPath;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Format permissions for display
|
|
349
|
+
*
|
|
350
|
+
* @param manifest - The app manifest
|
|
351
|
+
* @returns Formatted permissions string
|
|
352
|
+
*/
|
|
353
|
+
export function formatPermissionsDisplay(manifest) {
|
|
354
|
+
const perms = manifest.default_permissions;
|
|
355
|
+
return Object.entries(perms)
|
|
356
|
+
.map(([key, value]) => {
|
|
357
|
+
const name = key
|
|
358
|
+
.split('_')
|
|
359
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
360
|
+
.join(' ');
|
|
361
|
+
const level = value.charAt(0).toUpperCase() + value.slice(1);
|
|
362
|
+
return ` • ${name}: ${level}`;
|
|
363
|
+
})
|
|
364
|
+
.join('\n');
|
|
365
|
+
}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
* Repo Client
|
|
3
3
|
*
|
|
4
4
|
* Integrates with @fractary/core SDK for repository and work tracking operations
|
|
5
|
+
* Supports both PAT and GitHub App authentication methods.
|
|
5
6
|
*/
|
|
7
|
+
import { WorkManager, RepoManager } from '@fractary/core';
|
|
8
|
+
import type { FaberConfig } from '../types/config.js';
|
|
6
9
|
interface Issue {
|
|
7
10
|
id: string;
|
|
8
11
|
number: number;
|
|
@@ -31,7 +34,7 @@ interface IssueUpdateOptions {
|
|
|
31
34
|
* Repo Client - integrates with @fractary/core SDK
|
|
32
35
|
*
|
|
33
36
|
* Provides repository and work tracking operations using WorkManager and RepoManager
|
|
34
|
-
* from the @fractary/core SDK.
|
|
37
|
+
* from the @fractary/core SDK. Supports both PAT and GitHub App authentication.
|
|
35
38
|
*/
|
|
36
39
|
export declare class RepoClient {
|
|
37
40
|
private config;
|
|
@@ -39,7 +42,24 @@ export declare class RepoClient {
|
|
|
39
42
|
private repoManager;
|
|
40
43
|
private organization;
|
|
41
44
|
private project;
|
|
42
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Create a RepoClient instance (async factory method)
|
|
47
|
+
*
|
|
48
|
+
* Use this method to create RepoClient instances as it supports
|
|
49
|
+
* both PAT and GitHub App authentication.
|
|
50
|
+
*
|
|
51
|
+
* @param config - FABER CLI configuration
|
|
52
|
+
* @returns Promise resolving to RepoClient instance
|
|
53
|
+
*/
|
|
54
|
+
static create(config: FaberConfig): Promise<RepoClient>;
|
|
55
|
+
/**
|
|
56
|
+
* Create a RepoClient instance
|
|
57
|
+
*
|
|
58
|
+
* @param config - FABER CLI configuration
|
|
59
|
+
* @param workManager - Optional pre-initialized WorkManager (for async factory)
|
|
60
|
+
* @param repoManager - Optional pre-initialized RepoManager (for async factory)
|
|
61
|
+
*/
|
|
62
|
+
constructor(config: FaberConfig, workManager?: WorkManager, repoManager?: RepoManager);
|
|
43
63
|
/**
|
|
44
64
|
* Fetch specific issues by ID
|
|
45
65
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo-client.d.ts","sourceRoot":"","sources":["../../src/lib/repo-client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"repo-client.d.ts","sourceRoot":"","sources":["../../src/lib/repo-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAS1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGtD,UAAU,KAAK;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,kBAAkB;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IAExB;;;;;;;;OAQG;WACU,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAmB7D;;;;;;OAMG;gBACS,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW;IA0CrF;;;;OAIG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAclD;;;;OAIG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAetD;;;;OAIG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrD;;;;OAIG;IACG,cAAc,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IA+BzF;;;;OAIG;IACG,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;CA0B9D"}
|
package/dist/lib/repo-client.js
CHANGED
|
@@ -2,29 +2,73 @@
|
|
|
2
2
|
* Repo Client
|
|
3
3
|
*
|
|
4
4
|
* Integrates with @fractary/core SDK for repository and work tracking operations
|
|
5
|
+
* Supports both PAT and GitHub App authentication methods.
|
|
5
6
|
*/
|
|
6
7
|
import { WorkManager, RepoManager } from '@fractary/core';
|
|
7
|
-
import { createWorkConfig, createRepoConfig } from './sdk-config-adapter.js';
|
|
8
|
+
import { createWorkConfig, createRepoConfig, createWorkConfigAsync, createRepoConfigAsync, isGitHubAppConfigured, } from './sdk-config-adapter.js';
|
|
8
9
|
import { sdkIssueToCLIIssue, sdkWorktreeToCLIWorktreeResult } from './sdk-type-adapter.js';
|
|
9
10
|
import os from 'os';
|
|
10
11
|
/**
|
|
11
12
|
* Repo Client - integrates with @fractary/core SDK
|
|
12
13
|
*
|
|
13
14
|
* Provides repository and work tracking operations using WorkManager and RepoManager
|
|
14
|
-
* from the @fractary/core SDK.
|
|
15
|
+
* from the @fractary/core SDK. Supports both PAT and GitHub App authentication.
|
|
15
16
|
*/
|
|
16
17
|
export class RepoClient {
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Create a RepoClient instance (async factory method)
|
|
20
|
+
*
|
|
21
|
+
* Use this method to create RepoClient instances as it supports
|
|
22
|
+
* both PAT and GitHub App authentication.
|
|
23
|
+
*
|
|
24
|
+
* @param config - FABER CLI configuration
|
|
25
|
+
* @returns Promise resolving to RepoClient instance
|
|
26
|
+
*/
|
|
27
|
+
static async create(config) {
|
|
28
|
+
// Use async config methods for GitHub App support
|
|
29
|
+
const workConfig = await createWorkConfigAsync(config);
|
|
30
|
+
const repoConfig = await createRepoConfigAsync(config);
|
|
31
|
+
try {
|
|
32
|
+
const workManager = new WorkManager(workConfig);
|
|
33
|
+
const repoManager = new RepoManager(repoConfig);
|
|
34
|
+
// Create client with pre-initialized managers
|
|
35
|
+
return new RepoClient(config, workManager, repoManager);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error instanceof Error) {
|
|
39
|
+
throw new Error(`Failed to initialize SDK managers: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a RepoClient instance
|
|
46
|
+
*
|
|
47
|
+
* @param config - FABER CLI configuration
|
|
48
|
+
* @param workManager - Optional pre-initialized WorkManager (for async factory)
|
|
49
|
+
* @param repoManager - Optional pre-initialized RepoManager (for async factory)
|
|
50
|
+
*/
|
|
51
|
+
constructor(config, workManager, repoManager) {
|
|
18
52
|
this.config = config;
|
|
19
|
-
|
|
53
|
+
this.organization = config.github?.organization || 'unknown';
|
|
54
|
+
this.project = config.github?.project || 'unknown';
|
|
55
|
+
// If managers are provided (from static create), use them
|
|
56
|
+
if (workManager && repoManager) {
|
|
57
|
+
this.workManager = workManager;
|
|
58
|
+
this.repoManager = repoManager;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Synchronous initialization - only works with PAT
|
|
62
|
+
if (isGitHubAppConfigured(config)) {
|
|
63
|
+
throw new Error('GitHub App authentication requires async initialization. ' +
|
|
64
|
+
'Use RepoClient.create() instead of new RepoClient().');
|
|
65
|
+
}
|
|
66
|
+
// Validate GitHub token for PAT auth
|
|
20
67
|
const token = config.github?.token;
|
|
21
68
|
if (!token) {
|
|
22
69
|
throw new Error('GitHub token not found. Set GITHUB_TOKEN environment variable.');
|
|
23
70
|
}
|
|
24
|
-
//
|
|
25
|
-
this.organization = config.github?.organization || 'unknown';
|
|
26
|
-
this.project = config.github?.project || 'unknown';
|
|
27
|
-
// Create SDK configurations
|
|
71
|
+
// Create SDK configurations (PAT only)
|
|
28
72
|
const workConfig = createWorkConfig(config);
|
|
29
73
|
const repoConfig = createRepoConfig(config);
|
|
30
74
|
// Initialize SDK managers
|
|
@@ -2,9 +2,41 @@
|
|
|
2
2
|
* SDK Configuration Adapter
|
|
3
3
|
*
|
|
4
4
|
* Converts FABER CLI configuration to @fractary/core SDK configuration format
|
|
5
|
+
* Supports both PAT and GitHub App authentication methods.
|
|
5
6
|
*/
|
|
6
7
|
import type { FaberConfig } from '../types/config.js';
|
|
7
8
|
import type { WorkConfig, RepoConfig } from '@fractary/core';
|
|
9
|
+
import { TokenProvider } from './github-app-auth.js';
|
|
10
|
+
/**
|
|
11
|
+
* Get the current token from the provider
|
|
12
|
+
* Used internally and for SDK configuration
|
|
13
|
+
*
|
|
14
|
+
* @param faberConfig - FABER CLI configuration
|
|
15
|
+
* @returns Promise resolving to the current token
|
|
16
|
+
*/
|
|
17
|
+
export declare function getToken(faberConfig: FaberConfig): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Get the token provider for dynamic token refresh
|
|
20
|
+
* Useful for long-running operations that need fresh tokens
|
|
21
|
+
*
|
|
22
|
+
* @param faberConfig - FABER CLI configuration
|
|
23
|
+
* @returns TokenProvider instance
|
|
24
|
+
*/
|
|
25
|
+
export declare function getTokenProviderInstance(faberConfig: FaberConfig): TokenProvider;
|
|
26
|
+
/**
|
|
27
|
+
* Validate GitHub authentication configuration
|
|
28
|
+
*
|
|
29
|
+
* @param faberConfig - FABER CLI configuration
|
|
30
|
+
* @throws Error if configuration is invalid
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateGitHubAuth(faberConfig: FaberConfig): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Check if GitHub App authentication is configured
|
|
35
|
+
*
|
|
36
|
+
* @param faberConfig - FABER CLI configuration
|
|
37
|
+
* @returns true if GitHub App is configured
|
|
38
|
+
*/
|
|
39
|
+
export declare function isGitHubAppConfigured(faberConfig: FaberConfig): boolean;
|
|
8
40
|
/**
|
|
9
41
|
* Create WorkConfig for WorkManager from FaberConfig
|
|
10
42
|
*
|
|
@@ -13,6 +45,16 @@ import type { WorkConfig, RepoConfig } from '@fractary/core';
|
|
|
13
45
|
* @throws Error if required fields are missing
|
|
14
46
|
*/
|
|
15
47
|
export declare function createWorkConfig(faberConfig: FaberConfig): WorkConfig;
|
|
48
|
+
/**
|
|
49
|
+
* Create WorkConfig for WorkManager from FaberConfig (async version)
|
|
50
|
+
*
|
|
51
|
+
* This version supports both PAT and GitHub App authentication.
|
|
52
|
+
*
|
|
53
|
+
* @param faberConfig - FABER CLI configuration
|
|
54
|
+
* @returns Promise resolving to WorkConfig for @fractary/core WorkManager
|
|
55
|
+
* @throws Error if required fields are missing
|
|
56
|
+
*/
|
|
57
|
+
export declare function createWorkConfigAsync(faberConfig: FaberConfig): Promise<WorkConfig>;
|
|
16
58
|
/**
|
|
17
59
|
* Create RepoConfig for RepoManager from FaberConfig
|
|
18
60
|
*
|
|
@@ -21,4 +63,14 @@ export declare function createWorkConfig(faberConfig: FaberConfig): WorkConfig;
|
|
|
21
63
|
* @throws Error if required fields are missing
|
|
22
64
|
*/
|
|
23
65
|
export declare function createRepoConfig(faberConfig: FaberConfig): RepoConfig;
|
|
66
|
+
/**
|
|
67
|
+
* Create RepoConfig for RepoManager from FaberConfig (async version)
|
|
68
|
+
*
|
|
69
|
+
* This version supports both PAT and GitHub App authentication.
|
|
70
|
+
*
|
|
71
|
+
* @param faberConfig - FABER CLI configuration
|
|
72
|
+
* @returns Promise resolving to RepoConfig for @fractary/core RepoManager
|
|
73
|
+
* @throws Error if required fields are missing
|
|
74
|
+
*/
|
|
75
|
+
export declare function createRepoConfigAsync(faberConfig: FaberConfig): Promise<RepoConfig>;
|
|
24
76
|
//# sourceMappingURL=sdk-config-adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk-config-adapter.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-config-adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sdk-config-adapter.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-config-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAEL,aAAa,EAGd,MAAM,sBAAsB,CAAC;AA2D9B;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAGxE;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,WAAW,GAAG,aAAa,CAEhF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAGvE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAmCrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAkBzF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAyBrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAYzF"}
|