snow-flow 10.0.1-dev.488 → 10.0.1-dev.489

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "10.0.1-dev.488",
3
+ "version": "10.0.1-dev.489",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -110,6 +110,26 @@ async function executeFlowPatchMutation(
110
110
  return resp.data?.data?.global?.snFlowDesigner?.flow || resp.data;
111
111
  }
112
112
 
113
+ /**
114
+ * Acquire the Flow Designer editing lock on a flow.
115
+ * The UI calls safeEdit(create: flowId) when opening the editor.
116
+ * This must be called before GraphQL mutations on existing flows.
117
+ */
118
+ async function acquireFlowEditingLock(client: any, flowId: string): Promise<{ success: boolean; error?: string }> {
119
+ try {
120
+ var mutation = 'mutation { global { snFlowDesigner { safeEdit(safeEditInput: {create: "' + flowId + '"}) { createResult { canEdit id editingUserDisplayName __typename } __typename } __typename } __typename } }';
121
+ var resp = await client.post('/api/now/graphql', { variables: {}, query: mutation });
122
+ var result = resp.data?.data?.global?.snFlowDesigner?.safeEdit?.createResult;
123
+ if (result?.canEdit === true || result?.canEdit === 'true') {
124
+ return { success: true };
125
+ }
126
+ var editingUser = result?.editingUserDisplayName || 'another user';
127
+ return { success: false, error: 'Flow is locked by ' + editingUser };
128
+ } catch (e: any) {
129
+ return { success: false, error: e.message || 'unknown error' };
130
+ }
131
+ }
132
+
113
133
  /**
114
134
  * Release the Flow Designer editing lock on a flow.
115
135
  * The UI calls safeEdit(delete: flowId) when closing the editor.
@@ -3727,22 +3747,27 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
3727
3747
  }
3728
3748
 
3729
3749
  // ────────────────────────────────────────────────────────────────
3730
- // OPEN_FLOW — acquire Flow Designer editing lock (processflow GET)
3750
+ // OPEN_FLOW — acquire Flow Designer editing lock (safeEdit create)
3731
3751
  // ────────────────────────────────────────────────────────────────
3732
3752
  case 'open_flow': {
3733
3753
  if (!args.flow_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required for open_flow');
3734
3754
  var openFlowId = await resolveFlowId(client, args.flow_id);
3735
3755
  var openSummary = summary();
3756
+
3757
+ // Step 1: Load flow data via processflow GET (same as UI)
3736
3758
  try {
3737
- // The processflow GET is what the Flow Designer UI calls when opening a flow for editing.
3738
- // This acquires the editing lock so subsequent GraphQL mutations can work.
3739
3759
  await client.get('/api/now/processflow/flow/' + openFlowId);
3740
- openSummary.success('Flow opened for editing').field('Flow', openFlowId)
3760
+ } catch (_) { /* best-effort — flow data load is not critical for lock acquisition */ }
3761
+
3762
+ // Step 2: Acquire editing lock via safeEdit create mutation (required for GraphQL mutations)
3763
+ var lockResult = await acquireFlowEditingLock(client, openFlowId);
3764
+ if (lockResult.success) {
3765
+ openSummary.success('Flow opened for editing (lock acquired)').field('Flow', openFlowId)
3741
3766
  .line('You can now use add_action, add_flow_logic, etc. Call close_flow when done.');
3742
3767
  return createSuccessResult({ action: 'open_flow', flow_id: openFlowId, editing_session: true }, {}, openSummary.build());
3743
- } catch (e: any) {
3744
- openSummary.error('Failed to open flow for editing: ' + (e.message || 'unknown')).field('Flow', openFlowId);
3745
- return createErrorResult('Failed to open flow for editing: ' + (e.message || 'unknown'));
3768
+ } else {
3769
+ openSummary.error('Cannot open flow: ' + (lockResult.error || 'lock acquisition failed')).field('Flow', openFlowId);
3770
+ return createErrorResult('Cannot open flow for editing: ' + (lockResult.error || 'lock acquisition failed'));
3746
3771
  }
3747
3772
  }
3748
3773