netizen-cli 0.10.0__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.0/netizen_cli.egg-info → netizen_cli-0.10.2}/PKG-INFO +2 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/README.md +3 -4
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/__init__.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/port_config.py +2 -19
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/presentation.py +0 -48
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/static/admin.css +7 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/static/admin.js +190 -73
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/static/index.html +28 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/transport.py +4 -12
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/web.py +37 -18
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/bindings.py +9 -159
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cards/controls.py +0 -32
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cards/reply.py +0 -15
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cards/scheduled.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel_app.py +21 -42
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cli_packages.py +5 -66
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cli_services.py +0 -4
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/codex_runtime.py +111 -169
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/defaults/models.py +0 -4
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/main.py +4 -7
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/management/__init__.py +0 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/management/service.py +34 -38
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/management/updates.py +2 -3
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/message_projection.py +11 -36
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/projects.py +0 -7
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/runtime/thread_naming.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/schedules/models.py +0 -4
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/schedules/service.py +3 -17
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/schedules/store.py +9 -20
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/sdk_gap_adapter.py +10 -36
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/settings.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/terminal_cleanup.py +2 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/turn_files.py +13 -68
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/turn_plan_observer.py +2 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.2/netizen_cli.egg-info}/PKG-INFO +2 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli.egg-info/requires.txt +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/pyproject.toml +2 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/skills/netizen-user-guide/references/user-guide.md +8 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_bindings.py +79 -54
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_channel_app.py +135 -49
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli_packages.py +41 -62
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_codex_runtime.py +525 -80
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_codex_sdk_capabilities.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_deployment.py +48 -19
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_error_messages.py +4 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_main.py +4 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_probe_python_sdk.py +197 -53
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_probe_scheduled_tasks.py +45 -14
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_project_delete_channel.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_project_delete_runtime.py +3 -3
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_project_deletion_storage.py +3 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_question_channel.py +5 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_question_target_parity.py +3 -3
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_quoted_context.py +39 -21
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_runtime_thread_naming.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_schedule_scope_matrix.py +4 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_schedule_service.py +87 -14
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_scheduled_runtime.py +3 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_schedules_storage.py +56 -13
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_sdk_gap_adapter.py +9 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_session_settings.py +2 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_settings_projects.py +9 -5
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_terminal_cleanup.py +2 -2
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_thread_naming.py +68 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_turn_files.py +37 -175
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_turn_patch_statistics.py +28 -8
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_turn_plan_observer.py +1 -1
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/MANIFEST.in +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/_build.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/config.example.yaml +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/__main__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/auth.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/errors.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/admin/queries.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/builtin_skills.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cards/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cards/callbacks.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cards/defaults.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cards/model_info.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cards/questions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/completion_mentions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/input_preparation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/messages.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/ports.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/question_inputs.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/reactions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/reply_presenter.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/channel/topics.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cli.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cli_data.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cli_setup.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cli_update.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cli_update_restore.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/cli_update_worker.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/completion_mention.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/database_migrations.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/defaults/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/defaults/service.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/defaults/store.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/deployment/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/deployment/restart_worker.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/deployment/update_executor.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/deployment/update_protocol.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/domain.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/error_messages.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/experience.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/feishu_app_onboarding.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/feishu_app_permissions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/git_status.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/image_inputs.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/instance.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/lark_app.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/management/blocking_io.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/management/chat_labels.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/management/coordination.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/markdown_images.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/message_content.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/message_history.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/message_preparation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/migrations/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/migrations/schema.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/migrations/v14.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/model_settings.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/package_resources.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/prompt_projection.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/quoted_context.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/result_images.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/runtime/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/runtime/contracts.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/runtime/name_writes.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/schedules/__init__.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/schedules/mcp.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/schedules/scheduler.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/service_launcher.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/session_settings.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/skill_references.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/turn_activity.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/turn_patch_children.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli/user_questions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli.egg-info/SOURCES.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli.egg-info/dependency_links.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli.egg-info/entry_points.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/netizen_cli.egg-info/top_level.txt +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/setup.cfg +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/skills/netizen-lark/SKILL.md +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/skills/netizen-user-guide/SKILL.md +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_admin_update_executor_probe.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_binding_scheduled_channel.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_channel_fixtures.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_channel_input_preparation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_check_sdk.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli_data.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli_distribution.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli_integration.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli_packaging.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli_services.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli_setup.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_cli_update.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_completion_mention_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_completion_mentions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_completion_topic_delivery.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_database_migrations.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_defaults_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_defaults_channel.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_defaults_runtime.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_documentation_links.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_domain_experience.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_feishu_app_onboarding.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_feishu_app_permissions.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_file_pagination.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_git_status.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_image_inputs.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_lark_app.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_lark_sdk_contract.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_markdown_images.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_message_content.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_message_history.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_message_material_inputs.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_message_preparation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_migration_schema_validation.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_model_info_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_model_settings.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_name_writes.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_probe_child_files.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_probe_feishu_completion_mention.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_probe_feishu_turn_file_card.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_probe_sdk_completion_race.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_probe_thread_naming.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_prompt_projection.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_question_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_release_script.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_repository_hygiene.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_result_images.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_retired_deployment.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_schedule_card_retry.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_schedule_cards.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_schedule_mcp.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_schedule_run_now.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_scheduled_channel.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_scheduler.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_scheduler_presenter.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_service_launcher.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_session_defaults.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_turn_activity.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_turn_patch_children.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_update_executor.py +0 -0
- {netizen_cli-0.10.0 → netizen_cli-0.10.2}/tests/test_user_guide_skill.py +0 -0
- {netizen_cli-0.10.0 → 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
|
|
@@ -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")
|
|
@@ -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,
|
|
@@ -17,40 +16,6 @@ from ..runtime.contracts import (
|
|
|
17
16
|
from ..management import BindingStatusProjection, ChatLabel
|
|
18
17
|
|
|
19
18
|
|
|
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
19
|
def _chat_open_url(chat: ChatLabel) -> str:
|
|
55
20
|
if chat.chat_mode == "p2p" and chat.p2p_target_open_id is not None:
|
|
56
21
|
query = urlencode({"openId": chat.p2p_target_open_id})
|
|
@@ -110,16 +75,6 @@ def _jsonable(value: object) -> object:
|
|
|
110
75
|
raise TypeError(f"unsupported JSON projection: {type(value).__name__}")
|
|
111
76
|
|
|
112
77
|
|
|
113
|
-
def _settings_json(settings: BindingTurnSettings | None) -> dict[str, str] | None:
|
|
114
|
-
if settings is None:
|
|
115
|
-
return None
|
|
116
|
-
return {
|
|
117
|
-
"modelId": settings.model_id,
|
|
118
|
-
"effortId": settings.effort_id,
|
|
119
|
-
"serviceTierId": settings.service_tier_id,
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
78
|
def _runtime_binding_json(status: BindingStatusProjection) -> dict[str, object]:
|
|
124
79
|
snapshot = status.snapshot
|
|
125
80
|
turn = snapshot.turn
|
|
@@ -251,7 +206,4 @@ def _binding_result(request_id: str, binding: Any) -> dict[str, object]:
|
|
|
251
206
|
"scopeKey": binding.scope_key,
|
|
252
207
|
"current": binding.active,
|
|
253
208
|
"nativeThreadId": binding.native_thread_id,
|
|
254
|
-
"settingsRevision": binding.settings_revision,
|
|
255
|
-
"messageContextMode": binding.message_context_mode.value,
|
|
256
|
-
"contextRevision": binding.context_revision,
|
|
257
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>
|
|
@@ -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,
|