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
@@ -5,13 +5,14 @@
5
5
 
6
6
  import React from 'react';
7
7
  import { Loader2 } from 'lucide-react';
8
- import { Button } from '@/components/ui/button';
8
+ import { ActionButton } from '@/components/ui/action-button';
9
+ import type { ActionButtonIntent } from '@/components/ui/action-button';
9
10
 
10
11
  export interface ActionDef {
11
12
  key: string;
12
13
  label: string;
13
- /** Dracula color string from theme (e.g., theme.dracula.green). */
14
- color: string;
14
+ /** Semantic role consumed by `<ActionButton intent="...">`. */
15
+ intent: ActionButtonIntent;
15
16
  onClick: () => void;
16
17
  disabled?: boolean;
17
18
  icon?: React.ReactNode;
@@ -31,20 +32,15 @@ const ActionBar: React.FC<Props> = ({ actions, loading }) => {
31
32
  .map((a) => {
32
33
  const isLoading = loading === a.key;
33
34
  return (
34
- <Button
35
+ <ActionButton
35
36
  key={a.key}
36
- variant="outline"
37
+ intent={a.intent}
37
38
  onClick={a.onClick}
38
39
  disabled={a.disabled || isLoading}
39
- style={{
40
- backgroundColor: `${a.color}25`,
41
- borderColor: `${a.color}60`,
42
- color: a.color,
43
- }}
44
40
  >
45
41
  {isLoading ? <Loader2 className="h-4 w-4 animate-spin" /> : a.icon}
46
42
  {a.label}
47
- </Button>
43
+ </ActionButton>
48
44
  );
49
45
  })}
50
46
  </div>
@@ -6,9 +6,8 @@
6
6
 
7
7
  import React from 'react';
8
8
  import { Loader2, RotateCcw } from 'lucide-react';
9
- import { Button } from '@/components/ui/button';
10
9
  import { Alert, AlertDescription } from '@/components/ui/alert';
11
- import { useAppTheme } from '../../../hooks/useAppTheme';
10
+ import { ActionButton } from '@/components/ui/action-button';
12
11
  import FieldRenderer from './FieldRenderer';
13
12
  import ActionBar, { type ActionDef } from './ActionBar';
14
13
  import StatusCard from './StatusCard';
@@ -39,17 +38,22 @@ const OAuthConnect: React.FC<Props> = ({
39
38
  config, form, connected, stored, loading, error, icon,
40
39
  onSaveCredentials, onLogin, onLogout, onRefresh, extraSection,
41
40
  }) => {
42
- const theme = useAppTheme();
41
+ // Some providers (e.g. Stripe) delegate auth entirely to an external
42
+ // CLI tool — they have no MachinaOs-side credentials to paste, so
43
+ // there's nothing to "store" before Login is meaningful.
44
+ const hasFields = !!config.fields?.length;
43
45
 
44
46
  const statusRows: StatusRowDef[] = [
45
47
  { label: 'Status', ok: () => connected, trueText: 'Connected', falseText: 'Not Connected' },
46
- { label: 'Credentials', ok: () => stored, trueText: 'Configured', falseText: 'Not configured' },
48
+ ...(hasFields
49
+ ? [{ label: 'Credentials', ok: () => stored, trueText: 'Configured', falseText: 'Not configured' } as StatusRowDef]
50
+ : []),
47
51
  ];
48
52
 
49
53
  const actions: ActionDef[] = [
50
- { key: 'login', label: `Login with ${config.name}`, color: theme.dracula.green, onClick: onLogin, disabled: !stored, hidden: connected },
51
- { key: 'logout', label: 'Disconnect', color: theme.dracula.pink, onClick: onLogout, hidden: !connected },
52
- { key: 'refresh', label: 'Refresh', color: theme.dracula.cyan, onClick: onRefresh, icon: <RotateCcw className="h-4 w-4" /> },
54
+ { key: 'login', label: `Login with ${config.name}`, intent: 'save', onClick: onLogin, disabled: hasFields && !stored, hidden: connected },
55
+ { key: 'logout', label: 'Disconnect', intent: 'stop', onClick: onLogout, hidden: !connected },
56
+ { key: 'refresh', label: 'Refresh', intent: 'save', onClick: onRefresh, icon: <RotateCcw className="h-4 w-4" /> },
53
57
  ];
54
58
 
55
59
  const isSaving = loading === 'save';
@@ -61,19 +65,10 @@ const OAuthConnect: React.FC<Props> = ({
61
65
  {!connected && config.fields && (
62
66
  <div className="flex w-full flex-col gap-3">
63
67
  <FieldRenderer fields={config.fields} form={form} />
64
- <Button
65
- variant="outline"
66
- onClick={onSaveCredentials}
67
- disabled={isSaving}
68
- style={{
69
- backgroundColor: `${theme.dracula.purple}25`,
70
- borderColor: `${theme.dracula.purple}60`,
71
- color: theme.dracula.purple,
72
- }}
73
- >
68
+ <ActionButton intent="secret" onClick={onSaveCredentials} disabled={isSaving}>
74
69
  {isSaving && <Loader2 className="h-4 w-4 animate-spin" />}
75
70
  Save Credentials
76
- </Button>
71
+ </ActionButton>
77
72
  {config.instructions && (
78
73
  <div className="text-xs leading-relaxed text-muted-foreground">
79
74
  {config.instructions}
@@ -81,7 +76,7 @@ const OAuthConnect: React.FC<Props> = ({
81
76
  <>
82
77
  <br />
83
78
  Callback URL:{' '}
84
- <code className="text-dracula-cyan">{config.callbackUrl}</code>
79
+ <code className="text-accent">{config.callbackUrl}</code>
85
80
  </>
86
81
  )}
87
82
  </div>
@@ -95,17 +90,13 @@ const OAuthConnect: React.FC<Props> = ({
95
90
  </Alert>
96
91
  )}
97
92
 
98
- <div
99
- className="rounded-md border p-3"
100
- style={{
101
- backgroundColor: `${theme.dracula.cyan}10`,
102
- borderColor: `${theme.dracula.cyan}30`,
103
- }}
104
- >
93
+ <div className="rounded-md border border-accent/30 bg-accent/10 p-3">
105
94
  <div className="text-sm leading-relaxed text-muted-foreground">
106
95
  {connected
107
- ? `Your ${config.name} account is connected.`
108
- : stored
96
+ ? config.account_label
97
+ ? `Connected as ${config.account_label}.`
98
+ : `Your ${config.name} account is connected.`
99
+ : (stored || !hasFields)
109
100
  ? 'Click Login to authorize.'
110
101
  : 'Enter your credentials above to get started.'}
111
102
  </div>
@@ -39,6 +39,7 @@ import {
39
39
  AccordionTrigger,
40
40
  } from '@/components/ui/accordion';
41
41
  import { useApiKeys, type ProviderDefaults, type ModelConstraints } from '../../../hooks/useApiKeys';
42
+ import { useWebSocket } from '../../../contexts/WebSocketContext';
42
43
 
43
44
  const formSchema = z.object({
44
45
  default_model: z.string().optional().default(''),
@@ -58,6 +59,14 @@ interface Props {
58
59
 
59
60
  const ProviderDefaultsSection: React.FC<Props> = ({ providerId }) => {
60
61
  const { getProviderDefaults, saveProviderDefaults, getStoredModels, getModelConstraints, isConnected } = useApiKeys();
62
+ // Subscribe to apiKeyStatuses[providerId]: WebSocketContext updates
63
+ // this reactively after a successful validate (handler at line 2175-
64
+ // 2180) AND on the backend's api_key_status broadcast (line 686-696
65
+ // -- fired after the validator stores fresh models). Adding it as a
66
+ // dep to the fetch effect below makes the model dropdown refresh
67
+ // immediately after Fetch instead of requiring a page reload.
68
+ const { apiKeyStatuses } = useWebSocket();
69
+ const apiKeyStatus = apiKeyStatuses[providerId];
61
70
 
62
71
  const [models, setModels] = useState<string[]>([]);
63
72
  const [constraints, setConstraints] = useState<ModelConstraints | null>(null);
@@ -71,9 +80,18 @@ const ProviderDefaultsSection: React.FC<Props> = ({ providerId }) => {
71
80
  const thinkingEnabled = form.watch('thinking_enabled');
72
81
  const { isDirty } = form.formState;
73
82
 
74
- // Load defaults + models on mount.
83
+ // Load defaults + models on mount AND on providerId change.
84
+ // Reset models / constraints synchronously when the provider switches
85
+ // so a stale list from the previous panel can't bleed through. Without
86
+ // this, opening "OpenAI" then "LM Studio" left the OpenAI model list
87
+ // visible in the LM Studio dropdown — the dropdown only saw an
88
+ // explicit `setModels(m)` when the new fetch returned a non-empty
89
+ // list, so an empty `lmstudio` response (no Fetch clicked yet) was a
90
+ // no-op and the previous panel's state survived.
75
91
  useEffect(() => {
76
92
  if (!isConnected) return;
93
+ setModels([]);
94
+ setConstraints(null);
77
95
  let cancelled = false;
78
96
  (async () => {
79
97
  setLoading(true);
@@ -84,14 +102,17 @@ const ProviderDefaultsSection: React.FC<Props> = ({ providerId }) => {
84
102
  ]);
85
103
  if (!cancelled) {
86
104
  form.reset((d as Partial<ProviderDefaults>) ?? {});
87
- if (m?.length) setModels(m);
105
+ // Unconditional set: clears the dropdown when the fetch comes
106
+ // back empty (e.g. local-LLM panel before "Fetch" was clicked,
107
+ // or a freshly-removed key).
108
+ setModels(m ?? []);
88
109
  }
89
110
  } finally {
90
111
  if (!cancelled) setLoading(false);
91
112
  }
92
113
  })();
93
114
  return () => { cancelled = true; };
94
- }, [providerId, isConnected]);
115
+ }, [providerId, isConnected, apiKeyStatus]);
95
116
 
96
117
  // Refetch constraints when selected model changes.
97
118
  useEffect(() => {
@@ -4,6 +4,9 @@
4
4
  * ALL conditional logic lives in config — panel components are pure renderers.
5
5
  */
6
6
 
7
+ import type { ActionButtonIntent } from '@/components/ui/action-button';
8
+ export type { ActionButtonIntent };
9
+
7
10
  // ============================================================================
8
11
  // Panel kinds — one renderer branch per kind
9
12
  // ============================================================================
@@ -19,7 +22,11 @@ export interface FieldDef {
19
22
  label: string;
20
23
  secret?: boolean;
21
24
  placeholder?: string;
25
+ /** Pre-fill value when nothing stored. See ServerFieldDef.default. */
26
+ default?: string;
22
27
  required?: boolean;
28
+ /** Help text shown beneath the input (always visible). */
29
+ help?: string;
23
30
  }
24
31
 
25
32
  // ============================================================================
@@ -43,8 +50,9 @@ export interface StatusRowDef {
43
50
  export interface ActionDef {
44
51
  key: string;
45
52
  label: string;
46
- /** Theme color key (e.g., 'green', 'pink', 'cyan', 'orange'). Resolved at render. */
47
- themeColor: string;
53
+ /** Semantic role consumed by `<ActionButton intent="...">`. Themes
54
+ * remap the underlying --action-X tokens without touching call sites. */
55
+ intent: ActionButtonIntent;
48
56
  /** Return true to hide this action. */
49
57
  hidden?: (status: any, stored: boolean) => boolean;
50
58
  /** Return true to disable this action. */
@@ -115,6 +123,8 @@ export interface ProviderConfig {
115
123
  usageService?: string;
116
124
  /** Server-resolved: whether a key/token exists in the credentials DB. */
117
125
  stored?: boolean;
126
+ /** Connected account identifier (email or display name) for OAuth providers. */
127
+ account_label?: string | null;
118
128
  }
119
129
 
120
130
  // ============================================================================
@@ -45,40 +45,48 @@ export function useCredentialPanel(config: ProviderConfig, visible: boolean) {
45
45
  }, [config.id]);
46
46
 
47
47
  const {
48
- validateApiKey, saveApiKey, getStoredApiKey, hasStoredKey, removeApiKey,
48
+ validateApiKey, saveApiKey, removeApiKey,
49
49
  getProviderDefaults, saveProviderDefaults,
50
50
  getProviderUsageSummary, getAPIUsageSummary, getStoredModels, getModelConstraints,
51
51
  isConnected,
52
52
  } = useApiKeys();
53
53
  const { sendRequest } = useWebSocket();
54
54
 
55
- // Server-cached credential values. Fields load in parallel via
56
- // Promise.all so a 3-field provider takes one WS round-trip instead
57
- // of six sequential ones.
58
- const credentialValuesQuery = useQuery<CredentialFormValues, Error>({
55
+ // Server-cached credential values. Single RPC per field consolidates
56
+ // the previous hasStoredKey + getStoredApiKey pair (both went to
57
+ // ``get_stored_api_key`` anyway). The handler returns
58
+ // ``{hasKey, apiKey?}``; ``apiKey`` may carry the catalogue's
59
+ // ``default`` (e.g. local-LLM canonical Base URL) even when
60
+ // ``hasKey: false``, so the form renders a sensible value on a fresh
61
+ // install. ``hadStored`` tracks the real server state separately so
62
+ // the validated/connected badge stays honest — pre-filled defaults
63
+ // do NOT flip it to true.
64
+ const credentialValuesQuery = useQuery<{ values: CredentialFormValues; hadStored: boolean }, Error>({
59
65
  queryKey: queryKeys.credentialValues.byProvider(config.id).queryKey,
60
66
  queryFn: async () => {
61
- if (!config.fields) return EMPTY_VALUES;
62
- const entries = await Promise.all(
67
+ if (!config.fields) return { values: EMPTY_VALUES, hadStored: false };
68
+ const results = await Promise.all(
63
69
  config.fields.map(async (field) => {
64
70
  const storeKey = field.key === 'apiKey' ? config.id : field.key;
65
- const has = await hasStoredKey(storeKey);
66
- if (!has) return null;
67
- const val = await getStoredApiKey(storeKey);
68
- return val ? ([field.key, val] as const) : null;
71
+ const r = await sendRequest<{ hasKey: boolean; apiKey?: string }>(
72
+ 'get_stored_api_key', { provider: storeKey },
73
+ );
74
+ return { key: field.key, hasKey: r.hasKey, apiKey: r.apiKey };
69
75
  }),
70
76
  );
71
77
  const next: CredentialFormValues = {};
72
- for (const entry of entries) {
73
- if (entry) next[entry[0]] = entry[1];
78
+ let hadStored = false;
79
+ for (const r of results) {
80
+ if (r.apiKey) next[r.key] = r.apiKey;
81
+ if (r.hasKey) hadStored = true;
74
82
  }
75
- return next;
83
+ return { values: next, hadStored };
76
84
  },
77
85
  enabled: visible && isConnected && !!config.fields,
78
86
  staleTime: STALE_TIME.FOREVER,
79
87
  });
80
88
 
81
- const values = credentialValuesQuery.data ?? EMPTY_VALUES;
89
+ const values = credentialValuesQuery.data?.values ?? EMPTY_VALUES;
82
90
 
83
91
  // Imperative form-like API kept for compat with existing panel code.
84
92
  // Writes go through setQueryData on the provider's query so the cache
@@ -87,11 +95,23 @@ export function useCredentialPanel(config: ProviderConfig, visible: boolean) {
87
95
  valuesRef.current = values;
88
96
 
89
97
  const providerKey = config.id;
98
+ // The query stores `{values, hadStored}` (see queryFn above), NOT a flat
99
+ // CredentialFormValues. Earlier this called
100
+ // `setQueryData<CredentialFormValues>(...)` and the updater spread `prev`
101
+ // as if it were the inner `values` dict — at runtime `prev` was the whole
102
+ // envelope, so typed characters were merged at the envelope level next to
103
+ // `values` / `hadStored` and the input selector (which reads `.values[k]`)
104
+ // never saw them. Net effect: input felt unresponsive even though the
105
+ // setter was firing. Fix: update only the envelope's inner `values` and
106
+ // preserve `hadStored` verbatim.
90
107
  const writeValues = useCallback(
91
108
  (updater: (prev: CredentialFormValues) => CredentialFormValues) => {
92
- qc.setQueryData<CredentialFormValues>(
109
+ qc.setQueryData<{ values: CredentialFormValues; hadStored: boolean }>(
93
110
  queryKeys.credentialValues.byProvider(providerKey).queryKey,
94
- (prev) => updater(prev ?? EMPTY_VALUES),
111
+ (prev) => ({
112
+ values: updater(prev?.values ?? EMPTY_VALUES),
113
+ hadStored: prev?.hadStored ?? false,
114
+ }),
95
115
  );
96
116
  },
97
117
  [qc, providerKey],
@@ -118,8 +138,12 @@ export function useCredentialPanel(config: ProviderConfig, visible: boolean) {
118
138
  );
119
139
 
120
140
  // Sync stored from query on first load — if the backend already has
121
- // a key, mark stored=true so the badge renders without a validate click.
122
- const queriedStored = Object.keys(values).length > 0;
141
+ // mark stored=true based on the real server state (`hadStored`),
142
+ // NOT on whether the form happens to be populated. Pre-filled
143
+ // catalogue defaults (e.g. local-LLM Base URL) populate the form
144
+ // without being saved, so deriving from `Object.keys(values).length`
145
+ // would prematurely show the connected badge.
146
+ const queriedStored = credentialValuesQuery.data?.hadStored ?? false;
123
147
  if (queriedStored && !stored) setStored(true);
124
148
 
125
149
  // Generic action executor — replaces 19 duplicate handler functions.
@@ -12,6 +12,8 @@ import Cerebras from '@lobehub/icons/es/Cerebras';
12
12
  import DeepSeek from '@lobehub/icons/es/DeepSeek';
13
13
  import Kimi from '@lobehub/icons/es/Kimi';
14
14
  import Mistral from '@lobehub/icons/es/Mistral';
15
+ import Ollama from '@lobehub/icons/es/Ollama';
16
+ import LmStudio from '@lobehub/icons/es/LmStudio';
15
17
  import { dracula, solarized } from '../../styles/theme';
16
18
 
17
19
  // Icon size constant for consistency
@@ -58,6 +60,16 @@ export const MistralIcon: React.FC<{ size?: number }> = ({ size = ICON_SIZE }) =
58
60
  <Mistral.Color size={size} />
59
61
  );
60
62
 
63
+ // Local LLM servers — Ollama exposes a Color variant; LmStudio's lobehub
64
+ // entry only ships an Avatar so we use that for parity.
65
+ export const OllamaIcon: React.FC<{ size?: number }> = ({ size = ICON_SIZE }) => (
66
+ <Ollama.Avatar size={size} />
67
+ );
68
+
69
+ export const LmStudioIcon: React.FC<{ size?: number }> = ({ size = ICON_SIZE }) => (
70
+ <LmStudio.Avatar size={size} />
71
+ );
72
+
61
73
  // Map provider IDs to their icon components
62
74
  export const AI_PROVIDER_ICONS: Record<string, React.FC<{ size?: number }>> = {
63
75
  openai: OpenAIIcon,
@@ -69,6 +81,8 @@ export const AI_PROVIDER_ICONS: Record<string, React.FC<{ size?: number }>> = {
69
81
  deepseek: DeepSeekIcon,
70
82
  kimi: KimiIcon,
71
83
  mistral: MistralIcon,
84
+ ollama: OllamaIcon,
85
+ lmstudio: LmStudioIcon,
72
86
  };
73
87
 
74
88
  // Centralized provider metadata (icon ref, theme color, display label).
@@ -86,6 +100,8 @@ export const AI_PROVIDER_META: Record<string, { iconRef: string; Icon: React.FC<
86
100
  deepseek: { iconRef: 'lobehub:DeepSeek', Icon: DeepSeekIcon, color: dracula.cyan, label: 'DeepSeek' },
87
101
  kimi: { iconRef: 'lobehub:Kimi', Icon: KimiIcon, color: dracula.purple, label: 'Kimi' },
88
102
  mistral: { iconRef: 'lobehub:Mistral', Icon: MistralIcon, color: dracula.pink, label: 'Mistral' },
103
+ ollama: { iconRef: 'lobehub:Ollama', Icon: OllamaIcon, color: dracula.foreground, label: 'Ollama' },
104
+ lmstudio: { iconRef: 'lobehub:LmStudio', Icon: LmStudioIcon, color: solarized.cyan, label: 'LM Studio' },
89
105
  };
90
106
 
91
107
  // Get icon component by provider ID
@@ -2,10 +2,10 @@ import React from 'react';
2
2
  import { ArrowLeft, ArrowRight, Check } from 'lucide-react';
3
3
 
4
4
  import { Button } from '@/components/ui/button';
5
+ import { ActionButton } from '@/components/ui/action-button';
5
6
  import { cn } from '@/lib/utils';
6
7
  import Modal from '../ui/Modal';
7
8
  import { useOnboarding } from '../../hooks/useOnboarding';
8
- import { useAppTheme } from '../../hooks/useAppTheme';
9
9
  import WelcomeStep from './steps/WelcomeStep';
10
10
  import ConceptsStep from './steps/ConceptsStep';
11
11
  import ApiKeyStep from './steps/ApiKeyStep';
@@ -17,42 +17,33 @@ interface OnboardingWizardProps {
17
17
  reopenTrigger?: number;
18
18
  }
19
19
 
20
- const stepItems = [
21
- { title: 'Welcome' },
22
- { title: 'Concepts' },
23
- { title: 'API Keys' },
24
- { title: 'Canvas' },
25
- { title: 'Get Started' },
20
+ // Single source of truth for the wizard's step list. Length feeds the
21
+ // hook's totalSteps and the progress indicator; renderer is dispatched
22
+ // by index. Adding a step is a one-line edit here.
23
+ const STEPS: { title: string; render: (props: { onOpenCredentials: () => void }) => React.ReactNode }[] = [
24
+ { title: 'Welcome', render: () => <WelcomeStep /> },
25
+ { title: 'Concepts', render: () => <ConceptsStep /> },
26
+ { title: 'API Keys', render: ({ onOpenCredentials }) => <ApiKeyStep onOpenCredentials={onOpenCredentials} /> },
27
+ { title: 'Canvas', render: () => <CanvasStep /> },
28
+ { title: 'Get Started', render: () => <GetStartedStep /> },
26
29
  ];
27
30
 
28
31
  const OnboardingWizard: React.FC<OnboardingWizardProps> = ({ onOpenCredentials, reopenTrigger }) => {
29
- const theme = useAppTheme();
30
32
  const {
31
33
  isVisible,
32
34
  currentStep,
33
35
  isLoading,
34
36
  hasChecked,
35
- totalSteps,
36
37
  nextStep,
37
38
  prevStep,
38
39
  skip,
39
40
  complete,
40
- } = useOnboarding(reopenTrigger);
41
+ } = useOnboarding(reopenTrigger, STEPS.length);
41
42
 
42
43
  if (!isVisible || !hasChecked || isLoading) return null;
43
44
 
44
- const isLastStep = currentStep >= totalSteps - 1;
45
-
46
- const renderStep = () => {
47
- switch (currentStep) {
48
- case 0: return <WelcomeStep />;
49
- case 1: return <ConceptsStep />;
50
- case 2: return <ApiKeyStep onOpenCredentials={onOpenCredentials} />;
51
- case 3: return <CanvasStep />;
52
- case 4: return <GetStartedStep />;
53
- default: return <WelcomeStep />;
54
- }
55
- };
45
+ const isLastStep = currentStep >= STEPS.length - 1;
46
+ const safeIndex = Math.min(Math.max(currentStep, 0), STEPS.length - 1);
56
47
 
57
48
  return (
58
49
  <Modal
@@ -61,19 +52,14 @@ const OnboardingWizard: React.FC<OnboardingWizardProps> = ({ onOpenCredentials,
61
52
  title="Welcome Guide"
62
53
  maxWidth="95vw"
63
54
  maxHeight="95vh"
64
- autoHeight
65
55
  >
66
- <div style={{
67
- display: 'flex',
68
- flexDirection: 'column',
69
- padding: '16px 20px 12px',
70
- }}>
56
+ <div className="flex flex-col px-5 pt-4 pb-3">
71
57
  {/* Progress steps */}
72
58
  <ol className="mb-4 flex w-full items-center">
73
- {stepItems.map((item, idx) => {
59
+ {STEPS.map((item, idx) => {
74
60
  const status: 'completed' | 'active' | 'upcoming' =
75
61
  idx < currentStep ? 'completed' : idx === currentStep ? 'active' : 'upcoming';
76
- const isLast = idx === stepItems.length - 1;
62
+ const isLast = idx === STEPS.length - 1;
77
63
  return (
78
64
  <li key={item.title} className="flex flex-1 items-center gap-2">
79
65
  <div
@@ -108,30 +94,16 @@ const OnboardingWizard: React.FC<OnboardingWizardProps> = ({ onOpenCredentials,
108
94
  </ol>
109
95
 
110
96
  {/* Step content */}
111
- <div style={{
112
- overflowY: 'auto',
113
- minHeight: 500,
114
- maxHeight: 'calc(95vh - 200px)',
115
- paddingRight: 4,
116
- }}>
117
- {renderStep()}
97
+ <div className="overflow-y-auto pr-1 max-h-[calc(95vh-200px)]">
98
+ {STEPS[safeIndex].render({ onOpenCredentials })}
118
99
  </div>
119
100
 
120
101
  {/* Footer navigation */}
121
- <div style={{
122
- display: 'flex',
123
- justifyContent: 'space-between',
124
- alignItems: 'center',
125
- paddingTop: 12,
126
- marginTop: 12,
127
- borderTop: `1px solid ${theme.colors.border}`,
128
- }}>
129
- {/* Left: Skip */}
102
+ <div className="flex items-center justify-between border-t border-border pt-3 mt-3">
130
103
  <Button variant="ghost" size="sm" onClick={skip} className="text-muted-foreground">
131
104
  Skip for now
132
105
  </Button>
133
106
 
134
- {/* Right: Back + Next/Finish */}
135
107
  <div className="flex items-center gap-2">
136
108
  {currentStep > 0 && (
137
109
  <Button variant="outline" onClick={prevStep}>
@@ -140,27 +112,15 @@ const OnboardingWizard: React.FC<OnboardingWizardProps> = ({ onOpenCredentials,
140
112
  </Button>
141
113
  )}
142
114
  {isLastStep ? (
143
- <Button
144
- onClick={complete}
145
- style={{
146
- backgroundColor: theme.dracula.green,
147
- borderColor: theme.dracula.green,
148
- }}
149
- >
115
+ <ActionButton intent="run" onClick={complete}>
150
116
  <Check className="h-4 w-4" />
151
117
  Start Building
152
- </Button>
118
+ </ActionButton>
153
119
  ) : (
154
- <Button
155
- onClick={nextStep}
156
- style={{
157
- backgroundColor: theme.dracula.purple,
158
- borderColor: theme.dracula.purple,
159
- }}
160
- >
120
+ <ActionButton intent="tools" onClick={nextStep}>
161
121
  Next
162
122
  <ArrowRight className="h-4 w-4" />
163
- </Button>
123
+ </ActionButton>
164
124
  )}
165
125
  </div>
166
126
  </div>
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Shared role-to-Tailwind-classes map for the onboarding step cards.
3
+ * Each role keys into the existing `--node-X` triplet (soft bg + border
4
+ * + accent text). Co-located with the onboarding folder because both
5
+ * consumers (ConceptsStep, GetStartedStep) live here.
6
+ */
7
+
8
+ export type NodeRole = 'model' | 'skill' | 'agent' | 'workflow' | 'trigger';
9
+
10
+ export interface NodeRoleClasses {
11
+ /** Tinted background + border for the card surface. */
12
+ card: string;
13
+ /** Accent text colour matching the card's role. */
14
+ text: string;
15
+ }
16
+
17
+ export const NODE_ROLE_CLASSES: Record<NodeRole, NodeRoleClasses> = {
18
+ model: { card: 'bg-node-model-soft border-node-model-border', text: 'text-node-model' },
19
+ skill: { card: 'bg-node-skill-soft border-node-skill-border', text: 'text-node-skill' },
20
+ agent: { card: 'bg-node-agent-soft border-node-agent-border', text: 'text-node-agent' },
21
+ workflow: { card: 'bg-node-workflow-soft border-node-workflow-border', text: 'text-node-workflow' },
22
+ trigger: { card: 'bg-node-trigger-soft border-node-trigger-border', text: 'text-node-trigger' },
23
+ };
@@ -19,13 +19,19 @@ const CanvasStep: React.FC = () => {
19
19
  </p>
20
20
  </div>
21
21
 
22
- {/* Visual layout diagram */}
22
+ {/*
23
+ * Visual layout diagram. Region tints mirror the live UI's
24
+ * accent colours via the predefined --node-X-soft tokens — no
25
+ * opacity arithmetic at the call site. Each region maps to a
26
+ * node-group colour: toolbar→workflow (orange), sidebar→model
27
+ * (cyan), canvas→agent (purple), palette→skill (green),
28
+ * console→trigger (pink). Decorative use of the dracula text
29
+ * colours is permitted (tutorial diagram intentionally mirrors
30
+ * the live UI palette).
31
+ */}
23
32
  <div className="mb-4 overflow-hidden rounded-lg border border-border">
24
33
  {/* Toolbar */}
25
- <div
26
- className="flex items-center gap-1.5 border-b border-border px-2.5 py-1.5"
27
- style={{ backgroundColor: 'hsl(var(--dracula-orange) / 0.12)' }}
28
- >
34
+ <div className="flex items-center gap-1.5 border-b border-border bg-node-workflow-soft px-2.5 py-1.5">
29
35
  <Wrench className="h-3 w-3 text-dracula-orange" />
30
36
  <span className="text-xs font-semibold text-dracula-orange">Toolbar</span>
31
37
  <div className="flex-1" />
@@ -37,10 +43,7 @@ const CanvasStep: React.FC = () => {
37
43
  {/* Main area */}
38
44
  <div className="flex h-[120px]">
39
45
  {/* Sidebar */}
40
- <div
41
- className="flex w-20 flex-col gap-1 border-r border-border p-1.5"
42
- style={{ backgroundColor: 'hsl(var(--dracula-cyan) / 0.08)' }}
43
- >
46
+ <div className="flex w-20 flex-col gap-1 border-r border-border bg-node-model-soft p-1.5">
44
47
  <span className="text-[9px] font-semibold text-dracula-cyan">Sidebar</span>
45
48
  {['Workflow 1', 'Workflow 2'].map((w) => (
46
49
  <div key={w} className="rounded-sm bg-muted px-1 py-0.5 text-[8px] text-muted-foreground">
@@ -50,10 +53,7 @@ const CanvasStep: React.FC = () => {
50
53
  </div>
51
54
 
52
55
  {/* Canvas */}
53
- <div
54
- className="relative flex flex-1 items-center justify-center"
55
- style={{ backgroundColor: 'hsl(var(--dracula-purple) / 0.06)' }}
56
- >
56
+ <div className="relative flex flex-1 items-center justify-center bg-node-agent-soft">
57
57
  <div className="text-center">
58
58
  <Layout className="mx-auto h-5 w-5 text-dracula-purple opacity-50" />
59
59
  <div className="mt-0.5 text-[9px] text-dracula-purple">Canvas</div>
@@ -61,10 +61,7 @@ const CanvasStep: React.FC = () => {
61
61
  </div>
62
62
 
63
63
  {/* Palette */}
64
- <div
65
- className="flex w-20 flex-col gap-1 border-l border-border p-1.5"
66
- style={{ backgroundColor: 'hsl(var(--dracula-green) / 0.08)' }}
67
- >
64
+ <div className="flex w-20 flex-col gap-1 border-l border-border bg-node-skill-soft p-1.5">
68
65
  <span className="text-[9px] font-semibold text-dracula-green">Palette</span>
69
66
  {['AI Agents', 'AI Models', 'Skills'].map((c) => (
70
67
  <div key={c} className="rounded-sm bg-muted px-1 py-0.5 text-[8px] text-muted-foreground">
@@ -75,10 +72,7 @@ const CanvasStep: React.FC = () => {
75
72
  </div>
76
73
 
77
74
  {/* Console */}
78
- <div
79
- className="flex items-center gap-1.5 border-t border-border px-2.5 py-1.5"
80
- style={{ backgroundColor: 'hsl(var(--dracula-pink) / 0.08)' }}
81
- >
75
+ <div className="flex items-center gap-1.5 border-t border-border bg-node-trigger-soft px-2.5 py-1.5">
82
76
  <Terminal className="h-3 w-3 text-dracula-pink" />
83
77
  <span className="text-xs font-semibold text-dracula-pink">Console</span>
84
78
  <div className="flex-1" />