dsh-recall-plugin 2.0.0 → 2.1.1
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 +63 -29
- package/README.en.md +2 -1
- package/README.md +2 -1
- package/lib/client.js +1459 -1479
- package/lib/diagnostics.js +59 -0
- package/lib/errors.js +73 -0
- package/lib/index.js +426 -1129
- package/lib/routes-core.js +162 -0
- package/lib/routes-manage.js +541 -0
- package/lib/scripts.posix.js +195 -27
- package/lib/scripts.pwsh.js +194 -33
- package/lib/session-info.js +71 -0
- package/lib/snapshots.js +243 -39
- package/lib/store.js +157 -19
- package/package.json +65 -61
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-recall-plugin — 环境错误诊断(纯函数模块,无 ctx 依赖)
|
|
3
|
+
*
|
|
4
|
+
* 环境类失败(锁冲突/磁盘满/权限等)的「识别 → 可行动提示」分层,仿
|
|
5
|
+
* errors.js 的机器码与人文案分层:kind 供机器分流(recordError 富集、
|
|
6
|
+
* status API、未来设置页过滤),提示文本在 Host 侧生成——client 的 toast
|
|
7
|
+
* 运行时读 res.error('快照失败:' + String(res.error).slice(0, 140)),
|
|
8
|
+
* Host 换 error 字段文本即生效,client 零改动。
|
|
9
|
+
*
|
|
10
|
+
* 文案硬约束( motivated by issue #11:锁路径就 100+ 字符,嵌进提示必被
|
|
11
|
+
* 140 截断出残句):提示不嵌原始路径,目标 ≤120 字符、硬上限 140;完整
|
|
12
|
+
* 原文(含路径)由设置页「最近错误」承载。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// 分类模式表,按根因优先级排列(同一文本命中多类时先命中者胜——
|
|
16
|
+
// 如 `Unable to create '…lock': No space left on device` 同时命中 lock 与
|
|
17
|
+
// space,磁盘满是根因,space 必须排在 lock 前面)。模式为不区分大小写的
|
|
18
|
+
// 正则,覆盖 git 两平台措辞(POSIX `command not found` / win32 `not
|
|
19
|
+
// recognized`)与常见 errno 文本。
|
|
20
|
+
const ENV_PATTERNS = [
|
|
21
|
+
['git', [/command not found/i, /not recognized/i, /git: not found/i, /is not a git command/i]],
|
|
22
|
+
['space', [/no space left on device/i, /disk quota exceeded/i, /enospc/i]],
|
|
23
|
+
['permission', [/permission denied/i, /operation not permitted/i, /not permitted/i, /access is denied/i]],
|
|
24
|
+
['lock', [/could not lock .*file exists/i, /unable to create .*\.lock/i, /fatal: cannot lock/i]],
|
|
25
|
+
['mkdir', [/fatal: cannot mkdir .*file exists/i, /mkdir: cannot create directory/i]],
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
// kind → 可行动中文提示(buildFeedbackError 与 status 端点 hint 共用同一
|
|
29
|
+
// 张表,保证 toast 与设置页看到同一套文案)。值都是静态短句,不带路径。
|
|
30
|
+
export const ENV_HINTS = {
|
|
31
|
+
git: '未检测到 git CLI 或版本过旧:请安装或升级 git,完成后自动恢复',
|
|
32
|
+
space: '磁盘空间已满,快照写入失败:清理磁盘空间后自动恢复',
|
|
33
|
+
permission: '快照目录无写入权限:请检查目录权限后重试',
|
|
34
|
+
lock: '疑似多个 DSH 实例并发使用同一快照库:请确认只启动了一个;确认后仍失败时,按「设置 · 插件配置 · 最近错误」中的路径删除锁文件',
|
|
35
|
+
mkdir: '快照存储目录被同名文件占用:处理后自动恢复',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 环境错误分类:命中返回 kind,未命中返回 null(未识别错误保现状回落
|
|
39
|
+
// 原文,误判只影响 toast 文案不影响功能)。git > space > permission >
|
|
40
|
+
// lock > mkdir 的表序即根因优先级,勿按字母序重排。
|
|
41
|
+
export function classifyEnvError(text) {
|
|
42
|
+
const s = String(text || '')
|
|
43
|
+
for (const [kind, patterns] of ENV_PATTERNS) {
|
|
44
|
+
for (const p of patterns) {
|
|
45
|
+
if (p.test(s)) return kind
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 把原始错误文本转成 snapFeedback 的失败条目字段:命中 → error 为提示
|
|
52
|
+
// 文案(client toast 直显);未命中 → error 为原文截断(保 issue #7 现状)、
|
|
53
|
+
// kind 标记 unknown。截断统一收在这里,调用方不再各写一遍 slice。
|
|
54
|
+
export function buildFeedbackError(raw) {
|
|
55
|
+
const text = String(raw || '')
|
|
56
|
+
const kind = classifyEnvError(text)
|
|
57
|
+
if (!kind) return { error: text.slice(0, 300), kind: 'unknown' }
|
|
58
|
+
return { error: ENV_HINTS[kind], kind }
|
|
59
|
+
}
|
package/lib/errors.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-recall-plugin — 错误码单一事实源(H3)
|
|
3
|
+
*
|
|
4
|
+
* 端点响应 code 字段的常量表:此前 code 字符串(STALE/NO_SNAPSHOT/...)
|
|
5
|
+
* 散布各 handler 内联,改一处漏一处。这里集中导出、值保持不变(code 是
|
|
6
|
+
* client 已消费的线上契约,不改值只收拢),每条注释说明触发条件与 client
|
|
7
|
+
* 预期行为。client 侧按 code 映射展示文案(见 client.js CODE_TEXT),未
|
|
8
|
+
* 命中回退 host 返回的 message——机器码与人文案分层,为将来 locale 留口
|
|
9
|
+
* (当前插件单语,不预建 i18n 抽象)。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// 预览后项目文件变化(P0-3 STALE total 比对失败)——client 自动重新预览
|
|
13
|
+
export const RECALL_STALE = 'STALE'
|
|
14
|
+
// 目标消息没有可用项目快照(未捕获/已删除)
|
|
15
|
+
export const RECALL_NO_SNAPSHOT = 'NO_SNAPSHOT'
|
|
16
|
+
// 快照存储不可用(store 未建/已失配)
|
|
17
|
+
export const RECALL_NO_STORE = 'NO_STORE'
|
|
18
|
+
// 目标工作区 agent 运行中(P0-1 保护,preview/execute 双处拒绝)
|
|
19
|
+
export const RECALL_AGENT_BUSY = 'AGENT_BUSY'
|
|
20
|
+
// 回退失败(H1)——可能已自动救援恢复;message 携带救援结果
|
|
21
|
+
export const RECALL_ROLLBACK_FAILED = 'ROLLBACK_FAILED'
|
|
22
|
+
// 排除配置写入路径不在已知白名单内(防「借 API 写任意文件」)
|
|
23
|
+
export const RECALL_UNKNOWN_PATH = 'UNKNOWN_PATH'
|
|
24
|
+
// 配置字段类型/取值非法
|
|
25
|
+
export const RECALL_BAD_TYPE = 'BAD_TYPE'
|
|
26
|
+
// 配置补丁为空(无可写字段)
|
|
27
|
+
export const RECALL_EMPTY_PATCH = 'EMPTY_PATCH'
|
|
28
|
+
// settings 服务未组装(非 web 部署 / 未挂载)
|
|
29
|
+
export const RECALL_SETTINGS_UNAVAILABLE = 'SETTINGS_UNAVAILABLE'
|
|
30
|
+
// settings.update 写入被拒
|
|
31
|
+
export const RECALL_SETTINGS_WRITE_FAILED = 'SETTINGS_WRITE_FAILED'
|
|
32
|
+
// 请求体超过 1MB 上限(errBody 统一映射)
|
|
33
|
+
export const RECALL_BODY_TOO_LARGE = 'BODY_TOO_LARGE'
|
|
34
|
+
// 系统异常/未分类错误兜底(errBody 统一映射)
|
|
35
|
+
export const RECALL_ERROR = 'ERROR'
|
|
36
|
+
// 无法解析当前工作区(manage usage/delete)
|
|
37
|
+
export const RECALL_NO_ROOT = 'NO_ROOT'
|
|
38
|
+
// 缺少会话 ID(manage delete scope=session)
|
|
39
|
+
export const RECALL_NO_SESSION = 'NO_SESSION'
|
|
40
|
+
// 管理操作部分完成(deleteAll 有 store 失败)
|
|
41
|
+
export const RECALL_PARTIAL_DELETE = 'PARTIAL_DELETE'
|
|
42
|
+
// 未知管理操作(manage 端点 op 未识别)
|
|
43
|
+
export const RECALL_UNKNOWN_OP = 'UNKNOWN_OP'
|
|
44
|
+
// 未知 API 端点(webServer 路由 404)
|
|
45
|
+
export const RECALL_UNKNOWN_ENDPOINT = 'UNKNOWN_ENDPOINT'
|
|
46
|
+
|
|
47
|
+
// 语义锚点:H2 的索引损坏经 status 端点 errors 通道暴露(recordError 文本
|
|
48
|
+
// 前缀 'recall index corrupt'),不作为端点 code 返回。保留单一命名供
|
|
49
|
+
// 一致性扫描与未来若要升级为端点 code 时复用,避免「损坏」这一事实在
|
|
50
|
+
// 代码里无归属。
|
|
51
|
+
export const RECALL_INDEX_CORRUPT = 'INDEX_CORRUPT'
|
|
52
|
+
|
|
53
|
+
// 全量常量集合:供单测做「端点返回的 code 都在表内」的一致性扫描
|
|
54
|
+
export const ALL_CODES = Object.freeze([
|
|
55
|
+
RECALL_STALE,
|
|
56
|
+
RECALL_NO_SNAPSHOT,
|
|
57
|
+
RECALL_NO_STORE,
|
|
58
|
+
RECALL_AGENT_BUSY,
|
|
59
|
+
RECALL_ROLLBACK_FAILED,
|
|
60
|
+
RECALL_UNKNOWN_PATH,
|
|
61
|
+
RECALL_BAD_TYPE,
|
|
62
|
+
RECALL_EMPTY_PATCH,
|
|
63
|
+
RECALL_SETTINGS_UNAVAILABLE,
|
|
64
|
+
RECALL_SETTINGS_WRITE_FAILED,
|
|
65
|
+
RECALL_BODY_TOO_LARGE,
|
|
66
|
+
RECALL_ERROR,
|
|
67
|
+
RECALL_NO_ROOT,
|
|
68
|
+
RECALL_NO_SESSION,
|
|
69
|
+
RECALL_PARTIAL_DELETE,
|
|
70
|
+
RECALL_UNKNOWN_OP,
|
|
71
|
+
RECALL_UNKNOWN_ENDPOINT,
|
|
72
|
+
RECALL_INDEX_CORRUPT,
|
|
73
|
+
])
|