sealos-cli 1.1.1 → 1.1.2

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.
@@ -33,6 +33,7 @@ interface DevboxUpdateOptions {
33
33
  cpu?: string
34
34
  memory?: string
35
35
  port: string[]
36
+ output: string
36
37
  }
37
38
 
38
39
  interface DevboxReleaseOptions {
@@ -40,6 +41,7 @@ interface DevboxReleaseOptions {
40
41
  description?: string
41
42
  execCommand?: string
42
43
  noStart?: boolean
44
+ output: string
43
45
  }
44
46
 
45
47
  function formatValue (value: unknown): string {
@@ -398,7 +400,7 @@ export function createDevboxCommand (): Command {
398
400
  devboxCmd
399
401
  .command('list')
400
402
  .description('List all devboxes')
401
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
403
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
402
404
  .action(withAuth({ spinnerText: 'Loading devboxes...' }, async (ctx, options: { output: string }) => {
403
405
  const client = createDevboxClient()
404
406
  const { data, error, response } = await client.GET('/devbox', {
@@ -427,7 +429,7 @@ export function createDevboxCommand (): Command {
427
429
  .option('--env <NAME=VALUE>', 'Environment variable', collectOption, [] as string[])
428
430
  .option('--secret-env <NAME=SECRET:KEY>', 'Environment variable from secret', collectOption, [] as string[])
429
431
  .option('--autostart', 'Auto start devbox after creation')
430
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
432
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
431
433
  .action(withAuth({ spinnerText: 'Creating devbox...' }, async (ctx, options: DevboxCreateOptions & { output: string }) => {
432
434
  const client = createDevboxClient()
433
435
  const { data, error, response } = await client.POST('/devbox', {
@@ -449,7 +451,7 @@ export function createDevboxCommand (): Command {
449
451
  devboxCmd
450
452
  .command('get <name>')
451
453
  .description('Get devbox details')
452
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
454
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
453
455
  .action(withAuth({ spinnerText: 'Loading devbox...' }, async (ctx, name: string, options: { output: string }) => {
454
456
  const client = createDevboxClient()
455
457
  const { data, error, response } = await client.GET('/devbox/{name}', {
@@ -475,6 +477,7 @@ export function createDevboxCommand (): Command {
475
477
  .option('--cpu <cpu>', 'CPU cores')
476
478
  .option('--memory <memory>', 'Memory in GB')
477
479
  .option('--port <spec>', 'Port spec. Existing ports can include portName=...', collectOption, [] as string[])
480
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
478
481
  .action(withAuth({ spinnerText: 'Updating devbox...' }, async (ctx, name: string, options: DevboxUpdateOptions) => {
479
482
  const client = createDevboxClient()
480
483
  const { error, response } = await client.PATCH('/devbox/{name}', {
@@ -486,6 +489,17 @@ export function createDevboxCommand (): Command {
486
489
  })
487
490
 
488
491
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
492
+ if (options.output === 'json') {
493
+ ctx.spinner.stop()
494
+ outputJson({
495
+ success: true,
496
+ action: 'update',
497
+ resource: 'devbox',
498
+ name,
499
+ status: 'requested'
500
+ })
501
+ return
502
+ }
489
503
  ctx.spinner.succeed(`Devbox "${name}" update requested`)
490
504
  }))
491
505
 
@@ -493,7 +507,8 @@ export function createDevboxCommand (): Command {
493
507
  .command('delete <name>')
494
508
  .description('Delete a devbox')
495
509
  .alias('rm')
496
- .action(withAuth({ spinnerText: 'Deleting devbox...' }, async (ctx, name: string) => {
510
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
511
+ .action(withAuth({ spinnerText: 'Deleting devbox...' }, async (ctx, name: string, options: { output: string }) => {
497
512
  const client = createDevboxClient()
498
513
  const { error, response } = await client.DELETE('/devbox/{name}', {
499
514
  headers: ctx.auth,
@@ -503,6 +518,17 @@ export function createDevboxCommand (): Command {
503
518
  })
504
519
 
505
520
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
521
+ if (options.output === 'json') {
522
+ ctx.spinner.stop()
523
+ outputJson({
524
+ success: true,
525
+ action: 'delete',
526
+ resource: 'devbox',
527
+ name,
528
+ status: 'deleted'
529
+ })
530
+ return
531
+ }
506
532
  ctx.spinner.succeed(`Devbox "${name}" deleted`)
507
533
  }))
508
534
 
@@ -520,26 +546,40 @@ export function createDevboxCommand (): Command {
520
546
 
521
547
  if (action.alias) command.alias(action.alias)
522
548
 
523
- command.action(withAuth({ spinnerText: action.spinnerText }, async (ctx, name: string) => {
524
- const client = createDevboxClient()
525
- const { error, response } = await client.POST(action.endpoint, {
526
- headers: ctx.auth,
527
- params: {
528
- path: { name }
529
- },
530
- body: {}
531
- } as any)
532
-
533
- if (error) throw mapApiError(response.status, error as ApiErrorBody)
534
- ctx.spinner.succeed(`Devbox "${name}" ${action.done}`)
535
- }))
549
+ command
550
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
551
+ .action(withAuth({ spinnerText: action.spinnerText }, async (ctx, name: string, options: { output: string }) => {
552
+ const client = createDevboxClient()
553
+ const { error, response } = await client.POST(action.endpoint, {
554
+ headers: ctx.auth,
555
+ params: {
556
+ path: { name }
557
+ },
558
+ body: {}
559
+ } as any)
560
+
561
+ if (error) throw mapApiError(response.status, error as ApiErrorBody)
562
+ if (options.output === 'json') {
563
+ ctx.spinner.stop()
564
+ outputJson({
565
+ success: true,
566
+ action: action.name,
567
+ resource: 'devbox',
568
+ name,
569
+ status: 'requested'
570
+ })
571
+ return
572
+ }
573
+ ctx.spinner.succeed(`Devbox "${name}" ${action.done}`)
574
+ }))
536
575
  }
537
576
 
538
577
  devboxCmd
539
578
  .command('autostart <name>')
540
579
  .description('Configure devbox autostart')
541
580
  .option('--exec-command <command>', 'Command to execute when the devbox starts')
542
- .action(withAuth({ spinnerText: 'Configuring autostart...' }, async (ctx, name: string, options: { execCommand?: string }) => {
581
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
582
+ .action(withAuth({ spinnerText: 'Configuring autostart...' }, async (ctx, name: string, options: { execCommand?: string; output: string }) => {
543
583
  const client = createDevboxClient()
544
584
  const body = options.execCommand ? { execCommand: options.execCommand } : {}
545
585
  const { error, response } = await client.POST('/devbox/{name}/autostart', {
@@ -551,6 +591,18 @@ export function createDevboxCommand (): Command {
551
591
  })
552
592
 
553
593
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
594
+ if (options.output === 'json') {
595
+ ctx.spinner.stop()
596
+ outputJson({
597
+ success: true,
598
+ action: 'autostart',
599
+ resource: 'devbox',
600
+ name,
601
+ execCommand: options.execCommand ?? null,
602
+ status: 'configured'
603
+ })
604
+ return
605
+ }
554
606
  ctx.spinner.succeed(`Autostart configured for "${name}"`)
555
607
  }))
556
608
 
@@ -560,7 +612,7 @@ export function createDevboxCommand (): Command {
560
612
  .option('--start <timestamp>', 'Start timestamp in seconds or milliseconds')
561
613
  .option('--end <timestamp>', 'End timestamp in seconds or milliseconds')
562
614
  .option('--step <step>', 'Sampling interval, e.g. 2m')
563
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
615
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
564
616
  .action(withAuth({ spinnerText: 'Loading monitor data...' }, async (
565
617
  ctx,
566
618
  name: string,
@@ -592,7 +644,7 @@ export function createDevboxCommand (): Command {
592
644
  devboxCmd
593
645
  .command('templates')
594
646
  .description('List available devbox templates')
595
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
647
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
596
648
  .action(withAuth({ spinnerText: 'Loading devbox templates...' }, async (ctx, options: { output: string }) => {
597
649
  const client = createDevboxClient()
598
650
  const { data, error, response } = await client.GET('/devbox/templates', {
@@ -616,7 +668,7 @@ export function createDevboxCommand (): Command {
616
668
  releasesCommand
617
669
  .command('list <name>')
618
670
  .description('List devbox releases')
619
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
671
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
620
672
  .action(withAuth({ spinnerText: 'Loading releases...' }, async (ctx, name: string, options: { output: string }) => {
621
673
  const client = createDevboxClient()
622
674
  const { data, error, response } = await client.GET('/devbox/{name}/releases', {
@@ -643,6 +695,7 @@ export function createDevboxCommand (): Command {
643
695
  .option('--description <description>', 'Release description')
644
696
  .option('--exec-command <command>', 'Autostart command after release restart')
645
697
  .option('--no-start', 'Keep devbox stopped after the release build completes')
698
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
646
699
  .action(withAuth({ spinnerText: 'Creating release...' }, async (ctx, name: string, options: DevboxReleaseOptions) => {
647
700
  const client = createDevboxClient()
648
701
  const { data, error, response } = await client.POST('/devbox/{name}/releases', {
@@ -654,6 +707,11 @@ export function createDevboxCommand (): Command {
654
707
  })
655
708
 
656
709
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
710
+ if (options.output === 'json') {
711
+ ctx.spinner.stop()
712
+ outputJson(data)
713
+ return
714
+ }
657
715
  ctx.spinner.succeed(`Release "${options.tag}" accepted for "${data.name}" (${data.status})`)
658
716
  }))
659
717
 
@@ -661,7 +719,8 @@ export function createDevboxCommand (): Command {
661
719
  .command('delete <name> <tag>')
662
720
  .alias('rm')
663
721
  .description('Delete a devbox release')
664
- .action(withAuth({ spinnerText: 'Deleting release...' }, async (ctx, name: string, tag: string) => {
722
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
723
+ .action(withAuth({ spinnerText: 'Deleting release...' }, async (ctx, name: string, tag: string, options: { output: string }) => {
665
724
  const client = createDevboxClient()
666
725
  const { error, response } = await client.DELETE('/devbox/{name}/releases/{tag}', {
667
726
  headers: ctx.auth,
@@ -671,13 +730,26 @@ export function createDevboxCommand (): Command {
671
730
  })
672
731
 
673
732
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
733
+ if (options.output === 'json') {
734
+ ctx.spinner.stop()
735
+ outputJson({
736
+ success: true,
737
+ action: 'delete',
738
+ resource: 'devbox-release',
739
+ name,
740
+ tag,
741
+ status: 'deleted'
742
+ })
743
+ return
744
+ }
674
745
  ctx.spinner.succeed(`Release "${tag}" deleted for "${name}"`)
675
746
  }))
676
747
 
677
748
  releasesCommand
678
749
  .command('deploy <name> <tag>')
679
750
  .description('Deploy a release to AppLaunchpad')
680
- .action(withAuth({ spinnerText: 'Deploying release...' }, async (ctx, name: string, tag: string) => {
751
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
752
+ .action(withAuth({ spinnerText: 'Deploying release...' }, async (ctx, name: string, tag: string, options: { output: string }) => {
681
753
  const client = createDevboxClient()
682
754
  const { error, response } = await client.POST('/devbox/{name}/releases/{tag}/deploy', {
683
755
  headers: ctx.auth,
@@ -687,13 +759,25 @@ export function createDevboxCommand (): Command {
687
759
  })
688
760
 
689
761
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
762
+ if (options.output === 'json') {
763
+ ctx.spinner.stop()
764
+ outputJson({
765
+ success: true,
766
+ action: 'deploy',
767
+ resource: 'devbox-release',
768
+ name,
769
+ tag,
770
+ status: 'deployed'
771
+ })
772
+ return
773
+ }
690
774
  ctx.spinner.succeed(`Release "${tag}" deployed for "${name}"`)
691
775
  }))
692
776
 
693
777
  devboxCmd
694
778
  .command('deployments <name>')
695
779
  .description('List deployed applications from a devbox')
696
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
780
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
697
781
  .action(withAuth({ spinnerText: 'Loading deployments...' }, async (ctx, name: string, options: { output: string }) => {
698
782
  const client = createDevboxClient()
699
783
  const { data, error, response } = await client.GET('/devbox/{name}/deployments', {
@@ -12,6 +12,7 @@ interface TemplateDeployOptions {
12
12
  yaml?: string
13
13
  set: string[]
14
14
  dryRun?: boolean
15
+ output: string
15
16
  }
16
17
 
17
18
  export type TemplateDeployMode = 'catalog' | 'raw'
@@ -208,6 +209,11 @@ export function createTemplateCommand (): Command {
208
209
 
209
210
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
210
211
 
212
+ if (deployOptions.output === 'json') {
213
+ ctx.spinner.stop()
214
+ outputJson(data)
215
+ return
216
+ }
211
217
  ctx.spinner.succeed(`Instance "${data.name}" created successfully`)
212
218
  printInstanceResult(data, { template: catalogTemplate })
213
219
  return
@@ -224,6 +230,12 @@ export function createTemplateCommand (): Command {
224
230
 
225
231
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
226
232
 
233
+ if (deployOptions.output === 'json') {
234
+ ctx.spinner.stop()
235
+ outputJson(data)
236
+ return
237
+ }
238
+
227
239
  if (deployOptions.dryRun) {
228
240
  ctx.spinner.succeed('Raw template validation passed; no resources were created')
229
241
  printInstanceResult(data, { raw: true, dryRun: true })
@@ -240,7 +252,7 @@ export function createTemplateCommand (): Command {
240
252
  .description('List available templates')
241
253
  .option('-c, --category <category>', 'Filter by category')
242
254
  .option('-l, --language <language>', 'Language code (for example: en, zh)')
243
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
255
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
244
256
  .action(withErrorHandling({ spinnerText: 'Loading templates...' }, async (ctx, options: { category?: string; language?: string; output: string }) => {
245
257
  const client = createTemplateClient()
246
258
  const { data, error, response } = await client.GET('/templates', {
@@ -281,7 +293,7 @@ export function createTemplateCommand (): Command {
281
293
  .alias('describe')
282
294
  .description('Get template details')
283
295
  .option('-l, --language <language>', 'Language code (for example: en, zh)')
284
- .option('-o, --output <format>', 'Output format (json|table)', 'table')
296
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
285
297
  .action(withErrorHandling({ spinnerText: 'Loading template...' }, async (ctx, name: string, options: { language?: string; output: string }) => {
286
298
  const client = createTemplateClient()
287
299
  const { data, error, response } = await client.GET('/templates/{name}', {
@@ -338,7 +350,8 @@ export function createTemplateCommand (): Command {
338
350
  .command('delete <instance>')
339
351
  .alias('rm')
340
352
  .description('Delete a deployed template instance')
341
- .action(withAuth({ spinnerText: 'Deleting template instance...' }, async (ctx, instance: string) => {
353
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
354
+ .action(withAuth({ spinnerText: 'Deleting template instance...' }, async (ctx, instance: string, options: { output: string }) => {
342
355
  const client = createTemplateClient()
343
356
  const { error, response } = await client.DELETE('/templates/instances/{instanceName}', {
344
357
  headers: ctx.auth,
@@ -349,6 +362,17 @@ export function createTemplateCommand (): Command {
349
362
 
350
363
  if (error) throw mapApiError(response.status, error as ApiErrorBody)
351
364
 
365
+ if (options.output === 'json') {
366
+ ctx.spinner.stop()
367
+ outputJson({
368
+ success: true,
369
+ action: 'delete',
370
+ resource: 'template-instance',
371
+ instance,
372
+ status: 'deleted'
373
+ })
374
+ return
375
+ }
352
376
  ctx.spinner.succeed(`Instance "${instance}" deleted`)
353
377
  }))
354
378
 
@@ -361,6 +385,7 @@ export function createTemplateCommand (): Command {
361
385
  .option('--yaml <yaml>', 'Template YAML string')
362
386
  .option('--set <KEY=VALUE...>', 'Set template arguments', (val: string, prev: string[]) => [...prev, val], [] as string[])
363
387
  .option('--dry-run', 'Validate raw template YAML without creating resources')
388
+ .option('-o, --output <format>', 'Output format (json|table)', 'json')
364
389
  .addHelpText('after', `
365
390
  Examples:
366
391
  Catalog:
@@ -12,7 +12,7 @@ export function createWorkspaceCommand (): Command {
12
12
  .command('switch')
13
13
  .description('Switch to another workspace')
14
14
  .argument('<namespace>', 'Workspace id, uid, or team name')
15
- .option('-o, --output <format>', 'Output format: json, table', 'table')
15
+ .option('-o, --output <format>', 'Output format: json, table', 'json')
16
16
  .action(async (namespace, options) => {
17
17
  try {
18
18
  const result = await switchWorkspace(namespace)
@@ -30,7 +30,7 @@ export function createWorkspaceCommand (): Command {
30
30
  workspaceCmd
31
31
  .command('list')
32
32
  .description('List all workspaces')
33
- .option('-o, --output <format>', 'Output format: json, table', 'table')
33
+ .option('-o, --output <format>', 'Output format: json, table', 'json')
34
34
  .action(async (options) => {
35
35
  try {
36
36
  const result = await listWorkspaces()
@@ -58,7 +58,7 @@ export function createWorkspaceCommand (): Command {
58
58
  workspaceCmd
59
59
  .command('current')
60
60
  .description('Show current workspace')
61
- .option('-o, --output <format>', 'Output format: json, table', 'table')
61
+ .option('-o, --output <format>', 'Output format: json, table', 'json')
62
62
  .action(async (options) => {
63
63
  try {
64
64
  const authInfo = getAuthInfo()
package/src/lib/auth.ts CHANGED
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node
3
3
  import { homedir, platform } from 'node:os'
4
4
  import { join } from 'node:path'
5
5
  import type { SealosAuthData, SealosWorkspace } from '../types/index.ts'
6
+ import { AuthError } from './errors.ts'
6
7
 
7
8
  export const SEALOS_AUTH_CLIENT_ID = 'af993c98-d19d-4bdc-b338-79b80dc4f8bf'
8
9
  export const DEFAULT_SEALOS_REGION = 'https://usw-1.sealos.io'
@@ -64,6 +65,11 @@ interface KubeconfigResponse {
64
65
  }
65
66
  }
66
67
 
68
+ interface SealosApiEnvelope {
69
+ code?: number
70
+ message?: string
71
+ }
72
+
67
73
  export interface LoginResult {
68
74
  kubeconfig_path?: string
69
75
  region: string
@@ -236,7 +242,17 @@ export function getAuthInfo (deps: AuthDependencies = {}): AuthInfo {
236
242
  }
237
243
 
238
244
  async function parseResponse<T> (res: Response): Promise<T> {
239
- return await res.json() as T
245
+ const body = await res.json() as T & SealosApiEnvelope
246
+ if (body && typeof body === 'object' && typeof body.code === 'number' && ![0, 200].includes(body.code)) {
247
+ const message = body.message || `Sealos API request failed with code ${body.code}`
248
+ if (body.code === 401) {
249
+ throw new AuthError(`Authentication expired. Please run "sealos-cli login" again. (${message})`)
250
+ }
251
+
252
+ throw new Error(message)
253
+ }
254
+
255
+ return body as T
240
256
  }
241
257
 
242
258
  async function readErrorBody (res: Response): Promise<string> {