auden 0.1.0-alpha.2
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/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/auden-paths.d.ts +8 -0
- package/dist/auden-paths.d.ts.map +1 -0
- package/dist/auden-paths.js +10 -0
- package/dist/auden-paths.js.map +1 -0
- package/dist/bin.d.ts +11 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +13 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk.d.ts +3 -0
- package/dist/chunk.d.ts.map +1 -0
- package/dist/chunk.js +13 -0
- package/dist/chunk.js.map +1 -0
- package/dist/commands/doctor.d.ts +13 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +125 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/eval/index.d.ts +2 -0
- package/dist/commands/eval/index.d.ts.map +1 -0
- package/dist/commands/eval/index.js +2 -0
- package/dist/commands/eval/index.js.map +1 -0
- package/dist/commands/eval/run.d.ts +38 -0
- package/dist/commands/eval/run.d.ts.map +1 -0
- package/dist/commands/eval/run.js +179 -0
- package/dist/commands/eval/run.js.map +1 -0
- package/dist/commands/guides/active.d.ts +2 -0
- package/dist/commands/guides/active.d.ts.map +1 -0
- package/dist/commands/guides/active.js +38 -0
- package/dist/commands/guides/active.js.map +1 -0
- package/dist/commands/guides/index.d.ts +2 -0
- package/dist/commands/guides/index.d.ts.map +1 -0
- package/dist/commands/guides/index.js +13 -0
- package/dist/commands/guides/index.js.map +1 -0
- package/dist/commands/guides/project.d.ts +13 -0
- package/dist/commands/guides/project.d.ts.map +1 -0
- package/dist/commands/guides/project.js +71 -0
- package/dist/commands/guides/project.js.map +1 -0
- package/dist/commands/guides/search.d.ts +8 -0
- package/dist/commands/guides/search.d.ts.map +1 -0
- package/dist/commands/guides/search.js +46 -0
- package/dist/commands/guides/search.js.map +1 -0
- package/dist/commands/init.d.ts +43 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +436 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mcp.d.ts +23 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +55 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/pull.d.ts +38 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +127 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/rules/export.d.ts +140 -0
- package/dist/commands/rules/export.d.ts.map +1 -0
- package/dist/commands/rules/export.js +635 -0
- package/dist/commands/rules/export.js.map +1 -0
- package/dist/commands/rules/import.d.ts +33 -0
- package/dist/commands/rules/import.d.ts.map +1 -0
- package/dist/commands/rules/import.js +122 -0
- package/dist/commands/rules/import.js.map +1 -0
- package/dist/commands/rules/index.d.ts +2 -0
- package/dist/commands/rules/index.d.ts.map +1 -0
- package/dist/commands/rules/index.js +13 -0
- package/dist/commands/rules/index.js.map +1 -0
- package/dist/commands/rules/watch.d.ts +43 -0
- package/dist/commands/rules/watch.d.ts.map +1 -0
- package/dist/commands/rules/watch.js +125 -0
- package/dist/commands/rules/watch.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +61 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/suggest.d.ts +8 -0
- package/dist/commands/suggest.d.ts.map +1 -0
- package/dist/commands/suggest.js +43 -0
- package/dist/commands/suggest.js.map +1 -0
- package/dist/commands/sync.d.ts +23 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +193 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/verdict/index.d.ts +2 -0
- package/dist/commands/verdict/index.d.ts.map +1 -0
- package/dist/commands/verdict/index.js +11 -0
- package/dist/commands/verdict/index.js.map +1 -0
- package/dist/commands/verdict/record.d.ts +18 -0
- package/dist/commands/verdict/record.d.ts.map +1 -0
- package/dist/commands/verdict/record.js +60 -0
- package/dist/commands/verdict/record.js.map +1 -0
- package/dist/config.d.ts +96 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +246 -0
- package/dist/config.js.map +1 -0
- package/dist/context/pull.d.ts +40 -0
- package/dist/context/pull.d.ts.map +1 -0
- package/dist/context/pull.js +46 -0
- package/dist/context/pull.js.map +1 -0
- package/dist/dashboard-health.d.ts +20 -0
- package/dist/dashboard-health.d.ts.map +1 -0
- package/dist/dashboard-health.js +24 -0
- package/dist/dashboard-health.js.map +1 -0
- package/dist/eval-provider.d.ts +30 -0
- package/dist/eval-provider.d.ts.map +1 -0
- package/dist/eval-provider.js +359 -0
- package/dist/eval-provider.js.map +1 -0
- package/dist/eval-summary.d.ts +24 -0
- package/dist/eval-summary.d.ts.map +1 -0
- package/dist/eval-summary.js +30 -0
- package/dist/eval-summary.js.map +1 -0
- package/dist/feedback/extract.d.ts +6 -0
- package/dist/feedback/extract.d.ts.map +1 -0
- package/dist/feedback/extract.js +580 -0
- package/dist/feedback/extract.js.map +1 -0
- package/dist/http-retry.d.ts +23 -0
- package/dist/http-retry.d.ts.map +1 -0
- package/dist/http-retry.js +54 -0
- package/dist/http-retry.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +70 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +121 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/scope.d.ts +30 -0
- package/dist/mcp/scope.d.ts.map +1 -0
- package/dist/mcp/scope.js +64 -0
- package/dist/mcp/scope.js.map +1 -0
- package/dist/mcp/server.d.ts +57 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +427 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/write-items.d.ts +34 -0
- package/dist/mcp/write-items.d.ts.map +1 -0
- package/dist/mcp/write-items.js +59 -0
- package/dist/mcp/write-items.js.map +1 -0
- package/dist/rules/deletions-client.d.ts +16 -0
- package/dist/rules/deletions-client.d.ts.map +1 -0
- package/dist/rules/deletions-client.js +40 -0
- package/dist/rules/deletions-client.js.map +1 -0
- package/dist/rules/diff.d.ts +19 -0
- package/dist/rules/diff.d.ts.map +1 -0
- package/dist/rules/diff.js +36 -0
- package/dist/rules/diff.js.map +1 -0
- package/dist/rules/discover.d.ts +81 -0
- package/dist/rules/discover.d.ts.map +1 -0
- package/dist/rules/discover.js +307 -0
- package/dist/rules/discover.js.map +1 -0
- package/dist/rules/file-origin.d.ts +16 -0
- package/dist/rules/file-origin.d.ts.map +1 -0
- package/dist/rules/file-origin.js +31 -0
- package/dist/rules/file-origin.js.map +1 -0
- package/dist/rules/global-flag.d.ts +12 -0
- package/dist/rules/global-flag.d.ts.map +1 -0
- package/dist/rules/global-flag.js +15 -0
- package/dist/rules/global-flag.js.map +1 -0
- package/dist/rules/import-client.d.ts +37 -0
- package/dist/rules/import-client.d.ts.map +1 -0
- package/dist/rules/import-client.js +98 -0
- package/dist/rules/import-client.js.map +1 -0
- package/dist/rules/init-pull.d.ts +34 -0
- package/dist/rules/init-pull.d.ts.map +1 -0
- package/dist/rules/init-pull.js +43 -0
- package/dist/rules/init-pull.js.map +1 -0
- package/dist/rules/member-files-client.d.ts +17 -0
- package/dist/rules/member-files-client.d.ts.map +1 -0
- package/dist/rules/member-files-client.js +29 -0
- package/dist/rules/member-files-client.js.map +1 -0
- package/dist/rules/path-contains.d.ts +9 -0
- package/dist/rules/path-contains.d.ts.map +1 -0
- package/dist/rules/path-contains.js +17 -0
- package/dist/rules/path-contains.js.map +1 -0
- package/dist/rules/payload.d.ts +13 -0
- package/dist/rules/payload.d.ts.map +1 -0
- package/dist/rules/payload.js +35 -0
- package/dist/rules/payload.js.map +1 -0
- package/dist/rules/pull-bundle.d.ts +13 -0
- package/dist/rules/pull-bundle.d.ts.map +1 -0
- package/dist/rules/pull-bundle.js +32 -0
- package/dist/rules/pull-bundle.js.map +1 -0
- package/dist/rules/watch.d.ts +121 -0
- package/dist/rules/watch.d.ts.map +1 -0
- package/dist/rules/watch.js +457 -0
- package/dist/rules/watch.js.map +1 -0
- package/dist/skill-template.d.ts +19 -0
- package/dist/skill-template.d.ts.map +1 -0
- package/dist/skill-template.js +174 -0
- package/dist/skill-template.js.map +1 -0
- package/dist/sync-batches.d.ts +37 -0
- package/dist/sync-batches.d.ts.map +1 -0
- package/dist/sync-batches.js +67 -0
- package/dist/sync-batches.js.map +1 -0
- package/dist/sync-client.d.ts +22 -0
- package/dist/sync-client.d.ts.map +1 -0
- package/dist/sync-client.js +38 -0
- package/dist/sync-client.js.map +1 -0
- package/dist/sync-response.d.ts +23 -0
- package/dist/sync-response.d.ts.map +1 -0
- package/dist/sync-response.js +22 -0
- package/dist/sync-response.js.map +1 -0
- package/dist/token-verify.d.ts +14 -0
- package/dist/token-verify.d.ts.map +1 -0
- package/dist/token-verify.js +30 -0
- package/dist/token-verify.js.map +1 -0
- package/dist/usage/extract.d.ts +31 -0
- package/dist/usage/extract.d.ts.map +1 -0
- package/dist/usage/extract.js +200 -0
- package/dist/usage/extract.js.map +1 -0
- package/dist/usage/providers.d.ts +8 -0
- package/dist/usage/providers.d.ts.map +1 -0
- package/dist/usage/providers.js +23 -0
- package/dist/usage/providers.js.map +1 -0
- package/dist/verdict-queue.d.ts +50 -0
- package/dist/verdict-queue.d.ts.map +1 -0
- package/dist/verdict-queue.js +131 -0
- package/dist/verdict-queue.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import { basename, dirname, extname, join } from 'node:path';
|
|
3
|
+
import { inferProviderFromModel } from './providers.js';
|
|
4
|
+
/** Same SKILL.md identification idiom as `rules/discover.ts`'s `isSkillMd`. */
|
|
5
|
+
function isSkillMdPath(path) {
|
|
6
|
+
return basename(path).toLowerCase() === 'skill.md';
|
|
7
|
+
}
|
|
8
|
+
function asRecord(value) {
|
|
9
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Detects a skill-load signal in one assistant message's content blocks
|
|
16
|
+
* (docs/plans/token-cost-tracking-plan.md §13.2): either an explicit `Skill`
|
|
17
|
+
* tool_use (input.skill names the skill), or a file-read tool_use whose path
|
|
18
|
+
* targets a `SKILL.md` (skill name = its parent directory). Only the first
|
|
19
|
+
* signal in a message is used — a message that requests two skill loads at
|
|
20
|
+
* once is a rare edge case not worth the double-counting risk of attributing
|
|
21
|
+
* the next message's usage to both.
|
|
22
|
+
*/
|
|
23
|
+
function detectSkillLoadSignal(message) {
|
|
24
|
+
const content = message.content;
|
|
25
|
+
if (!Array.isArray(content)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
for (const block of content) {
|
|
29
|
+
const blockRecord = asRecord(block);
|
|
30
|
+
if (!blockRecord || blockRecord.type !== 'tool_use') {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const toolName = typeof blockRecord.name === 'string' ? blockRecord.name : null;
|
|
34
|
+
const input = asRecord(blockRecord.input);
|
|
35
|
+
if (toolName === 'Skill') {
|
|
36
|
+
const skillName = typeof input?.skill === 'string' ? input.skill : null;
|
|
37
|
+
if (skillName) {
|
|
38
|
+
return skillName;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const path = typeof input?.file_path === 'string'
|
|
43
|
+
? input.file_path
|
|
44
|
+
: typeof input?.path === 'string'
|
|
45
|
+
? input.path
|
|
46
|
+
: null;
|
|
47
|
+
if (path && isSkillMdPath(path)) {
|
|
48
|
+
return basename(dirname(path));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function toNonNegativeInt(value) {
|
|
54
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0
|
|
55
|
+
? Math.floor(value)
|
|
56
|
+
: 0;
|
|
57
|
+
}
|
|
58
|
+
async function collectJsonlFiles(dir) {
|
|
59
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
60
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
61
|
+
const fullPath = join(dir, entry.name);
|
|
62
|
+
if (entry.isDirectory()) {
|
|
63
|
+
return collectJsonlFiles(fullPath);
|
|
64
|
+
}
|
|
65
|
+
return extname(entry.name) === '.jsonl' ? [fullPath] : [];
|
|
66
|
+
}));
|
|
67
|
+
return files.flat().sort();
|
|
68
|
+
}
|
|
69
|
+
async function readJsonlRecords(path) {
|
|
70
|
+
const raw = await readFile(path, 'utf8');
|
|
71
|
+
return raw
|
|
72
|
+
.split('\n')
|
|
73
|
+
.map((line) => line.trim())
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
.flatMap((line) => {
|
|
76
|
+
try {
|
|
77
|
+
return [JSON.parse(line)];
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Extracts per-session, per-model token usage from Claude Code JSONL
|
|
86
|
+
* transcripts (docs/plans/token-cost-tracking-plan.md §5.1), plus per-skill
|
|
87
|
+
* Tier-1 "load overhead" usage (§13.3): when an assistant message requests a
|
|
88
|
+
* `Skill` tool or reads a `SKILL.md`, the *next* assistant message's usage is
|
|
89
|
+
* the first-load cost of that skill (v1 approximation — see §13.3's caching
|
|
90
|
+
* nuance). Reads only `usage`/`model`/`sessionId` and tool_use `name`/`input`
|
|
91
|
+
* off assistant messages — never message text.
|
|
92
|
+
*
|
|
93
|
+
* Each message carries its own (non-cumulative) usage, so we sum every
|
|
94
|
+
* assistant message's usage per (session, model). Messages are deduped by
|
|
95
|
+
* their id so re-reading a transcript that grew since the last sync does not
|
|
96
|
+
* double-count lines already summed.
|
|
97
|
+
*/
|
|
98
|
+
export async function extractRunUsage(options) {
|
|
99
|
+
if (!options?.transcriptsDir) {
|
|
100
|
+
return { runUsage: [], skillUsage: [] };
|
|
101
|
+
}
|
|
102
|
+
const files = await collectJsonlFiles(options.transcriptsDir).catch(() => []);
|
|
103
|
+
const sessionUsage = new Map();
|
|
104
|
+
const sessionSkillUsage = new Map();
|
|
105
|
+
const pendingSkillLoads = new Map();
|
|
106
|
+
const seenMessageIds = new Set();
|
|
107
|
+
for (const file of files) {
|
|
108
|
+
const fallbackSessionId = basename(file, extname(file));
|
|
109
|
+
const records = await readJsonlRecords(file);
|
|
110
|
+
for (const recordValue of records) {
|
|
111
|
+
const record = asRecord(recordValue);
|
|
112
|
+
if (!record || record.type !== 'assistant') {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const message = asRecord(record.message);
|
|
116
|
+
if (!message) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const usage = asRecord(message.usage);
|
|
120
|
+
if (!usage) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const model = typeof message.model === 'string' ? message.model : null;
|
|
124
|
+
if (!model) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const messageId = typeof message.id === 'string'
|
|
128
|
+
? message.id
|
|
129
|
+
: typeof record.uuid === 'string'
|
|
130
|
+
? record.uuid
|
|
131
|
+
: null;
|
|
132
|
+
if (messageId) {
|
|
133
|
+
if (seenMessageIds.has(messageId)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
seenMessageIds.add(messageId);
|
|
137
|
+
}
|
|
138
|
+
const sessionId = typeof record.sessionId === 'string' && record.sessionId.length > 0
|
|
139
|
+
? record.sessionId
|
|
140
|
+
: fallbackSessionId;
|
|
141
|
+
const modelMap = sessionUsage.get(sessionId) ?? new Map();
|
|
142
|
+
sessionUsage.set(sessionId, modelMap);
|
|
143
|
+
const existing = modelMap.get(model) ?? {
|
|
144
|
+
model,
|
|
145
|
+
provider: inferProviderFromModel(model),
|
|
146
|
+
inputTokens: 0,
|
|
147
|
+
outputTokens: 0,
|
|
148
|
+
cacheReadTokens: 0,
|
|
149
|
+
cacheWriteTokens: 0,
|
|
150
|
+
};
|
|
151
|
+
modelMap.set(model, {
|
|
152
|
+
...existing,
|
|
153
|
+
inputTokens: existing.inputTokens + toNonNegativeInt(usage.input_tokens),
|
|
154
|
+
outputTokens: existing.outputTokens + toNonNegativeInt(usage.output_tokens),
|
|
155
|
+
cacheReadTokens: (existing.cacheReadTokens ?? 0) +
|
|
156
|
+
toNonNegativeInt(usage.cache_read_input_tokens),
|
|
157
|
+
cacheWriteTokens: (existing.cacheWriteTokens ?? 0) +
|
|
158
|
+
toNonNegativeInt(usage.cache_creation_input_tokens),
|
|
159
|
+
});
|
|
160
|
+
// Attribute this message's usage to a skill load requested by the
|
|
161
|
+
// *previous* assistant message, if any — this message is the first turn
|
|
162
|
+
// to see that skill's content in context. Must run before detecting a
|
|
163
|
+
// new signal below, so a message that both closes one load and opens
|
|
164
|
+
// another handles both correctly.
|
|
165
|
+
const pendingSkillName = pendingSkillLoads.get(sessionId);
|
|
166
|
+
if (pendingSkillName) {
|
|
167
|
+
pendingSkillLoads.delete(sessionId);
|
|
168
|
+
const skillMap = sessionSkillUsage.get(sessionId) ?? new Map();
|
|
169
|
+
sessionSkillUsage.set(sessionId, skillMap);
|
|
170
|
+
const skillKey = `${pendingSkillName}::${model}`;
|
|
171
|
+
const existingSkillUsage = skillMap.get(skillKey) ?? {
|
|
172
|
+
skillName: pendingSkillName,
|
|
173
|
+
model,
|
|
174
|
+
inputTokens: 0,
|
|
175
|
+
cacheWriteTokens: 0,
|
|
176
|
+
};
|
|
177
|
+
skillMap.set(skillKey, {
|
|
178
|
+
...existingSkillUsage,
|
|
179
|
+
inputTokens: existingSkillUsage.inputTokens + toNonNegativeInt(usage.input_tokens),
|
|
180
|
+
cacheWriteTokens: (existingSkillUsage.cacheWriteTokens ?? 0) +
|
|
181
|
+
toNonNegativeInt(usage.cache_creation_input_tokens),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const skillLoadSignal = detectSkillLoadSignal(message);
|
|
185
|
+
if (skillLoadSignal) {
|
|
186
|
+
pendingSkillLoads.set(sessionId, skillLoadSignal);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const runUsage = Array.from(sessionUsage.entries()).map(([sessionId, modelMap]) => ({
|
|
191
|
+
sessionId,
|
|
192
|
+
usage: Array.from(modelMap.values()),
|
|
193
|
+
}));
|
|
194
|
+
const skillUsage = Array.from(sessionSkillUsage.entries()).map(([sessionId, skillMap]) => ({
|
|
195
|
+
sessionId,
|
|
196
|
+
usage: Array.from(skillMap.values()),
|
|
197
|
+
}));
|
|
198
|
+
return { runUsage, skillUsage };
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/usage/extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infers a model's API provider from its id prefix
|
|
3
|
+
* (docs/plans/token-cost-tracking-plan.md §8: "derive `provider` from model id
|
|
4
|
+
* prefix when the transcript doesn't state it"). Returns `undefined` rather
|
|
5
|
+
* than guessing wrong for an id we don't recognize.
|
|
6
|
+
*/
|
|
7
|
+
export declare function inferProviderFromModel(model: string): string | undefined;
|
|
8
|
+
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/usage/providers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiBxE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infers a model's API provider from its id prefix
|
|
3
|
+
* (docs/plans/token-cost-tracking-plan.md §8: "derive `provider` from model id
|
|
4
|
+
* prefix when the transcript doesn't state it"). Returns `undefined` rather
|
|
5
|
+
* than guessing wrong for an id we don't recognize.
|
|
6
|
+
*/
|
|
7
|
+
export function inferProviderFromModel(model) {
|
|
8
|
+
if (model.startsWith('claude-') || model.startsWith('anthropic/')) {
|
|
9
|
+
return 'anthropic';
|
|
10
|
+
}
|
|
11
|
+
if (model.startsWith('gpt-') ||
|
|
12
|
+
model.startsWith('o1-') ||
|
|
13
|
+
model.startsWith('o3-') ||
|
|
14
|
+
model.startsWith('o4-') ||
|
|
15
|
+
model.startsWith('openai/')) {
|
|
16
|
+
return 'openai';
|
|
17
|
+
}
|
|
18
|
+
if (model.startsWith('gemini-') || model.startsWith('google/')) {
|
|
19
|
+
return 'google';
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/usage/providers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAClE,OAAO,WAAW,CAAA;IACpB,CAAC;IACD,IACE,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;QACxB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;QACvB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;QACvB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;QACvB,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAC3B,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/D,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DashboardSyncAction } from '@auden.to/protocol';
|
|
2
|
+
export type VerdictQueueResult = {
|
|
3
|
+
actions: DashboardSyncAction[];
|
|
4
|
+
filePaths: string[];
|
|
5
|
+
/**
|
|
6
|
+
* Files that were read in full but whose content is not a valid queued
|
|
7
|
+
* verdict (bad JSON or failing schema). They will never become valid, so the
|
|
8
|
+
* caller deletes them instead of re-warning about them on every run. Files
|
|
9
|
+
* that only failed to read (transient I/O, permissions) are omitted — they
|
|
10
|
+
* are left in place to retry next time.
|
|
11
|
+
*/
|
|
12
|
+
invalidFilePaths: string[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Read all valid queued verdict files from `dir`.
|
|
16
|
+
* Returns the mapped sync actions, the absolute paths of the files that
|
|
17
|
+
* produced them (so the caller can delete them after a successful sync), and
|
|
18
|
+
* the paths of files whose content is permanently invalid (so the caller can
|
|
19
|
+
* clean them up instead of re-warning about them on every run).
|
|
20
|
+
*/
|
|
21
|
+
export declare function readVerdictQueue(dir: string): Promise<VerdictQueueResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Hard cap on pending queued verdicts. Bounds disk growth if the dashboard is
|
|
24
|
+
* unreachable for a long stretch — nothing else limited how many `auden
|
|
25
|
+
* verdict record` calls could pile up in `~/.auden/queue` while an outage
|
|
26
|
+
* lasts. Past the cap, the oldest entries are evicted to make room for new
|
|
27
|
+
* ones: a long enough outage loses old verdicts rather than filling the disk.
|
|
28
|
+
*/
|
|
29
|
+
export declare const MAX_QUEUED_VERDICTS = 5000;
|
|
30
|
+
export type EnforceQueueCapResult = {
|
|
31
|
+
/** Number of oldest queue files deleted to get back under the cap. */
|
|
32
|
+
evicted: number;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Trim `dir` down to `maxEntries` queued verdict files. No-op when the
|
|
36
|
+
* directory is missing or already within the cap — that check is a cheap
|
|
37
|
+
* `readdir` with no file reads, since it runs on every `auden verdict
|
|
38
|
+
* record` call and the common case is nowhere near the cap.
|
|
39
|
+
*
|
|
40
|
+
* Once over the cap, permanently-invalid entries (bad JSON or failing
|
|
41
|
+
* schema — the same classification `readVerdictQueue` uses) are evicted
|
|
42
|
+
* first regardless of age: they can never be synced, so deleting them costs
|
|
43
|
+
* nothing, whereas evicting a still-syncable verdict is real data loss.
|
|
44
|
+
* Only once invalid entries are exhausted does eviction fall back to the
|
|
45
|
+
* oldest valid verdicts (by file mtime). Best-effort throughout: a file that
|
|
46
|
+
* fails to delete is left in place rather than throwing — eviction is
|
|
47
|
+
* housekeeping, not the operation the caller is actually trying to perform.
|
|
48
|
+
*/
|
|
49
|
+
export declare function enforceVerdictQueueCap(dir: string, maxEntries?: number): Promise<EnforceQueueCapResult>;
|
|
50
|
+
//# sourceMappingURL=verdict-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict-queue.d.ts","sourceRoot":"","sources":["../src/verdict-queue.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAc7D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,mBAAmB,EAAE,CAAA;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC3B,CAAA;AAaD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAgD/E;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,OAAO,CAAA;AAEvC,MAAM,MAAM,qBAAqB,GAAG;IAClC,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAA4B,GACvC,OAAO,CAAC,qBAAqB,CAAC,CAsChC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { readFile, readdir, stat, unlink } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { VerdictSchema } from '@auden.to/protocol';
|
|
4
|
+
import * as v from 'valibot';
|
|
5
|
+
const QueuedVerdictSchema = v.object({
|
|
6
|
+
id: v.string(),
|
|
7
|
+
sessionId: v.string(),
|
|
8
|
+
actionType: v.string(),
|
|
9
|
+
summary: v.string(),
|
|
10
|
+
verdict: VerdictSchema,
|
|
11
|
+
timestamp: v.string(),
|
|
12
|
+
});
|
|
13
|
+
function toSyncAction(entry) {
|
|
14
|
+
return {
|
|
15
|
+
id: entry.id,
|
|
16
|
+
sessionId: entry.sessionId,
|
|
17
|
+
actionType: entry.actionType,
|
|
18
|
+
summary: entry.summary,
|
|
19
|
+
verdict: entry.verdict,
|
|
20
|
+
metadata: { recordedAt: entry.timestamp },
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read all valid queued verdict files from `dir`.
|
|
25
|
+
* Returns the mapped sync actions, the absolute paths of the files that
|
|
26
|
+
* produced them (so the caller can delete them after a successful sync), and
|
|
27
|
+
* the paths of files whose content is permanently invalid (so the caller can
|
|
28
|
+
* clean them up instead of re-warning about them on every run).
|
|
29
|
+
*/
|
|
30
|
+
export async function readVerdictQueue(dir) {
|
|
31
|
+
const actions = [];
|
|
32
|
+
const filePaths = [];
|
|
33
|
+
const invalidFilePaths = [];
|
|
34
|
+
let entries;
|
|
35
|
+
try {
|
|
36
|
+
entries = await readdir(dir, { withFileTypes: true, encoding: 'utf8' });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return { actions, filePaths, invalidFilePaths };
|
|
40
|
+
}
|
|
41
|
+
const jsonFiles = entries.filter((d) => d.isFile() && d.name.endsWith('.json'));
|
|
42
|
+
for (const dirent of jsonFiles) {
|
|
43
|
+
// dirent.parentPath was added in Node 21.4; dirent.path is the equivalent
|
|
44
|
+
// property available in Node 20. Fall back to dir as a safety net.
|
|
45
|
+
const absPath = resolve(dirent.parentPath ?? dirent.path ?? dir, dirent.name);
|
|
46
|
+
let raw;
|
|
47
|
+
try {
|
|
48
|
+
raw = await readFile(absPath, 'utf8');
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
// ENOENT is a harmless readdir/read race (the file was removed between
|
|
52
|
+
// the listing and the read) — stay silent and retry next run. Any other
|
|
53
|
+
// read failure (permissions, I/O) is a real queue file we can neither
|
|
54
|
+
// sync nor safely delete, so surface it rather than black-holing it: it
|
|
55
|
+
// would otherwise show as neither pending nor invalid anywhere.
|
|
56
|
+
if (err?.code !== 'ENOENT') {
|
|
57
|
+
console.warn(` warning: could not read queue file ${absPath}: ${err?.message}`);
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const parsed = v.parse(QueuedVerdictSchema, JSON.parse(raw));
|
|
63
|
+
actions.push(toSyncAction(parsed));
|
|
64
|
+
filePaths.push(absPath);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Read in full but the content is not a valid queued verdict — it will
|
|
68
|
+
// never become valid, so mark it for cleanup by the caller.
|
|
69
|
+
invalidFilePaths.push(absPath);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { actions, filePaths, invalidFilePaths };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Hard cap on pending queued verdicts. Bounds disk growth if the dashboard is
|
|
76
|
+
* unreachable for a long stretch — nothing else limited how many `auden
|
|
77
|
+
* verdict record` calls could pile up in `~/.auden/queue` while an outage
|
|
78
|
+
* lasts. Past the cap, the oldest entries are evicted to make room for new
|
|
79
|
+
* ones: a long enough outage loses old verdicts rather than filling the disk.
|
|
80
|
+
*/
|
|
81
|
+
export const MAX_QUEUED_VERDICTS = 5000;
|
|
82
|
+
/**
|
|
83
|
+
* Trim `dir` down to `maxEntries` queued verdict files. No-op when the
|
|
84
|
+
* directory is missing or already within the cap — that check is a cheap
|
|
85
|
+
* `readdir` with no file reads, since it runs on every `auden verdict
|
|
86
|
+
* record` call and the common case is nowhere near the cap.
|
|
87
|
+
*
|
|
88
|
+
* Once over the cap, permanently-invalid entries (bad JSON or failing
|
|
89
|
+
* schema — the same classification `readVerdictQueue` uses) are evicted
|
|
90
|
+
* first regardless of age: they can never be synced, so deleting them costs
|
|
91
|
+
* nothing, whereas evicting a still-syncable verdict is real data loss.
|
|
92
|
+
* Only once invalid entries are exhausted does eviction fall back to the
|
|
93
|
+
* oldest valid verdicts (by file mtime). Best-effort throughout: a file that
|
|
94
|
+
* fails to delete is left in place rather than throwing — eviction is
|
|
95
|
+
* housekeeping, not the operation the caller is actually trying to perform.
|
|
96
|
+
*/
|
|
97
|
+
export async function enforceVerdictQueueCap(dir, maxEntries = MAX_QUEUED_VERDICTS) {
|
|
98
|
+
let entries;
|
|
99
|
+
try {
|
|
100
|
+
entries = await readdir(dir, { withFileTypes: true, encoding: 'utf8' });
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return { evicted: 0 };
|
|
104
|
+
}
|
|
105
|
+
const jsonFiles = entries.filter((d) => d.isFile() && d.name.endsWith('.json'));
|
|
106
|
+
if (jsonFiles.length <= maxEntries)
|
|
107
|
+
return { evicted: 0 };
|
|
108
|
+
const { filePaths: validPaths, invalidFilePaths } = await readVerdictQueue(dir);
|
|
109
|
+
let overflow = validPaths.length + invalidFilePaths.length - maxEntries;
|
|
110
|
+
if (overflow <= 0)
|
|
111
|
+
return { evicted: 0 };
|
|
112
|
+
let evicted = 0;
|
|
113
|
+
const invalidToEvict = invalidFilePaths.slice(0, overflow);
|
|
114
|
+
if (invalidToEvict.length > 0) {
|
|
115
|
+
const results = await Promise.allSettled(invalidToEvict.map((p) => unlink(p)));
|
|
116
|
+
evicted += results.filter((r) => r.status === 'fulfilled').length;
|
|
117
|
+
overflow -= invalidToEvict.length;
|
|
118
|
+
}
|
|
119
|
+
if (overflow > 0) {
|
|
120
|
+
const withMtime = await Promise.all(validPaths.map(async (absPath) => {
|
|
121
|
+
const info = await stat(absPath).catch(() => null);
|
|
122
|
+
return { absPath, mtimeMs: info?.mtimeMs ?? 0 };
|
|
123
|
+
}));
|
|
124
|
+
withMtime.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
125
|
+
const validToEvict = withMtime.slice(0, overflow);
|
|
126
|
+
const results = await Promise.allSettled(validToEvict.map((f) => unlink(f.absPath)));
|
|
127
|
+
evicted += results.filter((r) => r.status === 'fulfilled').length;
|
|
128
|
+
}
|
|
129
|
+
return { evicted };
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=verdict-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict-queue.js","sourceRoot":"","sources":["../src/verdict-queue.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "auden",
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Local-first CLI for Auden — evaluates AI coding agent actions against your guides on-device and syncs verdicts to your dashboard.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"auden": "./dist/bin.js"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"!dist/.tsbuildinfo",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/auden-to/auden.git",
|
|
31
|
+
"directory": "packages/cli"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://app.auden.to",
|
|
34
|
+
"keywords": [
|
|
35
|
+
"ai",
|
|
36
|
+
"agents",
|
|
37
|
+
"claude-code",
|
|
38
|
+
"agent-guidance",
|
|
39
|
+
"evaluation",
|
|
40
|
+
"cli"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"provenance": false
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc --project tsconfig.json && node -e \"require('node:fs').chmodSync('dist/bin.js', 0o755)\"",
|
|
48
|
+
"dev": "tsc --project tsconfig.json --watch",
|
|
49
|
+
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
50
|
+
"lint": "oxlint",
|
|
51
|
+
"lint:fix": "oxfmt && oxlint --fix",
|
|
52
|
+
"format": "oxfmt",
|
|
53
|
+
"format:check": "oxfmt --check",
|
|
54
|
+
"test": "vitest run"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@auden.to/eval-core": "0.1.0-alpha.2",
|
|
58
|
+
"@auden.to/projections": "0.1.0-alpha.2",
|
|
59
|
+
"@auden.to/protocol": "0.1.0-alpha.2",
|
|
60
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
61
|
+
"citty": "^0.1.6",
|
|
62
|
+
"diff": "^9.0.0"
|
|
63
|
+
},
|
|
64
|
+
"overrides": {
|
|
65
|
+
"fast-uri": "3.1.4"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/diff": "^8.0.0",
|
|
69
|
+
"@types/node": "^20.0.0",
|
|
70
|
+
"typescript": "*"
|
|
71
|
+
}
|
|
72
|
+
}
|