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.
- package/.env.template +71 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/bin/cli.js +159 -0
- package/client/.dockerignore +45 -0
- package/client/Dockerfile +68 -0
- package/client/eslint.config.js +29 -0
- package/client/index.html +13 -0
- package/client/nginx.conf +66 -0
- package/client/package.json +48 -0
- package/client/src/App.tsx +27 -0
- package/client/src/Dashboard.tsx +1173 -0
- package/client/src/ParameterPanel.tsx +301 -0
- package/client/src/components/AIAgentNode.tsx +321 -0
- package/client/src/components/APIKeyValidator.tsx +118 -0
- package/client/src/components/ClaudeChatModelNode.tsx +18 -0
- package/client/src/components/ConditionalEdge.tsx +189 -0
- package/client/src/components/CredentialsModal.tsx +306 -0
- package/client/src/components/EdgeConditionEditor.tsx +443 -0
- package/client/src/components/GeminiChatModelNode.tsx +18 -0
- package/client/src/components/GenericNode.tsx +357 -0
- package/client/src/components/LocationParameterPanel.tsx +154 -0
- package/client/src/components/ModelNode.tsx +286 -0
- package/client/src/components/OpenAIChatModelNode.tsx +18 -0
- package/client/src/components/OutputPanel.tsx +471 -0
- package/client/src/components/ParameterRenderer.tsx +1874 -0
- package/client/src/components/SkillEditorModal.tsx +417 -0
- package/client/src/components/SquareNode.tsx +797 -0
- package/client/src/components/StartNode.tsx +250 -0
- package/client/src/components/ToolkitNode.tsx +365 -0
- package/client/src/components/TriggerNode.tsx +463 -0
- package/client/src/components/auth/LoginPage.tsx +247 -0
- package/client/src/components/auth/ProtectedRoute.tsx +59 -0
- package/client/src/components/base/BaseChatModelNode.tsx +271 -0
- package/client/src/components/icons/AIProviderIcons.tsx +50 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
- package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
- package/client/src/components/maps/index.ts +26 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
- package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
- package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
- package/client/src/components/parameterPanel/index.ts +42 -0
- package/client/src/components/shared/DataPanel.tsx +142 -0
- package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
- package/client/src/components/ui/AIResultModal.tsx +204 -0
- package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -0
- package/client/src/components/ui/CollapsibleSection.tsx +88 -0
- package/client/src/components/ui/ComponentItem.tsx +154 -0
- package/client/src/components/ui/ComponentPalette.tsx +321 -0
- package/client/src/components/ui/ConsolePanel.tsx +1074 -0
- package/client/src/components/ui/ErrorBoundary.tsx +196 -0
- package/client/src/components/ui/InputNodesPanel.tsx +204 -0
- package/client/src/components/ui/MapSelector.tsx +314 -0
- package/client/src/components/ui/Modal.tsx +149 -0
- package/client/src/components/ui/NodeContextMenu.tsx +192 -0
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
- package/client/src/components/ui/SettingsPanel.tsx +243 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -0
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
- package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
- package/client/src/config/antdTheme.ts +186 -0
- package/client/src/config/api.ts +54 -0
- package/client/src/contexts/AuthContext.tsx +221 -0
- package/client/src/contexts/ThemeContext.tsx +42 -0
- package/client/src/contexts/WebSocketContext.tsx +1971 -0
- package/client/src/factories/baseChatModelFactory.ts +256 -0
- package/client/src/hooks/useAndroidOperations.ts +164 -0
- package/client/src/hooks/useApiKeyValidation.ts +107 -0
- package/client/src/hooks/useApiKeys.ts +238 -0
- package/client/src/hooks/useAppTheme.ts +17 -0
- package/client/src/hooks/useComponentPalette.ts +51 -0
- package/client/src/hooks/useCopyPaste.ts +155 -0
- package/client/src/hooks/useDragAndDrop.ts +124 -0
- package/client/src/hooks/useDragVariable.ts +88 -0
- package/client/src/hooks/useExecution.ts +313 -0
- package/client/src/hooks/useParameterPanel.ts +176 -0
- package/client/src/hooks/useReactFlowNodes.ts +189 -0
- package/client/src/hooks/useToolSchema.ts +209 -0
- package/client/src/hooks/useWhatsApp.ts +196 -0
- package/client/src/hooks/useWorkflowManagement.ts +46 -0
- package/client/src/index.css +315 -0
- package/client/src/main.tsx +19 -0
- package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
- package/client/src/nodeDefinitions/chatNodes.ts +135 -0
- package/client/src/nodeDefinitions/codeNodes.ts +54 -0
- package/client/src/nodeDefinitions/documentNodes.ts +379 -0
- package/client/src/nodeDefinitions/index.ts +15 -0
- package/client/src/nodeDefinitions/locationNodes.ts +463 -0
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
- package/client/src/nodeDefinitions/skillNodes.ts +211 -0
- package/client/src/nodeDefinitions/toolNodes.ts +198 -0
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
- package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
- package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
- package/client/src/nodeDefinitions.ts +104 -0
- package/client/src/schemas/workflowSchema.ts +264 -0
- package/client/src/services/dynamicParameterService.ts +96 -0
- package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
- package/client/src/services/executionService.ts +232 -0
- package/client/src/services/workflowApi.ts +91 -0
- package/client/src/store/useAppStore.ts +582 -0
- package/client/src/styles/theme.ts +508 -0
- package/client/src/styles/zIndex.ts +17 -0
- package/client/src/types/ComponentTypes.ts +39 -0
- package/client/src/types/EdgeCondition.ts +231 -0
- package/client/src/types/INodeProperties.ts +288 -0
- package/client/src/types/NodeTypes.ts +28 -0
- package/client/src/utils/formatters.ts +33 -0
- package/client/src/utils/googleMapsLoader.ts +140 -0
- package/client/src/utils/locationUtils.ts +85 -0
- package/client/src/utils/nodeUtils.ts +31 -0
- package/client/src/utils/workflow.ts +30 -0
- package/client/src/utils/workflowExport.ts +120 -0
- package/client/src/vite-env.d.ts +12 -0
- package/client/tailwind.config.js +60 -0
- package/client/tsconfig.json +25 -0
- package/client/tsconfig.node.json +11 -0
- package/client/vite.config.js +35 -0
- package/docker-compose.prod.yml +107 -0
- package/docker-compose.yml +104 -0
- package/docs-MachinaOs/README.md +85 -0
- package/docs-MachinaOs/deployment/docker.mdx +228 -0
- package/docs-MachinaOs/deployment/production.mdx +345 -0
- package/docs-MachinaOs/docs.json +75 -0
- package/docs-MachinaOs/faq.mdx +309 -0
- package/docs-MachinaOs/favicon.svg +5 -0
- package/docs-MachinaOs/installation.mdx +160 -0
- package/docs-MachinaOs/introduction.mdx +114 -0
- package/docs-MachinaOs/logo/dark.svg +6 -0
- package/docs-MachinaOs/logo/light.svg +6 -0
- package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
- package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
- package/docs-MachinaOs/nodes/android.mdx +411 -0
- package/docs-MachinaOs/nodes/overview.mdx +181 -0
- package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
- package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
- package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
- package/docs-MachinaOs/quickstart.mdx +119 -0
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
- package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
- package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
- package/nul +0 -0
- package/package.json +70 -0
- package/scripts/build.js +158 -0
- package/scripts/check-ports.ps1 +33 -0
- package/scripts/clean.js +40 -0
- package/scripts/docker.js +93 -0
- package/scripts/kill-port.ps1 +154 -0
- package/scripts/start.js +210 -0
- package/scripts/stop.js +325 -0
- package/server/.dockerignore +44 -0
- package/server/Dockerfile +45 -0
- package/server/constants.py +249 -0
- package/server/core/__init__.py +1 -0
- package/server/core/cache.py +461 -0
- package/server/core/config.py +128 -0
- package/server/core/container.py +99 -0
- package/server/core/database.py +1211 -0
- package/server/core/logging.py +314 -0
- package/server/main.py +289 -0
- package/server/middleware/__init__.py +5 -0
- package/server/middleware/auth.py +89 -0
- package/server/models/__init__.py +1 -0
- package/server/models/auth.py +52 -0
- package/server/models/cache.py +24 -0
- package/server/models/database.py +211 -0
- package/server/models/nodes.py +455 -0
- package/server/package.json +9 -0
- package/server/pyproject.toml +72 -0
- package/server/requirements.txt +83 -0
- package/server/routers/__init__.py +1 -0
- package/server/routers/android.py +294 -0
- package/server/routers/auth.py +203 -0
- package/server/routers/database.py +151 -0
- package/server/routers/maps.py +142 -0
- package/server/routers/nodejs_compat.py +289 -0
- package/server/routers/webhook.py +90 -0
- package/server/routers/websocket.py +2127 -0
- package/server/routers/whatsapp.py +761 -0
- package/server/routers/workflow.py +200 -0
- package/server/services/__init__.py +1 -0
- package/server/services/ai.py +2415 -0
- package/server/services/android/__init__.py +27 -0
- package/server/services/android/broadcaster.py +114 -0
- package/server/services/android/client.py +608 -0
- package/server/services/android/manager.py +78 -0
- package/server/services/android/protocol.py +165 -0
- package/server/services/android_service.py +588 -0
- package/server/services/auth.py +131 -0
- package/server/services/chat_client.py +160 -0
- package/server/services/deployment/__init__.py +12 -0
- package/server/services/deployment/manager.py +706 -0
- package/server/services/deployment/state.py +47 -0
- package/server/services/deployment/triggers.py +275 -0
- package/server/services/event_waiter.py +785 -0
- package/server/services/execution/__init__.py +77 -0
- package/server/services/execution/cache.py +769 -0
- package/server/services/execution/conditions.py +373 -0
- package/server/services/execution/dlq.py +132 -0
- package/server/services/execution/executor.py +1351 -0
- package/server/services/execution/models.py +531 -0
- package/server/services/execution/recovery.py +235 -0
- package/server/services/handlers/__init__.py +126 -0
- package/server/services/handlers/ai.py +355 -0
- package/server/services/handlers/android.py +260 -0
- package/server/services/handlers/code.py +278 -0
- package/server/services/handlers/document.py +598 -0
- package/server/services/handlers/http.py +193 -0
- package/server/services/handlers/polyglot.py +105 -0
- package/server/services/handlers/tools.py +845 -0
- package/server/services/handlers/triggers.py +107 -0
- package/server/services/handlers/utility.py +822 -0
- package/server/services/handlers/whatsapp.py +476 -0
- package/server/services/maps.py +289 -0
- package/server/services/memory_store.py +103 -0
- package/server/services/node_executor.py +375 -0
- package/server/services/parameter_resolver.py +218 -0
- package/server/services/polyglot_client.py +169 -0
- package/server/services/scheduler.py +155 -0
- package/server/services/skill_loader.py +417 -0
- package/server/services/status_broadcaster.py +826 -0
- package/server/services/temporal/__init__.py +23 -0
- package/server/services/temporal/activities.py +344 -0
- package/server/services/temporal/client.py +76 -0
- package/server/services/temporal/executor.py +147 -0
- package/server/services/temporal/worker.py +251 -0
- package/server/services/temporal/workflow.py +355 -0
- package/server/services/temporal/ws_client.py +236 -0
- package/server/services/text.py +111 -0
- package/server/services/user_auth.py +172 -0
- package/server/services/websocket_client.py +29 -0
- package/server/services/workflow.py +597 -0
- package/server/skills/android-skill/SKILL.md +82 -0
- package/server/skills/assistant-personality/SKILL.md +45 -0
- package/server/skills/code-skill/SKILL.md +140 -0
- package/server/skills/http-skill/SKILL.md +161 -0
- package/server/skills/maps-skill/SKILL.md +170 -0
- package/server/skills/memory-skill/SKILL.md +154 -0
- package/server/skills/scheduler-skill/SKILL.md +84 -0
- package/server/skills/whatsapp-skill/SKILL.md +283 -0
- package/server/uv.lock +2916 -0
- package/server/whatsapp-rpc/.dockerignore +30 -0
- package/server/whatsapp-rpc/Dockerfile +44 -0
- package/server/whatsapp-rpc/Dockerfile.web +17 -0
- package/server/whatsapp-rpc/README.md +139 -0
- package/server/whatsapp-rpc/cli.js +95 -0
- package/server/whatsapp-rpc/configs/config.yaml +7 -0
- package/server/whatsapp-rpc/docker-compose.yml +35 -0
- package/server/whatsapp-rpc/docs/API.md +410 -0
- package/server/whatsapp-rpc/go.mod +67 -0
- package/server/whatsapp-rpc/go.sum +203 -0
- package/server/whatsapp-rpc/package.json +30 -0
- package/server/whatsapp-rpc/schema.json +1294 -0
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
- package/server/whatsapp-rpc/scripts/cli.js +162 -0
- package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
- package/server/whatsapp-rpc/src/go/config/config.go +49 -0
- package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
- package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
- package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
- package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
- package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
- package/server/whatsapp-rpc/web/app.py +609 -0
- package/server/whatsapp-rpc/web/requirements.txt +6 -0
- package/server/whatsapp-rpc/web/rpc_client.py +427 -0
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
- package/server/whatsapp-rpc/web/templates/base.html +150 -0
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
- package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
- package/server/whatsapp-rpc/web/templates/groups.html +328 -0
- package/server/whatsapp-rpc/web/templates/messages.html +465 -0
- package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
- package/server/whatsapp-rpc/web/templates/send.html +259 -0
- package/server/whatsapp-rpc/web/templates/settings.html +459 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Polyglot Server Client - HTTP client for polyglot-server plugin registry."""
|
|
2
|
+
|
|
3
|
+
import aiohttp
|
|
4
|
+
from typing import Dict, Any, List, Optional
|
|
5
|
+
|
|
6
|
+
from core.logging import get_logger
|
|
7
|
+
|
|
8
|
+
logger = get_logger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PolyglotClient:
|
|
12
|
+
"""Client for polyglot-server plugin registry.
|
|
13
|
+
|
|
14
|
+
Provides async HTTP methods to interact with the polyglot-server
|
|
15
|
+
for plugin discovery, schema retrieval, and execution.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, base_url: str = "http://localhost:8080"):
|
|
19
|
+
self.base_url = base_url.rstrip("/")
|
|
20
|
+
self._session: Optional[aiohttp.ClientSession] = None
|
|
21
|
+
|
|
22
|
+
async def initialize(self) -> None:
|
|
23
|
+
"""Initialize the HTTP session."""
|
|
24
|
+
if self._session is None or self._session.closed:
|
|
25
|
+
timeout = aiohttp.ClientTimeout(total=30)
|
|
26
|
+
self._session = aiohttp.ClientSession(timeout=timeout)
|
|
27
|
+
logger.info("PolyglotClient initialized", base_url=self.base_url)
|
|
28
|
+
|
|
29
|
+
async def close(self) -> None:
|
|
30
|
+
"""Close the HTTP session."""
|
|
31
|
+
if self._session and not self._session.closed:
|
|
32
|
+
await self._session.close()
|
|
33
|
+
self._session = None
|
|
34
|
+
logger.info("PolyglotClient closed")
|
|
35
|
+
|
|
36
|
+
async def _ensure_session(self) -> aiohttp.ClientSession:
|
|
37
|
+
"""Ensure session is initialized and return it."""
|
|
38
|
+
if self._session is None or self._session.closed:
|
|
39
|
+
await self.initialize()
|
|
40
|
+
return self._session
|
|
41
|
+
|
|
42
|
+
async def health_check(self) -> Dict[str, Any]:
|
|
43
|
+
"""Check polyglot-server health status."""
|
|
44
|
+
session = await self._ensure_session()
|
|
45
|
+
try:
|
|
46
|
+
async with session.get(f"{self.base_url}/api/health") as resp:
|
|
47
|
+
if resp.status == 200:
|
|
48
|
+
return await resp.json()
|
|
49
|
+
return {"status": "error", "code": resp.status}
|
|
50
|
+
except aiohttp.ClientError as e:
|
|
51
|
+
logger.warning("Polyglot health check failed", error=str(e))
|
|
52
|
+
return {"status": "unreachable", "error": str(e)}
|
|
53
|
+
|
|
54
|
+
async def list_plugins(self) -> List[Dict[str, Any]]:
|
|
55
|
+
"""List all available plugins from polyglot-server."""
|
|
56
|
+
session = await self._ensure_session()
|
|
57
|
+
try:
|
|
58
|
+
async with session.get(f"{self.base_url}/api/plugins") as resp:
|
|
59
|
+
if resp.status == 200:
|
|
60
|
+
data = await resp.json()
|
|
61
|
+
return data.get("plugins", [])
|
|
62
|
+
logger.warning("Failed to list plugins", status=resp.status)
|
|
63
|
+
return []
|
|
64
|
+
except aiohttp.ClientError as e:
|
|
65
|
+
logger.error("Failed to list plugins", error=str(e))
|
|
66
|
+
return []
|
|
67
|
+
|
|
68
|
+
async def get_plugin(self, name: str) -> Optional[Dict[str, Any]]:
|
|
69
|
+
"""Get details for a specific plugin."""
|
|
70
|
+
session = await self._ensure_session()
|
|
71
|
+
try:
|
|
72
|
+
async with session.get(f"{self.base_url}/api/plugins/{name}") as resp:
|
|
73
|
+
if resp.status == 200:
|
|
74
|
+
return await resp.json()
|
|
75
|
+
return None
|
|
76
|
+
except aiohttp.ClientError as e:
|
|
77
|
+
logger.error("Failed to get plugin", plugin=name, error=str(e))
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
async def get_schema(self, plugin_name: str) -> Optional[Dict[str, Any]]:
|
|
81
|
+
"""Get plugin input/output schema for workflow node integration."""
|
|
82
|
+
session = await self._ensure_session()
|
|
83
|
+
try:
|
|
84
|
+
async with session.get(
|
|
85
|
+
f"{self.base_url}/api/plugins/{plugin_name}/schema"
|
|
86
|
+
) as resp:
|
|
87
|
+
if resp.status == 200:
|
|
88
|
+
return await resp.json()
|
|
89
|
+
return None
|
|
90
|
+
except aiohttp.ClientError as e:
|
|
91
|
+
logger.error("Failed to get schema", plugin=plugin_name, error=str(e))
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
async def execute(
|
|
95
|
+
self,
|
|
96
|
+
plugin_name: str,
|
|
97
|
+
action: str = "default",
|
|
98
|
+
params: Optional[Dict[str, Any]] = None,
|
|
99
|
+
) -> Dict[str, Any]:
|
|
100
|
+
"""Execute a plugin action.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
plugin_name: Name of the plugin to execute
|
|
104
|
+
action: Action to perform (default: "default")
|
|
105
|
+
params: Optional parameters for the action
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Execution result with success status and result/error
|
|
109
|
+
"""
|
|
110
|
+
session = await self._ensure_session()
|
|
111
|
+
payload = {
|
|
112
|
+
"action": action,
|
|
113
|
+
"params": params or {},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
async with session.post(
|
|
118
|
+
f"{self.base_url}/api/plugins/{plugin_name}/execute",
|
|
119
|
+
json=payload,
|
|
120
|
+
) as resp:
|
|
121
|
+
result = await resp.json()
|
|
122
|
+
if resp.status == 200:
|
|
123
|
+
return result
|
|
124
|
+
return {
|
|
125
|
+
"success": False,
|
|
126
|
+
"error": result.get("error", f"HTTP {resp.status}"),
|
|
127
|
+
}
|
|
128
|
+
except aiohttp.ClientError as e:
|
|
129
|
+
logger.error(
|
|
130
|
+
"Plugin execution failed",
|
|
131
|
+
plugin=plugin_name,
|
|
132
|
+
action=action,
|
|
133
|
+
error=str(e),
|
|
134
|
+
)
|
|
135
|
+
return {"success": False, "error": str(e)}
|
|
136
|
+
|
|
137
|
+
async def list_categories(self) -> List[str]:
|
|
138
|
+
"""List available plugin categories."""
|
|
139
|
+
session = await self._ensure_session()
|
|
140
|
+
try:
|
|
141
|
+
async with session.get(f"{self.base_url}/api/categories") as resp:
|
|
142
|
+
if resp.status == 200:
|
|
143
|
+
data = await resp.json()
|
|
144
|
+
return data.get("categories", [])
|
|
145
|
+
return []
|
|
146
|
+
except aiohttp.ClientError as e:
|
|
147
|
+
logger.error("Failed to list categories", error=str(e))
|
|
148
|
+
return []
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# Global client instance for simple usage
|
|
152
|
+
_client: Optional[PolyglotClient] = None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
async def get_polyglot_client(base_url: str = "http://localhost:8080") -> PolyglotClient:
|
|
156
|
+
"""Get or create the global polyglot client instance."""
|
|
157
|
+
global _client
|
|
158
|
+
if _client is None:
|
|
159
|
+
_client = PolyglotClient(base_url)
|
|
160
|
+
await _client.initialize()
|
|
161
|
+
return _client
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
async def close_polyglot_client() -> None:
|
|
165
|
+
"""Close the global polyglot client instance."""
|
|
166
|
+
global _client
|
|
167
|
+
if _client is not None:
|
|
168
|
+
await _client.close()
|
|
169
|
+
_client = None
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cron Scheduler Service using APScheduler.
|
|
3
|
+
Manages scheduled jobs for workflow automation.
|
|
4
|
+
"""
|
|
5
|
+
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
6
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
7
|
+
from apscheduler.jobstores.base import JobLookupError
|
|
8
|
+
from typing import Callable, Dict, Optional
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
_scheduler: Optional[AsyncIOScheduler] = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_scheduler() -> AsyncIOScheduler:
|
|
17
|
+
"""Get or create the singleton scheduler instance."""
|
|
18
|
+
global _scheduler
|
|
19
|
+
if _scheduler is None:
|
|
20
|
+
_scheduler = AsyncIOScheduler(timezone="UTC")
|
|
21
|
+
return _scheduler
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def start_scheduler():
|
|
25
|
+
"""Start the scheduler if not already running."""
|
|
26
|
+
scheduler = get_scheduler()
|
|
27
|
+
if not scheduler.running:
|
|
28
|
+
scheduler.start()
|
|
29
|
+
logger.info("[Scheduler] Started")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def shutdown_scheduler():
|
|
33
|
+
"""Shutdown the scheduler gracefully."""
|
|
34
|
+
scheduler = get_scheduler()
|
|
35
|
+
if scheduler.running:
|
|
36
|
+
scheduler.shutdown(wait=False)
|
|
37
|
+
logger.info("[Scheduler] Shutdown")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def register_cron_job(
|
|
41
|
+
job_id: str,
|
|
42
|
+
cron_expression: str,
|
|
43
|
+
callback: Callable,
|
|
44
|
+
timezone: str = "UTC",
|
|
45
|
+
**kwargs
|
|
46
|
+
) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Register a cron job with the scheduler.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
job_id: Unique identifier for the job
|
|
52
|
+
cron_expression: 6-field cron expression (second minute hour day month weekday)
|
|
53
|
+
or 5-field (minute hour day month weekday)
|
|
54
|
+
callback: Async function to call when job fires
|
|
55
|
+
timezone: Timezone for schedule (default: UTC)
|
|
56
|
+
**kwargs: Additional arguments passed to the callback
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The job_id
|
|
60
|
+
"""
|
|
61
|
+
scheduler = get_scheduler()
|
|
62
|
+
|
|
63
|
+
# Parse cron expression - support both 5-field and 6-field formats
|
|
64
|
+
parts = cron_expression.split()
|
|
65
|
+
|
|
66
|
+
if len(parts) >= 6:
|
|
67
|
+
# 6-field format: second minute hour day month weekday
|
|
68
|
+
trigger = CronTrigger(
|
|
69
|
+
second=parts[0],
|
|
70
|
+
minute=parts[1],
|
|
71
|
+
hour=parts[2],
|
|
72
|
+
day=parts[3],
|
|
73
|
+
month=parts[4],
|
|
74
|
+
day_of_week=parts[5],
|
|
75
|
+
timezone=timezone
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
# 5-field format: minute hour day month weekday (default second=0)
|
|
79
|
+
if len(parts) < 5:
|
|
80
|
+
parts.extend(['*'] * (5 - len(parts)))
|
|
81
|
+
trigger = CronTrigger(
|
|
82
|
+
second='0',
|
|
83
|
+
minute=parts[0],
|
|
84
|
+
hour=parts[1],
|
|
85
|
+
day=parts[2],
|
|
86
|
+
month=parts[3],
|
|
87
|
+
day_of_week=parts[4],
|
|
88
|
+
timezone=timezone
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
scheduler.add_job(
|
|
92
|
+
callback,
|
|
93
|
+
trigger=trigger,
|
|
94
|
+
id=job_id,
|
|
95
|
+
replace_existing=True,
|
|
96
|
+
kwargs=kwargs
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
logger.info(f"[Scheduler] Registered cron job: {job_id} with expression: {cron_expression}")
|
|
100
|
+
return job_id
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def remove_cron_job(job_id: str) -> bool:
|
|
104
|
+
"""
|
|
105
|
+
Remove a cron job from the scheduler.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
job_id: The job identifier to remove
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
True if job was removed, False if not found
|
|
112
|
+
"""
|
|
113
|
+
scheduler = get_scheduler()
|
|
114
|
+
try:
|
|
115
|
+
scheduler.remove_job(job_id)
|
|
116
|
+
logger.info(f"[Scheduler] Removed cron job: {job_id}")
|
|
117
|
+
return True
|
|
118
|
+
except JobLookupError:
|
|
119
|
+
logger.warning(f"[Scheduler] Job not found: {job_id}")
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_job_info(job_id: str) -> Optional[Dict]:
|
|
124
|
+
"""
|
|
125
|
+
Get information about a scheduled job.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
job_id: The job identifier
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Dict with job info or None if not found
|
|
132
|
+
"""
|
|
133
|
+
scheduler = get_scheduler()
|
|
134
|
+
job = scheduler.get_job(job_id)
|
|
135
|
+
if job:
|
|
136
|
+
return {
|
|
137
|
+
"id": job.id,
|
|
138
|
+
"next_run_time": job.next_run_time.isoformat() if job.next_run_time else None,
|
|
139
|
+
"trigger": str(job.trigger)
|
|
140
|
+
}
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_all_jobs() -> list:
|
|
145
|
+
"""Get list of all scheduled jobs."""
|
|
146
|
+
scheduler = get_scheduler()
|
|
147
|
+
jobs = scheduler.get_jobs()
|
|
148
|
+
return [
|
|
149
|
+
{
|
|
150
|
+
"id": job.id,
|
|
151
|
+
"next_run_time": job.next_run_time.isoformat() if job.next_run_time else None,
|
|
152
|
+
"trigger": str(job.trigger)
|
|
153
|
+
}
|
|
154
|
+
for job in jobs
|
|
155
|
+
]
|