@bububuger/spanory 0.1.16 → 0.1.19
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/index.js +5143 -1594
- package/package.json +11 -7
- package/dist/alert/evaluate.d.ts +0 -3
- package/dist/alert/evaluate.js +0 -197
- package/dist/env.d.ts +0 -2
- package/dist/env.js +0 -54
- package/dist/issue/state.d.ts +0 -39
- package/dist/issue/state.js +0 -151
- package/dist/otlp.d.ts +0 -5
- package/dist/otlp.js +0 -12
- package/dist/report/aggregate.d.ts +0 -21
- package/dist/report/aggregate.js +0 -245
- package/dist/runtime/claude/adapter.d.ts +0 -18
- package/dist/runtime/claude/adapter.js +0 -222
- package/dist/runtime/codex/adapter.d.ts +0 -18
- package/dist/runtime/codex/adapter.js +0 -493
- package/dist/runtime/codex/proxy.d.ts +0 -9
- package/dist/runtime/codex/proxy.js +0 -212
- package/dist/runtime/openclaw/adapter.d.ts +0 -18
- package/dist/runtime/openclaw/adapter.js +0 -380
- package/dist/runtime/shared/capabilities.d.ts +0 -30
- package/dist/runtime/shared/capabilities.js +0 -39
- package/dist/runtime/shared/file-settle.d.ts +0 -19
- package/dist/runtime/shared/file-settle.js +0 -44
- package/dist/runtime/shared/normalize.d.ts +0 -9
- package/dist/runtime/shared/normalize.js +0 -644
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bububuger/spanory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"description": "Spanory CLI for cross-runtime agent observability",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,21 +10,25 @@
|
|
|
10
10
|
"directory": "packages/cli"
|
|
11
11
|
},
|
|
12
12
|
"publishConfig": {
|
|
13
|
-
"access": "public"
|
|
13
|
+
"access": "public",
|
|
14
|
+
"registry": "https://registry.npmjs.org"
|
|
14
15
|
},
|
|
15
16
|
"files": [
|
|
16
|
-
"dist"
|
|
17
|
+
"dist/index.js",
|
|
18
|
+
"dist/index.d.ts"
|
|
17
19
|
],
|
|
18
20
|
"bin": {
|
|
19
21
|
"spanory": "dist/index.js"
|
|
20
22
|
},
|
|
21
23
|
"scripts": {
|
|
22
|
-
"check": "tsc -p tsconfig.runtime.json --noEmit",
|
|
23
|
-
"build": "
|
|
24
|
-
"
|
|
24
|
+
"check": "npm run --workspace @bububuger/core build && tsc -p tsconfig.runtime.json --noEmit",
|
|
25
|
+
"build:deps": "npm run --workspace @bububuger/core build && npm run --workspace @bububuger/otlp-core build && npm run --workspace @bububuger/backend-langfuse build",
|
|
26
|
+
"build": "npm run build:deps && node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.runtime.json && npx esbuild dist/index.js --bundle --platform=node --format=esm --target=node18 --external:node:* --external:commander --outfile=dist/index.bundle.js && node -e \"require('node:fs').renameSync('dist/index.bundle.js','dist/index.js')\" && chmod +x dist/index.js",
|
|
27
|
+
"test": "npm run build && vitest run test/unit",
|
|
25
28
|
"test:bdd": "npm run build && vitest run test/bdd",
|
|
29
|
+
"pack:test-install": "bash test/pack-test-install.sh",
|
|
26
30
|
"test:golden:update": "npm run build && node test/fixtures/golden/otlp/update-golden.mjs && node test/fixtures/golden/codex/update-golden.mjs",
|
|
27
|
-
"build:bundle": "npm run --workspace @bububuger/backend-langfuse build && npm run --workspace @bububuger/otlp-core build && npm run build && mkdir -p build && esbuild dist/index.js --bundle --platform=node --format=cjs --target=node18 --outfile=build/spanory.cjs",
|
|
31
|
+
"build:bundle": "npm run --workspace @bububuger/backend-langfuse build && npm run --workspace @bububuger/otlp-core build && npm run build && mkdir -p build && esbuild dist/index.js --bundle --platform=node --format=cjs --target=node18 --define:process.env.SPANORY_VERSION=\\\"$npm_package_version\\\" --outfile=build/spanory.cjs",
|
|
28
32
|
"build:bin:macos-arm64": "npm run build:bundle && pkg build/spanory.cjs --targets node18-macos-arm64 --output ../../dist/spanory-macos-arm64",
|
|
29
33
|
"build:bin:macos-x64": "npm run build:bundle && pkg build/spanory.cjs --targets node18-macos-x64 --output ../../dist/spanory-macos-x64",
|
|
30
34
|
"build:bin:linux-x64": "npm run build:bundle && pkg build/spanory.cjs --targets node18-linux-x64 --output ../../dist/spanory-linux-x64",
|
package/dist/alert/evaluate.d.ts
DELETED
package/dist/alert/evaluate.js
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { summarizeAgents, summarizeCache, summarizeCommands, summarizeMcp, summarizeSessions, summarizeTurnDiff, } from '../report/aggregate.js';
|
|
4
|
-
function getMetricFromSessionRow(row, metric, refs = {}) {
|
|
5
|
-
const cacheRow = refs.cacheBySessionId?.get(row.sessionId);
|
|
6
|
-
const agentRow = refs.agentBySessionId?.get(row.sessionId);
|
|
7
|
-
const turnDiffRows = refs.turnDiffBySessionId?.get(row.sessionId) ?? [];
|
|
8
|
-
switch (metric) {
|
|
9
|
-
case 'events':
|
|
10
|
-
return row.events ?? 0;
|
|
11
|
-
case 'turns':
|
|
12
|
-
return row.turns ?? 0;
|
|
13
|
-
case 'usage.total':
|
|
14
|
-
return row.usage?.total ?? 0;
|
|
15
|
-
case 'usage.input':
|
|
16
|
-
return row.usage?.input ?? 0;
|
|
17
|
-
case 'usage.output':
|
|
18
|
-
return row.usage?.output ?? 0;
|
|
19
|
-
case 'cache.read':
|
|
20
|
-
return cacheRow?.cacheReadInputTokens ?? 0;
|
|
21
|
-
case 'cache.creation':
|
|
22
|
-
return cacheRow?.cacheCreationInputTokens ?? 0;
|
|
23
|
-
case 'cache.hit_rate':
|
|
24
|
-
return cacheRow?.cacheHitRate ?? 0;
|
|
25
|
-
case 'subagent.calls':
|
|
26
|
-
return agentRow?.agentTasks ?? 0;
|
|
27
|
-
case 'diff.char_delta.max':
|
|
28
|
-
return turnDiffRows.reduce((max, rowItem) => Math.max(max, Math.abs(Number(rowItem.charDelta ?? 0))), 0);
|
|
29
|
-
default:
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function getMetricFromAgentRow(row, metric) {
|
|
34
|
-
switch (metric) {
|
|
35
|
-
case 'agentTasks':
|
|
36
|
-
return row.agentTasks ?? 0;
|
|
37
|
-
case 'shellCommands':
|
|
38
|
-
return row.shellCommands ?? 0;
|
|
39
|
-
case 'mcpCalls':
|
|
40
|
-
return row.mcpCalls ?? 0;
|
|
41
|
-
case 'usage.total':
|
|
42
|
-
return row.usage?.total ?? 0;
|
|
43
|
-
default:
|
|
44
|
-
return 0;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function compare(value, op, threshold) {
|
|
48
|
-
if (op === 'gt')
|
|
49
|
-
return value > threshold;
|
|
50
|
-
if (op === 'gte')
|
|
51
|
-
return value >= threshold;
|
|
52
|
-
if (op === 'lt')
|
|
53
|
-
return value < threshold;
|
|
54
|
-
if (op === 'lte')
|
|
55
|
-
return value <= threshold;
|
|
56
|
-
if (op === 'eq')
|
|
57
|
-
return value === threshold;
|
|
58
|
-
throw new Error(`unsupported operator: ${op}`);
|
|
59
|
-
}
|
|
60
|
-
function evaluateSessionRule(rule, sessions) {
|
|
61
|
-
const rows = summarizeSessions(sessions);
|
|
62
|
-
const cacheBySessionId = new Map(summarizeCache(sessions).map((row) => [row.sessionId, row]));
|
|
63
|
-
const agentBySessionId = new Map(summarizeAgents(sessions).map((row) => [row.sessionId, row]));
|
|
64
|
-
const turnDiffBySessionId = new Map();
|
|
65
|
-
for (const row of summarizeTurnDiff(sessions)) {
|
|
66
|
-
const key = row.sessionId;
|
|
67
|
-
const list = turnDiffBySessionId.get(key) ?? [];
|
|
68
|
-
list.push(row);
|
|
69
|
-
turnDiffBySessionId.set(key, list);
|
|
70
|
-
}
|
|
71
|
-
const matched = rows
|
|
72
|
-
.map((row) => {
|
|
73
|
-
const value = getMetricFromSessionRow(row, rule.metric, {
|
|
74
|
-
cacheBySessionId,
|
|
75
|
-
agentBySessionId,
|
|
76
|
-
turnDiffBySessionId,
|
|
77
|
-
});
|
|
78
|
-
return { row, value };
|
|
79
|
-
})
|
|
80
|
-
.filter((x) => compare(x.value, rule.op, Number(rule.threshold)));
|
|
81
|
-
return matched.map((m) => ({
|
|
82
|
-
ruleId: rule.id,
|
|
83
|
-
severity: rule.severity ?? 'warning',
|
|
84
|
-
scope: 'session',
|
|
85
|
-
metric: rule.metric,
|
|
86
|
-
op: rule.op,
|
|
87
|
-
threshold: Number(rule.threshold),
|
|
88
|
-
value: m.value,
|
|
89
|
-
context: {
|
|
90
|
-
sessionId: m.row.sessionId,
|
|
91
|
-
projectId: m.row.projectId,
|
|
92
|
-
runtime: m.row.runtime,
|
|
93
|
-
},
|
|
94
|
-
}));
|
|
95
|
-
}
|
|
96
|
-
function evaluateAgentRule(rule, sessions) {
|
|
97
|
-
const rows = summarizeAgents(sessions);
|
|
98
|
-
const matched = rows
|
|
99
|
-
.map((row) => {
|
|
100
|
-
const value = getMetricFromAgentRow(row, rule.metric);
|
|
101
|
-
return { row, value };
|
|
102
|
-
})
|
|
103
|
-
.filter((x) => compare(x.value, rule.op, Number(rule.threshold)));
|
|
104
|
-
return matched.map((m) => ({
|
|
105
|
-
ruleId: rule.id,
|
|
106
|
-
severity: rule.severity ?? 'warning',
|
|
107
|
-
scope: 'agent',
|
|
108
|
-
metric: rule.metric,
|
|
109
|
-
op: rule.op,
|
|
110
|
-
threshold: Number(rule.threshold),
|
|
111
|
-
value: m.value,
|
|
112
|
-
context: {
|
|
113
|
-
sessionId: m.row.sessionId,
|
|
114
|
-
projectId: m.row.projectId,
|
|
115
|
-
runtime: m.row.runtime,
|
|
116
|
-
},
|
|
117
|
-
}));
|
|
118
|
-
}
|
|
119
|
-
function evaluateMcpRule(rule, sessions) {
|
|
120
|
-
const rows = summarizeMcp(sessions);
|
|
121
|
-
const matched = rows.filter((row) => compare(row.calls ?? 0, rule.op, Number(rule.threshold)));
|
|
122
|
-
return matched.map((row) => ({
|
|
123
|
-
ruleId: rule.id,
|
|
124
|
-
severity: rule.severity ?? 'warning',
|
|
125
|
-
scope: 'mcp',
|
|
126
|
-
metric: 'calls',
|
|
127
|
-
op: rule.op,
|
|
128
|
-
threshold: Number(rule.threshold),
|
|
129
|
-
value: row.calls,
|
|
130
|
-
context: {
|
|
131
|
-
server: row.server,
|
|
132
|
-
},
|
|
133
|
-
}));
|
|
134
|
-
}
|
|
135
|
-
function evaluateCommandRule(rule, sessions) {
|
|
136
|
-
const rows = summarizeCommands(sessions);
|
|
137
|
-
const matched = rows.filter((row) => compare(row.calls ?? 0, rule.op, Number(rule.threshold)));
|
|
138
|
-
return matched.map((row) => ({
|
|
139
|
-
ruleId: rule.id,
|
|
140
|
-
severity: rule.severity ?? 'warning',
|
|
141
|
-
scope: 'command',
|
|
142
|
-
metric: 'calls',
|
|
143
|
-
op: rule.op,
|
|
144
|
-
threshold: Number(rule.threshold),
|
|
145
|
-
value: row.calls,
|
|
146
|
-
context: {
|
|
147
|
-
command: row.command,
|
|
148
|
-
},
|
|
149
|
-
}));
|
|
150
|
-
}
|
|
151
|
-
export async function loadAlertRules(path) {
|
|
152
|
-
const raw = await readFile(path, 'utf-8');
|
|
153
|
-
const parsed = JSON.parse(raw);
|
|
154
|
-
if (!Array.isArray(parsed.rules)) {
|
|
155
|
-
throw new Error('rule file must be JSON with {"rules": [...]}');
|
|
156
|
-
}
|
|
157
|
-
return parsed.rules;
|
|
158
|
-
}
|
|
159
|
-
export function evaluateRules(rules, sessions) {
|
|
160
|
-
const alerts = [];
|
|
161
|
-
for (const rule of rules) {
|
|
162
|
-
if (!rule.id || !rule.scope || !rule.metric || !rule.op) {
|
|
163
|
-
throw new Error(`invalid rule: ${JSON.stringify(rule)}`);
|
|
164
|
-
}
|
|
165
|
-
if (rule.scope === 'session') {
|
|
166
|
-
alerts.push(...evaluateSessionRule(rule, sessions));
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
if (rule.scope === 'agent') {
|
|
170
|
-
alerts.push(...evaluateAgentRule(rule, sessions));
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (rule.scope === 'mcp') {
|
|
174
|
-
alerts.push(...evaluateMcpRule(rule, sessions));
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
if (rule.scope === 'command') {
|
|
178
|
-
alerts.push(...evaluateCommandRule(rule, sessions));
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
throw new Error(`unsupported rule scope: ${rule.scope}`);
|
|
182
|
-
}
|
|
183
|
-
return alerts;
|
|
184
|
-
}
|
|
185
|
-
export async function sendAlertWebhook(url, payload, headers = {}) {
|
|
186
|
-
const response = await fetch(url, {
|
|
187
|
-
method: 'POST',
|
|
188
|
-
headers: {
|
|
189
|
-
'content-type': 'application/json',
|
|
190
|
-
...headers,
|
|
191
|
-
},
|
|
192
|
-
body: JSON.stringify(payload),
|
|
193
|
-
});
|
|
194
|
-
if (!response.ok) {
|
|
195
|
-
throw new Error(`webhook HTTP ${response.status}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
package/dist/env.d.ts
DELETED
package/dist/env.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { readFile } from 'node:fs/promises';
|
|
4
|
-
function resolveUserHome() {
|
|
5
|
-
return process.env.HOME || process.env.USERPROFILE || '';
|
|
6
|
-
}
|
|
7
|
-
export function parseSimpleDotEnv(raw) {
|
|
8
|
-
const out = {};
|
|
9
|
-
for (const line of String(raw).split('\n')) {
|
|
10
|
-
let s = line.trim();
|
|
11
|
-
if (!s || s.startsWith('#'))
|
|
12
|
-
continue;
|
|
13
|
-
if (s.startsWith('export ')) {
|
|
14
|
-
s = s.slice('export '.length).trim();
|
|
15
|
-
}
|
|
16
|
-
const idx = s.indexOf('=');
|
|
17
|
-
if (idx <= 0)
|
|
18
|
-
continue;
|
|
19
|
-
const key = s.slice(0, idx).trim();
|
|
20
|
-
if (!key)
|
|
21
|
-
continue;
|
|
22
|
-
let value = s.slice(idx + 1).trim();
|
|
23
|
-
const quoted = (value.startsWith('"') && value.endsWith('"'))
|
|
24
|
-
|| (value.startsWith("'") && value.endsWith("'"));
|
|
25
|
-
if (quoted) {
|
|
26
|
-
value = value.slice(1, -1);
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
const commentIdx = value.indexOf(' #');
|
|
30
|
-
if (commentIdx >= 0) {
|
|
31
|
-
value = value.slice(0, commentIdx).trimEnd();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
out[key] = value;
|
|
35
|
-
}
|
|
36
|
-
return out;
|
|
37
|
-
}
|
|
38
|
-
export async function loadUserEnv() {
|
|
39
|
-
const home = resolveUserHome();
|
|
40
|
-
if (!home)
|
|
41
|
-
return;
|
|
42
|
-
const envPath = path.join(home, '.env');
|
|
43
|
-
try {
|
|
44
|
-
const raw = await readFile(envPath, 'utf-8');
|
|
45
|
-
const parsed = parseSimpleDotEnv(raw);
|
|
46
|
-
for (const [k, v] of Object.entries(parsed)) {
|
|
47
|
-
if (process.env[k] === undefined)
|
|
48
|
-
process.env[k] = v;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
// ignore missing ~/.env
|
|
53
|
-
}
|
|
54
|
-
}
|
package/dist/issue/state.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
export declare const ISSUE_STATUSES: readonly ["open", "in_progress", "blocked", "done"];
|
|
2
|
-
export type IssueStatus = (typeof ISSUE_STATUSES)[number];
|
|
3
|
-
export interface IssueItem {
|
|
4
|
-
id: string;
|
|
5
|
-
title: string;
|
|
6
|
-
source: string;
|
|
7
|
-
status: IssueStatus;
|
|
8
|
-
notes: string[];
|
|
9
|
-
createdAt: string;
|
|
10
|
-
updatedAt: string;
|
|
11
|
-
closedAt?: string;
|
|
12
|
-
}
|
|
13
|
-
export interface IssueState {
|
|
14
|
-
version: 1;
|
|
15
|
-
updatedAt: string;
|
|
16
|
-
issues: IssueItem[];
|
|
17
|
-
}
|
|
18
|
-
export interface PendingTodoItem {
|
|
19
|
-
id: string;
|
|
20
|
-
title: string;
|
|
21
|
-
source: string;
|
|
22
|
-
}
|
|
23
|
-
export declare function parsePendingTodoItems(todoContent: string, source?: string): PendingTodoItem[];
|
|
24
|
-
export declare function syncIssueState(prev: IssueState, pending: PendingTodoItem[], now?: string): {
|
|
25
|
-
state: IssueState;
|
|
26
|
-
added: number;
|
|
27
|
-
reopened: number;
|
|
28
|
-
autoClosed: number;
|
|
29
|
-
};
|
|
30
|
-
export declare function setIssueStatus(prev: IssueState, input: {
|
|
31
|
-
id: string;
|
|
32
|
-
status: string;
|
|
33
|
-
note?: string;
|
|
34
|
-
}, now?: string): IssueState;
|
|
35
|
-
export declare function createEmptyIssueState(now?: string): IssueState;
|
|
36
|
-
export declare function loadIssueState(filePath: string): Promise<IssueState>;
|
|
37
|
-
export declare function saveIssueState(filePath: string, state: IssueState): Promise<void>;
|
|
38
|
-
export declare function resolveIssueStatePath(input?: string): string;
|
|
39
|
-
export declare function resolveTodoPath(input?: string): string;
|
package/dist/issue/state.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
export const ISSUE_STATUSES = ['open', 'in_progress', 'blocked', 'done'];
|
|
4
|
-
function nowIso(now) {
|
|
5
|
-
return now ?? new Date().toISOString();
|
|
6
|
-
}
|
|
7
|
-
function normalizeId(id) {
|
|
8
|
-
return String(id ?? '').trim();
|
|
9
|
-
}
|
|
10
|
-
function assertStatus(status) {
|
|
11
|
-
if (!ISSUE_STATUSES.includes(status)) {
|
|
12
|
-
throw new Error(`unsupported issue status: ${status}`);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
function assertAllowedTransition(from, to) {
|
|
16
|
-
if (from === to)
|
|
17
|
-
return;
|
|
18
|
-
if (from === 'done' && to !== 'done') {
|
|
19
|
-
throw new Error('cannot transition issue from done to non-done status');
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export function parsePendingTodoItems(todoContent, source = 'todo.md') {
|
|
23
|
-
const lines = String(todoContent ?? '').split(/\r?\n/);
|
|
24
|
-
const items = [];
|
|
25
|
-
for (const line of lines) {
|
|
26
|
-
const m = line.match(/^\s*-\s*\[\s\]\s+([A-Za-z0-9_-]+)\s+(.*)$/);
|
|
27
|
-
if (!m)
|
|
28
|
-
continue;
|
|
29
|
-
items.push({
|
|
30
|
-
id: normalizeId(m[1]),
|
|
31
|
-
title: m[2].trim(),
|
|
32
|
-
source,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
return items;
|
|
36
|
-
}
|
|
37
|
-
export function syncIssueState(prev, pending, now) {
|
|
38
|
-
const timestamp = nowIso(now);
|
|
39
|
-
const byId = new Map(prev.issues.map((issue) => [issue.id, issue]));
|
|
40
|
-
const activeIds = new Set(pending.map((item) => item.id));
|
|
41
|
-
let added = 0;
|
|
42
|
-
let reopened = 0;
|
|
43
|
-
let autoClosed = 0;
|
|
44
|
-
for (const item of pending) {
|
|
45
|
-
const existing = byId.get(item.id);
|
|
46
|
-
if (!existing) {
|
|
47
|
-
byId.set(item.id, {
|
|
48
|
-
id: item.id,
|
|
49
|
-
title: item.title,
|
|
50
|
-
source: item.source,
|
|
51
|
-
status: 'open',
|
|
52
|
-
notes: ['synced from todo pending item'],
|
|
53
|
-
createdAt: timestamp,
|
|
54
|
-
updatedAt: timestamp,
|
|
55
|
-
});
|
|
56
|
-
added += 1;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
existing.title = item.title;
|
|
60
|
-
existing.source = item.source;
|
|
61
|
-
existing.updatedAt = timestamp;
|
|
62
|
-
if (existing.status === 'done') {
|
|
63
|
-
existing.status = 'open';
|
|
64
|
-
existing.closedAt = undefined;
|
|
65
|
-
existing.notes.push('reopened by todo pending item');
|
|
66
|
-
reopened += 1;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
for (const issue of byId.values()) {
|
|
70
|
-
if (issue.source !== 'todo.md')
|
|
71
|
-
continue;
|
|
72
|
-
if (activeIds.has(issue.id))
|
|
73
|
-
continue;
|
|
74
|
-
if (issue.status === 'done')
|
|
75
|
-
continue;
|
|
76
|
-
issue.status = 'done';
|
|
77
|
-
issue.updatedAt = timestamp;
|
|
78
|
-
issue.closedAt = timestamp;
|
|
79
|
-
issue.notes.push('auto-closed because todo item is no longer pending');
|
|
80
|
-
autoClosed += 1;
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
state: {
|
|
84
|
-
version: 1,
|
|
85
|
-
updatedAt: timestamp,
|
|
86
|
-
issues: Array.from(byId.values()).sort((a, b) => a.id.localeCompare(b.id)),
|
|
87
|
-
},
|
|
88
|
-
added,
|
|
89
|
-
reopened,
|
|
90
|
-
autoClosed,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
export function setIssueStatus(prev, input, now) {
|
|
94
|
-
const issueId = normalizeId(input.id);
|
|
95
|
-
const timestamp = nowIso(now);
|
|
96
|
-
assertStatus(input.status);
|
|
97
|
-
const issue = prev.issues.find((item) => item.id === issueId);
|
|
98
|
-
if (!issue)
|
|
99
|
-
throw new Error(`issue not found: ${issueId}`);
|
|
100
|
-
assertAllowedTransition(issue.status, input.status);
|
|
101
|
-
issue.status = input.status;
|
|
102
|
-
issue.updatedAt = timestamp;
|
|
103
|
-
if (input.status === 'done')
|
|
104
|
-
issue.closedAt = timestamp;
|
|
105
|
-
if (input.status !== 'done')
|
|
106
|
-
issue.closedAt = undefined;
|
|
107
|
-
if (input.note && input.note.trim())
|
|
108
|
-
issue.notes.push(input.note.trim());
|
|
109
|
-
return {
|
|
110
|
-
version: 1,
|
|
111
|
-
updatedAt: timestamp,
|
|
112
|
-
issues: prev.issues,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
export function createEmptyIssueState(now) {
|
|
116
|
-
return {
|
|
117
|
-
version: 1,
|
|
118
|
-
updatedAt: nowIso(now),
|
|
119
|
-
issues: [],
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
export async function loadIssueState(filePath) {
|
|
123
|
-
try {
|
|
124
|
-
const raw = await readFile(filePath, 'utf-8');
|
|
125
|
-
const parsed = JSON.parse(raw);
|
|
126
|
-
if (!parsed || !Array.isArray(parsed.issues)) {
|
|
127
|
-
throw new Error('invalid issue state file');
|
|
128
|
-
}
|
|
129
|
-
return {
|
|
130
|
-
version: 1,
|
|
131
|
-
updatedAt: String(parsed.updatedAt ?? new Date(0).toISOString()),
|
|
132
|
-
issues: parsed.issues,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
if (error?.code === 'ENOENT') {
|
|
137
|
-
return createEmptyIssueState();
|
|
138
|
-
}
|
|
139
|
-
throw error;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
export async function saveIssueState(filePath, state) {
|
|
143
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
144
|
-
await writeFile(filePath, `${JSON.stringify(state, null, 2)}\n`, 'utf-8');
|
|
145
|
-
}
|
|
146
|
-
export function resolveIssueStatePath(input) {
|
|
147
|
-
return input ? path.resolve(process.cwd(), input) : path.resolve(process.cwd(), 'docs/issues/state.json');
|
|
148
|
-
}
|
|
149
|
-
export function resolveTodoPath(input) {
|
|
150
|
-
return input ? path.resolve(process.cwd(), input) : path.resolve(process.cwd(), 'todo.md');
|
|
151
|
-
}
|
package/dist/otlp.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { buildResource } from '../../otlp-core/dist/index.js';
|
|
2
|
-
export { buildResource };
|
|
3
|
-
export declare function parseHeaders(input: any): any;
|
|
4
|
-
export declare function compileOtlp(events: any, resource: any): any;
|
|
5
|
-
export declare function sendOtlp(endpoint: any, payload: any, headers?: {}): Promise<void>;
|
package/dist/otlp.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
import { buildResource, compileOtlpSpans, parseOtlpHeaders, sendOtlpHttp, } from '../../otlp-core/dist/index.js';
|
|
3
|
-
export { buildResource };
|
|
4
|
-
export function parseHeaders(input) {
|
|
5
|
-
return parseOtlpHeaders(input);
|
|
6
|
-
}
|
|
7
|
-
export function compileOtlp(events, resource) {
|
|
8
|
-
return compileOtlpSpans(events, resource);
|
|
9
|
-
}
|
|
10
|
-
export async function sendOtlp(endpoint, payload, headers = {}) {
|
|
11
|
-
await sendOtlpHttp(endpoint, payload, headers);
|
|
12
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export declare function loadExportedEvents(inputPath: any): Promise<any[]>;
|
|
2
|
-
export declare function summarizeSessions(sessions: any): any;
|
|
3
|
-
export declare function summarizeMcp(sessions: any): {
|
|
4
|
-
server: any;
|
|
5
|
-
calls: any;
|
|
6
|
-
sessions: any;
|
|
7
|
-
}[];
|
|
8
|
-
export declare function summarizeCommands(sessions: any): {
|
|
9
|
-
command: any;
|
|
10
|
-
calls: any;
|
|
11
|
-
sessions: any;
|
|
12
|
-
}[];
|
|
13
|
-
export declare function summarizeAgents(sessions: any): any[];
|
|
14
|
-
export declare function summarizeCache(sessions: any): any;
|
|
15
|
-
export declare function summarizeTools(sessions: any): {
|
|
16
|
-
category: any;
|
|
17
|
-
tool: any;
|
|
18
|
-
calls: any;
|
|
19
|
-
sessions: any;
|
|
20
|
-
}[];
|
|
21
|
-
export declare function summarizeTurnDiff(sessions: any): any[];
|