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.
Files changed (436) hide show
  1. painapple_code/__init__.py +30 -0
  2. painapple_code/__main__.py +18 -0
  3. painapple_code/_version.py +24 -0
  4. painapple_code/auth_middleware.py +930 -0
  5. painapple_code/cli/__init__.py +204 -0
  6. painapple_code/cli/deploy/__init__.py +15 -0
  7. painapple_code/cli/deploy/claude_seed.py +41 -0
  8. painapple_code/cli/deploy/config.py +260 -0
  9. painapple_code/cli/deploy/container.py +715 -0
  10. painapple_code/cli/deploy/launch.py +163 -0
  11. painapple_code/cli/deploy/runtime.py +223 -0
  12. painapple_code/cli/lifecycle_cmd.py +443 -0
  13. painapple_code/cli/list_cmd.py +572 -0
  14. painapple_code/cli/manage_cmd.py +670 -0
  15. painapple_code/cli/netinfo.py +125 -0
  16. painapple_code/cli/profiles.py +390 -0
  17. painapple_code/cli/serve_args.py +163 -0
  18. painapple_code/cli/serve_config.py +256 -0
  19. painapple_code/cli/setup_cmd.py +664 -0
  20. painapple_code/cli/ui.py +438 -0
  21. painapple_code/cost_analytics.py +879 -0
  22. painapple_code/data/models.defaults.yaml +18 -0
  23. painapple_code/data/models.yaml +18 -0
  24. painapple_code/data/presets.defaults.json +32 -0
  25. painapple_code/data/strings.yaml +2269 -0
  26. painapple_code/helpers.py +406 -0
  27. painapple_code/paths.py +1514 -0
  28. painapple_code/prompt_explorer.py +984 -0
  29. painapple_code/providers/__init__.py +224 -0
  30. painapple_code/providers/base.py +710 -0
  31. painapple_code/providers/claude/__init__.py +74 -0
  32. painapple_code/providers/claude/capabilities.py +218 -0
  33. painapple_code/providers/claude/errors.py +79 -0
  34. painapple_code/providers/claude/launch.py +50 -0
  35. painapple_code/providers/claude/summary.py +89 -0
  36. painapple_code/providers/claude/translate.py +74 -0
  37. painapple_code/providers/claude_sdk/__init__.py +139 -0
  38. painapple_code/providers/claude_sdk/driver.py +412 -0
  39. painapple_code/providers/codex/__init__.py +87 -0
  40. painapple_code/providers/codex/capabilities.py +321 -0
  41. painapple_code/providers/codex/errors.py +70 -0
  42. painapple_code/providers/codex/launch.py +142 -0
  43. painapple_code/providers/codex/summary.py +196 -0
  44. painapple_code/providers/codex/translate.py +204 -0
  45. painapple_code/providers/codex_app_server/__init__.py +100 -0
  46. painapple_code/providers/codex_app_server/capabilities.py +41 -0
  47. painapple_code/providers/codex_app_server/launch.py +202 -0
  48. painapple_code/providers/codex_app_server/summary.py +291 -0
  49. painapple_code/providers/codex_app_server/translate.py +263 -0
  50. painapple_code/providers/codex_app_server/transport.py +238 -0
  51. painapple_code/routes/__init__.py +0 -0
  52. painapple_code/routes/api_agents.py +441 -0
  53. painapple_code/routes/api_app.py +368 -0
  54. painapple_code/routes/api_app_commit_sections.py +127 -0
  55. painapple_code/routes/api_app_config.py +997 -0
  56. painapple_code/routes/api_app_session_prefs.py +217 -0
  57. painapple_code/routes/api_browser.py +690 -0
  58. painapple_code/routes/api_commands.py +374 -0
  59. painapple_code/routes/api_costs.py +128 -0
  60. painapple_code/routes/api_drafts.py +139 -0
  61. painapple_code/routes/api_exec.py +149 -0
  62. painapple_code/routes/api_files.py +542 -0
  63. painapple_code/routes/api_git.py +657 -0
  64. painapple_code/routes/api_logs.py +649 -0
  65. painapple_code/routes/api_plugins.py +275 -0
  66. painapple_code/routes/api_project_config.py +418 -0
  67. painapple_code/routes/api_prompts.py +231 -0
  68. painapple_code/routes/api_providers.py +53 -0
  69. painapple_code/routes/api_search.py +411 -0
  70. painapple_code/routes/api_session_stash.py +234 -0
  71. painapple_code/routes/api_session_welcome.py +151 -0
  72. painapple_code/routes/api_sessions.py +742 -0
  73. painapple_code/routes/api_shadow.py +323 -0
  74. painapple_code/routes/api_shadow_db.py +512 -0
  75. painapple_code/routes/api_shadow_files.py +332 -0
  76. painapple_code/routes/api_shadow_search.py +476 -0
  77. painapple_code/routes/api_skills.py +610 -0
  78. painapple_code/routes/api_tasks.py +147 -0
  79. painapple_code/routes/api_terminal.py +540 -0
  80. painapple_code/routes/api_upload.py +293 -0
  81. painapple_code/routes/api_viewer.py +229 -0
  82. painapple_code/routes/dependencies.py +109 -0
  83. painapple_code/routes/ws_chat.py +837 -0
  84. painapple_code/server.py +2208 -0
  85. painapple_code/server_logging.py +263 -0
  86. painapple_code/services/__init__.py +0 -0
  87. painapple_code/services/agent_session.py +2738 -0
  88. painapple_code/session_store.py +253 -0
  89. painapple_code/session_store_core.py +879 -0
  90. painapple_code/shadow_db.py +830 -0
  91. painapple_code/shadow_db_plans.py +369 -0
  92. painapple_code/shadow_db_queries.py +595 -0
  93. painapple_code/shadow_db_schema.py +676 -0
  94. painapple_code/shadow_git.py +1296 -0
  95. painapple_code/shadow_git_frontmatter.py +314 -0
  96. painapple_code/shadow_git_sections.py +520 -0
  97. painapple_code/shadow_git_summary.py +462 -0
  98. painapple_code/shadow_parser.py +560 -0
  99. painapple_code/static/css/00-variables.css +276 -0
  100. painapple_code/static/css/01-base.css +245 -0
  101. painapple_code/static/css/02-tooltips.css +36 -0
  102. painapple_code/static/css/09-left-rail.css +448 -0
  103. painapple_code/static/css/10-header.css +777 -0
  104. painapple_code/static/css/11-connection-bar.css +120 -0
  105. painapple_code/static/css/20-messages.css +750 -0
  106. painapple_code/static/css/21-tool-blocks.css +2340 -0
  107. painapple_code/static/css/22-edit-diff.css +619 -0
  108. painapple_code/static/css/23-thinking.css +1584 -0
  109. painapple_code/static/css/24-background-tasks.css +359 -0
  110. painapple_code/static/css/25-context-block.css +252 -0
  111. painapple_code/static/css/26-session-setup.css +144 -0
  112. painapple_code/static/css/30-markdown.css +854 -0
  113. painapple_code/static/css/40-input.css +2418 -0
  114. painapple_code/static/css/41-autocomplete.css +127 -0
  115. painapple_code/static/css/42-activity-strip.css +97 -0
  116. painapple_code/static/css/43-file-autocomplete.css +121 -0
  117. painapple_code/static/css/44-snippets-autocomplete.css +224 -0
  118. painapple_code/static/css/45-selection-bar.css +433 -0
  119. painapple_code/static/css/46-keyboard-bar.css +119 -0
  120. painapple_code/static/css/47-keyboard-longpress.css +116 -0
  121. painapple_code/static/css/47-token-profile.css +106 -0
  122. painapple_code/static/css/50-welcome.css +3095 -0
  123. painapple_code/static/css/51-modals.css +301 -0
  124. painapple_code/static/css/52-utilities.css +77 -0
  125. painapple_code/static/css/53-image-upload.css +456 -0
  126. painapple_code/static/css/54-todo-list.css +102 -0
  127. painapple_code/static/css/55-file-upload.css +113 -0
  128. painapple_code/static/css/56-chat-navigator.css +218 -0
  129. painapple_code/static/css/56-permission-card.css +332 -0
  130. painapple_code/static/css/56-plan-approval.css +204 -0
  131. painapple_code/static/css/56-question-form.css +602 -0
  132. painapple_code/static/css/57-swipe-indicator.css +95 -0
  133. painapple_code/static/css/58-session-families.css +627 -0
  134. painapple_code/static/css/59-about.css +196 -0
  135. painapple_code/static/css/61-log-explorer.css +723 -0
  136. painapple_code/static/css/62-json-tree.css +489 -0
  137. painapple_code/static/css/62-skills-widget.css +650 -0
  138. painapple_code/static/css/63-commands-widget.css +30 -0
  139. painapple_code/static/css/63-plugins-widget.css +174 -0
  140. painapple_code/static/css/63-terminal.css +516 -0
  141. painapple_code/static/css/64-search-files-widget.css +448 -0
  142. painapple_code/static/css/65-snippets-widget.css +426 -0
  143. painapple_code/static/css/66-active-sessions.css +979 -0
  144. painapple_code/static/css/66-agents-widget.css +265 -0
  145. painapple_code/static/css/66-file-explorer-widget.css +855 -0
  146. painapple_code/static/css/66-git-panel.css +791 -0
  147. painapple_code/static/css/66-history-explorer.css +1496 -0
  148. painapple_code/static/css/67-diff-viewer.css +602 -0
  149. painapple_code/static/css/67-editor-view.css +748 -0
  150. painapple_code/static/css/67-file-preview-widget.css +1873 -0
  151. painapple_code/static/css/68-browser-widget.css +337 -0
  152. painapple_code/static/css/68-compare-wizard.css +355 -0
  153. painapple_code/static/css/68-csv-preview.css +106 -0
  154. painapple_code/static/css/68-diff-preview.css +103 -0
  155. painapple_code/static/css/68-helpers-install-widget.css +491 -0
  156. painapple_code/static/css/68-image-annotate.css +346 -0
  157. painapple_code/static/css/68-terminal-view.css +98 -0
  158. painapple_code/static/css/68-uploads-widget.css +229 -0
  159. painapple_code/static/css/69-cost-analytics.css +537 -0
  160. painapple_code/static/css/70-config-panel.css +3736 -0
  161. painapple_code/static/css/70-excalidraw.css +136 -0
  162. painapple_code/static/css/70-prompt-explorer.css +890 -0
  163. painapple_code/static/css/71-chart.css +111 -0
  164. painapple_code/static/css/72-discussion-sidebar.css +678 -0
  165. painapple_code/static/css/74-debug-logs.css +656 -0
  166. painapple_code/static/css/80-open-dialog.css +146 -0
  167. painapple_code/static/css/80-quick-switcher.css +383 -0
  168. painapple_code/static/css/80-widget-system.css +1763 -0
  169. painapple_code/static/css/81-quick-actions.css +1020 -0
  170. painapple_code/static/css/82-context-popover.css +1233 -0
  171. painapple_code/static/css/83-context-menu.css +339 -0
  172. painapple_code/static/css/85-lazy-loading.css +102 -0
  173. painapple_code/static/css/86-zen-mode.css +708 -0
  174. painapple_code/static/css/87-grid-switcher.css +346 -0
  175. painapple_code/static/css/login.css +198 -0
  176. painapple_code/static/dist/app.bundle.js +7177 -0
  177. painapple_code/static/feature-triage.html +446 -0
  178. painapple_code/static/icons/apple-touch-icon.png +0 -0
  179. painapple_code/static/icons/favicon-32.png +0 -0
  180. painapple_code/static/icons/favicon.ico +0 -0
  181. painapple_code/static/icons/icon-128.png +0 -0
  182. painapple_code/static/icons/icon-144.png +0 -0
  183. painapple_code/static/icons/icon-152.png +0 -0
  184. painapple_code/static/icons/icon-180.png +0 -0
  185. painapple_code/static/icons/icon-192.png +0 -0
  186. painapple_code/static/icons/icon-384.png +0 -0
  187. painapple_code/static/icons/icon-512.png +0 -0
  188. painapple_code/static/icons/icon-72.png +0 -0
  189. painapple_code/static/icons/icon-96.png +0 -0
  190. painapple_code/static/js/action-delegate.js +187 -0
  191. painapple_code/static/js/activity-strip.js +193 -0
  192. painapple_code/static/js/app/context-menus.js +619 -0
  193. painapple_code/static/js/app/file-preview.js +163 -0
  194. painapple_code/static/js/app/init-ui.js +574 -0
  195. painapple_code/static/js/app/input-events.js +561 -0
  196. painapple_code/static/js/app/input-resize.js +313 -0
  197. painapple_code/static/js/app/message-actions.js +419 -0
  198. painapple_code/static/js/app/panels.js +210 -0
  199. painapple_code/static/js/app/rendering-delegators.js +246 -0
  200. painapple_code/static/js/app/server-loading.js +365 -0
  201. painapple_code/static/js/app/session-ops.js +287 -0
  202. painapple_code/static/js/app/session-switch.js +443 -0
  203. painapple_code/static/js/app/settings-helpers.js +280 -0
  204. painapple_code/static/js/app/startup-welcome.js +290 -0
  205. painapple_code/static/js/app-context.js +157 -0
  206. painapple_code/static/js/app.js +2134 -0
  207. painapple_code/static/js/auth-fetch.js +46 -0
  208. painapple_code/static/js/background-tasks.js +274 -0
  209. painapple_code/static/js/boot.js +73 -0
  210. painapple_code/static/js/caret-position.js +171 -0
  211. painapple_code/static/js/chat-navigator.js +710 -0
  212. painapple_code/static/js/chat-search.js +323 -0
  213. painapple_code/static/js/command-executor.js +176 -0
  214. painapple_code/static/js/command-store.js +497 -0
  215. painapple_code/static/js/components.js +1414 -0
  216. painapple_code/static/js/config.js +218 -0
  217. painapple_code/static/js/context-menu.js +740 -0
  218. painapple_code/static/js/controllers/chat-controller.js +4252 -0
  219. painapple_code/static/js/controllers/dialog-controller.js +217 -0
  220. painapple_code/static/js/controllers/tab-controller.js +1828 -0
  221. painapple_code/static/js/controllers/thinking-controller.js +915 -0
  222. painapple_code/static/js/diff-utils.js +486 -0
  223. painapple_code/static/js/drafts-pill.js +95 -0
  224. painapple_code/static/js/editor-view.js +815 -0
  225. painapple_code/static/js/effort-settings.js +430 -0
  226. painapple_code/static/js/feature-triage.js +811 -0
  227. painapple_code/static/js/file-autocomplete.js +516 -0
  228. painapple_code/static/js/file-tabs.js +123 -0
  229. painapple_code/static/js/gestures.js +448 -0
  230. painapple_code/static/js/grid-switcher.js +334 -0
  231. painapple_code/static/js/input-handler.js +1472 -0
  232. painapple_code/static/js/keyboard-bar.js +495 -0
  233. painapple_code/static/js/layout-switcher.js +109 -0
  234. painapple_code/static/js/linkify-utils.js +200 -0
  235. painapple_code/static/js/login.js +141 -0
  236. painapple_code/static/js/open-dialog.js +879 -0
  237. painapple_code/static/js/orphan-terminals.js +373 -0
  238. painapple_code/static/js/path-utils.js +179 -0
  239. painapple_code/static/js/perf-marks.js +102 -0
  240. painapple_code/static/js/permission-settings.js +475 -0
  241. painapple_code/static/js/pointer-input.js +59 -0
  242. painapple_code/static/js/preview/json-tree-menu.js +226 -0
  243. painapple_code/static/js/preview/json-tree-toolbar.js +291 -0
  244. painapple_code/static/js/preview/json-tree.js +457 -0
  245. painapple_code/static/js/preview/preview-breadcrumb.js +387 -0
  246. painapple_code/static/js/preview/preview-edit.js +367 -0
  247. painapple_code/static/js/preview/preview-events.js +361 -0
  248. painapple_code/static/js/preview/preview-history.js +567 -0
  249. painapple_code/static/js/preview/preview-inline-edit.js +869 -0
  250. painapple_code/static/js/preview/preview-poll.js +108 -0
  251. painapple_code/static/js/preview/preview-render.js +572 -0
  252. painapple_code/static/js/preview/preview-search.js +421 -0
  253. painapple_code/static/js/preview/preview-state.js +256 -0
  254. painapple_code/static/js/preview/preview-utils.js +251 -0
  255. painapple_code/static/js/preview-plugins/chart-plugin.js +22 -0
  256. painapple_code/static/js/preview-plugins/csv-plugin.js +188 -0
  257. painapple_code/static/js/preview-plugins/diff-plugin.js +258 -0
  258. painapple_code/static/js/preview-plugins/excalidraw-plugin.js +25 -0
  259. painapple_code/static/js/preview-plugins/html-plugin.js +104 -0
  260. painapple_code/static/js/preview-plugins/image-plugin.js +20 -0
  261. painapple_code/static/js/preview-plugins/index.js +34 -0
  262. painapple_code/static/js/preview-plugins/json-plugin.js +99 -0
  263. painapple_code/static/js/preview-plugins/jsonl-plugin.js +152 -0
  264. painapple_code/static/js/preview-plugins/markdown-plugin.js +266 -0
  265. painapple_code/static/js/preview-plugins/panzoom-plugin.js +63 -0
  266. painapple_code/static/js/preview-plugins/plugin-helpers.js +282 -0
  267. painapple_code/static/js/project-color-picker.js +110 -0
  268. painapple_code/static/js/project-colors.js +163 -0
  269. painapple_code/static/js/prompt-favorites.js +187 -0
  270. painapple_code/static/js/quick-actions-menu.js +1736 -0
  271. painapple_code/static/js/quick-actions-registry.js +1239 -0
  272. painapple_code/static/js/quick-switcher/controller.js +294 -0
  273. painapple_code/static/js/quick-switcher/fuzzy-scorer.js +123 -0
  274. painapple_code/static/js/quick-switcher/index.js +30 -0
  275. painapple_code/static/js/quick-switcher/providers/base-provider.js +51 -0
  276. painapple_code/static/js/quick-switcher/providers/command-provider.js +56 -0
  277. painapple_code/static/js/quick-switcher/providers/file-provider.js +291 -0
  278. painapple_code/static/js/quick-switcher/providers/panel-provider.js +132 -0
  279. painapple_code/static/js/quick-switcher/providers/project-provider.js +361 -0
  280. painapple_code/static/js/quick-switcher/providers/read-files-provider.js +104 -0
  281. painapple_code/static/js/quick-switcher/providers/skills-provider.js +129 -0
  282. painapple_code/static/js/quick-switcher/registry.js +69 -0
  283. painapple_code/static/js/quick-switcher/ui/item.js +54 -0
  284. painapple_code/static/js/quick-switcher/ui/picker.js +464 -0
  285. painapple_code/static/js/recent-opens.js +40 -0
  286. painapple_code/static/js/scroll-manager.js +530 -0
  287. painapple_code/static/js/scroll-state-machine.js +359 -0
  288. painapple_code/static/js/selection/action-bar.js +793 -0
  289. painapple_code/static/js/selection/index.js +15 -0
  290. painapple_code/static/js/selection/selection-handler.js +877 -0
  291. painapple_code/static/js/selection/state.js +181 -0
  292. painapple_code/static/js/session/agent-progress.js +202 -0
  293. painapple_code/static/js/session/handle-agent-message.js +481 -0
  294. painapple_code/static/js/session/handle-message.js +550 -0
  295. painapple_code/static/js/session/interactive.js +587 -0
  296. painapple_code/static/js/session/message-store.js +271 -0
  297. painapple_code/static/js/session/messages.js +253 -0
  298. painapple_code/static/js/session/persistence.js +179 -0
  299. painapple_code/static/js/session/restore.js +136 -0
  300. painapple_code/static/js/session/sync.js +333 -0
  301. painapple_code/static/js/session-container-pool.js +371 -0
  302. painapple_code/static/js/session-setup-panel.js +274 -0
  303. painapple_code/static/js/session.js +1047 -0
  304. painapple_code/static/js/shortcut-hints.js +272 -0
  305. painapple_code/static/js/shortcuts.js +1146 -0
  306. painapple_code/static/js/skills-autocomplete.js +262 -0
  307. painapple_code/static/js/snippets-autocomplete.js +738 -0
  308. painapple_code/static/js/stash-refs-view.js +83 -0
  309. painapple_code/static/js/stash-ui.js +768 -0
  310. painapple_code/static/js/stash.js +785 -0
  311. painapple_code/static/js/status-bar.js +1184 -0
  312. painapple_code/static/js/token-profile.js +274 -0
  313. painapple_code/static/js/tool-renderer-blocks.js +1626 -0
  314. painapple_code/static/js/tool-renderer-thinking.js +813 -0
  315. painapple_code/static/js/tool-renderer.js +1500 -0
  316. painapple_code/static/js/tooltips.js +197 -0
  317. painapple_code/static/js/upload-manager.js +1019 -0
  318. painapple_code/static/js/utils.js +521 -0
  319. painapple_code/static/js/welcome/api.js +363 -0
  320. painapple_code/static/js/welcome/cards.js +626 -0
  321. painapple_code/static/js/welcome/context-menu.js +585 -0
  322. painapple_code/static/js/welcome/families.js +801 -0
  323. painapple_code/static/js/welcome/preview.js +234 -0
  324. painapple_code/static/js/welcome/state.js +129 -0
  325. painapple_code/static/js/welcome.js +1874 -0
  326. painapple_code/static/js/widget-system/base-widget.js +717 -0
  327. painapple_code/static/js/widget-system/device-manager.js +178 -0
  328. painapple_code/static/js/widget-system/event-bus.js +152 -0
  329. painapple_code/static/js/widget-system/icons.js +120 -0
  330. painapple_code/static/js/widget-system/index.js +49 -0
  331. painapple_code/static/js/widget-system/init.js +54 -0
  332. painapple_code/static/js/widget-system/types/bottom-sheet.js +307 -0
  333. painapple_code/static/js/widget-system/types/floating.js +915 -0
  334. painapple_code/static/js/widget-system/types/index.js +40 -0
  335. painapple_code/static/js/widget-system/types/modal.js +147 -0
  336. painapple_code/static/js/widget-system/types/sidebar.js +158 -0
  337. painapple_code/static/js/widget-system/types/tab.js +146 -0
  338. painapple_code/static/js/widget-system/types/top-sheet.js +203 -0
  339. painapple_code/static/js/widget-system/widget-manager.js +1187 -0
  340. painapple_code/static/js/widgets/about-widget.js +260 -0
  341. painapple_code/static/js/widgets/active-sessions-widget.js +760 -0
  342. painapple_code/static/js/widgets/agents-widget.js +897 -0
  343. painapple_code/static/js/widgets/browser-widget.js +579 -0
  344. painapple_code/static/js/widgets/commands-widget.js +694 -0
  345. painapple_code/static/js/widgets/config/commit-sections.js +449 -0
  346. painapple_code/static/js/widgets/config/dir-autocomplete.js +224 -0
  347. painapple_code/static/js/widgets/config/gestures.js +161 -0
  348. painapple_code/static/js/widgets/config/models-tab.js +988 -0
  349. painapple_code/static/js/widgets/config/quick-actions-tab.js +882 -0
  350. painapple_code/static/js/widgets/config/shortcut-editor.js +587 -0
  351. painapple_code/static/js/widgets/config/state.js +517 -0
  352. painapple_code/static/js/widgets/config/system-controls.js +90 -0
  353. painapple_code/static/js/widgets/config-widget.js +1535 -0
  354. painapple_code/static/js/widgets/cost-analytics-widget.js +621 -0
  355. painapple_code/static/js/widgets/debug-widget.js +693 -0
  356. painapple_code/static/js/widgets/diff-viewer-widget.js +1420 -0
  357. painapple_code/static/js/widgets/discussion-widget.js +1107 -0
  358. painapple_code/static/js/widgets/file-explorer-widget.js +2166 -0
  359. painapple_code/static/js/widgets/file-preview-widget.js +861 -0
  360. painapple_code/static/js/widgets/git-widget.js +794 -0
  361. painapple_code/static/js/widgets/helpers-install-widget.js +720 -0
  362. painapple_code/static/js/widgets/history-explorer-widget.js +1804 -0
  363. painapple_code/static/js/widgets/image-annotate-widget.js +951 -0
  364. painapple_code/static/js/widgets/image-preview-widget.js +692 -0
  365. painapple_code/static/js/widgets/index.js +176 -0
  366. painapple_code/static/js/widgets/log-explorer-widget.js +1057 -0
  367. painapple_code/static/js/widgets/plugins-widget.js +305 -0
  368. painapple_code/static/js/widgets/prompt-explorer-widget.js +1119 -0
  369. painapple_code/static/js/widgets/search-files-widget.js +529 -0
  370. painapple_code/static/js/widgets/skills-widget.js +843 -0
  371. painapple_code/static/js/widgets/snippets-widget.js +479 -0
  372. painapple_code/static/js/widgets/sub-agents-widget.js +333 -0
  373. painapple_code/static/js/widgets/tasks-widget.js +379 -0
  374. painapple_code/static/js/widgets/terminal/connection.js +283 -0
  375. painapple_code/static/js/widgets/terminal/gestures.js +357 -0
  376. painapple_code/static/js/widgets/terminal/init.js +642 -0
  377. painapple_code/static/js/widgets/terminal/link-providers.js +532 -0
  378. painapple_code/static/js/widgets/terminal/osc52.js +213 -0
  379. painapple_code/static/js/widgets/terminal/render.js +159 -0
  380. painapple_code/static/js/widgets/terminal/size.js +54 -0
  381. painapple_code/static/js/widgets/terminal/state.js +211 -0
  382. painapple_code/static/js/widgets/terminal/wrap-utils.js +101 -0
  383. painapple_code/static/js/widgets/terminal-widget.js +793 -0
  384. painapple_code/static/js/widgets/uploads-widget.js +461 -0
  385. painapple_code/static/js/widgets/zen-widget.js +1001 -0
  386. painapple_code/static/login.html +51 -0
  387. painapple_code/static/manifest.json +73 -0
  388. painapple_code/static/sw.js +293 -0
  389. painapple_code/static/vendor/LICENSES.txt +769 -0
  390. painapple_code/static/vendor/README.md +103 -0
  391. painapple_code/static/vendor/codemirror.js +35 -0
  392. painapple_code/static/vendor/codemirror.js.LEGAL.txt +304 -0
  393. painapple_code/static/vendor/github-markdown-dark.min.css +1124 -0
  394. painapple_code/static/vendor/highlight-github-dark.min.css +10 -0
  395. painapple_code/static/vendor/highlight-lang-dockerfile.min.js +8 -0
  396. painapple_code/static/vendor/highlight-lang-nginx.min.js +21 -0
  397. painapple_code/static/vendor/highlight-lang-properties.min.js +10 -0
  398. painapple_code/static/vendor/highlight-lang-scala.min.js +28 -0
  399. painapple_code/static/vendor/highlight.min.js +1213 -0
  400. painapple_code/static/vendor/marked.min.js +6 -0
  401. painapple_code/static/vendor/purify.min.js +3 -0
  402. painapple_code/static/vendor/xterm-addon-fit.js +2 -0
  403. painapple_code/static/vendor/xterm.css +209 -0
  404. painapple_code/static/vendor/xterm.js +2 -0
  405. painapple_code/static/web-client.html +635 -0
  406. painapple_code/subprocess_registry.py +535 -0
  407. painapple_code/tls_cert.py +79 -0
  408. painapple_code/tools/agents/shadow-git-helper.md +400 -0
  409. painapple_code/tools/excalidraw-to-svg.js +143 -0
  410. painapple_code/tools/install-helpers.sh +148 -0
  411. painapple_code/tools/shadow-git +320 -0
  412. painapple_code/tools/shadow-query +120 -0
  413. painapple_code/tools/vegalite-to-svg.js +79 -0
  414. painapple_code/turn_query.py +766 -0
  415. painapple_code/turn_tracker.py +221 -0
  416. painapple_code/utils/__init__.py +0 -0
  417. painapple_code/utils/agent_cli.py +306 -0
  418. painapple_code/utils/chart.py +103 -0
  419. painapple_code/utils/excalidraw.py +107 -0
  420. painapple_code/utils/file_lock.py +162 -0
  421. painapple_code/utils/file_paths.py +648 -0
  422. painapple_code/utils/generate_icons.py +251 -0
  423. painapple_code/utils/proc.py +205 -0
  424. painapple_code/utils/pty_backend.py +59 -0
  425. painapple_code/utils/pty_posix.py +160 -0
  426. painapple_code/utils/pty_windows.py +245 -0
  427. painapple_code/utils/token_profiles.py +91 -0
  428. painapple_code/viewer_templates.py +657 -0
  429. painapple_code/welcome_search.py +879 -0
  430. painapple_code-1.0.0.dist-info/METADATA +292 -0
  431. painapple_code-1.0.0.dist-info/RECORD +436 -0
  432. painapple_code-1.0.0.dist-info/WHEEL +5 -0
  433. painapple_code-1.0.0.dist-info/entry_points.txt +2 -0
  434. painapple_code-1.0.0.dist-info/licenses/LICENSE +661 -0
  435. painapple_code-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +261 -0
  436. painapple_code-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,163 @@
1
+ """Launch assembly for ``painapple --in-docker`` and docker-mode
2
+ profiles — turns global defaults / profile data / explicit serve flags
3
+ into DockerSettings and hands off to container.run_container().
4
+ """
5
+
6
+ from pathlib import Path
7
+
8
+ from painapple_code.cli import serve_config
9
+ from painapple_code.cli.ui import DIM, RESET, info, warn
10
+ from painapple_code.cli.deploy.config import ConfigError, DockerSettings
11
+ from painapple_code.cli.deploy.container import run_container
12
+
13
+
14
+ def _flag(argv, *names):
15
+ """Value of an explicitly passed ``--flag value`` / ``--flag=value``,
16
+ or None. Used to tell an explicit flag from an argparse default."""
17
+ for i, tok in enumerate(argv):
18
+ for name in names:
19
+ if tok == name and i + 1 < len(argv):
20
+ return argv[i + 1]
21
+ if tok.startswith(name + "="):
22
+ return tok.split("=", 1)[1]
23
+ return None
24
+
25
+
26
+ def _try_assign(cfg, key, value, source):
27
+ try:
28
+ cfg.assign(key, str(value))
29
+ except ConfigError as e:
30
+ warn(f"{source}: {e} — keeping {cfg.get(key)!r}")
31
+
32
+
33
+ def _apply_serve_flags(cfg, argv):
34
+ """Explicit serve flags override whatever the settings came from —
35
+ same precedence story as host mode (defaults < saved < flags)."""
36
+ overrides = (("--host", "LISTEN_HOST"), ("--port", "PORT"),
37
+ ("--tls", "TLS_MODE"), ("--instance-name", "INSTANCE_NAME"),
38
+ ("--accent", "ACCENT"))
39
+ for flag, key in overrides:
40
+ value = _flag(argv, flag)
41
+ if value is not None:
42
+ _try_assign(cfg, key, value, flag)
43
+ # The container invocation is assembled from DockerSettings, not the
44
+ # host argv, so the password-visibility flags don't forward. That's
45
+ # fine for --no-password: the container binds non-loopback internally,
46
+ # so its startup box hides credentials by default anyway. --show-password
47
+ # would need forwarding to have any effect — warn instead of silently
48
+ # dropping it (the host-side bootstrap block prints the URL regardless).
49
+ if "--show-password" in argv:
50
+ warn("--show-password is not forwarded into the container; its "
51
+ "startup box hides credentials (non-loopback bind). The "
52
+ "login URL is printed host-side after start, or via "
53
+ "`painapple password`.")
54
+ ws = _flag(argv, "--workspace", "--cwd")
55
+ if ws is not None:
56
+ _try_assign(cfg, "WORKSPACE", ws, "--workspace")
57
+ return True
58
+ return False
59
+
60
+
61
+ def _auto_workspace_mode(cfg):
62
+ """Zero-config mode pick: a git checkout → 'project', anything else
63
+ → 'parent' — matching "I'm in a repo" vs "I opened a folder of
64
+ repos" without asking. (.git can be a dir or a worktree file.)"""
65
+ ws = Path(cfg.workspace or ".")
66
+ mode = "project" if (ws / ".git").exists() else "parent"
67
+ reason = "found .git" if mode == "project" else "no .git"
68
+ cfg.workspace_mode = mode
69
+ info(f"{ws} → {mode} mode {DIM}({reason}){RESET}")
70
+
71
+
72
+ def docker_settings_with_globals(prof):
73
+ """A docker-mode profile's settings with the GLOBAL runtime/image
74
+ defaults filled in wherever the profile doesn't pin its own (the
75
+ wizard leaves them unset by default)."""
76
+ cfg = prof.docker_settings()
77
+ gvalues, _ = serve_config.load(serve_config.root_home() / "serve.yaml")
78
+ for key, cfg_key in (("runtime", "RUNTIME"),
79
+ ("runtime_flags", "RUNTIME_FLAGS"),
80
+ ("image", "IMAGE")):
81
+ if not prof.data.get(key) and gvalues.get(key) not in (None, ""):
82
+ _try_assign(cfg, cfg_key, gvalues[key], "serve.yaml")
83
+ return cfg
84
+
85
+
86
+ def adhoc_settings(argv):
87
+ """DockerSettings for a bare ``painapple --in-docker``: global
88
+ serve.yaml defaults (network + runtime/image) + explicit serve
89
+ flags, cwd as the workspace."""
90
+ cfg = DockerSettings(profile=None)
91
+ values, problems = serve_config.load()
92
+ for problem in problems:
93
+ warn(f"serve.yaml: {problem}")
94
+ for key, cfg_key in (("runtime", "RUNTIME"), ("runtime_flags", "RUNTIME_FLAGS"),
95
+ ("image", "IMAGE"), ("host", "LISTEN_HOST"),
96
+ ("port", "PORT"), ("tls", "TLS_MODE")):
97
+ if values.get(key) not in (None, ""):
98
+ _try_assign(cfg, cfg_key, values[key], "serve.yaml")
99
+
100
+ explicit_ws = _apply_serve_flags(cfg, argv)
101
+ if not explicit_ws:
102
+ cfg.workspace = str(Path.cwd())
103
+ _auto_workspace_mode(cfg)
104
+ return cfg
105
+
106
+
107
+ def run_adhoc(argv):
108
+ """``painapple --in-docker``: the cwd (or --workspace) as a
109
+ foreground container sandbox. Ctrl-C stops it, --rm cleans up."""
110
+ from painapple_code.cli import profiles
111
+ profiles.ensure_migrated()
112
+ cfg = adhoc_settings(argv)
113
+ return run_container(cfg, detach=False, profile=None)
114
+
115
+
116
+ def profile_settings(prof, argv=()):
117
+ """DockerSettings for a profile + explicit flag overrides.
118
+
119
+ ``mode: docker`` profiles carry the full settings themselves. A
120
+ ``mode: host`` profile forced into docker (--in-docker override)
121
+ maps its host values onto container settings — its data home rides
122
+ along as a bind-mounted /data, so it stays the same deployment."""
123
+ if prof.is_docker:
124
+ cfg = docker_settings_with_globals(prof)
125
+ else:
126
+ cfg = DockerSettings(profile=prof.name)
127
+ values = prof.host_values()
128
+ for key, cfg_key in (("host", "LISTEN_HOST"), ("port", "PORT"),
129
+ ("tls", "TLS_MODE"), ("instance_name", "INSTANCE_NAME"),
130
+ ("accent", "ACCENT"), ("workspace", "WORKSPACE")):
131
+ if values.get(key) not in (None, ""):
132
+ _try_assign(cfg, cfg_key, values[key], f"profile {prof.name}")
133
+ # Same deployment, containerized: the profile's data home IS the
134
+ # container's /data.
135
+ cfg.data_volume = str(prof.home())
136
+ if not cfg.instance_name:
137
+ cfg.instance_name = prof.name[:12]
138
+ # Global --in-docker defaults still supply runtime/image.
139
+ gvalues, _ = serve_config.load(serve_config.root_home() / "serve.yaml")
140
+ for key, cfg_key in (("runtime", "RUNTIME"),
141
+ ("runtime_flags", "RUNTIME_FLAGS"),
142
+ ("image", "IMAGE")):
143
+ if gvalues.get(key) not in (None, ""):
144
+ _try_assign(cfg, cfg_key, gvalues[key], "serve.yaml")
145
+
146
+ explicit_ws = _apply_serve_flags(cfg, list(argv))
147
+ if not cfg.workspace and cfg.workspace_mode != "multi":
148
+ cfg.workspace = str(Path.cwd())
149
+ _auto_workspace_mode(cfg)
150
+ elif explicit_ws and prof.is_docker is False:
151
+ _auto_workspace_mode(cfg)
152
+ return cfg
153
+
154
+
155
+ def run_profile(prof, argv=(), detach=False):
156
+ """Run a profile as a container (its own mode, or forced by
157
+ --in-docker). Foreground unless ``detach``."""
158
+ if not prof.is_docker:
159
+ warn(f"Profile '{prof.name}' is mode: host — running it in docker for "
160
+ f"this launch (--in-docker). Make it permanent with: "
161
+ f"painapple setup {prof.name}")
162
+ cfg = profile_settings(prof, argv)
163
+ return run_container(cfg, detach=detach, profile=prof.name)
@@ -0,0 +1,223 @@
1
+ """Container runtime resolution (docker/podman) and invocation helpers."""
2
+
3
+ import os
4
+ import re
5
+ import shlex
6
+ import shutil
7
+ import subprocess
8
+ from pathlib import Path
9
+
10
+ from painapple_code.cli.ui import die
11
+
12
+
13
+ # Labels a painapple image carries about the user it runs as. Images
14
+ # built before the uid-alignment work have none of them — every reader
15
+ # below falls back to probing, so pulled older tags still work.
16
+ APP_UID_LABEL = "io.painapple.app-uid"
17
+ APP_GID_LABEL = "io.painapple.app-gid"
18
+ ADAPT_LABEL = "io.painapple.uid-adapt"
19
+
20
+
21
+ def _responds(binary):
22
+ """Does this runtime actually answer? A `docker` CLI whose daemon
23
+ socket we can't reach is on PATH but useless — picking it means the
24
+ real failure surfaces much later as a nonsense message ("Image not
25
+ found locally", because `image inspect` silently returned nothing).
26
+ """
27
+ try:
28
+ return subprocess.run([binary, "info"], capture_output=True,
29
+ timeout=20).returncode == 0
30
+ except (OSError, subprocess.TimeoutExpired):
31
+ return False
32
+
33
+
34
+ def auto_detect():
35
+ """Prefer docker (more common default); podman as fallback — but
36
+ only pick a runtime that's actually reachable, so a docker CLI with
37
+ no daemon behind it doesn't shadow a working podman."""
38
+ present = [(name, path) for name in ("docker", "podman")
39
+ if (path := shutil.which(name))]
40
+ if not present:
41
+ # No RUNTIME env var here — that's the build wrapper's lever
42
+ # (painapple-docker.sh). The CLI's runtime lives in config.
43
+ die("Neither docker nor podman found in PATH.",
44
+ "Install one, then point painapple at it:\n"
45
+ " painapple setup (global runtime default)\n"
46
+ " painapple profile set NAME RUNTIME=… (one deployment — also "
47
+ "takes an absolute path to a custom binary)")
48
+ for name, path in present:
49
+ if _responds(path):
50
+ return name
51
+ names = ", ".join(name for name, _ in present)
52
+ die(f"Found {names} on PATH, but neither answers — `{present[0][0]} info` failed.",
53
+ "Start the service (`systemctl --user start docker`/`podman.socket`), "
54
+ "check you're in the `docker` group, or run `painapple setup` to point "
55
+ "at the runtime you actually use.")
56
+
57
+
58
+ def detect_runtimes():
59
+ """[(name, path, version)] for every container runtime found on
60
+ PATH — what the setup wizard lists as concrete choices."""
61
+ found = []
62
+ for name in ("docker", "podman"):
63
+ path = shutil.which(name)
64
+ if not path:
65
+ continue
66
+ version = ""
67
+ try:
68
+ out = subprocess.run([path, "--version"], capture_output=True,
69
+ text=True, encoding="utf-8", errors="replace", timeout=5).stdout.strip()
70
+ m = re.search(r"version\s+v?([0-9][\w.-]*)", out)
71
+ version = m.group(1).rstrip(",") if m else out
72
+ except (OSError, subprocess.TimeoutExpired):
73
+ pass
74
+ found.append((name, path, version))
75
+ return found
76
+
77
+
78
+ class Runtime:
79
+ """One resolved runtime: binary (name or explicit path) + persistent
80
+ global flags.
81
+
82
+ Every invocation goes through here so RUNTIME_FLAGS injection applies
83
+ uniformly — otherwise `run` would see the flags but `ps`/`exec`
84
+ would silently use the default storage driver and miss images.
85
+ """
86
+
87
+ def __init__(self, cfg):
88
+ self.binary = cfg.runtime or auto_detect()
89
+ # Display/behavior name: an explicit /path/to/podman still counts
90
+ # as podman for --userns/SELinux handling.
91
+ self.name = Path(self.binary).name
92
+ self.flags = shlex.split(cfg.runtime_flags) if cfg.runtime_flags else []
93
+ self._image_ids = {} # image → (uid, gid), memoized probe results
94
+
95
+ def argv(self, *args):
96
+ return [self.binary, *self.flags, *args]
97
+
98
+ def run(self, *args, **kwargs):
99
+ return subprocess.run(self.argv(*args), **kwargs)
100
+
101
+ def output(self, *args):
102
+ """Stdout of a runtime call, or '' on any failure."""
103
+ try:
104
+ proc = self.run(*args, capture_output=True, text=True, encoding="utf-8", errors="replace")
105
+ return proc.stdout if proc.returncode == 0 else ""
106
+ except OSError:
107
+ return ""
108
+
109
+ def ok(self, *args):
110
+ try:
111
+ return self.run(*args, capture_output=True).returncode == 0
112
+ except OSError:
113
+ return False
114
+
115
+ def exec_interactive(self, *args):
116
+ """Replace this process with the runtime call — full TTY handoff,
117
+ native Ctrl-C, no Python in the signal path."""
118
+ argv = self.argv(*args)
119
+ os.execvp(argv[0], argv)
120
+
121
+ # ── Queries ─────────────────────────────────────────────────────────
122
+
123
+ def image_exists(self, image):
124
+ return self.ok("image", "inspect", image)
125
+
126
+ def volume_exists(self, volume):
127
+ return self.ok("volume", "inspect", volume)
128
+
129
+ def container_running(self, name):
130
+ names = self.output("ps", "--format", "{{.Names}}").splitlines()
131
+ return name in names
132
+
133
+ def container_exists(self, name):
134
+ names = self.output("ps", "-a", "--format", "{{.Names}}").splitlines()
135
+ return name in names
136
+
137
+ def container_status(self, name):
138
+ for line in self.output("ps", "--format", "{{.Names}}\t{{.Status}}").splitlines():
139
+ cname, _, status = line.partition("\t")
140
+ if cname == name:
141
+ return status
142
+ return None
143
+
144
+ def selinux_enforcing(self):
145
+ """Podman bind mounts need :Z under SELinux."""
146
+ if self.name != "podman" or not shutil.which("selinuxenabled"):
147
+ return False
148
+ try:
149
+ return subprocess.run(["selinuxenabled"], capture_output=True).returncode == 0
150
+ except OSError:
151
+ return False
152
+
153
+ def responds(self):
154
+ """Runtime reachable? (Explicitly configured binaries skip
155
+ auto_detect's check, so run_container asks again.)"""
156
+ return _responds(self.binary)
157
+
158
+ # ── Image identity ──────────────────────────────────────────────────
159
+
160
+ def label(self, image, name):
161
+ """One image label, '' when absent. Docker renders a missing key
162
+ as '<no value>', podman as an empty string — normalize both."""
163
+ raw = self.output("image", "inspect", image, "--format",
164
+ '{{index .Config.Labels "%s"}}' % name).strip()
165
+ return "" if raw in ("<no value>", "<nil>") else raw
166
+
167
+ def image_adapts_uid(self, image):
168
+ """Does the image's entrypoint re-stamp its user to match host
169
+ ownership and drop privileges? (Images predating that: no.)"""
170
+ return self.label(image, ADAPT_LABEL) == "1"
171
+
172
+ def image_user_ids(self, image):
173
+ """(uid, gid) of the image's `app` user.
174
+
175
+ Labels first — an adapting image starts as root, so its
176
+ Config.User says nothing about the uid the server ends up
177
+ running as. Falls back to one throwaway container for images
178
+ built before the labels existed.
179
+ """
180
+ if image in self._image_ids:
181
+ return self._image_ids[image]
182
+
183
+ uid, gid = self.label(image, APP_UID_LABEL), self.label(image, APP_GID_LABEL)
184
+ if uid.isdigit() and gid.isdigit():
185
+ ids = (int(uid), int(gid))
186
+ else:
187
+ out = self.output("run", "--rm", "--entrypoint", "", "--network", "none",
188
+ image, "sh", "-c", "id -u; id -g").split()
189
+ ids = ((int(out[0]), int(out[1]))
190
+ if len(out) >= 2 and all(t.isdigit() for t in out[:2]) else (None, None))
191
+ self._image_ids[image] = ids
192
+ return ids
193
+
194
+ def supports_userns_map(self):
195
+ """podman ≥ 4.3 understands `keep-id:uid=…,gid=…` (the flag that
196
+ lands the host user ON the image's app uid). Unparsable version
197
+ → assume yes; podman 4.3 shipped in 2022 and the failure is a
198
+ loud one-liner from podman itself, not a silent misbehavior."""
199
+ if self.name != "podman":
200
+ return False
201
+ raw = self.output("version", "--format", "{{.Client.Version}}").strip()
202
+ m = re.match(r"(\d+)\.(\d+)", raw)
203
+ return (int(m.group(1)), int(m.group(2))) >= (4, 3) if m else True
204
+
205
+ # ── Volume ownership ────────────────────────────────────────────────
206
+
207
+ def chown_volume(self, image, volume, uid, gid, extra_args=()):
208
+ """Make a named volume owned by the uid the container will run
209
+ as — as root inside the same user namespace the real run uses,
210
+ so the mapping matches.
211
+
212
+ Testing the top-level dir is NOT enough: a volume seeded from the
213
+ image has its root owned by `app` while everything a previous
214
+ container wrote under it (/data/logs/server.log, the classic) is
215
+ owned by that container's uid instead. `-print -quit` stops at
216
+ the first offender, so this is one cheap walk when there's
217
+ nothing to fix — `chown -R` over a populated data home (DuckDB
218
+ plus every session log) is not something to redo on every start.
219
+ """
220
+ script = (f'[ -n "$(find /vol \\( ! -uid {uid} -o ! -gid {gid} \\) '
221
+ f'-print -quit 2>/dev/null)" ] && chown -R {uid}:{gid} /vol; :')
222
+ return self.ok("run", "--rm", "--user", "0", "--entrypoint", "",
223
+ *extra_args, "-v", f"{volume}:/vol", image, "sh", "-c", script)