snow-flow 8.5.7 → 8.5.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.
Files changed (56) hide show
  1. package/CLAUDE.md +52 -4
  2. package/README.md +99 -30
  3. package/{OPENCODE-SETUP.md → SNOWCODE-SETUP.md} +47 -21
  4. package/{OPENCODE-TROUBLESHOOTING.md → SNOWCODE-TROUBLESHOOTING.md} +18 -18
  5. package/dist/cli/auth.d.ts.map +1 -1
  6. package/dist/cli/auth.js +526 -280
  7. package/dist/cli/auth.js.map +1 -1
  8. package/dist/cli.d.ts +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +528 -250
  11. package/dist/cli.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/templates/claude-md-template.d.ts +1 -1
  15. package/dist/templates/claude-md-template.js +1 -1
  16. package/dist/templates/readme-template.d.ts +1 -1
  17. package/dist/templates/readme-template.js +18 -18
  18. package/dist/utils/artifact-local-sync.d.ts +1 -1
  19. package/dist/utils/artifact-local-sync.js +4 -4
  20. package/dist/utils/snow-oauth.d.ts +11 -5
  21. package/dist/utils/snow-oauth.d.ts.map +1 -1
  22. package/dist/utils/snow-oauth.js +337 -90
  23. package/dist/utils/snow-oauth.js.map +1 -1
  24. package/dist/utils/{opencode-output-interceptor.d.ts → snowcode-output-interceptor.d.ts} +7 -7
  25. package/dist/utils/{opencode-output-interceptor.d.ts.map → snowcode-output-interceptor.d.ts.map} +1 -1
  26. package/dist/utils/{opencode-output-interceptor.js → snowcode-output-interceptor.js} +10 -10
  27. package/dist/utils/{opencode-output-interceptor.js.map → snowcode-output-interceptor.js.map} +1 -1
  28. package/package.json +20 -9
  29. package/scripts/{start-opencode.sh → start-snowcode.sh} +28 -28
  30. package/scripts/verify-snowcode-fork.sh +141 -0
  31. package/templates/snowcode-package.json +5 -0
  32. package/THEMES.md +0 -223
  33. package/bin/opencode +0 -17
  34. package/dist/mcp/servicenow-mcp-unified/config/tool-definitions.json +0 -3935
  35. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_automation_discover.js +0 -164
  36. package/dist/mcp/servicenow-mcp-unified/tools/deployment/snow_artifact_transfer.js +0 -282
  37. package/dist/mcp/servicenow-mcp-unified/tools/filters/snow_build_filter.js +0 -171
  38. package/dist/mcp/servicenow-mcp-unified/tools/formatters/snow_format_value.js +0 -164
  39. package/dist/mcp/servicenow-mcp-unified/tools/knowledge/index.js.bak +0 -45
  40. package/dist/mcp/servicenow-mcp-unified/tools/local-sync/snow_artifact_sync.js +0 -172
  41. package/dist/mcp/servicenow-mcp-unified/tools/system-properties/index.js +0 -36
  42. package/dist/mcp/servicenow-mcp-unified/tools/ui-builder/snow_discover_uib.js +0 -296
  43. package/dist/mcp/servicenow-mcp-unified/tools/workspace/snow_create_ux_component.js +0 -292
  44. package/dist/memory/session-memory.d.ts +0 -80
  45. package/dist/memory/session-memory.d.ts.map +0 -1
  46. package/dist/memory/session-memory.js +0 -468
  47. package/dist/memory/session-memory.js.map +0 -1
  48. package/dist/templates/opencode-agents-template.d.ts +0 -2
  49. package/dist/templates/opencode-agents-template.d.ts.map +0 -1
  50. package/dist/templates/opencode-agents-template.js +0 -469
  51. package/dist/templates/opencode-agents-template.js.map +0 -1
  52. package/scripts/bulk-optimize-tools.js +0 -486
  53. package/scripts/optimize-mcp-tools.ts +0 -410
  54. package/themes/README.md +0 -83
  55. package/themes/servicenow.json +0 -117
  56. /package/{opencode-config.example.json → snowcode-config.example.json} +0 -0
@@ -1,9 +1,42 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  /**
4
- * ServiceNow OAuth Authentication Utility with Dynamic Port
5
- * Handles OAuth2 flow for ServiceNow integration
4
+ * ServiceNow OAuth Authentication Utility with Code Paste Flow
5
+ * Handles OAuth2 flow for ServiceNow integration (Claude-style)
6
6
  */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
7
40
  var __importDefault = (this && this.__importDefault) || function (mod) {
8
41
  return (mod && mod.__esModule) ? mod : { "default": mod };
9
42
  };
@@ -18,6 +51,7 @@ const axios_1 = __importDefault(require("axios"));
18
51
  const https_1 = __importDefault(require("https"));
19
52
  const net_1 = __importDefault(require("net"));
20
53
  const crypto_1 = __importDefault(require("crypto"));
54
+ const prompts = __importStar(require("@clack/prompts"));
21
55
  const snow_flow_config_js_1 = require("../config/snow-flow-config.js");
22
56
  const unified_auth_store_js_1 = require("./unified-auth-store.js");
23
57
  const oauth_html_templates_js_1 = require("./oauth-html-templates.js");
@@ -44,7 +78,7 @@ class ServiceNowOAuth {
44
78
  }
45
79
  // Check if within rate limit
46
80
  if (this.tokenRequestCount >= this.MAX_TOKEN_REQUESTS_PER_WINDOW) {
47
- console.warn('🔒 Rate limit exceeded: Too many token requests. Please wait before retrying.');
81
+ prompts.log.warn('Rate limit exceeded: Too many token requests. Please wait before retrying.');
48
82
  return false;
49
83
  }
50
84
  this.tokenRequestCount++;
@@ -84,9 +118,6 @@ class ServiceNowOAuth {
84
118
  });
85
119
  });
86
120
  }
87
- /**
88
- * Initialize OAuth flow - opens browser and handles callback
89
- */
90
121
  /**
91
122
  * 🔧 CRIT-002 FIX: Normalize instance URL to prevent trailing slash 400 errors
92
123
  */
@@ -104,6 +135,109 @@ class ServiceNowOAuth {
104
135
  }
105
136
  return normalized;
106
137
  }
138
+ /**
139
+ * 🎯 NEW: Simplified OAuth flow with code paste (Claude-style)
140
+ * No local server required - user manually pastes authorization code
141
+ */
142
+ async authenticateWithCodePaste(instance, clientId, clientSecret) {
143
+ try {
144
+ // Normalize instance URL
145
+ const normalizedInstance = this.normalizeInstanceUrl(instance);
146
+ // Validate client secret
147
+ const secretValidation = this.validateClientSecret(clientSecret);
148
+ if (!secretValidation.valid) {
149
+ prompts.log.error(`Invalid OAuth Client Secret: ${secretValidation.reason}`);
150
+ prompts.log.info('To get a valid OAuth secret:');
151
+ prompts.log.message(' 1. Log into ServiceNow as admin');
152
+ prompts.log.message(' 2. Navigate to: System OAuth > Application Registry');
153
+ prompts.log.message(' 3. Create a new OAuth application');
154
+ prompts.log.message(' 4. Copy the generated Client Secret (long random string)');
155
+ return {
156
+ success: false,
157
+ error: secretValidation.reason
158
+ };
159
+ }
160
+ // For code paste flow, we use a special redirect URI that shows the code
161
+ const redirectUri = 'urn:ietf:wg:oauth:2.0:oob'; // Out-of-band redirect for manual code entry
162
+ // Store credentials
163
+ this.credentials = {
164
+ instance: normalizedInstance.replace('https://', '').replace('http://', ''),
165
+ clientId,
166
+ clientSecret,
167
+ redirectUri
168
+ };
169
+ prompts.log.step('Starting ServiceNow OAuth flow');
170
+ prompts.log.info(`Instance: ${normalizedInstance}`);
171
+ prompts.log.info(`Client ID: ${clientId}`);
172
+ // Generate state parameter and PKCE
173
+ this.stateParameter = this.generateState();
174
+ this.generatePKCE();
175
+ // Generate authorization URL
176
+ const authUrl = this.generateAuthUrl(this.credentials.instance, clientId, redirectUri);
177
+ prompts.log.message('');
178
+ prompts.log.step('Authorization URL generated');
179
+ prompts.log.message(`\n${authUrl}\n`);
180
+ prompts.log.warn(`Go to: ${authUrl}`);
181
+ prompts.log.message('');
182
+ const authCode = await prompts.text({
183
+ message: 'Paste the authorization code here',
184
+ placeholder: 'Enter the code from the browser after authorizing',
185
+ validate: (value) => {
186
+ if (!value || value.trim() === '')
187
+ return 'Authorization code is required';
188
+ if (value.length < 10)
189
+ return 'Code seems too short - please paste the full authorization code';
190
+ }
191
+ });
192
+ if (prompts.isCancel(authCode)) {
193
+ prompts.cancel('Authentication cancelled');
194
+ return {
195
+ success: false,
196
+ error: 'Authentication cancelled by user'
197
+ };
198
+ }
199
+ // Extract code if user pasted full URL
200
+ let code = authCode.trim();
201
+ if (code.includes('code=')) {
202
+ const match = code.match(/code=([^&]+)/);
203
+ if (match) {
204
+ code = match[1];
205
+ }
206
+ }
207
+ // Exchange code for tokens
208
+ const spinner = prompts.spinner();
209
+ spinner.start('Exchanging authorization code for tokens');
210
+ const tokenResult = await this.exchangeCodeForTokens(code);
211
+ if (tokenResult.success && tokenResult.accessToken) {
212
+ // Save tokens
213
+ await this.saveTokens({
214
+ accessToken: tokenResult.accessToken,
215
+ refreshToken: tokenResult.refreshToken || '',
216
+ expiresIn: tokenResult.expiresIn || 3600,
217
+ instance: this.credentials.instance,
218
+ clientId,
219
+ clientSecret
220
+ });
221
+ spinner.stop('Authentication successful');
222
+ prompts.log.success('Tokens saved securely');
223
+ }
224
+ else {
225
+ spinner.stop('Token exchange failed');
226
+ }
227
+ return tokenResult;
228
+ }
229
+ catch (error) {
230
+ const errorMessage = error instanceof Error ? error.message : String(error);
231
+ prompts.log.error(`Authentication failed: ${errorMessage}`);
232
+ return {
233
+ success: false,
234
+ error: errorMessage
235
+ };
236
+ }
237
+ }
238
+ /**
239
+ * Original OAuth flow with local server (fallback)
240
+ */
107
241
  async authenticate(instance, clientId, clientSecret) {
108
242
  try {
109
243
  // 🔧 CRIT-002 FIX: Apply URL normalization
@@ -111,12 +245,12 @@ class ServiceNowOAuth {
111
245
  // Validate client secret format
112
246
  const secretValidation = this.validateClientSecret(clientSecret);
113
247
  if (!secretValidation.valid) {
114
- console.error('❌ Invalid OAuth Client Secret:', secretValidation.reason);
115
- console.error('💡 To get a valid OAuth secret:');
116
- console.error(' 1. Log into ServiceNow as admin');
117
- console.error(' 2. Navigate to: System OAuth > Application Registry');
118
- console.error(' 3. Create a new OAuth application');
119
- console.error(' 4. Copy the generated Client Secret (long random string)');
248
+ prompts.log.error(`Invalid OAuth Client Secret: ${secretValidation.reason}`);
249
+ prompts.log.info('To get a valid OAuth secret:');
250
+ prompts.log.message(' 1. Log into ServiceNow as admin');
251
+ prompts.log.message(' 2. Navigate to: System OAuth > Application Registry');
252
+ prompts.log.message(' 3. Create a new OAuth application');
253
+ prompts.log.message(' 4. Copy the generated Client Secret (long random string)');
120
254
  return {
121
255
  success: false,
122
256
  error: secretValidation.reason
@@ -131,8 +265,8 @@ class ServiceNowOAuth {
131
265
  // Check if port is available
132
266
  const isPortAvailable = await this.checkPortAvailable(port);
133
267
  if (!isPortAvailable) {
134
- console.error(`❌ Port ${port} is already in use!`);
135
- console.error(`💡 Please close any application using port ${port} and try again.`);
268
+ prompts.log.error(`Port ${port} is already in use!`);
269
+ prompts.log.warn(`Please close any application using port ${port} and try again.`);
136
270
  return {
137
271
  success: false,
138
272
  error: `Port ${port} is already in use. Please free up the port and try again.`
@@ -145,18 +279,20 @@ class ServiceNowOAuth {
145
279
  clientSecret,
146
280
  redirectUri
147
281
  };
148
- console.log('🚀 Starting ServiceNow OAuth flow...');
149
- console.log(`📋 Instance: ${normalizedInstance}`);
150
- console.log(`🔐 Client ID: ${clientId}`);
151
- console.log(`🔗 Redirect URI: ${redirectUri}`);
282
+ prompts.log.step('Starting ServiceNow OAuth flow');
283
+ prompts.log.info(`Instance: ${normalizedInstance}`);
284
+ prompts.log.info(`Client ID: ${clientId}`);
285
+ prompts.log.info(`Redirect URI: ${redirectUri}`);
152
286
  // Generate state parameter for CSRF protection
153
287
  this.stateParameter = this.generateState();
154
288
  // Generate PKCE parameters
155
289
  this.generatePKCE();
156
290
  // Generate authorization URL
157
291
  const authUrl = this.generateAuthUrl(this.credentials.instance, clientId, redirectUri);
158
- console.log('\n🌐 Authorization URL generated:');
159
- console.log(`${authUrl}\n`);
292
+ prompts.log.step('Authorization URL generated');
293
+ prompts.log.message('');
294
+ prompts.log.message(`\n${authUrl}\n`);
295
+ prompts.log.message('');
160
296
  // Start local server to handle callback
161
297
  const authResult = await this.startCallbackServer(redirectUri, port);
162
298
  if (authResult.success && authResult.accessToken) {
@@ -169,14 +305,14 @@ class ServiceNowOAuth {
169
305
  clientId,
170
306
  clientSecret
171
307
  });
172
- console.log('\n✅ Authentication successful!');
173
- console.log('🔐 Tokens saved securely');
308
+ prompts.log.success('Authentication successful');
309
+ prompts.log.success('Tokens saved securely');
174
310
  }
175
311
  return authResult;
176
312
  }
177
313
  catch (error) {
178
314
  const errorMessage = error instanceof Error ? error.message : String(error);
179
- console.error('❌ Authentication failed:', errorMessage);
315
+ prompts.log.error(`Authentication failed: ${errorMessage}`);
180
316
  return {
181
317
  success: false,
182
318
  error: errorMessage
@@ -201,10 +337,14 @@ class ServiceNowOAuth {
201
337
  }
202
338
  /**
203
339
  * Start local HTTP server to handle OAuth callback
340
+ * Also supports manual callback URL paste as fallback
204
341
  */
205
342
  async startCallbackServer(redirectUri, port) {
206
- return new Promise((resolve) => {
343
+ return new Promise(async (resolve) => {
344
+ let resolved = false;
207
345
  const server = (0, http_1.createServer)(async (req, res) => {
346
+ if (resolved)
347
+ return;
208
348
  try {
209
349
  const url = new url_1.URL(req.url, `http://${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectHost}:${port}`);
210
350
  if (url.pathname === '/callback') {
@@ -215,6 +355,7 @@ class ServiceNowOAuth {
215
355
  if (state !== this.stateParameter) {
216
356
  res.writeHead(400, { 'Content-Type': 'text/html' });
217
357
  res.end(oauth_html_templates_js_1.OAuthTemplates.securityError);
358
+ resolved = true;
218
359
  server.close();
219
360
  resolve({
220
361
  success: false,
@@ -225,6 +366,7 @@ class ServiceNowOAuth {
225
366
  if (error) {
226
367
  res.writeHead(400, { 'Content-Type': 'text/html' });
227
368
  res.end(oauth_html_templates_js_1.OAuthTemplates.error(error));
369
+ resolved = true;
228
370
  server.close();
229
371
  resolve({
230
372
  success: false,
@@ -235,6 +377,7 @@ class ServiceNowOAuth {
235
377
  if (!code) {
236
378
  res.writeHead(400, { 'Content-Type': 'text/html' });
237
379
  res.end(oauth_html_templates_js_1.OAuthTemplates.missingCode);
380
+ resolved = true;
238
381
  server.close();
239
382
  resolve({
240
383
  success: false,
@@ -243,7 +386,9 @@ class ServiceNowOAuth {
243
386
  return;
244
387
  }
245
388
  // Exchange code for tokens
246
- console.log('🔄 Exchanging authorization code for tokens...');
389
+ resolved = true;
390
+ const spinner = prompts.spinner();
391
+ spinner.start('Exchanging authorization code for tokens');
247
392
  const tokenResult = await this.exchangeCodeForTokens(code);
248
393
  if (tokenResult.success) {
249
394
  res.writeHead(200, { 'Content-Type': 'text/html' });
@@ -264,9 +409,10 @@ class ServiceNowOAuth {
264
409
  }
265
410
  }
266
411
  catch (error) {
267
- console.error('Callback server error:', error);
412
+ prompts.log.error(`Callback server error: ${error}`);
268
413
  res.writeHead(500, { 'Content-Type': 'text/plain' });
269
414
  res.end('Internal Server Error');
415
+ resolved = true;
270
416
  server.close();
271
417
  resolve({
272
418
  success: false,
@@ -275,9 +421,9 @@ class ServiceNowOAuth {
275
421
  }
276
422
  });
277
423
  server.listen(port, () => {
278
- console.log(`🌐 OAuth callback server started on http://${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectHost}:${port}`);
279
- console.log('🚀 Please open the authorization URL in your browser...');
280
- console.log('Waiting for OAuth callback...');
424
+ prompts.log.step(`OAuth callback server started on http://${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectHost}:${port}`);
425
+ prompts.log.warn('Please open the authorization URL in your browser');
426
+ prompts.log.info('Waiting for OAuth callback...');
281
427
  // Auto-open browser if possible
282
428
  // Try to auto-open browser if not in headless environment
283
429
  const isCodespaces = process.env.CODESPACES === 'true';
@@ -310,7 +456,7 @@ class ServiceNowOAuth {
310
456
  }
311
457
  }
312
458
  else {
313
- console.log('⚠️ Unknown OS:', process.platform);
459
+ prompts.log.warn(`Unknown OS: ${process.platform}`);
314
460
  }
315
461
  // Prevent the spawn from keeping the process alive
316
462
  if (browserProcess && browserProcess.unref) {
@@ -319,23 +465,92 @@ class ServiceNowOAuth {
319
465
  }
320
466
  catch (err) {
321
467
  // Silently fail - user can manually open URL
322
- console.log('\n📋 Browser auto-open failed. Please manually copy and open the URL above.');
468
+ prompts.log.warn('Browser auto-open failed. Please manually copy and open the URL above.');
323
469
  }
324
470
  }
325
- else {
326
- console.log('\n🐳 Running in headless environment (Codespaces/Container/CI)');
327
- console.log('📋 Please manually copy and open the authorization URL above in your browser.');
328
- if (isCodespaces) {
329
- console.log('\n💡 TIP for GitHub Codespaces:');
330
- console.log(' 1. Copy the authorization URL above');
331
- console.log(' 2. Open it in a new browser tab');
332
- console.log(' 3. After authorizing, you\'ll be redirected to localhost:3005');
333
- console.log(' 4. Copy the FULL redirect URL from your browser');
334
- console.log(' 5. Open a new Codespaces terminal and run:');
335
- console.log(' curl "http://localhost:3005/callback?code=YOUR_CODE&state=YOUR_STATE"');
336
- console.log(' 6. Or use port forwarding in Codespaces to make port 3005 accessible');
471
+ // Offer manual callback URL paste as alternative
472
+ prompts.log.message('');
473
+ prompts.log.info('Alternatively, paste the callback URL here after authorizing:');
474
+ // Start prompt for manual URL paste (race with server callback)
475
+ (async () => {
476
+ try {
477
+ const callbackUrl = await prompts.text({
478
+ message: 'Paste callback URL (or press Enter to wait for automatic redirect)',
479
+ placeholder: 'http://localhost:3005/callback?code=...&state=...',
480
+ validate: (value) => {
481
+ // Allow empty (waiting for server callback)
482
+ if (!value || value.trim() === '')
483
+ return undefined;
484
+ // Validate if URL was pasted
485
+ if (!value.includes('callback'))
486
+ return 'Invalid callback URL - must contain "callback"';
487
+ if (!value.includes('code='))
488
+ return 'Invalid callback URL - must contain code parameter';
489
+ }
490
+ });
491
+ // If user pressed Enter without pasting, just wait for server callback
492
+ if (prompts.isCancel(callbackUrl) || !callbackUrl || callbackUrl.trim() === '') {
493
+ prompts.log.info('Waiting for browser redirect...');
494
+ return;
495
+ }
496
+ // User pasted a URL - parse it
497
+ if (resolved)
498
+ return; // Server already handled it
499
+ resolved = true;
500
+ try {
501
+ const url = new url_1.URL(callbackUrl);
502
+ const code = url.searchParams.get('code');
503
+ const state = url.searchParams.get('state');
504
+ const error = url.searchParams.get('error');
505
+ // Validate state
506
+ if (state !== this.stateParameter) {
507
+ server.close();
508
+ resolve({
509
+ success: false,
510
+ error: 'Invalid state parameter - security check failed'
511
+ });
512
+ return;
513
+ }
514
+ if (error) {
515
+ server.close();
516
+ resolve({
517
+ success: false,
518
+ error: `OAuth error: ${error}`
519
+ });
520
+ return;
521
+ }
522
+ if (!code) {
523
+ server.close();
524
+ resolve({
525
+ success: false,
526
+ error: 'No authorization code found in URL'
527
+ });
528
+ return;
529
+ }
530
+ // Exchange code for tokens
531
+ prompts.log.success('Authorization code received from pasted URL');
532
+ const spinner = prompts.spinner();
533
+ spinner.start('Exchanging authorization code for tokens');
534
+ const tokenResult = await this.exchangeCodeForTokens(code);
535
+ spinner.stop(tokenResult.success ? 'Token exchange successful' : 'Token exchange failed');
536
+ server.close();
537
+ resolve(tokenResult);
538
+ }
539
+ catch (err) {
540
+ server.close();
541
+ resolve({
542
+ success: false,
543
+ error: 'Invalid callback URL format'
544
+ });
545
+ }
337
546
  }
338
- }
547
+ catch (err) {
548
+ // Prompt was cancelled or errored - just wait for server callback
549
+ if (!resolved) {
550
+ prompts.log.info('Waiting for browser redirect...');
551
+ }
552
+ }
553
+ })();
339
554
  });
340
555
  });
341
556
  }
@@ -387,9 +602,41 @@ class ServiceNowOAuth {
387
602
  }
388
603
  catch (error) {
389
604
  const errorMessage = error instanceof Error ? error.message : String(error);
390
- console.error('Token exchange error:', errorMessage);
605
+ // Check for invalid redirect_uri error
391
606
  if (axios_1.default.isAxiosError(error) && error.response) {
392
- console.error('Response data:', error.response.data);
607
+ const responseData = error.response.data;
608
+ const errorDescription = responseData?.error_description || responseData?.error || '';
609
+ // Special handling for redirect_uri errors
610
+ if (errorDescription.toLowerCase().includes('redirect_uri') ||
611
+ errorDescription.toLowerCase().includes('redirect uri')) {
612
+ prompts.log.error('Invalid redirect_uri configuration');
613
+ prompts.log.message('');
614
+ prompts.log.warn('The OAuth application in ServiceNow is not configured correctly.');
615
+ prompts.log.message('');
616
+ prompts.log.step('Fix this by following these steps:');
617
+ prompts.log.message('');
618
+ prompts.log.info('1. Log into ServiceNow as administrator');
619
+ prompts.log.info(`2. Navigate to: System OAuth → Application Registry`);
620
+ prompts.log.info(`3. Find your OAuth application (Client ID: ${this.credentials.clientId})`);
621
+ prompts.log.info('4. Edit the "Redirect URL" field');
622
+ prompts.log.info('5. Change it to: http://localhost:3005/callback');
623
+ prompts.log.message(' (exactly this - copy/paste to avoid typos!)');
624
+ prompts.log.info('6. Save the application');
625
+ prompts.log.info('7. Run "snow-flow auth login" again');
626
+ prompts.log.message('');
627
+ prompts.log.warn('The redirect URI MUST be exactly: http://localhost:3005/callback');
628
+ prompts.log.message('');
629
+ return {
630
+ success: false,
631
+ error: 'Invalid redirect_uri - OAuth application not configured. See instructions above.'
632
+ };
633
+ }
634
+ // Log other API errors
635
+ prompts.log.error(`ServiceNow OAuth error: ${errorDescription}`);
636
+ prompts.log.error(`Response data: ${JSON.stringify(responseData)}`);
637
+ }
638
+ else {
639
+ prompts.log.error(`Token exchange error: ${errorMessage}`);
393
640
  }
394
641
  return {
395
642
  success: false,
@@ -414,7 +661,7 @@ class ServiceNowOAuth {
414
661
  await unified_auth_store_js_1.unifiedAuthStore.bridgeToMCP();
415
662
  }
416
663
  catch (error) {
417
- console.error('Failed to save tokens:', error);
664
+ prompts.log.error(`Failed to save tokens: ${error}`);
418
665
  throw error;
419
666
  }
420
667
  }
@@ -459,7 +706,7 @@ class ServiceNowOAuth {
459
706
  const now = new Date();
460
707
  if (now >= expiresAt && tokens.refreshToken) {
461
708
  // Token expired, try to refresh
462
- console.log('🔄 Token expired, refreshing...');
709
+ prompts.log.info('Token expired, refreshing...');
463
710
  const refreshResult = await this.refreshAccessToken(tokens);
464
711
  if (refreshResult.success && refreshResult.accessToken) {
465
712
  // Update saved tokens
@@ -471,14 +718,14 @@ class ServiceNowOAuth {
471
718
  return refreshResult.accessToken;
472
719
  }
473
720
  else {
474
- console.error('❌ Token refresh failed:', refreshResult.error);
721
+ prompts.log.error(`Token refresh failed: ${refreshResult.error}`);
475
722
  return null;
476
723
  }
477
724
  }
478
725
  return tokens.accessToken;
479
726
  }
480
727
  catch (error) {
481
- console.error('Failed to get access token:', error);
728
+ prompts.log.error(`Failed to get access token: ${error}`);
482
729
  return null;
483
730
  }
484
731
  }
@@ -555,10 +802,10 @@ class ServiceNowOAuth {
555
802
  async logout() {
556
803
  try {
557
804
  await fs_1.promises.unlink(this.tokenPath);
558
- console.log('Logged out successfully');
805
+ prompts.log.success('Logged out successfully');
559
806
  }
560
807
  catch (error) {
561
- console.log('No active session to logout from');
808
+ prompts.log.info('No active session to logout from');
562
809
  }
563
810
  }
564
811
  /**
@@ -579,15 +826,15 @@ class ServiceNowOAuth {
579
826
  if (tokens.clientSecret) {
580
827
  const secretValidation = this.validateClientSecret(tokens.clientSecret);
581
828
  if (!secretValidation.valid) {
582
- console.warn('⚠️ OAuth Configuration Issue:', secretValidation.reason);
583
- console.warn('💡 Your stored client secret may be incorrect. Re-authenticate with: snow-flow auth login');
829
+ prompts.log.warn(`OAuth Configuration Issue: ${secretValidation.reason}`);
830
+ prompts.log.info('Your stored client secret may be incorrect. Re-authenticate with: snow-flow auth login');
584
831
  }
585
832
  }
586
833
  // Check if token is expired
587
834
  const expiresAt = new Date(tokens.expiresAt);
588
835
  const now = new Date();
589
836
  if (now < expiresAt) {
590
- console.log('Using saved OAuth tokens');
837
+ prompts.log.success('Using saved OAuth tokens');
591
838
  return {
592
839
  instance: tokens.instance,
593
840
  clientId: tokens.clientId,
@@ -598,39 +845,39 @@ class ServiceNowOAuth {
598
845
  };
599
846
  }
600
847
  else {
601
- console.log('Saved OAuth token expired, will try refresh...');
848
+ prompts.log.warn('Saved OAuth token expired, will try refresh...');
602
849
  }
603
850
  }
604
851
  // 🔧 NEW: Fallback to .env file if no valid tokens
605
- console.log('🔍 No valid OAuth tokens found, checking .env file...');
852
+ prompts.log.info('No valid OAuth tokens found, checking .env file...');
606
853
  // Load environment variables with dotenv
607
854
  try {
608
855
  require('dotenv').config();
609
856
  }
610
857
  catch (err) {
611
- console.log('📝 dotenv not available, using process.env directly');
858
+ prompts.log.message('dotenv not available, using process.env directly');
612
859
  }
613
860
  const envInstance = process.env.SNOW_INSTANCE;
614
861
  const envClientId = process.env.SNOW_CLIENT_ID;
615
862
  const envClientSecret = process.env.SNOW_CLIENT_SECRET;
616
863
  if (envInstance && envClientId && envClientSecret) {
617
- console.log('Found ServiceNow credentials in .env file');
618
- console.log(` - Instance: ${envInstance}`);
619
- console.log(` - Client ID: ${envClientId}`);
620
- console.log(` - Client Secret: Present`);
864
+ prompts.log.success('Found ServiceNow credentials in .env file');
865
+ prompts.log.message(` - Instance: ${envInstance}`);
866
+ prompts.log.message(` - Client ID: ${envClientId}`);
867
+ prompts.log.message(' - Client Secret: Present');
621
868
  // Validate client secret
622
869
  const secretValidation = this.validateClientSecret(envClientSecret);
623
870
  if (!secretValidation.valid) {
624
- console.error('❌ Invalid OAuth Client Secret in .env file:', secretValidation.reason);
625
- console.error('💡 Please update SNOW_CLIENT_SECRET in .env with proper OAuth secret from ServiceNow');
871
+ prompts.log.error(`Invalid OAuth Client Secret in .env file: ${secretValidation.reason}`);
872
+ prompts.log.warn('Please update SNOW_CLIENT_SECRET in .env with proper OAuth secret from ServiceNow');
626
873
  return null;
627
874
  }
628
- console.log('');
629
- console.log('🔐 OAuth Setup Required:');
630
- console.log(' Your .env has OAuth credentials but no active session.');
631
- console.log(' Run: snow-flow auth login');
632
- console.log(' This will authenticate and create persistent tokens.');
633
- console.log('');
875
+ prompts.log.message('');
876
+ prompts.log.info('OAuth Setup Required:');
877
+ prompts.log.message(' Your .env has OAuth credentials but no active session.');
878
+ prompts.log.message(' Run: snow-flow auth login');
879
+ prompts.log.message(' This will authenticate and create persistent tokens.');
880
+ prompts.log.message('');
634
881
  // Return credentials without access token - this will trigger auth flow
635
882
  return {
636
883
  instance: envInstance.replace(/\/$/, ''),
@@ -643,33 +890,33 @@ class ServiceNowOAuth {
643
890
  const envUsername = process.env.SNOW_USERNAME;
644
891
  const envPassword = process.env.SNOW_PASSWORD;
645
892
  if (envInstance && envUsername && envPassword) {
646
- console.warn('⚠️ Found username/password in .env - OAuth is recommended');
647
- console.warn('💡 For better security, set up OAuth credentials:');
648
- console.warn(' 1. In ServiceNow: System OAuth > Application Registry > New');
649
- console.warn(' 2. Update .env with SNOW_CLIENT_ID and SNOW_CLIENT_SECRET');
650
- console.warn(' 3. Run: snow-flow auth login');
893
+ prompts.log.warn('Found username/password in .env - OAuth is recommended');
894
+ prompts.log.info('For better security, set up OAuth credentials:');
895
+ prompts.log.message(' 1. In ServiceNow: System OAuth > Application Registry > New');
896
+ prompts.log.message(' 2. Update .env with SNOW_CLIENT_ID and SNOW_CLIENT_SECRET');
897
+ prompts.log.message(' 3. Run: snow-flow auth login');
651
898
  // Don't return username/password - force OAuth setup
652
899
  return null;
653
900
  }
654
901
  // No credentials found anywhere
655
- console.error('No ServiceNow credentials found!');
656
- console.error('');
657
- console.error('🔧 Setup Instructions:');
658
- console.error(' 1. Create .env file with OAuth credentials:');
659
- console.error(' SNOW_INSTANCE=your-instance.service-now.com');
660
- console.error(' SNOW_CLIENT_ID=your_oauth_client_id');
661
- console.error(' SNOW_CLIENT_SECRET=your_oauth_client_secret');
662
- console.error(' 2. Run: snow-flow auth login');
663
- console.error('');
664
- console.error('💡 To get OAuth credentials:');
665
- console.error(' • ServiceNow: System OAuth > Application Registry > New OAuth Application');
666
- console.error(` • Redirect URI: http://${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectHost}:${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectPort}${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectPath}`);
667
- console.error(' • Scopes: useraccount write admin');
668
- console.error('');
902
+ prompts.log.error('No ServiceNow credentials found!');
903
+ prompts.log.message('');
904
+ prompts.log.info('Setup Instructions:');
905
+ prompts.log.message(' 1. Create .env file with OAuth credentials:');
906
+ prompts.log.message(' SNOW_INSTANCE=your-instance.service-now.com');
907
+ prompts.log.message(' SNOW_CLIENT_ID=your_oauth_client_id');
908
+ prompts.log.message(' SNOW_CLIENT_SECRET=your_oauth_client_secret');
909
+ prompts.log.message(' 2. Run: snow-flow auth login');
910
+ prompts.log.message('');
911
+ prompts.log.info('To get OAuth credentials:');
912
+ prompts.log.message(' • ServiceNow: System OAuth > Application Registry > New OAuth Application');
913
+ prompts.log.message(` • Redirect URI: http://${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectHost}:${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectPort}${snow_flow_config_js_1.snowFlowConfig.servicenow.oauth.redirectPath}`);
914
+ prompts.log.message(' • Scopes: useraccount write admin');
915
+ prompts.log.message('');
669
916
  return null;
670
917
  }
671
918
  catch (error) {
672
- console.error('❌ Error loading credentials:', error);
919
+ prompts.log.error(`Error loading credentials: ${error}`);
673
920
  return null;
674
921
  }
675
922
  }