@vee-stack/delta-cli 2.0.9 → 2.0.11

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.
Files changed (98) hide show
  1. package/dist/adapters/analysis.adapter.d.ts +29 -0
  2. package/dist/analyzer/commands/analyze.d.ts +14 -0
  3. package/dist/analyzer/commands/config.d.ts +12 -0
  4. package/dist/analyzer/commands/report.d.ts +11 -0
  5. package/dist/analyzer/generators/report.generator.d.ts +43 -0
  6. package/dist/analyzer/index.d.ts +8 -0
  7. package/dist/analyzer/scanners/project.scanner.d.ts +29 -0
  8. package/dist/analyzer/validators/contracts.validator.d.ts +27 -0
  9. package/dist/analyzer/validators/maintainability.validator.d.ts +27 -0
  10. package/dist/analyzer/validators/observability.validator.d.ts +27 -0
  11. package/dist/analyzer/validators/performance.validator.d.ts +27 -0
  12. package/dist/analyzer/validators/security.validator.d.ts +27 -0
  13. package/dist/analyzer/validators/soc.validator.d.ts +27 -0
  14. package/dist/auth/device-auth.d.ts +18 -0
  15. package/dist/auth/secure-auth.d.ts +35 -0
  16. package/dist/bundle.mjs +422 -0
  17. package/dist/commands/analyze.d.ts +12 -0
  18. package/dist/commands/auth-new.d.ts +6 -0
  19. package/dist/commands/auth.d.ts +17 -0
  20. package/dist/commands/config.d.ts +8 -0
  21. package/dist/commands/deploy.d.ts +5 -0
  22. package/dist/commands/init.d.ts +7 -0
  23. package/dist/commands/logout.d.ts +2 -0
  24. package/dist/commands/plugins.d.ts +7 -0
  25. package/dist/commands/status.d.ts +5 -0
  26. package/dist/commands/sync.d.ts +5 -0
  27. package/dist/commands/whoami.d.ts +4 -0
  28. package/dist/components/Dashboard.d.ts +8 -0
  29. package/dist/components/DeltaApp.d.ts +13 -0
  30. package/dist/components/UnifiedManager.d.ts +10 -0
  31. package/dist/core/audit.d.ts +60 -0
  32. package/dist/core/completion.d.ts +79 -0
  33. package/dist/core/contracts.d.ts +127 -0
  34. package/dist/core/engine.d.ts +26 -0
  35. package/dist/core/exit-codes.d.ts +19 -0
  36. package/dist/core/hooks.d.ts +50 -0
  37. package/dist/core/{index.js → index.d.ts} +1 -1
  38. package/dist/core/policy.d.ts +24 -0
  39. package/dist/core/profiles.d.ts +45 -0
  40. package/dist/core/wizard.d.ts +28 -0
  41. package/dist/interactive/index.d.ts +5 -0
  42. package/dist/plugins/GitStatusPlugin.d.ts +25 -0
  43. package/dist/providers/ai-provider.d.ts +21 -0
  44. package/dist/providers/local-provider.d.ts +21 -0
  45. package/dist/providers/remote-provider.d.ts +15 -0
  46. package/dist/telemetry/wrapper.d.ts +18 -0
  47. package/dist/types/api.d.ts +46 -0
  48. package/dist/ui.d.ts +52 -0
  49. package/dist/welcome.d.ts +8 -0
  50. package/package.json +8 -10
  51. package/dist/adapters/analysis.adapter.js +0 -42
  52. package/dist/analyzer/commands/analyze.js +0 -220
  53. package/dist/analyzer/commands/config.js +0 -83
  54. package/dist/analyzer/commands/report.js +0 -38
  55. package/dist/analyzer/generators/report.generator.js +0 -123
  56. package/dist/analyzer/index.js +0 -44
  57. package/dist/analyzer/scanners/project.scanner.js +0 -92
  58. package/dist/analyzer/validators/contracts.validator.js +0 -42
  59. package/dist/analyzer/validators/maintainability.validator.js +0 -40
  60. package/dist/analyzer/validators/observability.validator.js +0 -39
  61. package/dist/analyzer/validators/performance.validator.js +0 -42
  62. package/dist/analyzer/validators/security.validator.js +0 -66
  63. package/dist/analyzer/validators/soc.validator.js +0 -75
  64. package/dist/auth/device-auth.js +0 -261
  65. package/dist/auth/secure-auth.js +0 -401
  66. package/dist/commands/analyze.js +0 -393
  67. package/dist/commands/auth-new.js +0 -37
  68. package/dist/commands/auth.js +0 -131
  69. package/dist/commands/config.js +0 -51
  70. package/dist/commands/deploy.js +0 -6
  71. package/dist/commands/init.js +0 -47
  72. package/dist/commands/logout.js +0 -31
  73. package/dist/commands/plugins.js +0 -21
  74. package/dist/commands/status.js +0 -82
  75. package/dist/commands/sync.js +0 -6
  76. package/dist/commands/whoami.js +0 -72
  77. package/dist/components/Dashboard.js +0 -167
  78. package/dist/components/DeltaApp.js +0 -57
  79. package/dist/components/UnifiedManager.js +0 -372
  80. package/dist/core/audit.js +0 -184
  81. package/dist/core/completion.js +0 -305
  82. package/dist/core/contracts.js +0 -6
  83. package/dist/core/engine.js +0 -130
  84. package/dist/core/exit-codes.js +0 -79
  85. package/dist/core/hooks.js +0 -181
  86. package/dist/core/policy.js +0 -115
  87. package/dist/core/profiles.js +0 -161
  88. package/dist/core/wizard.js +0 -203
  89. package/dist/index.js +0 -403
  90. package/dist/interactive/index.js +0 -11
  91. package/dist/plugins/GitStatusPlugin.js +0 -93
  92. package/dist/providers/ai-provider.js +0 -74
  93. package/dist/providers/local-provider.js +0 -304
  94. package/dist/providers/remote-provider.js +0 -100
  95. package/dist/telemetry/wrapper.js +0 -114
  96. package/dist/types/api.js +0 -3
  97. package/dist/ui.js +0 -226
  98. package/dist/welcome.js +0 -91
@@ -1,261 +0,0 @@
1
- /**
2
- * Device Authorization Flow for CLI Authentication
3
- * Implements OAuth 2.0 Device Authorization Grant (RFC 8628)
4
- */
5
- import * as https from 'https';
6
- import * as http from 'http';
7
- import { spawn } from 'child_process';
8
- import * as os from 'os';
9
- import { printSuccess, printError, printInfo, printSection, startSpinner, stopSpinner, printKeyValue, printCommandHint, printHeader, } from '../ui.js';
10
- import { SecureTokenStore } from '../auth/secure-auth.js';
11
- export class DeviceAuthorizationFlow {
12
- apiUrl;
13
- clientId;
14
- constructor(apiUrl, clientId = 'delta-cli') {
15
- this.apiUrl = apiUrl;
16
- this.clientId = clientId;
17
- }
18
- async startDeviceFlow() {
19
- printHeader('Device Authorization');
20
- printInfo('Starting device authorization flow...');
21
- console.log();
22
- try {
23
- // Step 1: Request device code
24
- startSpinner('Requesting device code...');
25
- const deviceCodeResponse = await this.requestDeviceCode();
26
- stopSpinner(true, 'Device code received');
27
- // Step 2: Show instructions to user
28
- this.displayAuthorizationInstructions(deviceCodeResponse);
29
- // Step 3: Open browser automatically
30
- await this.openBrowser(deviceCodeResponse.verification_uri_complete);
31
- // Step 4: Poll for token
32
- printSection('Waiting for Authorization');
33
- printInfo('Waiting for you to complete the authorization in your browser...');
34
- console.log();
35
- const tokenResponse = await this.pollForToken(deviceCodeResponse);
36
- // Step 5: Save tokens and show success
37
- await this.saveTokens(tokenResponse);
38
- this.showSuccess();
39
- return true;
40
- }
41
- catch (error) {
42
- this.handleError(error);
43
- return false;
44
- }
45
- }
46
- async requestDeviceCode() {
47
- const response = await this.makeRequest('/api/auth/device/code', {
48
- method: 'POST',
49
- headers: {
50
- 'Content-Type': 'application/json',
51
- },
52
- body: JSON.stringify({
53
- client_id: this.clientId,
54
- scope: 'read write analyze',
55
- }),
56
- });
57
- const data = (await response.json());
58
- if (!response.ok) {
59
- throw new Error(`Failed to request device code: ${data.error || 'Unknown error'}`);
60
- }
61
- return data;
62
- }
63
- displayAuthorizationInstructions(response) {
64
- console.log();
65
- printSection('Authorization Required');
66
- printInfo('Please complete the authorization using one of these methods:');
67
- console.log();
68
- // Method 1: Auto-opened browser
69
- printKeyValue('🌐 Browser', 'A browser window should have opened automatically');
70
- // Method 2: Manual URL
71
- console.log();
72
- printKeyValue('📋 Manual URL', response.verification_uri);
73
- printKeyValue('🔢 Code', response.user_code);
74
- console.log();
75
- // Method 3: Complete URL
76
- printInfo('Or use this direct link:');
77
- console.log(` ${response.verification_uri_complete}`);
78
- console.log();
79
- printInfo('⏱️ This code will expire in ' + Math.floor(response.expires_in / 60) + ' minutes');
80
- console.log();
81
- }
82
- async openBrowser(url) {
83
- const platform = os.platform();
84
- let command;
85
- switch (platform) {
86
- case 'darwin':
87
- command = `open "${url}"`;
88
- break;
89
- case 'win32':
90
- command = `start "" "${url}"`;
91
- break;
92
- default: // linux
93
- command = `xdg-open "${url}"`;
94
- break;
95
- }
96
- try {
97
- await this.executeCommand(command);
98
- printInfo('✓ Browser opened successfully');
99
- }
100
- catch (error) {
101
- printInfo('⚠️ Could not open browser automatically');
102
- printInfo('Please manually open the URL above');
103
- }
104
- }
105
- executeCommand(command) {
106
- return new Promise((resolve, reject) => {
107
- const child = spawn(command, [], { shell: true, stdio: 'ignore' });
108
- child.on('close', code => {
109
- if (code === 0) {
110
- resolve();
111
- }
112
- else {
113
- reject(new Error(`Command failed with code ${code}`));
114
- }
115
- });
116
- child.on('error', reject);
117
- });
118
- }
119
- async pollForToken(deviceCode) {
120
- const startTime = Date.now();
121
- const endTime = startTime + deviceCode.expires_in * 1000;
122
- const interval = deviceCode.interval * 1000;
123
- const spinner = startSpinner('Checking authorization status...');
124
- void spinner; // Spinner is managed internally by the UI system
125
- while (Date.now() < endTime) {
126
- try {
127
- const response = await this.makeRequest('/api/auth/device/token', {
128
- method: 'POST',
129
- headers: {
130
- 'Content-Type': 'application/json',
131
- },
132
- body: JSON.stringify({
133
- device_code: deviceCode.device_code,
134
- grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
135
- }),
136
- });
137
- const data = (await response.json());
138
- if ('error' in data) {
139
- if (data.error === 'authorization_pending') {
140
- // Still waiting, continue polling
141
- await this.sleep(interval);
142
- continue;
143
- }
144
- else if (data.error === 'slow_down') {
145
- // Server asks to slow down
146
- await this.sleep(interval * 2);
147
- continue;
148
- }
149
- else {
150
- // Error occurred
151
- stopSpinner(false);
152
- throw new Error(`Authorization failed: ${data.error_description || data.error}`);
153
- }
154
- }
155
- else {
156
- // Success!
157
- stopSpinner(true, 'Authorization complete');
158
- return data;
159
- }
160
- }
161
- catch (error) {
162
- stopSpinner(false);
163
- throw error;
164
- }
165
- }
166
- stopSpinner(false);
167
- throw new Error('Authorization timed out. Please try again.');
168
- }
169
- async saveTokens(tokenResponse) {
170
- // Save access token securely
171
- await SecureTokenStore.saveAccessToken(tokenResponse.access_token);
172
- // Save refresh token securely
173
- await SecureTokenStore.saveRefreshToken(tokenResponse.refresh_token);
174
- // Save session info for future reference
175
- await SecureTokenStore.saveSessionInfo({
176
- session_id: tokenResponse.session_id,
177
- expires_at: new Date(Date.now() + tokenResponse.expires_in * 1000).toISOString(),
178
- });
179
- }
180
- showSuccess() {
181
- console.log();
182
- printSuccess('🎉 Device authorization successful!');
183
- console.log();
184
- printSection('Next Steps');
185
- printCommandHint('delta whoami', 'to verify your account');
186
- printCommandHint('delta analyze', 'to start analyzing code');
187
- console.log();
188
- }
189
- handleError(error) {
190
- console.log();
191
- printError('Device authorization failed');
192
- console.log();
193
- if (error instanceof Error) {
194
- if (error.message.includes('ENOTFOUND') || error.message.includes('ECONNREFUSED')) {
195
- printError('Connection Error', 'Cannot connect to the Delta platform');
196
- printInfo('Please check:');
197
- printKeyValue('• Server', 'Is the web platform running?');
198
- printKeyValue('• Network', 'Check your internet connection');
199
- printKeyValue('• URL', `Is ${this.apiUrl} correct?`);
200
- }
201
- else if (error.message.includes('authorization_pending')) {
202
- printError('Timeout', 'Authorization was not completed in time');
203
- printInfo('Please try again');
204
- }
205
- else if (error.message.includes('access_denied')) {
206
- printError('Access Denied', 'You denied the authorization request');
207
- printInfo('If this was a mistake, please try again');
208
- }
209
- else {
210
- printError('Error', error.message);
211
- }
212
- }
213
- else {
214
- printError('Unknown Error', String(error));
215
- }
216
- console.log();
217
- printInfo('Alternative: Use a Personal Access Token');
218
- printCommandHint('delta login --token delta_pat_xxxxx', '');
219
- }
220
- async makeRequest(endpoint, options) {
221
- const url = `${this.apiUrl}${endpoint}`;
222
- // Use Node.js built-in fetch if available, otherwise fallback to http/https modules
223
- if (typeof fetch !== 'undefined') {
224
- return fetch(url, options);
225
- }
226
- // Fallback for older Node.js versions
227
- return new Promise((resolve, reject) => {
228
- const urlObj = new URL(url);
229
- const module = urlObj.protocol === 'https:' ? https : http;
230
- const requestOptions = {
231
- method: options.method || 'GET',
232
- headers: options.headers || {},
233
- };
234
- const req = module.request(url, requestOptions, res => {
235
- let data = '';
236
- res.on('data', chunk => (data += chunk));
237
- res.on('end', () => {
238
- resolve(new Response(data, {
239
- status: res.statusCode,
240
- statusText: res.statusMessage,
241
- headers: res.headers,
242
- }));
243
- });
244
- });
245
- req.on('error', reject);
246
- if (options.body) {
247
- req.write(options.body);
248
- }
249
- req.end();
250
- });
251
- }
252
- sleep(ms) {
253
- return new Promise(resolve => setTimeout(resolve, ms));
254
- }
255
- }
256
- // Export for use in auth commands
257
- export async function startDeviceFlow(apiUrl) {
258
- const flow = new DeviceAuthorizationFlow(apiUrl);
259
- return flow.startDeviceFlow();
260
- }
261
- //# sourceMappingURL=device-auth.js.map
@@ -1,401 +0,0 @@
1
- /**
2
- * Secure Authentication System with OAuth2 + Keychain Integration
3
- * Features:
4
- * - OAuth2 PKCE flow
5
- * - Secure token storage in OS keychain
6
- * - Automatic token refresh
7
- * - Session management
8
- * - PAT (Personal Access Token) support for CI/CD
9
- */
10
- import * as fs from 'fs/promises';
11
- import * as path from 'path';
12
- import * as os from 'os';
13
- import fetch from 'node-fetch';
14
- import open from 'open';
15
- import { createServer } from 'http';
16
- import { URL } from 'url';
17
- import crypto from 'crypto';
18
- import chalk from 'chalk';
19
- import { printSuccess, printError, printInfo, printWarning, startSpinner, stopSpinner, } from '../ui.js';
20
- const CONFIG_DIR = path.join(os.homedir(), '.delta');
21
- const TOKEN_FILE = path.join(CONFIG_DIR, 'tokens.enc');
22
- const KEY_FILE = path.join(CONFIG_DIR, '.key');
23
- // Derive encryption key from machine-specific data
24
- async function getEncryptionKey() {
25
- try {
26
- const keyData = await fs.readFile(KEY_FILE, 'utf-8').catch(() => null);
27
- if (keyData) {
28
- return Buffer.from(keyData, 'hex');
29
- }
30
- }
31
- catch {
32
- // File doesn't exist, generate new key
33
- }
34
- const machineId = `${os.userInfo().username}@${os.hostname()}`;
35
- const salt = 'delta-cli-v2-fixed-salt';
36
- const key = crypto.pbkdf2Sync(machineId, salt, 100000, 32, 'sha256');
37
- await fs.mkdir(CONFIG_DIR, { recursive: true });
38
- await fs.writeFile(KEY_FILE, key.toString('hex'), { mode: 0o600 });
39
- return key;
40
- }
41
- // Encrypt data
42
- async function encrypt(data) {
43
- const key = await getEncryptionKey();
44
- const iv = crypto.randomBytes(16);
45
- const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
46
- let encrypted = cipher.update(data, 'utf-8', 'hex');
47
- encrypted += cipher.final('hex');
48
- const authTag = cipher.getAuthTag();
49
- return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
50
- }
51
- // Decrypt data
52
- async function decrypt(encryptedData) {
53
- try {
54
- const key = await getEncryptionKey();
55
- const parts = encryptedData.split(':');
56
- if (parts.length !== 3)
57
- return null;
58
- const iv = Buffer.from(parts[0], 'hex');
59
- const authTag = Buffer.from(parts[1], 'hex');
60
- const encrypted = parts[2];
61
- const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
62
- decipher.setAuthTag(authTag);
63
- let decrypted = decipher.update(encrypted, 'hex', 'utf-8');
64
- decrypted += decipher.final('utf-8');
65
- return decrypted;
66
- }
67
- catch {
68
- return null;
69
- }
70
- }
71
- // PKCE Code Generator
72
- function generatePKCE() {
73
- const verifier = crypto.randomBytes(32).toString('base64url');
74
- const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
75
- return { verifier, challenge };
76
- }
77
- // Secure Token Storage using encrypted file
78
- export class SecureTokenStore {
79
- static async saveTokens(tokens) {
80
- await fs.mkdir(CONFIG_DIR, { recursive: true });
81
- const data = JSON.stringify({
82
- access: tokens.access || null,
83
- refresh: tokens.refresh || null,
84
- updatedAt: new Date().toISOString(),
85
- });
86
- const encrypted = await encrypt(data);
87
- await fs.writeFile(TOKEN_FILE, encrypted, { mode: 0o600 });
88
- }
89
- static async loadTokens() {
90
- try {
91
- const encrypted = await fs.readFile(TOKEN_FILE, 'utf-8');
92
- const decrypted = await decrypt(encrypted);
93
- if (!decrypted)
94
- return { access: null, refresh: null };
95
- const data = JSON.parse(decrypted);
96
- return {
97
- access: data.access || null,
98
- refresh: data.refresh || null,
99
- };
100
- }
101
- catch {
102
- return { access: null, refresh: null };
103
- }
104
- }
105
- static async saveAccessToken(token) {
106
- const tokens = await this.loadTokens();
107
- await this.saveTokens({ access: token, refresh: tokens.refresh ?? undefined });
108
- }
109
- static async getAccessToken() {
110
- const tokens = await this.loadTokens();
111
- return tokens.access;
112
- }
113
- static async saveRefreshToken(token) {
114
- // Encrypt token (side effect - encryption happens before storage)
115
- const encrypted = await encrypt(token);
116
- void encrypted; // Mark as intentionally used for encryption side effect
117
- const tokens = await this.loadTokens();
118
- await this.saveTokens({
119
- access: tokens.access || undefined,
120
- refresh: token,
121
- });
122
- }
123
- static async saveSessionInfo(sessionInfo) {
124
- // For now, store session info in a separate file
125
- // In production, this should be encrypted and stored securely
126
- const sessionFile = path.join(CONFIG_DIR, 'session.json');
127
- await fs.mkdir(CONFIG_DIR, { recursive: true });
128
- await fs.writeFile(sessionFile, JSON.stringify(sessionInfo, null, 2));
129
- }
130
- static async getRefreshToken() {
131
- const tokens = await this.loadTokens();
132
- return tokens.refresh;
133
- }
134
- static async clearTokens() {
135
- try {
136
- // Check if file exists first
137
- try {
138
- await fs.access(TOKEN_FILE);
139
- }
140
- catch {
141
- // File doesn't exist, nothing to clear
142
- return;
143
- }
144
- await fs.unlink(TOKEN_FILE);
145
- // Also clear session file if exists
146
- const sessionFile = path.join(CONFIG_DIR, 'session.json');
147
- try {
148
- await fs.access(sessionFile);
149
- await fs.unlink(sessionFile);
150
- }
151
- catch {
152
- // Session file may not exist
153
- }
154
- console.log(' Tokens cleared from:', TOKEN_FILE);
155
- }
156
- catch (error) {
157
- console.error(' Warning: Could not clear tokens:', error.message);
158
- throw error;
159
- }
160
- }
161
- static async hasTokens() {
162
- const token = await this.getAccessToken();
163
- return !!token;
164
- }
165
- }
166
- // OAuth2 Flow Handler - Uses configurable API URL for local development
167
- export async function startOAuthFlow(method = 'oauth') {
168
- startSpinner('Starting OAuth2 authentication flow...');
169
- try {
170
- // Get API URL from environment or use default
171
- const apiUrl = process.env.DELTA_API_URL || 'http://localhost:3000';
172
- const { verifier, challenge } = generatePKCE();
173
- const state = crypto.randomBytes(16).toString('hex');
174
- // Start local server FIRST to capture callback and get actual port
175
- const { authCode, redirectUri } = await new Promise((resolve, reject) => {
176
- const server = createServer(async (req, res) => {
177
- const url = new URL(req.url || '/', `http://localhost:${redirectPort}`);
178
- if (url.pathname === '/callback') {
179
- const code = url.searchParams.get('code');
180
- const returnedState = url.searchParams.get('state');
181
- const error = url.searchParams.get('error');
182
- if (error) {
183
- res.writeHead(400, { 'Content-Type': 'text/html' });
184
- res.end(`<html><body><h1>❌ Authentication Failed</h1><p>${error}</p></body></html>`);
185
- reject(new Error(error));
186
- server.close();
187
- return;
188
- }
189
- if (!code || returnedState !== state) {
190
- res.writeHead(400, { 'Content-Type': 'text/html' });
191
- res.end(`<html><body><h1>❌ Invalid Response</h1></body></html>`);
192
- reject(new Error('Invalid OAuth response'));
193
- server.close();
194
- return;
195
- }
196
- res.writeHead(200, { 'Content-Type': 'text/html' });
197
- res.end(`<html><body style="text-align:center;padding:50px;"><h1>✓ Successfully Signed In!</h1><p>You can close this window.</p></body></html>`);
198
- const address = server.address();
199
- const actualPort = typeof address === 'object' && address ? address.port : 3456;
200
- resolve({ authCode: code, redirectUri: `http://localhost:${actualPort}/callback` });
201
- server.close();
202
- }
203
- });
204
- let redirectPort = 3456;
205
- // Try to listen on port 3456
206
- server.listen(3456, '127.0.0.1', () => {
207
- console.log(chalk.dim(` Waiting for authentication on port ${redirectPort}...`));
208
- });
209
- // Handle port in use error by trying random port
210
- server.on('error', (err) => {
211
- if (err.code === 'EADDRINUSE') {
212
- printWarning('Port 3456 is busy, trying alternative port...');
213
- redirectPort = 0; // Let OS assign
214
- server.listen(0, '127.0.0.1', () => {
215
- const address = server.address();
216
- const actualPort = typeof address === 'object' && address ? address.port : 3456;
217
- redirectPort = actualPort;
218
- console.log(chalk.dim(` Waiting for authentication on port ${actualPort}...`));
219
- });
220
- }
221
- else {
222
- reject(err);
223
- }
224
- });
225
- // Timeout after 5 minutes
226
- setTimeout(() => {
227
- server.close();
228
- reject(new Error('Authentication timeout'));
229
- }, 5 * 60 * 1000);
230
- });
231
- // NOW build OAuth URL with correct redirect_uri
232
- const oauthUrl = new URL(`${apiUrl}/api/auth/authorize`);
233
- oauthUrl.searchParams.set('response_type', 'code');
234
- oauthUrl.searchParams.set('client_id', 'delta-cli');
235
- oauthUrl.searchParams.set('redirect_uri', redirectUri);
236
- oauthUrl.searchParams.set('code_challenge', challenge);
237
- oauthUrl.searchParams.set('code_challenge_method', 'S256');
238
- oauthUrl.searchParams.set('state', state);
239
- oauthUrl.searchParams.set('scope', 'read write analyze');
240
- if (method === 'github') {
241
- oauthUrl.searchParams.set('provider', 'github');
242
- }
243
- else if (method === 'google') {
244
- oauthUrl.searchParams.set('provider', 'google');
245
- }
246
- stopSpinner(true, 'OAuth server ready');
247
- // Open browser with correct URL
248
- printInfo(`Opening browser for authentication...`);
249
- printInfo(`Full URL: ${oauthUrl.toString()}`);
250
- await open(oauthUrl.toString());
251
- // Wait for authCode (already resolved above)
252
- // Exchange code for tokens
253
- startSpinner('Exchanging authorization code...');
254
- const tokenResponse = await fetch(`${apiUrl}/api/auth/token`, {
255
- method: 'POST',
256
- headers: { 'Content-Type': 'application/json' },
257
- body: JSON.stringify({
258
- grant_type: 'authorization_code',
259
- code: authCode,
260
- redirect_uri: redirectUri,
261
- client_id: 'delta-cli',
262
- code_verifier: verifier,
263
- }),
264
- });
265
- if (!tokenResponse.ok) {
266
- throw new Error('Failed to exchange authorization code');
267
- }
268
- const tokens = (await tokenResponse.json());
269
- // Store tokens securely
270
- await SecureTokenStore.saveAccessToken(tokens.access_token);
271
- await SecureTokenStore.saveRefreshToken(tokens.refresh_token);
272
- stopSpinner(true, 'Authentication successful!');
273
- printSuccess('Successfully authenticated with Delta Cloud');
274
- printInfo(`Token expires in ${tokens.expires_in} seconds`);
275
- return true;
276
- }
277
- catch (error) {
278
- stopSpinner(false, 'Authentication failed');
279
- printError('OAuth2 authentication failed', error.message);
280
- return false;
281
- }
282
- }
283
- // PAT Authentication for CI/CD - Uses configurable API URL
284
- export async function authenticateWithPAT(token) {
285
- startSpinner('Validating Personal Access Token...');
286
- try {
287
- const apiUrl = process.env.DELTA_API_URL || 'http://localhost:3000';
288
- // Validate token
289
- const response = await fetch(`${apiUrl}/api/auth/verify`, {
290
- headers: {
291
- Authorization: `Bearer ${token}`,
292
- },
293
- });
294
- if (!response.ok) {
295
- throw new Error('Invalid token');
296
- }
297
- const data = (await response.json());
298
- // Store token
299
- await SecureTokenStore.saveAccessToken(token);
300
- stopSpinner(true, 'Token validated');
301
- printSuccess(`Authenticated as ${data.user.email}`);
302
- return true;
303
- }
304
- catch (error) {
305
- stopSpinner(false, 'Token validation failed');
306
- printError('PAT authentication failed', error.message);
307
- return false;
308
- }
309
- }
310
- // Token Refresh - Uses configurable API URL
311
- export async function refreshAccessToken() {
312
- const refreshToken = await SecureTokenStore.getRefreshToken();
313
- if (!refreshToken) {
314
- printError('No refresh token available');
315
- return false;
316
- }
317
- try {
318
- const apiUrl = process.env.DELTA_API_URL || 'http://localhost:3000';
319
- const response = await fetch(`${apiUrl}/api/auth/refresh`, {
320
- method: 'POST',
321
- headers: { 'Content-Type': 'application/json' },
322
- body: JSON.stringify({
323
- grant_type: 'refresh_token',
324
- refresh_token: refreshToken,
325
- client_id: 'delta-cli',
326
- }),
327
- });
328
- if (!response.ok) {
329
- throw new Error('Token refresh failed');
330
- }
331
- const tokens = (await response.json());
332
- await SecureTokenStore.saveAccessToken(tokens.access_token);
333
- if (tokens.refresh_token) {
334
- await SecureTokenStore.saveRefreshToken(tokens.refresh_token);
335
- }
336
- return true;
337
- }
338
- catch (error) {
339
- printError('Failed to refresh token', error.message);
340
- return false;
341
- }
342
- }
343
- // Logout
344
- export async function logout() {
345
- startSpinner('Clearing credentials...');
346
- try {
347
- await SecureTokenStore.clearTokens();
348
- stopSpinner(true, 'Logged out');
349
- printSuccess('Successfully logged out');
350
- }
351
- catch (error) {
352
- stopSpinner(false, 'Logout failed');
353
- printError('Failed to clear credentials', error.message);
354
- }
355
- }
356
- // Auth Status - Uses configurable API URL
357
- export async function getAuthStatus() {
358
- const token = await SecureTokenStore.getAccessToken();
359
- if (!token) {
360
- return { authenticated: false };
361
- }
362
- try {
363
- const apiUrl = process.env.DELTA_API_URL || 'http://localhost:3000';
364
- const response = await fetch(`${apiUrl}/api/auth/me`, {
365
- headers: {
366
- Authorization: `Bearer ${token}`,
367
- },
368
- });
369
- if (!response.ok) {
370
- // Try to refresh token
371
- const refreshed = await refreshAccessToken();
372
- if (!refreshed) {
373
- return { authenticated: false };
374
- }
375
- // Retry with new token
376
- const newToken = await SecureTokenStore.getAccessToken();
377
- const retryResponse = await fetch(`${apiUrl}/api/auth/me`, {
378
- headers: {
379
- Authorization: `Bearer ${newToken}`,
380
- },
381
- });
382
- if (!retryResponse.ok) {
383
- return { authenticated: false };
384
- }
385
- const userData = (await retryResponse.json());
386
- return {
387
- authenticated: true,
388
- user: userData,
389
- };
390
- }
391
- const userData = (await response.json());
392
- return {
393
- authenticated: true,
394
- user: userData,
395
- };
396
- }
397
- catch {
398
- return { authenticated: false };
399
- }
400
- }
401
- //# sourceMappingURL=secure-auth.js.map