fraim-framework 2.0.124 → 2.0.127
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/bin/fraim.js +1 -1
- package/dist/src/ai-hub/catalog.js +280 -44
- package/dist/src/ai-hub/desktop-main.js +2 -2
- package/dist/src/ai-hub/hosts.js +384 -10
- package/dist/src/ai-hub/server.js +255 -9
- package/dist/src/cli/commands/add-ide.js +4 -3
- package/dist/src/cli/commands/first-run.js +61 -0
- package/dist/src/cli/commands/hub.js +4 -4
- package/dist/src/cli/commands/init-project.js +4 -4
- package/dist/src/cli/commands/setup.js +4 -3
- package/dist/src/cli/commands/sync.js +21 -2
- package/dist/src/cli/doctor/checks/ide-config-checks.js +20 -2
- package/dist/src/cli/fraim.js +2 -0
- package/dist/src/cli/mcp/ide-formats.js +29 -1
- package/dist/src/cli/mcp/mcp-server-registry.js +1 -0
- package/dist/src/cli/setup/auto-mcp-setup.js +14 -8
- package/dist/src/cli/setup/ide-detector.js +32 -1
- package/dist/src/cli/setup/ide-global-integration.js +5 -1
- package/dist/src/cli/setup/ide-invocation-surfaces.js +70 -17
- package/dist/src/cli/setup/mcp-config-generator.js +12 -1
- package/dist/src/cli/utils/agent-adapters.js +12 -2
- package/dist/src/cli/utils/project-bootstrap.js +4 -3
- package/dist/src/core/quality-evidence.js +81 -8
- package/dist/src/core/utils/git-utils.js +32 -7
- package/dist/src/core/utils/job-aliases.js +47 -0
- package/dist/src/core/utils/workflow-parser.js +3 -5
- package/dist/src/first-run/install-state.js +68 -0
- package/dist/src/first-run/server.js +153 -0
- package/dist/src/first-run/session-service.js +302 -0
- package/dist/src/first-run/types.js +40 -0
- package/dist/src/local-mcp-server/agent-token-prices.js +114 -0
- package/dist/src/local-mcp-server/codex-token-adapter.js +232 -0
- package/dist/src/local-mcp-server/learning-context-builder.js +21 -8
- package/dist/src/local-mcp-server/otlp-metrics-receiver.js +7 -1
- package/dist/src/local-mcp-server/stdio-server.js +70 -17
- package/dist/src/local-mcp-server/token-adapter-registry.js +64 -0
- package/dist/src/local-mcp-server/usage-collector.js +25 -0
- package/index.js +83 -83
- package/package.json +7 -1
- package/public/ai-hub/index.html +149 -102
- package/public/ai-hub/script.js +1154 -271
- package/public/ai-hub/styles.css +753 -450
- package/public/first-run/index.html +221 -0
- package/public/first-run/script.js +361 -0
- package/dist/src/cli/services/device-flow-service.js +0 -83
- package/dist/src/local-mcp-server/prometheus-scraper.js +0 -152
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Prometheus Scraper for Claude Code OTel Token Usage Metrics
|
|
4
|
-
*
|
|
5
|
-
* Scrapes Claude Code's local Prometheus endpoint to capture cumulative
|
|
6
|
-
* token usage counters. Returns raw snapshot values — delta computation
|
|
7
|
-
* happens server-side per architecture constraint 15.2.
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.parsePrometheusText = parsePrometheusText;
|
|
11
|
-
exports.extractTokenSnapshot = extractTokenSnapshot;
|
|
12
|
-
exports.scrapeTokenSnapshot = scrapeTokenSnapshot;
|
|
13
|
-
/** Candidate Prometheus endpoint ports (OTel SDK defaults) */
|
|
14
|
-
const CANDIDATE_PORTS = [9464, 8888];
|
|
15
|
-
/** Candidate metric names for token usage (OTel dots→underscores, optional suffixes) */
|
|
16
|
-
const TOKEN_METRIC_CANDIDATES = [
|
|
17
|
-
'claude_code_token_usage_total',
|
|
18
|
-
'claude_code_token_usage',
|
|
19
|
-
'claude_code_token_usage_tokens_total',
|
|
20
|
-
'claude_code_token_usage_tokens',
|
|
21
|
-
];
|
|
22
|
-
/** Candidate metric names for cost usage */
|
|
23
|
-
const COST_METRIC_CANDIDATES = [
|
|
24
|
-
'claude_code_cost_usage_total',
|
|
25
|
-
'claude_code_cost_usage',
|
|
26
|
-
'claude_code_cost_usage_usd_total',
|
|
27
|
-
'claude_code_cost_usage_usd',
|
|
28
|
-
];
|
|
29
|
-
/**
|
|
30
|
-
* Parse Prometheus text exposition format into structured metrics.
|
|
31
|
-
*
|
|
32
|
-
* Format per line: metric_name{label1="val1",label2="val2"} value [timestamp]
|
|
33
|
-
* Lines starting with # are comments (HELP, TYPE). Empty lines are skipped.
|
|
34
|
-
*/
|
|
35
|
-
function parsePrometheusText(text) {
|
|
36
|
-
const results = [];
|
|
37
|
-
for (const line of text.split('\n')) {
|
|
38
|
-
if (line.startsWith('#') || line.trim() === '')
|
|
39
|
-
continue;
|
|
40
|
-
// Match: metric_name{labels} value
|
|
41
|
-
const labeledMatch = line.match(/^([a-zA-Z_:][a-zA-Z0-9_:]*)\{([^}]*)\}\s+([\d.eE+-]+)/);
|
|
42
|
-
if (labeledMatch) {
|
|
43
|
-
const [, name, labelsStr, valueStr] = labeledMatch;
|
|
44
|
-
const labels = {};
|
|
45
|
-
for (const pair of labelsStr.matchAll(/(\w+)="([^"]*)"/g)) {
|
|
46
|
-
labels[pair[1]] = pair[2];
|
|
47
|
-
}
|
|
48
|
-
results.push({ name, labels, value: parseFloat(valueStr) });
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
// Match: metric_name value (no labels)
|
|
52
|
-
const noLabelMatch = line.match(/^([a-zA-Z_:][a-zA-Z0-9_:]*)\s+([\d.eE+-]+)/);
|
|
53
|
-
if (noLabelMatch) {
|
|
54
|
-
const [, name, valueStr] = noLabelMatch;
|
|
55
|
-
results.push({ name, labels: {}, value: parseFloat(valueStr) });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return results;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Extract a TokenSnapshot from parsed Prometheus metrics.
|
|
62
|
-
* Tries multiple candidate metric names for resilience to SDK version changes.
|
|
63
|
-
*/
|
|
64
|
-
function extractTokenSnapshot(metrics) {
|
|
65
|
-
const snapshot = {
|
|
66
|
-
inputTokens: 0,
|
|
67
|
-
outputTokens: 0,
|
|
68
|
-
cacheReadTokens: 0,
|
|
69
|
-
cacheCreationTokens: 0,
|
|
70
|
-
costUsd: 0,
|
|
71
|
-
claudeSessionId: null,
|
|
72
|
-
model: null,
|
|
73
|
-
capturedAt: new Date(),
|
|
74
|
-
};
|
|
75
|
-
for (const m of metrics) {
|
|
76
|
-
if (TOKEN_METRIC_CANDIDATES.includes(m.name)) {
|
|
77
|
-
const type = m.labels['type'];
|
|
78
|
-
switch (type) {
|
|
79
|
-
case 'input':
|
|
80
|
-
snapshot.inputTokens = m.value;
|
|
81
|
-
break;
|
|
82
|
-
case 'output':
|
|
83
|
-
snapshot.outputTokens = m.value;
|
|
84
|
-
break;
|
|
85
|
-
case 'cacheRead':
|
|
86
|
-
snapshot.cacheReadTokens = m.value;
|
|
87
|
-
break;
|
|
88
|
-
case 'cacheCreation':
|
|
89
|
-
snapshot.cacheCreationTokens = m.value;
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
snapshot.claudeSessionId = m.labels['session_id'] || snapshot.claudeSessionId;
|
|
93
|
-
snapshot.model = m.labels['model'] || snapshot.model;
|
|
94
|
-
}
|
|
95
|
-
if (COST_METRIC_CANDIDATES.includes(m.name)) {
|
|
96
|
-
snapshot.costUsd += m.value;
|
|
97
|
-
snapshot.claudeSessionId = m.labels['session_id'] || snapshot.claudeSessionId;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return snapshot;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Attempt to fetch Prometheus metrics from a candidate endpoint.
|
|
104
|
-
* Returns response text or null on failure.
|
|
105
|
-
*/
|
|
106
|
-
async function tryFetch(port, timeoutMs) {
|
|
107
|
-
try {
|
|
108
|
-
const controller = new AbortController();
|
|
109
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
110
|
-
const resp = await fetch(`http://localhost:${port}/metrics`, {
|
|
111
|
-
signal: controller.signal,
|
|
112
|
-
});
|
|
113
|
-
clearTimeout(timeout);
|
|
114
|
-
if (resp.ok) {
|
|
115
|
-
return await resp.text();
|
|
116
|
-
}
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Scrape Claude Code's Prometheus endpoint and return a TokenSnapshot.
|
|
125
|
-
*
|
|
126
|
-
* Returns null if:
|
|
127
|
-
* - Prometheus endpoint is not available (OTel not enabled)
|
|
128
|
-
* - Scrape times out (>2s by default)
|
|
129
|
-
* - No matching token metrics found
|
|
130
|
-
*
|
|
131
|
-
* @param log Optional logging function for debug output
|
|
132
|
-
*/
|
|
133
|
-
async function scrapeTokenSnapshot(log) {
|
|
134
|
-
const configuredPort = process.env.FRAIM_PROMETHEUS_PORT;
|
|
135
|
-
const ports = configuredPort ? [parseInt(configuredPort, 10)] : CANDIDATE_PORTS;
|
|
136
|
-
const timeoutMs = 2000;
|
|
137
|
-
for (const port of ports) {
|
|
138
|
-
const text = await tryFetch(port, timeoutMs);
|
|
139
|
-
if (text && text.includes('#')) {
|
|
140
|
-
const metrics = parsePrometheusText(text);
|
|
141
|
-
const snapshot = extractTokenSnapshot(metrics);
|
|
142
|
-
// Only return if we actually found token metrics
|
|
143
|
-
if (snapshot.inputTokens > 0 || snapshot.outputTokens > 0) {
|
|
144
|
-
log?.(`Captured token snapshot from localhost:${port} (input=${snapshot.inputTokens}, output=${snapshot.outputTokens})`);
|
|
145
|
-
return snapshot;
|
|
146
|
-
}
|
|
147
|
-
log?.(`Prometheus endpoint found at localhost:${port} but no token metrics matched`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
log?.('No Prometheus endpoint available — token snapshot skipped');
|
|
151
|
-
return null;
|
|
152
|
-
}
|