dsh-mnemon 0.5.32 → 0.5.33
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 +15 -1
- package/README.zh-CN.md +15 -1
- package/lib/client.js +71 -20
- package/lib/index.js +164 -16
- package/lib/types/src/client/locales.d.ts +5 -0
- package/lib/types/src/host/sync-rules.d.ts +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ npx @deepseek-ai/dsh web
|
|
|
92
92
|
4. For Memory Spaces, install the Mnemon CLI for Mnemon Native with `npm install --global @mnemon-dev/mnemon`, or enable another Provider on the Memory Spaces page.
|
|
93
93
|
5. Choose the main strategy and enhancements under **Plugins → dsh-mnemon**.
|
|
94
94
|
|
|
95
|
-
dsh-mnemon supports DSH `0.2.
|
|
95
|
+
dsh-mnemon supports DSH `0.2.1-rc.2` (npm `latest` and `next`) and `0.2.0-rc.2`. The same package serves the desktop app's Plugins page, `dsh plugin --profile web add dsh-mnemon` on the command line, and Headless with `dsh plugin --profile headless add dsh-mnemon`; older hosts keep the release [compatibility and upgrades](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/en/reference/compatibility.md) names for them. [Getting started](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/en/guides/getting-started.md) continues from here, and [compatibility and upgrades](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/en/reference/compatibility.md) covers existing installations.
|
|
96
96
|
|
|
97
97
|
## How it works
|
|
98
98
|
|
|
@@ -146,3 +146,17 @@ pnpm verify:plugins
|
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
Use Node.js `^22.19.0 || >=24.0.0` and pnpm. `node scripts/serve-e2e.mjs` starts a disposable real WebUI; add `--docs-demo` for the seeded project behind these screenshots, and `--live-model` to have the DeepSeek API answer, with the key read from `DEEPSEEK_API_KEY`. Mechanical tests are not claims about model accuracy or live cloud Providers. [Development and verification](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/en/development/README.md).
|
|
149
|
+
|
|
150
|
+
## Star History
|
|
151
|
+
|
|
152
|
+
<a href="https://www.star-history.com/?repos=omdsh-dev%2Fdsh-mnemon&type=timeline&legend=bottom-right">
|
|
153
|
+
<picture>
|
|
154
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=omdsh-dev/dsh-mnemon&type=timeline&theme=dark&legend=bottom-right" />
|
|
155
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=omdsh-dev/dsh-mnemon&type=timeline&legend=bottom-right" />
|
|
156
|
+
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=omdsh-dev/dsh-mnemon&type=timeline&legend=bottom-right" />
|
|
157
|
+
</picture>
|
|
158
|
+
</a>
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
[MIT](https://github.com/omdsh-dev/dsh-mnemon/blob/main/LICENSE). Third-party providers keep their own licenses and terms; see [Third-party notices](https://github.com/omdsh-dev/dsh-mnemon/blob/main/THIRD_PARTY_NOTICES.md).
|
package/README.zh-CN.md
CHANGED
|
@@ -93,7 +93,7 @@ npx @deepseek-ai/dsh web
|
|
|
93
93
|
4. 使用记忆空间时,可用 `npm install --global @mnemon-dev/mnemon` 安装 Mnemon Native 所需的 CLI,或在记忆空间页面启用其他 Provider。
|
|
94
94
|
5. 在**插件 → 可组合记忆**中选择主策略与增强。
|
|
95
95
|
|
|
96
|
-
dsh-mnemon 支持 DSH `0.2.
|
|
96
|
+
dsh-mnemon 支持 DSH `0.2.1-rc.2`(npm `latest` 与 `next`)与 `0.2.0-rc.2`。同一个包也用于桌面版的插件页、命令行 `dsh plugin --profile web add dsh-mnemon`,以及 Headless:`dsh plugin --profile headless add dsh-mnemon`;更早的宿主请继续使用[兼容性与升级](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/zh-CN/reference/compatibility.md)为其列出的版本。接下来可以看[入门指南](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/zh-CN/guides/getting-started.md),已有安装的升级见[兼容性与升级](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/zh-CN/reference/compatibility.md)。
|
|
97
97
|
|
|
98
98
|
## 工作原理
|
|
99
99
|
|
|
@@ -147,3 +147,17 @@ pnpm verify:plugins
|
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
需要 Node.js `^22.19.0 || >=24.0.0` 与 pnpm。`node scripts/serve-e2e.mjs` 会启动一个用后即弃的真实 WebUI;加上 `--docs-demo` 即可得到这些截图背后的示例项目,再加 `--live-model` 则由 DeepSeek API(读取 `DEEPSEEK_API_KEY`)真实作答。机制测试不代表模型准确率,也不代表云端 Provider 的实际表现。详见[开发与验证](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/zh-CN/development/README.md)。
|
|
150
|
+
|
|
151
|
+
## Star 历史
|
|
152
|
+
|
|
153
|
+
<a href="https://www.star-history.com/?repos=omdsh-dev%2Fdsh-mnemon&type=timeline&legend=bottom-right">
|
|
154
|
+
<picture>
|
|
155
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=omdsh-dev/dsh-mnemon&type=timeline&theme=dark&legend=bottom-right" />
|
|
156
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=omdsh-dev/dsh-mnemon&type=timeline&legend=bottom-right" />
|
|
157
|
+
<img alt="Star 历史图表" src="https://api.star-history.com/chart?repos=omdsh-dev/dsh-mnemon&type=timeline&legend=bottom-right" />
|
|
158
|
+
</picture>
|
|
159
|
+
</a>
|
|
160
|
+
|
|
161
|
+
## 许可证
|
|
162
|
+
|
|
163
|
+
[MIT](https://github.com/omdsh-dev/dsh-mnemon/blob/main/LICENSE)。第三方 Provider 适用各自的许可证与服务条款,见[第三方声明](https://github.com/omdsh-dev/dsh-mnemon/blob/main/THIRD_PARTY_NOTICES.md)。
|
package/lib/client.js
CHANGED
|
@@ -131,7 +131,11 @@ window.__ModuleLoader__.load({
|
|
|
131
131
|
const SSH = /^ssh:\/\/[^\s/]+(?::\d+)?\/[^\s]+$/u;
|
|
132
132
|
const SCP = /^[A-Za-z0-9._-]+@[A-Za-z0-9._-]+:[^\s]+$/u;
|
|
133
133
|
const SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//u;
|
|
134
|
-
const
|
|
134
|
+
const HTTPS_USER = /^https:\/\/([^/]*)@/u;
|
|
135
|
+
const USER_TEXT = /^(?:[A-Za-z0-9._~!$&'()*+,;=-]|%[0-9A-Fa-f]{2})+$/u;
|
|
136
|
+
const NOT_A_NAME = /[:@\s\p{Cc}]/u;
|
|
137
|
+
const TOKEN_SHAPE = /^(?:gh[opsru]_|github_pat_|glpat-|gldt-|glcbt-|hf_|ATBB|ATCTT|ATATT)|^[0-9A-Fa-f]{32,}$|^[A-Za-z0-9]{52,}$/u;
|
|
138
|
+
const USER_NAME_MAX = 100;
|
|
135
139
|
const EMAIL = /^[^\s<>@]+@[^\s<>@]+$/u;
|
|
136
140
|
const WINDOWS_DRIVE = /^[A-Za-z]:[\\/]/u;
|
|
137
141
|
const CONTROL = /[\p{Cc}\u2028\u2029]/u;
|
|
@@ -149,17 +153,35 @@ window.__ModuleLoader__.load({
|
|
|
149
153
|
}
|
|
150
154
|
/**
|
|
151
155
|
* What is wrong with a repository address, or nothing for an https:// or ssh:// URL without
|
|
152
|
-
* credentials in it, a git@host:path address, or a local path that `localPath` takes.
|
|
156
|
+
* credentials in it, a git@host:path address, or a local path that `localPath` takes. An HTTPS
|
|
157
|
+
* address may name the user a host wants its token offered under, as Bitbucket's do, but never a
|
|
158
|
+
* password, nor a token written where that name goes.
|
|
153
159
|
*/
|
|
154
160
|
function syncRepositoryProblem(url, localPath) {
|
|
155
161
|
if (url === "" || url.length > 2e3 || /\s/u.test(url)) return "format";
|
|
156
162
|
if (SCHEME.test(url)) {
|
|
157
163
|
if (!HTTPS.test(url) && !SSH.test(url)) return "format";
|
|
158
|
-
|
|
164
|
+
const user = HTTPS_USER.exec(url)?.[1];
|
|
165
|
+
return user === void 0 || syncUserName(user) ? void 0 : "credentials";
|
|
159
166
|
}
|
|
160
167
|
return SCP.test(url) || localPath(url) ? void 0 : "format";
|
|
161
168
|
}
|
|
162
169
|
/**
|
|
170
|
+
* Whether the user part of an HTTPS address is a user name. It is judged as Git reads it, with its
|
|
171
|
+
* escapes decoded, so an escaped colon, an escaped token prefix or an escaped line break counts as what
|
|
172
|
+
* it decodes to; one that does not decode is no name at all.
|
|
173
|
+
*/
|
|
174
|
+
function syncUserName(written) {
|
|
175
|
+
if (!USER_TEXT.test(written)) return false;
|
|
176
|
+
let name;
|
|
177
|
+
try {
|
|
178
|
+
name = decodeURIComponent(written);
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return name.length <= USER_NAME_MAX && !NOT_A_NAME.test(name) && !TOKEN_SHAPE.test(name);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
163
185
|
* A local path that is absolute on POSIX or on Windows, for the browser, which cannot tell which
|
|
164
186
|
* platform the Host runs on: a POSIX path, a Windows path on a drive, or a UNC share. The Host
|
|
165
187
|
* checks a path by its own platform's rule instead.
|
|
@@ -2832,6 +2854,11 @@ window.__ModuleLoader__.load({
|
|
|
2832
2854
|
"config.syncNoCredentialToken": "推送需要访问令牌",
|
|
2833
2855
|
"config.syncIssueAuthHttps": "需要凭据,请用 GitHub 登录或填写访问令牌",
|
|
2834
2856
|
"config.syncIssueAuthToken": "需要凭据,请填写访问令牌",
|
|
2857
|
+
"config.syncIssueRejectedGitHub": "GitHub 拒绝了登录凭据,请重新登录,或检查它能否访问这个仓库",
|
|
2858
|
+
"config.syncIssueRejectedGitHubToken": "GitHub 拒绝了保存的访问令牌,请更换令牌",
|
|
2859
|
+
"config.syncIssueRejectedGitHubEnvironment": "GitHub 拒绝了 {env} 中的令牌,请更换它",
|
|
2860
|
+
"config.syncIssueRejected": "仓库主机拒绝了保存的访问令牌,请更换令牌,或在地址中写上它要求的用户名",
|
|
2861
|
+
"config.syncIssueRejectedEnvironment": "仓库主机拒绝了 {env} 中的令牌,请更换它",
|
|
2835
2862
|
"config.syncIssueAuthSsh": "SSH 认证失败,请检查这台电脑的 SSH 密钥",
|
|
2836
2863
|
"config.syncIssueAuth": "没有访问这个仓库的权限",
|
|
2837
2864
|
"config.syncIssueNotFound": "找不到这个仓库,请检查地址和访问权限",
|
|
@@ -2850,7 +2877,7 @@ window.__ModuleLoader__.load({
|
|
|
2850
2877
|
"config.syncRepoUrl": "仓库地址",
|
|
2851
2878
|
"config.syncRepoUrlPlaceholder": "https://…、git@主机:路径 或本机绝对路径",
|
|
2852
2879
|
"config.syncRepoUrlInvalid": "地址需要是 https://、ssh://、git@主机:路径 或本机绝对路径",
|
|
2853
|
-
"config.syncRepoUrlCredentials": "
|
|
2880
|
+
"config.syncRepoUrlCredentials": "地址里只能写用户名,令牌请填在“访问令牌”里",
|
|
2854
2881
|
"config.syncToken": "访问令牌",
|
|
2855
2882
|
"config.syncTokenOptional": "可选",
|
|
2856
2883
|
"config.syncTokenSaved": "已保存;输入新令牌可替换",
|
|
@@ -3585,6 +3612,11 @@ window.__ModuleLoader__.load({
|
|
|
3585
3612
|
"config.syncNoCredentialToken": "pushing needs an access token",
|
|
3586
3613
|
"config.syncIssueAuthHttps": "Needs credentials; sign in with GitHub or add an access token",
|
|
3587
3614
|
"config.syncIssueAuthToken": "Needs credentials; add an access token",
|
|
3615
|
+
"config.syncIssueRejectedGitHub": "GitHub rejected the sign-in; sign in again or check its access to this repository",
|
|
3616
|
+
"config.syncIssueRejectedGitHubToken": "GitHub rejected the saved access token; replace it",
|
|
3617
|
+
"config.syncIssueRejectedGitHubEnvironment": "GitHub rejected the token in {env}; replace it",
|
|
3618
|
+
"config.syncIssueRejected": "The host rejected the saved token; replace it, or name the user it expects in the address",
|
|
3619
|
+
"config.syncIssueRejectedEnvironment": "The host rejected the token in {env}; replace it",
|
|
3588
3620
|
"config.syncIssueAuthSsh": "SSH authentication failed; check this computer's SSH key",
|
|
3589
3621
|
"config.syncIssueAuth": "Access to this repository was refused",
|
|
3590
3622
|
"config.syncIssueNotFound": "Repository not found; check the address and your access",
|
|
@@ -3603,7 +3635,7 @@ window.__ModuleLoader__.load({
|
|
|
3603
3635
|
"config.syncRepoUrl": "Repository address",
|
|
3604
3636
|
"config.syncRepoUrlPlaceholder": "https://…, git@host:path, or an absolute local path",
|
|
3605
3637
|
"config.syncRepoUrlInvalid": "Use an https:// or ssh:// URL, git@host:path, or an absolute local path",
|
|
3606
|
-
"config.syncRepoUrlCredentials": "
|
|
3638
|
+
"config.syncRepoUrlCredentials": "Only a user name may go in the address; enter the token under Access token",
|
|
3607
3639
|
"config.syncToken": "Access token",
|
|
3608
3640
|
"config.syncTokenOptional": "Optional",
|
|
3609
3641
|
"config.syncTokenSaved": "Saved; type a new one to replace it",
|
|
@@ -7460,11 +7492,15 @@ window.__ModuleLoader__.load({
|
|
|
7460
7492
|
minute: "2-digit"
|
|
7461
7493
|
});
|
|
7462
7494
|
}
|
|
7463
|
-
/**
|
|
7464
|
-
|
|
7495
|
+
/**
|
|
7496
|
+
* Why a remote could not be read, said for the transport the repository uses and for the credential
|
|
7497
|
+
* the Host offered it, which `credential` names as the status does.
|
|
7498
|
+
*/
|
|
7499
|
+
function issueText(t, issue, url, error, credential = "none") {
|
|
7465
7500
|
const transport = transportOf(url);
|
|
7466
7501
|
switch (issue) {
|
|
7467
7502
|
case "auth":
|
|
7503
|
+
if (transport === "https" && credential !== "none") return rejectedText(t, url, credential);
|
|
7468
7504
|
if (transport === "https") return onGitHub(url) ? t("config.syncIssueAuthHttps") : t("config.syncIssueAuthToken");
|
|
7469
7505
|
return transport === "ssh" ? t("config.syncIssueAuthSsh") : t("config.syncIssueAuth");
|
|
7470
7506
|
case "not-found": return onGitHub(url) ? t("config.syncIssueNotFoundGitHub") : t("config.syncIssueNotFound");
|
|
@@ -7473,6 +7509,16 @@ window.__ModuleLoader__.load({
|
|
|
7473
7509
|
default: return t("config.syncIssueUnknown", { error: error ?? "" });
|
|
7474
7510
|
}
|
|
7475
7511
|
}
|
|
7512
|
+
/**
|
|
7513
|
+
* A credential the host refused, said for where it came from: a saved token or the environment variable
|
|
7514
|
+
* comes before the GitHub sign-in, so only replacing that one changes what Git is given.
|
|
7515
|
+
*/
|
|
7516
|
+
function rejectedText(t, url, credential) {
|
|
7517
|
+
const github = onGitHub(url);
|
|
7518
|
+
if (credential === "environment") return t(github ? "config.syncIssueRejectedGitHubEnvironment" : "config.syncIssueRejectedEnvironment", { env: MNEMON_SYNC_TOKEN_ENV });
|
|
7519
|
+
if (credential === "github") return t("config.syncIssueRejectedGitHub");
|
|
7520
|
+
return t(github ? "config.syncIssueRejectedGitHubToken" : "config.syncIssueRejected");
|
|
7521
|
+
}
|
|
7476
7522
|
const REMOTE_ISSUE = {
|
|
7477
7523
|
"remote-auth": "auth",
|
|
7478
7524
|
"remote-not-found": "not-found",
|
|
@@ -7480,9 +7526,9 @@ window.__ModuleLoader__.load({
|
|
|
7480
7526
|
"remote-timeout": "timeout"
|
|
7481
7527
|
};
|
|
7482
7528
|
/** A failure the Host named, in this page's language; anything it could not name keeps its own words. */
|
|
7483
|
-
function failureText(t, reason, message, url) {
|
|
7529
|
+
function failureText(t, reason, message, url, credential = "none") {
|
|
7484
7530
|
const issue = reason === void 0 ? void 0 : REMOTE_ISSUE[reason];
|
|
7485
|
-
if (issue !== void 0) return issueText(t, issue, url, message);
|
|
7531
|
+
if (issue !== void 0) return issueText(t, issue, url, message, credential);
|
|
7486
7532
|
switch (reason) {
|
|
7487
7533
|
case "busy": return t("config.syncReasonBusy");
|
|
7488
7534
|
case "no-repository": return t("config.syncReasonNoRepository");
|
|
@@ -7541,7 +7587,7 @@ window.__ModuleLoader__.load({
|
|
|
7541
7587
|
* which computer, and what it held. A page is read at a time; the commit is a
|
|
7542
7588
|
* quiet tag, because the time and the computer are what a reader looks for.
|
|
7543
7589
|
*/
|
|
7544
|
-
function SyncHistoryDialog({ client, repoUrl, t, onClose }) {
|
|
7590
|
+
function SyncHistoryDialog({ client, repoUrl, credential, t, onClose }) {
|
|
7545
7591
|
const [history, setHistory] = (0, react.useState)(null);
|
|
7546
7592
|
const [busy, setBusy] = (0, react.useState)(true);
|
|
7547
7593
|
const [failed, setFailed] = (0, react.useState)(null);
|
|
@@ -7561,13 +7607,14 @@ window.__ModuleLoader__.load({
|
|
|
7561
7607
|
};
|
|
7562
7608
|
});
|
|
7563
7609
|
} catch (reason) {
|
|
7564
|
-
setFailed(failureText(t, reasonOf(reason), message(reason), repoUrl));
|
|
7610
|
+
setFailed(failureText(t, reasonOf(reason), message(reason), repoUrl, credential));
|
|
7565
7611
|
} finally {
|
|
7566
7612
|
setBusy(false);
|
|
7567
7613
|
}
|
|
7568
7614
|
}, [
|
|
7569
7615
|
client,
|
|
7570
7616
|
repoUrl,
|
|
7617
|
+
credential,
|
|
7571
7618
|
t
|
|
7572
7619
|
]);
|
|
7573
7620
|
(0, react.useEffect)(() => {
|
|
@@ -7647,7 +7694,7 @@ window.__ModuleLoader__.load({
|
|
|
7647
7694
|
* deleted, and which removals the branch recorded. Reading writes nothing; the
|
|
7648
7695
|
* footer offers the sync itself and a merge that publishes nothing.
|
|
7649
7696
|
*/
|
|
7650
|
-
function SyncCompareDialog({ client, repoUrl, writable, t, onClose, onSync, onChanged }) {
|
|
7697
|
+
function SyncCompareDialog({ client, repoUrl, credential, writable, t, onClose, onSync, onChanged }) {
|
|
7651
7698
|
const [preview, setPreview] = (0, react.useState)(null);
|
|
7652
7699
|
const [difference, setDifference] = (0, react.useState)(null);
|
|
7653
7700
|
const [busy, setBusy] = (0, react.useState)("read");
|
|
@@ -7668,13 +7715,14 @@ window.__ModuleLoader__.load({
|
|
|
7668
7715
|
setPreview(null);
|
|
7669
7716
|
setDifference(null);
|
|
7670
7717
|
setEmpty(reasonOf(reason) === "remote-empty");
|
|
7671
|
-
setFailed(failureText(t, reasonOf(reason), message(reason), repoUrl));
|
|
7718
|
+
setFailed(failureText(t, reasonOf(reason), message(reason), repoUrl, credential));
|
|
7672
7719
|
} finally {
|
|
7673
7720
|
setBusy(null);
|
|
7674
7721
|
}
|
|
7675
7722
|
}, [
|
|
7676
7723
|
client,
|
|
7677
7724
|
repoUrl,
|
|
7725
|
+
credential,
|
|
7678
7726
|
t
|
|
7679
7727
|
]);
|
|
7680
7728
|
(0, react.useEffect)(() => {
|
|
@@ -7691,7 +7739,7 @@ window.__ModuleLoader__.load({
|
|
|
7691
7739
|
const added = result.runtime?.added ?? 0;
|
|
7692
7740
|
setNotice(revive ? t("config.syncCompareRevived", { count: added }) : added > 0 ? t("config.syncMergedCount", { count: added }) : t("config.syncMerged", { commit: result.commit.slice(0, 8) }));
|
|
7693
7741
|
} catch (reason) {
|
|
7694
|
-
failure = failureText(t, reasonOf(reason), message(reason), repoUrl);
|
|
7742
|
+
failure = failureText(t, reasonOf(reason), message(reason), repoUrl, credential);
|
|
7695
7743
|
} finally {
|
|
7696
7744
|
setBusy(null);
|
|
7697
7745
|
}
|
|
@@ -8659,6 +8707,7 @@ window.__ModuleLoader__.load({
|
|
|
8659
8707
|
const connected = status !== null && status.enabled !== false && repoUrl !== void 0;
|
|
8660
8708
|
const editable = !disabled && client !== null;
|
|
8661
8709
|
const gitReady = status?.git.available === true;
|
|
8710
|
+
const credential = status?.config.credentialSource ?? "none";
|
|
8662
8711
|
/**
|
|
8663
8712
|
* The one primary action: merge the repository here, then publish the result.
|
|
8664
8713
|
* Answers why it did not finish, or null when it did, for a caller that shows it elsewhere.
|
|
@@ -8669,11 +8718,11 @@ window.__ModuleLoader__.load({
|
|
|
8669
8718
|
setSyncFeedback(null);
|
|
8670
8719
|
let failure = null;
|
|
8671
8720
|
try {
|
|
8672
|
-
const feedback = syncResult(t, await client.pushSync(), repoUrl);
|
|
8721
|
+
const feedback = syncResult(t, await client.pushSync(), repoUrl, credential);
|
|
8673
8722
|
setSyncFeedback(feedback);
|
|
8674
8723
|
if (feedback.tone === "error") failure = feedback.text;
|
|
8675
8724
|
} catch (reason) {
|
|
8676
|
-
failure = t("config.syncFailed", { error: failureText(t, reasonOf(reason), message(reason), repoUrl) });
|
|
8725
|
+
failure = t("config.syncFailed", { error: failureText(t, reasonOf(reason), message(reason), repoUrl, credential) });
|
|
8677
8726
|
setSyncFeedback({
|
|
8678
8727
|
tone: "error",
|
|
8679
8728
|
text: failure
|
|
@@ -8755,7 +8804,7 @@ window.__ModuleLoader__.load({
|
|
|
8755
8804
|
}));
|
|
8756
8805
|
const autoHint = autoBackup?.lastError !== void 0 && minutes > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8757
8806
|
className: MnemonSettingsCard_module_css_default.error,
|
|
8758
|
-
children: t("config.syncAutoFailed", { error: failureText(t, autoBackup.lastFailure, autoBackup.lastError, repoUrl ?? "") })
|
|
8807
|
+
children: t("config.syncAutoFailed", { error: failureText(t, autoBackup.lastFailure, autoBackup.lastError, repoUrl ?? "", credential) })
|
|
8759
8808
|
}) : minutes <= 0 ? t("config.syncAutoHintOff") : [autoBackup?.nextAt === void 0 ? "" : t("config.syncAutoNext", { time: when(autoBackup.nextAt) }), autoBackup?.lastAt === void 0 ? "" : t("config.syncAutoLast", { time: when(autoBackup.lastAt) })].filter(Boolean).join(" · ") || t("config.syncAutoHintOn");
|
|
8760
8809
|
const lastRun = status?.lastRun;
|
|
8761
8810
|
const syncHint = lastRun === void 0 ? t("config.syncNever") : lastRun.kind === "merge" ? t("config.syncLastMerge", { time: when(lastRun.at) }) : t("config.syncLast", { time: when(lastRun.at) });
|
|
@@ -8913,6 +8962,7 @@ window.__ModuleLoader__.load({
|
|
|
8913
8962
|
client !== null && repoUrl !== void 0 && dialog === "compare" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SyncCompareDialog, {
|
|
8914
8963
|
client,
|
|
8915
8964
|
repoUrl,
|
|
8965
|
+
credential,
|
|
8916
8966
|
writable: editable,
|
|
8917
8967
|
t,
|
|
8918
8968
|
onClose: () => setDialog(null),
|
|
@@ -8925,6 +8975,7 @@ window.__ModuleLoader__.load({
|
|
|
8925
8975
|
client !== null && repoUrl !== void 0 && dialog === "history" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SyncHistoryDialog, {
|
|
8926
8976
|
client,
|
|
8927
8977
|
repoUrl,
|
|
8978
|
+
credential,
|
|
8928
8979
|
t,
|
|
8929
8980
|
onClose: () => setDialog(null)
|
|
8930
8981
|
}),
|
|
@@ -8945,7 +8996,7 @@ window.__ModuleLoader__.load({
|
|
|
8945
8996
|
});
|
|
8946
8997
|
}
|
|
8947
8998
|
/** What one finished sync did, said as one line. */
|
|
8948
|
-
function syncResult(t, result, repoUrl) {
|
|
8999
|
+
function syncResult(t, result, repoUrl, credential) {
|
|
8949
9000
|
const added = result.merged?.runtime?.added ?? 0;
|
|
8950
9001
|
const commit = result.commit.slice(0, 8);
|
|
8951
9002
|
if (result.pushed) return {
|
|
@@ -8961,7 +9012,7 @@ window.__ModuleLoader__.load({
|
|
|
8961
9012
|
};
|
|
8962
9013
|
return {
|
|
8963
9014
|
tone: "error",
|
|
8964
|
-
text: t("config.syncPushFailed", { reason: failureText(t, result.failure, result.reason ?? "", repoUrl) })
|
|
9015
|
+
text: t("config.syncPushFailed", { reason: failureText(t, result.failure, result.reason ?? "", repoUrl, credential) })
|
|
8965
9016
|
};
|
|
8966
9017
|
}
|
|
8967
9018
|
/** Where the repository is, whether it answers, and how it is authenticated. */
|
|
@@ -8977,7 +9028,7 @@ window.__ModuleLoader__.load({
|
|
|
8977
9028
|
text: t("config.syncReasonGitMissing")
|
|
8978
9029
|
} : !remote.reachable ? {
|
|
8979
9030
|
dot: "error",
|
|
8980
|
-
text: issueText(t, remote.issue, url, remote.error)
|
|
9031
|
+
text: issueText(t, remote.issue, url, remote.error, status.config.credentialSource)
|
|
8981
9032
|
} : {
|
|
8982
9033
|
dot: credential === void 0 && transportOf(url) === "https" ? "warning" : "done",
|
|
8983
9034
|
text: [remote.branchExists ? t("config.syncStateConnected") : t("config.syncStateNewBranch", { branch: status.config.branch }), ...credential !== void 0 ? [credential] : transportOf(url) === "https" ? [onGitHub(url) ? t("config.syncNoCredential") : t("config.syncNoCredentialToken")] : []].join(" · ")
|
package/lib/index.js
CHANGED
|
@@ -155,7 +155,11 @@ const HTTPS = /^https:\/\/[^\s/]+(?::\d+)?\/[^\s]+$/u;
|
|
|
155
155
|
const SSH = /^ssh:\/\/[^\s/]+(?::\d+)?\/[^\s]+$/u;
|
|
156
156
|
const SCP = /^[A-Za-z0-9._-]+@[A-Za-z0-9._-]+:[^\s]+$/u;
|
|
157
157
|
const SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//u;
|
|
158
|
-
const
|
|
158
|
+
const HTTPS_USER = /^https:\/\/([^/]*)@/u;
|
|
159
|
+
const USER_TEXT = /^(?:[A-Za-z0-9._~!$&'()*+,;=-]|%[0-9A-Fa-f]{2})+$/u;
|
|
160
|
+
const NOT_A_NAME = /[:@\s\p{Cc}]/u;
|
|
161
|
+
const TOKEN_SHAPE = /^(?:gh[opsru]_|github_pat_|glpat-|gldt-|glcbt-|hf_|ATBB|ATCTT|ATATT)|^[0-9A-Fa-f]{32,}$|^[A-Za-z0-9]{52,}$/u;
|
|
162
|
+
const USER_NAME_MAX = 100;
|
|
159
163
|
const EMAIL = /^[^\s<>@]+@[^\s<>@]+$/u;
|
|
160
164
|
const CONTROL = /[\p{Cc}\u2028\u2029]/u;
|
|
161
165
|
const GITHUB_REPOSITORY = /^(?!\.\.?$)[A-Za-z0-9._-]{1,100}$/u;
|
|
@@ -172,16 +176,34 @@ function syncDirectoryParts(value) {
|
|
|
172
176
|
}
|
|
173
177
|
/**
|
|
174
178
|
* What is wrong with a repository address, or nothing for an https:// or ssh:// URL without
|
|
175
|
-
* credentials in it, a git@host:path address, or a local path that `localPath` takes.
|
|
179
|
+
* credentials in it, a git@host:path address, or a local path that `localPath` takes. An HTTPS
|
|
180
|
+
* address may name the user a host wants its token offered under, as Bitbucket's do, but never a
|
|
181
|
+
* password, nor a token written where that name goes.
|
|
176
182
|
*/
|
|
177
183
|
function syncRepositoryProblem(url, localPath) {
|
|
178
184
|
if (url === "" || url.length > 2e3 || /\s/u.test(url)) return "format";
|
|
179
185
|
if (SCHEME.test(url)) {
|
|
180
186
|
if (!HTTPS.test(url) && !SSH.test(url)) return "format";
|
|
181
|
-
|
|
187
|
+
const user = HTTPS_USER.exec(url)?.[1];
|
|
188
|
+
return user === void 0 || syncUserName(user) ? void 0 : "credentials";
|
|
182
189
|
}
|
|
183
190
|
return SCP.test(url) || localPath(url) ? void 0 : "format";
|
|
184
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Whether the user part of an HTTPS address is a user name. It is judged as Git reads it, with its
|
|
194
|
+
* escapes decoded, so an escaped colon, an escaped token prefix or an escaped line break counts as what
|
|
195
|
+
* it decodes to; one that does not decode is no name at all.
|
|
196
|
+
*/
|
|
197
|
+
function syncUserName(written) {
|
|
198
|
+
if (!USER_TEXT.test(written)) return false;
|
|
199
|
+
let name;
|
|
200
|
+
try {
|
|
201
|
+
name = decodeURIComponent(written);
|
|
202
|
+
} catch {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return name.length <= USER_NAME_MAX && !NOT_A_NAME.test(name) && !TOKEN_SHAPE.test(name);
|
|
206
|
+
}
|
|
185
207
|
/** A commit author's address. */
|
|
186
208
|
function syncEmailValid(email) {
|
|
187
209
|
return email.length <= 320 && EMAIL.test(email);
|
|
@@ -4597,6 +4619,47 @@ function isGitHubHttps(repoUrl) {
|
|
|
4597
4619
|
}
|
|
4598
4620
|
}
|
|
4599
4621
|
/**
|
|
4622
|
+
* The user name a token goes to Git under: the one GitHub documents for its tokens and its sign-in's
|
|
4623
|
+
* grant, which every host has been offered, and which stays for every host not known to refuse it.
|
|
4624
|
+
*/
|
|
4625
|
+
const TOKEN_USER = "x-access-token";
|
|
4626
|
+
/**
|
|
4627
|
+
* The hosts known to refuse `x-access-token` with a token, and the name each takes instead: Gitee
|
|
4628
|
+
* answers it with HTTP 403 and takes `oauth2` or the account name (#408). Any other host that wants
|
|
4629
|
+
* a name of its own, a self-hosted Gitee included, is given it in the address.
|
|
4630
|
+
*/
|
|
4631
|
+
const TOKEN_USER_BY_HOST = /* @__PURE__ */ new Map([["gitee.com", "oauth2"]]);
|
|
4632
|
+
/**
|
|
4633
|
+
* From this version on, the last `credential.username` Git reads wins, which is the command line's.
|
|
4634
|
+
* Before it, the first one configured did, and only a name in the address came before that.
|
|
4635
|
+
*/
|
|
4636
|
+
const LAST_USERNAME_WINS_GIT = "2.26";
|
|
4637
|
+
/**
|
|
4638
|
+
* Who Git is to look the token up as for one HTTPS address, and for which host: the user name the
|
|
4639
|
+
* address names, else the one its host takes a token under; the host exactly as the address writes
|
|
4640
|
+
* it, since Git compares a stored credential's host as text, its case and port included; and the
|
|
4641
|
+
* address without its user name. `user` is written as an address writes it, `name` as Git reads it.
|
|
4642
|
+
*/
|
|
4643
|
+
function credentialTarget(url) {
|
|
4644
|
+
const [, written, host = "", rest = ""] = /^https:\/\/(?:([^/@]*)@)?([^/]*)(.*)$/u.exec(url) ?? [];
|
|
4645
|
+
let hostname = "";
|
|
4646
|
+
try {
|
|
4647
|
+
hostname = new URL(url).hostname.toLowerCase().replace(/\.$/u, "");
|
|
4648
|
+
} catch {}
|
|
4649
|
+
const user = written ?? TOKEN_USER_BY_HOST.get(hostname) ?? TOKEN_USER;
|
|
4650
|
+
let name = user;
|
|
4651
|
+
try {
|
|
4652
|
+
name = decodeURIComponent(user);
|
|
4653
|
+
} catch {}
|
|
4654
|
+
return {
|
|
4655
|
+
user,
|
|
4656
|
+
name,
|
|
4657
|
+
named: written !== void 0,
|
|
4658
|
+
host,
|
|
4659
|
+
address: "https://" + host + rest
|
|
4660
|
+
};
|
|
4661
|
+
}
|
|
4662
|
+
/**
|
|
4600
4663
|
* What kind of failure one Git error describes. Git words these differently per
|
|
4601
4664
|
* transport and per host, so the patterns are the phrases each transport uses:
|
|
4602
4665
|
* a refused or missing credential, an address that leads nowhere (GitHub answers
|
|
@@ -4644,6 +4707,14 @@ function versionAtLeast(version, minimum) {
|
|
|
4644
4707
|
return true;
|
|
4645
4708
|
}
|
|
4646
4709
|
/**
|
|
4710
|
+
* A boolean as Git reads one: `true`, `yes`, `on` or a number other than 0 is true, and
|
|
4711
|
+
* `false`, `no`, `off`, 0 or an empty value is false.
|
|
4712
|
+
*/
|
|
4713
|
+
function gitBoolean(value) {
|
|
4714
|
+
const text = value.trim().toLowerCase();
|
|
4715
|
+
return text === "true" || text === "yes" || text === "on" || /^[-+]?\d+$/u.test(text) && Number(text) !== 0;
|
|
4716
|
+
}
|
|
4717
|
+
/**
|
|
4647
4718
|
* The deletions one side of the channel recorded. They live in the sync extension, never
|
|
4648
4719
|
* in the payload, so a Pack stays readable by any version; a branch that predates the
|
|
4649
4720
|
* extension recorded none, and an unreadable list is treated the same way rather than
|
|
@@ -4932,6 +5003,8 @@ var MnemonGitSync = class {
|
|
|
4932
5003
|
store;
|
|
4933
5004
|
machine;
|
|
4934
5005
|
sequence = 0;
|
|
5006
|
+
/** The Git version the last check found, which decides how a token's user name is given to Git. */
|
|
5007
|
+
gitVersion;
|
|
4935
5008
|
auth;
|
|
4936
5009
|
autoBackupView;
|
|
4937
5010
|
constructor(runner, config, packs, run = runProcess, now = () => /* @__PURE__ */ new Date()) {
|
|
@@ -5579,12 +5652,14 @@ var MnemonGitSync = class {
|
|
|
5579
5652
|
const result = await this.git(["--version"], { signal });
|
|
5580
5653
|
version = /(\d+\.\d+(?:\.\d+)?)/u.exec(result.stdout)?.[1];
|
|
5581
5654
|
} catch (error) {
|
|
5655
|
+
this.gitVersion = void 0;
|
|
5582
5656
|
return {
|
|
5583
5657
|
available: false,
|
|
5584
5658
|
required,
|
|
5585
5659
|
issue: error instanceof Error ? error.message : String(error)
|
|
5586
5660
|
};
|
|
5587
5661
|
}
|
|
5662
|
+
this.gitVersion = version;
|
|
5588
5663
|
if (version === void 0) return {
|
|
5589
5664
|
available: false,
|
|
5590
5665
|
required,
|
|
@@ -6116,7 +6191,7 @@ var MnemonGitSync = class {
|
|
|
6116
6191
|
}
|
|
6117
6192
|
}
|
|
6118
6193
|
async git(args, options) {
|
|
6119
|
-
const prepared = this.credentials(args, options);
|
|
6194
|
+
const prepared = await this.credentials(args, options);
|
|
6120
6195
|
try {
|
|
6121
6196
|
return await this.run(GIT_LABEL, prepared.args, {
|
|
6122
6197
|
timeoutMs: options.timeoutMs ?? 12e4,
|
|
@@ -6134,22 +6209,43 @@ var MnemonGitSync = class {
|
|
|
6134
6209
|
* Hand the token to Git through a `0600` credential store that lives for one
|
|
6135
6210
|
* command. The token never reaches an argument, a URL, a log, or the mirror,
|
|
6136
6211
|
* and it is removed before the caller sees the result.
|
|
6212
|
+
*
|
|
6213
|
+
* Git must look the token up under the name the store holds, not under one this
|
|
6214
|
+
* machine configures, as Git Credential Manager setups do, nor by a path the store
|
|
6215
|
+
* does not hold (#408). Git 2.26 and later are told so on the command line, which
|
|
6216
|
+
* they read last and let win, and are given the address without a user name, as
|
|
6217
|
+
* Git offers a name it finds there once without the token. An older Git kept the
|
|
6218
|
+
* first name it read, and only a name in the address comes before that: the name
|
|
6219
|
+
* goes there only where this machine's configuration names another, and the path
|
|
6220
|
+
* leaves the lookup only where it puts the path in, so every other command, one
|
|
6221
|
+
* that configures the store's own name included, stays the one that has always run.
|
|
6137
6222
|
*/
|
|
6138
|
-
credentials(args, options) {
|
|
6223
|
+
async credentials(args, options) {
|
|
6139
6224
|
const token = options.token;
|
|
6140
6225
|
if (token === void 0 || token === "" || options.authenticated !== true) return {
|
|
6141
6226
|
args: [...NO_REWRITE, ...args],
|
|
6142
6227
|
cleanup: () => {}
|
|
6143
6228
|
};
|
|
6144
|
-
const
|
|
6145
|
-
if (
|
|
6229
|
+
const index = args.findIndex((argument) => argument.startsWith("https://"));
|
|
6230
|
+
if (index === -1) return {
|
|
6146
6231
|
args: [...NO_REWRITE, ...args],
|
|
6147
6232
|
cleanup: () => {}
|
|
6148
6233
|
};
|
|
6234
|
+
const url = args[index];
|
|
6235
|
+
const target = credentialTarget(url);
|
|
6236
|
+
const command = [...args];
|
|
6237
|
+
const lookup = [];
|
|
6238
|
+
if (this.gitVersion !== void 0 && versionAtLeast(this.gitVersion, LAST_USERNAME_WINS_GIT)) {
|
|
6239
|
+
lookup.push("-c", "credential.useHttpPath=false", "-c", "credential.username=" + target.name);
|
|
6240
|
+
command[index] = target.address;
|
|
6241
|
+
} else {
|
|
6242
|
+
const configured = await this.configuredLookup(url, options);
|
|
6243
|
+
if (configured.path) lookup.push("-c", "credential.useHttpPath=false");
|
|
6244
|
+
if (configured.user !== void 0 && configured.user !== target.name && !target.named) command[index] = "https://" + target.user + "@" + target.address.slice(8);
|
|
6245
|
+
}
|
|
6149
6246
|
this.sequence += 1;
|
|
6150
|
-
const host = new URL(url).host;
|
|
6151
6247
|
const path = join(this.store.directory(), ".credentials." + branchSlug(String(process.pid)) + "." + String(this.sequence) + ".tmp");
|
|
6152
|
-
writeFileSync(path, "https://
|
|
6248
|
+
writeFileSync(path, "https://" + target.user + ":" + encodeURIComponent(token) + "@" + target.host + "\n", {
|
|
6153
6249
|
encoding: "utf8",
|
|
6154
6250
|
mode: 384
|
|
6155
6251
|
});
|
|
@@ -6160,13 +6256,53 @@ var MnemonGitSync = class {
|
|
|
6160
6256
|
"credential.helper=",
|
|
6161
6257
|
"-c",
|
|
6162
6258
|
"credential.helper=store --file=" + shellQuote(path),
|
|
6163
|
-
...
|
|
6259
|
+
...lookup,
|
|
6260
|
+
...command
|
|
6164
6261
|
],
|
|
6165
6262
|
cleanup: () => {
|
|
6166
6263
|
rmSync(path, { force: true });
|
|
6167
6264
|
}
|
|
6168
6265
|
};
|
|
6169
6266
|
}
|
|
6267
|
+
/**
|
|
6268
|
+
* The user name this machine's configuration gives an address, if any, and whether
|
|
6269
|
+
* it adds the address's path to the lookup, read in the environment and directory
|
|
6270
|
+
* the command runs in. A configuration Git cannot read leaves the command as it
|
|
6271
|
+
* was, which fails on it the same way.
|
|
6272
|
+
*/
|
|
6273
|
+
async configuredLookup(url, options) {
|
|
6274
|
+
let result;
|
|
6275
|
+
try {
|
|
6276
|
+
result = await this.run(GIT_LABEL, [
|
|
6277
|
+
"config",
|
|
6278
|
+
"--get-urlmatch",
|
|
6279
|
+
"credential",
|
|
6280
|
+
url
|
|
6281
|
+
], {
|
|
6282
|
+
timeoutMs: options.timeoutMs ?? 12e4,
|
|
6283
|
+
env: gitEnvironment(),
|
|
6284
|
+
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
6285
|
+
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
6286
|
+
label: GIT_LABEL
|
|
6287
|
+
});
|
|
6288
|
+
} catch (error) {
|
|
6289
|
+
if (options.signal?.aborted === true) throw error;
|
|
6290
|
+
return {
|
|
6291
|
+
user: void 0,
|
|
6292
|
+
path: false
|
|
6293
|
+
};
|
|
6294
|
+
}
|
|
6295
|
+
const settings = /* @__PURE__ */ new Map();
|
|
6296
|
+
if (result.exitCode === 0) for (const line of result.stdout.split(/\r?\n/u)) {
|
|
6297
|
+
const space = line.indexOf(" ");
|
|
6298
|
+
if (space > 0) settings.set(line.slice(0, space).toLowerCase(), line.slice(space + 1));
|
|
6299
|
+
}
|
|
6300
|
+
const path = settings.get("credential.usehttppath");
|
|
6301
|
+
return {
|
|
6302
|
+
user: settings.get("credential.username"),
|
|
6303
|
+
path: path !== void 0 && gitBoolean(path)
|
|
6304
|
+
};
|
|
6305
|
+
}
|
|
6170
6306
|
};
|
|
6171
6307
|
/**
|
|
6172
6308
|
* How the payload files differ. The manifest and the checksum inventory describe a
|
|
@@ -12313,7 +12449,17 @@ var MnemonLifecycle = class {
|
|
|
12313
12449
|
this.taskAgentIds.delete(sessionId);
|
|
12314
12450
|
}
|
|
12315
12451
|
}
|
|
12316
|
-
/**
|
|
12452
|
+
/**
|
|
12453
|
+
* Resolve the same model route and preset composition as an ordinary fresh DSH Agent.
|
|
12454
|
+
*
|
|
12455
|
+
* DSH persists a task Agent's session like any other. Its sidebar and ACP session
|
|
12456
|
+
* list leave out a session whose header has `origin: 'subagent'`, the origin DSH
|
|
12457
|
+
* gives its own subagents. Without it the sidebar hides a session that never started
|
|
12458
|
+
* a turn only while DSH's projection cache holds a record of it, so a finished task
|
|
12459
|
+
* could stay listed as an untitled Ungrouped conversation (#435). No `parentSession`
|
|
12460
|
+
* or `delegationDepth`: the task belongs to no conversation, and its children stay
|
|
12461
|
+
* one level deep.
|
|
12462
|
+
*/
|
|
12317
12463
|
async taskAgentCreation(fallbackSessionId, workspaceRoot, signal) {
|
|
12318
12464
|
const agentOptions = this.taskAgentModelOptions(fallbackSessionId, workspaceRoot);
|
|
12319
12465
|
if (agentOptions === void 0) throw new Error("no default provider/model is available for a clean task Agent");
|
|
@@ -12321,15 +12467,17 @@ var MnemonLifecycle = class {
|
|
|
12321
12467
|
const presets = presetService(this.ctx.get("agentPresets"));
|
|
12322
12468
|
const presetId = presets === void 0 ? void 0 : (await presets.resolve()).id;
|
|
12323
12469
|
const workingDirectory = workFrom === void 0 ? void 0 : this.ctx.get("workingDirectory");
|
|
12470
|
+
const meta = {
|
|
12471
|
+
cwd,
|
|
12472
|
+
origin: "subagent",
|
|
12473
|
+
...presetId === void 0 ? {} : { agentPreset: presetId }
|
|
12474
|
+
};
|
|
12324
12475
|
if (presets === void 0 && typeof workingDirectory?.set !== "function") return {
|
|
12325
|
-
meta
|
|
12476
|
+
meta,
|
|
12326
12477
|
agentOptions
|
|
12327
12478
|
};
|
|
12328
12479
|
return {
|
|
12329
|
-
meta
|
|
12330
|
-
cwd,
|
|
12331
|
-
...presetId === void 0 ? {} : { agentPreset: presetId }
|
|
12332
|
-
},
|
|
12480
|
+
meta,
|
|
12333
12481
|
agentOptions,
|
|
12334
12482
|
setup: async (agentCtx, agent) => {
|
|
12335
12483
|
if (presets !== void 0 && presetId !== void 0) await presets.mount(agentCtx, presetId);
|
|
@@ -537,6 +537,11 @@ export declare const zh: {
|
|
|
537
537
|
'config.syncNoCredentialToken': string;
|
|
538
538
|
'config.syncIssueAuthHttps': string;
|
|
539
539
|
'config.syncIssueAuthToken': string;
|
|
540
|
+
'config.syncIssueRejectedGitHub': string;
|
|
541
|
+
'config.syncIssueRejectedGitHubToken': string;
|
|
542
|
+
'config.syncIssueRejectedGitHubEnvironment': string;
|
|
543
|
+
'config.syncIssueRejected': string;
|
|
544
|
+
'config.syncIssueRejectedEnvironment': string;
|
|
540
545
|
'config.syncIssueAuthSsh': string;
|
|
541
546
|
'config.syncIssueAuth': string;
|
|
542
547
|
'config.syncIssueNotFound': string;
|
|
@@ -13,7 +13,9 @@ export declare function syncDirectoryParts(value: string): string[] | undefined;
|
|
|
13
13
|
export type MnemonSyncRepositoryProblem = 'format' | 'credentials';
|
|
14
14
|
/**
|
|
15
15
|
* What is wrong with a repository address, or nothing for an https:// or ssh:// URL without
|
|
16
|
-
* credentials in it, a git@host:path address, or a local path that `localPath` takes.
|
|
16
|
+
* credentials in it, a git@host:path address, or a local path that `localPath` takes. An HTTPS
|
|
17
|
+
* address may name the user a host wants its token offered under, as Bitbucket's do, but never a
|
|
18
|
+
* password, nor a token written where that name goes.
|
|
17
19
|
*/
|
|
18
20
|
export declare function syncRepositoryProblem(url: string, localPath: (path: string) => boolean): MnemonSyncRepositoryProblem | undefined;
|
|
19
21
|
/**
|
package/package.json
CHANGED