devops-bot-sdk 1.6.25__tar.gz → 1.6.28__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.25 → devops_bot_sdk-1.6.28}/PKG-INFO +3 -1
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/devops_bot_sdk.egg-info/PKG-INFO +3 -1
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/devops_bot_sdk.egg-info/SOURCES.txt +6 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/devops_bot_sdk.egg-info/requires.txt +2 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/pyproject.toml +5 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/activity.py +47 -14
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/cli.py +278 -1
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/client.py +230 -1
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/exceptions.py +17 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/models/responses.py +73 -0
- devops_bot_sdk-1.6.28/sdk/proof_readers.py +420 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/run_auto.py +89 -1
- devops_bot_sdk-1.6.28/sdk/self_report.py +768 -0
- devops_bot_sdk-1.6.28/sdk/self_report_local.py +631 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/api.py +240 -1
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/models.py +29 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/app.js +202 -1
- devops_bot_sdk-1.6.28/sdk/ui/static/report.html +97 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/styles.css +30 -0
- devops_bot_sdk-1.6.28/tests/test_proof_readers.py +263 -0
- devops_bot_sdk-1.6.28/tests/test_self_report.py +1060 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/README.md +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/login.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_otp_login.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.25 → devops_bot_sdk-1.6.28}/tests/test_vps_health.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devops-bot-sdk
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.28
|
|
4
4
|
Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
|
|
5
5
|
Author: noumanaziz2128
|
|
6
6
|
License-Expression: LicenseRef-Proprietary
|
|
@@ -21,11 +21,13 @@ Provides-Extra: ui
|
|
|
21
21
|
Requires-Dist: fastapi>=0.110; extra == "ui"
|
|
22
22
|
Requires-Dist: uvicorn>=0.29; extra == "ui"
|
|
23
23
|
Requires-Dist: psutil>=5.9; extra == "ui"
|
|
24
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "ui"
|
|
24
25
|
Provides-Extra: all
|
|
25
26
|
Requires-Dist: Pillow>=10.0; extra == "all"
|
|
26
27
|
Requires-Dist: psutil>=5.9; extra == "all"
|
|
27
28
|
Requires-Dist: fastapi>=0.110; extra == "all"
|
|
28
29
|
Requires-Dist: uvicorn>=0.29; extra == "all"
|
|
30
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "all"
|
|
29
31
|
|
|
30
32
|
# DevOpsBot SDK
|
|
31
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devops-bot-sdk
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.28
|
|
4
4
|
Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
|
|
5
5
|
Author: noumanaziz2128
|
|
6
6
|
License-Expression: LicenseRef-Proprietary
|
|
@@ -21,11 +21,13 @@ Provides-Extra: ui
|
|
|
21
21
|
Requires-Dist: fastapi>=0.110; extra == "ui"
|
|
22
22
|
Requires-Dist: uvicorn>=0.29; extra == "ui"
|
|
23
23
|
Requires-Dist: psutil>=5.9; extra == "ui"
|
|
24
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "ui"
|
|
24
25
|
Provides-Extra: all
|
|
25
26
|
Requires-Dist: Pillow>=10.0; extra == "all"
|
|
26
27
|
Requires-Dist: psutil>=5.9; extra == "all"
|
|
27
28
|
Requires-Dist: fastapi>=0.110; extra == "all"
|
|
28
29
|
Requires-Dist: uvicorn>=0.29; extra == "all"
|
|
30
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "all"
|
|
29
31
|
|
|
30
32
|
# DevOpsBot SDK
|
|
31
33
|
|
|
@@ -24,9 +24,12 @@ sdk/local_exec.py
|
|
|
24
24
|
sdk/phase_timer.py
|
|
25
25
|
sdk/plan_run.py
|
|
26
26
|
sdk/platform_compat.py
|
|
27
|
+
sdk/proof_readers.py
|
|
27
28
|
sdk/py.typed
|
|
28
29
|
sdk/run_auto.py
|
|
29
30
|
sdk/scheduler.py
|
|
31
|
+
sdk/self_report.py
|
|
32
|
+
sdk/self_report_local.py
|
|
30
33
|
sdk/sse.py
|
|
31
34
|
sdk/task_file.py
|
|
32
35
|
sdk/test.py
|
|
@@ -98,6 +101,7 @@ sdk/ui/static/custom-agents.html
|
|
|
98
101
|
sdk/ui/static/default-agents.html
|
|
99
102
|
sdk/ui/static/index.html
|
|
100
103
|
sdk/ui/static/login.html
|
|
104
|
+
sdk/ui/static/report.html
|
|
101
105
|
sdk/ui/static/runs.html
|
|
102
106
|
sdk/ui/static/schedules.html
|
|
103
107
|
sdk/ui/static/setup.html
|
|
@@ -135,11 +139,13 @@ tests/test_otp_login.py
|
|
|
135
139
|
tests/test_phase_timer.py
|
|
136
140
|
tests/test_plan_new_command.py
|
|
137
141
|
tests/test_plan_run_activity.py
|
|
142
|
+
tests/test_proof_readers.py
|
|
138
143
|
tests/test_recon_session_isolation.py
|
|
139
144
|
tests/test_run_auto_agent_only.py
|
|
140
145
|
tests/test_run_outputs.py
|
|
141
146
|
tests/test_schedule_api.py
|
|
142
147
|
tests/test_scheduler.py
|
|
148
|
+
tests/test_self_report.py
|
|
143
149
|
tests/test_service_token_warning.py
|
|
144
150
|
tests/test_task_file_and_plan_run.py
|
|
145
151
|
tests/test_task_file_format_doc.py
|
|
@@ -36,12 +36,17 @@ ui = [
|
|
|
36
36
|
# bootstrap installs `[ui]` only, so without it here every
|
|
37
37
|
# VPS reports "resources.available: False" and the health
|
|
38
38
|
# cron can only send status + IP.
|
|
39
|
+
"python-multipart>=0.0.9", # POST /api/ui/reports/self — the ONE multipart route in
|
|
40
|
+
# the panel (self-report proofs are file uploads). Starlette
|
|
41
|
+
# raises at request time without it, so it belongs in [ui]
|
|
42
|
+
# alongside fastapi rather than in an optional extra.
|
|
39
43
|
]
|
|
40
44
|
all = [
|
|
41
45
|
"Pillow>=10.0",
|
|
42
46
|
"psutil>=5.9",
|
|
43
47
|
"fastapi>=0.110",
|
|
44
48
|
"uvicorn>=0.29",
|
|
49
|
+
"python-multipart>=0.0.9",
|
|
45
50
|
]
|
|
46
51
|
|
|
47
52
|
[project.scripts]
|
|
@@ -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.28
|
|
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.28" # 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
|
|
@@ -124,6 +124,46 @@ def _lines_block(lines: list[str]) -> str:
|
|
|
124
124
|
return "\n".join(reversed(out))
|
|
125
125
|
|
|
126
126
|
|
|
127
|
+
def mirror_to_auto_log_line(text: str, *, pointer: str = "") -> bool:
|
|
128
|
+
"""Append ONE already-composed summary line to ``~/.agentos/auto.log``.
|
|
129
|
+
|
|
130
|
+
The append-with-opt-out core of :func:`mirror_to_auto_log`, split out so a
|
|
131
|
+
caller that is not an agent run can use it. `agentos-auto logs [-f]` tails
|
|
132
|
+
that file and nothing else, so anything a person should be able to watch
|
|
133
|
+
live has to land here — including a self-report submission, which has no
|
|
134
|
+
agent, no `agent_dir()` and no `describe()` verb to render.
|
|
135
|
+
|
|
136
|
+
`text` is written verbatim under the shared ` • [<stamp>] ` prefix so every
|
|
137
|
+
line in the file reads the same way. `pointer` adds the indented `↳` line
|
|
138
|
+
the agent path uses for a transcript path; pass an empty string for none.
|
|
139
|
+
|
|
140
|
+
Honours ``AGENTOS_NO_AGENT_LOG_MIRROR=1`` and never raises: a log line must
|
|
141
|
+
never break the work that produced it. Returns True when a line was written.
|
|
142
|
+
"""
|
|
143
|
+
line = str(text or "").strip()
|
|
144
|
+
if not line:
|
|
145
|
+
return False
|
|
146
|
+
if os.getenv("AGENTOS_NO_AGENT_LOG_MIRROR"):
|
|
147
|
+
return False
|
|
148
|
+
try:
|
|
149
|
+
from sdk.run_auto import _LOG_FILE
|
|
150
|
+
except Exception: # noqa: BLE001 — never break a caller over a log line
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
stamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%SZ")
|
|
154
|
+
out = f" • [{stamp}] {line}"
|
|
155
|
+
if pointer:
|
|
156
|
+
out += f"\n ↳ {pointer}"
|
|
157
|
+
try:
|
|
158
|
+
_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
159
|
+
with _LOG_FILE.open("a", encoding="utf-8") as fh:
|
|
160
|
+
fh.write(out + "\n")
|
|
161
|
+
return True
|
|
162
|
+
except Exception as exc: # noqa: BLE001
|
|
163
|
+
logger.debug("auto.log mirror failed err=%s", exc)
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
|
|
127
167
|
def mirror_to_auto_log(name: str, kind: str, result: dict | None,
|
|
128
168
|
duration_s: float = 0.0, *, detail: str = "",
|
|
129
169
|
task_id: str = "") -> bool:
|
|
@@ -141,18 +181,20 @@ def mirror_to_auto_log(name: str, kind: str, result: dict | None,
|
|
|
141
181
|
the pointer below is how you get from one to the other. Best-effort and
|
|
142
182
|
silent on failure: a log line must never break a run. Set
|
|
143
183
|
``AGENTOS_NO_AGENT_LOG_MIRROR=1`` to turn it off.
|
|
184
|
+
|
|
185
|
+
The append itself lives in :func:`mirror_to_auto_log_line`; what stays here
|
|
186
|
+
is the AGENT-shaped part — the `describe()` verb, the ticket suffix and the
|
|
187
|
+
`agent_dir()` transcript pointer — none of which a non-agent caller wants.
|
|
144
188
|
"""
|
|
145
189
|
if os.getenv("AGENTOS_NO_AGENT_LOG_MIRROR"):
|
|
146
190
|
return False
|
|
147
191
|
try:
|
|
148
192
|
from sdk.agents.run_log import agent_dir
|
|
149
|
-
from sdk.run_auto import _LOG_FILE
|
|
150
193
|
except Exception: # noqa: BLE001 — never break a run over a log line
|
|
151
194
|
return False
|
|
152
195
|
|
|
153
196
|
res = result or {}
|
|
154
|
-
|
|
155
|
-
line = f" • [{stamp}] {describe(name, kind, res, duration_s, detail)}"
|
|
197
|
+
line = describe(name, kind, res, duration_s, detail)
|
|
156
198
|
# agentd passes the ticket BOTH as `detail` ("task T1") and as `task_id`, so an
|
|
157
199
|
# unconditional suffix renders "… — task T1 · task T1".
|
|
158
200
|
if task_id and f"task {task_id}" not in line:
|
|
@@ -161,17 +203,8 @@ def mirror_to_auto_log(name: str, kind: str, result: dict | None,
|
|
|
161
203
|
where = agent_dir(name)
|
|
162
204
|
except Exception: # noqa: BLE001
|
|
163
205
|
where = None
|
|
164
|
-
|
|
165
|
-
line
|
|
166
|
-
|
|
167
|
-
try:
|
|
168
|
-
_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
169
|
-
with _LOG_FILE.open("a", encoding="utf-8") as fh:
|
|
170
|
-
fh.write(line + "\n")
|
|
171
|
-
return True
|
|
172
|
-
except Exception as exc: # noqa: BLE001
|
|
173
|
-
logger.debug("auto.log mirror failed agent=%s err=%s", name, exc)
|
|
174
|
-
return False
|
|
206
|
+
return mirror_to_auto_log_line(
|
|
207
|
+
line, pointer=f"transcript: {where}" if where else "")
|
|
175
208
|
|
|
176
209
|
|
|
177
210
|
async def emit(name: str, kind: str, result: dict | None, duration_s: float = 0.0,
|
|
@@ -58,6 +58,22 @@ Command surface:
|
|
|
58
58
|
agentos integration rm <name>
|
|
59
59
|
Remove a registered integration.
|
|
60
60
|
|
|
61
|
+
agentos report submit [--date YYYY-MM-DD] (--text "…" | --file PATH)
|
|
62
|
+
[--project NAME] [--proof PATH|URL]… [--no-wait]
|
|
63
|
+
Report work that never reached Jira or GitHub, attach proofs (local
|
|
64
|
+
files and/or http(s) URLs), and have the submission SCORED against
|
|
65
|
+
them. Prints the submission id, the score and the verdict. The same
|
|
66
|
+
mechanism the control panel's Report work page uses — see
|
|
67
|
+
sdk/self_report.py.
|
|
68
|
+
agentos report list [--limit N]
|
|
69
|
+
Recent submissions, newest first: date, score, verdict, id.
|
|
70
|
+
agentos report formats
|
|
71
|
+
Every proof format the SDK can read on this machine, and what happens
|
|
72
|
+
to the ones it cannot.
|
|
73
|
+
agentos report show <ID>
|
|
74
|
+
One submission in full — its text, its proofs and the evaluator's
|
|
75
|
+
per-criterion reasoning.
|
|
76
|
+
|
|
61
77
|
agentos agentd start [--foreground]
|
|
62
78
|
Start the local agent daemon (background by default).
|
|
63
79
|
agentos agentd status
|
|
@@ -123,12 +139,16 @@ def main() -> None:
|
|
|
123
139
|
_github_command(rest)
|
|
124
140
|
return
|
|
125
141
|
|
|
142
|
+
if noun == "report":
|
|
143
|
+
_report_command(rest)
|
|
144
|
+
return
|
|
145
|
+
|
|
126
146
|
_print_usage()
|
|
127
147
|
sys.exit(1)
|
|
128
148
|
|
|
129
149
|
|
|
130
150
|
def _print_usage() -> None:
|
|
131
|
-
print("usage: agentos <configure|chat|agent|integration|default-agents|agentd|github> ...")
|
|
151
|
+
print("usage: agentos <configure|chat|agent|integration|default-agents|agentd|github|report> ...")
|
|
132
152
|
print()
|
|
133
153
|
print(" agentos configure [--rotate]")
|
|
134
154
|
print(" agentos chat [--product-id ID] [--pv-id ID] [--no-autorun] # requirements → ticket")
|
|
@@ -138,6 +158,7 @@ def _print_usage() -> None:
|
|
|
138
158
|
print(" agentos default-agents list [--department-id UUID] # read-only; agents are created in the web app")
|
|
139
159
|
print(" agentos agentd <start [--foreground]|status>")
|
|
140
160
|
print(" agentos github <ingest [--days N]|status> # PR ingestion; --days N backfills N days")
|
|
161
|
+
print(" agentos report <submit|list|show ID> # report work Jira/GitHub never saw")
|
|
141
162
|
|
|
142
163
|
|
|
143
164
|
# ── agent ────────────────────────────────────────────────────────────────
|
|
@@ -703,5 +724,261 @@ def _github_command(args: list[str]) -> None:
|
|
|
703
724
|
sys.exit(1)
|
|
704
725
|
|
|
705
726
|
|
|
727
|
+
# ── report ───────────────────────────────────────────────────────────────
|
|
728
|
+
|
|
729
|
+
def _report_usage() -> None:
|
|
730
|
+
print("usage: agentos report submit [--date YYYY-MM-DD] (--text \"…\" | --file PATH)")
|
|
731
|
+
print(" [--project NAME] [--proof PATH|URL]… [--no-wait]")
|
|
732
|
+
print(" agentos report list [--limit N]")
|
|
733
|
+
print(" agentos report show <ID>")
|
|
734
|
+
print(" agentos report formats # what a proof can be")
|
|
735
|
+
print()
|
|
736
|
+
print(" Report work that never reached Jira or GitHub, attach proofs, and have the")
|
|
737
|
+
print(" submission scored against them. It then appears in your daily and overall")
|
|
738
|
+
print(" reports and in the reporting chat.")
|
|
739
|
+
print()
|
|
740
|
+
print(" --date the day the work happened (default: today; never the future)")
|
|
741
|
+
print(" --text the report body; --file reads it from a file instead")
|
|
742
|
+
print(" --proof repeatable — a local file path OR an http(s) URL")
|
|
743
|
+
print(" --no-wait submit and exit without waiting for the verdict")
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def _report_flag(args: list[str], name: str) -> list[str]:
|
|
747
|
+
"""Every ``--name VALUE`` in args, in order. Repeatable flags collect.
|
|
748
|
+
|
|
749
|
+
Same hand-rolled shape as `run_auto._flag_values` — this CLI has no
|
|
750
|
+
argparse subparsers by design (see the module docstring).
|
|
751
|
+
"""
|
|
752
|
+
out: list[str] = []
|
|
753
|
+
for i, a in enumerate(args):
|
|
754
|
+
if a == name and i + 1 < len(args):
|
|
755
|
+
out.append(args[i + 1])
|
|
756
|
+
elif a.startswith(name + "="):
|
|
757
|
+
out.append(a.split("=", 1)[1])
|
|
758
|
+
return out
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def _report_command(args: list[str]) -> None:
|
|
762
|
+
"""``agentos report submit|list|show`` — the CLI half of self-reporting.
|
|
763
|
+
|
|
764
|
+
Every verb resolves the backend client the same way and fails the same way
|
|
765
|
+
when the box has no ``co_`` token: the shared "not configured" message
|
|
766
|
+
``agentos configure`` prints, and exit 1 — never a traceback.
|
|
767
|
+
"""
|
|
768
|
+
from sdk.exceptions import TokenNotConfigured
|
|
769
|
+
from sdk.self_report import _SUBMIT_ERRORS, SelfReportError
|
|
770
|
+
|
|
771
|
+
verb = args[0] if args else None
|
|
772
|
+
rest = args[1:]
|
|
773
|
+
|
|
774
|
+
try:
|
|
775
|
+
if verb == "submit":
|
|
776
|
+
_report_submit(rest)
|
|
777
|
+
return
|
|
778
|
+
if verb == "list":
|
|
779
|
+
limits = _report_flag(rest, "--limit")
|
|
780
|
+
_report_list(int(limits[0]) if limits and limits[0].isdigit() else 25)
|
|
781
|
+
return
|
|
782
|
+
if verb == "show" and rest and not rest[0].startswith("-"):
|
|
783
|
+
_report_show(rest[0])
|
|
784
|
+
return
|
|
785
|
+
if verb == "formats":
|
|
786
|
+
_report_formats()
|
|
787
|
+
return
|
|
788
|
+
except TokenNotConfigured as exc:
|
|
789
|
+
# The same failure every other verb gives on an unconfigured machine —
|
|
790
|
+
# `sdk.config` composes a cause-specific message; we only add the prefix.
|
|
791
|
+
print(f"error: {exc}")
|
|
792
|
+
sys.exit(1)
|
|
793
|
+
except SelfReportError as exc:
|
|
794
|
+
print(f"error: {exc}")
|
|
795
|
+
sys.exit(1)
|
|
796
|
+
except _SUBMIT_ERRORS as exc:
|
|
797
|
+
# The backend answered and the answer was no. Its own words — a 404 says
|
|
798
|
+
# the deployment has no self-report routes, which is not a token problem
|
|
799
|
+
# and must not be printed as one.
|
|
800
|
+
print(f"error: {exc}")
|
|
801
|
+
sys.exit(1)
|
|
802
|
+
|
|
803
|
+
_report_usage()
|
|
804
|
+
sys.exit(1)
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def _report_submit(args: list[str]) -> None:
|
|
808
|
+
"""Submit one report, then print the id, the score and the verdict."""
|
|
809
|
+
from sdk import self_report
|
|
810
|
+
|
|
811
|
+
texts = _report_flag(args, "--text")
|
|
812
|
+
files = _report_flag(args, "--file")
|
|
813
|
+
if not texts and not files:
|
|
814
|
+
# A usage line and a non-zero exit — not an empty submission.
|
|
815
|
+
print("error: say what you did — pass --text \"…\" or --file report.md")
|
|
816
|
+
_report_usage()
|
|
817
|
+
sys.exit(1)
|
|
818
|
+
|
|
819
|
+
body = texts[0] if texts else ""
|
|
820
|
+
if files:
|
|
821
|
+
src = Path(files[0]).expanduser()
|
|
822
|
+
if not src.is_file():
|
|
823
|
+
print(f"error: no such file: {src}")
|
|
824
|
+
sys.exit(1)
|
|
825
|
+
try:
|
|
826
|
+
# --file supplies the body; a --text alongside it is a header line.
|
|
827
|
+
file_body = src.read_text(encoding="utf-8", errors="replace").strip()
|
|
828
|
+
except OSError as exc:
|
|
829
|
+
print(f"error: cannot read {src}: {exc}")
|
|
830
|
+
sys.exit(1)
|
|
831
|
+
body = f"{body}\n\n{file_body}".strip() if body else file_body
|
|
832
|
+
|
|
833
|
+
dates = _report_flag(args, "--date")
|
|
834
|
+
projects = _report_flag(args, "--project")
|
|
835
|
+
proofs, rejected = self_report.collect_proofs(_report_flag(args, "--proof"))
|
|
836
|
+
for r in rejected:
|
|
837
|
+
print(f"⚠ proof skipped — {r.get('name', '')}: {r.get('reason', '')}")
|
|
838
|
+
|
|
839
|
+
result = asyncio.run(self_report.submit_and_evaluate(
|
|
840
|
+
date=dates[0] if dates else "",
|
|
841
|
+
text=body,
|
|
842
|
+
project=projects[0] if projects else "",
|
|
843
|
+
proofs=proofs,
|
|
844
|
+
rejected=rejected,
|
|
845
|
+
wait="--no-wait" not in args,
|
|
846
|
+
))
|
|
847
|
+
|
|
848
|
+
status = str(result.get("status") or "")
|
|
849
|
+
if status == "failed":
|
|
850
|
+
print(f"error: {result.get('error') or 'the submission was not stored.'}")
|
|
851
|
+
sys.exit(1)
|
|
852
|
+
|
|
853
|
+
print(f"submission : {result.get('id') or '(no id returned)'}")
|
|
854
|
+
print(f"date : {result.get('date', '')}"
|
|
855
|
+
+ (f" project: {result['project']}" if result.get("project") else ""))
|
|
856
|
+
print(f"proofs : {len(result.get('proofs') or [])} accepted"
|
|
857
|
+
+ (f", {len(result.get('rejected') or [])} skipped" if result.get("rejected") else ""))
|
|
858
|
+
if result.get("stored") == "gateway":
|
|
859
|
+
# Say which half holds it. "Recorded" and "recorded in the durable store"
|
|
860
|
+
# are different promises, and only the person filing it can decide whether
|
|
861
|
+
# the difference matters to them.
|
|
862
|
+
print("stored : locally + the activity feed the daily/overall reports read")
|
|
863
|
+
print(" (the platform's self-report store is not deployed on this "
|
|
864
|
+
"backend; scored on this machine)")
|
|
865
|
+
if status == "evaluated":
|
|
866
|
+
print(f"score : {result.get('score', 0)}/100")
|
|
867
|
+
print(f"verdict : {result.get('verdict') or 'unknown'}")
|
|
868
|
+
elif status == "evaluation_failed":
|
|
869
|
+
print("score : —")
|
|
870
|
+
print("verdict : evaluation failed — the submission is stored and can be "
|
|
871
|
+
"re-evaluated")
|
|
872
|
+
else:
|
|
873
|
+
print(f"status : {status or 'accepted'} — not evaluated yet; "
|
|
874
|
+
f"`agentos report show {result.get('id', '')}` for the verdict")
|
|
875
|
+
# Exit 0 whenever the submission was STORED. A failed evaluation is a
|
|
876
|
+
# backend-side outcome the person cannot act on, and it is recorded, not lost.
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def _report_formats() -> None:
|
|
880
|
+
"""What the SDK can read from a proof without the backend.
|
|
881
|
+
|
|
882
|
+
Worth its own verb: a person choosing what to attach needs to know that a
|
|
883
|
+
screen recording is usable and a .7z is not BEFORE they file, not after the
|
|
884
|
+
score comes back lower than the work deserved.
|
|
885
|
+
"""
|
|
886
|
+
from sdk import proof_readers
|
|
887
|
+
|
|
888
|
+
print("Proof files the SDK reads on this machine:\n")
|
|
889
|
+
for group, exts in proof_readers.supported().items():
|
|
890
|
+
joined = " ".join(exts)
|
|
891
|
+
print(f" {group}")
|
|
892
|
+
for i in range(0, len(joined), 68):
|
|
893
|
+
print(f" {joined[i:i + 68]}")
|
|
894
|
+
print()
|
|
895
|
+
if proof_readers.have_ffmpeg():
|
|
896
|
+
print(" ffmpeg is installed — video proofs are sampled into still frames the")
|
|
897
|
+
print(" scorer can look at.")
|
|
898
|
+
else:
|
|
899
|
+
print(" ffmpeg is NOT installed — a video proof is recorded and described, but")
|
|
900
|
+
print(" its content cannot be inspected. Install ffmpeg to change that.")
|
|
901
|
+
print(" Audio cannot be transcribed locally; it is recorded and described.")
|
|
902
|
+
print(" URL proofs are fetched here. A GitHub PR or issue is read through the API")
|
|
903
|
+
print(" with your linked token, so proofs in private repos are readable.")
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
def _report_list(limit: int) -> None:
|
|
907
|
+
"""Recent submissions, newest first — date, score, verdict, id."""
|
|
908
|
+
from sdk import self_report
|
|
909
|
+
|
|
910
|
+
rows = asyncio.run(self_report.list_reports(limit))
|
|
911
|
+
if not rows:
|
|
912
|
+
print("no submissions yet — `agentos report submit --text \"…\"` records one.")
|
|
913
|
+
return
|
|
914
|
+
print(f"{'DATE':<12}{'SCORE':<8}{'VERDICT':<26}{'ID':<26}WHAT")
|
|
915
|
+
for r in rows:
|
|
916
|
+
ev = r.get("evaluation") or {}
|
|
917
|
+
score = f"{ev.get('score')}/100" if r.get("status") == "evaluated" else "—"
|
|
918
|
+
verdict = (ev.get("verdict") or r.get("status") or "")[:25]
|
|
919
|
+
what = " ".join(str(r.get("text") or "").split())[:46]
|
|
920
|
+
rid = str(r.get("id", "")) + (" *" if r.get("stored") == "gateway" else "")
|
|
921
|
+
print(f"{str(r.get('date', '')):<12}{score:<8}{verdict:<26}{rid:<26}{what}")
|
|
922
|
+
if any(r.get("stored") == "gateway" for r in rows):
|
|
923
|
+
print("\n* recorded on this machine and in the activity feed — the platform's "
|
|
924
|
+
"self-report store was not deployed when it was filed.")
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def _report_show(report_id: str) -> None:
|
|
928
|
+
"""One submission in full — its text, its proofs, and the per-criterion reasoning."""
|
|
929
|
+
from sdk import self_report
|
|
930
|
+
|
|
931
|
+
sub = asyncio.run(self_report.show_report(report_id))
|
|
932
|
+
if not sub:
|
|
933
|
+
print(f"error: no submission {report_id} (or it is not yours).")
|
|
934
|
+
sys.exit(1)
|
|
935
|
+
|
|
936
|
+
ev = sub.get("evaluation") or {}
|
|
937
|
+
print(f"submission : {sub.get('id', report_id)}")
|
|
938
|
+
print(f"date : {sub.get('date', '')}")
|
|
939
|
+
if sub.get("project"):
|
|
940
|
+
print(f"project : {sub['project']}")
|
|
941
|
+
print(f"status : {sub.get('status', '')}")
|
|
942
|
+
if sub.get("status") == "evaluated":
|
|
943
|
+
print(f"score : {ev.get('score', 0)}/100")
|
|
944
|
+
print(f"verdict : {ev.get('verdict', '')}")
|
|
945
|
+
print()
|
|
946
|
+
print("what I did")
|
|
947
|
+
for line in str(sub.get("text") or "").splitlines() or [""]:
|
|
948
|
+
print(f" {line}")
|
|
949
|
+
|
|
950
|
+
proofs = sub.get("proofs") or []
|
|
951
|
+
print()
|
|
952
|
+
print(f"proofs ({len(proofs)})")
|
|
953
|
+
if not proofs:
|
|
954
|
+
print(" (none — an unproven report is recorded, never silently dropped)")
|
|
955
|
+
for p in proofs:
|
|
956
|
+
head = f" · {p.get('name') or p.get('url') or 'proof'}"
|
|
957
|
+
judged = p.get("judgement") or "not judged"
|
|
958
|
+
print(f"{head} [{judged}]")
|
|
959
|
+
if p.get("url"):
|
|
960
|
+
print(f" url : {p['url']}")
|
|
961
|
+
if p.get("size_bytes"):
|
|
962
|
+
print(f" size : {p['size_bytes']} bytes {p.get('content_type', '')}")
|
|
963
|
+
if p.get("reason"):
|
|
964
|
+
print(f" why : {p['reason']}")
|
|
965
|
+
|
|
966
|
+
findings = ev.get("findings") or []
|
|
967
|
+
print()
|
|
968
|
+
print(f"evaluator findings ({len(findings)})")
|
|
969
|
+
if ev.get("summary"):
|
|
970
|
+
print(f" {ev['summary']}")
|
|
971
|
+
for f in findings:
|
|
972
|
+
print(f" · [{f.get('verdict', '')}] {f.get('criterion', '')}")
|
|
973
|
+
if f.get("detail"):
|
|
974
|
+
print(f" {f['detail']}")
|
|
975
|
+
if not findings and sub.get("status") == "evaluation_failed":
|
|
976
|
+
print(" the evaluator returned unparseable output — the raw response is retained "
|
|
977
|
+
"server-side for diagnosis, and the submission can be re-evaluated.")
|
|
978
|
+
|
|
979
|
+
for r in (sub.get("rejected") or []):
|
|
980
|
+
print(f"⚠ proof not accepted — {r.get('name', '')}: {r.get('reason', '')}")
|
|
981
|
+
|
|
982
|
+
|
|
706
983
|
if __name__ == "__main__":
|
|
707
984
|
main()
|