@vee-stack/delta-cli 2.0.3 → 2.0.5

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 (135) hide show
  1. package/dist/analyzer/commands/analyze.js +260 -0
  2. package/dist/analyzer/commands/config.js +83 -0
  3. package/dist/analyzer/commands/report.js +38 -0
  4. package/dist/analyzer/generators/report.generator.js +123 -0
  5. package/dist/analyzer/index.js +44 -0
  6. package/dist/analyzer/scanners/project.scanner.js +92 -0
  7. package/dist/analyzer/validators/contracts.validator.js +42 -0
  8. package/dist/analyzer/validators/maintainability.validator.js +40 -0
  9. package/dist/analyzer/validators/observability.validator.js +39 -0
  10. package/dist/analyzer/validators/performance.validator.js +42 -0
  11. package/dist/analyzer/validators/security.validator.js +66 -0
  12. package/dist/analyzer/validators/soc.validator.js +75 -0
  13. package/dist/apps/cli/src/analyzer/commands/analyze.js +256 -0
  14. package/dist/apps/cli/src/analyzer/commands/config.js +83 -0
  15. package/dist/apps/cli/src/analyzer/commands/report.js +38 -0
  16. package/dist/apps/cli/src/analyzer/generators/report.generator.js +123 -0
  17. package/dist/apps/cli/src/analyzer/index.js +44 -0
  18. package/dist/apps/cli/src/analyzer/scanners/project.scanner.js +92 -0
  19. package/dist/apps/cli/src/analyzer/validators/contracts.validator.js +42 -0
  20. package/dist/apps/cli/src/analyzer/validators/maintainability.validator.js +40 -0
  21. package/dist/apps/cli/src/analyzer/validators/observability.validator.js +39 -0
  22. package/dist/apps/cli/src/analyzer/validators/performance.validator.js +42 -0
  23. package/dist/apps/cli/src/analyzer/validators/security.validator.js +66 -0
  24. package/dist/apps/cli/src/analyzer/validators/soc.validator.js +75 -0
  25. package/dist/apps/cli/src/auth/secure-auth.js +312 -0
  26. package/dist/apps/cli/src/commands/analyze.js +286 -0
  27. package/dist/apps/cli/src/commands/auth-new.js +37 -0
  28. package/dist/apps/cli/src/commands/auth.js +122 -0
  29. package/dist/apps/cli/src/commands/config.js +49 -0
  30. package/dist/apps/cli/src/commands/deploy.js +6 -0
  31. package/dist/apps/cli/src/commands/init.js +47 -0
  32. package/dist/apps/cli/src/commands/logout.js +23 -0
  33. package/dist/apps/cli/src/commands/plugins.js +21 -0
  34. package/dist/apps/cli/src/commands/status.js +80 -0
  35. package/dist/apps/cli/src/commands/sync.js +6 -0
  36. package/dist/apps/cli/src/commands/whoami.js +115 -0
  37. package/dist/apps/cli/src/components/Dashboard.js +168 -0
  38. package/dist/apps/cli/src/components/DeltaApp.js +56 -0
  39. package/dist/apps/cli/src/components/UnifiedManager.js +324 -0
  40. package/dist/apps/cli/src/core/audit.js +184 -0
  41. package/dist/apps/cli/src/core/completion.js +294 -0
  42. package/dist/apps/cli/src/core/contracts.js +6 -0
  43. package/dist/apps/cli/src/core/engine.js +124 -0
  44. package/dist/apps/cli/src/core/exit-codes.js +71 -0
  45. package/dist/apps/cli/src/core/hooks.js +181 -0
  46. package/dist/apps/cli/src/core/index.js +7 -0
  47. package/dist/apps/cli/src/core/policy.js +115 -0
  48. package/dist/apps/cli/src/core/profiles.js +161 -0
  49. package/dist/apps/cli/src/core/wizard.js +203 -0
  50. package/dist/apps/cli/src/index.js +636 -0
  51. package/dist/apps/cli/src/interactive/index.js +11 -0
  52. package/dist/apps/cli/src/plugins/GitStatusPlugin.js +99 -0
  53. package/dist/apps/cli/src/providers/ai-provider.js +74 -0
  54. package/dist/apps/cli/src/providers/local-provider.js +302 -0
  55. package/dist/apps/cli/src/providers/remote-provider.js +100 -0
  56. package/dist/apps/cli/src/types/api.js +3 -0
  57. package/dist/apps/cli/src/ui.js +219 -0
  58. package/dist/apps/cli/src/welcome.js +81 -0
  59. package/dist/auth/secure-auth.js +418 -0
  60. package/dist/bundle.js +45 -46
  61. package/dist/commands/analyze.js +363 -0
  62. package/dist/commands/auth-new.js +37 -0
  63. package/dist/commands/auth.js +133 -0
  64. package/dist/commands/config.js +50 -0
  65. package/dist/commands/deploy.js +6 -0
  66. package/dist/commands/init.js +47 -0
  67. package/dist/commands/logout.js +30 -0
  68. package/dist/commands/plugins.js +21 -0
  69. package/dist/commands/status.js +82 -0
  70. package/dist/commands/sync.js +6 -0
  71. package/dist/commands/whoami.js +71 -0
  72. package/dist/components/Dashboard.js +169 -0
  73. package/dist/components/DeltaApp.js +57 -0
  74. package/dist/components/UnifiedManager.js +344 -0
  75. package/dist/core/audit.js +184 -0
  76. package/dist/core/completion.js +294 -0
  77. package/dist/core/contracts.js +6 -0
  78. package/dist/core/engine.js +124 -0
  79. package/dist/core/exit-codes.js +71 -0
  80. package/dist/core/hooks.js +181 -0
  81. package/dist/core/index.js +7 -0
  82. package/dist/core/policy.js +115 -0
  83. package/dist/core/profiles.js +161 -0
  84. package/dist/core/wizard.js +203 -0
  85. package/dist/index.js +387 -0
  86. package/dist/interactive/index.js +11 -0
  87. package/dist/packages/domain/src/constitution/contracts/index.js +43 -0
  88. package/dist/packages/domain/src/constitution/contracts/ts.rules.js +268 -0
  89. package/dist/packages/domain/src/constitution/index.js +139 -0
  90. package/dist/packages/domain/src/constitution/maintainability/index.js +43 -0
  91. package/dist/packages/domain/src/constitution/maintainability/ts.rules.js +344 -0
  92. package/dist/packages/domain/src/constitution/observability/index.js +43 -0
  93. package/dist/packages/domain/src/constitution/observability/ts.rules.js +307 -0
  94. package/dist/packages/domain/src/constitution/performance/index.js +43 -0
  95. package/dist/packages/domain/src/constitution/performance/ts.rules.js +325 -0
  96. package/dist/packages/domain/src/constitution/security/index.js +50 -0
  97. package/dist/packages/domain/src/constitution/security/ts.rules.js +267 -0
  98. package/dist/packages/domain/src/constitution/soc/index.js +43 -0
  99. package/dist/packages/domain/src/constitution/soc/ts.rules.js +360 -0
  100. package/dist/packages/domain/src/contracts/analysis.contract.js +18 -0
  101. package/dist/packages/domain/src/contracts/index.js +7 -0
  102. package/dist/packages/domain/src/contracts/projects.contract.js +18 -0
  103. package/dist/packages/domain/src/control/registry/rules.registry.js +29 -0
  104. package/dist/packages/domain/src/control/schemas/policies.js +6 -0
  105. package/dist/packages/domain/src/core/analysis/discovery.js +163 -0
  106. package/dist/packages/domain/src/core/analysis/engine.contract.js +298 -0
  107. package/dist/packages/domain/src/core/analysis/engine.js +77 -0
  108. package/dist/packages/domain/src/core/analysis/index.js +14 -0
  109. package/dist/packages/domain/src/core/analysis/orchestrator.js +242 -0
  110. package/dist/packages/domain/src/core/comparison/engine.js +29 -0
  111. package/dist/packages/domain/src/core/comparison/index.js +5 -0
  112. package/dist/packages/domain/src/core/documentation/index.js +5 -0
  113. package/dist/packages/domain/src/core/documentation/pipeline.js +41 -0
  114. package/dist/packages/domain/src/core/fs/adapter.js +111 -0
  115. package/dist/packages/domain/src/core/fs/index.js +5 -0
  116. package/dist/packages/domain/src/core/parser/unified-parser.js +166 -0
  117. package/dist/packages/domain/src/index.js +33 -0
  118. package/dist/packages/domain/src/plugin/registry.js +195 -0
  119. package/dist/packages/domain/src/plugin/types.js +6 -0
  120. package/dist/packages/domain/src/ports/analysis.engine.js +7 -0
  121. package/dist/packages/domain/src/ports/audit.logger.js +7 -0
  122. package/dist/packages/domain/src/ports/project.repository.js +7 -0
  123. package/dist/packages/domain/src/rules/index.js +134 -0
  124. package/dist/packages/domain/src/types/analysis.js +6 -0
  125. package/dist/packages/domain/src/types/errors.js +53 -0
  126. package/dist/packages/domain/src/types/fs.js +6 -0
  127. package/dist/packages/domain/src/types/index.js +7 -0
  128. package/dist/plugins/GitStatusPlugin.js +93 -0
  129. package/dist/providers/ai-provider.js +74 -0
  130. package/dist/providers/local-provider.js +304 -0
  131. package/dist/providers/remote-provider.js +100 -0
  132. package/dist/types/api.js +3 -0
  133. package/dist/ui.js +219 -0
  134. package/dist/welcome.js +81 -0
  135. package/package.json +18 -18
@@ -0,0 +1,312 @@
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 keytar from 'keytar';
11
+ import fetch from 'node-fetch';
12
+ import open from 'open';
13
+ import { createServer } from 'http';
14
+ import { URL } from 'url';
15
+ import crypto from 'crypto';
16
+ import chalk from 'chalk';
17
+ import { printSuccess, printError, printInfo, startSpinner, stopSpinner } from '../ui.js';
18
+ const SERVICE_NAME = 'delta-cli';
19
+ const ACCOUNT_NAME = 'user-token';
20
+ const REFRESH_ACCOUNT = 'refresh-token';
21
+ // PKCE Code Generator
22
+ function generatePKCE() {
23
+ const verifier = crypto.randomBytes(32).toString('base64url');
24
+ const challenge = crypto
25
+ .createHash('sha256')
26
+ .update(verifier)
27
+ .digest('base64url');
28
+ return { verifier, challenge };
29
+ }
30
+ // Secure Token Storage
31
+ export class SecureTokenStore {
32
+ static async saveAccessToken(token) {
33
+ await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, token);
34
+ }
35
+ static async getAccessToken() {
36
+ return await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);
37
+ }
38
+ static async saveRefreshToken(token) {
39
+ await keytar.setPassword(SERVICE_NAME, REFRESH_ACCOUNT, token);
40
+ }
41
+ static async getRefreshToken() {
42
+ return await keytar.getPassword(SERVICE_NAME, REFRESH_ACCOUNT);
43
+ }
44
+ static async clearTokens() {
45
+ await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
46
+ await keytar.deletePassword(SERVICE_NAME, REFRESH_ACCOUNT);
47
+ }
48
+ static async hasTokens() {
49
+ const token = await this.getAccessToken();
50
+ return !!token;
51
+ }
52
+ }
53
+ // OAuth2 Flow Handler
54
+ export async function startOAuthFlow(method = 'oauth') {
55
+ startSpinner('Starting OAuth2 authentication flow...');
56
+ try {
57
+ const { verifier, challenge } = generatePKCE();
58
+ const redirectUri = 'http://localhost:3456/callback';
59
+ const state = crypto.randomBytes(16).toString('hex');
60
+ // Build OAuth URL
61
+ const oauthUrl = new URL('https://api.delta.dev/auth/authorize');
62
+ oauthUrl.searchParams.set('response_type', 'code');
63
+ oauthUrl.searchParams.set('client_id', 'delta-cli');
64
+ oauthUrl.searchParams.set('redirect_uri', redirectUri);
65
+ oauthUrl.searchParams.set('code_challenge', challenge);
66
+ oauthUrl.searchParams.set('code_challenge_method', 'S256');
67
+ oauthUrl.searchParams.set('state', state);
68
+ oauthUrl.searchParams.set('scope', 'read write analyze');
69
+ if (method === 'github') {
70
+ oauthUrl.searchParams.set('provider', 'github');
71
+ }
72
+ else if (method === 'google') {
73
+ oauthUrl.searchParams.set('provider', 'google');
74
+ }
75
+ stopSpinner(true, 'OAuth server ready');
76
+ // Open browser
77
+ printInfo('Opening browser for authentication...');
78
+ await open(oauthUrl.toString());
79
+ // Start local server to capture callback
80
+ const authCode = await new Promise((resolve, reject) => {
81
+ const server = createServer(async (req, res) => {
82
+ const url = new URL(req.url || '/', `http://localhost:3456`);
83
+ if (url.pathname === '/callback') {
84
+ const code = url.searchParams.get('code');
85
+ const returnedState = url.searchParams.get('state');
86
+ const error = url.searchParams.get('error');
87
+ if (error) {
88
+ res.writeHead(400, { 'Content-Type': 'text/html' });
89
+ res.end(`
90
+ <html>
91
+ <body style="font-family: system-ui; text-align: center; padding: 50px;">
92
+ <h1 style="color: #e74c3c;">❌ Authentication Failed</h1>
93
+ <p>${error}</p>
94
+ <p>You can close this window.</p>
95
+ </body>
96
+ </html>
97
+ `);
98
+ reject(new Error(error));
99
+ server.close();
100
+ return;
101
+ }
102
+ if (!code || returnedState !== state) {
103
+ res.writeHead(400, { 'Content-Type': 'text/html' });
104
+ res.end(`
105
+ <html>
106
+ <body style="font-family: system-ui; text-align: center; padding: 50px;">
107
+ <h1 style="color: #e74c3c;">❌ Invalid Response</h1>
108
+ <p>You can close this window.</p>
109
+ </body>
110
+ </html>
111
+ `);
112
+ reject(new Error('Invalid OAuth response'));
113
+ server.close();
114
+ return;
115
+ }
116
+ res.writeHead(200, { 'Content-Type': 'text/html' });
117
+ res.end(`
118
+ <html>
119
+ <head>
120
+ <style>
121
+ body {
122
+ font-family: system-ui, -apple-system, sans-serif;
123
+ text-align: center;
124
+ padding: 50px;
125
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
126
+ color: white;
127
+ min-height: 100vh;
128
+ display: flex;
129
+ align-items: center;
130
+ justify-content: center;
131
+ }
132
+ .container {
133
+ background: rgba(255,255,255,0.1);
134
+ backdrop-filter: blur(10px);
135
+ padding: 40px;
136
+ border-radius: 20px;
137
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
138
+ }
139
+ h1 { margin: 0 0 20px; font-size: 48px; }
140
+ .check { font-size: 64px; margin-bottom: 20px; }
141
+ p { font-size: 18px; opacity: 0.9; }
142
+ </style>
143
+ </head>
144
+ <body>
145
+ <div class="container">
146
+ <div class="check">✓</div>
147
+ <h1>Successfully Signed In!</h1>
148
+ <p>You can close this window and return to the terminal.</p>
149
+ </div>
150
+ </body>
151
+ </html>
152
+ `);
153
+ resolve(code);
154
+ server.close();
155
+ }
156
+ });
157
+ server.listen(3456, () => {
158
+ console.log(chalk.dim(' Waiting for authentication...'));
159
+ });
160
+ // Timeout after 5 minutes
161
+ setTimeout(() => {
162
+ server.close();
163
+ reject(new Error('Authentication timeout'));
164
+ }, 5 * 60 * 1000);
165
+ });
166
+ // Exchange code for tokens
167
+ startSpinner('Exchanging authorization code...');
168
+ const tokenResponse = await fetch('https://api.delta.dev/auth/token', {
169
+ method: 'POST',
170
+ headers: { 'Content-Type': 'application/json' },
171
+ body: JSON.stringify({
172
+ grant_type: 'authorization_code',
173
+ code: authCode,
174
+ redirect_uri: redirectUri,
175
+ client_id: 'delta-cli',
176
+ code_verifier: verifier,
177
+ }),
178
+ });
179
+ if (!tokenResponse.ok) {
180
+ throw new Error('Failed to exchange authorization code');
181
+ }
182
+ const tokens = await tokenResponse.json();
183
+ // Store tokens securely
184
+ await SecureTokenStore.saveAccessToken(tokens.access_token);
185
+ await SecureTokenStore.saveRefreshToken(tokens.refresh_token);
186
+ stopSpinner(true, 'Authentication successful!');
187
+ printSuccess('Successfully authenticated with Delta Cloud');
188
+ printInfo(`Token expires in ${tokens.expires_in} seconds`);
189
+ return true;
190
+ }
191
+ catch (error) {
192
+ stopSpinner(false, 'Authentication failed');
193
+ printError('OAuth2 authentication failed', error.message);
194
+ return false;
195
+ }
196
+ }
197
+ // PAT Authentication for CI/CD
198
+ export async function authenticateWithPAT(token) {
199
+ startSpinner('Validating Personal Access Token...');
200
+ try {
201
+ // Validate token
202
+ const response = await fetch('https://api.delta.dev/auth/verify', {
203
+ headers: {
204
+ 'Authorization': `Bearer ${token}`,
205
+ },
206
+ });
207
+ if (!response.ok) {
208
+ throw new Error('Invalid token');
209
+ }
210
+ const data = await response.json();
211
+ // Store token
212
+ await SecureTokenStore.saveAccessToken(token);
213
+ stopSpinner(true, 'Token validated');
214
+ printSuccess(`Authenticated as ${data.user.email}`);
215
+ return true;
216
+ }
217
+ catch (error) {
218
+ stopSpinner(false, 'Token validation failed');
219
+ printError('PAT authentication failed', error.message);
220
+ return false;
221
+ }
222
+ }
223
+ // Token Refresh
224
+ export async function refreshAccessToken() {
225
+ const refreshToken = await SecureTokenStore.getRefreshToken();
226
+ if (!refreshToken) {
227
+ printError('No refresh token available');
228
+ return false;
229
+ }
230
+ try {
231
+ const response = await fetch('https://api.delta.dev/auth/refresh', {
232
+ method: 'POST',
233
+ headers: { 'Content-Type': 'application/json' },
234
+ body: JSON.stringify({
235
+ grant_type: 'refresh_token',
236
+ refresh_token: refreshToken,
237
+ client_id: 'delta-cli',
238
+ }),
239
+ });
240
+ if (!response.ok) {
241
+ throw new Error('Token refresh failed');
242
+ }
243
+ const tokens = await response.json();
244
+ await SecureTokenStore.saveAccessToken(tokens.access_token);
245
+ if (tokens.refresh_token) {
246
+ await SecureTokenStore.saveRefreshToken(tokens.refresh_token);
247
+ }
248
+ return true;
249
+ }
250
+ catch (error) {
251
+ printError('Failed to refresh token', error.message);
252
+ return false;
253
+ }
254
+ }
255
+ // Logout
256
+ export async function logout() {
257
+ startSpinner('Clearing credentials...');
258
+ try {
259
+ await SecureTokenStore.clearTokens();
260
+ stopSpinner(true, 'Logged out');
261
+ printSuccess('Successfully logged out');
262
+ }
263
+ catch (error) {
264
+ stopSpinner(false, 'Logout failed');
265
+ printError('Failed to clear credentials', error.message);
266
+ }
267
+ }
268
+ // Auth Status
269
+ export async function getAuthStatus() {
270
+ const token = await SecureTokenStore.getAccessToken();
271
+ if (!token) {
272
+ return { authenticated: false };
273
+ }
274
+ try {
275
+ const response = await fetch('https://api.delta.dev/auth/me', {
276
+ headers: {
277
+ 'Authorization': `Bearer ${token}`,
278
+ },
279
+ });
280
+ if (!response.ok) {
281
+ // Try to refresh token
282
+ const refreshed = await refreshAccessToken();
283
+ if (!refreshed) {
284
+ return { authenticated: false };
285
+ }
286
+ // Retry with new token
287
+ const newToken = await SecureTokenStore.getAccessToken();
288
+ const retryResponse = await fetch('https://api.delta.dev/auth/me', {
289
+ headers: {
290
+ 'Authorization': `Bearer ${newToken}`,
291
+ },
292
+ });
293
+ if (!retryResponse.ok) {
294
+ return { authenticated: false };
295
+ }
296
+ const userData = await retryResponse.json();
297
+ return {
298
+ authenticated: true,
299
+ user: userData,
300
+ };
301
+ }
302
+ const userData = await response.json();
303
+ return {
304
+ authenticated: true,
305
+ user: userData,
306
+ };
307
+ }
308
+ catch {
309
+ return { authenticated: false };
310
+ }
311
+ }
312
+ //# sourceMappingURL=secure-auth.js.map
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Analyze Command - Run local analysis with optional upload to cloud
3
+ */
4
+ import * as fs from 'fs/promises';
5
+ import * as path from 'path';
6
+ import * as crypto from 'crypto';
7
+ import { loadConfig } from './auth.js';
8
+ export async function analyzeCommand(projectPath, options) {
9
+ const targetPath = path.resolve(projectPath);
10
+ console.log('🔍 Running analysis...');
11
+ console.log(` Path: ${targetPath}`);
12
+ // Simple file discovery
13
+ const files = await discoverFiles(targetPath, {
14
+ include: options.include ? [options.include] : ['**/*.{ts,tsx,js,jsx}'],
15
+ exclude: options.exclude ? [options.exclude].concat(['node_modules', 'dist', '.git']) : ['node_modules', 'dist', '.git'],
16
+ maxSize: parseInt(options.maxSize, 10),
17
+ });
18
+ console.log(` Found ${files.length} files to analyze`);
19
+ // Run analysis
20
+ const startTime = Date.now();
21
+ const summary = {
22
+ totalFiles: files.length,
23
+ successfulParses: files.length,
24
+ failedParses: 0,
25
+ totalLines: 0,
26
+ totalFunctions: 0,
27
+ totalClasses: 0,
28
+ totalDuration: 0,
29
+ errors: [],
30
+ };
31
+ const findings = [];
32
+ for (const file of files) {
33
+ try {
34
+ const content = await fs.readFile(file, 'utf-8');
35
+ const lines = content.split('\n');
36
+ summary.totalLines += lines.length;
37
+ // Simple heuristics for demo
38
+ if (content.includes('function '))
39
+ summary.totalFunctions++;
40
+ if (content.includes('class '))
41
+ summary.totalClasses++;
42
+ // Find potential issues
43
+ if (content.includes('eval(')) {
44
+ findings.push({
45
+ file: path.relative(targetPath, file),
46
+ type: 'security',
47
+ severity: 'high',
48
+ message: 'Use of eval() detected',
49
+ });
50
+ }
51
+ if (content.includes('console.log')) {
52
+ findings.push({
53
+ file: path.relative(targetPath, file),
54
+ type: 'maintainability',
55
+ severity: 'low',
56
+ message: 'Console.log statement found',
57
+ });
58
+ }
59
+ }
60
+ catch (error) {
61
+ summary.failedParses++;
62
+ summary.successfulParses--;
63
+ summary.errors.push({
64
+ file: path.relative(targetPath, file),
65
+ error: error instanceof Error ? error.message : String(error),
66
+ });
67
+ }
68
+ }
69
+ summary.totalDuration = Date.now() - startTime;
70
+ // Build report
71
+ const report = {
72
+ schema_version: '1.0',
73
+ engine_version: '0.5.0',
74
+ created_at: new Date().toISOString(),
75
+ project: {
76
+ name: options.projectName || path.basename(targetPath),
77
+ root_fingerprint: '',
78
+ path: targetPath,
79
+ },
80
+ tools_used: [
81
+ { id: 'delta-core', version: '0.5.0', adapter_version: '0.5.0' },
82
+ ],
83
+ summary: {
84
+ total: findings.length,
85
+ high: findings.filter(f => f.severity === 'high').length,
86
+ medium: findings.filter(f => f.severity === 'medium').length,
87
+ low: findings.filter(f => f.severity === 'low').length,
88
+ },
89
+ findings,
90
+ metadata: summary,
91
+ };
92
+ // Save report to file
93
+ const outputDir = path.resolve(options.output);
94
+ await fs.mkdir(outputDir, { recursive: true });
95
+ const reportFile = path.join(outputDir, `report-${Date.now()}.json`);
96
+ await fs.writeFile(reportFile, JSON.stringify(report, null, 2));
97
+ console.log('\n✅ Analysis complete');
98
+ console.log(` Files analyzed: ${summary.totalFiles}`);
99
+ console.log(` Total lines: ${summary.totalLines.toLocaleString()}`);
100
+ console.log(` Functions: ${summary.totalFunctions}`);
101
+ console.log(` Classes: ${summary.totalClasses}`);
102
+ console.log(` Duration: ${(summary.totalDuration / 1000).toFixed(2)}s`);
103
+ console.log(` Findings: ${findings.length} (${report.summary.high} high, ${report.summary.medium} medium, ${report.summary.low} low)`);
104
+ console.log(` Report saved: ${reportFile}`);
105
+ // Upload to cloud if requested
106
+ if (options.upload) {
107
+ console.log('\n☁️ Uploading report to Delta cloud...');
108
+ await uploadReport(report);
109
+ }
110
+ }
111
+ async function uploadReport(report) {
112
+ const config = await loadConfig();
113
+ if (!config.pat) {
114
+ console.error('❌ Error: Not authenticated. Run: delta login --token <pat>');
115
+ process.exit(1);
116
+ }
117
+ const apiUrl = process.env.DELTA_API_URL || config.apiUrl || 'https://api.delta.dev';
118
+ const reportJson = JSON.stringify(report);
119
+ const reportHash = crypto.createHash('sha256').update(reportJson).digest('hex');
120
+ try {
121
+ // Step 1: Init upload session
122
+ const initResponse = await fetch(`${apiUrl}/api/upload/init`, {
123
+ method: 'POST',
124
+ headers: {
125
+ 'Authorization': `Bearer ${config.pat}`,
126
+ 'Content-Type': 'application/json',
127
+ 'Idempotency-Key': crypto.randomUUID(),
128
+ },
129
+ body: JSON.stringify({
130
+ schemaVersion: '1.0',
131
+ engineVersion: '0.5.0',
132
+ reportHash: reportHash,
133
+ reportSizeBytes: Buffer.byteLength(reportJson),
134
+ toolsUsed: [{ id: 'delta-core', version: '0.5.0', adapterVersion: '0.5.0' }],
135
+ }),
136
+ });
137
+ if (!initResponse.ok) {
138
+ let payload = undefined;
139
+ try {
140
+ payload = await initResponse.json();
141
+ }
142
+ catch {
143
+ payload = await initResponse.text().catch(() => undefined);
144
+ }
145
+ const details = payload?.error?.details ? `\nDetails: ${JSON.stringify(payload.error.details)}` : '';
146
+ const message = payload?.error?.message || `Init failed: ${initResponse.status}`;
147
+ throw new Error(message + details);
148
+ }
149
+ const initData = await initResponse.json();
150
+ if (!initData.success) {
151
+ throw new Error(initData.error?.message || 'Upload init failed');
152
+ }
153
+ const uploadId = initData.data.uploadId;
154
+ const secret = initData.data.signing.secret;
155
+ console.log(` Upload session: ${uploadId}`);
156
+ // Step 2: Sign and upload report
157
+ const signature = crypto.createHmac('sha256', secret).update(reportJson).digest('hex');
158
+ const uploadResponse = await fetch(`${apiUrl}/api/upload/${uploadId}`, {
159
+ method: 'PUT',
160
+ headers: {
161
+ 'Authorization': `Bearer ${config.pat}`,
162
+ 'Content-Type': 'application/json',
163
+ },
164
+ body: JSON.stringify({
165
+ report: Object.assign({}, report, {
166
+ integrity: {
167
+ report_hash: reportHash,
168
+ signature: signature,
169
+ algorithm: 'HMAC-SHA256',
170
+ },
171
+ }),
172
+ }),
173
+ });
174
+ if (!uploadResponse.ok) {
175
+ let payload = undefined;
176
+ try {
177
+ payload = await uploadResponse.json();
178
+ }
179
+ catch {
180
+ payload = await uploadResponse.text().catch(() => undefined);
181
+ }
182
+ const details = payload?.error?.details ? `\nDetails: ${JSON.stringify(payload.error.details)}` : '';
183
+ const message = payload?.error?.message || `Upload failed: ${uploadResponse.status}`;
184
+ throw new Error(message + details);
185
+ }
186
+ console.log(' Report uploaded');
187
+ // Step 3: Finalize
188
+ const finalizeResponse = await fetch(`${apiUrl}/api/upload/${uploadId}/finalize`, {
189
+ method: 'POST',
190
+ headers: {
191
+ 'Authorization': `Bearer ${config.pat}`,
192
+ 'Content-Type': 'application/json',
193
+ 'Idempotency-Key': crypto.randomUUID(),
194
+ },
195
+ body: '{}',
196
+ });
197
+ if (!finalizeResponse.ok) {
198
+ let payload = undefined;
199
+ try {
200
+ payload = await finalizeResponse.json();
201
+ }
202
+ catch {
203
+ payload = await finalizeResponse.text().catch(() => undefined);
204
+ }
205
+ const details = payload?.error?.details ? `\nDetails: ${JSON.stringify(payload.error.details)}` : '';
206
+ const message = payload?.error?.message || `Finalize failed: ${finalizeResponse.status}`;
207
+ throw new Error(message + details);
208
+ }
209
+ const finalizeData = await finalizeResponse.json();
210
+ if (!finalizeData.success) {
211
+ throw new Error(finalizeData.error?.message || 'Upload finalize failed');
212
+ }
213
+ console.log(`✅ Report finalized: ${finalizeData.data.reportId}`);
214
+ console.log(` Quota updated`);
215
+ }
216
+ catch (error) {
217
+ console.error('❌ Upload failed:', error instanceof Error ? error.message : String(error));
218
+ process.exit(1);
219
+ }
220
+ }
221
+ async function discoverFiles(rootPath, options) {
222
+ const files = [];
223
+ async function walk(dir) {
224
+ const entries = await fs.readdir(dir, { withFileTypes: true });
225
+ for (const entry of entries) {
226
+ const fullPath = path.join(dir, entry.name);
227
+ if (entry.isDirectory()) {
228
+ // Skip excluded directories
229
+ if (options.exclude.some(e => entry.name.includes(e)))
230
+ continue;
231
+ await walk(fullPath);
232
+ }
233
+ else if (entry.isFile()) {
234
+ // Check include patterns - support glob patterns like **/*.{ts,tsx}
235
+ const shouldInclude = options.include.some(pattern => {
236
+ // Handle brace expansion: **/*.{ts,tsx} → match **/*.ts or **/*.tsx
237
+ if (pattern.includes('{') && pattern.includes('}')) {
238
+ const braceMatch = pattern.match(/\{([^}]+)\}/);
239
+ if (braceMatch) {
240
+ const extensions = braceMatch[1].split(',');
241
+ const basePattern = pattern.replace(/\{[^}]+\}/, '');
242
+ return extensions.some(ext => {
243
+ const fullPattern = basePattern + ext;
244
+ return matchGlob(fullPath, fullPattern);
245
+ });
246
+ }
247
+ }
248
+ return matchGlob(fullPath, pattern);
249
+ });
250
+ if (shouldInclude) {
251
+ // Check file size
252
+ try {
253
+ const stats = await fs.stat(fullPath);
254
+ if (stats.size <= options.maxSize) {
255
+ files.push(fullPath);
256
+ }
257
+ }
258
+ catch {
259
+ // Skip files we can't stat
260
+ }
261
+ }
262
+ }
263
+ }
264
+ }
265
+ await walk(rootPath);
266
+ return files;
267
+ }
268
+ function matchGlob(filePath, pattern) {
269
+ // Convert glob pattern to regex
270
+ // **/*.ts → matches any .ts file in any directory
271
+ // *.ts → matches .ts files in current directory
272
+ const fileName = filePath.split(/[/\\]/).pop() || '';
273
+ if (pattern.includes('**')) {
274
+ // Match any directory depth
275
+ const ext = pattern.replace('**/*', '');
276
+ return fileName.endsWith(ext);
277
+ }
278
+ else if (pattern.includes('*')) {
279
+ // Simple wildcard
280
+ const ext = pattern.replace('*', '');
281
+ return fileName.endsWith(ext);
282
+ }
283
+ // Exact match
284
+ return filePath.includes(pattern);
285
+ }
286
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1,37 @@
1
+ import chalk from 'chalk';
2
+ import { startOAuthFlow, authenticateWithPAT, SecureTokenStore } from '../auth/secure-auth.js';
3
+ import { printSuccess, printInfo } from '../ui.js';
4
+ import prompts from 'prompts';
5
+ export async function loginCommand(options) {
6
+ // Check if already authenticated
7
+ if (await SecureTokenStore.hasTokens()) {
8
+ const overwrite = await prompts({
9
+ type: 'confirm',
10
+ name: 'value',
11
+ message: chalk.yellow('Already authenticated. Overwrite existing credentials?'),
12
+ initial: false,
13
+ });
14
+ if (!overwrite.value) {
15
+ printInfo('Login cancelled');
16
+ return;
17
+ }
18
+ }
19
+ if (options.token) {
20
+ // PAT authentication
21
+ await authenticateWithPAT(options.token);
22
+ }
23
+ else {
24
+ // OAuth2 flow
25
+ const method = options.method || 'oauth';
26
+ const success = await startOAuthFlow(method);
27
+ if (success) {
28
+ printSuccess('You are now logged in!');
29
+ printInfo('Run "delta whoami" to see your account info');
30
+ }
31
+ }
32
+ }
33
+ export async function logoutCommand() {
34
+ const { logout } = await import('../auth/secure-auth.js');
35
+ await logout();
36
+ }
37
+ //# sourceMappingURL=auth-new.js.map