berget 1.2.0 → 1.3.1

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.
@@ -4,6 +4,7 @@ import * as os from 'os'
4
4
  import chalk from 'chalk'
5
5
  import { ApiKeyService } from '../services/api-key-service'
6
6
  import readline from 'readline'
7
+ import { logger } from './logger'
7
8
 
8
9
  interface DefaultApiKeyData {
9
10
  id: string
@@ -47,9 +48,7 @@ export class DefaultApiKeyManager {
47
48
  this.defaultApiKey = JSON.parse(data)
48
49
  }
49
50
  } catch (error) {
50
- if (process.argv.includes('--debug')) {
51
- console.error(chalk.dim('Failed to load default API key configuration'))
52
- }
51
+ logger.debug('Failed to load default API key configuration')
53
52
  this.defaultApiKey = null
54
53
  }
55
54
  }
@@ -70,9 +69,7 @@ export class DefaultApiKeyManager {
70
69
  }
71
70
  }
72
71
  } catch (error) {
73
- if (process.argv.includes('--debug')) {
74
- console.error(chalk.dim('Failed to save default API key configuration'))
75
- }
72
+ logger.debug('Failed to save default API key configuration')
76
73
  }
77
74
  }
78
75
 
@@ -112,42 +109,30 @@ export class DefaultApiKeyManager {
112
109
  */
113
110
  public async promptForDefaultApiKey(): Promise<string | null> {
114
111
  try {
115
- const isDebug = process.argv.includes('--debug')
116
-
117
- if (isDebug) {
118
- console.log(chalk.yellow('DEBUG: promptForDefaultApiKey called'))
119
- }
112
+ logger.debug('promptForDefaultApiKey called')
120
113
 
121
114
  // If we already have a default API key, return it
122
115
  if (this.defaultApiKey) {
123
- if (isDebug) {
124
- console.log(chalk.yellow('DEBUG: Using existing default API key'))
125
- }
116
+ logger.debug('Using existing default API key')
126
117
  return this.defaultApiKey.key
127
118
  }
128
119
 
129
- if (isDebug) {
130
- console.log(chalk.yellow('DEBUG: No default API key found, getting ApiKeyService'))
131
- }
120
+ logger.debug('No default API key found, getting ApiKeyService')
132
121
 
133
122
  const apiKeyService = ApiKeyService.getInstance()
134
123
 
135
124
  // Get all API keys
136
125
  let apiKeys;
137
126
  try {
138
- if (isDebug) {
139
- console.log(chalk.yellow('DEBUG: Calling apiKeyService.list()'))
140
- }
127
+ logger.debug('Calling apiKeyService.list()')
141
128
 
142
129
  apiKeys = await apiKeyService.list()
143
130
 
144
- if (isDebug) {
145
- console.log(chalk.yellow(`DEBUG: Got ${apiKeys ? apiKeys.length : 0} API keys`))
146
- }
131
+ logger.debug(`Got ${apiKeys ? apiKeys.length : 0} API keys`)
147
132
 
148
133
  if (!apiKeys || apiKeys.length === 0) {
149
- console.log(chalk.yellow('No API keys found. Create one with:'))
150
- console.log(chalk.blue(' berget api-keys create --name "My Key"'))
134
+ logger.warn('No API keys found. Create one with:')
135
+ logger.info(' berget api-keys create --name "My Key"')
151
136
  return null
152
137
  }
153
138
  } catch (error) {
@@ -158,25 +143,23 @@ export class DefaultApiKeyManager {
158
143
  errorMessage.includes('AUTH_FAILED');
159
144
 
160
145
  if (isAuthError) {
161
- console.log(chalk.yellow('Authentication required. Please run `berget auth login` first.'));
146
+ logger.warn('Authentication required. Please run `berget auth login` first.');
162
147
  } else {
163
- console.log(chalk.red('Error fetching API keys:'));
148
+ logger.error('Error fetching API keys:');
164
149
  if (error instanceof Error) {
165
- console.log(chalk.red(error.message));
166
- if (isDebug) {
167
- console.log(chalk.yellow(`DEBUG: API key list error: ${error.message}`));
168
- console.log(chalk.yellow(`DEBUG: Stack: ${error.stack}`));
169
- }
150
+ logger.error(error.message);
151
+ logger.debug(`API key list error: ${error.message}`);
152
+ logger.debug(`Stack: ${error.stack}`);
170
153
  }
171
154
  }
172
155
  return null;
173
156
  }
174
157
 
175
- console.log(chalk.blue('Select an API key to use as default:'))
158
+ logger.info('Select an API key to use as default:')
176
159
 
177
160
  // Display available API keys
178
161
  apiKeys.forEach((key, index) => {
179
- console.log(` ${index + 1}. ${key.name} (${key.prefix}...)`)
162
+ logger.log(` ${index + 1}. ${key.name} (${key.prefix}...)`)
180
163
  })
181
164
 
182
165
  // Create readline interface for user input
@@ -199,7 +182,7 @@ export class DefaultApiKeyManager {
199
182
  })
200
183
 
201
184
  if (selection === -1) {
202
- console.log(chalk.yellow('No API key selected'))
185
+ logger.warn('No API key selected')
203
186
  return null
204
187
  }
205
188
 
@@ -219,10 +202,10 @@ export class DefaultApiKeyManager {
219
202
  newKey.key
220
203
  )
221
204
 
222
- console.log(chalk.green(`✓ Default API key set to: ${newKey.name}`))
205
+ logger.success(`✓ Default API key set to: ${newKey.name}`)
223
206
  return newKey.key
224
207
  } catch (error) {
225
- console.error(chalk.red('Failed to set default API key:'), error)
208
+ logger.error('Failed to set default API key:', error)
226
209
  return null
227
210
  }
228
211
  }
@@ -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()
@@ -0,0 +1,68 @@
1
+ import { marked } from 'marked'
2
+ import TerminalRenderer from 'marked-terminal'
3
+ import chalk from 'chalk'
4
+
5
+ // Configure marked to use the terminal renderer
6
+ marked.setOptions({
7
+ renderer: new TerminalRenderer({
8
+ // Customize the rendering options
9
+ code: chalk.cyan,
10
+ blockquote: chalk.gray.italic,
11
+ table: chalk.white,
12
+ listitem: chalk.yellow,
13
+ strong: chalk.bold,
14
+ em: chalk.italic,
15
+ heading: chalk.bold.blueBright,
16
+ hr: chalk.gray,
17
+ link: chalk.blue.underline,
18
+ // Adjust the width to fit the terminal
19
+ width: process.stdout.columns || 80,
20
+ // Customize code block rendering
21
+ codespan: chalk.cyan
22
+ })
23
+ })
24
+
25
+ /**
26
+ * Render markdown text to terminal-friendly formatted text
27
+ * @param markdown The markdown text to render
28
+ * @returns Formatted text for terminal display
29
+ */
30
+ export function renderMarkdown(markdown: string): string {
31
+ if (!markdown) return ''
32
+
33
+ try {
34
+ // Convert markdown to terminal-friendly text
35
+ return marked(markdown)
36
+ } catch (error) {
37
+ // If rendering fails, return the original text
38
+ console.error(`Error rendering markdown: ${error}`)
39
+ return markdown
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Check if a string contains markdown formatting
45
+ * @param text The text to check
46
+ * @returns True if the text contains markdown formatting
47
+ */
48
+ export function containsMarkdown(text: string): boolean {
49
+ if (!text) return false
50
+
51
+ // Check for common markdown patterns
52
+ const markdownPatterns = [
53
+ /^#+\s+/m, // Headers
54
+ /\*\*.*?\*\*/, // Bold
55
+ /\*.*?\*/, // Italic
56
+ /`.*?`/, // Inline code
57
+ /```[\s\S]*?```/, // Code blocks
58
+ /\[.*?\]\(.*?\)/, // Links
59
+ /^\s*[-*+]\s+/m, // Lists
60
+ /^\s*\d+\.\s+/m, // Numbered lists
61
+ /^\s*>\s+/m, // Blockquotes
62
+ /\|.*\|.*\|/, // Tables
63
+ /^---+$/m, // Horizontal rules
64
+ /^===+$/m // Alternative headers
65
+ ]
66
+
67
+ return markdownPatterns.some(pattern => pattern.test(text))
68
+ }
@@ -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
  }