netizen-cli 0.14.1__tar.gz → 0.14.3__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.14.1/netizen_cli.egg-info → netizen_cli-0.14.3}/PKG-INFO +1 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/README.md +2 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/__init__.py +1 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/admin.js +48 -31
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/index.html +9 -5
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/worktrees.js +21 -28
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/web.py +6 -6
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/bindings.py +19 -11
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/controls.py +14 -20
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/defaults.py +2 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/scheduled.py +24 -6
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/workspace.py +3 -11
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/messages.py +12 -11
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/ports.py +6 -6
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/reactions.py +3 -6
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/reply_presenter.py +51 -93
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/topics.py +7 -7
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel_app.py +175 -207
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_packages.py +20 -7
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/codex_runtime.py +31 -40
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/database_migrations.py +2 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/domain.py +2 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/experience.py +7 -7
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/main.py +2 -3
- netizen_cli-0.14.3/netizen_cli/migrations/v20.py +96 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/model_settings.py +0 -22
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/mcp.py +7 -4
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/models.py +1 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/service.py +18 -8
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/store.py +25 -10
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sdk_gap_adapter.py +13 -5
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/presentation.py +38 -61
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/service.py +20 -32
- {netizen_cli-0.14.1 → netizen_cli-0.14.3/netizen_cli.egg-info}/PKG-INFO +1 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/SOURCES.txt +4 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/pyproject.toml +1 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/admin.md +4 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/commands.md +1 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/scheduled-tasks.md +24 -3
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/sessions.md +5 -7
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/user-guide.md +4 -4
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_bindings.py +45 -8
- netizen_cli-0.14.3/tests/test_card_error_feedback.py +244 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cards.py +2 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_channel_app.py +142 -125
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_channel_fixtures.py +20 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_data.py +6 -6
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_distribution.py +34 -6
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_packages.py +104 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_codex_runtime.py +130 -12
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_completion_mentions.py +15 -17
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_completion_topic_delivery.py +3 -3
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cron_chat_targets.py +10 -3
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_database_migrations.py +4 -4
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_defaults_cards.py +1 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_domain_experience.py +33 -11
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_lark_sdk_contract.py +19 -12
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_main.py +21 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_migration_schema_validation.py +1 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_model_settings.py +4 -37
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_optional_progress_card_settings_migration.py +6 -4
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_partial_answer_delivery.py +9 -9
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_persistent_fork_channel.py +26 -3
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_feishu_completion_mention.py +4 -4
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_feishu_partial_answers.py +3 -3
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_feishu_turn_file_card.py +4 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_scheduled_tasks.py +46 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_progress_card_settings_migration.py +3 -3
- netizen_cli-0.14.3/tests/test_schedule_auto_archive_migration.py +110 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_card_retry.py +3 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_cards.py +49 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_mcp.py +25 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_scope_matrix.py +2 -2
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_service.py +54 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_workspace_migration.py +2 -2
- netizen_cli-0.14.3/tests/test_scheduled_auto_archive.py +270 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduled_channel.py +9 -8
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduler_presenter.py +8 -8
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedules_storage.py +30 -1
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_sdk_gap_adapter.py +45 -8
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_collaboration_app.py +4 -3
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_defaults_migration.py +3 -3
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_tools.py +40 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_new.py +78 -21
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_service.py +11 -10
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_tools_integration.py +1 -1
- netizen_cli-0.14.1/netizen_cli/cards/model_info.py +0 -80
- netizen_cli-0.14.1/tests/test_model_info_cards.py +0 -125
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/MANIFEST.in +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/_build.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/config.example.yaml +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/__main__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/account_rate_limits.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/auth.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/credentials.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/errors.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/feishu.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/login.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/port_config.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/presentation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/queries.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/admin.css +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/chat-picker.css +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/chat-picker.js +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/identity.js +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/login.html +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/login.js +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/progress-card.js +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/session-access.js +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/transport.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/users.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/builtin_skills.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/callbacks.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/chat_target.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/fork.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/pagination.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/questions.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/reply.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/session_ids.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/status.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/usage.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/completion_mentions.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/input_preparation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/question_inputs.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/session_pages.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/chat_avatars.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/chat_targets.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_data.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_services.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_setup.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_update.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_update_restore.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_update_worker.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/completion_mention.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/defaults/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/defaults/models.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/defaults/service.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/defaults/store.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/deployment/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/deployment/restart_worker.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/deployment/update_executor.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/deployment/update_protocol.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/error_messages.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/feishu_app_onboarding.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/feishu_app_permissions.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/feishu_links.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/git_status.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/image_inputs.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/instance.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/lark_app.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/blocking_io.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/chat_directory.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/chat_labels.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/coordination.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/service.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/updates.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/markdown_images.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/mcp_transport.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/message_content.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/message_history.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/message_preparation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/message_projection.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/schema.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v14.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v15.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v16.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v17.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v18.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v19.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/package_resources.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/pagination.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/partial_answers.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/progress_card_config.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/progress_card_settings.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/projects.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/prompt_projection.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/quoted_context.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/result_images.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/runtime/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/runtime/contracts.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/runtime/name_writes.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/runtime/thread_naming.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/scheduler.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/service_launcher.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/session_inventory.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/session_settings.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/__init__.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/delegation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/errors.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/mcp.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/policy.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/schema.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/service.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/settings.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/skill_references.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/task_text.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/terminal_cleanup.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/thread_history.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/turn_activity.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/turn_files.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/turn_patch_children.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/turn_plan_observer.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/user_questions.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/errors.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/git.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/models.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/policy.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/settings.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/store.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/dependency_links.txt +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/entry_points.txt +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/requires.txt +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/top_level.txt +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/setup.cfg +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-lark/SKILL.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/SKILL.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/concepts.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/faq.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/feedback-and-files.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/fork-side-goal.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/messages.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/native-capabilities.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/session-collaboration.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/status-and-control.md +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_account_rate_limits.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_admin_update_executor_probe.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_binding_scheduled_channel.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_channel_input_preparation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_chat_avatars.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_chat_target_cards.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_check_sdk.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_integration.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_packaging.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_services.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_setup.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_update.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_codex_sdk_capabilities.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_codex_sdk_upgrade_0161.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_codex_sdk_upgrade_0163.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_completion_mention_cards.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_configuration_publication.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_defaults_channel.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_defaults_runtime.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_defaults_worktree_channel.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_deployment.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_documentation_links.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_error_messages.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_feishu_app_onboarding.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_feishu_app_permissions.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_file_pagination.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_fork_cards.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_git_status.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_goal_reply_separation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_image_inputs.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_lark_app.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_markdown_images.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_message_content.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_message_history.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_message_material_inputs.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_message_preparation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_name_writes.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_pagination.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_partial_answers.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_persistent_fork_bindings.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_persistent_fork_runtime.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_child_files.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_persistent_fork.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_python_sdk.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_sdk_completion_race.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_sdk_response_recovery.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_skill_execution.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_thread_naming.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_progress_card_config.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_progress_card_entrypoints.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_progress_card_presentation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_project_delete_channel.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_project_delete_runtime.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_project_deletion_storage.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_prompt_projection.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_question_cards.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_question_channel.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_question_target_parity.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_quoted_context.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_release_script.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_repository_hygiene.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_result_images.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_resume_channel.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_retired_deployment.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_runtime_resume_activation.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_runtime_thread_naming.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_run_now.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_workspace_cards.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_workspace_service.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_workspace_storage.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduled_runtime.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduled_worktrees.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduler.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_service_launcher.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_card_pages.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_defaults.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_external_runtime.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_id_cards.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_policy.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_settings.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_settings_projects.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_status_cards.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_status_commands.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_task_text.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_terminal_cleanup.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_thread_history.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_thread_naming.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_activity.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_files.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_patch_children.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_patch_statistics.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_plan_observer.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_update_executor.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_usage_card.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_usage_control.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_user_guide_skill.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_user_questions.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_channel_cwd.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_git.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_runtime_cwd.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_settings.py +0 -0
- {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_storage.py +0 -0
|
@@ -103,7 +103,7 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
|
|
|
103
103
|
### 3. 在飞书开始第一次对话
|
|
104
104
|
|
|
105
105
|
1. 打开机器人单聊;如需在群里使用,先将机器人加入目标群。
|
|
106
|
-
2. 发送 `/
|
|
106
|
+
2. 发送 `/projects`,创建或登记一个项目并启用。项目登记一个默认工作目录。
|
|
107
107
|
3. 发送 `/new`,在卡片中选择项目。模型配置可选择“继承 Codex”;过程卡默认开启,可按需关闭。
|
|
108
108
|
4. 直接发送任务,例如:**“梳理这个项目的结构,并生成一份入门说明。”**
|
|
109
109
|
5. 收到回复后继续交流;如果出现“本轮文件”,点击“发送”取回需要的文件。
|
|
@@ -130,7 +130,7 @@ Git 项目可在 `/new` 中加载 worktree 选项,选择已有受管 worktree
|
|
|
130
130
|
|
|
131
131
|
| 想做什么 | 入口 |
|
|
132
132
|
| --- | --- |
|
|
133
|
-
| 创建或登记项目工作目录 | `/
|
|
133
|
+
| 创建或登记项目工作目录 | `/projects` |
|
|
134
134
|
| 新建会话,选择项目及受管 worktree | `/new` |
|
|
135
135
|
| 查看、保存或删除当前聊天的会话默认配置 | `/defaults` |
|
|
136
136
|
| 查看并切换已有会话 | `/sessions` |
|
|
@@ -2616,13 +2616,6 @@ function renderWorkspacePolicy(input, editor, { project, loading = false, schedu
|
|
|
2616
2616
|
input("worktree").required = workspace.mode === "existing";
|
|
2617
2617
|
input("workspace-ref").value = workspace.ref || "";
|
|
2618
2618
|
for (const field of ["workspace", "worktree", "workspace-ref"]) input(field).disabled = loading;
|
|
2619
|
-
input("workspace-refs").replaceChildren();
|
|
2620
|
-
for (const branch of options?.branches || []) {
|
|
2621
|
-
const option = document.createElement("option");
|
|
2622
|
-
option.value = branch.ref;
|
|
2623
|
-
option.textContent = branch.name;
|
|
2624
|
-
input("workspace-refs").append(option);
|
|
2625
|
-
}
|
|
2626
2619
|
const notes = [];
|
|
2627
2620
|
if (loading) notes.push("正在读取 worktree 选项…");
|
|
2628
2621
|
if (editor.workspaceError) notes.push(workspaceErrorMessage(editor.workspaceError));
|
|
@@ -3392,6 +3385,8 @@ function renderScheduleTarget() {
|
|
|
3392
3385
|
scheduleInput("chat-target").hidden = binding;
|
|
3393
3386
|
if (binding) { scheduleInput("chat").required = false; chatTarget("schedule").close(); }
|
|
3394
3387
|
scheduleInput("session-settings").hidden = binding;
|
|
3388
|
+
scheduleInput("auto-archive-field").hidden = binding;
|
|
3389
|
+
scheduleInput("auto-archive").disabled = binding;
|
|
3395
3390
|
renderScheduleWorkspace();
|
|
3396
3391
|
const target = editor.targetSession;
|
|
3397
3392
|
const selected = editor.plan?.target_binding_id || target?.bindingId || "";
|
|
@@ -3445,6 +3440,7 @@ function scheduleEditorValues() {
|
|
|
3445
3440
|
.filter((input) => input.checked).map((input) => input.value);
|
|
3446
3441
|
return { name: scheduleInput("name").value, instructions: scheduleInput("instructions").value,
|
|
3447
3442
|
enabled: scheduleInput("enabled").checked, targetKind: scheduleTargetKind(),
|
|
3443
|
+
autoArchive: binding ? null : scheduleInput("auto-archive").checked,
|
|
3448
3444
|
binding: binding ? scheduleInput("binding").value : "",
|
|
3449
3445
|
project: binding ? "" : scheduleInput("project").value,
|
|
3450
3446
|
chat: binding ? "" : scheduleInput("chat").value, rule,
|
|
@@ -3458,7 +3454,7 @@ function scheduleEditorHasChanges() {
|
|
|
3458
3454
|
}
|
|
3459
3455
|
|
|
3460
3456
|
function captureScheduleDraft() {
|
|
3461
|
-
const fields = ["name", "instructions", "enabled", "kind", "timezone", "time", "at", "offset", "end-at", "end-offset", "every"];
|
|
3457
|
+
const fields = ["name", "instructions", "enabled", "auto-archive", "kind", "timezone", "time", "at", "offset", "end-at", "end-offset", "every"];
|
|
3462
3458
|
return {
|
|
3463
3459
|
discardBase: structuredClone(scheduleEditor.discardBase), dirty: scheduleEditorHasChanges(),
|
|
3464
3460
|
fields: fields.map((id) => ({ id, value: scheduleInput(id).value, checked: scheduleInput(id).checked })),
|
|
@@ -3733,8 +3729,8 @@ function renderScheduleWorkspace() {
|
|
|
3733
3729
|
}
|
|
3734
3730
|
|
|
3735
3731
|
function updateScheduleSaveAvailability() {
|
|
3736
|
-
scheduleInput("save").disabled =
|
|
3737
|
-
||
|
|
3732
|
+
scheduleInput("save").disabled = scheduleInput("fields").disabled || !scheduleEditor?.action
|
|
3733
|
+
|| !scheduleWorkspaceCanSave();
|
|
3738
3734
|
}
|
|
3739
3735
|
|
|
3740
3736
|
async function changeScheduleProject() {
|
|
@@ -3852,9 +3848,9 @@ function readScheduleRule() {
|
|
|
3852
3848
|
function invalidateSchedulePreview() {
|
|
3853
3849
|
schedulePrepared = null;
|
|
3854
3850
|
schedulePreviewSerial += 1;
|
|
3855
|
-
|
|
3851
|
+
updateScheduleSaveAvailability();
|
|
3856
3852
|
scheduleInput("preview-times").replaceChildren();
|
|
3857
|
-
scheduleInput("preview-message").textContent = "
|
|
3853
|
+
scheduleInput("preview-message").textContent = "可预览触发时间,也可直接保存。";
|
|
3858
3854
|
scheduleInput("preview-message").classList.toggle("error", false);
|
|
3859
3855
|
for (const node of document.querySelectorAll("[data-schedule-rule]")) {
|
|
3860
3856
|
node.hidden = !node.dataset.scheduleRule.split(" ").includes(scheduleInput("kind").value);
|
|
@@ -3879,9 +3875,12 @@ function renderSchedulePreview(selector, preview) {
|
|
|
3879
3875
|
|
|
3880
3876
|
async function previewSchedule() {
|
|
3881
3877
|
if (scheduleInput("fields").disabled || !scheduleEditor) return;
|
|
3878
|
+
return prepareSchedule();
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
async function prepareSchedule() {
|
|
3882
3882
|
const serial = ++schedulePreviewSerial;
|
|
3883
3883
|
schedulePrepared = null;
|
|
3884
|
-
scheduleInput("save").disabled = true;
|
|
3885
3884
|
if (!validateScheduleFields()) return;
|
|
3886
3885
|
scheduleInput("preview-message").textContent = "正在计算触发时间…";
|
|
3887
3886
|
scheduleInput("preview-message").classList.toggle("error", false);
|
|
@@ -3918,6 +3917,7 @@ async function previewSchedule() {
|
|
|
3918
3917
|
? `预计触发时间 · ${data.schedule.timezone}`
|
|
3919
3918
|
: scheduleEditor?.plan ? "没有后续触发时间。仍可保存计划修改;已认领的执行可以继续。" : "没有后续触发时间,请调整规则。";
|
|
3920
3919
|
updateScheduleSaveAvailability();
|
|
3920
|
+
return schedulePrepared;
|
|
3921
3921
|
} catch (error) {
|
|
3922
3922
|
if (serial !== schedulePreviewSerial) return;
|
|
3923
3923
|
if (["ambiguous_local_time", "ambiguous_end_time"].includes(error.code) && Array.isArray(error.choices)) {
|
|
@@ -3963,6 +3963,7 @@ function openScheduleEditor(plan = null, source = {}) {
|
|
|
3963
3963
|
chatTarget("schedule", changeScheduleChat).set({ chatId: plan?.chat_id, chat: plan?.chat });
|
|
3964
3964
|
scheduleInput("instructions").value = plan?.instructions || "";
|
|
3965
3965
|
scheduleInput("enabled").checked = plan?.enabled ?? true;
|
|
3966
|
+
scheduleInput("auto-archive").checked = plan?.auto_archive ?? false;
|
|
3966
3967
|
scheduleInput("timezone").value = rule.timezone;
|
|
3967
3968
|
scheduleInput("kind").value = rule.kind;
|
|
3968
3969
|
scheduleInput("time").value = ["daily", "weekly"].includes(rule.kind) ? rule.at : "09:00";
|
|
@@ -4001,33 +4002,45 @@ async function editSchedule(plan) {
|
|
|
4001
4002
|
|
|
4002
4003
|
async function saveSchedule(event) {
|
|
4003
4004
|
event.preventDefault();
|
|
4004
|
-
if (scheduleInput("fields").disabled || !scheduleEditor?.action
|
|
4005
|
-
|| schedulePrepared.fingerprint !== scheduleRuleFingerprint()) return;
|
|
4005
|
+
if (scheduleInput("fields").disabled || !scheduleEditor?.action) return;
|
|
4006
4006
|
if (!validateScheduleFields({ saving: true })) return;
|
|
4007
4007
|
const editor = scheduleEditor;
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
scheduleInput("
|
|
4019
|
-
|
|
4008
|
+
let prepared = schedulePrepared;
|
|
4009
|
+
let validationError = null;
|
|
4010
|
+
scheduleInput("fields").disabled = true;
|
|
4011
|
+
scheduleInput("close").disabled = true;
|
|
4012
|
+
updateScheduleSaveAvailability();
|
|
4013
|
+
try {
|
|
4014
|
+
// Resolve local times and validate the current draft without requiring a
|
|
4015
|
+
// separate preview click. The same read preserves DST and interval anchors.
|
|
4016
|
+
if (!prepared || prepared.fingerprint !== scheduleRuleFingerprint()) prepared = await prepareSchedule();
|
|
4017
|
+
if (!prepared) {
|
|
4018
|
+
if (scheduleInput("form-error").hidden) showScheduleFormError("配置已更新,请检查后重新保存。");
|
|
4019
|
+
return;
|
|
4020
4020
|
}
|
|
4021
|
-
if (
|
|
4022
|
-
|
|
4021
|
+
if (!prepared.hasFuture && !editor.plan) {
|
|
4022
|
+
showScheduleFormError("没有后续触发时间,请调整规则后保存。");
|
|
4023
|
+
return;
|
|
4024
|
+
}
|
|
4025
|
+
if (scheduleTargetKind() !== "binding") await chatTarget("schedule").validate();
|
|
4026
|
+
} catch (error) {
|
|
4027
|
+
invalidateSchedulePreview();
|
|
4028
|
+
validationError = error;
|
|
4029
|
+
} finally {
|
|
4030
|
+
scheduleInput("fields").disabled = false;
|
|
4031
|
+
scheduleInput("close").disabled = false;
|
|
4032
|
+
updateScheduleSaveAvailability();
|
|
4033
|
+
if (scheduleFieldErrors.size) focusScheduleError();
|
|
4023
4034
|
}
|
|
4035
|
+
if (scheduleEditor !== editor) return;
|
|
4036
|
+
if (validationError) { showScheduleRequestError(validationError); return; }
|
|
4024
4037
|
// A late options response may replace defaults and invalidate the preview
|
|
4025
4038
|
// while group validation is in flight. Never submit that stale confirmation.
|
|
4026
4039
|
if (schedulePrepared !== prepared || prepared.fingerprint !== scheduleRuleFingerprint()) {
|
|
4027
4040
|
invalidateSchedulePreview();
|
|
4028
|
-
scheduleInput("preview-message").textContent = "
|
|
4041
|
+
scheduleInput("preview-message").textContent = "配置已更新,请检查后重新保存。";
|
|
4029
4042
|
scheduleInput("preview-message").classList.toggle("error", true);
|
|
4030
|
-
showScheduleFormError("
|
|
4043
|
+
showScheduleFormError("配置已更新,请检查后重新保存。");
|
|
4031
4044
|
return;
|
|
4032
4045
|
}
|
|
4033
4046
|
if (!scheduleWorkspaceCanSave()) {
|
|
@@ -4051,6 +4064,7 @@ async function saveSchedule(event) {
|
|
|
4051
4064
|
} else {
|
|
4052
4065
|
definition.project = scheduleInput("project").value.trim();
|
|
4053
4066
|
definition.chat_id = scheduleInput("chat").value.trim();
|
|
4067
|
+
definition.auto_archive = scheduleInput("auto-archive").checked;
|
|
4054
4068
|
if (scheduleWorkspaceChanged()) {
|
|
4055
4069
|
definition.workspace = structuredClone(editor.workspace);
|
|
4056
4070
|
if (editor.workspaceOptions?.projectAlias === definition.project) {
|
|
@@ -4324,6 +4338,9 @@ async function showSchedule(planId, returnFocus = document.activeElement) {
|
|
|
4324
4338
|
: scheduleSessionSummary(data.plan.session_settings || defaultScheduleSessionSettings());
|
|
4325
4339
|
scheduleInput("detail-settings").textContent += ` · ${data.plan.target_kind === "binding"
|
|
4326
4340
|
? scheduleInheritedWorkspace(data.plan) : workspacePolicySummary(data.plan.workspace, { scheduled: true })}`;
|
|
4341
|
+
if (data.plan.target_kind !== "binding") {
|
|
4342
|
+
scheduleInput("detail-settings").textContent += ` · 成功后自动归档:${data.plan.auto_archive ? "开启" : "关闭"}`;
|
|
4343
|
+
}
|
|
4327
4344
|
scheduleInput("detail-rule").textContent = scheduleRuleLabel(data.plan.schedule);
|
|
4328
4345
|
renderSchedulePreview("#schedule-detail-preview", data.preview);
|
|
4329
4346
|
scheduleInput("detail-note").textContent = "";
|
|
@@ -94,8 +94,8 @@
|
|
|
94
94
|
<div class="workspace-fields">
|
|
95
95
|
<label>Project<select id="worktree-project" required><option value="">选择 Project</option></select></label>
|
|
96
96
|
<label>检出方式<select id="worktree-mode"><option value="detached">Detached(不绑定命名分支)</option><option value="existing">检出已有本地分支</option><option value="new">创建并检出新分支</option></select></label>
|
|
97
|
-
<label id="worktree-ref-label">起点提交 / 分支<input id="worktree-ref"
|
|
98
|
-
<label id="worktree-existing-label" hidden>本地分支<
|
|
97
|
+
<label id="worktree-ref-label">起点提交 / 分支<input id="worktree-ref" maxlength="256" placeholder="HEAD、分支、标签或提交"></label>
|
|
98
|
+
<label id="worktree-existing-label" hidden>本地分支<input id="worktree-existing" maxlength="256" placeholder="填写本地分支名"></label>
|
|
99
99
|
<label id="worktree-new-label" hidden>新分支名称<input id="worktree-new-branch" maxlength="1024"></label>
|
|
100
100
|
</div>
|
|
101
101
|
<p id="worktree-create-note" class="hint">只检出已提交内容,不复制未提交、未跟踪或忽略文件,不执行环境初始化。不创建会话。</p>
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
<label>Project<select id="defaults-project" required><option value="">选择 Project</option></select></label>
|
|
292
292
|
<label>worktree 使用方式<select id="defaults-workspace"><option value="project">项目目录(不使用 worktree)</option><option value="existing">使用已有 worktree</option><option value="new">每个新会话新建 worktree</option></select></label>
|
|
293
293
|
<label id="defaults-worktree-field" hidden>受管 worktree<select id="defaults-worktree"></select></label>
|
|
294
|
-
<label id="defaults-workspace-ref-field" hidden>起点提交 / 分支<input id="defaults-workspace-ref"
|
|
294
|
+
<label id="defaults-workspace-ref-field" hidden>起点提交 / 分支<input id="defaults-workspace-ref" maxlength="256" placeholder="留空使用创建时 Project 的 HEAD"></label>
|
|
295
295
|
</div>
|
|
296
296
|
<p id="defaults-workspace-help" class="defaults-field-help" role="status"></p>
|
|
297
297
|
<div class="schedule-form-grid">
|
|
@@ -417,7 +417,7 @@
|
|
|
417
417
|
<div id="schedule-workspace-fields" class="schedule-section-fields">
|
|
418
418
|
<label>worktree 使用方式<select id="schedule-workspace"><option value="project">项目目录(不使用 worktree)</option><option value="existing">使用已有 worktree</option><option value="new">每次执行新建 worktree</option></select></label>
|
|
419
419
|
<label id="schedule-worktree-field" hidden>受管 worktree<select id="schedule-worktree"></select></label>
|
|
420
|
-
<label id="schedule-workspace-ref-field" hidden>起点提交 / 分支<input id="schedule-workspace-ref"
|
|
420
|
+
<label id="schedule-workspace-ref-field" hidden>起点提交 / 分支<input id="schedule-workspace-ref" maxlength="256" placeholder="留空使用执行时 Project 的 HEAD"></label>
|
|
421
421
|
<p id="schedule-workspace-help" class="schedule-field-help" role="status"></p>
|
|
422
422
|
</div>
|
|
423
423
|
<div id="schedule-chat-target" class="chat-target">
|
|
@@ -461,11 +461,15 @@
|
|
|
461
461
|
</fieldset>
|
|
462
462
|
<div class="schedule-preview-panel">
|
|
463
463
|
<button id="schedule-preview" type="button">预览触发时间</button>
|
|
464
|
-
<p id="schedule-preview-message" role="status" aria-live="polite"
|
|
464
|
+
<p id="schedule-preview-message" role="status" aria-live="polite">可预览触发时间,也可直接保存。</p>
|
|
465
465
|
<ol id="schedule-preview-times"></ol>
|
|
466
466
|
</div>
|
|
467
467
|
</div>
|
|
468
468
|
</section>
|
|
469
|
+
<section id="schedule-auto-archive-field" class="schedule-editor-section">
|
|
470
|
+
<label class="schedule-checkbox"><input id="schedule-auto-archive" type="checkbox" aria-describedby="schedule-auto-archive-help">成功后自动归档</label>
|
|
471
|
+
<p id="schedule-auto-archive-help" class="schedule-field-help">首轮正常结束且结果送达后归档,恢复使用 /sessions archived。</p>
|
|
472
|
+
</section>
|
|
469
473
|
<details id="schedule-session-settings" class="schedule-editor-section schedule-session-settings" open>
|
|
470
474
|
<summary>会话配置<span id="schedule-session-summary">继承 Codex · 当前消息</span></summary>
|
|
471
475
|
<div class="schedule-section-fields">
|
|
@@ -6,10 +6,10 @@ window.NetizenWorktrees = (() => {
|
|
|
6
6
|
const node = (name) => document.querySelector(`#worktree-${name}`);
|
|
7
7
|
let dependencies;
|
|
8
8
|
let settings = null;
|
|
9
|
-
let
|
|
9
|
+
let creationOptions = null;
|
|
10
10
|
let pending = false;
|
|
11
11
|
let serial = 0;
|
|
12
|
-
let
|
|
12
|
+
let creationSerial = 0;
|
|
13
13
|
let editor = null;
|
|
14
14
|
let cursor = null;
|
|
15
15
|
let nextCursor = null;
|
|
@@ -59,7 +59,7 @@ window.NetizenWorktrees = (() => {
|
|
|
59
59
|
node("new-branch").required = mode === "new";
|
|
60
60
|
node("existing").required = mode === "existing";
|
|
61
61
|
node("create-submit").disabled = disabled || editor?.kind !== "create" || editor.loading || editor.checking
|
|
62
|
-
|| editor.uncertain || !
|
|
62
|
+
|| editor.uncertain || !creationOptions?.actions?.create || (mode === "existing" && !node("existing").value.trim());
|
|
63
63
|
node("create-submit").textContent = pending && editor?.kind === "create" ? "正在创建…" : "创建 worktree";
|
|
64
64
|
node("new").disabled = disabled;
|
|
65
65
|
node("configure").disabled = disabled;
|
|
@@ -73,8 +73,8 @@ window.NetizenWorktrees = (() => {
|
|
|
73
73
|
if (!saved && !confirmed && dirty() && !window.confirm("有未保存的修改,确定放弃吗?")) return false;
|
|
74
74
|
const current = editor;
|
|
75
75
|
editor = null;
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
creationSerial += 1;
|
|
77
|
+
creationOptions = null;
|
|
78
78
|
settings = null;
|
|
79
79
|
window.NetizenSelect?.close();
|
|
80
80
|
node(current.kind).hidden = true;
|
|
@@ -89,14 +89,14 @@ window.NetizenWorktrees = (() => {
|
|
|
89
89
|
const current = { kind, returnFocus, loading: true, checking: false, uncertain: false, checkAvailable: false, base: null };
|
|
90
90
|
editor = current;
|
|
91
91
|
settings = null;
|
|
92
|
-
|
|
92
|
+
creationOptions = null;
|
|
93
93
|
if (kind === "settings") {
|
|
94
94
|
node("root").value = ""; node("fetch").checked = false;
|
|
95
95
|
node("effective-root").textContent = "";
|
|
96
96
|
} else {
|
|
97
97
|
node("project").replaceChildren(); option(node("project"), "", "选择 Project"); node("project").value = "";
|
|
98
98
|
node("mode").value = "detached"; node("ref").value = ""; node("new-branch").value = "";
|
|
99
|
-
node("existing").
|
|
99
|
+
node("existing").value = "";
|
|
100
100
|
node("create-note").textContent = ""; node("create-config").textContent = "";
|
|
101
101
|
}
|
|
102
102
|
current.base = values(kind);
|
|
@@ -149,32 +149,25 @@ window.NetizenWorktrees = (() => {
|
|
|
149
149
|
return dependencies.api(path, { method: "POST", headers: { "Content-Type": "application/json" },
|
|
150
150
|
body: JSON.stringify(dependencies.actionPayload(envelope, extra)) });
|
|
151
151
|
}
|
|
152
|
-
async function
|
|
152
|
+
async function loadCreationOptions() {
|
|
153
153
|
const current = editor;
|
|
154
154
|
if (current?.kind !== "create" || current.loading || pending || expired()) return;
|
|
155
|
-
const request = ++
|
|
155
|
+
const request = ++creationSerial;
|
|
156
156
|
const project = node("project").value;
|
|
157
|
-
|
|
158
|
-
node("existing").
|
|
159
|
-
node("refs").replaceChildren();
|
|
157
|
+
creationOptions = null;
|
|
158
|
+
node("existing").value = "";
|
|
160
159
|
controls();
|
|
161
160
|
if (!project || expired()) return;
|
|
162
161
|
try {
|
|
163
|
-
const result = await dependencies.api(`/api/v1/worktrees/
|
|
164
|
-
if (expired() || editor !== current || request !==
|
|
165
|
-
|
|
166
|
-
option(node("existing"), "", "选择未被占用的本地分支");
|
|
167
|
-
for (const branch of result.items) {
|
|
168
|
-
option(node("refs"), branch.ref, branch.name);
|
|
169
|
-
if (!branch.remote) option(node("existing"), branch.name,
|
|
170
|
-
`${branch.name}${branch.occupiedPaths?.length ? "(已被其他 checkout 占用)" : ""}`, Boolean(branch.occupiedPaths?.length));
|
|
171
|
-
}
|
|
162
|
+
const result = await dependencies.api(`/api/v1/worktrees/create-options?${new URLSearchParams({ project })}`);
|
|
163
|
+
if (expired() || editor !== current || request !== creationSerial || node("project").value !== project) return;
|
|
164
|
+
creationOptions = result;
|
|
172
165
|
node("create-note").textContent = result.isGit === false ? "该 Project 不是 Git 仓库,不能创建 worktree。"
|
|
173
166
|
: "默认从此 Project 当前 HEAD 检出;不复制未提交、未跟踪或忽略文件,不执行环境初始化。不创建会话。";
|
|
174
167
|
} catch (error) {
|
|
175
|
-
if (editor === current && request ===
|
|
168
|
+
if (editor === current && request === creationSerial && !expired()) node("create-note").textContent = error.message;
|
|
176
169
|
} finally {
|
|
177
|
-
if (request ===
|
|
170
|
+
if (request === creationSerial) controls();
|
|
178
171
|
}
|
|
179
172
|
}
|
|
180
173
|
function renderRows(items) {
|
|
@@ -273,11 +266,11 @@ window.NetizenWorktrees = (() => {
|
|
|
273
266
|
event.preventDefault();
|
|
274
267
|
const current = editor;
|
|
275
268
|
if (expired() || pending || current?.kind !== "create" || current.loading || current.checking
|
|
276
|
-
|| current.uncertain || !
|
|
269
|
+
|| current.uncertain || !creationOptions?.actions?.create) return;
|
|
277
270
|
const mode = node("mode").value;
|
|
278
|
-
const branch = mode === "existing" ? node("existing").value : mode === "new" ? node("new-branch").value.trim() : null;
|
|
271
|
+
const branch = mode === "existing" ? node("existing").value.trim() : mode === "new" ? node("new-branch").value.trim() : null;
|
|
279
272
|
const ref = mode === "existing" ? `refs/heads/${branch}` : node("ref").value.trim() || "HEAD";
|
|
280
|
-
const grant =
|
|
273
|
+
const grant = creationOptions.actions.create; creationOptions.actions.create = null;
|
|
281
274
|
pending = true; controls(); editorMessage("正在创建 worktree…");
|
|
282
275
|
let result;
|
|
283
276
|
try {
|
|
@@ -339,7 +332,7 @@ window.NetizenWorktrees = (() => {
|
|
|
339
332
|
dependencies = values;
|
|
340
333
|
node("new").addEventListener("click", (event) => openEditor("create", event.currentTarget));
|
|
341
334
|
node("configure").addEventListener("click", (event) => openEditor("settings", event.currentTarget));
|
|
342
|
-
node("project").addEventListener("change",
|
|
335
|
+
node("project").addEventListener("change", loadCreationOptions);
|
|
343
336
|
for (const name of ["mode", "root", "fetch", "ref", "existing", "new-branch"]) {
|
|
344
337
|
node(name).addEventListener(name === "mode" ? "change" : "input", controls);
|
|
345
338
|
}
|
|
@@ -361,5 +354,5 @@ window.NetizenWorktrees = (() => {
|
|
|
361
354
|
controls();
|
|
362
355
|
}
|
|
363
356
|
return { init, load, dirty, busy: () => pending, discard,
|
|
364
|
-
expire() { serial += 1;
|
|
357
|
+
expire() { serial += 1; creationSerial += 1; controls(); } };
|
|
365
358
|
})();
|
|
@@ -609,8 +609,8 @@ class AdminWebApplication:
|
|
|
609
609
|
return await self._projects(context)
|
|
610
610
|
if route == ("GET", "/api/v1/worktrees"):
|
|
611
611
|
return await self._worktrees(context)
|
|
612
|
-
if route == ("GET", "/api/v1/worktrees/
|
|
613
|
-
return await self.
|
|
612
|
+
if route == ("GET", "/api/v1/worktrees/create-options"):
|
|
613
|
+
return await self._worktree_creation_options(context)
|
|
614
614
|
if route == ("GET", "/api/v1/worktrees/settings"):
|
|
615
615
|
return self._worktree_settings(context)
|
|
616
616
|
if route == ("GET", "/api/v1/progress-card/settings"):
|
|
@@ -986,7 +986,7 @@ class AdminWebApplication:
|
|
|
986
986
|
)
|
|
987
987
|
definition = payload.get("definition", {})
|
|
988
988
|
if not isinstance(definition, dict) or set(definition) - {
|
|
989
|
-
"name", "instructions", "project", "expected_project_revision", "chat_id", "schedule", "enabled", "session_settings", "workspace", "target_kind", "target_binding_id",
|
|
989
|
+
"name", "instructions", "project", "expected_project_revision", "chat_id", "schedule", "enabled", "session_settings", "workspace", "target_kind", "target_binding_id", "auto_archive",
|
|
990
990
|
}:
|
|
991
991
|
raise AdminWebError(400, "invalid_input", "定时任务定义字段无效。")
|
|
992
992
|
request: dict[str, object] = {
|
|
@@ -1054,14 +1054,14 @@ class AdminWebApplication:
|
|
|
1054
1054
|
)}} for item in result["items"]]
|
|
1055
1055
|
return _json_response(200, {**result, "items": items, "requestId": context.request.request_id})
|
|
1056
1056
|
|
|
1057
|
-
async def
|
|
1057
|
+
async def _worktree_creation_options(self, context: _RequestContext) -> Response:
|
|
1058
1058
|
_require_query_keys(context.query, {"project"})
|
|
1059
1059
|
alias = _optional_text_query(context.query, "project", maximum=64)
|
|
1060
1060
|
if not alias:
|
|
1061
1061
|
raise AdminWebError(400, "invalid_input", "请选择 Project。")
|
|
1062
|
-
result = await self._worktree_service().
|
|
1062
|
+
result = await self._worktree_service().creation_options(project_alias=alias)
|
|
1063
1063
|
actions = {}
|
|
1064
|
-
if result
|
|
1064
|
+
if result["isGit"]:
|
|
1065
1065
|
actions["create"] = self._grant(
|
|
1066
1066
|
context, "worktrees.create", AdminActionTarget("project", alias),
|
|
1067
1067
|
_empty_preconditions(project_revision=ExpectedValue.expect(result["projectRevision"])),
|
|
@@ -40,7 +40,7 @@ from .worktrees.models import WorktreeRecord
|
|
|
40
40
|
from .worktrees.store import WorktreeStore, create_schema as create_worktree_schema
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
SCHEMA_VERSION =
|
|
43
|
+
SCHEMA_VERSION = 20
|
|
44
44
|
|
|
45
45
|
# Includes the running query and all submitted queries waiting for the reader.
|
|
46
46
|
_ADMIN_QUERY_CAPACITY = 32
|
|
@@ -467,7 +467,7 @@ def _require_current_schema(connection: sqlite3.Connection) -> None:
|
|
|
467
467
|
_require_project_metadata_schema(connection)
|
|
468
468
|
require_schema(connection)
|
|
469
469
|
require_defaults_schema(connection)
|
|
470
|
-
from .migrations.
|
|
470
|
+
from .migrations.v20 import validate
|
|
471
471
|
validate(connection)
|
|
472
472
|
|
|
473
473
|
|
|
@@ -563,23 +563,29 @@ class BindingStore:
|
|
|
563
563
|
A file-backed Store owns a WAL writer plus one query-only Admin reader.
|
|
564
564
|
``:memory:`` is the explicit compatibility/test mode: Admin reads run on
|
|
565
565
|
the same connection under the writer lock, never against a second empty
|
|
566
|
-
in-memory database.
|
|
566
|
+
in-memory database. Service startup uses ``create=False`` so validation and
|
|
567
|
+
opening share the requirement for an existing, nonempty database.
|
|
567
568
|
"""
|
|
568
569
|
|
|
569
570
|
def __init__(
|
|
570
571
|
self,
|
|
571
572
|
path: str | Path = ":memory:",
|
|
572
573
|
*,
|
|
574
|
+
create: bool = True,
|
|
573
575
|
id_factory: Callable[[], str] | None = None,
|
|
574
576
|
wall_clock: Callable[[], float] = time.time,
|
|
575
577
|
) -> None:
|
|
576
578
|
self._path = path
|
|
577
579
|
self._path_string = str(path)
|
|
578
580
|
self._is_memory = self._path_string == ":memory:"
|
|
579
|
-
if not self._is_memory and Path(path).exists():
|
|
581
|
+
if not create or (not self._is_memory and Path(path).exists()):
|
|
580
582
|
# Reject unsupported data before a writer can recover/checkpoint a
|
|
581
583
|
# previous process's WAL, even while closing after an open failure.
|
|
582
|
-
validate_channel_database(path, allow_empty=
|
|
584
|
+
validate_channel_database(path, allow_empty=create)
|
|
585
|
+
self._connection_target = (
|
|
586
|
+
self._path_string if create else Path(path).resolve().as_uri() + "?mode=rw"
|
|
587
|
+
)
|
|
588
|
+
self._connection_uri = not create
|
|
583
589
|
self._id_factory = id_factory or (lambda: str(uuid.uuid4()))
|
|
584
590
|
self._wall_clock = wall_clock
|
|
585
591
|
self._lock = threading.RLock()
|
|
@@ -593,11 +599,12 @@ class BindingStore:
|
|
|
593
599
|
max_workers=1,
|
|
594
600
|
thread_name_prefix="netizen-binding-query",
|
|
595
601
|
)
|
|
596
|
-
if not self._is_memory:
|
|
602
|
+
if create and not self._is_memory:
|
|
597
603
|
db_path = Path(path)
|
|
598
604
|
db_path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
599
605
|
self._connection = sqlite3.connect(
|
|
600
|
-
self.
|
|
606
|
+
self._connection_target,
|
|
607
|
+
uri=self._connection_uri,
|
|
601
608
|
isolation_level=None,
|
|
602
609
|
check_same_thread=False,
|
|
603
610
|
)
|
|
@@ -606,7 +613,7 @@ class BindingStore:
|
|
|
606
613
|
self.defaults = DefaultsStore(self)
|
|
607
614
|
self.worktrees = WorktreeStore(self)
|
|
608
615
|
try:
|
|
609
|
-
self._initialize()
|
|
616
|
+
self._initialize(create=create)
|
|
610
617
|
if not self._is_memory:
|
|
611
618
|
self._query_connection = self._query_executor.submit(
|
|
612
619
|
self._open_query_connection
|
|
@@ -618,14 +625,14 @@ class BindingStore:
|
|
|
618
625
|
if not self._is_memory:
|
|
619
626
|
os.chmod(Path(path), 0o600)
|
|
620
627
|
|
|
621
|
-
def _initialize(self) -> None:
|
|
628
|
+
def _initialize(self, *, create: bool) -> None:
|
|
622
629
|
with self._lock:
|
|
623
630
|
tables = {
|
|
624
631
|
row["name"] for row in self._connection.execute(
|
|
625
632
|
"SELECT name FROM sqlite_master WHERE type = 'table'"
|
|
626
633
|
)
|
|
627
634
|
}
|
|
628
|
-
if tables:
|
|
635
|
+
if tables or not create:
|
|
629
636
|
if "schema_version" not in tables:
|
|
630
637
|
raise RuntimeError("Channel database is missing its schema version; existing data was not changed")
|
|
631
638
|
_require_current_schema(self._connection)
|
|
@@ -862,7 +869,8 @@ class BindingStore:
|
|
|
862
869
|
|
|
863
870
|
def _open_query_connection(self) -> sqlite3.Connection:
|
|
864
871
|
connection = sqlite3.connect(
|
|
865
|
-
self.
|
|
872
|
+
self._connection_target,
|
|
873
|
+
uri=self._connection_uri,
|
|
866
874
|
isolation_level=None,
|
|
867
875
|
check_same_thread=False,
|
|
868
876
|
)
|
|
@@ -79,7 +79,6 @@ from .callbacks import (
|
|
|
79
79
|
_turn_reference,
|
|
80
80
|
)
|
|
81
81
|
from .reply import reply_card
|
|
82
|
-
from .model_info import with_model_details
|
|
83
82
|
from .session_ids import session_identity_lines
|
|
84
83
|
|
|
85
84
|
|
|
@@ -139,7 +138,7 @@ def settings_card(
|
|
|
139
138
|
notice: str | None = None,
|
|
140
139
|
notice_is_error: bool = False,
|
|
141
140
|
) -> OutboundCard:
|
|
142
|
-
builder = _builder("Netizen
|
|
141
|
+
builder = _builder("Netizen 项目管理", _settings_section_label(section))
|
|
143
142
|
builder.raw(_settings_navigation(scope=scope, active=section))
|
|
144
143
|
builder.markdown(
|
|
145
144
|
"当前聊天的参与者均可管理项目;修改会影响使用这个机器人的其他聊天。"
|
|
@@ -180,7 +179,7 @@ def _render_projects_settings(
|
|
|
180
179
|
|
|
181
180
|
builder.raw(
|
|
182
181
|
_repeatable_callback_button(
|
|
183
|
-
label="
|
|
182
|
+
label="刷新项目列表",
|
|
184
183
|
value=_envelope(
|
|
185
184
|
scope,
|
|
186
185
|
CardControlName.REFRESH_SETTINGS,
|
|
@@ -546,11 +545,6 @@ def _new_workspace_elements(options: Mapping[str, Any]) -> list[dict[str, Any]]:
|
|
|
546
545
|
f"已有 worktree · {branch} · {item['cwd']}",
|
|
547
546
|
_workspace_reference(options, "id:" + item["id"]),
|
|
548
547
|
))
|
|
549
|
-
refs = [("当前 HEAD", "HEAD")]
|
|
550
|
-
for branch in options.get("branches", ()):
|
|
551
|
-
occupied = " · 已占用,仅可作起点" if branch.get("occupiedPaths") else ""
|
|
552
|
-
remote = "远端 · " if branch.get("remote") else "本地 · "
|
|
553
|
-
refs.append((remote + branch["name"] + occupied, branch["ref"]))
|
|
554
548
|
return [
|
|
555
549
|
_form_label("worktree(切换项目后请重新加载选项)"),
|
|
556
550
|
_static_select(
|
|
@@ -565,11 +559,9 @@ def _new_workspace_elements(options: Mapping[str, Any]) -> list[dict[str, Any]]:
|
|
|
565
559
|
("创建并检出新分支", "new")),
|
|
566
560
|
initial_option="detached",
|
|
567
561
|
),
|
|
568
|
-
_form_label("起点 revision
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
options=tuple(refs), initial_option="HEAD",
|
|
572
|
-
),
|
|
562
|
+
_form_label("起点 revision;检出已有分支时填写本地分支名"),
|
|
563
|
+
{"tag": "input", "name": "new_worktree_ref", "max_length": 256,
|
|
564
|
+
"placeholder": _plain_text("HEAD、分支、标签或提交"), "default_value": "HEAD"},
|
|
573
565
|
{"tag": "input", "name": "new_worktree_branch", "max_length": 255,
|
|
574
566
|
"label": _plain_text("新分支名(仅创建新分支时填写)"),
|
|
575
567
|
"placeholder": _plain_text("例如:feature/my-task")},
|
|
@@ -1023,7 +1015,7 @@ def new_binding_card(
|
|
|
1023
1015
|
)
|
|
1024
1016
|
if not projects:
|
|
1025
1017
|
builder.markdown(
|
|
1026
|
-
"还没有已启用的项目。请先发送 `/
|
|
1018
|
+
"还没有已启用的项目。请先发送 `/projects` 创建、登记或启用项目,"
|
|
1027
1019
|
"再发送 `/new`。发送 `/help` 查看快速开始。"
|
|
1028
1020
|
)
|
|
1029
1021
|
else:
|
|
@@ -1056,7 +1048,7 @@ def new_binding_card(
|
|
|
1056
1048
|
settings_draft=settings_draft,
|
|
1057
1049
|
)
|
|
1058
1050
|
)
|
|
1059
|
-
return OutboundCard(card=
|
|
1051
|
+
return OutboundCard(card=builder.to_dict())
|
|
1060
1052
|
|
|
1061
1053
|
|
|
1062
1054
|
def config_card(
|
|
@@ -1116,7 +1108,7 @@ def config_card(
|
|
|
1116
1108
|
catalog=catalog,
|
|
1117
1109
|
)
|
|
1118
1110
|
)
|
|
1119
|
-
return OutboundCard(card=
|
|
1111
|
+
return OutboundCard(card=builder.to_dict())
|
|
1120
1112
|
|
|
1121
1113
|
|
|
1122
1114
|
def rename_binding_card(
|
|
@@ -2393,7 +2385,8 @@ def _decode_new_workspace(
|
|
|
2393
2385
|
if match is None:
|
|
2394
2386
|
raise CardActionError("worktree 选择无效,请重新加载 worktree 选项。")
|
|
2395
2387
|
mode = _required_string(payload["new_worktree_mode"], "new_worktree_mode")
|
|
2396
|
-
|
|
2388
|
+
ref_value = payload["new_worktree_ref"]
|
|
2389
|
+
ref = _bounded_string(ref_value, "new_worktree_ref", max_chars=256).strip() if ref_value != "" else ""
|
|
2397
2390
|
branch_value = payload.get("new_worktree_branch")
|
|
2398
2391
|
branch = (
|
|
2399
2392
|
_bounded_string(branch_value, "new_worktree_branch", max_chars=255).strip()
|
|
@@ -2412,10 +2405,11 @@ def _decode_new_workspace(
|
|
|
2412
2405
|
if mode == "new" and not branch:
|
|
2413
2406
|
raise CardActionError("创建新分支时请填写新分支名。")
|
|
2414
2407
|
if mode == "existing":
|
|
2415
|
-
if not ref.startswith("refs/heads/") or not ref.removeprefix("refs/heads/"):
|
|
2416
|
-
raise CardActionError("检出已有分支时,请选择未被占用的本地分支。")
|
|
2417
2408
|
branch = ref.removeprefix("refs/heads/")
|
|
2418
|
-
|
|
2409
|
+
if not branch or branch == "HEAD" or branch.startswith("refs/"):
|
|
2410
|
+
raise CardActionError("检出已有分支时,请填写本地分支名。")
|
|
2411
|
+
ref = "refs/heads/" + branch
|
|
2412
|
+
return None, {"mode": mode, "ref": ref or "HEAD", "branch": branch if mode != "detached" else None}
|
|
2419
2413
|
|
|
2420
2414
|
|
|
2421
2415
|
def _decode_config_form(
|
|
@@ -139,10 +139,10 @@ def defaults_card(
|
|
|
139
139
|
project_options.append({"text": _plain_text(f"{selected_project} · 不可用(保留原配置)"), "value": selected_value})
|
|
140
140
|
builder.raw(_notice(f"Project「{selected_project}」已停用或不可用;请选择可用项目后保存,或删除本聊天的默认配置。", error=True))
|
|
141
141
|
if not enabled_projects:
|
|
142
|
-
builder.raw(_notice("没有可用 Project,请先通过 /
|
|
142
|
+
builder.raw(_notice("没有可用 Project,请先通过 /projects 登记或启用项目。"))
|
|
143
143
|
if not project_options:
|
|
144
144
|
# Feishu cannot render an empty static select. There is no saved exact
|
|
145
|
-
# record to delete in this branch; opening again after /
|
|
145
|
+
# record to delete in this branch; opening again after /projects gives
|
|
146
146
|
# the ordinary single form without inventing a default Project.
|
|
147
147
|
return OutboundCard(card=builder.to_dict())
|
|
148
148
|
|