cachibot 0.2.5.dev1__tar.gz → 0.2.6.dev1__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.
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/PKG-INFO +1 -1
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/pyproject.toml +1 -1
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/auth.py +29 -3
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/bots.py +9 -9
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/chats.py +9 -9
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/connections.py +13 -9
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/contacts.py +5 -5
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/documents.py +5 -5
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/instructions.py +4 -4
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/models.py +14 -4
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/skills.py +7 -1
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/work.py +46 -46
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/websocket.py +4 -2
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/.dockerignore +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/.github/workflows/dev.yml +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/.github/workflows/publish.yml +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/.gitignore +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/CLAUDE.md +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/CONTRIBUTING.md +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/Dockerfile +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/LICENSE +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/README.md +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/cachibot.example.toml +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/docker-compose.yml +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/.dockerignore +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/.gitignore +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/Dockerfile +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/eslint.config.js +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/index.html +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/package-lock.json +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/package.json +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/postcss.config.js +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/public/favicon.svg +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/App.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/auth.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/client.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/connections.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/contacts.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/index.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/knowledge.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/models.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/skills.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/api/websocket.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/auth/LoginPage.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/auth/ProtectedRoute.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/auth/SetupPage.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/chat/ChatPanel.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/chat/InputArea.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/chat/MessageList.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/chat/ThinkingIndicator.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/chat/ToolCallList.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/chat/UsageDisplay.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/BotIconRenderer.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/Button.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/Dialog/Dialog.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/Dialog/DialogContent.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/Dialog/DialogFooter.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/Dialog/DialogHeader.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/Dialog/DialogStepper.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/Dialog/index.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/MarkdownRenderer.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/ModelSelect.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/Spinner.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/common/ToolIconRenderer.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/ApprovalDialog.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotDialog.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/index.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/AppearanceStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/CapabilitiesStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/ConfirmStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/DetailsStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/ImportStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/MethodSelectStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/NamePickerStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/PersonalityStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/PreviewStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/PromptReviewStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/PurposeStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/CreateBotWizard/steps/TemplateSelectStep.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/SettingsDialog.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/dialogs/ToolConfigDialog.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/knowledge/DocumentList.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/knowledge/DocumentUploader.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/knowledge/InstructionsEditor.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/knowledge/index.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/layout/BotRail.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/layout/BotSidebar.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/layout/Header.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/layout/MainLayout.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/layout/Sidebar.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/marketplace/BotCard.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/marketplace/BotDetailDialog.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/marketplace/MarketplaceBrowser.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/marketplace/index.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/settings/BotConnectionsPanel.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/settings/ContactsPanel.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/AppSettingsView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/ChatView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/ConnectionsView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/DashboardView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/JobsView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/ModelsView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/SchedulesView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/SettingsView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/TasksView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/ToolsView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/UsersView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/WorkView.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/components/views/index.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/hooks/useCommands.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/hooks/useWebSocket.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/index.css +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/lib/language-detector.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/lib/prompt-generator.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/lib/utils.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/main.tsx +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/auth.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/bots.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/config.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/connections.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/contacts.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/creation-flow.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/creation.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/index.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/knowledge.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/models.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/stores/ui.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/types/index.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/src/vite-env.d.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/tailwind.config.js +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/tsconfig.json +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/tsconfig.tsbuildinfo +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/frontend/vite.config.ts +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/agent.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/auth.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/chat.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/config.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/creation.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/health.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/routes/marketplace.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/api/server.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/cli.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/config.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/data/marketplace_templates.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/auth.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/bot.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/capabilities.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/chat.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/chat_model.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/command.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/config.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/connection.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/job.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/knowledge.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/skill.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/websocket.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/models/work.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/adapters/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/adapters/base.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/adapters/discord.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/adapters/telegram.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/auth_service.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/bot_creation_service.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/command_processor.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/context_builder.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/document_processor.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/message_processor.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/name_generator.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/platform_manager.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/skills.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/services/vector_store.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/storage/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/storage/database.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/storage/repository.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/storage/user_repository.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/storage/work_repository.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/utils/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/src/cachibot/utils/markdown.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/tests/__init__.py +0 -0
- {cachibot-0.2.5.dev1 → cachibot-0.2.6.dev1}/tests/test_cachibot.py +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Authentication endpoints."""
|
|
2
2
|
|
|
3
|
+
import time
|
|
3
4
|
import uuid
|
|
5
|
+
from collections import defaultdict
|
|
4
6
|
from datetime import datetime
|
|
5
7
|
|
|
6
|
-
from fastapi import APIRouter, Depends, HTTPException, status
|
|
8
|
+
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
|
7
9
|
|
|
8
10
|
from cachibot.api.auth import get_admin_user, get_current_user
|
|
9
11
|
from cachibot.models.auth import (
|
|
@@ -26,6 +28,30 @@ from cachibot.storage.user_repository import UserRepository
|
|
|
26
28
|
|
|
27
29
|
router = APIRouter(prefix="/auth")
|
|
28
30
|
|
|
31
|
+
# Simple in-memory rate limiter for auth endpoints
|
|
32
|
+
_rate_limit_store: dict[str, list[float]] = defaultdict(list)
|
|
33
|
+
RATE_LIMIT_WINDOW = 60 # seconds
|
|
34
|
+
RATE_LIMIT_MAX_REQUESTS = 5 # max attempts per window
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def rate_limit_auth(request: Request) -> None:
|
|
38
|
+
"""Rate limit authentication endpoints by client IP."""
|
|
39
|
+
client_ip = request.client.host if request.client else "unknown"
|
|
40
|
+
now = time.monotonic()
|
|
41
|
+
|
|
42
|
+
# Prune old entries
|
|
43
|
+
_rate_limit_store[client_ip] = [
|
|
44
|
+
t for t in _rate_limit_store[client_ip] if now - t < RATE_LIMIT_WINDOW
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
if len(_rate_limit_store[client_ip]) >= RATE_LIMIT_MAX_REQUESTS:
|
|
48
|
+
raise HTTPException(
|
|
49
|
+
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
|
|
50
|
+
detail="Too many attempts. Please try again later.",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
_rate_limit_store[client_ip].append(now)
|
|
54
|
+
|
|
29
55
|
|
|
30
56
|
@router.get("/setup-required", response_model=SetupStatusResponse)
|
|
31
57
|
async def check_setup_required() -> SetupStatusResponse:
|
|
@@ -35,7 +61,7 @@ async def check_setup_required() -> SetupStatusResponse:
|
|
|
35
61
|
return SetupStatusResponse(setup_required=count == 0)
|
|
36
62
|
|
|
37
63
|
|
|
38
|
-
@router.post("/setup", response_model=LoginResponse)
|
|
64
|
+
@router.post("/setup", response_model=LoginResponse, dependencies=[Depends(rate_limit_auth)])
|
|
39
65
|
async def setup_initial_admin(request: SetupRequest) -> LoginResponse:
|
|
40
66
|
"""
|
|
41
67
|
Create the initial admin user (first-time setup).
|
|
@@ -103,7 +129,7 @@ async def setup_initial_admin(request: SetupRequest) -> LoginResponse:
|
|
|
103
129
|
)
|
|
104
130
|
|
|
105
131
|
|
|
106
|
-
@router.post("/login", response_model=LoginResponse)
|
|
132
|
+
@router.post("/login", response_model=LoginResponse, dependencies=[Depends(rate_limit_auth)])
|
|
107
133
|
async def login(request: LoginRequest) -> LoginResponse:
|
|
108
134
|
"""
|
|
109
135
|
Login with email or username and password.
|
|
@@ -9,7 +9,7 @@ from datetime import datetime
|
|
|
9
9
|
from fastapi import APIRouter, Depends, HTTPException
|
|
10
10
|
from pydantic import BaseModel, Field
|
|
11
11
|
|
|
12
|
-
from cachibot.api.auth import get_current_user
|
|
12
|
+
from cachibot.api.auth import get_current_user, require_bot_access
|
|
13
13
|
from cachibot.models.auth import User
|
|
14
14
|
from cachibot.models.bot import Bot, BotResponse
|
|
15
15
|
from cachibot.models.skill import BotSkillRequest, SkillResponse
|
|
@@ -49,7 +49,7 @@ async def list_bots(
|
|
|
49
49
|
@router.get("/{bot_id}")
|
|
50
50
|
async def get_bot(
|
|
51
51
|
bot_id: str,
|
|
52
|
-
user: User = Depends(
|
|
52
|
+
user: User = Depends(require_bot_access),
|
|
53
53
|
) -> BotResponse:
|
|
54
54
|
"""Get a specific bot."""
|
|
55
55
|
bot = await repo.get_bot(bot_id)
|
|
@@ -62,7 +62,7 @@ async def get_bot(
|
|
|
62
62
|
async def sync_bot(
|
|
63
63
|
bot_id: str,
|
|
64
64
|
body: BotSyncRequest,
|
|
65
|
-
user: User = Depends(
|
|
65
|
+
user: User = Depends(require_bot_access),
|
|
66
66
|
) -> BotResponse:
|
|
67
67
|
"""Sync a bot from frontend (create or update)."""
|
|
68
68
|
if body.id != bot_id:
|
|
@@ -88,7 +88,7 @@ async def sync_bot(
|
|
|
88
88
|
@router.delete("/{bot_id}", status_code=204)
|
|
89
89
|
async def delete_bot(
|
|
90
90
|
bot_id: str,
|
|
91
|
-
user: User = Depends(
|
|
91
|
+
user: User = Depends(require_bot_access),
|
|
92
92
|
) -> None:
|
|
93
93
|
"""Delete a bot."""
|
|
94
94
|
deleted = await repo.delete_bot(bot_id)
|
|
@@ -104,7 +104,7 @@ async def delete_bot(
|
|
|
104
104
|
@router.get("/{bot_id}/skills")
|
|
105
105
|
async def get_bot_skills(
|
|
106
106
|
bot_id: str,
|
|
107
|
-
user: User = Depends(
|
|
107
|
+
user: User = Depends(require_bot_access),
|
|
108
108
|
) -> list[SkillResponse]:
|
|
109
109
|
"""Get all activated skills for a bot."""
|
|
110
110
|
skill_defs = await skills_repo.get_bot_skill_definitions(bot_id)
|
|
@@ -114,7 +114,7 @@ async def get_bot_skills(
|
|
|
114
114
|
@router.get("/{bot_id}/skills/ids")
|
|
115
115
|
async def get_bot_skill_ids(
|
|
116
116
|
bot_id: str,
|
|
117
|
-
user: User = Depends(
|
|
117
|
+
user: User = Depends(require_bot_access),
|
|
118
118
|
) -> list[str]:
|
|
119
119
|
"""Get just the IDs of activated skills for a bot."""
|
|
120
120
|
return await skills_repo.get_bot_skills(bot_id)
|
|
@@ -124,7 +124,7 @@ async def get_bot_skill_ids(
|
|
|
124
124
|
async def activate_bot_skill(
|
|
125
125
|
bot_id: str,
|
|
126
126
|
body: BotSkillRequest,
|
|
127
|
-
user: User = Depends(
|
|
127
|
+
user: User = Depends(require_bot_access),
|
|
128
128
|
) -> dict:
|
|
129
129
|
"""Activate a skill for a bot."""
|
|
130
130
|
# Verify skill exists
|
|
@@ -140,7 +140,7 @@ async def activate_bot_skill(
|
|
|
140
140
|
async def deactivate_bot_skill(
|
|
141
141
|
bot_id: str,
|
|
142
142
|
skill_id: str,
|
|
143
|
-
user: User = Depends(
|
|
143
|
+
user: User = Depends(require_bot_access),
|
|
144
144
|
) -> None:
|
|
145
145
|
"""Deactivate a skill for a bot."""
|
|
146
146
|
deactivated = await skills_repo.deactivate_skill(bot_id, skill_id)
|
|
@@ -181,7 +181,7 @@ class BotImportResponse(BaseModel):
|
|
|
181
181
|
@router.get("/{bot_id}/export")
|
|
182
182
|
async def export_bot(
|
|
183
183
|
bot_id: str,
|
|
184
|
-
user: User = Depends(
|
|
184
|
+
user: User = Depends(require_bot_access),
|
|
185
185
|
) -> BotExportFormat:
|
|
186
186
|
"""
|
|
187
187
|
Export a bot configuration as JSON.
|
|
@@ -7,7 +7,7 @@ Endpoints for managing bot chats, including platform conversations.
|
|
|
7
7
|
from fastapi import APIRouter, Depends, HTTPException
|
|
8
8
|
from pydantic import BaseModel
|
|
9
9
|
|
|
10
|
-
from cachibot.api.auth import get_current_user
|
|
10
|
+
from cachibot.api.auth import get_current_user, require_bot_access
|
|
11
11
|
from cachibot.models.auth import User
|
|
12
12
|
from cachibot.models.chat_model import ChatResponse
|
|
13
13
|
from cachibot.models.knowledge import BotMessage
|
|
@@ -46,7 +46,7 @@ class MessageResponse(BaseModel):
|
|
|
46
46
|
async def list_chats(
|
|
47
47
|
bot_id: str,
|
|
48
48
|
include_archived: bool = False,
|
|
49
|
-
user: User = Depends(
|
|
49
|
+
user: User = Depends(require_bot_access),
|
|
50
50
|
) -> list[ChatResponse]:
|
|
51
51
|
"""Get all chats for a bot (including platform chats). Excludes archived by default."""
|
|
52
52
|
chats = await chat_repo.get_chats_by_bot(bot_id, include_archived=include_archived)
|
|
@@ -71,7 +71,7 @@ class ArchiveResponse(BaseModel):
|
|
|
71
71
|
@router.post("/_clear", status_code=200)
|
|
72
72
|
async def clear_all_chats(
|
|
73
73
|
bot_id: str,
|
|
74
|
-
user: User = Depends(
|
|
74
|
+
user: User = Depends(require_bot_access),
|
|
75
75
|
) -> ClearDataResponse:
|
|
76
76
|
"""Delete all chats and messages for a bot (platform data cleanup)."""
|
|
77
77
|
# Delete all messages first (they reference chats)
|
|
@@ -90,7 +90,7 @@ async def clear_all_chats(
|
|
|
90
90
|
async def get_chat(
|
|
91
91
|
bot_id: str,
|
|
92
92
|
chat_id: str,
|
|
93
|
-
user: User = Depends(
|
|
93
|
+
user: User = Depends(require_bot_access),
|
|
94
94
|
) -> ChatResponse:
|
|
95
95
|
"""Get a specific chat."""
|
|
96
96
|
chat = await chat_repo.get_chat(chat_id)
|
|
@@ -104,7 +104,7 @@ async def get_chat_messages(
|
|
|
104
104
|
bot_id: str,
|
|
105
105
|
chat_id: str,
|
|
106
106
|
limit: int = 50,
|
|
107
|
-
user: User = Depends(
|
|
107
|
+
user: User = Depends(require_bot_access),
|
|
108
108
|
) -> list[MessageResponse]:
|
|
109
109
|
"""Get messages for a chat."""
|
|
110
110
|
chat = await chat_repo.get_chat(chat_id)
|
|
@@ -119,7 +119,7 @@ async def get_chat_messages(
|
|
|
119
119
|
async def delete_chat(
|
|
120
120
|
bot_id: str,
|
|
121
121
|
chat_id: str,
|
|
122
|
-
user: User = Depends(
|
|
122
|
+
user: User = Depends(require_bot_access),
|
|
123
123
|
) -> None:
|
|
124
124
|
"""Delete a chat permanently (including messages)."""
|
|
125
125
|
chat = await chat_repo.get_chat(chat_id)
|
|
@@ -136,7 +136,7 @@ async def delete_chat(
|
|
|
136
136
|
async def clear_chat_messages(
|
|
137
137
|
bot_id: str,
|
|
138
138
|
chat_id: str,
|
|
139
|
-
user: User = Depends(
|
|
139
|
+
user: User = Depends(require_bot_access),
|
|
140
140
|
) -> ClearDataResponse:
|
|
141
141
|
"""Clear all messages for a chat but keep the chat itself."""
|
|
142
142
|
chat = await chat_repo.get_chat(chat_id)
|
|
@@ -151,7 +151,7 @@ async def clear_chat_messages(
|
|
|
151
151
|
async def archive_chat(
|
|
152
152
|
bot_id: str,
|
|
153
153
|
chat_id: str,
|
|
154
|
-
user: User = Depends(
|
|
154
|
+
user: User = Depends(require_bot_access),
|
|
155
155
|
) -> ArchiveResponse:
|
|
156
156
|
"""Archive a chat. Archived chats are hidden and won't receive new messages."""
|
|
157
157
|
chat = await chat_repo.get_chat(chat_id)
|
|
@@ -166,7 +166,7 @@ async def archive_chat(
|
|
|
166
166
|
async def unarchive_chat(
|
|
167
167
|
bot_id: str,
|
|
168
168
|
chat_id: str,
|
|
169
|
-
user: User = Depends(
|
|
169
|
+
user: User = Depends(require_bot_access),
|
|
170
170
|
) -> ArchiveResponse:
|
|
171
171
|
"""Unarchive a chat. It will appear in listings and receive messages again."""
|
|
172
172
|
chat = await chat_repo.get_chat(chat_id)
|
|
@@ -4,18 +4,21 @@ Connections API Routes
|
|
|
4
4
|
CRUD endpoints for managing bot platform connections.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import logging
|
|
7
8
|
import uuid
|
|
8
9
|
from datetime import datetime
|
|
9
10
|
|
|
10
11
|
from fastapi import APIRouter, Depends, HTTPException
|
|
11
12
|
from pydantic import BaseModel
|
|
12
13
|
|
|
13
|
-
from cachibot.api.auth import get_current_user
|
|
14
|
+
from cachibot.api.auth import get_current_user, require_bot_access
|
|
14
15
|
from cachibot.models.auth import User
|
|
15
16
|
from cachibot.models.connection import BotConnection, ConnectionPlatform, ConnectionStatus
|
|
16
17
|
from cachibot.services.platform_manager import get_platform_manager
|
|
17
18
|
from cachibot.storage.repository import ConnectionRepository
|
|
18
19
|
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
19
22
|
router = APIRouter(prefix="/api/bots/{bot_id}/connections", tags=["connections"])
|
|
20
23
|
|
|
21
24
|
# Repository instance
|
|
@@ -79,7 +82,7 @@ class ConnectionResponse(BaseModel):
|
|
|
79
82
|
@router.get("")
|
|
80
83
|
async def list_connections(
|
|
81
84
|
bot_id: str,
|
|
82
|
-
user: User = Depends(
|
|
85
|
+
user: User = Depends(require_bot_access),
|
|
83
86
|
) -> list[ConnectionResponse]:
|
|
84
87
|
"""Get all connections for a bot."""
|
|
85
88
|
connections = await repo.get_connections_by_bot(bot_id)
|
|
@@ -90,7 +93,7 @@ async def list_connections(
|
|
|
90
93
|
async def create_connection(
|
|
91
94
|
bot_id: str,
|
|
92
95
|
body: ConnectionCreate,
|
|
93
|
-
user: User = Depends(
|
|
96
|
+
user: User = Depends(require_bot_access),
|
|
94
97
|
) -> ConnectionResponse:
|
|
95
98
|
"""Create a new connection for a bot."""
|
|
96
99
|
if not body.name.strip():
|
|
@@ -126,7 +129,7 @@ async def create_connection(
|
|
|
126
129
|
async def get_connection(
|
|
127
130
|
bot_id: str,
|
|
128
131
|
connection_id: str,
|
|
129
|
-
user: User = Depends(
|
|
132
|
+
user: User = Depends(require_bot_access),
|
|
130
133
|
) -> ConnectionResponse:
|
|
131
134
|
"""Get a specific connection."""
|
|
132
135
|
connection = await repo.get_connection(connection_id)
|
|
@@ -140,7 +143,7 @@ async def update_connection(
|
|
|
140
143
|
bot_id: str,
|
|
141
144
|
connection_id: str,
|
|
142
145
|
body: ConnectionUpdate,
|
|
143
|
-
user: User = Depends(
|
|
146
|
+
user: User = Depends(require_bot_access),
|
|
144
147
|
) -> ConnectionResponse:
|
|
145
148
|
"""Update an existing connection."""
|
|
146
149
|
connection = await repo.get_connection(connection_id)
|
|
@@ -166,7 +169,7 @@ async def update_connection(
|
|
|
166
169
|
async def delete_connection(
|
|
167
170
|
bot_id: str,
|
|
168
171
|
connection_id: str,
|
|
169
|
-
user: User = Depends(
|
|
172
|
+
user: User = Depends(require_bot_access),
|
|
170
173
|
) -> None:
|
|
171
174
|
"""Delete a connection."""
|
|
172
175
|
connection = await repo.get_connection(connection_id)
|
|
@@ -185,7 +188,7 @@ async def delete_connection(
|
|
|
185
188
|
async def connect_platform(
|
|
186
189
|
bot_id: str,
|
|
187
190
|
connection_id: str,
|
|
188
|
-
user: User = Depends(
|
|
191
|
+
user: User = Depends(require_bot_access),
|
|
189
192
|
) -> ConnectionResponse:
|
|
190
193
|
"""Start a platform connection."""
|
|
191
194
|
connection = await repo.get_connection(connection_id)
|
|
@@ -199,14 +202,15 @@ async def connect_platform(
|
|
|
199
202
|
connection = await repo.get_connection(connection_id)
|
|
200
203
|
return ConnectionResponse.from_connection(connection)
|
|
201
204
|
except Exception as e:
|
|
202
|
-
|
|
205
|
+
logger.error(f"Failed to connect {connection_id}: {e}")
|
|
206
|
+
raise HTTPException(status_code=500, detail="Failed to start connection")
|
|
203
207
|
|
|
204
208
|
|
|
205
209
|
@router.post("/{connection_id}/disconnect")
|
|
206
210
|
async def disconnect_platform(
|
|
207
211
|
bot_id: str,
|
|
208
212
|
connection_id: str,
|
|
209
|
-
user: User = Depends(
|
|
213
|
+
user: User = Depends(require_bot_access),
|
|
210
214
|
) -> ConnectionResponse:
|
|
211
215
|
"""Stop a platform connection."""
|
|
212
216
|
connection = await repo.get_connection(connection_id)
|
|
@@ -61,7 +61,7 @@ class ContactResponse(BaseModel):
|
|
|
61
61
|
@router.get("")
|
|
62
62
|
async def list_contacts(
|
|
63
63
|
bot_id: str,
|
|
64
|
-
user: User = Depends(
|
|
64
|
+
user: User = Depends(require_bot_access),
|
|
65
65
|
) -> list[ContactResponse]:
|
|
66
66
|
"""Get all contacts for a bot."""
|
|
67
67
|
contacts = await repo.get_contacts_by_bot(bot_id)
|
|
@@ -72,7 +72,7 @@ async def list_contacts(
|
|
|
72
72
|
async def create_contact(
|
|
73
73
|
bot_id: str,
|
|
74
74
|
body: ContactCreate,
|
|
75
|
-
user: User = Depends(
|
|
75
|
+
user: User = Depends(require_bot_access),
|
|
76
76
|
) -> ContactResponse:
|
|
77
77
|
"""Create a new contact for a bot."""
|
|
78
78
|
if not body.name.strip():
|
|
@@ -95,7 +95,7 @@ async def create_contact(
|
|
|
95
95
|
async def get_contact(
|
|
96
96
|
bot_id: str,
|
|
97
97
|
contact_id: str,
|
|
98
|
-
user: User = Depends(
|
|
98
|
+
user: User = Depends(require_bot_access),
|
|
99
99
|
) -> ContactResponse:
|
|
100
100
|
"""Get a specific contact."""
|
|
101
101
|
contact = await repo.get_contact(contact_id)
|
|
@@ -109,7 +109,7 @@ async def update_contact(
|
|
|
109
109
|
bot_id: str,
|
|
110
110
|
contact_id: str,
|
|
111
111
|
body: ContactUpdate,
|
|
112
|
-
user: User = Depends(
|
|
112
|
+
user: User = Depends(require_bot_access),
|
|
113
113
|
) -> ContactResponse:
|
|
114
114
|
"""Update an existing contact."""
|
|
115
115
|
contact = await repo.get_contact(contact_id)
|
|
@@ -131,7 +131,7 @@ async def update_contact(
|
|
|
131
131
|
async def delete_contact(
|
|
132
132
|
bot_id: str,
|
|
133
133
|
contact_id: str,
|
|
134
|
-
user: User = Depends(
|
|
134
|
+
user: User = Depends(require_bot_access),
|
|
135
135
|
) -> None:
|
|
136
136
|
"""Delete a contact."""
|
|
137
137
|
contact = await repo.get_contact(contact_id)
|
|
@@ -15,7 +15,7 @@ import aiofiles
|
|
|
15
15
|
from fastapi import APIRouter, BackgroundTasks, Depends, File, HTTPException, UploadFile
|
|
16
16
|
from pydantic import BaseModel
|
|
17
17
|
|
|
18
|
-
from cachibot.api.auth import get_current_user
|
|
18
|
+
from cachibot.api.auth import get_current_user, require_bot_access
|
|
19
19
|
from cachibot.models.auth import User
|
|
20
20
|
from cachibot.models.knowledge import Document, DocumentStatus
|
|
21
21
|
from cachibot.services.document_processor import get_document_processor
|
|
@@ -85,7 +85,7 @@ async def upload_document(
|
|
|
85
85
|
bot_id: str,
|
|
86
86
|
file: Annotated[UploadFile, File()],
|
|
87
87
|
background_tasks: BackgroundTasks,
|
|
88
|
-
user: User = Depends(
|
|
88
|
+
user: User = Depends(require_bot_access),
|
|
89
89
|
) -> UploadResponse:
|
|
90
90
|
"""
|
|
91
91
|
Upload a document to the bot's knowledge base.
|
|
@@ -163,7 +163,7 @@ async def upload_document(
|
|
|
163
163
|
@router.get("/", response_model=list[DocumentResponse])
|
|
164
164
|
async def list_documents(
|
|
165
165
|
bot_id: str,
|
|
166
|
-
user: User = Depends(
|
|
166
|
+
user: User = Depends(require_bot_access),
|
|
167
167
|
) -> list[DocumentResponse]:
|
|
168
168
|
"""List all documents for a bot."""
|
|
169
169
|
repo = KnowledgeRepository()
|
|
@@ -175,7 +175,7 @@ async def list_documents(
|
|
|
175
175
|
async def get_document(
|
|
176
176
|
bot_id: str,
|
|
177
177
|
document_id: str,
|
|
178
|
-
user: User = Depends(
|
|
178
|
+
user: User = Depends(require_bot_access),
|
|
179
179
|
) -> DocumentResponse:
|
|
180
180
|
"""Get a specific document."""
|
|
181
181
|
repo = KnowledgeRepository()
|
|
@@ -191,7 +191,7 @@ async def get_document(
|
|
|
191
191
|
async def delete_document(
|
|
192
192
|
bot_id: str,
|
|
193
193
|
document_id: str,
|
|
194
|
-
user: User = Depends(
|
|
194
|
+
user: User = Depends(require_bot_access),
|
|
195
195
|
) -> dict:
|
|
196
196
|
"""Delete a document and its chunks."""
|
|
197
197
|
repo = KnowledgeRepository()
|
|
@@ -7,7 +7,7 @@ Endpoints for managing bot custom instructions.
|
|
|
7
7
|
from fastapi import APIRouter, Depends
|
|
8
8
|
from pydantic import BaseModel, Field
|
|
9
9
|
|
|
10
|
-
from cachibot.api.auth import get_current_user
|
|
10
|
+
from cachibot.api.auth import get_current_user, require_bot_access
|
|
11
11
|
from cachibot.models.auth import User
|
|
12
12
|
from cachibot.storage.repository import KnowledgeRepository
|
|
13
13
|
|
|
@@ -30,7 +30,7 @@ class InstructionsUpdate(BaseModel):
|
|
|
30
30
|
@router.get("/", response_model=InstructionsResponse)
|
|
31
31
|
async def get_instructions(
|
|
32
32
|
bot_id: str,
|
|
33
|
-
user: User = Depends(
|
|
33
|
+
user: User = Depends(require_bot_access),
|
|
34
34
|
) -> InstructionsResponse:
|
|
35
35
|
"""Get custom instructions for a bot."""
|
|
36
36
|
repo = KnowledgeRepository()
|
|
@@ -49,7 +49,7 @@ async def get_instructions(
|
|
|
49
49
|
async def update_instructions(
|
|
50
50
|
bot_id: str,
|
|
51
51
|
data: InstructionsUpdate,
|
|
52
|
-
user: User = Depends(
|
|
52
|
+
user: User = Depends(require_bot_access),
|
|
53
53
|
) -> InstructionsResponse:
|
|
54
54
|
"""Update custom instructions for a bot."""
|
|
55
55
|
repo = KnowledgeRepository()
|
|
@@ -64,7 +64,7 @@ async def update_instructions(
|
|
|
64
64
|
@router.delete("/")
|
|
65
65
|
async def delete_instructions(
|
|
66
66
|
bot_id: str,
|
|
67
|
-
user: User = Depends(
|
|
67
|
+
user: User = Depends(require_bot_access),
|
|
68
68
|
) -> dict:
|
|
69
69
|
"""Delete custom instructions for a bot."""
|
|
70
70
|
repo = KnowledgeRepository()
|
|
@@ -7,9 +7,12 @@ import os
|
|
|
7
7
|
import re
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
-
from fastapi import APIRouter
|
|
10
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
11
11
|
from pydantic import BaseModel, Field
|
|
12
12
|
|
|
13
|
+
from cachibot.api.auth import get_current_user
|
|
14
|
+
from cachibot.models.auth import User
|
|
15
|
+
|
|
13
16
|
logger = logging.getLogger("cachibot.api.models")
|
|
14
17
|
router = APIRouter()
|
|
15
18
|
|
|
@@ -52,7 +55,7 @@ class DefaultModelUpdate(BaseModel):
|
|
|
52
55
|
|
|
53
56
|
|
|
54
57
|
@router.get("/models", response_model=ModelsResponse)
|
|
55
|
-
async def get_models() -> ModelsResponse:
|
|
58
|
+
async def get_models(user: User = Depends(get_current_user)) -> ModelsResponse:
|
|
56
59
|
"""
|
|
57
60
|
Get all available models from configured providers.
|
|
58
61
|
|
|
@@ -188,14 +191,17 @@ async def get_models() -> ModelsResponse:
|
|
|
188
191
|
|
|
189
192
|
|
|
190
193
|
@router.get("/models/default", response_model=DefaultModelResponse)
|
|
191
|
-
async def get_default_model() -> DefaultModelResponse:
|
|
194
|
+
async def get_default_model(user: User = Depends(get_current_user)) -> DefaultModelResponse:
|
|
192
195
|
"""Get the current default model."""
|
|
193
196
|
model = os.getenv("CACHIBOT_DEFAULT_MODEL", DEFAULT_MODEL)
|
|
194
197
|
return DefaultModelResponse(model=model)
|
|
195
198
|
|
|
196
199
|
|
|
197
200
|
@router.put("/models/default")
|
|
198
|
-
async def set_default_model(
|
|
201
|
+
async def set_default_model(
|
|
202
|
+
body: DefaultModelUpdate,
|
|
203
|
+
user: User = Depends(get_current_user),
|
|
204
|
+
) -> dict:
|
|
199
205
|
"""
|
|
200
206
|
Set the default model.
|
|
201
207
|
|
|
@@ -205,6 +211,10 @@ async def set_default_model(body: DefaultModelUpdate) -> dict:
|
|
|
205
211
|
key = "CACHIBOT_DEFAULT_MODEL"
|
|
206
212
|
value = body.model
|
|
207
213
|
|
|
214
|
+
# Reject values with newlines or control chars to prevent .env injection
|
|
215
|
+
if any(c in value for c in ("\n", "\r", "\0")):
|
|
216
|
+
raise HTTPException(status_code=400, detail="Invalid model ID")
|
|
217
|
+
|
|
208
218
|
# Update .env file
|
|
209
219
|
content = ""
|
|
210
220
|
if ENV_PATH.exists():
|
|
@@ -84,9 +84,15 @@ async def create_skill(
|
|
|
84
84
|
else:
|
|
85
85
|
filename = "new-skill.md"
|
|
86
86
|
|
|
87
|
+
# Sanitize filename to prevent path traversal
|
|
88
|
+
base_name = filename.rsplit(".", 1)[0]
|
|
89
|
+
base_name = re.sub(r"[^a-z0-9_-]+", "-", base_name.lower()).strip("-")
|
|
90
|
+
if not base_name:
|
|
91
|
+
base_name = "new-skill"
|
|
92
|
+
|
|
87
93
|
# Create in user's .claude/skills directory
|
|
88
94
|
# Create skill directory with SKILL.md inside
|
|
89
|
-
skill_dir = USER_SKILLS_DIR /
|
|
95
|
+
skill_dir = USER_SKILLS_DIR / base_name
|
|
90
96
|
counter = 1
|
|
91
97
|
while skill_dir.exists():
|
|
92
98
|
base = filename.rsplit(".", 1)[0]
|