cursor-guard 4.6.0 → 4.6.2
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/LICENSE +65 -21
- package/ROADMAP.md +14 -2
- package/package.json +2 -2
- package/references/dashboard/public/app.js +54 -19
package/LICENSE
CHANGED
|
@@ -1,21 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: zhangqiang8vipp
|
|
6
|
+
Licensed Work: cursor-guard
|
|
7
|
+
The Licensed Work is (c) 2026 zhangqiang8vipp
|
|
8
|
+
Additional Use Grant: You may make use of the Licensed Work, provided that
|
|
9
|
+
you may not use the Licensed Work for a Commercial Use.
|
|
10
|
+
"Commercial Use" means distribution, sale, licensing,
|
|
11
|
+
sublicensing, or providing the Licensed Work (or any
|
|
12
|
+
derivative work) to third parties as a paid product or
|
|
13
|
+
service, or incorporating it into a paid product or
|
|
14
|
+
service offered to third parties.
|
|
15
|
+
Change Date: 2056-03-22
|
|
16
|
+
Change License: Apache License, Version 2.0
|
|
17
|
+
|
|
18
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
19
|
+
please contact: zhangqiang8vipp
|
|
20
|
+
|
|
21
|
+
Notice
|
|
22
|
+
|
|
23
|
+
Business Source License 1.1
|
|
24
|
+
|
|
25
|
+
Terms
|
|
26
|
+
|
|
27
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
28
|
+
works, redistribute, and make non-commercial use of the Licensed Work. The
|
|
29
|
+
Licensor may make an Additional Use Grant, above, permitting limited commercial
|
|
30
|
+
use.
|
|
31
|
+
|
|
32
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
33
|
+
available distribution of a specific version of the Licensed Work under this
|
|
34
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
35
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
36
|
+
above terminate.
|
|
37
|
+
|
|
38
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
39
|
+
currently in effect as described in this License, you must purchase a
|
|
40
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
41
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
42
|
+
|
|
43
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
44
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
45
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
46
|
+
for each version of the Licensed Work released by Licensor.
|
|
47
|
+
|
|
48
|
+
You must conspicuously display this License on each original or modified copy
|
|
49
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
50
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
51
|
+
License apply to your use of that work.
|
|
52
|
+
|
|
53
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
54
|
+
terminate your rights under this License for the current and all other
|
|
55
|
+
versions of the Licensed Work.
|
|
56
|
+
|
|
57
|
+
This License does not grant you any right in any trademark or logo of
|
|
58
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
59
|
+
Licensor as expressly required by this License).
|
|
60
|
+
|
|
61
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
62
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
63
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
64
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
65
|
+
TITLE.
|
package/ROADMAP.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
> 本文档描述 cursor-guard 从 V2 到 V7 的长期演进方向。
|
|
4
4
|
> 每一代向下兼容,低版本功能永远不废弃。
|
|
5
5
|
>
|
|
6
|
-
> **当前版本**:`V4.6.
|
|
7
|
-
> **文档状态**:`V2` ~ `V4.6.
|
|
6
|
+
> **当前版本**:`V4.6.1`
|
|
7
|
+
> **文档状态**:`V2` ~ `V4.6.1` 已完成交付(含 V5 intent/audit 基础),`V5` 主体规划中
|
|
8
8
|
|
|
9
9
|
## 阅读导航
|
|
10
10
|
|
|
@@ -466,6 +466,7 @@ V4 经过 4 轮系统性代码审查,修复了以下关键问题:
|
|
|
466
466
|
| V4.5.7 | **文件详情 Modal 修复 + Dashboard 端口复用**:见下方详细说明 | ✅ |
|
|
467
467
|
| V4.5.8 | **Dashboard 版本更新检测 + 一键重启**:见下方详细说明 | ✅ |
|
|
468
468
|
| V4.6.0 | **告警 UX 全面升级**:见下方详细说明 | ✅ |
|
|
469
|
+
| V4.6.1 | **告警历史弹窗化**:见下方详细说明 | ✅ |
|
|
469
470
|
|
|
470
471
|
#### V4.4.1 详细内容
|
|
471
472
|
|
|
@@ -698,6 +699,17 @@ V4 经过 4 轮系统性代码审查,修复了以下关键问题:
|
|
|
698
699
|
| 告警历史常驻入口 | `buildAlertHistoryHtml()` 提取为独立函数,在活跃告警和无告警两种状态下都渲染"历史 N 条"折叠按钮,告警激活时也能查看历史 |
|
|
699
700
|
| 告警历史 localStorage 持久化 | 新增 `loadAlertHistory()` / `saveAlertHistory()`,使用 `localStorage` key `cursorGuard_alertHistory` 持久化最近 20 条告警历史,刷新页面不丢失 |
|
|
700
701
|
|
|
702
|
+
#### V4.6.1 详细内容
|
|
703
|
+
|
|
704
|
+
**告警历史弹窗化**:
|
|
705
|
+
|
|
706
|
+
| 改动 | 说明 |
|
|
707
|
+
|------|------|
|
|
708
|
+
| 历史按钮改为弹窗触发 | "历史 N 条"按钮点击后打开全屏 Modal(复用 `file-modal`),不再内联折叠展开 |
|
|
709
|
+
| 历史表格化展示 | Modal 内以表格呈现所有历史告警:触发时间、详情、文件类型分布、文件列表按钮 |
|
|
710
|
+
| 嵌套文件详情 | 每条历史告警的"查看文件"按钮可再打开文件详情 Modal,查看具体变更文件列表 |
|
|
711
|
+
| 新增 i18n keys | `alert.col.detail` / `alert.col.breakdown` / `alert.col.files`(中英双语) |
|
|
712
|
+
|
|
701
713
|
#### V4.5.x 新增配置参考
|
|
702
714
|
|
|
703
715
|
| 字段 | 类型 | 默认值 | 引入版本 | 说明 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-guard",
|
|
3
|
-
"version": "4.6.
|
|
3
|
+
"version": "4.6.2",
|
|
4
4
|
"description": "Protects code from accidental AI overwrite or deletion in Cursor IDE — mandatory pre-write snapshots, review-before-apply, local Git safety net, and deterministic recovery. | 保护代码免受 Cursor AI 代理意外覆写或删除——强制写前快照、预览再执行、本地 Git 安全网、确定性恢复。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cursor",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"cross-platform"
|
|
15
15
|
],
|
|
16
16
|
"author": "zhangqiang8vipp",
|
|
17
|
-
"license": "
|
|
17
|
+
"license": "BUSL-1.1",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
20
|
"url": "https://github.com/zhangqiang8vipp/cursor-guard.git"
|
|
@@ -52,6 +52,9 @@ const I18N = {
|
|
|
52
52
|
'alert.col.file': 'File',
|
|
53
53
|
'alert.col.action': 'Action',
|
|
54
54
|
'alert.col.changes':'Changes',
|
|
55
|
+
'alert.col.detail': 'Detail',
|
|
56
|
+
'alert.col.breakdown': 'Breakdown',
|
|
57
|
+
'alert.col.files': 'Files',
|
|
55
58
|
'alert.action.modified': 'Modified',
|
|
56
59
|
'alert.action.added': 'Added',
|
|
57
60
|
'alert.action.deleted': 'Deleted',
|
|
@@ -276,6 +279,9 @@ const I18N = {
|
|
|
276
279
|
'alert.col.file': '文件',
|
|
277
280
|
'alert.col.action': '操作',
|
|
278
281
|
'alert.col.changes':'变化量',
|
|
282
|
+
'alert.col.detail': '详情',
|
|
283
|
+
'alert.col.breakdown': '文件类型',
|
|
284
|
+
'alert.col.files': '文件列表',
|
|
279
285
|
'alert.action.modified': '修改',
|
|
280
286
|
'alert.action.added': '新增',
|
|
281
287
|
'alert.action.deleted': '删除',
|
|
@@ -913,24 +919,55 @@ function alertFileBreakdown(files) {
|
|
|
913
919
|
function buildAlertHistoryHtml() {
|
|
914
920
|
if (state.alertHistory.length === 0) return '';
|
|
915
921
|
const count = state.alertHistory.length;
|
|
916
|
-
const rows = state.alertHistory.slice(-5).reverse().map(h => {
|
|
917
|
-
const breakdown = alertFileBreakdown(h.files);
|
|
918
|
-
return `<div class="alert-history-row text-sm text-muted">
|
|
919
|
-
<span class="alert-history-time">${esc(formatTime(h.timestamp))}</span>
|
|
920
|
-
<span>${t('alert.detail', { count: h.fileCount, window: h.windowSeconds, threshold: h.threshold })}</span>
|
|
921
|
-
${breakdown ? `<span class="alert-history-breakdown">${esc(breakdown)}</span>` : ''}
|
|
922
|
-
<span class="badge badge-expired">${t('alert.expired')}</span>
|
|
923
|
-
</div>`;
|
|
924
|
-
}).join('');
|
|
925
922
|
return `
|
|
926
923
|
<div class="alert-history-toggle-wrap">
|
|
927
|
-
<button class="alert-history-toggle-btn text-sm text-muted" data-alert-history-
|
|
928
|
-
</div>
|
|
929
|
-
<div class="alert-history alert-history-collapsed">
|
|
930
|
-
<div class="alert-history-label text-sm">${t('alert.history')}</div>${rows}
|
|
924
|
+
<button class="alert-history-toggle-btn text-sm text-muted" data-alert-history-modal>${t('alert.historyCount', { n: count })}</button>
|
|
931
925
|
</div>`;
|
|
932
926
|
}
|
|
933
927
|
|
|
928
|
+
function openAlertHistoryModal() {
|
|
929
|
+
const list = [...state.alertHistory].reverse();
|
|
930
|
+
if (list.length === 0) return;
|
|
931
|
+
$('#file-modal-title').textContent = t('alert.history');
|
|
932
|
+
const body = $('#file-modal-body');
|
|
933
|
+
|
|
934
|
+
const rows = list.map((h, i) => {
|
|
935
|
+
const breakdown = alertFileBreakdown(h.files);
|
|
936
|
+
const fileCount = Array.isArray(h.files) ? h.files.length : 0;
|
|
937
|
+
return `<tr>
|
|
938
|
+
<td class="text-mono">${esc(formatTime(h.timestamp))}</td>
|
|
939
|
+
<td>${t('alert.detail', { count: h.fileCount, window: h.windowSeconds, threshold: h.threshold })}</td>
|
|
940
|
+
<td>${breakdown ? esc(breakdown) : '-'}</td>
|
|
941
|
+
<td>${fileCount > 0 ? `<button class="modal-restore-btn" data-history-files="${i}">${t('alert.viewFiles', { n: fileCount })}</button>` : '-'}</td>
|
|
942
|
+
</tr>`;
|
|
943
|
+
}).join('');
|
|
944
|
+
|
|
945
|
+
body.innerHTML = `<table>
|
|
946
|
+
<thead><tr>
|
|
947
|
+
<th>${t('alert.triggered')}</th>
|
|
948
|
+
<th>${t('alert.col.detail')}</th>
|
|
949
|
+
<th>${t('alert.col.breakdown')}</th>
|
|
950
|
+
<th>${t('alert.col.files')}</th>
|
|
951
|
+
</tr></thead>
|
|
952
|
+
<tbody>${rows}</tbody>
|
|
953
|
+
</table>`;
|
|
954
|
+
|
|
955
|
+
body.addEventListener('click', (e) => {
|
|
956
|
+
const btn = e.target.closest('[data-history-files]');
|
|
957
|
+
if (btn) {
|
|
958
|
+
const idx = parseInt(btn.dataset.historyFiles);
|
|
959
|
+
const h = list[idx];
|
|
960
|
+
if (h?.files?.length > 0) {
|
|
961
|
+
const proj = state.pageData?.dashboard?.watcher?.path || '';
|
|
962
|
+
openFileModal(t('modal.alertFiles'), h.files, proj, '');
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
$('#file-modal-overlay').classList.add('active');
|
|
968
|
+
document.body.style.overflow = 'hidden';
|
|
969
|
+
}
|
|
970
|
+
|
|
934
971
|
function renderAlertCard(alerts) {
|
|
935
972
|
const el = $('#card-alert');
|
|
936
973
|
if (!alerts?.active) {
|
|
@@ -1573,13 +1610,11 @@ function setupEvents() {
|
|
|
1573
1610
|
if (backup) openRestoreDrawer(backup);
|
|
1574
1611
|
});
|
|
1575
1612
|
|
|
1576
|
-
// Alert history
|
|
1613
|
+
// Alert history modal + file modal (event delegation)
|
|
1577
1614
|
$('#card-alert').addEventListener('click', (e) => {
|
|
1578
|
-
const
|
|
1579
|
-
if (
|
|
1580
|
-
|
|
1581
|
-
const history = card?.querySelector('.alert-history');
|
|
1582
|
-
if (history) history.classList.toggle('alert-history-collapsed');
|
|
1615
|
+
const historyBtn = e.target.closest('[data-alert-history-modal]');
|
|
1616
|
+
if (historyBtn) {
|
|
1617
|
+
openAlertHistoryModal();
|
|
1583
1618
|
return;
|
|
1584
1619
|
}
|
|
1585
1620
|
const modalBtn = e.target.closest('[data-alert-files-modal]');
|