@octo-cyber/workflow 0.5.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.
Files changed (297) hide show
  1. package/dist/controllers/ai-workflow-session.controller.d.ts +19 -0
  2. package/dist/controllers/ai-workflow-session.controller.d.ts.map +1 -0
  3. package/dist/controllers/ai-workflow-session.controller.js +135 -0
  4. package/dist/controllers/ai-workflow-session.controller.js.map +1 -0
  5. package/dist/controllers/credential.controller.d.ts +68 -0
  6. package/dist/controllers/credential.controller.d.ts.map +1 -0
  7. package/dist/controllers/credential.controller.js +303 -0
  8. package/dist/controllers/credential.controller.js.map +1 -0
  9. package/dist/controllers/index.d.ts +3 -0
  10. package/dist/controllers/index.d.ts.map +1 -0
  11. package/dist/controllers/index.js +8 -0
  12. package/dist/controllers/index.js.map +1 -0
  13. package/dist/controllers/workflow-ai.controller.d.ts +23 -0
  14. package/dist/controllers/workflow-ai.controller.d.ts.map +1 -0
  15. package/dist/controllers/workflow-ai.controller.js +164 -0
  16. package/dist/controllers/workflow-ai.controller.js.map +1 -0
  17. package/dist/controllers/workflow.controller.d.ts +66 -0
  18. package/dist/controllers/workflow.controller.d.ts.map +1 -0
  19. package/dist/controllers/workflow.controller.js +239 -0
  20. package/dist/controllers/workflow.controller.js.map +1 -0
  21. package/dist/core/expression-resolver.d.ts +49 -0
  22. package/dist/core/expression-resolver.d.ts.map +1 -0
  23. package/dist/core/expression-resolver.js +113 -0
  24. package/dist/core/expression-resolver.js.map +1 -0
  25. package/dist/core/node-registry.d.ts +24 -0
  26. package/dist/core/node-registry.d.ts.map +1 -0
  27. package/dist/core/node-registry.js +62 -0
  28. package/dist/core/node-registry.js.map +1 -0
  29. package/dist/core/workflow-executor.d.ts +50 -0
  30. package/dist/core/workflow-executor.d.ts.map +1 -0
  31. package/dist/core/workflow-executor.js +458 -0
  32. package/dist/core/workflow-executor.js.map +1 -0
  33. package/dist/entities/ai-workflow-session.entity.d.ts +17 -0
  34. package/dist/entities/ai-workflow-session.entity.d.ts.map +1 -0
  35. package/dist/entities/ai-workflow-session.entity.js +70 -0
  36. package/dist/entities/ai-workflow-session.entity.js.map +1 -0
  37. package/dist/entities/ai-workflow-version.entity.d.ts +18 -0
  38. package/dist/entities/ai-workflow-version.entity.d.ts.map +1 -0
  39. package/dist/entities/ai-workflow-version.entity.js +71 -0
  40. package/dist/entities/ai-workflow-version.entity.js.map +1 -0
  41. package/dist/entities/credential-definition.entity.d.ts +17 -0
  42. package/dist/entities/credential-definition.entity.d.ts.map +1 -0
  43. package/dist/entities/credential-definition.entity.js +66 -0
  44. package/dist/entities/credential-definition.entity.js.map +1 -0
  45. package/dist/entities/index.d.ts +9 -0
  46. package/dist/entities/index.d.ts.map +1 -0
  47. package/dist/entities/index.js +22 -0
  48. package/dist/entities/index.js.map +1 -0
  49. package/dist/entities/workflow-definition.entity.d.ts +20 -0
  50. package/dist/entities/workflow-definition.entity.d.ts.map +1 -0
  51. package/dist/entities/workflow-definition.entity.js +85 -0
  52. package/dist/entities/workflow-definition.entity.js.map +1 -0
  53. package/dist/entities/workflow-execution.entity.d.ts +26 -0
  54. package/dist/entities/workflow-execution.entity.d.ts.map +1 -0
  55. package/dist/entities/workflow-execution.entity.js +99 -0
  56. package/dist/entities/workflow-execution.entity.js.map +1 -0
  57. package/dist/index.d.ts +52 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +96 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/n8n/cipher/cipher.d.ts +11 -0
  62. package/dist/n8n/cipher/cipher.d.ts.map +1 -0
  63. package/dist/n8n/cipher/cipher.js +54 -0
  64. package/dist/n8n/cipher/cipher.js.map +1 -0
  65. package/dist/n8n/controllers/n8n-session.controller.d.ts +20 -0
  66. package/dist/n8n/controllers/n8n-session.controller.d.ts.map +1 -0
  67. package/dist/n8n/controllers/n8n-session.controller.js +84 -0
  68. package/dist/n8n/controllers/n8n-session.controller.js.map +1 -0
  69. package/dist/n8n/index.d.ts +8 -0
  70. package/dist/n8n/index.d.ts.map +1 -0
  71. package/dist/n8n/index.js +14 -0
  72. package/dist/n8n/index.js.map +1 -0
  73. package/dist/n8n/launcher/n8n-launcher.d.ts +41 -0
  74. package/dist/n8n/launcher/n8n-launcher.d.ts.map +1 -0
  75. package/dist/n8n/launcher/n8n-launcher.js +186 -0
  76. package/dist/n8n/launcher/n8n-launcher.js.map +1 -0
  77. package/dist/n8n/nodes/OctoClaudeCliTool.node.d.ts +13 -0
  78. package/dist/n8n/nodes/OctoClaudeCliTool.node.d.ts.map +1 -0
  79. package/dist/n8n/nodes/OctoClaudeCliTool.node.js +103 -0
  80. package/dist/n8n/nodes/OctoClaudeCliTool.node.js.map +1 -0
  81. package/dist/n8n/nodes/OctoCliChatModel.node.d.ts +14 -0
  82. package/dist/n8n/nodes/OctoCliChatModel.node.d.ts.map +1 -0
  83. package/dist/n8n/nodes/OctoCliChatModel.node.js +431 -0
  84. package/dist/n8n/nodes/OctoCliChatModel.node.js.map +1 -0
  85. package/dist/n8n/nodes/OctoCodexCliTool.node.d.ts +13 -0
  86. package/dist/n8n/nodes/OctoCodexCliTool.node.d.ts.map +1 -0
  87. package/dist/n8n/nodes/OctoCodexCliTool.node.js +100 -0
  88. package/dist/n8n/nodes/OctoCodexCliTool.node.js.map +1 -0
  89. package/dist/n8n/nodes/OctoGeminiCliTool.node.d.ts +13 -0
  90. package/dist/n8n/nodes/OctoGeminiCliTool.node.d.ts.map +1 -0
  91. package/dist/n8n/nodes/OctoGeminiCliTool.node.js +91 -0
  92. package/dist/n8n/nodes/OctoGeminiCliTool.node.js.map +1 -0
  93. package/dist/n8n/nodes/OctoKnowledge.node.d.ts +6 -0
  94. package/dist/n8n/nodes/OctoKnowledge.node.d.ts.map +1 -0
  95. package/dist/n8n/nodes/OctoKnowledge.node.js +95 -0
  96. package/dist/n8n/nodes/OctoKnowledge.node.js.map +1 -0
  97. package/dist/n8n/nodes/OctoNotification.node.d.ts +6 -0
  98. package/dist/n8n/nodes/OctoNotification.node.d.ts.map +1 -0
  99. package/dist/n8n/nodes/OctoNotification.node.js +72 -0
  100. package/dist/n8n/nodes/OctoNotification.node.js.map +1 -0
  101. package/dist/n8n/nodes/OctoSyncBridge.node.d.ts +6 -0
  102. package/dist/n8n/nodes/OctoSyncBridge.node.d.ts.map +1 -0
  103. package/dist/n8n/nodes/OctoSyncBridge.node.js +60 -0
  104. package/dist/n8n/nodes/OctoSyncBridge.node.js.map +1 -0
  105. package/dist/n8n/nodes/n8n-node-types.d.ts +111 -0
  106. package/dist/n8n/nodes/n8n-node-types.d.ts.map +1 -0
  107. package/dist/n8n/nodes/n8n-node-types.js +30 -0
  108. package/dist/n8n/nodes/n8n-node-types.js.map +1 -0
  109. package/dist/n8n/nodes/octo-cli-chat-model.svg +23 -0
  110. package/dist/n8n/nodes/package.json +16 -0
  111. package/dist/n8n/proxy/css-injector.d.ts +3 -0
  112. package/dist/n8n/proxy/css-injector.d.ts.map +1 -0
  113. package/dist/n8n/proxy/css-injector.js +62 -0
  114. package/dist/n8n/proxy/css-injector.js.map +1 -0
  115. package/dist/n8n/proxy/n8n-proxy.middleware.d.ts +12 -0
  116. package/dist/n8n/proxy/n8n-proxy.middleware.d.ts.map +1 -0
  117. package/dist/n8n/proxy/n8n-proxy.middleware.js +131 -0
  118. package/dist/n8n/proxy/n8n-proxy.middleware.js.map +1 -0
  119. package/dist/n8n/proxy/n8n-theme.css +45 -0
  120. package/dist/n8n/sync/n8n-user-sync.service.d.ts +75 -0
  121. package/dist/n8n/sync/n8n-user-sync.service.d.ts.map +1 -0
  122. package/dist/n8n/sync/n8n-user-sync.service.js +286 -0
  123. package/dist/n8n/sync/n8n-user-sync.service.js.map +1 -0
  124. package/dist/n8n/watcher/n8n-execution-watcher.d.ts +39 -0
  125. package/dist/n8n/watcher/n8n-execution-watcher.d.ts.map +1 -0
  126. package/dist/n8n/watcher/n8n-execution-watcher.js +110 -0
  127. package/dist/n8n/watcher/n8n-execution-watcher.js.map +1 -0
  128. package/dist/nodes/code.node.d.ts +24 -0
  129. package/dist/nodes/code.node.d.ts.map +1 -0
  130. package/dist/nodes/code.node.js +150 -0
  131. package/dist/nodes/code.node.js.map +1 -0
  132. package/dist/nodes/error-trigger.node.d.ts +15 -0
  133. package/dist/nodes/error-trigger.node.d.ts.map +1 -0
  134. package/dist/nodes/error-trigger.node.js +53 -0
  135. package/dist/nodes/error-trigger.node.js.map +1 -0
  136. package/dist/nodes/execute-command.node.d.ts +9 -0
  137. package/dist/nodes/execute-command.node.d.ts.map +1 -0
  138. package/dist/nodes/execute-command.node.js +81 -0
  139. package/dist/nodes/execute-command.node.js.map +1 -0
  140. package/dist/nodes/filter.node.d.ts +10 -0
  141. package/dist/nodes/filter.node.d.ts.map +1 -0
  142. package/dist/nodes/filter.node.js +95 -0
  143. package/dist/nodes/filter.node.js.map +1 -0
  144. package/dist/nodes/http-request.node.d.ts +11 -0
  145. package/dist/nodes/http-request.node.d.ts.map +1 -0
  146. package/dist/nodes/http-request.node.js +139 -0
  147. package/dist/nodes/http-request.node.js.map +1 -0
  148. package/dist/nodes/if.node.d.ts +13 -0
  149. package/dist/nodes/if.node.d.ts.map +1 -0
  150. package/dist/nodes/if.node.js +137 -0
  151. package/dist/nodes/if.node.js.map +1 -0
  152. package/dist/nodes/index.d.ts +12 -0
  153. package/dist/nodes/index.d.ts.map +1 -0
  154. package/dist/nodes/index.js +26 -0
  155. package/dist/nodes/index.js.map +1 -0
  156. package/dist/nodes/manual-trigger.node.d.ts +10 -0
  157. package/dist/nodes/manual-trigger.node.d.ts.map +1 -0
  158. package/dist/nodes/manual-trigger.node.js +36 -0
  159. package/dist/nodes/manual-trigger.node.js.map +1 -0
  160. package/dist/nodes/merge.node.d.ts +15 -0
  161. package/dist/nodes/merge.node.d.ts.map +1 -0
  162. package/dist/nodes/merge.node.js +99 -0
  163. package/dist/nodes/merge.node.js.map +1 -0
  164. package/dist/nodes/noop.node.d.ts +12 -0
  165. package/dist/nodes/noop.node.d.ts.map +1 -0
  166. package/dist/nodes/noop.node.js +32 -0
  167. package/dist/nodes/noop.node.js.map +1 -0
  168. package/dist/nodes/placeholder.node.d.ts +10 -0
  169. package/dist/nodes/placeholder.node.d.ts.map +1 -0
  170. package/dist/nodes/placeholder.node.js +50 -0
  171. package/dist/nodes/placeholder.node.js.map +1 -0
  172. package/dist/nodes/remove-duplicates.node.d.ts +9 -0
  173. package/dist/nodes/remove-duplicates.node.d.ts.map +1 -0
  174. package/dist/nodes/remove-duplicates.node.js +68 -0
  175. package/dist/nodes/remove-duplicates.node.js.map +1 -0
  176. package/dist/nodes/respond-to-webhook.node.d.ts +14 -0
  177. package/dist/nodes/respond-to-webhook.node.d.ts.map +1 -0
  178. package/dist/nodes/respond-to-webhook.node.js +116 -0
  179. package/dist/nodes/respond-to-webhook.node.js.map +1 -0
  180. package/dist/nodes/schedule-trigger.node.d.ts +9 -0
  181. package/dist/nodes/schedule-trigger.node.d.ts.map +1 -0
  182. package/dist/nodes/schedule-trigger.node.js +67 -0
  183. package/dist/nodes/schedule-trigger.node.js.map +1 -0
  184. package/dist/nodes/set.node.d.ts +12 -0
  185. package/dist/nodes/set.node.d.ts.map +1 -0
  186. package/dist/nodes/set.node.js +81 -0
  187. package/dist/nodes/set.node.js.map +1 -0
  188. package/dist/nodes/sort.node.d.ts +9 -0
  189. package/dist/nodes/sort.node.d.ts.map +1 -0
  190. package/dist/nodes/sort.node.js +61 -0
  191. package/dist/nodes/sort.node.js.map +1 -0
  192. package/dist/nodes/split-in-batches.node.d.ts +9 -0
  193. package/dist/nodes/split-in-batches.node.d.ts.map +1 -0
  194. package/dist/nodes/split-in-batches.node.js +53 -0
  195. package/dist/nodes/split-in-batches.node.js.map +1 -0
  196. package/dist/nodes/split-out.node.d.ts +9 -0
  197. package/dist/nodes/split-out.node.d.ts.map +1 -0
  198. package/dist/nodes/split-out.node.js +76 -0
  199. package/dist/nodes/split-out.node.js.map +1 -0
  200. package/dist/nodes/switch.node.d.ts +16 -0
  201. package/dist/nodes/switch.node.d.ts.map +1 -0
  202. package/dist/nodes/switch.node.js +156 -0
  203. package/dist/nodes/switch.node.js.map +1 -0
  204. package/dist/nodes/wait.node.d.ts +12 -0
  205. package/dist/nodes/wait.node.d.ts.map +1 -0
  206. package/dist/nodes/wait.node.js +81 -0
  207. package/dist/nodes/wait.node.js.map +1 -0
  208. package/dist/nodes/webhook.node.d.ts +9 -0
  209. package/dist/nodes/webhook.node.d.ts.map +1 -0
  210. package/dist/nodes/webhook.node.js +69 -0
  211. package/dist/nodes/webhook.node.js.map +1 -0
  212. package/dist/services/ai-workflow-session.service.d.ts +31 -0
  213. package/dist/services/ai-workflow-session.service.d.ts.map +1 -0
  214. package/dist/services/ai-workflow-session.service.js +118 -0
  215. package/dist/services/ai-workflow-session.service.js.map +1 -0
  216. package/dist/services/credential.service.d.ts +57 -0
  217. package/dist/services/credential.service.d.ts.map +1 -0
  218. package/dist/services/credential.service.js +155 -0
  219. package/dist/services/credential.service.js.map +1 -0
  220. package/dist/services/index.d.ts +10 -0
  221. package/dist/services/index.d.ts.map +1 -0
  222. package/dist/services/index.js +14 -0
  223. package/dist/services/index.js.map +1 -0
  224. package/dist/services/push.service.d.ts +60 -0
  225. package/dist/services/push.service.d.ts.map +1 -0
  226. package/dist/services/push.service.js +121 -0
  227. package/dist/services/push.service.js.map +1 -0
  228. package/dist/services/workflow-ai.service.d.ts +61 -0
  229. package/dist/services/workflow-ai.service.d.ts.map +1 -0
  230. package/dist/services/workflow-ai.service.js +219 -0
  231. package/dist/services/workflow-ai.service.js.map +1 -0
  232. package/dist/services/workflow-context.service.d.ts +32 -0
  233. package/dist/services/workflow-context.service.d.ts.map +1 -0
  234. package/dist/services/workflow-context.service.js +155 -0
  235. package/dist/services/workflow-context.service.js.map +1 -0
  236. package/dist/services/workflow-engine.service.d.ts +90 -0
  237. package/dist/services/workflow-engine.service.d.ts.map +1 -0
  238. package/dist/services/workflow-engine.service.js +305 -0
  239. package/dist/services/workflow-engine.service.js.map +1 -0
  240. package/dist/services/workflow.service.d.ts +84 -0
  241. package/dist/services/workflow.service.d.ts.map +1 -0
  242. package/dist/services/workflow.service.js +241 -0
  243. package/dist/services/workflow.service.js.map +1 -0
  244. package/dist/triggers/cron-trigger.d.ts +39 -0
  245. package/dist/triggers/cron-trigger.d.ts.map +1 -0
  246. package/dist/triggers/cron-trigger.js +137 -0
  247. package/dist/triggers/cron-trigger.js.map +1 -0
  248. package/dist/triggers/index.d.ts +3 -0
  249. package/dist/triggers/index.d.ts.map +1 -0
  250. package/dist/triggers/index.js +8 -0
  251. package/dist/triggers/index.js.map +1 -0
  252. package/dist/triggers/webhook-trigger.d.ts +51 -0
  253. package/dist/triggers/webhook-trigger.d.ts.map +1 -0
  254. package/dist/triggers/webhook-trigger.js +122 -0
  255. package/dist/triggers/webhook-trigger.js.map +1 -0
  256. package/dist/types/index.d.ts +5 -0
  257. package/dist/types/index.d.ts.map +1 -0
  258. package/dist/types/index.js +8 -0
  259. package/dist/types/index.js.map +1 -0
  260. package/dist/types/node.types.d.ts +313 -0
  261. package/dist/types/node.types.d.ts.map +1 -0
  262. package/dist/types/node.types.js +23 -0
  263. package/dist/types/node.types.js.map +1 -0
  264. package/dist/types/workflow.types.d.ts +153 -0
  265. package/dist/types/workflow.types.d.ts.map +1 -0
  266. package/dist/types/workflow.types.js +44 -0
  267. package/dist/types/workflow.types.js.map +1 -0
  268. package/dist/utils/n8n-converter.d.ts +52 -0
  269. package/dist/utils/n8n-converter.d.ts.map +1 -0
  270. package/dist/utils/n8n-converter.js +107 -0
  271. package/dist/utils/n8n-converter.js.map +1 -0
  272. package/dist/utils/n8n-node-map.d.ts +6 -0
  273. package/dist/utils/n8n-node-map.d.ts.map +1 -0
  274. package/dist/utils/n8n-node-map.js +59 -0
  275. package/dist/utils/n8n-node-map.js.map +1 -0
  276. package/dist/workflow.module.d.ts +40 -0
  277. package/dist/workflow.module.d.ts.map +1 -0
  278. package/dist/workflow.module.js +240 -0
  279. package/dist/workflow.module.js.map +1 -0
  280. package/package.json +97 -0
  281. package/web/components/ChatPanel.tsx +344 -0
  282. package/web/components/N8nIframe.tsx +119 -0
  283. package/web/components/SessionPanel.tsx +301 -0
  284. package/web/components/ToolPanel.tsx +404 -0
  285. package/web/components/WorkflowDiff.tsx +161 -0
  286. package/web/components/WorkflowGraph.tsx +158 -0
  287. package/web/components/WorkflowPreviewPanel.tsx +186 -0
  288. package/web/hooks/use-n8n-session.ts +46 -0
  289. package/web/index.ts +29 -0
  290. package/web/manifest.ts +16 -0
  291. package/web/messages/en-US.json +94 -0
  292. package/web/messages/zh-CN.json +94 -0
  293. package/web/pages/AiDesignPage.tsx +215 -0
  294. package/web/pages/CredentialsPage.tsx +7 -0
  295. package/web/pages/ExecutionsPage.tsx +7 -0
  296. package/web/pages/WorkflowsPage.tsx +7 -0
  297. package/web/services/workflow-ai-service.ts +173 -0
@@ -0,0 +1,161 @@
1
+ 'use client'
2
+
3
+ import { useMemo } from 'react'
4
+ import { useTranslations } from 'next-intl'
5
+ import { Badge } from '@octo-cyber/ui/components/ui/badge'
6
+
7
+ interface WorkflowDiffProps {
8
+ originalJson: string
9
+ modifiedJson: string
10
+ className?: string
11
+ }
12
+
13
+ interface DiffResult {
14
+ nodesAdded: string[]
15
+ nodesRemoved: string[]
16
+ nodesModified: string[]
17
+ connectionsChanged: boolean
18
+ }
19
+
20
+ function computeDiff(originalJson: string, modifiedJson: string): DiffResult {
21
+ const result: DiffResult = {
22
+ nodesAdded: [],
23
+ nodesRemoved: [],
24
+ nodesModified: [],
25
+ connectionsChanged: false,
26
+ }
27
+
28
+ try {
29
+ const original = JSON.parse(originalJson) as Record<string, unknown>
30
+ const modified = JSON.parse(modifiedJson) as Record<string, unknown>
31
+
32
+ const origNodes = (original.nodes as Array<{ name: string }>) ?? []
33
+ const modNodes = (modified.nodes as Array<{ name: string }>) ?? []
34
+
35
+ const origNodeNames = new Set(origNodes.map((n) => n.name))
36
+ const modNodeNames = new Set(modNodes.map((n) => n.name))
37
+
38
+ for (const name of modNodeNames) {
39
+ if (!origNodeNames.has(name)) {
40
+ result.nodesAdded.push(name)
41
+ }
42
+ }
43
+
44
+ for (const name of origNodeNames) {
45
+ if (!modNodeNames.has(name)) {
46
+ result.nodesRemoved.push(name)
47
+ }
48
+ }
49
+
50
+ // Check for modified nodes (same name, different params)
51
+ for (const modNode of modNodes) {
52
+ const origNode = origNodes.find((n) => n.name === modNode.name)
53
+ if (origNode && JSON.stringify(origNode) !== JSON.stringify(modNode)) {
54
+ result.nodesModified.push(modNode.name)
55
+ }
56
+ }
57
+
58
+ // Compare connections
59
+ const origConn = JSON.stringify(original.connections ?? {})
60
+ const modConn = JSON.stringify(modified.connections ?? {})
61
+ result.connectionsChanged = origConn !== modConn
62
+ } catch {
63
+ // invalid JSON
64
+ }
65
+
66
+ return result
67
+ }
68
+
69
+ export function WorkflowDiff({
70
+ originalJson,
71
+ modifiedJson,
72
+ className,
73
+ }: WorkflowDiffProps) {
74
+ const t = useTranslations('workflows.aiDesign')
75
+ const diff = useMemo(
76
+ () => computeDiff(originalJson, modifiedJson),
77
+ [originalJson, modifiedJson],
78
+ )
79
+
80
+ const hasChanges =
81
+ diff.nodesAdded.length > 0 ||
82
+ diff.nodesRemoved.length > 0 ||
83
+ diff.nodesModified.length > 0 ||
84
+ diff.connectionsChanged
85
+
86
+ if (!hasChanges) {
87
+ return (
88
+ <div className={`p-4 text-center text-sm text-muted-foreground ${className ?? ''}`}>
89
+ {t('noChanges')}
90
+ </div>
91
+ )
92
+ }
93
+
94
+ return (
95
+ <div className={`space-y-3 ${className ?? ''}`}>
96
+ {diff.nodesAdded.length > 0 && (
97
+ <DiffSection
98
+ label={t('nodesAdded')}
99
+ items={diff.nodesAdded}
100
+ color="green"
101
+ />
102
+ )}
103
+ {diff.nodesRemoved.length > 0 && (
104
+ <DiffSection
105
+ label={t('nodesRemoved')}
106
+ items={diff.nodesRemoved}
107
+ color="red"
108
+ />
109
+ )}
110
+ {diff.nodesModified.length > 0 && (
111
+ <DiffSection
112
+ label={t('nodesModified')}
113
+ items={diff.nodesModified}
114
+ color="yellow"
115
+ />
116
+ )}
117
+ {diff.connectionsChanged && (
118
+ <div className="rounded-md border border-border p-2">
119
+ <Badge variant="outline" className="text-xs">
120
+ {t('connectionsChanged')}
121
+ </Badge>
122
+ </div>
123
+ )}
124
+ </div>
125
+ )
126
+ }
127
+
128
+ function DiffSection({
129
+ label,
130
+ items,
131
+ color,
132
+ }: {
133
+ label: string
134
+ items: string[]
135
+ color: 'green' | 'red' | 'yellow'
136
+ }) {
137
+ const colorMap = {
138
+ green: 'border-green-500/30 bg-green-500/5',
139
+ red: 'border-red-500/30 bg-red-500/5',
140
+ yellow: 'border-yellow-500/30 bg-yellow-500/5',
141
+ }
142
+
143
+ const badgeMap = {
144
+ green: 'bg-green-500/10 text-green-600 dark:text-green-400',
145
+ red: 'bg-red-500/10 text-red-600 dark:text-red-400',
146
+ yellow: 'bg-yellow-500/10 text-yellow-600 dark:text-yellow-400',
147
+ }
148
+
149
+ return (
150
+ <div className={`rounded-md border p-2 ${colorMap[color]}`}>
151
+ <p className="mb-1 text-xs font-medium text-muted-foreground">{label}</p>
152
+ <div className="flex flex-wrap gap-1">
153
+ {items.map((item) => (
154
+ <span key={item} className={`rounded px-1.5 py-0.5 text-xs ${badgeMap[color]}`}>
155
+ {item}
156
+ </span>
157
+ ))}
158
+ </div>
159
+ </div>
160
+ )
161
+ }
@@ -0,0 +1,158 @@
1
+ 'use client'
2
+
3
+ import { useMemo } from 'react'
4
+ import {
5
+ ReactFlow,
6
+ Background,
7
+ Controls,
8
+ type Node,
9
+ type Edge,
10
+ Position,
11
+ } from '@xyflow/react'
12
+ import '@xyflow/react/dist/style.css'
13
+
14
+ interface N8nNode {
15
+ name: string
16
+ type: string
17
+ position: [number, number]
18
+ parameters?: Record<string, unknown>
19
+ }
20
+
21
+ interface N8nConnection {
22
+ node: string
23
+ type: string
24
+ index: number
25
+ }
26
+
27
+ interface N8nWorkflowJson {
28
+ name?: string
29
+ nodes?: N8nNode[]
30
+ connections?: Record<string, { main?: N8nConnection[][] }>
31
+ }
32
+
33
+ interface WorkflowGraphProps {
34
+ workflowJson: string
35
+ className?: string
36
+ diffStatus?: Record<string, 'added' | 'removed' | 'modified'>
37
+ }
38
+
39
+ function getNodeColor(type: string): string {
40
+ if (type.includes('trigger') || type.includes('Trigger')) return '#e2e8f0'
41
+ if (type.includes('if') || type.includes('switch')) return '#fef3c7'
42
+ if (type.includes('http') || type.includes('Http')) return '#dbeafe'
43
+ if (type.includes('code') || type.includes('Code')) return '#e0e7ff'
44
+ if (type.includes('agent') || type.includes('Agent')) return '#ede9fe'
45
+ return '#f1f5f9'
46
+ }
47
+
48
+ function getDiffBorderColor(
49
+ status: 'added' | 'removed' | 'modified' | undefined,
50
+ ): string {
51
+ if (status === 'added') return '#22c55e'
52
+ if (status === 'removed') return '#ef4444'
53
+ if (status === 'modified') return '#eab308'
54
+ return '#94a3b8'
55
+ }
56
+
57
+ function parseWorkflowToFlow(
58
+ json: string,
59
+ diffStatus?: Record<string, 'added' | 'removed' | 'modified'>,
60
+ ): { nodes: Node[]; edges: Edge[] } {
61
+ let parsed: N8nWorkflowJson
62
+ try {
63
+ parsed = JSON.parse(json) as N8nWorkflowJson
64
+ } catch {
65
+ return { nodes: [], edges: [] }
66
+ }
67
+
68
+ const n8nNodes = parsed.nodes ?? []
69
+ const n8nConnections = parsed.connections ?? {}
70
+
71
+ const nodes: Node[] = n8nNodes.map((n) => {
72
+ const status = diffStatus?.[n.name]
73
+ const shortType = n.type.split('.').pop() ?? n.type
74
+
75
+ return {
76
+ id: n.name,
77
+ position: { x: n.position[0], y: n.position[1] },
78
+ data: { label: n.name, type: shortType },
79
+ style: {
80
+ background: getNodeColor(n.type),
81
+ border: `2px solid ${getDiffBorderColor(status)}`,
82
+ borderRadius: '8px',
83
+ padding: '8px 12px',
84
+ fontSize: '12px',
85
+ minWidth: '140px',
86
+ },
87
+ sourcePosition: Position.Right,
88
+ targetPosition: Position.Left,
89
+ }
90
+ })
91
+
92
+ const edges: Edge[] = []
93
+ for (const [sourceName, conn] of Object.entries(n8nConnections)) {
94
+ const mainOutputs = conn.main ?? []
95
+ for (let outputIdx = 0; outputIdx < mainOutputs.length; outputIdx++) {
96
+ const targets = mainOutputs[outputIdx] ?? []
97
+ for (const target of targets) {
98
+ const edgeId = `${sourceName}-${outputIdx}-${target.node}-${target.index}`
99
+ const sourceStatus = diffStatus?.[sourceName]
100
+ const targetStatus = diffStatus?.[target.node]
101
+ const isNew = sourceStatus === 'added' || targetStatus === 'added'
102
+ const isRemoved = sourceStatus === 'removed' || targetStatus === 'removed'
103
+
104
+ edges.push({
105
+ id: edgeId,
106
+ source: sourceName,
107
+ target: target.node,
108
+ type: 'smoothstep',
109
+ style: {
110
+ stroke: isNew ? '#22c55e' : isRemoved ? '#ef4444' : '#94a3b8',
111
+ strokeDasharray: isRemoved ? '5,5' : undefined,
112
+ },
113
+ })
114
+ }
115
+ }
116
+ }
117
+
118
+ return { nodes, edges }
119
+ }
120
+
121
+ export function WorkflowGraph({
122
+ workflowJson,
123
+ className,
124
+ diffStatus,
125
+ }: WorkflowGraphProps) {
126
+ const { nodes, edges } = useMemo(
127
+ () => parseWorkflowToFlow(workflowJson, diffStatus),
128
+ [workflowJson, diffStatus],
129
+ )
130
+
131
+ if (nodes.length === 0) {
132
+ return (
133
+ <div className={`flex items-center justify-center text-sm text-muted-foreground ${className ?? ''}`}>
134
+ No workflow nodes to display
135
+ </div>
136
+ )
137
+ }
138
+
139
+ return (
140
+ <div className={className} style={{ minHeight: 300 }}>
141
+ <ReactFlow
142
+ nodes={nodes}
143
+ edges={edges}
144
+ fitView
145
+ attributionPosition="bottom-left"
146
+ proOptions={{ hideAttribution: true }}
147
+ nodesDraggable={false}
148
+ nodesConnectable={false}
149
+ elementsSelectable={false}
150
+ panOnDrag
151
+ zoomOnScroll
152
+ >
153
+ <Background />
154
+ <Controls showInteractive={false} />
155
+ </ReactFlow>
156
+ </div>
157
+ )
158
+ }
@@ -0,0 +1,186 @@
1
+ 'use client'
2
+
3
+ import { useMemo } from 'react'
4
+ import { useTranslations } from 'next-intl'
5
+ import { GitCompare, Eye } from 'lucide-react'
6
+ import { Badge } from '@octo-cyber/ui/components/ui/badge'
7
+ import {
8
+ Tabs,
9
+ TabsContent,
10
+ TabsList,
11
+ TabsTrigger,
12
+ } from '@octo-cyber/ui/components/ui/tabs'
13
+ import { WorkflowGraph } from './WorkflowGraph'
14
+ import { WorkflowDiff } from './WorkflowDiff'
15
+
16
+ interface WorkflowPreviewPanelProps {
17
+ /** The latest AI-generated workflow JSON */
18
+ generatedJson: string | null
19
+ /** The original/base workflow JSON (from session's initial state) */
20
+ originalJson: string | null
21
+ className?: string
22
+ }
23
+
24
+ /**
25
+ * Compute diff status map for WorkflowGraph overlay:
26
+ * compares original and modified node sets to determine added/removed/modified.
27
+ */
28
+ function computeDiffStatus(
29
+ originalJson: string,
30
+ modifiedJson: string,
31
+ ): Record<string, 'added' | 'removed' | 'modified'> {
32
+ const status: Record<string, 'added' | 'removed' | 'modified'> = {}
33
+ try {
34
+ const original = JSON.parse(originalJson) as { nodes?: Array<{ name: string }> }
35
+ const modified = JSON.parse(modifiedJson) as { nodes?: Array<{ name: string }> }
36
+ const origNodes = original.nodes ?? []
37
+ const modNodes = modified.nodes ?? []
38
+ const origNames = new Set(origNodes.map((n) => n.name))
39
+ const modNames = new Set(modNodes.map((n) => n.name))
40
+
41
+ for (const name of modNames) {
42
+ if (!origNames.has(name)) {
43
+ status[name] = 'added'
44
+ }
45
+ }
46
+ for (const name of origNames) {
47
+ if (!modNames.has(name)) {
48
+ status[name] = 'removed'
49
+ }
50
+ }
51
+ // Check modified nodes
52
+ for (const modNode of modNodes) {
53
+ const origNode = origNodes.find((n) => n.name === modNode.name)
54
+ if (origNode && JSON.stringify(origNode) !== JSON.stringify(modNode)) {
55
+ status[modNode.name] = 'modified'
56
+ }
57
+ }
58
+ } catch {
59
+ // invalid JSON
60
+ }
61
+ return status
62
+ }
63
+
64
+ export function WorkflowPreviewPanel({
65
+ generatedJson,
66
+ originalJson,
67
+ className,
68
+ }: WorkflowPreviewPanelProps) {
69
+ const t = useTranslations('workflows.aiDesign')
70
+
71
+ const hasOriginal = Boolean(originalJson)
72
+ const hasGenerated = Boolean(generatedJson)
73
+
74
+ const diffStatus = useMemo(() => {
75
+ if (!originalJson || !generatedJson) return undefined
76
+ return computeDiffStatus(originalJson, generatedJson)
77
+ }, [originalJson, generatedJson])
78
+
79
+ if (!hasGenerated) {
80
+ return (
81
+ <div className={`flex items-center justify-center border-t border-border bg-muted/20 text-sm text-muted-foreground ${className ?? ''}`}>
82
+ {t('noWorkflowPreview')}
83
+ </div>
84
+ )
85
+ }
86
+
87
+ // If no original (creating from scratch), just show the generated workflow
88
+ if (!hasOriginal) {
89
+ return (
90
+ <div className={`flex flex-col border-t border-border ${className ?? ''}`}>
91
+ <div className="flex items-center gap-2 border-b border-border px-3 py-1.5">
92
+ <Eye className="h-3.5 w-3.5 text-muted-foreground" />
93
+ <span className="text-xs font-medium">{t('workflowPreview')}</span>
94
+ </div>
95
+ <div className="flex-1">
96
+ <WorkflowGraph
97
+ workflowJson={generatedJson!}
98
+ className="h-full w-full"
99
+ />
100
+ </div>
101
+ </div>
102
+ )
103
+ }
104
+
105
+ // Has both original and generated: show comparison tabs
106
+ return (
107
+ <div className={`flex flex-col border-t border-border ${className ?? ''}`}>
108
+ <Tabs defaultValue="comparison" className="flex flex-1 flex-col">
109
+ <div className="flex items-center border-b border-border px-3">
110
+ <TabsList className="h-8 bg-transparent">
111
+ <TabsTrigger value="comparison" className="h-7 text-xs">
112
+ <GitCompare className="mr-1 h-3.5 w-3.5" />
113
+ {t('comparison')}
114
+ </TabsTrigger>
115
+ <TabsTrigger value="original" className="h-7 text-xs">
116
+ {t('original')}
117
+ </TabsTrigger>
118
+ <TabsTrigger value="modified" className="h-7 text-xs">
119
+ {t('modified')}
120
+ </TabsTrigger>
121
+ </TabsList>
122
+
123
+ {/* Diff summary badges */}
124
+ {diffStatus && (
125
+ <div className="ml-auto flex gap-1">
126
+ {Object.values(diffStatus).filter((s) => s === 'added').length > 0 && (
127
+ <Badge variant="outline" className="h-5 border-green-500/30 bg-green-500/5 text-[10px] text-green-600 dark:text-green-400">
128
+ +{Object.values(diffStatus).filter((s) => s === 'added').length}
129
+ </Badge>
130
+ )}
131
+ {Object.values(diffStatus).filter((s) => s === 'removed').length > 0 && (
132
+ <Badge variant="outline" className="h-5 border-red-500/30 bg-red-500/5 text-[10px] text-red-600 dark:text-red-400">
133
+ -{Object.values(diffStatus).filter((s) => s === 'removed').length}
134
+ </Badge>
135
+ )}
136
+ {Object.values(diffStatus).filter((s) => s === 'modified').length > 0 && (
137
+ <Badge variant="outline" className="h-5 border-yellow-500/30 bg-yellow-500/5 text-[10px] text-yellow-600 dark:text-yellow-400">
138
+ ~{Object.values(diffStatus).filter((s) => s === 'modified').length}
139
+ </Badge>
140
+ )}
141
+ </div>
142
+ )}
143
+ </div>
144
+
145
+ <TabsContent value="comparison" className="flex-1 m-0">
146
+ <div className="grid h-full grid-cols-2 divide-x divide-border">
147
+ <div className="relative">
148
+ <span className="absolute left-2 top-1 z-10 text-[10px] font-medium text-muted-foreground">
149
+ {t('original')}
150
+ </span>
151
+ <WorkflowGraph
152
+ workflowJson={originalJson!}
153
+ className="h-full w-full"
154
+ />
155
+ </div>
156
+ <div className="relative">
157
+ <span className="absolute left-2 top-1 z-10 text-[10px] font-medium text-muted-foreground">
158
+ {t('modified')}
159
+ </span>
160
+ <WorkflowGraph
161
+ workflowJson={generatedJson!}
162
+ diffStatus={diffStatus}
163
+ className="h-full w-full"
164
+ />
165
+ </div>
166
+ </div>
167
+ </TabsContent>
168
+
169
+ <TabsContent value="original" className="flex-1 m-0">
170
+ <WorkflowGraph
171
+ workflowJson={originalJson!}
172
+ className="h-full w-full"
173
+ />
174
+ </TabsContent>
175
+
176
+ <TabsContent value="modified" className="flex-1 m-0">
177
+ <WorkflowGraph
178
+ workflowJson={generatedJson!}
179
+ diffStatus={diffStatus}
180
+ className="h-full w-full"
181
+ />
182
+ </TabsContent>
183
+ </Tabs>
184
+ </div>
185
+ )
186
+ }
@@ -0,0 +1,46 @@
1
+ 'use client'
2
+
3
+ import { useCallback, useState } from 'react'
4
+
5
+ export type N8nErrorType = 'auth' | 'unavailable' | 'network' | 'unknown'
6
+
7
+ interface N8nSessionError {
8
+ type: N8nErrorType
9
+ messageKey: string
10
+ messageParams?: Record<string, string>
11
+ }
12
+
13
+ function classifyError(status: number): N8nSessionError {
14
+ if (status === 401 || status === 403) {
15
+ return { type: 'auth', messageKey: 'n8nSession.authRequired' }
16
+ }
17
+ if (status === 502 || status === 503 || status === 504) {
18
+ return { type: 'unavailable', messageKey: 'n8nSession.unavailable' }
19
+ }
20
+ return { type: 'unknown', messageKey: 'n8nSession.sessionFailed', messageParams: { status: String(status) } }
21
+ }
22
+
23
+ export function useN8nSession() {
24
+ const [isReady, setIsReady] = useState(false)
25
+ const [error, setError] = useState<N8nSessionError | null>(null)
26
+
27
+ const ensureSession = useCallback(async () => {
28
+ try {
29
+ setError(null)
30
+ const res = await fetch('/api/n8n/session', {
31
+ method: 'POST',
32
+ credentials: 'include',
33
+ })
34
+
35
+ if (res.ok) {
36
+ setIsReady(true)
37
+ } else {
38
+ setError(classifyError(res.status))
39
+ }
40
+ } catch {
41
+ setError({ type: 'network', messageKey: 'n8nSession.networkError' })
42
+ }
43
+ }, [])
44
+
45
+ return { ensureSession, isReady, error }
46
+ }
package/web/index.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @octo-cyber/workflow — Frontend entry point.
3
+ *
4
+ * Re-exports page components and the frontend manifest.
5
+ */
6
+
7
+ // Manifest
8
+ export { workflowFrontendManifest } from './manifest';
9
+
10
+ // i18n Messages
11
+ export { default as workflowMessagesZhCN } from './messages/zh-CN.json';
12
+ export { default as workflowMessagesEnUS } from './messages/en-US.json';
13
+
14
+ // Pages
15
+ export { default as WorkflowsPage } from './pages/WorkflowsPage';
16
+ export { default as CredentialsPage } from './pages/CredentialsPage';
17
+ export { default as ExecutionsPage } from './pages/ExecutionsPage';
18
+ export { default as AiDesignPage } from './pages/AiDesignPage';
19
+
20
+ // Components
21
+ export { N8nIframe } from './components/N8nIframe';
22
+ export { WorkflowDiff } from './components/WorkflowDiff';
23
+ export { SessionPanel } from './components/SessionPanel';
24
+ export { ChatPanel } from './components/ChatPanel';
25
+ export { ToolPanel } from './components/ToolPanel';
26
+
27
+ // Hooks
28
+ export { useN8nSession } from './hooks/use-n8n-session';
29
+ export type { N8nErrorType } from './hooks/use-n8n-session';
@@ -0,0 +1,16 @@
1
+ import type { IFrontendManifest } from '@octo-cyber/core';
2
+
3
+ export const workflowFrontendManifest: IFrontendManifest = {
4
+ moduleId: 'workflow',
5
+ icon: 'Workflow',
6
+ color: 'indigo',
7
+ position: 'main',
8
+ titleKey: 'workflows.title',
9
+ descriptionKey: 'workflows.description',
10
+ pages: [
11
+ { path: '/workflows', titleKey: 'workflows.pages.list', icon: 'Workflow' },
12
+ { path: '/workflows/credentials', titleKey: 'workflows.pages.credentials', icon: 'Key' },
13
+ { path: '/workflows/executions', titleKey: 'workflows.pages.executions', icon: 'Play' },
14
+ { path: '/workflows/ai-design', titleKey: 'workflows.pages.aiDesign', icon: 'Sparkles' },
15
+ ],
16
+ };
@@ -0,0 +1,94 @@
1
+ {
2
+ "workflows": {
3
+ "loading": "Loading workflow editor...",
4
+ "errors": {
5
+ "auth": {
6
+ "title": "Login Required"
7
+ },
8
+ "unavailable": {
9
+ "title": "n8n Service Unavailable",
10
+ "troubleshootTitle": "Troubleshooting steps:",
11
+ "step1": "Confirm the backend service is running (port 9988)",
12
+ "step2": "Confirm the n8n subprocess is running (port 5678)",
13
+ "step3": "Check the backend logs for n8n startup errors"
14
+ },
15
+ "network": {
16
+ "title": "Network Connection Failed"
17
+ },
18
+ "unknown": {
19
+ "title": "Connection Failed"
20
+ }
21
+ },
22
+ "aiDesign": {
23
+ "title": "AI Workflow Design",
24
+ "description": "Describe your automation needs and let AI generate workflow configurations",
25
+ "placeholder": "Describe the workflow you want to create, e.g. \"Send a Slack notification when a new GitHub issue is created\"",
26
+ "inputPlaceholder": "Describe your workflow requirements...",
27
+ "thinking": "AI is designing your workflow...",
28
+ "jsonPreview": "Workflow JSON",
29
+ "copied": "Copied to clipboard",
30
+ "requestFailed": "Failed to generate workflow, please try again",
31
+ "unknownError": "An unknown error occurred",
32
+ "generatedIn": "Generated in",
33
+ "sessions": "Sessions",
34
+ "noSessions": "No sessions yet. Click + to create one.",
35
+ "newSession": "New Design Session",
36
+ "sessionName": "Session Name",
37
+ "sessionNamePlaceholder": "e.g. Email notification workflow",
38
+ "baseWorkflow": "Base Workflow (optional)",
39
+ "selectWorkflow": "Select an existing n8n workflow",
40
+ "newFromScratch": "Create from scratch",
41
+ "create": "Create",
42
+ "cancel": "Cancel",
43
+ "sessionDeleted": "Session deleted",
44
+ "loadSessionsFailed": "Failed to load sessions",
45
+ "deleteSessionFailed": "Failed to delete session",
46
+ "createSessionFailed": "Failed to create session",
47
+ "tools": "Tools",
48
+ "versions": "Versions",
49
+ "apiDocs": "API Docs",
50
+ "aiTool": "AI Tool",
51
+ "model": "Model",
52
+ "defaultModel": "Default",
53
+ "unavailable": "N/A",
54
+ "selectSessionFirst": "Select a session to view version history",
55
+ "rollback": "Rollback",
56
+ "rollbackSuccess": "Rolled back successfully",
57
+ "rollbackFailed": "Rollback failed",
58
+ "apiDocsDesc": "Select API docs to include as context for the AI",
59
+ "estimatedTokens": "Estimated tokens",
60
+ "apply": "Apply",
61
+ "applyConfirmTitle": "Apply Workflow to n8n?",
62
+ "applyConfirmDesc": "This will deactivate the workflow, update it with the new JSON, and you can manually reactivate it afterwards.",
63
+ "applyConfirmNoWorkflow": "This session has no linked n8n workflow. Please create a session from an existing workflow to use the apply feature.",
64
+ "applyConfirm": "Apply",
65
+ "applySuccess": "Workflow applied to n8n successfully",
66
+ "applyFailed": "Failed to apply workflow to n8n",
67
+ "noLinkedWorkflow": "No linked n8n workflow. JSON has been saved.",
68
+ "noChanges": "No changes detected",
69
+ "nodesAdded": "Nodes added",
70
+ "nodesRemoved": "Nodes removed",
71
+ "nodesModified": "Nodes modified",
72
+ "connectionsChanged": "Connections changed",
73
+ "n8nWorkflows": "n8n Workflows",
74
+ "noN8nWorkflows": "No n8n workflows found",
75
+ "loadN8nWorkflowsFailed": "Failed to load n8n workflows",
76
+ "systemPrompt": "System Prompt",
77
+ "systemPromptDesc": "The system prompt used by the AI workflow designer. Updated on server startup.",
78
+ "loadPromptFailed": "Failed to load system prompt",
79
+ "close": "Close",
80
+ "workflowPreview": "Workflow Preview",
81
+ "comparison": "Comparison",
82
+ "original": "Original",
83
+ "modified": "Modified",
84
+ "noWorkflowPreview": "Generate a workflow to see the preview",
85
+ "viewPrompt": "View Prompt"
86
+ },
87
+ "n8nSession": {
88
+ "authRequired": "Please log in before using the workflow editor",
89
+ "unavailable": "n8n service is not running. Please check if the backend service is running properly",
90
+ "sessionFailed": "n8n session creation failed ({status})",
91
+ "networkError": "Unable to connect to the backend service. Please check your network connection"
92
+ }
93
+ }
94
+ }