netizen-cli 0.10.2__tar.gz → 0.11.0__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.2/netizen_cli.egg-info → netizen_cli-0.11.0}/PKG-INFO +2 -2
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/README.md +6 -1
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/__init__.py +1 -1
- netizen_cli-0.11.0/netizen_cli/account_rate_limits.py +340 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/presentation.py +5 -18
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/static/admin.css +10 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/static/admin.js +285 -48
- netizen_cli-0.11.0/netizen_cli/admin/static/chat-picker.css +19 -0
- netizen_cli-0.11.0/netizen_cli/admin/static/chat-picker.js +258 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/static/index.html +33 -8
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/web.py +114 -30
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/bindings.py +48 -2
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cards/__init__.py +4 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cards/controls.py +99 -28
- netizen_cli-0.11.0/netizen_cli/cards/fork.py +327 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cards/questions.py +79 -21
- netizen_cli-0.11.0/netizen_cli/cards/usage.py +101 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel_app.py +439 -58
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/codex_runtime.py +286 -82
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/domain.py +3 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/experience.py +25 -1
- netizen_cli-0.11.0/netizen_cli/feishu_links.py +26 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/main.py +19 -10
- netizen_cli-0.11.0/netizen_cli/management/chat_directory.py +236 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/management/service.py +44 -5
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/runtime/contracts.py +9 -5
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/sdk_gap_adapter.py +9 -2
- netizen_cli-0.11.0/netizen_cli/task_text.py +174 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/terminal_cleanup.py +2 -2
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/turn_plan_observer.py +2 -2
- {netizen_cli-0.10.2 → netizen_cli-0.11.0/netizen_cli.egg-info}/PKG-INFO +2 -2
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli.egg-info/SOURCES.txt +19 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli.egg-info/requires.txt +1 -1
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/pyproject.toml +2 -2
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/skills/netizen-user-guide/SKILL.md +6 -2
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/skills/netizen-user-guide/references/user-guide.md +121 -24
- netizen_cli-0.11.0/tests/test_account_rate_limits.py +445 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cards.py +91 -1
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_channel_app.py +413 -13
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli_update.py +10 -1
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_codex_runtime.py +69 -136
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_codex_sdk_capabilities.py +1 -1
- netizen_cli-0.11.0/tests/test_codex_sdk_upgrade_0161.py +207 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_deployment.py +1 -1
- netizen_cli-0.11.0/tests/test_fork_cards.py +244 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_main.py +5 -1
- netizen_cli-0.11.0/tests/test_persistent_fork_bindings.py +104 -0
- netizen_cli-0.11.0/tests/test_persistent_fork_channel.py +450 -0
- netizen_cli-0.11.0/tests/test_persistent_fork_runtime.py +312 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_question_cards.py +68 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_question_channel.py +130 -9
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_question_target_parity.py +79 -7
- netizen_cli-0.11.0/tests/test_resume_channel.py +91 -0
- netizen_cli-0.11.0/tests/test_runtime_resume_activation.py +226 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_sdk_gap_adapter.py +22 -1
- netizen_cli-0.11.0/tests/test_task_text.py +250 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_terminal_cleanup.py +2 -2
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_turn_plan_observer.py +1 -1
- netizen_cli-0.11.0/tests/test_usage_card.py +135 -0
- netizen_cli-0.11.0/tests/test_usage_control.py +167 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/MANIFEST.in +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/_build.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/config.example.yaml +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/__main__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/__init__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/auth.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/errors.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/port_config.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/queries.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/admin/transport.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/builtin_skills.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cards/callbacks.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cards/defaults.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cards/model_info.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cards/reply.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cards/scheduled.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/__init__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/completion_mentions.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/input_preparation.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/messages.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/ports.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/question_inputs.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/reactions.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/reply_presenter.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/channel/topics.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cli.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cli_data.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cli_packages.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cli_services.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cli_setup.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cli_update.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cli_update_restore.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/cli_update_worker.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/completion_mention.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/database_migrations.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/defaults/__init__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/defaults/models.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/defaults/service.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/defaults/store.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/deployment/__init__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/deployment/restart_worker.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/deployment/update_executor.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/deployment/update_protocol.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/error_messages.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/feishu_app_onboarding.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/feishu_app_permissions.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/git_status.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/image_inputs.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/instance.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/lark_app.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/management/__init__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/management/blocking_io.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/management/chat_labels.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/management/coordination.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/management/updates.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/markdown_images.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/message_content.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/message_history.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/message_preparation.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/message_projection.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/migrations/__init__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/migrations/schema.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/migrations/v14.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/model_settings.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/package_resources.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/projects.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/prompt_projection.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/quoted_context.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/result_images.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/runtime/__init__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/runtime/name_writes.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/runtime/thread_naming.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/schedules/__init__.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/schedules/mcp.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/schedules/models.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/schedules/scheduler.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/schedules/service.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/schedules/store.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/service_launcher.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/session_settings.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/settings.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/skill_references.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/turn_activity.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/turn_files.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/turn_patch_children.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli/user_questions.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli.egg-info/dependency_links.txt +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli.egg-info/entry_points.txt +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/netizen_cli.egg-info/top_level.txt +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/setup.cfg +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/skills/netizen-lark/SKILL.md +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_admin_update_executor_probe.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_binding_scheduled_channel.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_bindings.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_channel_fixtures.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_channel_input_preparation.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_check_sdk.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli_data.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli_distribution.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli_integration.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli_packages.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli_packaging.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli_services.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_cli_setup.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_completion_mention_cards.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_completion_mentions.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_completion_topic_delivery.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_database_migrations.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_defaults_cards.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_defaults_channel.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_defaults_runtime.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_documentation_links.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_domain_experience.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_error_messages.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_feishu_app_onboarding.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_feishu_app_permissions.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_file_pagination.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_git_status.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_image_inputs.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_lark_app.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_lark_sdk_contract.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_markdown_images.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_message_content.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_message_history.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_message_material_inputs.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_message_preparation.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_migration_schema_validation.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_model_info_cards.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_model_settings.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_name_writes.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_probe_child_files.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_probe_feishu_completion_mention.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_probe_feishu_turn_file_card.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_probe_python_sdk.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_probe_scheduled_tasks.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_probe_sdk_completion_race.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_probe_thread_naming.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_project_delete_channel.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_project_delete_runtime.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_project_deletion_storage.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_prompt_projection.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_quoted_context.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_release_script.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_repository_hygiene.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_result_images.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_retired_deployment.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_runtime_thread_naming.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_schedule_card_retry.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_schedule_cards.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_schedule_mcp.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_schedule_run_now.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_schedule_scope_matrix.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_schedule_service.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_scheduled_channel.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_scheduled_runtime.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_scheduler.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_scheduler_presenter.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_schedules_storage.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_service_launcher.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_session_defaults.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_session_settings.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_settings_projects.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_thread_naming.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_turn_activity.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_turn_files.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_turn_patch_children.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_turn_patch_statistics.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_update_executor.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/tests/test_user_guide_skill.py +0 -0
- {netizen_cli-0.10.2 → netizen_cli-0.11.0}/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.
|
|
3
|
+
Version: 0.11.0
|
|
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.161.0
|
|
14
14
|
Requires-Dist: pydantic==2.13.4
|
|
15
15
|
Requires-Dist: PyYAML==6.0.3
|
|
16
16
|
Requires-Dist: qrcode==8.2
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
[群聊上下文](skills/netizen-user-guide/references/user-guide.md#发送消息引用与图片)。
|
|
42
42
|
- **取回本轮文件。** 完成后从回复卡片中按需发送有原生执行记录的文件;发送的是点击时
|
|
43
43
|
的当前内容,详见[文件说明](skills/netizen-user-guide/references/user-guide.md#查看和发送本轮文件)。
|
|
44
|
+
- **保留上下文,另开一条讨论。** 用
|
|
45
|
+
[持久分支](skills/netizen-user-guide/references/user-guide.md#fork-持久分支)在当前聊天或其他群
|
|
46
|
+
创建普通话题,之后独立继续。
|
|
44
47
|
- **展开临时讨论,推进持续目标。** 用
|
|
45
48
|
[Side](skills/netizen-user-guide/references/user-guide.md#side-临时话题)另开临时话题,或用
|
|
46
49
|
[Goal](skills/netizen-user-guide/references/user-guide.md#goal)让 Codex 围绕目标持续推进。
|
|
@@ -122,8 +125,10 @@ setup 准备配置、飞书授权和实例数据并注册服务,不默认启
|
|
|
122
125
|
| 新建会话,选择项目 | `/new` |
|
|
123
126
|
| 查看、保存或删除当前聊天的会话默认配置 | `/defaults` |
|
|
124
127
|
| 查看并切换已有会话 | `/sessions` |
|
|
128
|
+
| 从当前空闲会话另建持久分支话题 | `/fork` |
|
|
125
129
|
| 调整模型、思考强度和过程卡 | `/config`,当前会话空闲时使用 |
|
|
126
130
|
| 查看当前任务和上下文用量 | `/status` |
|
|
131
|
+
| 查看共享 Codex 账号额度和重置时间 | `/usage` |
|
|
127
132
|
| 中断当前任务 | `/stop`;不保证前台工具进程退出,见[停止说明](skills/netizen-user-guide/references/user-guide.md#stop) |
|
|
128
133
|
| 查找当前机器人的管理入口 | `/admin`;返回实际管理 URL 和实例根目录,不返回登录凭据 |
|
|
129
134
|
| 管理定时任务 | `/cron`,也可以直接用自然语言描述计划 |
|
|
@@ -175,7 +180,7 @@ Side 会在同一聊天中新建话题,可继续多轮讨论。它与原会话
|
|
|
175
180
|
飞书 1.0 卡片明确不支持。
|
|
176
181
|
输出文件按本轮原生记录发现,不扫描工作区补齐,也不自动上传。
|
|
177
182
|
- **Codex 能力有宿主差异。** 飞书复用原生会话和工具,但并非每个 App/CLI 控件都可用;
|
|
178
|
-
|
|
183
|
+
全新会话采用 `auto_review`,持久分支沿用原生继承权限;不提供 App 的 Ask/Custom 选择器。
|
|
179
184
|
[完整差异与当前限制](skills/netizen-user-guide/references/user-guide.md#与-codex-appcli-的差异)
|
|
180
185
|
|
|
181
186
|
<a id="用户指南-skill"></a>
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""One read-only, removable bridge for native account quota snapshots."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
|
10
|
+
from typing import Any, Protocol
|
|
11
|
+
|
|
12
|
+
from openai_codex import AsyncCodex
|
|
13
|
+
from pydantic import ValidationError, field_validator
|
|
14
|
+
|
|
15
|
+
from .sdk_gap_adapter import (
|
|
16
|
+
SdkFacadeMigrationRequired,
|
|
17
|
+
SdkGapCapabilityUnavailable,
|
|
18
|
+
_generated_type,
|
|
19
|
+
_initialized_client,
|
|
20
|
+
_require_model_fields,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_CAPABILITY = "account-rate-limits"
|
|
25
|
+
_READ_METHOD = "account/rateLimits/read"
|
|
26
|
+
_READ_TIMEOUT_SECONDS = 10.0
|
|
27
|
+
_MAX_BUCKETS = 64
|
|
28
|
+
_MAX_LABEL_LENGTH = 256
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AccountRateLimitsUnavailable(RuntimeError):
|
|
32
|
+
"""The response cannot supply a trustworthy quota projection."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True, slots=True)
|
|
36
|
+
class AccountRateLimitWindow:
|
|
37
|
+
used_percent: int
|
|
38
|
+
window_duration_mins: int | None
|
|
39
|
+
resets_at: int | None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True, slots=True)
|
|
43
|
+
class AccountCredits:
|
|
44
|
+
has_credits: bool
|
|
45
|
+
unlimited: bool
|
|
46
|
+
balance: int | None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True, slots=True)
|
|
50
|
+
class AccountMonthlyCreditLimit:
|
|
51
|
+
limit: int
|
|
52
|
+
used: int
|
|
53
|
+
remaining_percent: int
|
|
54
|
+
resets_at: int
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True, slots=True)
|
|
58
|
+
class AccountRateLimitBucket:
|
|
59
|
+
limit_id: str | None
|
|
60
|
+
limit_name: str | None
|
|
61
|
+
primary: AccountRateLimitWindow | None
|
|
62
|
+
secondary: AccountRateLimitWindow | None
|
|
63
|
+
credits: AccountCredits | None = None
|
|
64
|
+
monthly_limit: AccountMonthlyCreditLimit | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True, slots=True)
|
|
68
|
+
class AccountRateLimitsSnapshot:
|
|
69
|
+
buckets: tuple[AccountRateLimitBucket, ...]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class AccountRateLimits(Protocol):
|
|
73
|
+
async def read(self) -> AccountRateLimitsSnapshot: ...
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def facade_migration_requirements() -> tuple[str, ...]:
|
|
77
|
+
"""Require review when a candidate public account-quota facade appears."""
|
|
78
|
+
|
|
79
|
+
candidates = (
|
|
80
|
+
"rate_limits",
|
|
81
|
+
"rate_limits_read",
|
|
82
|
+
"account_rate_limits",
|
|
83
|
+
"account_rate_limits_read",
|
|
84
|
+
"get_account_rate_limits",
|
|
85
|
+
"read_account_rate_limits",
|
|
86
|
+
)
|
|
87
|
+
names = tuple(
|
|
88
|
+
f"AsyncCodex.{name}" for name in candidates if hasattr(AsyncCodex, name)
|
|
89
|
+
)
|
|
90
|
+
return (
|
|
91
|
+
(f"migration-required:{_CAPABILITY}:{','.join(names)}",) if names else ()
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class AppServerAccountRateLimits:
|
|
96
|
+
"""Read once through the same initialized SDK-owned client.
|
|
97
|
+
|
|
98
|
+
Timeout/cancellation stops waiting, not the SDK's underlying sync request.
|
|
99
|
+
This bridge never retries, resets quota, consumes notifications, or closes
|
|
100
|
+
the shared client. Late results remain owned by the SDK transport.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
__slots__ = ("_client", "_params_model", "_response_model", "_bucket_model")
|
|
104
|
+
|
|
105
|
+
def __init__(self, codex: AsyncCodex) -> None:
|
|
106
|
+
if requirements := facade_migration_requirements():
|
|
107
|
+
raise SdkFacadeMigrationRequired("; ".join(requirements))
|
|
108
|
+
self._client = _initialized_client(codex, capability=_CAPABILITY)
|
|
109
|
+
self._params_model = _model(
|
|
110
|
+
"GetAccountRateLimitsParams",
|
|
111
|
+
{
|
|
112
|
+
"exclude_reset_credit_details": "excludeResetCreditDetails",
|
|
113
|
+
"supports_luna_reserve": "supportsLunaReserve",
|
|
114
|
+
},
|
|
115
|
+
)
|
|
116
|
+
self._response_model = _model(
|
|
117
|
+
"GetAccountRateLimitsResponse",
|
|
118
|
+
{
|
|
119
|
+
"account_id": "accountId",
|
|
120
|
+
"ordinary_usage_allowed": "ordinaryUsageAllowed",
|
|
121
|
+
"rate_limit_reset_credits": "rateLimitResetCredits",
|
|
122
|
+
"rate_limit_upsell": "rateLimitUpsell",
|
|
123
|
+
"rate_limits": "rateLimits",
|
|
124
|
+
"rate_limits_by_limit_id": "rateLimitsByLimitId",
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
self._bucket_model = _model(
|
|
128
|
+
"RateLimitSnapshot",
|
|
129
|
+
{
|
|
130
|
+
"credits": "credits",
|
|
131
|
+
"individual_limit": "individualLimit",
|
|
132
|
+
"limit_id": "limitId",
|
|
133
|
+
"limit_name": "limitName",
|
|
134
|
+
"normal_model_slug": "normalModelSlug",
|
|
135
|
+
"plan_type": "planType",
|
|
136
|
+
"primary": "primary",
|
|
137
|
+
"rate_limit_reached_type": "rateLimitReachedType",
|
|
138
|
+
"secondary": "secondary",
|
|
139
|
+
"spend_control_reached": "spendControlReached",
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
_model(
|
|
143
|
+
"RateLimitWindow",
|
|
144
|
+
{
|
|
145
|
+
"resets_at": "resetsAt",
|
|
146
|
+
"used_percent": "usedPercent",
|
|
147
|
+
"window_duration_mins": "windowDurationMins",
|
|
148
|
+
},
|
|
149
|
+
)
|
|
150
|
+
_model(
|
|
151
|
+
"CreditsSnapshot",
|
|
152
|
+
{
|
|
153
|
+
"balance": "balance",
|
|
154
|
+
"has_credits": "hasCredits",
|
|
155
|
+
"unlimited": "unlimited",
|
|
156
|
+
},
|
|
157
|
+
)
|
|
158
|
+
_model(
|
|
159
|
+
"SpendControlLimitSnapshot",
|
|
160
|
+
{
|
|
161
|
+
"limit": "limit",
|
|
162
|
+
"used": "used",
|
|
163
|
+
"remaining_percent": "remainingPercent",
|
|
164
|
+
"resets_at": "resetsAt",
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
self._response_model = _strict_response_model(
|
|
168
|
+
self._response_model, self._bucket_model
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
async def read(self) -> AccountRateLimitsSnapshot:
|
|
172
|
+
params = self._params_model(
|
|
173
|
+
excludeResetCreditDetails=True, supportsLunaReserve=False
|
|
174
|
+
)
|
|
175
|
+
try:
|
|
176
|
+
async with asyncio.timeout(_READ_TIMEOUT_SECONDS):
|
|
177
|
+
response = await self._client.request(
|
|
178
|
+
_READ_METHOD,
|
|
179
|
+
params.model_dump(by_alias=True),
|
|
180
|
+
response_model=self._response_model,
|
|
181
|
+
)
|
|
182
|
+
except ValidationError as error:
|
|
183
|
+
raise AccountRateLimitsUnavailable("Codex 额度响应格式无效。") from error
|
|
184
|
+
if not isinstance(response, self._response_model):
|
|
185
|
+
raise AccountRateLimitsUnavailable("Codex 额度响应类型无效。")
|
|
186
|
+
multiple = response.rate_limits_by_limit_id
|
|
187
|
+
if multiple is None:
|
|
188
|
+
buckets = (self._project_bucket(response.rate_limits),)
|
|
189
|
+
else:
|
|
190
|
+
if not isinstance(multiple, dict) or not 0 < len(multiple) <= _MAX_BUCKETS:
|
|
191
|
+
raise AccountRateLimitsUnavailable("Codex 额度分组为空或超出读取范围。")
|
|
192
|
+
projected = []
|
|
193
|
+
for key, raw in multiple.items():
|
|
194
|
+
limit_id = _label(key, required=True)
|
|
195
|
+
if not isinstance(raw, dict):
|
|
196
|
+
raise AccountRateLimitsUnavailable("Codex 额度分组格式无效。")
|
|
197
|
+
try:
|
|
198
|
+
# JSON strict validation preserves valid string enums while
|
|
199
|
+
# rejecting coercion of window strings/bools into numbers.
|
|
200
|
+
bucket = self._bucket_model.model_validate_json(
|
|
201
|
+
json.dumps(raw, allow_nan=False), strict=True
|
|
202
|
+
)
|
|
203
|
+
except (ValidationError, TypeError, ValueError) as error:
|
|
204
|
+
raise AccountRateLimitsUnavailable(
|
|
205
|
+
"Codex 额度分组格式无效。"
|
|
206
|
+
) from error
|
|
207
|
+
projected.append(self._project_bucket(bucket, limit_id=limit_id))
|
|
208
|
+
buckets = tuple(projected)
|
|
209
|
+
if not any(
|
|
210
|
+
bucket.primary is not None or bucket.secondary is not None
|
|
211
|
+
or bucket.credits is not None or bucket.monthly_limit is not None
|
|
212
|
+
for bucket in buckets
|
|
213
|
+
):
|
|
214
|
+
raise AccountRateLimitsUnavailable("Codex 尚未提供可用的额度数据。")
|
|
215
|
+
return AccountRateLimitsSnapshot(buckets)
|
|
216
|
+
|
|
217
|
+
def _project_bucket(
|
|
218
|
+
self, bucket: Any, *, limit_id: str | None = None
|
|
219
|
+
) -> AccountRateLimitBucket:
|
|
220
|
+
if not isinstance(bucket, self._bucket_model):
|
|
221
|
+
raise AccountRateLimitsUnavailable("Codex 额度分组格式无效。")
|
|
222
|
+
supplied_id = _label(bucket.limit_id)
|
|
223
|
+
if limit_id is not None and supplied_id is not None and limit_id != supplied_id:
|
|
224
|
+
raise AccountRateLimitsUnavailable("Codex 额度分组标识不一致。")
|
|
225
|
+
return AccountRateLimitBucket(
|
|
226
|
+
limit_id=limit_id if limit_id is not None else supplied_id,
|
|
227
|
+
limit_name=_label(bucket.limit_name),
|
|
228
|
+
primary=_window(bucket.primary),
|
|
229
|
+
secondary=_window(bucket.secondary),
|
|
230
|
+
credits=_credits(bucket.credits),
|
|
231
|
+
monthly_limit=_monthly_limit(bucket.individual_limit),
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _model(name: str, aliases: dict[str, str]) -> type[Any]:
|
|
236
|
+
model = _generated_type(name, capability=_CAPABILITY)
|
|
237
|
+
_require_model_fields(model, capability=_CAPABILITY, aliases=aliases)
|
|
238
|
+
if not callable(getattr(model, "model_validate_json", None)):
|
|
239
|
+
raise SdkGapCapabilityUnavailable(f"{_CAPABILITY} model validation changed")
|
|
240
|
+
return model
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _strict_response_model(
|
|
244
|
+
response_model: type[Any], bucket_model: type[Any]
|
|
245
|
+
) -> type[Any]:
|
|
246
|
+
# Inherit every field from the installed SDK. Its request decoder calls
|
|
247
|
+
# model_validate on the raw response, so validate legacy windows before
|
|
248
|
+
# Pydantic can coerce false, "0", or 0.0 into a known zero percent.
|
|
249
|
+
class StrictAccountRateLimitsResponse(response_model):
|
|
250
|
+
@field_validator("rate_limits", mode="before")
|
|
251
|
+
@classmethod
|
|
252
|
+
def _strict_legacy_bucket(cls, value: Any) -> Any:
|
|
253
|
+
if not isinstance(value, dict):
|
|
254
|
+
raise ValueError("expected a quota bucket object")
|
|
255
|
+
try:
|
|
256
|
+
return bucket_model.model_validate_json(
|
|
257
|
+
json.dumps(value, allow_nan=False), strict=True
|
|
258
|
+
)
|
|
259
|
+
except TypeError as error:
|
|
260
|
+
raise ValueError("invalid quota bucket object") from error
|
|
261
|
+
|
|
262
|
+
return StrictAccountRateLimitsResponse
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _label(value: Any, *, required: bool = False) -> str | None:
|
|
266
|
+
if value is None and not required:
|
|
267
|
+
return None
|
|
268
|
+
if (
|
|
269
|
+
not isinstance(value, str)
|
|
270
|
+
or not value
|
|
271
|
+
or value.strip() != value
|
|
272
|
+
or len(value) > _MAX_LABEL_LENGTH
|
|
273
|
+
or any(ord(char) < 32 or ord(char) == 127 for char in value)
|
|
274
|
+
):
|
|
275
|
+
raise AccountRateLimitsUnavailable("Codex 额度分组名称或标识无效。")
|
|
276
|
+
return value
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _window(value: Any) -> AccountRateLimitWindow | None:
|
|
280
|
+
if value is None:
|
|
281
|
+
return None
|
|
282
|
+
used = _nonnegative_int(value.used_percent)
|
|
283
|
+
duration = (
|
|
284
|
+
None if value.window_duration_mins is None
|
|
285
|
+
else _nonnegative_int(value.window_duration_mins)
|
|
286
|
+
)
|
|
287
|
+
resets = None if value.resets_at is None else _nonnegative_int(value.resets_at)
|
|
288
|
+
if duration == 0:
|
|
289
|
+
raise AccountRateLimitsUnavailable("Codex 额度窗口时长无效。")
|
|
290
|
+
# Native i32 percentages can exceed 100; do not invent a capped usage value.
|
|
291
|
+
return AccountRateLimitWindow(used, duration, resets)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _credits(value: Any) -> AccountCredits | None:
|
|
295
|
+
if value is None or not (value.unlimited or value.has_credits):
|
|
296
|
+
return None
|
|
297
|
+
# The native display treats a hidden or invalid balance as Available; it
|
|
298
|
+
# never exposes the raw string or infers requests from credit balances.
|
|
299
|
+
balance = _credit_amount(value.balance, positive=True)
|
|
300
|
+
return AccountCredits(value.has_credits, value.unlimited, balance)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _monthly_limit(value: Any) -> AccountMonthlyCreditLimit | None:
|
|
304
|
+
if value is None:
|
|
305
|
+
return None
|
|
306
|
+
limit, used = _credit_amount(value.limit), _credit_amount(value.used)
|
|
307
|
+
if limit is None or used is None:
|
|
308
|
+
return None
|
|
309
|
+
# Native remainingPercent can fall outside the display range; clamp only
|
|
310
|
+
# while rendering, independently of the reported used/limit amounts.
|
|
311
|
+
remaining = value.remaining_percent
|
|
312
|
+
if isinstance(remaining, bool) or not isinstance(remaining, int):
|
|
313
|
+
raise AccountRateLimitsUnavailable("Codex 月度额度数值无效。")
|
|
314
|
+
return AccountMonthlyCreditLimit(
|
|
315
|
+
limit, used, remaining, _nonnegative_int(value.resets_at)
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _credit_amount(value: str | None, *, positive: bool = False) -> int | None:
|
|
320
|
+
if value is None:
|
|
321
|
+
return None
|
|
322
|
+
raw = value.strip()
|
|
323
|
+
if re.fullmatch(r"[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?", raw) is None:
|
|
324
|
+
return None
|
|
325
|
+
try:
|
|
326
|
+
amount = Decimal(raw)
|
|
327
|
+
except InvalidOperation:
|
|
328
|
+
return None
|
|
329
|
+
if (
|
|
330
|
+
not amount.is_finite() or amount < 0 or (positive and amount == 0)
|
|
331
|
+
or amount > 2**63 - 1
|
|
332
|
+
):
|
|
333
|
+
return None
|
|
334
|
+
return int(amount.to_integral_value(rounding=ROUND_HALF_UP))
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _nonnegative_int(value: Any) -> int:
|
|
338
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
339
|
+
raise AccountRateLimitsUnavailable("Codex 额度窗口数值无效。")
|
|
340
|
+
return value
|
|
@@ -6,9 +6,9 @@ from dataclasses import asdict, is_dataclass
|
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
from enum import Enum
|
|
8
8
|
from typing import Any
|
|
9
|
-
from urllib.parse import urlencode
|
|
10
9
|
from zoneinfo import ZoneInfo
|
|
11
10
|
|
|
11
|
+
from ..feishu_links import chat_open_url, topic_open_url as _topic_open_url
|
|
12
12
|
from ..runtime.contracts import (
|
|
13
13
|
ReleaseDisposition,
|
|
14
14
|
StopDisposition,
|
|
@@ -17,23 +17,10 @@ from ..management import BindingStatusProjection, ChatLabel
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _chat_open_url(chat: ChatLabel) -> str:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return f"https://applink.feishu.cn/client/chat/open?{query}"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def _topic_open_url(chat_id: str, topic_id: str) -> str:
|
|
28
|
-
# Match the official Lark CLI's desktop/mobile ID spellings and root position.
|
|
29
|
-
query = urlencode({
|
|
30
|
-
"open_chat_id": chat_id,
|
|
31
|
-
"open_thread_id": topic_id,
|
|
32
|
-
"openchatid": chat_id,
|
|
33
|
-
"openthreadid": topic_id,
|
|
34
|
-
"thread_position": -1,
|
|
35
|
-
})
|
|
36
|
-
return f"https://applink.feishu.cn/client/thread/open?{query}"
|
|
20
|
+
return chat_open_url(
|
|
21
|
+
chat.chat_id,
|
|
22
|
+
p2p_target_open_id=(chat.p2p_target_open_id if chat.chat_mode == "p2p" else None),
|
|
23
|
+
)
|
|
37
24
|
|
|
38
25
|
|
|
39
26
|
def _schedule_plan_json(plan: dict[str, Any], chat: ChatLabel) -> dict[str, Any]:
|
|
@@ -37,6 +37,16 @@ input, select, textarea { min-width: 0; border: 1px solid #bac3d0; border-radius
|
|
|
37
37
|
.card-form input { width: 100%; margin: 5px 0 12px; }
|
|
38
38
|
.card-form button, .filter-bar > button[type="submit"], .action { border: 0; border-radius: 7px; padding: 8px 11px; background: var(--blue); color: white; }
|
|
39
39
|
.filter-bar { display: grid; grid-template-columns: repeat(auto-fit, minmax(145px, 1fr)); align-items: end; gap: 10px; margin: 20px 0; }
|
|
40
|
+
.filter-bar > input[type="hidden"] { display: none; }
|
|
41
|
+
.chat-target, .schedule-binding-target { grid-column: 1 / -1; min-width: 0; }
|
|
42
|
+
.chat-target > label:first-child { max-width: 240px; margin-bottom: 12px; }
|
|
43
|
+
.card-form .chat-manual-toggle { display: flex; align-items: center; gap: 7px; margin: 10px 0; }
|
|
44
|
+
.card-form .chat-manual-toggle input { width: auto; margin: 0; }
|
|
45
|
+
.chat-target [hidden], .schedule-binding-target[hidden], .selection-notice[hidden] { display: none; }
|
|
46
|
+
.target-summary { white-space: pre-line; overflow-wrap: anywhere; padding: 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--wash); }
|
|
47
|
+
.selection-notice { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 14px 0; padding: 12px; border: 1px solid #bbcff5; border-radius: 8px; background: var(--blue-soft); }
|
|
48
|
+
.selection-notice p { margin: 0; font-size: 13px; }
|
|
49
|
+
.selection-notice button { flex-shrink: 0; }
|
|
40
50
|
.multi-filter { position: relative; min-width: 0; margin: 0; padding: 0; border: 0; }
|
|
41
51
|
.multi-filter legend { margin-bottom: 5px; padding: 0; color: var(--muted); font-size: 13px; }
|
|
42
52
|
.multi-filter-trigger { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; min-height: 36px; border: 1px solid #bac3d0; border-radius: 7px; padding: 8px 10px; background: white; color: var(--ink); text-align: left; }
|