PraisonAI 3.0.0__py3-none-any.whl

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. praisonai/__init__.py +54 -0
  2. praisonai/__main__.py +15 -0
  3. praisonai/acp/__init__.py +54 -0
  4. praisonai/acp/config.py +159 -0
  5. praisonai/acp/server.py +587 -0
  6. praisonai/acp/session.py +219 -0
  7. praisonai/adapters/__init__.py +50 -0
  8. praisonai/adapters/readers.py +395 -0
  9. praisonai/adapters/rerankers.py +315 -0
  10. praisonai/adapters/retrievers.py +394 -0
  11. praisonai/adapters/vector_stores.py +409 -0
  12. praisonai/agent_scheduler.py +337 -0
  13. praisonai/agents_generator.py +903 -0
  14. praisonai/api/call.py +292 -0
  15. praisonai/auto.py +1197 -0
  16. praisonai/capabilities/__init__.py +275 -0
  17. praisonai/capabilities/a2a.py +140 -0
  18. praisonai/capabilities/assistants.py +283 -0
  19. praisonai/capabilities/audio.py +320 -0
  20. praisonai/capabilities/batches.py +469 -0
  21. praisonai/capabilities/completions.py +336 -0
  22. praisonai/capabilities/container_files.py +155 -0
  23. praisonai/capabilities/containers.py +93 -0
  24. praisonai/capabilities/embeddings.py +158 -0
  25. praisonai/capabilities/files.py +467 -0
  26. praisonai/capabilities/fine_tuning.py +293 -0
  27. praisonai/capabilities/guardrails.py +182 -0
  28. praisonai/capabilities/images.py +330 -0
  29. praisonai/capabilities/mcp.py +190 -0
  30. praisonai/capabilities/messages.py +270 -0
  31. praisonai/capabilities/moderations.py +154 -0
  32. praisonai/capabilities/ocr.py +217 -0
  33. praisonai/capabilities/passthrough.py +204 -0
  34. praisonai/capabilities/rag.py +207 -0
  35. praisonai/capabilities/realtime.py +160 -0
  36. praisonai/capabilities/rerank.py +165 -0
  37. praisonai/capabilities/responses.py +266 -0
  38. praisonai/capabilities/search.py +109 -0
  39. praisonai/capabilities/skills.py +133 -0
  40. praisonai/capabilities/vector_store_files.py +334 -0
  41. praisonai/capabilities/vector_stores.py +304 -0
  42. praisonai/capabilities/videos.py +141 -0
  43. praisonai/chainlit_ui.py +304 -0
  44. praisonai/chat/__init__.py +106 -0
  45. praisonai/chat/app.py +125 -0
  46. praisonai/cli/__init__.py +26 -0
  47. praisonai/cli/app.py +213 -0
  48. praisonai/cli/commands/__init__.py +75 -0
  49. praisonai/cli/commands/acp.py +70 -0
  50. praisonai/cli/commands/completion.py +333 -0
  51. praisonai/cli/commands/config.py +166 -0
  52. praisonai/cli/commands/debug.py +142 -0
  53. praisonai/cli/commands/diag.py +55 -0
  54. praisonai/cli/commands/doctor.py +166 -0
  55. praisonai/cli/commands/environment.py +179 -0
  56. praisonai/cli/commands/lsp.py +112 -0
  57. praisonai/cli/commands/mcp.py +210 -0
  58. praisonai/cli/commands/profile.py +457 -0
  59. praisonai/cli/commands/run.py +228 -0
  60. praisonai/cli/commands/schedule.py +150 -0
  61. praisonai/cli/commands/serve.py +97 -0
  62. praisonai/cli/commands/session.py +212 -0
  63. praisonai/cli/commands/traces.py +145 -0
  64. praisonai/cli/commands/version.py +101 -0
  65. praisonai/cli/configuration/__init__.py +18 -0
  66. praisonai/cli/configuration/loader.py +353 -0
  67. praisonai/cli/configuration/paths.py +114 -0
  68. praisonai/cli/configuration/schema.py +164 -0
  69. praisonai/cli/features/__init__.py +268 -0
  70. praisonai/cli/features/acp.py +236 -0
  71. praisonai/cli/features/action_orchestrator.py +546 -0
  72. praisonai/cli/features/agent_scheduler.py +773 -0
  73. praisonai/cli/features/agent_tools.py +474 -0
  74. praisonai/cli/features/agents.py +375 -0
  75. praisonai/cli/features/at_mentions.py +471 -0
  76. praisonai/cli/features/auto_memory.py +182 -0
  77. praisonai/cli/features/autonomy_mode.py +490 -0
  78. praisonai/cli/features/background.py +356 -0
  79. praisonai/cli/features/base.py +168 -0
  80. praisonai/cli/features/capabilities.py +1326 -0
  81. praisonai/cli/features/checkpoints.py +338 -0
  82. praisonai/cli/features/code_intelligence.py +652 -0
  83. praisonai/cli/features/compaction.py +294 -0
  84. praisonai/cli/features/compare.py +534 -0
  85. praisonai/cli/features/cost_tracker.py +514 -0
  86. praisonai/cli/features/debug.py +810 -0
  87. praisonai/cli/features/deploy.py +517 -0
  88. praisonai/cli/features/diag.py +289 -0
  89. praisonai/cli/features/doctor/__init__.py +63 -0
  90. praisonai/cli/features/doctor/checks/__init__.py +24 -0
  91. praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
  92. praisonai/cli/features/doctor/checks/config_checks.py +366 -0
  93. praisonai/cli/features/doctor/checks/db_checks.py +366 -0
  94. praisonai/cli/features/doctor/checks/env_checks.py +543 -0
  95. praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
  96. praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
  97. praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
  98. praisonai/cli/features/doctor/checks/network_checks.py +251 -0
  99. praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
  100. praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
  101. praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
  102. praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
  103. praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
  104. praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
  105. praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
  106. praisonai/cli/features/doctor/engine.py +266 -0
  107. praisonai/cli/features/doctor/formatters.py +310 -0
  108. praisonai/cli/features/doctor/handler.py +397 -0
  109. praisonai/cli/features/doctor/models.py +264 -0
  110. praisonai/cli/features/doctor/registry.py +239 -0
  111. praisonai/cli/features/endpoints.py +1019 -0
  112. praisonai/cli/features/eval.py +560 -0
  113. praisonai/cli/features/external_agents.py +231 -0
  114. praisonai/cli/features/fast_context.py +410 -0
  115. praisonai/cli/features/flow_display.py +566 -0
  116. praisonai/cli/features/git_integration.py +651 -0
  117. praisonai/cli/features/guardrail.py +171 -0
  118. praisonai/cli/features/handoff.py +185 -0
  119. praisonai/cli/features/hooks.py +583 -0
  120. praisonai/cli/features/image.py +384 -0
  121. praisonai/cli/features/interactive_runtime.py +585 -0
  122. praisonai/cli/features/interactive_tools.py +380 -0
  123. praisonai/cli/features/interactive_tui.py +603 -0
  124. praisonai/cli/features/jobs.py +632 -0
  125. praisonai/cli/features/knowledge.py +531 -0
  126. praisonai/cli/features/lite.py +244 -0
  127. praisonai/cli/features/lsp_cli.py +225 -0
  128. praisonai/cli/features/mcp.py +169 -0
  129. praisonai/cli/features/message_queue.py +587 -0
  130. praisonai/cli/features/metrics.py +211 -0
  131. praisonai/cli/features/n8n.py +673 -0
  132. praisonai/cli/features/observability.py +293 -0
  133. praisonai/cli/features/ollama.py +361 -0
  134. praisonai/cli/features/output_style.py +273 -0
  135. praisonai/cli/features/package.py +631 -0
  136. praisonai/cli/features/performance.py +308 -0
  137. praisonai/cli/features/persistence.py +636 -0
  138. praisonai/cli/features/profile.py +226 -0
  139. praisonai/cli/features/profiler/__init__.py +81 -0
  140. praisonai/cli/features/profiler/core.py +558 -0
  141. praisonai/cli/features/profiler/optimizations.py +652 -0
  142. praisonai/cli/features/profiler/suite.py +386 -0
  143. praisonai/cli/features/profiling.py +350 -0
  144. praisonai/cli/features/queue/__init__.py +73 -0
  145. praisonai/cli/features/queue/manager.py +395 -0
  146. praisonai/cli/features/queue/models.py +286 -0
  147. praisonai/cli/features/queue/persistence.py +564 -0
  148. praisonai/cli/features/queue/scheduler.py +484 -0
  149. praisonai/cli/features/queue/worker.py +372 -0
  150. praisonai/cli/features/recipe.py +1723 -0
  151. praisonai/cli/features/recipes.py +449 -0
  152. praisonai/cli/features/registry.py +229 -0
  153. praisonai/cli/features/repo_map.py +860 -0
  154. praisonai/cli/features/router.py +466 -0
  155. praisonai/cli/features/sandbox_executor.py +515 -0
  156. praisonai/cli/features/serve.py +829 -0
  157. praisonai/cli/features/session.py +222 -0
  158. praisonai/cli/features/skills.py +856 -0
  159. praisonai/cli/features/slash_commands.py +650 -0
  160. praisonai/cli/features/telemetry.py +179 -0
  161. praisonai/cli/features/templates.py +1384 -0
  162. praisonai/cli/features/thinking.py +305 -0
  163. praisonai/cli/features/todo.py +334 -0
  164. praisonai/cli/features/tools.py +680 -0
  165. praisonai/cli/features/tui/__init__.py +83 -0
  166. praisonai/cli/features/tui/app.py +580 -0
  167. praisonai/cli/features/tui/cli.py +566 -0
  168. praisonai/cli/features/tui/debug.py +511 -0
  169. praisonai/cli/features/tui/events.py +99 -0
  170. praisonai/cli/features/tui/mock_provider.py +328 -0
  171. praisonai/cli/features/tui/orchestrator.py +652 -0
  172. praisonai/cli/features/tui/screens/__init__.py +50 -0
  173. praisonai/cli/features/tui/screens/main.py +245 -0
  174. praisonai/cli/features/tui/screens/queue.py +174 -0
  175. praisonai/cli/features/tui/screens/session.py +124 -0
  176. praisonai/cli/features/tui/screens/settings.py +148 -0
  177. praisonai/cli/features/tui/widgets/__init__.py +56 -0
  178. praisonai/cli/features/tui/widgets/chat.py +261 -0
  179. praisonai/cli/features/tui/widgets/composer.py +224 -0
  180. praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
  181. praisonai/cli/features/tui/widgets/status.py +167 -0
  182. praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
  183. praisonai/cli/features/workflow.py +720 -0
  184. praisonai/cli/legacy.py +236 -0
  185. praisonai/cli/main.py +5559 -0
  186. praisonai/cli/schedule_cli.py +54 -0
  187. praisonai/cli/state/__init__.py +31 -0
  188. praisonai/cli/state/identifiers.py +161 -0
  189. praisonai/cli/state/sessions.py +313 -0
  190. praisonai/code/__init__.py +93 -0
  191. praisonai/code/agent_tools.py +344 -0
  192. praisonai/code/diff/__init__.py +21 -0
  193. praisonai/code/diff/diff_strategy.py +432 -0
  194. praisonai/code/tools/__init__.py +27 -0
  195. praisonai/code/tools/apply_diff.py +221 -0
  196. praisonai/code/tools/execute_command.py +275 -0
  197. praisonai/code/tools/list_files.py +274 -0
  198. praisonai/code/tools/read_file.py +206 -0
  199. praisonai/code/tools/search_replace.py +248 -0
  200. praisonai/code/tools/write_file.py +217 -0
  201. praisonai/code/utils/__init__.py +46 -0
  202. praisonai/code/utils/file_utils.py +307 -0
  203. praisonai/code/utils/ignore_utils.py +308 -0
  204. praisonai/code/utils/text_utils.py +276 -0
  205. praisonai/db/__init__.py +64 -0
  206. praisonai/db/adapter.py +531 -0
  207. praisonai/deploy/__init__.py +62 -0
  208. praisonai/deploy/api.py +231 -0
  209. praisonai/deploy/docker.py +454 -0
  210. praisonai/deploy/doctor.py +367 -0
  211. praisonai/deploy/main.py +327 -0
  212. praisonai/deploy/models.py +179 -0
  213. praisonai/deploy/providers/__init__.py +33 -0
  214. praisonai/deploy/providers/aws.py +331 -0
  215. praisonai/deploy/providers/azure.py +358 -0
  216. praisonai/deploy/providers/base.py +101 -0
  217. praisonai/deploy/providers/gcp.py +314 -0
  218. praisonai/deploy/schema.py +208 -0
  219. praisonai/deploy.py +185 -0
  220. praisonai/endpoints/__init__.py +53 -0
  221. praisonai/endpoints/a2u_server.py +410 -0
  222. praisonai/endpoints/discovery.py +165 -0
  223. praisonai/endpoints/providers/__init__.py +28 -0
  224. praisonai/endpoints/providers/a2a.py +253 -0
  225. praisonai/endpoints/providers/a2u.py +208 -0
  226. praisonai/endpoints/providers/agents_api.py +171 -0
  227. praisonai/endpoints/providers/base.py +231 -0
  228. praisonai/endpoints/providers/mcp.py +263 -0
  229. praisonai/endpoints/providers/recipe.py +206 -0
  230. praisonai/endpoints/providers/tools_mcp.py +150 -0
  231. praisonai/endpoints/registry.py +131 -0
  232. praisonai/endpoints/server.py +161 -0
  233. praisonai/inbuilt_tools/__init__.py +24 -0
  234. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  235. praisonai/inc/__init__.py +2 -0
  236. praisonai/inc/config.py +96 -0
  237. praisonai/inc/models.py +155 -0
  238. praisonai/integrations/__init__.py +56 -0
  239. praisonai/integrations/base.py +303 -0
  240. praisonai/integrations/claude_code.py +270 -0
  241. praisonai/integrations/codex_cli.py +255 -0
  242. praisonai/integrations/cursor_cli.py +195 -0
  243. praisonai/integrations/gemini_cli.py +222 -0
  244. praisonai/jobs/__init__.py +67 -0
  245. praisonai/jobs/executor.py +425 -0
  246. praisonai/jobs/models.py +230 -0
  247. praisonai/jobs/router.py +314 -0
  248. praisonai/jobs/server.py +186 -0
  249. praisonai/jobs/store.py +203 -0
  250. praisonai/llm/__init__.py +66 -0
  251. praisonai/llm/registry.py +382 -0
  252. praisonai/mcp_server/__init__.py +152 -0
  253. praisonai/mcp_server/adapters/__init__.py +74 -0
  254. praisonai/mcp_server/adapters/agents.py +128 -0
  255. praisonai/mcp_server/adapters/capabilities.py +168 -0
  256. praisonai/mcp_server/adapters/cli_tools.py +568 -0
  257. praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
  258. praisonai/mcp_server/adapters/knowledge.py +93 -0
  259. praisonai/mcp_server/adapters/memory.py +104 -0
  260. praisonai/mcp_server/adapters/prompts.py +306 -0
  261. praisonai/mcp_server/adapters/resources.py +124 -0
  262. praisonai/mcp_server/adapters/tools_bridge.py +280 -0
  263. praisonai/mcp_server/auth/__init__.py +48 -0
  264. praisonai/mcp_server/auth/api_key.py +291 -0
  265. praisonai/mcp_server/auth/oauth.py +460 -0
  266. praisonai/mcp_server/auth/oidc.py +289 -0
  267. praisonai/mcp_server/auth/scopes.py +260 -0
  268. praisonai/mcp_server/cli.py +852 -0
  269. praisonai/mcp_server/elicitation.py +445 -0
  270. praisonai/mcp_server/icons.py +302 -0
  271. praisonai/mcp_server/recipe_adapter.py +573 -0
  272. praisonai/mcp_server/recipe_cli.py +824 -0
  273. praisonai/mcp_server/registry.py +703 -0
  274. praisonai/mcp_server/sampling.py +422 -0
  275. praisonai/mcp_server/server.py +490 -0
  276. praisonai/mcp_server/tasks.py +443 -0
  277. praisonai/mcp_server/transports/__init__.py +18 -0
  278. praisonai/mcp_server/transports/http_stream.py +376 -0
  279. praisonai/mcp_server/transports/stdio.py +132 -0
  280. praisonai/persistence/__init__.py +84 -0
  281. praisonai/persistence/config.py +238 -0
  282. praisonai/persistence/conversation/__init__.py +25 -0
  283. praisonai/persistence/conversation/async_mysql.py +427 -0
  284. praisonai/persistence/conversation/async_postgres.py +410 -0
  285. praisonai/persistence/conversation/async_sqlite.py +371 -0
  286. praisonai/persistence/conversation/base.py +151 -0
  287. praisonai/persistence/conversation/json_store.py +250 -0
  288. praisonai/persistence/conversation/mysql.py +387 -0
  289. praisonai/persistence/conversation/postgres.py +401 -0
  290. praisonai/persistence/conversation/singlestore.py +240 -0
  291. praisonai/persistence/conversation/sqlite.py +341 -0
  292. praisonai/persistence/conversation/supabase.py +203 -0
  293. praisonai/persistence/conversation/surrealdb.py +287 -0
  294. praisonai/persistence/factory.py +301 -0
  295. praisonai/persistence/hooks/__init__.py +18 -0
  296. praisonai/persistence/hooks/agent_hooks.py +297 -0
  297. praisonai/persistence/knowledge/__init__.py +26 -0
  298. praisonai/persistence/knowledge/base.py +144 -0
  299. praisonai/persistence/knowledge/cassandra.py +232 -0
  300. praisonai/persistence/knowledge/chroma.py +295 -0
  301. praisonai/persistence/knowledge/clickhouse.py +242 -0
  302. praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
  303. praisonai/persistence/knowledge/couchbase.py +286 -0
  304. praisonai/persistence/knowledge/lancedb.py +216 -0
  305. praisonai/persistence/knowledge/langchain_adapter.py +291 -0
  306. praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
  307. praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
  308. praisonai/persistence/knowledge/milvus.py +277 -0
  309. praisonai/persistence/knowledge/mongodb_vector.py +306 -0
  310. praisonai/persistence/knowledge/pgvector.py +335 -0
  311. praisonai/persistence/knowledge/pinecone.py +253 -0
  312. praisonai/persistence/knowledge/qdrant.py +301 -0
  313. praisonai/persistence/knowledge/redis_vector.py +291 -0
  314. praisonai/persistence/knowledge/singlestore_vector.py +299 -0
  315. praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
  316. praisonai/persistence/knowledge/upstash_vector.py +266 -0
  317. praisonai/persistence/knowledge/weaviate.py +223 -0
  318. praisonai/persistence/migrations/__init__.py +10 -0
  319. praisonai/persistence/migrations/manager.py +251 -0
  320. praisonai/persistence/orchestrator.py +406 -0
  321. praisonai/persistence/state/__init__.py +21 -0
  322. praisonai/persistence/state/async_mongodb.py +200 -0
  323. praisonai/persistence/state/base.py +107 -0
  324. praisonai/persistence/state/dynamodb.py +226 -0
  325. praisonai/persistence/state/firestore.py +175 -0
  326. praisonai/persistence/state/gcs.py +155 -0
  327. praisonai/persistence/state/memory.py +245 -0
  328. praisonai/persistence/state/mongodb.py +158 -0
  329. praisonai/persistence/state/redis.py +190 -0
  330. praisonai/persistence/state/upstash.py +144 -0
  331. praisonai/persistence/tests/__init__.py +3 -0
  332. praisonai/persistence/tests/test_all_backends.py +633 -0
  333. praisonai/profiler.py +1214 -0
  334. praisonai/recipe/__init__.py +134 -0
  335. praisonai/recipe/bridge.py +278 -0
  336. praisonai/recipe/core.py +893 -0
  337. praisonai/recipe/exceptions.py +54 -0
  338. praisonai/recipe/history.py +402 -0
  339. praisonai/recipe/models.py +266 -0
  340. praisonai/recipe/operations.py +440 -0
  341. praisonai/recipe/policy.py +422 -0
  342. praisonai/recipe/registry.py +849 -0
  343. praisonai/recipe/runtime.py +214 -0
  344. praisonai/recipe/security.py +711 -0
  345. praisonai/recipe/serve.py +859 -0
  346. praisonai/recipe/server.py +613 -0
  347. praisonai/scheduler/__init__.py +45 -0
  348. praisonai/scheduler/agent_scheduler.py +552 -0
  349. praisonai/scheduler/base.py +124 -0
  350. praisonai/scheduler/daemon_manager.py +225 -0
  351. praisonai/scheduler/state_manager.py +155 -0
  352. praisonai/scheduler/yaml_loader.py +193 -0
  353. praisonai/scheduler.py +194 -0
  354. praisonai/setup/__init__.py +1 -0
  355. praisonai/setup/build.py +21 -0
  356. praisonai/setup/post_install.py +23 -0
  357. praisonai/setup/setup_conda_env.py +25 -0
  358. praisonai/setup.py +16 -0
  359. praisonai/templates/__init__.py +116 -0
  360. praisonai/templates/cache.py +364 -0
  361. praisonai/templates/dependency_checker.py +358 -0
  362. praisonai/templates/discovery.py +391 -0
  363. praisonai/templates/loader.py +564 -0
  364. praisonai/templates/registry.py +511 -0
  365. praisonai/templates/resolver.py +206 -0
  366. praisonai/templates/security.py +327 -0
  367. praisonai/templates/tool_override.py +498 -0
  368. praisonai/templates/tools_doctor.py +256 -0
  369. praisonai/test.py +105 -0
  370. praisonai/train.py +562 -0
  371. praisonai/train_vision.py +306 -0
  372. praisonai/ui/agents.py +824 -0
  373. praisonai/ui/callbacks.py +57 -0
  374. praisonai/ui/chainlit_compat.py +246 -0
  375. praisonai/ui/chat.py +532 -0
  376. praisonai/ui/code.py +717 -0
  377. praisonai/ui/colab.py +474 -0
  378. praisonai/ui/colab_chainlit.py +81 -0
  379. praisonai/ui/components/aicoder.py +284 -0
  380. praisonai/ui/context.py +283 -0
  381. praisonai/ui/database_config.py +56 -0
  382. praisonai/ui/db.py +294 -0
  383. praisonai/ui/realtime.py +488 -0
  384. praisonai/ui/realtimeclient/__init__.py +756 -0
  385. praisonai/ui/realtimeclient/tools.py +242 -0
  386. praisonai/ui/sql_alchemy.py +710 -0
  387. praisonai/upload_vision.py +140 -0
  388. praisonai/version.py +1 -0
  389. praisonai-3.0.0.dist-info/METADATA +3493 -0
  390. praisonai-3.0.0.dist-info/RECORD +393 -0
  391. praisonai-3.0.0.dist-info/WHEEL +5 -0
  392. praisonai-3.0.0.dist-info/entry_points.txt +4 -0
  393. praisonai-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,773 @@
1
+ """
2
+ Agent Scheduler CLI Feature.
3
+
4
+ Provides CLI commands for scheduling agents to run 24/7 at regular intervals.
5
+ """
6
+
7
+ import logging
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class AgentSchedulerHandler:
13
+ """Handler for agent scheduler CLI commands."""
14
+
15
+ @staticmethod
16
+ def handle_daemon_command(subcommand: str, args, unknown_args=None) -> int:
17
+ """
18
+ Handle daemon management commands (start, list, stop, logs, restart).
19
+
20
+ Args:
21
+ subcommand: Command to execute (start, list, stop, logs, restart)
22
+ args: Parsed command-line arguments
23
+ unknown_args: Additional arguments
24
+
25
+ Returns:
26
+ Exit code
27
+ """
28
+ from praisonai.scheduler.state_manager import SchedulerStateManager
29
+ from praisonai.scheduler.daemon_manager import DaemonManager
30
+
31
+ state_manager = SchedulerStateManager()
32
+ daemon_manager = DaemonManager()
33
+
34
+ if subcommand == "start":
35
+ return AgentSchedulerHandler._handle_start(args, unknown_args, state_manager, daemon_manager)
36
+ elif subcommand == "list":
37
+ return AgentSchedulerHandler._handle_list(state_manager)
38
+ elif subcommand == "stop":
39
+ return AgentSchedulerHandler._handle_stop(unknown_args, state_manager, daemon_manager)
40
+ elif subcommand == "logs":
41
+ return AgentSchedulerHandler._handle_logs(unknown_args, daemon_manager)
42
+ elif subcommand == "restart":
43
+ return AgentSchedulerHandler._handle_restart(unknown_args, state_manager, daemon_manager)
44
+ elif subcommand == "delete":
45
+ return AgentSchedulerHandler._handle_delete(unknown_args, state_manager)
46
+ elif subcommand == "describe":
47
+ return AgentSchedulerHandler._handle_describe(unknown_args, state_manager, daemon_manager)
48
+ elif subcommand == "save":
49
+ return AgentSchedulerHandler._handle_save(unknown_args, state_manager)
50
+ elif subcommand == "stop-all":
51
+ return AgentSchedulerHandler._handle_stop_all(state_manager, daemon_manager)
52
+ elif subcommand == "stats":
53
+ return AgentSchedulerHandler._handle_stats(state_manager, unknown_args, daemon_manager)
54
+ else:
55
+ print(f"āŒ Unknown subcommand: {subcommand}")
56
+ print("\nAvailable commands:")
57
+ print(" start, list, stop, logs, restart, delete, describe, save, stop-all, stats")
58
+ return 1
59
+
60
+ @staticmethod
61
+ def _handle_start(args, unknown_args, state_manager, daemon_manager) -> int:
62
+ """Handle 'schedule start' command."""
63
+ from datetime import datetime
64
+
65
+ if not unknown_args:
66
+ print("āŒ Error: Please provide scheduler name and task/recipe")
67
+ print("\nUsage:")
68
+ print(' praisonai schedule start <name> "Your task" --interval hourly')
69
+ print(' praisonai schedule start <name> --recipe <recipe-name> --interval hourly')
70
+ print("\nExample:")
71
+ print(' praisonai schedule start news-checker "Check AI news" --interval hourly')
72
+ print(' praisonai schedule start news-checker --recipe news-monitor --interval hourly')
73
+ return 1
74
+
75
+ # Parse arguments
76
+ name = unknown_args[0]
77
+
78
+ # Check for --recipe flag in unknown_args
79
+ recipe_name = None
80
+ task = None
81
+ if "--recipe" in unknown_args:
82
+ recipe_idx = unknown_args.index("--recipe")
83
+ if recipe_idx + 1 < len(unknown_args):
84
+ recipe_name = unknown_args[recipe_idx + 1]
85
+ else:
86
+ task = unknown_args[1] if len(unknown_args) > 1 else None
87
+
88
+ if not task and not recipe_name:
89
+ print("āŒ Error: Please provide a task or --recipe <name>")
90
+ return 1
91
+
92
+ # Get options
93
+ interval = getattr(args, 'schedule_interval', None) or 'hourly'
94
+ max_retries = getattr(args, 'schedule_max_retries', None) or 3
95
+ timeout = getattr(args, 'timeout', None)
96
+ max_cost = getattr(args, 'max_cost', None)
97
+
98
+ # Check if name already exists
99
+ existing = state_manager.load_state(name)
100
+ if existing and daemon_manager.get_status(existing.get('pid', 0))['is_alive']:
101
+ print(f"āŒ Error: Scheduler '{name}' is already running (PID: {existing['pid']})")
102
+ print(f" Use 'praisonai schedule stop {name}' to stop it first")
103
+ return 1
104
+
105
+ # Start daemon
106
+ print(f"šŸš€ Starting scheduler '{name}'...")
107
+ if recipe_name:
108
+ print(f" Recipe: {recipe_name}")
109
+ else:
110
+ print(f" Task: {task}")
111
+ print(f" Interval: {interval}")
112
+ if timeout:
113
+ print(f" Timeout: {timeout}s")
114
+ if max_cost:
115
+ print(f" Budget: ${max_cost}")
116
+
117
+ pid = daemon_manager.start_scheduler_daemon(
118
+ name=name,
119
+ task=task,
120
+ recipe_name=recipe_name,
121
+ interval=interval,
122
+ max_cost=max_cost,
123
+ timeout=timeout,
124
+ max_retries=max_retries
125
+ )
126
+
127
+ # Save state
128
+ state = {
129
+ "name": name,
130
+ "pid": pid,
131
+ "task": task,
132
+ "recipe_name": recipe_name,
133
+ "interval": interval,
134
+ "timeout": timeout,
135
+ "max_cost": max_cost,
136
+ "max_retries": max_retries,
137
+ "status": "running",
138
+ "started_at": datetime.now().isoformat(),
139
+ "executions": 0,
140
+ "cost": 0.0
141
+ }
142
+ state_manager.save_state(name, state)
143
+
144
+ print(f"\nāœ… Scheduler '{name}' started successfully!")
145
+ print(f" PID: {pid}")
146
+ print(f" Logs: ~/.praisonai/logs/{name}.log")
147
+ print(f"\nManage:")
148
+ print(f" praisonai schedule list")
149
+ print(f" praisonai schedule describe {name}")
150
+ print(f" praisonai schedule logs {name} --follow")
151
+ print(f" praisonai schedule stop {name}")
152
+
153
+ return 0
154
+
155
+ @staticmethod
156
+ def _handle_list(state_manager) -> int:
157
+ """Handle 'schedule list' command."""
158
+ states = state_manager.list_all()
159
+
160
+ if not states:
161
+ print("No schedulers running.")
162
+ print("\nStart one with:")
163
+ print(' praisonai schedule start <name> "Your task" --interval hourly')
164
+ return 0
165
+
166
+ # Clean up dead processes
167
+ state_manager.cleanup_dead_processes()
168
+ states = state_manager.list_all()
169
+
170
+ # Display table
171
+ print(f"\n{'Name':<20} {'Status':<10} {'PID':<8} {'Interval':<12} {'Task':<40}")
172
+ print("=" * 90)
173
+
174
+ for state in states:
175
+ name = state.get('name', 'unknown')[:20]
176
+ pid = state.get('pid', 0)
177
+ status = "running" if state_manager.is_process_alive(pid) else "stopped"
178
+ interval = state.get('interval', 'unknown')[:12]
179
+ task = state.get('task', '')[:40]
180
+
181
+ status_icon = "🟢" if status == "running" else "šŸ”“"
182
+ print(f"{name:<20} {status_icon} {status:<8} {pid:<8} {interval:<12} {task:<40}")
183
+
184
+ print(f"\nTotal: {len(states)} scheduler(s)")
185
+ return 0
186
+
187
+ @staticmethod
188
+ def _handle_stats(state_manager, unknown_args=None, daemon_manager=None) -> int:
189
+ """Handle 'schedule stats' command - show aggregate or individual statistics."""
190
+ # If a name is provided, show individual stats (alias for describe)
191
+ if unknown_args and len(unknown_args) > 0:
192
+ name = unknown_args[0]
193
+ return AgentSchedulerHandler._handle_describe([name], state_manager, daemon_manager)
194
+
195
+ # Otherwise show aggregate stats
196
+ states = state_manager.list_all()
197
+
198
+ if not states:
199
+ print("No schedulers found.")
200
+ print("\nStart one with:")
201
+ print(' praisonai schedule start <name> "Your task" --interval hourly')
202
+ return 0
203
+
204
+ # Calculate aggregate stats
205
+ total_schedulers = len(states)
206
+ running = sum(1 for s in states if s.get('status') == 'running')
207
+ stopped = total_schedulers - running
208
+ total_executions = sum(s.get('executions', 0) for s in states)
209
+ total_cost = sum(s.get('cost', 0.0) for s in states)
210
+
211
+ print(f"\n{'='*60}")
212
+ print(f"šŸ“Š Aggregate Scheduler Statistics")
213
+ print(f"{'='*60}")
214
+ print(f"Total Schedulers: {total_schedulers}")
215
+ print(f" 🟢 Running: {running}")
216
+ print(f" šŸ”“ Stopped: {stopped}")
217
+ print(f"\nTotal Executions: {total_executions}")
218
+ print(f"Total Cost: ${total_cost:.4f}")
219
+
220
+ if total_executions > 0:
221
+ avg_cost = total_cost / total_executions
222
+ print(f"Avg Cost/Execution: ${avg_cost:.4f}")
223
+
224
+ print(f"{'='*60}\n")
225
+
226
+ # Show per-scheduler breakdown
227
+ print("Per-Scheduler Breakdown:")
228
+ print(f"{'Name':<20} {'Status':<10} {'Executions':<12} {'Cost':<12}")
229
+ print("="*60)
230
+
231
+ for state in sorted(states, key=lambda x: x.get('executions', 0), reverse=True):
232
+ name = state['name'][:20]
233
+ status = state.get('status', 'unknown')
234
+ executions = state.get('executions', 0)
235
+ cost = state.get('cost', 0.0)
236
+
237
+ status_icon = "🟢" if status == "running" else "šŸ”“"
238
+ print(f"{name:<20} {status_icon} {status:<8} {executions:<12} ${cost:<11.4f}")
239
+
240
+ return 0
241
+
242
+ @staticmethod
243
+ def _handle_stop_all(state_manager, daemon_manager) -> int:
244
+ """Handle 'schedule stop-all' command."""
245
+ states = state_manager.list_all()
246
+
247
+ if not states:
248
+ print("No schedulers running.")
249
+ return 0
250
+
251
+ print(f"\nšŸ›‘ Stopping all schedulers ({len(states)} total)...\n")
252
+
253
+ stopped = 0
254
+ failed = 0
255
+
256
+ for state in states:
257
+ name = state['name']
258
+ pid = state['pid']
259
+
260
+ try:
261
+ if daemon_manager.stop_daemon(pid):
262
+ state_manager.delete_state(name)
263
+ print(f"āœ… Stopped '{name}' (PID: {pid})")
264
+ stopped += 1
265
+ else:
266
+ print(f"āŒ Failed to stop '{name}' (PID: {pid})")
267
+ failed += 1
268
+ except Exception as e:
269
+ print(f"āŒ Error stopping '{name}': {e}")
270
+ failed += 1
271
+
272
+ print(f"\nšŸ“Š Summary: {stopped} stopped, {failed} failed")
273
+ return 0 if failed == 0 else 1
274
+
275
+ @staticmethod
276
+ def _handle_stop(unknown_args, state_manager, daemon_manager) -> int:
277
+ """Handle 'schedule stop' command."""
278
+ if not unknown_args:
279
+ print("āŒ Error: Please provide scheduler name")
280
+ print("\nUsage: praisonai schedule stop <name>")
281
+ return 1
282
+
283
+ name = unknown_args[0]
284
+ state = state_manager.load_state(name)
285
+
286
+ if not state:
287
+ print(f"āŒ Error: Scheduler '{name}' not found")
288
+ print("\nList schedulers with: praisonai schedule list")
289
+ return 1
290
+
291
+ pid = state.get('pid')
292
+ if not pid:
293
+ print(f"āŒ Error: No PID found for scheduler '{name}'")
294
+ return 1
295
+
296
+ print(f"šŸ›‘ Stopping scheduler '{name}' (PID: {pid})...")
297
+
298
+ success = daemon_manager.stop_daemon(pid)
299
+
300
+ if success:
301
+ state['status'] = 'stopped'
302
+ state_manager.save_state(name, state)
303
+ print(f"āœ… Scheduler '{name}' stopped successfully")
304
+ return 0
305
+ else:
306
+ print(f"āŒ Failed to stop scheduler '{name}'")
307
+ return 1
308
+
309
+ @staticmethod
310
+ def _handle_logs(unknown_args, daemon_manager) -> int:
311
+ """Handle 'schedule logs' command."""
312
+ if not unknown_args:
313
+ print("āŒ Error: Please provide scheduler name")
314
+ print("\nUsage: praisonai schedule logs <name> [-f]")
315
+ return 1
316
+
317
+ name = unknown_args[0]
318
+ follow = '-f' in unknown_args or '--follow' in unknown_args
319
+
320
+ if follow:
321
+ print(f"šŸ“‹ Following logs for '{name}' (Ctrl+C to stop)...")
322
+ import subprocess
323
+ log_file = daemon_manager.log_dir / f"{name}.log"
324
+ if not log_file.exists():
325
+ print(f"āŒ Error: Log file not found for '{name}'")
326
+ return 1
327
+
328
+ try:
329
+ subprocess.run(['tail', '-f', str(log_file)])
330
+ except KeyboardInterrupt:
331
+ print("\nāœ… Stopped following logs")
332
+ return 0
333
+ else:
334
+ logs = daemon_manager.read_logs(name, lines=50)
335
+ if logs:
336
+ print(f"šŸ“‹ Last 50 lines of logs for '{name}':\n")
337
+ print(logs)
338
+ return 0
339
+ else:
340
+ print(f"āŒ Error: No logs found for '{name}'")
341
+ return 1
342
+
343
+ @staticmethod
344
+ def _handle_restart(unknown_args, state_manager, daemon_manager) -> int:
345
+ """Handle 'schedule restart' command."""
346
+ from datetime import datetime
347
+ import time
348
+
349
+ if not unknown_args:
350
+ print("āŒ Error: Please provide scheduler name")
351
+ print("\nUsage: praisonai schedule restart <name>")
352
+ return 1
353
+
354
+ name = unknown_args[0]
355
+ state = state_manager.load_state(name)
356
+
357
+ if not state:
358
+ print(f"āŒ Error: Scheduler '{name}' not found")
359
+ return 1
360
+
361
+ print(f"šŸ”„ Restarting scheduler '{name}'...")
362
+
363
+ # Stop if running
364
+ pid = state.get('pid')
365
+ if pid and state_manager.is_process_alive(pid):
366
+ daemon_manager.stop_daemon(pid)
367
+ time.sleep(1)
368
+
369
+ # Start again
370
+ new_pid = daemon_manager.start_scheduler_daemon(
371
+ name=name,
372
+ task=state['task'],
373
+ interval=state['interval'],
374
+ max_cost=state.get('max_cost'),
375
+ timeout=state.get('timeout'),
376
+ max_retries=state.get('max_retries', 3)
377
+ )
378
+
379
+ state['pid'] = new_pid
380
+ state['status'] = 'running'
381
+ state['started_at'] = datetime.now().isoformat()
382
+ state_manager.save_state(name, state)
383
+
384
+ print(f"āœ… Scheduler '{name}' restarted (PID: {new_pid})")
385
+ return 0
386
+
387
+ @staticmethod
388
+ def _handle_delete(unknown_args, state_manager) -> int:
389
+ """Handle 'schedule delete' command."""
390
+ if not unknown_args:
391
+ print("āŒ Error: Please provide scheduler name")
392
+ print("\nUsage: praisonai schedule delete <name>")
393
+ return 1
394
+
395
+ name = unknown_args[0]
396
+
397
+ if state_manager.delete_state(name):
398
+ print(f"āœ… Scheduler '{name}' deleted from list")
399
+ return 0
400
+ else:
401
+ print(f"āŒ Error: Scheduler '{name}' not found")
402
+ return 1
403
+
404
+ @staticmethod
405
+ def _handle_describe(unknown_args, state_manager, daemon_manager) -> int:
406
+ """Handle 'schedule describe' command."""
407
+ from datetime import datetime
408
+
409
+ if not unknown_args:
410
+ print("āŒ Error: Please provide scheduler name")
411
+ print("\nUsage: praisonai schedule describe <name>")
412
+ return 1
413
+
414
+ name = unknown_args[0]
415
+ state = state_manager.load_state(name)
416
+
417
+ if not state:
418
+ print(f"āŒ Error: Scheduler '{name}' not found")
419
+ print("\nList schedulers with: praisonai schedule list")
420
+ return 1
421
+
422
+ # Get process status
423
+ pid = state.get('pid', 0)
424
+ is_alive = state_manager.is_process_alive(pid)
425
+ status = "🟢 running" if is_alive else "šŸ”“ stopped"
426
+
427
+ # Calculate uptime
428
+ started_at = state.get('started_at')
429
+ uptime = "N/A"
430
+ if started_at:
431
+ try:
432
+ start_time = datetime.fromisoformat(started_at)
433
+ uptime_delta = datetime.now() - start_time
434
+ hours = int(uptime_delta.total_seconds() // 3600)
435
+ minutes = int((uptime_delta.total_seconds() % 3600) // 60)
436
+ uptime = f"{hours}h {minutes}m"
437
+ except:
438
+ pass
439
+
440
+ # Display detailed info
441
+ print(f"\n{'='*60}")
442
+ print(f"šŸ“‹ Scheduler Details: {name}")
443
+ print(f"{'='*60}")
444
+ print(f"Status: {status}")
445
+ print(f"PID: {pid}")
446
+ print(f"Uptime: {uptime}")
447
+ print(f"Task: {state.get('task', 'N/A')}")
448
+ print(f"Interval: {state.get('interval', 'N/A')}")
449
+ print(f"Max Retries: {state.get('max_retries', 'N/A')}")
450
+
451
+ if state.get('timeout'):
452
+ print(f"Timeout: {state['timeout']}s")
453
+
454
+ if state.get('max_cost'):
455
+ print(f"Budget: ${state['max_cost']}")
456
+
457
+ print(f"Executions: {state.get('executions', 0)}")
458
+ print(f"Total Cost: ${state.get('cost', 0.0):.4f}")
459
+ print(f"Started: {started_at or 'N/A'}")
460
+
461
+ # Log file location
462
+ log_file = daemon_manager.log_dir / f"{name}.log"
463
+ print(f"Logs: {log_file}")
464
+
465
+ print(f"{'='*60}\n")
466
+
467
+ return 0
468
+
469
+ @staticmethod
470
+ def _handle_save(unknown_args, state_manager) -> int:
471
+ """Handle 'schedule save' command."""
472
+ import yaml
473
+
474
+ if not unknown_args:
475
+ print("āŒ Error: Please provide scheduler name")
476
+ print("\nUsage: praisonai schedule save <name> [output.yaml]")
477
+ return 1
478
+
479
+ name = unknown_args[0]
480
+ output_file = unknown_args[1] if len(unknown_args) > 1 else f"{name}.yaml"
481
+
482
+ state = state_manager.load_state(name)
483
+
484
+ if not state:
485
+ print(f"āŒ Error: Scheduler '{name}' not found")
486
+ return 1
487
+
488
+ # Create YAML config from state
489
+ yaml_config = {
490
+ 'framework': 'praisonai',
491
+ 'agents': [{
492
+ 'name': name,
493
+ 'role': 'Task Executor',
494
+ 'goal': state.get('task', ''),
495
+ 'instructions': state.get('task', ''),
496
+ 'verbose': True
497
+ }],
498
+ 'task': state.get('task', ''),
499
+ 'schedule': {
500
+ 'interval': state.get('interval', 'hourly'),
501
+ 'max_retries': state.get('max_retries', 3),
502
+ 'run_immediately': True
503
+ }
504
+ }
505
+
506
+ # Add optional fields
507
+ if state.get('timeout'):
508
+ yaml_config['schedule']['timeout'] = state['timeout']
509
+
510
+ if state.get('max_cost'):
511
+ yaml_config['schedule']['max_cost'] = state['max_cost']
512
+
513
+ # Write to file
514
+ try:
515
+ with open(output_file, 'w') as f:
516
+ yaml.dump(yaml_config, f, default_flow_style=False, sort_keys=False)
517
+
518
+ print(f"āœ… Configuration saved to: {output_file}")
519
+ print(f"\nRun with:")
520
+ print(f" praisonai schedule {output_file}")
521
+ return 0
522
+ except Exception as e:
523
+ print(f"āŒ Error saving configuration: {e}")
524
+ return 1
525
+
526
+ @staticmethod
527
+ def handle_schedule_command(args, unknown_args, daemon_mode=False) -> int:
528
+ """
529
+ Handle the schedule command for running agents periodically.
530
+
531
+ Supports two modes:
532
+ 1. YAML mode: praisonai schedule agents.yaml
533
+ 2. Prompt mode: praisonai schedule "Your task here" --interval hourly
534
+
535
+ Args:
536
+ args: Parsed command-line arguments
537
+ unknown_args: Additional arguments after the command
538
+
539
+ Returns:
540
+ Exit code (0 for success, 1 for error)
541
+ """
542
+ try:
543
+ from praisonai.scheduler import AgentScheduler
544
+ from praisonaiagents import Agent
545
+ except ImportError:
546
+ print("Error: praisonai.scheduler or praisonaiagents module not found")
547
+ print("Please ensure PraisonAI is properly installed")
548
+ print("pip install praisonai praisonaiagents")
549
+ return 1
550
+
551
+ # Check if first arg is a YAML file or a prompt
552
+ first_arg = unknown_args[0] if unknown_args else None
553
+ is_yaml_mode = first_arg and (first_arg.endswith('.yaml') or first_arg.endswith('.yml'))
554
+
555
+ # Get overrides from CLI
556
+ interval_override = getattr(args, 'schedule_interval', None) or 'hourly'
557
+ max_retries_override = getattr(args, 'schedule_max_retries', None) or 3
558
+ timeout_override = getattr(args, 'timeout', None)
559
+ max_cost_override = getattr(args, 'max_cost', None)
560
+ verbose = getattr(args, 'verbose', False)
561
+
562
+ # Set up logging - only show logs if verbose
563
+ if verbose:
564
+ logging.basicConfig(
565
+ level=logging.INFO,
566
+ format='[%(asctime)s] %(levelname)s - %(message)s',
567
+ datefmt='%Y-%m-%d %H:%M:%S'
568
+ )
569
+ else:
570
+ # Suppress scheduler logs in non-verbose mode
571
+ logging.getLogger('praisonai.scheduler').setLevel(logging.WARNING)
572
+
573
+ try:
574
+ # Check if this is a recipe name (not a file, not a prompt with spaces)
575
+ is_recipe_mode = False
576
+ if first_arg and not is_yaml_mode:
577
+ # Try to resolve as recipe if it looks like a recipe name (no spaces, no file extension)
578
+ if ' ' not in first_arg and not first_arg.endswith('.yaml') and not first_arg.endswith('.yml'):
579
+ try:
580
+ from praisonai.recipe.bridge import resolve
581
+ resolve(first_arg) # Just check if it resolves
582
+ is_recipe_mode = True
583
+ except Exception:
584
+ pass # Not a recipe, continue with prompt mode
585
+
586
+ if is_yaml_mode:
587
+ # YAML mode: Load from agents.yaml
588
+ yaml_path = first_arg
589
+ print(f"šŸ¤– Loading agent configuration from: {yaml_path}")
590
+ scheduler = AgentScheduler.from_yaml(
591
+ yaml_path=yaml_path,
592
+ interval_override=interval_override,
593
+ max_retries_override=max_retries_override,
594
+ timeout_override=timeout_override,
595
+ max_cost_override=max_cost_override
596
+ )
597
+ elif is_recipe_mode:
598
+ # Recipe mode: Load from recipe name
599
+ print(f"šŸ³ Loading recipe: {first_arg}")
600
+ scheduler = AgentScheduler.from_recipe(
601
+ recipe_name=first_arg,
602
+ interval_override=interval_override,
603
+ max_retries_override=max_retries_override,
604
+ timeout_override=timeout_override,
605
+ max_cost_override=max_cost_override
606
+ )
607
+ else:
608
+ # Prompt mode: Create agent from direct prompt
609
+ if not first_arg:
610
+ print("āŒ Error: Please provide either a YAML file, recipe name, or a task prompt")
611
+ print("\nExamples:")
612
+ print(" praisonai schedule agents.yaml")
613
+ print(" praisonai schedule my-recipe --interval hourly")
614
+ print(' praisonai schedule "Check news every hour" --interval hourly')
615
+ return 1
616
+
617
+ task = first_arg
618
+
619
+ # Create agent
620
+ agent = Agent(
621
+ name="Scheduled Agent",
622
+ role="Task Executor",
623
+ goal=task,
624
+ instructions=task,
625
+ verbose=True # Enable verbose to see output in logs
626
+ )
627
+
628
+ # Create scheduler
629
+ scheduler = AgentScheduler(
630
+ agent=agent,
631
+ task=task,
632
+ timeout=timeout_override,
633
+ max_cost=max_cost_override
634
+ )
635
+
636
+ # Get configuration
637
+ interval = interval_override
638
+ max_retries = max_retries_override
639
+
640
+ if is_yaml_mode:
641
+ schedule_config = scheduler._yaml_schedule_config
642
+ interval = interval_override or schedule_config.get('interval', 'hourly')
643
+ max_retries = max_retries_override or schedule_config.get('max_retries', 3)
644
+
645
+ print(f"Schedule: {interval}")
646
+ print(f"Max Retries: {max_retries}")
647
+ if scheduler.timeout:
648
+ print(f"Timeout: {scheduler.timeout}s")
649
+ if scheduler.max_cost:
650
+ print(f"Budget: ${scheduler.max_cost}")
651
+ print(f"{'='*60}\n")
652
+
653
+ # Start scheduler
654
+ if is_yaml_mode:
655
+ scheduler.start_from_yaml_config()
656
+ else:
657
+ scheduler.start(schedule_expr=interval, max_retries=max_retries, run_immediately=True)
658
+
659
+ # If daemon mode, block to keep process alive
660
+ if daemon_mode:
661
+ import time
662
+ try:
663
+ while scheduler.is_running:
664
+ time.sleep(1)
665
+ except KeyboardInterrupt:
666
+ scheduler.stop()
667
+ else:
668
+ # Foreground mode - wait for Ctrl+C
669
+ print("ā° Starting scheduler... (Press Ctrl+C to stop)\n")
670
+ import time
671
+ try:
672
+ while scheduler.is_running:
673
+ time.sleep(1)
674
+ except KeyboardInterrupt:
675
+ print("\n\nšŸ›‘ Stopping scheduler...")
676
+ scheduler.stop()
677
+
678
+ # Display final stats
679
+ stats = scheduler.get_stats()
680
+ print("\nšŸ“Š Final Statistics:")
681
+ print(f" Total Executions: {stats['total_executions']}")
682
+ print(f" Successful: {stats['successful_executions']}")
683
+ print(f" Failed: {stats['failed_executions']}")
684
+ print(f" Success Rate: {stats['success_rate']:.1f}%")
685
+ print("\nāœ… Agent stopped successfully\n")
686
+
687
+ return 0
688
+
689
+ except FileNotFoundError as e:
690
+ print(f"āŒ Error: {e}")
691
+ print(f"\nMake sure {yaml_path} exists in the current directory.")
692
+ print("\nExample agents.yaml structure:")
693
+ print("""
694
+ framework: praisonai
695
+
696
+ agents:
697
+ - name: "AI News Monitor"
698
+ role: "News Analyst"
699
+ instructions: "Search and summarize AI news"
700
+ tools:
701
+ - search_tool
702
+
703
+ task: "Search for latest AI news"
704
+
705
+ schedule:
706
+ interval: "hourly"
707
+ max_retries: 3
708
+ run_immediately: true
709
+ """)
710
+ return 1
711
+
712
+ except ValueError as e:
713
+ print(f"āŒ Configuration Error: {e}")
714
+ return 1
715
+
716
+ except Exception as e:
717
+ print(f"āŒ Error: {e}")
718
+ if verbose:
719
+ import traceback
720
+ traceback.print_exc()
721
+ return 1
722
+
723
+ @staticmethod
724
+ def add_schedule_arguments(subparsers):
725
+ """
726
+ Add schedule subcommand and arguments to parser.
727
+
728
+ Args:
729
+ subparsers: Subparsers object from argparse
730
+ """
731
+ schedule_parser = subparsers.add_parser(
732
+ 'schedule',
733
+ help='Schedule an agent to run continuously at regular intervals'
734
+ )
735
+
736
+ schedule_parser.add_argument(
737
+ 'schedule_yaml',
738
+ nargs='?',
739
+ default='agents.yaml',
740
+ help='Path to agents.yaml file (default: agents.yaml)'
741
+ )
742
+
743
+ schedule_parser.add_argument(
744
+ '--interval',
745
+ dest='schedule_interval',
746
+ type=str,
747
+ help='Override schedule interval (e.g., "hourly", "*/30m", "daily")'
748
+ )
749
+
750
+ schedule_parser.add_argument(
751
+ '--max-retries',
752
+ dest='schedule_max_retries',
753
+ type=int,
754
+ help='Override maximum retry attempts (default: from YAML or 3)'
755
+ )
756
+
757
+ schedule_parser.add_argument(
758
+ '--verbose', '-v',
759
+ action='store_true',
760
+ help='Enable verbose logging'
761
+ )
762
+
763
+ schedule_parser.set_defaults(func=AgentSchedulerHandler.handle_schedule_command)
764
+
765
+
766
+ def setup_scheduler_cli(subparsers):
767
+ """
768
+ Setup scheduler CLI commands.
769
+
770
+ Args:
771
+ subparsers: Subparsers object from argparse
772
+ """
773
+ AgentSchedulerHandler.add_schedule_arguments(subparsers)