painapple-code 1.0.0__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.
- painapple_code/__init__.py +30 -0
- painapple_code/__main__.py +18 -0
- painapple_code/_version.py +24 -0
- painapple_code/auth_middleware.py +930 -0
- painapple_code/cli/__init__.py +204 -0
- painapple_code/cli/deploy/__init__.py +15 -0
- painapple_code/cli/deploy/claude_seed.py +41 -0
- painapple_code/cli/deploy/config.py +260 -0
- painapple_code/cli/deploy/container.py +715 -0
- painapple_code/cli/deploy/launch.py +163 -0
- painapple_code/cli/deploy/runtime.py +223 -0
- painapple_code/cli/lifecycle_cmd.py +443 -0
- painapple_code/cli/list_cmd.py +572 -0
- painapple_code/cli/manage_cmd.py +670 -0
- painapple_code/cli/netinfo.py +125 -0
- painapple_code/cli/profiles.py +390 -0
- painapple_code/cli/serve_args.py +163 -0
- painapple_code/cli/serve_config.py +256 -0
- painapple_code/cli/setup_cmd.py +664 -0
- painapple_code/cli/ui.py +438 -0
- painapple_code/cost_analytics.py +879 -0
- painapple_code/data/models.defaults.yaml +18 -0
- painapple_code/data/models.yaml +18 -0
- painapple_code/data/presets.defaults.json +32 -0
- painapple_code/data/strings.yaml +2269 -0
- painapple_code/helpers.py +406 -0
- painapple_code/paths.py +1514 -0
- painapple_code/prompt_explorer.py +984 -0
- painapple_code/providers/__init__.py +224 -0
- painapple_code/providers/base.py +710 -0
- painapple_code/providers/claude/__init__.py +74 -0
- painapple_code/providers/claude/capabilities.py +218 -0
- painapple_code/providers/claude/errors.py +79 -0
- painapple_code/providers/claude/launch.py +50 -0
- painapple_code/providers/claude/summary.py +89 -0
- painapple_code/providers/claude/translate.py +74 -0
- painapple_code/providers/claude_sdk/__init__.py +139 -0
- painapple_code/providers/claude_sdk/driver.py +412 -0
- painapple_code/providers/codex/__init__.py +87 -0
- painapple_code/providers/codex/capabilities.py +321 -0
- painapple_code/providers/codex/errors.py +70 -0
- painapple_code/providers/codex/launch.py +142 -0
- painapple_code/providers/codex/summary.py +196 -0
- painapple_code/providers/codex/translate.py +204 -0
- painapple_code/providers/codex_app_server/__init__.py +100 -0
- painapple_code/providers/codex_app_server/capabilities.py +41 -0
- painapple_code/providers/codex_app_server/launch.py +202 -0
- painapple_code/providers/codex_app_server/summary.py +291 -0
- painapple_code/providers/codex_app_server/translate.py +263 -0
- painapple_code/providers/codex_app_server/transport.py +238 -0
- painapple_code/routes/__init__.py +0 -0
- painapple_code/routes/api_agents.py +441 -0
- painapple_code/routes/api_app.py +368 -0
- painapple_code/routes/api_app_commit_sections.py +127 -0
- painapple_code/routes/api_app_config.py +997 -0
- painapple_code/routes/api_app_session_prefs.py +217 -0
- painapple_code/routes/api_browser.py +690 -0
- painapple_code/routes/api_commands.py +374 -0
- painapple_code/routes/api_costs.py +128 -0
- painapple_code/routes/api_drafts.py +139 -0
- painapple_code/routes/api_exec.py +149 -0
- painapple_code/routes/api_files.py +542 -0
- painapple_code/routes/api_git.py +657 -0
- painapple_code/routes/api_logs.py +649 -0
- painapple_code/routes/api_plugins.py +275 -0
- painapple_code/routes/api_project_config.py +418 -0
- painapple_code/routes/api_prompts.py +231 -0
- painapple_code/routes/api_providers.py +53 -0
- painapple_code/routes/api_search.py +411 -0
- painapple_code/routes/api_session_stash.py +234 -0
- painapple_code/routes/api_session_welcome.py +151 -0
- painapple_code/routes/api_sessions.py +742 -0
- painapple_code/routes/api_shadow.py +323 -0
- painapple_code/routes/api_shadow_db.py +512 -0
- painapple_code/routes/api_shadow_files.py +332 -0
- painapple_code/routes/api_shadow_search.py +476 -0
- painapple_code/routes/api_skills.py +610 -0
- painapple_code/routes/api_tasks.py +147 -0
- painapple_code/routes/api_terminal.py +540 -0
- painapple_code/routes/api_upload.py +293 -0
- painapple_code/routes/api_viewer.py +229 -0
- painapple_code/routes/dependencies.py +109 -0
- painapple_code/routes/ws_chat.py +837 -0
- painapple_code/server.py +2208 -0
- painapple_code/server_logging.py +263 -0
- painapple_code/services/__init__.py +0 -0
- painapple_code/services/agent_session.py +2738 -0
- painapple_code/session_store.py +253 -0
- painapple_code/session_store_core.py +879 -0
- painapple_code/shadow_db.py +830 -0
- painapple_code/shadow_db_plans.py +369 -0
- painapple_code/shadow_db_queries.py +595 -0
- painapple_code/shadow_db_schema.py +676 -0
- painapple_code/shadow_git.py +1296 -0
- painapple_code/shadow_git_frontmatter.py +314 -0
- painapple_code/shadow_git_sections.py +520 -0
- painapple_code/shadow_git_summary.py +462 -0
- painapple_code/shadow_parser.py +560 -0
- painapple_code/static/css/00-variables.css +276 -0
- painapple_code/static/css/01-base.css +245 -0
- painapple_code/static/css/02-tooltips.css +36 -0
- painapple_code/static/css/09-left-rail.css +448 -0
- painapple_code/static/css/10-header.css +777 -0
- painapple_code/static/css/11-connection-bar.css +120 -0
- painapple_code/static/css/20-messages.css +750 -0
- painapple_code/static/css/21-tool-blocks.css +2340 -0
- painapple_code/static/css/22-edit-diff.css +619 -0
- painapple_code/static/css/23-thinking.css +1584 -0
- painapple_code/static/css/24-background-tasks.css +359 -0
- painapple_code/static/css/25-context-block.css +252 -0
- painapple_code/static/css/26-session-setup.css +144 -0
- painapple_code/static/css/30-markdown.css +854 -0
- painapple_code/static/css/40-input.css +2418 -0
- painapple_code/static/css/41-autocomplete.css +127 -0
- painapple_code/static/css/42-activity-strip.css +97 -0
- painapple_code/static/css/43-file-autocomplete.css +121 -0
- painapple_code/static/css/44-snippets-autocomplete.css +224 -0
- painapple_code/static/css/45-selection-bar.css +433 -0
- painapple_code/static/css/46-keyboard-bar.css +119 -0
- painapple_code/static/css/47-keyboard-longpress.css +116 -0
- painapple_code/static/css/47-token-profile.css +106 -0
- painapple_code/static/css/50-welcome.css +3095 -0
- painapple_code/static/css/51-modals.css +301 -0
- painapple_code/static/css/52-utilities.css +77 -0
- painapple_code/static/css/53-image-upload.css +456 -0
- painapple_code/static/css/54-todo-list.css +102 -0
- painapple_code/static/css/55-file-upload.css +113 -0
- painapple_code/static/css/56-chat-navigator.css +218 -0
- painapple_code/static/css/56-permission-card.css +332 -0
- painapple_code/static/css/56-plan-approval.css +204 -0
- painapple_code/static/css/56-question-form.css +602 -0
- painapple_code/static/css/57-swipe-indicator.css +95 -0
- painapple_code/static/css/58-session-families.css +627 -0
- painapple_code/static/css/59-about.css +196 -0
- painapple_code/static/css/61-log-explorer.css +723 -0
- painapple_code/static/css/62-json-tree.css +489 -0
- painapple_code/static/css/62-skills-widget.css +650 -0
- painapple_code/static/css/63-commands-widget.css +30 -0
- painapple_code/static/css/63-plugins-widget.css +174 -0
- painapple_code/static/css/63-terminal.css +516 -0
- painapple_code/static/css/64-search-files-widget.css +448 -0
- painapple_code/static/css/65-snippets-widget.css +426 -0
- painapple_code/static/css/66-active-sessions.css +979 -0
- painapple_code/static/css/66-agents-widget.css +265 -0
- painapple_code/static/css/66-file-explorer-widget.css +855 -0
- painapple_code/static/css/66-git-panel.css +791 -0
- painapple_code/static/css/66-history-explorer.css +1496 -0
- painapple_code/static/css/67-diff-viewer.css +602 -0
- painapple_code/static/css/67-editor-view.css +748 -0
- painapple_code/static/css/67-file-preview-widget.css +1873 -0
- painapple_code/static/css/68-browser-widget.css +337 -0
- painapple_code/static/css/68-compare-wizard.css +355 -0
- painapple_code/static/css/68-csv-preview.css +106 -0
- painapple_code/static/css/68-diff-preview.css +103 -0
- painapple_code/static/css/68-helpers-install-widget.css +491 -0
- painapple_code/static/css/68-image-annotate.css +346 -0
- painapple_code/static/css/68-terminal-view.css +98 -0
- painapple_code/static/css/68-uploads-widget.css +229 -0
- painapple_code/static/css/69-cost-analytics.css +537 -0
- painapple_code/static/css/70-config-panel.css +3736 -0
- painapple_code/static/css/70-excalidraw.css +136 -0
- painapple_code/static/css/70-prompt-explorer.css +890 -0
- painapple_code/static/css/71-chart.css +111 -0
- painapple_code/static/css/72-discussion-sidebar.css +678 -0
- painapple_code/static/css/74-debug-logs.css +656 -0
- painapple_code/static/css/80-open-dialog.css +146 -0
- painapple_code/static/css/80-quick-switcher.css +383 -0
- painapple_code/static/css/80-widget-system.css +1763 -0
- painapple_code/static/css/81-quick-actions.css +1020 -0
- painapple_code/static/css/82-context-popover.css +1233 -0
- painapple_code/static/css/83-context-menu.css +339 -0
- painapple_code/static/css/85-lazy-loading.css +102 -0
- painapple_code/static/css/86-zen-mode.css +708 -0
- painapple_code/static/css/87-grid-switcher.css +346 -0
- painapple_code/static/css/login.css +198 -0
- painapple_code/static/dist/app.bundle.js +7177 -0
- painapple_code/static/feature-triage.html +446 -0
- painapple_code/static/icons/apple-touch-icon.png +0 -0
- painapple_code/static/icons/favicon-32.png +0 -0
- painapple_code/static/icons/favicon.ico +0 -0
- painapple_code/static/icons/icon-128.png +0 -0
- painapple_code/static/icons/icon-144.png +0 -0
- painapple_code/static/icons/icon-152.png +0 -0
- painapple_code/static/icons/icon-180.png +0 -0
- painapple_code/static/icons/icon-192.png +0 -0
- painapple_code/static/icons/icon-384.png +0 -0
- painapple_code/static/icons/icon-512.png +0 -0
- painapple_code/static/icons/icon-72.png +0 -0
- painapple_code/static/icons/icon-96.png +0 -0
- painapple_code/static/js/action-delegate.js +187 -0
- painapple_code/static/js/activity-strip.js +193 -0
- painapple_code/static/js/app/context-menus.js +619 -0
- painapple_code/static/js/app/file-preview.js +163 -0
- painapple_code/static/js/app/init-ui.js +574 -0
- painapple_code/static/js/app/input-events.js +561 -0
- painapple_code/static/js/app/input-resize.js +313 -0
- painapple_code/static/js/app/message-actions.js +419 -0
- painapple_code/static/js/app/panels.js +210 -0
- painapple_code/static/js/app/rendering-delegators.js +246 -0
- painapple_code/static/js/app/server-loading.js +365 -0
- painapple_code/static/js/app/session-ops.js +287 -0
- painapple_code/static/js/app/session-switch.js +443 -0
- painapple_code/static/js/app/settings-helpers.js +280 -0
- painapple_code/static/js/app/startup-welcome.js +290 -0
- painapple_code/static/js/app-context.js +157 -0
- painapple_code/static/js/app.js +2134 -0
- painapple_code/static/js/auth-fetch.js +46 -0
- painapple_code/static/js/background-tasks.js +274 -0
- painapple_code/static/js/boot.js +73 -0
- painapple_code/static/js/caret-position.js +171 -0
- painapple_code/static/js/chat-navigator.js +710 -0
- painapple_code/static/js/chat-search.js +323 -0
- painapple_code/static/js/command-executor.js +176 -0
- painapple_code/static/js/command-store.js +497 -0
- painapple_code/static/js/components.js +1414 -0
- painapple_code/static/js/config.js +218 -0
- painapple_code/static/js/context-menu.js +740 -0
- painapple_code/static/js/controllers/chat-controller.js +4252 -0
- painapple_code/static/js/controllers/dialog-controller.js +217 -0
- painapple_code/static/js/controllers/tab-controller.js +1828 -0
- painapple_code/static/js/controllers/thinking-controller.js +915 -0
- painapple_code/static/js/diff-utils.js +486 -0
- painapple_code/static/js/drafts-pill.js +95 -0
- painapple_code/static/js/editor-view.js +815 -0
- painapple_code/static/js/effort-settings.js +430 -0
- painapple_code/static/js/feature-triage.js +811 -0
- painapple_code/static/js/file-autocomplete.js +516 -0
- painapple_code/static/js/file-tabs.js +123 -0
- painapple_code/static/js/gestures.js +448 -0
- painapple_code/static/js/grid-switcher.js +334 -0
- painapple_code/static/js/input-handler.js +1472 -0
- painapple_code/static/js/keyboard-bar.js +495 -0
- painapple_code/static/js/layout-switcher.js +109 -0
- painapple_code/static/js/linkify-utils.js +200 -0
- painapple_code/static/js/login.js +141 -0
- painapple_code/static/js/open-dialog.js +879 -0
- painapple_code/static/js/orphan-terminals.js +373 -0
- painapple_code/static/js/path-utils.js +179 -0
- painapple_code/static/js/perf-marks.js +102 -0
- painapple_code/static/js/permission-settings.js +475 -0
- painapple_code/static/js/pointer-input.js +59 -0
- painapple_code/static/js/preview/json-tree-menu.js +226 -0
- painapple_code/static/js/preview/json-tree-toolbar.js +291 -0
- painapple_code/static/js/preview/json-tree.js +457 -0
- painapple_code/static/js/preview/preview-breadcrumb.js +387 -0
- painapple_code/static/js/preview/preview-edit.js +367 -0
- painapple_code/static/js/preview/preview-events.js +361 -0
- painapple_code/static/js/preview/preview-history.js +567 -0
- painapple_code/static/js/preview/preview-inline-edit.js +869 -0
- painapple_code/static/js/preview/preview-poll.js +108 -0
- painapple_code/static/js/preview/preview-render.js +572 -0
- painapple_code/static/js/preview/preview-search.js +421 -0
- painapple_code/static/js/preview/preview-state.js +256 -0
- painapple_code/static/js/preview/preview-utils.js +251 -0
- painapple_code/static/js/preview-plugins/chart-plugin.js +22 -0
- painapple_code/static/js/preview-plugins/csv-plugin.js +188 -0
- painapple_code/static/js/preview-plugins/diff-plugin.js +258 -0
- painapple_code/static/js/preview-plugins/excalidraw-plugin.js +25 -0
- painapple_code/static/js/preview-plugins/html-plugin.js +104 -0
- painapple_code/static/js/preview-plugins/image-plugin.js +20 -0
- painapple_code/static/js/preview-plugins/index.js +34 -0
- painapple_code/static/js/preview-plugins/json-plugin.js +99 -0
- painapple_code/static/js/preview-plugins/jsonl-plugin.js +152 -0
- painapple_code/static/js/preview-plugins/markdown-plugin.js +266 -0
- painapple_code/static/js/preview-plugins/panzoom-plugin.js +63 -0
- painapple_code/static/js/preview-plugins/plugin-helpers.js +282 -0
- painapple_code/static/js/project-color-picker.js +110 -0
- painapple_code/static/js/project-colors.js +163 -0
- painapple_code/static/js/prompt-favorites.js +187 -0
- painapple_code/static/js/quick-actions-menu.js +1736 -0
- painapple_code/static/js/quick-actions-registry.js +1239 -0
- painapple_code/static/js/quick-switcher/controller.js +294 -0
- painapple_code/static/js/quick-switcher/fuzzy-scorer.js +123 -0
- painapple_code/static/js/quick-switcher/index.js +30 -0
- painapple_code/static/js/quick-switcher/providers/base-provider.js +51 -0
- painapple_code/static/js/quick-switcher/providers/command-provider.js +56 -0
- painapple_code/static/js/quick-switcher/providers/file-provider.js +291 -0
- painapple_code/static/js/quick-switcher/providers/panel-provider.js +132 -0
- painapple_code/static/js/quick-switcher/providers/project-provider.js +361 -0
- painapple_code/static/js/quick-switcher/providers/read-files-provider.js +104 -0
- painapple_code/static/js/quick-switcher/providers/skills-provider.js +129 -0
- painapple_code/static/js/quick-switcher/registry.js +69 -0
- painapple_code/static/js/quick-switcher/ui/item.js +54 -0
- painapple_code/static/js/quick-switcher/ui/picker.js +464 -0
- painapple_code/static/js/recent-opens.js +40 -0
- painapple_code/static/js/scroll-manager.js +530 -0
- painapple_code/static/js/scroll-state-machine.js +359 -0
- painapple_code/static/js/selection/action-bar.js +793 -0
- painapple_code/static/js/selection/index.js +15 -0
- painapple_code/static/js/selection/selection-handler.js +877 -0
- painapple_code/static/js/selection/state.js +181 -0
- painapple_code/static/js/session/agent-progress.js +202 -0
- painapple_code/static/js/session/handle-agent-message.js +481 -0
- painapple_code/static/js/session/handle-message.js +550 -0
- painapple_code/static/js/session/interactive.js +587 -0
- painapple_code/static/js/session/message-store.js +271 -0
- painapple_code/static/js/session/messages.js +253 -0
- painapple_code/static/js/session/persistence.js +179 -0
- painapple_code/static/js/session/restore.js +136 -0
- painapple_code/static/js/session/sync.js +333 -0
- painapple_code/static/js/session-container-pool.js +371 -0
- painapple_code/static/js/session-setup-panel.js +274 -0
- painapple_code/static/js/session.js +1047 -0
- painapple_code/static/js/shortcut-hints.js +272 -0
- painapple_code/static/js/shortcuts.js +1146 -0
- painapple_code/static/js/skills-autocomplete.js +262 -0
- painapple_code/static/js/snippets-autocomplete.js +738 -0
- painapple_code/static/js/stash-refs-view.js +83 -0
- painapple_code/static/js/stash-ui.js +768 -0
- painapple_code/static/js/stash.js +785 -0
- painapple_code/static/js/status-bar.js +1184 -0
- painapple_code/static/js/token-profile.js +274 -0
- painapple_code/static/js/tool-renderer-blocks.js +1626 -0
- painapple_code/static/js/tool-renderer-thinking.js +813 -0
- painapple_code/static/js/tool-renderer.js +1500 -0
- painapple_code/static/js/tooltips.js +197 -0
- painapple_code/static/js/upload-manager.js +1019 -0
- painapple_code/static/js/utils.js +521 -0
- painapple_code/static/js/welcome/api.js +363 -0
- painapple_code/static/js/welcome/cards.js +626 -0
- painapple_code/static/js/welcome/context-menu.js +585 -0
- painapple_code/static/js/welcome/families.js +801 -0
- painapple_code/static/js/welcome/preview.js +234 -0
- painapple_code/static/js/welcome/state.js +129 -0
- painapple_code/static/js/welcome.js +1874 -0
- painapple_code/static/js/widget-system/base-widget.js +717 -0
- painapple_code/static/js/widget-system/device-manager.js +178 -0
- painapple_code/static/js/widget-system/event-bus.js +152 -0
- painapple_code/static/js/widget-system/icons.js +120 -0
- painapple_code/static/js/widget-system/index.js +49 -0
- painapple_code/static/js/widget-system/init.js +54 -0
- painapple_code/static/js/widget-system/types/bottom-sheet.js +307 -0
- painapple_code/static/js/widget-system/types/floating.js +915 -0
- painapple_code/static/js/widget-system/types/index.js +40 -0
- painapple_code/static/js/widget-system/types/modal.js +147 -0
- painapple_code/static/js/widget-system/types/sidebar.js +158 -0
- painapple_code/static/js/widget-system/types/tab.js +146 -0
- painapple_code/static/js/widget-system/types/top-sheet.js +203 -0
- painapple_code/static/js/widget-system/widget-manager.js +1187 -0
- painapple_code/static/js/widgets/about-widget.js +260 -0
- painapple_code/static/js/widgets/active-sessions-widget.js +760 -0
- painapple_code/static/js/widgets/agents-widget.js +897 -0
- painapple_code/static/js/widgets/browser-widget.js +579 -0
- painapple_code/static/js/widgets/commands-widget.js +694 -0
- painapple_code/static/js/widgets/config/commit-sections.js +449 -0
- painapple_code/static/js/widgets/config/dir-autocomplete.js +224 -0
- painapple_code/static/js/widgets/config/gestures.js +161 -0
- painapple_code/static/js/widgets/config/models-tab.js +988 -0
- painapple_code/static/js/widgets/config/quick-actions-tab.js +882 -0
- painapple_code/static/js/widgets/config/shortcut-editor.js +587 -0
- painapple_code/static/js/widgets/config/state.js +517 -0
- painapple_code/static/js/widgets/config/system-controls.js +90 -0
- painapple_code/static/js/widgets/config-widget.js +1535 -0
- painapple_code/static/js/widgets/cost-analytics-widget.js +621 -0
- painapple_code/static/js/widgets/debug-widget.js +693 -0
- painapple_code/static/js/widgets/diff-viewer-widget.js +1420 -0
- painapple_code/static/js/widgets/discussion-widget.js +1107 -0
- painapple_code/static/js/widgets/file-explorer-widget.js +2166 -0
- painapple_code/static/js/widgets/file-preview-widget.js +861 -0
- painapple_code/static/js/widgets/git-widget.js +794 -0
- painapple_code/static/js/widgets/helpers-install-widget.js +720 -0
- painapple_code/static/js/widgets/history-explorer-widget.js +1804 -0
- painapple_code/static/js/widgets/image-annotate-widget.js +951 -0
- painapple_code/static/js/widgets/image-preview-widget.js +692 -0
- painapple_code/static/js/widgets/index.js +176 -0
- painapple_code/static/js/widgets/log-explorer-widget.js +1057 -0
- painapple_code/static/js/widgets/plugins-widget.js +305 -0
- painapple_code/static/js/widgets/prompt-explorer-widget.js +1119 -0
- painapple_code/static/js/widgets/search-files-widget.js +529 -0
- painapple_code/static/js/widgets/skills-widget.js +843 -0
- painapple_code/static/js/widgets/snippets-widget.js +479 -0
- painapple_code/static/js/widgets/sub-agents-widget.js +333 -0
- painapple_code/static/js/widgets/tasks-widget.js +379 -0
- painapple_code/static/js/widgets/terminal/connection.js +283 -0
- painapple_code/static/js/widgets/terminal/gestures.js +357 -0
- painapple_code/static/js/widgets/terminal/init.js +642 -0
- painapple_code/static/js/widgets/terminal/link-providers.js +532 -0
- painapple_code/static/js/widgets/terminal/osc52.js +213 -0
- painapple_code/static/js/widgets/terminal/render.js +159 -0
- painapple_code/static/js/widgets/terminal/size.js +54 -0
- painapple_code/static/js/widgets/terminal/state.js +211 -0
- painapple_code/static/js/widgets/terminal/wrap-utils.js +101 -0
- painapple_code/static/js/widgets/terminal-widget.js +793 -0
- painapple_code/static/js/widgets/uploads-widget.js +461 -0
- painapple_code/static/js/widgets/zen-widget.js +1001 -0
- painapple_code/static/login.html +51 -0
- painapple_code/static/manifest.json +73 -0
- painapple_code/static/sw.js +293 -0
- painapple_code/static/vendor/LICENSES.txt +769 -0
- painapple_code/static/vendor/README.md +103 -0
- painapple_code/static/vendor/codemirror.js +35 -0
- painapple_code/static/vendor/codemirror.js.LEGAL.txt +304 -0
- painapple_code/static/vendor/github-markdown-dark.min.css +1124 -0
- painapple_code/static/vendor/highlight-github-dark.min.css +10 -0
- painapple_code/static/vendor/highlight-lang-dockerfile.min.js +8 -0
- painapple_code/static/vendor/highlight-lang-nginx.min.js +21 -0
- painapple_code/static/vendor/highlight-lang-properties.min.js +10 -0
- painapple_code/static/vendor/highlight-lang-scala.min.js +28 -0
- painapple_code/static/vendor/highlight.min.js +1213 -0
- painapple_code/static/vendor/marked.min.js +6 -0
- painapple_code/static/vendor/purify.min.js +3 -0
- painapple_code/static/vendor/xterm-addon-fit.js +2 -0
- painapple_code/static/vendor/xterm.css +209 -0
- painapple_code/static/vendor/xterm.js +2 -0
- painapple_code/static/web-client.html +635 -0
- painapple_code/subprocess_registry.py +535 -0
- painapple_code/tls_cert.py +79 -0
- painapple_code/tools/agents/shadow-git-helper.md +400 -0
- painapple_code/tools/excalidraw-to-svg.js +143 -0
- painapple_code/tools/install-helpers.sh +148 -0
- painapple_code/tools/shadow-git +320 -0
- painapple_code/tools/shadow-query +120 -0
- painapple_code/tools/vegalite-to-svg.js +79 -0
- painapple_code/turn_query.py +766 -0
- painapple_code/turn_tracker.py +221 -0
- painapple_code/utils/__init__.py +0 -0
- painapple_code/utils/agent_cli.py +306 -0
- painapple_code/utils/chart.py +103 -0
- painapple_code/utils/excalidraw.py +107 -0
- painapple_code/utils/file_lock.py +162 -0
- painapple_code/utils/file_paths.py +648 -0
- painapple_code/utils/generate_icons.py +251 -0
- painapple_code/utils/proc.py +205 -0
- painapple_code/utils/pty_backend.py +59 -0
- painapple_code/utils/pty_posix.py +160 -0
- painapple_code/utils/pty_windows.py +245 -0
- painapple_code/utils/token_profiles.py +91 -0
- painapple_code/viewer_templates.py +657 -0
- painapple_code/welcome_search.py +879 -0
- painapple_code-1.0.0.dist-info/METADATA +292 -0
- painapple_code-1.0.0.dist-info/RECORD +436 -0
- painapple_code-1.0.0.dist-info/WHEEL +5 -0
- painapple_code-1.0.0.dist-info/entry_points.txt +2 -0
- painapple_code-1.0.0.dist-info/licenses/LICENSE +661 -0
- painapple_code-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +261 -0
- painapple_code-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session Stash & Favorites API Routes
|
|
3
|
+
|
|
4
|
+
Endpoints for managing:
|
|
5
|
+
- Session stash (context references for next message)
|
|
6
|
+
- Favorites (bookmarked sessions)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
|
|
16
|
+
from painapple_code.session_store import SessionStore
|
|
17
|
+
from painapple_code.routes.dependencies import get_session_store
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
router = APIRouter(tags=["sessions"])
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
25
|
+
# Stash API
|
|
26
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
27
|
+
|
|
28
|
+
@router.get("/api/session/{session_id}/stash")
|
|
29
|
+
async def get_session_stash(session_id: str, store: SessionStore = Depends(get_session_store)):
|
|
30
|
+
"""Get all stash items for a session."""
|
|
31
|
+
items = store.get_stash(session_id)
|
|
32
|
+
return {"items": items, "count": len(items)}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@router.post("/api/session/{session_id}/stash")
|
|
36
|
+
async def add_stash_item(session_id: str, request: Request, store: SessionStore = Depends(get_session_store)):
|
|
37
|
+
"""Add an item to the session stash."""
|
|
38
|
+
item = await request.json()
|
|
39
|
+
success = store.add_stash_item(session_id, item)
|
|
40
|
+
if not success:
|
|
41
|
+
raise HTTPException(status_code=500, detail="Failed to save stash item")
|
|
42
|
+
return {"success": True, "item": item}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@router.delete("/api/session/{session_id}/stash")
|
|
46
|
+
async def clear_session_stash(session_id: str, scope: str = "all",
|
|
47
|
+
store: SessionStore = Depends(get_session_store)):
|
|
48
|
+
"""Clear stash items by scope: 'pending' (default UI clear keeps sent
|
|
49
|
+
history), 'history' (keeps pending), or 'all' (legacy behavior)."""
|
|
50
|
+
if scope not in ("pending", "history", "all"):
|
|
51
|
+
raise HTTPException(status_code=400, detail=f"Unknown scope: {scope}")
|
|
52
|
+
success = store.clear_stash_scope(session_id, scope)
|
|
53
|
+
if not success:
|
|
54
|
+
raise HTTPException(status_code=500, detail="Failed to clear stash")
|
|
55
|
+
return {"success": True, "scope": scope}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class MarkSentRequest(BaseModel):
|
|
59
|
+
item_ids: list[str]
|
|
60
|
+
message_id: Optional[str] = None
|
|
61
|
+
sent_at: Optional[str] = None
|
|
62
|
+
sent_session_id: Optional[str] = None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@router.post("/api/session/{session_id}/stash/mark-sent")
|
|
66
|
+
async def mark_stash_sent(session_id: str, req: MarkSentRequest,
|
|
67
|
+
store: SessionStore = Depends(get_session_store)):
|
|
68
|
+
"""Mark stash items as sent — they move to history instead of being
|
|
69
|
+
deleted. Trims sent history beyond the per-session cap."""
|
|
70
|
+
marked, saved = store.mark_stash_sent(
|
|
71
|
+
session_id,
|
|
72
|
+
req.item_ids,
|
|
73
|
+
message_id=req.message_id,
|
|
74
|
+
sent_at=req.sent_at,
|
|
75
|
+
sent_session_id=req.sent_session_id,
|
|
76
|
+
)
|
|
77
|
+
if marked and not saved:
|
|
78
|
+
raise HTTPException(status_code=500, detail="Failed to save stash")
|
|
79
|
+
return {"success": True, "marked": marked}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@router.delete("/api/session/{session_id}/stash/{item_id}")
|
|
83
|
+
async def remove_stash_item(session_id: str, item_id: str, store: SessionStore = Depends(get_session_store)):
|
|
84
|
+
"""Remove a specific item from the session stash."""
|
|
85
|
+
success = store.remove_stash_item(session_id, item_id)
|
|
86
|
+
if not success:
|
|
87
|
+
raise HTTPException(status_code=500, detail="Failed to remove stash item")
|
|
88
|
+
return {"success": True}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@router.patch("/api/session/{session_id}/stash/{item_id}")
|
|
92
|
+
async def update_stash_item(session_id: str, item_id: str, request: Request, store: SessionStore = Depends(get_session_store)):
|
|
93
|
+
"""Update a stash item (toggle enabled, edit note)."""
|
|
94
|
+
updates = await request.json()
|
|
95
|
+
found, saved = store.update_stash_item(session_id, item_id, updates)
|
|
96
|
+
if not found:
|
|
97
|
+
raise HTTPException(status_code=404, detail="Item not found")
|
|
98
|
+
if not saved:
|
|
99
|
+
raise HTTPException(status_code=500, detail="Failed to save stash update")
|
|
100
|
+
|
|
101
|
+
return {"success": True}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
105
|
+
# Favorites API
|
|
106
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
107
|
+
|
|
108
|
+
class FavoriteRequest(BaseModel):
|
|
109
|
+
note: Optional[str] = None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@router.get("/api/favorites")
|
|
113
|
+
async def get_favorites():
|
|
114
|
+
"""Get all favorited sessions with full session metadata."""
|
|
115
|
+
from painapple_code.paths import load_favorites
|
|
116
|
+
from painapple_code.welcome_search import get_welcome_searcher
|
|
117
|
+
|
|
118
|
+
fav_data = load_favorites()
|
|
119
|
+
favorites = fav_data.get("favorites", [])
|
|
120
|
+
|
|
121
|
+
if not favorites:
|
|
122
|
+
return {"favorites": []}
|
|
123
|
+
|
|
124
|
+
searcher = get_welcome_searcher()
|
|
125
|
+
all_sessions_result = await searcher.search("", limit=500)
|
|
126
|
+
all_sessions = {s["session_id"]: s for s in all_sessions_result.get("results", [])}
|
|
127
|
+
|
|
128
|
+
enriched = []
|
|
129
|
+
for fav in favorites:
|
|
130
|
+
session_id = fav["session_id"]
|
|
131
|
+
session = all_sessions.get(session_id)
|
|
132
|
+
|
|
133
|
+
if session:
|
|
134
|
+
enriched.append({
|
|
135
|
+
"session_id": session_id,
|
|
136
|
+
"note": fav.get("note"),
|
|
137
|
+
"added_at": fav.get("added_at"),
|
|
138
|
+
"updated_at": fav.get("updated_at"),
|
|
139
|
+
"project_hash": fav.get("project_hash") or session.get("project_hash"),
|
|
140
|
+
"session": {
|
|
141
|
+
"name": session.get("name") or session.get("project") or "Session",
|
|
142
|
+
"project": session.get("project"),
|
|
143
|
+
"project_path": session.get("project_path"),
|
|
144
|
+
"last_activity": session.get("last_activity"),
|
|
145
|
+
"created_at": session.get("created_at"),
|
|
146
|
+
"summary": session.get("summary"),
|
|
147
|
+
"tags": session.get("tags", []),
|
|
148
|
+
"total_cost": session.get("total_cost", 0),
|
|
149
|
+
"message_count": session.get("message_count", 0),
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
else:
|
|
153
|
+
session_data = SessionStore.load(session_id)
|
|
154
|
+
if session_data:
|
|
155
|
+
cwd = session_data.get("cwd", "")
|
|
156
|
+
enriched.append({
|
|
157
|
+
"session_id": session_id,
|
|
158
|
+
"note": fav.get("note"),
|
|
159
|
+
"added_at": fav.get("added_at"),
|
|
160
|
+
"updated_at": fav.get("updated_at"),
|
|
161
|
+
"project_hash": fav.get("project_hash") or session_data.get("project_hash"),
|
|
162
|
+
"session": {
|
|
163
|
+
"name": session_data.get("name") or (Path(cwd).name if cwd else "") or "Session",
|
|
164
|
+
"project": Path(cwd).name if cwd else None,
|
|
165
|
+
"project_path": cwd,
|
|
166
|
+
"last_activity": session_data.get("last_activity"),
|
|
167
|
+
"created_at": session_data.get("created_at"),
|
|
168
|
+
"summary": None,
|
|
169
|
+
"tags": [],
|
|
170
|
+
"total_cost": session_data.get("total_cost", 0),
|
|
171
|
+
"message_count": session_data.get("message_count", 0),
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
else:
|
|
175
|
+
enriched.append({
|
|
176
|
+
"session_id": session_id,
|
|
177
|
+
"note": fav.get("note"),
|
|
178
|
+
"added_at": fav.get("added_at"),
|
|
179
|
+
"updated_at": fav.get("updated_at"),
|
|
180
|
+
"project_hash": fav.get("project_hash"),
|
|
181
|
+
"session": None,
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
enriched.sort(key=lambda x: (x.get("session") or {}).get("last_activity") or x.get("added_at") or "", reverse=True)
|
|
185
|
+
|
|
186
|
+
return {"favorites": enriched}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@router.post("/api/favorites/{session_id}")
|
|
190
|
+
async def add_favorite(session_id: str, request: FavoriteRequest = None):
|
|
191
|
+
"""Add a session to favorites."""
|
|
192
|
+
from painapple_code.paths import add_favorite as _add_favorite
|
|
193
|
+
|
|
194
|
+
session_data = SessionStore.load(session_id)
|
|
195
|
+
if not session_data:
|
|
196
|
+
raise HTTPException(status_code=404, detail="Session not found")
|
|
197
|
+
|
|
198
|
+
cwd = session_data.get("cwd")
|
|
199
|
+
project_hash = session_data.get("project_hash")
|
|
200
|
+
if not project_hash and cwd:
|
|
201
|
+
from painapple_code.paths import get_project_hash
|
|
202
|
+
project_hash = get_project_hash(cwd)
|
|
203
|
+
|
|
204
|
+
note = request.note if request else None
|
|
205
|
+
added = _add_favorite(session_id, project_hash, note)
|
|
206
|
+
|
|
207
|
+
if added:
|
|
208
|
+
return {"success": True, "session_id": session_id, "note": note}
|
|
209
|
+
else:
|
|
210
|
+
return {"success": False, "message": "Already favorited", "session_id": session_id}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@router.delete("/api/favorites/{session_id}")
|
|
214
|
+
async def remove_favorite_endpoint(session_id: str):
|
|
215
|
+
"""Remove a session from favorites."""
|
|
216
|
+
from painapple_code.paths import remove_favorite
|
|
217
|
+
|
|
218
|
+
removed = remove_favorite(session_id)
|
|
219
|
+
if removed:
|
|
220
|
+
return {"success": True, "session_id": session_id}
|
|
221
|
+
else:
|
|
222
|
+
raise HTTPException(status_code=404, detail="Favorite not found")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@router.patch("/api/favorites/{session_id}")
|
|
226
|
+
async def update_favorite(session_id: str, request: FavoriteRequest):
|
|
227
|
+
"""Update a favorite's note."""
|
|
228
|
+
from painapple_code.paths import update_favorite_note
|
|
229
|
+
|
|
230
|
+
updated = update_favorite_note(session_id, request.note or "")
|
|
231
|
+
if updated:
|
|
232
|
+
return {"success": True, "session_id": session_id, "note": request.note}
|
|
233
|
+
else:
|
|
234
|
+
raise HTTPException(status_code=404, detail="Favorite not found")
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Welcome Screen API Routes
|
|
3
|
+
|
|
4
|
+
Endpoints for:
|
|
5
|
+
- Welcome screen data (recent sessions, search, project list)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from fastapi import APIRouter, Request
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
router = APIRouter(tags=["sessions"])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
20
|
+
# Welcome Screen Endpoints
|
|
21
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
22
|
+
|
|
23
|
+
@router.get("/api/welcome/sessions")
|
|
24
|
+
async def get_welcome_sessions(limit: int = 10):
|
|
25
|
+
"""Get recent sessions with enriched shadow git data for the welcome screen."""
|
|
26
|
+
from painapple_code.welcome_search import get_recent_sessions
|
|
27
|
+
|
|
28
|
+
# Fetch extra to account for filtering out empty sessions
|
|
29
|
+
sessions = await get_recent_sessions(limit=limit + 20)
|
|
30
|
+
|
|
31
|
+
# Filter out empty sessions (opened but no messages sent)
|
|
32
|
+
sessions = [
|
|
33
|
+
s for s in sessions
|
|
34
|
+
if s.get("message_count", 0) > 0 or s.get("turn_count", 0) > 0
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
sessions = sessions[:limit]
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
"sessions": sessions,
|
|
41
|
+
"total": len(sessions),
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@router.post("/api/welcome/search")
|
|
46
|
+
async def search_welcome(request: Request):
|
|
47
|
+
"""Smart session search using shadow git archaeology."""
|
|
48
|
+
from painapple_code.welcome_search import get_welcome_searcher
|
|
49
|
+
|
|
50
|
+
body = await request.json()
|
|
51
|
+
query = body.get("query", "").strip()
|
|
52
|
+
limit = body.get("limit", 10)
|
|
53
|
+
|
|
54
|
+
if not query:
|
|
55
|
+
from painapple_code.welcome_search import get_recent_sessions
|
|
56
|
+
sessions = await get_recent_sessions(limit=limit)
|
|
57
|
+
return {
|
|
58
|
+
"query": "",
|
|
59
|
+
"parsed": {"keywords": [], "tags": [], "files": [], "intent": "browse"},
|
|
60
|
+
"results": sessions,
|
|
61
|
+
"total_found": len(sessions),
|
|
62
|
+
"suggestions": [
|
|
63
|
+
"Try: '#bugfix last week'",
|
|
64
|
+
"Try: 'files:server.py'",
|
|
65
|
+
"Try: 'auth bug fix'",
|
|
66
|
+
],
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
searcher = get_welcome_searcher()
|
|
70
|
+
result = await searcher.search(query=query, limit=limit)
|
|
71
|
+
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@router.get("/api/welcome/projects")
|
|
76
|
+
async def get_welcome_projects(request: Request):
|
|
77
|
+
"""Get projects for the quick-start picker, plus workspace siblings the
|
|
78
|
+
user hasn't opened a session in yet."""
|
|
79
|
+
from painapple_code.paths import list_projects, list_workspace_dirs
|
|
80
|
+
|
|
81
|
+
projects = list_projects()
|
|
82
|
+
|
|
83
|
+
enriched = []
|
|
84
|
+
for p in projects:
|
|
85
|
+
enriched.append({
|
|
86
|
+
"path": p["path"],
|
|
87
|
+
"hash": p["hash"],
|
|
88
|
+
"name": Path(p["path"]).name,
|
|
89
|
+
"session_count": p.get("session_count", 0),
|
|
90
|
+
"color": p.get("color"), # custom override, else None (→ hash color)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
enriched.sort(key=lambda x: x["name"].lower())
|
|
94
|
+
|
|
95
|
+
workspace_root = getattr(request.app.state, "workspace_root", None)
|
|
96
|
+
workspace_dirs: list[dict] = []
|
|
97
|
+
if workspace_root:
|
|
98
|
+
exclude = [p["path"] for p in projects]
|
|
99
|
+
workspace_dirs = list_workspace_dirs(workspace_root, exclude_paths=exclude)
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
"projects": enriched,
|
|
103
|
+
"workspace_root": workspace_root,
|
|
104
|
+
"workspace_dirs": workspace_dirs,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@router.get("/api/welcome/projects/sessions")
|
|
109
|
+
async def get_project_sessions(path: str, limit: int = 15):
|
|
110
|
+
"""Recent sessions for a specific project — drives the quick-switcher drill-in.
|
|
111
|
+
|
|
112
|
+
limit=0 returns ALL non-empty sessions, enriched with shadow-git AI
|
|
113
|
+
summaries — the quick-switcher's search-over-everything path. Positive
|
|
114
|
+
limits stay summary-free so the instant drill-in preview keeps its
|
|
115
|
+
meta.json-only fast path.
|
|
116
|
+
"""
|
|
117
|
+
from painapple_code.session_store import SessionStoreV2
|
|
118
|
+
from painapple_code.welcome_search import get_session_summaries
|
|
119
|
+
|
|
120
|
+
# Skip if the project path isn't mounted in this container. Avoids
|
|
121
|
+
# listing sessions the user can't actually resume.
|
|
122
|
+
if not Path(path).is_dir():
|
|
123
|
+
return {"sessions": []}
|
|
124
|
+
|
|
125
|
+
store = SessionStoreV2(path)
|
|
126
|
+
all_sessions = store.list_all()
|
|
127
|
+
|
|
128
|
+
# Drop empty/never-used sessions — they're noise here.
|
|
129
|
+
active = [s for s in all_sessions if s.get("message_count", 0) > 0]
|
|
130
|
+
summaries = {}
|
|
131
|
+
if limit > 0:
|
|
132
|
+
active = active[:limit]
|
|
133
|
+
else:
|
|
134
|
+
summaries = await get_session_summaries(path)
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
"sessions": [
|
|
138
|
+
{
|
|
139
|
+
"id": s["id"],
|
|
140
|
+
"name": s.get("name", ""),
|
|
141
|
+
"description": s.get("description", ""),
|
|
142
|
+
"summary": summaries.get(s["id"], ""),
|
|
143
|
+
"message_count": s.get("message_count", 0),
|
|
144
|
+
"total_cost": s.get("total_cost", 0),
|
|
145
|
+
"last_activity": s.get("last_activity", ""),
|
|
146
|
+
"model": s.get("model"),
|
|
147
|
+
"provider": s.get("provider"), # None = default engine (claude-sdk)
|
|
148
|
+
}
|
|
149
|
+
for s in active
|
|
150
|
+
],
|
|
151
|
+
}
|