tokmon 0.28.2 → 0.28.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 +156 -234
- package/THIRD_PARTY_NOTICES.md +62 -0
- package/dist/{bootstrap-ink-IMT3S5HU.js → bootstrap-ink-WG4WG3R2.js} +1579 -1136
- package/dist/chunk-5B3XCEMC.js +294 -0
- package/dist/chunk-7CH6S7BR.js +447 -0
- package/dist/{chunk-LCAHTRGP.js → chunk-CFKUZCMB.js} +56 -29
- package/dist/{chunk-LKYOWBD4.js → chunk-FLANGRI6.js} +21 -42
- package/dist/chunk-HNWEJ6MS.js +1094 -0
- package/dist/{chunk-WUK3MQUB.js → chunk-JZSZEHVD.js} +167 -10
- package/dist/{chunk-6ZEW2M6P.js → chunk-P57DVFNH.js} +125 -25
- package/dist/chunk-TN6V7XSL.js +512 -0
- package/dist/chunk-UYPDMVW5.js +21 -0
- package/dist/{cli-command-DCENRCO3.js → cli-command-I4GRR3MF.js} +249 -15
- package/dist/cli.js +6 -6
- package/dist/{config-HXFJTNLM.js → config-3DGZ47F7.js} +23 -3
- package/dist/daemon-ZXV3V5CS.js +384 -0
- package/dist/daemon-handle-G4OGPFR3.js +11 -0
- package/dist/server-EPSH52DR.js +12 -0
- package/dist/web/assets/{Area-CMXvOw33.js → Area-stRnp8Km.js} +1 -1
- package/dist/web/assets/analytics-moZZt9L_.js +2 -0
- package/dist/web/assets/breakdown-km9w7myU.js +4 -0
- package/dist/web/assets/button-DrX04Cc2.js +1 -0
- package/dist/web/assets/{chart-cZoLgpmG.js → chart-D0CtuL2k.js} +1 -1
- package/dist/web/assets/explore-B11uNJV1.js +22 -0
- package/dist/web/assets/index-RDhXsCNG.js +83 -0
- package/dist/web/assets/index-ZmOS8p9O.css +1 -0
- package/dist/web/assets/models-DYbZnY34.js +2 -0
- package/dist/web/assets/overview-CruICbfJ.js +2 -0
- package/dist/web/assets/panel-xckM3_GC.js +1 -0
- package/dist/web/assets/settings-sheet-G-BTSIF6.js +1 -0
- package/dist/web/assets/share-sheet-weIdd4DW.js +2 -0
- package/dist/web/assets/timeline-BHgniliZ.js +1 -0
- package/dist/web/index.html +30 -4
- package/package.json +13 -4
- package/site/THIRD_PARTY_NOTICES.md +83 -0
- package/site/public/fonts/OFL.txt +93 -0
- package/dist/chunk-44ZSQEOS.js +0 -299
- package/dist/chunk-HP5UZCXP.js +0 -436
- package/dist/chunk-J3JW3RFP.js +0 -196
- package/dist/chunk-M7XMV36F.js +0 -275
- package/dist/daemon-6Y4O7NXS.js +0 -241
- package/dist/daemon-handle-GVCYVDPO.js +0 -10
- package/dist/server-GXXOWUVZ.js +0 -11
- package/dist/web/assets/analytics-DqdZXOJL.js +0 -2
- package/dist/web/assets/breakdown-kOSSJ3mI.js +0 -4
- package/dist/web/assets/explore-vcsM8K9I.js +0 -22
- package/dist/web/assets/index-BErCXT7f.css +0 -1
- package/dist/web/assets/index-DH3dOnbg.js +0 -83
- package/dist/web/assets/models-B5ikm83_.js +0 -2
- package/dist/web/assets/overview-DD1kP8CP.js +0 -2
- package/dist/web/assets/panel-DTQBwExW.js +0 -1
- package/dist/web/assets/primitives-C36qJlA2.js +0 -1
- package/dist/web/assets/settings-sheet-_i2zTQjf.js +0 -1
- package/dist/web/assets/share-sheet-BLHmrFbm.js +0 -2
- package/dist/web/assets/timeline-B4uG-Emo.js +0 -1
- package/dist/web/assets/use-dialog-trap-bfaXEMz9.js +0 -1
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ConfigUpdateConflictFailure,
|
|
4
|
+
TOKMON_WS_METHODS,
|
|
5
|
+
TOKMON_WS_PATH,
|
|
6
|
+
TokmonRpcGroup
|
|
7
|
+
} from "./chunk-JZSZEHVD.js";
|
|
8
|
+
import {
|
|
9
|
+
normalizeConfig
|
|
10
|
+
} from "./chunk-HNWEJ6MS.js";
|
|
11
|
+
|
|
12
|
+
// src/client/daemon-rpc-client.ts
|
|
13
|
+
import { Cause, Context, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, Schedule, Schema, Stream } from "effect";
|
|
14
|
+
import { RpcClient, RpcSerialization } from "effect/unstable/rpc";
|
|
15
|
+
import { RpcClientError } from "effect/unstable/rpc/RpcClientError";
|
|
16
|
+
import * as Socket from "effect/unstable/socket/Socket";
|
|
17
|
+
var DaemonRpcConnectionError = class extends Error {
|
|
18
|
+
constructor(message = "daemon RPC connection closed") {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "DaemonRpcConnectionError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var DaemonRpcRequestTimeoutError = class extends Error {
|
|
24
|
+
constructor(method, timeoutMs) {
|
|
25
|
+
super(`${method} timed out after ${timeoutMs}ms`);
|
|
26
|
+
this.method = method;
|
|
27
|
+
this.timeoutMs = timeoutMs;
|
|
28
|
+
this.name = "DaemonRpcRequestTimeoutError";
|
|
29
|
+
}
|
|
30
|
+
method;
|
|
31
|
+
timeoutMs;
|
|
32
|
+
};
|
|
33
|
+
var TokmonRpcClient = class extends Context.Service()(
|
|
34
|
+
"tokmon/client/DaemonRpcClient/TokmonRpcClient"
|
|
35
|
+
) {
|
|
36
|
+
};
|
|
37
|
+
var dynamicImport = new Function("specifier", "return import(specifier)");
|
|
38
|
+
function makeSignal() {
|
|
39
|
+
let settled = false;
|
|
40
|
+
let resolvePromise;
|
|
41
|
+
const promise = new Promise((resolve) => {
|
|
42
|
+
resolvePromise = resolve;
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
promise,
|
|
46
|
+
resolve: (value) => {
|
|
47
|
+
if (settled) return;
|
|
48
|
+
settled = true;
|
|
49
|
+
resolvePromise(value);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function toWsUrl(baseUrl) {
|
|
54
|
+
const base = typeof window === "undefined" ? void 0 : window.location.origin;
|
|
55
|
+
const url = new URL(baseUrl, base);
|
|
56
|
+
if (url.protocol === "http:") url.protocol = "ws:";
|
|
57
|
+
else if (url.protocol === "https:") url.protocol = "wss:";
|
|
58
|
+
if (url.protocol !== "ws:" && url.protocol !== "wss:") {
|
|
59
|
+
throw new Error(`unsupported daemon RPC protocol: ${url.protocol}`);
|
|
60
|
+
}
|
|
61
|
+
url.hash = "";
|
|
62
|
+
url.searchParams.delete("tokmonToken");
|
|
63
|
+
url.searchParams.delete("wsToken");
|
|
64
|
+
url.pathname = TOKMON_WS_PATH;
|
|
65
|
+
return url.toString();
|
|
66
|
+
}
|
|
67
|
+
function shouldUseNodeTransport(transport) {
|
|
68
|
+
if (transport === "node") return true;
|
|
69
|
+
if (transport === "browser") return false;
|
|
70
|
+
return typeof window === "undefined";
|
|
71
|
+
}
|
|
72
|
+
async function socketLayerFor(url, transport, openTimeoutMs) {
|
|
73
|
+
const socketOptions = { openTimeout: Duration.millis(openTimeoutMs) };
|
|
74
|
+
if (shouldUseNodeTransport(transport)) {
|
|
75
|
+
const NodeSocket = await dynamicImport("@effect/platform-node/NodeSocket");
|
|
76
|
+
return NodeSocket.layerWebSocket(url, socketOptions);
|
|
77
|
+
}
|
|
78
|
+
return Socket.layerWebSocket(url, socketOptions).pipe(
|
|
79
|
+
Layer.provide(Socket.layerWebSocketConstructorGlobal)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
function normalizeConfigState(state) {
|
|
83
|
+
return {
|
|
84
|
+
protocol: {
|
|
85
|
+
version: state.protocol.version,
|
|
86
|
+
capabilities: [...state.protocol.capabilities]
|
|
87
|
+
},
|
|
88
|
+
config: normalizeConfig(state.config)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function normalizeSnapshot(snapshot) {
|
|
92
|
+
const copyHeadroom = (headroom) => headroom ? {
|
|
93
|
+
...headroom,
|
|
94
|
+
activeAccountIds: [...headroom.activeAccountIds],
|
|
95
|
+
factors: headroom.factors.map((factor) => ({ ...factor }))
|
|
96
|
+
} : void 0;
|
|
97
|
+
const copyRows = (rows) => rows.map((row) => ({
|
|
98
|
+
...row,
|
|
99
|
+
models: [...row.models],
|
|
100
|
+
breakdown: row.breakdown.map((detail) => ({ ...detail }))
|
|
101
|
+
}));
|
|
102
|
+
return {
|
|
103
|
+
...snapshot,
|
|
104
|
+
providers: snapshot.providers.map((provider) => ({
|
|
105
|
+
...provider,
|
|
106
|
+
headroom: copyHeadroom(provider.headroom)
|
|
107
|
+
})),
|
|
108
|
+
accounts: snapshot.accounts.map((account) => ({
|
|
109
|
+
...account,
|
|
110
|
+
identity: account.identity ? { ...account.identity } : void 0,
|
|
111
|
+
quotas: account.quotas?.map((quota) => ({ ...quota })),
|
|
112
|
+
headroom: copyHeadroom(account.headroom),
|
|
113
|
+
dashboard: account.dashboard ? {
|
|
114
|
+
...account.dashboard,
|
|
115
|
+
today: { ...account.dashboard.today },
|
|
116
|
+
week: { ...account.dashboard.week },
|
|
117
|
+
month: { ...account.dashboard.month },
|
|
118
|
+
series: [...account.dashboard.series]
|
|
119
|
+
} : null,
|
|
120
|
+
table: account.table ? {
|
|
121
|
+
daily: copyRows(account.table.daily),
|
|
122
|
+
weekly: copyRows(account.table.weekly),
|
|
123
|
+
monthly: copyRows(account.table.monthly)
|
|
124
|
+
} : null,
|
|
125
|
+
billing: account.billing ? {
|
|
126
|
+
...account.billing,
|
|
127
|
+
metrics: account.billing.metrics.map((metric) => ({
|
|
128
|
+
...metric,
|
|
129
|
+
format: { ...metric.format }
|
|
130
|
+
})),
|
|
131
|
+
activity: account.billing.activity ? { ...account.billing.activity, series: [...account.billing.activity.series] } : account.billing.activity,
|
|
132
|
+
modelSpend: account.billing.modelSpend == null ? account.billing.modelSpend : account.billing.modelSpend.map((spend) => ({ ...spend }))
|
|
133
|
+
} : null,
|
|
134
|
+
summaryUpdatedAt: account.summaryUpdatedAt ?? null,
|
|
135
|
+
billingUpdatedAt: account.billingUpdatedAt ?? null,
|
|
136
|
+
tableUpdatedAt: account.tableUpdatedAt ?? null
|
|
137
|
+
})),
|
|
138
|
+
peak: snapshot.peak ? { ...snapshot.peak } : null
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function normalizeRequestFailure(error) {
|
|
142
|
+
if (!error || typeof error !== "object" || error.kind !== "conflict") return error;
|
|
143
|
+
const state = error.state;
|
|
144
|
+
if (!state) return error;
|
|
145
|
+
const normalized = normalizeConfigState(state);
|
|
146
|
+
if (error instanceof ConfigUpdateConflictFailure) {
|
|
147
|
+
return new ConfigUpdateConflictFailure({ kind: "conflict", state: normalized });
|
|
148
|
+
}
|
|
149
|
+
return { ...error, state: normalized };
|
|
150
|
+
}
|
|
151
|
+
function isSessionFailure(error) {
|
|
152
|
+
return error instanceof RpcClientError || Schema.isSchemaError(error);
|
|
153
|
+
}
|
|
154
|
+
async function waitForReady(ready, disconnected, timeoutMs) {
|
|
155
|
+
let timer = null;
|
|
156
|
+
try {
|
|
157
|
+
await Promise.race([
|
|
158
|
+
ready.promise,
|
|
159
|
+
disconnected.promise.then((error) => Promise.reject(error)),
|
|
160
|
+
new Promise((_resolve, reject) => {
|
|
161
|
+
timer = setTimeout(() => reject(new DaemonRpcConnectionError(`daemon RPC connection timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
162
|
+
timer.unref?.();
|
|
163
|
+
})
|
|
164
|
+
]);
|
|
165
|
+
} finally {
|
|
166
|
+
if (timer) clearTimeout(timer);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function createDaemonRpcClient(baseUrl, options = {}) {
|
|
170
|
+
const url = toWsUrl(baseUrl);
|
|
171
|
+
const requestTimeoutMs = Math.max(1, options.requestTimeoutMs ?? 3e4);
|
|
172
|
+
const reconnectBaseDelayMs = Math.max(1, options.reconnectBaseDelayMs ?? 250);
|
|
173
|
+
const subscriptions = /* @__PURE__ */ new Set();
|
|
174
|
+
let session = null;
|
|
175
|
+
let sessionPromise = null;
|
|
176
|
+
let pendingRuntime = null;
|
|
177
|
+
let supervisorPromise = null;
|
|
178
|
+
let reconnectTimer = null;
|
|
179
|
+
let reconnectAttemptsUsed = 0;
|
|
180
|
+
let hasConnected = false;
|
|
181
|
+
let closed = false;
|
|
182
|
+
const reportSubscriberError = (error) => {
|
|
183
|
+
if (options.onSubscriberError) {
|
|
184
|
+
try {
|
|
185
|
+
options.onSubscriberError(error);
|
|
186
|
+
} catch (reportError) {
|
|
187
|
+
console.error("[tokmon] daemon RPC subscriber error reporter failed", reportError);
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
console.error("[tokmon] daemon RPC subscriber failed", error);
|
|
192
|
+
};
|
|
193
|
+
const setConn = (state, error) => {
|
|
194
|
+
try {
|
|
195
|
+
options.onConn?.(state, error);
|
|
196
|
+
} catch (callbackError) {
|
|
197
|
+
reportSubscriberError(callbackError);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
const clearReconnectTimer = () => {
|
|
201
|
+
if (!reconnectTimer) return;
|
|
202
|
+
clearTimeout(reconnectTimer);
|
|
203
|
+
reconnectTimer = null;
|
|
204
|
+
};
|
|
205
|
+
const stopSubscriptionFiber = (subscription) => {
|
|
206
|
+
const fiber = subscription.fiber;
|
|
207
|
+
subscription.fiber = null;
|
|
208
|
+
subscription.owner = null;
|
|
209
|
+
return fiber ? Effect.runPromise(Fiber.interrupt(fiber)).catch(() => {
|
|
210
|
+
}) : Promise.resolve();
|
|
211
|
+
};
|
|
212
|
+
const disposeSubscription = (subscription) => {
|
|
213
|
+
subscription.active = false;
|
|
214
|
+
subscriptions.delete(subscription);
|
|
215
|
+
if (subscription.watchdogTimer) {
|
|
216
|
+
clearInterval(subscription.watchdogTimer);
|
|
217
|
+
subscription.watchdogTimer = null;
|
|
218
|
+
}
|
|
219
|
+
return stopSubscriptionFiber(subscription);
|
|
220
|
+
};
|
|
221
|
+
const scheduleReconnect = () => {
|
|
222
|
+
if (closed || subscriptions.size === 0 || reconnectTimer || session || sessionPromise) return;
|
|
223
|
+
if (typeof options.reconnectAttempts === "number" && reconnectAttemptsUsed >= options.reconnectAttempts) return;
|
|
224
|
+
const delayMs = Math.min(2500, reconnectBaseDelayMs * 1.5 ** reconnectAttemptsUsed);
|
|
225
|
+
reconnectTimer = setTimeout(() => {
|
|
226
|
+
reconnectTimer = null;
|
|
227
|
+
reconnectAttemptsUsed++;
|
|
228
|
+
connectSubscriptions();
|
|
229
|
+
}, delayMs);
|
|
230
|
+
reconnectTimer.unref?.();
|
|
231
|
+
};
|
|
232
|
+
const invalidateSession = (active, state, error) => {
|
|
233
|
+
if (active.invalidated) return;
|
|
234
|
+
active.invalidated = true;
|
|
235
|
+
if (session === active) session = null;
|
|
236
|
+
void active.runtime.dispose().catch(() => {
|
|
237
|
+
});
|
|
238
|
+
if (!closed) setConn(state, error);
|
|
239
|
+
scheduleReconnect();
|
|
240
|
+
};
|
|
241
|
+
const makeProtocolLayer = async (ready, disconnected) => {
|
|
242
|
+
const socketLayer = await socketLayerFor(url, options.transport, requestTimeoutMs);
|
|
243
|
+
const connectionHooksLayer = Layer.succeed(
|
|
244
|
+
RpcClient.ConnectionHooks,
|
|
245
|
+
RpcClient.ConnectionHooks.of({
|
|
246
|
+
onConnect: Effect.sync(() => {
|
|
247
|
+
ready.resolve();
|
|
248
|
+
}),
|
|
249
|
+
onDisconnect: Effect.sync(() => {
|
|
250
|
+
disconnected.resolve(new DaemonRpcConnectionError());
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
);
|
|
254
|
+
return Layer.effect(
|
|
255
|
+
RpcClient.Protocol,
|
|
256
|
+
RpcClient.makeProtocolSocket({
|
|
257
|
+
retryPolicy: Schedule.recurs(0),
|
|
258
|
+
retryTransientErrors: false
|
|
259
|
+
})
|
|
260
|
+
).pipe(
|
|
261
|
+
Layer.provide(Layer.mergeAll(socketLayer, RpcSerialization.layerJson, connectionHooksLayer))
|
|
262
|
+
);
|
|
263
|
+
};
|
|
264
|
+
const ensureSession = async () => {
|
|
265
|
+
if (closed) throw new Error("daemon RPC client is closed");
|
|
266
|
+
if (session) return session;
|
|
267
|
+
if (sessionPromise) return sessionPromise;
|
|
268
|
+
setConn(hasConnected ? "reconnecting" : "connecting");
|
|
269
|
+
const pending = (async () => {
|
|
270
|
+
let runtime = null;
|
|
271
|
+
try {
|
|
272
|
+
const ready = makeSignal();
|
|
273
|
+
const disconnected = makeSignal();
|
|
274
|
+
const protocolLayer = await makeProtocolLayer(ready, disconnected);
|
|
275
|
+
const clientLayer = Layer.effect(
|
|
276
|
+
TokmonRpcClient,
|
|
277
|
+
RpcClient.make(TokmonRpcGroup)
|
|
278
|
+
).pipe(Layer.provide(protocolLayer));
|
|
279
|
+
runtime = ManagedRuntime.make(clientLayer);
|
|
280
|
+
pendingRuntime = runtime;
|
|
281
|
+
await runtime.runPromise(TokmonRpcClient.asEffect());
|
|
282
|
+
await waitForReady(ready, disconnected, requestTimeoutMs);
|
|
283
|
+
if (closed) throw new Error("daemon RPC client is closed");
|
|
284
|
+
const active = {
|
|
285
|
+
runtime,
|
|
286
|
+
disconnected: disconnected.promise,
|
|
287
|
+
invalidated: false
|
|
288
|
+
};
|
|
289
|
+
session = active;
|
|
290
|
+
pendingRuntime = null;
|
|
291
|
+
hasConnected = true;
|
|
292
|
+
reconnectAttemptsUsed = 0;
|
|
293
|
+
setConn("live");
|
|
294
|
+
void active.disconnected.then((error) => {
|
|
295
|
+
invalidateSession(active, "reconnecting", error);
|
|
296
|
+
});
|
|
297
|
+
startAllSubscriptions(active);
|
|
298
|
+
return active;
|
|
299
|
+
} catch (error) {
|
|
300
|
+
if (pendingRuntime === runtime) pendingRuntime = null;
|
|
301
|
+
await runtime?.dispose().catch(() => {
|
|
302
|
+
});
|
|
303
|
+
if (!closed) setConn("error", error);
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
})();
|
|
307
|
+
sessionPromise = pending;
|
|
308
|
+
try {
|
|
309
|
+
return await pending;
|
|
310
|
+
} finally {
|
|
311
|
+
if (sessionPromise === pending) sessionPromise = null;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const run = async (method, effectFor) => {
|
|
315
|
+
const active = await ensureSession();
|
|
316
|
+
try {
|
|
317
|
+
return await active.runtime.runPromise(
|
|
318
|
+
TokmonRpcClient.use((client) => effectFor(client)).pipe(
|
|
319
|
+
Effect.timeoutOrElse({
|
|
320
|
+
duration: Duration.millis(requestTimeoutMs),
|
|
321
|
+
orElse: () => Effect.fail(new DaemonRpcRequestTimeoutError(method, requestTimeoutMs))
|
|
322
|
+
})
|
|
323
|
+
)
|
|
324
|
+
);
|
|
325
|
+
} catch (rawError) {
|
|
326
|
+
const error = normalizeRequestFailure(rawError);
|
|
327
|
+
if (!closed && isSessionFailure(error)) invalidateSession(active, "error", error);
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
const startSubscription = (subscription, active) => {
|
|
332
|
+
if (closed || !subscription.active || subscription.fiber || active.invalidated) return;
|
|
333
|
+
subscription.lastValueAt = Date.now();
|
|
334
|
+
subscription.staleAfterMs = options.snapshotStaleFloorMs ?? 9e4;
|
|
335
|
+
subscription.owner = active;
|
|
336
|
+
const fiber = active.runtime.runFork(
|
|
337
|
+
TokmonRpcClient.use(
|
|
338
|
+
(client) => subscription.streamFor(client).pipe(
|
|
339
|
+
Stream.runForEach(
|
|
340
|
+
(value) => Effect.sync(() => {
|
|
341
|
+
subscription.lastValueAt = Date.now();
|
|
342
|
+
if (subscription.staleAfterFor) subscription.staleAfterMs = subscription.staleAfterFor(value);
|
|
343
|
+
try {
|
|
344
|
+
subscription.onValue(value);
|
|
345
|
+
} catch (error) {
|
|
346
|
+
reportSubscriberError(error);
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
);
|
|
353
|
+
subscription.fiber = fiber;
|
|
354
|
+
fiber.addObserver((exit) => {
|
|
355
|
+
if (subscription.fiber === fiber) subscription.fiber = null;
|
|
356
|
+
if (subscription.owner === active) subscription.owner = null;
|
|
357
|
+
if (closed || !subscription.active || active.invalidated) return;
|
|
358
|
+
if (Exit.isSuccess(exit)) {
|
|
359
|
+
invalidateSession(active, "reconnecting");
|
|
360
|
+
} else {
|
|
361
|
+
invalidateSession(active, "error", Cause.squash(exit.cause));
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
};
|
|
365
|
+
const startAllSubscriptions = (active) => {
|
|
366
|
+
for (const subscription of subscriptions) startSubscription(subscription, active);
|
|
367
|
+
};
|
|
368
|
+
function connectSubscriptions() {
|
|
369
|
+
if (closed || subscriptions.size === 0 || supervisorPromise) return;
|
|
370
|
+
supervisorPromise = ensureSession().then(() => void 0).catch(() => {
|
|
371
|
+
scheduleReconnect();
|
|
372
|
+
}).finally(() => {
|
|
373
|
+
supervisorPromise = null;
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const subscribe = (streamFor, onValue, staleAfterFor) => {
|
|
377
|
+
if (closed) return () => {
|
|
378
|
+
};
|
|
379
|
+
const subscription = {
|
|
380
|
+
streamFor,
|
|
381
|
+
onValue,
|
|
382
|
+
staleAfterFor,
|
|
383
|
+
active: true,
|
|
384
|
+
fiber: null,
|
|
385
|
+
owner: null,
|
|
386
|
+
watchdogTimer: null,
|
|
387
|
+
lastValueAt: 0,
|
|
388
|
+
staleAfterMs: Number.POSITIVE_INFINITY
|
|
389
|
+
};
|
|
390
|
+
subscriptions.add(subscription);
|
|
391
|
+
if (staleAfterFor) {
|
|
392
|
+
const checkEveryMs = Math.min(5e3, Math.max(10, (options.snapshotStaleFloorMs ?? 9e4) / 2));
|
|
393
|
+
subscription.watchdogTimer = setInterval(() => {
|
|
394
|
+
if (!subscription.fiber || !subscription.owner || subscription.lastValueAt === 0) return;
|
|
395
|
+
if (Date.now() - subscription.lastValueAt <= subscription.staleAfterMs) return;
|
|
396
|
+
subscription.lastValueAt = Date.now();
|
|
397
|
+
invalidateSession(subscription.owner, "reconnecting");
|
|
398
|
+
}, checkEveryMs);
|
|
399
|
+
subscription.watchdogTimer.unref?.();
|
|
400
|
+
}
|
|
401
|
+
if (session) startSubscription(subscription, session);
|
|
402
|
+
else connectSubscriptions();
|
|
403
|
+
return () => {
|
|
404
|
+
void disposeSubscription(subscription);
|
|
405
|
+
if (subscriptions.size === 0) clearReconnectTimer();
|
|
406
|
+
};
|
|
407
|
+
};
|
|
408
|
+
return {
|
|
409
|
+
getConfig: () => run(TOKMON_WS_METHODS.getConfig, (client) => client[TOKMON_WS_METHODS.getConfig]({})).then(normalizeConfigState),
|
|
410
|
+
setConfig: (update) => run(TOKMON_WS_METHODS.setConfig, (client) => client[TOKMON_WS_METHODS.setConfig](update)).then(normalizeConfigState),
|
|
411
|
+
refresh: (scope = "all") => run(TOKMON_WS_METHODS.refresh, (client) => client[TOKMON_WS_METHODS.refresh]({ scope })),
|
|
412
|
+
browseFs: (path) => run(TOKMON_WS_METHODS.browseFs, (client) => client[TOKMON_WS_METHODS.browseFs]({ path })),
|
|
413
|
+
subscribeSnapshot: (onSnapshot) => subscribe(
|
|
414
|
+
(client) => client[TOKMON_WS_METHODS.snapshot]({}).pipe(Stream.map(normalizeSnapshot)),
|
|
415
|
+
onSnapshot,
|
|
416
|
+
(snapshot) => Math.max(options.snapshotStaleFloorMs ?? 9e4, snapshot.intervalMs * 3)
|
|
417
|
+
),
|
|
418
|
+
subscribeConfig: (onConfig) => subscribe(
|
|
419
|
+
(client) => client[TOKMON_WS_METHODS.config]({}).pipe(Stream.map(normalizeConfigState)),
|
|
420
|
+
onConfig
|
|
421
|
+
),
|
|
422
|
+
async close() {
|
|
423
|
+
if (closed) return;
|
|
424
|
+
closed = true;
|
|
425
|
+
setConn("closed");
|
|
426
|
+
clearReconnectTimer();
|
|
427
|
+
const cleanup = [...subscriptions].map(disposeSubscription);
|
|
428
|
+
subscriptions.clear();
|
|
429
|
+
const activeSession = session;
|
|
430
|
+
session = null;
|
|
431
|
+
await pendingRuntime?.dispose().catch(() => {
|
|
432
|
+
});
|
|
433
|
+
await Promise.all(cleanup);
|
|
434
|
+
await activeSession?.runtime.dispose().catch(() => {
|
|
435
|
+
});
|
|
436
|
+
await sessionPromise?.catch(() => {
|
|
437
|
+
});
|
|
438
|
+
pendingRuntime = null;
|
|
439
|
+
sessionPromise = null;
|
|
440
|
+
supervisorPromise = null;
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export {
|
|
446
|
+
createDaemonRpcClient
|
|
447
|
+
};
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
envDir,
|
|
6
6
|
expandHome,
|
|
7
7
|
isValidTimezone
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-HNWEJ6MS.js";
|
|
9
9
|
|
|
10
10
|
// src/async.ts
|
|
11
11
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
@@ -20,13 +20,6 @@ function withTimeout(promise, ms = DEFAULT_TIMEOUT_MS) {
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
// src/providers/usage-core.ts
|
|
24
|
-
import { readFileSync, readdirSync } from "fs";
|
|
25
|
-
import { readdir as readdir2 } from "fs/promises";
|
|
26
|
-
import { dirname, join as join2, relative } from "path";
|
|
27
|
-
import { createHash as createHash2 } from "crypto";
|
|
28
|
-
import { fileURLToPath } from "url";
|
|
29
|
-
|
|
30
23
|
// src/tz.ts
|
|
31
24
|
function systemTimezone() {
|
|
32
25
|
try {
|
|
@@ -120,14 +113,17 @@ function startOfWeek(ts, tz) {
|
|
|
120
113
|
const offset = p.weekday === 0 ? 6 : p.weekday - 1;
|
|
121
114
|
return instantFromTz(p.y, p.m, p.d - offset, 0, 0, 0, tz);
|
|
122
115
|
}
|
|
123
|
-
function monthsAgoStart(ts, months, tz) {
|
|
124
|
-
const p = tzParts(ts, tz);
|
|
125
|
-
return instantFromTz(p.y, p.m - months, 1, 0, 0, 0, tz);
|
|
126
|
-
}
|
|
127
116
|
function weekKey(ts, tz) {
|
|
128
117
|
return dayKey(startOfWeek(ts, tz), tz);
|
|
129
118
|
}
|
|
130
119
|
|
|
120
|
+
// src/providers/usage-core.ts
|
|
121
|
+
import { readFileSync, readdirSync } from "fs";
|
|
122
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
123
|
+
import { dirname, join as join2, relative } from "path";
|
|
124
|
+
import { createHash as createHash2 } from "crypto";
|
|
125
|
+
import { fileURLToPath } from "url";
|
|
126
|
+
|
|
131
127
|
// src/providers/_shared/metric.ts
|
|
132
128
|
var finite = (value, fallback = 0) => typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
133
129
|
var finiteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
@@ -395,7 +391,7 @@ async function mapLimit(items, limit, fn) {
|
|
|
395
391
|
}
|
|
396
392
|
|
|
397
393
|
// src/providers/usage-core.ts
|
|
398
|
-
var SPARK_DAYS =
|
|
394
|
+
var SPARK_DAYS = 30;
|
|
399
395
|
var DAY_MS = 864e5;
|
|
400
396
|
var PRUNE_AGE_MS = 230 * DAY_MS;
|
|
401
397
|
var stores = /* @__PURE__ */ new Map();
|
|
@@ -560,8 +556,8 @@ function dashboardSince(tz) {
|
|
|
560
556
|
const now = Date.now();
|
|
561
557
|
return Math.min(startOfMonth(now, tz), startOfWeek(now, tz), now - SPARK_DAYS * DAY_MS);
|
|
562
558
|
}
|
|
563
|
-
function tableSince(
|
|
564
|
-
return
|
|
559
|
+
function tableSince(_tz) {
|
|
560
|
+
return 0;
|
|
565
561
|
}
|
|
566
562
|
function cleanEntry(e) {
|
|
567
563
|
return {
|
|
@@ -596,6 +592,7 @@ function summarize(entries, tz) {
|
|
|
596
592
|
const byDay = /* @__PURE__ */ new Map();
|
|
597
593
|
let oldestToday = now;
|
|
598
594
|
let hadToday = false;
|
|
595
|
+
let lastActivityAt = null;
|
|
599
596
|
const add = (s, e) => {
|
|
600
597
|
s.cost += e.cost;
|
|
601
598
|
s.tokens += e.input + e.output + e.cacheCreate + e.cacheRead;
|
|
@@ -605,6 +602,9 @@ function summarize(entries, tz) {
|
|
|
605
602
|
};
|
|
606
603
|
for (const raw of entries) {
|
|
607
604
|
const e = cleanEntry(raw);
|
|
605
|
+
if (e.ts > 0 && (lastActivityAt === null || e.ts > lastActivityAt)) {
|
|
606
|
+
lastActivityAt = Math.floor(e.ts);
|
|
607
|
+
}
|
|
608
608
|
if (e.ts >= monthStart) add(month, e);
|
|
609
609
|
if (e.ts >= weekStart) add(week, e);
|
|
610
610
|
if (e.ts >= todayStart) {
|
|
@@ -619,7 +619,7 @@ function summarize(entries, tz) {
|
|
|
619
619
|
const rawBurnRate = hadToday ? today.cost / hrs : 0;
|
|
620
620
|
const burnRate = Number.isFinite(rawBurnRate) ? rawBurnRate : 0;
|
|
621
621
|
const series = lastDayKeys(now, tz, SPARK_DAYS).map((k) => byDay.get(k) ?? 0);
|
|
622
|
-
return { today, week, month, burnRate, series };
|
|
622
|
+
return { today, week, month, burnRate, series, lastActivityAt };
|
|
623
623
|
}
|
|
624
624
|
function groupBy(entries, keyFn) {
|
|
625
625
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -747,7 +747,10 @@ function formatCurrency(value, opts = {}) {
|
|
|
747
747
|
const abs = Math.abs(value);
|
|
748
748
|
if (abs >= 1e5) return `${sign}$${(value / 1e3).toFixed(0)}k`;
|
|
749
749
|
if (abs >= 1e4) return `${sign}$${(value / 1e3).toFixed(1)}k`;
|
|
750
|
-
if (abs >= 1)
|
|
750
|
+
if (abs >= 1) {
|
|
751
|
+
const polarity = value < 0 ? "-" : "";
|
|
752
|
+
return `${sign}${polarity}$${abs.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
753
|
+
}
|
|
751
754
|
if (abs >= 0.01) return `${sign}$${value.toFixed(3)}`;
|
|
752
755
|
if (abs === 0) return "$0.00";
|
|
753
756
|
return `${sign}$${value.toFixed(4)}`;
|
|
@@ -784,17 +787,22 @@ function formatShortDate(label, opts = {}) {
|
|
|
784
787
|
const day = Number(match[3]);
|
|
785
788
|
return `${MONTHS[Number(match[2]) - 1]} ${opts.padDay ? day.toString().padStart(2, " ") : day}`;
|
|
786
789
|
}
|
|
790
|
+
function resetParts(diffMs) {
|
|
791
|
+
if (!Number.isFinite(diffMs) || diffMs <= 0) return null;
|
|
792
|
+
const totalMinutes = Math.max(1, Math.ceil(diffMs / 6e4));
|
|
793
|
+
return {
|
|
794
|
+
days: Math.floor(totalMinutes / (24 * 60)),
|
|
795
|
+
hours: Math.floor(totalMinutes % (24 * 60) / 60),
|
|
796
|
+
minutes: totalMinutes % 60,
|
|
797
|
+
totalMinutes
|
|
798
|
+
};
|
|
799
|
+
}
|
|
787
800
|
function formatResetIn(iso, now = Date.now()) {
|
|
788
|
-
const
|
|
789
|
-
if (!
|
|
790
|
-
|
|
791
|
-
if (
|
|
792
|
-
|
|
793
|
-
const mins = minutes % 60;
|
|
794
|
-
if (hours < 24) return `${hours}h ${mins}m`;
|
|
795
|
-
const days = Math.floor(hours / 24);
|
|
796
|
-
const hrs = hours % 24;
|
|
797
|
-
return `${days}d ${hrs}h`;
|
|
801
|
+
const parts = resetParts(new Date(iso).getTime() - now);
|
|
802
|
+
if (!parts) return "now";
|
|
803
|
+
if (parts.days > 0) return `${parts.days}d ${parts.hours}h`;
|
|
804
|
+
if (parts.hours > 0) return `${parts.hours}h ${parts.minutes}m`;
|
|
805
|
+
return `${parts.minutes}m`;
|
|
798
806
|
}
|
|
799
807
|
function formatResetAt(iso, display, now = Date.now(), tz) {
|
|
800
808
|
const timestamp = Date.parse(iso);
|
|
@@ -1269,7 +1277,19 @@ function limitMetric(entry, primary) {
|
|
|
1269
1277
|
if (used === void 0) return null;
|
|
1270
1278
|
const label = limitLabel(o);
|
|
1271
1279
|
if (!label) return null;
|
|
1272
|
-
|
|
1280
|
+
const scope = recordValue(o.scope);
|
|
1281
|
+
const model = recordValue(scope?.model);
|
|
1282
|
+
const modelId = nonEmptyString(model?.id);
|
|
1283
|
+
const kind = nonEmptyString(o.kind)?.toLowerCase() ?? "";
|
|
1284
|
+
const scopedModel = kind === "weekly_scoped" || kind.startsWith("weekly_model");
|
|
1285
|
+
const role = kind === "session" || nonEmptyString(o.group)?.toLowerCase() === "session" ? "session" : kind === "weekly_all" ? "weekly" : modelId || scopedModel ? "model" : "other";
|
|
1286
|
+
return {
|
|
1287
|
+
...percentMetric(label, used, resetFrom(o.resets_at), primary),
|
|
1288
|
+
key: modelId ? `model:${modelId}` : kind || label.toLowerCase(),
|
|
1289
|
+
role,
|
|
1290
|
+
modelId: modelId ?? (scopedModel ? label.toLowerCase().replace(/\s+/g, "-") : null),
|
|
1291
|
+
active: boolValue(o.is_active)
|
|
1292
|
+
};
|
|
1273
1293
|
}
|
|
1274
1294
|
function limitMetrics(limits) {
|
|
1275
1295
|
if (!Array.isArray(limits)) return [];
|
|
@@ -1296,7 +1316,12 @@ function topLevelUsageMetrics(data) {
|
|
|
1296
1316
|
const window = recordValue(value);
|
|
1297
1317
|
if (!window || numberValue(window.utilization) === void 0) continue;
|
|
1298
1318
|
const metric = usageMetric(usageLabelFromKey(key), window, key === "five_hour" ? true : void 0);
|
|
1299
|
-
if (metric) metrics.push(
|
|
1319
|
+
if (metric) metrics.push({
|
|
1320
|
+
...metric,
|
|
1321
|
+
key,
|
|
1322
|
+
role: key === "five_hour" ? "session" : key === "seven_day" ? "weekly" : key.startsWith("seven_day_") ? "model" : "other",
|
|
1323
|
+
modelId: key.startsWith("seven_day_") ? key.slice("seven_day_".length) : null
|
|
1324
|
+
});
|
|
1300
1325
|
}
|
|
1301
1326
|
return metrics;
|
|
1302
1327
|
}
|
|
@@ -1339,6 +1364,8 @@ async function claudeBilling(account) {
|
|
|
1339
1364
|
if (usedCredits !== void 0 && (usedCredits > 0 || monthlyLimit !== void 0 && monthlyLimit > 0)) {
|
|
1340
1365
|
const scale = decimalScale(data.extra_usage?.decimal_places);
|
|
1341
1366
|
metrics.push({
|
|
1367
|
+
key: "extra_usage",
|
|
1368
|
+
role: "unbounded",
|
|
1342
1369
|
label: "Extra",
|
|
1343
1370
|
used: finite(usedCredits) / scale,
|
|
1344
1371
|
limit: monthlyLimit !== void 0 && monthlyLimit > 0 ? monthlyLimit / scale : null,
|