@vee-stack/delta-cli 2.0.6 → 2.0.8
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.
- package/dist/auth/secure-auth.js +40 -86
- package/dist/commands/analyze.js +14 -8
- package/package.json +1 -1
package/dist/auth/secure-auth.js
CHANGED
|
@@ -141,120 +141,53 @@ export async function startOAuthFlow(method = 'oauth') {
|
|
|
141
141
|
// Get API URL from environment or use default
|
|
142
142
|
const apiUrl = process.env.DELTA_API_URL || 'http://localhost:3000';
|
|
143
143
|
const { verifier, challenge } = generatePKCE();
|
|
144
|
-
const redirectUri = 'http://localhost:3456/callback';
|
|
145
144
|
const state = crypto.randomBytes(16).toString('hex');
|
|
146
|
-
//
|
|
147
|
-
const
|
|
148
|
-
oauthUrl.searchParams.set('response_type', 'code');
|
|
149
|
-
oauthUrl.searchParams.set('client_id', 'delta-cli');
|
|
150
|
-
oauthUrl.searchParams.set('redirect_uri', redirectUri);
|
|
151
|
-
oauthUrl.searchParams.set('code_challenge', challenge);
|
|
152
|
-
oauthUrl.searchParams.set('code_challenge_method', 'S256');
|
|
153
|
-
oauthUrl.searchParams.set('state', state);
|
|
154
|
-
oauthUrl.searchParams.set('scope', 'read write analyze');
|
|
155
|
-
if (method === 'github') {
|
|
156
|
-
oauthUrl.searchParams.set('provider', 'github');
|
|
157
|
-
}
|
|
158
|
-
else if (method === 'google') {
|
|
159
|
-
oauthUrl.searchParams.set('provider', 'google');
|
|
160
|
-
}
|
|
161
|
-
stopSpinner(true, 'OAuth server ready');
|
|
162
|
-
// Open browser
|
|
163
|
-
printInfo(`Opening browser for authentication...`);
|
|
164
|
-
printInfo(`URL: ${oauthUrl.toString().substring(0, 80)}...`);
|
|
165
|
-
await open(oauthUrl.toString());
|
|
166
|
-
// Start local server to capture callback
|
|
167
|
-
const authCode = await new Promise((resolve, reject) => {
|
|
145
|
+
// Start local server FIRST to capture callback and get actual port
|
|
146
|
+
const { authCode, redirectUri } = await new Promise((resolve, reject) => {
|
|
168
147
|
const server = createServer(async (req, res) => {
|
|
169
|
-
|
|
170
|
-
const address = server.address();
|
|
171
|
-
const actualPort = typeof address === 'object' && address ? address.port : 3000;
|
|
172
|
-
const url = new URL(req.url || '/', `http://localhost:${actualPort}`);
|
|
148
|
+
const url = new URL(req.url || '/', `http://localhost:${redirectPort}`);
|
|
173
149
|
if (url.pathname === '/callback') {
|
|
174
150
|
const code = url.searchParams.get('code');
|
|
175
151
|
const returnedState = url.searchParams.get('state');
|
|
176
152
|
const error = url.searchParams.get('error');
|
|
177
153
|
if (error) {
|
|
178
154
|
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
179
|
-
res.end(
|
|
180
|
-
<html>
|
|
181
|
-
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
182
|
-
<h1 style="color: #e74c3c;">❌ Authentication Failed</h1>
|
|
183
|
-
<p>${error}</p>
|
|
184
|
-
<p>You can close this window.</p>
|
|
185
|
-
</body>
|
|
186
|
-
</html>
|
|
187
|
-
`);
|
|
155
|
+
res.end(`<html><body><h1>❌ Authentication Failed</h1><p>${error}</p></body></html>`);
|
|
188
156
|
reject(new Error(error));
|
|
189
157
|
server.close();
|
|
190
158
|
return;
|
|
191
159
|
}
|
|
192
160
|
if (!code || returnedState !== state) {
|
|
193
161
|
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
194
|
-
res.end(
|
|
195
|
-
<html>
|
|
196
|
-
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
197
|
-
<h1 style="color: #e74c3c;">❌ Invalid Response</h1>
|
|
198
|
-
<p>You can close this window.</p>
|
|
199
|
-
</body>
|
|
200
|
-
</html>
|
|
201
|
-
`);
|
|
162
|
+
res.end(`<html><body><h1>❌ Invalid Response</h1></body></html>`);
|
|
202
163
|
reject(new Error('Invalid OAuth response'));
|
|
203
164
|
server.close();
|
|
204
165
|
return;
|
|
205
166
|
}
|
|
206
167
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
207
|
-
res.end(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
body {
|
|
212
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
213
|
-
text-align: center;
|
|
214
|
-
padding: 50px;
|
|
215
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
216
|
-
color: white;
|
|
217
|
-
min-height: 100vh;
|
|
218
|
-
display: flex;
|
|
219
|
-
align-items: center;
|
|
220
|
-
justify-content: center;
|
|
221
|
-
}
|
|
222
|
-
.container {
|
|
223
|
-
background: rgba(255,255,255,0.1);
|
|
224
|
-
backdrop-filter: blur(10px);
|
|
225
|
-
padding: 40px;
|
|
226
|
-
border-radius: 20px;
|
|
227
|
-
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
228
|
-
}
|
|
229
|
-
h1 { margin: 0 0 20px; font-size: 48px; }
|
|
230
|
-
.check { font-size: 64px; margin-bottom: 20px; }
|
|
231
|
-
p { font-size: 18px; opacity: 0.9; }
|
|
232
|
-
</style>
|
|
233
|
-
</head>
|
|
234
|
-
<body>
|
|
235
|
-
<div class="container">
|
|
236
|
-
<div class="check">✓</div>
|
|
237
|
-
<h1>Successfully Signed In!</h1>
|
|
238
|
-
<p>You can close this window and return to the terminal.</p>
|
|
239
|
-
</div>
|
|
240
|
-
</body>
|
|
241
|
-
</html>
|
|
242
|
-
`);
|
|
243
|
-
resolve(code);
|
|
168
|
+
res.end(`<html><body style="text-align:center;padding:50px;"><h1>✓ Successfully Signed In!</h1><p>You can close this window.</p></body></html>`);
|
|
169
|
+
const address = server.address();
|
|
170
|
+
const actualPort = typeof address === 'object' && address ? address.port : 3456;
|
|
171
|
+
resolve({ authCode: code, redirectUri: `http://localhost:${actualPort}/callback` });
|
|
244
172
|
server.close();
|
|
245
173
|
}
|
|
246
174
|
});
|
|
247
|
-
|
|
175
|
+
let redirectPort = 3456;
|
|
176
|
+
// Try to listen on port 3456
|
|
248
177
|
server.listen(3456, '127.0.0.1', () => {
|
|
249
|
-
|
|
250
|
-
const actualPort = typeof address === 'object' && address ? address.port : 3456;
|
|
251
|
-
console.log(chalk.dim(` Waiting for authentication on port ${actualPort}...`));
|
|
178
|
+
console.log(chalk.dim(` Waiting for authentication on port ${redirectPort}...`));
|
|
252
179
|
});
|
|
253
180
|
// Handle port in use error by trying random port
|
|
254
181
|
server.on('error', (err) => {
|
|
255
182
|
if (err.code === 'EADDRINUSE') {
|
|
256
183
|
printWarning('Port 3456 is busy, trying alternative port...');
|
|
257
|
-
|
|
184
|
+
redirectPort = 0; // Let OS assign
|
|
185
|
+
server.listen(0, '127.0.0.1', () => {
|
|
186
|
+
const address = server.address();
|
|
187
|
+
const actualPort = typeof address === 'object' && address ? address.port : 3456;
|
|
188
|
+
redirectPort = actualPort;
|
|
189
|
+
console.log(chalk.dim(` Waiting for authentication on port ${actualPort}...`));
|
|
190
|
+
});
|
|
258
191
|
}
|
|
259
192
|
else {
|
|
260
193
|
reject(err);
|
|
@@ -266,6 +199,27 @@ export async function startOAuthFlow(method = 'oauth') {
|
|
|
266
199
|
reject(new Error('Authentication timeout'));
|
|
267
200
|
}, 5 * 60 * 1000);
|
|
268
201
|
});
|
|
202
|
+
// NOW build OAuth URL with correct redirect_uri
|
|
203
|
+
const oauthUrl = new URL(`${apiUrl}/api/auth/authorize`);
|
|
204
|
+
oauthUrl.searchParams.set('response_type', 'code');
|
|
205
|
+
oauthUrl.searchParams.set('client_id', 'delta-cli');
|
|
206
|
+
oauthUrl.searchParams.set('redirect_uri', redirectUri);
|
|
207
|
+
oauthUrl.searchParams.set('code_challenge', challenge);
|
|
208
|
+
oauthUrl.searchParams.set('code_challenge_method', 'S256');
|
|
209
|
+
oauthUrl.searchParams.set('state', state);
|
|
210
|
+
oauthUrl.searchParams.set('scope', 'read write analyze');
|
|
211
|
+
if (method === 'github') {
|
|
212
|
+
oauthUrl.searchParams.set('provider', 'github');
|
|
213
|
+
}
|
|
214
|
+
else if (method === 'google') {
|
|
215
|
+
oauthUrl.searchParams.set('provider', 'google');
|
|
216
|
+
}
|
|
217
|
+
stopSpinner(true, 'OAuth server ready');
|
|
218
|
+
// Open browser with correct URL
|
|
219
|
+
printInfo(`Opening browser for authentication...`);
|
|
220
|
+
printInfo(`Full URL: ${oauthUrl.toString()}`);
|
|
221
|
+
await open(oauthUrl.toString());
|
|
222
|
+
// Wait for authCode (already resolved above)
|
|
269
223
|
// Exchange code for tokens
|
|
270
224
|
startSpinner('Exchanging authorization code...');
|
|
271
225
|
const tokenResponse = await fetch(`${apiUrl}/api/auth/token`, {
|
package/dist/commands/analyze.js
CHANGED
|
@@ -25,7 +25,7 @@ export async function analyzeCommand(projectPath, options) {
|
|
|
25
25
|
// Check authentication FIRST if upload is requested
|
|
26
26
|
if (options.upload) {
|
|
27
27
|
printInfo('🔐 Checking authentication...');
|
|
28
|
-
|
|
28
|
+
await loadConfig();
|
|
29
29
|
const pat = await SecureTokenStore.getAccessToken();
|
|
30
30
|
if (!pat) {
|
|
31
31
|
console.error();
|
|
@@ -173,13 +173,19 @@ export async function analyzeCommand(projectPath, options) {
|
|
|
173
173
|
printSuccess('✨ No issues found! Great job!');
|
|
174
174
|
}
|
|
175
175
|
console.log();
|
|
176
|
-
// Save report to file
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
176
|
+
// Save report to file ONLY if not uploading
|
|
177
|
+
if (!options.upload) {
|
|
178
|
+
const outputDir = path.resolve(options.output);
|
|
179
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
180
|
+
const reportFile = path.join(outputDir, `report-${Date.now()}.json`);
|
|
181
|
+
await fs.writeFile(reportFile, JSON.stringify(report, null, 2));
|
|
182
|
+
printInfo(`${icons.chart} Report saved: ${chalk.cyan(reportFile)}`);
|
|
183
|
+
console.log();
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
printInfo(`${icons.chart} Report will be available on Delta Cloud Dashboard`);
|
|
187
|
+
console.log();
|
|
188
|
+
}
|
|
183
189
|
// Upload to cloud if requested
|
|
184
190
|
if (options.upload) {
|
|
185
191
|
console.log(`${icons.rocket} Uploading to Delta Cloud...`);
|