pythinker-code 0.8.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 +60 -0
- pythinker_code/__init__.py +0 -0
- pythinker_code/__main__.py +97 -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 +301 -0
- pythinker_code/acp/mcp.py +46 -0
- pythinker_code/acp/server.py +497 -0
- pythinker_code/acp/session.py +502 -0
- pythinker_code/acp/tools.py +174 -0
- pythinker_code/acp/types.py +13 -0
- pythinker_code/acp/version.py +45 -0
- pythinker_code/agents/default/agent.yaml +55 -0
- pythinker_code/agents/default/code_reviewer.yaml +47 -0
- pythinker_code/agents/default/coder.yaml +42 -0
- pythinker_code/agents/default/debugger.yaml +35 -0
- pythinker_code/agents/default/explore.yaml +59 -0
- pythinker_code/agents/default/implementer.yaml +46 -0
- pythinker_code/agents/default/plan.yaml +42 -0
- pythinker_code/agents/default/review.yaml +47 -0
- pythinker_code/agents/default/security_reviewer.yaml +37 -0
- pythinker_code/agents/default/system.md +192 -0
- pythinker_code/agents/default/verifier.yaml +46 -0
- pythinker_code/agents/okabe/agent.yaml +22 -0
- pythinker_code/agentspec.py +163 -0
- pythinker_code/app.py +847 -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/github_feedback.py +228 -0
- pythinker_code/auth/lm_studio.py +418 -0
- pythinker_code/auth/minimax.py +203 -0
- pythinker_code/auth/oauth.py +1145 -0
- pythinker_code/auth/ollama.py +293 -0
- pythinker_code/auth/openai.py +783 -0
- pythinker_code/auth/opencode_go.py +203 -0
- pythinker_code/auth/openrouter.py +225 -0
- pythinker_code/auth/platforms.py +475 -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 +668 -0
- pythinker_code/background/models.py +118 -0
- pythinker_code/background/store.py +243 -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 +268 -0
- pythinker_code/cli/debug.py +11 -0
- pythinker_code/cli/export.py +322 -0
- pythinker_code/cli/info.py +62 -0
- pythinker_code/cli/mcp.py +362 -0
- pythinker_code/cli/plugin.py +351 -0
- pythinker_code/cli/review.py +74 -0
- pythinker_code/cli/secscan.py +11 -0
- pythinker_code/cli/security_scan.py +35 -0
- pythinker_code/cli/toad.py +74 -0
- pythinker_code/cli/update.py +26 -0
- pythinker_code/cli/vis.py +38 -0
- pythinker_code/cli/web.py +80 -0
- pythinker_code/config.py +511 -0
- pythinker_code/constant.py +33 -0
- pythinker_code/events.py +106 -0
- pythinker_code/exception.py +43 -0
- pythinker_code/extensions.py +151 -0
- pythinker_code/hooks/__init__.py +4 -0
- pythinker_code/hooks/config.py +34 -0
- pythinker_code/hooks/engine.py +383 -0
- pythinker_code/hooks/events.py +190 -0
- pythinker_code/hooks/runner.py +92 -0
- pythinker_code/llm.py +441 -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 +166 -0
- pythinker_code/plugin/tool.py +173 -0
- pythinker_code/prompt_templates.py +181 -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 +552 -0
- pythinker_code/soul/approval.py +267 -0
- pythinker_code/soul/btw.py +220 -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/permission.py +368 -0
- pythinker_code/soul/pythinkersoul.py +1763 -0
- pythinker_code/soul/slash.py +340 -0
- pythinker_code/soul/toolset.py +826 -0
- pythinker_code/subagents/__init__.py +21 -0
- pythinker_code/subagents/builder.py +43 -0
- pythinker_code/subagents/core.py +86 -0
- pythinker_code/subagents/discovery.py +234 -0
- pythinker_code/subagents/git_context.py +172 -0
- pythinker_code/subagents/models.py +56 -0
- pythinker_code/subagents/output.py +71 -0
- pythinker_code/subagents/registry.py +28 -0
- pythinker_code/subagents/runner.py +442 -0
- pythinker_code/subagents/store.py +200 -0
- pythinker_code/telemetry/__init__.py +217 -0
- pythinker_code/telemetry/config.py +113 -0
- pythinker_code/telemetry/crash.py +191 -0
- pythinker_code/telemetry/errors.py +113 -0
- pythinker_code/telemetry/metrics.py +208 -0
- pythinker_code/telemetry/otel.py +303 -0
- pythinker_code/telemetry/sentry.py +212 -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 +326 -0
- pythinker_code/tools/agent/description.md +65 -0
- pythinker_code/tools/ask_user/__init__.py +162 -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 +31 -0
- pythinker_code/tools/file/glob.md +17 -0
- pythinker_code/tools/file/glob.py +163 -0
- pythinker_code/tools/file/grep.md +6 -0
- pythinker_code/tools/file/grep_local.py +904 -0
- pythinker_code/tools/file/plan_mode.py +45 -0
- pythinker_code/tools/file/read.md +16 -0
- pythinker_code/tools/file/read.py +303 -0
- pythinker_code/tools/file/read_media.md +24 -0
- pythinker_code/tools/file/read_media.py +220 -0
- pythinker_code/tools/file/replace.md +7 -0
- pythinker_code/tools/file/replace.py +204 -0
- pythinker_code/tools/file/utils.py +257 -0
- pythinker_code/tools/file/write.md +5 -0
- pythinker_code/tools/file/write.py +186 -0
- pythinker_code/tools/plan/__init__.py +362 -0
- pythinker_code/tools/plan/description.md +29 -0
- pythinker_code/tools/plan/enter.py +193 -0
- pythinker_code/tools/plan/enter_description.md +35 -0
- pythinker_code/tools/plan/handoff.py +69 -0
- pythinker_code/tools/plan/heroes.py +277 -0
- pythinker_code/tools/shell/__init__.py +263 -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 +200 -0
- pythinker_code/tools/web/__init__.py +4 -0
- pythinker_code/tools/web/fetch.md +1 -0
- pythinker_code/tools/web/fetch.py +261 -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 +1806 -0
- pythinker_code/ui/shell/components/__init__.py +110 -0
- pythinker_code/ui/shell/components/base.py +25 -0
- pythinker_code/ui/shell/components/bash_execution.py +249 -0
- pythinker_code/ui/shell/components/bordered_loader.py +62 -0
- pythinker_code/ui/shell/components/diff.py +308 -0
- pythinker_code/ui/shell/components/footer.py +231 -0
- pythinker_code/ui/shell/components/key_hints.py +27 -0
- pythinker_code/ui/shell/components/messages.py +152 -0
- pythinker_code/ui/shell/components/render_utils.py +198 -0
- pythinker_code/ui/shell/components/settings_list.py +369 -0
- pythinker_code/ui/shell/components/special_messages.py +125 -0
- pythinker_code/ui/shell/components/tool_execution.py +261 -0
- pythinker_code/ui/shell/console.py +109 -0
- pythinker_code/ui/shell/debug.py +190 -0
- pythinker_code/ui/shell/echo.py +30 -0
- pythinker_code/ui/shell/export_import.py +117 -0
- pythinker_code/ui/shell/keyboard.py +300 -0
- pythinker_code/ui/shell/keymap.py +84 -0
- pythinker_code/ui/shell/mcp_status.py +112 -0
- pythinker_code/ui/shell/model_picker.py +318 -0
- pythinker_code/ui/shell/oauth.py +273 -0
- pythinker_code/ui/shell/placeholders.py +578 -0
- pythinker_code/ui/shell/prompt.py +2888 -0
- pythinker_code/ui/shell/replay.py +215 -0
- pythinker_code/ui/shell/selector.py +364 -0
- pythinker_code/ui/shell/selectors/__init__.py +38 -0
- pythinker_code/ui/shell/selectors/extension.py +37 -0
- pythinker_code/ui/shell/selectors/oauth.py +63 -0
- pythinker_code/ui/shell/selectors/settings.py +349 -0
- pythinker_code/ui/shell/selectors/show_images.py +29 -0
- pythinker_code/ui/shell/selectors/theme.py +28 -0
- pythinker_code/ui/shell/selectors/thinking.py +42 -0
- pythinker_code/ui/shell/session_picker.py +227 -0
- pythinker_code/ui/shell/setup.py +212 -0
- pythinker_code/ui/shell/slash.py +1433 -0
- pythinker_code/ui/shell/spinner_words.py +222 -0
- pythinker_code/ui/shell/startup.py +32 -0
- pythinker_code/ui/shell/task_browser.py +486 -0
- pythinker_code/ui/shell/tool_renderers/__init__.py +197 -0
- pythinker_code/ui/shell/tool_renderers/_render_utils.py +168 -0
- pythinker_code/ui/shell/tool_renderers/agent.py +140 -0
- pythinker_code/ui/shell/tool_renderers/ask_user.py +93 -0
- pythinker_code/ui/shell/tool_renderers/background.py +144 -0
- pythinker_code/ui/shell/tool_renderers/bash.py +103 -0
- pythinker_code/ui/shell/tool_renderers/edit.py +163 -0
- pythinker_code/ui/shell/tool_renderers/find.py +81 -0
- pythinker_code/ui/shell/tool_renderers/generic.py +60 -0
- pythinker_code/ui/shell/tool_renderers/grep.py +98 -0
- pythinker_code/ui/shell/tool_renderers/plan.py +98 -0
- pythinker_code/ui/shell/tool_renderers/read.py +103 -0
- pythinker_code/ui/shell/tool_renderers/think.py +66 -0
- pythinker_code/ui/shell/tool_renderers/todo.py +164 -0
- pythinker_code/ui/shell/tool_renderers/web.py +128 -0
- pythinker_code/ui/shell/tool_renderers/write.py +102 -0
- pythinker_code/ui/shell/update.py +352 -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 +539 -0
- pythinker_code/ui/shell/visualize/_blocks.py +802 -0
- pythinker_code/ui/shell/visualize/_btw_panel.py +227 -0
- pythinker_code/ui/shell/visualize/_input_router.py +48 -0
- pythinker_code/ui/shell/visualize/_interactive.py +531 -0
- pythinker_code/ui/shell/visualize/_live_view.py +891 -0
- pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
- pythinker_code/ui/shell/visualize/_worklog.py +245 -0
- pythinker_code/ui/theme.py +395 -0
- pythinker_code/ui/tui_config.py +82 -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 +38 -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 +935 -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 +83 -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 +714 -0
- pythinker_code/vis/api/statistics.py +209 -0
- pythinker_code/vis/api/system.py +19 -0
- pythinker_code/vis/app.py +199 -0
- pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-CY1rtwrX.js +1 -0
- pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
- pythinker_code/vis/static/assets/index-DgmTI2M_.js +185 -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 +217 -0
- pythinker_code/web/api/open_in.py +233 -0
- pythinker_code/web/api/sessions.py +1256 -0
- pythinker_code/web/app.py +449 -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-DpSMr1jx.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-DpsahJyV.js +1 -0
- pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-DqiRv9Eg.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-WgtUvqbp.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-rK0RPuZd.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-B0rlvkH-.js +1 -0
- pythinker_code/web/static/assets/chunk-4BX2VUAB-DIkMuLV-.js +1 -0
- pythinker_code/web/static/assets/chunk-55IACEB6-CORdm4k4.js +1 -0
- pythinker_code/web/static/assets/chunk-B4BG7PRW-D9xDhwHO.js +165 -0
- pythinker_code/web/static/assets/chunk-DI55MBZ5-BDmF9Bh-.js +220 -0
- pythinker_code/web/static/assets/chunk-FMBD7UC4-BCse_HmM.js +15 -0
- pythinker_code/web/static/assets/chunk-QN33PNHL-DCpBmTzA.js +1 -0
- pythinker_code/web/static/assets/chunk-QZHKN3VN-BqLuqobw.js +1 -0
- pythinker_code/web/static/assets/chunk-TZMSLE5B-8K2ogOKS.js +1 -0
- pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
- pythinker_code/web/static/assets/classDiagram-2ON5EDUG-D_ZHSii2.js +1 -0
- pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-D_ZHSii2.js +1 -0
- pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
- pythinker_code/web/static/assets/clone-GSXejyY1.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-DWTFYA28.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-BRI7ES-N.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-B6BxUuKW.js +321 -0
- pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
- pythinker_code/web/static/assets/dagre-6UL2VRFP-Ci5GdWfi.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-0hhAylV4.js +24 -0
- pythinker_code/web/static/assets/diagram-QEK2KX5R-8fxgaW6d.js +43 -0
- pythinker_code/web/static/assets/diagram-S2PKOQOG-FRr0_atE.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-B3T-hJUM.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-D0S3u7ot.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-CHrN2a23.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-CfcXZWg0.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-8jMJwCqE.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-BXrFnzMy.js +153 -0
- pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
- pythinker_code/web/static/assets/index-BrfQJnRD.js +5 -0
- pythinker_code/web/static/assets/index-C4gFzubz.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/infoDiagram-WHAUD3N6-DdxonBf3.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-BXf4aQei.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-DLpPPOu8.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-DH73UoAH.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-bAer2-sK.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-CuqbwKXv.js +465 -0
- pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
- pythinker_code/web/static/assets/mermaid.core-Nx-rTKiV.js +191 -0
- pythinker_code/web/static/assets/min-DbfD8Ywu.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-C6l761Ue.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-fNg41mT9.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-DJz3Kx87.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-B4SbrfE9.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-CoSUjLAG.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-PjhBNHi3.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-DOwESt8-.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-yl3OHWiP.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-CkCLnAgi.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-CZS5XwTf.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-DkqqHNLh.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 +433 -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 +1072 -0
- pythinker_code/wire/types.py +698 -0
- pythinker_code-0.8.0.dist-info/METADATA +706 -0
- pythinker_code-0.8.0.dist-info/RECORD +790 -0
- pythinker_code-0.8.0.dist-info/WHEEL +4 -0
- pythinker_code-0.8.0.dist-info/entry_points.txt +4 -0
- pythinker_code-0.8.0.dist-info/licenses/LICENSE +202 -0
- pythinker_code-0.8.0.dist-info/licenses/NOTICE +14 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import re
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
from enum import Enum, auto
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from shutil import which
|
|
11
|
+
|
|
12
|
+
import aiohttp
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
|
|
15
|
+
from pythinker_code.share import get_share_dir
|
|
16
|
+
from pythinker_code.ui.shell.console import console
|
|
17
|
+
from pythinker_code.utils.aiohttp import new_client_session
|
|
18
|
+
from pythinker_code.utils.logging import logger
|
|
19
|
+
|
|
20
|
+
PYPI_JSON_URL = "https://pypi.org/pypi/pythinker-code/json"
|
|
21
|
+
CHANGELOG_URL_EN = "https://github.com/mohamed-elkholy95/Pythinker-Code/blob/main/CHANGELOG.md"
|
|
22
|
+
|
|
23
|
+
# Default upgrade command. `_detect_upgrade_command()` overrides this when the
|
|
24
|
+
# install method is recognizable from `sys.executable`.
|
|
25
|
+
UPGRADE_COMMAND = ["uv", "tool", "upgrade", "pythinker-code"]
|
|
26
|
+
|
|
27
|
+
LATEST_VERSION_FILE = get_share_dir() / "latest_version.txt"
|
|
28
|
+
SKIPPED_VERSION_FILE = get_share_dir() / "skipped_version.txt"
|
|
29
|
+
LAST_UPDATE_CHECK_FILE = get_share_dir() / "last_update_check.txt"
|
|
30
|
+
AUTO_UPDATE_CHECK_INTERVAL_SECONDS = 24 * 60 * 60
|
|
31
|
+
|
|
32
|
+
_UPDATE_LOCK = asyncio.Lock()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class UpdateResult(Enum):
|
|
36
|
+
UPDATE_AVAILABLE = auto()
|
|
37
|
+
UPDATED = auto()
|
|
38
|
+
UP_TO_DATE = auto()
|
|
39
|
+
FAILED = auto()
|
|
40
|
+
UNSUPPORTED = auto()
|
|
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_upgrade_command() -> list[str]:
|
|
57
|
+
"""Pick the right upgrade argv based on how this interpreter was installed."""
|
|
58
|
+
exe = sys.executable.replace("\\", "/").lower()
|
|
59
|
+
if "/uv/tools/" in exe:
|
|
60
|
+
return ["uv", "tool", "upgrade", "pythinker-code"]
|
|
61
|
+
if "/pipx/venvs/" in exe:
|
|
62
|
+
return ["pipx", "upgrade", "pythinker-code"]
|
|
63
|
+
return [sys.executable, "-m", "pip", "install", "--upgrade", "pythinker-code"]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _format_upgrade_command(command: list[str]) -> str:
|
|
67
|
+
if _is_windows():
|
|
68
|
+
return subprocess.list2cmdline(command)
|
|
69
|
+
return " ".join(shlex_quote(part) for part in command)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def shlex_quote(value: str) -> str:
|
|
73
|
+
if not value:
|
|
74
|
+
return "''"
|
|
75
|
+
if re.fullmatch(r"[A-Za-z0-9_@%+=:,./-]+", value):
|
|
76
|
+
return value
|
|
77
|
+
return "'" + value.replace("'", "'\\''") + "'"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _is_windows() -> bool:
|
|
81
|
+
return sys.platform == "win32"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _spawn_detached_windows_upgrade(upgrade_command: list[str]) -> bool:
|
|
85
|
+
"""Launch the upgrade in a new console window that survives this process exit.
|
|
86
|
+
|
|
87
|
+
Returns True if the helper was spawned. The new console sleeps a few seconds
|
|
88
|
+
before invoking ``upgrade_command`` so the currently running ``pythinker.exe``
|
|
89
|
+
has time to exit and release the executable lock that ``uv``/``pip`` would
|
|
90
|
+
otherwise trip over with ``os error 32``.
|
|
91
|
+
"""
|
|
92
|
+
if not _is_windows():
|
|
93
|
+
return False
|
|
94
|
+
if which("cmd") is None:
|
|
95
|
+
return False
|
|
96
|
+
DETACHED_PROCESS = 0x00000008
|
|
97
|
+
CREATE_NEW_PROCESS_GROUP = 0x00000200
|
|
98
|
+
# `start "" cmd /k <inner>` opens a new console; `timeout` gives the parent
|
|
99
|
+
# time to exit so the exe lock releases before the upgrade runs.
|
|
100
|
+
formatted_command = _format_upgrade_command(upgrade_command)
|
|
101
|
+
inner = (
|
|
102
|
+
"echo Waiting for Pythinker to exit... "
|
|
103
|
+
"& timeout /t 3 /nobreak >nul "
|
|
104
|
+
f"& {formatted_command} "
|
|
105
|
+
"& echo. "
|
|
106
|
+
"& echo Upgrade finished. Press any key to close this window. "
|
|
107
|
+
"& pause >nul"
|
|
108
|
+
)
|
|
109
|
+
try:
|
|
110
|
+
subprocess.Popen(
|
|
111
|
+
["cmd", "/c", "start", "", "cmd", "/k", inner],
|
|
112
|
+
creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
|
|
113
|
+
close_fds=True,
|
|
114
|
+
)
|
|
115
|
+
except OSError:
|
|
116
|
+
logger.exception("Failed to spawn detached Windows upgrade:")
|
|
117
|
+
return False
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def _get_latest_version(session: aiohttp.ClientSession) -> str | None:
|
|
122
|
+
try:
|
|
123
|
+
async with session.get(PYPI_JSON_URL) as resp:
|
|
124
|
+
resp.raise_for_status()
|
|
125
|
+
data = await resp.json(content_type=None)
|
|
126
|
+
version = data.get("info", {}).get("version")
|
|
127
|
+
return str(version).strip() if version else None
|
|
128
|
+
except (TimeoutError, aiohttp.ClientError):
|
|
129
|
+
logger.exception("Failed to fetch latest version from PyPI:")
|
|
130
|
+
return None
|
|
131
|
+
except Exception:
|
|
132
|
+
logger.exception("Failed to parse PyPI response:")
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _auto_update_disabled() -> bool:
|
|
137
|
+
from pythinker_code.utils.envvar import get_env_bool
|
|
138
|
+
|
|
139
|
+
return get_env_bool("PYTHINKER_CLI_NO_AUTO_UPDATE")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _is_running_from_source_checkout() -> bool:
|
|
143
|
+
"""Return true when invoked from this repository via ``uv run``/editable source.
|
|
144
|
+
|
|
145
|
+
In that mode PyPI can legitimately have a newer released version than the
|
|
146
|
+
checkout's local ``pyproject.toml`` version. Showing the normal upgrade
|
|
147
|
+
banner is noisy and suggests replacing the developer checkout.
|
|
148
|
+
"""
|
|
149
|
+
try:
|
|
150
|
+
import pythinker_code
|
|
151
|
+
|
|
152
|
+
package_path = Path(pythinker_code.__file__).resolve()
|
|
153
|
+
except Exception:
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
for parent in package_path.parents:
|
|
157
|
+
pyproject = parent / "pyproject.toml"
|
|
158
|
+
git_dir = parent / ".git"
|
|
159
|
+
if pyproject.exists() and git_dir.exists():
|
|
160
|
+
try:
|
|
161
|
+
text = pyproject.read_text(encoding="utf-8")
|
|
162
|
+
except OSError:
|
|
163
|
+
return False
|
|
164
|
+
return 'name = "pythinker-code"' in text or "name = 'pythinker-code'" in text
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _should_auto_check_for_updates(now: float | None = None) -> bool:
|
|
169
|
+
if _auto_update_disabled() or _is_running_from_source_checkout():
|
|
170
|
+
return False
|
|
171
|
+
if not sys.stdout.isatty():
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
now = time.time() if now is None else now
|
|
175
|
+
try:
|
|
176
|
+
last_check = LAST_UPDATE_CHECK_FILE.stat().st_mtime
|
|
177
|
+
except FileNotFoundError:
|
|
178
|
+
return True
|
|
179
|
+
except OSError:
|
|
180
|
+
logger.exception("Failed to read last update-check timestamp:")
|
|
181
|
+
return True
|
|
182
|
+
return now - last_check >= AUTO_UPDATE_CHECK_INTERVAL_SECONDS
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _mark_auto_update_check_attempt() -> None:
|
|
186
|
+
try:
|
|
187
|
+
LAST_UPDATE_CHECK_FILE.write_text(str(int(time.time())), encoding="utf-8")
|
|
188
|
+
except OSError:
|
|
189
|
+
logger.exception("Failed to write last update-check timestamp:")
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def schedule_auto_update_check() -> asyncio.Task[UpdateResult] | None:
|
|
193
|
+
"""Schedule a silent, throttled PyPI update check for interactive shell startup."""
|
|
194
|
+
if not _should_auto_check_for_updates():
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
_mark_auto_update_check_attempt()
|
|
198
|
+
task = asyncio.create_task(
|
|
199
|
+
do_update(print=False, check_only=True),
|
|
200
|
+
name="pythinker-update-check",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
def _log_result(done: asyncio.Task[UpdateResult]) -> None:
|
|
204
|
+
try:
|
|
205
|
+
result = done.result()
|
|
206
|
+
except asyncio.CancelledError:
|
|
207
|
+
return
|
|
208
|
+
except Exception:
|
|
209
|
+
logger.exception("Background update check failed:")
|
|
210
|
+
return
|
|
211
|
+
if result is UpdateResult.UPDATE_AVAILABLE:
|
|
212
|
+
logger.info("Update available; showing notification banner")
|
|
213
|
+
print_update_banner()
|
|
214
|
+
|
|
215
|
+
task.add_done_callback(_log_result)
|
|
216
|
+
return task
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def print_update_banner() -> None:
|
|
220
|
+
"""Print a non-blocking 'Update Available' banner if a newer version is cached."""
|
|
221
|
+
from pythinker_code.constant import VERSION as current_version
|
|
222
|
+
|
|
223
|
+
if _auto_update_disabled() or _is_running_from_source_checkout():
|
|
224
|
+
return
|
|
225
|
+
if not sys.stdout.isatty():
|
|
226
|
+
return
|
|
227
|
+
if not LATEST_VERSION_FILE.exists():
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
latest_version = LATEST_VERSION_FILE.read_text(encoding="utf-8").strip()
|
|
232
|
+
except OSError:
|
|
233
|
+
return
|
|
234
|
+
if not latest_version or semver_tuple(latest_version) <= semver_tuple(current_version):
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
if SKIPPED_VERSION_FILE.exists():
|
|
238
|
+
try:
|
|
239
|
+
skipped = SKIPPED_VERSION_FILE.read_text(encoding="utf-8").strip()
|
|
240
|
+
except OSError:
|
|
241
|
+
skipped = ""
|
|
242
|
+
if skipped == latest_version:
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
_render_update_banner(current_version, latest_version)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _update_banner_text(current_version: str, latest_version: str) -> Text:
|
|
249
|
+
upgrade_command = _detect_upgrade_command()
|
|
250
|
+
upgrade_command_text = _format_upgrade_command(upgrade_command)
|
|
251
|
+
return Text.assemble(
|
|
252
|
+
(" ✨ ", "bold cyan"),
|
|
253
|
+
("Update available!", "bold"),
|
|
254
|
+
(f" {current_version} -> {latest_version}", "grey50"),
|
|
255
|
+
("\n\n Release notes: ", "grey50"),
|
|
256
|
+
(CHANGELOG_URL_EN, "grey50 underline"),
|
|
257
|
+
("\n\n ", ""),
|
|
258
|
+
("1", "bold cyan"),
|
|
259
|
+
(". Run ", ""),
|
|
260
|
+
("pythinker update", "bold cyan"),
|
|
261
|
+
("\n ", ""),
|
|
262
|
+
("2", "bold cyan"),
|
|
263
|
+
(". Or run ", ""),
|
|
264
|
+
(upgrade_command_text, "bold"),
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _render_update_banner(current_version: str, latest_version: str) -> None:
|
|
269
|
+
console.print()
|
|
270
|
+
console.print(_update_banner_text(current_version, latest_version))
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
async def do_update(*, print: bool = True, check_only: bool = False) -> UpdateResult:
|
|
274
|
+
async with _UPDATE_LOCK:
|
|
275
|
+
return await _do_update(print=print, check_only=check_only)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
async def _do_update(*, print: bool, check_only: bool) -> UpdateResult:
|
|
279
|
+
from pythinker_code.constant import VERSION as current_version
|
|
280
|
+
|
|
281
|
+
def _print(message: str) -> None:
|
|
282
|
+
if print:
|
|
283
|
+
console.print(message)
|
|
284
|
+
|
|
285
|
+
timeout = aiohttp.ClientTimeout(total=15, sock_connect=5, sock_read=10)
|
|
286
|
+
async with new_client_session(timeout=timeout) as session:
|
|
287
|
+
logger.info("Checking for updates...")
|
|
288
|
+
_print("Checking for updates...")
|
|
289
|
+
latest_version = await _get_latest_version(session)
|
|
290
|
+
if not latest_version:
|
|
291
|
+
_print("[red]Failed to check for updates.[/red]")
|
|
292
|
+
return UpdateResult.FAILED
|
|
293
|
+
|
|
294
|
+
logger.debug("Latest version: {latest_version}", latest_version=latest_version)
|
|
295
|
+
try:
|
|
296
|
+
LATEST_VERSION_FILE.write_text(latest_version, encoding="utf-8")
|
|
297
|
+
except OSError:
|
|
298
|
+
logger.exception("Failed to cache latest version:")
|
|
299
|
+
|
|
300
|
+
if semver_tuple(current_version) >= semver_tuple(latest_version):
|
|
301
|
+
logger.debug("Already up to date: {current_version}", current_version=current_version)
|
|
302
|
+
_print("[green]Already up to date.[/green]")
|
|
303
|
+
return UpdateResult.UP_TO_DATE
|
|
304
|
+
|
|
305
|
+
if check_only:
|
|
306
|
+
logger.info(
|
|
307
|
+
"Update available: current={current_version}, latest={latest_version}",
|
|
308
|
+
current_version=current_version,
|
|
309
|
+
latest_version=latest_version,
|
|
310
|
+
)
|
|
311
|
+
_print(f"[yellow]Update available: {latest_version}[/yellow]")
|
|
312
|
+
return UpdateResult.UPDATE_AVAILABLE
|
|
313
|
+
|
|
314
|
+
upgrade_command = _detect_upgrade_command()
|
|
315
|
+
upgrade_command_text = _format_upgrade_command(upgrade_command)
|
|
316
|
+
logger.info(
|
|
317
|
+
"Updating from {current_version} to {latest_version} via: {cmd}",
|
|
318
|
+
current_version=current_version,
|
|
319
|
+
latest_version=latest_version,
|
|
320
|
+
cmd=upgrade_command_text,
|
|
321
|
+
)
|
|
322
|
+
_print(f"Updating pythinker-code {current_version} → {latest_version}...")
|
|
323
|
+
_print(f"[grey50]Running: {upgrade_command_text}[/grey50]")
|
|
324
|
+
|
|
325
|
+
# On Windows, the running pythinker.exe holds an exclusive lock on its own
|
|
326
|
+
# binary. Any in-process `uv tool upgrade` / `pip install --upgrade` fails
|
|
327
|
+
# with `os error 32` (file in use). Spawn the upgrade in a detached console
|
|
328
|
+
# that waits a few seconds, then exit so the lock releases first.
|
|
329
|
+
if _is_windows() and _spawn_detached_windows_upgrade(upgrade_command):
|
|
330
|
+
_print(
|
|
331
|
+
"[yellow]Pythinker will exit so Windows can release the running executable.[/yellow]"
|
|
332
|
+
)
|
|
333
|
+
_print("[grey50]The upgrade will continue in a new console window.[/grey50]")
|
|
334
|
+
# Brief pause so the user can read the banner before the process dies.
|
|
335
|
+
await asyncio.sleep(1.0)
|
|
336
|
+
sys.exit(0)
|
|
337
|
+
|
|
338
|
+
try:
|
|
339
|
+
result = subprocess.run(upgrade_command)
|
|
340
|
+
except OSError as e:
|
|
341
|
+
logger.exception("Upgrade failed:")
|
|
342
|
+
_print(f"[red]Upgrade failed:[/red] {e}")
|
|
343
|
+
_print(f"Please run manually: {upgrade_command_text}")
|
|
344
|
+
return UpdateResult.FAILED
|
|
345
|
+
|
|
346
|
+
if result.returncode == 0:
|
|
347
|
+
_print("[green]Updated successfully![/green]")
|
|
348
|
+
_print("[yellow]Restart Pythinker CLI to use the new version.[/yellow]")
|
|
349
|
+
return UpdateResult.UPDATED
|
|
350
|
+
_print("[red]Upgrade failed. Please try running manually:[/red]")
|
|
351
|
+
_print(f" {upgrade_command_text}")
|
|
352
|
+
return UpdateResult.FAILED
|
|
@@ -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"]
|