termbeam 1.15.1 → 1.16.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.md +2 -0
- package/package.json +2 -1
- package/public/assets/{_basePickBy-DDga1sgN.js → _basePickBy-CYR9pyOe.js} +1 -1
- package/public/assets/{_baseUniq-CKfoLvLE.js → _baseUniq-DeFSIx-P.js} +1 -1
- package/public/assets/{arc-DbSCVdo8.js → arc-CUEX1fu7.js} +1 -1
- package/public/assets/{architectureDiagram-2XIMDMQ5-Ce5knFNR.js → architectureDiagram-2XIMDMQ5-BHhXPzZJ.js} +1 -1
- package/public/assets/{blockDiagram-WCTKOSBZ-DJiZx7DH.js → blockDiagram-WCTKOSBZ-RsOwF2Ow.js} +1 -1
- package/public/assets/{c4Diagram-IC4MRINW-kOHNvx7n.js → c4Diagram-IC4MRINW-B7KKaZ1J.js} +1 -1
- package/public/assets/channel-CBJEzKmm.js +1 -0
- package/public/assets/{chunk-4BX2VUAB-DxmUWf39.js → chunk-4BX2VUAB-DOUcZxxl.js} +1 -1
- package/public/assets/{chunk-55IACEB6-BdvL648G.js → chunk-55IACEB6-bPgkuqF0.js} +1 -1
- package/public/assets/{chunk-FMBD7UC4-Bp3FkcH2.js → chunk-FMBD7UC4-BWT_ExWr.js} +1 -1
- package/public/assets/{chunk-JSJVCQXG-DOtbuVd2.js → chunk-JSJVCQXG-Df0AgfkZ.js} +1 -1
- package/public/assets/{chunk-KX2RTZJC-b_RAN48_.js → chunk-KX2RTZJC-DnYuhgK5.js} +1 -1
- package/public/assets/{chunk-NQ4KR5QH-CKHEKES_.js → chunk-NQ4KR5QH-Dge50UUS.js} +1 -1
- package/public/assets/{chunk-QZHKN3VN-Ce3Cy8iK.js → chunk-QZHKN3VN-BT0knyhA.js} +1 -1
- package/public/assets/{chunk-WL4C6EOR-CYlFnkd_.js → chunk-WL4C6EOR-DutXGT-d.js} +1 -1
- package/public/assets/classDiagram-VBA2DB6C-C-rOD9EU.js +1 -0
- package/public/assets/classDiagram-v2-RAHNMMFH-C-rOD9EU.js +1 -0
- package/public/assets/clone-DIyhZC23.js +1 -0
- package/public/assets/{cose-bilkent-S5V4N54A-Curtohg5.js → cose-bilkent-S5V4N54A-doAicD_V.js} +1 -1
- package/public/assets/{dagre-KLK3FWXG-CuZNu96V.js → dagre-KLK3FWXG-O4cFm_hK.js} +1 -1
- package/public/assets/{diagram-E7M64L7V-CunKBx6l.js → diagram-E7M64L7V-BifAzLVq.js} +1 -1
- package/public/assets/{diagram-IFDJBPK2-BN7aHVm4.js → diagram-IFDJBPK2-BfnxORJG.js} +1 -1
- package/public/assets/{diagram-P4PSJMXO-B4lVLdoW.js → diagram-P4PSJMXO-DTr1JYXb.js} +1 -1
- package/public/assets/{erDiagram-INFDFZHY-EenQr3uP.js → erDiagram-INFDFZHY-l1N_y881.js} +1 -1
- package/public/assets/{flowDiagram-PKNHOUZH-CvFkNm_6.js → flowDiagram-PKNHOUZH-CLGWYVco.js} +1 -1
- package/public/assets/{ganttDiagram-A5KZAMGK-DY9bVz9l.js → ganttDiagram-A5KZAMGK-9ERk2sFV.js} +1 -1
- package/public/assets/{gitGraphDiagram-K3NZZRJ6-BAd-taYp.js → gitGraphDiagram-K3NZZRJ6-BxK3Z85E.js} +1 -1
- package/public/assets/{graph-B5Mupk4w.js → graph-PwJPsvsO.js} +1 -1
- package/public/assets/index-0a9Qn-A1.js +394 -0
- package/public/assets/index-Z_lybSmO.css +32 -0
- package/public/assets/{infoDiagram-LFFYTUFH-vhp46Ys0.js → infoDiagram-LFFYTUFH-DygGOypU.js} +1 -1
- package/public/assets/{ishikawaDiagram-PHBUUO56-GppOId5G.js → ishikawaDiagram-PHBUUO56-CySga9vu.js} +1 -1
- package/public/assets/{journeyDiagram-4ABVD52K-Bf8IH4_E.js → journeyDiagram-4ABVD52K-ZIZNkXyJ.js} +1 -1
- package/public/assets/{kanban-definition-K7BYSVSG-CsV7UppO.js → kanban-definition-K7BYSVSG-IxWUQjiQ.js} +1 -1
- package/public/assets/{layout-TFe_JtAk.js → layout-DbFs-9Gp.js} +1 -1
- package/public/assets/{linear-jRuCITkz.js → linear-F1crC_h8.js} +1 -1
- package/public/assets/{mindmap-definition-YRQLILUH-Drqk-jqT.js → mindmap-definition-YRQLILUH-BwXWnIOB.js} +1 -1
- package/public/assets/{pieDiagram-SKSYHLDU-zhtwIYVW.js → pieDiagram-SKSYHLDU-CTKX_qGt.js} +1 -1
- package/public/assets/{quadrantDiagram-337W2JSQ-CgighrLO.js → quadrantDiagram-337W2JSQ-C9hYwyla.js} +1 -1
- package/public/assets/{requirementDiagram-Z7DCOOCP-Blze02a-.js → requirementDiagram-Z7DCOOCP-eOJ_I7lS.js} +1 -1
- package/public/assets/{sankeyDiagram-WA2Y5GQK-DP2pOuJP.js → sankeyDiagram-WA2Y5GQK-CNYzy0Z-.js} +1 -1
- package/public/assets/{sequenceDiagram-2WXFIKYE-2ZQZVVEw.js → sequenceDiagram-2WXFIKYE-ChHor0a9.js} +1 -1
- package/public/assets/{stateDiagram-RAJIS63D-CxBDjO6s.js → stateDiagram-RAJIS63D-CF2B1sp8.js} +1 -1
- package/public/assets/stateDiagram-v2-FVOUBMTO-Doxped-1.js +1 -0
- package/public/assets/{timeline-definition-YZTLITO2-Du6zdjZw.js → timeline-definition-YZTLITO2-H72vBjMX.js} +1 -1
- package/public/assets/{treemap-KZPCXAKY-Duew5oqq.js → treemap-KZPCXAKY-CWEZDi_m.js} +1 -1
- package/public/assets/{vennDiagram-LZ73GAT5-D9zfCBm9.js → vennDiagram-LZ73GAT5-BEqSLv2W.js} +1 -1
- package/public/assets/{xychartDiagram-JWTSCODW-BJuwWw4_.js → xychartDiagram-JWTSCODW-enJM4ByX.js} +1 -1
- package/public/index.html +2 -2
- package/public/sw.js +2 -2
- package/src/server/index.js +22 -3
- package/src/server/push.js +118 -0
- package/src/server/routes.js +227 -1
- package/src/server/sessions.js +144 -0
- package/src/server/websocket.js +21 -2
- package/src/utils/git.js +338 -1
- package/src/utils/vapid.js +45 -0
- package/public/assets/channel-Ccb6hGZz.js +0 -1
- package/public/assets/classDiagram-VBA2DB6C-BIrAPXFF.js +0 -1
- package/public/assets/classDiagram-v2-RAHNMMFH-BIrAPXFF.js +0 -1
- package/public/assets/clone-D5RGMzJC.js +0 -1
- package/public/assets/index-BEOqWnh5.js +0 -391
- package/public/assets/index-D_1GL6a5.css +0 -32
- package/public/assets/stateDiagram-v2-FVOUBMTO-3AffBMDC.js +0 -1
package/src/utils/git.js
CHANGED
|
@@ -125,4 +125,341 @@ function parseStatus(output, ahead, behind) {
|
|
|
125
125
|
return { clean, modified, staged, untracked, ahead: ahead || 0, behind: behind || 0, summary };
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
// --- Extended git utilities (async, using execFile for security) ---
|
|
129
|
+
|
|
130
|
+
const GIT_TIMEOUT = 5000;
|
|
131
|
+
const MAX_DIFF_BUFFER = 1024 * 1024; // 1 MB
|
|
132
|
+
const MAX_BLAME_BUFFER = 2 * 1024 * 1024; // 2 MB
|
|
133
|
+
const MAX_LOG_BUFFER = 1024 * 1024; // 1 MB
|
|
134
|
+
|
|
135
|
+
async function gitAsync(args, cwd, options = {}) {
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
require('child_process').execFile(
|
|
138
|
+
'git',
|
|
139
|
+
args,
|
|
140
|
+
{
|
|
141
|
+
cwd,
|
|
142
|
+
timeout: options.timeout || GIT_TIMEOUT,
|
|
143
|
+
maxBuffer: options.maxBuffer || MAX_DIFF_BUFFER,
|
|
144
|
+
},
|
|
145
|
+
(err, stdout) => {
|
|
146
|
+
if (err) return reject(err);
|
|
147
|
+
resolve(stdout);
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function getDetailedStatus(cwd) {
|
|
154
|
+
try {
|
|
155
|
+
await gitAsync(['rev-parse', '--is-inside-work-tree'], cwd);
|
|
156
|
+
} catch {
|
|
157
|
+
return { isGitRepo: false };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const result = {
|
|
161
|
+
branch: null,
|
|
162
|
+
ahead: 0,
|
|
163
|
+
behind: 0,
|
|
164
|
+
staged: [],
|
|
165
|
+
modified: [],
|
|
166
|
+
untracked: [],
|
|
167
|
+
isGitRepo: true,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
const raw = await gitAsync(['status', '--porcelain=v1', '-b'], cwd);
|
|
172
|
+
const lines = raw.split('\n').filter(Boolean);
|
|
173
|
+
|
|
174
|
+
for (const line of lines) {
|
|
175
|
+
// Branch header line
|
|
176
|
+
if (line.startsWith('## ')) {
|
|
177
|
+
const branchInfo = line.slice(3);
|
|
178
|
+
const trackMatch = branchInfo.match(/^(.+?)(?:\.\.\.(.+?))?(?:\s+\[(.+)\])?$/);
|
|
179
|
+
if (trackMatch) {
|
|
180
|
+
result.branch = trackMatch[1];
|
|
181
|
+
const tracking = trackMatch[3];
|
|
182
|
+
if (tracking) {
|
|
183
|
+
const aheadMatch = tracking.match(/ahead (\d+)/);
|
|
184
|
+
const behindMatch = tracking.match(/behind (\d+)/);
|
|
185
|
+
if (aheadMatch) result.ahead = parseInt(aheadMatch[1], 10);
|
|
186
|
+
if (behindMatch) result.behind = parseInt(behindMatch[1], 10);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const index = line[0];
|
|
193
|
+
const working = line[1];
|
|
194
|
+
const filePart = line.slice(3);
|
|
195
|
+
|
|
196
|
+
// Untracked
|
|
197
|
+
if (index === '?' && working === '?') {
|
|
198
|
+
result.untracked.push(filePart);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Staged changes (index column)
|
|
203
|
+
if (index !== ' ' && index !== '?') {
|
|
204
|
+
const entry = { path: filePart, status: index, oldPath: null };
|
|
205
|
+
if (index === 'R' || index === 'C') {
|
|
206
|
+
const parts = filePart.split(' -> ');
|
|
207
|
+
if (parts.length === 2) {
|
|
208
|
+
entry.oldPath = parts[0];
|
|
209
|
+
entry.path = parts[1];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
result.staged.push(entry);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Working tree changes (working column)
|
|
216
|
+
if (working !== ' ' && working !== '?') {
|
|
217
|
+
result.modified.push({ path: filePart, status: working, oldPath: null });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
log.warn(`getDetailedStatus failed: ${err.message}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function parseDiffOutput(raw, filePath) {
|
|
228
|
+
const result = {
|
|
229
|
+
file: filePath,
|
|
230
|
+
hunks: [],
|
|
231
|
+
additions: 0,
|
|
232
|
+
deletions: 0,
|
|
233
|
+
isBinary: false,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
if (!raw.trim()) return result;
|
|
237
|
+
|
|
238
|
+
// Binary file detection — only check the diff header (lines before the first hunk).
|
|
239
|
+
// Content lines (prefixed with +/-/ ) may contain "Binary files ... differ" as text.
|
|
240
|
+
const firstHunkIdx = raw.indexOf('\n@@');
|
|
241
|
+
const header = firstHunkIdx >= 0 ? raw.slice(0, firstHunkIdx) : raw;
|
|
242
|
+
if (header.includes('Binary files') && header.includes('differ')) {
|
|
243
|
+
result.isBinary = true;
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Parse unified diff into hunks
|
|
248
|
+
const lines = raw.split('\n');
|
|
249
|
+
let currentHunk = null;
|
|
250
|
+
let oldLine = 0;
|
|
251
|
+
let newLine = 0;
|
|
252
|
+
|
|
253
|
+
for (const line of lines) {
|
|
254
|
+
// Hunk header
|
|
255
|
+
const hunkMatch = line.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
|
256
|
+
if (hunkMatch) {
|
|
257
|
+
currentHunk = {
|
|
258
|
+
header: line.match(/^@@.*@@/)[0],
|
|
259
|
+
oldStart: parseInt(hunkMatch[1], 10),
|
|
260
|
+
oldLines: parseInt(hunkMatch[2] ?? '1', 10),
|
|
261
|
+
newStart: parseInt(hunkMatch[3], 10),
|
|
262
|
+
newLines: parseInt(hunkMatch[4] ?? '1', 10),
|
|
263
|
+
lines: [],
|
|
264
|
+
};
|
|
265
|
+
result.hunks.push(currentHunk);
|
|
266
|
+
oldLine = currentHunk.oldStart;
|
|
267
|
+
newLine = currentHunk.newStart;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!currentHunk) continue;
|
|
272
|
+
|
|
273
|
+
if (line.startsWith('+')) {
|
|
274
|
+
currentHunk.lines.push({
|
|
275
|
+
type: 'add',
|
|
276
|
+
content: line.slice(1),
|
|
277
|
+
oldLine: null,
|
|
278
|
+
newLine: newLine++,
|
|
279
|
+
});
|
|
280
|
+
result.additions++;
|
|
281
|
+
} else if (line.startsWith('-')) {
|
|
282
|
+
currentHunk.lines.push({
|
|
283
|
+
type: 'remove',
|
|
284
|
+
content: line.slice(1),
|
|
285
|
+
oldLine: oldLine++,
|
|
286
|
+
newLine: null,
|
|
287
|
+
});
|
|
288
|
+
result.deletions++;
|
|
289
|
+
} else if (line.startsWith(' ')) {
|
|
290
|
+
currentHunk.lines.push({
|
|
291
|
+
type: 'context',
|
|
292
|
+
content: line.slice(1),
|
|
293
|
+
oldLine: oldLine++,
|
|
294
|
+
newLine: newLine++,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
// Skip diff header lines (diff --git, index, ---, +++)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function getFileDiff(cwd, filePath, options = {}) {
|
|
304
|
+
const { staged = false, untracked = false, context = 3 } = options;
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
// Untracked files: use --no-index to diff against the null device
|
|
308
|
+
const nullDevice = process.platform === 'win32' ? 'NUL' : '/dev/null';
|
|
309
|
+
if (untracked) {
|
|
310
|
+
const raw = await new Promise((resolve, reject) => {
|
|
311
|
+
require('child_process').execFile(
|
|
312
|
+
'git',
|
|
313
|
+
['diff', '--no-index', '--no-color', `--unified=${context}`, '--', nullDevice, filePath],
|
|
314
|
+
{
|
|
315
|
+
cwd,
|
|
316
|
+
timeout: GIT_TIMEOUT,
|
|
317
|
+
maxBuffer: MAX_DIFF_BUFFER,
|
|
318
|
+
},
|
|
319
|
+
(err, stdout) => {
|
|
320
|
+
// git diff --no-index exits with 1 when files differ — that's expected
|
|
321
|
+
if (err && err.code !== 1) return reject(err);
|
|
322
|
+
resolve(stdout || '');
|
|
323
|
+
},
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
return parseDiffOutput(raw, filePath);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const args = ['diff', `--unified=${context}`, '--no-color'];
|
|
330
|
+
if (staged) args.push('--cached');
|
|
331
|
+
args.push('--', filePath);
|
|
332
|
+
|
|
333
|
+
const raw = await gitAsync(args, cwd, { maxBuffer: MAX_DIFF_BUFFER });
|
|
334
|
+
return parseDiffOutput(raw, filePath);
|
|
335
|
+
} catch (err) {
|
|
336
|
+
// Empty diff or git error
|
|
337
|
+
if (err.code !== 1) {
|
|
338
|
+
log.warn(`getFileDiff failed: ${err.message}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
file: filePath,
|
|
344
|
+
hunks: [],
|
|
345
|
+
additions: 0,
|
|
346
|
+
deletions: 0,
|
|
347
|
+
isBinary: false,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function getFileBlame(cwd, filePath) {
|
|
352
|
+
const result = { file: filePath, lines: [] };
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
const raw = await gitAsync(['blame', '--porcelain', '--', filePath], cwd, {
|
|
356
|
+
maxBuffer: MAX_BLAME_BUFFER,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const rawLines = raw.split('\n');
|
|
360
|
+
let currentCommit = null;
|
|
361
|
+
let currentLine = null;
|
|
362
|
+
const commitInfo = {}; // cache commit metadata
|
|
363
|
+
|
|
364
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
365
|
+
const line = rawLines[i];
|
|
366
|
+
|
|
367
|
+
// Commit line: <40-char-hash> <orig-line> <final-line> [<num-lines>]
|
|
368
|
+
const commitMatch = line.match(/^([0-9a-f]{40})\s+(\d+)\s+(\d+)(?:\s+(\d+))?$/);
|
|
369
|
+
if (commitMatch) {
|
|
370
|
+
currentCommit = commitMatch[1];
|
|
371
|
+
currentLine = parseInt(commitMatch[3], 10);
|
|
372
|
+
|
|
373
|
+
if (!commitInfo[currentCommit]) {
|
|
374
|
+
commitInfo[currentCommit] = {
|
|
375
|
+
author: null,
|
|
376
|
+
date: null,
|
|
377
|
+
summary: null,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Metadata lines
|
|
384
|
+
if (currentCommit && line.startsWith('author ')) {
|
|
385
|
+
commitInfo[currentCommit].author = line.slice(7);
|
|
386
|
+
} else if (currentCommit && line.startsWith('author-time ')) {
|
|
387
|
+
const timestamp = parseInt(line.slice(12), 10);
|
|
388
|
+
commitInfo[currentCommit].date = new Date(timestamp * 1000).toISOString();
|
|
389
|
+
} else if (currentCommit && line.startsWith('summary ')) {
|
|
390
|
+
commitInfo[currentCommit].summary = line.slice(8);
|
|
391
|
+
} else if (currentCommit && line.startsWith('\t')) {
|
|
392
|
+
// Content line
|
|
393
|
+
const info = commitInfo[currentCommit];
|
|
394
|
+
const isUncommitted = currentCommit === '0000000000000000000000000000000000000000';
|
|
395
|
+
result.lines.push({
|
|
396
|
+
line: currentLine,
|
|
397
|
+
content: line.slice(1),
|
|
398
|
+
commit: isUncommitted ? null : currentCommit.slice(0, 7),
|
|
399
|
+
author: isUncommitted ? 'Not Committed Yet' : info.author || 'Unknown',
|
|
400
|
+
date: isUncommitted ? null : info.date || null,
|
|
401
|
+
summary: isUncommitted ? 'Uncommitted changes' : info.summary || '',
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
} catch (err) {
|
|
406
|
+
log.warn(`getFileBlame failed: ${err.message}`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return result;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const LOG_SEPARATOR = '---GIT_LOG_SEP---';
|
|
413
|
+
const LOG_FIELD_SEP = '---GIT_FIELD_SEP---';
|
|
414
|
+
const LOG_FORMAT = [
|
|
415
|
+
'%H', // hash
|
|
416
|
+
'%h', // short hash
|
|
417
|
+
'%an', // author name
|
|
418
|
+
'%ae', // author email
|
|
419
|
+
'%aI', // author date ISO
|
|
420
|
+
'%s', // subject
|
|
421
|
+
'%b', // body
|
|
422
|
+
].join(LOG_FIELD_SEP);
|
|
423
|
+
|
|
424
|
+
async function getGitLog(cwd, options = {}) {
|
|
425
|
+
const limit = Math.min(Math.max(parseInt(options.limit, 10) || 20, 1), 100);
|
|
426
|
+
const result = { commits: [] };
|
|
427
|
+
|
|
428
|
+
try {
|
|
429
|
+
const args = ['log', `--format=${LOG_SEPARATOR}${LOG_FORMAT}`, `-n`, String(limit)];
|
|
430
|
+
if (options.file) {
|
|
431
|
+
args.push('--follow', '--', options.file);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const raw = await gitAsync(args, cwd, { maxBuffer: MAX_LOG_BUFFER });
|
|
435
|
+
|
|
436
|
+
const entries = raw.split(LOG_SEPARATOR).filter((e) => e.trim());
|
|
437
|
+
for (const entry of entries) {
|
|
438
|
+
const fields = entry.trim().split(LOG_FIELD_SEP);
|
|
439
|
+
if (fields.length < 6) continue;
|
|
440
|
+
result.commits.push({
|
|
441
|
+
hash: fields[0],
|
|
442
|
+
shortHash: fields[1],
|
|
443
|
+
author: fields[2],
|
|
444
|
+
email: fields[3],
|
|
445
|
+
date: fields[4],
|
|
446
|
+
subject: fields[5],
|
|
447
|
+
body: (fields[6] || '').trim(),
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
} catch (err) {
|
|
451
|
+
log.warn(`getGitLog failed: ${err.message}`);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return result;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
module.exports = {
|
|
458
|
+
getGitInfo,
|
|
459
|
+
parseRemoteUrl,
|
|
460
|
+
parseStatus,
|
|
461
|
+
getDetailedStatus,
|
|
462
|
+
getFileDiff,
|
|
463
|
+
getFileBlame,
|
|
464
|
+
getGitLog,
|
|
465
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const webpush = require('web-push');
|
|
4
|
+
const log = require('./logger');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Load existing VAPID keys from configDir/vapid.json or generate new ones.
|
|
8
|
+
* Keys are persisted so they survive server restarts.
|
|
9
|
+
* @param {string} configDir - Directory to store vapid.json
|
|
10
|
+
* @returns {{ publicKey: string, privateKey: string, subject: string }}
|
|
11
|
+
*/
|
|
12
|
+
function getOrCreateVapidKeys(configDir) {
|
|
13
|
+
const vapidPath = path.join(configDir, 'vapid.json');
|
|
14
|
+
const subject = 'https://termbeam.dev';
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const raw = fs.readFileSync(vapidPath, 'utf8');
|
|
18
|
+
const keys = JSON.parse(raw);
|
|
19
|
+
if (keys.publicKey && keys.privateKey) {
|
|
20
|
+
log.debug('Loaded existing VAPID keys');
|
|
21
|
+
return { publicKey: keys.publicKey, privateKey: keys.privateKey, subject };
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
// File doesn't exist or is invalid — generate new keys
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
log.info('Generating new VAPID keys');
|
|
28
|
+
const keys = webpush.generateVAPIDKeys();
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
32
|
+
fs.writeFileSync(
|
|
33
|
+
vapidPath,
|
|
34
|
+
JSON.stringify({ publicKey: keys.publicKey, privateKey: keys.privateKey }, null, 2),
|
|
35
|
+
{ mode: 0o600 },
|
|
36
|
+
);
|
|
37
|
+
log.debug(`VAPID keys saved to ${vapidPath}`);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
log.warn(`Could not save VAPID keys to ${vapidPath}: ${err.message}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { publicKey: keys.publicKey, privateKey: keys.privateKey, subject };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { getOrCreateVapidKeys };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{aq as o,ar as n}from"./index-BEOqWnh5.js";const t=(r,a)=>o.lang.round(n.parse(r)[a]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-WL4C6EOR-CYlFnkd_.js";import{_ as i}from"./index-BEOqWnh5.js";import"./chunk-FMBD7UC4-Bp3FkcH2.js";import"./chunk-JSJVCQXG-DOtbuVd2.js";import"./chunk-55IACEB6-BdvL648G.js";import"./chunk-KX2RTZJC-b_RAN48_.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-WL4C6EOR-CYlFnkd_.js";import{_ as i}from"./index-BEOqWnh5.js";import"./chunk-FMBD7UC4-Bp3FkcH2.js";import"./chunk-JSJVCQXG-DOtbuVd2.js";import"./chunk-55IACEB6-BdvL648G.js";import"./chunk-KX2RTZJC-b_RAN48_.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as r}from"./_baseUniq-CKfoLvLE.js";var e=4;function a(o){return r(o,e)}export{a as c};
|