tokmon 0.28.0 → 0.28.2
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-5LQMSU4B.js → bootstrap-ink-IMT3S5HU.js} +120 -9
- package/dist/{chunk-KCY53RUE.js → chunk-44ZSQEOS.js} +2 -2
- package/dist/{chunk-JK3U54G7.js → chunk-6ZEW2M6P.js} +167 -39
- package/dist/{chunk-E2YXYU73.js → chunk-HP5UZCXP.js} +29 -0
- package/dist/{chunk-QM5E5RJZ.js → chunk-J3JW3RFP.js} +10 -7
- package/dist/{chunk-IWKSA64G.js → chunk-LCAHTRGP.js} +15 -281
- package/dist/{chunk-AMD4PXDG.js → chunk-LKYOWBD4.js} +15 -15
- package/dist/{chunk-QOA6KOJQ.js → chunk-M7XMV36F.js} +1 -1
- package/dist/chunk-WUK3MQUB.js +249 -0
- package/dist/{cli-command-HXKWB6IP.js → cli-command-DCENRCO3.js} +6 -6
- package/dist/cli.js +6 -6
- package/dist/{config-2PXUENQL.js → config-HXFJTNLM.js} +5 -1
- package/dist/{daemon-JCQF645I.js → daemon-6Y4O7NXS.js} +36 -14
- package/dist/daemon-handle-GVCYVDPO.js +10 -0
- package/dist/server-GXXOWUVZ.js +11 -0
- package/dist/web/assets/{Area-DgGVYaNT.js → Area-CMXvOw33.js} +1 -1
- package/dist/web/assets/{analytics-D5EvgF4d.js → analytics-DqdZXOJL.js} +2 -2
- package/dist/web/assets/{breakdown-CGdJkAHx.js → breakdown-kOSSJ3mI.js} +1 -1
- package/dist/web/assets/{chart-D4KHj2-J.js → chart-cZoLgpmG.js} +1 -1
- package/dist/web/assets/{explore-B2QnGzfO.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-D7w7JJCX.js → models-B5ikm83_.js} +2 -2
- package/dist/web/assets/{overview-BPz0j397.js → overview-DD1kP8CP.js} +2 -2
- package/dist/web/assets/{panel-Da3rkvLu.js → panel-DTQBwExW.js} +1 -1
- package/dist/web/assets/{primitives-B8eGD8S_.js → primitives-C36qJlA2.js} +1 -1
- package/dist/web/assets/settings-sheet-_i2zTQjf.js +1 -0
- package/dist/web/assets/{share-sheet-BXrrMoLk.js → share-sheet-BLHmrFbm.js} +2 -2
- package/dist/web/assets/{timeline-CiGI4EmA.js → timeline-B4uG-Emo.js} +1 -1
- package/dist/web/assets/{use-dialog-trap-Dhs8W7s1.js → use-dialog-trap-bfaXEMz9.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-SMPY52EV.js +0 -125
- package/dist/daemon-handle-2GVZT55B.js +0 -10
- package/dist/server-BJOXAGRI.js +0 -11
- package/dist/web/assets/index-C0txIEHK.js +0 -83
- package/dist/web/assets/index-NnKaHxPO.css +0 -1
- package/dist/web/assets/settings-sheet-D6tnWqmt.js +0 -1
|
@@ -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,
|
|
@@ -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";
|
|
@@ -23,7 +23,7 @@ function isLoopbackUrl(value) {
|
|
|
23
23
|
}
|
|
24
24
|
function validLock(value) {
|
|
25
25
|
const lock = value;
|
|
26
|
-
return !!lock && typeof lock.pid === "number" && Number.isInteger(lock.pid) && lock.pid > 0 && typeof lock.port === "number" && Number.isInteger(lock.port) && lock.port >= 0 && lock.port <= 65535 && typeof lock.url === "string" && (lock.state === "starting" || isLoopbackUrl(lock.url)) && typeof lock.wsToken === "string" && lock.wsToken.length >= 32 && typeof lock.version === "string" && typeof lock.startedAt === "number" && typeof lock.ownerId === "string" && lock.ownerId.length >= 32 && (lock.state === "starting" || lock.state === "ready");
|
|
26
|
+
return !!lock && typeof lock.pid === "number" && Number.isInteger(lock.pid) && lock.pid > 0 && typeof lock.port === "number" && Number.isInteger(lock.port) && lock.port >= 0 && lock.port <= 65535 && typeof lock.url === "string" && (lock.state === "starting" || isLoopbackUrl(lock.url)) && typeof lock.wsToken === "string" && lock.wsToken.length >= 32 && typeof lock.version === "string" && typeof lock.protocolVersion === "number" && Number.isSafeInteger(lock.protocolVersion) && lock.protocolVersion >= 1 && Array.isArray(lock.capabilities) && lock.capabilities.every((capability) => typeof capability === "string") && (lock.ownerKind === "cli" || lock.ownerKind === "desktop") && typeof lock.startedAt === "number" && typeof lock.ownerId === "string" && lock.ownerId.length >= 32 && (lock.state === "starting" || lock.state === "ready");
|
|
27
27
|
}
|
|
28
28
|
function readLock(opts = {}) {
|
|
29
29
|
try {
|
|
@@ -161,7 +161,10 @@ function isAlive(pid) {
|
|
|
161
161
|
return err.code === "EPERM";
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
-
|
|
164
|
+
function sameCapabilities(actual, expected) {
|
|
165
|
+
return Array.isArray(actual) && actual.length === expected.length && actual.every((capability, index) => capability === expected[index]);
|
|
166
|
+
}
|
|
167
|
+
async function probeHealth(url, token, expected = {}, timeoutMs = 500) {
|
|
165
168
|
if (!isLoopbackUrl(url) || !token) return false;
|
|
166
169
|
try {
|
|
167
170
|
const res = await fetch(`${url.replace(/\/+$/, "")}/healthz`, {
|
|
@@ -170,14 +173,14 @@ async function probeHealth(url, token, version, timeoutMs = 500) {
|
|
|
170
173
|
});
|
|
171
174
|
if (!res.ok) return false;
|
|
172
175
|
const body = await res.json();
|
|
173
|
-
return body.ok === true && body.owner === true && (version === void 0 || body.version === version);
|
|
176
|
+
return body.ok === true && body.owner === true && (expected.version === void 0 || body.version === expected.version) && (expected.protocolVersion === void 0 || body.protocolVersion === expected.protocolVersion) && (expected.capabilities === void 0 || sameCapabilities(body.capabilities, expected.capabilities)) && (expected.ownerKind === void 0 || body.ownerKind === expected.ownerKind);
|
|
174
177
|
} catch {
|
|
175
178
|
return false;
|
|
176
179
|
}
|
|
177
180
|
}
|
|
178
|
-
async function verifyLock(lock,
|
|
179
|
-
if (!lock || lock.state !== "ready" || lock.
|
|
180
|
-
return await probeHealth(lock.url, lock.wsToken,
|
|
181
|
+
async function verifyLock(lock, protocolVersion, timeoutMs) {
|
|
182
|
+
if (!lock || lock.state !== "ready" || lock.protocolVersion !== protocolVersion || !isAlive(lock.pid)) return null;
|
|
183
|
+
return await probeHealth(lock.url, lock.wsToken, lock, timeoutMs) ? lock : null;
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
export {
|
|
@@ -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;
|
|
@@ -739,242 +739,6 @@ function coalesceTables(list) {
|
|
|
739
739
|
return mergeTables(list);
|
|
740
740
|
}
|
|
741
741
|
|
|
742
|
-
// src/rpc/contract.ts
|
|
743
|
-
import { Schema } from "effect";
|
|
744
|
-
import * as Rpc from "effect/unstable/rpc/Rpc";
|
|
745
|
-
import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
|
746
|
-
var TOKMON_WS_PATH = "/ws";
|
|
747
|
-
var TOKMON_PROTOCOL_VERSION = 2;
|
|
748
|
-
var TOKMON_CAPABILITIES = ["config-cas", "config-revision"];
|
|
749
|
-
var TOKMON_WS_METHODS = {
|
|
750
|
-
getConfig: "tokmon.getConfig",
|
|
751
|
-
setConfig: "tokmon.setConfig",
|
|
752
|
-
refresh: "tokmon.refresh",
|
|
753
|
-
browseFs: "tokmon.browseFs",
|
|
754
|
-
snapshot: "tokmon.snapshot",
|
|
755
|
-
config: "tokmon.config"
|
|
756
|
-
};
|
|
757
|
-
var RefreshScopeSchema = Schema.Literals([
|
|
758
|
-
"all",
|
|
759
|
-
"summary",
|
|
760
|
-
"table",
|
|
761
|
-
"billing",
|
|
762
|
-
"peak"
|
|
763
|
-
]);
|
|
764
|
-
var ProviderIdSchema = Schema.Literals(PROVIDER_IDS);
|
|
765
|
-
var NonNegativeIntegerSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
|
|
766
|
-
var PositiveFiniteSchema = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(1));
|
|
767
|
-
var AccountSchema = Schema.Struct({
|
|
768
|
-
id: Schema.String,
|
|
769
|
-
providerId: ProviderIdSchema,
|
|
770
|
-
name: Schema.String,
|
|
771
|
-
homeDir: Schema.String,
|
|
772
|
-
color: Schema.optionalKey(Schema.String)
|
|
773
|
-
});
|
|
774
|
-
var ConfigSchema = Schema.Struct({
|
|
775
|
-
revision: NonNegativeIntegerSchema,
|
|
776
|
-
interval: PositiveFiniteSchema,
|
|
777
|
-
billingInterval: PositiveFiniteSchema,
|
|
778
|
-
clearScreen: Schema.Boolean,
|
|
779
|
-
privacyMode: Schema.Boolean,
|
|
780
|
-
privacyToggleKey: Schema.String,
|
|
781
|
-
timezone: Schema.NullOr(Schema.String),
|
|
782
|
-
accounts: Schema.Array(AccountSchema),
|
|
783
|
-
activeAccountId: Schema.NullOr(Schema.String),
|
|
784
|
-
disabledProviders: Schema.Array(ProviderIdSchema),
|
|
785
|
-
onboarded: Schema.Boolean,
|
|
786
|
-
dashboardLayout: Schema.Literals(["grid", "single"]),
|
|
787
|
-
defaultFocus: Schema.Literals(["all", "last"]),
|
|
788
|
-
ascii: Schema.Literals(["auto", "on", "off"]),
|
|
789
|
-
allowNetworkAccess: Schema.Boolean,
|
|
790
|
-
resetDisplay: Schema.Literals(["relative", "absolute"]),
|
|
791
|
-
knownProviders: Schema.Array(ProviderIdSchema)
|
|
792
|
-
});
|
|
793
|
-
var ProtocolInfoSchema = Schema.Struct({
|
|
794
|
-
version: Schema.Literal(TOKMON_PROTOCOL_VERSION),
|
|
795
|
-
// Capabilities are additive within a protocol version. Older clients must
|
|
796
|
-
// preserve forward compatibility when a newer daemon advertises one they do
|
|
797
|
-
// not understand yet.
|
|
798
|
-
capabilities: Schema.Array(Schema.String)
|
|
799
|
-
});
|
|
800
|
-
var ConfigStateSchema = Schema.Struct({
|
|
801
|
-
protocol: ProtocolInfoSchema,
|
|
802
|
-
config: ConfigSchema
|
|
803
|
-
});
|
|
804
|
-
var ConfigUpdateRequestSchema = Schema.Struct({
|
|
805
|
-
expectedRevision: NonNegativeIntegerSchema,
|
|
806
|
-
config: ConfigSchema
|
|
807
|
-
});
|
|
808
|
-
var ConfigUpdateConflictSchema = Schema.Struct({
|
|
809
|
-
kind: Schema.Literal("conflict"),
|
|
810
|
-
state: ConfigStateSchema
|
|
811
|
-
});
|
|
812
|
-
var ConfigPersistenceFailureSchema = Schema.Struct({
|
|
813
|
-
kind: Schema.Literal("persistence"),
|
|
814
|
-
message: Schema.String
|
|
815
|
-
});
|
|
816
|
-
var FsEntrySchema = Schema.Struct({
|
|
817
|
-
name: Schema.String,
|
|
818
|
-
path: Schema.String,
|
|
819
|
-
dir: Schema.Boolean
|
|
820
|
-
});
|
|
821
|
-
var FsListingSchema = Schema.Struct({
|
|
822
|
-
path: Schema.String,
|
|
823
|
-
parent: Schema.NullOr(Schema.String),
|
|
824
|
-
entries: Schema.Array(FsEntrySchema)
|
|
825
|
-
});
|
|
826
|
-
var UsageSummarySchema = Schema.Struct({
|
|
827
|
-
cost: Schema.Finite,
|
|
828
|
-
tokens: Schema.Finite,
|
|
829
|
-
input: Schema.Finite,
|
|
830
|
-
cacheRead: Schema.Finite,
|
|
831
|
-
cacheSavings: Schema.Finite
|
|
832
|
-
});
|
|
833
|
-
var ModelDetailSchema = Schema.Struct({
|
|
834
|
-
name: Schema.String,
|
|
835
|
-
input: Schema.Finite,
|
|
836
|
-
output: Schema.Finite,
|
|
837
|
-
cacheCreate: Schema.Finite,
|
|
838
|
-
cacheRead: Schema.Finite,
|
|
839
|
-
cacheSavings: Schema.Finite,
|
|
840
|
-
cost: Schema.Finite,
|
|
841
|
-
count: Schema.Finite
|
|
842
|
-
});
|
|
843
|
-
var TableRowSchema = Schema.Struct({
|
|
844
|
-
label: Schema.String,
|
|
845
|
-
models: Schema.Array(Schema.String),
|
|
846
|
-
input: Schema.Finite,
|
|
847
|
-
output: Schema.Finite,
|
|
848
|
-
cacheCreate: Schema.Finite,
|
|
849
|
-
cacheRead: Schema.Finite,
|
|
850
|
-
cacheSavings: Schema.Finite,
|
|
851
|
-
total: Schema.Finite,
|
|
852
|
-
cost: Schema.Finite,
|
|
853
|
-
count: Schema.Finite,
|
|
854
|
-
breakdown: Schema.Array(ModelDetailSchema)
|
|
855
|
-
});
|
|
856
|
-
var DashboardDataSchema = Schema.Struct({
|
|
857
|
-
today: UsageSummarySchema,
|
|
858
|
-
week: UsageSummarySchema,
|
|
859
|
-
month: UsageSummarySchema,
|
|
860
|
-
burnRate: Schema.Finite,
|
|
861
|
-
series: Schema.Array(Schema.Finite)
|
|
862
|
-
});
|
|
863
|
-
var TableDataSchema = Schema.Struct({
|
|
864
|
-
daily: Schema.Array(TableRowSchema),
|
|
865
|
-
weekly: Schema.Array(TableRowSchema),
|
|
866
|
-
monthly: Schema.Array(TableRowSchema)
|
|
867
|
-
});
|
|
868
|
-
var MetricFormatSchema = Schema.Union([
|
|
869
|
-
Schema.Struct({ kind: Schema.Literal("percent") }),
|
|
870
|
-
Schema.Struct({ kind: Schema.Literal("dollars"), currency: Schema.optionalKey(Schema.String) }),
|
|
871
|
-
Schema.Struct({ kind: Schema.Literal("count"), suffix: Schema.optionalKey(Schema.String) })
|
|
872
|
-
]);
|
|
873
|
-
var MetricSchema = Schema.Struct({
|
|
874
|
-
label: Schema.String,
|
|
875
|
-
used: Schema.Finite,
|
|
876
|
-
limit: Schema.NullOr(Schema.Finite),
|
|
877
|
-
format: MetricFormatSchema,
|
|
878
|
-
resetsAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
879
|
-
primary: Schema.optionalKey(Schema.Boolean)
|
|
880
|
-
});
|
|
881
|
-
var BillingResultSchema = Schema.Struct({
|
|
882
|
-
plan: Schema.NullOr(Schema.String),
|
|
883
|
-
metrics: Schema.Array(MetricSchema),
|
|
884
|
-
error: Schema.NullOr(Schema.String),
|
|
885
|
-
email: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
886
|
-
displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
887
|
-
activity: Schema.optionalKey(Schema.NullOr(Schema.Struct({
|
|
888
|
-
series: Schema.Array(Schema.Finite),
|
|
889
|
-
summary: Schema.String
|
|
890
|
-
}))),
|
|
891
|
-
modelSpend: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
|
|
892
|
-
name: Schema.String,
|
|
893
|
-
usd: Schema.Finite,
|
|
894
|
-
requests: Schema.Finite
|
|
895
|
-
})))),
|
|
896
|
-
asOfMs: Schema.optionalKey(Schema.Finite)
|
|
897
|
-
});
|
|
898
|
-
var WebAccountSchema = Schema.Struct({
|
|
899
|
-
id: Schema.String,
|
|
900
|
-
providerId: ProviderIdSchema,
|
|
901
|
-
name: Schema.String,
|
|
902
|
-
color: Schema.String,
|
|
903
|
-
homeDir: Schema.NullOr(Schema.String),
|
|
904
|
-
hasUsage: Schema.Boolean,
|
|
905
|
-
hasBilling: Schema.Boolean,
|
|
906
|
-
email: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
907
|
-
displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
908
|
-
plan: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
909
|
-
dashboard: Schema.NullOr(DashboardDataSchema),
|
|
910
|
-
table: Schema.NullOr(TableDataSchema),
|
|
911
|
-
billing: Schema.NullOr(BillingResultSchema),
|
|
912
|
-
summaryState: Schema.Literals(["pending", "ready", "error"]),
|
|
913
|
-
billingState: Schema.Literals(["pending", "ready", "error"]),
|
|
914
|
-
tableState: Schema.Literals(["pending", "ready", "error"]),
|
|
915
|
-
summaryUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
|
|
916
|
-
billingUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema)),
|
|
917
|
-
tableUpdatedAt: Schema.optionalKey(Schema.NullOr(NonNegativeIntegerSchema))
|
|
918
|
-
});
|
|
919
|
-
var WebSnapshotSchema = Schema.Struct({
|
|
920
|
-
version: Schema.String,
|
|
921
|
-
generatedAt: NonNegativeIntegerSchema,
|
|
922
|
-
tz: Schema.String,
|
|
923
|
-
intervalMs: PositiveFiniteSchema,
|
|
924
|
-
billingIntervalMs: Schema.optionalKey(PositiveFiniteSchema),
|
|
925
|
-
providers: Schema.Array(Schema.Struct({
|
|
926
|
-
id: ProviderIdSchema,
|
|
927
|
-
name: Schema.String,
|
|
928
|
-
color: Schema.String
|
|
929
|
-
})),
|
|
930
|
-
accounts: Schema.Array(WebAccountSchema),
|
|
931
|
-
seeded: Schema.Boolean,
|
|
932
|
-
peak: Schema.NullOr(Schema.Struct({
|
|
933
|
-
state: Schema.Literals(["peak", "off-peak", "weekend"]),
|
|
934
|
-
label: Schema.String,
|
|
935
|
-
// The upstream clock is only normalized to a finite number; do not make
|
|
936
|
-
// the wire contract narrower than the producer's declared type.
|
|
937
|
-
minutesUntilChange: Schema.NullOr(Schema.Finite),
|
|
938
|
-
changesAt: Schema.optionalKey(Schema.NullOr(Schema.String))
|
|
939
|
-
}))
|
|
940
|
-
});
|
|
941
|
-
var EmptyPayloadSchema = Schema.Struct({});
|
|
942
|
-
var GetConfigRpc = Rpc.make(TOKMON_WS_METHODS.getConfig, {
|
|
943
|
-
payload: EmptyPayloadSchema,
|
|
944
|
-
success: ConfigStateSchema
|
|
945
|
-
});
|
|
946
|
-
var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
|
|
947
|
-
payload: ConfigUpdateRequestSchema,
|
|
948
|
-
success: ConfigStateSchema,
|
|
949
|
-
error: Schema.Union([ConfigUpdateConflictSchema, ConfigPersistenceFailureSchema])
|
|
950
|
-
});
|
|
951
|
-
var RefreshRpc = Rpc.make(TOKMON_WS_METHODS.refresh, {
|
|
952
|
-
payload: Schema.Struct({ scope: RefreshScopeSchema }),
|
|
953
|
-
success: Schema.Void
|
|
954
|
-
});
|
|
955
|
-
var BrowseFsRpc = Rpc.make(TOKMON_WS_METHODS.browseFs, {
|
|
956
|
-
payload: Schema.Struct({ path: Schema.String }),
|
|
957
|
-
success: FsListingSchema
|
|
958
|
-
});
|
|
959
|
-
var SnapshotRpc = Rpc.make(TOKMON_WS_METHODS.snapshot, {
|
|
960
|
-
payload: EmptyPayloadSchema,
|
|
961
|
-
success: WebSnapshotSchema,
|
|
962
|
-
stream: true
|
|
963
|
-
});
|
|
964
|
-
var ConfigRpc = Rpc.make(TOKMON_WS_METHODS.config, {
|
|
965
|
-
payload: EmptyPayloadSchema,
|
|
966
|
-
success: ConfigStateSchema,
|
|
967
|
-
stream: true
|
|
968
|
-
});
|
|
969
|
-
var TokmonRpcGroup = RpcGroup.make(
|
|
970
|
-
GetConfigRpc,
|
|
971
|
-
SetConfigRpc,
|
|
972
|
-
RefreshRpc,
|
|
973
|
-
BrowseFsRpc,
|
|
974
|
-
SnapshotRpc,
|
|
975
|
-
ConfigRpc
|
|
976
|
-
);
|
|
977
|
-
|
|
978
742
|
// src/shared/format.ts
|
|
979
743
|
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
980
744
|
function formatCurrency(value, opts = {}) {
|
|
@@ -1726,13 +1490,14 @@ function timestampSecond(value) {
|
|
|
1726
1490
|
const ts = timestampMs(value);
|
|
1727
1491
|
return ts === null ? null : new Date(ts).toISOString().slice(0, 19);
|
|
1728
1492
|
}
|
|
1729
|
-
async function
|
|
1493
|
+
async function hasForkedHistory(path) {
|
|
1730
1494
|
let handle = null;
|
|
1731
1495
|
try {
|
|
1732
1496
|
handle = await openFile(path, "r");
|
|
1733
1497
|
const buffer = Buffer.alloc(16 * 1024);
|
|
1734
1498
|
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
1735
|
-
|
|
1499
|
+
const prefix = buffer.subarray(0, bytesRead).toString("utf8");
|
|
1500
|
+
return prefix.includes("thread_spawn") && /"forked_from_id"\s*:\s*"[^"]+"/.test(prefix);
|
|
1736
1501
|
} catch {
|
|
1737
1502
|
return false;
|
|
1738
1503
|
} finally {
|
|
@@ -1740,32 +1505,11 @@ async function hasThreadSpawn(path) {
|
|
|
1740
1505
|
});
|
|
1741
1506
|
}
|
|
1742
1507
|
}
|
|
1743
|
-
|
|
1744
|
-
if (
|
|
1745
|
-
|
|
1746
|
-
const
|
|
1747
|
-
|
|
1748
|
-
});
|
|
1749
|
-
const rl = createInterface2({ input, crlfDelay: Infinity });
|
|
1750
|
-
try {
|
|
1751
|
-
for await (const rawLine of rl) {
|
|
1752
|
-
if (!rawLine.includes("token_count")) continue;
|
|
1753
|
-
try {
|
|
1754
|
-
const line = rawLine.charCodeAt(0) === 65279 ? rawLine.slice(1) : rawLine;
|
|
1755
|
-
const obj = JSON.parse(line);
|
|
1756
|
-
if ((obj?.payload?.type ?? obj?.type) !== "token_count") continue;
|
|
1757
|
-
const info = obj?.payload?.info;
|
|
1758
|
-
if (!normalizeUsage(info?.last_token_usage) && !normalizeUsage(info?.total_token_usage)) continue;
|
|
1759
|
-
const second = timestampSecond(obj.timestamp ?? obj?.payload?.timestamp);
|
|
1760
|
-
if (!second) continue;
|
|
1761
|
-
if (!first) first = second;
|
|
1762
|
-
else return first === second ? second : null;
|
|
1763
|
-
} catch {
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
} catch {
|
|
1767
|
-
}
|
|
1768
|
-
return null;
|
|
1508
|
+
function isLiveTaskStart(obj) {
|
|
1509
|
+
if ((obj?.payload?.type ?? obj?.type) !== "task_started") return false;
|
|
1510
|
+
const eventSecond = timestampSecond(obj?.timestamp ?? obj?.payload?.timestamp);
|
|
1511
|
+
const startedSecond = timestampSecond(obj?.payload?.started_at);
|
|
1512
|
+
return eventSecond !== null && eventSecond === startedSecond;
|
|
1769
1513
|
}
|
|
1770
1514
|
function findUsage(obj) {
|
|
1771
1515
|
return normalizeUsage(obj?.usage) ?? normalizeUsage(obj?.payload?.usage) ?? normalizeUsage(obj?.payload?.info?.usage) ?? normalizeUsage(obj?.result?.usage) ?? normalizeUsage(obj?.response?.usage) ?? normalizeUsage(obj?.token_usage) ?? normalizeUsage(obj);
|
|
@@ -1778,19 +1522,22 @@ async function parseFile2(path) {
|
|
|
1778
1522
|
let model = "gpt-5";
|
|
1779
1523
|
let prevTotal = null;
|
|
1780
1524
|
let prevSig = null;
|
|
1781
|
-
|
|
1782
|
-
let skipReplay = replaySecond !== null;
|
|
1525
|
+
let skipReplay = await hasForkedHistory(path);
|
|
1783
1526
|
const input = createReadStream2(path);
|
|
1784
1527
|
input.on("error", () => {
|
|
1785
1528
|
});
|
|
1786
1529
|
const rl = createInterface2({ input, crlfDelay: Infinity });
|
|
1787
1530
|
try {
|
|
1788
1531
|
for await (const rawLine of rl) {
|
|
1789
|
-
if (!rawLine.includes("token_count") && !rawLine.includes("turn_context") && !rawLine.includes('"usage"') && !rawLine.includes("input_tokens") && !rawLine.includes("prompt_tokens")) continue;
|
|
1532
|
+
if (!rawLine.includes("token_count") && !rawLine.includes("task_started") && !rawLine.includes("turn_context") && !rawLine.includes('"usage"') && !rawLine.includes("input_tokens") && !rawLine.includes("prompt_tokens")) continue;
|
|
1790
1533
|
try {
|
|
1791
1534
|
const line = rawLine.charCodeAt(0) === 65279 ? rawLine.slice(1) : rawLine;
|
|
1792
1535
|
const obj = JSON.parse(line);
|
|
1793
1536
|
const payloadType = obj?.payload?.type ?? obj?.type;
|
|
1537
|
+
if (skipReplay) {
|
|
1538
|
+
if (isLiveTaskStart(obj)) skipReplay = false;
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1794
1541
|
if (payloadType === "turn_context") {
|
|
1795
1542
|
const m2 = extractModel(obj);
|
|
1796
1543
|
if (typeof m2 === "string" && m2.trim()) model = m2;
|
|
@@ -1826,14 +1573,6 @@ async function parseFile2(path) {
|
|
|
1826
1573
|
const total = normalizeUsage(info?.total_token_usage);
|
|
1827
1574
|
const last = normalizeUsage(info?.last_token_usage);
|
|
1828
1575
|
const tsValue = obj.timestamp ?? obj?.payload?.timestamp;
|
|
1829
|
-
if (skipReplay && replaySecond) {
|
|
1830
|
-
const second = timestampSecond(tsValue);
|
|
1831
|
-
if (second === replaySecond) {
|
|
1832
|
-
if (total) prevTotal = total;
|
|
1833
|
-
continue;
|
|
1834
|
-
}
|
|
1835
|
-
if (second) skipReplay = false;
|
|
1836
|
-
}
|
|
1837
1576
|
const sig = eventSig(last, total);
|
|
1838
1577
|
if (sig === prevSig) continue;
|
|
1839
1578
|
prevSig = sig;
|
|
@@ -4621,11 +4360,6 @@ async function detectProviders() {
|
|
|
4621
4360
|
}
|
|
4622
4361
|
|
|
4623
4362
|
export {
|
|
4624
|
-
TOKMON_WS_PATH,
|
|
4625
|
-
TOKMON_PROTOCOL_VERSION,
|
|
4626
|
-
TOKMON_CAPABILITIES,
|
|
4627
|
-
TOKMON_WS_METHODS,
|
|
4628
|
-
TokmonRpcGroup,
|
|
4629
4363
|
withTimeout,
|
|
4630
4364
|
systemTimezone,
|
|
4631
4365
|
resolveTimezone,
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
readLock,
|
|
6
6
|
reclaimDeadLock,
|
|
7
7
|
verifyLock
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-J3JW3RFP.js";
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
TOKMON_PROTOCOL_VERSION
|
|
11
|
+
} from "./chunk-WUK3MQUB.js";
|
|
12
12
|
|
|
13
13
|
// src/client/daemon-handle.ts
|
|
14
14
|
import { spawn } from "child_process";
|
|
@@ -37,7 +37,7 @@ function runtimeExecArgv(entry, override) {
|
|
|
37
37
|
function parseHandshake(line) {
|
|
38
38
|
try {
|
|
39
39
|
const value = JSON.parse(line);
|
|
40
|
-
return value?.ready === 1 && typeof value.url === "string" && typeof value.wsToken === "string" && typeof value.version === "string" ? value : null;
|
|
40
|
+
return value?.ready === 1 && typeof value.url === "string" && typeof value.wsToken === "string" && typeof value.version === "string" && typeof value.protocolVersion === "number" && Array.isArray(value.capabilities) && value.capabilities.every((capability) => typeof capability === "string") && (value.ownerKind === "cli" || value.ownerKind === "desktop") ? value : null;
|
|
41
41
|
} catch {
|
|
42
42
|
return null;
|
|
43
43
|
}
|
|
@@ -46,18 +46,18 @@ function connected(url) {
|
|
|
46
46
|
return { kind: "spawned", baseUrl: url, stop: () => {
|
|
47
47
|
} };
|
|
48
48
|
}
|
|
49
|
-
async function attach(opts,
|
|
50
|
-
const lock = await verifyLock(readLock(opts),
|
|
49
|
+
async function attach(opts, protocolVersion) {
|
|
50
|
+
const lock = await verifyLock(readLock(opts), protocolVersion);
|
|
51
51
|
return lock ? connected(lock.url) : null;
|
|
52
52
|
}
|
|
53
53
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
54
|
-
async function retireIncompatibleDaemon(opts,
|
|
54
|
+
async function retireIncompatibleDaemon(opts, protocolVersion, timeoutMs) {
|
|
55
55
|
const lock = readLock(opts);
|
|
56
|
-
if (!lock || lock.state !== "ready" || lock.
|
|
56
|
+
if (!lock || lock.state !== "ready" || lock.protocolVersion === protocolVersion || lock.ownerKind === "desktop" || lock.pid === process.pid || !isAlive(lock.pid)) return;
|
|
57
57
|
const authenticated = await probeHealth(
|
|
58
58
|
lock.url,
|
|
59
59
|
lock.wsToken,
|
|
60
|
-
lock
|
|
60
|
+
lock,
|
|
61
61
|
Math.min(1e3, timeoutMs)
|
|
62
62
|
);
|
|
63
63
|
if (!authenticated) return;
|
|
@@ -79,12 +79,12 @@ async function retireIncompatibleDaemon(opts, version, timeoutMs) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
async function attachOrSpawn(opts = {}) {
|
|
82
|
-
const
|
|
82
|
+
const protocolVersion = TOKMON_PROTOCOL_VERSION;
|
|
83
83
|
const timeoutMs = opts.timeoutMs ?? HANDSHAKE_TIMEOUT_MS;
|
|
84
|
-
const existing = await attach(opts,
|
|
84
|
+
const existing = await attach(opts, protocolVersion);
|
|
85
85
|
if (existing) return existing;
|
|
86
|
-
await retireIncompatibleDaemon(opts,
|
|
87
|
-
const upgraded = await attach(opts,
|
|
86
|
+
await retireIncompatibleDaemon(opts, protocolVersion, timeoutMs);
|
|
87
|
+
const upgraded = await attach(opts, protocolVersion);
|
|
88
88
|
if (upgraded) return upgraded;
|
|
89
89
|
const entry = opts.entry ?? process.argv[1];
|
|
90
90
|
if (!entry) return degraded();
|
|
@@ -117,7 +117,7 @@ async function attachOrSpawn(opts = {}) {
|
|
|
117
117
|
resolve(handle);
|
|
118
118
|
};
|
|
119
119
|
const tryAttach = (final = false) => {
|
|
120
|
-
void attach(opts,
|
|
120
|
+
void attach(opts, protocolVersion).then((found) => {
|
|
121
121
|
if (found) {
|
|
122
122
|
finish(found);
|
|
123
123
|
return;
|
|
@@ -143,7 +143,7 @@ async function attachOrSpawn(opts = {}) {
|
|
|
143
143
|
const line = stdout.slice(0, newline).trim();
|
|
144
144
|
stdout = stdout.slice(newline + 1);
|
|
145
145
|
const handshake = parseHandshake(line);
|
|
146
|
-
if (!handshake || handshake.
|
|
146
|
+
if (!handshake || handshake.protocolVersion !== protocolVersion) continue;
|
|
147
147
|
tryAttach();
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
TOKMON_WS_METHODS,
|
|
4
4
|
TOKMON_WS_PATH,
|
|
5
5
|
TokmonRpcGroup
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WUK3MQUB.js";
|
|
7
7
|
|
|
8
8
|
// src/client/daemon-rpc-client.ts
|
|
9
9
|
import { Cause, Context, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, Schedule, Stream } from "effect";
|