machinaos 0.0.1

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 (288) hide show
  1. package/.env.template +71 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/bin/cli.js +159 -0
  5. package/client/.dockerignore +45 -0
  6. package/client/Dockerfile +68 -0
  7. package/client/eslint.config.js +29 -0
  8. package/client/index.html +13 -0
  9. package/client/nginx.conf +66 -0
  10. package/client/package.json +48 -0
  11. package/client/src/App.tsx +27 -0
  12. package/client/src/Dashboard.tsx +1173 -0
  13. package/client/src/ParameterPanel.tsx +301 -0
  14. package/client/src/components/AIAgentNode.tsx +321 -0
  15. package/client/src/components/APIKeyValidator.tsx +118 -0
  16. package/client/src/components/ClaudeChatModelNode.tsx +18 -0
  17. package/client/src/components/ConditionalEdge.tsx +189 -0
  18. package/client/src/components/CredentialsModal.tsx +306 -0
  19. package/client/src/components/EdgeConditionEditor.tsx +443 -0
  20. package/client/src/components/GeminiChatModelNode.tsx +18 -0
  21. package/client/src/components/GenericNode.tsx +357 -0
  22. package/client/src/components/LocationParameterPanel.tsx +154 -0
  23. package/client/src/components/ModelNode.tsx +286 -0
  24. package/client/src/components/OpenAIChatModelNode.tsx +18 -0
  25. package/client/src/components/OutputPanel.tsx +471 -0
  26. package/client/src/components/ParameterRenderer.tsx +1874 -0
  27. package/client/src/components/SkillEditorModal.tsx +417 -0
  28. package/client/src/components/SquareNode.tsx +797 -0
  29. package/client/src/components/StartNode.tsx +250 -0
  30. package/client/src/components/ToolkitNode.tsx +365 -0
  31. package/client/src/components/TriggerNode.tsx +463 -0
  32. package/client/src/components/auth/LoginPage.tsx +247 -0
  33. package/client/src/components/auth/ProtectedRoute.tsx +59 -0
  34. package/client/src/components/base/BaseChatModelNode.tsx +271 -0
  35. package/client/src/components/icons/AIProviderIcons.tsx +50 -0
  36. package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
  37. package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
  38. package/client/src/components/maps/index.ts +26 -0
  39. package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
  40. package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
  41. package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
  42. package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
  43. package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
  44. package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
  45. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
  46. package/client/src/components/parameterPanel/index.ts +42 -0
  47. package/client/src/components/shared/DataPanel.tsx +142 -0
  48. package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
  49. package/client/src/components/ui/AIResultModal.tsx +204 -0
  50. package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
  51. package/client/src/components/ui/CodeEditor.tsx +81 -0
  52. package/client/src/components/ui/CollapsibleSection.tsx +88 -0
  53. package/client/src/components/ui/ComponentItem.tsx +154 -0
  54. package/client/src/components/ui/ComponentPalette.tsx +321 -0
  55. package/client/src/components/ui/ConsolePanel.tsx +1074 -0
  56. package/client/src/components/ui/ErrorBoundary.tsx +196 -0
  57. package/client/src/components/ui/InputNodesPanel.tsx +204 -0
  58. package/client/src/components/ui/MapSelector.tsx +314 -0
  59. package/client/src/components/ui/Modal.tsx +149 -0
  60. package/client/src/components/ui/NodeContextMenu.tsx +192 -0
  61. package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
  62. package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
  63. package/client/src/components/ui/SettingsPanel.tsx +243 -0
  64. package/client/src/components/ui/TopToolbar.tsx +736 -0
  65. package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
  66. package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
  67. package/client/src/config/antdTheme.ts +186 -0
  68. package/client/src/config/api.ts +54 -0
  69. package/client/src/contexts/AuthContext.tsx +221 -0
  70. package/client/src/contexts/ThemeContext.tsx +42 -0
  71. package/client/src/contexts/WebSocketContext.tsx +1971 -0
  72. package/client/src/factories/baseChatModelFactory.ts +256 -0
  73. package/client/src/hooks/useAndroidOperations.ts +164 -0
  74. package/client/src/hooks/useApiKeyValidation.ts +107 -0
  75. package/client/src/hooks/useApiKeys.ts +238 -0
  76. package/client/src/hooks/useAppTheme.ts +17 -0
  77. package/client/src/hooks/useComponentPalette.ts +51 -0
  78. package/client/src/hooks/useCopyPaste.ts +155 -0
  79. package/client/src/hooks/useDragAndDrop.ts +124 -0
  80. package/client/src/hooks/useDragVariable.ts +88 -0
  81. package/client/src/hooks/useExecution.ts +313 -0
  82. package/client/src/hooks/useParameterPanel.ts +176 -0
  83. package/client/src/hooks/useReactFlowNodes.ts +189 -0
  84. package/client/src/hooks/useToolSchema.ts +209 -0
  85. package/client/src/hooks/useWhatsApp.ts +196 -0
  86. package/client/src/hooks/useWorkflowManagement.ts +46 -0
  87. package/client/src/index.css +315 -0
  88. package/client/src/main.tsx +19 -0
  89. package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
  90. package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
  91. package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
  92. package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
  93. package/client/src/nodeDefinitions/chatNodes.ts +135 -0
  94. package/client/src/nodeDefinitions/codeNodes.ts +54 -0
  95. package/client/src/nodeDefinitions/documentNodes.ts +379 -0
  96. package/client/src/nodeDefinitions/index.ts +15 -0
  97. package/client/src/nodeDefinitions/locationNodes.ts +463 -0
  98. package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
  99. package/client/src/nodeDefinitions/skillNodes.ts +211 -0
  100. package/client/src/nodeDefinitions/toolNodes.ts +198 -0
  101. package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
  102. package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
  103. package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
  104. package/client/src/nodeDefinitions.ts +104 -0
  105. package/client/src/schemas/workflowSchema.ts +264 -0
  106. package/client/src/services/dynamicParameterService.ts +96 -0
  107. package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
  108. package/client/src/services/executionService.ts +232 -0
  109. package/client/src/services/workflowApi.ts +91 -0
  110. package/client/src/store/useAppStore.ts +582 -0
  111. package/client/src/styles/theme.ts +508 -0
  112. package/client/src/styles/zIndex.ts +17 -0
  113. package/client/src/types/ComponentTypes.ts +39 -0
  114. package/client/src/types/EdgeCondition.ts +231 -0
  115. package/client/src/types/INodeProperties.ts +288 -0
  116. package/client/src/types/NodeTypes.ts +28 -0
  117. package/client/src/utils/formatters.ts +33 -0
  118. package/client/src/utils/googleMapsLoader.ts +140 -0
  119. package/client/src/utils/locationUtils.ts +85 -0
  120. package/client/src/utils/nodeUtils.ts +31 -0
  121. package/client/src/utils/workflow.ts +30 -0
  122. package/client/src/utils/workflowExport.ts +120 -0
  123. package/client/src/vite-env.d.ts +12 -0
  124. package/client/tailwind.config.js +60 -0
  125. package/client/tsconfig.json +25 -0
  126. package/client/tsconfig.node.json +11 -0
  127. package/client/vite.config.js +35 -0
  128. package/docker-compose.prod.yml +107 -0
  129. package/docker-compose.yml +104 -0
  130. package/docs-MachinaOs/README.md +85 -0
  131. package/docs-MachinaOs/deployment/docker.mdx +228 -0
  132. package/docs-MachinaOs/deployment/production.mdx +345 -0
  133. package/docs-MachinaOs/docs.json +75 -0
  134. package/docs-MachinaOs/faq.mdx +309 -0
  135. package/docs-MachinaOs/favicon.svg +5 -0
  136. package/docs-MachinaOs/installation.mdx +160 -0
  137. package/docs-MachinaOs/introduction.mdx +114 -0
  138. package/docs-MachinaOs/logo/dark.svg +6 -0
  139. package/docs-MachinaOs/logo/light.svg +6 -0
  140. package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
  141. package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
  142. package/docs-MachinaOs/nodes/android.mdx +411 -0
  143. package/docs-MachinaOs/nodes/overview.mdx +181 -0
  144. package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
  145. package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
  146. package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
  147. package/docs-MachinaOs/quickstart.mdx +119 -0
  148. package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
  149. package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
  150. package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
  151. package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
  152. package/nul +0 -0
  153. package/package.json +70 -0
  154. package/scripts/build.js +158 -0
  155. package/scripts/check-ports.ps1 +33 -0
  156. package/scripts/clean.js +40 -0
  157. package/scripts/docker.js +93 -0
  158. package/scripts/kill-port.ps1 +154 -0
  159. package/scripts/start.js +210 -0
  160. package/scripts/stop.js +325 -0
  161. package/server/.dockerignore +44 -0
  162. package/server/Dockerfile +45 -0
  163. package/server/constants.py +249 -0
  164. package/server/core/__init__.py +1 -0
  165. package/server/core/cache.py +461 -0
  166. package/server/core/config.py +128 -0
  167. package/server/core/container.py +99 -0
  168. package/server/core/database.py +1211 -0
  169. package/server/core/logging.py +314 -0
  170. package/server/main.py +289 -0
  171. package/server/middleware/__init__.py +5 -0
  172. package/server/middleware/auth.py +89 -0
  173. package/server/models/__init__.py +1 -0
  174. package/server/models/auth.py +52 -0
  175. package/server/models/cache.py +24 -0
  176. package/server/models/database.py +211 -0
  177. package/server/models/nodes.py +455 -0
  178. package/server/package.json +9 -0
  179. package/server/pyproject.toml +72 -0
  180. package/server/requirements.txt +83 -0
  181. package/server/routers/__init__.py +1 -0
  182. package/server/routers/android.py +294 -0
  183. package/server/routers/auth.py +203 -0
  184. package/server/routers/database.py +151 -0
  185. package/server/routers/maps.py +142 -0
  186. package/server/routers/nodejs_compat.py +289 -0
  187. package/server/routers/webhook.py +90 -0
  188. package/server/routers/websocket.py +2127 -0
  189. package/server/routers/whatsapp.py +761 -0
  190. package/server/routers/workflow.py +200 -0
  191. package/server/services/__init__.py +1 -0
  192. package/server/services/ai.py +2415 -0
  193. package/server/services/android/__init__.py +27 -0
  194. package/server/services/android/broadcaster.py +114 -0
  195. package/server/services/android/client.py +608 -0
  196. package/server/services/android/manager.py +78 -0
  197. package/server/services/android/protocol.py +165 -0
  198. package/server/services/android_service.py +588 -0
  199. package/server/services/auth.py +131 -0
  200. package/server/services/chat_client.py +160 -0
  201. package/server/services/deployment/__init__.py +12 -0
  202. package/server/services/deployment/manager.py +706 -0
  203. package/server/services/deployment/state.py +47 -0
  204. package/server/services/deployment/triggers.py +275 -0
  205. package/server/services/event_waiter.py +785 -0
  206. package/server/services/execution/__init__.py +77 -0
  207. package/server/services/execution/cache.py +769 -0
  208. package/server/services/execution/conditions.py +373 -0
  209. package/server/services/execution/dlq.py +132 -0
  210. package/server/services/execution/executor.py +1351 -0
  211. package/server/services/execution/models.py +531 -0
  212. package/server/services/execution/recovery.py +235 -0
  213. package/server/services/handlers/__init__.py +126 -0
  214. package/server/services/handlers/ai.py +355 -0
  215. package/server/services/handlers/android.py +260 -0
  216. package/server/services/handlers/code.py +278 -0
  217. package/server/services/handlers/document.py +598 -0
  218. package/server/services/handlers/http.py +193 -0
  219. package/server/services/handlers/polyglot.py +105 -0
  220. package/server/services/handlers/tools.py +845 -0
  221. package/server/services/handlers/triggers.py +107 -0
  222. package/server/services/handlers/utility.py +822 -0
  223. package/server/services/handlers/whatsapp.py +476 -0
  224. package/server/services/maps.py +289 -0
  225. package/server/services/memory_store.py +103 -0
  226. package/server/services/node_executor.py +375 -0
  227. package/server/services/parameter_resolver.py +218 -0
  228. package/server/services/polyglot_client.py +169 -0
  229. package/server/services/scheduler.py +155 -0
  230. package/server/services/skill_loader.py +417 -0
  231. package/server/services/status_broadcaster.py +826 -0
  232. package/server/services/temporal/__init__.py +23 -0
  233. package/server/services/temporal/activities.py +344 -0
  234. package/server/services/temporal/client.py +76 -0
  235. package/server/services/temporal/executor.py +147 -0
  236. package/server/services/temporal/worker.py +251 -0
  237. package/server/services/temporal/workflow.py +355 -0
  238. package/server/services/temporal/ws_client.py +236 -0
  239. package/server/services/text.py +111 -0
  240. package/server/services/user_auth.py +172 -0
  241. package/server/services/websocket_client.py +29 -0
  242. package/server/services/workflow.py +597 -0
  243. package/server/skills/android-skill/SKILL.md +82 -0
  244. package/server/skills/assistant-personality/SKILL.md +45 -0
  245. package/server/skills/code-skill/SKILL.md +140 -0
  246. package/server/skills/http-skill/SKILL.md +161 -0
  247. package/server/skills/maps-skill/SKILL.md +170 -0
  248. package/server/skills/memory-skill/SKILL.md +154 -0
  249. package/server/skills/scheduler-skill/SKILL.md +84 -0
  250. package/server/skills/whatsapp-skill/SKILL.md +283 -0
  251. package/server/uv.lock +2916 -0
  252. package/server/whatsapp-rpc/.dockerignore +30 -0
  253. package/server/whatsapp-rpc/Dockerfile +44 -0
  254. package/server/whatsapp-rpc/Dockerfile.web +17 -0
  255. package/server/whatsapp-rpc/README.md +139 -0
  256. package/server/whatsapp-rpc/cli.js +95 -0
  257. package/server/whatsapp-rpc/configs/config.yaml +7 -0
  258. package/server/whatsapp-rpc/docker-compose.yml +35 -0
  259. package/server/whatsapp-rpc/docs/API.md +410 -0
  260. package/server/whatsapp-rpc/go.mod +67 -0
  261. package/server/whatsapp-rpc/go.sum +203 -0
  262. package/server/whatsapp-rpc/package.json +30 -0
  263. package/server/whatsapp-rpc/schema.json +1294 -0
  264. package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
  265. package/server/whatsapp-rpc/scripts/cli.js +162 -0
  266. package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
  267. package/server/whatsapp-rpc/src/go/config/config.go +49 -0
  268. package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
  269. package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
  270. package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
  271. package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
  272. package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
  273. package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
  274. package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
  275. package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
  276. package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
  277. package/server/whatsapp-rpc/web/app.py +609 -0
  278. package/server/whatsapp-rpc/web/requirements.txt +6 -0
  279. package/server/whatsapp-rpc/web/rpc_client.py +427 -0
  280. package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
  281. package/server/whatsapp-rpc/web/templates/base.html +150 -0
  282. package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
  283. package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
  284. package/server/whatsapp-rpc/web/templates/groups.html +328 -0
  285. package/server/whatsapp-rpc/web/templates/messages.html +465 -0
  286. package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
  287. package/server/whatsapp-rpc/web/templates/send.html +259 -0
  288. package/server/whatsapp-rpc/web/templates/settings.html +459 -0
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import { Node } from 'reactflow';
3
+ import InputSection from './InputSection';
4
+ import MiddleSection from './MiddleSection';
5
+ import OutputSection from './OutputSection';
6
+ import { INodeTypeDescription } from '../../types/INodeProperties';
7
+ import { ExecutionResult } from '../../services/executionService';
8
+
9
+ interface ParameterPanelLayoutProps {
10
+ // Node data
11
+ selectedNode: Node;
12
+ nodeDefinition: INodeTypeDescription;
13
+ parameters: Record<string, any>;
14
+ hasUnsavedChanges: boolean;
15
+
16
+ // Parameter handling
17
+ onParameterChange: (paramName: string, value: any) => void;
18
+
19
+ // Execution data
20
+ executionResults: ExecutionResult[];
21
+ onClearResults: () => void;
22
+
23
+ // Layout configuration
24
+ showInputSection?: boolean;
25
+ showOutputSection?: boolean;
26
+
27
+ // Loading state
28
+ isLoadingParameters?: boolean;
29
+ }
30
+
31
+ const ParameterPanelLayout: React.FC<ParameterPanelLayoutProps> = ({
32
+ selectedNode,
33
+ nodeDefinition,
34
+ parameters,
35
+ hasUnsavedChanges: _hasUnsavedChanges,
36
+ onParameterChange,
37
+ executionResults,
38
+ onClearResults,
39
+ showInputSection = true,
40
+ showOutputSection = true,
41
+ isLoadingParameters = false
42
+ }) => {
43
+ return (
44
+ <div style={{ display: 'flex', height: '100%', minHeight: 0 }}>
45
+ {/* Left: Input Nodes JSON Data */}
46
+ {showInputSection && (
47
+ <div style={{ flex: 0.7, height: '100%', overflow: 'hidden' }}>
48
+ <InputSection
49
+ nodeId={selectedNode.id}
50
+ visible={showInputSection}
51
+ />
52
+ </div>
53
+ )}
54
+
55
+ {/* Middle: Parameter Content */}
56
+ <div style={{ flex: 1.6, height: '100%', overflow: 'hidden', minWidth: 0 }}>
57
+ <MiddleSection
58
+ nodeId={selectedNode.id}
59
+ nodeDefinition={nodeDefinition}
60
+ parameters={parameters}
61
+ onParameterChange={onParameterChange}
62
+ isLoadingParameters={isLoadingParameters}
63
+ executionResults={executionResults}
64
+ />
65
+ </div>
66
+
67
+ {/* Right: Current Node Output */}
68
+ {showOutputSection && (
69
+ <div style={{ flex: 0.7, height: '100%', overflow: 'hidden' }}>
70
+ <OutputSection
71
+ selectedNode={selectedNode}
72
+ executionResults={executionResults}
73
+ onClearResults={onClearResults}
74
+ visible={showOutputSection}
75
+ />
76
+ </div>
77
+ )}
78
+ </div>
79
+ );
80
+ };
81
+
82
+ export default ParameterPanelLayout;
@@ -0,0 +1,436 @@
1
+ /**
2
+ * ToolSchemaEditor - Schema editor for Android Toolkit node
3
+ *
4
+ * Shows connected Android service nodes and schema fields for the LLM
5
+ */
6
+
7
+ import React, { useState, useEffect, useCallback, useMemo } from 'react';
8
+ import { Node } from 'reactflow';
9
+ import { useAppTheme } from '../../hooks/useAppTheme';
10
+ import { useToolSchema, ToolSchemaConfig, SchemaFieldConfig } from '../../hooks/useToolSchema';
11
+ import { useWebSocket } from '../../contexts/WebSocketContext';
12
+ import { useAppStore } from '../../store/useAppStore';
13
+ import { ANDROID_SERVICE_NODE_TYPES } from '../../nodeDefinitions/androidServiceNodes';
14
+
15
+ interface ToolSchemaEditorProps {
16
+ nodeId: string;
17
+ toolName: string;
18
+ toolDescription: string;
19
+ }
20
+
21
+ const FIELD_TYPES = ['string', 'number', 'integer', 'boolean', 'object', 'array'] as const;
22
+
23
+ // Default schema for androidTool
24
+ const DEFAULT_SCHEMA: ToolSchemaConfig = {
25
+ description: 'Control Android device via connected services',
26
+ fields: {
27
+ service_id: {
28
+ type: 'string',
29
+ description: 'Service to use (determined by connected Android nodes)',
30
+ required: true
31
+ },
32
+ action: {
33
+ type: 'string',
34
+ description: 'Action to perform (see service list for available actions)',
35
+ required: true
36
+ },
37
+ parameters: {
38
+ type: 'object',
39
+ description: 'Action parameters. Examples: {package_name: "com.app"} for app_launcher',
40
+ required: false
41
+ }
42
+ }
43
+ };
44
+
45
+ const ToolSchemaEditor: React.FC<ToolSchemaEditorProps> = ({ nodeId }) => {
46
+ const theme = useAppTheme();
47
+ const { getToolSchema, saveToolSchema, deleteToolSchema, isLoading } = useToolSchema();
48
+ const { isConnected } = useWebSocket();
49
+ const { currentWorkflow } = useAppStore();
50
+
51
+ // Get the current node
52
+ const currentNode = useMemo(() => {
53
+ if (!currentWorkflow?.nodes) return null;
54
+ return currentWorkflow.nodes.find(n => n.id === nodeId);
55
+ }, [currentWorkflow?.nodes, nodeId]);
56
+
57
+ const isAndroidTool = currentNode?.type === 'androidTool';
58
+
59
+ // Only show for androidTool
60
+ if (!isAndroidTool) return null;
61
+
62
+ // Find connected Android service nodes via edges
63
+ const connectedServices = useMemo(() => {
64
+ if (!currentWorkflow?.edges || !currentWorkflow?.nodes) return [];
65
+
66
+ // Find all edges targeting this androidTool node
67
+ const incomingEdges = currentWorkflow.edges.filter(edge => edge.target === nodeId);
68
+
69
+ const services: Node[] = [];
70
+ for (const edge of incomingEdges) {
71
+ const sourceNode = currentWorkflow.nodes.find(n => n.id === edge.source);
72
+ if (sourceNode && ANDROID_SERVICE_NODE_TYPES.includes(sourceNode.type || '')) {
73
+ services.push(sourceNode);
74
+ }
75
+ }
76
+ return services;
77
+ }, [currentWorkflow?.edges, currentWorkflow?.nodes, nodeId]);
78
+
79
+ const [selectedServiceId, setSelectedServiceId] = useState<string | null>(null);
80
+ const [localSchema, setLocalSchema] = useState<ToolSchemaConfig>(DEFAULT_SCHEMA);
81
+ const [hasChanges, setHasChanges] = useState(false);
82
+ const [isExpanded, setIsExpanded] = useState(true);
83
+ const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
84
+
85
+ // Auto-select first connected service if none selected
86
+ useEffect(() => {
87
+ if (connectedServices.length > 0 && !selectedServiceId) {
88
+ setSelectedServiceId(connectedServices[0].id);
89
+ } else if (connectedServices.length === 0) {
90
+ setSelectedServiceId(null);
91
+ } else if (selectedServiceId && !connectedServices.find(s => s.id === selectedServiceId)) {
92
+ // Selected service was disconnected, select first available
93
+ setSelectedServiceId(connectedServices[0]?.id || null);
94
+ }
95
+ }, [connectedServices, selectedServiceId]);
96
+
97
+ const selectedService = useMemo(() => {
98
+ if (!selectedServiceId) return null;
99
+ return connectedServices.find(s => s.id === selectedServiceId) || null;
100
+ }, [connectedServices, selectedServiceId]);
101
+
102
+ // Build default schema for selected service
103
+ const getDefaultSchemaForService = useCallback((service: Node | null): ToolSchemaConfig => {
104
+ if (!service) return DEFAULT_SCHEMA;
105
+
106
+ const serviceType = service.type || 'unknown';
107
+ const serviceName = service.data?.label || serviceType;
108
+
109
+ // Create service-specific default schema
110
+ return {
111
+ description: `Control ${serviceName} on Android device`,
112
+ fields: {
113
+ action: {
114
+ type: 'string',
115
+ description: `Action to perform on ${serviceName}`,
116
+ required: true
117
+ },
118
+ parameters: {
119
+ type: 'object',
120
+ description: `Parameters for the ${serviceName} action`,
121
+ required: false
122
+ }
123
+ }
124
+ };
125
+ }, []);
126
+
127
+ // Load existing schema for selected service
128
+ useEffect(() => {
129
+ const loadSchema = async () => {
130
+ if (!isConnected || !selectedServiceId) {
131
+ setLocalSchema(DEFAULT_SCHEMA);
132
+ return;
133
+ }
134
+
135
+ // Use service node ID as the key for storing schema
136
+ const schema = await getToolSchema(selectedServiceId);
137
+ if (schema?.schema_config && Object.keys(schema.schema_config.fields || {}).length > 0) {
138
+ setLocalSchema(schema.schema_config);
139
+ } else {
140
+ // Use service-specific default
141
+ setLocalSchema(getDefaultSchemaForService(selectedService));
142
+ }
143
+ setHasChanges(false);
144
+ };
145
+ loadSchema();
146
+ }, [selectedServiceId, selectedService, isConnected, getToolSchema, getDefaultSchemaForService]);
147
+
148
+ const handleAddField = useCallback(() => {
149
+ const newFieldName = `field_${Object.keys(localSchema.fields).length + 1}`;
150
+ setLocalSchema(prev => ({
151
+ ...prev,
152
+ fields: {
153
+ ...prev.fields,
154
+ [newFieldName]: { type: 'string', description: '', required: false }
155
+ }
156
+ }));
157
+ setHasChanges(true);
158
+ }, [localSchema.fields]);
159
+
160
+ const handleRemoveField = useCallback((fieldName: string) => {
161
+ setLocalSchema(prev => {
162
+ const { [fieldName]: _, ...rest } = prev.fields;
163
+ return { ...prev, fields: rest };
164
+ });
165
+ setHasChanges(true);
166
+ }, []);
167
+
168
+ const handleFieldChange = useCallback((oldName: string, newName: string, config: SchemaFieldConfig) => {
169
+ setLocalSchema(prev => {
170
+ const fields = { ...prev.fields };
171
+ if (oldName !== newName) delete fields[oldName];
172
+ fields[newName] = config;
173
+ return { ...prev, fields };
174
+ });
175
+ setHasChanges(true);
176
+ }, []);
177
+
178
+ const handleSave = useCallback(async () => {
179
+ if (!selectedServiceId || !selectedService) return;
180
+ setSaveStatus('saving');
181
+ const serviceName = selectedService.data?.label || selectedService.type || 'unknown';
182
+ const success = await saveToolSchema(selectedServiceId, serviceName, localSchema.description, localSchema);
183
+ setSaveStatus(success ? 'saved' : 'error');
184
+ if (success) setHasChanges(false);
185
+ setTimeout(() => setSaveStatus('idle'), 2000);
186
+ }, [selectedServiceId, selectedService, localSchema, saveToolSchema]);
187
+
188
+ const handleReset = useCallback(async () => {
189
+ if (!selectedServiceId) return;
190
+ await deleteToolSchema(selectedServiceId);
191
+ setLocalSchema(getDefaultSchemaForService(selectedService));
192
+ setHasChanges(false);
193
+ }, [selectedServiceId, selectedService, deleteToolSchema, getDefaultSchemaForService]);
194
+
195
+ const fieldEntries = Object.entries(localSchema.fields);
196
+
197
+ return (
198
+ <div style={{
199
+ backgroundColor: theme.colors.background,
200
+ border: `1px solid ${theme.colors.border}`,
201
+ borderRadius: theme.borderRadius.md,
202
+ overflow: 'hidden'
203
+ }}>
204
+ {/* Header */}
205
+ <div
206
+ onClick={() => setIsExpanded(!isExpanded)}
207
+ style={{
208
+ display: 'flex',
209
+ justifyContent: 'space-between',
210
+ alignItems: 'center',
211
+ padding: theme.spacing.md,
212
+ backgroundColor: theme.colors.backgroundAlt,
213
+ cursor: 'pointer',
214
+ borderBottom: isExpanded ? `1px solid ${theme.colors.border}` : 'none'
215
+ }}
216
+ >
217
+ <div style={{ display: 'flex', alignItems: 'center', gap: theme.spacing.sm }}>
218
+ <span>{isExpanded ? '▼' : '▶'}</span>
219
+ <span style={{ fontWeight: theme.fontWeight.semibold, color: theme.colors.text }}>
220
+ Connected Services
221
+ </span>
222
+ </div>
223
+ <span style={{ fontSize: theme.fontSize.sm, color: theme.colors.textSecondary }}>
224
+ {connectedServices.length} service(s)
225
+ </span>
226
+ </div>
227
+
228
+ {isExpanded && (
229
+ <div style={{ padding: theme.spacing.md }}>
230
+ {/* Service Selector Dropdown */}
231
+ {connectedServices.length > 0 ? (
232
+ <div style={{ marginBottom: theme.spacing.md }}>
233
+ <label style={{
234
+ display: 'block',
235
+ fontSize: theme.fontSize.sm,
236
+ color: theme.colors.textSecondary,
237
+ marginBottom: theme.spacing.xs
238
+ }}>
239
+ Select Service
240
+ </label>
241
+ <select
242
+ value={selectedServiceId || ''}
243
+ onChange={(e) => setSelectedServiceId(e.target.value)}
244
+ style={{
245
+ width: '100%',
246
+ padding: `${theme.spacing.sm} ${theme.spacing.md}`,
247
+ borderRadius: theme.borderRadius.sm,
248
+ border: `1px solid ${theme.colors.border}`,
249
+ backgroundColor: theme.colors.background,
250
+ color: theme.colors.text,
251
+ fontSize: theme.fontSize.sm,
252
+ cursor: 'pointer'
253
+ }}
254
+ >
255
+ {connectedServices.map(service => (
256
+ <option key={service.id} value={service.id}>
257
+ {service.data?.label || service.type}
258
+ </option>
259
+ ))}
260
+ </select>
261
+ {selectedService && (
262
+ <div style={{
263
+ marginTop: theme.spacing.xs,
264
+ fontSize: theme.fontSize.xs,
265
+ color: theme.colors.textMuted
266
+ }}>
267
+ Type: {selectedService.type}
268
+ </div>
269
+ )}
270
+ </div>
271
+ ) : (
272
+ <div style={{
273
+ padding: theme.spacing.sm,
274
+ backgroundColor: `${theme.dracula.orange}10`,
275
+ borderRadius: theme.borderRadius.sm,
276
+ border: `1px solid ${theme.dracula.orange}30`,
277
+ fontSize: theme.fontSize.sm,
278
+ color: theme.dracula.orange,
279
+ marginBottom: theme.spacing.md
280
+ }}>
281
+ Connect Android nodes to the input handle
282
+ </div>
283
+ )}
284
+
285
+ {/* Schema Fields */}
286
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: theme.spacing.sm }}>
287
+ <label style={{ fontSize: theme.fontSize.sm, color: theme.colors.textSecondary }}>
288
+ Schema Fields
289
+ </label>
290
+ <button
291
+ onClick={handleAddField}
292
+ style={{
293
+ padding: `2px ${theme.spacing.sm}`,
294
+ backgroundColor: `${theme.dracula.cyan}20`,
295
+ color: theme.dracula.cyan,
296
+ border: `1px solid ${theme.dracula.cyan}40`,
297
+ borderRadius: theme.borderRadius.sm,
298
+ cursor: 'pointer',
299
+ fontSize: theme.fontSize.xs
300
+ }}
301
+ >
302
+ + Add
303
+ </button>
304
+ </div>
305
+
306
+ <div style={{ display: 'flex', flexDirection: 'column', gap: theme.spacing.xs }}>
307
+ {fieldEntries.map(([name, config]) => (
308
+ <FieldRow
309
+ key={name}
310
+ fieldName={name}
311
+ config={config}
312
+ onChange={(newName, newConfig) => handleFieldChange(name, newName, newConfig)}
313
+ onRemove={() => handleRemoveField(name)}
314
+ theme={theme}
315
+ />
316
+ ))}
317
+ </div>
318
+
319
+ {/* Save/Reset */}
320
+ {hasChanges && (
321
+ <div style={{ display: 'flex', justifyContent: 'flex-end', gap: theme.spacing.sm, marginTop: theme.spacing.md }}>
322
+ <button
323
+ onClick={handleReset}
324
+ style={{
325
+ padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
326
+ backgroundColor: 'transparent',
327
+ color: theme.colors.textSecondary,
328
+ border: `1px solid ${theme.colors.border}`,
329
+ borderRadius: theme.borderRadius.sm,
330
+ cursor: 'pointer',
331
+ fontSize: theme.fontSize.sm
332
+ }}
333
+ >
334
+ Reset
335
+ </button>
336
+ <button
337
+ onClick={handleSave}
338
+ disabled={isLoading}
339
+ style={{
340
+ padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
341
+ backgroundColor: `${theme.dracula.green}20`,
342
+ color: theme.dracula.green,
343
+ border: `1px solid ${theme.dracula.green}40`,
344
+ borderRadius: theme.borderRadius.sm,
345
+ cursor: 'pointer',
346
+ fontSize: theme.fontSize.sm
347
+ }}
348
+ >
349
+ {saveStatus === 'saving' ? 'Saving...' : saveStatus === 'saved' ? 'Saved!' : 'Save'}
350
+ </button>
351
+ </div>
352
+ )}
353
+ </div>
354
+ )}
355
+ </div>
356
+ );
357
+ };
358
+
359
+ // Field Row Component
360
+ const FieldRow: React.FC<{
361
+ fieldName: string;
362
+ config: SchemaFieldConfig;
363
+ onChange: (newName: string, newConfig: SchemaFieldConfig) => void;
364
+ onRemove: () => void;
365
+ theme: any;
366
+ }> = ({ fieldName, config, onChange, onRemove, theme }) => {
367
+ const [localName, setLocalName] = useState(fieldName);
368
+
369
+ useEffect(() => {
370
+ setLocalName(fieldName);
371
+ }, [fieldName]);
372
+
373
+ return (
374
+ <div style={{
375
+ padding: theme.spacing.sm,
376
+ backgroundColor: theme.colors.backgroundAlt,
377
+ borderRadius: theme.borderRadius.sm,
378
+ border: `1px solid ${theme.colors.border}`
379
+ }}>
380
+ <div style={{ display: 'flex', gap: theme.spacing.xs, alignItems: 'center', marginBottom: '4px' }}>
381
+ <input
382
+ value={localName}
383
+ onChange={(e) => setLocalName(e.target.value.replace(/\s/g, '_'))}
384
+ onBlur={() => localName !== fieldName && onChange(localName, config)}
385
+ style={{
386
+ flex: 1,
387
+ padding: '4px 8px',
388
+ borderRadius: theme.borderRadius.sm,
389
+ border: `1px solid ${theme.colors.border}`,
390
+ backgroundColor: theme.colors.background,
391
+ color: theme.colors.text,
392
+ fontSize: theme.fontSize.sm
393
+ }}
394
+ />
395
+ <select
396
+ value={config.type}
397
+ onChange={(e) => onChange(fieldName, { ...config, type: e.target.value as any })}
398
+ style={{
399
+ padding: '4px',
400
+ borderRadius: theme.borderRadius.sm,
401
+ border: `1px solid ${theme.colors.border}`,
402
+ backgroundColor: theme.colors.background,
403
+ color: theme.colors.text,
404
+ fontSize: theme.fontSize.xs
405
+ }}
406
+ >
407
+ {FIELD_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
408
+ </select>
409
+ <label style={{ display: 'flex', alignItems: 'center', gap: '2px', fontSize: theme.fontSize.xs, color: theme.colors.textSecondary }}>
410
+ <input type="checkbox" checked={config.required || false} onChange={(e) => onChange(fieldName, { ...config, required: e.target.checked })} />
411
+ Req
412
+ </label>
413
+ <button onClick={onRemove} style={{ padding: '2px 6px', backgroundColor: `${theme.dracula.red}20`, color: theme.dracula.red, border: 'none', borderRadius: theme.borderRadius.sm, cursor: 'pointer', fontSize: theme.fontSize.xs }}>
414
+ X
415
+ </button>
416
+ </div>
417
+ <input
418
+ value={config.description}
419
+ onChange={(e) => onChange(fieldName, { ...config, description: e.target.value })}
420
+ placeholder="Description..."
421
+ style={{
422
+ width: '100%',
423
+ padding: '4px 8px',
424
+ borderRadius: theme.borderRadius.sm,
425
+ border: `1px solid ${theme.colors.border}`,
426
+ backgroundColor: theme.colors.background,
427
+ color: theme.colors.text,
428
+ fontSize: theme.fontSize.xs,
429
+ boxSizing: 'border-box'
430
+ }}
431
+ />
432
+ </div>
433
+ );
434
+ };
435
+
436
+ export default ToolSchemaEditor;
@@ -0,0 +1,42 @@
1
+ // Modular Parameter Panel Components
2
+ export { default as InputSection } from './InputSection';
3
+ export { default as MiddleSection } from './MiddleSection';
4
+ export { default as OutputSection } from './OutputSection';
5
+ export { default as MapsSection } from './MapsSection';
6
+ export { default as ParameterPanelLayout } from './ParameterPanelLayout';
7
+ export { default as LocationPanelLayout } from './LocationPanelLayout';
8
+
9
+ // Types defined inline for convenience
10
+
11
+ // Export interface definitions
12
+ export interface InputSectionProps {
13
+ nodeId: string;
14
+ visible?: boolean;
15
+ }
16
+
17
+ export interface MiddleSectionProps {
18
+ nodeDefinition: any;
19
+ parameters: Record<string, any>;
20
+ onParameterChange: (paramName: string, value: any) => void;
21
+ }
22
+
23
+ export interface OutputSectionProps {
24
+ selectedNode: any;
25
+ executionResults: any[];
26
+ onClearResults: () => void;
27
+ visible?: boolean;
28
+ }
29
+
30
+ export interface ParameterPanelLayoutProps {
31
+ selectedNode: any;
32
+ nodeDefinition: any;
33
+ parameters: Record<string, any>;
34
+ hasUnsavedChanges: boolean;
35
+ onParameterChange: (paramName: string, value: any) => void;
36
+ onSave: () => void;
37
+ onCancel: () => void;
38
+ executionResults: any[];
39
+ onClearResults: () => void;
40
+ showInputSection?: boolean;
41
+ showOutputSection?: boolean;
42
+ }
@@ -0,0 +1,142 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { Card, Space, Empty, Typography } from 'antd';
3
+ import { useAppTheme } from '../../hooks/useAppTheme';
4
+
5
+ const { Text } = Typography;
6
+
7
+ // Compound Component Pattern
8
+ interface DataPanelProps {
9
+ children: ReactNode;
10
+ }
11
+
12
+ interface HeaderProps {
13
+ title: ReactNode;
14
+ extra?: ReactNode;
15
+ children?: ReactNode;
16
+ }
17
+
18
+ interface ContentProps {
19
+ children: ReactNode;
20
+ }
21
+
22
+ interface FooterProps {
23
+ children: ReactNode;
24
+ }
25
+
26
+ interface EmptyStateProps {
27
+ icon: ReactNode;
28
+ title: string;
29
+ description: string;
30
+ }
31
+
32
+ const DataPanelRoot: React.FC<DataPanelProps> = ({ children }) => {
33
+ const theme = useAppTheme();
34
+ return (
35
+ <div style={{
36
+ width: '350px',
37
+ height: '100%',
38
+ backgroundColor: theme.colors.backgroundPanel,
39
+ borderLeft: `1px solid ${theme.colors.border}`,
40
+ display: 'flex',
41
+ flexDirection: 'column'
42
+ }}>
43
+ {children}
44
+ </div>
45
+ );
46
+ };
47
+
48
+ const DataPanelHeader: React.FC<HeaderProps> = ({ title, extra, children }) => {
49
+ const theme = useAppTheme();
50
+ return (
51
+ <Card
52
+ size="small"
53
+ title={title}
54
+ extra={extra}
55
+ headStyle={{
56
+ borderBottom: `1px solid ${theme.colors.border}`,
57
+ padding: 8,
58
+ backgroundColor: theme.colors.backgroundPanel
59
+ }}
60
+ bodyStyle={{ padding: children ? 8 : 0 }}
61
+ style={{
62
+ borderRadius: 0,
63
+ border: 'none',
64
+ borderBottom: `1px solid ${theme.colors.border}`,
65
+ backgroundColor: theme.colors.backgroundPanel
66
+ }}
67
+ >
68
+ {children}
69
+ </Card>
70
+ );
71
+ };
72
+
73
+ const DataPanelContent: React.FC<ContentProps> = ({ children }) => (
74
+ <div style={{
75
+ flex: 1,
76
+ overflowY: 'auto',
77
+ padding: '8px'
78
+ }}>
79
+ {children}
80
+ </div>
81
+ );
82
+
83
+ const DataPanelFooter: React.FC<FooterProps> = ({ children }) => {
84
+ const theme = useAppTheme();
85
+ return (
86
+ <Card
87
+ size="small"
88
+ bodyStyle={{ padding: 8, textAlign: 'center' }}
89
+ style={{
90
+ borderRadius: 0,
91
+ border: 'none',
92
+ borderTop: `1px solid ${theme.colors.border}`,
93
+ backgroundColor: theme.colors.backgroundPanel
94
+ }}
95
+ >
96
+ {children}
97
+ </Card>
98
+ );
99
+ };
100
+
101
+ const DataPanelEmpty: React.FC<EmptyStateProps> = ({ icon, title, description }) => {
102
+ const theme = useAppTheme();
103
+ return (
104
+ <div style={{
105
+ width: '350px',
106
+ height: '100%',
107
+ backgroundColor: theme.colors.backgroundPanel,
108
+ borderLeft: `1px solid ${theme.colors.border}`,
109
+ display: 'flex',
110
+ flexDirection: 'column'
111
+ }}>
112
+ <Empty
113
+ image={icon}
114
+ description={
115
+ <Space direction="vertical" size="small">
116
+ <Text style={{ color: theme.colors.textSecondary }}>{title}</Text>
117
+ <Text style={{ fontSize: 12, color: theme.colors.textSecondary }}>
118
+ {description}
119
+ </Text>
120
+ </Space>
121
+ }
122
+ style={{
123
+ height: '100%',
124
+ display: 'flex',
125
+ flexDirection: 'column',
126
+ justifyContent: 'center'
127
+ }}
128
+ />
129
+ </div>
130
+ );
131
+ };
132
+
133
+ // Compound Component Export
134
+ const DataPanel = {
135
+ Root: DataPanelRoot,
136
+ Header: DataPanelHeader,
137
+ Content: DataPanelContent,
138
+ Footer: DataPanelFooter,
139
+ Empty: DataPanelEmpty
140
+ };
141
+
142
+ export default DataPanel;