snow-flow 3.5.8 ā 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.d.ts +1 -1
- package/dist/utils/artifact-local-sync.js +65 -131
- 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' });
|
|
@@ -37,7 +37,7 @@ export declare class ArtifactLocalSync {
|
|
|
37
37
|
private artifacts;
|
|
38
38
|
private client;
|
|
39
39
|
private smartFetcher;
|
|
40
|
-
constructor(client: ServiceNowClient);
|
|
40
|
+
constructor(client: ServiceNowClient, customBaseDir?: string);
|
|
41
41
|
/**
|
|
42
42
|
* DYNAMIC pull artifact from ServiceNow using artifact registry
|
|
43
43
|
* Works with ANY artifact type defined in the registry!
|
|
@@ -45,20 +45,76 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.ArtifactLocalSync = void 0;
|
|
46
46
|
const fs = __importStar(require("fs"));
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
|
-
const os = __importStar(require("os"));
|
|
49
48
|
const smart_field_fetcher_js_1 = require("./smart-field-fetcher.js");
|
|
50
49
|
const artifact_registry_1 = require("./artifact-sync/artifact-registry");
|
|
51
50
|
class ArtifactLocalSync {
|
|
52
|
-
constructor(client) {
|
|
51
|
+
constructor(client, customBaseDir) {
|
|
53
52
|
this.artifacts = new Map();
|
|
54
53
|
this.client = client;
|
|
55
54
|
this.smartFetcher = new smart_field_fetcher_js_1.SmartFieldFetcher(client);
|
|
56
|
-
//
|
|
57
|
-
|
|
55
|
+
// Version check for debugging
|
|
56
|
+
console.log(`š§ ArtifactLocalSync v3.5.10 initializing...`);
|
|
57
|
+
// Use custom directory, environment variable, or default to current project's servicenow folder
|
|
58
|
+
if (customBaseDir) {
|
|
59
|
+
this.baseDir = customBaseDir;
|
|
60
|
+
console.log(` š Using custom directory: ${customBaseDir}`);
|
|
61
|
+
}
|
|
62
|
+
else if (process.env.SNOW_FLOW_ARTIFACTS_DIR) {
|
|
63
|
+
this.baseDir = process.env.SNOW_FLOW_ARTIFACTS_DIR;
|
|
64
|
+
console.log(` š Using environment variable directory: ${this.baseDir}`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Default to 'servicenow' folder in current working directory
|
|
68
|
+
this.baseDir = path.join(process.cwd(), 'servicenow');
|
|
69
|
+
console.log(` š Using project directory: ${this.baseDir}`);
|
|
70
|
+
}
|
|
71
|
+
// Create base directory if it doesn't exist
|
|
58
72
|
if (!fs.existsSync(this.baseDir)) {
|
|
59
73
|
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
74
|
+
console.log(` ā
Created ServiceNow artifacts directory`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.log(` ā
Directory exists`);
|
|
78
|
+
}
|
|
79
|
+
// Create .gitignore if it doesn't exist to optionally exclude from version control
|
|
80
|
+
const gitignorePath = path.join(this.baseDir, '.gitignore');
|
|
81
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
82
|
+
fs.writeFileSync(gitignorePath, '# ServiceNow Artifacts\n' +
|
|
83
|
+
'# Uncomment the following lines to exclude from version control:\n' +
|
|
84
|
+
'# *\n' +
|
|
85
|
+
'# !.gitignore\n' +
|
|
86
|
+
'# !README.md\n');
|
|
87
|
+
}
|
|
88
|
+
// Create README if it doesn't exist
|
|
89
|
+
const readmePath = path.join(this.baseDir, 'README.md');
|
|
90
|
+
if (!fs.existsSync(readmePath)) {
|
|
91
|
+
fs.writeFileSync(readmePath, '# ServiceNow Artifacts Directory\n\n' +
|
|
92
|
+
'This directory contains ServiceNow artifacts synchronized from your instance for local development.\n\n' +
|
|
93
|
+
'## Structure\n\n' +
|
|
94
|
+
'```\n' +
|
|
95
|
+
'servicenow/\n' +
|
|
96
|
+
'āāā widgets/ # Service Portal widgets\n' +
|
|
97
|
+
'āāā script_includes/ # Script Includes\n' +
|
|
98
|
+
'āāā business_rules/ # Business Rules\n' +
|
|
99
|
+
'āāā flows/ # Flow Designer flows\n' +
|
|
100
|
+
'āāā ui_pages/ # UI Pages\n' +
|
|
101
|
+
'āāā ... # Other artifact types\n' +
|
|
102
|
+
'```\n\n' +
|
|
103
|
+
'## Workflow\n\n' +
|
|
104
|
+
'1. **Pull artifacts**: `snow_pull_artifact` downloads artifacts here\n' +
|
|
105
|
+
'2. **Edit locally**: Use your IDE/editor to modify files\n' +
|
|
106
|
+
'3. **Push changes**: `snow_push_artifact` syncs changes back to ServiceNow\n' +
|
|
107
|
+
'4. **Clean up**: `snow_sync_cleanup` removes local files after sync\n\n' +
|
|
108
|
+
'## Version Control\n\n' +
|
|
109
|
+
'You can choose to:\n' +
|
|
110
|
+
'- **Track changes**: Keep artifacts in git for version history\n' +
|
|
111
|
+
'- **Ignore artifacts**: Edit `.gitignore` to exclude from git\n\n' +
|
|
112
|
+
'## Configuration\n\n' +
|
|
113
|
+
'Set custom location with environment variable:\n' +
|
|
114
|
+
'```bash\n' +
|
|
115
|
+
'export SNOW_FLOW_ARTIFACTS_DIR=/path/to/artifacts\n' +
|
|
116
|
+
'```\n');
|
|
60
117
|
}
|
|
61
|
-
console.log(`š Snow-Flow local sync directory: ${this.baseDir}`);
|
|
62
118
|
}
|
|
63
119
|
/**
|
|
64
120
|
* DYNAMIC pull artifact from ServiceNow using artifact registry
|
|
@@ -143,8 +199,11 @@ class ArtifactLocalSync {
|
|
|
143
199
|
artifactConfig: config
|
|
144
200
|
};
|
|
145
201
|
this.artifacts.set(sys_id, artifact);
|
|
202
|
+
// Show relative path if in project directory
|
|
203
|
+
const relativePath = path.relative(process.cwd(), artifactPath);
|
|
204
|
+
const displayPath = relativePath.startsWith('..') ? artifactPath : relativePath;
|
|
146
205
|
console.log(`ā
${config.displayName} synced to local files:`);
|
|
147
|
-
console.log(`š Location: ${
|
|
206
|
+
console.log(`š Location: ${displayPath}`);
|
|
148
207
|
console.log(`š Files created:`);
|
|
149
208
|
files.forEach(f => console.log(` - ${f.filename} (${f.type})`));
|
|
150
209
|
console.log(`\nš” Claude Code can now use its native tools on these files!`);
|
|
@@ -158,77 +217,6 @@ class ArtifactLocalSync {
|
|
|
158
217
|
*/
|
|
159
218
|
async pullWidget(sys_id) {
|
|
160
219
|
return this.pullArtifact('sp_widget', sys_id);
|
|
161
|
-
console.log(`\nš Pulling widget ${sys_id} to local files...`);
|
|
162
|
-
// Fetch widget with smart chunking
|
|
163
|
-
const widget = await this.smartFetcher.fetchWidget(sys_id);
|
|
164
|
-
// Create local directory structure
|
|
165
|
-
const widgetName = this.sanitizeFilename(widget.name || `widget_${sys_id}`);
|
|
166
|
-
const widgetPath = path.join(this.baseDir, 'widgets', widgetName);
|
|
167
|
-
// Clean up any existing files
|
|
168
|
-
if (fs.existsSync(widgetPath)) {
|
|
169
|
-
fs.rmSync(widgetPath, { recursive: true });
|
|
170
|
-
}
|
|
171
|
-
fs.mkdirSync(widgetPath, { recursive: true });
|
|
172
|
-
// Create local files for each widget component
|
|
173
|
-
const files = [];
|
|
174
|
-
// 1. HTML Template
|
|
175
|
-
if (widget.template) {
|
|
176
|
-
const htmlFile = this.createLocalFile(widgetPath, `${widgetName}.html`, widget.template, 'template', 'html', '<!-- ServiceNow Widget Template -->\n<!-- Widget: ' + widget.name + ' -->\n<!-- Bindings: {{data.x}} from server, ng-click calls client methods -->\n\n');
|
|
177
|
-
files.push(htmlFile);
|
|
178
|
-
}
|
|
179
|
-
// 2. Server Script (ES5)
|
|
180
|
-
if (widget.script) {
|
|
181
|
-
const serverFile = this.createLocalFile(widgetPath, `${widgetName}.server.js`, widget.script, 'script', 'js', '/**\n * ServiceNow Widget Server Script (ES5 ONLY!)\n * Widget: ' + widget.name + '\n * \n * Available objects:\n * - data: Object to send to client\n * - input: Data from client\n * - options: Widget instance options\n * - gs: GlideSystem\n * - $sp: Service Portal API\n */\n\n(function() {\n', '\n})();');
|
|
182
|
-
files.push(serverFile);
|
|
183
|
-
}
|
|
184
|
-
// 3. Client Script (AngularJS)
|
|
185
|
-
if (widget.client_script) {
|
|
186
|
-
const clientFile = this.createLocalFile(widgetPath, `${widgetName}.client.js`, widget.client_script, 'client_script', 'js', '/**\n * ServiceNow Widget Client Controller (AngularJS)\n * Widget: ' + widget.name + '\n * \n * Available objects:\n * - c: Widget controller (this)\n * - c.data: Data from server\n * - c.server: Server communication\n * - $scope: Angular scope\n */\n\nfunction(');
|
|
187
|
-
files.push(clientFile);
|
|
188
|
-
}
|
|
189
|
-
// 4. CSS
|
|
190
|
-
if (widget.css) {
|
|
191
|
-
const cssFile = this.createLocalFile(widgetPath, `${widgetName}.css`, widget.css, 'css', 'css', '/* ServiceNow Widget Styles */\n/* Widget: ' + widget.name + ' */\n/* Prefix classes to avoid conflicts */\n\n');
|
|
192
|
-
files.push(cssFile);
|
|
193
|
-
}
|
|
194
|
-
// 5. Widget Configuration (JSON)
|
|
195
|
-
const config = {
|
|
196
|
-
sys_id: widget.sys_id,
|
|
197
|
-
name: widget.name,
|
|
198
|
-
title: widget.title,
|
|
199
|
-
option_schema: widget.option_schema,
|
|
200
|
-
data_table: widget.data_table,
|
|
201
|
-
roles: widget.roles,
|
|
202
|
-
public: widget.public,
|
|
203
|
-
_coherence_hints: widget._coherence_hints || []
|
|
204
|
-
};
|
|
205
|
-
const configFile = this.createLocalFile(widgetPath, `${widgetName}.config.json`, JSON.stringify(config, null, 2), 'metadata', 'json');
|
|
206
|
-
files.push(configFile);
|
|
207
|
-
// 6. README with context
|
|
208
|
-
const readmeContent = this.generateWidgetReadme(widget, files);
|
|
209
|
-
const readmeFile = this.createLocalFile(widgetPath, 'README.md', readmeContent, 'documentation', 'md');
|
|
210
|
-
files.push(readmeFile);
|
|
211
|
-
// Create artifact record
|
|
212
|
-
const artifact = {
|
|
213
|
-
sys_id: widget.sys_id,
|
|
214
|
-
name: widget.name,
|
|
215
|
-
type: 'widget',
|
|
216
|
-
tableName: 'sp_widget', // Added missing tableName
|
|
217
|
-
localPath: widgetPath,
|
|
218
|
-
files: files,
|
|
219
|
-
metadata: widget,
|
|
220
|
-
syncStatus: 'synced',
|
|
221
|
-
createdAt: new Date(),
|
|
222
|
-
lastSyncedAt: new Date()
|
|
223
|
-
};
|
|
224
|
-
this.artifacts.set(sys_id, artifact);
|
|
225
|
-
console.log(`ā
Widget synced to local files:`);
|
|
226
|
-
console.log(`š Location: ${widgetPath}`);
|
|
227
|
-
console.log(`š Files created:`);
|
|
228
|
-
files.forEach(f => console.log(` - ${f.filename} (${f.type})`));
|
|
229
|
-
console.log(`\nš” Claude Code can now use its native tools on these files!`);
|
|
230
|
-
console.log(` Edit, search, refactor - then run 'pushWidget' to sync back.`);
|
|
231
|
-
return artifact;
|
|
232
220
|
}
|
|
233
221
|
/**
|
|
234
222
|
* DYNAMIC push local changes back to ServiceNow using artifact registry
|
|
@@ -336,60 +324,6 @@ class ArtifactLocalSync {
|
|
|
336
324
|
*/
|
|
337
325
|
async pushWidget(sys_id) {
|
|
338
326
|
return this.pushArtifact(sys_id);
|
|
339
|
-
const artifact = this.artifacts.get(sys_id);
|
|
340
|
-
if (!artifact) {
|
|
341
|
-
throw new Error(`No local artifact found for ${sys_id}. Run pullWidget first.`);
|
|
342
|
-
}
|
|
343
|
-
console.log(`\nš Pushing local changes back to ServiceNow...`);
|
|
344
|
-
// Read current content from all files
|
|
345
|
-
const updates = {};
|
|
346
|
-
let hasChanges = false;
|
|
347
|
-
for (const file of artifact.files) {
|
|
348
|
-
if (fs.existsSync(file.path)) {
|
|
349
|
-
const currentContent = fs.readFileSync(file.path, 'utf8');
|
|
350
|
-
// Strip our added headers/footers for comparison
|
|
351
|
-
const cleanContent = this.stripAddedWrappers(currentContent, file.type);
|
|
352
|
-
if (cleanContent !== file.originalContent) {
|
|
353
|
-
hasChanges = true;
|
|
354
|
-
file.currentContent = cleanContent;
|
|
355
|
-
file.isModified = true;
|
|
356
|
-
// Map back to ServiceNow field
|
|
357
|
-
if (file.field && file.field !== 'documentation' && file.field !== 'metadata') {
|
|
358
|
-
updates[file.field] = cleanContent;
|
|
359
|
-
console.log(` š Changed: ${file.filename} (${file.field})`);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
if (!hasChanges) {
|
|
365
|
-
console.log(`ā
No changes detected. Widget is up to date.`);
|
|
366
|
-
return true;
|
|
367
|
-
}
|
|
368
|
-
// Validate ES5 compliance for server script
|
|
369
|
-
if (updates.script) {
|
|
370
|
-
const es5Issues = this.validateES5(updates.script);
|
|
371
|
-
if (es5Issues.length > 0) {
|
|
372
|
-
console.log(`\nā ļø ES5 Validation Issues in server script:`);
|
|
373
|
-
es5Issues.forEach(issue => console.log(` - ${issue}`));
|
|
374
|
-
console.log(`\nā Continue with deployment anyway? (ServiceNow might fail)`);
|
|
375
|
-
// In real implementation, prompt for confirmation
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
// Update in ServiceNow
|
|
379
|
-
try {
|
|
380
|
-
console.log(`\nš¤ Updating widget in ServiceNow...`);
|
|
381
|
-
await this.client.updateRecord('sp_widget', sys_id, updates);
|
|
382
|
-
artifact.syncStatus = 'synced';
|
|
383
|
-
artifact.lastSyncedAt = new Date();
|
|
384
|
-
console.log(`ā
Widget successfully updated in ServiceNow!`);
|
|
385
|
-
console.log(`š sys_id: ${sys_id}`);
|
|
386
|
-
return true;
|
|
387
|
-
}
|
|
388
|
-
catch (error) {
|
|
389
|
-
console.error(`ā Failed to update widget:`, error);
|
|
390
|
-
artifact.syncStatus = 'pending_upload';
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
327
|
}
|
|
394
328
|
/**
|
|
395
329
|
* Clean up local files after successful sync
|
|
@@ -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",
|