dsh-git-branch-manage 1.1.0 → 1.3.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/CHANGELOG.md +34 -0
- package/lib/client.js +271 -29
- package/lib/credential-service.d.ts +32 -38
- package/lib/credential-service.js +56 -27
- package/lib/credentials.d.ts +145 -40
- package/lib/credentials.js +398 -70
- package/lib/git.d.ts +82 -9
- package/lib/git.js +260 -24
- package/lib/index.d.ts +11 -0
- package/lib/index.js +56 -19
- package/lib/local-import.d.ts +68 -0
- package/lib/local-import.js +151 -0
- package/lib/network-ip.d.ts +99 -0
- package/lib/network-ip.js +206 -0
- package/lib/network-probe.d.ts +28 -0
- package/lib/network-probe.js +47 -0
- package/lib/network-proxy.d.ts +35 -0
- package/lib/network-proxy.js +55 -0
- package/lib/network-tool.d.ts +5 -8
- package/lib/network-tool.js +17 -20
- package/lib/network.d.ts +58 -0
- package/lib/network.js +128 -0
- package/lib/normalize.d.ts +14 -2
- package/lib/normalize.js +38 -2
- package/lib/routes.d.ts +41 -3
- package/lib/routes.js +159 -27
- package/lib/shell-bridge.d.ts +57 -115
- package/lib/shell-bridge.js +96 -170
- package/lib/tool.js +2 -2
- package/lib/wire.d.ts +64 -0
- package/lib/wire.js +5 -0
- package/package.json +1 -1
- package/lib/helpers/git-credential-bridge.d.ts +0 -37
- package/lib/helpers/git-credential-bridge.js +0 -119
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
本文件记录 `dsh-git-branch-manage` 的重要变更。版本号遵循语义化版本。
|
|
4
4
|
|
|
5
|
+
## [1.3.0] - 2026-11-04
|
|
6
|
+
|
|
7
|
+
### 新增
|
|
8
|
+
|
|
9
|
+
- **网络备用通道与代理配置**(决策 0042、0043):直连发生可重试网络故障时,对 GitHub 地址使用运行时 DoH 查询和真实 Git 请求竞速选择可用 IP;支持显式配置 HTTP(S)/SOCKS 代理,并提供只读系统代理探测建议与「检测网络」诊断。
|
|
10
|
+
- **网络操作状态诊断**(决策 0041):按探测、读取、写入和超时核验分别设置预算;推送前探测远端,推送超时后核验远端 ref,并按失败类型给出建议。
|
|
11
|
+
|
|
12
|
+
### 变更
|
|
13
|
+
|
|
14
|
+
- **镜像仅作为读取加速**(决策 0044):镜像 fetch/update 失败后自动恢复原始 URL 回直连,再按备用通道链尝试;推送不走镜像,避免将写入交给第三方。
|
|
15
|
+
- SSH 远端不再进行无效的 HTTP 代理/IP 通道探测,错误信息改为提示可执行的 HTTPS 方案。
|
|
16
|
+
- 加速前缀改为拼接完整 GitHub URL(决策 0040),并用真实 Git URL 解析与提取覆盖回归场景。
|
|
17
|
+
|
|
18
|
+
### 修复
|
|
19
|
+
|
|
20
|
+
- **CI 在非 Windows 上失败**(决策 0046):系统代理探测的用例此前无条件假设 Windows 注册表,在 ubuntu-latest 上必然失败;现按平台分派,Windows 侧保留注册表行为覆盖,非 Windows 侧断言不读取注册表。
|
|
21
|
+
|
|
22
|
+
## [1.2.0] - 2026-11-02
|
|
23
|
+
|
|
24
|
+
### 新增
|
|
25
|
+
|
|
26
|
+
- **凭据管理直接读写本机 `~/.git-credentials`**(决策 0037):面板里存一次,任何 shell、任何子进程的 `git push/fetch/pull` 都用同一账号;文件里不属于本插件的行(注释、`ssh://` 等)原样保留,卸载插件后凭据仍属于用户。
|
|
27
|
+
- **从本机导入凭据,启动时默认执行**(决策 0038):按用户 gitconfig 里声明过的 credential 域名逐个探测(`git credential fill`,串行 + 20 秒超时),答出令牌即导入面板;面板另有手动「从本机导入」入口(`POST /credentials-import`),toast 汇总导入/跳过/失败数量。`gh`、GCM、`store` 里的账号一次导入即可接管。
|
|
28
|
+
|
|
29
|
+
### 变更
|
|
30
|
+
|
|
31
|
+
- 凭据生效通道改用 git 原生机制:`~/.gitconfig` 幂等追加一行 `[include]` 指向生成的 URL 作用域配置(每个已存域名重置 helper 链后装内置 `store`),不再向进程环境注入(废弃 0032/0034 的 `GIT_CONFIG_*` / `GIT_CONFIG_GLOBAL` 通道),令牌也不再进入命令行参数。
|
|
32
|
+
- 未管理域名的机器原有 helper(gh、GCM 等)保持原样,不再被全局清空;未存域名依旧 fail-fast 不弹窗。
|
|
33
|
+
- 令牌明文落在 `~/.git-credentials`(0600)——与 git 自带 `store` helper 一致的安全取舍;面板列表元数据(`method`/`updatedAt`)另存不含 secret 的旁文件。
|
|
34
|
+
|
|
35
|
+
### 移除
|
|
36
|
+
|
|
37
|
+
- 删除自研 `git-credential-bridge` helper,改用 git 内置 `credential-store`,不再为每次 git 调用拉起 node 进程。
|
|
38
|
+
|
|
5
39
|
## [1.1.0] - 2026-11-02
|
|
6
40
|
|
|
7
41
|
### 修复
|
package/lib/client.js
CHANGED
|
@@ -65,8 +65,8 @@ window.__ModuleLoader__.load({
|
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
67
|
* Normalize an accepted accelerate URL: trim, and append the trailing `/`
|
|
68
|
-
* the prefix concatenation requires (`https://gh-proxy.com/` +
|
|
69
|
-
*
|
|
68
|
+
* the prefix concatenation requires (`https://gh-proxy.com/` + the complete
|
|
69
|
+
* `https://github.com/o/r.git` URL). accelArgs builds the Git rewrite base.
|
|
70
70
|
* @param value - an accepted (non-empty, http(s), whitespace-free) URL.
|
|
71
71
|
* @returns the prefix ending in `/`.
|
|
72
72
|
*/
|
|
@@ -74,6 +74,37 @@ window.__ModuleLoader__.load({
|
|
|
74
74
|
const trimmed = value.trim();
|
|
75
75
|
return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
|
|
76
76
|
}
|
|
77
|
+
/** Schemes git's libcurl accepts for an HTTP(S) remote. */
|
|
78
|
+
const PROXY_SCHEMES = [
|
|
79
|
+
"http",
|
|
80
|
+
"https",
|
|
81
|
+
"socks5",
|
|
82
|
+
"socks5h"
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* Why this proxy value cannot be used, or undefined when it can.
|
|
86
|
+
*
|
|
87
|
+
* Credentials are rejected on purpose: the value is stored in the plugin
|
|
88
|
+
* config and passed as one `-c` argument, so `user:pass@` would be readable
|
|
89
|
+
* in the settings UI and in any diagnostics (0043).
|
|
90
|
+
* @param value - candidate proxy URL.
|
|
91
|
+
* @returns the user-facing reason, or undefined.
|
|
92
|
+
*/
|
|
93
|
+
function proxyUrlError(value) {
|
|
94
|
+
let url;
|
|
95
|
+
try {
|
|
96
|
+
url = new URL(value);
|
|
97
|
+
} catch {
|
|
98
|
+
return "must be an absolute proxy URL such as http://127.0.0.1:7890";
|
|
99
|
+
}
|
|
100
|
+
if (!PROXY_SCHEMES.includes(url.protocol.replace(":", ""))) return `scheme must be one of ${PROXY_SCHEMES.join(", ")}`;
|
|
101
|
+
if (url.hostname === "") return "must name a proxy host";
|
|
102
|
+
if (url.port === "") return "must name a proxy port";
|
|
103
|
+
const port = Number(url.port);
|
|
104
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return "proxy port must be 1-65535";
|
|
105
|
+
if (url.username !== "" || url.password !== "") return "credentials in the proxy URL are not supported";
|
|
106
|
+
if (url.pathname !== "" && url.pathname !== "/" || url.search !== "" || url.hash !== "") return "must not carry a path, query, or fragment";
|
|
107
|
+
}
|
|
77
108
|
//#endregion
|
|
78
109
|
//#region src/wire.ts
|
|
79
110
|
/**
|
|
@@ -131,6 +162,8 @@ window.__ModuleLoader__.load({
|
|
|
131
162
|
const ROUTE_TAG = `${ROUTE_PREFIX}/tag`;
|
|
132
163
|
/** POST ROUTE_PREFIX/tag-push — push ONE existing local tag to its remote. */
|
|
133
164
|
const ROUTE_TAG_PUSH = `${ROUTE_PREFIX}/tag-push`;
|
|
165
|
+
/** POST ROUTE_PREFIX/network-probe — read the endpoint through every configured channel (diagnostics; no state change). */
|
|
166
|
+
const ROUTE_NETWORK_PROBE = `${ROUTE_PREFIX}/network-probe`;
|
|
134
167
|
/** POST ROUTE_PREFIX/credentials — the global per-host credentials, secrets
|
|
135
168
|
* masked (the stored secret never leaves the host). */
|
|
136
169
|
const ROUTE_CREDENTIALS = `${ROUTE_PREFIX}/credentials`;
|
|
@@ -144,6 +177,9 @@ window.__ModuleLoader__.load({
|
|
|
144
177
|
/** POST ROUTE_PREFIX/credentials-oauth-poll — poll a started browser sign-in;
|
|
145
178
|
* stores the token and answers the fresh masked list once the user approves. */
|
|
146
179
|
const ROUTE_CREDENTIALS_OAUTH_POLL = `${ROUTE_PREFIX}/credentials-oauth-poll`;
|
|
180
|
+
/** POST ROUTE_PREFIX/credentials-import — copy the credentials the machine's
|
|
181
|
+
* own helpers already answer for into the managed store (decision 0038). */
|
|
182
|
+
const ROUTE_CREDENTIALS_IMPORT = `${ROUTE_PREFIX}/credentials-import`;
|
|
147
183
|
/** POST ROUTE_PREFIX/remotes — the repository's configured remotes. */
|
|
148
184
|
const ROUTE_REMOTES = `${ROUTE_PREFIX}/remotes`;
|
|
149
185
|
/** POST ROUTE_PREFIX/remote — add, re-point or remove ONE remote. */
|
|
@@ -220,6 +256,15 @@ window.__ModuleLoader__.load({
|
|
|
220
256
|
}
|
|
221
257
|
}
|
|
222
258
|
/**
|
|
259
|
+
* Probe every network channel (direct, proxy, pinned IP) against the
|
|
260
|
+
* repository's push URL; failures come back per channel rather than as one
|
|
261
|
+
* error, so the popover can show which route works right now (0043).
|
|
262
|
+
* @param repoPath - absolute directory inside the repository.
|
|
263
|
+
*/
|
|
264
|
+
function requestNetworkProbe(repoPath) {
|
|
265
|
+
return post(ROUTE_NETWORK_PROBE, { repoPath });
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
223
268
|
* Create or reuse the worktree for a branch.
|
|
224
269
|
* @param repoPath - absolute directory inside the repository.
|
|
225
270
|
* @param branch - branch display name.
|
|
@@ -508,6 +553,16 @@ window.__ModuleLoader__.load({
|
|
|
508
553
|
return post(ROUTE_CREDENTIALS_OAUTH_POLL, body);
|
|
509
554
|
}
|
|
510
555
|
/**
|
|
556
|
+
* Adopt the credentials this machine's own git helpers already answer for
|
|
557
|
+
* (gh, Windows Credential Manager, Keychain) into the plugin's store. Hosts
|
|
558
|
+
* the panel already manages are skipped; a host whose helper had nothing is
|
|
559
|
+
* reported per host, never as a request failure.
|
|
560
|
+
* @param body - `{}` (probe what the gitconfig names) or `{ hosts }`.
|
|
561
|
+
*/
|
|
562
|
+
function requestImportCredentials(body = {}) {
|
|
563
|
+
return post(ROUTE_CREDENTIALS_IMPORT, body);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
511
566
|
* The repository's configured remotes (fetch/push URLs).
|
|
512
567
|
* @param repoPath - absolute directory inside the repository.
|
|
513
568
|
*/
|
|
@@ -3545,6 +3600,8 @@ window.__ModuleLoader__.load({
|
|
|
3545
3600
|
/** The fields this form edits. */
|
|
3546
3601
|
const ACCEL_ENABLED_FIELD = "cnAccelerate";
|
|
3547
3602
|
const ACCEL_URL_FIELD = "cnAccelerateUrl";
|
|
3603
|
+
/** The proxy channel's config key (0043); blank = no proxy channel. */
|
|
3604
|
+
const NETWORK_PROXY_FIELD = "networkProxy";
|
|
3548
3605
|
/**
|
|
3549
3606
|
* Stages the cn-accelerate edits over the `dsh-git-branch` Host entry.
|
|
3550
3607
|
*
|
|
@@ -3558,6 +3615,7 @@ window.__ModuleLoader__.load({
|
|
|
3558
3615
|
listeners = /* @__PURE__ */ new Set();
|
|
3559
3616
|
enabledDraft;
|
|
3560
3617
|
urlDraft;
|
|
3618
|
+
proxyDraft;
|
|
3561
3619
|
saving = false;
|
|
3562
3620
|
failed = false;
|
|
3563
3621
|
/**
|
|
@@ -3598,6 +3656,11 @@ window.__ModuleLoader__.load({
|
|
|
3598
3656
|
this.failed = false;
|
|
3599
3657
|
this.publish();
|
|
3600
3658
|
},
|
|
3659
|
+
editNetworkProxy: (text) => {
|
|
3660
|
+
this.proxyDraft = text;
|
|
3661
|
+
this.failed = false;
|
|
3662
|
+
this.publish();
|
|
3663
|
+
},
|
|
3601
3664
|
restoreAccelDefault: () => {
|
|
3602
3665
|
this.urlDraft = ACCEL_DEFAULT_URL;
|
|
3603
3666
|
this.failed = false;
|
|
@@ -3605,9 +3668,10 @@ window.__ModuleLoader__.load({
|
|
|
3605
3668
|
},
|
|
3606
3669
|
saveAccel: () => this.save(),
|
|
3607
3670
|
discardAccel: () => {
|
|
3608
|
-
if (this.enabledDraft === void 0 && this.urlDraft === void 0 && !this.failed) return;
|
|
3671
|
+
if (this.enabledDraft === void 0 && this.urlDraft === void 0 && this.proxyDraft === void 0 && !this.failed) return;
|
|
3609
3672
|
this.enabledDraft = void 0;
|
|
3610
3673
|
this.urlDraft = void 0;
|
|
3674
|
+
this.proxyDraft = void 0;
|
|
3611
3675
|
this.failed = false;
|
|
3612
3676
|
this.publish();
|
|
3613
3677
|
}
|
|
@@ -3619,6 +3683,11 @@ window.__ModuleLoader__.load({
|
|
|
3619
3683
|
const value = this.scope.getSnapshot().value?.[ACCEL_URL_FIELD];
|
|
3620
3684
|
return this.urlDraft ?? value ?? "https://gh-proxy.com/";
|
|
3621
3685
|
}
|
|
3686
|
+
/** The proxy as the user sees it: the live draft, else the stored value. */
|
|
3687
|
+
effectiveProxyText() {
|
|
3688
|
+
const value = this.scope.getSnapshot().value?.[NETWORK_PROXY_FIELD];
|
|
3689
|
+
return this.proxyDraft ?? (typeof value === "string" ? value : "");
|
|
3690
|
+
}
|
|
3622
3691
|
/**
|
|
3623
3692
|
* Write the switch and the normalized URL TOGETHER, then re-seed from what
|
|
3624
3693
|
* the Host accepted. The URL is validated first — an unusable draft blocks
|
|
@@ -3627,11 +3696,14 @@ window.__ModuleLoader__.load({
|
|
|
3627
3696
|
* A save that did not land keeps its drafts so the user can correct them.
|
|
3628
3697
|
*/
|
|
3629
3698
|
async save() {
|
|
3630
|
-
if (this.enabledDraft === void 0 && this.urlDraft === void 0 || this.saving) return;
|
|
3699
|
+
if (this.enabledDraft === void 0 && this.urlDraft === void 0 && this.proxyDraft === void 0 || this.saving) return;
|
|
3631
3700
|
const urlText = this.effectiveUrlText();
|
|
3632
3701
|
if (accelerateUrlError(urlText) !== void 0) return;
|
|
3702
|
+
const proxyText = this.effectiveProxyText().trim();
|
|
3703
|
+
if (proxyText !== "" && proxyUrlError(proxyText) !== void 0) return;
|
|
3633
3704
|
const intendedEnabled = this.enabledDraft ?? this.scope.getSnapshot().value?.["cnAccelerate"] ?? false;
|
|
3634
3705
|
const intendedUrl = normalizeAccelerateUrl(urlText);
|
|
3706
|
+
const writeProxy = this.proxyDraft !== void 0;
|
|
3635
3707
|
this.saving = true;
|
|
3636
3708
|
this.failed = false;
|
|
3637
3709
|
this.publish();
|
|
@@ -3639,14 +3711,16 @@ window.__ModuleLoader__.load({
|
|
|
3639
3711
|
try {
|
|
3640
3712
|
await this.scope.set(ACCEL_ENABLED_FIELD, intendedEnabled);
|
|
3641
3713
|
await this.scope.set(ACCEL_URL_FIELD, intendedUrl);
|
|
3714
|
+
if (writeProxy) await this.scope.set(NETWORK_PROXY_FIELD, proxyText);
|
|
3642
3715
|
const user = this.userLayer();
|
|
3643
|
-
if (user?.["cnAccelerate"] !== intendedEnabled || user?.["cnAccelerateUrl"] !== intendedUrl) landed = false;
|
|
3716
|
+
if (user?.["cnAccelerate"] !== intendedEnabled || user?.["cnAccelerateUrl"] !== intendedUrl || writeProxy && (user?.["networkProxy"] ?? "") !== proxyText) landed = false;
|
|
3644
3717
|
} catch (_settingsWriteFailure) {
|
|
3645
3718
|
landed = false;
|
|
3646
3719
|
}
|
|
3647
3720
|
if (landed) {
|
|
3648
3721
|
this.enabledDraft = void 0;
|
|
3649
3722
|
this.urlDraft = void 0;
|
|
3723
|
+
this.proxyDraft = void 0;
|
|
3650
3724
|
}
|
|
3651
3725
|
this.saving = false;
|
|
3652
3726
|
this.failed = !landed;
|
|
@@ -3662,17 +3736,22 @@ window.__ModuleLoader__.load({
|
|
|
3662
3736
|
const storedEnabled = snapshot.value?.["cnAccelerate"] ?? false;
|
|
3663
3737
|
const storedUrl = snapshot.value?.["cnAccelerateUrl"] ?? "https://gh-proxy.com/";
|
|
3664
3738
|
const urlText = this.effectiveUrlText();
|
|
3739
|
+
const storedProxy = typeof snapshot.value?.["networkProxy"] === "string" ? snapshot.value[NETWORK_PROXY_FIELD] : "";
|
|
3740
|
+
const proxyText = this.effectiveProxyText();
|
|
3665
3741
|
const enabledDirty = this.enabledDraft !== void 0 && this.enabledDraft !== storedEnabled;
|
|
3666
3742
|
const urlDirty = this.urlDraft !== void 0 && this.urlDraft !== storedUrl;
|
|
3743
|
+
const proxyDirty = this.proxyDraft !== void 0 && this.proxyDraft !== storedProxy;
|
|
3667
3744
|
return {
|
|
3668
3745
|
available: snapshot.status === "ready",
|
|
3669
3746
|
writable: snapshot.writable,
|
|
3670
3747
|
enabled: this.enabledDraft ?? storedEnabled,
|
|
3671
3748
|
urlText,
|
|
3672
|
-
dirty: enabledDirty || urlDirty,
|
|
3749
|
+
dirty: enabledDirty || urlDirty || proxyDirty,
|
|
3673
3750
|
saving: this.saving,
|
|
3674
3751
|
failed: this.failed,
|
|
3675
|
-
urlError: accelerateUrlError(urlText) ?? null
|
|
3752
|
+
urlError: accelerateUrlError(urlText) ?? null,
|
|
3753
|
+
proxyText,
|
|
3754
|
+
proxyError: proxyText.trim() === "" ? null : proxyUrlError(proxyText.trim()) ?? null
|
|
3676
3755
|
};
|
|
3677
3756
|
}
|
|
3678
3757
|
/** Replace the snapshot reference and notify, only when the fact moved. */
|
|
@@ -4607,8 +4686,17 @@ window.__ModuleLoader__.load({
|
|
|
4607
4686
|
*
|
|
4608
4687
|
* @module git-worktree/client/panel/CnAcceleratePopover
|
|
4609
4688
|
*/
|
|
4689
|
+
/** Channel id → its display name; the probe rows stay in host order. */
|
|
4690
|
+
const CHANNEL_LABELS = {
|
|
4691
|
+
direct: (t) => t("accel.channel.direct"),
|
|
4692
|
+
proxy: (t) => t("accel.channel.proxy"),
|
|
4693
|
+
ip: (t) => t("accel.channel.ip")
|
|
4694
|
+
};
|
|
4610
4695
|
function CnAcceleratePopover(props) {
|
|
4611
|
-
const { accel, t, pushToast, onClose } = props;
|
|
4696
|
+
const { accel, t, pushToast, cwd, onClose } = props;
|
|
4697
|
+
const [probe, setProbe] = (0, react.useState)(null);
|
|
4698
|
+
const [probing, setProbing] = (0, react.useState)(false);
|
|
4699
|
+
const [probeError, setProbeError] = (0, react.useState)(null);
|
|
4612
4700
|
const rootRef = (0, react.useRef)(null);
|
|
4613
4701
|
const state = (0, react.useSyncExternalStore)(accel.store.subscribe, accel.store.getSnapshot);
|
|
4614
4702
|
(0, react.useEffect)(() => {
|
|
@@ -4627,7 +4715,7 @@ window.__ModuleLoader__.load({
|
|
|
4627
4715
|
}, [onClose]);
|
|
4628
4716
|
const locked = state.saving || !state.writable;
|
|
4629
4717
|
const onSave = (0, react.useCallback)(() => {
|
|
4630
|
-
if (state.saving || state.urlError !== null || !state.writable) return;
|
|
4718
|
+
if (state.saving || state.urlError !== null || state.proxyError !== null || !state.writable) return;
|
|
4631
4719
|
(async () => {
|
|
4632
4720
|
await accel.saveAccel();
|
|
4633
4721
|
const after = accel.store.getSnapshot();
|
|
@@ -4647,6 +4735,25 @@ window.__ModuleLoader__.load({
|
|
|
4647
4735
|
state.writable,
|
|
4648
4736
|
t
|
|
4649
4737
|
]);
|
|
4738
|
+
const onProbe = (0, react.useCallback)(() => {
|
|
4739
|
+
if (cwd === void 0 || probing) return;
|
|
4740
|
+
setProbing(true);
|
|
4741
|
+
setProbeError(null);
|
|
4742
|
+
(async () => {
|
|
4743
|
+
const result = await requestNetworkProbe(cwd);
|
|
4744
|
+
setProbing(false);
|
|
4745
|
+
if (!result.ok) {
|
|
4746
|
+
setProbe(null);
|
|
4747
|
+
setProbeError(result.error);
|
|
4748
|
+
return;
|
|
4749
|
+
}
|
|
4750
|
+
setProbe({
|
|
4751
|
+
url: result.url,
|
|
4752
|
+
channels: result.channels,
|
|
4753
|
+
...result.systemProxy === void 0 ? {} : { systemProxy: result.systemProxy }
|
|
4754
|
+
});
|
|
4755
|
+
})();
|
|
4756
|
+
}, [cwd, probing]);
|
|
4650
4757
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4651
4758
|
className: GitHistoryPanel_module_default.cnPop,
|
|
4652
4759
|
ref: rootRef,
|
|
@@ -4702,6 +4809,46 @@ window.__ModuleLoader__.load({
|
|
|
4702
4809
|
className: GitHistoryPanel_module_default.cnError,
|
|
4703
4810
|
children: state.urlError === "empty" ? t("accel.urlEmpty") : t("accel.urlInvalid")
|
|
4704
4811
|
}) : null,
|
|
4812
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4813
|
+
className: GitHistoryPanel_module_default.cnFieldHead,
|
|
4814
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4815
|
+
className: GitHistoryPanel_module_default.cnFieldLabel,
|
|
4816
|
+
children: t("accel.proxyLabel")
|
|
4817
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4818
|
+
type: "button",
|
|
4819
|
+
className: GitHistoryPanel_module_default.cnLink,
|
|
4820
|
+
disabled: locked || cwd === void 0 || probing,
|
|
4821
|
+
onClick: onProbe,
|
|
4822
|
+
children: probing ? t("accel.probing") : t("accel.probe")
|
|
4823
|
+
})]
|
|
4824
|
+
}),
|
|
4825
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4826
|
+
type: "text",
|
|
4827
|
+
className: state.proxyError !== null ? GitHistoryPanel_module_default.cnInputBad : GitHistoryPanel_module_default.cnInput,
|
|
4828
|
+
value: state.proxyText,
|
|
4829
|
+
spellCheck: false,
|
|
4830
|
+
disabled: locked,
|
|
4831
|
+
placeholder: "http://127.0.0.1:7890",
|
|
4832
|
+
"aria-invalid": state.proxyError !== null,
|
|
4833
|
+
onChange: (event) => accel.editNetworkProxy(event.target.value)
|
|
4834
|
+
}),
|
|
4835
|
+
state.proxyError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4836
|
+
className: GitHistoryPanel_module_default.cnError,
|
|
4837
|
+
children: t("accel.proxyInvalid")
|
|
4838
|
+
}) : null,
|
|
4839
|
+
probe !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4840
|
+
className: GitHistoryPanel_module_default.cnHint,
|
|
4841
|
+
children: probe.channels.map((channel) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
4842
|
+
channel.ok ? "✓ " : "✗ ",
|
|
4843
|
+
CHANNEL_LABELS[channel.channel](t),
|
|
4844
|
+
` \u00b7 ${String(channel.elapsedMs)} ms`,
|
|
4845
|
+
channel.detail === void 0 ? null : ` \u00b7 ${channel.detail}`
|
|
4846
|
+
] }, channel.channel))
|
|
4847
|
+
}) : null,
|
|
4848
|
+
probeError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4849
|
+
className: GitHistoryPanel_module_default.cnError,
|
|
4850
|
+
children: probeError
|
|
4851
|
+
}) : null,
|
|
4705
4852
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4706
4853
|
className: GitHistoryPanel_module_default.cnFooter,
|
|
4707
4854
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -4712,7 +4859,7 @@ window.__ModuleLoader__.load({
|
|
|
4712
4859
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4713
4860
|
type: "button",
|
|
4714
4861
|
className: GitHistoryPanel_module_default.cnSave,
|
|
4715
|
-
disabled: state.saving || state.urlError !== null || !state.writable,
|
|
4862
|
+
disabled: state.saving || state.urlError !== null || state.proxyError !== null || !state.writable,
|
|
4716
4863
|
onClick: onSave,
|
|
4717
4864
|
children: t("cardSave")
|
|
4718
4865
|
})]
|
|
@@ -4784,6 +4931,10 @@ window.__ModuleLoader__.load({
|
|
|
4784
4931
|
const [seed, setSeed] = (0, react.useState)();
|
|
4785
4932
|
const [formError, setFormError] = (0, react.useState)();
|
|
4786
4933
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
4934
|
+
/** The one-click machine import runs on its own flag: it is read-only on the
|
|
4935
|
+
* form, so it must not disable editing, and a slow helper (a cold keychain
|
|
4936
|
+
* prompt) must not look like a hung save. */
|
|
4937
|
+
const [importing, setImporting] = (0, react.useState)(false);
|
|
4787
4938
|
/** Two-step delete: the first click only arms the row. */
|
|
4788
4939
|
const [confirmHost, setConfirmHost] = (0, react.useState)();
|
|
4789
4940
|
/** The editor starts collapsed; the "+" icon in the header row opens it,
|
|
@@ -5006,6 +5157,34 @@ window.__ModuleLoader__.load({
|
|
|
5006
5157
|
seed,
|
|
5007
5158
|
t
|
|
5008
5159
|
]);
|
|
5160
|
+
/** Adopt what this machine's own git helpers already answer for. The host
|
|
5161
|
+
* decides which hosts to probe (the user's gitconfig names them) and skips
|
|
5162
|
+
* the ones the panel already manages, so this is always safe to click. */
|
|
5163
|
+
const importLocal = (0, react.useCallback)(async () => {
|
|
5164
|
+
if (busy || importing) return;
|
|
5165
|
+
setImporting(true);
|
|
5166
|
+
const result = await requestImportCredentials();
|
|
5167
|
+
setImporting(false);
|
|
5168
|
+
if (!result.ok) {
|
|
5169
|
+
pushToast(result.error, "error");
|
|
5170
|
+
return;
|
|
5171
|
+
}
|
|
5172
|
+
setEntries(result.credentials);
|
|
5173
|
+
if (result.imported.length === 0) {
|
|
5174
|
+
pushToast(t("cred.importNone"), result.failed.length === 0 ? "success" : "error");
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
pushToast(t("cred.importDone", {
|
|
5178
|
+
count: result.imported.length,
|
|
5179
|
+
skipped: result.skipped.length,
|
|
5180
|
+
failed: result.failed.length
|
|
5181
|
+
}), "success");
|
|
5182
|
+
}, [
|
|
5183
|
+
busy,
|
|
5184
|
+
importing,
|
|
5185
|
+
pushToast,
|
|
5186
|
+
t
|
|
5187
|
+
]);
|
|
5009
5188
|
const title = seed === void 0 ? t("cred.add") : t("cred.edit");
|
|
5010
5189
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5011
5190
|
open: true,
|
|
@@ -5021,21 +5200,34 @@ window.__ModuleLoader__.load({
|
|
|
5021
5200
|
children: [
|
|
5022
5201
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5023
5202
|
className: GitHistoryPanel_module_default.credTop,
|
|
5024
|
-
children: [
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5203
|
+
children: [
|
|
5204
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5205
|
+
className: GitHistoryPanel_module_default.credHint,
|
|
5206
|
+
children: t("cred.hint")
|
|
5207
|
+
}),
|
|
5208
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5209
|
+
size: "sm",
|
|
5210
|
+
variant: "ghost",
|
|
5211
|
+
disabled: busy || importing,
|
|
5212
|
+
title: t("cred.import"),
|
|
5213
|
+
onClick: () => {
|
|
5214
|
+
importLocal();
|
|
5215
|
+
},
|
|
5216
|
+
children: importing ? t("cred.importing") : t("cred.import")
|
|
5217
|
+
}),
|
|
5218
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5219
|
+
type: "button",
|
|
5220
|
+
className: GitHistoryPanel_module_default.credAdd,
|
|
5221
|
+
title: t("cred.add"),
|
|
5222
|
+
"aria-label": t("cred.add"),
|
|
5223
|
+
"aria-expanded": editorOpen,
|
|
5224
|
+
onClick: openNew,
|
|
5225
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5226
|
+
"aria-hidden": "true",
|
|
5227
|
+
children: "+"
|
|
5228
|
+
})
|
|
5037
5229
|
})
|
|
5038
|
-
|
|
5230
|
+
]
|
|
5039
5231
|
}),
|
|
5040
5232
|
loadError !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5041
5233
|
className: GitHistoryPanel_module_default.formError,
|
|
@@ -7067,6 +7259,7 @@ window.__ModuleLoader__.load({
|
|
|
7067
7259
|
accel,
|
|
7068
7260
|
t,
|
|
7069
7261
|
pushToast,
|
|
7262
|
+
cwd,
|
|
7070
7263
|
onClose: closeAccel
|
|
7071
7264
|
}) : null]
|
|
7072
7265
|
}),
|
|
@@ -7888,7 +8081,7 @@ window.__ModuleLoader__.load({
|
|
|
7888
8081
|
children: t("cardDescription")
|
|
7889
8082
|
});
|
|
7890
8083
|
const lockInput = !state.writable;
|
|
7891
|
-
const lockActions = !state.dirty && !accelState.dirty || state.saving || accelState.saving || !state.keepWorktreesValid || accelState.urlError !== null;
|
|
8084
|
+
const lockActions = !state.dirty && !accelState.dirty || state.saving || accelState.saving || !state.keepWorktreesValid || accelState.urlError !== null || accelState.proxyError !== null;
|
|
7892
8085
|
const legacyRootText = storage?.legacyRoot ?? (state.rootDir === "" ? "—" : state.rootDir);
|
|
7893
8086
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7894
8087
|
className: GitWorktreeCard_module_default.page,
|
|
@@ -8112,6 +8305,33 @@ window.__ModuleLoader__.load({
|
|
|
8112
8305
|
}) : null
|
|
8113
8306
|
]
|
|
8114
8307
|
}),
|
|
8308
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
8309
|
+
className: `${GitWorktreeCard_module_default.field} ${GitWorktreeCard_module_default.copyFilesRow}`,
|
|
8310
|
+
htmlFor: "git-worktree-card-network-proxy",
|
|
8311
|
+
children: [
|
|
8312
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8313
|
+
className: GitWorktreeCard_module_default.fieldLabel,
|
|
8314
|
+
children: t("accel.proxyLabel")
|
|
8315
|
+
}),
|
|
8316
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
8317
|
+
id: "git-worktree-card-network-proxy",
|
|
8318
|
+
className: GitWorktreeCard_module_default.accelInput,
|
|
8319
|
+
type: "text",
|
|
8320
|
+
spellCheck: false,
|
|
8321
|
+
placeholder: "http://127.0.0.1:7890",
|
|
8322
|
+
disabled: lockInput || accelState.saving,
|
|
8323
|
+
value: accelState.proxyText,
|
|
8324
|
+
onChange: (event) => {
|
|
8325
|
+
props.editNetworkProxy(event.target.value);
|
|
8326
|
+
}
|
|
8327
|
+
}),
|
|
8328
|
+
accelState.proxyError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8329
|
+
className: GitWorktreeCard_module_default.keepBad,
|
|
8330
|
+
role: "alert",
|
|
8331
|
+
children: t("accel.proxyInvalid")
|
|
8332
|
+
}) : null
|
|
8333
|
+
]
|
|
8334
|
+
}),
|
|
8115
8335
|
history.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8116
8336
|
className: `${GitWorktreeCard_module_default.field} ${GitWorktreeCard_module_default.historyRow}`,
|
|
8117
8337
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -8421,18 +8641,25 @@ window.__ModuleLoader__.load({
|
|
|
8421
8641
|
"panel.stUnmerged": "conflict",
|
|
8422
8642
|
"panel.cnAccelerate": "China acceleration",
|
|
8423
8643
|
"accel.enable": "Enable acceleration",
|
|
8424
|
-
"accel.hint": "
|
|
8644
|
+
"accel.hint": "Enter a proxy prefix (e.g. https://gh-proxy.com/); fetch/update appends the complete GitHub URL. Push stays direct. The mirror sees repository content — do not use it for private or sensitive repositories.",
|
|
8425
8645
|
"accel.urlLabel": "Acceleration URL",
|
|
8426
8646
|
"accel.restoreDefault": "Restore default",
|
|
8427
8647
|
"accel.urlInvalid": "Enter an http(s):// URL",
|
|
8428
8648
|
"accel.urlEmpty": "Enter an acceleration URL",
|
|
8649
|
+
"accel.proxyLabel": "Proxy (optional)",
|
|
8650
|
+
"accel.proxyInvalid": "Enter an http(s)/socks5 proxy URL as host:port, without credentials",
|
|
8651
|
+
"accel.probe": "Test network",
|
|
8652
|
+
"accel.probing": "Testing...",
|
|
8653
|
+
"accel.channel.direct": "Direct",
|
|
8654
|
+
"accel.channel.proxy": "Proxy",
|
|
8655
|
+
"accel.channel.ip": "Pinned IP",
|
|
8429
8656
|
"accel.saveFailed": "The change did not save. Try again.",
|
|
8430
8657
|
"accel.savedOn": "China acceleration enabled: {url}",
|
|
8431
8658
|
"accel.savedOff": "China acceleration disabled",
|
|
8432
8659
|
"accel.mirrorDownHint": "If the mirror is down, disable acceleration or change the URL",
|
|
8433
8660
|
"panel.credentials": "Credentials",
|
|
8434
8661
|
"cred.title": "Git credentials",
|
|
8435
|
-
"cred.hint": "
|
|
8662
|
+
"cred.hint": "One entry per host, shared by all repos. Stored in ~/.git-credentials, so plain git uses it too.",
|
|
8436
8663
|
"cred.empty": "No credentials saved yet",
|
|
8437
8664
|
"cred.hostLabel": "Host",
|
|
8438
8665
|
"cred.hostPlaceholder": "github.com",
|
|
@@ -8447,6 +8674,10 @@ window.__ModuleLoader__.load({
|
|
|
8447
8674
|
"cred.methodPassword": "Password",
|
|
8448
8675
|
"cred.getToken": "Get a token",
|
|
8449
8676
|
"cred.add": "Add credential",
|
|
8677
|
+
"cred.import": "Import from this machine",
|
|
8678
|
+
"cred.importing": "Importing…",
|
|
8679
|
+
"cred.importDone": "Imported {count} host(s) — skipped {skipped}, failed {failed}",
|
|
8680
|
+
"cred.importNone": "Nothing new to import from this machine",
|
|
8450
8681
|
"cred.edit": "Edit",
|
|
8451
8682
|
"cred.save": "Save",
|
|
8452
8683
|
"cred.saving": "Saving…",
|
|
@@ -8707,18 +8938,25 @@ window.__ModuleLoader__.load({
|
|
|
8707
8938
|
"panel.stUnmerged": "冲突",
|
|
8708
8939
|
"panel.cnAccelerate": "国内加速",
|
|
8709
8940
|
"accel.enable": "启用加速",
|
|
8710
|
-
"accel.hint": "
|
|
8941
|
+
"accel.hint": "填写加速站前缀(如 https://gh-proxy.com/),提取/更新时会在其后拼接完整 GitHub 地址;推送不走加速。第三方加速站可看到仓库内容,请勿用于私有/敏感仓库。",
|
|
8711
8942
|
"accel.urlLabel": "加速地址",
|
|
8712
8943
|
"accel.restoreDefault": "恢复默认",
|
|
8713
8944
|
"accel.urlInvalid": "请输入以 http(s):// 开头的地址",
|
|
8714
8945
|
"accel.urlEmpty": "请输入加速地址",
|
|
8946
|
+
"accel.proxyLabel": "代理(可选)",
|
|
8947
|
+
"accel.proxyInvalid": "请输入 主机:端口 形式的 http(s)/socks5 代理地址,且不带账号密码",
|
|
8948
|
+
"accel.probe": "检测网络",
|
|
8949
|
+
"accel.probing": "检测中...",
|
|
8950
|
+
"accel.channel.direct": "直连",
|
|
8951
|
+
"accel.channel.proxy": "代理",
|
|
8952
|
+
"accel.channel.ip": "固定 IP",
|
|
8715
8953
|
"accel.saveFailed": "保存未生效,请重试。",
|
|
8716
8954
|
"accel.savedOn": "已启用国内加速:{url}",
|
|
8717
8955
|
"accel.savedOff": "已关闭国内加速",
|
|
8718
8956
|
"accel.mirrorDownHint": "若加速地址不可用,可停用国内加速或更换地址",
|
|
8719
8957
|
"panel.credentials": "密钥配置",
|
|
8720
8958
|
"cred.title": "Git 密钥配置",
|
|
8721
|
-
"cred.hint": "
|
|
8959
|
+
"cred.hint": "按域名保存,所有仓库共用;密钥写入本机 ~/.git-credentials,命令行 git 同样生效。",
|
|
8722
8960
|
"cred.empty": "还没有配置任何密钥",
|
|
8723
8961
|
"cred.hostLabel": "域名",
|
|
8724
8962
|
"cred.hostPlaceholder": "github.com",
|
|
@@ -8733,6 +8971,10 @@ window.__ModuleLoader__.load({
|
|
|
8733
8971
|
"cred.methodPassword": "密码",
|
|
8734
8972
|
"cred.getToken": "获取令牌",
|
|
8735
8973
|
"cred.add": "新增密钥",
|
|
8974
|
+
"cred.import": "从本机导入",
|
|
8975
|
+
"cred.importing": "导入中…",
|
|
8976
|
+
"cred.importDone": "已导入 {count} 个域名(跳过 {skipped},失败 {failed})",
|
|
8977
|
+
"cred.importNone": "本机没有新的凭据可导入",
|
|
8736
8978
|
"cred.edit": "编辑",
|
|
8737
8979
|
"cred.save": "保存",
|
|
8738
8980
|
"cred.saving": "保存中…",
|