mingdao-harness 0.2.3 → 0.2.5
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/docs/ROADMAP-NEXT.md +13 -13
- package/package.json +1 -1
- package/skills/release-checklist/SKILL.md +16 -3
- package/src/agent.js +102 -30
- package/src/audit.js +2 -2
- package/src/batch.js +74 -16
- package/src/cachestats.js +57 -11
- package/src/cli.js +29 -679
- package/src/commands/desktop.js +1 -1
- package/src/commands/key.js +1 -1
- package/src/commands/repl.js +711 -0
- package/src/commands/schedule.js +7 -7
- package/src/commands/skill.js +18 -16
- package/src/commands/sync.js +5 -5
- package/src/commands/update.js +22 -9
- package/src/commands/workspace.js +2 -2
- package/src/compact.js +6 -6
- package/src/config.js +7 -4
- package/src/context.js +11 -11
- package/src/cost-guard.js +15 -5
- package/src/credentials.js +6 -0
- package/src/hooks.js +11 -11
- package/src/mcp-presets.js +5 -5
- package/src/mcp.js +19 -19
- package/src/memory.js +16 -16
- package/src/model-discovery.js +16 -15
- package/src/models.js +4 -4
- package/src/notify.js +2 -2
- package/src/permissions.js +7 -3
- package/src/pricing.js +35 -1
- package/src/prompts.js +1 -1
- package/src/providers/index.js +7 -7
- package/src/providers/openai-compatible.js +8 -8
- package/src/routing.js +5 -5
- package/src/schedule.js +28 -28
- package/src/session-index.js +5 -1
- package/src/session.js +11 -2
- package/src/skill-lib.js +62 -3
- package/src/skill-registry.js +10 -9
- package/src/skills.js +10 -10
- package/src/sync-server.js +86 -3
- package/src/sync.js +53 -20
- package/src/tasks/worker.js +126 -0
- package/src/tasks.js +13 -13
- package/src/titles.js +5 -5
- package/src/tokenizer.js +22 -4
- package/src/tools/bash.js +12 -12
- package/src/tools/fs-tools.js +66 -9
- package/src/tools/index.js +77 -36
- package/src/ui.js +83 -6
- package/src/update.js +8 -8
- package/src/web/app.js +43 -0
- package/src/web/attachments.js +1 -1
- package/src/web/index.html +12 -0
- package/src/web/routes/api.js +38 -786
- package/src/web/routes/domains/config.js +280 -0
- package/src/web/routes/domains/misc.js +128 -0
- package/src/web/routes/domains/schedule.js +127 -0
- package/src/web/routes/domains/sessions.js +105 -0
- package/src/web/routes/domains/skills.js +79 -0
- package/src/web/routes/domains/sync.js +101 -0
- package/src/web/routes/domains/workspace.js +113 -0
- package/src/web/server.js +29 -14
- package/src/web/web-io.js +54 -12
- package/src/workspace.js +14 -14
package/src/cachestats.js
CHANGED
|
@@ -16,7 +16,7 @@ const MAX_LINES = 20000;
|
|
|
16
16
|
const KEEP_LINES = 10000;
|
|
17
17
|
let cacheStatsCount = 0;
|
|
18
18
|
|
|
19
|
-
export function recordCacheStats(entry) {
|
|
19
|
+
export function recordCacheStats(/** @type {any} */ entry) {
|
|
20
20
|
try {
|
|
21
21
|
ensureHome(); // 审计 B10:与 audit/journal 一致,目录缺失不静默丢统计
|
|
22
22
|
const line = JSON.stringify({
|
|
@@ -33,6 +33,8 @@ export function recordCacheStats(entry) {
|
|
|
33
33
|
llmMs: entry.llmMs ?? undefined, // 模型调用总耗时(状态栏 LLM 时长/tok-s)
|
|
34
34
|
toolMs: entry.toolMs ?? undefined, // 工具执行总耗时(状态栏 工具调用时长)
|
|
35
35
|
firstTokenMs: entry.firstTokenMs ?? undefined, // 首 token 延迟(状态栏平均首 token)
|
|
36
|
+
reasoning: entry.reasoning ?? undefined, // 省钱 B3:推理 token 估算(费用二级分账维度)
|
|
37
|
+
byTool: Array.isArray(entry.byTool) ? entry.byTool : undefined, // 省钱 B3:逐工具 {tool,calls,ms}
|
|
36
38
|
});
|
|
37
39
|
fs.appendFileSync(cacheStatsFile(), line + '\n');
|
|
38
40
|
cacheStatsCount += 1;
|
|
@@ -52,13 +54,13 @@ export function recordCacheStats(entry) {
|
|
|
52
54
|
// listCacheStats 读取缓存(审计:costGuard 每步 / WebUI 每 15s / /cost 命令都调用——
|
|
53
55
|
// 四报告共识 P2-2/§3.3-A:全文件读+逐行 parse 会随 JSONL 增长线性放大 IO。
|
|
54
56
|
// 按 mtimeMs+size 双键缓存;写入追加会改两者,轮转重写同样改,缓存自动失效)
|
|
55
|
-
let _statsCache = null;
|
|
57
|
+
let /** @type {any} */ _statsCache = null;
|
|
56
58
|
|
|
57
59
|
export function listCacheStats(limit = 2000) {
|
|
58
60
|
try {
|
|
59
61
|
const file = cacheStatsFile();
|
|
60
62
|
const st = fs.statSync(file);
|
|
61
|
-
if (
|
|
63
|
+
if (!/** @type {any} */ _statsCache || _statsCache.mtimeMs !== st.mtimeMs || _statsCache.size !== st.size || _statsCache.limit < limit) {
|
|
62
64
|
const raw = fs.readFileSync(file, 'utf8');
|
|
63
65
|
const out = [];
|
|
64
66
|
for (const l of raw.split('\n')) {
|
|
@@ -75,8 +77,8 @@ export function listCacheStats(limit = 2000) {
|
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
export function summarizeCacheStats(entries) {
|
|
79
|
-
const sum = { turns: entries.length, prompt: 0, completion: 0, hit: 0, miss: 0, cost: 0, saved: 0, steps: 0, llmMs: 0, toolMs: 0, firstTokenCount: 0, firstTokenSumMs: 0 };
|
|
80
|
+
export function summarizeCacheStats(/** @type {any} */ entries) {
|
|
81
|
+
const sum = /** @type {any} */ ({ turns: entries.length, prompt: 0, completion: 0, hit: 0, miss: 0, cost: 0, saved: 0, steps: 0, llmMs: 0, toolMs: 0, firstTokenCount: 0, firstTokenSumMs: 0 });
|
|
80
82
|
for (const e of entries) {
|
|
81
83
|
sum.prompt += e.prompt || 0;
|
|
82
84
|
sum.completion += e.completion || 0;
|
|
@@ -100,7 +102,7 @@ export function summarizeCacheStats(entries) {
|
|
|
100
102
|
|
|
101
103
|
// 记录一次用量(agent 侧调用):自动计算命中拆分与节省额
|
|
102
104
|
// 记录一次用量(agent 侧调用):自动计算命中拆分与节省额;perf 为回合性能指标(状态栏)
|
|
103
|
-
export function recordUsage(modelName, usage, perf = null) {
|
|
105
|
+
export function recordUsage(/** @type {any} */ modelName, /** @type {any} */ usage, /** @type {any} */ perf = null) {
|
|
104
106
|
const split = cacheSplit(usage);
|
|
105
107
|
const prompt = usage?.prompt_tokens || 0;
|
|
106
108
|
const completion = usage?.completion_tokens || 0;
|
|
@@ -125,11 +127,13 @@ export function recordUsage(modelName, usage, perf = null) {
|
|
|
125
127
|
llmMs: perf?.llmMs ?? undefined,
|
|
126
128
|
toolMs: perf?.toolMs ?? undefined,
|
|
127
129
|
firstTokenMs: perf?.firstTokenMs ?? undefined,
|
|
130
|
+
reasoning: perf?.reasoningTokens ?? undefined, // 省钱 B3
|
|
131
|
+
byTool: perf?.toolStats ?? undefined, // 省钱 B3
|
|
128
132
|
});
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
export function formatCacheSummary(sum) {
|
|
132
|
-
const fmt = (n) => (n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n));
|
|
135
|
+
export function formatCacheSummary(/** @type {any} */ sum) {
|
|
136
|
+
const fmt = (/** @type {any} */ n) => (n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n));
|
|
133
137
|
const lines = [
|
|
134
138
|
`轮次 ${sum.turns} · ↑${fmt(sum.prompt)} ↓${fmt(sum.completion)} tokens`,
|
|
135
139
|
`缓存命中率 ${sum.rate != null ? (sum.rate * 100).toFixed(0) + '%' : '暂无缓存数据'}`,
|
|
@@ -139,9 +143,12 @@ export function formatCacheSummary(sum) {
|
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
// 分账统计(评估 /cost 升级):按模型分账、今日费用、batch 半价任务、节省归因
|
|
146
|
+
// 省钱 B3:新增 reasoning 维度、byTool 逐工具累加、byDay 近 14 天按日折线数据
|
|
142
147
|
export function costBreakdown() {
|
|
143
148
|
const entries = listCacheStats(100000);
|
|
144
149
|
const byModel = new Map();
|
|
150
|
+
const byTool = /** @type {Map<string, {calls: number, ms: number}>} */ (new Map());
|
|
151
|
+
const byDay = /** @type {Map<string, number>} */ (new Map()); // 'MM-DD' → cost(近 14 个北京日,含 0 值日)
|
|
145
152
|
const start = beijingDayStart().getTime();
|
|
146
153
|
let totalCost = 0;
|
|
147
154
|
let totalSaved = 0;
|
|
@@ -149,21 +156,45 @@ export function costBreakdown() {
|
|
|
149
156
|
let batchCost = 0;
|
|
150
157
|
let hit = 0;
|
|
151
158
|
let miss = 0;
|
|
159
|
+
let reasoning = 0;
|
|
152
160
|
for (const e of entries) {
|
|
153
161
|
totalCost += e.cost || 0;
|
|
154
162
|
totalSaved += e.saved || 0;
|
|
155
163
|
hit += e.hit || 0;
|
|
156
164
|
miss += e.miss || 0;
|
|
165
|
+
reasoning += e.reasoning || 0;
|
|
157
166
|
if (e.batch) batchCost += e.cost || 0;
|
|
158
167
|
if (e.at >= start) today += e.cost || 0;
|
|
159
|
-
const m = byModel.get(e.model) || { prompt: 0, completion: 0, cost: 0, saved: 0, turns: 0, batchTurns: 0 };
|
|
168
|
+
const m = byModel.get(e.model) || { prompt: 0, completion: 0, cost: 0, saved: 0, turns: 0, batchTurns: 0, reasoning: 0 };
|
|
160
169
|
m.prompt += e.prompt || 0;
|
|
161
170
|
m.completion += e.completion || 0;
|
|
162
171
|
m.cost += e.cost || 0;
|
|
163
172
|
m.saved += e.saved || 0;
|
|
164
173
|
m.turns += 1;
|
|
174
|
+
m.reasoning += e.reasoning || 0;
|
|
165
175
|
if (e.batch) m.batchTurns += 1;
|
|
166
176
|
byModel.set(e.model, m);
|
|
177
|
+
if (Array.isArray(e.byTool)) {
|
|
178
|
+
for (const t of e.byTool) {
|
|
179
|
+
const tt = byTool.get(t.tool) || { calls: 0, ms: 0 };
|
|
180
|
+
tt.calls += t.calls || 0;
|
|
181
|
+
tt.ms += t.ms || 0;
|
|
182
|
+
byTool.set(t.tool, tt);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (e.at) {
|
|
186
|
+
const p = beijingParts(new Date(e.at));
|
|
187
|
+
const key = `${String(p.month).padStart(2, '0')}-${String(p.day).padStart(2, '0')}`;
|
|
188
|
+
byDay.set(key, (byDay.get(key) || 0) + (e.cost || 0));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// 补全近 14 天(含无记录的 0 值日),保证折线 x 轴连续
|
|
192
|
+
const days = /** @type {Array<{day: string, cost: number}>} */ ([]);
|
|
193
|
+
for (let i = 13; i >= 0; i--) {
|
|
194
|
+
const d = new Date(start - i * 86400000);
|
|
195
|
+
const p = beijingParts(d);
|
|
196
|
+
const key = `${String(p.month).padStart(2, '0')}-${String(p.day).padStart(2, '0')}`;
|
|
197
|
+
days.push({ day: key, cost: byDay.get(key) || 0 });
|
|
167
198
|
}
|
|
168
199
|
return {
|
|
169
200
|
totalCost,
|
|
@@ -173,12 +204,16 @@ export function costBreakdown() {
|
|
|
173
204
|
hit,
|
|
174
205
|
miss,
|
|
175
206
|
rate: hit + miss > 0 ? hit / (hit + miss) : null,
|
|
207
|
+
reasoning,
|
|
176
208
|
byModel: [...byModel.entries()].map(([model, m]) => ({ model, ...m })).sort((a, b) => b.cost - a.cost),
|
|
209
|
+
byTool: [...byTool.entries()].map(([tool, t]) => ({ tool, ...t })).sort((a, b) => b.ms - a.ms),
|
|
210
|
+
byDay: days,
|
|
177
211
|
};
|
|
178
212
|
}
|
|
179
213
|
|
|
180
214
|
// 月度费用报告(/cost 导出):按北京时间月份聚合,month 形如 'YYYY-MM',缺省返回全部月份
|
|
181
|
-
|
|
215
|
+
// 省钱 B3:月度聚合增加 reasoning 与 byTool 维度
|
|
216
|
+
export function costMonthlyReport(/** @type {any} */ month) {
|
|
182
217
|
const entries = listCacheStats(100000);
|
|
183
218
|
const want = String(month || '').trim();
|
|
184
219
|
const months = new Map();
|
|
@@ -188,7 +223,7 @@ export function costMonthlyReport(month) {
|
|
|
188
223
|
if (want && key !== want) continue;
|
|
189
224
|
let m = months.get(key);
|
|
190
225
|
if (!m) {
|
|
191
|
-
m = { month: key, cost: 0, saved: 0, prompt: 0, completion: 0, hit: 0, miss: 0, batchCost: 0, days: new Map(), models: new Map() };
|
|
226
|
+
m = { month: key, cost: 0, saved: 0, prompt: 0, completion: 0, hit: 0, miss: 0, batchCost: 0, reasoning: 0, days: new Map(), models: new Map(), tools: new Map() };
|
|
192
227
|
months.set(key, m);
|
|
193
228
|
}
|
|
194
229
|
m.cost += e.cost || 0;
|
|
@@ -197,6 +232,7 @@ export function costMonthlyReport(month) {
|
|
|
197
232
|
m.completion += e.completion || 0;
|
|
198
233
|
m.hit += e.hit || 0;
|
|
199
234
|
m.miss += e.miss || 0;
|
|
235
|
+
m.reasoning += e.reasoning || 0;
|
|
200
236
|
if (e.batch) m.batchCost += e.cost || 0;
|
|
201
237
|
const day = String(p.day).padStart(2, '0');
|
|
202
238
|
m.days.set(day, (m.days.get(day) || 0) + (e.cost || 0));
|
|
@@ -206,6 +242,14 @@ export function costMonthlyReport(month) {
|
|
|
206
242
|
mm.cost += e.cost || 0;
|
|
207
243
|
mm.turns += 1;
|
|
208
244
|
m.models.set(e.model, mm);
|
|
245
|
+
if (Array.isArray(e.byTool)) {
|
|
246
|
+
for (const t of e.byTool) {
|
|
247
|
+
const tt = m.tools.get(t.tool) || { calls: 0, ms: 0 };
|
|
248
|
+
tt.calls += t.calls || 0;
|
|
249
|
+
tt.ms += t.ms || 0;
|
|
250
|
+
m.tools.set(t.tool, tt);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
209
253
|
}
|
|
210
254
|
return [...months.values()]
|
|
211
255
|
.sort((a, b) => a.month.localeCompare(b.month))
|
|
@@ -219,7 +263,9 @@ export function costMonthlyReport(month) {
|
|
|
219
263
|
miss: m.miss,
|
|
220
264
|
rate: m.hit + m.miss > 0 ? m.hit / (m.hit + m.miss) : null,
|
|
221
265
|
batchCost: m.batchCost,
|
|
266
|
+
reasoning: m.reasoning,
|
|
222
267
|
days: [...m.days.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([day, cost]) => ({ day, cost })),
|
|
223
268
|
models: [...m.models.entries()].map(([model, x]) => ({ model, ...x })).sort((a, b) => b.cost - a.cost),
|
|
269
|
+
tools: [...m.tools.entries()].map(([tool, t]) => ({ tool, ...t })).sort((a, b) => b.ms - a.ms),
|
|
224
270
|
}));
|
|
225
271
|
}
|