pythinker-code 2.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.
- pythinker_code/CHANGELOG.md +16 -0
- pythinker_code/__init__.py +0 -0
- pythinker_code/__main__.py +92 -0
- pythinker_code/acp/AGENTS.md +93 -0
- pythinker_code/acp/__init__.py +13 -0
- pythinker_code/acp/convert.py +128 -0
- pythinker_code/acp/host.py +298 -0
- pythinker_code/acp/mcp.py +46 -0
- pythinker_code/acp/server.py +497 -0
- pythinker_code/acp/session.py +496 -0
- pythinker_code/acp/tools.py +167 -0
- pythinker_code/acp/types.py +13 -0
- pythinker_code/acp/version.py +45 -0
- pythinker_code/agents/default/agent.yaml +36 -0
- pythinker_code/agents/default/coder.yaml +25 -0
- pythinker_code/agents/default/explore.yaml +46 -0
- pythinker_code/agents/default/plan.yaml +30 -0
- pythinker_code/agents/default/system.md +164 -0
- pythinker_code/agents/okabe/agent.yaml +22 -0
- pythinker_code/agentspec.py +163 -0
- pythinker_code/app.py +820 -0
- pythinker_code/approval_runtime/__init__.py +29 -0
- pythinker_code/approval_runtime/models.py +42 -0
- pythinker_code/approval_runtime/runtime.py +235 -0
- pythinker_code/auth/__init__.py +25 -0
- pythinker_code/auth/anthropic_direct.py +207 -0
- pythinker_code/auth/deepseek.py +192 -0
- pythinker_code/auth/lm_studio.py +418 -0
- pythinker_code/auth/minimax.py +203 -0
- pythinker_code/auth/oauth.py +1122 -0
- pythinker_code/auth/ollama.py +293 -0
- pythinker_code/auth/openai.py +771 -0
- pythinker_code/auth/opencode_go.py +203 -0
- pythinker_code/auth/openrouter.py +225 -0
- pythinker_code/auth/platforms.py +466 -0
- pythinker_code/background/__init__.py +36 -0
- pythinker_code/background/agent_runner.py +231 -0
- pythinker_code/background/ids.py +19 -0
- pythinker_code/background/manager.py +650 -0
- pythinker_code/background/models.py +105 -0
- pythinker_code/background/store.py +237 -0
- pythinker_code/background/summary.py +66 -0
- pythinker_code/background/worker.py +209 -0
- pythinker_code/cli/__init__.py +1326 -0
- pythinker_code/cli/__main__.py +19 -0
- pythinker_code/cli/_lazy_group.py +238 -0
- pythinker_code/cli/export.py +322 -0
- pythinker_code/cli/info.py +62 -0
- pythinker_code/cli/mcp.py +349 -0
- pythinker_code/cli/plugin.py +351 -0
- pythinker_code/cli/toad.py +74 -0
- pythinker_code/cli/vis.py +38 -0
- pythinker_code/cli/web.py +80 -0
- pythinker_code/config.py +453 -0
- pythinker_code/constant.py +33 -0
- pythinker_code/exception.py +43 -0
- pythinker_code/hooks/__init__.py +4 -0
- pythinker_code/hooks/config.py +34 -0
- pythinker_code/hooks/engine.py +371 -0
- pythinker_code/hooks/events.py +190 -0
- pythinker_code/hooks/runner.py +89 -0
- pythinker_code/llm.py +412 -0
- pythinker_code/metadata.py +79 -0
- pythinker_code/notifications/__init__.py +33 -0
- pythinker_code/notifications/llm.py +77 -0
- pythinker_code/notifications/manager.py +145 -0
- pythinker_code/notifications/models.py +50 -0
- pythinker_code/notifications/notifier.py +41 -0
- pythinker_code/notifications/store.py +118 -0
- pythinker_code/notifications/wire.py +21 -0
- pythinker_code/plugin/__init__.py +124 -0
- pythinker_code/plugin/manager.py +153 -0
- pythinker_code/plugin/tool.py +173 -0
- pythinker_code/prompts/__init__.py +6 -0
- pythinker_code/prompts/compact.md +73 -0
- pythinker_code/prompts/init.md +21 -0
- pythinker_code/py.typed +0 -0
- pythinker_code/session.py +319 -0
- pythinker_code/session_fork.py +325 -0
- pythinker_code/session_state.py +132 -0
- pythinker_code/share.py +14 -0
- pythinker_code/skill/__init__.py +727 -0
- pythinker_code/skill/flow/__init__.py +99 -0
- pythinker_code/skill/flow/d2.py +482 -0
- pythinker_code/skill/flow/mermaid.py +266 -0
- pythinker_code/skills/pythinker-code-help/SKILL.md +54 -0
- pythinker_code/skills/skill-creator/SKILL.md +367 -0
- pythinker_code/soul/__init__.py +304 -0
- pythinker_code/soul/agent.py +520 -0
- pythinker_code/soul/approval.py +267 -0
- pythinker_code/soul/btw.py +214 -0
- pythinker_code/soul/compaction.py +189 -0
- pythinker_code/soul/context.py +339 -0
- pythinker_code/soul/denwarenji.py +39 -0
- pythinker_code/soul/dynamic_injection.py +84 -0
- pythinker_code/soul/dynamic_injections/__init__.py +0 -0
- pythinker_code/soul/dynamic_injections/auto_mode.py +72 -0
- pythinker_code/soul/dynamic_injections/plan_mode.py +239 -0
- pythinker_code/soul/message.py +92 -0
- pythinker_code/soul/pythinkersoul.py +1613 -0
- pythinker_code/soul/slash.py +340 -0
- pythinker_code/soul/toolset.py +788 -0
- pythinker_code/subagents/__init__.py +21 -0
- pythinker_code/subagents/builder.py +42 -0
- pythinker_code/subagents/core.py +86 -0
- pythinker_code/subagents/git_context.py +172 -0
- pythinker_code/subagents/models.py +54 -0
- pythinker_code/subagents/output.py +71 -0
- pythinker_code/subagents/registry.py +28 -0
- pythinker_code/subagents/runner.py +428 -0
- pythinker_code/subagents/store.py +196 -0
- pythinker_code/telemetry/__init__.py +211 -0
- pythinker_code/telemetry/config.py +54 -0
- pythinker_code/telemetry/crash.py +157 -0
- pythinker_code/telemetry/metrics.py +208 -0
- pythinker_code/telemetry/otel.py +240 -0
- pythinker_code/telemetry/sentry.py +167 -0
- pythinker_code/telemetry/sink.py +189 -0
- pythinker_code/tools/AGENTS.md +6 -0
- pythinker_code/tools/__init__.py +105 -0
- pythinker_code/tools/agent/__init__.py +277 -0
- pythinker_code/tools/agent/description.md +41 -0
- pythinker_code/tools/ask_user/__init__.py +159 -0
- pythinker_code/tools/ask_user/description.md +19 -0
- pythinker_code/tools/background/__init__.py +318 -0
- pythinker_code/tools/background/list.md +10 -0
- pythinker_code/tools/background/output.md +11 -0
- pythinker_code/tools/background/stop.md +8 -0
- pythinker_code/tools/display.py +46 -0
- pythinker_code/tools/dmail/__init__.py +38 -0
- pythinker_code/tools/dmail/dmail.md +17 -0
- pythinker_code/tools/file/__init__.py +30 -0
- pythinker_code/tools/file/glob.md +17 -0
- pythinker_code/tools/file/glob.py +160 -0
- pythinker_code/tools/file/grep.md +6 -0
- pythinker_code/tools/file/grep_local.py +589 -0
- pythinker_code/tools/file/plan_mode.py +45 -0
- pythinker_code/tools/file/read.md +16 -0
- pythinker_code/tools/file/read.py +300 -0
- pythinker_code/tools/file/read_media.md +24 -0
- pythinker_code/tools/file/read_media.py +217 -0
- pythinker_code/tools/file/replace.md +7 -0
- pythinker_code/tools/file/replace.py +195 -0
- pythinker_code/tools/file/utils.py +257 -0
- pythinker_code/tools/file/write.md +5 -0
- pythinker_code/tools/file/write.py +177 -0
- pythinker_code/tools/plan/__init__.py +327 -0
- pythinker_code/tools/plan/description.md +29 -0
- pythinker_code/tools/plan/enter.py +190 -0
- pythinker_code/tools/plan/enter_description.md +35 -0
- pythinker_code/tools/plan/heroes.py +277 -0
- pythinker_code/tools/shell/__init__.py +253 -0
- pythinker_code/tools/shell/bash.md +35 -0
- pythinker_code/tools/shell/powershell.md +30 -0
- pythinker_code/tools/test.py +55 -0
- pythinker_code/tools/think/__init__.py +21 -0
- pythinker_code/tools/think/think.md +1 -0
- pythinker_code/tools/todo/__init__.py +168 -0
- pythinker_code/tools/todo/set_todo_list.md +23 -0
- pythinker_code/tools/utils.py +199 -0
- pythinker_code/tools/web/__init__.py +4 -0
- pythinker_code/tools/web/fetch.md +1 -0
- pythinker_code/tools/web/fetch.py +189 -0
- pythinker_code/tools/web/search.md +1 -0
- pythinker_code/tools/web/search.py +163 -0
- pythinker_code/ui/__init__.py +0 -0
- pythinker_code/ui/acp/__init__.py +99 -0
- pythinker_code/ui/print/__init__.py +474 -0
- pythinker_code/ui/print/visualize.py +185 -0
- pythinker_code/ui/shell/__init__.py +1696 -0
- pythinker_code/ui/shell/console.py +109 -0
- pythinker_code/ui/shell/debug.py +190 -0
- pythinker_code/ui/shell/echo.py +17 -0
- pythinker_code/ui/shell/export_import.py +117 -0
- pythinker_code/ui/shell/keyboard.py +300 -0
- pythinker_code/ui/shell/mcp_status.py +113 -0
- pythinker_code/ui/shell/model_picker.py +318 -0
- pythinker_code/ui/shell/oauth.py +272 -0
- pythinker_code/ui/shell/placeholders.py +531 -0
- pythinker_code/ui/shell/prompt.py +2278 -0
- pythinker_code/ui/shell/replay.py +215 -0
- pythinker_code/ui/shell/session_picker.py +227 -0
- pythinker_code/ui/shell/setup.py +212 -0
- pythinker_code/ui/shell/slash.py +898 -0
- pythinker_code/ui/shell/startup.py +32 -0
- pythinker_code/ui/shell/task_browser.py +486 -0
- pythinker_code/ui/shell/update.py +350 -0
- pythinker_code/ui/shell/usage.py +291 -0
- pythinker_code/ui/shell/usage_adapters/__init__.py +50 -0
- pythinker_code/ui/shell/usage_adapters/anthropic_admin.py +233 -0
- pythinker_code/ui/shell/usage_adapters/base.py +72 -0
- pythinker_code/ui/shell/usage_adapters/deepseek.py +137 -0
- pythinker_code/ui/shell/usage_adapters/minimax.py +236 -0
- pythinker_code/ui/shell/usage_adapters/openai_admin.py +225 -0
- pythinker_code/ui/shell/usage_adapters/openai_chatgpt.py +241 -0
- pythinker_code/ui/shell/usage_adapters/opencode_go.py +232 -0
- pythinker_code/ui/shell/usage_adapters/openrouter.py +105 -0
- pythinker_code/ui/shell/usage_adapters/pythinker.py +189 -0
- pythinker_code/ui/shell/usage_adapters/pythinker_ai.py +50 -0
- pythinker_code/ui/shell/usage_render.py +150 -0
- pythinker_code/ui/shell/visualize/__init__.py +165 -0
- pythinker_code/ui/shell/visualize/_approval_panel.py +505 -0
- pythinker_code/ui/shell/visualize/_blocks.py +629 -0
- pythinker_code/ui/shell/visualize/_btw_panel.py +224 -0
- pythinker_code/ui/shell/visualize/_input_router.py +48 -0
- pythinker_code/ui/shell/visualize/_interactive.py +523 -0
- pythinker_code/ui/shell/visualize/_live_view.py +826 -0
- pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
- pythinker_code/ui/theme.py +241 -0
- pythinker_code/usage_ratelimit_cache.py +175 -0
- pythinker_code/utils/__init__.py +0 -0
- pythinker_code/utils/aiohttp.py +24 -0
- pythinker_code/utils/aioqueue.py +72 -0
- pythinker_code/utils/broadcast.py +37 -0
- pythinker_code/utils/changelog.py +108 -0
- pythinker_code/utils/clipboard.py +246 -0
- pythinker_code/utils/datetime.py +64 -0
- pythinker_code/utils/diff.py +135 -0
- pythinker_code/utils/editor.py +91 -0
- pythinker_code/utils/environment.py +73 -0
- pythinker_code/utils/envvar.py +22 -0
- pythinker_code/utils/export.py +696 -0
- pythinker_code/utils/file_filter.py +375 -0
- pythinker_code/utils/frontmatter.py +70 -0
- pythinker_code/utils/io.py +27 -0
- pythinker_code/utils/logging.py +146 -0
- pythinker_code/utils/media_tags.py +29 -0
- pythinker_code/utils/message.py +24 -0
- pythinker_code/utils/path.py +199 -0
- pythinker_code/utils/proctitle.py +33 -0
- pythinker_code/utils/proxy.py +31 -0
- pythinker_code/utils/pyinstaller.py +45 -0
- pythinker_code/utils/rich/__init__.py +33 -0
- pythinker_code/utils/rich/columns.py +99 -0
- pythinker_code/utils/rich/diff_render.py +481 -0
- pythinker_code/utils/rich/markdown.py +900 -0
- pythinker_code/utils/rich/markdown_sample.md +108 -0
- pythinker_code/utils/rich/markdown_sample_short.md +2 -0
- pythinker_code/utils/rich/syntax.py +114 -0
- pythinker_code/utils/sensitive.py +54 -0
- pythinker_code/utils/server.py +121 -0
- pythinker_code/utils/signals.py +43 -0
- pythinker_code/utils/slashcmd.py +124 -0
- pythinker_code/utils/string.py +41 -0
- pythinker_code/utils/subprocess_env.py +73 -0
- pythinker_code/utils/term.py +168 -0
- pythinker_code/utils/typing.py +20 -0
- pythinker_code/vis/__init__.py +0 -0
- pythinker_code/vis/api/__init__.py +5 -0
- pythinker_code/vis/api/sessions.py +687 -0
- pythinker_code/vis/api/statistics.py +209 -0
- pythinker_code/vis/api/system.py +19 -0
- pythinker_code/vis/app.py +175 -0
- pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-D2MTYyJz.js +1 -0
- pythinker_code/vis/static/assets/index-CezafTt_.js +185 -0
- pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
- pythinker_code/vis/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- pythinker_code/vis/static/index.html +17 -0
- pythinker_code/web/__init__.py +5 -0
- pythinker_code/web/api/__init__.py +15 -0
- pythinker_code/web/api/config.py +208 -0
- pythinker_code/web/api/open_in.py +199 -0
- pythinker_code/web/api/sessions.py +1225 -0
- pythinker_code/web/app.py +451 -0
- pythinker_code/web/auth.py +191 -0
- pythinker_code/web/models.py +98 -0
- pythinker_code/web/runner/__init__.py +5 -0
- pythinker_code/web/runner/messages.py +57 -0
- pythinker_code/web/runner/process.py +754 -0
- pythinker_code/web/runner/worker.py +97 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- pythinker_code/web/static/assets/_baseUniq--dyU3g5v.js +1 -0
- pythinker_code/web/static/assets/abap-BdImnpbu.js +1 -0
- pythinker_code/web/static/assets/actionscript-3-CfeIJUat.js +1 -0
- pythinker_code/web/static/assets/ada-bCR0ucgS.js +1 -0
- pythinker_code/web/static/assets/andromeeda-C-Jbm3Hp.js +1 -0
- pythinker_code/web/static/assets/angular-html-CU67Zn6k.js +1 -0
- pythinker_code/web/static/assets/angular-ts-BwZT4LLn.js +1 -0
- pythinker_code/web/static/assets/apache-Pmp26Uib.js +1 -0
- pythinker_code/web/static/assets/apex-D8_7TLub.js +1 -0
- pythinker_code/web/static/assets/apl-dKokRX4l.js +1 -0
- pythinker_code/web/static/assets/applescript-Co6uUVPk.js +1 -0
- pythinker_code/web/static/assets/ara-BRHolxvo.js +1 -0
- pythinker_code/web/static/assets/arc-DkMjLpYa.js +1 -0
- pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-CHWVaGo9.js +36 -0
- pythinker_code/web/static/assets/asciidoc-Dv7Oe6Be.js +1 -0
- pythinker_code/web/static/assets/asm-D_Q5rh1f.js +1 -0
- pythinker_code/web/static/assets/astro-CbQHKStN.js +1 -0
- pythinker_code/web/static/assets/aurora-x-D-2ljcwZ.js +1 -0
- pythinker_code/web/static/assets/awk-DMzUqQB5.js +1 -0
- pythinker_code/web/static/assets/ayu-dark-CmMr59Fi.js +1 -0
- pythinker_code/web/static/assets/ballerina-BFfxhgS-.js +1 -0
- pythinker_code/web/static/assets/bat-BkioyH1T.js +1 -0
- pythinker_code/web/static/assets/beancount-k_qm7-4y.js +1 -0
- pythinker_code/web/static/assets/berry-uYugtg8r.js +1 -0
- pythinker_code/web/static/assets/bibtex-CHM0blh-.js +1 -0
- pythinker_code/web/static/assets/bicep-Bmn6On1c.js +1 -0
- pythinker_code/web/static/assets/blade-D4QpJJKB.js +1 -0
- pythinker_code/web/static/assets/blockDiagram-VD42YOAC-DzdKe497.js +122 -0
- pythinker_code/web/static/assets/bsl-BO_Y6i37.js +1 -0
- pythinker_code/web/static/assets/c-BIGW1oBm.js +1 -0
- pythinker_code/web/static/assets/c3-VCDPK7BO.js +1 -0
- pythinker_code/web/static/assets/c4Diagram-YG6GDRKO-D84Blotg.js +10 -0
- pythinker_code/web/static/assets/cadence-Bv_4Rxtq.js +1 -0
- pythinker_code/web/static/assets/cairo-KRGpt6FW.js +1 -0
- pythinker_code/web/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- pythinker_code/web/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- pythinker_code/web/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- pythinker_code/web/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- pythinker_code/web/static/assets/channel-CllSjjdl.js +1 -0
- pythinker_code/web/static/assets/chunk-4BX2VUAB-C9w8wleE.js +1 -0
- pythinker_code/web/static/assets/chunk-55IACEB6-YlYJ8HnF.js +1 -0
- pythinker_code/web/static/assets/chunk-B4BG7PRW-Bwtz_AHU.js +165 -0
- pythinker_code/web/static/assets/chunk-DI55MBZ5-BbEHkl8h.js +220 -0
- pythinker_code/web/static/assets/chunk-FMBD7UC4-BKPbvjLC.js +15 -0
- pythinker_code/web/static/assets/chunk-QN33PNHL-D73dQvKf.js +1 -0
- pythinker_code/web/static/assets/chunk-QZHKN3VN-zGiLKes_.js +1 -0
- pythinker_code/web/static/assets/chunk-TZMSLE5B-LHJCi2fy.js +1 -0
- pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
- pythinker_code/web/static/assets/classDiagram-2ON5EDUG-vX27iZwa.js +1 -0
- pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-vX27iZwa.js +1 -0
- pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
- pythinker_code/web/static/assets/clone-DYBkaPm2.js +1 -0
- pythinker_code/web/static/assets/cmake-D1j8_8rp.js +1 -0
- pythinker_code/web/static/assets/cobol-nwyudZeR.js +1 -0
- pythinker_code/web/static/assets/code-block-IT6T5CEO-NtKViZGl.js +2 -0
- pythinker_code/web/static/assets/codeowners-Bp6g37R7.js +1 -0
- pythinker_code/web/static/assets/codeql-DsOJ9woJ.js +1 -0
- pythinker_code/web/static/assets/coffee-Ch7k5sss.js +1 -0
- pythinker_code/web/static/assets/common-lisp-Cg-RD9OK.js +1 -0
- pythinker_code/web/static/assets/coq-DkFqJrB1.js +1 -0
- pythinker_code/web/static/assets/cose-bilkent-S5V4N54A-DialjZpd.js +1 -0
- pythinker_code/web/static/assets/cpp-CofmeUqb.js +1 -0
- pythinker_code/web/static/assets/crystal-tKQVLTB8.js +1 -0
- pythinker_code/web/static/assets/csharp-K5feNrxe.js +1 -0
- pythinker_code/web/static/assets/css-DPfMkruS.js +1 -0
- pythinker_code/web/static/assets/csv-fuZLfV_i.js +1 -0
- pythinker_code/web/static/assets/cue-D82EKSYY.js +1 -0
- pythinker_code/web/static/assets/cypher-COkxafJQ.js +1 -0
- pythinker_code/web/static/assets/cytoscape.esm-C_Fzpdck.js +321 -0
- pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
- pythinker_code/web/static/assets/dagre-6UL2VRFP-DfuvkZZ7.js +4 -0
- pythinker_code/web/static/assets/dark-plus-C3mMm8J8.js +1 -0
- pythinker_code/web/static/assets/dart-CF10PKvl.js +1 -0
- pythinker_code/web/static/assets/dax-CEL-wOlO.js +1 -0
- pythinker_code/web/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- pythinker_code/web/static/assets/desktop-BmXAJ9_W.js +1 -0
- pythinker_code/web/static/assets/diagram-PSM6KHXK-DLGctX3v.js +24 -0
- pythinker_code/web/static/assets/diagram-QEK2KX5R-DnxN6S0F.js +43 -0
- pythinker_code/web/static/assets/diagram-S2PKOQOG-Caq_Set9.js +24 -0
- pythinker_code/web/static/assets/diff-D97Zzqfu.js +1 -0
- pythinker_code/web/static/assets/docker-BcOcwvcX.js +1 -0
- pythinker_code/web/static/assets/dotenv-Da5cRb03.js +1 -0
- pythinker_code/web/static/assets/dracula-BzJJZx-M.js +1 -0
- pythinker_code/web/static/assets/dracula-soft-BXkSAIEj.js +1 -0
- pythinker_code/web/static/assets/dream-maker-BtqSS_iP.js +1 -0
- pythinker_code/web/static/assets/edge-BkV0erSs.js +1 -0
- pythinker_code/web/static/assets/elixir-CDX3lj18.js +1 -0
- pythinker_code/web/static/assets/elm-DbKCFpqz.js +1 -0
- pythinker_code/web/static/assets/emacs-lisp-C9XAeP06.js +1 -0
- pythinker_code/web/static/assets/erDiagram-Q2GNP2WA-BgTfALoK.js +60 -0
- pythinker_code/web/static/assets/erb-BOJIQeun.js +1 -0
- pythinker_code/web/static/assets/erlang-DsQrWhSR.js +1 -0
- pythinker_code/web/static/assets/everforest-dark-BgDCqdQA.js +1 -0
- pythinker_code/web/static/assets/everforest-light-C8M2exoo.js +1 -0
- pythinker_code/web/static/assets/fennel-BYunw83y.js +1 -0
- pythinker_code/web/static/assets/fish-BvzEVeQv.js +1 -0
- pythinker_code/web/static/assets/flowDiagram-NV44I4VS-QjW_fnGi.js +162 -0
- pythinker_code/web/static/assets/fluent-C4IJs8-o.js +1 -0
- pythinker_code/web/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- pythinker_code/web/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
- pythinker_code/web/static/assets/fsharp-CXgrBDvD.js +1 -0
- pythinker_code/web/static/assets/ganttDiagram-JELNMOA3-fqi8JFof.js +267 -0
- pythinker_code/web/static/assets/gdresource-B7Tvp0Sc.js +1 -0
- pythinker_code/web/static/assets/gdscript-DTMYz4Jt.js +1 -0
- pythinker_code/web/static/assets/gdshader-DkwncUOv.js +1 -0
- pythinker_code/web/static/assets/genie-D0YGMca9.js +1 -0
- pythinker_code/web/static/assets/gherkin-DyxjwDmM.js +1 -0
- pythinker_code/web/static/assets/git-commit-F4YmCXRG.js +1 -0
- pythinker_code/web/static/assets/git-rebase-r7XF79zn.js +1 -0
- pythinker_code/web/static/assets/gitGraphDiagram-NY62KEGX-i7o6VQ8x.js +65 -0
- pythinker_code/web/static/assets/github-dark-DHJKELXO.js +1 -0
- pythinker_code/web/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
- pythinker_code/web/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- pythinker_code/web/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- pythinker_code/web/static/assets/github-light-DAi9KRSo.js +1 -0
- pythinker_code/web/static/assets/github-light-default-D7oLnXFd.js +1 -0
- pythinker_code/web/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- pythinker_code/web/static/assets/gleam-BspZqrRM.js +1 -0
- pythinker_code/web/static/assets/glimmer-js-Rg0-pVw9.js +1 -0
- pythinker_code/web/static/assets/glimmer-ts-U6CK756n.js +1 -0
- pythinker_code/web/static/assets/glsl-DplSGwfg.js +1 -0
- pythinker_code/web/static/assets/gn-n2N0HUVH.js +1 -0
- pythinker_code/web/static/assets/gnuplot-DdkO51Og.js +1 -0
- pythinker_code/web/static/assets/go-Dn2_MT6a.js +1 -0
- pythinker_code/web/static/assets/graph-C0vZK2pT.js +1 -0
- pythinker_code/web/static/assets/graphql-ChdNCCLP.js +1 -0
- pythinker_code/web/static/assets/groovy-gcz8RCvz.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- pythinker_code/web/static/assets/hack-CaT9iCJl.js +1 -0
- pythinker_code/web/static/assets/haml-B8DHNrY2.js +1 -0
- pythinker_code/web/static/assets/handlebars-BL8al0AC.js +1 -0
- pythinker_code/web/static/assets/haskell-Df6bDoY_.js +1 -0
- pythinker_code/web/static/assets/haxe-CzTSHFRz.js +1 -0
- pythinker_code/web/static/assets/hcl-BWvSN4gD.js +1 -0
- pythinker_code/web/static/assets/hjson-D5-asLiD.js +1 -0
- pythinker_code/web/static/assets/hlsl-D3lLCCz7.js +1 -0
- pythinker_code/web/static/assets/houston-DnULxvSX.js +1 -0
- pythinker_code/web/static/assets/html-GMplVEZG.js +1 -0
- pythinker_code/web/static/assets/html-derivative-BFtXZ54Q.js +1 -0
- pythinker_code/web/static/assets/http-jrhK8wxY.js +1 -0
- pythinker_code/web/static/assets/hurl-irOxFIW8.js +1 -0
- pythinker_code/web/static/assets/hxml-Bvhsp5Yf.js +1 -0
- pythinker_code/web/static/assets/hy-DFXneXwc.js +1 -0
- pythinker_code/web/static/assets/imba-DGztddWO.js +1 -0
- pythinker_code/web/static/assets/index-BYCCk6-K.js +153 -0
- pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
- pythinker_code/web/static/assets/index-Cpy4G3uJ.js +2 -0
- pythinker_code/web/static/assets/index-CzV_vCfu.css +1 -0
- pythinker_code/web/static/assets/index-DI2oedCt.js +19 -0
- pythinker_code/web/static/assets/index-DdIkp80K.js +5 -0
- pythinker_code/web/static/assets/infoDiagram-WHAUD3N6-BMPpeZSM.js +2 -0
- pythinker_code/web/static/assets/ini-BEwlwnbL.js +1 -0
- pythinker_code/web/static/assets/init-Gi6I4Gst.js +1 -0
- pythinker_code/web/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- pythinker_code/web/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- pythinker_code/web/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- pythinker_code/web/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- pythinker_code/web/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- pythinker_code/web/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- pythinker_code/web/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- pythinker_code/web/static/assets/java-CylS5w8V.js +1 -0
- pythinker_code/web/static/assets/javascript-wDzz0qaB.js +1 -0
- pythinker_code/web/static/assets/jinja-4LBKfQ-Z.js +1 -0
- pythinker_code/web/static/assets/jison-wvAkD_A8.js +1 -0
- pythinker_code/web/static/assets/journeyDiagram-XKPGCS4Q-DAM7gngo.js +139 -0
- pythinker_code/web/static/assets/json-Cp-IABpG.js +1 -0
- pythinker_code/web/static/assets/json5-C9tS-k6U.js +1 -0
- pythinker_code/web/static/assets/jsonc-Des-eS-w.js +1 -0
- pythinker_code/web/static/assets/jsonl-DcaNXYhu.js +1 -0
- pythinker_code/web/static/assets/jsonnet-DFQXde-d.js +1 -0
- pythinker_code/web/static/assets/jssm-C2t-YnRu.js +1 -0
- pythinker_code/web/static/assets/jsx-g9-lgVsj.js +1 -0
- pythinker_code/web/static/assets/julia-CxzCAyBv.js +1 -0
- pythinker_code/web/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- pythinker_code/web/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- pythinker_code/web/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
- pythinker_code/web/static/assets/kanban-definition-3W4ZIXB7-ChpBpV0k.js +89 -0
- pythinker_code/web/static/assets/katex-D2lIc1rk.css +1 -0
- pythinker_code/web/static/assets/kdl-DV7GczEv.js +1 -0
- pythinker_code/web/static/assets/kotlin-BdnUsdx6.js +1 -0
- pythinker_code/web/static/assets/kusto-DZf3V79B.js +1 -0
- pythinker_code/web/static/assets/laserwave-DUszq2jm.js +1 -0
- pythinker_code/web/static/assets/latex-B4uzh10-.js +1 -0
- pythinker_code/web/static/assets/layout-C3Jp1gKO.js +1 -0
- pythinker_code/web/static/assets/lean-BZvkOJ9d.js +1 -0
- pythinker_code/web/static/assets/less-B1dDrJ26.js +1 -0
- pythinker_code/web/static/assets/light-plus-B7mTdjB0.js +1 -0
- pythinker_code/web/static/assets/linear-BGHtL1N4.js +1 -0
- pythinker_code/web/static/assets/liquid-DYVedYrR.js +1 -0
- pythinker_code/web/static/assets/llvm-BtvRca6l.js +1 -0
- pythinker_code/web/static/assets/log-2UxHyX5q.js +1 -0
- pythinker_code/web/static/assets/logo-BtOb2qkB.js +1 -0
- pythinker_code/web/static/assets/lua-BbnMAYS6.js +1 -0
- pythinker_code/web/static/assets/luau-C-HG3fhB.js +1 -0
- pythinker_code/web/static/assets/make-CHLpvVh8.js +1 -0
- pythinker_code/web/static/assets/markdown-Cvjx9yec.js +1 -0
- pythinker_code/web/static/assets/marko-DZsq8hO1.js +1 -0
- pythinker_code/web/static/assets/material-theme-D5KoaKCx.js +1 -0
- pythinker_code/web/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
- pythinker_code/web/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- pythinker_code/web/static/assets/material-theme-ocean-CyktbL80.js +1 -0
- pythinker_code/web/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- pythinker_code/web/static/assets/matlab-D7o27uSR.js +1 -0
- pythinker_code/web/static/assets/mdc-DUICxH0z.js +1 -0
- pythinker_code/web/static/assets/mdx-Cmh6b_Ma.js +1 -0
- pythinker_code/web/static/assets/mermaid-VLURNSYL-B2P5VJ9v.css +1 -0
- pythinker_code/web/static/assets/mermaid-VLURNSYL-C_HW6koB.js +465 -0
- pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
- pythinker_code/web/static/assets/mermaid.core-CnT4VrPC.js +191 -0
- pythinker_code/web/static/assets/min-Dn5VRVmX.js +1 -0
- pythinker_code/web/static/assets/min-dark-CafNBF8u.js +1 -0
- pythinker_code/web/static/assets/min-light-CTRr51gU.js +1 -0
- pythinker_code/web/static/assets/mindmap-definition-VGOIOE7T-x8EwhfIt.js +68 -0
- pythinker_code/web/static/assets/mipsasm-CKIfxQSi.js +1 -0
- pythinker_code/web/static/assets/mojo-B93PlW-d.js +1 -0
- pythinker_code/web/static/assets/monokai-D4h5O-jR.js +1 -0
- pythinker_code/web/static/assets/moonbit-Ba13S78F.js +1 -0
- pythinker_code/web/static/assets/move-Bu9oaDYs.js +1 -0
- pythinker_code/web/static/assets/narrat-DRg8JJMk.js +1 -0
- pythinker_code/web/static/assets/nextflow-BrzmwbiE.js +1 -0
- pythinker_code/web/static/assets/nginx-DknmC5AR.js +1 -0
- pythinker_code/web/static/assets/night-owl-C39BiMTA.js +1 -0
- pythinker_code/web/static/assets/nim-CVrawwO9.js +1 -0
- pythinker_code/web/static/assets/nix-CwoSXNpI.js +1 -0
- pythinker_code/web/static/assets/nord-Ddv68eIx.js +1 -0
- pythinker_code/web/static/assets/nushell-C-sUppwS.js +1 -0
- pythinker_code/web/static/assets/objective-c-DXmwc3jG.js +1 -0
- pythinker_code/web/static/assets/objective-cpp-CLxacb5B.js +1 -0
- pythinker_code/web/static/assets/ocaml-C0hk2d4L.js +1 -0
- pythinker_code/web/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- pythinker_code/web/static/assets/one-light-PoHY5YXO.js +1 -0
- pythinker_code/web/static/assets/openscad-C4EeE6gA.js +1 -0
- pythinker_code/web/static/assets/ordinal-Cboi1Yqb.js +1 -0
- pythinker_code/web/static/assets/pascal-D93ZcfNL.js +1 -0
- pythinker_code/web/static/assets/perl-C0TMdlhV.js +1 -0
- pythinker_code/web/static/assets/php-CDn_0X-4.js +1 -0
- pythinker_code/web/static/assets/pieDiagram-ADFJNKIX-DgxBKGz2.js +30 -0
- pythinker_code/web/static/assets/pkl-u5AG7uiY.js +1 -0
- pythinker_code/web/static/assets/plastic-3e1v2bzS.js +1 -0
- pythinker_code/web/static/assets/plsql-ChMvpjG-.js +1 -0
- pythinker_code/web/static/assets/po-BTJTHyun.js +1 -0
- pythinker_code/web/static/assets/poimandres-CS3Unz2-.js +1 -0
- pythinker_code/web/static/assets/polar-C0HS_06l.js +1 -0
- pythinker_code/web/static/assets/postcss-CXtECtnM.js +1 -0
- pythinker_code/web/static/assets/powerquery-CEu0bR-o.js +1 -0
- pythinker_code/web/static/assets/powershell-Dpen1YoG.js +1 -0
- pythinker_code/web/static/assets/prisma-Dd19v3D-.js +1 -0
- pythinker_code/web/static/assets/prolog-CbFg5uaA.js +1 -0
- pythinker_code/web/static/assets/proto-C7zT0LnQ.js +1 -0
- pythinker_code/web/static/assets/pug-CGlum2m_.js +1 -0
- pythinker_code/web/static/assets/puppet-BMWR74SV.js +1 -0
- pythinker_code/web/static/assets/purescript-CklMAg4u.js +1 -0
- pythinker_code/web/static/assets/python-B6aJPvgy.js +1 -0
- pythinker_code/web/static/assets/qml-3beO22l8.js +1 -0
- pythinker_code/web/static/assets/qmldir-C8lEn-DE.js +1 -0
- pythinker_code/web/static/assets/qss-IeuSbFQv.js +1 -0
- pythinker_code/web/static/assets/quadrantDiagram-AYHSOK5B-DSpe_dqk.js +7 -0
- pythinker_code/web/static/assets/r-Dspwwk_N.js +1 -0
- pythinker_code/web/static/assets/racket-BqYA7rlc.js +1 -0
- pythinker_code/web/static/assets/raku-DXvB9xmW.js +1 -0
- pythinker_code/web/static/assets/razor-C1TweQQi.js +1 -0
- pythinker_code/web/static/assets/red-bN70gL4F.js +1 -0
- pythinker_code/web/static/assets/reg-C-SQnVFl.js +1 -0
- pythinker_code/web/static/assets/regexp-CDVJQ6XC.js +1 -0
- pythinker_code/web/static/assets/rel-C3B-1QV4.js +1 -0
- pythinker_code/web/static/assets/requirementDiagram-UZGBJVZJ-8o9hozL-.js +64 -0
- pythinker_code/web/static/assets/riscv-BM1_JUlF.js +1 -0
- pythinker_code/web/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- pythinker_code/web/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- pythinker_code/web/static/assets/rose-pine-qdsjHGoJ.js +1 -0
- pythinker_code/web/static/assets/rosmsg-BJDFO7_C.js +1 -0
- pythinker_code/web/static/assets/rst-B0xPkSld.js +1 -0
- pythinker_code/web/static/assets/ruby-BvKwtOVI.js +1 -0
- pythinker_code/web/static/assets/rust-B1yitclQ.js +1 -0
- pythinker_code/web/static/assets/sankeyDiagram-TZEHDZUN-BLOSUixH.js +10 -0
- pythinker_code/web/static/assets/sas-cz2c8ADy.js +1 -0
- pythinker_code/web/static/assets/sass-Cj5Yp3dK.js +1 -0
- pythinker_code/web/static/assets/scala-C151Ov-r.js +1 -0
- pythinker_code/web/static/assets/scheme-C98Dy4si.js +1 -0
- pythinker_code/web/static/assets/scss-OYdSNvt2.js +1 -0
- pythinker_code/web/static/assets/sdbl-DVxCFoDh.js +1 -0
- pythinker_code/web/static/assets/sequenceDiagram-WL72ISMW-6F2G8JTU.js +145 -0
- pythinker_code/web/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
- pythinker_code/web/static/assets/shellscript-Yzrsuije.js +1 -0
- pythinker_code/web/static/assets/shellsession-BADoaaVG.js +1 -0
- pythinker_code/web/static/assets/slack-dark-BthQWCQV.js +1 -0
- pythinker_code/web/static/assets/slack-ochin-DqwNpetd.js +1 -0
- pythinker_code/web/static/assets/smalltalk-BERRCDM3.js +1 -0
- pythinker_code/web/static/assets/snazzy-light-Bw305WKR.js +1 -0
- pythinker_code/web/static/assets/solarized-dark-DXbdFlpD.js +1 -0
- pythinker_code/web/static/assets/solarized-light-L9t79GZl.js +1 -0
- pythinker_code/web/static/assets/solidity-rGO070M0.js +1 -0
- pythinker_code/web/static/assets/soy-Brmx7dQM.js +1 -0
- pythinker_code/web/static/assets/sparql-rVzFXLq3.js +1 -0
- pythinker_code/web/static/assets/splunk-BtCnVYZw.js +1 -0
- pythinker_code/web/static/assets/sql-BLtJtn59.js +1 -0
- pythinker_code/web/static/assets/ssh-config-_ykCGR6B.js +1 -0
- pythinker_code/web/static/assets/stata-BH5u7GGu.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-FKZM4ZOC-DP8xP0iJ.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-1l6-EZNX.js +1 -0
- pythinker_code/web/static/assets/stylus-BEDo0Tqx.js +1 -0
- pythinker_code/web/static/assets/svelte-zxCyuUbr.js +1 -0
- pythinker_code/web/static/assets/swift-Dg5xB15N.js +1 -0
- pythinker_code/web/static/assets/synthwave-84-CbfX1IO0.js +1 -0
- pythinker_code/web/static/assets/system-verilog-CnnmHF94.js +1 -0
- pythinker_code/web/static/assets/systemd-4A_iFExJ.js +1 -0
- pythinker_code/web/static/assets/talonscript-CkByrt1z.js +1 -0
- pythinker_code/web/static/assets/tasl-QIJgUcNo.js +1 -0
- pythinker_code/web/static/assets/tcl-dwOrl1Do.js +1 -0
- pythinker_code/web/static/assets/templ-W15q3VgB.js +1 -0
- pythinker_code/web/static/assets/terraform-BETggiCN.js +1 -0
- pythinker_code/web/static/assets/tex-CvyZ59Mk.js +1 -0
- pythinker_code/web/static/assets/timeline-definition-IT6M3QCI-DMgruDfK.js +61 -0
- pythinker_code/web/static/assets/tokyo-night-hegEt444.js +1 -0
- pythinker_code/web/static/assets/toml-vGWfd6FD.js +1 -0
- pythinker_code/web/static/assets/treemap-KMMF4GRG-Bo9ZkrAK.js +128 -0
- pythinker_code/web/static/assets/ts-tags-zn1MmPIZ.js +1 -0
- pythinker_code/web/static/assets/tsv-B_m7g4N7.js +1 -0
- pythinker_code/web/static/assets/tsx-COt5Ahok.js +1 -0
- pythinker_code/web/static/assets/turtle-BsS91CYL.js +1 -0
- pythinker_code/web/static/assets/twig-CO9l9SDP.js +1 -0
- pythinker_code/web/static/assets/typescript-BPQ3VLAy.js +1 -0
- pythinker_code/web/static/assets/typespec-BGHnOYBU.js +1 -0
- pythinker_code/web/static/assets/typst-DHCkPAjA.js +1 -0
- pythinker_code/web/static/assets/v-BcVCzyr7.js +1 -0
- pythinker_code/web/static/assets/vala-CsfeWuGM.js +1 -0
- pythinker_code/web/static/assets/vb-D17OF-Vu.js +1 -0
- pythinker_code/web/static/assets/verilog-BQ8w6xss.js +1 -0
- pythinker_code/web/static/assets/vesper-DU1UobuO.js +1 -0
- pythinker_code/web/static/assets/vhdl-CeAyd5Ju.js +1 -0
- pythinker_code/web/static/assets/viml-CJc9bBzg.js +1 -0
- pythinker_code/web/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
- pythinker_code/web/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
- pythinker_code/web/static/assets/vitesse-light-CVO1_9PV.js +1 -0
- pythinker_code/web/static/assets/vue-DN_0RTcg.js +1 -0
- pythinker_code/web/static/assets/vue-html-AaS7Mt5G.js +1 -0
- pythinker_code/web/static/assets/vue-vine-CQOfvN7w.js +1 -0
- pythinker_code/web/static/assets/vyper-CDx5xZoG.js +1 -0
- pythinker_code/web/static/assets/wasm-CG6Dc4jp.js +1 -0
- pythinker_code/web/static/assets/wasm-MzD3tlZU.js +1 -0
- pythinker_code/web/static/assets/wenyan-BV7otONQ.js +1 -0
- pythinker_code/web/static/assets/wgsl-Dx-B1_4e.js +1 -0
- pythinker_code/web/static/assets/wikitext-BhOHFoWU.js +1 -0
- pythinker_code/web/static/assets/wit-5i3qLPDT.js +1 -0
- pythinker_code/web/static/assets/wolfram-lXgVvXCa.js +1 -0
- pythinker_code/web/static/assets/xml-sdJ4AIDG.js +1 -0
- pythinker_code/web/static/assets/xsl-CtQFsRM5.js +1 -0
- pythinker_code/web/static/assets/xychartDiagram-PRI3JC2R-GeF2johi.js +7 -0
- pythinker_code/web/static/assets/yaml-Buea-lGh.js +1 -0
- pythinker_code/web/static/assets/zenscript-DVFEvuxE.js +1 -0
- pythinker_code/web/static/assets/zig-VOosw3JB.js +1 -0
- pythinker_code/web/static/brand/apple-touch-icon.png +0 -0
- pythinker_code/web/static/brand/arctecture.webp +0 -0
- pythinker_code/web/static/brand/bimi-logo.svg +46 -0
- pythinker_code/web/static/brand/favicon.ico +0 -0
- pythinker_code/web/static/brand/fonts/dm-sans-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/dm-sans-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-sans-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-sans-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-serif-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-serif-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/roboto-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/roboto-latin.woff2 +0 -0
- pythinker_code/web/static/brand/icon-192.png +0 -0
- pythinker_code/web/static/brand/icon-512.png +0 -0
- pythinker_code/web/static/brand/icon.svg +1 -0
- pythinker_code/web/static/brand/logo.png +0 -0
- pythinker_code/web/static/brand/pythinker_animated.svg +79 -0
- pythinker_code/web/static/brand/robots.txt +4 -0
- pythinker_code/web/static/index.html +15 -0
- pythinker_code/web/static/logo.png +0 -0
- pythinker_code/web/store/__init__.py +1 -0
- pythinker_code/web/store/sessions.py +432 -0
- pythinker_code/wire/__init__.py +148 -0
- pythinker_code/wire/file.py +151 -0
- pythinker_code/wire/jsonrpc.py +263 -0
- pythinker_code/wire/protocol.py +2 -0
- pythinker_code/wire/root_hub.py +27 -0
- pythinker_code/wire/serde.py +26 -0
- pythinker_code/wire/server.py +1069 -0
- pythinker_code/wire/types.py +698 -0
- pythinker_code-2.0.0.dist-info/METADATA +660 -0
- pythinker_code-2.0.0.dist-info/RECORD +731 -0
- pythinker_code-2.0.0.dist-info/WHEEL +4 -0
- pythinker_code-2.0.0.dist-info/entry_points.txt +4 -0
- pythinker_code-2.0.0.dist-info/licenses/LICENSE +202 -0
- pythinker_code-2.0.0.dist-info/licenses/NOTICE +14 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
import os
|
|
6
|
+
import platform
|
|
7
|
+
import re
|
|
8
|
+
import shlex
|
|
9
|
+
import shutil
|
|
10
|
+
import stat
|
|
11
|
+
import subprocess
|
|
12
|
+
import tarfile
|
|
13
|
+
import tempfile
|
|
14
|
+
from enum import Enum, auto
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import aiohttp
|
|
18
|
+
|
|
19
|
+
from pythinker_code.share import get_share_dir
|
|
20
|
+
from pythinker_code.ui.shell.console import console
|
|
21
|
+
from pythinker_code.utils.aiohttp import new_client_session
|
|
22
|
+
from pythinker_code.utils.logging import logger
|
|
23
|
+
|
|
24
|
+
BASE_URL = "https://cdn.pythinker.com/binaries/pythinker-code"
|
|
25
|
+
LATEST_VERSION_URL = f"{BASE_URL}/latest"
|
|
26
|
+
INSTALL_DIR = Path.home() / ".local" / "bin"
|
|
27
|
+
|
|
28
|
+
# Upgrade command shown in toast notifications. Can be overridden by wrappers
|
|
29
|
+
UPGRADE_COMMAND = "uv tool upgrade pythinker-code"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class UpdateResult(Enum):
|
|
33
|
+
UPDATE_AVAILABLE = auto()
|
|
34
|
+
UPDATED = auto()
|
|
35
|
+
UP_TO_DATE = auto()
|
|
36
|
+
FAILED = auto()
|
|
37
|
+
UNSUPPORTED = auto()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_UPDATE_LOCK = asyncio.Lock()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def semver_tuple(version: str) -> tuple[int, int, int]:
|
|
44
|
+
v = version.strip()
|
|
45
|
+
if v.startswith("v"):
|
|
46
|
+
v = v[1:]
|
|
47
|
+
match = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?", v)
|
|
48
|
+
if not match:
|
|
49
|
+
return (0, 0, 0)
|
|
50
|
+
major = int(match.group(1))
|
|
51
|
+
minor = int(match.group(2))
|
|
52
|
+
patch = int(match.group(3) or 0)
|
|
53
|
+
return (major, minor, patch)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _detect_target() -> str | None:
|
|
57
|
+
sys_name = platform.system()
|
|
58
|
+
mach = platform.machine()
|
|
59
|
+
if mach in ("x86_64", "amd64", "AMD64"):
|
|
60
|
+
arch = "x86_64"
|
|
61
|
+
elif mach in ("arm64", "aarch64"):
|
|
62
|
+
arch = "aarch64"
|
|
63
|
+
else:
|
|
64
|
+
logger.error("Unsupported architecture: {mach}", mach=mach)
|
|
65
|
+
return None
|
|
66
|
+
if sys_name == "Darwin":
|
|
67
|
+
os_name = "apple-darwin"
|
|
68
|
+
elif sys_name == "Linux":
|
|
69
|
+
os_name = "unknown-linux-gnu"
|
|
70
|
+
else:
|
|
71
|
+
logger.error("Unsupported OS: {sys_name}", sys_name=sys_name)
|
|
72
|
+
return None
|
|
73
|
+
return f"{arch}-{os_name}"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def _get_latest_version(session: aiohttp.ClientSession) -> str | None:
|
|
77
|
+
try:
|
|
78
|
+
async with session.get(LATEST_VERSION_URL) as resp:
|
|
79
|
+
resp.raise_for_status()
|
|
80
|
+
data = await resp.text()
|
|
81
|
+
return data.strip()
|
|
82
|
+
except (TimeoutError, aiohttp.ClientError):
|
|
83
|
+
logger.exception("Failed to get latest version:")
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def do_update(*, print: bool = True, check_only: bool = False) -> UpdateResult:
|
|
88
|
+
async with _UPDATE_LOCK:
|
|
89
|
+
return await _do_update(print=print, check_only=check_only)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
LATEST_VERSION_FILE = get_share_dir() / "latest_version.txt"
|
|
93
|
+
SKIPPED_VERSION_FILE = get_share_dir() / "skipped_version.txt"
|
|
94
|
+
CHANGELOG_URL_EN = (
|
|
95
|
+
"https://mohamed-elkholy95.github.io/Pythinker-Code/en/release-notes/changelog.html"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _read_key() -> str:
|
|
100
|
+
"""Read a single character from stdin in raw terminal mode."""
|
|
101
|
+
import sys
|
|
102
|
+
|
|
103
|
+
if sys.platform == "win32":
|
|
104
|
+
import msvcrt
|
|
105
|
+
|
|
106
|
+
return msvcrt.getwch()
|
|
107
|
+
else:
|
|
108
|
+
import termios
|
|
109
|
+
import tty
|
|
110
|
+
|
|
111
|
+
fd = sys.stdin.fileno()
|
|
112
|
+
old = termios.tcgetattr(fd)
|
|
113
|
+
try:
|
|
114
|
+
tty.setraw(fd)
|
|
115
|
+
return sys.stdin.read(1)
|
|
116
|
+
finally:
|
|
117
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def check_update_gate() -> None:
|
|
121
|
+
"""Block interactive shell startup if a newer version is cached locally."""
|
|
122
|
+
import sys
|
|
123
|
+
|
|
124
|
+
from pythinker_code.constant import VERSION as current_version
|
|
125
|
+
from pythinker_code.utils.envvar import get_env_bool
|
|
126
|
+
|
|
127
|
+
if get_env_bool("PYTHINKER_CLI_NO_AUTO_UPDATE"):
|
|
128
|
+
return
|
|
129
|
+
if not sys.stdin.isatty() or not sys.stdout.isatty():
|
|
130
|
+
return
|
|
131
|
+
if not LATEST_VERSION_FILE.exists():
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
latest_version = LATEST_VERSION_FILE.read_text(encoding="utf-8").strip()
|
|
136
|
+
except OSError:
|
|
137
|
+
return
|
|
138
|
+
if semver_tuple(latest_version) <= semver_tuple(current_version):
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
if SKIPPED_VERSION_FILE.exists():
|
|
142
|
+
try:
|
|
143
|
+
skipped = SKIPPED_VERSION_FILE.read_text(encoding="utf-8").strip()
|
|
144
|
+
except OSError:
|
|
145
|
+
skipped = ""
|
|
146
|
+
if skipped == latest_version:
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
_run_update_gate(current_version, latest_version)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _run_update_gate(current_version: str, latest_version: str) -> None:
|
|
153
|
+
"""Display the blocking update UI and handle user key input."""
|
|
154
|
+
import sys
|
|
155
|
+
|
|
156
|
+
from rich.panel import Panel
|
|
157
|
+
from rich.rule import Rule
|
|
158
|
+
from rich.text import Text
|
|
159
|
+
|
|
160
|
+
body = Text.assemble(
|
|
161
|
+
(" Current version ", ""),
|
|
162
|
+
(current_version + "\n", ""),
|
|
163
|
+
(" Latest version ", ""),
|
|
164
|
+
(latest_version + "\n\n", "bold green"),
|
|
165
|
+
(" What's new:\n", ""),
|
|
166
|
+
(" · ", ""),
|
|
167
|
+
(CHANGELOG_URL_EN + "\n", "dodger_blue1"),
|
|
168
|
+
)
|
|
169
|
+
console.print()
|
|
170
|
+
console.print(
|
|
171
|
+
Panel(
|
|
172
|
+
body,
|
|
173
|
+
title="[bold]pythinker-code update available[/bold]",
|
|
174
|
+
border_style="yellow",
|
|
175
|
+
expand=False,
|
|
176
|
+
padding=(1, 2),
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
console.print(Rule(style="grey50"))
|
|
180
|
+
console.print(
|
|
181
|
+
Text.assemble(
|
|
182
|
+
" ",
|
|
183
|
+
("[Enter]", "bold"),
|
|
184
|
+
" Upgrade now ",
|
|
185
|
+
(f"({UPGRADE_COMMAND})", "grey50"),
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
console.print(Text.assemble(" ", ("[q]", "bold"), " Not now, remind me next time"))
|
|
189
|
+
console.print(
|
|
190
|
+
Text.assemble(" ", ("[s]", "bold"), f" Skip reminders for version {latest_version}")
|
|
191
|
+
)
|
|
192
|
+
console.print(Rule(style="grey50"))
|
|
193
|
+
console.print()
|
|
194
|
+
|
|
195
|
+
key = _read_key()
|
|
196
|
+
console.print()
|
|
197
|
+
|
|
198
|
+
if key in ("\r", "\n"):
|
|
199
|
+
console.print(f"[grey50]Running: {UPGRADE_COMMAND}[/grey50]\n")
|
|
200
|
+
try:
|
|
201
|
+
result = subprocess.run(shlex.split(UPGRADE_COMMAND))
|
|
202
|
+
except OSError:
|
|
203
|
+
console.print()
|
|
204
|
+
console.print("[red]Upgrade failed. Please try running manually:[/red]")
|
|
205
|
+
console.print(f" {UPGRADE_COMMAND}")
|
|
206
|
+
sys.exit(1)
|
|
207
|
+
console.print()
|
|
208
|
+
if result.returncode == 0:
|
|
209
|
+
console.print(
|
|
210
|
+
"[green]Upgrade complete! Run pythinker-code to start the new version.[/green]"
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
console.print("[red]Upgrade failed. Please try running manually:[/red]")
|
|
214
|
+
console.print(f" {UPGRADE_COMMAND}")
|
|
215
|
+
sys.exit(result.returncode)
|
|
216
|
+
elif key in ("s", "S"):
|
|
217
|
+
with contextlib.suppress(OSError):
|
|
218
|
+
SKIPPED_VERSION_FILE.write_text(latest_version, encoding="utf-8")
|
|
219
|
+
console.print(f"[grey50]Reminders skipped for version {latest_version}.[/grey50]\n")
|
|
220
|
+
elif key in ("\x03", "\x1b"):
|
|
221
|
+
sys.exit(0)
|
|
222
|
+
# q/Q/other: fall through, continue startup
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
async def _do_update(*, print: bool, check_only: bool) -> UpdateResult:
|
|
226
|
+
from pythinker_code.constant import VERSION as current_version
|
|
227
|
+
|
|
228
|
+
def _print(message: str) -> None:
|
|
229
|
+
if print:
|
|
230
|
+
console.print(message)
|
|
231
|
+
|
|
232
|
+
target = _detect_target()
|
|
233
|
+
if not target:
|
|
234
|
+
_print("[red]Failed to detect target platform.[/red]")
|
|
235
|
+
return UpdateResult.UNSUPPORTED
|
|
236
|
+
|
|
237
|
+
# Version check is fast, but the binary download can be large on slow links.
|
|
238
|
+
download_timeout = aiohttp.ClientTimeout(total=600, sock_read=60, sock_connect=15)
|
|
239
|
+
async with new_client_session(timeout=download_timeout) as session:
|
|
240
|
+
logger.info("Checking for updates...")
|
|
241
|
+
_print("Checking for updates...")
|
|
242
|
+
latest_version = await _get_latest_version(session)
|
|
243
|
+
if not latest_version:
|
|
244
|
+
_print("[red]Failed to check for updates.[/red]")
|
|
245
|
+
return UpdateResult.FAILED
|
|
246
|
+
|
|
247
|
+
logger.debug("Latest version: {latest_version}", latest_version=latest_version)
|
|
248
|
+
LATEST_VERSION_FILE.write_text(latest_version, encoding="utf-8")
|
|
249
|
+
|
|
250
|
+
cur_t = semver_tuple(current_version)
|
|
251
|
+
lat_t = semver_tuple(latest_version)
|
|
252
|
+
|
|
253
|
+
if cur_t >= lat_t:
|
|
254
|
+
logger.debug("Already up to date: {current_version}", current_version=current_version)
|
|
255
|
+
_print("[green]Already up to date.[/green]")
|
|
256
|
+
return UpdateResult.UP_TO_DATE
|
|
257
|
+
|
|
258
|
+
if check_only:
|
|
259
|
+
logger.info(
|
|
260
|
+
"Update available: current={current_version}, latest={latest_version}",
|
|
261
|
+
current_version=current_version,
|
|
262
|
+
latest_version=latest_version,
|
|
263
|
+
)
|
|
264
|
+
_print(f"[yellow]Update available: {latest_version}[/yellow]")
|
|
265
|
+
return UpdateResult.UPDATE_AVAILABLE
|
|
266
|
+
|
|
267
|
+
logger.info(
|
|
268
|
+
"Updating from {current_version} to {latest_version}...",
|
|
269
|
+
current_version=current_version,
|
|
270
|
+
latest_version=latest_version,
|
|
271
|
+
)
|
|
272
|
+
_print(f"Updating from {current_version} to {latest_version}...")
|
|
273
|
+
|
|
274
|
+
filename = f"pythinker-{latest_version}-{target}.tar.gz"
|
|
275
|
+
download_url = f"{BASE_URL}/{latest_version}/{filename}"
|
|
276
|
+
|
|
277
|
+
with tempfile.TemporaryDirectory(prefix="pythinker-code-") as tmpdir:
|
|
278
|
+
tar_path = os.path.join(tmpdir, filename)
|
|
279
|
+
|
|
280
|
+
logger.info("Downloading from {download_url}...", download_url=download_url)
|
|
281
|
+
_print("[grey50]Downloading...[/grey50]")
|
|
282
|
+
try:
|
|
283
|
+
async with session.get(download_url) as resp:
|
|
284
|
+
resp.raise_for_status()
|
|
285
|
+
with open(tar_path, "wb") as f:
|
|
286
|
+
async for chunk in resp.content.iter_chunked(1024 * 64):
|
|
287
|
+
if chunk:
|
|
288
|
+
f.write(chunk)
|
|
289
|
+
except (TimeoutError, aiohttp.ClientError):
|
|
290
|
+
logger.exception(
|
|
291
|
+
"Failed to download update from {download_url}",
|
|
292
|
+
download_url=download_url,
|
|
293
|
+
)
|
|
294
|
+
_print("[red]Failed to download.[/red]")
|
|
295
|
+
return UpdateResult.FAILED
|
|
296
|
+
except Exception:
|
|
297
|
+
logger.exception("Failed to download:")
|
|
298
|
+
_print("[red]Failed to download.[/red]")
|
|
299
|
+
return UpdateResult.FAILED
|
|
300
|
+
|
|
301
|
+
logger.info("Extracting archive {tar_path}...", tar_path=tar_path)
|
|
302
|
+
_print("[grey50]Extracting...[/grey50]")
|
|
303
|
+
try:
|
|
304
|
+
with tarfile.open(tar_path, "r:gz") as tar:
|
|
305
|
+
tar.extractall(tmpdir)
|
|
306
|
+
binary_path = None
|
|
307
|
+
for root, _, files in os.walk(tmpdir):
|
|
308
|
+
if "pythinker" in files:
|
|
309
|
+
binary_path = os.path.join(root, "pythinker")
|
|
310
|
+
break
|
|
311
|
+
if not binary_path:
|
|
312
|
+
logger.error("Binary 'pythinker' not found in archive.")
|
|
313
|
+
_print("[red]Binary 'pythinker' not found in archive.[/red]")
|
|
314
|
+
return UpdateResult.FAILED
|
|
315
|
+
except Exception:
|
|
316
|
+
logger.exception("Failed to extract archive:")
|
|
317
|
+
_print("[red]Failed to extract archive.[/red]")
|
|
318
|
+
return UpdateResult.FAILED
|
|
319
|
+
|
|
320
|
+
INSTALL_DIR.mkdir(parents=True, exist_ok=True)
|
|
321
|
+
dest_path = INSTALL_DIR / "pythinker"
|
|
322
|
+
logger.info("Installing to {dest_path}...", dest_path=dest_path)
|
|
323
|
+
_print("[grey50]Installing...[/grey50]")
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
shutil.copy2(binary_path, dest_path)
|
|
327
|
+
os.chmod(
|
|
328
|
+
dest_path,
|
|
329
|
+
os.stat(dest_path).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH,
|
|
330
|
+
)
|
|
331
|
+
except Exception:
|
|
332
|
+
logger.exception("Failed to install:")
|
|
333
|
+
_print("[red]Failed to install.[/red]")
|
|
334
|
+
return UpdateResult.FAILED
|
|
335
|
+
|
|
336
|
+
_print("[green]Updated successfully![/green]")
|
|
337
|
+
_print("[yellow]Restart Pythinker CLI to use the new version.[/yellow]")
|
|
338
|
+
return UpdateResult.UPDATED
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# @meta_command
|
|
342
|
+
# async def update(app: "Shell", args: list[str]):
|
|
343
|
+
# """Check for updates"""
|
|
344
|
+
# await do_update(print=True)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
# @meta_command(name="check-update")
|
|
348
|
+
# async def check_update(app: "Shell", args: list[str]):
|
|
349
|
+
# """Check for updates"""
|
|
350
|
+
# await do_update(print=True, check_only=True)
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import shlex
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from pythinker_code.auth.platforms import parse_managed_provider_key
|
|
9
|
+
from pythinker_code.config import LLMProvider
|
|
10
|
+
from pythinker_code.soul.pythinkersoul import PythinkerSoul
|
|
11
|
+
from pythinker_code.ui.shell.console import console
|
|
12
|
+
from pythinker_code.ui.shell.slash import registry
|
|
13
|
+
from pythinker_code.ui.shell.usage_adapters import ADAPTERS
|
|
14
|
+
from pythinker_code.ui.shell.usage_adapters.base import UsageAdapter, UsageReport, UsageRow
|
|
15
|
+
from pythinker_code.ui.shell.usage_adapters.pythinker import to_int as _to_int
|
|
16
|
+
from pythinker_code.ui.shell.usage_render import (
|
|
17
|
+
build_panel,
|
|
18
|
+
)
|
|
19
|
+
from pythinker_code.ui.shell.usage_render import (
|
|
20
|
+
format_row as _format_row,
|
|
21
|
+
)
|
|
22
|
+
from pythinker_code.ui.shell.usage_render import (
|
|
23
|
+
ratio_color as _ratio_color,
|
|
24
|
+
)
|
|
25
|
+
from pythinker_code.ui.shell.usage_render import (
|
|
26
|
+
remaining_quota as _remaining_quota,
|
|
27
|
+
)
|
|
28
|
+
from pythinker_code.usage_ratelimit_cache import get_cache
|
|
29
|
+
from pythinker_code.utils.datetime import format_duration
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from pythinker_code.auth.oauth import OAuthManager
|
|
33
|
+
from pythinker_code.ui.shell import Shell
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"UsageRow",
|
|
37
|
+
"_format_row",
|
|
38
|
+
"_ratio_color",
|
|
39
|
+
"_remaining_quota",
|
|
40
|
+
"_gather_reports",
|
|
41
|
+
"_print_json",
|
|
42
|
+
"_print_no_usage_providers",
|
|
43
|
+
"_select_providers",
|
|
44
|
+
"_to_int",
|
|
45
|
+
"usage",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _select_providers(
|
|
50
|
+
config_providers: dict[str, LLMProvider],
|
|
51
|
+
registered_platform_ids: set[str],
|
|
52
|
+
filter_provider_key: str | None,
|
|
53
|
+
) -> list[tuple[str, LLMProvider]]:
|
|
54
|
+
providers = (
|
|
55
|
+
[(filter_provider_key, config_providers[filter_provider_key])]
|
|
56
|
+
if filter_provider_key in config_providers
|
|
57
|
+
else list(config_providers.items())
|
|
58
|
+
if filter_provider_key is None
|
|
59
|
+
else []
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
pairs: list[tuple[str, LLMProvider]] = []
|
|
63
|
+
for provider_key, provider in providers:
|
|
64
|
+
platform_id = parse_managed_provider_key(provider_key)
|
|
65
|
+
if platform_id in registered_platform_ids:
|
|
66
|
+
pairs.append((platform_id, provider))
|
|
67
|
+
return pairs
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def _fetch_report(
|
|
71
|
+
adapter: UsageAdapter,
|
|
72
|
+
provider: LLMProvider,
|
|
73
|
+
oauth_mgr: OAuthManager,
|
|
74
|
+
) -> UsageReport:
|
|
75
|
+
try:
|
|
76
|
+
return await adapter.fetch(provider, oauth_mgr)
|
|
77
|
+
except asyncio.CancelledError:
|
|
78
|
+
raise
|
|
79
|
+
except Exception as e:
|
|
80
|
+
return UsageReport(
|
|
81
|
+
provider_label=getattr(adapter, "provider_label", adapter.platform_id),
|
|
82
|
+
summary=None,
|
|
83
|
+
limits=[],
|
|
84
|
+
notes=[f"Adapter raised: {e!r}"],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async def _gather_reports(
|
|
89
|
+
pairs: list[tuple[UsageAdapter, LLMProvider]],
|
|
90
|
+
oauth_mgr: OAuthManager,
|
|
91
|
+
) -> list[UsageReport]:
|
|
92
|
+
return list(
|
|
93
|
+
await asyncio.gather(
|
|
94
|
+
*(_fetch_report(adapter, provider, oauth_mgr) for adapter, provider in pairs)
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _print_json(value: object) -> None:
|
|
100
|
+
console.file.write(json.dumps(value, indent=2))
|
|
101
|
+
console.file.write("\n")
|
|
102
|
+
console.file.flush()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _print_no_usage_providers(json_mode: bool) -> None:
|
|
106
|
+
if json_mode:
|
|
107
|
+
_print_json([])
|
|
108
|
+
return
|
|
109
|
+
console.print("[yellow]No providers with usage support are configured.[/yellow]")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _ratelimit_fallback_report(provider_key: str, provider_label: str) -> UsageReport | None:
|
|
113
|
+
"""Build a 'live rate limits' UsageReport from the most recent snapshot
|
|
114
|
+
captured for `provider_key`, or None when nothing has been recorded yet."""
|
|
115
|
+
snap = get_cache().snapshot(provider_key)
|
|
116
|
+
if snap is None:
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
rows: list[UsageRow] = []
|
|
120
|
+
if snap.requests_limit is not None and snap.requests_remaining is not None:
|
|
121
|
+
rows.append(
|
|
122
|
+
UsageRow(
|
|
123
|
+
label="Requests",
|
|
124
|
+
used=snap.requests_remaining,
|
|
125
|
+
limit=snap.requests_limit,
|
|
126
|
+
unit="requests",
|
|
127
|
+
reset_hint=_seconds_to_reset_hint(snap.requests_reset_seconds),
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
if snap.tokens_limit is not None and snap.tokens_remaining is not None:
|
|
131
|
+
rows.append(
|
|
132
|
+
UsageRow(
|
|
133
|
+
label="Tokens",
|
|
134
|
+
used=snap.tokens_remaining,
|
|
135
|
+
limit=snap.tokens_limit,
|
|
136
|
+
unit="tokens",
|
|
137
|
+
reset_hint=_seconds_to_reset_hint(snap.tokens_reset_seconds),
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if not rows:
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
return UsageReport(
|
|
145
|
+
provider_label=f"{provider_label} (live rate limits)",
|
|
146
|
+
summary=rows[0],
|
|
147
|
+
limits=rows[1:],
|
|
148
|
+
notes=[
|
|
149
|
+
"Captured from the most recent chat-completion response headers — "
|
|
150
|
+
"no dedicated usage adapter is available for this provider yet."
|
|
151
|
+
],
|
|
152
|
+
unit_hint="rate-limit",
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _seconds_to_reset_hint(seconds: float | None) -> str | None:
|
|
157
|
+
if seconds is None:
|
|
158
|
+
return None
|
|
159
|
+
if seconds <= 0:
|
|
160
|
+
return "reset"
|
|
161
|
+
return f"resets in {format_duration(int(seconds))}"
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _enrich_with_ratelimit_fallback(
|
|
165
|
+
reports: list[UsageReport],
|
|
166
|
+
selected: list[tuple[str, LLMProvider]],
|
|
167
|
+
) -> list[UsageReport]:
|
|
168
|
+
"""Merge live rate-limit cache rows into reports that lack real data.
|
|
169
|
+
|
|
170
|
+
A "real data" report has a `summary` or non-empty `limits`. Anything
|
|
171
|
+
without that — including notes-only reports from best-effort adapters
|
|
172
|
+
(MiniMax, OpenCode Go, Pythinker AI) — gets the cached rate-limit rows
|
|
173
|
+
appended, while the adapter's notes (which usually explain *why* there's
|
|
174
|
+
no real data) are preserved.
|
|
175
|
+
"""
|
|
176
|
+
enriched: list[UsageReport] = []
|
|
177
|
+
for report, (platform_id, _) in zip(reports, selected, strict=True):
|
|
178
|
+
if report.summary is not None or report.limits:
|
|
179
|
+
enriched.append(report)
|
|
180
|
+
continue
|
|
181
|
+
fallback = _ratelimit_fallback_report(f"managed:{platform_id}", report.provider_label)
|
|
182
|
+
if fallback is None:
|
|
183
|
+
enriched.append(report)
|
|
184
|
+
continue
|
|
185
|
+
enriched.append(
|
|
186
|
+
UsageReport(
|
|
187
|
+
provider_label=fallback.provider_label,
|
|
188
|
+
summary=fallback.summary,
|
|
189
|
+
limits=fallback.limits,
|
|
190
|
+
# Adapter notes first so the user reads "why" before "what".
|
|
191
|
+
notes=[*report.notes, *fallback.notes],
|
|
192
|
+
unit_hint=fallback.unit_hint,
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
return enriched
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@registry.command(aliases=["/status"])
|
|
199
|
+
async def usage(app: Shell, args: str):
|
|
200
|
+
"""Display usage for the current model's provider.
|
|
201
|
+
|
|
202
|
+
Pass `all` for every provider, or a provider key to filter.
|
|
203
|
+
"""
|
|
204
|
+
assert isinstance(app.soul, PythinkerSoul)
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
tokens = shlex.split(args.strip())
|
|
208
|
+
except ValueError as e:
|
|
209
|
+
console.print(f"[red]Invalid usage arguments: {e}[/red]")
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
json_mode = "--json" in tokens
|
|
213
|
+
positional = [token for token in tokens if token != "--json"]
|
|
214
|
+
scoped_to_active = False
|
|
215
|
+
active_provider_key: str | None = None
|
|
216
|
+
if positional:
|
|
217
|
+
# `/usage all` → all providers; `/usage <key>` → just that one.
|
|
218
|
+
filter_provider_key = None if positional[0] == "all" else positional[0]
|
|
219
|
+
else:
|
|
220
|
+
# `/usage` with no args → scope to the active model's provider so
|
|
221
|
+
# users with multiple providers configured don't see a wall of
|
|
222
|
+
# unrelated reports. Falls back to all providers when no model is
|
|
223
|
+
# active (e.g. before /login).
|
|
224
|
+
llm = app.soul.runtime.llm
|
|
225
|
+
if llm is not None and llm.model_config is not None:
|
|
226
|
+
active_provider_key = llm.model_config.provider
|
|
227
|
+
scoped_to_active = True
|
|
228
|
+
filter_provider_key = active_provider_key
|
|
229
|
+
|
|
230
|
+
selected = _select_providers(
|
|
231
|
+
app.soul.runtime.config.providers,
|
|
232
|
+
registered_platform_ids=set(ADAPTERS),
|
|
233
|
+
filter_provider_key=filter_provider_key,
|
|
234
|
+
)
|
|
235
|
+
if not selected:
|
|
236
|
+
# No dedicated adapter for the active provider (e.g. MiniMax,
|
|
237
|
+
# OpenCode Go). Try the live-rate-limit cache as a universal
|
|
238
|
+
# fallback before giving up — chat-completion responses populate
|
|
239
|
+
# it for every openai-shape provider.
|
|
240
|
+
if scoped_to_active and active_provider_key in app.soul.runtime.config.providers:
|
|
241
|
+
platform_id = parse_managed_provider_key(active_provider_key)
|
|
242
|
+
fallback = _ratelimit_fallback_report(active_provider_key, platform_id or "Provider")
|
|
243
|
+
if fallback is not None:
|
|
244
|
+
if json_mode:
|
|
245
|
+
_print_json([fallback.to_dict()])
|
|
246
|
+
else:
|
|
247
|
+
console.print(build_panel(fallback))
|
|
248
|
+
return
|
|
249
|
+
if json_mode:
|
|
250
|
+
_print_json([])
|
|
251
|
+
else:
|
|
252
|
+
console.print(
|
|
253
|
+
f"[yellow]Usage tracking is not yet available for the active "
|
|
254
|
+
f"provider ({active_provider_key}). Send a message first so "
|
|
255
|
+
f"live rate-limit headers can be captured, or run "
|
|
256
|
+
f"[bold]/usage all[/bold] to see other configured providers."
|
|
257
|
+
f"[/yellow]"
|
|
258
|
+
)
|
|
259
|
+
return
|
|
260
|
+
_print_no_usage_providers(json_mode)
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
pairs = [(ADAPTERS[platform_id], provider) for platform_id, provider in selected]
|
|
264
|
+
|
|
265
|
+
if json_mode:
|
|
266
|
+
reports = await _gather_reports(pairs, app.soul.runtime.oauth)
|
|
267
|
+
reports = _enrich_with_ratelimit_fallback(reports, selected)
|
|
268
|
+
_print_json([r.to_dict() for r in reports])
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
with console.status("[cyan]Fetching usage...[/cyan]"):
|
|
272
|
+
reports = await _gather_reports(pairs, app.soul.runtime.oauth)
|
|
273
|
+
|
|
274
|
+
# Swap any empty primary report for a live-rate-limit fallback when one
|
|
275
|
+
# has been captured for that provider's chat completions.
|
|
276
|
+
reports = _enrich_with_ratelimit_fallback(reports, selected)
|
|
277
|
+
|
|
278
|
+
non_empty_reports = [report for report in reports if not report.is_empty]
|
|
279
|
+
if not non_empty_reports:
|
|
280
|
+
# When scoped to a single active provider (no positional arg), always
|
|
281
|
+
# render the report even if empty — otherwise the user sees a flat
|
|
282
|
+
# "No usage data available." with no idea which provider was queried
|
|
283
|
+
# or what the adapter actually got back.
|
|
284
|
+
if scoped_to_active and len(reports) == 1:
|
|
285
|
+
console.print(build_panel(reports[0]))
|
|
286
|
+
return
|
|
287
|
+
console.print("[yellow]No usage data available.[/yellow]")
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
for report in non_empty_reports:
|
|
291
|
+
console.print(build_panel(report))
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pythinker_code.ui.shell.usage_adapters.anthropic_admin import AnthropicAdminAdapter
|
|
4
|
+
from pythinker_code.ui.shell.usage_adapters.base import (
|
|
5
|
+
UsageAdapter,
|
|
6
|
+
UsageReport,
|
|
7
|
+
UsageRow,
|
|
8
|
+
)
|
|
9
|
+
from pythinker_code.ui.shell.usage_adapters.deepseek import DeepSeekAdapter
|
|
10
|
+
from pythinker_code.ui.shell.usage_adapters.minimax import MiniMaxAdapter
|
|
11
|
+
from pythinker_code.ui.shell.usage_adapters.openai_admin import OpenAIAdminAdapter
|
|
12
|
+
from pythinker_code.ui.shell.usage_adapters.openai_chatgpt import OpenAIChatGPTAdapter
|
|
13
|
+
from pythinker_code.ui.shell.usage_adapters.opencode_go import OpenCodeGoAdapter
|
|
14
|
+
from pythinker_code.ui.shell.usage_adapters.openrouter import OpenRouterAdapter
|
|
15
|
+
from pythinker_code.ui.shell.usage_adapters.pythinker import PythinkerAdapter
|
|
16
|
+
from pythinker_code.ui.shell.usage_adapters.pythinker_ai import PythinkerAIAdapter
|
|
17
|
+
|
|
18
|
+
_pythinker_ai_adapter = PythinkerAIAdapter()
|
|
19
|
+
_minimax_adapter = MiniMaxAdapter()
|
|
20
|
+
_opencode_go_adapter = OpenCodeGoAdapter()
|
|
21
|
+
|
|
22
|
+
# A single provider can be registered under several `managed:<platform_id>`
|
|
23
|
+
# keys when the chat path is exposed through both Anthropic-compat and
|
|
24
|
+
# OpenAI-compat shapes (e.g. `managed:minimax-anthropic`,
|
|
25
|
+
# `managed:opencode-go-openai`, `managed:opencode-go-anthropic`). The active
|
|
26
|
+
# model's `provider` field carries one of these specific keys, so the adapter
|
|
27
|
+
# registry has to cover every variant — otherwise `_select_providers` filters
|
|
28
|
+
# the active provider out and `/usage` falls into the no-adapter branch.
|
|
29
|
+
ADAPTERS: dict[str, UsageAdapter] = {
|
|
30
|
+
AnthropicAdminAdapter.platform_id: AnthropicAdminAdapter(),
|
|
31
|
+
DeepSeekAdapter.platform_id: DeepSeekAdapter(),
|
|
32
|
+
OpenAIAdminAdapter.platform_id: OpenAIAdminAdapter(),
|
|
33
|
+
OpenAIChatGPTAdapter.platform_id: OpenAIChatGPTAdapter(),
|
|
34
|
+
OpenRouterAdapter.platform_id: OpenRouterAdapter(),
|
|
35
|
+
PythinkerAdapter.platform_id: PythinkerAdapter(),
|
|
36
|
+
# MiniMax — only the anthropic-compat variant exists today; the bare
|
|
37
|
+
# `minimax` key is kept for a possible future openai-compat variant.
|
|
38
|
+
MiniMaxAdapter.platform_id: _minimax_adapter,
|
|
39
|
+
"minimax-anthropic": _minimax_adapter,
|
|
40
|
+
# OpenCode Go — both compat surfaces in use today.
|
|
41
|
+
OpenCodeGoAdapter.platform_id: _opencode_go_adapter,
|
|
42
|
+
"opencode-go-openai": _opencode_go_adapter,
|
|
43
|
+
"opencode-go-anthropic": _opencode_go_adapter,
|
|
44
|
+
# Pythinker AI — global and China regions share the adapter (same shape,
|
|
45
|
+
# different host).
|
|
46
|
+
PythinkerAIAdapter.platform_id: _pythinker_ai_adapter,
|
|
47
|
+
"pythinker_ai-cn": _pythinker_ai_adapter,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
__all__ = ["ADAPTERS", "UsageAdapter", "UsageReport", "UsageRow"]
|