mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-02-04 13:16:37 +08:00
Complete rewrite: original educational content only
- Remove all reverse-engineered Claude Code source code - Replace with 100% original educational content from mini-claude-code - Add clear disclaimer: independent project, not affiliated with Anthropic - 5 progressive agent implementations (v0-v4, ~1100 lines total) - Include agent-builder skill for teaching agent construction - Bilingual documentation (EN + ZH) This repository now focuses purely on teaching how modern AI agents work through original, from-scratch implementations. Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3a93602aac
commit
85f44c358a
4
.env.example
Normal file
4
.env.example
Normal file
@ -0,0 +1,4 @@
|
||||
# API Configuration
|
||||
ANTHROPIC_API_KEY=sk-xxx
|
||||
ANTHROPIC_BASE_URL=https://api.moonshot.cn/anthropic
|
||||
MODEL_NAME=kimi-k2-turbo-preview
|
||||
207
.gitignore
vendored
Normal file
207
.gitignore
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
#poetry.toml
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||
#pdm.lock
|
||||
#pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# pixi
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||
#pixi.lock
|
||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||
.pixi
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
# .vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Cursor
|
||||
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
||||
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
||||
# refer to https://docs.cursor.com/context/ignore-files
|
||||
.cursorignore
|
||||
.cursorindexingignore
|
||||
|
||||
# Marimo
|
||||
marimo/_static/
|
||||
marimo/_lsp/
|
||||
__marimo__/
|
||||
214
LICENSE
214
LICENSE
@ -1,201 +1,21 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
MIT License
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) 2024 shareAI Lab
|
||||
|
||||
1. Definitions.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
477
README.md
477
README.md
@ -1,391 +1,164 @@
|
||||
# Claude Code 逆向工程研究仓库
|
||||
# Learn Claude Code
|
||||
|
||||
Fellow us on X: https://x.com/baicai003
|
||||
注意:非100%准确!本仓库是我们研究学习“Agent模型公司是如何做Agent工程?”这件事的时候,一时兴起,借助claude code自行分析的claude code本身的混淆后代码,由于混淆代码很乱很散分析难度极大,CC多少会稍微有一些幻觉,该仓库仅作参考和学习!相关Claude code提示词在`work_doc_for_this`文件夹里,感兴趣的小伙伴可以自己复制提示词尝试复现!
|
||||
> **Disclaimer**: This is an independent educational project by [shareAI Lab](https://github.com/shareAI-lab). It is not affiliated with, endorsed by, or sponsored by Anthropic. "Claude Code" is a trademark of Anthropic.
|
||||
|
||||
**Learn how modern AI agents work by building one from scratch.**
|
||||
|
||||
## 通知
|
||||
<img width="360" height="360" alt="image" src="https://github.com/user-attachments/assets/10664e2e-36c8-4e29-b740-f5d06e71c1be" />
|
||||
<img width="360" height="360" alt="image" src="https://github.com/user-attachments/assets/9813fca0-a6dd-4813-972e-f9bf6d62add8" />
|
||||
[中文文档](./README_zh.md)
|
||||
|
||||
开源复现版会在这里发布:https://github.com/shareAI-lab/AgentKode
|
||||
相关解析文章已经二次核对提取整理后发布在ShareAI lab的官方公众号上.
|
||||
> Works with **[Kode CLI](https://github.com/shareAI-lab/Kode)**, **Claude Code**, **Cursor**, and any agent supporting the [Agent Skills Spec](https://github.com/anthropics/agent-skills).
|
||||
|
||||
## 📋 项目概述
|
||||
<img height="400" alt="demo" src="https://github.com/user-attachments/assets/0e1e31f8-064f-4908-92ce-121e2eb8d453" />
|
||||
|
||||
本仓库是对 Claude Code v1.0.33 进行深度逆向工程分析的完整研究资料库。通过对混淆源代码的系统性分析,我们揭示了这个现代AI编程助手的核心架构设计、实现机制和运行逻辑。
|
||||
## What is this?
|
||||
|
||||
项目包含超过 **50,000 行混淆代码** 的分析结果,覆盖了从UI交互到Agent核心引擎的完整技术栈。通过多轮迭代分析和严格验证,我们成功还原了Claude Code的核心技术架构,为理解现代AI Agent系统的工程实现提供了宝贵的技术参考。
|
||||
A progressive tutorial that demystifies AI coding agents like Kode, Claude Code, and Cursor Agent.
|
||||
|
||||
### 🎯 研究目标
|
||||
**5 versions, ~1100 lines total, each adding one concept:**
|
||||
|
||||
1. **深度理解** Claude Code的系统架构和核心机制
|
||||
2. **完整还原** 混淆代码背后的技术实现逻辑
|
||||
3. **严格验证** 分析结果的准确性和一致性
|
||||
4. **开源重建** 提供可复现的技术实现指南
|
||||
5. **知识共享** 为AI Agent系统设计提供参考
|
||||
| Version | Lines | What it adds | Core insight |
|
||||
|---------|-------|--------------|--------------|
|
||||
| [v0](./v0_bash_agent.py) | ~50 | 1 bash tool | Bash is all you need |
|
||||
| [v1](./v1_basic_agent.py) | ~200 | 4 core tools | Model as Agent |
|
||||
| [v2](./v2_todo_agent.py) | ~300 | Todo tracking | Explicit planning |
|
||||
| [v3](./v3_subagent.py) | ~450 | Subagents | Divide and conquer |
|
||||
| [v4](./v4_skills_agent.py) | ~550 | Skills | Domain expertise on-demand |
|
||||
|
||||
## 🔬 核心技术发现
|
||||
## Quick Start
|
||||
|
||||
### 🚀 突破性技术创新
|
||||
|
||||
#### 1. 实时 Steering 机制
|
||||
- **基础架构**: h2A 双重缓冲异步消息队列
|
||||
- **核心特性**: 零延迟消息传递,吞吐量 > 10,000 消息/秒
|
||||
- **实现原理**: Promise-based 异步迭代器 + 智能背压控制
|
||||
- **技术优势**: 真正的非阻塞异步处理,支持实时流式响应
|
||||
|
||||
#### 2. 分层多 Agent 架构
|
||||
- **主Agent**: nO 主循环引擎,负责核心任务调度
|
||||
- **SubAgent**: I2A 子任务代理,提供隔离执行环境
|
||||
- **Task Agent**: 专用任务处理器,支持并发执行
|
||||
- **权限隔离**: 每个Agent都有独立的权限范围和资源访问控制
|
||||
|
||||
#### 3. 智能上下文管理
|
||||
- **压缩算法**: 92% 阈值自动触发上下文压缩
|
||||
- **内存优化**: wU2 压缩器,智能保留关键信息
|
||||
- **持久化**: CLAUDE.md 文件作为长期记忆存储
|
||||
- **动态管理**: 根据Token使用情况动态调整上下文大小
|
||||
|
||||
#### 4. 强化安全防护
|
||||
- **6层权限验证**: 从UI到工具执行的完整安全链
|
||||
- **沙箱隔离**: 工具执行环境完全隔离
|
||||
- **输入验证**: 多层次的恶意输入检测和过滤
|
||||
- **权限网关**: 细粒度的功能权限控制
|
||||
|
||||
### 🏗️ 系统架构全景
|
||||
|
||||
```ascii
|
||||
Claude Code Agent 系统架构
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 用户交互层 │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ CLI接口 │ │ VSCode集成 │ │ Web界面 │ │
|
||||
│ │ (命令行) │ │ (插件) │ │ (浏览器) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└─────────────┬───────────────┬───────────────┬───────────────────┘
|
||||
│ │ │
|
||||
┌─────────────▼───────────────▼───────────────▼───────────────────┐
|
||||
│ Agent核心调度层 │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ nO主循环引擎 │◄────────┤ h2A消息队列 │ │
|
||||
│ │ (AgentLoop) │ │ (AsyncQueue) │ │
|
||||
│ │ • 任务调度 │ │ • 异步通信 │ │
|
||||
│ │ • 状态管理 │ │ • 流式处理 │ │
|
||||
│ │ • 异常处理 │ │ • 背压控制 │ │
|
||||
│ └─────────────────┘ └─────────────────┘ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ wu会话流生成器 │ │ wU2消息压缩器 │ │
|
||||
│ │ (StreamGen) │ │ (Compressor) │ │
|
||||
│ │ • 实时响应 │ │ • 智能压缩 │ │
|
||||
│ │ • 流式输出 │ │ • 上下文优化 │ │
|
||||
│ └─────────────────┘ └─────────────────┘ │
|
||||
└─────────────┬───────────────────────┬─────────────────────────────┘
|
||||
│ │
|
||||
┌─────────────▼───────────────────────▼─────────────────────────────┐
|
||||
│ 工具执行与管理层 │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────────┐│
|
||||
│ │MH1工具引擎 │ │UH1并发控制│ │SubAgent管理│ │ 权限验证网关 ││
|
||||
│ │(ToolEngine)│ │(Scheduler) │ │(TaskAgent) │ │ (PermissionGW) ││
|
||||
│ │• 工具发现 │ │• 并发限制 │ │• 任务隔离 │ │ • 权限检查 ││
|
||||
│ │• 参数验证 │ │• 负载均衡 │ │• 错误恢复 │ │ • 安全审计 ││
|
||||
│ │• 执行调度 │ │• 资源管理 │ │• 状态同步 │ │ • 访问控制 ││
|
||||
│ └────────────┘ └────────────┘ └────────────┘ └─────────────────┘│
|
||||
│ │ │ │ │ │
|
||||
│ ▼ ▼ ▼ ▼ │
|
||||
│ ┌────────────────────────────────────────────────────────────────┐│
|
||||
│ │ 工具生态系统 ││
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐││
|
||||
│ │ │ 文件操作工具│ │ 搜索发现工具│ │ 任务管理工具│ │ 系统执行工具│││
|
||||
│ │ │• Read/Write │ │• Glob/Grep │ │• Todo系统 │ │• Bash执行 │││
|
||||
│ │ │• Edit/Multi │ │• 模式匹配 │ │• 状态跟踪 │ │• 命令调用 │││
|
||||
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘││
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐││
|
||||
│ │ │ 网络交互工具│ │ 特殊功能工具│ │ MCP集成工具 │ │ 开发者工具 │││
|
||||
│ │ │• WebFetch │ │• Plan模式 │ │• 协议支持 │ │• 代码诊断 │││
|
||||
│ │ │• WebSearch │ │• 退出计划 │ │• 服务发现 │ │• 性能监控 │││
|
||||
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘││
|
||||
│ └────────────────────────────────────────────────────────────────┘│
|
||||
└─────────────┬─────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────▼─────────────────────────────────────────────────────┐
|
||||
│ 存储与持久化层 │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │短期记忆存储 │ │中期压缩历史 │ │长期持久存储 │ │状态缓存系统 │ │
|
||||
│ │(Messages) │ │(Compressed) │ │(CLAUDE.md) │ │(StateCache) │ │
|
||||
│ │• 当前会话 │ │• 历史摘要 │ │• 用户偏好 │ │• 工具状态 │ │
|
||||
│ │• 上下文队列 │ │• 关键信息 │ │• 配置信息 │ │• 执行历史 │ │
|
||||
│ │• 临时缓存 │ │• 压缩算法 │ │• 持久化机制 │ │• 性能指标 │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 📁 仓库结构详解
|
||||
|
||||
### 📂 主要目录组织
|
||||
|
||||
```
|
||||
about_claude_code/
|
||||
├── claude_code_v_1.0.33/ # v1.0.33版本完整分析工作区
|
||||
│ └── stage1_analysis_workspace/ # 第一阶段分析结果
|
||||
│ ├── Claude_Code_Agent系统完整技术解析.md # 核心技术解析文档
|
||||
│ ├── chunks/ # 代码分块文件 (102个)
|
||||
│ │ ├── chunks.1.mjs ~ chunks.102.mjs # 去混淆后的代码块
|
||||
│ │ ├── chunks.index.json # 分块索引文件
|
||||
│ │ └── cli.chunks.mjs # CLI主文件分块
|
||||
│ ├── analysis_results/ # 分析结果汇总
|
||||
│ │ └── merged-chunks/ # 合并优化的代码块
|
||||
│ ├── scripts/ # 分析脚本工具集
|
||||
│ │ ├── beautify.js # 代码美化脚本
|
||||
│ │ ├── split.js # 代码分割脚本
|
||||
│ │ ├── merge-again.js # 代码合并脚本
|
||||
│ │ └── llm.js # LLM分析接口
|
||||
│ ├── docs/ # 详细技术文档集
|
||||
│ └── source/ # 原始源码文件
|
||||
├── work_doc_for_this/ # 项目工作文档
|
||||
│ ├── CLAUDE_CODE_REVERSE_SOP.md # 逆向工程标准作业程序
|
||||
│ ├── stage_1_analysis_sop.md # 第一阶段分析方法论
|
||||
│ └── stage_2_reconstruction_sop.md # 第二阶段重建方法论
|
||||
├── LICENSE # 开源许可证
|
||||
└── README.md # 项目说明文档
|
||||
```
|
||||
|
||||
### 📋 核心技术文档
|
||||
|
||||
#### 🔧 核心机制深度分析
|
||||
- **`实时Steering机制完整技术文档.md`** - h2A异步消息队列的完整实现原理
|
||||
- **`Edit工具强制读取机制完整技术文档.md`** - Edit工具的文件读取验证机制
|
||||
- **`分层多Agent架构完整技术文档.md`** - 多层Agent系统的架构设计
|
||||
- **`Plan模式机制完整技术文档.md`** - Plan模式的触发和执行机制
|
||||
- **`Claude_Code_Sandbox_Mechanism_Deep_Analysis.md`** - 沙箱安全机制深度分析
|
||||
- **`Claude_Code_MCP_Deep_Analysis.md`** - MCP协议集成机制分析
|
||||
|
||||
#### 📊 验证与交叉分析报告
|
||||
- **`FINAL_VALIDATION_REPORT.md`** - 最终综合验证报告 (95%准确性)
|
||||
- **`CROSS_VALIDATION_REPORT.md`** - 跨文档一致性验证
|
||||
- **`Claude_Code_关键机制严格验证报告.md`** - 关键机制的源码级验证
|
||||
- **`Claude_Code_最终验证后的完整认知更新.md`** - 完整认知框架更新
|
||||
|
||||
#### 🏗️ 开源重建指南
|
||||
- **`Open-Claude-Code/`** - 开源重建项目模板
|
||||
- 完整的TypeScript实现框架
|
||||
- 核心组件接口定义
|
||||
- 测试用例和基准测试
|
||||
- **`Demo_Repo/`** - 演示实现仓库
|
||||
- **`施工步骤/`** - 分阶段实施指南
|
||||
- 阶段1: 项目初始化和基础架构
|
||||
- 阶段2: Agent核心引擎和工具系统
|
||||
- 阶段3: 高级特性和交互模式
|
||||
- 阶段4: MCP集成和扩展系统
|
||||
- 阶段5: 测试优化和发布准备
|
||||
|
||||
#### 🔍 特殊机制分析
|
||||
- **`Claude_Code_UI_Component_System_Deep_Analysis.md`** - UI组件系统分析
|
||||
- **`Claude_Code_Image_Processing_and_LLM_API_Deep_Analysis.md`** - 图像处理和LLM API分析
|
||||
- **`Claude Code隐藏特性和高级机制深度挖掘.md`** - 隐藏特性发现
|
||||
- **`Claude_Code_IDE_Connection_and_Interaction_Deep_Analysis.md`** - IDE集成机制
|
||||
|
||||
## 🛠️ 分析方法论详解
|
||||
|
||||
### 第一阶段:静态代码分析
|
||||
|
||||
#### 1. 代码预处理 (Pre-processing)
|
||||
```bash
|
||||
# 代码美化和格式化
|
||||
node scripts/beautify.js source/cli.mjs
|
||||
pip install anthropic python-dotenv
|
||||
|
||||
# 智能分块处理 (102个块)
|
||||
node scripts/split.js cli.beautify.mjs
|
||||
# Configure your API
|
||||
cp .env.example .env
|
||||
# Edit .env with your API key
|
||||
|
||||
# 生成分块索引
|
||||
node scripts/generate-index.js chunks/
|
||||
# Run any version
|
||||
python v0_bash_agent.py # Minimal
|
||||
python v1_basic_agent.py # Core agent loop
|
||||
python v2_todo_agent.py # + Todo planning
|
||||
python v3_subagent.py # + Subagents
|
||||
python v4_skills_agent.py # + Skills
|
||||
```
|
||||
|
||||
#### 2. LLM辅助分析 (LLM-Assisted Analysis)
|
||||
- **模式识别**: 使用GPT-4识别代码模式和架构
|
||||
- **函数分析**: 逐函数解析混淆后的逻辑
|
||||
- **依赖映射**: 构建模块间的依赖关系图
|
||||
- **API追踪**: 追踪关键API的调用链
|
||||
## The Core Pattern
|
||||
|
||||
#### 3. 交叉验证 (Cross-Validation)
|
||||
- **多轮迭代**: 3轮深度分析确保准确性
|
||||
- **一致性检查**: 跨文档技术描述的一致性验证
|
||||
- **源码对照**: 每个技术断言都有源码位置支持
|
||||
Every coding agent is just this loop:
|
||||
|
||||
### 第二阶段:动态行为验证
|
||||
|
||||
#### 1. 运行时分析 (Runtime Analysis)
|
||||
- **函数调用追踪**: 记录关键函数的执行路径
|
||||
- **状态变化监控**: 监控系统状态的变化过程
|
||||
- **性能指标收集**: 收集内存使用和执行时间数据
|
||||
|
||||
#### 2. 集成测试 (Integration Testing)
|
||||
- **组件交互验证**: 验证组件间的交互逻辑
|
||||
- **边界条件测试**: 测试系统在极限条件下的行为
|
||||
- **错误恢复验证**: 验证系统的错误处理和恢复机制
|
||||
|
||||
## 🔍 详细研究范围
|
||||
|
||||
### 🎯 已分析的核心组件
|
||||
|
||||
#### 1. Agent循环系统 (Agent Loop System)
|
||||
- **nO主循环引擎**:
|
||||
- 异步Generator实现的核心调度器
|
||||
- 支持中断和恢复的执行控制
|
||||
- 多层异常处理和错误恢复
|
||||
- **消息处理管道**:
|
||||
- 实时消息队列处理
|
||||
- 消息优先级和调度算法
|
||||
- 背压控制和流量管理
|
||||
|
||||
#### 2. 工具执行框架 (Tool Execution Framework)
|
||||
- **6阶段执行管道**:
|
||||
1. 工具发现和注册
|
||||
2. 参数验证和类型检查
|
||||
3. 权限验证和安全检查
|
||||
4. 资源分配和环境准备
|
||||
5. 并发执行和状态监控
|
||||
6. 结果收集和清理回收
|
||||
- **并发控制**: 最大10并发,智能负载均衡
|
||||
- **错误隔离**: 每个工具独立的错误处理域
|
||||
|
||||
#### 3. 内存与上下文管理 (Memory & Context Management)
|
||||
- **智能压缩算法**:
|
||||
- 92%阈值自动触发压缩
|
||||
- 保留关键信息的压缩策略
|
||||
- 分层存储和检索机制
|
||||
- **Token优化**:
|
||||
- 动态上下文窗口调整
|
||||
- 重要性评分和内容筛选
|
||||
- 历史对话的智能摘要
|
||||
|
||||
#### 4. 安全防护框架 (Security Framework)
|
||||
- **6层权限验证**:
|
||||
1. UI输入验证层
|
||||
2. 消息路由验证层
|
||||
3. 工具调用验证层
|
||||
4. 参数内容验证层
|
||||
5. 系统资源访问层
|
||||
6. 输出内容过滤层
|
||||
- **沙箱隔离**: 完全隔离的工具执行环境
|
||||
- **恶意输入检测**: 多种模式的恶意内容识别
|
||||
|
||||
#### 5. 用户界面集成 (UI Integration)
|
||||
- **React组件系统**: 模块化的UI组件架构
|
||||
- **实时更新机制**: WebSocket-based的实时通信
|
||||
- **事件处理系统**: 12种不同类型的UI事件处理
|
||||
|
||||
### 📊 验证结果统计
|
||||
|
||||
| 验证维度 | 准确性 | 覆盖范围 | 置信度 |
|
||||
|---------|--------|----------|--------|
|
||||
| 核心架构设计 | 95% | 完整覆盖 | 高 |
|
||||
| 关键机制实现 | 98% | 完整覆盖 | 极高 |
|
||||
| API调用链路 | 92% | 85%覆盖 | 高 |
|
||||
| 安全机制验证 | 90% | 主要功能 | 中高 |
|
||||
| 性能参数验证 | 88% | 关键指标 | 中高 |
|
||||
| UI交互机制 | 85% | 主要流程 | 中 |
|
||||
|
||||
### 🔬 创新技术发现
|
||||
|
||||
#### 1. 实时Steering技术突破
|
||||
这是我们发现的最重要的技术创新。h2A类实现了真正的零延迟异步消息传递:
|
||||
|
||||
```javascript
|
||||
// 核心双重缓冲机制伪代码
|
||||
class h2AAsyncMessageQueue {
|
||||
enqueue(message) {
|
||||
// 策略1: 零延迟路径 - 直接传递给等待的读取者
|
||||
if (this.readResolve) {
|
||||
this.readResolve({ done: false, value: message });
|
||||
this.readResolve = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 策略2: 缓冲路径 - 存储到循环缓冲区
|
||||
this.primaryBuffer.push(message);
|
||||
this.processBackpressure();
|
||||
}
|
||||
}
|
||||
```python
|
||||
while True:
|
||||
response = model(messages, tools)
|
||||
if response.stop_reason != "tool_use":
|
||||
return response.text
|
||||
results = execute(response.tool_calls)
|
||||
messages.append(results)
|
||||
```
|
||||
|
||||
#### 2. 智能上下文压缩算法
|
||||
基于重要性评分的智能压缩,保留92%的关键信息:
|
||||
That's it. The model calls tools until done. Everything else is refinement.
|
||||
|
||||
```javascript
|
||||
// 压缩触发逻辑
|
||||
if (tokenUsage > CONTEXT_THRESHOLD * 0.92) {
|
||||
const compressedContext = await wU2Compressor.compress({
|
||||
messages: currentContext,
|
||||
preserveRatio: 0.3,
|
||||
importanceScoring: true
|
||||
});
|
||||
}
|
||||
## File Structure
|
||||
|
||||
```
|
||||
learn-claude-code/
|
||||
├── v0_bash_agent.py # ~50 lines: 1 tool, recursive subagents
|
||||
├── v0_bash_agent_mini.py # ~16 lines: extreme compression
|
||||
├── v1_basic_agent.py # ~200 lines: 4 tools, core loop
|
||||
├── v2_todo_agent.py # ~300 lines: + TodoManager
|
||||
├── v3_subagent.py # ~450 lines: + Task tool, agent registry
|
||||
├── v4_skills_agent.py # ~550 lines: + Skill tool, SkillLoader
|
||||
├── skills/ # Example skills (for learning)
|
||||
└── docs/ # Detailed explanations (EN + ZH)
|
||||
```
|
||||
|
||||
## 🎯 应用场景与价值
|
||||
## Using the Agent Builder Skill
|
||||
|
||||
### 📚 教育研究价值
|
||||
1. **AI Agent架构学习**: 完整的现代AI Agent系统实现案例
|
||||
2. **异步编程模式**: 高性能异步系统的设计参考
|
||||
3. **安全架构设计**: 多层安全防护的实现方案
|
||||
4. **性能优化技巧**: 内存管理和并发控制的最佳实践
|
||||
This repository includes a meta-skill that teaches agents how to build agents:
|
||||
|
||||
### 🏗️ 系统设计参考
|
||||
1. **架构模式借鉴**: 分层架构和组件化设计
|
||||
2. **工具系统设计**: 插件化工具执行框架
|
||||
3. **状态管理方案**: 分布式状态同步机制
|
||||
4. **错误处理策略**: 多层错误恢复机制
|
||||
```bash
|
||||
# Scaffold a new agent project
|
||||
python skills/agent-builder/scripts/init_agent.py my-agent
|
||||
|
||||
### 🔒 安全分析应用
|
||||
1. **安全机制审计**: 多层权限验证的实现分析
|
||||
2. **沙箱技术研究**: 隔离执行环境的设计原理
|
||||
3. **输入验证模式**: 恶意输入检测和过滤技术
|
||||
4. **权限控制系统**: 细粒度权限管理的实现
|
||||
# Or with specific complexity level
|
||||
python skills/agent-builder/scripts/init_agent.py my-agent --level 0 # Minimal
|
||||
python skills/agent-builder/scripts/init_agent.py my-agent --level 1 # 4 tools (default)
|
||||
```
|
||||
|
||||
### 🚀 开源开发指导
|
||||
1. **项目架构搭建**: 基于分析结果的架构设计
|
||||
2. **核心组件实现**: 关键组件的开源实现指南
|
||||
3. **测试策略制定**: 基于分析的测试用例设计
|
||||
4. **性能优化指导**: 性能瓶颈的识别和优化方案
|
||||
### Install Skills for Production Use
|
||||
|
||||
## 🤝 贡献指南
|
||||
```bash
|
||||
# Kode CLI (recommended)
|
||||
kode plugins install https://github.com/shareAI-lab/shareAI-skills
|
||||
|
||||
### 📝 贡献类型
|
||||
1. **准确性改进**: 修正分析中的错误或不准确之处
|
||||
2. **深度分析**: 对现有分析的进一步深化
|
||||
3. **新发现补充**: 添加新发现的技术细节
|
||||
4. **文档完善**: 改进文档结构和可读性
|
||||
5. **代码实现**: 基于分析的开源实现
|
||||
# Claude Code
|
||||
claude plugins install https://github.com/shareAI-lab/shareAI-skills
|
||||
```
|
||||
|
||||
### ✅ 贡献标准
|
||||
- 所有技术断言必须有源码位置支持
|
||||
- 新增分析需要经过交叉验证
|
||||
- 文档格式需要保持一致性
|
||||
- 代码实现需要通过测试验证
|
||||
See [shareAI-skills](https://github.com/shareAI-lab/shareAI-skills) for the full collection of production-ready skills.
|
||||
|
||||
## ⚖️ 免责声明
|
||||
## Key Concepts
|
||||
|
||||
本仓库专门用于教育和学术研究目的。所有分析工作基于公开可获得的混淆代码,旨在理解现代AI系统的设计模式和架构原理。
|
||||
### v0: Bash is All You Need
|
||||
One tool. Recursive self-calls for subagents. Proves the core is tiny.
|
||||
|
||||
**重要说明**:
|
||||
- 本项目不涉及任何恶意逆向工程活动
|
||||
- 所有分析都在合法合规的框架内进行
|
||||
- 研究成果仅用于学术交流和技术学习
|
||||
- 不建议将分析结果用于商业竞争目的
|
||||
### v1: Model as Agent
|
||||
4 tools (bash, read, write, edit). The complete agent in one function.
|
||||
|
||||
## 📄 开源许可
|
||||
### v2: Structured Planning
|
||||
Todo tool makes plans explicit. Constraints enable complex tasks.
|
||||
|
||||
本项目采用Apache License Version 2.0许可证开源 - 详见 [LICENSE](LICENSE) 文件。
|
||||
### v3: Subagent Mechanism
|
||||
Task tool spawns isolated child agents. Context stays clean.
|
||||
|
||||
### v4: Skills Mechanism
|
||||
SKILL.md files provide domain expertise on-demand. Knowledge as a first-class citizen.
|
||||
|
||||
## Deep Dives
|
||||
|
||||
**Technical tutorials (docs/):**
|
||||
|
||||
| English | 中文 |
|
||||
|---------|------|
|
||||
| [v0: Bash is All You Need](./docs/v0-bash-is-all-you-need.md) | [v0: Bash 就是一切](./docs/v0-Bash就是一切.md) |
|
||||
| [v1: Model as Agent](./docs/v1-model-as-agent.md) | [v1: 模型即代理](./docs/v1-模型即代理.md) |
|
||||
| [v2: Structured Planning](./docs/v2-structured-planning.md) | [v2: 结构化规划](./docs/v2-结构化规划.md) |
|
||||
| [v3: Subagent Mechanism](./docs/v3-subagent-mechanism.md) | [v3: 子代理机制](./docs/v3-子代理机制.md) |
|
||||
| [v4: Skills Mechanism](./docs/v4-skills-mechanism.md) | [v4: Skills 机制](./docs/v4-Skills机制.md) |
|
||||
|
||||
**Original articles (articles/) - Chinese only, social media style:**
|
||||
- [v0文章](./articles/v0文章.md) | [v1文章](./articles/v1文章.md) | [v2文章](./articles/v2文章.md) | [v3文章](./articles/v3文章.md) | [v4文章](./articles/v4文章.md)
|
||||
- [上下文缓存经济学](./articles/上下文缓存经济学.md) - Context Caching Economics for Agent Developers
|
||||
|
||||
## Related Projects
|
||||
|
||||
| Repository | Purpose |
|
||||
|------------|---------|
|
||||
| [Kode](https://github.com/shareAI-lab/Kode) | Full-featured open source agent CLI (production) |
|
||||
| [shareAI-skills](https://github.com/shareAI-lab/shareAI-skills) | Production-ready skills for AI agents |
|
||||
| [Agent Skills Spec](https://github.com/anthropics/agent-skills) | Official specification |
|
||||
|
||||
### Use as Template
|
||||
|
||||
Fork and customize for your own agent projects:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/shareAI-lab/learn-claude-code
|
||||
cd learn-claude-code
|
||||
# Start from any version level
|
||||
cp v1_basic_agent.py my_agent.py
|
||||
```
|
||||
|
||||
## Philosophy
|
||||
|
||||
> The model is 80%. Code is 20%.
|
||||
|
||||
Modern agents like Kode and Claude Code work not because of clever engineering, but because the model is trained to be an agent. Our job is to give it tools and stay out of the way.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2025年 6 月 29
|
||||
**项目灵感来源**: [claude-code-reverse](https://github.com/Yuyz0112/claude-code-reverse)
|
||||
**维护团队**: ShareAI-Lab
|
||||
**Model as Agent. That's the whole secret.**
|
||||
|
||||
[@baicai003](https://x.com/baicai003)
|
||||
|
||||
164
README_zh.md
Normal file
164
README_zh.md
Normal file
@ -0,0 +1,164 @@
|
||||
# Learn Claude Code
|
||||
|
||||
> **声明**: 这是 [shareAI Lab](https://github.com/shareAI-lab) 的独立教育项目,与 Anthropic 无关,未获其认可或赞助。"Claude Code" 是 Anthropic 的商标。
|
||||
|
||||
**从零开始构建你自己的 AI Agent。**
|
||||
|
||||
[English](./README.md)
|
||||
|
||||
> 兼容 **[Kode CLI](https://github.com/shareAI-lab/Kode)**、**Claude Code**、**Cursor**,以及任何支持 [Agent Skills Spec](https://github.com/anthropics/agent-skills) 的 Agent。
|
||||
|
||||
<img height="400" alt="demo" src="https://github.com/user-attachments/assets/0e1e31f8-064f-4908-92ce-121e2eb8d453" />
|
||||
|
||||
## 这是什么?
|
||||
|
||||
一个渐进式教程,揭开 Kode、Claude Code、Cursor Agent 等 AI Agent 的神秘面纱。
|
||||
|
||||
**5 个版本,总共约 1100 行,每个版本只添加一个概念:**
|
||||
|
||||
| 版本 | 行数 | 新增内容 | 核心洞察 |
|
||||
|------|------|---------|---------|
|
||||
| [v0](./v0_bash_agent.py) | ~50 | 1 个 bash 工具 | Bash 就是一切 |
|
||||
| [v1](./v1_basic_agent.py) | ~200 | 4 个核心工具 | 模型即代理 |
|
||||
| [v2](./v2_todo_agent.py) | ~300 | Todo 追踪 | 显式规划 |
|
||||
| [v3](./v3_subagent.py) | ~450 | 子代理 | 分而治之 |
|
||||
| [v4](./v4_skills_agent.py) | ~550 | Skills | 按需领域专业 |
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
pip install anthropic python-dotenv
|
||||
|
||||
# 配置 API
|
||||
cp .env.example .env
|
||||
# 编辑 .env 填入你的 API key
|
||||
|
||||
# 运行任意版本
|
||||
python v0_bash_agent.py # 极简版
|
||||
python v1_basic_agent.py # 核心 Agent 循环
|
||||
python v2_todo_agent.py # + Todo 规划
|
||||
python v3_subagent.py # + 子代理
|
||||
python v4_skills_agent.py # + Skills
|
||||
```
|
||||
|
||||
## 核心模式
|
||||
|
||||
每个 Agent 都只是这个循环:
|
||||
|
||||
```python
|
||||
while True:
|
||||
response = model(messages, tools)
|
||||
if response.stop_reason != "tool_use":
|
||||
return response.text
|
||||
results = execute(response.tool_calls)
|
||||
messages.append(results)
|
||||
```
|
||||
|
||||
就这样。模型持续调用工具直到完成。其他一切都是精化。
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
learn-claude-code/
|
||||
├── v0_bash_agent.py # ~50 行: 1 个工具,递归子代理
|
||||
├── v0_bash_agent_mini.py # ~16 行: 极限压缩
|
||||
├── v1_basic_agent.py # ~200 行: 4 个工具,核心循环
|
||||
├── v2_todo_agent.py # ~300 行: + TodoManager
|
||||
├── v3_subagent.py # ~450 行: + Task 工具,代理注册表
|
||||
├── v4_skills_agent.py # ~550 行: + Skill 工具,SkillLoader
|
||||
├── skills/ # 示例 Skills(用于学习)
|
||||
└── docs/ # 详细文档 (中英双语)
|
||||
```
|
||||
|
||||
## 使用 Agent Builder Skill
|
||||
|
||||
本仓库包含一个元技能,教 Agent 如何构建 Agent:
|
||||
|
||||
```bash
|
||||
# 脚手架生成新 Agent 项目
|
||||
python skills/agent-builder/scripts/init_agent.py my-agent
|
||||
|
||||
# 或指定复杂度级别
|
||||
python skills/agent-builder/scripts/init_agent.py my-agent --level 0 # 极简
|
||||
python skills/agent-builder/scripts/init_agent.py my-agent --level 1 # 4 工具 (默认)
|
||||
```
|
||||
|
||||
### 生产环境安装 Skills
|
||||
|
||||
```bash
|
||||
# Kode CLI(推荐)
|
||||
kode plugins install https://github.com/shareAI-lab/shareAI-skills
|
||||
|
||||
# Claude Code
|
||||
claude plugins install https://github.com/shareAI-lab/shareAI-skills
|
||||
```
|
||||
|
||||
详见 [shareAI-skills](https://github.com/shareAI-lab/shareAI-skills) 获取完整的生产就绪 skills 集合。
|
||||
|
||||
## 核心概念
|
||||
|
||||
### v0: Bash 就是一切
|
||||
一个工具。递归自调用实现子代理。证明核心是极小的。
|
||||
|
||||
### v1: 模型即代理
|
||||
4 个工具 (bash, read, write, edit)。完整 Agent 在一个函数里。
|
||||
|
||||
### v2: 结构化规划
|
||||
Todo 工具让计划显式化。约束赋能复杂任务。
|
||||
|
||||
### v3: 子代理机制
|
||||
Task 工具生成隔离的子代理。上下文保持干净。
|
||||
|
||||
### v4: Skills 机制
|
||||
SKILL.md 文件按需提供领域专业知识。知识作为一等公民。
|
||||
|
||||
## 深入阅读
|
||||
|
||||
**技术教程 (docs/):**
|
||||
|
||||
| English | 中文 |
|
||||
|---------|------|
|
||||
| [v0: Bash is All You Need](./docs/v0-bash-is-all-you-need.md) | [v0: Bash 就是一切](./docs/v0-Bash就是一切.md) |
|
||||
| [v1: Model as Agent](./docs/v1-model-as-agent.md) | [v1: 模型即代理](./docs/v1-模型即代理.md) |
|
||||
| [v2: Structured Planning](./docs/v2-structured-planning.md) | [v2: 结构化规划](./docs/v2-结构化规划.md) |
|
||||
| [v3: Subagent Mechanism](./docs/v3-subagent-mechanism.md) | [v3: 子代理机制](./docs/v3-子代理机制.md) |
|
||||
| [v4: Skills Mechanism](./docs/v4-skills-mechanism.md) | [v4: Skills 机制](./docs/v4-Skills机制.md) |
|
||||
|
||||
**原创文章 (articles/) - 公众号风格:**
|
||||
- [v0文章](./articles/v0文章.md) | [v1文章](./articles/v1文章.md) | [v2文章](./articles/v2文章.md) | [v3文章](./articles/v3文章.md) | [v4文章](./articles/v4文章.md)
|
||||
- [上下文缓存经济学](./articles/上下文缓存经济学.md) - Agent 开发者必知的成本优化指南
|
||||
|
||||
## 相关项目
|
||||
|
||||
| 仓库 | 用途 |
|
||||
|------|------|
|
||||
| [Kode](https://github.com/shareAI-lab/Kode) | 全功能开源 Agent CLI(生产环境) |
|
||||
| [shareAI-skills](https://github.com/shareAI-lab/shareAI-skills) | 生产就绪的 AI Agent Skills |
|
||||
| [Agent Skills Spec](https://github.com/anthropics/agent-skills) | 官方规范 |
|
||||
|
||||
### 作为模板
|
||||
|
||||
Fork 并自定义为你自己的 Agent 项目:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/shareAI-lab/learn-claude-code
|
||||
cd learn-claude-code
|
||||
# 从任意版本级别开始
|
||||
cp v1_basic_agent.py my_agent.py
|
||||
```
|
||||
|
||||
## 设计哲学
|
||||
|
||||
> 模型是 80%,代码是 20%。
|
||||
|
||||
Kode 和 Claude Code 等现代 Agent 能工作,不是因为巧妙的工程,而是因为模型被训练成了 Agent。我们的工作就是给它工具,然后闪开。
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
---
|
||||
|
||||
**模型即代理。这就是全部秘密。**
|
||||
|
||||
[@baicai003](https://x.com/baicai003)
|
||||
215
articles/v0文章.md
Normal file
215
articles/v0文章.md
Normal file
@ -0,0 +1,215 @@
|
||||
# mini Claude Code v0:Bash is All You Need
|
||||
|
||||
当我们完成 v1、v2、v3 三个版本后,一个问题浮出水面:**Agent 的本质到底是什么?**
|
||||
|
||||
v1 用 400 行代码证明了"模型即代理";v2 加入 Todo 实现结构化规划;v3 引入 Task 工具支持子代理。功能越来越强大,但代码也越来越多。能不能反过来走——**用最少的代码,保留最核心的能力?**
|
||||
|
||||
答案是 v0:**20 行核心代码,1 个 bash 工具,却拥有完整的 Agent 能力——包括子代理。**
|
||||
|
||||
本次完整教学代码地址:
|
||||
https://github.com/shareAI-lab/mini_claude_code
|
||||
|
||||
## 1. 核心洞察:Bash 是meta接口、万能工具箱
|
||||
|
||||
Unix 哲学告诉我们:一切皆文件,一切皆可管道。而 bash 是这个哲学的入口:
|
||||
|
||||
| 你需要 | bash 命令 |
|
||||
|--------|-----------|
|
||||
| 读文件 | `cat`, `head`, `tail`, `grep` `...` |
|
||||
| 写文件 | `echo '...' > file`, `sed -i`, `...` |
|
||||
| 搜索 | `find`, `grep`, `rg` |
|
||||
| 执行 | `python`, `npm`, `make` |
|
||||
| **子代理** | `python v0_bash_agent.py "task"`|
|
||||
|
||||
最后一行是关键:**通过 bash 调用自身,就实现了子代理机制**。不需要 Task 工具,不需要 Agent Registry,不需要任何额外代码。
|
||||
|
||||
## 2. 完整版代码
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
"""v0_bash_agent.py - 极简 Claude Code (20行核心) | Bash is All You Need"""
|
||||
from anthropic import Anthropic
|
||||
import subprocess, sys, os
|
||||
|
||||
client = Anthropic(api_key="your-api-key", base_url="https://api.moonshot.cn/anthropic")
|
||||
TOOL = [{"name": "bash", "description": """Execute shell command. Common patterns:
|
||||
- Read: cat/head/tail, grep/find/rg/ls, wc -l
|
||||
- Write: echo 'content' > file, sed -i 's/old/new/g' file
|
||||
- Subagent: python v0_bash_agent.py 'task description' (spawns isolated agent, returns summary)""",
|
||||
"input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}}]
|
||||
SYSTEM = f"""You are a CLI agent at {os.getcwd()}. Solve problems using bash commands.
|
||||
|
||||
Rules:
|
||||
- Prefer tools over prose. Act first, explain briefly after.
|
||||
- Read files: cat, grep, find, rg, ls, head, tail
|
||||
- Write files: echo '...' > file, sed -i, or cat << 'EOF' > file
|
||||
- Subagent: For complex subtasks, spawn a subagent to keep context clean:
|
||||
python v0_bash_agent.py "explore src/ and summarize the architecture"
|
||||
|
||||
When to use subagent:
|
||||
- Task requires reading many files (isolate the exploration)
|
||||
- Task is independent and self-contained
|
||||
- You want to avoid polluting current conversation with intermediate details
|
||||
|
||||
The subagent runs in isolation and returns only its final summary."""
|
||||
|
||||
def chat(prompt, history=[]):
|
||||
history.append({"role": "user", "content": prompt})
|
||||
while True:
|
||||
r = client.messages.create(model="kimi-k2-turbo-preview", system=SYSTEM, messages=history, tools=TOOL, max_tokens=8000)
|
||||
content = [{"type": b.type, **({"text": b.text} if hasattr(b, "text") else {"id": b.id, "name": b.name, "input": b.input})} for b in r.content]
|
||||
history.append({"role": "assistant", "content": content})
|
||||
if r.stop_reason != "tool_use":
|
||||
return "".join(b.text for b in r.content if hasattr(b, "text"))
|
||||
results = []
|
||||
for b in [x for x in r.content if x.type == "tool_use"]:
|
||||
print(f"\033[33m$ {b.input['command']}\033[0m")
|
||||
try: out = subprocess.run(b.input["command"], shell=True, capture_output=True, text=True, timeout=300, cwd=os.getcwd())
|
||||
except subprocess.TimeoutExpired: out = type('', (), {'stdout': '', 'stderr': '(timeout)'})()
|
||||
print(out.stdout + out.stderr or "(empty)")
|
||||
results.append({"type": "tool_result", "tool_use_id": b.id, "content": (out.stdout + out.stderr)[:50000]})
|
||||
history.append({"role": "user", "content": results})
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1: print(chat(sys.argv[1])) # 子代理模式
|
||||
else:
|
||||
h = []
|
||||
while (q := input("\033[36m>> \033[0m")) not in ("q", "exit", ""): print(chat(q, h)) # 交互模式
|
||||
```
|
||||
|
||||
## 3. 子代理机制:递归的优雅
|
||||
|
||||
v3 用了 ~150 行代码实现子代理:Agent Registry、Task 工具、工具过滤、进度显示...
|
||||
|
||||
v0 只需要一行 bash 命令:
|
||||
|
||||
```bash
|
||||
python v0_bash_agent.py "explore the codebase and summarize"
|
||||
```
|
||||
|
||||
**为什么这能工作?**
|
||||
|
||||
1. **进程隔离** = 上下文隔离
|
||||
- 子进程有独立的 `history=[]`
|
||||
- 不会污染父进程的对话历史
|
||||
|
||||
2. **stdout** = 结果返回
|
||||
- 子代理的 `print(chat(...))` 输出到 stdout
|
||||
- 父代理通过 `subprocess` 捕获,作为工具结果
|
||||
|
||||
3. **递归调用** = 无限嵌套
|
||||
- 子代理可以再调用子代理
|
||||
- 天然支持任意深度的任务分解
|
||||
|
||||
```sh
|
||||
主代理
|
||||
└─ bash: python v0_bash_agent.py "分析架构"
|
||||
└─ 子代理(独立进程,独立历史)
|
||||
├─ bash: find . -name "*.py"
|
||||
├─ bash: cat src/main.py
|
||||
└─ 返回摘要 → stdout → 父代理收到结果
|
||||
```
|
||||
|
||||
## 4. 与 v3 的对比
|
||||
|
||||
| 机制 | v3 (Task 工具) | v0 (Bash 递归) |
|
||||
|------|----------------|----------------|
|
||||
| 代码行数 | ~900 行 | ~50 行 |
|
||||
| 子代理实现 | Task tool + Agent Registry | `python self 'task'` |
|
||||
| 上下文隔离 | 独立 messages[] | 独立进程 |
|
||||
| 工具过滤 | 白名单机制 | 无(bash 万能) |
|
||||
| 进度显示 | SubagentProgress 类 | 直接 stdout |
|
||||
| 灵活性 | 高(多种代理类型) | 中(统一行为) |
|
||||
|
||||
**v0 牺牲了什么?**
|
||||
- 没有代理类型区分(explore/code/plan)
|
||||
- 没有工具白名单(子代理也能写文件)
|
||||
- 没有优雅的进度显示
|
||||
|
||||
**v0 得到了什么?**
|
||||
- 极致简洁:20 行核心逻辑
|
||||
- 零额外概念:不需要理解 Task、Agent Registry
|
||||
- 完整能力:读、写、搜索、执行、子代理
|
||||
|
||||
## 5. 背后的思想
|
||||
|
||||
> **简单系统的涌现能力**
|
||||
|
||||
v0 证明了一个反直觉的事实:**复杂能力可以从简单规则中涌现**。
|
||||
|
||||
1. **一个工具足矣**
|
||||
|
||||
bash 是通往 Unix 世界的大门。通过它,模型可以调用任何命令、任何程序、任何脚本。不需要为每种能力单独实现工具。
|
||||
|
||||
2. **递归即层级**
|
||||
|
||||
程序调用自身是计算机科学最优雅的模式之一。v0 用同样的方式实现子代理:不是设计新机制,而是复用已有能力。
|
||||
|
||||
3. **进程即隔离**
|
||||
|
||||
操作系统早就解决了"如何隔离执行环境"的问题。v0 直接借用这个能力,而不是在应用层重新实现。
|
||||
|
||||
4. **约束在提示词**
|
||||
|
||||
v3 用代码实现工具过滤;v0 用提示词引导行为。前者是硬约束,后者是软约束。对于教学目的,软约束足够了。
|
||||
|
||||
## 6. 极限压缩版(16 行)
|
||||
|
||||
如果追求极致,还可以进一步压缩:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
"""v0_bash_agent_mini.py - 极限压缩版 (16 行)"""
|
||||
from anthropic import Anthropic; import subprocess as sp, sys, os
|
||||
C = Anthropic(api_key="your-key", base_url="https://api.moonshot.cn/anthropic")
|
||||
T = [{"name":"bash","description":"Shell. Subagent: python v0_bash_agent_mini.py 'task'","input_schema":{"type":"object","properties":{"command":{"type":"string"}},"required":["command"]}}]
|
||||
S = f"CLI agent at {os.getcwd()}. Use bash. Spawn subagent for complex tasks. Be concise."
|
||||
|
||||
def chat(p, h=[]):
|
||||
h.append({"role":"user","content":p})
|
||||
while (r:=C.messages.create(model="kimi-k2-turbo-preview",system=S,messages=h,tools=T,max_tokens=8000)).stop_reason=="tool_use":
|
||||
h.append({"role":"assistant","content":[{"type":b.type,**({"text":b.text}if hasattr(b,"text")else{"id":b.id,"name":b.name,"input":b.input})}for b in r.content]})
|
||||
h.append({"role":"user","content":[{"type":"tool_result","tool_use_id":b.id,"content":(print(f"\033[33m$ {b.input['command']}\033[0m"),o:=sp.run(b.input["command"],shell=1,capture_output=1,text=1,timeout=300),print(o.stdout+o.stderr or"(empty)"))and""or(o.stdout+o.stderr)[:50000]}for b in r.content if b.type=="tool_use"]})
|
||||
h.append({"role":"assistant","content":[{"type":b.type,**({"text":b.text}if hasattr(b,"text")else{"id":b.id,"name":b.name,"input":b.input})}for b in r.content]})
|
||||
return "".join(b.text for b in r.content if hasattr(b,"text"))
|
||||
|
||||
if __name__=="__main__":[print(chat(sys.argv[1]))]if len(sys.argv)>1 else[print(chat(q,h))for h in[[]]for _ in iter(int,1)if(q:=input("\033[36m>> \033[0m"))not in("q","")]
|
||||
```
|
||||
|
||||
16 行,功能完全相同。纯属炫技,不推荐用于学习。
|
||||
|
||||
## 7. 系列总结
|
||||
|
||||
| 版本 | 行数 | 核心主题 | 关键洞察 |
|
||||
|------|------|----------|----------|
|
||||
| **v0** | ~50 | Bash is All You Need | 一个工具 + 递归 = 完整 Agent |
|
||||
| v1 | ~400 | Model as Agent | 模型是主体,代码是工具循环 |
|
||||
| v2 | ~650 | 结构化规划 | Todo 工具引导模型行为 |
|
||||
| v3 | ~900 | 分而治之 | Task 工具实现专业化子代理 |
|
||||
|
||||
**从 v0 到 v3,代码在增加,但核心没变:**
|
||||
|
||||
```sh
|
||||
while True:
|
||||
response = model.call(messages, tools)
|
||||
if response.stop_reason != "tool_use":
|
||||
return response.text
|
||||
results = execute_tools(response.tool_calls)
|
||||
messages.append(results)
|
||||
```
|
||||
|
||||
这就是 Agent 的全部本质——**一个让模型持续调用工具直到完成任务的循环**。
|
||||
|
||||
v0 用最少的代码证明了这一点。其他版本只是在这个框架上添加细则:Todo 让规划可见,Task 让分工成为可能,进度显示让过程可观测。但大的框架从未改变
|
||||
|
||||
|
||||
👋 代码是思想在计算机世界的投影,Agent代码是【Agent模型与、真实计算机系统、间的“信号线”】
|
||||
|
||||
---
|
||||
|
||||
**Bash is All You Need.**
|
||||
|
||||
完整代码见仓库 `v0_bash_agent.py` 和 `v0_bash_agent_mini.py`。
|
||||
|
||||
|
||||
如果你想要生产级实现,欢迎使用 [Kode Agent CLI](https://github.com/shareAI-lab/Kode)
|
||||
485
articles/v1文章.md
Normal file
485
articles/v1文章.md
Normal file
@ -0,0 +1,485 @@
|
||||
Claude Code 没有秘密!价值 3000 万美金的 400 行代码
|
||||
首先,这又是一个标题党。
|
||||
|
||||
接着,让我们回到正题。
|
||||
|
||||
为什么Claude Code 比Cursor 调用同一个模型API但效果牛 100 倍?
|
||||
Claude Code 到底有什么工程设计上的秘密?
|
||||
答案是:Claude Code 没有秘密。
|
||||
|
||||
|
||||
如果一定要说有,就是model as agent。
|
||||
什么意思呢?也就是模型才是agent,代码只是agent模型的道具。
|
||||
|
||||
模型as agent 思想是关键。
|
||||
|
||||
模型是80%,代码是20%。
|
||||
|
||||
|
||||
|
||||
市面上哪些已有模型勉强是agent 模型?
|
||||
claude sonnet, kimi k2 0905, glm4.5, qwen-coder,gpt5-codex,cwm code world model etc.
|
||||
之前的模型都是QA 模型,训练目标是回答用户输入问题的答案,而不是独立长时间连续超多轮工具调用以完成用户要求的工作。
|
||||
|
||||
|
||||
|
||||
我们做了个 0 - 1手搓 mini Claude Code 的仓库,每天更新一部分代码,让你这个国庆假期 7 天学个够:
|
||||
|
||||
https://github.com/shareAI-lab/mini_claude_code
|
||||
|
||||
教你假期闲的没事,搓一个值 3000 万美金的Agent AI
|
||||
|
||||
本期我们将会分享一个400行的Claude Code的超迷你代码(但已经能完成Claude Code的90%以上工作)
|
||||
|
||||
之后,我们将会陆续添加Todo 工具、Task(subagent)工具、System-reminder工具,并为你一一解开Claude Code 的所有功能特性设计疑惑。
|
||||
|
||||
•
|
||||
如果你不关心Claude Code的实现原理,可以直接使用Kode这个开源项目,Kode是响应ShareAI-lab在之前临时分享了Claude Code的逆向分析设计资料Repo后,应广大网友热切要求,顺便维护的开源版Claude Code(基于Cc早期逆向版本+逆向分析继续开发维护,并持续添加几乎所有Claude Code后续更新特性以及Codex的好用特性)
|
||||
|
||||
图片
|
||||
|
||||
•
|
||||
目前已被多个大型 / 明星初创商业公司的闭源产品参考 / 直接修改使用。
|
||||
|
||||
图片
|
||||
图片
|
||||
|
||||
|
||||
•
|
||||
欢迎 众多网友一起参与维护(也完全鼓励使用vibe Coding方式提PR),过去已有网友参与了Bash工具对Windows的支持、Docker环境添加、 WebSearch & WebFetch工具添加等,下个版本正在大面积重构中,正在添加内置Web、IDE插件,同时CLI包和Core SDK包解离。
|
||||
|
||||
•
|
||||
Core SDK包支持在任意非cli场景用于各位开发者自己的业务场景下的灵活二次开发使用,让Claude Code 驱动你的下一个“世界上第一个 xx 领域 Manus 产品”运转。(当前SDK 的特性设计还没有完全收敛,我们将会在Kode群里组织腾讯会议进行方案讨论,欢迎一起参与)
|
||||
|
||||
图片
|
||||
|
||||
|
||||
•
|
||||
Kode - 开源版Claude Code:https://github.com/shareAI-lab/Kode
|
||||
|
||||
•
|
||||
kimi 0905 搭配开源版Claude Code(kode)挺好用!
|
||||
可以用开源 claude code (kode) + k2 0905 / deepseek v3.1 / gpt4.5 比闭源claude code + sonnet 降智的情况下好。
|
||||
安装后配置自定义模型使用:
|
||||
npm install -g @shareai-lab/kode
|
||||
|
||||
大道至简, 万法归一
|
||||
|
||||
很多人看Claude Code 的逆向分析解读 / Kode 源码只见其形、不抓其神。我们重新出这个mini_claude_code系列仓库(包括之前的训练营)就是想把超出“术”的“道”进行传播和分享。
|
||||
|
||||
|
||||
|
||||
下面的代码推荐你复制到本地终端运行,随意修改、二开玩耍,这样你也搓了一个值 3000 万美金的agent AI项目(doge。
|
||||
|
||||
|
||||
记住:对于下一代Agent 系统,模型是80%,代码是20%。
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import threading
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from anthropic import Anthropic
|
||||
except Exception as e:
|
||||
sys.stderr.write("Install with: pip install anthropic\n")
|
||||
raise
|
||||
|
||||
ANTHROPIC_BASE_URL = "https://api.moonshot.cn/anthropic"
|
||||
ANTHROPIC_API_KEY = "sk-xxx" # 替换为你的key
|
||||
AGENT_MODEL = "kimi-k2-turbo-preview"
|
||||
|
||||
# ---------- Workspace & Helpers ----------
|
||||
WORKDIR = Path.cwd()
|
||||
MAX_TOOL_RESULT_CHARS = 100_000
|
||||
|
||||
|
||||
def safe_path(p: str) -> Path:
|
||||
abs_path = (WORKDIR / str(p or "")).resolve()
|
||||
rel = abs_path.relative_to(WORKDIR) if abs_path.is_relative_to(WORKDIR) else None
|
||||
if rel is None:
|
||||
raise ValueError("Path escapes workspace")
|
||||
return abs_path
|
||||
|
||||
|
||||
def clamp_text(s: str, n: int = MAX_TOOL_RESULT_CHARS) -> str:
|
||||
if len(s) <= n:
|
||||
return s
|
||||
return s[:n] + f"\n\n...<truncated {len(s) - n} chars>"
|
||||
|
||||
|
||||
def pretty_tool_line(kind: str, title: str) -> None:
|
||||
print(f"⏺ {kind}({title})…")
|
||||
|
||||
|
||||
def pretty_sub_line(text: str) -> None:
|
||||
print(f" ⎿ {text}")
|
||||
|
||||
|
||||
# 轻量等待指示器(最小实现)
|
||||
class Spinner:
|
||||
def __init__(self, label: str = "等待模型响应") -> None:
|
||||
self.label = label
|
||||
self.frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||
self._stop = threading.Event()
|
||||
self._thread = None
|
||||
|
||||
def start(self):
|
||||
if not sys.stdout.isatty() or self._thread is not None:
|
||||
return
|
||||
self._stop.clear()
|
||||
|
||||
def run():
|
||||
i = 0
|
||||
while not self._stop.is_set():
|
||||
sys.stdout.write("\r" + self.label + " " + self.frames[i % len(self.frames)])
|
||||
sys.stdout.flush()
|
||||
i += 1
|
||||
time.sleep(0.08)
|
||||
|
||||
self._thread = threading.Thread(target=run, daemon=True)
|
||||
self._thread.start()
|
||||
|
||||
def stop(self):
|
||||
if self._thread is None:
|
||||
return
|
||||
self._stop.set()
|
||||
self._thread.join(timeout=1)
|
||||
self._thread = None
|
||||
try:
|
||||
# clear current line
|
||||
sys.stdout.write("\r\x1b[2K")
|
||||
sys.stdout.flush()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def log_error_debug(tag: str, info) -> None:
|
||||
try:
|
||||
js = json.dumps(info, ensure_ascii=False, indent=2)
|
||||
out = js if len(js) <= 4000 else js[:4000] + "\n...<truncated>"
|
||||
print(f"⚠️ {tag}:")
|
||||
print(out)
|
||||
except Exception:
|
||||
print(f"⚠️ {tag}: (unserializable info)")
|
||||
|
||||
|
||||
# ---------- Content normalization helpers ----------
|
||||
def block_to_dict(block):
|
||||
"""Convert SDK response block objects to plain dicts for reuse in messages.
|
||||
Supports TextBlock, ToolUseBlock, and dict inputs. Best-effort fallback.
|
||||
"""
|
||||
if isinstance(block, dict):
|
||||
return block
|
||||
out = {}
|
||||
for key in ("type", "text", "id", "name", "input", "citations"):
|
||||
if hasattr(block, key):
|
||||
out[key] = getattr(block, key)
|
||||
# Fallback: include any public attributes
|
||||
if not out and hasattr(block, "__dict__"):
|
||||
out = {k: v for k, v in vars(block).items() if not k.startswith("_")}
|
||||
if hasattr(block, "type"):
|
||||
out["type"] = getattr(block, "type")
|
||||
return out
|
||||
|
||||
|
||||
def normalize_content_list(content):
|
||||
try:
|
||||
return [block_to_dict(b) for b in (content or [])]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
# ---------- SDK client ----------
|
||||
api_key = ANTHROPIC_API_KEY
|
||||
if not api_key:
|
||||
sys.stderr.write("❌ ANTHROPIC_API_KEY not set\n")
|
||||
sys.exit(1)
|
||||
|
||||
base_url = ANTHROPIC_BASE_URL
|
||||
client = Anthropic(api_key=api_key, base_url=base_url) if base_url else Anthropic(api_key=api_key)
|
||||
|
||||
|
||||
# ---------- System prompt ----------
|
||||
SYSTEM = (
|
||||
f"You are a coding agent operating INSIDE the user's repository at {WORKDIR}.\n"
|
||||
"Follow this loop strictly: plan briefly → use TOOLS to act directly on files/shell → report concise results.\n"
|
||||
"Rules:\n"
|
||||
"- Prefer taking actions with tools (read/write/edit/bash) over long prose.\n"
|
||||
"- Keep outputs terse. Use bullet lists / checklists when summarizing.\n"
|
||||
"- Never invent file paths. Ask via reads or list directories first if unsure.\n"
|
||||
"- For edits, apply the smallest change that satisfies the request.\n"
|
||||
"- For bash, avoid destructive or privileged commands; stay inside the workspace.\n"
|
||||
"- After finishing, summarize what changed and how to run or test."
|
||||
)
|
||||
|
||||
|
||||
# ---------- Tools ----------
|
||||
tools = [
|
||||
{
|
||||
"name": "bash",
|
||||
"description": (
|
||||
"Execute a shell command inside the project workspace. Use for scaffolding, "
|
||||
"formatting, running scripts, etc."
|
||||
),
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {"type": "string", "description": "Shell command to run"},
|
||||
"timeout_ms": {"type": "integer", "minimum": 1000, "maximum": 120000},
|
||||
},
|
||||
"required": ["command"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "read_file",
|
||||
"description": "Read a UTF-8 text file. Optionally slice by line range or clamp length.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {"type": "string"},
|
||||
"start_line": {"type": "integer", "minimum": 1},
|
||||
"end_line": {"type": "integer", "minimum": -1},
|
||||
"max_chars": {"type": "integer", "minimum": 1, "maximum": 200000},
|
||||
},
|
||||
"required": ["path"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "write_file",
|
||||
"description": "Create or overwrite/append a UTF-8 text file. Use overwrite unless explicitly asked to append.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {"type": "string"},
|
||||
"content": {"type": "string"},
|
||||
"mode": {"type": "string", "enum": ["overwrite", "append"], "default": "overwrite"},
|
||||
},
|
||||
"required": ["path", "content"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "edit_text",
|
||||
"description": "Small, precise text edits. Choose one action: replace | insert | delete_range.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {"type": "string"},
|
||||
"action": {"type": "string", "enum": ["replace", "insert", "delete_range"]},
|
||||
"find": {"type": "string"},
|
||||
"replace": {"type": "string"},
|
||||
"insert_after": {"type": "integer", "minimum": -1},
|
||||
"new_text": {"type": "string"},
|
||||
"range": {"type": "array", "items": {"type": "integer"}, "minItems": 2, "maxItems": 2},
|
||||
},
|
||||
"required": ["path", "action"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# ---------- Tool executors ----------
|
||||
def run_bash(input_obj: dict) -> str:
|
||||
cmd = str(input_obj.get("command") or "")
|
||||
if not cmd:
|
||||
raise ValueError("missing bash.command")
|
||||
if (
|
||||
subprocess is not None
|
||||
and ("rm -rf /" in cmd or "shutdown" in cmd or "reboot" in cmd or "sudo " in cmd)
|
||||
):
|
||||
raise ValueError("blocked dangerous command")
|
||||
timeout_ms = int(input_obj.get("timeout_ms") or 30000)
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
cwd=str(WORKDIR),
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout_ms / 1000.0,
|
||||
)
|
||||
out = "\n".join([x for x in [proc.stdout, proc.stderr] if x]).strip()
|
||||
return clamp_text(out or "(no output)")
|
||||
except subprocess.TimeoutExpired:
|
||||
return "(timeout)"
|
||||
|
||||
|
||||
def run_read(input_obj: dict) -> str:
|
||||
fp = safe_path(input_obj.get("path"))
|
||||
text = fp.read_text("utf-8")
|
||||
lines = text.split("\n")
|
||||
start = (max(1, int(input_obj.get("start_line") or 1)) - 1) if input_obj.get("start_line") else 0
|
||||
if isinstance(input_obj.get("end_line"), int):
|
||||
end_val = input_obj.get("end_line")
|
||||
end = len(lines) if end_val < 0 else max(start, end_val)
|
||||
else:
|
||||
end = len(lines)
|
||||
text = "\n".join(lines[start:end])
|
||||
max_chars = int(input_obj.get("max_chars") or 100_000)
|
||||
return clamp_text(text, max_chars)
|
||||
|
||||
|
||||
def run_write(input_obj: dict) -> str:
|
||||
fp = safe_path(input_obj.get("path"))
|
||||
fp.parent.mkdir(parents=True, exist_ok=True)
|
||||
content = input_obj.get("content") or ""
|
||||
mode = input_obj.get("mode")
|
||||
if mode == "append" and fp.exists():
|
||||
with fp.open("a", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
else:
|
||||
fp.write_text(content, encoding="utf-8")
|
||||
bytes_len = len(content.encode("utf-8"))
|
||||
rel = fp.relative_to(WORKDIR)
|
||||
return f"wrote {bytes_len} bytes to {rel}"
|
||||
|
||||
|
||||
def run_edit(input_obj: dict) -> str:
|
||||
fp = safe_path(input_obj.get("path"))
|
||||
text = fp.read_text("utf-8")
|
||||
action = input_obj.get("action")
|
||||
if action == "replace":
|
||||
find = str(input_obj.get("find") or "")
|
||||
if not find:
|
||||
raise ValueError("edit_text.replace missing find")
|
||||
replaced = text.replace(find, str(input_obj.get("replace") or ""))
|
||||
fp.write_text(replaced, encoding="utf-8")
|
||||
return f"replace done ({len(replaced.encode('utf-8'))} bytes)"
|
||||
elif action == "insert":
|
||||
line = int(input_obj.get("insert_after") if input_obj.get("insert_after") is not None else -1)
|
||||
lines = text.split("\n")
|
||||
idx = max(-1, min(len(lines) - 1, line))
|
||||
lines[idx + 1:idx + 1] = [str(input_obj.get("new_text") or "")]
|
||||
nxt = "\n".join(lines)
|
||||
fp.write_text(nxt, encoding="utf-8")
|
||||
return f"inserted after line {line}"
|
||||
elif action == "delete_range":
|
||||
rng = input_obj.get("range") or []
|
||||
if not (len(rng) == 2 and isinstance(rng[0], int) and isinstance(rng[1], int) and rng[1] >= rng[0]):
|
||||
raise ValueError("edit_text.delete_range invalid range")
|
||||
s, e = rng
|
||||
lines = text.split("\n")
|
||||
nxt = "\n".join([*lines[:s], *lines[e:]])
|
||||
fp.write_text(nxt, encoding="utf-8")
|
||||
return f"deleted lines [{s}, {e})"
|
||||
else:
|
||||
raise ValueError(f"unsupported edit_text.action: {action}")
|
||||
|
||||
|
||||
def dispatch_tool(tu: dict) -> dict:
|
||||
try:
|
||||
# Support both dict and SDK block objects
|
||||
def gv(obj, key, default=None):
|
||||
return obj.get(key, default) if isinstance(obj, dict) else getattr(obj, key, default)
|
||||
|
||||
name = gv(tu, "name")
|
||||
input_obj = gv(tu, "input", {}) or {}
|
||||
tool_use_id = gv(tu, "id")
|
||||
|
||||
if name == "bash":
|
||||
pretty_tool_line("Bash", (input_obj.get("command") if isinstance(input_obj, dict) else None))
|
||||
out = run_bash(input_obj if isinstance(input_obj, dict) else {})
|
||||
pretty_sub_line(clamp_text(out, 2000) if out else "(No content)")
|
||||
return {"type": "tool_result", "tool_use_id": tool_use_id, "content": out}
|
||||
if name == "read_file":
|
||||
pretty_tool_line("Read", (input_obj.get("path") if isinstance(input_obj, dict) else None))
|
||||
out = run_read(input_obj if isinstance(input_obj, dict) else {})
|
||||
pretty_sub_line(clamp_text(out, 2000))
|
||||
return {"type": "tool_result", "tool_use_id": tool_use_id, "content": out}
|
||||
if name == "write_file":
|
||||
pretty_tool_line("Write", (input_obj.get("path") if isinstance(input_obj, dict) else None))
|
||||
out = run_write(input_obj if isinstance(input_obj, dict) else {})
|
||||
pretty_sub_line(out)
|
||||
return {"type": "tool_result", "tool_use_id": tool_use_id, "content": out}
|
||||
if name == "edit_text":
|
||||
action = input_obj.get("action") if isinstance(input_obj, dict) else None
|
||||
path_v = input_obj.get("path") if isinstance(input_obj, dict) else None
|
||||
pretty_tool_line("Edit", f"{action} {path_v}")
|
||||
out = run_edit(input_obj if isinstance(input_obj, dict) else {})
|
||||
pretty_sub_line(out)
|
||||
return {"type": "tool_result", "tool_use_id": tool_use_id, "content": out}
|
||||
return {"type": "tool_result", "tool_use_id": tool_use_id, "content": f"unknown tool: {name}", "is_error": True}
|
||||
except Exception as e:
|
||||
tool_use_id = tu.get("id") if isinstance(tu, dict) else getattr(tu, "id", None)
|
||||
return {"type": "tool_result", "tool_use_id": tool_use_id, "content": str(e), "is_error": True}
|
||||
|
||||
|
||||
# ---------- Core loop ----------
|
||||
def query(messages: list, opts: dict | None = None) -> list:
|
||||
opts = opts or {}
|
||||
while True:
|
||||
spinner = Spinner()
|
||||
spinner.start()
|
||||
try:
|
||||
res = client.messages.create(
|
||||
model=AGENT_MODEL,
|
||||
system=SYSTEM,
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
max_tokens=16000,
|
||||
**({"tool_choice": opts["tool_choice"]} if "tool_choice" in opts else {}),
|
||||
)
|
||||
finally:
|
||||
spinner.stop()
|
||||
|
||||
tool_uses = []
|
||||
try:
|
||||
for block in getattr(res, "content", []):
|
||||
btype = getattr(block, "type", None) if not isinstance(block, dict) else block.get("type")
|
||||
if btype == "text":
|
||||
text = getattr(block, "text", None) if not isinstance(block, dict) else block.get("text")
|
||||
sys.stdout.write((text or "") + "\n")
|
||||
if btype == "tool_use":
|
||||
tool_uses.append(block)
|
||||
except Exception as err:
|
||||
log_error_debug(
|
||||
"Iterating res.content failed",
|
||||
{
|
||||
"error": str(err),
|
||||
"stop_reason": getattr(res, "stop_reason", None),
|
||||
"content_type": type(getattr(res, "content", None)).__name__,
|
||||
"is_array": isinstance(getattr(res, "content", None), list),
|
||||
"keys": list(res.__dict__.keys()) if hasattr(res, "__dict__") else [],
|
||||
"preview": (json.dumps(res, default=lambda o: getattr(o, "__dict__", str(o)))[:2000] if res else ""),
|
||||
},
|
||||
)
|
||||
raise
|
||||
|
||||
if getattr(res, "stop_reason", None) == "tool_use":
|
||||
results = [dispatch_tool(tu) for tu in tool_uses]
|
||||
messages.append({"role": "assistant", "content": normalize_content_list(res.content)})
|
||||
messages.append({"role": "user", "content": results})
|
||||
continue
|
||||
|
||||
messages.append({"role": "assistant", "content": normalize_content_list(res.content)})
|
||||
return messages
|
||||
|
||||
|
||||
def main():
|
||||
print(f"Tiny CC Agent (custom tools only) — cwd: {WORKDIR}")
|
||||
print('Type "exit" or "quit" to leave.\n')
|
||||
history: list = []
|
||||
while True:
|
||||
try:
|
||||
line = input("User: ")
|
||||
except EOFError:
|
||||
break
|
||||
if not line or line.strip().lower() in {"q", "quit", "exit"}:
|
||||
break
|
||||
history.append({"role": "user", "content": [{"type": "text", "text": line}]})
|
||||
try:
|
||||
query(history)
|
||||
except Exception as e:
|
||||
print("Error:", str(e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
30
articles/v2文章.md
Normal file
30
articles/v2文章.md
Normal file
@ -0,0 +1,30 @@
|
||||
# mini Claude Code v2:让模型自我约束的 Todo 工具
|
||||
|
||||
v1 版本的 mini Claude Code 已经证明:只要把大模型嵌入一个简洁的工具循环,它就能像“代码工人”一样在本地仓库里读写文件、执行命令。但我们也遇到一个老问题——**模型是否真的在有计划地推进任务?**
|
||||
|
||||
顺便推荐一下我们维护的 Kode 项目:这是面向生产的开源版 Claude Code,收录了 Bash 扩展、WebSearch / WebFetch、Docker 适配等高级能力,适合直接落地企业级场景。若你在寻找更多资料,也可以参考我们先前整理的一系列开源实现与逆向分析,它们共同构成了如今 mini Claude Code 的"孵化器"。
|
||||
|
||||
在 v2 中,我们引入了一个看似朴素、却改变工作流体验的核心能力:**Todo 工具链**。它把“规划—执行—复盘”的节奏落在了代码级别,迫使模型对复杂任务进行显式拆解,从而让用户和模型都能清楚地看到每一步发生了什么。
|
||||
|
||||
## 1. 具体做了什么?
|
||||
|
||||
- **新增 TodoWrite 工具与 TodoManager**:在工具列表中加入 `TodoWrite`,由 `TodoManager` 维护一个至多 20 条的任务列表,约束只允许一个条目处于 `in_progress`,并在每次更新时返回彩色状态视图与统计数据。
|
||||
- **消息循环挂钩待办状态**:在每轮模型输出后,如果出现对 Todo 的调用,就把更新结果作为工具反馈写回对话历史,让模型看到自己刚刚操作的“计划板”,形成自监督闭环。
|
||||
- **系统提醒机制**:在会话伊始植入 system reminder,如果连续 10 轮交互未触发 Todo,再次注入温和提醒,让模型持续感知“规划要求”;每次真正更新 Todo 后则重置计数器。
|
||||
- **严格输入校验**:对 Todo 输入的 `id`、`status`、`activeForm` 等字段进行检查,防止模型写入空任务、重复条目或异常状态,引导它遵循清晰的规范。
|
||||
|
||||
## 2. 背后的思想
|
||||
|
||||
> **模型依旧是核心,但要用规则把它“拴”在结构化工作流里。**
|
||||
|
||||
1. **结构化约束代替自由生成**:单纯的文本指令很容易让模型忘记计划,Todo 工具则把“计划”实体化为数据结构,模型只有维护好它才能继续执行。
|
||||
2. **显式反馈增强自我监督**:每次 Todo 更新都会立即显示当前状态,模型能在下一轮读到这段文本,相当于用环境反馈提醒它“你现在在做哪一步”。
|
||||
3. **系统提示形成软约束**:提醒块不是条件语句,而是上下文资料;它们用最小侵入的方式告诉模型“别忘记 Todo”,既保留灵活性,又能降低跑偏概率。
|
||||
|
||||
## 3. 能应用到哪些场景?
|
||||
|
||||
- **多步骤代码任务**:例如“写接口 → 补测试 → 更新文档”,模型必须先把步骤写进 Todo,然后逐项勾选,用户可以随时检查进度。
|
||||
- **长周期对话**:在需要几十轮推演的需求中,system reminder 会周期性提醒模型遵守规划流程,避免它在自由文本里迷失。
|
||||
- **团队协同与审计**:把 Todo 输出存档,就能追踪模型在每次会话里的计划与执行痕迹,为回顾和复盘提供依据。
|
||||
|
||||
v1 的意义在于验证“模型 as agent”这一底层设计;v2 则进一步证明:**只要给模型配备合适的结构化工具,它不仅能执行,更能规划、记忆和自我纠错**。后续我们还会继续叠加 Task 子代理与更丰富的 system reminder 组合,并把 Kode 里的成熟特性不断反馈到 mini Claude Code,让它逐步靠近真实产品级 Agent 的形态。
|
||||
177
articles/v3文章.md
Normal file
177
articles/v3文章.md
Normal file
@ -0,0 +1,177 @@
|
||||
# mini Claude Code v3:用 150 行代码揭开 Subagent 的神秘面纱
|
||||
|
||||
v2 版本让模型学会了"做计划"——通过 Todo 工具把任务显式拆解、逐项推进。但当任务规模再大一些,比如"探索整个代码库然后重构认证模块",单一代理就会陷入上下文膨胀的泥潭:探索时的大量文件内容、重构时的代码细节,全部堆在一个对话里,模型的注意力被稀释,效果急剧下降。
|
||||
|
||||
Claude Code 的解法是 **Task 工具**——让主代理像项目经理一样,把子任务派发给专门的"子代理"(Subagent),各自独立完成后再汇报结果。这套机制在 Kode Agent CLI里也有完整实现,而 v3 版 mini Claude Code 则用最精简的方式复现了它的核心:**约 150 行新增代码,还原核心机制**。
|
||||
|
||||
本次完整教学代码地址:https://github.com/shareAI-lab/Mini_Claude_Code
|
||||
|
||||
## 1. 具体做了什么?
|
||||
|
||||
### 1.1 Agent 类型注册表
|
||||
|
||||
```python
|
||||
AGENT_TYPES = {
|
||||
"explore": {
|
||||
"description": "Fast read-only agent for exploring codebases...",
|
||||
"tools": ["bash", "read_file"], # 只读工具
|
||||
"system_prompt": "You are an exploration agent..."
|
||||
},
|
||||
"code": {
|
||||
"tools": "*", # 全部工具
|
||||
"system_prompt": "You are a coding agent..."
|
||||
},
|
||||
"plan": {
|
||||
"tools": ["bash", "read_file"],
|
||||
"system_prompt": "You are a planning agent..."
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
每种代理类型定义三件事:
|
||||
- **什么时候用**(description):写进 Task 工具描述,让主代理知道何时派发
|
||||
- **能用什么工具**(tools):白名单机制,explore 只能读不能写
|
||||
- **怎么工作**(system_prompt):代理专属指令,聚焦单一职责
|
||||
|
||||
### 1.2 Task 工具
|
||||
|
||||
```python
|
||||
{
|
||||
"name": "Task",
|
||||
"input_schema": {
|
||||
"description": "短描述(3-5 词)",
|
||||
"prompt": "详细指令",
|
||||
"subagent_type": "explore | code | plan"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
主代理调用 Task 时,只需指定代理类型和任务描述,剩下的全部由框架处理。
|
||||
|
||||
### 1.3 子代理执行核心(run_task)
|
||||
|
||||
这是整个机制的核新,核心逻辑只有 5 步:
|
||||
|
||||
```python
|
||||
def run_task(inp, depth=0):
|
||||
# 1. 构造专属 system prompt
|
||||
sub_system = f"You are a {agent_type} subagent...\n{agent_config['system_prompt']}"
|
||||
|
||||
# 2. 过滤可用工具
|
||||
sub_tools = get_tools_for_agent(agent_type) # explore 只拿 bash + read_file
|
||||
|
||||
# 3. 创建隔离的消息历史(关键!)
|
||||
sub_messages = [{"role": "user", "content": prompt}]
|
||||
|
||||
# 4. 递归调用同一个 query 循环
|
||||
result_messages = query(sub_messages, sub_system, sub_tools, silent=True)
|
||||
|
||||
# 5. 提取最终文本返回给主代理
|
||||
return extract_final_text(result_messages)
|
||||
```
|
||||
|
||||
**隔离消息历史**是灵魂:子代理看不到主对话的任何内容,也不会污染主对话。它执行完毕后,只有最终总结文本被返回——就像员工提交的工作报告,而不是把所有草稿都堆到老板桌上。
|
||||
|
||||
### 1.4 Kode 风格的进度显示
|
||||
|
||||
子代理执行时,内部的工具调用不会打印到主聊天区,而是用单行进度实时更新:
|
||||
|
||||
```
|
||||
@ Task(explore: 探索代码库)...
|
||||
| Read(README.md) (+3 tool uses, 2.1s) <- 实时刷新
|
||||
| completed: 8 tool calls in 15.2s <- 最终摘要
|
||||
```
|
||||
|
||||
这在v3代码中通过 `silent` 参数 + `SubagentProgress` 类实现:子代理的 query 循环不打印任何内容,而是把工具调用信息发送给进度追踪器,由它负责覆盖更新同一行。
|
||||
|
||||
## 2. 背后的思想
|
||||
|
||||
> **分而治之 + 最小知识原则**
|
||||
|
||||
1. **上下文隔离解决注意力稀释**
|
||||
|
||||
探索代码库可能需要读 20 个文件,但重构模块只需要知道"哪些文件和认证相关"。如果所有内容都堆在一个对话里,模型很快就会"忘记"前面的内容。子代理机制让每个子任务拥有干净的上下文窗口,专注做好一件事。
|
||||
|
||||
2. **工具白名单实现职责分离**
|
||||
|
||||
explore 代理只能读不能写,code 代理才有写权限。这不是为了"安全"(虽然也有帮助),而是为了**引导模型行为**:当模型知道自己只有只读工具时,它就不会尝试"顺手改一下",而是专注于信息收集。
|
||||
|
||||
3. **递归复用而非重复实现**
|
||||
|
||||
子代理用的是同一个 `query()` 函数,只是参数不同。这意味着:
|
||||
- 工具执行逻辑共享
|
||||
- 错误处理一致
|
||||
- 未来扩展(如添加新工具)自动生效
|
||||
|
||||
Claude Code、Kode 的设计也是如此:agent loop 被主代理和子代理共用(但隔离用各自的Context、tools)
|
||||
|
||||
4. **结果抽象而非细节透传**
|
||||
|
||||
子代理可能调用了 105 次工具、读了 35 个文件,但返回给主代理的只是一段详细总结文本。主代理不需要知道这些细节,它只关心"探索的结论是什么"。这就像公司里的汇报机制:CTO 不需要看每个工程师的代码提交记录,只需要看项目进度报告。
|
||||
|
||||
## 3. 与 Claude Code 、 Kode CLI的对比
|
||||
|
||||
| 机制 | Claude Code 、 Kode CLI | mini Claude Code v3 |
|
||||
|------|-------------------|--------------|
|
||||
| Agent Registry | AgentConfig (type, tools, prompt, model, forkContext) | AGENT_TYPES dict |
|
||||
| Task Schema | description, prompt, subagent_type, model, resume, run_in_background | description, prompt, subagent_type |
|
||||
| Tool Filtering | 白名单 + 黑名单 | 白名单 |
|
||||
| 消息隔离 | 独立 messages[] | 独立 sub_messages[] |
|
||||
| 进度显示 | 折叠式进度面板 | 单行实时刷新 |
|
||||
| 后台执行 | run_in_background + TaskOutput | 省略(同步执行) |
|
||||
| Resume | transcript 存储恢复 | 省略 |
|
||||
| forkContext | 可传递父对话上下文 | 省略 |
|
||||
|
||||
省略的功能(后台执行、Resume、forkContext)都是高级特性,不影响理解子代理的核心原理。v3 的目标是用最少代码展示最关键机制。
|
||||
|
||||
## 4. 典型使用场景
|
||||
|
||||
**场景一:大型代码库探索**
|
||||
```
|
||||
用户:帮我理解这个项目的架构
|
||||
|
||||
主代理:我来派一个探索子代理...
|
||||
@ Task(explore: 分析项目架构)...
|
||||
| completed: 12 tool calls in 18.3s
|
||||
|
||||
根据探索结果,这个项目是一个...
|
||||
```
|
||||
|
||||
**场景二:规划后执行**
|
||||
```
|
||||
用户:重构用户认证模块
|
||||
|
||||
主代理:
|
||||
1. 先派 plan 代理分析现有代码
|
||||
@ Task(plan: 分析认证模块)...
|
||||
|
||||
2. 根据计划,派 code 代理逐步实现
|
||||
@ Task(code: 重构 auth.py)...
|
||||
@ Task(code: 更新测试用例)...
|
||||
```
|
||||
|
||||
**场景三:并行探索(概念)**
|
||||
```
|
||||
主代理同时派出多个 explore 子代理,分别探索不同目录
|
||||
(当前 v3 是同步执行,但机制上完全支持并行)
|
||||
```
|
||||
|
||||
## 5. 三部曲回顾
|
||||
|
||||
| 版本 | 核心主题 | 新增代码 | 关键洞察 |
|
||||
|------|----------|----------|----------|
|
||||
| v1 | Model as Agent | ~400 行 | 模型是 80%,代码只是工具循环的载体 |
|
||||
| v2 | 结构化规划 | ~170 行 | 用 Todo 工具把模型"拴"在工作流里 |
|
||||
| v3 | 分而治之 | ~150 行 | 子代理隔离上下文,专注单一职责 |
|
||||
|
||||
三个版本加起来不到 900 行 Python,却覆盖了 Claude Code 最核心的设计思想:
|
||||
|
||||
1. **工具循环**:模型持续调用工具直到任务完成
|
||||
2. **结构化约束**:用数据结构(Todo)引导模型行为
|
||||
3. **层级委派**:复杂任务拆解为子代理独立执行
|
||||
|
||||
这就是"Model as Agent"的全部奥秘——**没有魔法,只有清晰的工程设计**。
|
||||
|
||||
---
|
||||
|
||||
完整代码见仓库 `v3_subagent.py`。如果你想要生产级实现,欢迎使用 [Kode](https://github.com/shareAI-lab/Kode)。
|
||||
422
articles/v4文章.md
Normal file
422
articles/v4文章.md
Normal file
@ -0,0 +1,422 @@
|
||||
# mini Claude Code v4:Skills 让模型成为领域专家
|
||||
|
||||
v3 版本引入了子代理机制——用 Task 工具将复杂任务分解给专门的"员工"处理。但有一个问题始终存在:**模型怎么知道"如何"处理特定领域的任务?**
|
||||
|
||||
让模型处理 PDF?它需要知道用 `pdftotext` 还是 `PyMuPDF`。让模型构建 MCP 服务器?它需要知道协议规范和最佳实践。让模型做代码审查?它需要一套系统的检查清单。
|
||||
|
||||
这些"领域知识"不是工具,而是**专业技能**。Claude Code 的解法是 **Skills 机制**——一套开放标准,让模型按需加载领域专家的"说明书"。
|
||||
|
||||
v4 用约 100 行新增代码实现了这个机制的核心:**渐进式披露 + SKILL.md 标准 + 上下文注入**。
|
||||
|
||||
本次完整教学代码地址:https://github.com/shareAI-lab/mini_claude_code
|
||||
|
||||
## 0. 知识外化:改变一切的范式转变
|
||||
|
||||
在深入 Skills 机制之前,我想先讲一个更大的故事:**知识外化 (Knowledge Externalization)**。
|
||||
|
||||
### 传统 AI 的困境
|
||||
|
||||
传统 AI 系统的知识都藏在模型参数里,你没法访问、没法修改、没法复用。
|
||||
|
||||
```sh
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 知识存储层级 │
|
||||
│ │
|
||||
│ Model Parameters → Context Window → File System → Skill Library │
|
||||
│ (内化) (运行时) (持久化) (结构化) │
|
||||
│ │
|
||||
│ ←───────── 训练修改 ──────────→ ←────── 自然语言修改 ─────────→ │
|
||||
│ 需要集群、数据、专业知识 任何人都可以编辑 │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
想让模型学会新技能?传统做法:收集数据 → 设置集群 → 参数微调(LoRA/全量)→ 部署新版本。
|
||||
|
||||
知识被100%完全锁在神经网络的权重矩阵中。
|
||||
|
||||
### 代码执行范式改变了这一切
|
||||
|
||||
**关键突破**:
|
||||
- **过去**:修改模型行为 = 修改参数 = 需要训练 = 需要 GPU 集群 + 训练数据 + 专业知识
|
||||
- **现在**:修改模型行为 = 修改 SKILL.md = 编辑文本文件 = 任何人都可以做
|
||||
|
||||
这就像给 base model 外挂了一个可热插拔的 LoRA 权重,但你不需要对模型本身进行任何参数训练。
|
||||
|
||||
### 知识层级对比
|
||||
|
||||
| 层级 | 修改方式 | 生效时间 | 持久性 | 成本 |
|
||||
|------|----------|----------|--------|------|
|
||||
| Model Parameters | 训练/微调 | 数小时-数天 | 永久 | $10K-$1M+ |
|
||||
| Context Window | API 调用 | 即时 | 会话内 | ~$0.01/次 |
|
||||
| File System | 编辑文件 | 下次加载 | 永久 | 免费 |
|
||||
| **Skill Library** | **编辑 SKILL.md** | **下次触发** | **永久** | **免费** |
|
||||
|
||||
Skills 是最甜蜜的平衡点:持久化存储 + 按需加载 + 人类可编辑。
|
||||
|
||||
### 这意味着什么
|
||||
|
||||
1. **民主化**:不再需要 ML 专业知识来定制模型行为
|
||||
2. **透明性**:知识以人类可读的 Markdown 存储,可审计、可理解
|
||||
3. **在线学习**:模型在更大的上下文窗口中"学习",无需离线训练
|
||||
4. **即时生效**:修改 SKILL.md 后,下次触发立即生效
|
||||
|
||||
传统的微调是**离线学习**:收集数据→训练→部署→使用。
|
||||
Skills 是**在线学习**:运行时按需加载知识,立即生效。
|
||||
|
||||
**这就是知识外化的力量:把需要训练才能编码的知识,变成任何人都能编辑的文档。**
|
||||
|
||||
理解了这个背景,Skills 机制的设计就显得顺理成章了。
|
||||
|
||||
## 1. Skills 的本质:知识包,不是工具
|
||||
|
||||
这是最关键的洞察:
|
||||
|
||||
| 概念 | 是什么 | 例子 |
|
||||
|------|--------|------|
|
||||
| **Tool** | 模型能**做**什么 | bash, read_file, write_file |
|
||||
| **Skill** | 模型**知道怎么做** | PDF 处理、MCP 构建、代码审查 |
|
||||
|
||||
工具是能力,技能是知识。工具执行动作,技能指导决策。
|
||||
|
||||
**为什么不直接把所有知识写进系统提示词?**
|
||||
|
||||
因为上下文是稀缺资源。一个 Skill 可能有 2000 词的详细指南,如果你有 20 个 Skills,启动时就要注入 40000 词——模型的注意力会被稀释到几乎无效。
|
||||
|
||||
## 2. 渐进式披露:三层加载
|
||||
|
||||
这是 Skills 的精髓设计:
|
||||
|
||||
```sh
|
||||
Layer 1: 元数据 (始终加载) ~100 tokens/skill
|
||||
└─ name + description
|
||||
└─ "当用户要处理 PDF 时用这个"
|
||||
|
||||
Layer 2: SKILL.md 主体 (触发时加载) ~2000 tokens
|
||||
└─ 详细指南、代码示例、决策树
|
||||
|
||||
Layer 3: 资源文件 (按需加载) 无限制
|
||||
└─ scripts/, references/, assets/
|
||||
```
|
||||
|
||||
**启动时**:只加载 20 个 Skills 的 name + description = ~2000 tokens
|
||||
**触发时**:加载相关 Skill 的完整内容 = ~2000 tokens
|
||||
**执行时**:按需读取脚本和参考文档
|
||||
|
||||
这让模型在保持轻量的同时,能够调用任意深度的领域知识。
|
||||
|
||||
## 3. SKILL.md 标准
|
||||
|
||||
每个 Skill 就是一个文件夹:
|
||||
|
||||
```sh
|
||||
skills/
|
||||
├── pdf/
|
||||
│ └── SKILL.md # 必需:元数据 + 指南
|
||||
├── mcp-builder/
|
||||
│ ├── SKILL.md
|
||||
│ └── references/ # 可选:参考文档
|
||||
│ └── protocol.md
|
||||
└── code-review/
|
||||
├── SKILL.md
|
||||
└── scripts/ # 可选:辅助脚本
|
||||
└── security_scan.sh
|
||||
```
|
||||
|
||||
**SKILL.md 格式**:YAML 前置 + Markdown 正文
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: pdf
|
||||
description: 处理 PDF 文件。当用户需要读取、创建、合并 PDF 时使用。
|
||||
---
|
||||
|
||||
# PDF 处理技能
|
||||
|
||||
## 读取 PDF
|
||||
|
||||
推荐使用 pdftotext:
|
||||
\`\`\`bash
|
||||
pdftotext input.pdf -
|
||||
\`\`\`
|
||||
|
||||
如果需要更精细控制,使用 PyMuPDF...
|
||||
```
|
||||
|
||||
**关键设计**:
|
||||
- `name`:唯一标识符,小写+连字符
|
||||
- `description`:触发条件——模型根据这个决定是否加载
|
||||
|
||||
## 4. 核心实现
|
||||
|
||||
### 4.1 SkillLoader
|
||||
|
||||
```python
|
||||
class SkillLoader:
|
||||
def __init__(self, skills_dir: Path):
|
||||
self.skills = {}
|
||||
self.load_skills() # 扫描所有 SKILL.md
|
||||
|
||||
def parse_skill_md(self, path: Path) -> dict:
|
||||
"""解析 YAML 前置 + Markdown 正文"""
|
||||
content = path.read_text()
|
||||
match = re.match(r'^---\s*\n(.*?)\n---\s*\n(.*)$', content, re.DOTALL)
|
||||
# 返回 {name, description, body, path, dir}
|
||||
|
||||
def get_descriptions(self) -> str:
|
||||
"""生成元数据列表(注入系统提示词)"""
|
||||
return "\n".join(f"- {name}: {skill['description']}"
|
||||
for name, skill in self.skills.items())
|
||||
|
||||
def get_skill_content(self, name: str) -> str:
|
||||
"""获取完整内容(Skill 工具调用时)"""
|
||||
skill = self.skills[name]
|
||||
content = f"# Skill: {name}\n\n{skill['body']}"
|
||||
# 附加可用资源列表
|
||||
return content
|
||||
```
|
||||
|
||||
### 4.2 Skill 工具
|
||||
|
||||
```python
|
||||
SKILL_TOOL = {
|
||||
"name": "Skill",
|
||||
"description": """加载技能获取专业知识。
|
||||
|
||||
可用技能:
|
||||
- pdf: 处理 PDF 文件
|
||||
- mcp-builder: 构建 MCP 服务器
|
||||
- code-review: 代码审查
|
||||
|
||||
当任务匹配技能描述时,立即调用此工具。""",
|
||||
"input_schema": {
|
||||
"properties": {"skill": {"type": "string"}},
|
||||
"required": ["skill"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 消息注入(保持缓存)
|
||||
|
||||
这是最精妙的部分——Skill 内容作为 **tool_result 注入对话历史**,而不是修改 system prompt:
|
||||
|
||||
```python
|
||||
def run_skill(skill_name: str) -> str:
|
||||
content = SKILLS.get_skill_content(skill_name)
|
||||
# 完整内容作为 tool_result 返回
|
||||
# 它会成为对话历史的一部分(user message)
|
||||
return f"""<skill-loaded name="{skill_name}">
|
||||
{content}
|
||||
</skill-loaded>
|
||||
|
||||
Follow the instructions in the skill above."""
|
||||
|
||||
def agent_loop(messages: list) -> list:
|
||||
while True:
|
||||
response = client.messages.create(
|
||||
model=MODEL,
|
||||
system=SYSTEM, # 永不改变 - 缓存保持有效!
|
||||
messages=messages,
|
||||
tools=ALL_TOOLS,
|
||||
)
|
||||
# ... Skill 内容作为 tool_result 进入 messages ...
|
||||
```
|
||||
|
||||
**关键洞察**:
|
||||
- Skill 内容作为新消息**追加到末尾**
|
||||
- 之前的所有内容(system prompt + 历史消息)都被缓存复用
|
||||
- 只有新追加的 skill 内容需要计算,**整个前缀都命中缓存**
|
||||
|
||||
## 5. 与 Claude Code 的对比
|
||||
|
||||
| 机制 | Claude Code / Kode CLI | mini Claude Code v4 |
|
||||
|------|------------------------|---------------------|
|
||||
| Skill 格式 | SKILL.md (YAML + MD) | SKILL.md (YAML + MD) |
|
||||
| 加载机制 | Container API + Skills 数组 | SkillLoader 类 |
|
||||
| 触发方式 | 自动(description 匹配)+ Skill 工具 | Skill 工具 |
|
||||
| 内容注入 | newMessages (user message) | tool_result (user message) |
|
||||
| 缓存机制 | 追加到末尾,前缀全部缓存 | 追加到末尾,前缀全部缓存 |
|
||||
| 资源访问 | scripts/, references/, assets/ | 相同目录结构 |
|
||||
| 版本控制 | Skill Versions API | 省略 |
|
||||
| 权限控制 | allowed-tools 字段 | 省略 |
|
||||
|
||||
**关键共同点**:两者都不修改 system prompt,而是将 skill 内容注入到对话历史中,从而保持 prompt cache 有效。
|
||||
|
||||
## 6. 缓存友好设计:Agent 开发中最容易被忽视的成本问题
|
||||
|
||||
Skill 内容作为 tool_result 注入对话历史,而不是修改 system prompt。这不是随意的设计选择,而是与大模型 API 缓存机制对齐的关键决策。
|
||||
|
||||
### 为什么这个问题如此重要?
|
||||
|
||||
许多开发者在使用 LangGraph、LangChain 等框架时,习惯性地:
|
||||
- 在 system prompt 中注入动态状态
|
||||
- 编辑和压缩历史消息
|
||||
- 使用滑动窗口截断对话
|
||||
|
||||
**这些操作会导致缓存完全失效,成本爆炸 7-50 倍。**
|
||||
|
||||
一个 50 轮的典型 SWE 任务:
|
||||
- **破坏缓存**: $14.06 (每轮修改 system prompt)
|
||||
- **缓存优化**: $1.85 (只追加,不修改)
|
||||
- **节省**: 86.9%
|
||||
|
||||
对于每天处理 100 个任务的应用,这意味着每年节省 **$45,000+**。
|
||||
|
||||
### 核心原理
|
||||
|
||||
大模型 API 提供商普遍实现了 **Prompt Cache**:当请求的前缀与之前完全相同时,可以复用已计算的 KV 向量,大幅降低成本。
|
||||
|
||||
```sh
|
||||
请求 1: [System, User1, Asst1, User2]
|
||||
←────── 全部计算 ──────→
|
||||
|
||||
请求 2: [System, User1, Asst1, User2, Asst2, User3]
|
||||
←────── 缓存命中 ──────→ ←─ 新计算 ─→
|
||||
(0.1x 价格) (正常价格)
|
||||
```
|
||||
|
||||
**关键条件**:缓存命中要求**前缀完全相同**。修改 system prompt 或历史消息会使整个前缀缓存失效。
|
||||
|
||||
### 为什么 Skill 内容追加到末尾
|
||||
|
||||
```python
|
||||
def run_skill(skill_name: str) -> str:
|
||||
content = SKILLS.get_skill_content(skill_name)
|
||||
# 作为 tool_result 返回,追加到对话末尾
|
||||
return f"<skill-loaded>{content}</skill-loaded>"
|
||||
|
||||
def agent_loop(messages: list):
|
||||
response = client.messages.create(
|
||||
system=SYSTEM, # 永不改变 - 缓存保持有效
|
||||
messages=messages, # 只追加,不修改
|
||||
)
|
||||
```
|
||||
|
||||
- Skill 内容追加到 messages 末尾
|
||||
- System prompt 和历史消息保持不变
|
||||
- 整个前缀命中缓存,只计算新增的 skill 内容
|
||||
|
||||
### 提供商差异
|
||||
|
||||
| 提供商 | 自动缓存 | 折扣 | 配置 |
|
||||
|--------|---------|------|------|
|
||||
| Claude | ✗ | 90% | 需 `cache_control` |
|
||||
| GPT-5.2 | ✓ | 90% | 无需配置 |
|
||||
| Kimi K2 | ✓ | 90% | 无需配置 |
|
||||
| GLM-4.7 | ✓ | 82% | 无需配置 |
|
||||
| MiniMax M2.1 | ✗ | 90% | 需 `cache_control` |
|
||||
| Gemini 3 | ✓ (隐式) | 90% | 无需配置 |
|
||||
|
||||
**重要**: Claude 和 MiniMax 需要显式配置 `cache_control`,否则即使前缀相同也不会有缓存。
|
||||
|
||||
### 深入了解
|
||||
|
||||
上下文缓存是 Agent 开发中最容易被忽视的成本杀手。完整的专题文章深入探讨:
|
||||
|
||||
1. **思维转变**: 上下文不是"可编辑的变量",而是"只追加的日志"
|
||||
2. **常见陷阱**: 动态system prompt、消息编辑、滑动窗口等5大反模式
|
||||
3. **成本对比**: 同样任务,破坏缓存vs优化缓存可相差7-50倍成本
|
||||
|
||||
请参阅:
|
||||
- [上下文缓存经济学:别让你的Agent"烧钱"](./上下文缓存经济学.md)
|
||||
- [LLM Agent开发者缓存意识自查清单](./开发者缓存意识自查清单.md)
|
||||
|
||||
## 7. 示例技能展示
|
||||
|
||||
**pdf**:PDF 处理专家
|
||||
```sh
|
||||
读取 → pdftotext / PyMuPDF
|
||||
创建 → pandoc / reportlab
|
||||
合并 → PyMuPDF
|
||||
```
|
||||
|
||||
**mcp-builder**:MCP 服务器构建专家
|
||||
```sh
|
||||
Python → mcp SDK + @server.tool 装饰器
|
||||
TypeScript → @modelcontextprotocol/sdk
|
||||
测试 → MCP Inspector
|
||||
```
|
||||
|
||||
**code-review**:代码审查专家
|
||||
```sh
|
||||
安全 → 注入、认证、授权、加密
|
||||
正确性 → 逻辑错误、资源泄漏
|
||||
性能 → N+1、内存、阻塞
|
||||
可维护性 → 命名、复杂度、重复
|
||||
```
|
||||
|
||||
## 8. 背后的思想:知识外化的实践
|
||||
|
||||
> **知识作为一等公民**
|
||||
|
||||
回到我们开头讨论的知识外化范式。传统观点把 AI Agent 看作"工具调用器"——模型决定用什么工具,代码执行工具。但这忽略了一个关键维度:**模型怎么知道应该怎么做?**
|
||||
|
||||
Skills 机制是知识外化的完整实践:
|
||||
|
||||
**过去(知识内化)**:
|
||||
- 知识锁在模型参数里
|
||||
- 修改需要训练
|
||||
- 用户无法访问或理解
|
||||
- 成本高昂,周期漫长
|
||||
|
||||
**现在(知识外化)**:
|
||||
- 知识存在 SKILL.md 文件中
|
||||
- 修改就是编辑文本
|
||||
- 人类可读、可审计
|
||||
- 免费,即时生效
|
||||
|
||||
Skills 机制承认:**领域知识本身就是一种资源**,需要被显式管理。
|
||||
|
||||
1. **分离元数据与内容**
|
||||
|
||||
description 是索引,body 是内容。就像搜索引擎:先用关键词定位,再加载完整页面。
|
||||
|
||||
2. **按需加载而非预加载**
|
||||
|
||||
上下文窗口是宝贵的认知资源。Skills 用渐进式披露确保每个 token 都在需要时才被使用。
|
||||
|
||||
3. **标准化格式促进复用**
|
||||
|
||||
SKILL.md 是开放标准。一个 Skill 写一次,可以在 Claude Code、Cursor、Kode CLI 等任何兼容的 Agent 上使用。这就是知识外化带来的复用性。
|
||||
|
||||
4. **追加而非编辑**
|
||||
|
||||
Skill 内容追加到对话末尾,而非修改 system prompt。这保持了前缀缓存有效,是与自回归模型正确交互的方式。
|
||||
|
||||
5. **在线学习 vs 离线学习**
|
||||
|
||||
传统微调需要离线收集数据、训练、部署。Skills 实现了真正的"在线学习"——在更大的上下文窗口中,模型通过读取 SKILL.md 即时获得新能力,无需任何参数修改。
|
||||
|
||||
知识外化的本质是**把隐式知识变成显式文档**。这不仅改变了技术实现,更改变了人与 AI 协作的方式:
|
||||
- 开发者可以用自然语言"教"模型新技能
|
||||
- 团队可以用 Git 管理和共享知识
|
||||
- 知识可以被版本控制、审计、回滚
|
||||
|
||||
**这是一个从"训练 AI"到"教育 AI"的范式转变。**
|
||||
|
||||
## 9. 五部曲回顾
|
||||
|
||||
| 版本 | 核心主题 | 行数 | 关键洞察 |
|
||||
|------|----------|------|----------|
|
||||
| v0 | Bash is All | ~50 行 | 一个工具 + 递归 = 完整 Agent |
|
||||
| v1 | Model as Agent | ~200 行 | 模型是 80%,代码是工具循环 |
|
||||
| v2 | 结构化规划 | ~300 行 | Todo 工具让计划可见 |
|
||||
| v3 | 分而治之 | ~450 行 | 子代理隔离上下文 |
|
||||
| **v4** | **领域专家** | **~550 行** | **Skills 注入专业知识** |
|
||||
|
||||
五个版本,约 1100 行 Python,覆盖了 Claude Code 的核心设计:
|
||||
|
||||
1. **工具循环**:模型持续调用工具直到完成
|
||||
2. **结构化约束**:Todo 引导规划行为
|
||||
3. **层级委派**:Task 实现任务分解
|
||||
4. **知识注入**:Skill 提供领域专业
|
||||
5. **缓存友好**:只追加不编辑,与自回归模型正确交互
|
||||
|
||||
---
|
||||
|
||||
**工具让模型能做事,技能让模型知道怎么做。**
|
||||
|
||||
这就是 Skills 机制的全部奥秘——把专家知识打包成可加载的模块,让通用模型在需要时变成领域专家。
|
||||
|
||||
完整代码见仓库 `v4_skills_agent.py` 和 `skills/` 目录。
|
||||
|
||||
如果你想要生产级实现,欢迎使用 [Kode](https://github.com/anthropics/kode)。
|
||||
977
articles/上下文缓存经济学.md
Normal file
977
articles/上下文缓存经济学.md
Normal file
@ -0,0 +1,977 @@
|
||||
# 大模型上下文缓存经济学:别让你的Agent"烧钱"
|
||||
|
||||
> **核心警告**: 你习以为常的"编辑消息、修改历史、DIY上下文"操作,在LLM Agent中会让成本暴增7-50倍。本文帮你建立缓存意识,避免成本灾难。
|
||||
|
||||
---
|
||||
|
||||
## 🚨 如果你正在做这些事,请立即停止
|
||||
|
||||
```python
|
||||
# ❌ 危险操作1: 每次都修改system prompt
|
||||
system = f"Current step: {step}, files: {files}..." # 成本爆炸!
|
||||
|
||||
# ❌ 危险操作2: 编辑历史消息
|
||||
messages[2]["content"] = "updated content" # 缓存全失效!
|
||||
|
||||
# ❌ 危险操作3: 删除旧消息(滑动窗口)
|
||||
messages = messages[-10:] # 每次都重新计算!
|
||||
|
||||
# ❌ 危险操作4: 用摘要替换历史
|
||||
messages = [summary] + messages[15:] # 前缀变了,缓存失效!
|
||||
|
||||
# ❌ 危险操作5: 在中间插入消息
|
||||
messages.insert(5, new_message) # 后面全部失效!
|
||||
```
|
||||
|
||||
**这些操作在传统编程中很正常,但在LLM API中会让你的成本增加几十倍。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 思维转变:上下文不是"可编辑的变量",而是"只追加的日志"
|
||||
|
||||
### 1.1 传统编程思维 vs LLM Agent开发
|
||||
|
||||
**传统后端开发** (你习惯的方式):
|
||||
```python
|
||||
# 数据是可以随意修改的
|
||||
user_state = {"step": 1, "data": []}
|
||||
user_state["step"] = 2 # 修改状态
|
||||
user_state["data"].append(new_item) # 添加数据
|
||||
del user_state["history"] # 删除不需要的
|
||||
```
|
||||
|
||||
**LLM Agent开发** (必须遵守的规则):
|
||||
```python
|
||||
# 上下文是"只追加日志",一旦修改前缀 = 缓存全失效
|
||||
messages = [system_msg] # 永不改变
|
||||
messages.append(user_msg) # ✅ 只能追加
|
||||
messages.append(assistant_msg) # ✅ 只能追加
|
||||
# messages[0] = xxx # ❌ 修改前缀 = 成本爆炸
|
||||
# messages = messages[-10:] # ❌ 删除 = 成本爆炸
|
||||
```
|
||||
|
||||
**关键认知**: LLM API的上下文有"前缀缓存"机制——前缀不变就能复用,前缀一变就要全部重算。
|
||||
|
||||
### 1.2 忽视缓存的真实代价
|
||||
|
||||
许多传统 AI 开发者在使用 **LangGraph、LangChain、AutoGen** 等框架开发 Agent 时,习惯性地采用传统软件开发思维:
|
||||
|
||||
- 编排和编辑上下文
|
||||
- DIY 消息队列,插入、删除、修改历史消息
|
||||
- 压缩或摘要替换长对话
|
||||
- 在 system prompt 中注入动态状态
|
||||
- 使用滑动窗口截断旧消息
|
||||
|
||||
**这些操作在传统软件中是正常的,但在大模型 API 中会导致缓存完全失效,成本和性能双重爆炸。**
|
||||
|
||||
### 1.2 成本差距有多大?
|
||||
|
||||
一个典型的 SWE (软件工程) Agent 任务可能消耗 **100K-1M tokens**。如果你的 Agent 每天处理 100 个任务,按 Claude Sonnet 4.5 的 $3/M 输入价格计算:
|
||||
|
||||
| 策略 | 每天成本 | 年成本 | 说明 |
|
||||
|------|---------|--------|------|
|
||||
| **破坏缓存** (每轮编辑上下文) | **$150** | **$54,750** | 每轮重新计算全部上下文 |
|
||||
| **有缓存优化** (只追加) | **$26** | **$9,490** | 60% 缓存命中率 |
|
||||
| **节省** | **$124/天** | **$45,260/年** | **83% 成本节省** |
|
||||
|
||||
对于一个中等规模的 Agent 应用,**理解缓存机制可以每年节省数万到数十万美元**。
|
||||
|
||||
### 1.3 自回归模型的本质特征
|
||||
|
||||
大语言模型是**自回归**的——生成每个新 token 都需要 attend 到之前所有 token。这意味着:
|
||||
|
||||
```
|
||||
上下文长度: 50K tokens
|
||||
生成 1 个 token: 需要对 50K tokens 做 attention
|
||||
生成 100 个 token: 需要对 50K-50.1K tokens 做 attention,共 5,005,000 次 attention 操作
|
||||
```
|
||||
|
||||
如果你**每次请求都修改上下文的前缀**:
|
||||
- 之前缓存的 KV 向量全部失效
|
||||
- 大模型提供商需要重新计算整个前缀
|
||||
- 你需要为相同内容重复付费
|
||||
|
||||
如果你**只追加新内容**:
|
||||
- 提供商复用已缓存的 KV 向量
|
||||
- 只计算新增部分
|
||||
- 缓存命中部分享受 **90% 折扣** (大部分提供商)
|
||||
|
||||
## 2. 自回归模型与 KV Cache 原理
|
||||
|
||||
### 2.1 为什么需要缓存?
|
||||
|
||||
大语言模型是**自回归**的:生成每个 token 都需要 attend 到之前所有 token。
|
||||
|
||||
```
|
||||
输入: [A, B, C, D, E]
|
||||
生成 F: Attention(F, [A,B,C,D,E]) → 计算 5 次 attention
|
||||
生成 G: Attention(G, [A,B,C,D,E,F]) → 计算 6 次 attention
|
||||
```
|
||||
|
||||
对于 200K 上下文窗口,每生成一个 token 都要与前面所有 token 做 attention 计算。**KV Cache** 通过缓存已计算的 Key-Value 向量来避免重复计算。
|
||||
|
||||
### 2.2 Prompt Cache 的工作原理
|
||||
|
||||
```
|
||||
请求 1: [System, User1, Asst1, User2]
|
||||
←────── 全部计算 ──────→
|
||||
(首次处理,写入缓存)
|
||||
|
||||
请求 2: [System, User1, Asst1, User2, Asst2, User3]
|
||||
←────── 缓存命中 ──────→ ←─ 新计算 ─→
|
||||
(复用已缓存的 KV) (仅计算新增部分)
|
||||
```
|
||||
|
||||
**关键条件**: 缓存命中要求**前缀完全相同**,一个字符都不能变。
|
||||
|
||||
## 3. 常见的缓存破坏模式与反模式
|
||||
|
||||
### 3.1 LangGraph / LangChain 常见反模式
|
||||
|
||||
许多开发者在使用这些流行框架时,会不自觉地采用破坏缓存的模式:
|
||||
|
||||
#### 反模式 1: 状态注入到 System Prompt
|
||||
|
||||
```python
|
||||
def build_system_prompt(state: dict) -> str:
|
||||
return f"""You are an assistant. Current state:
|
||||
- Step: {state['current_step']}
|
||||
- Progress: {state['progress']}%
|
||||
- Context: {state['context']}
|
||||
"""
|
||||
|
||||
response = client.chat(
|
||||
system=build_system_prompt(state), # 每次都不同!
|
||||
messages=messages
|
||||
)
|
||||
```
|
||||
|
||||
**问题**: 每轮 state 都变,system prompt 每次都不同,**缓存 100% 失效**。
|
||||
|
||||
**正确做法**:
|
||||
```python
|
||||
FIXED_SYSTEM_PROMPT = "You are an assistant." # 固定不变
|
||||
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": f"Current state: step={state['current_step']}, progress={state['progress']}%"
|
||||
})
|
||||
|
||||
response = client.chat(
|
||||
system=FIXED_SYSTEM_PROMPT, # 永远不变,缓存有效
|
||||
messages=messages # 只追加
|
||||
)
|
||||
```
|
||||
|
||||
#### 反模式 2: 消息压缩与摘要替换
|
||||
|
||||
```python
|
||||
def compress_messages(messages: list) -> list:
|
||||
if len(messages) > 20:
|
||||
old_messages = messages[:15]
|
||||
summary = summarize(old_messages)
|
||||
# 用摘要替换旧消息
|
||||
return [{"role": "system", "content": summary}] + messages[15:]
|
||||
return messages
|
||||
|
||||
messages = compress_messages(messages) # 前缀变了,缓存失效!
|
||||
```
|
||||
|
||||
**问题**: 替换操作改变了前缀,之前的所有缓存失效。
|
||||
|
||||
**成本影响** (50 轮对话,每轮 50K 上下文):
|
||||
- 无压缩: 首次 $0.1875 (写入) + 后续 49 轮 × $0.015 = **$0.92**
|
||||
- 压缩替换 (每 20 轮): 3 次重建缓存 × $0.1875 = **$0.56** (看似更便宜)
|
||||
- **实际**: 压缩后丢失上下文,模型表现下降,需要更多轮次,**总成本反而更高**
|
||||
|
||||
**正确做法**: 使用子 Agent 隔离上下文,只返回摘要结果。
|
||||
|
||||
#### 反模式 3: 滑动窗口截断
|
||||
|
||||
```python
|
||||
def sliding_window(messages: list, window_size: int = 10) -> list:
|
||||
if len(messages) > window_size:
|
||||
return messages[-window_size:] # 只保留最近 10 条
|
||||
return messages
|
||||
```
|
||||
|
||||
**问题**: 删除旧消息 = 改变前缀 = 缓存失效。
|
||||
|
||||
**成本对比** (Claude Sonnet 4.5,30 轮对话,每轮 3K 新增):
|
||||
|
||||
| 策略 | Token 计算 | 成本 |
|
||||
|------|-----------|------|
|
||||
| 只追加 | 首次 8K + 29 轮 × 3K (缓存) = 8K + 87K (缓存) | $0.030 + $0.026 = **$0.056** |
|
||||
| 滑动窗口 (10 条) | 每轮重算 30K | 30 轮 × 30K × $3/M = **$2.70** |
|
||||
| **成本差距** | | **48倍** |
|
||||
|
||||
#### 反模式 4: 消息编辑与插入
|
||||
|
||||
```python
|
||||
def fix_message_format(messages: list) -> list:
|
||||
for msg in messages:
|
||||
if 'timestamp' in msg:
|
||||
del msg['timestamp'] # 修改了历史消息
|
||||
if msg['role'] == 'assistant' and 'tool_calls' in msg:
|
||||
msg['content'] = msg['content'].strip() # 修改内容
|
||||
return messages
|
||||
```
|
||||
|
||||
**问题**: 即使是微小的修改(删除字段、trim 空格),也会使缓存失效。
|
||||
|
||||
#### 反模式 5: LangGraph 状态管理不当
|
||||
|
||||
```python
|
||||
from langgraph.graph import StateGraph
|
||||
|
||||
class AgentState(TypedDict):
|
||||
messages: list[BaseMessage]
|
||||
context: str
|
||||
step: int
|
||||
|
||||
def node_a(state: AgentState) -> AgentState:
|
||||
# 错误: 修改了 messages
|
||||
state["messages"][0].content += f"\n[Step {state['step']}]"
|
||||
return state
|
||||
|
||||
graph = StateGraph(AgentState)
|
||||
graph.add_node("a", node_a)
|
||||
```
|
||||
|
||||
**问题**: 在 LangGraph 节点中修改历史消息,会导致后续所有节点的缓存失效。
|
||||
|
||||
**正确做法**: 在 LangGraph 中,将状态信息作为**新消息追加**,而非修改现有消息。
|
||||
|
||||
### 3.2 缓存破坏模式总结
|
||||
|
||||
| 反模式 | 表现 | 缓存影响 | 成本倍数 |
|
||||
|--------|------|----------|----------|
|
||||
| 动态 System Prompt | 每轮注入状态 | 100% 失效 | **20-50x** |
|
||||
| 消息压缩替换 | 用摘要替换历史 | 替换点后全失效 | **5-15x** |
|
||||
| 滑动窗口 | 删除旧消息 | 100% 失效 | **30-50x** |
|
||||
| 消息编辑 | 修改历史内容 | 修改点后全失效 | **10-30x** |
|
||||
| 消息插入 | 在中间插入 | 插入点后全失效 | **10-30x** |
|
||||
| 多 Agent 全连接 | 每个 Agent 看到所有消息 | 上下文膨胀 | **3-4x** (相对单Agent) |
|
||||
|
||||
### 3.3 为什么这些模式如此常见?
|
||||
|
||||
1. **传统编程习惯**: 在常规软件中,编辑、插入、删除是正常操作
|
||||
2. **框架误导**: 一些 Agent 框架提供了"方便的"消息管理 API,但没有警告缓存影响
|
||||
3. **缺乏可见性**: 大部分 API 不显示缓存命中率,开发者看不到成本差异
|
||||
4. **示例代码**: 许多教程和示例代码为了"简洁"而忽略了缓存最佳实践
|
||||
|
||||
### 3.4 如何检测你是否在破坏缓存?
|
||||
|
||||
**Claude API 响应头**:
|
||||
```json
|
||||
{
|
||||
"usage": {
|
||||
"input_tokens": 50000,
|
||||
"cache_creation_input_tokens": 48000, // 首次写入缓存
|
||||
"cache_read_input_tokens": 48000, // 后续命中
|
||||
"output_tokens": 150
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**如果你看到**:
|
||||
- `cache_read_input_tokens` 始终为 0 → 缓存从未命中
|
||||
- `cache_creation_input_tokens` 每次都很大 → 你在破坏缓存
|
||||
|
||||
**OpenAI / Kimi K2 / GLM (自动缓存)**:
|
||||
```json
|
||||
{
|
||||
"usage": {
|
||||
"prompt_tokens": 50000,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 48000 // 缓存命中的 token 数
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**如果你看到**:
|
||||
- `cached_tokens` 为 0 或很小 → 缓存未生效
|
||||
|
||||
## 4. 主流模型缓存策略详解
|
||||
|
||||
### 4.1 Claude (Anthropic)
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| **是否自动缓存** | **否**,必须显式使用 `cache_control` 参数 |
|
||||
| **最大断点数** | 每个请求最多 4 个 cache breakpoint |
|
||||
| **TTL (生存时间)** | 默认 5 分钟,可选 1 小时 (价格更高) |
|
||||
| **最小 token 要求** | Opus 4.5: 1,024 tokens; Sonnet 4.5: 1,024 tokens; Haiku 4.5: 4,096 tokens |
|
||||
| **缓存层级** | tools → system → messages (修改任一层级会使后续层级缓存失效) |
|
||||
| **Lookback 窗口** | 每个断点向前最多检查 20 个 blocks 寻找最长可命中前缀 |
|
||||
|
||||
**定价 (USD/M tokens)**:
|
||||
|
||||
| 模型 | 输入 | 缓存写入(5min) | 缓存写入(1hr) | 缓存读取 | 输出 |
|
||||
|------|------|----------------|---------------|----------|------|
|
||||
| Claude Sonnet 4.5 | $3.00 | $3.75 (1.25x) | $6.00 (2x) | $0.30 (0.1x) | $15.00 |
|
||||
| Claude Opus 4.5 | $5.00 | $6.25 (1.25x) | $10.00 (2x) | $0.50 (0.1x) | $25.00 |
|
||||
| Claude Haiku 4.5 | $1.00 | $1.25 (1.25x) | $2.00 (2x) | $0.10 (0.1x) | $5.00 |
|
||||
|
||||
**使用方法**:
|
||||
|
||||
```json
|
||||
{
|
||||
"system": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "You are a coding assistant...",
|
||||
"cache_control": {"type": "ephemeral"}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**关键洞察**:
|
||||
- Claude 的缓存**不是自动的**。如果你不显式设置 `cache_control`,即使前缀完全相同也不会有缓存命中
|
||||
- 每次缓存命中会**自动刷新 TTL**,且不额外收费
|
||||
- 修改 system prompt 会使 system 和后续 messages 缓存失效,但 tools 缓存仍保留
|
||||
|
||||
### 4.2 Kimi K2 (Moonshot AI)
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| **是否自动缓存** | **是**,系统自动检测重复前缀 (无需代码修改) |
|
||||
| **上下文窗口** | 256K tokens (kimi-k2-0905 版本,2025年9月更新) |
|
||||
| **TTL** | 自动缓存: 未公开; 显式缓存: 用户可设置 (支持刷新) |
|
||||
| **型号** | `kimi-k2-0905` (Instruct 版本) |
|
||||
|
||||
**定价 (CNY/M tokens)**:
|
||||
|
||||
| 类型 | 价格 (CNY) | 价格 (USD 约) | 说明 |
|
||||
|------|------------|---------------|------|
|
||||
| 输入 (cache miss) | ¥4.20 | ~$0.60 | 标准输入价 |
|
||||
| 输入 (cache hit) | ¥0.42 | ~$0.06 | **90% 折扣** |
|
||||
| 输出 | ¥17.50 | ~$2.50 | - |
|
||||
|
||||
**特点**:
|
||||
- Kimi K2 的自动缓存是**全自动**的,无需任何代码修改
|
||||
- 系统自动检测相同前缀并复用缓存,缓存命中时享受 **90% 折扣**
|
||||
- 官方称可降低长文本成本最高 90%,首 Token 延迟降低 83%
|
||||
|
||||
**显式缓存 API (Context Caching,公测中)**:
|
||||
- 缓存创建费: ¥24/M tokens (~$3.30)
|
||||
- 存储费用: ¥5/M tokens/分钟 (2024年8月降价50%后)
|
||||
- 每次命中调用费: ¥0.02/次
|
||||
- 初期仅限 Tier5 用户,后开放公测
|
||||
|
||||
### 4.3 GLM-4.7 (智谱 AI)
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| **是否自动缓存** | **是**,自动前缀匹配 (需按 Context Caching 用法组织上下文) |
|
||||
| **上下文窗口** | 200K tokens (支持最高 128K 输出) |
|
||||
| **TTL** | 官方未公开,描述为"有合理的时间限制,过期后重新计算" |
|
||||
| **发布日期** | 2025年12月22日 (开源) |
|
||||
|
||||
**定价 (USD/M tokens)**:
|
||||
|
||||
| 类型 | 价格 | 说明 |
|
||||
|------|------|------|
|
||||
| 输入 (新 token) | $0.60 | 标准输入价 |
|
||||
| 输入 (缓存命中) | $0.11 | **82% 折扣** |
|
||||
| 输出 | $2.20 | - |
|
||||
| 缓存存储 | 免费 | **限时免费**,后续可能收费 |
|
||||
|
||||
**特点**:
|
||||
- GLM-4.7 提供 **82% 的缓存折扣**,是国产模型中折扣力度较大的
|
||||
- 响应中返回 `usage.prompt_tokens_details.cached_tokens` 用于追踪缓存命中
|
||||
- 智谱同时提供开源版本,本地部署可避免按 token 计费
|
||||
- 在编程评测中表现接近 Claude Sonnet 4.5 和 GPT-5.2
|
||||
|
||||
### 4.4 MiniMax M2.1
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| **是否自动缓存** | **否**,需要使用 `cache_control` 参数 (与 Claude 类似) |
|
||||
| **上下文窗口** | 200K+ tokens |
|
||||
| **TTL** | 5 分钟 (命中时自动刷新,不额外收费) |
|
||||
| **发布日期** | 2025年12月22日 (开源) |
|
||||
| **缓存层级** | tools → system → messages |
|
||||
| **最大断点数** | 4 个 `cache_control` |
|
||||
|
||||
**定价 (USD/M tokens)**:
|
||||
|
||||
| 类型 | 价格 | 说明 |
|
||||
|------|------|------|
|
||||
| 输入 | $0.30 | 标准输入价 |
|
||||
| 缓存写入 | $0.375 | 1.25x 输入价 |
|
||||
| 缓存读取 | $0.03 | **0.1x 输入价 (90% 折扣)** |
|
||||
| 输出 | $1.20 | - |
|
||||
|
||||
**特点**:
|
||||
- MiniMax M2.1 是目前**最便宜的高性能模型之一**
|
||||
- 缓存读取仅 $0.03/M,是 Claude Sonnet 的 1/10
|
||||
- 采用 MoE 架构 (230B 总参数,10B 激活),专为代码和 Agent 场景优化
|
||||
- 缓存机制与 Claude 高度相似 (cache_control, TTL, 层级)
|
||||
- 提供 M2.1-lightning 高速版本,输出价 $2.4/M
|
||||
|
||||
### 4.5 GPT-5.2 (OpenAI)
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| **是否自动缓存** | **是**,无需代码修改,自动针对重复前缀 |
|
||||
| **上下文窗口** | 256K-400K tokens |
|
||||
| **TTL** | 默认 5-10 分钟,部分模型支持扩展至 24 小时 |
|
||||
| **最小 token 要求** | ≥1024 tokens 自动启用 |
|
||||
|
||||
**定价 (USD/M tokens)**:
|
||||
|
||||
| 类型 | 价格 | 说明 |
|
||||
|------|------|------|
|
||||
| 输入 | $1.75 | 标准输入价 |
|
||||
| 缓存读取 | $0.175 | **0.1x 输入价 (90% 折扣)** |
|
||||
| 输出 | $14.00 | - |
|
||||
|
||||
**特点**:
|
||||
- OpenAI 的缓存是**全自动**的,无需任何参数配置
|
||||
- 可选 `prompt_cache_key` 和 `prompt_cache_retention` 参数进行精细控制
|
||||
- 官方称可降低输入成本最高 90%、延迟最高 80%
|
||||
|
||||
**注意**:GPT-4o 等 2024 年模型缓存折扣为 50%,GPT-5 系列为 90%
|
||||
|
||||
### 4.6 Gemini 3 (Google)
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| **隐式缓存** | **是** (Gemini 2.5+ 默认启用),自动检测重复前缀 |
|
||||
| **显式缓存** | 需要创建 `CachedContent` 对象并引用 |
|
||||
| **上下文窗口** | Flash: 1M tokens; Pro: 1M-2M tokens |
|
||||
| **TTL** | 显式缓存默认 1 小时,可自定义 |
|
||||
| **最小 token 要求** | Gemini 3 Pro: 4096; Gemini 2.5 Flash: 1024 |
|
||||
|
||||
**定价 (USD/M tokens)**:
|
||||
|
||||
| 模型 | 输入 | 缓存读取 | 输出 | 存储费 |
|
||||
|------|------|----------|------|--------|
|
||||
| Gemini 3 Flash | $0.50 | $0.05 (0.1x) | $3.00 | $1.00/M·hour |
|
||||
| Gemini 3 Pro | $2.00 | $0.20 (0.1x) | $12.00 | $1.00/M·hour |
|
||||
|
||||
**特点**:
|
||||
- 隐式缓存自动生效,但不保证每次都命中 (高并发时可能溢出)
|
||||
- 显式缓存需要创建缓存对象,但可确保命中
|
||||
- 显式缓存有**存储费**,按 token·小时计费
|
||||
- 支持超长上下文 (1M-2M),适合处理整本书或大型代码库
|
||||
|
||||
### 4.7 综合对比
|
||||
|
||||
| 模型 | 上下文 | 标准输入 | 缓存读取 | 缓存折扣 | 自动缓存 |
|
||||
|------|--------|----------|----------|----------|----------|
|
||||
| Claude Sonnet 4.5 | 200K | $3.00/M | $0.30/M | 90% | 否 (需 cache_control) |
|
||||
| Claude Opus 4.5 | 200K | $5.00/M | $0.50/M | 90% | 否 |
|
||||
| GPT-5.2 | 256K+ | $1.75/M | $0.175/M | 90% | **是** |
|
||||
| Gemini 3 Flash | 1M | $0.50/M | $0.05/M | 90% | 是 (隐式) |
|
||||
| Gemini 3 Pro | 1M+ | $2.00/M | $0.20/M | 90% | 是 (隐式) |
|
||||
| Kimi K2 | 256K | ~$0.60/M | ~$0.06/M | 90% | **是** |
|
||||
| GLM-4.7 | 200K | $0.60/M | $0.11/M | 82% | 是 |
|
||||
| MiniMax M2.1 | 200K | $0.30/M | $0.03/M | 90% | 否 (需 cache_control) |
|
||||
|
||||
## 5. 详细成本计算:50 轮 SWE 任务实例
|
||||
|
||||
本节通过一个典型的软件工程 Agent 任务,逐轮计算 token 消耗和成本,展示缓存优化的真实影响。
|
||||
|
||||
### 5.1 任务场景设定
|
||||
|
||||
**任务**: 修复一个涉及多文件的 bug,需要读取代码、分析、修改、测试
|
||||
|
||||
**Agent 配置**:
|
||||
- System prompt: 5,000 tokens
|
||||
- 工具定义 (bash, read_file, write_file, grep, etc.): 3,000 tokens
|
||||
- 基础上下文: 8,000 tokens
|
||||
|
||||
**每轮典型操作**:
|
||||
- 用户指令: 200 tokens
|
||||
- 模型推理 + 工具调用: 500 tokens
|
||||
- 工具返回 (代码、测试结果等): 2,000 tokens (平均)
|
||||
- 模型响应: 800 tokens
|
||||
|
||||
### 5.2 场景 A: 破坏缓存 (动态 System Prompt)
|
||||
|
||||
**错误做法**: 每轮将当前状态注入 system prompt
|
||||
|
||||
```python
|
||||
def build_system_prompt(step: int, context: dict) -> str:
|
||||
return f"""You are a coding assistant.
|
||||
Current step: {step}
|
||||
Files modified: {context['files']}
|
||||
Tests passed: {context['tests_passed']}
|
||||
...(total 5,000 tokens)"""
|
||||
```
|
||||
|
||||
**逐轮成本计算** (Claude Sonnet 4.5, $3/M 输入):
|
||||
|
||||
| 轮次 | 上下文累计 | System 变化 | 缓存状态 | 计算 tokens | 成本 |
|
||||
|------|-----------|------------|---------|-------------|------|
|
||||
| 1 | 8K + 3.5K = 11.5K | ✗ | 无缓存 | 11.5K | $0.0345 |
|
||||
| 2 | 11.5K + 3.5K = 15K | ✗ | 完全失效 | 15K | $0.0450 |
|
||||
| 3 | 15K + 3.5K = 18.5K | ✗ | 完全失效 | 18.5K | $0.0555 |
|
||||
| ... | ... | ... | ... | ... | ... |
|
||||
| 50 | 8K + 49×3.5K = 179.5K | ✗ | 完全失效 | 179.5K | $0.5385 |
|
||||
|
||||
**总成本 (50 轮)**:
|
||||
```
|
||||
Sum(8K + n×3.5K) for n=0 to 49
|
||||
= 50×8K + 3.5K×(0+1+2+...+49)
|
||||
= 400K + 3.5K × 1,225
|
||||
= 400K + 4,287.5K
|
||||
= 4,687.5K tokens × $3/M = $14.06
|
||||
```
|
||||
|
||||
### 5.3 场景 B: 缓存优化 (固定 System Prompt)
|
||||
|
||||
**正确做法**: System prompt 固定,状态作为新消息追加
|
||||
|
||||
```python
|
||||
FIXED_SYSTEM_PROMPT = "You are a coding assistant. (5,000 tokens)"
|
||||
|
||||
# 每轮追加状态信息
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": f"Status update: step={step}, files={files}..."
|
||||
})
|
||||
```
|
||||
|
||||
**使用 Claude cache_control**:
|
||||
|
||||
```python
|
||||
system = [{
|
||||
"type": "text",
|
||||
"text": FIXED_SYSTEM_PROMPT,
|
||||
"cache_control": {"type": "ephemeral"}
|
||||
}]
|
||||
```
|
||||
|
||||
**逐轮成本计算**:
|
||||
|
||||
| 轮次 | 上下文累计 | 缓存写入 | 缓存读取 | 新增计算 | 成本 |
|
||||
|------|-----------|---------|---------|---------|------|
|
||||
| 1 | 11.5K | 8K @ $3.75/M | 0 | 3.5K @ $3/M | $0.030 + $0.0105 = $0.0405 |
|
||||
| 2 | 15K | 0 | 11.5K @ $0.30/M | 3.5K @ $3/M | $0.00345 + $0.0105 = $0.01395 |
|
||||
| 3 | 18.5K | 0 | 15K @ $0.30/M | 3.5K @ $0.30/M | $0.0045 + $0.0105 = $0.0150 |
|
||||
| ... | ... | ... | ... | ... | ... |
|
||||
| 50 | 179.5K | 0 | 176K @ $0.30/M | 3.5K @ $3/M | $0.0528 + $0.0105 = $0.0633 |
|
||||
|
||||
**总成本 (50 轮)**:
|
||||
```
|
||||
首轮: 8K×$3.75/M + 3.5K×$3/M = $0.030 + $0.0105 = $0.0405
|
||||
后续 49 轮:
|
||||
- 缓存读取: (11.5K + 15K + ... + 176K) × $0.30/M
|
||||
- 新增计算: 49 × 3.5K × $3/M = 171.5K × $3/M = $0.5145
|
||||
|
||||
缓存读取累计:
|
||||
Sum(8K + n×3.5K) for n=1 to 49 = 4,287.5K × $0.30/M = $1.29
|
||||
|
||||
总计: $0.0405 + $1.29 + $0.5145 = $1.845
|
||||
```
|
||||
|
||||
**节省**: ($14.06 - $1.845) / $14.06 = **86.9%**
|
||||
|
||||
### 5.4 场景 C: 使用国产模型 (Kimi K2 自动缓存)
|
||||
|
||||
**价格**: $0.60/M 输入 (标准),$0.15/M 输入 (缓存命中)
|
||||
|
||||
**逐轮成本计算**:
|
||||
|
||||
| 轮次 | 上下文累计 | 缓存命中 | 新增计算 | 成本 |
|
||||
|------|-----------|---------|---------|------|
|
||||
| 1 | 11.5K | 0 | 11.5K @ $0.60/M | $0.0069 |
|
||||
| 2 | 15K | 11.5K @ $0.15/M | 3.5K @ $0.60/M | $0.001725 + $0.0021 = $0.003825 |
|
||||
| 3 | 18.5K | 15K @ $0.15/M | 3.5K @ $0.60/M | $0.00225 + $0.0021 = $0.00435 |
|
||||
| ... | ... | ... | ... | ... |
|
||||
| 50 | 179.5K | 176K @ $0.15/M | 3.5K @ $0.60/M | $0.0264 + $0.0021 = $0.0285 |
|
||||
|
||||
**总成本 (50 轮)**:
|
||||
```
|
||||
首轮: 11.5K × $0.60/M = $0.0069
|
||||
后续 49 轮:
|
||||
- 缓存命中: 4,287.5K × $0.15/M = $0.643
|
||||
- 新增计算: 49 × 3.5K × $0.60/M = $0.1029
|
||||
|
||||
总计: $0.0069 + $0.643 + $0.1029 = $0.753
|
||||
```
|
||||
|
||||
**相比 Claude 无缓存**: ($14.06 - $0.753) / $14.06 = **94.6% 节省**
|
||||
|
||||
### 5.5 场景 D: 滑动窗口反模式
|
||||
|
||||
**错误做法**: 保留最近 10 轮对话,删除旧的
|
||||
|
||||
**每轮都需要重新计算** (因为前缀一直在变):
|
||||
|
||||
| 轮次 | 上下文 | 计算 tokens | 成本 (Claude) |
|
||||
|------|--------|-------------|---------------|
|
||||
| 1-10 | 8K + n×3.5K | 逐渐增长 | 逐渐增长 |
|
||||
| 11+ | 8K + 10×3.5K = 43K | **每轮 43K** | **每轮 $0.129** |
|
||||
|
||||
**总成本 (50 轮)**:
|
||||
```
|
||||
前 10 轮: Sum(8K + n×3.5K) for n=0 to 9 = 237.5K × $3/M = $0.7125
|
||||
后 40 轮: 40 × 43K × $3/M = 5,160K × $3/M = $15.48
|
||||
|
||||
总计: $0.7125 + $15.48 = $16.19
|
||||
```
|
||||
|
||||
**比缓存优化版贵**: $16.19 / $1.845 = **8.8 倍**
|
||||
|
||||
### 5.6 多模型对比总结
|
||||
|
||||
**50 轮 SWE 任务总成本对比**:
|
||||
|
||||
| 策略 | 模型 | 总成本 | 相对成本 | 节省 |
|
||||
|------|------|--------|---------|------|
|
||||
| **破坏缓存** | Claude Sonnet 4.5 | **$14.06** | 7.6x | 基准 |
|
||||
| **滑动窗口** | Claude Sonnet 4.5 | **$16.19** | 8.8x | -15% (更贵!) |
|
||||
| **缓存优化** | Claude Sonnet 4.5 | **$1.845** | 1.0x | **87%** |
|
||||
| **缓存优化** | GPT-5.2 | **$1.08** | 0.59x | **92%** |
|
||||
| **自动缓存** | Kimi K2 | **$0.753** | 0.41x | **95%** |
|
||||
| **自动缓存** | GLM-4.7 | **$0.84** | 0.46x | **94%** |
|
||||
| **自动缓存** | MiniMax M2.1 | **$0.38** | 0.21x | **97%** |
|
||||
|
||||
**关键洞察**:
|
||||
1. 破坏缓存比优化版贵 **7-8 倍**
|
||||
2. 滑动窗口不仅破坏缓存,还因为重复计算更贵
|
||||
3. 国产模型的自动缓存 + 低价格组合极具竞争力
|
||||
4. MiniMax M2.1 在这个场景下成本最低
|
||||
|
||||
## 6. Agent 编排策略与缓存影响
|
||||
|
||||
不同的 Agent 编排策略对缓存有着截然不同的影响。
|
||||
|
||||
### 6.1 编排模式对比
|
||||
|
||||
| 模式 | 描述 | 缓存友好度 | Token 消耗倍数 |
|
||||
|------|------|-----------|---------------|
|
||||
| **单 Agent 循环** | 一个 Agent 持续调用工具直到完成 | 好 | 1x (基准) |
|
||||
| **多 Agent 顺序** | 多个 Agent 顺序执行,传递结果 | 中 | 4-6x |
|
||||
| **多 Agent 协作** | 多个 Agent 相互对话协商 | 差 | **15x+** |
|
||||
| **层级 Agent** | 父子层级结构,子 Agent 隔离上下文 | 好 | 2-4x |
|
||||
| **Graph 编排** | DAG 节点式编排 (LangGraph) | 好* | 2-4x |
|
||||
| **ReAct 循环** | Thought → Action → Observation 循环 | 差 | 高 (每轮累积) |
|
||||
|
||||
*需要正确配置节点级缓存
|
||||
|
||||
**研究数据支撑**:
|
||||
- Anthropic 研究 (2025年6月): 多 Agent 系统相比简单聊天消耗约**15倍** tokens,性能提升90.2%
|
||||
- arXiv 论文: 部分复杂任务在多 Agent 框架下消耗高达 **200 万 tokens**
|
||||
- 学术研究表明,引入 Supervisor Agent 可减少约 30% 的无效 token 消耗
|
||||
|
||||
**重要**: 上述"15倍"是相对于简单聊天任务。多Agent相对单Agent的实际倍数约为**3-4倍**,而非更高的数值。选择多Agent架构需要权衡性能提升(90%+)与成本增加(3-4x)。
|
||||
|
||||
### 6.2 破坏缓存的操作
|
||||
|
||||
| 操作 | 影响 | 结果 |
|
||||
|------|------|------|
|
||||
| **编辑历史消息** | 改变前缀 | 缓存完全失效 |
|
||||
| **在中间插入消息** | 后续前缀变化 | 插入点之后需重新计算 |
|
||||
| **修改 system prompt** | 最前面变化 | **整个对话缓存失效** |
|
||||
| **消息压缩/摘要替换** | 替换原始内容 | 前缀变化,缓存失效 |
|
||||
| **滑动窗口截断** | 删除旧消息 | 前缀变化,缓存失效 |
|
||||
|
||||
### 6.3 缓存层级与失效传播
|
||||
|
||||
Claude 的缓存按以下顺序处理:
|
||||
|
||||
```
|
||||
tools 变更
|
||||
↓ (使下游全部失效)
|
||||
system prompt 变更
|
||||
↓ (使下游全部失效)
|
||||
messages 内容变更
|
||||
↓ (使该点之后失效)
|
||||
tool_result (作为 user message)
|
||||
✓ (保持缓存有效)
|
||||
```
|
||||
|
||||
**关键洞察**: 将动态内容放在对话末尾(作为 tool_result 或新的 user message),而不是修改前面的内容。这是 Claude Code 和 Kode CLI 采用的策略。
|
||||
|
||||
### 6.4 多 Agent 的 Token 消耗问题
|
||||
|
||||
根据 Anthropic 2025年6月的研究:
|
||||
|
||||
| 指标 | 简单聊天 | 单 Agent | 多 Agent | 倍数关系 |
|
||||
|------|---------|----------|----------|---------|
|
||||
| 输入 tokens | 1x | 4x | 15x | 多Agent约为单Agent的**3.75x** |
|
||||
| 性能提升 | 基准 | - | +90.2% | 相对单Agent |
|
||||
| Token重要性 | - | - | - | Token使用量解释80%性能差异 |
|
||||
|
||||
**结论**:
|
||||
- 多 Agent 系统相比简单聊天消耗约**15倍** tokens
|
||||
- 多 Agent 相比单 Agent 约**3-4倍**消耗
|
||||
- 性能提升90.2%,但成本显著增加
|
||||
- 适合高价值任务,其收益能覆盖额外成本
|
||||
|
||||
**优化策略**:
|
||||
- 使用**环形拓扑**或**层级结构**而非全连接,减少冗余传递
|
||||
- 子 Agent 采用**隔离上下文**,只返回摘要结果给父 Agent
|
||||
- 选择性共享而非广播所有消息
|
||||
- 通过prompt caching复用重复的system prompt和工具定义
|
||||
|
||||
**数据来源**: [Anthropic: Multi-agent Research System (2025-06)](https://www.anthropic.com/engineering/multi-agent-research-system)
|
||||
|
||||
## 7. 典型任务的 Token 消耗
|
||||
|
||||
### 7.1 软件工程 (SWE) 任务
|
||||
|
||||
| 指标 | 范围 | 说明 |
|
||||
|------|------|------|
|
||||
| 单个 issue 总消耗 | 100K - 1M tokens | SWE-bench 限制 |
|
||||
| 基础上下文 | ~20K tokens | 系统提示 + 工具定义 |
|
||||
| 每轮交互增量 | 2K - 10K tokens | 取决于文件大小和工具输出 |
|
||||
| 典型任务轮数 | 10 - 50 轮 | 复杂任务可能更多 |
|
||||
|
||||
**Claude Code 实际数据**:
|
||||
- 平均每开发者每天: $6 (90% 用户低于 $12/天)
|
||||
- Pro Plan 每 5 小时: ~44,000 tokens (10-40 次提示)
|
||||
- 高级工具使用可减少 37% token 消耗
|
||||
|
||||
**SWE-bench 成本演变** (Claude Sonnet):
|
||||
- 2024年8月 (无缓存): $5.29/任务
|
||||
- 2024年9月 (有缓存): $0.91/任务
|
||||
- **降幅: 83%**
|
||||
|
||||
### 7.2 浏览器自动化任务
|
||||
|
||||
| 指标 | 范围 | 说明 |
|
||||
|------|------|------|
|
||||
| 每次截图 | ~1,600 tokens | Claude Vision 最大图片 |
|
||||
| 每个任务交互轮数 | 10 - 50 轮 | 每轮包含截图分析 |
|
||||
| 单任务总消耗 | 765 - 3,225 tokens | 不含截图 |
|
||||
| 完整任务 (含截图) | 20K - 100K tokens | 估算 |
|
||||
|
||||
**不同 Browser Agent 成本对比**:
|
||||
|
||||
| Agent | 成功率 | 平均 tokens/任务 | 成本/任务 |
|
||||
|-------|--------|------------------|-----------|
|
||||
| DroidRun | 43% | 3,225 | $0.075 |
|
||||
| Mobile-Agent | 29% | 1,130 | $0.025 |
|
||||
| AutoDroid | 14% | 765 | $0.017 |
|
||||
|
||||
**Vision Token 计算** (Claude):
|
||||
```
|
||||
tokens = (width × height) / 750
|
||||
```
|
||||
最大图片约 1,600 tokens,相当于 $4.80/千张图片 (Sonnet 4.5)。
|
||||
|
||||
### 7.3 工具调用开销
|
||||
|
||||
| 指标 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| 每条消息开销 | 3-6 tokens | ChatML 格式 |
|
||||
| 工具定义 | 100-500 tokens/工具 | 取决于 schema 复杂度 |
|
||||
| 工具返回值 | 800-1,500 tokens/次 | 平均增量,长文档可达数万 |
|
||||
| 单次工具调用增量 | 1K-5K tokens | 包含调用描述和返回结果 |
|
||||
|
||||
**Anthropic 工程案例**:
|
||||
- DevOps 助理预载几十个工具定义后,基础上下文已达 **50K+ tokens**
|
||||
- 未优化时,工具定义和结果在提示中堆积可达 **134K tokens**
|
||||
- 一个 Slack 查询工具返回可能有几千 tokens,Jira 查询可达上万 tokens
|
||||
|
||||
**优化案例**: Red Sift 通过 LLM-aware API 设计减少了 **84% 的 token 消耗**,且无质量损失。
|
||||
|
||||
### 7.4 Token 消耗估算模板
|
||||
|
||||
**场景: 20 轮 SWE 任务**
|
||||
|
||||
```
|
||||
基础上下文:
|
||||
- System prompt: 5,000 tokens
|
||||
- 工具定义: 3,000 tokens (6 个工具 × 500)
|
||||
- 基础合计: 8,000 tokens
|
||||
|
||||
每轮增量 (平均):
|
||||
- 用户指令: 200 tokens
|
||||
- 工具调用: 500 tokens
|
||||
- 工具返回: 2,000 tokens (代码文件等)
|
||||
- 助手响应: 800 tokens
|
||||
- 每轮合计: 3,500 tokens
|
||||
|
||||
20 轮总消耗:
|
||||
- 理论最大: 8,000 + 20 × 3,500 = 78,000 tokens
|
||||
- 有缓存时实际计算: 8,000 (首次) + 20 × 3,500 × 部分 ≈ 40,000 tokens 新计算
|
||||
```
|
||||
|
||||
## 8. 成本计算实例
|
||||
|
||||
### 8.1 场景 A: 无缓存意识的 Agent
|
||||
|
||||
**假设**:
|
||||
- 每轮修改 system prompt 注入状态
|
||||
- 20 轮工具调用
|
||||
- 基础上下文 50K tokens
|
||||
|
||||
**计算**:
|
||||
```
|
||||
每轮都需要重新处理 50K tokens
|
||||
20 轮 × 50K × $3/M = $3.00 (仅 system prompt 部分)
|
||||
加上每轮增量... 总成本显著
|
||||
```
|
||||
|
||||
### 8.2 场景 B: 缓存优化的 Agent
|
||||
|
||||
**假设**:
|
||||
- System prompt 固定不变
|
||||
- 内容只追加,不修改
|
||||
- 使用 Claude 的 cache_control
|
||||
|
||||
**计算**:
|
||||
```
|
||||
首轮: 50K tokens × $3.75/M (缓存写入) = $0.1875
|
||||
后续 19 轮: 50K × $0.30/M × 19 = $0.285
|
||||
总计: $0.4725
|
||||
|
||||
节省: ($3.00 - $0.4725) / $3.00 = 84%
|
||||
```
|
||||
|
||||
### 8.3 场景 C: 使用国产模型
|
||||
|
||||
**使用 GLM-4.7, 相同任务**:
|
||||
```
|
||||
首轮: 50K × $0.60/M = $0.03
|
||||
后续 19 轮 (缓存命中): 50K × $0.11/M × 19 = $0.1045
|
||||
总计: $0.1345
|
||||
|
||||
相比 Claude 无缓存场景节省: 95.5%
|
||||
相比 Claude 有缓存场景节省: 72%
|
||||
```
|
||||
|
||||
### 8.4 多 Agent 场景成本
|
||||
|
||||
**假设**: 4 个 Agent 协作,全连接消息传递
|
||||
|
||||
```
|
||||
单 Agent 基准: 100K tokens
|
||||
多 Agent (77x 系数): 7.7M tokens
|
||||
|
||||
Claude Sonnet 4.5:
|
||||
- 单 Agent: 100K × $3/M = $0.30
|
||||
- 多 Agent: 7.7M × $3/M = $23.10
|
||||
|
||||
使用环形拓扑优化后:
|
||||
- 预估减少 60-80%: ~$5-9
|
||||
```
|
||||
|
||||
## 9. 最佳实践
|
||||
|
||||
### 9.1 缓存友好的 Agent 设计
|
||||
|
||||
```python
|
||||
# 正确: 只追加,不修改
|
||||
messages.append({"role": "user", "content": results})
|
||||
|
||||
# 错误: 编辑历史
|
||||
messages[2]["content"] = "updated" # 缓存失效!
|
||||
|
||||
# 错误: 修改 system prompt
|
||||
system_prompt = f"Current state: {state}" # 每次都变,缓存失效!
|
||||
|
||||
# 正确: 状态作为新消息追加
|
||||
messages.append({"role": "user", "content": f"Current state: {state}"})
|
||||
```
|
||||
|
||||
### 9.2 Claude 缓存配置
|
||||
|
||||
```python
|
||||
response = client.messages.create(
|
||||
model="claude-sonnet-4-5-20250514",
|
||||
system=[
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Your long system prompt here...",
|
||||
"cache_control": {"type": "ephemeral"} # 关键!
|
||||
}
|
||||
],
|
||||
messages=messages,
|
||||
tools=tools
|
||||
)
|
||||
```
|
||||
|
||||
### 9.3 上下文管理策略选择
|
||||
|
||||
| 场景 | 推荐策略 | 原因 |
|
||||
|------|----------|------|
|
||||
| 短对话 (<10 轮) | 只追加 | 简单,缓存友好 |
|
||||
| 中等对话 (10-50 轮) | 只追加 + 子 Agent 隔离 | 保持主上下文干净 |
|
||||
| 长对话 (>50 轮) | 分段 + 摘要 (接受缓存损失) | 避免超出上下文窗口 |
|
||||
| 多 Agent | 环形拓扑 + 选择性共享 | 平衡性能和成本 |
|
||||
|
||||
### 9.4 模型选择建议
|
||||
|
||||
| 需求 | 推荐模型 | 原因 |
|
||||
|------|----------|------|
|
||||
| 最高质量 | Claude Opus 4.5 | 最强推理能力 |
|
||||
| 性价比 (海外) | GPT-5.2 | 自动缓存,$1.75/M 输入 |
|
||||
| 性价比 (国产) | GLM-4.7 / Kimi K2 | 自动缓存,~$0.60/M 输入 |
|
||||
| 最低成本 | MiniMax M2.1 | $0.03/M 缓存读取 |
|
||||
| 超长上下文 | Gemini 3 Pro (2M) | 最大上下文窗口 |
|
||||
| 编程专精 | GLM-4.7 / MiniMax M2.1 | 开源可自部署 |
|
||||
|
||||
## 10. 总结
|
||||
|
||||
1. **缓存不是万能的,但值得理解**: 合理利用缓存可节省 50-90% 成本
|
||||
|
||||
2. **Claude 需要显式配置**: 使用 `cache_control` 参数,否则不会有缓存
|
||||
|
||||
3. **国产模型自动缓存**: Kimi K2 和 GLM-4.7 自动检测重复前缀,无需配置
|
||||
|
||||
4. **只追加是关键**: 把上下文当作只追加日志,不要编辑历史
|
||||
|
||||
5. **多 Agent 代价适中**: 相对单Agent约**3-4倍**token消耗,但性能提升90%+,适合高价值任务
|
||||
|
||||
6. **先让它工作,再优化**: 过早优化可能得不偿失
|
||||
|
||||
## 11. 数据来源与免责声明
|
||||
|
||||
**本文数据来源**:
|
||||
1. **官方文档** (A级): Anthropic、OpenAI、Google官方定价和机制文档
|
||||
2. **官方研究** (A级): Anthropic Engineering Blog的多Agent研究
|
||||
3. **第三方报道** (B级): 国产模型价格和特性(已标注来源)
|
||||
4. **实验估算** (C级): 50轮SWE任务等场景(已标注假设条件)
|
||||
|
||||
**重要提醒**:
|
||||
- 所有价格和机制以**各家官方文档为准**,本文仅供参考
|
||||
- 计算示例基于特定假设(如连续操作在TTL内、固定每轮增量等)
|
||||
- 实际成本受任务复杂度、缓存命中率、模型选择等多因素影响
|
||||
- 建议在实际应用中监控`usage`字段验证缓存效果
|
||||
|
||||
**数据更新日期**: 2025年12月28日
|
||||
|
||||
本文持续更新中,欢迎指正和补充。
|
||||
|
||||
---
|
||||
|
||||
## 参考资料
|
||||
|
||||
**官方文档**:
|
||||
- [Claude Prompt Caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)
|
||||
- [Anthropic Pricing](https://www.anthropic.com/pricing)
|
||||
- [OpenAI Prompt Caching](https://platform.openai.com/docs/guides/prompt-caching)
|
||||
- [OpenAI Pricing](https://openai.com/api/pricing/)
|
||||
- [Gemini Context Caching](https://ai.google.dev/gemini-api/docs/caching)
|
||||
- [Gemini Pricing](https://ai.google.dev/gemini-api/docs/pricing)
|
||||
- [Moonshot AI Pricing](https://platform.moonshot.ai/docs/pricing/chat)
|
||||
- [Moonshot Context Caching](https://platform.moonshot.cn/blog/posts/context-caching)
|
||||
- [智谱 AI 定价](https://open.bigmodel.cn/pricing)
|
||||
- [Z.AI Context Caching](https://docs.z.ai/guides/capabilities/cache)
|
||||
- [MiniMax Prompt Caching](https://platform.minimax.io/docs/api-reference/text-prompt-caching)
|
||||
|
||||
**研究论文**:
|
||||
- [SWE-agent: Agent-Computer Interfaces (NeurIPS 2024)](https://proceedings.neurips.cc/paper_files/paper/2024/file/5a7c947568c1b1328ccc5230172e1e7c-Paper-Conference.pdf)
|
||||
- [CodeAgents: Token-Efficient Framework](https://arxiv.org/html/2507.03254v1)
|
||||
- [Rethinking Multi-Agent Through Small-World Networks](https://arxiv.org/html/2512.18094v1)
|
||||
- [Stop Wasting Your Tokens: Towards Efficient Runtime Multi-Agent Systems](https://arxiv.org/html/2510.26585v1)
|
||||
|
||||
**工程实践**:
|
||||
- [Anthropic: Effective Context Engineering for AI Agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)
|
||||
- [Anthropic: Advanced Tool Use](https://www.anthropic.com/engineering/advanced-tool-use)
|
||||
- [Building Agents with Claude Agent SDK](https://www.anthropic.com/engineering/building-agents-with-the-claude-agent-sdk)
|
||||
- [AWS: Claude Code + Bedrock Prompt Caching](https://aws.amazon.com/blogs/machine-learning/supercharge-your-development-with-claude-code-and-amazon-bedrock-prompt-caching/)
|
||||
- [Microsoft Azure SRE Agent Context Engineering](https://techcommunity.microsoft.com/blog/appsonazureblog/context-engineering-lessons-from-building-azure-sre-agent/4481200/)
|
||||
- [Google: Architecting Efficient Context-Aware Multi-Agent Framework](https://developers.googleblog.com/architecting-efficient-context-aware-multi-agent-framework-for-production/)
|
||||
|
||||
**数据来源说明**: 本文价格和机制数据来自多个 AI 的网络调研结果交叉验证,部分数据可能随时间变化,请以各家官方文档为准。
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,498 +0,0 @@
|
||||
|
||||
// @from(Start 10193957, End 10194327)
|
||||
function rq5(A) {
|
||||
let B = {
|
||||
exitOnCtrlC: A,
|
||||
onFlicker() {
|
||||
E1("tengu_flicker", {})
|
||||
}
|
||||
};
|
||||
if (!process.stdin.isTTY && !0 && !process.argv.includes("mcp")) {
|
||||
if (process.platform !== "win32") try {
|
||||
let Q = dq5("/dev/tty", "r");
|
||||
B = {
|
||||
...B,
|
||||
stdin: new mq5(Q)
|
||||
}
|
||||
} catch (Q) {
|
||||
b1(Q)
|
||||
}
|
||||
}
|
||||
return B
|
||||
}
|
||||
// @from(Start 10194328, End 10194592)
|
||||
async function oq5(A, B) {
|
||||
if (!process.stdin.isTTY && !process.argv.includes("mcp")) {
|
||||
if (B === "stream-json") return process.stdin;
|
||||
let Q = "";
|
||||
for await (let I of process.stdin) Q += I;
|
||||
return [A, Q].filter(Boolean).join(`
|
||||
`)
|
||||
}
|
||||
return A
|
||||
}
|
||||
// @from(Start 10194593, End 10217244)
|
||||
async function tq5() {
|
||||
aq5();
|
||||
let A = new Ty2;
|
||||
A.name("claude").description(`${m0} - starts an interactive session by default, use -p/--print for non-interactive output`).argument("[prompt]", "Your prompt", String).helpOption("-h, --help", "Display help for command").option("-d, --debug", "Enable debug mode", () => !0).option("--verbose", "Override verbose mode setting from config", () => !0).option("-p, --print", "Print response and exit (useful for pipes)", () => !0).addOption(new UT("--output-format <format>", 'Output format (only works with --print): "text" (default), "json" (single result), or "stream-json" (realtime streaming)').choices(["text", "json", "stream-json"])).addOption(new UT("--input-format <format>", 'Input format (only works with --print): "text" (default), or "stream-json" (realtime streaming input)').choices(["text", "stream-json"])).option("--mcp-debug", "[DEPRECATED. Use --debug instead] Enable MCP debug mode (shows MCP server errors)", () => !0).option("--dangerously-skip-permissions", "Bypass all permission checks. Recommended only for sandboxes with no internet access.", () => !0).addOption(new UT("--max-turns <turns>", "Maximum number of agentic turns in non-interactive mode. This will early exit the conversation after the specified number of turns. (only works with --print)").argParser(Number).hideHelp()).option("--allowedTools <tools...>", 'Comma or space-separated list of tool names to allow (e.g. "Bash(git:*) Edit")').option("--disallowedTools <tools...>", 'Comma or space-separated list of tool names to deny (e.g. "Bash(git:*) Edit")').option("--mcp-config <file or string>", "Load MCP servers from a JSON file or string").addOption(new UT("--permission-prompt-tool <tool>", "MCP tool to use for permission prompts (only works with --print)").argParser(String).hideHelp()).addOption(new UT("--system-prompt <prompt>", "System prompt to use for the session (only works with --print)").argParser(String).hideHelp()).addOption(new UT("--append-system-prompt <prompt>", "Append a system prompt to the default system prompt (only works with --print)").argParser(String).hideHelp()).addOption(new UT("--permission-mode <mode>", "Permission mode to use for the session").argParser(String).hideHelp().choices(S81)).option("-c, --continue", "Continue the most recent conversation", () => !0).option("-r, --resume [sessionId]", "Resume a conversation - provide a session ID or interactively select a conversation to resume", (I) => I || !0).option("--model <model>", "Model for the current session. Provide an alias for the latest model (e.g. 'sonnet' or 'opus') or a model's full name (e.g. 'claude-sonnet-4-20250514').").option("--fallback-model <model>", "Enable automatic fallback to specified model when default model is overloaded (only works with --print)").option("--add-dir <directories...>", "Additional directories to allow tool access to").action(async (I, G) => {
|
||||
let {
|
||||
debug: Z = !1,
|
||||
verbose: D = !1,
|
||||
print: Y,
|
||||
dangerouslySkipPermissions: W,
|
||||
allowedTools: J = [],
|
||||
disallowedTools: F = [],
|
||||
mcpConfig: X,
|
||||
outputFormat: V,
|
||||
inputFormat: C,
|
||||
permissionMode: K,
|
||||
addDir: E = [],
|
||||
fallbackModel: N
|
||||
} = G, q = !1, O = null, R = Y ?? !process.stdout.isTTY;
|
||||
if (C9A(R), N && G.model && N === G.model) process.stderr.write(UA.red(`Error: Fallback model cannot be the same as the main model. Please specify a different model for --fallback-model.
|
||||
`)), process.exit(1);
|
||||
let T = s_2({
|
||||
permissionModeCli: K,
|
||||
dangerouslySkipPermissions: W
|
||||
}),
|
||||
L = void 0;
|
||||
if (X) try {
|
||||
let YA, bA = Z8(X);
|
||||
if (bA) {
|
||||
let e1 = Ug.safeParse(bA);
|
||||
if (!e1.success) {
|
||||
let k1 = e1.error.errors.map((Q1) => `${Q1.path.join(".")}: ${Q1.message}`).join(", ");
|
||||
throw new Error(`Invalid MCP configuration: ${k1}`)
|
||||
}
|
||||
YA = e1.data.mcpServers
|
||||
} else {
|
||||
let e1 = pq5(X);
|
||||
YA = wo1(e1).mcpServers
|
||||
}
|
||||
L = UU(YA, (e1) => ({
|
||||
...e1,
|
||||
scope: "dynamic"
|
||||
}))
|
||||
} catch (YA) {
|
||||
console.error(`Error: ${YA instanceof Error?YA.message:String(YA)}`), process.exit(1)
|
||||
}
|
||||
if (!R) {
|
||||
let YA = await lq5(T);
|
||||
if (YA && I?.trim().toLowerCase() === "/login") I = "";
|
||||
if (!YA) zH1()
|
||||
}
|
||||
let {
|
||||
toolPermissionContext: _,
|
||||
warnings: k
|
||||
} = r_2({
|
||||
allowedToolsCli: J,
|
||||
disallowedToolsCli: F,
|
||||
permissionMode: T,
|
||||
addDirs: E
|
||||
});
|
||||
if (k.forEach((YA) => {
|
||||
console.error(YA)
|
||||
}), AS2(), AK1(L), C && C !== "text" && C !== "stream-json") console.error(`Error: Invalid input format "${C}".`), process.exit(1);
|
||||
if (C === "stream-json" && V !== "stream-json") console.error("Error: --input-format=stream-json requires output-format=stream-json."), process.exit(1);
|
||||
let i = await oq5(I || "", C ?? "text"),
|
||||
x = CT(_, ZA().todoFeatureEnabled);
|
||||
await qT($T(), T, Y ?? !1, !1);
|
||||
let [s, {
|
||||
clients: d = [],
|
||||
tools: F1 = [],
|
||||
commands: X1 = []
|
||||
}] = await Promise.all([J2A(), i || R ? await AK1(L) : {
|
||||
clients: [],
|
||||
tools: [],
|
||||
commands: []
|
||||
}]);
|
||||
if (E1("tengu_init", {
|
||||
entrypoint: "claude",
|
||||
hasInitialPrompt: Boolean(I),
|
||||
hasStdin: Boolean(i),
|
||||
verbose: D,
|
||||
debug: Z,
|
||||
print: Y,
|
||||
outputFormat: V,
|
||||
numAllowedTools: J.length,
|
||||
numDisallowedTools: F.length,
|
||||
mcpClientCount: Object.keys(DV()).length,
|
||||
worktree: !1
|
||||
}), uT2(), R) {
|
||||
Yk2(i, _, d, s, X1, x, F1, {
|
||||
continue: G.continue,
|
||||
resume: G.resume,
|
||||
verbose: G.verbose,
|
||||
outputFormat: G.outputFormat,
|
||||
permissionPromptToolName: G.permissionPromptTool,
|
||||
allowedTools: J,
|
||||
maxTurns: G.maxTurns,
|
||||
systemPrompt: G.systemPrompt,
|
||||
appendSystemPrompt: G.appendSystemPrompt,
|
||||
userSpecifiedModel: G.model,
|
||||
fallbackModel: N
|
||||
});
|
||||
return
|
||||
}
|
||||
let [v, D1] = await Promise.all([rq5(!1), By2(ry2)]);
|
||||
E1("tengu_startup_manual_model_config", {
|
||||
cli_flag: G.model,
|
||||
env_var: process.env.ANTHROPIC_MODEL,
|
||||
settings_file: m6().model
|
||||
});
|
||||
let N1 = G.model || process.env.ANTHROPIC_MODEL || m6().model;
|
||||
if (T9() && !qZ() && N1 !== void 0 && N1.includes("opus")) console.error(UA.yellow("Claude Pro users are not currently able to use Opus 4 in Claude Code. The current model is now Sonnet 4."));
|
||||
let u1 = G.model;
|
||||
Xc(u1), Q9A(Vg() || null);
|
||||
let d1 = {
|
||||
verbose: D ?? !1,
|
||||
mainLoopModel: C21(),
|
||||
todoFeatureEnabled: ZA().todoFeatureEnabled,
|
||||
toolPermissionContext: _,
|
||||
maxRateLimitFallbackActive: !1,
|
||||
mcp: {
|
||||
clients: [],
|
||||
tools: [],
|
||||
commands: [],
|
||||
resources: {}
|
||||
}
|
||||
};
|
||||
if (AE1(_), iq5(), G.continue) try {
|
||||
E1("tengu_continue", {});
|
||||
let YA = await ET(void 0, F1);
|
||||
if (!YA) console.error("No conversation found to continue"), process.exit(1);
|
||||
let bA = jJ(y9());
|
||||
n5(HB.default.createElement(c3, {
|
||||
initialState: d1,
|
||||
onChangeAppState: NT
|
||||
}, HB.default.createElement(_p, {
|
||||
debug: Z,
|
||||
initialPrompt: "",
|
||||
shouldShowPromptInput: !0,
|
||||
commands: [...s, ...X1],
|
||||
initialTools: F1,
|
||||
initialMessages: YA.messages,
|
||||
initialTodos: bA,
|
||||
mcpClients: d,
|
||||
dynamicMcpConfig: L
|
||||
})), v)
|
||||
} catch (YA) {
|
||||
b1(YA instanceof Error ? YA : new Error(String(YA))), process.exit(1)
|
||||
} else if (G.resume) {
|
||||
let YA = null,
|
||||
bA = fC(G.resume);
|
||||
if (!1) {
|
||||
if (G.resume && typeof G.resume === "string" && !bA) try {} catch (e1) {}
|
||||
}
|
||||
if (bA) {
|
||||
let e1 = bA;
|
||||
try {
|
||||
let k1 = await ET(e1, F1);
|
||||
if (!k1) console.error(`No conversation found with session ID: ${e1}`), process.exit(1);
|
||||
YA = k1.messages
|
||||
} catch (k1) {
|
||||
b1(k1 instanceof Error ? k1 : new Error(String(k1))), console.error(`Failed to resume session ${e1}`), process.exit(1)
|
||||
}
|
||||
}
|
||||
if (Array.isArray(YA)) n5(HB.default.createElement(c3, {
|
||||
initialState: d1,
|
||||
onChangeAppState: NT
|
||||
}, HB.default.createElement(_p, {
|
||||
debug: Z,
|
||||
initialPrompt: i,
|
||||
shouldShowPromptInput: !0,
|
||||
commands: [...s, ...X1],
|
||||
initialTools: F1,
|
||||
initialMessages: YA,
|
||||
mcpClients: d,
|
||||
dynamicMcpConfig: L
|
||||
})), v);
|
||||
else {
|
||||
let e1 = {},
|
||||
k1 = await Hg();
|
||||
if (!k1.length) console.error("No conversations found to resume"), process.exit(1);
|
||||
let {
|
||||
unmount: Q1
|
||||
} = n5(HB.default.createElement(fy2, {
|
||||
commands: [...s, ...X1],
|
||||
context: e1,
|
||||
debug: Z,
|
||||
logs: k1,
|
||||
initialTools: F1,
|
||||
mcpClients: d,
|
||||
dynamicMcpConfig: L,
|
||||
appState: d1,
|
||||
onChangeAppState: NT
|
||||
}), v);
|
||||
e1.unmount = Q1
|
||||
}
|
||||
} else {
|
||||
let YA = jJ(y9());
|
||||
n5(HB.default.createElement(c3, {
|
||||
initialState: d1,
|
||||
onChangeAppState: NT
|
||||
}, HB.default.createElement(_p, {
|
||||
debug: Z,
|
||||
commands: [...s, ...X1],
|
||||
initialPrompt: i,
|
||||
shouldShowPromptInput: !0,
|
||||
initialTools: F1,
|
||||
initialTodos: YA,
|
||||
tipOfTheDay: D1,
|
||||
mcpClients: d,
|
||||
dynamicMcpConfig: L
|
||||
})), v)
|
||||
}
|
||||
}).version(`${{ISSUES_EXPLAINER:"report the issue at https://github.com/anthropics/claude-code/issues",PACKAGE_URL:"@anthropic-ai/claude-code",README_URL:"https://docs.anthropic.com/s/claude-code",VERSION:"1.0.34"}.VERSION} (${m0})`, "-v, --version", "Output the version number");
|
||||
let B = A.command("config").description("Manage configuration (eg. claude config set -g theme dark)").helpOption("-h, --help", "Display help for command");
|
||||
B.command("get <key>").description("Get a config value").option("-g, --global", "Use global config").helpOption("-h, --help", "Display help for command").action(async (I, {
|
||||
global: G
|
||||
}) => {
|
||||
await qT($T(), "default", !1, !1), console.log(WD0(I, G ?? !1)), process.exit(0)
|
||||
}), B.command("set <key> <value>").description("Set a config value").option("-g, --global", "Use global config").helpOption("-h, --help", "Display help for command").action(async (I, G, {
|
||||
global: Z
|
||||
}) => {
|
||||
await qT($T(), "default", !1, !1), JD0(I, G, Z ?? !1), console.log(`Set ${I} to ${G}`), process.exit(0)
|
||||
}), B.command("remove <key> [values...]").alias("rm").description("Remove a config value or items from a config array").option("-g, --global", "Use global config").helpOption("-h, --help", "Display help for command").action(async (I, G, {
|
||||
global: Z
|
||||
}) => {
|
||||
if (await qT($T(), "default", !1, !1), Ng(I, Z ?? !1) && G && G.length > 0) {
|
||||
let D = G.flatMap((Y) => Y.includes(",") ? Y.split(",") : Y).map((Y) => Y.trim()).filter((Y) => Y.length > 0);
|
||||
if (D.length === 0) console.error("Error: No valid values provided"), process.exit(1);
|
||||
ID0(I, D, Z ?? !1, !1), console.log(`Removed from ${I} in ${Z?"global":"project"} config: ${D.join(", ")}`)
|
||||
} else FD0(I, Z ?? !1), console.log(`Removed ${I}`);
|
||||
process.exit(0)
|
||||
}), B.command("list").alias("ls").description("List all config values").option("-g, --global", "Use global config", !1).helpOption("-h, --help", "Display help for command").action(async ({
|
||||
global: I
|
||||
}) => {
|
||||
await qT($T(), "default", !1, !1), console.log(JSON.stringify(XD0(I ?? !1), null, 2)), process.exit(0)
|
||||
}), B.command("add <key> <values...>").description("Add items to a config array (space or comma separated)").option("-g, --global", "Use global config").helpOption("-h, --help", "Display help for command").action(async (I, G, {
|
||||
global: Z
|
||||
}) => {
|
||||
await qT($T(), "default", !1, !1);
|
||||
let D = G.flatMap((Y) => Y.includes(",") ? Y.split(",") : Y).map((Y) => Y.trim()).filter((Y) => Y.length > 0);
|
||||
if (D.length === 0) console.error("Error: No valid values provided"), process.exit(1);
|
||||
_G1(I, D, Z ?? !1, !1), console.log(`Added to ${I} in ${Z?"global":"project"} config: ${D.join(", ")}`), process.exit(0)
|
||||
});
|
||||
let Q = A.command("mcp").description("Configure and manage MCP servers").helpOption("-h, --help", "Display help for command");
|
||||
return Q.command("serve").description(`Start the ${m0} MCP server`).helpOption("-h, --help", "Display help for command").option("-d, --debug", "Enable debug mode", () => !0).option("--verbose", "Override verbose mode setting from config", () => !0).action(async ({
|
||||
debug: I,
|
||||
verbose: G
|
||||
}) => {
|
||||
let Z = $T();
|
||||
if (E1("tengu_mcp_start", {}), !uq5(Z)) console.error(`Error: Directory ${Z} does not exist`), process.exit(1);
|
||||
try {
|
||||
await qT(Z, "default", !1, !1), await hy2(Z, I ?? !1, G ?? !1)
|
||||
} catch (D) {
|
||||
console.error("Error: Failed to start MCP server:", D), process.exit(1)
|
||||
}
|
||||
}), Q.command("add <name> <commandOrUrl> [args...]").description("Add a server").option("-s, --scope <scope>", "Configuration scope (local, user, or project)", "local").option("-t, --transport <transport>", "Transport type (stdio, sse, http)", "stdio").option("-e, --env <env...>", "Set environment variables (e.g. -e KEY=value)").option("-H, --header <header...>", 'Set HTTP headers for SSE and HTTP transports (e.g. -H "X-Api-Key: abc123" -H "X-Custom: value")').helpOption("-h, --help", "Display help for command").action(async (I, G, Z, D) => {
|
||||
if (!I) console.error("Error: Server name is required."), console.error("Usage: claude mcp add <name> <command> [args...]"), process.exit(1);
|
||||
else if (!G) console.error("Error: Command is required when server name is provided."), console.error("Usage: claude mcp add <name> <command> [args...]"), process.exit(1);
|
||||
try {
|
||||
let Y = cd(D.scope),
|
||||
W = Ho1(D.transport);
|
||||
if (await E1("tengu_mcp_add", {
|
||||
type: W,
|
||||
scope: Y,
|
||||
source: "command",
|
||||
transport: W
|
||||
}), W === "sse") {
|
||||
if (!G) console.error("Error: URL is required for SSE transport."), process.exit(1);
|
||||
let J = D.header ? oC1(D.header) : void 0;
|
||||
if (LO(I, {
|
||||
type: "sse",
|
||||
url: G,
|
||||
headers: J
|
||||
}, Y), console.log(`Added SSE MCP server ${I} with URL: ${G} to ${Y} config`), J) console.log("Headers:", JSON.stringify(J, null, 2))
|
||||
} else if (W === "http") {
|
||||
if (!G) console.error("Error: URL is required for HTTP transport."), process.exit(1);
|
||||
let J = D.header ? oC1(D.header) : void 0;
|
||||
if (LO(I, {
|
||||
type: "http",
|
||||
url: G,
|
||||
headers: J
|
||||
}, Y), console.log(`Added HTTP MCP server ${I} with URL: ${G} to ${Y} config`), J) console.log("Headers:", JSON.stringify(J, null, 2))
|
||||
} else {
|
||||
let J = eZ0(D.env);
|
||||
LO(I, {
|
||||
type: "stdio",
|
||||
command: G,
|
||||
args: Z || [],
|
||||
env: J
|
||||
}, Y), console.log(`Added stdio MCP server ${I} with command: ${G} ${(Z||[]).join(" ")} to ${Y} config`)
|
||||
}
|
||||
process.exit(0)
|
||||
} catch (Y) {
|
||||
console.error(Y.message), process.exit(1)
|
||||
}
|
||||
}), Q.command("remove <name>").description("Remove an MCP server").option("-s, --scope <scope>", "Configuration scope (local, user, or project) - if not specified, removes from whichever scope it exists in").helpOption("-h, --help", "Display help for command").action(async (I, G) => {
|
||||
try {
|
||||
if (G.scope) {
|
||||
let J = cd(G.scope);
|
||||
await E1("tengu_mcp_delete", {
|
||||
name: I,
|
||||
scope: J
|
||||
}), aC1(I, J), process.stdout.write(`Removed MCP server ${I} from ${J} config
|
||||
`), process.exit(0)
|
||||
}
|
||||
let Z = m9(),
|
||||
D = ZA(),
|
||||
Y = !1;
|
||||
try {
|
||||
Y = !!vC()?.[I]
|
||||
} catch {}
|
||||
let W = [];
|
||||
if (Z.mcpServers?.[I]) W.push("local");
|
||||
if (Y) W.push("project");
|
||||
if (D.mcpServers?.[I]) W.push("user");
|
||||
if (W.length === 0) process.stderr.write(`No MCP server found with name: "${I}"
|
||||
`), process.exit(1);
|
||||
else if (W.length === 1) {
|
||||
let J = W[0];
|
||||
await E1("tengu_mcp_delete", {
|
||||
name: I,
|
||||
scope: J
|
||||
}), aC1(I, J), process.stdout.write(`Removed MCP server "${I}" from ${J} config
|
||||
`), process.exit(0)
|
||||
} else process.stderr.write(`MCP server "${I}" exists in multiple scopes:
|
||||
`), W.forEach((J) => {
|
||||
process.stderr.write(` - ${nC1(J)}
|
||||
`)
|
||||
}), process.stderr.write(`
|
||||
To remove from a specific scope, use:
|
||||
`), W.forEach((J) => {
|
||||
process.stderr.write(` claude mcp remove "${I}" -s ${J}
|
||||
`)
|
||||
}), process.exit(1)
|
||||
} catch (Z) {
|
||||
process.stderr.write(`${Z.message}
|
||||
`), process.exit(1)
|
||||
}
|
||||
}), Q.command("list").description("List configured MCP servers").helpOption("-h, --help", "Display help for command").action(async () => {
|
||||
await E1("tengu_mcp_list", {});
|
||||
let I = DV();
|
||||
if (Object.keys(I).length === 0) console.log("No MCP servers configured. Use `claude mcp add` to add a server.");
|
||||
else
|
||||
for (let [G, Z] of Object.entries(I))
|
||||
if (Z.type === "sse") console.log(`${G}: ${Z.url} (SSE)`);
|
||||
else if (Z.type === "http") console.log(`${G}: ${Z.url} (HTTP)`);
|
||||
else if (!Z.type || Z.type === "stdio") {
|
||||
let D = Array.isArray(Z.args) ? Z.args : [];
|
||||
console.log(`${G}: ${Z.command} ${D.join(" ")}`)
|
||||
}
|
||||
process.exit(0)
|
||||
}), Q.command("get <name>").description("Get details about an MCP server").helpOption("-h, --help", "Display help for command").action(async (I) => {
|
||||
await E1("tengu_mcp_get", {
|
||||
name: I
|
||||
});
|
||||
let G = sC1(I);
|
||||
if (!G) console.error(`No MCP server found with name: ${I}`), process.exit(1);
|
||||
if (console.log(`${I}:`), console.log(` Scope: ${nC1(G.scope)}`), G.type === "sse") {
|
||||
if (console.log(" Type: sse"), console.log(` URL: ${G.url}`), G.headers) {
|
||||
console.log(" Headers:");
|
||||
for (let [Z, D] of Object.entries(G.headers)) console.log(` ${Z}: ${D}`)
|
||||
}
|
||||
} else if (G.type === "http") {
|
||||
if (console.log(" Type: http"), console.log(` URL: ${G.url}`), G.headers) {
|
||||
console.log(" Headers:");
|
||||
for (let [Z, D] of Object.entries(G.headers)) console.log(` ${Z}: ${D}`)
|
||||
}
|
||||
} else if (G.type === "stdio") {
|
||||
console.log(" Type: stdio"), console.log(` Command: ${G.command}`);
|
||||
let Z = Array.isArray(G.args) ? G.args : [];
|
||||
if (console.log(` Args: ${Z.join(" ")}`), G.env) {
|
||||
console.log(" Environment:");
|
||||
for (let [D, Y] of Object.entries(G.env)) console.log(` ${D}=${Y}`)
|
||||
}
|
||||
}
|
||||
console.log(`
|
||||
To remove this server, run: claude mcp remove "${I}" -s ${G.scope}`), process.exit(0)
|
||||
}), Q.command("add-json <name> <json>").description("Add an MCP server (stdio or SSE) with a JSON string").option("-s, --scope <scope>", "Configuration scope (local, user, or project)", "local").helpOption("-h, --help", "Display help for command").action(async (I, G, Z) => {
|
||||
try {
|
||||
let D = cd(Z.scope),
|
||||
Y = Z8(G),
|
||||
W = Y && typeof Y === "object" && "type" in Y ? String(Y.type || "stdio") : "stdio";
|
||||
await E1("tengu_mcp_add", {
|
||||
scope: D,
|
||||
source: "json",
|
||||
type: W
|
||||
}), zo1(I, G, D), console.log(`Added ${W} MCP server ${I} to ${D} config`), process.exit(0)
|
||||
} catch (D) {
|
||||
console.error(D.message), process.exit(1)
|
||||
}
|
||||
}), Q.command("add-from-claude-desktop").description("Import MCP servers from Claude Desktop (Mac and WSL only)").option("-s, --scope <scope>", "Configuration scope (local, user, or project)", "local").helpOption("-h, --help", "Display help for command").action(async (I) => {
|
||||
try {
|
||||
let G = cd(I.scope),
|
||||
Z = Z7();
|
||||
E1("tengu_mcp_add", {
|
||||
scope: G,
|
||||
platform: Z,
|
||||
source: "desktop"
|
||||
});
|
||||
let D = _y2();
|
||||
if (Object.keys(D).length === 0) console.log("No MCP servers found in Claude Desktop configuration or configuration file does not exist."), process.exit(0);
|
||||
let {
|
||||
unmount: Y
|
||||
} = n5(HB.default.createElement(c3, null, HB.default.createElement(Py2, {
|
||||
servers: D,
|
||||
scope: G,
|
||||
onDone: () => {
|
||||
Y()
|
||||
}
|
||||
})), {
|
||||
exitOnCtrlC: !0
|
||||
})
|
||||
} catch (G) {
|
||||
console.error(G.message), process.exit(1)
|
||||
}
|
||||
}), Q.command("reset-project-choices").description("Reset all approved and rejected project-scoped (.mcp.json) servers within this project").helpOption("-h, --help", "Display help for command").action(async () => {
|
||||
await E1("tengu_mcp_reset_mcpjson_choices", {});
|
||||
let I = m9();
|
||||
B5({
|
||||
...I,
|
||||
enabledMcpjsonServers: [],
|
||||
disabledMcpjsonServers: [],
|
||||
enableAllProjectMcpServers: !1
|
||||
}), console.log("All project-scoped (.mcp.json) server approvals and rejections have been reset."), console.log("You will be prompted for approval next time you start Claude Code."), process.exit(0)
|
||||
}), A.command("migrate-installer").description("Migrate from global npm installation to local installation").helpOption("-h, --help", "Display help for command").action(async () => {
|
||||
if (JT()) console.log("Already running from local installation. No migration needed."), process.exit(0);
|
||||
E1("tengu_migrate_installer_command", {}), await new Promise((I) => {
|
||||
let {
|
||||
waitUntilExit: G
|
||||
} = n5(HB.default.createElement(c3, null, HB.default.createElement(Hp, null)));
|
||||
G().then(() => {
|
||||
I()
|
||||
})
|
||||
}), process.exit(0)
|
||||
}), A.command("doctor").description("Check the health of your Claude Code auto-updater").helpOption("-h, --help", "Display help for command").action(async () => {
|
||||
E1("tengu_doctor_command", {}), await new Promise((I) => {
|
||||
let {
|
||||
unmount: G
|
||||
} = n5(HB.default.createElement(c3, null, HB.default.createElement($w1, {
|
||||
onDone: () => {
|
||||
G(), I()
|
||||
}
|
||||
})), {
|
||||
exitOnCtrlC: !1
|
||||
})
|
||||
}), process.exit(0)
|
||||
}), A.command("update").description("Check for updates and install if available").helpOption("-h, --help", "Display help for command").action(Wk2), A.command("install").description("Install Claude Code native build").option("--force", "Force installation even if already installed").helpOption("-h, --help", "Display help for command").action(async (I) => {
|
||||
await qT($T(), "default", !1, !1), await new Promise((G) => {
|
||||
let Z = [];
|
||||
if (I.force) Z.push("--force");
|
||||
Jk2.call(() => {
|
||||
G(), process.exit(0)
|
||||
}, {}, Z)
|
||||
})
|
||||
}), await A.parseAsync(process.argv), A
|
||||
}
|
||||
// @from(Start 10217246, End 10217381)
|
||||
function eq5() {
|
||||
(process.stderr.isTTY ? process.stderr : process.stdout.isTTY ? process.stdout : void 0)?.write(`\x1B[?25h${OP1}`)
|
||||
}
|
||||
// @from(Start 10217389, End 10217466)
|
||||
export {
|
||||
lq5 as showSetupScreens, qT as setup, cq5 as completeOnboarding
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,442 +0,0 @@
|
||||
|
||||
// @from(Start 2305579, End 2313240)
|
||||
oT1 = z((jA8, CfA) => {
|
||||
var FA = TTA();
|
||||
FA.registerLanguage("1c", STA());
|
||||
FA.registerLanguage("abnf", jTA());
|
||||
FA.registerLanguage("accesslog", xTA());
|
||||
FA.registerLanguage("actionscript", vTA());
|
||||
FA.registerLanguage("ada", gTA());
|
||||
FA.registerLanguage("angelscript", mTA());
|
||||
FA.registerLanguage("apache", uTA());
|
||||
FA.registerLanguage("applescript", nTA());
|
||||
FA.registerLanguage("arcade", sTA());
|
||||
FA.registerLanguage("arduino", oTA());
|
||||
FA.registerLanguage("armasm", eTA());
|
||||
FA.registerLanguage("xml", IPA());
|
||||
FA.registerLanguage("asciidoc", DPA());
|
||||
FA.registerLanguage("aspectj", WPA());
|
||||
FA.registerLanguage("autohotkey", FPA());
|
||||
FA.registerLanguage("autoit", VPA());
|
||||
FA.registerLanguage("avrasm", KPA());
|
||||
FA.registerLanguage("awk", zPA());
|
||||
FA.registerLanguage("axapta", EPA());
|
||||
FA.registerLanguage("bash", NPA());
|
||||
FA.registerLanguage("basic", qPA());
|
||||
FA.registerLanguage("bnf", LPA());
|
||||
FA.registerLanguage("brainfuck", OPA());
|
||||
FA.registerLanguage("c-like", PPA());
|
||||
FA.registerLanguage("c", _PA());
|
||||
FA.registerLanguage("cal", yPA());
|
||||
FA.registerLanguage("capnproto", xPA());
|
||||
FA.registerLanguage("ceylon", vPA());
|
||||
FA.registerLanguage("clean", gPA());
|
||||
FA.registerLanguage("clojure", mPA());
|
||||
FA.registerLanguage("clojure-repl", uPA());
|
||||
FA.registerLanguage("cmake", cPA());
|
||||
FA.registerLanguage("coffeescript", iPA());
|
||||
FA.registerLanguage("coq", aPA());
|
||||
FA.registerLanguage("cos", rPA());
|
||||
FA.registerLanguage("cpp", tPA());
|
||||
FA.registerLanguage("crmsh", ASA());
|
||||
FA.registerLanguage("crystal", QSA());
|
||||
FA.registerLanguage("csharp", GSA());
|
||||
FA.registerLanguage("csp", DSA());
|
||||
FA.registerLanguage("css", WSA());
|
||||
FA.registerLanguage("d", FSA());
|
||||
FA.registerLanguage("markdown", VSA());
|
||||
FA.registerLanguage("dart", KSA());
|
||||
FA.registerLanguage("delphi", zSA());
|
||||
FA.registerLanguage("diff", ESA());
|
||||
FA.registerLanguage("django", NSA());
|
||||
FA.registerLanguage("dns", qSA());
|
||||
FA.registerLanguage("dockerfile", LSA());
|
||||
FA.registerLanguage("dos", OSA());
|
||||
FA.registerLanguage("dsconfig", PSA());
|
||||
FA.registerLanguage("dts", _SA());
|
||||
FA.registerLanguage("dust", ySA());
|
||||
FA.registerLanguage("ebnf", xSA());
|
||||
FA.registerLanguage("elixir", vSA());
|
||||
FA.registerLanguage("elm", gSA());
|
||||
FA.registerLanguage("ruby", dSA());
|
||||
FA.registerLanguage("erb", pSA());
|
||||
FA.registerLanguage("erlang-repl", lSA());
|
||||
FA.registerLanguage("erlang", nSA());
|
||||
FA.registerLanguage("excel", sSA());
|
||||
FA.registerLanguage("fix", oSA());
|
||||
FA.registerLanguage("flix", eSA());
|
||||
FA.registerLanguage("fortran", B_A());
|
||||
FA.registerLanguage("fsharp", I_A());
|
||||
FA.registerLanguage("gams", Z_A());
|
||||
FA.registerLanguage("gauss", Y_A());
|
||||
FA.registerLanguage("gcode", J_A());
|
||||
FA.registerLanguage("gherkin", X_A());
|
||||
FA.registerLanguage("glsl", C_A());
|
||||
FA.registerLanguage("gml", H_A());
|
||||
FA.registerLanguage("go", w_A());
|
||||
FA.registerLanguage("golo", U_A());
|
||||
FA.registerLanguage("gradle", $_A());
|
||||
FA.registerLanguage("groovy", M_A());
|
||||
FA.registerLanguage("haml", R_A());
|
||||
FA.registerLanguage("handlebars", P_A());
|
||||
FA.registerLanguage("haskell", __A());
|
||||
FA.registerLanguage("haxe", y_A());
|
||||
FA.registerLanguage("hsp", x_A());
|
||||
FA.registerLanguage("htmlbars", b_A());
|
||||
FA.registerLanguage("http", h_A());
|
||||
FA.registerLanguage("hy", d_A());
|
||||
FA.registerLanguage("inform7", p_A());
|
||||
FA.registerLanguage("ini", n_A());
|
||||
FA.registerLanguage("irpf90", s_A());
|
||||
FA.registerLanguage("isbl", o_A());
|
||||
FA.registerLanguage("java", e_A());
|
||||
FA.registerLanguage("javascript", QjA());
|
||||
FA.registerLanguage("jboss-cli", GjA());
|
||||
FA.registerLanguage("json", DjA());
|
||||
FA.registerLanguage("julia", WjA());
|
||||
FA.registerLanguage("julia-repl", FjA());
|
||||
FA.registerLanguage("kotlin", VjA());
|
||||
FA.registerLanguage("lasso", KjA());
|
||||
FA.registerLanguage("latex", zjA());
|
||||
FA.registerLanguage("ldif", EjA());
|
||||
FA.registerLanguage("leaf", NjA());
|
||||
FA.registerLanguage("less", LjA());
|
||||
FA.registerLanguage("lisp", OjA());
|
||||
FA.registerLanguage("livecodeserver", PjA());
|
||||
FA.registerLanguage("livescript", _jA());
|
||||
FA.registerLanguage("llvm", yjA());
|
||||
FA.registerLanguage("lsl", xjA());
|
||||
FA.registerLanguage("lua", vjA());
|
||||
FA.registerLanguage("makefile", gjA());
|
||||
FA.registerLanguage("mathematica", pjA());
|
||||
FA.registerLanguage("matlab", ljA());
|
||||
FA.registerLanguage("maxima", njA());
|
||||
FA.registerLanguage("mel", sjA());
|
||||
FA.registerLanguage("mercury", ojA());
|
||||
FA.registerLanguage("mipsasm", ejA());
|
||||
FA.registerLanguage("mizar", ByA());
|
||||
FA.registerLanguage("perl", ZyA());
|
||||
FA.registerLanguage("mojolicious", YyA());
|
||||
FA.registerLanguage("monkey", JyA());
|
||||
FA.registerLanguage("moonscript", XyA());
|
||||
FA.registerLanguage("n1ql", CyA());
|
||||
FA.registerLanguage("nginx", HyA());
|
||||
FA.registerLanguage("nim", wyA());
|
||||
FA.registerLanguage("nix", UyA());
|
||||
FA.registerLanguage("node-repl", $yA());
|
||||
FA.registerLanguage("nsis", MyA());
|
||||
FA.registerLanguage("objectivec", RyA());
|
||||
FA.registerLanguage("ocaml", TyA());
|
||||
FA.registerLanguage("openscad", SyA());
|
||||
FA.registerLanguage("oxygene", jyA());
|
||||
FA.registerLanguage("parser3", kyA());
|
||||
FA.registerLanguage("pf", fyA());
|
||||
FA.registerLanguage("pgsql", byA());
|
||||
FA.registerLanguage("php", hyA());
|
||||
FA.registerLanguage("php-template", dyA());
|
||||
FA.registerLanguage("plaintext", pyA());
|
||||
FA.registerLanguage("pony", lyA());
|
||||
FA.registerLanguage("powershell", nyA());
|
||||
FA.registerLanguage("processing", syA());
|
||||
FA.registerLanguage("profile", oyA());
|
||||
FA.registerLanguage("prolog", eyA());
|
||||
FA.registerLanguage("properties", BkA());
|
||||
FA.registerLanguage("protobuf", IkA());
|
||||
FA.registerLanguage("puppet", ZkA());
|
||||
FA.registerLanguage("purebasic", YkA());
|
||||
FA.registerLanguage("python", JkA());
|
||||
FA.registerLanguage("python-repl", XkA());
|
||||
FA.registerLanguage("q", CkA());
|
||||
FA.registerLanguage("qml", HkA());
|
||||
FA.registerLanguage("r", wkA());
|
||||
FA.registerLanguage("reasonml", UkA());
|
||||
FA.registerLanguage("rib", $kA());
|
||||
FA.registerLanguage("roboconf", MkA());
|
||||
FA.registerLanguage("routeros", RkA());
|
||||
FA.registerLanguage("rsl", TkA());
|
||||
FA.registerLanguage("ruleslanguage", SkA());
|
||||
FA.registerLanguage("rust", jkA());
|
||||
FA.registerLanguage("sas", kkA());
|
||||
FA.registerLanguage("scala", fkA());
|
||||
FA.registerLanguage("scheme", bkA());
|
||||
FA.registerLanguage("scilab", hkA());
|
||||
FA.registerLanguage("scss", dkA());
|
||||
FA.registerLanguage("shell", pkA());
|
||||
FA.registerLanguage("smali", lkA());
|
||||
FA.registerLanguage("smalltalk", nkA());
|
||||
FA.registerLanguage("sml", skA());
|
||||
FA.registerLanguage("sqf", okA());
|
||||
FA.registerLanguage("sql_more", ekA());
|
||||
FA.registerLanguage("sql", QxA());
|
||||
FA.registerLanguage("stan", GxA());
|
||||
FA.registerLanguage("stata", DxA());
|
||||
FA.registerLanguage("step21", WxA());
|
||||
FA.registerLanguage("stylus", FxA());
|
||||
FA.registerLanguage("subunit", VxA());
|
||||
FA.registerLanguage("swift", $xA());
|
||||
FA.registerLanguage("taggerscript", MxA());
|
||||
FA.registerLanguage("yaml", RxA());
|
||||
FA.registerLanguage("tap", TxA());
|
||||
FA.registerLanguage("tcl", _xA());
|
||||
FA.registerLanguage("thrift", yxA());
|
||||
FA.registerLanguage("tp", xxA());
|
||||
FA.registerLanguage("twig", vxA());
|
||||
FA.registerLanguage("typescript", uxA());
|
||||
FA.registerLanguage("vala", cxA());
|
||||
FA.registerLanguage("vbnet", nxA());
|
||||
FA.registerLanguage("vbscript", rxA());
|
||||
FA.registerLanguage("vbscript-html", txA());
|
||||
FA.registerLanguage("verilog", AfA());
|
||||
FA.registerLanguage("vhdl", QfA());
|
||||
FA.registerLanguage("vim", GfA());
|
||||
FA.registerLanguage("x86asm", DfA());
|
||||
FA.registerLanguage("xl", WfA());
|
||||
FA.registerLanguage("xquery", FfA());
|
||||
FA.registerLanguage("zephir", VfA());
|
||||
CfA.exports = FA
|
||||
})
|
||||
// @from(Start 2313246, End 2319412)
|
||||
rvA = z((Ht9) => {
|
||||
function xP1(A, B) {
|
||||
var Q = A.length;
|
||||
A.push(B);
|
||||
A: for (; 0 < Q;) {
|
||||
var I = Q - 1 >>> 1,
|
||||
G = A[I];
|
||||
if (0 < e81(G, B)) A[I] = B, A[Q] = G, Q = I;
|
||||
else break A
|
||||
}
|
||||
}
|
||||
|
||||
function zC(A) {
|
||||
return A.length === 0 ? null : A[0]
|
||||
}
|
||||
|
||||
function IB1(A) {
|
||||
if (A.length === 0) return null;
|
||||
var B = A[0],
|
||||
Q = A.pop();
|
||||
if (Q !== B) {
|
||||
A[0] = Q;
|
||||
A: for (var I = 0, G = A.length, Z = G >>> 1; I < Z;) {
|
||||
var D = 2 * (I + 1) - 1,
|
||||
Y = A[D],
|
||||
W = D + 1,
|
||||
J = A[W];
|
||||
if (0 > e81(Y, Q)) W < G && 0 > e81(J, Y) ? (A[I] = J, A[W] = Q, I = W) : (A[I] = Y, A[D] = Q, I = D);
|
||||
else if (W < G && 0 > e81(J, Q)) A[I] = J, A[W] = Q, I = W;
|
||||
else break A
|
||||
}
|
||||
}
|
||||
return B
|
||||
}
|
||||
|
||||
function e81(A, B) {
|
||||
var Q = A.sortIndex - B.sortIndex;
|
||||
return Q !== 0 ? Q : A.id - B.id
|
||||
}
|
||||
if (typeof performance === "object" && typeof performance.now === "function") fP1 = performance, Ht9.unstable_now = function() {
|
||||
return fP1.now()
|
||||
};
|
||||
else AB1 = Date, vP1 = AB1.now(), Ht9.unstable_now = function() {
|
||||
return AB1.now() - vP1
|
||||
};
|
||||
var fP1, AB1, vP1, Lz = [],
|
||||
QL = [],
|
||||
Kt9 = 1,
|
||||
BX = null,
|
||||
EZ = 3,
|
||||
GB1 = !1,
|
||||
RS = !1,
|
||||
Dn = !1,
|
||||
lvA = typeof setTimeout === "function" ? setTimeout : null,
|
||||
ivA = typeof clearTimeout === "function" ? clearTimeout : null,
|
||||
cvA = typeof setImmediate !== "undefined" ? setImmediate : null;
|
||||
typeof navigator !== "undefined" && navigator.scheduling !== void 0 && navigator.scheduling.isInputPending !== void 0 && navigator.scheduling.isInputPending.bind(navigator.scheduling);
|
||||
|
||||
function bP1(A) {
|
||||
for (var B = zC(QL); B !== null;) {
|
||||
if (B.callback === null) IB1(QL);
|
||||
else if (B.startTime <= A) IB1(QL), B.sortIndex = B.expirationTime, xP1(Lz, B);
|
||||
else break;
|
||||
B = zC(QL)
|
||||
}
|
||||
}
|
||||
|
||||
function hP1(A) {
|
||||
if (Dn = !1, bP1(A), !RS)
|
||||
if (zC(Lz) !== null) RS = !0, dP1(mP1);
|
||||
else {
|
||||
var B = zC(QL);
|
||||
B !== null && uP1(hP1, B.startTime - A)
|
||||
}
|
||||
}
|
||||
|
||||
function mP1(A, B) {
|
||||
RS = !1, Dn && (Dn = !1, ivA(Yn), Yn = -1), GB1 = !0;
|
||||
var Q = EZ;
|
||||
try {
|
||||
bP1(B);
|
||||
for (BX = zC(Lz); BX !== null && (!(BX.expirationTime > B) || A && !svA());) {
|
||||
var I = BX.callback;
|
||||
if (typeof I === "function") {
|
||||
BX.callback = null, EZ = BX.priorityLevel;
|
||||
var G = I(BX.expirationTime <= B);
|
||||
B = Ht9.unstable_now(), typeof G === "function" ? BX.callback = G : BX === zC(Lz) && IB1(Lz), bP1(B)
|
||||
} else IB1(Lz);
|
||||
BX = zC(Lz)
|
||||
}
|
||||
if (BX !== null) var Z = !0;
|
||||
else {
|
||||
var D = zC(QL);
|
||||
D !== null && uP1(hP1, D.startTime - B), Z = !1
|
||||
}
|
||||
return Z
|
||||
} finally {
|
||||
BX = null, EZ = Q, GB1 = !1
|
||||
}
|
||||
}
|
||||
var ZB1 = !1,
|
||||
BB1 = null,
|
||||
Yn = -1,
|
||||
nvA = 5,
|
||||
avA = -1;
|
||||
|
||||
function svA() {
|
||||
return Ht9.unstable_now() - avA < nvA ? !1 : !0
|
||||
}
|
||||
|
||||
function kP1() {
|
||||
if (BB1 !== null) {
|
||||
var A = Ht9.unstable_now();
|
||||
avA = A;
|
||||
var B = !0;
|
||||
try {
|
||||
B = BB1(!0, A)
|
||||
} finally {
|
||||
B ? Zn() : (ZB1 = !1, BB1 = null)
|
||||
}
|
||||
} else ZB1 = !1
|
||||
}
|
||||
var Zn;
|
||||
if (typeof cvA === "function") Zn = function() {
|
||||
cvA(kP1)
|
||||
};
|
||||
else if (typeof MessageChannel !== "undefined") QB1 = new MessageChannel, gP1 = QB1.port2, QB1.port1.onmessage = kP1, Zn = function() {
|
||||
gP1.postMessage(null)
|
||||
};
|
||||
else Zn = function() {
|
||||
lvA(kP1, 0)
|
||||
};
|
||||
var QB1, gP1;
|
||||
|
||||
function dP1(A) {
|
||||
BB1 = A, ZB1 || (ZB1 = !0, Zn())
|
||||
}
|
||||
|
||||
function uP1(A, B) {
|
||||
Yn = lvA(function() {
|
||||
A(Ht9.unstable_now())
|
||||
}, B)
|
||||
}
|
||||
Ht9.unstable_IdlePriority = 5;
|
||||
Ht9.unstable_ImmediatePriority = 1;
|
||||
Ht9.unstable_LowPriority = 4;
|
||||
Ht9.unstable_NormalPriority = 3;
|
||||
Ht9.unstable_Profiling = null;
|
||||
Ht9.unstable_UserBlockingPriority = 2;
|
||||
Ht9.unstable_cancelCallback = function(A) {
|
||||
A.callback = null
|
||||
};
|
||||
Ht9.unstable_continueExecution = function() {
|
||||
RS || GB1 || (RS = !0, dP1(mP1))
|
||||
};
|
||||
Ht9.unstable_forceFrameRate = function(A) {
|
||||
0 > A || 125 < A ? console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported") : nvA = 0 < A ? Math.floor(1000 / A) : 5
|
||||
};
|
||||
Ht9.unstable_getCurrentPriorityLevel = function() {
|
||||
return EZ
|
||||
};
|
||||
Ht9.unstable_getFirstCallbackNode = function() {
|
||||
return zC(Lz)
|
||||
};
|
||||
Ht9.unstable_next = function(A) {
|
||||
switch (EZ) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
var B = 3;
|
||||
break;
|
||||
default:
|
||||
B = EZ
|
||||
}
|
||||
var Q = EZ;
|
||||
EZ = B;
|
||||
try {
|
||||
return A()
|
||||
} finally {
|
||||
EZ = Q
|
||||
}
|
||||
};
|
||||
Ht9.unstable_pauseExecution = function() {};
|
||||
Ht9.unstable_requestPaint = function() {};
|
||||
Ht9.unstable_runWithPriority = function(A, B) {
|
||||
switch (A) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
break;
|
||||
default:
|
||||
A = 3
|
||||
}
|
||||
var Q = EZ;
|
||||
EZ = A;
|
||||
try {
|
||||
return B()
|
||||
} finally {
|
||||
EZ = Q
|
||||
}
|
||||
};
|
||||
Ht9.unstable_scheduleCallback = function(A, B, Q) {
|
||||
var I = Ht9.unstable_now();
|
||||
switch (typeof Q === "object" && Q !== null ? (Q = Q.delay, Q = typeof Q === "number" && 0 < Q ? I + Q : I) : Q = I, A) {
|
||||
case 1:
|
||||
var G = -1;
|
||||
break;
|
||||
case 2:
|
||||
G = 250;
|
||||
break;
|
||||
case 5:
|
||||
G = 1073741823;
|
||||
break;
|
||||
case 4:
|
||||
G = 1e4;
|
||||
break;
|
||||
default:
|
||||
G = 5000
|
||||
}
|
||||
return G = Q + G, A = {
|
||||
id: Kt9++,
|
||||
callback: B,
|
||||
priorityLevel: A,
|
||||
startTime: Q,
|
||||
expirationTime: G,
|
||||
sortIndex: -1
|
||||
}, Q > I ? (A.sortIndex = Q, xP1(QL, A), zC(Lz) === null && A === zC(QL) && (Dn ? (ivA(Yn), Yn = -1) : Dn = !0, uP1(hP1, Q - I))) : (A.sortIndex = G, xP1(Lz, A), RS || GB1 || (RS = !0, dP1(mP1))), A
|
||||
};
|
||||
Ht9.unstable_shouldYield = svA;
|
||||
Ht9.unstable_wrapCallback = function(A) {
|
||||
var B = EZ;
|
||||
return function() {
|
||||
var Q = EZ;
|
||||
EZ = B;
|
||||
try {
|
||||
return A.apply(this, arguments)
|
||||
} finally {
|
||||
EZ = Q
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,287 +0,0 @@
|
||||
|
||||
// @from(Start 2571554, End 2579166)
|
||||
YhA = z((V48, DhA) => {
|
||||
var M14 = Z1("events"),
|
||||
oB1 = Z1("http"),
|
||||
{
|
||||
Duplex: X48
|
||||
} = Z1("stream"),
|
||||
{
|
||||
createHash: L14
|
||||
} = Z1("crypto"),
|
||||
IhA = JS1(),
|
||||
SS = qn(),
|
||||
R14 = QhA(),
|
||||
O14 = rB1(),
|
||||
{
|
||||
GUID: T14,
|
||||
kWebSocket: P14
|
||||
} = AN(),
|
||||
S14 = /^[+/0-9A-Za-z]{22}==$/;
|
||||
class ZhA extends M14 {
|
||||
constructor(A, B) {
|
||||
super();
|
||||
if (A = {
|
||||
allowSynchronousEvents: !0,
|
||||
autoPong: !0,
|
||||
maxPayload: 104857600,
|
||||
skipUTF8Validation: !1,
|
||||
perMessageDeflate: !1,
|
||||
handleProtocols: null,
|
||||
clientTracking: !0,
|
||||
verifyClient: null,
|
||||
noServer: !1,
|
||||
backlog: null,
|
||||
server: null,
|
||||
host: null,
|
||||
path: null,
|
||||
port: null,
|
||||
WebSocket: O14,
|
||||
...A
|
||||
}, A.port == null && !A.server && !A.noServer || A.port != null && (A.server || A.noServer) || A.server && A.noServer) throw new TypeError('One and only one of the "port", "server", or "noServer" options must be specified');
|
||||
if (A.port != null) this._server = oB1.createServer((Q, I) => {
|
||||
let G = oB1.STATUS_CODES[426];
|
||||
I.writeHead(426, {
|
||||
"Content-Length": G.length,
|
||||
"Content-Type": "text/plain"
|
||||
}), I.end(G)
|
||||
}), this._server.listen(A.port, A.host, A.backlog, B);
|
||||
else if (A.server) this._server = A.server;
|
||||
if (this._server) {
|
||||
let Q = this.emit.bind(this, "connection");
|
||||
this._removeListeners = _14(this._server, {
|
||||
listening: this.emit.bind(this, "listening"),
|
||||
error: this.emit.bind(this, "error"),
|
||||
upgrade: (I, G, Z) => {
|
||||
this.handleUpgrade(I, G, Z, Q)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (A.perMessageDeflate === !0) A.perMessageDeflate = {};
|
||||
if (A.clientTracking) this.clients = new Set, this._shouldEmitClose = !1;
|
||||
this.options = A, this._state = 0
|
||||
}
|
||||
address() {
|
||||
if (this.options.noServer) throw new Error('The server is operating in "noServer" mode');
|
||||
if (!this._server) return null;
|
||||
return this._server.address()
|
||||
}
|
||||
close(A) {
|
||||
if (this._state === 2) {
|
||||
if (A) this.once("close", () => {
|
||||
A(new Error("The server is not running"))
|
||||
});
|
||||
process.nextTick(On, this);
|
||||
return
|
||||
}
|
||||
if (A) this.once("close", A);
|
||||
if (this._state === 1) return;
|
||||
if (this._state = 1, this.options.noServer || this.options.server) {
|
||||
if (this._server) this._removeListeners(), this._removeListeners = this._server = null;
|
||||
if (this.clients)
|
||||
if (!this.clients.size) process.nextTick(On, this);
|
||||
else this._shouldEmitClose = !0;
|
||||
else process.nextTick(On, this)
|
||||
} else {
|
||||
let B = this._server;
|
||||
this._removeListeners(), this._removeListeners = this._server = null, B.close(() => {
|
||||
On(this)
|
||||
})
|
||||
}
|
||||
}
|
||||
shouldHandle(A) {
|
||||
if (this.options.path) {
|
||||
let B = A.url.indexOf("?");
|
||||
if ((B !== -1 ? A.url.slice(0, B) : A.url) !== this.options.path) return !1
|
||||
}
|
||||
return !0
|
||||
}
|
||||
handleUpgrade(A, B, Q, I) {
|
||||
B.on("error", GhA);
|
||||
let G = A.headers["sec-websocket-key"],
|
||||
Z = A.headers.upgrade,
|
||||
D = +A.headers["sec-websocket-version"];
|
||||
if (A.method !== "GET") {
|
||||
_S(this, A, B, 405, "Invalid HTTP method");
|
||||
return
|
||||
}
|
||||
if (Z === void 0 || Z.toLowerCase() !== "websocket") {
|
||||
_S(this, A, B, 400, "Invalid Upgrade header");
|
||||
return
|
||||
}
|
||||
if (G === void 0 || !S14.test(G)) {
|
||||
_S(this, A, B, 400, "Missing or invalid Sec-WebSocket-Key header");
|
||||
return
|
||||
}
|
||||
if (D !== 8 && D !== 13) {
|
||||
_S(this, A, B, 400, "Missing or invalid Sec-WebSocket-Version header");
|
||||
return
|
||||
}
|
||||
if (!this.shouldHandle(A)) {
|
||||
Tn(B, 400);
|
||||
return
|
||||
}
|
||||
let Y = A.headers["sec-websocket-protocol"],
|
||||
W = new Set;
|
||||
if (Y !== void 0) try {
|
||||
W = R14.parse(Y)
|
||||
} catch (X) {
|
||||
_S(this, A, B, 400, "Invalid Sec-WebSocket-Protocol header");
|
||||
return
|
||||
}
|
||||
let J = A.headers["sec-websocket-extensions"],
|
||||
F = {};
|
||||
if (this.options.perMessageDeflate && J !== void 0) {
|
||||
let X = new SS(this.options.perMessageDeflate, !0, this.options.maxPayload);
|
||||
try {
|
||||
let V = IhA.parse(J);
|
||||
if (V[SS.extensionName]) X.accept(V[SS.extensionName]), F[SS.extensionName] = X
|
||||
} catch (V) {
|
||||
_S(this, A, B, 400, "Invalid or unacceptable Sec-WebSocket-Extensions header");
|
||||
return
|
||||
}
|
||||
}
|
||||
if (this.options.verifyClient) {
|
||||
let X = {
|
||||
origin: A.headers[`${D===8?"sec-websocket-origin":"origin"}`],
|
||||
secure: !!(A.socket.authorized || A.socket.encrypted),
|
||||
req: A
|
||||
};
|
||||
if (this.options.verifyClient.length === 2) {
|
||||
this.options.verifyClient(X, (V, C, K, E) => {
|
||||
if (!V) return Tn(B, C || 401, K, E);
|
||||
this.completeUpgrade(F, G, W, A, B, Q, I)
|
||||
});
|
||||
return
|
||||
}
|
||||
if (!this.options.verifyClient(X)) return Tn(B, 401)
|
||||
}
|
||||
this.completeUpgrade(F, G, W, A, B, Q, I)
|
||||
}
|
||||
completeUpgrade(A, B, Q, I, G, Z, D) {
|
||||
if (!G.readable || !G.writable) return G.destroy();
|
||||
if (G[P14]) throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");
|
||||
if (this._state > 0) return Tn(G, 503);
|
||||
let W = ["HTTP/1.1 101 Switching Protocols", "Upgrade: websocket", "Connection: Upgrade", `Sec-WebSocket-Accept: ${L14("sha1").update(B+T14).digest("base64")}`],
|
||||
J = new this.options.WebSocket(null, void 0, this.options);
|
||||
if (Q.size) {
|
||||
let F = this.options.handleProtocols ? this.options.handleProtocols(Q, I) : Q.values().next().value;
|
||||
if (F) W.push(`Sec-WebSocket-Protocol: ${F}`), J._protocol = F
|
||||
}
|
||||
if (A[SS.extensionName]) {
|
||||
let F = A[SS.extensionName].params,
|
||||
X = IhA.format({
|
||||
[SS.extensionName]: [F]
|
||||
});
|
||||
W.push(`Sec-WebSocket-Extensions: ${X}`), J._extensions = A
|
||||
}
|
||||
if (this.emit("headers", W, I), G.write(W.concat(`\r
|
||||
`).join(`\r
|
||||
`)), G.removeListener("error", GhA), J.setSocket(G, Z, {
|
||||
allowSynchronousEvents: this.options.allowSynchronousEvents,
|
||||
maxPayload: this.options.maxPayload,
|
||||
skipUTF8Validation: this.options.skipUTF8Validation
|
||||
}), this.clients) this.clients.add(J), J.on("close", () => {
|
||||
if (this.clients.delete(J), this._shouldEmitClose && !this.clients.size) process.nextTick(On, this)
|
||||
});
|
||||
D(J, I)
|
||||
}
|
||||
}
|
||||
DhA.exports = ZhA;
|
||||
|
||||
function _14(A, B) {
|
||||
for (let Q of Object.keys(B)) A.on(Q, B[Q]);
|
||||
return function Q() {
|
||||
for (let I of Object.keys(B)) A.removeListener(I, B[I])
|
||||
}
|
||||
}
|
||||
|
||||
function On(A) {
|
||||
A._state = 2, A.emit("close")
|
||||
}
|
||||
|
||||
function GhA() {
|
||||
this.destroy()
|
||||
}
|
||||
|
||||
function Tn(A, B, Q, I) {
|
||||
Q = Q || oB1.STATUS_CODES[B], I = {
|
||||
Connection: "close",
|
||||
"Content-Type": "text/html",
|
||||
"Content-Length": Buffer.byteLength(Q),
|
||||
...I
|
||||
}, A.once("finish", A.destroy), A.end(`HTTP/1.1 ${B} ${oB1.STATUS_CODES[B]}\r
|
||||
` + Object.keys(I).map((G) => `${G}: ${I[G]}`).join(`\r
|
||||
`) + `\r
|
||||
\r
|
||||
` + Q)
|
||||
}
|
||||
|
||||
function _S(A, B, Q, I, G) {
|
||||
if (A.listenerCount("wsClientError")) {
|
||||
let Z = new Error(G);
|
||||
Error.captureStackTrace(Z, _S), A.emit("wsClientError", Z, Q, B)
|
||||
} else Tn(Q, I, G)
|
||||
}
|
||||
})
|
||||
// @from(Start 2579172, End 2579175)
|
||||
j14
|
||||
// @from(Start 2579177, End 2579180)
|
||||
y14
|
||||
// @from(Start 2579182, End 2579185)
|
||||
k14
|
||||
// @from(Start 2579187, End 2579190)
|
||||
tB1
|
||||
// @from(Start 2579192, End 2579195)
|
||||
x14
|
||||
// @from(Start 2579197, End 2579199)
|
||||
XL
|
||||
// @from(Start 2579205, End 2579344)
|
||||
eB1 = J21(() => {
|
||||
j14 = I1(AhA(), 1), y14 = I1(ZS1(), 1), k14 = I1(YS1(), 1), tB1 = I1(rB1(), 1), x14 = I1(YhA(), 1), XL = tB1.default
|
||||
})
|
||||
// @from(Start 2579350, End 2579353)
|
||||
A31
|
||||
// @from(Start 2579359, End 2580112)
|
||||
WhA = J21(() => {
|
||||
eB1();
|
||||
A31 = global;
|
||||
A31.WebSocket ||= XL;
|
||||
A31.window ||= global;
|
||||
A31.self ||= global;
|
||||
A31.window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = [{
|
||||
type: 1,
|
||||
value: 7,
|
||||
isEnabled: !0
|
||||
}, {
|
||||
type: 2,
|
||||
value: "InternalApp",
|
||||
isEnabled: !0,
|
||||
isValid: !0
|
||||
}, {
|
||||
type: 2,
|
||||
value: "InternalAppContext",
|
||||
isEnabled: !0,
|
||||
isValid: !0
|
||||
}, {
|
||||
type: 2,
|
||||
value: "InternalStdoutContext",
|
||||
isEnabled: !0,
|
||||
isValid: !0
|
||||
}, {
|
||||
type: 2,
|
||||
value: "InternalStderrContext",
|
||||
isEnabled: !0,
|
||||
isValid: !0
|
||||
}, {
|
||||
type: 2,
|
||||
value: "InternalStdinContext",
|
||||
isEnabled: !0,
|
||||
isValid: !0
|
||||
}, {
|
||||
type: 2,
|
||||
value: "InternalFocusContext",
|
||||
isEnabled: !0,
|
||||
isValid: !0
|
||||
}]
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,124 +0,0 @@
|
||||
|
||||
// @from(Start 3846158, End 3846482)
|
||||
nQ0 = z((lQ0) => {
|
||||
Object.defineProperty(lQ0, "__esModule", {
|
||||
value: !0
|
||||
});
|
||||
lQ0.default = void 0;
|
||||
var CS4 = cQ0(Af1()),
|
||||
KS4 = cQ0(pQ0());
|
||||
|
||||
function cQ0(A) {
|
||||
return A && A.__esModule ? A : {
|
||||
default: A
|
||||
}
|
||||
}
|
||||
var HS4 = CS4.default("v5", 80, KS4.default),
|
||||
zS4 = HS4;
|
||||
lQ0.default = zS4
|
||||
})
|
||||
// @from(Start 3846488, End 3846670)
|
||||
rQ0 = z((aQ0) => {
|
||||
Object.defineProperty(aQ0, "__esModule", {
|
||||
value: !0
|
||||
});
|
||||
aQ0.default = void 0;
|
||||
var wS4 = "00000000-0000-0000-0000-000000000000";
|
||||
aQ0.default = wS4
|
||||
})
|
||||
// @from(Start 3846676, End 3847055)
|
||||
eQ0 = z((oQ0) => {
|
||||
Object.defineProperty(oQ0, "__esModule", {
|
||||
value: !0
|
||||
});
|
||||
oQ0.default = void 0;
|
||||
var ES4 = US4(na());
|
||||
|
||||
function US4(A) {
|
||||
return A && A.__esModule ? A : {
|
||||
default: A
|
||||
}
|
||||
}
|
||||
|
||||
function NS4(A) {
|
||||
if (!ES4.default(A)) throw TypeError("Invalid UUID");
|
||||
return parseInt(A.slice(14, 15), 16)
|
||||
}
|
||||
var $S4 = NS4;
|
||||
oQ0.default = $S4
|
||||
})
|
||||
// @from(Start 3847061, End 3848475)
|
||||
A70 = z((PC) => {
|
||||
Object.defineProperty(PC, "__esModule", {
|
||||
value: !0
|
||||
});
|
||||
Object.defineProperty(PC, "NIL", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return OS4.default
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PC, "parse", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return _S4.default
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PC, "stringify", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return SS4.default
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PC, "v1", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return qS4.default
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PC, "v3", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return MS4.default
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PC, "v4", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return LS4.default
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PC, "v5", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return RS4.default
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PC, "validate", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return PS4.default
|
||||
}
|
||||
});
|
||||
Object.defineProperty(PC, "version", {
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return TS4.default
|
||||
}
|
||||
});
|
||||
var qS4 = EN(UQ0()),
|
||||
MS4 = EN(yQ0()),
|
||||
LS4 = EN(mQ0()),
|
||||
RS4 = EN(nQ0()),
|
||||
OS4 = EN(rQ0()),
|
||||
TS4 = EN(eQ0()),
|
||||
PS4 = EN(na()),
|
||||
SS4 = EN(aa()),
|
||||
_S4 = EN(ex1());
|
||||
|
||||
function EN(A) {
|
||||
return A && A.__esModule ? A : {
|
||||
default: A
|
||||
}
|
||||
}
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,590 +0,0 @@
|
||||
|
||||
// @from(Start 4294524, End 4298822)
|
||||
Iw = z((Pg1) => {
|
||||
var z9 = Pg1;
|
||||
z9.asPromise = Lg1();
|
||||
z9.base64 = tR0();
|
||||
z9.EventEmitter = AO0();
|
||||
z9.float = YO0();
|
||||
z9.inquire = Og1();
|
||||
z9.utf8 = FO0();
|
||||
z9.pool = VO0();
|
||||
z9.LongBits = KO0();
|
||||
z9.isNode = Boolean(typeof global !== "undefined" && global && global.process && global.process.versions && global.process.versions.node);
|
||||
z9.global = z9.isNode && global || typeof window !== "undefined" && window || typeof self !== "undefined" && self || Pg1;
|
||||
z9.emptyArray = Object.freeze ? Object.freeze([]) : [];
|
||||
z9.emptyObject = Object.freeze ? Object.freeze({}) : {};
|
||||
z9.isInteger = Number.isInteger || function A(B) {
|
||||
return typeof B === "number" && isFinite(B) && Math.floor(B) === B
|
||||
};
|
||||
z9.isString = function A(B) {
|
||||
return typeof B === "string" || B instanceof String
|
||||
};
|
||||
z9.isObject = function A(B) {
|
||||
return B && typeof B === "object"
|
||||
};
|
||||
z9.isset = z9.isSet = function A(B, Q) {
|
||||
var I = B[Q];
|
||||
if (I != null && B.hasOwnProperty(Q)) return typeof I !== "object" || (Array.isArray(I) ? I.length : Object.keys(I).length) > 0;
|
||||
return !1
|
||||
};
|
||||
z9.Buffer = function() {
|
||||
try {
|
||||
var A = z9.inquire("buffer").Buffer;
|
||||
return A.prototype.utf8Write ? A : null
|
||||
} catch (B) {
|
||||
return null
|
||||
}
|
||||
}();
|
||||
z9._Buffer_from = null;
|
||||
z9._Buffer_allocUnsafe = null;
|
||||
z9.newBuffer = function A(B) {
|
||||
return typeof B === "number" ? z9.Buffer ? z9._Buffer_allocUnsafe(B) : new z9.Array(B) : z9.Buffer ? z9._Buffer_from(B) : typeof Uint8Array === "undefined" ? B : new Uint8Array(B)
|
||||
};
|
||||
z9.Array = typeof Uint8Array !== "undefined" ? Uint8Array : Array;
|
||||
z9.Long = z9.global.dcodeIO && z9.global.dcodeIO.Long || z9.global.Long || z9.inquire("long");
|
||||
z9.key2Re = /^true|false|0|1$/;
|
||||
z9.key32Re = /^-?(?:0|[1-9][0-9]*)$/;
|
||||
z9.key64Re = /^(?:[\\x00-\\xff]{8}|-?(?:0|[1-9][0-9]*))$/;
|
||||
z9.longToHash = function A(B) {
|
||||
return B ? z9.LongBits.from(B).toHash() : z9.LongBits.zeroHash
|
||||
};
|
||||
z9.longFromHash = function A(B, Q) {
|
||||
var I = z9.LongBits.fromHash(B);
|
||||
if (z9.Long) return z9.Long.fromBits(I.lo, I.hi, Q);
|
||||
return I.toNumber(Boolean(Q))
|
||||
};
|
||||
|
||||
function HO0(A, B, Q) {
|
||||
for (var I = Object.keys(B), G = 0; G < I.length; ++G)
|
||||
if (A[I[G]] === void 0 || !Q) A[I[G]] = B[I[G]];
|
||||
return A
|
||||
}
|
||||
z9.merge = HO0;
|
||||
z9.lcFirst = function A(B) {
|
||||
return B.charAt(0).toLowerCase() + B.substring(1)
|
||||
};
|
||||
|
||||
function zO0(A) {
|
||||
function B(Q, I) {
|
||||
if (!(this instanceof B)) return new B(Q, I);
|
||||
if (Object.defineProperty(this, "message", {
|
||||
get: function() {
|
||||
return Q
|
||||
}
|
||||
}), Error.captureStackTrace) Error.captureStackTrace(this, B);
|
||||
else Object.defineProperty(this, "stack", {
|
||||
value: new Error().stack || ""
|
||||
});
|
||||
if (I) HO0(this, I)
|
||||
}
|
||||
return B.prototype = Object.create(Error.prototype, {
|
||||
constructor: {
|
||||
value: B,
|
||||
writable: !0,
|
||||
enumerable: !1,
|
||||
configurable: !0
|
||||
},
|
||||
name: {
|
||||
get: function Q() {
|
||||
return A
|
||||
},
|
||||
set: void 0,
|
||||
enumerable: !1,
|
||||
configurable: !0
|
||||
},
|
||||
toString: {
|
||||
value: function Q() {
|
||||
return this.name + ": " + this.message
|
||||
},
|
||||
writable: !0,
|
||||
enumerable: !1,
|
||||
configurable: !0
|
||||
}
|
||||
}), B
|
||||
}
|
||||
z9.newError = zO0;
|
||||
z9.ProtocolError = zO0("ProtocolError");
|
||||
z9.oneOfGetter = function A(B) {
|
||||
var Q = {};
|
||||
for (var I = 0; I < B.length; ++I) Q[B[I]] = 1;
|
||||
return function() {
|
||||
for (var G = Object.keys(this), Z = G.length - 1; Z > -1; --Z)
|
||||
if (Q[G[Z]] === 1 && this[G[Z]] !== void 0 && this[G[Z]] !== null) return G[Z]
|
||||
}
|
||||
};
|
||||
z9.oneOfSetter = function A(B) {
|
||||
return function(Q) {
|
||||
for (var I = 0; I < B.length; ++I)
|
||||
if (B[I] !== Q) delete this[B[I]]
|
||||
}
|
||||
};
|
||||
z9.toJSONOptions = {
|
||||
longs: String,
|
||||
enums: String,
|
||||
bytes: String,
|
||||
json: !0
|
||||
};
|
||||
z9._configure = function() {
|
||||
var A = z9.Buffer;
|
||||
if (!A) {
|
||||
z9._Buffer_from = z9._Buffer_allocUnsafe = null;
|
||||
return
|
||||
}
|
||||
z9._Buffer_from = A.from !== Uint8Array.from && A.from || function B(Q, I) {
|
||||
return new A(Q, I)
|
||||
}, z9._Buffer_allocUnsafe = A.allocUnsafe || function B(Q) {
|
||||
return new A(Q)
|
||||
}
|
||||
}
|
||||
})
|
||||
// @from(Start 4298828, End 4303187)
|
||||
yZ1 = z((BN8, NO0) => {
|
||||
NO0.exports = V5;
|
||||
var OX = Iw(),
|
||||
Sg1, jZ1 = OX.LongBits,
|
||||
wO0 = OX.base64,
|
||||
EO0 = OX.utf8;
|
||||
|
||||
function fs(A, B, Q) {
|
||||
this.fn = A, this.len = B, this.next = void 0, this.val = Q
|
||||
}
|
||||
|
||||
function jg1() {}
|
||||
|
||||
function S26(A) {
|
||||
this.head = A.head, this.tail = A.tail, this.len = A.len, this.next = A.states
|
||||
}
|
||||
|
||||
function V5() {
|
||||
this.len = 0, this.head = new fs(jg1, 0, 0), this.tail = this.head, this.states = null
|
||||
}
|
||||
var UO0 = function A() {
|
||||
return OX.Buffer ? function B() {
|
||||
return (V5.create = function Q() {
|
||||
return new Sg1
|
||||
})()
|
||||
} : function B() {
|
||||
return new V5
|
||||
}
|
||||
};
|
||||
V5.create = UO0();
|
||||
V5.alloc = function A(B) {
|
||||
return new OX.Array(B)
|
||||
};
|
||||
if (OX.Array !== Array) V5.alloc = OX.pool(V5.alloc, OX.Array.prototype.subarray);
|
||||
V5.prototype._push = function A(B, Q, I) {
|
||||
return this.tail = this.tail.next = new fs(B, Q, I), this.len += Q, this
|
||||
};
|
||||
|
||||
function yg1(A, B, Q) {
|
||||
B[Q] = A & 255
|
||||
}
|
||||
|
||||
function _26(A, B, Q) {
|
||||
while (A > 127) B[Q++] = A & 127 | 128, A >>>= 7;
|
||||
B[Q] = A
|
||||
}
|
||||
|
||||
function kg1(A, B) {
|
||||
this.len = A, this.next = void 0, this.val = B
|
||||
}
|
||||
kg1.prototype = Object.create(fs.prototype);
|
||||
kg1.prototype.fn = _26;
|
||||
V5.prototype.uint32 = function A(B) {
|
||||
return this.len += (this.tail = this.tail.next = new kg1((B = B >>> 0) < 128 ? 1 : B < 16384 ? 2 : B < 2097152 ? 3 : B < 268435456 ? 4 : 5, B)).len, this
|
||||
};
|
||||
V5.prototype.int32 = function A(B) {
|
||||
return B < 0 ? this._push(xg1, 10, jZ1.fromNumber(B)) : this.uint32(B)
|
||||
};
|
||||
V5.prototype.sint32 = function A(B) {
|
||||
return this.uint32((B << 1 ^ B >> 31) >>> 0)
|
||||
};
|
||||
|
||||
function xg1(A, B, Q) {
|
||||
while (A.hi) B[Q++] = A.lo & 127 | 128, A.lo = (A.lo >>> 7 | A.hi << 25) >>> 0, A.hi >>>= 7;
|
||||
while (A.lo > 127) B[Q++] = A.lo & 127 | 128, A.lo = A.lo >>> 7;
|
||||
B[Q++] = A.lo
|
||||
}
|
||||
V5.prototype.uint64 = function A(B) {
|
||||
var Q = jZ1.from(B);
|
||||
return this._push(xg1, Q.length(), Q)
|
||||
};
|
||||
V5.prototype.int64 = V5.prototype.uint64;
|
||||
V5.prototype.sint64 = function A(B) {
|
||||
var Q = jZ1.from(B).zzEncode();
|
||||
return this._push(xg1, Q.length(), Q)
|
||||
};
|
||||
V5.prototype.bool = function A(B) {
|
||||
return this._push(yg1, 1, B ? 1 : 0)
|
||||
};
|
||||
|
||||
function _g1(A, B, Q) {
|
||||
B[Q] = A & 255, B[Q + 1] = A >>> 8 & 255, B[Q + 2] = A >>> 16 & 255, B[Q + 3] = A >>> 24
|
||||
}
|
||||
V5.prototype.fixed32 = function A(B) {
|
||||
return this._push(_g1, 4, B >>> 0)
|
||||
};
|
||||
V5.prototype.sfixed32 = V5.prototype.fixed32;
|
||||
V5.prototype.fixed64 = function A(B) {
|
||||
var Q = jZ1.from(B);
|
||||
return this._push(_g1, 4, Q.lo)._push(_g1, 4, Q.hi)
|
||||
};
|
||||
V5.prototype.sfixed64 = V5.prototype.fixed64;
|
||||
V5.prototype.float = function A(B) {
|
||||
return this._push(OX.float.writeFloatLE, 4, B)
|
||||
};
|
||||
V5.prototype.double = function A(B) {
|
||||
return this._push(OX.float.writeDoubleLE, 8, B)
|
||||
};
|
||||
var j26 = OX.Array.prototype.set ? function A(B, Q, I) {
|
||||
Q.set(B, I)
|
||||
} : function A(B, Q, I) {
|
||||
for (var G = 0; G < B.length; ++G) Q[I + G] = B[G]
|
||||
};
|
||||
V5.prototype.bytes = function A(B) {
|
||||
var Q = B.length >>> 0;
|
||||
if (!Q) return this._push(yg1, 1, 0);
|
||||
if (OX.isString(B)) {
|
||||
var I = V5.alloc(Q = wO0.length(B));
|
||||
wO0.decode(B, I, 0), B = I
|
||||
}
|
||||
return this.uint32(Q)._push(j26, Q, B)
|
||||
};
|
||||
V5.prototype.string = function A(B) {
|
||||
var Q = EO0.length(B);
|
||||
return Q ? this.uint32(Q)._push(EO0.write, Q, B) : this._push(yg1, 1, 0)
|
||||
};
|
||||
V5.prototype.fork = function A() {
|
||||
return this.states = new S26(this), this.head = this.tail = new fs(jg1, 0, 0), this.len = 0, this
|
||||
};
|
||||
V5.prototype.reset = function A() {
|
||||
if (this.states) this.head = this.states.head, this.tail = this.states.tail, this.len = this.states.len, this.states = this.states.next;
|
||||
else this.head = this.tail = new fs(jg1, 0, 0), this.len = 0;
|
||||
return this
|
||||
};
|
||||
V5.prototype.ldelim = function A() {
|
||||
var B = this.head,
|
||||
Q = this.tail,
|
||||
I = this.len;
|
||||
if (this.reset().uint32(I), I) this.tail.next = B.next, this.tail = Q, this.len += I;
|
||||
return this
|
||||
};
|
||||
V5.prototype.finish = function A() {
|
||||
var B = this.head.next,
|
||||
Q = this.constructor.alloc(this.len),
|
||||
I = 0;
|
||||
while (B) B.fn(B.val, Q, I), I += B.len, B = B.next;
|
||||
return Q
|
||||
};
|
||||
V5._configure = function(A) {
|
||||
Sg1 = A, V5.create = UO0(), Sg1._configure()
|
||||
}
|
||||
})
|
||||
// @from(Start 4303193, End 4304290)
|
||||
MO0 = z((QN8, qO0) => {
|
||||
qO0.exports = Gw;
|
||||
var $O0 = yZ1();
|
||||
(Gw.prototype = Object.create($O0.prototype)).constructor = Gw;
|
||||
var ZR = Iw();
|
||||
|
||||
function Gw() {
|
||||
$O0.call(this)
|
||||
}
|
||||
Gw._configure = function() {
|
||||
Gw.alloc = ZR._Buffer_allocUnsafe, Gw.writeBytesBuffer = ZR.Buffer && ZR.Buffer.prototype instanceof Uint8Array && ZR.Buffer.prototype.set.name === "set" ? function A(B, Q, I) {
|
||||
Q.set(B, I)
|
||||
} : function A(B, Q, I) {
|
||||
if (B.copy) B.copy(Q, I, 0, B.length);
|
||||
else
|
||||
for (var G = 0; G < B.length;) Q[I++] = B[G++]
|
||||
}
|
||||
};
|
||||
Gw.prototype.bytes = function A(B) {
|
||||
if (ZR.isString(B)) B = ZR._Buffer_from(B, "base64");
|
||||
var Q = B.length >>> 0;
|
||||
if (this.uint32(Q), Q) this._push(Gw.writeBytesBuffer, Q, B);
|
||||
return this
|
||||
};
|
||||
|
||||
function y26(A, B, Q) {
|
||||
if (A.length < 40) ZR.utf8.write(A, B, Q);
|
||||
else if (B.utf8Write) B.utf8Write(A, Q);
|
||||
else B.write(A, Q)
|
||||
}
|
||||
Gw.prototype.string = function A(B) {
|
||||
var Q = ZR.Buffer.byteLength(B);
|
||||
if (this.uint32(Q), Q) this._push(y26, Q, B);
|
||||
return this
|
||||
};
|
||||
Gw._configure()
|
||||
})
|
||||
// @from(Start 4304296, End 4309921)
|
||||
xZ1 = z((IN8, PO0) => {
|
||||
PO0.exports = PQ;
|
||||
var mC = Iw(),
|
||||
vg1, OO0 = mC.LongBits,
|
||||
k26 = mC.utf8;
|
||||
|
||||
function dC(A, B) {
|
||||
return RangeError("index out of range: " + A.pos + " + " + (B || 1) + " > " + A.len)
|
||||
}
|
||||
|
||||
function PQ(A) {
|
||||
this.buf = A, this.pos = 0, this.len = A.length
|
||||
}
|
||||
var LO0 = typeof Uint8Array !== "undefined" ? function A(B) {
|
||||
if (B instanceof Uint8Array || Array.isArray(B)) return new PQ(B);
|
||||
throw Error("illegal buffer")
|
||||
} : function A(B) {
|
||||
if (Array.isArray(B)) return new PQ(B);
|
||||
throw Error("illegal buffer")
|
||||
},
|
||||
TO0 = function A() {
|
||||
return mC.Buffer ? function B(Q) {
|
||||
return (PQ.create = function I(G) {
|
||||
return mC.Buffer.isBuffer(G) ? new vg1(G) : LO0(G)
|
||||
})(Q)
|
||||
} : LO0
|
||||
};
|
||||
PQ.create = TO0();
|
||||
PQ.prototype._slice = mC.Array.prototype.subarray || mC.Array.prototype.slice;
|
||||
PQ.prototype.uint32 = function A() {
|
||||
var B = 4294967295;
|
||||
return function Q() {
|
||||
if (B = (this.buf[this.pos] & 127) >>> 0, this.buf[this.pos++] < 128) return B;
|
||||
if (B = (B | (this.buf[this.pos] & 127) << 7) >>> 0, this.buf[this.pos++] < 128) return B;
|
||||
if (B = (B | (this.buf[this.pos] & 127) << 14) >>> 0, this.buf[this.pos++] < 128) return B;
|
||||
if (B = (B | (this.buf[this.pos] & 127) << 21) >>> 0, this.buf[this.pos++] < 128) return B;
|
||||
if (B = (B | (this.buf[this.pos] & 15) << 28) >>> 0, this.buf[this.pos++] < 128) return B;
|
||||
if ((this.pos += 5) > this.len) throw this.pos = this.len, dC(this, 10);
|
||||
return B
|
||||
}
|
||||
}();
|
||||
PQ.prototype.int32 = function A() {
|
||||
return this.uint32() | 0
|
||||
};
|
||||
PQ.prototype.sint32 = function A() {
|
||||
var B = this.uint32();
|
||||
return B >>> 1 ^ -(B & 1) | 0
|
||||
};
|
||||
|
||||
function fg1() {
|
||||
var A = new OO0(0, 0),
|
||||
B = 0;
|
||||
if (this.len - this.pos > 4) {
|
||||
for (; B < 4; ++B)
|
||||
if (A.lo = (A.lo | (this.buf[this.pos] & 127) << B * 7) >>> 0, this.buf[this.pos++] < 128) return A;
|
||||
if (A.lo = (A.lo | (this.buf[this.pos] & 127) << 28) >>> 0, A.hi = (A.hi | (this.buf[this.pos] & 127) >> 4) >>> 0, this.buf[this.pos++] < 128) return A;
|
||||
B = 0
|
||||
} else {
|
||||
for (; B < 3; ++B) {
|
||||
if (this.pos >= this.len) throw dC(this);
|
||||
if (A.lo = (A.lo | (this.buf[this.pos] & 127) << B * 7) >>> 0, this.buf[this.pos++] < 128) return A
|
||||
}
|
||||
return A.lo = (A.lo | (this.buf[this.pos++] & 127) << B * 7) >>> 0, A
|
||||
}
|
||||
if (this.len - this.pos > 4) {
|
||||
for (; B < 5; ++B)
|
||||
if (A.hi = (A.hi | (this.buf[this.pos] & 127) << B * 7 + 3) >>> 0, this.buf[this.pos++] < 128) return A
|
||||
} else
|
||||
for (; B < 5; ++B) {
|
||||
if (this.pos >= this.len) throw dC(this);
|
||||
if (A.hi = (A.hi | (this.buf[this.pos] & 127) << B * 7 + 3) >>> 0, this.buf[this.pos++] < 128) return A
|
||||
}
|
||||
throw Error("invalid varint encoding")
|
||||
}
|
||||
PQ.prototype.bool = function A() {
|
||||
return this.uint32() !== 0
|
||||
};
|
||||
|
||||
function kZ1(A, B) {
|
||||
return (A[B - 4] | A[B - 3] << 8 | A[B - 2] << 16 | A[B - 1] << 24) >>> 0
|
||||
}
|
||||
PQ.prototype.fixed32 = function A() {
|
||||
if (this.pos + 4 > this.len) throw dC(this, 4);
|
||||
return kZ1(this.buf, this.pos += 4)
|
||||
};
|
||||
PQ.prototype.sfixed32 = function A() {
|
||||
if (this.pos + 4 > this.len) throw dC(this, 4);
|
||||
return kZ1(this.buf, this.pos += 4) | 0
|
||||
};
|
||||
|
||||
function RO0() {
|
||||
if (this.pos + 8 > this.len) throw dC(this, 8);
|
||||
return new OO0(kZ1(this.buf, this.pos += 4), kZ1(this.buf, this.pos += 4))
|
||||
}
|
||||
PQ.prototype.float = function A() {
|
||||
if (this.pos + 4 > this.len) throw dC(this, 4);
|
||||
var B = mC.float.readFloatLE(this.buf, this.pos);
|
||||
return this.pos += 4, B
|
||||
};
|
||||
PQ.prototype.double = function A() {
|
||||
if (this.pos + 8 > this.len) throw dC(this, 4);
|
||||
var B = mC.float.readDoubleLE(this.buf, this.pos);
|
||||
return this.pos += 8, B
|
||||
};
|
||||
PQ.prototype.bytes = function A() {
|
||||
var B = this.uint32(),
|
||||
Q = this.pos,
|
||||
I = this.pos + B;
|
||||
if (I > this.len) throw dC(this, B);
|
||||
if (this.pos += B, Array.isArray(this.buf)) return this.buf.slice(Q, I);
|
||||
if (Q === I) {
|
||||
var G = mC.Buffer;
|
||||
return G ? G.alloc(0) : new this.buf.constructor(0)
|
||||
}
|
||||
return this._slice.call(this.buf, Q, I)
|
||||
};
|
||||
PQ.prototype.string = function A() {
|
||||
var B = this.bytes();
|
||||
return k26.read(B, 0, B.length)
|
||||
};
|
||||
PQ.prototype.skip = function A(B) {
|
||||
if (typeof B === "number") {
|
||||
if (this.pos + B > this.len) throw dC(this, B);
|
||||
this.pos += B
|
||||
} else
|
||||
do
|
||||
if (this.pos >= this.len) throw dC(this); while (this.buf[this.pos++] & 128);
|
||||
return this
|
||||
};
|
||||
PQ.prototype.skipType = function(A) {
|
||||
switch (A) {
|
||||
case 0:
|
||||
this.skip();
|
||||
break;
|
||||
case 1:
|
||||
this.skip(8);
|
||||
break;
|
||||
case 2:
|
||||
this.skip(this.uint32());
|
||||
break;
|
||||
case 3:
|
||||
while ((A = this.uint32() & 7) !== 4) this.skipType(A);
|
||||
break;
|
||||
case 5:
|
||||
this.skip(4);
|
||||
break;
|
||||
default:
|
||||
throw Error("invalid wire type " + A + " at offset " + this.pos)
|
||||
}
|
||||
return this
|
||||
};
|
||||
PQ._configure = function(A) {
|
||||
vg1 = A, PQ.create = TO0(), vg1._configure();
|
||||
var B = mC.Long ? "toLong" : "toNumber";
|
||||
mC.merge(PQ.prototype, {
|
||||
int64: function Q() {
|
||||
return fg1.call(this)[B](!1)
|
||||
},
|
||||
uint64: function Q() {
|
||||
return fg1.call(this)[B](!0)
|
||||
},
|
||||
sint64: function Q() {
|
||||
return fg1.call(this).zzDecode()[B](!1)
|
||||
},
|
||||
fixed64: function Q() {
|
||||
return RO0.call(this)[B](!0)
|
||||
},
|
||||
sfixed64: function Q() {
|
||||
return RO0.call(this)[B](!1)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// @from(Start 4309927, End 4310506)
|
||||
yO0 = z((GN8, jO0) => {
|
||||
jO0.exports = l_;
|
||||
var _O0 = xZ1();
|
||||
(l_.prototype = Object.create(_O0.prototype)).constructor = l_;
|
||||
var SO0 = Iw();
|
||||
|
||||
function l_(A) {
|
||||
_O0.call(this, A)
|
||||
}
|
||||
l_._configure = function() {
|
||||
if (SO0.Buffer) l_.prototype._slice = SO0.Buffer.prototype.slice
|
||||
};
|
||||
l_.prototype.string = function A() {
|
||||
var B = this.uint32();
|
||||
return this.buf.utf8Slice ? this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + B, this.len)) : this.buf.toString("utf-8", this.pos, this.pos = Math.min(this.pos + B, this.len))
|
||||
};
|
||||
l_._configure()
|
||||
})
|
||||
// @from(Start 4310512, End 4311979)
|
||||
xO0 = z((ZN8, kO0) => {
|
||||
kO0.exports = vs;
|
||||
var bg1 = Iw();
|
||||
(vs.prototype = Object.create(bg1.EventEmitter.prototype)).constructor = vs;
|
||||
|
||||
function vs(A, B, Q) {
|
||||
if (typeof A !== "function") throw TypeError("rpcImpl must be a function");
|
||||
bg1.EventEmitter.call(this), this.rpcImpl = A, this.requestDelimited = Boolean(B), this.responseDelimited = Boolean(Q)
|
||||
}
|
||||
vs.prototype.rpcCall = function A(B, Q, I, G, Z) {
|
||||
if (!G) throw TypeError("request must be specified");
|
||||
var D = this;
|
||||
if (!Z) return bg1.asPromise(A, D, B, Q, I, G);
|
||||
if (!D.rpcImpl) {
|
||||
setTimeout(function() {
|
||||
Z(Error("already ended"))
|
||||
}, 0);
|
||||
return
|
||||
}
|
||||
try {
|
||||
return D.rpcImpl(B, Q[D.requestDelimited ? "encodeDelimited" : "encode"](G).finish(), function Y(W, J) {
|
||||
if (W) return D.emit("error", W, B), Z(W);
|
||||
if (J === null) {
|
||||
D.end(!0);
|
||||
return
|
||||
}
|
||||
if (!(J instanceof I)) try {
|
||||
J = I[D.responseDelimited ? "decodeDelimited" : "decode"](J)
|
||||
} catch (F) {
|
||||
return D.emit("error", F, B), Z(F)
|
||||
}
|
||||
return D.emit("data", J, B), Z(null, J)
|
||||
})
|
||||
} catch (Y) {
|
||||
D.emit("error", Y, B), setTimeout(function() {
|
||||
Z(Y)
|
||||
}, 0);
|
||||
return
|
||||
}
|
||||
};
|
||||
vs.prototype.end = function A(B) {
|
||||
if (this.rpcImpl) {
|
||||
if (!B) this.rpcImpl(null, null, null);
|
||||
this.rpcImpl = null, this.emit("end").off()
|
||||
}
|
||||
return this
|
||||
}
|
||||
})
|
||||
// @from(Start 4311985, End 4312045)
|
||||
gg1 = z((fO0) => {
|
||||
var x26 = fO0;
|
||||
x26.Service = xO0()
|
||||
})
|
||||
// @from(Start 4312051, End 4312096)
|
||||
hg1 = z((YN8, vO0) => {
|
||||
vO0.exports = {}
|
||||
})
|
||||
// @from(Start 4312102, End 4312472)
|
||||
mg1 = z((gO0) => {
|
||||
var dY = gO0;
|
||||
dY.build = "minimal";
|
||||
dY.Writer = yZ1();
|
||||
dY.BufferWriter = MO0();
|
||||
dY.Reader = xZ1();
|
||||
dY.BufferReader = yO0();
|
||||
dY.util = Iw();
|
||||
dY.rpc = gg1();
|
||||
dY.roots = hg1();
|
||||
dY.configure = bO0;
|
||||
|
||||
function bO0() {
|
||||
dY.util._configure(), dY.Writer._configure(dY.BufferWriter), dY.Reader._configure(dY.BufferReader)
|
||||
}
|
||||
bO0()
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,684 +0,0 @@
|
||||
|
||||
// @from(Start 650506, End 656272)
|
||||
vJA = z((fJA) => {
|
||||
Object.defineProperty(fJA, "__esModule", {
|
||||
value: !0
|
||||
});
|
||||
var d2 = z4(),
|
||||
lJ9 = vZA(),
|
||||
iJ9 = Qq1(),
|
||||
nJ9 = Zq1(),
|
||||
zl = Eq1(),
|
||||
Tq1 = rA(),
|
||||
aJ9 = RYA(),
|
||||
jJA = wq1(),
|
||||
sJ9 = kYA(),
|
||||
rJ9 = uYA(),
|
||||
oJ9 = oYA(),
|
||||
tJ9 = eYA(),
|
||||
WM = UJA(),
|
||||
eJ9 = S41(),
|
||||
AF9 = m41(),
|
||||
BF9 = u41(),
|
||||
QF9 = b41(),
|
||||
IF9 = y41(),
|
||||
GF9 = _41(),
|
||||
ZF9 = v41(),
|
||||
DF9 = p41(),
|
||||
YF9 = a41(),
|
||||
yJA = Mq1(),
|
||||
kJA = l41(),
|
||||
xJA = k41(),
|
||||
WF9 = qq1(),
|
||||
JF9 = RJA(),
|
||||
FF9 = PJA(),
|
||||
XF9 = _JA(),
|
||||
VF9 = jJA.createGetModuleFromFilename(),
|
||||
CF9 = {
|
||||
...d2.Integrations,
|
||||
...oJ9,
|
||||
...tJ9
|
||||
},
|
||||
KF9 = {
|
||||
instrumentCron: JF9.instrumentCron,
|
||||
instrumentNodeCron: FF9.instrumentNodeCron,
|
||||
instrumentNodeSchedule: XF9.instrumentNodeSchedule
|
||||
};
|
||||
fJA.Hub = d2.Hub;
|
||||
fJA.SDK_VERSION = d2.SDK_VERSION;
|
||||
fJA.SEMANTIC_ATTRIBUTE_SENTRY_OP = d2.SEMANTIC_ATTRIBUTE_SENTRY_OP;
|
||||
fJA.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN = d2.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN;
|
||||
fJA.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE = d2.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE;
|
||||
fJA.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE = d2.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE;
|
||||
fJA.Scope = d2.Scope;
|
||||
fJA.addBreadcrumb = d2.addBreadcrumb;
|
||||
fJA.addEventProcessor = d2.addEventProcessor;
|
||||
fJA.addGlobalEventProcessor = d2.addGlobalEventProcessor;
|
||||
fJA.addIntegration = d2.addIntegration;
|
||||
fJA.captureCheckIn = d2.captureCheckIn;
|
||||
fJA.captureEvent = d2.captureEvent;
|
||||
fJA.captureException = d2.captureException;
|
||||
fJA.captureMessage = d2.captureMessage;
|
||||
fJA.captureSession = d2.captureSession;
|
||||
fJA.close = d2.close;
|
||||
fJA.configureScope = d2.configureScope;
|
||||
fJA.continueTrace = d2.continueTrace;
|
||||
fJA.createTransport = d2.createTransport;
|
||||
fJA.endSession = d2.endSession;
|
||||
fJA.extractTraceparentData = d2.extractTraceparentData;
|
||||
fJA.flush = d2.flush;
|
||||
fJA.functionToStringIntegration = d2.functionToStringIntegration;
|
||||
fJA.getActiveSpan = d2.getActiveSpan;
|
||||
fJA.getActiveTransaction = d2.getActiveTransaction;
|
||||
fJA.getClient = d2.getClient;
|
||||
fJA.getCurrentHub = d2.getCurrentHub;
|
||||
fJA.getCurrentScope = d2.getCurrentScope;
|
||||
fJA.getGlobalScope = d2.getGlobalScope;
|
||||
fJA.getHubFromCarrier = d2.getHubFromCarrier;
|
||||
fJA.getIsolationScope = d2.getIsolationScope;
|
||||
fJA.getSpanStatusFromHttpCode = d2.getSpanStatusFromHttpCode;
|
||||
fJA.inboundFiltersIntegration = d2.inboundFiltersIntegration;
|
||||
fJA.isInitialized = d2.isInitialized;
|
||||
fJA.lastEventId = d2.lastEventId;
|
||||
fJA.linkedErrorsIntegration = d2.linkedErrorsIntegration;
|
||||
fJA.makeMain = d2.makeMain;
|
||||
fJA.metrics = d2.metrics;
|
||||
fJA.parameterize = d2.parameterize;
|
||||
fJA.requestDataIntegration = d2.requestDataIntegration;
|
||||
fJA.runWithAsyncContext = d2.runWithAsyncContext;
|
||||
fJA.setContext = d2.setContext;
|
||||
fJA.setCurrentClient = d2.setCurrentClient;
|
||||
fJA.setExtra = d2.setExtra;
|
||||
fJA.setExtras = d2.setExtras;
|
||||
fJA.setHttpStatus = d2.setHttpStatus;
|
||||
fJA.setMeasurement = d2.setMeasurement;
|
||||
fJA.setTag = d2.setTag;
|
||||
fJA.setTags = d2.setTags;
|
||||
fJA.setUser = d2.setUser;
|
||||
fJA.spanStatusfromHttpCode = d2.spanStatusfromHttpCode;
|
||||
fJA.startActiveSpan = d2.startActiveSpan;
|
||||
fJA.startInactiveSpan = d2.startInactiveSpan;
|
||||
fJA.startSession = d2.startSession;
|
||||
fJA.startSpan = d2.startSpan;
|
||||
fJA.startSpanManual = d2.startSpanManual;
|
||||
fJA.startTransaction = d2.startTransaction;
|
||||
fJA.trace = d2.trace;
|
||||
fJA.withActiveSpan = d2.withActiveSpan;
|
||||
fJA.withIsolationScope = d2.withIsolationScope;
|
||||
fJA.withMonitor = d2.withMonitor;
|
||||
fJA.withScope = d2.withScope;
|
||||
fJA.autoDiscoverNodePerformanceMonitoringIntegrations = lJ9.autoDiscoverNodePerformanceMonitoringIntegrations;
|
||||
fJA.NodeClient = iJ9.NodeClient;
|
||||
fJA.makeNodeTransport = nJ9.makeNodeTransport;
|
||||
fJA.defaultIntegrations = zl.defaultIntegrations;
|
||||
fJA.defaultStackParser = zl.defaultStackParser;
|
||||
fJA.getDefaultIntegrations = zl.getDefaultIntegrations;
|
||||
fJA.getSentryRelease = zl.getSentryRelease;
|
||||
fJA.init = zl.init;
|
||||
fJA.DEFAULT_USER_INCLUDES = Tq1.DEFAULT_USER_INCLUDES;
|
||||
fJA.addRequestDataToEvent = Tq1.addRequestDataToEvent;
|
||||
fJA.extractRequestData = Tq1.extractRequestData;
|
||||
fJA.deepReadDirSync = aJ9.deepReadDirSync;
|
||||
fJA.createGetModuleFromFilename = jJA.createGetModuleFromFilename;
|
||||
fJA.enableAnrDetection = sJ9.enableAnrDetection;
|
||||
fJA.Handlers = rJ9;
|
||||
fJA.captureConsoleIntegration = WM.captureConsoleIntegration;
|
||||
fJA.debugIntegration = WM.debugIntegration;
|
||||
fJA.dedupeIntegration = WM.dedupeIntegration;
|
||||
fJA.extraErrorDataIntegration = WM.extraErrorDataIntegration;
|
||||
fJA.httpClientIntegration = WM.httpClientIntegration;
|
||||
fJA.reportingObserverIntegration = WM.reportingObserverIntegration;
|
||||
fJA.rewriteFramesIntegration = WM.rewriteFramesIntegration;
|
||||
fJA.sessionTimingIntegration = WM.sessionTimingIntegration;
|
||||
fJA.consoleIntegration = eJ9.consoleIntegration;
|
||||
fJA.onUncaughtExceptionIntegration = AF9.onUncaughtExceptionIntegration;
|
||||
fJA.onUnhandledRejectionIntegration = BF9.onUnhandledRejectionIntegration;
|
||||
fJA.modulesIntegration = QF9.modulesIntegration;
|
||||
fJA.contextLinesIntegration = IF9.contextLinesIntegration;
|
||||
fJA.nodeContextIntegration = GF9.nodeContextIntegration;
|
||||
fJA.localVariablesIntegration = ZF9.localVariablesIntegration;
|
||||
fJA.spotlightIntegration = DF9.spotlightIntegration;
|
||||
fJA.anrIntegration = YF9.anrIntegration;
|
||||
fJA.hapiErrorPlugin = yJA.hapiErrorPlugin;
|
||||
fJA.hapiIntegration = yJA.hapiIntegration;
|
||||
fJA.Undici = kJA.Undici;
|
||||
fJA.nativeNodeFetchintegration = kJA.nativeNodeFetchintegration;
|
||||
fJA.Http = xJA.Http;
|
||||
fJA.httpIntegration = xJA.httpIntegration;
|
||||
fJA.trpcMiddleware = WF9.trpcMiddleware;
|
||||
fJA.Integrations = CF9;
|
||||
fJA.cron = KF9;
|
||||
fJA.getModuleFromFilename = VF9
|
||||
})
|
||||
// @from(Start 656278, End 665637)
|
||||
U1 = z((SV9) => {
|
||||
var wl = Symbol.for("react.element"),
|
||||
KV9 = Symbol.for("react.portal"),
|
||||
HV9 = Symbol.for("react.fragment"),
|
||||
zV9 = Symbol.for("react.strict_mode"),
|
||||
wV9 = Symbol.for("react.profiler"),
|
||||
EV9 = Symbol.for("react.provider"),
|
||||
UV9 = Symbol.for("react.context"),
|
||||
NV9 = Symbol.for("react.forward_ref"),
|
||||
$V9 = Symbol.for("react.suspense"),
|
||||
qV9 = Symbol.for("react.memo"),
|
||||
MV9 = Symbol.for("react.lazy"),
|
||||
hJA = Symbol.iterator;
|
||||
|
||||
function LV9(A) {
|
||||
if (A === null || typeof A !== "object") return null;
|
||||
return A = hJA && A[hJA] || A["@@iterator"], typeof A === "function" ? A : null
|
||||
}
|
||||
var uJA = {
|
||||
isMounted: function() {
|
||||
return !1
|
||||
},
|
||||
enqueueForceUpdate: function() {},
|
||||
enqueueReplaceState: function() {},
|
||||
enqueueSetState: function() {}
|
||||
},
|
||||
pJA = Object.assign,
|
||||
cJA = {};
|
||||
|
||||
function vx(A, B, Q) {
|
||||
this.props = A, this.context = B, this.refs = cJA, this.updater = Q || uJA
|
||||
}
|
||||
vx.prototype.isReactComponent = {};
|
||||
vx.prototype.setState = function(A, B) {
|
||||
if (typeof A !== "object" && typeof A !== "function" && A != null) throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");
|
||||
this.updater.enqueueSetState(this, A, B, "setState")
|
||||
};
|
||||
vx.prototype.forceUpdate = function(A) {
|
||||
this.updater.enqueueForceUpdate(this, A, "forceUpdate")
|
||||
};
|
||||
|
||||
function lJA() {}
|
||||
lJA.prototype = vx.prototype;
|
||||
|
||||
function Sq1(A, B, Q) {
|
||||
this.props = A, this.context = B, this.refs = cJA, this.updater = Q || uJA
|
||||
}
|
||||
var _q1 = Sq1.prototype = new lJA;
|
||||
_q1.constructor = Sq1;
|
||||
pJA(_q1, vx.prototype);
|
||||
_q1.isPureReactComponent = !0;
|
||||
var mJA = Array.isArray,
|
||||
iJA = Object.prototype.hasOwnProperty,
|
||||
jq1 = {
|
||||
current: null
|
||||
},
|
||||
nJA = {
|
||||
key: !0,
|
||||
ref: !0,
|
||||
__self: !0,
|
||||
__source: !0
|
||||
};
|
||||
|
||||
function aJA(A, B, Q) {
|
||||
var I, G = {},
|
||||
Z = null,
|
||||
D = null;
|
||||
if (B != null)
|
||||
for (I in B.ref !== void 0 && (D = B.ref), B.key !== void 0 && (Z = "" + B.key), B) iJA.call(B, I) && !nJA.hasOwnProperty(I) && (G[I] = B[I]);
|
||||
var Y = arguments.length - 2;
|
||||
if (Y === 1) G.children = Q;
|
||||
else if (1 < Y) {
|
||||
for (var W = Array(Y), J = 0; J < Y; J++) W[J] = arguments[J + 2];
|
||||
G.children = W
|
||||
}
|
||||
if (A && A.defaultProps)
|
||||
for (I in Y = A.defaultProps, Y) G[I] === void 0 && (G[I] = Y[I]);
|
||||
return {
|
||||
$$typeof: wl,
|
||||
type: A,
|
||||
key: Z,
|
||||
ref: D,
|
||||
props: G,
|
||||
_owner: jq1.current
|
||||
}
|
||||
}
|
||||
|
||||
function RV9(A, B) {
|
||||
return {
|
||||
$$typeof: wl,
|
||||
type: A.type,
|
||||
key: B,
|
||||
ref: A.ref,
|
||||
props: A.props,
|
||||
_owner: A._owner
|
||||
}
|
||||
}
|
||||
|
||||
function yq1(A) {
|
||||
return typeof A === "object" && A !== null && A.$$typeof === wl
|
||||
}
|
||||
|
||||
function OV9(A) {
|
||||
var B = {
|
||||
"=": "=0",
|
||||
":": "=2"
|
||||
};
|
||||
return "$" + A.replace(/[=:]/g, function(Q) {
|
||||
return B[Q]
|
||||
})
|
||||
}
|
||||
var dJA = /\/+/g;
|
||||
|
||||
function Pq1(A, B) {
|
||||
return typeof A === "object" && A !== null && A.key != null ? OV9("" + A.key) : B.toString(36)
|
||||
}
|
||||
|
||||
function A61(A, B, Q, I, G) {
|
||||
var Z = typeof A;
|
||||
if (Z === "undefined" || Z === "boolean") A = null;
|
||||
var D = !1;
|
||||
if (A === null) D = !0;
|
||||
else switch (Z) {
|
||||
case "string":
|
||||
case "number":
|
||||
D = !0;
|
||||
break;
|
||||
case "object":
|
||||
switch (A.$$typeof) {
|
||||
case wl:
|
||||
case KV9:
|
||||
D = !0
|
||||
}
|
||||
}
|
||||
if (D) return D = A, G = G(D), A = I === "" ? "." + Pq1(D, 0) : I, mJA(G) ? (Q = "", A != null && (Q = A.replace(dJA, "$&/") + "/"), A61(G, B, Q, "", function(J) {
|
||||
return J
|
||||
})) : G != null && (yq1(G) && (G = RV9(G, Q + (!G.key || D && D.key === G.key ? "" : ("" + G.key).replace(dJA, "$&/") + "/") + A)), B.push(G)), 1;
|
||||
if (D = 0, I = I === "" ? "." : I + ":", mJA(A))
|
||||
for (var Y = 0; Y < A.length; Y++) {
|
||||
Z = A[Y];
|
||||
var W = I + Pq1(Z, Y);
|
||||
D += A61(Z, B, Q, W, G)
|
||||
} else if (W = LV9(A), typeof W === "function")
|
||||
for (A = W.call(A), Y = 0; !(Z = A.next()).done;) Z = Z.value, W = I + Pq1(Z, Y++), D += A61(Z, B, Q, W, G);
|
||||
else if (Z === "object") throw B = String(A), Error("Objects are not valid as a React child (found: " + (B === "[object Object]" ? "object with keys {" + Object.keys(A).join(", ") + "}" : B) + "). If you meant to render a collection of children, use an array instead.");
|
||||
return D
|
||||
}
|
||||
|
||||
function e41(A, B, Q) {
|
||||
if (A == null) return A;
|
||||
var I = [],
|
||||
G = 0;
|
||||
return A61(A, I, "", "", function(Z) {
|
||||
return B.call(Q, Z, G++)
|
||||
}), I
|
||||
}
|
||||
|
||||
function TV9(A) {
|
||||
if (A._status === -1) {
|
||||
var B = A._result;
|
||||
B = B(), B.then(function(Q) {
|
||||
if (A._status === 0 || A._status === -1) A._status = 1, A._result = Q
|
||||
}, function(Q) {
|
||||
if (A._status === 0 || A._status === -1) A._status = 2, A._result = Q
|
||||
}), A._status === -1 && (A._status = 0, A._result = B)
|
||||
}
|
||||
if (A._status === 1) return A._result.default;
|
||||
throw A._result
|
||||
}
|
||||
var $D = {
|
||||
current: null
|
||||
},
|
||||
B61 = {
|
||||
transition: null
|
||||
},
|
||||
PV9 = {
|
||||
ReactCurrentDispatcher: $D,
|
||||
ReactCurrentBatchConfig: B61,
|
||||
ReactCurrentOwner: jq1
|
||||
};
|
||||
|
||||
function sJA() {
|
||||
throw Error("act(...) is not supported in production builds of React.")
|
||||
}
|
||||
SV9.Children = {
|
||||
map: e41,
|
||||
forEach: function(A, B, Q) {
|
||||
e41(A, function() {
|
||||
B.apply(this, arguments)
|
||||
}, Q)
|
||||
},
|
||||
count: function(A) {
|
||||
var B = 0;
|
||||
return e41(A, function() {
|
||||
B++
|
||||
}), B
|
||||
},
|
||||
toArray: function(A) {
|
||||
return e41(A, function(B) {
|
||||
return B
|
||||
}) || []
|
||||
},
|
||||
only: function(A) {
|
||||
if (!yq1(A)) throw Error("React.Children.only expected to receive a single React element child.");
|
||||
return A
|
||||
}
|
||||
};
|
||||
SV9.Component = vx;
|
||||
SV9.Fragment = HV9;
|
||||
SV9.Profiler = wV9;
|
||||
SV9.PureComponent = Sq1;
|
||||
SV9.StrictMode = zV9;
|
||||
SV9.Suspense = $V9;
|
||||
SV9.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = PV9;
|
||||
SV9.act = sJA;
|
||||
SV9.cloneElement = function(A, B, Q) {
|
||||
if (A === null || A === void 0) throw Error("React.cloneElement(...): The argument must be a React element, but you passed " + A + ".");
|
||||
var I = pJA({}, A.props),
|
||||
G = A.key,
|
||||
Z = A.ref,
|
||||
D = A._owner;
|
||||
if (B != null) {
|
||||
if (B.ref !== void 0 && (Z = B.ref, D = jq1.current), B.key !== void 0 && (G = "" + B.key), A.type && A.type.defaultProps) var Y = A.type.defaultProps;
|
||||
for (W in B) iJA.call(B, W) && !nJA.hasOwnProperty(W) && (I[W] = B[W] === void 0 && Y !== void 0 ? Y[W] : B[W])
|
||||
}
|
||||
var W = arguments.length - 2;
|
||||
if (W === 1) I.children = Q;
|
||||
else if (1 < W) {
|
||||
Y = Array(W);
|
||||
for (var J = 0; J < W; J++) Y[J] = arguments[J + 2];
|
||||
I.children = Y
|
||||
}
|
||||
return {
|
||||
$$typeof: wl,
|
||||
type: A.type,
|
||||
key: G,
|
||||
ref: Z,
|
||||
props: I,
|
||||
_owner: D
|
||||
}
|
||||
};
|
||||
SV9.createContext = function(A) {
|
||||
return A = {
|
||||
$$typeof: UV9,
|
||||
_currentValue: A,
|
||||
_currentValue2: A,
|
||||
_threadCount: 0,
|
||||
Provider: null,
|
||||
Consumer: null,
|
||||
_defaultValue: null,
|
||||
_globalName: null
|
||||
}, A.Provider = {
|
||||
$$typeof: EV9,
|
||||
_context: A
|
||||
}, A.Consumer = A
|
||||
};
|
||||
SV9.createElement = aJA;
|
||||
SV9.createFactory = function(A) {
|
||||
var B = aJA.bind(null, A);
|
||||
return B.type = A, B
|
||||
};
|
||||
SV9.createRef = function() {
|
||||
return {
|
||||
current: null
|
||||
}
|
||||
};
|
||||
SV9.forwardRef = function(A) {
|
||||
return {
|
||||
$$typeof: NV9,
|
||||
render: A
|
||||
}
|
||||
};
|
||||
SV9.isValidElement = yq1;
|
||||
SV9.lazy = function(A) {
|
||||
return {
|
||||
$$typeof: MV9,
|
||||
_payload: {
|
||||
_status: -1,
|
||||
_result: A
|
||||
},
|
||||
_init: TV9
|
||||
}
|
||||
};
|
||||
SV9.memo = function(A, B) {
|
||||
return {
|
||||
$$typeof: qV9,
|
||||
type: A,
|
||||
compare: B === void 0 ? null : B
|
||||
}
|
||||
};
|
||||
SV9.startTransition = function(A) {
|
||||
var B = B61.transition;
|
||||
B61.transition = {};
|
||||
try {
|
||||
A()
|
||||
} finally {
|
||||
B61.transition = B
|
||||
}
|
||||
};
|
||||
SV9.unstable_act = sJA;
|
||||
SV9.useCallback = function(A, B) {
|
||||
return $D.current.useCallback(A, B)
|
||||
};
|
||||
SV9.useContext = function(A) {
|
||||
return $D.current.useContext(A)
|
||||
};
|
||||
SV9.useDebugValue = function() {};
|
||||
SV9.useDeferredValue = function(A) {
|
||||
return $D.current.useDeferredValue(A)
|
||||
};
|
||||
SV9.useEffect = function(A, B) {
|
||||
return $D.current.useEffect(A, B)
|
||||
};
|
||||
SV9.useId = function() {
|
||||
return $D.current.useId()
|
||||
};
|
||||
SV9.useImperativeHandle = function(A, B, Q) {
|
||||
return $D.current.useImperativeHandle(A, B, Q)
|
||||
};
|
||||
SV9.useInsertionEffect = function(A, B) {
|
||||
return $D.current.useInsertionEffect(A, B)
|
||||
};
|
||||
SV9.useLayoutEffect = function(A, B) {
|
||||
return $D.current.useLayoutEffect(A, B)
|
||||
};
|
||||
SV9.useMemo = function(A, B) {
|
||||
return $D.current.useMemo(A, B)
|
||||
};
|
||||
SV9.useReducer = function(A, B, Q) {
|
||||
return $D.current.useReducer(A, B, Q)
|
||||
};
|
||||
SV9.useRef = function(A) {
|
||||
return $D.current.useRef(A)
|
||||
};
|
||||
SV9.useState = function(A) {
|
||||
return $D.current.useState(A)
|
||||
};
|
||||
SV9.useSyncExternalStore = function(A, B, Q) {
|
||||
return $D.current.useSyncExternalStore(A, B, Q)
|
||||
};
|
||||
SV9.useTransition = function() {
|
||||
return $D.current.useTransition()
|
||||
};
|
||||
SV9.version = "18.3.1"
|
||||
})
|
||||
// @from(Start 665643, End 667603)
|
||||
YFA = z((Tg5, DFA) => {
|
||||
var ZFA = Z1("stream").Stream,
|
||||
rC9 = Z1("util");
|
||||
DFA.exports = QC;
|
||||
|
||||
function QC() {
|
||||
this.source = null, this.dataSize = 0, this.maxDataSize = 1048576, this.pauseStream = !0, this._maxDataSizeExceeded = !1, this._released = !1, this._bufferedEvents = []
|
||||
}
|
||||
rC9.inherits(QC, ZFA);
|
||||
QC.create = function(A, B) {
|
||||
var Q = new this;
|
||||
B = B || {};
|
||||
for (var I in B) Q[I] = B[I];
|
||||
Q.source = A;
|
||||
var G = A.emit;
|
||||
if (A.emit = function() {
|
||||
return Q._handleEmit(arguments), G.apply(A, arguments)
|
||||
}, A.on("error", function() {}), Q.pauseStream) A.pause();
|
||||
return Q
|
||||
};
|
||||
Object.defineProperty(QC.prototype, "readable", {
|
||||
configurable: !0,
|
||||
enumerable: !0,
|
||||
get: function() {
|
||||
return this.source.readable
|
||||
}
|
||||
});
|
||||
QC.prototype.setEncoding = function() {
|
||||
return this.source.setEncoding.apply(this.source, arguments)
|
||||
};
|
||||
QC.prototype.resume = function() {
|
||||
if (!this._released) this.release();
|
||||
this.source.resume()
|
||||
};
|
||||
QC.prototype.pause = function() {
|
||||
this.source.pause()
|
||||
};
|
||||
QC.prototype.release = function() {
|
||||
this._released = !0, this._bufferedEvents.forEach(function(A) {
|
||||
this.emit.apply(this, A)
|
||||
}.bind(this)), this._bufferedEvents = []
|
||||
};
|
||||
QC.prototype.pipe = function() {
|
||||
var A = ZFA.prototype.pipe.apply(this, arguments);
|
||||
return this.resume(), A
|
||||
};
|
||||
QC.prototype._handleEmit = function(A) {
|
||||
if (this._released) {
|
||||
this.emit.apply(this, A);
|
||||
return
|
||||
}
|
||||
if (A[0] === "data") this.dataSize += A[1].length, this._checkIfMaxDataSizeExceeded();
|
||||
this._bufferedEvents.push(A)
|
||||
};
|
||||
QC.prototype._checkIfMaxDataSizeExceeded = function() {
|
||||
if (this._maxDataSizeExceeded) return;
|
||||
if (this.dataSize <= this.maxDataSize) return;
|
||||
this._maxDataSizeExceeded = !0;
|
||||
var A = "DelayedStream#maxDataSize of " + this.maxDataSize + " bytes exceeded.";
|
||||
this.emit("error", new Error(A))
|
||||
}
|
||||
})
|
||||
// @from(Start 667609, End 671451)
|
||||
XFA = z((Pg5, FFA) => {
|
||||
var oC9 = Z1("util"),
|
||||
JFA = Z1("stream").Stream,
|
||||
WFA = YFA();
|
||||
FFA.exports = m3;
|
||||
|
||||
function m3() {
|
||||
this.writable = !1, this.readable = !0, this.dataSize = 0, this.maxDataSize = 2097152, this.pauseStreams = !0, this._released = !1, this._streams = [], this._currentStream = null, this._insideLoop = !1, this._pendingNext = !1
|
||||
}
|
||||
oC9.inherits(m3, JFA);
|
||||
m3.create = function(A) {
|
||||
var B = new this;
|
||||
A = A || {};
|
||||
for (var Q in A) B[Q] = A[Q];
|
||||
return B
|
||||
};
|
||||
m3.isStreamLike = function(A) {
|
||||
return typeof A !== "function" && typeof A !== "string" && typeof A !== "boolean" && typeof A !== "number" && !Buffer.isBuffer(A)
|
||||
};
|
||||
m3.prototype.append = function(A) {
|
||||
var B = m3.isStreamLike(A);
|
||||
if (B) {
|
||||
if (!(A instanceof WFA)) {
|
||||
var Q = WFA.create(A, {
|
||||
maxDataSize: 1 / 0,
|
||||
pauseStream: this.pauseStreams
|
||||
});
|
||||
A.on("data", this._checkDataSize.bind(this)), A = Q
|
||||
}
|
||||
if (this._handleErrors(A), this.pauseStreams) A.pause()
|
||||
}
|
||||
return this._streams.push(A), this
|
||||
};
|
||||
m3.prototype.pipe = function(A, B) {
|
||||
return JFA.prototype.pipe.call(this, A, B), this.resume(), A
|
||||
};
|
||||
m3.prototype._getNext = function() {
|
||||
if (this._currentStream = null, this._insideLoop) {
|
||||
this._pendingNext = !0;
|
||||
return
|
||||
}
|
||||
this._insideLoop = !0;
|
||||
try {
|
||||
do this._pendingNext = !1, this._realGetNext(); while (this._pendingNext)
|
||||
} finally {
|
||||
this._insideLoop = !1
|
||||
}
|
||||
};
|
||||
m3.prototype._realGetNext = function() {
|
||||
var A = this._streams.shift();
|
||||
if (typeof A == "undefined") {
|
||||
this.end();
|
||||
return
|
||||
}
|
||||
if (typeof A !== "function") {
|
||||
this._pipeNext(A);
|
||||
return
|
||||
}
|
||||
var B = A;
|
||||
B(function(Q) {
|
||||
var I = m3.isStreamLike(Q);
|
||||
if (I) Q.on("data", this._checkDataSize.bind(this)), this._handleErrors(Q);
|
||||
this._pipeNext(Q)
|
||||
}.bind(this))
|
||||
};
|
||||
m3.prototype._pipeNext = function(A) {
|
||||
this._currentStream = A;
|
||||
var B = m3.isStreamLike(A);
|
||||
if (B) {
|
||||
A.on("end", this._getNext.bind(this)), A.pipe(this, {
|
||||
end: !1
|
||||
});
|
||||
return
|
||||
}
|
||||
var Q = A;
|
||||
this.write(Q), this._getNext()
|
||||
};
|
||||
m3.prototype._handleErrors = function(A) {
|
||||
var B = this;
|
||||
A.on("error", function(Q) {
|
||||
B._emitError(Q)
|
||||
})
|
||||
};
|
||||
m3.prototype.write = function(A) {
|
||||
this.emit("data", A)
|
||||
};
|
||||
m3.prototype.pause = function() {
|
||||
if (!this.pauseStreams) return;
|
||||
if (this.pauseStreams && this._currentStream && typeof this._currentStream.pause == "function") this._currentStream.pause();
|
||||
this.emit("pause")
|
||||
};
|
||||
m3.prototype.resume = function() {
|
||||
if (!this._released) this._released = !0, this.writable = !0, this._getNext();
|
||||
if (this.pauseStreams && this._currentStream && typeof this._currentStream.resume == "function") this._currentStream.resume();
|
||||
this.emit("resume")
|
||||
};
|
||||
m3.prototype.end = function() {
|
||||
this._reset(), this.emit("end")
|
||||
};
|
||||
m3.prototype.destroy = function() {
|
||||
this._reset(), this.emit("close")
|
||||
};
|
||||
m3.prototype._reset = function() {
|
||||
this.writable = !1, this._streams = [], this._currentStream = null
|
||||
};
|
||||
m3.prototype._checkDataSize = function() {
|
||||
if (this._updateDataSize(), this.dataSize <= this.maxDataSize) return;
|
||||
var A = "DelayedStream#maxDataSize of " + this.maxDataSize + " bytes exceeded.";
|
||||
this._emitError(new Error(A))
|
||||
};
|
||||
m3.prototype._updateDataSize = function() {
|
||||
this.dataSize = 0;
|
||||
var A = this;
|
||||
if (this._streams.forEach(function(B) {
|
||||
if (!B.dataSize) return;
|
||||
A.dataSize += B.dataSize
|
||||
}), this._currentStream && this._currentStream.dataSize) this.dataSize += this._currentStream.dataSize
|
||||
};
|
||||
m3.prototype._emitError = function(A) {
|
||||
this._reset(), this.emit("error", A)
|
||||
}
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,568 +0,0 @@
|
||||
|
||||
// @from(Start 7655730, End 7667300)
|
||||
m$2 = z((g$2, h$2) => {
|
||||
(function() {
|
||||
var A, B, Q, I, G, Z, D, Y, W, J, F, X, V, C, K, E, N, q, O, R, T, L, _, k = {}.hasOwnProperty;
|
||||
({
|
||||
isObject: L,
|
||||
isFunction: T,
|
||||
isPlainObject: _,
|
||||
getValue: R
|
||||
} = HE()), A = mQ(), X = ae1(), C = vH1(), I = bH1(), G = gH1(), E = lH1(), O = iH1(), K = nH1(), J = hH1(), F = cH1(), Z = mH1(), Y = dH1(), D = uH1(), W = pH1(), Q = ce1(), q = ie1(), N = aH1(), B = IA1(), h$2.exports = V = class i {
|
||||
constructor(x, s, d) {
|
||||
var F1;
|
||||
if (this.name = "?xml", this.type = A.Document, x || (x = {}), F1 = {}, !x.writer) x.writer = new N;
|
||||
else if (_(x.writer)) F1 = x.writer, x.writer = new N;
|
||||
this.options = x, this.writer = x.writer, this.writerOptions = this.writer.filterOptions(F1), this.stringify = new q(x), this.onDataCallback = s || function() {}, this.onEndCallback = d || function() {}, this.currentNode = null, this.currentLevel = -1, this.openTags = {}, this.documentStarted = !1, this.documentCompleted = !1, this.root = null
|
||||
}
|
||||
createChildNode(x) {
|
||||
var s, d, F1, X1, v, D1, N1, u1;
|
||||
switch (x.type) {
|
||||
case A.CData:
|
||||
this.cdata(x.value);
|
||||
break;
|
||||
case A.Comment:
|
||||
this.comment(x.value);
|
||||
break;
|
||||
case A.Element:
|
||||
F1 = {}, N1 = x.attribs;
|
||||
for (d in N1) {
|
||||
if (!k.call(N1, d)) continue;
|
||||
s = N1[d], F1[d] = s.value
|
||||
}
|
||||
this.node(x.name, F1);
|
||||
break;
|
||||
case A.Dummy:
|
||||
this.dummy();
|
||||
break;
|
||||
case A.Raw:
|
||||
this.raw(x.value);
|
||||
break;
|
||||
case A.Text:
|
||||
this.text(x.value);
|
||||
break;
|
||||
case A.ProcessingInstruction:
|
||||
this.instruction(x.target, x.value);
|
||||
break;
|
||||
default:
|
||||
throw new Error("This XML node type is not supported in a JS object: " + x.constructor.name)
|
||||
}
|
||||
u1 = x.children;
|
||||
for (v = 0, D1 = u1.length; v < D1; v++)
|
||||
if (X1 = u1[v], this.createChildNode(X1), X1.type === A.Element) this.up();
|
||||
return this
|
||||
}
|
||||
dummy() {
|
||||
return this
|
||||
}
|
||||
node(x, s, d) {
|
||||
if (x == null) throw new Error("Missing node name.");
|
||||
if (this.root && this.currentLevel === -1) throw new Error("Document can only have one root node. " + this.debugInfo(x));
|
||||
if (this.openCurrent(), x = R(x), s == null) s = {};
|
||||
if (s = R(s), !L(s))[d, s] = [s, d];
|
||||
if (this.currentNode = new C(this, x, s), this.currentNode.children = !1, this.currentLevel++, this.openTags[this.currentLevel] = this.currentNode, d != null) this.text(d);
|
||||
return this
|
||||
}
|
||||
element(x, s, d) {
|
||||
var F1, X1, v, D1, N1, u1;
|
||||
if (this.currentNode && this.currentNode.type === A.DocType) this.dtdElement(...arguments);
|
||||
else if (Array.isArray(x) || L(x) || T(x)) {
|
||||
D1 = this.options.noValidation, this.options.noValidation = !0, u1 = new X(this.options).element("TEMP_ROOT"), u1.element(x), this.options.noValidation = D1, N1 = u1.children;
|
||||
for (X1 = 0, v = N1.length; X1 < v; X1++)
|
||||
if (F1 = N1[X1], this.createChildNode(F1), F1.type === A.Element) this.up()
|
||||
} else this.node(x, s, d);
|
||||
return this
|
||||
}
|
||||
attribute(x, s) {
|
||||
var d, F1;
|
||||
if (!this.currentNode || this.currentNode.children) throw new Error("att() can only be used immediately after an ele() call in callback mode. " + this.debugInfo(x));
|
||||
if (x != null) x = R(x);
|
||||
if (L(x))
|
||||
for (d in x) {
|
||||
if (!k.call(x, d)) continue;
|
||||
F1 = x[d], this.attribute(d, F1)
|
||||
} else {
|
||||
if (T(s)) s = s.apply();
|
||||
if (this.options.keepNullAttributes && s == null) this.currentNode.attribs[x] = new Q(this, x, "");
|
||||
else if (s != null) this.currentNode.attribs[x] = new Q(this, x, s)
|
||||
}
|
||||
return this
|
||||
}
|
||||
text(x) {
|
||||
var s;
|
||||
return this.openCurrent(), s = new O(this, x), this.onData(this.writer.text(s, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
cdata(x) {
|
||||
var s;
|
||||
return this.openCurrent(), s = new I(this, x), this.onData(this.writer.cdata(s, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
comment(x) {
|
||||
var s;
|
||||
return this.openCurrent(), s = new G(this, x), this.onData(this.writer.comment(s, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
raw(x) {
|
||||
var s;
|
||||
return this.openCurrent(), s = new E(this, x), this.onData(this.writer.raw(s, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
instruction(x, s) {
|
||||
var d, F1, X1, v, D1;
|
||||
if (this.openCurrent(), x != null) x = R(x);
|
||||
if (s != null) s = R(s);
|
||||
if (Array.isArray(x))
|
||||
for (d = 0, v = x.length; d < v; d++) F1 = x[d], this.instruction(F1);
|
||||
else if (L(x))
|
||||
for (F1 in x) {
|
||||
if (!k.call(x, F1)) continue;
|
||||
X1 = x[F1], this.instruction(F1, X1)
|
||||
} else {
|
||||
if (T(s)) s = s.apply();
|
||||
D1 = new K(this, x, s), this.onData(this.writer.processingInstruction(D1, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1)
|
||||
}
|
||||
return this
|
||||
}
|
||||
declaration(x, s, d) {
|
||||
var F1;
|
||||
if (this.openCurrent(), this.documentStarted) throw new Error("declaration() must be the first node.");
|
||||
return F1 = new J(this, x, s, d), this.onData(this.writer.declaration(F1, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
doctype(x, s, d) {
|
||||
if (this.openCurrent(), x == null) throw new Error("Missing root node name.");
|
||||
if (this.root) throw new Error("dtd() must come before the root node.");
|
||||
return this.currentNode = new F(this, s, d), this.currentNode.rootNodeName = x, this.currentNode.children = !1, this.currentLevel++, this.openTags[this.currentLevel] = this.currentNode, this
|
||||
}
|
||||
dtdElement(x, s) {
|
||||
var d;
|
||||
return this.openCurrent(), d = new D(this, x, s), this.onData(this.writer.dtdElement(d, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
attList(x, s, d, F1, X1) {
|
||||
var v;
|
||||
return this.openCurrent(), v = new Z(this, x, s, d, F1, X1), this.onData(this.writer.dtdAttList(v, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
entity(x, s) {
|
||||
var d;
|
||||
return this.openCurrent(), d = new Y(this, !1, x, s), this.onData(this.writer.dtdEntity(d, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
pEntity(x, s) {
|
||||
var d;
|
||||
return this.openCurrent(), d = new Y(this, !0, x, s), this.onData(this.writer.dtdEntity(d, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
notation(x, s) {
|
||||
var d;
|
||||
return this.openCurrent(), d = new W(this, x, s), this.onData(this.writer.dtdNotation(d, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1), this
|
||||
}
|
||||
up() {
|
||||
if (this.currentLevel < 0) throw new Error("The document node has no parent.");
|
||||
if (this.currentNode) {
|
||||
if (this.currentNode.children) this.closeNode(this.currentNode);
|
||||
else this.openNode(this.currentNode);
|
||||
this.currentNode = null
|
||||
} else this.closeNode(this.openTags[this.currentLevel]);
|
||||
return delete this.openTags[this.currentLevel], this.currentLevel--, this
|
||||
}
|
||||
end() {
|
||||
while (this.currentLevel >= 0) this.up();
|
||||
return this.onEnd()
|
||||
}
|
||||
openCurrent() {
|
||||
if (this.currentNode) return this.currentNode.children = !0, this.openNode(this.currentNode)
|
||||
}
|
||||
openNode(x) {
|
||||
var s, d, F1, X1;
|
||||
if (!x.isOpen) {
|
||||
if (!this.root && this.currentLevel === 0 && x.type === A.Element) this.root = x;
|
||||
if (d = "", x.type === A.Element) {
|
||||
this.writerOptions.state = B.OpenTag, d = this.writer.indent(x, this.writerOptions, this.currentLevel) + "<" + x.name, X1 = x.attribs;
|
||||
for (F1 in X1) {
|
||||
if (!k.call(X1, F1)) continue;
|
||||
s = X1[F1], d += this.writer.attribute(s, this.writerOptions, this.currentLevel)
|
||||
}
|
||||
d += (x.children ? ">" : "/>") + this.writer.endline(x, this.writerOptions, this.currentLevel), this.writerOptions.state = B.InsideTag
|
||||
} else {
|
||||
if (this.writerOptions.state = B.OpenTag, d = this.writer.indent(x, this.writerOptions, this.currentLevel) + "<!DOCTYPE " + x.rootNodeName, x.pubID && x.sysID) d += ' PUBLIC "' + x.pubID + '" "' + x.sysID + '"';
|
||||
else if (x.sysID) d += ' SYSTEM "' + x.sysID + '"';
|
||||
if (x.children) d += " [", this.writerOptions.state = B.InsideTag;
|
||||
else this.writerOptions.state = B.CloseTag, d += ">";
|
||||
d += this.writer.endline(x, this.writerOptions, this.currentLevel)
|
||||
}
|
||||
return this.onData(d, this.currentLevel), x.isOpen = !0
|
||||
}
|
||||
}
|
||||
closeNode(x) {
|
||||
var s;
|
||||
if (!x.isClosed) {
|
||||
if (s = "", this.writerOptions.state = B.CloseTag, x.type === A.Element) s = this.writer.indent(x, this.writerOptions, this.currentLevel) + "</" + x.name + ">" + this.writer.endline(x, this.writerOptions, this.currentLevel);
|
||||
else s = this.writer.indent(x, this.writerOptions, this.currentLevel) + "]>" + this.writer.endline(x, this.writerOptions, this.currentLevel);
|
||||
return this.writerOptions.state = B.None, this.onData(s, this.currentLevel), x.isClosed = !0
|
||||
}
|
||||
}
|
||||
onData(x, s) {
|
||||
return this.documentStarted = !0, this.onDataCallback(x, s + 1)
|
||||
}
|
||||
onEnd() {
|
||||
return this.documentCompleted = !0, this.onEndCallback()
|
||||
}
|
||||
debugInfo(x) {
|
||||
if (x == null) return "";
|
||||
else return "node: <" + x + ">"
|
||||
}
|
||||
ele() {
|
||||
return this.element(...arguments)
|
||||
}
|
||||
nod(x, s, d) {
|
||||
return this.node(x, s, d)
|
||||
}
|
||||
txt(x) {
|
||||
return this.text(x)
|
||||
}
|
||||
dat(x) {
|
||||
return this.cdata(x)
|
||||
}
|
||||
com(x) {
|
||||
return this.comment(x)
|
||||
}
|
||||
ins(x, s) {
|
||||
return this.instruction(x, s)
|
||||
}
|
||||
dec(x, s, d) {
|
||||
return this.declaration(x, s, d)
|
||||
}
|
||||
dtd(x, s, d) {
|
||||
return this.doctype(x, s, d)
|
||||
}
|
||||
e(x, s, d) {
|
||||
return this.element(x, s, d)
|
||||
}
|
||||
n(x, s, d) {
|
||||
return this.node(x, s, d)
|
||||
}
|
||||
t(x) {
|
||||
return this.text(x)
|
||||
}
|
||||
d(x) {
|
||||
return this.cdata(x)
|
||||
}
|
||||
c(x) {
|
||||
return this.comment(x)
|
||||
}
|
||||
r(x) {
|
||||
return this.raw(x)
|
||||
}
|
||||
i(x, s) {
|
||||
return this.instruction(x, s)
|
||||
}
|
||||
att() {
|
||||
if (this.currentNode && this.currentNode.type === A.DocType) return this.attList(...arguments);
|
||||
else return this.attribute(...arguments)
|
||||
}
|
||||
a() {
|
||||
if (this.currentNode && this.currentNode.type === A.DocType) return this.attList(...arguments);
|
||||
else return this.attribute(...arguments)
|
||||
}
|
||||
ent(x, s) {
|
||||
return this.entity(x, s)
|
||||
}
|
||||
pent(x, s) {
|
||||
return this.pEntity(x, s)
|
||||
}
|
||||
not(x, s) {
|
||||
return this.notation(x, s)
|
||||
}
|
||||
}
|
||||
}).call(g$2)
|
||||
})
|
||||
// @from(Start 7667306, End 7671808)
|
||||
p$2 = z((d$2, u$2) => {
|
||||
(function() {
|
||||
var A, B, Q, I, G = {}.hasOwnProperty;
|
||||
A = mQ(), I = ne1(), B = IA1(), u$2.exports = Q = class Z extends I {
|
||||
constructor(D, Y) {
|
||||
super(Y);
|
||||
this.stream = D
|
||||
}
|
||||
endline(D, Y, W) {
|
||||
if (D.isLastRootNode && Y.state === B.CloseTag) return "";
|
||||
else return super.endline(D, Y, W)
|
||||
}
|
||||
document(D, Y) {
|
||||
var W, J, F, X, V, C, K, E, N;
|
||||
K = D.children;
|
||||
for (J = F = 0, V = K.length; F < V; J = ++F) W = K[J], W.isLastRootNode = J === D.children.length - 1;
|
||||
Y = this.filterOptions(Y), E = D.children, N = [];
|
||||
for (X = 0, C = E.length; X < C; X++) W = E[X], N.push(this.writeChildNode(W, Y, 0));
|
||||
return N
|
||||
}
|
||||
cdata(D, Y, W) {
|
||||
return this.stream.write(super.cdata(D, Y, W))
|
||||
}
|
||||
comment(D, Y, W) {
|
||||
return this.stream.write(super.comment(D, Y, W))
|
||||
}
|
||||
declaration(D, Y, W) {
|
||||
return this.stream.write(super.declaration(D, Y, W))
|
||||
}
|
||||
docType(D, Y, W) {
|
||||
var J, F, X, V;
|
||||
if (W || (W = 0), this.openNode(D, Y, W), Y.state = B.OpenTag, this.stream.write(this.indent(D, Y, W)), this.stream.write("<!DOCTYPE " + D.root().name), D.pubID && D.sysID) this.stream.write(' PUBLIC "' + D.pubID + '" "' + D.sysID + '"');
|
||||
else if (D.sysID) this.stream.write(' SYSTEM "' + D.sysID + '"');
|
||||
if (D.children.length > 0) {
|
||||
this.stream.write(" ["), this.stream.write(this.endline(D, Y, W)), Y.state = B.InsideTag, V = D.children;
|
||||
for (F = 0, X = V.length; F < X; F++) J = V[F], this.writeChildNode(J, Y, W + 1);
|
||||
Y.state = B.CloseTag, this.stream.write("]")
|
||||
}
|
||||
return Y.state = B.CloseTag, this.stream.write(Y.spaceBeforeSlash + ">"), this.stream.write(this.endline(D, Y, W)), Y.state = B.None, this.closeNode(D, Y, W)
|
||||
}
|
||||
element(D, Y, W) {
|
||||
var J, F, X, V, C, K, E, N, q, O, R, T, L, _, k, i;
|
||||
if (W || (W = 0), this.openNode(D, Y, W), Y.state = B.OpenTag, R = this.indent(D, Y, W) + "<" + D.name, Y.pretty && Y.width > 0) {
|
||||
E = R.length, L = D.attribs;
|
||||
for (q in L) {
|
||||
if (!G.call(L, q)) continue;
|
||||
if (J = L[q], T = this.attribute(J, Y, W), F = T.length, E + F > Y.width) i = this.indent(D, Y, W + 1) + T, R += this.endline(D, Y, W) + i, E = i.length;
|
||||
else i = " " + T, R += i, E += i.length
|
||||
}
|
||||
} else {
|
||||
_ = D.attribs;
|
||||
for (q in _) {
|
||||
if (!G.call(_, q)) continue;
|
||||
J = _[q], R += this.attribute(J, Y, W)
|
||||
}
|
||||
}
|
||||
if (this.stream.write(R), V = D.children.length, C = V === 0 ? null : D.children[0], V === 0 || D.children.every(function(x) {
|
||||
return (x.type === A.Text || x.type === A.Raw || x.type === A.CData) && x.value === ""
|
||||
}))
|
||||
if (Y.allowEmpty) this.stream.write(">"), Y.state = B.CloseTag, this.stream.write("</" + D.name + ">");
|
||||
else Y.state = B.CloseTag, this.stream.write(Y.spaceBeforeSlash + "/>");
|
||||
else if (Y.pretty && V === 1 && (C.type === A.Text || C.type === A.Raw || C.type === A.CData) && C.value != null) this.stream.write(">"), Y.state = B.InsideTag, Y.suppressPrettyCount++, O = !0, this.writeChildNode(C, Y, W + 1), Y.suppressPrettyCount--, O = !1, Y.state = B.CloseTag, this.stream.write("</" + D.name + ">");
|
||||
else {
|
||||
this.stream.write(">" + this.endline(D, Y, W)), Y.state = B.InsideTag, k = D.children;
|
||||
for (K = 0, N = k.length; K < N; K++) X = k[K], this.writeChildNode(X, Y, W + 1);
|
||||
Y.state = B.CloseTag, this.stream.write(this.indent(D, Y, W) + "</" + D.name + ">")
|
||||
}
|
||||
return this.stream.write(this.endline(D, Y, W)), Y.state = B.None, this.closeNode(D, Y, W)
|
||||
}
|
||||
processingInstruction(D, Y, W) {
|
||||
return this.stream.write(super.processingInstruction(D, Y, W))
|
||||
}
|
||||
raw(D, Y, W) {
|
||||
return this.stream.write(super.raw(D, Y, W))
|
||||
}
|
||||
text(D, Y, W) {
|
||||
return this.stream.write(super.text(D, Y, W))
|
||||
}
|
||||
dtdAttList(D, Y, W) {
|
||||
return this.stream.write(super.dtdAttList(D, Y, W))
|
||||
}
|
||||
dtdElement(D, Y, W) {
|
||||
return this.stream.write(super.dtdElement(D, Y, W))
|
||||
}
|
||||
dtdEntity(D, Y, W) {
|
||||
return this.stream.write(super.dtdEntity(D, Y, W))
|
||||
}
|
||||
dtdNotation(D, Y, W) {
|
||||
return this.stream.write(super.dtdNotation(D, Y, W))
|
||||
}
|
||||
}
|
||||
}).call(d$2)
|
||||
})
|
||||
// @from(Start 7671814, End 7672680)
|
||||
l$2 = z((c$2, rO) => {
|
||||
(function() {
|
||||
var A, B, Q, I, G, Z, D, Y, W;
|
||||
({
|
||||
assign: Y,
|
||||
isFunction: W
|
||||
} = HE()), Q = pe1(), I = ae1(), G = m$2(), D = aH1(), Z = p$2(), A = mQ(), B = IA1(), c$2.create = function(J, F, X, V) {
|
||||
var C, K;
|
||||
if (J == null) throw new Error("Root element needs a name.");
|
||||
if (V = Y({}, F, X, V), C = new I(V), K = C.element(J), !V.headless) {
|
||||
if (C.declaration(V), V.pubID != null || V.sysID != null) C.dtd(V)
|
||||
}
|
||||
return K
|
||||
}, c$2.begin = function(J, F, X) {
|
||||
if (W(J))[F, X] = [J, F], J = {};
|
||||
if (F) return new G(J, F, X);
|
||||
else return new I(J)
|
||||
}, c$2.stringWriter = function(J) {
|
||||
return new D(J)
|
||||
}, c$2.streamWriter = function(J, F) {
|
||||
return new Z(J, F)
|
||||
}, c$2.implementation = new Q, c$2.nodeType = A, c$2.writerState = B
|
||||
}).call(c$2)
|
||||
})
|
||||
// @from(Start 7672686, End 7674590)
|
||||
a$2 = z((YF5) => {
|
||||
var i$2 = cs1(),
|
||||
IF5 = l$2();
|
||||
YF5.build = DF5;
|
||||
|
||||
function GF5(A) {
|
||||
function B(Q) {
|
||||
return Q < 10 ? "0" + Q : Q
|
||||
}
|
||||
return A.getUTCFullYear() + "-" + B(A.getUTCMonth() + 1) + "-" + B(A.getUTCDate()) + "T" + B(A.getUTCHours()) + ":" + B(A.getUTCMinutes()) + ":" + B(A.getUTCSeconds()) + "Z"
|
||||
}
|
||||
var ZF5 = Object.prototype.toString;
|
||||
|
||||
function n$2(A) {
|
||||
var B = ZF5.call(A).match(/\[object (.*)\]/);
|
||||
return B ? B[1] : B
|
||||
}
|
||||
|
||||
function DF5(A, B) {
|
||||
var Q = {
|
||||
version: "1.0",
|
||||
encoding: "UTF-8"
|
||||
},
|
||||
I = {
|
||||
pubid: "-//Apple//DTD PLIST 1.0//EN",
|
||||
sysid: "http://www.apple.com/DTDs/PropertyList-1.0.dtd"
|
||||
},
|
||||
G = IF5.create("plist");
|
||||
if (G.dec(Q.version, Q.encoding, Q.standalone), G.dtd(I.pubid, I.sysid), G.att("version", "1.0"), se1(A, G), !B) B = {};
|
||||
return B.pretty = B.pretty !== !1, G.end(B)
|
||||
}
|
||||
|
||||
function se1(A, B) {
|
||||
var Q, I, G, Z = n$2(A);
|
||||
if (Z == "Undefined") return;
|
||||
else if (Array.isArray(A)) {
|
||||
B = B.ele("array");
|
||||
for (I = 0; I < A.length; I++) se1(A[I], B)
|
||||
} else if (Buffer.isBuffer(A)) B.ele("data").raw(A.toString("base64"));
|
||||
else if (Z == "Object") {
|
||||
B = B.ele("dict");
|
||||
for (G in A)
|
||||
if (A.hasOwnProperty(G)) B.ele("key").txt(G), se1(A[G], B)
|
||||
} else if (Z == "Number") Q = A % 1 === 0 ? "integer" : "real", B.ele(Q).txt(A.toString());
|
||||
else if (Z == "BigInt") B.ele("integer").txt(A);
|
||||
else if (Z == "Date") B.ele("date").txt(GF5(new Date(A)));
|
||||
else if (Z == "Boolean") B.ele(A ? "true" : "false");
|
||||
else if (Z == "String") B.ele("string").txt(A);
|
||||
else if (Z == "ArrayBuffer") B.ele("data").raw(i$2.fromByteArray(A));
|
||||
else if (A && A.buffer && n$2(A.buffer) == "ArrayBuffer") B.ele("data").raw(i$2.fromByteArray(new Uint8Array(A.buffer), B));
|
||||
else if (Z === "Null") B.ele("null").txt("")
|
||||
}
|
||||
})
|
||||
// @from(Start 7674596, End 7674788)
|
||||
o$2 = z((re1) => {
|
||||
var s$2 = TN2();
|
||||
Object.keys(s$2).forEach(function(A) {
|
||||
re1[A] = s$2[A]
|
||||
});
|
||||
var r$2 = a$2();
|
||||
Object.keys(r$2).forEach(function(A) {
|
||||
re1[A] = r$2[A]
|
||||
})
|
||||
})
|
||||
// @from(Start 7674794, End 7677508)
|
||||
Cq2 = z((m4B, Vq2) => {
|
||||
var KF5 = "Expected a function",
|
||||
Fq2 = NaN,
|
||||
HF5 = "[object Symbol]",
|
||||
zF5 = /^\s+|\s+$/g,
|
||||
wF5 = /^[-+]0x[0-9a-f]+$/i,
|
||||
EF5 = /^0b[01]+$/i,
|
||||
UF5 = /^0o[0-7]+$/i,
|
||||
NF5 = parseInt,
|
||||
$F5 = typeof global == "object" && global && global.Object === Object && global,
|
||||
qF5 = typeof self == "object" && self && self.Object === Object && self,
|
||||
MF5 = $F5 || qF5 || Function("return this")(),
|
||||
LF5 = Object.prototype,
|
||||
RF5 = LF5.toString,
|
||||
OF5 = Math.max,
|
||||
TF5 = Math.min,
|
||||
A1A = function() {
|
||||
return MF5.Date.now()
|
||||
};
|
||||
|
||||
function PF5(A, B, Q) {
|
||||
var I, G, Z, D, Y, W, J = 0,
|
||||
F = !1,
|
||||
X = !1,
|
||||
V = !0;
|
||||
if (typeof A != "function") throw new TypeError(KF5);
|
||||
if (B = Xq2(B) || 0, B1A(Q)) F = !!Q.leading, X = "maxWait" in Q, Z = X ? OF5(Xq2(Q.maxWait) || 0, B) : Z, V = "trailing" in Q ? !!Q.trailing : V;
|
||||
|
||||
function C(_) {
|
||||
var k = I,
|
||||
i = G;
|
||||
return I = G = void 0, J = _, D = A.apply(i, k), D
|
||||
}
|
||||
|
||||
function K(_) {
|
||||
return J = _, Y = setTimeout(q, B), F ? C(_) : D
|
||||
}
|
||||
|
||||
function E(_) {
|
||||
var k = _ - W,
|
||||
i = _ - J,
|
||||
x = B - k;
|
||||
return X ? TF5(x, Z - i) : x
|
||||
}
|
||||
|
||||
function N(_) {
|
||||
var k = _ - W,
|
||||
i = _ - J;
|
||||
return W === void 0 || k >= B || k < 0 || X && i >= Z
|
||||
}
|
||||
|
||||
function q() {
|
||||
var _ = A1A();
|
||||
if (N(_)) return O(_);
|
||||
Y = setTimeout(q, E(_))
|
||||
}
|
||||
|
||||
function O(_) {
|
||||
if (Y = void 0, V && I) return C(_);
|
||||
return I = G = void 0, D
|
||||
}
|
||||
|
||||
function R() {
|
||||
if (Y !== void 0) clearTimeout(Y);
|
||||
J = 0, I = W = G = Y = void 0
|
||||
}
|
||||
|
||||
function T() {
|
||||
return Y === void 0 ? D : O(A1A())
|
||||
}
|
||||
|
||||
function L() {
|
||||
var _ = A1A(),
|
||||
k = N(_);
|
||||
if (I = arguments, G = this, W = _, k) {
|
||||
if (Y === void 0) return K(W);
|
||||
if (X) return Y = setTimeout(q, B), C(W)
|
||||
}
|
||||
if (Y === void 0) Y = setTimeout(q, B);
|
||||
return D
|
||||
}
|
||||
return L.cancel = R, L.flush = T, L
|
||||
}
|
||||
|
||||
function B1A(A) {
|
||||
var B = typeof A;
|
||||
return !!A && (B == "object" || B == "function")
|
||||
}
|
||||
|
||||
function SF5(A) {
|
||||
return !!A && typeof A == "object"
|
||||
}
|
||||
|
||||
function _F5(A) {
|
||||
return typeof A == "symbol" || SF5(A) && RF5.call(A) == HF5
|
||||
}
|
||||
|
||||
function Xq2(A) {
|
||||
if (typeof A == "number") return A;
|
||||
if (_F5(A)) return Fq2;
|
||||
if (B1A(A)) {
|
||||
var B = typeof A.valueOf == "function" ? A.valueOf() : A;
|
||||
A = B1A(B) ? B + "" : B
|
||||
}
|
||||
if (typeof A != "string") return A === 0 ? A : +A;
|
||||
A = A.replace(zF5, "");
|
||||
var Q = EF5.test(A);
|
||||
return Q || UF5.test(A) ? NF5(A.slice(2), Q ? 2 : 8) : wF5.test(A) ? Fq2 : +A
|
||||
}
|
||||
Vq2.exports = PF5
|
||||
})
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user