berget 1.1.0 → 1.3.0

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.
@@ -14,6 +14,20 @@ export interface paths {
14
14
  /**
15
15
  * List all API keys
16
16
  * @description Lists all API keys for the authenticated user's organization.
17
+ *
18
+ * ## Using the CLI
19
+ *
20
+ * You can also manage API keys using our CLI tool:
21
+ * ```
22
+ * # Login first (if you haven't already)
23
+ * npx berget auth login
24
+ *
25
+ * # List all API keys
26
+ * npx berget api-keys list
27
+ *
28
+ * # Create a new API key
29
+ * npx berget api-keys create --name my-api-key
30
+ * ```
17
31
  */
18
32
  get: {
19
33
  responses: {
@@ -36,6 +50,17 @@ export interface paths {
36
50
  /**
37
51
  * Create a new API key
38
52
  * @description Creates a new API key for the authenticated user's organization. The full API key is only returned once at creation time.
53
+ *
54
+ * ## Using the CLI
55
+ *
56
+ * Creating an API key is easier with our CLI tool:
57
+ * ```
58
+ * # Login first (if you haven't already)
59
+ * npx berget auth login
60
+ *
61
+ * # Create a new API key
62
+ * npx berget api-keys create --name my-api-key
63
+ * ```
39
64
  */
40
65
  post: {
41
66
  requestBody: {
@@ -97,6 +122,21 @@ export interface paths {
97
122
  /**
98
123
  * Rotate an API key
99
124
  * @description Rotates an API key by invalidating the old key and generating a new one. The new key is returned in the response and is only shown once.
125
+ *
126
+ * ## Using the CLI
127
+ *
128
+ * You can also rotate API keys using our CLI tool:
129
+ * ```
130
+ * # Login first (if you haven't already)
131
+ * npx berget auth login
132
+ *
133
+ * # Rotate an API key
134
+ * npx berget api-keys rotate --id <key-id>
135
+ * ```
136
+ *
137
+ * ## Security Note
138
+ *
139
+ * When you rotate an API key, the old key becomes invalid immediately. Make sure to update any applications using the key.
100
140
  */
101
141
  put: {
102
142
  parameters: {
@@ -131,6 +171,20 @@ export interface paths {
131
171
  /**
132
172
  * Get API key usage statistics
133
173
  * @description Returns usage statistics for a specific API key including request count, daily breakdown, model-specific usage, and token consumption.
174
+ *
175
+ * ## Using the CLI
176
+ *
177
+ * You can also view API key usage using our CLI tool:
178
+ * ```
179
+ * # Login first (if you haven't already)
180
+ * npx berget auth login
181
+ *
182
+ * # View usage for a specific API key
183
+ * npx berget api-keys usage --id <key-id>
184
+ *
185
+ * # View usage for all API keys
186
+ * npx berget usage
187
+ * ```
134
188
  */
135
189
  get: {
136
190
  parameters: {
@@ -351,13 +405,27 @@ export interface paths {
351
405
  "/v1/auth/login": {
352
406
  /**
353
407
  * OAuth login
354
- * @description Initiates OAuth login flow via Keycloak
408
+ * @description Initiates OAuth login flow via Keycloak.
409
+ *
410
+ * ## CLI Authentication
411
+ *
412
+ * For a simpler experience, you can use our CLI tool to authenticate:
413
+ * ```
414
+ * npx berget auth login
415
+ * ```
416
+ *
417
+ * After logging in, you can create an API key with:
418
+ * ```
419
+ * npx berget api-keys create --name my-api-key
420
+ * ```
355
421
  */
356
422
  get: {
357
423
  parameters: {
358
424
  query?: {
359
425
  /** @description URL to redirect to after successful login */
360
426
  redirect_uri?: string;
427
+ /** @description How to return the token after successful login (default is redirect) */
428
+ response_type?: "redirect" | "json";
361
429
  };
362
430
  };
363
431
  responses: {
@@ -389,29 +457,108 @@ export interface paths {
389
457
  };
390
458
  };
391
459
  "/v1/auth/device": {
392
- /** Initiate device authorization flow */
460
+ /**
461
+ * Initiate device authorization flow
462
+ * @description Initiates the device authorization flow, returning a device code and user verification URL.
463
+ *
464
+ * ## Using the CLI
465
+ *
466
+ * The recommended way to authenticate is through our CLI tool:
467
+ * ```
468
+ * npx berget auth login
469
+ * ```
470
+ *
471
+ * This handles the device flow automatically and provides a better user experience.
472
+ */
393
473
  post: {
394
474
  responses: {
395
475
  /** @description Device authorization initiated */
396
476
  200: {
397
- content: never;
477
+ content: {
478
+ "application/json": components["schemas"]["DeviceAuthInitResponse"];
479
+ };
398
480
  };
399
481
  };
400
482
  };
401
483
  };
402
484
  "/v1/auth/device/token": {
403
- /** Poll for device token */
485
+ /**
486
+ * Poll for device token
487
+ * @description Polls for the status of a device authorization flow. The client should poll this endpoint
488
+ * until it receives a token or an error.
489
+ *
490
+ * ## Using the CLI
491
+ *
492
+ * The recommended way to authenticate is through our CLI tool:
493
+ * ```
494
+ * npx berget auth login
495
+ * ```
496
+ *
497
+ * This handles the polling automatically and provides a better user experience.
498
+ *
499
+ * ## Troubleshooting
500
+ *
501
+ * - If you receive a 400 error, the device code may be invalid or expired
502
+ * - If you receive a 429 error, you're polling too frequently
503
+ * - If you receive a 500 error, there may be an issue with the authentication service
504
+ */
404
505
  post: {
405
506
  requestBody: {
406
507
  content: {
407
- "application/json": {
408
- device_code?: string;
409
- };
508
+ "application/json": components["schemas"]["DeviceAuthRequest"];
410
509
  };
411
510
  };
412
511
  responses: {
413
512
  /** @description Token returned or pending status */
414
513
  200: {
514
+ content: {
515
+ "application/json": components["schemas"]["DeviceAuthPendingResponse"] | components["schemas"]["DeviceAuthTokenResponse"];
516
+ };
517
+ };
518
+ /** @description Invalid device code or expired token */
519
+ 400: {
520
+ content: never;
521
+ };
522
+ /** @description Polling too frequently */
523
+ 429: {
524
+ content: never;
525
+ };
526
+ /** @description Server error during authentication */
527
+ 500: {
528
+ content: never;
529
+ };
530
+ };
531
+ };
532
+ };
533
+ "/v1/auth/refresh": {
534
+ /**
535
+ * Refresh access token
536
+ * @description Refreshes an access token using a refresh token. This endpoint can be used to obtain a new
537
+ * access token when the current one expires.
538
+ *
539
+ * ## Using the CLI
540
+ *
541
+ * The CLI tool handles token refresh automatically:
542
+ * ```
543
+ * # The CLI will refresh tokens as needed
544
+ * npx berget api-keys list
545
+ * ```
546
+ */
547
+ post: {
548
+ requestBody: {
549
+ content: {
550
+ "application/json": components["schemas"]["RefreshTokenRequest"];
551
+ };
552
+ };
553
+ responses: {
554
+ /** @description New access and refresh tokens */
555
+ 200: {
556
+ content: {
557
+ "application/json": components["schemas"]["RefreshTokenResponse"];
558
+ };
559
+ };
560
+ /** @description Invalid or expired refresh token */
561
+ 401: {
415
562
  content: never;
416
563
  };
417
564
  };
@@ -1418,8 +1565,6 @@ export interface components {
1418
1565
  };
1419
1566
  AuthToken: {
1420
1567
  accessToken: string;
1421
- /** @enum {string} */
1422
- tokenType: "Bearer";
1423
1568
  expiresIn: number;
1424
1569
  user?: {
1425
1570
  id: string;
@@ -1437,6 +1582,55 @@ export interface components {
1437
1582
  /** Format: uri */
1438
1583
  avatarUrl: string;
1439
1584
  };
1585
+ RefreshTokenRequest: {
1586
+ /** @description The refresh token to use */
1587
+ refresh_token: string;
1588
+ /** @description Whether this is a device token */
1589
+ is_device_token?: boolean;
1590
+ };
1591
+ RefreshTokenResponse: {
1592
+ /** @description The new access token */
1593
+ token: string;
1594
+ /** @description The new refresh token */
1595
+ refresh_token: string;
1596
+ /** @description Seconds until the access token expires */
1597
+ expires_in: number;
1598
+ /** @description Seconds until the refresh token expires */
1599
+ refresh_expires_in: number;
1600
+ };
1601
+ DeviceAuthRequest: {
1602
+ /** @description The device code obtained from the device authorization request */
1603
+ device_code: string;
1604
+ };
1605
+ DeviceAuthInitResponse: {
1606
+ /** @description Code used by the device to poll for authentication status */
1607
+ device_code: string;
1608
+ /** @description Code displayed to the user for authentication */
1609
+ user_code: string;
1610
+ /** @description URL where the user should enter the user_code */
1611
+ verification_url: string;
1612
+ /** @description Expiration time in seconds */
1613
+ expires_in: number;
1614
+ /** @description Polling interval in seconds */
1615
+ interval: number;
1616
+ };
1617
+ DeviceAuthPendingResponse: {
1618
+ /**
1619
+ * @description Authentication is still pending
1620
+ * @enum {string}
1621
+ */
1622
+ status: "pending";
1623
+ };
1624
+ DeviceAuthTokenResponse: {
1625
+ /** @description Access token */
1626
+ token: string;
1627
+ /** @description Refresh token */
1628
+ refresh_token: string;
1629
+ /** @description Access token expiration time in seconds */
1630
+ expires_in: number;
1631
+ /** @description Refresh token expiration time in seconds */
1632
+ refresh_expires_in: number;
1633
+ };
1440
1634
  Usage: {
1441
1635
  current_period: {
1442
1636
  /** Format: date-time */
@@ -0,0 +1,4 @@
1
+ declare module "*.json" {
2
+ const value: any;
3
+ export = value;
4
+ }
@@ -2,11 +2,15 @@ import * as fs from 'fs'
2
2
  import * as path from 'path'
3
3
  import * as os from 'os'
4
4
  import chalk from 'chalk'
5
+ import { ApiKeyService } from '../services/api-key-service'
6
+ import readline from 'readline'
7
+ import { logger } from './logger'
5
8
 
6
9
  interface DefaultApiKeyData {
7
10
  id: string
8
11
  name: string
9
12
  prefix: string
13
+ key: string
10
14
  }
11
15
 
12
16
  /**
@@ -44,7 +48,7 @@ export class DefaultApiKeyManager {
44
48
  this.defaultApiKey = JSON.parse(data)
45
49
  }
46
50
  } catch (error) {
47
- console.error(chalk.dim('Failed to load default API key configuration'))
51
+ logger.debug('Failed to load default API key configuration')
48
52
  this.defaultApiKey = null
49
53
  }
50
54
  }
@@ -65,22 +69,29 @@ export class DefaultApiKeyManager {
65
69
  }
66
70
  }
67
71
  } catch (error) {
68
- console.error(chalk.dim('Failed to save default API key configuration'))
72
+ logger.debug('Failed to save default API key configuration')
69
73
  }
70
74
  }
71
75
 
72
76
  /**
73
77
  * Set the default API key
74
78
  */
75
- public setDefaultApiKey(id: string, name: string, prefix: string): void {
76
- this.defaultApiKey = { id, name, prefix }
79
+ public setDefaultApiKey(id: string, name: string, prefix: string, key: string): void {
80
+ this.defaultApiKey = { id, name, prefix, key }
77
81
  this.saveConfig()
78
82
  }
79
83
 
80
84
  /**
81
- * Get the default API key
85
+ * Get the default API key string
82
86
  */
83
- public getDefaultApiKey(): DefaultApiKeyData | null {
87
+ public getDefaultApiKey(): string | null {
88
+ return this.defaultApiKey?.key || null
89
+ }
90
+
91
+ /**
92
+ * Get the default API key data object
93
+ */
94
+ public getDefaultApiKeyData(): DefaultApiKeyData | null {
84
95
  return this.defaultApiKey
85
96
  }
86
97
 
@@ -91,4 +102,111 @@ export class DefaultApiKeyManager {
91
102
  this.defaultApiKey = null
92
103
  this.saveConfig()
93
104
  }
105
+
106
+ /**
107
+ * Prompts the user to select a default API key if none is set
108
+ * @returns The selected API key or null if none was selected
109
+ */
110
+ public async promptForDefaultApiKey(): Promise<string | null> {
111
+ try {
112
+ logger.debug('promptForDefaultApiKey called')
113
+
114
+ // If we already have a default API key, return it
115
+ if (this.defaultApiKey) {
116
+ logger.debug('Using existing default API key')
117
+ return this.defaultApiKey.key
118
+ }
119
+
120
+ logger.debug('No default API key found, getting ApiKeyService')
121
+
122
+ const apiKeyService = ApiKeyService.getInstance()
123
+
124
+ // Get all API keys
125
+ let apiKeys;
126
+ try {
127
+ logger.debug('Calling apiKeyService.list()')
128
+
129
+ apiKeys = await apiKeyService.list()
130
+
131
+ logger.debug(`Got ${apiKeys ? apiKeys.length : 0} API keys`)
132
+
133
+ if (!apiKeys || apiKeys.length === 0) {
134
+ logger.warn('No API keys found. Create one with:')
135
+ logger.info(' berget api-keys create --name "My Key"')
136
+ return null
137
+ }
138
+ } catch (error) {
139
+ // Check if this is an authentication error
140
+ const errorMessage = error instanceof Error ? error.message : String(error);
141
+ const isAuthError = errorMessage.includes('Unauthorized') ||
142
+ errorMessage.includes('Authentication failed') ||
143
+ errorMessage.includes('AUTH_FAILED');
144
+
145
+ if (isAuthError) {
146
+ logger.warn('Authentication required. Please run `berget auth login` first.');
147
+ } else {
148
+ logger.error('Error fetching API keys:');
149
+ if (error instanceof Error) {
150
+ logger.error(error.message);
151
+ logger.debug(`API key list error: ${error.message}`);
152
+ logger.debug(`Stack: ${error.stack}`);
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+
158
+ logger.info('Select an API key to use as default:')
159
+
160
+ // Display available API keys
161
+ apiKeys.forEach((key, index) => {
162
+ logger.log(` ${index + 1}. ${key.name} (${key.prefix}...)`)
163
+ })
164
+
165
+ // Create readline interface for user input
166
+ const rl = readline.createInterface({
167
+ input: process.stdin,
168
+ output: process.stdout
169
+ })
170
+
171
+ // Prompt for selection
172
+ const selection = await new Promise<number>((resolve) => {
173
+ rl.question('Enter number (or press Enter to cancel): ', (answer) => {
174
+ rl.close()
175
+ const num = parseInt(answer.trim(), 10)
176
+ if (isNaN(num) || num < 1 || num > apiKeys.length) {
177
+ resolve(-1) // Invalid selection
178
+ } else {
179
+ resolve(num - 1) // Convert to zero-based index
180
+ }
181
+ })
182
+ })
183
+
184
+ if (selection === -1) {
185
+ logger.warn('No API key selected')
186
+ return null
187
+ }
188
+
189
+ const selectedKey = apiKeys[selection]
190
+
191
+ // Create a new API key with the selected name
192
+ const newKey = await apiKeyService.create({
193
+ name: `CLI Default (copy of ${selectedKey.name})`,
194
+ description: 'Created automatically by the Berget CLI for default use'
195
+ })
196
+
197
+ // Save the new key as default
198
+ this.setDefaultApiKey(
199
+ newKey.id.toString(),
200
+ newKey.name,
201
+ newKey.key.substring(0, 8), // Use first 8 chars as prefix
202
+ newKey.key
203
+ )
204
+
205
+ logger.success(`✓ Default API key set to: ${newKey.name}`)
206
+ return newKey.key
207
+ } catch (error) {
208
+ logger.error('Failed to set default API key:', error)
209
+ return null
210
+ }
211
+ }
94
212
  }
@@ -30,11 +30,11 @@ export function handleError(message: string, error: any): void {
30
30
 
31
31
  // Check for authentication errors
32
32
  if (
33
- (typeof error === 'string' && error.includes('Unauthorized')) ||
34
- (error && error.message && error.message.includes('Unauthorized')) ||
35
- (error && error.code && error.code === 401)
33
+ (typeof error === 'string' && (error.includes('Unauthorized') || error.includes('Authentication failed'))) ||
34
+ (error && error.message && (error.message.includes('Unauthorized') || error.message.includes('Authentication failed'))) ||
35
+ (error && error.code && (error.code === 401 || error.code === 'AUTH_FAILED'))
36
36
  ) {
37
37
  console.error(chalk.yellow('\nYou need to be logged in to use this command.'));
38
- console.error(chalk.yellow('Run `berget login` to authenticate.'));
38
+ console.error(chalk.yellow('Run `berget auth login` to authenticate.'));
39
39
  }
40
40
  }
@@ -0,0 +1,159 @@
1
+ import chalk from 'chalk'
2
+
3
+ /**
4
+ * Log levels in order of increasing verbosity
5
+ */
6
+ export enum LogLevel {
7
+ NONE = 0,
8
+ ERROR = 1,
9
+ WARN = 2,
10
+ INFO = 3,
11
+ DEBUG = 4
12
+ }
13
+
14
+ /**
15
+ * Logger class for centralized logging with configurable log levels
16
+ */
17
+ export class Logger {
18
+ private static instance: Logger
19
+ private logLevel: LogLevel = LogLevel.INFO // Default log level
20
+
21
+ private constructor() {
22
+ // Set log level from environment variable or command line argument
23
+ if (process.env.LOG_LEVEL) {
24
+ this.setLogLevelFromString(process.env.LOG_LEVEL)
25
+ } else if (process.argv.includes('--debug')) {
26
+ this.logLevel = LogLevel.DEBUG
27
+ } else if (process.argv.includes('--quiet')) {
28
+ this.logLevel = LogLevel.ERROR
29
+ }
30
+ }
31
+
32
+ public static getInstance(): Logger {
33
+ if (!Logger.instance) {
34
+ Logger.instance = new Logger()
35
+ }
36
+ return Logger.instance
37
+ }
38
+
39
+ /**
40
+ * Set the log level from a string
41
+ */
42
+ private setLogLevelFromString(level: string): void {
43
+ switch (level.toLowerCase()) {
44
+ case 'none':
45
+ this.logLevel = LogLevel.NONE
46
+ break
47
+ case 'error':
48
+ this.logLevel = LogLevel.ERROR
49
+ break
50
+ case 'warn':
51
+ this.logLevel = LogLevel.WARN
52
+ break
53
+ case 'info':
54
+ this.logLevel = LogLevel.INFO
55
+ break
56
+ case 'debug':
57
+ this.logLevel = LogLevel.DEBUG
58
+ break
59
+ default:
60
+ // Invalid log level, keep default
61
+ console.warn(`Invalid log level: ${level}. Using default (INFO).`)
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Set the log level
67
+ */
68
+ public setLogLevel(level: LogLevel): void {
69
+ this.logLevel = level
70
+ }
71
+
72
+ /**
73
+ * Get the current log level
74
+ */
75
+ public getLogLevel(): LogLevel {
76
+ return this.logLevel
77
+ }
78
+
79
+ /**
80
+ * Log a debug message (only shown at DEBUG level)
81
+ */
82
+ public debug(message: string, ...args: any[]): void {
83
+ if (this.logLevel >= LogLevel.DEBUG) {
84
+ if (args.length > 0) {
85
+ console.log(chalk.yellow(`DEBUG: ${message}`), ...args)
86
+ } else {
87
+ console.log(chalk.yellow(`DEBUG: ${message}`))
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Log an info message (shown at INFO level and above)
94
+ */
95
+ public info(message: string, ...args: any[]): void {
96
+ if (this.logLevel >= LogLevel.INFO) {
97
+ if (args.length > 0) {
98
+ console.log(chalk.blue(message), ...args)
99
+ } else {
100
+ console.log(chalk.blue(message))
101
+ }
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Log a warning message (shown at WARN level and above)
107
+ */
108
+ public warn(message: string, ...args: any[]): void {
109
+ if (this.logLevel >= LogLevel.WARN) {
110
+ if (args.length > 0) {
111
+ console.log(chalk.yellow(message), ...args)
112
+ } else {
113
+ console.log(chalk.yellow(message))
114
+ }
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Log an error message (shown at ERROR level and above)
120
+ */
121
+ public error(message: string, ...args: any[]): void {
122
+ if (this.logLevel >= LogLevel.ERROR) {
123
+ if (args.length > 0) {
124
+ console.error(chalk.red(message), ...args)
125
+ } else {
126
+ console.error(chalk.red(message))
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Log a success message (shown at INFO level and above)
133
+ */
134
+ public success(message: string, ...args: any[]): void {
135
+ if (this.logLevel >= LogLevel.INFO) {
136
+ if (args.length > 0) {
137
+ console.log(chalk.green(message), ...args)
138
+ } else {
139
+ console.log(chalk.green(message))
140
+ }
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Log a plain message without color (shown at INFO level and above)
146
+ */
147
+ public log(message: string, ...args: any[]): void {
148
+ if (this.logLevel >= LogLevel.INFO) {
149
+ if (args.length > 0) {
150
+ console.log(message, ...args)
151
+ } else {
152
+ console.log(message)
153
+ }
154
+ }
155
+ }
156
+ }
157
+
158
+ // Export a singleton instance for easy import
159
+ export const logger = Logger.getInstance()
@@ -2,6 +2,7 @@ import * as fs from 'fs'
2
2
  import * as path from 'path'
3
3
  import * as os from 'os'
4
4
  import chalk from 'chalk'
5
+ import { logger } from './logger'
5
6
 
6
7
  interface TokenData {
7
8
  access_token: string
@@ -44,7 +45,7 @@ export class TokenManager {
44
45
  this.tokenData = JSON.parse(data)
45
46
  }
46
47
  } catch (error) {
47
- console.error(chalk.dim('Failed to load authentication token'))
48
+ logger.error('Failed to load authentication token')
48
49
  this.tokenData = null
49
50
  }
50
51
  }
@@ -65,7 +66,7 @@ export class TokenManager {
65
66
  }
66
67
  }
67
68
  } catch (error) {
68
- console.error(chalk.dim('Failed to save authentication token'))
69
+ logger.error('Failed to save authentication token')
69
70
  }
70
71
  }
71
72
 
@@ -100,14 +101,14 @@ export class TokenManager {
100
101
  const expirationBuffer = 10 * 60 * 1000 // 10 minutes in milliseconds
101
102
  const isExpired = Date.now() + expirationBuffer >= this.tokenData.expires_at;
102
103
 
103
- if (isExpired && process.argv.includes('--debug')) {
104
- console.log(chalk.yellow(`DEBUG: Token expired or expiring soon. Current time: ${new Date().toISOString()}, Expiry: ${new Date(this.tokenData.expires_at).toISOString()}`));
104
+ if (isExpired) {
105
+ logger.debug(`Token expired or expiring soon. Current time: ${new Date().toISOString()}, Expiry: ${new Date(this.tokenData.expires_at).toISOString()}`);
105
106
  }
106
107
 
107
108
  return isExpired;
108
109
  } catch (error) {
109
110
  // If there's any error checking expiration, assume token is expired
110
- console.error(chalk.dim(`Error checking token expiration: ${error instanceof Error ? error.message : String(error)}`));
111
+ logger.error(`Error checking token expiration: ${error instanceof Error ? error.message : String(error)}`);
111
112
  return true;
112
113
  }
113
114
  }
package/tsconfig.json CHANGED
@@ -39,7 +39,7 @@
39
39
  // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
40
40
  // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
41
41
  // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
42
- // "resolveJsonModule": true, /* Enable importing .json files. */
42
+ "resolveJsonModule": true, /* Enable importing .json files. */
43
43
  // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
44
44
  // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
45
45