mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 21:05:51 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b32dd4549d | ||
|
|
0135ad99ad | ||
|
|
9018c7afdb | ||
|
|
65d7f1994c | ||
|
|
ce2f19cc48 | ||
|
|
f6fe94463e | ||
|
|
c57f5a29e8 | ||
|
|
8f6800f508 | ||
|
|
722d59b6d5 | ||
|
|
b51b2d7675 | ||
|
|
975b4876cc | ||
|
|
30e863c9b8 | ||
|
|
c6491372d0 | ||
|
|
04c8ef2ecc | ||
|
|
b759df5b0e | ||
|
|
173d18bea8 | ||
|
|
c587a64320 | ||
|
|
17ec716dbf | ||
|
|
e443a8fa51 | ||
|
|
9dd1eeff2f | ||
|
|
dc2fdc7cc7 | ||
|
|
1d15e30f3c | ||
|
|
4319afc08f | ||
|
|
074ea844dc | ||
|
|
4692c3e2ef | ||
|
|
b1c6249f03 | ||
|
|
2de3d309b4 | ||
|
|
f10d179ace | ||
|
|
7e15974be9 | ||
|
|
fac9341e73 | ||
|
|
58f1bd49cb | ||
|
|
91f77ea571 | ||
|
|
dd9cd782a7 | ||
|
|
d7a729ca68 | ||
|
|
4c0a655a1c | ||
|
|
2c759fe6fa |
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{json,yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
22
.githooks/pre-commit
Executable file
22
.githooks/pre-commit
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
# pre-commit hook: 对暂存的文件运行 Biome 检查
|
||||
# 仅检查 src/ 下的 .ts/.tsx/.js/.jsx 文件
|
||||
|
||||
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^src/.*\.(ts|tsx|js|jsx)$')
|
||||
|
||||
if [ -z "$STAGED_FILES" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Running Biome lint on staged files..."
|
||||
|
||||
# 使用 biome lint 对暂存文件进行检查(仅 lint,不格式化,不自动修复)
|
||||
echo "$STAGED_FILES" | xargs bunx biome lint --no-errors-on-unmatched
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo ""
|
||||
echo "Biome lint failed. Fix errors or use --no-verify to bypass."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
30
.github/workflows/ci.yml
vendored
Normal file
30
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, feature/*]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Lint
|
||||
run: bun run lint
|
||||
|
||||
- name: Test
|
||||
run: bun test
|
||||
|
||||
- name: Build
|
||||
run: bun run build
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,4 +6,5 @@ coverage
|
||||
*.log
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.suo
|
||||
*.lock
|
||||
115
CLAUDE.md
Normal file
115
CLAUDE.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a **reverse-engineered / decompiled** version of Anthropic's official Claude Code CLI tool. The goal is to restore core functionality while trimming secondary capabilities. Many modules are stubbed or feature-flagged off. The codebase has ~1341 tsc errors from decompilation (mostly `unknown`/`never`/`{}` types) — these do **not** block Bun runtime execution.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
bun install
|
||||
|
||||
# Dev mode (direct execution via Bun)
|
||||
bun run dev
|
||||
# equivalent to: bun run src/entrypoints/cli.tsx
|
||||
|
||||
# Pipe mode
|
||||
echo "say hello" | bun run src/entrypoints/cli.tsx -p
|
||||
|
||||
# Build (outputs dist/cli.js, ~25MB)
|
||||
bun run build
|
||||
```
|
||||
|
||||
No test runner is configured. No linter is configured.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Runtime & Build
|
||||
|
||||
- **Runtime**: Bun (not Node.js). All imports, builds, and execution use Bun APIs.
|
||||
- **Build**: `bun build src/entrypoints/cli.tsx --outdir dist --target bun` — single-file bundle.
|
||||
- **Module system**: ESM (`"type": "module"`), TSX with `react-jsx` transform.
|
||||
- **Monorepo**: Bun workspaces — internal packages live in `packages/` resolved via `workspace:*`.
|
||||
|
||||
### Entry & Bootstrap
|
||||
|
||||
1. **`src/entrypoints/cli.tsx`** — True entrypoint. Injects runtime polyfills at the top:
|
||||
- `feature()` always returns `false` (all feature flags disabled, skipping unimplemented branches).
|
||||
- `globalThis.MACRO` — simulates build-time macro injection (VERSION, BUILD_TIME, etc.).
|
||||
- `BUILD_TARGET`, `BUILD_ENV`, `INTERFACE_TYPE` globals.
|
||||
2. **`src/main.tsx`** — Commander.js CLI definition. Parses args, initializes services (auth, analytics, policy), then launches the REPL or runs in pipe mode.
|
||||
3. **`src/entrypoints/init.ts`** — One-time initialization (telemetry, config, trust dialog).
|
||||
|
||||
### Core Loop
|
||||
|
||||
- **`src/query.ts`** — The main API query function. Sends messages to Claude API, handles streaming responses, processes tool calls, and manages the conversation turn loop.
|
||||
- **`src/QueryEngine.ts`** — Higher-level orchestrator wrapping `query()`. Manages conversation state, compaction, file history snapshots, attribution, and turn-level bookkeeping. Used by the REPL screen.
|
||||
- **`src/screens/REPL.tsx`** — The interactive REPL screen (React/Ink component). Handles user input, message display, tool permission prompts, and keyboard shortcuts.
|
||||
|
||||
### API Layer
|
||||
|
||||
- **`src/services/api/claude.ts`** — Core API client. Builds request params (system prompt, messages, tools, betas), calls the Anthropic SDK streaming endpoint, and processes `BetaRawMessageStreamEvent` events.
|
||||
- Supports multiple providers: Anthropic direct, AWS Bedrock, Google Vertex, Azure.
|
||||
- Provider selection in `src/utils/model/providers.ts`.
|
||||
|
||||
### Tool System
|
||||
|
||||
- **`src/Tool.ts`** — Tool interface definition (`Tool` type) and utilities (`findToolByName`, `toolMatchesName`).
|
||||
- **`src/tools.ts`** — Tool registry. Assembles the tool list; some tools are conditionally loaded via `feature()` flags or `process.env.USER_TYPE`.
|
||||
- **`src/tools/<ToolName>/`** — Each tool in its own directory (e.g., `BashTool`, `FileEditTool`, `GrepTool`, `AgentTool`).
|
||||
- Tools define: `name`, `description`, `inputSchema` (JSON Schema), `call()` (execution), and optionally a React component for rendering results.
|
||||
|
||||
### UI Layer (Ink)
|
||||
|
||||
- **`src/ink.ts`** — Ink render wrapper with ThemeProvider injection.
|
||||
- **`src/ink/`** — Custom Ink framework (forked/internal): custom reconciler, hooks (`useInput`, `useTerminalSize`, `useSearchHighlight`), virtual list rendering.
|
||||
- **`src/components/`** — React components rendered in terminal via Ink. Key ones:
|
||||
- `App.tsx` — Root provider (AppState, Stats, FpsMetrics).
|
||||
- `Messages.tsx` / `MessageRow.tsx` — Conversation message rendering.
|
||||
- `PromptInput/` — User input handling.
|
||||
- `permissions/` — Tool permission approval UI.
|
||||
- Components use React Compiler runtime (`react/compiler-runtime`) — decompiled output has `_c()` memoization calls throughout.
|
||||
|
||||
### State Management
|
||||
|
||||
- **`src/state/AppState.tsx`** — Central app state type and context provider. Contains messages, tools, permissions, MCP connections, etc.
|
||||
- **`src/state/store.ts`** — Zustand-style store for AppState.
|
||||
- **`src/bootstrap/state.ts`** — Module-level singletons for session-global state (session ID, CWD, project root, token counts).
|
||||
|
||||
### Context & System Prompt
|
||||
|
||||
- **`src/context.ts`** — Builds system/user context for the API call (git status, date, CLAUDE.md contents, memory files).
|
||||
- **`src/utils/claudemd.ts`** — Discovers and loads CLAUDE.md files from project hierarchy.
|
||||
|
||||
### Feature Flag System
|
||||
|
||||
All `feature('FLAG_NAME')` calls come from `bun:bundle` (a build-time API). In this decompiled version, `feature()` is polyfilled to always return `false` in `cli.tsx`. This means all Anthropic-internal features (COORDINATOR_MODE, KAIROS, PROACTIVE, etc.) are disabled.
|
||||
|
||||
### Stubbed/Deleted Modules
|
||||
|
||||
| Module | Status |
|
||||
|--------|--------|
|
||||
| Computer Use (`@ant/*`) | Stub packages in `packages/@ant/` |
|
||||
| `*-napi` packages (audio, image, url, modifiers) | Stubs in `packages/` (except `color-diff-napi` which is fully implemented) |
|
||||
| Analytics / GrowthBook / Sentry | Empty implementations |
|
||||
| Magic Docs / Voice Mode / LSP Server | Removed |
|
||||
| Plugins / Marketplace | Removed |
|
||||
| MCP OAuth | Simplified |
|
||||
|
||||
### Key Type Files
|
||||
|
||||
- **`src/types/global.d.ts`** — Declares `MACRO`, `BUILD_TARGET`, `BUILD_ENV` and internal Anthropic-only identifiers.
|
||||
- **`src/types/internal-modules.d.ts`** — Type declarations for `bun:bundle`, `bun:ffi`, `@anthropic-ai/mcpb`.
|
||||
- **`src/types/message.ts`** — Message type hierarchy (UserMessage, AssistantMessage, SystemMessage, etc.).
|
||||
- **`src/types/permissions.ts`** — Permission mode and result types.
|
||||
|
||||
## Working with This Codebase
|
||||
|
||||
- **Don't try to fix all tsc errors** — they're from decompilation and don't affect runtime.
|
||||
- **`feature()` is always `false`** — any code behind a feature flag is dead code in this build.
|
||||
- **React Compiler output** — Components have decompiled memoization boilerplate (`const $ = _c(N)`). This is normal.
|
||||
- **`bun:bundle` import** — In `src/main.tsx` and other files, `import { feature } from 'bun:bundle'` works at build time. At dev-time, the polyfill in `cli.tsx` provides it.
|
||||
- **`src/` path alias** — tsconfig maps `src/*` to `./src/*`. Imports like `import { ... } from 'src/utils/...'` are valid.
|
||||
368
README.md
368
README.md
@@ -1,34 +1,31 @@
|
||||
# Claude Code (Reverse-Engineered)
|
||||
# Claude Code Best V1 (CCB)
|
||||
|
||||
Anthropic 官方 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 工具的源码反编译/逆向还原项目。目标是将 Claude Code 核心功能跑通,必要时删减次级能力。
|
||||
Anthropic 官方 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 工具的源码反编译/逆向还原项目。目标是将 Claude Code 大部分功能及工程化能力复现。虽然很难绷, 但是它叫做 CCB(踩踩背)...
|
||||
|
||||
## 核心能力
|
||||
|
||||
- API 通信(Anthropic SDK / Bedrock / Vertex)
|
||||
- Bash / FileRead / FileWrite / FileEdit 等核心工具
|
||||
- REPL 交互界面(ink 终端渲染)
|
||||
- 对话历史与会话管理
|
||||
- 权限系统
|
||||
- Agent / 子代理系统
|
||||
|
||||
## 已删减模块
|
||||
|
||||
| 模块 | 处理方式 |
|
||||
|------|----------|
|
||||
| Computer Use (`@ant/computer-use-*`) | stub |
|
||||
| Claude for Chrome MCP | stub |
|
||||
| Magic Docs / Voice Mode / LSP Server | 移除 |
|
||||
| Analytics / GrowthBook / Sentry | 空实现 |
|
||||
| Plugins / Marketplace / Desktop Upsell | 移除 |
|
||||
| Ultraplan / Tungsten / Auto Dream | 移除 |
|
||||
| MCP OAuth/IDP | 简化 |
|
||||
| DAEMON / BRIDGE / BG_SESSIONS / TEMPLATES 等 | feature flag 关闭 |
|
||||
> V1 会完成跑通及基本的类型检查通过;
|
||||
>
|
||||
> V2 会完整实现工程化配套设施;
|
||||
>
|
||||
> V3 会实现多层级解耦, 很多比如 UI 包, Agent 包都可以独立优化;
|
||||
>
|
||||
> V4 会完成大量的测试文件, 以提高稳定性
|
||||
>
|
||||
> 我不知道这个项目还会存在多久, fork 不好使, git clone 或者下载 .zip 包才稳健;
|
||||
>
|
||||
> 这个项目更新很快, 后台有 Opus 持续优化, 所以你可以提 issues, 但是 PR 暂时不会接受;
|
||||
>
|
||||
> 存活记录:
|
||||
> 开源后 12 小时: 愚人节, star 破 1k, 并且牢 A 没有发邮件搞这个项目
|
||||
>
|
||||
> 如果你想要私人咨询服务, 那么可以发送邮件到 claude-code-best@proton.me, 备注咨询与联系方式即可; 由于后续工作非常多, 可能会忽略邮件, 半天没回复, 可以多发;
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- [Bun](https://bun.sh/) >= 1.0
|
||||
一定要最新版本的 bun 啊, 不然一堆奇奇怪怪的 BUG!!! bun upgrade!!!
|
||||
|
||||
- [Bun](https://bun.sh/) >= 1.3.11
|
||||
- Node.js >= 18(部分依赖需要)
|
||||
- 有效的 Anthropic API Key(或 Bedrock / Vertex 凭据)
|
||||
|
||||
@@ -41,7 +38,7 @@ bun install
|
||||
### 运行
|
||||
|
||||
```bash
|
||||
# 开发模式(watch)
|
||||
# 开发模式, 看到版本号 888 说明就是对了
|
||||
bun run dev
|
||||
|
||||
# 直接运行
|
||||
@@ -54,7 +51,257 @@ echo "say hello" | bun run src/entrypoints/cli.tsx -p
|
||||
bun run build
|
||||
```
|
||||
|
||||
构建产物输出到 `dist/cli.js`(~25 MB,5300+ 模块)。
|
||||
构建产物输出到 `dist/cli.js`(~25.75 MB,5326 模块)。
|
||||
|
||||
## 能力清单
|
||||
|
||||
> ✅ = 已实现 ⚠️ = 部分实现 / 条件启用 ❌ = stub / 移除 / feature flag 关闭
|
||||
|
||||
### 核心系统
|
||||
|
||||
| 能力 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| REPL 交互界面(Ink 终端渲染) | ✅ | 主屏幕 5000+ 行,完整交互 |
|
||||
| API 通信 — Anthropic Direct | ✅ | 支持 API Key + OAuth |
|
||||
| API 通信 — AWS Bedrock | ✅ | 支持凭据刷新、Bearer Token |
|
||||
| API 通信 — Google Vertex | ✅ | 支持 GCP 凭据刷新 |
|
||||
| API 通信 — Azure Foundry | ✅ | 支持 API Key + Azure AD |
|
||||
| 流式对话与工具调用循环 (`query.ts`) | ✅ | 1700+ 行,含自动压缩、token 追踪 |
|
||||
| 会话引擎 (`QueryEngine.ts`) | ✅ | 1300+ 行,管理对话状态与归因 |
|
||||
| 上下文构建(git status / CLAUDE.md / memory) | ✅ | `context.ts` 完整实现 |
|
||||
| 权限系统(plan/auto/manual 模式) | ✅ | 6300+ 行,含 YOLO 分类器、路径验证、规则匹配 |
|
||||
| Hook 系统(pre/post tool use) | ✅ | 支持 settings.json 配置 |
|
||||
| 会话恢复 (`/resume`) | ✅ | 独立 ResumeConversation 屏幕 |
|
||||
| Doctor 诊断 (`/doctor`) | ✅ | 版本、API、插件、沙箱检查 |
|
||||
| 自动压缩 (compaction) | ✅ | auto-compact / micro-compact / API compact |
|
||||
|
||||
### 工具 — 始终可用
|
||||
|
||||
| 工具 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| BashTool | ✅ | Shell 执行,沙箱,权限检查 |
|
||||
| FileReadTool | ✅ | 文件 / PDF / 图片 / Notebook 读取 |
|
||||
| FileEditTool | ✅ | 字符串替换式编辑 + diff 追踪 |
|
||||
| FileWriteTool | ✅ | 文件创建 / 覆写 + diff 生成 |
|
||||
| NotebookEditTool | ✅ | Jupyter Notebook 单元格编辑 |
|
||||
| AgentTool | ✅ | 子代理派生(fork / async / background / remote) |
|
||||
| WebFetchTool | ✅ | URL 抓取 → Markdown → AI 摘要 |
|
||||
| WebSearchTool | ✅ | 网页搜索 + 域名过滤 |
|
||||
| AskUserQuestionTool | ✅ | 多问题交互提示 + 预览 |
|
||||
| SendMessageTool | ✅ | 消息发送(peers / teammates / mailbox) |
|
||||
| SkillTool | ✅ | 斜杠命令 / Skill 调用 |
|
||||
| EnterPlanModeTool | ✅ | 进入计划模式 |
|
||||
| ExitPlanModeTool (V2) | ✅ | 退出计划模式 |
|
||||
| TodoWriteTool | ✅ | Todo 列表 v1 |
|
||||
| BriefTool | ✅ | 简短消息 + 附件发送 |
|
||||
| TaskOutputTool | ✅ | 后台任务输出读取 |
|
||||
| TaskStopTool | ✅ | 后台任务停止 |
|
||||
| ListMcpResourcesTool | ✅ | MCP 资源列表 |
|
||||
| ReadMcpResourceTool | ✅ | MCP 资源读取 |
|
||||
| SyntheticOutputTool | ✅ | 非交互会话结构化输出 |
|
||||
|
||||
### 工具 — 条件启用
|
||||
|
||||
| 工具 | 状态 | 启用条件 |
|
||||
|------|------|----------|
|
||||
| GlobTool | ✅ | 未嵌入 bfs/ugrep 时启用(默认启用) |
|
||||
| GrepTool | ✅ | 同上 |
|
||||
| TaskCreateTool | ⚠️ | `isTodoV2Enabled()` 为 true 时 |
|
||||
| TaskGetTool | ⚠️ | 同上 |
|
||||
| TaskUpdateTool | ⚠️ | 同上 |
|
||||
| TaskListTool | ⚠️ | 同上 |
|
||||
| EnterWorktreeTool | ⚠️ | `isWorktreeModeEnabled()` |
|
||||
| ExitWorktreeTool | ⚠️ | 同上 |
|
||||
| TeamCreateTool | ⚠️ | `isAgentSwarmsEnabled()` |
|
||||
| TeamDeleteTool | ⚠️ | 同上 |
|
||||
| ToolSearchTool | ⚠️ | `isToolSearchEnabledOptimistic()` |
|
||||
| PowerShellTool | ⚠️ | Windows 平台检测 |
|
||||
| LSPTool | ⚠️ | `ENABLE_LSP_TOOL` 环境变量 |
|
||||
| ConfigTool | ❌ | `USER_TYPE === 'ant'`(永远为 false) |
|
||||
|
||||
### 工具 — Feature Flag 关闭(全部不可用)
|
||||
|
||||
| 工具 | Feature Flag |
|
||||
|------|-------------|
|
||||
| SleepTool | `PROACTIVE` / `KAIROS` |
|
||||
| CronCreate/Delete/ListTool | `AGENT_TRIGGERS` |
|
||||
| RemoteTriggerTool | `AGENT_TRIGGERS_REMOTE` |
|
||||
| MonitorTool | `MONITOR_TOOL` |
|
||||
| SendUserFileTool | `KAIROS` |
|
||||
| OverflowTestTool | `OVERFLOW_TEST_TOOL` |
|
||||
| TerminalCaptureTool | `TERMINAL_PANEL` |
|
||||
| WebBrowserTool | `WEB_BROWSER_TOOL` |
|
||||
| SnipTool | `HISTORY_SNIP` |
|
||||
| WorkflowTool | `WORKFLOW_SCRIPTS` |
|
||||
| PushNotificationTool | `KAIROS` |
|
||||
| SubscribePRTool | `KAIROS_GITHUB_WEBHOOKS` |
|
||||
| ListPeersTool | `UDS_INBOX` |
|
||||
| CtxInspectTool | `CONTEXT_COLLAPSE` |
|
||||
|
||||
### 工具 — Stub / 不可用
|
||||
|
||||
| 工具 | 说明 |
|
||||
|------|------|
|
||||
| TungstenTool | ANT-ONLY stub |
|
||||
| REPLTool | ANT-ONLY,`isEnabled: () => false` |
|
||||
| SuggestBackgroundPRTool | ANT-ONLY,`isEnabled: () => false` |
|
||||
| VerifyPlanExecutionTool | 需 `CLAUDE_CODE_VERIFY_PLAN=true` 环境变量,且为 stub |
|
||||
| ReviewArtifactTool | stub,未注册到 tools.ts |
|
||||
| DiscoverSkillsTool | stub,未注册到 tools.ts |
|
||||
|
||||
### 斜杠命令 — 可用
|
||||
|
||||
| 命令 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| `/add-dir` | ✅ | 添加目录 |
|
||||
| `/advisor` | ✅ | Advisor 配置 |
|
||||
| `/agents` | ✅ | 代理列表/管理 |
|
||||
| `/branch` | ✅ | 分支管理 |
|
||||
| `/btw` | ✅ | 快速备注 |
|
||||
| `/chrome` | ✅ | Chrome 集成 |
|
||||
| `/clear` | ✅ | 清屏 |
|
||||
| `/color` | ✅ | Agent 颜色 |
|
||||
| `/compact` | ✅ | 压缩对话 |
|
||||
| `/config` (`/settings`) | ✅ | 配置管理 |
|
||||
| `/context` | ✅ | 上下文信息 |
|
||||
| `/copy` | ✅ | 复制最后消息 |
|
||||
| `/cost` | ✅ | 会话费用 |
|
||||
| `/desktop` | ✅ | Claude Desktop 集成 |
|
||||
| `/diff` | ✅ | 显示 diff |
|
||||
| `/doctor` | ✅ | 健康检查 |
|
||||
| `/effort` | ✅ | 设置 effort 等级 |
|
||||
| `/exit` | ✅ | 退出 |
|
||||
| `/export` | ✅ | 导出对话 |
|
||||
| `/extra-usage` | ✅ | 额外用量信息 |
|
||||
| `/fast` | ✅ | 切换 fast 模式 |
|
||||
| `/feedback` | ✅ | 反馈 |
|
||||
| `/files` | ✅ | 已跟踪文件 |
|
||||
| `/heapdump` | ✅ | Heap dump(调试) |
|
||||
| `/help` | ✅ | 帮助 |
|
||||
| `/hooks` | ✅ | Hook 管理 |
|
||||
| `/ide` | ✅ | IDE 连接 |
|
||||
| `/init` | ✅ | 初始化项目 |
|
||||
| `/install-github-app` | ✅ | 安装 GitHub App |
|
||||
| `/install-slack-app` | ✅ | 安装 Slack App |
|
||||
| `/keybindings` | ✅ | 快捷键管理 |
|
||||
| `/login` / `/logout` | ✅ | 登录 / 登出 |
|
||||
| `/mcp` | ✅ | MCP 服务管理 |
|
||||
| `/memory` | ✅ | Memory / CLAUDE.md 管理 |
|
||||
| `/mobile` | ✅ | 移动端 QR 码 |
|
||||
| `/model` | ✅ | 模型选择 |
|
||||
| `/output-style` | ✅ | 输出风格 |
|
||||
| `/passes` | ✅ | 推荐码 |
|
||||
| `/permissions` | ✅ | 权限管理 |
|
||||
| `/plan` | ✅ | 计划模式 |
|
||||
| `/plugin` | ✅ | 插件管理 |
|
||||
| `/pr-comments` | ✅ | PR 评论 |
|
||||
| `/privacy-settings` | ✅ | 隐私设置 |
|
||||
| `/rate-limit-options` | ✅ | 限速选项 |
|
||||
| `/release-notes` | ✅ | 更新日志 |
|
||||
| `/reload-plugins` | ✅ | 重载插件 |
|
||||
| `/remote-env` | ✅ | 远程环境配置 |
|
||||
| `/rename` | ✅ | 重命名会话 |
|
||||
| `/resume` | ✅ | 恢复会话 |
|
||||
| `/review` | ✅ | 代码审查(本地) |
|
||||
| `/ultrareview` | ✅ | 云端审查 |
|
||||
| `/rewind` | ✅ | 回退对话 |
|
||||
| `/sandbox-toggle` | ✅ | 切换沙箱 |
|
||||
| `/security-review` | ✅ | 安全审查 |
|
||||
| `/session` | ✅ | 会话信息 |
|
||||
| `/skills` | ✅ | Skill 管理 |
|
||||
| `/stats` | ✅ | 会话统计 |
|
||||
| `/status` | ✅ | 状态信息 |
|
||||
| `/statusline` | ✅ | 状态栏 UI |
|
||||
| `/stickers` | ✅ | 贴纸 |
|
||||
| `/tasks` | ✅ | 任务管理 |
|
||||
| `/theme` | ✅ | 终端主题 |
|
||||
| `/think-back` | ✅ | 年度回顾 |
|
||||
| `/upgrade` | ✅ | 升级 CLI |
|
||||
| `/usage` | ✅ | 用量信息 |
|
||||
| `/insights` | ✅ | 使用分析报告 |
|
||||
| `/vim` | ✅ | Vim 模式 |
|
||||
|
||||
### 斜杠命令 — Feature Flag 关闭
|
||||
|
||||
| 命令 | Feature Flag |
|
||||
|------|-------------|
|
||||
| `/voice` | `VOICE_MODE` |
|
||||
| `/proactive` | `PROACTIVE` / `KAIROS` |
|
||||
| `/brief` | `KAIROS` / `KAIROS_BRIEF` |
|
||||
| `/assistant` | `KAIROS` |
|
||||
| `/bridge` | `BRIDGE_MODE` |
|
||||
| `/remote-control-server` | `DAEMON` + `BRIDGE_MODE` |
|
||||
| `/force-snip` | `HISTORY_SNIP` |
|
||||
| `/workflows` | `WORKFLOW_SCRIPTS` |
|
||||
| `/web-setup` | `CCR_REMOTE_SETUP` |
|
||||
| `/subscribe-pr` | `KAIROS_GITHUB_WEBHOOKS` |
|
||||
| `/ultraplan` | `ULTRAPLAN` |
|
||||
| `/torch` | `TORCH` |
|
||||
| `/peers` | `UDS_INBOX` |
|
||||
| `/fork` | `FORK_SUBAGENT` |
|
||||
| `/buddy` | `BUDDY` |
|
||||
|
||||
### 斜杠命令 — ANT-ONLY(不可用)
|
||||
|
||||
`/tag` `/backfill-sessions` `/break-cache` `/bughunter` `/commit` `/commit-push-pr` `/ctx_viz` `/good-claude` `/issue` `/init-verifiers` `/mock-limits` `/bridge-kick` `/version` `/reset-limits` `/onboarding` `/share` `/summary` `/teleport` `/ant-trace` `/perf-issue` `/env` `/oauth-refresh` `/debug-tool-call` `/agents-platform` `/autofix-pr`
|
||||
|
||||
### CLI 子命令
|
||||
|
||||
| 子命令 | 状态 | 说明 |
|
||||
|--------|------|------|
|
||||
| `claude`(默认) | ✅ | 主 REPL / 交互 / print 模式 |
|
||||
| `claude mcp serve/add/remove/list/get/...` | ✅ | MCP 服务管理(7 个子命令) |
|
||||
| `claude auth login/status/logout` | ✅ | 认证管理 |
|
||||
| `claude plugin validate/list/install/...` | ✅ | 插件管理(7 个子命令) |
|
||||
| `claude setup-token` | ✅ | 长效 Token 配置 |
|
||||
| `claude agents` | ✅ | 代理列表 |
|
||||
| `claude doctor` | ✅ | 健康检查 |
|
||||
| `claude update` / `upgrade` | ✅ | 自动更新 |
|
||||
| `claude install [target]` | ✅ | Native 安装 |
|
||||
| `claude server` | ❌ | `DIRECT_CONNECT` flag |
|
||||
| `claude ssh <host>` | ❌ | `SSH_REMOTE` flag |
|
||||
| `claude open <cc-url>` | ❌ | `DIRECT_CONNECT` flag |
|
||||
| `claude auto-mode` | ❌ | `TRANSCRIPT_CLASSIFIER` flag |
|
||||
| `claude remote-control` | ❌ | `BRIDGE_MODE` + `DAEMON` flag |
|
||||
| `claude assistant` | ❌ | `KAIROS` flag |
|
||||
| `claude up/rollback/log/error/export/task/completion` | ❌ | ANT-ONLY |
|
||||
|
||||
### 服务层
|
||||
|
||||
| 服务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| API 客户端 (`services/api/`) | ✅ | 3400+ 行,4 个 provider |
|
||||
| MCP (`services/mcp/`) | ✅ | 24 个文件,12000+ 行 |
|
||||
| OAuth (`services/oauth/`) | ✅ | 完整 OAuth 流程 |
|
||||
| 插件 (`services/plugins/`) | ✅ | 基础设施完整,无内置插件 |
|
||||
| LSP (`services/lsp/`) | ⚠️ | 实现存在,默认关闭 |
|
||||
| 压缩 (`services/compact/`) | ✅ | auto / micro / API 压缩 |
|
||||
| Hook 系统 (`services/tools/toolHooks.ts`) | ✅ | pre/post tool use hooks |
|
||||
| 会话记忆 (`services/SessionMemory/`) | ✅ | 会话记忆管理 |
|
||||
| 记忆提取 (`services/extractMemories/`) | ✅ | 自动记忆提取 |
|
||||
| Skill 搜索 (`services/skillSearch/`) | ✅ | 本地/远程 skill 搜索 |
|
||||
| 策略限制 (`services/policyLimits/`) | ✅ | 策略限制执行 |
|
||||
| 分析 / GrowthBook / Sentry | ⚠️ | 框架存在,实际 sink 为空 |
|
||||
| Voice (`services/voice.ts`) | ❌ | `VOICE_MODE` flag 关闭 |
|
||||
|
||||
### 内部包 (`packages/`)
|
||||
|
||||
| 包 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| `color-diff-napi` | ✅ | 997 行完整 TypeScript 实现(语法高亮 diff) |
|
||||
| `audio-capture-napi` | ❌ | stub,`isNativeAudioAvailable()` 返回 false |
|
||||
| `image-processor-napi` | ❌ | stub,`getNativeModule()` 返回 null |
|
||||
| `modifiers-napi` | ❌ | stub,`isModifierPressed()` 返回 false |
|
||||
| `url-handler-napi` | ❌ | stub,`waitForUrlEvent()` 返回 null |
|
||||
| `@ant/claude-for-chrome-mcp` | ❌ | stub,`createServer()` 返回 null |
|
||||
| `@ant/computer-use-mcp` | ❌ | stub,`buildTools()` 返回 [] |
|
||||
| `@ant/computer-use-input` | ❌ | stub,仅类型声明 |
|
||||
| `@ant/computer-use-swift` | ❌ | stub,仅类型声明 |
|
||||
|
||||
### Feature Flags(30 个,全部返回 `false`)
|
||||
|
||||
`ABLATION_BASELINE` `AGENT_MEMORY_SNAPSHOT` `BG_SESSIONS` `BRIDGE_MODE` `BUDDY` `CCR_MIRROR` `CCR_REMOTE_SETUP` `CHICAGO_MCP` `COORDINATOR_MODE` `DAEMON` `DIRECT_CONNECT` `EXPERIMENTAL_SKILL_SEARCH` `FORK_SUBAGENT` `HARD_FAIL` `HISTORY_SNIP` `KAIROS` `KAIROS_BRIEF` `KAIROS_CHANNELS` `KAIROS_GITHUB_WEBHOOKS` `LODESTONE` `MCP_SKILLS` `PROACTIVE` `SSH_REMOTE` `TORCH` `TRANSCRIPT_CLASSIFIER` `UDS_INBOX` `ULTRAPLAN` `UPLOAD_USER_SETTINGS` `VOICE_MODE` `WEB_BROWSER_TOOL` `WORKFLOW_SCRIPTS`
|
||||
|
||||
|
||||
## 项目结构
|
||||
|
||||
@@ -93,14 +340,75 @@ claude-code/
|
||||
- `feature()` — 所有 feature flag 返回 `false`,跳过未实现分支
|
||||
- `globalThis.MACRO` — 模拟构建时宏注入(VERSION 等)
|
||||
|
||||
### 类型状态
|
||||
|
||||
仍有 ~1341 个 tsc 错误,均为反编译产生的源码级类型问题(`unknown` / `never` / `{}`),**不影响 Bun 运行时**。
|
||||
|
||||
### Monorepo
|
||||
|
||||
项目采用 Bun workspaces 管理内部包。原先手工放在 `node_modules/` 下的 stub 已统一迁入 `packages/`,通过 `workspace:*` 解析。
|
||||
|
||||
## Feature Flags 详解
|
||||
|
||||
原版 Claude Code 通过 `bun:bundle` 的 `feature()` 在构建时注入 feature flag,由 GrowthBook 等 A/B 实验平台控制灰度发布。本项目中 `feature()` 被 polyfill 为始终返回 `false`,因此以下 30 个 flag 全部关闭。
|
||||
|
||||
### 自主 Agent
|
||||
|
||||
| Flag | 用途 |
|
||||
|------|------|
|
||||
| `KAIROS` | Assistant 模式 — 长期运行的自主 Agent(含 brief、push 通知、文件发送) |
|
||||
| `KAIROS_BRIEF` | Kairos Brief — 向用户发送简报摘要 |
|
||||
| `KAIROS_CHANNELS` | Kairos 频道 — 多频道通信 |
|
||||
| `KAIROS_GITHUB_WEBHOOKS` | GitHub Webhook 订阅 — PR 事件实时推送给 Agent |
|
||||
| `PROACTIVE` | 主动模式 — Agent 主动执行任务,含 SleepTool 定时唤醒 |
|
||||
| `COORDINATOR_MODE` | 协调器模式 — 多 Agent 编排调度 |
|
||||
| `BUDDY` | Buddy 配对编程功能 |
|
||||
| `FORK_SUBAGENT` | Fork 子代理 — 从当前会话分叉出独立子代理 |
|
||||
|
||||
### 远程 / 分布式
|
||||
|
||||
| Flag | 用途 |
|
||||
|------|------|
|
||||
| `BRIDGE_MODE` | 远程控制桥接 — 允许外部客户端远程操控 Claude Code |
|
||||
| `DAEMON` | 守护进程 — 后台常驻服务,支持 worker 和 supervisor |
|
||||
| `BG_SESSIONS` | 后台会话 — `ps`/`logs`/`attach`/`kill`/`--bg` 等后台进程管理 |
|
||||
| `SSH_REMOTE` | SSH 远程 — `claude ssh <host>` 连接远程主机 |
|
||||
| `DIRECT_CONNECT` | 直连模式 — `cc://` URL 协议、server 命令、`open` 命令 |
|
||||
| `CCR_REMOTE_SETUP` | 网页端远程配置 — 通过浏览器配置 Claude Code |
|
||||
| `CCR_MIRROR` | Claude Code Runtime 镜像 — 会话状态同步/复制 |
|
||||
|
||||
### 通信
|
||||
|
||||
| Flag | 用途 |
|
||||
|------|------|
|
||||
| `UDS_INBOX` | Unix Domain Socket 收件箱 — Agent 间本地通信(`/peers`) |
|
||||
|
||||
### 增强工具
|
||||
|
||||
| Flag | 用途 |
|
||||
|------|------|
|
||||
| `CHICAGO_MCP` | Computer Use MCP — 计算机操作(屏幕截图、鼠标键盘控制) |
|
||||
| `WEB_BROWSER_TOOL` | 网页浏览器工具 — 在终端内嵌浏览器交互 |
|
||||
| `VOICE_MODE` | 语音模式 — 语音输入输出,麦克风 push-to-talk |
|
||||
| `WORKFLOW_SCRIPTS` | 工作流脚本 — 用户自定义自动化工作流 |
|
||||
| `MCP_SKILLS` | 基于 MCP 的 Skill 加载机制 |
|
||||
|
||||
### 对话管理
|
||||
|
||||
| Flag | 用途 |
|
||||
|------|------|
|
||||
| `HISTORY_SNIP` | 历史裁剪 — 手动裁剪对话历史中的片段(`/force-snip`) |
|
||||
| `ULTRAPLAN` | 超级计划 — 远程 Agent 协作的大规模规划功能 |
|
||||
| `AGENT_MEMORY_SNAPSHOT` | Agent 运行时的记忆快照功能 |
|
||||
|
||||
### 基础设施 / 实验
|
||||
|
||||
| Flag | 用途 |
|
||||
|------|------|
|
||||
| `ABLATION_BASELINE` | 科学实验 — 基线消融测试,用于 A/B 实验对照组 |
|
||||
| `HARD_FAIL` | 硬失败模式 — 遇错直接中断而非降级 |
|
||||
| `TRANSCRIPT_CLASSIFIER` | 对话分类器 — `auto-mode` 命令,自动分析和分类对话记录 |
|
||||
| `UPLOAD_USER_SETTINGS` | 设置同步上传 — 将本地配置同步到云端 |
|
||||
| `LODESTONE` | 深度链接协议处理器 — 从外部应用跳转到 Claude Code 指定位置 |
|
||||
| `EXPERIMENTAL_SKILL_SEARCH` | 实验性 Skill 搜索索引 |
|
||||
| `TORCH` | Torch 功能(具体用途未知,可能是某种高亮/追踪机制) |
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目仅供学习研究用途。Claude Code 的所有权利归 [Anthropic](https://www.anthropic.com/) 所有。
|
||||
|
||||
61
RECORD.md
61
RECORD.md
@@ -57,7 +57,7 @@ bun run build
|
||||
|
||||
### TS 类型错误说明
|
||||
|
||||
仍有 ~1341 个 tsc 错误,绝大多数是反编译产生的源码级类型问题(unknown/never/{}),**不影响 Bun 运行时**。不再逐个修复。
|
||||
~~仍有 ~1341 个 tsc 错误~~ → 经过系统性类型修复,已降至 **~294 个**(减少 78%)。剩余错误分散在小文件中,均为反编译产生的源码级类型问题(`unknown`/`never`/`{}`),**不影响 Bun 运行时**。
|
||||
|
||||
---
|
||||
|
||||
@@ -157,3 +157,62 @@ $ bun run build
|
||||
Bundled 5326 modules in 491ms
|
||||
cli.js 25.74 MB (entry point)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、系统性类型修复(2026-03-31)
|
||||
|
||||
### 6.1 背景
|
||||
|
||||
反编译产生的源码存在 ~1341 个 tsc 类型错误,主要成因:
|
||||
- `unknown` 类型上的属性访问(714 个,占 54%)
|
||||
- 类型赋值不兼容(212 个)
|
||||
- 参数类型不匹配(140 个)
|
||||
- 不可能的字面量比较(106 个,如 `"external" === 'ant'`)
|
||||
|
||||
### 6.2 修复策略
|
||||
|
||||
通过 4 轮并行 agent(每轮 7 个)系统性修复,**从 1341 降至 ~294**(减少 78%)。
|
||||
|
||||
#### 根因修复(影响面最大)
|
||||
|
||||
| 修复 | 影响 |
|
||||
|------|------|
|
||||
| `useAppState<R>` 添加泛型签名 (`AppState.tsx`) | 消除全局大量 `unknown` 返回值 |
|
||||
| `Message` 类型重构 (`message.ts`) | content 改为 `string \| ContentBlockParam[] \| ContentBlock[]`;添加 `MessageType` 扩展联合;`GroupedToolUseMessage`/`CollapsedReadSearchGroup` 结构化 |
|
||||
| `SDKAssistantMessageError` 命名冲突修复 (`coreTypes.generated.ts`) | 解决 37 个 errors.ts 类型错误 |
|
||||
| SDK 消息类型增强 (`coreTypes.generated.ts`) | `SDKAssistantMessage`/`SDKUserMessage` 等添加具体字段声明 |
|
||||
| `NonNullableUsage` 扩展 (`sdkUtilityTypes.ts`) | 添加 snake_case 属性声明 |
|
||||
|
||||
#### 批量模式修复
|
||||
|
||||
| 模式 | 修复方式 | 数量 |
|
||||
|------|----------|------|
|
||||
| `"external" === 'ant'` 编译常量比较 | `("external" as string) === 'ant'` | ~60 处 |
|
||||
| `unknown` 属性访问 | 精确类型断言(`as SomeType`) | ~400 处 |
|
||||
| `message.content` union 无法调用数组方法 | `Array.isArray()` 守卫 | ~80 处 |
|
||||
| stub 包缺失方法/类型 | 补全 stub 类型声明 | ~15 个包 |
|
||||
|
||||
#### Stub 包类型补全
|
||||
|
||||
| 包 | 补全内容 |
|
||||
|----|----------|
|
||||
| `@ant/computer-use-swift` | `ComputerUseAPI` 完整接口(apps/display/screenshot) |
|
||||
| `@ant/computer-use-input` | `ComputerUseInputAPI` 完整接口 |
|
||||
| `audio-capture-napi` | 4 个函数签名 |
|
||||
|
||||
### 6.3 修复的关键文件
|
||||
|
||||
| 文件 | 修复错误数 |
|
||||
|------|-----------|
|
||||
| `src/screens/REPL.tsx` | ~100 |
|
||||
| `src/utils/hooks.ts` | ~81 |
|
||||
| `src/utils/sessionStorage.ts` | ~58 |
|
||||
| `src/components/PromptInput/` | ~45 |
|
||||
| `src/services/api/errors.ts` | ~37 |
|
||||
| `src/utils/computerUse/executor.ts` | ~36 |
|
||||
| `src/utils/messages.ts` | ~83 |
|
||||
| `src/QueryEngine.ts` | ~39 |
|
||||
| `src/services/api/claude.ts` | ~35 |
|
||||
| `src/cli/print.ts` + `structuredIO.ts` | ~46 |
|
||||
| 其他 ~50 个文件 | ~487 |
|
||||
|
||||
21
SECURITY.md
Normal file
21
SECURITY.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 5.1.x | :white_check_mark: |
|
||||
| 5.0.x | :x: |
|
||||
| 4.0.x | :white_check_mark: |
|
||||
| < 4.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Use this section to tell people how to report a vulnerability.
|
||||
|
||||
Tell them where to go, how often they can expect to get an update on a
|
||||
reported vulnerability, what to expect if the vulnerability is accepted or
|
||||
declined, etc.
|
||||
26
TODO.md
Normal file
26
TODO.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# TODO
|
||||
|
||||
尽可能实现下面的包, 使得与主包的关系完全吻合
|
||||
|
||||
## Packages
|
||||
|
||||
- [x] `url-handler-napi` — URL 处理 NAPI 模块 (签名修正,保持 null fallback)
|
||||
- [x] `modifiers-napi` — 修饰键检测 NAPI 模块 (Bun FFI + Carbon)
|
||||
- [x] `audio-capture-napi` — 音频捕获 NAPI 模块 (SoX/arecord)
|
||||
- [x] `color-diff-napi` — 颜色差异计算 NAPI 模块 (纯 TS 实现)
|
||||
- [x] `image-processor-napi` — 图像处理 NAPI 模块 (sharp + osascript 剪贴板)
|
||||
|
||||
- [x] `@ant/computer-use-swift` — Computer Use Swift 原生模块 (macOS JXA/screencapture 实现)
|
||||
- [x] `@ant/computer-use-mcp` — Computer Use MCP 服务 (类型安全 stub + sentinel apps + targetImageSize)
|
||||
- [x] `@ant/computer-use-input` — Computer Use 输入模块 (macOS AppleScript/JXA 实现)
|
||||
<!-- - [ ] `@ant/claude-for-chrome-mcp` — Chrome MCP 扩展 -->
|
||||
|
||||
## 工程化能力
|
||||
|
||||
- [x] 代码格式化与校验
|
||||
- [x] 冗余代码检查
|
||||
- [x] git hook 的配置
|
||||
- [x] 代码健康度检查
|
||||
- [x] Biome lint 规则调优(适配反编译代码,关闭格式化避免大规模 diff)
|
||||
- [x] 单元测试基础设施搭建 (test runner 配置)
|
||||
- [x] CI/CD 流水线 (GitHub Actions)
|
||||
114
biome.json
Normal file
114
biome.json
Normal file
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"includes": ["**", "!!**/dist", "!!**/packages/@ant"]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineWidth": 80
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"suspicious": {
|
||||
"noExplicitAny": "off",
|
||||
"noAssignInExpressions": "off",
|
||||
"noDoubleEquals": "off",
|
||||
"noRedeclare": "off",
|
||||
"noImplicitAnyLet": "off",
|
||||
"noGlobalIsNan": "off",
|
||||
"noFallthroughSwitchClause": "off",
|
||||
"noShadowRestrictedNames": "off",
|
||||
"noArrayIndexKey": "off",
|
||||
"noConsole": "off",
|
||||
"noConfusingLabels": "off",
|
||||
"useIterableCallbackReturn": "off"
|
||||
},
|
||||
"style": {
|
||||
"useConst": "off",
|
||||
"noNonNullAssertion": "off",
|
||||
"noParameterAssign": "off",
|
||||
"useDefaultParameterLast": "off",
|
||||
"noUnusedTemplateLiteral": "off",
|
||||
"useTemplate": "off",
|
||||
"useNumberNamespace": "off",
|
||||
"useNodejsImportProtocol": "off",
|
||||
"useImportType": "off"
|
||||
},
|
||||
"complexity": {
|
||||
"noForEach": "off",
|
||||
"noBannedTypes": "off",
|
||||
"noUselessConstructor": "off",
|
||||
"noStaticOnlyClass": "off",
|
||||
"useOptionalChain": "off",
|
||||
"noUselessSwitchCase": "off",
|
||||
"noUselessFragments": "off",
|
||||
"noUselessTernary": "off",
|
||||
"noUselessLoneBlockStatements": "off",
|
||||
"noUselessEmptyExport": "off",
|
||||
"useArrowFunction": "off",
|
||||
"useLiteralKeys": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noUnusedVariables": "off",
|
||||
"noUnusedImports": "off",
|
||||
"useExhaustiveDependencies": "off",
|
||||
"noSwitchDeclarations": "off",
|
||||
"noUnreachable": "off",
|
||||
"useHookAtTopLevel": "off",
|
||||
"noVoidTypeReturn": "off",
|
||||
"noConstantCondition": "off",
|
||||
"noUnusedFunctionParameters": "off"
|
||||
},
|
||||
"a11y": {
|
||||
"recommended": false
|
||||
},
|
||||
"nursery": {
|
||||
"recommended": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"json": {
|
||||
"formatter": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "single",
|
||||
"semicolons": "asNeeded",
|
||||
"arrowParentheses": "asNeeded",
|
||||
"trailingCommas": "all"
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"includes": ["**/*.tsx"],
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"semicolons": "always"
|
||||
}
|
||||
},
|
||||
"formatter": {
|
||||
"lineWidth": 120
|
||||
}
|
||||
},
|
||||
{
|
||||
"includes": ["scripts/**", "packages/**", "**/*.js", "**/*.mjs", "**/*.jsx"],
|
||||
"formatter": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"assist": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
411
bun.lock
411
bun.lock
@@ -4,119 +4,119 @@
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "claude-code",
|
||||
"dependencies": {
|
||||
"devDependencies": {
|
||||
"@alcalzone/ansi-tokenize": "^0.3.0",
|
||||
"@ant/claude-for-chrome-mcp": "workspace:*",
|
||||
"@ant/computer-use-input": "workspace:*",
|
||||
"@ant/computer-use-mcp": "workspace:*",
|
||||
"@ant/computer-use-swift": "workspace:*",
|
||||
"@anthropic-ai/bedrock-sdk": "^0.26.4",
|
||||
"@anthropic-ai/claude-agent-sdk": "latest",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.87",
|
||||
"@anthropic-ai/foundry-sdk": "^0.2.3",
|
||||
"@anthropic-ai/mcpb": "latest",
|
||||
"@anthropic-ai/sandbox-runtime": "latest",
|
||||
"@anthropic-ai/sdk": "latest",
|
||||
"@anthropic-ai/mcpb": "^2.1.2",
|
||||
"@anthropic-ai/sandbox-runtime": "^0.0.44",
|
||||
"@anthropic-ai/sdk": "^0.80.0",
|
||||
"@anthropic-ai/vertex-sdk": "^0.14.4",
|
||||
"@aws-sdk/client-bedrock": "latest",
|
||||
"@aws-sdk/client-bedrock-runtime": "latest",
|
||||
"@aws-sdk/client-bedrock": "^3.1020.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.1020.0",
|
||||
"@aws-sdk/client-sts": "^3.1020.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.28",
|
||||
"@aws-sdk/credential-providers": "latest",
|
||||
"@aws-sdk/credential-providers": "^3.1020.0",
|
||||
"@azure/identity": "^4.13.1",
|
||||
"@commander-js/extra-typings": "latest",
|
||||
"@growthbook/growthbook": "latest",
|
||||
"@modelcontextprotocol/sdk": "latest",
|
||||
"@opentelemetry/api": "latest",
|
||||
"@opentelemetry/api-logs": "latest",
|
||||
"@opentelemetry/core": "latest",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "latest",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "latest",
|
||||
"@biomejs/biome": "^2.4.10",
|
||||
"@commander-js/extra-typings": "^14.0.0",
|
||||
"@growthbook/growthbook": "^1.6.5",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@opentelemetry/api-logs": "^0.214.0",
|
||||
"@opentelemetry/core": "^2.6.1",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.214.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.214.0",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "latest",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "latest",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.214.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.214.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/exporter-prometheus": "latest",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "latest",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "latest",
|
||||
"@opentelemetry/exporter-prometheus": "^0.214.0",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.214.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.214.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/resources": "latest",
|
||||
"@opentelemetry/sdk-logs": "latest",
|
||||
"@opentelemetry/sdk-metrics": "latest",
|
||||
"@opentelemetry/sdk-trace-base": "latest",
|
||||
"@opentelemetry/semantic-conventions": "latest",
|
||||
"@smithy/core": "latest",
|
||||
"@smithy/node-http-handler": "latest",
|
||||
"ajv": "latest",
|
||||
"asciichart": "latest",
|
||||
"audio-capture-napi": "workspace:*",
|
||||
"auto-bind": "latest",
|
||||
"axios": "latest",
|
||||
"bidi-js": "latest",
|
||||
"cacache": "^20.0.4",
|
||||
"chalk": "latest",
|
||||
"chokidar": "latest",
|
||||
"cli-boxes": "latest",
|
||||
"cli-highlight": "^2.1.11",
|
||||
"code-excerpt": "latest",
|
||||
"color-diff-napi": "workspace:*",
|
||||
"diff": "latest",
|
||||
"emoji-regex": "latest",
|
||||
"env-paths": "latest",
|
||||
"execa": "latest",
|
||||
"fflate": "latest",
|
||||
"figures": "latest",
|
||||
"fuse.js": "latest",
|
||||
"get-east-asian-width": "latest",
|
||||
"google-auth-library": "latest",
|
||||
"highlight.js": "latest",
|
||||
"https-proxy-agent": "latest",
|
||||
"ignore": "latest",
|
||||
"image-processor-napi": "workspace:*",
|
||||
"indent-string": "latest",
|
||||
"jsonc-parser": "^3.3.1",
|
||||
"lodash-es": "latest",
|
||||
"lru-cache": "latest",
|
||||
"marked": "latest",
|
||||
"modifiers-napi": "workspace:*",
|
||||
"p-map": "latest",
|
||||
"picomatch": "latest",
|
||||
"plist": "^3.1.0",
|
||||
"proper-lockfile": "latest",
|
||||
"qrcode": "latest",
|
||||
"react": "latest",
|
||||
"react-compiler-runtime": "^1.0.0",
|
||||
"react-reconciler": "latest",
|
||||
"semver": "latest",
|
||||
"sharp": "^0.34.5",
|
||||
"shell-quote": "latest",
|
||||
"signal-exit": "latest",
|
||||
"stack-utils": "latest",
|
||||
"strip-ansi": "latest",
|
||||
"supports-hyperlinks": "latest",
|
||||
"tree-kill": "latest",
|
||||
"turndown": "^7.2.2",
|
||||
"type-fest": "latest",
|
||||
"undici": "latest",
|
||||
"url-handler-napi": "workspace:*",
|
||||
"usehooks-ts": "latest",
|
||||
"vscode-jsonrpc": "latest",
|
||||
"vscode-languageserver-protocol": "latest",
|
||||
"vscode-languageserver-types": "latest",
|
||||
"wrap-ansi": "latest",
|
||||
"ws": "latest",
|
||||
"xss": "latest",
|
||||
"yaml": "^2.8.3",
|
||||
"zod": "latest",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opentelemetry/resources": "^2.6.1",
|
||||
"@opentelemetry/sdk-logs": "^0.214.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.6.1",
|
||||
"@opentelemetry/sdk-trace-base": "^2.6.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.40.0",
|
||||
"@smithy/core": "^3.23.13",
|
||||
"@smithy/node-http-handler": "^4.5.1",
|
||||
"@types/bun": "^1.3.11",
|
||||
"@types/cacache": "^20.0.1",
|
||||
"@types/plist": "^3.0.5",
|
||||
"@types/react": "latest",
|
||||
"@types/react-reconciler": "latest",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-reconciler": "^0.33.0",
|
||||
"@types/sharp": "^0.32.0",
|
||||
"@types/turndown": "^5.0.6",
|
||||
"typescript": "latest",
|
||||
"ajv": "^8.18.0",
|
||||
"asciichart": "^1.5.25",
|
||||
"audio-capture-napi": "workspace:*",
|
||||
"auto-bind": "^5.0.1",
|
||||
"axios": "^1.14.0",
|
||||
"bidi-js": "^1.0.3",
|
||||
"cacache": "^20.0.4",
|
||||
"chalk": "^5.6.2",
|
||||
"chokidar": "^5.0.0",
|
||||
"cli-boxes": "^4.0.1",
|
||||
"cli-highlight": "^2.1.11",
|
||||
"code-excerpt": "^4.0.0",
|
||||
"color-diff-napi": "workspace:*",
|
||||
"diff": "^8.0.4",
|
||||
"emoji-regex": "^10.6.0",
|
||||
"env-paths": "^4.0.0",
|
||||
"execa": "^9.6.1",
|
||||
"fflate": "^0.8.2",
|
||||
"figures": "^6.1.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"get-east-asian-width": "^1.5.0",
|
||||
"google-auth-library": "^10.6.2",
|
||||
"highlight.js": "^11.11.1",
|
||||
"https-proxy-agent": "^8.0.0",
|
||||
"ignore": "^7.0.5",
|
||||
"image-processor-napi": "workspace:*",
|
||||
"indent-string": "^5.0.0",
|
||||
"jsonc-parser": "^3.3.1",
|
||||
"knip": "^6.1.1",
|
||||
"lodash-es": "^4.17.23",
|
||||
"lru-cache": "^11.2.7",
|
||||
"marked": "^17.0.5",
|
||||
"modifiers-napi": "workspace:*",
|
||||
"p-map": "^7.0.4",
|
||||
"picomatch": "^4.0.4",
|
||||
"plist": "^3.1.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.2.4",
|
||||
"react-compiler-runtime": "^1.0.0",
|
||||
"react-reconciler": "^0.33.0",
|
||||
"semver": "^7.7.4",
|
||||
"sharp": "^0.34.5",
|
||||
"shell-quote": "^1.8.3",
|
||||
"signal-exit": "^4.1.0",
|
||||
"stack-utils": "^2.0.6",
|
||||
"strip-ansi": "^7.2.0",
|
||||
"supports-hyperlinks": "^4.4.0",
|
||||
"tree-kill": "^1.2.2",
|
||||
"turndown": "^7.2.2",
|
||||
"type-fest": "^5.5.0",
|
||||
"typescript": "^6.0.2",
|
||||
"undici": "^7.24.6",
|
||||
"url-handler-napi": "workspace:*",
|
||||
"usehooks-ts": "^3.1.1",
|
||||
"vscode-jsonrpc": "^8.2.1",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"vscode-languageserver-types": "^3.17.5",
|
||||
"wrap-ansi": "^10.0.0",
|
||||
"ws": "^8.20.0",
|
||||
"xss": "^1.0.15",
|
||||
"yaml": "^2.8.3",
|
||||
"zod": "^4.3.6",
|
||||
},
|
||||
},
|
||||
"packages/@ant/claude-for-chrome-mcp": {
|
||||
@@ -149,6 +149,9 @@
|
||||
"packages/image-processor-napi": {
|
||||
"name": "image-processor-napi",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"sharp": "^0.33.5",
|
||||
},
|
||||
},
|
||||
"packages/modifiers-napi": {
|
||||
"name": "modifiers-napi",
|
||||
@@ -286,10 +289,32 @@
|
||||
|
||||
"@babel/runtime": ["@babel/runtime@7.29.2", "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
|
||||
|
||||
"@biomejs/biome": ["@biomejs/biome@2.4.10", "https://registry.npmmirror.com/@biomejs/biome/-/biome-2.4.10.tgz", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.10", "@biomejs/cli-darwin-x64": "2.4.10", "@biomejs/cli-linux-arm64": "2.4.10", "@biomejs/cli-linux-arm64-musl": "2.4.10", "@biomejs/cli-linux-x64": "2.4.10", "@biomejs/cli-linux-x64-musl": "2.4.10", "@biomejs/cli-win32-arm64": "2.4.10", "@biomejs/cli-win32-x64": "2.4.10" }, "bin": { "biome": "bin/biome" } }, "sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.10", "https://registry.npmmirror.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.10.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.10", "https://registry.npmmirror.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.10.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.10", "https://registry.npmmirror.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.10.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.10", "https://registry.npmmirror.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.10.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.10", "https://registry.npmmirror.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.10.tgz", { "os": "linux", "cpu": "x64" }, "sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.10", "https://registry.npmmirror.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.10.tgz", { "os": "linux", "cpu": "x64" }, "sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.10", "https://registry.npmmirror.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.10.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.10", "https://registry.npmmirror.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.10.tgz", { "os": "win32", "cpu": "x64" }, "sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg=="],
|
||||
|
||||
"@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "https://registry.npmmirror.com/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="],
|
||||
|
||||
"@emnapi/core": ["@emnapi/core@1.9.1", "https://registry.npmmirror.com/@emnapi/core/-/core-1.9.1.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.9.1", "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.9.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
|
||||
|
||||
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
|
||||
|
||||
"@growthbook/growthbook": ["@growthbook/growthbook@1.6.5", "https://registry.npmmirror.com/@growthbook/growthbook/-/growthbook-1.6.5.tgz", { "dependencies": { "dom-mutator": "^0.6.0" } }, "sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A=="],
|
||||
|
||||
"@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "https://registry.npmmirror.com/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="],
|
||||
@@ -382,6 +407,14 @@
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "https://registry.npmmirror.com/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
||||
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
|
||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||
|
||||
"@npmcli/fs": ["@npmcli/fs@5.0.0", "https://registry.npmmirror.com/@npmcli/fs/-/fs-5.0.0.tgz", { "dependencies": { "semver": "^7.3.5" } }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="],
|
||||
|
||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.1", "https://registry.npmmirror.com/@opentelemetry/api/-/api-1.9.1.tgz", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
|
||||
@@ -426,6 +459,88 @@
|
||||
|
||||
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "https://registry.npmmirror.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
|
||||
|
||||
"@oxc-parser/binding-android-arm-eabi": ["@oxc-parser/binding-android-arm-eabi@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.121.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-n07FQcySwOlzap424/PLMtOkbS7xOu8nsJduKL8P3COGHKgKoDYXwoAHCbChfgFpHnviehrLWIPX0lKGtbEk/A=="],
|
||||
|
||||
"@oxc-parser/binding-android-arm64": ["@oxc-parser/binding-android-arm64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.121.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-/Dd1xIXboYAicw+twT2utxPD7bL8qh7d3ej0qvaYIMj3/EgIrGR+tSnjCUkiCT6g6uTC0neSS4JY8LxhdSU/sA=="],
|
||||
|
||||
"@oxc-parser/binding-darwin-arm64": ["@oxc-parser/binding-darwin-arm64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.121.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-A0jNEvv7QMtCO1yk205t3DWU9sWUjQ2KNF0hSVO5W9R9r/R1BIvzG01UQAfmtC0dQm7sCrs5puixurKSfr2bRQ=="],
|
||||
|
||||
"@oxc-parser/binding-darwin-x64": ["@oxc-parser/binding-darwin-x64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.121.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-SsHzipdxTKUs3I9EOAPmnIimEeJOemqRlRDOp9LIj+96wtxZejF51gNibmoGq8KoqbT1ssAI5po/E3J+vEtXGA=="],
|
||||
|
||||
"@oxc-parser/binding-freebsd-x64": ["@oxc-parser/binding-freebsd-x64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.121.0.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-v1APOTkCp+RWOIDAHRoaeW/UoaHF15a60E8eUL6kUQXh+i4K7PBwq2Wi7jm8p0ymID5/m/oC1w3W31Z/+r7HQw=="],
|
||||
|
||||
"@oxc-parser/binding-linux-arm-gnueabihf": ["@oxc-parser/binding-linux-arm-gnueabihf@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.121.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-PmqPQuqHZyFVWA4ycr0eu4VnTMmq9laOHZd+8R359w6kzuNZPvmmunmNJ8ybkm769A0nCoVp3TJ6dUz7B3FYIQ=="],
|
||||
|
||||
"@oxc-parser/binding-linux-arm-musleabihf": ["@oxc-parser/binding-linux-arm-musleabihf@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.121.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-vF24htj+MOH+Q7y9A8NuC6pUZu8t/C2Fr/kDOi2OcNf28oogr2xadBPXAbml802E8wRAVfbta6YLDQTearz+jw=="],
|
||||
|
||||
"@oxc-parser/binding-linux-arm64-gnu": ["@oxc-parser/binding-linux-arm64-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.121.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-wjH8cIG2Lu/3d64iZpbYr73hREMgKAfu7fqpXjgM2S16y2zhTfDIp8EQjxO8vlDtKP5Rc7waZW72lh8nZtWrpA=="],
|
||||
|
||||
"@oxc-parser/binding-linux-arm64-musl": ["@oxc-parser/binding-linux-arm64-musl@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.121.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-qT663J/W8yQFw3dtscbEi9LKJevr20V7uWs2MPGTnvNZ3rm8anhhE16gXGpxDOHeg9raySaSHKhd4IGa3YZvuw=="],
|
||||
|
||||
"@oxc-parser/binding-linux-ppc64-gnu": ["@oxc-parser/binding-linux-ppc64-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.121.0.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-mYNe4NhVvDBbPkAP8JaVS8lC1dsoJZWH5WCjpw5E+sjhk1R08wt3NnXYUzum7tIiWPfgQxbCMcoxgeemFASbRw=="],
|
||||
|
||||
"@oxc-parser/binding-linux-riscv64-gnu": ["@oxc-parser/binding-linux-riscv64-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.121.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-+QiFoGxhAbaI/amqX567784cDyyuZIpinBrJNxUzb+/L2aBRX67mN6Jv40pqduHf15yYByI+K5gUEygCuv0z9w=="],
|
||||
|
||||
"@oxc-parser/binding-linux-riscv64-musl": ["@oxc-parser/binding-linux-riscv64-musl@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.121.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-9ykEgyTa5JD/Uhv2sttbKnCfl2PieUfOjyxJC/oDL2UO0qtXOtjPLl7H8Kaj5G7p3hIvFgu3YWvAxvE0sqY+hQ=="],
|
||||
|
||||
"@oxc-parser/binding-linux-s390x-gnu": ["@oxc-parser/binding-linux-s390x-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.121.0.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-DB1EW5VHZdc1lIRjOI3bW/wV6R6y0xlfvdVrqj6kKi7Ayu2U3UqUBdq9KviVkcUGd5Oq+dROqvUEEFRXGAM7EQ=="],
|
||||
|
||||
"@oxc-parser/binding-linux-x64-gnu": ["@oxc-parser/binding-linux-x64-gnu@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.121.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-s4lfobX9p4kPTclvMiH3gcQUd88VlnkMTF6n2MTMDAyX5FPNRhhRSFZK05Ykhf8Zy5NibV4PbGR6DnK7FGNN6A=="],
|
||||
|
||||
"@oxc-parser/binding-linux-x64-musl": ["@oxc-parser/binding-linux-x64-musl@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.121.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-P9KlyTpuBuMi3NRGpJO8MicuGZfOoqZVRP1WjOecwx8yk4L/+mrCRNc5egSi0byhuReblBF2oVoDSMgV9Bj4Hw=="],
|
||||
|
||||
"@oxc-parser/binding-openharmony-arm64": ["@oxc-parser/binding-openharmony-arm64@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.121.0.tgz", { "os": "none", "cpu": "arm64" }, "sha512-R+4jrWOfF2OAPPhj3Eb3U5CaKNAH9/btMveMULIrcNW/hjfysFQlF8wE0GaVBr81dWz8JLgQlsxwctoL78JwXw=="],
|
||||
|
||||
"@oxc-parser/binding-wasm32-wasi": ["@oxc-parser/binding-wasm32-wasi@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.121.0.tgz", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-5TFISkPTymKvsmIlKasPVTPuWxzCcrT8pM+p77+mtQbIZDd1UC8zww4CJcRI46kolmgrEX6QpKO8AvWMVZ+ifw=="],
|
||||
|
||||
"@oxc-parser/binding-win32-arm64-msvc": ["@oxc-parser/binding-win32-arm64-msvc@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.121.0.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-V0pxh4mql4XTt3aiEtRNUeBAUFOw5jzZNxPABLaOKAWrVzSr9+XUaB095lY7jqMf5t8vkfh8NManGB28zanYKw=="],
|
||||
|
||||
"@oxc-parser/binding-win32-ia32-msvc": ["@oxc-parser/binding-win32-ia32-msvc@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.121.0.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-4Ob1qvYMPnlF2N9rdmKdkQFdrq16QVcQwBsO8yiPZXof0fHKFF+LmQV501XFbi7lHyrKm8rlJRfQ/M8bZZPVLw=="],
|
||||
|
||||
"@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.121.0", "https://registry.npmmirror.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.121.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-BOp1KCzdboB1tPqoCPXgntgFs0jjeSyOXHzgxVFR7B/qfr3F8r4YDacHkTOUNXtDgM8YwKnkf3rE5gwALYX7NA=="],
|
||||
|
||||
"@oxc-project/types": ["@oxc-project/types@0.121.0", "https://registry.npmmirror.com/@oxc-project/types/-/types-0.121.0.tgz", {}, "sha512-CGtOARQb9tyv7ECgdAlFxi0Fv7lmzvmlm2rpD/RdijOO9rfk/JvB1CjT8EnoD+tjna/IYgKKw3IV7objRb+aYw=="],
|
||||
|
||||
"@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.19.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg=="],
|
||||
|
||||
"@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.19.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA=="],
|
||||
|
||||
"@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.19.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ=="],
|
||||
|
||||
"@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.19.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ=="],
|
||||
|
||||
"@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.19.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.19.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.19.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.19.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.19.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.19.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.19.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.19.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.19.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.19.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.19.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw=="],
|
||||
|
||||
"@oxc-resolver/binding-openharmony-arm64": ["@oxc-resolver/binding-openharmony-arm64@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.19.1.tgz", { "os": "none", "cpu": "arm64" }, "sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA=="],
|
||||
|
||||
"@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.19.1.tgz", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg=="],
|
||||
|
||||
"@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.19.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ=="],
|
||||
|
||||
"@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.19.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA=="],
|
||||
|
||||
"@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.19.1", "https://registry.npmmirror.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.19.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw=="],
|
||||
|
||||
"@pondwader/socks5-server": ["@pondwader/socks5-server@1.0.10", "https://registry.npmmirror.com/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", {}, "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg=="],
|
||||
|
||||
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
||||
@@ -542,6 +657,8 @@
|
||||
|
||||
"@smithy/uuid": ["@smithy/uuid@1.1.2", "https://registry.npmmirror.com/@smithy/uuid/-/uuid-1.1.2.tgz", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g=="],
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.11", "https://registry.npmmirror.com/@types/bun/-/bun-1.3.11.tgz", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||
|
||||
"@types/cacache": ["@types/cacache@20.0.1", "https://registry.npmmirror.com/@types/cacache/-/cacache-20.0.1.tgz", { "dependencies": { "@types/node": "*", "minipass": "*" } }, "sha512-QlKW3AFoFr/hvPHwFHMIVUH/ZCYeetBNou3PCmxu5LaNDvrtBlPJtIA6uhmU9JRt9oxj7IYoqoLcpxtzpPiTcw=="],
|
||||
@@ -610,6 +727,8 @@
|
||||
|
||||
"brace-expansion": ["brace-expansion@5.0.5", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.5.tgz", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
|
||||
|
||||
"braces": ["braces@3.0.3", "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||
|
||||
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.11", "https://registry.npmmirror.com/bun-types/-/bun-types-1.3.11.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
||||
@@ -642,12 +761,16 @@
|
||||
|
||||
"code-excerpt": ["code-excerpt@4.0.0", "https://registry.npmmirror.com/code-excerpt/-/code-excerpt-4.0.0.tgz", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
|
||||
|
||||
"color": ["color@4.2.3", "https://registry.npmmirror.com/color/-/color-4.2.3.tgz", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-diff-napi": ["color-diff-napi@workspace:packages/color-diff-napi"],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"color-string": ["color-string@1.9.1", "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"commander": ["commander@13.1.0", "https://registry.npmmirror.com/commander/-/commander-13.1.0.tgz", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="],
|
||||
@@ -738,18 +861,26 @@
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-glob": ["fast-glob@3.3.3", "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.0", "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||
|
||||
"fast-xml-builder": ["fast-xml-builder@1.1.4", "https://registry.npmmirror.com/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg=="],
|
||||
|
||||
"fast-xml-parser": ["fast-xml-parser@5.5.8", "https://registry.npmmirror.com/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
||||
|
||||
"fastq": ["fastq@1.20.1", "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
|
||||
|
||||
"fd-package-json": ["fd-package-json@2.0.0", "https://registry.npmmirror.com/fd-package-json/-/fd-package-json-2.0.0.tgz", { "dependencies": { "walk-up-path": "^4.0.0" } }, "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ=="],
|
||||
|
||||
"fetch-blob": ["fetch-blob@3.2.0", "https://registry.npmmirror.com/fetch-blob/-/fetch-blob-3.2.0.tgz", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||
|
||||
"fflate": ["fflate@0.8.2", "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||
|
||||
"figures": ["figures@6.1.0", "https://registry.npmmirror.com/figures/-/figures-6.1.0.tgz", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
||||
"finalhandler": ["finalhandler@2.1.1", "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
||||
|
||||
"find-up": ["find-up@4.1.0", "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
||||
@@ -760,6 +891,8 @@
|
||||
|
||||
"form-data": ["form-data@4.0.5", "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
|
||||
|
||||
"formatly": ["formatly@0.3.0", "https://registry.npmmirror.com/formatly/-/formatly-0.3.0.tgz", { "dependencies": { "fd-package-json": "^2.0.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w=="],
|
||||
|
||||
"formdata-polyfill": ["formdata-polyfill@4.0.10", "https://registry.npmmirror.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||
|
||||
"forwarded": ["forwarded@0.2.0", "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||
@@ -790,8 +923,12 @@
|
||||
|
||||
"get-stream": ["get-stream@9.0.1", "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
|
||||
|
||||
"get-tsconfig": ["get-tsconfig@4.13.7", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.13.7.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q=="],
|
||||
|
||||
"glob": ["glob@13.0.6", "https://registry.npmmirror.com/glob/-/glob-13.0.6.tgz", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
|
||||
|
||||
"glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"google-auth-library": ["google-auth-library@10.6.2", "https://registry.npmmirror.com/google-auth-library/-/google-auth-library-10.6.2.tgz", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw=="],
|
||||
|
||||
"google-logging-utils": ["google-logging-utils@1.1.3", "https://registry.npmmirror.com/google-logging-utils/-/google-logging-utils-1.1.3.tgz", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
|
||||
@@ -836,12 +973,20 @@
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
||||
"is-arrayish": ["is-arrayish@0.3.4", "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.4.tgz", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
|
||||
|
||||
"is-docker": ["is-docker@3.0.0", "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-inside-container": ["is-inside-container@1.0.0", "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
|
||||
|
||||
"is-number": ["is-number@7.0.0", "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||
|
||||
"is-plain-obj": ["is-plain-obj@4.1.0", "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
|
||||
"is-promise": ["is-promise@4.0.0", "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||
@@ -856,6 +1001,8 @@
|
||||
|
||||
"isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jiti": ["jiti@2.6.1", "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||
|
||||
"jose": ["jose@6.2.2", "https://registry.npmmirror.com/jose/-/jose-6.2.2.tgz", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="],
|
||||
|
||||
"json-bigint": ["json-bigint@1.0.0", "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
|
||||
@@ -876,6 +1023,8 @@
|
||||
|
||||
"jws": ["jws@4.0.1", "https://registry.npmmirror.com/jws/-/jws-4.0.1.tgz", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
|
||||
|
||||
"knip": ["knip@6.1.1", "https://registry.npmmirror.com/knip/-/knip-6.1.1.tgz", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "get-tsconfig": "4.13.7", "jiti": "^2.6.0", "minimist": "^1.2.8", "oxc-parser": "^0.121.0", "oxc-resolver": "^11.19.1", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", "unbash": "^2.2.0", "yaml": "^2.8.2", "zod": "^4.1.11" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-BC/kbdxwCgv+p/3YkGbtlLxbOXhQDuR+CeKKFEpJyKb3BFwG1gZa+CMWSqAnPi+kUexz74m327d3zWxyn2fMew=="],
|
||||
|
||||
"locate-path": ["locate-path@5.0.0", "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
|
||||
|
||||
"lodash-es": ["lodash-es@4.17.23", "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.23.tgz", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
|
||||
@@ -910,12 +1059,18 @@
|
||||
|
||||
"merge-descriptors": ["merge-descriptors@2.0.0", "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"mime-db": ["mime-db@1.54.0", "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"mime-types": ["mime-types@3.0.2", "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||
|
||||
"minimatch": ["minimatch@10.2.5", "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.5.tgz", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"minipass": ["minipass@7.1.3", "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"minipass-collect": ["minipass-collect@2.0.1", "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-2.0.1.tgz", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="],
|
||||
@@ -954,6 +1109,10 @@
|
||||
|
||||
"os-tmpdir": ["os-tmpdir@1.0.2", "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="],
|
||||
|
||||
"oxc-parser": ["oxc-parser@0.121.0", "https://registry.npmmirror.com/oxc-parser/-/oxc-parser-0.121.0.tgz", { "dependencies": { "@oxc-project/types": "^0.121.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm-eabi": "0.121.0", "@oxc-parser/binding-android-arm64": "0.121.0", "@oxc-parser/binding-darwin-arm64": "0.121.0", "@oxc-parser/binding-darwin-x64": "0.121.0", "@oxc-parser/binding-freebsd-x64": "0.121.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.121.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.121.0", "@oxc-parser/binding-linux-arm64-gnu": "0.121.0", "@oxc-parser/binding-linux-arm64-musl": "0.121.0", "@oxc-parser/binding-linux-ppc64-gnu": "0.121.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.121.0", "@oxc-parser/binding-linux-riscv64-musl": "0.121.0", "@oxc-parser/binding-linux-s390x-gnu": "0.121.0", "@oxc-parser/binding-linux-x64-gnu": "0.121.0", "@oxc-parser/binding-linux-x64-musl": "0.121.0", "@oxc-parser/binding-openharmony-arm64": "0.121.0", "@oxc-parser/binding-wasm32-wasi": "0.121.0", "@oxc-parser/binding-win32-arm64-msvc": "0.121.0", "@oxc-parser/binding-win32-ia32-msvc": "0.121.0", "@oxc-parser/binding-win32-x64-msvc": "0.121.0" } }, "sha512-ek9o58+SCv6AV7nchiAcUJy1DNE2CC5WRdBcO0mF+W4oRjNQfPO7b3pLjTHSFECpHkKGOZSQxx3hk8viIL5YCg=="],
|
||||
|
||||
"oxc-resolver": ["oxc-resolver@11.19.1", "https://registry.npmmirror.com/oxc-resolver/-/oxc-resolver-11.19.1.tgz", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.19.1", "@oxc-resolver/binding-android-arm64": "11.19.1", "@oxc-resolver/binding-darwin-arm64": "11.19.1", "@oxc-resolver/binding-darwin-x64": "11.19.1", "@oxc-resolver/binding-freebsd-x64": "11.19.1", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.19.1", "@oxc-resolver/binding-linux-arm-musleabihf": "11.19.1", "@oxc-resolver/binding-linux-arm64-gnu": "11.19.1", "@oxc-resolver/binding-linux-arm64-musl": "11.19.1", "@oxc-resolver/binding-linux-ppc64-gnu": "11.19.1", "@oxc-resolver/binding-linux-riscv64-gnu": "11.19.1", "@oxc-resolver/binding-linux-riscv64-musl": "11.19.1", "@oxc-resolver/binding-linux-s390x-gnu": "11.19.1", "@oxc-resolver/binding-linux-x64-gnu": "11.19.1", "@oxc-resolver/binding-linux-x64-musl": "11.19.1", "@oxc-resolver/binding-openharmony-arm64": "11.19.1", "@oxc-resolver/binding-wasm32-wasi": "11.19.1", "@oxc-resolver/binding-win32-arm64-msvc": "11.19.1", "@oxc-resolver/binding-win32-ia32-msvc": "11.19.1", "@oxc-resolver/binding-win32-x64-msvc": "11.19.1" } }, "sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg=="],
|
||||
|
||||
"p-limit": ["p-limit@2.3.0", "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
||||
|
||||
"p-locate": ["p-locate@4.1.0", "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
||||
@@ -980,6 +1139,8 @@
|
||||
|
||||
"path-to-regexp": ["path-to-regexp@8.4.1", "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.4.1.tgz", {}, "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.4", "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
|
||||
"pkce-challenge": ["pkce-challenge@5.0.1", "https://registry.npmmirror.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
||||
@@ -1004,6 +1165,8 @@
|
||||
|
||||
"qs": ["qs@6.15.0", "https://registry.npmmirror.com/qs/-/qs-6.15.0.tgz", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"range-parser": ["range-parser@1.2.1", "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
"raw-body": ["raw-body@3.0.2", "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.2.tgz", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||
@@ -1022,12 +1185,18 @@
|
||||
|
||||
"require-main-filename": ["require-main-filename@2.0.0", "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="],
|
||||
|
||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||
|
||||
"retry": ["retry@0.12.0", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
|
||||
|
||||
"reusify": ["reusify@1.1.0", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
"router": ["router@2.2.0", "https://registry.npmmirror.com/router/-/router-2.2.0.tgz", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||
|
||||
"run-applescript": ["run-applescript@7.1.0", "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.1.0.tgz", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
@@ -1062,6 +1231,10 @@
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"simple-swizzle": ["simple-swizzle@0.2.4", "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
|
||||
|
||||
"smol-toml": ["smol-toml@1.6.1", "https://registry.npmmirror.com/smol-toml/-/smol-toml-1.6.1.tgz", {}, "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg=="],
|
||||
|
||||
"ssri": ["ssri@13.0.1", "https://registry.npmmirror.com/ssri/-/ssri-13.0.1.tgz", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
|
||||
|
||||
"stack-utils": ["stack-utils@2.0.6", "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
|
||||
@@ -1074,6 +1247,8 @@
|
||||
|
||||
"strip-final-newline": ["strip-final-newline@4.0.0", "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@5.0.3", "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-5.0.3.tgz", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="],
|
||||
|
||||
"strnum": ["strnum@2.2.2", "https://registry.npmmirror.com/strnum/-/strnum-2.2.2.tgz", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
||||
|
||||
"supports-color": ["supports-color@10.2.2", "https://registry.npmmirror.com/supports-color/-/supports-color-10.2.2.tgz", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
||||
@@ -1088,6 +1263,8 @@
|
||||
|
||||
"tmp": ["tmp@0.0.33", "https://registry.npmmirror.com/tmp/-/tmp-0.0.33.tgz", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"tr46": ["tr46@0.0.3", "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||
@@ -1106,6 +1283,8 @@
|
||||
|
||||
"typescript": ["typescript@6.0.2", "https://registry.npmmirror.com/typescript/-/typescript-6.0.2.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
|
||||
|
||||
"unbash": ["unbash@2.2.0", "https://registry.npmmirror.com/unbash/-/unbash-2.2.0.tgz", {}, "sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w=="],
|
||||
|
||||
"undici": ["undici@7.24.6", "https://registry.npmmirror.com/undici/-/undici-7.24.6.tgz", {}, "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA=="],
|
||||
|
||||
"undici-types": ["undici-types@7.18.2", "https://registry.npmmirror.com/undici-types/-/undici-types-7.18.2.tgz", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
@@ -1130,6 +1309,8 @@
|
||||
|
||||
"vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
|
||||
|
||||
"walk-up-path": ["walk-up-path@4.0.0", "https://registry.npmmirror.com/walk-up-path/-/walk-up-path-4.0.0.tgz", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="],
|
||||
|
||||
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@3.0.1", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||
@@ -1470,6 +1651,10 @@
|
||||
|
||||
"http-proxy-agent/agent-base": ["agent-base@7.1.4", "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"image-processor-napi/sharp": ["sharp@0.33.5", "https://registry.npmmirror.com/sharp/-/sharp-0.33.5.tgz", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.2", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"minipass-flush/minipass": ["minipass@3.3.6", "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
"minipass-pipeline/minipass": ["minipass@3.3.6", "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
@@ -1608,6 +1793,44 @@
|
||||
|
||||
"gtoken/gaxios/uuid": ["uuid@9.0.1", "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "https://registry.npmmirror.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "https://registry.npmmirror.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "https://registry.npmmirror.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "https://registry.npmmirror.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "https://registry.npmmirror.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "https://registry.npmmirror.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="],
|
||||
|
||||
"image-processor-napi/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
|
||||
|
||||
"qrcode/yargs/cliui": ["cliui@6.0.0", "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
||||
|
||||
"qrcode/yargs/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
3
bunfig.toml
Normal file
3
bunfig.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[test]
|
||||
root = "."
|
||||
timeout = 10000
|
||||
58
docs/agent/coordinator-and-swarm.mdx
Normal file
58
docs/agent/coordinator-and-swarm.mdx
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "协调者与蜂群"
|
||||
description: "从单兵作战到团队协作——多 Agent 的高级编排模式"
|
||||
---
|
||||
|
||||
{/* 本章目标:介绍 Coordinator Mode 和 Agent Swarms */}
|
||||
|
||||
## 两种协作模式
|
||||
|
||||
子 Agent 是"临时帮手"——主 Agent 派出去做一件事就回来。对于更复杂的协作需求,Claude Code 提供了两种高级模式:
|
||||
|
||||
## Coordinator Mode:一个指挥,多个执行
|
||||
|
||||
就像一个团队 leader 带着几个开发者:
|
||||
|
||||
- **Coordinator**(协调者):负责理解需求、拆解任务、分配工作、汇总结果
|
||||
- **Workers**(执行者):各自领取任务独立执行,通过邮箱向 Coordinator 汇报
|
||||
|
||||
```
|
||||
┌─── Worker A (重构 API)
|
||||
│
|
||||
Coordinator ──┼─── Worker B (更新测试)
|
||||
│
|
||||
└─── Worker C (更新文档)
|
||||
```
|
||||
|
||||
Coordinator 不自己写代码,它的职责是**编排**——确保所有 Worker 的工作能拼合在一起。
|
||||
|
||||
## Agent Swarms:蜂群式协作
|
||||
|
||||
比 Coordinator 更松散的协作模式:
|
||||
|
||||
- 多个 Agent 以对等身份同时工作
|
||||
- 没有中心化的指挥者
|
||||
- 通过消息邮箱互相通信和协调
|
||||
- 适合"各自负责一块、偶尔需要沟通"的场景
|
||||
|
||||
## Teammate 机制
|
||||
|
||||
进程内的"队友"——一种更轻量的协作方式:
|
||||
|
||||
- 在同一个进程内运行,共享部分基础设施状态
|
||||
- 有独立的对话上下文和工具权限
|
||||
- 适合"我需要一个搭档帮忙看看这段代码"的场景
|
||||
|
||||
## 任务类型
|
||||
|
||||
支撑多 Agent 协作的是丰富的任务类型:
|
||||
|
||||
| 任务类型 | 用途 |
|
||||
|----------|------|
|
||||
| **LocalAgentTask** | 本地子 Agent 任务 |
|
||||
| **LocalShellTask** | 后台 shell 命令 |
|
||||
| **InProcessTeammateTask** | 进程内队友 |
|
||||
| **RemoteAgentTask** | 远程 Agent |
|
||||
| **DreamTask** | 后台自主任务 |
|
||||
|
||||
每种任务类型都有自己的生命周期管理、状态追踪和通信方式。
|
||||
69
docs/agent/sub-agents.mdx
Normal file
69
docs/agent/sub-agents.mdx
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: "子 Agent:分身术"
|
||||
description: "当一个 AI 不够用时,它会召唤更多的自己"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释子 Agent 机制的设计和应用场景 */}
|
||||
|
||||
## 为什么需要子 Agent
|
||||
|
||||
有些任务太大,一个 AI 实例忙不过来:
|
||||
|
||||
- "在 5 个不同的文件中分别找到并修复同类 bug"
|
||||
- "一边重构后端 API,一边更新前端调用"
|
||||
- "研究这个库的用法,同时修改我们的代码"
|
||||
|
||||
## 分身术的运作方式
|
||||
|
||||
Claude Code 中的 Agent 工具让 AI 能够**启动另一个 AI 实例**来处理子任务:
|
||||
|
||||
<Steps>
|
||||
<Step title="主 Agent 分析任务">
|
||||
主 Agent 判断任务可以被拆解为独立的子任务
|
||||
</Step>
|
||||
<Step title="启动子 Agent">
|
||||
通过 Agent 工具创建一个或多个子 Agent,每个子 Agent 收到一个清晰的子任务描述
|
||||
</Step>
|
||||
<Step title="并行执行">
|
||||
多个子 Agent 可以同时工作,互不干扰
|
||||
</Step>
|
||||
<Step title="结果汇总">
|
||||
子 Agent 完成后,结果返回给主 Agent,主 Agent 汇总并呈现给用户
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 子 Agent 的边界
|
||||
|
||||
子 Agent 不是和主 Agent 完全一样的——它有明确的能力边界:
|
||||
|
||||
| 特性 | 主 Agent | 子 Agent |
|
||||
|------|---------|---------|
|
||||
| 可用工具 | 全部工具 | 受限子集(不能再启动子 Agent 等) |
|
||||
| 上下文 | 完整的会话历史 | 只有主 Agent 给的任务描述 |
|
||||
| 权限 | 用户设定 | 继承主 Agent 的权限,或更严格 |
|
||||
| 状态 | 可修改全局状态 | 隔离的状态空间 |
|
||||
|
||||
## 通信方式
|
||||
|
||||
主 Agent 和子 Agent 之间通过**消息邮箱**通信:
|
||||
|
||||
- 主 Agent 通过 `Agent` 工具启动子 Agent
|
||||
- 子 Agent 通过 `SendMessage` 工具向主 Agent 报告进度
|
||||
- 这种松耦合的通信方式让 Agent 可以异步协作
|
||||
|
||||
## 适用场景
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="并行研究" icon="magnifying-glass">
|
||||
多个子 Agent 同时搜索不同方向的信息
|
||||
</Card>
|
||||
<Card title="分治修改" icon="code-branch">
|
||||
把大规模修改拆分到多个子 Agent 并行执行
|
||||
</Card>
|
||||
<Card title="前后台配合" icon="layer-group">
|
||||
一个子 Agent 在后台运行测试,主 Agent 继续写代码
|
||||
</Card>
|
||||
<Card title="隔离实验" icon="flask">
|
||||
在 worktree 中启动子 Agent 尝试一个方案,不影响主分支
|
||||
</Card>
|
||||
</CardGroup>
|
||||
55
docs/agent/worktree-isolation.mdx
Normal file
55
docs/agent/worktree-isolation.mdx
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "Worktree 隔离"
|
||||
description: "给子 Agent 一个独立的工作空间,互不污染"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 git worktree 在多 Agent 协作中的作用 */}
|
||||
|
||||
## 问题:多个 Agent 改同一份代码
|
||||
|
||||
当多个 Agent 同时修改项目文件时,冲突在所难免:
|
||||
|
||||
- Agent A 修改了 `config.ts`,Agent B 也在改同一个文件
|
||||
- Agent A 的测试需要某个状态,Agent B 的修改破坏了它
|
||||
- 半完成的修改混在一起,无法分辨哪些是哪个 Agent 做的
|
||||
|
||||
## 解决方案:Git Worktree
|
||||
|
||||
Git 原生支持 **worktree**(工作树)——在同一个仓库中创建多个独立的工作目录,每个目录在自己的分支上独立工作。
|
||||
|
||||
Claude Code 利用这个特性为子 Agent 提供隔离的工作空间:
|
||||
|
||||
| | 共享工作目录 | Worktree 隔离 |
|
||||
|---|---|---|
|
||||
| 文件冲突 | 多个 Agent 可能互相覆盖 | 每个 Agent 在自己的目录中工作 |
|
||||
| 分支 | 都在同一个分支上 | 每个 Agent 有自己的分支 |
|
||||
| 测试 | 互相干扰 | 完全独立 |
|
||||
| 合并 | 需要手动处理冲突 | 通过 git merge 有序合并 |
|
||||
|
||||
## 工作流程
|
||||
|
||||
<Steps>
|
||||
<Step title="创建 Worktree">
|
||||
AI 启动带隔离模式的子 Agent,系统自动在 `.claude/worktrees/` 下创建新的工作目录
|
||||
</Step>
|
||||
<Step title="独立工作">
|
||||
子 Agent 在自己的 worktree 中自由修改文件、执行命令
|
||||
</Step>
|
||||
<Step title="完成任务">
|
||||
子 Agent 完成后,变更留在 worktree 的分支上
|
||||
</Step>
|
||||
<Step title="合并或丢弃">
|
||||
主 Agent(或用户)决定:合并这些变更到主分支,还是丢弃
|
||||
</Step>
|
||||
<Step title="清理">
|
||||
不再需要的 worktree 会被自动清理
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 安全网
|
||||
|
||||
Worktree 还充当了一个安全网:
|
||||
|
||||
- 子 Agent 的实验性修改不会影响主分支
|
||||
- 如果方案不可行,整个 worktree 可以直接丢弃
|
||||
- 多个方案可以在不同 worktree 中并行尝试,最后选最好的
|
||||
63
docs/context/compaction.mdx
Normal file
63
docs/context/compaction.mdx
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: "上下文压缩"
|
||||
description: "对话太长怎么办——优雅地'遗忘'不重要的信息"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 Compaction 机制的设计和策略 */}
|
||||
|
||||
## 为什么需要压缩
|
||||
|
||||
每次 API 调用的 token 有上限(通常 200K)。一场长时间的编程对话可能产生:
|
||||
|
||||
- 大量的文件内容(AI 读了几十个文件)
|
||||
- 长篇的命令输出(构建日志、测试结果)
|
||||
- 往返的对话历史
|
||||
|
||||
不压缩的话,很快就会撞到 token 上限,对话被迫终止。
|
||||
|
||||
## 压缩的策略
|
||||
|
||||
Claude Code 提供了多层压缩机制:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="自动压缩">
|
||||
当 token 接近上限时,系统自动触发压缩。AI 生成一份当前对话的**摘要**,替换掉早期的详细消息。效果就像人类的"记忆"——记住要点,忘记细节。
|
||||
</Accordion>
|
||||
<Accordion title="手动压缩">
|
||||
用户可以随时通过 `/compact` 命令主动触发压缩。可以附带提示语(如 `/compact 聚焦在认证模块的修改上`),引导 AI 保留特定信息。
|
||||
</Accordion>
|
||||
<Accordion title="Micro Compact">
|
||||
更细粒度的局部压缩——不是压缩整个对话,而是压缩某些特别长的工具输出(比如一个 5000 行的测试日志)。
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## 压缩边界
|
||||
|
||||
压缩后,系统在消息历史中插入一个"边界标记"。后续的 API 调用只发送边界之后的消息:
|
||||
|
||||
```
|
||||
[早期的 50 条消息] ← 被压缩
|
||||
[压缩摘要边界] ← 一段浓缩的摘要
|
||||
[后续的 10 条消息] ← 正常发送
|
||||
```
|
||||
|
||||
这个设计保证了:
|
||||
- 压缩后的摘要为 AI 提供了历史上下文
|
||||
- 新的对话不受旧消息的 token 负担
|
||||
- 用户无感知——对话继续自然进行
|
||||
|
||||
## 压缩前后的 Hooks
|
||||
|
||||
压缩是一个可能丢失信息的操作,因此系统允许用户在压缩前后执行自定义脚本:
|
||||
|
||||
- **Pre-compact Hook**:压缩前执行,可以标记"这些信息不能丢"
|
||||
- **Post-compact Hook**:压缩后执行,可以验证关键信息是否保留
|
||||
|
||||
## 什么信息会被保留
|
||||
|
||||
压缩不是简单的截断,AI 会智能地决定保留什么:
|
||||
|
||||
- 用户的核心需求和目标
|
||||
- 重要的决策和原因
|
||||
- 当前工作的状态(改了哪些文件、做到哪一步)
|
||||
- 之前犯过的错误(避免重蹈覆辙)
|
||||
58
docs/context/project-memory.mdx
Normal file
58
docs/context/project-memory.mdx
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "项目记忆"
|
||||
description: "让 AI 跨对话记住你的偏好和项目上下文"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释记忆系统如何让 AI 变得'有记忆' */}
|
||||
|
||||
## AI 的记忆困境
|
||||
|
||||
大语言模型没有真正的记忆。每次新对话,它都是一张白纸。用户不得不反复解释"我的项目用 Bun 不用 Node"、"commit 消息用中文"。
|
||||
|
||||
## 记忆系统的解决方案
|
||||
|
||||
Claude Code 通过一个基于文件的持久化记忆系统来模拟"跨会话记忆":
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="用户记忆" icon="user">
|
||||
关于用户的信息:角色、偏好、技术背景
|
||||
</Card>
|
||||
<Card title="反馈记忆" icon="message">
|
||||
用户对 AI 行为的纠正和肯定
|
||||
</Card>
|
||||
<Card title="项目记忆" icon="folder">
|
||||
项目中的非代码信息:谁负责什么、截止日期
|
||||
</Card>
|
||||
<Card title="参考记忆" icon="link">
|
||||
外部资源的位置:Issue tracker、Dashboard URL
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 记忆的读写时机
|
||||
|
||||
| 时机 | 动作 |
|
||||
|------|------|
|
||||
| 每次对话开始 | 加载记忆索引(MEMORY.md),相关记忆注入 System Prompt |
|
||||
| 用户纠正 AI | AI 自动判断是否值得记住,写入反馈记忆 |
|
||||
| 用户说"记住这个" | 立即保存到对应类型的记忆文件 |
|
||||
| 用户说"忘掉这个" | 找到并删除对应的记忆条目 |
|
||||
| 记忆可能过期时 | 使用前先验证(文件还在?函数还存在?),过期则更新或删除 |
|
||||
|
||||
## 记忆 vs 代码注释 vs CLAUDE.md
|
||||
|
||||
| | 记忆 | 代码注释 | CLAUDE.md |
|
||||
|---|---|---|---|
|
||||
| 存储位置 | `~/.claude/` 目录 | 代码文件中 | 项目目录中 |
|
||||
| 谁能看到 | 只有当前用户 | 所有开发者 | 所有使用 Claude Code 的人 |
|
||||
| 适合存什么 | 个人偏好、非公开的上下文 | 代码逻辑解释 | 项目约定、开发指南 |
|
||||
| 跨项目 | 是 | 否 | 否 |
|
||||
|
||||
## 不该存什么
|
||||
|
||||
记忆系统明确规定了不应存储的内容:
|
||||
|
||||
- 代码结构和架构(读代码就知道)
|
||||
- git 历史(`git log` 就能查)
|
||||
- 调试方案(修复已在代码中)
|
||||
- CLAUDE.md 里已有的内容(避免重复)
|
||||
- 临时性任务状态(用任务系统)
|
||||
53
docs/context/system-prompt.mdx
Normal file
53
docs/context/system-prompt.mdx
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "System Prompt 的动态组装"
|
||||
description: "AI 的'工作记忆'是如何在每次对话前被精心拼装的"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 System Prompt 的组装过程和设计思想 */}
|
||||
|
||||
## 什么是 System Prompt
|
||||
|
||||
每次调用 AI API 时,都需要发送一个 System Prompt——它是 AI 的"人设说明书",告诉 AI:
|
||||
|
||||
- 你是谁(Claude Code,一个编程助手)
|
||||
- 你能做什么(可用工具列表)
|
||||
- 你在什么环境(操作系统、当前目录、git 状态)
|
||||
- 你需要遵守什么规则(安全规范、输出格式)
|
||||
|
||||
## 不是静态模板,而是动态组装
|
||||
|
||||
Claude Code 的 System Prompt 不是一段写死的文本,而是根据当前环境**实时组装**的:
|
||||
|
||||
| 组成部分 | 内容 | 来源 |
|
||||
|----------|------|------|
|
||||
| 基础人设 | 角色定义、行为准则 | 内置模板 |
|
||||
| 环境信息 | 操作系统、shell 类型、当前日期 | 运行时检测 |
|
||||
| Git 状态 | 当前分支、最近提交、工作区状态 | `git` 命令输出 |
|
||||
| 项目知识 | CLAUDE.md 文件内容 | 项目目录层级扫描 |
|
||||
| 记忆文件 | 用户偏好、项目约定 | 持久化记忆系统 |
|
||||
| 工具说明 | 每个可用工具的描述和参数 | 工具注册表 |
|
||||
|
||||
## CLAUDE.md:项目级知识注入
|
||||
|
||||
这是 Claude Code 最巧妙的设计之一。在项目根目录放一个 `CLAUDE.md` 文件,就能让 AI "理解" 你的项目:
|
||||
|
||||
- **项目概述**:这个项目做什么、用了什么技术栈
|
||||
- **开发约定**:代码风格、命名规范、分支策略
|
||||
- **常用命令**:怎么构建、怎么测试、怎么部署
|
||||
- **注意事项**:已知的坑、特殊的配置
|
||||
|
||||
系统会自动发现并合并多级 CLAUDE.md:
|
||||
|
||||
```
|
||||
~/.claude/CLAUDE.md ← 用户全局(个人偏好)
|
||||
└── /project/CLAUDE.md ← 项目根目录(团队共享)
|
||||
└── /project/src/CLAUDE.md ← 子目录(模块特定)
|
||||
```
|
||||
|
||||
## 缓存策略
|
||||
|
||||
System Prompt 的 token 消耗不小(可能占总量的 30%+)。为了降低成本,系统使用了缓存机制:
|
||||
|
||||
- 不变的部分(基础人设、工具说明)可以跨请求复用
|
||||
- 变化的部分(git 状态、记忆文件)每次重新生成
|
||||
- 缓存节点的位置经过精心设计,最大化缓存命中率
|
||||
55
docs/context/token-budget.mdx
Normal file
55
docs/context/token-budget.mdx
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "Token 预算管理"
|
||||
description: "精打细算每一个 token——AI 的'注意力'是有限资源"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 token 预算管理的思路 */}
|
||||
|
||||
## Token 是什么
|
||||
|
||||
简单理解:token 约等于一个英文单词或半个中文字。AI 处理的所有输入和输出都按 token 计费。
|
||||
|
||||
| 类型 | 说明 | 谁付费 |
|
||||
|------|------|--------|
|
||||
| 输入 token | 发给 AI 的所有内容(System Prompt + 对话历史 + 工具结果) | 用户 |
|
||||
| 输出 token | AI 生成的回复和工具调用 | 用户 |
|
||||
| 缓存 token | 重复发送的内容如果命中缓存,价格更低 | 部分用户 |
|
||||
|
||||
## 预算控制的三个层面
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="单次请求" icon="1">
|
||||
每次 API 调用的最大输入/输出 token
|
||||
</Card>
|
||||
<Card title="单轮对话" icon="arrows-rotate">
|
||||
一个 Agentic Loop 内的累计 token 消耗
|
||||
</Card>
|
||||
<Card title="整个会话" icon="clock">
|
||||
全部对话轮次的累计花费(美元)
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 工具输出的预算控制
|
||||
|
||||
工具返回的内容可能非常长(一个大文件、一段长日志),直接全部塞给 AI 会浪费大量 token。系统对此有专门的控制:
|
||||
|
||||
- **结果截断**:超过长度限制的工具输出自动截断
|
||||
- **结果替换**:已经被 AI"消化"过的旧工具结果,可以被替换为简短的摘要
|
||||
- **按需读取**:大文件不一次性读完,AI 可以指定读取范围
|
||||
|
||||
## 缓存的经济学
|
||||
|
||||
System Prompt 每次都要发送,但大部分内容不变。缓存机制让这部分"免费"(或大幅降价):
|
||||
|
||||
- 首次发送:全价
|
||||
- 后续请求命中缓存:约 1/10 的价格
|
||||
- 这就是为什么 System Prompt 的结构被精心设计——不变的部分放前面,变化的部分放后面
|
||||
|
||||
## token 警告与自动压缩
|
||||
|
||||
| token 使用率 | 系统行为 |
|
||||
|-------------|---------|
|
||||
| < 70% | 正常运行 |
|
||||
| 70% ~ 90% | 显示警告,提示用户可以手动压缩 |
|
||||
| > 90% | 自动触发压缩 |
|
||||
| 接近 100% | 强制压缩或终止当前轮次 |
|
||||
59
docs/conversation/multi-turn.mdx
Normal file
59
docs/conversation/multi-turn.mdx
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "多轮对话管理"
|
||||
description: "一场跨越数小时的编程对话是如何被管理的"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释会话编排、持久化、成本追踪 */}
|
||||
|
||||
## 单轮 vs 多轮
|
||||
|
||||
- **单轮**(一次 Agentic Loop):用户说一句 → AI 执行一系列操作 → 回答
|
||||
- **多轮**(一个 Session):用户和 AI 来回对话几十轮,持续数小时
|
||||
|
||||
多轮对话带来的挑战远超单轮:消息越来越多、token 不断累积、上下文逐渐模糊。
|
||||
|
||||
## 会话编排器的职责
|
||||
|
||||
在单轮 Agentic Loop 之上,有一个编排器负责管理整个会话生命周期:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="对话状态管理" icon="database">
|
||||
维护完整的消息历史,包括用户消息、AI 回复、工具调用结果
|
||||
</Card>
|
||||
<Card title="会话持久化" icon="floppy-disk">
|
||||
自动保存对话记录到磁盘,支持断线重连、历史回顾
|
||||
</Card>
|
||||
<Card title="文件快照" icon="camera">
|
||||
在 AI 修改文件前自动保存快照,支持回滚
|
||||
</Card>
|
||||
<Card title="成本追踪" icon="calculator">
|
||||
精确记录每轮的 token 消耗和 API 费用
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 会话恢复
|
||||
|
||||
意外退出?网络断了?没关系:
|
||||
|
||||
- 每轮对话结束后,完整的 transcript 会被写入磁盘
|
||||
- 下次启动时,可以选择恢复之前的对话
|
||||
- 恢复时,系统重建消息历史和上下文状态
|
||||
|
||||
## 成本感知
|
||||
|
||||
AI 编程助手的一个现实问题是**费用可能失控**。Claude Code 内建了多层成本控制:
|
||||
|
||||
| 机制 | 作用 |
|
||||
|------|------|
|
||||
| Token 计数器 | 实时显示本次会话已消耗的输入/输出 token |
|
||||
| 费用估算 | 根据模型定价计算累计美元花费 |
|
||||
| 预算上限 | 用户可设定最大花费,到达后自动停止 |
|
||||
| 压缩提醒 | Token 接近上限时提示用户触发压缩 |
|
||||
|
||||
## 模型切换
|
||||
|
||||
在一个会话中,用户可以随时切换模型或调整参数:
|
||||
|
||||
- `/model` 切换到不同的模型(Sonnet / Opus / Haiku)
|
||||
- `/fast` 切换快速模式
|
||||
- 模型切换不会丢失对话历史
|
||||
50
docs/conversation/streaming.mdx
Normal file
50
docs/conversation/streaming.mdx
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: "流式响应:逐字呈现"
|
||||
description: "为什么 Claude Code 的回答是'打字机效果'而不是一整块弹出"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释流式通信的意义和它如何与工具执行交织 */}
|
||||
|
||||
## 为什么需要流式
|
||||
|
||||
想象 AI 需要 30 秒才能生成完整回答——如果等 30 秒后才一次性显示,用户体验是灾难性的。
|
||||
|
||||
流式响应让用户**实时看到 AI 的思考过程**:
|
||||
- 文字逐字出现,用户能提前判断方向是否正确
|
||||
- 工具调用的参数在生成过程中就能预览
|
||||
- 长时间任务不会让用户觉得"卡死了"
|
||||
|
||||
## 流式与工具调用的交织
|
||||
|
||||
一次 AI 响应中可能同时包含文字和工具调用。流式系统需要处理这种交织:
|
||||
|
||||
```
|
||||
AI 开始输出文字 → "我来看看这个文件的内容..."
|
||||
AI 发出工具调用 → [FileRead: src/main.ts]
|
||||
↓ 暂停流式输出
|
||||
工具执行中...
|
||||
结果返回给 AI
|
||||
↓ 恢复流式输出
|
||||
AI 继续输出 → "这个文件里有一个 bug,第 42 行..."
|
||||
AI 发出下一个工具调用 → [FileEdit: src/main.ts]
|
||||
```
|
||||
|
||||
## 流式工具执行
|
||||
|
||||
更进阶的是,**工具本身的执行也可以是流式的**:
|
||||
|
||||
- 运行一个长命令(比如 `npm install`),输出逐行显示
|
||||
- 搜索大型项目时,匹配结果逐条呈现
|
||||
- AI 在等待工具结果的同时,已经开始规划下一步
|
||||
|
||||
## 多 Provider 适配
|
||||
|
||||
Claude Code 支持多个 AI 服务提供商,每个提供商的流式协议略有不同:
|
||||
|
||||
| Provider | 特点 |
|
||||
|----------|------|
|
||||
| Anthropic Direct | 原生 SSE 流,延迟最低 |
|
||||
| AWS Bedrock | 通过 AWS SDK 包装的流式接口 |
|
||||
| Google Vertex | gRPC 流转换为事件流 |
|
||||
|
||||
系统通过统一的事件抽象层屏蔽这些差异——上层代码不需要关心底层用的是哪个 Provider。
|
||||
66
docs/conversation/the-loop.mdx
Normal file
66
docs/conversation/the-loop.mdx
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "Agentic Loop:对话的心跳"
|
||||
description: "AI 不只回答问题,它会反复思考-行动-观察,直到任务完成"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 Agentic Loop 这个核心机制 */}
|
||||
|
||||
## 什么是 Agentic Loop
|
||||
|
||||
传统聊天机器人:你问一句,它答一句。
|
||||
Claude Code 不一样:你说一个需求,它可能连续执行十几步操作才给你最终结果。
|
||||
|
||||
这背后的机制叫做 **Agentic Loop**(智能体循环):
|
||||
|
||||
{/* TODO: 插入 Loop 示意图 */}
|
||||
|
||||
<Steps>
|
||||
<Step title="思考">
|
||||
AI 分析当前上下文,决定下一步该做什么
|
||||
</Step>
|
||||
<Step title="行动">
|
||||
AI 发出工具调用请求(比如"读取这个文件"、"执行这条命令")
|
||||
</Step>
|
||||
<Step title="观察">
|
||||
工具执行完毕,结果回传给 AI
|
||||
</Step>
|
||||
<Step title="循环或结束">
|
||||
AI 根据观察结果决定:继续下一步操作,还是任务已完成、直接回答用户
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 一个真实的例子
|
||||
|
||||
> 用户:"帮我找到项目里所有未使用的导入语句,然后删掉它们"
|
||||
|
||||
AI 的内部过程:
|
||||
|
||||
1. **思考**:我需要先了解项目结构 → **行动**:调用 Glob 工具扫描所有源文件
|
||||
2. **观察**:拿到文件列表 → **思考**:逐个检查 → **行动**:调用 Grep 搜索 import 语句
|
||||
3. **观察**:发现 3 个文件有未使用导入 → **行动**:调用 FileEdit 逐个删除
|
||||
4. **观察**:编辑成功 → **结束**:告诉用户"已清理 3 个文件中的 5 条未使用导入"
|
||||
|
||||
整个过程可能涉及 10+ 次工具调用,但用户只需要说一句话。
|
||||
|
||||
## 为什么不是"一次规划,批量执行"
|
||||
|
||||
<Note>
|
||||
一个常见的替代方案是:AI 先生成一个完整的计划,然后一次性执行所有步骤。Claude Code 选择了逐步循环,原因是:
|
||||
</Note>
|
||||
|
||||
- **每一步都能看到真实结果**:文件内容、命令输出、错误信息——这些只有执行后才知道
|
||||
- **动态调整**:如果第 3 步发现了意外情况,AI 可以立刻改变策略
|
||||
- **错误恢复**:某步失败了,AI 可以当场诊断和修复,不需要推倒重来
|
||||
- **用户可控**:用户可以在任何一步中断,AI 的循环不会失控
|
||||
|
||||
## 循环的终止条件
|
||||
|
||||
Agentic Loop 不会无限运行,以下情况会让循环停止:
|
||||
|
||||
| 条件 | 说明 |
|
||||
|------|------|
|
||||
| AI 主动结束 | AI 判断任务完成,返回纯文本回答(不再调用工具) |
|
||||
| 用户中断 | 用户按 Ctrl+C 或 ESC 打断当前操作 |
|
||||
| Token 预算耗尽 | 单轮对话的 token 用量达到上限 |
|
||||
| 输出过长自动续写 | AI 回复被截断时,系统自动发起续写请求(有次数上限) |
|
||||
| 成本上限 | 累计 API 花费超过用户设定的预算 |
|
||||
56
docs/extensibility/custom-agents.mdx
Normal file
56
docs/extensibility/custom-agents.mdx
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: "自定义 Agent"
|
||||
description: "定义你自己的 AI 角色——不同的人格、能力和边界"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释自定义 Agent 定义的设计 */}
|
||||
|
||||
## 为什么需要自定义 Agent
|
||||
|
||||
默认的 Claude Code 是一个"全能型"助手。但有些场景需要更专门化的角色:
|
||||
|
||||
- 一个只负责代码审查、不会修改代码的 Agent
|
||||
- 一个专门处理 DevOps 任务的 Agent
|
||||
- 一个面向初学者、回答更详细的 Agent
|
||||
|
||||
## Agent 定义
|
||||
|
||||
自定义 Agent 通过 Markdown 文件定义,放在 `.claude/agents/` 目录:
|
||||
|
||||
| 配置项 | 说明 |
|
||||
|--------|------|
|
||||
| **名称** | Agent 的标识和显示名 |
|
||||
| **描述** | 这个 Agent 的职责说明 |
|
||||
| **System Prompt** | 自定义的角色指令——替换或追加到默认 System Prompt |
|
||||
| **允许的工具** | 这个 Agent 可以使用哪些工具 |
|
||||
| **模型** | 使用哪个 AI 模型 |
|
||||
|
||||
## 与子 Agent 的关系
|
||||
|
||||
自定义 Agent 可以作为子 Agent 被启动:
|
||||
|
||||
- 主 Agent 说"这个任务需要安全审查"
|
||||
- 系统启动一个自定义的"安全审查 Agent"
|
||||
- 该 Agent 只有阅读权限,使用专门的安全审查 Prompt
|
||||
|
||||
这实现了**角色分离**——不同的任务由不同"人格"的 Agent 处理。
|
||||
|
||||
## 复用与共享
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="项目级" icon="folder">
|
||||
放在项目的 `.claude/agents/` 目录,团队所有人可用
|
||||
</Card>
|
||||
<Card title="用户级" icon="user">
|
||||
放在 `~/.claude/agents/` 目录,跨项目可用
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 实际应用
|
||||
|
||||
| Agent | 角色 | 工具限制 |
|
||||
|-------|------|---------|
|
||||
| `reviewer` | 代码审查员 | 只允许 Read、Glob、Grep |
|
||||
| `devops` | DevOps 工程师 | 允许 Bash,限制在 infra/ 目录 |
|
||||
| `tutor` | 编程导师 | 全部工具,但 Prompt 强调教学 |
|
||||
| `security` | 安全审计员 | 只允许搜索和阅读,输出安全报告 |
|
||||
72
docs/extensibility/hooks.mdx
Normal file
72
docs/extensibility/hooks.mdx
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: "Hooks:生命周期钩子"
|
||||
description: "在 AI 的关键行为节点插入你自己的逻辑"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 Hooks 系统的设计和应用场景 */}
|
||||
|
||||
## 什么是 Hooks
|
||||
|
||||
Hooks 是用户定义的 shell 命令,在 Claude Code 生命周期的特定时刻自动执行。
|
||||
|
||||
类比:React 的 `useEffect` 让你在组件渲染后执行自定义逻辑。Claude Code 的 Hooks 让你在 AI 的关键行为前后执行自定义脚本。
|
||||
|
||||
## 可用的 Hook 事件
|
||||
|
||||
| 事件 | 触发时机 | 典型用途 |
|
||||
|------|---------|---------|
|
||||
| **PreToolUse** | 工具调用前 | 拦截危险操作、自定义审批逻辑 |
|
||||
| **PostToolUse** | 工具调用后 | 记录日志、触发通知、自动格式化 |
|
||||
| **PreCompact** | 上下文压缩前 | 标记不可丢失的信息 |
|
||||
| **PostCompact** | 上下文压缩后 | 验证关键信息是否保留 |
|
||||
| **Notification** | AI 发出通知时 | 自定义通知渠道(Slack、邮件等) |
|
||||
| **StopFailure** | AI 循环异常停止时 | 自定义错误处理 |
|
||||
|
||||
## Hook 的能力
|
||||
|
||||
Hook 脚本不仅能"观察",还能"干预":
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="拦截操作" icon="hand">
|
||||
返回特定信号可以阻止工具调用执行
|
||||
</Card>
|
||||
<Card title="修改行为" icon="pen">
|
||||
返回结构化的 JSON 输出,影响 Claude Code 的后续行为
|
||||
</Card>
|
||||
<Card title="注入上下文" icon="syringe">
|
||||
向 AI 的对话中注入额外信息
|
||||
</Card>
|
||||
<Card title="触发外部流程" icon="bolt">
|
||||
调用 CI/CD、发送通知、更新 Issue tracker
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 配置方式
|
||||
|
||||
Hooks 在 `settings.json` 中配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": { "tool_name": "Write" },
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "npx prettier --write $CLAUDE_FILE_PATH"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个例子:每当 AI 写入一个文件后,自动用 Prettier 格式化。
|
||||
|
||||
## 安全控制
|
||||
|
||||
- 托管设置(企业管理员)的 Hooks 优先级最高,用户不能覆盖
|
||||
- Hook 执行有超时限制
|
||||
- Hook 的输出会被解析和验证,防止注入攻击
|
||||
75
docs/extensibility/mcp-protocol.mdx
Normal file
75
docs/extensibility/mcp-protocol.mdx
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: "MCP:开放的工具生态"
|
||||
description: "通过标准协议对接任何外部能力——数据库、API、自定义服务"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 MCP 协议如何扩展 AI 的能力边界 */}
|
||||
|
||||
## 内置工具的局限
|
||||
|
||||
Claude Code 内置了 50+ 工具,覆盖了通用的软件开发需求。但每个团队都有特殊需求:
|
||||
|
||||
- 连接内部数据库查询数据
|
||||
- 调用公司内部 API
|
||||
- 操作特定的 DevOps 工具
|
||||
- 访问私有的知识库
|
||||
|
||||
不可能把所有人的需求都内置进去。
|
||||
|
||||
## MCP:一个标准的"插头"
|
||||
|
||||
**Model Context Protocol**(模型上下文协议)是 Anthropic 提出的开放标准,定义了 AI 与外部工具之间的通信方式。
|
||||
|
||||
类比:USB 是电脑连接外设的标准接口。MCP 是 AI 连接外部能力的标准接口。
|
||||
|
||||
## 工作原理
|
||||
|
||||
<Steps>
|
||||
<Step title="启动 MCP Server">
|
||||
开发者编写一个 MCP Server,暴露自定义工具(比如"查询数据库"、"发送 Slack 消息")
|
||||
</Step>
|
||||
<Step title="Claude Code 连接">
|
||||
在配置文件中声明要连接的 MCP Server
|
||||
</Step>
|
||||
<Step title="工具自动发现">
|
||||
连接后,MCP Server 提供的工具自动出现在 AI 的可用工具列表中
|
||||
</Step>
|
||||
<Step title="透明调用">
|
||||
AI 像使用内置工具一样使用 MCP 工具——无需知道底层实现
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 三种连接方式
|
||||
|
||||
| 方式 | 适用场景 |
|
||||
|------|---------|
|
||||
| **stdio** | MCP Server 作为子进程运行,通过标准输入/输出通信。最简单 |
|
||||
| **SSE** | 通过 HTTP Server-Sent Events 通信。适合远程服务 |
|
||||
| **StreamableHTTP** | 基于 HTTP 流的双向通信。适合复杂的交互场景 |
|
||||
|
||||
## 权限一视同仁
|
||||
|
||||
MCP 提供的工具和内置工具一样受权限系统管控:
|
||||
|
||||
- 需要用户确认才能调用
|
||||
- 可以设置 allow/deny 规则
|
||||
- 支持沙箱限制
|
||||
|
||||
这确保了第三方工具不会绕过安全边界。
|
||||
|
||||
## 实际例子
|
||||
|
||||
```json
|
||||
// settings.json 中的 MCP 配置
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-database": {
|
||||
"command": "npx",
|
||||
"args": ["@my-org/db-mcp-server"],
|
||||
"env": { "DB_URL": "postgres://..." }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
配置后,AI 就多了"查询数据库"这个能力——用自然语言描述需求,AI 自动生成查询并执行。
|
||||
60
docs/extensibility/skills.mdx
Normal file
60
docs/extensibility/skills.mdx
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "Skills:预制的能力包"
|
||||
description: "把常用的工作流封装成可复用的技能"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 Skills 系统的设计思想 */}
|
||||
|
||||
## Tool vs Skill
|
||||
|
||||
| | Tool | Skill |
|
||||
|---|---|---|
|
||||
| 粒度 | 单个原子操作(读文件、执行命令) | 一套完整的工作流(代码审查、创建 PR) |
|
||||
| 触发方式 | AI 自主选择 | 用户主动调用(`/skill-name`)或 AI 根据场景推荐 |
|
||||
| 本质 | 执行逻辑 | 预制的 Prompt + 工具权限配置 |
|
||||
|
||||
## Skill 的三个来源
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="内置 Skill" icon="box">
|
||||
编译进 CLI 的技能包。如 `/commit`、`/review`、`/debug`
|
||||
</Card>
|
||||
<Card title="项目 Skill" icon="folder-open">
|
||||
项目 `.claude/skills/` 目录中的 Markdown 文件。团队共享
|
||||
</Card>
|
||||
<Card title="MCP Skill" icon="plug">
|
||||
通过 MCP Server 提供的技能。动态发现
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 一个 Skill 包含什么
|
||||
|
||||
每个 Skill 本质上是一个"AI 行为的预设":
|
||||
|
||||
| 组成部分 | 作用 |
|
||||
|----------|------|
|
||||
| **名称和描述** | 告诉 AI 和用户这个技能做什么 |
|
||||
| **whenToUse** | 什么场景下应该使用这个技能(AI 据此自动推荐) |
|
||||
| **Prompt 模板** | 注入给 AI 的详细指令——相当于"操作手册" |
|
||||
| **allowedTools** | 这个技能允许使用哪些工具(能力边界) |
|
||||
| **model** | 可选指定使用的模型 |
|
||||
|
||||
## 设计精妙之处
|
||||
|
||||
Skill 的核心洞见是:**很多复杂任务的关键不在于代码逻辑,而在于 Prompt 的质量**。
|
||||
|
||||
一个好的代码审查,不是写了什么代码来审查,而是:
|
||||
- 告诉 AI 审查的标准是什么
|
||||
- 告诉 AI 按什么顺序审查
|
||||
- 告诉 AI 输出什么格式的报告
|
||||
- 限制 AI 只能用读取类工具(不要边审查边改代码)
|
||||
|
||||
Skill 把这些"经验"封装起来,任何人都能一键调用。
|
||||
|
||||
## 技能发现
|
||||
|
||||
当可用技能很多时,AI 可以通过 **SkillTool** 搜索匹配的技能:
|
||||
|
||||
- 用户说"帮我做代码审查"
|
||||
- AI 搜索已注册的技能,发现 `code-review` 匹配
|
||||
- AI 调用该技能,按预设的流程执行
|
||||
59
docs/introduction/architecture-overview.mdx
Normal file
59
docs/introduction/architecture-overview.mdx
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "架构全景"
|
||||
description: "五层架构,一条数据流"
|
||||
---
|
||||
|
||||
{/* 本章目标:一张图讲清楚整体架构,为后续章节建立坐标系 */}
|
||||
|
||||
## 五层架构
|
||||
|
||||
Claude Code 从上到下分为五个层次,每一层职责清晰、边界分明:
|
||||
|
||||
| 层次 | 职责 | 关键词 |
|
||||
|------|------|--------|
|
||||
| **交互层** | 终端 UI、用户输入、消息展示 | React/Ink、REPL |
|
||||
| **编排层** | 管理多轮对话、会话生命周期、成本追踪 | QueryEngine、会话持久化 |
|
||||
| **核心循环层** | 单轮对话:发请求 → 拿响应 → 执行工具 → 再发请求 | Agentic Loop |
|
||||
| **工具层** | AI 的"双手"——读写文件、执行命令、搜索代码 | Tool System、MCP |
|
||||
| **通信层** | 与 Claude API 的流式通信、多云 Provider 适配 | Streaming、Bedrock/Vertex |
|
||||
|
||||
## 一条主数据流
|
||||
|
||||
{/* TODO: 插入数据流序列图 */}
|
||||
|
||||
整个系统的运转可以浓缩为一条核心数据流:
|
||||
|
||||
<Steps>
|
||||
<Step title="用户输入">
|
||||
用户在终端键入自然语言需求
|
||||
</Step>
|
||||
<Step title="上下文组装">
|
||||
系统自动拼接项目信息、git 状态、配置文件、记忆,形成完整的 System Prompt
|
||||
</Step>
|
||||
<Step title="API 调用">
|
||||
将 System Prompt + 对话历史发送给 Claude API,流式接收响应
|
||||
</Step>
|
||||
<Step title="工具调用循环">
|
||||
若 AI 返回工具调用请求 → 权限检查 → 执行工具 → 结果回传 → AI 继续思考 → 循环
|
||||
</Step>
|
||||
<Step title="任务完成">
|
||||
AI 不再调用工具,输出最终回答,等待用户下一条输入
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 四个核心设计原则
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="流式优先 (Streaming-first)">
|
||||
所有 API 通信都是流式的——用户看到 AI "逐字打出"回答,而不是等待完整响应。工具执行结果也实时反馈。这不仅提升体验,更是处理长时间任务的工程必需。
|
||||
</Accordion>
|
||||
<Accordion title="工具即能力 (Tool as Capability)">
|
||||
AI 的每一项"动手能力"都被抽象为一个 Tool。想让 AI 能做新事情?注册一个新 Tool 就好。统一的接口让能力可插拔、可组合。
|
||||
</Accordion>
|
||||
<Accordion title="权限即边界 (Permission as Boundary)">
|
||||
AI 能操作真实环境是强大的,也是危险的。每个工具调用都经过权限系统的裁决——该放行的自动放行,该拦截的坚决拦截。
|
||||
</Accordion>
|
||||
<Accordion title="上下文即记忆 (Context as Memory)">
|
||||
AI 没有真正的记忆,但通过精心的 System Prompt 组装、对话压缩、持久化记忆文件,系统营造出"AI 理解你的项目"的效果。这是上下文工程的核心。
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
37
docs/introduction/what-is-claude-code.mdx
Normal file
37
docs/introduction/what-is-claude-code.mdx
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: "什么是 Claude Code"
|
||||
description: "一个住在终端里的 AI 编程搭档"
|
||||
---
|
||||
|
||||
{/* 本章目标:让完全不了解 Claude Code 的读者在 3 分钟内建立直觉 */}
|
||||
|
||||
## 一句话定义
|
||||
|
||||
Claude Code 是一个运行在命令行终端里的 AI 编程助手。你用自然语言描述需求,它直接帮你读代码、改文件、跑命令、搜索项目——全部在你的本地环境中完成。
|
||||
|
||||
## 它能做什么
|
||||
|
||||
- **对话式编程**:像和同事聊天一样描述需求,AI 直接动手实现
|
||||
- **理解整个项目**:自动读取项目结构、git 历史、配置文件,建立项目全景认知
|
||||
- **操作你的电脑**:读写文件、执行 shell 命令、搜索代码——不只是给建议,而是真正动手
|
||||
- **保护你的安全**:每个敏感操作都需要你确认,有沙箱、有权限管控
|
||||
|
||||
## 它和 ChatGPT / 普通聊天机器人的区别
|
||||
|
||||
| | 普通聊天 AI | Claude Code |
|
||||
|---|---|---|
|
||||
| 运行环境 | 云端网页 | 你的本地终端 |
|
||||
| 能做什么 | 回答问题、生成文本 | 直接操作你的项目文件和命令行 |
|
||||
| 项目理解 | 你需要手动粘贴代码 | 自动读取整个项目上下文 |
|
||||
| 安全性 | 无需考虑 | 多层权限保护 |
|
||||
|
||||
## 一次典型的交互流程
|
||||
|
||||
{/* TODO: 插入一张交互流程示意图 */}
|
||||
|
||||
1. 你在终端输入自然语言需求
|
||||
2. Claude Code 分析你的项目上下文
|
||||
3. 它决定使用哪些工具(读文件?执行命令?)
|
||||
4. 每个操作请求你确认(或根据规则自动放行)
|
||||
5. 执行完成后,把结果反馈给 AI,AI 决定下一步
|
||||
6. 循环,直到任务完成
|
||||
40
docs/introduction/why-this-whitepaper.mdx
Normal file
40
docs/introduction/why-this-whitepaper.mdx
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "为什么写这份白皮书"
|
||||
description: "将 LLM 能力落地到真实工作流的工程范本"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释为什么这个项目的架构值得深入研究 */}
|
||||
|
||||
## 不只是一个聊天工具
|
||||
|
||||
Claude Code 解决的核心问题是:**如何让大语言模型从"能说会道"变成"能说会做"**。
|
||||
|
||||
这不是简单地给 AI 套一个 shell。它涉及一系列精巧的工程决策:
|
||||
|
||||
- AI 说"我要编辑这个文件"时,如何确保安全?
|
||||
- 对话越来越长,token 快爆了怎么办?
|
||||
- AI 需要并行处理多个子任务时,如何隔离和协调?
|
||||
- 用户想扩展 AI 的能力(接数据库、连 API),如何设计插拔机制?
|
||||
|
||||
## 这份白皮书关注什么
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="功能视角" icon="eye">
|
||||
不堆代码,从"用户能做什么、AI 如何决策"出发
|
||||
</Card>
|
||||
<Card title="设计决策" icon="lightbulb">
|
||||
每个功能背后的"为什么这样设计"
|
||||
</Card>
|
||||
<Card title="架构模式" icon="sitemap">
|
||||
可复用的模式:Agentic Loop、工具抽象、上下文工程
|
||||
</Card>
|
||||
<Card title="安全哲学" icon="shield">
|
||||
AI 操作真实环境时的信任与控制平衡
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 适合谁读
|
||||
|
||||
- 想理解 AI Agent 产品架构的开发者
|
||||
- 正在构建类似工具的团队
|
||||
- 对 LLM 应用工程化感兴趣的任何人
|
||||
84
docs/mint.json
Normal file
84
docs/mint.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/schema.json",
|
||||
"name": "Claude Code Architecture",
|
||||
"logo": {
|
||||
"dark": "/logo/dark.svg",
|
||||
"light": "/logo/light.svg"
|
||||
},
|
||||
"favicon": "/favicon.svg",
|
||||
"colors": {
|
||||
"primary": "#D97706",
|
||||
"light": "#F59E0B",
|
||||
"dark": "#B45309",
|
||||
"background": {
|
||||
"dark": "#0F172A",
|
||||
"light": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
"navigation": [
|
||||
{
|
||||
"group": "开始",
|
||||
"pages": [
|
||||
"introduction/what-is-claude-code",
|
||||
"introduction/why-this-whitepaper",
|
||||
"introduction/architecture-overview"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "对话是如何运转的",
|
||||
"pages": [
|
||||
"conversation/the-loop",
|
||||
"conversation/streaming",
|
||||
"conversation/multi-turn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "工具:AI 的双手",
|
||||
"pages": [
|
||||
"tools/what-are-tools",
|
||||
"tools/file-operations",
|
||||
"tools/shell-execution",
|
||||
"tools/search-and-navigation",
|
||||
"tools/task-management"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "安全与权限",
|
||||
"pages": [
|
||||
"safety/why-safety-matters",
|
||||
"safety/permission-model",
|
||||
"safety/sandbox",
|
||||
"safety/plan-mode"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "上下文工程",
|
||||
"pages": [
|
||||
"context/system-prompt",
|
||||
"context/project-memory",
|
||||
"context/compaction",
|
||||
"context/token-budget"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "多 Agent 协作",
|
||||
"pages": [
|
||||
"agent/sub-agents",
|
||||
"agent/worktree-isolation",
|
||||
"agent/coordinator-and-swarm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "可扩展性",
|
||||
"pages": [
|
||||
"extensibility/mcp-protocol",
|
||||
"extensibility/hooks",
|
||||
"extensibility/skills",
|
||||
"extensibility/custom-agents"
|
||||
]
|
||||
}
|
||||
],
|
||||
"footerSocials": {
|
||||
"github": "https://github.com/anthropics/claude-code"
|
||||
}
|
||||
}
|
||||
64
docs/safety/permission-model.mdx
Normal file
64
docs/safety/permission-model.mdx
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: "权限模型"
|
||||
description: "三种行为 x 多级来源 = 灵活而严谨的权限体系"
|
||||
---
|
||||
|
||||
{/* 本章目标:详解权限系统的设计 */}
|
||||
|
||||
## 三种权限行为
|
||||
|
||||
每一次工具调用,系统都会做出三种裁决之一:
|
||||
|
||||
| 行为 | 含义 | 典型场景 |
|
||||
|------|------|---------|
|
||||
| **Allow** | 自动放行,用户无感知 | Read 工具读取项目内的文件 |
|
||||
| **Ask** | 弹出确认对话框,等待用户决定 | Bash 执行一条未知命令 |
|
||||
| **Deny** | 直接拒绝,AI 收到"权限被拒"的反馈 | 尝试执行被禁止的命令 |
|
||||
|
||||
## 权限规则的层级
|
||||
|
||||
规则可以来自多个来源,优先级从高到低:
|
||||
|
||||
<Steps>
|
||||
<Step title="用户会话内设置">
|
||||
用户在当前对话中手动授权的("对这个工具始终允许")
|
||||
</Step>
|
||||
<Step title="项目配置">
|
||||
项目目录中的 `.claude/settings.json`,团队共享
|
||||
</Step>
|
||||
<Step title="用户全局配置">
|
||||
`~/.claude/settings.json`,跨项目生效
|
||||
</Step>
|
||||
<Step title="托管设置">
|
||||
企业管理员下发的策略,用户不可覆盖
|
||||
</Step>
|
||||
<Step title="默认规则">
|
||||
系统内置的基线规则
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 规则的匹配方式
|
||||
|
||||
权限规则不是简单的"允许/禁止某个工具",它支持丰富的匹配条件:
|
||||
|
||||
- **工具名匹配**:`"tool": "Bash"` → 针对所有 Bash 命令
|
||||
- **命令模式匹配**:`"command": "git *"` → 只针对 git 开头的命令
|
||||
- **路径匹配**:`"path": "src/**"` → 只允许操作 src 目录下的文件
|
||||
- **组合条件**:以上条件可以组合使用
|
||||
|
||||
## 权限模式
|
||||
|
||||
系统提供几种预设的权限模式,适应不同信任级别:
|
||||
|
||||
| 模式 | 适用场景 |
|
||||
|------|---------|
|
||||
| **Default** | 日常使用,敏感操作逐一确认 |
|
||||
| **Plan Mode** | 探索阶段,AI 只能读不能写 |
|
||||
| **Bypass** | 完全信任 AI,所有操作自动放行(需显式开启) |
|
||||
|
||||
## Denial Tracking
|
||||
|
||||
系统不仅记录"允许了什么",还追踪"拒绝了什么":
|
||||
|
||||
- 如果 AI 被连续拒绝多次同一类操作,系统会调整策略
|
||||
- 这防止 AI 陷入"反复请求同一个被拒操作"的死循环
|
||||
60
docs/safety/plan-mode.mdx
Normal file
60
docs/safety/plan-mode.mdx
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "计划模式"
|
||||
description: "先看后做——给 AI 一个只读的探索阶段"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释 Plan Mode 的设计理念 */}
|
||||
|
||||
## 问题场景
|
||||
|
||||
你说"重构这个模块",AI 立刻开始改代码——但你还没搞清楚它打算怎么改。等改了一半发现方向不对,已经来不及了。
|
||||
|
||||
## Plan Mode 的解决方案
|
||||
|
||||
计划模式给对话加了一个"只读阶段":
|
||||
|
||||
<Steps>
|
||||
<Step title="进入计划模式">
|
||||
AI 自主判断任务需要规划(或用户主动触发),进入计划模式
|
||||
</Step>
|
||||
<Step title="探索阶段">
|
||||
在这个阶段,AI 只能使用读取和搜索类工具——不能编辑文件、不能执行命令
|
||||
</Step>
|
||||
<Step title="形成计划">
|
||||
AI 把理解和方案写入计划文件,提交给用户审阅
|
||||
</Step>
|
||||
<Step title="用户审批">
|
||||
用户阅读计划,提出修改意见或直接批准
|
||||
</Step>
|
||||
<Step title="退出计划模式">
|
||||
计划被批准后,AI 恢复全部工具权限,按计划执行
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 权限的自动收窄与恢复
|
||||
|
||||
计划模式的精妙之处在于**自动改变权限上下文**:
|
||||
|
||||
- 进入时:系统自动保存当前权限模式,切换为"只读"
|
||||
- 退出时:系统自动恢复之前的权限模式
|
||||
|
||||
AI 和用户都不需要手动调整权限设置。
|
||||
|
||||
## 什么时候该用计划模式
|
||||
|
||||
| 场景 | 是否需要计划 |
|
||||
|------|-------------|
|
||||
| 修复一个明确的 typo | 不需要,直接修 |
|
||||
| 添加一个简单的函数 | 不需要 |
|
||||
| 重构一个大模块 | 需要——先搞清楚影响范围 |
|
||||
| 涉及多个文件的 feature | 需要——先统一方案 |
|
||||
| 不确定该怎么做 | 需要——让 AI 先调研 |
|
||||
|
||||
## 计划模式 + 任务系统
|
||||
|
||||
计划模式通常与任务系统配合使用:
|
||||
|
||||
1. 在计划模式中,AI 把实施步骤创建为任务列表
|
||||
2. 用户审批计划(包含任务列表)
|
||||
3. 退出计划模式后,AI 按任务列表逐项执行
|
||||
4. 用户可以通过任务列表追踪进度
|
||||
35
docs/safety/sandbox.mdx
Normal file
35
docs/safety/sandbox.mdx
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "沙箱机制"
|
||||
description: "即使命令被允许执行,也不意味着它可以为所欲为"
|
||||
---
|
||||
|
||||
{/* 本章目标:解释沙箱的作用和原理 */}
|
||||
|
||||
## 权限之外的第二道防线
|
||||
|
||||
权限系统决定"这条命令能不能执行",沙箱决定"执行时能做到什么程度"。
|
||||
|
||||
即使一条命令通过了权限审批,沙箱仍然可以限制它的行为:
|
||||
|
||||
| 限制维度 | 说明 |
|
||||
|----------|------|
|
||||
| **文件系统** | 只能访问项目目录及其子目录 |
|
||||
| **网络** | 可以禁止或限制网络访问 |
|
||||
| **进程** | 限制可启动的子进程 |
|
||||
| **时间** | 超时自动终止 |
|
||||
|
||||
## 何时启用沙箱
|
||||
|
||||
沙箱不是默认对所有命令生效——它根据风险评估动态决定:
|
||||
|
||||
- 用户显式请求禁用沙箱的命令(`dangerouslyDisableSandbox`)不走沙箱
|
||||
- 已通过安全规则白名单的命令可以跳过沙箱
|
||||
- 未知命令或高风险命令强制进入沙箱
|
||||
|
||||
## 沙箱的实现思路
|
||||
|
||||
不同平台使用不同的沙箱技术:
|
||||
|
||||
- **macOS**:利用系统级沙箱机制限制文件和网络访问
|
||||
- **Linux**:基于命名空间和 cgroup 的进程隔离
|
||||
- 沙箱策略由系统自动选择,用户不需要手动配置
|
||||
40
docs/safety/why-safety-matters.mdx
Normal file
40
docs/safety/why-safety-matters.mdx
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "为什么安全至关重要"
|
||||
description: "当 AI 能操作你的电脑,信任的边界在哪里"
|
||||
---
|
||||
|
||||
{/* 本章目标:建立安全意识,解释为什么需要这么多安全机制 */}
|
||||
|
||||
## AI 动手的代价
|
||||
|
||||
Claude Code 不是在沙盒里回答问题——它在你的真实项目中修改文件、执行命令。一个失误可能意味着:
|
||||
|
||||
- 覆盖了你还没提交的工作
|
||||
- 执行了危险的 `rm -rf` 命令
|
||||
- 推送了包含 bug 的代码到远程仓库
|
||||
- 泄露了 `.env` 文件中的密钥
|
||||
|
||||
## 安全设计的核心理念
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="最小权限原则" icon="lock">
|
||||
AI 默认没有任何"动手"权限,每项能力都需要显式授予
|
||||
</Card>
|
||||
<Card title="可逆优先" icon="rotate-left">
|
||||
优先执行可逆操作(读文件、搜索),对不可逆操作(删除、推送)严格审批
|
||||
</Card>
|
||||
<Card title="人在回路" icon="user">
|
||||
关键操作必须经过人类确认,AI 不能绕过用户自行决定
|
||||
</Card>
|
||||
<Card title="纵深防御" icon="shield-halved">
|
||||
多层安全机制叠加——权限规则、沙箱、计划模式、预算上限——任何一层都能阻止危险操作
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 安全 vs 效率的平衡
|
||||
|
||||
如果每个操作都要确认,AI 就变成了一个不停弹窗的烦人助手。Claude Code 的设计在安全和效率之间找到了平衡:
|
||||
|
||||
- **低风险操作自动放行**:读取文件、搜索代码——这些不会改变任何东西
|
||||
- **中风险操作规则放行**:编辑指定目录的文件——用户可以预设"允许"规则
|
||||
- **高风险操作人工确认**:删除文件、执行未知命令——必须手动审批
|
||||
54
docs/tools/file-operations.mdx
Normal file
54
docs/tools/file-operations.mdx
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: "文件操作"
|
||||
description: "AI 如何安全、高效地读写你的代码"
|
||||
---
|
||||
|
||||
{/* 本章目标:介绍文件类工具的设计理念 */}
|
||||
|
||||
## 读、写、改——三种操作模式
|
||||
|
||||
Claude Code 把文件操作拆分为三个独立工具,而不是一个万能的"文件工具":
|
||||
|
||||
| 工具 | 功能 | 设计考量 |
|
||||
|------|------|---------|
|
||||
| **Read** | 读取文件内容 | 只读操作,权限最低,AI 可以随意使用 |
|
||||
| **Write** | 创建新文件或完全重写 | 高风险操作,需要确认 |
|
||||
| **Edit** | 精确替换文件中的特定片段 | 中等风险,但比 Write 安全——只改你指定的部分 |
|
||||
|
||||
<Tip>
|
||||
为什么 Edit 和 Write 要分开?因为"编辑一行"和"重写整个文件"的风险完全不同。分离后,权限系统可以对它们施加不同的控制策略。
|
||||
</Tip>
|
||||
|
||||
## 文件读取的智慧
|
||||
|
||||
Read 工具不是简单的 `cat` 命令,它有很多精细的设计:
|
||||
|
||||
- **分页读取**:超大文件不会一次性全部读入,支持 offset + limit 指定范围
|
||||
- **多格式支持**:除了文本文件,还能读取图片(多模态展示)、PDF、Jupyter Notebook
|
||||
- **文件状态缓存**:记住已读过的文件内容,避免重复读取浪费 token
|
||||
- **Token 感知**:文件内容计入 token 预算,系统会自动评估是否"读得起"
|
||||
|
||||
## 精确编辑 vs 全量重写
|
||||
|
||||
Edit 工具的核心设计是**精确字符串替换**:
|
||||
|
||||
- AI 指定 `old_string`(要被替换的原文)和 `new_string`(替换后的新文)
|
||||
- 系统确保 `old_string` 在文件中**唯一匹配**——如果匹配到多处或零处,操作失败
|
||||
- 这个设计确保 AI 不会"改错地方"
|
||||
|
||||
## 搜索与导航
|
||||
|
||||
在动手修改之前,AI 通常需要先"找到目标"。两个搜索工具分工明确:
|
||||
|
||||
- **Glob**:按文件名模式搜索("找到所有 `.ts` 文件"),替代 `find` 命令
|
||||
- **Grep**:按文件内容搜索("找到所有包含 `TODO` 的行"),替代 `grep/rg` 命令
|
||||
|
||||
两者都经过优化,能在大型项目中快速返回结果,并自动截断过长的输出。
|
||||
|
||||
## 文件历史快照
|
||||
|
||||
每当 AI 准备修改文件时,系统会自动保存一份快照。这意味着:
|
||||
|
||||
- 用户可以随时回滚到 AI 修改前的状态
|
||||
- 即使 AI 做了错误的编辑,原始内容不会丢失
|
||||
- 快照与 git 互补——git 追踪已提交的变更,快照保护未提交的工作
|
||||
43
docs/tools/search-and-navigation.mdx
Normal file
43
docs/tools/search-and-navigation.mdx
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: "搜索与导航"
|
||||
description: "AI 如何在百万行代码中精准定位目标"
|
||||
---
|
||||
|
||||
{/* 本章目标:介绍搜索类工具和工具搜索机制 */}
|
||||
|
||||
## 两种搜索维度
|
||||
|
||||
| 维度 | 工具 | 适用场景 |
|
||||
|------|------|---------|
|
||||
| **按名称找文件** | Glob | "找到所有测试文件"、"找 config 开头的文件" |
|
||||
| **按内容找代码** | Grep | "哪里定义了这个函数"、"谁在调用这个 API" |
|
||||
|
||||
两者组合使用,AI 就拥有了在大型项目中"导航"的能力。
|
||||
|
||||
## 搜索结果的智能处理
|
||||
|
||||
大型项目的搜索结果可能有成千上万条,直接全部返回不现实:
|
||||
|
||||
- **结果数量限制**:默认最多返回 250 条匹配
|
||||
- **上下文行**:Grep 支持显示匹配行前后的上下文(类似 `grep -C`)
|
||||
- **按修改时间排序**:Glob 默认把最近修改的文件排在前面
|
||||
- **文件类型过滤**:按语言类型过滤(只搜 `.ts` 文件、只搜 `.py` 文件)
|
||||
|
||||
## 工具发现机制
|
||||
|
||||
当可用工具超过 50 个时,AI 可能不知道该用哪个。系统提供了 **ToolSearch** 机制:
|
||||
|
||||
- AI 可以用自然语言描述需求("我需要连接数据库")
|
||||
- 系统在所有已注册工具(包括 MCP 提供的)中搜索匹配
|
||||
- 返回最相关的工具列表及使用说明
|
||||
|
||||
这让 AI 在面对庞大的工具库时不会迷路。
|
||||
|
||||
## Web 搜索与抓取
|
||||
|
||||
AI 的信息获取不局限于本地代码:
|
||||
|
||||
- **WebSearch**:搜索互联网获取最新信息
|
||||
- **WebFetch**:抓取特定网页内容,转换为 Markdown 供 AI 阅读
|
||||
|
||||
这让 AI 可以查阅文档、搜索 Stack Overflow、阅读 GitHub issue——和人类开发者的工作方式一致。
|
||||
53
docs/tools/shell-execution.mdx
Normal file
53
docs/tools/shell-execution.mdx
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "命令执行"
|
||||
description: "让 AI 在你的终端里运行命令——安全地"
|
||||
---
|
||||
|
||||
{/* 本章目标:介绍 Bash 工具的能力与安全设计 */}
|
||||
|
||||
## AI 能执行命令意味着什么
|
||||
|
||||
这是 Claude Code 最强大也最敏感的能力。AI 可以:
|
||||
|
||||
- 运行构建命令(`npm run build`、`cargo build`)
|
||||
- 执行测试(`pytest`、`jest`)
|
||||
- 使用 git(`git status`、`git commit`)
|
||||
- 调用系统工具(`curl`、`docker`、`kubectl`)
|
||||
|
||||
几乎你在终端里能做的事,AI 都能做。
|
||||
|
||||
## 安全设计
|
||||
|
||||
强大的能力需要严格的控制:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="权限确认">
|
||||
默认情况下,每条命令执行前都需要用户手动确认。用户可以设置白名单规则,让特定命令自动放行。
|
||||
</Accordion>
|
||||
<Accordion title="沙箱隔离">
|
||||
在支持的平台上,命令可以运行在沙箱环境中——限制文件系统访问范围、禁止网络请求、阻止危险操作。
|
||||
</Accordion>
|
||||
<Accordion title="超时控制">
|
||||
每条命令都有超时限制(默认 2 分钟,最长 10 分钟),防止 AI 启动一个永远不会结束的进程。
|
||||
</Accordion>
|
||||
<Accordion title="输出截断">
|
||||
命令输出过长时自动截断,避免把海量日志全部塞进 AI 的上下文。
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## 前台与后台
|
||||
|
||||
有些命令需要等待结果(比如 `git status`),有些适合在后台运行(比如 `npm install`):
|
||||
|
||||
- **前台执行**:AI 等待命令完成,拿到输出后继续思考
|
||||
- **后台执行**:命令在后台运行,AI 可以继续做其他事,稍后再检查结果
|
||||
|
||||
## 为什么用专用工具而不是直接调 shell
|
||||
|
||||
<Note>
|
||||
Claude Code 为文件读写、代码搜索等操作提供了专用工具(Read、Grep、Glob),而不是让 AI 用 `cat`、`grep` 等 shell 命令。原因有三:
|
||||
</Note>
|
||||
|
||||
1. **权限粒度更细**:`Read` 是只读操作可以自动放行,但 `Bash: cat file` 需要审批整条命令
|
||||
2. **输出结构化**:专用工具的返回值是结构化的,方便 UI 渲染(高亮、diff 视图等)
|
||||
3. **性能优化**:专用工具可以做缓存、分页、token 预算控制,shell 命令做不到
|
||||
50
docs/tools/task-management.mdx
Normal file
50
docs/tools/task-management.mdx
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: "任务管理"
|
||||
description: "让 AI 的工作有条理、可追踪"
|
||||
---
|
||||
|
||||
{/* 本章目标:介绍任务系统如何帮助 AI 和用户保持同步 */}
|
||||
|
||||
## 为什么需要任务管理
|
||||
|
||||
当你给 AI 一个复杂需求(比如"重构整个认证模块"),它可能需要执行几十个步骤。没有任务管理,用户只能被动等待,不知道 AI 做到哪了、还要做什么。
|
||||
|
||||
## 任务系统的运作方式
|
||||
|
||||
AI 可以自主创建和管理任务列表:
|
||||
|
||||
<Steps>
|
||||
<Step title="分解任务">
|
||||
AI 把大需求拆解为多个小任务,创建到任务列表
|
||||
</Step>
|
||||
<Step title="标记进度">
|
||||
开始某个任务时标记为"进行中",完成后标记为"已完成"
|
||||
</Step>
|
||||
<Step title="依赖管理">
|
||||
任务之间可以设定依赖关系——"任务 B 必须等任务 A 完成后才能开始"
|
||||
</Step>
|
||||
<Step title="用户可见">
|
||||
用户随时可以查看任务列表,了解整体进度
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## 任务与 Plan Mode 的配合
|
||||
|
||||
面对复杂任务,AI 可以先进入**计划模式**:
|
||||
|
||||
1. AI 进入计划模式 → 只允许使用搜索和阅读类工具(不能修改文件)
|
||||
2. AI 探索代码库、理解现有架构
|
||||
3. AI 制定实施计划,创建任务列表
|
||||
4. 用户审批计划
|
||||
5. AI 退出计划模式,按计划逐项执行
|
||||
|
||||
这种"先规划、后执行"的方式避免了 AI 盲目行动造成的返工。
|
||||
|
||||
## 状态展示
|
||||
|
||||
终端 UI 中,任务列表会实时更新:
|
||||
|
||||
- 待办任务灰色显示
|
||||
- 进行中的任务有旋转动画
|
||||
- 已完成的任务打勾标记
|
||||
- 被阻塞的任务标注依赖项
|
||||
74
docs/tools/what-are-tools.mdx
Normal file
74
docs/tools/what-are-tools.mdx
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: "工具:AI 的双手"
|
||||
description: "理解 Tool 这个核心抽象——AI 是怎么从'说'变成'做'的"
|
||||
---
|
||||
|
||||
{/* 本章目标:让读者理解 Tool 抽象的设计思想 */}
|
||||
|
||||
## AI 为什么需要工具
|
||||
|
||||
大语言模型本质上只能做一件事:**根据输入文本,生成输出文本**。
|
||||
|
||||
它不能读文件、不能执行命令、不能搜索代码。要让 AI 真正"动手",需要一个桥梁——这就是 **Tool**(工具)。
|
||||
|
||||
工具是 AI 的双手。AI 说"我想读这个文件",工具系统替它真正去读;AI 说"我想执行这条命令",工具系统替它真正去跑。
|
||||
|
||||
## 一个工具长什么样
|
||||
|
||||
每个工具都是一个标准化的"能力单元",包含四个要素:
|
||||
|
||||
| 要素 | 说明 | 示例(FileRead 工具) |
|
||||
|------|------|----------------------|
|
||||
| **名称** | 工具的唯一标识 | `Read` |
|
||||
| **描述** | 告诉 AI 这个工具能做什么(AI 据此决定是否使用) | "读取本地文件系统中的文件" |
|
||||
| **参数定义** | 工具接受什么输入 | `file_path`(必填)、`offset`、`limit` |
|
||||
| **执行逻辑** | 工具被调用时实际做什么 | 读取文件内容并返回 |
|
||||
|
||||
## AI 如何选择工具
|
||||
|
||||
AI 不是从下拉菜单里选工具——它是根据**工具描述**和**当前任务**自主决策的:
|
||||
|
||||
1. 系统把所有可用工具的名称、描述、参数告诉 AI
|
||||
2. AI 在思考过程中决定"我需要用某个工具"
|
||||
3. AI 生成一个结构化的工具调用请求(工具名 + 参数)
|
||||
4. 系统执行工具,将结果返回给 AI
|
||||
|
||||
<Note>
|
||||
工具描述的质量直接影响 AI 的决策准确性。一段好的描述不仅说明"能做什么",还说明"什么时候该用、什么时候不该用"。
|
||||
</Note>
|
||||
|
||||
## 50+ 内置工具
|
||||
|
||||
Claude Code 内置了覆盖软件开发全流程的工具集:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="文件操作" icon="file">
|
||||
Read / Write / Edit / Glob / Grep / NotebookEdit
|
||||
</Card>
|
||||
<Card title="命令执行" icon="terminal">
|
||||
Bash / PowerShell
|
||||
</Card>
|
||||
<Card title="对话管理" icon="comments">
|
||||
Agent / SendMessage / AskUserQuestion
|
||||
</Card>
|
||||
<Card title="任务追踪" icon="list-check">
|
||||
TaskCreate / TaskUpdate / TaskList / TaskGet
|
||||
</Card>
|
||||
<Card title="Web 能力" icon="globe">
|
||||
WebFetch / WebSearch
|
||||
</Card>
|
||||
<Card title="规划与版本" icon="map">
|
||||
EnterPlanMode / Worktree / TodoWrite
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 工具的可视化渲染
|
||||
|
||||
工具不仅能"做事",还能"展示"。每个工具可以定义自己的 UI 渲染方式:
|
||||
|
||||
- **FileEdit** → 在终端里展示语法高亮的 diff 视图
|
||||
- **Bash** → 实时显示命令输出,带进度指示
|
||||
- **Grep** → 高亮匹配结果,显示文件路径和行号链接
|
||||
- **Agent** → 显示子 Agent 的进度条和状态
|
||||
|
||||
这让用户能直观地看到"AI 在做什么、做到哪了"。
|
||||
22
knip.json
Normal file
22
knip.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@6/schema.json",
|
||||
"entry": ["src/entrypoints/cli.tsx"],
|
||||
"project": ["src/**/*.{ts,tsx}"],
|
||||
"ignore": ["src/types/**", "src/**/*.d.ts"],
|
||||
"ignoreDependencies": [
|
||||
"@ant/*",
|
||||
"react-compiler-runtime",
|
||||
"@anthropic-ai/mcpb",
|
||||
"@anthropic-ai/sandbox-runtime"
|
||||
],
|
||||
"ignoreBinaries": ["bun"],
|
||||
"workspaces": {
|
||||
"packages/*": {
|
||||
"entry": ["src/index.ts"],
|
||||
"project": ["src/**/*.ts"]
|
||||
},
|
||||
"packages/@ant/*": {
|
||||
"ignore": ["**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
288
package.json
288
package.json
@@ -1,128 +1,164 @@
|
||||
{
|
||||
"name": "claude-code",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"packages/@ant/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "bun build src/entrypoints/cli.tsx --outdir dist --target bun",
|
||||
"dev": "bun run src/entrypoints/cli.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alcalzone/ansi-tokenize": "^0.3.0",
|
||||
"@ant/claude-for-chrome-mcp": "workspace:*",
|
||||
"@ant/computer-use-input": "workspace:*",
|
||||
"@ant/computer-use-mcp": "workspace:*",
|
||||
"@ant/computer-use-swift": "workspace:*",
|
||||
"@anthropic-ai/bedrock-sdk": "^0.26.4",
|
||||
"@anthropic-ai/claude-agent-sdk": "latest",
|
||||
"@anthropic-ai/foundry-sdk": "^0.2.3",
|
||||
"@anthropic-ai/mcpb": "latest",
|
||||
"@anthropic-ai/sandbox-runtime": "latest",
|
||||
"@anthropic-ai/sdk": "latest",
|
||||
"@anthropic-ai/vertex-sdk": "^0.14.4",
|
||||
"@aws-sdk/client-bedrock": "latest",
|
||||
"@aws-sdk/client-bedrock-runtime": "latest",
|
||||
"@aws-sdk/client-sts": "^3.1020.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.28",
|
||||
"@aws-sdk/credential-providers": "latest",
|
||||
"@azure/identity": "^4.13.1",
|
||||
"@commander-js/extra-typings": "latest",
|
||||
"@growthbook/growthbook": "latest",
|
||||
"@modelcontextprotocol/sdk": "latest",
|
||||
"@opentelemetry/api": "latest",
|
||||
"@opentelemetry/api-logs": "latest",
|
||||
"@opentelemetry/core": "latest",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "latest",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "latest",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "latest",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "latest",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/exporter-prometheus": "latest",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "latest",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "latest",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/resources": "latest",
|
||||
"@opentelemetry/sdk-logs": "latest",
|
||||
"@opentelemetry/sdk-metrics": "latest",
|
||||
"@opentelemetry/sdk-trace-base": "latest",
|
||||
"@opentelemetry/semantic-conventions": "latest",
|
||||
"@smithy/core": "latest",
|
||||
"@smithy/node-http-handler": "latest",
|
||||
"ajv": "latest",
|
||||
"asciichart": "latest",
|
||||
"audio-capture-napi": "workspace:*",
|
||||
"auto-bind": "latest",
|
||||
"axios": "latest",
|
||||
"bidi-js": "latest",
|
||||
"cacache": "^20.0.4",
|
||||
"chalk": "latest",
|
||||
"chokidar": "latest",
|
||||
"cli-boxes": "latest",
|
||||
"cli-highlight": "^2.1.11",
|
||||
"code-excerpt": "latest",
|
||||
"color-diff-napi": "workspace:*",
|
||||
"diff": "latest",
|
||||
"emoji-regex": "latest",
|
||||
"env-paths": "latest",
|
||||
"execa": "latest",
|
||||
"fflate": "latest",
|
||||
"figures": "latest",
|
||||
"fuse.js": "latest",
|
||||
"get-east-asian-width": "latest",
|
||||
"google-auth-library": "latest",
|
||||
"highlight.js": "latest",
|
||||
"https-proxy-agent": "latest",
|
||||
"ignore": "latest",
|
||||
"image-processor-napi": "workspace:*",
|
||||
"indent-string": "latest",
|
||||
"jsonc-parser": "^3.3.1",
|
||||
"lodash-es": "latest",
|
||||
"lru-cache": "latest",
|
||||
"marked": "latest",
|
||||
"modifiers-napi": "workspace:*",
|
||||
"p-map": "latest",
|
||||
"picomatch": "latest",
|
||||
"plist": "^3.1.0",
|
||||
"proper-lockfile": "latest",
|
||||
"qrcode": "latest",
|
||||
"react": "latest",
|
||||
"react-compiler-runtime": "^1.0.0",
|
||||
"react-reconciler": "latest",
|
||||
"semver": "latest",
|
||||
"sharp": "^0.34.5",
|
||||
"shell-quote": "latest",
|
||||
"signal-exit": "latest",
|
||||
"stack-utils": "latest",
|
||||
"strip-ansi": "latest",
|
||||
"supports-hyperlinks": "latest",
|
||||
"tree-kill": "latest",
|
||||
"turndown": "^7.2.2",
|
||||
"type-fest": "latest",
|
||||
"undici": "latest",
|
||||
"url-handler-napi": "workspace:*",
|
||||
"usehooks-ts": "latest",
|
||||
"vscode-jsonrpc": "latest",
|
||||
"vscode-languageserver-protocol": "latest",
|
||||
"vscode-languageserver-types": "latest",
|
||||
"wrap-ansi": "latest",
|
||||
"ws": "latest",
|
||||
"xss": "latest",
|
||||
"yaml": "^2.8.3",
|
||||
"zod": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.11",
|
||||
"@types/cacache": "^20.0.1",
|
||||
"@types/plist": "^3.0.5",
|
||||
"@types/react": "latest",
|
||||
"@types/react-reconciler": "latest",
|
||||
"@types/sharp": "^0.32.0",
|
||||
"@types/turndown": "^5.0.6",
|
||||
"typescript": "latest"
|
||||
}
|
||||
"name": "claude-js",
|
||||
"version": "1.0.1",
|
||||
"description": "Reverse-engineered Anthropic Claude Code CLI — interactive AI coding assistant in the terminal",
|
||||
"type": "module",
|
||||
"author": "claude-code-best <claude-code-best@proton.me>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/claude-code-best/claude-code.git"
|
||||
},
|
||||
"homepage": "https://github.com/claude-code-best/claude-code#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/claude-code-best/claude-code/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"claude",
|
||||
"anthropic",
|
||||
"cli",
|
||||
"ai",
|
||||
"coding-assistant",
|
||||
"terminal",
|
||||
"repl"
|
||||
],
|
||||
"engines": {
|
||||
"bun": ">=1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"claude-js": "dist/cli.js"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"packages/@ant/*"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "bun build src/entrypoints/cli.tsx --outdir dist --target bun",
|
||||
"dev": "bun run src/entrypoints/cli.tsx",
|
||||
"prepublishOnly": "bun run build",
|
||||
"lint": "biome lint src/",
|
||||
"lint:fix": "biome lint --fix src/",
|
||||
"format": "biome format --write src/",
|
||||
"prepare": "git config core.hooksPath .githooks",
|
||||
"test": "bun test",
|
||||
"check:unused": "knip-bun",
|
||||
"health": "bun run scripts/health-check.ts"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@alcalzone/ansi-tokenize": "^0.3.0",
|
||||
"@ant/claude-for-chrome-mcp": "workspace:*",
|
||||
"@ant/computer-use-input": "workspace:*",
|
||||
"@ant/computer-use-mcp": "workspace:*",
|
||||
"@ant/computer-use-swift": "workspace:*",
|
||||
"@anthropic-ai/bedrock-sdk": "^0.26.4",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.87",
|
||||
"@anthropic-ai/foundry-sdk": "^0.2.3",
|
||||
"@anthropic-ai/mcpb": "^2.1.2",
|
||||
"@anthropic-ai/sandbox-runtime": "^0.0.44",
|
||||
"@anthropic-ai/sdk": "^0.80.0",
|
||||
"@anthropic-ai/vertex-sdk": "^0.14.4",
|
||||
"@aws-sdk/client-bedrock": "^3.1020.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.1020.0",
|
||||
"@aws-sdk/client-sts": "^3.1020.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.28",
|
||||
"@aws-sdk/credential-providers": "^3.1020.0",
|
||||
"@azure/identity": "^4.13.1",
|
||||
"@commander-js/extra-typings": "^14.0.0",
|
||||
"@growthbook/growthbook": "^1.6.5",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@opentelemetry/api-logs": "^0.214.0",
|
||||
"@opentelemetry/core": "^2.6.1",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.214.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.214.0",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.214.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "^0.214.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.214.0",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.214.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.214.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/resources": "^2.6.1",
|
||||
"@opentelemetry/sdk-logs": "^0.214.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.6.1",
|
||||
"@opentelemetry/sdk-trace-base": "^2.6.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.40.0",
|
||||
"@smithy/core": "^3.23.13",
|
||||
"@smithy/node-http-handler": "^4.5.1",
|
||||
"ajv": "^8.18.0",
|
||||
"asciichart": "^1.5.25",
|
||||
"audio-capture-napi": "workspace:*",
|
||||
"auto-bind": "^5.0.1",
|
||||
"axios": "^1.14.0",
|
||||
"bidi-js": "^1.0.3",
|
||||
"cacache": "^20.0.4",
|
||||
"chalk": "^5.6.2",
|
||||
"chokidar": "^5.0.0",
|
||||
"cli-boxes": "^4.0.1",
|
||||
"cli-highlight": "^2.1.11",
|
||||
"code-excerpt": "^4.0.0",
|
||||
"color-diff-napi": "workspace:*",
|
||||
"diff": "^8.0.4",
|
||||
"emoji-regex": "^10.6.0",
|
||||
"env-paths": "^4.0.0",
|
||||
"execa": "^9.6.1",
|
||||
"fflate": "^0.8.2",
|
||||
"figures": "^6.1.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"get-east-asian-width": "^1.5.0",
|
||||
"google-auth-library": "^10.6.2",
|
||||
"highlight.js": "^11.11.1",
|
||||
"https-proxy-agent": "^8.0.0",
|
||||
"ignore": "^7.0.5",
|
||||
"image-processor-napi": "workspace:*",
|
||||
"indent-string": "^5.0.0",
|
||||
"jsonc-parser": "^3.3.1",
|
||||
"lodash-es": "^4.17.23",
|
||||
"lru-cache": "^11.2.7",
|
||||
"marked": "^17.0.5",
|
||||
"modifiers-napi": "workspace:*",
|
||||
"p-map": "^7.0.4",
|
||||
"picomatch": "^4.0.4",
|
||||
"plist": "^3.1.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.2.4",
|
||||
"react-compiler-runtime": "^1.0.0",
|
||||
"react-reconciler": "^0.33.0",
|
||||
"semver": "^7.7.4",
|
||||
"sharp": "^0.34.5",
|
||||
"shell-quote": "^1.8.3",
|
||||
"signal-exit": "^4.1.0",
|
||||
"stack-utils": "^2.0.6",
|
||||
"strip-ansi": "^7.2.0",
|
||||
"supports-hyperlinks": "^4.4.0",
|
||||
"tree-kill": "^1.2.2",
|
||||
"turndown": "^7.2.2",
|
||||
"type-fest": "^5.5.0",
|
||||
"undici": "^7.24.6",
|
||||
"url-handler-napi": "workspace:*",
|
||||
"usehooks-ts": "^3.1.1",
|
||||
"vscode-jsonrpc": "^8.2.1",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"vscode-languageserver-types": "^3.17.5",
|
||||
"wrap-ansi": "^10.0.0",
|
||||
"ws": "^8.20.0",
|
||||
"xss": "^1.0.15",
|
||||
"yaml": "^2.8.3",
|
||||
"zod": "^4.3.6",
|
||||
"@biomejs/biome": "^2.4.10",
|
||||
"@types/bun": "^1.3.11",
|
||||
"@types/cacache": "^20.0.1",
|
||||
"@types/plist": "^3.0.5",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-reconciler": "^0.33.0",
|
||||
"@types/sharp": "^0.32.0",
|
||||
"@types/turndown": "^5.0.6",
|
||||
"knip": "^6.1.1",
|
||||
"typescript": "^6.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"name": "@ant/computer-use-input",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
|
||||
@@ -1,39 +1,174 @@
|
||||
/**
|
||||
* @ant/computer-use-input — macOS 键鼠模拟实现
|
||||
*
|
||||
* 使用 macOS 原生工具实现:
|
||||
* - AppleScript (osascript) — 应用信息、键盘输入
|
||||
* - CGEvent via AppleScript-ObjC bridge — 鼠标操作、位置查询
|
||||
*
|
||||
* 仅 macOS 支持。其他平台返回 { isSupported: false }
|
||||
*/
|
||||
|
||||
import { $ } from 'bun'
|
||||
|
||||
interface FrontmostAppInfo {
|
||||
bundleId: string
|
||||
appName: string
|
||||
}
|
||||
|
||||
// AppleScript key code mapping
|
||||
const KEY_MAP: Record<string, number> = {
|
||||
return: 36, enter: 36, tab: 48, space: 49, delete: 51, backspace: 51,
|
||||
escape: 53, esc: 53,
|
||||
left: 123, right: 124, down: 125, up: 126,
|
||||
f1: 122, f2: 120, f3: 99, f4: 118, f5: 96, f6: 97,
|
||||
f7: 98, f8: 100, f9: 101, f10: 109, f11: 103, f12: 111,
|
||||
home: 115, end: 119, pageup: 116, pagedown: 121,
|
||||
}
|
||||
|
||||
const MODIFIER_MAP: Record<string, string> = {
|
||||
command: 'command down', cmd: 'command down', meta: 'command down', super: 'command down',
|
||||
shift: 'shift down',
|
||||
option: 'option down', alt: 'option down',
|
||||
control: 'control down', ctrl: 'control down',
|
||||
}
|
||||
|
||||
async function osascript(script: string): Promise<string> {
|
||||
const result = await $`osascript -e ${script}`.quiet().nothrow().text()
|
||||
return result.trim()
|
||||
}
|
||||
|
||||
async function jxa(script: string): Promise<string> {
|
||||
const result = await $`osascript -l JavaScript -e ${script}`.quiet().nothrow().text()
|
||||
return result.trim()
|
||||
}
|
||||
|
||||
function jxaSync(script: string): string {
|
||||
const result = Bun.spawnSync({
|
||||
cmd: ['osascript', '-l', 'JavaScript', '-e', script],
|
||||
stdout: 'pipe', stderr: 'pipe',
|
||||
})
|
||||
return new TextDecoder().decode(result.stdout).trim()
|
||||
}
|
||||
|
||||
function buildMouseJxa(eventType: string, x: number, y: number, btn: number, clickState?: number): string {
|
||||
let script = `ObjC.import("CoreGraphics"); var p = $.CGPointMake(${x},${y}); var e = $.CGEventCreateMouseEvent(null, $.${eventType}, p, ${btn});`
|
||||
if (clickState !== undefined) {
|
||||
script += ` $.CGEventSetIntegerValueField(e, $.kCGMouseEventClickState, ${clickState});`
|
||||
}
|
||||
script += ` $.CGEventPost($.kCGHIDEventTap, e);`
|
||||
return script
|
||||
}
|
||||
|
||||
// ---- Implementation functions ----
|
||||
|
||||
async function moveMouse(x: number, y: number, _animated: boolean): Promise<void> {
|
||||
await jxa(buildMouseJxa('kCGEventMouseMoved', x, y, 0))
|
||||
}
|
||||
|
||||
async function key(keyName: string, action: 'press' | 'release'): Promise<void> {
|
||||
if (action === 'release') return
|
||||
const lower = keyName.toLowerCase()
|
||||
const keyCode = KEY_MAP[lower]
|
||||
if (keyCode !== undefined) {
|
||||
await osascript(`tell application "System Events" to key code ${keyCode}`)
|
||||
} else {
|
||||
await osascript(`tell application "System Events" to keystroke "${keyName.length === 1 ? keyName : lower}"`)
|
||||
}
|
||||
}
|
||||
|
||||
async function keys(parts: string[]): Promise<void> {
|
||||
const modifiers: string[] = []
|
||||
let finalKey: string | null = null
|
||||
for (const part of parts) {
|
||||
const mod = MODIFIER_MAP[part.toLowerCase()]
|
||||
if (mod) modifiers.push(mod)
|
||||
else finalKey = part
|
||||
}
|
||||
if (!finalKey) return
|
||||
const lower = finalKey.toLowerCase()
|
||||
const keyCode = KEY_MAP[lower]
|
||||
const modStr = modifiers.length > 0 ? ` using {${modifiers.join(', ')}}` : ''
|
||||
if (keyCode !== undefined) {
|
||||
await osascript(`tell application "System Events" to key code ${keyCode}${modStr}`)
|
||||
} else {
|
||||
await osascript(`tell application "System Events" to keystroke "${finalKey.length === 1 ? finalKey : lower}"${modStr}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function mouseLocation(): Promise<{ x: number; y: number }> {
|
||||
const result = await jxa('ObjC.import("CoreGraphics"); var e = $.CGEventCreate(null); var p = $.CGEventGetLocation(e); p.x + "," + p.y')
|
||||
const [xStr, yStr] = result.split(',')
|
||||
return { x: Math.round(Number(xStr)), y: Math.round(Number(yStr)) }
|
||||
}
|
||||
|
||||
async function mouseButton(
|
||||
button: 'left' | 'right' | 'middle',
|
||||
action: 'click' | 'press' | 'release',
|
||||
count?: number,
|
||||
): Promise<void> {
|
||||
const pos = await mouseLocation()
|
||||
const btn = button === 'left' ? 0 : button === 'right' ? 1 : 2
|
||||
const downType = btn === 0 ? 'kCGEventLeftMouseDown' : btn === 1 ? 'kCGEventRightMouseDown' : 'kCGEventOtherMouseDown'
|
||||
const upType = btn === 0 ? 'kCGEventLeftMouseUp' : btn === 1 ? 'kCGEventRightMouseUp' : 'kCGEventOtherMouseUp'
|
||||
|
||||
if (action === 'click') {
|
||||
for (let i = 0; i < (count ?? 1); i++) {
|
||||
await jxa(buildMouseJxa(downType, pos.x, pos.y, btn, i + 1))
|
||||
await jxa(buildMouseJxa(upType, pos.x, pos.y, btn, i + 1))
|
||||
}
|
||||
} else if (action === 'press') {
|
||||
await jxa(buildMouseJxa(downType, pos.x, pos.y, btn))
|
||||
} else {
|
||||
await jxa(buildMouseJxa(upType, pos.x, pos.y, btn))
|
||||
}
|
||||
}
|
||||
|
||||
async function mouseScroll(amount: number, direction: 'vertical' | 'horizontal'): Promise<void> {
|
||||
const script = direction === 'vertical'
|
||||
? `ObjC.import("CoreGraphics"); var e = $.CGEventCreateScrollWheelEvent(null, 0, 1, ${amount}); $.CGEventPost($.kCGHIDEventTap, e);`
|
||||
: `ObjC.import("CoreGraphics"); var e = $.CGEventCreateScrollWheelEvent(null, 0, 2, 0, ${amount}); $.CGEventPost($.kCGHIDEventTap, e);`
|
||||
await jxa(script)
|
||||
}
|
||||
|
||||
async function typeText(text: string): Promise<void> {
|
||||
const escaped = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
||||
await osascript(`tell application "System Events" to keystroke "${escaped}"`)
|
||||
}
|
||||
|
||||
function getFrontmostAppInfo(): FrontmostAppInfo | null {
|
||||
try {
|
||||
const result = Bun.spawnSync({
|
||||
cmd: ['osascript', '-e', `
|
||||
tell application "System Events"
|
||||
set frontApp to first application process whose frontmost is true
|
||||
set appName to name of frontApp
|
||||
set bundleId to bundle identifier of frontApp
|
||||
return bundleId & "|" & appName
|
||||
end tell
|
||||
`],
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
})
|
||||
const output = new TextDecoder().decode(result.stdout).trim()
|
||||
if (!output || !output.includes('|')) return null
|
||||
const [bundleId, appName] = output.split('|', 2)
|
||||
return { bundleId: bundleId!, appName: appName! }
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Exports ----
|
||||
|
||||
export class ComputerUseInputAPI {
|
||||
declare moveMouse: (
|
||||
x: number,
|
||||
y: number,
|
||||
animated: boolean,
|
||||
) => Promise<void>
|
||||
|
||||
declare key: (
|
||||
key: string,
|
||||
action: 'press' | 'release',
|
||||
) => Promise<void>
|
||||
|
||||
declare moveMouse: (x: number, y: number, animated: boolean) => Promise<void>
|
||||
declare key: (key: string, action: 'press' | 'release') => Promise<void>
|
||||
declare keys: (parts: string[]) => Promise<void>
|
||||
|
||||
declare mouseLocation: () => Promise<{ x: number; y: number }>
|
||||
|
||||
declare mouseButton: (
|
||||
button: 'left' | 'right' | 'middle',
|
||||
action: 'click' | 'press' | 'release',
|
||||
count?: number,
|
||||
) => Promise<void>
|
||||
|
||||
declare mouseScroll: (
|
||||
amount: number,
|
||||
direction: 'vertical' | 'horizontal',
|
||||
) => Promise<void>
|
||||
|
||||
declare mouseButton: (button: 'left' | 'right' | 'middle', action: 'click' | 'press' | 'release', count?: number) => Promise<void>
|
||||
declare mouseScroll: (amount: number, direction: 'vertical' | 'horizontal') => Promise<void>
|
||||
declare typeText: (text: string) => Promise<void>
|
||||
|
||||
declare getFrontmostAppInfo: () => FrontmostAppInfo | null
|
||||
|
||||
declare isSupported: true
|
||||
}
|
||||
|
||||
@@ -42,3 +177,7 @@ interface ComputerUseInputUnsupported {
|
||||
}
|
||||
|
||||
export type ComputerUseInput = ComputerUseInputAPI | ComputerUseInputUnsupported
|
||||
|
||||
// Plain object with all methods as own properties — compatible with require()
|
||||
export const isSupported = process.platform === 'darwin'
|
||||
export { moveMouse, key, keys, mouseLocation, mouseButton, mouseScroll, typeText, getFrontmostAppInfo }
|
||||
|
||||
@@ -1,30 +1,163 @@
|
||||
export const API_RESIZE_PARAMS: any = {}
|
||||
/**
|
||||
* @ant/computer-use-mcp — Stub 实现
|
||||
*
|
||||
* 提供类型安全的 stub,所有函数返回合理的默认值。
|
||||
* 在 feature('CHICAGO_MCP') = false 时不会被实际调用,
|
||||
* 但确保 import 不报错且类型正确。
|
||||
*/
|
||||
|
||||
export class ComputerExecutor {}
|
||||
import type {
|
||||
ComputerUseHostAdapter,
|
||||
CoordinateMode,
|
||||
GrantFlags,
|
||||
Logger,
|
||||
} from './types'
|
||||
|
||||
export type ComputerUseSessionContext = any
|
||||
export type CuCallToolResult = any
|
||||
export type CuPermissionRequest = any
|
||||
export type CuPermissionResponse = any
|
||||
export const DEFAULT_GRANT_FLAGS: any = {}
|
||||
export type DisplayGeometry = any
|
||||
export type FrontmostApp = any
|
||||
export type InstalledApp = any
|
||||
export type ResolvePrepareCaptureResult = any
|
||||
export type RunningApp = any
|
||||
export type ScreenshotDims = any
|
||||
export type ScreenshotResult = any
|
||||
// Re-export types from types.ts
|
||||
export type { CoordinateMode, Logger } from './types'
|
||||
export type {
|
||||
ComputerUseConfig,
|
||||
ComputerUseHostAdapter,
|
||||
CuPermissionRequest,
|
||||
CuPermissionResponse,
|
||||
CuSubGates,
|
||||
} from './types'
|
||||
export { DEFAULT_GRANT_FLAGS } from './types'
|
||||
|
||||
export function bindSessionContext(..._args: any[]): any {
|
||||
return null
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types (defined here for callers that import from the main entry)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface DisplayGeometry {
|
||||
width: number
|
||||
height: number
|
||||
displayId?: number
|
||||
originX?: number
|
||||
originY?: number
|
||||
}
|
||||
|
||||
export function buildComputerUseTools(..._args: any[]): any[] {
|
||||
export interface FrontmostApp {
|
||||
bundleId: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
export interface InstalledApp {
|
||||
bundleId: string
|
||||
displayName: string
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface RunningApp {
|
||||
bundleId: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
export interface ScreenshotResult {
|
||||
base64: string
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
export type ResolvePrepareCaptureResult = ScreenshotResult
|
||||
|
||||
export interface ScreenshotDims {
|
||||
width: number
|
||||
height: number
|
||||
displayWidth: number
|
||||
displayHeight: number
|
||||
displayId: number
|
||||
originX: number
|
||||
originY: number
|
||||
}
|
||||
|
||||
export interface CuCallToolResultContent {
|
||||
type: 'image' | 'text'
|
||||
data?: string
|
||||
mimeType?: string
|
||||
text?: string
|
||||
}
|
||||
|
||||
export interface CuCallToolResult {
|
||||
content: CuCallToolResultContent[]
|
||||
telemetry: {
|
||||
error_kind?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export type ComputerUseSessionContext = Record<string, unknown>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// API_RESIZE_PARAMS — 默认的截图缩放参数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const API_RESIZE_PARAMS = {
|
||||
maxWidth: 1280,
|
||||
maxHeight: 800,
|
||||
maxPixels: 1280 * 800,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ComputerExecutor — stub class
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export class ComputerExecutor {
|
||||
capabilities: Record<string, boolean> = {}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Functions — 返回合理默认值的 stub
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 计算目标截图尺寸。
|
||||
* 在物理宽高和 API 限制之间取最优尺寸。
|
||||
*/
|
||||
export function targetImageSize(
|
||||
physW: number,
|
||||
physH: number,
|
||||
_params?: typeof API_RESIZE_PARAMS,
|
||||
): [number, number] {
|
||||
const maxW = _params?.maxWidth ?? 1280
|
||||
const maxH = _params?.maxHeight ?? 800
|
||||
const scale = Math.min(1, maxW / physW, maxH / physH)
|
||||
return [Math.round(physW * scale), Math.round(physH * scale)]
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定会话上下文,返回工具调度函数。
|
||||
* Stub 返回一个始终返回空结果的调度器。
|
||||
*/
|
||||
export function bindSessionContext(
|
||||
_adapter: ComputerUseHostAdapter,
|
||||
_coordinateMode: CoordinateMode,
|
||||
_ctx: ComputerUseSessionContext,
|
||||
): (name: string, args: unknown) => Promise<CuCallToolResult> {
|
||||
return async (_name: string, _args: unknown) => ({
|
||||
content: [],
|
||||
telemetry: {},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 Computer Use 工具定义列表。
|
||||
* Stub 返回空数组(无工具)。
|
||||
*/
|
||||
export function buildComputerUseTools(
|
||||
_capabilities?: Record<string, boolean>,
|
||||
_coordinateMode?: CoordinateMode,
|
||||
_installedAppNames?: string[],
|
||||
): Array<{ name: string; description: string; inputSchema: Record<string, unknown> }> {
|
||||
return []
|
||||
}
|
||||
|
||||
export function createComputerUseMcpServer(..._args: any[]): any {
|
||||
/**
|
||||
* 创建 Computer Use MCP server。
|
||||
* Stub 返回 null(服务未启用)。
|
||||
*/
|
||||
export function createComputerUseMcpServer(
|
||||
_adapter?: ComputerUseHostAdapter,
|
||||
_coordinateMode?: CoordinateMode,
|
||||
): null {
|
||||
return null
|
||||
}
|
||||
|
||||
export const targetImageSize: any = null
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
export const sentinelApps: string[] = []
|
||||
/**
|
||||
* Sentinel apps — 需要特殊权限警告的应用列表
|
||||
*
|
||||
* 包含终端、文件管理器、系统设置等敏感应用。
|
||||
* Computer Use 操作这些应用时会显示额外警告。
|
||||
*/
|
||||
|
||||
export function getSentinelCategory(_appName: string): string | null {
|
||||
return null
|
||||
type SentinelCategory = 'shell' | 'filesystem' | 'system_settings'
|
||||
|
||||
const SENTINEL_MAP: Record<string, SentinelCategory> = {
|
||||
// Shell / Terminal
|
||||
'com.apple.Terminal': 'shell',
|
||||
'com.googlecode.iterm2': 'shell',
|
||||
'dev.warp.Warp-Stable': 'shell',
|
||||
'io.alacritty': 'shell',
|
||||
'com.github.wez.wezterm': 'shell',
|
||||
'net.kovidgoyal.kitty': 'shell',
|
||||
'co.zeit.hyper': 'shell',
|
||||
|
||||
// Filesystem
|
||||
'com.apple.finder': 'filesystem',
|
||||
|
||||
// System Settings
|
||||
'com.apple.systempreferences': 'system_settings',
|
||||
'com.apple.SystemPreferences': 'system_settings',
|
||||
}
|
||||
|
||||
export const sentinelApps: string[] = Object.keys(SENTINEL_MAP)
|
||||
|
||||
export function getSentinelCategory(bundleId: string): SentinelCategory | null {
|
||||
return SENTINEL_MAP[bundleId] ?? null
|
||||
}
|
||||
|
||||
@@ -1,8 +1,70 @@
|
||||
export type ComputerUseConfig = any
|
||||
export type ComputerUseHostAdapter = any
|
||||
export type CoordinateMode = any
|
||||
export type CuPermissionRequest = any
|
||||
export type CuPermissionResponse = any
|
||||
export type CuSubGates = any
|
||||
export const DEFAULT_GRANT_FLAGS: any = {}
|
||||
export type Logger = any
|
||||
/**
|
||||
* @ant/computer-use-mcp — Types
|
||||
*
|
||||
* 从调用侧反推的真实类型定义,替代 any stub。
|
||||
*/
|
||||
|
||||
export type CoordinateMode = 'pixels' | 'normalized'
|
||||
|
||||
export interface CuSubGates {
|
||||
pixelValidation: boolean
|
||||
clipboardPasteMultiline: boolean
|
||||
mouseAnimation: boolean
|
||||
hideBeforeAction: boolean
|
||||
autoTargetDisplay: boolean
|
||||
clipboardGuard: boolean
|
||||
}
|
||||
|
||||
export interface Logger {
|
||||
silly(message: string, ...args: unknown[]): void
|
||||
debug(message: string, ...args: unknown[]): void
|
||||
info(message: string, ...args: unknown[]): void
|
||||
warn(message: string, ...args: unknown[]): void
|
||||
error(message: string, ...args: unknown[]): void
|
||||
}
|
||||
|
||||
export interface CuPermissionRequest {
|
||||
apps: Array<{ bundleId: string; displayName: string }>
|
||||
requestedFlags: GrantFlags
|
||||
reason: string
|
||||
tccState: { accessibility: boolean; screenRecording: boolean }
|
||||
willHide: string[]
|
||||
}
|
||||
|
||||
export interface GrantFlags {
|
||||
clipboardRead: boolean
|
||||
clipboardWrite: boolean
|
||||
systemKeyCombos: boolean
|
||||
}
|
||||
|
||||
export interface CuPermissionResponse {
|
||||
granted: string[]
|
||||
denied: string[]
|
||||
flags: GrantFlags
|
||||
}
|
||||
|
||||
export const DEFAULT_GRANT_FLAGS: GrantFlags = {
|
||||
clipboardRead: false,
|
||||
clipboardWrite: false,
|
||||
systemKeyCombos: false,
|
||||
}
|
||||
|
||||
export interface ComputerUseConfig {
|
||||
coordinateMode: CoordinateMode
|
||||
enabledTools: string[]
|
||||
}
|
||||
|
||||
export interface ComputerUseHostAdapter {
|
||||
serverName: string
|
||||
logger: Logger
|
||||
executor: ComputerExecutor
|
||||
ensureOsPermissions(): Promise<{ granted: true } | { granted: false; accessibility: boolean; screenRecording: boolean }>
|
||||
isDisabled(): boolean
|
||||
getSubGates(): CuSubGates
|
||||
getAutoUnhideEnabled(): boolean
|
||||
cropRawPatch?(base64: string, x: number, y: number, w: number, h: number): Promise<string>
|
||||
}
|
||||
|
||||
export interface ComputerExecutor {
|
||||
capabilities: Record<string, boolean>
|
||||
}
|
||||
|
||||
@@ -1,66 +1,194 @@
|
||||
interface DisplayGeometry {
|
||||
/**
|
||||
* @ant/computer-use-swift — macOS 实现
|
||||
*
|
||||
* 用 AppleScript/JXA/screencapture 替代原始 Swift 原生模块。
|
||||
* 提供显示器信息、应用管理、截图等功能。
|
||||
*
|
||||
* 仅 macOS 支持。
|
||||
*/
|
||||
|
||||
import { readFileSync, unlinkSync } from 'fs'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types (exported for callers)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface DisplayGeometry {
|
||||
width: number
|
||||
height: number
|
||||
scaleFactor: number
|
||||
displayId: number
|
||||
}
|
||||
|
||||
interface PrepareDisplayResult {
|
||||
export interface PrepareDisplayResult {
|
||||
activated: string
|
||||
hidden: string[]
|
||||
}
|
||||
|
||||
interface AppInfo {
|
||||
export interface AppInfo {
|
||||
bundleId: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
interface InstalledApp {
|
||||
export interface InstalledApp {
|
||||
bundleId: string
|
||||
displayName: string
|
||||
path: string
|
||||
iconDataUrl?: string
|
||||
}
|
||||
|
||||
interface RunningApp {
|
||||
export interface RunningApp {
|
||||
bundleId: string
|
||||
displayName: string
|
||||
}
|
||||
|
||||
interface ScreenshotResult {
|
||||
export interface ScreenshotResult {
|
||||
base64: string
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
interface ResolvePrepareCaptureResult {
|
||||
export interface ResolvePrepareCaptureResult {
|
||||
base64: string
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
interface WindowDisplayInfo {
|
||||
export interface WindowDisplayInfo {
|
||||
bundleId: string
|
||||
displayIds: number[]
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function jxaSync(script: string): string {
|
||||
const result = Bun.spawnSync({
|
||||
cmd: ['osascript', '-l', 'JavaScript', '-e', script],
|
||||
stdout: 'pipe', stderr: 'pipe',
|
||||
})
|
||||
return new TextDecoder().decode(result.stdout).trim()
|
||||
}
|
||||
|
||||
function osascriptSync(script: string): string {
|
||||
const result = Bun.spawnSync({
|
||||
cmd: ['osascript', '-e', script],
|
||||
stdout: 'pipe', stderr: 'pipe',
|
||||
})
|
||||
return new TextDecoder().decode(result.stdout).trim()
|
||||
}
|
||||
|
||||
async function osascript(script: string): Promise<string> {
|
||||
const proc = Bun.spawn(['osascript', '-e', script], {
|
||||
stdout: 'pipe', stderr: 'pipe',
|
||||
})
|
||||
const text = await new Response(proc.stdout).text()
|
||||
await proc.exited
|
||||
return text.trim()
|
||||
}
|
||||
|
||||
async function jxa(script: string): Promise<string> {
|
||||
const proc = Bun.spawn(['osascript', '-l', 'JavaScript', '-e', script], {
|
||||
stdout: 'pipe', stderr: 'pipe',
|
||||
})
|
||||
const text = await new Response(proc.stdout).text()
|
||||
await proc.exited
|
||||
return text.trim()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DisplayAPI
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface DisplayAPI {
|
||||
getSize(displayId?: number): DisplayGeometry
|
||||
listAll(): DisplayGeometry[]
|
||||
}
|
||||
|
||||
const displayAPI: DisplayAPI = {
|
||||
getSize(displayId?: number): DisplayGeometry {
|
||||
const all = this.listAll()
|
||||
if (displayId !== undefined) {
|
||||
const found = all.find(d => d.displayId === displayId)
|
||||
if (found) return found
|
||||
}
|
||||
return all[0] ?? { width: 1920, height: 1080, scaleFactor: 2, displayId: 1 }
|
||||
},
|
||||
|
||||
listAll(): DisplayGeometry[] {
|
||||
try {
|
||||
const raw = jxaSync(`
|
||||
ObjC.import("CoreGraphics");
|
||||
var displays = $.CGDisplayCopyAllDisplayModes ? [] : [];
|
||||
var active = $.CGGetActiveDisplayList(10, null, Ref());
|
||||
var countRef = Ref();
|
||||
$.CGGetActiveDisplayList(0, null, countRef);
|
||||
var count = countRef[0];
|
||||
var idBuf = Ref();
|
||||
$.CGGetActiveDisplayList(count, idBuf, countRef);
|
||||
var result = [];
|
||||
for (var i = 0; i < count; i++) {
|
||||
var did = idBuf[i];
|
||||
var w = $.CGDisplayPixelsWide(did);
|
||||
var h = $.CGDisplayPixelsHigh(did);
|
||||
var mode = $.CGDisplayCopyDisplayMode(did);
|
||||
var pw = $.CGDisplayModeGetPixelWidth(mode);
|
||||
var sf = pw > 0 && w > 0 ? pw / w : 2;
|
||||
result.push({width: w, height: h, scaleFactor: sf, displayId: did});
|
||||
}
|
||||
JSON.stringify(result);
|
||||
`)
|
||||
return (JSON.parse(raw) as DisplayGeometry[]).map(d => ({
|
||||
width: Number(d.width), height: Number(d.height),
|
||||
scaleFactor: Number(d.scaleFactor), displayId: Number(d.displayId),
|
||||
}))
|
||||
} catch {
|
||||
// Fallback: use NSScreen via JXA
|
||||
try {
|
||||
const raw = jxaSync(`
|
||||
ObjC.import("AppKit");
|
||||
var screens = $.NSScreen.screens;
|
||||
var result = [];
|
||||
for (var i = 0; i < screens.count; i++) {
|
||||
var s = screens.objectAtIndex(i);
|
||||
var frame = s.frame;
|
||||
var desc = s.deviceDescription;
|
||||
var screenNumber = desc.objectForKey($("NSScreenNumber")).intValue;
|
||||
var backingFactor = s.backingScaleFactor;
|
||||
result.push({
|
||||
width: Math.round(frame.size.width),
|
||||
height: Math.round(frame.size.height),
|
||||
scaleFactor: backingFactor,
|
||||
displayId: screenNumber
|
||||
});
|
||||
}
|
||||
JSON.stringify(result);
|
||||
`)
|
||||
return (JSON.parse(raw) as DisplayGeometry[]).map(d => ({
|
||||
width: Number(d.width),
|
||||
height: Number(d.height),
|
||||
scaleFactor: Number(d.scaleFactor),
|
||||
displayId: Number(d.displayId),
|
||||
}))
|
||||
} catch {
|
||||
return [{ width: 1920, height: 1080, scaleFactor: 2, displayId: 1 }]
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AppsAPI
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface AppsAPI {
|
||||
prepareDisplay(
|
||||
allowlistBundleIds: string[],
|
||||
surrogateHost: string,
|
||||
displayId?: number,
|
||||
): Promise<PrepareDisplayResult>
|
||||
previewHideSet(
|
||||
bundleIds: string[],
|
||||
displayId?: number,
|
||||
): Promise<Array<AppInfo>>
|
||||
findWindowDisplays(
|
||||
bundleIds: string[],
|
||||
): Promise<Array<WindowDisplayInfo>>
|
||||
appUnderPoint(
|
||||
x: number,
|
||||
y: number,
|
||||
): Promise<AppInfo | null>
|
||||
prepareDisplay(allowlistBundleIds: string[], surrogateHost: string, displayId?: number): Promise<PrepareDisplayResult>
|
||||
previewHideSet(bundleIds: string[], displayId?: number): Promise<AppInfo[]>
|
||||
findWindowDisplays(bundleIds: string[]): Promise<WindowDisplayInfo[]>
|
||||
appUnderPoint(x: number, y: number): Promise<AppInfo | null>
|
||||
listInstalled(): Promise<InstalledApp[]>
|
||||
iconDataUrl(path: string): string | null
|
||||
listRunning(): RunningApp[]
|
||||
@@ -68,45 +196,193 @@ interface AppsAPI {
|
||||
unhide(bundleIds: string[]): Promise<void>
|
||||
}
|
||||
|
||||
interface DisplayAPI {
|
||||
getSize(displayId?: number): DisplayGeometry
|
||||
listAll(): DisplayGeometry[]
|
||||
const appsAPI: AppsAPI = {
|
||||
async prepareDisplay(
|
||||
_allowlistBundleIds: string[],
|
||||
_surrogateHost: string,
|
||||
_displayId?: number,
|
||||
): Promise<PrepareDisplayResult> {
|
||||
return { activated: '', hidden: [] }
|
||||
},
|
||||
|
||||
async previewHideSet(
|
||||
_bundleIds: string[],
|
||||
_displayId?: number,
|
||||
): Promise<AppInfo[]> {
|
||||
return []
|
||||
},
|
||||
|
||||
async findWindowDisplays(bundleIds: string[]): Promise<WindowDisplayInfo[]> {
|
||||
// Each running app is assumed to be on display 1
|
||||
return bundleIds.map(bundleId => ({ bundleId, displayIds: [1] }))
|
||||
},
|
||||
|
||||
async appUnderPoint(_x: number, _y: number): Promise<AppInfo | null> {
|
||||
// Use JXA to find app at mouse position via accessibility
|
||||
try {
|
||||
const result = await jxa(`
|
||||
ObjC.import("CoreGraphics");
|
||||
ObjC.import("AppKit");
|
||||
var pt = $.CGPointMake(${_x}, ${_y});
|
||||
// Get frontmost app as a fallback
|
||||
var app = $.NSWorkspace.sharedWorkspace.frontmostApplication;
|
||||
JSON.stringify({bundleId: app.bundleIdentifier.js, displayName: app.localizedName.js});
|
||||
`)
|
||||
return JSON.parse(result)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
async listInstalled(): Promise<InstalledApp[]> {
|
||||
try {
|
||||
const result = await osascript(`
|
||||
tell application "System Events"
|
||||
set appList to ""
|
||||
repeat with appFile in (every file of folder "Applications" of startup disk whose name ends with ".app")
|
||||
set appPath to POSIX path of (appFile as alias)
|
||||
set appName to name of appFile
|
||||
set appList to appList & appPath & "|" & appName & "\\n"
|
||||
end repeat
|
||||
return appList
|
||||
end tell
|
||||
`)
|
||||
return result.split('\n').filter(Boolean).map(line => {
|
||||
const [path, name] = line.split('|', 2)
|
||||
// Derive bundleId from Info.plist would be ideal, but use path-based fallback
|
||||
const displayName = (name ?? '').replace(/\.app$/, '')
|
||||
return {
|
||||
bundleId: `com.app.${displayName.toLowerCase().replace(/\s+/g, '-')}`,
|
||||
displayName,
|
||||
path: path ?? '',
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
iconDataUrl(_path: string): string | null {
|
||||
return null
|
||||
},
|
||||
|
||||
listRunning(): RunningApp[] {
|
||||
try {
|
||||
const raw = jxaSync(`
|
||||
var apps = Application("System Events").applicationProcesses.whose({backgroundOnly: false});
|
||||
var result = [];
|
||||
for (var i = 0; i < apps.length; i++) {
|
||||
try {
|
||||
var a = apps[i];
|
||||
result.push({bundleId: a.bundleIdentifier(), displayName: a.name()});
|
||||
} catch(e) {}
|
||||
}
|
||||
JSON.stringify(result);
|
||||
`)
|
||||
return JSON.parse(raw)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
async open(bundleId: string): Promise<void> {
|
||||
await osascript(`tell application id "${bundleId}" to activate`)
|
||||
},
|
||||
|
||||
async unhide(bundleIds: string[]): Promise<void> {
|
||||
for (const bundleId of bundleIds) {
|
||||
await osascript(`
|
||||
tell application "System Events"
|
||||
set visible of application process (name of application process whose bundle identifier is "${bundleId}") to true
|
||||
end tell
|
||||
`)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ScreenshotAPI
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface ScreenshotAPI {
|
||||
captureExcluding(
|
||||
allowedBundleIds: string[],
|
||||
quality: number,
|
||||
targetW: number,
|
||||
targetH: number,
|
||||
displayId?: number,
|
||||
allowedBundleIds: string[], quality: number,
|
||||
targetW: number, targetH: number, displayId?: number,
|
||||
): Promise<ScreenshotResult>
|
||||
captureRegion(
|
||||
allowedBundleIds: string[],
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
outW: number,
|
||||
outH: number,
|
||||
quality: number,
|
||||
displayId?: number,
|
||||
x: number, y: number, w: number, h: number,
|
||||
outW: number, outH: number, quality: number, displayId?: number,
|
||||
): Promise<ScreenshotResult>
|
||||
}
|
||||
|
||||
export class ComputerUseAPI {
|
||||
declare apps: AppsAPI
|
||||
declare display: DisplayAPI
|
||||
declare screenshot: ScreenshotAPI
|
||||
async function captureScreenToBase64(args: string[]): Promise<{ base64: string; width: number; height: number }> {
|
||||
const tmpFile = join(tmpdir(), `cu-screenshot-${Date.now()}.png`)
|
||||
const proc = Bun.spawn(['screencapture', ...args, tmpFile], {
|
||||
stdout: 'pipe', stderr: 'pipe',
|
||||
})
|
||||
await proc.exited
|
||||
|
||||
declare resolvePrepareCapture: (
|
||||
try {
|
||||
const buf = readFileSync(tmpFile)
|
||||
const base64 = buf.toString('base64')
|
||||
// Parse PNG header for dimensions (bytes 16-23)
|
||||
const width = buf.readUInt32BE(16)
|
||||
const height = buf.readUInt32BE(20)
|
||||
return { base64, width, height }
|
||||
} finally {
|
||||
try { unlinkSync(tmpFile) } catch {}
|
||||
}
|
||||
}
|
||||
|
||||
const screenshotAPI: ScreenshotAPI = {
|
||||
async captureExcluding(
|
||||
_allowedBundleIds: string[],
|
||||
_quality: number,
|
||||
_targetW: number,
|
||||
_targetH: number,
|
||||
displayId?: number,
|
||||
): Promise<ScreenshotResult> {
|
||||
const args = ['-x'] // silent
|
||||
if (displayId !== undefined) {
|
||||
args.push('-D', String(displayId))
|
||||
}
|
||||
return captureScreenToBase64(args)
|
||||
},
|
||||
|
||||
async captureRegion(
|
||||
_allowedBundleIds: string[],
|
||||
x: number, y: number, w: number, h: number,
|
||||
_outW: number, _outH: number, _quality: number,
|
||||
displayId?: number,
|
||||
): Promise<ScreenshotResult> {
|
||||
const args = ['-x', '-R', `${x},${y},${w},${h}`]
|
||||
if (displayId !== undefined) {
|
||||
args.push('-D', String(displayId))
|
||||
}
|
||||
return captureScreenToBase64(args)
|
||||
},
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ComputerUseAPI — Main export
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export class ComputerUseAPI {
|
||||
apps: AppsAPI = appsAPI
|
||||
display: DisplayAPI = displayAPI
|
||||
screenshot: ScreenshotAPI = screenshotAPI
|
||||
|
||||
async resolvePrepareCapture(
|
||||
allowedBundleIds: string[],
|
||||
surrogateHost: string,
|
||||
_surrogateHost: string,
|
||||
quality: number,
|
||||
targetW: number,
|
||||
targetH: number,
|
||||
displayId?: number,
|
||||
autoResolve?: boolean,
|
||||
doHide?: boolean,
|
||||
) => Promise<ResolvePrepareCaptureResult>
|
||||
_autoResolve?: boolean,
|
||||
_doHide?: boolean,
|
||||
): Promise<ResolvePrepareCaptureResult> {
|
||||
return this.screenshot.captureExcluding(allowedBundleIds, quality, targetW, targetH, displayId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,151 @@
|
||||
const stub: any = {}
|
||||
export default stub
|
||||
// audio-capture-napi: cross-platform audio capture using SoX (rec) on macOS
|
||||
// and arecord (ALSA) on Linux. Replaces the original cpal-based native module.
|
||||
|
||||
import { type ChildProcess, spawn, spawnSync } from 'child_process'
|
||||
|
||||
// ─── State ───────────────────────────────────────────────────────────
|
||||
|
||||
let recordingProcess: ChildProcess | null = null
|
||||
let availabilityCache: boolean | null = null
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
function commandExists(cmd: string): boolean {
|
||||
const result = spawnSync(cmd, ['--version'], {
|
||||
stdio: 'ignore',
|
||||
timeout: 3000,
|
||||
})
|
||||
return result.error === undefined
|
||||
}
|
||||
|
||||
// ─── Public API ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Check whether a supported audio recording command is available.
|
||||
* Returns true if `rec` (SoX) is found on macOS, or `arecord` (ALSA) on Linux.
|
||||
* Windows is not supported and always returns false.
|
||||
*/
|
||||
export function isNativeAudioAvailable(): boolean {
|
||||
if (availabilityCache !== null) {
|
||||
return availabilityCache
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
availabilityCache = false
|
||||
return false
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
// macOS: use SoX rec
|
||||
availabilityCache = commandExists('rec')
|
||||
return availabilityCache
|
||||
}
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
// Linux: prefer arecord, fall back to rec
|
||||
availabilityCache = commandExists('arecord') || commandExists('rec')
|
||||
return availabilityCache
|
||||
}
|
||||
|
||||
availabilityCache = false
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a recording is currently in progress.
|
||||
*/
|
||||
export function isNativeRecordingActive(): boolean {
|
||||
return recordingProcess !== null && !recordingProcess.killed
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the active recording process, if any.
|
||||
*/
|
||||
export function stopNativeRecording(): void {
|
||||
if (recordingProcess) {
|
||||
const proc = recordingProcess
|
||||
recordingProcess = null
|
||||
if (!proc.killed) {
|
||||
proc.kill('SIGTERM')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording audio. Raw PCM data (16kHz, 16-bit signed, mono) is
|
||||
* streamed via the onData callback. onEnd is called when recording stops
|
||||
* (either from silence detection or process termination).
|
||||
*
|
||||
* Returns true if recording started successfully, false otherwise.
|
||||
*/
|
||||
export function startNativeRecording(
|
||||
onData: (data: Buffer) => void,
|
||||
onEnd: () => void,
|
||||
): boolean {
|
||||
// Don't start if already recording
|
||||
if (isNativeRecordingActive()) {
|
||||
stopNativeRecording()
|
||||
}
|
||||
|
||||
if (!isNativeAudioAvailable()) {
|
||||
return false
|
||||
}
|
||||
|
||||
let child: ChildProcess
|
||||
|
||||
if (process.platform === 'darwin' || (process.platform === 'linux' && commandExists('rec'))) {
|
||||
// Use SoX rec: output raw PCM 16kHz 16-bit signed mono to stdout
|
||||
child = spawn(
|
||||
'rec',
|
||||
[
|
||||
'-q', // quiet
|
||||
'--buffer',
|
||||
'1024', // small buffer for low latency
|
||||
'-t', 'raw', // raw PCM output
|
||||
'-r', '16000', // 16kHz sample rate
|
||||
'-e', 'signed', // signed integer encoding
|
||||
'-b', '16', // 16-bit
|
||||
'-c', '1', // mono
|
||||
'-', // output to stdout
|
||||
],
|
||||
{ stdio: ['pipe', 'pipe', 'pipe'] },
|
||||
)
|
||||
} else if (process.platform === 'linux' && commandExists('arecord')) {
|
||||
// Use arecord: output raw PCM 16kHz 16-bit signed LE mono to stdout
|
||||
child = spawn(
|
||||
'arecord',
|
||||
[
|
||||
'-f', 'S16_LE', // signed 16-bit little-endian
|
||||
'-r', '16000', // 16kHz sample rate
|
||||
'-c', '1', // mono
|
||||
'-t', 'raw', // raw PCM, no header
|
||||
'-q', // quiet
|
||||
'-', // output to stdout
|
||||
],
|
||||
{ stdio: ['pipe', 'pipe', 'pipe'] },
|
||||
)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
recordingProcess = child
|
||||
|
||||
child.stdout?.on('data', (chunk: Buffer) => {
|
||||
onData(chunk)
|
||||
})
|
||||
|
||||
// Consume stderr to prevent backpressure
|
||||
child.stderr?.on('data', () => {})
|
||||
|
||||
child.on('close', () => {
|
||||
recordingProcess = null
|
||||
onEnd()
|
||||
})
|
||||
|
||||
child.on('error', () => {
|
||||
recordingProcess = null
|
||||
onEnd()
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
102
packages/color-diff-napi/src/__tests__/color-diff.test.ts
Normal file
102
packages/color-diff-napi/src/__tests__/color-diff.test.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { __test } from "../index";
|
||||
|
||||
const { ansi256FromRgb, colorToEscape, detectColorMode, detectLanguage, tokenize } = __test;
|
||||
|
||||
describe("ansi256FromRgb", () => {
|
||||
test("black maps to index 16", () => {
|
||||
expect(ansi256FromRgb(0, 0, 0)).toBe(16);
|
||||
});
|
||||
|
||||
test("pure red maps to cube red", () => {
|
||||
expect(ansi256FromRgb(255, 0, 0)).toBe(196);
|
||||
});
|
||||
|
||||
test("pure green maps to cube green", () => {
|
||||
expect(ansi256FromRgb(0, 255, 0)).toBe(46);
|
||||
});
|
||||
|
||||
test("pure blue maps to cube blue", () => {
|
||||
expect(ansi256FromRgb(0, 0, 255)).toBe(21);
|
||||
});
|
||||
|
||||
test("grey values map to grey ramp", () => {
|
||||
const idx = ansi256FromRgb(128, 128, 128);
|
||||
// Should be in the grey ramp range (232-255)
|
||||
expect(idx).toBeGreaterThanOrEqual(232);
|
||||
expect(idx).toBeLessThanOrEqual(255);
|
||||
});
|
||||
});
|
||||
|
||||
describe("colorToEscape", () => {
|
||||
test("palette index < 8 uses standard ANSI codes", () => {
|
||||
const color = { r: 1, g: 0, b: 0, a: 0 }; // palette index 1
|
||||
expect(colorToEscape(color, true, "truecolor")).toBe("\x1b[31m"); // fg red
|
||||
expect(colorToEscape(color, false, "truecolor")).toBe("\x1b[41m"); // bg red
|
||||
});
|
||||
|
||||
test("palette index 8-15 uses bright ANSI codes", () => {
|
||||
const color = { r: 9, g: 0, b: 0, a: 0 }; // bright red
|
||||
expect(colorToEscape(color, true, "truecolor")).toBe("\x1b[91m");
|
||||
});
|
||||
|
||||
test("alpha=1 returns terminal default", () => {
|
||||
const color = { r: 0, g: 0, b: 0, a: 1 };
|
||||
expect(colorToEscape(color, true, "truecolor")).toBe("\x1b[39m");
|
||||
expect(colorToEscape(color, false, "truecolor")).toBe("\x1b[49m");
|
||||
});
|
||||
|
||||
test("truecolor uses RGB escape", () => {
|
||||
const color = { r: 100, g: 150, b: 200, a: 255 };
|
||||
expect(colorToEscape(color, true, "truecolor")).toBe("\x1b[38;2;100;150;200m");
|
||||
});
|
||||
|
||||
test("color256 uses 256-color escape", () => {
|
||||
const color = { r: 100, g: 150, b: 200, a: 255 };
|
||||
const result = colorToEscape(color, true, "color256");
|
||||
expect(result).toMatch(/^\x1b\[38;5;\d+m$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("detectColorMode", () => {
|
||||
test("returns ansi for ansi-containing theme names", () => {
|
||||
expect(detectColorMode("ansi")).toBe("ansi");
|
||||
expect(detectColorMode("base16-ansi-dark")).toBe("ansi");
|
||||
});
|
||||
|
||||
test("returns truecolor or color256 for non-ansi themes", () => {
|
||||
const mode = detectColorMode("monokai");
|
||||
expect(["truecolor", "color256"]).toContain(mode);
|
||||
});
|
||||
});
|
||||
|
||||
describe("detectLanguage", () => {
|
||||
test("detects language from file extension", () => {
|
||||
expect(detectLanguage("index.ts")).toBe("ts");
|
||||
expect(detectLanguage("main.py")).toBe("py");
|
||||
expect(detectLanguage("style.css")).toBe("css");
|
||||
});
|
||||
|
||||
test("detects language from known filenames", () => {
|
||||
expect(detectLanguage("Makefile")).toBe("makefile");
|
||||
expect(detectLanguage("Dockerfile")).toBe("dockerfile");
|
||||
});
|
||||
|
||||
test("returns null for unknown extensions", () => {
|
||||
expect(detectLanguage("file.xyz123")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("tokenize", () => {
|
||||
test("returns array of tokens", () => {
|
||||
const result = tokenize("hello world");
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("preserves original text when joined", () => {
|
||||
const text = "foo bar baz";
|
||||
const tokens = tokenize(text);
|
||||
expect(tokens.join("")).toBe(text);
|
||||
});
|
||||
});
|
||||
@@ -30,7 +30,7 @@ import { basename, extname } from 'path'
|
||||
// pushed later tests in the same shard into GC-pause territory and a
|
||||
// beforeEach/afterEach hook timeout (officialRegistry.test.ts, PR #24150).
|
||||
// Same lazy pattern the NAPI wrapper used for dlopen.
|
||||
type HLJSApi = typeof hljsNamespace
|
||||
type HLJSApi = typeof hljsNamespace.default
|
||||
let cachedHljs: HLJSApi | null = null
|
||||
function hljs(): HLJSApi {
|
||||
if (cachedHljs) return cachedHljs
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
"types": "./src/index.ts",
|
||||
"dependencies": {
|
||||
"sharp": "^0.33.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,125 @@
|
||||
export function getNativeModule(): null {
|
||||
import sharpModule from 'sharp'
|
||||
|
||||
export const sharp = sharpModule
|
||||
|
||||
interface NativeModule {
|
||||
hasClipboardImage(): boolean
|
||||
readClipboardImage(
|
||||
maxWidth?: number,
|
||||
maxHeight?: number,
|
||||
): {
|
||||
png: Buffer
|
||||
width: number
|
||||
height: number
|
||||
originalWidth: number
|
||||
originalHeight: number
|
||||
} | null
|
||||
}
|
||||
|
||||
function createDarwinNativeModule(): NativeModule {
|
||||
return {
|
||||
hasClipboardImage(): boolean {
|
||||
try {
|
||||
const result = Bun.spawnSync({
|
||||
cmd: [
|
||||
'osascript',
|
||||
'-e',
|
||||
'try\nthe clipboard as «class PNGf»\nreturn "yes"\non error\nreturn "no"\nend try',
|
||||
],
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
})
|
||||
const output = result.stdout.toString().trim()
|
||||
return output === 'yes'
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
readClipboardImage(
|
||||
maxWidth?: number,
|
||||
maxHeight?: number,
|
||||
) {
|
||||
try {
|
||||
// Use osascript to read clipboard image as PNG data and write to a temp file,
|
||||
// then read the temp file back
|
||||
const tmpPath = `/tmp/claude_clipboard_native_${Date.now()}.png`
|
||||
const script = `
|
||||
set png_data to (the clipboard as «class PNGf»)
|
||||
set fp to open for access POSIX file "${tmpPath}" with write permission
|
||||
write png_data to fp
|
||||
close access fp
|
||||
return "${tmpPath}"
|
||||
`
|
||||
const result = Bun.spawnSync({
|
||||
cmd: ['osascript', '-e', script],
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
})
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const file = Bun.file(tmpPath)
|
||||
// Use synchronous read via Node compat
|
||||
const fs = require('fs')
|
||||
const buffer: Buffer = fs.readFileSync(tmpPath)
|
||||
|
||||
// Clean up temp file
|
||||
try {
|
||||
fs.unlinkSync(tmpPath)
|
||||
} catch {
|
||||
// ignore cleanup errors
|
||||
}
|
||||
|
||||
if (buffer.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Read PNG dimensions from IHDR chunk
|
||||
// PNG header: 8 bytes signature, then IHDR chunk
|
||||
// IHDR starts at offset 8 (4 bytes length) + 4 bytes "IHDR" + 4 bytes width + 4 bytes height
|
||||
let width = 0
|
||||
let height = 0
|
||||
if (buffer.length > 24 && buffer[12] === 0x49 && buffer[13] === 0x48 && buffer[14] === 0x44 && buffer[15] === 0x52) {
|
||||
width = buffer.readUInt32BE(16)
|
||||
height = buffer.readUInt32BE(20)
|
||||
}
|
||||
|
||||
const originalWidth = width
|
||||
const originalHeight = height
|
||||
|
||||
// If maxWidth/maxHeight are specified and the image exceeds them,
|
||||
// we still return the full PNG - the caller handles resizing via sharp
|
||||
// But we report the capped dimensions
|
||||
if (maxWidth && maxHeight) {
|
||||
if (width > maxWidth || height > maxHeight) {
|
||||
const scale = Math.min(maxWidth / width, maxHeight / height)
|
||||
width = Math.round(width * scale)
|
||||
height = Math.round(height * scale)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
png: buffer,
|
||||
width,
|
||||
height,
|
||||
originalWidth,
|
||||
originalHeight,
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function getNativeModule(): NativeModule | null {
|
||||
if (process.platform === 'darwin') {
|
||||
return createDarwinNativeModule()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const stub: any = {}
|
||||
export default stub
|
||||
export default sharp
|
||||
|
||||
@@ -1,5 +1,68 @@
|
||||
export function prewarm(): void {}
|
||||
import { dlopen, FFIType, suffix } from "bun:ffi";
|
||||
|
||||
export function isModifierPressed(_modifier: string): boolean {
|
||||
return false
|
||||
const FLAG_SHIFT = 0x20000;
|
||||
const FLAG_CONTROL = 0x40000;
|
||||
const FLAG_OPTION = 0x80000;
|
||||
const FLAG_COMMAND = 0x100000;
|
||||
|
||||
const modifierFlags: Record<string, number> = {
|
||||
shift: FLAG_SHIFT,
|
||||
control: FLAG_CONTROL,
|
||||
option: FLAG_OPTION,
|
||||
command: FLAG_COMMAND,
|
||||
};
|
||||
|
||||
// kCGEventSourceStateCombinedSessionState = 0
|
||||
const kCGEventSourceStateCombinedSessionState = 0;
|
||||
|
||||
let cgEventSourceFlagsState: ((stateID: number) => number) | null = null;
|
||||
|
||||
function loadFFI(): void {
|
||||
if (cgEventSourceFlagsState !== null || process.platform !== "darwin") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const lib = dlopen(
|
||||
`/System/Library/Frameworks/Carbon.framework/Carbon`,
|
||||
{
|
||||
CGEventSourceFlagsState: {
|
||||
args: [FFIType.i32],
|
||||
returns: FFIType.u64,
|
||||
},
|
||||
}
|
||||
);
|
||||
cgEventSourceFlagsState = (stateID: number): number => {
|
||||
return Number(lib.symbols.CGEventSourceFlagsState(stateID));
|
||||
};
|
||||
} catch {
|
||||
// If loading fails, keep the function null so isModifierPressed returns false
|
||||
cgEventSourceFlagsState = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function prewarm(): void {
|
||||
loadFFI();
|
||||
}
|
||||
|
||||
export function isModifierPressed(modifier: string): boolean {
|
||||
if (process.platform !== "darwin") {
|
||||
return false;
|
||||
}
|
||||
|
||||
loadFFI();
|
||||
|
||||
if (cgEventSourceFlagsState === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const flag = modifierFlags[modifier];
|
||||
if (flag === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentFlags = cgEventSourceFlagsState(
|
||||
kCGEventSourceStateCombinedSessionState
|
||||
);
|
||||
return (currentFlags & flag) !== 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export async function waitForUrlEvent(): Promise<string | null> {
|
||||
export async function waitForUrlEvent(timeoutMs?: number): Promise<string | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
163
scripts/health-check.ts
Normal file
163
scripts/health-check.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* 代码健康度检查脚本
|
||||
*
|
||||
* 汇总项目各维度指标,输出健康度报告:
|
||||
* - 代码规模(文件数、代码行数)
|
||||
* - Lint 问题数(Biome)
|
||||
* - 测试结果(Bun test)
|
||||
* - 冗余代码(Knip)
|
||||
* - 构建状态
|
||||
*/
|
||||
|
||||
import { $ } from "bun";
|
||||
|
||||
const DIVIDER = "─".repeat(60);
|
||||
|
||||
interface Metric {
|
||||
label: string;
|
||||
value: string | number;
|
||||
status: "ok" | "warn" | "error" | "info";
|
||||
}
|
||||
|
||||
const metrics: Metric[] = [];
|
||||
|
||||
function add(label: string, value: string | number, status: Metric["status"] = "info") {
|
||||
metrics.push({ label, value, status });
|
||||
}
|
||||
|
||||
function icon(status: Metric["status"]): string {
|
||||
switch (status) {
|
||||
case "ok":
|
||||
return "[OK]";
|
||||
case "warn":
|
||||
return "[!!]";
|
||||
case "error":
|
||||
return "[XX]";
|
||||
case "info":
|
||||
return "[--]";
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. 代码规模
|
||||
// ---------------------------------------------------------------------------
|
||||
async function checkCodeSize() {
|
||||
const tsFiles = await $`find src -name '*.ts' -o -name '*.tsx' | grep -v node_modules`.text();
|
||||
const fileCount = tsFiles.trim().split("\n").filter(Boolean).length;
|
||||
add("TypeScript 文件数", fileCount, "info");
|
||||
|
||||
const loc = await $`find src -name '*.ts' -o -name '*.tsx' | grep -v node_modules | xargs wc -l | tail -1`.text();
|
||||
const totalLines = loc.trim().split(/\s+/)[0] ?? "?";
|
||||
add("总代码行数 (src/)", totalLines, "info");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. Lint 检查
|
||||
// ---------------------------------------------------------------------------
|
||||
async function checkLint() {
|
||||
try {
|
||||
const result = await $`bunx biome check src/ 2>&1`.quiet().nothrow().text();
|
||||
const errorMatch = result.match(/Found (\d+) errors?/);
|
||||
const warnMatch = result.match(/Found (\d+) warnings?/);
|
||||
const errors = errorMatch ? Number.parseInt(errorMatch[1]) : 0;
|
||||
const warnings = warnMatch ? Number.parseInt(warnMatch[1]) : 0;
|
||||
add("Lint 错误", errors, errors === 0 ? "ok" : errors < 100 ? "warn" : "info");
|
||||
add("Lint 警告", warnings, warnings === 0 ? "ok" : "info");
|
||||
} catch {
|
||||
add("Lint 检查", "执行失败", "error");
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3. 测试
|
||||
// ---------------------------------------------------------------------------
|
||||
async function checkTests() {
|
||||
try {
|
||||
const result = await $`bun test 2>&1`.quiet().nothrow().text();
|
||||
const passMatch = result.match(/(\d+) pass/);
|
||||
const failMatch = result.match(/(\d+) fail/);
|
||||
const pass = passMatch ? Number.parseInt(passMatch[1]) : 0;
|
||||
const fail = failMatch ? Number.parseInt(failMatch[1]) : 0;
|
||||
add("测试通过", pass, pass > 0 ? "ok" : "warn");
|
||||
add("测试失败", fail, fail === 0 ? "ok" : "error");
|
||||
} catch {
|
||||
add("测试", "执行失败", "error");
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 4. 冗余代码
|
||||
// ---------------------------------------------------------------------------
|
||||
async function checkUnused() {
|
||||
try {
|
||||
const result = await $`bunx knip-bun 2>&1`.quiet().nothrow().text();
|
||||
const unusedFiles = result.match(/Unused files \((\d+)\)/);
|
||||
const unusedExports = result.match(/Unused exports \((\d+)\)/);
|
||||
const unusedDeps = result.match(/Unused dependencies \((\d+)\)/);
|
||||
add("未使用文件", unusedFiles?.[1] ?? "0", "info");
|
||||
add("未使用导出", unusedExports?.[1] ?? "0", "info");
|
||||
add("未使用依赖", unusedDeps?.[1] ?? "0", unusedDeps && Number(unusedDeps[1]) > 0 ? "warn" : "ok");
|
||||
} catch {
|
||||
add("冗余代码检查", "执行失败", "error");
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 5. 构建
|
||||
// ---------------------------------------------------------------------------
|
||||
async function checkBuild() {
|
||||
try {
|
||||
const result = await $`bun run build 2>&1`.quiet().nothrow();
|
||||
if (result.exitCode === 0) {
|
||||
// 获取产物大小
|
||||
const stat = Bun.file("dist/cli.js");
|
||||
const mb = (stat.size / 1024 / 1024).toFixed(1);
|
||||
const size = `${mb} MB`;
|
||||
add("构建状态", "成功", "ok");
|
||||
add("产物大小 (dist/cli.js)", size, "info");
|
||||
} else {
|
||||
add("构建状态", "失败", "error");
|
||||
}
|
||||
} catch {
|
||||
add("构建", "执行失败", "error");
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Run
|
||||
// ---------------------------------------------------------------------------
|
||||
console.log("");
|
||||
console.log(DIVIDER);
|
||||
console.log(" 代码健康度检查报告");
|
||||
console.log(` ${new Date().toLocaleString("zh-CN")}`);
|
||||
console.log(DIVIDER);
|
||||
|
||||
await checkCodeSize();
|
||||
await checkLint();
|
||||
await checkTests();
|
||||
await checkUnused();
|
||||
await checkBuild();
|
||||
|
||||
console.log("");
|
||||
for (const m of metrics) {
|
||||
const tag = icon(m.status);
|
||||
console.log(` ${tag} ${m.label.padEnd(20)} ${m.value}`);
|
||||
}
|
||||
|
||||
const errorCount = metrics.filter((m) => m.status === "error").length;
|
||||
const warnCount = metrics.filter((m) => m.status === "warn").length;
|
||||
|
||||
console.log("");
|
||||
console.log(DIVIDER);
|
||||
if (errorCount > 0) {
|
||||
console.log(` 结果: ${errorCount} 个错误, ${warnCount} 个警告`);
|
||||
} else if (warnCount > 0) {
|
||||
console.log(` 结果: 无错误, ${warnCount} 个警告`);
|
||||
} else {
|
||||
console.log(" 结果: 全部通过");
|
||||
}
|
||||
console.log(DIVIDER);
|
||||
console.log("");
|
||||
|
||||
process.exit(errorCount > 0 ? 1 : 0);
|
||||
40
scripts/remove-sourcemaps.mjs
Normal file
40
scripts/remove-sourcemaps.mjs
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 清除 src/ 下所有 .ts/.tsx 文件中的 //# sourceMappingURL= 行
|
||||
* 用法: node scripts/remove-sourcemaps.mjs [--dry-run]
|
||||
*/
|
||||
import { readdir, readFile, writeFile } from "fs/promises";
|
||||
import { join, extname } from "path";
|
||||
|
||||
const SRC_DIR = new URL("../src", import.meta.url).pathname;
|
||||
const DRY_RUN = process.argv.includes("--dry-run");
|
||||
const EXTENSIONS = new Set([".ts", ".tsx"]);
|
||||
const PATTERN = /^\s*\/\/# sourceMappingURL=.*$/gm;
|
||||
|
||||
async function* walk(dir) {
|
||||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||
const full = join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
yield* walk(full);
|
||||
} else if (EXTENSIONS.has(extname(entry.name))) {
|
||||
yield full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let total = 0;
|
||||
for await (const file of walk(SRC_DIR)) {
|
||||
const content = await readFile(file, "utf8");
|
||||
if (!PATTERN.test(content)) continue;
|
||||
// reset lastIndex after test
|
||||
PATTERN.lastIndex = 0;
|
||||
const cleaned = content.replace(PATTERN, "").replace(/\n{3,}/g, "\n\n");
|
||||
if (DRY_RUN) {
|
||||
console.log(`[dry-run] ${file}`);
|
||||
} else {
|
||||
await writeFile(file, cleaned, "utf8");
|
||||
}
|
||||
total++;
|
||||
}
|
||||
|
||||
console.log(`\n${DRY_RUN ? "[dry-run] " : ""}Processed ${total} files.`);
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
SDKStatus,
|
||||
SDKUserMessageReplay,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { BetaMessageDeltaUsage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
|
||||
import { accumulateUsage, updateUsage } from 'src/services/api/claude.js'
|
||||
import type { NonNullableUsage } from 'src/services/api/logging.js'
|
||||
import { EMPTY_USAGE } from 'src/services/api/logging.js'
|
||||
@@ -39,7 +40,8 @@ import type { AppState } from './state/AppState.js'
|
||||
import { type Tools, type ToolUseContext, toolMatchesName } from './Tool.js'
|
||||
import type { AgentDefinition } from './tools/AgentTool/loadAgentsDir.js'
|
||||
import { SYNTHETIC_OUTPUT_TOOL_NAME } from './tools/SyntheticOutputTool/SyntheticOutputTool.js'
|
||||
import type { Message } from './types/message.js'
|
||||
import type { APIError } from '@anthropic-ai/sdk'
|
||||
import type { CompactMetadata, Message, SystemCompactBoundaryMessage } from './types/message.js'
|
||||
import type { OrphanedPermission } from './types/textInputTypes.js'
|
||||
import { createAbortController } from './utils/abortController.js'
|
||||
import type { AttributionState } from './utils/commitAttribution.js'
|
||||
@@ -261,6 +263,7 @@ export class QueryEngine {
|
||||
// Track denials for SDK reporting
|
||||
if (result.behavior !== 'allow') {
|
||||
this.permissionDenials.push({
|
||||
type: 'permission_denial',
|
||||
tool_name: sdkCompatToolName(tool.name),
|
||||
tool_use_id: toolUseID,
|
||||
tool_input: input,
|
||||
@@ -577,7 +580,7 @@ export class QueryEngine {
|
||||
timestamp: msg.timestamp,
|
||||
isReplay: !msg.isCompactSummary,
|
||||
isSynthetic: msg.isMeta || msg.isVisibleInTranscriptOnly,
|
||||
} as SDKUserMessageReplay
|
||||
} as unknown as SDKUserMessageReplay
|
||||
}
|
||||
|
||||
// Local command output — yield as a synthetic assistant message so
|
||||
@@ -595,13 +598,14 @@ export class QueryEngine {
|
||||
}
|
||||
|
||||
if (msg.type === 'system' && msg.subtype === 'compact_boundary') {
|
||||
const compactMsg = msg as SystemCompactBoundaryMessage
|
||||
yield {
|
||||
type: 'system',
|
||||
subtype: 'compact_boundary' as const,
|
||||
session_id: getSessionId(),
|
||||
uuid: msg.uuid,
|
||||
compact_metadata: toSDKCompactMetadata(msg.compactMetadata),
|
||||
} as SDKCompactBoundaryMessage
|
||||
compact_metadata: toSDKCompactMetadata(compactMsg.compactMetadata),
|
||||
} as unknown as SDKCompactBoundaryMessage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,7 +707,8 @@ export class QueryEngine {
|
||||
message.type === 'system' &&
|
||||
message.subtype === 'compact_boundary'
|
||||
) {
|
||||
const tailUuid = message.compactMetadata?.preservedSegment?.tailUuid
|
||||
const compactMsg = message as SystemCompactBoundaryMessage
|
||||
const tailUuid = compactMsg.compactMetadata?.preservedSegment?.tailUuid
|
||||
if (tailUuid) {
|
||||
const tailIdx = this.mutableMessages.findLastIndex(
|
||||
m => m.uuid === tailUuid,
|
||||
@@ -713,7 +718,7 @@ export class QueryEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
messages.push(message)
|
||||
messages.push(message as Message)
|
||||
if (persistSession) {
|
||||
// Fire-and-forget for assistant messages. claude.ts yields one
|
||||
// assistant message per content block, then mutates the last
|
||||
@@ -744,7 +749,7 @@ export class QueryEngine {
|
||||
uuid: msgToAck.uuid,
|
||||
timestamp: msgToAck.timestamp,
|
||||
isReplay: true,
|
||||
} as SDKUserMessageReplay
|
||||
} as unknown as SDKUserMessageReplay
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -758,56 +763,66 @@ export class QueryEngine {
|
||||
case 'tombstone':
|
||||
// Tombstone messages are control signals for removing messages, skip them
|
||||
break
|
||||
case 'assistant':
|
||||
case 'assistant': {
|
||||
// Capture stop_reason if already set (synthetic messages). For
|
||||
// streamed responses, this is null at content_block_stop time;
|
||||
// the real value arrives via message_delta (handled below).
|
||||
if (message.message.stop_reason != null) {
|
||||
lastStopReason = message.message.stop_reason
|
||||
const msg = message as Message
|
||||
const stopReason = msg.message?.stop_reason as string | null | undefined
|
||||
if (stopReason != null) {
|
||||
lastStopReason = stopReason
|
||||
}
|
||||
this.mutableMessages.push(message)
|
||||
yield* normalizeMessage(message)
|
||||
this.mutableMessages.push(msg)
|
||||
yield* normalizeMessage(msg)
|
||||
break
|
||||
case 'progress':
|
||||
this.mutableMessages.push(message)
|
||||
}
|
||||
case 'progress': {
|
||||
const msg = message as Message
|
||||
this.mutableMessages.push(msg)
|
||||
// Record inline so the dedup loop in the next ask() call sees it
|
||||
// as already-recorded. Without this, deferred progress interleaves
|
||||
// with already-recorded tool_results in mutableMessages, and the
|
||||
// dedup walk freezes startingParentUuid at the wrong message —
|
||||
// forking the chain and orphaning the conversation on resume.
|
||||
if (persistSession) {
|
||||
messages.push(message)
|
||||
messages.push(msg)
|
||||
void recordTranscript(messages)
|
||||
}
|
||||
yield* normalizeMessage(message)
|
||||
yield* normalizeMessage(msg)
|
||||
break
|
||||
case 'user':
|
||||
this.mutableMessages.push(message)
|
||||
yield* normalizeMessage(message)
|
||||
}
|
||||
case 'user': {
|
||||
const msg = message as Message
|
||||
this.mutableMessages.push(msg)
|
||||
yield* normalizeMessage(msg)
|
||||
break
|
||||
case 'stream_event':
|
||||
if (message.event.type === 'message_start') {
|
||||
}
|
||||
case 'stream_event': {
|
||||
const event = (message as unknown as { event: Record<string, unknown> }).event
|
||||
if (event.type === 'message_start') {
|
||||
// Reset current message usage for new message
|
||||
currentMessageUsage = EMPTY_USAGE
|
||||
const eventMessage = event.message as { usage: BetaMessageDeltaUsage }
|
||||
currentMessageUsage = updateUsage(
|
||||
currentMessageUsage,
|
||||
message.event.message.usage,
|
||||
eventMessage.usage,
|
||||
)
|
||||
}
|
||||
if (message.event.type === 'message_delta') {
|
||||
if (event.type === 'message_delta') {
|
||||
currentMessageUsage = updateUsage(
|
||||
currentMessageUsage,
|
||||
message.event.usage,
|
||||
event.usage as BetaMessageDeltaUsage,
|
||||
)
|
||||
// Capture stop_reason from message_delta. The assistant message
|
||||
// is yielded at content_block_stop with stop_reason=null; the
|
||||
// real value only arrives here (see claude.ts message_delta
|
||||
// handler). Without this, result.stop_reason is always null.
|
||||
if (message.event.delta.stop_reason != null) {
|
||||
lastStopReason = message.event.delta.stop_reason
|
||||
const delta = event.delta as { stop_reason?: string | null }
|
||||
if (delta.stop_reason != null) {
|
||||
lastStopReason = delta.stop_reason
|
||||
}
|
||||
}
|
||||
if (message.event.type === 'message_stop') {
|
||||
if (event.type === 'message_stop') {
|
||||
// Accumulate current message usage into total
|
||||
this.totalUsage = accumulateUsage(
|
||||
this.totalUsage,
|
||||
@@ -818,7 +833,7 @@ export class QueryEngine {
|
||||
if (includePartialMessages) {
|
||||
yield {
|
||||
type: 'stream_event' as const,
|
||||
event: message.event,
|
||||
event,
|
||||
session_id: getSessionId(),
|
||||
parent_tool_use_id: null,
|
||||
uuid: randomUUID(),
|
||||
@@ -826,20 +841,24 @@ export class QueryEngine {
|
||||
}
|
||||
|
||||
break
|
||||
case 'attachment':
|
||||
this.mutableMessages.push(message)
|
||||
}
|
||||
case 'attachment': {
|
||||
const msg = message as Message
|
||||
this.mutableMessages.push(msg)
|
||||
// Record inline (same reason as progress above).
|
||||
if (persistSession) {
|
||||
messages.push(message)
|
||||
messages.push(msg)
|
||||
void recordTranscript(messages)
|
||||
}
|
||||
|
||||
const attachment = msg.attachment as { type: string; data?: unknown; turnCount?: number; maxTurns?: number; prompt?: string; source_uuid?: string; [key: string]: unknown }
|
||||
|
||||
// Extract structured output from StructuredOutput tool calls
|
||||
if (message.attachment.type === 'structured_output') {
|
||||
structuredOutputFromTool = message.attachment.data
|
||||
if (attachment.type === 'structured_output') {
|
||||
structuredOutputFromTool = attachment.data
|
||||
}
|
||||
// Handle max turns reached signal from query.ts
|
||||
else if (message.attachment.type === 'max_turns_reached') {
|
||||
else if (attachment.type === 'max_turns_reached') {
|
||||
if (persistSession) {
|
||||
if (
|
||||
isEnvTruthy(process.env.CLAUDE_CODE_EAGER_FLUSH) ||
|
||||
@@ -854,7 +873,7 @@ export class QueryEngine {
|
||||
duration_ms: Date.now() - startTime,
|
||||
duration_api_ms: getTotalAPIDuration(),
|
||||
is_error: true,
|
||||
num_turns: message.attachment.turnCount,
|
||||
num_turns: attachment.turnCount as number,
|
||||
stop_reason: lastStopReason,
|
||||
session_id: getSessionId(),
|
||||
total_cost_usd: getTotalCost(),
|
||||
@@ -867,7 +886,7 @@ export class QueryEngine {
|
||||
),
|
||||
uuid: randomUUID(),
|
||||
errors: [
|
||||
`Reached maximum number of turns (${message.attachment.maxTurns})`,
|
||||
`Reached maximum number of turns (${attachment.maxTurns})`,
|
||||
],
|
||||
}
|
||||
return
|
||||
@@ -875,26 +894,28 @@ export class QueryEngine {
|
||||
// Yield queued_command attachments as SDK user message replays
|
||||
else if (
|
||||
replayUserMessages &&
|
||||
message.attachment.type === 'queued_command'
|
||||
attachment.type === 'queued_command'
|
||||
) {
|
||||
yield {
|
||||
type: 'user',
|
||||
message: {
|
||||
role: 'user' as const,
|
||||
content: message.attachment.prompt,
|
||||
content: attachment.prompt,
|
||||
},
|
||||
session_id: getSessionId(),
|
||||
parent_tool_use_id: null,
|
||||
uuid: message.attachment.source_uuid || message.uuid,
|
||||
timestamp: message.timestamp,
|
||||
uuid: attachment.source_uuid || msg.uuid,
|
||||
timestamp: msg.timestamp,
|
||||
isReplay: true,
|
||||
} as SDKUserMessageReplay
|
||||
} as unknown as SDKUserMessageReplay
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'stream_request_start':
|
||||
// Don't yield stream request start messages
|
||||
break
|
||||
case 'system': {
|
||||
const msg = message as Message
|
||||
// Snip boundary: replay on our store to remove zombie messages and
|
||||
// stale markers. The yielded boundary is a signal, not data to push —
|
||||
// the replay produces its own equivalent boundary. Without this,
|
||||
@@ -903,7 +924,7 @@ export class QueryEngine {
|
||||
// check lives inside the injected callback so feature-gated strings
|
||||
// stay out of this file (excluded-strings check).
|
||||
const snipResult = this.config.snipReplay?.(
|
||||
message,
|
||||
msg,
|
||||
this.mutableMessages,
|
||||
)
|
||||
if (snipResult !== undefined) {
|
||||
@@ -913,12 +934,13 @@ export class QueryEngine {
|
||||
}
|
||||
break
|
||||
}
|
||||
this.mutableMessages.push(message)
|
||||
this.mutableMessages.push(msg)
|
||||
// Yield compact boundary messages to SDK
|
||||
if (
|
||||
message.subtype === 'compact_boundary' &&
|
||||
message.compactMetadata
|
||||
msg.subtype === 'compact_boundary' &&
|
||||
msg.compactMetadata
|
||||
) {
|
||||
const compactMsg = msg as SystemCompactBoundaryMessage
|
||||
// Release pre-compaction messages for GC. The boundary was just
|
||||
// pushed so it's the last element. query.ts already uses
|
||||
// getMessagesAfterCompactBoundary() internally, so only
|
||||
@@ -936,36 +958,39 @@ export class QueryEngine {
|
||||
type: 'system',
|
||||
subtype: 'compact_boundary' as const,
|
||||
session_id: getSessionId(),
|
||||
uuid: message.uuid,
|
||||
compact_metadata: toSDKCompactMetadata(message.compactMetadata),
|
||||
uuid: msg.uuid,
|
||||
compact_metadata: toSDKCompactMetadata(compactMsg.compactMetadata),
|
||||
}
|
||||
}
|
||||
if (message.subtype === 'api_error') {
|
||||
if (msg.subtype === 'api_error') {
|
||||
const apiErrorMsg = msg as Message & { retryAttempt: number; maxRetries: number; retryInMs: number; error: APIError }
|
||||
yield {
|
||||
type: 'system',
|
||||
subtype: 'api_retry' as const,
|
||||
attempt: message.retryAttempt,
|
||||
max_retries: message.maxRetries,
|
||||
retry_delay_ms: message.retryInMs,
|
||||
error_status: message.error.status ?? null,
|
||||
error: categorizeRetryableAPIError(message.error),
|
||||
attempt: apiErrorMsg.retryAttempt,
|
||||
max_retries: apiErrorMsg.maxRetries,
|
||||
retry_delay_ms: apiErrorMsg.retryInMs,
|
||||
error_status: apiErrorMsg.error.status ?? null,
|
||||
error: categorizeRetryableAPIError(apiErrorMsg.error),
|
||||
session_id: getSessionId(),
|
||||
uuid: message.uuid,
|
||||
uuid: msg.uuid,
|
||||
}
|
||||
}
|
||||
// Don't yield other system messages in headless mode
|
||||
break
|
||||
}
|
||||
case 'tool_use_summary':
|
||||
case 'tool_use_summary': {
|
||||
const msg = message as Message & { summary: unknown; precedingToolUseIds: unknown }
|
||||
// Yield tool use summary messages to SDK
|
||||
yield {
|
||||
type: 'tool_use_summary' as const,
|
||||
summary: message.summary,
|
||||
preceding_tool_use_ids: message.precedingToolUseIds,
|
||||
summary: msg.summary,
|
||||
preceding_tool_use_ids: msg.precedingToolUseIds,
|
||||
session_id: getSessionId(),
|
||||
uuid: message.uuid,
|
||||
uuid: msg.uuid,
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check if USD budget has been exceeded
|
||||
@@ -1251,7 +1276,7 @@ export async function* ask({
|
||||
tools,
|
||||
commands,
|
||||
mcpClients,
|
||||
agents,
|
||||
agents: agents ?? [],
|
||||
canUseTool,
|
||||
getAppState,
|
||||
setAppState,
|
||||
|
||||
@@ -314,7 +314,7 @@ export function filterToolProgressMessages(
|
||||
): ProgressMessage<ToolProgressData>[] {
|
||||
return progressMessagesForMessage.filter(
|
||||
(msg): msg is ProgressMessage<ToolProgressData> =>
|
||||
msg.data?.type !== 'hook_progress',
|
||||
(msg.data as { type?: string })?.type !== 'hook_progress',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export const AssistantSessionChooser: any = (() => {}) as any;
|
||||
export const AssistantSessionChooser: (props: Record<string, unknown>) => null = () => null;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export const isKairosEnabled: any = (() => {}) as any;
|
||||
export const isKairosEnabled: () => Promise<boolean> = () => Promise.resolve(false);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export const isAssistantMode: any = (() => {}) as any;
|
||||
export const initializeAssistantTeam: any = (() => {}) as any;
|
||||
export const markAssistantForced: any = (() => {}) as any;
|
||||
export const isAssistantForced: any = (() => {}) as any;
|
||||
export const getAssistantSystemPromptAddendum: any = (() => {}) as any;
|
||||
export const getAssistantActivationPath: any = (() => {}) as any;
|
||||
export const isAssistantMode: () => boolean = () => false;
|
||||
export const initializeAssistantTeam: () => Promise<void> = async () => {};
|
||||
export const markAssistantForced: () => void = () => {};
|
||||
export const isAssistantForced: () => boolean = () => false;
|
||||
export const getAssistantSystemPromptAddendum: () => string = () => '';
|
||||
export const getAssistantActivationPath: () => string | undefined = () => undefined;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export type AssistantSession = any;
|
||||
export const discoverAssistantSessions: any = (() => {}) as any;
|
||||
export type AssistantSession = { id: string; [key: string]: unknown };
|
||||
export const discoverAssistantSessions: () => Promise<AssistantSession[]> = () => Promise.resolve([]);
|
||||
|
||||
@@ -1755,4 +1755,4 @@ export function getPromptId(): string | null {
|
||||
export function setPromptId(id: string | null): void {
|
||||
STATE.promptId = id
|
||||
}
|
||||
export type isReplBridgeActive = any;
|
||||
export function isReplBridgeActive(): boolean { return false; }
|
||||
|
||||
@@ -103,7 +103,7 @@ export function isEligibleBridgeMessage(m: Message): boolean {
|
||||
export function extractTitleText(m: Message): string | undefined {
|
||||
if (m.type !== 'user' || m.isMeta || m.toolUseResult || m.isCompactSummary)
|
||||
return undefined
|
||||
if (m.origin && m.origin.kind !== 'human') return undefined
|
||||
if (m.origin && (m.origin as { kind?: string }).kind !== 'human') return undefined
|
||||
const content = m.message.content
|
||||
let raw: string | undefined
|
||||
if (typeof content === 'string') {
|
||||
@@ -151,7 +151,7 @@ export function handleIngressMessage(
|
||||
// Must respond promptly or the server kills the WS (~10-14s timeout).
|
||||
if (isSDKControlRequest(parsed)) {
|
||||
logForDebugging(
|
||||
`[bridge:repl] Inbound control_request subtype=${parsed.request.subtype}`,
|
||||
`[bridge:repl] Inbound control_request subtype=${(parsed.request as { subtype?: string }).subtype}`,
|
||||
)
|
||||
onControlRequest?.(parsed)
|
||||
return
|
||||
@@ -265,7 +265,8 @@ export function handleServerControlRequest(
|
||||
// Outbound-only: reply error for mutable requests so claude.ai doesn't show
|
||||
// false success. initialize must still succeed (server kills the connection
|
||||
// if it doesn't — see comment above).
|
||||
if (outboundOnly && request.request.subtype !== 'initialize') {
|
||||
const req = request.request as { subtype: string; model?: string; max_thinking_tokens?: number | null; mode?: string; [key: string]: unknown }
|
||||
if (outboundOnly && req.subtype !== 'initialize') {
|
||||
response = {
|
||||
type: 'control_response',
|
||||
response: {
|
||||
@@ -277,12 +278,12 @@ export function handleServerControlRequest(
|
||||
const event = { ...response, session_id: sessionId }
|
||||
void transport.write(event)
|
||||
logForDebugging(
|
||||
`[bridge:repl] Rejected ${request.request.subtype} (outbound-only) request_id=${request.request_id}`,
|
||||
`[bridge:repl] Rejected ${req.subtype} (outbound-only) request_id=${request.request_id}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
switch (request.request.subtype) {
|
||||
switch (req.subtype) {
|
||||
case 'initialize':
|
||||
// Respond with minimal capabilities — the REPL handles
|
||||
// commands, models, and account info itself.
|
||||
@@ -304,7 +305,7 @@ export function handleServerControlRequest(
|
||||
break
|
||||
|
||||
case 'set_model':
|
||||
onSetModel?.(request.request.model)
|
||||
onSetModel?.(req.model)
|
||||
response = {
|
||||
type: 'control_response',
|
||||
response: {
|
||||
@@ -315,7 +316,7 @@ export function handleServerControlRequest(
|
||||
break
|
||||
|
||||
case 'set_max_thinking_tokens':
|
||||
onSetMaxThinkingTokens?.(request.request.max_thinking_tokens)
|
||||
onSetMaxThinkingTokens?.(req.max_thinking_tokens ?? null)
|
||||
response = {
|
||||
type: 'control_response',
|
||||
response: {
|
||||
@@ -333,7 +334,7 @@ export function handleServerControlRequest(
|
||||
// see daemonBridge.ts), return an error verdict rather than a silent
|
||||
// false-success: the mode is never actually applied in that context,
|
||||
// so success would lie to the client.
|
||||
const verdict = onSetPermissionMode?.(request.request.mode) ?? {
|
||||
const verdict = onSetPermissionMode?.(req.mode as PermissionMode) ?? {
|
||||
ok: false,
|
||||
error:
|
||||
'set_permission_mode is not supported in this context (onSetPermissionMode callback not registered)',
|
||||
@@ -352,7 +353,7 @@ export function handleServerControlRequest(
|
||||
response: {
|
||||
subtype: 'error',
|
||||
request_id: request.request_id,
|
||||
error: verdict.error,
|
||||
error: (verdict as { ok: false; error: string }).error,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -378,7 +379,7 @@ export function handleServerControlRequest(
|
||||
response: {
|
||||
subtype: 'error',
|
||||
request_id: request.request_id,
|
||||
error: `REPL bridge does not handle control_request subtype: ${request.request.subtype}`,
|
||||
error: `REPL bridge does not handle control_request subtype: ${req.subtype}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -386,7 +387,7 @@ export function handleServerControlRequest(
|
||||
const event = { ...response, session_id: sessionId }
|
||||
void transport.write(event)
|
||||
logForDebugging(
|
||||
`[bridge:repl] Sent control_response for ${request.request.subtype} request_id=${request.request_id} result=${response.response.subtype}`,
|
||||
`[bridge:repl] Sent control_response for ${req.subtype} request_id=${request.request_id} result=${(response.response as { subtype?: string }).subtype}`,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -398,7 +399,7 @@ export function handleServerControlRequest(
|
||||
*/
|
||||
export function makeResultMessage(sessionId: string): SDKResultSuccess {
|
||||
return {
|
||||
type: 'result',
|
||||
type: 'result_success',
|
||||
subtype: 'success',
|
||||
duration_ms: 0,
|
||||
duration_api_ms: 0,
|
||||
|
||||
@@ -24,7 +24,7 @@ export function extractInboundMessageFields(
|
||||
| { content: string | Array<ContentBlockParam>; uuid: UUID | undefined }
|
||||
| undefined {
|
||||
if (msg.type !== 'user') return undefined
|
||||
const content = msg.message?.content
|
||||
const content = (msg.message as { content?: string | Array<ContentBlockParam> } | undefined)?.content
|
||||
if (!content) return undefined
|
||||
if (Array.isArray(content) && content.length === 0) return undefined
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ export async function initReplBridge(
|
||||
msg.isMeta ||
|
||||
msg.toolUseResult ||
|
||||
msg.isCompactSummary ||
|
||||
(msg.origin && msg.origin.kind !== 'human') ||
|
||||
(msg.origin && (msg.origin as { kind?: string }).kind !== 'human') ||
|
||||
isSyntheticMessage(msg)
|
||||
)
|
||||
continue
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export const postInterClaudeMessage: any = (() => {}) as any;
|
||||
export const postInterClaudeMessage: (target: string, message: string) => Promise<{ ok: boolean; error?: string }> = () => Promise.resolve({ ok: false });
|
||||
|
||||
@@ -812,11 +812,11 @@ export async function initEnvLessBridgeCore(
|
||||
},
|
||||
writeSdkMessages(messages: SDKMessage[]) {
|
||||
const filtered = messages.filter(
|
||||
m => !m.uuid || !recentPostedUUIDs.has(m.uuid),
|
||||
m => !m.uuid || !recentPostedUUIDs.has(m.uuid as string),
|
||||
)
|
||||
if (filtered.length === 0) return
|
||||
for (const msg of filtered) {
|
||||
if (msg.uuid) recentPostedUUIDs.add(msg.uuid)
|
||||
if (msg.uuid) recentPostedUUIDs.add(msg.uuid as string)
|
||||
}
|
||||
const events = filtered.map(m => ({ ...m, session_id: sessionId }))
|
||||
void transport.writeBatch(events)
|
||||
@@ -829,7 +829,7 @@ export async function initEnvLessBridgeCore(
|
||||
return
|
||||
}
|
||||
const event = { ...request, session_id: sessionId }
|
||||
if (request.request.subtype === 'can_use_tool') {
|
||||
if ((request as { request?: { subtype?: string } }).request?.subtype === 'can_use_tool') {
|
||||
transport.reportState('requires_action')
|
||||
}
|
||||
void transport.write(event)
|
||||
|
||||
@@ -1295,7 +1295,7 @@ export async function initBridgeCore(
|
||||
if (previouslyFlushedUUIDs) {
|
||||
for (const sdkMsg of sdkMessages) {
|
||||
if (sdkMsg.uuid) {
|
||||
previouslyFlushedUUIDs.add(sdkMsg.uuid)
|
||||
previouslyFlushedUUIDs.add(sdkMsg.uuid as string)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1760,7 +1760,7 @@ export async function initBridgeCore(
|
||||
// No initialMessageUUIDs filter — daemon has no initial messages.
|
||||
// No flushGate — daemon never starts it (no initial flush).
|
||||
const filtered = messages.filter(
|
||||
m => !m.uuid || !recentPostedUUIDs.has(m.uuid),
|
||||
m => !m.uuid || !recentPostedUUIDs.has(m.uuid as string),
|
||||
)
|
||||
if (filtered.length === 0) return
|
||||
if (!transport) {
|
||||
@@ -1771,7 +1771,7 @@ export async function initBridgeCore(
|
||||
return
|
||||
}
|
||||
for (const msg of filtered) {
|
||||
if (msg.uuid) recentPostedUUIDs.add(msg.uuid)
|
||||
if (msg.uuid) recentPostedUUIDs.add(msg.uuid as string)
|
||||
}
|
||||
const events = filtered.map(m => ({ ...m, session_id: currentSessionId }))
|
||||
void transport.writeBatch(events)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export const sanitizeInboundWebhookContent: any = (() => {}) as any;
|
||||
export const sanitizeInboundWebhookContent: (content: string) => string = (content) => content;
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export const psHandler: any = (() => {}) as any;
|
||||
export const logsHandler: any = (() => {}) as any;
|
||||
export const attachHandler: any = (() => {}) as any;
|
||||
export const killHandler: any = (() => {}) as any;
|
||||
export const handleBgFlag: any = (() => {}) as any;
|
||||
export const psHandler: (args: string[]) => Promise<void> = (async () => {}) as (args: string[]) => Promise<void>;
|
||||
export const logsHandler: (sessionId: string | undefined) => Promise<void> = (async () => {}) as (sessionId: string | undefined) => Promise<void>;
|
||||
export const attachHandler: (sessionId: string | undefined) => Promise<void> = (async () => {}) as (sessionId: string | undefined) => Promise<void>;
|
||||
export const killHandler: (sessionId: string | undefined) => Promise<void> = (async () => {}) as (sessionId: string | undefined) => Promise<void>;
|
||||
export const handleBgFlag: (args: string[]) => Promise<void> = (async () => {}) as (args: string[]) => Promise<void>;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
import type { Command } from '@commander-js/extra-typings';
|
||||
|
||||
export {};
|
||||
export const logHandler: any = (() => {}) as any;
|
||||
export const errorHandler: any = (() => {}) as any;
|
||||
export const exportHandler: any = (() => {}) as any;
|
||||
export const taskCreateHandler: any = (() => {}) as any;
|
||||
export const taskListHandler: any = (() => {}) as any;
|
||||
export const taskGetHandler: any = (() => {}) as any;
|
||||
export const taskUpdateHandler: any = (() => {}) as any;
|
||||
export const taskDirHandler: any = (() => {}) as any;
|
||||
export const completionHandler: any = (() => {}) as any;
|
||||
export const logHandler: (logId: string | number | undefined) => Promise<void> = (async () => {}) as (logId: string | number | undefined) => Promise<void>;
|
||||
export const errorHandler: (num: number | undefined) => Promise<void> = (async () => {}) as (num: number | undefined) => Promise<void>;
|
||||
export const exportHandler: (source: string, outputFile: string) => Promise<void> = (async () => {}) as (source: string, outputFile: string) => Promise<void>;
|
||||
export const taskCreateHandler: (subject: string, opts: { description?: string; list?: string }) => Promise<void> = (async () => {}) as (subject: string, opts: { description?: string; list?: string }) => Promise<void>;
|
||||
export const taskListHandler: (opts: { list?: string; pending?: boolean; json?: boolean }) => Promise<void> = (async () => {}) as (opts: { list?: string; pending?: boolean; json?: boolean }) => Promise<void>;
|
||||
export const taskGetHandler: (id: string, opts: { list?: string }) => Promise<void> = (async () => {}) as (id: string, opts: { list?: string }) => Promise<void>;
|
||||
export const taskUpdateHandler: (id: string, opts: { list?: string; status?: string; subject?: string; description?: string; owner?: string; clearOwner?: boolean }) => Promise<void> = (async () => {}) as (id: string, opts: { list?: string; status?: string; subject?: string; description?: string; owner?: string; clearOwner?: boolean }) => Promise<void>;
|
||||
export const taskDirHandler: (opts: { list?: string }) => Promise<void> = (async () => {}) as (opts: { list?: string }) => Promise<void>;
|
||||
export const completionHandler: (shell: string, opts: { output?: string }, program: Command) => Promise<void> = (async () => {}) as (shell: string, opts: { output?: string }, program: Command) => Promise<void>;
|
||||
|
||||
@@ -159,7 +159,7 @@ export async function authLogin({
|
||||
|
||||
const orgResult = await validateForceLoginOrg()
|
||||
if (!orgResult.valid) {
|
||||
process.stderr.write(orgResult.message + '\n')
|
||||
process.stderr.write((orgResult as { valid: false; message: string }).message + '\n')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ export async function authLogin({
|
||||
|
||||
const orgResult = await validateForceLoginOrg()
|
||||
if (!orgResult.valid) {
|
||||
process.stderr.write(orgResult.message + '\n')
|
||||
process.stderr.write((orgResult as { valid: false; message: string }).message + '\n')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
export {};
|
||||
export const templatesMain: any = (() => {}) as any;
|
||||
export const templatesMain: (args: string[]) => Promise<void> = () => Promise.resolve();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -935,9 +935,9 @@ export async function runHeadless(
|
||||
switch (lastMessage.subtype) {
|
||||
case 'success':
|
||||
writeToStdout(
|
||||
lastMessage.result.endsWith('\n')
|
||||
? lastMessage.result
|
||||
: lastMessage.result + '\n',
|
||||
(lastMessage.result as string).endsWith('\n')
|
||||
? (lastMessage.result as string)
|
||||
: (lastMessage.result as string) + '\n',
|
||||
)
|
||||
break
|
||||
case 'error_during_execution':
|
||||
@@ -1203,6 +1203,7 @@ function runHeadlessStreaming(
|
||||
const hasFastMode = isFastModeSupportedByModel(option.value)
|
||||
const hasAutoMode = modelSupportsAutoMode(resolvedModel)
|
||||
return {
|
||||
name: modelId,
|
||||
value: modelId,
|
||||
displayName: option.label,
|
||||
description: option.description,
|
||||
@@ -1235,13 +1236,14 @@ function runHeadlessStreaming(
|
||||
) {
|
||||
output.enqueue({
|
||||
type: 'user',
|
||||
content: crumb.message.content,
|
||||
message: crumb.message,
|
||||
session_id: getSessionId(),
|
||||
parent_tool_use_id: null,
|
||||
uuid: crumb.uuid,
|
||||
timestamp: crumb.timestamp,
|
||||
isReplay: true,
|
||||
} satisfies SDKUserMessageReplay)
|
||||
} as SDKUserMessageReplay)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1646,10 +1648,11 @@ function runHeadlessStreaming(
|
||||
connection.config.type === 'stdio' ||
|
||||
connection.config.type === undefined
|
||||
) {
|
||||
const stdioConfig = connection.config as { command: string; args: string[] }
|
||||
config = {
|
||||
type: 'stdio' as const,
|
||||
command: connection.config.command,
|
||||
args: connection.config.args,
|
||||
command: stdioConfig.command,
|
||||
args: stdioConfig.args,
|
||||
}
|
||||
}
|
||||
const serverTools =
|
||||
@@ -1688,7 +1691,7 @@ function runHeadlessStreaming(
|
||||
}
|
||||
return {
|
||||
name: connection.name,
|
||||
status: connection.type,
|
||||
status: connection.type as McpServerStatus['status'],
|
||||
serverInfo:
|
||||
connection.type === 'connected' ? connection.serverInfo : undefined,
|
||||
error: connection.type === 'failed' ? connection.error : undefined,
|
||||
@@ -1697,7 +1700,7 @@ function runHeadlessStreaming(
|
||||
tools: serverTools,
|
||||
capabilities,
|
||||
}
|
||||
})
|
||||
}) as McpServerStatus[]
|
||||
}
|
||||
|
||||
// NOTE: Nested function required - needs closure access to applyMcpServerChanges and updateSdkMcp
|
||||
@@ -1802,12 +1805,12 @@ function runHeadlessStreaming(
|
||||
type === 'http' ||
|
||||
type === 'sdk'
|
||||
) {
|
||||
supportedConfigs[name] = config
|
||||
supportedConfigs[name] = config as McpServerConfigForProcessTransport
|
||||
}
|
||||
}
|
||||
for (const [name, config] of Object.entries(sdkMcpConfigs)) {
|
||||
if (config.type === 'sdk' && !(name in supportedConfigs)) {
|
||||
supportedConfigs[name] = config
|
||||
supportedConfigs[name] = config as unknown as McpServerConfigForProcessTransport
|
||||
}
|
||||
}
|
||||
const { response, sdkServersChanged } =
|
||||
@@ -1971,12 +1974,13 @@ function runHeadlessStreaming(
|
||||
if (c.uuid && c.uuid !== command.uuid) {
|
||||
output.enqueue({
|
||||
type: 'user',
|
||||
content: c.value,
|
||||
message: { role: 'user', content: c.value },
|
||||
session_id: getSessionId(),
|
||||
parent_tool_use_id: null,
|
||||
uuid: c.uuid,
|
||||
uuid: c.uuid as string,
|
||||
isReplay: true,
|
||||
} satisfies SDKUserMessageReplay)
|
||||
} as SDKUserMessageReplay)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2255,14 +2259,14 @@ function runHeadlessStreaming(
|
||||
|
||||
if (feature('FILE_PERSISTENCE') && turnStartTime !== undefined) {
|
||||
void executeFilePersistence(
|
||||
turnStartTime,
|
||||
{ turnStartTime } as import('src/utils/filePersistence/types.js').TurnStartTime,
|
||||
abortController.signal,
|
||||
result => {
|
||||
output.enqueue({
|
||||
type: 'system' as const,
|
||||
subtype: 'files_persisted' as const,
|
||||
files: result.files,
|
||||
failed: result.failed,
|
||||
files: (result as any).persistedFiles,
|
||||
failed: (result as any).failedFiles,
|
||||
processed_at: new Date().toISOString(),
|
||||
uuid: randomUUID(),
|
||||
session_id: getSessionId(),
|
||||
@@ -3005,7 +3009,7 @@ function runHeadlessStreaming(
|
||||
} else {
|
||||
sendControlResponseError(
|
||||
message,
|
||||
result.error ?? 'Unexpected error',
|
||||
(result.error as string) ?? 'Unexpected error',
|
||||
)
|
||||
}
|
||||
} else if (message.request.subtype === 'cancel_async_message') {
|
||||
@@ -4077,13 +4081,14 @@ function runHeadlessStreaming(
|
||||
)
|
||||
output.enqueue({
|
||||
type: 'user',
|
||||
content: message.message?.content ?? '',
|
||||
message: message.message,
|
||||
session_id: sessionId,
|
||||
parent_tool_use_id: null,
|
||||
uuid: message.uuid,
|
||||
timestamp: message.timestamp,
|
||||
isReplay: true,
|
||||
} as SDKUserMessageReplay)
|
||||
} as unknown as SDKUserMessageReplay)
|
||||
}
|
||||
// Historical dup = transcript already has this turn's output, so it
|
||||
// ran but its lifecycle was never closed (interrupted before ack).
|
||||
@@ -4434,7 +4439,7 @@ async function handleInitializeRequest(
|
||||
const accountInfo = getAccountInformation()
|
||||
if (request.hooks) {
|
||||
const hooks: Partial<Record<HookEvent, HookCallbackMatcher[]>> = {}
|
||||
for (const [event, matchers] of Object.entries(request.hooks)) {
|
||||
for (const [event, matchers] of Object.entries(request.hooks) as [string, Array<{ hookCallbackIds: string[]; timeout?: number; matcher?: string }>][]) {
|
||||
hooks[event as HookEvent] = matchers.map(matcher => {
|
||||
const callbacks = matcher.hookCallbackIds.map(callbackId => {
|
||||
return structuredIO.createHookCallback(callbackId, matcher.timeout)
|
||||
@@ -4524,12 +4529,13 @@ async function handleRewindFiles(
|
||||
dryRun: boolean,
|
||||
): Promise<RewindFilesResult> {
|
||||
if (!fileHistoryEnabled()) {
|
||||
return { canRewind: false, error: 'File rewinding is not enabled.' }
|
||||
return { canRewind: false, error: 'File rewinding is not enabled.', filesChanged: [] }
|
||||
}
|
||||
if (!fileHistoryCanRestore(appState.fileHistory, userMessageId)) {
|
||||
return {
|
||||
canRewind: false,
|
||||
error: 'No file checkpoint found for this message.',
|
||||
filesChanged: [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4559,10 +4565,11 @@ async function handleRewindFiles(
|
||||
return {
|
||||
canRewind: false,
|
||||
error: `Failed to rewind: ${errorMessage(error)}`,
|
||||
filesChanged: [],
|
||||
}
|
||||
}
|
||||
|
||||
return { canRewind: true }
|
||||
return { canRewind: true, filesChanged: [] }
|
||||
}
|
||||
|
||||
function handleSetPermissionMode(
|
||||
@@ -4751,7 +4758,7 @@ function handleChannelEnable(
|
||||
value: wrapChannelMessage(serverName, content, meta),
|
||||
priority: 'next',
|
||||
isMeta: true,
|
||||
origin: { kind: 'channel', server: serverName },
|
||||
origin: { kind: 'channel', server: serverName } as unknown as string,
|
||||
skipSlashCommands: true,
|
||||
})
|
||||
},
|
||||
@@ -4827,7 +4834,7 @@ function reregisterChannelHandlerAfterReconnect(
|
||||
value: wrapChannelMessage(connection.name, content, meta),
|
||||
priority: 'next',
|
||||
isMeta: true,
|
||||
origin: { kind: 'channel', server: connection.name },
|
||||
origin: { kind: 'channel', server: connection.name } as unknown as string,
|
||||
skipSlashCommands: true,
|
||||
})
|
||||
},
|
||||
@@ -5210,6 +5217,8 @@ function getStructuredIO(
|
||||
inputStream = fromArray([
|
||||
jsonStringify({
|
||||
type: 'user',
|
||||
content: inputPrompt,
|
||||
uuid: '',
|
||||
session_id: '',
|
||||
message: {
|
||||
role: 'user',
|
||||
@@ -5249,19 +5258,20 @@ export async function handleOrphanedPermissionResponse({
|
||||
onEnqueued?: () => void
|
||||
handledToolUseIds: Set<string>
|
||||
}): Promise<boolean> {
|
||||
const responseInner = message.response as { subtype?: string; response?: Record<string, unknown>; request_id?: string } | undefined
|
||||
if (
|
||||
message.response.subtype === 'success' &&
|
||||
message.response.response?.toolUseID &&
|
||||
typeof message.response.response.toolUseID === 'string'
|
||||
responseInner?.subtype === 'success' &&
|
||||
responseInner.response?.toolUseID &&
|
||||
typeof responseInner.response.toolUseID === 'string'
|
||||
) {
|
||||
const permissionResult = message.response.response as PermissionResult
|
||||
const { toolUseID } = permissionResult
|
||||
const permissionResult = responseInner.response as PermissionResult & { toolUseID?: string }
|
||||
const toolUseID = permissionResult.toolUseID
|
||||
if (!toolUseID) {
|
||||
return false
|
||||
}
|
||||
|
||||
logForDebugging(
|
||||
`handleOrphanedPermissionResponse: received orphaned control_response for toolUseID=${toolUseID} request_id=${message.response.request_id}`,
|
||||
`handleOrphanedPermissionResponse: received orphaned control_response for toolUseID=${toolUseID} request_id=${responseInner.request_id}`,
|
||||
)
|
||||
|
||||
// Prevent re-processing the same orphaned tool_use. Without this guard,
|
||||
@@ -5373,8 +5383,8 @@ export async function handleMcpSetServers(
|
||||
const processServers: Record<string, McpServerConfigForProcessTransport> = {}
|
||||
|
||||
for (const [name, config] of Object.entries(allowedServers)) {
|
||||
if (config.type === 'sdk') {
|
||||
sdkServers[name] = config
|
||||
if ((config.type as string) === 'sdk') {
|
||||
sdkServers[name] = config as unknown as McpSdkServerConfig
|
||||
} else {
|
||||
processServers[name] = config
|
||||
}
|
||||
@@ -5515,7 +5525,7 @@ export async function reconcileMcpServers(
|
||||
|
||||
// SDK servers are managed by the SDK process, not the CLI.
|
||||
// Just track them without trying to connect.
|
||||
if (config.type === 'sdk') {
|
||||
if ((config.type as string) === 'sdk') {
|
||||
added.push(name)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Auto-generated stub
|
||||
export {};
|
||||
export async function rollback(target?: string, options?: { list?: boolean; dryRun?: boolean; safe?: boolean }): Promise<void> {}
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { AssistantMessage } from 'src//types/message.js'
|
||||
import type {
|
||||
HookInput,
|
||||
HookJSONOutput,
|
||||
PermissionUpdate,
|
||||
PermissionUpdate as SDKPermissionUpdate,
|
||||
SDKMessage,
|
||||
SDKUserMessage,
|
||||
} from 'src/entrypoints/agentSdkTypes.js'
|
||||
@@ -19,6 +19,7 @@ import type {
|
||||
StdinMessage,
|
||||
StdoutMessage,
|
||||
} from 'src/entrypoints/sdk/controlTypes.js'
|
||||
import type { PermissionUpdate as InternalPermissionUpdate } from 'src/types/permissions.js'
|
||||
import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js'
|
||||
import type { Tool, ToolUseContext } from 'src/Tool.js'
|
||||
import { type HookCallback, hookJSONOutputSchema } from 'src/types/hooks.js'
|
||||
@@ -174,8 +175,9 @@ export class StructuredIO {
|
||||
* messages for the same tool are ignored by the orphan handler.
|
||||
*/
|
||||
private trackResolvedToolUseId(request: SDKControlRequest): void {
|
||||
if (request.request.subtype === 'can_use_tool') {
|
||||
this.resolvedToolUseIds.add(request.request.tool_use_id)
|
||||
const inner = request.request as { subtype?: string; tool_use_id?: string }
|
||||
if (inner.subtype === 'can_use_tool') {
|
||||
this.resolvedToolUseIds.add(inner.tool_use_id as string)
|
||||
if (this.resolvedToolUseIds.size > MAX_RESOLVED_TOOL_USE_IDS) {
|
||||
// Evict the oldest entry (Sets iterate in insertion order)
|
||||
const first = this.resolvedToolUseIds.values().next().value
|
||||
@@ -205,6 +207,8 @@ export class StructuredIO {
|
||||
this.prependedLines.push(
|
||||
jsonStringify({
|
||||
type: 'user',
|
||||
content,
|
||||
uuid: '',
|
||||
session_id: '',
|
||||
message: { role: 'user', content },
|
||||
parent_tool_use_id: null,
|
||||
@@ -263,7 +267,7 @@ export class StructuredIO {
|
||||
getPendingPermissionRequests() {
|
||||
return Array.from(this.pendingRequests.values())
|
||||
.map(entry => entry.request)
|
||||
.filter(pr => pr.request.subtype === 'can_use_tool')
|
||||
.filter(pr => (pr.request as { subtype?: string }).subtype === 'can_use_tool')
|
||||
}
|
||||
|
||||
setUnexpectedResponseCallback(
|
||||
@@ -281,21 +285,22 @@ export class StructuredIO {
|
||||
* callback is aborted via the signal — otherwise the callback hangs.
|
||||
*/
|
||||
injectControlResponse(response: SDKControlResponse): void {
|
||||
const requestId = response.response?.request_id
|
||||
const responseInner = response.response as { request_id?: string; subtype?: string; error?: string; response?: unknown } | undefined
|
||||
const requestId = responseInner?.request_id
|
||||
if (!requestId) return
|
||||
const request = this.pendingRequests.get(requestId)
|
||||
const request = this.pendingRequests.get(requestId as string)
|
||||
if (!request) return
|
||||
this.trackResolvedToolUseId(request.request)
|
||||
this.pendingRequests.delete(requestId)
|
||||
this.pendingRequests.delete(requestId as string)
|
||||
// Cancel the SDK consumer's canUseTool callback — the bridge won.
|
||||
void this.write({
|
||||
type: 'control_cancel_request',
|
||||
request_id: requestId,
|
||||
})
|
||||
if (response.response.subtype === 'error') {
|
||||
request.reject(new Error(response.response.error))
|
||||
if (responseInner.subtype === 'error') {
|
||||
request.reject(new Error(responseInner.error as string))
|
||||
} else {
|
||||
const result = response.response.response
|
||||
const result = responseInner.response
|
||||
if (request.schema) {
|
||||
try {
|
||||
request.resolve(request.schema.parse(result))
|
||||
@@ -350,8 +355,9 @@ export class StructuredIO {
|
||||
// Used by bridge session runner for auth token refresh
|
||||
// (CLAUDE_CODE_SESSION_ACCESS_TOKEN) which must be readable
|
||||
// by the REPL process itself, not just child Bash commands.
|
||||
const keys = Object.keys(message.variables)
|
||||
for (const [key, value] of Object.entries(message.variables)) {
|
||||
const variables = message.variables as Record<string, string>
|
||||
const keys = Object.keys(variables)
|
||||
for (const [key, value] of Object.entries(variables)) {
|
||||
process.env[key] = value
|
||||
}
|
||||
logForDebugging(
|
||||
@@ -402,7 +408,7 @@ export class StructuredIO {
|
||||
// Notify the bridge when the SDK consumer resolves a can_use_tool
|
||||
// request, so it can cancel the stale permission prompt on claude.ai.
|
||||
if (
|
||||
request.request.request.subtype === 'can_use_tool' &&
|
||||
(request.request.request as { subtype?: string }).subtype === 'can_use_tool' &&
|
||||
this.onControlRequestResolved
|
||||
) {
|
||||
this.onControlRequestResolved(message.response.request_id)
|
||||
@@ -484,7 +490,7 @@ export class StructuredIO {
|
||||
throw new Error('Request aborted')
|
||||
}
|
||||
this.outbound.enqueue(message)
|
||||
if (request.subtype === 'can_use_tool' && this.onControlRequestSent) {
|
||||
if ((request as { subtype?: string }).subtype === 'can_use_tool' && this.onControlRequestSent) {
|
||||
this.onControlRequestSent(message)
|
||||
}
|
||||
const aborted = () => {
|
||||
@@ -789,7 +795,7 @@ async function executePermissionRequestHooksForSDK(
|
||||
toolUseID: string,
|
||||
input: Record<string, unknown>,
|
||||
toolUseContext: ToolUseContext,
|
||||
suggestions: PermissionUpdate[] | undefined,
|
||||
suggestions: InternalPermissionUpdate[] | undefined,
|
||||
): Promise<PermissionDecision | undefined> {
|
||||
const appState = toolUseContext.getAppState()
|
||||
const permissionMode = appState.toolPermissionContext.mode
|
||||
@@ -801,7 +807,7 @@ async function executePermissionRequestHooksForSDK(
|
||||
input,
|
||||
toolUseContext,
|
||||
permissionMode,
|
||||
suggestions,
|
||||
suggestions as unknown as SDKPermissionUpdate[] | undefined,
|
||||
toolUseContext.abortController.signal,
|
||||
)
|
||||
|
||||
@@ -816,7 +822,7 @@ async function executePermissionRequestHooksForSDK(
|
||||
const finalInput = decision.updatedInput || input
|
||||
|
||||
// Apply permission updates if provided by hook ("always allow")
|
||||
const permissionUpdates = decision.updatedPermissions ?? []
|
||||
const permissionUpdates = (decision.updatedPermissions ?? []) as unknown as InternalPermissionUpdate[]
|
||||
if (permissionUpdates.length > 0) {
|
||||
persistPermissionUpdates(permissionUpdates)
|
||||
const currentAppState = toolUseContext.getAppState()
|
||||
|
||||
@@ -375,7 +375,7 @@ export class CCRClient {
|
||||
if (!result.ok) {
|
||||
throw new RetryableError(
|
||||
'client event POST failed',
|
||||
result.retryAfterMs,
|
||||
(result as any).retryAfterMs,
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -398,7 +398,7 @@ export class CCRClient {
|
||||
if (!result.ok) {
|
||||
throw new RetryableError(
|
||||
'internal event POST failed',
|
||||
result.retryAfterMs,
|
||||
(result as any).retryAfterMs,
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -427,7 +427,7 @@ export class CCRClient {
|
||||
'delivery batch',
|
||||
)
|
||||
if (!result.ok) {
|
||||
throw new RetryableError('delivery POST failed', result.retryAfterMs)
|
||||
throw new RetryableError('delivery POST failed', (result as any).retryAfterMs)
|
||||
}
|
||||
},
|
||||
baseDelayMs: 500,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Auto-generated stub
|
||||
export {};
|
||||
export async function up(): Promise<void> {}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -9,4 +9,3 @@ export async function call(onDone: LocalJSXCommandOnDone, context: ToolUseContex
|
||||
const tools = getTools(permissionContext);
|
||||
return <AgentsMenu tools={tools} onExit={onDone} />;
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkFnZW50c01lbnUiLCJUb29sVXNlQ29udGV4dCIsImdldFRvb2xzIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiY2FsbCIsIm9uRG9uZSIsImNvbnRleHQiLCJQcm9taXNlIiwiUmVhY3ROb2RlIiwiYXBwU3RhdGUiLCJnZXRBcHBTdGF0ZSIsInBlcm1pc3Npb25Db250ZXh0IiwidG9vbFBlcm1pc3Npb25Db250ZXh0IiwidG9vbHMiXSwic291cmNlcyI6WyJhZ2VudHMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQWdlbnRzTWVudSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvYWdlbnRzL0FnZW50c01lbnUuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xVc2VDb250ZXh0IH0gZnJvbSAnLi4vLi4vVG9vbC5qcydcbmltcG9ydCB7IGdldFRvb2xzIH0gZnJvbSAnLi4vLi4vdG9vbHMuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogVG9vbFVzZUNvbnRleHQsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICBjb25zdCBhcHBTdGF0ZSA9IGNvbnRleHQuZ2V0QXBwU3RhdGUoKVxuICBjb25zdCBwZXJtaXNzaW9uQ29udGV4dCA9IGFwcFN0YXRlLnRvb2xQZXJtaXNzaW9uQ29udGV4dFxuICBjb25zdCB0b29scyA9IGdldFRvb2xzKHBlcm1pc3Npb25Db250ZXh0KVxuXG4gIHJldHVybiA8QWdlbnRzTWVudSB0b29scz17dG9vbHN9IG9uRXhpdD17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFVBQVUsUUFBUSx1Q0FBdUM7QUFDbEUsY0FBY0MsY0FBYyxRQUFRLGVBQWU7QUFDbkQsU0FBU0MsUUFBUSxRQUFRLGdCQUFnQjtBQUN6QyxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFFbkUsT0FBTyxlQUFlQyxJQUFJQSxDQUN4QkMsTUFBTSxFQUFFRixxQkFBcUIsRUFDN0JHLE9BQU8sRUFBRUwsY0FBYyxDQUN4QixFQUFFTSxPQUFPLENBQUNSLEtBQUssQ0FBQ1MsU0FBUyxDQUFDLENBQUM7RUFDMUIsTUFBTUMsUUFBUSxHQUFHSCxPQUFPLENBQUNJLFdBQVcsQ0FBQyxDQUFDO0VBQ3RDLE1BQU1DLGlCQUFpQixHQUFHRixRQUFRLENBQUNHLHFCQUFxQjtFQUN4RCxNQUFNQyxLQUFLLEdBQUdYLFFBQVEsQ0FBQ1MsaUJBQWlCLENBQUM7RUFFekMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQ0UsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUNSLE1BQU0sQ0FBQyxHQUFHO0FBQ3JEIiwiaWdub3JlTGlzdCI6W119
|
||||
@@ -1,4 +1,11 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
import type React from 'react';
|
||||
|
||||
export {};
|
||||
export const NewInstallWizard: any = (() => {}) as any;
|
||||
export const computeDefaultInstallDir: any = (() => {}) as any;
|
||||
export const NewInstallWizard: React.FC<{
|
||||
defaultDir: string;
|
||||
onInstalled: (dir: string) => void;
|
||||
onCancel: () => void;
|
||||
onError: (message: string) => void;
|
||||
}> = (() => null);
|
||||
export const computeDefaultInstallDir: () => Promise<string> = (() => Promise.resolve(''));
|
||||
|
||||
@@ -38,7 +38,7 @@ type TranscriptEntry = TranscriptMessage & {
|
||||
export function deriveFirstPrompt(
|
||||
firstUserMessage: Extract<SerializedMessage, { type: 'user' }> | undefined,
|
||||
): string {
|
||||
const content = firstUserMessage?.message?.content
|
||||
const content = (firstUserMessage as any)?.message?.content
|
||||
if (!content) return 'Branched conversation'
|
||||
const raw =
|
||||
typeof content === 'string'
|
||||
@@ -240,7 +240,7 @@ export async function call(
|
||||
// Build LogOption for resume
|
||||
const now = new Date()
|
||||
const firstPrompt = deriveFirstPrompt(
|
||||
serializedMessages.find(m => m.type === 'user'),
|
||||
serializedMessages.find(m => m.type === 'user') as Extract<SerializedMessage, { type: 'user' }> | undefined,
|
||||
)
|
||||
|
||||
// Save custom title - use provided title or firstPrompt as default
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
|
||||
// Auto-generated stub — replace with real implementation
|
||||
const _default: any = (() => {}) as any;
|
||||
const _default: Record<string, unknown> = {};
|
||||
export default _default;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -207,14 +207,14 @@ async function compactViaReactive(
|
||||
// they can merge its userDisplayMessage with PostCompact's here. This
|
||||
// caller additionally runs it concurrently with getCacheSharingParams.
|
||||
const combinedMessage =
|
||||
[hookResult.userDisplayMessage, outcome.result.userDisplayMessage]
|
||||
[hookResult.userDisplayMessage, outcome.result!.userDisplayMessage]
|
||||
.filter(Boolean)
|
||||
.join('\n') || undefined
|
||||
|
||||
return {
|
||||
type: 'compact',
|
||||
compactionResult: {
|
||||
...outcome.result,
|
||||
...outcome.result!,
|
||||
userDisplayMessage: combinedMessage,
|
||||
},
|
||||
displayText: buildDisplayText(context, combinedMessage),
|
||||
|
||||
@@ -4,4 +4,3 @@ import type { LocalJSXCommandCall } from '../../types/command.js';
|
||||
export const call: LocalJSXCommandCall = async (onDone, context) => {
|
||||
return <Settings onClose={onDone} context={context} defaultTab="Config" />;
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlNldHRpbmdzIiwiTG9jYWxKU1hDb21tYW5kQ2FsbCIsImNhbGwiLCJvbkRvbmUiLCJjb250ZXh0Il0sInNvdXJjZXMiOlsiY29uZmlnLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFNldHRpbmdzIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9TZXR0aW5ncy9TZXR0aW5ncy5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ2FsbCB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBjb25zdCBjYWxsOiBMb2NhbEpTWENvbW1hbmRDYWxsID0gYXN5bmMgKG9uRG9uZSwgY29udGV4dCkgPT4ge1xuICByZXR1cm4gPFNldHRpbmdzIG9uQ2xvc2U9e29uRG9uZX0gY29udGV4dD17Y29udGV4dH0gZGVmYXVsdFRhYj1cIkNvbmZpZ1wiIC8+XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsUUFBUSxRQUFRLHVDQUF1QztBQUNoRSxjQUFjQyxtQkFBbUIsUUFBUSx3QkFBd0I7QUFFakUsT0FBTyxNQUFNQyxJQUFJLEVBQUVELG1CQUFtQixHQUFHLE1BQUFDLENBQU9DLE1BQU0sRUFBRUMsT0FBTyxLQUFLO0VBQ2xFLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUNELE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDQyxPQUFPLENBQUMsQ0FBQyxVQUFVLENBQUMsUUFBUSxHQUFHO0FBQzVFLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0=
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,4 +6,3 @@ export async function call(onDone: (result?: string, options?: {
|
||||
}) => void): Promise<React.ReactNode> {
|
||||
return <DesktopHandoff onDone={onDone} />;
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiRGVza3RvcEhhbmRvZmYiLCJjYWxsIiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJQcm9taXNlIiwiUmVhY3ROb2RlIl0sInNvdXJjZXMiOlsiZGVza3RvcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgRGVza3RvcEhhbmRvZmYgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0Rlc2t0b3BIYW5kb2ZmLmpzJ1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWQsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICByZXR1cm4gPERlc2t0b3BIYW5kb2ZmIG9uRG9uZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixjQUFjQyxvQkFBb0IsUUFBUSxtQkFBbUI7QUFDN0QsU0FBU0MsY0FBYyxRQUFRLG9DQUFvQztBQUVuRSxPQUFPLGVBQWVDLElBQUlBLENBQ3hCQyxNQUFNLEVBQUUsQ0FDTkMsTUFBZSxDQUFSLEVBQUUsTUFBTSxFQUNmQyxPQUE0QyxDQUFwQyxFQUFFO0VBQUVDLE9BQU8sQ0FBQyxFQUFFTixvQkFBb0I7QUFBQyxDQUFDLEVBQzVDLEdBQUcsSUFBSSxDQUNWLEVBQUVPLE9BQU8sQ0FBQ1IsS0FBSyxDQUFDUyxTQUFTLENBQUMsQ0FBQztFQUMxQixPQUFPLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDTCxNQUFNLENBQUMsR0FBRztBQUMzQyIsImlnbm9yZUxpc3QiOltdfQ==
|
||||
@@ -6,4 +6,3 @@ export const call: LocalJSXCommandCall = async (onDone, context) => {
|
||||
} = await import('../../components/diff/DiffDialog.js');
|
||||
return <DiffDialog messages={context.messages} onDone={onDone} />;
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwiY29udGV4dCIsIkRpZmZEaWFsb2ciLCJtZXNzYWdlcyJdLCJzb3VyY2VzIjpbImRpZmYudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0KSA9PiB7XG4gIGNvbnN0IHsgRGlmZkRpYWxvZyB9ID0gYXdhaXQgaW1wb3J0KCcuLi8uLi9jb21wb25lbnRzL2RpZmYvRGlmZkRpYWxvZy5qcycpXG4gIHJldHVybiA8RGlmZkRpYWxvZyBtZXNzYWdlcz17Y29udGV4dC5tZXNzYWdlc30gb25Eb25lPXtvbkRvbmV9IC8+XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFBQyxDQUFPQyxNQUFNLEVBQUVDLE9BQU8sS0FBSztFQUNsRSxNQUFNO0lBQUVDO0VBQVcsQ0FBQyxHQUFHLE1BQU0sTUFBTSxDQUFDLHFDQUFxQyxDQUFDO0VBQzFFLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUNELE9BQU8sQ0FBQ0UsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUNILE1BQU0sQ0FBQyxHQUFHO0FBQ25FLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0=
|
||||
@@ -4,4 +4,3 @@ import type { LocalJSXCommandCall } from '../../types/command.js';
|
||||
export const call: LocalJSXCommandCall = (onDone, _context, _args) => {
|
||||
return Promise.resolve(<Doctor onDone={onDone} />);
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkRvY3RvciIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwiX2NvbnRleHQiLCJfYXJncyIsIlByb21pc2UiLCJyZXNvbHZlIl0sInNvdXJjZXMiOlsiZG9jdG9yLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBEb2N0b3IgfSBmcm9tICcuLi8uLi9zY3JlZW5zL0RvY3Rvci5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ2FsbCB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBjb25zdCBjYWxsOiBMb2NhbEpTWENvbW1hbmRDYWxsID0gKG9uRG9uZSwgX2NvbnRleHQsIF9hcmdzKSA9PiB7XG4gIHJldHVybiBQcm9taXNlLnJlc29sdmUoPERvY3RvciBvbkRvbmU9e29uRG9uZX0gLz4pXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLE1BQU0sUUFBUSx5QkFBeUI7QUFDaEQsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBR0MsQ0FBQ0MsTUFBTSxFQUFFQyxRQUFRLEVBQUVDLEtBQUssS0FBSztFQUNwRSxPQUFPQyxPQUFPLENBQUNDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQ0osTUFBTSxDQUFDLEdBQUcsQ0FBQztBQUNwRCxDQUFDIiwiaWdub3JlTGlzdCI6W119
|
||||
File diff suppressed because one or more lines are too long
@@ -30,4 +30,3 @@ export async function call(onDone: LocalJSXCommandOnDone): Promise<React.ReactNo
|
||||
await gracefulShutdown(0, 'prompt_input_exit');
|
||||
return null;
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwic3Bhd25TeW5jIiwic2FtcGxlIiwiUmVhY3QiLCJFeGl0RmxvdyIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImlzQmdTZXNzaW9uIiwiZ3JhY2VmdWxTaHV0ZG93biIsImdldEN1cnJlbnRXb3JrdHJlZVNlc3Npb24iLCJHT09EQllFX01FU1NBR0VTIiwiZ2V0UmFuZG9tR29vZGJ5ZU1lc3NhZ2UiLCJjYWxsIiwib25Eb25lIiwiUHJvbWlzZSIsIlJlYWN0Tm9kZSIsInN0ZGlvIiwic2hvd1dvcmt0cmVlIl0sInNvdXJjZXMiOlsiZXhpdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgeyBzcGF3blN5bmMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJ1xuaW1wb3J0IHNhbXBsZSBmcm9tICdsb2Rhc2gtZXMvc2FtcGxlLmpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBFeGl0RmxvdyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvRXhpdEZsb3cuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgeyBpc0JnU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbmN1cnJlbnRTZXNzaW9ucy5qcydcbmltcG9ydCB7IGdyYWNlZnVsU2h1dGRvd24gfSBmcm9tICcuLi8uLi91dGlscy9ncmFjZWZ1bFNodXRkb3duLmpzJ1xuaW1wb3J0IHsgZ2V0Q3VycmVudFdvcmt0cmVlU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL3dvcmt0cmVlLmpzJ1xuXG5jb25zdCBHT09EQllFX01FU1NBR0VTID0gWydHb29kYnllIScsICdTZWUgeWEhJywgJ0J5ZSEnLCAnQ2F0Y2ggeW91IGxhdGVyISddXG5cbmZ1bmN0aW9uIGdldFJhbmRvbUdvb2RieWVNZXNzYWdlKCk6IHN0cmluZyB7XG4gIHJldHVybiBzYW1wbGUoR09PREJZRV9NRVNTQUdFUykgPz8gJ0dvb2RieWUhJ1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICAvLyBJbnNpZGUgYSBgY2xhdWRlIC0tYmdgIHRtdXggc2Vzc2lvbjogZGV0YWNoIGluc3RlYWQgb2Yga2lsbC4gVGhlIFJFUExcbiAgLy8ga2VlcHMgcnVubmluZzsgYGNsYXVkZSBhdHRhY2hgIGNhbiByZWNvbm5lY3QuIENvdmVycyAvZXhpdCwgL3F1aXQsXG4gIC8vIGN0cmwrYywgY3RybCtkIOKAlCBhbGwgZnVubmVsIHRocm91Z2ggaGVyZSB2aWEgUkVQTCdzIGhhbmRsZUV4aXQuXG4gIGlmIChmZWF0dXJlKCdCR19TRVNTSU9OUycpICYmIGlzQmdTZXNzaW9uKCkpIHtcbiAgICBvbkRvbmUoKVxuICAgIHNwYXduU3luYygndG11eCcsIFsnZGV0YWNoLWNsaWVudCddLCB7IHN0ZGlvOiAnaWdub3JlJyB9KVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBzaG93V29ya3RyZWUgPSBnZXRDdXJyZW50V29ya3RyZWVTZXNzaW9uKCkgIT09IG51bGxcblxuICBpZiAoc2hvd1dvcmt0cmVlKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxFeGl0Rmxvd1xuICAgICAgICBzaG93V29ya3RyZWU9e3Nob3dXb3JrdHJlZX1cbiAgICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBvbkRvbmUoKX1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgb25Eb25lKGdldFJhbmRvbUdvb2RieWVNZXNzYWdlKCkpXG4gIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMCwgJ3Byb21wdF9pbnB1dF9leGl0JylcbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsU0FBU0MsU0FBUyxRQUFRLGVBQWU7QUFDekMsT0FBT0MsTUFBTSxNQUFNLHFCQUFxQjtBQUN4QyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFFBQVEsUUFBUSw4QkFBOEI7QUFDdkQsY0FBY0MscUJBQXFCLFFBQVEsd0JBQXdCO0FBQ25FLFNBQVNDLFdBQVcsUUFBUSxtQ0FBbUM7QUFDL0QsU0FBU0MsZ0JBQWdCLFFBQVEsaUNBQWlDO0FBQ2xFLFNBQVNDLHlCQUF5QixRQUFRLHlCQUF5QjtBQUVuRSxNQUFNQyxnQkFBZ0IsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLGtCQUFrQixDQUFDO0FBRTVFLFNBQVNDLHVCQUF1QkEsQ0FBQSxDQUFFLEVBQUUsTUFBTSxDQUFDO0VBQ3pDLE9BQU9SLE1BQU0sQ0FBQ08sZ0JBQWdCLENBQUMsSUFBSSxVQUFVO0FBQy9DO0FBRUEsT0FBTyxlQUFlRSxJQUFJQSxDQUN4QkMsTUFBTSxFQUFFUCxxQkFBcUIsQ0FDOUIsRUFBRVEsT0FBTyxDQUFDVixLQUFLLENBQUNXLFNBQVMsQ0FBQyxDQUFDO0VBQzFCO0VBQ0E7RUFDQTtFQUNBLElBQUlkLE9BQU8sQ0FBQyxhQUFhLENBQUMsSUFBSU0sV0FBVyxDQUFDLENBQUMsRUFBRTtJQUMzQ00sTUFBTSxDQUFDLENBQUM7SUFDUlgsU0FBUyxDQUFDLE1BQU0sRUFBRSxDQUFDLGVBQWUsQ0FBQyxFQUFFO01BQUVjLEtBQUssRUFBRTtJQUFTLENBQUMsQ0FBQztJQUN6RCxPQUFPLElBQUk7RUFDYjtFQUVBLE1BQU1DLFlBQVksR0FBR1IseUJBQXlCLENBQUMsQ0FBQyxLQUFLLElBQUk7RUFFekQsSUFBSVEsWUFBWSxFQUFFO0lBQ2hCLE9BQ0UsQ0FBQyxRQUFRLENBQ1AsWUFBWSxDQUFDLENBQUNBLFlBQVksQ0FBQyxDQUMzQixNQUFNLENBQUMsQ0FBQ0osTUFBTSxDQUFDLENBQ2YsUUFBUSxDQUFDLENBQUMsTUFBTUEsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUN6QjtFQUVOO0VBRUFBLE1BQU0sQ0FBQ0YsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO0VBQ2pDLE1BQU1ILGdCQUFnQixDQUFDLENBQUMsRUFBRSxtQkFBbUIsQ0FBQztFQUM5QyxPQUFPLElBQUk7QUFDYiIsImlnbm9yZUxpc3QiOltdfQ==
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user