dsh-plugin-exe 0.0.0-stage → 0.1.0

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 CHANGED
@@ -1,3 +1,137 @@
1
- # Temporary Holding Version
1
+ # dsh-plugin-exe
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 把 eXe 的设备交给 DeepSeek Harness:在**你自己的机器**上跑 shell、把截图作为图片交给模型。
4
+
5
+ > A DeepSeek Harness bundle for [eXe](https://exe.ntnl.io) — three tools
6
+ > (`exe_devices` / `exe_bash` / `exe_image`) that reach the user's own devices
7
+ > through the relay, with no extra binary and no platform account.
8
+
9
+ ## 它是什么
10
+
11
+ eXe 是一层**执行面**:设备只执行它 `trust` 里那把公钥签过的指令,云端只做信封中转,
12
+ 读不懂内容([协议](../../docs/PROTOCOL.md))。eXe 自带两样东西——设备端 `exe`
13
+ 和调用方 `exe mcp`(一个 stdio MCP server)。
14
+
15
+ 这个插件是**第二个调用方实现**,跑在 harness 进程里:
16
+
17
+ - 不需要 `exe` 二进制、不 spawn 子进程、不绕一圈 MCP;
18
+ - 截图以 image 内容块交给模型,而不是把 base64 塞进文本;
19
+ - 用的是这台机器上 `exe mcp` **已有的那把钥匙**(`~/.config/exe/mcp.toml`),
20
+ 所以设备上已经信任过的地址,一个字都不用改。
21
+ - 所有参数(中继入口、钥匙、设备表、工具名前缀、超时)都是配置,不是源码常量。
22
+
23
+ ## 安装
24
+
25
+ ```sh
26
+ # 装进 web profile(也可以换 tui / headless)
27
+ dsh plugin --profile web add dsh-plugin-exe
28
+
29
+ # 看一眼这一层确实挂上了
30
+ dsh --profile web --dump-config | grep -A2 dsh-plugin-exe
31
+ ```
32
+
33
+ 插件不自带钥匙。在这台跑 harness 的机器上:
34
+
35
+ ```sh
36
+ exe mcp init # 生成调用方钥匙,打印它的地址
37
+ exe mcp # 或者直接跑一次,也会打印地址
38
+ ```
39
+
40
+ 把打印出的地址加到每台设备上(设备端执行):
41
+
42
+ ```sh
43
+ exe trust <调用方地址>
44
+ ```
45
+
46
+ 装好后在会话里说「用 exe_devices 看看我能用哪些机器」,第一个工具会返回
47
+ `caller <地址>` 与每台设备的在线状态——地址就是上面那串,抄到设备上即可。
48
+
49
+ ## 三个工具
50
+
51
+ | 工具 | 做什么 |
52
+ |---|---|
53
+ | `exe_devices` | 列出配置里的设备,以及是否已登记、是否在线;输出第一行是调用方地址 |
54
+ | `exe_bash` | 在**远端设备**上执行 shell(`device` / `command` / `cwd` / `timeout_seconds`) |
55
+ | `exe_image` | 读设备上的图片,作为图片交给模型(截图 → 看) |
56
+
57
+ 工具名带前缀是刻意的:harness 把所有工具拍平成一张表,每个 agent 本来就有自己的
58
+ `bash`,裸名会被内建工具或先到的插件遮蔽。前缀可以改(见下)。
59
+
60
+ `exe_bash` 的工具描述里写明了三件模型必须知道的事:命令在设备上以设备用户的身份跑、
61
+ 没有沙箱也没有确认;每次调用都是一次中继往返,能一条命令做完就别拆成十条;
62
+ **状态为 `lost` 的调用可能已经执行过,不要盲目重试**。
63
+
64
+ ## 配置
65
+
66
+ 装好后零配置即可用。需要改的时候,在自己的 profile 的 `cordis.patch.yml` 里覆盖,
67
+ 不要改这个包:
68
+
69
+ ```yaml
70
+ - id: dsh-plugin-exe
71
+ config:
72
+ toolPrefix: box # 工具名变成 box_devices / box_bash / box_image
73
+ endpoints:
74
+ - https://exe.ntnl.io
75
+ - https://exe.ntnl.top
76
+ devices: # 与 mcp.toml 的 [devices] 合并
77
+ macmini: <设备地址>
78
+ defaultTimeoutSeconds: 60 # 模型没说时,命令最多跑多久
79
+ maxCallSeconds: 3600 # 一次调用(含等待)的生命上限
80
+ ```
81
+
82
+ | 字段 | 默认 | 说明 |
83
+ |---|---|---|
84
+ | `endpoints` | `https://exe.ntnl.io`、`https://exe.ntnl.top` | 中继入口,按顺序探测 `/api/health`,选中回答 `service: "exe"` 的那个 |
85
+ | `secret` | 空 | 调用方私钥 `base58(seed32 ‖ pub32)`;留空则读 `configPath`。给没有 eXe CLI 的机器用 |
86
+ | `configPath` | `~/.config/exe/mcp.toml` | 钥匙与设备表的来源,和 `exe mcp` 是同一个文件(`EXE_CONFIG_DIR` 也认) |
87
+ | `devices` | `{}` | 设备标签 → 地址,覆盖在文件的 `[devices]` 之上 |
88
+ | `toolPrefix` | `exe` | 工具名前缀 |
89
+ | `defaultTimeoutSeconds` | `60` | 命令默认超时 |
90
+ | `maxCallSeconds` | `3600` | 单次调用生命上限(协议上限也是一小时) |
91
+
92
+ 设备参数既可以写标签(`lab`),也可以直接写地址——地址本来就是公钥,认得出来。
93
+
94
+ ## 和 `exe mcp` 的关系
95
+
96
+ 两者是**同等资格的调用方**,只是形态不同:
97
+
98
+ | | `exe mcp` | 本插件 |
99
+ |---|---|---|
100
+ | 进程 | harness spawn 一个 Rust 子进程 | 在 harness 进程内 |
101
+ | 配置 | `~/.config/exe/mcp.toml` | 同一个文件(或插件配置) |
102
+ | 图片 | MCP 的 image 内容块 | harness 的附件服务(durable ref) |
103
+ | 钥匙 | 机器上的 | 同一把 |
104
+
105
+ 已经用 `exe mcp` + MCP 接好的 harness,不装这个插件也能干活;装上它换来的是:
106
+ 不需要 Rust 二进制、图片进的是附件存储、配置和 harness 一起走。
107
+
108
+ ## 安全与边界
109
+
110
+ - **钥匙不出本机。** 云端只看到地址、密文和签名;设备的 `trust` 是唯一权威。
111
+ - **命令不经过沙箱。** `exe_bash` 在设备上以该用户的身份直接执行,和人类敲的一样。
112
+ 这个插件不提供确认环节——要收紧,就在 harness 的权限预设(`permission.defaultPreset`)
113
+ 和设备的 `trust` 列表上收紧,而不是在这里加一层假确认。
114
+ - **结果可以被取走一次。** 拿到结果后插件会 `ack`,中继随即删掉这一行与附件。
115
+ - **`lost` 不是失败。** 设备进程死在执行中间时,命令可能已经跑过;插件把这句话原样
116
+ 交给模型,重试与否由人决定。
117
+
118
+ ## 开发
119
+
120
+ 这个包是 eXe 仓库里的 workspace 成员,协议实现直接引用根目录的 `shared/protocol.ts`
121
+ (同一份被 Rust 向量钉住的文件),构建时内联进 `lib/index.js`:
122
+
123
+ ```sh
124
+ pnpm --dir plugins/dsh build # esbuild → lib/index.js(单文件 ESM)
125
+ pnpm --dir plugins/dsh typecheck # tsc --noEmit
126
+ pnpm --dir plugins/dsh test # node --test:配置、协议往返、工具行为
127
+ ```
128
+
129
+ 测试里的「设备」是真的:`tests/fake-device.ts` 会验调用方签名、解封调用、签回结果,
130
+ 走的是 `docs/PROTOCOL.md` 里那四步,所以封印、签名、轮询写错了会在这里挂掉。
131
+
132
+ 发布:`dsh-v*` 标签触发 `.github/workflows/publish-dsh-plugin.yml`,
133
+ 先校验标签与 `plugins/dsh/package.json` 一致,再 `npm publish --access public --provenance`。
134
+
135
+ 兼容性:目标 DSH 0.2.x。`peerDependencies` 里 `@deepseek-ai/dsh-*` 写的是 `*` 而不是
136
+ 区间——DSH 是预发布版本线,`^0.2.1-rc.5` 这类区间接不住 `0.2.2-rc.1`(semver 的预发布
137
+ 规则),写了反而会在升级时报错。
@@ -0,0 +1,15 @@
1
+ # The layer this bundle adds to a dsh profile: one row, three tools.
2
+ #
3
+ # Everything the row may need is a schema default, so an installed bundle works
4
+ # with no configuration at all — it reads the caller key `exe mcp` already keeps
5
+ # on this machine. Users override any field here, in their profile's own
6
+ # cordis.patch.yml, without touching this package:
7
+ #
8
+ # - id: dsh-plugin-exe
9
+ # config:
10
+ # toolPrefix: box
11
+ # devices:
12
+ # macmini: <device address>
13
+ - insert:
14
+ - id: dsh-plugin-exe
15
+ name: dsh-plugin-exe