opencode-metrics-plugin 0.1.5 → 0.2.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/README.md +108 -390
- package/dist/api.d.ts +3 -0
- package/dist/api.js +4 -0
- package/dist/index.d.ts +2 -51
- package/dist/index.js +4 -61
- package/dist/metrics/dirs.d.ts +23 -0
- package/dist/{dirs.js → metrics/dirs.js} +4 -10
- package/dist/metrics/engine/engine.d.ts +15 -0
- package/dist/{metrics-engine.js → metrics/engine/engine.js} +33 -146
- package/dist/{metrics-handlers.d.ts → metrics/engine/handlers.d.ts} +1 -1
- package/dist/{metrics-handlers.js → metrics/engine/handlers.js} +2 -2
- package/dist/metrics/engine/state.d.ts +79 -0
- package/dist/{metrics-types.js → metrics/engine/state.js} +1 -19
- package/dist/{event-logger.d.ts → metrics/eventlog/event-logger.d.ts} +0 -5
- package/dist/{event-logger.js → metrics/eventlog/event-logger.js} +2 -18
- package/dist/metrics/index.d.ts +9 -0
- package/dist/metrics/index.js +6 -0
- package/dist/metrics/runtime.d.ts +25 -0
- package/dist/metrics/runtime.js +28 -0
- package/dist/metrics/snapshot/flush.d.ts +6 -0
- package/dist/{metrics-output.js → metrics/snapshot/flush.js} +6 -290
- package/dist/metrics/snapshot/merge.d.ts +9 -0
- package/dist/metrics/snapshot/merge.js +282 -0
- package/dist/{metrics-steps.d.ts → metrics/snapshot/steps.d.ts} +1 -1
- package/dist/{metrics-steps.js → metrics/snapshot/steps.js} +1 -1
- package/dist/{metrics-types.d.ts → metrics/types.d.ts} +3 -81
- package/dist/metrics/types.js +20 -0
- package/dist/plugin.d.ts +22 -0
- package/dist/plugin.js +49 -0
- package/dist/{logger.d.ts → shared/log.d.ts} +2 -0
- package/dist/{logger.js → shared/log.js} +9 -2
- package/package.json +12 -4
- package/dist/backfill-cli.d.ts +0 -1
- package/dist/backfill-cli.js +0 -74
- package/dist/backfill.d.ts +0 -58
- package/dist/backfill.js +0 -550
- package/dist/dirs.d.ts +0 -34
- package/dist/metrics-engine.d.ts +0 -37
- package/dist/metrics-output.d.ts +0 -11
- package/dist/summary-store.d.ts +0 -150
- package/dist/summary-store.js +0 -560
- /package/dist/{compile-analyzer.d.ts → metrics/analysis/hvigor.d.ts} +0 -0
- /package/dist/{compile-analyzer.js → metrics/analysis/hvigor.js} +0 -0
|
@@ -50,7 +50,7 @@ export interface SessionMeta {
|
|
|
50
50
|
agentUsage: Record<string, number>;
|
|
51
51
|
modelSwitches: number;
|
|
52
52
|
modelTokenDistribution: Record<string, TokenUsage>;
|
|
53
|
-
/** opencode session.title
|
|
53
|
+
/** opencode session.title(实时事件不产出,可选填充) */
|
|
54
54
|
title?: string;
|
|
55
55
|
}
|
|
56
56
|
export interface FixCycle {
|
|
@@ -192,8 +192,8 @@ export interface SubAgentOutput {
|
|
|
192
192
|
}
|
|
193
193
|
export interface MetricsOutput {
|
|
194
194
|
sessionId: string;
|
|
195
|
-
/**
|
|
196
|
-
source?: "live"
|
|
195
|
+
/** 快照来源标记(0.2.0 起仅 live) */
|
|
196
|
+
source?: "live";
|
|
197
197
|
startTime: number;
|
|
198
198
|
endTime: number;
|
|
199
199
|
duration: number;
|
|
@@ -300,82 +300,4 @@ export interface MetricsEngine {
|
|
|
300
300
|
flush(sessionId: string): void;
|
|
301
301
|
dispose(): void;
|
|
302
302
|
}
|
|
303
|
-
export interface SubAgentState {
|
|
304
|
-
sessionId: string;
|
|
305
|
-
agentName: string;
|
|
306
|
-
title: string;
|
|
307
|
-
messageMap: Map<string, MessageEntry>;
|
|
308
|
-
toolCallMap: Map<string, ToolCallEntry>;
|
|
309
|
-
textLengthMap: Map<string, number>;
|
|
310
|
-
orderCounter: number;
|
|
311
|
-
_userMessageIds: Set<string>;
|
|
312
|
-
_pendingUserMessage: string[];
|
|
313
|
-
}
|
|
314
|
-
export interface SessionMetricsState {
|
|
315
|
-
sessionId: string;
|
|
316
|
-
startTime: number;
|
|
317
|
-
endTime: number;
|
|
318
|
-
rounds: RoundSnapshot[];
|
|
319
|
-
tokens: TokenUsage;
|
|
320
|
-
tools: {
|
|
321
|
-
totalCalls: number;
|
|
322
|
-
invalidCalls: number;
|
|
323
|
-
completedCalls: number;
|
|
324
|
-
errorCalls: number;
|
|
325
|
-
distribution: Map<string, ToolStats>;
|
|
326
|
-
slowestCall: {
|
|
327
|
-
tool: string;
|
|
328
|
-
duration: number;
|
|
329
|
-
};
|
|
330
|
-
};
|
|
331
|
-
agent: {
|
|
332
|
-
current: string;
|
|
333
|
-
switches: number;
|
|
334
|
-
lastSwitchTime: number;
|
|
335
|
-
usage: Map<string, number>;
|
|
336
|
-
};
|
|
337
|
-
model: {
|
|
338
|
-
current: string;
|
|
339
|
-
switches: number;
|
|
340
|
-
tokenDistribution: Map<string, TokenUsage>;
|
|
341
|
-
};
|
|
342
|
-
compactions: number;
|
|
343
|
-
anomaly: {
|
|
344
|
-
triggered: boolean;
|
|
345
|
-
events: string[];
|
|
346
|
-
};
|
|
347
|
-
currentStage: StageInfo | null;
|
|
348
|
-
stages: StageInfo[];
|
|
349
|
-
lastStageEndTime: number;
|
|
350
|
-
_stageTokens: TokenUsage;
|
|
351
|
-
_roundStartTime: number;
|
|
352
|
-
_firstEventTime: number;
|
|
353
|
-
_firstTextTime: number;
|
|
354
|
-
_hasFirstEvent: boolean;
|
|
355
|
-
_hasTextPart: boolean;
|
|
356
|
-
_roundTokens: TokenUsage;
|
|
357
|
-
_roundToolCalls: number;
|
|
358
|
-
_roundErrors: number;
|
|
359
|
-
_roundFirstBuildTracked: boolean;
|
|
360
|
-
_roundIdleProcessed: boolean;
|
|
361
|
-
_idleFlushed: boolean;
|
|
362
|
-
messageMap: Map<string, MessageEntry>;
|
|
363
|
-
toolCallMap: Map<string, ToolCallEntry>;
|
|
364
|
-
textLengthMap: Map<string, number>;
|
|
365
|
-
sessionMeta: SessionMeta;
|
|
366
|
-
compileStats: CompileStats;
|
|
367
|
-
orderCounter: number;
|
|
368
|
-
skillCallMap: Map<string, SkillCallEntry>;
|
|
369
|
-
htmlPreviewMsgId: string | null;
|
|
370
|
-
skillSearchMap: Map<string, SkillSearchEntry>;
|
|
371
|
-
_pendingReadChain: {
|
|
372
|
-
searchCallID: string;
|
|
373
|
-
} | null;
|
|
374
|
-
_userMessageIds: Set<string>;
|
|
375
|
-
_pendingUserMessage: string[];
|
|
376
|
-
subAgents: Map<string, SubAgentState>;
|
|
377
|
-
planningCalls: PlanningCall[];
|
|
378
|
-
systemPrompts: Map<string, string[]>;
|
|
379
|
-
}
|
|
380
303
|
export declare function createTokenUsage(): TokenUsage;
|
|
381
|
-
export declare function createSessionMetrics(sessionId: string): SessionMetricsState;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const TRACKED_EVENT_TYPES = new Set([
|
|
2
|
+
"session.created",
|
|
3
|
+
"session.updated",
|
|
4
|
+
"session.idle",
|
|
5
|
+
"session.deleted",
|
|
6
|
+
"session.compacted",
|
|
7
|
+
"session.status",
|
|
8
|
+
"session.error",
|
|
9
|
+
"message.updated",
|
|
10
|
+
"message.removed",
|
|
11
|
+
"message.part.updated",
|
|
12
|
+
"message.part.removed",
|
|
13
|
+
"permission.asked",
|
|
14
|
+
"permission.replied",
|
|
15
|
+
"command.executed",
|
|
16
|
+
]);
|
|
17
|
+
// ─── Factory Functions ───────────────────────────────────────────────────────
|
|
18
|
+
export function createTokenUsage() {
|
|
19
|
+
return { input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
20
|
+
}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
import type { MetricsRuntimeOptions } from "./metrics/runtime.js";
|
|
3
|
+
/**
|
|
4
|
+
* opencode.json 插件配置项(["opencode-metrics-plugin", { ... }] 的第二参)。
|
|
5
|
+
* 与 MetricsRuntimeOptions 一致:enabled / eventLogging / dirs / onFlush。
|
|
6
|
+
*/
|
|
7
|
+
export type MetricsPluginOptions = MetricsRuntimeOptions;
|
|
8
|
+
/**
|
|
9
|
+
* opencode 插件入口。
|
|
10
|
+
*
|
|
11
|
+
* ```jsonc
|
|
12
|
+
* // opencode.json
|
|
13
|
+
* { "plugin": ["opencode-metrics-plugin"] }
|
|
14
|
+
* // 带配置:
|
|
15
|
+
* { "plugin": [["opencode-metrics-plugin", { "enabled": true, "eventLogging": true }]] }
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* 挂上后自动记录全部会话:每轮 session.idle 落盘快照 JSON,
|
|
19
|
+
* dispose 时兜底 flush 未落盘会话。@opencode-ai/plugin 仅作类型依赖(零运行时依赖)。
|
|
20
|
+
*/
|
|
21
|
+
export declare const MetricsPlugin: Plugin;
|
|
22
|
+
export default MetricsPlugin;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createMetricsRuntime } from "./metrics/runtime.js";
|
|
2
|
+
import { log } from "./shared/log.js";
|
|
3
|
+
/**
|
|
4
|
+
* opencode 插件入口。
|
|
5
|
+
*
|
|
6
|
+
* ```jsonc
|
|
7
|
+
* // opencode.json
|
|
8
|
+
* { "plugin": ["opencode-metrics-plugin"] }
|
|
9
|
+
* // 带配置:
|
|
10
|
+
* { "plugin": [["opencode-metrics-plugin", { "enabled": true, "eventLogging": true }]] }
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* 挂上后自动记录全部会话:每轮 session.idle 落盘快照 JSON,
|
|
14
|
+
* dispose 时兜底 flush 未落盘会话。@opencode-ai/plugin 仅作类型依赖(零运行时依赖)。
|
|
15
|
+
*/
|
|
16
|
+
export const MetricsPlugin = async (_input, options) => {
|
|
17
|
+
const opts = (options ?? {});
|
|
18
|
+
const runtime = createMetricsRuntime(opts);
|
|
19
|
+
log.info("[MetricsPlugin] started", {
|
|
20
|
+
metricsDir: runtime.dirs.metricsDir,
|
|
21
|
+
eventsDir: runtime.dirs.eventsDir,
|
|
22
|
+
enabled: opts.enabled !== false,
|
|
23
|
+
});
|
|
24
|
+
const hooks = {
|
|
25
|
+
event: async ({ event }) => {
|
|
26
|
+
runtime.event({
|
|
27
|
+
event: event,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
// 记录各模型 system prompt(只读不改写、不返回;子代理跳过)
|
|
31
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
32
|
+
const sessionId = input.sessionID;
|
|
33
|
+
if (!sessionId)
|
|
34
|
+
return;
|
|
35
|
+
if (runtime.eventLogger?.isSubAgent(sessionId))
|
|
36
|
+
return;
|
|
37
|
+
const modelId = input.model?.id ?? "unknown";
|
|
38
|
+
runtime.eventLogger?.logPrompt(sessionId, `system:${modelId}`, output.system);
|
|
39
|
+
runtime.engine.ingestPrompt(sessionId, modelId, output.system);
|
|
40
|
+
},
|
|
41
|
+
dispose: async () => {
|
|
42
|
+
runtime.eventLogger?.dispose();
|
|
43
|
+
runtime.engine.dispose();
|
|
44
|
+
log.info("[MetricsPlugin] disposed");
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
return hooks;
|
|
48
|
+
};
|
|
49
|
+
export default MetricsPlugin;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
2
|
export declare function setLogLevel(level: LogLevel): void;
|
|
3
3
|
export declare function getLogLevel(): LogLevel;
|
|
4
|
+
/** 设置运行日志落盘文件;传 null 关闭文件输出(仅 console)。 */
|
|
5
|
+
export declare function setLogFile(file: string | null): void;
|
|
4
6
|
export declare function flushLogs(): void;
|
|
5
7
|
export declare const log: {
|
|
6
8
|
debug(...parts: unknown[]): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import
|
|
3
|
+
import envPaths from 'env-paths';
|
|
4
4
|
const LEVEL_ORDER = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
5
5
|
let minOrder = LEVEL_ORDER.info;
|
|
6
6
|
export function setLogLevel(level) {
|
|
@@ -9,12 +9,19 @@ export function setLogLevel(level) {
|
|
|
9
9
|
export function getLogLevel() {
|
|
10
10
|
return Object.keys(LEVEL_ORDER).find((k) => LEVEL_ORDER[k] === minOrder) ?? 'info';
|
|
11
11
|
}
|
|
12
|
+
const DEFAULT_LOG_FILE = path.join(envPaths('opencode-metrics-plugin', { suffix: '' }).log, 'plugin.log');
|
|
13
|
+
let logFile = DEFAULT_LOG_FILE;
|
|
14
|
+
/** 设置运行日志落盘文件;传 null 关闭文件输出(仅 console)。 */
|
|
15
|
+
export function setLogFile(file) {
|
|
16
|
+
logFile = file;
|
|
17
|
+
}
|
|
12
18
|
let buffer = [];
|
|
13
19
|
let flushTimer = null;
|
|
14
20
|
const FLUSH_INTERVAL_MS = 500;
|
|
15
21
|
const BUFFER_SIZE_LIMIT = 50;
|
|
16
22
|
function writeLine(line) {
|
|
17
|
-
|
|
23
|
+
if (logFile == null)
|
|
24
|
+
return;
|
|
18
25
|
try {
|
|
19
26
|
fs.mkdirSync(path.dirname(logFile), { recursive: true });
|
|
20
27
|
fs.appendFileSync(logFile, line + '\n');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-metrics-plugin",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "opencode plugin: auto-records every session's metrics (tokens/rounds/steps/sub-agents/hvigor builds) to snapshot JSON. Zero DB, zero config. Pair with session-viewer for visualization.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
".": {
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"import": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./api": {
|
|
22
|
+
"types": "./dist/api.d.ts",
|
|
23
|
+
"import": "./dist/api.js"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"files": [
|
|
@@ -24,7 +28,7 @@
|
|
|
24
28
|
"README.md"
|
|
25
29
|
],
|
|
26
30
|
"scripts": {
|
|
27
|
-
"build": "tsc",
|
|
31
|
+
"build": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\" && tsc",
|
|
28
32
|
"typecheck": "tsc --noEmit",
|
|
29
33
|
"test": "tsx example/smoke.mts",
|
|
30
34
|
"prepublishOnly": "npm run build && npm test"
|
|
@@ -35,9 +39,13 @@
|
|
|
35
39
|
"dependencies": {
|
|
36
40
|
"env-paths": "^3.0.0"
|
|
37
41
|
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@opencode-ai/plugin": "^1.18.0"
|
|
44
|
+
},
|
|
38
45
|
"devDependencies": {
|
|
46
|
+
"@opencode-ai/plugin": "^1.18.9",
|
|
39
47
|
"@types/node": "^22.9.0",
|
|
40
48
|
"tsx": "^4.23.13",
|
|
41
49
|
"typescript": "^5.6.0"
|
|
42
|
-
}
|
|
50
|
+
}
|
|
43
51
|
}
|
package/dist/backfill-cli.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/backfill-cli.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { backfillFromOpencode } from "./backfill.js";
|
|
4
|
-
function loadConfig(configPath) {
|
|
5
|
-
let raw = fs.readFileSync(configPath, "utf-8");
|
|
6
|
-
// 容忍 // 行注释与尾逗号外的常见 jsonc 写法(去掉整行注释)
|
|
7
|
-
raw = raw
|
|
8
|
-
.split("\n")
|
|
9
|
-
.map((l) => l.replace(/^\s*\/\/.*$/, ""))
|
|
10
|
-
.join("\n");
|
|
11
|
-
const cfg = JSON.parse(raw);
|
|
12
|
-
if (!Array.isArray(cfg.engines) || cfg.engines.length === 0) {
|
|
13
|
-
throw new Error("配置文件缺少 engines 数组");
|
|
14
|
-
}
|
|
15
|
-
return cfg;
|
|
16
|
-
}
|
|
17
|
-
function parseArgs(argv) {
|
|
18
|
-
const out = { agents: [], dryRun: false };
|
|
19
|
-
for (let i = 0; i < argv.length; i++) {
|
|
20
|
-
const a = argv[i];
|
|
21
|
-
if (a === "--config")
|
|
22
|
-
out.config = argv[++i];
|
|
23
|
-
else if (a === "--agent")
|
|
24
|
-
out.agents.push(argv[++i]);
|
|
25
|
-
else if (a === "--since") {
|
|
26
|
-
const v = argv[++i];
|
|
27
|
-
const n = Number(v);
|
|
28
|
-
out.since = Number.isFinite(n) && v.trim() !== "" ? n : Date.parse(v);
|
|
29
|
-
}
|
|
30
|
-
else if (a === "--dry-run")
|
|
31
|
-
out.dryRun = true;
|
|
32
|
-
else if (a === "--db")
|
|
33
|
-
out.dbPath = argv[++i];
|
|
34
|
-
else if (a === "--help" || a === "-h") {
|
|
35
|
-
console.log("用法: node dist/backfill-cli.js --config <engines.json> [--agent <name>...] [--since <ms|ISO>] [--db <path>] [--dry-run]");
|
|
36
|
-
process.exit(0);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return out;
|
|
40
|
-
}
|
|
41
|
-
async function main() {
|
|
42
|
-
const args = parseArgs(process.argv.slice(2));
|
|
43
|
-
if (!args.config) {
|
|
44
|
-
console.error("必须指定 --config <engines.json>");
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
const cfg = loadConfig(path.resolve(args.config));
|
|
48
|
-
const filter = { ...(cfg.filter ?? {}) };
|
|
49
|
-
if (args.agents.length > 0)
|
|
50
|
-
filter.agents = args.agents;
|
|
51
|
-
if (typeof args.since === "number" && Number.isFinite(args.since))
|
|
52
|
-
filter.since = args.since;
|
|
53
|
-
const opts = {
|
|
54
|
-
dbPath: args.dbPath ?? cfg.dbPath,
|
|
55
|
-
engines: cfg.engines,
|
|
56
|
-
fallback: cfg.fallback,
|
|
57
|
-
filter,
|
|
58
|
-
skipExisting: true,
|
|
59
|
-
dryRun: args.dryRun,
|
|
60
|
-
};
|
|
61
|
-
console.log(`回填开始: db=${opts.dbPath ?? "默认"} 引擎数=${opts.engines.length}${opts.dryRun ? " [dry-run]" : ""}`);
|
|
62
|
-
const result = await backfillFromOpencode(opts);
|
|
63
|
-
for (const s of result.perEngine) {
|
|
64
|
-
console.log(` [${s.label}] ${s.metricsDir} → 写入 ${s.written} | 已有跳过 ${s.skippedExisting} | 空会话 ${s.skippedEmpty}`);
|
|
65
|
-
}
|
|
66
|
-
if (result.fallback) {
|
|
67
|
-
console.log(` [fallback] ${result.fallback.metricsDir} → 写入 ${result.fallback.written} | 已有跳过 ${result.fallback.skippedExisting} | 空会话 ${result.fallback.skippedEmpty}`);
|
|
68
|
-
}
|
|
69
|
-
console.log(` 过滤跳过 ${result.skippedByFilter} | 无人认领 ${result.unattributed} | 顶层会话总数 ${result.total}`);
|
|
70
|
-
}
|
|
71
|
-
main().catch((err) => {
|
|
72
|
-
console.error("回填失败:", err);
|
|
73
|
-
process.exit(1);
|
|
74
|
-
});
|
package/dist/backfill.d.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { MetricsOutput } from "./metrics-types.js";
|
|
2
|
-
/** 引擎归属规则(与运行时 scope 同语义:agents 命中或 cwd 前缀命中即归属,首条命中优先) */
|
|
3
|
-
export interface BackfillEngineRule {
|
|
4
|
-
label: string;
|
|
5
|
-
/** 该引擎快照写入目录(<sessionId>.json 落这里) */
|
|
6
|
-
metricsDir: string;
|
|
7
|
-
/** agent 名单(session.agent 列,缺失时回退首条含 agent 的 message) */
|
|
8
|
-
agents?: string[];
|
|
9
|
-
/** 工作目录前缀(与 session.directory 前缀匹配,斜杠/大小写不敏感) */
|
|
10
|
-
cwdPrefixes?: string[];
|
|
11
|
-
/** 摘要库落点;缺省写全局共享库(与 live flush 一致,多宿主聚合) */
|
|
12
|
-
summaryFile?: string;
|
|
13
|
-
}
|
|
14
|
-
/** 回填会话选择过滤器(在归属判定之前应用) */
|
|
15
|
-
export interface BackfillFilter {
|
|
16
|
-
agents?: string[];
|
|
17
|
-
cwdPrefixes?: string[];
|
|
18
|
-
since?: number;
|
|
19
|
-
sessionIds?: string[];
|
|
20
|
-
}
|
|
21
|
-
export interface BackfillOptions {
|
|
22
|
-
/** opencode 数据库路径,默认 ~/.local/share/opencode/opencode.db */
|
|
23
|
-
dbPath?: string;
|
|
24
|
-
engines: BackfillEngineRule[];
|
|
25
|
-
/** 无人认领会话的兜底目录(不配则跳过不回填) */
|
|
26
|
-
fallback?: {
|
|
27
|
-
metricsDir: string;
|
|
28
|
-
summaryFile?: string;
|
|
29
|
-
};
|
|
30
|
-
filter?: BackfillFilter;
|
|
31
|
-
/** 目标目录已有快照(live 优先)则跳过,默认 true */
|
|
32
|
-
skipExisting?: boolean;
|
|
33
|
-
/** 增量模式:读各目录 .backfill-state.json 水位,只回填 time_created 更晚的会话 */
|
|
34
|
-
incremental?: boolean;
|
|
35
|
-
/** 只统计不写盘 */
|
|
36
|
-
dryRun?: boolean;
|
|
37
|
-
/** 单会话回填完成回调 */
|
|
38
|
-
onSessionBackfilled?: (sessionId: string, label: string, metricsDir: string) => void;
|
|
39
|
-
}
|
|
40
|
-
export interface BackfillEngineStat {
|
|
41
|
-
label: string;
|
|
42
|
-
metricsDir: string;
|
|
43
|
-
written: number;
|
|
44
|
-
skippedExisting: number;
|
|
45
|
-
skippedEmpty: number;
|
|
46
|
-
}
|
|
47
|
-
export interface BackfillResult {
|
|
48
|
-
perEngine: BackfillEngineStat[];
|
|
49
|
-
fallback: BackfillEngineStat | null;
|
|
50
|
-
skippedByFilter: number;
|
|
51
|
-
unattributed: number;
|
|
52
|
-
total: number;
|
|
53
|
-
}
|
|
54
|
-
export type BackfillMetricsOutput = MetricsOutput & {
|
|
55
|
-
source: "backfill";
|
|
56
|
-
};
|
|
57
|
-
/** 从 opencode.db 回填历史会话统计:按归属规则路由写入各引擎目录(快照带 source:"backfill")。 */
|
|
58
|
-
export declare function backfillFromOpencode(opts: BackfillOptions): Promise<BackfillResult>;
|