sealos-cli 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.
package/package.json ADDED
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "sealos-cli",
3
+ "version": "0.1.0",
4
+ "description": "Official CLI tool for Sealos Cloud - Manage devbox, applications, databases, and object storage",
5
+ "types": "dist/main.d.ts",
6
+ "type": "module",
7
+ "bin": {
8
+ "sealos-cli": "dist/bin/cli.cjs"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/main.d.ts",
14
+ "default": "./dist/main.mjs"
15
+ },
16
+ "require": {
17
+ "types": "./dist/main.d.cts",
18
+ "default": "./dist/main.cjs"
19
+ },
20
+ "default": "./dist/main.mjs"
21
+ },
22
+ "./dist/*": {
23
+ "types": "./dist/*.d.ts",
24
+ "import": "./dist/*.mjs",
25
+ "require": "./dist/*.cjs"
26
+ }
27
+ },
28
+ "engines": {
29
+ "node": ">=22.0.0"
30
+ },
31
+ "packageManager": "npm@8.4.0",
32
+ "files": [
33
+ "dist",
34
+ "src",
35
+ "bin"
36
+ ],
37
+ "scripts": {
38
+ "start": "node --import tsx src/bin/cli.ts",
39
+ "generate:api": "openapi-typescript src/docs/template_openapi.json -o src/generated/template.ts && openapi-typescript src/docs/database_openapi.json -o src/generated/database.ts",
40
+ "build": "npm run generate:api && tsc && tsup",
41
+ "lint": "eslint . && npm run lint:lockfile && npm run lint:markdown",
42
+ "lint:markdown": "npx -y markdownlint-cli@0.45.0 -c .github/.markdownlint.yml -i '.git' -i '__tests__' -i '.github' -i '.changeset' -i 'CODE_OF_CONDUCT.md' -i 'CHANGELOG.md' -i 'docs/**' -i 'node_modules' -i 'dist' '**/**.md' --fix",
43
+ "lint:fix": "eslint . --fix",
44
+ "lint:lockfile": "lockfile-lint --path package-lock.json --validate-https --allowed-hosts npm yarn",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "test:coverage": "vitest run --coverage",
48
+ "coverage:view": "open coverage/lcov-report/index.html",
49
+ "version": "changeset version",
50
+ "release": "changeset publish"
51
+ },
52
+ "author": {
53
+ "name": "zjy365",
54
+ "email": "3161362058@qq.com",
55
+ "url": "https://github.com/zjy365"
56
+ },
57
+ "publishConfig": {
58
+ "provenance": true,
59
+ "access": "public"
60
+ },
61
+ "license": "Apache-2.0",
62
+ "keywords": [
63
+ "sealos",
64
+ "cli",
65
+ "cloud",
66
+ "devbox",
67
+ "s3",
68
+ "database",
69
+ "kubernetes",
70
+ "deployment"
71
+ ],
72
+ "homepage": "https://github.com/zjy365/sealos-cli.git",
73
+ "bugs": {
74
+ "url": "https://github.com/zjy365/sealos-cli.git/issues"
75
+ },
76
+ "repository": {
77
+ "type": "git",
78
+ "url": "git+https://github.com/zjy365/sealos-cli.git"
79
+ },
80
+ "dependencies": {
81
+ "axios": "^1.15.0",
82
+ "chalk": "^5.2.0",
83
+ "commander": "^14.0.3",
84
+ "openapi-fetch": "^0.17.0",
85
+ "ora": "^9.3.0",
86
+ "table": "^6.8.1"
87
+ },
88
+ "devDependencies": {
89
+ "@changesets/changelog-github": "^0.5.0",
90
+ "@changesets/cli": "^2.27.7",
91
+ "@types/node": "^20.14.10",
92
+ "@vitest/coverage-v8": "^4.1.6",
93
+ "eslint": "^9.6.0",
94
+ "eslint-plugin-security": "^3.0.1",
95
+ "husky": "^9.0.11",
96
+ "lint-staged": "^15.2.7",
97
+ "lockfile-lint": "^4.14.0",
98
+ "neostandard": "^0.11.0",
99
+ "openapi-typescript": "^7.13.0",
100
+ "ts-node": "^10.9.2",
101
+ "tsup": "^8.1.0",
102
+ "tsx": "^4.19.4",
103
+ "typescript": "^5.5.3",
104
+ "validate-conventional-commit": "^1.0.4",
105
+ "vitest": "^4.1.6"
106
+ },
107
+ "lint-staged": {
108
+ "**/*.{js,json}": [
109
+ "npm run lint:fix"
110
+ ]
111
+ }
112
+ }
package/src/bin/cli.ts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runCLI } from '../main.ts'
3
+
4
+ runCLI()
@@ -0,0 +1,22 @@
1
+ import { Command } from 'commander'
2
+ import { handleError } from '../../lib/errors.ts'
3
+
4
+ export function createAppCommand (): Command {
5
+ const appCmd = new Command('app')
6
+ .description('Manage applications')
7
+
8
+ // TODO: 实现应用相关命令
9
+
10
+ appCmd
11
+ .command('list')
12
+ .description('List all applications')
13
+ .action(async () => {
14
+ try {
15
+ console.log('TODO: Implement app list')
16
+ } catch (error) {
17
+ handleError(error)
18
+ }
19
+ })
20
+
21
+ return appCmd
22
+ }
@@ -0,0 +1,124 @@
1
+ import { Command } from 'commander'
2
+ import { createLoginCommand } from './login.ts'
3
+ import { createLogoutCommand } from './logout.ts'
4
+ import { createWhoamiCommand } from './whoami.ts'
5
+ import { checkAuth, getAuthInfo, listWorkspaces, switchWorkspace } from '../../lib/auth.ts'
6
+ import { handleError, AuthError } from '../../lib/errors.ts'
7
+ import { outputJson, outputTable, success } from '../../lib/output.ts'
8
+
9
+ /**
10
+ * Register all authentication related commands
11
+ */
12
+ export function registerAuthCommands (program: Command): void {
13
+ program.addCommand(createLoginCommand())
14
+ program.addCommand(createLogoutCommand())
15
+ program.addCommand(createWhoamiCommand())
16
+ program.addCommand(createAuthCommand())
17
+ }
18
+
19
+ export function createAuthCommand (): Command {
20
+ const authCmd = new Command('auth')
21
+ .description('Manage Sealos authentication')
22
+
23
+ authCmd
24
+ .command('check')
25
+ .description('Check authentication status')
26
+ .option('-o, --output <format>', 'Output format: json, table', 'json')
27
+ .action(async (options) => {
28
+ try {
29
+ const status = checkAuth()
30
+ if (options.output === 'table') {
31
+ outputTable([
32
+ ['Field', 'Value'],
33
+ ['Authenticated', String(status.authenticated)],
34
+ ['Region', status.region || 'unknown'],
35
+ ['Workspace', status.workspace || 'unknown'],
36
+ ['Kubeconfig', status.kubeconfig_path || 'unknown']
37
+ ])
38
+ } else {
39
+ outputJson(status)
40
+ }
41
+ } catch (error) {
42
+ handleError(error)
43
+ }
44
+ })
45
+
46
+ authCmd
47
+ .command('info')
48
+ .description('Show current auth details')
49
+ .option('-o, --output <format>', 'Output format: json, table', 'json')
50
+ .action(async (options) => {
51
+ try {
52
+ const info = getAuthInfo()
53
+ if (!info.authenticated) {
54
+ throw new AuthError()
55
+ }
56
+
57
+ if (options.output === 'table') {
58
+ outputTable([
59
+ ['Field', 'Value'],
60
+ ['Authenticated', String(info.authenticated)],
61
+ ['Region', info.region || 'unknown'],
62
+ ['Auth Method', info.auth_method || 'unknown'],
63
+ ['Workspace', info.current_workspace?.id || info.workspace || 'unknown'],
64
+ ['Team', info.current_workspace?.teamName || 'unknown'],
65
+ ['Kubeconfig', info.kubeconfig_path || 'unknown'],
66
+ ['Authenticated At', info.authenticated_at || 'unknown']
67
+ ])
68
+ } else {
69
+ outputJson(info)
70
+ }
71
+ } catch (error) {
72
+ handleError(error)
73
+ }
74
+ })
75
+
76
+ authCmd
77
+ .command('list')
78
+ .description('List all workspaces')
79
+ .option('-o, --output <format>', 'Output format: json, table', 'table')
80
+ .action(async (options) => {
81
+ try {
82
+ const result = await listWorkspaces()
83
+ if (options.output === 'json') {
84
+ outputJson(result)
85
+ return
86
+ }
87
+
88
+ outputTable([
89
+ ['UID', 'ID', 'TEAM', 'ROLE', 'TYPE', 'CURRENT'],
90
+ ...result.workspaces.map(workspace => [
91
+ workspace.uid || '',
92
+ workspace.id || '',
93
+ workspace.teamName || '',
94
+ workspace.role || '',
95
+ workspace.nstype || '',
96
+ workspace.id === result.current ? '*' : ''
97
+ ])
98
+ ])
99
+ } catch (error) {
100
+ handleError(error)
101
+ }
102
+ })
103
+
104
+ authCmd
105
+ .command('switch')
106
+ .description('Switch workspace')
107
+ .argument('<namespace>', 'Workspace id, uid, or team name')
108
+ .option('-o, --output <format>', 'Output format: json, table', 'table')
109
+ .action(async (namespace, options) => {
110
+ try {
111
+ const result = await switchWorkspace(namespace)
112
+ if (options.output === 'json') {
113
+ outputJson(result)
114
+ return
115
+ }
116
+
117
+ success(`Switched to workspace: ${result.workspace.id || result.workspace.uid || namespace}`)
118
+ } catch (error) {
119
+ handleError(error)
120
+ }
121
+ })
122
+
123
+ return authCmd
124
+ }
@@ -0,0 +1,35 @@
1
+ import { Command } from 'commander'
2
+ import { loginWithDeviceFlow, loginWithToken } from '../../lib/auth.ts'
3
+ import { info, outputJson, success, warn } from '../../lib/output.ts'
4
+ import { handleError } from '../../lib/errors.ts'
5
+
6
+ export function createLoginCommand (): Command {
7
+ return new Command('login')
8
+ .description('Login to Sealos Cloud')
9
+ .argument('[region]', 'Sealos region URL (e.g., https://usw-1.sealos.io)')
10
+ .option('-t, --token <token>', 'Store a regional token without OAuth device login')
11
+ .option('-o, --output <format>', 'Output format: json, table', 'table')
12
+ .action(async (region, options) => {
13
+ try {
14
+ const result = options.token
15
+ ? await loginWithToken(region, options.token)
16
+ : await loginWithDeviceFlow(region)
17
+
18
+ if (options.token) {
19
+ warn('Token login is limited: kubeconfig is not generated. Use device login for full auth.')
20
+ }
21
+
22
+ if (options.output === 'json') {
23
+ outputJson(result)
24
+ } else {
25
+ success(`Logged in to ${result.region}`)
26
+ info(`Workspace: ${result.workspace}`)
27
+ if (result.kubeconfig_path) {
28
+ info(`Kubeconfig: ${result.kubeconfig_path}`)
29
+ }
30
+ }
31
+ } catch (error) {
32
+ handleError(error)
33
+ }
34
+ })
35
+ }
@@ -0,0 +1,23 @@
1
+ import { Command } from 'commander'
2
+ import { checkAuth, clearAuth } from '../../lib/auth.ts'
3
+ import { success, warn } from '../../lib/output.ts'
4
+ import { handleError } from '../../lib/errors.ts'
5
+
6
+ export function createLogoutCommand (): Command {
7
+ return new Command('logout')
8
+ .description('Logout from Sealos Cloud')
9
+ .action(async () => {
10
+ try {
11
+ const status = checkAuth()
12
+ if (!status.authenticated) {
13
+ warn('You are not logged in')
14
+ return
15
+ }
16
+
17
+ clearAuth()
18
+ success('Logged out from Sealos Cloud')
19
+ } catch (error) {
20
+ handleError(error)
21
+ }
22
+ })
23
+ }
@@ -0,0 +1,38 @@
1
+ import { Command } from 'commander'
2
+ import { getAuthInfo } from '../../lib/auth.ts'
3
+ import { outputJson, outputTable } from '../../lib/output.ts'
4
+ import { handleError, AuthError } from '../../lib/errors.ts'
5
+
6
+ export function createWhoamiCommand (): Command {
7
+ return new Command('whoami')
8
+ .description('Display current user information')
9
+ .option('-o, --output <format>', 'Output format: json, table', 'table')
10
+ .action(async (options) => {
11
+ try {
12
+ const authInfo = getAuthInfo()
13
+ if (!authInfo.authenticated) {
14
+ throw new AuthError()
15
+ }
16
+
17
+ if (options.output === 'json') {
18
+ outputJson(authInfo)
19
+ return
20
+ }
21
+
22
+ const data = [
23
+ ['Field', 'Value'],
24
+ ['Authenticated', 'true'],
25
+ ['Region', authInfo.region || 'unknown'],
26
+ ['Auth Method', authInfo.auth_method || 'unknown'],
27
+ ['Workspace', authInfo.current_workspace?.id || authInfo.workspace || 'unknown'],
28
+ ['Team', authInfo.current_workspace?.teamName || 'unknown'],
29
+ ['Kubeconfig', authInfo.kubeconfig_path || 'unknown'],
30
+ ['Authenticated At', authInfo.authenticated_at || 'unknown']
31
+ ]
32
+
33
+ outputTable(data)
34
+ } catch (error) {
35
+ handleError(error)
36
+ }
37
+ })
38
+ }
@@ -0,0 +1,54 @@
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
+ }