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.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/dist/{bootstrap-ink-5LQMSU4B.js → bootstrap-ink-IMT3S5HU.js} +120 -9
  3. package/dist/{chunk-KCY53RUE.js → chunk-44ZSQEOS.js} +2 -2
  4. package/dist/{chunk-JK3U54G7.js → chunk-6ZEW2M6P.js} +167 -39
  5. package/dist/{chunk-E2YXYU73.js → chunk-HP5UZCXP.js} +29 -0
  6. package/dist/{chunk-QM5E5RJZ.js → chunk-J3JW3RFP.js} +10 -7
  7. package/dist/{chunk-IWKSA64G.js → chunk-LCAHTRGP.js} +15 -281
  8. package/dist/{chunk-AMD4PXDG.js → chunk-LKYOWBD4.js} +15 -15
  9. package/dist/{chunk-QOA6KOJQ.js → chunk-M7XMV36F.js} +1 -1
  10. package/dist/chunk-WUK3MQUB.js +249 -0
  11. package/dist/{cli-command-HXKWB6IP.js → cli-command-DCENRCO3.js} +6 -6
  12. package/dist/cli.js +6 -6
  13. package/dist/{config-2PXUENQL.js → config-HXFJTNLM.js} +5 -1
  14. package/dist/{daemon-JCQF645I.js → daemon-6Y4O7NXS.js} +36 -14
  15. package/dist/daemon-handle-GVCYVDPO.js +10 -0
  16. package/dist/server-GXXOWUVZ.js +11 -0
  17. package/dist/web/assets/{Area-DgGVYaNT.js → Area-CMXvOw33.js} +1 -1
  18. package/dist/web/assets/{analytics-D5EvgF4d.js → analytics-DqdZXOJL.js} +2 -2
  19. package/dist/web/assets/{breakdown-CGdJkAHx.js → breakdown-kOSSJ3mI.js} +1 -1
  20. package/dist/web/assets/{chart-D4KHj2-J.js → chart-cZoLgpmG.js} +1 -1
  21. package/dist/web/assets/{explore-B2QnGzfO.js → explore-vcsM8K9I.js} +1 -1
  22. package/dist/web/assets/index-BErCXT7f.css +1 -0
  23. package/dist/web/assets/index-DH3dOnbg.js +83 -0
  24. package/dist/web/assets/{models-D7w7JJCX.js → models-B5ikm83_.js} +2 -2
  25. package/dist/web/assets/{overview-BPz0j397.js → overview-DD1kP8CP.js} +2 -2
  26. package/dist/web/assets/{panel-Da3rkvLu.js → panel-DTQBwExW.js} +1 -1
  27. package/dist/web/assets/{primitives-B8eGD8S_.js → primitives-C36qJlA2.js} +1 -1
  28. package/dist/web/assets/settings-sheet-_i2zTQjf.js +1 -0
  29. package/dist/web/assets/{share-sheet-BXrrMoLk.js → share-sheet-BLHmrFbm.js} +2 -2
  30. package/dist/web/assets/{timeline-CiGI4EmA.js → timeline-B4uG-Emo.js} +1 -1
  31. package/dist/web/assets/{use-dialog-trap-Dhs8W7s1.js → use-dialog-trap-bfaXEMz9.js} +1 -1
  32. package/dist/web/index.html +2 -2
  33. package/package.json +1 -1
  34. package/dist/chunk-SMPY52EV.js +0 -125
  35. package/dist/daemon-handle-2GVZT55B.js +0 -10
  36. package/dist/server-BJOXAGRI.js +0 -11
  37. package/dist/web/assets/index-C0txIEHK.js +0 -83
  38. package/dist/web/assets/index-NnKaHxPO.css +0 -1
  39. 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-E2YXYU73.js";
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
- async function probeHealth(url, token, version, timeoutMs = 500) {
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, version, timeoutMs) {
179
- if (!lock || lock.state !== "ready" || lock.version !== version || !isAlive(lock.pid)) return null;
180
- return await probeHealth(lock.url, lock.wsToken, version, timeoutMs) ? lock : null;
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-E2YXYU73.js";
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 hasThreadSpawn(path) {
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
- return buffer.subarray(0, bytesRead).includes("thread_spawn");
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
- async function detectReplaySecond(path) {
1744
- if (!await hasThreadSpawn(path)) return null;
1745
- let first = null;
1746
- const input = createReadStream2(path);
1747
- input.on("error", () => {
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
- const replaySecond = await detectReplaySecond(path);
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-QM5E5RJZ.js";
8
+ } from "./chunk-J3JW3RFP.js";
9
9
  import {
10
- appVersion
11
- } from "./chunk-SMPY52EV.js";
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, version) {
50
- const lock = await verifyLock(readLock(opts), version);
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, version, timeoutMs) {
54
+ async function retireIncompatibleDaemon(opts, protocolVersion, timeoutMs) {
55
55
  const lock = readLock(opts);
56
- if (!lock || lock.state !== "ready" || lock.version === version || lock.pid === process.pid || !isAlive(lock.pid)) return;
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.version,
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 version = appVersion();
82
+ const protocolVersion = TOKMON_PROTOCOL_VERSION;
83
83
  const timeoutMs = opts.timeoutMs ?? HANDSHAKE_TIMEOUT_MS;
84
- const existing = await attach(opts, version);
84
+ const existing = await attach(opts, protocolVersion);
85
85
  if (existing) return existing;
86
- await retireIncompatibleDaemon(opts, version, timeoutMs);
87
- const upgraded = await attach(opts, version);
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, version).then((found) => {
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.version !== version) continue;
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-IWKSA64G.js";
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";