snow-flow 1.3.18 → 1.3.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -65,8 +65,16 @@ program
65
65
  // Helper function to deploy XML to ServiceNow
66
66
  async function deployXMLToServiceNow(xmlFile, options = {}) {
67
67
  const oauth = new snow_oauth_js_1.ServiceNowOAuth();
68
- const isAuthenticated = await oauth.getStoredTokens() !== null;
69
- if (!isAuthenticated) {
68
+ let tokens;
69
+ try {
70
+ tokens = await oauth.getStoredTokens();
71
+ }
72
+ catch (error) {
73
+ cliLogger.error('❌ Failed to get authentication tokens:', error);
74
+ cliLogger.error('Please run: snow-flow auth login');
75
+ return false;
76
+ }
77
+ if (!tokens || !tokens.accessToken) {
70
78
  cliLogger.error('❌ Not authenticated. Please run: snow-flow auth login');
71
79
  return false;
72
80
  }
@@ -166,11 +174,43 @@ async function deployXMLToServiceNow(xmlFile, options = {}) {
166
174
  return true;
167
175
  }
168
176
  catch (error) {
169
- cliLogger.error('\n❌ Deployment failed:', error instanceof Error ? error.message : String(error));
177
+ cliLogger.error('\n❌ Deployment failed:');
178
+ // Detailed error handling for 400 errors
179
+ if (error.response?.status === 400) {
180
+ cliLogger.error(` Status: ${error.response.status} Bad Request`);
181
+ if (error.response.data) {
182
+ cliLogger.error(` Message: ${error.response.data.error?.message || error.response.data.message || 'Unknown error'}`);
183
+ if (error.response.data.error?.detail) {
184
+ cliLogger.error(` Detail: ${error.response.data.error.detail}`);
185
+ }
186
+ if (error.response.data.error?.fields) {
187
+ cliLogger.error(' Missing or invalid fields:');
188
+ Object.entries(error.response.data.error.fields).forEach(([field, msg]) => {
189
+ cliLogger.error(` - ${field}: ${msg}`);
190
+ });
191
+ }
192
+ }
193
+ }
194
+ else if (error.response?.status === 401) {
195
+ cliLogger.error(' Status: 401 Unauthorized - Authentication failed');
196
+ cliLogger.error(' Your OAuth token may be expired. Please run: snow-flow auth login');
197
+ }
198
+ else if (error.response?.status === 403) {
199
+ cliLogger.error(' Status: 403 Forbidden - Insufficient permissions');
200
+ cliLogger.error(' You need admin or update_set_admin role to import update sets');
201
+ }
202
+ else if (error.response) {
203
+ cliLogger.error(` Status: ${error.response.status}`);
204
+ cliLogger.error(` Message: ${error.response.data?.error?.message || error.response.statusText}`);
205
+ }
206
+ else {
207
+ cliLogger.error(` ${error instanceof Error ? error.message : String(error)}`);
208
+ }
170
209
  cliLogger.info('\n💡 Troubleshooting tips:');
171
210
  cliLogger.info(' 1. Check your authentication: snow-flow auth status');
172
211
  cliLogger.info(' 2. Verify XML file format is correct');
173
212
  cliLogger.info(' 3. Ensure you have required permissions in ServiceNow');
213
+ cliLogger.info(' 4. Check ServiceNow system logs for more details');
174
214
  return false;
175
215
  }
176
216
  }
@@ -12,6 +12,7 @@ const fs_1 = require("fs");
12
12
  const path_1 = require("path");
13
13
  const events_1 = require("events");
14
14
  const os_1 = __importDefault(require("os"));
15
+ const unified_auth_store_js_1 = require("./unified-auth-store.js");
15
16
  class MCPServerManager extends events_1.EventEmitter {
16
17
  constructor(configPath) {
17
18
  super();
@@ -172,15 +173,33 @@ class MCPServerManager extends events_1.EventEmitter {
172
173
  }
173
174
  // Check if script exists
174
175
  await fs_1.promises.access(scriptPath);
175
- // Start the process
176
+ // Bridge OAuth tokens to MCP servers
177
+ await unified_auth_store_js_1.unifiedAuthStore.bridgeToMCP();
178
+ // Get current tokens for the MCP server
179
+ const tokens = await unified_auth_store_js_1.unifiedAuthStore.getTokens();
180
+ const authEnv = {};
181
+ if (tokens) {
182
+ authEnv.SNOW_OAUTH_TOKENS = JSON.stringify(tokens);
183
+ authEnv.SNOW_INSTANCE = tokens.instance;
184
+ authEnv.SNOW_CLIENT_ID = tokens.clientId;
185
+ authEnv.SNOW_CLIENT_SECRET = tokens.clientSecret;
186
+ if (tokens.accessToken) {
187
+ authEnv.SNOW_ACCESS_TOKEN = tokens.accessToken;
188
+ }
189
+ if (tokens.refreshToken) {
190
+ authEnv.SNOW_REFRESH_TOKEN = tokens.refreshToken;
191
+ }
192
+ if (tokens.expiresAt) {
193
+ authEnv.SNOW_TOKEN_EXPIRES_AT = tokens.expiresAt;
194
+ }
195
+ }
196
+ // Start the process with OAuth tokens
176
197
  const childProcess = (0, child_process_1.spawn)('node', [scriptPath], {
177
198
  stdio: ['ignore', 'pipe', 'pipe'],
178
199
  detached: true,
179
200
  env: {
180
201
  ...process.env,
181
- SNOW_INSTANCE: process.env.SNOW_INSTANCE,
182
- SNOW_CLIENT_ID: process.env.SNOW_CLIENT_ID,
183
- SNOW_CLIENT_SECRET: process.env.SNOW_CLIENT_SECRET
202
+ ...authEnv
184
203
  }
185
204
  });
186
205
  // Set up logging
@@ -15,6 +15,7 @@ const action_type_cache_1 = require("./action-type-cache");
15
15
  const snow_flow_config_js_1 = require("../config/snow-flow-config.js");
16
16
  const widget_template_generator_js_1 = require("./widget-template-generator.js");
17
17
  const logger_1 = require("./logger");
18
+ const unified_auth_store_js_1 = require("./unified-auth-store.js");
18
19
  const flow_structure_builder_1 = require("./flow-structure-builder");
19
20
  class ServiceNowClient {
20
21
  constructor() {
@@ -115,7 +116,22 @@ class ServiceNowClient {
115
116
  */
116
117
  async ensureAuthenticated() {
117
118
  if (!this.credentials) {
118
- this.credentials = await this.oauth.loadCredentials();
119
+ // Try unified auth store first (most reliable)
120
+ const tokens = await unified_auth_store_js_1.unifiedAuthStore.getTokens();
121
+ if (tokens) {
122
+ this.credentials = {
123
+ instance: tokens.instance,
124
+ clientId: tokens.clientId,
125
+ clientSecret: tokens.clientSecret,
126
+ accessToken: tokens.accessToken,
127
+ refreshToken: tokens.refreshToken,
128
+ expiresAt: tokens.expiresAt
129
+ };
130
+ }
131
+ else {
132
+ // Fallback to OAuth loadCredentials
133
+ this.credentials = await this.oauth.loadCredentials();
134
+ }
119
135
  }
120
136
  if (!this.credentials) {
121
137
  console.error('❌ No ServiceNow credentials found');
@@ -18,6 +18,7 @@ const axios_1 = __importDefault(require("axios"));
18
18
  const net_1 = __importDefault(require("net"));
19
19
  const crypto_1 = __importDefault(require("crypto"));
20
20
  const snow_flow_config_js_1 = require("../config/snow-flow-config.js");
21
+ const unified_auth_store_js_1 = require("./unified-auth-store.js");
21
22
  class ServiceNowOAuth {
22
23
  constructor() {
23
24
  // Store tokens in user's home directory
@@ -348,15 +349,16 @@ class ServiceNowOAuth {
348
349
  */
349
350
  async saveTokens(tokenData) {
350
351
  try {
351
- const configDir = process.env.SNOW_FLOW_HOME || (0, path_1.join)(os_1.default.homedir(), '.snow-flow');
352
- await fs_1.promises.mkdir(configDir, { recursive: true });
353
352
  const expiresAt = new Date();
354
353
  expiresAt.setSeconds(expiresAt.getSeconds() + tokenData.expiresIn);
355
354
  const authData = {
356
355
  ...tokenData,
357
356
  expiresAt: expiresAt.toISOString()
358
357
  };
359
- await fs_1.promises.writeFile(this.tokenPath, JSON.stringify(authData, null, 2), 'utf8');
358
+ // Use unified auth store
359
+ await unified_auth_store_js_1.unifiedAuthStore.saveTokens(authData);
360
+ // Bridge to MCP servers immediately
361
+ await unified_auth_store_js_1.unifiedAuthStore.bridgeToMCP();
360
362
  }
361
363
  catch (error) {
362
364
  console.error('Failed to save tokens:', error);
@@ -368,8 +370,7 @@ class ServiceNowOAuth {
368
370
  */
369
371
  async loadTokens() {
370
372
  try {
371
- const data = await fs_1.promises.readFile(this.tokenPath, 'utf8');
372
- return JSON.parse(data);
373
+ return await unified_auth_store_js_1.unifiedAuthStore.getTokens();
373
374
  }
374
375
  catch (error) {
375
376
  return null;
@@ -495,6 +496,12 @@ class ServiceNowOAuth {
495
496
  console.log('No active session to logout from');
496
497
  }
497
498
  }
499
+ /**
500
+ * Get stored OAuth tokens for use in other contexts (MCP servers)
501
+ */
502
+ async getStoredTokens() {
503
+ return await this.loadTokens();
504
+ }
498
505
  /**
499
506
  * Load credentials (including tokens) with .env fallback
500
507
  */
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ /**
3
+ * Unified Authentication Store for ServiceNow
4
+ *
5
+ * Provides shared token storage accessible from both CLI and MCP contexts.
6
+ * Solves the token isolation problem between different execution contexts.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.unifiedAuthStore = exports.UnifiedAuthStore = void 0;
13
+ const fs_1 = require("fs");
14
+ const path_1 = require("path");
15
+ const os_1 = __importDefault(require("os"));
16
+ const dotenv_1 = __importDefault(require("dotenv"));
17
+ // Load environment variables
18
+ dotenv_1.default.config();
19
+ class UnifiedAuthStore {
20
+ constructor() {
21
+ this.memoryStore = null;
22
+ // Use consistent path across all contexts
23
+ const configDir = process.env.SNOW_FLOW_HOME || (0, path_1.join)(os_1.default.homedir(), '.snow-flow');
24
+ this.tokenPath = (0, path_1.join)(configDir, 'auth.json');
25
+ // Also check environment for shared tokens (from MCP bridge)
26
+ if (process.env.SNOW_OAUTH_TOKENS) {
27
+ try {
28
+ this.memoryStore = JSON.parse(process.env.SNOW_OAUTH_TOKENS);
29
+ }
30
+ catch (e) {
31
+ console.error('Failed to parse SNOW_OAUTH_TOKENS from environment');
32
+ }
33
+ }
34
+ }
35
+ static getInstance() {
36
+ if (!UnifiedAuthStore.instance) {
37
+ UnifiedAuthStore.instance = new UnifiedAuthStore();
38
+ }
39
+ return UnifiedAuthStore.instance;
40
+ }
41
+ /**
42
+ * Get tokens from file or memory
43
+ */
44
+ async getTokens() {
45
+ try {
46
+ // First check memory store (fastest)
47
+ if (this.memoryStore) {
48
+ return this.memoryStore;
49
+ }
50
+ // Then check file system
51
+ const data = await fs_1.promises.readFile(this.tokenPath, 'utf8');
52
+ const tokens = JSON.parse(data);
53
+ // Cache in memory for performance
54
+ this.memoryStore = tokens;
55
+ return tokens;
56
+ }
57
+ catch (error) {
58
+ // Fallback to environment variables
59
+ return this.getTokensFromEnv();
60
+ }
61
+ }
62
+ /**
63
+ * Save tokens to file and memory
64
+ */
65
+ async saveTokens(tokens) {
66
+ try {
67
+ const configDir = (0, path_1.join)(os_1.default.homedir(), '.snow-flow');
68
+ await fs_1.promises.mkdir(configDir, { recursive: true });
69
+ await fs_1.promises.writeFile(this.tokenPath, JSON.stringify(tokens, null, 2));
70
+ // Update memory store
71
+ this.memoryStore = tokens;
72
+ // Update environment for child processes
73
+ process.env.SNOW_OAUTH_TOKENS = JSON.stringify(tokens);
74
+ }
75
+ catch (error) {
76
+ console.error('Failed to save tokens:', error);
77
+ throw error;
78
+ }
79
+ }
80
+ /**
81
+ * Get tokens from environment variables (fallback)
82
+ */
83
+ getTokensFromEnv() {
84
+ const instance = process.env.SNOW_INSTANCE;
85
+ const clientId = process.env.SNOW_CLIENT_ID;
86
+ const clientSecret = process.env.SNOW_CLIENT_SECRET;
87
+ if (!instance || !clientId || !clientSecret) {
88
+ return null;
89
+ }
90
+ return {
91
+ instance: instance.replace(/\/$/, ''),
92
+ clientId,
93
+ clientSecret,
94
+ accessToken: process.env.SNOW_ACCESS_TOKEN,
95
+ refreshToken: process.env.SNOW_REFRESH_TOKEN,
96
+ expiresAt: process.env.SNOW_TOKEN_EXPIRES_AT
97
+ };
98
+ }
99
+ /**
100
+ * Check if tokens are valid and not expired
101
+ */
102
+ async isAuthenticated() {
103
+ try {
104
+ const tokens = await this.getTokens();
105
+ if (!tokens || !tokens.accessToken) {
106
+ return false;
107
+ }
108
+ // Check expiration
109
+ if (tokens.expiresAt) {
110
+ const expiresAt = new Date(tokens.expiresAt);
111
+ const now = new Date();
112
+ return now < expiresAt;
113
+ }
114
+ // If no expiration, assume valid
115
+ return true;
116
+ }
117
+ catch (error) {
118
+ return false;
119
+ }
120
+ }
121
+ /**
122
+ * Clear all stored tokens
123
+ */
124
+ async clearTokens() {
125
+ try {
126
+ await fs_1.promises.unlink(this.tokenPath);
127
+ }
128
+ catch (error) {
129
+ // Ignore if file doesn't exist
130
+ }
131
+ // Clear memory and environment
132
+ this.memoryStore = null;
133
+ delete process.env.SNOW_OAUTH_TOKENS;
134
+ delete process.env.SNOW_ACCESS_TOKEN;
135
+ delete process.env.SNOW_REFRESH_TOKEN;
136
+ delete process.env.SNOW_TOKEN_EXPIRES_AT;
137
+ }
138
+ /**
139
+ * Get ServiceNow instance URL
140
+ */
141
+ async getInstanceUrl() {
142
+ const tokens = await this.getTokens();
143
+ if (!tokens) {
144
+ return null;
145
+ }
146
+ let instance = tokens.instance;
147
+ if (!instance.startsWith('http')) {
148
+ instance = `https://${instance}`;
149
+ }
150
+ if (!instance.endsWith('.service-now.com')) {
151
+ instance = `${instance}.service-now.com`;
152
+ }
153
+ return instance;
154
+ }
155
+ /**
156
+ * Get headers for API requests
157
+ */
158
+ async getAuthHeaders() {
159
+ const tokens = await this.getTokens();
160
+ if (!tokens || !tokens.accessToken) {
161
+ return null;
162
+ }
163
+ return {
164
+ 'Authorization': `Bearer ${tokens.accessToken}`,
165
+ 'Content-Type': 'application/json',
166
+ 'Accept': 'application/json'
167
+ };
168
+ }
169
+ /**
170
+ * Bridge tokens to MCP servers via environment
171
+ */
172
+ async bridgeToMCP() {
173
+ const tokens = await this.getTokens();
174
+ if (tokens) {
175
+ process.env.SNOW_OAUTH_TOKENS = JSON.stringify(tokens);
176
+ process.env.SNOW_INSTANCE = tokens.instance;
177
+ process.env.SNOW_CLIENT_ID = tokens.clientId;
178
+ process.env.SNOW_CLIENT_SECRET = tokens.clientSecret;
179
+ if (tokens.accessToken) {
180
+ process.env.SNOW_ACCESS_TOKEN = tokens.accessToken;
181
+ }
182
+ if (tokens.refreshToken) {
183
+ process.env.SNOW_REFRESH_TOKEN = tokens.refreshToken;
184
+ }
185
+ if (tokens.expiresAt) {
186
+ process.env.SNOW_TOKEN_EXPIRES_AT = tokens.expiresAt;
187
+ }
188
+ }
189
+ }
190
+ }
191
+ exports.UnifiedAuthStore = UnifiedAuthStore;
192
+ // Export singleton instance
193
+ exports.unifiedAuthStore = UnifiedAuthStore.getInstance();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "1.3.18",
3
+ "version": "1.3.20",
4
4
  "description": "ServiceNow Queen Agent - Hive-Mind Intelligence for ServiceNow Development inspired by claude-flow. Transform complex workflows into elegant one-command orchestration.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",