@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.
@@ -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, '&#39;')}'>
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. Replaces the previous CLI-based approach.
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
- constructor(config: any);
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;;;;GAIG;AAOH,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,CAAM;IACpB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,GAAG;IA6BvB;;;;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"}
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"}
@@ -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. Replaces the previous CLI-based approach.
15
+ * from the @fractary/core SDK. Supports both PAT and GitHub App authentication.
15
16
  */
16
17
  export class RepoClient {
17
- constructor(config) {
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
- // Validate GitHub token
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
- // Extract organization and project
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;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAuBrE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAiBrE"}
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"}