1+ import { ConfigIO , serializeResult } from '../../../lib' ;
12import { getErrorMessage } from '../../errors' ;
3+ import { withCommandRunTelemetry } from '../../telemetry/cli-command-run.js' ;
24import { COMMAND_DESCRIPTIONS } from '../../tui/copy' ;
35import { requireProject , requireTTY } from '../../tui/guards' ;
46import { DeployScreen } from '../../tui/screens/deploy/DeployScreen' ;
57import { handleDeploy } from './actions' ;
6- import { createSpinnerProgress } from './progress ' ;
7- import type { DeployOptions } from './types ' ;
8+ import type { DeployOptions , DeployResult } from './types ' ;
9+ import { DEFAULT_DEPLOY_ATTRS , computeDeployAttrs } from './utils ' ;
810import { validateDeployOptions } from './validate' ;
911import type { Command } from '@commander-js/extra-typings' ;
1012import { Text , render } from 'ink' ;
1113import React from 'react' ;
1214
15+ const SPINNER_FRAMES = [ '⠋' , '⠙' , '⠹' , '⠸' , '⠼' , '⠴' , '⠦' , '⠧' , '⠇' , '⠏' ] ;
16+
1317function handleDeployTUI ( options : { autoConfirm ?: boolean ; diffMode ?: boolean } = { } ) : void {
1418 requireProject ( ) ;
1519
@@ -37,8 +41,69 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
3741 process . exit ( 1 ) ;
3842 }
3943
40- const progressUtil = options . progress ? createSpinnerProgress ( ) : undefined ;
41- const onProgress = progressUtil ?. onProgress ;
44+ // Compute attrs upfront from project spec (available before deploy)
45+ const mode = options . diff ? 'diff' : options . plan ? 'dry-run' : 'deploy' ;
46+ const attrs = await new ConfigIO ( )
47+ . readProjectSpec ( )
48+ . then ( spec => computeDeployAttrs ( spec , mode ) )
49+ . catch ( ( ) => ( { ...DEFAULT_DEPLOY_ATTRS , mode } ) as const ) ;
50+
51+ const { deployResult } = await withCommandRunTelemetry ( 'deploy' , attrs , async ( ) => {
52+ const result = await executeDeploy ( options ) . catch (
53+ ( e ) : DeployResult => ( { success : false , error : e instanceof Error ? e : new Error ( getErrorMessage ( e ) ) } )
54+ ) ;
55+ if ( ! result . success ) {
56+ return { success : false as const , error : result . error , deployResult : result } ;
57+ }
58+ return { success : true as const , deployResult : result } ;
59+ } ) ;
60+
61+ // ALL output happens here, after telemetry
62+ if ( ! deployResult . success ) {
63+ if ( options . json ) {
64+ console . log ( JSON . stringify ( serializeResult ( deployResult ) ) ) ;
65+ } else {
66+ console . error ( deployResult . error . message ) ;
67+ if ( deployResult . logPath ) {
68+ console . error ( `Log: ${ deployResult . logPath } ` ) ;
69+ }
70+ }
71+ process . exit ( 1 ) ;
72+ }
73+
74+ printDeployResult ( deployResult , options ) ;
75+
76+ if ( deployResult . postDeployWarnings && deployResult . postDeployWarnings . length > 0 ) {
77+ process . exit ( 2 ) ;
78+ }
79+ process . exit ( 0 ) ;
80+ }
81+
82+ async function executeDeploy ( options : DeployOptions ) : Promise < DeployResult > {
83+ let spinner : NodeJS . Timeout | undefined ;
84+
85+ // Progress callback for --progress mode
86+ const onProgress = options . progress
87+ ? ( step : string , status : 'start' | 'success' | 'error' ) => {
88+ if ( spinner ) {
89+ clearInterval ( spinner ) ;
90+ process . stdout . write ( '\r\x1b[K' ) ; // Clear line
91+ }
92+
93+ if ( status === 'start' ) {
94+ let i = 0 ;
95+ process . stdout . write ( `${ SPINNER_FRAMES [ 0 ] } ${ step } ...` ) ;
96+ spinner = setInterval ( ( ) => {
97+ i = ( i + 1 ) % SPINNER_FRAMES . length ;
98+ process . stdout . write ( `\r${ SPINNER_FRAMES [ i ] } ${ step } ...` ) ;
99+ } , 80 ) ;
100+ } else if ( status === 'success' ) {
101+ console . log ( `✓ ${ step } ` ) ;
102+ } else {
103+ console . log ( `✗ ${ step } ` ) ;
104+ }
105+ }
106+ : undefined ;
42107
43108 const onResourceEvent = options . verbose
44109 ? ( message : string ) => {
@@ -56,57 +121,57 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
56121 onResourceEvent,
57122 } ) ;
58123
59- progressUtil ?. cleanup ( ) ;
124+ if ( spinner ) {
125+ clearInterval ( spinner ) ;
126+ process . stdout . write ( '\r\x1b[K' ) ;
127+ }
128+
129+ return result ;
130+ }
60131
132+ function printDeployResult ( result : DeployResult & { success : true } , options : DeployOptions ) : void {
61133 if ( options . json ) {
62134 console . log ( JSON . stringify ( result ) ) ;
63- } else if ( result . success ) {
64- if ( options . diff ) {
65- console . log ( `\n✓ Diff complete for '${ result . targetName } ' (stack: ${ result . stackName } )` ) ;
66- } else if ( options . plan ) {
67- console . log ( `\n✓ Dry run complete for '${ result . targetName } ' (stack: ${ result . stackName } )` ) ;
68- console . log ( '\nRun `agentcore deploy` to deploy.' ) ;
69- } else {
70- console . log ( `\n✓ Deployed to '${ result . targetName } ' (stack: ${ result . stackName } )` ) ;
135+ return ;
136+ }
71137
72- // Show stack outputs in non-JSON mode
73- if ( result . outputs && Object . keys ( result . outputs ) . length > 0 ) {
74- console . log ( '\nOutputs:' ) ;
75- for ( const [ key , value ] of Object . entries ( result . outputs ) ) {
76- console . log ( ` ${ key } : ${ value } ` ) ;
77- }
78- }
138+ if ( options . diff ) {
139+ console . log ( `\n✓ Diff complete for ' ${ result . targetName } ' (stack: ${ result . stackName } )` ) ;
140+ } else if ( options . plan ) {
141+ console . log ( `\n✓ Dry run complete for ' ${ result . targetName } ' (stack: ${ result . stackName } )` ) ;
142+ console . log ( '\nRun `agentcore deploy` to deploy.' ) ;
143+ } else {
144+ console . log ( `\n✓ Deployed to ' ${ result . targetName } ' (stack: ${ result . stackName } )` ) ;
79145
80- if ( result . postDeployWarnings && result . postDeployWarnings . length > 0 ) {
81- console . log ( '\n⚠ Post-deploy warnings:' ) ;
82- for ( const warning of result . postDeployWarnings ) {
83- console . log ( ` ${ warning } ` ) ;
84- }
146+ // Show stack outputs in non-JSON mode
147+ if ( result . outputs && Object . keys ( result . outputs ) . length > 0 ) {
148+ console . log ( '\nOutputs:' ) ;
149+ for ( const [ key , value ] of Object . entries ( result . outputs ) ) {
150+ console . log ( ` ${ key } : ${ value } ` ) ;
85151 }
152+ }
86153
87- if ( result . notes && result . notes . length > 0 ) {
88- for ( const note of result . notes ) {
89- console . log ( `\nNote: ${ note } ` ) ;
90- }
154+ if ( result . postDeployWarnings && result . postDeployWarnings . length > 0 ) {
155+ console . log ( '\n⚠ Post-deploy warnings:' ) ;
156+ for ( const warning of result . postDeployWarnings ) {
157+ console . log ( ` ${ warning } ` ) ;
91158 }
159+ }
92160
93- if ( result . nextSteps && result . nextSteps . length > 0 ) {
94- console . log ( `Next: ${ result . nextSteps . join ( ' | ' ) } ` ) ;
161+ if ( result . notes && result . notes . length > 0 ) {
162+ for ( const note of result . notes ) {
163+ console . log ( `\nNote: ${ note } ` ) ;
95164 }
96165 }
97166
98- if ( result . logPath ) {
99- console . log ( `\nLog: ${ result . logPath } ` ) ;
100- }
101- } else {
102- console . error ( result . error ) ;
103- if ( result . logPath ) {
104- console . error ( `Log: ${ result . logPath } ` ) ;
167+ if ( result . nextSteps && result . nextSteps . length > 0 ) {
168+ console . log ( `Next: ${ result . nextSteps . join ( ' | ' ) } ` ) ;
105169 }
106170 }
107171
108- const hasPostDeployWarnings = result . success && result . postDeployWarnings && result . postDeployWarnings . length > 0 ;
109- process . exit ( result . success ? ( hasPostDeployWarnings ? 2 : 0 ) : 1 ) ;
172+ if ( result . logPath ) {
173+ console . log ( `\nLog: ${ result . logPath } ` ) ;
174+ }
110175}
111176
112177export const registerDeploy = ( program : Command ) => {
0 commit comments