@flowfuse/nr-assistant 0.6.1-f8348b5-202509051340.0 → 0.6.1-fd37d56-202512091447.0

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/README.md CHANGED
@@ -1,9 +1,10 @@
1
- # FlowFuse Node-RED Assistant
1
+ # FlowFuse Node-RED Expert Plugin
2
2
 
3
- A Node-RED plugin to assist FlowFuse users.
3
+ A plugin to bring AI assistance to your Node-RED flow building.
4
4
 
5
+ This plugin is preinstalled in Node-RED instances hosted and managed by the FlowFuse platform.
5
6
 
6
- **This plugin can only be used within FlowFuse-managed Node-RED instances.**
7
+ It can also be installed locally for use outside of FlowFuse - but does require a FlowFuse Cloud user account.
7
8
 
8
9
  FlowFuse is the Industrial application platform for building and operating custom industrial solutions that digitalize processes and operations. It integrates seamlessly into both IT and OT environments, leveraging Node-RED to enable teams to connect, collect, transform, and visualize data from industrial systems. Companies use FlowFuse to manage, scale, and secure their Node-RED-based applications across industrial environments.
9
10
 
@@ -36,7 +37,6 @@ This plugin is designed to assist users of the FlowFuse platform by providing to
36
37
 
37
38
 
38
39
  ### Inline Code Completions
39
- NOTE: This feature will be limited to pro and enterprise tiers.
40
40
 
41
41
  #### functions
42
42
  ![flowfuse-assistant-inline-function-completions](https://github.com/user-attachments/assets/487b07be-861b-48d1-88c7-22c9cebffefa)
@@ -57,29 +57,6 @@ npm install @flowfuse/nr-assistant
57
57
 
58
58
  Client-side portion of the plugin is in `index.html`. The server side code is in `index.js`
59
59
 
60
-
61
- ## NOTES:
62
-
63
- * Requires the settings.js file to contain the following:
64
-
65
- ```json
66
- {
67
- "flowforge": {
68
- "assistant": {
69
- "enabled": true,
70
- "url": "https://", // URL of the AI service
71
- "token": "", // API token for the AI service
72
- "requestTimeout": 60000 // Timeout value for the AI service request
73
- }
74
- }
75
- }
76
- ```
77
-
78
- These values are automatically set when running within the FlowFuse platform via the `nr-launcher` component.
79
-
80
- The `url` and `token` are for an AI service hosted by FlowFuse; it is not publicly available for use outside of the FlowFuse platform.
81
-
82
-
83
60
  ## Limitations
84
61
 
85
62
  ### Function Builder
package/completions.html CHANGED
@@ -91,7 +91,7 @@
91
91
  debug('prediction', typeSearchSuggestions)
92
92
  return typeSearchSuggestions || []
93
93
  },
94
- name: 'Node-RED Assistant Completions',
94
+ name: 'Node-RED Expert Completions',
95
95
  icon: 'font-awesome/fa-magic',
96
96
  onadd: async function () {
97
97
  if (!window.FFAssistantUtils) {
package/index.html CHANGED
@@ -50,7 +50,6 @@
50
50
  let initialisedInterlock = false
51
51
  let mcpReadyInterlock = false
52
52
  let assistantInitialised = false
53
- let mcpReady = false
54
53
  debug('Loading Node-RED Assistant Plugin...')
55
54
  const plugin = {
56
55
  type: 'assistant',
@@ -62,20 +61,18 @@
62
61
  }
63
62
  RED.comms.subscribe('nr-assistant/#', (topic, msg) => {
64
63
  debug('comms', topic, msg)
65
- if (topic === 'nr-assistant/initialise' && !initialisedInterlock) {
66
- initialisedInterlock = true
64
+ if (topic === 'nr-assistant/initialise') {
65
+ assistantOptions.standalone = !!msg?.standalone
67
66
  assistantOptions.enabled = !!msg?.enabled
68
67
  assistantOptions.requestTimeout = msg?.requestTimeout || AI_TIMEOUT
69
68
  assistantOptions.tablesEnabled = msg?.tablesEnabled === true
70
69
  assistantOptions.inlineCompletionsEnabled = msg?.inlineCompletionsEnabled === true
71
70
  initAssistant(msg)
72
- RED.actions.add('flowfuse-nr-assistant:function-builder', showFunctionBuilderPrompt, { label: '@flowfuse/nr-assistant/flowfuse-nr-assistant:function-builder.action.label' })
73
- setMenuShortcutKey('ff-assistant-function-builder', 'red-ui-workspace', 'ctrl-alt-f', 'flowfuse-nr-assistant:function-builder')
74
71
  }
75
- if (topic === 'nr-assistant/mcp/ready' && !mcpReadyInterlock) {
76
- mcpReadyInterlock = true
77
- mcpReady = !!msg?.enabled && assistantOptions.enabled
78
- if (mcpReady) {
72
+ if (topic === 'nr-assistant/mcp/ready') {
73
+ if (!mcpReadyInterlock && !!msg?.enabled) {
74
+ mcpReadyInterlock = true
75
+ // Complete first time setup
79
76
  debug('assistant MCP initialised')
80
77
  RED.actions.add('flowfuse-nr-assistant:explain-flows', explainSelectedNodes, { label: '@flowfuse/nr-assistant/flowfuse-nr-assistant:explain-flows.action.label' })
81
78
  const menuEntry = {
@@ -89,6 +86,12 @@
89
86
  }
90
87
  RED.menu.addItem('red-ui-header-button-ff-ai', menuEntry)
91
88
  setMenuShortcutKey('ff-assistant-explain-flows', 'red-ui-workspace', 'ctrl-alt-e', 'flowfuse-nr-assistant:explain-flows')
89
+ } else if (mcpReadyInterlock) {
90
+ if (msg?.enabled) {
91
+ RED.menu.setVisible('ff-assistant-explain-flows', true)
92
+ } else {
93
+ RED.menu.setVisible('ff-assistant-explain-flows', false)
94
+ }
92
95
  }
93
96
  }
94
97
  })
@@ -96,21 +99,117 @@
96
99
  }
97
100
  RED.plugins.registerPlugin('flowfuse-nr-assistant', plugin)
98
101
 
102
+ function createAssistantMenu () {
103
+ const toolbarMenuButton = $('<li><a id="red-ui-header-button-ff-ai" class="button" href="#"></a></li>')
104
+ const toolbarMenuButtonAnchor = toolbarMenuButton.find('a')
105
+ const deployButtonLi = $('#red-ui-header-button-deploy').closest('li')
106
+ if (deployButtonLi.length) {
107
+ deployButtonLi.before(toolbarMenuButton) // add the button before the deploy button
108
+ } else {
109
+ toolbarMenuButton.prependTo('.red-ui-header-toolbar') // add the button leftmost of the toolbar
110
+ }
111
+ RED.popover.tooltip(toolbarMenuButtonAnchor, plugin._('name'))
112
+
113
+ /* NOTE: For the menu entries icons' property...
114
+ If `.icon` is a URL (e.g. resource/xxx/icon.svg), the RED.menu API will add it as an <img> tag.
115
+ That makes it impossible to set the fill colour of the SVG PATH via a CSS var.
116
+ So, by not specifying an icon URL, an <i> tag with the class set to <icon> will be created by the API
117
+ This permits us to use CSS classes (defined below) that can set the icon and affect the fill colour
118
+ */
119
+ debug('Building FlowFuse Expert menu')
120
+
121
+ const ffAssistantMenu = [
122
+ { id: 'ff-assistant-title', label: plugin._('name'), visible: true }, // header
123
+ null // separator
124
+ ]
125
+ RED.menu.init({ id: 'red-ui-header-button-ff-ai', options: ffAssistantMenu })
126
+ }
127
+ function showLoginPrompt () {
128
+ $.ajax({
129
+ contentType: 'application/json',
130
+ url: 'nr-assistant/auth/start',
131
+ method: 'POST',
132
+ data: JSON.stringify({
133
+ editorURL: window.location.origin + window.location.pathname
134
+ })
135
+ }).then(data => {
136
+ if (data && data.path && data.state) {
137
+ const handleAuthCallback = function (evt) {
138
+ debug('handleAuthCallback', evt)
139
+ try {
140
+ const message = JSON.parse(evt.data)
141
+ if (message.code === 'flowfuse-auth-complete') {
142
+ showNotification('Connected to FlowFuse', { type: 'success' })
143
+ if (message.state === data.state) {
144
+ RED.menu.setVisible('ff-assistant-login', false)
145
+ RED.menu.setVisible('ff-assistant-function-builder', true)
146
+ }
147
+ } else if (message.code === 'flowfuse-auth-error') {
148
+ showNotification('Failed to connect to FlowFuse', { type: 'error' })
149
+ console.warn('Failed to connect to FlowFuse:', message.error)
150
+ }
151
+ } catch (err) {}
152
+ window.removeEventListener('message', handleAuthCallback, false)
153
+ }
154
+ window.open(document.location.toString().replace(/[?#].*$/, '') + data.path, 'FlowFuseNodeREDPluginAuthWindow', 'menubar=no,location=no,toolbar=no,chrome,height=650,width=500')
155
+ window.addEventListener('message', handleAuthCallback, false)
156
+ } else if (data && data.error) {
157
+ RED.notify(`Failed to connect to server: ${data.error}`, { type: 'error' })
158
+ }
159
+ })
160
+ }
99
161
  function initAssistant (options) {
100
- if (assistantInitialised) {
101
- return
162
+ debug('initialising...', assistantOptions)
163
+ if (!initialisedInterlock) {
164
+ // Initialiise common UI elements
165
+ initialisedInterlock = true
166
+ createAssistantMenu()
167
+ if (assistantOptions.standalone) {
168
+ RED.menu.addItem('red-ui-header-button-ff-ai', {
169
+ id: 'ff-assistant-login',
170
+ label: `<span>${plugin._('login.menu.label')}</span>`,
171
+ onselect: showLoginPrompt,
172
+ visible: !assistantOptions.enabled
173
+ })
174
+ }
175
+ RED.menu.addItem('red-ui-header-button-ff-ai', {
176
+ id: 'ff-assistant-function-builder',
177
+ icon: 'ff-assistant-menu-icon function',
178
+ label: `<span>${plugin._('function-builder.menu.label')}</span>`,
179
+ sublabel: plugin._('function-builder.menu.description'),
180
+ onselect: 'flowfuse-nr-assistant:function-builder',
181
+ shortcutSpan: $('<span class="red-ui-popover-key"></span>'),
182
+ visible: assistantOptions.enabled
183
+ })
102
184
  }
103
- debug('initialising...')
185
+
104
186
  if (!assistantOptions.enabled) {
187
+ if (assistantOptions.standalone) {
188
+ RED.menu.setVisible('ff-assistant-login', true)
189
+ }
190
+ RED.menu.setVisible('ff-assistant-function-builder', false)
105
191
  console.warn(plugin._('errors.assistant-not-enabled'))
106
192
  return
193
+ } else {
194
+ if (assistantOptions.standalone) {
195
+ RED.menu.setVisible('ff-assistant-login', false)
196
+ }
197
+ RED.menu.setVisible('ff-assistant-function-builder', true)
198
+ }
199
+
200
+ if (!assistantInitialised) {
201
+ registerMonacoExtensions()
202
+ RED.actions.add('flowfuse-nr-assistant:function-builder', showFunctionBuilderPrompt, { label: '@flowfuse/nr-assistant/flowfuse-nr-assistant:function-builder.action.label' })
203
+ setMenuShortcutKey('ff-assistant-function-builder', 'red-ui-workspace', 'ctrl-alt-f', 'flowfuse-nr-assistant:function-builder')
204
+ assistantInitialised = true
107
205
  }
206
+ }
108
207
 
208
+ function registerMonacoExtensions () {
109
209
  if (!window.monaco) {
110
210
  console.warn('Monaco editor not found. Unable to register code lens provider. Consider using the Monaco editor for a better experience.')
111
211
  return
112
212
  }
113
-
114
213
  const funcCommandId = 'nr-assistant-fn-inline'
115
214
  const jsonCommandId = 'nr-assistant-json-inline'
116
215
  const cssCommandId = 'nr-assistant-css-inline'
@@ -121,6 +220,9 @@
121
220
 
122
221
  monaco.languages.registerCodeLensProvider('javascript', {
123
222
  provideCodeLenses: function (model, token) {
223
+ if (!assistantOptions.enabled) {
224
+ return
225
+ }
124
226
  const thisEditor = getMonacoEditorForModel(model)
125
227
  if (!thisEditor) {
126
228
  return
@@ -168,8 +270,8 @@
168
270
  }
169
271
  codeLens.command = {
170
272
  id: codeLens.id,
171
- title: 'Ask the FlowFuse Assistant 🪄',
172
- tooltip: 'Click to ask FlowFuse Assistant for help writing code',
273
+ title: 'Ask the FlowFuse Expert 🪄',
274
+ tooltip: 'Click to ask FlowFuse Expert for help writing code',
173
275
  arguments: [model, codeLens, token]
174
276
  }
175
277
  return codeLens
@@ -178,6 +280,9 @@
178
280
 
179
281
  monaco.languages.registerCodeLensProvider('json', {
180
282
  provideCodeLenses: function (model, token) {
283
+ if (!assistantOptions.enabled) {
284
+ return
285
+ }
181
286
  const thisEditor = getMonacoEditorForModel(model)
182
287
  if (!thisEditor) {
183
288
  return
@@ -203,8 +308,8 @@
203
308
  }
204
309
  codeLens.command = {
205
310
  id: codeLens.id,
206
- title: 'Ask the FlowFuse Assistant 🪄',
207
- tooltip: 'Click to ask FlowFuse Assistant for help with JSON',
311
+ title: 'Ask the FlowFuse Expert 🪄',
312
+ tooltip: 'Click to ask FlowFuse Expert for help with JSON',
208
313
  arguments: [model, codeLens, token]
209
314
  }
210
315
  return codeLens
@@ -213,6 +318,9 @@
213
318
 
214
319
  monaco.languages.registerCodeLensProvider('css', {
215
320
  provideCodeLenses: function (model, token) {
321
+ if (!assistantOptions.enabled) {
322
+ return
323
+ }
216
324
  debug('CSS CodeLens provider called', model, token)
217
325
  const thisEditor = getMonacoEditorForModel(model)
218
326
  const node = RED.view.selection()?.nodes?.[0]
@@ -242,8 +350,8 @@
242
350
  }
243
351
  codeLens.command = {
244
352
  id: codeLens.id,
245
- title: 'Ask the FlowFuse Assistant 🪄',
246
- tooltip: 'Click to ask FlowFuse Assistant for help with CSS',
353
+ title: 'Ask the FlowFuse Expert 🪄',
354
+ tooltip: 'Click to ask FlowFuse Expert for help with CSS',
247
355
  arguments: [model, codeLens, token]
248
356
  }
249
357
  return codeLens
@@ -252,6 +360,9 @@
252
360
 
253
361
  monaco.languages.registerCodeLensProvider('html', {
254
362
  provideCodeLenses: function (model, token) {
363
+ if (!assistantOptions.enabled) {
364
+ return
365
+ }
255
366
  debug('HTML CodeLens provider called', model, token)
256
367
  const thisEditor = getMonacoEditorForModel(model)
257
368
  if (!thisEditor) {
@@ -284,8 +395,8 @@
284
395
  }
285
396
  codeLens.command = {
286
397
  id: codeLens.id,
287
- title: 'Ask the FlowFuse Assistant 🪄',
288
- tooltip: 'Click to ask FlowFuse Assistant for help with VUE or HTML',
398
+ title: 'Ask the FlowFuse Expert 🪄',
399
+ tooltip: 'Click to ask FlowFuse Expert for help with VUE or HTML',
289
400
  arguments: [model, codeLens, token]
290
401
  }
291
402
  return codeLens
@@ -294,6 +405,9 @@
294
405
 
295
406
  assistantOptions.tablesEnabled && monaco.languages.registerCodeLensProvider('sql', {
296
407
  provideCodeLenses: function (model, token) {
408
+ if (!assistantOptions.enabled) {
409
+ return
410
+ }
297
411
  debug('SQL CodeLens provider called', model, token)
298
412
  const thisEditor = getMonacoEditorForModel(model)
299
413
  if (!thisEditor) {
@@ -326,8 +440,8 @@
326
440
  }
327
441
  codeLens.command = {
328
442
  id: codeLens.id,
329
- title: 'Ask the FlowFuse Assistant 🪄',
330
- tooltip: 'Click to ask FlowFuse Assistant for help with PostgreSQL',
443
+ title: 'Ask the FlowFuse Expert 🪄',
444
+ tooltip: 'Click to ask FlowFuse Expert for help with PostgreSQL',
331
445
  arguments: [model, codeLens, token]
332
446
  }
333
447
  return codeLens
@@ -365,8 +479,8 @@
365
479
  }
366
480
  /** @type {PromptUIOptions} */
367
481
  const uiOptions = {
368
- title: 'FlowFuse Assistant : Function Code',
369
- explanation: 'The FlowFuse Assistant can help you write JavaScript code.',
482
+ title: 'FlowFuse Expert : Function Code',
483
+ explanation: 'The FlowFuse Expert can help you write JavaScript code.',
370
484
  description: 'Enter a short description of what you want it to do.'
371
485
  }
372
486
  doPrompt(node, thisEditor, promptOptions, uiOptions, (error, response) => {
@@ -451,8 +565,8 @@
451
565
  }
452
566
  /** @type {PromptUIOptions} */
453
567
  const uiOptions = {
454
- title: 'FlowFuse Assistant : JSON',
455
- explanation: 'The FlowFuse Assistant can help you write JSON.',
568
+ title: 'FlowFuse Expert : JSON',
569
+ explanation: 'The FlowFuse Expert can help you write JSON.',
456
570
  description: 'Enter a short description of what you want it to do.'
457
571
  }
458
572
  doPrompt(node, thisEditor, promptOptions, uiOptions, (error, response) => {
@@ -513,8 +627,8 @@
513
627
  }
514
628
  /** @type {PromptUIOptions} */
515
629
  const uiOptions = {
516
- title: 'FlowFuse Assistant : CSS',
517
- explanation: 'The FlowFuse Assistant can help you write CSS.',
630
+ title: 'FlowFuse Expert : CSS',
631
+ explanation: 'The FlowFuse Expert can help you write CSS.',
518
632
  description: 'Enter a short description of what you want it to do.'
519
633
  }
520
634
  doPrompt(node, thisEditor, promptOptions, uiOptions, (error, response) => {
@@ -575,8 +689,8 @@
575
689
  }
576
690
  /** @type {PromptUIOptions} */
577
691
  const uiOptions = {
578
- title: 'FlowFuse Assistant : Dashboard 2 UI Template',
579
- explanation: 'The FlowFuse Assistant can help you write HTML, VUE and JavaScript.',
692
+ title: 'FlowFuse Expert : Dashboard 2 UI Template',
693
+ explanation: 'The FlowFuse Expert can help you write HTML, VUE and JavaScript.',
580
694
  description: 'Enter a short description of what you want it to do.'
581
695
  }
582
696
  doPrompt(node, thisEditor, promptOptions, uiOptions, (error, response) => {
@@ -638,8 +752,8 @@
638
752
  }
639
753
  /** @type {PromptUIOptions} */
640
754
  const uiOptions = {
641
- title: 'FlowFuse Assistant : FlowFuse Query',
642
- explanation: 'The FlowFuse Assistant can help you write SQL queries.',
755
+ title: 'FlowFuse Expert : FlowFuse Query',
756
+ explanation: 'The FlowFuse Expert can help you write SQL queries.',
643
757
  description: 'Enter a short description of what you want it to do.'
644
758
  }
645
759
  doPrompt(node, thisEditor, promptOptions, uiOptions, (error, response) => {
@@ -946,7 +1060,9 @@
946
1060
  provideInlineCompletions: (model, position, context, token) =>
947
1061
  new Promise((resolve) => {
948
1062
  debug('provideInlineCompletions: handling inline completion for languageId ' + languageId, position, context, token)
949
-
1063
+ if (!assistantOptions.enabled) {
1064
+ return
1065
+ }
950
1066
  // prep - get the editor and node, exit if not found or not supported
951
1067
  const { editor, node } = getEditorAndNode(model)
952
1068
  if (!editor || !node) { return }
@@ -1025,41 +1141,7 @@
1025
1141
  }
1026
1142
  supportedInlineCompletions.forEach(options => registerAIInlineCompletions(options))
1027
1143
  }
1028
-
1029
- const toolbarMenuButton = $('<li><a id="red-ui-header-button-ff-ai" class="button" href="#"></a></li>')
1030
- const toolbarMenuButtonAnchor = toolbarMenuButton.find('a')
1031
- const deployButtonLi = $('#red-ui-header-button-deploy').closest('li')
1032
- if (deployButtonLi.length) {
1033
- deployButtonLi.before(toolbarMenuButton) // add the button before the deploy button
1034
- } else {
1035
- toolbarMenuButton.prependTo('.red-ui-header-toolbar') // add the button leftmost of the toolbar
1036
- }
1037
- RED.popover.tooltip(toolbarMenuButtonAnchor, plugin._('name'))
1038
-
1039
- /* NOTE: For the menu entries icons' property...
1040
- If `.icon` is a URL (e.g. resource/xxx/icon.svg), the RED.menu API will add it as an <img> tag.
1041
- That makes it impossible to set the fill colour of the SVG PATH via a CSS var.
1042
- So, by not specifying an icon URL, an <i> tag with the class set to <icon> will be created by the API
1043
- This permits us to use CSS classes (defined below) that can set the icon and affect the fill colour
1044
- */
1045
- debug('Building FlowFuse Assistant menu')
1046
- const ffAssistantMenu = [
1047
- { id: 'ff-assistant-title', label: plugin._('name'), visible: true }, // header
1048
- null, // separator
1049
- {
1050
- id: 'ff-assistant-function-builder',
1051
- icon: 'ff-assistant-menu-icon function',
1052
- label: `<span>${plugin._('function-builder.menu.label')}</span>`,
1053
- sublabel: plugin._('function-builder.menu.description'),
1054
- onselect: 'flowfuse-nr-assistant:function-builder',
1055
- shortcutSpan: $('<span class="red-ui-popover-key"></span>'),
1056
- visible: true
1057
- }
1058
- ]
1059
- RED.menu.init({ id: 'red-ui-header-button-ff-ai', options: ffAssistantMenu })
1060
- assistantInitialised = true
1061
1144
  }
1062
-
1063
1145
  const previousPrompts = {}
1064
1146
 
1065
1147
  /**
@@ -1151,7 +1233,7 @@
1151
1233
 
1152
1234
  function getUserInput ({ title, explanation, description, placeholder, defaultInput } = {
1153
1235
  title: plugin._('name'),
1154
- explanation: 'The FlowFuse Assistant can help you create things.',
1236
+ explanation: 'The FlowFuse Expert can help you create things.',
1155
1237
  description: 'Enter a short description explaining what you want it to do.',
1156
1238
  placeholder: '',
1157
1239
  defaultInput: ''
@@ -1200,7 +1282,7 @@
1200
1282
  },
1201
1283
  buttons: [
1202
1284
  {
1203
- text: 'Ask the FlowFuse Assistant 🪄',
1285
+ text: 'Ask the FlowFuse Expert 🪄',
1204
1286
  // class: 'primary',
1205
1287
  click: function () {
1206
1288
  const prompt = dialog.find('#ff-nr-ai-dialog-input-editor').val()
@@ -1225,7 +1307,7 @@
1225
1307
  }
1226
1308
  getUserInput({
1227
1309
  defaultInput: previousFunctionBuilderPrompt,
1228
- title: title || 'FlowFuse Assistant : Create A Function Node',
1310
+ title: title || 'FlowFuse Expert : Create A Function Node',
1229
1311
  explanation: plugin._('function-builder.dialog-input.explanation'),
1230
1312
  description: plugin._('function-builder.dialog-input.description')
1231
1313
  }).then((prompt) => {
@@ -1421,7 +1503,7 @@
1421
1503
  const options = {
1422
1504
  buttons: [{
1423
1505
  text: plugin._('explain-flows.dialog-result.close-button'),
1424
- icon: 'ui-icon-close',
1506
+ // icon: 'ui-icon-close',
1425
1507
  class: 'primary',
1426
1508
  click: function () {
1427
1509
  $(dlg).dialog('close')
@@ -1435,7 +1517,7 @@
1435
1517
  options.buttons.unshift(
1436
1518
  {
1437
1519
  text: plugin._('explain-flows.dialog-result.copy-button'),
1438
- icon: 'ui-icon-copy',
1520
+ // icon: 'ui-icon-copy',
1439
1521
  click: function () {
1440
1522
  navigator.clipboard.writeText(text).then(() => {
1441
1523
  showNotification('Copied to clipboard', { type: 'success' })
@@ -1452,7 +1534,7 @@
1452
1534
  options.buttons.unshift(
1453
1535
  {
1454
1536
  text: plugin._('explain-flows.dialog-result.comment-node-button'),
1455
- icon: 'ui-icon-comment',
1537
+ // icon: 'ui-icon-comment',
1456
1538
  // class: 'primary',
1457
1539
  click: function () {
1458
1540
  const commentNode = {
@@ -1502,8 +1584,8 @@
1502
1584
  }
1503
1585
  getUserInput({
1504
1586
  defaultInput: previousFlowBuilderPrompt,
1505
- title: title || 'FlowFuse Assistant : Flow Builder',
1506
- explanation: 'The FlowFuse Assistant can help you create a new flow.',
1587
+ title: title || 'FlowFuse Expert : Flow Builder',
1588
+ explanation: 'The FlowFuse Expert can help you create a new flow.',
1507
1589
  description: 'Enter a short description of what you want the flow to do.'
1508
1590
  }).then((prompt) => {
1509
1591
  /** @type {JQueryXHR} */
@@ -1589,7 +1671,7 @@
1589
1671
  }
1590
1672
 
1591
1673
  /**
1592
- * Shows a busy notification with a cancel button
1674
+ * Shows a notification
1593
1675
  * @param {string} message - The message to display in the notification
1594
1676
  * @param {object} [options] - The options to pass to the notification
1595
1677
  * @param {'error'|'warning'|'success'|'compact'} [options.type] - The type of notification to display
@@ -1663,25 +1745,25 @@
1663
1745
  const minutes = Math.floor(seconds / 60)
1664
1746
  const remainingSeconds = seconds % 60
1665
1747
  if (minutes > 0) {
1666
- RED.notify(`Sorry, the FlowFuse Assistant is busy. Please try again in ${minutes} minute${minutes > 1 ? 's' : ''}.`, 'warning')
1748
+ RED.notify(`Sorry, the FlowFuse Expert is busy. Please try again in ${minutes} minute${minutes > 1 ? 's' : ''}.`, 'warning')
1667
1749
  } else {
1668
- RED.notify(`Sorry, the FlowFuse Assistant is busy. Please try again in ${remainingSeconds} second${remainingSeconds > 1 ? 's' : ''}.`, 'warning')
1750
+ RED.notify(`Sorry, the FlowFuse Expert is busy. Please try again in ${remainingSeconds} second${remainingSeconds > 1 ? 's' : ''}.`, 'warning')
1669
1751
  }
1670
1752
  return
1671
1753
  }
1672
- RED.notify('Sorry, the FlowFuse Assistant is busy. Please try again later.', 'warning')
1754
+ RED.notify('Sorry, the FlowFuse Expert is busy. Please try again later.', 'warning')
1673
1755
  return
1674
1756
  }
1675
1757
  if (jqXHR.status === 404) {
1676
- RED.notify('Sorry, the FlowFuse Assistant is not available at the moment', 'warning')
1758
+ RED.notify('Sorry, the FlowFuse Expert is not available at the moment', 'warning')
1677
1759
  return
1678
1760
  }
1679
1761
  if (jqXHR.status === 401) {
1680
- RED.notify('Sorry, you are not authorised to use the FlowFuse Assistant', 'warning')
1762
+ RED.notify('Sorry, you are not authorised to use the FlowFuse Expert', 'warning')
1681
1763
  return
1682
1764
  }
1683
1765
  if (jqXHR.status >= 400 && jqXHR.status < 500) {
1684
- let message = 'Sorry, the FlowFuse Assistant cannot help with this request'
1766
+ let message = 'Sorry, the FlowFuse Expert cannot help with this request'
1685
1767
  if (jqXHR.responseJSON?.body?.code === 'assistant_service_denied' && jqXHR.responseJSON?.body?.error) {
1686
1768
  message = jqXHR.responseJSON.body.error
1687
1769
  }
@@ -1907,7 +1989,7 @@
1907
1989
  #red-ui-header ul#red-ui-header-button-ff-ai-submenu.red-ui-menu-dropdown {
1908
1990
  width: 300px !important; /* make the submenu wider to prevent wrapping elements */
1909
1991
  }
1910
- #red-ui-header ul.red-ui-menu-dropdown li a {
1992
+ #red-ui-header #red-ui-header-button-ff-ai+ul.red-ui-menu-dropdown li a {
1911
1993
  padding: 8px 16px;
1912
1994
  }
1913
1995
 
package/index.js CHANGED
@@ -1,22 +1,30 @@
1
1
  module.exports = (RED) => {
2
2
  const assistant = require('./lib/assistant.js')
3
-
3
+ const settings = require('./lib/settings.js')
4
+ const auth = require('./lib/auth/index.js')
4
5
  RED.plugins.registerPlugin('flowfuse-nr-assistant', {
5
6
  type: 'assistant',
6
- name: 'Node-RED Assistant Plugin',
7
+ name: 'Node-RED Expert Plugin',
7
8
  icon: 'font-awesome/fa-magic',
8
9
  settings: {
9
10
  '*': { exportable: true }
10
11
  },
11
- onadd: function () {
12
- const assistantSettings = require('./lib/settings.js').getSettings(RED)
13
- if (!assistant.isInitialized && !assistant.isLoading) {
14
- assistant.init(RED, assistantSettings).then(() => {
15
- // All good, the assistant is initialized.
16
- // Any info messages made during initialization are logged in the assistant module
17
- }).catch((error) => {
18
- RED.log.error('Failed to initialize FlowFuse Assistant Plugin:', error)
19
- })
12
+ onadd: async function () {
13
+ try {
14
+ await auth.init(RED)
15
+ const assistantSettings = await settings.getSettings(RED)
16
+ if (!assistant.isInitialized && !assistant.isLoading) {
17
+ assistant.init(RED, assistantSettings).then(() => {
18
+ // All good, the assistant is initialized.
19
+ // Any info messages made during initialization are logged in the assistant module
20
+ }).catch((error) => {
21
+ console.error(error)
22
+ RED.log.error('Failed to initialize FlowFuse Expert Plugin:', error)
23
+ })
24
+ }
25
+ } catch (error) {
26
+ console.error(error)
27
+ RED.log.error('Failed to initialize FlowFuse Expert Plugin:', error)
20
28
  }
21
29
  }
22
30
  })