berget 0.0.4 → 1.0.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.
@@ -1,11 +1,27 @@
1
- import { createAuthenticatedClient, saveAuthToken, clearAuthToken, apiClient } from '../client'
2
- import open from 'open'
1
+ import {
2
+ createAuthenticatedClient,
3
+ saveAuthToken,
4
+ clearAuthToken,
5
+ apiClient,
6
+ } from '../client'
7
+ // We'll use dynamic import for 'open' to support ESM modules in CommonJS
3
8
  import chalk from 'chalk'
4
9
  import { handleError } from '../utils/error-handler'
10
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
5
11
 
12
+ /**
13
+ * Service for authentication operations
14
+ * Command group: auth
15
+ */
6
16
  export class AuthService {
7
17
  private static instance: AuthService
8
18
  private client = createAuthenticatedClient()
19
+
20
+ // Command group name for this service
21
+ public static readonly COMMAND_GROUP = COMMAND_GROUPS.AUTH
22
+
23
+ // Subcommands for this service
24
+ public static readonly COMMANDS = SUBCOMMANDS.AUTH
9
25
 
10
26
  private constructor() {}
11
27
 
@@ -20,67 +36,99 @@ export class AuthService {
20
36
  try {
21
37
  // Clear any existing token to ensure a fresh login
22
38
  clearAuthToken()
23
-
39
+
24
40
  console.log(chalk.blue('Initiating login process...'))
25
41
 
26
42
  // Step 1: Initiate device authorization
27
- const { data: deviceData, error: deviceError } = await apiClient.POST('/v1/auth/device', {})
28
-
43
+ const { data: deviceData, error: deviceError } = await apiClient.POST(
44
+ '/v1/auth/device',
45
+ {}
46
+ )
47
+
29
48
  if (deviceError || !deviceData) {
30
- throw new Error(deviceError ? JSON.stringify(deviceError) : 'Failed to get device authorization data')
49
+ throw new Error(
50
+ deviceError
51
+ ? JSON.stringify(deviceError)
52
+ : 'Failed to get device authorization data'
53
+ )
31
54
  }
32
-
55
+
33
56
  // Display information to user
34
57
  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
-
58
+ console.log(
59
+ chalk.cyan(
60
+ `1. Open this URL: ${chalk.bold(
61
+ deviceData.verification_url || 'https://auth.berget.ai/device'
62
+ )}`
63
+ )
64
+ )
65
+ console.log(
66
+ chalk.cyan(
67
+ `2. Enter this code: ${chalk.bold(deviceData.user_code || '')}\n`
68
+ )
69
+ )
70
+
38
71
  // Try to open browser automatically
39
72
  try {
40
73
  if (deviceData.verification_url) {
41
- await open(deviceData.verification_url)
42
- console.log(chalk.dim('Browser opened automatically. If it didn\'t open, please use the URL above.'))
74
+ // Use dynamic import for the 'open' package
75
+ const open = await import('open').then(m => m.default);
76
+ await open(deviceData.verification_url);
77
+ console.log(
78
+ chalk.dim(
79
+ "Browser opened automatically. If it didn't open, please use the URL above."
80
+ )
81
+ )
43
82
  }
44
83
  } catch (error) {
45
- console.log(chalk.yellow('Could not open browser automatically. Please open the URL manually.'))
84
+ console.log(
85
+ chalk.yellow(
86
+ 'Could not open browser automatically. Please open the URL manually.'
87
+ )
88
+ )
46
89
  }
47
-
90
+
48
91
  console.log(chalk.dim('\nWaiting for authentication to complete...'))
49
-
92
+
50
93
  // Step 2: Poll for completion
51
94
  const startTime = Date.now()
52
- const expiresIn = deviceData.expires_in !== undefined ? deviceData.expires_in : 900
53
- const expiresAt = startTime + (expiresIn * 1000)
95
+ const expiresIn =
96
+ deviceData.expires_in !== undefined ? deviceData.expires_in : 900
97
+ const expiresAt = startTime + expiresIn * 1000
54
98
  let pollInterval = (deviceData.interval || 5) * 1000
55
-
99
+
56
100
  const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
57
101
  let spinnerIdx = 0
58
-
102
+
59
103
  while (Date.now() < expiresAt) {
60
104
  // Wait for the polling interval
61
- await new Promise(resolve => setTimeout(resolve, pollInterval))
62
-
105
+ await new Promise((resolve) => setTimeout(resolve, pollInterval))
106
+
63
107
  // Update spinner
64
- process.stdout.write(`\r${chalk.blue(spinner[spinnerIdx])} Waiting for authentication...`)
108
+ process.stdout.write(
109
+ `\r${chalk.blue(spinner[spinnerIdx])} Waiting for authentication...`
110
+ )
65
111
  spinnerIdx = (spinnerIdx + 1) % spinner.length
66
-
112
+
67
113
  // Check if authentication is complete
68
114
  const deviceCode = deviceData.device_code || ''
69
- const { data: tokenData, error: tokenError } = await apiClient.POST('/v1/auth/device/token', {
70
- body: {
71
- device_code: deviceCode
115
+ const { data: tokenData, error: tokenError } = await apiClient.POST(
116
+ '/v1/auth/device/token',
117
+ {
118
+ body: {
119
+ device_code: deviceCode,
120
+ },
72
121
  }
73
- })
74
-
122
+ )
123
+
75
124
  if (tokenError) {
76
125
  // 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
-
126
+ const errorObj =
127
+ typeof tokenError === 'string' ? JSON.parse(tokenError) : tokenError
128
+
129
+ const status = errorObj.status || 0
130
+ const errorCode = errorObj.code || ''
131
+
84
132
  if (status === 401 || errorCode === 'AUTHORIZATION_PENDING') {
85
133
  // Still waiting for user to complete authorization
86
134
  continue
@@ -91,10 +139,12 @@ export class AuthService {
91
139
  } else if (status === 400) {
92
140
  // Error or expired
93
141
  if (errorCode === 'EXPIRED_TOKEN') {
94
- console.log(chalk.red('\n\nAuthentication timed out. Please try again.'))
142
+ console.log(
143
+ chalk.red('\n\nAuthentication timed out. Please try again.')
144
+ )
95
145
  } else if (errorCode !== 'AUTHORIZATION_PENDING') {
96
146
  // Only show error if it's not the expected "still waiting" error
97
- const errorMessage = errorObj.message || JSON.stringify(errorObj);
147
+ const errorMessage = errorObj.message || JSON.stringify(errorObj)
98
148
  console.log(chalk.red(`\n\nError: ${errorMessage}`))
99
149
  return false
100
150
  } else {
@@ -106,28 +156,38 @@ export class AuthService {
106
156
  // For any other error, log it but continue polling
107
157
  // This makes the flow more resilient to temporary issues
108
158
  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...`)
159
+ console.log(
160
+ chalk.yellow(`\n\nReceived error: ${JSON.stringify(errorObj)}`)
161
+ )
162
+ console.log(
163
+ chalk.yellow('Continuing to wait for authentication...')
164
+ )
165
+ process.stdout.write(
166
+ `\r${chalk.blue(
167
+ spinner[spinnerIdx]
168
+ )} Waiting for authentication...`
169
+ )
112
170
  }
113
171
  continue
114
172
  }
115
173
  } else if (tokenData && tokenData.token) {
116
174
  // Success!
117
175
  saveAuthToken(tokenData.token)
118
-
176
+
119
177
  process.stdout.write('\r' + ' '.repeat(50) + '\r') // Clear the spinner line
120
178
  console.log(chalk.green('✓ Successfully logged in to Berget'))
121
-
179
+
122
180
  if (tokenData.user) {
123
181
  const user = tokenData.user as any
124
- console.log(chalk.green(`Logged in as ${user.name || user.email || 'User'}`))
182
+ console.log(
183
+ chalk.green(`Logged in as ${user.name || user.email || 'User'}`)
184
+ )
125
185
  }
126
-
186
+
127
187
  return true
128
188
  }
129
189
  }
130
-
190
+
131
191
  console.log(chalk.red('\n\nAuthentication timed out. Please try again.'))
132
192
  return false
133
193
  } catch (error) {
@@ -145,8 +205,12 @@ export class AuthService {
145
205
  return false
146
206
  }
147
207
  }
148
-
149
- public async getUserProfile() {
208
+
209
+ /**
210
+ * Get current user profile
211
+ * Command: berget auth whoami
212
+ */
213
+ public async whoami() {
150
214
  try {
151
215
  const { data, error } = await this.client.GET('/v1/users/me')
152
216
  if (error) throw new Error(JSON.stringify(error))
@@ -1,4 +1,5 @@
1
1
  import { createAuthenticatedClient } from '../client'
2
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
2
3
 
3
4
  export interface Cluster {
4
5
  id: string
@@ -8,9 +9,19 @@ export interface Cluster {
8
9
  created: string
9
10
  }
10
11
 
12
+ /**
13
+ * Service for managing Kubernetes clusters
14
+ * Command group: clusters
15
+ */
11
16
  export class ClusterService {
12
17
  private static instance: ClusterService
13
18
  private client = createAuthenticatedClient()
19
+
20
+ // Command group name for this service
21
+ public static readonly COMMAND_GROUP = COMMAND_GROUPS.CLUSTERS
22
+
23
+ // Subcommands for this service
24
+ public static readonly COMMANDS = SUBCOMMANDS.CLUSTERS
14
25
 
15
26
  private constructor() {}
16
27
 
@@ -21,7 +32,11 @@ export class ClusterService {
21
32
  return ClusterService.instance
22
33
  }
23
34
 
24
- public async getClusterUsage(clusterId: string): Promise<any> {
35
+ /**
36
+ * Get resource usage for a cluster
37
+ * Command: berget clusters get-usage
38
+ */
39
+ public async getUsage(clusterId: string): Promise<any> {
25
40
  try {
26
41
  const { data, error } = await this.client.GET(
27
42
  '/v1/clusters/{clusterId}/usage',
@@ -37,7 +52,11 @@ export class ClusterService {
37
52
  }
38
53
  }
39
54
 
40
- public async listClusters(): Promise<Cluster[]> {
55
+ /**
56
+ * List all clusters
57
+ * Command: berget clusters list
58
+ */
59
+ public async list(): Promise<Cluster[]> {
41
60
  try {
42
61
  const { data, error } = await this.client.GET('/v1/clusters')
43
62
  if (error) throw new Error(JSON.stringify(error))
@@ -47,4 +66,20 @@ export class ClusterService {
47
66
  throw error
48
67
  }
49
68
  }
69
+
70
+ /**
71
+ * Get detailed information about a cluster
72
+ * Command: berget clusters describe
73
+ */
74
+ public async describe(clusterId: string): Promise<Cluster | null> {
75
+ try {
76
+ // This is a placeholder since the API doesn't have a specific endpoint
77
+ // In a real implementation, this would call a specific endpoint
78
+ const clusters = await this.list()
79
+ return clusters.find(cluster => cluster.id === clusterId) || null
80
+ } catch (error) {
81
+ console.error('Failed to describe cluster:', error)
82
+ throw error
83
+ }
84
+ }
50
85
  }
@@ -1,4 +1,5 @@
1
1
  import { createAuthenticatedClient } from '../client'
2
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
2
3
 
3
4
  export interface Collaborator {
4
5
  username: string
@@ -6,9 +7,19 @@ export interface Collaborator {
6
7
  status: string
7
8
  }
8
9
 
10
+ /**
11
+ * Service for managing collaborators
12
+ * Command group: users
13
+ */
9
14
  export class CollaboratorService {
10
15
  private static instance: CollaboratorService
11
16
  private client = createAuthenticatedClient()
17
+
18
+ // Command group name for this service
19
+ public static readonly COMMAND_GROUP = COMMAND_GROUPS.USERS
20
+
21
+ // Subcommands for this service
22
+ public static readonly COMMANDS = SUBCOMMANDS.USERS
12
23
 
13
24
  private constructor() {}
14
25
 
@@ -19,16 +30,24 @@ export class CollaboratorService {
19
30
  return CollaboratorService.instance
20
31
  }
21
32
 
22
- // This endpoint is not available in the API
23
- public async addCollaborator(
33
+ /**
34
+ * Invite a new collaborator
35
+ * Command: berget users invite
36
+ * This endpoint is not available in the API
37
+ */
38
+ public async invite(
24
39
  clusterId: string,
25
40
  githubUsername: string
26
41
  ): Promise<Collaborator[]> {
27
42
  throw new Error('This functionality is not available in the API')
28
43
  }
29
44
 
30
- // This endpoint is not available in the API
31
- public async listCollaborators(clusterId: string): Promise<Collaborator[]> {
45
+ /**
46
+ * List all collaborators
47
+ * Command: berget users list
48
+ * This endpoint is not available in the API
49
+ */
50
+ public async list(clusterId: string): Promise<Collaborator[]> {
32
51
  throw new Error('This functionality is not available in the API')
33
52
  }
34
53
  }
@@ -1,4 +1,5 @@
1
1
  import { createAuthenticatedClient } from '../client'
2
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
2
3
 
3
4
  export interface FluxInstallOptions {
4
5
  cluster: string
@@ -12,9 +13,19 @@ export interface FluxBootstrapOptions {
12
13
  personal?: boolean
13
14
  }
14
15
 
16
+ /**
17
+ * Service for managing Flux CD
18
+ * Command group: flux
19
+ */
15
20
  export class FluxService {
16
21
  private static instance: FluxService
17
22
  private client = createAuthenticatedClient()
23
+
24
+ // Command group name for this service
25
+ public static readonly COMMAND_GROUP = COMMAND_GROUPS.FLUX
26
+
27
+ // Subcommands for this service
28
+ public static readonly COMMANDS = SUBCOMMANDS.FLUX
18
29
 
19
30
  private constructor() {}
20
31
 
@@ -25,13 +36,21 @@ export class FluxService {
25
36
  return FluxService.instance
26
37
  }
27
38
 
28
- // This endpoint is not available in the API
29
- public async installFlux(options: FluxInstallOptions): Promise<boolean> {
39
+ /**
40
+ * Install Flux CD
41
+ * Command: berget flux install
42
+ * This endpoint is not available in the API
43
+ */
44
+ public async install(options: FluxInstallOptions): Promise<boolean> {
30
45
  throw new Error('This functionality is not available in the API')
31
46
  }
32
47
 
33
- // This endpoint is not available in the API
34
- public async bootstrapFlux(options: FluxBootstrapOptions): Promise<boolean> {
48
+ /**
49
+ * Bootstrap Flux CD
50
+ * Command: berget flux bootstrap
51
+ * This endpoint is not available in the API
52
+ */
53
+ public async bootstrap(options: FluxBootstrapOptions): Promise<boolean> {
35
54
  throw new Error('This functionality is not available in the API')
36
55
  }
37
56
  }
@@ -1,4 +1,5 @@
1
1
  import { createAuthenticatedClient } from '../client'
2
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
2
3
 
3
4
  export interface HelmRepoAddOptions {
4
5
  name: string
@@ -12,9 +13,19 @@ export interface HelmInstallOptions {
12
13
  values?: Record<string, string>
13
14
  }
14
15
 
16
+ /**
17
+ * Service for managing Helm charts
18
+ * Command group: helm
19
+ */
15
20
  export class HelmService {
16
21
  private static instance: HelmService
17
22
  private client = createAuthenticatedClient()
23
+
24
+ // Command group name for this service
25
+ public static readonly COMMAND_GROUP = COMMAND_GROUPS.HELM
26
+
27
+ // Subcommands for this service
28
+ public static readonly COMMANDS = SUBCOMMANDS.HELM
18
29
 
19
30
  private constructor() {}
20
31
 
@@ -25,13 +36,21 @@ export class HelmService {
25
36
  return HelmService.instance
26
37
  }
27
38
 
28
- // This endpoint is not available in the API
39
+ /**
40
+ * Add a Helm repository
41
+ * Command: berget helm add-repo
42
+ * This endpoint is not available in the API
43
+ */
29
44
  public async addRepo(options: HelmRepoAddOptions): Promise<boolean> {
30
45
  throw new Error('This functionality is not available in the API')
31
46
  }
32
47
 
33
- // This endpoint is not available in the API
34
- public async installChart(options: HelmInstallOptions): Promise<any> {
48
+ /**
49
+ * Install a Helm chart
50
+ * Command: berget helm install
51
+ * This endpoint is not available in the API
52
+ */
53
+ public async install(options: HelmInstallOptions): Promise<any> {
35
54
  throw new Error('This functionality is not available in the API')
36
55
  }
37
56
  }
@@ -1,8 +1,19 @@
1
1
  import { createAuthenticatedClient } from '../client'
2
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
2
3
 
4
+ /**
5
+ * Service for managing Kubernetes resources
6
+ * Command group: kubectl
7
+ */
3
8
  export class KubectlService {
4
9
  private static instance: KubectlService
5
10
  private client = createAuthenticatedClient()
11
+
12
+ // Command group name for this service
13
+ public static readonly COMMAND_GROUP = COMMAND_GROUPS.KUBECTL
14
+
15
+ // Subcommands for this service
16
+ public static readonly COMMANDS = SUBCOMMANDS.KUBECTL
6
17
 
7
18
  private constructor() {}
8
19
 
@@ -13,18 +24,30 @@ export class KubectlService {
13
24
  return KubectlService.instance
14
25
  }
15
26
 
16
- // This endpoint is not available in the API
27
+ /**
28
+ * Create a Kubernetes namespace
29
+ * Command: berget kubectl create-namespace
30
+ * This endpoint is not available in the API
31
+ */
17
32
  public async createNamespace(name: string): Promise<boolean> {
18
33
  throw new Error('This functionality is not available in the API')
19
34
  }
20
35
 
21
- // This endpoint is not available in the API
22
- public async applyConfiguration(filename: string): Promise<boolean> {
36
+ /**
37
+ * Apply a Kubernetes configuration
38
+ * Command: berget kubectl apply
39
+ * This endpoint is not available in the API
40
+ */
41
+ public async apply(filename: string): Promise<boolean> {
23
42
  throw new Error('This functionality is not available in the API')
24
43
  }
25
44
 
26
- // This endpoint is not available in the API
27
- public async getResources(
45
+ /**
46
+ * Get Kubernetes resources
47
+ * Command: berget kubectl get
48
+ * This endpoint is not available in the API
49
+ */
50
+ public async get(
28
51
  resource: string,
29
52
  namespace?: string
30
53
  ): Promise<any[]> {