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.
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = exports.Logger = exports.LogLevel = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ /**
9
+ * Log levels in order of increasing verbosity
10
+ */
11
+ var LogLevel;
12
+ (function (LogLevel) {
13
+ LogLevel[LogLevel["NONE"] = 0] = "NONE";
14
+ LogLevel[LogLevel["ERROR"] = 1] = "ERROR";
15
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
16
+ LogLevel[LogLevel["INFO"] = 3] = "INFO";
17
+ LogLevel[LogLevel["DEBUG"] = 4] = "DEBUG";
18
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
19
+ /**
20
+ * Logger class for centralized logging with configurable log levels
21
+ */
22
+ class Logger {
23
+ constructor() {
24
+ this.logLevel = LogLevel.INFO; // Default log level
25
+ // Set log level from environment variable or command line argument
26
+ if (process.env.LOG_LEVEL) {
27
+ this.setLogLevelFromString(process.env.LOG_LEVEL);
28
+ }
29
+ else if (process.argv.includes('--debug')) {
30
+ this.logLevel = LogLevel.DEBUG;
31
+ }
32
+ else if (process.argv.includes('--quiet')) {
33
+ this.logLevel = LogLevel.ERROR;
34
+ }
35
+ }
36
+ static getInstance() {
37
+ if (!Logger.instance) {
38
+ Logger.instance = new Logger();
39
+ }
40
+ return Logger.instance;
41
+ }
42
+ /**
43
+ * Set the log level from a string
44
+ */
45
+ setLogLevelFromString(level) {
46
+ switch (level.toLowerCase()) {
47
+ case 'none':
48
+ this.logLevel = LogLevel.NONE;
49
+ break;
50
+ case 'error':
51
+ this.logLevel = LogLevel.ERROR;
52
+ break;
53
+ case 'warn':
54
+ this.logLevel = LogLevel.WARN;
55
+ break;
56
+ case 'info':
57
+ this.logLevel = LogLevel.INFO;
58
+ break;
59
+ case 'debug':
60
+ this.logLevel = LogLevel.DEBUG;
61
+ break;
62
+ default:
63
+ // Invalid log level, keep default
64
+ console.warn(`Invalid log level: ${level}. Using default (INFO).`);
65
+ }
66
+ }
67
+ /**
68
+ * Set the log level
69
+ */
70
+ setLogLevel(level) {
71
+ this.logLevel = level;
72
+ }
73
+ /**
74
+ * Get the current log level
75
+ */
76
+ getLogLevel() {
77
+ return this.logLevel;
78
+ }
79
+ /**
80
+ * Log a debug message (only shown at DEBUG level)
81
+ */
82
+ debug(message, ...args) {
83
+ if (this.logLevel >= LogLevel.DEBUG) {
84
+ if (args.length > 0) {
85
+ console.log(chalk_1.default.yellow(`DEBUG: ${message}`), ...args);
86
+ }
87
+ else {
88
+ console.log(chalk_1.default.yellow(`DEBUG: ${message}`));
89
+ }
90
+ }
91
+ }
92
+ /**
93
+ * Log an info message (shown at INFO level and above)
94
+ */
95
+ info(message, ...args) {
96
+ if (this.logLevel >= LogLevel.INFO) {
97
+ if (args.length > 0) {
98
+ console.log(chalk_1.default.blue(message), ...args);
99
+ }
100
+ else {
101
+ console.log(chalk_1.default.blue(message));
102
+ }
103
+ }
104
+ }
105
+ /**
106
+ * Log a warning message (shown at WARN level and above)
107
+ */
108
+ warn(message, ...args) {
109
+ if (this.logLevel >= LogLevel.WARN) {
110
+ if (args.length > 0) {
111
+ console.log(chalk_1.default.yellow(message), ...args);
112
+ }
113
+ else {
114
+ console.log(chalk_1.default.yellow(message));
115
+ }
116
+ }
117
+ }
118
+ /**
119
+ * Log an error message (shown at ERROR level and above)
120
+ */
121
+ error(message, ...args) {
122
+ if (this.logLevel >= LogLevel.ERROR) {
123
+ if (args.length > 0) {
124
+ console.error(chalk_1.default.red(message), ...args);
125
+ }
126
+ else {
127
+ console.error(chalk_1.default.red(message));
128
+ }
129
+ }
130
+ }
131
+ /**
132
+ * Log a success message (shown at INFO level and above)
133
+ */
134
+ success(message, ...args) {
135
+ if (this.logLevel >= LogLevel.INFO) {
136
+ if (args.length > 0) {
137
+ console.log(chalk_1.default.green(message), ...args);
138
+ }
139
+ else {
140
+ console.log(chalk_1.default.green(message));
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Log a plain message without color (shown at INFO level and above)
146
+ */
147
+ log(message, ...args) {
148
+ if (this.logLevel >= LogLevel.INFO) {
149
+ if (args.length > 0) {
150
+ console.log(message, ...args);
151
+ }
152
+ else {
153
+ console.log(message);
154
+ }
155
+ }
156
+ }
157
+ }
158
+ exports.Logger = Logger;
159
+ // Export a singleton instance for easy import
160
+ exports.logger = Logger.getInstance();
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.containsMarkdown = exports.renderMarkdown = void 0;
7
+ const marked_1 = require("marked");
8
+ const marked_terminal_1 = __importDefault(require("marked-terminal"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ // Configure marked to use the terminal renderer
11
+ marked_1.marked.setOptions({
12
+ renderer: new marked_terminal_1.default({
13
+ // Customize the rendering options
14
+ code: chalk_1.default.cyan,
15
+ blockquote: chalk_1.default.gray.italic,
16
+ table: chalk_1.default.white,
17
+ listitem: chalk_1.default.yellow,
18
+ strong: chalk_1.default.bold,
19
+ em: chalk_1.default.italic,
20
+ heading: chalk_1.default.bold.blueBright,
21
+ hr: chalk_1.default.gray,
22
+ link: chalk_1.default.blue.underline,
23
+ // Adjust the width to fit the terminal
24
+ width: process.stdout.columns || 80,
25
+ // Customize code block rendering
26
+ codespan: chalk_1.default.cyan
27
+ })
28
+ });
29
+ /**
30
+ * Render markdown text to terminal-friendly formatted text
31
+ * @param markdown The markdown text to render
32
+ * @returns Formatted text for terminal display
33
+ */
34
+ function renderMarkdown(markdown) {
35
+ if (!markdown)
36
+ return '';
37
+ try {
38
+ // Convert markdown to terminal-friendly text
39
+ return (0, marked_1.marked)(markdown);
40
+ }
41
+ catch (error) {
42
+ // If rendering fails, return the original text
43
+ console.error(`Error rendering markdown: ${error}`);
44
+ return markdown;
45
+ }
46
+ }
47
+ exports.renderMarkdown = renderMarkdown;
48
+ /**
49
+ * Check if a string contains markdown formatting
50
+ * @param text The text to check
51
+ * @returns True if the text contains markdown formatting
52
+ */
53
+ function containsMarkdown(text) {
54
+ if (!text)
55
+ return false;
56
+ // Check for common markdown patterns
57
+ const markdownPatterns = [
58
+ /^#+\s+/m, // Headers
59
+ /\*\*.*?\*\*/, // Bold
60
+ /\*.*?\*/, // Italic
61
+ /`.*?`/, // Inline code
62
+ /```[\s\S]*?```/, // Code blocks
63
+ /\[.*?\]\(.*?\)/, // Links
64
+ /^\s*[-*+]\s+/m, // Lists
65
+ /^\s*\d+\.\s+/m, // Numbered lists
66
+ /^\s*>\s+/m, // Blockquotes
67
+ /\|.*\|.*\|/, // Tables
68
+ /^---+$/m, // Horizontal rules
69
+ /^===+$/m // Alternative headers
70
+ ];
71
+ return markdownPatterns.some(pattern => pattern.test(text));
72
+ }
73
+ exports.containsMarkdown = containsMarkdown;
@@ -22,15 +22,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.TokenManager = void 0;
30
27
  const fs = __importStar(require("fs"));
31
28
  const path = __importStar(require("path"));
32
29
  const os = __importStar(require("os"));
33
- const chalk_1 = __importDefault(require("chalk"));
30
+ const logger_1 = require("./logger");
34
31
  /**
35
32
  * Manages authentication tokens including refresh functionality
36
33
  */
@@ -62,7 +59,7 @@ class TokenManager {
62
59
  }
63
60
  }
64
61
  catch (error) {
65
- console.error(chalk_1.default.dim('Failed to load authentication token'));
62
+ logger_1.logger.error('Failed to load authentication token');
66
63
  this.tokenData = null;
67
64
  }
68
65
  }
@@ -84,7 +81,7 @@ class TokenManager {
84
81
  }
85
82
  }
86
83
  catch (error) {
87
- console.error(chalk_1.default.dim('Failed to save authentication token'));
84
+ logger_1.logger.error('Failed to save authentication token');
88
85
  }
89
86
  }
90
87
  /**
@@ -117,14 +114,14 @@ class TokenManager {
117
114
  // Using a larger buffer to be more proactive about refreshing
118
115
  const expirationBuffer = 10 * 60 * 1000; // 10 minutes in milliseconds
119
116
  const isExpired = Date.now() + expirationBuffer >= this.tokenData.expires_at;
120
- if (isExpired && process.argv.includes('--debug')) {
121
- console.log(chalk_1.default.yellow(`DEBUG: Token expired or expiring soon. Current time: ${new Date().toISOString()}, Expiry: ${new Date(this.tokenData.expires_at).toISOString()}`));
117
+ if (isExpired) {
118
+ logger_1.logger.debug(`Token expired or expiring soon. Current time: ${new Date().toISOString()}, Expiry: ${new Date(this.tokenData.expires_at).toISOString()}`);
122
119
  }
123
120
  return isExpired;
124
121
  }
125
122
  catch (error) {
126
123
  // If there's any error checking expiration, assume token is expired
127
- console.error(chalk_1.default.dim(`Error checking token expiration: ${error instanceof Error ? error.message : String(error)}`));
124
+ logger_1.logger.error(`Error checking token expiration: ${error instanceof Error ? error.message : String(error)}`);
128
125
  return true;
129
126
  }
130
127
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "berget",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "berget": "dist/index.js"
@@ -19,6 +19,8 @@
19
19
  "license": "MIT",
20
20
  "description": "This is a cli command for interacting with the AI infrastructure provider Berget",
21
21
  "devDependencies": {
22
+ "@types/marked": "^5.0.2",
23
+ "@types/marked-terminal": "^6.1.1",
22
24
  "@types/node": "^20.11.20",
23
25
  "tsx": "^4.19.3",
24
26
  "typescript": "^5.3.3"
@@ -27,6 +29,8 @@
27
29
  "chalk": "^4.1.2",
28
30
  "commander": "^12.0.0",
29
31
  "fs-extra": "^11.3.0",
32
+ "marked": "^9.1.6",
33
+ "marked-terminal": "^6.2.0",
30
34
  "open": "^9.1.0",
31
35
  "openapi-fetch": "^0.9.1",
32
36
  "openapi-typescript": "^6.7.4",
package/src/client.ts CHANGED
@@ -5,16 +5,17 @@ import * as path from 'path'
5
5
  import * as os from 'os'
6
6
  import chalk from 'chalk'
7
7
  import { TokenManager } from './utils/token-manager'
8
+ import { logger } from './utils/logger'
8
9
 
9
10
  // API Base URL
10
11
  // Use --local flag to test against local API
11
12
  const isLocalMode = process.argv.includes('--local')
12
- const API_BASE_URL =
13
+ export const API_BASE_URL =
13
14
  process.env.BERGET_API_URL ||
14
15
  (isLocalMode ? 'http://localhost:3000' : 'https://api.berget.ai')
15
16
 
16
- if (isLocalMode && !process.env.BERGET_API_URL && process.argv.includes('--debug')) {
17
- console.log(chalk.yellow('Using local API endpoint: http://localhost:3000'))
17
+ if (isLocalMode && !process.env.BERGET_API_URL) {
18
+ logger.debug('Using local API endpoint: http://localhost:3000')
18
19
  }
19
20
 
20
21
  // Create a typed client for the Berget API
@@ -50,11 +51,9 @@ export const clearAuthToken = (): void => {
50
51
  export const createAuthenticatedClient = () => {
51
52
  const tokenManager = TokenManager.getInstance()
52
53
 
53
- if (!tokenManager.getAccessToken() && process.argv.includes('--debug')) {
54
- console.warn(
55
- chalk.yellow(
56
- 'No authentication token found. Please run `berget auth login` first.'
57
- )
54
+ if (!tokenManager.getAccessToken()) {
55
+ logger.debug(
56
+ 'No authentication token found. Please run `berget auth login` first.'
58
57
  )
59
58
  }
60
59
 
@@ -88,7 +87,10 @@ export const createAuthenticatedClient = () => {
88
87
  }
89
88
 
90
89
  // Update the Authorization header with the current token
91
- if (tokenManager.getAccessToken()) {
90
+ if (
91
+ !args[1]?.headers?.Authorization &&
92
+ tokenManager.getAccessToken()
93
+ ) {
92
94
  if (!args[1]) args[1] = {}
93
95
  if (!args[1].headers) args[1].headers = {}
94
96
  args[1].headers.Authorization = `Bearer ${tokenManager.getAccessToken()}`
@@ -101,17 +103,13 @@ export const createAuthenticatedClient = () => {
101
103
  ...args
102
104
  )
103
105
  } catch (requestError) {
104
- if (process.argv.includes('--debug')) {
105
- console.log(
106
- chalk.red(
107
- `DEBUG: Request error: ${
108
- requestError instanceof Error
109
- ? requestError.message
110
- : String(requestError)
111
- }`
112
- )
113
- )
114
- }
106
+ logger.debug(
107
+ `Request error: ${
108
+ requestError instanceof Error
109
+ ? requestError.message
110
+ : String(requestError)
111
+ }`
112
+ )
115
113
  return {
116
114
  error: {
117
115
  message: `Request failed: ${
@@ -126,66 +124,59 @@ export const createAuthenticatedClient = () => {
126
124
  // If we get an auth error, try to refresh the token and retry
127
125
  if (result.error) {
128
126
  // Detect various forms of authentication errors
129
- let isAuthError = false;
130
-
127
+ let isAuthError = false
128
+
131
129
  try {
132
130
  // Standard 401 Unauthorized
133
- if (typeof result.error === 'object' && result.error.status === 401) {
134
- isAuthError = true;
131
+ if (
132
+ typeof result.error === 'object' &&
133
+ result.error.status === 401
134
+ ) {
135
+ isAuthError = true
135
136
  }
136
137
  // OAuth specific errors
137
- else if (result.error.error &&
138
+ else if (
139
+ result.error.error &&
138
140
  (result.error.error.code === 'invalid_token' ||
139
- result.error.error.code === 'token_expired' ||
140
- result.error.error.message === 'Invalid API key' ||
141
- result.error.error.message?.toLowerCase().includes('token') ||
142
- result.error.error.message?.toLowerCase().includes('unauthorized'))) {
143
- isAuthError = true;
141
+ result.error.error.code === 'token_expired' ||
142
+ result.error.error.message === 'Invalid API key' ||
143
+ result.error.error.message?.toLowerCase().includes('token') ||
144
+ result.error.error.message
145
+ ?.toLowerCase()
146
+ .includes('unauthorized'))
147
+ ) {
148
+ isAuthError = true
144
149
  }
145
150
  // Message-based detection as fallback
146
- else if (typeof result.error === 'string' &&
151
+ else if (
152
+ typeof result.error === 'string' &&
147
153
  (result.error.toLowerCase().includes('unauthorized') ||
148
- result.error.toLowerCase().includes('token') ||
149
- result.error.toLowerCase().includes('auth'))) {
150
- isAuthError = true;
154
+ result.error.toLowerCase().includes('token') ||
155
+ result.error.toLowerCase().includes('auth'))
156
+ ) {
157
+ isAuthError = true
151
158
  }
152
159
  } catch (parseError) {
153
160
  // If we can't parse the error structure, do a simple string check
154
- const errorStr = String(result.error);
155
- if (errorStr.toLowerCase().includes('unauthorized') ||
156
- errorStr.toLowerCase().includes('token') ||
157
- errorStr.toLowerCase().includes('auth')) {
158
- isAuthError = true;
161
+ const errorStr = String(result.error)
162
+ if (
163
+ errorStr.toLowerCase().includes('unauthorized') ||
164
+ errorStr.toLowerCase().includes('token') ||
165
+ errorStr.toLowerCase().includes('auth')
166
+ ) {
167
+ isAuthError = true
159
168
  }
160
169
  }
161
170
 
162
171
  if (isAuthError && tokenManager.getRefreshToken()) {
163
- if (process.argv.includes('--debug')) {
164
- console.log(
165
- chalk.yellow(
166
- 'DEBUG: Auth error detected, attempting token refresh'
167
- )
168
- )
169
- console.log(
170
- chalk.yellow(
171
- `DEBUG: Error details: ${JSON.stringify(
172
- result.error,
173
- null,
174
- 2
175
- )}`
176
- )
177
- )
178
- }
172
+ logger.debug('Auth error detected, attempting token refresh')
173
+ logger.debug(
174
+ `Error details: ${JSON.stringify(result.error, null, 2)}`
175
+ )
179
176
 
180
177
  const refreshed = await refreshAccessToken(tokenManager)
181
178
  if (refreshed) {
182
- if (process.argv.includes('--debug')) {
183
- console.log(
184
- chalk.green(
185
- 'DEBUG: Token refreshed successfully, retrying request'
186
- )
187
- )
188
- }
179
+ logger.debug('Token refreshed successfully, retrying request')
189
180
 
190
181
  // Update the Authorization header with the new token
191
182
  if (!args[1]) args[1] = {}
@@ -197,13 +188,12 @@ export const createAuthenticatedClient = () => {
197
188
  ...args
198
189
  )
199
190
  } else {
200
- if (process.argv.includes('--debug')) {
201
- console.log(chalk.red('DEBUG: Token refresh failed'))
202
- }
203
-
191
+ logger.debug('Token refresh failed')
192
+
204
193
  // Add a more helpful error message for users
205
194
  if (typeof result.error === 'object') {
206
- result.error.userMessage = 'Your session has expired. Please run `berget auth login` to log in again.'
195
+ result.error.userMessage =
196
+ 'Your session has expired. Please run `berget auth login` to log in again.'
207
197
  }
208
198
  }
209
199
  }
@@ -227,9 +217,7 @@ async function refreshAccessToken(
227
217
  const refreshToken = tokenManager.getRefreshToken()
228
218
  if (!refreshToken) return false
229
219
 
230
- if (process.argv.includes('--debug')) {
231
- console.log(chalk.yellow('DEBUG: Attempting to refresh access token'))
232
- }
220
+ logger.debug('Attempting to refresh access token')
233
221
 
234
222
  // Use fetch directly since this endpoint might not be in the OpenAPI spec
235
223
  try {
@@ -240,15 +228,13 @@ async function refreshAccessToken(
240
228
  Accept: 'application/json',
241
229
  },
242
230
  body: JSON.stringify({ refresh_token: refreshToken }),
243
- });
231
+ })
244
232
 
245
233
  // Handle HTTP errors
246
234
  if (!response.ok) {
247
- if (process.argv.includes('--debug')) {
248
- console.log(
249
- chalk.yellow(`DEBUG: Token refresh error: HTTP ${response.status} ${response.statusText}`)
250
- )
251
- }
235
+ logger.debug(
236
+ `Token refresh error: HTTP ${response.status} ${response.statusText}`
237
+ )
252
238
 
253
239
  // Check if the refresh token itself is expired or invalid
254
240
  if (response.status === 401 || response.status === 403) {
@@ -290,34 +276,33 @@ async function refreshAccessToken(
290
276
  return false
291
277
  }
292
278
 
293
- if (process.argv.includes('--debug')) {
294
- console.log(chalk.green('DEBUG: Token refreshed successfully'))
295
- }
279
+ logger.debug('Token refreshed successfully')
296
280
 
297
281
  // Update the token
298
- tokenManager.updateAccessToken(
299
- data.token,
300
- data.expires_in || 3600
301
- )
302
-
282
+ tokenManager.updateAccessToken(data.token, data.expires_in || 3600)
283
+
303
284
  // If a new refresh token was provided, update that too
304
285
  if (data.refresh_token) {
305
- tokenManager.setTokens(data.token, data.refresh_token, data.expires_in || 3600)
306
- if (process.argv.includes('--debug')) {
307
- console.log(chalk.green('DEBUG: Refresh token also updated'))
308
- }
286
+ tokenManager.setTokens(
287
+ data.token,
288
+ data.refresh_token,
289
+ data.expires_in || 3600
290
+ )
291
+ logger.debug('Refresh token also updated')
309
292
  }
310
293
  } catch (fetchError) {
311
294
  console.warn(
312
295
  chalk.yellow(
313
296
  `Failed to refresh token: ${
314
- fetchError instanceof Error ? fetchError.message : String(fetchError)
297
+ fetchError instanceof Error
298
+ ? fetchError.message
299
+ : String(fetchError)
315
300
  }`
316
301
  )
317
302
  )
318
303
  return false
319
304
  }
320
-
305
+
321
306
  return true
322
307
  } catch (error) {
323
308
  console.warn(