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
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
BUILT_IN_THEME_PRESET_IDS,
|
|
4
|
+
DESKTOP_GRAPH_RANGES,
|
|
5
|
+
PROVIDER_IDS,
|
|
6
|
+
THEME_PRESET_IDS
|
|
7
|
+
} from "./chunk-HNWEJ6MS.js";
|
|
5
8
|
|
|
6
9
|
// src/rpc/contract.ts
|
|
7
10
|
import { Schema } from "effect";
|
|
@@ -9,7 +12,7 @@ import * as Rpc from "effect/unstable/rpc/Rpc";
|
|
|
9
12
|
import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
|
10
13
|
var TOKMON_WS_PATH = "/ws";
|
|
11
14
|
var TOKMON_PROTOCOL_VERSION = 3;
|
|
12
|
-
var TOKMON_CAPABILITIES = ["config-cas", "config-revision", "allowed-hosts"];
|
|
15
|
+
var TOKMON_CAPABILITIES = ["config-cas", "config-revision", "allowed-hosts", "tray-config", "usage-activity", "tray-pins", "provider-pins", "desktop-disclosure", "desktop-graph-range", "provider-headroom", "canonical-identity", "appearance-v1", "theme-engine", "account-detection-v1"];
|
|
13
16
|
var TOKMON_WS_METHODS = {
|
|
14
17
|
getConfig: "tokmon.getConfig",
|
|
15
18
|
setConfig: "tokmon.setConfig",
|
|
@@ -35,6 +38,65 @@ var AccountSchema = Schema.Struct({
|
|
|
35
38
|
homeDir: Schema.String,
|
|
36
39
|
color: Schema.optionalKey(Schema.String)
|
|
37
40
|
});
|
|
41
|
+
var TrayConfigSchema = Schema.Struct({
|
|
42
|
+
enabled: Schema.Boolean,
|
|
43
|
+
showMenuBarText: Schema.Boolean,
|
|
44
|
+
displayMetric: Schema.Literals(["smartHeadroom", "tightestRemaining"]),
|
|
45
|
+
pollIntervalSec: Schema.Finite.check(Schema.isBetween({ minimum: 1, maximum: 86400 })),
|
|
46
|
+
activeTimeoutMin: Schema.Finite.check(Schema.isBetween({ minimum: 1, maximum: 1440 })),
|
|
47
|
+
graceMin: Schema.Finite.check(Schema.isBetween({ minimum: 1, maximum: 10080 })),
|
|
48
|
+
promotionHoldMin: Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1440 })),
|
|
49
|
+
lowWatermarkPct: Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 100 })),
|
|
50
|
+
criticalWatermarkPct: Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 100 })),
|
|
51
|
+
pinnedAccount: Schema.NullOr(Schema.String),
|
|
52
|
+
pins: Schema.Array(Schema.String),
|
|
53
|
+
// Additive since `tray-pins`: older daemons omit it, so tolerate absence and let
|
|
54
|
+
// `repairConfig` materialise the empty default rather than failing the decode.
|
|
55
|
+
pinnedProviders: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
56
|
+
launchAtLogin: Schema.Boolean,
|
|
57
|
+
theme: Schema.Literal("dark")
|
|
58
|
+
});
|
|
59
|
+
var DesktopConfigSchema = Schema.Struct({
|
|
60
|
+
expandedProviders: Schema.Array(Schema.String),
|
|
61
|
+
graphRangeDays: Schema.optionalKey(Schema.Literals(DESKTOP_GRAPH_RANGES))
|
|
62
|
+
});
|
|
63
|
+
var AccountDetectionConfigSchema = Schema.Struct({
|
|
64
|
+
enabled: Schema.Boolean,
|
|
65
|
+
disabledProviders: Schema.Array(ProviderIdSchema),
|
|
66
|
+
excludedAccounts: Schema.Array(Schema.Struct({
|
|
67
|
+
providerId: ProviderIdSchema,
|
|
68
|
+
homeDir: Schema.String
|
|
69
|
+
}))
|
|
70
|
+
});
|
|
71
|
+
var HexColorSchema = Schema.String.check(Schema.isPattern(/^#[0-9a-f]{6}$/i));
|
|
72
|
+
var ThemeOverridesSchema = Schema.Struct({
|
|
73
|
+
canvas: Schema.optionalKey(HexColorSchema),
|
|
74
|
+
panel: Schema.optionalKey(HexColorSchema),
|
|
75
|
+
inset: Schema.optionalKey(HexColorSchema),
|
|
76
|
+
insetStrong: Schema.optionalKey(HexColorSchema),
|
|
77
|
+
chrome: Schema.optionalKey(HexColorSchema),
|
|
78
|
+
line: Schema.optionalKey(HexColorSchema),
|
|
79
|
+
lineStrong: Schema.optionalKey(HexColorSchema),
|
|
80
|
+
lineFaint: Schema.optionalKey(HexColorSchema),
|
|
81
|
+
text: Schema.optionalKey(HexColorSchema),
|
|
82
|
+
textDim: Schema.optionalKey(HexColorSchema),
|
|
83
|
+
textFaint: Schema.optionalKey(HexColorSchema),
|
|
84
|
+
textStrong: Schema.optionalKey(HexColorSchema),
|
|
85
|
+
accent: Schema.optionalKey(HexColorSchema),
|
|
86
|
+
cost: Schema.optionalKey(HexColorSchema),
|
|
87
|
+
positive: Schema.optionalKey(HexColorSchema)
|
|
88
|
+
});
|
|
89
|
+
var AppearanceConfigSchema = Schema.Struct({
|
|
90
|
+
version: Schema.Literal(1),
|
|
91
|
+
mode: Schema.Literals(["auto", "light", "dark"]),
|
|
92
|
+
preset: Schema.Literals(THEME_PRESET_IDS),
|
|
93
|
+
terminal: Schema.Literals(["ansi", "dark", "light", "off"]),
|
|
94
|
+
custom: Schema.optionalKey(Schema.Struct({
|
|
95
|
+
base: Schema.Literals(BUILT_IN_THEME_PRESET_IDS),
|
|
96
|
+
light: ThemeOverridesSchema,
|
|
97
|
+
dark: ThemeOverridesSchema
|
|
98
|
+
}))
|
|
99
|
+
});
|
|
38
100
|
var ConfigSchema = Schema.Struct({
|
|
39
101
|
revision: NonNegativeIntegerSchema,
|
|
40
102
|
interval: PositiveFiniteSchema,
|
|
@@ -53,7 +115,17 @@ var ConfigSchema = Schema.Struct({
|
|
|
53
115
|
allowNetworkAccess: Schema.Boolean,
|
|
54
116
|
allowedHosts: Schema.Array(Schema.String),
|
|
55
117
|
resetDisplay: Schema.Literals(["relative", "absolute"]),
|
|
56
|
-
knownProviders: Schema.Array(ProviderIdSchema)
|
|
118
|
+
knownProviders: Schema.Array(ProviderIdSchema),
|
|
119
|
+
// Additive under `appearance-v1`; omission is accepted for old clients and
|
|
120
|
+
// preserved by the daemon's CAS boundary rather than resetting the theme.
|
|
121
|
+
appearance: Schema.optionalKey(AppearanceConfigSchema),
|
|
122
|
+
// Additive: older clients omit detector policy, so the daemon preserves its
|
|
123
|
+
// current value and repairConfig supplies legacy current-behaviour defaults.
|
|
124
|
+
accountDetection: Schema.optionalKey(AccountDetectionConfigSchema),
|
|
125
|
+
// The complete tray block was added within protocol v3. Accept omission from
|
|
126
|
+
// older peers and normalize it at the client / preserve it at the CAS boundary.
|
|
127
|
+
tray: Schema.optionalKey(TrayConfigSchema),
|
|
128
|
+
desktop: Schema.optionalKey(DesktopConfigSchema)
|
|
57
129
|
});
|
|
58
130
|
var ProtocolInfoSchema = Schema.Struct({
|
|
59
131
|
version: Schema.Literal(TOKMON_PROTOCOL_VERSION),
|
|
@@ -70,14 +142,46 @@ var ConfigUpdateRequestSchema = Schema.Struct({
|
|
|
70
142
|
expectedRevision: NonNegativeIntegerSchema,
|
|
71
143
|
config: ConfigSchema
|
|
72
144
|
});
|
|
73
|
-
var
|
|
145
|
+
var LegacyConfigUpdateConflictSchema = Schema.Struct({
|
|
74
146
|
kind: Schema.Literal("conflict"),
|
|
75
147
|
state: ConfigStateSchema
|
|
76
148
|
});
|
|
77
|
-
var
|
|
149
|
+
var ConfigUpdateConflictFailure = class extends Schema.TaggedErrorClass()(
|
|
150
|
+
"ConfigUpdateConflictFailure",
|
|
151
|
+
{
|
|
152
|
+
kind: Schema.Literal("conflict"),
|
|
153
|
+
state: ConfigStateSchema
|
|
154
|
+
}
|
|
155
|
+
) {
|
|
156
|
+
};
|
|
157
|
+
var LegacyConfigPersistenceFailureSchema = Schema.Struct({
|
|
78
158
|
kind: Schema.Literal("persistence"),
|
|
79
159
|
message: Schema.String
|
|
80
160
|
});
|
|
161
|
+
var ConfigPersistenceFailure = class extends Schema.TaggedErrorClass()(
|
|
162
|
+
"ConfigPersistenceFailure",
|
|
163
|
+
{
|
|
164
|
+
kind: Schema.Literal("persistence"),
|
|
165
|
+
message: Schema.String
|
|
166
|
+
}
|
|
167
|
+
) {
|
|
168
|
+
};
|
|
169
|
+
var ConfigUpdateConflictSchema = Schema.Union([
|
|
170
|
+
ConfigUpdateConflictFailure,
|
|
171
|
+
LegacyConfigUpdateConflictSchema
|
|
172
|
+
]);
|
|
173
|
+
var ConfigPersistenceFailureSchema = Schema.Union([
|
|
174
|
+
ConfigPersistenceFailure,
|
|
175
|
+
LegacyConfigPersistenceFailureSchema
|
|
176
|
+
]);
|
|
177
|
+
var RefreshFailure = class extends Schema.TaggedErrorClass()(
|
|
178
|
+
"RefreshFailure",
|
|
179
|
+
{
|
|
180
|
+
kind: Schema.Literal("refresh"),
|
|
181
|
+
message: Schema.String
|
|
182
|
+
}
|
|
183
|
+
) {
|
|
184
|
+
};
|
|
81
185
|
var FsEntrySchema = Schema.Struct({
|
|
82
186
|
name: Schema.String,
|
|
83
187
|
path: Schema.String,
|
|
@@ -123,7 +227,8 @@ var DashboardDataSchema = Schema.Struct({
|
|
|
123
227
|
week: UsageSummarySchema,
|
|
124
228
|
month: UsageSummarySchema,
|
|
125
229
|
burnRate: Schema.Finite,
|
|
126
|
-
series: Schema.Array(Schema.Finite)
|
|
230
|
+
series: Schema.Array(Schema.Finite),
|
|
231
|
+
lastActivityAt: Schema.NullOr(NonNegativeIntegerSchema)
|
|
127
232
|
});
|
|
128
233
|
var TableDataSchema = Schema.Struct({
|
|
129
234
|
daily: Schema.Array(TableRowSchema),
|
|
@@ -136,6 +241,10 @@ var MetricFormatSchema = Schema.Union([
|
|
|
136
241
|
Schema.Struct({ kind: Schema.Literal("count"), suffix: Schema.optionalKey(Schema.String) })
|
|
137
242
|
]);
|
|
138
243
|
var MetricSchema = Schema.Struct({
|
|
244
|
+
key: Schema.optionalKey(Schema.String),
|
|
245
|
+
role: Schema.optionalKey(Schema.Literals(["session", "weekly", "model", "other", "unbounded"])),
|
|
246
|
+
modelId: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
247
|
+
active: Schema.optionalKey(Schema.Boolean),
|
|
139
248
|
label: Schema.String,
|
|
140
249
|
used: Schema.Finite,
|
|
141
250
|
limit: Schema.NullOr(Schema.Finite),
|
|
@@ -143,6 +252,45 @@ var MetricSchema = Schema.Struct({
|
|
|
143
252
|
resetsAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
144
253
|
primary: Schema.optionalKey(Schema.Boolean)
|
|
145
254
|
});
|
|
255
|
+
var MetricRoleSchema = Schema.Literals(["session", "weekly", "model", "other", "unbounded"]);
|
|
256
|
+
var AccountIdentityViewSchema = Schema.Struct({
|
|
257
|
+
title: Schema.String,
|
|
258
|
+
subtitle: Schema.NullOr(Schema.String),
|
|
259
|
+
accessibleLabel: Schema.String,
|
|
260
|
+
redacted: Schema.Boolean
|
|
261
|
+
});
|
|
262
|
+
var QuotaViewSchema = Schema.Struct({
|
|
263
|
+
key: Schema.String,
|
|
264
|
+
label: Schema.String,
|
|
265
|
+
role: MetricRoleSchema,
|
|
266
|
+
modelId: Schema.NullOr(Schema.String),
|
|
267
|
+
usedPct: Schema.NullOr(Schema.Finite),
|
|
268
|
+
remainingPct: Schema.NullOr(Schema.Finite),
|
|
269
|
+
resetsAt: Schema.NullOr(NonNegativeIntegerSchema),
|
|
270
|
+
bounded: Schema.Boolean,
|
|
271
|
+
primary: Schema.Boolean,
|
|
272
|
+
active: Schema.Boolean,
|
|
273
|
+
displayOrder: NonNegativeIntegerSchema,
|
|
274
|
+
valueText: Schema.String
|
|
275
|
+
});
|
|
276
|
+
var HeadroomFactorSchema = Schema.Struct({
|
|
277
|
+
key: Schema.String,
|
|
278
|
+
label: Schema.String,
|
|
279
|
+
role: MetricRoleSchema,
|
|
280
|
+
remainingPct: Schema.Finite,
|
|
281
|
+
included: Schema.Boolean,
|
|
282
|
+
reason: Schema.Literals(["session", "active-model", "weekly-cap", "primary", "fallback-floor"])
|
|
283
|
+
});
|
|
284
|
+
var HeadroomViewSchema = Schema.Struct({
|
|
285
|
+
value: Schema.NullOr(Schema.Finite),
|
|
286
|
+
unit: Schema.Literal("index-100"),
|
|
287
|
+
mode: Schema.Literals(["smart", "single-window", "fallback-floor", "unavailable"]),
|
|
288
|
+
basis: Schema.Literals(["active", "idle-runway", "unavailable"]),
|
|
289
|
+
representativeAccountId: Schema.NullOr(Schema.String),
|
|
290
|
+
activeAccountIds: Schema.Array(Schema.String),
|
|
291
|
+
factors: Schema.Array(HeadroomFactorSchema),
|
|
292
|
+
explanation: Schema.String
|
|
293
|
+
});
|
|
146
294
|
var BillingResultSchema = Schema.Struct({
|
|
147
295
|
plan: Schema.NullOr(Schema.String),
|
|
148
296
|
metrics: Schema.Array(MetricSchema),
|
|
@@ -158,7 +306,7 @@ var BillingResultSchema = Schema.Struct({
|
|
|
158
306
|
usd: Schema.Finite,
|
|
159
307
|
requests: Schema.Finite
|
|
160
308
|
})))),
|
|
161
|
-
asOfMs: Schema.optionalKey(
|
|
309
|
+
asOfMs: Schema.optionalKey(NonNegativeIntegerSchema)
|
|
162
310
|
});
|
|
163
311
|
var WebAccountSchema = Schema.Struct({
|
|
164
312
|
id: Schema.String,
|
|
@@ -170,7 +318,11 @@ var WebAccountSchema = Schema.Struct({
|
|
|
170
318
|
hasBilling: Schema.Boolean,
|
|
171
319
|
email: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
172
320
|
displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
321
|
+
identity: Schema.optionalKey(AccountIdentityViewSchema),
|
|
322
|
+
quotas: Schema.optionalKey(Schema.Array(QuotaViewSchema)),
|
|
323
|
+
headroom: Schema.optionalKey(HeadroomViewSchema),
|
|
173
324
|
plan: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
325
|
+
lastActivityAt: Schema.NullOr(NonNegativeIntegerSchema),
|
|
174
326
|
dashboard: Schema.NullOr(DashboardDataSchema),
|
|
175
327
|
table: Schema.NullOr(TableDataSchema),
|
|
176
328
|
billing: Schema.NullOr(BillingResultSchema),
|
|
@@ -190,7 +342,8 @@ var WebSnapshotSchema = Schema.Struct({
|
|
|
190
342
|
providers: Schema.Array(Schema.Struct({
|
|
191
343
|
id: ProviderIdSchema,
|
|
192
344
|
name: Schema.String,
|
|
193
|
-
color: Schema.String
|
|
345
|
+
color: Schema.String,
|
|
346
|
+
headroom: Schema.optionalKey(HeadroomViewSchema)
|
|
194
347
|
})),
|
|
195
348
|
accounts: Schema.Array(WebAccountSchema),
|
|
196
349
|
seeded: Schema.Boolean,
|
|
@@ -215,7 +368,8 @@ var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
|
|
|
215
368
|
});
|
|
216
369
|
var RefreshRpc = Rpc.make(TOKMON_WS_METHODS.refresh, {
|
|
217
370
|
payload: Schema.Struct({ scope: RefreshScopeSchema }),
|
|
218
|
-
success: Schema.Void
|
|
371
|
+
success: Schema.Void,
|
|
372
|
+
error: RefreshFailure
|
|
219
373
|
});
|
|
220
374
|
var BrowseFsRpc = Rpc.make(TOKMON_WS_METHODS.browseFs, {
|
|
221
375
|
payload: Schema.Struct({ path: Schema.String }),
|
|
@@ -245,5 +399,8 @@ export {
|
|
|
245
399
|
TOKMON_PROTOCOL_VERSION,
|
|
246
400
|
TOKMON_CAPABILITIES,
|
|
247
401
|
TOKMON_WS_METHODS,
|
|
402
|
+
ConfigUpdateConflictFailure,
|
|
403
|
+
ConfigPersistenceFailure,
|
|
404
|
+
RefreshFailure,
|
|
248
405
|
TokmonRpcGroup
|
|
249
406
|
};
|
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveDaemonChannel
|
|
4
|
+
} from "./chunk-UYPDMVW5.js";
|
|
2
5
|
import {
|
|
3
6
|
buildAccounts,
|
|
4
7
|
colorHex,
|
|
8
|
+
deriveAccountIdentity,
|
|
9
|
+
deriveProviderHeadroom,
|
|
10
|
+
deriveQuotaViews,
|
|
5
11
|
fetchPeak,
|
|
6
12
|
namedHex
|
|
7
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TN6V7XSL.js";
|
|
8
14
|
import {
|
|
9
15
|
PROVIDERS,
|
|
10
16
|
detectProviders,
|
|
11
17
|
resolveTimezone,
|
|
12
18
|
withTimeout
|
|
13
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-CFKUZCMB.js";
|
|
14
20
|
import {
|
|
21
|
+
ConfigPersistenceFailure,
|
|
22
|
+
ConfigUpdateConflictFailure,
|
|
23
|
+
RefreshFailure,
|
|
15
24
|
TOKMON_CAPABILITIES,
|
|
16
25
|
TOKMON_PROTOCOL_VERSION,
|
|
17
26
|
TOKMON_WS_METHODS,
|
|
18
27
|
TOKMON_WS_PATH,
|
|
19
28
|
TokmonRpcGroup
|
|
20
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-JZSZEHVD.js";
|
|
21
30
|
import {
|
|
22
31
|
cacheDir,
|
|
23
32
|
expandHome,
|
|
@@ -25,7 +34,7 @@ import {
|
|
|
25
34
|
normalizeConfig,
|
|
26
35
|
saveConfig,
|
|
27
36
|
snapshotCacheFile
|
|
28
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-HNWEJ6MS.js";
|
|
29
38
|
|
|
30
39
|
// src/web/server.ts
|
|
31
40
|
import { createServer } from "http";
|
|
@@ -61,9 +70,13 @@ async function fetchAccountBilling(account, tz) {
|
|
|
61
70
|
return p.fetchBilling(account, tz);
|
|
62
71
|
}
|
|
63
72
|
function assembleSnapshot(opts) {
|
|
73
|
+
const providerOrdinals = /* @__PURE__ */ new Map();
|
|
64
74
|
const accounts = opts.resolved.map((r) => {
|
|
65
75
|
const u = opts.usage.get(r.account.id);
|
|
66
76
|
const billing = opts.billing.get(r.account.id) ?? null;
|
|
77
|
+
const ordinal = (providerOrdinals.get(r.account.providerId) ?? 0) + 1;
|
|
78
|
+
providerOrdinals.set(r.account.providerId, ordinal);
|
|
79
|
+
const quotas = deriveQuotaViews(billing?.metrics ?? []);
|
|
67
80
|
return {
|
|
68
81
|
id: r.account.id,
|
|
69
82
|
providerId: r.account.providerId,
|
|
@@ -74,7 +87,22 @@ function assembleSnapshot(opts) {
|
|
|
74
87
|
hasBilling: r.hasBilling,
|
|
75
88
|
email: billing?.email ?? null,
|
|
76
89
|
displayName: billing?.displayName ?? null,
|
|
90
|
+
identity: deriveAccountIdentity({
|
|
91
|
+
name: r.account.name,
|
|
92
|
+
email: billing?.email,
|
|
93
|
+
displayName: billing?.displayName,
|
|
94
|
+
providerName: PROVIDERS[r.account.providerId].name,
|
|
95
|
+
ordinal,
|
|
96
|
+
privacyMode: opts.config.privacyMode
|
|
97
|
+
}),
|
|
98
|
+
quotas,
|
|
99
|
+
headroom: deriveProviderHeadroom([{
|
|
100
|
+
id: r.account.id,
|
|
101
|
+
lastActivityAt: u?.dashboard?.lastActivityAt ?? null,
|
|
102
|
+
quotas
|
|
103
|
+
}], opts.config.tray.activeTimeoutMin, Date.now(), opts.config.tray.displayMetric),
|
|
77
104
|
plan: billing?.plan ?? null,
|
|
105
|
+
lastActivityAt: u?.dashboard?.lastActivityAt ?? null,
|
|
78
106
|
dashboard: u?.dashboard ?? null,
|
|
79
107
|
table: u?.table ?? null,
|
|
80
108
|
billing,
|
|
@@ -86,6 +114,12 @@ function assembleSnapshot(opts) {
|
|
|
86
114
|
tableUpdatedAt: opts.tableUpdatedAt?.get(r.account.id) ?? null
|
|
87
115
|
};
|
|
88
116
|
});
|
|
117
|
+
const accountsByProvider = /* @__PURE__ */ new Map();
|
|
118
|
+
for (const account of accounts) {
|
|
119
|
+
const group = accountsByProvider.get(account.providerId) ?? [];
|
|
120
|
+
group.push(account);
|
|
121
|
+
accountsByProvider.set(account.providerId, group);
|
|
122
|
+
}
|
|
89
123
|
const seen = /* @__PURE__ */ new Set();
|
|
90
124
|
const providers = [];
|
|
91
125
|
for (const r of opts.resolved) {
|
|
@@ -94,7 +128,17 @@ function assembleSnapshot(opts) {
|
|
|
94
128
|
providers.push({
|
|
95
129
|
id: r.account.providerId,
|
|
96
130
|
name: PROVIDERS[r.account.providerId].name,
|
|
97
|
-
color: namedHex(PROVIDERS[r.account.providerId].color)
|
|
131
|
+
color: namedHex(PROVIDERS[r.account.providerId].color),
|
|
132
|
+
headroom: deriveProviderHeadroom(
|
|
133
|
+
(accountsByProvider.get(r.account.providerId) ?? []).map((account) => ({
|
|
134
|
+
id: account.id,
|
|
135
|
+
lastActivityAt: account.lastActivityAt,
|
|
136
|
+
quotas: account.quotas ?? []
|
|
137
|
+
})),
|
|
138
|
+
opts.config.tray.activeTimeoutMin,
|
|
139
|
+
Date.now(),
|
|
140
|
+
opts.config.tray.displayMetric
|
|
141
|
+
)
|
|
98
142
|
});
|
|
99
143
|
}
|
|
100
144
|
return {
|
|
@@ -238,6 +282,7 @@ function isDevMode() {
|
|
|
238
282
|
const forced = process.env.TOKMON_WEB_MODE;
|
|
239
283
|
if (forced === "dev") return true;
|
|
240
284
|
if (forced === "prod") return false;
|
|
285
|
+
if (process.env.NODE_TEST_CONTEXT) return false;
|
|
241
286
|
return import.meta.url.includes("/src/");
|
|
242
287
|
}
|
|
243
288
|
function findWebSource() {
|
|
@@ -442,7 +487,8 @@ function createDataEngine(opts) {
|
|
|
442
487
|
billingUpdatedAt,
|
|
443
488
|
tableUpdatedAt,
|
|
444
489
|
seeded,
|
|
445
|
-
peak
|
|
490
|
+
peak,
|
|
491
|
+
config: currentConfig
|
|
446
492
|
});
|
|
447
493
|
const hydrateFromCache = () => {
|
|
448
494
|
try {
|
|
@@ -450,7 +496,11 @@ function createDataEngine(opts) {
|
|
|
450
496
|
if (!cached || !Array.isArray(cached.accounts)) return;
|
|
451
497
|
for (const a of cached.accounts) {
|
|
452
498
|
if (a.dashboard || a.table) {
|
|
453
|
-
|
|
499
|
+
const dashboard = a.dashboard ? {
|
|
500
|
+
...a.dashboard,
|
|
501
|
+
lastActivityAt: typeof a.dashboard.lastActivityAt === "number" && Number.isSafeInteger(a.dashboard.lastActivityAt) && a.dashboard.lastActivityAt >= 0 ? a.dashboard.lastActivityAt : null
|
|
502
|
+
} : null;
|
|
503
|
+
usage.set(a.id, { dashboard, table: a.table });
|
|
454
504
|
if (a.dashboard) {
|
|
455
505
|
summaryState.set(a.id, "ready");
|
|
456
506
|
if (typeof a.summaryUpdatedAt === "number") summaryUpdatedAt.set(a.id, a.summaryUpdatedAt);
|
|
@@ -689,6 +739,7 @@ function createDataEngine(opts) {
|
|
|
689
739
|
broadcastConfig(config) {
|
|
690
740
|
if (stopped) return;
|
|
691
741
|
currentConfig = config;
|
|
742
|
+
rebuild();
|
|
692
743
|
for (const onConfig of configSubscribers) {
|
|
693
744
|
try {
|
|
694
745
|
onConfig(config);
|
|
@@ -776,23 +827,51 @@ async function resolveEngineConfig(config) {
|
|
|
776
827
|
billingIntervalMs: billingIntervalFor(config)
|
|
777
828
|
};
|
|
778
829
|
}
|
|
830
|
+
function configAffectsEngine(previous, next) {
|
|
831
|
+
return previous.interval !== next.interval || previous.billingInterval !== next.billingInterval || previous.timezone !== next.timezone || JSON.stringify(previous.accounts) !== JSON.stringify(next.accounts) || JSON.stringify(previous.disabledProviders) !== JSON.stringify(next.disabledProviders) || JSON.stringify(previous.accountDetection) !== JSON.stringify(next.accountDetection);
|
|
832
|
+
}
|
|
779
833
|
var updateQueues = /* @__PURE__ */ new WeakMap();
|
|
834
|
+
function hasOwn(value, key) {
|
|
835
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
836
|
+
}
|
|
837
|
+
function mergeCapabilityFields(incomingConfig, current) {
|
|
838
|
+
const incoming = incomingConfig;
|
|
839
|
+
const incomingTray = incoming.tray;
|
|
840
|
+
const incomingDesktop = incoming.desktop;
|
|
841
|
+
return {
|
|
842
|
+
...incoming,
|
|
843
|
+
appearance: hasOwn(incoming, "appearance") ? incoming.appearance : current.appearance,
|
|
844
|
+
accountDetection: hasOwn(incoming, "accountDetection") ? incoming.accountDetection : current.accountDetection,
|
|
845
|
+
tray: hasOwn(incoming, "tray") && incomingTray ? {
|
|
846
|
+
...current.tray,
|
|
847
|
+
...incomingTray,
|
|
848
|
+
pinnedProviders: hasOwn(incomingTray, "pinnedProviders") ? incomingTray.pinnedProviders : current.tray.pinnedProviders
|
|
849
|
+
} : current.tray,
|
|
850
|
+
desktop: hasOwn(incoming, "desktop") && incomingDesktop ? {
|
|
851
|
+
...current.desktop,
|
|
852
|
+
...incomingDesktop,
|
|
853
|
+
expandedProviders: hasOwn(incomingDesktop, "expandedProviders") ? incomingDesktop.expandedProviders : current.desktop.expandedProviders,
|
|
854
|
+
graphRangeDays: hasOwn(incomingDesktop, "graphRangeDays") ? incomingDesktop.graphRangeDays : current.desktop.graphRangeDays
|
|
855
|
+
} : current.desktop
|
|
856
|
+
};
|
|
857
|
+
}
|
|
780
858
|
async function applyConfigUpdateUnlocked(engine, state, input) {
|
|
781
859
|
if (input.expectedRevision !== state.config.revision) {
|
|
782
860
|
throw new ConfigConflictError(toConfigState(state.config));
|
|
783
861
|
}
|
|
784
862
|
const normalized = normalizeConfig({
|
|
785
|
-
...input.config,
|
|
863
|
+
...mergeCapabilityFields(input.config, state.config),
|
|
786
864
|
revision: state.config.revision + 1
|
|
787
865
|
});
|
|
788
|
-
const
|
|
866
|
+
const reconfigureEngine = configAffectsEngine(state.config, normalized);
|
|
867
|
+
const engineConfig = reconfigureEngine ? await resolveEngineConfig(normalized) : null;
|
|
789
868
|
try {
|
|
790
869
|
await saveConfig(normalized);
|
|
791
870
|
} catch (error) {
|
|
792
871
|
throw new ConfigPersistenceError(error);
|
|
793
872
|
}
|
|
794
873
|
state.config = normalized;
|
|
795
|
-
engine.setConfig(engineConfig);
|
|
874
|
+
if (engineConfig) engine.setConfig(engineConfig);
|
|
796
875
|
engine.broadcastConfig(normalized);
|
|
797
876
|
return toConfigState(normalized);
|
|
798
877
|
}
|
|
@@ -958,27 +1037,45 @@ function configStream(engine) {
|
|
|
958
1037
|
yield* Scope.addFinalizer(scope, Effect.sync(unsubscribe));
|
|
959
1038
|
}), { bufferSize: 16, strategy: "sliding" });
|
|
960
1039
|
}
|
|
961
|
-
function
|
|
1040
|
+
function configUpdateFailureEffect(error) {
|
|
962
1041
|
if (error instanceof ConfigConflictError) {
|
|
963
|
-
return { kind: "conflict", state: error.state };
|
|
1042
|
+
return Effect.fail(new ConfigUpdateConflictFailure({ kind: "conflict", state: error.state }));
|
|
964
1043
|
}
|
|
965
1044
|
if (error instanceof ConfigPersistenceError) {
|
|
966
|
-
return { kind: "persistence", message: error.message };
|
|
1045
|
+
return Effect.fail(new ConfigPersistenceFailure({ kind: "persistence", message: error.message }));
|
|
967
1046
|
}
|
|
968
|
-
return
|
|
1047
|
+
return Effect.die(error);
|
|
1048
|
+
}
|
|
1049
|
+
function configUpdateEffect(engine, state, input) {
|
|
1050
|
+
return Effect.tryPromise({
|
|
1051
|
+
try: () => applyConfigUpdate(engine, state, input),
|
|
1052
|
+
catch: (error) => error
|
|
1053
|
+
}).pipe(Effect.matchEffect({
|
|
1054
|
+
onFailure: configUpdateFailureEffect,
|
|
1055
|
+
onSuccess: Effect.succeed
|
|
1056
|
+
}));
|
|
1057
|
+
}
|
|
1058
|
+
function refreshFailureEffect(error) {
|
|
1059
|
+
return error instanceof AggregateError ? Effect.fail(new RefreshFailure({ kind: "refresh", message: error.message })) : Effect.die(error);
|
|
1060
|
+
}
|
|
1061
|
+
function refreshEffect(engine, scope) {
|
|
1062
|
+
return Effect.tryPromise({
|
|
1063
|
+
try: () => engine.refresh(scope),
|
|
1064
|
+
catch: (error) => error
|
|
1065
|
+
}).pipe(Effect.matchEffect({
|
|
1066
|
+
onFailure: refreshFailureEffect,
|
|
1067
|
+
onSuccess: Effect.succeed
|
|
1068
|
+
}));
|
|
969
1069
|
}
|
|
970
1070
|
async function mountWsRpc(server, deps) {
|
|
971
1071
|
const scope = await Effect.runPromise(Scope.make());
|
|
972
1072
|
const wss = new NodeWS.WebSocketServer({ noServer: true });
|
|
973
1073
|
const handlersLayer = TokmonRpcGroup.toLayer(
|
|
974
1074
|
TokmonRpcGroup.of({
|
|
975
|
-
[TOKMON_WS_METHODS.getConfig]: () => Effect.
|
|
976
|
-
[TOKMON_WS_METHODS.setConfig]: (config) =>
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
}),
|
|
980
|
-
[TOKMON_WS_METHODS.refresh]: ({ scope: scope2 }) => Effect.promise(() => deps.engine.refresh(scope2)),
|
|
981
|
-
[TOKMON_WS_METHODS.browseFs]: ({ path }) => Effect.tryPromise(() => listHomeDirectory(path)),
|
|
1075
|
+
[TOKMON_WS_METHODS.getConfig]: () => Effect.promise(() => Promise.resolve(deps.state.config ?? loadConfig()).then(toConfigState)),
|
|
1076
|
+
[TOKMON_WS_METHODS.setConfig]: (config) => configUpdateEffect(deps.engine, deps.state, config),
|
|
1077
|
+
[TOKMON_WS_METHODS.refresh]: ({ scope: scope2 }) => refreshEffect(deps.engine, scope2),
|
|
1078
|
+
[TOKMON_WS_METHODS.browseFs]: ({ path }) => Effect.promise(() => listHomeDirectory(path)),
|
|
982
1079
|
[TOKMON_WS_METHODS.snapshot]: () => snapshotStream(deps.engine),
|
|
983
1080
|
[TOKMON_WS_METHODS.config]: () => configStream(deps.engine).pipe(Stream.map(toConfigState))
|
|
984
1081
|
})
|
|
@@ -1068,7 +1165,7 @@ function guardSameOrigin(req, res) {
|
|
|
1068
1165
|
}
|
|
1069
1166
|
return true;
|
|
1070
1167
|
}
|
|
1071
|
-
function createRouter(engine, state, vite, webRoot, wsToken, version, protocolVersion, capabilities, ownerKind) {
|
|
1168
|
+
function createRouter(engine, state, vite, webRoot, wsToken, version, protocolVersion, capabilities, ownerKind, channel) {
|
|
1072
1169
|
return (req, res) => {
|
|
1073
1170
|
const url = req.url || "/";
|
|
1074
1171
|
const path = url.split("?")[0];
|
|
@@ -1087,6 +1184,7 @@ function createRouter(engine, state, vite, webRoot, wsToken, version, protocolVe
|
|
|
1087
1184
|
protocolVersion,
|
|
1088
1185
|
capabilities,
|
|
1089
1186
|
ownerKind,
|
|
1187
|
+
channel,
|
|
1090
1188
|
// Discovery requires this proof; public health checks retain their useful 200 response.
|
|
1091
1189
|
owner: tokenMatches(header2(req, "x-tokmon-token"), wsToken)
|
|
1092
1190
|
});
|
|
@@ -1117,10 +1215,11 @@ function createRouter(engine, state, vite, webRoot, wsToken, version, protocolVe
|
|
|
1117
1215
|
async function startWebServer(opts) {
|
|
1118
1216
|
const state = { config: opts.config };
|
|
1119
1217
|
const tz = tzFor(state.config);
|
|
1120
|
-
const version = appVersion();
|
|
1218
|
+
const version = opts.version ?? appVersion();
|
|
1121
1219
|
const protocolVersion = opts.protocolVersion ?? TOKMON_PROTOCOL_VERSION;
|
|
1122
1220
|
const capabilities = opts.capabilities ?? TOKMON_CAPABILITIES;
|
|
1123
1221
|
const ownerKind = opts.ownerKind ?? "cli";
|
|
1222
|
+
const channel = resolveDaemonChannel(opts.channel);
|
|
1124
1223
|
const summaryIntervalMs = summaryIntervalFor(state.config);
|
|
1125
1224
|
const billingIntervalMs = billingIntervalFor(state.config);
|
|
1126
1225
|
const wsToken = opts.wsToken ?? randomBytes(32).toString("base64url");
|
|
@@ -1134,7 +1233,7 @@ async function startWebServer(opts) {
|
|
|
1134
1233
|
let closeWsRpc = null;
|
|
1135
1234
|
try {
|
|
1136
1235
|
if (isDevMode()) vite = await createViteDevServer(server, log);
|
|
1137
|
-
const webRoot = vite ? null : findWebRoot();
|
|
1236
|
+
const webRoot = vite ? null : opts.webRoot ?? findWebRoot();
|
|
1138
1237
|
if (!vite && !webRoot) log(" \u26A0 no dashboard available \u2014 see the page for build/dev instructions");
|
|
1139
1238
|
engine = createDataEngine({ version, config: state.config, tz, summaryIntervalMs, billingIntervalMs, resolved });
|
|
1140
1239
|
server.addListener("request", createRouter(
|
|
@@ -1146,7 +1245,8 @@ async function startWebServer(opts) {
|
|
|
1146
1245
|
version,
|
|
1147
1246
|
protocolVersion,
|
|
1148
1247
|
capabilities,
|
|
1149
|
-
ownerKind
|
|
1248
|
+
ownerKind,
|
|
1249
|
+
channel
|
|
1150
1250
|
));
|
|
1151
1251
|
closeWsRpc = await mountWsRpc(server, { engine, state });
|
|
1152
1252
|
const bindHost = state.config.allowNetworkAccess ? NETWORK_HOST : LOOPBACK_HOST;
|