iCode-TUI 0.29.1__py3-none-any.whl
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.
- chrys/__init__.py +16 -0
- chrys/app/__init__.py +3 -0
- chrys/app/acp/__init__.py +3 -0
- chrys/app/acp/bridge.py +664 -0
- chrys/app/acp/content.py +132 -0
- chrys/app/acp/doc/frontend-api.md +425 -0
- chrys/app/acp/history.py +585 -0
- chrys/app/acp/server.py +1796 -0
- chrys/app/acp/session_manager.py +1815 -0
- chrys/app/acp/tool_status.py +45 -0
- chrys/app/cli/__init__.py +3 -0
- chrys/app/cli/acp.py +178 -0
- chrys/app/cli/app.py +165 -0
- chrys/app/cli/approvals.py +101 -0
- chrys/app/cli/headless.py +223 -0
- chrys/app/cli/launch_cwd.py +42 -0
- chrys/app/cli/profiles.py +298 -0
- chrys/app/cli/progress.py +857 -0
- chrys/app/cli/run.py +425 -0
- chrys/app/cli/serve.py +1593 -0
- chrys/app/cli/tool_summary.py +225 -0
- chrys/app/cli/trajectory.py +201 -0
- chrys/app/cli/workflow.py +601 -0
- chrys/app/features/__init__.py +3 -0
- chrys/app/features/buddy/__init__.py +3 -0
- chrys/app/features/buddy/actions.py +68 -0
- chrys/app/features/buddy/animation.py +76 -0
- chrys/app/features/buddy/commands.py +196 -0
- chrys/app/features/buddy/docs/design.md +187 -0
- chrys/app/features/buddy/hatchery.py +80 -0
- chrys/app/features/buddy/lifecycle.py +41 -0
- chrys/app/features/buddy/model.py +265 -0
- chrys/app/features/buddy/pixel_renderer.py +113 -0
- chrys/app/features/buddy/pixel_sprites.py +279 -0
- chrys/app/features/buddy/portrait.py +76 -0
- chrys/app/features/buddy/progression.py +58 -0
- chrys/app/features/buddy/replies.py +191 -0
- chrys/app/features/buddy/sprites/alien.toml +70 -0
- chrys/app/features/buddy/sprites/axolotl.toml +71 -0
- chrys/app/features/buddy/sprites/bat.toml +69 -0
- chrys/app/features/buddy/sprites/bee.toml +69 -0
- chrys/app/features/buddy/sprites/cactus.toml +73 -0
- chrys/app/features/buddy/sprites/capybara.toml +70 -0
- chrys/app/features/buddy/sprites/cat.toml +69 -0
- chrys/app/features/buddy/sprites/crab.toml +70 -0
- chrys/app/features/buddy/sprites/dragon.toml +71 -0
- chrys/app/features/buddy/sprites/duck.toml +69 -0
- chrys/app/features/buddy/sprites/fox.toml +69 -0
- chrys/app/features/buddy/sprites/frog.toml +70 -0
- chrys/app/features/buddy/sprites/ghost.toml +70 -0
- chrys/app/features/buddy/sprites/goose.toml +69 -0
- chrys/app/features/buddy/sprites/jelly.toml +70 -0
- chrys/app/features/buddy/sprites/llama.toml +70 -0
- chrys/app/features/buddy/sprites/mushroom.toml +69 -0
- chrys/app/features/buddy/sprites/octopus.toml +70 -0
- chrys/app/features/buddy/sprites/owl.toml +70 -0
- chrys/app/features/buddy/sprites/panda.toml +71 -0
- chrys/app/features/buddy/sprites/penguin.toml +69 -0
- chrys/app/features/buddy/sprites/rabbit.toml +70 -0
- chrys/app/features/buddy/sprites/robot.toml +72 -0
- chrys/app/features/buddy/sprites/shark.toml +70 -0
- chrys/app/features/buddy/sprites/snail.toml +69 -0
- chrys/app/features/buddy/sprites/snake.toml +69 -0
- chrys/app/features/buddy/sprites/tabby.toml +69 -0
- chrys/app/features/buddy/sprites/turtle.toml +69 -0
- chrys/app/features/buddy/store.py +143 -0
- chrys/app/features/session_title/__init__.py +7 -0
- chrys/app/features/session_title/updater.py +582 -0
- chrys/app/installer.py +651 -0
- chrys/app/parsing.py +17 -0
- chrys/app/tui/__init__.py +3 -0
- chrys/app/tui/app.py +1287 -0
- chrys/app/tui/behaviors/__init__.py +3 -0
- chrys/app/tui/behaviors/click_outside_dismiss.py +90 -0
- chrys/app/tui/behaviors/insert_clipboard.py +73 -0
- chrys/app/tui/behaviors/right_click_copy.py +146 -0
- chrys/app/tui/binding_display.py +139 -0
- chrys/app/tui/buddy_messages.py +17 -0
- chrys/app/tui/buddy_reply.py +86 -0
- chrys/app/tui/chrys.tcss +549 -0
- chrys/app/tui/clipboard.py +109 -0
- chrys/app/tui/copy_messages.py +10 -0
- chrys/app/tui/i18n.py +230 -0
- chrys/app/tui/language.py +58 -0
- chrys/app/tui/notifications/__init__.py +12 -0
- chrys/app/tui/notifications/drivers.py +180 -0
- chrys/app/tui/notifications/service.py +126 -0
- chrys/app/tui/notifications/settings.py +141 -0
- chrys/app/tui/screens/__init__.py +3 -0
- chrys/app/tui/screens/agents/__init__.py +8 -0
- chrys/app/tui/screens/agents/agent_draft_store.py +937 -0
- chrys/app/tui/screens/agents/config.py +2142 -0
- chrys/app/tui/screens/agents/config.tcss +169 -0
- chrys/app/tui/screens/agents/panels/__init__.py +3 -0
- chrys/app/tui/screens/agents/panels/acp.py +1176 -0
- chrys/app/tui/screens/agents/panels/basic.py +497 -0
- chrys/app/tui/screens/agents/panels/compaction.py +231 -0
- chrys/app/tui/screens/agents/panels/config_card.py +218 -0
- chrys/app/tui/screens/agents/panels/instructions.py +91 -0
- chrys/app/tui/screens/agents/panels/markdown_report.py +76 -0
- chrys/app/tui/screens/agents/panels/mcp.py +2262 -0
- chrys/app/tui/screens/agents/panels/memory.py +684 -0
- chrys/app/tui/screens/agents/panels/path_entry.py +227 -0
- chrys/app/tui/screens/agents/panels/skills.py +806 -0
- chrys/app/tui/screens/agents/panels/subagents.py +652 -0
- chrys/app/tui/screens/agents/panels/tools.py +205 -0
- chrys/app/tui/screens/agents/picker.py +103 -0
- chrys/app/tui/screens/agents/skill_paths.py +38 -0
- chrys/app/tui/screens/agents/validation_messages.py +452 -0
- chrys/app/tui/screens/dialogs/__init__.py +35 -0
- chrys/app/tui/screens/dialogs/agent_load.py +834 -0
- chrys/app/tui/screens/dialogs/agent_load.tcss +78 -0
- chrys/app/tui/screens/dialogs/app_mode.py +55 -0
- chrys/app/tui/screens/dialogs/approval/__init__.py +15 -0
- chrys/app/tui/screens/dialogs/approval/approval.tcss +311 -0
- chrys/app/tui/screens/dialogs/approval/bodies/__init__.py +3 -0
- chrys/app/tui/screens/dialogs/approval/bodies/file_edit.py +210 -0
- chrys/app/tui/screens/dialogs/approval/bodies/sub_agent.py +82 -0
- chrys/app/tui/screens/dialogs/approval/body.py +110 -0
- chrys/app/tui/screens/dialogs/approval/dialog.py +565 -0
- chrys/app/tui/screens/dialogs/approval/mode.py +105 -0
- chrys/app/tui/screens/dialogs/ask_user.py +148 -0
- chrys/app/tui/screens/dialogs/ask_user.tcss +62 -0
- chrys/app/tui/screens/dialogs/base.py +84 -0
- chrys/app/tui/screens/dialogs/confirm.py +184 -0
- chrys/app/tui/screens/dialogs/confirm.tcss +46 -0
- chrys/app/tui/screens/dialogs/connection_test.py +183 -0
- chrys/app/tui/screens/dialogs/connection_test.tcss +92 -0
- chrys/app/tui/screens/dialogs/diagram.py +102 -0
- chrys/app/tui/screens/dialogs/diagram.tcss +24 -0
- chrys/app/tui/screens/dialogs/editor.py +378 -0
- chrys/app/tui/screens/dialogs/editor.tcss +132 -0
- chrys/app/tui/screens/dialogs/file_picker.py +582 -0
- chrys/app/tui/screens/dialogs/file_picker.tcss +78 -0
- chrys/app/tui/screens/dialogs/fork_session.py +219 -0
- chrys/app/tui/screens/dialogs/fork_session.tcss +75 -0
- chrys/app/tui/screens/dialogs/image_compression.py +102 -0
- chrys/app/tui/screens/dialogs/image_compression.tcss +28 -0
- chrys/app/tui/screens/dialogs/man_page.py +172 -0
- chrys/app/tui/screens/dialogs/man_page.tcss +39 -0
- chrys/app/tui/screens/dialogs/new_folder.py +277 -0
- chrys/app/tui/screens/dialogs/new_folder.tcss +51 -0
- chrys/app/tui/screens/dialogs/notice.tcss +14 -0
- chrys/app/tui/screens/dialogs/prompt_cache.py +144 -0
- chrys/app/tui/screens/dialogs/prompt_cache.tcss +38 -0
- chrys/app/tui/screens/dialogs/runtime_details.py +371 -0
- chrys/app/tui/screens/dialogs/runtime_details.tcss +49 -0
- chrys/app/tui/screens/dialogs/session_title.py +110 -0
- chrys/app/tui/screens/dialogs/session_title.tcss +43 -0
- chrys/app/tui/screens/dialogs/tool_view.py +207 -0
- chrys/app/tui/screens/dialogs/tool_view.tcss +43 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.py +122 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.tcss +35 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.py +91 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.tcss +17 -0
- chrys/app/tui/screens/dialogs/workflow_input.py +258 -0
- chrys/app/tui/screens/dialogs/workflow_input.tcss +73 -0
- chrys/app/tui/screens/dialogs/workflow_node.py +462 -0
- chrys/app/tui/screens/dialogs/workflow_node.tcss +41 -0
- chrys/app/tui/screens/dialogs/workflow_picker.py +46 -0
- chrys/app/tui/screens/dialogs/workflow_result.py +136 -0
- chrys/app/tui/screens/dialogs/workflow_result.tcss +14 -0
- chrys/app/tui/screens/diff/__init__.py +22 -0
- chrys/app/tui/screens/diff/rollback_modal.py +1381 -0
- chrys/app/tui/screens/diff/rollback_modal.tcss +248 -0
- chrys/app/tui/screens/diff/screen.py +1532 -0
- chrys/app/tui/screens/diff/screen.tcss +104 -0
- chrys/app/tui/screens/guides/__init__.py +7 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/en/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/en/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/en/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/en/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/en/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/en/reference/workflows.md +1006 -0
- chrys/app/tui/screens/guides/_docs/en/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/en/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/_docs/index.yaml +100 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/workflows.md +1005 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/index.py +215 -0
- chrys/app/tui/screens/guides/reader.py +173 -0
- chrys/app/tui/screens/guides/screen.py +442 -0
- chrys/app/tui/screens/guides/screen.tcss +52 -0
- chrys/app/tui/screens/logs/__init__.py +7 -0
- chrys/app/tui/screens/logs/screen.py +504 -0
- chrys/app/tui/screens/logs/screen.tcss +45 -0
- chrys/app/tui/screens/main/__init__.py +7 -0
- chrys/app/tui/screens/main/action_availability.py +30 -0
- chrys/app/tui/screens/main/buddy_command.py +143 -0
- chrys/app/tui/screens/main/commands.py +1073 -0
- chrys/app/tui/screens/main/config_actions.py +464 -0
- chrys/app/tui/screens/main/copy_actions.py +117 -0
- chrys/app/tui/screens/main/dialog_controllers.py +826 -0
- chrys/app/tui/screens/main/dialog_gateway.py +166 -0
- chrys/app/tui/screens/main/diff_controller.py +478 -0
- chrys/app/tui/screens/main/engine_read_model.py +184 -0
- chrys/app/tui/screens/main/event_handlers.py +2014 -0
- chrys/app/tui/screens/main/input_flow.py +241 -0
- chrys/app/tui/screens/main/live_diff.py +244 -0
- chrys/app/tui/screens/main/main_screen_presenter.py +295 -0
- chrys/app/tui/screens/main/model_indicator.py +217 -0
- chrys/app/tui/screens/main/navigation.py +343 -0
- chrys/app/tui/screens/main/ports.py +571 -0
- chrys/app/tui/screens/main/recent_dirs.py +123 -0
- chrys/app/tui/screens/main/rollback_controller.py +586 -0
- chrys/app/tui/screens/main/runtime_info.py +71 -0
- chrys/app/tui/screens/main/screen.py +2265 -0
- chrys/app/tui/screens/main/screen.tcss +21 -0
- chrys/app/tui/screens/main/session_handlers.py +1127 -0
- chrys/app/tui/screens/main/session_title.py +239 -0
- chrys/app/tui/screens/main/settings_coordinator.py +365 -0
- chrys/app/tui/screens/main/settings_persistence.py +184 -0
- chrys/app/tui/screens/main/shell_mode.py +112 -0
- chrys/app/tui/screens/main/state.py +289 -0
- chrys/app/tui/screens/main/submit_state.py +11 -0
- chrys/app/tui/screens/main/subscriptions.py +177 -0
- chrys/app/tui/screens/main/suggestions.py +982 -0
- chrys/app/tui/screens/main/tool_action_bridge.py +75 -0
- chrys/app/tui/screens/main/view_adapter.py +1567 -0
- chrys/app/tui/screens/main/workflow_browser.py +747 -0
- chrys/app/tui/screens/main/workflow_content.py +248 -0
- chrys/app/tui/screens/main/workflow_controller.py +240 -0
- chrys/app/tui/screens/main/workflow_feedback.py +137 -0
- chrys/app/tui/screens/main/workflow_flow.py +49 -0
- chrys/app/tui/screens/main/workflow_run_control.py +456 -0
- chrys/app/tui/screens/main/workflow_session_view.py +316 -0
- chrys/app/tui/screens/main/workspace_actions.py +272 -0
- chrys/app/tui/screens/main/workspace_branch.py +194 -0
- chrys/app/tui/screens/menus/__init__.py +8 -0
- chrys/app/tui/screens/menus/languages.py +114 -0
- chrys/app/tui/screens/models/__init__.py +7 -0
- chrys/app/tui/screens/models/picker.py +147 -0
- chrys/app/tui/screens/models/screen.py +2043 -0
- chrys/app/tui/screens/models/screen.tcss +255 -0
- chrys/app/tui/screens/sessions/__init__.py +8 -0
- chrys/app/tui/screens/sessions/presenter.py +586 -0
- chrys/app/tui/screens/sessions/screen.py +779 -0
- chrys/app/tui/screens/sessions/screen.tcss +127 -0
- chrys/app/tui/screens/settings/__init__.py +29 -0
- chrys/app/tui/screens/settings/danger.py +26 -0
- chrys/app/tui/screens/settings/dialog.py +281 -0
- chrys/app/tui/screens/settings/layout.py +519 -0
- chrys/app/tui/screens/settings/migrate_dialog.py +253 -0
- chrys/app/tui/screens/settings/panes/__init__.py +3 -0
- chrys/app/tui/screens/settings/panes/notifications.py +248 -0
- chrys/app/tui/screens/settings/panes/sessions.py +148 -0
- chrys/app/tui/screens/settings/ports.py +55 -0
- chrys/app/tui/screens/settings/provenance.py +102 -0
- chrys/app/tui/screens/settings/rows.py +546 -0
- chrys/app/tui/screens/settings/settings.tcss +406 -0
- chrys/app/tui/screens/themes/__init__.py +8 -0
- chrys/app/tui/screens/themes/dialogs.py +330 -0
- chrys/app/tui/screens/themes/editor.py +632 -0
- chrys/app/tui/screens/themes/editor.tcss +268 -0
- chrys/app/tui/screens/themes/messages.py +95 -0
- chrys/app/tui/screens/themes/palette.py +694 -0
- chrys/app/tui/screens/themes/panel.py +113 -0
- chrys/app/tui/screens/themes/picker.py +203 -0
- chrys/app/tui/screens/themes/save.py +191 -0
- chrys/app/tui/support/__init__.py +3 -0
- chrys/app/tui/support/file_manager.py +64 -0
- chrys/app/tui/support/gc_freeze.py +1114 -0
- chrys/app/tui/support/path_shortcuts.py +50 -0
- chrys/app/tui/support/prompt_history.py +298 -0
- chrys/app/tui/support/workspace_mru.py +476 -0
- chrys/app/tui/terminal/__init__.py +6 -0
- chrys/app/tui/terminal/_pty_backend.py +114 -0
- chrys/app/tui/terminal/emulator/__init__.py +24 -0
- chrys/app/tui/terminal/emulator/buffer.py +456 -0
- chrys/app/tui/terminal/emulator/charsets.py +113 -0
- chrys/app/tui/terminal/emulator/core.py +963 -0
- chrys/app/tui/terminal/emulator/input.py +342 -0
- chrys/app/tui/terminal/emulator/parser.py +227 -0
- chrys/app/tui/terminal/emulator/pen.py +158 -0
- chrys/app/tui/terminal/launcher.py +455 -0
- chrys/app/tui/terminal/leaked_reports.py +44 -0
- chrys/app/tui/terminal/panel.py +323 -0
- chrys/app/tui/terminal/pty_process.py +405 -0
- chrys/app/tui/terminal/rendering.py +151 -0
- chrys/app/tui/terminal/shell.py +273 -0
- chrys/app/tui/terminal/shell_integration.py +199 -0
- chrys/app/tui/terminal/title.py +224 -0
- chrys/app/tui/terminal/widget.py +654 -0
- chrys/app/tui/theme.py +422 -0
- chrys/app/tui/theme_loader.py +201 -0
- chrys/app/tui/theme_stylesheet.py +49 -0
- chrys/app/tui/themes/__init__.py +3 -0
- chrys/app/tui/themes/document.py +120 -0
- chrys/app/tui/themes/preview.py +170 -0
- chrys/app/tui/themes/store.py +176 -0
- chrys/app/tui/toasts.py +69 -0
- chrys/app/tui/util/__init__.py +3 -0
- chrys/app/tui/util/context_pressure.py +146 -0
- chrys/app/tui/util/debug_log.py +292 -0
- chrys/app/tui/util/diff_entries.py +64 -0
- chrys/app/tui/util/formatting.py +53 -0
- chrys/app/tui/util/git_branch.py +326 -0
- chrys/app/tui/util/invocation_progress.py +57 -0
- chrys/app/tui/util/logo.py +32 -0
- chrys/app/tui/util/message_gate.py +35 -0
- chrys/app/tui/util/mutation_sources.py +14 -0
- chrys/app/tui/util/picker_text.py +27 -0
- chrys/app/tui/util/removal.py +47 -0
- chrys/app/tui/util/rich_style.py +36 -0
- chrys/app/tui/util/source_text.py +12 -0
- chrys/app/tui/util/static_update.py +88 -0
- chrys/app/tui/util/todo_format.py +51 -0
- chrys/app/tui/util/visibility.py +214 -0
- chrys/app/tui/widgets/__init__.py +60 -0
- chrys/app/tui/widgets/ask_user_controls.py +652 -0
- chrys/app/tui/widgets/ask_user_markdown.py +17 -0
- chrys/app/tui/widgets/ask_user_prompt.py +606 -0
- chrys/app/tui/widgets/buttons.py +52 -0
- chrys/app/tui/widgets/chat/__init__.py +3 -0
- chrys/app/tui/widgets/chat/agent_transcript_surface.py +818 -0
- chrys/app/tui/widgets/chat/chrome.py +296 -0
- chrys/app/tui/widgets/chat/compaction_card.py +353 -0
- chrys/app/tui/widgets/chat/context_fold.py +77 -0
- chrys/app/tui/widgets/chat/file_snapshot.py +91 -0
- chrys/app/tui/widgets/chat/image_preview.py +214 -0
- chrys/app/tui/widgets/chat/live_renderer.py +383 -0
- chrys/app/tui/widgets/chat/messages.py +1039 -0
- chrys/app/tui/widgets/chat/panel.py +1769 -0
- chrys/app/tui/widgets/chat/ports.py +245 -0
- chrys/app/tui/widgets/chat/renderers/__init__.py +7 -0
- chrys/app/tui/widgets/chat/renderers/ask_user.py +747 -0
- chrys/app/tui/widgets/chat/renderers/execute.py +431 -0
- chrys/app/tui/widgets/chat/renderers/file_edit.py +925 -0
- chrys/app/tui/widgets/chat/renderers/hosted_code.py +83 -0
- chrys/app/tui/widgets/chat/renderers/hosted_generic.py +330 -0
- chrys/app/tui/widgets/chat/renderers/hosted_image.py +107 -0
- chrys/app/tui/widgets/chat/renderers/hosted_mcp.py +43 -0
- chrys/app/tui/widgets/chat/renderers/hosted_search.py +167 -0
- chrys/app/tui/widgets/chat/renderers/hosted_shell.py +132 -0
- chrys/app/tui/widgets/chat/renderers/read_file.py +399 -0
- chrys/app/tui/widgets/chat/renderers/search.py +348 -0
- chrys/app/tui/widgets/chat/renderers/skill.py +564 -0
- chrys/app/tui/widgets/chat/renderers/sleep.py +287 -0
- chrys/app/tui/widgets/chat/renderers/sub_agent.py +1385 -0
- chrys/app/tui/widgets/chat/renderers/todo.py +114 -0
- chrys/app/tui/widgets/chat/renderers/web.py +69 -0
- chrys/app/tui/widgets/chat/replay.py +1605 -0
- chrys/app/tui/widgets/chat/replay_mount.py +322 -0
- chrys/app/tui/widgets/chat/scroll_controller.py +579 -0
- chrys/app/tui/widgets/chat/selection_controller.py +435 -0
- chrys/app/tui/widgets/chat/session_json.py +508 -0
- chrys/app/tui/widgets/chat/toc_model.py +156 -0
- chrys/app/tui/widgets/chat/tool_call.py +2478 -0
- chrys/app/tui/widgets/chat/tool_registry.py +753 -0
- chrys/app/tui/widgets/chat/tool_renderers.py +247 -0
- chrys/app/tui/widgets/chat/tool_view_builders.py +260 -0
- chrys/app/tui/widgets/chat/transcript_reader.py +57 -0
- chrys/app/tui/widgets/checkbox.py +24 -0
- chrys/app/tui/widgets/chrome/__init__.py +3 -0
- chrys/app/tui/widgets/chrome/app_header.py +283 -0
- chrys/app/tui/widgets/chrome/commands.py +95 -0
- chrys/app/tui/widgets/chrome/file_index.py +68 -0
- chrys/app/tui/widgets/chrome/file_scanner.py +670 -0
- chrys/app/tui/widgets/chrome/footer.py +284 -0
- chrys/app/tui/widgets/chrome/image_paste.py +351 -0
- chrys/app/tui/widgets/chrome/input_bar.py +1299 -0
- chrys/app/tui/widgets/chrome/status_bar.py +911 -0
- chrys/app/tui/widgets/chrome/suggestion_list.py +652 -0
- chrys/app/tui/widgets/click_affordance.py +24 -0
- chrys/app/tui/widgets/color_picker/__init__.py +254 -0
- chrys/app/tui/widgets/color_picker/controls.py +171 -0
- chrys/app/tui/widgets/color_picker/model.py +89 -0
- chrys/app/tui/widgets/dialog_buttons.py +58 -0
- chrys/app/tui/widgets/dialog_scroll.py +24 -0
- chrys/app/tui/widgets/diff_view/__init__.py +20 -0
- chrys/app/tui/widgets/diff_view/cells.py +59 -0
- chrys/app/tui/widgets/diff_view/code.py +280 -0
- chrys/app/tui/widgets/diff_view/compute.py +286 -0
- chrys/app/tui/widgets/diff_view/gutter.py +161 -0
- chrys/app/tui/widgets/diff_view/inline.py +77 -0
- chrys/app/tui/widgets/diff_view/palette.py +87 -0
- chrys/app/tui/widgets/diff_view/protocols.py +24 -0
- chrys/app/tui/widgets/diff_view/rows.py +138 -0
- chrys/app/tui/widgets/diff_view/unified.py +267 -0
- chrys/app/tui/widgets/diff_view/widget.py +209 -0
- chrys/app/tui/widgets/editor/__init__.py +79 -0
- chrys/app/tui/widgets/editor/coordinates.py +30 -0
- chrys/app/tui/widgets/editor/highlighter.py +139 -0
- chrys/app/tui/widgets/editor/keymaps.py +357 -0
- chrys/app/tui/widgets/editor/types.py +216 -0
- chrys/app/tui/widgets/editor/vim.py +1195 -0
- chrys/app/tui/widgets/editor/widget.py +401 -0
- chrys/app/tui/widgets/file_preview.py +58 -0
- chrys/app/tui/widgets/hatch.py +138 -0
- chrys/app/tui/widgets/input.py +66 -0
- chrys/app/tui/widgets/loading.py +125 -0
- chrys/app/tui/widgets/markdown/__init__.py +25 -0
- chrys/app/tui/widgets/markdown/_utils.py +21 -0
- chrys/app/tui/widgets/markdown/anchors.py +71 -0
- chrys/app/tui/widgets/markdown/blocks.py +115 -0
- chrys/app/tui/widgets/markdown/copyable.py +163 -0
- chrys/app/tui/widgets/markdown/diagram/__init__.py +35 -0
- chrys/app/tui/widgets/markdown/diagram/canvas.py +409 -0
- chrys/app/tui/widgets/markdown/diagram/charts.py +52 -0
- chrys/app/tui/widgets/markdown/diagram/diagnostics.py +101 -0
- chrys/app/tui/widgets/markdown/diagram/geometry.py +46 -0
- chrys/app/tui/widgets/markdown/diagram/layout.py +494 -0
- chrys/app/tui/widgets/markdown/diagram/messages.py +18 -0
- chrys/app/tui/widgets/markdown/diagram/model.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/parser.py +173 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/charts.py +349 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/common.py +403 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/graphs.py +1072 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/planning.py +317 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schedule.py +298 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schemas.py +241 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/structure.py +221 -0
- chrys/app/tui/widgets/markdown/diagram/presentation.py +32 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/charts.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/common.py +73 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/planning.py +157 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/schedule.py +202 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/structure.py +77 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/xy.py +385 -0
- chrys/app/tui/widgets/markdown/diagram/router.py +651 -0
- chrys/app/tui/widgets/markdown/diagram/specs/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/specs/charts.py +94 -0
- chrys/app/tui/widgets/markdown/diagram/specs/planning.py +68 -0
- chrys/app/tui/widgets/markdown/diagram/specs/schedule.py +65 -0
- chrys/app/tui/widgets/markdown/diagram/specs/structure.py +23 -0
- chrys/app/tui/widgets/markdown/diagram/viewer.py +92 -0
- chrys/app/tui/widgets/markdown/links.py +47 -0
- chrys/app/tui/widgets/markdown/math/__init__.py +36 -0
- chrys/app/tui/widgets/markdown/math/layout.py +739 -0
- chrys/app/tui/widgets/markdown/math/markdown.py +291 -0
- chrys/app/tui/widgets/markdown/math/parser.py +325 -0
- chrys/app/tui/widgets/markdown/math/symbols.py +325 -0
- chrys/app/tui/widgets/markdown/navigator.py +80 -0
- chrys/app/tui/widgets/markdown/parser.py +1057 -0
- chrys/app/tui/widgets/markdown/stream.py +73 -0
- chrys/app/tui/widgets/markdown/toc.py +98 -0
- chrys/app/tui/widgets/markdown/viewer.py +146 -0
- chrys/app/tui/widgets/markdown/widget.py +2046 -0
- chrys/app/tui/widgets/marquee.py +101 -0
- chrys/app/tui/widgets/option_menu.py +82 -0
- chrys/app/tui/widgets/page_navigator.py +118 -0
- chrys/app/tui/widgets/select.py +196 -0
- chrys/app/tui/widgets/selection.py +59 -0
- chrys/app/tui/widgets/sidebar/__init__.py +3 -0
- chrys/app/tui/widgets/sidebar/buddy.py +367 -0
- chrys/app/tui/widgets/sidebar/context.py +482 -0
- chrys/app/tui/widgets/sidebar/debug.py +195 -0
- chrys/app/tui/widgets/sidebar/empty_state.py +11 -0
- chrys/app/tui/widgets/sidebar/panel.py +198 -0
- chrys/app/tui/widgets/sidebar/tasks.py +198 -0
- chrys/app/tui/widgets/sidebar/toc.py +166 -0
- chrys/app/tui/widgets/sidebar/toc_types.py +30 -0
- chrys/app/tui/widgets/syntax_theme.py +85 -0
- chrys/app/tui/widgets/text_area.py +258 -0
- chrys/app/tui/widgets/trajectory/__init__.py +8 -0
- chrys/app/tui/widgets/trajectory/chartkit.py +264 -0
- chrys/app/tui/widgets/trajectory/insights.py +1266 -0
- chrys/app/tui/widgets/trajectory/overview.py +1101 -0
- chrys/app/tui/widgets/trajectory/panel.py +983 -0
- chrys/app/tui/widgets/trajectory/presentation.py +311 -0
- chrys/app/tui/widgets/trajectory/session_info.py +124 -0
- chrys/app/tui/widgets/trajectory/text_view.py +124 -0
- chrys/app/tui/widgets/trajectory/timeline.py +380 -0
- chrys/app/tui/widgets/welcome.py +126 -0
- chrys/app/tui/widgets/workflow/__init__.py +5 -0
- chrys/app/tui/widgets/workflow/edges.py +89 -0
- chrys/app/tui/widgets/workflow/graph.py +869 -0
- chrys/app/tui/widgets/workflow/info.py +310 -0
- chrys/app/tui/widgets/workflow/node_view.py +27 -0
- chrys/app/tui/widgets/workflow/output.py +225 -0
- chrys/app/tui/widgets/workflow/panel.py +658 -0
- chrys/app/tui/widgets/workflow/projector.py +324 -0
- chrys/app/tui/widgets/workflow/records.py +69 -0
- chrys/app/tui/widgets/workflow/scrollbar.py +82 -0
- chrys/app/tui/widgets/workflow/selection.py +151 -0
- chrys/app/tui/widgets/workflow/source.py +65 -0
- chrys/app/tui/widgets/workflow/text.py +257 -0
- chrys/app/tui/widgets/workflow/values.py +317 -0
- chrys/foundation/__init__.py +0 -0
- chrys/foundation/branding.py +15 -0
- chrys/foundation/config/__init__.py +7 -0
- chrys/foundation/config/coercion.py +374 -0
- chrys/foundation/config/context.py +34 -0
- chrys/foundation/config/env_file.py +141 -0
- chrys/foundation/config/env_layers.py +370 -0
- chrys/foundation/config/migrations.py +521 -0
- chrys/foundation/config/notification_events.py +22 -0
- chrys/foundation/config/process_settings.py +332 -0
- chrys/foundation/config/runtime_pointer.py +133 -0
- chrys/foundation/config/settings.py +1952 -0
- chrys/foundation/config/settings_store.py +996 -0
- chrys/foundation/config/spec.py +396 -0
- chrys/foundation/config/user_settings.py +140 -0
- chrys/foundation/config/warnings.py +262 -0
- chrys/foundation/config/web_values.py +135 -0
- chrys/foundation/config/yaml_store.py +487 -0
- chrys/foundation/errors/__init__.py +68 -0
- chrys/foundation/errors/_legacy.py +117 -0
- chrys/foundation/errors/_text.py +13 -0
- chrys/foundation/errors/_walk.py +85 -0
- chrys/foundation/errors/classify.py +278 -0
- chrys/foundation/errors/display.py +274 -0
- chrys/foundation/errors/formatting.py +283 -0
- chrys/foundation/errors/kinds.py +67 -0
- chrys/foundation/errors/network.py +497 -0
- chrys/foundation/errors/provider.py +445 -0
- chrys/foundation/errors/route.py +85 -0
- chrys/foundation/events/__init__.py +8 -0
- chrys/foundation/events/bus.py +143 -0
- chrys/foundation/events/types.py +1629 -0
- chrys/foundation/events/workflow.py +38 -0
- chrys/foundation/hosted_tools.py +142 -0
- chrys/foundation/i18n/__init__.py +32 -0
- chrys/foundation/i18n/_catalogs/zh-Hans/LC_MESSAGES/chrys.mo +0 -0
- chrys/foundation/i18n/catalogs.py +42 -0
- chrys/foundation/i18n/formatting.py +202 -0
- chrys/foundation/i18n/locale.py +92 -0
- chrys/foundation/i18n/localizer.py +187 -0
- chrys/foundation/i18n/messages.py +268 -0
- chrys/foundation/io/__init__.py +3 -0
- chrys/foundation/io/result_content.py +237 -0
- chrys/foundation/models/__init__.py +3 -0
- chrys/foundation/models/approval_reuse.py +23 -0
- chrys/foundation/models/ask_user.py +468 -0
- chrys/foundation/models/execution.py +18 -0
- chrys/foundation/models/history_markers.py +115 -0
- chrys/foundation/models/invocations.py +63 -0
- chrys/foundation/models/mutation_scope.py +28 -0
- chrys/foundation/models/session_env.py +87 -0
- chrys/foundation/models/session_surface.py +29 -0
- chrys/foundation/models/todos.py +74 -0
- chrys/foundation/models/turns.py +125 -0
- chrys/foundation/models/workflow_session.py +89 -0
- chrys/foundation/models/workspace.py +59 -0
- chrys/foundation/net/__init__.py +3 -0
- chrys/foundation/net/address_class.py +57 -0
- chrys/foundation/net/route_probe.py +94 -0
- chrys/foundation/net/url.py +107 -0
- chrys/foundation/observability/__init__.py +3 -0
- chrys/foundation/observability/exporters.py +174 -0
- chrys/foundation/observability/gate.py +31 -0
- chrys/foundation/observability/setup.py +497 -0
- chrys/foundation/observability/sink.py +228 -0
- chrys/foundation/patches/__init__.py +19 -0
- chrys/foundation/patches/mdurl_cache.py +59 -0
- chrys/foundation/patches/patcher.py +287 -0
- chrys/foundation/patches/staged_members.py +205 -0
- chrys/foundation/patches/textual_block_border.py +105 -0
- chrys/foundation/patches/textual_button_ansi.py +65 -0
- chrys/foundation/patches/textual_button_ansi_old.txt +68 -0
- chrys/foundation/patches/textual_callback_cache.py +248 -0
- chrys/foundation/patches/textual_callback_dispatch.py +109 -0
- chrys/foundation/patches/textual_compositor_cjk.py +566 -0
- chrys/foundation/patches/textual_dispatch_cache.py +150 -0
- chrys/foundation/patches/textual_ime_cursor_anchor.py +92 -0
- chrys/foundation/patches/textual_kitty_keyboard.py +268 -0
- chrys/foundation/patches/textual_lru_acyclic.py +241 -0
- chrys/foundation/patches/textual_message_pump.py +136 -0
- chrys/foundation/patches/textual_node_diet.py +875 -0
- chrys/foundation/patches/textual_one_shot_timer.py +48 -0
- chrys/foundation/patches/textual_option_list.py +90 -0
- chrys/foundation/patches/textual_precompose.py +89 -0
- chrys/foundation/patches/textual_pruned_tabs.py +69 -0
- chrys/foundation/patches/textual_reflow_reuse.py +1753 -0
- chrys/foundation/patches/textual_removed_node_caches.py +86 -0
- chrys/foundation/patches/textual_removed_screen_callbacks.py +55 -0
- chrys/foundation/patches/textual_selection_extract.py +48 -0
- chrys/foundation/patches/textual_strip_cycles.py +119 -0
- chrys/foundation/patches/textual_tab_selection.py +1611 -0
- chrys/foundation/patches/textual_timer_skip.py +70 -0
- chrys/foundation/patches/textual_utf8_decoder.py +106 -0
- chrys/foundation/patches/textual_win_sleep.py +167 -0
- chrys/foundation/patches/watchdog_fsevents.py +70 -0
- chrys/foundation/patches/watchdog_windows.py +52 -0
- chrys/foundation/platform/__init__.py +556 -0
- chrys/foundation/platform/c_api.py +32 -0
- chrys/foundation/platform/child_reap.py +107 -0
- chrys/foundation/platform/command_line.py +95 -0
- chrys/foundation/platform/files.py +1470 -0
- chrys/foundation/platform/output_capture.py +113 -0
- chrys/foundation/platform/paths.py +193 -0
- chrys/foundation/platform/process.py +1468 -0
- chrys/foundation/platform/pty_output.py +19 -0
- chrys/foundation/platform/runtime_paths.py +281 -0
- chrys/foundation/platform/win_console.py +129 -0
- chrys/foundation/reasoning_origin.py +67 -0
- chrys/foundation/recovery.py +16 -0
- chrys/foundation/retry.py +278 -0
- chrys/foundation/text/__init__.py +3 -0
- chrys/foundation/text/encoding.py +603 -0
- chrys/foundation/text/images.py +304 -0
- chrys/foundation/text/lines.py +24 -0
- chrys/foundation/text/mentions.py +90 -0
- chrys/foundation/text/model_json.py +25 -0
- chrys/foundation/text/reading_order.py +20 -0
- chrys/foundation/text/tokenizer.py +25 -0
- chrys/foundation/text/tool_output.py +287 -0
- chrys/foundation/text/yaml_io.py +62 -0
- chrys/foundation/tool_call_context.py +202 -0
- chrys/foundation/tool_execution_stamp.py +184 -0
- chrys/foundation/tool_invocation_order.py +48 -0
- chrys/foundation/tool_kinds.py +102 -0
- chrys/foundation/tool_result_metadata.py +287 -0
- chrys/foundation/trajectory/__init__.py +10 -0
- chrys/foundation/trajectory/context.py +481 -0
- chrys/foundation/trajectory/envelope.py +571 -0
- chrys/foundation/trajectory/event_types.py +294 -0
- chrys/foundation/trajectory/finalizers.py +46 -0
- chrys/foundation/trajectory/fingerprint.py +62 -0
- chrys/foundation/trajectory/ids.py +155 -0
- chrys/foundation/trajectory/keys.py +99 -0
- chrys/foundation/trajectory/lease.py +173 -0
- chrys/foundation/trajectory/metadata.py +72 -0
- chrys/foundation/trajectory/reader.py +271 -0
- chrys/foundation/trajectory/recovery.py +225 -0
- chrys/foundation/trajectory/revisions.py +152 -0
- chrys/foundation/trajectory/segments.py +225 -0
- chrys/foundation/trajectory/tools.py +117 -0
- chrys/foundation/trajectory/usage.py +151 -0
- chrys/foundation/trajectory/writer.py +972 -0
- chrys/foundation/trajectory_timing.py +97 -0
- chrys/foundation/util/__init__.py +3 -0
- chrys/foundation/util/async_tasks.py +29 -0
- chrys/foundation/util/chrys_headers.py +23 -0
- chrys/foundation/util/env_templates.py +104 -0
- chrys/foundation/util/filenames.py +96 -0
- chrys/foundation/util/header_charset.py +138 -0
- chrys/foundation/util/httpx_helpers.py +12 -0
- chrys/foundation/util/lock.py +236 -0
- chrys/foundation/util/once_close.py +46 -0
- chrys/foundation/util/session_ids.py +29 -0
- chrys/foundation/util/shell_tokens.py +88 -0
- chrys/foundation/util/sub_agent_context.py +38 -0
- chrys/foundation/util/time.py +35 -0
- chrys/foundation/util/unicode_scalars.py +36 -0
- chrys/foundation/vendor/__init__.py +43 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-unknown-linux-gnu +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-unknown-linux-musl +0 -0
- chrys/kernel/__init__.py +256 -0
- chrys/kernel/_content.py +1866 -0
- chrys/kernel/_loop_recorder.py +631 -0
- chrys/kernel/_null_overlay.py +1026 -0
- chrys/kernel/_result_ceiling.py +216 -0
- chrys/kernel/_serialization.py +431 -0
- chrys/kernel/_tool_arg_errors.py +1629 -0
- chrys/kernel/_tool_execution.py +1172 -0
- chrys/kernel/_tool_expansion.py +26 -0
- chrys/kernel/_types.py +2598 -0
- chrys/kernel/agent.py +994 -0
- chrys/kernel/client.py +1084 -0
- chrys/kernel/compaction.py +1315 -0
- chrys/kernel/exceptions.py +211 -0
- chrys/kernel/exchanges.py +683 -0
- chrys/kernel/identity.py +95 -0
- chrys/kernel/images.py +36 -0
- chrys/kernel/instrumentation.py +1618 -0
- chrys/kernel/loop.py +2308 -0
- chrys/kernel/middleware.py +616 -0
- chrys/kernel/sessions.py +604 -0
- chrys/kernel/tools.py +1061 -0
- chrys/kernel/types.py +126 -0
- chrys/orchestration/__init__.py +3 -0
- chrys/orchestration/engine/__init__.py +3 -0
- chrys/orchestration/engine/assembly.py +236 -0
- chrys/orchestration/engine/build/__init__.py +3 -0
- chrys/orchestration/engine/build/builder.py +1061 -0
- chrys/orchestration/engine/build/construction.py +408 -0
- chrys/orchestration/engine/build/loaded.py +139 -0
- chrys/orchestration/engine/engine.py +789 -0
- chrys/orchestration/engine/execution.py +771 -0
- chrys/orchestration/engine/loader.py +865 -0
- chrys/orchestration/engine/rollback.py +918 -0
- chrys/orchestration/engine/run/__init__.py +3 -0
- chrys/orchestration/engine/run/active_injection.py +419 -0
- chrys/orchestration/engine/run/attachments.py +519 -0
- chrys/orchestration/engine/run/bindings.py +1095 -0
- chrys/orchestration/engine/run/coordinator.py +1062 -0
- chrys/orchestration/engine/run/finalizer.py +417 -0
- chrys/orchestration/engine/run/input_refs.py +60 -0
- chrys/orchestration/engine/run/prompt_content.py +457 -0
- chrys/orchestration/engine/run/resume.py +299 -0
- chrys/orchestration/engine/run/retry.py +948 -0
- chrys/orchestration/engine/run/runner.py +625 -0
- chrys/orchestration/engine/run/runtime_skills.py +146 -0
- chrys/orchestration/engine/run/sub_agent_coordination.py +126 -0
- chrys/orchestration/engine/run/turn_hooks.py +258 -0
- chrys/orchestration/engine/run/turn_state.py +149 -0
- chrys/orchestration/engine/run/working_dir.py +50 -0
- chrys/orchestration/engine/session_lifecycle.py +2013 -0
- chrys/orchestration/engine/state/__init__.py +3 -0
- chrys/orchestration/engine/state/active_session.py +183 -0
- chrys/orchestration/engine/state/controls.py +639 -0
- chrys/orchestration/engine/state/current_agent.py +26 -0
- chrys/orchestration/engine/state/lifecycle_permits.py +395 -0
- chrys/orchestration/engine/state/machine.py +193 -0
- chrys/orchestration/engine/state/session_writer.py +434 -0
- chrys/orchestration/engine/trajectory.py +714 -0
- chrys/orchestration/engine/usage.py +118 -0
- chrys/orchestration/invoker/__init__.py +3 -0
- chrys/orchestration/invoker/acp.py +727 -0
- chrys/orchestration/invoker/acp_protocol.py +1115 -0
- chrys/orchestration/invoker/acp_spec.py +79 -0
- chrys/orchestration/invoker/attempts.py +994 -0
- chrys/orchestration/invoker/child_compaction.py +131 -0
- chrys/orchestration/invoker/child_history.py +99 -0
- chrys/orchestration/invoker/contracts.py +253 -0
- chrys/orchestration/invoker/evidence.py +94 -0
- chrys/orchestration/invoker/kernel.py +343 -0
- chrys/orchestration/invoker/origin.py +79 -0
- chrys/orchestration/invoker/resources.py +343 -0
- chrys/orchestration/invoker/runtime.py +299 -0
- chrys/orchestration/invoker/usage.py +80 -0
- chrys/orchestration/session_hooks.py +117 -0
- chrys/orchestration/session_host.py +1023 -0
- chrys/orchestration/session_resources.py +95 -0
- chrys/orchestration/session_usage.py +219 -0
- chrys/orchestration/startup.py +255 -0
- chrys/orchestration/sub_agents/__init__.py +3 -0
- chrys/orchestration/sub_agents/acp_policy.py +269 -0
- chrys/orchestration/sub_agents/kernel_policy.py +848 -0
- chrys/orchestration/sub_agents/registration.py +44 -0
- chrys/orchestration/sub_agents/shell.py +337 -0
- chrys/orchestration/sub_agents/tools.py +2228 -0
- chrys/orchestration/workflows/__init__.py +3 -0
- chrys/orchestration/workflows/agent_archive.py +156 -0
- chrys/orchestration/workflows/agent_node.py +894 -0
- chrys/orchestration/workflows/agent_node_build.py +687 -0
- chrys/orchestration/workflows/catalog.py +332 -0
- chrys/orchestration/workflows/coordinator.py +771 -0
- chrys/orchestration/workflows/hooks.py +176 -0
- chrys/orchestration/workflows/preview.py +322 -0
- chrys/orchestration/workflows/rollback.py +151 -0
- chrys/orchestration/workflows/runner.py +986 -0
- chrys/orchestration/workflows/session.py +390 -0
- chrys/orchestration/workflows/settings.py +97 -0
- chrys/orchestration/workflows/validation.py +674 -0
- chrys/orchestration/workflows/worker_client.py +1002 -0
- chrys/service/__init__.py +3 -0
- chrys/service/acp_client/__init__.py +65 -0
- chrys/service/acp_client/_transport.py +922 -0
- chrys/service/acp_client/client.py +1311 -0
- chrys/service/acp_client/errors.py +224 -0
- chrys/service/acp_client/protocol.py +220 -0
- chrys/service/acp_client/spawn.py +239 -0
- chrys/service/acp_client/spec.py +100 -0
- chrys/service/agent_middleware/__init__.py +60 -0
- chrys/service/agent_middleware/_metadata_keys.py +52 -0
- chrys/service/agent_middleware/control/__init__.py +3 -0
- chrys/service/agent_middleware/control/approval.py +769 -0
- chrys/service/agent_middleware/control/approval_hooks.py +62 -0
- chrys/service/agent_middleware/control/ask_user.py +207 -0
- chrys/service/agent_middleware/control/interrupt.py +50 -0
- chrys/service/agent_middleware/control/sleep.py +118 -0
- chrys/service/agent_middleware/control/todo.py +117 -0
- chrys/service/agent_middleware/events/__init__.py +3 -0
- chrys/service/agent_middleware/events/hook_dispatch.py +250 -0
- chrys/service/agent_middleware/events/hosted_tools.py +1762 -0
- chrys/service/agent_middleware/events/intermediate_text.py +128 -0
- chrys/service/agent_middleware/events/rejection_metadata.py +50 -0
- chrys/service/agent_middleware/events/result_persistence.py +98 -0
- chrys/service/agent_middleware/events/sub_agent_events.py +951 -0
- chrys/service/agent_middleware/events/tool_error_text.py +22 -0
- chrys/service/agent_middleware/events/tool_events.py +779 -0
- chrys/service/agent_middleware/injection.py +510 -0
- chrys/service/agent_middleware/reminders/__init__.py +91 -0
- chrys/service/agent_middleware/reminders/archive_pointer.py +154 -0
- chrys/service/agent_middleware/reminders/base.py +25 -0
- chrys/service/agent_middleware/reminders/context_usage.py +74 -0
- chrys/service/agent_middleware/reminders/file_change.py +63 -0
- chrys/service/agent_middleware/reminders/mcp.py +39 -0
- chrys/service/agent_middleware/reminders/profile_switch.py +106 -0
- chrys/service/agent_middleware/reminders/runtime_env.py +190 -0
- chrys/service/agent_middleware/reminders/skills.py +44 -0
- chrys/service/agent_middleware/reminders/sub_agents.py +35 -0
- chrys/service/agent_middleware/reminders/todo.py +35 -0
- chrys/service/agent_middleware/reminders/turn_line.py +48 -0
- chrys/service/agent_middleware/response_validation.py +1380 -0
- chrys/service/agent_middleware/system_reminder.py +1107 -0
- chrys/service/agent_middleware/validators.py +396 -0
- chrys/service/analytics/__init__.py +124 -0
- chrys/service/analytics/_actions.py +896 -0
- chrys/service/analytics/_context_evidence.py +357 -0
- chrys/service/analytics/_critical_path.py +179 -0
- chrys/service/analytics/_facts.py +919 -0
- chrys/service/analytics/_insights.py +769 -0
- chrys/service/analytics/_metric_ops.py +83 -0
- chrys/service/analytics/_session_projection.py +460 -0
- chrys/service/analytics/_timeline.py +434 -0
- chrys/service/analytics/_turn_graph.py +1237 -0
- chrys/service/analytics/_turns.py +1377 -0
- chrys/service/analytics/_workflows.py +171 -0
- chrys/service/analytics/aggregation.py +1474 -0
- chrys/service/analytics/classification.py +128 -0
- chrys/service/analytics/export.py +803 -0
- chrys/service/analytics/findings.py +282 -0
- chrys/service/analytics/math.py +79 -0
- chrys/service/analytics/model.py +886 -0
- chrys/service/analytics/reader.py +321 -0
- chrys/service/approval/__init__.py +3 -0
- chrys/service/approval/arbitration.py +102 -0
- chrys/service/approval/command_identity.py +109 -0
- chrys/service/approval/correlation.py +57 -0
- chrys/service/approval/grant_store.py +108 -0
- chrys/service/approval/judge.py +589 -0
- chrys/service/approval/level.py +15 -0
- chrys/service/approval/policy.py +209 -0
- chrys/service/approval/reuse.py +223 -0
- chrys/service/approval/reuse_binding.py +133 -0
- chrys/service/approval/safety_classifier.py +536 -0
- chrys/service/approval/turn_context.py +49 -0
- chrys/service/context/__init__.py +3 -0
- chrys/service/context/compaction/__init__.py +38 -0
- chrys/service/context/compaction/breaker.py +156 -0
- chrys/service/context/compaction/budgets.py +93 -0
- chrys/service/context/compaction/calibration.py +77 -0
- chrys/service/context/compaction/compression.py +690 -0
- chrys/service/context/compaction/current_turn_drop.py +458 -0
- chrys/service/context/compaction/events.py +201 -0
- chrys/service/context/compaction/exclusions.py +261 -0
- chrys/service/context/compaction/groups.py +361 -0
- chrys/service/context/compaction/last_words.py +1996 -0
- chrys/service/context/compaction/last_words_state.py +752 -0
- chrys/service/context/compaction/scoped.py +472 -0
- chrys/service/context/compaction/spill.py +1947 -0
- chrys/service/context/compaction/strategy.py +1116 -0
- chrys/service/context/compaction/summaries.py +319 -0
- chrys/service/context/compaction/turns.py +375 -0
- chrys/service/context/manager.py +174 -0
- chrys/service/context/memory_loader.py +792 -0
- chrys/service/context/middleware/__init__.py +3 -0
- chrys/service/context/middleware/usage.py +332 -0
- chrys/service/context/providers/__init__.py +3 -0
- chrys/service/context/providers/context_management.py +432 -0
- chrys/service/context/providers/history.py +647 -0
- chrys/service/context/providers/memory.py +50 -0
- chrys/service/context/tool_content.py +22 -0
- chrys/service/hooks/__init__.py +37 -0
- chrys/service/hooks/detached_worker.py +118 -0
- chrys/service/hooks/docs/README.md +14 -0
- chrys/service/hooks/docs/authoring.md +684 -0
- chrys/service/hooks/docs/configuration.md +499 -0
- chrys/service/hooks/docs/design.md +359 -0
- chrys/service/hooks/docs/project-hooks.md +348 -0
- chrys/service/hooks/events.py +157 -0
- chrys/service/hooks/loader.py +665 -0
- chrys/service/hooks/manager.py +807 -0
- chrys/service/hooks/matcher.py +111 -0
- chrys/service/hooks/outbox.py +294 -0
- chrys/service/hooks/runner.py +572 -0
- chrys/service/hooks/schema.py +407 -0
- chrys/service/llm/__init__.py +7 -0
- chrys/service/llm/_structured_outputs.py +192 -0
- chrys/service/llm/anthropic_messages/__init__.py +9 -0
- chrys/service/llm/anthropic_messages/client.py +254 -0
- chrys/service/llm/anthropic_messages/decode.py +331 -0
- chrys/service/llm/anthropic_messages/history.py +279 -0
- chrys/service/llm/anthropic_messages/request.py +323 -0
- chrys/service/llm/anthropic_messages/server_tools.py +319 -0
- chrys/service/llm/anthropic_messages/stream.py +297 -0
- chrys/service/llm/anthropic_messages/thinking_binding.py +156 -0
- chrys/service/llm/chat_completions/__init__.py +9 -0
- chrys/service/llm/chat_completions/client.py +369 -0
- chrys/service/llm/chat_completions/decode.py +338 -0
- chrys/service/llm/chat_completions/history.py +657 -0
- chrys/service/llm/chat_completions/reasoning.py +244 -0
- chrys/service/llm/chat_completions/request.py +192 -0
- chrys/service/llm/chat_completions/stream.py +546 -0
- chrys/service/llm/chat_completions/validation.py +308 -0
- chrys/service/llm/clients.py +613 -0
- chrys/service/llm/images.py +65 -0
- chrys/service/llm/mock.py +413 -0
- chrys/service/llm/observer.py +446 -0
- chrys/service/llm/one_shot.py +106 -0
- chrys/service/llm/openai_exceptions.py +93 -0
- chrys/service/llm/openai_responses/__init__.py +10 -0
- chrys/service/llm/openai_responses/client.py +323 -0
- chrys/service/llm/openai_responses/decode.py +432 -0
- chrys/service/llm/openai_responses/history.py +519 -0
- chrys/service/llm/openai_responses/hosted.py +625 -0
- chrys/service/llm/openai_responses/replay.py +629 -0
- chrys/service/llm/openai_responses/request.py +342 -0
- chrys/service/llm/openai_responses/stream.py +847 -0
- chrys/service/llm/openai_timestamps.py +57 -0
- chrys/service/llm/providers.py +107 -0
- chrys/service/llm/proxy_route.py +54 -0
- chrys/service/llm/raw_http_log.py +309 -0
- chrys/service/llm/route_facts.py +63 -0
- chrys/service/llm/route_sessions.py +49 -0
- chrys/service/llm/sdk_anthropic.py +18 -0
- chrys/service/llm/sdk_headers.py +47 -0
- chrys/service/llm/sdk_openai.py +18 -0
- chrys/service/llm/sdk_retry.py +59 -0
- chrys/service/llm/wire_client.py +246 -0
- chrys/service/mcp/__init__.py +3 -0
- chrys/service/mcp/_connection.py +235 -0
- chrys/service/mcp/_http_transport.py +350 -0
- chrys/service/mcp/_progressive.py +459 -0
- chrys/service/mcp/_stdio_transport.py +614 -0
- chrys/service/mcp/_tool_mixins.py +96 -0
- chrys/service/mcp/adapter.py +1031 -0
- chrys/service/mcp/cache.py +476 -0
- chrys/service/mcp/content.py +32 -0
- chrys/service/mcp/errors.py +169 -0
- chrys/service/mcp/owned.py +1330 -0
- chrys/service/mcp/result_limits.py +168 -0
- chrys/service/mcp/thinking_warning.py +95 -0
- chrys/service/mcp/validation.py +74 -0
- chrys/service/mutations/__init__.py +84 -0
- chrys/service/mutations/coordination.py +894 -0
- chrys/service/mutations/detector.py +458 -0
- chrys/service/mutations/git_calibrator.py +397 -0
- chrys/service/mutations/git_state.py +647 -0
- chrys/service/mutations/pipeline.py +779 -0
- chrys/service/mutations/scanner.py +466 -0
- chrys/service/mutations/snapshot_files.py +233 -0
- chrys/service/mutations/store.py +625 -0
- chrys/service/mutations/tool_names.py +15 -0
- chrys/service/mutations/trace.py +246 -0
- chrys/service/mutations/tracker.py +1289 -0
- chrys/service/mutations/types.py +559 -0
- chrys/service/mutations/workspace_changes.py +1583 -0
- chrys/service/profiles/__init__.py +2 -0
- chrys/service/profiles/agents/__init__.py +3 -0
- chrys/service/profiles/agents/builtins/Code.yaml +199 -0
- chrys/service/profiles/agents/builtins/Explore.yaml +62 -0
- chrys/service/profiles/agents/builtins/General.yaml +114 -0
- chrys/service/profiles/agents/builtins/QA.yaml +128 -0
- chrys/service/profiles/agents/loader.py +808 -0
- chrys/service/profiles/agents/registry.py +727 -0
- chrys/service/profiles/agents/schema.py +371 -0
- chrys/service/profiles/agents/serializer.py +196 -0
- chrys/service/profiles/coercion.py +22 -0
- chrys/service/profiles/models/__init__.py +3 -0
- chrys/service/profiles/models/env_bridge.py +157 -0
- chrys/service/profiles/models/loader.py +282 -0
- chrys/service/profiles/models/options.py +438 -0
- chrys/service/profiles/models/registry.py +104 -0
- chrys/service/profiles/models/resolver.py +286 -0
- chrys/service/profiles/models/schema.py +91 -0
- chrys/service/profiles/models/serializer.py +76 -0
- chrys/service/session/__init__.py +3 -0
- chrys/service/session/checkpoint.py +156 -0
- chrys/service/session/history.py +1616 -0
- chrys/service/session/message_metadata.py +151 -0
- chrys/service/session/persistence.py +435 -0
- chrys/service/session/runtime_metadata.py +210 -0
- chrys/service/session/sub_agent_logs.py +1069 -0
- chrys/service/session/sub_agent_transcript.py +654 -0
- chrys/service/skills/__init__.py +3 -0
- chrys/service/skills/adapter.py +408 -0
- chrys/service/skills/constants.py +8 -0
- chrys/service/skills/loader.py +575 -0
- chrys/service/skills/model.py +130 -0
- chrys/service/skills/provider.py +841 -0
- chrys/service/skills/runner.py +390 -0
- chrys/service/state/__init__.py +3 -0
- chrys/service/state/_fork.py +710 -0
- chrys/service/state/_session_files.py +175 -0
- chrys/service/state/_session_meta.py +552 -0
- chrys/service/state/locks.py +154 -0
- chrys/service/state/serializers.py +156 -0
- chrys/service/state/session_catalog.py +361 -0
- chrys/service/state/session_listing.py +84 -0
- chrys/service/state/session_migration.py +446 -0
- chrys/service/state/session_mru.py +315 -0
- chrys/service/state/store.py +2132 -0
- chrys/service/state/workflow.py +158 -0
- chrys/service/text_blocks.py +47 -0
- chrys/service/todos/__init__.py +3 -0
- chrys/service/todos/reminder.py +31 -0
- chrys/service/todos/tracker.py +58 -0
- chrys/service/tools/__init__.py +3 -0
- chrys/service/tools/approval_targets.py +92 -0
- chrys/service/tools/builtins/__init__.py +3 -0
- chrys/service/tools/builtins/ask_user.py +326 -0
- chrys/service/tools/builtins/doc_converter/__init__.py +494 -0
- chrys/service/tools/builtins/doc_converter/artifacts.py +392 -0
- chrys/service/tools/builtins/doc_converter/parsers/__init__.py +3 -0
- chrys/service/tools/builtins/doc_converter/parsers/_table.py +34 -0
- chrys/service/tools/builtins/doc_converter/parsers/base.py +101 -0
- chrys/service/tools/builtins/doc_converter/parsers/docx.py +88 -0
- chrys/service/tools/builtins/doc_converter/parsers/pdf.py +144 -0
- chrys/service/tools/builtins/doc_converter/parsers/pptx.py +103 -0
- chrys/service/tools/builtins/doc_converter/parsers/xls.py +32 -0
- chrys/service/tools/builtins/doc_converter/parsers/xlsx.py +51 -0
- chrys/service/tools/builtins/doc_converter/registry.py +55 -0
- chrys/service/tools/builtins/filesystem.py +1071 -0
- chrys/service/tools/builtins/search.py +1265 -0
- chrys/service/tools/builtins/shell.py +967 -0
- chrys/service/tools/builtins/shell_command_filtering/__init__.py +5 -0
- chrys/service/tools/builtins/shell_command_filtering/policy.py +60 -0
- chrys/service/tools/builtins/shell_command_filtering/presets.py +152 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/__init__.py +358 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/commands.py +416 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/git.py +408 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/syntax.py +87 -0
- chrys/service/tools/builtins/shell_command_filtering/tokenizer.py +214 -0
- chrys/service/tools/builtins/shell_filter.py +24 -0
- chrys/service/tools/builtins/sleep.py +91 -0
- chrys/service/tools/builtins/todo.py +64 -0
- chrys/service/tools/builtins/web/__init__.py +6 -0
- chrys/service/tools/builtins/web/build.py +268 -0
- chrys/service/tools/builtins/web/config.py +338 -0
- chrys/service/tools/builtins/web/credentials.py +37 -0
- chrys/service/tools/builtins/web/fetch/__init__.py +3 -0
- chrys/service/tools/builtins/web/fetch/cache.py +68 -0
- chrys/service/tools/builtins/web/fetch/convert.py +56 -0
- chrys/service/tools/builtins/web/fetch/tool.py +223 -0
- chrys/service/tools/builtins/web/hosted.py +61 -0
- chrys/service/tools/builtins/web/http.py +330 -0
- chrys/service/tools/builtins/web/progress.py +18 -0
- chrys/service/tools/builtins/web/search/__init__.py +3 -0
- chrys/service/tools/builtins/web/search/exa_mcp.py +146 -0
- chrys/service/tools/builtins/web/search/html.py +152 -0
- chrys/service/tools/builtins/web/search/json_api.py +227 -0
- chrys/service/tools/builtins/web/search/providers.py +77 -0
- chrys/service/tools/builtins/web/search/tool.py +218 -0
- chrys/service/tools/builtins/web/search/types.py +28 -0
- chrys/service/tools/builtins/web/tls_identity.py +66 -0
- chrys/service/tools/kinds.py +115 -0
- chrys/service/tools/names.py +47 -0
- chrys/service/tools/registry.py +242 -0
- chrys/service/tools/result_metadata.py +103 -0
- chrys/service/tools/session_artifacts.py +196 -0
- chrys/service/tools/spill.py +218 -0
- chrys/service/tools/workspace_paths.py +81 -0
- chrys/service/trajectory/__init__.py +11 -0
- chrys/service/trajectory/approvals.py +201 -0
- chrys/service/trajectory/compaction.py +420 -0
- chrys/service/trajectory/fork.py +75 -0
- chrys/service/trajectory/hooks.py +217 -0
- chrys/service/trajectory/items.py +41 -0
- chrys/service/trajectory/preparation.py +374 -0
- chrys/service/trajectory/retries.py +95 -0
- chrys/service/trajectory/revisions.py +129 -0
- chrys/service/trajectory/segmented.py +163 -0
- chrys/service/trajectory/session.py +762 -0
- chrys/service/trajectory/state.py +83 -0
- chrys/service/trajectory/sub_agents.py +169 -0
- chrys/service/trajectory/tombstone.py +262 -0
- chrys/service/trajectory/tools.py +323 -0
- chrys/service/trajectory/validation.py +130 -0
- chrys/service/trajectory/waits.py +161 -0
- chrys/service/trajectory/workflow.py +148 -0
- chrys/service/vision.py +128 -0
- chrys/service/workflows/__init__.py +3 -0
- chrys/service/workflows/admission.py +151 -0
- chrys/service/workflows/artifacts.py +109 -0
- chrys/service/workflows/asks.py +130 -0
- chrys/service/workflows/builtins/__init__.py +12 -0
- chrys/service/workflows/builtins/demo-workflow.manifest.json +635 -0
- chrys/service/workflows/builtins/demo-workflow.py +756 -0
- chrys/service/workflows/discovery.py +554 -0
- chrys/service/workflows/environment.py +284 -0
- chrys/service/workflows/graph.py +430 -0
- chrys/service/workflows/history.py +82 -0
- chrys/service/workflows/interpreter.py +107 -0
- chrys/service/workflows/journal.py +334 -0
- chrys/service/workflows/layout.py +56 -0
- chrys/service/workflows/ledger.py +149 -0
- chrys/service/workflows/model_selection.py +18 -0
- chrys/service/workflows/orphans.py +209 -0
- chrys/service/workflows/outcomes.py +46 -0
- chrys/service/workflows/protocol.py +210 -0
- chrys/service/workflows/records.py +142 -0
- chrys/service/workflows/scheduler.py +770 -0
- chrys/service/workflows/sdk/__init__.py +51 -0
- chrys/service/workflows/sdk/_ask.py +167 -0
- chrys/service/workflows/sdk/_builder.py +781 -0
- chrys/service/workflows/sdk/_values.py +92 -0
- chrys/service/workflows/sdk_artifact.py +74 -0
- chrys/service/workflows/store.py +494 -0
- chrys/service/workflows/transcript.py +102 -0
- chrys/service/workflows/values.py +73 -0
- chrys/service/workflows/worker_host.py +1820 -0
- chrys/workflows.py +38 -0
- icode_tui-0.29.1.dist-info/METADATA +104 -0
- icode_tui-0.29.1.dist-info/RECORD +1138 -0
- icode_tui-0.29.1.dist-info/WHEEL +4 -0
- icode_tui-0.29.1.dist-info/entry_points.txt +2 -0
- icode_tui-0.29.1.dist-info/licenses/LICENSE +202 -0
- icode_tui-0.29.1.dist-info/licenses/NOTICE +1955 -0
chrys/app/installer.py
ADDED
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""PyApp installer support for the Chrys CLI."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import contextlib
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import shutil
|
|
11
|
+
import stat
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
import tempfile
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
import psutil
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
from rich.text import Text
|
|
21
|
+
from rich.theme import Theme
|
|
22
|
+
|
|
23
|
+
from chrys import DISTRIBUTION_NAME
|
|
24
|
+
from chrys.foundation.branding import APP_COMMAND, APP_DISPLAY_NAME
|
|
25
|
+
from chrys.foundation.platform import get_platform
|
|
26
|
+
|
|
27
|
+
_INSTALLER_THEME = Theme(
|
|
28
|
+
{
|
|
29
|
+
"error": "bold red",
|
|
30
|
+
"warning": "yellow",
|
|
31
|
+
"success": "green",
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# The binary keeps its original name on PATH; the product-named command is an alias of it.
|
|
36
|
+
_INSTALLED_BINARY = "chrys"
|
|
37
|
+
|
|
38
|
+
_CHRYS_PROCESS_BASENAMES = frozenset(
|
|
39
|
+
{
|
|
40
|
+
"chrys",
|
|
41
|
+
"chrys.exe",
|
|
42
|
+
APP_COMMAND,
|
|
43
|
+
f"{APP_COMMAND}.exe",
|
|
44
|
+
"chrys-runtime",
|
|
45
|
+
"chrys-runtime.exe",
|
|
46
|
+
"chrys-runtimew.exe",
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
_CHRYS_CMDLINE_MARKERS = (
|
|
50
|
+
"chrys.app.cli.",
|
|
51
|
+
"chrys.app.tui.app",
|
|
52
|
+
)
|
|
53
|
+
_PROCESS_SCAN_ATTRS = ("pid", "name", "exe", "cmdline")
|
|
54
|
+
_PROCESS_INFO_UNAVAILABLE = object()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True)
|
|
58
|
+
class _RunningChrysProcess:
|
|
59
|
+
"""A Chrys-looking process found during install preflight."""
|
|
60
|
+
|
|
61
|
+
pid: int
|
|
62
|
+
name: str
|
|
63
|
+
command: str
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class _ChrysProcessScan:
|
|
68
|
+
"""Running Chrys processes found by a scan and whether enumeration was complete."""
|
|
69
|
+
|
|
70
|
+
processes: tuple[_RunningChrysProcess, ...]
|
|
71
|
+
complete: bool
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _print_text(text: Text) -> None:
|
|
75
|
+
"""Render installer text through the current stdout capture/terminal."""
|
|
76
|
+
Console(file=sys.stdout, theme=_INSTALLER_THEME, highlight=False).print(text, soft_wrap=True)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _print_line(message: str = "", *, style: str | None = None) -> None:
|
|
80
|
+
"""Print literal installer text with optional Rich styling."""
|
|
81
|
+
_print_text(Text(message) if style is None else Text(message, style=style))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _print_status(label: str, message: str, *, style: str, leading_blank: bool = False) -> None:
|
|
85
|
+
"""Print a status line with a styled label and literal message body."""
|
|
86
|
+
text = Text("\n" if leading_blank else "")
|
|
87
|
+
text.append(f"{label}: {message}", style=style)
|
|
88
|
+
_print_text(text)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _print_error(message: str, *, leading_blank: bool = False) -> None:
|
|
92
|
+
_print_status("Error", message, style="error", leading_blank=leading_blank)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _print_warning(message: str, *, leading_blank: bool = False) -> None:
|
|
96
|
+
_print_status("Warning", message, style="warning", leading_blank=leading_blank)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _print_success(message: str) -> None:
|
|
100
|
+
_print_status("Success", message, style="success")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _parse_pyapp_version_key(name: str) -> tuple[int, ...] | None:
|
|
104
|
+
"""Parse a leading 'A.B[.C[.D]]' numeric prefix from a folder name to a comparable tuple.
|
|
105
|
+
|
|
106
|
+
Trailing suffixes like ``-dev-abc1234`` are tolerated and ignored for ordering.
|
|
107
|
+
Returns ``None`` if the name does not start with a numeric version.
|
|
108
|
+
"""
|
|
109
|
+
m = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?", name)
|
|
110
|
+
if m is None:
|
|
111
|
+
return None
|
|
112
|
+
return tuple(int(g) for g in m.groups() if g is not None)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _runtime_interpreter(version_dir: Path) -> Path | None:
|
|
116
|
+
"""Return the embedded interpreter of a PyApp version directory, if present."""
|
|
117
|
+
for rel in ("bin/python3", "python.exe"):
|
|
118
|
+
candidate = version_dir / "python" / rel
|
|
119
|
+
if candidate.is_file():
|
|
120
|
+
return candidate
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _interpreter_binary_tag(path: Path | None) -> tuple[object, ...] | None:
|
|
125
|
+
"""Identify the executable format and machine type of ``path`` from its header.
|
|
126
|
+
|
|
127
|
+
Reads a few bytes of the ELF / Mach-O / PE header — never executes the
|
|
128
|
+
binary. Returns ``None`` for a missing file or unrecognized format, which
|
|
129
|
+
callers must treat as "unknown platform", never as a match.
|
|
130
|
+
"""
|
|
131
|
+
if path is None:
|
|
132
|
+
return None
|
|
133
|
+
try:
|
|
134
|
+
with open(path, "rb") as f:
|
|
135
|
+
head = f.read(64)
|
|
136
|
+
if head[:4] == b"\x7fELF" and len(head) >= 20:
|
|
137
|
+
# EI_CLASS, EI_DATA, e_machine
|
|
138
|
+
return ("elf", head[4], head[5], head[18:20])
|
|
139
|
+
if head[:4] in (b"\xcf\xfa\xed\xfe", b"\xce\xfa\xed\xfe") and len(head) >= 8:
|
|
140
|
+
# Mach-O magic + cputype
|
|
141
|
+
return ("macho", head[:4], head[4:8])
|
|
142
|
+
if head[:2] == b"MZ" and len(head) >= 64:
|
|
143
|
+
f.seek(int.from_bytes(head[60:64], "little"))
|
|
144
|
+
sig = f.read(6)
|
|
145
|
+
if sig[:4] == b"PE\x00\x00":
|
|
146
|
+
# COFF machine type
|
|
147
|
+
return ("pe", sig[4:6])
|
|
148
|
+
except OSError:
|
|
149
|
+
return None
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _pyapp_cache_dir() -> Path | None:
|
|
154
|
+
"""Mirror PyApp's platform cache directory (``directories`` crate, project "pyapp")."""
|
|
155
|
+
platform = get_platform()
|
|
156
|
+
if platform.is_windows:
|
|
157
|
+
base = os.environ.get("LOCALAPPDATA")
|
|
158
|
+
return Path(base) / "pyapp" / "cache" if base else None
|
|
159
|
+
if platform.is_macos:
|
|
160
|
+
return Path.home() / "Library" / "Caches" / "pyapp"
|
|
161
|
+
xdg = os.environ.get("XDG_CACHE_HOME")
|
|
162
|
+
return (Path(xdg) if xdg else Path.home() / ".cache") / "pyapp"
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# PyApp's project directory name: the offline binaries are built with
|
|
166
|
+
# PYAPP_PROJECT_NAME=chrys, while the pip and uv flavors embed the wheel and
|
|
167
|
+
# PyApp names them after its metadata instead (iCode-TUI, normalized).
|
|
168
|
+
_PYAPP_PROJECT_NAMES = frozenset({"chrys", "icode-tui"})
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _is_default_pyapp_version_dir(exe_path: Path, version_dir: Path) -> bool:
|
|
172
|
+
"""Return whether ``version_dir`` matches PyApp's default Chrys layout."""
|
|
173
|
+
if any(os.environ.get(f"PYAPP_INSTALL_DIR_{name.upper()}") for name in _PYAPP_PROJECT_NAMES):
|
|
174
|
+
return False
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
exe_path.relative_to(version_dir / "python")
|
|
178
|
+
except ValueError:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
distribution_dir = version_dir.parent
|
|
182
|
+
project_dir = distribution_dir.parent
|
|
183
|
+
if distribution_dir == version_dir or project_dir == distribution_dir:
|
|
184
|
+
return False
|
|
185
|
+
if project_dir.name not in _PYAPP_PROJECT_NAMES:
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
data_dir = project_dir.parent
|
|
189
|
+
if data_dir.name == "pyapp":
|
|
190
|
+
return True
|
|
191
|
+
return data_dir.name == "data" and data_dir.parent.name == "pyapp"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _find_pyapp_version_dir(exe_path: Path) -> Path | None:
|
|
195
|
+
"""Find the current PyApp version directory, only for the known-safe default layout."""
|
|
196
|
+
for ancestor in exe_path.parents:
|
|
197
|
+
if _parse_pyapp_version_key(ancestor.name) is None:
|
|
198
|
+
continue
|
|
199
|
+
if _is_default_pyapp_version_dir(exe_path, ancestor):
|
|
200
|
+
return ancestor
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _prune_old_pyapp_versions() -> None:
|
|
205
|
+
"""Remove PyApp version folders older than the current one, keeping the most recent older as a fallback.
|
|
206
|
+
|
|
207
|
+
PyApp's installation directory layout (see https://github.com/ofek/pyapp/blob/master/src/app.rs)::
|
|
208
|
+
|
|
209
|
+
<data_local_dir>/pyapp/<project>/<distribution_id>/<version>/
|
|
210
|
+
|
|
211
|
+
Where ``data_local_dir`` resolves to:
|
|
212
|
+
|
|
213
|
+
* Windows: ``%LOCALAPPDATA%\\pyapp\\data`` (the ``directories`` crate appends ``\\data`` on Windows only)
|
|
214
|
+
* macOS: ``~/Library/Application Support/pyapp``
|
|
215
|
+
* Linux: ``$XDG_DATA_HOME/pyapp`` or ``~/.local/share/pyapp``
|
|
216
|
+
|
|
217
|
+
With ``PYAPP_FULL_ISOLATION=true`` (every flavor we ship), the embedded Python sits at
|
|
218
|
+
``<version>/python/python.exe`` (Windows) or ``<version>/python/bin/python3`` (Unix), so the
|
|
219
|
+
version folder is always an ancestor of ``sys.executable``. Pruning spans every
|
|
220
|
+
``<distribution_id>`` under the project directory: the distribution ID is the hash of the
|
|
221
|
+
embedded archive, and the offline flavor bakes the versioned chrys wheel into its archive, so
|
|
222
|
+
every release — and every rebuild of the same version — ships a new distribution ID. Scanning
|
|
223
|
+
only the current ID would strand each superseded runtime (hundreds of MB) forever. A runtime
|
|
224
|
+
of the current version under another ID is a superseded rebuild and is removed alongside older
|
|
225
|
+
versions. Distribution IDs also differ by architecture (a home directory can be shared
|
|
226
|
+
between machines), so a runtime under another ID is only touched when its interpreter's binary
|
|
227
|
+
header matches the running one; the fallback is likewise chosen among matching runtimes only.
|
|
228
|
+
|
|
229
|
+
Distribution directories emptied by the removals are dropped together with their PyApp cache
|
|
230
|
+
leftovers: the cached copy of the embedded archive (``<cache>/distributions/<id>``, a plain
|
|
231
|
+
re-creatable file) and this project's per-version installation locks. The shared unpacked
|
|
232
|
+
base ``<cache>/distributions/_<id>`` used by non-full-isolation apps is never touched — other
|
|
233
|
+
PyApp-packaged applications symlink into it.
|
|
234
|
+
|
|
235
|
+
Called from ``chrys install`` so users opt in by re-running install on a new version.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
def _force_remove(func, path, _exc):
|
|
239
|
+
# Clear read-only bit (common on Windows) and retry the failed operation.
|
|
240
|
+
os.chmod(path, stat.S_IWRITE)
|
|
241
|
+
func(path)
|
|
242
|
+
|
|
243
|
+
try:
|
|
244
|
+
exe_path = Path(sys.executable).resolve()
|
|
245
|
+
|
|
246
|
+
# Find the version folder by walking up, but only accept the expected
|
|
247
|
+
# default PyApp layout. Install-dir overrides are intentionally skipped:
|
|
248
|
+
# their structure is user-defined, so sibling pruning is not safe.
|
|
249
|
+
version_dir = _find_pyapp_version_dir(exe_path)
|
|
250
|
+
if version_dir is None:
|
|
251
|
+
return # Not running from a PyApp version folder (dev/source install) — nothing to prune.
|
|
252
|
+
|
|
253
|
+
distribution_dir = version_dir.parent
|
|
254
|
+
project_dir = distribution_dir.parent
|
|
255
|
+
if not project_dir.is_dir():
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
current_key = _parse_pyapp_version_key(version_dir.name)
|
|
259
|
+
if current_key is None:
|
|
260
|
+
return
|
|
261
|
+
|
|
262
|
+
current_resolved = version_dir.resolve()
|
|
263
|
+
current_tag = _interpreter_binary_tag(exe_path)
|
|
264
|
+
|
|
265
|
+
# A symlinked (or junction'd) entry would make the scan — and the
|
|
266
|
+
# shutil.rmtree below — operate on paths outside the PyApp tree. Only
|
|
267
|
+
# descend into real directories whose resolved parent is still the
|
|
268
|
+
# project directory, and never consider symlinked version folders.
|
|
269
|
+
project_resolved = project_dir.resolve()
|
|
270
|
+
superseded: list[Path] = [] # current version under an obsolete distribution ID (rebuilds)
|
|
271
|
+
older: list[tuple[tuple[int, ...], Path]] = []
|
|
272
|
+
for dist_dir in project_dir.iterdir():
|
|
273
|
+
if dist_dir.is_symlink() or dist_dir.is_junction() or not dist_dir.is_dir():
|
|
274
|
+
continue
|
|
275
|
+
if dist_dir.resolve().parent != project_resolved:
|
|
276
|
+
continue
|
|
277
|
+
same_distribution = dist_dir == distribution_dir
|
|
278
|
+
for child in dist_dir.iterdir():
|
|
279
|
+
if child.is_symlink() or child.is_junction() or not child.is_dir():
|
|
280
|
+
continue
|
|
281
|
+
if child.resolve() == current_resolved:
|
|
282
|
+
continue
|
|
283
|
+
key = _parse_pyapp_version_key(child.name)
|
|
284
|
+
if key is None or key > current_key:
|
|
285
|
+
continue
|
|
286
|
+
# Within the current distribution ID the platform matches by
|
|
287
|
+
# construction; across IDs, only a runtime whose interpreter
|
|
288
|
+
# header provably matches the running binary belongs to this
|
|
289
|
+
# installation (IDs also differ by architecture).
|
|
290
|
+
if not same_distribution and (
|
|
291
|
+
current_tag is None or _interpreter_binary_tag(_runtime_interpreter(child)) != current_tag
|
|
292
|
+
):
|
|
293
|
+
continue
|
|
294
|
+
if key == current_key:
|
|
295
|
+
superseded.append(child)
|
|
296
|
+
else:
|
|
297
|
+
older.append((key, child))
|
|
298
|
+
|
|
299
|
+
# Sort descending by version; keep older[0] (most recent older) as fallback. Superseded
|
|
300
|
+
# rebuilds of the current version are never useful as a fallback — always remove them.
|
|
301
|
+
older.sort(key=lambda t: t[0], reverse=True)
|
|
302
|
+
kept = older[0][1].name if older else None
|
|
303
|
+
to_remove = [(d, f"superseded rebuild of {d.name}") for d in superseded]
|
|
304
|
+
to_remove += [(d, f"old version {d.name}") for _, d in older[1:]]
|
|
305
|
+
if not to_remove:
|
|
306
|
+
return
|
|
307
|
+
|
|
308
|
+
_print_line(f"\nCleaning up old {APP_DISPLAY_NAME} versions in {project_dir}:")
|
|
309
|
+
removed: list[Path] = []
|
|
310
|
+
for d, label in to_remove:
|
|
311
|
+
try:
|
|
312
|
+
shutil.rmtree(str(d), onexc=_force_remove)
|
|
313
|
+
_print_line(f" Removed {label}.", style="success")
|
|
314
|
+
removed.append(d)
|
|
315
|
+
except OSError as e:
|
|
316
|
+
_print_warning(f"Could not remove {label}: {e}")
|
|
317
|
+
|
|
318
|
+
# PyApp also keeps a copy of every embedded archive in its platform
|
|
319
|
+
# cache, keyed by the same distribution ID, plus a lock file per
|
|
320
|
+
# installed version — reclaim the entries belonging to what was just
|
|
321
|
+
# removed. Both are re-creatable, but never touch entries that still
|
|
322
|
+
# back a surviving runtime.
|
|
323
|
+
cache_dir = _pyapp_cache_dir()
|
|
324
|
+
if cache_dir is not None:
|
|
325
|
+
for d in removed:
|
|
326
|
+
lock = cache_dir / "locks" / f"installation-{project_dir.name}-{d.parent.name}-{d.name}"
|
|
327
|
+
with contextlib.suppress(OSError):
|
|
328
|
+
lock.unlink(missing_ok=True)
|
|
329
|
+
|
|
330
|
+
# Each offline release strands the previous release's <distribution_id>
|
|
331
|
+
# directory; the removals above empty it, so drop the husk too.
|
|
332
|
+
# ``rmdir`` refuses non-empty directories, which keeps this safe for
|
|
333
|
+
# distribution dirs still holding the fallback or unexpected content.
|
|
334
|
+
# Its cached archive is deleted only after the rmdir succeeds, i.e.
|
|
335
|
+
# only once no runtime of that distribution ID remains.
|
|
336
|
+
for dist_dir in project_dir.iterdir():
|
|
337
|
+
if dist_dir.is_symlink() or dist_dir.is_junction() or not dist_dir.is_dir():
|
|
338
|
+
continue
|
|
339
|
+
if dist_dir == distribution_dir:
|
|
340
|
+
continue
|
|
341
|
+
with contextlib.suppress(OSError):
|
|
342
|
+
dist_dir.rmdir()
|
|
343
|
+
if cache_dir is not None:
|
|
344
|
+
(cache_dir / "distributions" / dist_dir.name).unlink(missing_ok=True)
|
|
345
|
+
|
|
346
|
+
if removed:
|
|
347
|
+
kept_summary = f"{version_dir.name}, {kept}" if kept else version_dir.name
|
|
348
|
+
_print_success(f"Removed {len(removed)} obsolete runtime(s); kept {kept_summary}.")
|
|
349
|
+
except Exception as e:
|
|
350
|
+
_print_warning(f"Could not clean up old {APP_DISPLAY_NAME} versions: {e}")
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _find_windows_powershell() -> str:
|
|
354
|
+
"""Find a PowerShell executable for installer helper commands."""
|
|
355
|
+
for name in ("pwsh", "powershell"):
|
|
356
|
+
found = shutil.which(name)
|
|
357
|
+
if found:
|
|
358
|
+
return found
|
|
359
|
+
return "powershell"
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _path_basename(value: str | None) -> str:
|
|
363
|
+
"""Return a lowercase basename for a possibly Windows-style path."""
|
|
364
|
+
if not value:
|
|
365
|
+
return ""
|
|
366
|
+
return value.replace("\\", "/").rsplit("/", 1)[-1].casefold()
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _process_command(cmdline: list[str] | tuple[str, ...] | None) -> str:
|
|
370
|
+
"""Return a readable process command line."""
|
|
371
|
+
if not cmdline:
|
|
372
|
+
return ""
|
|
373
|
+
return " ".join(str(part) for part in cmdline)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _is_python_host_command(cmdline: list[str] | tuple[str, ...] | None) -> bool:
|
|
377
|
+
"""Return whether cmdline looks like a Python interpreter process."""
|
|
378
|
+
if not cmdline:
|
|
379
|
+
return False
|
|
380
|
+
first_arg = _path_basename(str(cmdline[0]))
|
|
381
|
+
return (
|
|
382
|
+
re.fullmatch(
|
|
383
|
+
r"(?:pythonw?|pypy)(?:\d+(?:\.\d+)*)?(?:\.exe)?",
|
|
384
|
+
first_arg,
|
|
385
|
+
)
|
|
386
|
+
is not None
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _is_chrys_process(name: str | None, exe: str | None, cmdline: list[str] | tuple[str, ...] | None) -> bool:
|
|
391
|
+
"""Return whether process metadata looks like a Chrys runtime."""
|
|
392
|
+
if _path_basename(name) in _CHRYS_PROCESS_BASENAMES:
|
|
393
|
+
return True
|
|
394
|
+
if _path_basename(exe) in _CHRYS_PROCESS_BASENAMES:
|
|
395
|
+
return True
|
|
396
|
+
|
|
397
|
+
is_python_host = _is_python_host_command(cmdline)
|
|
398
|
+
for raw_arg in cmdline or ():
|
|
399
|
+
arg = str(raw_arg)
|
|
400
|
+
if _path_basename(arg) in _CHRYS_PROCESS_BASENAMES:
|
|
401
|
+
return True
|
|
402
|
+
if is_python_host and any(marker in arg for marker in _CHRYS_CMDLINE_MARKERS):
|
|
403
|
+
return True
|
|
404
|
+
return False
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _current_install_process_ids() -> set[int]:
|
|
408
|
+
"""Return the installer process plus ancestor PIDs to exclude from checks."""
|
|
409
|
+
ignored = {os.getpid()}
|
|
410
|
+
with contextlib.suppress(psutil.Error, OSError, TypeError, ValueError):
|
|
411
|
+
ignored.update(parent.pid for parent in psutil.Process().parents())
|
|
412
|
+
return ignored
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _find_running_chrys_processes(*, ignored_pids: set[int] | None = None) -> _ChrysProcessScan:
|
|
416
|
+
"""Find other Chrys runtimes and report whether every process could be inspected."""
|
|
417
|
+
ignored = ignored_pids or set()
|
|
418
|
+
running: list[_RunningChrysProcess] = []
|
|
419
|
+
complete = True
|
|
420
|
+
try:
|
|
421
|
+
for proc in psutil.process_iter(_PROCESS_SCAN_ATTRS, ad_value=_PROCESS_INFO_UNAVAILABLE):
|
|
422
|
+
try:
|
|
423
|
+
info = proc.info
|
|
424
|
+
pid = int(info.get("pid") or proc.pid)
|
|
425
|
+
if pid in ignored:
|
|
426
|
+
continue
|
|
427
|
+
name = info.get("name")
|
|
428
|
+
if name is _PROCESS_INFO_UNAVAILABLE:
|
|
429
|
+
name = None
|
|
430
|
+
exe = info.get("exe")
|
|
431
|
+
if exe is _PROCESS_INFO_UNAVAILABLE:
|
|
432
|
+
exe = None
|
|
433
|
+
cmdline = info.get("cmdline")
|
|
434
|
+
if cmdline is _PROCESS_INFO_UNAVAILABLE:
|
|
435
|
+
cmdline = None
|
|
436
|
+
name = str(name or "")
|
|
437
|
+
if not _is_chrys_process(name, str(exe) if exe else None, cmdline):
|
|
438
|
+
# Classification runs on whatever fields are readable, so a positive match (e.g. an
|
|
439
|
+
# elevated Chrys on Windows whose snapshot name stays visible while its command line
|
|
440
|
+
# is access-denied) is never lost. Unreadable fields cannot rescue a negative verdict:
|
|
441
|
+
# PyApp runtimes that can reference version directories under this user's home run as
|
|
442
|
+
# ordinary same-user processes whose fields are readable, so access-denied fields
|
|
443
|
+
# belong to processes outside the cleanup scope (other users, setuid processes, or
|
|
444
|
+
# zombies). A root Chrys launched via sudo with this user's HOME preserved is an
|
|
445
|
+
# accepted exception: the unprivileged installer cannot reliably identify it.
|
|
446
|
+
continue
|
|
447
|
+
running.append(
|
|
448
|
+
_RunningChrysProcess(pid=pid, name=name or "(unknown)", command=_process_command(cmdline))
|
|
449
|
+
)
|
|
450
|
+
except psutil.Error, OSError, TypeError, ValueError:
|
|
451
|
+
complete = False
|
|
452
|
+
continue
|
|
453
|
+
except (psutil.Error, OSError, TypeError, ValueError) as e:
|
|
454
|
+
_print_warning(f"Could not check for running {APP_DISPLAY_NAME} processes: {e}. Continuing install.")
|
|
455
|
+
return _ChrysProcessScan(
|
|
456
|
+
processes=tuple(sorted(running, key=lambda process: process.pid)),
|
|
457
|
+
complete=False,
|
|
458
|
+
)
|
|
459
|
+
return _ChrysProcessScan(processes=tuple(sorted(running, key=lambda process: process.pid)), complete=complete)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _require_no_running_chrys_instances() -> bool:
|
|
463
|
+
"""Block until known Chrys instances exit, returning whether the final scan was complete."""
|
|
464
|
+
ignored_pids = _current_install_process_ids()
|
|
465
|
+
while True:
|
|
466
|
+
scan = _find_running_chrys_processes(ignored_pids=ignored_pids)
|
|
467
|
+
running = scan.processes
|
|
468
|
+
if not running:
|
|
469
|
+
return scan.complete
|
|
470
|
+
|
|
471
|
+
_print_error(f"{APP_DISPLAY_NAME} is already running.", leading_blank=True)
|
|
472
|
+
_print_line(f"Quit or kill all running {APP_DISPLAY_NAME} instances before continuing with install:")
|
|
473
|
+
for process in running:
|
|
474
|
+
summary = f" - PID {process.pid}: {process.name}"
|
|
475
|
+
if process.command:
|
|
476
|
+
summary += f" - {process.command[:160]}"
|
|
477
|
+
_print_line(summary)
|
|
478
|
+
|
|
479
|
+
if not sys.stdin.isatty():
|
|
480
|
+
_print_error(f"Rerun '{APP_COMMAND} install' after those processes have exited.", leading_blank=True)
|
|
481
|
+
sys.exit(1)
|
|
482
|
+
|
|
483
|
+
response = input("\nPress Enter after quitting them to check again, or type 'q' to abort: ").strip().casefold()
|
|
484
|
+
if response in {"q", "quit", "abort", "n", "no"}:
|
|
485
|
+
_print_error("Install aborted.")
|
|
486
|
+
sys.exit(1)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def _atomic_copy_executable(src: Path, dest: Path) -> None:
|
|
490
|
+
"""Copy an executable into place without exposing a partially written destination.
|
|
491
|
+
|
|
492
|
+
The temporary file lives beside ``dest`` so ``os.replace`` cannot cross a filesystem boundary.
|
|
493
|
+
Copying through a distinct path also supports reinstalling from ``dest`` itself.
|
|
494
|
+
"""
|
|
495
|
+
fd, temporary_name = tempfile.mkstemp(prefix=f".{dest.name}.", suffix=".tmp", dir=dest.parent)
|
|
496
|
+
temporary_path = Path(temporary_name)
|
|
497
|
+
try:
|
|
498
|
+
os.close(fd)
|
|
499
|
+
shutil.copy2(str(src), str(temporary_path))
|
|
500
|
+
temporary_path.chmod(0o755)
|
|
501
|
+
os.replace(temporary_path, dest)
|
|
502
|
+
finally:
|
|
503
|
+
with contextlib.suppress(OSError):
|
|
504
|
+
temporary_path.unlink()
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def _install_command_alias(src: Path, dest: Path) -> Path | None:
|
|
508
|
+
"""Make the product-named command start the binary installed at ``dest`` from ``src``.
|
|
509
|
+
|
|
510
|
+
Returns the alias, or ``None`` when there is none to use. POSIX shares ``~/.local/bin`` with
|
|
511
|
+
other tools, so a command of the same name that starts something else is left alone there.
|
|
512
|
+
The Windows directory is the installer's own, and symlinks need privileges there, so it
|
|
513
|
+
takes a hard link or, failing that, a copy.
|
|
514
|
+
"""
|
|
515
|
+
alias = dest.with_name(APP_COMMAND + dest.suffix)
|
|
516
|
+
try:
|
|
517
|
+
# An alias that is the running binary was just copied to ``dest``, so it is current too,
|
|
518
|
+
# and Windows would refuse to unlink it.
|
|
519
|
+
if alias.exists() and (alias.samefile(dest) or alias.samefile(src)):
|
|
520
|
+
return alias
|
|
521
|
+
if get_platform().is_windows:
|
|
522
|
+
alias.unlink(missing_ok=True)
|
|
523
|
+
try:
|
|
524
|
+
os.link(dest, alias)
|
|
525
|
+
except OSError:
|
|
526
|
+
shutil.copy2(str(dest), str(alias))
|
|
527
|
+
else:
|
|
528
|
+
if alias.exists():
|
|
529
|
+
_print_warning(f"{alias} already exists and starts something else, so it was left alone.")
|
|
530
|
+
return None
|
|
531
|
+
# Only a dangling symlink can remain here.
|
|
532
|
+
alias.unlink(missing_ok=True)
|
|
533
|
+
alias.symlink_to(dest.name)
|
|
534
|
+
except OSError as e:
|
|
535
|
+
_print_warning(f"Could not create the '{APP_COMMAND}' command at {alias}: {e}")
|
|
536
|
+
return None
|
|
537
|
+
return alias
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def _installed_commands(alias: Path | None) -> str:
|
|
541
|
+
"""Name the commands that now start the app, the product-named one first."""
|
|
542
|
+
return f"'{APP_COMMAND}' or '{_INSTALLED_BINARY}'" if alias is not None else f"'{_INSTALLED_BINARY}'"
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def install_to_path() -> None:
|
|
546
|
+
"""Copy the running PyApp binary to a PATH-friendly location."""
|
|
547
|
+
binary = os.environ.get("PYAPP", "")
|
|
548
|
+
if not binary:
|
|
549
|
+
# Installed with uv (or pip), whose own commands put iCode on PATH and upgrade it.
|
|
550
|
+
_print_error(f"{APP_COMMAND} install is only for the offline packages.")
|
|
551
|
+
_print_line(
|
|
552
|
+
f"If you installed {APP_DISPLAY_NAME} with uv, it is already set up: run 'uv tool update-shell' if your "
|
|
553
|
+
f"terminal cannot find '{APP_COMMAND}', and 'uv tool upgrade {DISTRIBUTION_NAME}' to upgrade."
|
|
554
|
+
)
|
|
555
|
+
sys.exit(1)
|
|
556
|
+
if binary == "1" or not Path(binary).is_file():
|
|
557
|
+
_print_error(f"{APP_COMMAND} install only works when running from a PyApp binary.")
|
|
558
|
+
_print_line("PYAPP_PASS_LOCATION must be enabled at build time.")
|
|
559
|
+
sys.exit(1)
|
|
560
|
+
|
|
561
|
+
src = Path(binary)
|
|
562
|
+
platform_info = get_platform()
|
|
563
|
+
scan_complete = True
|
|
564
|
+
|
|
565
|
+
if platform_info.is_windows:
|
|
566
|
+
scan_complete = _require_no_running_chrys_instances()
|
|
567
|
+
install_dir = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local")) / "chrys" / "bin"
|
|
568
|
+
install_dir.mkdir(parents=True, exist_ok=True)
|
|
569
|
+
dest = install_dir / f"{_INSTALLED_BINARY}.exe"
|
|
570
|
+
# Installing from the installed binary, or from its alias, has nothing to copy.
|
|
571
|
+
if not (dest.exists() and src.samefile(dest)):
|
|
572
|
+
shutil.copy2(str(src), str(dest))
|
|
573
|
+
alias = _install_command_alias(src, dest)
|
|
574
|
+
_print_success(f"Installed {APP_DISPLAY_NAME} to {dest}. Run it with {_installed_commands(alias)}.")
|
|
575
|
+
|
|
576
|
+
# Add to user PATH if not already there
|
|
577
|
+
from chrys.foundation.platform.process import windows_hidden_subprocess_kwargs
|
|
578
|
+
|
|
579
|
+
result = subprocess.run(
|
|
580
|
+
[
|
|
581
|
+
_find_windows_powershell(),
|
|
582
|
+
"-NoProfile",
|
|
583
|
+
"-Command",
|
|
584
|
+
(
|
|
585
|
+
f'$p = [Environment]::GetEnvironmentVariable("Path","User"); '
|
|
586
|
+
f'if ($p -notlike "*{install_dir}*") {{ '
|
|
587
|
+
f'[Environment]::SetEnvironmentVariable("Path", "{install_dir};$p", "User"); '
|
|
588
|
+
f'"Added {install_dir} to user PATH (restart terminal to take effect)" }} '
|
|
589
|
+
f'else {{ "Already in PATH" }}'
|
|
590
|
+
),
|
|
591
|
+
],
|
|
592
|
+
stdin=subprocess.DEVNULL,
|
|
593
|
+
capture_output=True,
|
|
594
|
+
text=True,
|
|
595
|
+
encoding="utf-8",
|
|
596
|
+
errors="replace",
|
|
597
|
+
**windows_hidden_subprocess_kwargs(),
|
|
598
|
+
)
|
|
599
|
+
if result.returncode == 0:
|
|
600
|
+
path_message = result.stdout.strip()
|
|
601
|
+
if path_message:
|
|
602
|
+
_print_line(path_message)
|
|
603
|
+
else:
|
|
604
|
+
_print_warning(f"{APP_DISPLAY_NAME} was installed, but updating the user PATH failed.")
|
|
605
|
+
detail = result.stderr.strip() or result.stdout.strip()
|
|
606
|
+
if detail:
|
|
607
|
+
_print_line(detail)
|
|
608
|
+
else:
|
|
609
|
+
install_dir = Path.home() / ".local" / "bin"
|
|
610
|
+
install_dir.mkdir(parents=True, exist_ok=True)
|
|
611
|
+
dest = install_dir / _INSTALLED_BINARY
|
|
612
|
+
_atomic_copy_executable(src, dest)
|
|
613
|
+
alias = _install_command_alias(src, dest)
|
|
614
|
+
_print_success(f"Installed {APP_DISPLAY_NAME} to {dest}.")
|
|
615
|
+
|
|
616
|
+
# Check if ~/.local/bin is in PATH
|
|
617
|
+
path_dirs = os.environ.get("PATH", "").split(":")
|
|
618
|
+
if str(install_dir) not in path_dirs:
|
|
619
|
+
shell = os.environ.get("SHELL", "")
|
|
620
|
+
if "zsh" in shell:
|
|
621
|
+
rc_file = "~/.zshrc"
|
|
622
|
+
elif "bash" in shell:
|
|
623
|
+
rc_file = "~/.bashrc"
|
|
624
|
+
else:
|
|
625
|
+
rc_file = "~/.profile"
|
|
626
|
+
_print_warning(f"{install_dir} is not currently on PATH.", leading_blank=True)
|
|
627
|
+
_print_line(f"Add this to {rc_file}:")
|
|
628
|
+
_print_line(' export PATH="$HOME/.local/bin:$PATH"')
|
|
629
|
+
_print_line("Restart your terminal after updating the file.")
|
|
630
|
+
else:
|
|
631
|
+
_print_success(
|
|
632
|
+
f"{APP_DISPLAY_NAME} is already on PATH. You can run {_installed_commands(alias)} from anywhere."
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
running_instances: tuple[_RunningChrysProcess, ...] = ()
|
|
636
|
+
if not platform_info.is_windows:
|
|
637
|
+
# Unlike the Windows preflight, ancestors are not exempt here: installing from a Chrys
|
|
638
|
+
# embedded terminal makes the still-running TUI an ancestor of this process, and its
|
|
639
|
+
# version directory must stay visible to the cleanup gate. PyApp execs on POSIX, so the
|
|
640
|
+
# installer itself is the only process of its own in the chain.
|
|
641
|
+
scan = _find_running_chrys_processes(ignored_pids={os.getpid()})
|
|
642
|
+
running_instances = scan.processes
|
|
643
|
+
scan_complete = scan.complete
|
|
644
|
+
if not scan_complete:
|
|
645
|
+
_print_warning(
|
|
646
|
+
f"Could not fully check for running {APP_DISPLAY_NAME} instances; skipped cleanup of old versions."
|
|
647
|
+
)
|
|
648
|
+
elif running_instances:
|
|
649
|
+
_print_warning(f"Detected running {APP_DISPLAY_NAME} instances; skipped cleanup of old versions.")
|
|
650
|
+
else:
|
|
651
|
+
_prune_old_pyapp_versions()
|
chrys/app/parsing.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Shared command-line parsing helpers."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
from typing import Never
|
|
9
|
+
|
|
10
|
+
from chrys.foundation.i18n.formatting import sanitize_legacy_scalar
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SanitizingArgumentParser(argparse.ArgumentParser):
|
|
14
|
+
"""Argument parser that sanitizes attacker-controlled error details."""
|
|
15
|
+
|
|
16
|
+
def error(self, message: str) -> Never:
|
|
17
|
+
super().error(sanitize_legacy_scalar(message))
|