sentinelayer-cli 0.1.2 → 0.4.4
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/README.md +998 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +63 -54
- package/src/agents/jules/config/definition.js +209 -209
- package/src/agents/jules/config/system-prompt.js +175 -175
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +377 -377
- package/src/agents/jules/loop.js +367 -367
- package/src/agents/jules/pulse.js +327 -319
- package/src/agents/jules/stream.js +186 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +308 -308
- package/src/agents/jules/tools/auth-audit.js +557 -222
- package/src/agents/jules/tools/dispatch.js +327 -327
- package/src/agents/jules/tools/file-edit.js +180 -180
- package/src/agents/jules/tools/file-read.js +100 -100
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +168 -168
- package/src/agents/jules/tools/grep.js +228 -228
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +161 -161
- package/src/agents/jules/tools/runtime-audit.js +503 -493
- package/src/agents/jules/tools/shell.js +383 -383
- package/src/agents/jules/tools/url-policy.js +100 -0
- package/src/ai/aidenid.js +972 -945
- package/src/ai/client.js +508 -508
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +45 -11
- package/src/auth/http.js +270 -113
- package/src/auth/service.js +891 -848
- package/src/auth/session-store.js +359 -345
- package/src/cli.js +252 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1337
- package/src/commands/ai/provision-governance.js +1272 -1246
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1166 -1166
- package/src/commands/auth.js +375 -366
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -404
- package/src/commands/omargate.js +15 -15
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +866 -788
- package/src/commands/spec.js +716 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +510 -510
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +770 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +626 -626
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/jira-lifecycle.js +632 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/watchdog.js +971 -971
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/legacy-cli.js +2592 -2435
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +118 -106
- package/src/review/ai-review.js +669 -669
- package/src/review/local-review.js +1295 -1284
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -0
- package/src/scaffold/templates.js +150 -0
- package/src/scan/generator.js +418 -351
- package/src/scan/gh-secrets.js +107 -0
- package/src/spec/generator.js +519 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/sync.js +107 -61
- package/src/ui/markdown.js +220 -220
package/src/cost/tracker.js
CHANGED
|
@@ -1,171 +1,171 @@
|
|
|
1
|
-
const DEFAULT_MODEL_PRICING = Object.freeze({
|
|
2
|
-
"gpt-4o": Object.freeze({
|
|
3
|
-
inputPerMillionUsd: 2.5,
|
|
4
|
-
outputPerMillionUsd: 10.0,
|
|
5
|
-
}),
|
|
6
|
-
"gpt-5.3-codex": Object.freeze({
|
|
7
|
-
inputPerMillionUsd: 1.5,
|
|
8
|
-
outputPerMillionUsd: 6.0,
|
|
9
|
-
}),
|
|
10
|
-
"claude-sonnet-4": Object.freeze({
|
|
11
|
-
inputPerMillionUsd: 3.0,
|
|
12
|
-
outputPerMillionUsd: 15.0,
|
|
13
|
-
}),
|
|
14
|
-
"claude-sonnet-4.5": Object.freeze({
|
|
15
|
-
inputPerMillionUsd: 3.0,
|
|
16
|
-
outputPerMillionUsd: 15.0,
|
|
17
|
-
}),
|
|
18
|
-
"gemini-2.5-pro": Object.freeze({
|
|
19
|
-
inputPerMillionUsd: 2.5,
|
|
20
|
-
outputPerMillionUsd: 10.0,
|
|
21
|
-
}),
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
function normalizeTokenCount(value, field) {
|
|
25
|
-
const normalized = Number(value || 0);
|
|
26
|
-
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
27
|
-
throw new Error(`${field} must be a non-negative number.`);
|
|
28
|
-
}
|
|
29
|
-
return normalized;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function normalizeUsd(value, field) {
|
|
33
|
-
const normalized = Number(value || 0);
|
|
34
|
-
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
35
|
-
throw new Error(`${field} must be a non-negative number.`);
|
|
36
|
-
}
|
|
37
|
-
return normalized;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function roundUsd(value) {
|
|
41
|
-
return Math.round((Number(value || 0) + Number.EPSILON) * 1_000_000) / 1_000_000;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Estimate cost in USD from token counts and per-million pricing inputs.
|
|
46
|
-
*
|
|
47
|
-
* @param {{
|
|
48
|
-
* inputTokens?: number,
|
|
49
|
-
* outputTokens?: number,
|
|
50
|
-
* inputPerMillionUsd?: number,
|
|
51
|
-
* outputPerMillionUsd?: number
|
|
52
|
-
* }} [options]
|
|
53
|
-
* @returns {number}
|
|
54
|
-
*/
|
|
55
|
-
export function estimateCostUsd({
|
|
56
|
-
inputTokens = 0,
|
|
57
|
-
outputTokens = 0,
|
|
58
|
-
inputPerMillionUsd = 0,
|
|
59
|
-
outputPerMillionUsd = 0,
|
|
60
|
-
} = {}) {
|
|
61
|
-
const normalizedInputTokens = normalizeTokenCount(inputTokens, "inputTokens");
|
|
62
|
-
const normalizedOutputTokens = normalizeTokenCount(outputTokens, "outputTokens");
|
|
63
|
-
const normalizedInputRate = normalizeUsd(inputPerMillionUsd, "inputPerMillionUsd");
|
|
64
|
-
const normalizedOutputRate = normalizeUsd(outputPerMillionUsd, "outputPerMillionUsd");
|
|
65
|
-
|
|
66
|
-
const inputCost = (normalizedInputTokens / 1_000_000) * normalizedInputRate;
|
|
67
|
-
const outputCost = (normalizedOutputTokens / 1_000_000) * normalizedOutputRate;
|
|
68
|
-
|
|
69
|
-
return roundUsd(inputCost + outputCost);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Estimate cost in USD using a named model pricing table entry.
|
|
74
|
-
*
|
|
75
|
-
* @param {{
|
|
76
|
-
* modelId: string,
|
|
77
|
-
* inputTokens?: number,
|
|
78
|
-
* outputTokens?: number,
|
|
79
|
-
* pricingTable?: Record<string, { inputPerMillionUsd: number, outputPerMillionUsd: number }>
|
|
80
|
-
* }} [options]
|
|
81
|
-
* @returns {number}
|
|
82
|
-
*/
|
|
83
|
-
export function estimateModelCost({
|
|
84
|
-
modelId,
|
|
85
|
-
inputTokens = 0,
|
|
86
|
-
outputTokens = 0,
|
|
87
|
-
pricingTable = DEFAULT_MODEL_PRICING,
|
|
88
|
-
} = {}) {
|
|
89
|
-
const normalizedModelId = String(modelId || "").trim();
|
|
90
|
-
if (!normalizedModelId) {
|
|
91
|
-
throw new Error("modelId is required for model-based cost estimation.");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const modelPricing = pricingTable[normalizedModelId];
|
|
95
|
-
if (!modelPricing) {
|
|
96
|
-
throw new Error(`No pricing data configured for model '${normalizedModelId}'.`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return estimateCostUsd({
|
|
100
|
-
inputTokens,
|
|
101
|
-
outputTokens,
|
|
102
|
-
inputPerMillionUsd: modelPricing.inputPerMillionUsd,
|
|
103
|
-
outputPerMillionUsd: modelPricing.outputPerMillionUsd,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Aggregate usage rows into a single token and cost summary.
|
|
109
|
-
*
|
|
110
|
-
* @param {Array<{ inputTokens?: number, outputTokens?: number, costUsd?: number }>} [entries]
|
|
111
|
-
* @returns {{ inputTokens: number, outputTokens: number, costUsd: number }}
|
|
112
|
-
*/
|
|
113
|
-
export function rollupUsage(entries = []) {
|
|
114
|
-
if (!Array.isArray(entries)) {
|
|
115
|
-
throw new Error("entries must be an array.");
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const totals = entries.reduce(
|
|
119
|
-
(accumulator, entry) => {
|
|
120
|
-
const inputTokens = normalizeTokenCount(entry?.inputTokens || 0, "entry.inputTokens");
|
|
121
|
-
const outputTokens = normalizeTokenCount(entry?.outputTokens || 0, "entry.outputTokens");
|
|
122
|
-
const costUsd = normalizeUsd(entry?.costUsd || 0, "entry.costUsd");
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
inputTokens: accumulator.inputTokens + inputTokens,
|
|
126
|
-
outputTokens: accumulator.outputTokens + outputTokens,
|
|
127
|
-
costUsd: accumulator.costUsd + costUsd,
|
|
128
|
-
};
|
|
129
|
-
},
|
|
130
|
-
{ inputTokens: 0, outputTokens: 0, costUsd: 0 }
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
inputTokens: totals.inputTokens,
|
|
135
|
-
outputTokens: totals.outputTokens,
|
|
136
|
-
costUsd: roundUsd(totals.costUsd),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Evaluate whether cumulative cost has exceeded a configured budget.
|
|
142
|
-
*
|
|
143
|
-
* @param {{ totalCostUsd?: number, budgetUsd?: number }} [options]
|
|
144
|
-
* @returns {{ budgetUsd: number, totalCostUsd: number, remainingUsd: number, exceeded: boolean }}
|
|
145
|
-
*/
|
|
146
|
-
export function enforceCostBudget({ totalCostUsd = 0, budgetUsd = 0 } = {}) {
|
|
147
|
-
const normalizedTotal = normalizeUsd(totalCostUsd, "totalCostUsd");
|
|
148
|
-
const normalizedBudget = normalizeUsd(budgetUsd, "budgetUsd");
|
|
149
|
-
const remainingUsd = roundUsd(Math.max(0, normalizedBudget - normalizedTotal));
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
budgetUsd: roundUsd(normalizedBudget),
|
|
153
|
-
totalCostUsd: roundUsd(normalizedTotal),
|
|
154
|
-
remainingUsd,
|
|
155
|
-
exceeded: normalizedTotal > normalizedBudget,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Return the built-in model pricing catalog for diagnostics and UI display.
|
|
161
|
-
*
|
|
162
|
-
* @returns {Array<{ modelId: string, inputPerMillionUsd: number, outputPerMillionUsd: number }>}
|
|
163
|
-
*/
|
|
164
|
-
export function listKnownModelPricing() {
|
|
165
|
-
return Object.entries(DEFAULT_MODEL_PRICING).map(([modelId, pricing]) => ({
|
|
166
|
-
modelId,
|
|
167
|
-
inputPerMillionUsd: pricing.inputPerMillionUsd,
|
|
168
|
-
outputPerMillionUsd: pricing.outputPerMillionUsd,
|
|
169
|
-
}));
|
|
170
|
-
}
|
|
171
|
-
|
|
1
|
+
const DEFAULT_MODEL_PRICING = Object.freeze({
|
|
2
|
+
"gpt-4o": Object.freeze({
|
|
3
|
+
inputPerMillionUsd: 2.5,
|
|
4
|
+
outputPerMillionUsd: 10.0,
|
|
5
|
+
}),
|
|
6
|
+
"gpt-5.3-codex": Object.freeze({
|
|
7
|
+
inputPerMillionUsd: 1.5,
|
|
8
|
+
outputPerMillionUsd: 6.0,
|
|
9
|
+
}),
|
|
10
|
+
"claude-sonnet-4": Object.freeze({
|
|
11
|
+
inputPerMillionUsd: 3.0,
|
|
12
|
+
outputPerMillionUsd: 15.0,
|
|
13
|
+
}),
|
|
14
|
+
"claude-sonnet-4.5": Object.freeze({
|
|
15
|
+
inputPerMillionUsd: 3.0,
|
|
16
|
+
outputPerMillionUsd: 15.0,
|
|
17
|
+
}),
|
|
18
|
+
"gemini-2.5-pro": Object.freeze({
|
|
19
|
+
inputPerMillionUsd: 2.5,
|
|
20
|
+
outputPerMillionUsd: 10.0,
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function normalizeTokenCount(value, field) {
|
|
25
|
+
const normalized = Number(value || 0);
|
|
26
|
+
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
27
|
+
throw new Error(`${field} must be a non-negative number.`);
|
|
28
|
+
}
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeUsd(value, field) {
|
|
33
|
+
const normalized = Number(value || 0);
|
|
34
|
+
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
35
|
+
throw new Error(`${field} must be a non-negative number.`);
|
|
36
|
+
}
|
|
37
|
+
return normalized;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function roundUsd(value) {
|
|
41
|
+
return Math.round((Number(value || 0) + Number.EPSILON) * 1_000_000) / 1_000_000;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Estimate cost in USD from token counts and per-million pricing inputs.
|
|
46
|
+
*
|
|
47
|
+
* @param {{
|
|
48
|
+
* inputTokens?: number,
|
|
49
|
+
* outputTokens?: number,
|
|
50
|
+
* inputPerMillionUsd?: number,
|
|
51
|
+
* outputPerMillionUsd?: number
|
|
52
|
+
* }} [options]
|
|
53
|
+
* @returns {number}
|
|
54
|
+
*/
|
|
55
|
+
export function estimateCostUsd({
|
|
56
|
+
inputTokens = 0,
|
|
57
|
+
outputTokens = 0,
|
|
58
|
+
inputPerMillionUsd = 0,
|
|
59
|
+
outputPerMillionUsd = 0,
|
|
60
|
+
} = {}) {
|
|
61
|
+
const normalizedInputTokens = normalizeTokenCount(inputTokens, "inputTokens");
|
|
62
|
+
const normalizedOutputTokens = normalizeTokenCount(outputTokens, "outputTokens");
|
|
63
|
+
const normalizedInputRate = normalizeUsd(inputPerMillionUsd, "inputPerMillionUsd");
|
|
64
|
+
const normalizedOutputRate = normalizeUsd(outputPerMillionUsd, "outputPerMillionUsd");
|
|
65
|
+
|
|
66
|
+
const inputCost = (normalizedInputTokens / 1_000_000) * normalizedInputRate;
|
|
67
|
+
const outputCost = (normalizedOutputTokens / 1_000_000) * normalizedOutputRate;
|
|
68
|
+
|
|
69
|
+
return roundUsd(inputCost + outputCost);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Estimate cost in USD using a named model pricing table entry.
|
|
74
|
+
*
|
|
75
|
+
* @param {{
|
|
76
|
+
* modelId: string,
|
|
77
|
+
* inputTokens?: number,
|
|
78
|
+
* outputTokens?: number,
|
|
79
|
+
* pricingTable?: Record<string, { inputPerMillionUsd: number, outputPerMillionUsd: number }>
|
|
80
|
+
* }} [options]
|
|
81
|
+
* @returns {number}
|
|
82
|
+
*/
|
|
83
|
+
export function estimateModelCost({
|
|
84
|
+
modelId,
|
|
85
|
+
inputTokens = 0,
|
|
86
|
+
outputTokens = 0,
|
|
87
|
+
pricingTable = DEFAULT_MODEL_PRICING,
|
|
88
|
+
} = {}) {
|
|
89
|
+
const normalizedModelId = String(modelId || "").trim();
|
|
90
|
+
if (!normalizedModelId) {
|
|
91
|
+
throw new Error("modelId is required for model-based cost estimation.");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const modelPricing = pricingTable[normalizedModelId];
|
|
95
|
+
if (!modelPricing) {
|
|
96
|
+
throw new Error(`No pricing data configured for model '${normalizedModelId}'.`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return estimateCostUsd({
|
|
100
|
+
inputTokens,
|
|
101
|
+
outputTokens,
|
|
102
|
+
inputPerMillionUsd: modelPricing.inputPerMillionUsd,
|
|
103
|
+
outputPerMillionUsd: modelPricing.outputPerMillionUsd,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Aggregate usage rows into a single token and cost summary.
|
|
109
|
+
*
|
|
110
|
+
* @param {Array<{ inputTokens?: number, outputTokens?: number, costUsd?: number }>} [entries]
|
|
111
|
+
* @returns {{ inputTokens: number, outputTokens: number, costUsd: number }}
|
|
112
|
+
*/
|
|
113
|
+
export function rollupUsage(entries = []) {
|
|
114
|
+
if (!Array.isArray(entries)) {
|
|
115
|
+
throw new Error("entries must be an array.");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const totals = entries.reduce(
|
|
119
|
+
(accumulator, entry) => {
|
|
120
|
+
const inputTokens = normalizeTokenCount(entry?.inputTokens || 0, "entry.inputTokens");
|
|
121
|
+
const outputTokens = normalizeTokenCount(entry?.outputTokens || 0, "entry.outputTokens");
|
|
122
|
+
const costUsd = normalizeUsd(entry?.costUsd || 0, "entry.costUsd");
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
inputTokens: accumulator.inputTokens + inputTokens,
|
|
126
|
+
outputTokens: accumulator.outputTokens + outputTokens,
|
|
127
|
+
costUsd: accumulator.costUsd + costUsd,
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
{ inputTokens: 0, outputTokens: 0, costUsd: 0 }
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
inputTokens: totals.inputTokens,
|
|
135
|
+
outputTokens: totals.outputTokens,
|
|
136
|
+
costUsd: roundUsd(totals.costUsd),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Evaluate whether cumulative cost has exceeded a configured budget.
|
|
142
|
+
*
|
|
143
|
+
* @param {{ totalCostUsd?: number, budgetUsd?: number }} [options]
|
|
144
|
+
* @returns {{ budgetUsd: number, totalCostUsd: number, remainingUsd: number, exceeded: boolean }}
|
|
145
|
+
*/
|
|
146
|
+
export function enforceCostBudget({ totalCostUsd = 0, budgetUsd = 0 } = {}) {
|
|
147
|
+
const normalizedTotal = normalizeUsd(totalCostUsd, "totalCostUsd");
|
|
148
|
+
const normalizedBudget = normalizeUsd(budgetUsd, "budgetUsd");
|
|
149
|
+
const remainingUsd = roundUsd(Math.max(0, normalizedBudget - normalizedTotal));
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
budgetUsd: roundUsd(normalizedBudget),
|
|
153
|
+
totalCostUsd: roundUsd(normalizedTotal),
|
|
154
|
+
remainingUsd,
|
|
155
|
+
exceeded: normalizedTotal > normalizedBudget,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Return the built-in model pricing catalog for diagnostics and UI display.
|
|
161
|
+
*
|
|
162
|
+
* @returns {Array<{ modelId: string, inputPerMillionUsd: number, outputPerMillionUsd: number }>}
|
|
163
|
+
*/
|
|
164
|
+
export function listKnownModelPricing() {
|
|
165
|
+
return Object.entries(DEFAULT_MODEL_PRICING).map(([modelId, pricing]) => ({
|
|
166
|
+
modelId,
|
|
167
|
+
inputPerMillionUsd: pricing.inputPerMillionUsd,
|
|
168
|
+
outputPerMillionUsd: pricing.outputPerMillionUsd,
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
|