55 outro ,
66 select ,
77 text ,
8+ confirm ,
89 spinner ,
910 log ,
1011 isCancel ,
@@ -14,6 +15,7 @@ import chalk from 'chalk';
1415import { Command } from 'commander' ;
1516import degit from 'degit' ;
1617import { existsSync , readdirSync , readFileSync , writeFileSync } from 'fs' ;
18+ import { readFile } from 'fs/promises' ;
1719import path from 'path' ;
1820import { spawn } from 'child_process' ;
1921
@@ -185,65 +187,67 @@ function isExternalTemplate(template: string): boolean {
185187 ) ;
186188}
187189
188- function extractGithubUsername ( templateUrl : string ) : string | null {
189- const match = templateUrl . match (
190- / g i t h u b \. c o m \/ ( [ ^ \/ ] + ) /
191- ) ;
192- return match ?. [ 1 ] ?? null ;
190+ async function extractReferralCodeFromTemplate (
191+ templatePath : string
192+ ) : Promise < string | null > {
193+ const configFile = path . join ( templatePath , 'echo.config.json' ) ;
194+
195+ if ( ! existsSync ( configFile ) ) {
196+ return null ;
197+ }
198+
199+ try {
200+ const content = await readFile ( configFile , 'utf-8' ) ;
201+ const config = JSON . parse ( content ) ;
202+ return config . referralCode || config . echo ?. referralCode || null ;
203+ } catch {
204+ return null ;
205+ }
193206}
194207
195208async function registerTemplateReferral (
196209 appId : string ,
197- templateUrl : string
210+ templatePath : string ,
211+ apiKey : string
198212) : Promise < void > {
199- const githubUsername = extractGithubUsername ( templateUrl ) ;
200-
201- if ( ! githubUsername ) {
202- log . warn ( 'Could not extract GitHub username from template URL' ) ;
203- return ;
204- }
205-
206213 try {
207- log . step ( `Registering template referral for ${ githubUsername } ...` ) ;
208-
209- const response = await fetch (
210- `${ ECHO_BASE_URL } /api/v1/referrals/register-template` ,
211- {
212- method : 'POST' ,
213- headers : {
214- 'Content-Type' : 'application/json' ,
215- } ,
216- body : JSON . stringify ( {
217- appId,
218- githubUsername,
219- templateUrl,
220- } ) ,
221- }
222- ) ;
214+ const referralCode = await extractReferralCodeFromTemplate ( templatePath ) ;
223215
224- if ( ! response . ok ) {
225- const errorText = await response . text ( ) ;
226- log . warn ( `Referral registration failed: ${ response . status } - ${ errorText } ` ) ;
216+ if ( ! referralCode ) {
217+ log . info ( 'No referral code found in template echo.config.json' ) ;
227218 return ;
228219 }
229220
230- const result = ( await response . json ( ) ) as {
231- status ? : string ;
232- referrerUsername ? : string ;
233- message ? : string ;
234- } ;
235-
236- if ( result . status === 'registered' && result . referrerUsername ) {
237- log . success (
238- `Template creator ${ result . referrerUsername } registered as referrer`
221+ log . step ( `Found template referral code, applying...` ) ;
222+
223+ const response = await fetch ( `${ ECHO_BASE_URL } /api/v1/user/referral` , {
224+ method : 'POST' ,
225+ headers : {
226+ 'Content-Type' : 'application/json' ,
227+ Authorization : `Bearer ${ apiKey } ` ,
228+ } ,
229+ body : JSON . stringify ( {
230+ echoAppId : appId ,
231+ code : referralCode ,
232+ } ) ,
233+ } ) ;
234+
235+ if ( ! response . ok ) {
236+ const errorData = ( await response . json ( ) ) as { message ?: string } ;
237+ log . warn (
238+ `Referral code could not be applied: ${ errorData . message || 'Unknown error' } `
239239 ) ;
240- } else if ( result . status === 'skipped' ) {
241- log . info ( `Referral skipped: ${ result . message || 'Unknown reason' } ` ) ;
242- } else if ( result . status === 'not_found' ) {
243- log . info ( `Template creator ${ githubUsername } not found on Echo` ) ;
240+ return ;
241+ }
242+
243+ const result = ( await response . json ( ) ) as { success ?: boolean } ;
244+ if ( result . success ) {
245+ log . success ( 'Template referral code applied successfully' ) ;
244246 }
245247 } catch ( error ) {
246- log . warn ( `Referral registration error: ${ error instanceof Error ? error . message : 'Unknown error' } ` ) ;
248+ log . warn (
249+ `Referral registration error: ${ error instanceof Error ? error . message : 'Unknown error' } `
250+ ) ;
247251 }
248252}
249253
@@ -372,10 +376,6 @@ async function createApp(projectDir: string, options: CreateAppOptions) {
372376
373377 log . step ( `Using App ID: ${ appId } ` ) ;
374378
375- if ( isExternal ) {
376- await registerTemplateReferral ( appId , template ) ;
377- }
378-
379379 const absoluteProjectPath = path . resolve ( projectDir ) ;
380380
381381 // Check if directory already exists
@@ -436,7 +436,36 @@ async function createApp(projectDir: string, options: CreateAppOptions) {
436436
437437 log . step ( 'Configuring project files' ) ;
438438
439- // Update package.json with the name of the project
439+ if ( isExternal ) {
440+ const referralCode = await extractReferralCodeFromTemplate (
441+ absoluteProjectPath
442+ ) ;
443+
444+ if ( referralCode ) {
445+ const shouldApplyReferral = await confirm ( {
446+ message : 'This template includes a referral code. Apply it?' ,
447+ initialValue : true ,
448+ } ) ;
449+
450+ if ( ! isCancel ( shouldApplyReferral ) && shouldApplyReferral ) {
451+ const apiKey = await text ( {
452+ message : 'Enter your Echo API key:' ,
453+ placeholder : 'Your API key from https://echo.merit.systems/keys' ,
454+ validate : ( value : string ) => {
455+ if ( ! value . trim ( ) ) {
456+ return 'API key is required to apply referral code' ;
457+ }
458+ return ;
459+ } ,
460+ } ) ;
461+
462+ if ( ! isCancel ( apiKey ) ) {
463+ await registerTemplateReferral ( appId , absoluteProjectPath , apiKey ) ;
464+ }
465+ }
466+ }
467+ }
468+
440469 const packageJsonPath = path . join ( absoluteProjectPath , 'package.json' ) ;
441470 // Technically this is checked above, but good practice to check again
442471 if ( existsSync ( packageJsonPath ) ) {
0 commit comments