@bububuger/spanory 0.1.14
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/dist/alert/evaluate.d.ts +3 -0
- package/dist/alert/evaluate.js +197 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.js +54 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1688 -0
- package/dist/otlp.d.ts +5 -0
- package/dist/otlp.js +12 -0
- package/dist/report/aggregate.d.ts +21 -0
- package/dist/report/aggregate.js +245 -0
- package/dist/runtime/claude/adapter.d.ts +18 -0
- package/dist/runtime/claude/adapter.js +73 -0
- package/dist/runtime/codex/adapter.d.ts +18 -0
- package/dist/runtime/codex/adapter.js +457 -0
- package/dist/runtime/codex/proxy.d.ts +9 -0
- package/dist/runtime/codex/proxy.js +212 -0
- package/dist/runtime/openclaw/adapter.d.ts +18 -0
- package/dist/runtime/openclaw/adapter.js +242 -0
- package/dist/runtime/shared/capabilities.d.ts +30 -0
- package/dist/runtime/shared/capabilities.js +39 -0
- package/dist/runtime/shared/normalize.d.ts +9 -0
- package/dist/runtime/shared/normalize.js +570 -0
- package/package.json +41 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { RUNTIME_CAPABILITIES } from '../shared/capabilities.js';
|
|
5
|
+
import { normalizeTranscriptMessages, parseProjectIdFromTranscriptPath, pickUsage } from '../shared/normalize.js';
|
|
6
|
+
function parseTimestamp(entry) {
|
|
7
|
+
const raw = entry?.timestamp ?? entry?.created_at ?? entry?.createdAt;
|
|
8
|
+
const date = raw ? new Date(raw) : new Date();
|
|
9
|
+
if (Number.isNaN(date.getTime()))
|
|
10
|
+
return new Date();
|
|
11
|
+
return date;
|
|
12
|
+
}
|
|
13
|
+
function normalizeToolName(name) {
|
|
14
|
+
if (name === 'exec')
|
|
15
|
+
return 'Bash';
|
|
16
|
+
return name;
|
|
17
|
+
}
|
|
18
|
+
function normalizeContentBlocks(content) {
|
|
19
|
+
if (typeof content === 'string')
|
|
20
|
+
return content;
|
|
21
|
+
if (!Array.isArray(content))
|
|
22
|
+
return '';
|
|
23
|
+
const blocks = [];
|
|
24
|
+
for (const block of content) {
|
|
25
|
+
if (typeof block === 'string') {
|
|
26
|
+
blocks.push(block);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!block || typeof block !== 'object')
|
|
30
|
+
continue;
|
|
31
|
+
if (block.type === 'text') {
|
|
32
|
+
blocks.push({ type: 'text', text: block.text ?? '' });
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (block.type === 'toolCall') {
|
|
36
|
+
blocks.push({
|
|
37
|
+
type: 'tool_use',
|
|
38
|
+
id: block.id ?? block.toolCallId ?? '',
|
|
39
|
+
name: normalizeToolName(block.name ?? block.toolName ?? ''),
|
|
40
|
+
input: block.arguments ?? block.input ?? {},
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (block.type === 'toolResult') {
|
|
45
|
+
blocks.push({
|
|
46
|
+
type: 'tool_result',
|
|
47
|
+
tool_use_id: block.toolCallId ?? block.tool_call_id ?? block.id ?? '',
|
|
48
|
+
content: block.content ?? '',
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (block.type === 'tool_use' || block.type === 'tool_result') {
|
|
53
|
+
blocks.push(block);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return blocks;
|
|
58
|
+
}
|
|
59
|
+
function normalizeRole(entry) {
|
|
60
|
+
if (entry?.type === 'message') {
|
|
61
|
+
const role = entry?.message?.role;
|
|
62
|
+
if (role === 'toolResult')
|
|
63
|
+
return 'user';
|
|
64
|
+
if (role)
|
|
65
|
+
return role;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (entry?.role)
|
|
69
|
+
return entry.role;
|
|
70
|
+
if (entry?.message?.role)
|
|
71
|
+
return entry.message.role;
|
|
72
|
+
if (entry?.payload?.role)
|
|
73
|
+
return entry.payload.role;
|
|
74
|
+
if (entry?.type === 'user' || entry?.type === 'assistant' || entry?.type === 'system')
|
|
75
|
+
return entry.type;
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function normalizeContent(entry) {
|
|
79
|
+
if (entry?.type === 'message') {
|
|
80
|
+
const role = entry?.message?.role;
|
|
81
|
+
if (role === 'toolResult') {
|
|
82
|
+
return [
|
|
83
|
+
{
|
|
84
|
+
type: 'tool_result',
|
|
85
|
+
tool_use_id: entry?.message?.toolCallId ?? entry?.message?.tool_call_id ?? '',
|
|
86
|
+
content: entry?.message?.content ?? '',
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
return normalizeContentBlocks(entry?.message?.content ?? '');
|
|
91
|
+
}
|
|
92
|
+
return normalizeContentBlocks(entry?.message?.content ?? entry?.content ?? entry?.payload?.content ?? '');
|
|
93
|
+
}
|
|
94
|
+
function normalizeModel(entry) {
|
|
95
|
+
return entry?.message?.model ?? entry?.model ?? entry?.payload?.model;
|
|
96
|
+
}
|
|
97
|
+
function normalizeMessageId(entry) {
|
|
98
|
+
return entry?.message?.id ?? entry?.messageId ?? entry?.message_id ?? entry?.id;
|
|
99
|
+
}
|
|
100
|
+
function normalizeToolUseResult(entry) {
|
|
101
|
+
return entry?.toolUseResult ?? entry?.tool_use_result ?? entry?.tool_result;
|
|
102
|
+
}
|
|
103
|
+
function normalizeSourceToolUseId(entry) {
|
|
104
|
+
return (entry?.sourceToolUseID
|
|
105
|
+
?? entry?.sourceToolUseId
|
|
106
|
+
?? entry?.source_tool_use_id
|
|
107
|
+
?? entry?.message?.toolCallId
|
|
108
|
+
?? entry?.message?.tool_call_id);
|
|
109
|
+
}
|
|
110
|
+
function normalizeUsage(entry) {
|
|
111
|
+
const raw = entry?.message?.usage
|
|
112
|
+
?? entry?.usage
|
|
113
|
+
?? entry?.message_usage
|
|
114
|
+
?? entry?.token_usage
|
|
115
|
+
?? entry?.payload?.usage;
|
|
116
|
+
if (!raw || typeof raw !== 'object')
|
|
117
|
+
return undefined;
|
|
118
|
+
return pickUsage({
|
|
119
|
+
input_tokens: raw.input_tokens ?? raw.prompt_tokens ?? raw.input,
|
|
120
|
+
output_tokens: raw.output_tokens ?? raw.completion_tokens ?? raw.output,
|
|
121
|
+
total_tokens: raw.total_tokens ?? raw.totalTokens,
|
|
122
|
+
cache_read_input_tokens: raw.cache_read_input_tokens ?? raw.cacheRead,
|
|
123
|
+
cache_creation_input_tokens: raw.cache_creation_input_tokens ?? raw.cacheWrite,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function normalizeIsSidechain(entry) {
|
|
127
|
+
const raw = entry?.isSidechain
|
|
128
|
+
?? entry?.is_sidechain
|
|
129
|
+
?? entry?.message?.isSidechain
|
|
130
|
+
?? entry?.message?.is_sidechain
|
|
131
|
+
?? entry?.payload?.isSidechain
|
|
132
|
+
?? entry?.payload?.is_sidechain;
|
|
133
|
+
return raw === true;
|
|
134
|
+
}
|
|
135
|
+
function normalizeAgentId(entry) {
|
|
136
|
+
return (entry?.agentId
|
|
137
|
+
?? entry?.agent_id
|
|
138
|
+
?? entry?.message?.agentId
|
|
139
|
+
?? entry?.message?.agent_id
|
|
140
|
+
?? entry?.payload?.agentId
|
|
141
|
+
?? entry?.payload?.agent_id);
|
|
142
|
+
}
|
|
143
|
+
async function readOpenclawTranscript(transcriptPath) {
|
|
144
|
+
const raw = await readFile(transcriptPath, 'utf-8');
|
|
145
|
+
const lines = raw.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
146
|
+
const messages = [];
|
|
147
|
+
let runtimeVersion;
|
|
148
|
+
for (const line of lines) {
|
|
149
|
+
try {
|
|
150
|
+
const entry = JSON.parse(line);
|
|
151
|
+
if (entry?.type === 'session') {
|
|
152
|
+
runtimeVersion = entry?.runtimeVersion
|
|
153
|
+
?? entry?.runtime_version
|
|
154
|
+
?? entry?.openclawVersion
|
|
155
|
+
?? entry?.openclaw_version
|
|
156
|
+
?? entry?.version
|
|
157
|
+
?? runtimeVersion;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const role = normalizeRole(entry);
|
|
161
|
+
if (!role)
|
|
162
|
+
continue;
|
|
163
|
+
messages.push({
|
|
164
|
+
role,
|
|
165
|
+
isMeta: entry?.isMeta ?? entry?.is_meta ?? false,
|
|
166
|
+
isSidechain: normalizeIsSidechain(entry),
|
|
167
|
+
agentId: normalizeAgentId(entry),
|
|
168
|
+
content: normalizeContent(entry),
|
|
169
|
+
model: normalizeModel(entry),
|
|
170
|
+
usage: normalizeUsage(entry),
|
|
171
|
+
messageId: normalizeMessageId(entry),
|
|
172
|
+
toolUseResult: normalizeToolUseResult(entry),
|
|
173
|
+
sourceToolUseId: normalizeSourceToolUseId(entry),
|
|
174
|
+
runtimeVersion: entry?.runtimeVersion
|
|
175
|
+
?? entry?.runtime_version
|
|
176
|
+
?? entry?.version
|
|
177
|
+
?? entry?.app_version
|
|
178
|
+
?? entry?.appVersion
|
|
179
|
+
?? runtimeVersion,
|
|
180
|
+
timestamp: parseTimestamp(entry),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// ignore malformed lines
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
messages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
188
|
+
return messages;
|
|
189
|
+
}
|
|
190
|
+
function resolveRuntimeHome(context) {
|
|
191
|
+
return (context.runtimeHome
|
|
192
|
+
?? process.env.SPANORY_OPENCLOW_HOME
|
|
193
|
+
?? process.env.SPANORY_OPENCLAW_HOME
|
|
194
|
+
?? path.join(process.env.HOME || '', '.openclaw'));
|
|
195
|
+
}
|
|
196
|
+
function parseOpenclawProjectId(transcriptPath) {
|
|
197
|
+
return (parseProjectIdFromTranscriptPath(transcriptPath, '/.openclaw/projects/')
|
|
198
|
+
?? parseProjectIdFromTranscriptPath(transcriptPath, '/.openclaw/agents/'));
|
|
199
|
+
}
|
|
200
|
+
async function resolveTranscriptPath(context) {
|
|
201
|
+
if (context.transcriptPath)
|
|
202
|
+
return context.transcriptPath;
|
|
203
|
+
const runtimeHome = resolveRuntimeHome(context);
|
|
204
|
+
const candidates = [
|
|
205
|
+
path.join(runtimeHome, 'projects', context.projectId, `${context.sessionId}.jsonl`),
|
|
206
|
+
path.join(runtimeHome, 'agents', context.projectId, 'sessions', `${context.sessionId}.jsonl`),
|
|
207
|
+
];
|
|
208
|
+
for (const p of candidates) {
|
|
209
|
+
try {
|
|
210
|
+
await stat(p);
|
|
211
|
+
return p;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// try next candidate
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return candidates[0];
|
|
218
|
+
}
|
|
219
|
+
export const openclawAdapter = {
|
|
220
|
+
runtimeName: 'openclaw',
|
|
221
|
+
capabilities: RUNTIME_CAPABILITIES.openclaw,
|
|
222
|
+
resolveContextFromHook(payload) {
|
|
223
|
+
const sessionId = payload.sessionId;
|
|
224
|
+
const transcriptPath = payload.transcriptPath;
|
|
225
|
+
if (!sessionId || !transcriptPath)
|
|
226
|
+
return null;
|
|
227
|
+
const projectId = parseOpenclawProjectId(transcriptPath);
|
|
228
|
+
if (!projectId)
|
|
229
|
+
return null;
|
|
230
|
+
return { projectId, sessionId, transcriptPath };
|
|
231
|
+
},
|
|
232
|
+
async collectEvents(context) {
|
|
233
|
+
const transcriptPath = await resolveTranscriptPath(context);
|
|
234
|
+
const messages = await readOpenclawTranscript(transcriptPath);
|
|
235
|
+
return normalizeTranscriptMessages({
|
|
236
|
+
runtime: 'openclaw',
|
|
237
|
+
projectId: context.projectId,
|
|
238
|
+
sessionId: context.sessionId,
|
|
239
|
+
messages,
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const CAPABILITY_KEYS: string[];
|
|
2
|
+
export declare const RUNTIME_CAPABILITIES: {
|
|
3
|
+
'claude-code': {
|
|
4
|
+
turnDetection: boolean;
|
|
5
|
+
toolCallAttribution: boolean;
|
|
6
|
+
toolResultCorrelation: boolean;
|
|
7
|
+
modelName: boolean;
|
|
8
|
+
usageDetails: boolean;
|
|
9
|
+
slashCommandExtraction: boolean;
|
|
10
|
+
mcpServerExtraction: boolean;
|
|
11
|
+
};
|
|
12
|
+
openclaw: {
|
|
13
|
+
turnDetection: boolean;
|
|
14
|
+
toolCallAttribution: boolean;
|
|
15
|
+
toolResultCorrelation: boolean;
|
|
16
|
+
modelName: boolean;
|
|
17
|
+
usageDetails: boolean;
|
|
18
|
+
slashCommandExtraction: boolean;
|
|
19
|
+
mcpServerExtraction: boolean;
|
|
20
|
+
};
|
|
21
|
+
codex: {
|
|
22
|
+
turnDetection: boolean;
|
|
23
|
+
toolCallAttribution: boolean;
|
|
24
|
+
toolResultCorrelation: boolean;
|
|
25
|
+
modelName: boolean;
|
|
26
|
+
usageDetails: boolean;
|
|
27
|
+
slashCommandExtraction: boolean;
|
|
28
|
+
mcpServerExtraction: boolean;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
export const CAPABILITY_KEYS = [
|
|
3
|
+
'turnDetection',
|
|
4
|
+
'toolCallAttribution',
|
|
5
|
+
'toolResultCorrelation',
|
|
6
|
+
'modelName',
|
|
7
|
+
'usageDetails',
|
|
8
|
+
'slashCommandExtraction',
|
|
9
|
+
'mcpServerExtraction',
|
|
10
|
+
];
|
|
11
|
+
export const RUNTIME_CAPABILITIES = {
|
|
12
|
+
'claude-code': {
|
|
13
|
+
turnDetection: true,
|
|
14
|
+
toolCallAttribution: true,
|
|
15
|
+
toolResultCorrelation: true,
|
|
16
|
+
modelName: true,
|
|
17
|
+
usageDetails: true,
|
|
18
|
+
slashCommandExtraction: true,
|
|
19
|
+
mcpServerExtraction: true,
|
|
20
|
+
},
|
|
21
|
+
openclaw: {
|
|
22
|
+
turnDetection: true,
|
|
23
|
+
toolCallAttribution: true,
|
|
24
|
+
toolResultCorrelation: true,
|
|
25
|
+
modelName: true,
|
|
26
|
+
usageDetails: true,
|
|
27
|
+
slashCommandExtraction: true,
|
|
28
|
+
mcpServerExtraction: true,
|
|
29
|
+
},
|
|
30
|
+
codex: {
|
|
31
|
+
turnDetection: true,
|
|
32
|
+
toolCallAttribution: true,
|
|
33
|
+
toolResultCorrelation: true,
|
|
34
|
+
modelName: true,
|
|
35
|
+
usageDetails: true,
|
|
36
|
+
slashCommandExtraction: true,
|
|
37
|
+
mcpServerExtraction: true,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function pickUsage(raw: any): {};
|
|
2
|
+
export declare function groupByTurns(messages: any): any[];
|
|
3
|
+
export declare function normalizeTranscriptMessages({ runtime, projectId, sessionId, messages }: {
|
|
4
|
+
runtime: any;
|
|
5
|
+
projectId: any;
|
|
6
|
+
sessionId: any;
|
|
7
|
+
messages: any;
|
|
8
|
+
}): any[];
|
|
9
|
+
export declare function parseProjectIdFromTranscriptPath(transcriptPath: any, marker: any): any;
|