sealos-cli 0.1.0 → 1.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.
@@ -104,6 +104,26 @@ export interface paths {
104
104
  patch?: never;
105
105
  trace?: never;
106
106
  };
107
+ "/templates/instances/{instanceName}": {
108
+ parameters: {
109
+ query?: never;
110
+ header?: never;
111
+ path?: never;
112
+ cookie?: never;
113
+ };
114
+ get?: never;
115
+ put?: never;
116
+ post?: never;
117
+ /**
118
+ * Delete template instance
119
+ * @description Deletes a deployed template instance from the user namespace. Instances created with ownerReferences enabled delete their explicit PVCs first and then delete the Instance so Kubernetes garbage collection removes owned dependents. Legacy instances without the ownerReferences-ready marker use the comprehensive label-selector cleanup path before deleting the Instance.
120
+ */
121
+ delete: operations["deleteInstance"];
122
+ options?: never;
123
+ head?: never;
124
+ patch?: never;
125
+ trace?: never;
126
+ };
107
127
  }
108
128
  export type webhooks = Record<string, never>;
109
129
  export interface components {
@@ -1004,4 +1024,212 @@ export interface operations {
1004
1024
  };
1005
1025
  };
1006
1026
  };
1027
+ deleteInstance: {
1028
+ parameters: {
1029
+ query?: never;
1030
+ header?: never;
1031
+ path: {
1032
+ /** @description Name of the deployed template instance to delete (must exist in user namespace) */
1033
+ instanceName: string;
1034
+ };
1035
+ cookie?: never;
1036
+ };
1037
+ requestBody?: never;
1038
+ responses: {
1039
+ /** @description Instance deleted successfully. No response body. */
1040
+ 204: {
1041
+ headers: {
1042
+ [name: string]: unknown;
1043
+ };
1044
+ content?: never;
1045
+ };
1046
+ /** @description Bad request - invalid instance name parameter */
1047
+ 400: {
1048
+ headers: {
1049
+ [name: string]: unknown;
1050
+ };
1051
+ content: {
1052
+ "application/json": {
1053
+ error: {
1054
+ /**
1055
+ * @description High-level error type for categorization
1056
+ * @enum {string}
1057
+ */
1058
+ type: "validation_error";
1059
+ /**
1060
+ * @description Specific error code for programmatic handling and i18n
1061
+ * @enum {string}
1062
+ */
1063
+ code: "INVALID_PARAMETER";
1064
+ /** @description Human-readable error message */
1065
+ message: string;
1066
+ /** @description For INVALID_PARAMETER: Array<{ field, message }>. For INVALID_VALUE: optional string. Omitted for other codes. */
1067
+ details?: {
1068
+ /** @description Field path using dot/bracket notation, e.g. "ports[0].number" */
1069
+ field: string;
1070
+ /** @description Validation error message for this field */
1071
+ message: string;
1072
+ }[] | string;
1073
+ };
1074
+ };
1075
+ };
1076
+ };
1077
+ /** @description Unauthorized - Missing or invalid kubeconfig */
1078
+ 401: {
1079
+ headers: {
1080
+ [name: string]: unknown;
1081
+ };
1082
+ content: {
1083
+ "application/json": {
1084
+ error: {
1085
+ /**
1086
+ * @description High-level error type for categorization
1087
+ * @constant
1088
+ */
1089
+ type: "authentication_error";
1090
+ /**
1091
+ * @description Specific error code for programmatic handling and i18n
1092
+ * @constant
1093
+ */
1094
+ code: "AUTHENTICATION_REQUIRED";
1095
+ /** @description Human-readable error message */
1096
+ message: string;
1097
+ /** @description Typically omitted. May contain additional context in edge cases. */
1098
+ details?: string;
1099
+ };
1100
+ };
1101
+ };
1102
+ };
1103
+ /** @description Forbidden - Insufficient permissions */
1104
+ 403: {
1105
+ headers: {
1106
+ [name: string]: unknown;
1107
+ };
1108
+ content: {
1109
+ "application/json": {
1110
+ error: {
1111
+ /**
1112
+ * @description High-level error type for categorization
1113
+ * @constant
1114
+ */
1115
+ type: "authorization_error";
1116
+ /**
1117
+ * @description Specific error code for programmatic handling and i18n
1118
+ * @enum {string}
1119
+ */
1120
+ code: "PERMISSION_DENIED";
1121
+ /** @description Human-readable error message */
1122
+ message: string;
1123
+ /** @description Typically omitted. May contain additional context in edge cases. */
1124
+ details?: string;
1125
+ };
1126
+ };
1127
+ };
1128
+ };
1129
+ /** @description Not Found - Instance not found */
1130
+ 404: {
1131
+ headers: {
1132
+ [name: string]: unknown;
1133
+ };
1134
+ content: {
1135
+ "application/json": {
1136
+ error: {
1137
+ /**
1138
+ * @description High-level error type for categorization
1139
+ * @constant
1140
+ */
1141
+ type: "resource_error";
1142
+ /**
1143
+ * @description Specific error code for programmatic handling and i18n
1144
+ * @constant
1145
+ */
1146
+ code: "NOT_FOUND";
1147
+ /** @description Human-readable error message */
1148
+ message: string;
1149
+ /** @description Typically omitted. May contain additional context in edge cases. */
1150
+ details?: string;
1151
+ };
1152
+ };
1153
+ };
1154
+ };
1155
+ /** @description Method Not Allowed */
1156
+ 405: {
1157
+ headers: {
1158
+ [name: string]: unknown;
1159
+ };
1160
+ content: {
1161
+ "application/json": {
1162
+ error: {
1163
+ /**
1164
+ * @description High-level error type for categorization
1165
+ * @constant
1166
+ */
1167
+ type: "client_error";
1168
+ /**
1169
+ * @description Specific error code for programmatic handling and i18n
1170
+ * @constant
1171
+ */
1172
+ code: "METHOD_NOT_ALLOWED";
1173
+ /** @description Human-readable error message */
1174
+ message: string;
1175
+ /** @description Typically omitted. */
1176
+ details?: string;
1177
+ };
1178
+ };
1179
+ };
1180
+ };
1181
+ /** @description Internal Server Error - Kubernetes API error or unexpected failure */
1182
+ 500: {
1183
+ headers: {
1184
+ [name: string]: unknown;
1185
+ };
1186
+ content: {
1187
+ "application/json": {
1188
+ error: {
1189
+ /**
1190
+ * @description High-level error type for categorization
1191
+ * @enum {string}
1192
+ */
1193
+ type: "operation_error" | "internal_error";
1194
+ /**
1195
+ * @description Specific error code for programmatic handling and i18n
1196
+ * @enum {string}
1197
+ */
1198
+ code: "KUBERNETES_ERROR" | "INTERNAL_ERROR";
1199
+ /** @description Human-readable error message */
1200
+ message: string;
1201
+ /** @description Raw error string from the underlying system, for troubleshooting. */
1202
+ details?: string;
1203
+ };
1204
+ };
1205
+ };
1206
+ };
1207
+ /** @description Service Unavailable - Kubernetes cluster temporarily unreachable */
1208
+ 503: {
1209
+ headers: {
1210
+ [name: string]: unknown;
1211
+ };
1212
+ content: {
1213
+ "application/json": {
1214
+ error: {
1215
+ /**
1216
+ * @description High-level error type for categorization
1217
+ * @constant
1218
+ */
1219
+ type: "internal_error";
1220
+ /**
1221
+ * @description Specific error code for programmatic handling and i18n
1222
+ * @constant
1223
+ */
1224
+ code: "SERVICE_UNAVAILABLE";
1225
+ /** @description Human-readable error message */
1226
+ message: string;
1227
+ /** @description Raw connection error from the underlying system (e.g. ECONNREFUSED). */
1228
+ details?: string;
1229
+ };
1230
+ };
1231
+ };
1232
+ };
1233
+ };
1234
+ };
1007
1235
  }
@@ -1,6 +1,7 @@
1
1
  import createClient from 'openapi-fetch'
2
2
  import type { paths as TemplatePaths } from '../generated/template.ts'
3
3
  import type { paths as DatabasePaths } from '../generated/database.ts'
4
+ import type { paths as DevboxPaths } from '../generated/devbox.ts'
4
5
  import { DEFAULT_SEALOS_REGION, loadAuth } from './auth.ts'
5
6
  import { ConfigError } from './errors.ts'
6
7
 
@@ -14,7 +15,7 @@ function resolveHost (options?: { baseUrl?: string }): string {
14
15
 
15
16
  const host = options?.baseUrl || process.env.SEALOS_REGION || authRegion || DEFAULT_SEALOS_REGION
16
17
  if (!host) {
17
- throw new ConfigError('No Sealos Cloud host configured. Run "sealos login <host>" first.')
18
+ throw new ConfigError('No Sealos Cloud host configured. Run "sealos-cli login <host>" first.')
18
19
  }
19
20
  return host.replace(/\/+$/, '')
20
21
  }
@@ -27,6 +28,10 @@ export function resolveTemplateProviderHost (host: string): string {
27
28
  return resolvePrefixedHost(host, 'template')
28
29
  }
29
30
 
31
+ export function resolveDevboxProviderHost (host: string): string {
32
+ return resolvePrefixedHost(host, 'devbox')
33
+ }
34
+
30
35
  function resolvePrefixedHost (host: string, prefix: string): string {
31
36
  const url = new URL(host)
32
37
 
@@ -55,6 +60,15 @@ function resolveTemplateHost (options?: { baseUrl?: string }): string {
55
60
  return resolveTemplateProviderHost(resolveHost(options))
56
61
  }
57
62
 
63
+ function resolveDevboxHost (options?: { baseUrl?: string }): string {
64
+ const override = process.env.SEALOS_DEVBOX_HOST?.trim()
65
+ if (override) {
66
+ return override.replace(/\/+$/, '')
67
+ }
68
+
69
+ return resolveDevboxProviderHost(resolveHost(options))
70
+ }
71
+
58
72
  export function createTemplateClient (options?: { baseUrl?: string }) {
59
73
  return createClient<TemplatePaths>({ baseUrl: `${resolveTemplateHost(options)}/api/v2alpha` })
60
74
  }
@@ -62,3 +76,7 @@ export function createTemplateClient (options?: { baseUrl?: string }) {
62
76
  export function createDatabaseClient (options?: { baseUrl?: string }) {
63
77
  return createClient<DatabasePaths>({ baseUrl: `${resolveDatabaseHost(options)}/api/v2alpha` })
64
78
  }
79
+
80
+ export function createDevboxClient (options?: { baseUrl?: string }) {
81
+ return createClient<DevboxPaths>({ baseUrl: `${resolveDevboxHost(options)}/api/v2alpha` })
82
+ }
package/src/lib/auth.ts CHANGED
@@ -141,13 +141,13 @@ export function saveAuth (auth: SealosAuthData, deps: AuthDependencies = {}): vo
141
141
  export function loadAuth (deps: AuthDependencies = {}): SealosAuthData {
142
142
  const { paths } = withDeps(deps)
143
143
  if (!existsSync(paths.authPath)) {
144
- throw new Error('Not authenticated. Please run: sealos login')
144
+ throw new Error('Not authenticated. Please run: sealos-cli login')
145
145
  }
146
146
 
147
147
  return JSON.parse(readFileSync(paths.authPath, 'utf-8')) as SealosAuthData
148
148
  }
149
149
 
150
- export function getToken (deps: AuthDependencies = {}): string | null {
150
+ export function getRegionalToken (deps: AuthDependencies = {}): string | null {
151
151
  try {
152
152
  return loadAuth(deps).regional_token || null
153
153
  } catch {
@@ -155,15 +155,24 @@ export function getToken (deps: AuthDependencies = {}): string | null {
155
155
  }
156
156
  }
157
157
 
158
+ export function getKubeconfigContent (deps: AuthDependencies = {}): string | null {
159
+ const { paths } = withDeps(deps)
160
+ try {
161
+ return readFileSync(paths.kubeconfigPath, 'utf-8')
162
+ } catch {
163
+ return null
164
+ }
165
+ }
166
+
158
167
  export function getAuthHeaders (deps: AuthDependencies = {}): { Authorization: string } | null {
159
- const token = getToken(deps)
160
- return token ? { Authorization: token } : null
168
+ const kubeconfig = getKubeconfigContent(deps)
169
+ return kubeconfig ? { Authorization: encodeURIComponent(kubeconfig) } : null
161
170
  }
162
171
 
163
172
  export function requireAuth (deps: AuthDependencies = {}): { Authorization: string } {
164
173
  const headers = getAuthHeaders(deps)
165
174
  if (!headers) {
166
- throw new Error('Authentication required. Please run "sealos login" first.')
175
+ throw new Error('Authentication required. Please run "sealos-cli login" first.')
167
176
  }
168
177
  return headers
169
178
  }
@@ -487,7 +496,7 @@ export async function loginWithDeviceFlow (region?: string, deps: AuthDependenci
487
496
  export async function listWorkspaces (deps: AuthDependencies = {}): Promise<WorkspaceListResult> {
488
497
  const auth = loadAuth(deps)
489
498
  if (!auth.regional_token) {
490
- throw new Error('No regional_token found. Please run: sealos login')
499
+ throw new Error('No regional_token found. Please run: sealos-cli login')
491
500
  }
492
501
 
493
502
  const workspaces = await listRemoteWorkspaces(auth.region, auth.regional_token, deps)
@@ -505,13 +514,13 @@ export async function listWorkspaces (deps: AuthDependencies = {}): Promise<Work
505
514
 
506
515
  export async function switchWorkspace (target: string, deps: AuthDependencies = {}): Promise<SwitchWorkspaceResult> {
507
516
  if (!target) {
508
- throw new Error('Usage: sealos auth switch <namespace-id-or-uid>')
517
+ throw new Error('Usage: sealos-cli auth switch <namespace-id-or-uid>')
509
518
  }
510
519
 
511
520
  const fullDeps = withDeps(deps)
512
521
  const auth = loadAuth(fullDeps)
513
522
  if (!auth.regional_token) {
514
- throw new Error('No regional_token found. Please run: sealos login')
523
+ throw new Error('No regional_token found. Please run: sealos-cli login')
515
524
  }
516
525
 
517
526
  const workspaces = await listRemoteWorkspaces(auth.region, auth.regional_token, fullDeps)
package/src/lib/errors.ts CHANGED
@@ -14,7 +14,7 @@ export class CliError extends Error {
14
14
  * Authentication error
15
15
  */
16
16
  export class AuthError extends CliError {
17
- constructor (message: string = 'Authentication required. Please run "sealos login" first.') {
17
+ constructor (message: string = 'Authentication required. Please run "sealos-cli login" first.') {
18
18
  super(message, 1)
19
19
  this.name = 'AuthError'
20
20
  }
package/src/lib/output.ts CHANGED
@@ -10,12 +10,11 @@ export function outputJson (data: any): void {
10
10
  }
11
11
 
12
12
  /**
13
- * Output YAML format
13
+ * Output YAML format.
14
+ * Generic YAML output is not part of the v1 command surface.
14
15
  */
15
- export function outputYaml (data: any): void {
16
- // TODO: Use yaml library to implement
17
- console.log('YAML output not implemented yet')
18
- console.log(data)
16
+ export function outputYaml (_data: any): never {
17
+ throw new Error('YAML output is not supported in the v1 release. Use json or table output.')
19
18
  }
20
19
 
21
20
  /**
@@ -87,7 +86,7 @@ export function spinner (text: string): Ora {
87
86
  * Confirmation prompt
88
87
  */
89
88
  export async function confirm (message: string): Promise<boolean> {
90
- // TODO: Use inquirer or other interactive library
89
+ // Non-interactive default for v1. Replace with a prompt implementation when needed.
91
90
  console.log(chalk.yellow('?'), message)
92
91
  return true
93
92
  }
package/src/main.ts CHANGED
@@ -3,32 +3,25 @@ import { Command } from 'commander'
3
3
  import { registerAuthCommands } from './commands/auth/index.ts'
4
4
  import { createWorkspaceCommand } from './commands/workspace/index.ts'
5
5
  import { createDevboxCommand } from './commands/devbox/index.ts'
6
- import { createS3Command } from './commands/s3/index.ts'
7
6
  import { createDatabaseCommand } from './commands/database/index.ts'
8
7
  import { createTemplateCommand } from './commands/template/index.ts'
9
- import { createQuotaCommand } from './commands/quota/index.ts'
10
- import { createAppCommand } from './commands/app/index.ts'
11
- import { createConfigCommand } from './commands/config/index.ts'
12
8
  import { handleError } from './lib/errors.ts'
9
+ import packageJson from '../package.json' with { type: 'json' }
13
10
 
14
11
  export function createProgram (): Command {
15
12
  const program = new Command()
16
13
 
17
14
  program
18
- .name('sealos')
19
- .description('Official CLI tool for Sealos Cloud - Manage devbox, applications, databases, and object storage')
20
- .version('0.0.1')
15
+ .name('sealos-cli')
16
+ .description('Official CLI tool for Sealos Cloud - Manage devbox, databases, templates, auth, and workspaces')
17
+ .version(packageJson.version)
21
18
 
22
19
  // Register all command modules
23
20
  registerAuthCommands(program)
24
21
  program.addCommand(createWorkspaceCommand())
25
22
  program.addCommand(createDevboxCommand())
26
- program.addCommand(createS3Command())
27
23
  program.addCommand(createDatabaseCommand())
28
24
  program.addCommand(createTemplateCommand())
29
- program.addCommand(createQuotaCommand())
30
- program.addCommand(createAppCommand())
31
- program.addCommand(createConfigCommand())
32
25
 
33
26
  return program
34
27
  }
@@ -1,17 +1,5 @@
1
1
  // Core type definitions
2
2
 
3
- export interface SealosConfig {
4
- currentContext: string
5
- contexts: Context[]
6
- }
7
-
8
- export interface Context {
9
- name: string
10
- host: string
11
- token: string
12
- workspace: string
13
- }
14
-
15
3
  export interface DevboxConfig {
16
4
  name?: string
17
5
  template: string
@@ -1,54 +0,0 @@
1
- import { Command } from 'commander'
2
- import { readConfig, setConfigValue, getConfigValue } from '../../lib/config.ts'
3
- import { success, outputJson } from '../../lib/output.ts'
4
- import { handleError } from '../../lib/errors.ts'
5
-
6
- export function createConfigCommand (): Command {
7
- const configCmd = new Command('config')
8
- .description('Manage CLI configuration')
9
-
10
- configCmd
11
- .command('set')
12
- .description('Set a config value')
13
- .argument('<key>', 'Config key')
14
- .argument('<value>', 'Config value')
15
- .action(async (key, value) => {
16
- try {
17
- setConfigValue(key, value)
18
- success(`Config ${key} set to ${value}`)
19
- } catch (error) {
20
- handleError(error)
21
- }
22
- })
23
-
24
- configCmd
25
- .command('get')
26
- .description('Get a config value')
27
- .argument('<key>', 'Config key')
28
- .action(async (key) => {
29
- try {
30
- const value = getConfigValue(key)
31
- if (value) {
32
- console.log(value)
33
- } else {
34
- console.log(`Config key "${key}" not found`)
35
- }
36
- } catch (error) {
37
- handleError(error)
38
- }
39
- })
40
-
41
- configCmd
42
- .command('list')
43
- .description('List all config values')
44
- .action(async () => {
45
- try {
46
- const config = readConfig()
47
- outputJson(config)
48
- } catch (error) {
49
- handleError(error)
50
- }
51
- })
52
-
53
- return configCmd
54
- }
package/src/lib/api.ts DELETED
@@ -1,83 +0,0 @@
1
- import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'
2
- import { getCurrentContext } from './config.ts'
3
-
4
- /**
5
- * API client base class
6
- */
7
- export class ApiClient {
8
- private client: AxiosInstance
9
-
10
- constructor (baseURL?: string) {
11
- const context = getCurrentContext()
12
-
13
- this.client = axios.create({
14
- baseURL: baseURL || context?.host || '',
15
- timeout: 30000,
16
- headers: {
17
- 'Content-Type': 'application/json'
18
- }
19
- })
20
-
21
- // Request interceptor - add auth token
22
- this.client.interceptors.request.use(
23
- (config) => {
24
- const context = getCurrentContext()
25
- if (context?.token) {
26
- config.headers.Authorization = `Bearer ${context.token}`
27
- }
28
-
29
- // Support KUBECONFIG environment variable
30
- const kubeconfig = process.env.KUBECONFIG
31
- if (kubeconfig) {
32
- config.headers['X-Kubeconfig'] = kubeconfig
33
- }
34
-
35
- return config
36
- },
37
- async (error) => await Promise.reject(error)
38
- )
39
-
40
- // Response interceptor - unified error handling
41
- this.client.interceptors.response.use(
42
- (response) => response,
43
- async (error) => {
44
- if (error.response?.status === 401) {
45
- throw new Error('Authentication failed. Please run "sealos login" first.')
46
- }
47
- return await Promise.reject(error)
48
- }
49
- )
50
- }
51
-
52
- async get<T = any> (url: string, config?: AxiosRequestConfig): Promise<T> {
53
- const response = await this.client.get<T>(url, config)
54
- return response.data
55
- }
56
-
57
- async post<T = any> (url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
58
- const response = await this.client.post<T>(url, data, config)
59
- return response.data
60
- }
61
-
62
- async put<T = any> (url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
63
- const response = await this.client.put<T>(url, data, config)
64
- return response.data
65
- }
66
-
67
- async delete<T = any> (url: string, config?: AxiosRequestConfig): Promise<T> {
68
- const response = await this.client.delete<T>(url, config)
69
- return response.data
70
- }
71
-
72
- async patch<T = any> (url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
73
- const response = await this.client.patch<T>(url, data, config)
74
- return response.data
75
- }
76
- }
77
-
78
- /**
79
- * Create API client instance
80
- */
81
- export function createApiClient (baseURL?: string): ApiClient {
82
- return new ApiClient(baseURL)
83
- }