machinaos 0.0.76 → 0.0.78

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 (393) hide show
  1. package/README.md +143 -107
  2. package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
  3. package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
  4. package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
  5. package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
  6. package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
  7. package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
  8. package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
  9. package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
  10. package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
  11. package/client/dist/assets/index-DQ0nwhec.js +257 -0
  12. package/client/dist/assets/index-DxmbVskS.css +1 -0
  13. package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
  14. package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
  15. package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
  16. package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
  17. package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
  18. package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
  19. package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
  20. package/client/dist/index.html +37 -3
  21. package/client/index.html +28 -1
  22. package/client/package.json +44 -40
  23. package/client/src/App.tsx +2 -0
  24. package/client/src/Dashboard.tsx +157 -45
  25. package/client/src/ParameterPanel.tsx +3 -5
  26. package/client/src/adapters/nodeSpecToDescription.ts +1 -0
  27. package/client/src/assets/icons/NodeIcon.tsx +32 -0
  28. package/client/src/assets/icons/index.ts +4 -0
  29. package/client/src/assets/icons/stripe.svg +1 -0
  30. package/client/src/assets/icons/themedGlyphs.ts +404 -0
  31. package/client/src/components/AIAgentNode.tsx +77 -53
  32. package/client/src/components/GenericNode.tsx +34 -52
  33. package/client/src/components/OutputPanel.tsx +64 -147
  34. package/client/src/components/ParameterRenderer.tsx +5 -3
  35. package/client/src/components/SkillEditorModal.tsx +9 -18
  36. package/client/src/components/SquareNode.tsx +97 -115
  37. package/client/src/components/StartNode.tsx +32 -42
  38. package/client/src/components/SvgFilterDefs.tsx +54 -0
  39. package/client/src/components/TeamMonitorNode.tsx +12 -14
  40. package/client/src/components/ToolkitNode.tsx +35 -60
  41. package/client/src/components/TriggerNode.tsx +43 -77
  42. package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
  43. package/client/src/components/credentials/CredentialsModal.tsx +98 -30
  44. package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
  45. package/client/src/components/credentials/catalogueAdapter.ts +17 -1
  46. package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
  47. package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
  48. package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
  49. package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
  50. package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
  51. package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
  52. package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
  53. package/client/src/components/credentials/types.ts +12 -2
  54. package/client/src/components/credentials/useCredentialPanel.ts +43 -19
  55. package/client/src/components/icons/AIProviderIcons.tsx +16 -0
  56. package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
  57. package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
  58. package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
  59. package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
  60. package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
  61. package/client/src/components/parameterPanel/InputSection.tsx +9 -7
  62. package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
  63. package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
  64. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
  65. package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
  66. package/client/src/components/ui/AIResultModal.tsx +1 -1
  67. package/client/src/components/ui/CollapsibleSection.tsx +9 -5
  68. package/client/src/components/ui/CommandPalette.tsx +147 -0
  69. package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
  70. package/client/src/components/ui/ComponentItem.tsx +13 -7
  71. package/client/src/components/ui/ComponentPalette.tsx +24 -13
  72. package/client/src/components/ui/ConsolePanel.tsx +19 -11
  73. package/client/src/components/ui/DropCap.tsx +28 -0
  74. package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
  75. package/client/src/components/ui/InputNodesPanel.tsx +1 -1
  76. package/client/src/components/ui/Modal.tsx +38 -6
  77. package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
  78. package/client/src/components/ui/SettingsPanel.tsx +42 -13
  79. package/client/src/components/ui/StatusBar.tsx +108 -0
  80. package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
  81. package/client/src/components/ui/TopToolbar.tsx +42 -25
  82. package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
  83. package/client/src/components/ui/action-button.tsx +40 -15
  84. package/client/src/components/ui/button.tsx +24 -1
  85. package/client/src/components/ui/dropdown-menu.tsx +24 -2
  86. package/client/src/components/ui/input.tsx +19 -2
  87. package/client/src/components/ui/select.tsx +15 -0
  88. package/client/src/components/ui/textarea.tsx +15 -2
  89. package/client/src/contexts/AuthContext.tsx +148 -109
  90. package/client/src/contexts/ThemeContext.tsx +93 -17
  91. package/client/src/contexts/WebSocketContext.tsx +373 -206
  92. package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
  93. package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
  94. package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
  95. package/client/src/hooks/useAppTheme.ts +209 -7
  96. package/client/src/hooks/useAutoSkillEdges.ts +7 -2
  97. package/client/src/hooks/useCatalogueQuery.ts +67 -1
  98. package/client/src/hooks/useDragVariable.ts +1 -1
  99. package/client/src/hooks/useNodeAllowlist.ts +115 -8
  100. package/client/src/hooks/useOnboarding.ts +20 -8
  101. package/client/src/hooks/useParameterPanel.ts +2 -1
  102. package/client/src/hooks/useReactFlowNodes.ts +2 -1
  103. package/client/src/hooks/useSound.ts +185 -0
  104. package/client/src/hooks/useWorkflowManagement.ts +6 -8
  105. package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
  106. package/client/src/index.css +65 -3
  107. package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
  108. package/client/src/lib/aiModelProviders.ts +8 -0
  109. package/client/src/lib/connectionConfig.ts +107 -0
  110. package/client/src/lib/queryPersist.ts +13 -5
  111. package/client/src/lib/sound.ts +393 -0
  112. package/client/src/main.tsx +20 -0
  113. package/client/src/store/useAppStore.ts +26 -0
  114. package/client/src/styles/canvasAnimations.ts +37 -36
  115. package/client/src/styles/theme.ts +36 -20
  116. package/client/src/test/setup.ts +1 -0
  117. package/client/src/themes/atomic.css +253 -0
  118. package/client/src/themes/base.css +373 -0
  119. package/client/src/themes/cyber.css +890 -0
  120. package/client/src/themes/dark.css +70 -0
  121. package/client/src/themes/edo.css +246 -0
  122. package/client/src/themes/greek.css +293 -0
  123. package/client/src/themes/light.css +78 -0
  124. package/client/src/themes/plague.css +253 -0
  125. package/client/src/themes/renaissance.css +727 -0
  126. package/client/src/themes/rot.css +249 -0
  127. package/client/src/themes/steampunk.css +272 -0
  128. package/client/src/themes/surveillance.css +289 -0
  129. package/client/src/themes/wasteland.css +250 -0
  130. package/client/src/types/INodeProperties.ts +5 -0
  131. package/client/src/types/NodeTypes.ts +11 -1
  132. package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
  133. package/client/src/types/cloudEvents.ts +78 -0
  134. package/client/src/vite-env.d.ts +7 -0
  135. package/client/tsconfig.json +1 -1
  136. package/client/vite.config.js +62 -2
  137. package/install.ps1 +1 -1
  138. package/install.sh +1 -1
  139. package/machina/commands/build.py +51 -7
  140. package/machina/pyproject.toml +4 -0
  141. package/machina/supervisor.py +12 -2
  142. package/machina/tree.py +71 -21
  143. package/package.json +4 -4
  144. package/scripts/install.js +16 -1
  145. package/server/config/ai_cli_providers.json +54 -0
  146. package/server/config/credential_providers.json +109 -2
  147. package/server/config/llm_defaults.json +24 -0
  148. package/server/config/model_registry.json +338 -499
  149. package/server/config/node_allowlist.json +16 -1
  150. package/server/config/pricing.json +8 -0
  151. package/server/constants.py +38 -15
  152. package/server/core/container.py +2 -2
  153. package/server/core/credentials_database.py +35 -2
  154. package/server/core/logging.py +4 -3
  155. package/server/main.py +99 -13
  156. package/server/models/node_metadata.py +1 -0
  157. package/server/nodejs/package.json +8 -6
  158. package/server/nodejs/src/index.ts +22 -5
  159. package/server/nodes/README.md +31 -4
  160. package/server/nodes/agent/_inline.py +2 -0
  161. package/server/nodes/agent/_specialized.py +6 -3
  162. package/server/nodes/agent/ai_agent.py +13 -3
  163. package/server/nodes/agent/chat_agent.py +6 -3
  164. package/server/nodes/agent/claude_code_agent.py +287 -75
  165. package/server/nodes/agent/codex_agent.py +239 -0
  166. package/server/nodes/agent/deep_agent.py +3 -3
  167. package/server/nodes/agent/rlm_agent.py +3 -3
  168. package/server/nodes/android/__init__.py +31 -1
  169. package/server/nodes/android/_base.py +9 -5
  170. package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
  171. package/server/nodes/android/_handlers.py +154 -0
  172. package/server/nodes/android/_option_loaders.py +44 -0
  173. package/server/nodes/android/_refresh.py +127 -0
  174. package/server/{services/android → nodes/android/_relay}/client.py +4 -4
  175. package/server/{routers/android.py → nodes/android/_router.py} +27 -8
  176. package/server/nodes/browser/browser.py +2 -2
  177. package/server/nodes/code/_base.py +6 -2
  178. package/server/nodes/code/_claude_code.py +134 -0
  179. package/server/nodes/document/embedding_generator.py +3 -3
  180. package/server/nodes/document/http_scraper.py +3 -3
  181. package/server/nodes/document/vector_store.py +5 -5
  182. package/server/nodes/email/__init__.py +11 -1
  183. package/server/nodes/email/_filters.py +21 -0
  184. package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
  185. package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
  186. package/server/nodes/email/email_read.py +1 -1
  187. package/server/nodes/email/email_receive.py +54 -5
  188. package/server/nodes/email/email_send.py +1 -1
  189. package/server/nodes/filesystem/shell.py +24 -1
  190. package/server/nodes/google/__init__.py +55 -1
  191. package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
  192. package/server/nodes/google/_base.py +2 -2
  193. package/server/nodes/google/_credentials.py +5 -5
  194. package/server/nodes/google/_filters.py +25 -0
  195. package/server/nodes/google/_handlers.py +57 -0
  196. package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
  197. package/server/nodes/google/_option_loaders.py +107 -0
  198. package/server/nodes/google/_refresh.py +66 -0
  199. package/server/nodes/google/_router.py +131 -0
  200. package/server/nodes/google/gmail_receive.py +41 -4
  201. package/server/nodes/groups.py +1 -0
  202. package/server/nodes/location/_credentials.py +45 -1
  203. package/server/{services/maps.py → nodes/location/_service.py} +18 -3
  204. package/server/nodes/location/gmaps_create.py +4 -4
  205. package/server/nodes/location/gmaps_locations.py +4 -4
  206. package/server/nodes/location/gmaps_nearby_places.py +4 -4
  207. package/server/nodes/model/_base.py +8 -3
  208. package/server/nodes/model/_credentials.py +96 -8
  209. package/server/nodes/model/_local_validator.py +345 -0
  210. package/server/nodes/model/lmstudio_chat_model.py +23 -0
  211. package/server/nodes/model/ollama_chat_model.py +25 -0
  212. package/server/nodes/proxy/_usage.py +2 -2
  213. package/server/nodes/proxy/proxy_config.py +14 -14
  214. package/server/nodes/proxy/proxy_request.py +4 -4
  215. package/server/nodes/scraper/_credentials.py +29 -1
  216. package/server/nodes/scraper/apify_actor.py +9 -9
  217. package/server/nodes/scraper/crawlee_scraper.py +5 -5
  218. package/server/nodes/search/brave_search.py +4 -0
  219. package/server/nodes/search/perplexity_search.py +9 -0
  220. package/server/nodes/search/serper_search.py +3 -0
  221. package/server/nodes/skill/simple_memory.py +12 -0
  222. package/server/nodes/social/_base.py +2 -2
  223. package/server/nodes/stripe/__init__.py +46 -0
  224. package/server/nodes/stripe/_credentials.py +33 -0
  225. package/server/nodes/stripe/_handlers.py +270 -0
  226. package/server/nodes/stripe/_install.py +127 -0
  227. package/server/nodes/stripe/_source.py +174 -0
  228. package/server/nodes/stripe/stripe_action.py +81 -0
  229. package/server/nodes/stripe/stripe_receive.py +92 -0
  230. package/server/nodes/telegram/_credentials.py +52 -1
  231. package/server/nodes/telegram/_handlers.py +19 -18
  232. package/server/nodes/telegram/_service.py +134 -32
  233. package/server/nodes/telegram/telegram_send.py +5 -6
  234. package/server/nodes/text/file_handler.py +2 -2
  235. package/server/nodes/text/text_generator.py +2 -2
  236. package/server/nodes/tool/agent_builder.py +630 -0
  237. package/server/nodes/tool/task_manager.py +144 -2
  238. package/server/nodes/twitter/__init__.py +38 -1
  239. package/server/nodes/twitter/_base.py +7 -7
  240. package/server/nodes/twitter/_credentials.py +1 -1
  241. package/server/nodes/twitter/_filters.py +37 -0
  242. package/server/nodes/twitter/_handlers.py +77 -0
  243. package/server/nodes/twitter/_oauth.py +124 -0
  244. package/server/nodes/twitter/_refresh.py +78 -0
  245. package/server/nodes/twitter/_router.py +29 -0
  246. package/server/nodes/twitter/twitter_receive.py +4 -0
  247. package/server/nodes/visuals.json +64 -19
  248. package/server/nodes/whatsapp/__init__.py +45 -5
  249. package/server/nodes/whatsapp/_base.py +3 -3
  250. package/server/nodes/whatsapp/_filters.py +137 -0
  251. package/server/nodes/whatsapp/_handlers.py +167 -0
  252. package/server/nodes/whatsapp/_option_loaders.py +68 -0
  253. package/server/nodes/whatsapp/_refresh.py +62 -0
  254. package/server/nodes/whatsapp/_runtime.py +1 -1
  255. package/server/pyproject.toml +29 -7
  256. package/server/routers/schemas.py +2 -2
  257. package/server/routers/webhook.py +26 -9
  258. package/server/routers/websocket.py +149 -810
  259. package/server/services/ai.py +89 -8
  260. package/server/services/auth.py +220 -43
  261. package/server/services/claude_oauth.py +126 -100
  262. package/server/services/cli_agent/__init__.py +78 -0
  263. package/server/services/cli_agent/_handlers.py +237 -0
  264. package/server/services/cli_agent/config.py +112 -0
  265. package/server/services/cli_agent/factory.py +48 -0
  266. package/server/services/cli_agent/lockfile.py +141 -0
  267. package/server/services/cli_agent/mcp_server.py +482 -0
  268. package/server/services/cli_agent/protocol.py +173 -0
  269. package/server/services/cli_agent/providers/__init__.py +9 -0
  270. package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
  271. package/server/services/cli_agent/providers/google_gemini.py +80 -0
  272. package/server/services/cli_agent/providers/openai_codex.py +310 -0
  273. package/server/services/cli_agent/service.py +607 -0
  274. package/server/services/cli_agent/session.py +618 -0
  275. package/server/services/cli_agent/types.py +227 -0
  276. package/server/services/cli_agent/workflow_tools.py +233 -0
  277. package/server/services/credential_registry.py +26 -1
  278. package/server/services/deployment/manager.py +26 -145
  279. package/server/services/deployment/poll_registry.py +59 -0
  280. package/server/services/event_waiter.py +76 -246
  281. package/server/services/events/__init__.py +54 -0
  282. package/server/services/events/cli.py +78 -0
  283. package/server/services/events/daemon.py +163 -0
  284. package/server/services/events/envelope.py +281 -0
  285. package/server/services/events/lifecycle.py +99 -0
  286. package/server/services/events/oauth_lifecycle.py +534 -0
  287. package/server/services/events/polling.py +60 -0
  288. package/server/services/events/push.py +36 -0
  289. package/server/services/events/source.py +63 -0
  290. package/server/services/events/triggers.py +118 -0
  291. package/server/services/events/verifiers/__init__.py +25 -0
  292. package/server/services/events/verifiers/base.py +28 -0
  293. package/server/services/events/verifiers/github.py +25 -0
  294. package/server/services/events/verifiers/hmac_basic.py +32 -0
  295. package/server/services/events/verifiers/standard_webhooks.py +47 -0
  296. package/server/services/events/verifiers/stripe.py +42 -0
  297. package/server/services/events/webhook.py +105 -0
  298. package/server/services/handlers/tools.py +28 -186
  299. package/server/services/llm/config.py +7 -0
  300. package/server/services/llm/factory.py +8 -2
  301. package/server/services/memory/__init__.py +52 -0
  302. package/server/services/memory/jsonl.py +80 -0
  303. package/server/services/memory/markdown.py +65 -0
  304. package/server/services/memory/state.py +112 -0
  305. package/server/services/memory/vector_store.py +40 -0
  306. package/server/services/model_registry.py +76 -0
  307. package/server/services/node_allowlist.py +71 -15
  308. package/server/services/node_executor.py +2 -2
  309. package/server/services/node_output_schemas.py +21 -10
  310. package/server/services/node_spec.py +1 -1
  311. package/server/services/oauth_utils.py +1 -1
  312. package/server/services/plugin/__init__.py +2 -0
  313. package/server/services/plugin/base.py +44 -2
  314. package/server/services/plugin/credential.py +288 -1
  315. package/server/services/plugin/deps.py +105 -0
  316. package/server/services/plugin/edge_walker.py +12 -4
  317. package/server/services/plugin/oauth.py +381 -0
  318. package/server/services/plugin/polling.py +247 -0
  319. package/server/services/plugin/registry.py +145 -0
  320. package/server/services/plugin/singleton.py +65 -0
  321. package/server/services/plugin/ws.py +81 -0
  322. package/server/services/process_service.py +31 -2
  323. package/server/services/status_broadcaster.py +155 -238
  324. package/server/services/temporal/workflow.py +7 -7
  325. package/server/services/workflow.py +21 -3
  326. package/server/services/ws_handler_registry.py +111 -28
  327. package/server/skills/GUIDE.md +16 -1
  328. package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
  329. package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
  330. package/server/tests/credentials/test_auth_service.py +16 -9
  331. package/server/tests/credentials/test_credential_broadcasts.py +219 -0
  332. package/server/tests/credentials/test_google_oauth.py +6 -6
  333. package/server/tests/credentials/test_oauth_utils.py +1 -1
  334. package/server/tests/credentials/test_twitter_oauth.py +2 -2
  335. package/server/tests/credentials/test_websocket_handlers.py +44 -20
  336. package/server/tests/llm/test_factory.py +1 -0
  337. package/server/tests/llm/test_wiring.py +5 -1
  338. package/server/tests/nodes/_compat.py +24 -24
  339. package/server/tests/nodes/test_agent_builder.py +439 -0
  340. package/server/tests/nodes/test_ai_tools.py +18 -14
  341. package/server/tests/nodes/test_code_fs_process.py +17 -8
  342. package/server/tests/nodes/test_email.py +10 -9
  343. package/server/tests/nodes/test_google_workspace.py +2 -2
  344. package/server/tests/nodes/test_specialized_agents.py +100 -53
  345. package/server/tests/nodes/test_stripe_plugin.py +293 -0
  346. package/server/tests/nodes/test_telegram_social.py +4 -4
  347. package/server/tests/nodes/test_twitter.py +1 -1
  348. package/server/tests/nodes/test_web_automation.py +2 -2
  349. package/server/tests/nodes/test_whatsapp.py +9 -9
  350. package/server/tests/services/cli_agent/__init__.py +0 -0
  351. package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
  352. package/server/tests/services/cli_agent/test_providers.py +358 -0
  353. package/server/tests/services/cli_agent/test_service.py +298 -0
  354. package/server/tests/services/memory/__init__.py +0 -0
  355. package/server/tests/services/memory/test_jsonl.py +188 -0
  356. package/server/tests/services/test_events.py +333 -0
  357. package/server/tests/test_node_spec.py +56 -16
  358. package/server/tests/test_plugin_helpers.py +116 -0
  359. package/server/tests/test_plugin_self_containment.py +486 -0
  360. package/server/tests/test_status_broadcasts.py +425 -0
  361. package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
  362. package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
  363. package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
  364. package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
  365. package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
  366. package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
  367. package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
  368. package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
  369. package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
  370. package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
  371. package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
  372. package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
  373. package/client/dist/assets/index-D9tZfgvi.js +0 -363
  374. package/client/dist/assets/index-al7snTkG.css +0 -1
  375. package/client/src/components/credentials/providers.tsx +0 -177
  376. package/server/routers/google.py +0 -277
  377. package/server/routers/maps.py +0 -142
  378. package/server/routers/twitter.py +0 -365
  379. package/server/services/claude_code_service.py +0 -106
  380. package/server/services/memory.py +0 -159
  381. package/server/services/node_option_loaders/__init__.py +0 -77
  382. package/server/services/node_option_loaders/android_loaders.py +0 -55
  383. package/server/services/node_option_loaders/google_loaders.py +0 -97
  384. package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
  385. package/server/services/twitter_oauth.py +0 -411
  386. package/server/services/websocket_client.py +0 -29
  387. /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
  388. /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
  389. /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
  390. /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
  391. /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
  392. /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
  393. /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
@@ -1,7 +1,7 @@
1
1
  import React, { memo, useState, useEffect, useRef, useCallback, useMemo } from 'react';
2
2
  import { nodePropsEqual } from './nodeMemoEquality';
3
3
  import { Handle, Position, NodeProps } from 'reactflow';
4
- import { NodeData } from '../types/NodeTypes';
4
+ import { NodeData, NodeStyle } from '../types/NodeTypes';
5
5
  import { useAppStore } from '../store/useAppStore';
6
6
  import { useAppTheme } from '../hooks/useAppTheme';
7
7
  import EditableNodeLabel from './ui/EditableNodeLabel';
@@ -9,6 +9,7 @@ import { useWebSocket, useWhatsAppStatus, useNodeStatus } from '../contexts/WebS
9
9
  import { getCachedNodeSpec, isNodeInBackendGroup, resolveNodeDescription, useNodeSpec } from '../lib/nodeSpec';
10
10
  import { NodeIcon } from '../assets/icons';
11
11
  import { AI_MODEL_PROVIDER_MAP } from '../lib/aiModelProviders';
12
+ import { useProviderStored } from '../hooks/useCatalogueQuery';
12
13
 
13
14
  // Nodes with 'tool' in their group can connect to AI Agent/Zeenie tool handles
14
15
  const hasToolGroup = (definition: any): boolean => {
@@ -31,7 +32,13 @@ const CREDENTIAL_TO_PROVIDER: Record<string, string> = {
31
32
 
32
33
  const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectable, selected }) => {
33
34
  const theme = useAppTheme();
34
- const { setSelectedNode, setRenamingNodeId, updateNodeData } = useAppStore();
35
+ // Slice selectors so an unrelated store mutation (sidebar toggle,
36
+ // workflow rename, parameter save on another node) does NOT re-render
37
+ // every canvas node. Setters are stable refs — single-field selector
38
+ // is the cheapest read.
39
+ const setSelectedNode = useAppStore((s) => s.setSelectedNode);
40
+ const setRenamingNodeId = useAppStore((s) => s.setRenamingNodeId);
41
+ const updateNodeData = useAppStore((s) => s.updateNodeData);
35
42
  const isDisabled = data?.disabled === true;
36
43
 
37
44
  // Get Android status + API key status from the broad WebSocket
@@ -139,11 +146,12 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
139
146
  return '';
140
147
  }, [definition?.credentials, type]);
141
148
 
142
- // "Is a credential configured for this provider?" is read from the
143
- // WS-context apiKeyStatuses mappopulated synchronously by
144
- // validateApiKeyAsync + the api_key_status broadcast. The canvas node
145
- // never fetches decrypted keys; that's the credentials modal's job.
146
- const hasApiKey = !!(providerId && getApiKeyStatus(providerId)?.hasKey);
149
+ // "Is a credential configured for this provider?" reads from the
150
+ // server-driven catalogue (single source of truth the `stored`
151
+ // flag is computed from `auth_service.has_valid_key()` on every
152
+ // catalogue read). The retired `apiKeyStatuses[id].hasKey` mirror
153
+ // duplicated this answer with no synchronisation contract.
154
+ const hasApiKey = useProviderStored(providerId);
147
155
  const isConfigured = hasApiKey && !!data && Object.keys(data).length > 0;
148
156
 
149
157
  const handleParametersClick = (e: React.MouseEvent) => {
@@ -151,45 +159,39 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
151
159
  setSelectedNode({ id, type, data, position: { x: 0, y: 0 } });
152
160
  };
153
161
 
154
- // Get status indicator color based on execution state
155
- const getStatusIndicatorColor = () => {
156
- // For executing or waiting state, show blue/cyan
157
- if (executionStatus === 'executing' || executionStatus === 'waiting') {
158
- return theme.dracula.cyan;
159
- }
160
- if (executionStatus === 'success') {
161
- return theme.dracula.green;
162
- }
163
- if (executionStatus === 'error') {
164
- return theme.dracula.red;
162
+ // Bucket execution + connection state into one of five status tokens
163
+ // consumed by `.sq-node-pip[data-status="..."]` in base.css. Per-theme
164
+ // CSS overrides these without fighting inline `backgroundColor`.
165
+ // Buckets: 'idle' | 'executing' | 'waiting' | 'success' | 'error'.
166
+ // Matches TriggerNode/StartNode/ToolkitNode contract (Wave 26.B).
167
+ const pipStatus: 'idle' | 'executing' | 'waiting' | 'success' | 'error' = (() => {
168
+ if (executionStatus === 'executing' || executionStatus === 'waiting' || isGlowing) {
169
+ return 'executing';
165
170
  }
171
+ if (executionStatus === 'success') return 'success';
172
+ if (executionStatus === 'error') return 'error';
166
173
 
167
- // Idle state - use Android or configuration status
174
+ // Idle bucket derive from connection / configuration signals.
168
175
  if (isAndroidNode) {
169
- return isAndroidConnected ? theme.dracula.green : theme.dracula.red;
176
+ return isAndroidConnected ? 'success' : 'error';
170
177
  }
171
-
172
- // WhatsApp nodes - use WebSocket connection status
173
178
  if (isWhatsAppNode) {
174
- if (whatsappStatus.connected) return theme.dracula.green;
175
- if (whatsappStatus.pairing) return theme.dracula.orange;
176
- return theme.dracula.red;
179
+ if (whatsappStatus.connected) return 'success';
180
+ if (whatsappStatus.pairing) return 'waiting';
181
+ return 'error';
177
182
  }
178
-
179
- // Google Maps nodes - use WebSocket API key validation status
180
183
  if (isGoogleMapsNode && googleMapsKeyStatus) {
181
- return googleMapsKeyStatus.valid ? theme.dracula.green : theme.dracula.red;
184
+ return googleMapsKeyStatus.valid ? 'success' : 'error';
182
185
  }
183
-
184
- // AI Model nodes - use reactive WebSocket API key status
185
186
  if (isAIModelNode) {
186
- if (aiKeyStatus?.valid && aiKeyStatus?.hasKey) return theme.dracula.green;
187
- if (aiKeyStatus?.hasKey) return theme.dracula.orange;
188
- return theme.dracula.red;
187
+ if (aiKeyStatus?.valid && hasApiKey) return 'success';
188
+ if (hasApiKey) return 'waiting';
189
+ return 'error';
189
190
  }
190
-
191
- return isConfigured ? theme.dracula.green : hasApiKey ? theme.dracula.orange : theme.dracula.red;
192
- };
191
+ if (isConfigured) return 'success';
192
+ if (hasApiKey) return 'waiting';
193
+ return 'idle';
194
+ })();
193
195
 
194
196
  const getStatusTitle = () => {
195
197
  switch (executionStatus) {
@@ -218,12 +220,14 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
218
220
  ? 'Google Maps API key validated'
219
221
  : `API key invalid: ${googleMapsKeyStatus.message || 'Validation failed'}`;
220
222
  }
221
- // AI Model nodes - use reactive WebSocket API key status
223
+ // AI Model nodes - "is stored" from catalogue (hasApiKey),
224
+ // "is validated" from apiKeyStatuses. Independent concerns
225
+ // post-Wave-12; the two sources don't drift across tabs.
222
226
  if (isAIModelNode) {
223
- if (aiKeyStatus?.valid && aiKeyStatus?.hasKey) {
227
+ if (aiKeyStatus?.valid && hasApiKey) {
224
228
  return `${aiProviderId?.charAt(0).toUpperCase()}${aiProviderId?.slice(1)} API key validated`;
225
229
  }
226
- if (aiKeyStatus?.hasKey) {
230
+ if (hasApiKey) {
227
231
  return 'API key found, validation pending';
228
232
  }
229
233
  return 'API key required - configure in Credentials';
@@ -248,8 +252,16 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
248
252
  const nodeColor = definition?.defaults?.color || '#1A73E8';
249
253
 
250
254
  return (
255
+ // `sq-node` + `selected` co-classes are the design-handoff structural
256
+ // hooks for per-theme square-node decorations (rivets on Steampunk
257
+ // sq-node-box::before/::after, hanko seal on Edo, REC LED + surv-blink
258
+ // on Surveillance, tombstone shape on Rot, gold-foil emblem on
259
+ // Renaissance, neon underglow on Cyber, etc.).
251
260
  <div
261
+ className={`sq-node ${selected ? 'selected' : ''}`}
262
+ data-executing={isExecuting ? '' : undefined}
252
263
  style={{
264
+ '--node-color': nodeColor,
253
265
  position: 'relative',
254
266
  display: 'flex',
255
267
  flexDirection: 'column',
@@ -257,23 +269,16 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
257
269
  fontFamily: 'system-ui, -apple-system, sans-serif',
258
270
  fontSize: '11px',
259
271
  cursor: 'pointer',
260
- }}
272
+ } as NodeStyle}
261
273
  >
262
274
  {/* Main Square Node */}
263
275
  <div
276
+ className="sq-node-box"
264
277
  style={{
278
+ '--node-color': nodeColor,
265
279
  position: 'relative',
266
280
  width: theme.nodeSize.square,
267
281
  height: theme.nodeSize.square,
268
- borderRadius: theme.borderRadius.lg,
269
- background: theme.isDarkMode
270
- ? `linear-gradient(135deg, ${nodeColor}25 0%, ${theme.colors.background} 100%)`
271
- : `linear-gradient(145deg, #ffffff 0%, ${nodeColor}08 100%)`,
272
- border: `2px solid ${isExecuting
273
- ? (theme.isDarkMode ? theme.dracula.cyan : '#2563eb')
274
- : selected
275
- ? theme.colors.focus
276
- : theme.isDarkMode ? nodeColor + '80' : `${nodeColor}40`}`,
277
282
  display: 'flex',
278
283
  alignItems: 'center',
279
284
  justifyContent: 'center',
@@ -281,18 +286,8 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
281
286
  fontSize: theme.nodeSize.squareIcon,
282
287
  fontWeight: '600',
283
288
  transition: 'all 0.2s ease',
284
- boxShadow: isExecuting
285
- ? theme.isDarkMode
286
- ? `0 4px 12px ${theme.dracula.cyan}66, 0 0 0 3px ${theme.dracula.cyan}4D`
287
- : `0 0 0 3px rgba(37, 99, 235, 0.5), 0 4px 16px rgba(37, 99, 235, 0.35)`
288
- : selected
289
- ? `0 4px 12px ${theme.colors.focusRing}, 0 0 0 1px ${theme.colors.focusRing}`
290
- : theme.isDarkMode
291
- ? `0 2px 8px ${nodeColor}40`
292
- : `0 2px 8px ${nodeColor}20, 0 4px 12px rgba(0,0,0,0.06)`,
293
- animation: isExecuting ? 'pulse 1.5s ease-in-out infinite' : 'none',
294
289
  opacity: isDisabled ? 0.5 : 1,
295
- }}
290
+ } as NodeStyle}
296
291
  >
297
292
  {/* Disabled Overlay */}
298
293
  {isDisabled && (
@@ -318,7 +313,9 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
318
313
  {/* Service Icon */}
319
314
  <NodeIcon icon={iconRef} className="h-7 w-7 text-3xl" />
320
315
 
321
- {/* Parameters Button */}
316
+ {/* Parameters Button — visual styles (background, border, hover)
317
+ owned by `.sq-node-gear` in base.css + per-theme overrides.
318
+ Inline keeps positioning + sizing only. */}
322
319
  <button
323
320
  onClick={handleParametersClick}
324
321
  style={{
@@ -327,71 +324,55 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
327
324
  right: '-8px',
328
325
  width: theme.nodeSize.paramButton,
329
326
  height: theme.nodeSize.paramButton,
330
- borderRadius: theme.borderRadius.sm,
331
- backgroundColor: theme.isDarkMode ? theme.colors.backgroundAlt : '#ffffff',
332
- border: `1px solid ${theme.isDarkMode ? theme.colors.border : '#d1d5db'}`,
333
- cursor: 'pointer',
334
- display: 'flex',
335
- alignItems: 'center',
336
- justifyContent: 'center',
337
327
  fontSize: theme.fontSize.xs,
338
- color: theme.colors.textSecondary,
339
- fontWeight: '400',
340
- transition: theme.transitions.fast,
341
328
  zIndex: 30,
342
- boxShadow: theme.isDarkMode
343
- ? `0 1px 3px ${theme.colors.shadow}`
344
- : '0 1px 4px rgba(0,0,0,0.1)'
345
329
  }}
346
330
  title="Edit Service Parameters"
331
+ className="sq-node-gear"
347
332
  >
348
333
  ⚙️
349
334
  </button>
350
335
 
351
- {/* Configuration/Execution Status Indicator */}
336
+ {/* Configuration/Execution Status Indicator.
337
+ Background color is owned by `.sq-node-pip[data-status="..."]`
338
+ in base.css (and per-theme overrides). We pass only the
339
+ execution/connection bucket here — no inline color. */}
352
340
  <div
341
+ className="sq-node-pip"
342
+ data-status={pipStatus}
353
343
  style={{
354
344
  position: 'absolute',
355
345
  top: '-4px',
356
346
  left: '-4px',
357
347
  width: theme.nodeSize.statusIndicator,
358
348
  height: theme.nodeSize.statusIndicator,
359
- borderRadius: '50%',
360
- backgroundColor: getStatusIndicatorColor(),
361
- border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
362
- boxShadow: isExecuting
363
- ? theme.isDarkMode
364
- ? `0 0 6px ${theme.dracula.cyan}80`
365
- : '0 0 4px rgba(37, 99, 235, 0.5)'
366
- : theme.isDarkMode
367
- ? `0 1px 2px ${theme.colors.shadow}`
368
- : '0 1px 3px rgba(0,0,0,0.15)',
369
349
  zIndex: 30,
370
- animation: isExecuting ? 'pulse 1s ease-in-out infinite' : 'none',
371
350
  }}
372
351
  title={getStatusTitle()}
373
352
  />
374
353
 
375
- {/* Square Input Handle */}
376
- <Handle
377
- id="input-main"
378
- type="target"
379
- position={Position.Left}
380
- isConnectable={isConnectable}
381
- style={{
382
- position: 'absolute',
383
- left: '-6px',
384
- top: '50%',
385
- transform: 'translateY(-50%)',
386
- width: theme.nodeSize.handle,
387
- height: theme.nodeSize.handle,
388
- backgroundColor: theme.isDarkMode ? theme.colors.background : '#ffffff',
389
- border: `2px solid ${theme.isDarkMode ? theme.colors.textSecondary : '#6b7280'}`,
390
- borderRadius: '50%',
391
- zIndex: 20
392
- }}
393
- title="Service Input"
394
- />
354
+ {/* Square Input Handle (gated by spec.hideInputHandle, mirrors
355
+ the hideOutputHandle pattern below — auto-derived True for
356
+ usable_as_tool=True nodes via BaseNode.__init_subclass__). */}
357
+ {!getCachedNodeSpec(type || '')?.hideInputHandle && (
358
+ <Handle
359
+ id="input-main"
360
+ type="target"
361
+ position={Position.Left}
362
+ isConnectable={isConnectable}
363
+ className="sq-node-handle in"
364
+ style={{
365
+ position: 'absolute',
366
+ left: '-6px',
367
+ top: '50%',
368
+ transform: 'translateY(-50%)',
369
+ width: theme.nodeSize.handle,
370
+ height: theme.nodeSize.handle,
371
+ zIndex: 20
372
+ }}
373
+ title="Service Input"
374
+ />
375
+ )}
395
376
 
396
377
  {/* Square Output Handle (Wave 10.E: spec.hideOutputHandle replaces the
397
378
  local NO_OUTPUT_NODE_TYPES list) */}
@@ -401,18 +382,17 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
401
382
  type="source"
402
383
  position={Position.Right}
403
384
  isConnectable={isConnectable}
385
+ className="sq-node-handle out"
404
386
  style={{
387
+ '--node-color': isConfigured ? nodeColor : theme.colors.textSecondary,
405
388
  position: 'absolute',
406
389
  right: '-6px',
407
390
  top: '50%',
408
391
  transform: 'translateY(-50%)',
409
392
  width: theme.nodeSize.handle,
410
393
  height: theme.nodeSize.handle,
411
- backgroundColor: isConfigured ? nodeColor : theme.colors.textSecondary,
412
- border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
413
- borderRadius: '50%',
414
394
  zIndex: 20
415
- }}
395
+ } as NodeStyle}
416
396
  title="Service Output"
417
397
  />
418
398
  )}
@@ -422,7 +402,10 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
422
402
  `nodeColor` carries the spec's brand color (Android green for
423
403
  Android services, dracula accents elsewhere). The tooltip
424
404
  reads from the spec's top-position output handle when one is
425
- declared there; otherwise falls back to a generic label. */}
405
+ declared there; otherwise falls back to a generic label.
406
+ Visual styles (background, border, radius) owned by
407
+ `.sq-node-handle.out` in base.css + per-theme overrides — we
408
+ pass `--node-color` for the CSS var only. */}
426
409
  {isToolCapable && (() => {
427
410
  const spec = getCachedNodeSpec(type || '');
428
411
  const topOut = spec?.handles?.find(h => h.kind === 'output' && h.position === 'top');
@@ -433,18 +416,17 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
433
416
  type="source"
434
417
  position={Position.Top}
435
418
  isConnectable={isConnectable}
419
+ className="sq-node-handle out"
436
420
  style={{
421
+ '--node-color': nodeColor,
437
422
  position: 'absolute',
438
423
  top: '-6px',
439
424
  left: '50%',
440
425
  transform: 'translateX(-50%)',
441
426
  width: theme.nodeSize.handle,
442
427
  height: theme.nodeSize.handle,
443
- backgroundColor: nodeColor,
444
- border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
445
- borderRadius: '50%',
446
428
  zIndex: 20,
447
- }}
429
+ } as NodeStyle}
448
430
  title={tooltip}
449
431
  />
450
432
  );
@@ -1,18 +1,26 @@
1
1
  import React, { memo, useCallback } from 'react';
2
2
  import { nodePropsEqual } from './nodeMemoEquality';
3
3
  import { Handle, Position, NodeProps } from 'reactflow';
4
- import { NodeData } from '../types/NodeTypes';
4
+ import { NodeData, NodeStyle } from '../types/NodeTypes';
5
5
  import { useAppStore } from '../store/useAppStore';
6
6
  import { useAppTheme } from '../hooks/useAppTheme';
7
7
  import { PlayCircle } from 'lucide-react';
8
+ import { resolveNodeDescription } from '../lib/nodeSpec';
8
9
  import EditableNodeLabel from './ui/EditableNodeLabel';
9
10
 
10
11
  const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectable, selected }) => {
11
- const { setSelectedNode, setRenamingNodeId, updateNodeData } = useAppStore();
12
+ const setSelectedNode = useAppStore((s) => s.setSelectedNode);
13
+ const setRenamingNodeId = useAppStore((s) => s.setRenamingNodeId);
14
+ const updateNodeData = useAppStore((s) => s.updateNodeData);
12
15
  const theme = useAppTheme();
13
16
 
14
17
  const defaultLabel = 'Start';
15
18
 
19
+ // Definition-driven color (Wave 26.A): backend NodeSpec is SSOT for
20
+ // node color. Falls back to dracula cyan if the spec hasn't loaded.
21
+ const definition = resolveNodeDescription(type || '');
22
+ const nodeColor = definition?.defaults?.color || theme.dracula.cyan;
23
+
16
24
  const handleLabelChange = useCallback(
17
25
  (newLabel: string) => updateNodeData(id, { label: newLabel }),
18
26
  [id, updateNodeData]
@@ -28,11 +36,17 @@ const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectabl
28
36
  setSelectedNode({ id, type, data, position: { x: 0, y: 0 } });
29
37
  };
30
38
 
31
- const nodeColor = theme.dracula.cyan; // Cyan color for start node (neutral "begin" color)
32
-
33
39
  return (
40
+ // `sq-node` + `selected` co-classes activate per-theme square-node
41
+ // decorations (Renaissance wax seal, Steampunk rivets, Edo hanko
42
+ // seal, Surveillance REC LED, Cyber neon underglow). Visual styling
43
+ // (background, border, radius, shadow) lives in base.css
44
+ // `.sq-node-box { ... }` defaults + per-theme overrides and reads
45
+ // `var(--node-color)` for the per-definition accent.
34
46
  <div
47
+ className={`sq-node ${selected ? 'selected' : ''}`}
35
48
  style={{
49
+ '--node-color': nodeColor,
36
50
  position: 'relative',
37
51
  display: 'flex',
38
52
  flexDirection: 'column',
@@ -40,82 +54,60 @@ const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectabl
40
54
  fontFamily: 'system-ui, -apple-system, sans-serif',
41
55
  fontSize: '11px',
42
56
  cursor: 'pointer',
43
- }}
57
+ } as NodeStyle}
44
58
  >
45
- {/* Main Square Node */}
59
+ {/* Main Square Node — layout only; visuals live in CSS. */}
46
60
  <div
61
+ className="sq-node-box"
47
62
  style={{
48
63
  position: 'relative',
49
64
  width: '60px',
50
65
  height: '60px',
51
- borderRadius: '8px',
52
- background: theme.isDarkMode
53
- ? `linear-gradient(135deg, ${nodeColor}20 0%, ${theme.colors.background} 100%)`
54
- : `linear-gradient(145deg, #ffffff 0%, ${nodeColor}10 100%)`,
55
- border: `2px solid ${selected
56
- ? theme.colors.focus
57
- : theme.isDarkMode ? `${nodeColor}60` : `${nodeColor}50`}`,
58
66
  display: 'flex',
59
67
  alignItems: 'center',
60
68
  justifyContent: 'center',
61
69
  color: theme.colors.text,
62
70
  fontSize: '28px',
63
71
  fontWeight: '600',
64
- transition: 'all 0.2s ease',
65
- boxShadow: selected
66
- ? `0 4px 12px ${theme.colors.focusRing}, 0 0 0 1px ${theme.colors.focusRing}`
67
- : theme.isDarkMode
68
- ? `0 2px 8px ${nodeColor}30`
69
- : `0 2px 8px ${nodeColor}25, 0 4px 12px rgba(0,0,0,0.06)`,
70
72
  }}
71
73
  >
72
- {/* Play Icon */}
74
+ {/* Play Icon (color reads from definition-driven nodeColor) */}
73
75
  <PlayCircle className="h-7 w-7" style={{ color: nodeColor }} />
74
76
 
75
- {/* Parameters Button */}
77
+ {/* Parameters Button — CSS owns bg/border via .sq-node-gear */}
76
78
  <button
77
79
  onClick={handleParametersClick}
80
+ className="sq-node-gear"
78
81
  style={{
79
82
  position: 'absolute',
80
83
  top: '-8px',
81
84
  right: '-8px',
82
85
  width: '16px',
83
86
  height: '16px',
84
- borderRadius: '3px',
85
- backgroundColor: theme.isDarkMode ? theme.colors.backgroundAlt : '#ffffff',
86
- border: `1px solid ${theme.isDarkMode ? theme.colors.border : `${nodeColor}40`}`,
87
87
  cursor: 'pointer',
88
88
  display: 'flex',
89
89
  alignItems: 'center',
90
90
  justifyContent: 'center',
91
91
  fontSize: '8px',
92
- color: theme.colors.textSecondary,
93
92
  fontWeight: '400',
94
- transition: 'all 0.2s ease',
95
93
  zIndex: 30,
96
- boxShadow: theme.isDarkMode
97
- ? `0 1px 3px ${theme.colors.shadow}`
98
- : `0 1px 4px ${nodeColor}20`
99
94
  }}
100
95
  title="Edit Parameters"
101
96
  >
102
- {'\u2699\uFE0F'}
97
+ {'⚙️'}
103
98
  </button>
104
99
 
105
- {/* Status Indicator - always green for start */}
100
+ {/* Status Indicator Start is always "ready" (success bucket).
101
+ CSS owns bg color via per-status rule on .sq-node-pip. */}
106
102
  <div
103
+ className="sq-node-pip"
104
+ data-status="success"
107
105
  style={{
108
106
  position: 'absolute',
109
107
  top: '-4px',
110
108
  left: '-4px',
111
109
  width: '10px',
112
110
  height: '10px',
113
- borderRadius: '50%',
114
- backgroundColor: nodeColor,
115
- border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
116
- boxShadow: theme.isDarkMode
117
- ? `0 1px 2px ${theme.colors.shadow}`
118
- : '0 1px 3px rgba(0,0,0,0.15)',
119
111
  zIndex: 30,
120
112
  }}
121
113
  title="Workflow start point"
@@ -134,12 +126,13 @@ const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectabl
134
126
  }}
135
127
  />
136
128
 
137
- {/* Output Handle */}
129
+ {/* Output Handle — CSS owns bg/border via .sq-node-handle.out */}
138
130
  <Handle
139
131
  id="output-main"
140
132
  type="source"
141
133
  position={Position.Right}
142
134
  isConnectable={isConnectable}
135
+ className="sq-node-handle out"
143
136
  style={{
144
137
  position: 'absolute',
145
138
  right: '-6px',
@@ -147,10 +140,7 @@ const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectabl
147
140
  transform: 'translateY(-50%)',
148
141
  width: '8px',
149
142
  height: '8px',
150
- backgroundColor: nodeColor,
151
- border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
152
- borderRadius: '50%',
153
- zIndex: 20
143
+ zIndex: 20,
154
144
  }}
155
145
  title="Workflow Output"
156
146
  />
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Mounts a hidden inline <svg><defs> block once at the app root so per-
3
+ * theme CSS rules like `filter: url(#ink-blot)` resolve. The SVG itself
4
+ * has zero pixel footprint (width=0 height=0); the filter definitions
5
+ * inside <defs> are document-wide referenceable by ID.
6
+ *
7
+ * Filter IDs declared:
8
+ * #ink-blot - Renaissance edge warble (turbulence + displacement map)
9
+ * #noise - Wasteland paper-grain turbulence
10
+ * #crt - Cyber chromatic aberration / scanline
11
+ *
12
+ * Audit: only `#ink-blot` is currently referenced in the upstream design
13
+ * handoff CSS (renaissance.css line 271). `#noise` and `#crt` are shipped
14
+ * preemptively so per-theme CSS can adopt them without a second wave.
15
+ */
16
+ import React from 'react';
17
+
18
+ export const SvgFilterDefs: React.FC = () => (
19
+ <svg
20
+ width={0}
21
+ height={0}
22
+ aria-hidden="true"
23
+ style={{ position: 'absolute', overflow: 'hidden' }}
24
+ >
25
+ <defs>
26
+ <filter id="ink-blot">
27
+ <feTurbulence
28
+ type="fractalNoise"
29
+ baseFrequency="0.02 0.04"
30
+ numOctaves="2"
31
+ seed="3"
32
+ />
33
+ <feDisplacementMap in="SourceGraphic" scale="2" />
34
+ </filter>
35
+ <filter id="noise">
36
+ <feTurbulence
37
+ type="fractalNoise"
38
+ baseFrequency="0.9"
39
+ numOctaves="2"
40
+ stitchTiles="stitch"
41
+ />
42
+ <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" />
43
+ </filter>
44
+ <filter id="crt">
45
+ <feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" />
46
+ <feOffset in="SourceGraphic" dx="-1" dy="0" result="r" />
47
+ <feOffset in="SourceGraphic" dx="1" dy="0" result="b" />
48
+ <feBlend in="r" in2="b" mode="screen" />
49
+ </filter>
50
+ </defs>
51
+ </svg>
52
+ );
53
+
54
+ export default SvgFilterDefs;