66import { Args , Flags , ux } from '@oclif/core' ;
77import { input , password , confirm , select } from '@inquirer/prompts' ;
88import { BaseCommand } from '@salesforce/b2c-tooling-sdk/cli' ;
9- import { DwJsonSource , type NormalizedConfig } from '@salesforce/b2c-tooling-sdk/config' ;
9+ import { DwJsonSource , createInstanceFromConfig , type NormalizedConfig } from '@salesforce/b2c-tooling-sdk/config' ;
10+ import { getActiveCodeVersion } from '@salesforce/b2c-tooling-sdk/operations/code' ;
1011import { withDocs } from '../../../i18n/index.js' ;
1112
1213/**
@@ -24,6 +25,19 @@ interface InstanceCreateResponse {
2425 */
2526type AuthType = 'basic' | 'both' | 'none' | 'oauth' ;
2627
28+ /**
29+ * Extract the hostname from a URL or return the input as-is if it's already a hostname.
30+ */
31+ function parseHostname ( input : string ) : string {
32+ const trimmed = input . trim ( ) ;
33+ try {
34+ const url = new URL ( trimmed ) ;
35+ return url . hostname ;
36+ } catch {
37+ return trimmed ;
38+ }
39+ }
40+
2741/**
2842 * Create a new B2C Commerce instance configuration.
2943 */
@@ -101,28 +115,24 @@ export default class SetupInstanceCreate extends BaseCommand<typeof SetupInstanc
101115 this . error ( `Instance "${ name } " already exists. Use a different name.` ) ;
102116 }
103117
104- // Get or prompt for hostname
118+ // Get or prompt for hostname (accepts a URL or plain hostname)
105119 let hostname = this . flags . hostname ;
106120 if ( ! hostname ) {
107121 if ( force ) {
108122 this . error ( 'Hostname is required in non-interactive mode. Use --hostname flag.' ) ;
109123 }
110124 hostname = await input ( {
111- message : 'Enter B2C instance hostname:' ,
125+ message : 'Enter B2C instance hostname or URL :' ,
112126 validate : ( v ) => ( v . trim ( ) ? true : 'Hostname is required' ) ,
113127 } ) ;
114128 }
129+ hostname = parseHostname ( hostname ) ;
115130
116131 // Build config
117132 const config : Partial < NormalizedConfig > = {
118133 hostname,
119134 } ;
120135
121- // Code version
122- if ( this . flags [ 'code-version' ] ) {
123- config . codeVersion = this . flags [ 'code-version' ] ;
124- }
125-
126136 // Handle authentication - in non-interactive mode, use provided flags
127137 if ( force ) {
128138 // Basic auth
@@ -137,18 +147,17 @@ export default class SetupInstanceCreate extends BaseCommand<typeof SetupInstanc
137147 // OAuth
138148 if ( this . flags [ 'client-id' ] ) {
139149 config . clientId = this . flags [ 'client-id' ] ;
140- if ( ! this . flags [ 'client-secret' ] ) {
141- this . error ( 'Client secret is required when client ID is provided in non-interactive mode.' ) ;
150+ if ( this . flags [ 'client-secret' ] ) {
151+ config . clientSecret = this . flags [ ' client-secret' ] ;
142152 }
143- config . clientSecret = this . flags [ 'client-secret' ] ;
144153 }
145154 } else {
146155 // Interactive mode - prompt for auth type and credentials
147156 const authType = await select < AuthType > ( {
148157 message : 'Configure authentication:' ,
149158 choices : [
150- { name : 'Basic (username/password)' , value : 'basic' } ,
151- { name : 'OAuth ( client credentials)' , value : 'oauth' } ,
159+ { name : 'WebDAV (username/password or access key )' , value : 'basic' } ,
160+ { name : 'API Client (OAuth client credentials)' , value : 'oauth' } ,
152161 { name : 'Both' , value : 'both' } ,
153162 { name : 'Skip for now' , value : 'none' } ,
154163 ] ,
@@ -163,11 +172,12 @@ export default class SetupInstanceCreate extends BaseCommand<typeof SetupInstanc
163172 validate : ( v ) => ( v . trim ( ) ? true : 'Username is required' ) ,
164173 } ) ) ;
165174
175+ const accessKeyUrl = `https://${ hostname } /on/demandware.store/Sites-Site/default/ViewAccessKeys-List` ;
166176 config . password =
167177 this . flags . password ||
168178 ( await password ( {
169- message : ' Enter WebDAV password:' ,
170- validate : ( v ) => ( v . trim ( ) ? true : 'Password is required' ) ,
179+ message : ` Enter WebDAV password or access key ( ${ accessKeyUrl } ):` ,
180+ validate : ( v ) => ( v . trim ( ) ? true : 'Password or access key is required' ) ,
171181 } ) ) ;
172182 }
173183
@@ -180,12 +190,45 @@ export default class SetupInstanceCreate extends BaseCommand<typeof SetupInstanc
180190 validate : ( v ) => ( v . trim ( ) ? true : 'Client ID is required' ) ,
181191 } ) ) ;
182192
183- config . clientSecret =
193+ const clientSecret =
184194 this . flags [ 'client-secret' ] ||
185195 ( await password ( {
186- message : 'Enter OAuth client secret:' ,
187- validate : ( v ) => ( v . trim ( ) ? true : 'Client secret is required' ) ,
196+ message : 'Enter OAuth client secret (leave blank for user auth):' ,
188197 } ) ) ;
198+
199+ if ( clientSecret . trim ( ) ) {
200+ config . clientSecret = clientSecret . trim ( ) ;
201+ }
202+ }
203+ }
204+
205+ // Code version - use flag, or try to detect via OCAPI if OAuth credentials are available
206+ if ( this . flags [ 'code-version' ] ) {
207+ config . codeVersion = this . flags [ 'code-version' ] ;
208+ } else if ( ! force ) {
209+ let detectedVersion : string | undefined ;
210+
211+ if ( config . clientId ) {
212+ try {
213+ const tempInstance = createInstanceFromConfig ( {
214+ hostname,
215+ clientId : config . clientId ,
216+ clientSecret : config . clientSecret ,
217+ } ) ;
218+ const activeVersion = await getActiveCodeVersion ( tempInstance ) ;
219+ detectedVersion = activeVersion ?. id ;
220+ } catch {
221+ // Detection failed - continue without a default
222+ }
223+ }
224+
225+ const codeVersion = await input ( {
226+ message : 'Enter code version:' ,
227+ default : detectedVersion ,
228+ } ) ;
229+
230+ if ( codeVersion . trim ( ) ) {
231+ config . codeVersion = codeVersion . trim ( ) ;
189232 }
190233 }
191234
0 commit comments