netizen-cli 0.10.0__tar.gz → 0.10.1__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.
- {netizen_cli-0.10.0/netizen_cli.egg-info → netizen_cli-0.10.1}/PKG-INFO +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/README.md +3 -4
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/__init__.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/port_config.py +2 -19
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/presentation.py +0 -34
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/transport.py +4 -12
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/web.py +2 -7
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/bindings.py +9 -116
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cards/controls.py +0 -32
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cards/reply.py +0 -15
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel_app.py +2 -20
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cli_packages.py +5 -66
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cli_services.py +0 -4
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/codex_runtime.py +0 -50
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/defaults/models.py +0 -4
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/management/__init__.py +0 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/management/service.py +0 -23
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/message_projection.py +11 -36
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/projects.py +0 -7
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/schedules/models.py +0 -4
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/schedules/service.py +3 -17
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/schedules/store.py +4 -12
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/sdk_gap_adapter.py +10 -36
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/turn_files.py +13 -68
- {netizen_cli-0.10.0 → netizen_cli-0.10.1/netizen_cli.egg-info}/PKG-INFO +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/pyproject.toml +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_bindings.py +58 -48
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_channel_app.py +21 -24
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli_packages.py +41 -62
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_codex_runtime.py +85 -58
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_deployment.py +47 -18
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_main.py +4 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_project_delete_channel.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_project_delete_runtime.py +3 -3
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_project_deletion_storage.py +3 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_question_channel.py +5 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_question_target_parity.py +3 -3
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_quoted_context.py +39 -21
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_runtime_thread_naming.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_schedule_scope_matrix.py +4 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_schedule_service.py +67 -12
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_scheduled_runtime.py +2 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_schedules_storage.py +12 -8
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_sdk_gap_adapter.py +9 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_session_settings.py +2 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_settings_projects.py +9 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_turn_files.py +37 -175
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_turn_patch_statistics.py +28 -8
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/MANIFEST.in +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/_build.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/config.example.yaml +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/__main__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/auth.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/errors.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/queries.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/static/admin.css +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/static/admin.js +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/admin/static/index.html +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/builtin_skills.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cards/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cards/callbacks.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cards/defaults.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cards/model_info.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cards/questions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cards/scheduled.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/completion_mentions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/input_preparation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/messages.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/ports.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/question_inputs.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/reactions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/reply_presenter.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/channel/topics.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cli.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cli_data.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cli_setup.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cli_update.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cli_update_restore.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/cli_update_worker.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/completion_mention.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/database_migrations.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/defaults/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/defaults/service.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/defaults/store.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/deployment/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/deployment/restart_worker.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/deployment/update_executor.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/deployment/update_protocol.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/domain.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/error_messages.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/experience.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/feishu_app_onboarding.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/feishu_app_permissions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/git_status.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/image_inputs.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/instance.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/lark_app.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/main.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/management/blocking_io.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/management/chat_labels.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/management/coordination.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/management/updates.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/markdown_images.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/message_content.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/message_history.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/message_preparation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/migrations/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/migrations/schema.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/migrations/v14.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/model_settings.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/package_resources.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/prompt_projection.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/quoted_context.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/result_images.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/runtime/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/runtime/contracts.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/runtime/name_writes.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/runtime/thread_naming.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/schedules/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/schedules/mcp.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/schedules/scheduler.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/service_launcher.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/session_settings.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/settings.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/skill_references.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/terminal_cleanup.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/turn_activity.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/turn_patch_children.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/turn_plan_observer.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli/user_questions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli.egg-info/SOURCES.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli.egg-info/dependency_links.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli.egg-info/entry_points.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli.egg-info/requires.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/netizen_cli.egg-info/top_level.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/setup.cfg +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/skills/netizen-lark/SKILL.md +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/skills/netizen-user-guide/SKILL.md +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/skills/netizen-user-guide/references/user-guide.md +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_admin_update_executor_probe.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_binding_scheduled_channel.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_channel_fixtures.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_channel_input_preparation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_check_sdk.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli_data.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli_distribution.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli_integration.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli_packaging.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli_services.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli_setup.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_cli_update.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_codex_sdk_capabilities.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_completion_mention_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_completion_mentions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_completion_topic_delivery.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_database_migrations.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_defaults_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_defaults_channel.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_defaults_runtime.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_documentation_links.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_domain_experience.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_error_messages.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_feishu_app_onboarding.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_feishu_app_permissions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_file_pagination.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_git_status.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_image_inputs.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_lark_app.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_lark_sdk_contract.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_markdown_images.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_message_content.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_message_history.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_message_material_inputs.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_message_preparation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_migration_schema_validation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_model_info_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_model_settings.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_name_writes.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_probe_child_files.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_probe_feishu_completion_mention.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_probe_feishu_turn_file_card.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_probe_python_sdk.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_probe_scheduled_tasks.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_probe_sdk_completion_race.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_probe_thread_naming.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_prompt_projection.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_question_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_release_script.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_repository_hygiene.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_result_images.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_retired_deployment.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_schedule_card_retry.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_schedule_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_schedule_mcp.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_schedule_run_now.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_scheduled_channel.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_scheduler.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_scheduler_presenter.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_service_launcher.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_session_defaults.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_terminal_cleanup.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_thread_naming.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_turn_activity.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_turn_patch_children.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_turn_plan_observer.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_update_executor.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_user_guide_skill.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.1}/tests/test_user_questions.py +0 -0
|
@@ -69,8 +69,8 @@ Linux 注销后常驻需要 linger;macOS 服务随桌面登录启动、注销
|
|
|
69
69
|
|
|
70
70
|
### 2. 安装 CLI 并创建实例
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
在选定 Python 环境从 PyPI 安装 `netizen-cli`。源码开发安装见
|
|
73
|
+
[贡献指南](docs/CONTRIBUTING.md)。
|
|
74
74
|
|
|
75
75
|
```sh
|
|
76
76
|
python -m pip install netizen-cli
|
|
@@ -91,8 +91,7 @@ setup 准备配置、飞书授权和实例数据并注册服务,不默认启
|
|
|
91
91
|
|
|
92
92
|
日常维护用 `netizen update` 更新当前环境并恢复原来运行的实例;它不接受 --root。
|
|
93
93
|
`netizen remove --root ...` 默认保留数据,--purge 才清理精确范围,-y 只省略确认。
|
|
94
|
-
完整流程、失败边界、跨环境切换和卸载见 [CLI 安装与维护](docs/cli.md)
|
|
95
|
-
不提供自动旧布局迁移,不要直接对现有旧实例执行新版清理命令。
|
|
94
|
+
完整流程、失败边界、跨环境切换和卸载见 [CLI 安装与维护](docs/cli.md)。
|
|
96
95
|
|
|
97
96
|
### 3. 在飞书开始第一次对话
|
|
98
97
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Atomic persistence of the Admin port allocated during first startup."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -56,7 +56,7 @@ def persist_admin_port(snapshot: ConfigFileSnapshot, port: int) -> None:
|
|
|
56
56
|
"""Preserve all YAML values and refuse a detected concurrent human edit.
|
|
57
57
|
|
|
58
58
|
The caller retains its listening socket and instance lifetime lock. This
|
|
59
|
-
deliberately does not acquire the
|
|
59
|
+
deliberately does not acquire the CLI maintenance lock (the CLI may still
|
|
60
60
|
be waiting for this startup). Only the previously absent port is added.
|
|
61
61
|
"""
|
|
62
62
|
|
|
@@ -75,23 +75,6 @@ def persist_admin_port(snapshot: ConfigFileSnapshot, port: int) -> None:
|
|
|
75
75
|
_write_configuration(snapshot, loaded)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def set_admin_port(path: Path, port: int) -> None:
|
|
79
|
-
"""Apply an explicit installer choice, even if Admin is currently disabled."""
|
|
80
|
-
|
|
81
|
-
_validate_port(port)
|
|
82
|
-
snapshot = ConfigFileSnapshot.read(path)
|
|
83
|
-
loaded = yaml.safe_load(snapshot.content)
|
|
84
|
-
if not isinstance(loaded, dict):
|
|
85
|
-
raise AdminPortConfigurationError("configuration root must be a mapping")
|
|
86
|
-
values = loaded.setdefault("adminWeb", {})
|
|
87
|
-
if not isinstance(values, dict):
|
|
88
|
-
raise AdminPortConfigurationError("adminWeb must be a mapping")
|
|
89
|
-
values = dict(values)
|
|
90
|
-
loaded["adminWeb"] = values
|
|
91
|
-
values["port"] = port
|
|
92
|
-
_write_configuration(snapshot, loaded)
|
|
93
|
-
|
|
94
|
-
|
|
95
78
|
def _validate_port(port: int) -> None:
|
|
96
79
|
if isinstance(port, bool) or not isinstance(port, int) or not 1 <= port <= 65535:
|
|
97
80
|
raise AdminPortConfigurationError("Admin port must be from 1 to 65535")
|
|
@@ -17,40 +17,6 @@ from ..runtime.contracts import (
|
|
|
17
17
|
from ..management import BindingStatusProjection, ChatLabel
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def _release_check_message(code: str | None) -> str | None:
|
|
21
|
-
if code is None:
|
|
22
|
-
return None
|
|
23
|
-
return {
|
|
24
|
-
"release_rate_limited": "GitHub 更新查询已触发限流,请稍后重试。",
|
|
25
|
-
"release_access_denied": (
|
|
26
|
-
"更新查询被拒绝(HTTP 403)。请检查服务所在机器的网络访问策略或代理配置;"
|
|
27
|
-
"若持续出现,请联系维护者。"
|
|
28
|
-
),
|
|
29
|
-
"release_not_found": (
|
|
30
|
-
"未找到可访问的官方发布版本(HTTP 404)。请稍后重试;"
|
|
31
|
-
"若持续出现,请联系维护者检查仓库与 Release 状态。"
|
|
32
|
-
),
|
|
33
|
-
"release_service_unavailable": (
|
|
34
|
-
"更新服务暂时异常(HTTP 5xx)。请稍后重新检查。"
|
|
35
|
-
),
|
|
36
|
-
"release_http_error": (
|
|
37
|
-
"更新查询收到异常 HTTP 响应。请稍后重试;"
|
|
38
|
-
"若持续出现,请联系维护者检查更新接口或网络代理。"
|
|
39
|
-
),
|
|
40
|
-
"release_check_timeout": (
|
|
41
|
-
"查询 GitHub 更新服务超时。请稍后重试;"
|
|
42
|
-
"若持续出现,请检查服务所在机器的网络或代理配置。"
|
|
43
|
-
),
|
|
44
|
-
"release_network_error": (
|
|
45
|
-
"访问 GitHub 更新服务时发生网络错误。请检查服务所在机器的网络、代理和 HTTPS 证书配置后再试。"
|
|
46
|
-
),
|
|
47
|
-
"release_invalid_response": (
|
|
48
|
-
"官方更新信息格式异常,或发布资产不完整、未通过校验。"
|
|
49
|
-
"请稍后重试;若持续出现,请联系维护者检查 Release。"
|
|
50
|
-
),
|
|
51
|
-
}.get(code, "检查更新失败,原因暂未识别。请稍后重试;若持续出现,请联系维护者。")
|
|
52
|
-
|
|
53
|
-
|
|
54
20
|
def _chat_open_url(chat: ChatLabel) -> str:
|
|
55
21
|
if chat.chat_mode == "p2p" and chat.p2p_target_open_id is not None:
|
|
56
22
|
query = urlencode({"openId": chat.p2p_target_open_id})
|
|
@@ -112,7 +112,6 @@ class _ClientDisconnected(Exception):
|
|
|
112
112
|
class _Connection:
|
|
113
113
|
writer: asyncio.StreamWriter
|
|
114
114
|
accepted_at: float
|
|
115
|
-
task: asyncio.Task[None] | None = None
|
|
116
115
|
handler_task: asyncio.Task[Response] | None = None
|
|
117
116
|
|
|
118
117
|
|
|
@@ -350,7 +349,6 @@ class AdminHttpTransport:
|
|
|
350
349
|
task = self._owned_loop().create_task(
|
|
351
350
|
self._serve_connection(connection, reader)
|
|
352
351
|
)
|
|
353
|
-
connection.task = task
|
|
354
352
|
self._track_task(task, self._connection_tasks)
|
|
355
353
|
|
|
356
354
|
async def _serve_connection(
|
|
@@ -365,17 +363,15 @@ class AdminHttpTransport:
|
|
|
365
363
|
h11.SERVER,
|
|
366
364
|
max_incomplete_event_size=MAX_HEADER_SECTION_BYTES,
|
|
367
365
|
)
|
|
368
|
-
pending = b""
|
|
369
366
|
first_request = True
|
|
370
367
|
|
|
371
368
|
try:
|
|
372
369
|
while not self._closing:
|
|
373
370
|
request_id = secrets.token_hex(16)
|
|
374
371
|
try:
|
|
375
|
-
request
|
|
372
|
+
request = await self._read_request(
|
|
376
373
|
reader=reader,
|
|
377
374
|
parser=parser,
|
|
378
|
-
pending=pending,
|
|
379
375
|
first_request=first_request,
|
|
380
376
|
accepted_at=connection.accepted_at,
|
|
381
377
|
peer=peer,
|
|
@@ -486,7 +482,6 @@ class AdminHttpTransport:
|
|
|
486
482
|
parser.start_next_cycle()
|
|
487
483
|
except h11.LocalProtocolError:
|
|
488
484
|
return
|
|
489
|
-
pending = b""
|
|
490
485
|
except asyncio.CancelledError:
|
|
491
486
|
handler = connection.handler_task
|
|
492
487
|
if handler is not None and not handler.done():
|
|
@@ -505,19 +500,17 @@ class AdminHttpTransport:
|
|
|
505
500
|
*,
|
|
506
501
|
reader: asyncio.StreamReader,
|
|
507
502
|
parser: h11.Connection,
|
|
508
|
-
pending: bytes,
|
|
509
503
|
first_request: bool,
|
|
510
504
|
accepted_at: float,
|
|
511
505
|
peer: Address,
|
|
512
506
|
sockname: Address,
|
|
513
507
|
request_id: str,
|
|
514
|
-
) ->
|
|
508
|
+
) -> Request:
|
|
515
509
|
loop = self._owned_loop()
|
|
510
|
+
pending = b""
|
|
516
511
|
|
|
517
512
|
if first_request:
|
|
518
513
|
header_deadline = accepted_at + HEADER_TIMEOUT_SECONDS
|
|
519
|
-
elif pending:
|
|
520
|
-
header_deadline = loop.time() + HEADER_TIMEOUT_SECONDS
|
|
521
514
|
else:
|
|
522
515
|
try:
|
|
523
516
|
first_byte = await asyncio.wait_for(
|
|
@@ -578,7 +571,7 @@ class AdminHttpTransport:
|
|
|
578
571
|
# entered are pipelining, including a same-write second request.
|
|
579
572
|
raise _RequestRejected(400, "pipelined_request")
|
|
580
573
|
|
|
581
|
-
|
|
574
|
+
return Request(
|
|
582
575
|
method=event.method,
|
|
583
576
|
target=event.target,
|
|
584
577
|
http_version=event.http_version,
|
|
@@ -588,7 +581,6 @@ class AdminHttpTransport:
|
|
|
588
581
|
sockname=sockname,
|
|
589
582
|
request_id=request_id,
|
|
590
583
|
)
|
|
591
|
-
return request, b""
|
|
592
584
|
|
|
593
585
|
async def _read_header_section(
|
|
594
586
|
self,
|
|
@@ -38,7 +38,6 @@ from .presentation import (
|
|
|
38
38
|
_chat_open_url,
|
|
39
39
|
_jsonable,
|
|
40
40
|
_project_result,
|
|
41
|
-
_release_check_message,
|
|
42
41
|
_release_disposition_message,
|
|
43
42
|
_runtime_binding_json,
|
|
44
43
|
_runtime_side_json,
|
|
@@ -127,7 +126,6 @@ from ..management import (
|
|
|
127
126
|
CurrentSideTarget,
|
|
128
127
|
ExactBindingTarget,
|
|
129
128
|
InstanceManagementService,
|
|
130
|
-
NativeCatalogInconsistent,
|
|
131
129
|
NativeThreadMissing,
|
|
132
130
|
ProjectDeletionResult,
|
|
133
131
|
RuntimePrecondition,
|
|
@@ -542,9 +540,9 @@ class AdminWebApplication:
|
|
|
542
540
|
self, context: _RequestContext, status: dict[str, Any]
|
|
543
541
|
) -> Response:
|
|
544
542
|
payload = dict(status)
|
|
545
|
-
|
|
543
|
+
payload.pop("checkingErrorCode")
|
|
546
544
|
payload["message"] = "程序更新属于 Python 环境级操作,请在对应环境运行 netizen update;管理页只重启当前实例。"
|
|
547
|
-
payload["checkingError"] =
|
|
545
|
+
payload["checkingError"] = None
|
|
548
546
|
actions: dict[str, object] = {
|
|
549
547
|
"check": None,
|
|
550
548
|
"install": None,
|
|
@@ -2608,10 +2606,8 @@ def _runtime_precondition(grant: object) -> RuntimePrecondition:
|
|
|
2608
2606
|
|
|
2609
2607
|
|
|
2610
2608
|
_UPDATE_HTTP_ERRORS = {
|
|
2611
|
-
"invalid_update_target": (400, "invalid_update_target", "升级目标无效。"),
|
|
2612
2609
|
"update_unsupported": (409, "update_unsupported", "请在对应 Python 环境运行 netizen update。"),
|
|
2613
2610
|
"restart_unsupported": (409, "restart_unsupported", "仅绑定当前 Python 环境的受管实例支持 Admin 重启。"),
|
|
2614
|
-
"update_target_changed": (409, "update_target_changed", "升级目标已变化,请重新检查更新。"),
|
|
2615
2611
|
"update_busy": (409, "update_busy", "另一个安装或维护操作正在执行,请稍后查看结果。"),
|
|
2616
2612
|
"update_state_unavailable": (
|
|
2617
2613
|
503, "update_state_unavailable", "维护状态无法确认,请检查实例维护记录。",
|
|
@@ -2693,7 +2689,6 @@ def _map_error(error: BaseException) -> AdminWebError | None:
|
|
|
2693
2689
|
BlockingIOResultUnknown,
|
|
2694
2690
|
BlockingIODrainTimeout,
|
|
2695
2691
|
BlockingIOShutdownTimeout,
|
|
2696
|
-
NativeCatalogInconsistent,
|
|
2697
2692
|
RuntimeClosed,
|
|
2698
2693
|
ThreadCatalogError,
|
|
2699
2694
|
ThreadLifecycleStateUnknown,
|
|
@@ -872,40 +872,6 @@ class BindingStore:
|
|
|
872
872
|
)
|
|
873
873
|
return tuple(self.get_side_topic(row["side_id"]) for row in rows)
|
|
874
874
|
|
|
875
|
-
def create_binding(
|
|
876
|
-
self,
|
|
877
|
-
*,
|
|
878
|
-
scope: FeishuScope,
|
|
879
|
-
project_alias: str,
|
|
880
|
-
creator_id: str,
|
|
881
|
-
turn_settings: BindingTurnSettings | None = None,
|
|
882
|
-
task_feedback: BindingTaskFeedback = BindingTaskFeedback(),
|
|
883
|
-
message_context_mode: MentionContextMode = MentionContextMode.CURRENT_ONLY,
|
|
884
|
-
context_anchor: MessageContextAnchor | None = None,
|
|
885
|
-
) -> ThreadBinding:
|
|
886
|
-
"""Compatibility entry point for older callers.
|
|
887
|
-
|
|
888
|
-
Production ingress should use :meth:`create_channel_binding`, whose
|
|
889
|
-
Project check is unconditional. Existing direct callers
|
|
890
|
-
that have an entirely empty Project registry retain the historical
|
|
891
|
-
construction seam; once any Project exists, this method applies the
|
|
892
|
-
same enabled-Project requirement as production.
|
|
893
|
-
"""
|
|
894
|
-
|
|
895
|
-
return self._create_binding(
|
|
896
|
-
scope=scope,
|
|
897
|
-
project_alias=project_alias,
|
|
898
|
-
creator_id=creator_id,
|
|
899
|
-
turn_settings=turn_settings,
|
|
900
|
-
task_feedback=task_feedback,
|
|
901
|
-
message_context_mode=message_context_mode,
|
|
902
|
-
context_anchor=context_anchor,
|
|
903
|
-
expected_project_revision=None,
|
|
904
|
-
activate=True,
|
|
905
|
-
allow_scope_insert=True,
|
|
906
|
-
allow_empty_project_registry=True,
|
|
907
|
-
)
|
|
908
|
-
|
|
909
875
|
def create_channel_binding(
|
|
910
876
|
self,
|
|
911
877
|
*,
|
|
@@ -933,7 +899,6 @@ class BindingStore:
|
|
|
933
899
|
expected_project_revision=expected_project_revision,
|
|
934
900
|
activate=True,
|
|
935
901
|
allow_scope_insert=True,
|
|
936
|
-
allow_empty_project_registry=False,
|
|
937
902
|
)
|
|
938
903
|
|
|
939
904
|
def create_admin_binding(
|
|
@@ -962,7 +927,6 @@ class BindingStore:
|
|
|
962
927
|
expected_project_revision=expected_project_revision,
|
|
963
928
|
activate=activate,
|
|
964
929
|
allow_scope_insert=False,
|
|
965
|
-
allow_empty_project_registry=False,
|
|
966
930
|
)
|
|
967
931
|
|
|
968
932
|
def _create_binding(
|
|
@@ -978,7 +942,6 @@ class BindingStore:
|
|
|
978
942
|
expected_project_revision: int | None,
|
|
979
943
|
activate: bool,
|
|
980
944
|
allow_scope_insert: bool,
|
|
981
|
-
allow_empty_project_registry: bool,
|
|
982
945
|
scheduled_run_id: str | None = None,
|
|
983
946
|
) -> ThreadBinding:
|
|
984
947
|
if not project_alias or not creator_id:
|
|
@@ -1057,22 +1020,15 @@ class BindingStore:
|
|
|
1057
1020
|
(project_alias,),
|
|
1058
1021
|
).fetchone()
|
|
1059
1022
|
self.require_project_not_deleting(project_alias)
|
|
1060
|
-
if project_row is None
|
|
1061
|
-
any_project = self._connection.execute(
|
|
1062
|
-
"SELECT 1 FROM projects LIMIT 1"
|
|
1063
|
-
).fetchone()
|
|
1064
|
-
if any_project is not None:
|
|
1065
|
-
raise ProjectNotFound(project_alias)
|
|
1066
|
-
elif project_row is None:
|
|
1023
|
+
if project_row is None:
|
|
1067
1024
|
raise ProjectNotFound(project_alias)
|
|
1068
|
-
if
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
)
|
|
1075
|
-
raise ProjectRevisionConflict(project_alias)
|
|
1025
|
+
if not bool(project_row["enabled"]):
|
|
1026
|
+
raise ProjectDisabled(project_alias)
|
|
1027
|
+
if (
|
|
1028
|
+
expected_project_revision is not None
|
|
1029
|
+
and project_row["revision"] != expected_project_revision
|
|
1030
|
+
):
|
|
1031
|
+
raise ProjectRevisionConflict(project_alias)
|
|
1076
1032
|
|
|
1077
1033
|
self._connection.execute(
|
|
1078
1034
|
"""
|
|
@@ -1133,7 +1089,7 @@ class BindingStore:
|
|
|
1133
1089
|
task_feedback=session_settings.task_feedback,
|
|
1134
1090
|
message_context_mode=session_settings.message_context_mode,
|
|
1135
1091
|
context_anchor=context_anchor, expected_project_revision=None, activate=True,
|
|
1136
|
-
allow_scope_insert=True,
|
|
1092
|
+
allow_scope_insert=True,
|
|
1137
1093
|
scheduled_run_id=run_id,
|
|
1138
1094
|
)
|
|
1139
1095
|
|
|
@@ -1498,62 +1454,6 @@ class BindingStore:
|
|
|
1498
1454
|
)
|
|
1499
1455
|
return self.get(binding_id)
|
|
1500
1456
|
|
|
1501
|
-
def deactivate(self, *, scope_key: str, binding_id: str) -> ThreadBinding:
|
|
1502
|
-
"""Clear the active pointer only when it still targets this Binding."""
|
|
1503
|
-
|
|
1504
|
-
with self._transaction():
|
|
1505
|
-
row = self._connection.execute(
|
|
1506
|
-
"SELECT active_binding_id FROM scopes WHERE scope_key = ?",
|
|
1507
|
-
(scope_key,),
|
|
1508
|
-
).fetchone()
|
|
1509
|
-
if row is None or row["active_binding_id"] != binding_id:
|
|
1510
|
-
raise BindingConflict("Binding is no longer active in this Scope")
|
|
1511
|
-
owner = self._connection.execute(
|
|
1512
|
-
"SELECT scope_key FROM bindings WHERE binding_id = ?",
|
|
1513
|
-
(binding_id,),
|
|
1514
|
-
).fetchone()
|
|
1515
|
-
if owner is None or owner["scope_key"] != scope_key:
|
|
1516
|
-
raise BindingNotFound(binding_id)
|
|
1517
|
-
self._connection.execute(
|
|
1518
|
-
"""
|
|
1519
|
-
UPDATE scopes
|
|
1520
|
-
SET active_binding_id = NULL, updated_at = ?
|
|
1521
|
-
WHERE scope_key = ? AND active_binding_id = ?
|
|
1522
|
-
""",
|
|
1523
|
-
(_now(), scope_key, binding_id),
|
|
1524
|
-
)
|
|
1525
|
-
return self.get(binding_id)
|
|
1526
|
-
|
|
1527
|
-
def deactivate_if_active(
|
|
1528
|
-
self,
|
|
1529
|
-
*,
|
|
1530
|
-
scope_key: str,
|
|
1531
|
-
binding_id: str,
|
|
1532
|
-
) -> ThreadBinding:
|
|
1533
|
-
"""Clear this Binding's Scope pointer when it is still current.
|
|
1534
|
-
|
|
1535
|
-
Exact management operations may archive an inactive Binding. They
|
|
1536
|
-
must still validate ownership, but must not disturb the Scope's actual
|
|
1537
|
-
current pointer merely because another Binding was the mutation target.
|
|
1538
|
-
"""
|
|
1539
|
-
|
|
1540
|
-
with self._transaction():
|
|
1541
|
-
owner = self._connection.execute(
|
|
1542
|
-
"SELECT scope_key FROM bindings WHERE binding_id = ?",
|
|
1543
|
-
(binding_id,),
|
|
1544
|
-
).fetchone()
|
|
1545
|
-
if owner is None or owner["scope_key"] != scope_key:
|
|
1546
|
-
raise BindingNotFound(binding_id)
|
|
1547
|
-
self._connection.execute(
|
|
1548
|
-
"""
|
|
1549
|
-
UPDATE scopes
|
|
1550
|
-
SET active_binding_id = NULL, updated_at = ?
|
|
1551
|
-
WHERE scope_key = ? AND active_binding_id = ?
|
|
1552
|
-
""",
|
|
1553
|
-
(_now(), scope_key, binding_id),
|
|
1554
|
-
)
|
|
1555
|
-
return self.get(binding_id)
|
|
1556
|
-
|
|
1557
1457
|
def delete_binding(self, binding_id: str) -> ThreadBinding:
|
|
1558
1458
|
"""Delete one Binding and clear its Scope pointer in one transaction."""
|
|
1559
1459
|
|
|
@@ -2283,13 +2183,6 @@ class BindingStore:
|
|
|
2283
2183
|
return root_record
|
|
2284
2184
|
return None
|
|
2285
2185
|
|
|
2286
|
-
def list_side_topics(self) -> list[SideTopicRecord]:
|
|
2287
|
-
with self._lock:
|
|
2288
|
-
rows = self._connection.execute(
|
|
2289
|
-
_SIDE_TOPIC_SELECT + " ORDER BY created_at, side_id"
|
|
2290
|
-
).fetchall()
|
|
2291
|
-
return [_side_topic(row) for row in rows]
|
|
2292
|
-
|
|
2293
2186
|
async def query_bindings(
|
|
2294
2187
|
self,
|
|
2295
2188
|
*,
|
|
@@ -2178,38 +2178,6 @@ def _decode_project_management_form(
|
|
|
2178
2178
|
)
|
|
2179
2179
|
|
|
2180
2180
|
|
|
2181
|
-
def _project_row(
|
|
2182
|
-
project: Project,
|
|
2183
|
-
controls: list[dict[str, Any]],
|
|
2184
|
-
*,
|
|
2185
|
-
status: str,
|
|
2186
|
-
) -> dict[str, Any]:
|
|
2187
|
-
return {
|
|
2188
|
-
"tag": "column_set",
|
|
2189
|
-
"flex_mode": "none",
|
|
2190
|
-
"background_style": "grey-50",
|
|
2191
|
-
"margin": "0 0 8px 0",
|
|
2192
|
-
"columns": [
|
|
2193
|
-
{
|
|
2194
|
-
"tag": "column",
|
|
2195
|
-
"width": "weighted",
|
|
2196
|
-
"weight": 5,
|
|
2197
|
-
"padding": "8px",
|
|
2198
|
-
"elements": [
|
|
2199
|
-
_plain(f"{project.alias} · {status}\n{project.cwd}"),
|
|
2200
|
-
],
|
|
2201
|
-
},
|
|
2202
|
-
{
|
|
2203
|
-
"tag": "column",
|
|
2204
|
-
"width": "auto",
|
|
2205
|
-
"vertical_align": "center",
|
|
2206
|
-
"padding": "8px",
|
|
2207
|
-
"elements": controls,
|
|
2208
|
-
},
|
|
2209
|
-
],
|
|
2210
|
-
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
2181
|
def _archived_session_row(
|
|
2214
2182
|
*,
|
|
2215
2183
|
scope: FeishuScope,
|
|
@@ -571,21 +571,6 @@ def turn_progress_card(
|
|
|
571
571
|
)
|
|
572
572
|
|
|
573
573
|
|
|
574
|
-
def _turn_file_manifest(
|
|
575
|
-
files: tuple[TurnFile, ...],
|
|
576
|
-
) -> tuple[TurnFileManifestItem, ...]:
|
|
577
|
-
return tuple(
|
|
578
|
-
TurnFileManifestItem(
|
|
579
|
-
path=str(turn_file.resolved_path),
|
|
580
|
-
label=turn_file.display_path,
|
|
581
|
-
additions=turn_file.additions,
|
|
582
|
-
deletions=turn_file.deletions,
|
|
583
|
-
deleted=turn_file.deleted,
|
|
584
|
-
)
|
|
585
|
-
for turn_file in files
|
|
586
|
-
)
|
|
587
|
-
|
|
588
|
-
|
|
589
574
|
def _turn_progress_manifest(
|
|
590
575
|
snapshot: _TurnActivitySnapshotLike,
|
|
591
576
|
) -> TurnProgressManifest:
|
|
@@ -91,7 +91,7 @@ from .channel.completion_mentions import send_completion_mention
|
|
|
91
91
|
from .channel.input_preparation import MessageInputPreparer
|
|
92
92
|
from .channel.question_inputs import CardAnswerConversation, CardAnswerOrigin, card_answer_sender
|
|
93
93
|
from .channel.ports import ReplyChannel
|
|
94
|
-
from .channel.topics import TopicPublishError, send_topic_message
|
|
94
|
+
from .channel.topics import TopicPublishError, send_topic_message
|
|
95
95
|
from .cards.scheduled import (
|
|
96
96
|
decode_schedule_action,
|
|
97
97
|
is_schedule_card_action,
|
|
@@ -652,10 +652,6 @@ class _SentMessage:
|
|
|
652
652
|
parent_id: str | None
|
|
653
653
|
|
|
654
654
|
|
|
655
|
-
# Compatibility construction seam retained for focused ordinary-Turn tests.
|
|
656
|
-
_ProgressCardController = _ReplyCardPresenter
|
|
657
|
-
|
|
658
|
-
|
|
659
655
|
class ChannelApplication:
|
|
660
656
|
def __init__(
|
|
661
657
|
self,
|
|
@@ -687,7 +683,7 @@ class ChannelApplication:
|
|
|
687
683
|
self._scope_coordinator = management.scope_coordinator
|
|
688
684
|
self._scheduled_completions: set[str] = set()
|
|
689
685
|
self._reactions = _ReactionController(channel)
|
|
690
|
-
self._progress_cards =
|
|
686
|
+
self._progress_cards = _ReplyCardPresenter(channel, runtime)
|
|
691
687
|
runtime.set_completion_handler(self.handle_completion)
|
|
692
688
|
runtime.set_question_handler(self.handle_questions)
|
|
693
689
|
|
|
@@ -6338,20 +6334,6 @@ def _side_reply_target(
|
|
|
6338
6334
|
)
|
|
6339
6335
|
|
|
6340
6336
|
|
|
6341
|
-
def _validated_sent_message(
|
|
6342
|
-
result: object,
|
|
6343
|
-
*,
|
|
6344
|
-
expected_chat_id: str,
|
|
6345
|
-
) -> _SentMessage:
|
|
6346
|
-
try:
|
|
6347
|
-
sent = validate_topic_message(result, expected_chat_id)
|
|
6348
|
-
except TopicPublishError as error:
|
|
6349
|
-
raise SideTopicCreateFailed("Side " + str(error)) from error
|
|
6350
|
-
return _SentMessage(
|
|
6351
|
-
sent.message_id, sent.chat_id, sent.thread_id, sent.root_id, sent.parent_id,
|
|
6352
|
-
)
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
6337
|
def _is_feishu_card_action_lock(result: object) -> bool:
|
|
6356
6338
|
if getattr(result, "success", None) is not False:
|
|
6357
6339
|
return False
|
|
@@ -150,66 +150,6 @@ def _canonical_name(value: str) -> str:
|
|
|
150
150
|
return re.sub(r"[-_.]+", "-", value).lower()
|
|
151
151
|
|
|
152
152
|
|
|
153
|
-
def _config_paths(backend: str, prefix: Path, env: Mapping[str, str]) -> set[Path]:
|
|
154
|
-
"""Bounded documented config locations, not a replacement config parser."""
|
|
155
|
-
home = Path.home()
|
|
156
|
-
config_home = Path(env.get("XDG_CONFIG_HOME") or home / ".config")
|
|
157
|
-
if backend == "pip":
|
|
158
|
-
paths = {home / ".pip/pip.conf", config_home / "pip/pip.conf", prefix / "pip.conf"}
|
|
159
|
-
paths.add(Path("/etc/pip.conf"))
|
|
160
|
-
for root in (env.get("XDG_CONFIG_DIRS") or "/etc/xdg").split(os.pathsep):
|
|
161
|
-
paths.add(Path(root) / "pip/pip.conf")
|
|
162
|
-
if sys.platform == "darwin":
|
|
163
|
-
# pip 26.2 also honors XDG data paths on macOS. Keep legacy and
|
|
164
|
-
# fallback locations in this conservative check across pip versions.
|
|
165
|
-
paths.update({Path("/Library/Application Support/pip/pip.conf"),
|
|
166
|
-
home / "Library/Application Support/pip/pip.conf",
|
|
167
|
-
home / ".config/pip/pip.conf"})
|
|
168
|
-
if env.get("XDG_DATA_HOME"):
|
|
169
|
-
paths.add(Path(env["XDG_DATA_HOME"]) / "pip/pip.conf")
|
|
170
|
-
for root in (env.get("XDG_DATA_DIRS") or "").split(os.pathsep):
|
|
171
|
-
if root:
|
|
172
|
-
paths.add(Path(root) / "pip/pip.conf")
|
|
173
|
-
else:
|
|
174
|
-
paths = {config_home / "uv/uv.toml", Path("/etc/uv/uv.toml")}
|
|
175
|
-
return paths
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def _check_tool_config(backend: str, prefix: Path, env: Mapping[str, str]) -> None:
|
|
179
|
-
variable = "PIP_CONFIG_FILE" if backend == "pip" else "UV_CONFIG_FILE"
|
|
180
|
-
explicit = env.get(variable)
|
|
181
|
-
# pip explicitly documents the null device as disabling all configuration.
|
|
182
|
-
if backend == "pip" and explicit == os.devnull:
|
|
183
|
-
return
|
|
184
|
-
if backend != "pip" and env.get("UV_NO_CONFIG", "").lower() in ("1", "true", "yes"):
|
|
185
|
-
return
|
|
186
|
-
if explicit:
|
|
187
|
-
raise PackageUpdateError(
|
|
188
|
-
f"{variable} selects custom package configuration; automatic update cannot verify "
|
|
189
|
-
"its target settings. Use the owning package manager manually."
|
|
190
|
-
)
|
|
191
|
-
existing = sorted(str(p) for p in _config_paths(backend, prefix, env) if p.exists())
|
|
192
|
-
if backend != "pip":
|
|
193
|
-
for directory in (Path.cwd(), *Path.cwd().parents):
|
|
194
|
-
config = directory / "uv.toml"
|
|
195
|
-
if config.exists():
|
|
196
|
-
existing.append(str(config))
|
|
197
|
-
project = directory / "pyproject.toml"
|
|
198
|
-
if project.is_file():
|
|
199
|
-
try:
|
|
200
|
-
settings = tomllib.loads(project.read_text())
|
|
201
|
-
except (OSError, ValueError) as exc:
|
|
202
|
-
raise PackageUpdateError(f"Cannot verify project configuration {project}; update manually.") from exc
|
|
203
|
-
if "uv" in settings.get("tool", {}):
|
|
204
|
-
existing.append(str(project))
|
|
205
|
-
if existing:
|
|
206
|
-
raise PackageUpdateError(
|
|
207
|
-
"Custom package configuration requires manual update in this release: "
|
|
208
|
-
+ ", ".join(existing)
|
|
209
|
-
+ ". Automatic update does not silently ignore indexes, constraints or target settings."
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
|
|
213
153
|
def _check_environment_options(backend: str, env: Mapping[str, str]) -> None:
|
|
214
154
|
if backend == "pip":
|
|
215
155
|
denied_values = (
|
|
@@ -294,7 +234,7 @@ def _uv_executable(env: Mapping[str, str]) -> str:
|
|
|
294
234
|
|
|
295
235
|
|
|
296
236
|
def _uv_directory(uv: str, kind: str, env: Mapping[str, str], cwd: Path) -> Path:
|
|
297
|
-
result = _run([uv,
|
|
237
|
+
result = _run([uv, kind, "dir"], env=env, cwd=cwd)
|
|
298
238
|
_require_success(result, f"uv {kind}-directory query")
|
|
299
239
|
raw = result.stdout.strip()
|
|
300
240
|
if not raw or "\n" in raw or not Path(raw).is_absolute():
|
|
@@ -317,7 +257,7 @@ def _reject_uv_cache_environment(prefix: Path, cache_root: Path | None = None) -
|
|
|
317
257
|
|
|
318
258
|
|
|
319
259
|
def _uv_context(uv: str, env: Mapping[str, str], cwd: Path) -> tuple[str, Path, Path]:
|
|
320
|
-
result = _run([uv, "
|
|
260
|
+
result = _run([uv, "self", "version", "--output-format", "json"], env=env, cwd=cwd)
|
|
321
261
|
_require_success(result, "uv version query")
|
|
322
262
|
version = _object(result.stdout, "uv version query").get("version")
|
|
323
263
|
if not isinstance(version, str) or not version.strip():
|
|
@@ -487,19 +427,18 @@ def prepare_update(
|
|
|
487
427
|
if selected is None:
|
|
488
428
|
raise PackageUpdateError("Installer identity is absent. Explicitly choose --via pip or --via uv-pip for a self-managed environment, or update manually.")
|
|
489
429
|
_check_environment_options(selected, env)
|
|
490
|
-
_check_tool_config(selected, prefix, env)
|
|
491
430
|
if selected == "pip":
|
|
492
431
|
if not info.get("pip_available"):
|
|
493
432
|
raise PackageUpdateError("This environment has no pip. Netizen will not install one or choose an unrelated pip; update manually.")
|
|
494
|
-
overrides.update({"PIP_DISABLE_PIP_VERSION_CHECK": "1"
|
|
433
|
+
overrides.update({"PIP_DISABLE_PIP_VERSION_CHECK": "1"})
|
|
495
434
|
command = [executable, "-I", "-m", "pip", "install", "--upgrade", "--no-input", DISTRIBUTION]
|
|
496
435
|
elif selected == "uv-pip":
|
|
497
436
|
assert uv is not None
|
|
498
|
-
command = [uv, "--no-
|
|
437
|
+
command = [uv, "--no-python-downloads", "pip", "install", "--python", executable,
|
|
499
438
|
"--upgrade", DISTRIBUTION]
|
|
500
439
|
else:
|
|
501
440
|
assert uv is not None
|
|
502
|
-
command = [uv, "--no-
|
|
441
|
+
command = [uv, "--no-python-downloads", "tool", "upgrade", DISTRIBUTION]
|
|
503
442
|
env.update(overrides)
|
|
504
443
|
if uv is not None:
|
|
505
444
|
paths[uv] = _identity(Path(uv))
|
|
@@ -598,10 +598,6 @@ class ServiceManager:
|
|
|
598
598
|
raise ServiceError(f"service did not fully exit within {timeout:g}s; no further action performed")
|
|
599
599
|
time.sleep(min(0.25, max(0.0, deadline - time.monotonic())))
|
|
600
600
|
|
|
601
|
-
def restart(self, root: Path) -> ServiceStatus:
|
|
602
|
-
self.stop(root)
|
|
603
|
-
return self.start(root)
|
|
604
|
-
|
|
605
601
|
def remove(self, root: Path) -> None:
|
|
606
602
|
root = self._root(root)
|
|
607
603
|
if self.inspect(root) is None:
|