dsh-my-observability 0.3.0 → 0.3.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 +9 -79
- package/README.md +18 -76
- package/lib/ai.js +123 -99
- package/lib/audit-view.js +173 -172
- package/lib/audit.js +175 -166
- package/lib/client.js +1024 -1278
- package/lib/client.src.js +55 -29
- package/lib/constants.js +4 -4
- package/lib/diff.js +61 -56
- package/lib/git.js +97 -98
- package/lib/index.js +46 -47
- package/lib/parts/native-tabs.js +45 -0
- package/lib/resource-monitor.js +82 -134
- package/lib/resource-rules.js +9 -99
- package/lib/review.js +68 -77
- package/lib/routes.js +223 -200
- package/lib/store-persist.js +114 -111
- package/lib/store.js +215 -212
- package/package.json +15 -7
- package/lib/parts/git.js +0 -298
- package/lib/parts/i18n.js +0 -104
- package/lib/parts/replay-ext.js +0 -396
- package/lib/parts/replay.js +0 -386
- package/lib/parts/resource.js +0 -80
- package/lib/parts/styles.js +0 -156
package/lib/store.js
CHANGED
|
@@ -13,291 +13,294 @@
|
|
|
13
13
|
*
|
|
14
14
|
* 磁盘 I/O 与格式规整在 store-persist.js。
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
writeSnapshot,
|
|
21
|
-
appendLines,
|
|
22
|
-
removeLegacyAfterMigration,
|
|
23
|
-
normalizeLoaded,
|
|
24
|
-
MAX_EVENTS_PER_SESSION,
|
|
25
|
-
MAX_TOTAL_EVENTS,
|
|
26
|
-
COMPACT_LINES,
|
|
27
|
-
} from './store-persist.js'
|
|
28
|
-
|
|
29
|
-
const FLUSH_INTERVAL_MS = 500
|
|
30
|
-
const COMPACT_DEBOUNCE_MS = 300
|
|
31
|
-
const PREFIX = '[dsh-my-observability]'
|
|
32
|
-
|
|
16
|
+
import { jsonlFile, legacyFile, loadPersisted, writeSnapshot, appendLines, removeLegacyAfterMigration, normalizeLoaded, MAX_EVENTS_PER_SESSION, MAX_TOTAL_EVENTS, COMPACT_LINES, } from './store-persist.js';
|
|
17
|
+
const FLUSH_INTERVAL_MS = 500;
|
|
18
|
+
const COMPACT_DEBOUNCE_MS = 300;
|
|
19
|
+
const PREFIX = '[dsh-my-observability]';
|
|
33
20
|
/** 初始空状态。 */
|
|
34
21
|
function createState() {
|
|
35
|
-
|
|
22
|
+
return { version: 1, bySession: {} };
|
|
36
23
|
}
|
|
37
|
-
|
|
38
24
|
/** 创建审计存储:{ record, events, sessions, count, dispose }。
|
|
39
25
|
* record 在状态加载完成前缓冲(不丢事件);dispose 冲刷未落盘数据。 */
|
|
40
26
|
export function createStore(ctx) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
27
|
+
const store = { state: createState() };
|
|
28
|
+
let markReady = () => { };
|
|
29
|
+
const readyPromise = new Promise((resolve) => {
|
|
30
|
+
markReady = resolve;
|
|
31
|
+
});
|
|
32
|
+
const handle = {
|
|
33
|
+
ctx,
|
|
34
|
+
file: jsonlFile(),
|
|
35
|
+
legacy: legacyFile(),
|
|
36
|
+
store,
|
|
37
|
+
pending: [],
|
|
38
|
+
ready: false,
|
|
39
|
+
readyPromise,
|
|
40
|
+
markReady,
|
|
41
|
+
lineQueue: [],
|
|
42
|
+
queuedLines: 0,
|
|
43
|
+
total: 0,
|
|
44
|
+
flushTimer: null,
|
|
45
|
+
compactTimer: null,
|
|
46
|
+
compacting: false,
|
|
47
|
+
migrated: false,
|
|
48
|
+
persistEnabled: true,
|
|
49
|
+
dirtyChain: Promise.resolve(),
|
|
50
|
+
};
|
|
51
|
+
store.record = (event) => record(handle, event);
|
|
52
|
+
store.events = (sessionId, type, limit) => eventsOf(handle, sessionId, type, limit);
|
|
53
|
+
store.sessions = () => sessionsOf(handle);
|
|
54
|
+
store.count = () => countOf(handle);
|
|
55
|
+
store.setPersistEnabled = (enabled) => setPersistEnabled(handle, enabled);
|
|
56
|
+
store.whenReady = () => handle.readyPromise;
|
|
57
|
+
store.dispose = () => dispose(handle);
|
|
58
|
+
void loadPersisted(handle.file, handle.legacy)
|
|
59
|
+
.then((result) => onLoaded(handle, result))
|
|
60
|
+
// 兜底:加载链上的任何异常都不能让 whenReady 永不 resolve——那会让所有
|
|
61
|
+
// API 请求永久挂起(比「读到空 state」严重得多)。loadPersisted 自身已
|
|
62
|
+
// 逐层 catch,这里是防未来改动引入 reject 的护栏。
|
|
63
|
+
.catch(() => onLoaded(handle, { state: createState(), migrated: false, lines: 0 }));
|
|
64
|
+
return store;
|
|
67
65
|
}
|
|
68
|
-
|
|
69
66
|
/** 追加一条审计事件(自动分配 id/时间戳);未就绪时缓冲。 */
|
|
70
67
|
function record(handle, event) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
const item = { id: nextId(handle), time: Date.now(), ...event };
|
|
69
|
+
if (!handle.ready) {
|
|
70
|
+
handle.pending.push(item);
|
|
71
|
+
return item;
|
|
72
|
+
}
|
|
73
|
+
enqueueRecord(handle, item);
|
|
74
|
+
scheduleFlush(handle);
|
|
75
|
+
return item;
|
|
79
76
|
}
|
|
80
|
-
|
|
81
77
|
/** 事件入内存桶 + 排队待落盘行(回放与运行时共用,保证回放也落盘)。
|
|
82
78
|
* 降级(persistEnabled=false)时事件照常入内存桶(有 FIFO 上限保护),
|
|
83
79
|
* 但不排队落盘行、不触发 flush——写盘完全停止,恢复时全量快照补齐。 */
|
|
84
80
|
function enqueueRecord(handle, item) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
81
|
+
appendEvent(handle, item);
|
|
82
|
+
if (!handle.persistEnabled)
|
|
83
|
+
return;
|
|
84
|
+
handle.lineQueue.push(JSON.stringify(item));
|
|
85
|
+
handle.queuedLines += 1;
|
|
86
|
+
if (handle.queuedLines >= COMPACT_LINES)
|
|
87
|
+
scheduleCompact(handle);
|
|
90
88
|
}
|
|
91
|
-
|
|
92
89
|
/**
|
|
93
90
|
* 落盘开关(资源看门狗降级用):
|
|
94
91
|
* - false(降级):停止落盘——清空待写行(内存 state 仍在,不丢),停 flush。
|
|
95
92
|
* - true(恢复):立即 compact 全量快照(内存=真相,补齐降级窗口事件),恢复增量。
|
|
96
93
|
*/
|
|
97
94
|
function setPersistEnabled(handle, enabled) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
95
|
+
if (handle.persistEnabled === enabled)
|
|
96
|
+
return;
|
|
97
|
+
handle.persistEnabled = enabled;
|
|
98
|
+
if (handle.flushTimer !== null) {
|
|
99
|
+
clearTimeout(handle.flushTimer);
|
|
100
|
+
handle.flushTimer = null;
|
|
101
|
+
}
|
|
102
|
+
handle.lineQueue = [];
|
|
103
|
+
handle.queuedLines = 0;
|
|
104
|
+
if (enabled)
|
|
105
|
+
compactNow(handle);
|
|
107
106
|
}
|
|
108
|
-
|
|
109
107
|
/** 全部会话事件:合并各会话并按时间正序(sessionId='*')。 */
|
|
110
108
|
function eventsAllOf(handle, type, limit) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
109
|
+
let all = [];
|
|
110
|
+
for (const bucket of Object.values(handle.store.state.bySession)) {
|
|
111
|
+
if (Array.isArray(bucket.events))
|
|
112
|
+
all.push(...bucket.events);
|
|
113
|
+
}
|
|
114
|
+
all.sort((a, b) => a.time - b.time);
|
|
115
|
+
if (type !== undefined && type !== null && type !== '') {
|
|
116
|
+
all = all.filter((event) => event.type === type);
|
|
117
|
+
}
|
|
118
|
+
const capped = typeof limit === 'number' && limit > 0 ? all.slice(-limit) : all;
|
|
119
|
+
return capped.map((event) => ({ ...event }));
|
|
121
120
|
}
|
|
122
|
-
|
|
123
121
|
/** 查询会话事件(正序时间轴;type 可选过滤;limit 限制条数)。
|
|
124
122
|
* sessionId === '*' 表示全部会话;空字符串('')保持既有语义返回空数组。 */
|
|
125
123
|
function eventsOf(handle, sessionId, type, limit) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
list =
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
124
|
+
if (sessionId === '*')
|
|
125
|
+
return eventsAllOf(handle, type, limit);
|
|
126
|
+
const bucket = handle.store.state.bySession[sessionId]?.events;
|
|
127
|
+
if (!Array.isArray(bucket))
|
|
128
|
+
return [];
|
|
129
|
+
let list = bucket;
|
|
130
|
+
if (type !== undefined && type !== null && type !== '') {
|
|
131
|
+
list = list.filter((event) => event.type === type);
|
|
132
|
+
}
|
|
133
|
+
const capped = typeof limit === 'number' && limit > 0 ? list.slice(-limit) : list;
|
|
134
|
+
return capped.map((event) => ({ ...event }));
|
|
135
135
|
}
|
|
136
|
-
|
|
137
136
|
/** 有审计事件的会话列表(按最后活动时间倒序,含事件数与可读标题)。 */
|
|
138
137
|
function sessionsOf(handle) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
138
|
+
const entries = Object.entries(handle.store.state.bySession);
|
|
139
|
+
const list = entries
|
|
140
|
+
.map(([sessionId, bucket]) => ({
|
|
141
|
+
sessionId,
|
|
142
|
+
count: bucket.events.length,
|
|
143
|
+
lastTime: bucket.events.length > 0 ? bucket.events[bucket.events.length - 1].time : 0,
|
|
144
|
+
title: sessionTitleOf(bucket),
|
|
146
145
|
}))
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
.filter((entry) => entry.count > 0);
|
|
147
|
+
list.sort((a, b) => b.lastTime - a.lastTime);
|
|
148
|
+
return list;
|
|
150
149
|
}
|
|
151
|
-
|
|
152
150
|
/** 会话标题:最早一条 user_message 事件的文本(无则空串,面板回退 UUID 短显)。 */
|
|
153
151
|
function sessionTitleOf(bucket) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
152
|
+
for (const event of bucket.events) {
|
|
153
|
+
const text = event.data?.text;
|
|
154
|
+
if (event.type === 'user_message' && typeof text === 'string' && text !== '') {
|
|
155
|
+
return text;
|
|
156
|
+
}
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
return ''
|
|
158
|
+
return '';
|
|
160
159
|
}
|
|
161
|
-
|
|
162
160
|
/** 全部会话事件总数(O(1) 计数)。 */
|
|
163
161
|
function countOf(handle) {
|
|
164
|
-
|
|
162
|
+
return handle.total;
|
|
165
163
|
}
|
|
166
|
-
|
|
167
164
|
/** 事件自增 id(跨会话单调)。 */
|
|
168
165
|
function nextId(handle) {
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
handle.seq = (handle.seq ?? 0) + 1;
|
|
167
|
+
return handle.seq;
|
|
171
168
|
}
|
|
172
|
-
|
|
173
169
|
/** 追加事件到会话桶:FIFO 淘汰 + 全局上限轮转淘汰(维护 O(1) 计数)。 */
|
|
174
170
|
function appendEvent(handle, event) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
171
|
+
const state = handle.store.state;
|
|
172
|
+
const bucket = state.bySession[event.sessionId] ?? (state.bySession[event.sessionId] = { events: [] });
|
|
173
|
+
bucket.events.push(event);
|
|
174
|
+
handle.total += 1;
|
|
175
|
+
if (bucket.events.length > MAX_EVENTS_PER_SESSION) {
|
|
176
|
+
const removed = bucket.events.splice(0, bucket.events.length - MAX_EVENTS_PER_SESSION).length;
|
|
177
|
+
handle.total -= removed;
|
|
178
|
+
}
|
|
179
|
+
if (handle.total > MAX_TOTAL_EVENTS)
|
|
180
|
+
evictOldest(handle);
|
|
184
181
|
}
|
|
185
|
-
|
|
186
182
|
/** 全局超限:从最早活动的会话整桶淘汰,直到回到上限内。 */
|
|
187
183
|
function evictOldest(handle) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
184
|
+
const state = handle.store.state;
|
|
185
|
+
const sessions = Object.entries(state.bySession)
|
|
186
|
+
.filter(([, bucket]) => bucket.events.length > 0)
|
|
187
|
+
.sort((a, b) => firstTimeOf(a[1]) - firstTimeOf(b[1]));
|
|
188
|
+
for (const [sessionId, bucket] of sessions) {
|
|
189
|
+
if (handle.total <= MAX_TOTAL_EVENTS)
|
|
190
|
+
break;
|
|
191
|
+
handle.total -= bucket.events.length;
|
|
192
|
+
delete state.bySession[sessionId];
|
|
193
|
+
}
|
|
197
194
|
}
|
|
198
|
-
|
|
199
195
|
function firstTimeOf(bucket) {
|
|
200
|
-
|
|
196
|
+
return bucket.events.length > 0 ? bucket.events[0].time : 0;
|
|
201
197
|
}
|
|
202
|
-
|
|
203
198
|
/** 状态加载完成:合并本进程已产生的事件(防 dispose 回放被覆盖)+ 回放缓冲 + 迁移/紧凑调度。 */
|
|
204
199
|
function onLoaded(handle, result) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
200
|
+
const parsed = result.state;
|
|
201
|
+
mergeCurrentEvents(handle.store.state, parsed);
|
|
202
|
+
const normalized = normalizeLoaded(parsed.bySession);
|
|
203
|
+
handle.store.state = normalized;
|
|
204
|
+
handle.total = countOfState(normalized);
|
|
205
|
+
handle.ready = true;
|
|
206
|
+
const pending = handle.pending.splice(0);
|
|
207
|
+
for (const item of pending)
|
|
208
|
+
enqueueRecord(handle, item);
|
|
209
|
+
if (pending.length > 0)
|
|
210
|
+
scheduleFlush(handle);
|
|
211
|
+
handle.migrated = result.migrated;
|
|
212
|
+
if (result.migrated || result.lines >= COMPACT_LINES)
|
|
213
|
+
scheduleCompact(handle);
|
|
214
|
+
// markReady 放在合并/回放**之后**:whenReady 返回即保证缓冲事件已并入
|
|
215
|
+
// state,查询结果与「加载耗时」无关(不再需要调用方猜 sleep 时长)。
|
|
216
|
+
handle.markReady();
|
|
216
217
|
}
|
|
217
|
-
|
|
218
218
|
/** 把 load 完成前(或 dispose 回放时)已进入内存的事件合并进加载状态:后到的事件追加于桶尾。 */
|
|
219
219
|
function mergeCurrentEvents(current, parsed) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
220
|
+
for (const [sessionId, bucket] of Object.entries(current.bySession ?? {})) {
|
|
221
|
+
if (!Array.isArray(bucket.events) || bucket.events.length === 0)
|
|
222
|
+
continue;
|
|
223
|
+
const target = parsed.bySession[sessionId] ?? (parsed.bySession[sessionId] = { events: [] });
|
|
224
|
+
target.events.push(...bucket.events);
|
|
225
|
+
}
|
|
225
226
|
}
|
|
226
|
-
|
|
227
227
|
function countOfState(state) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
228
|
+
let total = 0;
|
|
229
|
+
for (const bucket of Object.values(state.bySession ?? {})) {
|
|
230
|
+
if (Array.isArray(bucket?.events))
|
|
231
|
+
total += bucket.events.length;
|
|
232
|
+
}
|
|
233
|
+
return total;
|
|
233
234
|
}
|
|
234
|
-
|
|
235
235
|
/** 防抖批量追加落盘(一次 append 全部待写行)。 */
|
|
236
236
|
function scheduleFlush(handle) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
handle.flushTimer =
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
if (handle.flushTimer !== null)
|
|
238
|
+
return;
|
|
239
|
+
handle.flushTimer = setTimeout(() => {
|
|
240
|
+
handle.flushTimer = null;
|
|
241
|
+
flushNow(handle);
|
|
242
|
+
}, FLUSH_INTERVAL_MS);
|
|
242
243
|
}
|
|
243
|
-
|
|
244
244
|
function flushNow(handle) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
if (handle.lineQueue.length === 0 || !handle.persistEnabled)
|
|
246
|
+
return;
|
|
247
|
+
const text = `${handle.lineQueue.join('\n')}\n`;
|
|
248
|
+
handle.lineQueue = [];
|
|
249
|
+
handle.dirtyChain = handle.dirtyChain.then(() => appendLines(handle.file, text, handle.ctx.logger, PREFIX));
|
|
249
250
|
}
|
|
250
|
-
|
|
251
251
|
/** 防抖紧凑调度(合并短时间多次触发)。 */
|
|
252
252
|
function scheduleCompact(handle) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
handle.compactTimer =
|
|
256
|
-
|
|
257
|
-
|
|
253
|
+
if (handle.compactTimer !== null || handle.compacting)
|
|
254
|
+
return;
|
|
255
|
+
handle.compactTimer = setTimeout(() => {
|
|
256
|
+
handle.compactTimer = null;
|
|
257
|
+
compactNow(handle);
|
|
258
|
+
}, COMPACT_DEBOUNCE_MS);
|
|
258
259
|
}
|
|
259
|
-
|
|
260
260
|
/** 原子快照重写 jsonl(内存 state 全量;含未 flush 行 → 排队行清空防重复)。 */
|
|
261
261
|
function compactNow(handle) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
handle.compacting = false
|
|
262
|
+
if (handle.compacting)
|
|
263
|
+
return;
|
|
264
|
+
handle.compacting = true;
|
|
265
|
+
if (handle.flushTimer !== null) {
|
|
266
|
+
clearTimeout(handle.flushTimer);
|
|
267
|
+
handle.flushTimer = null;
|
|
268
|
+
}
|
|
269
|
+
handle.lineQueue = [];
|
|
270
|
+
handle.queuedLines = 0;
|
|
271
|
+
const migrated = handle.migrated;
|
|
272
|
+
handle.migrated = false;
|
|
273
|
+
const file = handle.file;
|
|
274
|
+
const legacy = handle.legacy;
|
|
275
|
+
handle.dirtyChain = handle.dirtyChain
|
|
276
|
+
.then(async () => {
|
|
277
|
+
await writeSnapshot(file, handle.store.state, handle.ctx.logger, PREFIX);
|
|
278
|
+
if (migrated)
|
|
279
|
+
await removeLegacyAfterMigration(legacy);
|
|
281
280
|
})
|
|
281
|
+
.finally(() => {
|
|
282
|
+
handle.compacting = false;
|
|
283
|
+
});
|
|
282
284
|
}
|
|
283
|
-
|
|
284
285
|
/** 卸载冲刷:清定时器 + 回放未就绪缓冲 + 立即落盘(迁移兜底)。
|
|
285
286
|
* 返回落盘链 promise:调用方 await 可保证冲刷完成(卸载/退出时事件
|
|
286
287
|
* 不丢);fire-and-forget 调用亦兼容(触发后异步落盘)。 */
|
|
287
288
|
function dispose(handle) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
289
|
+
if (handle.flushTimer !== null) {
|
|
290
|
+
clearTimeout(handle.flushTimer);
|
|
291
|
+
handle.flushTimer = null;
|
|
292
|
+
}
|
|
293
|
+
if (handle.compactTimer !== null) {
|
|
294
|
+
clearTimeout(handle.compactTimer);
|
|
295
|
+
handle.compactTimer = null;
|
|
296
|
+
}
|
|
297
|
+
if (!handle.ready) {
|
|
298
|
+
const pending = handle.pending.splice(0);
|
|
299
|
+
for (const item of pending)
|
|
300
|
+
enqueueRecord(handle, item);
|
|
301
|
+
}
|
|
302
|
+
flushNow(handle);
|
|
303
|
+
if (handle.migrated || handle.queuedLines >= COMPACT_LINES)
|
|
304
|
+
compactNow(handle);
|
|
305
|
+
return handle.dirtyChain;
|
|
303
306
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-my-observability",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "DSH 可观测性 + Git 工程工具插件:会话轨迹回放(时间轴)、事件审计(agent 行为记录)、结构化 Git 提交(Conventional Commits)、增量 diff 审查(提交前审查)。DSH web plugin: trajectory replay timeline, event audit log, structured git commits, pre-commit incremental diff review.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
"node": ">=22"
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "node scripts/build.mjs",
|
|
16
|
-
"
|
|
15
|
+
"build": "npx tsc -p tsconfig.json && node scripts/build.mjs",
|
|
16
|
+
"typecheck": "npx tsc --noEmit -p tsconfig.json && npx tsc --noEmit -p tsconfig.client.json",
|
|
17
|
+
"test": "npx vitest run --coverage && cucumber-js test/features --import \"test/features/steps/*.mjs\""
|
|
17
18
|
},
|
|
18
19
|
"exports": {
|
|
19
20
|
".": {
|
|
@@ -41,9 +42,9 @@
|
|
|
41
42
|
}
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
|
-
"cordis": "^4.0.0-rc.
|
|
45
|
-
"
|
|
46
|
-
"
|
|
45
|
+
"cordis": "^4.0.0-rc.10",
|
|
46
|
+
"dsh-shared": "^0.1.4",
|
|
47
|
+
"react": "^18.2.0 || ^19.3.0"
|
|
47
48
|
},
|
|
48
49
|
"peerDependenciesMeta": {
|
|
49
50
|
"cordis": {
|
|
@@ -53,5 +54,12 @@
|
|
|
53
54
|
"optional": true
|
|
54
55
|
}
|
|
55
56
|
},
|
|
56
|
-
"
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^26.5.1",
|
|
59
|
+
"typescript": "^7.0.2"
|
|
60
|
+
},
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"tmp": "^0.2.7"
|
|
64
|
+
}
|
|
57
65
|
}
|