@lancc/cmark 0.0.0-stage → 1.2.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 +36 -0
- package/LICENSE +21 -0
- package/README.md +217 -2
- package/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/assets/abnfDiagram-WEYOIA3C-BgXsTF7a.js +1 -0
- package/assets/architectureDiagram-ZHVYKT53-BsCWa_qC.js +36 -0
- package/assets/blockDiagram-RJAE7QA3-wbIxEiYN.js +140 -0
- package/assets/c4Diagram-XDPPPE54-5zdyxFvA.js +38 -0
- package/assets/chunk-2E7WHVMD-F85Lca-i.js +88 -0
- package/assets/chunk-2Q5K7J3B-CjpAZ0OI.js +1 -0
- package/assets/chunk-4EMNKR2T-C8Kbh83c.js +1 -0
- package/assets/chunk-55XCU76K-BINcFiLN.js +1 -0
- package/assets/chunk-5VM5RSS4-Diy5h2LS.js +15 -0
- package/assets/chunk-JWPE2WC7-BXnjDdh1.js +1 -0
- package/assets/chunk-VQ5W62QI-BM7ZZo1I.js +1 -0
- package/assets/chunk-XXDRQBXY-CW8CjB1w.js +1 -0
- package/assets/chunk-ZCHMQSOR-C7ATz1L1.js +2 -0
- package/assets/classDiagram-v2-4MYWR3UA-CMoqNk-b.js +217 -0
- package/assets/cose-bilkent-JH36ORCC-6rqR5iJk.js +1 -0
- package/assets/cynefin-EF2NZ3EQ-DypLIJxl.js +141 -0
- package/assets/cynefinDiagram-WF24UDRP-8b2CEBlR.js +62 -0
- package/assets/cytoscape.esm-CjX6Hyah.js +331 -0
- package/assets/dagre-NK4GIZVX-Dv6vuUbR.js +4 -0
- package/assets/diagram-CBK3MWTC-DJgMDeYd.js +24 -0
- package/assets/diagram-I5XIIV2D-BB9cMoRW.js +24 -0
- package/assets/diagram-ODIL2UCW-BVcRtsdy.js +30 -0
- package/assets/diagram-PFBEJUXE-BbGSOtJx.js +3 -0
- package/assets/diagram-RJA3I4VV-CzkY_8Fo.js +41 -0
- package/assets/diagram-TYUNFSNR-YTl5QdfL.js +200 -0
- package/assets/ebnfDiagram-V4P3TTPT-COVjPAjE.js +1 -0
- package/assets/echarts-BAPDkhe3.js +61 -0
- package/assets/elk-QONTBCTQ-DHsjY8Z_.js +27 -0
- package/assets/erDiagram-KYQIGQAA-CUAbTUl1.js +99 -0
- package/assets/flowDiagram-IUGLLWBU-CWsiqTQC.js +213 -0
- package/assets/ganttDiagram-R7GPNTJY-DetBW0Wm.js +292 -0
- package/assets/gitGraphDiagram-PDYL7XHY-ByrdgJmw.js +106 -0
- package/assets/graph-D8UmR1vK.js +1 -0
- package/assets/index-B17NCAw4.js +758 -0
- package/assets/index-eB_q_VA3.css +1 -0
- package/assets/infoDiagram-MVDRKAX5-BM48fjhU.js +2 -0
- package/assets/ishikawaDiagram-HPEHV2GE-DtHYywHZ.js +70 -0
- package/assets/journeyDiagram-BOSHW7O7-DJ1LiRh3.js +139 -0
- package/assets/kanban-definition-FNSA4HC5-DPPt8pwJ.js +89 -0
- package/assets/katex-CQL9J12G.js +257 -0
- package/assets/markdown-DVehI1Sg.js +314 -0
- package/assets/mermaid-C_q9pug0.js +312 -0
- package/assets/mindmap-definition-6OY57UG3-zbqYOEY6.js +98 -0
- package/assets/pegDiagram-ECIUJNBX-e7QG7TMq.js +1 -0
- package/assets/pieDiagram-GNMPY74T-Duf6YP0N.js +39 -0
- package/assets/quadrantDiagram-D57K4MC3-B2eYoUUy.js +7 -0
- package/assets/railroadDiagram-Y72NRBL7-BWCzPcI_.js +1 -0
- package/assets/requirementDiagram-EQYSW5RA-HhPMEqB1.js +84 -0
- package/assets/sankeyDiagram-SY24JHO5-CMUTwFBB.js +40 -0
- package/assets/sequenceDiagram-BLEHNHQU-C9wR2ll7.js +169 -0
- package/assets/sizeCapture-INFHLROL-DXfz37S6.js +1 -0
- package/assets/stateDiagram-v2-A6IESKWZ-D_gtonhE.js +291 -0
- package/assets/swimlanes-B3ENRKFQ-DdJOuuMV.js +1 -0
- package/assets/swimlanesDiagram-AYQRV4AK-BWt3HxHV.js +8 -0
- package/assets/timeline-definition-JXYKGLGB-DQsdwJe-.js +120 -0
- package/assets/usecaseDiagram-WPOLBNNX-DTRyFq7d.js +370 -0
- package/assets/vennDiagram-Q3Y3IDCW-dihYszm0.js +34 -0
- package/assets/wardleyDiagram-IVAIDKWA-BoPpgYfK.js +78 -0
- package/assets/xychartDiagram-4URNHDU4-DoLjYxeB.js +7 -0
- package/export-file/github-markdown.css +550 -0
- package/export-file/index.html +33 -0
- package/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/fonts/KaTeX_Main-Bold.woff +0 -0
- package/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/fonts/KaTeX_Main-Italic.woff +0 -0
- package/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/fonts/KaTeX_Main-Regular.woff +0 -0
- package/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/fonts/KaTeX_Math-Italic.woff +0 -0
- package/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/fonts/KaTeX_Script-Regular.woff +0 -0
- package/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/index.html +15 -0
- package/katex.min.css +1 -0
- package/logo.png +0 -0
- package/package.json +31 -4
- package/plugin.json +31 -0
- package/preload/branch-bar.js +201 -0
- package/preload/export-css.js +59 -0
- package/preload/git-panel.js +1366 -0
- package/preload/git-service.js +1463 -0
- package/preload/host.js +130 -0
- package/preload/ndb.js +1407 -0
- package/preload/package.json +3 -0
- package/preload/remote-setup.js +392 -0
- package/preload/remote-wizard.js +328 -0
- package/preload/rename-note.js +150 -0
- package/preload/services.js +423 -0
- package/preload/settings-panel.js +959 -0
- package/preload/ws-prefs.js +55 -0
- package/preload.js +1870 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remote-setup.js —— 一键授权建仓 / 绑定远程笔记仓库
|
|
3
|
+
*
|
|
4
|
+
* 为什么要这个模块:
|
|
5
|
+
* 原来绑定远程要用户自己开网页建仓、复制仓库地址、再配 SSH 或凭据管理器,
|
|
6
|
+
* 对不熟 git 的人门槛太高。这里把「授权 → 建仓(或选已有仓库)→ 绑定 remote → 首次推送」串起来。
|
|
7
|
+
*
|
|
8
|
+
* 两个平台的能力差异(查过官方文档):
|
|
9
|
+
* GitHub:OAuth Device Flow,只需 client_id,不需要 client_secret,
|
|
10
|
+
* 用户在 github.com/login/device 输入 8 位码即可,桌面端的安全做法。
|
|
11
|
+
* Gitee :OAuth2 强制 client_secret(嵌进安装包会被提取),所以改用「私人令牌」,
|
|
12
|
+
* 用户在 gitee.com/profile/personal_access_tokens 生成后粘进来。
|
|
13
|
+
*
|
|
14
|
+
* token 存哪:
|
|
15
|
+
* ~/.cmark-config.json 的 remoteToken 字段(明文,与 git 自身的凭据存储同级别)。
|
|
16
|
+
* git 运行时通过内联 credential helper + 环境变量传入,不写进 .git/config。
|
|
17
|
+
*/
|
|
18
|
+
const fs = require('fs')
|
|
19
|
+
const os = require('os')
|
|
20
|
+
const path = require('path')
|
|
21
|
+
const https = require('https')
|
|
22
|
+
const { execFile } = require('child_process')
|
|
23
|
+
const wsPrefs = require('./ws-prefs.js')
|
|
24
|
+
|
|
25
|
+
const CFG_PATH = path.join(os.homedir(), '.cmark-config.json')
|
|
26
|
+
const GITHUB_CLIENT_ID = 'Ov23liDQDiefOfwzHHLA'
|
|
27
|
+
const GITHUB_SCOPE = 'repo'
|
|
28
|
+
const UA = 'CMark-ZTools-Plugin'
|
|
29
|
+
|
|
30
|
+
/* ---------------- 配置读写 ---------------- */
|
|
31
|
+
function readJSON (p) {
|
|
32
|
+
try { return JSON.parse(fs.readFileSync(p, 'utf8')) } catch (e) { return null }
|
|
33
|
+
}
|
|
34
|
+
function readCfg () {
|
|
35
|
+
return readJSON(CFG_PATH) || {}
|
|
36
|
+
}
|
|
37
|
+
function patchCfg (patch) {
|
|
38
|
+
const next = Object.assign({}, readCfg(), patch || {})
|
|
39
|
+
try {
|
|
40
|
+
fs.mkdirSync(path.dirname(CFG_PATH), { recursive: true })
|
|
41
|
+
fs.writeFileSync(CFG_PATH, JSON.stringify(next, null, 2), 'utf8')
|
|
42
|
+
return true
|
|
43
|
+
} catch (e) { return false }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* ---------------- HTTP ---------------- */
|
|
47
|
+
function request (opts) {
|
|
48
|
+
const o = opts || {}
|
|
49
|
+
return new Promise(function (resolve) {
|
|
50
|
+
let body = o.body || ''
|
|
51
|
+
const headers = Object.assign({
|
|
52
|
+
'User-Agent': UA,
|
|
53
|
+
Accept: 'application/json'
|
|
54
|
+
}, o.headers || {})
|
|
55
|
+
if (body && !headers['Content-Type']) headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
|
56
|
+
if (body) headers['Content-Length'] = Buffer.byteLength(body)
|
|
57
|
+
const req = https.request({
|
|
58
|
+
hostname: o.hostname,
|
|
59
|
+
path: o.path,
|
|
60
|
+
method: o.method || 'GET',
|
|
61
|
+
headers: headers,
|
|
62
|
+
timeout: o.timeout || 20000
|
|
63
|
+
}, function (res) {
|
|
64
|
+
let buf = ''
|
|
65
|
+
res.setEncoding('utf8')
|
|
66
|
+
res.on('data', function (c) { buf += c })
|
|
67
|
+
res.on('end', function () {
|
|
68
|
+
let data = null
|
|
69
|
+
try { data = JSON.parse(buf) } catch (e) { data = { raw: buf.slice(0, 500) } }
|
|
70
|
+
resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, data: data })
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
req.on('timeout', function () { req.destroy(new Error('请求超时')) })
|
|
74
|
+
req.on('error', function (e) { resolve({ ok: false, status: 0, data: { message: (e && e.message) || String(e) } }) })
|
|
75
|
+
if (body) req.write(body)
|
|
76
|
+
req.end()
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
function form (obj) {
|
|
80
|
+
return Object.keys(obj).map(function (k) {
|
|
81
|
+
return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k] == null ? '' : obj[k])
|
|
82
|
+
}).join('&')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ---------------- GitHub Device Flow ---------------- */
|
|
86
|
+
function ghStart () {
|
|
87
|
+
return request({
|
|
88
|
+
hostname: 'github.com',
|
|
89
|
+
path: '/login/device/code',
|
|
90
|
+
method: 'POST',
|
|
91
|
+
body: form({ client_id: GITHUB_CLIENT_ID, scope: GITHUB_SCOPE })
|
|
92
|
+
}).then(function (r) {
|
|
93
|
+
const d = r.data || {}
|
|
94
|
+
if (!r.ok || !d.device_code) {
|
|
95
|
+
return { ok: false, message: d.error_description || d.message || '申请设备码失败(检查 OAuth App 是否开启 Device Flow)' }
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
ok: true,
|
|
99
|
+
deviceCode: d.device_code,
|
|
100
|
+
userCode: d.user_code,
|
|
101
|
+
verificationUri: d.verification_uri || 'https://github.com/login/device',
|
|
102
|
+
expiresIn: d.expires_in || 900,
|
|
103
|
+
interval: d.interval || 5
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 单次轮询:pending = 用户还没在浏览器里确认
|
|
109
|
+
function ghPoll (deviceCode) {
|
|
110
|
+
return request({
|
|
111
|
+
hostname: 'github.com',
|
|
112
|
+
path: '/login/oauth/access_token',
|
|
113
|
+
method: 'POST',
|
|
114
|
+
body: form({
|
|
115
|
+
client_id: GITHUB_CLIENT_ID,
|
|
116
|
+
device_code: deviceCode,
|
|
117
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
|
|
118
|
+
})
|
|
119
|
+
}).then(function (r) {
|
|
120
|
+
const d = r.data || {}
|
|
121
|
+
if (d.access_token) return { ok: true, token: d.access_token }
|
|
122
|
+
if (d.error === 'authorization_pending') return { ok: false, pending: true }
|
|
123
|
+
if (d.error === 'slow_down') return { ok: false, pending: true, slowDown: true }
|
|
124
|
+
if (d.error === 'expired_token') return { ok: false, message: '设备码已过期,请重新发起授权' }
|
|
125
|
+
if (d.error === 'access_denied') return { ok: false, message: '你取消了授权' }
|
|
126
|
+
return { ok: false, message: d.error_description || d.error || ('轮询失败 HTTP ' + r.status) }
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function ghApi (token, method, apiPath, bodyObj) {
|
|
131
|
+
const body = bodyObj ? JSON.stringify(bodyObj) : ''
|
|
132
|
+
return request({
|
|
133
|
+
hostname: 'api.github.com',
|
|
134
|
+
path: apiPath,
|
|
135
|
+
method: method,
|
|
136
|
+
headers: Object.assign({
|
|
137
|
+
Authorization: 'Bearer ' + token,
|
|
138
|
+
Accept: 'application/vnd.github+json'
|
|
139
|
+
}, body ? { 'Content-Type': 'application/json' } : {}),
|
|
140
|
+
body: body
|
|
141
|
+
}).then(function (r) { return { ok: r.ok, status: r.status, data: r.data || {} } })
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function ghUser (token) {
|
|
145
|
+
return ghApi(token, 'GET', '/user').then(function (r) {
|
|
146
|
+
if (!r.ok) return { ok: false, message: (r.data && r.data.message) || '无法读取 GitHub 账号' }
|
|
147
|
+
return { ok: true, login: r.data.login, name: r.data.name || r.data.login }
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function ghListRepos (token) {
|
|
152
|
+
return ghApi(token, 'GET', '/user/repos?per_page=100&sort=updated&affiliation=owner').then(function (r) {
|
|
153
|
+
if (!r.ok) return { ok: false, message: (r.data && r.data.message) || '读取仓库列表失败' }
|
|
154
|
+
const list = (Array.isArray(r.data) ? r.data : []).map(function (x) {
|
|
155
|
+
return {
|
|
156
|
+
name: x.name,
|
|
157
|
+
fullName: x.full_name,
|
|
158
|
+
private: !!x.private,
|
|
159
|
+
url: x.clone_url,
|
|
160
|
+
updatedAt: x.updated_at,
|
|
161
|
+
empty: x.size === 0
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
return { ok: true, repos: list }
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function ghCreateRepo (token, name, isPrivate) {
|
|
169
|
+
return ghApi(token, 'POST', '/user/repos', {
|
|
170
|
+
name: name,
|
|
171
|
+
private: isPrivate !== false,
|
|
172
|
+
auto_init: false,
|
|
173
|
+
description: 'CMark 笔记'
|
|
174
|
+
}).then(function (r) {
|
|
175
|
+
if (!r.ok) {
|
|
176
|
+
const msg = (r.data && r.data.message) || '建仓失败'
|
|
177
|
+
const detail = (r.data && r.data.errors && r.data.errors[0] && r.data.errors[0].message) || ''
|
|
178
|
+
return { ok: false, message: msg + (detail ? ':' + detail : '') }
|
|
179
|
+
}
|
|
180
|
+
return { ok: true, url: r.data.clone_url, fullName: r.data.full_name }
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* ---------------- Gitee(私人令牌) ---------------- */
|
|
185
|
+
function giteeApi (method, apiPath, params, token) {
|
|
186
|
+
const query = Object.assign({}, params || {}, { access_token: token })
|
|
187
|
+
const qs = form(query)
|
|
188
|
+
const isGet = method === 'GET'
|
|
189
|
+
return request({
|
|
190
|
+
hostname: 'gitee.com',
|
|
191
|
+
path: '/api/v5' + apiPath + (isGet ? '?' + qs : ''),
|
|
192
|
+
method: method,
|
|
193
|
+
body: isGet ? '' : qs
|
|
194
|
+
}).then(function (r) { return { ok: r.ok, status: r.status, data: r.data || {} } })
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function giteeVerify (token) {
|
|
198
|
+
return giteeApi('GET', '/user', {}, token).then(function (r) {
|
|
199
|
+
if (!r.ok) return { ok: false, message: (r.data && (r.data.message || r.data.error)) || '令牌无效或已过期' }
|
|
200
|
+
return { ok: true, login: r.data.login || r.data.name, name: r.data.name || r.data.login }
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function giteeListRepos (token) {
|
|
205
|
+
return giteeApi('GET', '/user/repos', { per_page: 100, sort: 'updated' }, token).then(function (r) {
|
|
206
|
+
if (!r.ok) return { ok: false, message: (r.data && (r.data.message || r.data.error)) || '读取仓库列表失败' }
|
|
207
|
+
const list = (Array.isArray(r.data) ? r.data : []).map(function (x) {
|
|
208
|
+
return {
|
|
209
|
+
name: x.name,
|
|
210
|
+
fullName: x.full_name || (x.namespace && x.namespace.path ? x.namespace.path + '/' + x.name : x.name),
|
|
211
|
+
private: !!x.private,
|
|
212
|
+
url: x.html_url ? x.html_url.replace(/\.git$/, '') + '.git' : '',
|
|
213
|
+
updatedAt: x.updated_at
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
return { ok: true, repos: list }
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function giteeCreateRepo (token, name, isPrivate) {
|
|
221
|
+
return giteeApi('POST', '/user/repos', {
|
|
222
|
+
name: name,
|
|
223
|
+
private: isPrivate !== false,
|
|
224
|
+
auto_init: false,
|
|
225
|
+
description: 'CMark 笔记'
|
|
226
|
+
}, token).then(function (r) {
|
|
227
|
+
if (!r.ok) return { ok: false, message: (r.data && (r.data.message || r.data.error)) || '建仓失败' }
|
|
228
|
+
const full = r.data.full_name || r.data.path
|
|
229
|
+
return { ok: true, url: 'https://gitee.com/' + full + '.git', fullName: full }
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* ---------------- git 绑定 ---------------- */
|
|
234
|
+
const TOKEN_HELPER = '!f() { echo username=x-access-token; echo password=$CMARK_GIT_TOKEN; }; f'
|
|
235
|
+
|
|
236
|
+
function git (args, cwd, token, timeout) {
|
|
237
|
+
return new Promise(function (resolve) {
|
|
238
|
+
const pre = token
|
|
239
|
+
? ['-c', 'core.quotepath=false', '-c', 'credential.helper=', '-c', 'credential.helper=' + TOKEN_HELPER]
|
|
240
|
+
: ['-c', 'core.quotepath=false', '-c', 'credential.helper=', '-c', 'credential.helper=manager']
|
|
241
|
+
execFile('git', pre.concat(args), {
|
|
242
|
+
cwd: cwd,
|
|
243
|
+
encoding: 'utf8',
|
|
244
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
245
|
+
windowsHide: true,
|
|
246
|
+
timeout: timeout || 120000,
|
|
247
|
+
env: Object.assign({}, process.env, {
|
|
248
|
+
GCM_INTERACTIVE: 'never',
|
|
249
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
250
|
+
LC_ALL: 'C',
|
|
251
|
+
GIT_LITERAL_PATHSPECS: '1',
|
|
252
|
+
CMARK_GIT_TOKEN: token || ''
|
|
253
|
+
})
|
|
254
|
+
}, function (err, stdout, stderr) {
|
|
255
|
+
if (err) {
|
|
256
|
+
resolve({ ok: false, message: String(stderr || stdout || err.message || '').trim().slice(0, 600) })
|
|
257
|
+
} else {
|
|
258
|
+
resolve({ ok: true, data: String(stdout || '').trim() })
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function isRepo (dir) {
|
|
265
|
+
try { return fs.existsSync(path.join(dir, '.git')) } catch (e) { return false }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 绑定远程笔记仓库
|
|
270
|
+
* @param {object} o
|
|
271
|
+
* provider: 'github' | 'gitee'
|
|
272
|
+
* url: https 仓库地址(不带 token)
|
|
273
|
+
* token: 访问令牌
|
|
274
|
+
* dir: 本地笔记目录
|
|
275
|
+
* existing: true 表示远程已有内容,需要先拉下来(否则会把本地空历史推上去)
|
|
276
|
+
*/
|
|
277
|
+
async function bind (o) {
|
|
278
|
+
const opt = o || {}
|
|
279
|
+
const dir = opt.dir
|
|
280
|
+
const url = opt.url
|
|
281
|
+
const token = opt.token
|
|
282
|
+
if (!dir || !url || !token) return { ok: false, message: '参数不完整' }
|
|
283
|
+
if (!fs.existsSync(dir)) {
|
|
284
|
+
try { fs.mkdirSync(dir, { recursive: true }) } catch (e) {
|
|
285
|
+
return { ok: false, message: '无法创建目录:' + dir }
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const steps = []
|
|
290
|
+
if (!isRepo(dir)) {
|
|
291
|
+
const init = await git(['init', '-b', 'main'], dir)
|
|
292
|
+
if (!init.ok) return { ok: false, message: 'git init 失败:' + init.message }
|
|
293
|
+
steps.push('init')
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const getRemote = await git(['remote', 'get-url', 'origin'], dir)
|
|
297
|
+
if (getRemote.ok) {
|
|
298
|
+
const setUrl = await git(['remote', 'set-url', 'origin', url], dir)
|
|
299
|
+
if (!setUrl.ok) return { ok: false, message: '更新远端地址失败:' + setUrl.message }
|
|
300
|
+
steps.push('remote-set')
|
|
301
|
+
} else {
|
|
302
|
+
const add = await git(['remote', 'add', 'origin', url], dir)
|
|
303
|
+
if (!add.ok) return { ok: false, message: '添加 remote 失败:' + add.message }
|
|
304
|
+
steps.push('remote-add')
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (opt.existing) {
|
|
308
|
+
// 远程已有仓库:把内容拉下来(只能 fast-forward,避免覆盖本地笔记)
|
|
309
|
+
const fetch = await git(['fetch', 'origin'], dir, token, 180000)
|
|
310
|
+
if (!fetch.ok) return { ok: false, message: '拉取失败:' + fetch.message, steps: steps }
|
|
311
|
+
steps.push('fetch')
|
|
312
|
+
|
|
313
|
+
// 远端默认分支不一定是 main(Gitee 新建仓库默认 master),按远端 HEAD 指向来合并
|
|
314
|
+
let remoteBranch = 'main'
|
|
315
|
+
try {
|
|
316
|
+
const sym = await git(['ls-remote', '--symref', 'origin', 'HEAD'], dir, token, 60000)
|
|
317
|
+
const m = sym.ok ? /ref:\s+refs\/heads\/(\S+)\s+HEAD/.exec(sym.data || '') : null
|
|
318
|
+
if (m && m[1]) remoteBranch = m[1]
|
|
319
|
+
} catch (e) {}
|
|
320
|
+
|
|
321
|
+
if (remoteBranch !== 'main') {
|
|
322
|
+
// 本地是 main、远程是别的名字:把本地主干指到远程分支,避免两个平行根
|
|
323
|
+
await git(['branch', '-M', remoteBranch], dir)
|
|
324
|
+
}
|
|
325
|
+
const merge = await git(['merge', '--ff-only', 'origin/' + remoteBranch], dir, token, 60000)
|
|
326
|
+
if (!merge.ok) {
|
|
327
|
+
return { ok: false, message: '合并远端分支 ' + remoteBranch + ' 失败:' + merge.message, steps: steps }
|
|
328
|
+
}
|
|
329
|
+
steps.push('merge')
|
|
330
|
+
} else {
|
|
331
|
+
// 全新仓库:提交本地内容后首次推送
|
|
332
|
+
const hasCommit = await git(['rev-parse', '--verify', 'HEAD'], dir)
|
|
333
|
+
if (!hasCommit.ok) {
|
|
334
|
+
const addAll = await git(['add', '-A', '--', '.'], dir)
|
|
335
|
+
if (!addAll.ok) return { ok: false, message: '暂存失败:' + addAll.message }
|
|
336
|
+
const commit = await git(['commit', '-m', '初始化笔记仓库'], dir)
|
|
337
|
+
if (!commit.ok && !/nothing to commit/i.test(commit.message)) {
|
|
338
|
+
return { ok: false, message: '提交失败:' + commit.message }
|
|
339
|
+
}
|
|
340
|
+
steps.push('commit')
|
|
341
|
+
}
|
|
342
|
+
const push = await git(['push', '-u', 'origin', 'main'], dir, token, 180000)
|
|
343
|
+
if (!push.ok) return { ok: false, message: '首次推送失败:' + push.message, steps: steps }
|
|
344
|
+
steps.push('push')
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// dir 是工作区(仓库根)。笔记目录若已经在它之内就保持不动,
|
|
348
|
+
// 这样「工作区 D:\Proj + 笔记 D:\Proj\Notes」这种布局不会被建仓流程压平
|
|
349
|
+
const cur = readCfg() || {}
|
|
350
|
+
const curWs = cur.workspace || ''
|
|
351
|
+
let keepWs = false
|
|
352
|
+
try {
|
|
353
|
+
const rel = path.relative(dir, curWs)
|
|
354
|
+
keepWs = !!curWs && !rel.startsWith('..') && !path.isAbsolute(rel)
|
|
355
|
+
} catch (e) { keepWs = false }
|
|
356
|
+
patchCfg(Object.assign({ repoDir: dir, initialized: true }, keepWs ? {} : { workspace: dir }))
|
|
357
|
+
const savedRes = wsPrefs.patch(dir, { remoteToken: { provider: opt.provider || '', token: token, url: url } })
|
|
358
|
+
const saved = !!(savedRes && savedRes.ok)
|
|
359
|
+
return { ok: saved, steps: steps, message: saved ? '' : ((savedRes && savedRes.message) || '写入配置失败') }
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function attach (win) {
|
|
363
|
+
win.__CMARK_REMOTE = {
|
|
364
|
+
githubClientId: GITHUB_CLIENT_ID,
|
|
365
|
+
ghStart: ghStart,
|
|
366
|
+
ghPoll: ghPoll,
|
|
367
|
+
ghUser: ghUser,
|
|
368
|
+
ghListRepos: ghListRepos,
|
|
369
|
+
ghCreateRepo: ghCreateRepo,
|
|
370
|
+
giteeVerify: giteeVerify,
|
|
371
|
+
giteeListRepos: giteeListRepos,
|
|
372
|
+
giteeCreateRepo: giteeCreateRepo,
|
|
373
|
+
bind: bind,
|
|
374
|
+
// 当前是否已存过凭据
|
|
375
|
+
current: function () {
|
|
376
|
+
const c = readCfg() || {}
|
|
377
|
+
const p = c.workspace ? wsPrefs.read(c.workspace) : {}
|
|
378
|
+
const t = p.remoteToken || c.remoteToken
|
|
379
|
+
return t ? { provider: t.provider, url: t.url } : null
|
|
380
|
+
},
|
|
381
|
+
clear: function () {
|
|
382
|
+
const c = readCfg() || {}
|
|
383
|
+
if (c.workspace) wsPrefs.patch(c.workspace, { remoteToken: null })
|
|
384
|
+
patchCfg({ remoteToken: null })
|
|
385
|
+
return { ok: true }
|
|
386
|
+
},
|
|
387
|
+
openUrl: function (u) { try { win.ztools && win.ztools.shellOpenExternal && win.ztools.shellOpenExternal(u) } catch (e) {} }
|
|
388
|
+
}
|
|
389
|
+
return win.__CMARK_REMOTE
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
module.exports = attach
|