devops-bot-sdk 1.7.46__tar.gz → 1.7.47__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.
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/PKG-INFO +1 -1
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/devops_bot_sdk.egg-info/SOURCES.txt +1 -1
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/cli.py +7 -7
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/config.py +21 -83
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/faqs.py +10 -13
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/firewall.py +112 -47
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/help_data/areas.json +4 -4
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/help_data/commands.json +2 -2
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/help_data/help.json +1 -1
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/help_data/pages.json +1 -1
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/help_data/releases.json +10 -0
- devops_bot_sdk-1.7.47/sdk/help_data/snapshot.json +17 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/helpdesk_kb.md +30 -33
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/api.py +28 -52
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/app.js +19 -37
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/index.html +7 -15
- devops_bot_sdk-1.7.47/tests/test_backend_only_allowlist.py +155 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_firewall_open_on_update.py +44 -1
- devops_bot_sdk-1.7.46/sdk/help_data/snapshot.json +0 -17
- devops_bot_sdk-1.7.46/tests/test_one_allowed_ip.py +0 -153
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/README.md +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/pyproject.toml +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/collectors/windows_eventlog.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/models.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/redact.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/shipper.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/activity/spool.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agent_sharing.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/claude_usage.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/client.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/custom_agents.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/deferred_tickets.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/fallback_handoff.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/help_actions.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/helpdesk.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/instructions_template.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/log_colors.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/model_policy.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/router.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/schedule_tickets.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/schedule_timeline.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/scope_plan.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/test.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/test_env.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ticket_board.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ticket_bulk.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ticket_plan.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ticket_pools.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/tickets.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/models.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/faqs.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/help.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/vps_identity.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/setup.cfg +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_activity_claude_runs.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_activity_collectors.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_activity_core.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_activity_live_tracking.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_activity_pipeline.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_crud_cli.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_sharing.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_approval_park.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_auto_ticket_queue.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_claude_usage_gate.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_create_safe_retry.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_deferred_tickets.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_fallback_cli.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_fallback_cli_upgrade.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_fallback_rate_limit.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_faqs.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_graph_off_by_default.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_help_actions.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_help_attachments.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_help_questions.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_helpdesk.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_infographic_data.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_instructions_template.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_limit_fallback_handoff.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_log_colors_backlog.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_model_policy.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_panel_help.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_panel_ticket_schedules.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_parallel_tickets.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_plan_ahead.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_pool_pr_modes.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_quiet_run_log.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_schedule_tickets.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_schedule_timeline.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_scheduler_transient_retry.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_stale_panel_restart.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_theme_switch.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_board.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_bulk.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_cli.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_detail_view.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_plan.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_pools.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_schedule_and_agent.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ui_bruteforce_guard.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_ui_no_native_dialogs.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_updater_ignores_cwd.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_updater_pep668.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_version_flag.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_vps_only_reporting.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_windows_panel_task.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_windows_stop_and_pidfile.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_windows_task_paths.py +0 -0
- {devops_bot_sdk-1.7.46 → devops_bot_sdk-1.7.47}/tests/test_write_hook.py +0 -0
|
@@ -190,6 +190,7 @@ tests/test_allowed_tools_config.py
|
|
|
190
190
|
tests/test_approval_park.py
|
|
191
191
|
tests/test_auto_agent_dispatch.py
|
|
192
192
|
tests/test_auto_ticket_queue.py
|
|
193
|
+
tests/test_backend_only_allowlist.py
|
|
193
194
|
tests/test_browser_profile.py
|
|
194
195
|
tests/test_cheap_tier.py
|
|
195
196
|
tests/test_claude_usage_gate.py
|
|
@@ -235,7 +236,6 @@ tests/test_log_colors_backlog.py
|
|
|
235
236
|
tests/test_mcp_route_contract.py
|
|
236
237
|
tests/test_memory_guard.py
|
|
237
238
|
tests/test_model_policy.py
|
|
238
|
-
tests/test_one_allowed_ip.py
|
|
239
239
|
tests/test_panel_help.py
|
|
240
240
|
tests/test_panel_ticket_schedules.py
|
|
241
241
|
tests/test_parallel_tickets.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.47
|
|
4
4
|
|
|
5
5
|
Public surface:
|
|
6
6
|
BackendClient.from_config() — create client from ~/.agentos/config.toml
|
|
@@ -34,7 +34,7 @@ Rules:
|
|
|
34
34
|
- All data egress through submit_webhook only
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
__version__ = "1.7.
|
|
37
|
+
__version__ = "1.7.47" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
|
|
38
38
|
__author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
|
|
39
39
|
|
|
40
40
|
from sdk.client import BackendClient
|
|
@@ -109,12 +109,12 @@ Command surface:
|
|
|
109
109
|
`--any` opens the port to every source instead (not recommended: every
|
|
110
110
|
scanner then reaches the panel). See sdk.firewall.
|
|
111
111
|
|
|
112
|
-
agentos allow-ip [
|
|
113
|
-
Show
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
agentos allow-ip [--clear]
|
|
113
|
+
Show who may reach the panel: this machine and the DevOps Bot backend only
|
|
114
|
+
— no outside IP (since 1.7.47). `agentos allow-ip <IP>` is refused (exit 2)
|
|
115
|
+
with the SSH-tunnel alternative (ssh -L 8765:127.0.0.1:8765 <user>@<server>).
|
|
116
|
+
`--clear` removes outside IPs an older SDK left in config.toml and syncs the
|
|
117
|
+
host firewall (that half needs root). See sdk.firewall.
|
|
118
118
|
|
|
119
119
|
agentos swagger [--open] [--json [PATH]] [--port N]
|
|
120
120
|
Where the panel's Swagger UI is and whether it can actually be reached:
|
|
@@ -294,7 +294,7 @@ def _print_usage() -> None:
|
|
|
294
294
|
print(" agentos report <submit|list|show ID> # report work Jira/GitHub never saw")
|
|
295
295
|
print(" agentos browser <login URL|status|logout> # sign in once; private links become readable")
|
|
296
296
|
print(" agentos fallback <install|login|status|logout> # the AI Draftsman CLI used when claude is not working")
|
|
297
|
-
print(" agentos allow-ip [
|
|
297
|
+
print(" agentos allow-ip [--clear] # who may reach the panel (no outside IP; use an SSH tunnel)")
|
|
298
298
|
print(" agentos firewall [--apply] [--revoke IP] [--port N] # limit the UI port to the allowlisted IPs")
|
|
299
299
|
print(" agentos firewall --any [--apply] # open the UI port to any source (allowlist is the gate)")
|
|
300
300
|
print(" agentos swagger [--open] [--json [PATH]] # where /docs is, and can you reach it")
|
|
@@ -228,30 +228,18 @@ def _parse_ip_list(raw: str) -> list[str]:
|
|
|
228
228
|
|
|
229
229
|
def get_allowed_ips() -> list[str]:
|
|
230
230
|
"""Client IPs permitted to reach the ``agentos-ui`` server: the built-in backend
|
|
231
|
-
IP(s)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
Loopback and this host's own IPs are always trusted
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
231
|
+
IP(s) and nothing else.
|
|
232
|
+
|
|
233
|
+
No outside user IP is allowed (since 1.7.47 — 1.7.46 allowed one). The panel is
|
|
234
|
+
for this machine and the DevOps Bot backend: open it on the server, or from
|
|
235
|
+
another computer through an SSH tunnel (``ssh -L 8765:127.0.0.1:8765 …``),
|
|
236
|
+
which arrives as loopback. Loopback and this host's own IPs are always trusted
|
|
237
|
+
separately. Anything still configured in ``AGENTOS_UI_ALLOWED_IPS`` or
|
|
238
|
+
config.toml ``allowed_ips`` is ignored and reported by
|
|
239
|
+
:func:`ignored_allowed_ips`; the host firewall (`sdk.firewall.ensure_restricted`)
|
|
240
|
+
removes its rule.
|
|
240
241
|
"""
|
|
241
|
-
|
|
242
|
-
user = user_allowed_ip()
|
|
243
|
-
if user and user not in out:
|
|
244
|
-
out.append(user)
|
|
245
|
-
return out
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def _single_address(value: str) -> bool:
|
|
249
|
-
"""True for one host address (a bare IP, or a /32 / /128) — not a range."""
|
|
250
|
-
import ipaddress
|
|
251
|
-
try:
|
|
252
|
-
return ipaddress.ip_network(value.strip(), strict=False).num_addresses == 1
|
|
253
|
-
except ValueError:
|
|
254
|
-
return False
|
|
242
|
+
return list(_DEFAULT_ALLOWED_IPS)
|
|
255
243
|
|
|
256
244
|
|
|
257
245
|
def _configured_user_ips() -> list[str]:
|
|
@@ -267,22 +255,9 @@ def _configured_user_ips() -> list[str]:
|
|
|
267
255
|
return out
|
|
268
256
|
|
|
269
257
|
|
|
270
|
-
def user_allowed_ip() -> str | None:
|
|
271
|
-
"""The ONE user IP allowed to reach the panel, or None.
|
|
272
|
-
|
|
273
|
-
The most recent wins: the runtime entry (config.toml — set by
|
|
274
|
-
``POST /api/ui/allowlist``) over ``.env``, and within a source the LAST entry,
|
|
275
|
-
since the documented way to add an IP is to append it. CIDR ranges are never
|
|
276
|
-
chosen — a range is many IPs.
|
|
277
|
-
"""
|
|
278
|
-
candidates = [ip for ip in _configured_user_ips() if _single_address(ip)]
|
|
279
|
-
return candidates[-1] if candidates else None
|
|
280
|
-
|
|
281
|
-
|
|
282
258
|
def ignored_allowed_ips() -> list[str]:
|
|
283
|
-
"""Configured
|
|
284
|
-
|
|
285
|
-
return [ip for ip in _configured_user_ips() if ip != keep]
|
|
259
|
+
"""Configured outside IPs/ranges that are NOT allowed — none ever is."""
|
|
260
|
+
return _configured_user_ips()
|
|
286
261
|
|
|
287
262
|
|
|
288
263
|
# Built-in baseline of tools a headless agent run may use. Kept here (not in
|
|
@@ -339,26 +314,14 @@ class PermanentAllowlistEntry(ValueError):
|
|
|
339
314
|
|
|
340
315
|
|
|
341
316
|
def permanent_allowed_ips() -> list[str]:
|
|
342
|
-
"""IPs
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
These are the fixed baseline: they are ALWAYS allowed and **cannot be disabled**
|
|
346
|
-
through the API — change them by editing ``.env`` and restarting.
|
|
347
|
-
"""
|
|
348
|
-
out: list[str] = []
|
|
349
|
-
for src in (",".join(_DEFAULT_ALLOWED_IPS), # built-in backend IP(s) — always permanent
|
|
350
|
-
_dotenv_value("AGENTOS_UI_ALLOWED_IPS"),
|
|
351
|
-
os.environ.get("AGENTOS_UI_ALLOWED_IPS", "")):
|
|
352
|
-
for ip in _parse_ip_list(src):
|
|
353
|
-
if ip not in out:
|
|
354
|
-
out.append(ip)
|
|
355
|
-
return out
|
|
317
|
+
"""The IPs that are always allowed and cannot be disabled — the built-in
|
|
318
|
+
backend IP(s). ``AGENTOS_UI_ALLOWED_IPS`` no longer adds to them (1.7.47)."""
|
|
319
|
+
return list(_DEFAULT_ALLOWED_IPS)
|
|
356
320
|
|
|
357
321
|
|
|
358
322
|
def runtime_allowed_ips() -> list[str]:
|
|
359
|
-
"""IPs/CIDRs
|
|
360
|
-
|
|
361
|
-
allowlist until disabled."""
|
|
323
|
+
"""IPs/CIDRs left in config.toml ``allowed_ips`` by an older SDK. Ignored since
|
|
324
|
+
1.7.47 (see :func:`get_allowed_ips`); ``agentos allow-ip --clear`` removes them."""
|
|
362
325
|
return _parse_ip_list(_load_raw().get("allowed_ips", ""))
|
|
363
326
|
|
|
364
327
|
|
|
@@ -461,40 +424,15 @@ def journal_enabled() -> bool:
|
|
|
461
424
|
return flag(FLAG_JOURNAL) or tiered_routing_enabled()
|
|
462
425
|
|
|
463
426
|
|
|
464
|
-
def add_allowed_ip(ip: str) -> list[str]:
|
|
465
|
-
"""Make ``ip`` THE user IP at runtime (persisted to config.toml ``allowed_ips``).
|
|
466
|
-
|
|
467
|
-
Only one user IP may reach the panel at a time, so this REPLACES whichever was
|
|
468
|
-
there before — including one from ``.env``, which it outranks while set.
|
|
469
|
-
Idempotent. The middleware sees it on the next request; the caller updates the
|
|
470
|
-
host firewall. A backend IP is already allowed and is left alone. Returns the
|
|
471
|
-
effective allowlist.
|
|
472
|
-
"""
|
|
473
|
-
ip = (ip or "").strip()
|
|
474
|
-
if not ip:
|
|
475
|
-
raise ValueError("ip is required")
|
|
476
|
-
if not _single_address(ip):
|
|
477
|
-
raise ValueError(f"{ip} is a range — only one IP may be allowed at a time")
|
|
478
|
-
if ip in _DEFAULT_ALLOWED_IPS:
|
|
479
|
-
return get_allowed_ips()
|
|
480
|
-
raw = _load_raw() # preserves token_enc / base_url / etc.
|
|
481
|
-
raw["allowed_ips"] = ip
|
|
482
|
-
_save_raw(raw)
|
|
483
|
-
return get_allowed_ips()
|
|
484
|
-
|
|
485
|
-
|
|
486
427
|
def remove_allowed_ip(ip: str) -> list[str]:
|
|
487
|
-
"""
|
|
428
|
+
"""Remove a leftover entry from config.toml ``allowed_ips`` (ignored anyway).
|
|
488
429
|
|
|
489
|
-
Raises :class:`PermanentAllowlistEntry`
|
|
490
|
-
``.env`` — those are never removable through the API (edit ``.env`` instead).
|
|
491
|
-
Returns the merged allowlist.
|
|
430
|
+
Raises :class:`PermanentAllowlistEntry` for a backend IP. Returns the allowlist.
|
|
492
431
|
"""
|
|
493
432
|
ip = (ip or "").strip()
|
|
494
433
|
if ip in permanent_allowed_ips():
|
|
495
434
|
raise PermanentAllowlistEntry(
|
|
496
|
-
f"{ip} is
|
|
497
|
-
"it cannot be disabled through the API; edit .env and restart to change it."
|
|
435
|
+
f"{ip} is a DevOps Bot backend IP — it is always allowed and cannot be removed."
|
|
498
436
|
)
|
|
499
437
|
raw = _load_raw()
|
|
500
438
|
entries = [x for x in _parse_ip_list(raw.get("allowed_ips", "")) if x != ip]
|
|
@@ -19,19 +19,16 @@ FAQS: list[tuple[str, str, str]] = [
|
|
|
19
19
|
"7. **Your co_ API token** — `agentos configure`\n"
|
|
20
20
|
"Then `agentos doctor` checks the lot."),
|
|
21
21
|
("Basics", "How do I open the control panel from another machine?",
|
|
22
|
-
"**
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"Or open
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"4. Browse `http://SERVER-IP:8765` and paste your co_ token. `agentos allow-ip` shows the allowed IP; "
|
|
33
|
-
"`agentos allow-ip --clear` removes it. The remote API cannot change it, so a leaked token cannot "
|
|
34
|
-
"open the panel to another address."),
|
|
22
|
+
"**Use an SSH tunnel** — no outside IP can be allowed; only the server itself and the DevOps Bot "
|
|
23
|
+
"backend reach the panel.\n"
|
|
24
|
+
"1. **Linux / macOS:** on your machine run `ssh -L 8765:127.0.0.1:8765 you@SERVER` and leave it open.\n"
|
|
25
|
+
"2. **Windows:** the same command in PowerShell (built-in OpenSSH client): "
|
|
26
|
+
"`ssh -L 8765:127.0.0.1:8765 you@SERVER`.\n"
|
|
27
|
+
"3. Browse `http://127.0.0.1:8765` on your machine — it arrives as loopback, so no IP to allow. "
|
|
28
|
+
"(Port 8765 busy locally? `ssh -L 9000:127.0.0.1:8765 …` and use `http://127.0.0.1:9000`.)\n"
|
|
29
|
+
"Or open `http://127.0.0.1:8765` on the server itself. `agentos allow-ip <IP>` is refused; "
|
|
30
|
+
"`agentos allow-ip` shows who may connect and `agentos allow-ip --clear` removes outside IPs an older "
|
|
31
|
+
"SDK left behind. A leaked token cannot open the panel to another address."),
|
|
35
32
|
("Basics", "How do I use devops-bot-sdk on another machine?",
|
|
36
33
|
"Install it there too — the same checklist (Python, git, SSH key, repo access, claude or ad), then "
|
|
37
34
|
"`pip install \"git+ssh://git@github.com/consultancy-outfit/DevOpsBot-SDK.git\"` and `agentos configure` "
|
|
@@ -158,6 +158,41 @@ def revoke_any_rule(port: int, system: str | None = None) -> str:
|
|
|
158
158
|
return f"# unsupported platform {sysname!r}"
|
|
159
159
|
|
|
160
160
|
|
|
161
|
+
def revoke_any_rules(port: int, system: str | None = None,
|
|
162
|
+
forms: list[str] | None = None) -> list[str]:
|
|
163
|
+
"""Every command needed to remove the open-to-any rule(s) for ``port``.
|
|
164
|
+
|
|
165
|
+
ufw keeps ``8765/tcp`` and a bare ``8765`` (tcp+udp, what a hand-typed
|
|
166
|
+
``ufw allow 8765`` makes) as DIFFERENT rules, and deleting one leaves the other
|
|
167
|
+
— the port then stays open to everyone after ``ufw delete allow 8765/tcp``.
|
|
168
|
+
``forms`` are the ``To`` spellings ufw shows for the Anywhere rows; unknown
|
|
169
|
+
(None) → both, since deleting a rule that does not exist is harmless.
|
|
170
|
+
"""
|
|
171
|
+
sysname = (system or _system()).lower()
|
|
172
|
+
if sysname != "linux":
|
|
173
|
+
return [revoke_any_rule(port, sysname)]
|
|
174
|
+
if forms is None:
|
|
175
|
+
forms = [f"{port}/tcp", str(port)]
|
|
176
|
+
return [f"sudo ufw delete allow {f}" for f in forms]
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _ufw_any_forms(port: int) -> list[str] | None:
|
|
180
|
+
"""The ``To`` spellings of the IPv4 open-to-any rows for ``port`` (e.g.
|
|
181
|
+
``["8765/tcp", "8765"]``), or None when ufw cannot be read."""
|
|
182
|
+
text = _run(_ufw_argv() + ["status"])
|
|
183
|
+
if text is None:
|
|
184
|
+
return None
|
|
185
|
+
out: list[str] = []
|
|
186
|
+
for line in text.splitlines():
|
|
187
|
+
parts = line.split("#", 1)[0].split()
|
|
188
|
+
if (len(parts) >= 3 and "(v6)" not in line and parts[1].upper() == "ALLOW"
|
|
189
|
+
and parts[-1] == "Anywhere"
|
|
190
|
+
and (parts[0] == str(port) or parts[0].startswith(f"{port}/"))
|
|
191
|
+
and parts[0] not in out):
|
|
192
|
+
out.append(parts[0])
|
|
193
|
+
return out
|
|
194
|
+
|
|
195
|
+
|
|
161
196
|
def ensure_open(port: int | None = None, *, apply: bool = True) -> tuple[bool, str]:
|
|
162
197
|
"""Make sure ``port`` is reachable. Returns ``(changed, message)``. Never raises.
|
|
163
198
|
|
|
@@ -197,7 +232,7 @@ def ensure_open(port: int | None = None, *, apply: bool = True) -> tuple[bool, s
|
|
|
197
232
|
return (False, f"macOS: pf state is not readable and pf rules are file-based —\n{printable}")
|
|
198
233
|
if sources is None:
|
|
199
234
|
ok, tool = _tool_present()
|
|
200
|
-
why = tool if not ok else "needs root to read the firewall state"
|
|
235
|
+
why = tool if not ok else "needs root or passwordless sudo to read the firewall state"
|
|
201
236
|
return (False, f"could not read the firewall state ({why}) — to open it yourself:\n{printable}")
|
|
202
237
|
|
|
203
238
|
# Readable and genuinely empty for this port.
|
|
@@ -214,13 +249,16 @@ def ensure_open(port: int | None = None, *, apply: bool = True) -> tuple[bool, s
|
|
|
214
249
|
|
|
215
250
|
|
|
216
251
|
def _apply_rules(rules: list[str]) -> list[str]:
|
|
217
|
-
"""Run each rule
|
|
252
|
+
"""Run each rule. As root a printed ``sudo `` is stripped; otherwise it becomes
|
|
253
|
+
``sudo -n `` (passwordless sudo — see :func:`_sudo_n_ok`), which fails instead
|
|
254
|
+
of waiting for a password.
|
|
218
255
|
|
|
219
256
|
Returns one ``"<cmd>: <reason>"`` per failure — empty means all applied.
|
|
220
257
|
"""
|
|
258
|
+
prefix = "sudo -n " if _sudo_n_ok() else ""
|
|
221
259
|
failed: list[str] = []
|
|
222
260
|
for rule in rules:
|
|
223
|
-
cmd = rule[len("sudo "):] if rule.startswith("sudo ") else rule
|
|
261
|
+
cmd = prefix + rule[len("sudo "):] if rule.startswith("sudo ") else rule
|
|
224
262
|
try:
|
|
225
263
|
proc = subprocess.run(
|
|
226
264
|
cmd, shell=True, capture_output=True, text=True, timeout=30,
|
|
@@ -293,10 +331,10 @@ def ensure_restricted(port: int | None = None, *, apply: bool = True,
|
|
|
293
331
|
" sudo ufw allow OpenSSH && sudo ufw enable")
|
|
294
332
|
|
|
295
333
|
if sources is None:
|
|
296
|
-
rules = allow_rules(ips, port, sysname) +
|
|
334
|
+
rules = allow_rules(ips, port, sysname) + revoke_any_rules(port, sysname)
|
|
297
335
|
printable = " " + "\n ".join(rules)
|
|
298
336
|
ok, tool = _tool_present()
|
|
299
|
-
why = tool if not ok else "needs root to read the firewall state"
|
|
337
|
+
why = tool if not ok else "needs root or passwordless sudo to read the firewall state"
|
|
300
338
|
return (False, f"could not read the firewall state ({why}) — to restrict TCP {port} "
|
|
301
339
|
f"to the allowlist yourself:\n{printable}")
|
|
302
340
|
|
|
@@ -309,7 +347,11 @@ def ensure_restricted(port: int | None = None, *, apply: bool = True,
|
|
|
309
347
|
if not missing and not open_any and not stale:
|
|
310
348
|
return (False, f"TCP {port} is restricted to the allowlist" + inactive_note)
|
|
311
349
|
|
|
312
|
-
|
|
350
|
+
# Read ufw's exact spelling only when the deletes will run; a printed list
|
|
351
|
+
# names both forms (deleting a rule that is not there is harmless).
|
|
352
|
+
any_forms = (_ufw_any_forms(port) or [f"{port}/tcp"]) if (
|
|
353
|
+
sysname == "linux" and open_any and apply and _is_privileged()) else None
|
|
354
|
+
removals = (revoke_any_rules(port, sysname, any_forms) if open_any else []) + [
|
|
313
355
|
revoke_rule(x, port, sysname) for x in stale]
|
|
314
356
|
rules = allow_rules(missing, port, sysname) + removals
|
|
315
357
|
printable = " " + "\n ".join(rules)
|
|
@@ -467,7 +509,7 @@ def port_sources(port: int, system: str | None = None, *,
|
|
|
467
509
|
"""
|
|
468
510
|
sysname = (system or _system()).lower()
|
|
469
511
|
if sysname == "linux":
|
|
470
|
-
text = _run(["
|
|
512
|
+
text = _run(_ufw_argv() + ["status"])
|
|
471
513
|
if text is None:
|
|
472
514
|
return None
|
|
473
515
|
got = _parse_ufw(text, port)
|
|
@@ -488,10 +530,42 @@ def _ufw_inactive() -> bool:
|
|
|
488
530
|
Then every ufw rule is decoration and the port is open regardless — the
|
|
489
531
|
"per-IP rules are applied but the flood still arrives" case.
|
|
490
532
|
"""
|
|
491
|
-
text = _run(["
|
|
533
|
+
text = _run(_ufw_argv() + ["status"])
|
|
492
534
|
return bool(text) and "status: inactive" in text.lower()
|
|
493
535
|
|
|
494
536
|
|
|
537
|
+
def _is_root() -> bool:
|
|
538
|
+
return hasattr(os, "geteuid") and os.geteuid() == 0
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
_SUDO_N: bool | None = None
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def _sudo_n_ok() -> bool:
|
|
545
|
+
"""True when this (non-root, Linux) user may run ufw through ``sudo -n``.
|
|
546
|
+
|
|
547
|
+
``agentos-auto update`` runs as the panel user, not root, so without this the
|
|
548
|
+
post-update hook could neither read nor fix ufw and only ever printed the
|
|
549
|
+
commands — the port stayed open to the world between updates. ``-n`` never
|
|
550
|
+
prompts, so an unattended update still cannot stall waiting for a password.
|
|
551
|
+
Probed once per process.
|
|
552
|
+
"""
|
|
553
|
+
global _SUDO_N
|
|
554
|
+
if _SUDO_N is None:
|
|
555
|
+
_SUDO_N = False
|
|
556
|
+
if _system() == "linux" and not _is_root() and shutil.which("sudo") and shutil.which("ufw"):
|
|
557
|
+
try:
|
|
558
|
+
_SUDO_N = subprocess.run(["sudo", "-n", "ufw", "status"], capture_output=True,
|
|
559
|
+
text=True, timeout=10).returncode == 0
|
|
560
|
+
except Exception: # noqa: BLE001
|
|
561
|
+
_SUDO_N = False
|
|
562
|
+
return _SUDO_N
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def _ufw_argv() -> list[str]:
|
|
566
|
+
return ["sudo", "-n", "ufw"] if _sudo_n_ok() else ["ufw"]
|
|
567
|
+
|
|
568
|
+
|
|
495
569
|
def _is_privileged() -> bool:
|
|
496
570
|
if _system() == "windows":
|
|
497
571
|
try:
|
|
@@ -499,7 +573,7 @@ def _is_privileged() -> bool:
|
|
|
499
573
|
return bool(ctypes.windll.shell32.IsUserAnAdmin())
|
|
500
574
|
except Exception: # noqa: BLE001 — not Windows, or no ctypes
|
|
501
575
|
return False
|
|
502
|
-
return
|
|
576
|
+
return _is_root() or _sudo_n_ok()
|
|
503
577
|
|
|
504
578
|
|
|
505
579
|
def cli(argv: list[str]) -> int:
|
|
@@ -572,7 +646,8 @@ def cli(argv: list[str]) -> int:
|
|
|
572
646
|
|
|
573
647
|
rules = allow_rules(ips, port, sysname)
|
|
574
648
|
if "Anywhere" in (port_sources(port, sysname) or set()):
|
|
575
|
-
rules.
|
|
649
|
+
rules.extend(revoke_any_rules(
|
|
650
|
+
port, sysname, _ufw_any_forms(port) if sysname == "linux" else None))
|
|
576
651
|
print(f"\nTCP {port} is currently open to ANY source — the last rule removes that.")
|
|
577
652
|
print("\nFirewall rules for this host:\n")
|
|
578
653
|
for r in rules:
|
|
@@ -608,55 +683,45 @@ def cli(argv: list[str]) -> int:
|
|
|
608
683
|
|
|
609
684
|
|
|
610
685
|
def allow_ip_cli(argv: list[str]) -> int:
|
|
611
|
-
"""``agentos allow-ip [
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
otherwise.
|
|
686
|
+
"""``agentos allow-ip [--clear]`` — show who may reach the panel, or clear the
|
|
687
|
+
outside IPs an older SDK left configured.
|
|
688
|
+
|
|
689
|
+
Since 1.7.47 no outside IP is allowed — only this machine and the DevOps Bot
|
|
690
|
+
backend — so setting one is refused with the SSH-tunnel alternative.
|
|
691
|
+
``--clear`` empties config.toml ``allowed_ips`` and syncs the host firewall
|
|
692
|
+
(which drops their rules); ``.env`` entries are reported for the operator to
|
|
693
|
+
delete.
|
|
620
694
|
"""
|
|
621
695
|
from sdk import config
|
|
622
696
|
|
|
623
697
|
def show() -> None:
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
print(f"Backend IPs : {', '.join(config._DEFAULT_ALLOWED_IPS)}")
|
|
698
|
+
print(f"Allowed : this machine + DevOps Bot backend "
|
|
699
|
+
f"({', '.join(config._DEFAULT_ALLOWED_IPS)}) — no outside IP")
|
|
627
700
|
ignored = config.ignored_allowed_ips()
|
|
628
701
|
if ignored:
|
|
629
|
-
print(f"Ignored
|
|
702
|
+
print(f"Ignored : {', '.join(ignored)} (outside IPs are not allowed; "
|
|
703
|
+
"`agentos allow-ip --clear` removes them)")
|
|
630
704
|
|
|
631
705
|
args = [a for a in argv if a.strip()]
|
|
632
706
|
if not args or args[0] in ("--show", "show"):
|
|
633
707
|
show()
|
|
634
708
|
return 0
|
|
635
709
|
|
|
636
|
-
if args[0] in ("--clear", "clear", "--remove"):
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
try:
|
|
652
|
-
config.add_allowed_ip(ip)
|
|
653
|
-
except ValueError as exc:
|
|
654
|
-
print(f"error: {exc}")
|
|
655
|
-
return 2
|
|
656
|
-
print(f"✓ {ip} is now the one IP allowed to reach the panel")
|
|
657
|
-
if before and before != ip:
|
|
658
|
-
print(f" (replaced {before} — it no longer has access)")
|
|
659
|
-
|
|
710
|
+
if args[0] not in ("--clear", "clear", "--remove"):
|
|
711
|
+
print("error: outside IPs can no longer be allowed — only this machine and the "
|
|
712
|
+
"DevOps Bot backend reach the panel.\n"
|
|
713
|
+
"From another computer use an SSH tunnel:\n"
|
|
714
|
+
" ssh -L 8765:127.0.0.1:8765 <user>@<server> then open http://127.0.0.1:8765")
|
|
715
|
+
return 2
|
|
716
|
+
|
|
717
|
+
for ip in config.runtime_allowed_ips():
|
|
718
|
+
if ip not in config._DEFAULT_ALLOWED_IPS:
|
|
719
|
+
config.remove_allowed_ip(ip)
|
|
720
|
+
print(f"✓ removed {ip} from ~/.agentos/config.toml")
|
|
721
|
+
left = config.ignored_allowed_ips()
|
|
722
|
+
if left:
|
|
723
|
+
print(f"Still in AGENTOS_UI_ALLOWED_IPS (~/.agentos/.env or the environment): "
|
|
724
|
+
f"{', '.join(left)} — ignored; delete that line to tidy up.")
|
|
660
725
|
changed, msg = sync_rules()
|
|
661
726
|
print(("↻ " if changed else "") + msg)
|
|
662
727
|
show()
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"area": "Control panel",
|
|
4
|
-
"changes_30d":
|
|
4
|
+
"changes_30d": 8,
|
|
5
5
|
"features_30d": 2,
|
|
6
6
|
"fixes_30d": 1,
|
|
7
|
-
"latest": "security:
|
|
8
|
-
"latest_pr":
|
|
9
|
-
"latest_date": "2026-10-
|
|
7
|
+
"latest": "security: backend-only panel allowlist; firewall removes bare 8765 rule, uses sudo -n",
|
|
8
|
+
"latest_pr": 190,
|
|
9
|
+
"latest_date": "2026-10-30"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"area": "Help",
|
|
@@ -511,10 +511,10 @@
|
|
|
511
511
|
},
|
|
512
512
|
{
|
|
513
513
|
"command": "agentos allow-ip",
|
|
514
|
-
"syntax": "agentos allow-ip [
|
|
514
|
+
"syntax": "agentos allow-ip [--clear]",
|
|
515
515
|
"program": "agentos",
|
|
516
516
|
"area": "Other",
|
|
517
|
-
"description": "
|
|
517
|
+
"description": "who may reach the panel (no outside IP); --clear removes leftover ones",
|
|
518
518
|
"valid": true,
|
|
519
519
|
"doc": "https://github.com/consultancy-outfit/DevOpsBot-SDK/blob/main/docs/COMMANDS.md",
|
|
520
520
|
"id": "agentos allow-ip"
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
"issue": "After `agentos-auto update`: \"no systemd unit 'agentos-ui' — restart the panel manually\", \"could not read the firewall state (needs root …) … sudo ufw allow 8765/tcp\", \"VPS health: not reported — not_assigned_vps\"",
|
|
60
|
-
"fix": "Not errors. The update restarts the panel so it runs the new code (`sdk/ui/service.py`), limits the panel's port to the
|
|
60
|
+
"fix": "Not errors. The update restarts the panel so it runs the new code (`sdk/ui/service.py`), limits the panel's port to the backend IPs; it removes any open-to-everyone rule — both `8765/tcp` and a bare `ufw allow 8765` — and the rules of ignored outside IPs (`sdk/firewall.py` `ensure_restricted` — not root, it tries passwordless `sudo -n`; without that it prints `sudo ufw delete allow 8765/tcp` / `sudo ufw delete allow 8765` instead), and reports VPS health only from the VPS assigned to the account (`sdk/vps_health.py`). On a laptop or any machine that is not the assigned VPS all three are expected and need no action; on the VPS, run `sudo agentos firewall --apply`, and restart `agentos-ui` if it runs by hand. \"ufw is INACTIVE\" in that output means no rule is enforced at all: `sudo ufw allow OpenSSH && sudo ufw enable`."
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
"issue": "Panel at 100% CPU / slow, lots of 403s or unknown IPs in `ss -tn sport = :8765`",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"page": "Dashboard",
|
|
4
4
|
"file": "index.html",
|
|
5
5
|
"heading": "Daily ticket activity",
|
|
6
|
-
"summary": "Bot — waiting for work To Do— In Progress— Done— Backlog— Daily ticket activity 7 days 30 days To Do In Progress Done Backlog Up next0 Remote access Allowed
|
|
6
|
+
"summary": "Bot — waiting for work To Do— In Progress— Done— Backlog— Daily ticket activity 7 days 30 days To Do In Progress Done Backlog Up next0 Remote access Allowed: this server + DevOps Bot backend"
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
"page": "Run & Logs",
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"pr": 190,
|
|
4
|
+
"date": "2026-10-30",
|
|
5
|
+
"merged_at": "2026-10-30T14:48:32+05:00",
|
|
6
|
+
"type": "Other",
|
|
7
|
+
"area": "Control panel",
|
|
8
|
+
"title": "security: backend-only panel allowlist; firewall removes bare 8765 rule, uses sudo -n",
|
|
9
|
+
"version": "1.7.47",
|
|
10
|
+
"url": "https://github.com/consultancy-outfit/DevOpsBot-SDK/pull/190"
|
|
11
|
+
},
|
|
2
12
|
{
|
|
3
13
|
"pr": 189,
|
|
4
14
|
"date": "2026-10-30",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.7.47",
|
|
4
|
+
"commit": "226a619",
|
|
5
|
+
"pushed_at": "2026-10-30T14:48:32+05:00",
|
|
6
|
+
"generated_at": "2026-10-30T09:50:43+00:00",
|
|
7
|
+
"commands": 59,
|
|
8
|
+
"commands_invalid": 0,
|
|
9
|
+
"commands_undocumented": 0,
|
|
10
|
+
"panel_pages": 11,
|
|
11
|
+
"tests": 2600,
|
|
12
|
+
"prs_7d": 26,
|
|
13
|
+
"prs_30d": 42,
|
|
14
|
+
"features_30d": 12,
|
|
15
|
+
"fixes_30d": 18
|
|
16
|
+
}
|
|
17
|
+
]
|