chenbhao Claude Big Pickle commited on
Commit
b7c4c8f
·
1 Parent(s): 642c567

chore: remove obsolete test and vendor source, add CLI SDK docs

Browse files

- Remove test_feishu_ws.ts (contained secrets)
- Remove vendor/audio-capture-src/index.ts (replaced by
packages/audio-capture-napi)
- Add docs/cli/ build system documentation
- Add src/cli/ SDK transport and handler modules

Co-Authored-By: Claude Big Pickle <noreply@anthropic.com>

docs/cli/build-system.md ADDED
@@ -0,0 +1,676 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 构建系统与功能标记架构
2
+
3
+ ## 1. 构建管道
4
+
5
+ 构建入口位于 `scripts/build.ts`,基于 Bun 的原生打包工具 `bun build`。整个构建流程由 `package.json` 中的 npm scripts 驱动。
6
+
7
+ ### 1.1 构建模式
8
+
9
+ | 命令 | 对应脚本参数 | 输出格式 | 说明 |
10
+ | --- | --- | --- | --- |
11
+ | `bun run build` | 无 | 源码产物 (`./cli`) | 默认构建,不编译为二进制 |
12
+ | `bun run build:dev` | `--dev` | 源码产物 (`./VersperClaw`) | 开发版本,自动附加 git SHA 和构建时间作为开发版本号 |
13
+ | `bun run build:dev:full` | `--dev --feature-set=dev-full` | 源码产物 (`./VersperClaw`) | 开发版本,启用全部实验性功能标记 |
14
+ | `bun run compile` | `--compile` | 二进制可执行文件 (`./dist/cli`) | 生产构建,编译为 Bun 原生二进制 |
15
+ | `bun run dev` | — | 直接运行 | 通过 `bun run ./src/entrypoints/cli.tsx` 直接执行,跳过构建步骤 |
16
+
17
+ ### 1.2 构建流程步骤
18
+
19
+ 1. **Friend 前端构建检查**:检查 `src/components/friend/frontend/dist/index.html` 是否存在,若不存在则调用 `npm run build` 构建 VRM 头像前端(该前端基于 Vite + React + Three.js,位于 `src/components/friend/frontend/`)。
20
+ 2. **版本号计算**:
21
+ - 开发模式(`--dev`):使用 `git rev-parse --short=8 HEAD` 获取当前 commit SHA,生成格式为 `{baseVersion}-dev.{YYYYMMDD}.t{HHmmss}.sha{commit}` 的版本号。
22
+ - 生产模式:直接使用 `package.json` 中的版本字段 `2.1.0`。
23
+ - 同时获取最近的 20 条 git log 作为 changelog(仅开发模式)。
24
+ 3. **编译时宏定义注入**:通过 `--define` 注入 `MACRO.*` 常量(见下文 2.1 节)。
25
+ 4. **Bun 打包**:调用 `bun build`,以 `./src/entrypoints/cli.tsx` 为入口,使用以下关键参数:
26
+ - `--compile`:仅在 `compile` 模式下启用
27
+ - `--target bun`:目标运行时为 Bun
28
+ - `--format esm`:输出 ESM 格式
29
+ - `--minify`:启用代码压缩
30
+ - `--bytecode`:启用字节码缓存
31
+ - `--packages bundle`:将所有依赖打包进产物
32
+ - `--conditions bun`:使用 Bun 条件导出
33
+ 5. **vendors 复制**:非编译模式下,将 `vendor/` 目录(包含 `audio-capture`、`opus-encdec` 等原生二进制库)复制到输出目录的 `vendor/` 子目录下。
34
+ 6. **权限设置**:产物文件设置 `0o755` 可执行权限。
35
+
36
+ ### 1.3 Friend VRM 前端构建集成
37
+
38
+ Friend 是桌面宠物 VRM 伴侣功能,其前端是一个独立的 Vite + React + Three.js 应用,位于 `src/components/friend/frontend/`。构建系统在主构建前检查其 `dist/` 目录是否已存在:
39
+
40
+ - 若已存在(如之前构建过),跳过前端构建步骤。
41
+ - 若不存在,自动执行 `npm run build`(调用 Vite 进行生产构建)。
42
+ - 构建产出包含 VRM 模型文件(`.vrm`)、FBX 动画文件、WASM 运行时(onnxruntime-web)、VAD 模型(silero_vad)和音效文件等静态资源。
43
+
44
+ 此步骤是构建流程的**前置必要条件**——若 Friend 前端构建失败,整个构建过程退出并返回错误码 1。
45
+
46
+ ### 1.4 外部依赖排除
47
+
48
+ 以下 native 模块在构建时被声明为 `--external`,不会打包进最终产物,需在运行时由 Bun 动态解析:
49
+
50
+ - `@ant/*`(Anthropic 内部包)
51
+ - `audio-capture-napi`
52
+ - `image-processor-napi`
53
+ - `modifiers-napi`
54
+ - `url-handler-napi`
55
+
56
+ 这些模块通过 workspace 管理或直接从 vendor 目录加载。
57
+
58
+ ---
59
+
60
+ ## 2. Feature Flag 系统
61
+
62
+ ### 2.1 `bun:bundle` 编译时 Feature 标记
63
+
64
+ 系统使用 Bun 内置的 `import { feature } from 'bun:bundle'` 实现编译时条件编译。`feature('NAME')` 是一个**编译时布尔常量**,在 `bun build` 阶段根据 `--feature=NAME` 参数确定值:
65
+
66
+ - 如果 `NAME` 在传递给 `bun build` 的 feature 集合中,`feature('NAME')` 求值为 `true`
67
+ - 否则求值为 `false`
68
+ - Bun 的打包器会对 `feature('NAME')` 条件分支进行**死代码消除**(Dead Code Elimination, DCE)
69
+
70
+ 代码中典型的使用模式:
71
+
72
+ ```typescript
73
+ import { feature } from 'bun:bundle'
74
+
75
+ // 编译时条件导入——false 分支在产物中完全不存在
76
+ const bridge = feature('BRIDGE_MODE')
77
+ ? require('./commands/bridge/index.js').default
78
+ : null
79
+
80
+ // 编译时条件执行
81
+ if (feature('VOICE_MODE')) {
82
+ // 启用语音模式的代码
83
+ }
84
+ ```
85
+
86
+ ### 2.2 Feature 传递机制
87
+
88
+ Feature 标记通过三种方式传递给构建系统:
89
+
90
+ 1. **编译参数**:`bun run ./scripts/build.ts --feature=NAME`
91
+ 2. **批量设置**:`--feature-set=dev-full` 启用 `fullExperimentalFeatures` 数组中的所有标记
92
+ 3. **默认集合**:`defaultFeatures = ['VOICE_MODE']` 始终包含在构建中
93
+
94
+ 构建脚本中的优先级逻辑:
95
+
96
+ ```typescript
97
+ const defaultFeatures = ['VOICE_MODE']
98
+ const featureSet = new Set(defaultFeatures)
99
+ // 解析 --feature-set=dev-full
100
+ // 解析 --feature NAME
101
+ // 解析 --feature=NAME
102
+ ```
103
+
104
+ 最终所有选中的 feature 通过 `--feature=${feature}` 逐条传递给 `bun build` 命��。
105
+
106
+ ### 2.3 编译时宏注入
107
+
108
+ 除 `feature()` 系统外,构建脚本还通过 `--define` 注入一系列编译时常量(`MACRO.*`)。这些常量在 TypeScript 环境中通过 `env.d.ts` 声明类型:
109
+
110
+ | 宏 | 含义 | 值来源 |
111
+ | --- | --- | --- |
112
+ | `MACRO.VERSION` | 当前版本号 | `package.json` version 或开发版 git 版本 |
113
+ | `MACRO.BUILD_TIME` | 构建时间 | `new Date().toISOString()` |
114
+ | `MACRO.PACKAGE_URL` | 包 URL | `package.json` name |
115
+ | `MACRO.NATIVE_PACKAGE_URL` | 原生包 URL | 始终为 `undefined`(外部构建) |
116
+ | `MACRO.FEEDBACK_CHANNEL` | 反馈渠道 | 固定为 `'github'` |
117
+ | `MACRO.ISSUES_EXPLAINER` | Issue 说明文本 | 固定字符串 |
118
+ | `MACRO.VERSION_CHANGELOG` | 版本变更日志 | 开发模式取 git log;生产模式固定 URL |
119
+
120
+ 除 `MACRO.*` 外,以下 `process.env` 变量也在构建时注入:
121
+
122
+ | 变量 | 值 | 说明 |
123
+ | --- | --- | --- |
124
+ | `process.env.USER_TYPE` | `'external'` | 标记为外部构建(非 Anthropic 内部) |
125
+ | `process.env.CLAUDE_CODE_FORCE_FULL_LOGO` | `'true'` | 强制显示完整 logo |
126
+ | `process.env.NODE_ENV` | `'development'`(仅 dev 模式) | 运行时环境标识 |
127
+ | `process.env.CLAUDE_CODE_EXPERIMENTAL_BUILD` | `'true'`(仅 dev 模式) | 标记为实验性构建 |
128
+ | `process.env.CLAUDE_CODE_VERIFY_PLAN` | `'false'` | 禁用计划验证 |
129
+ | `process.env.CCR_FORCE_BUNDLE` | `'true'` | 强制打包 CCR 相关代码 |
130
+
131
+ ### 2.4 运行时环境变量动态配置
132
+
133
+ 开发环境中,`preload.ts` 在运行时会覆盖 `MACRO` 的值,使用环境变量进行本地开发配置:
134
+
135
+ ```typescript
136
+ const version = process.env.CLAUDE_CODE_LOCAL_VERSION ?? '999.0.0-local'
137
+ // ...
138
+ Object.assign(globalThis, {
139
+ MACRO: {
140
+ VERSION: version,
141
+ PACKAGE_URL: packageUrl,
142
+ NATIVE_PACKAGE_URL: packageUrl,
143
+ BUILD_TIME: buildTime,
144
+ FEEDBACK_CHANNEL: 'local',
145
+ // ...
146
+ },
147
+ })
148
+ ```
149
+
150
+ ### 2.5 Feature 死代码消除机制详解
151
+
152
+ `feature()` 的死代码消除分为两种形态:
153
+
154
+ **形态 A — 条件导入(模块级 DCE)**:
155
+
156
+ ```typescript
157
+ const remoteControlServerCommand =
158
+ feature('DAEMON') && feature('BRIDGE_MODE')
159
+ ? require('./commands/remoteControlServer/index.js').default
160
+ : null
161
+ ```
162
+
163
+ 当 `DAEMON` 或 `BRIDGE_MODE` 未启用时,`require()` 调用和整个依赖图(包括被导入模块的所有递归依赖)均被消除。这是最有效的 DCE 形式,可大幅减小产物体积。
164
+
165
+ **形态 B — 条件执行(语句级 DCE)**:
166
+
167
+ ```typescript
168
+ if (feature('VOICE_MODE')) {
169
+ // 语音相关逻辑
170
+ }
171
+ ```
172
+
173
+ 当 `VOICE_MODE` 未启用时,整个 `if` 块被消除。但被调用的模块如果已在前面的代码中被无条件导入,则仍会保留在产物中。
174
+
175
+ ### 2.6 完整 Feature Flag 列表
176
+
177
+ 以下是从代码库中提取的所有 `feature('NAME')` 调用,按类别分组:
178
+
179
+ #### 语音与输入
180
+
181
+ | Feature | 用途 | 涉及文件数 |
182
+ | --- | --- | --- |
183
+ | `VOICE_MODE` | 语音模式:语音录制、流式 STT(语音转文字)、语音状态指示器、VAD(语音活动检测);默认启用 | ~60+ 处调用 |
184
+ | `NATIVE_CLIPBOARD_IMAGE` | 原生剪贴板图片支持 | 1 |
185
+
186
+ #### 助手/Kairos 会话模式
187
+
188
+ | Feature | 用途 | 涉及文件数 |
189
+ | --- | --- | --- |
190
+ | `KAIROS` | Kairos 助手模式:会话管理、--session-id、--continue 参数、assistant 命令/模块、团队上下文、消息队列管理、daily-log 提示词 | ~100+ 处调用(最广泛) |
191
+ | `KAIROS_BRIEF` | Brief 摘要模式:简化版助手界面、brief 命令、SendUserMessage 替代 | ~25 处调用 |
192
+ | `KAIROS_CHANNELS` | Kairos 频道/通道系统:消息通道路由 | ~8 处调用 |
193
+ | `KAIROS_PUSH_NOTIFICATION` | 推送通知功能 | ~4 处调用 |
194
+ | `KAIROS_GITHUB_WEBHOOKS` | GitHub Webhook 订阅功能(subscribe-pr 命令) | ~3 处调用 |
195
+ | `KAIROS_BRIEF` | 同上(独立用途) | — |
196
+
197
+ #### 桥接/远程控制
198
+
199
+ | Feature | 用途 | 涉及文件数 |
200
+ | --- | --- | --- |
201
+ | `BRIDGE_MODE` | 桥接模式:CLI 与 mobile/web 客户端通信、remote-control 子命令 | ~30 处调用 |
202
+ | `CCR_AUTO_CONNECT` | CCR(Claude Code Remote)自动连接 | 2 |
203
+ | `CCR_MIRROR` | CCR 镜像模式 | ~5 处调用 |
204
+ | `CCR_REMOTE_SETUP` | 远程设置向导(web 命令) | 2 |
205
+ | `DAEMON` | 守护进程模式:daemon worker、后台长期运行 | 4 |
206
+ | `DIRECT_CONNECT` | 直接连接模式(URL/二维码直接连接) | ~8 处调用 |
207
+ | `SSH_REMOTE` | SSH 远程模式:通过 SSH 连接远程会话 | ~6 处调用 |
208
+ | `UDS_INBOX` | Unix Domain Socket 收件箱:对等节点发现、跨进程消息传递 | ~30 处调用 |
209
+
210
+ #### 协作与团队
211
+
212
+ | Feature | 用途 | 涉及文件数 |
213
+ | --- | --- | --- |
214
+ | `COORDINATOR_MODE` | 协调者模式:多 agent 协作、任务分配、worker 管理 | ~20 处调用 |
215
+ | `TEAMMEM` | 团队记忆系统:共享记忆文件、团队上下文读取/搜索/写入 | ~25 处调用 |
216
+ | `BUDDY` | 桌面宠物伴侣:CompanionSprite 渲染、提示词注入、通知 | ~15 处调用 |
217
+ | `FORK_SUBAGENT` | 子 agent 分支:fork 命令、agent 分支执行 | ~3 处调用 |
218
+ | `AGENT_TRIGGERS` | Agent 触发器:cron 定时任务(Create/Delete/List) | ~8 处调用 |
219
+ | `AGENT_TRIGGERS_REMOTE` | 远程触发器 | ~3 处调用 |
220
+ | `AGENT_MEMORY_SNAPSHOT` | Agent 记忆快照:自定义 agent 记忆持久化 | 2 |
221
+ | `COWORKER_TYPE_TELEMETRY` | 同事类型遥测 | 1 |
222
+
223
+ #### 权限与安全
224
+
225
+ | Feature | 用途 | 涉及文件数 |
226
+ | --- | --- | --- |
227
+ | `TRANSCRIPT_CLASSIFIER` | 转录分类器:自动模式(auto mode)、权限模式扩展、YOLO 分类器 | ~60 处调用 |
228
+ | `BASH_CLASSIFIER` | Bash 命令分类器:对 bash 命令进行安全分类、自动批准 | ~20 处调用 |
229
+ | `POWERSHELL_AUTO_MODE` | PowerShell 自动模式 | ~3 处调用 |
230
+ | `PROACTIVE` | 主动模式:模型主动发起交互、背景任务提示 | ~25 处调用 |
231
+ | `BYOC_ENVIRONMENT_RUNNER` | BYOC 环境运行器 | 1 |
232
+ | `SELF_HOSTED_RUNNER` | 自托管运行器 | 1 |
233
+
234
+ #### 上下文管理
235
+
236
+ | Feature | 用途 | 涉及文件数 |
237
+ | --- | --- | --- |
238
+ | `CONTEXT_COLLAPSE` | 上下文折叠:长上下文管理、413 错误处理 | ~15 处调用 |
239
+ | `CACHED_MICROCOMPACT` | 缓存微压缩:在对话流中增量压缩上下文 | ~15 处调用 |
240
+ | `COMPACTION_REMINDERS` | 压缩提醒 | 1 |
241
+ | `HISTORY_SNIP` | 历史摘要截取:SnipTool、会话上下文裁剪 | ~10 处调用 |
242
+ | `HISTORY_PICKER` | 历史选择器:对话历史浏览 | ~5 处调用 |
243
+ | `REACTIVE_COMPACT` | 响应式压缩 | 3 |
244
+ | `PROMPT_CACHE_BREAK_DETECTION` | 提示缓存断裂检测 | 3 |
245
+ | `BREAK_CACHE_COMMAND` | 缓存断裂命令 | 2 |
246
+ | `EXTRACT_MEMORIES` | 记忆提取:会话结束时的自动记忆提取 | ~10 处调用 |
247
+ | `MEMORY_SHAPE_TELEMETRY` | 记忆形状遥测 | 4 |
248
+
249
+ #### 工具
250
+
251
+ | Feature | 用途 | 涉及文件数 |
252
+ | --- | --- | --- |
253
+ | `WORKFLOW_SCRIPTS` | 工作流脚本:WorkflowTool、workflows 命令、后台工作流任务 | ~15 处调用 |
254
+ | `MONITOR_TOOL` | 监控工具:MonitorMcpTask、后台监控 | ~8 处调用 |
255
+ | `WEB_BROWSER_TOOL` | Web 浏览器工具:基于 Bun WebView 的浏览器 | ~4 处调用 |
256
+ | `OVERFLOW_TEST_TOOL` | 溢出测试工具 | 3 |
257
+ | `TERMINAL_PANEL` | 终端面板工具 | 2 |
258
+ | `TORCH` | Torch 命令 | 2 |
259
+ | `ULTRAPLAN` | 超计划模式:增强规划能力 | ~10 处调用 |
260
+ | `ULTRATHINK` | 超思考模式 | 1 |
261
+ | `VERIFICATION_AGENT` | 验证 agent | 2 |
262
+ | `EXPERIMENTAL_SKILL_SEARCH` | 实验性技能搜索 | ~10 处调用 |
263
+ | `SKILL_IMPROVEMENT` | 技能改进反馈 | 1 |
264
+ | `TEMPLATES` | 模板系统:new/list/reply 命令 | ~6 处调用 |
265
+
266
+ #### 构建与发布
267
+
268
+ | Feature | 用途 | 涉及文件数 |
269
+ | --- | --- | --- |
270
+ | `ABLATION_BASELINE` | 消融实验基线:设置多个环境变量 | 1 |
271
+ | `ALLOW_TEST_VERSIONS` | 允许测试版本(99.99.x) | 2 |
272
+ | `DUMP_SYSTEM_PROMPT` | 导出系统提示词(`--dump-system-prompt`) | 1 |
273
+ | `NEW_INIT` | 新的初始化流程 | 2 |
274
+
275
+ #### 用户界面
276
+
277
+ | Feature | 用途 | 涉及文件数 |
278
+ | --- | --- | --- |
279
+ | `MESSAGE_ACTIONS` | 消息操作:消息级交互操作 | ~4 处调用 |
280
+ | `QUICK_SEARCH` | 快速搜索:PromptInput 内联搜索 | ~5 处调用 |
281
+ | `MCP_RICH_OUTPUT` | MCP 富文本输出 | 3 |
282
+ | `AUTO_THEME` | 自动主题:跟随终端主题变化 | 1 |
283
+ | `BUDDY` | 同上(见协作与团队分组) | — |
284
+
285
+ #### 内部与实验性
286
+
287
+ | Feature | 用途 | 涉及文件数 |
288
+ | --- | --- | --- |
289
+ | `ANTI_DISTILLATION_CC` | 反蒸馏保护 | 1 |
290
+ | `BG_SESSIONS` | 后台会话:ps/logs/attach/kill 命令 | ~8 处调用 |
291
+ | `BUILDING_CLAUDE_APPS` | 构建 Claude Apps 技能 | 1 |
292
+ | `BUILTIN_EXPLORE_PLAN_AGENTS` | 内置探索/计划 agent | 1 |
293
+ | `CHICAGO_MCP` | Chicago MCP 协议:MCP 配置、computer-use-mcp 入口 | ~10 处调用 |
294
+ | `COMMIT_ATTRIBUTION` | 提交归属追踪 | ~4 处调用 |
295
+ | `CONNECTOR_TEXT` | Connector 文本块处理 | ~6 处调用 |
296
+ | `DOWNLOAD_USER_SETTINGS` | 下载用户设置 | 1 |
297
+ | `FILE_PERSISTENCE` | 文件持久化(public API / sessions) | 1 |
298
+ | `HARD_FAIL` | 硬失败模式 | 1 |
299
+ | `HOOK_PROMPTS` | Hook 提示词注入 | 1 |
300
+ | `IS_LIBC_GLIBC` | 检测是否使用 glibc | 1 |
301
+ | `IS_LIBC_MUSL` | 检测是否使用 musl libc | 1 |
302
+ | `LODESTONE` | LODESTONE 协议注册 | ~6 处调用 |
303
+ | `MCP_SKILLS` | MCP 技能 | 2 |
304
+ | `NATIVE_CLIENT_ATTESTATION` | 原生客户端认证 | 1 |
305
+ | `PERFETTO_TRACING` | Perfetto 性能追踪 | 1 |
306
+ | `REVIEW_ARTIFACT` | 审查构件技能 | 1 |
307
+ | `RUN_SKILL_GENERATOR` | 运行技能生成器 | 1 |
308
+ | `SHOT_STATS` | 射击统计(对话轮次分布) | 3 |
309
+ | `SLOW_OPERATION_LOGGING` | 慢操作日志 | 1 |
310
+ | `TOKEN_BUDGET` | Token 预算跟踪 | ~10 处调用 |
311
+ | `TREE_SITTER_BASH` | Tree-sitter Bash 解析器 | 1 |
312
+ | `TREE_SITTER_BASH_SHADOW` | Tree-sitter Bash 影子解析 | 1 |
313
+ | `UNATTENDED_RETRY` | 无人值守重试 | 1 |
314
+ | `UPLOAD_USER_SETTINGS` | 上传用户设置 | 1 |
315
+
316
+ ### 2.7 实验性功能全集(`fullExperimentalFeatures`)
317
+
318
+ 在 `scripts/build.ts` 中定义了一个`fullExperimentalFeatures` 常量数组,通过 `--feature-set=dev-full` 批量启用。完整列表:
319
+
320
+ ```
321
+ AGENT_MEMORY_SNAPSHOT, AGENT_TRIGGERS, AGENT_TRIGGERS_REMOTE,
322
+ AWAY_SUMMARY, BASH_CLASSIFIER, BUDDY, BRIDGE_MODE,
323
+ BUILTIN_EXPLORE_PLAN_AGENTS, CACHED_MICROCOMPACT,
324
+ CCR_AUTO_CONNECT, CCR_MIRROR, CCR_REMOTE_SETUP,
325
+ COMPACTION_REMINDERS, CONNECTOR_TEXT, EXTRACT_MEMORIES,
326
+ HISTORY_PICKER, HOOK_PROMPTS, KAIROS_BRIEF, KAIROS_CHANNELS,
327
+ LODESTONE, MCP_RICH_OUTPUT, MESSAGE_ACTIONS, NATIVE_CLIPBOARD_IMAGE,
328
+ NEW_INIT, POWERSHELL_AUTO_MODE, PROMPT_CACHE_BREAK_DETECTION,
329
+ QUICK_SEARCH, SHOT_STATS, TEAMMEM, TOKEN_BUDGET, TREE_SITTER_BASH,
330
+ TREE_SITTER_BASH_SHADOW, TRANSCRIPT_CLASSIFIER, ULTRAPLAN, ULTRATHINK,
331
+ UNATTENDED_RETRY, VERIFICATION_AGENT, VOICE_MODE
332
+ ```
333
+
334
+ 注意:`VOICE_MODE` 同时出现在默认集合 `defaultFeatures` 和实验性集合中,此重复不会造成问题(Set 去重)。
335
+
336
+ ---
337
+
338
+ ## 3. 命令可用性门控
339
+
340
+ ### 3.1 `availability` 声明
341
+
342
+ 每个命令可以通过 `availability` 字段声明其适用的认证/供应商环境。`src/types/command.ts` 中定义了 `CommandAvailability` 类型:
343
+
344
+ ```typescript
345
+ export type CommandAvailability =
346
+ | 'claude-ai' // claude.ai OAuth 订阅用户(Pro/Max/Team/Enterprise)
347
+ | 'console' // Console API key 用户(直接使用 api.anthropic.com)
348
+ ```
349
+
350
+ 可用性声明的命令示例:
351
+
352
+ ```typescript
353
+ // src/commands/usage/index.ts
354
+ { name: 'usage', availability: ['claude-ai'], ... }
355
+
356
+ // src/commands/fast/index.ts
357
+ { name: 'fast', availability: ['claude-ai', 'console'], ... }
358
+ ```
359
+
360
+ `availability` 与 `isEnabled()` 的职责分离:
361
+ - **`availability`** = 谁能使用(基于认证/供应商的静态检查)
362
+ - **`isEnabled()`** = 当前是否开启(基于 feature flags、GrowthBook、环境变量等动态条件)
363
+
364
+ 不存在 `availability` 字段的命令被视为通用命令,在所有环境中可用。
365
+
366
+ ### 3.2 `meetsAvailabilityRequirement()` 检查链
367
+
368
+ `src/commands.ts` 中实现了 `meetsAvailabilityRequirement()` 函数,负责检查命令是否满足可用性要求:
369
+
370
+ ```typescript
371
+ export function meetsAvailabilityRequirement(cmd: Command): boolean {
372
+ if (!cmd.availability) return true // 无限制
373
+ for (const a of cmd.availability) {
374
+ switch (a) {
375
+ case 'claude-ai':
376
+ if (isClaudeAISubscriber()) return true
377
+ break
378
+ case 'console':
379
+ if (!isClaudeAISubscriber() && !isUsing3PServices() && isFirstPartyAnthropicBaseUrl())
380
+ return true
381
+ break
382
+ }
383
+ }
384
+ return false
385
+ }
386
+ ```
387
+
388
+ 检查逻辑:
389
+ - **`claude-ai`**:用户通过 claude.ai OAuth 认证且为订阅用户(Pro/Max/Team/Enterprise)
390
+ - **`console`**:用户非 claude.ai 订阅用户、不使用第三方服务(Bedrock/Vertex/Foundry)、且使用官方 Anthropic API base URL
391
+
392
+ 该检查在命令列表构建时执行,且**不缓存**——因为认证状态可在会话中变化(例如通过 `/login` 命令)。
393
+
394
+ ### 3.3 `getCommands()` 中的过滤链
395
+
396
+ 命令的最终可用性由 `getCommands()` 函数(`src/commands.ts`)计算,过滤链如下:
397
+
398
+ ```typescript
399
+ const allCommands = await loadAllCommands(cwd)
400
+ const baseCommands = allCommands.filter(
401
+ _ => meetsAvailabilityRequirement(_) && isCommandEnabled(_),
402
+ )
403
+ ```
404
+
405
+ 每个命令需**同时满足**:
406
+ 1. `meetsAvailabilityRequirement()` — 认证/供应商匹配
407
+ 2. `isCommandEnabled()` — 命令级启用检查(可关连 feature flag 或动态条件)
408
+
409
+ ### 3.4 命令级 `isEnabled()` / `isHidden()` 控制
410
+
411
+ 除了全局的 feature flag 和 availability 机制,每个命令还可以单独定义 `isEnabled()` 和 `isHidden`:
412
+
413
+ **`isEnabled()` 使用示例**:
414
+
415
+ ```typescript
416
+ // src/commands/voice/index.ts
417
+ { isEnabled: () => isVoiceGrowthBookEnabled() }
418
+
419
+ // src/commands/review.ts
420
+ { isEnabled: () => isUltrareviewEnabled() }
421
+
422
+ // src/commands/session/index.ts
423
+ { isEnabled: () => getIsRemoteMode() }
424
+
425
+ // src/commands/extra-usage/index.ts
426
+ { isEnabled: () => isExtraUsageAllowed() && !getIsNonInteractiveSession() }
427
+ ```
428
+
429
+ **`isHidden` 使用模式**:
430
+
431
+ ```typescript
432
+ // 内部命令(外部构建中完全隐藏——这些命令的 stub 文件导出此配置)
433
+ // src/commands/share/index.js
434
+ export default { isEnabled: () => false, isHidden: true, name: 'stub' }
435
+
436
+ // 条件隐藏
437
+ // src/commands/cost/index.ts
438
+ { get isHidden() { /* 动态条件 */ } }
439
+
440
+ // 始终隐藏
441
+ // src/commands/heapdump/index.ts
442
+ { isHidden: true }
443
+ ```
444
+
445
+ `isCommandEnabled()` 的默认值为 `true`(未定义时),`isHidden` 默认值为 `false`。
446
+
447
+ ### 3.5 `USER_TYPE` 环境变量
448
+
449
+ 构建时通过 `process.env.USER_TYPE` 控制内部/外部构建的差异:
450
+
451
+ - 外部构建:`USER_TYPE = 'external'`(当前版本如此设置)
452
+ - 内部构建:`USER_TYPE = 'ant'`(Anthropic 内部)
453
+
454
+ `USER_TYPE` 控制以下差异:
455
+
456
+ 1. **内部命令注册**:`INTERNAL_ONLY_COMMANDS` 数组(包含 `backfillSessions`、`breakCache`、`bughunter`、`initVerifiers` 等 ~30 个内部命令)仅在 `USER_TYPE === 'ant'` 时注册。
457
+ 2. **工具可用性**:`ConfigTool`、`TungstenTool`、`REPLTool` 仅在内部构建中可用。
458
+ 3. **YOLO 分类器**:内部构建使用更详细的权限分类模板。
459
+
460
+ ---
461
+
462
+ ## 4. 工具过滤
463
+
464
+ ### 4.1 `filterToolsByDenyRules()`
465
+
466
+ `src/tools.ts` 中的 `filterToolsByDenyRules()` 函数根据权限上下文中的拒绝规则过滤工具:
467
+
468
+ ```typescript
469
+ export function filterToolsByDenyRules<T extends { name: string; mcpInfo?: ... }>(
470
+ tools: readonly T[],
471
+ permissionContext: ToolPermissionContext,
472
+ ): T[] {
473
+ return tools.filter(tool => !getDenyRuleForTool(permissionContext, tool))
474
+ }
475
+ ```
476
+
477
+ 此过滤在工具列表最终组装前执行,确保被拒绝的工具(包括按 MCP 服务器前缀拒绝的)在模型看到之前就已移除。
478
+
479
+ ### 4.2 完整工具组装流程
480
+
481
+ `getTools()` 函数(`src/tools.ts`)的组装流程:
482
+
483
+ 1. **Simple 模式**(`CLAUDE_CODE_SIMPLE=1`):仅返回 `BashTool`、`FileReadTool`、`FileEditTool`(或 REPL 模式下的 `REPLTool`),加上协调者模式所需的 `AgentTool` + `TaskStopTool`。
484
+ 2. **完整模式**:通过 `getAllBaseTools()` 获取所有工具,移除特殊工具(`ListMcpResourcesTool`、`ReadMcpResourceTool`、`SYNTHETIC_OUTPUT_TOOL_NAME`)。
485
+ 3. **应用拒绝规则**:`filterToolsByDenyRules()`。
486
+ 4. **REPL 模式屏蔽**:当 REPL 启用时,隐藏 `REPL_ONLY_TOOLS` 集合中的原始工具。
487
+ 5. **应用 `isEnabled()`**:每个工具自身的 `isEnabled()` 检查。
488
+
489
+ ### 4.3 条件工具(`feature()` 门控导入)
490
+
491
+ `src/tools.ts` 中使用 `feature()` 进行条件导入的工具清单:
492
+
493
+ | Feature | 工具类 | 说明 |
494
+ | --- | --- | --- |
495
+ | `PROACTIVE` / `KAIROS` | `SleepTool` | 计划休眠工具 |
496
+ | `AGENT_TRIGGERS` | `CronCreateTool`, `CronDeleteTool`, `CronListTool` | 定时任务管理 |
497
+ | `AGENT_TRIGGERS_REMOTE` | `RemoteTriggerTool` | 远程触发器 |
498
+ | `MONITOR_TOOL` | `MonitorTool` | 监控工具 |
499
+ | `KAIROS` | `SendUserFileTool` | 发送用户文件 |
500
+ | `KAIROS` / `KAIROS_PUSH_NOTIFICATION` | `PushNotificationTool` | 推送通知 |
501
+ | `KAIROS_GITHUB_WEBHOOKS` | `SubscribePRTool` | PR 订阅 |
502
+ | `OVERFLOW_TEST_TOOL` | `OverflowTestTool` | 溢出测试 |
503
+ | `CONTEXT_COLLAPSE` | `CtxInspectTool` | 上下文检查 |
504
+ | `TERMINAL_PANEL` | `TerminalCaptureTool` | 终端捕获 |
505
+ | `WEB_BROWSER_TOOL` | `WebBrowserTool` | 浏览器工具 |
506
+ | `HISTORY_SNIP` | `SnipTool` | 历史摘要 |
507
+ | `UDS_INBOX` | `ListPeersTool` | 对等节点列表 |
508
+ | `WORKFLOW_SCRIPTS` | `WorkflowTool` | 工作流执行 |
509
+
510
+ 内部构建特有的工具(不受 `feature()` 控制,受 `USER_TYPE === 'ant'` 控制):
511
+
512
+ - `REPLTool`:REPL 交互式开发环境
513
+ - `SuggestBackgroundPRTool`:PR 建议工具
514
+ - `ConfigTool`、`TungstenTool`:内部配置工具
515
+
516
+ ### 4.4 MCP 工具合并
517
+
518
+ `assembleToolPool()` 函数(`src/tools.ts`)合并内置工具和 MCP 工具:
519
+
520
+ 1. 通过 `getTools()` 获取内置工具
521
+ 2. 通过 `filterToolsByDenyRules()` 过滤 MCP 工具
522
+ 3. 使用 `uniqBy()` 按名称去重(内置工具优先)
523
+ 4. 按名称排序以保证提示缓存稳定性
524
+
525
+ ---
526
+
527
+ ## 5. 构建产物
528
+
529
+ ### 5.1 输出目录结构
530
+
531
+ 构建产物的 `dist/` 目录结构:
532
+
533
+ ```
534
+ dist/
535
+ ├── cli # 生产二进制(`--compile` 模式,~192MB)
536
+ ├── cli.js # 生产源码产物(非编译模式,~20MB)
537
+ ├── VersperClaw # 开发二进制(`--dev --compile` 模式,~202MB)
538
+ └── vendor/ # 原生二进制库(仅非编译模式)
539
+ ├── audio-capture/ # 音频捕获原生模块
540
+ ├── audio-capture-src/ # 音频捕获源码
541
+ └── opus-encdec/ # Opus 编码/解码
542
+ ```
543
+
544
+ ### 5.2 各模式产物对比
545
+
546
+ | 构建模式 | 入口文件 | 产物路径 | 大小 | 类型 |
547
+ | --- | --- | --- | --- | --- |
548
+ | `build`(默认) | `scripts/build.ts` | `./cli` | ~0(源码引用) | 源码(Bun bundle) |
549
+ | `build:dev` | `scripts/build.ts --dev` | `./VersperClaw` | ~0(源码引用) | 源码 |
550
+ | `compile` | `scripts/build.ts --compile` | `./dist/cli` | ~192MB | Bun 编译二进制 |
551
+ | `compile + dev` | `scripts/build.ts --compile --dev` | `./dist/VersperClaw` | ~202MB | Bun 编译二进制(调试) |
552
+
553
+ ### 5.3 预加载脚本
554
+
555
+ `preload.ts` 是开发模式下的运行时预加载脚本,在执行入口文件前:
556
+ - 设置 `MACRO` 全局变量
557
+ - 设置 `CLAUDE_CODE_LOCAL_SKIP_REMOTE_PREFETCH=1` 跳过远程预取
558
+ - 切换到调用目录(`process.chdir(CALLER_DIR)`)
559
+
560
+ ### 5.4 Friend 前端静态资源
561
+
562
+ Friend VRM 前端构建产出包含大量静态资源(位于 `src/components/friend/frontend/dist/`):
563
+
564
+ - **VRM 模型**:5 个 `.vrm` 文件(11MB-48MB 不等)
565
+ - **FBX 动画**:约 10 个动画文件(`angry.fbx`、`happy.fbx`、`greeting.fbx` 等)
566
+ - **VMD/VRMA 动作**:`jile.vmd`、`idle_loop.vrma`、`playFingers.vrma` 等
567
+ - **WASM 文件**:onnxruntime-web 运行时的多�� WASM 二进制(`ort-wasm-simd-threaded.wasm` 等,合计约 80MB)
568
+ - **VAD 模型**:`silero_vad_legacy.onnx`(1.8MB)、`silero_vad_v5.onnx`(2.3MB)
569
+ - **音效**:`jile.mp3`(4.2MB)、`love.mp3`(11MB)
570
+ - **音频工作集**:`vad.worklet.bundle.min.js`(2.5KB)
571
+ - **HTML 入口**:`index.html`
572
+
573
+ ### 5.5 WASM 与原生库
574
+
575
+ 项目依赖多个 WASM 和原生库:
576
+
577
+ - **onnxruntime-web**:用于语音活动检测(VAD)的推理引擎
578
+ - **audio-capture-napi**(workspace):原生音频捕获库,通过 workspace `packages/audio-capture-napi` 管理
579
+ - **doubaoime-asr**:字节跳动豆包语音识别引擎(workspace symlink)
580
+
581
+ ---
582
+
583
+ ## 6. 开发工作流
584
+
585
+ ### 6.1 开发模式启动
586
+
587
+ ```bash
588
+ # 直接运行(无需构建,适合快速迭代)
589
+ bun run dev
590
+ # 等价于:bun run ./src/entrypoints/cli.tsx
591
+
592
+ # 开发构建 + 运行
593
+ bun run build:dev
594
+ ./VersperClaw
595
+
596
+ # 开发构建(全部实验特性)+ 运行
597
+ bun run build:dev:full
598
+ ./VersperClaw
599
+ ```
600
+
601
+ ### 6.2 生产构建
602
+
603
+ ```bash
604
+ # 生产编译
605
+ bun run compile
606
+ # 输出:./dist/cli(~192MB 二进制)
607
+
608
+ # 测试构建
609
+ bun run build:dev:compile # 注意:此命令需要额外添加 --compile 参数
610
+ ```
611
+
612
+ ### 6.3 包管理
613
+
614
+ 项目使用 **Bun workspaces** 管理 monorepo:
615
+
616
+ ```json
617
+ {
618
+ "workspaces": ["packages/*"],
619
+ "packageManager": "bun@1.3.11",
620
+ "engines": { "bun": ">=1.3.11" }
621
+ }
622
+ ```
623
+
624
+ workspace 包:
625
+
626
+ - `packages/audio-capture-napi/`:音频捕获原生 N-API 模块
627
+ - `packages/doubaoime-asr/`:豆包语音识别(symlink 到 .bun 缓存)
628
+
629
+ TypeScript 配置(`tsconfig.json`):
630
+
631
+ ```json
632
+ {
633
+ "compilerOptions": {
634
+ "lib": ["ESNext"],
635
+ "target": "ESNext",
636
+ "module": "Preserve",
637
+ "jsx": "react-jsx",
638
+ "types": ["bun"],
639
+ "moduleResolution": "bundler",
640
+ "noEmit": true,
641
+ "strict": false,
642
+ "skipLibCheck": true
643
+ },
644
+ "include": ["src", "scripts", "env.d.ts"]
645
+ }
646
+ ```
647
+
648
+ ### 6.4 依赖特性说明
649
+
650
+ - **运行时 UI**:基于 Ink(React for CLI)和 JSX 构建交互式终端界面
651
+ - **语音处理**:使用 `@ericedouard/vad-node-realtime`(VAD)、`node-edge-tts`(TTS)
652
+ - **AI 服务**:多供应商支持(Anthropic SDK、AWS Bedrock、Azure、Google Vertex、Groq)
653
+ - **MCP 协议**:`@modelcontextprotocol/sdk` 和 `@anthropic-ai/mcpb`
654
+ - **功能标记运行时**:`@growthbook/growthbook` 用于运行时功能开关(与编译时 `feature()` 互补)
655
+ - **WebView**:通过 Bun 内置的 `WebView` 支持 Web 浏览器工具
656
+ - **Feishu/Lark**:`@larksuiteoapi/node-sdk` 集成飞书机器人
657
+ - **Telegram**:telegram 命令集成
658
+
659
+ ### 6.5 构建脚本快速参考
660
+
661
+ | 命令 | 完整脚本 |
662
+ | --- | --- |
663
+ | `bun run dev` | `bun run ./src/entrypoints/cli.tsx` |
664
+ | `bun run build` | `bun run ./scripts/build.ts` |
665
+ | `bun run build:dev` | `bun run ./scripts/build.ts --dev` |
666
+ | `bun run build:dev:full` | `bun run ./scripts/build.ts --dev --feature-set=dev-full` |
667
+ | `bun run compile` | `bun run ./scripts/build.ts --compile` |
668
+
669
+ 自定义构建示例:
670
+ ```bash
671
+ # 带自定义 feature 的开发构建
672
+ bun run ./scripts/build.ts --dev --feature=KAIROS --feature=BUDDY
673
+
674
+ # 编译 + 特定 feature
675
+ bun run ./scripts/build.ts --compile --feature=TRANSCRIPT_CLASSIFIER
676
+ ```
docs/cli/overview.md ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CLI 命令系统
2
+
3
+ ## 架构概述
4
+
5
+ ### 入口
6
+
7
+ CLI 入口由 **Commander.js** 解析命令行参数,随后启动 **REPL**(交互式循环)。REPL 负责接收用户输入、解析斜杠命令、调用对应的处理函数。
8
+
9
+ ### 命令注册
10
+
11
+ 命令通过 **6 种来源** 注册到系统:
12
+
13
+ | 来源 | 说明 | 注册位置 |
14
+ |------|------|----------|
15
+ | **内置命令** | `COMMANDS()` 函数返回的核心命令列表 | `src/commands.ts:261-354` |
16
+ | **内置技能** | 随 CLI 打包的 Markdown 技能文件 | `getBundledSkills()` |
17
+ | **内置插件** | 内置插件导出的技能命令 | `getBuiltinPluginSkillCommands()` |
18
+ | **用户技能目录** | 用户自定义的 Markdown 技能文件 | `getSkillDirCommands()` |
19
+ | **工作流** | 多步骤脚本工作流 | `getWorkflowCommands()` |
20
+ | **市场插件** | 从插件市场安装的外部插件 | `getPluginCommands()`, `getPluginSkills()` |
21
+
22
+ ### 命令过滤
23
+
24
+ `getCommands()` 是命令的最终汇聚函数,执行以下过滤:
25
+
26
+ 1. **availability 检查**:`meetsAvailabilityRequirement()` — 验证当前用户是否有权限(如 claude.ai 订阅者、Console API 用户)
27
+ 2. **isEnabled 检查**:`isCommandEnabled()` — feature flag 控制是否启用
28
+ 3. **去重**:内置命令与动态技能(`getDynamicSkills()`)间去重,内置命令优先
29
+ 4. **动态技能插入**:在插件命令之后、内置命令之前插入
30
+
31
+ ### 远程模式过滤
32
+
33
+ `REMOTE_SAFE_COMMANDS` 集合定义了可在 `--remote` 模式下安全使用的命令(仅影响本地 TUI 状态)。
34
+
35
+ `BRIDGE_SAFE_COMMANDS` 和 `isBridgeSafeCommand()` 定义了可通过 Remote Control bridge(移动端/web 客户端)执行的命令。
36
+
37
+ ---
38
+
39
+ ## 内置命令详表
40
+
41
+ 以下为 `COMMANDS()` 中注册的所有内置命令,按功能分组:
42
+
43
+ ### 导航与信息
44
+
45
+ | 命令 | 别名 | 描述 |
46
+ |------|------|------|
47
+ | `/help` | - | 显示帮助信息,列出所有可用命令 |
48
+ | `/clear` | - | 清屏,重置终端显示 |
49
+ | `/exit` | - | 退出 CLI |
50
+ | `/init` | - | 在项目中初始化 Claude Code 配置文件 |
51
+ | `/resume` | - | 恢复之前的会话 |
52
+ | `/status` | - | 显示当前会话状态 |
53
+ | `/stats` | - | 统计信息和指标 |
54
+
55
+ ### 配置管理
56
+
57
+ | 命令 | 别名 | 描述 |
58
+ |------|------|------|
59
+ | `/config` | - | 系统设置管理(theme, model, permissions 等) |
60
+ | `/model` | - | 切换 AI 模型 |
61
+ | `/theme` | - | 切换终端主题 |
62
+ | `/color` | - | 更改 AI 回复颜色 |
63
+ | `/permissions` | - | 权限模式管理 |
64
+ | `/privacySettings` | - | 隐私设置 |
65
+ | `/outputStyle` | - | 输出风格切换 |
66
+ | `/statusline` | - | 状态行开关 |
67
+ | `/effort` | - | 设置思考/推理投入度 |
68
+ | `/fast` | - | 快速模式切换 |
69
+ | `/env` | ant 内部 | 环境变量管理 |
70
+ | `/remoteEnv` | - | 远程环境变量管理 |
71
+ | `/passes` | - | 管理预设的 always-allow/always-deny 规则 |
72
+
73
+ ### 会话管理
74
+
75
+ | 命令 | 别名 | 描述 |
76
+ |------|------|------|
77
+ | `/session` | - | 会话管理(分享、导出、查看记录) |
78
+ | `/cost` | - | 显示当前会话费用 |
79
+ | `/usage` | - | 显示 API 用量 |
80
+ | `/compact` | - | 压缩上下文,减少 token 消耗 |
81
+ | `/copy` | - | 复制最后一条消息 |
82
+ | `/rename` | - | 重命名当前会话 |
83
+ | `/tag` | - | 为会话添加标签 |
84
+ | `/btw` | - | 快速记笔记,补充上下文 |
85
+ | `/rewind` | - | 回滚到之前的对话状态 |
86
+
87
+ ### 工具与技能
88
+
89
+ | 命令 | 别名 | 描述 |
90
+ |------|------|------|
91
+ | `/skills` | - | 管理技能(列出、启用、禁用) |
92
+ | `/mcp` | - | MCP 服务器管理(添加、连接、查看) |
93
+ | `/plugin` | - | 插件管理 |
94
+ | `/reloadPlugins` | - | 重新加载所有插件 |
95
+ | `/hooks` | - | 钩子系统管理 |
96
+ | `/keys` / `/keybindings` | - | 快捷键管理 |
97
+ | `/add-dir` | - | 添加技能目录 |
98
+ | `/terminalSetup` | - | 终端设置工具 |
99
+
100
+ ### 语音与 Friend
101
+
102
+ | 命令 | 别名 | 描述 |
103
+ |------|------|------|
104
+ | `/voice` | feature 控制 | 语音听写模式 |
105
+ | `/friend` | - | Friend 虚拟宠物管理 |
106
+ | `/thinkback` | - | Thinkback 回放 |
107
+ | `/thinkbackPlay` | - | Thinkback 播放控制 |
108
+
109
+ ### Goal 与规划
110
+
111
+ | 命令 | 别名 | 描述 |
112
+ |------|------|------|
113
+ | `/goal` / `/goals` | - | 目标管理(创建、查看、更新) |
114
+ | `/plan` | - | 规划模式切换 |
115
+ | `/ultraplan` | feature 控制 | 高级规划模式 |
116
+ | `/agent` / `/agents` | - | 代理管理(列出、配置) |
117
+ | `/tasks` | - | 后台任务管理 |
118
+
119
+ ### Git 与文件
120
+
121
+ | 命令 | 别名 | 描述 |
122
+ |------|------|------|
123
+ | `/diff` | - | 显示 Git diff |
124
+ | `/branch` | - | 分支管理 |
125
+ | `/files` | - | 显示会话中跟踪的文件 |
126
+ | `/commit` | ant 内部 | 创建 Git 提交 |
127
+ | `/commit-push-pr` | ant 内部 | 提交、推送、创建 PR |
128
+ | `/fork` | feature 控制 | Fork 子代理 |
129
+ | `/buddy` | feature 控制 | 协作编程伙伴 |
130
+
131
+ ### 集成与外部服务
132
+
133
+ | 命令 | 别名 | 描述 |
134
+ |------|------|------|
135
+ | `/login` | - | 登录(��� 3P 用户) |
136
+ | `/logout` | - | 登出 |
137
+ | `/feishu` | - | 飞书集成 |
138
+ | `/telegram` | - | Telegram 集成 |
139
+ | `/desktop` | - | 桌面应用模式 |
140
+ | `/mobile` | - | 移动端二维码 |
141
+ | `/install-github-app` | - | 安装 GitHub App |
142
+ | `/install-slack-app` | - | 安装 Slack App |
143
+ | `/bridge` | feature 控制 | 桥接模式 |
144
+ | `/peers` | feature 控制 | UDS 对等节点管理 |
145
+ | `/subscribe-pr` | feature 控制 | 订阅 PR 通知 |
146
+
147
+ ### 诊断与开发
148
+
149
+ | 命令 | 别名 | 描述 |
150
+ |------|------|------|
151
+ | `/doctor` | - | 系统诊断,检查配置和依赖 |
152
+ | `/upgrade` | - | 升级 CLI 版本 |
153
+ | `/version` | ant 内部 | 显示版本信息 |
154
+ | `/heapdump` | - | 堆转储(调试用) |
155
+ | `/sandbox-toggle` | - | 沙箱模式开关 |
156
+ | `/debug-tool-call` | ant 内部 | 调试工具调用 |
157
+ | `/perf-issue` | ant 内部 | 性能问题报告 |
158
+ | `/ant-trace` | ant 内部 | Ant 追踪 |
159
+ | `/oauth-refresh` | ant 内部 | OAuth token 刷新 |
160
+ | `/color` | - | 更改 AI 颜色 |
161
+ | `/stickers` | - | 贴纸管理 |
162
+
163
+ ### 反馈与分析
164
+
165
+ | 命令 | 别名 | 描述 |
166
+ |------|------|------|
167
+ | `/feedback` | - | 发送反馈 |
168
+ | `/review` | - | 代码审查 |
169
+ | `/ultrareview` | - | 深度代码审查 |
170
+ | `/security-review` | - | 安全审查 |
171
+ | `/insights` | - | 会话分析报告 |
172
+ | `/extra-usage` | - | 额外用量显示 |
173
+ | `/rate-limit-options` | - | 速率限制选项 |
174
+ | `/summary` | ant 内部 | 会话摘要生成 |
175
+ | `/share` | ant 内部 | 分享会话 |
176
+ | `/release-notes` | - | 版本发布说明 |
177
+ | `/cost` | - | 会话费用 |
178
+ | `/usage` | - | 使用情况 |
179
+
180
+ ### 实验性 & Feature-gated
181
+
182
+ | 命令 | 别名 | 描述 |
183
+ |------|------|------|
184
+ | `/proactive` | feature 控制 | 主动模式 |
185
+ | `/brief` | feature 控制 | 简报模式 |
186
+ | `/assistant` | feature 控制 | 助手模式 |
187
+ | `/torch` | feature 控制 | Torch 调试工具 |
188
+ | `/web` / `/remote-setup` | feature 控制 | 远程设置 |
189
+ | `/workflows` | feature 控制 | 工作流管理 |
190
+ | `/force-snip` | feature 控制 | 强制截断上下文 |
191
+ | `/remoteControlServer` | feature 控制 | 远程控制服务器 |
192
+ | `/buddy` | feature 控制 | 编程伙伴 |
193
+ | `/agents-platform` | ant 内部 | 代理平台管理 |
194
+ | `/bughunter` | ant 内部 | Bug 猎人工具 |
195
+ | `/autofix-pr` | ant 内部 | 自动修复 PR |
196
+ | `/backfill-sessions` | ant 内部 | 回填会话数据 |
197
+ | `/issue` | ant 内部 | 问题管理 |
198
+ | `/onboarding` | ant 内部 | 引导流程 |
199
+ | `/teleport` | ant 内部 | 远程会话跳转 |
200
+ | `/good-claude` | ant 内部 | 内部工具 |
201
+ | `/ctx_viz` | ant 内部 | 上下文可视化 |
202
+ | `/mock-limits` | ant 内部 | Mock 限制测试 |
203
+ | `/bridge-kick` | ant 内部 | 桥接踢出 |
204
+ | `/reset-limits` | ant 内部 | 重置限制 |
205
+
206
+ ---
207
+
208
+ ## Skill 系统
209
+
210
+ 技能(Skills)是以 Markdown 文件形式定义的提示词模板,可被 LLM 或用户调用。技能系统分为:
211
+
212
+ - **打包技能(Bundled Skills)**:随 CLI 发布的内置 `.md` 文件
213
+ - **用户技能目录(Skill Dir Commands)**:用户在工作区 `./claude/skills/` 或全局 `~/.claude/skills/` 中定义的技能
214
+ - **插件技能(Plugin Skills)**:从插件加载的技能
215
+ - **MCP 技能**:通过 MCP 协议提供的技能(`getMcpSkillCommands()`)
216
+
217
+ 技能文件的 YAML frontmatter 定义 `name`、`description`、`model` 等属性,正文为发送给 LLM 的提示词。
218
+
219
+ ### 技能相关工具
220
+
221
+ - `SkillTool`:AI 可调用的技能执行工具
222
+ - `getSkillToolCommands()`:获取所有可被模型调用的 prompt 类型命令
223
+ - `getSlashCommandToolSkills()`:获取斜杠命令可用的技能列表
224
+
225
+ ---
226
+
227
+ ## 工作流系统
228
+
229
+ 工作流(Workflows)是通过 `WorkflowTool` 实现的多步骤脚本,支持:
230
+
231
+ - 顺序执行多个步骤
232
+ - 条件分支
233
+ - 变量传递
234
+ - 用户确认点
235
+
236
+ 当 `WORKFLOW_SCRIPTS` feature flag 启用时,工作流命令通过 `createWorkflowCommand()` 注册。
237
+
238
+ ---
239
+
240
+ ## 命令类型
241
+
242
+ `Command` 类型包括三种变体:
243
+
244
+ | 类型 | 说明 |
245
+ |------|------|
246
+ | `prompt` | 扩展为提示词发送给模型(技能、工作流) |
247
+ | `local` | 本地执行,输出纯文本 |
248
+ | `local-jsx` | 本地执行,渲染 Ink UI 组件 |
249
+
250
+ ---
251
+
252
+ ## 配置管理
253
+
254
+ `/config` 命令管理系统设置。配置值存储在:
255
+
256
+ - **用户设置**:`~/.claude/settings.json`
257
+ - **项目设置**:`.claude/settings.json`(项目级覆盖)
258
+ - **会话设置**:仅当前会话有效
259
+
260
+ 支持的配置项包括 theme、model、permissions(权限模式)、音效、通知等。
261
+
262
+ 相关文件:
263
+ - `/home/yuki/Code/Agent/VersperClaw/src/commands.ts` — 命令注册与过滤核心
264
+ - `/home/yuki/Code/Agent/VersperClaw/src/types/command.ts` — Command 类型定义
265
+ - `/home/yuki/Code/Agent/VersperClaw/src/skills/loadSkillsDir.ts` — 技能目录加载
266
+ - `/home/yuki/Code/Agent/VersperClaw/src/tools/WorkflowTool/` — 工作流工具实现
src/cli/bg.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // @generated stub from scan-missing-imports
2
+ // 该文件自动生成,对应 ant-internal 的 feature() gated 模块。
3
+ // 所有外部 build 的代码路径在 DCE 后都不会真的执行这里的代码,这只是
4
+ // bun build resolver 的占位符。
5
+ const __target = function noop() {}
6
+ const __handler: ProxyHandler<any> = {
7
+ get(_t, prop) {
8
+ if (prop === '__esModule') return true
9
+ if (prop === 'default') return new Proxy(__target, __handler)
10
+ if (prop === Symbol.toPrimitive) return () => undefined
11
+ if (prop === Symbol.iterator) return function* () {}
12
+ if (prop === Symbol.asyncIterator) return async function* () {}
13
+ if (prop === 'then') return undefined
14
+ return new Proxy(__target, __handler)
15
+ },
16
+ apply() {
17
+ return new Proxy(__target, __handler)
18
+ },
19
+ construct() {
20
+ return new Proxy(__target, __handler)
21
+ },
22
+ }
23
+ const stub: any = new Proxy(__target, __handler)
24
+ export default stub
25
+ export const __stubMissing = true
26
+ // 兼容常见的命名导出 —— 没列在这里的也会通过 default Proxy 兜底
27
+ export const createCachedMCState = stub
28
+ export const isCachedMicrocompactEnabled = stub
29
+ export const isModelSupportedForCacheEditing = stub
30
+ export const getCachedMCConfig = stub
31
+ export const markToolsSentToAPI = stub
32
+ export const resetCachedMCState = stub
33
+ export const checkProtectedNamespace = stub
34
+ export const getCoordinatorUserContext = stub
src/cli/exit.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Exit handler
3
+ * This handler is used to exit the CLI with a status code
4
+ */
5
+
6
+ export function exit(): never {
7
+ process.exit(0);
8
+ }
9
+
10
+ export function exitWithError(message: string, code: number = 1): never {
11
+ console.error(message);
12
+ process.exit(code);
13
+ }
14
+
15
+ export function cliError(message: string): never {
16
+ console.error(message);
17
+ process.exit(1);
18
+ }
19
+
20
+ export function cliOk(message?: string): never {
21
+ if (message) {
22
+ console.log(message);
23
+ }
24
+ process.exit(0);
25
+ }
src/cli/handlers/agents.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Agents command handlers
3
+ * These handlers are used by the CLI agents commands
4
+ */
5
+
6
+ export async function agentsHandler(): Promise<void> {
7
+ console.error('Agents handler not implemented');
8
+ process.exit(1);
9
+ }
src/cli/handlers/ant.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // @generated stub from scan-missing-imports
2
+ // 该文件自动生成,对应 ant-internal 的 feature() gated 模块。
3
+ // 所有外部 build 的代码路径在 DCE 后都不会真的执行这里的代码,这只是
4
+ // bun build resolver 的占位符。
5
+ const __target = function noop() {}
6
+ const __handler: ProxyHandler<any> = {
7
+ get(_t, prop) {
8
+ if (prop === '__esModule') return true
9
+ if (prop === 'default') return new Proxy(__target, __handler)
10
+ if (prop === Symbol.toPrimitive) return () => undefined
11
+ if (prop === Symbol.iterator) return function* () {}
12
+ if (prop === Symbol.asyncIterator) return async function* () {}
13
+ if (prop === 'then') return undefined
14
+ return new Proxy(__target, __handler)
15
+ },
16
+ apply() {
17
+ return new Proxy(__target, __handler)
18
+ },
19
+ construct() {
20
+ return new Proxy(__target, __handler)
21
+ },
22
+ }
23
+ const stub: any = new Proxy(__target, __handler)
24
+ export default stub
25
+ export const __stubMissing = true
26
+ // 兼容常见的命名导出 —— 没列在这里的也会通过 default Proxy 兜底
27
+ export const createCachedMCState = stub
28
+ export const isCachedMicrocompactEnabled = stub
29
+ export const isModelSupportedForCacheEditing = stub
30
+ export const getCachedMCConfig = stub
31
+ export const markToolsSentToAPI = stub
32
+ export const resetCachedMCState = stub
33
+ export const checkProtectedNamespace = stub
34
+ export const getCoordinatorUserContext = stub
src/cli/handlers/auth.ts ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Auth command handlers
3
+ * These handlers are used by the CLI auth commands
4
+ */
5
+
6
+ export async function installOAuthTokens(result: any): Promise<void> {
7
+ console.error('Install OAuth tokens handler not implemented');
8
+ process.exit(1);
9
+ }
10
+
11
+ export async function authLogin(options: any): Promise<void> {
12
+ console.error('Auth login handler not implemented');
13
+ process.exit(1);
14
+ }
15
+
16
+ export async function authLogout(): Promise<void> {
17
+ console.error('Auth logout handler not implemented');
18
+ process.exit(1);
19
+ }
20
+
21
+ export async function authStatus(opts: any): Promise<void> {
22
+ console.error('Auth status handler not implemented');
23
+ process.exit(1);
24
+ }
src/cli/handlers/autoMode.ts ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Auto mode subcommand handlers — dump default/merged classifier rules and
3
+ * critique user-written rules. Dynamically imported when `claude auto-mode ...` runs.
4
+ */
5
+
6
+ import { errorMessage } from '../../utils/errors.js'
7
+ import {
8
+ getMainLoopModel,
9
+ parseUserSpecifiedModel,
10
+ } from '../../utils/model/model.js'
11
+ import {
12
+ type AutoModeRules,
13
+ buildDefaultExternalSystemPrompt,
14
+ getDefaultExternalAutoModeRules,
15
+ } from '../../utils/permissions/yoloClassifier.js'
16
+ import { getAutoModeConfig } from '../../utils/settings/settings.js'
17
+ import { sideQuery } from '../../utils/sideQuery.js'
18
+ import { jsonStringify } from '../../utils/slowOperations.js'
19
+
20
+ function writeRules(rules: AutoModeRules): void {
21
+ process.stdout.write(jsonStringify(rules, null, 2) + '\n')
22
+ }
23
+
24
+ export function autoModeDefaultsHandler(): void {
25
+ writeRules(getDefaultExternalAutoModeRules())
26
+ }
27
+
28
+ /**
29
+ * Dump the effective auto mode config: user settings where provided, external
30
+ * defaults otherwise. Per-section REPLACE semantics — matches how
31
+ * buildYoloSystemPrompt resolves the external template (a non-empty user
32
+ * section replaces that section's defaults entirely; an empty/absent section
33
+ * falls through to defaults).
34
+ */
35
+ export function autoModeConfigHandler(): void {
36
+ const config = getAutoModeConfig()
37
+ const defaults = getDefaultExternalAutoModeRules()
38
+ writeRules({
39
+ allow: config?.allow?.length ? config.allow : defaults.allow,
40
+ soft_deny: config?.soft_deny?.length
41
+ ? config.soft_deny
42
+ : defaults.soft_deny,
43
+ environment: config?.environment?.length
44
+ ? config.environment
45
+ : defaults.environment,
46
+ })
47
+ }
48
+
49
+ const CRITIQUE_SYSTEM_PROMPT =
50
+ 'You are an expert reviewer of auto mode classifier rules for Claude Code.\n' +
51
+ '\n' +
52
+ 'Claude Code has an "auto mode" that uses an AI classifier to decide whether ' +
53
+ 'tool calls should be auto-approved or require user confirmation. Users can ' +
54
+ 'write custom rules in three categories:\n' +
55
+ '\n' +
56
+ '- **allow**: Actions the classifier should auto-approve\n' +
57
+ '- **soft_deny**: Actions the classifier should block (require user confirmation)\n' +
58
+ "- **environment**: Context about the user's setup that helps the classifier make decisions\n" +
59
+ '\n' +
60
+ "Your job is to critique the user's custom rules for clarity, completeness, " +
61
+ 'and potential issues. The classifier is an LLM that reads these rules as ' +
62
+ 'part of its system prompt.\n' +
63
+ '\n' +
64
+ 'For each rule, evaluate:\n' +
65
+ '1. **Clarity**: Is the rule unambiguous? Could the classifier misinterpret it?\n' +
66
+ "2. **Completeness**: Are there gaps or edge cases the rule doesn't cover?\n" +
67
+ '3. **Conflicts**: Do any of the rules conflict with each other?\n' +
68
+ '4. **Actionability**: Is the rule specific enough for the classifier to act on?\n' +
69
+ '\n' +
70
+ 'Be concise and constructive. Only comment on rules that could be improved. ' +
71
+ 'If all rules look good, say so.'
72
+
73
+ export async function autoModeCritiqueHandler(options: {
74
+ model?: string
75
+ }): Promise<void> {
76
+ const config = getAutoModeConfig()
77
+ const hasCustomRules =
78
+ (config?.allow?.length ?? 0) > 0 ||
79
+ (config?.soft_deny?.length ?? 0) > 0 ||
80
+ (config?.environment?.length ?? 0) > 0
81
+
82
+ if (!hasCustomRules) {
83
+ process.stdout.write(
84
+ 'No custom auto mode rules found.\n\n' +
85
+ 'Add rules to your settings file under autoMode.{allow, soft_deny, environment}.\n' +
86
+ 'Run `claude auto-mode defaults` to see the default rules for reference.\n',
87
+ )
88
+ return
89
+ }
90
+
91
+ const model = options.model
92
+ ? parseUserSpecifiedModel(options.model)
93
+ : getMainLoopModel()
94
+
95
+ const defaults = getDefaultExternalAutoModeRules()
96
+ const classifierPrompt = buildDefaultExternalSystemPrompt()
97
+
98
+ const userRulesSummary =
99
+ formatRulesForCritique('allow', config?.allow ?? [], defaults.allow) +
100
+ formatRulesForCritique(
101
+ 'soft_deny',
102
+ config?.soft_deny ?? [],
103
+ defaults.soft_deny,
104
+ ) +
105
+ formatRulesForCritique(
106
+ 'environment',
107
+ config?.environment ?? [],
108
+ defaults.environment,
109
+ )
110
+
111
+ process.stdout.write('Analyzing your auto mode rules…\n\n')
112
+
113
+ let response
114
+ try {
115
+ response = await sideQuery({
116
+ querySource: 'auto_mode_critique',
117
+ model,
118
+ system: CRITIQUE_SYSTEM_PROMPT,
119
+ skipSystemPromptPrefix: true,
120
+ max_tokens: 4096,
121
+ messages: [
122
+ {
123
+ role: 'user',
124
+ content:
125
+ 'Here is the full classifier system prompt that the auto mode classifier receives:\n\n' +
126
+ '<classifier_system_prompt>\n' +
127
+ classifierPrompt +
128
+ '\n</classifier_system_prompt>\n\n' +
129
+ "Here are the user's custom rules that REPLACE the corresponding default sections:\n\n" +
130
+ userRulesSummary +
131
+ '\nPlease critique these custom rules.',
132
+ },
133
+ ],
134
+ })
135
+ } catch (error) {
136
+ process.stderr.write(
137
+ 'Failed to analyze rules: ' + errorMessage(error) + '\n',
138
+ )
139
+ process.exitCode = 1
140
+ return
141
+ }
142
+
143
+ const textBlock = response.content.find(block => block.type === 'text')
144
+ if (textBlock?.type === 'text') {
145
+ process.stdout.write(textBlock.text + '\n')
146
+ } else {
147
+ process.stdout.write('No critique was generated. Please try again.\n')
148
+ }
149
+ }
150
+
151
+ function formatRulesForCritique(
152
+ section: string,
153
+ userRules: string[],
154
+ defaultRules: string[],
155
+ ): string {
156
+ if (userRules.length === 0) return ''
157
+ const customLines = userRules.map(r => '- ' + r).join('\n')
158
+ const defaultLines = defaultRules.map(r => '- ' + r).join('\n')
159
+ return (
160
+ '## ' +
161
+ section +
162
+ ' (custom rules replacing defaults)\n' +
163
+ 'Custom:\n' +
164
+ customLines +
165
+ '\n\n' +
166
+ 'Defaults being replaced:\n' +
167
+ defaultLines +
168
+ '\n\n'
169
+ )
170
+ }
src/cli/handlers/mcp.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * MCP command handlers
3
+ * These handlers are used by the CLI MCP commands
4
+ */
5
+
6
+ export async function mcpServeHandler(options: { debug?: boolean; verbose?: boolean }): Promise<void> {
7
+ console.error('MCP serve handler not implemented');
8
+ process.exit(1);
9
+ }
src/cli/handlers/plugins.ts ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Plugin command handlers
3
+ * These handlers are used by the CLI plugin commands
4
+ */
5
+
6
+ export async function pluginInstallHandler(plugin: string, options: any): Promise<void> {
7
+ console.error('Plugin install handler not implemented');
8
+ process.exit(1);
9
+ }
10
+
11
+ export async function pluginUninstallHandler(plugin: string, options: any): Promise<void> {
12
+ console.error('Plugin uninstall handler not implemented');
13
+ process.exit(1);
14
+ }
15
+
16
+ export async function pluginEnableHandler(plugin: string, options: any): Promise<void> {
17
+ console.error('Plugin enable handler not implemented');
18
+ process.exit(1);
19
+ }
20
+
21
+ export async function pluginDisableHandler(plugin: string | undefined, options: any): Promise<void> {
22
+ console.error('Plugin disable handler not implemented');
23
+ process.exit(1);
24
+ }
25
+
26
+ export async function pluginUpdateHandler(plugin: string, options: any): Promise<void> {
27
+ console.error('Plugin update handler not implemented');
28
+ process.exit(1);
29
+ }
30
+
31
+ export async function pluginValidateHandler(manifestPath: string, options: any): Promise<void> {
32
+ console.error('Plugin validate handler not implemented');
33
+ process.exit(1);
34
+ }
35
+
36
+ export async function pluginListHandler(options: any): Promise<void> {
37
+ console.error('Plugin list handler not implemented');
38
+ process.exit(1);
39
+ }
40
+
41
+ export async function marketplaceAddHandler(source: string, options: any): Promise<void> {
42
+ console.error('Marketplace add handler not implemented');
43
+ process.exit(1);
44
+ }
45
+
46
+ export async function marketplaceListHandler(options: any): Promise<void> {
47
+ console.error('Marketplace list handler not implemented');
48
+ process.exit(1);
49
+ }
50
+
51
+ export async function marketplaceRemoveHandler(name: string, options: any): Promise<void> {
52
+ console.error('Marketplace remove handler not implemented');
53
+ process.exit(1);
54
+ }
55
+
56
+ export async function marketplaceUpdateHandler(name: string | undefined, options: any): Promise<void> {
57
+ console.error('Marketplace update handler not implemented');
58
+ process.exit(1);
59
+ }
src/cli/handlers/templateJobs.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // @generated stub from scan-missing-imports
2
+ // 该文件自动生成,对应 ant-internal 的 feature() gated 模块。
3
+ // 所有外部 build 的代码路径在 DCE 后都不会真的执行这里的代码,这只是
4
+ // bun build resolver 的占位符。
5
+ const __target = function noop() {}
6
+ const __handler: ProxyHandler<any> = {
7
+ get(_t, prop) {
8
+ if (prop === '__esModule') return true
9
+ if (prop === 'default') return new Proxy(__target, __handler)
10
+ if (prop === Symbol.toPrimitive) return () => undefined
11
+ if (prop === Symbol.iterator) return function* () {}
12
+ if (prop === Symbol.asyncIterator) return async function* () {}
13
+ if (prop === 'then') return undefined
14
+ return new Proxy(__target, __handler)
15
+ },
16
+ apply() {
17
+ return new Proxy(__target, __handler)
18
+ },
19
+ construct() {
20
+ return new Proxy(__target, __handler)
21
+ },
22
+ }
23
+ const stub: any = new Proxy(__target, __handler)
24
+ export default stub
25
+ export const __stubMissing = true
26
+ // 兼容常见的命名导出 —— 没列在这里的也会通过 default Proxy 兜底
27
+ export const createCachedMCState = stub
28
+ export const isCachedMicrocompactEnabled = stub
29
+ export const isModelSupportedForCacheEditing = stub
30
+ export const getCachedMCConfig = stub
31
+ export const markToolsSentToAPI = stub
32
+ export const resetCachedMCState = stub
33
+ export const checkProtectedNamespace = stub
34
+ export const getCoordinatorUserContext = stub
src/cli/handlers/util.ts ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Utility handlers
3
+ * These handlers are used by various CLI commands
4
+ */
5
+
6
+ import type { Root } from '../../ink.js';
7
+
8
+ export async function setupTokenHandler(root: Root): Promise<void> {
9
+ console.error('Setup token handler not implemented');
10
+ process.exit(1);
11
+ }
src/cli/print.ts ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Print utility functions — headless / SDK bridge
3
+ *
4
+ * The desktop server spawns the CLI subprocess with --print --sdk-url and
5
+ * expects it to connect back via a WebSocket. This file exports:
6
+ * - runHeadless() — SDK WebSocket ↔ QueryEngine bridge
7
+ * - externalMetadataToAppState() — stub for headless metadata
8
+ */
9
+
10
+ import { getCwd } from '../utils/cwd.js'
11
+ import { createAbortController } from '../utils/abortController.js'
12
+ import { cloneFileStateCache } from '../utils/fileStateCache.js'
13
+
14
+ export function externalMetadataToAppState(metadata: any): any {
15
+ console.error('externalMetadataToAppState not implemented');
16
+ return {};
17
+ }
18
+
19
+ // ── SDK protocol helpers ──────────────────────────────────────────────
20
+
21
+ function extractUserText(msg: any): string {
22
+ const content = msg?.message?.content
23
+ if (!Array.isArray(content)) return ''
24
+ return content
25
+ .filter((block: any) => block?.type === 'text' && typeof block.text === 'string')
26
+ .map((block: any) => block.text)
27
+ .join(' ')
28
+ }
29
+
30
+ function sendWs(ws: WebSocket, payload: Record<string, unknown>) {
31
+ if (ws.readyState === WebSocket.OPEN) {
32
+ ws.send(JSON.stringify(payload) + '\n')
33
+ }
34
+ }
35
+
36
+ // ── runHeadless — SDK mode entry point ────────────────────────────────
37
+
38
+ export async function runHeadless(
39
+ _inputPrompt: unknown,
40
+ getState: () => any,
41
+ setState: (fn: (state: any) => any) => void,
42
+ commandsHeadless: readonly any[],
43
+ tools: readonly any[],
44
+ _sdkMcpConfigs: unknown,
45
+ activeAgents: readonly any[],
46
+ options: {
47
+ verbose?: boolean
48
+ outputFormat?: string
49
+ jsonSchema?: Record<string, unknown>
50
+ allowedTools?: string[]
51
+ thinkingConfig?: { type: string; [key: string]: unknown }
52
+ maxTurns?: number
53
+ maxBudgetUsd?: number
54
+ taskBudget?: { total: number }
55
+ systemPrompt?: string
56
+ appendSystemPrompt?: string
57
+ userSpecifiedModel?: string
58
+ fallbackModel?: string
59
+ sdkUrl?: string
60
+ replayUserMessages?: boolean
61
+ includePartialMessages?: boolean
62
+ enableAuthStatus?: boolean
63
+ agent?: unknown
64
+ },
65
+ ): Promise<void> {
66
+ const sdkUrl = options.sdkUrl
67
+ if (!sdkUrl) {
68
+ console.error('[runHeadless] Non-SDK headless mode is not implemented')
69
+ return
70
+ }
71
+
72
+ console.error(`[runHeadless] Connecting SDK WebSocket …`)
73
+
74
+ const ws = new WebSocket(sdkUrl)
75
+ await new Promise<void>((resolve, reject) => {
76
+ ws.addEventListener('open', () => {
77
+ console.error('[runHeadless] SDK WebSocket connected')
78
+ resolve()
79
+ })
80
+ ws.addEventListener('error', (err) => {
81
+ console.error('[runHeadless] SDK WebSocket error:', err)
82
+ reject(err)
83
+ })
84
+ })
85
+
86
+ const abortController = createAbortController()
87
+
88
+ // QueryEngine is lazy-imported so the first invocation bears the cost.
89
+ const { QueryEngine } = await import('../QueryEngine.js')
90
+
91
+ let engine: InstanceType<typeof QueryEngine> | null = null
92
+ let processing = false
93
+ const pendingMessages: any[] = []
94
+
95
+ async function processNextMessage(msg: any): Promise<void> {
96
+ const text = extractUserText(msg)
97
+ if (!text.trim()) return
98
+
99
+ const state = getState()
100
+
101
+ if (!engine) {
102
+ const mcpClients = state?.mcp?.clients ?? []
103
+ engine = new QueryEngine({
104
+ cwd: getCwd(),
105
+ tools: tools as any,
106
+ commands: commandsHeadless as any,
107
+ mcpClients,
108
+ agents: activeAgents as any,
109
+ /** In SDK mode tool permission decisions are owned by the host
110
+ * (desktop server / remote client). The CLI always allows;
111
+ * the host gates execution on its side. */
112
+ canUseTool: async () => ({ behavior: 'allow' as const }),
113
+ getAppState: getState,
114
+ setAppState: setState,
115
+ initialMessages: [],
116
+ readFileCache: state?.files ? cloneFileStateCache(state.files) : cloneFileStateCache(undefined),
117
+ verbose: !!options.verbose,
118
+ thinkingConfig: options.thinkingConfig as any,
119
+ maxTurns: options.maxTurns,
120
+ maxBudgetUsd: options.maxBudgetUsd,
121
+ taskBudget: options.taskBudget,
122
+ jsonSchema: options.jsonSchema,
123
+ replayUserMessages: !!options.replayUserMessages,
124
+ includePartialMessages: !!options.includePartialMessages,
125
+ userSpecifiedModel: options.userSpecifiedModel || undefined,
126
+ fallbackModel: options.fallbackModel || undefined,
127
+ setSDKStatus: (_status) => { /* no-op for now */ },
128
+ abortController,
129
+ })
130
+ }
131
+
132
+ try {
133
+ for await (const sdkMsg of engine.submitMessage(text)) {
134
+ sendWs(ws, sdkMsg as Record<string, unknown>)
135
+ }
136
+ } catch (err) {
137
+ console.error('[runHeadless] submitMessage error:', err)
138
+ sendWs(ws, {
139
+ type: 'result',
140
+ subtype: 'error',
141
+ is_error: true,
142
+ result: err instanceof Error ? err.message : String(err),
143
+ usage: { input_tokens: 0, output_tokens: 0 },
144
+ })
145
+ }
146
+
147
+ // Create a fresh engine for the next user turn.
148
+ engine = null
149
+ }
150
+
151
+ async function processQueue(): Promise<void> {
152
+ if (processing || pendingMessages.length === 0) return
153
+ processing = true
154
+ try {
155
+ while (pendingMessages.length > 0) {
156
+ const msg = pendingMessages.shift()!
157
+ await processNextMessage(msg)
158
+ }
159
+ } finally {
160
+ processing = false
161
+ }
162
+ }
163
+
164
+ ws.addEventListener('message', (event) => {
165
+ const payload = typeof event.data === 'string' ? event.data : String(event.data)
166
+ const lines = payload.split('\n').map((l) => l.trim()).filter(Boolean)
167
+
168
+ for (const line of lines) {
169
+ try {
170
+ const msg = JSON.parse(line)
171
+ if (msg.type === 'user') {
172
+ pendingMessages.push(msg)
173
+ void processQueue()
174
+ } else if (msg.type === 'control_request') {
175
+ handleControlRequest(msg, ws, setState, abortController)
176
+ }
177
+ } catch (err) {
178
+ console.error('[runHeadless] Invalid JSON line:', err)
179
+ }
180
+ }
181
+ })
182
+
183
+ // Wait for the WebSocket to close (SDK session ended).
184
+ await new Promise<void>((resolve) => {
185
+ ws.addEventListener('close', () => {
186
+ console.error('[runHeadless] SDK WebSocket closed')
187
+ resolve()
188
+ })
189
+ })
190
+ }
191
+
192
+ // ── Control request handling ──────────────────────────────────────────
193
+
194
+ function handleControlRequest(
195
+ msg: any,
196
+ ws: WebSocket,
197
+ setState: (fn: (state: any) => any) => void,
198
+ abortController: AbortController,
199
+ ): void {
200
+ const { request_id, request } = msg
201
+ if (!request_id) return
202
+
203
+ switch (request?.subtype) {
204
+ case 'interrupt': {
205
+ abortController.abort()
206
+ sendWs(ws, {
207
+ type: 'result',
208
+ subtype: 'success',
209
+ is_error: false,
210
+ result: 'Interrupted',
211
+ usage: { input_tokens: 0, output_tokens: 0 },
212
+ session_id: msg.session_id ?? '',
213
+ })
214
+ break
215
+ }
216
+
217
+ case 'set_permission_mode': {
218
+ setState((prev: any) => ({
219
+ ...prev,
220
+ toolPermissionContext: {
221
+ ...prev.toolPermissionContext,
222
+ mode: request.mode ?? prev.toolPermissionContext?.mode ?? 'default',
223
+ },
224
+ }))
225
+ sendWs(ws, {
226
+ type: 'control_response',
227
+ response: { subtype: 'success', request_id, response: {} },
228
+ session_id: msg.session_id ?? '',
229
+ })
230
+ break
231
+ }
232
+
233
+ case 'set_max_thinking_tokens': {
234
+ sendWs(ws, {
235
+ type: 'control_response',
236
+ response: { subtype: 'success', request_id, response: {} },
237
+ session_id: msg.session_id ?? '',
238
+ })
239
+ break
240
+ }
241
+
242
+ default: {
243
+ sendWs(ws, {
244
+ type: 'control_response',
245
+ response: {
246
+ subtype: 'error',
247
+ request_id,
248
+ error: `Unknown control request: ${request?.subtype ?? '<unknown>'}`,
249
+ },
250
+ session_id: msg.session_id ?? '',
251
+ })
252
+ break
253
+ }
254
+ }
255
+ }
src/cli/structuredIO.ts ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ /**
2
+ * Structured I/O constants
3
+ * These constants are used for structured input/output operations
4
+ */
5
+
6
+ export const SANDBOX_NETWORK_ACCESS_TOOL_NAME = 'sandbox_network_access';
src/cli/transports/HybridTransport.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Hybrid Transport
3
+ * This transport combines multiple transport mechanisms for hybrid communication
4
+ */
5
+
6
+ export class HybridTransport {
7
+ constructor() {
8
+ console.error('HybridTransport not implemented');
9
+ }
10
+
11
+ async connect(): Promise<void> {
12
+ console.error('HybridTransport.connect not implemented');
13
+ }
14
+
15
+ async disconnect(): Promise<void> {
16
+ console.error('HybridTransport.disconnect not implemented');
17
+ }
18
+
19
+ async send(data: any): Promise<void> {
20
+ console.error('HybridTransport.send not implemented');
21
+ }
22
+
23
+ async receive(): Promise<any> {
24
+ console.error('HybridTransport.receive not implemented');
25
+ return null;
26
+ }
27
+ }
src/cli/transports/SSETransport.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * SSE Transport
3
+ * This transport uses Server-Sent Events for communication
4
+ */
5
+
6
+ export class SSETransport {
7
+ constructor() {
8
+ console.error('SSETransport not implemented');
9
+ }
10
+
11
+ async connect(): Promise<void> {
12
+ console.error('SSETransport.connect not implemented');
13
+ }
14
+
15
+ async disconnect(): Promise<void> {
16
+ console.error('SSETransport.disconnect not implemented');
17
+ }
18
+
19
+ async send(data: any): Promise<void> {
20
+ console.error('SSETransport.send not implemented');
21
+ }
22
+
23
+ async receive(): Promise<any> {
24
+ console.error('SSETransport.receive not implemented');
25
+ return null;
26
+ }
27
+ }
src/cli/transports/ccrClient.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * CCR Client
3
+ * This client is used for CCR (Claude Code Remote) communication
4
+ */
5
+
6
+ export class CCRClient {
7
+ constructor() {
8
+ console.error('CCRClient not implemented');
9
+ }
10
+
11
+ async connect(): Promise<void> {
12
+ console.error('CCRClient.connect not implemented');
13
+ }
14
+
15
+ async disconnect(): Promise<void> {
16
+ console.error('CCRClient.disconnect not implemented');
17
+ }
18
+
19
+ async send(data: any): Promise<void> {
20
+ console.error('CCRClient.send not implemented');
21
+ }
22
+
23
+ async receive(): Promise<any> {
24
+ console.error('CCRClient.receive not implemented');
25
+ return null;
26
+ }
27
+ }
src/cli/update.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Update command handler
3
+ * This handler is used by the CLI update command
4
+ */
5
+
6
+ export async function update(): Promise<void> {
7
+ console.error('Update handler not implemented');
8
+ process.exit(1);
9
+ }
test_feishu_ws.ts DELETED
@@ -1,44 +0,0 @@
1
- import * as Lark from '@larksuiteoapi/node-sdk'
2
-
3
- const appId = 'cli_aaa6659174b85bec'
4
- const appSecret = 'nMV7WvGm9Pbvtf0PA3RVLbqNujzeqP7I'
5
-
6
- const client = new Lark.Client({
7
- appId,
8
- appSecret,
9
- appType: Lark.AppType.SelfBuild,
10
- domain: Lark.Domain.Feishu,
11
- })
12
-
13
- // Test bot info
14
- try {
15
- const info = await client.request({
16
- method: 'GET',
17
- url: '/open-apis/bot/v3/info',
18
- })
19
- console.log('Bot info:', JSON.stringify(info, null, 2))
20
- } catch (e) {
21
- console.error('Bot info failed:', e)
22
- }
23
-
24
- // Test WSClient
25
- const dispatcher = new Lark.EventDispatcher({})
26
-
27
- dispatcher.register({
28
- 'im.message.receive_v1': async (data: any) => {
29
- console.log('Received message:', JSON.stringify(data, null, 2))
30
- },
31
- } as any)
32
-
33
- const ws = new Lark.WSClient({
34
- appId,
35
- appSecret,
36
- domain: Lark.Domain.Feishu,
37
- loggerLevel: Lark.LoggerLevel.debug,
38
- })
39
-
40
- await ws.start({ eventDispatcher: dispatcher })
41
- console.log('[WS] started, waiting 10s...')
42
- await new Promise(r => setTimeout(r, 10000))
43
- console.log('[WS] done')
44
- ws.stop?.()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/audio-capture-src/index.ts DELETED
@@ -1,147 +0,0 @@
1
- type AudioCaptureNapi = {
2
- startRecording(onData: (data: Buffer) => void, onEnd: () => void): boolean
3
- stopRecording(): void
4
- isRecording(): boolean
5
- startPlayback(sampleRate: number, channels: number): boolean
6
- writePlaybackData(data: Buffer): void
7
- stopPlayback(): void
8
- isPlaying(): boolean
9
- // TCC microphone authorization status (macOS only):
10
- // 0 = notDetermined, 1 = restricted, 2 = denied, 3 = authorized.
11
- // Linux: always returns 3 (authorized) — no system-level microphone permission API.
12
- // Windows: returns 3 (authorized) if registry key absent or allowed,
13
- // 2 (denied) if microphone access is explicitly denied.
14
- microphoneAuthorizationStatus?(): number
15
- }
16
-
17
- let cachedModule: AudioCaptureNapi | null = null
18
- let loadAttempted = false
19
-
20
- function loadModule(): AudioCaptureNapi | null {
21
- if (loadAttempted) {
22
- return cachedModule
23
- }
24
- loadAttempted = true
25
-
26
- // Supported platforms: macOS (darwin), Linux, Windows (win32)
27
- const platform = process.platform
28
- if (platform !== 'darwin' && platform !== 'linux' && platform !== 'win32') {
29
- return null
30
- }
31
-
32
- // Candidate 1: native-embed path (bun compile). AUDIO_CAPTURE_NODE_PATH is
33
- // defined at build time in build-with-plugins.ts for native builds only — the
34
- // define resolves it to the static literal "../../audio-capture.node" so bun
35
- // compile can rewrite it to /$bunfs/root/audio-capture.node. MUST stay a
36
- // direct require(env var) — bun cannot analyze require(variable) from a loop.
37
- if (process.env.AUDIO_CAPTURE_NODE_PATH) {
38
- try {
39
- // eslint-disable-next-line @typescript-eslint/no-require-imports
40
- cachedModule = require(
41
- process.env.AUDIO_CAPTURE_NODE_PATH,
42
- ) as AudioCaptureNapi
43
- return cachedModule
44
- } catch {
45
- // fall through to runtime fallbacks below
46
- }
47
- }
48
-
49
- // Candidates 2/3: npm-install and dev/source layouts. Dynamic require is
50
- // fine here — in bundled output (node --target build) require() resolves at
51
- // runtime relative to cli.js at the package root; in dev it resolves
52
- // relative to this file (vendor/audio-capture-src/index.ts).
53
- const platformDir = `${process.arch}-${platform}`
54
- const fallbacks = [
55
- `./vendor/audio-capture/${platformDir}/audio-capture.node`,
56
- `../audio-capture/${platformDir}/audio-capture.node`,
57
- ]
58
- for (const p of fallbacks) {
59
- try {
60
- // eslint-disable-next-line @typescript-eslint/no-require-imports
61
- cachedModule = require(p) as AudioCaptureNapi
62
- return cachedModule
63
- } catch {
64
- // try next
65
- }
66
- }
67
- return null
68
- }
69
-
70
- export function isNativeAudioAvailable(): boolean {
71
- return loadModule() !== null
72
- }
73
-
74
- export function startNativeRecording(
75
- onData: (data: Buffer) => void,
76
- onEnd: () => void,
77
- ): boolean {
78
- const mod = loadModule()
79
- if (!mod) {
80
- return false
81
- }
82
- return mod.startRecording(onData, onEnd)
83
- }
84
-
85
- export function stopNativeRecording(): void {
86
- const mod = loadModule()
87
- if (!mod) {
88
- return
89
- }
90
- mod.stopRecording()
91
- }
92
-
93
- export function isNativeRecordingActive(): boolean {
94
- const mod = loadModule()
95
- if (!mod) {
96
- return false
97
- }
98
- return mod.isRecording()
99
- }
100
-
101
- export function startNativePlayback(
102
- sampleRate: number,
103
- channels: number,
104
- ): boolean {
105
- const mod = loadModule()
106
- if (!mod) {
107
- return false
108
- }
109
- return mod.startPlayback(sampleRate, channels)
110
- }
111
-
112
- export function writeNativePlaybackData(data: Buffer): void {
113
- const mod = loadModule()
114
- if (!mod) {
115
- return
116
- }
117
- mod.writePlaybackData(data)
118
- }
119
-
120
- export function stopNativePlayback(): void {
121
- const mod = loadModule()
122
- if (!mod) {
123
- return
124
- }
125
- mod.stopPlayback()
126
- }
127
-
128
- export function isNativePlaying(): boolean {
129
- const mod = loadModule()
130
- if (!mod) {
131
- return false
132
- }
133
- return mod.isPlaying()
134
- }
135
-
136
- // Returns the microphone authorization status.
137
- // On macOS, returns the TCC status: 0=notDetermined, 1=restricted, 2=denied, 3=authorized.
138
- // On Linux, always returns 3 (authorized) — no system-level mic permission API.
139
- // On Windows, returns 3 (authorized) if registry key absent or allowed, 2 (denied) if explicitly denied.
140
- // Returns 0 (notDetermined) if the native module is unavailable.
141
- export function microphoneAuthorizationStatus(): number {
142
- const mod = loadModule()
143
- if (!mod || !mod.microphoneAuthorizationStatus) {
144
- return 0
145
- }
146
- return mod.microphoneAuthorizationStatus()
147
- }