source-code-mgmt 1.5.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +210 -0
- package/README.md +56 -83
- package/cordis.patch.yml +15 -0
- package/lib/client.js +514 -366
- package/lib/index.js +143 -64
- package/package.json +41 -38
package/lib/index.js
CHANGED
|
@@ -24,6 +24,20 @@ import {
|
|
|
24
24
|
import { join, dirname } from 'node:path'
|
|
25
25
|
import { homedir } from 'node:os'
|
|
26
26
|
|
|
27
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
28
|
+
|
|
29
|
+
// ---------- i18n: host messages follow the client’s language (?lang= query param, per request) ----------
|
|
30
|
+
const HOST_EN = {"尚未配置 Gitee 私人令牌(请打开 ③ 代码管理输入令牌)":"No Gitee personal token configured (enter one in ③ Code Management)","Gitee API 请求失败(需要 curl)":"Gitee API request failed (curl required)","not a git repository(尚未 git init,可用「新建仓库并推送」初始化为 git 仓库并上传)":"not a git repository (run \"Create repo & push\" to init one and upload)","超过 GitHub 100MB 单文件限制(${}),已被 .gitignore 的 \"${}\" 排除":"Exceeds GitHub's 100MB file limit (${}) — already excluded by .gitignore entry \"${}\"","超过 GitHub 100MB 单文件限制(${}),${} 所在的一级目录「${}」整体忽略(该文件夹为一整体)":"Exceeds GitHub's 100MB file limit (${}) — the whole first-level folder \"${}\" of ${} is ignored (the folder is one unit)","超过 GitHub 100MB 单文件限制(${}),已忽略单个文件「${}」":"Exceeds GitHub's 100MB file limit (${}) — the file \"${}\" was ignored individually","尚未配置远程仓库 origin,请使用「新建仓库」创建远程仓库。":"No origin remote configured — use \"Create repo\" to create a remote repo.","not a git repository(尚未 git init,无法拉取)":"not a git repository (no git init yet — cannot pull)","尚未配置远程仓库 origin,无法拉取。":"No origin remote configured — cannot pull.","拉取存在冲突:本地有未合并改动或与远程冲突,请手动 git pull 处理合并。":"Pull conflicts: you have unmerged local changes or conflicts with the remote — resolve them manually with git pull.","git pull 失败":"git pull failed","not a git repository(尚未 git init)":"not a git repository (run git init first)","拉取并推送失败:合并存在冲突,请手动解决后重试。":"Pull-and-push failed: merge conflicts — resolve them manually and retry.","pull --rebase 失败":"pull --rebase failed","push 失败":"push failed","push --force 失败":"push --force failed","强制拉取存在冲突,请手动处理。":"Force-pull conflicts — handle them manually.","git pull --force 失败":"git pull --force failed","缺少仓库名称":"Repo name required","仓库名称包含非法字符(仅允许字母、数字、点、下划线、横线)":"Repo name contains invalid characters (only letters, digits, dots, underscores, dashes)","git add 失败:${}":"git add failed: ${}","需要先配置 Gitee 私人令牌":"Configure the Gitee personal token first","Gitee 创建仓库失败":"Failed to create the Gitee repo","缺少文件路径":"File path required","无法识别当前分支":"Cannot determine the current branch","尚未配置远程仓库 origin,无法对齐。":"No origin remote configured — cannot align.","git fetch 失败":"git fetch failed","git reset --hard 失败":"git reset --hard failed","git init 失败":"git init failed","git add 失败":"git add failed","git reset 失败":"git reset failed","提交信息不能为空":"Commit message cannot be empty","没有已暂存(staged)的改动可提交":"Nothing staged to commit","git commit 失败":"git commit failed","缺少分支名":"Branch name required","切换分支失败":"Failed to switch branch","git log 失败":"git log failed","缺少 commit hash":"Commit hash required","revert 失败(可能有冲突,请手动处理)":"revert failed (possible conflicts — handle manually)","cherry-pick 失败(可能有冲突,请手动处理)":"cherry-pick failed (possible conflicts — handle manually)","读取提交 diff 失败":"Failed to read the commit diff","未知工具:":"Unknown tool: ","未找到可用的包管理器(":"No usable package manager found (",")。请手动安装。":"). Please install manually.","安装成功":"Installed successfully","安装失败":"Installation failed","$dlg.Description = '选择本地目录';":"$dlg.Description = 'Choose a local folder';","Gitee 修改可见性失败":"Failed to change Gitee visibility","需要先登录 GitHub CLI(gh)":"Log in to the GitHub CLI (gh) first","修改可见性失败:${}":"Failed to change visibility: ${}","gh repo edit 失败":"gh repo edit failed","缺少目录路径":"Folder path required","未选择目录":"No folder selected","目录不存在或不是文件夹:":"Folder does not exist or is not a directory: ","令牌不能为空":"Token cannot be empty","Gitee 令牌无效,请检查(需有 projects 权限)":"Invalid Gitee token — check it (needs projects permission)"};
|
|
31
|
+
const requestLangStore = new AsyncLocalStorage();
|
|
32
|
+
function langOf(req) { try { const l = new URL(req.url ?? "", "http://localhost").searchParams.get("lang"); return l === "en" ? "en" : "zh" } catch { return "zh" } }
|
|
33
|
+
function lang() { return requestLangStore.getStore() ?? "zh" }
|
|
34
|
+
function tr(key, params) {
|
|
35
|
+
let s = lang() === "en" ? (HOST_EN[key] ?? key) : key;
|
|
36
|
+
if (params && params.length) { let i = 0; s = String(s).replace(/\$\{\}/g, () => String(params[i++] ?? "")) }
|
|
37
|
+
return s
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
27
41
|
/** Stable cordis plugin name (also the browser bundle id). */
|
|
28
42
|
export const name = 'source-code-mgmt'
|
|
29
43
|
|
|
@@ -525,7 +539,7 @@ function saveGiteeToken(token) {
|
|
|
525
539
|
*/
|
|
526
540
|
function giteeApi(method, path, body, token) {
|
|
527
541
|
const tok = token || readGiteeToken()
|
|
528
|
-
if (!tok) return { ok: false, error: '尚未配置 Gitee 私人令牌(请打开 ③ 代码管理输入令牌)' }
|
|
542
|
+
if (!tok) return { ok: false, error: tr('尚未配置 Gitee 私人令牌(请打开 ③ 代码管理输入令牌)') }
|
|
529
543
|
const args = [
|
|
530
544
|
'-sS', '-L',
|
|
531
545
|
'-X', String(method || 'GET').toUpperCase(),
|
|
@@ -539,7 +553,7 @@ function giteeApi(method, path, body, token) {
|
|
|
539
553
|
args.push('https://gitee.com/api/v5/' + String(path).replace(/^\/+/, ''))
|
|
540
554
|
const r = run(CURL, args, { timeout: 70_000 })
|
|
541
555
|
if (!r.ok) {
|
|
542
|
-
return { ok: false, error: (r.stderr || r.stdout || '').trim() || 'Gitee API 请求失败(需要 curl)' }
|
|
556
|
+
return { ok: false, error: (r.stderr || r.stdout || '').trim() || tr('Gitee API 请求失败(需要 curl)') }
|
|
543
557
|
}
|
|
544
558
|
let data
|
|
545
559
|
try { data = JSON.parse(r.stdout || '{}') } catch { data = null }
|
|
@@ -733,7 +747,7 @@ function repoStatus(dir, provider = 'github', full = false) {
|
|
|
733
747
|
repoOwner: ev.owner,
|
|
734
748
|
visibility: ev.checked && ev.exists ? ev.visibility : undefined,
|
|
735
749
|
provider,
|
|
736
|
-
error: 'not a git repository(尚未 git init,可用「新建仓库并推送」初始化为 git 仓库并上传)',
|
|
750
|
+
error: tr('not a git repository(尚未 git init,可用「新建仓库并推送」初始化为 git 仓库并上传)'),
|
|
737
751
|
}
|
|
738
752
|
}
|
|
739
753
|
|
|
@@ -910,10 +924,10 @@ function pushFlow(dir) {
|
|
|
910
924
|
const plan = ignorePlan(dir)
|
|
911
925
|
const skipped = plan.entries.map((e) => {
|
|
912
926
|
const reason = e.ignored
|
|
913
|
-
?
|
|
927
|
+
? tr("超过 GitHub 100MB 单文件限制(${}),已被 .gitignore 的 \"${}\" 排除", [fmtMB(e.bytes), e.path])
|
|
914
928
|
: e.kind === 'dir'
|
|
915
|
-
?
|
|
916
|
-
:
|
|
929
|
+
? tr("超过 GitHub 100MB 单文件限制(${}),${} 所在的一级目录「${}」整体忽略(该文件夹为一整体)", [fmtMB(e.bytes), e.source, e.path])
|
|
930
|
+
: tr("超过 GitHub 100MB 单文件限制(${}),已忽略单个文件「${}」", [fmtMB(e.bytes), e.path])
|
|
917
931
|
return { path: e.kind === 'dir' ? e.path + '/' : e.path, reason }
|
|
918
932
|
})
|
|
919
933
|
|
|
@@ -965,7 +979,7 @@ function pushFlow(dir) {
|
|
|
965
979
|
return {
|
|
966
980
|
ok: false, needsRemote: true, branch: branchName,
|
|
967
981
|
committed, commitHash, skipped,
|
|
968
|
-
error: '尚未配置远程仓库 origin,请使用「新建仓库」创建远程仓库。',
|
|
982
|
+
error: tr('尚未配置远程仓库 origin,请使用「新建仓库」创建远程仓库。'),
|
|
969
983
|
}
|
|
970
984
|
}
|
|
971
985
|
|
|
@@ -994,11 +1008,11 @@ function pushFlow(dir) {
|
|
|
994
1008
|
function pullFlow(dir) {
|
|
995
1009
|
if (!dir || !existsSync(dir)) return { ok: false, error: 'folder does not exist' }
|
|
996
1010
|
if (!existsSync(join(dir, '.git'))) {
|
|
997
|
-
return { ok: false, error: 'not a git repository(尚未 git init,无法拉取)' }
|
|
1011
|
+
return { ok: false, error: tr('not a git repository(尚未 git init,无法拉取)') }
|
|
998
1012
|
}
|
|
999
1013
|
const hasRemote = run(GIT, ['-C', dir, 'remote', 'get-url', 'origin']).ok
|
|
1000
1014
|
if (!hasRemote) {
|
|
1001
|
-
return { ok: false, error: '尚未配置远程仓库 origin,无法拉取。' }
|
|
1015
|
+
return { ok: false, error: tr('尚未配置远程仓库 origin,无法拉取。') }
|
|
1002
1016
|
}
|
|
1003
1017
|
|
|
1004
1018
|
// Was already up to date? First cheap check via ls-remote comparison.
|
|
@@ -1028,8 +1042,8 @@ function pullFlow(dir) {
|
|
|
1028
1042
|
conflict,
|
|
1029
1043
|
branch: branchName,
|
|
1030
1044
|
error: conflict
|
|
1031
|
-
? '拉取存在冲突:本地有未合并改动或与远程冲突,请手动 git pull 处理合并。'
|
|
1032
|
-
: (pull.stderr || pull.stdout).trim() || 'git pull 失败',
|
|
1045
|
+
? tr('拉取存在冲突:本地有未合并改动或与远程冲突,请手动 git pull 处理合并。')
|
|
1046
|
+
: (pull.stderr || pull.stdout).trim() || tr('git pull 失败'),
|
|
1033
1047
|
detail: blob,
|
|
1034
1048
|
}
|
|
1035
1049
|
}
|
|
@@ -1043,7 +1057,7 @@ function pullFlow(dir) {
|
|
|
1043
1057
|
function mergePushFlow(dir) {
|
|
1044
1058
|
if (!dir || !existsSync(dir)) return { ok: false, error: 'folder does not exist' }
|
|
1045
1059
|
if (!existsSync(join(dir, '.git'))) {
|
|
1046
|
-
return { ok: false, error: 'not a git repository(尚未 git init)' }
|
|
1060
|
+
return { ok: false, error: tr('not a git repository(尚未 git init)') }
|
|
1047
1061
|
}
|
|
1048
1062
|
const branch = run(GIT, ['-C', dir, 'rev-parse', '--abbrev-ref', 'HEAD'])
|
|
1049
1063
|
const branchName = branch.ok ? branch.stdout.trim() : 'main'
|
|
@@ -1057,8 +1071,8 @@ function mergePushFlow(dir) {
|
|
|
1057
1071
|
return {
|
|
1058
1072
|
ok: false, conflict, branch: branchName,
|
|
1059
1073
|
error: conflict
|
|
1060
|
-
? '拉取并推送失败:合并存在冲突,请手动解决后重试。'
|
|
1061
|
-
: (rebase.stderr || rebase.stdout).trim() || 'pull --rebase 失败',
|
|
1074
|
+
? tr('拉取并推送失败:合并存在冲突,请手动解决后重试。')
|
|
1075
|
+
: (rebase.stderr || rebase.stdout).trim() || tr('pull --rebase 失败'),
|
|
1062
1076
|
detail: blob,
|
|
1063
1077
|
}
|
|
1064
1078
|
}
|
|
@@ -1069,7 +1083,7 @@ function mergePushFlow(dir) {
|
|
|
1069
1083
|
if (!push.ok) {
|
|
1070
1084
|
return {
|
|
1071
1085
|
ok: false, branch: branchName, detail: (push.stdout + '\n' + push.stderr).trim(),
|
|
1072
|
-
error: (push.stderr || push.stdout).trim() || 'push 失败',
|
|
1086
|
+
error: (push.stderr || push.stdout).trim() || tr('push 失败'),
|
|
1073
1087
|
}
|
|
1074
1088
|
}
|
|
1075
1089
|
return { ok: true, branch: branchName, rebased: true, pushed: true }
|
|
@@ -1084,7 +1098,7 @@ function mergePushFlow(dir) {
|
|
|
1084
1098
|
function forcePushFlow(dir) {
|
|
1085
1099
|
if (!dir || !existsSync(dir)) return { ok: false, error: 'folder does not exist' }
|
|
1086
1100
|
if (!existsSync(join(dir, '.git'))) {
|
|
1087
|
-
return { ok: false, error: 'not a git repository(尚未 git init)' }
|
|
1101
|
+
return { ok: false, error: tr('not a git repository(尚未 git init)') }
|
|
1088
1102
|
}
|
|
1089
1103
|
const branch = run(GIT, ['-C', dir, 'rev-parse', '--abbrev-ref', 'HEAD'])
|
|
1090
1104
|
const branchName = branch.ok ? branch.stdout.trim() : 'main'
|
|
@@ -1094,7 +1108,7 @@ function forcePushFlow(dir) {
|
|
|
1094
1108
|
const blob = (push.stdout + '\n' + push.stderr).trim()
|
|
1095
1109
|
return {
|
|
1096
1110
|
ok: push.ok, branch: branchName, forced: push.ok,
|
|
1097
|
-
error: push.ok ? undefined : (push.stderr || push.stdout).trim() || 'push --force 失败',
|
|
1111
|
+
error: push.ok ? undefined : (push.stderr || push.stdout).trim() || tr('push --force 失败'),
|
|
1098
1112
|
detail: blob,
|
|
1099
1113
|
}
|
|
1100
1114
|
}
|
|
@@ -1109,7 +1123,7 @@ function forcePushFlow(dir) {
|
|
|
1109
1123
|
function forcePullFlow(dir) {
|
|
1110
1124
|
if (!dir || !existsSync(dir)) return { ok: false, error: 'folder does not exist' }
|
|
1111
1125
|
if (!existsSync(join(dir, '.git'))) {
|
|
1112
|
-
return { ok: false, error: 'not a git repository(尚未 git init)' }
|
|
1126
|
+
return { ok: false, error: tr('not a git repository(尚未 git init)') }
|
|
1113
1127
|
}
|
|
1114
1128
|
const branch = run(GIT, ['-C', dir, 'rev-parse', '--abbrev-ref', 'HEAD'])
|
|
1115
1129
|
const branchName = branch.ok ? branch.stdout.trim() : 'main'
|
|
@@ -1122,8 +1136,8 @@ function forcePullFlow(dir) {
|
|
|
1122
1136
|
ok: pull.ok, conflict, branch: branchName,
|
|
1123
1137
|
error: !pull.ok
|
|
1124
1138
|
? (conflict
|
|
1125
|
-
? '强制拉取存在冲突,请手动处理。'
|
|
1126
|
-
: (pull.stderr || pull.stdout).trim() || 'git pull --force 失败')
|
|
1139
|
+
? tr('强制拉取存在冲突,请手动处理。')
|
|
1140
|
+
: (pull.stderr || pull.stdout).trim() || tr('git pull --force 失败'))
|
|
1127
1141
|
: undefined,
|
|
1128
1142
|
detail: blob,
|
|
1129
1143
|
}
|
|
@@ -1144,9 +1158,9 @@ function forcePullFlow(dir) {
|
|
|
1144
1158
|
function createRepoFlow(dir, name, visibility, provider = 'github') {
|
|
1145
1159
|
if (!dir || !existsSync(dir)) return { ok: false, error: 'folder does not exist' }
|
|
1146
1160
|
const repoName = String(name || '').trim()
|
|
1147
|
-
if (!repoName) return { ok: false, error: '缺少仓库名称' }
|
|
1161
|
+
if (!repoName) return { ok: false, error: tr('缺少仓库名称') }
|
|
1148
1162
|
if (!/^[A-Za-z0-9._-]+$/.test(repoName) || repoName === '.') {
|
|
1149
|
-
return { ok: false, error: '仓库名称包含非法字符(仅允许字母、数字、点、下划线、横线)' }
|
|
1163
|
+
return { ok: false, error: tr('仓库名称包含非法字符(仅允许字母、数字、点、下划线、横线)') }
|
|
1150
1164
|
}
|
|
1151
1165
|
if (!existsSync(join(dir, '.git'))) {
|
|
1152
1166
|
const init = run(GIT, ['-C', dir, 'init'])
|
|
@@ -1167,7 +1181,7 @@ function createRepoFlow(dir, name, visibility, provider = 'github') {
|
|
|
1167
1181
|
if (!head.ok) {
|
|
1168
1182
|
const stagedCount = run(GIT, ['-C', dir, 'add', '-A'])
|
|
1169
1183
|
if (!stagedCount.ok) {
|
|
1170
|
-
return { ok: false, error:
|
|
1184
|
+
return { ok: false, error: tr("git add 失败:${}", [(stagedCount.stderr || '')]) }
|
|
1171
1185
|
}
|
|
1172
1186
|
const commit = run(GIT, ['-C', dir, 'commit', '-m', 'init: initial commit from DSH source-code-mgmt'])
|
|
1173
1187
|
if (!commit.ok) {
|
|
@@ -1182,10 +1196,10 @@ function createRepoFlow(dir, name, visibility, provider = 'github') {
|
|
|
1182
1196
|
// 1) Create the empty remote repo via the Gitee OpenAPI.
|
|
1183
1197
|
const owner = giteeOwner()
|
|
1184
1198
|
const token = readGiteeToken()
|
|
1185
|
-
if (!owner || !token) return { ok: false, error: '需要先配置 Gitee 私人令牌' }
|
|
1199
|
+
if (!owner || !token) return { ok: false, error: tr('需要先配置 Gitee 私人令牌') }
|
|
1186
1200
|
const api = giteeApi('POST', 'user/repos', { name: repoName, private: vis === 'private' }, token)
|
|
1187
1201
|
if (!api.ok || giteeApiFailed(api.data)) {
|
|
1188
|
-
return { ok: false, error: (api.error || (api.data && api.data.message) || 'Gitee 创建仓库失败'), detail: api.data }
|
|
1202
|
+
return { ok: false, error: (api.error || (api.data && api.data.message) || tr('Gitee 创建仓库失败')), detail: api.data }
|
|
1189
1203
|
}
|
|
1190
1204
|
// 2) Point origin at the SSH remote and push (SSH per user's choice).
|
|
1191
1205
|
const sshUrl = 'git@gitee.com:' + owner + '/' + repoName + '.git'
|
|
@@ -1244,7 +1258,7 @@ function repoDiffFlow(dir, path) {
|
|
|
1244
1258
|
return { ok: false, error: 'not a git repository' }
|
|
1245
1259
|
}
|
|
1246
1260
|
const p = String(path || '').trim()
|
|
1247
|
-
if (!p) return { ok: false, error: '缺少文件路径' }
|
|
1261
|
+
if (!p) return { ok: false, error: tr('缺少文件路径') }
|
|
1248
1262
|
// Untracked files have no tracked diff.
|
|
1249
1263
|
const status = run(GIT, ['-C', dir, 'status', '--porcelain', '--', p])
|
|
1250
1264
|
const isUntracked = status.ok && /^\?\?/.test(status.stdout.trim().slice(0, 2))
|
|
@@ -1266,23 +1280,23 @@ function repoDiffFlow(dir, path) {
|
|
|
1266
1280
|
function alignFlow(dir) {
|
|
1267
1281
|
if (!dir || !existsSync(dir)) return { ok: false, error: 'folder does not exist' }
|
|
1268
1282
|
if (!existsSync(join(dir, '.git'))) {
|
|
1269
|
-
return { ok: false, error: 'not a git repository(尚未 git init)' }
|
|
1283
|
+
return { ok: false, error: tr('not a git repository(尚未 git init)') }
|
|
1270
1284
|
}
|
|
1271
1285
|
const branch = run(GIT, ['-C', dir, 'rev-parse', '--abbrev-ref', 'HEAD'])
|
|
1272
1286
|
const branchName = branch.ok ? branch.stdout.trim() : undefined
|
|
1273
|
-
if (!branchName) return { ok: false, error: '无法识别当前分支' }
|
|
1287
|
+
if (!branchName) return { ok: false, error: tr('无法识别当前分支') }
|
|
1274
1288
|
const hasRemote = run(GIT, ['-C', dir, 'remote', 'get-url', 'origin']).ok
|
|
1275
|
-
if (!hasRemote) return { ok: false, error: '尚未配置远程仓库 origin,无法对齐。' }
|
|
1289
|
+
if (!hasRemote) return { ok: false, error: tr('尚未配置远程仓库 origin,无法对齐。') }
|
|
1276
1290
|
|
|
1277
1291
|
const fetch = run(GIT, ['-C', dir, 'fetch', 'origin'], { timeout: 120_000 })
|
|
1278
1292
|
if (!fetch.ok) {
|
|
1279
|
-
return { ok: false, error: (fetch.stderr || fetch.stdout).trim() || 'git fetch 失败', detail: (fetch.stdout + '\n' + fetch.stderr).trim() }
|
|
1293
|
+
return { ok: false, error: (fetch.stderr || fetch.stdout).trim() || tr('git fetch 失败'), detail: (fetch.stdout + '\n' + fetch.stderr).trim() }
|
|
1280
1294
|
}
|
|
1281
1295
|
const reset = run(GIT, ['-C', dir, 'reset', '--hard', 'origin/' + branchName], { timeout: 60_000 })
|
|
1282
1296
|
return {
|
|
1283
1297
|
ok: reset.ok,
|
|
1284
1298
|
branch: branchName,
|
|
1285
|
-
error: reset.ok ? undefined : (reset.stderr || reset.stdout).trim() || 'git reset --hard 失败',
|
|
1299
|
+
error: reset.ok ? undefined : (reset.stderr || reset.stdout).trim() || tr('git reset --hard 失败'),
|
|
1286
1300
|
detail: (fetch.stdout + '\n' + reset.stdout).trim(),
|
|
1287
1301
|
}
|
|
1288
1302
|
}
|
|
@@ -1299,7 +1313,7 @@ function initGitFlow(dir) {
|
|
|
1299
1313
|
return { ok: true, alreadyRepo: true, dir }
|
|
1300
1314
|
}
|
|
1301
1315
|
const init = run(GIT, ['-C', dir, 'init'])
|
|
1302
|
-
if (!init.ok) return { ok: false, error: (init.stderr || init.stdout).trim() || 'git init 失败' }
|
|
1316
|
+
if (!init.ok) return { ok: false, error: (init.stderr || init.stdout).trim() || tr('git init 失败') }
|
|
1303
1317
|
const ident = run(GIT, ['-C', dir, 'config', 'user.email'])
|
|
1304
1318
|
if (!ident.ok) {
|
|
1305
1319
|
run(GIT, ['-C', dir, 'config', 'user.name', 'DSH User'])
|
|
@@ -1316,7 +1330,7 @@ function initGitFlow(dir) {
|
|
|
1316
1330
|
/** Basic guard: the folder must exist and be a git repo; returns an error object or null. */
|
|
1317
1331
|
function requireGitRepo(dir) {
|
|
1318
1332
|
if (!dir || !existsSync(dir)) return { error: 'folder does not exist' }
|
|
1319
|
-
if (!existsSync(join(dir, '.git'))) return { error: 'not a git repository(尚未 git init)' }
|
|
1333
|
+
if (!existsSync(join(dir, '.git'))) return { error: tr('not a git repository(尚未 git init)') }
|
|
1320
1334
|
return null
|
|
1321
1335
|
}
|
|
1322
1336
|
|
|
@@ -1331,7 +1345,7 @@ function stageFlow(dir, path) {
|
|
|
1331
1345
|
const args = ['-C', dir, 'add', '-A']
|
|
1332
1346
|
if (path && String(path).trim() !== '') args.push('--', String(path).trim())
|
|
1333
1347
|
const r = run(GIT, args)
|
|
1334
|
-
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || 'git add 失败' }
|
|
1348
|
+
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || tr('git add 失败') }
|
|
1335
1349
|
return { ok: true, path: path || null }
|
|
1336
1350
|
}
|
|
1337
1351
|
|
|
@@ -1346,7 +1360,7 @@ function unstageFlow(dir, path) {
|
|
|
1346
1360
|
const args = ['-C', dir, 'reset', '-q']
|
|
1347
1361
|
if (path && String(path).trim() !== '') args.push('--', String(path).trim())
|
|
1348
1362
|
const r = run(GIT, args)
|
|
1349
|
-
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || 'git reset 失败' }
|
|
1363
|
+
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || tr('git reset 失败') }
|
|
1350
1364
|
return { ok: true, path: path || null }
|
|
1351
1365
|
}
|
|
1352
1366
|
|
|
@@ -1364,7 +1378,7 @@ function commitFlow(dir, message, paths) {
|
|
|
1364
1378
|
const guard = requireGitRepo(dir)
|
|
1365
1379
|
if (guard) return { ok: false, error: guard.error }
|
|
1366
1380
|
const msg = String(message || '').trim()
|
|
1367
|
-
if (msg === '') return { ok: false, error: '提交信息不能为空' }
|
|
1381
|
+
if (msg === '') return { ok: false, error: tr('提交信息不能为空') }
|
|
1368
1382
|
|
|
1369
1383
|
const ident = run(GIT, ['-C', dir, 'config', 'user.email'])
|
|
1370
1384
|
if (!ident.ok) {
|
|
@@ -1377,7 +1391,7 @@ function commitFlow(dir, message, paths) {
|
|
|
1377
1391
|
if (list.length > 0) {
|
|
1378
1392
|
// 只提交指定文件:先把这些文件 add 进暂存区。
|
|
1379
1393
|
const add = run(GIT, ['-C', dir, 'add', '--', ...list])
|
|
1380
|
-
if (!add.ok) return { ok: false, error: (add.stderr || add.stdout).trim() || 'git add 失败' }
|
|
1394
|
+
if (!add.ok) return { ok: false, error: (add.stderr || add.stdout).trim() || tr('git add 失败') }
|
|
1381
1395
|
stagedPaths = list
|
|
1382
1396
|
} else {
|
|
1383
1397
|
// 提交整个暂存区:列出已暂存文件用于提示。
|
|
@@ -1387,10 +1401,10 @@ function commitFlow(dir, message, paths) {
|
|
|
1387
1401
|
|
|
1388
1402
|
// 无可提交内容时避免报错。
|
|
1389
1403
|
const hasChanges = run(GIT, ['-C', dir, 'diff', '--cached', '--quiet']).code !== 0
|
|
1390
|
-
if (!hasChanges) return { ok: false, error: '没有已暂存(staged)的改动可提交', staged: 0 }
|
|
1404
|
+
if (!hasChanges) return { ok: false, error: tr('没有已暂存(staged)的改动可提交'), staged: 0 }
|
|
1391
1405
|
|
|
1392
1406
|
const commit = run(GIT, ['-C', dir, 'commit', '-m', msg])
|
|
1393
|
-
if (!commit.ok) return { ok: false, error: (commit.stderr || commit.stdout).trim() || 'git commit 失败', staged: stagedPaths.length }
|
|
1407
|
+
if (!commit.ok) return { ok: false, error: (commit.stderr || commit.stdout).trim() || tr('git commit 失败'), staged: stagedPaths.length }
|
|
1394
1408
|
|
|
1395
1409
|
const rev = run(GIT, ['-C', dir, 'rev-parse', '--short', 'HEAD'])
|
|
1396
1410
|
return {
|
|
@@ -1402,6 +1416,62 @@ function commitFlow(dir, message, paths) {
|
|
|
1402
1416
|
}
|
|
1403
1417
|
}
|
|
1404
1418
|
|
|
1419
|
+
/**
|
|
1420
|
+
* Push STAGED changes only: commit whatever is currently staged (using the
|
|
1421
|
+
* given message, or an autogenerated one when empty), then push to origin.
|
|
1422
|
+
* Unlike {@link pushFlow} it does NOT go on to stage untracked/unstaged files
|
|
1423
|
+
* itself and does NOT overwrite the user's own commit message. When nothing is
|
|
1424
|
+
* newly staged yet (e.g. the local branch already has commits ahead), it simply
|
|
1425
|
+
* pushes those existing commits.
|
|
1426
|
+
* @param {string} dir - local folder.
|
|
1427
|
+
* @param {string} [message] - commit message used only when something is staged
|
|
1428
|
+
* and the caller supplies one; empty falls back to an autogenerated message.
|
|
1429
|
+
*/
|
|
1430
|
+
function pushStagedFlow(dir, message) {
|
|
1431
|
+
const guard = requireGitRepo(dir)
|
|
1432
|
+
if (guard) return { ok: false, error: guard.error }
|
|
1433
|
+
|
|
1434
|
+
// Remote must exist before we can push.
|
|
1435
|
+
const hasRemote = run(GIT, ['-C', dir, 'remote', 'get-url', 'origin']).ok
|
|
1436
|
+
if (!hasRemote) {
|
|
1437
|
+
return { ok: false, needsRemote: true, error: tr('尚未配置远程仓库 origin,请使用「新建仓库」创建远程仓库。') }
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
const msg = String(message || '').trim()
|
|
1441
|
+
let committed = false
|
|
1442
|
+
let commitHash
|
|
1443
|
+
// Commit staged content only if there is anything staged right now.
|
|
1444
|
+
const hasStaged = run(GIT, ['-C', dir, 'diff', '--cached', '--quiet']).code !== 0
|
|
1445
|
+
if (hasStaged) {
|
|
1446
|
+
const ident = run(GIT, ['-C', dir, 'config', 'user.email'])
|
|
1447
|
+
if (!ident.ok) {
|
|
1448
|
+
run(GIT, ['-C', dir, 'config', 'user.name', 'DSH User'])
|
|
1449
|
+
run(GIT, ['-C', dir, 'config', 'user.email', 'dsh@localhost'])
|
|
1450
|
+
}
|
|
1451
|
+
const useMsg = msg || ('chore: update ' + baseName(dir))
|
|
1452
|
+
const commit = run(GIT, ['-C', dir, 'commit', '-m', useMsg])
|
|
1453
|
+
if (!commit.ok) return { ok: false, error: (commit.stderr || commit.stdout).trim() || tr('git commit 失败') }
|
|
1454
|
+
committed = true
|
|
1455
|
+
const rev = run(GIT, ['-C', dir, 'rev-parse', '--short', 'HEAD'])
|
|
1456
|
+
commitHash = rev.ok ? rev.stdout.trim() : undefined
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const curBranch = run(GIT, ['-C', dir, 'rev-parse', '--abbrev-ref', 'HEAD'])
|
|
1460
|
+
const branchName = curBranch.ok ? curBranch.stdout.trim() : 'main'
|
|
1461
|
+
const push = run(GIT, ['-C', dir, 'push', '-u', 'origin', branchName], { timeout: 120_000 })
|
|
1462
|
+
const pushed = push.ok
|
|
1463
|
+
return {
|
|
1464
|
+
ok: pushed,
|
|
1465
|
+
needsRemote: false,
|
|
1466
|
+
committed,
|
|
1467
|
+
commitHash,
|
|
1468
|
+
message: msg || (committed ? ('chore: update ' + baseName(dir)) : undefined),
|
|
1469
|
+
branch: branchName,
|
|
1470
|
+
pushed,
|
|
1471
|
+
pushError: pushed ? undefined : (push.stderr || push.stdout).trim(),
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1405
1475
|
/**
|
|
1406
1476
|
* List branches (current first) using `git for-each-ref`.
|
|
1407
1477
|
* @param {string} dir - local folder.
|
|
@@ -1426,9 +1496,9 @@ function checkoutFlow(dir, branch) {
|
|
|
1426
1496
|
const guard = requireGitRepo(dir)
|
|
1427
1497
|
if (guard) return { ok: false, error: guard.error }
|
|
1428
1498
|
const b = String(branch || '').trim()
|
|
1429
|
-
if (b === '') return { ok: false, error: '缺少分支名' }
|
|
1499
|
+
if (b === '') return { ok: false, error: tr('缺少分支名') }
|
|
1430
1500
|
const r = run(GIT, ['-C', dir, 'checkout', b])
|
|
1431
|
-
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || '切换分支失败' }
|
|
1501
|
+
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || tr('切换分支失败') }
|
|
1432
1502
|
return { ok: true, branch: b }
|
|
1433
1503
|
}
|
|
1434
1504
|
|
|
@@ -1449,7 +1519,7 @@ function logFlow(dir, count) {
|
|
|
1449
1519
|
if (/does not have any commits|bad revision|your current branch/i.test((r.stderr || r.stdout))) {
|
|
1450
1520
|
return { ok: true, commits: [] }
|
|
1451
1521
|
}
|
|
1452
|
-
return { ok: false, error: (r.stderr || r.stdout).trim() || 'git log 失败' }
|
|
1522
|
+
return { ok: false, error: (r.stderr || r.stdout).trim() || tr('git log 失败') }
|
|
1453
1523
|
}
|
|
1454
1524
|
const commits = r.stdout.split(/\r?\n/).filter((l) => l.trim() !== '').map((line) => {
|
|
1455
1525
|
const [short, subject, author, date, full, refs] = line.split('\x1f')
|
|
@@ -1474,9 +1544,9 @@ function revertFlow(dir, hash) {
|
|
|
1474
1544
|
const guard = requireGitRepo(dir)
|
|
1475
1545
|
if (guard) return { ok: false, error: guard.error }
|
|
1476
1546
|
const h = String(hash || '').trim()
|
|
1477
|
-
if (h === '') return { ok: false, error: '缺少 commit hash' }
|
|
1547
|
+
if (h === '') return { ok: false, error: tr('缺少 commit hash') }
|
|
1478
1548
|
const r = run(GIT, ['-C', dir, 'revert', '--no-edit', h], { timeout: 60_000 })
|
|
1479
|
-
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || 'revert 失败(可能有冲突,请手动处理)' }
|
|
1549
|
+
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || tr('revert 失败(可能有冲突,请手动处理)') }
|
|
1480
1550
|
return { ok: true, hash: h }
|
|
1481
1551
|
}
|
|
1482
1552
|
|
|
@@ -1489,9 +1559,9 @@ function cherryPickFlow(dir, hash) {
|
|
|
1489
1559
|
const guard = requireGitRepo(dir)
|
|
1490
1560
|
if (guard) return { ok: false, error: guard.error }
|
|
1491
1561
|
const h = String(hash || '').trim()
|
|
1492
|
-
if (h === '') return { ok: false, error: '缺少 commit hash' }
|
|
1562
|
+
if (h === '') return { ok: false, error: tr('缺少 commit hash') }
|
|
1493
1563
|
const r = run(GIT, ['-C', dir, 'cherry-pick', h], { timeout: 60_000 })
|
|
1494
|
-
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || 'cherry-pick 失败(可能有冲突,请手动处理)' }
|
|
1564
|
+
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || tr('cherry-pick 失败(可能有冲突,请手动处理)') }
|
|
1495
1565
|
return { ok: true, hash: h }
|
|
1496
1566
|
}
|
|
1497
1567
|
|
|
@@ -1505,9 +1575,9 @@ function commitDiffFlow(dir, hash) {
|
|
|
1505
1575
|
const guard = requireGitRepo(dir)
|
|
1506
1576
|
if (guard) return { ok: false, error: guard.error }
|
|
1507
1577
|
const h = String(hash || '').trim()
|
|
1508
|
-
if (h === '') return { ok: false, error: '缺少 commit hash' }
|
|
1578
|
+
if (h === '') return { ok: false, error: tr('缺少 commit hash') }
|
|
1509
1579
|
const r = run(GIT, ['-C', dir, 'show', '--no-ext-diff', '--no-color', '--format=', '-m', '--first-parent', h])
|
|
1510
|
-
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || '读取提交 diff 失败' }
|
|
1580
|
+
if (!r.ok) return { ok: false, error: (r.stderr || r.stdout).trim() || tr('读取提交 diff 失败') }
|
|
1511
1581
|
return { ok: true, hash: h, diff: r.stdout }
|
|
1512
1582
|
}
|
|
1513
1583
|
|
|
@@ -1577,7 +1647,7 @@ function installCommand(tool) {
|
|
|
1577
1647
|
*/
|
|
1578
1648
|
function installToolFlow(tool) {
|
|
1579
1649
|
const t = String(tool || '').trim().toLowerCase()
|
|
1580
|
-
if (t !== 'git' && t !== 'gh' && t !== 'ssh') return { ok: false, error: '未知工具:' + tool }
|
|
1650
|
+
if (t !== 'git' && t !== 'gh' && t !== 'ssh') return { ok: false, error: tr('未知工具:') + tool }
|
|
1581
1651
|
// 已安装就直接返回,不重复安装。
|
|
1582
1652
|
const present = t === 'git' ? (GIT && GIT !== 'git')
|
|
1583
1653
|
: t === 'gh' ? (GH && GH !== 'gh')
|
|
@@ -1586,7 +1656,7 @@ function installToolFlow(tool) {
|
|
|
1586
1656
|
|
|
1587
1657
|
const cmd = installCommand(t)
|
|
1588
1658
|
if (!cmd) {
|
|
1589
|
-
return { ok: false, error: '未找到可用的包管理器(' + (IS_WIN ? 'winget / choco / scoop' : process.platform === 'darwin' ? 'brew' : 'apt-get / dnf / pacman') + ')。请手动安装。', tool: t }
|
|
1659
|
+
return { ok: false, error: tr('未找到可用的包管理器(') + (IS_WIN ? 'winget / choco / scoop' : process.platform === 'darwin' ? 'brew' : 'apt-get / dnf / pacman') + tr(')。请手动安装。'), tool: t }
|
|
1590
1660
|
}
|
|
1591
1661
|
const r = run(cmd.bin, cmd.args, { timeout: 300_000 })
|
|
1592
1662
|
const output = (r.stdout + '\n' + r.stderr).trim()
|
|
@@ -1596,7 +1666,7 @@ function installToolFlow(tool) {
|
|
|
1596
1666
|
ok,
|
|
1597
1667
|
tool: t,
|
|
1598
1668
|
command: cmd.label,
|
|
1599
|
-
output: output || (ok ? '安装成功' : '安装失败'),
|
|
1669
|
+
output: output || (ok ? tr('安装成功') : tr('安装失败')),
|
|
1600
1670
|
needElevation: IS_WIN && t === 'ssh',
|
|
1601
1671
|
}
|
|
1602
1672
|
}
|
|
@@ -1755,7 +1825,7 @@ function pickDirDialog(initialDir) {
|
|
|
1755
1825
|
const script =
|
|
1756
1826
|
'Add-Type -AssemblyName System.Windows.Forms;' +
|
|
1757
1827
|
'$dlg = New-Object System.Windows.Forms.FolderBrowserDialog;' +
|
|
1758
|
-
'$dlg.Description = \'选择本地目录\';' +
|
|
1828
|
+
tr('$dlg.Description = \'选择本地目录\';') +
|
|
1759
1829
|
(initial ? `$dlg.SelectedPath = '${initial}';` : '') +
|
|
1760
1830
|
'if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { Write-Output $dlg.SelectedPath }'
|
|
1761
1831
|
const r = run('powershell', ['-NoProfile', '-STA', '-Command', script], { timeout: 180_000 })
|
|
@@ -1882,15 +1952,15 @@ function setVisibilityFlow(name, target, provider = 'github') {
|
|
|
1882
1952
|
const vis = target === 'public' ? 'public' : 'private'
|
|
1883
1953
|
if (provider === 'gitee') {
|
|
1884
1954
|
const owner = giteeOwner()
|
|
1885
|
-
if (!owner || !name) return { ok: false, error: '需要先配置 Gitee 私人令牌' }
|
|
1955
|
+
if (!owner || !name) return { ok: false, error: tr('需要先配置 Gitee 私人令牌') }
|
|
1886
1956
|
const api = giteeApi('PATCH', 'repos/' + owner + '/' + name, { private: vis === 'private' })
|
|
1887
1957
|
if (!api.ok || giteeApiFailed(api.data)) {
|
|
1888
|
-
return { ok: false, error: (api.error || (api.data && api.data.message) || 'Gitee 修改可见性失败') }
|
|
1958
|
+
return { ok: false, error: (api.error || (api.data && api.data.message) || tr('Gitee 修改可见性失败')) }
|
|
1889
1959
|
}
|
|
1890
1960
|
return { ok: true, name, visibility: vis, provider: 'gitee' }
|
|
1891
1961
|
}
|
|
1892
1962
|
const owner = ghOwner()
|
|
1893
|
-
if (!owner || !name) return { ok: false, error: '需要先登录 GitHub CLI(gh)' }
|
|
1963
|
+
if (!owner || !name) return { ok: false, error: tr('需要先登录 GitHub CLI(gh)') }
|
|
1894
1964
|
const r = run(GH, [
|
|
1895
1965
|
'repo', 'edit', owner + '/' + name,
|
|
1896
1966
|
'--visibility', vis,
|
|
@@ -1899,7 +1969,7 @@ function setVisibilityFlow(name, target, provider = 'github') {
|
|
|
1899
1969
|
if (!r.ok) {
|
|
1900
1970
|
return {
|
|
1901
1971
|
ok: false,
|
|
1902
|
-
error:
|
|
1972
|
+
error: tr("修改可见性失败:${}", [(r.stderr || r.stdout || '').trim() || tr('gh repo edit 失败')]),
|
|
1903
1973
|
}
|
|
1904
1974
|
}
|
|
1905
1975
|
return { ok: true, name, visibility: vis, provider: 'github' }
|
|
@@ -1914,11 +1984,13 @@ function baseName(p) {
|
|
|
1914
1984
|
|
|
1915
1985
|
export function apply(ctx) {
|
|
1916
1986
|
const fallbackDir = resolveDefaultDir(process.cwd())
|
|
1917
|
-
|
|
1987
|
+
// 每个请求按 ?lang= 参数跑在独立语言上下文里(AsyncLocalStorage),
|
|
1988
|
+
// 所有 flow 函数内的 tr() 都能读到当前请求的语言,并发请求互不干扰。
|
|
1989
|
+
const handle = async (req, res, fn) => requestLangStore.run(langOf(req), async () => {
|
|
1918
1990
|
if (!isLoopbackRequest(req)) return forbidden(res)
|
|
1919
1991
|
const payload = await fn(req)
|
|
1920
1992
|
json(res, 200, payload)
|
|
1921
|
-
}
|
|
1993
|
+
})
|
|
1922
1994
|
|
|
1923
1995
|
const routes = {
|
|
1924
1996
|
'/env': (req, res) => handle(req, res, async () => ({ ok: true, ...checkEnv() })),
|
|
@@ -1966,7 +2038,7 @@ export function apply(ctx) {
|
|
|
1966
2038
|
await handle(req, res, async (req) => {
|
|
1967
2039
|
const body = JSON.parse((await readBody(req)) || '{}')
|
|
1968
2040
|
const dir = String(body.dir || '').trim()
|
|
1969
|
-
if (!dir) return { ok: false, error: '缺少目录路径' }
|
|
2041
|
+
if (!dir) return { ok: false, error: tr('缺少目录路径') }
|
|
1970
2042
|
removeCustomDir(dir)
|
|
1971
2043
|
return { ok: true, removed: dir, customDirs: readCustomDirs(), workspaces: listWorkspaces() }
|
|
1972
2044
|
})
|
|
@@ -1990,7 +2062,7 @@ export function apply(ctx) {
|
|
|
1990
2062
|
await handle(req, res, async (req) => {
|
|
1991
2063
|
const body = JSON.parse((await readBody(req)) || '{}')
|
|
1992
2064
|
const picked = pickDirDialog(body.initial || fallbackDir)
|
|
1993
|
-
if (!picked) return { ok: false, cancelled: true, error: '未选择目录' }
|
|
2065
|
+
if (!picked) return { ok: false, cancelled: true, error: tr('未选择目录') }
|
|
1994
2066
|
return { ok: true, dir: picked, name: baseName(picked) }
|
|
1995
2067
|
})
|
|
1996
2068
|
},
|
|
@@ -1999,9 +2071,9 @@ export function apply(ctx) {
|
|
|
1999
2071
|
await handle(req, res, async (req) => {
|
|
2000
2072
|
const body = JSON.parse((await readBody(req)) || '{}')
|
|
2001
2073
|
const dir = String(body.dir || '').trim()
|
|
2002
|
-
if (!dir) return { ok: false, error: '缺少目录路径' }
|
|
2074
|
+
if (!dir) return { ok: false, error: tr('缺少目录路径') }
|
|
2003
2075
|
if (!existsSync(dir) || !statSync(dir).isDirectory()) {
|
|
2004
|
-
return { ok: false, error: '目录不存在或不是文件夹:' + dir }
|
|
2076
|
+
return { ok: false, error: tr('目录不存在或不是文件夹:') + dir }
|
|
2005
2077
|
}
|
|
2006
2078
|
saveCustomDir(dir)
|
|
2007
2079
|
return { ok: true, dir, name: baseName(dir), workspaces: listWorkspaces(), customDirs: readCustomDirs() }
|
|
@@ -2099,6 +2171,13 @@ export function apply(ctx) {
|
|
|
2099
2171
|
return pushFlow(body.dir || fallbackDir)
|
|
2100
2172
|
})
|
|
2101
2173
|
},
|
|
2174
|
+
'/push-staged': async (req, res) => {
|
|
2175
|
+
if (req.method !== 'POST') return methodNotAllowed(res, req.method)
|
|
2176
|
+
await handle(req, res, async (req) => {
|
|
2177
|
+
const body = JSON.parse((await readBody(req)) || '{}')
|
|
2178
|
+
return pushStagedFlow(body.dir || fallbackDir, body.message)
|
|
2179
|
+
})
|
|
2180
|
+
},
|
|
2102
2181
|
'/pull': async (req, res) => {
|
|
2103
2182
|
if (req.method !== 'POST') return methodNotAllowed(res, req.method)
|
|
2104
2183
|
await handle(req, res, async (req) => {
|
|
@@ -2158,13 +2237,13 @@ export function apply(ctx) {
|
|
|
2158
2237
|
return { ok: true, configured: false, cleared: true }
|
|
2159
2238
|
}
|
|
2160
2239
|
const token = String(body.token || '').trim()
|
|
2161
|
-
if (!token) return { ok: false, error: '令牌不能为空' }
|
|
2240
|
+
if (!token) return { ok: false, error: tr('令牌不能为空') }
|
|
2162
2241
|
saveGiteeToken(token)
|
|
2163
2242
|
const owner = giteeOwner()
|
|
2164
2243
|
if (!owner) {
|
|
2165
2244
|
// 令牌无效则清掉,避免留有坏令牌。
|
|
2166
2245
|
saveGiteeToken('')
|
|
2167
|
-
return { ok: false, error: 'Gitee 令牌无效,请检查(需有 projects 权限)' }
|
|
2246
|
+
return { ok: false, error: tr('Gitee 令牌无效,请检查(需有 projects 权限)') }
|
|
2168
2247
|
}
|
|
2169
2248
|
return { ok: true, configured: true, owner }
|
|
2170
2249
|
})
|
package/package.json
CHANGED
|
@@ -1,38 +1,41 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "source-code-mgmt",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "DSH plugin: source code management. Registers「代码管理」as a dsh-better-sidebar sidebar Tab when installed, otherwise a
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "lib/index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"default": "./lib/index.js"
|
|
10
|
-
},
|
|
11
|
-
"./client": {
|
|
12
|
-
"default": "./lib/client.js"
|
|
13
|
-
},
|
|
14
|
-
"./package.json": "./package.json"
|
|
15
|
-
},
|
|
16
|
-
"dsh": {
|
|
17
|
-
"bundle":
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "source-code-mgmt",
|
|
3
|
+
"version": "1.10.0",
|
|
4
|
+
"description": "DSH plugin: source code management. Registers「代码管理」as a dsh-better-sidebar sidebar Tab when installed, otherwise a button in the DSH session header's right-aligned utilities (beside \"Session log\") opening a right-side integrated panel that pushes the main content — env checks (git/gh), SSH ed25519 key setup, GitHub/Gitee dual-platform (SSH config 443 + Gitee OpenAPI), connectivity test, push / new repo, and auto-ignore files over 100MB with per-file skipped reasons. Cross-platform (Windows / Linux / macOS).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"default": "./lib/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./client": {
|
|
12
|
+
"default": "./lib/client.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"dsh": {
|
|
17
|
+
"bundle": {
|
|
18
|
+
"patch": "./cordis.patch.yml"
|
|
19
|
+
},
|
|
20
|
+
"client": {
|
|
21
|
+
"inject": [
|
|
22
|
+
"@deepseek-ai/dsh-client-runtime"
|
|
23
|
+
],
|
|
24
|
+
"platform": "web"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"react": "^18.2.0",
|
|
29
|
+
"dsh-better-sidebar": ">=0.4.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"dsh-better-sidebar": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"lib/**/*.js",
|
|
38
|
+
"cordis.patch.yml"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT"
|
|
41
|
+
}
|