homingo 0.1.0
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 +28 -0
- package/LICENSE +68 -0
- package/README.md +249 -0
- package/dist/commands/audit.d.ts +14 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +178 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +240 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lint.d.ts +20 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +490 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/logs.d.ts +8 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +67 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +67 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/anthropic.d.ts +7 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +29 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.d.ts +20 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +43 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +7 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +32 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/tracked-provider.d.ts +15 -0
- package/dist/providers/tracked-provider.d.ts.map +1 -0
- package/dist/providers/tracked-provider.js +26 -0
- package/dist/providers/tracked-provider.js.map +1 -0
- package/dist/providers/types.d.ts +23 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/reporting/html-renderer.d.ts +26 -0
- package/dist/reporting/html-renderer.d.ts.map +1 -0
- package/dist/reporting/html-renderer.js +549 -0
- package/dist/reporting/html-renderer.js.map +1 -0
- package/dist/reporting/logs-viewer.d.ts +12 -0
- package/dist/reporting/logs-viewer.d.ts.map +1 -0
- package/dist/reporting/logs-viewer.js +221 -0
- package/dist/reporting/logs-viewer.js.map +1 -0
- package/dist/reporting/opener.d.ts +9 -0
- package/dist/reporting/opener.d.ts.map +1 -0
- package/dist/reporting/opener.js +49 -0
- package/dist/reporting/opener.js.map +1 -0
- package/dist/reporting/run-metadata.d.ts +55 -0
- package/dist/reporting/run-metadata.d.ts.map +1 -0
- package/dist/reporting/run-metadata.js +61 -0
- package/dist/reporting/run-metadata.js.map +1 -0
- package/dist/reporting/storage.d.ts +27 -0
- package/dist/reporting/storage.d.ts.map +1 -0
- package/dist/reporting/storage.js +126 -0
- package/dist/reporting/storage.js.map +1 -0
- package/dist/rewriter/rewriter.d.ts +21 -0
- package/dist/rewriter/rewriter.d.ts.map +1 -0
- package/dist/rewriter/rewriter.js +63 -0
- package/dist/rewriter/rewriter.js.map +1 -0
- package/dist/shadow-router/generator.d.ts +14 -0
- package/dist/shadow-router/generator.d.ts.map +1 -0
- package/dist/shadow-router/generator.js +58 -0
- package/dist/shadow-router/generator.js.map +1 -0
- package/dist/shadow-router/pair-selector.d.ts +17 -0
- package/dist/shadow-router/pair-selector.d.ts.map +1 -0
- package/dist/shadow-router/pair-selector.js +146 -0
- package/dist/shadow-router/pair-selector.js.map +1 -0
- package/dist/shadow-router/scorer.d.ts +4 -0
- package/dist/shadow-router/scorer.d.ts.map +1 -0
- package/dist/shadow-router/scorer.js +169 -0
- package/dist/shadow-router/scorer.js.map +1 -0
- package/dist/shadow-router/simulator.d.ts +17 -0
- package/dist/shadow-router/simulator.d.ts.map +1 -0
- package/dist/shadow-router/simulator.js +64 -0
- package/dist/shadow-router/simulator.js.map +1 -0
- package/dist/shard/analyzer.d.ts +28 -0
- package/dist/shard/analyzer.d.ts.map +1 -0
- package/dist/shard/analyzer.js +146 -0
- package/dist/shard/analyzer.js.map +1 -0
- package/dist/shard/writer.d.ts +11 -0
- package/dist/shard/writer.d.ts.map +1 -0
- package/dist/shard/writer.js +53 -0
- package/dist/shard/writer.js.map +1 -0
- package/dist/skills/parser.d.ts +3 -0
- package/dist/skills/parser.d.ts.map +1 -0
- package/dist/skills/parser.js +48 -0
- package/dist/skills/parser.js.map +1 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/concurrency.d.ts +2 -0
- package/dist/utils/concurrency.d.ts.map +1 -0
- package/dist/utils/concurrency.js +14 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/retry.d.ts +7 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +44 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a self-contained HTML page that lets the user browse
|
|
3
|
+
* the last 10 runs per command with tabs and "View Report" links.
|
|
4
|
+
*/
|
|
5
|
+
export function generateLogsViewer(allRuns) {
|
|
6
|
+
const commands = ["audit", "lint"];
|
|
7
|
+
const runData = JSON.stringify(allRuns);
|
|
8
|
+
const tabs = commands
|
|
9
|
+
.map((cmd) => `<button class="tab-btn" data-tab="${cmd}" onclick="switchTab('${cmd}')">${capitalize(cmd)} <span class="badge">${allRuns[cmd]?.length ?? 0}</span></button>`)
|
|
10
|
+
.join("\n ");
|
|
11
|
+
const panels = commands
|
|
12
|
+
.map((cmd) => {
|
|
13
|
+
const runs = allRuns[cmd] || [];
|
|
14
|
+
if (runs.length === 0) {
|
|
15
|
+
return `<div class="tab-panel" id="panel-${cmd}"><p class="empty">No ${cmd} runs recorded yet.</p></div>`;
|
|
16
|
+
}
|
|
17
|
+
let rows = "";
|
|
18
|
+
for (const entry of runs) {
|
|
19
|
+
const m = entry.metadata;
|
|
20
|
+
const date = m.timestamp.slice(0, 19).replace("T", " ");
|
|
21
|
+
const duration = (m.durationMs / 1000).toFixed(1);
|
|
22
|
+
const totalTokens = m.tokens.total.toLocaleString();
|
|
23
|
+
const summary = getResultSummary(m);
|
|
24
|
+
rows += `<tr>
|
|
25
|
+
<td>${esc(date)}</td>
|
|
26
|
+
<td>${duration}s</td>
|
|
27
|
+
<td>${esc(m.model)}</td>
|
|
28
|
+
<td>${m.skillCount}</td>
|
|
29
|
+
<td>${totalTokens}</td>
|
|
30
|
+
<td>${esc(summary)}</td>
|
|
31
|
+
<td><a href="file://${esc(entry.htmlPath)}" class="view-link">View Report</a></td>
|
|
32
|
+
</tr>`;
|
|
33
|
+
}
|
|
34
|
+
return `<div class="tab-panel" id="panel-${cmd}">
|
|
35
|
+
<table>
|
|
36
|
+
<thead>
|
|
37
|
+
<tr><th>Date</th><th>Duration</th><th>Model</th><th>Skills</th><th>Tokens</th><th>Result</th><th></th></tr>
|
|
38
|
+
</thead>
|
|
39
|
+
<tbody>${rows}</tbody>
|
|
40
|
+
</table>
|
|
41
|
+
</div>`;
|
|
42
|
+
})
|
|
43
|
+
.join("\n ");
|
|
44
|
+
return `<!DOCTYPE html>
|
|
45
|
+
<html lang="en">
|
|
46
|
+
<head>
|
|
47
|
+
<meta charset="utf-8">
|
|
48
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
49
|
+
<title>Homingo — Run History</title>
|
|
50
|
+
<style>${VIEWER_CSS}</style>
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
<header>
|
|
54
|
+
<h1>🐦 Homingo Run History</h1>
|
|
55
|
+
</header>
|
|
56
|
+
<nav class="tab-bar">
|
|
57
|
+
${tabs}
|
|
58
|
+
</nav>
|
|
59
|
+
<main>
|
|
60
|
+
${panels}
|
|
61
|
+
</main>
|
|
62
|
+
<footer>
|
|
63
|
+
<p>Generated by <strong>Homingo</strong></p>
|
|
64
|
+
</footer>
|
|
65
|
+
<script>
|
|
66
|
+
const runData = ${runData};
|
|
67
|
+
function switchTab(cmd) {
|
|
68
|
+
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
|
69
|
+
document.querySelectorAll('.tab-panel').forEach(p => p.style.display = 'none');
|
|
70
|
+
document.querySelector('[data-tab="' + cmd + '"]').classList.add('active');
|
|
71
|
+
document.getElementById('panel-' + cmd).style.display = 'block';
|
|
72
|
+
}
|
|
73
|
+
// Activate first tab with runs, or first tab
|
|
74
|
+
const first = ${JSON.stringify(commands)}.find(c => runData[c] && runData[c].length > 0) || 'audit';
|
|
75
|
+
switchTab(first);
|
|
76
|
+
</script>
|
|
77
|
+
</body>
|
|
78
|
+
</html>`;
|
|
79
|
+
}
|
|
80
|
+
function getResultSummary(m) {
|
|
81
|
+
const r = m.result;
|
|
82
|
+
switch (r.type) {
|
|
83
|
+
case "audit":
|
|
84
|
+
return `${r.fleetErrorRate}% error rate, ${r.criticalCount} critical`;
|
|
85
|
+
case "lint":
|
|
86
|
+
return `${r.passingCount} pass, ${r.failingCount} fail, ${r.overloadedSkills} overloaded`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function esc(s) {
|
|
90
|
+
return s
|
|
91
|
+
.replace(/&/g, "&")
|
|
92
|
+
.replace(/</g, "<")
|
|
93
|
+
.replace(/>/g, ">")
|
|
94
|
+
.replace(/"/g, """);
|
|
95
|
+
}
|
|
96
|
+
function capitalize(s) {
|
|
97
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
98
|
+
}
|
|
99
|
+
const VIEWER_CSS = `
|
|
100
|
+
:root {
|
|
101
|
+
--bg: #f8fafc;
|
|
102
|
+
--card-bg: #ffffff;
|
|
103
|
+
--text: #1e293b;
|
|
104
|
+
--text-muted: #64748b;
|
|
105
|
+
--border: #e2e8f0;
|
|
106
|
+
--accent: #6366f1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
110
|
+
|
|
111
|
+
body {
|
|
112
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
|
|
113
|
+
background: var(--bg);
|
|
114
|
+
color: var(--text);
|
|
115
|
+
line-height: 1.6;
|
|
116
|
+
padding: 2rem;
|
|
117
|
+
max-width: 1200px;
|
|
118
|
+
margin: 0 auto;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
header h1 { font-size: 1.5rem; margin-bottom: 1rem; }
|
|
122
|
+
|
|
123
|
+
.tab-bar {
|
|
124
|
+
display: flex;
|
|
125
|
+
gap: 0.5rem;
|
|
126
|
+
margin-bottom: 1.5rem;
|
|
127
|
+
border-bottom: 2px solid var(--border);
|
|
128
|
+
padding-bottom: 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.tab-btn {
|
|
132
|
+
background: none;
|
|
133
|
+
border: none;
|
|
134
|
+
padding: 0.5rem 1rem;
|
|
135
|
+
font-size: 0.9rem;
|
|
136
|
+
cursor: pointer;
|
|
137
|
+
color: var(--text-muted);
|
|
138
|
+
border-bottom: 2px solid transparent;
|
|
139
|
+
margin-bottom: -2px;
|
|
140
|
+
transition: color 0.15s, border-color 0.15s;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.tab-btn:hover { color: var(--text); }
|
|
144
|
+
.tab-btn.active {
|
|
145
|
+
color: var(--accent);
|
|
146
|
+
border-bottom-color: var(--accent);
|
|
147
|
+
font-weight: 600;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.badge {
|
|
151
|
+
display: inline-block;
|
|
152
|
+
background: var(--border);
|
|
153
|
+
color: var(--text-muted);
|
|
154
|
+
font-size: 0.7rem;
|
|
155
|
+
padding: 1px 6px;
|
|
156
|
+
border-radius: 10px;
|
|
157
|
+
margin-left: 4px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.tab-btn.active .badge {
|
|
161
|
+
background: var(--accent);
|
|
162
|
+
color: #fff;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.tab-panel { display: none; }
|
|
166
|
+
|
|
167
|
+
.empty {
|
|
168
|
+
text-align: center;
|
|
169
|
+
color: var(--text-muted);
|
|
170
|
+
padding: 3rem 0;
|
|
171
|
+
font-size: 0.95rem;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
table {
|
|
175
|
+
width: 100%;
|
|
176
|
+
border-collapse: collapse;
|
|
177
|
+
background: var(--card-bg);
|
|
178
|
+
border-radius: 8px;
|
|
179
|
+
overflow: hidden;
|
|
180
|
+
border: 1px solid var(--border);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
thead th {
|
|
184
|
+
text-align: left;
|
|
185
|
+
padding: 0.75rem 1rem;
|
|
186
|
+
background: #f1f5f9;
|
|
187
|
+
font-size: 0.8rem;
|
|
188
|
+
text-transform: uppercase;
|
|
189
|
+
letter-spacing: 0.05em;
|
|
190
|
+
color: var(--text-muted);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
tbody td {
|
|
194
|
+
padding: 0.75rem 1rem;
|
|
195
|
+
border-top: 1px solid var(--border);
|
|
196
|
+
font-size: 0.9rem;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.view-link {
|
|
200
|
+
color: var(--accent);
|
|
201
|
+
text-decoration: none;
|
|
202
|
+
font-weight: 500;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.view-link:hover { text-decoration: underline; }
|
|
206
|
+
|
|
207
|
+
footer {
|
|
208
|
+
margin-top: 3rem;
|
|
209
|
+
padding-top: 1rem;
|
|
210
|
+
border-top: 1px solid var(--border);
|
|
211
|
+
text-align: center;
|
|
212
|
+
font-size: 0.8rem;
|
|
213
|
+
color: var(--text-muted);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@media (max-width: 768px) {
|
|
217
|
+
body { padding: 1rem; }
|
|
218
|
+
.tab-bar { flex-wrap: wrap; }
|
|
219
|
+
}
|
|
220
|
+
`;
|
|
221
|
+
//# sourceMappingURL=logs-viewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs-viewer.js","sourceRoot":"","sources":["../../src/reporting/logs-viewer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect headless/CI environments where opening a browser would fail.
|
|
3
|
+
*/
|
|
4
|
+
export declare function isHeadless(): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Open a file in the default browser. Fire-and-forget — errors are silently ignored.
|
|
7
|
+
*/
|
|
8
|
+
export declare function openInBrowser(filePath: string): void;
|
|
9
|
+
//# sourceMappingURL=opener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opener.d.ts","sourceRoot":"","sources":["../../src/reporting/opener.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAuBpC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAsBpD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
/**
|
|
4
|
+
* Detect headless/CI environments where opening a browser would fail.
|
|
5
|
+
*/
|
|
6
|
+
export function isHeadless() {
|
|
7
|
+
const env = process.env;
|
|
8
|
+
// Common CI environment variables
|
|
9
|
+
if (env.CI ||
|
|
10
|
+
env.GITHUB_ACTIONS ||
|
|
11
|
+
env.GITLAB_CI ||
|
|
12
|
+
env.JENKINS_URL ||
|
|
13
|
+
env.CIRCLECI ||
|
|
14
|
+
env.TRAVIS ||
|
|
15
|
+
env.CODEBUILD_BUILD_ID ||
|
|
16
|
+
env.BUILDKITE) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
// Linux without a display server
|
|
20
|
+
if (platform() === "linux" && !env.DISPLAY && !env.WAYLAND_DISPLAY) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Open a file in the default browser. Fire-and-forget — errors are silently ignored.
|
|
27
|
+
*/
|
|
28
|
+
export function openInBrowser(filePath) {
|
|
29
|
+
const os = platform();
|
|
30
|
+
let command;
|
|
31
|
+
switch (os) {
|
|
32
|
+
case "darwin":
|
|
33
|
+
command = `open "${filePath}"`;
|
|
34
|
+
break;
|
|
35
|
+
case "win32":
|
|
36
|
+
command = `start "" "${filePath}"`;
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
command = `xdg-open "${filePath}"`;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
exec(command, (err) => {
|
|
43
|
+
// Silently ignore errors — this is a convenience feature
|
|
44
|
+
if (err && process.env.DEBUG) {
|
|
45
|
+
console.error(`Failed to open browser: ${err.message}`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=opener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opener.js","sourceRoot":"","sources":["../../src/reporting/opener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAExB,kCAAkC;IAClC,IACE,GAAG,CAAC,EAAE;QACN,GAAG,CAAC,cAAc;QAClB,GAAG,CAAC,SAAS;QACb,GAAG,CAAC,WAAW;QACf,GAAG,CAAC,QAAQ;QACZ,GAAG,CAAC,MAAM;QACV,GAAG,CAAC,kBAAkB;QACtB,GAAG,CAAC,SAAS,EACb,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iCAAiC;IACjC,IAAI,QAAQ,EAAE,KAAK,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC;IACtB,IAAI,OAAe,CAAC;IAEpB,QAAQ,EAAE,EAAE,CAAC;QACX,KAAK,QAAQ;YACX,OAAO,GAAG,SAAS,QAAQ,GAAG,CAAC;YAC/B,MAAM;QACR,KAAK,OAAO;YACV,OAAO,GAAG,aAAa,QAAQ,GAAG,CAAC;YACnC,MAAM;QACR;YACE,OAAO,GAAG,aAAa,QAAQ,GAAG,CAAC;YACnC,MAAM;IACV,CAAC;IAED,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACpB,yDAAyD;QACzD,IAAI,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,2BAA2B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { TrackedProvider } from "../providers/tracked-provider.js";
|
|
2
|
+
export type CommandName = "audit" | "lint";
|
|
3
|
+
export interface AuditResult {
|
|
4
|
+
type: "audit";
|
|
5
|
+
fleetErrorRate: number;
|
|
6
|
+
pairsTested: number;
|
|
7
|
+
criticalCount: number;
|
|
8
|
+
highCount: number;
|
|
9
|
+
mediumCount: number;
|
|
10
|
+
lowCount: number;
|
|
11
|
+
}
|
|
12
|
+
export interface LintResult {
|
|
13
|
+
type: "lint";
|
|
14
|
+
targetSkill: string;
|
|
15
|
+
neighborsTested: number;
|
|
16
|
+
passingCount: number;
|
|
17
|
+
failingCount: number;
|
|
18
|
+
overallAccuracy: number;
|
|
19
|
+
overloadedSkills: number;
|
|
20
|
+
shardPlansGenerated: number;
|
|
21
|
+
shardsApplied: boolean;
|
|
22
|
+
}
|
|
23
|
+
export type CommandResult = AuditResult | LintResult;
|
|
24
|
+
export interface RunMetadata {
|
|
25
|
+
id: string;
|
|
26
|
+
timestamp: string;
|
|
27
|
+
durationMs: number;
|
|
28
|
+
command: CommandName;
|
|
29
|
+
args: Record<string, unknown>;
|
|
30
|
+
model: string;
|
|
31
|
+
provider: "anthropic" | "openai";
|
|
32
|
+
skillCount: number;
|
|
33
|
+
tokens: {
|
|
34
|
+
input: number;
|
|
35
|
+
output: number;
|
|
36
|
+
total: number;
|
|
37
|
+
};
|
|
38
|
+
gitCommitHash: string | null;
|
|
39
|
+
result: CommandResult;
|
|
40
|
+
}
|
|
41
|
+
export declare class RunCollector {
|
|
42
|
+
private id;
|
|
43
|
+
private startTime;
|
|
44
|
+
private command;
|
|
45
|
+
private args;
|
|
46
|
+
private model;
|
|
47
|
+
private providerName;
|
|
48
|
+
private _skillCount;
|
|
49
|
+
private _result;
|
|
50
|
+
constructor(command: CommandName, args: Record<string, unknown>, model: string, providerName: "anthropic" | "openai");
|
|
51
|
+
setSkillCount(n: number): void;
|
|
52
|
+
setResult(result: CommandResult): void;
|
|
53
|
+
finalize(tracked: TrackedProvider): RunMetadata;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=run-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-metadata.d.ts","sourceRoot":"","sources":["../../src/reporting/run-metadata.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAIxE,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAI3C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;AAIrD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,WAAW,GAAG,QAAQ,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,aAAa,CAAC;CACvB;AAID,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,OAAO,CAA8B;gBAG3C,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,WAAW,GAAG,QAAQ;IAUtC,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAI9B,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAItC,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW;CA0BhD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
// ── RunCollector ───────────────────────────────────────────────
|
|
4
|
+
export class RunCollector {
|
|
5
|
+
id;
|
|
6
|
+
startTime;
|
|
7
|
+
command;
|
|
8
|
+
args;
|
|
9
|
+
model;
|
|
10
|
+
providerName;
|
|
11
|
+
_skillCount = 0;
|
|
12
|
+
_result = null;
|
|
13
|
+
constructor(command, args, model, providerName) {
|
|
14
|
+
this.id = randomUUID();
|
|
15
|
+
this.startTime = Date.now();
|
|
16
|
+
this.command = command;
|
|
17
|
+
this.args = args;
|
|
18
|
+
this.model = model;
|
|
19
|
+
this.providerName = providerName;
|
|
20
|
+
}
|
|
21
|
+
setSkillCount(n) {
|
|
22
|
+
this._skillCount = n;
|
|
23
|
+
}
|
|
24
|
+
setResult(result) {
|
|
25
|
+
this._result = result;
|
|
26
|
+
}
|
|
27
|
+
finalize(tracked) {
|
|
28
|
+
if (!this._result) {
|
|
29
|
+
throw new Error("RunCollector.finalize() called before setResult()");
|
|
30
|
+
}
|
|
31
|
+
const usage = tracked.totalUsage;
|
|
32
|
+
const durationMs = Date.now() - this.startTime;
|
|
33
|
+
return {
|
|
34
|
+
id: this.id,
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
durationMs,
|
|
37
|
+
command: this.command,
|
|
38
|
+
args: this.args,
|
|
39
|
+
model: this.model,
|
|
40
|
+
provider: this.providerName,
|
|
41
|
+
skillCount: this._skillCount,
|
|
42
|
+
tokens: {
|
|
43
|
+
input: usage.inputTokens,
|
|
44
|
+
output: usage.outputTokens,
|
|
45
|
+
total: usage.inputTokens + usage.outputTokens,
|
|
46
|
+
},
|
|
47
|
+
gitCommitHash: getGitCommitHash(),
|
|
48
|
+
result: this._result,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// ── Helpers ────────────────────────────────────────────────────
|
|
53
|
+
function getGitCommitHash() {
|
|
54
|
+
try {
|
|
55
|
+
return execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=run-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-metadata.js","sourceRoot":"","sources":["../../src/reporting/run-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAiD9C,kEAAkE;AAElE,MAAM,OAAO,YAAY;IACf,EAAE,CAAS;IACX,SAAS,CAAS;IAClB,OAAO,CAAc;IACrB,IAAI,CAA0B;IAC9B,KAAK,CAAS;IACd,YAAY,CAAyB;IACrC,WAAW,GAAG,CAAC,CAAC;IAChB,OAAO,GAAyB,IAAI,CAAC;IAE7C,YACE,OAAoB,EACpB,IAA6B,EAC7B,KAAa,EACb,YAAoC;QAEpC,IAAI,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED,aAAa,CAAC,CAAS;QACrB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IACvB,CAAC;IAED,SAAS,CAAC,MAAqB;QAC7B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED,QAAQ,CAAC,OAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;QACjC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;QAE/C,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,UAAU;YACV,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,YAAY;YAC3B,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,MAAM,EAAE;gBACN,KAAK,EAAE,KAAK,CAAC,WAAW;gBACxB,MAAM,EAAE,KAAK,CAAC,YAAY;gBAC1B,KAAK,EAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,YAAY;aAC9C;YACD,aAAa,EAAE,gBAAgB,EAAE;YACjC,MAAM,EAAE,IAAI,CAAC,OAAO;SACrB,CAAC;IACJ,CAAC;CACF;AAED,kEAAkE;AAElE,SAAS,gBAAgB;IACvB,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,4BAA4B,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CommandName, RunMetadata } from "./run-metadata.js";
|
|
2
|
+
export interface StoredRun {
|
|
3
|
+
metadataPath: string;
|
|
4
|
+
htmlPath: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Persist a run's metadata + command data as JSON and generate an HTML report.
|
|
8
|
+
* Returns paths to both files.
|
|
9
|
+
*/
|
|
10
|
+
export declare function saveRun(command: CommandName, metadata: RunMetadata, commandData: unknown, reportDir: string): StoredRun;
|
|
11
|
+
/**
|
|
12
|
+
* Delete the oldest runs beyond `maxRuns`, identified by .json files.
|
|
13
|
+
* Deletes both .json and .html for each evicted run.
|
|
14
|
+
*/
|
|
15
|
+
export declare function enforceRetention(commandDir: string, maxRuns: number): void;
|
|
16
|
+
/**
|
|
17
|
+
* Scan all command directories and regenerate the top-level logs-viewer.html.
|
|
18
|
+
* Called automatically after every saveRun so the viewer is always current.
|
|
19
|
+
*/
|
|
20
|
+
export declare function refreshLogsViewer(reportDir: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Re-render every stored HTML report from its JSON source.
|
|
23
|
+
* Useful after template changes so existing reports pick up new markup (e.g. nav buttons).
|
|
24
|
+
* Returns the number of reports regenerated.
|
|
25
|
+
*/
|
|
26
|
+
export declare function regenerateReports(reportDir: string): number;
|
|
27
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/reporting/storage.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOlE,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,OAAO,EACpB,SAAS,EAAE,MAAM,GAChB,SAAS,CA0BX;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CA0B1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAgCzD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA4B3D"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, readdirSync, readFileSync, unlinkSync, existsSync, } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { generateHtml } from "./html-renderer.js";
|
|
4
|
+
import { generateLogsViewer } from "./logs-viewer.js";
|
|
5
|
+
const MAX_RUNS_PER_COMMAND = 10;
|
|
6
|
+
/**
|
|
7
|
+
* Persist a run's metadata + command data as JSON and generate an HTML report.
|
|
8
|
+
* Returns paths to both files.
|
|
9
|
+
*/
|
|
10
|
+
export function saveRun(command, metadata, commandData, reportDir) {
|
|
11
|
+
const commandDir = join(reportDir, command);
|
|
12
|
+
mkdirSync(commandDir, { recursive: true });
|
|
13
|
+
// Filename: timestamp (colons → hyphens) + short ID
|
|
14
|
+
const safeTimestamp = metadata.timestamp.replace(/:/g, "-").replace(/\.\d+Z$/, "Z");
|
|
15
|
+
const shortId = metadata.id.slice(0, 8);
|
|
16
|
+
const baseName = `${safeTimestamp}-${shortId}`;
|
|
17
|
+
const metadataPath = join(commandDir, `${baseName}.json`);
|
|
18
|
+
const htmlPath = join(commandDir, `${baseName}.html`);
|
|
19
|
+
// Write JSON
|
|
20
|
+
writeFileSync(metadataPath, JSON.stringify({ metadata, data: commandData }, null, 2));
|
|
21
|
+
// Generate and write HTML
|
|
22
|
+
const html = generateHtml(command, metadata, commandData);
|
|
23
|
+
writeFileSync(htmlPath, html);
|
|
24
|
+
// Enforce retention — keep last N runs
|
|
25
|
+
enforceRetention(commandDir, MAX_RUNS_PER_COMMAND);
|
|
26
|
+
// Regenerate the logs viewer so it's always up-to-date
|
|
27
|
+
refreshLogsViewer(reportDir);
|
|
28
|
+
return { metadataPath, htmlPath };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Delete the oldest runs beyond `maxRuns`, identified by .json files.
|
|
32
|
+
* Deletes both .json and .html for each evicted run.
|
|
33
|
+
*/
|
|
34
|
+
export function enforceRetention(commandDir, maxRuns) {
|
|
35
|
+
if (!existsSync(commandDir))
|
|
36
|
+
return;
|
|
37
|
+
const jsonFiles = readdirSync(commandDir)
|
|
38
|
+
.filter((f) => f.endsWith(".json"))
|
|
39
|
+
.sort(); // lexicographic = chronological for ISO timestamps
|
|
40
|
+
if (jsonFiles.length <= maxRuns)
|
|
41
|
+
return;
|
|
42
|
+
const toRemove = jsonFiles.slice(0, jsonFiles.length - maxRuns);
|
|
43
|
+
for (const jsonFile of toRemove) {
|
|
44
|
+
const jsonPath = join(commandDir, jsonFile);
|
|
45
|
+
const htmlFile = jsonFile.replace(/\.json$/, ".html");
|
|
46
|
+
const htmlPath = join(commandDir, htmlFile);
|
|
47
|
+
try {
|
|
48
|
+
unlinkSync(jsonPath);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
/* ignore */
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
unlinkSync(htmlPath);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
/* ignore */
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Scan all command directories and regenerate the top-level logs-viewer.html.
|
|
63
|
+
* Called automatically after every saveRun so the viewer is always current.
|
|
64
|
+
*/
|
|
65
|
+
export function refreshLogsViewer(reportDir) {
|
|
66
|
+
const commands = ["audit", "lint"];
|
|
67
|
+
const allRuns = { audit: [], lint: [] };
|
|
68
|
+
for (const cmd of commands) {
|
|
69
|
+
const commandDir = join(reportDir, cmd);
|
|
70
|
+
if (!existsSync(commandDir))
|
|
71
|
+
continue;
|
|
72
|
+
const jsonFiles = readdirSync(commandDir)
|
|
73
|
+
.filter((f) => f.endsWith(".json"))
|
|
74
|
+
.sort()
|
|
75
|
+
.reverse() // newest first
|
|
76
|
+
.slice(0, MAX_RUNS_PER_COMMAND);
|
|
77
|
+
for (const jsonFile of jsonFiles) {
|
|
78
|
+
const jsonPath = join(commandDir, jsonFile);
|
|
79
|
+
const htmlFile = jsonFile.replace(/\.json$/, ".html");
|
|
80
|
+
const htmlPath = join(commandDir, htmlFile);
|
|
81
|
+
try {
|
|
82
|
+
const raw = readFileSync(jsonPath, "utf-8");
|
|
83
|
+
const parsed = JSON.parse(raw);
|
|
84
|
+
const entry = { metadata: parsed.metadata, htmlPath };
|
|
85
|
+
allRuns[cmd].push(entry);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Skip corrupted files
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const viewerHtml = generateLogsViewer(allRuns);
|
|
93
|
+
writeFileSync(join(reportDir, "logs-viewer.html"), viewerHtml);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Re-render every stored HTML report from its JSON source.
|
|
97
|
+
* Useful after template changes so existing reports pick up new markup (e.g. nav buttons).
|
|
98
|
+
* Returns the number of reports regenerated.
|
|
99
|
+
*/
|
|
100
|
+
export function regenerateReports(reportDir) {
|
|
101
|
+
const commands = ["audit", "lint"];
|
|
102
|
+
let count = 0;
|
|
103
|
+
for (const cmd of commands) {
|
|
104
|
+
const commandDir = join(reportDir, cmd);
|
|
105
|
+
if (!existsSync(commandDir))
|
|
106
|
+
continue;
|
|
107
|
+
const jsonFiles = readdirSync(commandDir).filter((f) => f.endsWith(".json"));
|
|
108
|
+
for (const jsonFile of jsonFiles) {
|
|
109
|
+
const jsonPath = join(commandDir, jsonFile);
|
|
110
|
+
const htmlFile = jsonFile.replace(/\.json$/, ".html");
|
|
111
|
+
const htmlPath = join(commandDir, htmlFile);
|
|
112
|
+
try {
|
|
113
|
+
const raw = readFileSync(jsonPath, "utf-8");
|
|
114
|
+
const parsed = JSON.parse(raw);
|
|
115
|
+
const html = generateHtml(cmd, parsed.metadata, parsed.data);
|
|
116
|
+
writeFileSync(htmlPath, html);
|
|
117
|
+
count++;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Skip corrupted files
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return count;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/reporting/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { LLMProvider } from "../providers/index.js";
|
|
2
|
+
import type { Skill, PairConflictReport } from "../types.js";
|
|
3
|
+
interface RewriterOptions {
|
|
4
|
+
provider: LLMProvider;
|
|
5
|
+
model: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RewriteSuggestion {
|
|
8
|
+
skillName: string;
|
|
9
|
+
originalDescription: string;
|
|
10
|
+
rewrittenDescription: string;
|
|
11
|
+
reasoning: string;
|
|
12
|
+
conflictsWith: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare class DescriptionRewriter {
|
|
15
|
+
private provider;
|
|
16
|
+
private model;
|
|
17
|
+
constructor(options: RewriterOptions);
|
|
18
|
+
rewrite(skill: Skill, conflicts: PairConflictReport[]): Promise<RewriteSuggestion>;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=rewriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewriter.d.ts","sourceRoot":"","sources":["../../src/rewriter/rewriter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE7D,UAAU,eAAe;IACvB,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,EAAE,eAAe;IAK9B,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA4DzF"}
|