berget 0.0.4 → 0.1.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.
@@ -48,8 +48,8 @@ exports.apiClient = (0, openapi_fetch_1.default)({
48
48
  baseUrl: API_BASE_URL,
49
49
  headers: {
50
50
  'Content-Type': 'application/json',
51
- 'Accept': 'application/json'
52
- }
51
+ Accept: 'application/json',
52
+ },
53
53
  });
54
54
  // Authentication functions
55
55
  const getAuthToken = () => {
@@ -72,7 +72,7 @@ const isTokenExpired = (token) => {
72
72
  const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
73
73
  const jsonPayload = decodeURIComponent(atob(base64)
74
74
  .split('')
75
- .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
75
+ .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
76
76
  .join(''));
77
77
  const payload = JSON.parse(jsonPayload);
78
78
  // Check if token has expired
@@ -127,20 +127,22 @@ const createAuthenticatedClient = () => {
127
127
  baseUrl: API_BASE_URL,
128
128
  headers: {
129
129
  'Content-Type': 'application/json',
130
- 'Accept': 'application/json'
131
- }
130
+ Accept: 'application/json',
131
+ },
132
132
  });
133
133
  }
134
134
  return (0, openapi_fetch_1.default)({
135
135
  baseUrl: API_BASE_URL,
136
- headers: token ? {
137
- 'Authorization': `Bearer ${token}`,
138
- 'Content-Type': 'application/json',
139
- 'Accept': 'application/json'
140
- } : {
141
- 'Content-Type': 'application/json',
142
- 'Accept': 'application/json'
143
- },
136
+ headers: token
137
+ ? {
138
+ Authorization: `Bearer ${token}`,
139
+ 'Content-Type': 'application/json',
140
+ Accept: 'application/json',
141
+ }
142
+ : {
143
+ 'Content-Type': 'application/json',
144
+ Accept: 'application/json',
145
+ },
144
146
  });
145
147
  };
146
148
  exports.createAuthenticatedClient = createAuthenticatedClient;
@@ -36,7 +36,9 @@ class AuthService {
36
36
  // Step 1: Initiate device authorization
37
37
  const { data: deviceData, error: deviceError } = yield client_1.apiClient.POST('/v1/auth/device', {});
38
38
  if (deviceError || !deviceData) {
39
- throw new Error(deviceError ? JSON.stringify(deviceError) : 'Failed to get device authorization data');
39
+ throw new Error(deviceError
40
+ ? JSON.stringify(deviceError)
41
+ : 'Failed to get device authorization data');
40
42
  }
41
43
  // Display information to user
42
44
  console.log(chalk_1.default.cyan('\nTo complete login:'));
@@ -46,7 +48,7 @@ class AuthService {
46
48
  try {
47
49
  if (deviceData.verification_url) {
48
50
  yield (0, open_1.default)(deviceData.verification_url);
49
- console.log(chalk_1.default.dim('Browser opened automatically. If it didn\'t open, please use the URL above.'));
51
+ console.log(chalk_1.default.dim("Browser opened automatically. If it didn't open, please use the URL above."));
50
52
  }
51
53
  }
52
54
  catch (error) {
@@ -56,13 +58,13 @@ class AuthService {
56
58
  // Step 2: Poll for completion
57
59
  const startTime = Date.now();
58
60
  const expiresIn = deviceData.expires_in !== undefined ? deviceData.expires_in : 900;
59
- const expiresAt = startTime + (expiresIn * 1000);
61
+ const expiresAt = startTime + expiresIn * 1000;
60
62
  let pollInterval = (deviceData.interval || 5) * 1000;
61
63
  const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
62
64
  let spinnerIdx = 0;
63
65
  while (Date.now() < expiresAt) {
64
66
  // Wait for the polling interval
65
- yield new Promise(resolve => setTimeout(resolve, pollInterval));
67
+ yield new Promise((resolve) => setTimeout(resolve, pollInterval));
66
68
  // Update spinner
67
69
  process.stdout.write(`\r${chalk_1.default.blue(spinner[spinnerIdx])} Waiting for authentication...`);
68
70
  spinnerIdx = (spinnerIdx + 1) % spinner.length;
@@ -70,14 +72,12 @@ class AuthService {
70
72
  const deviceCode = deviceData.device_code || '';
71
73
  const { data: tokenData, error: tokenError } = yield client_1.apiClient.POST('/v1/auth/device/token', {
72
74
  body: {
73
- device_code: deviceCode
74
- }
75
+ device_code: deviceCode,
76
+ },
75
77
  });
76
78
  if (tokenError) {
77
79
  // Parse the error to get status and other details
78
- const errorObj = typeof tokenError === 'string'
79
- ? JSON.parse(tokenError)
80
- : tokenError;
80
+ const errorObj = typeof tokenError === 'string' ? JSON.parse(tokenError) : tokenError;
81
81
  const status = errorObj.status || 0;
82
82
  const errorCode = errorObj.code || '';
83
83
  if (status === 401 || errorCode === 'AUTHORIZATION_PENDING') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "berget",
3
- "version": "0.0.4",
3
+ "version": "0.1.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "berget": "dist/index.js"
package/src/client.ts CHANGED
@@ -1,22 +1,23 @@
1
- import createClient from 'openapi-fetch';
2
- import type { paths } from './types/api';
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import * as os from 'os';
6
- import chalk from 'chalk';
1
+ import createClient from 'openapi-fetch'
2
+ import type { paths } from './types/api'
3
+ import * as fs from 'fs'
4
+ import * as path from 'path'
5
+ import * as os from 'os'
6
+ import chalk from 'chalk'
7
7
 
8
8
  // Configuration directory
9
- const CONFIG_DIR = path.join(os.homedir(), '.berget');
10
- const TOKEN_FILE = path.join(CONFIG_DIR, 'token.json');
9
+ const CONFIG_DIR = path.join(os.homedir(), '.berget')
10
+ const TOKEN_FILE = path.join(CONFIG_DIR, 'token.json')
11
11
 
12
12
  // API Base URL
13
13
  // Use --local flag to test against local API
14
- const isLocalMode = process.argv.includes('--local');
15
- const API_BASE_URL = process.env.BERGET_API_URL ||
16
- (isLocalMode ? 'http://localhost:3000' : 'https://api.berget.ai');
14
+ const isLocalMode = process.argv.includes('--local')
15
+ const API_BASE_URL =
16
+ process.env.BERGET_API_URL ||
17
+ (isLocalMode ? 'http://localhost:3000' : 'https://api.berget.ai')
17
18
 
18
19
  if (isLocalMode && !process.env.BERGET_API_URL) {
19
- console.log(chalk.yellow('Using local API endpoint: http://localhost:3000'));
20
+ console.log(chalk.yellow('Using local API endpoint: http://localhost:3000'))
20
21
  }
21
22
 
22
23
  // Create a typed client for the Berget API
@@ -24,100 +25,109 @@ export const apiClient = createClient<paths>({
24
25
  baseUrl: API_BASE_URL,
25
26
  headers: {
26
27
  'Content-Type': 'application/json',
27
- 'Accept': 'application/json'
28
- }
29
- });
28
+ Accept: 'application/json',
29
+ },
30
+ })
30
31
 
31
32
  // Authentication functions
32
33
  export const getAuthToken = (): string | null => {
33
34
  try {
34
35
  if (fs.existsSync(TOKEN_FILE)) {
35
- const tokenData = JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf8'));
36
- return tokenData.accessToken;
36
+ const tokenData = JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf8'))
37
+ return tokenData.accessToken
37
38
  }
38
39
  } catch (error) {
39
- console.error('Error reading auth token:', error);
40
+ console.error('Error reading auth token:', error)
40
41
  }
41
- return null;
42
- };
42
+ return null
43
+ }
43
44
 
44
45
  // Check if token is expired (JWT tokens have an exp claim)
45
46
  export const isTokenExpired = (token: string): boolean => {
46
47
  try {
47
- const base64Url = token.split('.')[1];
48
- const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
48
+ const base64Url = token.split('.')[1]
49
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
49
50
  const jsonPayload = decodeURIComponent(
50
51
  atob(base64)
51
52
  .split('')
52
- .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
53
+ .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
53
54
  .join('')
54
- );
55
- const payload = JSON.parse(jsonPayload);
56
-
55
+ )
56
+ const payload = JSON.parse(jsonPayload)
57
+
57
58
  // Check if token has expired
58
59
  if (payload.exp) {
59
- return payload.exp * 1000 < Date.now();
60
+ return payload.exp * 1000 < Date.now()
60
61
  }
61
62
  } catch (error) {
62
63
  // If we can't decode the token, assume it's expired
63
- return true;
64
+ return true
64
65
  }
65
-
66
+
66
67
  // If there's no exp claim, assume it's valid
67
- return false;
68
- };
68
+ return false
69
+ }
69
70
 
70
71
  export const saveAuthToken = (token: string): void => {
71
72
  try {
72
73
  if (!fs.existsSync(CONFIG_DIR)) {
73
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
74
+ fs.mkdirSync(CONFIG_DIR, { recursive: true })
74
75
  }
75
- fs.writeFileSync(TOKEN_FILE, JSON.stringify({ accessToken: token }), 'utf8');
76
+ fs.writeFileSync(TOKEN_FILE, JSON.stringify({ accessToken: token }), 'utf8')
76
77
  // Set file permissions to be readable only by the owner
77
- fs.chmodSync(TOKEN_FILE, 0o600);
78
+ fs.chmodSync(TOKEN_FILE, 0o600)
78
79
  } catch (error) {
79
- console.error('Error saving auth token:', error);
80
+ console.error('Error saving auth token:', error)
80
81
  }
81
- };
82
+ }
82
83
 
83
84
  export const clearAuthToken = (): void => {
84
85
  try {
85
86
  if (fs.existsSync(TOKEN_FILE)) {
86
- fs.unlinkSync(TOKEN_FILE);
87
+ fs.unlinkSync(TOKEN_FILE)
87
88
  }
88
89
  } catch (error) {
89
- console.error('Error clearing auth token:', error);
90
+ console.error('Error clearing auth token:', error)
90
91
  }
91
- };
92
+ }
92
93
 
93
94
  // Create an authenticated client
94
95
  export const createAuthenticatedClient = () => {
95
- const token = getAuthToken();
96
-
96
+ const token = getAuthToken()
97
97
  if (!token) {
98
- console.warn(chalk.yellow('No authentication token found. Please run `berget login` first.'));
98
+ console.warn(
99
+ chalk.yellow(
100
+ 'No authentication token found. Please run `berget login` first.'
101
+ )
102
+ )
99
103
  } else if (isTokenExpired(token)) {
100
- console.warn(chalk.yellow('Your authentication token has expired. Please run `berget login` to get a new token.'));
104
+ console.warn(
105
+ chalk.yellow(
106
+ 'Your authentication token has expired. Please run `berget login` to get a new token.'
107
+ )
108
+ )
101
109
  // Optionally clear the expired token
102
- clearAuthToken();
110
+ clearAuthToken()
103
111
  return createClient<paths>({
104
112
  baseUrl: API_BASE_URL,
105
113
  headers: {
106
114
  'Content-Type': 'application/json',
107
- 'Accept': 'application/json'
108
- }
109
- });
115
+ Accept: 'application/json',
116
+ },
117
+ })
110
118
  }
111
-
119
+
112
120
  return createClient<paths>({
113
121
  baseUrl: API_BASE_URL,
114
- headers: token ? {
115
- 'Authorization': `Bearer ${token}`,
116
- 'Content-Type': 'application/json',
117
- 'Accept': 'application/json'
118
- } : {
119
- 'Content-Type': 'application/json',
120
- 'Accept': 'application/json'
121
- },
122
- });
123
- };
122
+ headers: token
123
+ ? {
124
+ Authorization: `Bearer ${token}`,
125
+ 'Content-Type': 'application/json',
126
+ Accept: 'application/json',
127
+ }
128
+ : {
129
+ 'Content-Type': 'application/json',
130
+ Accept: 'application/json',
131
+ },
132
+ })
133
+ }
@@ -1,4 +1,9 @@
1
- import { createAuthenticatedClient, saveAuthToken, clearAuthToken, apiClient } from '../client'
1
+ import {
2
+ createAuthenticatedClient,
3
+ saveAuthToken,
4
+ clearAuthToken,
5
+ apiClient,
6
+ } from '../client'
2
7
  import open from 'open'
3
8
  import chalk from 'chalk'
4
9
  import { handleError } from '../utils/error-handler'
@@ -20,67 +25,97 @@ export class AuthService {
20
25
  try {
21
26
  // Clear any existing token to ensure a fresh login
22
27
  clearAuthToken()
23
-
28
+
24
29
  console.log(chalk.blue('Initiating login process...'))
25
30
 
26
31
  // Step 1: Initiate device authorization
27
- const { data: deviceData, error: deviceError } = await apiClient.POST('/v1/auth/device', {})
28
-
32
+ const { data: deviceData, error: deviceError } = await apiClient.POST(
33
+ '/v1/auth/device',
34
+ {}
35
+ )
36
+
29
37
  if (deviceError || !deviceData) {
30
- throw new Error(deviceError ? JSON.stringify(deviceError) : 'Failed to get device authorization data')
38
+ throw new Error(
39
+ deviceError
40
+ ? JSON.stringify(deviceError)
41
+ : 'Failed to get device authorization data'
42
+ )
31
43
  }
32
-
44
+
33
45
  // Display information to user
34
46
  console.log(chalk.cyan('\nTo complete login:'))
35
- console.log(chalk.cyan(`1. Open this URL: ${chalk.bold(deviceData.verification_url || 'https://auth.berget.ai/device')}`))
36
- console.log(chalk.cyan(`2. Enter this code: ${chalk.bold(deviceData.user_code || '')}\n`))
37
-
47
+ console.log(
48
+ chalk.cyan(
49
+ `1. Open this URL: ${chalk.bold(
50
+ deviceData.verification_url || 'https://auth.berget.ai/device'
51
+ )}`
52
+ )
53
+ )
54
+ console.log(
55
+ chalk.cyan(
56
+ `2. Enter this code: ${chalk.bold(deviceData.user_code || '')}\n`
57
+ )
58
+ )
59
+
38
60
  // Try to open browser automatically
39
61
  try {
40
62
  if (deviceData.verification_url) {
41
63
  await open(deviceData.verification_url)
42
- console.log(chalk.dim('Browser opened automatically. If it didn\'t open, please use the URL above.'))
64
+ console.log(
65
+ chalk.dim(
66
+ "Browser opened automatically. If it didn't open, please use the URL above."
67
+ )
68
+ )
43
69
  }
44
70
  } catch (error) {
45
- console.log(chalk.yellow('Could not open browser automatically. Please open the URL manually.'))
71
+ console.log(
72
+ chalk.yellow(
73
+ 'Could not open browser automatically. Please open the URL manually.'
74
+ )
75
+ )
46
76
  }
47
-
77
+
48
78
  console.log(chalk.dim('\nWaiting for authentication to complete...'))
49
-
79
+
50
80
  // Step 2: Poll for completion
51
81
  const startTime = Date.now()
52
- const expiresIn = deviceData.expires_in !== undefined ? deviceData.expires_in : 900
53
- const expiresAt = startTime + (expiresIn * 1000)
82
+ const expiresIn =
83
+ deviceData.expires_in !== undefined ? deviceData.expires_in : 900
84
+ const expiresAt = startTime + expiresIn * 1000
54
85
  let pollInterval = (deviceData.interval || 5) * 1000
55
-
86
+
56
87
  const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
57
88
  let spinnerIdx = 0
58
-
89
+
59
90
  while (Date.now() < expiresAt) {
60
91
  // Wait for the polling interval
61
- await new Promise(resolve => setTimeout(resolve, pollInterval))
62
-
92
+ await new Promise((resolve) => setTimeout(resolve, pollInterval))
93
+
63
94
  // Update spinner
64
- process.stdout.write(`\r${chalk.blue(spinner[spinnerIdx])} Waiting for authentication...`)
95
+ process.stdout.write(
96
+ `\r${chalk.blue(spinner[spinnerIdx])} Waiting for authentication...`
97
+ )
65
98
  spinnerIdx = (spinnerIdx + 1) % spinner.length
66
-
99
+
67
100
  // Check if authentication is complete
68
101
  const deviceCode = deviceData.device_code || ''
69
- const { data: tokenData, error: tokenError } = await apiClient.POST('/v1/auth/device/token', {
70
- body: {
71
- device_code: deviceCode
102
+ const { data: tokenData, error: tokenError } = await apiClient.POST(
103
+ '/v1/auth/device/token',
104
+ {
105
+ body: {
106
+ device_code: deviceCode,
107
+ },
72
108
  }
73
- })
74
-
109
+ )
110
+
75
111
  if (tokenError) {
76
112
  // Parse the error to get status and other details
77
- const errorObj = typeof tokenError === 'string'
78
- ? JSON.parse(tokenError)
79
- : tokenError;
80
-
81
- const status = errorObj.status || 0;
82
- const errorCode = errorObj.code || '';
83
-
113
+ const errorObj =
114
+ typeof tokenError === 'string' ? JSON.parse(tokenError) : tokenError
115
+
116
+ const status = errorObj.status || 0
117
+ const errorCode = errorObj.code || ''
118
+
84
119
  if (status === 401 || errorCode === 'AUTHORIZATION_PENDING') {
85
120
  // Still waiting for user to complete authorization
86
121
  continue
@@ -91,10 +126,12 @@ export class AuthService {
91
126
  } else if (status === 400) {
92
127
  // Error or expired
93
128
  if (errorCode === 'EXPIRED_TOKEN') {
94
- console.log(chalk.red('\n\nAuthentication timed out. Please try again.'))
129
+ console.log(
130
+ chalk.red('\n\nAuthentication timed out. Please try again.')
131
+ )
95
132
  } else if (errorCode !== 'AUTHORIZATION_PENDING') {
96
133
  // Only show error if it's not the expected "still waiting" error
97
- const errorMessage = errorObj.message || JSON.stringify(errorObj);
134
+ const errorMessage = errorObj.message || JSON.stringify(errorObj)
98
135
  console.log(chalk.red(`\n\nError: ${errorMessage}`))
99
136
  return false
100
137
  } else {
@@ -106,28 +143,38 @@ export class AuthService {
106
143
  // For any other error, log it but continue polling
107
144
  // This makes the flow more resilient to temporary issues
108
145
  if (process.env.DEBUG) {
109
- console.log(chalk.yellow(`\n\nReceived error: ${JSON.stringify(errorObj)}`))
110
- console.log(chalk.yellow('Continuing to wait for authentication...'))
111
- process.stdout.write(`\r${chalk.blue(spinner[spinnerIdx])} Waiting for authentication...`)
146
+ console.log(
147
+ chalk.yellow(`\n\nReceived error: ${JSON.stringify(errorObj)}`)
148
+ )
149
+ console.log(
150
+ chalk.yellow('Continuing to wait for authentication...')
151
+ )
152
+ process.stdout.write(
153
+ `\r${chalk.blue(
154
+ spinner[spinnerIdx]
155
+ )} Waiting for authentication...`
156
+ )
112
157
  }
113
158
  continue
114
159
  }
115
160
  } else if (tokenData && tokenData.token) {
116
161
  // Success!
117
162
  saveAuthToken(tokenData.token)
118
-
163
+
119
164
  process.stdout.write('\r' + ' '.repeat(50) + '\r') // Clear the spinner line
120
165
  console.log(chalk.green('✓ Successfully logged in to Berget'))
121
-
166
+
122
167
  if (tokenData.user) {
123
168
  const user = tokenData.user as any
124
- console.log(chalk.green(`Logged in as ${user.name || user.email || 'User'}`))
169
+ console.log(
170
+ chalk.green(`Logged in as ${user.name || user.email || 'User'}`)
171
+ )
125
172
  }
126
-
173
+
127
174
  return true
128
175
  }
129
176
  }
130
-
177
+
131
178
  console.log(chalk.red('\n\nAuthentication timed out. Please try again.'))
132
179
  return false
133
180
  } catch (error) {
@@ -145,7 +192,7 @@ export class AuthService {
145
192
  return false
146
193
  }
147
194
  }
148
-
195
+
149
196
  public async getUserProfile() {
150
197
  try {
151
198
  const { data, error } = await this.client.GET('/v1/users/me')