1
1
import { OptionValues } from 'commander'
2
2
3
- import { chalk , log } from '../../utils/command-helpers.js'
3
+ import { chalk , log , logAndThrowError } from '../../utils/command-helpers.js'
4
+ import { normalizeRepoUrl } from '../../utils/normalize-repo-url.js'
5
+ import { runGit } from '../../utils/run-git.js'
6
+ import { startSpinner } from '../../lib/spinner.js'
4
7
import BaseCommand from '../base-command.js'
5
8
9
+ interface MockHashDecodeResult {
10
+ siteId ?: string
11
+ repoUrl ?: string
12
+ targetDir ?: string
13
+ branch ?: string
14
+ }
15
+
16
+ // Mock hash decoding - in real implementation this would decode the actual hash
17
+ const mockDecodeHash = ( hash : string ) : MockHashDecodeResult => {
18
+ // Mock: If hash starts with 'site_', treat it as a site ID
19
+ // Otherwise, treat it as a direct repo URL
20
+ if ( hash . startsWith ( 'site_' ) ) {
21
+ return {
22
+ siteId : hash . replace ( 'site_' , '' ) ,
23
+ targetDir : `ai-project-${ hash . substring ( 0 , 8 ) } ` ,
24
+ branch : 'main'
25
+ }
26
+ } else {
27
+ return {
28
+ repoUrl : 'https://github.com/netlify/netlify-cli.git' , // Mock repo
29
+ targetDir : `ai-project-${ hash . substring ( 0 , 8 ) } ` ,
30
+ branch : 'main'
31
+ }
32
+ }
33
+ }
34
+
35
+ // Get repository URL from site ID using existing API functionality
36
+ const getRepoUrlFromSiteId = async ( api : any , siteId : string ) : Promise < string > => {
37
+ try {
38
+ const siteData = await api . getSite ( { siteId } )
39
+
40
+ const repoUrl = siteData . build_settings ?. repo_url
41
+
42
+ if ( ! repoUrl ) {
43
+ throw new Error ( `No repository URL found for site ID: ${ siteId } ` )
44
+ }
45
+
46
+ return repoUrl
47
+
48
+ } catch ( error : any ) {
49
+ if ( error . status === 404 ) {
50
+ throw new Error ( `Project with ID ${ siteId } not found` )
51
+ }
52
+ throw new Error ( `Failed to fetch project data: ${ error . message } ` )
53
+ }
54
+ }
55
+
6
56
// Mock server response for now
7
- const mockServerRequest = async ( hash : string , authToken : string ) => {
57
+ const mockServerRequest = async ( hash : string , _authToken : string ) => {
8
58
// Simulate API call delay
9
59
await new Promise ( resolve => setTimeout ( resolve , 1500 ) )
10
60
11
61
// Mock successful response
12
62
return {
13
63
success : true ,
14
64
message : 'AI project initialization started successfully' ,
15
- projectId : `proj_ ${ hash . substring ( 0 , 8 ) } ` ,
65
+ projectId : '4d6c8c75-2278-409e-bcb7-06e07b79e1bc' ,
16
66
status : 'processing' ,
17
67
estimatedTime : '2-3 minutes' ,
18
68
dashboardUrl : `https://app.netlify.com/ai/projects/proj_${ hash . substring ( 0 , 8 ) } `
19
69
}
20
70
}
21
71
22
- export const aiStartCommand = async ( _options : OptionValues , command : BaseCommand ) => {
72
+ const cloneRepo = async ( repoUrl : string , targetDir : string , debug : boolean ) : Promise < void > => {
73
+ try {
74
+ await runGit ( [ 'clone' , repoUrl , targetDir ] , ! debug )
75
+ } catch ( error ) {
76
+ throw new Error ( `Failed to clone repository: ${ error instanceof Error ? error . message : error ?. toString ( ) ?? '' } ` )
77
+ }
78
+ }
79
+
80
+ export const aiStartCommand = async ( options : OptionValues , command : BaseCommand ) => {
23
81
const hash = command . args [ 0 ]
24
82
25
83
// Validate hash parameter
@@ -37,11 +95,62 @@ export const aiStartCommand = async (_options: OptionValues, command: BaseComman
37
95
log ( `${ chalk . blue ( 'π€ AI Start' ) } - Initializing AI project...` )
38
96
log ( `${ chalk . gray ( 'Hash:' ) } ${ hash } ` )
39
97
log ( `${ chalk . gray ( 'User:' ) } ${ api . accessToken ? 'Authenticated β
' : 'Not authenticated β' } ` )
40
- log ( '\nSending request to AI server...' )
41
98
99
+ // Step 1: Decode hash to get repository information
100
+ log ( '\nπ Decoding project hash...' )
101
+ const hashResult = mockDecodeHash ( hash )
102
+
103
+ let finalRepoUrl : string
104
+
105
+ // Step 1a: If hash contains site ID, fetch repository URL from Netlify API
106
+ if ( hashResult . siteId ) {
107
+ log ( `${ chalk . cyan ( 'Site ID:' ) } ${ hashResult . siteId } ` )
108
+ log ( 'π Fetching repository information from Netlify...' )
109
+
110
+ try {
111
+ finalRepoUrl = await getRepoUrlFromSiteId ( api , hashResult . siteId )
112
+ log ( `${ chalk . cyan ( 'Repository:' ) } ${ finalRepoUrl } ` )
113
+ } catch ( error ) {
114
+ const errorMessage = error instanceof Error ? error . message : 'Unknown error'
115
+ log ( chalk . red ( 'β Error:' ) , errorMessage )
116
+ return
117
+ }
118
+ } else if ( hashResult . repoUrl ) {
119
+ // Direct repository URL provided
120
+ finalRepoUrl = hashResult . repoUrl
121
+ log ( `${ chalk . cyan ( 'Repository:' ) } ${ finalRepoUrl } ` )
122
+ } else {
123
+ log ( chalk . red ( 'β Error: No repository information found in hash' ) )
124
+ return
125
+ }
126
+
127
+ log ( `${ chalk . cyan ( 'Target Directory:' ) } ${ hashResult . targetDir ?? 'auto-generated' } ` )
128
+ if ( hashResult . branch ) {
129
+ log ( `${ chalk . cyan ( 'Branch:' ) } ${ hashResult . branch } ` )
130
+ }
131
+
132
+ // Step 2: Clone repository using existing functionality
42
133
try {
43
- // Mock server request for now
44
- const response = await mockServerRequest ( hash , api . accessToken || '' )
134
+ const { repoUrl, repoName } = normalizeRepoUrl ( finalRepoUrl )
135
+ const targetDir = hashResult . targetDir ?? `ai-project-${ repoName } -${ hash . substring ( 0 , 8 ) } `
136
+
137
+ const cloneSpinner = startSpinner ( { text : `Cloning repository to ${ chalk . cyan ( targetDir ) } ` } )
138
+
139
+ try {
140
+ await cloneRepo ( repoUrl , targetDir , Boolean ( options . debug ) )
141
+ cloneSpinner . success ( { text : `Cloned repository to ${ chalk . cyan ( targetDir ) } ` } )
142
+ } catch ( error ) {
143
+ cloneSpinner . error ( )
144
+ return logAndThrowError ( error )
145
+ }
146
+
147
+ // Update working directory to cloned repo
148
+ command . workingDir = targetDir
149
+ process . chdir ( targetDir )
150
+
151
+ // Step 3: Send request to AI server for project setup
152
+ log ( '\nπ Sending request to AI server...' )
153
+ const response = await mockServerRequest ( hash , api . accessToken ?? '' )
45
154
46
155
if ( response . success ) {
47
156
log ( `\n${ chalk . green ( 'β
Success!' ) } ${ response . message } ` )
@@ -53,16 +162,23 @@ export const aiStartCommand = async (_options: OptionValues, command: BaseComman
53
162
log ( `${ chalk . cyan ( 'Dashboard:' ) } ${ response . dashboardUrl } ` )
54
163
}
55
164
56
- log ( `\n${ chalk . gray ( 'π‘ Your AI project is being set up in the background.' ) } ` )
57
- log ( `${ chalk . gray ( 'You can check the progress in your Netlify dashboard.' ) } ` )
165
+ // Success message with next steps
166
+ log ( )
167
+ log ( chalk . green ( 'β Your AI project is ready to go!' ) )
168
+ log ( `β Project cloned to: ${ chalk . cyanBright ( targetDir ) } ` )
169
+ log ( `β Enter your project directory: ${ chalk . cyanBright ( `cd ${ targetDir } ` ) } ` )
170
+ log ( `β AI setup is processing in the background` )
171
+ log ( `β Check progress at: ${ chalk . cyanBright ( response . dashboardUrl ) } ` )
172
+ log ( )
173
+
58
174
} else {
59
- log ( ` ${ chalk . red ( 'β Failed to start AI project' ) } ` )
175
+ log ( chalk . red ( 'β Failed to start AI project' ) )
60
176
}
61
177
62
178
} catch ( error ) {
63
179
const errorMessage = error instanceof Error ? error . message : 'Unknown error occurred'
64
180
65
- log ( ` ${ chalk . red ( 'β Error:' ) } ${ errorMessage } ` )
66
- log ( ` ${ chalk . gray ( 'Please try again or contact support if the issue persists.' ) } ` )
181
+ log ( chalk . red ( 'β Error:' ) , errorMessage )
182
+ log ( chalk . gray ( 'Please try again or contact support if the issue persists.' ) )
67
183
}
68
184
}
0 commit comments