netizen-cli 0.10.1__tar.gz → 0.10.2__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.1/netizen_cli.egg-info → netizen_cli-0.10.2}/PKG-INFO +2 -2
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/__init__.py +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/presentation.py +0 -14
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/static/admin.css +7 -5
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/static/admin.js +190 -73
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/static/index.html +28 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/web.py +37 -13
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/bindings.py +0 -43
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cards/scheduled.py +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel_app.py +19 -22
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/codex_runtime.py +111 -119
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/main.py +4 -7
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/management/service.py +34 -15
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/management/updates.py +2 -3
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/runtime/thread_naming.py +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/schedules/store.py +5 -8
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/settings.py +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/terminal_cleanup.py +2 -2
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/turn_plan_observer.py +2 -2
- {netizen_cli-0.10.1 → netizen_cli-0.10.2/netizen_cli.egg-info}/PKG-INFO +2 -2
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli.egg-info/requires.txt +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/pyproject.toml +2 -2
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/skills/netizen-user-guide/references/user-guide.md +8 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_bindings.py +21 -6
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_channel_app.py +114 -25
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_codex_runtime.py +443 -25
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_codex_sdk_capabilities.py +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_deployment.py +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_error_messages.py +4 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_probe_python_sdk.py +197 -53
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_probe_scheduled_tasks.py +45 -14
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_schedule_service.py +20 -2
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_scheduled_runtime.py +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_schedules_storage.py +44 -5
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_terminal_cleanup.py +2 -2
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_thread_naming.py +68 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_turn_plan_observer.py +1 -1
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/MANIFEST.in +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/README.md +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/_build.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/config.example.yaml +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/__main__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/auth.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/errors.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/port_config.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/queries.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/admin/transport.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/builtin_skills.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cards/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cards/callbacks.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cards/controls.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cards/defaults.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cards/model_info.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cards/questions.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cards/reply.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/completion_mentions.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/input_preparation.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/messages.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/ports.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/question_inputs.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/reactions.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/reply_presenter.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/channel/topics.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cli.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cli_data.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cli_packages.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cli_services.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cli_setup.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cli_update.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cli_update_restore.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/cli_update_worker.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/completion_mention.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/database_migrations.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/defaults/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/defaults/models.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/defaults/service.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/defaults/store.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/deployment/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/deployment/restart_worker.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/deployment/update_executor.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/deployment/update_protocol.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/domain.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/error_messages.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/experience.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/feishu_app_onboarding.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/feishu_app_permissions.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/git_status.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/image_inputs.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/instance.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/lark_app.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/management/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/management/blocking_io.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/management/chat_labels.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/management/coordination.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/markdown_images.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/message_content.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/message_history.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/message_preparation.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/message_projection.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/migrations/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/migrations/schema.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/migrations/v14.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/model_settings.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/package_resources.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/projects.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/prompt_projection.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/quoted_context.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/result_images.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/runtime/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/runtime/contracts.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/runtime/name_writes.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/schedules/__init__.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/schedules/mcp.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/schedules/models.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/schedules/scheduler.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/schedules/service.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/sdk_gap_adapter.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/service_launcher.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/session_settings.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/skill_references.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/turn_activity.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/turn_files.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/turn_patch_children.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli/user_questions.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli.egg-info/SOURCES.txt +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli.egg-info/dependency_links.txt +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli.egg-info/entry_points.txt +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/netizen_cli.egg-info/top_level.txt +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/setup.cfg +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/skills/netizen-lark/SKILL.md +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/skills/netizen-user-guide/SKILL.md +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_admin_update_executor_probe.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_binding_scheduled_channel.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cards.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_channel_fixtures.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_channel_input_preparation.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_check_sdk.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli_data.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli_distribution.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli_integration.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli_packages.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli_packaging.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli_services.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli_setup.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_cli_update.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_completion_mention_cards.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_completion_mentions.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_completion_topic_delivery.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_database_migrations.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_defaults_cards.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_defaults_channel.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_defaults_runtime.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_documentation_links.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_domain_experience.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_feishu_app_onboarding.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_feishu_app_permissions.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_file_pagination.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_git_status.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_image_inputs.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_lark_app.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_lark_sdk_contract.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_main.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_markdown_images.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_message_content.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_message_history.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_message_material_inputs.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_message_preparation.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_migration_schema_validation.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_model_info_cards.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_model_settings.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_name_writes.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_probe_child_files.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_probe_feishu_completion_mention.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_probe_feishu_turn_file_card.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_probe_sdk_completion_race.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_probe_thread_naming.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_project_delete_channel.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_project_delete_runtime.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_project_deletion_storage.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_prompt_projection.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_question_cards.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_question_channel.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_question_target_parity.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_quoted_context.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_release_script.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_repository_hygiene.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_result_images.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_retired_deployment.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_runtime_thread_naming.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_schedule_card_retry.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_schedule_cards.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_schedule_mcp.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_schedule_run_now.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_schedule_scope_matrix.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_scheduled_channel.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_scheduler.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_scheduler_presenter.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_sdk_gap_adapter.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_service_launcher.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_session_defaults.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_session_settings.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_settings_projects.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_turn_activity.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_turn_files.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_turn_patch_children.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_turn_patch_statistics.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_update_executor.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_user_guide_skill.py +0 -0
- {netizen_cli-0.10.1 → netizen_cli-0.10.2}/tests/test_user_questions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netizen-cli
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.2
|
|
4
4
|
Summary: Use native Codex threads through a Feishu Channel.
|
|
5
5
|
Requires-Python: <3.15,>=3.11
|
|
6
6
|
Requires-Dist: h11==0.16.0
|
|
@@ -10,7 +10,7 @@ Requires-Dist: markdown-it-py==4.0.0
|
|
|
10
10
|
Requires-Dist: mdformat==1.0.0
|
|
11
11
|
Requires-Dist: mdformat-gfm==1.0.0
|
|
12
12
|
Requires-Dist: mcp==2.2.0
|
|
13
|
-
Requires-Dist: openai-codex==0.
|
|
13
|
+
Requires-Dist: openai-codex==0.159.2
|
|
14
14
|
Requires-Dist: pydantic==2.13.4
|
|
15
15
|
Requires-Dist: PyYAML==6.0.3
|
|
16
16
|
Requires-Dist: qrcode==8.2
|
|
@@ -9,7 +9,6 @@ from typing import Any
|
|
|
9
9
|
from urllib.parse import urlencode
|
|
10
10
|
from zoneinfo import ZoneInfo
|
|
11
11
|
|
|
12
|
-
from ..bindings import BindingTurnSettings
|
|
13
12
|
from ..runtime.contracts import (
|
|
14
13
|
ReleaseDisposition,
|
|
15
14
|
StopDisposition,
|
|
@@ -76,16 +75,6 @@ def _jsonable(value: object) -> object:
|
|
|
76
75
|
raise TypeError(f"unsupported JSON projection: {type(value).__name__}")
|
|
77
76
|
|
|
78
77
|
|
|
79
|
-
def _settings_json(settings: BindingTurnSettings | None) -> dict[str, str] | None:
|
|
80
|
-
if settings is None:
|
|
81
|
-
return None
|
|
82
|
-
return {
|
|
83
|
-
"modelId": settings.model_id,
|
|
84
|
-
"effortId": settings.effort_id,
|
|
85
|
-
"serviceTierId": settings.service_tier_id,
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
78
|
def _runtime_binding_json(status: BindingStatusProjection) -> dict[str, object]:
|
|
90
79
|
snapshot = status.snapshot
|
|
91
80
|
turn = snapshot.turn
|
|
@@ -217,7 +206,4 @@ def _binding_result(request_id: str, binding: Any) -> dict[str, object]:
|
|
|
217
206
|
"scopeKey": binding.scope_key,
|
|
218
207
|
"current": binding.active,
|
|
219
208
|
"nativeThreadId": binding.native_thread_id,
|
|
220
|
-
"settingsRevision": binding.settings_revision,
|
|
221
|
-
"messageContextMode": binding.message_context_mode.value,
|
|
222
|
-
"contextRevision": binding.context_revision,
|
|
223
209
|
}
|
|
@@ -129,14 +129,10 @@ button:disabled { cursor: default; opacity: .48; }
|
|
|
129
129
|
.update-operation { padding: 16px; margin: 20px 0; border-radius: 9px; background: var(--wash); }
|
|
130
130
|
.update-operation p { margin: 8px 0 0; line-height: 1.6; }
|
|
131
131
|
.update-warning { color: var(--muted); font-size: 13px; line-height: 1.6; }
|
|
132
|
-
.update-actions { gap: 10px; }
|
|
133
|
-
.update-actions .action { padding: 8px 13px; font-size: inherit; }
|
|
134
132
|
.update-help { margin-top: 16px; font-size: 13px; }
|
|
135
133
|
.service-restart { margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--line); }
|
|
136
134
|
.service-restart p { line-height: 1.6; }
|
|
137
135
|
#updates a { color: var(--blue); }
|
|
138
|
-
.update-notes { margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--line); }
|
|
139
|
-
#update-notes { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.6; }
|
|
140
136
|
button:focus-visible, input:focus-visible, select:focus-visible { outline: 3px solid #8cb4ff; outline-offset: 2px; }
|
|
141
137
|
.login-body { min-height: 100vh; display: grid; place-items: center; padding: 20px; }
|
|
142
138
|
.login-card { width: min(420px, 100%); background: white; border: 1px solid var(--line); border-radius: 14px; padding: 28px; }
|
|
@@ -158,7 +154,7 @@ button:focus-visible, input:focus-visible, select:focus-visible { outline: 3px s
|
|
|
158
154
|
.time-range-fields { grid-template-columns: 1fr; }
|
|
159
155
|
}
|
|
160
156
|
.schedule-help { margin-top: 18px; color: var(--muted); }
|
|
161
|
-
body.schedule-drawer-open, body.defaults-drawer-open { overflow: hidden; }
|
|
157
|
+
body.schedule-drawer-open, body.defaults-drawer-open, body.session-settings-drawer-open { overflow: hidden; }
|
|
162
158
|
.editor-drawer { position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0; width: min(720px, 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh; border: 0; color: var(--ink); background: var(--paper); box-shadow: -12px 0 40px rgb(23 39 68 / 16%); }
|
|
163
159
|
.editor-drawer::backdrop { background: rgb(16 33 63 / 35%); }
|
|
164
160
|
#schedule-editor { height: 100%; display: flex; flex-direction: column; border: 0; border-radius: 0; padding: 0; margin: 0; }
|
|
@@ -236,6 +232,12 @@ body.schedule-drawer-open, body.defaults-drawer-open { overflow: hidden; }
|
|
|
236
232
|
.defaults-feedback label { display: flex; align-items: center; gap: 7px; }
|
|
237
233
|
.drawer-form .defaults-feedback input { width: 16px; height: 16px; margin: 0; accent-color: var(--blue); }
|
|
238
234
|
#defaults-note.error { color: var(--danger); }
|
|
235
|
+
#session-settings-editor[hidden], #session-settings-editor [hidden] { display: none; }
|
|
236
|
+
#session-settings-fields { border: 0; padding: 0; margin: 0; min-width: 0; }
|
|
237
|
+
#session-settings-fields:disabled { opacity: .6; }
|
|
238
|
+
.session-settings-help { color: var(--muted); font-size: 13px; line-height: 1.6; }
|
|
239
|
+
#session-settings-note { margin-top: 24px; }
|
|
240
|
+
#session-settings-note.error { color: var(--danger); }
|
|
239
241
|
#defaults table { table-layout: fixed; }
|
|
240
242
|
#defaults th:first-child { width: 25%; }
|
|
241
243
|
#defaults th:nth-child(2) { width: 16%; }
|
|
@@ -112,8 +112,8 @@ function renderUpdates() {
|
|
|
112
112
|
document.querySelector("#update-source").textContent = {
|
|
113
113
|
python: "Python 环境安装",
|
|
114
114
|
}[data.current.source] || "安装来源未知";
|
|
115
|
-
document.querySelector("#update-message").textContent = data.
|
|
116
|
-
||
|
|
115
|
+
document.querySelector("#update-message").textContent = data.message
|
|
116
|
+
|| "程序更新请在对应 Python 环境运行 netizen update。";
|
|
117
117
|
const operation = data.operation;
|
|
118
118
|
const restarting = updateExpectedTarget != null || operation?.kind === "restart";
|
|
119
119
|
const operationName = restarting ? "重启" : "升级";
|
|
@@ -1371,9 +1371,10 @@ async function loadSessions(cursor = state.sessionPage.cursor) {
|
|
|
1371
1371
|
updateRuntimeCell(runtime, session.runtime);
|
|
1372
1372
|
row.append(runtime);
|
|
1373
1373
|
cell(row, session.projectAlias);
|
|
1374
|
-
const settings = session.
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1374
|
+
const settings = session.sessionSettings;
|
|
1375
|
+
const turnSettings = settings.turn_settings;
|
|
1376
|
+
const model = turnSettings ? `${turnSettings.model_id} / ${turnSettings.effort_id} / ${turnSettings.service_tier_id}` : "继承 Codex";
|
|
1377
|
+
cell(row, `${settings.message_context_mode} · ${model}`);
|
|
1377
1378
|
cell(row, sessionUpdatedAtLabel(session));
|
|
1378
1379
|
const actions = actionsCell(row);
|
|
1379
1380
|
wireSessionActions(actions, session);
|
|
@@ -1399,18 +1400,10 @@ function wireSessionActions(actions, session) {
|
|
|
1399
1400
|
});
|
|
1400
1401
|
}));
|
|
1401
1402
|
if (a.activate) actions.append(actionButton("设为当前", () => mutate("/api/v1/sessions/activate", a.activate)));
|
|
1402
|
-
if (a.configure)
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
const modelId = window.prompt("Model ID", session.turnSettings?.modelId || "");
|
|
1407
|
-
const effortId = window.prompt("Effort ID", session.turnSettings?.effortId || "");
|
|
1408
|
-
const serviceTierId = window.prompt("Speed / Service Tier ID", session.turnSettings?.serviceTierId || "");
|
|
1409
|
-
if (!modelId || !effortId || !serviceTierId) return;
|
|
1410
|
-
turnSettings = { modelId, effortId, serviceTierId };
|
|
1411
|
-
}
|
|
1412
|
-
await mutate("/api/v1/sessions/configure", a.configure, { turnSettings });
|
|
1413
|
-
}));
|
|
1403
|
+
if (a.configure) {
|
|
1404
|
+
const button = actionButton("配置", () => openSessionSettingsEditor(session, button));
|
|
1405
|
+
actions.append(button);
|
|
1406
|
+
}
|
|
1414
1407
|
if (a.rename) actions.append(actionButton("重命名", async () => {
|
|
1415
1408
|
const name = window.prompt("新的原生会话名称");
|
|
1416
1409
|
if (name) await mutate("/api/v1/sessions/rename", a.rename, { name });
|
|
@@ -1492,6 +1485,137 @@ function rowByIdentity(selector, key, value) {
|
|
|
1492
1485
|
return null;
|
|
1493
1486
|
}
|
|
1494
1487
|
|
|
1488
|
+
let sessionSettingsEditor = null;
|
|
1489
|
+
const sessionSettingsInput = (name) => document.querySelector(`#session-settings-${name}`);
|
|
1490
|
+
|
|
1491
|
+
function renderSessionSettingsEditor() {
|
|
1492
|
+
const editor = sessionSettingsEditor;
|
|
1493
|
+
if (!editor) return;
|
|
1494
|
+
renderSessionSettingsFields(sessionSettingsInput, editor.settings, editor.models, editor.contextAvailable);
|
|
1495
|
+
sessionSettingsInput("fields").disabled = editor.loading || editor.saving;
|
|
1496
|
+
sessionSettingsInput("context-field").hidden = editor.privateChat;
|
|
1497
|
+
sessionSettingsInput("context").disabled = editor.privateChat;
|
|
1498
|
+
sessionSettingsInput("context-note").hidden = editor.privateChat;
|
|
1499
|
+
sessionSettingsInput("context-note").textContent = editor.contextAvailable
|
|
1500
|
+
? "补齐未读上下文会在下次 @ 时带入同一位置未 @ 机器人的成员消息。关闭后,请到飞书 /config 重新开启。"
|
|
1501
|
+
: "开启补齐未读上下文,请到飞书 /config 设置。";
|
|
1502
|
+
const notes = [editor.loading ? "正在读取模型目录…" : "", editor.catalogError, editor.error];
|
|
1503
|
+
if (editor.settings.turn_settings && !editor.models.some((model) => model.id === editor.settings.turn_settings.model_id)) {
|
|
1504
|
+
notes.push("已保存的模型当前不可用,原选择保持不变;可调整其他配置,或显式选择继承 Codex。");
|
|
1505
|
+
}
|
|
1506
|
+
sessionSettingsInput("note").textContent = notes.filter(Boolean).join(" ");
|
|
1507
|
+
sessionSettingsInput("note").classList.toggle("error", Boolean(editor.error));
|
|
1508
|
+
sessionSettingsInput("save").disabled = editor.loading || editor.saving || !editor.action;
|
|
1509
|
+
sessionSettingsInput("save").textContent = editor.saving ? "保存中…" : "保存";
|
|
1510
|
+
sessionSettingsInput("cancel").disabled = editor.saving;
|
|
1511
|
+
sessionSettingsInput("close").disabled = editor.saving;
|
|
1512
|
+
sessionSettingsInput("editor").setAttribute("aria-busy", String(editor.loading || editor.saving));
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
async function loadSessionSettingsOptions() {
|
|
1516
|
+
const editor = sessionSettingsEditor;
|
|
1517
|
+
if (!editor) return;
|
|
1518
|
+
const serial = ++editor.optionsSerial;
|
|
1519
|
+
editor.loading = true;
|
|
1520
|
+
renderSessionSettingsEditor();
|
|
1521
|
+
try {
|
|
1522
|
+
const data = await api("/api/v1/sessions/options");
|
|
1523
|
+
if (sessionSettingsEditor !== editor || serial !== editor.optionsSerial) return;
|
|
1524
|
+
editor.models = data.models;
|
|
1525
|
+
editor.catalogError = data.model_catalog_error || "";
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
if (sessionSettingsEditor !== editor || serial !== editor.optionsSerial) return;
|
|
1528
|
+
editor.models = [];
|
|
1529
|
+
editor.catalogError = `${error.message} 模型目录暂未读取,原选择保持不变。`;
|
|
1530
|
+
} finally {
|
|
1531
|
+
if (sessionSettingsEditor === editor && serial === editor.optionsSerial) {
|
|
1532
|
+
editor.loading = false;
|
|
1533
|
+
renderSessionSettingsEditor();
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function openSessionSettingsEditor(session, returnFocus) {
|
|
1539
|
+
if (sessionSettingsEditor?.saving || !session.actions.configure) return;
|
|
1540
|
+
const base = structuredClone(session.sessionSettings);
|
|
1541
|
+
const privateChat = session.scopeKind === "direct" || session.chatMode === "p2p";
|
|
1542
|
+
sessionSettingsEditor = { session, returnFocus, settings: structuredClone(base), base,
|
|
1543
|
+
action: session.actions.configure, models: [], catalogError: "", error: "",
|
|
1544
|
+
privateChat, contextAvailable: !privateChat && base.message_context_mode === "catch-up",
|
|
1545
|
+
loading: true, saving: false, optionsSerial: 0 };
|
|
1546
|
+
sessionSettingsInput("editor").hidden = false;
|
|
1547
|
+
sessionSettingsInput("identity").textContent = `${session.nativeTitle || "未命名 Session"} · ${session.shortId} · ${session.projectAlias}`;
|
|
1548
|
+
renderSessionSettingsEditor();
|
|
1549
|
+
if (!sessionSettingsInput("drawer").open) sessionSettingsInput("drawer").showModal();
|
|
1550
|
+
document.body.classList.toggle("session-settings-drawer-open", true);
|
|
1551
|
+
sessionSettingsInput("close").focus();
|
|
1552
|
+
const editor = sessionSettingsEditor;
|
|
1553
|
+
loadSessionSettingsOptions().then(() => {
|
|
1554
|
+
if (sessionSettingsEditor === editor && document.activeElement === sessionSettingsInput("close")) {
|
|
1555
|
+
sessionSettingsInput("model").focus();
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function closeSessionSettingsEditor() {
|
|
1561
|
+
const editor = sessionSettingsEditor;
|
|
1562
|
+
if (!editor || editor.saving) return;
|
|
1563
|
+
sessionSettingsEditor = null;
|
|
1564
|
+
sessionSettingsInput("editor").hidden = true;
|
|
1565
|
+
sessionSettingsInput("drawer").close();
|
|
1566
|
+
document.body.classList.toggle("session-settings-drawer-open", false);
|
|
1567
|
+
const returnFocus = editor.returnFocus?.disabled
|
|
1568
|
+
? document.querySelector("#sessions [data-refresh]") : editor.returnFocus;
|
|
1569
|
+
returnFocus?.focus();
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
function changeSessionSettings(field) {
|
|
1573
|
+
const editor = sessionSettingsEditor;
|
|
1574
|
+
if (!editor || editor.loading || editor.saving) return;
|
|
1575
|
+
if (field === "context" && (editor.privateChat
|
|
1576
|
+
|| (sessionSettingsInput("context").value === "catch-up" && !editor.contextAvailable))) {
|
|
1577
|
+
renderSessionSettingsEditor();
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
changeSessionSettingsField(sessionSettingsInput, editor.settings, editor.models, field, editor.base.turn_settings);
|
|
1581
|
+
renderSessionSettingsEditor();
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
async function saveSessionSettings(event) {
|
|
1585
|
+
event.preventDefault();
|
|
1586
|
+
const editor = sessionSettingsEditor;
|
|
1587
|
+
if (!editor || editor.loading || editor.saving || !editor.action) return;
|
|
1588
|
+
const action = editor.action;
|
|
1589
|
+
editor.action = null;
|
|
1590
|
+
editor.session.actions.configure = null;
|
|
1591
|
+
if (editor.returnFocus) editor.returnFocus.disabled = true;
|
|
1592
|
+
editor.saving = true;
|
|
1593
|
+
renderSessionSettingsEditor();
|
|
1594
|
+
setStatus("正在保存会话配置…");
|
|
1595
|
+
let result;
|
|
1596
|
+
try {
|
|
1597
|
+
result = await api("/api/v1/sessions/configure", { method: "POST", headers: { "Content-Type": "application/json" },
|
|
1598
|
+
body: JSON.stringify(actionPayload(action, { sessionSettings: structuredClone(editor.settings) })) });
|
|
1599
|
+
} catch (error) {
|
|
1600
|
+
editor.error = `${error.message} 请关闭表单,刷新 Sessions 后重新打开配置。`;
|
|
1601
|
+
setStatus(editor.error, true);
|
|
1602
|
+
editor.saving = false;
|
|
1603
|
+
renderSessionSettingsEditor();
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
editor.saving = false;
|
|
1607
|
+
closeSessionSettingsEditor();
|
|
1608
|
+
if (await refresh("sessions")) {
|
|
1609
|
+
setStatus(result?.message || "会话配置已保存。");
|
|
1610
|
+
if (!sessionSettingsEditor && state.tab === "sessions") {
|
|
1611
|
+
const row = rowByIdentity("#sessions-body", "bindingId", editor.session.bindingId);
|
|
1612
|
+
Array.from(row?.querySelectorAll("button") || []).find((button) => button.textContent === "配置")?.focus();
|
|
1613
|
+
}
|
|
1614
|
+
} else {
|
|
1615
|
+
setStatus("会话配置已保存,但 Sessions 列表刷新失败,请手动刷新。", true);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1495
1619
|
let defaultsEditor = null;
|
|
1496
1620
|
let defaultsProjects = [];
|
|
1497
1621
|
let defaultsOffset = 0;
|
|
@@ -1533,20 +1657,7 @@ function renderDefaultSettings() {
|
|
|
1533
1657
|
if (!editor) return;
|
|
1534
1658
|
const settings = editor.settings;
|
|
1535
1659
|
const turn = settings.turn_settings;
|
|
1536
|
-
const model = editor.models
|
|
1537
|
-
scheduleSelectOptions(defaultsInput("model"), [["", "继承 Codex"],
|
|
1538
|
-
...editor.models.map((item) => [item.id, item.display_name])], turn?.model_id);
|
|
1539
|
-
scheduleSelectOptions(defaultsInput("effort"), (model?.efforts || []).map((item) => [item.id, item.id]), turn?.effort_id);
|
|
1540
|
-
scheduleSelectOptions(defaultsInput("tier"), (model?.service_tiers || []).map((item) => [item.id, item.name || item.id]), turn?.service_tier_id);
|
|
1541
|
-
defaultsInput("effort").disabled = !model;
|
|
1542
|
-
defaultsInput("tier").disabled = !model;
|
|
1543
|
-
defaultsInput("context").value = settings.message_context_mode;
|
|
1544
|
-
for (const option of defaultsInput("context").querySelectorAll("option")) {
|
|
1545
|
-
option.disabled = option.value === "catch-up" && editor.contextAvailable === false;
|
|
1546
|
-
}
|
|
1547
|
-
defaultsInput("reactions").checked = settings.reaction_pulse_enabled;
|
|
1548
|
-
defaultsInput("progress").checked = settings.progress_card_enabled;
|
|
1549
|
-
defaultsInput("completion-mention").checked = settings.completion_mention_enabled;
|
|
1660
|
+
const model = renderSessionSettingsFields(defaultsInput, settings, editor.models, editor.contextAvailable);
|
|
1550
1661
|
const notes = [editor.loading ? "正在读取配置…" : "", editor.sourceNote, editor.error, editor.catalogError];
|
|
1551
1662
|
if (turn && !model) notes.push("已保存的模型当前不可用,选择保持不变;可显式选择其他模型。");
|
|
1552
1663
|
if (editor.contextAvailable === false) notes.push("单聊不支持补齐未读上下文,请使用当前消息。");
|
|
@@ -1659,18 +1770,7 @@ function changeDefaultSettings(field) {
|
|
|
1659
1770
|
const editor = defaultsEditor;
|
|
1660
1771
|
if (!editor) return;
|
|
1661
1772
|
editor.touched = true;
|
|
1662
|
-
|
|
1663
|
-
if (field === "model") {
|
|
1664
|
-
const selected = editor.models.find((item) => item.id === defaultsInput("model").value);
|
|
1665
|
-
if (!defaultsInput("model").value) settings.turn_settings = null;
|
|
1666
|
-
else if (selected) settings.turn_settings = { model_id: selected.id,
|
|
1667
|
-
effort_id: selected.default_effort_id, service_tier_id: selected.default_service_tier_id };
|
|
1668
|
-
} else if (field === "effort" && settings.turn_settings) settings.turn_settings.effort_id = defaultsInput("effort").value;
|
|
1669
|
-
else if (field === "tier" && settings.turn_settings) settings.turn_settings.service_tier_id = defaultsInput("tier").value;
|
|
1670
|
-
else if (field === "context") settings.message_context_mode = defaultsInput("context").value;
|
|
1671
|
-
else if (field === "reactions") settings.reaction_pulse_enabled = defaultsInput("reactions").checked;
|
|
1672
|
-
else if (field === "progress") settings.progress_card_enabled = defaultsInput("progress").checked;
|
|
1673
|
-
else if (field === "completion-mention") settings.completion_mention_enabled = defaultsInput("completion-mention").checked;
|
|
1773
|
+
changeSessionSettingsField(defaultsInput, editor.settings, editor.models, field);
|
|
1674
1774
|
renderDefaultSettings();
|
|
1675
1775
|
}
|
|
1676
1776
|
|
|
@@ -2038,25 +2138,47 @@ function scheduleSelectOptions(node, choices, selected) {
|
|
|
2038
2138
|
node.value = selected || "";
|
|
2039
2139
|
}
|
|
2040
2140
|
|
|
2141
|
+
function renderSessionSettingsFields(input, settings, models, contextAvailable) {
|
|
2142
|
+
const turn = settings.turn_settings;
|
|
2143
|
+
const model = models.find((item) => item.id === turn?.model_id);
|
|
2144
|
+
scheduleSelectOptions(input("model"), [["", "继承 Codex"],
|
|
2145
|
+
...models.map((item) => [item.id, item.display_name])], turn?.model_id);
|
|
2146
|
+
scheduleSelectOptions(input("effort"), (model?.efforts || []).map((item) => [item.id, item.id]), turn?.effort_id);
|
|
2147
|
+
scheduleSelectOptions(input("tier"), (model?.service_tiers || []).map((item) => [item.id, item.name || item.id]), turn?.service_tier_id);
|
|
2148
|
+
input("effort").disabled = !model;
|
|
2149
|
+
input("tier").disabled = !model;
|
|
2150
|
+
input("context").value = settings.message_context_mode;
|
|
2151
|
+
for (const option of input("context").querySelectorAll("option")) {
|
|
2152
|
+
option.disabled = option.value === "catch-up" && contextAvailable === false;
|
|
2153
|
+
}
|
|
2154
|
+
input("reactions").checked = settings.reaction_pulse_enabled;
|
|
2155
|
+
input("progress").checked = settings.progress_card_enabled;
|
|
2156
|
+
input("completion-mention").checked = settings.completion_mention_enabled;
|
|
2157
|
+
return model;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
function changeSessionSettingsField(input, settings, models, field, baseTurn = null) {
|
|
2161
|
+
if (field === "model") {
|
|
2162
|
+
const id = input("model").value;
|
|
2163
|
+
const selected = models.find((item) => item.id === id);
|
|
2164
|
+
if (!id) settings.turn_settings = null;
|
|
2165
|
+
else if (selected) settings.turn_settings = { model_id: selected.id,
|
|
2166
|
+
effort_id: selected.default_effort_id, service_tier_id: selected.default_service_tier_id };
|
|
2167
|
+
else if (id === baseTurn?.model_id) settings.turn_settings = structuredClone(baseTurn);
|
|
2168
|
+
} else if (field === "effort" && settings.turn_settings) settings.turn_settings.effort_id = input("effort").value;
|
|
2169
|
+
else if (field === "tier" && settings.turn_settings) settings.turn_settings.service_tier_id = input("tier").value;
|
|
2170
|
+
else if (field === "context") settings.message_context_mode = input("context").value;
|
|
2171
|
+
else if (field === "reactions") settings.reaction_pulse_enabled = input("reactions").checked;
|
|
2172
|
+
else if (field === "progress") settings.progress_card_enabled = input("progress").checked;
|
|
2173
|
+
else if (field === "completion-mention") settings.completion_mention_enabled = input("completion-mention").checked;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2041
2176
|
function renderScheduleSessionSettings() {
|
|
2042
2177
|
const editor = scheduleEditor;
|
|
2043
2178
|
if (!editor) return;
|
|
2044
2179
|
const settings = editor.sessionDraft;
|
|
2045
2180
|
const current = settings.turn_settings;
|
|
2046
|
-
const model = editor.models
|
|
2047
|
-
scheduleSelectOptions(scheduleInput("model"), [["", "继承 Codex"],
|
|
2048
|
-
...editor.models.map((item) => [item.id, item.display_name])], current?.model_id);
|
|
2049
|
-
scheduleSelectOptions(scheduleInput("effort"), (model?.efforts || []).map((item) => [item.id, item.id]), current?.effort_id);
|
|
2050
|
-
scheduleSelectOptions(scheduleInput("tier"), (model?.service_tiers || []).map((item) => [item.id, item.name || item.id]), current?.service_tier_id);
|
|
2051
|
-
scheduleInput("effort").disabled = !model;
|
|
2052
|
-
scheduleInput("tier").disabled = !model;
|
|
2053
|
-
scheduleInput("context").value = settings.message_context_mode;
|
|
2054
|
-
for (const option of scheduleInput("context").querySelectorAll("option")) {
|
|
2055
|
-
option.disabled = option.value === "catch-up" && editor.contextAvailable === false;
|
|
2056
|
-
}
|
|
2057
|
-
scheduleInput("reactions").checked = settings.reaction_pulse_enabled;
|
|
2058
|
-
scheduleInput("progress").checked = settings.progress_card_enabled;
|
|
2059
|
-
scheduleInput("completion-mention").checked = settings.completion_mention_enabled;
|
|
2181
|
+
const model = renderSessionSettingsFields(scheduleInput, settings, editor.models, editor.contextAvailable);
|
|
2060
2182
|
scheduleInput("session-summary").textContent = scheduleSessionSummary(settings);
|
|
2061
2183
|
const notes = [];
|
|
2062
2184
|
if (editor.catalogMessage) notes.push(editor.catalogMessage);
|
|
@@ -2106,23 +2228,8 @@ async function loadScheduleSessionOptions() {
|
|
|
2106
2228
|
function changeScheduleSessionSettings(field) {
|
|
2107
2229
|
const editor = scheduleEditor;
|
|
2108
2230
|
if (!editor) return;
|
|
2109
|
-
const settings = editor.sessionDraft;
|
|
2110
2231
|
editor.settingsTouched = true;
|
|
2111
|
-
|
|
2112
|
-
const id = scheduleInput("model").value;
|
|
2113
|
-
const selected = editor.models.find((item) => item.id === id);
|
|
2114
|
-
if (!id) settings.turn_settings = null;
|
|
2115
|
-
else if (selected) settings.turn_settings = { model_id: selected.id,
|
|
2116
|
-
effort_id: selected.default_effort_id, service_tier_id: selected.default_service_tier_id };
|
|
2117
|
-
else if (id === editor.sessionBase.turn_settings?.model_id) settings.turn_settings = structuredClone(editor.sessionBase.turn_settings);
|
|
2118
|
-
} else if (field === "effort" && settings.turn_settings) {
|
|
2119
|
-
settings.turn_settings.effort_id = scheduleInput("effort").value;
|
|
2120
|
-
} else if (field === "tier" && settings.turn_settings) {
|
|
2121
|
-
settings.turn_settings.service_tier_id = scheduleInput("tier").value;
|
|
2122
|
-
} else if (field === "context") settings.message_context_mode = scheduleInput("context").value;
|
|
2123
|
-
else if (field === "reactions") settings.reaction_pulse_enabled = scheduleInput("reactions").checked;
|
|
2124
|
-
else if (field === "progress") settings.progress_card_enabled = scheduleInput("progress").checked;
|
|
2125
|
-
else if (field === "completion-mention") settings.completion_mention_enabled = scheduleInput("completion-mention").checked;
|
|
2232
|
+
changeSessionSettingsField(scheduleInput, editor.sessionDraft, editor.models, field, editor.sessionBase.turn_settings);
|
|
2126
2233
|
invalidateSchedulePreview();
|
|
2127
2234
|
renderScheduleSessionSettings();
|
|
2128
2235
|
}
|
|
@@ -2743,6 +2850,16 @@ document.querySelector("#projects-next").addEventListener("click", () => refresh
|
|
|
2743
2850
|
document.querySelector("#sessions-previous").addEventListener("click", () => moveSessionPage("previous"));
|
|
2744
2851
|
document.querySelector("#sessions-next").addEventListener("click", () => moveSessionPage("next"));
|
|
2745
2852
|
document.querySelector("#sides-next").addEventListener("click", () => refresh("side-topics", state.sideCursor));
|
|
2853
|
+
sessionSettingsInput("cancel").addEventListener("click", closeSessionSettingsEditor);
|
|
2854
|
+
sessionSettingsInput("close").addEventListener("click", closeSessionSettingsEditor);
|
|
2855
|
+
sessionSettingsInput("drawer").addEventListener("cancel", (event) => {
|
|
2856
|
+
event.preventDefault();
|
|
2857
|
+
closeSessionSettingsEditor();
|
|
2858
|
+
});
|
|
2859
|
+
sessionSettingsInput("editor").addEventListener("submit", saveSessionSettings);
|
|
2860
|
+
for (const field of ["model", "effort", "tier", "context", "reactions", "progress", "completion-mention"]) {
|
|
2861
|
+
sessionSettingsInput(field).addEventListener("change", () => changeSessionSettings(field));
|
|
2862
|
+
}
|
|
2746
2863
|
defaultsInput("new").addEventListener("click", () => openDefaultEditor(defaultsTab));
|
|
2747
2864
|
for (const kind of ["chat", "group_name"]) {
|
|
2748
2865
|
defaultsInput(`tab-${kind}`).addEventListener("click", () => selectDefaultTab(kind));
|
|
@@ -88,6 +88,34 @@
|
|
|
88
88
|
</nav>
|
|
89
89
|
</section>
|
|
90
90
|
|
|
91
|
+
<dialog id="session-settings-drawer" class="editor-drawer" aria-labelledby="session-settings-title" aria-describedby="session-settings-identity">
|
|
92
|
+
<form id="session-settings-editor" class="card-form drawer-form" hidden>
|
|
93
|
+
<div class="drawer-head"><div><p class="eyebrow">SESSION SETTINGS</p><h3 id="session-settings-title">配置 Session</h3></div><button id="session-settings-close" class="drawer-close" type="button" aria-label="关闭会话配置表单">×</button></div>
|
|
94
|
+
<div class="drawer-body">
|
|
95
|
+
<p id="session-settings-identity" class="meta-line"></p>
|
|
96
|
+
<p class="session-settings-help">只影响之后由 Netizen 启动的任务。选择继承 Codex 时,沿用 Codex 的原生配置。</p>
|
|
97
|
+
<fieldset id="session-settings-fields">
|
|
98
|
+
<h4>模型配置</h4>
|
|
99
|
+
<div class="schedule-form-grid">
|
|
100
|
+
<label>模型<select id="session-settings-model"><option value="">继承 Codex</option></select></label>
|
|
101
|
+
<label>推理强度<select id="session-settings-effort" disabled></select></label>
|
|
102
|
+
<label>速度<select id="session-settings-tier" disabled></select></label>
|
|
103
|
+
<label id="session-settings-context-field">消息上下文<select id="session-settings-context" aria-describedby="session-settings-context-note"><option value="current-only">当前消息</option><option value="catch-up">补齐未读上下文</option></select></label>
|
|
104
|
+
</div>
|
|
105
|
+
<p id="session-settings-context-note" class="session-settings-help"></p>
|
|
106
|
+
<h4>任务反馈</h4>
|
|
107
|
+
<div class="defaults-feedback">
|
|
108
|
+
<label><input id="session-settings-reactions" type="checkbox">表情反馈</label>
|
|
109
|
+
<label><input id="session-settings-progress" type="checkbox">进度卡片</label>
|
|
110
|
+
<label><input id="session-settings-completion-mention" type="checkbox">结束时 @ 提醒</label>
|
|
111
|
+
</div>
|
|
112
|
+
</fieldset>
|
|
113
|
+
<p id="session-settings-note" class="session-settings-help" role="status" aria-live="polite"></p>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="drawer-footer"><button id="session-settings-cancel" class="drawer-cancel" type="button">取消</button><button id="session-settings-save" type="submit">保存</button></div>
|
|
116
|
+
</form>
|
|
117
|
+
</dialog>
|
|
118
|
+
|
|
91
119
|
<section id="side-topics" class="panel" aria-labelledby="sides-heading" hidden>
|
|
92
120
|
<div class="section-head">
|
|
93
121
|
<div><p class="eyebrow">EPHEMERAL THREADS</p><h2 id="sides-heading">Side Topics</h2></div>
|
|
@@ -42,7 +42,6 @@ from .presentation import (
|
|
|
42
42
|
_runtime_binding_json,
|
|
43
43
|
_runtime_side_json,
|
|
44
44
|
_schedule_plan_json,
|
|
45
|
-
_settings_json,
|
|
46
45
|
_stop_disposition_message,
|
|
47
46
|
_topic_open_url,
|
|
48
47
|
)
|
|
@@ -78,6 +77,8 @@ from ..bindings import (
|
|
|
78
77
|
BindingQueryBusy,
|
|
79
78
|
BindingQueryClosed,
|
|
80
79
|
BindingQueryTimeout,
|
|
80
|
+
BindingContextRevisionConflict,
|
|
81
|
+
BindingFeedbackRevisionConflict,
|
|
81
82
|
BindingSettingsRevisionConflict,
|
|
82
83
|
BindingTurnSettings,
|
|
83
84
|
ProjectConflict as StoredProjectConflict,
|
|
@@ -143,6 +144,7 @@ from ..projects import (
|
|
|
143
144
|
)
|
|
144
145
|
from ..schedules.models import AmbiguousLocalTime, ScheduleError, resolve_once_local
|
|
145
146
|
from ..defaults import DefaultConfigurationError
|
|
147
|
+
from ..session_settings import SessionSettings, SessionSettingsError
|
|
146
148
|
|
|
147
149
|
|
|
148
150
|
logger = logging.getLogger(__name__)
|
|
@@ -193,6 +195,8 @@ class AdminActionPreconditions:
|
|
|
193
195
|
active_binding_id: ExpectedValue[str]
|
|
194
196
|
project_revision: ExpectedValue[int]
|
|
195
197
|
settings_revision: ExpectedValue[int]
|
|
198
|
+
context_revision: ExpectedValue[int]
|
|
199
|
+
feedback_revision: ExpectedValue[int]
|
|
196
200
|
native_thread_id: ExpectedValue[str]
|
|
197
201
|
activity_revision: ExpectedValue[int]
|
|
198
202
|
physical_turn_id: ExpectedValue[str]
|
|
@@ -489,6 +493,16 @@ class AdminWebApplication:
|
|
|
489
493
|
return await self._defaults(context)
|
|
490
494
|
if route == ("GET", "/api/v1/sessions"):
|
|
491
495
|
return await self._sessions(context)
|
|
496
|
+
if route == ("GET", "/api/v1/sessions/options"):
|
|
497
|
+
_require_query_keys(context.query, set())
|
|
498
|
+
if self._management.defaults is None:
|
|
499
|
+
raise AdminWebError(503, "unavailable", "会话配置选项暂不可用。")
|
|
500
|
+
options = await self._default_manage({"mode": "options"})
|
|
501
|
+
return _json_response(200, {
|
|
502
|
+
"requestId": context.request.request_id,
|
|
503
|
+
"models": options["models"],
|
|
504
|
+
"model_catalog_error": options["model_catalog_error"],
|
|
505
|
+
})
|
|
492
506
|
if route == ("GET", "/api/v1/projects/options"):
|
|
493
507
|
return await self._project_options(context)
|
|
494
508
|
if route == ("GET", "/api/v1/runtime-snapshots"):
|
|
@@ -540,12 +554,8 @@ class AdminWebApplication:
|
|
|
540
554
|
self, context: _RequestContext, status: dict[str, Any]
|
|
541
555
|
) -> Response:
|
|
542
556
|
payload = dict(status)
|
|
543
|
-
payload.pop("checkingErrorCode")
|
|
544
557
|
payload["message"] = "程序更新属于 Python 环境级操作,请在对应环境运行 netizen update;管理页只重启当前实例。"
|
|
545
|
-
payload["checkingError"] = None
|
|
546
558
|
actions: dict[str, object] = {
|
|
547
|
-
"check": None,
|
|
548
|
-
"install": None,
|
|
549
559
|
"restart": None,
|
|
550
560
|
}
|
|
551
561
|
if status.get("restartAvailable") is True:
|
|
@@ -1060,6 +1070,8 @@ class AdminWebApplication:
|
|
|
1060
1070
|
preconditions = _empty_preconditions(
|
|
1061
1071
|
active_binding_id=active_expected,
|
|
1062
1072
|
settings_revision=ExpectedValue.expect(binding.settings_revision),
|
|
1073
|
+
context_revision=ExpectedValue.expect(binding.context_revision),
|
|
1074
|
+
feedback_revision=ExpectedValue.expect(binding.feedback_revision),
|
|
1063
1075
|
native_thread_id=native_expected,
|
|
1064
1076
|
activity_revision=ExpectedValue.expect(runtime.activity_revision),
|
|
1065
1077
|
physical_turn_id=(
|
|
@@ -1171,10 +1183,7 @@ class AdminWebApplication:
|
|
|
1171
1183
|
"creator": binding.creator_id,
|
|
1172
1184
|
"createdAt": binding.created_at,
|
|
1173
1185
|
"activatedAt": binding.activated_at,
|
|
1174
|
-
"
|
|
1175
|
-
"turnSettings": _settings_json(binding.turn_settings),
|
|
1176
|
-
"messageContextMode": binding.message_context_mode.value,
|
|
1177
|
-
"contextRevision": binding.context_revision,
|
|
1186
|
+
"sessionSettings": SessionSettings.from_binding(binding).to_dict(),
|
|
1178
1187
|
"runtime": _runtime_binding_json(status),
|
|
1179
1188
|
"actions": actions,
|
|
1180
1189
|
}
|
|
@@ -1559,9 +1568,9 @@ class AdminWebApplication:
|
|
|
1559
1568
|
context,
|
|
1560
1569
|
"sessions.configure",
|
|
1561
1570
|
expected_resource="binding",
|
|
1562
|
-
allowed_extra={"
|
|
1571
|
+
allowed_extra={"sessionSettings"},
|
|
1563
1572
|
)
|
|
1564
|
-
settings =
|
|
1573
|
+
settings = SessionSettings.from_dict(payload.get("sessionSettings"))
|
|
1565
1574
|
preconditions = _grant_preconditions(grant)
|
|
1566
1575
|
revision = _expected_value(
|
|
1567
1576
|
preconditions.settings_revision,
|
|
@@ -1574,7 +1583,15 @@ class AdminWebApplication:
|
|
|
1574
1583
|
self._management.configure_exact_binding(
|
|
1575
1584
|
target=_binding_target(grant),
|
|
1576
1585
|
expected_settings_revision=revision,
|
|
1577
|
-
|
|
1586
|
+
expected_context_revision=_expected_value(
|
|
1587
|
+
preconditions.context_revision, "Binding context revision",
|
|
1588
|
+
),
|
|
1589
|
+
expected_feedback_revision=_expected_value(
|
|
1590
|
+
preconditions.feedback_revision, "Binding feedback revision",
|
|
1591
|
+
),
|
|
1592
|
+
settings=settings.turn_settings,
|
|
1593
|
+
task_feedback=settings.task_feedback,
|
|
1594
|
+
message_context_mode=settings.message_context_mode,
|
|
1578
1595
|
),
|
|
1579
1596
|
)
|
|
1580
1597
|
return _json_response(
|
|
@@ -2513,6 +2530,8 @@ def _empty_preconditions(
|
|
|
2513
2530
|
active_binding_id: ExpectedValue[str] | None = None,
|
|
2514
2531
|
project_revision: ExpectedValue[int] | None = None,
|
|
2515
2532
|
settings_revision: ExpectedValue[int] | None = None,
|
|
2533
|
+
context_revision: ExpectedValue[int] | None = None,
|
|
2534
|
+
feedback_revision: ExpectedValue[int] | None = None,
|
|
2516
2535
|
native_thread_id: ExpectedValue[str] | None = None,
|
|
2517
2536
|
activity_revision: ExpectedValue[int] | None = None,
|
|
2518
2537
|
physical_turn_id: ExpectedValue[str] | None = None,
|
|
@@ -2529,6 +2548,8 @@ def _empty_preconditions(
|
|
|
2529
2548
|
active_binding_id or ExpectedValue.dont_check(),
|
|
2530
2549
|
project_revision or ExpectedValue.dont_check(),
|
|
2531
2550
|
settings_revision or ExpectedValue.dont_check(),
|
|
2551
|
+
context_revision or ExpectedValue.dont_check(),
|
|
2552
|
+
feedback_revision or ExpectedValue.dont_check(),
|
|
2532
2553
|
native_thread_id or ExpectedValue.dont_check(),
|
|
2533
2554
|
activity_revision or ExpectedValue.dont_check(),
|
|
2534
2555
|
physical_turn_id or ExpectedValue.dont_check(),
|
|
@@ -2606,7 +2627,6 @@ def _runtime_precondition(grant: object) -> RuntimePrecondition:
|
|
|
2606
2627
|
|
|
2607
2628
|
|
|
2608
2629
|
_UPDATE_HTTP_ERRORS = {
|
|
2609
|
-
"update_unsupported": (409, "update_unsupported", "请在对应 Python 环境运行 netizen update。"),
|
|
2610
2630
|
"restart_unsupported": (409, "restart_unsupported", "仅绑定当前 Python 环境的受管实例支持 Admin 重启。"),
|
|
2611
2631
|
"update_busy": (409, "update_busy", "另一个安装或维护操作正在执行,请稍后查看结果。"),
|
|
2612
2632
|
"update_state_unavailable": (
|
|
@@ -2639,6 +2659,8 @@ def _map_error(error: BaseException) -> AdminWebError | None:
|
|
|
2639
2659
|
if isinstance(error, UpdateError):
|
|
2640
2660
|
mapped = _UPDATE_HTTP_ERRORS.get(error.code)
|
|
2641
2661
|
return AdminWebError(*mapped) if mapped is not None else None
|
|
2662
|
+
if isinstance(error, SessionSettingsError):
|
|
2663
|
+
return AdminWebError(400, error.code, str(error))
|
|
2642
2664
|
if isinstance(error, (ValueError, AmbiguousBinding)) and not isinstance(error, ProjectError):
|
|
2643
2665
|
return AdminWebError(400, "invalid_input", "请求参数无效。")
|
|
2644
2666
|
if isinstance(
|
|
@@ -2652,6 +2674,8 @@ def _map_error(error: BaseException) -> AdminWebError | None:
|
|
|
2652
2674
|
ActivePointerChanged,
|
|
2653
2675
|
BindingScopeMismatch,
|
|
2654
2676
|
BindingSettingsRevisionConflict,
|
|
2677
|
+
BindingContextRevisionConflict,
|
|
2678
|
+
BindingFeedbackRevisionConflict,
|
|
2655
2679
|
RuntimeStateChanged,
|
|
2656
2680
|
StaleProject,
|
|
2657
2681
|
StoredProjectConflict,
|