dsh-my-observability 0.1.0 → 0.1.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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  本文件记录 dsh-my-observability 的所有版本变更。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.1.2] - 2026-09-01
6
+
7
+ ### 变更
8
+
9
+ - fix(ui): 9 个插件未定义 token danger-primary 改用 error-primary(DSH 主题仅定义 business/error/success/warn)
10
+
11
+ ## [0.1.1] - 2026-08-28
12
+
13
+ ### 变更
14
+
15
+ - feat(ui): dsh-my-observability 时间轴翻新——类型图标/连线节点/前缀(issue #54)
16
+ - fix(security): 修复 CodeQL 告警——正则 ReDoS/随机数/命令注入(issue #5/#6/#7/#9/#10)
17
+ - refactor(shared): 抽取 dsh-shared 共享工具包,10 个插件迁移消除重复实现(issue #45)
18
+ - chore(deps): 升级 react 19 兼容性——13 个插件 peer 声明 ^18.2.0 || ^19.2.0(issue #49)
19
+ - style(format): 全仓 prettier 格式化(issue #44)
20
+
5
21
  ## [0.1.0] - 2026-08-28
6
22
 
7
23
  ### 新增
@@ -15,6 +31,7 @@
15
31
  - **测试**:`test/host-audit.mjs`(事件监听/会话隔离/重启恢复/上限/fence)、`test/host-git.mjs`(真实临时 git 仓库 status/diff/commit)、`test/host-review.mjs`(规则引擎 + AI 各路径)、`test/host-mutation.mjs`(变异补充)、Gherkin 验收(observability/git/review 3 个 feature 16 场景);覆盖率 行 95% / 分支 85%;变异测试 ≥70%。
16
32
 
17
33
  **真实环境验证**(独立端口 3081 隔离实例):
34
+
18
35
  - 侧边栏「轨迹回放」页签出现,时间轴展示真实 agent 行为(状态/模型流/工具调用);
19
36
  - 「Git 工具」页签:真实仓库状态/差异/类型化提交/提交前审查全流程可用;
20
37
  - 重启验证实例后审计事件恢复。
package/README.md CHANGED
@@ -15,12 +15,12 @@
15
15
 
16
16
  Server 端只读观察 DSH 生命周期事件并记录审计日志:
17
17
 
18
- | 事件 | 审计记录 | 说明 |
19
- |------|---------|------|
20
- | `agent/status` | `agent_status` | 状态变化 + 顶层/子代理标记 |
21
- | `llm/stream` | `llm_stream` | 流开始/结束/错误 + chunk/字符/耗时统计(同步包装流,透传全部 chunk) |
22
- | `tools/pre-execute` | `tool_call` | 工具名 + 参数键列表 + 主要文本参数截断摘要 |
23
- | `tools/execute` | `tool_result` | 工具名 + 成功/失败 + 耗时 |
18
+ | 事件 | 审计记录 | 说明 |
19
+ | ------------------- | -------------- | -------------------------------------------------------------------- |
20
+ | `agent/status` | `agent_status` | 状态变化 + 顶层/子代理标记 |
21
+ | `llm/stream` | `llm_stream` | 流开始/结束/错误 + chunk/字符/耗时统计(同步包装流,透传全部 chunk) |
22
+ | `tools/pre-execute` | `tool_call` | 工具名 + 参数键列表 + 主要文本参数截断摘要 |
23
+ | `tools/execute` | `tool_result` | 工具名 + 成功/失败 + 耗时 |
24
24
 
25
25
  - **会话隔离**:事件按会话分桶,切换会话互不串扰;
26
26
  - **重启恢复**:持久化到 `$DSH_HOME/observability/audit.json`(防抖 + 原子写),重启后完整恢复;
@@ -43,16 +43,16 @@ Server 端只读观察 DSH 生命周期事件并记录审计日志:
43
43
 
44
44
  「提交前审查」按钮对增量 diff 运行规则引擎(确定性、可测试):
45
45
 
46
- | 规则 | 严重级别 | 检查内容 |
47
- |------|---------|---------|
48
- | `secret-leak` | error | 密钥/凭据硬编码(password/api_key/secret/token) |
49
- | `conflict-marker` | error | 合并冲突标记残留(`<<<<<<<` / `=======` / `>>>>>>>`) |
50
- | `debug-statement` | warning | 调试残留(console.* / debugger / print 族) |
51
- | `large-diff` | warning | 单文件变更 > 300 行 |
52
- | `binary-file` | warning | 二进制文件变更 |
53
- | `todo-marker` | info | TODO/FIXME/HACK 标记 |
54
- | `trailing-space` | info | 行尾多余空格 |
55
- | `no-test-change` | info | 有源码变更但无测试变更 |
46
+ | 规则 | 严重级别 | 检查内容 |
47
+ | ----------------- | -------- | ----------------------------------------------------- |
48
+ | `secret-leak` | error | 密钥/凭据硬编码(password/api_key/secret/token) |
49
+ | `conflict-marker` | error | 合并冲突标记残留(`<<<<<<<` / `=======` / `>>>>>>>`) |
50
+ | `debug-statement` | warning | 调试残留(console.* / debugger / print 族) |
51
+ | `large-diff` | warning | 单文件变更 > 300 行 |
52
+ | `binary-file` | warning | 二进制文件变更 |
53
+ | `todo-marker` | info | TODO/FIXME/HACK 标记 |
54
+ | `trailing-space` | info | 行尾多余空格 |
55
+ | `no-test-change` | info | 有源码变更但无测试变更 |
56
56
 
57
57
  - **可选 AI 审查**:配置 `aiReview`(默认开)且 agents 服务可用时,创建独立审查 agent 阅读 diff 输出总评(verdict/summary/topIssues);超时/失败/解析失败自动降级,规则引擎结果不受影响(AI 是增强,不是门禁)。
58
58
 
@@ -82,17 +82,17 @@ dsh plugin --profile web add link:<仓库路径>/plugins/dsh-my-observability
82
82
  - insert:
83
83
  - id: observability
84
84
  name: 'dsh-my-observability'
85
- - config: # 传给 apply(ctx, config)
86
- aiReview: true # 增量 diff 审查启用 AI 增强(默认 true;agents 服务不可用时自动降级)
87
- aiTimeoutMs: 60000 # AI 审查超时(毫秒,默认 60000)
85
+ - config: # 传给 apply(ctx, config)
86
+ aiReview: true # 增量 diff 审查启用 AI 增强(默认 true;agents 服务不可用时自动降级)
87
+ aiTimeoutMs: 60000 # AI 审查超时(毫秒,默认 60000)
88
88
  ```
89
89
 
90
90
  ## 依赖
91
91
 
92
- | 依赖 | 用途 | 可选 |
93
- |------|------|------|
94
- | `cordis` | 插件运行时 | 是(宿主提供) |
95
- | `react` | client 端组件 | 是(宿主提供) |
92
+ | 依赖 | 用途 | 可选 |
93
+ | -------- | ------------- | -------------- |
94
+ | `cordis` | 插件运行时 | 是(宿主提供) |
95
+ | `react` | client 端组件 | 是(宿主提供) |
96
96
 
97
97
  ## 限制与说明
98
98
 
package/lib/ai.js CHANGED
@@ -10,6 +10,8 @@
10
10
  * 参考 dsh-task-reliability 的校验 agent 模式(agents.create + whenIdle
11
11
  * 超时 + dispose)。
12
12
  */
13
+ import { randomUUID } from 'node:crypto'
14
+ import { withTimeout, userMessage } from 'dsh-shared'
13
15
  import { REVIEW_TIMEOUT_MS } from './constants.js'
14
16
 
15
17
  /** diff 送入 prompt 的最大长度(截断防 token 爆炸)。 */
@@ -19,7 +21,9 @@ const AI_DIFF_MAX = 8000
19
21
  export async function runAiReview(ctx, diffText, report, timeoutMs = REVIEW_TIMEOUT_MS) {
20
22
  const agents = agentsServiceOf(ctx)
21
23
  if (agents === undefined) return { enabled: true, failed: true, note: 'agents service unavailable' }
22
- const sessionId = `obs-review-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
24
+ // CodeQL js/insecure-randomness 修复:sessionId 随机段改用 crypto.randomUUID()
25
+ // (密码学安全随机,无需种子),保持 `obs-review-<ts>-<6位hex>` 格式兼容。
26
+ const sessionId = `obs-review-${Date.now()}-${randomUUID().slice(0, 6)}`
23
27
  let handle
24
28
  try {
25
29
  handle = await agents.create({
@@ -33,7 +37,11 @@ export async function runAiReview(ctx, diffText, report, timeoutMs = REVIEW_TIME
33
37
  if (parsed === undefined) return { enabled: true, failed: true, note: 'AI 结论解析失败' }
34
38
  return { enabled: true, ...parsed }
35
39
  } catch (error) {
36
- return { enabled: true, failed: true, note: error instanceof Error ? error.message : String(error) }
40
+ return {
41
+ enabled: true,
42
+ failed: true,
43
+ note: error instanceof Error ? error.message : String(error),
44
+ }
37
45
  } finally {
38
46
  await disposeHandle(handle)
39
47
  }
@@ -119,27 +127,6 @@ function extractJsonBlock(text) {
119
127
  return match !== null ? match[1].trim() : undefined
120
128
  }
121
129
 
122
- // ── helpers(与 dsh-task-reliability 同契约的轻量实现)────────────────────
123
-
124
- function withTimeout(promise, ms) {
125
- return new Promise((resolve) => {
126
- const timer = setTimeout(() => resolve(undefined), ms)
127
- Promise.resolve(promise).then(
128
- (value) => { clearTimeout(timer); resolve(value) },
129
- () => { clearTimeout(timer); resolve(undefined) },
130
- )
131
- })
132
- }
133
-
134
- function userMessage(text) {
135
- return {
136
- id: `msg-${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`,
137
- role: 'user',
138
- content: [{ type: 'text', text }],
139
- source: { kind: 'user' },
140
- }
141
- }
142
-
143
130
  function lastAssistantText(session) {
144
131
  try {
145
132
  const events = session?.events
@@ -159,7 +146,9 @@ function assistantTextOf(event) {
159
146
  const blocks = event.data?.message?.content
160
147
  if (!Array.isArray(blocks)) return ''
161
148
  return blocks
162
- .filter((block) => block !== null && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string')
149
+ .filter(
150
+ (block) => block !== null && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string',
151
+ )
163
152
  .map((block) => block.text)
164
153
  .join('\n')
165
154
  }
package/lib/audit.js CHANGED
@@ -80,9 +80,7 @@ function agentTypeOf(agent) {
80
80
  if (header === undefined || header === null) return 'unknown'
81
81
  if (header.origin === 'subagent') return 'subagent'
82
82
  if (typeof header.delegationDepth === 'number' && header.delegationDepth > 0) return 'subagent'
83
- return typeof agent.options?.subagentDepth === 'number' && agent.options.subagentDepth > 0
84
- ? 'subagent'
85
- : 'top'
83
+ return typeof agent.options?.subagentDepth === 'number' && agent.options.subagentDepth > 0 ? 'subagent' : 'top'
86
84
  }
87
85
 
88
86
  /** 工具结果 ok 判定:非对象视为成功;error 字段非空视为失败。 */
@@ -131,9 +129,17 @@ function wrapStream(sessionId, stream, record) {
131
129
  }
132
130
  yield chunk
133
131
  }
134
- record({ type: 'llm_stream', sessionId, data: streamSummary('end', startedAt, chunks, chars) })
132
+ record({
133
+ type: 'llm_stream',
134
+ sessionId,
135
+ data: streamSummary('end', startedAt, chunks, chars),
136
+ })
135
137
  } catch (error) {
136
- record({ type: 'llm_stream', sessionId, data: streamSummary('error', startedAt, chunks, chars, error) })
138
+ record({
139
+ type: 'llm_stream',
140
+ sessionId,
141
+ data: streamSummary('error', startedAt, chunks, chars, error),
142
+ })
137
143
  throw error
138
144
  }
139
145
  })()