nvicode 0.1.2 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/usage.js ADDED
@@ -0,0 +1,146 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { getNvicodePaths } from "./config.js";
3
+ const OPUS_4_6_INPUT_USD_PER_MTOK = 5;
4
+ const OPUS_4_6_OUTPUT_USD_PER_MTOK = 25;
5
+ const OPUS_4_6_PRICING_SOURCE = "https://www.anthropic.com/claude/opus";
6
+ const OPUS_4_6_PRICING_UPDATED_AT = "2026-03-30";
7
+ const getEnvUsdRate = (name, fallback) => {
8
+ const raw = process.env[name];
9
+ if (raw === undefined || raw === null || raw.trim() === "") {
10
+ return fallback;
11
+ }
12
+ const parsed = Number(raw);
13
+ if (!Number.isFinite(parsed) || parsed < 0) {
14
+ return fallback;
15
+ }
16
+ return parsed;
17
+ };
18
+ export const getPricingSnapshot = () => ({
19
+ providerInputUsdPerMTok: getEnvUsdRate("NVICODE_INPUT_USD_PER_MTOK", 0),
20
+ providerOutputUsdPerMTok: getEnvUsdRate("NVICODE_OUTPUT_USD_PER_MTOK", 0),
21
+ compareModel: "Claude Opus 4.6",
22
+ compareInputUsdPerMTok: OPUS_4_6_INPUT_USD_PER_MTOK,
23
+ compareOutputUsdPerMTok: OPUS_4_6_OUTPUT_USD_PER_MTOK,
24
+ comparePricingSource: OPUS_4_6_PRICING_SOURCE,
25
+ comparePricingUpdatedAt: OPUS_4_6_PRICING_UPDATED_AT,
26
+ });
27
+ export const estimateCostUsd = (inputTokens, outputTokens, inputUsdPerMTok, outputUsdPerMTok) => (inputTokens / 1_000_000) * inputUsdPerMTok +
28
+ (outputTokens / 1_000_000) * outputUsdPerMTok;
29
+ export const buildUsageRecord = ({ id, timestamp = new Date().toISOString(), status, model, inputTokens, outputTokens, turnInputTokens, turnOutputTokens, visibleInputTokens, visibleOutputTokens, latencyMs, stopReason, error, pricing = getPricingSnapshot(), }) => {
30
+ const providerCostUsd = estimateCostUsd(inputTokens, outputTokens, pricing.providerInputUsdPerMTok, pricing.providerOutputUsdPerMTok);
31
+ const compareCostUsd = estimateCostUsd(inputTokens, outputTokens, pricing.compareInputUsdPerMTok, pricing.compareOutputUsdPerMTok);
32
+ return {
33
+ id,
34
+ timestamp,
35
+ status,
36
+ model,
37
+ inputTokens,
38
+ outputTokens,
39
+ ...(turnInputTokens !== undefined
40
+ ? { turnInputTokens }
41
+ : {}),
42
+ ...(turnOutputTokens !== undefined
43
+ ? { turnOutputTokens }
44
+ : {}),
45
+ ...(visibleInputTokens !== undefined
46
+ ? { visibleInputTokens }
47
+ : {}),
48
+ ...(visibleOutputTokens !== undefined
49
+ ? { visibleOutputTokens }
50
+ : {}),
51
+ latencyMs,
52
+ providerCostUsd,
53
+ compareCostUsd,
54
+ savingsUsd: compareCostUsd - providerCostUsd,
55
+ stopReason: stopReason ?? null,
56
+ ...(error ? { error } : {}),
57
+ pricing,
58
+ };
59
+ };
60
+ export const appendUsageRecord = async (record) => {
61
+ const paths = getNvicodePaths();
62
+ await fs.mkdir(paths.stateDir, { recursive: true });
63
+ await fs.appendFile(paths.usageLogFile, `${JSON.stringify(record)}\n`, "utf8");
64
+ };
65
+ export const readUsageRecords = async () => {
66
+ const paths = getNvicodePaths();
67
+ try {
68
+ const raw = await fs.readFile(paths.usageLogFile, "utf8");
69
+ return raw
70
+ .split("\n")
71
+ .map((line) => line.trim())
72
+ .filter(Boolean)
73
+ .map((line) => JSON.parse(line))
74
+ .filter((record) => typeof record.timestamp === "string")
75
+ .sort((left, right) => right.timestamp.localeCompare(left.timestamp));
76
+ }
77
+ catch (error) {
78
+ if (error.code === "ENOENT") {
79
+ return [];
80
+ }
81
+ throw error;
82
+ }
83
+ };
84
+ export const summarizeUsage = (records) => records.reduce((summary, record) => {
85
+ summary.requests += 1;
86
+ summary.successes += record.status === "success" ? 1 : 0;
87
+ summary.errors += record.status === "error" ? 1 : 0;
88
+ summary.inputTokens += record.inputTokens;
89
+ summary.outputTokens += record.outputTokens;
90
+ summary.turnInputTokens +=
91
+ record.turnInputTokens ??
92
+ record.visibleInputTokens ??
93
+ record.inputTokens;
94
+ summary.turnOutputTokens +=
95
+ record.turnOutputTokens ??
96
+ record.visibleOutputTokens ??
97
+ record.outputTokens;
98
+ summary.visibleInputTokens += record.visibleInputTokens ?? record.inputTokens;
99
+ summary.visibleOutputTokens += record.visibleOutputTokens ?? record.outputTokens;
100
+ summary.providerCostUsd += record.providerCostUsd;
101
+ summary.compareCostUsd += record.compareCostUsd;
102
+ summary.savingsUsd += record.savingsUsd;
103
+ return summary;
104
+ }, {
105
+ requests: 0,
106
+ successes: 0,
107
+ errors: 0,
108
+ inputTokens: 0,
109
+ outputTokens: 0,
110
+ turnInputTokens: 0,
111
+ turnOutputTokens: 0,
112
+ visibleInputTokens: 0,
113
+ visibleOutputTokens: 0,
114
+ providerCostUsd: 0,
115
+ compareCostUsd: 0,
116
+ savingsUsd: 0,
117
+ });
118
+ export const filterRecordsSince = (records, sinceMs) => records.filter((record) => {
119
+ const timestamp = Date.parse(record.timestamp);
120
+ return !Number.isNaN(timestamp) && timestamp >= sinceMs;
121
+ });
122
+ const integerFormatter = new Intl.NumberFormat("en-US");
123
+ const moneyFormatter = new Intl.NumberFormat("en-US", {
124
+ style: "currency",
125
+ currency: "USD",
126
+ minimumFractionDigits: 4,
127
+ maximumFractionDigits: 4,
128
+ });
129
+ export const formatInteger = (value) => integerFormatter.format(Math.round(value));
130
+ export const formatUsd = (value) => moneyFormatter.format(value);
131
+ export const formatDuration = (ms) => {
132
+ if (ms < 1_000) {
133
+ return `${ms}ms`;
134
+ }
135
+ if (ms < 60_000) {
136
+ return `${(ms / 1_000).toFixed(1)}s`;
137
+ }
138
+ return `${(ms / 60_000).toFixed(1)}m`;
139
+ };
140
+ export const formatTimestamp = (value) => {
141
+ const timestamp = new Date(value);
142
+ if (Number.isNaN(timestamp.getTime())) {
143
+ return value;
144
+ }
145
+ return timestamp.toISOString().replace("T", " ").slice(0, 19);
146
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nvicode",
3
- "version": "0.1.2",
4
- "description": "Run Claude Code through NVIDIA-hosted models using a local Anthropic-compatible gateway.",
3
+ "version": "0.1.6",
4
+ "description": "Run Claude Code through NVIDIA-hosted models or OpenRouter using a simple CLI wrapper.",
5
5
  "author": "Dinesh Potla",
6
6
  "keywords": [
7
7
  "claude-code",