@tunnelbox/hermes 0.1.10 → 0.1.11
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.
- package/README.md +122 -122
- package/README.zh-CN.md +120 -120
- package/dist/index.mjs +68 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
# @tunnelbox/hermes
|
|
2
|
-
|
|
3
|
-
[English](README.md) · [简体中文](README.zh-CN.md)
|
|
4
|
-
|
|
5
|
-
**Official website:** [www.tunnelbox.top](https://www.tunnelbox.top)
|
|
6
|
-
|
|
7
|
-
**tunnelbox Hermes adapter (standalone daemon):** drive the Hermes Agent on your computer from your phone — sessions, streaming, abort, delete. It drives the Hermes CLI (headless oneshot) and dials **out** to a relay only (no public IP, no port forwarding).
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
Phone app ──WSS──► relay ──WSS──► tunnelbox-hermes (this adapter)
|
|
11
|
-
└─ hermes chat [--resume <id>] -Q -q "<text>" (spawns a hermes subprocess per message)
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Quick start (install → run → pair → use)
|
|
15
|
-
|
|
16
|
-
Follow these steps once on a computer that has Hermes installed and configured.
|
|
17
|
-
|
|
18
|
-
### Step 0 — Prepare your phone
|
|
19
|
-
|
|
20
|
-
Install the tunnelbox App (Android & iOS) from the official website: **https://www.tunnelbox.top**
|
|
21
|
-
|
|
22
|
-
- Sign in / create an account when prompted.
|
|
23
|
-
|
|
24
|
-
### Step 1 — Install the adapter on your computer
|
|
25
|
-
|
|
26
|
-
Prerequisites: Node.js ≥ 22, and **Hermes Agent** (NousResearch/hermes-agent) installed with login/model configured — `hermes chat -Q -q "hi"` answers correctly.
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npm install -g @tunnelbox/hermes
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Step 2 — Run it
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
tunnelbox-hermes
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
On first start the adapter connects to the relay and prints a **pairing QR code + pairing code** in the terminal.
|
|
39
|
-
|
|
40
|
-
### Step 3 — Pair with your phone
|
|
41
|
-
|
|
42
|
-
In the phone app tap **Scan to pair** and scan the terminal QR code (or type the pairing code manually). The code is single-use and valid for about 10 minutes.
|
|
43
|
-
|
|
44
|
-
- Once bound, the adapter prints "connected account — open the app and pick this computer" on later starts and does not refresh the code.
|
|
45
|
-
- Want a fresh code any time? Run `tunnelbox-hermes --pair`.
|
|
46
|
-
|
|
47
|
-
### Step 4 — Use it from your phone
|
|
48
|
-
|
|
49
|
-
In the app, open this computer and you can start new sessions, send messages, watch streaming output, and abort a running session.
|
|
50
|
-
|
|
51
|
-
> This package's standalone daemon is the **whole-block fallback** path. For **real-time** remote control
|
|
52
|
-
> (live token streaming, bidirectional messages, phone approval), install the official Hermes **native plugin**
|
|
53
|
-
> (`native-plugin/`, in-process): it runs inside a running Hermes CLI/Gateway and connects to the phone in real time.
|
|
54
|
-
|
|
55
|
-
## Configuration
|
|
56
|
-
|
|
57
|
-
| Env var | Default | Description |
|
|
58
|
-
|---|---|---|
|
|
59
|
-
| `TUNNELBOX_RELAY_URL` | saved relay URL | Relay address |
|
|
60
|
-
| `TUNNELBOX_CWD` | `process.cwd()` | Default workspace |
|
|
61
|
-
| `TUNNELBOX_HERMES_MODEL` | hermes default | `--model` |
|
|
62
|
-
| `TUNNELBOX_HERMES_PROVIDER` | hermes default | `--provider` (e.g. nous/openrouter) |
|
|
63
|
-
| `TUNNELBOX_HERMES_TOOLSETS` | hermes default | `--toolsets` (comma-separated) |
|
|
64
|
-
| `TUNNELBOX_HERMES_VERBOSE` | off | `--verbose` |
|
|
65
|
-
| `TUNNELBOX_HERMES_BIN` | `hermes` | Hermes executable name / absolute path (may include leading args, e.g. `node /path/fake`) |
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
TUNNELBOX_RELAY_URL=wss://chat.example.com TUNNELBOX_HERMES_MODEL="anthropic/claude-sonnet-4" tunnelbox-hermes
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Capabilities
|
|
72
|
-
|
|
73
|
-
| Capability | Value | Notes |
|
|
74
|
-
|---|---|---|
|
|
75
|
-
| streaming | ✅ | `chat -Q -q` emits the whole answer in one block → reported as turn-level `text` + `complete` |
|
|
76
|
-
| thinking | ❌ | oneshot exposes no structured reasoning parts |
|
|
77
|
-
| permission | ❌ | headless has no official approval channel → Hermes' own policy/sandbox fail-closed (phone approval lives in `native-plugin/`) |
|
|
78
|
-
| commands | ✅ | `/new`, `/help` |
|
|
79
|
-
| abort | ✅ | kill subprocess (SIGTERM → SIGKILL) |
|
|
80
|
-
|
|
81
|
-
## Sessions
|
|
82
|
-
|
|
83
|
-
- Sessions are local mirrors (`~/.tunnelbox/hermes-sessions/<uuid>/`): each message is written immediately for list/history/delete.
|
|
84
|
-
- Continuation: after the first turn the adapter discovers the real Hermes session id via `hermes sessions list --workspace <dir>`, stores it in the mirror, and every later turn uses `chat --resume <id> -Q -q` (Hermes persistence guarantees multi-turn context). Verified on Hermes v0.21.1 (note: `hermes -z` does not resume, and caller-minted ids are invalid).
|
|
85
|
-
- State file: `~/.tunnelbox/remote-state.hermes.json`.
|
|
86
|
-
|
|
87
|
-
## Security notes
|
|
88
|
-
|
|
89
|
-
- Tool execution / command authorization is governed by Hermes' own policy (dangerous commands fail closed); this adapter never auto-approves and never rewrites `~/.hermes/config.yaml`.
|
|
90
|
-
- Don't run unrestricted toolsets in untrusted directories, and don't expose sensitive projects to an untrusted phone.
|
|
91
|
-
|
|
92
|
-
## Multilingual
|
|
93
|
-
|
|
94
|
-
User-visible output supports 8 languages (`zh-CN` / `zh-TW` / `en-US` / `ja-JP` / `ko-KR` / `fr-FR` / `de-DE` / `es-ES`). Resolution order: `TUNNELBOX_LANG` > saved `remote-state.hermes.json.lang` > system locale > `en-US`.
|
|
95
|
-
|
|
96
|
-
## Troubleshooting
|
|
97
|
-
|
|
98
|
-
| Problem | Fix |
|
|
99
|
-
|---|---|
|
|
100
|
-
| No QR / pairing code on screen | Run `tunnelbox-hermes --pair` to print a fresh one |
|
|
101
|
-
| Need to pair a different phone | Unbind in the app settings, then run with `--pair` again |
|
|
102
|
-
| Already bound but phone doesn't connect | Confirm both use the same relay; check `~/.tunnelbox/tunnelbox.log` |
|
|
103
|
-
| Use your own relay | Start with `TUNNELBOX_RELAY_URL=wss://<your-relay>` |
|
|
104
|
-
| Hermes misbehaves / unexpected output | On a machine with hermes, run `npm run probe` and send the structured diagnostics back to maintainers |
|
|
105
|
-
|
|
106
|
-
## State files
|
|
107
|
-
|
|
108
|
-
- `~/.tunnelbox/remote-state.hermes.json` — agentID / relay / bound state / language
|
|
109
|
-
- `~/.tunnelbox/hermes-sessions/` — local session mirrors
|
|
110
|
-
- `~/.tunnelbox/remote-pairing.hermes.txt` + `remote-pairing.json` — latest pairing info
|
|
111
|
-
- `~/.tunnelbox/tunnelbox.log` — adapter log
|
|
112
|
-
|
|
113
|
-
## Uninstall
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
npm uninstall -g @tunnelbox/hermes
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Remove `~/.tunnelbox` (all adapters' shared state) only if you no longer use any tunnelbox adapter.
|
|
120
|
-
|
|
121
|
-
## Development
|
|
122
|
-
|
|
1
|
+
# @tunnelbox/hermes
|
|
2
|
+
|
|
3
|
+
[English](README.md) · [简体中文](README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
**Official website:** [www.tunnelbox.top](https://www.tunnelbox.top)
|
|
6
|
+
|
|
7
|
+
**tunnelbox Hermes adapter (standalone daemon):** drive the Hermes Agent on your computer from your phone — sessions, streaming, abort, delete. It drives the Hermes CLI (headless oneshot) and dials **out** to a relay only (no public IP, no port forwarding).
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Phone app ──WSS──► relay ──WSS──► tunnelbox-hermes (this adapter)
|
|
11
|
+
└─ hermes chat [--resume <id>] -Q -q "<text>" (spawns a hermes subprocess per message)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick start (install → run → pair → use)
|
|
15
|
+
|
|
16
|
+
Follow these steps once on a computer that has Hermes installed and configured.
|
|
17
|
+
|
|
18
|
+
### Step 0 — Prepare your phone
|
|
19
|
+
|
|
20
|
+
Install the tunnelbox App (Android & iOS) from the official website: **https://www.tunnelbox.top**
|
|
21
|
+
|
|
22
|
+
- Sign in / create an account when prompted.
|
|
23
|
+
|
|
24
|
+
### Step 1 — Install the adapter on your computer
|
|
25
|
+
|
|
26
|
+
Prerequisites: Node.js ≥ 22, and **Hermes Agent** (NousResearch/hermes-agent) installed with login/model configured — `hermes chat -Q -q "hi"` answers correctly.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g @tunnelbox/hermes
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Step 2 — Run it
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
tunnelbox-hermes
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
On first start the adapter connects to the relay and prints a **pairing QR code + pairing code** in the terminal.
|
|
39
|
+
|
|
40
|
+
### Step 3 — Pair with your phone
|
|
41
|
+
|
|
42
|
+
In the phone app tap **Scan to pair** and scan the terminal QR code (or type the pairing code manually). The code is single-use and valid for about 10 minutes.
|
|
43
|
+
|
|
44
|
+
- Once bound, the adapter prints "connected account — open the app and pick this computer" on later starts and does not refresh the code.
|
|
45
|
+
- Want a fresh code any time? Run `tunnelbox-hermes --pair`.
|
|
46
|
+
|
|
47
|
+
### Step 4 — Use it from your phone
|
|
48
|
+
|
|
49
|
+
In the app, open this computer and you can start new sessions, send messages, watch streaming output, and abort a running session.
|
|
50
|
+
|
|
51
|
+
> This package's standalone daemon is the **whole-block fallback** path. For **real-time** remote control
|
|
52
|
+
> (live token streaming, bidirectional messages, phone approval), install the official Hermes **native plugin**
|
|
53
|
+
> (`native-plugin/`, in-process): it runs inside a running Hermes CLI/Gateway and connects to the phone in real time.
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
| Env var | Default | Description |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `TUNNELBOX_RELAY_URL` | saved relay URL | Relay address |
|
|
60
|
+
| `TUNNELBOX_CWD` | `process.cwd()` | Default workspace |
|
|
61
|
+
| `TUNNELBOX_HERMES_MODEL` | hermes default | `--model` |
|
|
62
|
+
| `TUNNELBOX_HERMES_PROVIDER` | hermes default | `--provider` (e.g. nous/openrouter) |
|
|
63
|
+
| `TUNNELBOX_HERMES_TOOLSETS` | hermes default | `--toolsets` (comma-separated) |
|
|
64
|
+
| `TUNNELBOX_HERMES_VERBOSE` | off | `--verbose` |
|
|
65
|
+
| `TUNNELBOX_HERMES_BIN` | `hermes` | Hermes executable name / absolute path (may include leading args, e.g. `node /path/fake`) |
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
TUNNELBOX_RELAY_URL=wss://chat.example.com TUNNELBOX_HERMES_MODEL="anthropic/claude-sonnet-4" tunnelbox-hermes
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Capabilities
|
|
72
|
+
|
|
73
|
+
| Capability | Value | Notes |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| streaming | ✅ | `chat -Q -q` emits the whole answer in one block → reported as turn-level `text` + `complete` |
|
|
76
|
+
| thinking | ❌ | oneshot exposes no structured reasoning parts |
|
|
77
|
+
| permission | ❌ | headless has no official approval channel → Hermes' own policy/sandbox fail-closed (phone approval lives in `native-plugin/`) |
|
|
78
|
+
| commands | ✅ | `/new`, `/help` |
|
|
79
|
+
| abort | ✅ | kill subprocess (SIGTERM → SIGKILL) |
|
|
80
|
+
|
|
81
|
+
## Sessions
|
|
82
|
+
|
|
83
|
+
- Sessions are local mirrors (`~/.tunnelbox/hermes-sessions/<uuid>/`): each message is written immediately for list/history/delete.
|
|
84
|
+
- Continuation: after the first turn the adapter discovers the real Hermes session id via `hermes sessions list --workspace <dir>`, stores it in the mirror, and every later turn uses `chat --resume <id> -Q -q` (Hermes persistence guarantees multi-turn context). Verified on Hermes v0.21.1 (note: `hermes -z` does not resume, and caller-minted ids are invalid).
|
|
85
|
+
- State file: `~/.tunnelbox/remote-state.hermes.json`.
|
|
86
|
+
|
|
87
|
+
## Security notes
|
|
88
|
+
|
|
89
|
+
- Tool execution / command authorization is governed by Hermes' own policy (dangerous commands fail closed); this adapter never auto-approves and never rewrites `~/.hermes/config.yaml`.
|
|
90
|
+
- Don't run unrestricted toolsets in untrusted directories, and don't expose sensitive projects to an untrusted phone.
|
|
91
|
+
|
|
92
|
+
## Multilingual
|
|
93
|
+
|
|
94
|
+
User-visible output supports 8 languages (`zh-CN` / `zh-TW` / `en-US` / `ja-JP` / `ko-KR` / `fr-FR` / `de-DE` / `es-ES`). Resolution order: `TUNNELBOX_LANG` > saved `remote-state.hermes.json.lang` > system locale > `en-US`.
|
|
95
|
+
|
|
96
|
+
## Troubleshooting
|
|
97
|
+
|
|
98
|
+
| Problem | Fix |
|
|
99
|
+
|---|---|
|
|
100
|
+
| No QR / pairing code on screen | Run `tunnelbox-hermes --pair` to print a fresh one |
|
|
101
|
+
| Need to pair a different phone | Unbind in the app settings, then run with `--pair` again |
|
|
102
|
+
| Already bound but phone doesn't connect | Confirm both use the same relay; check `~/.tunnelbox/tunnelbox.log` |
|
|
103
|
+
| Use your own relay | Start with `TUNNELBOX_RELAY_URL=wss://<your-relay>` |
|
|
104
|
+
| Hermes misbehaves / unexpected output | On a machine with hermes, run `npm run probe` and send the structured diagnostics back to maintainers |
|
|
105
|
+
|
|
106
|
+
## State files
|
|
107
|
+
|
|
108
|
+
- `~/.tunnelbox/remote-state.hermes.json` — agentID / relay / bound state / language
|
|
109
|
+
- `~/.tunnelbox/hermes-sessions/` — local session mirrors
|
|
110
|
+
- `~/.tunnelbox/remote-pairing.hermes.txt` + `remote-pairing.json` — latest pairing info
|
|
111
|
+
- `~/.tunnelbox/tunnelbox.log` — adapter log
|
|
112
|
+
|
|
113
|
+
## Uninstall
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm uninstall -g @tunnelbox/hermes
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Remove `~/.tunnelbox` (all adapters' shared state) only if you no longer use any tunnelbox adapter.
|
|
120
|
+
|
|
121
|
+
## Development
|
|
122
|
+
|
|
123
123
|
Implementation details (protocol mapping, source layout, design decisions, untested items) are kept in the source repository as `DEV.md` next to this package, plus the protocol spec under the repo's `docs/`.
|
package/README.zh-CN.md
CHANGED
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
# @tunnelbox/hermes
|
|
2
|
-
|
|
3
|
-
[English](README.md) · [简体中文](README.zh-CN.md)
|
|
4
|
-
|
|
5
|
-
**官网:** [www.tunnelbox.top](https://www.tunnelbox.top)
|
|
6
|
-
|
|
7
|
-
**tunnelbox Hermes 适配器(独立进程)**:用手机远程驱动电脑上的 Hermes Agent——会话 / 流式 / 中止 / 删除。它驱动 Hermes CLI(headless oneshot),只向中继发出站连接(无需公网 IP / 端口映射)。
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
手机 App ──WSS──► relay ──WSS──► tunnelbox-hermes(本适配器)
|
|
11
|
-
└─ hermes chat [--resume <id>] -Q -q "<text>"(每条消息拉起一个 hermes 子进程)
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## 快速上手(安装 → 运行 → 配对 → 使用)
|
|
15
|
-
|
|
16
|
-
在装有 Hermes 并完成配置的电脑上,按以下步骤操作一次即可。
|
|
17
|
-
|
|
18
|
-
### 第 0 步 — 准备手机
|
|
19
|
-
|
|
20
|
-
在官网下载并安装 tunnelbox App(Android / iOS 均已上架):**https://www.tunnelbox.top**
|
|
21
|
-
|
|
22
|
-
- 按提示登录/注册账号。
|
|
23
|
-
|
|
24
|
-
### 第 1 步 — 电脑上安装适配器
|
|
25
|
-
|
|
26
|
-
前置条件:Node.js ≥ 22,且本机已安装 **Hermes Agent**(NousResearch/hermes-agent)并完成登录/模型配置——`hermes chat -Q -q "hi"` 能正常应答。
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npm install -g @tunnelbox/hermes
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### 第 2 步 — 运行
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
tunnelbox-hermes
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
首次启动会连接中继,并在终端打印**配对二维码 + 配对码**。
|
|
39
|
-
|
|
40
|
-
### 第 3 步 — 与手机配对
|
|
41
|
-
|
|
42
|
-
在手机 App 点「扫一扫配对」,扫描终端二维码(或手动输入配对码)。配对码一次性有效,约 10 分钟。
|
|
43
|
-
|
|
44
|
-
- 绑定后再次启动会打印「已绑定账号,从手机「我的电脑」直接连接」,不再刷新码;
|
|
45
|
-
- 随时需要新码:运行 `tunnelbox-hermes --pair`。
|
|
46
|
-
|
|
47
|
-
### 第 4 步 — 在手机上使用
|
|
48
|
-
|
|
49
|
-
在 App 里打开这台电脑,即可新建会话、发消息、看流式输出、中止运行中的会话。
|
|
50
|
-
|
|
51
|
-
> 本包的 headless 桥(spawn-per-message)为**整段 fallback** 路径;需要**实时**远程控制(逐 token 流式 / 双向消息 / 手机审批 / 选择)请安装官方 **Native 插件**(`native-plugin/`,进程内,需本机运行 Hermes CLI/Gateway)。headless 本身不提供手机审批。
|
|
52
|
-
|
|
53
|
-
## 配置
|
|
54
|
-
|
|
55
|
-
| 环境变量 | 默认 | 说明 |
|
|
56
|
-
|---|---|---|
|
|
57
|
-
| `TUNNELBOX_RELAY_URL` | state 保存的地址 | 中继地址 |
|
|
58
|
-
| `TUNNELBOX_CWD` | `process.cwd()` | 默认工作区 |
|
|
59
|
-
| `TUNNELBOX_HERMES_MODEL` | hermes 默认 | `--model` |
|
|
60
|
-
| `TUNNELBOX_HERMES_PROVIDER` | hermes 默认 | `--provider`(如 nous/openrouter) |
|
|
61
|
-
| `TUNNELBOX_HERMES_TOOLSETS` | hermes 默认 | `--toolsets`(逗号分隔) |
|
|
62
|
-
| `TUNNELBOX_HERMES_VERBOSE` | 关 | `--verbose` |
|
|
63
|
-
| `TUNNELBOX_HERMES_BIN` | `hermes` | hermes 可执行名/绝对路径(可带前置参数,如 `node /path/fake`) |
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
TUNNELBOX_RELAY_URL=wss://chat.example.com TUNNELBOX_HERMES_MODEL="anthropic/claude-sonnet-4" tunnelbox-hermes
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## 能力位
|
|
70
|
-
|
|
71
|
-
| 能力 | 值 | 说明 |
|
|
72
|
-
|---|---|---|
|
|
73
|
-
| streaming | ✅ | `chat -Q -q` 整段一次性输出 → 回合级 `text`+`complete` 回传 |
|
|
74
|
-
| thinking | ❌ | oneshot 无结构化推理部件 |
|
|
75
|
-
| permission | ❌ | headless 无官方审批通道 → Hermes 自身策略/沙箱 fail-closed 兜底(手机审批见 `native-plugin/`) |
|
|
76
|
-
| commands | ✅ | `/new`、`/help` |
|
|
77
|
-
| abort | ✅ | kill 子进程(SIGTERM → SIGKILL) |
|
|
78
|
-
|
|
79
|
-
## 会话模型
|
|
80
|
-
|
|
81
|
-
- 会话 = 本地镜像(`~/.tunnelbox/hermes-sessions/<uuid>/`):每条消息即时落盘,供列表/历史/删除。
|
|
82
|
-
- 续聊:首轮后经 `hermes sessions list --workspace <目录>` 发现 Hermes 真实会话 id 存镜像,后续每轮 `chat --resume <id> -Q -q`(Hermes 官方持久化保证多轮上下文)。已在 Hermes v0.21.1 真机验证(`hermes -z` 不续聊、自定义 id 无效)。
|
|
83
|
-
- 状态文件:`~/.tunnelbox/remote-state.hermes.json`。
|
|
84
|
-
|
|
85
|
-
## 安全说明
|
|
86
|
-
|
|
87
|
-
- 工具执行/命令授权由 Hermes 自身策略管理(危险命令 fail-closed);本适配器不自动放行,也不改写用户 `~/.hermes/config.yaml`。
|
|
88
|
-
- 请勿在不受信目录运行不受限工具集,也别把敏感工程目录暴露给不受信手机。
|
|
89
|
-
|
|
90
|
-
## 多语言
|
|
91
|
-
|
|
92
|
-
用户可见输出支持 8 种语言(`zh-CN` / `zh-TW` / `en-US` / `ja-JP` / `ko-KR` / `fr-FR` / `de-DE` / `es-ES`)。解析顺序:`TUNNELBOX_LANG` > `remote-state.hermes.json` 的 `lang` > 系统 locale > 默认 `en-US`。
|
|
93
|
-
|
|
94
|
-
## 故障排查
|
|
95
|
-
|
|
96
|
-
| 问题 | 解决 |
|
|
97
|
-
|---|---|
|
|
98
|
-
| 终端没有二维码/配对码 | 运行 `tunnelbox-hermes --pair` 打印新码 |
|
|
99
|
-
| 需要换手机配对 | 在 App 设置里解绑,再带 `--pair` 运行 |
|
|
100
|
-
| 已绑定但手机连不上 | 确认两端同一中继;查看 `~/.tunnelbox/tunnelbox.log` |
|
|
101
|
-
| 使用自建中继 | 启动时带 `TUNNELBOX_RELAY_URL=wss://<你的中继>` |
|
|
102
|
-
| Hermes 应答异常/行为不符 | 在装有 hermes 的机器运行 `npm run probe`,输出结构化诊断回传维护者 |
|
|
103
|
-
|
|
104
|
-
## 状态文件
|
|
105
|
-
|
|
106
|
-
- `~/.tunnelbox/remote-state.hermes.json` — agentID / 中继地址 / 绑定状态 / 语言
|
|
107
|
-
- `~/.tunnelbox/hermes-sessions/` — 本地会话镜像
|
|
108
|
-
- `~/.tunnelbox/remote-pairing.hermes.txt` + `remote-pairing.json` — 最新配对信息
|
|
109
|
-
- `~/.tunnelbox/tunnelbox.log` — 适配器日志
|
|
110
|
-
|
|
111
|
-
## 卸载
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
npm uninstall -g @tunnelbox/hermes
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
仅当不再使用任何 tunnelbox 适配器时,才删除 `~/.tunnelbox`(各适配器共享状态)。
|
|
118
|
-
|
|
119
|
-
## 开发
|
|
120
|
-
|
|
1
|
+
# @tunnelbox/hermes
|
|
2
|
+
|
|
3
|
+
[English](README.md) · [简体中文](README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
**官网:** [www.tunnelbox.top](https://www.tunnelbox.top)
|
|
6
|
+
|
|
7
|
+
**tunnelbox Hermes 适配器(独立进程)**:用手机远程驱动电脑上的 Hermes Agent——会话 / 流式 / 中止 / 删除。它驱动 Hermes CLI(headless oneshot),只向中继发出站连接(无需公网 IP / 端口映射)。
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
手机 App ──WSS──► relay ──WSS──► tunnelbox-hermes(本适配器)
|
|
11
|
+
└─ hermes chat [--resume <id>] -Q -q "<text>"(每条消息拉起一个 hermes 子进程)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 快速上手(安装 → 运行 → 配对 → 使用)
|
|
15
|
+
|
|
16
|
+
在装有 Hermes 并完成配置的电脑上,按以下步骤操作一次即可。
|
|
17
|
+
|
|
18
|
+
### 第 0 步 — 准备手机
|
|
19
|
+
|
|
20
|
+
在官网下载并安装 tunnelbox App(Android / iOS 均已上架):**https://www.tunnelbox.top**
|
|
21
|
+
|
|
22
|
+
- 按提示登录/注册账号。
|
|
23
|
+
|
|
24
|
+
### 第 1 步 — 电脑上安装适配器
|
|
25
|
+
|
|
26
|
+
前置条件:Node.js ≥ 22,且本机已安装 **Hermes Agent**(NousResearch/hermes-agent)并完成登录/模型配置——`hermes chat -Q -q "hi"` 能正常应答。
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g @tunnelbox/hermes
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 第 2 步 — 运行
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
tunnelbox-hermes
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
首次启动会连接中继,并在终端打印**配对二维码 + 配对码**。
|
|
39
|
+
|
|
40
|
+
### 第 3 步 — 与手机配对
|
|
41
|
+
|
|
42
|
+
在手机 App 点「扫一扫配对」,扫描终端二维码(或手动输入配对码)。配对码一次性有效,约 10 分钟。
|
|
43
|
+
|
|
44
|
+
- 绑定后再次启动会打印「已绑定账号,从手机「我的电脑」直接连接」,不再刷新码;
|
|
45
|
+
- 随时需要新码:运行 `tunnelbox-hermes --pair`。
|
|
46
|
+
|
|
47
|
+
### 第 4 步 — 在手机上使用
|
|
48
|
+
|
|
49
|
+
在 App 里打开这台电脑,即可新建会话、发消息、看流式输出、中止运行中的会话。
|
|
50
|
+
|
|
51
|
+
> 本包的 headless 桥(spawn-per-message)为**整段 fallback** 路径;需要**实时**远程控制(逐 token 流式 / 双向消息 / 手机审批 / 选择)请安装官方 **Native 插件**(`native-plugin/`,进程内,需本机运行 Hermes CLI/Gateway)。headless 本身不提供手机审批。
|
|
52
|
+
|
|
53
|
+
## 配置
|
|
54
|
+
|
|
55
|
+
| 环境变量 | 默认 | 说明 |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `TUNNELBOX_RELAY_URL` | state 保存的地址 | 中继地址 |
|
|
58
|
+
| `TUNNELBOX_CWD` | `process.cwd()` | 默认工作区 |
|
|
59
|
+
| `TUNNELBOX_HERMES_MODEL` | hermes 默认 | `--model` |
|
|
60
|
+
| `TUNNELBOX_HERMES_PROVIDER` | hermes 默认 | `--provider`(如 nous/openrouter) |
|
|
61
|
+
| `TUNNELBOX_HERMES_TOOLSETS` | hermes 默认 | `--toolsets`(逗号分隔) |
|
|
62
|
+
| `TUNNELBOX_HERMES_VERBOSE` | 关 | `--verbose` |
|
|
63
|
+
| `TUNNELBOX_HERMES_BIN` | `hermes` | hermes 可执行名/绝对路径(可带前置参数,如 `node /path/fake`) |
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
TUNNELBOX_RELAY_URL=wss://chat.example.com TUNNELBOX_HERMES_MODEL="anthropic/claude-sonnet-4" tunnelbox-hermes
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 能力位
|
|
70
|
+
|
|
71
|
+
| 能力 | 值 | 说明 |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| streaming | ✅ | `chat -Q -q` 整段一次性输出 → 回合级 `text`+`complete` 回传 |
|
|
74
|
+
| thinking | ❌ | oneshot 无结构化推理部件 |
|
|
75
|
+
| permission | ❌ | headless 无官方审批通道 → Hermes 自身策略/沙箱 fail-closed 兜底(手机审批见 `native-plugin/`) |
|
|
76
|
+
| commands | ✅ | `/new`、`/help` |
|
|
77
|
+
| abort | ✅ | kill 子进程(SIGTERM → SIGKILL) |
|
|
78
|
+
|
|
79
|
+
## 会话模型
|
|
80
|
+
|
|
81
|
+
- 会话 = 本地镜像(`~/.tunnelbox/hermes-sessions/<uuid>/`):每条消息即时落盘,供列表/历史/删除。
|
|
82
|
+
- 续聊:首轮后经 `hermes sessions list --workspace <目录>` 发现 Hermes 真实会话 id 存镜像,后续每轮 `chat --resume <id> -Q -q`(Hermes 官方持久化保证多轮上下文)。已在 Hermes v0.21.1 真机验证(`hermes -z` 不续聊、自定义 id 无效)。
|
|
83
|
+
- 状态文件:`~/.tunnelbox/remote-state.hermes.json`。
|
|
84
|
+
|
|
85
|
+
## 安全说明
|
|
86
|
+
|
|
87
|
+
- 工具执行/命令授权由 Hermes 自身策略管理(危险命令 fail-closed);本适配器不自动放行,也不改写用户 `~/.hermes/config.yaml`。
|
|
88
|
+
- 请勿在不受信目录运行不受限工具集,也别把敏感工程目录暴露给不受信手机。
|
|
89
|
+
|
|
90
|
+
## 多语言
|
|
91
|
+
|
|
92
|
+
用户可见输出支持 8 种语言(`zh-CN` / `zh-TW` / `en-US` / `ja-JP` / `ko-KR` / `fr-FR` / `de-DE` / `es-ES`)。解析顺序:`TUNNELBOX_LANG` > `remote-state.hermes.json` 的 `lang` > 系统 locale > 默认 `en-US`。
|
|
93
|
+
|
|
94
|
+
## 故障排查
|
|
95
|
+
|
|
96
|
+
| 问题 | 解决 |
|
|
97
|
+
|---|---|
|
|
98
|
+
| 终端没有二维码/配对码 | 运行 `tunnelbox-hermes --pair` 打印新码 |
|
|
99
|
+
| 需要换手机配对 | 在 App 设置里解绑,再带 `--pair` 运行 |
|
|
100
|
+
| 已绑定但手机连不上 | 确认两端同一中继;查看 `~/.tunnelbox/tunnelbox.log` |
|
|
101
|
+
| 使用自建中继 | 启动时带 `TUNNELBOX_RELAY_URL=wss://<你的中继>` |
|
|
102
|
+
| Hermes 应答异常/行为不符 | 在装有 hermes 的机器运行 `npm run probe`,输出结构化诊断回传维护者 |
|
|
103
|
+
|
|
104
|
+
## 状态文件
|
|
105
|
+
|
|
106
|
+
- `~/.tunnelbox/remote-state.hermes.json` — agentID / 中继地址 / 绑定状态 / 语言
|
|
107
|
+
- `~/.tunnelbox/hermes-sessions/` — 本地会话镜像
|
|
108
|
+
- `~/.tunnelbox/remote-pairing.hermes.txt` + `remote-pairing.json` — 最新配对信息
|
|
109
|
+
- `~/.tunnelbox/tunnelbox.log` — 适配器日志
|
|
110
|
+
|
|
111
|
+
## 卸载
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm uninstall -g @tunnelbox/hermes
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
仅当不再使用任何 tunnelbox 适配器时,才删除 `~/.tunnelbox`(各适配器共享状态)。
|
|
118
|
+
|
|
119
|
+
## 开发
|
|
120
|
+
|
|
121
121
|
实现细节(协议映射、源码结构、决策记录、待实测项)保存在源码仓库中本包旁的 `DEV.md` 与仓库 `docs/` 下。
|
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,8 @@ function envelope(type, payload, id) {
|
|
|
14
14
|
}
|
|
15
15
|
var BASE_DELAY = 1e3;
|
|
16
16
|
var MAX_DELAY = 15e3;
|
|
17
|
+
var KEEPALIVE_INTERVAL_MS = 15e3;
|
|
18
|
+
var KEEPALIVE_TIMEOUT_MS = 25e3;
|
|
17
19
|
var RelayClient = class {
|
|
18
20
|
ws = null;
|
|
19
21
|
url = "";
|
|
@@ -23,6 +25,11 @@ var RelayClient = class {
|
|
|
23
25
|
retry = 0;
|
|
24
26
|
stopped = false;
|
|
25
27
|
connected = false;
|
|
28
|
+
reconnectScheduled = false;
|
|
29
|
+
/** 保活心跳定时器(onopen 启动 / onclose 停止) */
|
|
30
|
+
keepaliveTimer = null;
|
|
31
|
+
/** 最近一次收到服务端帧的时间(含心跳回执 pong),用于静默断流检测 */
|
|
32
|
+
lastInbound = 0;
|
|
26
33
|
get isConnected() {
|
|
27
34
|
return this.connected;
|
|
28
35
|
}
|
|
@@ -32,6 +39,8 @@ var RelayClient = class {
|
|
|
32
39
|
this.handlers = handlers;
|
|
33
40
|
this.stopped = false;
|
|
34
41
|
this.retry = 0;
|
|
42
|
+
this.reconnectScheduled = false;
|
|
43
|
+
this.stopKeepalive();
|
|
35
44
|
this.open();
|
|
36
45
|
}
|
|
37
46
|
send(env) {
|
|
@@ -47,6 +56,8 @@ var RelayClient = class {
|
|
|
47
56
|
}
|
|
48
57
|
close() {
|
|
49
58
|
this.stopped = true;
|
|
59
|
+
this.reconnectScheduled = false;
|
|
60
|
+
this.stopKeepalive();
|
|
50
61
|
if (this.timer) clearTimeout(this.timer);
|
|
51
62
|
this.timer = null;
|
|
52
63
|
if (this.ws) {
|
|
@@ -66,11 +77,14 @@ var RelayClient = class {
|
|
|
66
77
|
ws.onopen = () => {
|
|
67
78
|
this.connected = true;
|
|
68
79
|
this.retry = 0;
|
|
80
|
+
this.startKeepalive();
|
|
69
81
|
this.handlers?.onOpen();
|
|
70
82
|
};
|
|
71
83
|
ws.onmessage = (ev) => {
|
|
84
|
+
this.lastInbound = Date.now();
|
|
72
85
|
try {
|
|
73
86
|
const msg = JSON.parse(String(ev.data));
|
|
87
|
+
if (msg.type === "pong") return;
|
|
74
88
|
this.handlers?.onMessage(msg);
|
|
75
89
|
} catch {
|
|
76
90
|
}
|
|
@@ -79,6 +93,7 @@ var RelayClient = class {
|
|
|
79
93
|
ws.onclose = () => {
|
|
80
94
|
this.connected = false;
|
|
81
95
|
this.ws = null;
|
|
96
|
+
this.stopKeepalive();
|
|
82
97
|
this.handlers?.onClose();
|
|
83
98
|
this.scheduleReconnect();
|
|
84
99
|
};
|
|
@@ -88,10 +103,52 @@ var RelayClient = class {
|
|
|
88
103
|
}
|
|
89
104
|
}
|
|
90
105
|
scheduleReconnect() {
|
|
91
|
-
if (this.stopped) return;
|
|
106
|
+
if (this.stopped || this.reconnectScheduled) return;
|
|
107
|
+
this.reconnectScheduled = true;
|
|
92
108
|
const delay = Math.min(BASE_DELAY * 2 ** this.retry, MAX_DELAY);
|
|
93
109
|
this.retry++;
|
|
94
|
-
this.timer = setTimeout(() =>
|
|
110
|
+
this.timer = setTimeout(() => {
|
|
111
|
+
this.reconnectScheduled = false;
|
|
112
|
+
this.open();
|
|
113
|
+
}, delay);
|
|
114
|
+
}
|
|
115
|
+
/** 启动保活心跳:每周期发 ping,长时间收不到任何帧则判定断流主动重连。 */
|
|
116
|
+
startKeepalive() {
|
|
117
|
+
this.stopKeepalive();
|
|
118
|
+
this.lastInbound = Date.now();
|
|
119
|
+
this.keepaliveTimer = setInterval(() => {
|
|
120
|
+
if (!this.connected || !this.ws) return;
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
if (now - this.lastInbound > KEEPALIVE_TIMEOUT_MS) {
|
|
123
|
+
this.forceReconnect();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
this.ws.send(JSON.stringify({ type: "ping", payload: {}, ts: now }));
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}, KEEPALIVE_INTERVAL_MS);
|
|
131
|
+
}
|
|
132
|
+
stopKeepalive() {
|
|
133
|
+
if (this.keepaliveTimer !== null) {
|
|
134
|
+
clearInterval(this.keepaliveTimer);
|
|
135
|
+
this.keepaliveTimer = null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** 判定连接已死:关闭当前 socket 并调度重连(沿用指数退避,onclose 去重)。 */
|
|
139
|
+
forceReconnect() {
|
|
140
|
+
if (this.stopped) return;
|
|
141
|
+
this.connected = false;
|
|
142
|
+
this.stopKeepalive();
|
|
143
|
+
const w = this.ws;
|
|
144
|
+
this.ws = null;
|
|
145
|
+
if (w) {
|
|
146
|
+
try {
|
|
147
|
+
w.close();
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
this.scheduleReconnect();
|
|
95
152
|
}
|
|
96
153
|
};
|
|
97
154
|
var zh_CN_default = {
|
|
@@ -110,6 +167,7 @@ var zh_CN_default = {
|
|
|
110
167
|
"agent.log.pendingResent": "session.pending: \u8865\u53D1 {session} \u79EF\u538B\u8BF7\u6C42 {count} \u6761",
|
|
111
168
|
"agent.log.unhandledType": "\u672A\u5904\u7406\u7684\u6D88\u606F\u7C7B\u578B: {type}",
|
|
112
169
|
"events.log.fwdPermission": "\u8F6C\u53D1\u6743\u9650\u8BF7\u6C42\u5230\u624B\u673A (id={id} tool={tool} {version})",
|
|
170
|
+
"events.log.sessionDeleted": "\u8F6C\u53D1 session.deleted \u5230\u624B\u673A (session={session})",
|
|
113
171
|
"events.log.permissionReplied": "opencode \u56DE\u6267 permission.replied (id={id} {response})",
|
|
114
172
|
"events.log.questionRejected": "opencode \u56DE\u6267 question.rejected (id={id})",
|
|
115
173
|
"events.log.questionReplied": "opencode \u56DE\u6267 question.replied (id={id})",
|
|
@@ -275,6 +333,7 @@ var zh_TW_default = {
|
|
|
275
333
|
"agent.log.pendingResent": "session.pending: \u88DC\u767C {session} \u7A4D\u58D3\u8ACB\u6C42 {count} \u689D",
|
|
276
334
|
"agent.log.unhandledType": "\u672A\u8655\u7406\u7684\u8A0A\u606F\u985E\u578B: {type}",
|
|
277
335
|
"events.log.fwdPermission": "\u8F49\u767C\u6B0A\u9650\u8ACB\u6C42\u5230\u624B\u6A5F (id={id} tool={tool} {version})",
|
|
336
|
+
"events.log.sessionDeleted": "\u8F49\u767C session.deleted \u5230\u624B\u6A5F (session={session})",
|
|
278
337
|
"events.log.permissionReplied": "opencode \u56DE\u57F7 permission.replied (id={id} {response})",
|
|
279
338
|
"events.log.questionRejected": "opencode \u56DE\u57F7 question.rejected (id={id})",
|
|
280
339
|
"events.log.questionReplied": "opencode \u56DE\u57F7 question.replied (id={id})",
|
|
@@ -440,6 +499,7 @@ var en_US_default = {
|
|
|
440
499
|
"agent.log.pendingResent": "session.pending: resent {count} pending request(s) for {session}",
|
|
441
500
|
"agent.log.unhandledType": "Unhandled message type: {type}",
|
|
442
501
|
"events.log.fwdPermission": "Forwarded permission request to phone (id={id} tool={tool} {version})",
|
|
502
|
+
"events.log.sessionDeleted": "Forwarded session.deleted to phone (session={session})",
|
|
443
503
|
"events.log.permissionReplied": "opencode acked permission.replied (id={id} {response})",
|
|
444
504
|
"events.log.questionRejected": "opencode acked question.rejected (id={id})",
|
|
445
505
|
"events.log.questionReplied": "opencode acked question.replied (id={id})",
|
|
@@ -605,6 +665,7 @@ var ja_JP_default = {
|
|
|
605
665
|
"agent.log.pendingResent": "session.pending: {session} \u306E\u4FDD\u7559\u30EA\u30AF\u30A8\u30B9\u30C8 {count} \u4EF6\u3092\u518D\u9001",
|
|
606
666
|
"agent.log.unhandledType": "\u672A\u51E6\u7406\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u30BF\u30A4\u30D7: {type}",
|
|
607
667
|
"events.log.fwdPermission": "\u6A29\u9650\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u643A\u5E2F\u3078\u8EE2\u9001 (id={id} tool={tool} {version})",
|
|
668
|
+
"events.log.sessionDeleted": "session.deleted \u3092\u643A\u5E2F\u3078\u8EE2\u9001 (session={session})",
|
|
608
669
|
"events.log.permissionReplied": "opencode \u5FDC\u7B54 permission.replied (id={id} {response})",
|
|
609
670
|
"events.log.questionRejected": "opencode \u5FDC\u7B54 question.rejected (id={id})",
|
|
610
671
|
"events.log.questionReplied": "opencode \u5FDC\u7B54 question.replied (id={id})",
|
|
@@ -770,6 +831,7 @@ var ko_KR_default = {
|
|
|
770
831
|
"agent.log.pendingResent": "session.pending: {session} \uBCF4\uB958 \uC694\uCCAD {count}\uAC74 \uC7AC\uC804\uC1A1",
|
|
771
832
|
"agent.log.unhandledType": "\uCC98\uB9AC\uB418\uC9C0 \uC54A\uC740 \uBA54\uC2DC\uC9C0 \uC720\uD615: {type}",
|
|
772
833
|
"events.log.fwdPermission": "\uAD8C\uD55C \uC694\uCCAD\uC744 \uD734\uB300\uD3F0\uC73C\uB85C \uC804\uB2EC (id={id} tool={tool} {version})",
|
|
834
|
+
"events.log.sessionDeleted": "session.deleted\uB97C \uD734\uB300\uD3F0\uC73C\uB85C \uC804\uB2EC (session={session})",
|
|
773
835
|
"events.log.permissionReplied": "opencode \uC751\uB2F5 permission.replied (id={id} {response})",
|
|
774
836
|
"events.log.questionRejected": "opencode \uC751\uB2F5 question.rejected (id={id})",
|
|
775
837
|
"events.log.questionReplied": "opencode \uC751\uB2F5 question.replied (id={id})",
|
|
@@ -935,6 +997,7 @@ var fr_FR_default = {
|
|
|
935
997
|
"agent.log.pendingResent": "session.pending : renvoi de {count} demande(s) en attente pour {session}",
|
|
936
998
|
"agent.log.unhandledType": "Type de message non g\xE9r\xE9 : {type}",
|
|
937
999
|
"events.log.fwdPermission": "Demande d'autorisation transmise au t\xE9l\xE9phone (id={id} tool={tool} {version})",
|
|
1000
|
+
"events.log.sessionDeleted": "session.deleted transmise au t\xE9l\xE9phone (session={session})",
|
|
938
1001
|
"events.log.permissionReplied": "opencode a accus\xE9 permission.replied (id={id} {response})",
|
|
939
1002
|
"events.log.questionRejected": "opencode a accus\xE9 question.rejected (id={id})",
|
|
940
1003
|
"events.log.questionReplied": "opencode a accus\xE9 question.replied (id={id})",
|
|
@@ -1100,6 +1163,7 @@ var de_DE_default = {
|
|
|
1100
1163
|
"agent.log.pendingResent": "session.pending: {count} ausstehende Anfrage(n) f\xFCr {session} erneut gesendet",
|
|
1101
1164
|
"agent.log.unhandledType": "Nicht behandelter Nachrichtentyp: {type}",
|
|
1102
1165
|
"events.log.fwdPermission": "Berechtigungsanfrage an Telefon weitergeleitet (id={id} tool={tool} {version})",
|
|
1166
|
+
"events.log.sessionDeleted": "session.deleted an Telefon weitergeleitet (session={session})",
|
|
1103
1167
|
"events.log.permissionReplied": "opencode quittierte permission.replied (id={id} {response})",
|
|
1104
1168
|
"events.log.questionRejected": "opencode quittierte question.rejected (id={id})",
|
|
1105
1169
|
"events.log.questionReplied": "opencode quittierte question.replied (id={id})",
|
|
@@ -1265,6 +1329,7 @@ var es_ES_default = {
|
|
|
1265
1329
|
"agent.log.pendingResent": "session.pending: reenviadas {count} solicitud(es) pendientes para {session}",
|
|
1266
1330
|
"agent.log.unhandledType": "Tipo de mensaje no gestionado: {type}",
|
|
1267
1331
|
"events.log.fwdPermission": "Solicitud de permiso reenviada al tel\xE9fono (id={id} tool={tool} {version})",
|
|
1332
|
+
"events.log.sessionDeleted": "session.deleted reenviada al tel\xE9fono (session={session})",
|
|
1268
1333
|
"events.log.permissionReplied": "opencode confirm\xF3 permission.replied (id={id} {response})",
|
|
1269
1334
|
"events.log.questionRejected": "opencode confirm\xF3 question.rejected (id={id})",
|
|
1270
1335
|
"events.log.questionReplied": "opencode confirm\xF3 question.replied (id={id})",
|
|
@@ -2017,7 +2082,7 @@ async function removeSession(sessionID) {
|
|
|
2017
2082
|
}
|
|
2018
2083
|
|
|
2019
2084
|
// src/bridge.ts
|
|
2020
|
-
var VERSION = true ? "0.1.
|
|
2085
|
+
var VERSION = true ? "0.1.11" : "0.0.0-dev";
|
|
2021
2086
|
var AGENT_TYPE = "hermes";
|
|
2022
2087
|
var HermesBridge = class {
|
|
2023
2088
|
constructor(cfg) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tunnelbox/hermes",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "tunnelbox 的 Hermes 适配器(B 类,CLI oneshot 桥接):手机远程驱动本机 Hermes Agent(hermes chat -Q -q + --resume 续聊,会话/流式/中止/删除;headless 无手机审批)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"qrcode": "^1.5.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@tunnelbox/core": "0.1.
|
|
37
|
+
"@tunnelbox/core": "0.1.11",
|
|
38
38
|
"@types/node": "^22.0.0",
|
|
39
39
|
"@types/qrcode": "^1.5.5",
|
|
40
40
|
"esbuild": "^0.24.0",
|