tokmon 0.27.0 → 0.28.1
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 +1 -1
- package/dist/{bootstrap-ink-3YNJFKCW.js → bootstrap-ink-L7QSJBMS.js} +142 -12
- package/dist/{chunk-4CY725NY.js → chunk-3RTWFGGD.js} +1 -1
- package/dist/{chunk-AMD4PXDG.js → chunk-4HU4EI5T.js} +1 -1
- package/dist/{chunk-RTGMDE7U.js → chunk-CVKCVHS7.js} +143 -81
- package/dist/{chunk-VGYBQH5I.js → chunk-FMP3P2WV.js} +2 -2
- package/dist/{chunk-E2YXYU73.js → chunk-HP5UZCXP.js} +29 -0
- package/dist/{chunk-OWPJUG4C.js → chunk-S33XIUAW.js} +11 -11
- package/dist/{chunk-QM5E5RJZ.js → chunk-XDA5RJST.js} +1 -1
- package/dist/{cli-command-U7RZ2GTD.js → cli-command-75LP4IDS.js} +5 -5
- package/dist/cli.js +6 -6
- package/dist/{config-2PXUENQL.js → config-HXFJTNLM.js} +5 -1
- package/dist/{daemon-PQWKWRC6.js → daemon-CL4FJW26.js} +5 -5
- package/dist/{daemon-handle-2GVZT55B.js → daemon-handle-QQLJE46Y.js} +3 -3
- package/dist/server-EUO7CWYH.js +11 -0
- package/dist/web/assets/{Area-Tx1mUuqM.js → Area-CMXvOw33.js} +1 -1
- package/dist/web/assets/{analytics-CAzklhXc.js → analytics-DqdZXOJL.js} +2 -2
- package/dist/web/assets/{breakdown-BoPnOv7Y.js → breakdown-kOSSJ3mI.js} +1 -1
- package/dist/web/assets/{chart-jK7A9m7R.js → chart-cZoLgpmG.js} +1 -1
- package/dist/web/assets/{explore-e8wtwk7s.js → explore-vcsM8K9I.js} +1 -1
- package/dist/web/assets/index-BErCXT7f.css +1 -0
- package/dist/web/assets/index-DH3dOnbg.js +83 -0
- package/dist/web/assets/{models-_4IZNDxW.js → models-B5ikm83_.js} +2 -2
- package/dist/web/assets/{overview-BBOfd_Fd.js → overview-DD1kP8CP.js} +2 -2
- package/dist/web/assets/{panel-CstB4hPm.js → panel-DTQBwExW.js} +1 -1
- package/dist/web/assets/{primitives-G9ALG7ih.js → primitives-C36qJlA2.js} +1 -1
- package/dist/web/assets/settings-sheet-_i2zTQjf.js +1 -0
- package/dist/web/assets/{share-sheet-BwmnebRB.js → share-sheet-BLHmrFbm.js} +2 -2
- package/dist/web/assets/{timeline-DkYWH6XW.js → timeline-B4uG-Emo.js} +1 -1
- package/dist/web/assets/{use-dialog-trap-Jci2xaMX.js → use-dialog-trap-bfaXEMz9.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/server-5WIWS27N.js +0 -11
- package/dist/web/assets/index-C8oe6EfM.js +0 -83
- package/dist/web/assets/index-NnKaHxPO.css +0 -1
- package/dist/web/assets/settings-sheet-rq6vbYXM.js +0 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
envDir,
|
|
6
6
|
expandHome,
|
|
7
7
|
isValidTimezone
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-HP5UZCXP.js";
|
|
9
9
|
|
|
10
10
|
// src/async.ts
|
|
11
11
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
@@ -744,8 +744,8 @@ import { Schema } from "effect";
|
|
|
744
744
|
import * as Rpc from "effect/unstable/rpc/Rpc";
|
|
745
745
|
import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
|
746
746
|
var TOKMON_WS_PATH = "/ws";
|
|
747
|
-
var TOKMON_PROTOCOL_VERSION =
|
|
748
|
-
var TOKMON_CAPABILITIES = ["config-cas", "config-revision"];
|
|
747
|
+
var TOKMON_PROTOCOL_VERSION = 3;
|
|
748
|
+
var TOKMON_CAPABILITIES = ["config-cas", "config-revision", "allowed-hosts"];
|
|
749
749
|
var TOKMON_WS_METHODS = {
|
|
750
750
|
getConfig: "tokmon.getConfig",
|
|
751
751
|
setConfig: "tokmon.setConfig",
|
|
@@ -787,6 +787,7 @@ var ConfigSchema = Schema.Struct({
|
|
|
787
787
|
defaultFocus: Schema.Literals(["all", "last"]),
|
|
788
788
|
ascii: Schema.Literals(["auto", "on", "off"]),
|
|
789
789
|
allowNetworkAccess: Schema.Boolean,
|
|
790
|
+
allowedHosts: Schema.Array(Schema.String),
|
|
790
791
|
resetDisplay: Schema.Literals(["relative", "absolute"]),
|
|
791
792
|
knownProviders: Schema.Array(ProviderIdSchema)
|
|
792
793
|
});
|
|
@@ -892,7 +893,8 @@ var BillingResultSchema = Schema.Struct({
|
|
|
892
893
|
name: Schema.String,
|
|
893
894
|
usd: Schema.Finite,
|
|
894
895
|
requests: Schema.Finite
|
|
895
|
-
}))))
|
|
896
|
+
})))),
|
|
897
|
+
asOfMs: Schema.optionalKey(Schema.Finite)
|
|
896
898
|
});
|
|
897
899
|
var WebAccountSchema = Schema.Struct({
|
|
898
900
|
id: Schema.String,
|
|
@@ -1003,6 +1005,16 @@ function formatTime(date, tz) {
|
|
|
1003
1005
|
timeZone: tz
|
|
1004
1006
|
});
|
|
1005
1007
|
}
|
|
1008
|
+
function formatAgo(ms, now) {
|
|
1009
|
+
const seconds = Math.max(0, Math.round((now - ms) / 1e3));
|
|
1010
|
+
if (seconds < 2) return "just now";
|
|
1011
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
1012
|
+
const minutes = Math.round(seconds / 60);
|
|
1013
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
1014
|
+
const hours = Math.round(minutes / 60);
|
|
1015
|
+
if (hours < 24) return `${hours}h ago`;
|
|
1016
|
+
return `${Math.round(hours / 24)}d ago`;
|
|
1017
|
+
}
|
|
1006
1018
|
function formatShortDate(label, opts = {}) {
|
|
1007
1019
|
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(label);
|
|
1008
1020
|
if (!match) return label;
|
|
@@ -1128,7 +1140,7 @@ async function detectClaude(homeDir) {
|
|
|
1128
1140
|
return false;
|
|
1129
1141
|
}
|
|
1130
1142
|
function claudePriceFor(model, timestamp = Date.now()) {
|
|
1131
|
-
const m = model.toLowerCase().trim();
|
|
1143
|
+
const m = model.toLowerCase().trim().replace(/\[[^\]]*\]$/, "");
|
|
1132
1144
|
for (const key of PRICE_KEYS) {
|
|
1133
1145
|
if (!m.startsWith(key)) continue;
|
|
1134
1146
|
const rest = m.slice(key.length);
|
|
@@ -2076,7 +2088,7 @@ async function fetchResetCredits(headers) {
|
|
|
2076
2088
|
return void 0;
|
|
2077
2089
|
}
|
|
2078
2090
|
}
|
|
2079
|
-
async function liveBilling(auth) {
|
|
2091
|
+
async function liveBilling(auth, failure) {
|
|
2080
2092
|
try {
|
|
2081
2093
|
const headers = {
|
|
2082
2094
|
"Authorization": `Bearer ${auth.accessToken}`,
|
|
@@ -2085,7 +2097,10 @@ async function liveBilling(auth) {
|
|
|
2085
2097
|
};
|
|
2086
2098
|
if (auth.accountId) headers["ChatGPT-Account-Id"] = auth.accountId;
|
|
2087
2099
|
const res = await fetch(USAGE_URL, { headers, signal: AbortSignal.timeout(1e4) });
|
|
2088
|
-
if (!res.ok)
|
|
2100
|
+
if (!res.ok) {
|
|
2101
|
+
if (failure) failure.status = res.status;
|
|
2102
|
+
return null;
|
|
2103
|
+
}
|
|
2089
2104
|
const data = await readJson(res);
|
|
2090
2105
|
if (!data) return null;
|
|
2091
2106
|
const headerPct = (name) => {
|
|
@@ -2186,19 +2201,19 @@ async function snapshotBilling(homeDir, auth = null) {
|
|
|
2186
2201
|
}
|
|
2187
2202
|
async function codexBilling(account) {
|
|
2188
2203
|
const auth = await getAuth2(account.homeDir);
|
|
2204
|
+
const failure = {};
|
|
2189
2205
|
if (auth) {
|
|
2190
|
-
const live = await liveBilling(auth);
|
|
2206
|
+
const live = await liveBilling(auth, failure);
|
|
2191
2207
|
if (live) return live;
|
|
2192
2208
|
}
|
|
2193
2209
|
const snap = await snapshotBilling(account.homeDir, auth);
|
|
2194
2210
|
if (snap && Date.now() - snap.asOfMs < SNAPSHOT_STALE_MS) {
|
|
2195
|
-
|
|
2196
|
-
return result;
|
|
2211
|
+
return snap;
|
|
2197
2212
|
}
|
|
2198
2213
|
return {
|
|
2199
2214
|
plan: auth?.plan ?? snap?.plan ?? null,
|
|
2200
2215
|
metrics: [],
|
|
2201
|
-
error: auth ?
|
|
2216
|
+
error: auth ? `Usage API failed${failure.status ? ` (HTTP ${failure.status})` : ""} \u2014 run codex to refresh` : "Not logged in \u2014 run codex",
|
|
2202
2217
|
...identityFields(auth)
|
|
2203
2218
|
};
|
|
2204
2219
|
}
|
|
@@ -2789,8 +2804,15 @@ async function fetchLocalEntries(tz, homeDir) {
|
|
|
2789
2804
|
function overlayEntries(api, local, tz) {
|
|
2790
2805
|
if (api.length === 0) return local.map((l) => l.entry);
|
|
2791
2806
|
if (local.length === 0) return api;
|
|
2792
|
-
|
|
2793
|
-
|
|
2807
|
+
let apiMin = Infinity;
|
|
2808
|
+
let apiMax = -Infinity;
|
|
2809
|
+
for (const e of api) {
|
|
2810
|
+
if (e.ts < apiMin) apiMin = e.ts;
|
|
2811
|
+
if (e.ts > apiMax) apiMax = e.ts;
|
|
2812
|
+
}
|
|
2813
|
+
const minDay = dayKey(apiMin, tz);
|
|
2814
|
+
const maxDay = dayKey(apiMax, tz);
|
|
2815
|
+
return [...api, ...local.filter((l) => l.day < minDay || l.day > maxDay).map((l) => l.entry)];
|
|
2794
2816
|
}
|
|
2795
2817
|
async function cursorEntries(since, tz, homeDir) {
|
|
2796
2818
|
const [apiResult, local] = await Promise.all([fetchApiEntries(homeDir), fetchLocalEntries(tz, homeDir)]);
|
|
@@ -2833,6 +2855,33 @@ async function detectPi(homeDir) {
|
|
|
2833
2855
|
return false;
|
|
2834
2856
|
}
|
|
2835
2857
|
}
|
|
2858
|
+
function recordToEntry(obj) {
|
|
2859
|
+
if (obj?.type !== "message") return null;
|
|
2860
|
+
const msg = obj.message;
|
|
2861
|
+
if (msg?.role !== "assistant" || !msg?.usage) return null;
|
|
2862
|
+
const u = msg.usage;
|
|
2863
|
+
const ts = timestampMs(obj.timestamp ?? msg.timestamp);
|
|
2864
|
+
if (ts === null) return null;
|
|
2865
|
+
const input = safeNum(u.input);
|
|
2866
|
+
const output = safeNum(u.output);
|
|
2867
|
+
const cacheRead = safeNum(u.cacheRead);
|
|
2868
|
+
const cacheCreate = safeNum(u.cacheWrite);
|
|
2869
|
+
if (input + output + cacheRead + cacheCreate === 0) return null;
|
|
2870
|
+
const c = u.cost ?? {};
|
|
2871
|
+
const costInput = finitePositive(c.input);
|
|
2872
|
+
const cacheSavings = input > 0 && cacheRead > 0 ? Math.max(0, cacheRead * (costInput / input) - finitePositive(c.cacheRead)) : 0;
|
|
2873
|
+
const model = typeof msg.responseModel === "string" && msg.responseModel || typeof msg.model === "string" && msg.model || "unknown";
|
|
2874
|
+
return {
|
|
2875
|
+
ts,
|
|
2876
|
+
model,
|
|
2877
|
+
cost: finitePositive(c.total),
|
|
2878
|
+
input,
|
|
2879
|
+
output,
|
|
2880
|
+
cacheCreate,
|
|
2881
|
+
cacheRead,
|
|
2882
|
+
cacheSavings
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2836
2885
|
async function parseFile3(path) {
|
|
2837
2886
|
const entries = [];
|
|
2838
2887
|
const stream = createReadStream4(path);
|
|
@@ -2844,32 +2893,8 @@ async function parseFile3(path) {
|
|
|
2844
2893
|
if (!rawLine.includes('"usage"')) continue;
|
|
2845
2894
|
try {
|
|
2846
2895
|
const line = rawLine.charCodeAt(0) === 65279 ? rawLine.slice(1) : rawLine;
|
|
2847
|
-
const
|
|
2848
|
-
if (
|
|
2849
|
-
const msg = obj.message;
|
|
2850
|
-
if (msg?.role !== "assistant" || !msg?.usage) continue;
|
|
2851
|
-
const u = msg.usage;
|
|
2852
|
-
const ts = timestampMs(obj.timestamp ?? msg.timestamp);
|
|
2853
|
-
if (ts === null) continue;
|
|
2854
|
-
const input = safeNum(u.input);
|
|
2855
|
-
const output = safeNum(u.output);
|
|
2856
|
-
const cacheRead = safeNum(u.cacheRead);
|
|
2857
|
-
const cacheCreate = safeNum(u.cacheWrite);
|
|
2858
|
-
if (input + output + cacheRead + cacheCreate === 0) continue;
|
|
2859
|
-
const c = u.cost ?? {};
|
|
2860
|
-
const costInput = finitePositive(c.input);
|
|
2861
|
-
const cacheSavings = input > 0 && cacheRead > 0 ? Math.max(0, cacheRead * (costInput / input) - finitePositive(c.cacheRead)) : 0;
|
|
2862
|
-
const model = typeof msg.responseModel === "string" && msg.responseModel || typeof msg.model === "string" && msg.model || "unknown";
|
|
2863
|
-
entries.push({
|
|
2864
|
-
ts,
|
|
2865
|
-
model,
|
|
2866
|
-
cost: finitePositive(c.total),
|
|
2867
|
-
input,
|
|
2868
|
-
output,
|
|
2869
|
-
cacheCreate,
|
|
2870
|
-
cacheRead,
|
|
2871
|
-
cacheSavings
|
|
2872
|
-
});
|
|
2896
|
+
const entry = recordToEntry(JSON.parse(line));
|
|
2897
|
+
if (entry) entries.push(entry);
|
|
2873
2898
|
} catch {
|
|
2874
2899
|
}
|
|
2875
2900
|
}
|
|
@@ -2951,31 +2976,35 @@ async function findDb(homeDir) {
|
|
|
2951
2976
|
async function detectOpencode(homeDir) {
|
|
2952
2977
|
return await findDb(homeDir) !== null;
|
|
2953
2978
|
}
|
|
2979
|
+
function rowToEntry(row) {
|
|
2980
|
+
const ts = finitePositive(row.ts);
|
|
2981
|
+
if (!ts) return null;
|
|
2982
|
+
const input = finitePositive(row.input);
|
|
2983
|
+
const output = finitePositive(row.output);
|
|
2984
|
+
const cacheRead = finitePositive(row.cacheRead);
|
|
2985
|
+
const cacheCreate = finitePositive(row.cacheWrite);
|
|
2986
|
+
if (input + output + cacheRead + cacheCreate === 0) return null;
|
|
2987
|
+
return {
|
|
2988
|
+
ts,
|
|
2989
|
+
model: typeof row.model === "string" && row.model ? row.model : "unknown",
|
|
2990
|
+
cost: finitePositive(row.cost),
|
|
2991
|
+
input,
|
|
2992
|
+
output,
|
|
2993
|
+
cacheCreate,
|
|
2994
|
+
cacheRead,
|
|
2995
|
+
cacheSavings: 0
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2954
2998
|
async function loadEntries4(since, homeDir) {
|
|
2955
2999
|
const db = await findDb(homeDir);
|
|
2956
3000
|
if (!db) return [];
|
|
2957
|
-
const sql = "SELECT CASE WHEN time_created < 10000000000 THEN time_created * 1000 ELSE time_created END AS ts, json_extract(data,'$.modelID') AS model, json_extract(data,'$.cost') AS cost, json_extract(data,'$.tokens.input') AS input, json_extract(data,'$.tokens.output') AS output, json_extract(data,'$.tokens.
|
|
3001
|
+
const sql = "SELECT CASE WHEN time_created < 10000000000 THEN time_created * 1000 ELSE time_created END AS ts, json_extract(data,'$.modelID') AS model, json_extract(data,'$.cost') AS cost, json_extract(data,'$.tokens.input') AS input, json_extract(data,'$.tokens.output') AS output, json_extract(data,'$.tokens.cache.read') AS cacheRead, json_extract(data,'$.tokens.cache.write') AS cacheWrite FROM message WHERE json_valid(data) AND json_extract(data,'$.role')='assistant' AND json_type(data,'$.tokens')='object' AND (CASE WHEN time_created < 10000000000 THEN time_created * 1000 ELSE time_created END) >= ?;";
|
|
2958
3002
|
const res = await runSqlite(db, sql, [Math.floor(since)]);
|
|
2959
3003
|
if (res.status !== "ok") return [];
|
|
2960
3004
|
const entries = [];
|
|
2961
3005
|
for (const row of res.rows) {
|
|
2962
|
-
const
|
|
2963
|
-
if (
|
|
2964
|
-
const input = finitePositive(row.input);
|
|
2965
|
-
const output = finitePositive(row.output) + finitePositive(row.reasoning);
|
|
2966
|
-
const cacheRead = finitePositive(row.cacheRead);
|
|
2967
|
-
const cacheCreate = finitePositive(row.cacheWrite);
|
|
2968
|
-
if (input + output + cacheRead + cacheCreate === 0) continue;
|
|
2969
|
-
entries.push({
|
|
2970
|
-
ts,
|
|
2971
|
-
model: typeof row.model === "string" && row.model ? row.model : "unknown",
|
|
2972
|
-
cost: finitePositive(row.cost),
|
|
2973
|
-
input,
|
|
2974
|
-
output,
|
|
2975
|
-
cacheCreate,
|
|
2976
|
-
cacheRead,
|
|
2977
|
-
cacheSavings: 0
|
|
2978
|
-
});
|
|
3006
|
+
const entry = rowToEntry(row);
|
|
3007
|
+
if (entry) entries.push(entry);
|
|
2979
3008
|
}
|
|
2980
3009
|
return entries;
|
|
2981
3010
|
}
|
|
@@ -3442,11 +3471,16 @@ async function discoverGoogleOAuthClient() {
|
|
|
3442
3471
|
}
|
|
3443
3472
|
return null;
|
|
3444
3473
|
}
|
|
3474
|
+
var discoverClient = discoverGoogleOAuthClient;
|
|
3445
3475
|
async function resolveGoogleClient() {
|
|
3446
3476
|
const envId = process.env.TOKMON_GOOGLE_CLIENT_ID?.trim();
|
|
3447
3477
|
const envSecret = process.env.TOKMON_GOOGLE_CLIENT_SECRET?.trim();
|
|
3448
3478
|
if (envId && envSecret) return { clientId: envId, clientSecret: envSecret };
|
|
3449
|
-
if (cachedClient
|
|
3479
|
+
if (cachedClient == null) {
|
|
3480
|
+
const client = await discoverClient();
|
|
3481
|
+
if (client) cachedClient = client;
|
|
3482
|
+
return client;
|
|
3483
|
+
}
|
|
3450
3484
|
return cachedClient;
|
|
3451
3485
|
}
|
|
3452
3486
|
async function refreshAccessToken(refreshToken) {
|
|
@@ -3737,38 +3771,38 @@ import { createInterface as createInterface5 } from "readline";
|
|
|
3737
3771
|
import { join as join16 } from "path";
|
|
3738
3772
|
import { homedir as homedir12 } from "os";
|
|
3739
3773
|
var PRICING3 = {
|
|
3740
|
-
"gemini-3.1-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7 },
|
|
3741
|
-
"gemini-3.1-pro": { in: 2e-6, out: 12e-6, cr: 2e-7 },
|
|
3742
|
-
"gemini-3-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7 },
|
|
3743
|
-
"gemini-3-pro": { in: 2e-6, out: 12e-6, cr: 2e-7 },
|
|
3774
|
+
"gemini-3.1-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7, long: { in: 4e-6, out: 18e-6, cr: 4e-7 } },
|
|
3775
|
+
"gemini-3.1-pro": { in: 2e-6, out: 12e-6, cr: 2e-7, long: { in: 4e-6, out: 18e-6, cr: 4e-7 } },
|
|
3776
|
+
"gemini-3-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7, long: { in: 4e-6, out: 18e-6, cr: 4e-7 } },
|
|
3777
|
+
"gemini-3-pro": { in: 2e-6, out: 12e-6, cr: 2e-7, long: { in: 4e-6, out: 18e-6, cr: 4e-7 } },
|
|
3744
3778
|
"gemini-3.5-flash": { in: 75e-8, out: 45e-7, cr: 75e-9 },
|
|
3745
3779
|
"gemini-3-flash-preview": { in: 5e-7, out: 3e-6, cr: 5e-8 },
|
|
3746
3780
|
"gemini-3-flash": { in: 5e-7, out: 3e-6, cr: 5e-8 },
|
|
3747
3781
|
"gemini-2.5-flash-lite": { in: 1e-7, out: 4e-7, cr: 1e-8 },
|
|
3748
3782
|
"gemini-3.1-flash-lite": { in: 25e-8, out: 15e-7, cr: 25e-9 },
|
|
3749
3783
|
"gemini-2.5-flash": { in: 3e-7, out: 25e-7, cr: 3e-8 },
|
|
3750
|
-
"gemini-2.5-pro": { in: 125e-8, out: 1e-5, cr: 125e-9 },
|
|
3784
|
+
"gemini-2.5-pro": { in: 125e-8, out: 1e-5, cr: 125e-9, long: { in: 25e-7, out: 15e-6, cr: 25e-8 } },
|
|
3751
3785
|
"gemini-2.0-flash": { in: 1e-7, out: 4e-7, cr: 25e-9 }
|
|
3752
3786
|
};
|
|
3753
3787
|
var PRICE_KEYS3 = Object.keys(PRICING3).sort((a, b) => b.length - a.length);
|
|
3754
|
-
var
|
|
3755
|
-
var GEMINI_31_PRO_LONG_PRICE = { in: 4e-6, out: 18e-6, cr: 4e-7 };
|
|
3788
|
+
var FALLBACK_PRICE2 = PRICING3["gemini-3.1-pro"];
|
|
3756
3789
|
var MAX_SESSION_FILE_BYTES = 16 * 1024 * 1024;
|
|
3757
3790
|
var MAX_JSON_ENTRIES = 1e5;
|
|
3758
3791
|
function geminiTmpDir(homeDir) {
|
|
3759
3792
|
return join16(homeDir ?? homedir12(), ".gemini", "tmp");
|
|
3760
3793
|
}
|
|
3761
|
-
function
|
|
3762
|
-
const m = model.toLowerCase().trim();
|
|
3794
|
+
function resolvePricing(m) {
|
|
3763
3795
|
for (const key of PRICE_KEYS3) {
|
|
3764
3796
|
if (!m.startsWith(key)) continue;
|
|
3765
3797
|
const rest = m.slice(key.length);
|
|
3766
|
-
if (rest === "" || rest[0] === "-")
|
|
3767
|
-
if (key.startsWith("gemini-3.1-pro") && promptTokens > 2e5) return GEMINI_31_PRO_LONG_PRICE;
|
|
3768
|
-
return PRICING3[key];
|
|
3769
|
-
}
|
|
3798
|
+
if (rest === "" || rest[0] === "-") return PRICING3[key];
|
|
3770
3799
|
}
|
|
3771
|
-
return
|
|
3800
|
+
return FALLBACK_PRICE2;
|
|
3801
|
+
}
|
|
3802
|
+
function geminiPriceFor(model, promptTokens = 0) {
|
|
3803
|
+
const p = resolvePricing(model.toLowerCase().trim());
|
|
3804
|
+
if (promptTokens > 2e5 && p.long) return p.long;
|
|
3805
|
+
return { in: p.in, out: p.out, cr: p.cr };
|
|
3772
3806
|
}
|
|
3773
3807
|
function shortModel2(model) {
|
|
3774
3808
|
return model.replace(/(-preview|-customtools)+$/, "");
|
|
@@ -4242,7 +4276,7 @@ var PRICING4 = {
|
|
|
4242
4276
|
"grok-code-fast": { in: 1e-6, cr: 2e-7, out: 2e-6 },
|
|
4243
4277
|
"grok-build": { in: 1e-6, cr: 2e-7, out: 2e-6 }
|
|
4244
4278
|
};
|
|
4245
|
-
var
|
|
4279
|
+
var FALLBACK_PRICE3 = PRICING4["grok-4.5"];
|
|
4246
4280
|
var PRICE_KEYS4 = Object.keys(PRICING4).sort((a, b) => b.length - a.length);
|
|
4247
4281
|
var MAX_SESSION_GROUPS = 128;
|
|
4248
4282
|
var MAX_SESSIONS_PER_GROUP = 512;
|
|
@@ -4265,7 +4299,7 @@ function priceFor(model) {
|
|
|
4265
4299
|
for (const key of PRICE_KEYS4) {
|
|
4266
4300
|
if (modelKeyMatches2(m, key)) return PRICING4[key];
|
|
4267
4301
|
}
|
|
4268
|
-
return
|
|
4302
|
+
return FALLBACK_PRICE3;
|
|
4269
4303
|
}
|
|
4270
4304
|
async function detectGrok(homeDir) {
|
|
4271
4305
|
for (const home of grokHomes(homeDir)) {
|
|
@@ -4279,6 +4313,14 @@ async function detectGrok(homeDir) {
|
|
|
4279
4313
|
}
|
|
4280
4314
|
return false;
|
|
4281
4315
|
}
|
|
4316
|
+
async function byMtimeDesc(dir, names) {
|
|
4317
|
+
const stated = await Promise.all(names.map(async (name) => {
|
|
4318
|
+
const st = await fsStat6(join19(dir, name)).catch(() => null);
|
|
4319
|
+
return { name, mtimeMs: st ? st.mtimeMs : -Infinity };
|
|
4320
|
+
}));
|
|
4321
|
+
stated.sort((a, b) => b.mtimeMs - a.mtimeMs || a.name.localeCompare(b.name));
|
|
4322
|
+
return stated.map((s) => s.name);
|
|
4323
|
+
}
|
|
4282
4324
|
async function loadSessionModels(home) {
|
|
4283
4325
|
const out = /* @__PURE__ */ new Map();
|
|
4284
4326
|
const sessionsRoot = join19(home, "sessions");
|
|
@@ -4288,8 +4330,9 @@ async function loadSessionModels(home) {
|
|
|
4288
4330
|
} catch {
|
|
4289
4331
|
return out;
|
|
4290
4332
|
}
|
|
4291
|
-
|
|
4292
|
-
|
|
4333
|
+
const groupNames = groups.filter((g) => g !== "session_search.sqlite" && !g.startsWith("."));
|
|
4334
|
+
const orderedGroups = (await byMtimeDesc(sessionsRoot, groupNames)).slice(0, MAX_SESSION_GROUPS);
|
|
4335
|
+
for (const group of orderedGroups) {
|
|
4293
4336
|
const groupDir = join19(sessionsRoot, group);
|
|
4294
4337
|
let sessions;
|
|
4295
4338
|
try {
|
|
@@ -4297,7 +4340,9 @@ async function loadSessionModels(home) {
|
|
|
4297
4340
|
} catch {
|
|
4298
4341
|
continue;
|
|
4299
4342
|
}
|
|
4300
|
-
|
|
4343
|
+
const sessionNames = sessions.filter((s) => !s.startsWith("."));
|
|
4344
|
+
const orderedSessions = (await byMtimeDesc(groupDir, sessionNames)).slice(0, MAX_SESSIONS_PER_GROUP);
|
|
4345
|
+
for (const sid of orderedSessions) {
|
|
4301
4346
|
try {
|
|
4302
4347
|
const raw = JSON.parse(await readFile8(join19(groupDir, sid, "summary.json"), "utf-8"));
|
|
4303
4348
|
const model = typeof raw?.current_model_id === "string" && raw.current_model_id ? raw.current_model_id : null;
|
|
@@ -4332,21 +4377,37 @@ function costOf2(model, input, cacheRead, output) {
|
|
|
4332
4377
|
const cacheSavings = cacheRead * Math.max(0, p.in - p.cr);
|
|
4333
4378
|
return { cost, cacheSavings };
|
|
4334
4379
|
}
|
|
4380
|
+
var DEFAULT_MODEL = "grok-4.5";
|
|
4381
|
+
function modelChangeFrom(obj) {
|
|
4382
|
+
if (obj?.msg !== "model changed" && obj?.msg !== "backend_search: model switch") return null;
|
|
4383
|
+
const ctx = obj.ctx;
|
|
4384
|
+
if (!ctx || typeof ctx !== "object") return null;
|
|
4385
|
+
const c = ctx;
|
|
4386
|
+
const model = typeof c.model === "string" ? c.model : typeof c.new_model === "string" ? c.new_model : null;
|
|
4387
|
+
return model && model.trim() ? model : null;
|
|
4388
|
+
}
|
|
4335
4389
|
async function parseUnifiedLog(path, models) {
|
|
4336
4390
|
const entries = [];
|
|
4391
|
+
const activeModel = /* @__PURE__ */ new Map();
|
|
4337
4392
|
const input = createReadStream6(path);
|
|
4338
4393
|
input.on("error", () => {
|
|
4339
4394
|
});
|
|
4340
4395
|
const rl = createInterface6({ input, crlfDelay: Infinity });
|
|
4341
4396
|
try {
|
|
4342
4397
|
for await (const line of rl) {
|
|
4343
|
-
|
|
4398
|
+
const isTurn = line.includes("shell.turn.inference_done");
|
|
4399
|
+
if (!isTurn && !line.includes("model changed") && !line.includes("model switch")) continue;
|
|
4344
4400
|
try {
|
|
4345
4401
|
const obj = JSON.parse(line.charCodeAt(0) === 65279 ? line.slice(1) : line);
|
|
4402
|
+
const sid = typeof obj.sid === "string" ? obj.sid : "unknown";
|
|
4403
|
+
const switched = modelChangeFrom(obj);
|
|
4404
|
+
if (switched) {
|
|
4405
|
+
activeModel.set(sid, switched);
|
|
4406
|
+
continue;
|
|
4407
|
+
}
|
|
4346
4408
|
if (obj?.msg !== "shell.turn.inference_done" || !obj.ctx) continue;
|
|
4347
4409
|
const ts = Date.parse(String(obj.ts ?? ""));
|
|
4348
4410
|
if (!Number.isFinite(ts)) continue;
|
|
4349
|
-
const sid = typeof obj.sid === "string" ? obj.sid : "unknown";
|
|
4350
4411
|
const ctx = obj.ctx;
|
|
4351
4412
|
const prompt = safeNum(ctx.prompt_tokens);
|
|
4352
4413
|
const cached = safeNum(ctx.cached_prompt_tokens);
|
|
@@ -4355,7 +4416,7 @@ async function parseUnifiedLog(path, models) {
|
|
|
4355
4416
|
const cacheRead = cached;
|
|
4356
4417
|
const output = completion;
|
|
4357
4418
|
if (inputTokens + cacheRead + output <= 0) continue;
|
|
4358
|
-
const model = models.get(sid) ??
|
|
4419
|
+
const model = activeModel.get(sid) ?? models.get(sid) ?? DEFAULT_MODEL;
|
|
4359
4420
|
const { cost, cacheSavings } = costOf2(model, inputTokens, cacheRead, output);
|
|
4360
4421
|
const loop = safeNum(ctx.loop_index);
|
|
4361
4422
|
entries.push({
|
|
@@ -4398,7 +4459,7 @@ async function loadEntries6(since, homeDir) {
|
|
|
4398
4459
|
files,
|
|
4399
4460
|
async (path) => parseUnifiedLog(path, modelByHome.get(path) ?? /* @__PURE__ */ new Map()),
|
|
4400
4461
|
since,
|
|
4401
|
-
{ fingerprint: { parser: `grok-unified-
|
|
4462
|
+
{ fingerprint: { parser: `grok-unified-v3.${grokModelMapFingerprint(modelByHome)}` } }
|
|
4402
4463
|
);
|
|
4403
4464
|
}
|
|
4404
4465
|
async function grokDashboard(tz, homeDir) {
|
|
@@ -4579,6 +4640,7 @@ export {
|
|
|
4579
4640
|
readClaudeIdentity,
|
|
4580
4641
|
codexHomes,
|
|
4581
4642
|
identityFromIdToken,
|
|
4643
|
+
formatAgo,
|
|
4582
4644
|
formatResetAt,
|
|
4583
4645
|
currency,
|
|
4584
4646
|
tokens,
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
identityFromIdToken,
|
|
6
6
|
readClaudeIdentity,
|
|
7
7
|
readJson
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CVKCVHS7.js";
|
|
9
9
|
import {
|
|
10
10
|
expandHome,
|
|
11
11
|
slugify
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HP5UZCXP.js";
|
|
13
13
|
|
|
14
14
|
// src/accounts.ts
|
|
15
15
|
import { existsSync, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
@@ -26,6 +26,7 @@ var DEFAULTS = {
|
|
|
26
26
|
defaultFocus: "all",
|
|
27
27
|
ascii: "auto",
|
|
28
28
|
allowNetworkAccess: false,
|
|
29
|
+
allowedHosts: [],
|
|
29
30
|
resetDisplay: "relative",
|
|
30
31
|
knownProviders: []
|
|
31
32
|
};
|
|
@@ -147,6 +148,27 @@ function sameJson(a, b) {
|
|
|
147
148
|
return false;
|
|
148
149
|
}
|
|
149
150
|
}
|
|
151
|
+
function normalizeAllowedHost(value) {
|
|
152
|
+
if (typeof value !== "string") return null;
|
|
153
|
+
const candidate = value.trim();
|
|
154
|
+
if (!candidate) return null;
|
|
155
|
+
try {
|
|
156
|
+
const url = new URL(`http://${candidate}`);
|
|
157
|
+
if (url.username || url.password || url.port || url.pathname !== "/" || url.search || url.hash) return null;
|
|
158
|
+
const hostname = url.hostname.toLowerCase().replace(/\.$/, "");
|
|
159
|
+
if (!hostname || hostname.length > 253 || hostname.includes("*")) return null;
|
|
160
|
+
const labels = hostname.split(".");
|
|
161
|
+
if (labels.some((label) => !label || label.length > 63 || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(label))) return null;
|
|
162
|
+
return hostname;
|
|
163
|
+
} catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function normalizeAllowedHosts(value) {
|
|
168
|
+
if (!Array.isArray(value)) return [];
|
|
169
|
+
const hosts = value.map(normalizeAllowedHost).filter((host) => host !== null);
|
|
170
|
+
return [...new Set(hosts)];
|
|
171
|
+
}
|
|
150
172
|
function repairConfig(input) {
|
|
151
173
|
const reasons = [];
|
|
152
174
|
const parsed = isRecord(input) ? input : {};
|
|
@@ -192,6 +214,10 @@ function repairConfig(input) {
|
|
|
192
214
|
if (parsed.knownProviders !== void 0 && !Array.isArray(parsed.knownProviders)) {
|
|
193
215
|
reasons.push("knownProviders was not an array");
|
|
194
216
|
}
|
|
217
|
+
const allowedHosts = normalizeAllowedHosts(parsed.allowedHosts);
|
|
218
|
+
if (parsed.allowedHosts !== void 0 && !sameJson(parsed.allowedHosts, allowedHosts)) {
|
|
219
|
+
reasons.push("allowedHosts contained invalid or duplicate hostnames");
|
|
220
|
+
}
|
|
195
221
|
const activeAccountId = typeof parsed.activeAccountId === "string" && (accountIds.has(parsed.activeAccountId) || PROVIDER_IDS.includes(parsed.activeAccountId)) ? parsed.activeAccountId : null;
|
|
196
222
|
if (parsed.activeAccountId !== void 0 && parsed.activeAccountId !== null && activeAccountId === null) {
|
|
197
223
|
reasons.push("activeAccountId did not match a known account/provider");
|
|
@@ -217,6 +243,7 @@ function repairConfig(input) {
|
|
|
217
243
|
defaultFocus: parsed.defaultFocus === "last" ? "last" : "all",
|
|
218
244
|
ascii: parsed.ascii === "on" ? "on" : parsed.ascii === "off" ? "off" : "auto",
|
|
219
245
|
allowNetworkAccess: parsed.allowNetworkAccess === true,
|
|
246
|
+
allowedHosts,
|
|
220
247
|
resetDisplay: parsed.resetDisplay === "absolute" ? "absolute" : "relative",
|
|
221
248
|
knownProviders
|
|
222
249
|
};
|
|
@@ -389,6 +416,8 @@ export {
|
|
|
389
416
|
getTrackedAccountRows,
|
|
390
417
|
clampNum,
|
|
391
418
|
isValidTimezone,
|
|
419
|
+
normalizeAllowedHost,
|
|
420
|
+
normalizeAllowedHosts,
|
|
392
421
|
repairConfig,
|
|
393
422
|
normalizeConfig,
|
|
394
423
|
slugify,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
colorHex,
|
|
12
12
|
fetchPeak,
|
|
13
13
|
namedHex
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FMP3P2WV.js";
|
|
15
15
|
import {
|
|
16
16
|
PROVIDERS,
|
|
17
17
|
TOKMON_CAPABILITIES,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
detectProviders,
|
|
23
23
|
resolveTimezone,
|
|
24
24
|
withTimeout
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-CVKCVHS7.js";
|
|
26
26
|
import {
|
|
27
27
|
cacheDir,
|
|
28
28
|
expandHome,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
normalizeConfig,
|
|
31
31
|
saveConfig,
|
|
32
32
|
snapshotCacheFile
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-HP5UZCXP.js";
|
|
34
34
|
|
|
35
35
|
// src/web/server.ts
|
|
36
36
|
import { createServer } from "http";
|
|
@@ -783,11 +783,11 @@ function isLoopbackHostHeader(value) {
|
|
|
783
783
|
const hostname = hostnameFromHost(value);
|
|
784
784
|
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
785
785
|
}
|
|
786
|
-
function isAllowedHostHeader(value, allowNetworkAccess) {
|
|
786
|
+
function isAllowedHostHeader(value, allowNetworkAccess, allowedHosts = []) {
|
|
787
787
|
if (isLoopbackHostHeader(value)) return true;
|
|
788
788
|
if (!allowNetworkAccess) return false;
|
|
789
789
|
const hostname = hostnameFromHost(value);
|
|
790
|
-
return hostname !== null && isIP(hostname) !== 0;
|
|
790
|
+
return hostname !== null && (isIP(hostname) !== 0 || allowedHosts.includes(hostname));
|
|
791
791
|
}
|
|
792
792
|
function isSameOriginRequest(req) {
|
|
793
793
|
const origin = header(req, "origin");
|
|
@@ -801,8 +801,8 @@ function isSameOriginRequest(req) {
|
|
|
801
801
|
return false;
|
|
802
802
|
}
|
|
803
803
|
}
|
|
804
|
-
function isAllowedLocalRequest(req, allowNetworkAccess) {
|
|
805
|
-
return isAllowedHostHeader(header(req, "host"), allowNetworkAccess) && isSameOriginRequest(req);
|
|
804
|
+
function isAllowedLocalRequest(req, allowNetworkAccess, allowedHosts = []) {
|
|
805
|
+
return isAllowedHostHeader(header(req, "host"), allowNetworkAccess, allowedHosts) && isSameOriginRequest(req);
|
|
806
806
|
}
|
|
807
807
|
|
|
808
808
|
// src/web/ws.ts
|
|
@@ -890,7 +890,7 @@ async function mountWsRpc(server, deps) {
|
|
|
890
890
|
)
|
|
891
891
|
);
|
|
892
892
|
const onUpgrade = (req, socket, head) => {
|
|
893
|
-
if (!isAllowedLocalRequest(req, deps.state.config.allowNetworkAccess)) {
|
|
893
|
+
if (!isAllowedLocalRequest(req, deps.state.config.allowNetworkAccess, deps.state.config.allowedHosts)) {
|
|
894
894
|
rejectUpgrade(socket);
|
|
895
895
|
return;
|
|
896
896
|
}
|
|
@@ -945,8 +945,8 @@ function tokenMatches(given, expected) {
|
|
|
945
945
|
const token = Buffer.from(expected);
|
|
946
946
|
return actual.length === token.length && timingSafeEqual(actual, token);
|
|
947
947
|
}
|
|
948
|
-
function guardHost(req, res,
|
|
949
|
-
if (isAllowedHostHeader(header2(req, "host"), allowNetworkAccess)) return true;
|
|
948
|
+
function guardHost(req, res, config) {
|
|
949
|
+
if (isAllowedHostHeader(header2(req, "host"), config.allowNetworkAccess, config.allowedHosts)) return true;
|
|
950
950
|
sendJson(res, 403, { error: "forbidden" });
|
|
951
951
|
return false;
|
|
952
952
|
}
|
|
@@ -962,7 +962,7 @@ function createRouter(engine, state, vite, webRoot, wsToken, version) {
|
|
|
962
962
|
const url = req.url || "/";
|
|
963
963
|
const path = url.split("?")[0];
|
|
964
964
|
const method = req.method || "GET";
|
|
965
|
-
if (!guardHost(req, res, state.config
|
|
965
|
+
if (!guardHost(req, res, state.config)) return;
|
|
966
966
|
if (path === "/api/data") {
|
|
967
967
|
engine.touch();
|
|
968
968
|
sendJson(res, 200, engine.snapshot() ?? { pending: true });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
cacheDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HP5UZCXP.js";
|
|
5
5
|
|
|
6
6
|
// src/web/lockfile.ts
|
|
7
7
|
import { closeSync, fchmodSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
attachOrSpawn
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-4HU4EI5T.js";
|
|
5
|
+
import "./chunk-XDA5RJST.js";
|
|
6
6
|
import "./chunk-SMPY52EV.js";
|
|
7
7
|
import {
|
|
8
8
|
createDaemonRpcClient
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3RTWFGGD.js";
|
|
10
10
|
import {
|
|
11
11
|
PROVIDERS,
|
|
12
12
|
antigravityStateDb,
|
|
@@ -26,11 +26,11 @@ import {
|
|
|
26
26
|
startOfMonth,
|
|
27
27
|
startOfWeek,
|
|
28
28
|
withTimeout
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-CVKCVHS7.js";
|
|
30
30
|
import {
|
|
31
31
|
PROVIDER_IDS,
|
|
32
32
|
configLocation
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-HP5UZCXP.js";
|
|
34
34
|
|
|
35
35
|
// src/provider-locations.ts
|
|
36
36
|
import { access } from "fs/promises";
|