praisonai-code 0.0.45__tar.gz → 0.0.46__tar.gz

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 (313) hide show
  1. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/PKG-INFO +1 -1
  2. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/__init__.py +1 -1
  3. praisonai_code-0.0.46/praisonai_code/_train_bridge.py +35 -0
  4. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/app.py +15 -2
  5. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/auth.py +231 -31
  6. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/setup/handler.py +116 -21
  7. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/legacy/praison_ai.py +28 -17
  8. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/llm/catalogue.py +43 -0
  9. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code.egg-info/PKG-INFO +1 -1
  10. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code.egg-info/SOURCES.txt +1 -0
  11. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/pyproject.toml +1 -1
  12. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/README.md +0 -0
  13. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/__main__.py +0 -0
  14. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/_bot_bridge.py +0 -0
  15. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/_framework_availability.py +0 -0
  16. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/_logging.py +0 -0
  17. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/_registry.py +0 -0
  18. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/_safe_loader.py +0 -0
  19. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/_version.py +0 -0
  20. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/_wrapper_bridge.py +0 -0
  21. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/__init__.py +0 -0
  22. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/_forward_shim.py +0 -0
  23. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/_paths.py +0 -0
  24. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/_warnings.py +0 -0
  25. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/_wrapper_reexport.py +0 -0
  26. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/approval_backend.py +0 -0
  27. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/branding.py +0 -0
  28. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/__init__.py +0 -0
  29. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/acp.py +0 -0
  30. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/agent.py +0 -0
  31. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/agents.py +0 -0
  32. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/attach.py +0 -0
  33. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/benchmark.py +0 -0
  34. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/browser.py +0 -0
  35. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/call.py +0 -0
  36. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/chat.py +0 -0
  37. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/checkpoint.py +0 -0
  38. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/code.py +0 -0
  39. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/command.py +0 -0
  40. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/commit.py +0 -0
  41. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/completion.py +0 -0
  42. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/config.py +0 -0
  43. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/daemon.py +0 -0
  44. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/debug.py +0 -0
  45. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/deploy.py +0 -0
  46. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/diag.py +0 -0
  47. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/doctor.py +0 -0
  48. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/endpoints.py +0 -0
  49. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/environment.py +0 -0
  50. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/eval.py +0 -0
  51. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/github.py +0 -0
  52. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/hooks.py +0 -0
  53. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/init.py +0 -0
  54. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/loop.py +0 -0
  55. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/lsp.py +0 -0
  56. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/memory.py +0 -0
  57. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/models.py +0 -0
  58. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/obs.py +0 -0
  59. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/package.py +0 -0
  60. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/paths.py +0 -0
  61. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/permissions.py +0 -0
  62. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/plugins.py +0 -0
  63. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/port.py +0 -0
  64. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/publish.py +0 -0
  65. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/registry.py +0 -0
  66. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/research.py +0 -0
  67. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/retrieval.py +0 -0
  68. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/rules.py +0 -0
  69. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/run.py +0 -0
  70. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/sandbox.py +0 -0
  71. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/serve.py +0 -0
  72. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/session.py +0 -0
  73. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/setup.py +0 -0
  74. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/skills.py +0 -0
  75. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/templates.py +0 -0
  76. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/test.py +0 -0
  77. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/todo.py +0 -0
  78. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/tools.py +0 -0
  79. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/traces.py +0 -0
  80. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/tracker.py +0 -0
  81. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/ui.py +0 -0
  82. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/uninstall.py +0 -0
  83. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/up.py +0 -0
  84. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/upgrade.py +0 -0
  85. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/version.py +0 -0
  86. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/commands/workflow.py +0 -0
  87. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/config_loader.py +0 -0
  88. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/__init__.py +0 -0
  89. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/config.schema.json +0 -0
  90. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/credentials.py +0 -0
  91. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/loader.py +0 -0
  92. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/model_resolver.py +0 -0
  93. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/oauth.py +0 -0
  94. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/paths.py +0 -0
  95. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/resolver.py +0 -0
  96. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/configuration/schema.py +0 -0
  97. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/execution/__init__.py +0 -0
  98. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/execution/core.py +0 -0
  99. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/execution/profiler.py +0 -0
  100. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/execution/request.py +0 -0
  101. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/execution/result.py +0 -0
  102. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/fallback_schema.py +0 -0
  103. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/__init__.py +0 -0
  104. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/_approval_bridge.py +0 -0
  105. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/_endpoint_registry.py +0 -0
  106. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/_search_registry.py +0 -0
  107. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/action_orchestrator.py +0 -0
  108. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/agents.py +0 -0
  109. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/at_mentions.py +0 -0
  110. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/audit_cli.py +0 -0
  111. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/auto_memory.py +0 -0
  112. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/auto_mode.py +0 -0
  113. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/autonomy_mode.py +0 -0
  114. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/base.py +0 -0
  115. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/benchmark.py +0 -0
  116. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/capabilities.py +0 -0
  117. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/checkpoints.py +0 -0
  118. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/cli_profiler.py +0 -0
  119. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/code_intelligence.py +0 -0
  120. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/compaction.py +0 -0
  121. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/compare.py +0 -0
  122. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/config_hierarchy.py +0 -0
  123. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/context_manager.py +0 -0
  124. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/cost_tracker.py +0 -0
  125. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/csv_test_runner.py +0 -0
  126. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/custom_definitions.py +0 -0
  127. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/debug.py +0 -0
  128. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/diag.py +0 -0
  129. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/display_jsonl.py +0 -0
  130. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/__init__.py +0 -0
  131. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/__init__.py +0 -0
  132. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/_wrapper_checks.py +0 -0
  133. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/acp_checks.py +0 -0
  134. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/bot_checks.py +0 -0
  135. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/config_checks.py +0 -0
  136. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/db_checks.py +0 -0
  137. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/env_checks.py +0 -0
  138. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/gateway_checks.py +0 -0
  139. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/lsp_checks.py +0 -0
  140. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/mcp_checks.py +0 -0
  141. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/memory_checks.py +0 -0
  142. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/network_checks.py +0 -0
  143. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/obs_checks.py +0 -0
  144. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/packaging_checks.py +0 -0
  145. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/performance_checks.py +0 -0
  146. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/permissions_checks.py +0 -0
  147. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/runtime_checks.py +0 -0
  148. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +0 -0
  149. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/selftest_checks.py +0 -0
  150. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/serve_checks.py +0 -0
  151. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/skills_checks.py +0 -0
  152. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/checks/tools_checks.py +0 -0
  153. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/engine.py +0 -0
  154. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/formatters.py +0 -0
  155. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/handler.py +0 -0
  156. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/models.py +0 -0
  157. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/doctor/registry.py +0 -0
  158. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/endpoints.py +0 -0
  159. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/examples.py +0 -0
  160. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/external_agents.py +0 -0
  161. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/fast_context.py +0 -0
  162. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/file_history.py +0 -0
  163. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/flow_display.py +0 -0
  164. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/git_attribution.py +0 -0
  165. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/git_integration.py +0 -0
  166. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/guardrail.py +0 -0
  167. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/handoff.py +0 -0
  168. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/hooks.py +0 -0
  169. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/hybrid_workflow.py +0 -0
  170. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/image.py +0 -0
  171. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/interactive_core_headless.py +0 -0
  172. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/interactive_runtime.py +0 -0
  173. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/interactive_test_harness.py +0 -0
  174. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/interactive_tools.py +0 -0
  175. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/interactive_tui.py +0 -0
  176. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/job_workflow.py +0 -0
  177. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/jobs.py +0 -0
  178. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/knowledge.py +0 -0
  179. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/knowledge_cli.py +0 -0
  180. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/lite.py +0 -0
  181. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/logs.py +0 -0
  182. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/lsp_cli.py +0 -0
  183. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/lsp_diagnostics.py +0 -0
  184. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/mcp.py +0 -0
  185. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/message_queue.py +0 -0
  186. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/metrics.py +0 -0
  187. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/migrate.py +0 -0
  188. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/migration_flow.py +0 -0
  189. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/migration_spec.py +0 -0
  190. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/n8n.py +0 -0
  191. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/observability.py +0 -0
  192. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/output_modes.py +0 -0
  193. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/output_style.py +0 -0
  194. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/package.py +0 -0
  195. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/performance.py +0 -0
  196. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/profiler/__init__.py +0 -0
  197. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/profiler/core.py +0 -0
  198. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/profiler/optimizations.py +0 -0
  199. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/profiler/suite.py +0 -0
  200. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/queue/__init__.py +0 -0
  201. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/queue/manager.py +0 -0
  202. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/queue/models.py +0 -0
  203. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/queue/persistence.py +0 -0
  204. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/queue/scheduler.py +0 -0
  205. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/queue/worker.py +0 -0
  206. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/recipe_creator.py +0 -0
  207. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/recipe_prompts.py +0 -0
  208. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/registry.py +0 -0
  209. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/repo_map.py +0 -0
  210. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/router.py +0 -0
  211. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/safe_shell.py +0 -0
  212. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/sandbox_executor.py +0 -0
  213. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/sdk_knowledge.py +0 -0
  214. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/self_manage.py +0 -0
  215. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/session.py +0 -0
  216. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/session_checkpoints.py +0 -0
  217. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/setup/__init__.py +0 -0
  218. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/setup/templates.py +0 -0
  219. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/skills.py +0 -0
  220. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/slash_commands.py +0 -0
  221. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/telemetry.py +0 -0
  222. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/thinking.py +0 -0
  223. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/todo.py +0 -0
  224. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tools.py +0 -0
  225. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/__init__.py +0 -0
  226. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/cli.py +0 -0
  227. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/config.py +0 -0
  228. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/debug.py +0 -0
  229. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/events.py +0 -0
  230. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/mock_provider.py +0 -0
  231. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/orchestrator.py +0 -0
  232. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/screens/__init__.py +0 -0
  233. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/screens/help.py +0 -0
  234. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/screens/main.py +0 -0
  235. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/screens/queue.py +0 -0
  236. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/screens/session.py +0 -0
  237. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/screens/settings.py +0 -0
  238. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/session_store.py +0 -0
  239. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/widgets/__init__.py +0 -0
  240. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/widgets/chat.py +0 -0
  241. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/widgets/command_popup.py +0 -0
  242. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/widgets/composer.py +0 -0
  243. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/widgets/file_popup.py +0 -0
  244. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/widgets/queue_panel.py +0 -0
  245. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/widgets/status.py +0 -0
  246. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/tui/widgets/tool_panel.py +0 -0
  247. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/wizard.py +0 -0
  248. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/workflow.py +0 -0
  249. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/features/yaml_utils.py +0 -0
  250. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/help_categories.py +0 -0
  251. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/__init__.py +0 -0
  252. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/async_tui.py +0 -0
  253. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/command_registry.py +0 -0
  254. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/config.py +0 -0
  255. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/core.py +0 -0
  256. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/events.py +0 -0
  257. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/frontends/__init__.py +0 -0
  258. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/frontends/rich_frontend.py +0 -0
  259. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/frontends/textual_frontend.py +0 -0
  260. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/praison_io.py +0 -0
  261. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/repl.py +0 -0
  262. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/split_tui.py +0 -0
  263. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/interactive/tui_app.py +0 -0
  264. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/legacy/__init__.py +0 -0
  265. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/legacy/env_security.py +0 -0
  266. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/legacy/prompt_dispatch.py +0 -0
  267. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/main.py +0 -0
  268. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/output/__init__.py +0 -0
  269. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/output/console.py +0 -0
  270. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/output/event_bridge.py +0 -0
  271. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/schedule_cli.py +0 -0
  272. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/schema_provider.py +0 -0
  273. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/session/__init__.py +0 -0
  274. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/session/resume.py +0 -0
  275. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/session/unified.py +0 -0
  276. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/state/__init__.py +0 -0
  277. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/state/identifiers.py +0 -0
  278. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/state/project_sessions.py +0 -0
  279. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/state/sessions.py +0 -0
  280. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/ui/__init__.py +0 -0
  281. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/ui/config.py +0 -0
  282. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/ui/events.py +0 -0
  283. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/ui/mg_backend.py +0 -0
  284. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/ui/plain.py +0 -0
  285. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/ui/rich_backend.py +0 -0
  286. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/unified_schema.py +0 -0
  287. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/utils/env_utils.py +0 -0
  288. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/utils/project.py +0 -0
  289. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli/utils/stdin.py +0 -0
  290. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli_backends/__init__.py +0 -0
  291. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli_backends/claude.py +0 -0
  292. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/cli_backends/registry.py +0 -0
  293. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/data/__init__.py +0 -0
  294. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/data/fixtures/interactive/multi_agent.csv +0 -0
  295. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/data/fixtures/interactive/refactor.csv +0 -0
  296. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/data/fixtures/interactive/smoke.csv +0 -0
  297. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/data/fixtures/interactive/tools.csv +0 -0
  298. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/llm/__init__.py +0 -0
  299. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/llm/config.py +0 -0
  300. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/llm/credentials.py +0 -0
  301. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/llm/env.py +0 -0
  302. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/runtime/__init__.py +0 -0
  303. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/runtime/__main__.py +0 -0
  304. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/runtime/client.py +0 -0
  305. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/runtime/descriptor.py +0 -0
  306. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/runtime/server.py +0 -0
  307. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/tool_registry.py +0 -0
  308. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code/tool_resolver.py +0 -0
  309. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code.egg-info/dependency_links.txt +0 -0
  310. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code.egg-info/entry_points.txt +0 -0
  311. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code.egg-info/requires.txt +0 -0
  312. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/praisonai_code.egg-info/top_level.txt +0 -0
  313. {praisonai_code-0.0.45 → praisonai_code-0.0.46}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praisonai-code
3
- Version: 0.0.45
3
+ Version: 0.0.46
4
4
  Summary: Agentic terminal CLI for PraisonAI — the terminal-native agent (run, chat, code, runtime, cli backends) extracted from the praisonai wrapper.
5
5
  Author: Mervin Praison
6
6
  License: MIT
@@ -12,6 +12,6 @@ the remaining terminal-agent modules, with PEP 562 shims left behind at the
12
12
  old ``praisonai.*`` paths for backward compatibility.
13
13
  """
14
14
 
15
- __version__ = "0.0.45"
15
+ __version__ = "0.0.46"
16
16
 
17
17
  __all__ = ["__version__"]
@@ -0,0 +1,35 @@
1
+ """Lazy access to ``praisonai-train`` from ``praisonai-code``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ from types import ModuleType
7
+ from typing import Any, TypeVar
8
+
9
+ T = TypeVar("T")
10
+
11
+ _INSTALL_HINT = "Install the training package: pip install praisonai-train"
12
+
13
+
14
+ def train_package_available() -> bool:
15
+ import importlib.util
16
+
17
+ return importlib.util.find_spec("praisonai_train") is not None
18
+
19
+
20
+ def import_train_module(name: str) -> ModuleType:
21
+ if not name.startswith("praisonai_train"):
22
+ raise ValueError(f"Expected praisonai_train module name, got {name!r}")
23
+ try:
24
+ return importlib.import_module(name)
25
+ except ImportError as exc:
26
+ raise ImportError(f"{name} requires praisonai-train. {_INSTALL_HINT}") from exc
27
+
28
+
29
+ def optional_train_attr(module_name: str, attr: str, default: T | None = None) -> Any | T | None:
30
+ if not train_package_available():
31
+ return default
32
+ try:
33
+ return getattr(import_train_module(module_name), attr)
34
+ except (ImportError, AttributeError):
35
+ return default
@@ -264,8 +264,7 @@ _WRAPPER_RESIDENT_COMMANDS = frozenset({
264
264
  "flow",
265
265
  "n8n",
266
266
  "replay",
267
- # C8.2 Batch B — training / managed / examples
268
- "train",
267
+ # C8.2 Batch B — managed / examples (train extracted to praisonai-train in C10)
269
268
  "managed",
270
269
  "examples",
271
270
  "standardise",
@@ -287,6 +286,13 @@ _WRAPPER_RESIDENT_COMMANDS = frozenset({
287
286
  "validate",
288
287
  })
289
288
 
289
+ # C10: Training commands implemented in ``praisonai_train.cli.commands.*``.
290
+ # ``get_command()`` loads them via ``praisonai_train.cli.commands.{name}`` when the
291
+ # train package is installed; standalone ``praisonai-code`` hides them from ``--help``.
292
+ _TRAIN_RESIDENT_COMMANDS = frozenset({
293
+ "train",
294
+ })
295
+
290
296
  # Backward-compatible alias (C7.1 name).
291
297
  _WRAPPER_COMMANDS = _WRAPPER_RESIDENT_COMMANDS
292
298
 
@@ -299,6 +305,7 @@ _SPECIAL_COMMANDS = {
299
305
 
300
306
  from praisonai_code._wrapper_bridge import wrapper_available
301
307
  from praisonai_code._bot_bridge import bot_package_available
308
+ from praisonai_code._train_bridge import train_package_available
302
309
 
303
310
 
304
311
  class LazyCommandGroup(TyperGroup):
@@ -314,10 +321,12 @@ class LazyCommandGroup(TyperGroup):
314
321
  # install does not surface commands it cannot resolve.
315
322
  wrapper_ok = wrapper_available()
316
323
  bot_ok = bot_package_available()
324
+ train_ok = train_package_available()
317
325
  commands.update(
318
326
  name for name in _LAZY_COMMANDS
319
327
  if (wrapper_ok or name not in _WRAPPER_RESIDENT_COMMANDS)
320
328
  and (bot_ok or name not in _BOT_RESIDENT_COMMANDS)
329
+ and (train_ok or name not in _TRAIN_RESIDENT_COMMANDS)
321
330
  )
322
331
  commands.update(_SPECIAL_COMMANDS.keys())
323
332
 
@@ -363,6 +372,10 @@ class LazyCommandGroup(TyperGroup):
363
372
  if not bot_package_available():
364
373
  return None
365
374
  module = importlib.import_module(f"praisonai_bot.cli.commands.{name}")
375
+ elif name in _TRAIN_RESIDENT_COMMANDS:
376
+ if not train_package_available():
377
+ return None
378
+ module = importlib.import_module(f"praisonai_train.cli.commands.{name}")
366
379
  elif name in _WRAPPER_RESIDENT_COMMANDS:
367
380
  if not wrapper_available():
368
381
  return None
@@ -21,6 +21,85 @@ from ..configuration.credentials import CredentialStore, redact_key, validate_ap
21
21
  app = typer.Typer(help="Manage API credentials")
22
22
 
23
23
 
24
+ # Provider id -> environment variable holding its API key. Used to fold active
25
+ # env-sourced credentials into `auth list`/`status` so users can tell which
26
+ # credential is live (env vs stored).
27
+ _PROVIDER_ENV_KEYS = {
28
+ "openai": "OPENAI_API_KEY",
29
+ "anthropic": "ANTHROPIC_API_KEY",
30
+ "google": "GOOGLE_API_KEY",
31
+ "gemini": "GEMINI_API_KEY",
32
+ "groq": "GROQ_API_KEY",
33
+ "openrouter": "OPENROUTER_API_KEY",
34
+ "mistral": "MISTRAL_API_KEY",
35
+ "deepseek": "DEEPSEEK_API_KEY",
36
+ "xai": "XAI_API_KEY",
37
+ "cohere": "COHERE_API_KEY",
38
+ "together": "TOGETHER_API_KEY",
39
+ "perplexity": "PERPLEXITYAI_API_KEY",
40
+ }
41
+
42
+
43
+ def _env_credentials() -> dict[str, tuple[str, str]]:
44
+ """Return ``{provider: (env_var, api_key)}`` for env-sourced keys present."""
45
+ found = {}
46
+ for provider, env_var in _PROVIDER_ENV_KEYS.items():
47
+ value = os.environ.get(env_var)
48
+ if value:
49
+ found[provider] = (env_var, value)
50
+ return found
51
+
52
+
53
+ def _select_provider_interactively(output) -> Optional[str]:
54
+ """Catalogue-driven provider picker for when no provider id is supplied.
55
+
56
+ Returns the chosen provider id, or ``None`` if selection is unavailable
57
+ (non-tty / rich missing) so the caller can prompt for a free-form id.
58
+ """
59
+ try:
60
+ from praisonai_code.llm.catalogue import ModelCatalogue
61
+
62
+ providers = ModelCatalogue().list_providers()
63
+ except Exception:
64
+ providers = []
65
+
66
+ ordered = [p for p in ("openai", "anthropic", "google", "ollama") if p in providers]
67
+ for p in providers:
68
+ if p not in ordered:
69
+ ordered.append(p)
70
+ ordered.append("custom")
71
+
72
+ try:
73
+ from rich.prompt import Prompt
74
+ except Exception:
75
+ return None
76
+
77
+ output.console.print("[bold]Choose your LLM provider:[/bold]")
78
+ choices = {}
79
+ for idx, pid in enumerate(ordered, start=1):
80
+ choices[str(idx)] = pid
81
+ label = "Other/custom" if pid == "custom" else pid
82
+ output.console.print(f" {idx}) {label}")
83
+
84
+ selected = Prompt.ask("Select provider", choices=list(choices.keys()), default="1")
85
+ pid = choices[selected]
86
+ if pid == "custom":
87
+ return Prompt.ask("Enter provider id").strip() or None
88
+ return pid
89
+
90
+
91
+ def _print_key_url_hint(provider: str, output) -> None:
92
+ """Print a one-line 'Get your key' hint for well-known providers."""
93
+ try:
94
+ from praisonai_code.llm.catalogue import key_url_for_provider
95
+
96
+ url = key_url_for_provider(provider)
97
+ except Exception:
98
+ url = None
99
+ if url:
100
+ output.print_info(f"Get your key: {url}")
101
+
102
+
24
103
  def _format_expiry(cred) -> str:
25
104
  """Format an OAuth credential's expiry for display ('(n/a)' for keys)."""
26
105
  if not getattr(cred, "auth_method", "apikey") == "oauth":
@@ -148,7 +227,7 @@ def _run_oauth_login(
148
227
 
149
228
  @app.command("login")
150
229
  def auth_login(
151
- provider: str = typer.Argument(help="Provider name (e.g., openai, anthropic)"),
230
+ provider: Optional[str] = typer.Argument(None, help="Provider name (e.g., openai, anthropic); prompts a picker if omitted"),
152
231
  method: str = typer.Option("auto", "--method", help="Auth method: auto, apikey, or oauth"),
153
232
  key: Optional[str] = typer.Option(None, "--key", help="API key (will prompt if not provided)"),
154
233
  key_stdin: bool = typer.Option(False, "--key-stdin", help="Read API key from stdin"),
@@ -173,6 +252,15 @@ def auth_login(
173
252
  """
174
253
  output = get_output_controller()
175
254
 
255
+ # Provider is optional: when omitted, present a catalogue-driven picker so
256
+ # users don't need to know the exact provider id up front. The positional
257
+ # argument still works for scripts.
258
+ if not provider:
259
+ provider = _select_provider_interactively(output)
260
+ if not provider:
261
+ output.print_error("A provider is required")
262
+ raise typer.Exit(1)
263
+
176
264
  # Assemble any OAuth endpoint overrides supplied on the command line. These
177
265
  # let users sign in to a built-in provider that needs an app client id, or
178
266
  # to a provider not in the registry (e.g. a self-hosted gateway).
@@ -232,6 +320,7 @@ def auth_login(
232
320
  api_key = key
233
321
  else:
234
322
  # Prompt for key
323
+ _print_key_url_hint(provider, output)
235
324
  try:
236
325
  api_key = typer.prompt(
237
326
  f"Enter API key for {provider}",
@@ -357,46 +446,74 @@ def auth_list():
357
446
  try:
358
447
  store = CredentialStore()
359
448
  providers = store.list_providers()
360
-
361
- if not providers:
362
- output.print_info("No stored credentials")
363
- if output.is_json_mode:
364
- output.print_json({"providers": []})
365
- return
366
-
367
- # Get detailed info for each provider
449
+
450
+ # Get detailed info for each stored provider.
368
451
  provider_info = []
452
+ stored_names = set()
369
453
  for provider_name in providers:
370
454
  cred = store.get_credential(provider_name)
371
455
  if cred:
372
- info = {
456
+ stored_names.add(provider_name.lower())
457
+ provider_info.append({
373
458
  "provider": provider_name,
374
459
  "auth_method": cred.auth_method,
460
+ "source": "stored",
461
+ "env_var": None,
375
462
  "key_redacted": redact_key(cred.api_key),
376
463
  "base_url": cred.base_url,
377
464
  "model": cred.model,
378
465
  "expires_at": cred.expires_at,
379
466
  "expires": _format_expiry(cred),
380
- }
381
- provider_info.append(info)
382
-
467
+ })
468
+
469
+ # Fold in active environment-variable keys (redacted) so the live
470
+ # credential is unambiguous. An env key is ALWAYS surfaced when present
471
+ # — even if the provider also has a stored key — because the env value
472
+ # overrides the stored one at runtime, so hiding it would point users at
473
+ # the wrong secret when they inspect or rotate credentials.
474
+ for provider_name, (env_var, value) in _env_credentials().items():
475
+ provider_info.append({
476
+ "provider": provider_name,
477
+ "auth_method": "apikey",
478
+ "source": "env",
479
+ "env_var": env_var,
480
+ "key_redacted": redact_key(value),
481
+ "base_url": None,
482
+ "model": None,
483
+ "expires_at": None,
484
+ "expires": "(n/a)",
485
+ "overrides_stored": provider_name in stored_names,
486
+ })
487
+
488
+ if not provider_info:
489
+ output.print_info("No stored credentials")
490
+ if output.is_json_mode:
491
+ output.print_json({"providers": []})
492
+ return
493
+
383
494
  if output.is_json_mode:
384
495
  output.print_json({"providers": provider_info})
385
496
  else:
386
497
  # Create table
387
- headers = ["Provider", "Method", "Secret", "Expires", "Base URL", "Model"]
498
+ headers = ["Provider", "Method", "Source", "Secret", "Expires", "Base URL", "Model"]
388
499
  rows = []
389
500
  for info in provider_info:
501
+ source = info["source"]
502
+ if source == "env" and info.get("env_var"):
503
+ source = f"env ({info['env_var']})"
504
+ if info.get("overrides_stored"):
505
+ source += " [live, overrides stored]"
390
506
  rows.append([
391
507
  info["provider"],
392
508
  info["auth_method"],
509
+ source,
393
510
  info["key_redacted"],
394
511
  info["expires"],
395
512
  info["base_url"] or "(default)",
396
513
  info["model"] or "(none)"
397
514
  ])
398
515
 
399
- output.print_table(headers, rows, title="Stored Credentials")
516
+ output.print_table(headers, rows, title="Credentials")
400
517
 
401
518
  except Exception as e:
402
519
  output.print_error(f"Failed to list credentials: {e}")
@@ -425,6 +542,40 @@ def auth_status(
425
542
  # Check specific provider
426
543
  cred = store.get_credential(provider)
427
544
  if not cred:
545
+ # Fall back to an active environment-variable key so the user
546
+ # can tell the credential is live from the env, not just missing
547
+ # from the store.
548
+ env_var = _PROVIDER_ENV_KEYS.get(provider.lower())
549
+ env_value = os.environ.get(env_var) if env_var else None
550
+ if env_value:
551
+ format_valid, format_msg = validate_api_key(provider, env_value)
552
+ status_info = {
553
+ "provider": provider,
554
+ "status": "found",
555
+ "auth_method": "apikey",
556
+ "source": "env",
557
+ "env_var": env_var,
558
+ "key_redacted": redact_key(env_value),
559
+ "format_valid": format_valid,
560
+ "format_message": format_msg,
561
+ "base_url": None,
562
+ "model": None,
563
+ "expires_at": None,
564
+ "expires": "(n/a)",
565
+ }
566
+ if output.is_json_mode:
567
+ output.print_json(status_info)
568
+ else:
569
+ output.print_panel(
570
+ f"Provider: {provider}\n"
571
+ f"Method: apikey\n"
572
+ f"Source: env ({env_var})\n"
573
+ f"Secret: {redact_key(env_value)}\n"
574
+ f"Format: {'✅' if format_valid else '❌'} {format_msg}",
575
+ title=f"Credentials Status: {provider}"
576
+ )
577
+ return
578
+
428
579
  output.print_warning(f"No credentials found for {provider}")
429
580
  if output.is_json_mode:
430
581
  output.print_json({
@@ -433,17 +584,34 @@ def auth_status(
433
584
  })
434
585
  raise typer.Exit(1)
435
586
 
436
- # Basic format validation (OAuth tokens have no static format)
587
+ # A live env var overrides the stored credential at runtime. When
588
+ # both exist, validate/report the env key (the credential actually
589
+ # in effect) so status never claims the wrong secret is active.
590
+ env_var = _PROVIDER_ENV_KEYS.get(provider.lower())
591
+ env_value = os.environ.get(env_var) if env_var else None
592
+ active_from_env = bool(env_value) and not cred.is_oauth()
593
+
437
594
  if cred.is_oauth():
438
595
  format_valid, format_msg = True, "OAuth token"
596
+ secret = cred.api_key
597
+ source = "stored"
598
+ elif active_from_env:
599
+ format_valid, format_msg = validate_api_key(provider, env_value)
600
+ secret = env_value
601
+ source = f"env ({env_var})"
439
602
  else:
440
603
  format_valid, format_msg = validate_api_key(provider, cred.api_key)
441
-
604
+ secret = cred.api_key
605
+ source = "stored"
606
+
442
607
  status_info = {
443
608
  "provider": provider,
444
609
  "status": "found",
445
610
  "auth_method": cred.auth_method,
446
- "key_redacted": redact_key(cred.api_key),
611
+ "source": source,
612
+ "env_var": env_var if active_from_env else None,
613
+ "overrides_stored": active_from_env,
614
+ "key_redacted": redact_key(secret),
447
615
  "format_valid": format_valid,
448
616
  "format_message": format_msg,
449
617
  "base_url": cred.base_url,
@@ -454,9 +622,11 @@ def auth_status(
454
622
 
455
623
  # Live validation if requested
456
624
  if validate:
457
- # Use a freshly-refreshed token for OAuth so we don't validate a
458
- # stale mirrored access token.
459
- live_secret = store.get_valid_token(provider) if cred.is_oauth() else cred.api_key
625
+ if cred.is_oauth():
626
+ # Freshly-refreshed token so we don't validate a stale mirror.
627
+ live_secret = store.get_valid_token(provider)
628
+ else:
629
+ live_secret = secret
460
630
  live_valid, live_msg = _validate_with_live_call(provider, live_secret, cred.base_url)
461
631
  status_info["live_valid"] = live_valid
462
632
  status_info["live_message"] = live_msg
@@ -467,7 +637,9 @@ def auth_status(
467
637
  output.print_panel(
468
638
  f"Provider: {provider}\n"
469
639
  f"Method: {cred.auth_method}\n"
470
- f"Secret: {redact_key(cred.api_key)}\n"
640
+ f"Source: {source}\n"
641
+ + ("Note: env var overrides stored key at runtime\n" if active_from_env else "") +
642
+ f"Secret: {redact_key(secret)}\n"
471
643
  + (f"Expires: {_format_expiry(cred)}\n" if cred.is_oauth() else "") +
472
644
  f"Format: {'✅' if format_valid else '❌'} {format_msg}\n"
473
645
  + (f"Live Test: {'✅' if status_info.get('live_valid') else '❌'} {status_info.get('live_message', 'Not tested')}\n" if validate else "") +
@@ -479,17 +651,13 @@ def auth_status(
479
651
  else:
480
652
  # Check all providers
481
653
  providers = store.list_providers()
482
-
483
- if not providers:
484
- output.print_info("No stored credentials")
485
- if output.is_json_mode:
486
- output.print_json({"providers": []})
487
- return
488
-
654
+
489
655
  all_status = []
656
+ stored_names = set()
490
657
  for provider_name in providers:
491
658
  cred = store.get_credential(provider_name)
492
659
  if cred:
660
+ stored_names.add(provider_name.lower())
493
661
  if cred.is_oauth():
494
662
  format_valid, format_msg = True, "OAuth token"
495
663
  else:
@@ -498,6 +666,7 @@ def auth_status(
498
666
  status = {
499
667
  "provider": provider_name,
500
668
  "auth_method": cred.auth_method,
669
+ "source": "stored",
501
670
  "key_redacted": redact_key(cred.api_key),
502
671
  "format_valid": format_valid,
503
672
  "format_message": format_msg,
@@ -516,12 +685,42 @@ def auth_status(
516
685
  status["live_message"] = live_msg
517
686
 
518
687
  all_status.append(status)
519
-
688
+
689
+ # Fold in active env-var keys (redacted), marked env-sourced. Always
690
+ # surfaced even when the provider also has a stored key, since the
691
+ # env value is the credential in effect at runtime.
692
+ for provider_name, (env_var, value) in _env_credentials().items():
693
+ format_valid, format_msg = validate_api_key(provider_name, value)
694
+ source = f"env ({env_var})"
695
+ if provider_name in stored_names:
696
+ source += " [live, overrides stored]"
697
+ status = {
698
+ "provider": provider_name,
699
+ "auth_method": "apikey",
700
+ "source": source,
701
+ "key_redacted": redact_key(value),
702
+ "format_valid": format_valid,
703
+ "format_message": format_msg,
704
+ "expires_at": None,
705
+ "expires": "(n/a)",
706
+ }
707
+ if validate:
708
+ live_valid, live_msg = _validate_with_live_call(provider_name, value, None)
709
+ status["live_valid"] = live_valid
710
+ status["live_message"] = live_msg
711
+ all_status.append(status)
712
+
713
+ if not all_status:
714
+ output.print_info("No stored credentials")
715
+ if output.is_json_mode:
716
+ output.print_json({"providers": []})
717
+ return
718
+
520
719
  if output.is_json_mode:
521
720
  output.print_json({"providers": all_status})
522
721
  else:
523
722
  # Create table
524
- headers = ["Provider", "Method", "Secret", "Expires", "Format"]
723
+ headers = ["Provider", "Method", "Source", "Secret", "Expires", "Format"]
525
724
  if validate:
526
725
  headers.append("Live Test")
527
726
 
@@ -530,6 +729,7 @@ def auth_status(
530
729
  row = [
531
730
  status["provider"],
532
731
  status["auth_method"],
732
+ status.get("source", "stored"),
533
733
  status["key_redacted"],
534
734
  status["expires"],
535
735
  "✅" if status["format_valid"] else "❌"
@@ -87,16 +87,18 @@ class SetupHandler(CommandHandler):
87
87
  return 1
88
88
 
89
89
  providers = self._provider_defaults()
90
-
91
- if provider not in providers:
92
- output.print_error(f"Unknown provider: {provider}")
93
- output.console.print("Valid providers: openai, anthropic, google, ollama, custom")
94
- return 1
95
-
96
- provider_info = providers[provider]
97
- provider_name = provider_info[0]
98
- env_key = provider_info[1]
99
- default_model = provider_info[2]
90
+
91
+ if provider in providers:
92
+ provider_info = providers[provider]
93
+ provider_name = provider_info[0]
94
+ env_key = provider_info[1]
95
+ default_model = provider_info[2]
96
+ else:
97
+ # Accept any catalogue-known provider (Groq, OpenRouter, Mistral, …)
98
+ # by deriving its env-var/default rather than rejecting it.
99
+ env_key, default_model, provider_name = self._provider_setup_info(
100
+ provider, providers
101
+ )
100
102
 
101
103
  # Validate API key requirement
102
104
  if env_key:
@@ -173,26 +175,24 @@ class SetupHandler(CommandHandler):
173
175
 
174
176
  # 2. Provider selection (only if nothing detected / accepted)
175
177
  if provider_id is None:
178
+ menu = self._catalogue_provider_choices()
176
179
  output.console.print("[bold]1. Choose your LLM provider:[/bold]")
177
- output.console.print(" 1) OpenAI (GPT-4o, GPT-4, GPT-3.5)")
178
- output.console.print(" 2) Anthropic (Claude)")
179
- output.console.print(" 3) Google (Gemini)")
180
- output.console.print(" 4) Ollama (Local models)")
181
- output.console.print(" 5) Custom provider")
182
-
180
+ for choice, (pid, _env_key, _model, name) in menu.items():
181
+ label = "Custom provider" if pid == "custom" else name
182
+ output.console.print(f" {choice}) {label}")
183
+
183
184
  provider_choice = Prompt.ask(
184
185
  "\nSelect provider",
185
- choices=["1", "2", "3", "4", "5"],
186
+ choices=list(menu.keys()),
186
187
  default="1"
187
188
  )
188
189
 
189
- provider_id, env_key, default_model, provider_name = (
190
- self._provider_menu()[provider_choice]
191
- )
190
+ provider_id, env_key, default_model, provider_name = menu[provider_choice]
192
191
 
193
192
  # API key input with live validation + re-prompt loop
194
193
  if env_key:
195
194
  output.console.print(f"\n[bold]2. Enter your {provider_name} API key:[/bold]")
195
+ self._print_key_url_hint(provider_id, output)
196
196
 
197
197
  existing_key = os.getenv(env_key)
198
198
  if existing_key and Confirm.ask(
@@ -291,7 +291,12 @@ class SetupHandler(CommandHandler):
291
291
  }
292
292
 
293
293
  def _provider_menu(self) -> Dict[str, tuple]:
294
- """Numeric provider menu → (provider_id, env_key, default_model, name)."""
294
+ """Numeric provider menu → (provider_id, env_key, default_model, name).
295
+
296
+ Retained for backward compatibility / non-interactive callers. The
297
+ interactive wizard now uses the catalogue-driven picker in
298
+ ``_catalogue_provider_choices``.
299
+ """
295
300
  defaults = self._provider_defaults()
296
301
  ordered = ["openai", "anthropic", "google", "ollama", "custom"]
297
302
  menu = {}
@@ -300,6 +305,96 @@ class SetupHandler(CommandHandler):
300
305
  menu[str(idx)] = (pid, env_key, model, name)
301
306
  return menu
302
307
 
308
+ def _catalogue_provider_choices(self) -> Dict[str, tuple]:
309
+ """Numeric picker keyed "1".. → (provider_id, env_key, default_model, name).
310
+
311
+ Catalogue-driven: unions the curated ``_provider_defaults`` entries with
312
+ every provider enumerated by ``ModelCatalogue`` so onboarding covers the
313
+ full set of supported providers (Groq, OpenRouter, Mistral, DeepSeek,
314
+ xAI, …) rather than a hardcoded five. Always ends with a "custom"
315
+ escape hatch.
316
+ """
317
+ defaults = self._provider_defaults()
318
+
319
+ ordered = ["openai", "anthropic", "google", "ollama"]
320
+
321
+ catalogue_providers = []
322
+ try:
323
+ from praisonai_code.llm.catalogue import ModelCatalogue
324
+
325
+ catalogue_providers = ModelCatalogue().list_providers()
326
+ except Exception:
327
+ catalogue_providers = []
328
+
329
+ for pid in catalogue_providers:
330
+ if pid not in ordered and pid not in ("custom",):
331
+ ordered.append(pid)
332
+
333
+ menu: Dict[str, tuple] = {}
334
+ idx = 1
335
+ for pid in ordered:
336
+ env_key, default_model, name = self._provider_setup_info(pid, defaults)
337
+ menu[str(idx)] = (pid, env_key, default_model, name)
338
+ idx += 1
339
+
340
+ # Custom escape hatch is always last.
341
+ menu[str(idx)] = ("custom", None, None, "Custom")
342
+ return menu
343
+
344
+ # Providers whose canonical API-key env-var is NOT ``<PROVIDER>_API_KEY``.
345
+ # Kept in sync with auth's ``_PROVIDER_ENV_KEYS`` so setup writes the same
346
+ # env-var name that auth (and the runtime) reads back.
347
+ _PROVIDER_ENV_KEY_OVERRIDES = {
348
+ "google": "GEMINI_API_KEY",
349
+ "gemini": "GEMINI_API_KEY",
350
+ "perplexity": "PERPLEXITYAI_API_KEY",
351
+ }
352
+
353
+ def _provider_setup_info(self, provider_id: str, defaults: Dict[str, tuple]) -> tuple:
354
+ """Resolve ``(env_key, default_model, name)`` for a provider id.
355
+
356
+ Prefers a curated ``_provider_defaults`` row; otherwise derives a
357
+ usable env-var, default model, and display name from the catalogue so
358
+ catalogue-only providers (Groq, OpenRouter, …) get a working
359
+ non-interactive path rather than falling through to the custom branch.
360
+ """
361
+ if provider_id in defaults:
362
+ name, env_key, model = defaults[provider_id]
363
+ return env_key, model, name
364
+ env_key = self._PROVIDER_ENV_KEY_OVERRIDES.get(
365
+ provider_id, f"{provider_id.upper()}_API_KEY"
366
+ )
367
+ default_model = self._catalogue_default_model(provider_id)
368
+ return env_key, default_model, provider_id.capitalize()
369
+
370
+ def _catalogue_default_model(self, provider_id: str) -> Optional[str]:
371
+ """Return a sensible default model for a catalogue provider, or ``None``.
372
+
373
+ Picks the first model the catalogue lists for the provider so
374
+ catalogue-known providers get a non-interactive default instead of
375
+ being rejected like a custom provider.
376
+ """
377
+ try:
378
+ from praisonai_code.llm.catalogue import ModelCatalogue
379
+
380
+ models = ModelCatalogue().list_models(provider=provider_id)
381
+ except Exception:
382
+ return None
383
+ if not models:
384
+ return None
385
+ return models[0].get("id")
386
+
387
+ def _print_key_url_hint(self, provider_id: str, output) -> None:
388
+ """Print a one-line 'Get your key' hint for well-known providers."""
389
+ try:
390
+ from praisonai_code.llm.catalogue import key_url_for_provider
391
+
392
+ url = key_url_for_provider(provider_id)
393
+ except Exception:
394
+ url = None
395
+ if url:
396
+ output.console.print(f"[dim]Get your key: {url}[/dim]")
397
+
303
398
  def _detect_provider_from_env(self) -> Optional[tuple]:
304
399
  """Detect a provider whose ``*_API_KEY`` is already in the environment.
305
400