netizen-cli 0.14.4__tar.gz → 0.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {netizen_cli-0.14.4/netizen_cli.egg-info → netizen_cli-0.15.0}/PKG-INFO +1 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/README.md +10 -6
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/config.example.yaml +6 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/__init__.py +1 -1
- netizen_cli-0.15.0/netizen_cli/admin/file_previews.py +205 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/admin.css +11 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/admin.js +295 -29
- netizen_cli-0.15.0/netizen_cli/admin/static/attachment-files.js +49 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/file-browser.js +751 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/index.html +114 -2
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/login.html +2 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/netizen.svg +5 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/project-files.css +130 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/project-files.js +34 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/question-notifications.js +91 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/vendor/ace/LICENSE +24 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/vendor/ace/README.md +22 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/vendor/ace/ace.js +23 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/vendor/ace/mode-javascript.js +8 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/vendor/ace/mode-json.js +8 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/vendor/ace/mode-markdown.js +8 -0
- netizen_cli-0.15.0/netizen_cli/admin/static/vendor/ace/mode-python.js +8 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/web.py +287 -2
- netizen_cli-0.15.0/netizen_cli/attachments.py +478 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/callbacks.py +1 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/controls.py +22 -5
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/questions.py +10 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/reply.py +9 -3
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/scheduled.py +20 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/status.py +1 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/completion_mentions.py +20 -8
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/input_preparation.py +53 -7
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/reply_presenter.py +7 -0
- netizen_cli-0.15.0/netizen_cli/channel/reply_suppression.py +37 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel_app.py +347 -43
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/codex_runtime.py +143 -8
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/domain.py +1 -0
- netizen_cli-0.15.0/netizen_cli/file_inputs.py +401 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/image_inputs.py +17 -7
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/main.py +10 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/management/__init__.py +2 -0
- netizen_cli-0.15.0/netizen_cli/management/attachment_files.py +47 -0
- netizen_cli-0.15.0/netizen_cli/management/project_files.py +118 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/management/service.py +130 -12
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/mcp_transport.py +31 -6
- netizen_cli-0.15.0/netizen_cli/project_files.py +337 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/prompt_projection.py +1 -1
- netizen_cli-0.15.0/netizen_cli/question_notifications.py +64 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/runtime/contracts.py +10 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/runtime/thread_naming.py +67 -6
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/session_inventory.py +2 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/mcp.py +16 -5
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/schema.py +70 -13
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/service.py +17 -2
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/settings.py +7 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/worktrees/service.py +20 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0/netizen_cli.egg-info}/PKG-INFO +1 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli.egg-info/SOURCES.txt +31 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/pyproject.toml +2 -2
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/admin.md +99 -9
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/commands.md +1 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/feedback-and-files.md +36 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/messages.md +22 -2
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/scheduled-tasks.md +6 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/session-collaboration.md +4 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/sessions.md +4 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/status-and-control.md +5 -3
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/user-guide.md +3 -2
- netizen_cli-0.15.0/tests/test_attachments.py +508 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_binding_scheduled_channel.py +41 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cards.py +2 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli_distribution.py +22 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli_packaging.py +14 -2
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_codex_runtime.py +330 -7
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_completion_mentions.py +151 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_completion_topic_delivery.py +3 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_deployment.py +5 -2
- netizen_cli-0.15.0/tests/test_file_input_channel.py +464 -0
- netizen_cli-0.15.0/tests/test_file_inputs.py +504 -0
- netizen_cli-0.15.0/tests/test_latest_answer.py +77 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_main.py +12 -0
- netizen_cli-0.15.0/tests/test_project_files.py +326 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_question_cards.py +23 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_question_channel.py +30 -0
- netizen_cli-0.15.0/tests/test_question_notification_config.py +106 -0
- netizen_cli-0.15.0/tests/test_rename_name_channel.py +248 -0
- netizen_cli-0.15.0/tests/test_reply_suppression.py +32 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_runtime_thread_naming.py +214 -3
- netizen_cli-0.15.0/tests/test_schedule_trigger_cards.py +66 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_scheduled_channel.py +30 -3
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_external_runtime.py +84 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_tools.py +244 -14
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_status_cards.py +8 -2
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_status_commands.py +46 -4
- netizen_cli-0.15.0/tests/test_suppress_reply.py +831 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_thread_naming.py +184 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_worktree_service.py +68 -1
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/MANIFEST.in +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/_build.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/__main__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/account_rate_limits.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/auth.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/credentials.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/errors.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/feishu.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/login.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/port_config.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/presentation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/queries.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/chat-picker.css +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/chat-picker.js +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/identity.js +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/login.js +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/progress-card.js +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/session-access.js +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/static/worktrees.js +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/transport.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/admin/users.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/bindings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/builtin_skills.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/chat_target.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/defaults.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/fork.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/pagination.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/session_ids.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/usage.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cards/workspace.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/messages.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/ports.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/question_inputs.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/reactions.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/session_pages.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/channel/topics.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/chat_avatars.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/chat_targets.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cli.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cli_data.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cli_packages.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cli_services.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cli_setup.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cli_update.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cli_update_restore.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/cli_update_worker.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/completion_mention.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/database_migrations.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/defaults/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/defaults/models.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/defaults/service.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/defaults/store.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/deployment/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/deployment/restart_worker.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/deployment/update_executor.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/deployment/update_protocol.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/error_messages.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/experience.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/feishu_app_onboarding.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/feishu_app_permissions.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/feishu_links.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/git_status.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/instance.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/lark_app.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/management/blocking_io.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/management/chat_directory.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/management/chat_labels.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/management/coordination.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/management/updates.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/markdown_images.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/message_content.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/message_history.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/message_preparation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/message_projection.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/schema.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/v14.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/v15.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/v16.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/v17.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/v18.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/v19.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/migrations/v20.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/model_settings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/package_resources.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/pagination.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/partial_answers.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/progress_card_config.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/progress_card_settings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/projects.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/quoted_context.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/result_images.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/runtime/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/runtime/name_writes.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/schedules/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/schedules/mcp.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/schedules/models.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/schedules/scheduler.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/schedules/service.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/schedules/store.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sdk_gap_adapter.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/service_launcher.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/session_settings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/__init__.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/delegation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/errors.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/policy.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/presentation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/sessions/sources.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/skill_references.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/task_text.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/terminal_cleanup.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/thread_history.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/turn_activity.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/turn_files.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/turn_patch_children.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/turn_plan_observer.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/user_questions.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/worktrees/errors.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/worktrees/git.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/worktrees/models.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/worktrees/policy.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/worktrees/settings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli/worktrees/store.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli.egg-info/dependency_links.txt +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli.egg-info/entry_points.txt +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli.egg-info/requires.txt +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/netizen_cli.egg-info/top_level.txt +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/setup.cfg +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-lark/SKILL.md +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-runtime-guidance/SKILL.md +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/SKILL.md +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/concepts.md +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/faq.md +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/fork-side-goal.md +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/skills/netizen-user-guide/references/native-capabilities.md +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_account_rate_limits.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_admin_update_executor_probe.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_bindings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_card_error_feedback.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_channel_app.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_channel_fixtures.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_channel_input_preparation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_chat_avatars.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_chat_target_cards.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_check_sdk.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli_data.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli_integration.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli_packages.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli_services.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli_setup.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cli_update.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_codex_sdk_capabilities.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_codex_sdk_upgrade_0161.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_codex_sdk_upgrade_0163.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_completion_mention_cards.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_configuration_publication.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_cron_chat_targets.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_database_migrations.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_defaults_cards.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_defaults_channel.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_defaults_runtime.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_defaults_worktree_channel.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_documentation_links.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_domain_experience.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_error_messages.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_feishu_app_onboarding.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_feishu_app_permissions.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_file_pagination.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_fork_cards.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_git_status.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_goal_reply_separation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_image_inputs.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_lark_app.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_lark_sdk_contract.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_markdown_images.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_message_content.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_message_history.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_message_material_inputs.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_message_preparation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_migration_schema_validation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_model_settings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_name_writes.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_optional_progress_card_settings_migration.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_pagination.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_partial_answer_delivery.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_partial_answers.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_persistent_fork_bindings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_persistent_fork_channel.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_persistent_fork_runtime.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_child_files.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_feishu_completion_mention.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_feishu_partial_answers.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_feishu_turn_file_card.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_persistent_fork.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_python_sdk.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_scheduled_tasks.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_sdk_completion_race.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_sdk_response_recovery.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_skill_execution.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_probe_thread_naming.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_progress_card_config.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_progress_card_entrypoints.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_progress_card_presentation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_progress_card_settings_migration.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_project_delete_channel.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_project_delete_runtime.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_project_deletion_storage.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_prompt_projection.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_question_target_parity.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_quoted_context.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_release_script.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_repository_hygiene.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_result_images.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_resume_channel.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_retired_deployment.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_runtime_resume_activation.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_auto_archive_migration.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_card_retry.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_cards.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_mcp.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_run_now.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_scope_matrix.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_service.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_workspace_cards.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_workspace_migration.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_workspace_service.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedule_workspace_storage.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_scheduled_auto_archive.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_scheduled_runtime.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_scheduled_worktrees.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_scheduler.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_scheduler_presenter.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_schedules_storage.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_sdk_gap_adapter.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_service_launcher.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_card_pages.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_collaboration_app.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_defaults.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_defaults_migration.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_id_cards.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_policy.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_session_settings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_settings_projects.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_task_text.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_terminal_cleanup.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_thread_history.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_turn_activity.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_turn_files.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_turn_patch_children.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_turn_patch_statistics.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_turn_plan_observer.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_update_executor.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_usage_card.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_usage_control.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_user_guide_skill.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_user_questions.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_worktree_channel_cwd.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_worktree_git.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_worktree_new.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_worktree_runtime_cwd.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_worktree_settings.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_worktree_storage.py +0 -0
- {netizen_cli-0.14.4 → netizen_cli-0.15.0}/tests/test_worktree_tools_integration.py +0 -0
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
管理的原生会话执行,沿用运行主机上的工具和工作目录。
|
|
38
38
|
- **边做边沟通。** 普通任务执行时继续发消息,可以补充条件或调整方向;也可以开启
|
|
39
39
|
[过程卡](skills/netizen-user-guide/references/feedback-and-files.md#飞书中的运行反馈)查看进展。
|
|
40
|
-
-
|
|
41
|
-
[
|
|
40
|
+
- **带上文件、图片与讨论背景。** 支持图片、逐条引用,以及可选的群聊上下文;
|
|
41
|
+
[文件附件](skills/netizen-user-guide/references/messages.md#图片与其他附件)先保存原件到本地,供 Agent 按需读取。
|
|
42
42
|
- **取回本轮文件。** 完成后从回复卡片中按需发送有原生执行记录的文件;发送的是点击时
|
|
43
43
|
的当前内容,详见[文件说明](skills/netizen-user-guide/references/feedback-and-files.md#查看和发送本轮文件)。
|
|
44
44
|
- **保留上下文,另开一条讨论。** 用
|
|
@@ -171,10 +171,12 @@ Side 会在同一聊天中新建话题,可继续多轮讨论。它与原会话
|
|
|
171
171
|
## 管理与维护
|
|
172
172
|
|
|
173
173
|
实例管理员可以通过 **Admin Web** 集中管理项目、会话、Side 话题、会话默认配置和定时任务,
|
|
174
|
-
|
|
174
|
+
会话列表支持关键词搜索、本页按项目分组和查看最近一轮回答;“系统管理”可配置提问时 @
|
|
175
|
+
发起人(默认关闭),并查看、逐个删除输入附件。“系统维护”提供安装信息和服务重启。
|
|
176
|
+
发送 `/admin` 可获取当前实例的管理 URL
|
|
175
177
|
和根目录,无需创建会话。未指定端口时首次从 `8787` 起分配并保存,各实例独立监听;
|
|
176
178
|
管理页使用飞书登录,并核对当前应用的管理员名单,命令不会返回凭据或免登录链接。
|
|
177
|
-
Admin 是实例必备能力;管理员可在“系统管理 →
|
|
179
|
+
Admin 是实例必备能力;管理员可在“系统管理 → 管理员”中维护名单。
|
|
178
180
|
服务重启后需要重新登录。
|
|
179
181
|
[访问方式](docs/deployment.md#配置与管理页访问)见部署文档。飞书登录与管理员维护描述
|
|
180
182
|
当前源码行为,真人和平台验收尚未完成,见[验收状态](docs/deployment.md#admin-飞书登录与管理员维护验收)。
|
|
@@ -188,8 +190,10 @@ Admin 是实例必备能力;管理员可在“系统管理 → 权限管理
|
|
|
188
190
|
- **共享环境。** 飞书应用的可用范围和群成员关系控制谁能访问;同一实例复用服务账号,
|
|
189
191
|
不提供按用户或项目隔离的权限体系。Admin Web 面向受信内网,名单中的管理员权限相同。
|
|
190
192
|
- **输入与输出。** 支持文本、普通图片和富文本图片,也可提供飞书 2.0 卡片、合并转发与转发话题作为
|
|
191
|
-
[背景材料](skills/netizen-user-guide/references/messages.md#卡片合并转发与转发话题)
|
|
192
|
-
|
|
193
|
+
[背景材料](skills/netizen-user-guide/references/messages.md#卡片合并转发与转发话题)。当前消息和准确逐条引用中的普通文件
|
|
194
|
+
可下载为输入原件;任一文件准备失败则整条输入不提交。补充历史、卡片和合并转发内部的
|
|
195
|
+
普通文件只提供附件描述。文件夹、音视频和飞书 1.0 卡片不支持。
|
|
196
|
+
输入原件可在 Admin 清理,删除可能影响正在执行或后续任务读取。
|
|
193
197
|
输出文件按本轮原生记录发现,不扫描工作区补齐,也不自动上传。
|
|
194
198
|
- **Codex 能力有宿主差异。** 飞书复用原生会话和工具,但并非每个 App/CLI 控件都可用;
|
|
195
199
|
全新会话采用 `auto_review`,持久分支沿用原生继承权限;不提供 App 的 Ask/Custom 选择器。
|
|
@@ -33,6 +33,12 @@ sessionTools:
|
|
|
33
33
|
mode: all
|
|
34
34
|
chatIds: []
|
|
35
35
|
|
|
36
|
+
# Independent instance-wide mentions on each new native question card.
|
|
37
|
+
# Admin -> System -> Question notifications can change this at runtime.
|
|
38
|
+
# Existing cards are not mentioned again; progress/completion settings are independent.
|
|
39
|
+
questionNotifications:
|
|
40
|
+
mentionInitiator: false
|
|
41
|
+
|
|
36
42
|
# Required management UI, using this app's Feishu administrator identities.
|
|
37
43
|
# It listens on all interfaces for direct access inside the trusted private network.
|
|
38
44
|
adminWeb:
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Bounded, inert Markdown previews for the authenticated Project file browser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Iterator, Sequence
|
|
6
|
+
from urllib.parse import unquote, urlsplit
|
|
7
|
+
|
|
8
|
+
from markdown_it import MarkdownIt
|
|
9
|
+
from markdown_it.token import Token
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
MAX_MARKDOWN_BYTES = 256 * 1024
|
|
13
|
+
MAX_PREVIEW_BYTES = 1024 * 1024
|
|
14
|
+
MAX_MARKDOWN_TOKENS = 20_000
|
|
15
|
+
MAX_MARKDOWN_IMAGES = 8
|
|
16
|
+
|
|
17
|
+
_MARKDOWN = MarkdownIt("commonmark", {
|
|
18
|
+
"html": False,
|
|
19
|
+
"linkify": False,
|
|
20
|
+
"maxNesting": 20,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PreviewUnavailable(ValueError):
|
|
25
|
+
"""The source remains viewable, but its rendered preview exceeds a budget."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _unsafe_characters(value: str) -> bool:
|
|
29
|
+
return "\\" in value or any(
|
|
30
|
+
ord(char) < 32 or 127 <= ord(char) <= 159 for char in value
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _local_path(value: str, directory: list[str]) -> str | None:
|
|
35
|
+
if value.startswith("/") or _unsafe_characters(value):
|
|
36
|
+
return None
|
|
37
|
+
parts = directory.copy()
|
|
38
|
+
for part in value.split("/"):
|
|
39
|
+
if part.casefold() == ".git":
|
|
40
|
+
return None
|
|
41
|
+
if part in ("", "."):
|
|
42
|
+
continue
|
|
43
|
+
if part == "..":
|
|
44
|
+
if not parts:
|
|
45
|
+
return None
|
|
46
|
+
parts.pop()
|
|
47
|
+
else:
|
|
48
|
+
parts.append(part)
|
|
49
|
+
return "/".join(parts)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _target(value: str, directory: list[str]) -> tuple[str, str] | None:
|
|
53
|
+
"""Classify decoded URLs without allowing another decoding pass to change them."""
|
|
54
|
+
if _unsafe_characters(value):
|
|
55
|
+
return None
|
|
56
|
+
try:
|
|
57
|
+
decoded = unquote(value, errors="strict")
|
|
58
|
+
if _unsafe_characters(decoded) or unquote(decoded, errors="strict") != decoded:
|
|
59
|
+
return None
|
|
60
|
+
parts = urlsplit(value)
|
|
61
|
+
decoded_parts = urlsplit(decoded)
|
|
62
|
+
# Decoding may reveal an unsafe scheme or authority, but encoded # and
|
|
63
|
+
# ? in a local filename must not become URL separators.
|
|
64
|
+
if decoded_parts.scheme != parts.scheme or decoded_parts.netloc != parts.netloc:
|
|
65
|
+
return None
|
|
66
|
+
except (UnicodeError, ValueError):
|
|
67
|
+
return None
|
|
68
|
+
if parts.scheme:
|
|
69
|
+
if parts.scheme not in {"http", "https", "mailto"}:
|
|
70
|
+
return None
|
|
71
|
+
if parts.scheme in {"http", "https"} and not parts.netloc:
|
|
72
|
+
return None
|
|
73
|
+
if parts.scheme == "mailto" and not parts.path:
|
|
74
|
+
return None
|
|
75
|
+
return "external", value
|
|
76
|
+
if parts.netloc or parts.query or not parts.path:
|
|
77
|
+
# Headings deliberately have no ids. Pure fragments cannot navigate the
|
|
78
|
+
# containing Admin document; file fragments open that file from the top.
|
|
79
|
+
return None
|
|
80
|
+
local_path = unquote(parts.path, errors="strict")
|
|
81
|
+
if (relative := _local_path(local_path, directory)) is None:
|
|
82
|
+
return None
|
|
83
|
+
directory_hint = local_path.endswith("/") or local_path.rsplit("/", 1)[-1] in {".", ".."}
|
|
84
|
+
return "directory" if directory_hint else "local", relative
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _tokens(tokens: Sequence[Token]) -> Iterator[Token]:
|
|
88
|
+
for token in tokens:
|
|
89
|
+
yield token
|
|
90
|
+
if token.children:
|
|
91
|
+
yield from _tokens(token.children)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _replace_image(token: Token, reason: str) -> None:
|
|
95
|
+
label = " ".join(_image_alt(token.children or []).split())[:120] or "图片"
|
|
96
|
+
token.type, token.tag = "text", ""
|
|
97
|
+
token.content = f"〔图片:{label};{reason}〕"
|
|
98
|
+
token.attrs, token.children = {}, None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _image_alt(tokens: Sequence[Token]) -> str:
|
|
102
|
+
# markdown-it-py's default image alt renderer omits text_special/code_inline.
|
|
103
|
+
# Flatten those parsed tokens to plain text so entities and code stay visible,
|
|
104
|
+
# and nested Markdown images never become independent resource requests.
|
|
105
|
+
parts: list[str] = []
|
|
106
|
+
for token in tokens:
|
|
107
|
+
if token.type in {"text", "text_special", "code_inline"}:
|
|
108
|
+
parts.append(token.content)
|
|
109
|
+
elif token.type in {"softbreak", "hardbreak"}:
|
|
110
|
+
parts.append("\n")
|
|
111
|
+
elif token.children:
|
|
112
|
+
parts.append(_image_alt(token.children))
|
|
113
|
+
return "".join(parts)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _escaped_bytes(value: str) -> int:
|
|
117
|
+
return (
|
|
118
|
+
len(value.encode("utf-8"))
|
|
119
|
+
+ value.count("&") * 4
|
|
120
|
+
+ value.count('"') * 5
|
|
121
|
+
+ (value.count("<") + value.count(">")) * 3
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _check_render_budget(tokens: Sequence[Token]) -> None:
|
|
126
|
+
# A conservative upper bound before rendering also limits reference-link
|
|
127
|
+
# amplification: many small [x] uses can share a very long URL/title. Count
|
|
128
|
+
# inline source and image children too rather than underestimate HTML output.
|
|
129
|
+
size = 0
|
|
130
|
+
for token in _tokens(tokens):
|
|
131
|
+
size += 64 + 2 * len(token.tag) + _escaped_bytes(token.content)
|
|
132
|
+
size += _escaped_bytes(token.info)
|
|
133
|
+
for key, value in token.attrs.items():
|
|
134
|
+
size += len(key) + 4 + _escaped_bytes(str(value))
|
|
135
|
+
if size > MAX_PREVIEW_BYTES:
|
|
136
|
+
raise PreviewUnavailable("Markdown 预览内容过大,请查看源码。")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def render_markdown(
|
|
140
|
+
text: str,
|
|
141
|
+
*,
|
|
142
|
+
path: str,
|
|
143
|
+
image_url: Callable[[str], str],
|
|
144
|
+
) -> str:
|
|
145
|
+
"""Render safe HTML; image_url maps a root-relative file to its Admin endpoint.
|
|
146
|
+
|
|
147
|
+
Local links carry ``data-file-browser-path`` for the file browser to intercept;
|
|
148
|
+
the empty path identifies its root. Explicit directory targets additionally
|
|
149
|
+
carry ``data-file-browser-directory="true"``.
|
|
150
|
+
Remote images and fragment-only links remain inert. No filesystem or network
|
|
151
|
+
access occurs here; the file endpoint separately validates image contents.
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
length = len(text.encode("utf-8"))
|
|
155
|
+
except UnicodeError as exc:
|
|
156
|
+
raise PreviewUnavailable("文件包含无法预览的文本编码,请查看源码。") from exc
|
|
157
|
+
if length > MAX_MARKDOWN_BYTES:
|
|
158
|
+
raise PreviewUnavailable("Markdown 文件超过 256 KiB,请查看源码。")
|
|
159
|
+
if not path or _local_path(path, []) != path:
|
|
160
|
+
raise PreviewUnavailable("Markdown 文件路径无效,请查看源码。")
|
|
161
|
+
directory = path.split("/")[:-1]
|
|
162
|
+
env: dict[str, object] = {}
|
|
163
|
+
tokens = _MARKDOWN.parse(text, env)
|
|
164
|
+
if sum(1 for _ in _tokens(tokens)) > MAX_MARKDOWN_TOKENS:
|
|
165
|
+
raise PreviewUnavailable("Markdown 内容过于复杂,请查看源码。")
|
|
166
|
+
_check_render_budget(tokens)
|
|
167
|
+
|
|
168
|
+
images = 0
|
|
169
|
+
for token in _tokens(tokens):
|
|
170
|
+
if token.type == "link_open":
|
|
171
|
+
target = _target(token.attrGet("href") or "", directory)
|
|
172
|
+
token.attrs.pop("href", None)
|
|
173
|
+
if target is None:
|
|
174
|
+
continue
|
|
175
|
+
kind, value = target
|
|
176
|
+
if kind == "external":
|
|
177
|
+
token.attrSet("href", value)
|
|
178
|
+
token.attrSet("rel", "noopener noreferrer")
|
|
179
|
+
token.attrSet("target", "_blank")
|
|
180
|
+
else:
|
|
181
|
+
token.attrSet("href", "#")
|
|
182
|
+
token.attrSet("data-file-browser-path", value)
|
|
183
|
+
if kind == "directory":
|
|
184
|
+
token.attrSet("data-file-browser-directory", "true")
|
|
185
|
+
elif token.type == "image":
|
|
186
|
+
target = _target(token.attrGet("src") or "", directory)
|
|
187
|
+
if target is None or target[0] != "local":
|
|
188
|
+
_replace_image(token, "仅预览当前根目录内的相对路径图片")
|
|
189
|
+
elif images >= MAX_MARKDOWN_IMAGES:
|
|
190
|
+
_replace_image(token, "本页最多预览 8 张图片")
|
|
191
|
+
else:
|
|
192
|
+
token.children = [Token(
|
|
193
|
+
"text", "", 0, content=_image_alt(token.children or []),
|
|
194
|
+
)]
|
|
195
|
+
token.attrSet("src", image_url(target[1]))
|
|
196
|
+
token.attrSet("loading", "lazy")
|
|
197
|
+
token.attrSet("decoding", "async")
|
|
198
|
+
token.attrSet("referrerpolicy", "no-referrer")
|
|
199
|
+
images += 1
|
|
200
|
+
|
|
201
|
+
_check_render_budget(tokens)
|
|
202
|
+
rendered = _MARKDOWN.renderer.render(tokens, _MARKDOWN.options, env)
|
|
203
|
+
if len(rendered.encode("utf-8")) > MAX_PREVIEW_BYTES:
|
|
204
|
+
raise PreviewUnavailable("Markdown 预览内容过大,请查看源码。")
|
|
205
|
+
return rendered
|
|
@@ -20,7 +20,9 @@ body.time-range-modal-open { overflow: hidden; }
|
|
|
20
20
|
button, input, select, textarea { font: inherit; }
|
|
21
21
|
button { cursor: pointer; }
|
|
22
22
|
.topbar { display: flex; justify-content: space-between; align-items: center; gap: 24px; padding: 12px clamp(20px, 4vw, 56px); background: #10213f; color: white; }
|
|
23
|
-
.topbar-brand { flex: 1; min-width: 0; }
|
|
23
|
+
.topbar-brand { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; }
|
|
24
|
+
.topbar-brand-text { min-width: 0; }
|
|
25
|
+
.brand-logo { display: block; flex: none; }
|
|
24
26
|
.topbar h1 { font-size: 24px; line-height: 1.2; }
|
|
25
27
|
.topbar .instance-root { margin: 4px 0 0; line-height: 1.4; }
|
|
26
28
|
.topbar .identity-toolbar { flex-wrap: nowrap; max-width: 50%; }
|
|
@@ -203,6 +205,7 @@ button:focus-visible, input:focus-visible, select:focus-visible { outline: 3px s
|
|
|
203
205
|
.login-body { min-height: 100vh; min-height: 100svh; display: grid; place-items: center; padding: clamp(12px, 3vw, 24px); }
|
|
204
206
|
.login-card { width: min(420px, 100%); min-width: 0; margin: 0; padding: clamp(20px, 5vw, 28px); border: 1px solid var(--line); border-radius: 14px; background: var(--paper); box-shadow: 0 8px 28px rgb(23 39 68 / 6%); font-size: 14px; line-height: 1.6; }
|
|
205
207
|
.login-heading h1 { font-size: 28px; line-height: 1.2; }
|
|
208
|
+
.login-heading .brand-logo { margin-bottom: 12px; }
|
|
206
209
|
.login-heading p { margin: 8px 0 0; color: var(--muted); font-size: 13px; }
|
|
207
210
|
.login-instance { margin: 20px 0; padding: 10px 12px; border-radius: 8px; background: var(--wash); }
|
|
208
211
|
.login-instance > span { color: var(--muted); font-size: 12px; }
|
|
@@ -244,6 +247,8 @@ button:focus-visible, input:focus-visible, select:focus-visible { outline: 3px s
|
|
|
244
247
|
body.schedule-drawer-open, body.defaults-drawer-open, body.session-settings-drawer-open, body.workspace-drawer-open { overflow: hidden; }
|
|
245
248
|
.editor-drawer { position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0; width: min(720px, 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh; border: 0; color: var(--ink); background: var(--paper); box-shadow: -12px 0 40px rgb(23 39 68 / 16%); }
|
|
246
249
|
.editor-drawer::backdrop { background: rgb(16 33 63 / 35%); }
|
|
250
|
+
.rename-dialog { width: min(520px, calc(100vw - 32px)); padding: 0; border: 0; border-radius: 12px; color: var(--ink); background: var(--paper); box-shadow: 0 12px 40px rgb(23 39 68 / 16%); }
|
|
251
|
+
.rename-dialog::backdrop { background: rgb(16 33 63 / 35%); }
|
|
247
252
|
#schedule-editor { height: 100%; display: flex; flex-direction: column; border: 0; border-radius: 0; padding: 0; margin: 0; }
|
|
248
253
|
.schedule-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 22px 24px; border-bottom: 1px solid var(--line); }
|
|
249
254
|
.schedule-editor-head h3 { margin: 0; font-size: 22px; }
|
|
@@ -464,3 +469,8 @@ body.schedule-detail-open { overflow: hidden; }
|
|
|
464
469
|
.topbar-brand { flex-basis: 100%; }
|
|
465
470
|
.topbar .identity-toolbar { max-width: 100%; justify-content: flex-start; }
|
|
466
471
|
}
|
|
472
|
+
|
|
473
|
+
.session-layout { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin: 12px 0; }
|
|
474
|
+
.session-layout label { display: flex; align-items: center; gap: 8px; }
|
|
475
|
+
.session-project-group th { background: var(--wash); font-weight: 600; }
|
|
476
|
+
.session-answer-content { white-space: pre-wrap; overflow-wrap: anywhere; font-family: inherit; line-height: 1.65; }
|