@ban-sky/dsh-github 0.0.0-stage → 0.4.2
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/LICENSE +21 -0
- package/README.md +246 -2
- package/README.zh.md +210 -0
- package/client.js +1689 -0
- package/cordis.patch.yml +8 -0
- package/icon.svg +10 -0
- package/index.js +78 -0
- package/lib/config.js +180 -0
- package/lib/dsh-go.js +674 -0
- package/lib/errors.js +67 -0
- package/lib/git.js +677 -0
- package/lib/github-api.js +390 -0
- package/lib/http.js +113 -0
- package/lib/service.js +974 -0
- package/lib/tools.js +102 -0
- package/lib/validate.js +197 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +125 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ban-sky
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,247 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-github
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](README.md) | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
GitHub integration for the DeepSeek Harness Web UI. Sign in with GitHub's
|
|
6
|
+
official OAuth **device flow**, then create, list, clone, pull, commit and push
|
|
7
|
+
repositories from a single Settings page — or work on the current session folder
|
|
8
|
+
directly from a button above the composer. An optional agent tool exposes the
|
|
9
|
+
same operations to the model.
|
|
10
|
+
|
|
11
|
+
Security comes first: the access token lives only in the local DSH credential
|
|
12
|
+
store and is never written to the settings file, a git remote, git config, a
|
|
13
|
+
command line, a log line or a Tool result.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Official sign-in** — GitHub OAuth device flow (`https://github.com/login/device`).
|
|
18
|
+
No password is ever handled by the plugin.
|
|
19
|
+
- **Repositories** — list the account's repositories (owner, collaborator and
|
|
20
|
+
organization member), create a new repository (public/private, optional
|
|
21
|
+
README), clone any `owner/name` into the configured clone root.
|
|
22
|
+
- **Local checkouts** — inspect branch, tracking state, ahead/behind and working
|
|
23
|
+
tree changes; `git pull --ff-only`; commit selected paths (or everything) and
|
|
24
|
+
push.
|
|
25
|
+
- **Session folder actions** — the GitHub button above the composer clones into,
|
|
26
|
+
publishes, pulls, updates (fetch) and pushes the current session working
|
|
27
|
+
directory.
|
|
28
|
+
- **Agent tool** (`github`, off by default) — the same operations as a Tool, so
|
|
29
|
+
the model can act on GitHub when the user enables it.
|
|
30
|
+
- **Bilingual UI** — Chinese and English, following the Harness locale.
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- Node.js 20+ (the Harness already provides this).
|
|
35
|
+
- `git` on `PATH` (HTTPS remotes only).
|
|
36
|
+
- A GitHub **OAuth App** with the *device flow* enabled — one minute of setup,
|
|
37
|
+
see below. (A device-flow-capable OAuth App client ID is public information;
|
|
38
|
+
it is not a secret.)
|
|
39
|
+
- Until an OAuth App is configured the plugin still opens; it explains what is
|
|
40
|
+
missing instead of failing silently.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
**From npm:**
|
|
45
|
+
|
|
46
|
+
Use the Harness `plugin_manager` tool with `install_bundle` and the exact version
|
|
47
|
+
`@ban-sky/dsh-github@0.4.2` as the target. The name carries a scope because
|
|
48
|
+
`dsh-github` is taken on npm; the Release packages contain the same code.
|
|
49
|
+
|
|
50
|
+
**From a release (recommended):**
|
|
51
|
+
|
|
52
|
+
Every release ships two packages; pick the one for your host. Both unpack to
|
|
53
|
+
`package/`:
|
|
54
|
+
|
|
55
|
+
| Package | For |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `dsh-github-<version>.tgz` | DSH Web / desktop: the Web UI only |
|
|
58
|
+
| `dsh-github-<version>-dsh-go.tgz` | the DSH GO phone app: Web UI plus the native panel and composer entries |
|
|
59
|
+
|
|
60
|
+
Open the [Releases](https://github.com/ban-sky/dsh-github/releases) page in a
|
|
61
|
+
browser where you are signed in, or use the GitHub CLI:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
gh release download v0.4.2 -R ban-sky/dsh-github # needs gh auth login first
|
|
65
|
+
sha256sum -c SHA256SUMS.txt
|
|
66
|
+
tar -xzf dsh-github-0.4.2.tgz # web
|
|
67
|
+
tar -xzf dsh-github-0.4.2-dsh-go.tgz # phone
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> The plain `releases/download/...` URL of a **private** repository only accepts
|
|
71
|
+
> a browser session; an API token gets a 404 there. For scripts use
|
|
72
|
+
> `gh release download`, or call
|
|
73
|
+
> `GET /repos/ban-sky/dsh-github/releases/assets/<id>` with
|
|
74
|
+
> `Accept: application/octet-stream`.
|
|
75
|
+
|
|
76
|
+
Then install `package/` with the Harness `plugin_manager` tool
|
|
77
|
+
(`install_bundle target:/path/to/package`). On a phone you can also run
|
|
78
|
+
`dsh plugin --profile web add /path/to/package`.
|
|
79
|
+
|
|
80
|
+
**From a source checkout:** install this directory as a bundle (Settings →
|
|
81
|
+
Plugins → install from directory, or the Harness `plugin_manager` tool with
|
|
82
|
+
`install_bundle`). The bundle mounts:
|
|
83
|
+
|
|
84
|
+
- Host: the `/api/github` authenticated route, the `github` authorization flow,
|
|
85
|
+
and (when enabled) the `github` tool.
|
|
86
|
+
- Client: one `Settings → GitHub` page.
|
|
87
|
+
|
|
88
|
+
## DSH GO
|
|
89
|
+
|
|
90
|
+
The plugin also speaks the DSH GO surface: `package.json.dshGo` (protocol v1)
|
|
91
|
+
declares the native panel and the composer entries (`minApp` 1.0.13), and the
|
|
92
|
+
`-dsh-go.tgz` package ships `lib/dsh-go.js`. The npm package carries the Web UI
|
|
93
|
+
and that surface together, so `@ban-sky/dsh-github` fits either host.
|
|
94
|
+
|
|
95
|
+
This plugin is not submitted to any plugin market. npm and the GitHub Release are
|
|
96
|
+
the only distribution channels.
|
|
97
|
+
|
|
98
|
+
## Setup: create the OAuth App
|
|
99
|
+
|
|
100
|
+
1. GitHub → **Settings → Developer settings → OAuth Apps → New OAuth App**.
|
|
101
|
+
2. Application name: anything, e.g. `DSH`; Homepage URL: anything you control
|
|
102
|
+
(`https://github.com/` is fine).
|
|
103
|
+
3. **Enable "Device flow"** on the app page. This is required: the plugin uses
|
|
104
|
+
the device authorization grant, not a redirect callback.
|
|
105
|
+
4. Copy the **Client ID** (`Iv1.…` or `Ov23…`) into **Settings → GitHub →
|
|
106
|
+
Client ID** and save.
|
|
107
|
+
|
|
108
|
+
## Sign in
|
|
109
|
+
|
|
110
|
+
1. Settings → GitHub → **Sign in with GitHub**.
|
|
111
|
+
2. A one-time code appears. Open <https://github.com/login/device> (the plugin
|
|
112
|
+
only ever links to that exact host) and enter the code.
|
|
113
|
+
3. Approve the request. The page updates as soon as GitHub confirms, and shows
|
|
114
|
+
the signed-in account.
|
|
115
|
+
|
|
116
|
+
The requested scopes are `repo` (read/write access to repositories, including
|
|
117
|
+
private ones) and `workflow` (lets a push create or update files under
|
|
118
|
+
`.github/workflows`). GitHub shows both on the approval screen.
|
|
119
|
+
|
|
120
|
+
If you signed in with an older release, the stored grant only has `repo`, so
|
|
121
|
+
GitHub refuses pushes that touch workflow files ("refusing to allow an OAuth App
|
|
122
|
+
to create or update workflow ... without `workflow` scope"). Settings → GitHub
|
|
123
|
+
then shows a warning with a **Re-authorize** button; one more device-flow
|
|
124
|
+
approval adds the scope, and the account, settings and local repositories are
|
|
125
|
+
left untouched.
|
|
126
|
+
|
|
127
|
+
Sign out deletes the stored grant from the credential store immediately; the
|
|
128
|
+
token is also invalidated locally for every in-flight operation.
|
|
129
|
+
|
|
130
|
+
## Session folder actions
|
|
131
|
+
|
|
132
|
+
The composer button sends only the **sessionId** to the Host. The Host resolves
|
|
133
|
+
the session's committed working directory through the agents and
|
|
134
|
+
workingDirectory services, so the Client can never name an arbitrary path.
|
|
135
|
+
|
|
136
|
+
| Action | Behaviour |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| Clone | Clones `owner/name` into `<session folder>/<name>`; refuses existing, non-empty or symlinked targets. |
|
|
139
|
+
| Create and push | Creates the repository in your account (private by default), runs `git init -b main` in the session folder, adds `origin`, commits and pushes. Only runs when the folder itself is the repository root; refuses when it already contains a repository or lives inside another one. |
|
|
140
|
+
| Pull | `git pull --ff-only` on the session repository. |
|
|
141
|
+
| Update | `git fetch --prune`, refreshing ahead/behind without touching the working tree. |
|
|
142
|
+
| Commit | Commits every change (or selected files), optionally followed by a push. |
|
|
143
|
+
|
|
144
|
+
When the session folder is not itself a repository, the plugin uses the **single**
|
|
145
|
+
immediate child repository (the one just cloned) for later actions. With more
|
|
146
|
+
than one child repository it refuses to guess.
|
|
147
|
+
|
|
148
|
+
The button adapts to the viewport: on desktop it shows the “GitHub” label, and on
|
|
149
|
+
the phone web UI (viewport ≤ 640px) it collapses to a 26px branch icon so a narrow
|
|
150
|
+
composer can never squeeze the label into a vertical column. The icon button keeps
|
|
151
|
+
its `aria-label`, so screen readers and tooltips still name it.
|
|
152
|
+
|
|
153
|
+
## DSH GO (the phone app)
|
|
154
|
+
|
|
155
|
+
DSH GO draws its own Flutter UI, so it cannot render the Web settings page. On a
|
|
156
|
+
host that provides the `dshGo` service the plugin registers a native surface that
|
|
157
|
+
shares this plugin's one service instance (one token, one settings file, one
|
|
158
|
+
sign-in state machine):
|
|
159
|
+
|
|
160
|
+
- **GitHub panel**: account state, device-code sign-in (copy the code, open the
|
|
161
|
+
authorization page, cancel, sign out), the Agent-tool switch, and
|
|
162
|
+
refresh/fetch/pull/push for the current session folder. Reachable from the add
|
|
163
|
+
sheet, the session details "Plugins" tab, or "All features".
|
|
164
|
+
- **Composer entries**: native form inputs are not available everywhere, so
|
|
165
|
+
text-driven work uses the message box: paste the Client ID and tap "Save ID",
|
|
166
|
+
type a commit message and tap "Commit", type `owner/name` and tap "Clone".
|
|
167
|
+
- An app that does not advertise the `form` / `textField` components gets the
|
|
168
|
+
panel without input fields, only those entries and their instructions, instead
|
|
169
|
+
of a form nobody can type into.
|
|
170
|
+
- Remove any older standalone adaptation package on the same host first, so two
|
|
171
|
+
GitHub panels do not compete.
|
|
172
|
+
|
|
173
|
+
Hosts without the `dshGo` service are unaffected: none of this code runs there.
|
|
174
|
+
|
|
175
|
+
## Settings
|
|
176
|
+
|
|
177
|
+
| Setting | Meaning |
|
|
178
|
+
| --- | --- |
|
|
179
|
+
| **Client ID** | Public Client ID of your OAuth App (requires device flow). |
|
|
180
|
+
| **Clone root** | Directory that clones must stay inside. Clones are placed at `<root>/<owner>/<name>`; paths that escape the root (including through symlinks) are rejected. Defaults to `~/github`. |
|
|
181
|
+
| **Agent tool** | Registers the `github` tool for the model. Takes effect on the next plugin mount (restart or re-save the plugin), as the page indicates. |
|
|
182
|
+
|
|
183
|
+
The settings file is `$DSH_HOME/dsh-github.json` (created with mode `0600`),
|
|
184
|
+
and it never contains the token.
|
|
185
|
+
|
|
186
|
+
## Agent tool
|
|
187
|
+
|
|
188
|
+
`github` accepts `action`:
|
|
189
|
+
|
|
190
|
+
| Action | Arguments | Result |
|
|
191
|
+
| --- | --- | --- |
|
|
192
|
+
| `status` | `path` | Sign-in state, settings and local checkouts |
|
|
193
|
+
| `list_repos` | — | Repositories of the signed-in account |
|
|
194
|
+
| `create_repo` | `name`, `description?`, `private?`, `autoInit?` | New repository |
|
|
195
|
+
| `clone` | `fullName` | Cloned checkout path + status |
|
|
196
|
+
| `pull` | `path` | Fast-forward pull result |
|
|
197
|
+
| `commit` | `path`, `message`, `files?` | Commit result, optionally appended to the next push |
|
|
198
|
+
| `push` | `path` | Push result (sets upstream on first push) |
|
|
199
|
+
|
|
200
|
+
Failures come back as `{ ok: false, code, message }` with the same redaction as
|
|
201
|
+
the UI.
|
|
202
|
+
|
|
203
|
+
## Security model
|
|
204
|
+
|
|
205
|
+
- Device flow only: the token is obtained from GitHub's token endpoint over
|
|
206
|
+
TLS and stored with `ctx.credentials.modifyRecord` as a `grant` record under
|
|
207
|
+
the key `dsh-github/token` (in `$DSH_HOME/.credentials.yaml`, mode `0600`).
|
|
208
|
+
- The token is read per operation; it is never cached in the settings file, the
|
|
209
|
+
UI payload, or the Tool description.
|
|
210
|
+
- Git never sees the token in `argv`, in a remote URL, or in git config. It is
|
|
211
|
+
passed only through the child process environment and answered by a one-shot
|
|
212
|
+
inline credential helper (`-c credential.helper=!…`); every configured
|
|
213
|
+
credential helper is cleared first and `GIT_TERMINAL_PROMPT=0` prevents any
|
|
214
|
+
interactive prompt or implicit credential persistence.
|
|
215
|
+
- Only `https://github.com/<owner>/<name>` remotes are used or accepted;
|
|
216
|
+
`http`, SSH, other hosts and URLs with embedded credentials are rejected.
|
|
217
|
+
Pull/push refuse to run on a checkout whose `origin` is not such a URL.
|
|
218
|
+
- Clone and commit paths are validated and contained: no `..`, no absolute
|
|
219
|
+
paths, no symlink escapes, no option-looking pathspecs.
|
|
220
|
+
- Every error message and Tool result is redacted (known tokens, `ghp_…`/
|
|
221
|
+
`github_pat_…` shapes and `user:password@` URL parts).
|
|
222
|
+
- The HTTP route is an authenticated Harness fetch endpoint (`POST
|
|
223
|
+
/api/github`); it accepts JSON only, caps the body at 1 MiB, and returns
|
|
224
|
+
business errors without leaking secrets.
|
|
225
|
+
|
|
226
|
+
## Limits
|
|
227
|
+
|
|
228
|
+
- One GitHub account per Harness profile.
|
|
229
|
+
- Git operations only (list/create/clone/pull/commit/push); no pull requests,
|
|
230
|
+
issues, releases or merges.
|
|
231
|
+
- No automatic credential refresh: when GitHub revokes or expires the grant,
|
|
232
|
+
the page asks you to sign in again.
|
|
233
|
+
|
|
234
|
+
## Layout
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
index.js Host entry (route, authorization flow, optional tool)
|
|
238
|
+
client.js Web UI: Settings → GitHub
|
|
239
|
+
lib/github-api.js Device flow + REST calls (token only in an auth header)
|
|
240
|
+
lib/git.js git invocations, remote/URL and path containment checks
|
|
241
|
+
lib/service.js State machine, dispatch, redaction boundary
|
|
242
|
+
lib/http.js POST /api/github handler
|
|
243
|
+
lib/tools.js `github` agent tool
|
|
244
|
+
lib/config.js Non-secret settings ($DSH_HOME/dsh-github.json)
|
|
245
|
+
lib/validate.js Owner/repo/path/message validation
|
|
246
|
+
lib/errors.js Error codes + secret redaction
|
|
247
|
+
```
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# dsh-github
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
DeepSeek Harness Web UI 的 GitHub 插件。用 GitHub 官方 OAuth **设备码流程**登录,
|
|
6
|
+
然后在设置页里创建、列出、克隆、拉取、提交、推送仓库,或在任意会话的输入框
|
|
7
|
+
下方直接操作该会话目录里的仓库;可选地把同样的能力注册成 agent 工具。
|
|
8
|
+
|
|
9
|
+
安全优先:访问令牌只存在本机 DSH 凭据库中,绝不写进设置文件、git remote、
|
|
10
|
+
git config、命令行参数、日志或 Tool 返回值。
|
|
11
|
+
|
|
12
|
+
## 功能
|
|
13
|
+
|
|
14
|
+
- **官方登录** —— GitHub OAuth 设备码流程(`https://github.com/login/device`),
|
|
15
|
+
插件不接触密码。
|
|
16
|
+
- **仓库管理** —— 列出账号可访问的仓库(自己的、协作者、组织成员),创建新仓库
|
|
17
|
+
(公开/私有,可选初始化 README),把 `owner/name` 克隆到配置的克隆根目录。
|
|
18
|
+
- **本地检出** —— 查看分支、上游跟踪、ahead/behind、工作区改动;`git pull
|
|
19
|
+
--ff-only`;提交指定路径(或全部改动)并推送。
|
|
20
|
+
- **会话目录操作** —— 每个会话的输入框旁有 GitHub 按钮:在会话工作目录里克隆仓库、
|
|
21
|
+
新建仓库并推送(发布当前目录)、拉取、更新(fetch)和提交/推送。
|
|
22
|
+
- **Agent 工具**(`github`,默认关闭)—— 与设置页相同的操作,开启后模型可使用。
|
|
23
|
+
- **中英双语界面**,跟随 Harness 的界面语言。
|
|
24
|
+
|
|
25
|
+
## 前置条件
|
|
26
|
+
|
|
27
|
+
- Node.js 20+(Harness 已提供)。
|
|
28
|
+
- `PATH` 中有 `git`(仅 HTTPS remote)。
|
|
29
|
+
- 一个启用了 **Device flow** 的 GitHub **OAuth App**:一分钟配置,见下。
|
|
30
|
+
(Client ID 是公开信息,不是密钥。)
|
|
31
|
+
- 未配置 OAuth App 时插件照常打开,会提示缺少什么,而不是静默失败。
|
|
32
|
+
|
|
33
|
+
## 安装
|
|
34
|
+
|
|
35
|
+
**从 npm 安装**:
|
|
36
|
+
|
|
37
|
+
在 Harness 里用 `plugin_manager` 的 `install_bundle`,target 写精确版本
|
|
38
|
+
`@ban-sky/dsh-github@0.4.2`。包名带 scope 是因为 `dsh-github` 在 npm 上已被他人
|
|
39
|
+
占用;Release 里的安装包内容相同。
|
|
40
|
+
|
|
41
|
+
**从 Release 下载安装包**(推荐):
|
|
42
|
+
|
|
43
|
+
每次发布出两个包,按宿主挑一个,都解出 `package/`:
|
|
44
|
+
|
|
45
|
+
| 安装包 | 给谁用 |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `dsh-github-<版本>.tgz` | DSH 网页版 / 桌面端:只有 Web UI |
|
|
48
|
+
| `dsh-github-<版本>-dsh-go.tgz` | DSH GO 手机 App:Web UI + 原生面板和输入框入口 |
|
|
49
|
+
|
|
50
|
+
在浏览器里登录 GitHub 后打开
|
|
51
|
+
[Releases](https://github.com/ban-sky/dsh-github/releases) 下载,或者用 GitHub CLI:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
gh release download v0.4.2 -R ban-sky/dsh-github # 需要先 gh auth login
|
|
55
|
+
sha256sum -c SHA256SUMS.txt
|
|
56
|
+
tar -xzf dsh-github-0.4.2.tgz # 网页版
|
|
57
|
+
tar -xzf dsh-github-0.4.2-dsh-go.tgz # 手机用这个
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
> 私有仓库的 `releases/download/...` 直链只认浏览器登录态,API 令牌访问会
|
|
61
|
+
> 404;脚本里请用 `gh release download`,或调
|
|
62
|
+
> `GET /repos/ban-sky/dsh-github/releases/assets/<id>` 并带
|
|
63
|
+
> `Accept: application/octet-stream`。
|
|
64
|
+
|
|
65
|
+
然后在 Harness 里用 `plugin_manager` 的 `install_bundle` 指向解出的
|
|
66
|
+
`package/` 目录即可;DSH GO 也可以直接 `dsh plugin --profile web add /path/to/package`。
|
|
67
|
+
|
|
68
|
+
**从源码目录安装**:把本仓库目录作为 bundle 安装(设置 → 插件 → 从目录安装,
|
|
69
|
+
或 `plugin_manager` 工具的 `install_bundle`)。安装后:
|
|
70
|
+
|
|
71
|
+
- Host 端:`/api/github` 认证路由、`github` 授权流程,以及(开启后)`github` 工具。
|
|
72
|
+
- Client 端:一个 `设置 → GitHub` 页面。
|
|
73
|
+
|
|
74
|
+
## DSH GO
|
|
75
|
+
|
|
76
|
+
插件同时提供 DSH GO 原生界面:`package.json` 的 `dshGo` 协议 v1 声明原生面板与
|
|
77
|
+
输入框入口(`minApp` 1.0.13),`-dsh-go.tgz` 里带 `lib/dsh-go.js`。npm 上的包同时
|
|
78
|
+
包含 Web UI 与这套界面,因此 `@ban-sky/dsh-github` 对两种宿主都适用。
|
|
79
|
+
|
|
80
|
+
本插件不提交任何插件市场,分发渠道只有 npm 与 GitHub Release。
|
|
81
|
+
|
|
82
|
+
## 配置:创建 OAuth App
|
|
83
|
+
|
|
84
|
+
1. GitHub → **Settings → Developer settings → OAuth Apps → New OAuth App**。
|
|
85
|
+
2. Application name 任意,如 `DSH`;Homepage URL 填你自己的地址
|
|
86
|
+
(`https://github.com/` 也可以)。
|
|
87
|
+
3. 在应用页面打开 **"Device flow"**。插件用的是设备授权,不依赖回调地址。
|
|
88
|
+
4. 复制 **Client ID**(`Iv1.…` 或 `Ov23…`)填入 **设置 → GitHub → Client ID** 并保存。
|
|
89
|
+
|
|
90
|
+
## 登录
|
|
91
|
+
|
|
92
|
+
1. 设置 → GitHub → **使用 GitHub 登录**。
|
|
93
|
+
2. 页面显示一次性设备码。打开 <https://github.com/login/device> 输入该码
|
|
94
|
+
(插件只会把这一精确地址渲染成链接)。
|
|
95
|
+
3. 在 GitHub 上批准。页面会自动刷新并显示已登录账号。
|
|
96
|
+
|
|
97
|
+
申请的 scope 是 `repo`(含私有仓库的读写权限)和 `workflow`(允许推送创建或修改
|
|
98
|
+
`.github/workflows` 下的文件)。GitHub 的批准页会显示这两个 scope。
|
|
99
|
+
|
|
100
|
+
如果你是用旧版本登录的,已存授权只有 `repo`,GitHub 会拒绝任何涉及工作流文件的推送
|
|
101
|
+
(报 "refusing to allow an OAuth App to create or update workflow ... without
|
|
102
|
+
`workflow` scope")。此时设置页会显示提示和 **重新授权** 按钮:再走一次设备码授权即可
|
|
103
|
+
补上 scope,账号、设置文件和本地仓库都不受影响。
|
|
104
|
+
|
|
105
|
+
退出登录会立即从凭据库删除授权记录,并让正在进行的操作失效。
|
|
106
|
+
|
|
107
|
+
## 会话目录操作
|
|
108
|
+
|
|
109
|
+
会话输入框下方的 GitHub 按钮只把 **sessionId** 发给 Host;Host 通过 agents 与
|
|
110
|
+
workingDirectory 服务自行解析该会话已提交的工作目录,客户端无法指定任意路径。
|
|
111
|
+
|
|
112
|
+
| 操作 | 说明 |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| 克隆 | 把 `owner/name` 克隆到 `<会话目录>/<name>`;目标已存在、非空、符号链接逃逸都会拒绝。 |
|
|
115
|
+
| 创建并推送 | 在账号下创建仓库(默认私有),在会话目录 `git init -b main`、添加 `origin`、提交并推送。仅当该目录本身是仓库根目录时执行;目录内已有仓库或位于别的仓库中会拒绝。 |
|
|
116
|
+
| 拉取 | 对会话目录里的仓库执行 `git pull --ff-only`。 |
|
|
117
|
+
| 更新 | `git fetch --prune`,刷新 ahead/behind,不改变工作区。 |
|
|
118
|
+
| 提交 | 提交全部改动(或指定文件),可选择随后推送。 |
|
|
119
|
+
|
|
120
|
+
会话目录本身不是仓库时,插件会识别目录里**唯一**的直接子仓库(即刚克隆的那个),
|
|
121
|
+
并在后续操作中沿用它;有多个子仓库时不作猜测。
|
|
122
|
+
|
|
123
|
+
按钮会随屏幕宽度自适应:桌面端显示“GitHub”文字标签,**手机网页版(视口宽度
|
|
124
|
+
≤ 640px)折叠成 26px 的分支图标按钮**,避免窄容器把文字挤成竖排;图标按钮仍带
|
|
125
|
+
`aria-label`,读屏软件与悬停提示照常可用。
|
|
126
|
+
|
|
127
|
+
## DSH GO 手机端
|
|
128
|
+
|
|
129
|
+
DSH GO 的手机界面是 Flutter 原生的,画不了 Web UI。在提供 `dshGo` 服务的宿主上,
|
|
130
|
+
插件会额外注册一套原生界面,和设置页共用同一个服务实例(同一份令牌、同一份配置、
|
|
131
|
+
同一个登录状态机):
|
|
132
|
+
|
|
133
|
+
- **GitHub 面板**:加号面板里的 GitHub、对话详情「插件」页签、或「全部功能」都能
|
|
134
|
+
打开。里面有账号状态、设备码登录(复制设备码 / 打开授权页 / 取消 / 退出)、
|
|
135
|
+
Agent 工具开关,以及针对当前对话目录的刷新、更新、拉取、推送。
|
|
136
|
+
- **输入框胶囊**:原生 App 不保证有能填的表单输入框,所以需要文字的操作走输入框
|
|
137
|
+
——把 Client ID 粘进去点「存 ID」,把提交信息写进去点「提交」,把 `owner/name`
|
|
138
|
+
写进去点加号里的「克隆」。
|
|
139
|
+
- App 没上报 `form` / `textField` 组件能力时,面板不画输入框,只保留上面这些入口
|
|
140
|
+
和说明,避免出现点不动的假表单。
|
|
141
|
+
- 同一台机器上如果装过早期的独立适配包,先卸掉它再用本插件,免得出现两个 GitHub
|
|
142
|
+
面板抢同一个位置。
|
|
143
|
+
|
|
144
|
+
网页版不受影响:没有 `dshGo` 服务的宿主上这些代码不会执行。
|
|
145
|
+
|
|
146
|
+
## 设置项
|
|
147
|
+
|
|
148
|
+
| 设置 | 含义 |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| **Client ID** | 你的 OAuth App 的公开 Client ID(需启用 Device flow)。 |
|
|
151
|
+
| **克隆根目录** | 克隆必须落在此目录内,实际路径为 `<root>/<owner>/<name>`;越界路径(含符号链接逃逸)一律拒绝。默认 `~/github`。 |
|
|
152
|
+
| **Agent 工具** | 为模型注册 `github` 工具。下次挂载插件后生效(重启或重新保存插件),页面会提示。 |
|
|
153
|
+
|
|
154
|
+
设置文件为 `$DSH_HOME/dsh-github.json`(权限 `0600`),里面没有令牌。
|
|
155
|
+
|
|
156
|
+
## Agent 工具
|
|
157
|
+
|
|
158
|
+
`github` 的 `action`:
|
|
159
|
+
|
|
160
|
+
| action | 参数 | 结果 |
|
|
161
|
+
| --- | --- | --- |
|
|
162
|
+
| `status` | `path` | 登录状态、设置与本地检出 |
|
|
163
|
+
| `list_repos` | — | 已登录账号的仓库 |
|
|
164
|
+
| `create_repo` | `name`、`description?`、`private?`、`autoInit?` | 新仓库 |
|
|
165
|
+
| `clone` | `fullName` | 克隆路径与状态 |
|
|
166
|
+
| `pull` | `path` | 快进拉取结果 |
|
|
167
|
+
| `commit` | `path`、`message`、`files?` | 提交结果,可选择随后推送 |
|
|
168
|
+
| `push` | `path` | 推送结果(首次推送会设置上游) |
|
|
169
|
+
|
|
170
|
+
失败统一返回 `{ ok: false, code, message }`,与界面同样做脱敏。
|
|
171
|
+
|
|
172
|
+
## 安全模型
|
|
173
|
+
|
|
174
|
+
- 只用设备码流程:令牌经 TLS 从 GitHub 令牌端点取得,用
|
|
175
|
+
`ctx.credentials.modifyRecord` 以 `grant` 记录存到 `dsh-github/token`
|
|
176
|
+
(`$DSH_HOME/.credentials.yaml`,权限 `0600`)。
|
|
177
|
+
- 每次操作现读令牌;设置文件、UI 返回值和 Tool 结果里都没有令牌。
|
|
178
|
+
- git 的 `argv`、remote URL、git config 中都看不到令牌。令牌只通过子进程环境变量
|
|
179
|
+
传入,由一次性内联 credential helper(`-c credential.helper=!…`)应答;之前先清空
|
|
180
|
+
所有已配置的 helper,并设置 `GIT_TERMINAL_PROMPT=0`,杜绝交互提示与隐式保存凭据。
|
|
181
|
+
- 只使用或接受 `https://github.com/<owner>/<name>` 形式的 remote;`http`、SSH、
|
|
182
|
+
其它域名以及内嵌凭据的 URL 一律拒绝;`origin` 不符合时拉取/推送直接拒绝。
|
|
183
|
+
- 克隆与提交路径做包含性校验:禁止 `..`、绝对路径、符号链接逃逸和形似选项的
|
|
184
|
+
pathspec。
|
|
185
|
+
- 所有错误信息和 Tool 结果都会脱敏(已知令牌、`ghp_…`/`github_pat_…` 形态、
|
|
186
|
+
`user:password@` 片段)。
|
|
187
|
+
- HTTP 路由是 Harness 的认证 fetch 端点(`POST /api/github`),只接受 JSON,
|
|
188
|
+
请求体上限 1 MiB,业务错误不回传密钥。
|
|
189
|
+
|
|
190
|
+
## 限制
|
|
191
|
+
|
|
192
|
+
- 每个 Harness profile 只支持一个 GitHub 账号。
|
|
193
|
+
- 只做 git 操作(列出/创建/克隆/拉取/提交/推送);不含 PR、Issue、Release、合并。
|
|
194
|
+
- 不自动刷新凭据:GitHub 撤销或过期后,页面会要求重新登录。
|
|
195
|
+
|
|
196
|
+
## 目录结构
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
index.js Host 入口(路由、授权流程、可选工具)
|
|
200
|
+
client.js Web UI:设置 → GitHub
|
|
201
|
+
lib/github-api.js 设备码流程 + REST 调用(令牌只放在 auth 头)
|
|
202
|
+
lib/git.js git 调用、remote/URL 与路径包含性校验
|
|
203
|
+
lib/service.js 状态机、dispatch、脱敏边界
|
|
204
|
+
lib/http.js POST /api/github 处理器
|
|
205
|
+
lib/tools.js `github` agent 工具
|
|
206
|
+
lib/dsh-go.js DSH GO 原生面板 + 输入框入口(共用同一服务实例)
|
|
207
|
+
lib/config.js 非秘密设置($DSH_HOME/dsh-github.json)
|
|
208
|
+
lib/validate.js owner/repo/路径/提交信息校验
|
|
209
|
+
lib/errors.js 错误码 + 密钥脱敏
|
|
210
|
+
```
|