snow-flow 3.5.9 ā 3.5.10
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/mcp/servicenow-local-development-mcp.d.ts +1 -0
- package/dist/mcp/servicenow-local-development-mcp.js +69 -0
- package/dist/utils/artifact-local-sync.js +7 -2
- package/dist/utils/servicenow-client-with-tracking.js +1 -1
- package/dist/utils/smart-field-fetcher.d.ts +4 -0
- package/dist/utils/smart-field-fetcher.js +117 -50
- package/package.json +1 -1
|
@@ -143,6 +143,20 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
|
|
|
143
143
|
required: ['code']
|
|
144
144
|
}
|
|
145
145
|
},
|
|
146
|
+
{
|
|
147
|
+
name: 'snow_debug_widget_fetch',
|
|
148
|
+
description: 'Debug widget fetching to diagnose API issues',
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
sys_id: {
|
|
153
|
+
type: 'string',
|
|
154
|
+
description: 'Widget sys_id to debug'
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
required: ['sys_id']
|
|
158
|
+
}
|
|
159
|
+
},
|
|
146
160
|
// Legacy compatibility tools
|
|
147
161
|
{
|
|
148
162
|
name: 'snow_pull_widget',
|
|
@@ -201,6 +215,9 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
|
|
|
201
215
|
case 'snow_convert_to_es5':
|
|
202
216
|
result = await this.convertToES5(args);
|
|
203
217
|
break;
|
|
218
|
+
case 'snow_debug_widget_fetch':
|
|
219
|
+
result = await this.debugWidgetFetch(args);
|
|
220
|
+
break;
|
|
204
221
|
// Legacy compatibility
|
|
205
222
|
case 'snow_pull_widget':
|
|
206
223
|
result = await this.pullWidget(args);
|
|
@@ -412,6 +429,58 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
|
|
|
412
429
|
]
|
|
413
430
|
};
|
|
414
431
|
}
|
|
432
|
+
async debugWidgetFetch(args) {
|
|
433
|
+
const { sys_id } = args;
|
|
434
|
+
try {
|
|
435
|
+
const debugResults = await this.syncManager['smartFetcher'].debugFetchWidget(sys_id);
|
|
436
|
+
let summaryText = `š Debug Results for Widget ${sys_id}\n\n`;
|
|
437
|
+
// Check which methods worked
|
|
438
|
+
const methods = ['searchRecords', 'getRecord', 'searchRecordsWithFields'];
|
|
439
|
+
for (const method of methods) {
|
|
440
|
+
if (debugResults[method]) {
|
|
441
|
+
const widget = debugResults[method];
|
|
442
|
+
summaryText += `ā
${method}:\n`;
|
|
443
|
+
summaryText += ` - Fields: ${Object.keys(widget).length}\n`;
|
|
444
|
+
summaryText += ` - Has script: ${!!widget.script}\n`;
|
|
445
|
+
summaryText += ` - Has client_script: ${!!widget.client_script}\n`;
|
|
446
|
+
summaryText += ` - Has template: ${!!widget.template}\n`;
|
|
447
|
+
summaryText += ` - Script size: ${widget.script?.length || 0} chars\n`;
|
|
448
|
+
summaryText += ` - Client script size: ${widget.client_script?.length || 0} chars\n`;
|
|
449
|
+
summaryText += ` - Template size: ${widget.template?.length || 0} chars\n\n`;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
summaryText += `ā ${method}: Failed\n\n`;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// Recommend best approach
|
|
456
|
+
const workingMethods = methods.filter(m => debugResults[m]);
|
|
457
|
+
if (workingMethods.length > 0) {
|
|
458
|
+
summaryText += `\nš Recommendation: Use ${workingMethods[0]} for fetching this widget.`;
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
summaryText += `\nā ļø All fetch methods failed. There may be an authentication or permission issue.`;
|
|
462
|
+
}
|
|
463
|
+
return {
|
|
464
|
+
content: [
|
|
465
|
+
{
|
|
466
|
+
type: 'text',
|
|
467
|
+
text: summaryText
|
|
468
|
+
}
|
|
469
|
+
]
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
474
|
+
return {
|
|
475
|
+
content: [
|
|
476
|
+
{
|
|
477
|
+
type: 'text',
|
|
478
|
+
text: `ā Debug failed: ${errorMessage}`
|
|
479
|
+
}
|
|
480
|
+
]
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
}
|
|
415
484
|
// Legacy compatibility methods
|
|
416
485
|
async pullWidget(args) {
|
|
417
486
|
return this.pullArtifact({ ...args, table: 'sp_widget' });
|
|
@@ -52,24 +52,29 @@ class ArtifactLocalSync {
|
|
|
52
52
|
this.artifacts = new Map();
|
|
53
53
|
this.client = client;
|
|
54
54
|
this.smartFetcher = new smart_field_fetcher_js_1.SmartFieldFetcher(client);
|
|
55
|
+
// Version check for debugging
|
|
56
|
+
console.log(`š§ ArtifactLocalSync v3.5.10 initializing...`);
|
|
55
57
|
// Use custom directory, environment variable, or default to current project's servicenow folder
|
|
56
58
|
if (customBaseDir) {
|
|
57
59
|
this.baseDir = customBaseDir;
|
|
60
|
+
console.log(` š Using custom directory: ${customBaseDir}`);
|
|
58
61
|
}
|
|
59
62
|
else if (process.env.SNOW_FLOW_ARTIFACTS_DIR) {
|
|
60
63
|
this.baseDir = process.env.SNOW_FLOW_ARTIFACTS_DIR;
|
|
64
|
+
console.log(` š Using environment variable directory: ${this.baseDir}`);
|
|
61
65
|
}
|
|
62
66
|
else {
|
|
63
67
|
// Default to 'servicenow' folder in current working directory
|
|
64
68
|
this.baseDir = path.join(process.cwd(), 'servicenow');
|
|
69
|
+
console.log(` š Using project directory: ${this.baseDir}`);
|
|
65
70
|
}
|
|
66
71
|
// Create base directory if it doesn't exist
|
|
67
72
|
if (!fs.existsSync(this.baseDir)) {
|
|
68
73
|
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
69
|
-
console.log(
|
|
74
|
+
console.log(` ā
Created ServiceNow artifacts directory`);
|
|
70
75
|
}
|
|
71
76
|
else {
|
|
72
|
-
console.log(
|
|
77
|
+
console.log(` ā
Directory exists`);
|
|
73
78
|
}
|
|
74
79
|
// Create .gitignore if it doesn't exist to optionally exclude from version control
|
|
75
80
|
const gitignorePath = path.join(this.baseDir, '.gitignore');
|
|
@@ -46,7 +46,7 @@ class ServiceNowClientWithTracking extends servicenow_client_js_1.ServiceNowClie
|
|
|
46
46
|
// Estimate tokens based on fields requested
|
|
47
47
|
const fieldCount = fields.length;
|
|
48
48
|
const estimatedTokens = Math.min(fieldCount * 100, 1000); // Rough estimate
|
|
49
|
-
this.mcpLogger.
|
|
49
|
+
this.mcpLogger.addTokens(estimatedTokens, 0);
|
|
50
50
|
if (result?.data?.result?.length > 0) {
|
|
51
51
|
this.mcpLogger.info(`Found ${result.data.result.length} records with ${fieldCount} fields`);
|
|
52
52
|
}
|
|
@@ -52,6 +52,10 @@ export declare class SmartFieldFetcher {
|
|
|
52
52
|
* Search within fields using GlideRecord queries
|
|
53
53
|
*/
|
|
54
54
|
searchInField(table: string, field: string, searchTerm: string, additionalQuery?: string): Promise<any[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Debug method to directly test API calls
|
|
57
|
+
*/
|
|
58
|
+
debugFetchWidget(sys_id: string): Promise<any>;
|
|
55
59
|
/**
|
|
56
60
|
* Get smart fetch strategy based on table - NOW USES ARTIFACT REGISTRY!
|
|
57
61
|
*/
|
|
@@ -163,8 +163,8 @@ class SmartFieldFetcher {
|
|
|
163
163
|
try {
|
|
164
164
|
// Try to get all fields at once
|
|
165
165
|
const response = await this.client.searchRecords('sp_widget', `sys_id=${sys_id}`, 1);
|
|
166
|
-
if (response && response.result && response.result.length > 0) {
|
|
167
|
-
const widgetData = response.result[0];
|
|
166
|
+
if (response && response.success && response.data && response.data.result && response.data.result.length > 0) {
|
|
167
|
+
const widgetData = response.data.result[0];
|
|
168
168
|
console.log(`ā
Successfully fetched complete widget`);
|
|
169
169
|
// Organize into field groups for better context
|
|
170
170
|
for (const group of WIDGET_FIELD_GROUPS) {
|
|
@@ -184,68 +184,72 @@ class SmartFieldFetcher {
|
|
|
184
184
|
Object.assign(results, widgetData);
|
|
185
185
|
}
|
|
186
186
|
else {
|
|
187
|
-
throw new Error('Widget not found');
|
|
187
|
+
throw new Error('Widget not found or no data returned');
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
catch (error) {
|
|
191
191
|
console.log(`ā ļø Complete fetch failed: ${error.message}`);
|
|
192
|
-
console.log(`š Switching to
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
192
|
+
console.log(`š Switching to more robust fetching approach...`);
|
|
193
|
+
// First, get the widget with minimal fields to ensure it exists
|
|
194
|
+
try {
|
|
195
|
+
const basicResponse = await this.client.searchRecords('sp_widget', `sys_id=${sys_id}`, 1);
|
|
196
|
+
if (!basicResponse || !basicResponse.success || !basicResponse.data || !basicResponse.data.result || basicResponse.data.result.length === 0) {
|
|
197
|
+
throw new Error(`Widget with sys_id ${sys_id} not found`);
|
|
198
|
+
}
|
|
199
|
+
// Widget exists, now get it via getRecord which might handle large fields better
|
|
200
|
+
const widgetData = basicResponse.data.result[0];
|
|
201
|
+
// Store what we got
|
|
202
|
+
if (widgetData) {
|
|
203
|
+
// Add all available fields to results
|
|
204
|
+
Object.assign(results, widgetData);
|
|
205
|
+
// Organize into field groups
|
|
206
|
+
for (const group of WIDGET_FIELD_GROUPS) {
|
|
207
|
+
const groupData = {};
|
|
208
|
+
for (const fieldName of group.fields) {
|
|
209
|
+
if (widgetData[fieldName] !== undefined) {
|
|
210
|
+
groupData[fieldName] = widgetData[fieldName];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
202
213
|
results._field_groups[group.groupName] = {
|
|
203
214
|
data: groupData,
|
|
204
215
|
description: group.description,
|
|
205
216
|
fields: group.fields
|
|
206
217
|
};
|
|
207
|
-
// Add to flat structure
|
|
208
|
-
Object.assign(results, groupData);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
throw new Error('No data returned for group');
|
|
212
218
|
}
|
|
219
|
+
// Log what we got
|
|
220
|
+
console.log(`š Retrieved widget fields:`);
|
|
221
|
+
console.log(` - name: ${widgetData.name || 'N/A'}`);
|
|
222
|
+
console.log(` - template: ${widgetData.template ? widgetData.template.length + ' chars' : 'empty'}`);
|
|
223
|
+
console.log(` - script: ${widgetData.script ? widgetData.script.length + ' chars' : 'empty'}`);
|
|
224
|
+
console.log(` - client_script: ${widgetData.client_script ? widgetData.client_script.length + ' chars' : 'empty'}`);
|
|
225
|
+
console.log(` - css: ${widgetData.css ? widgetData.css.length + ' chars' : 'empty'}`);
|
|
213
226
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
results[fieldName] = fieldValue; // Also add to flat structure
|
|
228
|
-
console.log(` ā
Got ${fieldName} (${typeof fieldValue === 'string' ? fieldValue.length : 0} chars)`);
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
groupData[fieldName] = '';
|
|
232
|
-
console.log(` ā ļø ${fieldName} is empty`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
catch (fieldError) {
|
|
237
|
-
console.log(` ā Failed to fetch ${fieldName}: ${fieldError.message}`);
|
|
238
|
-
groupData[fieldName] = ''; // Empty string for failed fields
|
|
239
|
-
}
|
|
227
|
+
// If critical fields are missing, try alternative approach
|
|
228
|
+
if (!widgetData.script && !widgetData.client_script && !widgetData.template) {
|
|
229
|
+
console.log(`ā ļø Critical fields missing, trying direct API call...`);
|
|
230
|
+
// Try using getRecord instead of searchRecords
|
|
231
|
+
const directResponse = await this.client.getRecord('sp_widget', sys_id);
|
|
232
|
+
if (directResponse && directResponse.success && directResponse.data && directResponse.data.result) {
|
|
233
|
+
const directData = directResponse.data.result;
|
|
234
|
+
// Merge any new data we got
|
|
235
|
+
Object.assign(results, directData);
|
|
236
|
+
console.log(`š Direct API retrieved:`);
|
|
237
|
+
console.log(` - template: ${directData.template ? directData.template.length + ' chars' : 'empty'}`);
|
|
238
|
+
console.log(` - script: ${directData.script ? directData.script.length + ' chars' : 'empty'}`);
|
|
239
|
+
console.log(` - client_script: ${directData.client_script ? directData.client_script.length + ' chars' : 'empty'}`);
|
|
240
240
|
}
|
|
241
|
-
results._field_groups[group.groupName] = {
|
|
242
|
-
data: groupData,
|
|
243
|
-
description: group.description,
|
|
244
|
-
fields: group.fields,
|
|
245
|
-
_fetched_individually: true
|
|
246
|
-
};
|
|
247
241
|
}
|
|
248
242
|
}
|
|
243
|
+
catch (fallbackError) {
|
|
244
|
+
console.log(`ā All fetch attempts failed: ${fallbackError.message}`);
|
|
245
|
+
// At least return basic structure with sys_id
|
|
246
|
+
results.sys_id = sys_id;
|
|
247
|
+
results.name = 'Unknown Widget';
|
|
248
|
+
results.template = '';
|
|
249
|
+
results.script = '';
|
|
250
|
+
results.client_script = '';
|
|
251
|
+
results.css = '';
|
|
252
|
+
}
|
|
249
253
|
// Make sure we have at least the sys_id
|
|
250
254
|
if (!results.sys_id) {
|
|
251
255
|
results.sys_id = sys_id;
|
|
@@ -350,6 +354,69 @@ class SmartFieldFetcher {
|
|
|
350
354
|
return [];
|
|
351
355
|
}
|
|
352
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Debug method to directly test API calls
|
|
359
|
+
*/
|
|
360
|
+
async debugFetchWidget(sys_id) {
|
|
361
|
+
console.log(`\nš DEBUG: Testing different fetch approaches for widget ${sys_id}\n`);
|
|
362
|
+
const results = {};
|
|
363
|
+
// Test 1: Basic searchRecords
|
|
364
|
+
try {
|
|
365
|
+
console.log(`Test 1: searchRecords...`);
|
|
366
|
+
const test1 = await this.client.searchRecords('sp_widget', `sys_id=${sys_id}`, 1);
|
|
367
|
+
console.log(` - Response structure: success=${test1?.success}, has data=${!!test1?.data}, has result=${!!test1?.data?.result}`);
|
|
368
|
+
if (test1?.data?.result?.[0]) {
|
|
369
|
+
const widget = test1.data.result[0];
|
|
370
|
+
console.log(` - Fields received: ${Object.keys(widget).join(', ')}`);
|
|
371
|
+
console.log(` - Script length: ${widget.script?.length || 0}`);
|
|
372
|
+
console.log(` - Client script length: ${widget.client_script?.length || 0}`);
|
|
373
|
+
console.log(` - Template length: ${widget.template?.length || 0}`);
|
|
374
|
+
results.searchRecords = widget;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch (e) {
|
|
378
|
+
console.log(` ā Error: ${e.message}`);
|
|
379
|
+
}
|
|
380
|
+
// Test 2: getRecord
|
|
381
|
+
try {
|
|
382
|
+
console.log(`Test 2: getRecord...`);
|
|
383
|
+
const test2 = await this.client.getRecord('sp_widget', sys_id);
|
|
384
|
+
console.log(` - Response structure: success=${test2?.success}, has data=${!!test2?.data}, has result=${!!test2?.data?.result}`);
|
|
385
|
+
if (test2?.data?.result) {
|
|
386
|
+
const widget = test2.data.result;
|
|
387
|
+
console.log(` - Fields received: ${Object.keys(widget).join(', ')}`);
|
|
388
|
+
console.log(` - Script length: ${widget.script?.length || 0}`);
|
|
389
|
+
console.log(` - Client script length: ${widget.client_script?.length || 0}`);
|
|
390
|
+
console.log(` - Template length: ${widget.template?.length || 0}`);
|
|
391
|
+
results.getRecord = widget;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch (e) {
|
|
395
|
+
console.log(` ā Error: ${e.message}`);
|
|
396
|
+
}
|
|
397
|
+
// Test 3: searchRecordsWithFields
|
|
398
|
+
try {
|
|
399
|
+
console.log(`Test 3: searchRecordsWithFields with specific fields...`);
|
|
400
|
+
const test3 = await this.client.searchRecordsWithFields('sp_widget', `sys_id=${sys_id}`, ['name', 'script', 'client_script', 'template', 'css'], 1);
|
|
401
|
+
console.log(` - Response structure: success=${test3?.success}, has data=${!!test3?.data}, has result=${!!test3?.data?.result}`);
|
|
402
|
+
if (test3?.data?.result?.[0]) {
|
|
403
|
+
const widget = test3.data.result[0];
|
|
404
|
+
console.log(` - Fields received: ${Object.keys(widget).join(', ')}`);
|
|
405
|
+
console.log(` - Script length: ${widget.script?.length || 0}`);
|
|
406
|
+
console.log(` - Client script length: ${widget.client_script?.length || 0}`);
|
|
407
|
+
console.log(` - Template length: ${widget.template?.length || 0}`);
|
|
408
|
+
results.searchRecordsWithFields = widget;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch (e) {
|
|
412
|
+
console.log(` ā Error: ${e.message}`);
|
|
413
|
+
}
|
|
414
|
+
console.log(`\nš Debug Results Summary:`);
|
|
415
|
+
console.log(` - searchRecords got: ${results.searchRecords ? Object.keys(results.searchRecords).length + ' fields' : 'failed'}`);
|
|
416
|
+
console.log(` - getRecord got: ${results.getRecord ? Object.keys(results.getRecord).length + ' fields' : 'failed'}`);
|
|
417
|
+
console.log(` - searchRecordsWithFields got: ${results.searchRecordsWithFields ? Object.keys(results.searchRecordsWithFields).length + ' fields' : 'failed'}`);
|
|
418
|
+
return results;
|
|
419
|
+
}
|
|
353
420
|
/**
|
|
354
421
|
* Get smart fetch strategy based on table - NOW USES ARTIFACT REGISTRY!
|
|
355
422
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.10",
|
|
4
4
|
"description": "ServiceNow development framework with MCP server integration. Executes background scripts using ES5 JavaScript only (ServiceNow Rhino engine requirement). Provides 18 MCP servers including local development sync for editing ServiceNow artifacts with Claude Code native tools, widget deployment with coherence validation, table operations, script execution, machine learning, advanced features, and comprehensive platform management.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|