netizen-cli 0.13.2__tar.gz → 0.13.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.13.2/netizen_cli.egg-info → netizen_cli-0.13.3}/PKG-INFO +1 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/__init__.py +1 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/admin.css +2 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/admin.js +234 -61
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/index.html +7 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/web.py +3 -3
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/bindings.py +7 -3
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/controls.py +27 -7
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/defaults.py +10 -96
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/scheduled.py +173 -56
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/status.py +3 -1
- netizen_cli-0.13.3/netizen_cli/cards/workspace.py +136 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel_app.py +80 -11
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/database_migrations.py +2 -0
- netizen_cli-0.13.3/netizen_cli/defaults/models.py +38 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/defaults/service.py +12 -16
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/defaults/store.py +2 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/management/service.py +1 -0
- netizen_cli-0.13.3/netizen_cli/migrations/v17.py +92 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/schedules/mcp.py +44 -3
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/schedules/models.py +3 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/schedules/scheduler.py +24 -6
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/schedules/service.py +242 -38
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/schedules/store.py +78 -24
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sessions/presentation.py +49 -19
- netizen_cli-0.13.2/netizen_cli/defaults/models.py → netizen_cli-0.13.3/netizen_cli/worktrees/policy.py +7 -34
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/worktrees/service.py +40 -4
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/worktrees/store.py +11 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3/netizen_cli.egg-info}/PKG-INFO +1 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli.egg-info/SOURCES.txt +8 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/pyproject.toml +1 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/admin.md +5 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/scheduled-tasks.md +45 -4
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/user-guide.md +3 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_binding_scheduled_channel.py +1 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_bindings.py +18 -7
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli_data.py +6 -5
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cron_chat_targets.py +88 -3
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_database_migrations.py +4 -4
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_defaults_channel.py +4 -4
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_migration_schema_validation.py +1 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_schedule_cards.py +11 -3
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_schedule_mcp.py +43 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_schedule_run_now.py +2 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_schedule_scope_matrix.py +2 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_schedule_service.py +1 -1
- netizen_cli-0.13.3/tests/test_schedule_workspace_cards.py +171 -0
- netizen_cli-0.13.3/tests/test_schedule_workspace_migration.py +129 -0
- netizen_cli-0.13.3/tests/test_schedule_workspace_service.py +446 -0
- netizen_cli-0.13.3/tests/test_schedule_workspace_storage.py +232 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_scheduled_channel.py +4 -3
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_scheduled_runtime.py +2 -1
- netizen_cli-0.13.3/tests/test_scheduled_worktrees.py +496 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_scheduler.py +3 -2
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_schedules_storage.py +8 -6
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_defaults.py +23 -3
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_defaults_migration.py +6 -4
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_tools.py +149 -38
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_status_cards.py +18 -11
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_status_commands.py +11 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_worktree_channel_cwd.py +1 -1
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/MANIFEST.in +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/README.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/_build.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/config.example.yaml +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/__main__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/account_rate_limits.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/auth.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/credentials.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/errors.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/feishu.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/login.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/port_config.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/presentation.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/queries.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/chat-picker.css +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/chat-picker.js +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/identity.js +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/login.html +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/login.js +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/session-access.js +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/static/worktrees.js +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/transport.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/admin/users.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/builtin_skills.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/callbacks.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/chat_target.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/fork.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/model_info.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/pagination.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/questions.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/reply.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/session_ids.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cards/usage.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/completion_mentions.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/input_preparation.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/messages.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/ports.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/question_inputs.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/reactions.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/reply_presenter.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/session_pages.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/channel/topics.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/chat_avatars.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/chat_targets.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cli.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cli_data.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cli_packages.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cli_services.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cli_setup.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cli_update.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cli_update_restore.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/cli_update_worker.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/codex_runtime.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/completion_mention.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/defaults/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/deployment/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/deployment/restart_worker.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/deployment/update_executor.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/deployment/update_protocol.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/domain.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/error_messages.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/experience.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/feishu_app_onboarding.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/feishu_app_permissions.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/feishu_links.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/git_status.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/image_inputs.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/instance.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/lark_app.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/main.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/management/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/management/blocking_io.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/management/chat_directory.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/management/chat_labels.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/management/coordination.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/management/updates.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/markdown_images.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/mcp_transport.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/message_content.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/message_history.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/message_preparation.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/message_projection.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/migrations/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/migrations/schema.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/migrations/v14.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/migrations/v15.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/migrations/v16.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/model_settings.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/package_resources.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/pagination.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/partial_answers.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/projects.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/prompt_projection.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/quoted_context.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/result_images.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/runtime/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/runtime/contracts.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/runtime/name_writes.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/runtime/thread_naming.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/schedules/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sdk_gap_adapter.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/service_launcher.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/session_inventory.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/session_settings.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sessions/__init__.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sessions/delegation.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sessions/errors.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sessions/mcp.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sessions/policy.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sessions/schema.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/sessions/service.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/settings.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/skill_references.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/task_text.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/terminal_cleanup.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/thread_history.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/turn_activity.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/turn_files.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/turn_patch_children.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/turn_plan_observer.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/user_questions.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/worktrees/errors.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/worktrees/git.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/worktrees/models.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli/worktrees/settings.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli.egg-info/dependency_links.txt +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli.egg-info/entry_points.txt +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli.egg-info/requires.txt +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/netizen_cli.egg-info/top_level.txt +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/setup.cfg +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-lark/SKILL.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/SKILL.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/commands.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/concepts.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/faq.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/feedback-and-files.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/fork-side-goal.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/messages.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/native-capabilities.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/session-collaboration.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/sessions.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/skills/netizen-user-guide/references/status-and-control.md +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_account_rate_limits.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_admin_update_executor_probe.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cards.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_channel_app.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_channel_fixtures.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_channel_input_preparation.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_chat_avatars.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_chat_target_cards.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_check_sdk.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli_distribution.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli_integration.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli_packages.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli_packaging.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli_services.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli_setup.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_cli_update.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_codex_runtime.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_codex_sdk_capabilities.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_codex_sdk_upgrade_0161.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_codex_sdk_upgrade_0163.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_completion_mention_cards.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_completion_mentions.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_completion_topic_delivery.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_defaults_cards.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_defaults_runtime.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_defaults_worktree_channel.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_deployment.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_documentation_links.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_domain_experience.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_error_messages.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_feishu_app_onboarding.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_feishu_app_permissions.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_file_pagination.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_fork_cards.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_git_status.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_goal_reply_separation.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_image_inputs.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_lark_app.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_lark_sdk_contract.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_main.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_markdown_images.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_message_content.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_message_history.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_message_material_inputs.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_message_preparation.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_model_info_cards.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_model_settings.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_name_writes.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_pagination.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_partial_answer_delivery.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_partial_answers.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_persistent_fork_bindings.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_persistent_fork_channel.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_persistent_fork_runtime.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_child_files.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_feishu_completion_mention.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_feishu_partial_answers.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_feishu_turn_file_card.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_persistent_fork.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_python_sdk.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_scheduled_tasks.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_sdk_completion_race.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_sdk_response_recovery.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_skill_execution.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_probe_thread_naming.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_project_delete_channel.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_project_delete_runtime.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_project_deletion_storage.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_prompt_projection.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_question_cards.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_question_channel.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_question_target_parity.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_quoted_context.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_release_script.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_repository_hygiene.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_result_images.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_resume_channel.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_retired_deployment.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_runtime_resume_activation.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_runtime_thread_naming.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_schedule_card_retry.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_scheduler_presenter.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_sdk_gap_adapter.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_service_launcher.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_card_pages.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_collaboration_app.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_external_runtime.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_id_cards.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_policy.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_session_settings.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_settings_projects.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_task_text.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_terminal_cleanup.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_thread_history.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_thread_naming.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_turn_activity.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_turn_files.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_turn_patch_children.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_turn_patch_statistics.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_turn_plan_observer.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_update_executor.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_usage_card.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_usage_control.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_user_guide_skill.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_user_questions.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_worktree_git.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_worktree_new.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_worktree_runtime_cwd.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_worktree_service.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_worktree_settings.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_worktree_storage.py +0 -0
- {netizen_cli-0.13.2 → netizen_cli-0.13.3}/tests/test_worktree_tools_integration.py +0 -0
|
@@ -287,7 +287,7 @@ body.schedule-drawer-open, body.defaults-drawer-open, body.session-settings-draw
|
|
|
287
287
|
#schedules-body .schedule-state { min-width: 118px; white-space: pre-line; }
|
|
288
288
|
#schedules-body .schedule-execution { min-width: 150px; white-space: pre-line; }
|
|
289
289
|
#schedules-body .schedule-time, #schedules-body .schedule-target { overflow-wrap: anywhere; }
|
|
290
|
-
#schedules-body .schedule-note { display: block; margin-top: 7px; color: var(--muted); font-size: 12px; line-height: 1.5; }
|
|
290
|
+
#schedules-body .schedule-note, #schedule-runs-body .schedule-note { display: block; margin-top: 7px; color: var(--muted); font-size: 12px; line-height: 1.5; overflow-wrap: anywhere; }
|
|
291
291
|
@media (max-width: 540px) {
|
|
292
292
|
.schedule-editor-head, .schedule-editor-body { padding: 16px; }
|
|
293
293
|
.schedule-editor-footer { padding: 12px 16px; flex-wrap: wrap; }
|
|
@@ -381,6 +381,7 @@ body.schedule-detail-open { overflow: hidden; }
|
|
|
381
381
|
.defaults-feedback label { display: flex; align-items: center; gap: 7px; }
|
|
382
382
|
.drawer-form .defaults-feedback input { width: 16px; height: 16px; margin: 0; accent-color: var(--blue); }
|
|
383
383
|
#defaults-note.error { color: var(--danger); }
|
|
384
|
+
#defaults-workspace-help.error, #schedule-editor #schedule-workspace-help.error { color: var(--danger); }
|
|
384
385
|
#session-settings-editor[hidden], #session-settings-editor [hidden] { display: none; }
|
|
385
386
|
#session-settings-fields { border: 0; padding: 0; margin: 0; min-width: 0; }
|
|
386
387
|
#session-settings-fields:disabled { opacity: .6; }
|
|
@@ -2520,6 +2520,81 @@ function chatTarget(prefix, onChange = null) {
|
|
|
2520
2520
|
return controller;
|
|
2521
2521
|
}
|
|
2522
2522
|
|
|
2523
|
+
function workspaceErrorMessage(error) {
|
|
2524
|
+
return typeof error === "string" ? error : error?.message || "";
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
function workspacePolicyAvailable(workspace, options, project) {
|
|
2528
|
+
if (workspace.mode === "project") return true;
|
|
2529
|
+
if (!options?.isGit || options.projectAlias !== project) return false;
|
|
2530
|
+
return workspace.mode === "new"
|
|
2531
|
+
|| options.items.some((item) => item.available === true && item.id === workspace.worktree_id);
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
function workspacePolicySummary(workspace, { scheduled = false } = {}) {
|
|
2535
|
+
if (!workspace) return "沿用原会话的实际工作目录";
|
|
2536
|
+
if (workspace.mode === "existing") return `指定 Worktree:${workspace.worktree_id}`;
|
|
2537
|
+
if (workspace.mode === "new") return `${scheduled ? "每次执行" : "每个新会话"}创建 Worktree · ${workspace.ref || "创建时 Project HEAD"}`;
|
|
2538
|
+
return "项目目录";
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
function renderWorkspacePolicy(input, editor, { project, loading = false, scheduled = false } = {}) {
|
|
2542
|
+
const workspace = editor.workspace;
|
|
2543
|
+
const options = editor.workspaceOptions;
|
|
2544
|
+
const available = options?.isGit && options.projectAlias === project;
|
|
2545
|
+
const items = available ? options.items.filter((item) => item.available === true) : [];
|
|
2546
|
+
input("workspace").value = workspace.mode;
|
|
2547
|
+
for (const option of input("workspace").querySelectorAll("option")) {
|
|
2548
|
+
option.disabled = option.value !== "project" && (!available || option.value === "existing" && !items.length);
|
|
2549
|
+
}
|
|
2550
|
+
input("worktree-field").hidden = workspace.mode !== "existing";
|
|
2551
|
+
input("workspace-ref-field").hidden = workspace.mode !== "new";
|
|
2552
|
+
scheduleSelectOptions(input("worktree"), [["", "选择受管 Worktree"],
|
|
2553
|
+
...items.map((item) => [item.id, `${item.cwd} · ${item.branch || "detached"} · ${item.id}`])],
|
|
2554
|
+
workspace.worktree_id || "");
|
|
2555
|
+
input("worktree").required = workspace.mode === "existing";
|
|
2556
|
+
input("workspace-ref").value = workspace.ref || "";
|
|
2557
|
+
for (const field of ["workspace", "worktree", "workspace-ref"]) input(field).disabled = loading;
|
|
2558
|
+
input("workspace-refs").replaceChildren();
|
|
2559
|
+
for (const branch of options?.branches || []) {
|
|
2560
|
+
const option = document.createElement("option");
|
|
2561
|
+
option.value = branch.ref;
|
|
2562
|
+
option.textContent = branch.name;
|
|
2563
|
+
input("workspace-refs").append(option);
|
|
2564
|
+
}
|
|
2565
|
+
const notes = [];
|
|
2566
|
+
if (loading) notes.push("正在读取工作区选项…");
|
|
2567
|
+
if (editor.workspaceError) notes.push(workspaceErrorMessage(editor.workspaceError));
|
|
2568
|
+
else if (options?.isGit === false) notes.push("该 Project 不是 Git 仓库,只支持项目目录。");
|
|
2569
|
+
else if (!project) notes.push("先选择 Project,再选择工作区策略。");
|
|
2570
|
+
if (workspace.mode === "existing") {
|
|
2571
|
+
notes.push(scheduled ? "各次执行共享所选 Worktree 的文件和分支;资源不可用时保留计划并跳过执行。"
|
|
2572
|
+
: "新会话共享所选 Worktree 的文件和分支;删除或失效后保留此配置,自动创建会话时明确报错。");
|
|
2573
|
+
if (!loading && !workspacePolicyAvailable(workspace, options, project)) {
|
|
2574
|
+
notes.push("所选 Worktree 当前不可用,请重新选择;不会切回项目目录。");
|
|
2575
|
+
}
|
|
2576
|
+
} else if (workspace.mode === "new") {
|
|
2577
|
+
notes.push(scheduled
|
|
2578
|
+
? "每次执行创建一份 detached Worktree;该次会话的后续消息复用。留空使用执行时 Project 的 HEAD,预览和保存不创建目录。"
|
|
2579
|
+
: "每次自动创建新会话时创建一次 detached Worktree;后续消息复用。留空使用创建时 Project 的 HEAD,保存配置不创建目录。");
|
|
2580
|
+
if (!loading && !available) notes.push("当前无法使用新建 Worktree 策略,请检查 Project。");
|
|
2581
|
+
} else notes.push(scheduled ? "每次执行创建的会话使用 Project 的项目目录。" : "新会话使用 Project 的项目目录。");
|
|
2582
|
+
input("workspace-help").textContent = notes.join(" ");
|
|
2583
|
+
input("workspace-help").classList.toggle("error", !loading && !workspacePolicyAvailable(workspace, options, project));
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
function changeWorkspacePolicy(input, editor, field) {
|
|
2587
|
+
if (field === "workspace") {
|
|
2588
|
+
const mode = input("workspace").value;
|
|
2589
|
+
editor.workspace = mode === "existing" ? { mode, worktree_id: "" } : { mode };
|
|
2590
|
+
} else if (field === "worktree") {
|
|
2591
|
+
editor.workspace = { mode: "existing", worktree_id: input(field).value };
|
|
2592
|
+
} else {
|
|
2593
|
+
const ref = input(field).value.trim();
|
|
2594
|
+
editor.workspace = { mode: "new", ...(ref ? { ref } : {}) };
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2523
2598
|
let defaultsEditor = null;
|
|
2524
2599
|
let defaultsProjects = [];
|
|
2525
2600
|
let defaultsOffset = 0;
|
|
@@ -2598,49 +2673,13 @@ function renderDefaultProject(project) {
|
|
|
2598
2673
|
|
|
2599
2674
|
function defaultWorkspaceAvailable() {
|
|
2600
2675
|
const editor = defaultsEditor;
|
|
2601
|
-
|
|
2602
|
-
const options = editor.workspaceOptions;
|
|
2603
|
-
if (!options?.isGit || options.projectAlias !== defaultsInput("project").value) return false;
|
|
2604
|
-
return editor.workspace.mode === "new"
|
|
2605
|
-
|| options.items.some((item) => item.id === editor.workspace.worktree_id);
|
|
2676
|
+
return !editor || workspacePolicyAvailable(editor.workspace, editor.workspaceOptions, defaultsInput("project").value);
|
|
2606
2677
|
}
|
|
2607
2678
|
|
|
2608
2679
|
function renderDefaultWorkspace() {
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
const available = options?.isGit && options.projectAlias === defaultsInput("project").value;
|
|
2613
|
-
defaultsInput("workspace").value = workspace.mode;
|
|
2614
|
-
for (const option of defaultsInput("workspace").querySelectorAll("option")) {
|
|
2615
|
-
option.disabled = option.value !== "project" && (!available || option.value === "existing" && !options.items.length);
|
|
2616
|
-
}
|
|
2617
|
-
defaultsInput("worktree-field").hidden = workspace.mode !== "existing";
|
|
2618
|
-
defaultsInput("workspace-ref-field").hidden = workspace.mode !== "new";
|
|
2619
|
-
scheduleSelectOptions(defaultsInput("worktree"), [["", "选择受管 Worktree"],
|
|
2620
|
-
...(available ? options.items : []).map((item) => [item.id, `${item.cwd} · ${item.branch || "detached"} · ${item.id}`])],
|
|
2621
|
-
workspace.worktree_id || "");
|
|
2622
|
-
defaultsInput("worktree").required = workspace.mode === "existing";
|
|
2623
|
-
defaultsInput("workspace-ref").value = workspace.ref || "";
|
|
2624
|
-
defaultsInput("workspace-refs").replaceChildren();
|
|
2625
|
-
for (const branch of options?.branches || []) {
|
|
2626
|
-
const option = document.createElement("option");
|
|
2627
|
-
option.value = branch.ref;
|
|
2628
|
-
option.textContent = branch.name;
|
|
2629
|
-
defaultsInput("workspace-refs").append(option);
|
|
2630
|
-
}
|
|
2631
|
-
const notes = [];
|
|
2632
|
-
if (editor.workspaceError) notes.push(editor.workspaceError);
|
|
2633
|
-
else if (options?.isGit === false) notes.push("该 Project 不是 Git 仓库,只支持项目目录。");
|
|
2634
|
-
else if (!defaultsInput("project").value) notes.push("先选择 Project,再选择工作区策略。");
|
|
2635
|
-
if (workspace.mode === "existing") {
|
|
2636
|
-
notes.push("新会话共享所选 Worktree 的文件和分支;删除或失效后保留此配置,自动创建会话时明确报错。");
|
|
2637
|
-
if (!editor.loading && !defaultWorkspaceAvailable()) notes.push("所选 Worktree 当前不可用,请重新选择;不会切回项目目录。");
|
|
2638
|
-
} else if (workspace.mode === "new") {
|
|
2639
|
-
notes.push("每次自动创建新会话时创建一次 detached Worktree;后续消息复用。留空使用创建时 Project 的 HEAD,保存配置不创建目录。");
|
|
2640
|
-
if (!editor.loading && !available) notes.push("当前无法使用新建 Worktree 策略,请检查 Project。");
|
|
2641
|
-
} else notes.push("新会话使用 Project 的项目目录。");
|
|
2642
|
-
defaultsInput("workspace-help").textContent = notes.join(" ");
|
|
2643
|
-
defaultsInput("workspace-help").classList.toggle("error", !editor.loading && !defaultWorkspaceAvailable());
|
|
2680
|
+
renderWorkspacePolicy(defaultsInput, defaultsEditor, {
|
|
2681
|
+
project: defaultsInput("project").value, loading: defaultsEditor.loading,
|
|
2682
|
+
});
|
|
2644
2683
|
}
|
|
2645
2684
|
|
|
2646
2685
|
async function changeDefaultProject() {
|
|
@@ -2655,15 +2694,7 @@ async function changeDefaultProject() {
|
|
|
2655
2694
|
function changeDefaultWorkspace(field) {
|
|
2656
2695
|
const editor = defaultsEditor;
|
|
2657
2696
|
if (!editor || editor.loading || defaultsBusy) return;
|
|
2658
|
-
|
|
2659
|
-
const mode = defaultsInput("workspace").value;
|
|
2660
|
-
editor.workspace = mode === "existing" ? { mode, worktree_id: "" } : { mode };
|
|
2661
|
-
} else if (field === "worktree") {
|
|
2662
|
-
editor.workspace = { mode: "existing", worktree_id: defaultsInput(field).value };
|
|
2663
|
-
} else {
|
|
2664
|
-
const ref = defaultsInput(field).value.trim();
|
|
2665
|
-
editor.workspace = { mode: "new", ...(ref ? { ref } : {}) };
|
|
2666
|
-
}
|
|
2697
|
+
changeWorkspacePolicy(defaultsInput, editor, field);
|
|
2667
2698
|
renderDefaultSettings();
|
|
2668
2699
|
}
|
|
2669
2700
|
|
|
@@ -2922,8 +2953,7 @@ function defaultSettingsCell(row, settings, workspace) {
|
|
|
2922
2953
|
detail.textContent = scheduleSessionSummary(settings).split(" · ").slice(1).join(" · ");
|
|
2923
2954
|
const location = document.createElement("div");
|
|
2924
2955
|
location.className = "default-settings-detail";
|
|
2925
|
-
location.textContent = workspace
|
|
2926
|
-
: workspace.mode === "new" ? `每个新会话创建 Worktree · ${workspace.ref || "创建时 Project HEAD"}` : "项目目录";
|
|
2956
|
+
location.textContent = workspacePolicySummary(workspace);
|
|
2927
2957
|
td.append(title, detail, location);
|
|
2928
2958
|
row.append(td);
|
|
2929
2959
|
}
|
|
@@ -3063,6 +3093,10 @@ function scheduleRunStatus(status) {
|
|
|
3063
3093
|
not_started: "尚未执行", expired: "已过期,未执行", missed: "已错过",
|
|
3064
3094
|
skipped_busy: "上次仍在执行,本次跳过", blocked_unknown: "上次结果待确认,本次跳过",
|
|
3065
3095
|
project_disabled: "Project 已停用,本次未执行", project_unavailable: "Project 不可用,本次未执行",
|
|
3096
|
+
workspace_unavailable: "工作区不可用,本次未执行",
|
|
3097
|
+
workspace_creation_failed: "工作区创建失败,本次未执行",
|
|
3098
|
+
workspace_creation_unknown: "工作区创建结果待确认,本次未执行",
|
|
3099
|
+
workspace_handoff_failed: "工作区已保留,会话交接失败",
|
|
3066
3100
|
released: "调度已收尾", recovery_no_start: "服务恢复,本次未启动",
|
|
3067
3101
|
publishing_unknown: "话题投递待确认", deleted: "执行会话已删除",
|
|
3068
3102
|
initial_start_rejected: "启动被拒绝", publishing_failed: "话题发布失败",
|
|
@@ -3077,25 +3111,54 @@ function scheduleTriggerSource(source) {
|
|
|
3077
3111
|
return source === "manual" ? "手动触发" : source === "scheduled" ? "定时触发" : "";
|
|
3078
3112
|
}
|
|
3079
3113
|
|
|
3114
|
+
function scheduleWorktreeReceipt(run, { details = true } = {}) {
|
|
3115
|
+
if (!run?.worktree_id) return "";
|
|
3116
|
+
const labels = [`Worktree:${run.worktree_id}`];
|
|
3117
|
+
const receipt = run.worktree;
|
|
3118
|
+
if (details && receipt) {
|
|
3119
|
+
if (receipt.cwd) labels.push(`工作目录:${receipt.cwd}`);
|
|
3120
|
+
if (receipt.root && receipt.root !== receipt.cwd) labels.push(`根目录:${receipt.root}`);
|
|
3121
|
+
if (!receipt.cwd && !receipt.root) labels.push("资源登记已不可用");
|
|
3122
|
+
if (receipt.creation_state === "unknown") labels.push("创建结果待确认");
|
|
3123
|
+
else if (receipt.creation_state === "creating") labels.push("创建尚未确认完成");
|
|
3124
|
+
}
|
|
3125
|
+
return labels.join(" · ");
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3080
3128
|
function scheduleExecutionLabel(execution) {
|
|
3081
3129
|
const label = scheduleRunStatus(execution.status);
|
|
3082
3130
|
if (execution.kind === "none") return label;
|
|
3083
3131
|
if (execution.status === "deleted") return label;
|
|
3084
3132
|
if (execution.kind === "current") return `${execution.is_last ? "最后一次" : "本次"}${label}`;
|
|
3085
|
-
if (["skipped_busy", "blocked_unknown", "project_disabled", "project_unavailable"].includes(execution.status)) return label;
|
|
3133
|
+
if (["skipped_busy", "blocked_unknown", "project_disabled", "project_unavailable", "workspace_unavailable"].includes(execution.status)) return label;
|
|
3086
3134
|
return `上次${label}`;
|
|
3087
3135
|
}
|
|
3088
3136
|
|
|
3089
3137
|
function scheduleBlockedLabel(plan) {
|
|
3090
|
-
|
|
3138
|
+
const primary = { blocked_unknown: "执行结果待确认", project_disabled: "Project 已停用",
|
|
3091
3139
|
project_unavailable: "Project 不可用", target_inactive: "原会话已切走或归档,自动暂停",
|
|
3092
3140
|
target_missing: "原会话不可用", target_mismatch: "原会话位置不匹配" }[plan.blocked_reason] || "";
|
|
3141
|
+
const workspace = workspaceErrorMessage(plan.workspace_error)
|
|
3142
|
+
|| ([plan.blocked_reason, plan.suspended_reason].includes("workspace_unavailable") ? "工作区不可用" : "");
|
|
3143
|
+
return [primary, workspace].filter(Boolean).join(" · ");
|
|
3093
3144
|
}
|
|
3094
3145
|
|
|
3095
3146
|
function scheduleTargetLabel(plan) {
|
|
3096
3147
|
return plan.target_kind === "binding" ? `原会话 · ${plan.target_label || plan.target_binding_id}` : "每次新建独立话题";
|
|
3097
3148
|
}
|
|
3098
3149
|
|
|
3150
|
+
function scheduleInheritedWorkspace(plan) {
|
|
3151
|
+
const workspace = plan.effective_workspace;
|
|
3152
|
+
if (!workspace) return "沿用原会话的实际工作目录;目录信息暂未取得。";
|
|
3153
|
+
const labels = [workspace.working_cwd
|
|
3154
|
+
? `${workspace.native_cwd_unresolved ? "已登记" : "已选"}工作目录:${workspace.working_cwd}`
|
|
3155
|
+
: "工作目录尚未登记"];
|
|
3156
|
+
if (workspace.worktree_id) labels.push(`Worktree:${workspace.worktree_id}`);
|
|
3157
|
+
labels.push(workspace.native_cwd_unresolved
|
|
3158
|
+
? "实际目录由原会话确定,本次未查询原生目录。" : "执行时沿用原会话的工作目录。");
|
|
3159
|
+
return labels.join(" · ");
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3099
3162
|
function scheduleNote(parent, value) {
|
|
3100
3163
|
const note = document.createElement("span");
|
|
3101
3164
|
note.className = "schedule-note";
|
|
@@ -3209,6 +3272,10 @@ function validateScheduleFields({ saving = false } = {}) {
|
|
|
3209
3272
|
if (kind === "weekly" && !Array.from(scheduleInput("weekdays").querySelectorAll("input")).some((input) => input.checked)) {
|
|
3210
3273
|
setScheduleFieldError("weekdays", "请至少选择一天。");
|
|
3211
3274
|
}
|
|
3275
|
+
if (saving && !scheduleWorkspaceCanSave()) {
|
|
3276
|
+
setScheduleFieldError("workspace", scheduleEditor.workspaceLoading ? "工作区选项仍在读取,请稍后保存。"
|
|
3277
|
+
: workspaceErrorMessage(scheduleEditor.workspaceError) || "请选择可用的工作区策略。");
|
|
3278
|
+
}
|
|
3212
3279
|
if (scheduleFieldErrors.size) {
|
|
3213
3280
|
if (scheduleEditor?.action) showScheduleFormError("请检查标红的表单项。");
|
|
3214
3281
|
focusScheduleError();
|
|
@@ -3223,7 +3290,8 @@ function validateScheduleFields({ saving = false } = {}) {
|
|
|
3223
3290
|
function showScheduleRequestError(error, { submitted = false } = {}) {
|
|
3224
3291
|
const field = ({ ambiguous_local_time: "offset", ambiguous_end_time: "end-offset",
|
|
3225
3292
|
invalid_chat_id: "chat", not_group_chat: "chat",
|
|
3226
|
-
project_disabled: "project", project_unavailable: "project", project_deleting: "project"
|
|
3293
|
+
project_disabled: "project", project_unavailable: "project", project_deleting: "project", project_changed: "project",
|
|
3294
|
+
workspace_unavailable: "workspace" })[error.code];
|
|
3227
3295
|
if (field && (!["chat", "project"].includes(field) || scheduleTargetKind() !== "binding")) {
|
|
3228
3296
|
setScheduleFieldError(field, error.message);
|
|
3229
3297
|
focusScheduleError();
|
|
@@ -3250,6 +3318,7 @@ function renderScheduleTarget() {
|
|
|
3250
3318
|
scheduleInput("chat-target").hidden = binding;
|
|
3251
3319
|
if (binding) { scheduleInput("chat").required = false; chatTarget("schedule").close(); }
|
|
3252
3320
|
scheduleInput("session-settings").hidden = binding;
|
|
3321
|
+
renderScheduleWorkspace();
|
|
3253
3322
|
const target = editor.targetSession;
|
|
3254
3323
|
const selected = editor.plan?.target_binding_id || target?.bindingId || "";
|
|
3255
3324
|
scheduleInput("binding").value = selected;
|
|
@@ -3259,6 +3328,7 @@ function renderScheduleTarget() {
|
|
|
3259
3328
|
+ (target.pointerState !== "current" || target.catalogState === "archived" ? "\n目标当前不是可执行的当前会话,计划将自动暂停;不会自动切换或恢复会话。" : "")
|
|
3260
3329
|
: selected ? `${editor.plan.target_label || selected}\n${editor.plan.chat?.chatLabel || editor.plan.chat_id} · ${editor.plan.project_alias}`
|
|
3261
3330
|
: "请前往 Sessions 找到具体会话,再点击该行的“创建定时任务”。已填写内容会在当前页面保留。";
|
|
3331
|
+
if (binding && editor.plan) scheduleInput("binding-summary").textContent += `\n${scheduleInheritedWorkspace(editor.plan)}`;
|
|
3262
3332
|
scheduleInput("preview").disabled = binding && !selected;
|
|
3263
3333
|
}
|
|
3264
3334
|
|
|
@@ -3304,7 +3374,8 @@ function scheduleEditorValues() {
|
|
|
3304
3374
|
binding: binding ? scheduleInput("binding").value : "",
|
|
3305
3375
|
project: binding ? "" : scheduleInput("project").value,
|
|
3306
3376
|
chat: binding ? "" : scheduleInput("chat").value, rule,
|
|
3307
|
-
settings: binding ? null : sessionSettingsFingerprint(scheduleEditor.sessionDraft)
|
|
3377
|
+
settings: binding ? null : sessionSettingsFingerprint(scheduleEditor.sessionDraft),
|
|
3378
|
+
workspace: binding ? null : structuredClone(scheduleEditor.workspace) };
|
|
3308
3379
|
}
|
|
3309
3380
|
|
|
3310
3381
|
function scheduleEditorHasChanges() {
|
|
@@ -3381,6 +3452,8 @@ function changeScheduleChat(resolve = true) {
|
|
|
3381
3452
|
if (!editor) return;
|
|
3382
3453
|
clearScheduleFieldError("chat");
|
|
3383
3454
|
editor.optionsSerial += 1;
|
|
3455
|
+
editor.workspaceLoading = false;
|
|
3456
|
+
editor.chatMessage = "";
|
|
3384
3457
|
editor.contextAvailable = chatTarget("schedule").kind() === "p2p" ? false : null;
|
|
3385
3458
|
if (editor.contextAvailable === false) {
|
|
3386
3459
|
editor.sessionDraft.message_context_mode = "current-only";
|
|
@@ -3388,11 +3461,14 @@ function changeScheduleChat(resolve = true) {
|
|
|
3388
3461
|
}
|
|
3389
3462
|
invalidateSchedulePreview();
|
|
3390
3463
|
renderScheduleSessionSettings();
|
|
3464
|
+
renderScheduleWorkspace();
|
|
3391
3465
|
if (resolve && scheduleInput("chat").value.trim()) loadScheduleSessionOptions();
|
|
3392
3466
|
}
|
|
3393
3467
|
|
|
3394
3468
|
function scheduleRuleFingerprint() {
|
|
3395
3469
|
return JSON.stringify({ rule: readScheduleRule(),
|
|
3470
|
+
project: scheduleTargetKind() === "binding" ? null : scheduleInput("project").value,
|
|
3471
|
+
workspace: scheduleTargetKind() === "binding" ? null : scheduleEditor?.workspace,
|
|
3396
3472
|
utc_offset: scheduleInput("kind").value === "once" ? scheduleInput("offset").value : "",
|
|
3397
3473
|
end_utc_offset: scheduleInput("kind").value !== "once" ? scheduleInput("end-offset").value : "" });
|
|
3398
3474
|
}
|
|
@@ -3489,11 +3565,65 @@ function renderScheduleSessionSettings() {
|
|
|
3489
3565
|
scheduleInput("session-summary").textContent = scheduleSessionSummary(settings);
|
|
3490
3566
|
const notes = [];
|
|
3491
3567
|
if (editor.catalogMessage) notes.push(editor.catalogMessage);
|
|
3568
|
+
if (editor.chatMessage) notes.push(editor.chatMessage);
|
|
3492
3569
|
if (current && !model && !editor.catalogMessage) notes.push("已保存的模型当前不可用,原配置保持不变。可调整其他配置,或显式选择新模型。");
|
|
3493
3570
|
if (editor.contextAvailable === false) notes.push("私聊目标不支持补齐未读上下文;请选择当前消息后保存。");
|
|
3494
3571
|
scheduleInput("session-note").textContent = notes.join(" ");
|
|
3495
3572
|
}
|
|
3496
3573
|
|
|
3574
|
+
function scheduleWorkspaceChanged() {
|
|
3575
|
+
const editor = scheduleEditor;
|
|
3576
|
+
return !editor?.plan || scheduleInput("project").value !== editor.plan.project_alias
|
|
3577
|
+
|| JSON.stringify(editor.workspace) !== JSON.stringify(editor.plan.workspace);
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3580
|
+
function scheduleWorkspaceCanSave() {
|
|
3581
|
+
if (!scheduleEditor || scheduleTargetKind() === "binding" || !scheduleWorkspaceChanged()) return true;
|
|
3582
|
+
return workspacePolicyAvailable(scheduleEditor.workspace, scheduleEditor.workspaceOptions, scheduleInput("project").value);
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3585
|
+
function renderScheduleWorkspace() {
|
|
3586
|
+
const editor = scheduleEditor;
|
|
3587
|
+
if (!editor) return;
|
|
3588
|
+
const binding = scheduleTargetKind() === "binding";
|
|
3589
|
+
scheduleInput("workspace-fields").hidden = binding;
|
|
3590
|
+
if (binding) {
|
|
3591
|
+
for (const field of ["workspace", "worktree", "workspace-ref"]) scheduleInput(field).disabled = true;
|
|
3592
|
+
scheduleInput("worktree").required = false;
|
|
3593
|
+
return;
|
|
3594
|
+
}
|
|
3595
|
+
const unchanged = !scheduleWorkspaceChanged();
|
|
3596
|
+
renderWorkspacePolicy(scheduleInput, { ...editor,
|
|
3597
|
+
workspaceError: editor.workspaceError || (unchanged ? editor.plan.workspace_error : null) }, { project: scheduleInput("project").value,
|
|
3598
|
+
loading: editor.workspaceLoading, scheduled: true });
|
|
3599
|
+
if (unchanged && (!workspacePolicyAvailable(editor.workspace, editor.workspaceOptions, scheduleInput("project").value)
|
|
3600
|
+
|| editor.plan.workspace_available === false)) {
|
|
3601
|
+
scheduleInput("workspace-help").textContent += " 可保留此策略修改计划,之后执行仍会重新校验。";
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
function updateScheduleSaveAvailability() {
|
|
3606
|
+
scheduleInput("save").disabled = !schedulePrepared || !scheduleEditor?.action
|
|
3607
|
+
|| (!schedulePrepared.hasFuture && !scheduleEditor.plan) || !scheduleWorkspaceCanSave();
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3610
|
+
async function changeScheduleProject() {
|
|
3611
|
+
if (!scheduleEditor || scheduleInput("fields").disabled) return;
|
|
3612
|
+
scheduleEditor.workspace = { mode: "project" };
|
|
3613
|
+
clearScheduleFieldError("workspace");
|
|
3614
|
+
invalidateSchedulePreview();
|
|
3615
|
+
await loadScheduleSessionOptions();
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3618
|
+
function changeScheduleWorkspace(field) {
|
|
3619
|
+
if (!scheduleEditor || scheduleEditor.workspaceLoading || scheduleInput("fields").disabled
|
|
3620
|
+
|| scheduleTargetKind() === "binding") return;
|
|
3621
|
+
changeWorkspacePolicy(scheduleInput, scheduleEditor, field);
|
|
3622
|
+
clearScheduleFieldError("workspace");
|
|
3623
|
+
invalidateSchedulePreview();
|
|
3624
|
+
renderScheduleWorkspace();
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3497
3627
|
async function loadScheduleSessionOptions() {
|
|
3498
3628
|
const editor = scheduleEditor;
|
|
3499
3629
|
if (!editor) return;
|
|
@@ -3501,11 +3631,22 @@ async function loadScheduleSessionOptions() {
|
|
|
3501
3631
|
const serial = ++editor.optionsSerial;
|
|
3502
3632
|
const query = new URLSearchParams({ mode: "options" });
|
|
3503
3633
|
if (chatId && scheduleTargetKind() !== "binding") query.set("chat_id", chatId);
|
|
3634
|
+
if (scheduleTargetKind() !== "binding" && scheduleInput("project").value) {
|
|
3635
|
+
query.set("project", scheduleInput("project").value);
|
|
3636
|
+
}
|
|
3637
|
+
editor.workspaceOptions = null;
|
|
3638
|
+
editor.workspaceError = null;
|
|
3639
|
+
editor.workspaceLoading = query.has("project");
|
|
3640
|
+
renderScheduleWorkspace();
|
|
3641
|
+
updateScheduleSaveAvailability();
|
|
3504
3642
|
try {
|
|
3505
3643
|
const data = await api(`/api/v1/schedules?${query}`);
|
|
3506
3644
|
if (scheduleEditor !== editor || serial !== editor.optionsSerial) return;
|
|
3507
3645
|
editor.models = data.models;
|
|
3508
|
-
editor.contextAvailable = data.context_mode_available;
|
|
3646
|
+
editor.contextAvailable = data.chat_info_error ? null : data.context_mode_available;
|
|
3647
|
+
editor.chatMessage = data.chat_info_error?.message || "";
|
|
3648
|
+
editor.workspaceOptions = data.workspace_options || null;
|
|
3649
|
+
editor.workspaceError = data.workspace_options_error || null;
|
|
3509
3650
|
if (scheduleTargetKind() !== "binding") chatTarget("schedule").resolveKind(editor.contextAvailable);
|
|
3510
3651
|
editor.catalogMessage = data.model_catalog_error
|
|
3511
3652
|
? `${data.model_catalog_error.message} 已有配置保持不变,仍可调整其他项目。` : "";
|
|
@@ -3523,9 +3664,17 @@ async function loadScheduleSessionOptions() {
|
|
|
3523
3664
|
if (scheduleEditor !== editor || serial !== editor.optionsSerial) return;
|
|
3524
3665
|
editor.models = [];
|
|
3525
3666
|
editor.contextAvailable = null;
|
|
3667
|
+
editor.chatMessage = "";
|
|
3526
3668
|
editor.catalogMessage = `${error.message} 可选配置暂未刷新,已有选择保持不变。`;
|
|
3669
|
+
editor.workspaceError = error.message;
|
|
3527
3670
|
renderScheduleSessionSettings();
|
|
3528
3671
|
renderScheduleTarget();
|
|
3672
|
+
} finally {
|
|
3673
|
+
if (scheduleEditor === editor && serial === editor.optionsSerial) {
|
|
3674
|
+
editor.workspaceLoading = false;
|
|
3675
|
+
renderScheduleWorkspace();
|
|
3676
|
+
updateScheduleSaveAvailability();
|
|
3677
|
+
}
|
|
3529
3678
|
}
|
|
3530
3679
|
}
|
|
3531
3680
|
|
|
@@ -3638,7 +3787,7 @@ async function previewSchedule() {
|
|
|
3638
3787
|
scheduleInput("preview-message").textContent = data.preview.length
|
|
3639
3788
|
? `预计触发时间 · ${data.schedule.timezone}`
|
|
3640
3789
|
: scheduleEditor?.plan ? "没有后续触发时间。仍可保存计划修改;已认领的执行可以继续。" : "没有后续触发时间,请调整规则。";
|
|
3641
|
-
|
|
3790
|
+
updateScheduleSaveAvailability();
|
|
3642
3791
|
} catch (error) {
|
|
3643
3792
|
if (serial !== schedulePreviewSerial) return;
|
|
3644
3793
|
if (["ambiguous_local_time", "ambiguous_end_time"].includes(error.code) && Array.isArray(error.choices)) {
|
|
@@ -3667,7 +3816,10 @@ function openScheduleEditor(plan = null, source = {}) {
|
|
|
3667
3816
|
scheduleEditor = { plan, action, sessionBase: structuredClone(settings), sessionDraft: structuredClone(settings),
|
|
3668
3817
|
targetSession: source.targetSession || null, returnFocus: source.returnFocus || document.activeElement,
|
|
3669
3818
|
fromSessions: Boolean(source.targetSession), models: [], contextAvailable: null,
|
|
3670
|
-
optionsSerial: 0, settingsTouched: false, catalogMessage: "正在读取模型目录…"
|
|
3819
|
+
optionsSerial: 0, settingsTouched: false, catalogMessage: "正在读取模型目录…", chatMessage: "",
|
|
3820
|
+
workspace: structuredClone(plan?.target_kind === "binding" || source.targetSession ? null
|
|
3821
|
+
: plan ? plan.workspace : { mode: "project" }),
|
|
3822
|
+
workspaceOptions: null, workspaceError: null, workspaceLoading: false };
|
|
3671
3823
|
const rule = plan?.schedule || { kind: "daily", at: "09:00", timezone: source.timezone || state.schedules?.default_timezone || "" };
|
|
3672
3824
|
scheduleInput("editor").hidden = false;
|
|
3673
3825
|
scheduleInput("editor-title").textContent = plan ? `编辑计划 · ${plan.name}` : "创建计划";
|
|
@@ -3748,6 +3900,10 @@ async function saveSchedule(event) {
|
|
|
3748
3900
|
showScheduleFormError("配置已更新,请重新预览后保存。");
|
|
3749
3901
|
return;
|
|
3750
3902
|
}
|
|
3903
|
+
if (!scheduleWorkspaceCanSave()) {
|
|
3904
|
+
validateScheduleFields({ saving: true });
|
|
3905
|
+
return;
|
|
3906
|
+
}
|
|
3751
3907
|
scheduleEditorSerial += 1;
|
|
3752
3908
|
const action = editor.action;
|
|
3753
3909
|
const hasFuture = prepared.hasFuture;
|
|
@@ -3765,6 +3921,12 @@ async function saveSchedule(event) {
|
|
|
3765
3921
|
} else {
|
|
3766
3922
|
definition.project = scheduleInput("project").value.trim();
|
|
3767
3923
|
definition.chat_id = scheduleInput("chat").value.trim();
|
|
3924
|
+
if (scheduleWorkspaceChanged()) {
|
|
3925
|
+
definition.workspace = structuredClone(editor.workspace);
|
|
3926
|
+
if (editor.workspaceOptions?.projectAlias === definition.project) {
|
|
3927
|
+
definition.expected_project_revision = editor.workspaceOptions.projectRevision;
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3768
3930
|
}
|
|
3769
3931
|
const settings = scheduleSessionPatch();
|
|
3770
3932
|
if (Object.keys(settings).length) definition.session_settings = settings;
|
|
@@ -3904,7 +4066,8 @@ async function loadSchedules(cursor = undefined, appliedQuery = scheduleListQuer
|
|
|
3904
4066
|
for (const plan of data.plans) {
|
|
3905
4067
|
const row = document.createElement("tr");
|
|
3906
4068
|
cell(row, plan.name);
|
|
3907
|
-
cell(row, plan.project_alias);
|
|
4069
|
+
const project = cell(row, plan.project_alias);
|
|
4070
|
+
scheduleNote(project, workspacePolicySummary(plan.workspace, { scheduled: true }));
|
|
3908
4071
|
const target = document.createElement("td");
|
|
3909
4072
|
target.className = "schedule-target";
|
|
3910
4073
|
const link = document.createElement("a");
|
|
@@ -3937,6 +4100,7 @@ async function loadSchedules(cursor = undefined, appliedQuery = scheduleListQuer
|
|
|
3937
4100
|
const source = scheduleTriggerSource(plan.execution.trigger_source);
|
|
3938
4101
|
if (source) scheduleNote(execution, source);
|
|
3939
4102
|
if (plan.execution.due_local) scheduleNote(execution, scheduleLocalTime(plan.execution.due_local));
|
|
4103
|
+
if (plan.execution.worktree_id) scheduleNote(execution, scheduleWorktreeReceipt(plan.execution, { details: false }));
|
|
3940
4104
|
row.append(execution);
|
|
3941
4105
|
const actions = actionsCell(row);
|
|
3942
4106
|
const detail = actionButton("详情 / 最近记录", () => showSchedule(plan.id, detail));
|
|
@@ -4021,12 +4185,15 @@ async function showSchedule(planId, returnFocus = document.activeElement) {
|
|
|
4021
4185
|
scheduleInput("detail-state").textContent = `${schedulePlanState(data.plan).replace("\n", " · ")} · ${scheduleExecutionLabel(data.plan.execution)}`
|
|
4022
4186
|
+ (scheduleTriggerSource(data.plan.execution.trigger_source) ? ` · ${scheduleTriggerSource(data.plan.execution.trigger_source)}` : "")
|
|
4023
4187
|
+ (scheduleBlockedLabel(data.plan) ? ` · ${scheduleBlockedLabel(data.plan)}` : "")
|
|
4188
|
+
+ (data.plan.execution.worktree_id ? ` · ${scheduleWorktreeReceipt(data.plan.execution)}` : "")
|
|
4024
4189
|
+ ` · 目标会话 ID:${data.plan.chat_id}`
|
|
4025
4190
|
+ (data.inflight ? "。编辑、暂停或删除不会取消本次执行。" : "");
|
|
4026
4191
|
scheduleInput("detail-instructions").textContent = data.plan.instructions;
|
|
4027
4192
|
scheduleInput("detail-settings").textContent = data.plan.target_kind === "binding"
|
|
4028
4193
|
? `${scheduleTargetLabel(data.plan)} · 沿用原会话当前配置;系统输入不新增 @ 通知。`
|
|
4029
4194
|
: scheduleSessionSummary(data.plan.session_settings || defaultScheduleSessionSettings());
|
|
4195
|
+
scheduleInput("detail-settings").textContent += ` · ${data.plan.target_kind === "binding"
|
|
4196
|
+
? scheduleInheritedWorkspace(data.plan) : workspacePolicySummary(data.plan.workspace, { scheduled: true })}`;
|
|
4030
4197
|
scheduleInput("detail-rule").textContent = scheduleRuleLabel(data.plan.schedule);
|
|
4031
4198
|
renderSchedulePreview("#schedule-detail-preview", data.preview);
|
|
4032
4199
|
scheduleInput("detail-note").textContent = "";
|
|
@@ -4064,7 +4231,8 @@ async function loadScheduleRuns(page = scheduleRunsPage) {
|
|
|
4064
4231
|
const row = document.createElement("tr");
|
|
4065
4232
|
cell(row, scheduleDate(run.due_at));
|
|
4066
4233
|
cell(row, scheduleTriggerSource(run.trigger_source));
|
|
4067
|
-
cell(row, scheduleRunStatus(run.status || run.stage));
|
|
4234
|
+
const result = cell(row, scheduleRunStatus(run.status || run.stage));
|
|
4235
|
+
if (run.worktree_id) scheduleNote(result, scheduleWorktreeReceipt(run));
|
|
4068
4236
|
const links = actionsCell(row);
|
|
4069
4237
|
if (run.feishu_url) {
|
|
4070
4238
|
const link = document.createElement("a");
|
|
@@ -4539,8 +4707,13 @@ for (const eventName of ["input", "change"]) {
|
|
|
4539
4707
|
scheduleInput("kind").addEventListener("change", invalidateSchedulePreview);
|
|
4540
4708
|
scheduleInput("editor").addEventListener("submit", saveSchedule);
|
|
4541
4709
|
scheduleInput("chat").addEventListener("change", loadScheduleSessionOptions);
|
|
4710
|
+
scheduleInput("project").addEventListener("change", changeScheduleProject);
|
|
4711
|
+
for (const field of ["workspace", "worktree"]) {
|
|
4712
|
+
scheduleInput(field).addEventListener("change", () => changeScheduleWorkspace(field));
|
|
4713
|
+
}
|
|
4714
|
+
scheduleInput("workspace-ref").addEventListener("input", () => changeScheduleWorkspace("workspace-ref"));
|
|
4542
4715
|
scheduleInput("target-kind").addEventListener("change", () => {
|
|
4543
|
-
for (const field of ["binding", "project", "chat"]) clearScheduleFieldError(field);
|
|
4716
|
+
for (const field of ["binding", "project", "chat", "workspace"]) clearScheduleFieldError(field);
|
|
4544
4717
|
invalidateSchedulePreview();
|
|
4545
4718
|
renderScheduleTarget();
|
|
4546
4719
|
loadScheduleSessionOptions();
|
|
@@ -379,6 +379,12 @@
|
|
|
379
379
|
<button id="schedule-select-session" type="button">前往 Sessions 选择会话</button>
|
|
380
380
|
</div>
|
|
381
381
|
<label id="schedule-project-field">Project<select id="schedule-project" required><option value="">选择 Project</option></select></label>
|
|
382
|
+
<div id="schedule-workspace-fields" class="schedule-section-fields">
|
|
383
|
+
<label>工作区策略<select id="schedule-workspace"><option value="project">项目目录</option><option value="existing">指定已有 Worktree</option><option value="new">每次执行创建 Worktree</option></select></label>
|
|
384
|
+
<label id="schedule-worktree-field" hidden>受管 Worktree<select id="schedule-worktree"></select></label>
|
|
385
|
+
<label id="schedule-workspace-ref-field" hidden>起点提交 / 分支<input id="schedule-workspace-ref" list="schedule-workspace-refs" maxlength="256" placeholder="留空使用执行时 Project 的 HEAD"><datalist id="schedule-workspace-refs"></datalist></label>
|
|
386
|
+
<p id="schedule-workspace-help" class="schedule-field-help" role="status"></p>
|
|
387
|
+
</div>
|
|
382
388
|
<div id="schedule-chat-target" class="chat-target">
|
|
383
389
|
<label>聊天类型<select id="schedule-chat-kind"><option value="group">群聊</option><option value="p2p">单聊</option><option value="unknown" hidden>类型待确认</option></select></label>
|
|
384
390
|
<div id="schedule-group-picker"></div>
|
|
@@ -388,7 +394,7 @@
|
|
|
388
394
|
<p id="schedule-chat-id-help" class="defaults-field-help" hidden>请填写聊天 ID,不是用户 ID;单聊请选择与本机器人的聊天。<a href="https://open.feishu.cn/document/server-docs/group/chat/chat-id-description" target="_blank" rel="noopener noreferrer">如何获取聊天 ID</a></p>
|
|
389
395
|
</div>
|
|
390
396
|
<div id="schedule-target-note" class="schedule-field-help" hidden>
|
|
391
|
-
<p>沿用原会话的 Project
|
|
397
|
+
<p>沿用原会话的 Project、实际工作目录、模型、上下文和反馈;空闲时启动新一轮,忙碌时追加当前任务。</p>
|
|
392
398
|
<p>切走或归档后自动暂停,恢复后继续;删除原会话也会删除计划。目标创建后不可更换。</p>
|
|
393
399
|
</div>
|
|
394
400
|
</div>
|
|
@@ -853,7 +853,7 @@ class AdminWebApplication:
|
|
|
853
853
|
"view": {"mode", "plan_id"},
|
|
854
854
|
"runs": {"mode", "plan_id", "cursor"},
|
|
855
855
|
"preview": {"mode", "schedule", "plan_id", "chat_id", "session_settings", "target_kind", "target_binding_id", "local_at", "utc_offset", "local_end_at", "end_utc_offset"},
|
|
856
|
-
"options": {"mode", "chat_id", "binding_query"},
|
|
856
|
+
"options": {"mode", "chat_id", "binding_query", "project"},
|
|
857
857
|
}
|
|
858
858
|
if mode not in allowed:
|
|
859
859
|
raise AdminWebError(400, "invalid_query", "定时任务查询类型无效。")
|
|
@@ -974,7 +974,7 @@ class AdminWebApplication:
|
|
|
974
974
|
)
|
|
975
975
|
definition = payload.get("definition", {})
|
|
976
976
|
if not isinstance(definition, dict) or set(definition) - {
|
|
977
|
-
"name", "instructions", "project", "chat_id", "schedule", "enabled", "session_settings", "target_kind", "target_binding_id",
|
|
977
|
+
"name", "instructions", "project", "expected_project_revision", "chat_id", "schedule", "enabled", "session_settings", "workspace", "target_kind", "target_binding_id",
|
|
978
978
|
}:
|
|
979
979
|
raise AdminWebError(400, "invalid_input", "定时任务定义字段无效。")
|
|
980
980
|
request: dict[str, object] = {
|
|
@@ -2950,7 +2950,7 @@ def _require_schedule_success(result: dict[str, Any]) -> None:
|
|
|
2950
2950
|
status = {
|
|
2951
2951
|
"revision_conflict": 409, "request_conflict": 409,
|
|
2952
2952
|
"run_in_progress": 409, "blocked_unknown": 409,
|
|
2953
|
-
"project_disabled": 409, "project_unavailable": 409,
|
|
2953
|
+
"project_disabled": 409, "project_unavailable": 409, "project_changed": 409, "workspace_unavailable": 409,
|
|
2954
2954
|
"not_found": 404, "unavailable": 503,
|
|
2955
2955
|
}.get(code, 400)
|
|
2956
2956
|
raise AdminWebError(status, code, error["message"])
|
|
@@ -39,7 +39,7 @@ from .worktrees.models import WorktreeRecord
|
|
|
39
39
|
from .worktrees.store import WorktreeStore, create_schema as create_worktree_schema
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
SCHEMA_VERSION =
|
|
42
|
+
SCHEMA_VERSION = 17
|
|
43
43
|
|
|
44
44
|
# Includes the running query and all submitted queries waiting for the reader.
|
|
45
45
|
_ADMIN_QUERY_CAPACITY = 32
|
|
@@ -466,7 +466,7 @@ def _require_current_schema(connection: sqlite3.Connection) -> None:
|
|
|
466
466
|
_require_project_metadata_schema(connection)
|
|
467
467
|
require_schema(connection)
|
|
468
468
|
require_defaults_schema(connection)
|
|
469
|
-
from .migrations.
|
|
469
|
+
from .migrations.v17 import validate
|
|
470
470
|
validate(connection)
|
|
471
471
|
|
|
472
472
|
|
|
@@ -1024,6 +1024,7 @@ class BindingStore:
|
|
|
1024
1024
|
run.target_kind != "new_topic"
|
|
1025
1025
|
or run.barrier != "held" or run.phase != "publishing_topic"
|
|
1026
1026
|
or run.binding_id is not None
|
|
1027
|
+
or run.worktree_id != worktree_id
|
|
1027
1028
|
or run.project_alias != project_alias
|
|
1028
1029
|
or scope.kind is not ScopeKind.TOPIC
|
|
1029
1030
|
or (run.app_id, run.chat_id, run.topic_id)
|
|
@@ -1154,6 +1155,8 @@ class BindingStore:
|
|
|
1154
1155
|
creator_id: str = "scheduled_plan",
|
|
1155
1156
|
session_settings: SessionSettings = SessionSettings(),
|
|
1156
1157
|
context_anchor: MessageContextAnchor | None = None,
|
|
1158
|
+
working_cwd: str | None = None, worktree_id: str | None = None,
|
|
1159
|
+
expected_project_revision: int | None = None,
|
|
1157
1160
|
) -> ThreadBinding:
|
|
1158
1161
|
if not isinstance(session_settings, SessionSettings):
|
|
1159
1162
|
raise ValueError("scheduled session_settings must be SessionSettings")
|
|
@@ -1163,7 +1166,8 @@ class BindingStore:
|
|
|
1163
1166
|
turn_settings=session_settings.turn_settings,
|
|
1164
1167
|
task_feedback=session_settings.task_feedback,
|
|
1165
1168
|
message_context_mode=session_settings.message_context_mode,
|
|
1166
|
-
context_anchor=context_anchor, expected_project_revision=
|
|
1169
|
+
context_anchor=context_anchor, expected_project_revision=expected_project_revision,
|
|
1170
|
+
working_cwd=working_cwd, worktree_id=worktree_id,
|
|
1167
1171
|
scheduled_run_id=run_id,
|
|
1168
1172
|
)
|
|
1169
1173
|
|