snow-flow 3.6.4 ā 3.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -21,6 +21,7 @@ export interface LocalArtifact {
|
|
|
21
21
|
createdAt: Date;
|
|
22
22
|
lastSyncedAt: Date;
|
|
23
23
|
artifactConfig?: ArtifactTypeConfig;
|
|
24
|
+
isGeneric?: boolean;
|
|
24
25
|
}
|
|
25
26
|
export interface LocalFile {
|
|
26
27
|
filename: string;
|
|
@@ -55,6 +56,11 @@ export declare class ArtifactLocalSync {
|
|
|
55
56
|
* DYNAMIC push local changes back to ServiceNow using artifact registry
|
|
56
57
|
*/
|
|
57
58
|
pushArtifact(sys_id: string): Promise<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* Push generic artifact changes back to ServiceNow
|
|
61
|
+
* Only updates known script fields to avoid data corruption
|
|
62
|
+
*/
|
|
63
|
+
pushGenericArtifact(artifact: LocalArtifact): Promise<boolean>;
|
|
58
64
|
/**
|
|
59
65
|
* Push local changes back to ServiceNow
|
|
60
66
|
* (Wrapper for backward compatibility)
|
|
@@ -109,9 +115,14 @@ export declare class ArtifactLocalSync {
|
|
|
109
115
|
getSyncStatus(sys_id: string): string;
|
|
110
116
|
/**
|
|
111
117
|
* Pull any supported artifact type by detecting table from sys_id
|
|
112
|
-
* ENHANCED:
|
|
118
|
+
* ENHANCED: Now uses sys_metadata to find ANY table, not just registered ones!
|
|
113
119
|
*/
|
|
114
120
|
pullArtifactBySysId(sys_id: string): Promise<LocalArtifact>;
|
|
121
|
+
/**
|
|
122
|
+
* Pull a generic artifact from an unknown/custom table
|
|
123
|
+
* Creates a basic file structure for any ServiceNow record
|
|
124
|
+
*/
|
|
125
|
+
pullGenericArtifact(tableName: string, sys_id: string): Promise<LocalArtifact>;
|
|
115
126
|
/**
|
|
116
127
|
* Get supported artifact types
|
|
117
128
|
*/
|
|
@@ -254,6 +254,10 @@ class ArtifactLocalSync {
|
|
|
254
254
|
if (!artifact) {
|
|
255
255
|
throw new Error(`No local artifact found for ${sys_id}. Run pullArtifact first.`);
|
|
256
256
|
}
|
|
257
|
+
// Handle generic artifacts differently
|
|
258
|
+
if (artifact.isGeneric) {
|
|
259
|
+
return this.pushGenericArtifact(artifact);
|
|
260
|
+
}
|
|
257
261
|
const config = artifact.artifactConfig;
|
|
258
262
|
if (!config) {
|
|
259
263
|
throw new Error(`No configuration found for artifact ${sys_id}`);
|
|
@@ -354,6 +358,56 @@ class ArtifactLocalSync {
|
|
|
354
358
|
return false;
|
|
355
359
|
}
|
|
356
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* Push generic artifact changes back to ServiceNow
|
|
363
|
+
* Only updates known script fields to avoid data corruption
|
|
364
|
+
*/
|
|
365
|
+
async pushGenericArtifact(artifact) {
|
|
366
|
+
console.log(`\nš Pushing generic artifact changes to ${artifact.tableName}...`);
|
|
367
|
+
const updates = {};
|
|
368
|
+
let hasChanges = false;
|
|
369
|
+
// Only update known script fields for safety
|
|
370
|
+
const scriptFields = ['script', 'condition', 'script_plain', 'advanced',
|
|
371
|
+
'client_script', 'server_script', 'template', 'body'];
|
|
372
|
+
for (const file of artifact.files) {
|
|
373
|
+
if (file.field && scriptFields.includes(file.field)) {
|
|
374
|
+
if (fs.existsSync(file.path)) {
|
|
375
|
+
const currentContent = fs.readFileSync(file.path, 'utf8');
|
|
376
|
+
if (currentContent !== file.originalContent) {
|
|
377
|
+
hasChanges = true;
|
|
378
|
+
updates[file.field] = currentContent;
|
|
379
|
+
console.log(` š Changed: ${file.filename} (${file.field})`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (!hasChanges) {
|
|
385
|
+
console.log(`ā
No changes detected. Generic artifact is up to date.`);
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
console.log(`\nš¤ Updating generic artifact in ServiceNow...`);
|
|
390
|
+
const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
|
|
391
|
+
const updateResult = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.updateRecord(artifact.tableName, artifact.sys_id, updates), timeoutConfig.pushToolTimeout, `Update generic artifact ${artifact.sys_id}`);
|
|
392
|
+
if (!updateResult || !updateResult.success) {
|
|
393
|
+
const errorMsg = updateResult?.error || 'Unknown API error';
|
|
394
|
+
console.error(`ā ServiceNow API returned failure: ${errorMsg}`);
|
|
395
|
+
artifact.syncStatus = 'pending_upload';
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
artifact.syncStatus = 'synced';
|
|
399
|
+
artifact.lastSyncedAt = new Date();
|
|
400
|
+
console.log(`ā
Generic artifact successfully updated in ServiceNow!`);
|
|
401
|
+
console.log(`š Table: ${artifact.tableName}`);
|
|
402
|
+
console.log(`š sys_id: ${artifact.sys_id}`);
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
console.error(`ā Failed to update generic artifact:`, error);
|
|
407
|
+
artifact.syncStatus = 'pending_upload';
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
357
411
|
/**
|
|
358
412
|
* Push local changes back to ServiceNow
|
|
359
413
|
* (Wrapper for backward compatibility)
|
|
@@ -703,20 +757,43 @@ snow-flow sync status ${widget.sys_id}
|
|
|
703
757
|
}
|
|
704
758
|
/**
|
|
705
759
|
* Pull any supported artifact type by detecting table from sys_id
|
|
706
|
-
* ENHANCED:
|
|
760
|
+
* ENHANCED: Now uses sys_metadata to find ANY table, not just registered ones!
|
|
707
761
|
*/
|
|
708
762
|
async pullArtifactBySysId(sys_id) {
|
|
709
763
|
console.log(`\nš Auto-detecting artifact type for sys_id: ${sys_id}`);
|
|
710
|
-
console.log(`š Checking ${Object.keys(artifact_registry_1.ARTIFACT_REGISTRY).length} supported tables...`);
|
|
711
764
|
// CRITICAL: Add MAXIMUM operation timeout to prevent infinite hanging
|
|
712
765
|
const MAX_OPERATION_TIME = 30000; // 30 seconds max for entire operation
|
|
713
766
|
const operationStart = Date.now();
|
|
714
|
-
|
|
767
|
+
const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
|
|
768
|
+
// STEP 1: Try to find the table name using sys_metadata (works for ANY table!)
|
|
769
|
+
console.log(`š® Querying sys_metadata to find table name...`);
|
|
770
|
+
try {
|
|
771
|
+
const metadataResponse = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords('sys_metadata', `sys_id=${sys_id}`, 1), 5000, `Query sys_metadata for ${sys_id}`);
|
|
772
|
+
if (metadataResponse.result?.[0]?.sys_class_name) {
|
|
773
|
+
const tableName = metadataResponse.result[0].sys_class_name;
|
|
774
|
+
console.log(` ā
Found in sys_metadata! Table: ${tableName}`);
|
|
775
|
+
// Check if this table is in our registry
|
|
776
|
+
if (artifact_registry_1.ARTIFACT_REGISTRY[tableName]) {
|
|
777
|
+
console.log(` ā
Table is supported! Proceeding with pull...`);
|
|
778
|
+
return this.pullArtifact(tableName, sys_id);
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
console.log(` ā ļø Table '${tableName}' is not in artifact registry`);
|
|
782
|
+
console.log(` š§ Attempting generic pull for custom table...`);
|
|
783
|
+
return this.pullGenericArtifact(tableName, sys_id);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
catch (error) {
|
|
788
|
+
console.log(` ā ļø sys_metadata query failed: ${error instanceof Error ? error.message : error}`);
|
|
789
|
+
console.log(` š Falling back to registered table search...`);
|
|
790
|
+
}
|
|
791
|
+
// STEP 2: If sys_metadata fails, try common tables first
|
|
792
|
+
console.log(`\nš Checking ${Object.keys(artifact_registry_1.ARTIFACT_REGISTRY).length} registered tables...`);
|
|
715
793
|
const allTables = Object.keys(artifact_registry_1.ARTIFACT_REGISTRY);
|
|
716
794
|
const commonTables = ['sp_widget', 'sys_script_include', 'sys_script', 'sys_ui_page'];
|
|
717
795
|
const otherTables = allTables.filter(t => !commonTables.includes(t));
|
|
718
796
|
const tables = [...commonTables, ...otherTables]; // Common tables first
|
|
719
|
-
const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
|
|
720
797
|
const errors = [];
|
|
721
798
|
for (const table of tables) {
|
|
722
799
|
// Check if we've exceeded maximum operation time
|
|
@@ -727,7 +804,7 @@ snow-flow sync status ${widget.sys_id}
|
|
|
727
804
|
try {
|
|
728
805
|
console.log(` š Checking table: ${table}...`);
|
|
729
806
|
// Reduced timeout per table for faster failure
|
|
730
|
-
const response = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords(table, `sys_id=${sys_id}`, 1), 3000, // 3 second timeout per table
|
|
807
|
+
const response = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords(table, `sys_id=${sys_id}`, 1), 3000, // 3 second timeout per table
|
|
731
808
|
`Detect table for ${sys_id}`);
|
|
732
809
|
if (response.result?.[0]) {
|
|
733
810
|
console.log(` ā
Found in table: ${table}`);
|
|
@@ -742,12 +819,29 @@ snow-flow sync status ${widget.sys_id}
|
|
|
742
819
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
743
820
|
console.log(` ā ļø Error checking ${table}: ${errorMsg}`);
|
|
744
821
|
errors.push({ table, error: errorMsg });
|
|
745
|
-
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
// STEP 3: Try a few common custom tables as last resort
|
|
825
|
+
const customTables = ['sys_ui_script', 'sys_processor', 'sys_ws_operation', 'sys_portal_page'];
|
|
826
|
+
console.log(`\nš Checking additional custom tables...`);
|
|
827
|
+
for (const table of customTables) {
|
|
828
|
+
if (Date.now() - operationStart > MAX_OPERATION_TIME)
|
|
829
|
+
break;
|
|
830
|
+
try {
|
|
831
|
+
console.log(` š Checking custom table: ${table}...`);
|
|
832
|
+
const response = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords(table, `sys_id=${sys_id}`, 1), 3000, `Check custom table ${table}`);
|
|
833
|
+
if (response.result?.[0]) {
|
|
834
|
+
console.log(` ā
Found in custom table: ${table}`);
|
|
835
|
+
return this.pullGenericArtifact(table, sys_id);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
catch (error) {
|
|
839
|
+
// Silent fail for custom tables
|
|
746
840
|
}
|
|
747
841
|
}
|
|
748
842
|
// Generate detailed error message
|
|
749
843
|
console.log(`\nā Artifact detection failed!`);
|
|
750
|
-
console.log(`š Searched ${tables.length} tables
|
|
844
|
+
console.log(`š Searched sys_metadata + ${tables.length} registered tables + ${customTables.length} custom tables`);
|
|
751
845
|
if (errors.length > 0) {
|
|
752
846
|
console.log(`\nā ļø Errors encountered:`);
|
|
753
847
|
errors.forEach(({ table, error }) => {
|
|
@@ -757,9 +851,92 @@ snow-flow sync status ${widget.sys_id}
|
|
|
757
851
|
console.log(`\nš” Troubleshooting tips:`);
|
|
758
852
|
console.log(` 1. Verify the sys_id exists in ServiceNow`);
|
|
759
853
|
console.log(` 2. Check your permissions for the target table`);
|
|
760
|
-
console.log(` 3. Try specifying the table explicitly: snow_pull_artifact({sys_id, table: '
|
|
761
|
-
console.log(` 4.
|
|
762
|
-
throw new Error(`Could not find artifact with sys_id ${sys_id} in
|
|
854
|
+
console.log(` 3. Try specifying the table explicitly: snow_pull_artifact({sys_id, table: 'table_name'})`);
|
|
855
|
+
console.log(` 4. The artifact might be in a custom or scoped application table`);
|
|
856
|
+
throw new Error(`Could not find artifact with sys_id ${sys_id}. It may be in a custom table or you may lack permissions.`);
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Pull a generic artifact from an unknown/custom table
|
|
860
|
+
* Creates a basic file structure for any ServiceNow record
|
|
861
|
+
*/
|
|
862
|
+
async pullGenericArtifact(tableName, sys_id) {
|
|
863
|
+
console.log(`\nš§ Pulling generic artifact from custom table: ${tableName}`);
|
|
864
|
+
try {
|
|
865
|
+
// Fetch the record with all fields
|
|
866
|
+
const response = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords(tableName, `sys_id=${sys_id}`, 1), 10000, `Fetch generic artifact from ${tableName}`);
|
|
867
|
+
const record = response.result?.[0];
|
|
868
|
+
if (!record) {
|
|
869
|
+
throw new Error(`Record not found in ${tableName}`);
|
|
870
|
+
}
|
|
871
|
+
// Create a generic folder structure
|
|
872
|
+
const name = record.name || record.short_description || record.sys_name || sys_id;
|
|
873
|
+
const sanitizedName = this.sanitizeFilename(name);
|
|
874
|
+
const artifactPath = path.join(this.baseDir, 'custom', tableName, sanitizedName);
|
|
875
|
+
// Clean up existing files
|
|
876
|
+
if (fs.existsSync(artifactPath)) {
|
|
877
|
+
fs.rmSync(artifactPath, { recursive: true });
|
|
878
|
+
}
|
|
879
|
+
fs.mkdirSync(artifactPath, { recursive: true });
|
|
880
|
+
const files = [];
|
|
881
|
+
// Create JSON file with all fields
|
|
882
|
+
const jsonFile = this.createLocalFile(artifactPath, `${sanitizedName}.json`, JSON.stringify(record, null, 2), 'all_fields', 'json');
|
|
883
|
+
files.push(jsonFile);
|
|
884
|
+
// Extract common script fields if they exist
|
|
885
|
+
const scriptFields = ['script', 'condition', 'script_plain', 'advanced',
|
|
886
|
+
'client_script', 'server_script', 'template', 'body'];
|
|
887
|
+
for (const field of scriptFields) {
|
|
888
|
+
if (record[field] && typeof record[field] === 'string' && record[field].trim()) {
|
|
889
|
+
const ext = field.includes('template') || field === 'body' ? 'html' : 'js';
|
|
890
|
+
const scriptFile = this.createLocalFile(artifactPath, `${sanitizedName}.${field}.${ext}`, record[field], field, ext);
|
|
891
|
+
files.push(scriptFile);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
// Create README
|
|
895
|
+
const readmeContent = `# Generic Artifact: ${name}\n\n` +
|
|
896
|
+
`**Table:** ${tableName}\n` +
|
|
897
|
+
`**Sys ID:** ${sys_id}\n` +
|
|
898
|
+
`**Created:** ${record.sys_created_on || 'Unknown'}\n` +
|
|
899
|
+
`**Updated:** ${record.sys_updated_on || 'Unknown'}\n\n` +
|
|
900
|
+
`## Notice\n\n` +
|
|
901
|
+
`This is a generic artifact from a custom/unknown table.\n` +
|
|
902
|
+
`Snow-Flow has created a basic file structure to allow editing.\n\n` +
|
|
903
|
+
`## Files\n\n` +
|
|
904
|
+
`- **${sanitizedName}.json** - Complete record data\n` +
|
|
905
|
+
(files.length > 1 ? files.slice(1).map(f => `- **${f.filename}** - ${f.field} field content\n`).join('') : '') +
|
|
906
|
+
`\n## Push Support\n\n` +
|
|
907
|
+
`Generic artifacts have limited push support. ` +
|
|
908
|
+
`Only script fields will be updated when pushing back.\n`;
|
|
909
|
+
const readmeFile = this.createLocalFile(artifactPath, 'README.md', readmeContent, 'documentation', 'md');
|
|
910
|
+
files.push(readmeFile);
|
|
911
|
+
// Create artifact record
|
|
912
|
+
const artifact = {
|
|
913
|
+
sys_id: sys_id,
|
|
914
|
+
name: name,
|
|
915
|
+
type: `Custom (${tableName})`,
|
|
916
|
+
tableName: tableName,
|
|
917
|
+
localPath: artifactPath,
|
|
918
|
+
files: files,
|
|
919
|
+
metadata: record,
|
|
920
|
+
syncStatus: 'synced',
|
|
921
|
+
createdAt: new Date(),
|
|
922
|
+
lastSyncedAt: new Date(),
|
|
923
|
+
isGeneric: true // Mark as generic for special handling
|
|
924
|
+
};
|
|
925
|
+
this.artifacts.set(sys_id, artifact);
|
|
926
|
+
const relativePath = path.relative(process.cwd(), artifactPath);
|
|
927
|
+
const displayPath = relativePath.startsWith('..') ? artifactPath : relativePath;
|
|
928
|
+
console.log(`ā
Generic artifact pulled successfully!`);
|
|
929
|
+
console.log(`š Location: ${displayPath}`);
|
|
930
|
+
console.log(`š Files created:`);
|
|
931
|
+
files.forEach(f => console.log(` - ${f.filename} (${f.type})`));
|
|
932
|
+
console.log(`\nā ļø Note: This is a generic pull from a custom table.`);
|
|
933
|
+
console.log(` Full push support may be limited.`);
|
|
934
|
+
return artifact;
|
|
935
|
+
}
|
|
936
|
+
catch (error) {
|
|
937
|
+
console.error(`ā Failed to pull generic artifact:`, error);
|
|
938
|
+
throw error;
|
|
939
|
+
}
|
|
763
940
|
}
|
|
764
941
|
/**
|
|
765
942
|
* Get supported artifact types
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "3.6.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.6.5",
|
|
4
|
+
"description": "UNIVERSAL ARTIFACT DETECTION via sys_metadata! v3.6.5 can now find ANY ServiceNow record by sys_id alone - even custom tables. Generic artifact support for unknown tables. Smarter pullArtifactBySysId that queries sys_metadata first. Auto-creates editable file structure for ANY table type. Includes early auto-compact at 65%, audit logging, and consolidated CLAUDE.md.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|