@caoruhua/open-claude-remote 0.1.7 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +514 -11
  2. package/dist/backend/src/api/config-routes.d.ts +2 -2
  3. package/dist/backend/src/api/config-routes.d.ts.map +1 -1
  4. package/dist/backend/src/api/config-routes.js +21 -6
  5. package/dist/backend/src/api/config-routes.js.map +1 -1
  6. package/dist/backend/src/api/health-routes.d.ts +4 -1
  7. package/dist/backend/src/api/health-routes.d.ts.map +1 -1
  8. package/dist/backend/src/api/health-routes.js +20 -2
  9. package/dist/backend/src/api/health-routes.js.map +1 -1
  10. package/dist/backend/src/api/hook-routes.d.ts +2 -2
  11. package/dist/backend/src/api/hook-routes.d.ts.map +1 -1
  12. package/dist/backend/src/api/hook-routes.js +10 -3
  13. package/dist/backend/src/api/hook-routes.js.map +1 -1
  14. package/dist/backend/src/api/instance-routes.d.ts +5 -3
  15. package/dist/backend/src/api/instance-routes.d.ts.map +1 -1
  16. package/dist/backend/src/api/instance-routes.js +48 -45
  17. package/dist/backend/src/api/instance-routes.js.map +1 -1
  18. package/dist/backend/src/api/router.d.ts +4 -12
  19. package/dist/backend/src/api/router.d.ts.map +1 -1
  20. package/dist/backend/src/api/router.js +28 -20
  21. package/dist/backend/src/api/router.js.map +1 -1
  22. package/dist/backend/src/api/status-routes.d.ts +2 -2
  23. package/dist/backend/src/api/status-routes.d.ts.map +1 -1
  24. package/dist/backend/src/api/status-routes.js +11 -6
  25. package/dist/backend/src/api/status-routes.js.map +1 -1
  26. package/dist/backend/src/attach.d.ts +1 -1
  27. package/dist/backend/src/attach.d.ts.map +1 -1
  28. package/dist/backend/src/attach.js +25 -71
  29. package/dist/backend/src/attach.js.map +1 -1
  30. package/dist/backend/src/cli-utils.d.ts +24 -1
  31. package/dist/backend/src/cli-utils.d.ts.map +1 -1
  32. package/dist/backend/src/cli-utils.js +73 -37
  33. package/dist/backend/src/cli-utils.js.map +1 -1
  34. package/dist/backend/src/cli.d.ts +22 -2
  35. package/dist/backend/src/cli.d.ts.map +1 -1
  36. package/dist/backend/src/cli.js +328 -23
  37. package/dist/backend/src/cli.js.map +1 -1
  38. package/dist/backend/src/config.d.ts +48 -15
  39. package/dist/backend/src/config.d.ts.map +1 -1
  40. package/dist/backend/src/config.js +124 -24
  41. package/dist/backend/src/config.js.map +1 -1
  42. package/dist/backend/src/daemon/daemon-client.d.ts +69 -0
  43. package/dist/backend/src/daemon/daemon-client.d.ts.map +1 -0
  44. package/dist/backend/src/daemon/daemon-client.js +209 -0
  45. package/dist/backend/src/daemon/daemon-client.js.map +1 -0
  46. package/dist/backend/src/daemon/daemon-entry.d.ts +8 -0
  47. package/dist/backend/src/daemon/daemon-entry.d.ts.map +1 -0
  48. package/dist/backend/src/daemon/daemon-entry.js +37 -0
  49. package/dist/backend/src/daemon/daemon-entry.js.map +1 -0
  50. package/dist/backend/src/daemon/daemon-launcher.d.ts +13 -0
  51. package/dist/backend/src/daemon/daemon-launcher.d.ts.map +1 -0
  52. package/dist/backend/src/daemon/daemon-launcher.js +62 -0
  53. package/dist/backend/src/daemon/daemon-launcher.js.map +1 -0
  54. package/dist/backend/src/index.d.ts.map +1 -1
  55. package/dist/backend/src/index.js +115 -251
  56. package/dist/backend/src/index.js.map +1 -1
  57. package/dist/backend/src/instance/index.d.ts +4 -0
  58. package/dist/backend/src/instance/index.d.ts.map +1 -0
  59. package/dist/backend/src/instance/index.js +3 -0
  60. package/dist/backend/src/instance/index.js.map +1 -0
  61. package/dist/backend/src/instance/instance-manager.d.ts +62 -0
  62. package/dist/backend/src/instance/instance-manager.d.ts.map +1 -0
  63. package/dist/backend/src/instance/instance-manager.js +194 -0
  64. package/dist/backend/src/instance/instance-manager.js.map +1 -0
  65. package/dist/backend/src/instance/instance-session.d.ts +87 -0
  66. package/dist/backend/src/instance/instance-session.d.ts.map +1 -0
  67. package/dist/backend/src/instance/instance-session.js +359 -0
  68. package/dist/backend/src/instance/instance-session.js.map +1 -0
  69. package/dist/backend/src/instance/types.d.ts +39 -0
  70. package/dist/backend/src/instance/types.d.ts.map +1 -0
  71. package/dist/backend/src/instance/types.js +2 -0
  72. package/dist/backend/src/instance/types.js.map +1 -0
  73. package/dist/backend/src/notification/notification-manager.js +1 -1
  74. package/dist/backend/src/notification/notification-manager.js.map +1 -1
  75. package/dist/backend/src/notification/notification-service-factory.js +1 -1
  76. package/dist/backend/src/notification/notification-service-factory.js.map +1 -1
  77. package/dist/backend/src/pty/virtual-pty.d.ts.map +1 -1
  78. package/dist/backend/src/pty/virtual-pty.js +6 -0
  79. package/dist/backend/src/pty/virtual-pty.js.map +1 -1
  80. package/dist/backend/src/registry/shared-token.d.ts +2 -2
  81. package/dist/backend/src/registry/shared-token.js +11 -11
  82. package/dist/backend/src/registry/shared-token.js.map +1 -1
  83. package/dist/backend/src/registry/stop-instances.d.ts +0 -25
  84. package/dist/backend/src/registry/stop-instances.d.ts.map +1 -1
  85. package/dist/backend/src/registry/stop-instances.js +6 -206
  86. package/dist/backend/src/registry/stop-instances.js.map +1 -1
  87. package/dist/backend/src/skills/index.d.ts +4 -0
  88. package/dist/backend/src/skills/index.d.ts.map +1 -0
  89. package/dist/backend/src/skills/index.js +4 -0
  90. package/dist/backend/src/skills/index.js.map +1 -0
  91. package/dist/backend/src/skills/skill-command-merger.d.ts +32 -0
  92. package/dist/backend/src/skills/skill-command-merger.d.ts.map +1 -0
  93. package/dist/backend/src/skills/skill-command-merger.js +78 -0
  94. package/dist/backend/src/skills/skill-command-merger.js.map +1 -0
  95. package/dist/backend/src/skills/skill-commands.d.ts +25 -0
  96. package/dist/backend/src/skills/skill-commands.d.ts.map +1 -0
  97. package/dist/backend/src/skills/skill-commands.js +56 -0
  98. package/dist/backend/src/skills/skill-commands.js.map +1 -0
  99. package/dist/backend/src/skills/skill-scanner.d.ts +36 -0
  100. package/dist/backend/src/skills/skill-scanner.d.ts.map +1 -0
  101. package/dist/backend/src/skills/skill-scanner.js +143 -0
  102. package/dist/backend/src/skills/skill-scanner.js.map +1 -0
  103. package/dist/backend/src/terminal/terminal-relay.d.ts +1 -0
  104. package/dist/backend/src/terminal/terminal-relay.d.ts.map +1 -1
  105. package/dist/backend/src/terminal/terminal-relay.js +6 -0
  106. package/dist/backend/src/terminal/terminal-relay.js.map +1 -1
  107. package/dist/backend/src/update.d.ts.map +1 -1
  108. package/dist/backend/src/update.js +46 -1
  109. package/dist/backend/src/update.js.map +1 -1
  110. package/dist/backend/src/utils/banner.d.ts +15 -0
  111. package/dist/backend/src/utils/banner.d.ts.map +1 -0
  112. package/dist/backend/src/utils/banner.js +95 -0
  113. package/dist/backend/src/utils/banner.js.map +1 -0
  114. package/dist/backend/src/ws/ws-server.d.ts +13 -54
  115. package/dist/backend/src/ws/ws-server.d.ts.map +1 -1
  116. package/dist/backend/src/ws/ws-server.js +57 -155
  117. package/dist/backend/src/ws/ws-server.js.map +1 -1
  118. package/dist/shared/constants.d.ts +2 -1
  119. package/dist/shared/constants.d.ts.map +1 -1
  120. package/dist/shared/constants.js +2 -1
  121. package/dist/shared/constants.js.map +1 -1
  122. package/dist/shared/defaults.d.ts.map +1 -1
  123. package/dist/shared/defaults.js +1 -5
  124. package/dist/shared/defaults.js.map +1 -1
  125. package/dist/shared/instance.d.ts +4 -9
  126. package/dist/shared/instance.d.ts.map +1 -1
  127. package/dist/shared/instance.js +0 -2
  128. package/dist/shared/instance.js.map +1 -1
  129. package/frontend-dist/assets/index-CM2xfmS8.js +152 -0
  130. package/frontend-dist/index.html +1 -1
  131. package/package.json +2 -2
  132. package/frontend-dist/assets/index-Cfhr3h3e.js +0 -152
package/README.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  **Control Claude Code from your mobile browser over LAN.**
4
4
 
5
+ [English](#english) | [中文](#中文)
6
+
7
+ ---
8
+
9
+ <a name="english"></a>
10
+ ## English
11
+
12
+ **Control Claude Code from your mobile browser over LAN.**
13
+
5
14
  View terminal output, send commands, and approve tool calls from your phone — keep working even when you step away from your desktop.
6
15
 
7
16
  ---
@@ -54,12 +63,14 @@ Scan the QR code shown in your terminal with your phone. The auth token is auto-
54
63
  - Auto-skips on subsequent visits
55
64
 
56
65
  ### Multi-Instance
57
- - Run multiple `claude-remote` instances simultaneously
66
+ - Run multiple `claude-remote` instances simultaneously in a single daemon process
67
+ - Fixed port 8866 — all instances share same origin, no cross-port auth needed
58
68
  - Browser tab bar for switching — no re-authentication needed
59
69
  - Auto-switch when an instance goes offline
60
70
  - Spawn new instances from the web UI ("+" button)
61
71
  - Copy instance via long-press/right-click tab — pre-fills working directory, settings, and arguments
62
- - `claude-remote attach <port|name>` to take over a web-spawned instance
72
+ - `claude-remote attach <name|id>` to take over a web-spawned instance
73
+ - `claude-remote stop` to stop the daemon and all instances
63
74
 
64
75
  ### Window Resize Priority
65
76
 
@@ -125,7 +136,7 @@ Get notified when Claude is waiting for input. All notifications include the ins
125
136
  ## Usage
126
137
 
127
138
  ```bash
128
- # Start Claude Code
139
+ # Start Claude Code (starts daemon + first instance)
129
140
  claude-remote
130
141
 
131
142
  # Pass arguments to Claude
@@ -133,13 +144,22 @@ claude-remote chat
133
144
  claude-remote -- --dangerously-skip-permissions
134
145
 
135
146
  # Custom options
136
- claude-remote --port 8080
137
147
  claude-remote --name my-project
138
148
  claude-remote --token my-secret-token
139
149
 
140
- # Attach to a web-spawned instance
141
- claude-remote attach 3001 # by port
150
+ # Attach to an existing instance
142
151
  claude-remote attach my-project # by name
152
+ claude-remote attach 550e8400 # by instance ID prefix
153
+
154
+ # List all running instances
155
+ claude-remote list
156
+
157
+ # Show daemon status (PID, port, uptime, instance count)
158
+ # Also checks daemon version and auto-restarts if mismatch
159
+ claude-remote status
160
+
161
+ # Stop the daemon and all instances
162
+ claude-remote stop
143
163
 
144
164
  # Headless mode (no local terminal, web UI only)
145
165
  claude-remote --no-terminal
@@ -151,6 +171,15 @@ claude-remote --version
151
171
  claude-remote update
152
172
  ```
153
173
 
174
+ ### Automatic Daemon Restart
175
+
176
+ The CLI checks daemon version on startup and when running `claude-remote status`:
177
+
178
+ - **Version mismatch** (CLI newer than daemon) → Auto-restarts daemon if no instances running
179
+ - **Running instances exist** → Shows warning to restart manually
180
+
181
+ After `claude-remote update`, the daemon is automatically restarted if no instances are running. If instances exist, you'll see a reminder to restart manually.
182
+
154
183
  ### Stopping
155
184
 
156
185
  - **Single Ctrl+C** — sent to Claude Code (cancels current task)
@@ -160,11 +189,10 @@ claude-remote update
160
189
 
161
190
  ## Configuration
162
191
 
163
- Config file: `~/.claude-remote/config.json`
192
+ Config file: `~/.claude-remote/settings.json` (legacy `config.json` is auto-migrated)
164
193
 
165
194
  | Option | Type | Default | Description |
166
195
  |--------|------|---------|-------------|
167
- | `port` | number | 3000 | Server port (auto-increments if busy) |
168
196
  | `host` | string | "0.0.0.0" | Bind address |
169
197
  | `token` | string \| null | null | Auth token; `null` = auto-generated shared token |
170
198
  | `claudeCommand` | string | "claude" | Claude CLI path |
@@ -180,6 +208,8 @@ Config file: `~/.claude-remote/config.json`
180
208
  | `settingsDirs` | string[] | ["~/.claude/", "~/.claude-remote/settings/"] | Directories to scan for settings files |
181
209
  | `notifications` | object | — | Notification channels config (see below) |
182
210
 
211
+ > **Note**: Port is fixed at 8866. All instances run in a single daemon process.
212
+
183
213
  **Notification channel config:**
184
214
 
185
215
  | Field | Type | Default | Description |
@@ -320,13 +350,12 @@ When creating instances from the web UI, you can select a custom settings file t
320
350
  }
321
351
  ```
322
352
 
323
- > **Note**: Files like `3000.json` (port configs) are automatically excluded from the settings file list.
353
+ > **Note**: Port-numbered JSON files are automatically excluded from the settings file list.
324
354
 
325
355
  ### Complete Example
326
356
 
327
357
  ```json
328
358
  {
329
- "port": 3000,
330
359
  "host": "0.0.0.0",
331
360
  "token": null,
332
361
 
@@ -396,7 +425,7 @@ Install build tools:
396
425
 
397
426
  ### Phone can't connect?
398
427
 
399
- 1. Check your PC firewall allows the port (default: 3000)
428
+ 1. Check your PC firewall allows port 8866
400
429
  2. Verify the URL shows the correct LAN IP
401
430
  3. If using a VPN, try setting the `host` option explicitly
402
431
 
@@ -447,3 +476,477 @@ pnpm stop
447
476
  ## License
448
477
 
449
478
  MIT
479
+
480
+ ---
481
+ ---
482
+
483
+ <a name="中文"></a>
484
+ ## 中文
485
+
486
+ **通过局域网在手机浏览器中控制 Claude Code。**
487
+
488
+ 在手机上查看终端输出、发送命令、审批工具调用 — 即使离开桌面也能继续工作。
489
+
490
+ ---
491
+
492
+ ## 快速开始
493
+
494
+ ### 1. 安装
495
+
496
+ ```bash
497
+ # npm
498
+ npm install -g @caoruhua/open-claude-remote
499
+
500
+ # pnpm(推荐)
501
+ pnpm add -g @caoruhua/open-claude-remote
502
+ ```
503
+
504
+ > **pnpm 用户注意**:pnpm v10 可能显示 "Ignored build scripts: node-pty" 警告。这是正常的,**不影响功能** — 包含预编译的二进制文件,开箱即用。要消除警告,运行 `pnpm approve-builds -g`(可选)。
505
+
506
+ ### 2. 运行
507
+
508
+ ```bash
509
+ claude-remote
510
+ ```
511
+
512
+ ### 3. 连接
513
+
514
+ 用手机扫描终端显示的二维码。认证令牌自动填充 — 就可以开始使用了。
515
+
516
+ > 首次运行时,`claude-remote` 会自动检查并安装缺失的依赖(pnpm、Claude CLI)。Hook 也会自动配置 — 无需手动设置。
517
+
518
+ ---
519
+
520
+ ## 功能特性
521
+
522
+ ### 终端同步
523
+ - 实时终端输出推送到手机
524
+ - 通过 xterm.js 完整渲染 ANSI 颜色
525
+ - 1万行滚动缓冲区,重连时自动恢复
526
+ - 局域网 IP 变更通知,附带新连接地址
527
+ - 智能自动滚动,配有"滚动到底部"悬浮按钮
528
+
529
+ ### 快捷操作
530
+ - 一键发送:Esc、Enter、Tab、方向键、Shift+Tab
531
+ - 自定义快捷键(设置中配置)
532
+ - 预设命令(/clear、/compact、/resume 等)
533
+
534
+ ### 新手引导
535
+ - 首次访问时显示交互式高亮引导
536
+ - 聚焦关键 UI 元素并显示提示标记
537
+ - 后续访问自动跳过
538
+
539
+ ### 多实例支持
540
+ - 在单个守护进程中同时运行多个 `claude-remote` 实例
541
+ - 固定端口 8866 — 所有实例共享同源,无需跨端口认证
542
+ - 浏览器标签栏切换 — 无需重新认证
543
+ - 实例离线时自动切换
544
+ - 从 Web UI 生成新实例("+" 按钮)
545
+ - 长按/右键标签复制实例 — 预填充工作目录、设置和参数
546
+ - `claude-remote attach <名称|ID>` 接管 Web 生成的实例
547
+ - `claude-remote stop` 停止守护进程和所有实例
548
+
549
+ ### 窗口大小优先级
550
+
551
+ 当多个客户端连接时,窗口大小由以下优先级控制:
552
+ **WebApp(手机)> Attach(PC)> PC 终端**
553
+
554
+ | 场景 | 控制方 | 行为 |
555
+ |------|--------|------|
556
+ | 无客户端连接 | PC 终端 | 正常本地使用 |
557
+ | 仅 WebApp | WebApp | 手机控制窗口大小 |
558
+ | 仅 Attach | Attach | PC attach 控制大小 |
559
+ | WebApp + Attach | WebApp | 手机优先,PC 跟随 |
560
+
561
+ - WebApp 在线时,Attach 的调整大小请求被忽略
562
+ - Attach 连接时自动同步 WebApp 的窗口大小
563
+
564
+ ### 通知
565
+
566
+ 当 Claude 等待输入时收到通知。所有通知都包含实例 URL,即使 IP 变更也能快速重连。
567
+
568
+ **Web 推送** — 即使浏览器在后台或屏幕锁定也能工作。只需在提示时允许通知即可。
569
+
570
+ **渠道开关** — 每个通知渠道在设置中都有开关。切换即可启用/禁用,无需删除配置。更改立即生效于所有运行中的实例。
571
+
572
+ **钉钉** — 配置钉钉机器人 webhook,在团队群中接收通知:
573
+
574
+ ```json
575
+ {
576
+ "notifications": {
577
+ "dingtalk": {
578
+ "webhookUrl": "https://oapi.dingtalk.com/robot/send?access_token=your-token"
579
+ }
580
+ }
581
+ }
582
+ ```
583
+
584
+ > **注意**:旧版配置文件中的根级 `dingtalk` 字段会在首次加载时自动迁移到 `notifications.dingtalk`。
585
+
586
+ **设置步骤:**
587
+ 1. 打开钉钉群 → 群设置 → 智能群助手 → 添加机器人 → 自定义
588
+ 2. 安全设置选择"自定义关键词",添加 `Claude`(消息标题包含此关键词)
589
+ 3. 将 Webhook URL 复制到配置文件或粘贴到 Web UI 设置中
590
+
591
+ **微信(Server酱³)** — 配置 Server酱³ 在微信中接收通知:
592
+
593
+ ```json
594
+ {
595
+ "notifications": {
596
+ "wechat_work": {
597
+ "sendKey": "SCTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
598
+ }
599
+ }
600
+ }
601
+ ```
602
+
603
+ **设置步骤:**
604
+ 1. 访问 [Server酱 Sendkey](https://sct.ftqq.com/sendkey) 并用微信登录
605
+ 2. 复制你的 SendKey(以 `SCT` 开头)
606
+ 3. 粘贴到配置文件或 Web UI 设置中
607
+
608
+ ---
609
+
610
+ ## 使用方法
611
+
612
+ ```bash
613
+ # 启动 Claude Code(启动守护进程 + 第一个实例)
614
+ claude-remote
615
+
616
+ # 向 Claude 传递参数
617
+ claude-remote chat
618
+ claude-remote -- --dangerously-skip-permissions
619
+
620
+ # 自定义选项
621
+ claude-remote --name my-project
622
+ claude-remote --token my-secret-token
623
+
624
+ # 附加到现有实例
625
+ claude-remote attach my-project # 按名称
626
+ claude-remote attach 550e8400 # 按实例 ID 前缀
627
+
628
+ # 列出所有运行中的实例
629
+ claude-remote list
630
+
631
+ # 显示守护进程状态(PID、端口、运行时间、实例数)
632
+ # 也会检测版本不匹配并自动重启
633
+ claude-remote status
634
+
635
+ # 停止守护进程和所有实例
636
+ claude-remote stop
637
+
638
+ # 无头模式(无本地终端,仅 Web UI)
639
+ claude-remote --no-terminal
640
+
641
+ # 查看版本
642
+ claude-remote --version
643
+
644
+ # 更新到最新版本
645
+ claude-remote update
646
+ ```
647
+
648
+ ### 自动重启守护进程
649
+
650
+ CLI 在启动时和执行 `claude-remote status` 时会检查守护进程版本:
651
+
652
+ - **版本不匹配**(CLI 版本高于 daemon)→ 无实例运行时自动重启 daemon
653
+ - **有运行中的实例** → 显示警告提示手动重启
654
+
655
+ 执行 `claude-remote update` 后,如果没有运行中的实例,守护进程会自动重启。如有实例运行,会提示手动重启。
656
+
657
+ ### 停止运行
658
+
659
+ - **单次 Ctrl+C** — 发送给 Claude Code(取消当前任务)
660
+ - **双次 Ctrl+C**(500毫秒内)— 停止代理服务器
661
+
662
+ ---
663
+
664
+ ## 配置
665
+
666
+ 配置文件:`~/.claude-remote/settings.json`(旧版 `config.json` 会自动迁移)
667
+
668
+ | 选项 | 类型 | 默认值 | 说明 |
669
+ |------|------|--------|------|
670
+ | `host` | string | "0.0.0.0" | 绑定地址 |
671
+ | `token` | string \| null | null | 认证令牌;`null` = 自动生成的共享令牌 |
672
+ | `claudeCommand` | string | "claude" | Claude CLI 路径 |
673
+ | `claudeArgs` | string[] | [] | 额外的 Claude CLI 参数(与 CLI 参数合并,去重) |
674
+ | `claudeCwd` | string \| null | null | Claude 工作目录;`null` = 当前目录 |
675
+ | `sessionTtlMs` | number | 86400000 | 会话 TTL(毫秒,默认 24 小时) |
676
+ | `authRateLimit` | number | 20 | 认证速率限制(每分钟每 IP) |
677
+ | `maxBufferLines` | number | 10000 | 输出缓冲区最大行数 |
678
+ | `instanceName` | string \| null | null | 实例名称;`null` = 工作目录名 |
679
+ | `shortcuts` | array | 见下文 | 快捷输入按钮 |
680
+ | `commands` | array | 见下文 | 自定义命令按钮 |
681
+ | `workspaces` | string[] | [] | Web 生成实例允许的工作目录 |
682
+ | `settingsDirs` | string[] | ["~/.claude/", "~/.claude-remote/settings/"] | 设置文件扫描目录 |
683
+ | `notifications` | object | — | 通知渠道配置(见下文) |
684
+
685
+ > **注意**:端口固定为 8866。所有实例运行在单个守护进程中。
686
+
687
+ **通知渠道配置:**
688
+
689
+ | 字段 | 类型 | 默认值 | 说明 |
690
+ |------|------|--------|------|
691
+ | `webhookUrl` | string | — | 钉钉 webhook URL |
692
+ | `sendKey` | string | — | Server酱³ SendKey(微信) |
693
+ | `enabled` | boolean | true | 渠道是否启用 |
694
+
695
+ **优先级**:CLI 参数 > 配置文件 > 默认值(`claudeArgs` 除外,它是合并的)
696
+
697
+ ### 快捷键
698
+
699
+ 终端下方显示的快捷输入按钮。
700
+
701
+ **默认快捷键:**
702
+
703
+ | 按键 | 数据 | 说明 |
704
+ |------|------|------|
705
+ | Esc | `\u001b` | ESC 键 |
706
+ | Enter | `\r` | 回车键 |
707
+ | Tab | `\t` | Tab 键 |
708
+ | ↑ | `\u001b[A` | 上箭头 |
709
+ | ↓ | `\u001b[B` | 下箭头 |
710
+ | ← | `\u001b[D` | 左箭头 |
711
+ | → | `\u001b[C` | 右箭头 |
712
+ | S-Tab | `\u001b[Z` | Shift+Tab |
713
+ | Ctrl+O | `\u000f` | Ctrl+O |
714
+ | Ctrl+E | `\u0005` | Ctrl+E |
715
+
716
+ **自定义示例:**
717
+
718
+ ```json
719
+ {
720
+ "shortcuts": [
721
+ { "label": "Yes", "data": "y", "enabled": true, "desc": "确认" },
722
+ { "label": "Esc", "data": "\u001b", "enabled": true, "desc": "取消 (ESC)" },
723
+ { "label": "Ctrl+C", "data": "\u0003", "enabled": true, "desc": "中断" }
724
+ ]
725
+ }
726
+ ```
727
+
728
+ | 字段 | 类型 | 说明 |
729
+ |------|------|------|
730
+ | `label` | string | 按钮显示文本 |
731
+ | `data` | string | 发送的数据(支持转义序列,如 `\u001b` 表示 ESC) |
732
+ | `enabled` | boolean | 按钮是否启用 |
733
+ | `desc` | string | 描述(可选) |
734
+
735
+ **常用转义值:**
736
+ - `\u001b` - ESC
737
+ - `\r` - 回车
738
+ - `\u0003` - Ctrl+C
739
+ - `\u0004` - Ctrl+D
740
+
741
+ ### 命令
742
+
743
+ 快捷栏中的自定义命令按钮。
744
+
745
+ **默认命令:**
746
+
747
+ | 命令 | 说明 |
748
+ |------|------|
749
+ | /clear | 清屏 |
750
+ | /compact | 压缩对话 |
751
+ | /resume | 恢复会话 |
752
+ | /stats | 统计信息 |
753
+ | /exit | 退出 |
754
+
755
+ **自定义示例:**
756
+
757
+ ```json
758
+ {
759
+ "commands": [
760
+ { "label": "/help", "command": "/help", "enabled": true },
761
+ { "label": "Git 状态", "command": "git status", "enabled": true, "autoSend": false }
762
+ ]
763
+ }
764
+ ```
765
+
766
+ | 字段 | 类型 | 默认值 | 说明 |
767
+ |------|------|--------|------|
768
+ | `label` | string | — | 按钮显示文本 |
769
+ | `command` | string | — | 要执行的命令 |
770
+ | `enabled` | boolean | — | 按钮是否启用 |
771
+ | `autoSend` | boolean | `true` | `true` = 立即发送,`false` = 填入输入框 |
772
+
773
+ > 以 `/` 开头的命令是 Claude Code 斜杠命令。其他命令在终端中执行。
774
+
775
+ ### 工作空间
776
+
777
+ 限制 Web 生成的实例可以使用哪些目录:
778
+
779
+ ```json
780
+ {
781
+ "workspaces": [
782
+ "/home/user/projects/api",
783
+ "/home/user/projects/web"
784
+ ]
785
+ }
786
+ ```
787
+
788
+ 如未配置,Web 生成的实例只能从已存在的 claude 实例中选择项目
789
+
790
+ ### 设置文件
791
+
792
+ 从 Web UI 创建实例时,可以选择自定义设置文件通过 `--settings` 传递。设置文件必须:
793
+
794
+ 1. 以 `settings` 为前缀命名(如 `settings-project-a.json`、`settings.idea.json`)
795
+ 2. 是有效的 `.json` 结尾的 JSON 文件
796
+ 3. 位于配置的扫描目录中
797
+
798
+ **默认扫描目录:**
799
+ - `~/.claude/` — Claude Code 配置目录
800
+ - `~/.claude-remote/settings/` — Claude Remote 自定义设置
801
+
802
+ **自定义目录:**
803
+
804
+ ```json
805
+ {
806
+ "settingsDirs": [
807
+ "~/.claude/",
808
+ "~/.claude-remote/settings/",
809
+ "~/my-custom-settings/"
810
+ ]
811
+ }
812
+ ```
813
+
814
+ **设置文件示例**(`~/.claude/settings-project-a.json`):
815
+
816
+ ```json
817
+ {
818
+ "env": {
819
+ "ANTHROPIC_BASE_URL": "https://api.anthropic.com"
820
+ },
821
+ "permissions": {
822
+ "allow": ["Bash(git:*)", "Bash(npm:*)"]
823
+ }
824
+ }
825
+ ```
826
+
827
+ > **注意**:带端口号的 JSON 文件会自动从设置文件列表中排除。
828
+
829
+ ### 完整示例
830
+
831
+ ```json
832
+ {
833
+ "host": "0.0.0.0",
834
+ "token": null,
835
+
836
+ "claudeCommand": "claude",
837
+ "claudeArgs": ["--no-telemetry"],
838
+ "claudeCwd": null,
839
+
840
+ "sessionTtlMs": 86400000,
841
+ "authRateLimit": 20,
842
+ "maxBufferLines": 10000,
843
+ "instanceName": null,
844
+
845
+ "shortcuts": [
846
+ { "label": "Yes", "data": "y", "enabled": true },
847
+ { "label": "Esc", "data": "\u001b", "enabled": true },
848
+ { "label": "Enter", "data": "\r", "enabled": true },
849
+ { "label": "Ctrl+C", "data": "\u0003", "enabled": true }
850
+ ],
851
+
852
+ "commands": [
853
+ { "label": "Git 状态", "command": "git status", "enabled": true },
854
+ { "label": "Git 日志", "command": "git log --oneline -10", "enabled": true }
855
+ ],
856
+
857
+ "workspaces": [
858
+ "/home/user/projects/api",
859
+ "/home/user/projects/web"
860
+ ],
861
+
862
+ "notifications": {
863
+ "dingtalk": {
864
+ "webhookUrl": "https://oapi.dingtalk.com/robot/send?access_token=your-token"
865
+ }
866
+ }
867
+ }
868
+ ```
869
+
870
+ ---
871
+
872
+ ## 安全性
873
+
874
+ - **令牌认证** — 32 字节随机令牌,首次运行生成
875
+ - **会话 Cookie** — HttpOnly + SameSite=Lax,24 小时 TTL
876
+ - **网络隔离** — 仅绑定局域网 IP,无公网暴露
877
+ - **速率限制** — 每个 IP 每分钟 20 次认证尝试
878
+ - **Hook 安全** — 仅接受 localhost 请求
879
+
880
+ ---
881
+
882
+ ## 故障排除
883
+
884
+ ### `node-pty` 编译失败
885
+
886
+ 安装编译工具:
887
+
888
+ | 操作系统 | 命令 |
889
+ |---------|------|
890
+ | macOS | `xcode-select --install` |
891
+ | Ubuntu/Debian | `sudo apt-get install build-essential python3` |
892
+ | Fedora/RHEL | `sudo dnf groupinstall 'Development Tools'` |
893
+ | Arch Linux | `sudo pacman -S base-devel python` |
894
+
895
+ ### 二维码扫不了?
896
+
897
+ 1. 确保手机和 PC 在同一网络
898
+ 2. 手动打开终端显示的 URL 并输入令牌
899
+
900
+ ### 手机连不上?
901
+
902
+ 1. 检查 PC 防火墙允许端口 8866
903
+ 2. 验证 URL 显示正确的局域网 IP
904
+ 3. 如使用 VPN,尝试显式设置 `host` 选项
905
+
906
+ ---
907
+
908
+ ## 前置要求
909
+
910
+ - **Node.js** >= 20
911
+ - **Claude Code CLI** — [安装指南](https://docs.anthropic.com/en/docs/claude-code)
912
+
913
+ > `pnpm` 和 Claude CLI 会在首次运行时自动安装(如缺失)。
914
+
915
+ ---
916
+
917
+ ## 开发
918
+
919
+ **源码开发:**
920
+
921
+ ```bash
922
+ # 克隆并安装
923
+ git clone https://github.com/StephenTowne/open-claude-remote.git
924
+ cd open-claude-remote
925
+ pnpm install
926
+
927
+ # 开发模式(热重载)
928
+ pnpm dev:cli
929
+
930
+ # 生产模式
931
+ pnpm build && pnpm link -g && claude-remote
932
+ ```
933
+
934
+ **项目结构:**
935
+
936
+ - 后端:`backend/src/`(Express + node-pty + WebSocket)
937
+ - 前端:`frontend/src/`(React + xterm.js)
938
+ - 共享类型:`shared/`(通过 `#shared` 别名导入)
939
+ - 测试:`pnpm test`(Vitest)
940
+ - 构建:`pnpm build`(TypeScript + Vite)
941
+
942
+ ### 停止所有实例
943
+
944
+ ```bash
945
+ pnpm stop
946
+ ```
947
+
948
+ ---
949
+
950
+ ## 许可证
951
+
952
+ MIT
@@ -2,6 +2,6 @@ import { Router } from 'express';
2
2
  import { AuthModule } from '../auth/auth-middleware.js';
3
3
  import type { NotificationManager } from '../notification/notification-manager.js';
4
4
  import type { NotificationServiceFactory } from '../notification/notification-service-factory.js';
5
- import type { WsServer } from '../ws/ws-server.js';
6
- export declare function createConfigRoutes(authModule: AuthModule, notificationManager?: NotificationManager, notificationServiceFactory?: NotificationServiceFactory, wsServer?: WsServer): Router;
5
+ import type { InstanceManager } from '../instance/instance-manager.js';
6
+ export declare function createConfigRoutes(authModule: AuthModule, notificationManager?: NotificationManager, notificationServiceFactory?: NotificationServiceFactory, instanceManager?: InstanceManager): Router;
7
7
  //# sourceMappingURL=config-routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-routes.d.ts","sourceRoot":"","sources":["../../../../backend/src/api/config-routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAKjC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAexD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAClG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AA+GnD,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,mBAAmB,CAAC,EAAE,mBAAmB,EACzC,0BAA0B,CAAC,EAAE,0BAA0B,EACvD,QAAQ,CAAC,EAAE,QAAQ,GAClB,MAAM,CA4LR"}
1
+ {"version":3,"file":"config-routes.d.ts","sourceRoot":"","sources":["../../../../backend/src/api/config-routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAKjC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAoBxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AA+GvE,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,mBAAmB,CAAC,EAAE,mBAAmB,EACzC,0BAA0B,CAAC,EAAE,0BAA0B,EACvD,eAAe,CAAC,EAAE,eAAe,GAChC,MAAM,CA4MR"}
@@ -7,9 +7,10 @@ import { createAuthRoutes } from './auth-routes.js';
7
7
  import { logger } from '../logger/logger.js';
8
8
  import { withFileLockAsync } from '../utils/file-lock.js';
9
9
  import { fillDefaultShortcuts, fillDefaultCommands, } from '../config.js';
10
+ import { scanSkills, convertSkillsToCommands, mergeSkillCommands, } from '../skills/index.js';
10
11
  import { getNotificationStatus, } from '../../../shared/index.js';
11
12
  const CONFIG_DIR = join(homedir(), '.claude-remote');
12
- const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
13
+ const CONFIG_FILE = join(CONFIG_DIR, 'settings.json');
13
14
  const CONFIG_LOCK = CONFIG_FILE + '.lock';
14
15
  /**
15
16
  * 验证配置结构
@@ -125,7 +126,7 @@ async function saveUserConfig(config) {
125
126
  // 直接写入文件
126
127
  await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
127
128
  }
128
- export function createConfigRoutes(authModule, notificationManager, notificationServiceFactory, wsServer) {
129
+ export function createConfigRoutes(authModule, notificationManager, notificationServiceFactory, instanceManager) {
129
130
  const router = Router();
130
131
  // 复用 auth 路由以支持测试认证
131
132
  router.use(createAuthRoutes(authModule));
@@ -149,6 +150,20 @@ export function createConfigRoutes(authModule, notificationManager, notification
149
150
  if (!config.commands) {
150
151
  filledConfig = fillDefaultCommands(filledConfig);
151
152
  }
153
+ // 扫描并合并 Skill Commands
154
+ const claudeCwd = filledConfig.claudeCwd ?? process.cwd();
155
+ const skills = scanSkills(claudeCwd);
156
+ const skillCommands = convertSkillsToCommands(skills);
157
+ const mergeResult = mergeSkillCommands(filledConfig.commands ?? [], skillCommands);
158
+ filledConfig = { ...filledConfig, commands: mergeResult.commands };
159
+ if (mergeResult.added > 0 || mergeResult.removed > 0) {
160
+ logger.info({
161
+ added: mergeResult.added,
162
+ removed: mergeResult.removed,
163
+ preserved: mergeResult.preserved,
164
+ total: mergeResult.total,
165
+ }, 'Skill commands merged');
166
+ }
152
167
  // 安全处理:不暴露敏感字段(token、webhook URL 等)
153
168
  const { token: _, notifications: _notif, ...safeConfig } = filledConfig;
154
169
  // 构建通知配置的安全视图
@@ -200,8 +215,8 @@ export function createConfigRoutes(authModule, notificationManager, notification
200
215
  notificationServiceFactory.refresh();
201
216
  }
202
217
  // 广播刷新消息通知其他实例
203
- if (wsServer) {
204
- wsServer.broadcast({
218
+ if (instanceManager) {
219
+ instanceManager.broadcastAll({
205
220
  type: 'service_refresh',
206
221
  source: 'config_update',
207
222
  });
@@ -272,8 +287,8 @@ export function createConfigRoutes(authModule, notificationManager, notification
272
287
  notificationServiceFactory.refresh(channel);
273
288
  }
274
289
  // 广播刷新消息通知其他实例
275
- if (wsServer) {
276
- wsServer.broadcast({
290
+ if (instanceManager) {
291
+ instanceManager.broadcastAll({
277
292
  type: 'service_refresh',
278
293
  channel: channel,
279
294
  source: 'config_update',