devops-bot-sdk 1.6.87__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.87 → devops_bot_sdk-1.6.89}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.6.87 → 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.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/styles.css +100 -9
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_panel_help.py +50 -0
- devops_bot_sdk-1.6.87/sdk/ui/static/help.html +0 -48
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/README.md +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/collectors/windows_eventlog.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/models.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/redact.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/shipper.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/activity/spool.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/claude_usage.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/cli.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/client.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/custom_agents.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/fallback_handoff.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/helpdesk.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/helpdesk_kb.md +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/router.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/schedule_tickets.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/tickets.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/api.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/models.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/vps_identity.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_activity_claude_runs.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_activity_collectors.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_activity_core.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_activity_live_tracking.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_activity_pipeline.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_crud_cli.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_auto_ticket_queue.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_claude_usage_gate.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_create_safe_retry.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_fallback_cli.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_fallback_cli_upgrade.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_helpdesk.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_limit_fallback_handoff.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_panel_ticket_schedules.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_schedule_tickets.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_scheduler_transient_retry.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_stale_panel_restart.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_ticket_cli.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_ticket_schedule_and_agent.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_updater_ignores_cwd.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_updater_pep668.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_vps_only_reporting.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_windows_panel_task.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_windows_stop_and_pidfile.py +0 -0
- {devops_bot_sdk-1.6.87 → devops_bot_sdk-1.6.89}/tests/test_windows_task_paths.py +0 -0
- {devops_bot_sdk-1.6.87 → 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
|
|
@@ -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
|
+
}
|
|
@@ -149,3 +149,53 @@ class TestPage:
|
|
|
149
149
|
js = (STATIC / "app.js").read_text()
|
|
150
150
|
assert 'href="/help.html"' in js and 'page === "help"' in js
|
|
151
151
|
assert "/api/ui/help/ask" in js and "/api/ui/help/doctor" in js
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# ── The page's script, not just its strings ──────────────────────────────────
|
|
155
|
+
# Added after the Help page shipped with `initHelp` using `$` without defining it:
|
|
156
|
+
# every check above passed (they look for strings), and Ask did nothing.
|
|
157
|
+
|
|
158
|
+
import re
|
|
159
|
+
import shutil
|
|
160
|
+
import subprocess
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _init_functions(js: str) -> dict[str, str]:
|
|
164
|
+
"""`function initX() { … }` bodies, cut at the next top-level `function`."""
|
|
165
|
+
out = {}
|
|
166
|
+
for m in re.finditer(r"\n function (init\w+)\(\) \{", js):
|
|
167
|
+
rest = js[m.end():]
|
|
168
|
+
nxt = re.search(r"\n (?:async )?function \w+\(", rest)
|
|
169
|
+
out[m.group(1)] = rest[: nxt.start() if nxt else len(rest)]
|
|
170
|
+
return out
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_every_page_init_defines_the_dollar_it_uses():
|
|
174
|
+
js = (STATIC / "app.js").read_text()
|
|
175
|
+
inits = _init_functions(js)
|
|
176
|
+
assert "initHelp" in inits
|
|
177
|
+
missing = [name for name, body in inits.items()
|
|
178
|
+
if "$(" in body and "const $ =" not in body
|
|
179
|
+
and not re.search(r"\n const \$ =", js[: js.index("function " + name)])]
|
|
180
|
+
assert not missing, f"these use $(…) without defining it: {missing}"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@pytest.mark.skipif(not shutil.which("node"), reason="node is not installed")
|
|
184
|
+
def test_the_answer_renderer_escapes_and_structures():
|
|
185
|
+
js = (STATIC / "app.js").read_text()
|
|
186
|
+
pieces = [re.search(r" function esc\(s\).*\n", js).group(0)]
|
|
187
|
+
start = js.index(" const HP_SECTIONS")
|
|
188
|
+
pieces.append(js[start: js.index(" async function hpCopy")])
|
|
189
|
+
answer = ("Diagnosis: loop is <b>not</b> running\n\nEvidence:\n- diagnostics.md:7: `x`\n\n"
|
|
190
|
+
"Fix:\n1. run `agentos-auto`\n ```\n agentos-auto logs -f\n ```\n"
|
|
191
|
+
"⚠ `set-status` changes status\n<script>alert(1)</script>\n"
|
|
192
|
+
"If that doesn't work: send the folder")
|
|
193
|
+
script = "\n".join(pieces) + f"\nprocess.stdout.write(renderHelpAnswer({answer!r}));"
|
|
194
|
+
html = subprocess.run(["node", "-e", script], capture_output=True, text=True, check=True).stdout
|
|
195
|
+
assert "<script>" not in html and "<script>" in html
|
|
196
|
+
assert "<b>not</b>" in html
|
|
197
|
+
for cls in ("hp-sec-diagnosis", "hp-sec-evidence", "hp-sec-fix", "hp-sec-next"):
|
|
198
|
+
assert cls in html
|
|
199
|
+
assert "<ol><li>run <code>agentos-auto</code></li></ol>" in html
|
|
200
|
+
assert "<pre><code>agentos-auto logs -f</code></pre>" in html, "fenced block dedented"
|
|
201
|
+
assert '<div class="hp-warn"><code>set-status</code> changes status</div>' in html
|