devops-bot-sdk 1.6.86__tar.gz → 1.6.89__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.6.86 → devops_bot_sdk-1.6.89}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/helpdesk_kb.md +39 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/app.js +215 -45
- devops_bot_sdk-1.6.89/sdk/ui/static/help.html +79 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/styles.css +100 -9
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_panel_help.py +50 -0
- devops_bot_sdk-1.6.86/sdk/ui/static/help.html +0 -48
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/README.md +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/windows_eventlog.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/models.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/redact.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/shipper.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/activity/spool.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/claude_usage.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/cli.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/client.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/custom_agents.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/fallback_handoff.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/helpdesk.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/router.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/schedule_tickets.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/tickets.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/api.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/models.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/vps_identity.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_activity_claude_runs.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_activity_collectors.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_activity_core.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_activity_live_tracking.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_activity_pipeline.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_crud_cli.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_auto_ticket_queue.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_claude_usage_gate.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_create_safe_retry.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_fallback_cli.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_fallback_cli_upgrade.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_helpdesk.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_limit_fallback_handoff.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_panel_ticket_schedules.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_schedule_tickets.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_scheduler_transient_retry.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_stale_panel_restart.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_ticket_cli.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_ticket_schedule_and_agent.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_updater_ignores_cwd.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_updater_pep668.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_vps_only_reporting.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_windows_panel_task.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_windows_stop_and_pidfile.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_windows_task_paths.py +0 -0
- {devops_bot_sdk-1.6.86 → devops_bot_sdk-1.6.89}/tests/test_write_hook.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.89
|
|
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.6.
|
|
37
|
+
__version__ = "1.6.89" # 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
|
|
@@ -101,6 +101,45 @@ the fix is not in their code, `agentos-auto update` installs the latest.
|
|
|
101
101
|
unresolved".** The `agentId` on the ticket could not be resolved
|
|
102
102
|
(`handlers._resolve_ticket_agent`); check the agent exists with
|
|
103
103
|
`agentos agent list`, or run with an explicit `--agent NAME`.
|
|
104
|
+
- **AI Draftsman fallback (`ad`) fails with `RATE_LIMIT: 429 RESOURCE_EXHAUSTED`.**
|
|
105
|
+
The text is Vertex's, but measured on 2026-10-24 the cause was Azure: the
|
|
106
|
+
gateway's `gpt-5.4` deployment (eastus) over its tokens-per-minute quota, then
|
|
107
|
+
every fallback model, with only the last error shown. Client side, `ad` 1.1.2
|
|
108
|
+
retries that error itself (5/10/20/40s); the SDK keeps machines on it —
|
|
109
|
+
`local_exec.FALLBACK_MIN_VERSION` is `(1, 1, 2)` and an older `ad` is upgraded
|
|
110
|
+
on `agentos-auto update`, or now with `agentos fallback install`. Check with
|
|
111
|
+
`agentos fallback status`. Server side (ai-draftsman-ml-b2b), rate-limited
|
|
112
|
+
deployments cool down and requests wait for capacity instead of failing, once
|
|
113
|
+
that change is deployed. Large prompts (a long ticket, `agentos help`) hit the
|
|
114
|
+
limit sooner; retrying a minute later usually works.
|
|
115
|
+
- **`agentos-auto update` says "still on vX — the upgrade did not take" but pip
|
|
116
|
+
exited 0.** Fixed in `updater._fresh_python_argv`: the post-install check ran
|
|
117
|
+
`python -c`, which reads the current directory first, so run from inside a
|
|
118
|
+
checkout of this repo it read the checkout's leftover `devops_bot_sdk.egg-info`
|
|
119
|
+
instead of the install. On an older SDK: run the update from any other folder,
|
|
120
|
+
or delete `devops_bot_sdk.egg-info` in the checkout (generated, git-ignored).
|
|
121
|
+
The install itself had worked.
|
|
122
|
+
- **After `agentos-auto update`: "no systemd unit 'agentos-ui' — restart the panel
|
|
123
|
+
manually", "could not read the firewall state (needs root …) … sudo ufw allow
|
|
124
|
+
8765/tcp", "VPS health: not reported — not_assigned_vps".** Not errors. The
|
|
125
|
+
update restarts the panel so it runs the new code (`sdk/ui/service.py`), checks
|
|
126
|
+
the panel's port is open for remote access (`sdk/firewall.py` — it never runs
|
|
127
|
+
sudo itself, so unprivileged it prints the command instead), and reports VPS
|
|
128
|
+
health only from the VPS assigned to the account (`sdk/vps_health.py`). On a
|
|
129
|
+
laptop or any machine that is not the assigned VPS all three are expected and
|
|
130
|
+
need no action; on the VPS, open the port with the printed command if the panel
|
|
131
|
+
must be reachable remotely, and restart `agentos-ui` if it runs by hand.
|
|
132
|
+
|
|
133
|
+
## Help in the control panel
|
|
134
|
+
|
|
135
|
+
The panel's **Help** page (`/help.html`, "Help" in the nav; `sdk/ui/static/help.html`)
|
|
136
|
+
is the same helper as `agentos help`: describe the problem, the answer shows which
|
|
137
|
+
engine answered (claude or the AI Draftsman fallback), follow-ups stay in the same
|
|
138
|
+
case. **Collect diagnostics only** = `agentos doctor`; **Download bundle for your
|
|
139
|
+
admin** = a zip of the case's diagnostics, errors and logs (no SDK source). API:
|
|
140
|
+
`POST /api/ui/help/ask` (a job), `POST /api/ui/help/doctor`,
|
|
141
|
+
`GET /api/ui/help/case/{name}/bundle` (`sdk/ui/api.py`). It runs on the machine the
|
|
142
|
+
panel runs on, so it reads that machine's logs.
|
|
104
143
|
|
|
105
144
|
## Useful commands (all verified to exist)
|
|
106
145
|
|
|
@@ -220,7 +220,12 @@
|
|
|
220
220
|
<span id="loop-badge" class="badge idle"><span class="pulse"></span> …</span>
|
|
221
221
|
<a class="btn" href="/setup.html">Setup</a>`;
|
|
222
222
|
const link = host.querySelector(`[data-nav="${active}"]`);
|
|
223
|
-
if (link)
|
|
223
|
+
if (link) {
|
|
224
|
+
link.classList.add("active");
|
|
225
|
+
// The nav scrolls sideways rather than wrapping when the window is narrow;
|
|
226
|
+
// keep the page you are on visible.
|
|
227
|
+
link.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
228
|
+
}
|
|
224
229
|
}
|
|
225
230
|
|
|
226
231
|
async function pollBadge() {
|
|
@@ -1216,10 +1221,14 @@
|
|
|
1216
1221
|
|
|
1217
1222
|
async function start() {
|
|
1218
1223
|
if (TOKEN) { boot(); return; }
|
|
1224
|
+
let unlocked = false;
|
|
1219
1225
|
try {
|
|
1220
1226
|
const res = await fetch("/api/ui/session-token");
|
|
1221
|
-
if (res.ok) { const d = await res.json(); if (d && d.token !== undefined) { setToken(d.token || "loopback");
|
|
1227
|
+
if (res.ok) { const d = await res.json(); if (d && d.token !== undefined) { setToken(d.token || "loopback"); unlocked = true; } }
|
|
1222
1228
|
} catch (e) { /* fall through */ }
|
|
1229
|
+
// boot() OUTSIDE the try: a page whose init throws must show that error in the
|
|
1230
|
+
// console, not be swallowed here and turned into a misleading token prompt.
|
|
1231
|
+
if (unlocked) { boot(); return; }
|
|
1223
1232
|
showGate();
|
|
1224
1233
|
}
|
|
1225
1234
|
|
|
@@ -2036,81 +2045,242 @@
|
|
|
2036
2045
|
}
|
|
2037
2046
|
|
|
2038
2047
|
// ── HELP: DevOps Bot Help (`agentos help` / `agentos doctor` in the panel) ──
|
|
2048
|
+
// A real answer is plain text with light markdown: "Diagnosis / Evidence / Fix /
|
|
2049
|
+
// If that doesn't work" sections, lists, `code`, ``` blocks and ⚠ lines.
|
|
2050
|
+
// renderHelpAnswer() ESCAPES FIRST and only then adds markup, so nothing the
|
|
2051
|
+
// model writes can become HTML.
|
|
2052
|
+
const HP_SECTIONS = [
|
|
2053
|
+
[/^diagnosis$/i, "diagnosis", "Diagnosis"],
|
|
2054
|
+
[/^evidence$/i, "evidence", "Evidence"],
|
|
2055
|
+
[/^fix$/i, "fix", "Fix"],
|
|
2056
|
+
[/^if that does(?:n'|n’|n)?t work$|^if that doesn't work$|^if it still fails$/i, "next", "If that doesn't work"],
|
|
2057
|
+
];
|
|
2058
|
+
|
|
2059
|
+
function hpInline(t) {
|
|
2060
|
+
return t
|
|
2061
|
+
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
|
2062
|
+
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
function renderHelpAnswer(text) {
|
|
2066
|
+
const parts = String(text || "").split(/```[^\n]*\n?/);
|
|
2067
|
+
let html = "", list = null, section = null;
|
|
2068
|
+
const closeList = () => { if (list) { html += `</${list}>`; list = null; } };
|
|
2069
|
+
const closeSection = () => { closeList(); if (section) { html += "</section>"; section = null; } };
|
|
2070
|
+
const openSection = (cls, label) => {
|
|
2071
|
+
closeSection();
|
|
2072
|
+
html += `<section class="hp-sec hp-sec-${cls}"><div class="hp-sec-label">${label}</div>`;
|
|
2073
|
+
section = cls;
|
|
2074
|
+
};
|
|
2075
|
+
parts.forEach((part, i) => {
|
|
2076
|
+
if (i % 2 === 1) { // inside a ``` block
|
|
2077
|
+
closeList();
|
|
2078
|
+
// Drop blank edge lines and the indent the block was nested at.
|
|
2079
|
+
const lines = part.replace(/^\s*\n/, "").replace(/\s+$/, "").split("\n");
|
|
2080
|
+
const indent = Math.min(...lines.filter(l => l.trim()).map(l => l.match(/^\s*/)[0].length));
|
|
2081
|
+
const code = esc(lines.map(l => l.slice(indent)).join("\n"));
|
|
2082
|
+
html += `<div class="hp-code"><pre><code>${code}</code></pre>` +
|
|
2083
|
+
`<button type="button" class="hp-copy" data-copy="${code}">Copy</button></div>`;
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
esc(part).split("\n").forEach(raw => {
|
|
2087
|
+
const line = raw.trimEnd();
|
|
2088
|
+
if (!line.trim()) { closeList(); return; }
|
|
2089
|
+
const head = line.match(/^\s*\**([A-Za-z' ’]+?)\**\s*(?::|—|–|-)\s*(.*)$/);
|
|
2090
|
+
const sec = head && HP_SECTIONS.find(([re]) => re.test(head[1].trim()));
|
|
2091
|
+
if (sec) {
|
|
2092
|
+
openSection(sec[1], sec[2]);
|
|
2093
|
+
if (head[2]) html += `<p>${hpInline(head[2])}</p>`;
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
const bullet = line.match(/^\s*[-*•]\s+(.*)$/);
|
|
2097
|
+
const num = line.match(/^\s*(\d+)[.)]\s+(.*)$/);
|
|
2098
|
+
if (bullet || num) {
|
|
2099
|
+
const kind = bullet ? "ul" : "ol";
|
|
2100
|
+
if (list !== kind) { closeList(); html += `<${kind}>`; list = kind; }
|
|
2101
|
+
html += `<li>${hpInline(bullet ? bullet[1] : num[2])}</li>`;
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
closeList();
|
|
2105
|
+
if (/^\s*(⚠|<!|warning:)/i.test(line)) {
|
|
2106
|
+
html += `<div class="hp-warn">${hpInline(line.replace(/^\s*⚠\s*/, ""))}</div>`;
|
|
2107
|
+
} else {
|
|
2108
|
+
html += `<p>${hpInline(line)}</p>`;
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
});
|
|
2112
|
+
closeSection();
|
|
2113
|
+
return html;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
async function hpCopy(text, btn) {
|
|
2117
|
+
try { await navigator.clipboard.writeText(text); }
|
|
2118
|
+
catch (e) { // http:// on a remote box: no clipboard API
|
|
2119
|
+
const ta = document.createElement("textarea");
|
|
2120
|
+
ta.value = text; document.body.appendChild(ta); ta.select();
|
|
2121
|
+
try { document.execCommand("copy"); } catch (_) { /* nothing more to try */ }
|
|
2122
|
+
ta.remove();
|
|
2123
|
+
}
|
|
2124
|
+
if (btn) { const was = btn.textContent; btn.textContent = "Copied"; setTimeout(() => { btn.textContent = was; }, 1200); }
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2039
2127
|
function initHelp() {
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2128
|
+
const $ = (id) => document.getElementById(id);
|
|
2129
|
+
let caseName = "", jobId = null, timer = null, busy = false;
|
|
2130
|
+
|
|
2131
|
+
function fmt(secs) { return `${Math.floor(secs / 60)}:${String(secs % 60).padStart(2, "0")}`; }
|
|
2132
|
+
|
|
2133
|
+
function working(on, followUp) {
|
|
2134
|
+
busy = on;
|
|
2135
|
+
$("hp-working").style.display = on ? "" : "none";
|
|
2136
|
+
$("hp-ask").disabled = on; $("hp-follow-btn").disabled = on; $("hp-doctor").disabled = on;
|
|
2137
|
+
clearInterval(timer);
|
|
2138
|
+
if (!on) { jobId = null; return; }
|
|
2139
|
+
const started = Date.now();
|
|
2140
|
+
const tick = () => {
|
|
2141
|
+
const secs = Math.round((Date.now() - started) / 1000);
|
|
2142
|
+
$("hp-elapsed").textContent = fmt(secs);
|
|
2143
|
+
$("hp-status").textContent = followUp ? "Thinking about your follow-up…"
|
|
2144
|
+
: secs < 6 ? "Collecting diagnostics and recent logs…"
|
|
2145
|
+
: secs < 20 ? "Reading your logs and the SDK source…"
|
|
2146
|
+
: "Working out the cause — usually 30 seconds to 2 minutes…";
|
|
2147
|
+
};
|
|
2148
|
+
tick(); timer = setInterval(tick, 1000);
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
// During a conversation only the follow-up box shows; "New question" brings
|
|
2152
|
+
// the main box back. Two inputs at once read as two different things.
|
|
2153
|
+
function conversation(on) {
|
|
2154
|
+
document.querySelector(".hp-composer").style.display = on ? "none" : "";
|
|
2155
|
+
$("hp-follow").style.display = on ? "" : "none";
|
|
2156
|
+
$("hp-empty").style.display = on ? "none" : "";
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
function addQuestion(question) {
|
|
2160
|
+
$("hp-empty").style.display = "none";
|
|
2161
|
+
document.querySelector(".hp-composer").style.display = "none";
|
|
2162
|
+
$("hp-thread").insertAdjacentHTML("beforeend",
|
|
2163
|
+
`<div class="hp-q"><div class="hp-bubble">${esc(question)}</div></div>`);
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
function addAnswer(res) {
|
|
2167
|
+
const fallback = res.engine === "ad";
|
|
2168
|
+
const engine = !res.engine ? "" : fallback
|
|
2169
|
+
? `<span class="hp-engine hp-engine-ad" title="claude was not available, so the AI Draftsman CLI answered">AI Draftsman fallback</span>`
|
|
2170
|
+
: `<span class="hp-engine">claude</span>`;
|
|
2171
|
+
const notes = (res.notes || []).map(n => `<div class="hp-note">${hpInline(esc(n))}</div>`).join("");
|
|
2172
|
+
const body = res.ok
|
|
2173
|
+
? `<div class="hp-body">${renderHelpAnswer(res.answer || "(the helper returned an empty answer)")}</div>`
|
|
2174
|
+
: `<div class="hp-err"><strong>The helper couldn't finish.</strong> ${esc(res.error || "")}</div>`;
|
|
2175
|
+
const card = document.createElement("div");
|
|
2176
|
+
card.className = "hp-a";
|
|
2177
|
+
card.innerHTML = `
|
|
2178
|
+
<div class="hp-a-head">
|
|
2179
|
+
<span class="hp-avatar" aria-hidden="true">🤖</span>
|
|
2180
|
+
<strong>DevOps Bot Help</strong>${engine}
|
|
2181
|
+
<span class="spacer"></span>
|
|
2182
|
+
${res.case ? `<span class="muted hp-case" title="Case folder on this machine">${esc(res.case)}</span>` : ""}
|
|
2183
|
+
${res.ok ? `<button type="button" class="btn hp-copy-all">Copy answer</button>` : ""}
|
|
2184
|
+
</div>${notes}${body}`;
|
|
2185
|
+
$("hp-thread").appendChild(card);
|
|
2186
|
+
card.querySelectorAll(".hp-copy").forEach(b =>
|
|
2187
|
+
b.addEventListener("click", () => {
|
|
2188
|
+
const t = document.createElement("textarea"); t.innerHTML = b.getAttribute("data-copy");
|
|
2189
|
+
hpCopy(t.value, b);
|
|
2190
|
+
}));
|
|
2191
|
+
const all = card.querySelector(".hp-copy-all");
|
|
2192
|
+
if (all) all.addEventListener("click", () => hpCopy(res.answer || "", all));
|
|
2193
|
+
card.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
2055
2194
|
}
|
|
2056
2195
|
|
|
2057
2196
|
async function ask(question, followUp) {
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
? "Thinking about your follow-up…"
|
|
2062
|
-
: "Collecting diagnostics and logs, then reading the SDK source… (a minute or two)";
|
|
2197
|
+
if (busy) return;
|
|
2198
|
+
addQuestion(question);
|
|
2199
|
+
working(true, followUp);
|
|
2063
2200
|
try {
|
|
2064
2201
|
const res = await runJob("/api/ui/help/ask",
|
|
2065
|
-
{ question, case: followUp ? caseName : "" },
|
|
2202
|
+
{ question, case: followUp ? caseName : "" },
|
|
2203
|
+
{ persistKey: "agentos_help_job", onStart: (id) => { jobId = id; } });
|
|
2066
2204
|
if (res.case) caseName = res.case;
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
$("hp-bundle").style.display = caseName ? "" : "none";
|
|
2205
|
+
addAnswer(res);
|
|
2206
|
+
conversation(true);
|
|
2207
|
+
$("hp-bundle-2").style.display = caseName ? "" : "none";
|
|
2208
|
+
$("hp-follow-q").focus();
|
|
2070
2209
|
} catch (e) {
|
|
2071
|
-
|
|
2210
|
+
addAnswer({ ok: false, error: e.cancelled ? "Stopped." : e.message });
|
|
2211
|
+
conversation(true);
|
|
2072
2212
|
} finally {
|
|
2073
|
-
|
|
2074
|
-
$("hp-status").textContent = "";
|
|
2213
|
+
working(false);
|
|
2075
2214
|
}
|
|
2076
2215
|
}
|
|
2077
2216
|
|
|
2078
|
-
|
|
2079
|
-
if (!caseName) return;
|
|
2080
|
-
const res = await fetch(`/api/ui/help/case/${encodeURIComponent(caseName)}/bundle`,
|
|
2081
|
-
{ headers: { "Authorization": "Bearer " + TOKEN } });
|
|
2082
|
-
if (!res.ok) { toast("Could not build the bundle", "err"); return; }
|
|
2083
|
-
const url = URL.createObjectURL(await res.blob());
|
|
2084
|
-
const a = document.createElement("a");
|
|
2085
|
-
a.href = url; a.download = caseName + ".zip"; a.click();
|
|
2086
|
-
setTimeout(() => URL.revokeObjectURL(url), 5000);
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
|
-
$("hp-ask").addEventListener("click", () => {
|
|
2217
|
+
function askNew() {
|
|
2090
2218
|
const q = $("hp-question").value.trim();
|
|
2091
|
-
if (!q) { toast("Describe the problem first", "err"); return; }
|
|
2219
|
+
if (!q) { toast("Describe the problem first", "err"); $("hp-question").focus(); return; }
|
|
2092
2220
|
$("hp-thread").innerHTML = "";
|
|
2093
2221
|
caseName = "";
|
|
2222
|
+
$("hp-question").value = "";
|
|
2094
2223
|
ask(q, false);
|
|
2095
|
-
}
|
|
2096
|
-
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
function askFollowUp() {
|
|
2097
2227
|
const q = $("hp-follow-q").value.trim();
|
|
2098
2228
|
if (!q) return;
|
|
2099
2229
|
$("hp-follow-q").value = "";
|
|
2100
2230
|
ask(q, true);
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
async function download() {
|
|
2234
|
+
if (!caseName) return;
|
|
2235
|
+
const btn = $("hp-bundle"); btn.disabled = true;
|
|
2236
|
+
try {
|
|
2237
|
+
const res = await fetch(`/api/ui/help/case/${encodeURIComponent(caseName)}/bundle`,
|
|
2238
|
+
{ headers: { "Authorization": "Bearer " + TOKEN } });
|
|
2239
|
+
if (!res.ok) throw new Error("could not build the bundle");
|
|
2240
|
+
const url = URL.createObjectURL(await res.blob());
|
|
2241
|
+
const a = document.createElement("a");
|
|
2242
|
+
a.href = url; a.download = caseName + ".zip"; a.click();
|
|
2243
|
+
setTimeout(() => URL.revokeObjectURL(url), 5000);
|
|
2244
|
+
} catch (e) { toast(e.message, "err"); }
|
|
2245
|
+
finally { btn.disabled = false; }
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
$("hp-ask").addEventListener("click", askNew);
|
|
2249
|
+
$("hp-question").addEventListener("keydown", (e) => {
|
|
2250
|
+
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); askNew(); }
|
|
2251
|
+
});
|
|
2252
|
+
$("hp-follow-btn").addEventListener("click", askFollowUp);
|
|
2253
|
+
$("hp-follow-q").addEventListener("keydown", (e) => {
|
|
2254
|
+
if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); askFollowUp(); }
|
|
2255
|
+
});
|
|
2256
|
+
$("hp-new").addEventListener("click", () => {
|
|
2257
|
+
$("hp-thread").innerHTML = ""; caseName = "";
|
|
2258
|
+
$("hp-bundle").style.display = "none";
|
|
2259
|
+
conversation(false); $("hp-question").focus();
|
|
2101
2260
|
});
|
|
2261
|
+
$("hp-stop").addEventListener("click", () => { if (jobId) cancelJob(jobId); });
|
|
2262
|
+
document.querySelectorAll(".hp-chip").forEach(chip =>
|
|
2263
|
+
chip.addEventListener("click", () => {
|
|
2264
|
+
$("hp-question").value = chip.textContent.trim();
|
|
2265
|
+
$("hp-question").focus();
|
|
2266
|
+
}));
|
|
2102
2267
|
$("hp-doctor").addEventListener("click", async () => {
|
|
2103
2268
|
const btn = $("hp-doctor"); btn.disabled = true;
|
|
2269
|
+
const was = btn.textContent; btn.textContent = "Collecting…";
|
|
2104
2270
|
try {
|
|
2105
2271
|
const d = await api("/api/ui/help/doctor", { method: "POST", body: {} });
|
|
2106
2272
|
caseName = d.case;
|
|
2107
2273
|
$("hp-diag").textContent = d.diagnostics + "\n" + d.errors;
|
|
2108
2274
|
$("hp-diag-wrap").style.display = "";
|
|
2275
|
+
$("hp-diag-wrap").open = true;
|
|
2109
2276
|
$("hp-bundle").style.display = "";
|
|
2277
|
+
$("hp-diag-wrap").scrollIntoView({ behavior: "smooth", block: "start" });
|
|
2110
2278
|
} catch (e) { toast(e.message, "err"); }
|
|
2111
|
-
finally { btn.disabled = false; }
|
|
2279
|
+
finally { btn.disabled = false; btn.textContent = was; }
|
|
2112
2280
|
});
|
|
2113
2281
|
$("hp-bundle").addEventListener("click", download);
|
|
2282
|
+
$("hp-bundle-2").addEventListener("click", download);
|
|
2283
|
+
$("hp-question").focus();
|
|
2114
2284
|
}
|
|
2115
2285
|
|
|
2116
2286
|
// ── RUN & LOGS: command buttons + live log ───────────────────────────────
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>AgentOS — Help</title>
|
|
7
|
+
<link rel="stylesheet" href="/styles.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body data-page="help">
|
|
10
|
+
<div id="topbar"></div>
|
|
11
|
+
|
|
12
|
+
<div class="wrap hp-wrap">
|
|
13
|
+
<div class="hp-head">
|
|
14
|
+
<div>
|
|
15
|
+
<h1>DevOps Bot Help</h1>
|
|
16
|
+
<p class="muted">
|
|
17
|
+
Describe what's going wrong. The helper reads this machine's diagnostics and recent
|
|
18
|
+
logs, checks them against the installed SDK, and tells you the cause and the fix —
|
|
19
|
+
with the log or code line behind every claim.
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
<span class="hp-readonly" title="It can read files only: no commands, no edits, no web.">🔒 Read-only</span>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div id="hp-thread" class="hp-thread"></div>
|
|
26
|
+
|
|
27
|
+
<div id="hp-empty" class="hp-empty">
|
|
28
|
+
<div class="muted hp-empty-label">Common problems — click one to start</div>
|
|
29
|
+
<div class="hp-chips">
|
|
30
|
+
<button type="button" class="hp-chip">My agentos-auto loop isn't picking up tickets</button>
|
|
31
|
+
<button type="button" class="hp-chip">agentos-auto update says the upgrade did not take</button>
|
|
32
|
+
<button type="button" class="hp-chip">Tickets are stuck In Progress and set-status fails</button>
|
|
33
|
+
<button type="button" class="hp-chip">The AI Draftsman fallback fails with 429 RESOURCE_EXHAUSTED</button>
|
|
34
|
+
<button type="button" class="hp-chip">A scheduled ticket run never started</button>
|
|
35
|
+
<button type="button" class="hp-chip">agentos-auto stop crashes on Windows</button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div id="hp-working" class="hp-working" style="display:none">
|
|
40
|
+
<span class="hp-spinner" aria-hidden="true"></span>
|
|
41
|
+
<span id="hp-status">Working…</span>
|
|
42
|
+
<span id="hp-elapsed" class="muted hp-elapsed">0:00</span>
|
|
43
|
+
<button id="hp-stop" class="btn" type="button">Stop</button>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="hp-composer panelbox">
|
|
47
|
+
<div class="bd">
|
|
48
|
+
<label for="hp-question" class="hp-label">Your problem</label>
|
|
49
|
+
<textarea id="hp-question" rows="3"
|
|
50
|
+
placeholder="e.g. agentos-auto stop crashes with AttributeError getpgid and tickets stay In Progress"></textarea>
|
|
51
|
+
<div class="hp-actions">
|
|
52
|
+
<button id="hp-ask" class="btn primary">Ask</button>
|
|
53
|
+
<span class="muted hp-kbd">Ctrl / ⌘ + Enter</span>
|
|
54
|
+
<span class="spacer"></span>
|
|
55
|
+
<button id="hp-doctor" class="btn" type="button" title="Collect this machine's diagnostics without asking the AI">Collect diagnostics only</button>
|
|
56
|
+
<button id="hp-bundle" class="btn" type="button" style="display:none"
|
|
57
|
+
title="A zip of the diagnostics, errors and logs — no source code, secrets removed">⬇ Bundle for your admin</button>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div id="hp-follow" class="hp-follow panelbox" style="display:none">
|
|
63
|
+
<div class="bd hp-follow-row">
|
|
64
|
+
<input id="hp-follow-q" type="text" placeholder="Ask a follow-up about the same problem (Enter to send)" />
|
|
65
|
+
<button id="hp-follow-btn" class="btn primary">Send</button>
|
|
66
|
+
<button id="hp-bundle-2" class="btn" type="button" style="display:none"
|
|
67
|
+
title="A zip of the diagnostics, errors and logs — no source code, secrets removed">⬇ Bundle</button>
|
|
68
|
+
<button id="hp-new" class="btn" type="button">New question</button>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<details id="hp-diag-wrap" class="hp-diag panelbox" style="display:none">
|
|
73
|
+
<summary>Diagnostics collected from this machine</summary>
|
|
74
|
+
<pre id="hp-diag"></pre>
|
|
75
|
+
</details>
|
|
76
|
+
</div>
|
|
77
|
+
<script src="/app.js"></script>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
@@ -53,10 +53,12 @@ a { color: var(--accent); }
|
|
|
53
53
|
.wrap { max-width: 1160px; margin: 0 auto; padding: 22px; }
|
|
54
54
|
|
|
55
55
|
/* ── Main nav ── */
|
|
56
|
-
.mainnav { display: flex; gap: 4px; margin-left: 10px; }
|
|
56
|
+
.mainnav { display: flex; gap: 4px; margin-left: 10px; min-width: 0; overflow-x: auto; scrollbar-width: none; }
|
|
57
|
+
.mainnav::-webkit-scrollbar { display: none; }
|
|
57
58
|
.mainnav a {
|
|
59
|
+
white-space: nowrap; flex: none;
|
|
58
60
|
text-decoration: none; color: var(--muted); font-weight: 600; font-size: 14px;
|
|
59
|
-
padding: 7px
|
|
61
|
+
padding: 7px 10px; border-radius: 8px; transition: .12s;
|
|
60
62
|
}
|
|
61
63
|
.mainnav a:hover { background: var(--panel-2); color: var(--text); }
|
|
62
64
|
.mainnav a.active { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
|
|
@@ -444,10 +446,99 @@ tr:last-child td { border-bottom: none; }
|
|
|
444
446
|
.sc-tk-list .empty { padding: 10px; }
|
|
445
447
|
|
|
446
448
|
/* Help page */
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
.hp-
|
|
450
|
-
.hp-
|
|
451
|
-
.hp-
|
|
452
|
-
|
|
453
|
-
|
|
449
|
+
.hp-wrap { max-width: 920px; }
|
|
450
|
+
.hp-head { display: flex; gap: 16px; align-items: flex-start; margin: 4px 0 18px; }
|
|
451
|
+
.hp-head h1 { margin: 0 0 6px; font-size: 22px; }
|
|
452
|
+
.hp-head p { margin: 0; max-width: 680px; }
|
|
453
|
+
.hp-readonly { flex: none; font-size: 12px; font-weight: 600; color: var(--green);
|
|
454
|
+
border: 1px solid color-mix(in srgb, var(--green) 40%, var(--border)); border-radius: 999px; padding: 4px 10px; }
|
|
455
|
+
|
|
456
|
+
.hp-empty { margin-bottom: 14px; }
|
|
457
|
+
.hp-empty-label { font-size: 13px; margin-bottom: 8px; }
|
|
458
|
+
.hp-chips { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
459
|
+
.hp-chip { font: inherit; font-size: 13px; color: var(--text); background: var(--panel); cursor: pointer;
|
|
460
|
+
border: 1px solid var(--border); border-radius: 999px; padding: 6px 12px; transition: .12s; }
|
|
461
|
+
.hp-chip:hover { border-color: var(--accent); color: var(--accent); }
|
|
462
|
+
|
|
463
|
+
.hp-composer .bd, .hp-follow .bd { padding: 14px 16px; }
|
|
464
|
+
.hp-label { display: block; font-weight: 600; font-size: 13px; margin-bottom: 6px; }
|
|
465
|
+
#hp-question { width: 100%; font: inherit; font-size: 15px; color: var(--text); background: var(--panel-2);
|
|
466
|
+
border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; resize: vertical; min-height: 84px; }
|
|
467
|
+
#hp-question:focus, #hp-follow-q:focus { outline: none; border-color: var(--accent);
|
|
468
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
|
|
469
|
+
.hp-actions { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
|
|
470
|
+
.hp-kbd { font-size: 12px; }
|
|
471
|
+
|
|
472
|
+
.hp-thread { display: flex; flex-direction: column; gap: 12px; margin-bottom: 14px; }
|
|
473
|
+
.hp-thread:empty { display: none; }
|
|
474
|
+
.hp-q { display: flex; justify-content: flex-end; }
|
|
475
|
+
.hp-bubble { max-width: 80%; background: var(--accent); color: var(--accent-ink); padding: 9px 14px;
|
|
476
|
+
border-radius: 14px 14px 4px 14px; white-space: pre-wrap; word-break: break-word; }
|
|
477
|
+
.hp-a { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
|
|
478
|
+
box-shadow: var(--shadow); padding: 14px 16px; }
|
|
479
|
+
.hp-a-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
|
|
480
|
+
.hp-avatar { font-size: 18px; }
|
|
481
|
+
.hp-engine { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
|
|
482
|
+
color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
|
|
483
|
+
border-radius: 6px; padding: 2px 7px; }
|
|
484
|
+
.hp-engine-ad { color: var(--amber); background: color-mix(in srgb, var(--amber) 14%, transparent); }
|
|
485
|
+
.hp-case { font-size: 12px; font-family: ui-monospace, Menlo, Consolas, monospace; }
|
|
486
|
+
.hp-a .btn { padding: 4px 10px; font-size: 12px; }
|
|
487
|
+
|
|
488
|
+
.hp-body { font-size: 14.5px; line-height: 1.55; }
|
|
489
|
+
.hp-body p { margin: 4px 0; }
|
|
490
|
+
.hp-body ul, .hp-body ol { margin: 4px 0 6px; padding-left: 22px; }
|
|
491
|
+
.hp-body li { margin: 3px 0; }
|
|
492
|
+
.hp-body code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12.5px;
|
|
493
|
+
background: var(--panel-2); border: 1px solid var(--border); border-radius: 5px; padding: 1px 5px;
|
|
494
|
+
word-break: break-word; }
|
|
495
|
+
.hp-sec { border-left: 3px solid var(--border); padding: 2px 0 2px 12px; margin: 10px 0; }
|
|
496
|
+
.hp-sec-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px;
|
|
497
|
+
color: var(--muted); margin-bottom: 2px; }
|
|
498
|
+
.hp-sec-diagnosis { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, transparent);
|
|
499
|
+
border-radius: 0 8px 8px 0; padding: 8px 12px; }
|
|
500
|
+
.hp-sec-diagnosis .hp-sec-label { color: var(--accent); }
|
|
501
|
+
.hp-sec-diagnosis p { font-size: 15px; font-weight: 500; }
|
|
502
|
+
.hp-sec-fix { border-left-color: var(--green); }
|
|
503
|
+
.hp-sec-fix .hp-sec-label { color: var(--green); }
|
|
504
|
+
.hp-sec-next { border-left-color: var(--amber); }
|
|
505
|
+
.hp-code { position: relative; margin: 6px 0; }
|
|
506
|
+
.hp-code pre { margin: 0; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px;
|
|
507
|
+
padding: 10px 12px; overflow-x: auto; font-size: 12.5px; }
|
|
508
|
+
.hp-code pre code { background: none; border: 0; padding: 0; }
|
|
509
|
+
.hp-copy { position: absolute; top: 6px; right: 6px; font: inherit; font-size: 11px; cursor: pointer;
|
|
510
|
+
color: var(--muted); background: var(--panel); border: 1px solid var(--border); border-radius: 6px; padding: 2px 8px; }
|
|
511
|
+
.hp-copy:hover { color: var(--accent); border-color: var(--accent); }
|
|
512
|
+
.hp-warn { margin: 6px 0; padding: 7px 10px; border-radius: 8px; font-size: 13.5px;
|
|
513
|
+
background: color-mix(in srgb, var(--amber) 12%, transparent); border: 1px solid color-mix(in srgb, var(--amber) 35%, transparent); }
|
|
514
|
+
.hp-warn::before { content: "⚠ "; }
|
|
515
|
+
.hp-note { margin: 0 0 8px; padding: 7px 10px; border-radius: 8px; font-size: 13px; color: var(--text);
|
|
516
|
+
background: color-mix(in srgb, var(--amber) 10%, transparent); }
|
|
517
|
+
.hp-err { padding: 8px 10px; border-radius: 8px; color: var(--red);
|
|
518
|
+
background: color-mix(in srgb, var(--red) 8%, transparent); }
|
|
519
|
+
|
|
520
|
+
.hp-working { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; padding: 10px 14px;
|
|
521
|
+
background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); }
|
|
522
|
+
.hp-spinner { width: 16px; height: 16px; border-radius: 50%; flex: none;
|
|
523
|
+
border: 2px solid color-mix(in srgb, var(--accent) 25%, transparent); border-top-color: var(--accent);
|
|
524
|
+
animation: hp-spin .8s linear infinite; }
|
|
525
|
+
@keyframes hp-spin { to { transform: rotate(360deg); } }
|
|
526
|
+
.hp-elapsed { font-variant-numeric: tabular-nums; margin-left: auto; }
|
|
527
|
+
|
|
528
|
+
.hp-follow { margin-top: 0; }
|
|
529
|
+
.hp-follow-row { display: flex; gap: 8px; align-items: center; }
|
|
530
|
+
#hp-follow-q { flex: 1; min-width: 0; font: inherit; color: var(--text); background: var(--panel-2);
|
|
531
|
+
border: 1px solid var(--border); border-radius: 10px; padding: 9px 12px; }
|
|
532
|
+
|
|
533
|
+
.hp-diag { margin-top: 14px; }
|
|
534
|
+
.hp-diag summary { cursor: pointer; padding: 12px 16px; font-weight: 600; }
|
|
535
|
+
.hp-diag pre { margin: 0; padding: 0 16px 16px; white-space: pre-wrap; word-break: break-word;
|
|
536
|
+
font-size: 12px; max-height: 60vh; overflow: auto; }
|
|
537
|
+
|
|
538
|
+
@media (max-width: 640px) {
|
|
539
|
+
.hp-head { flex-direction: column; }
|
|
540
|
+
.hp-bubble { max-width: 92%; }
|
|
541
|
+
.hp-follow-row { flex-wrap: wrap; }
|
|
542
|
+
#hp-follow-q { flex: 1 1 100%; }
|
|
543
|
+
.hp-kbd { display: none; }
|
|
544
|
+
}
|