netizen-cli 0.14.4__tar.gz → 0.14.5__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.
Files changed (336) hide show
  1. {netizen_cli-0.14.4/netizen_cli.egg-info → netizen_cli-0.14.5}/PKG-INFO +1 -1
  2. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/__init__.py +1 -1
  3. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/admin.css +4 -1
  4. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/index.html +6 -2
  5. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/login.html +2 -0
  6. netizen_cli-0.14.5/netizen_cli/admin/static/netizen.svg +5 -0
  7. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/web.py +3 -1
  8. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/scheduled.py +19 -0
  9. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel_app.py +16 -7
  10. {netizen_cli-0.14.4 → netizen_cli-0.14.5/netizen_cli.egg-info}/PKG-INFO +1 -1
  11. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli.egg-info/SOURCES.txt +2 -0
  12. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/pyproject.toml +2 -2
  13. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/scheduled-tasks.md +6 -0
  14. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_binding_scheduled_channel.py +19 -0
  15. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli_packaging.py +7 -2
  16. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_deployment.py +1 -1
  17. netizen_cli-0.14.5/tests/test_schedule_trigger_cards.py +66 -0
  18. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_scheduled_channel.py +30 -3
  19. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/MANIFEST.in +0 -0
  20. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/README.md +0 -0
  21. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/_build.py +0 -0
  22. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/config.example.yaml +0 -0
  23. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/__main__.py +0 -0
  24. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/account_rate_limits.py +0 -0
  25. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/__init__.py +0 -0
  26. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/auth.py +0 -0
  27. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/credentials.py +0 -0
  28. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/errors.py +0 -0
  29. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/feishu.py +0 -0
  30. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/login.py +0 -0
  31. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/port_config.py +0 -0
  32. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/presentation.py +0 -0
  33. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/queries.py +0 -0
  34. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/admin.js +0 -0
  35. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/chat-picker.css +0 -0
  36. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/chat-picker.js +0 -0
  37. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/identity.js +0 -0
  38. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/login.js +0 -0
  39. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/progress-card.js +0 -0
  40. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/session-access.js +0 -0
  41. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/static/worktrees.js +0 -0
  42. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/transport.py +0 -0
  43. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/admin/users.py +0 -0
  44. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/bindings.py +0 -0
  45. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/builtin_skills.py +0 -0
  46. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/__init__.py +0 -0
  47. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/callbacks.py +0 -0
  48. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/chat_target.py +0 -0
  49. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/controls.py +0 -0
  50. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/defaults.py +0 -0
  51. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/fork.py +0 -0
  52. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/pagination.py +0 -0
  53. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/questions.py +0 -0
  54. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/reply.py +0 -0
  55. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/session_ids.py +0 -0
  56. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/status.py +0 -0
  57. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/usage.py +0 -0
  58. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cards/workspace.py +0 -0
  59. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/__init__.py +0 -0
  60. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/completion_mentions.py +0 -0
  61. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/input_preparation.py +0 -0
  62. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/messages.py +0 -0
  63. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/ports.py +0 -0
  64. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/question_inputs.py +0 -0
  65. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/reactions.py +0 -0
  66. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/reply_presenter.py +0 -0
  67. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/session_pages.py +0 -0
  68. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/channel/topics.py +0 -0
  69. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/chat_avatars.py +0 -0
  70. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/chat_targets.py +0 -0
  71. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cli.py +0 -0
  72. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cli_data.py +0 -0
  73. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cli_packages.py +0 -0
  74. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cli_services.py +0 -0
  75. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cli_setup.py +0 -0
  76. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cli_update.py +0 -0
  77. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cli_update_restore.py +0 -0
  78. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/cli_update_worker.py +0 -0
  79. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/codex_runtime.py +0 -0
  80. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/completion_mention.py +0 -0
  81. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/database_migrations.py +0 -0
  82. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/defaults/__init__.py +0 -0
  83. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/defaults/models.py +0 -0
  84. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/defaults/service.py +0 -0
  85. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/defaults/store.py +0 -0
  86. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/deployment/__init__.py +0 -0
  87. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/deployment/restart_worker.py +0 -0
  88. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/deployment/update_executor.py +0 -0
  89. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/deployment/update_protocol.py +0 -0
  90. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/domain.py +0 -0
  91. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/error_messages.py +0 -0
  92. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/experience.py +0 -0
  93. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/feishu_app_onboarding.py +0 -0
  94. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/feishu_app_permissions.py +0 -0
  95. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/feishu_links.py +0 -0
  96. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/git_status.py +0 -0
  97. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/image_inputs.py +0 -0
  98. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/instance.py +0 -0
  99. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/lark_app.py +0 -0
  100. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/main.py +0 -0
  101. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/management/__init__.py +0 -0
  102. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/management/blocking_io.py +0 -0
  103. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/management/chat_directory.py +0 -0
  104. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/management/chat_labels.py +0 -0
  105. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/management/coordination.py +0 -0
  106. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/management/service.py +0 -0
  107. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/management/updates.py +0 -0
  108. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/markdown_images.py +0 -0
  109. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/mcp_transport.py +0 -0
  110. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/message_content.py +0 -0
  111. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/message_history.py +0 -0
  112. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/message_preparation.py +0 -0
  113. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/message_projection.py +0 -0
  114. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/__init__.py +0 -0
  115. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/schema.py +0 -0
  116. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/v14.py +0 -0
  117. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/v15.py +0 -0
  118. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/v16.py +0 -0
  119. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/v17.py +0 -0
  120. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/v18.py +0 -0
  121. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/v19.py +0 -0
  122. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/migrations/v20.py +0 -0
  123. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/model_settings.py +0 -0
  124. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/package_resources.py +0 -0
  125. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/pagination.py +0 -0
  126. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/partial_answers.py +0 -0
  127. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/progress_card_config.py +0 -0
  128. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/progress_card_settings.py +0 -0
  129. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/projects.py +0 -0
  130. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/prompt_projection.py +0 -0
  131. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/quoted_context.py +0 -0
  132. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/result_images.py +0 -0
  133. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/runtime/__init__.py +0 -0
  134. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/runtime/contracts.py +0 -0
  135. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/runtime/name_writes.py +0 -0
  136. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/runtime/thread_naming.py +0 -0
  137. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/schedules/__init__.py +0 -0
  138. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/schedules/mcp.py +0 -0
  139. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/schedules/models.py +0 -0
  140. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/schedules/scheduler.py +0 -0
  141. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/schedules/service.py +0 -0
  142. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/schedules/store.py +0 -0
  143. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sdk_gap_adapter.py +0 -0
  144. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/service_launcher.py +0 -0
  145. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/session_inventory.py +0 -0
  146. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/session_settings.py +0 -0
  147. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/__init__.py +0 -0
  148. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/delegation.py +0 -0
  149. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/errors.py +0 -0
  150. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/mcp.py +0 -0
  151. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/policy.py +0 -0
  152. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/presentation.py +0 -0
  153. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/schema.py +0 -0
  154. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/service.py +0 -0
  155. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/sessions/sources.py +0 -0
  156. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/settings.py +0 -0
  157. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/skill_references.py +0 -0
  158. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/task_text.py +0 -0
  159. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/terminal_cleanup.py +0 -0
  160. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/thread_history.py +0 -0
  161. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/turn_activity.py +0 -0
  162. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/turn_files.py +0 -0
  163. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/turn_patch_children.py +0 -0
  164. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/turn_plan_observer.py +0 -0
  165. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/user_questions.py +0 -0
  166. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/worktrees/errors.py +0 -0
  167. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/worktrees/git.py +0 -0
  168. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/worktrees/models.py +0 -0
  169. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/worktrees/policy.py +0 -0
  170. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/worktrees/service.py +0 -0
  171. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/worktrees/settings.py +0 -0
  172. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli/worktrees/store.py +0 -0
  173. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli.egg-info/dependency_links.txt +0 -0
  174. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli.egg-info/entry_points.txt +0 -0
  175. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli.egg-info/requires.txt +0 -0
  176. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/netizen_cli.egg-info/top_level.txt +0 -0
  177. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/setup.cfg +0 -0
  178. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-lark/SKILL.md +0 -0
  179. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-runtime-guidance/SKILL.md +0 -0
  180. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/SKILL.md +0 -0
  181. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/admin.md +0 -0
  182. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/commands.md +0 -0
  183. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/concepts.md +0 -0
  184. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/faq.md +0 -0
  185. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/feedback-and-files.md +0 -0
  186. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/fork-side-goal.md +0 -0
  187. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/messages.md +0 -0
  188. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/native-capabilities.md +0 -0
  189. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/session-collaboration.md +0 -0
  190. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/sessions.md +0 -0
  191. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/status-and-control.md +0 -0
  192. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/skills/netizen-user-guide/references/user-guide.md +0 -0
  193. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_account_rate_limits.py +0 -0
  194. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_admin_update_executor_probe.py +0 -0
  195. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_bindings.py +0 -0
  196. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_card_error_feedback.py +0 -0
  197. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cards.py +0 -0
  198. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_channel_app.py +0 -0
  199. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_channel_fixtures.py +0 -0
  200. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_channel_input_preparation.py +0 -0
  201. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_chat_avatars.py +0 -0
  202. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_chat_target_cards.py +0 -0
  203. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_check_sdk.py +0 -0
  204. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli.py +0 -0
  205. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli_data.py +0 -0
  206. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli_distribution.py +0 -0
  207. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli_integration.py +0 -0
  208. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli_packages.py +0 -0
  209. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli_services.py +0 -0
  210. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli_setup.py +0 -0
  211. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cli_update.py +0 -0
  212. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_codex_runtime.py +0 -0
  213. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_codex_sdk_capabilities.py +0 -0
  214. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_codex_sdk_upgrade_0161.py +0 -0
  215. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_codex_sdk_upgrade_0163.py +0 -0
  216. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_completion_mention_cards.py +0 -0
  217. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_completion_mentions.py +0 -0
  218. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_completion_topic_delivery.py +0 -0
  219. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_configuration_publication.py +0 -0
  220. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_cron_chat_targets.py +0 -0
  221. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_database_migrations.py +0 -0
  222. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_defaults_cards.py +0 -0
  223. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_defaults_channel.py +0 -0
  224. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_defaults_runtime.py +0 -0
  225. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_defaults_worktree_channel.py +0 -0
  226. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_documentation_links.py +0 -0
  227. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_domain_experience.py +0 -0
  228. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_error_messages.py +0 -0
  229. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_feishu_app_onboarding.py +0 -0
  230. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_feishu_app_permissions.py +0 -0
  231. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_file_pagination.py +0 -0
  232. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_fork_cards.py +0 -0
  233. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_git_status.py +0 -0
  234. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_goal_reply_separation.py +0 -0
  235. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_image_inputs.py +0 -0
  236. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_lark_app.py +0 -0
  237. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_lark_sdk_contract.py +0 -0
  238. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_main.py +0 -0
  239. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_markdown_images.py +0 -0
  240. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_message_content.py +0 -0
  241. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_message_history.py +0 -0
  242. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_message_material_inputs.py +0 -0
  243. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_message_preparation.py +0 -0
  244. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_migration_schema_validation.py +0 -0
  245. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_model_settings.py +0 -0
  246. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_name_writes.py +0 -0
  247. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_optional_progress_card_settings_migration.py +0 -0
  248. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_pagination.py +0 -0
  249. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_partial_answer_delivery.py +0 -0
  250. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_partial_answers.py +0 -0
  251. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_persistent_fork_bindings.py +0 -0
  252. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_persistent_fork_channel.py +0 -0
  253. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_persistent_fork_runtime.py +0 -0
  254. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_child_files.py +0 -0
  255. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_feishu_completion_mention.py +0 -0
  256. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_feishu_partial_answers.py +0 -0
  257. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_feishu_turn_file_card.py +0 -0
  258. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_persistent_fork.py +0 -0
  259. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_python_sdk.py +0 -0
  260. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_scheduled_tasks.py +0 -0
  261. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_sdk_completion_race.py +0 -0
  262. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_sdk_response_recovery.py +0 -0
  263. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_skill_execution.py +0 -0
  264. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_probe_thread_naming.py +0 -0
  265. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_progress_card_config.py +0 -0
  266. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_progress_card_entrypoints.py +0 -0
  267. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_progress_card_presentation.py +0 -0
  268. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_progress_card_settings_migration.py +0 -0
  269. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_project_delete_channel.py +0 -0
  270. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_project_delete_runtime.py +0 -0
  271. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_project_deletion_storage.py +0 -0
  272. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_prompt_projection.py +0 -0
  273. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_question_cards.py +0 -0
  274. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_question_channel.py +0 -0
  275. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_question_target_parity.py +0 -0
  276. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_quoted_context.py +0 -0
  277. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_release_script.py +0 -0
  278. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_repository_hygiene.py +0 -0
  279. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_result_images.py +0 -0
  280. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_resume_channel.py +0 -0
  281. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_retired_deployment.py +0 -0
  282. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_runtime_resume_activation.py +0 -0
  283. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_runtime_thread_naming.py +0 -0
  284. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_auto_archive_migration.py +0 -0
  285. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_card_retry.py +0 -0
  286. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_cards.py +0 -0
  287. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_mcp.py +0 -0
  288. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_run_now.py +0 -0
  289. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_scope_matrix.py +0 -0
  290. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_service.py +0 -0
  291. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_workspace_cards.py +0 -0
  292. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_workspace_migration.py +0 -0
  293. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_workspace_service.py +0 -0
  294. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedule_workspace_storage.py +0 -0
  295. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_scheduled_auto_archive.py +0 -0
  296. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_scheduled_runtime.py +0 -0
  297. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_scheduled_worktrees.py +0 -0
  298. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_scheduler.py +0 -0
  299. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_scheduler_presenter.py +0 -0
  300. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_schedules_storage.py +0 -0
  301. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_sdk_gap_adapter.py +0 -0
  302. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_service_launcher.py +0 -0
  303. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_card_pages.py +0 -0
  304. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_collaboration_app.py +0 -0
  305. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_defaults.py +0 -0
  306. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_defaults_migration.py +0 -0
  307. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_external_runtime.py +0 -0
  308. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_id_cards.py +0 -0
  309. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_policy.py +0 -0
  310. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_settings.py +0 -0
  311. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_session_tools.py +0 -0
  312. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_settings_projects.py +0 -0
  313. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_status_cards.py +0 -0
  314. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_status_commands.py +0 -0
  315. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_task_text.py +0 -0
  316. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_terminal_cleanup.py +0 -0
  317. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_thread_history.py +0 -0
  318. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_thread_naming.py +0 -0
  319. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_turn_activity.py +0 -0
  320. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_turn_files.py +0 -0
  321. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_turn_patch_children.py +0 -0
  322. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_turn_patch_statistics.py +0 -0
  323. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_turn_plan_observer.py +0 -0
  324. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_update_executor.py +0 -0
  325. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_usage_card.py +0 -0
  326. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_usage_control.py +0 -0
  327. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_user_guide_skill.py +0 -0
  328. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_user_questions.py +0 -0
  329. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_worktree_channel_cwd.py +0 -0
  330. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_worktree_git.py +0 -0
  331. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_worktree_new.py +0 -0
  332. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_worktree_runtime_cwd.py +0 -0
  333. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_worktree_service.py +0 -0
  334. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_worktree_settings.py +0 -0
  335. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_worktree_storage.py +0 -0
  336. {netizen_cli-0.14.4 → netizen_cli-0.14.5}/tests/test_worktree_tools_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netizen-cli
3
- Version: 0.14.4
3
+ Version: 0.14.5
4
4
  Summary: Use native Codex threads through a Feishu Channel.
5
5
  Requires-Python: <3.15,>=3.11
6
6
  Requires-Dist: h11==0.16.0
@@ -1,3 +1,3 @@
1
1
  """Netizen: a thin Feishu Channel for native Codex Threads."""
2
2
 
3
- __version__ = "0.14.4"
3
+ __version__ = "0.14.5"
@@ -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; }
@@ -4,6 +4,7 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Netizen Admin</title>
7
+ <link rel="icon" type="image/svg+xml" href="/static/netizen.svg">
7
8
  <link rel="stylesheet" href="/static/admin.css">
8
9
  <link rel="stylesheet" href="/static/chat-picker.css">
9
10
  <script src="/static/chat-picker.js" defer></script>
@@ -16,8 +17,11 @@
16
17
  <body>
17
18
  <header class="topbar">
18
19
  <div class="topbar-brand">
19
- <h1>Netizen Admin</h1>
20
- <p id="instance-root" class="instance-root">__NETIZEN_INSTANCE_ROOT__</p>
20
+ <img class="brand-logo" src="/static/netizen.svg" width="40" height="40" alt="">
21
+ <div class="topbar-brand-text">
22
+ <h1>Netizen Admin</h1>
23
+ <p id="instance-root" class="instance-root">__NETIZEN_INSTANCE_ROOT__</p>
24
+ </div>
21
25
  </div>
22
26
  <div class="identity-toolbar"><div id="current-user" class="person" aria-label="当前管理员"></div><button id="logout" class="quiet">退出登录</button></div>
23
27
  </header>
@@ -4,12 +4,14 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Netizen Admin 登录</title>
7
+ <link rel="icon" type="image/svg+xml" href="/static/netizen.svg">
7
8
  <link rel="stylesheet" href="/static/admin.css">
8
9
  <script src="/static/login.js" defer></script>
9
10
  </head>
10
11
  <body class="login-body">
11
12
  <main class="login-card">
12
13
  <header class="login-heading">
14
+ <img class="brand-logo" src="/static/netizen.svg" width="48" height="48" alt="">
13
15
  <h1>Netizen Admin</h1>
14
16
  <p>使用已获授权的飞书管理员账号登录</p>
15
17
  </header>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
2
+ <path d="M17 3h30c8 0 14 6 14 14v24c0 8-6 14-14 14H20L9 62c-1.2.8-2.4-.1-2.2-1.5L8 53C4.8 50.5 3 46.2 3 41V17C3 9 9 3 17 3Z" fill="#1858d7"/>
3
+ <path d="m18 20 11 10-11 10" fill="none" stroke="#fff" stroke-width="6.5" stroke-linecap="square" stroke-linejoin="round"/>
4
+ <rect x="32" y="39" width="18" height="5.5" rx=".6" fill="#fff"/>
5
+ </svg>
@@ -432,6 +432,8 @@ class AdminWebApplication:
432
432
  return Response(204 if self._ready else 503)
433
433
  if not self._ready:
434
434
  raise AdminWebError(503, "not_ready", "管理服务尚未就绪。")
435
+ if context.path == "/static/netizen.svg" and context.method == "GET":
436
+ return Response(200, ((b"Content-Type", b"image/svg+xml"),), self._assets["netizen.svg"])
435
437
  if context.path == "/static/admin.css" and context.method == "GET":
436
438
  # The login page reuses the same inert stylesheet. Keep HTML,
437
439
  # JavaScript, API routes, and all instance state authenticated.
@@ -2761,7 +2763,7 @@ def admin_access_urls(
2761
2763
  def _load_assets() -> dict[str, bytes]:
2762
2764
  root = importlib.resources.files("netizen_cli.admin").joinpath("static")
2763
2765
  assets: dict[str, bytes] = {}
2764
- for name in ("index.html", "admin.css", "admin.js", "chat-picker.js", "chat-picker.css", "login.html", "login.js", "identity.js", "session-access.js", "worktrees.js", "progress-card.js"):
2766
+ for name in ("index.html", "admin.css", "admin.js", "chat-picker.js", "chat-picker.css", "login.html", "login.js", "identity.js", "session-access.js", "worktrees.js", "progress-card.js", "netizen.svg"):
2765
2767
  assets[name] = root.joinpath(name).read_bytes()
2766
2768
  return assets
2767
2769
 
@@ -292,6 +292,25 @@ def _fold(title: str, *items: dict[str, Any], expanded: bool = False) -> dict[st
292
292
  return {"tag": "collapsible_panel", "expanded": expanded, "header": {"title": _plain_text(title)}, "elements": list(items)}
293
293
 
294
294
 
295
+ def schedule_trigger_instructions(instructions: str) -> list[dict[str, Any]]:
296
+ """Keep the trigger preview and bounded body on the original static card."""
297
+ preview_end = min(len(instructions), 120)
298
+ for line_number, newline in enumerate(re.finditer(r"\r\n|\r|\n", instructions), start=1):
299
+ if line_number == 3:
300
+ preview_end = min(preview_end, newline.start())
301
+ break
302
+ # Do not split a Windows line ending at the character boundary.
303
+ if instructions[preview_end - 1:preview_end + 1] == "\r\n":
304
+ preview_end -= 1
305
+ if preview_end == len(instructions):
306
+ return [_plain(instructions)]
307
+ visible = instructions[:3000] + ("…" if len(instructions) > 3000 else "")
308
+ return [
309
+ _plain("内容预览\n" + instructions[:preview_end] + "…"),
310
+ _fold("查看完整任务内容", _plain(visible)),
311
+ ]
312
+
313
+
295
314
  def _form_name(scope: FeishuScope, kind: str, nonce: str) -> str:
296
315
  # Feishu caps each component name at 100 characters. Keep the common
297
316
  # identity small; edit revision / rule evidence travels on its own field.
@@ -108,6 +108,7 @@ from .cards.scheduled import (
108
108
  schedule_navigation,
109
109
  schedule_query,
110
110
  schedule_retry_card,
111
+ schedule_trigger_instructions,
111
112
  )
112
113
  from .cards.chat_target import CHAT_SEARCH_LIMIT, ChatSearchSnapshot
113
114
  from .cards.questions import (
@@ -1027,13 +1028,20 @@ class ChannelApplication:
1027
1028
  trigger_label = "手动触发" if run.trigger_source == "manual" else "自动触发"
1028
1029
  # Plain text prevents instructions from manufacturing mentions in the card.
1029
1030
  description = _FEISHU_AT_TAG_START.sub("‹", plan.instructions)
1031
+ hint = (
1032
+ "成功完成且结果送达后自动归档;运行中可用 /stop 停止。"
1033
+ if plan.auto_archive else
1034
+ "运行中可用 /stop 停止,结束后可在本话题继续交流。"
1035
+ )
1030
1036
  root_content = OutboundCard(card={
1031
1037
  "schema": "2.0", "config": {"width_mode": "default", "update_multi": True},
1032
1038
  "header": {"title": {"tag": "plain_text", "content": "定时任务 · " + plan.name}, "template": "blue"},
1033
- "body": {"elements": [{"tag": "div", "text": {"tag": "plain_text", "content":
1034
- f"{trigger_label} · {local_due} · {plan.schedule.timezone}\n\n"
1035
- + description[:3000] + ("…" if len(description) > 3000 else "")
1036
- + "\n\n本次使用独立普通会话,可在话题中继续交流或使用 /stop。"}}]},
1039
+ "body": {"elements": [
1040
+ {"tag": "div", "text": {"tag": "plain_text", "content":
1041
+ f"{trigger_label} · {local_due} · {plan.schedule.timezone}"}},
1042
+ *schedule_trigger_instructions(description),
1043
+ {"tag": "div", "text": {"tag": "plain_text", "content": hint}},
1044
+ ]},
1037
1045
  })
1038
1046
  publishing_started = True
1039
1047
  root = await send_topic_message(self._channel, run.chat_id, root_content, SendOpts(receive_id_type="chat_id", uuid=run.root_uuid))
@@ -1175,9 +1183,10 @@ class ChannelApplication:
1175
1183
  anchor_content = OutboundCard(card={
1176
1184
  "schema": "2.0", "config": {"width_mode": "default"},
1177
1185
  "header": {"title": {"tag": "plain_text", "content": "定时任务 · " + plan.name}},
1178
- "body": {"elements": [{"tag": "div", "text": {"tag": "plain_text", "content":
1179
- f"{trigger} · {local_due}\n\n" + plan.instructions[:3000]
1180
- + ("…" if len(plan.instructions) > 3000 else "")}}]},
1186
+ "body": {"elements": [
1187
+ {"tag": "div", "text": {"tag": "plain_text", "content": f"{trigger} · {local_due}"}},
1188
+ *schedule_trigger_instructions(plan.instructions),
1189
+ ]},
1181
1190
  })
1182
1191
  sent = await send_topic_message(self._channel, scope.chat_id, anchor_content, SendOpts(
1183
1192
  receive_id_type="chat_id", reply_to=reply_to,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netizen-cli
3
- Version: 0.14.4
3
+ Version: 0.14.5
4
4
  Summary: Use native Codex threads through a Feishu Channel.
5
5
  Requires-Python: <3.15,>=3.11
6
6
  Requires-Dist: h11==0.16.0
@@ -89,6 +89,7 @@ netizen_cli/admin/static/identity.js
89
89
  netizen_cli/admin/static/index.html
90
90
  netizen_cli/admin/static/login.html
91
91
  netizen_cli/admin/static/login.js
92
+ netizen_cli/admin/static/netizen.svg
92
93
  netizen_cli/admin/static/progress-card.js
93
94
  netizen_cli/admin/static/session-access.js
94
95
  netizen_cli/admin/static/worktrees.js
@@ -283,6 +284,7 @@ tests/test_schedule_mcp.py
283
284
  tests/test_schedule_run_now.py
284
285
  tests/test_schedule_scope_matrix.py
285
286
  tests/test_schedule_service.py
287
+ tests/test_schedule_trigger_cards.py
286
288
  tests/test_schedule_workspace_cards.py
287
289
  tests/test_schedule_workspace_migration.py
288
290
  tests/test_schedule_workspace_service.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "netizen-cli"
7
- version = "0.14.4"
7
+ version = "0.14.5"
8
8
  description = "Use native Codex threads through a Feishu Channel."
9
9
  requires-python = ">=3.11,<3.15"
10
10
  dependencies = [
@@ -34,4 +34,4 @@ include = ["netizen_cli*"]
34
34
  build_py = "_build.BuildPy"
35
35
 
36
36
  [tool.setuptools.package-data]
37
- "netizen_cli.admin" = ["static/*.html", "static/*.css", "static/*.js"]
37
+ "netizen_cli.admin" = ["static/*.html", "static/*.css", "static/*.js", "static/*.svg"]
@@ -130,6 +130,12 @@ Admin 的“定时任务”页提供相同管理能力,也可点击“立即
130
130
 
131
131
  ## 会话配置与任务反馈
132
132
 
133
+ 触发通知卡片始终显示任务名、触发方式和时间。任务内容不超过 120 字符且不超过原文
134
+ 3 行时直接展示;较长内容显示前 120 字符或前 3 行(先到为准)的预览,保留换行并加省略号。
135
+ 点击“查看完整任务内容”可展开、再次点击可收起,展开后预览仍保留。展开区最多显示
136
+ 3000 字符,超出以省略号结尾;实际执行始终使用完整指令。卡片展示本次触发时的内容,
137
+ 后续修改计划不会改变它。新话题卡片底部会按本次设置提示自动归档或完成后继续交流。
138
+
133
139
  新话题计划支持与 `/new` 相同的模型、思考强度、速度、执行中表情闪烁、过程卡、结束时 @ 提醒和消息读取范围。
134
140
  默认采用创建时当前会话的配置,可在“会话配置”中修改,也可自然语言指定,例如“使用当前
135
141
  配置,但开启过程卡”。保存后与来源会话独立;修改计划只影响后续执行,某次话题的 `/config`
@@ -36,6 +36,7 @@ from netizen_cli.message_history import (
36
36
  from netizen_cli.projects import ProjectRegistry
37
37
  from netizen_cli.progress_card_settings import ProgressCardSettings
38
38
  from netizen_cli.schedules.models import ScheduleRule
39
+ from tests.support.channel_cards import elements
39
40
  from tests.support.channel_messages import FakeChannel, FakeMessage, FakeMessageHistory
40
41
  from tests.support.channel_results import sent_result
41
42
  from tests.test_codex_runtime import FakeCodex, FakeGoalControl, FakeTerminalCleanup, FakeThread
@@ -203,6 +204,24 @@ class BindingScheduledChannelTest(unittest.IsolatedAsyncioTestCase):
203
204
  self.assertEqual(self.channel.replies, [("om_first", "检查完成")])
204
205
  self.assertEqual(len(self.outcomes), 1)
205
206
 
207
+ async def test_long_trigger_card_is_folded_without_shortening_binding_input(self):
208
+ scope, binding = self.binding()
209
+ claim = self.claim(binding, "检查原会话\n\n第三行\n" + "完整执行指令" * 600)
210
+ self.queue_anchor(scope)
211
+
212
+ await self.app.dispatch_scheduled_run(claim)
213
+
214
+ card = self.channel.send_calls[0][1].card
215
+ body = card["body"]["elements"]
216
+ self.assertEqual(body[0]["text"]["content"], "定时触发 · 1970-01-01T00:02+00:00")
217
+ self.assertEqual(body[1]["text"]["content"], "内容预览\n检查原会话\n\n第三行…")
218
+ panel, = elements(card, "collapsible_panel")
219
+ self.assertFalse(panel["expanded"])
220
+ self.assertEqual(panel["elements"][0]["text"]["content"], claim.plan.instructions[:3000] + "…")
221
+ self.assertNotIn("自动归档", str(card))
222
+ self.assertEqual(self.codex.turn_inputs[0][1].split("\n\n<scheduled_plan>\n")[0], claim.plan.instructions)
223
+ await self.finish()
224
+
206
225
  async def test_steering_human_task_retains_original_origin_and_completion_mention(self):
207
226
  scope, binding = self.binding()
208
227
  human = FakeMessage(
@@ -72,6 +72,8 @@ class BuiltArtifactTests(unittest.TestCase):
72
72
  (ROOT / "config.example.yaml").read_bytes())
73
73
  self.assertIn("netizen_cli/service_launcher.py", names)
74
74
  self.assertIn("netizen_cli/admin/static/index.html", names)
75
+ self.assertEqual(wheel.read("netizen_cli/admin/static/netizen.svg"),
76
+ (ROOT / "netizen_cli/admin/static/netizen.svg").read_bytes())
75
77
  self.assertFalse(any(name.startswith("netizen/") for name in names))
76
78
  entrypoint = next(name for name in names if name.endswith(".dist-info/entry_points.txt"))
77
79
  self.assertIn("netizen = netizen_cli.cli:main", wheel.read(entrypoint).decode())
@@ -84,6 +86,8 @@ class BuiltArtifactTests(unittest.TestCase):
84
86
  "import json,sys; sys.path.insert(0,sys.argv[1]); "
85
87
  "from netizen_cli.builtin_skills import builtin_skill_root; "
86
88
  "from netizen_cli.package_resources import resource_path; "
89
+ "from importlib.resources import files; "
90
+ "assert files('netizen_cli.admin').joinpath('static/netizen.svg').read_bytes().startswith(b'<svg'); "
87
91
  "print(json.dumps([str(builtin_skill_root()),str(resource_path('config.example.yaml'))]))"
88
92
  )
89
93
  result = subprocess.run(
@@ -106,8 +110,9 @@ class BuiltArtifactTests(unittest.TestCase):
106
110
  self._build(source_root, "build_wheel")
107
111
  rebuilt = next((source_root / "dist").glob("*.whl"))
108
112
  with zipfile.ZipFile(self.wheel) as direct, zipfile.ZipFile(rebuilt) as indirect:
109
- resources = {name for name in direct.namelist() if name.startswith("netizen_cli/resources/")}
110
- self.assertEqual(resources, {name for name in indirect.namelist() if name.startswith("netizen_cli/resources/")})
113
+ prefixes = ("netizen_cli/resources/", "netizen_cli/admin/static/")
114
+ resources = {name for name in direct.namelist() if name.startswith(prefixes)}
115
+ self.assertEqual(resources, {name for name in indirect.namelist() if name.startswith(prefixes)})
111
116
  for name in resources:
112
117
  self.assertEqual(direct.read(name), indirect.read(name))
113
118
 
@@ -26,7 +26,7 @@ class DeploymentAssetsTest(unittest.TestCase):
26
26
 
27
27
  self.assertEqual(
28
28
  project["tool"]["setuptools"]["package-data"]["netizen_cli.admin"],
29
- ["static/*.html", "static/*.css", "static/*.js"],
29
+ ["static/*.html", "static/*.css", "static/*.js", "static/*.svg"],
30
30
  )
31
31
 
32
32
  def test_local_documentation_links_and_anchors_resolve(self) -> None:
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import unittest
5
+
6
+ from netizen_cli.cards.scheduled import (
7
+ SCHEDULE_CARD_JSON_LIMIT_BYTES,
8
+ schedule_trigger_instructions,
9
+ )
10
+ from tests.support.channel_cards import elements
11
+
12
+
13
+ class ScheduleTriggerInstructionsTest(unittest.TestCase):
14
+ def test_short_content_is_literal_without_preview_or_fold(self):
15
+ for content in ("检查资源", "汉" * 120, "甲\n\n乙", "甲\r\n乙\r\n丙", "😀" * 120):
16
+ with self.subTest(content=content):
17
+ body = schedule_trigger_instructions(content)
18
+ self.assertEqual(elements(body, "collapsible_panel"), [])
19
+ self.assertEqual(elements(body, "plain_text"), [
20
+ {"tag": "plain_text", "content": content},
21
+ ])
22
+
23
+ def test_preview_stops_at_first_limit_preserving_original_whitespace(self):
24
+ cases = (
25
+ ("汉" * 121, "汉" * 120),
26
+ ("😀" * 121, "😀" * 120),
27
+ ("甲\n\n 乙\n丙", "甲\n\n 乙"),
28
+ ("甲\r\n\r\n 乙\r\n丙", "甲\r\n\r\n 乙"),
29
+ ("甲\r乙\r丙\r丁", "甲\r乙\r丙"),
30
+ ("甲\n\n" + "乙" * 118, "甲\n\n" + "乙" * 117),
31
+ ("甲" * 119 + "\r\n乙", "甲" * 119),
32
+ )
33
+ for content, preview in cases:
34
+ with self.subTest(content=content):
35
+ body = schedule_trigger_instructions(content)
36
+ self.assertEqual(body[0]["text"]["content"], "内容预览\n" + preview + "…")
37
+ panel, = elements(body, "collapsible_panel")
38
+ self.assertFalse(panel["expanded"])
39
+ self.assertEqual(panel["header"]["title"]["content"], "查看完整任务内容")
40
+ self.assertEqual(panel["elements"][0]["text"]["content"], content)
41
+ # The preview remains outside the panel during native expansion.
42
+ self.assertNotIn(body[0], panel["elements"])
43
+
44
+ def test_expanded_content_uses_ellipsis_only_when_over_limit(self):
45
+ for length in (2999, 3000, 3001, 32000):
46
+ with self.subTest(length=length):
47
+ content = "首行\n\n" + "汉" * (length - 4)
48
+ panel, = elements(schedule_trigger_instructions(content), "collapsible_panel")
49
+ self.assertEqual(panel["elements"][0]["text"]["content"],
50
+ content[:3000] + ("…" if length > 3000 else ""))
51
+
52
+ def test_markup_remains_plain_text_and_bounded_for_multibyte_and_escaped_content(self):
53
+ for sample in ("汉", "😀", "\x01", '"\\', '<at id="all">所有人</at>\n**literal**'):
54
+ with self.subTest(sample=sample):
55
+ content = sample * 3001
56
+ body = schedule_trigger_instructions(content)
57
+ self.assertEqual(elements(body, "markdown"), [])
58
+ self.assertEqual(elements(body, "button"), [])
59
+ panel, = elements(body, "collapsible_panel")
60
+ self.assertEqual(panel["elements"][0]["text"], {
61
+ "tag": "plain_text", "content": content[:3000] + "…",
62
+ })
63
+ # Even worst-case JSON escaping leaves ample space for the
64
+ # bounded title, trigger metadata, and footer on the full card.
65
+ self.assertLess(len(json.dumps(body, ensure_ascii=False).encode()),
66
+ SCHEDULE_CARD_JSON_LIMIT_BYTES - 5000)
@@ -80,9 +80,9 @@ class ScheduledChannelTest(unittest.IsolatedAsyncioTestCase):
80
80
  self.history = self.fixture.message_history
81
81
 
82
82
 
83
- def claim(self, instructions="检查明确资源", *, session_settings=SessionSettings()):
83
+ def claim(self, instructions="检查明确资源", *, session_settings=SessionSettings(), auto_archive=False):
84
84
  result = self.store.schedules.create(workspace=WorkspacePolicy(), name="日报", instructions=instructions, project_alias="work", app_id="app", chat_id="oc_group",
85
- schedule=ScheduleRule("interval", "UTC", every_minutes=1, anchor=100), request_id="create-plan", now=100, session_settings=session_settings)
85
+ schedule=ScheduleRule("interval", "UTC", every_minutes=1, anchor=100), request_id="create-plan", now=100, session_settings=session_settings, auto_archive=auto_archive)
86
86
  claimed = self.store.schedules.claim_due(result.plan_id, app_id="app", now=160)
87
87
  self.assertIsNotNone(claimed)
88
88
  return claimed
@@ -125,7 +125,7 @@ class ScheduledChannelTest(unittest.IsolatedAsyncioTestCase):
125
125
  self.assertIsNone(self.channel.send_calls[0][2].reply_to)
126
126
 
127
127
  async def test_manual_trigger_uses_same_topic_origin_and_saved_request_without_human_attribution(self):
128
- claim = self.claim("$reports 使用已保存的明确资源")
128
+ claim = self.claim("$reports 使用已保存的明确资源\n\n" + "保留完整指令" * 600)
129
129
  claim = replace(claim, run=replace(claim.run, trigger_source="manual"))
130
130
  group = FeishuScope("app", "oc_group", ScopeKind.GROUP)
131
131
  previous = self.store.create_channel_binding(scope=group, project_alias="work", creator_id="ou_user")
@@ -136,6 +136,12 @@ class ScheduledChannelTest(unittest.IsolatedAsyncioTestCase):
136
136
  self.assertIn("手动触发", str(root[1].card))
137
137
  self.assertIn("手动触发", seed[1])
138
138
  self.assertNotIn("自动", str(root[1].card) + seed[1])
139
+ body = root[1].card["body"]["elements"]
140
+ self.assertIn("手动触发", body[0]["text"]["content"])
141
+ panel, = _elements(root[1].card, "collapsible_panel")
142
+ self.assertFalse(panel["expanded"])
143
+ self.assertEqual(panel["elements"][0]["text"]["content"], claim.plan.instructions[:3000] + "…")
144
+ self.assertEqual(body[-1]["text"]["content"], "运行中可用 /stop 停止,结束后可在本话题继续交流。")
139
145
  submission, = self.submissions
140
146
  metadata = json.loads(submission["input"].split("<scheduled_plan>\n")[1].split("\n</scheduled_plan>")[0])
141
147
  self.assertEqual(metadata["trigger_source"], "manual")
@@ -151,6 +157,27 @@ class ScheduledChannelTest(unittest.IsolatedAsyncioTestCase):
151
157
  self.assertIsInstance(submission["origin"], ScheduledOrigin)
152
158
  self.assertEqual(self.receipts, [claim.run.id])
153
159
 
160
+ async def test_trigger_card_keeps_claimed_content_and_auto_archive_hint_after_plan_edit(self):
161
+ claim = self.claim("首行\n\n第三行\n" + "任务内容" * 800, auto_archive=True)
162
+ self.store.schedules.update(claim.plan.id, expected_revision=claim.plan.revision,
163
+ request_id="edit-after-claim", changes={"instructions": "后续的新内容", "auto_archive": False}, now=160)
164
+ self.queue_topic()
165
+
166
+ await self.app.dispatch_scheduled_run(claim)
167
+
168
+ card = self.channel.send_calls[0][1].card
169
+ self.assertEqual(card["header"]["title"]["content"], "定时任务 · 日报")
170
+ body = card["body"]["elements"]
171
+ self.assertEqual(body[0]["text"]["content"], "自动触发 · 1970-01-01T00:02+00:00 · UTC")
172
+ self.assertEqual(body[1]["text"]["content"], "内容预览\n首行\n\n第三行…")
173
+ panel, = _elements(card, "collapsible_panel")
174
+ self.assertFalse(panel["expanded"])
175
+ self.assertEqual(panel["elements"][0]["text"]["content"], claim.plan.instructions[:3000] + "…")
176
+ self.assertEqual(body[-1]["text"]["content"], "成功完成且结果送达后自动归档;运行中可用 /stop 停止。")
177
+ self.assertNotIn("后续的新内容", str(card))
178
+ self.assertNotIn("继续交流", str(card))
179
+ self.assertEqual(self.submissions[0]["input"].split("\n\n<scheduled_plan>\n")[0], claim.plan.instructions)
180
+
154
181
  async def test_promotion_uses_distinct_stable_uuid_and_seed_completion_origin(self):
155
182
  claim = self.claim()
156
183
  self.queue_topic(promote=True)
File without changes
File without changes
File without changes