tokmon 0.27.0 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/dist/{bootstrap-ink-3YNJFKCW.js → bootstrap-ink-L7QSJBMS.js} +142 -12
  3. package/dist/{chunk-4CY725NY.js → chunk-3RTWFGGD.js} +1 -1
  4. package/dist/{chunk-AMD4PXDG.js → chunk-4HU4EI5T.js} +1 -1
  5. package/dist/{chunk-RTGMDE7U.js → chunk-CVKCVHS7.js} +143 -81
  6. package/dist/{chunk-VGYBQH5I.js → chunk-FMP3P2WV.js} +2 -2
  7. package/dist/{chunk-E2YXYU73.js → chunk-HP5UZCXP.js} +29 -0
  8. package/dist/{chunk-OWPJUG4C.js → chunk-S33XIUAW.js} +11 -11
  9. package/dist/{chunk-QM5E5RJZ.js → chunk-XDA5RJST.js} +1 -1
  10. package/dist/{cli-command-U7RZ2GTD.js → cli-command-75LP4IDS.js} +5 -5
  11. package/dist/cli.js +6 -6
  12. package/dist/{config-2PXUENQL.js → config-HXFJTNLM.js} +5 -1
  13. package/dist/{daemon-PQWKWRC6.js → daemon-CL4FJW26.js} +5 -5
  14. package/dist/{daemon-handle-2GVZT55B.js → daemon-handle-QQLJE46Y.js} +3 -3
  15. package/dist/server-EUO7CWYH.js +11 -0
  16. package/dist/web/assets/{Area-Tx1mUuqM.js → Area-CMXvOw33.js} +1 -1
  17. package/dist/web/assets/{analytics-CAzklhXc.js → analytics-DqdZXOJL.js} +2 -2
  18. package/dist/web/assets/{breakdown-BoPnOv7Y.js → breakdown-kOSSJ3mI.js} +1 -1
  19. package/dist/web/assets/{chart-jK7A9m7R.js → chart-cZoLgpmG.js} +1 -1
  20. package/dist/web/assets/{explore-e8wtwk7s.js → explore-vcsM8K9I.js} +1 -1
  21. package/dist/web/assets/index-BErCXT7f.css +1 -0
  22. package/dist/web/assets/index-DH3dOnbg.js +83 -0
  23. package/dist/web/assets/{models-_4IZNDxW.js → models-B5ikm83_.js} +2 -2
  24. package/dist/web/assets/{overview-BBOfd_Fd.js → overview-DD1kP8CP.js} +2 -2
  25. package/dist/web/assets/{panel-CstB4hPm.js → panel-DTQBwExW.js} +1 -1
  26. package/dist/web/assets/{primitives-G9ALG7ih.js → primitives-C36qJlA2.js} +1 -1
  27. package/dist/web/assets/settings-sheet-_i2zTQjf.js +1 -0
  28. package/dist/web/assets/{share-sheet-BwmnebRB.js → share-sheet-BLHmrFbm.js} +2 -2
  29. package/dist/web/assets/{timeline-DkYWH6XW.js → timeline-B4uG-Emo.js} +1 -1
  30. package/dist/web/assets/{use-dialog-trap-Jci2xaMX.js → use-dialog-trap-bfaXEMz9.js} +1 -1
  31. package/dist/web/index.html +2 -2
  32. package/package.json +1 -1
  33. package/dist/server-5WIWS27N.js +0 -11
  34. package/dist/web/assets/index-C8oe6EfM.js +0 -83
  35. package/dist/web/assets/index-NnKaHxPO.css +0 -1
  36. package/dist/web/assets/settings-sheet-rq6vbYXM.js +0 -1
package/README.md CHANGED
@@ -105,7 +105,7 @@ tokmon serve --port 8080
105
105
  tokmon serve --no-open # don't auto-open the browser
106
106
  ```
107
107
 
108
- It binds to `127.0.0.1` by default and reads the same local daemon state — nothing leaves your machine. The browser connects directly over a same-origin WebSocket; dashboard URLs have no token or login step. Optional LAN access can be enabled in settings, with an explicit unsafe-access warning, and takes effect after the daemon restarts. The dashboard renders instantly from a cached snapshot, then streams live updates and goes idle when no tab is open. Press `R` or use the visible **Refresh** control to update it. Filter by provider, model, account, and period, flip between dark and light, and export any panel — or a summary card — as a PNG with the **Share** button.
108
+ It binds to `127.0.0.1` by default and reads the same local daemon state — nothing leaves your machine. The browser connects directly over a same-origin WebSocket; dashboard URLs have no token or login step. Optional LAN access can be enabled in settings, with an explicit unsafe-access warning, and takes effect after the daemon restarts. When publishing the dashboard through an ingress or reverse proxy, add each exact DNS name to **Allowed hosts** in settings (for example, `tokmon.example.com`); IP hosts remain allowed in LAN mode. The dashboard renders instantly from a cached snapshot, then streams live updates and goes idle when no tab is open. Press `R` or use the visible **Refresh** control to update it. Filter by provider, model, account, and period, flip between dark and light, and export any panel — or a summary card — as a PNG with the **Share** button.
109
109
 
110
110
  ### Overview
111
111
 
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createDaemonRpcClient
4
- } from "./chunk-4CY725NY.js";
4
+ } from "./chunk-3RTWFGGD.js";
5
5
  import {
6
6
  accountsByProvider,
7
7
  buildAccounts,
8
8
  fetchPeak,
9
9
  modelColor
10
- } from "./chunk-VGYBQH5I.js";
10
+ } from "./chunk-FMP3P2WV.js";
11
11
  import {
12
12
  PROVIDERS,
13
13
  PROVIDER_ORDER,
@@ -15,6 +15,7 @@ import {
15
15
  col,
16
16
  currency,
17
17
  detectProviders,
18
+ formatAgo,
18
19
  formatResetAt,
19
20
  resetAt,
20
21
  resolveTimezone,
@@ -23,7 +24,7 @@ import {
23
24
  time,
24
25
  tokens,
25
26
  withTimeout
26
- } from "./chunk-RTGMDE7U.js";
27
+ } from "./chunk-CVKCVHS7.js";
27
28
  import {
28
29
  COLOR_PALETTE,
29
30
  DEFAULTS,
@@ -32,13 +33,14 @@ import {
32
33
  getTrackedAccountRows,
33
34
  isValidTimezone,
34
35
  loadConfig,
36
+ normalizeAllowedHost,
35
37
  normalizeConfig,
36
38
  pickAccentColor,
37
39
  redactEmail,
38
40
  sanitizeTyped,
39
41
  saveConfigSync,
40
42
  snapshotCacheFile
41
- } from "./chunk-E2YXYU73.js";
43
+ } from "./chunk-HP5UZCXP.js";
42
44
  import {
43
45
  glyphs
44
46
  } from "./chunk-RF4GGQGM.js";
@@ -219,6 +221,12 @@ import { memo as memo2 } from "react";
219
221
  import { Box as Box2, Text as Text2 } from "ink";
220
222
 
221
223
  // src/ui/provider-card.logic.ts
224
+ var BILLING_STALE_AFTER_MS = 30 * 6e4;
225
+ function billingStaleLabel(billingUpdatedAt, now) {
226
+ if (billingUpdatedAt == null || billingUpdatedAt <= 0) return null;
227
+ if (now - billingUpdatedAt < BILLING_STALE_AFTER_MS) return null;
228
+ return `as of ${formatAgo(billingUpdatedAt, now)}`;
229
+ }
222
230
  function normalizePlan(plan) {
223
231
  if (plan == null) return null;
224
232
  const t = plan.trim();
@@ -253,7 +261,9 @@ function estimateCardHeights(groups, stats) {
253
261
  if (meta.hasUsage) h += 1;
254
262
  const multi = g.accounts.length > 1;
255
263
  g.accounts.forEach((a, i) => {
256
- const metricRows = stats.get(a.id)?.billing?.metrics.length || 1;
264
+ const s = stats.get(a.id);
265
+ const staleRow = s?.billing && !s.billing.error && s.billing.metrics.length > 0 && billingStaleLabel(s.billing.asOfMs ?? s.billingUpdatedAt, Date.now()) ? 1 : 0;
266
+ const metricRows = (s?.billing?.metrics.length || 1) + staleRow;
257
267
  h += metricRows + (multi ? 1 : 0) + (multi && i > 0 ? 1 : 0);
258
268
  });
259
269
  }
@@ -460,6 +470,7 @@ function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz
460
470
  const barW = Math.max(10, Math.min(46, inner - labelW - resetW - 5));
461
471
  return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: items.map(({ account, s }, idx) => {
462
472
  const billing = s?.billing;
473
+ const staleLabel = billing && !billing.error && billing.metrics.length > 0 ? billingStaleLabel(billing.asOfMs ?? s?.billingUpdatedAt, Date.now()) : null;
463
474
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: showName && idx > 0 ? 1 : 0, children: [
464
475
  showName && (() => {
465
476
  const rowPlan = showRowPlans ? normalizePlan(billing?.plan) : null;
@@ -482,7 +493,16 @@ function LimitsBlock({ items, inner, showRowPlans, privacyMode, resetDisplay, tz
482
493
  !billing ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
483
494
  "Fetching",
484
495
  glyphs().ellipsis
485
- ] }) : billing.error ? /* @__PURE__ */ jsx2(Text2, { color: "red", wrap: "truncate-end", children: privacyMode ? redactEmail(billing.error) : billing.error }) : billing.metrics.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "No data" }) : billing.metrics.map((m, i) => /* @__PURE__ */ jsx2(MetricRow, { m, color: account.color, barW, labelW, resetW, resetDisplay, tz }, `${m.label}${i}`))
496
+ ] }) : billing.error ? /* @__PURE__ */ jsx2(Text2, { color: "red", wrap: "truncate-end", children: privacyMode ? redactEmail(billing.error) : billing.error }) : billing.metrics.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "No data" }) : billing.metrics.map((m, i) => /* @__PURE__ */ jsx2(MetricRow, { m, color: account.color, barW, labelW, resetW, resetDisplay, tz }, `${m.label}${i}`)),
497
+ staleLabel && /* @__PURE__ */ jsxs2(Text2, { color: "yellow", dimColor: true, children: [
498
+ glyphs().warn,
499
+ " ",
500
+ staleLabel,
501
+ " ",
502
+ glyphs().middot,
503
+ " refreshing",
504
+ glyphs().ellipsis
505
+ ] })
486
506
  ] }, account.id);
487
507
  }) });
488
508
  }
@@ -570,7 +590,7 @@ import { Box as Box4, Text as Text4 } from "ink";
570
590
  import { memo as memo3 } from "react";
571
591
  import { Box as Box3, Text as Text3 } from "ink";
572
592
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
573
- var GENERAL_ROWS = 10;
593
+ var GENERAL_ROWS = 11;
574
594
  var SETTINGS_TABS = ["general", "providers", "accounts"];
575
595
  var SETTINGS_TAB_LABELS = {
576
596
  general: "General",
@@ -585,6 +605,9 @@ var SettingsView = memo3(function SettingsView2({
585
605
  tzEdit,
586
606
  tzCaret,
587
607
  tzError,
608
+ allowedHostsEdit,
609
+ allowedHostsCaret,
610
+ allowedHostsError,
588
611
  resolvedTz,
589
612
  accountForm,
590
613
  activeAccountId,
@@ -593,6 +616,7 @@ var SettingsView = memo3(function SettingsView2({
593
616
  }) {
594
617
  if (accountForm) return /* @__PURE__ */ jsx3(AccountFormView, { form: accountForm, accounts: config.accounts });
595
618
  const editingTz = tzEdit !== null;
619
+ const editingAllowedHosts = allowedHostsEdit !== null;
596
620
  const tzDisplay = config.timezone === null ? `System (${resolvedTz})` : config.timezone;
597
621
  const tabFocused = cursor < 0;
598
622
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, children: [
@@ -666,8 +690,17 @@ var SettingsView = memo3(function SettingsView2({
666
690
  ] })
667
691
  ] }),
668
692
  /* @__PURE__ */ jsx3(Row, { cursor, idx: 8, label: "Network access", children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: config.allowNetworkAccess ? "red" : "green", children: config.allowNetworkAccess ? "LAN (unsafe)" : "local only" }) }),
693
+ /* @__PURE__ */ jsx3(Row, { cursor, idx: 9, label: "Allowed hosts", children: editingAllowedHosts ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
694
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "[" }),
695
+ /* @__PURE__ */ jsx3(CaretText, { value: allowedHostsEdit ?? "", caret: allowedHostsCaret, color: "cyan" }),
696
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "]" })
697
+ ] }) : /* @__PURE__ */ jsx3(Text3, { bold: true, color: "yellow", children: config.allowedHosts.join(", ") || "none" }) }),
698
+ cursor === 9 && allowedHostsError && /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
699
+ " ",
700
+ allowedHostsError
701
+ ] }),
669
702
  config.allowNetworkAccess && /* @__PURE__ */ jsx3(Text3, { color: "red", children: " Warning: dashboard data and settings will be exposed to your local network after daemon restart." }),
670
- /* @__PURE__ */ jsxs3(Row, { cursor, idx: 9, label: "Reset times", children: [
703
+ /* @__PURE__ */ jsxs3(Row, { cursor, idx: 10, label: "Reset times", children: [
671
704
  /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
672
705
  glyphs().caretL,
673
706
  " "
@@ -760,6 +793,12 @@ var SettingsView = memo3(function SettingsView2({
760
793
  " Enter save ",
761
794
  glyphs().middot,
762
795
  " Esc cancel"
796
+ ] }) : editingAllowedHosts ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
797
+ "comma-separated exact DNS names ",
798
+ glyphs().middot,
799
+ " Enter save ",
800
+ glyphs().middot,
801
+ " Esc cancel"
763
802
  ] }) : activeTab === "providers" ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
764
803
  glyphs().arrowU,
765
804
  glyphs().arrowD,
@@ -1711,7 +1750,8 @@ function toStatsMap(snapshot, accounts) {
1711
1750
  out.set(account.id, {
1712
1751
  account,
1713
1752
  dashboard: wa?.dashboard ?? null,
1714
- billing: wa?.billing ?? null
1753
+ billing: wa?.billing ?? null,
1754
+ billingUpdatedAt: wa?.billingUpdatedAt ?? null
1715
1755
  });
1716
1756
  }
1717
1757
  return out;
@@ -1972,6 +2012,56 @@ function handleTimezoneEditor(input, key, editor, text, updateConfig) {
1972
2012
  if (clean) text.insert(clean);
1973
2013
  }
1974
2014
  }
2015
+ function handleAllowedHostsEditor(input, key, editor, text, updateConfig) {
2016
+ const { value, setValue, setError, setCaret, valueRef, caretRef } = editor;
2017
+ if (value === null) return;
2018
+ if (key.escape) {
2019
+ setValue(null);
2020
+ setError(null);
2021
+ return;
2022
+ }
2023
+ if (key.return) {
2024
+ const parts = value.split(",").map((part) => part.trim()).filter(Boolean);
2025
+ const normalized = parts.map(normalizeAllowedHost);
2026
+ if (normalized.some((host) => host === null)) {
2027
+ setError("Use exact DNS names without a scheme, port, path, or wildcard");
2028
+ return;
2029
+ }
2030
+ updateConfig((config) => ({ ...config, allowedHosts: [...new Set(normalized)] }));
2031
+ setValue(null);
2032
+ setError(null);
2033
+ return;
2034
+ }
2035
+ if (key.leftArrow) {
2036
+ setCaret((caret) => clampCaret(caret - 1, value.length));
2037
+ return;
2038
+ }
2039
+ if (key.rightArrow) {
2040
+ setCaret((caret) => clampCaret(caret + 1, value.length));
2041
+ return;
2042
+ }
2043
+ if (key.ctrl && input === "a") {
2044
+ setCaret(0);
2045
+ return;
2046
+ }
2047
+ if (key.ctrl && input === "e") {
2048
+ setCaret(value.length);
2049
+ return;
2050
+ }
2051
+ if (key.backspace || key.delete) {
2052
+ const result = spliceBackspace(valueRef.current, caretRef.current);
2053
+ valueRef.current = result.value;
2054
+ caretRef.current = result.caret;
2055
+ setValue(result.value);
2056
+ setCaret(result.caret);
2057
+ setError(null);
2058
+ return;
2059
+ }
2060
+ if (text.isPrintable(input, key)) {
2061
+ const clean = sanitizeTyped(input);
2062
+ if (clean) text.insert(clean);
2063
+ }
2064
+ }
1975
2065
  function handleSearchEditor(input, key, table, text) {
1976
2066
  const { search, setSearch, setSearchMode, setSearchCaret, searchValueRef, searchCaretRef } = table;
1977
2067
  if (key.return || key.escape) {
@@ -2044,6 +2134,8 @@ function handleSettings(input, key, ctx) {
2044
2134
  setCursor(-1);
2045
2135
  timezoneEditor.setValue(null);
2046
2136
  timezoneEditor.setError(null);
2137
+ ctx.allowedHostsEditor.setValue(null);
2138
+ ctx.allowedHostsEditor.setError(null);
2047
2139
  };
2048
2140
  const rowCount = tab === "general" ? GENERAL_ROWS : tab === "providers" ? PROVIDER_ORDER.length : trackedAccounts.length + 1;
2049
2141
  if (key.tab) {
@@ -2174,7 +2266,14 @@ function handleSettings(input, key, ctx) {
2174
2266
  updateConfig((current) => ({ ...current, allowNetworkAccess: !current.allowNetworkAccess }));
2175
2267
  return;
2176
2268
  }
2177
- if (index === 9 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
2269
+ if (index === 9 && inputKey.return) {
2270
+ const initial = config.allowedHosts.join(", ");
2271
+ context.allowedHostsEditor.setValue(initial);
2272
+ context.allowedHostsEditor.setCaret(initial.length);
2273
+ context.allowedHostsEditor.setError(null);
2274
+ return;
2275
+ }
2276
+ if (index === 10 && (inputKey.leftArrow || inputKey.rightArrow || inputKey.return)) {
2178
2277
  updateConfig((current) => ({ ...current, resetDisplay: current.resetDisplay === "relative" ? "absolute" : "relative" }));
2179
2278
  }
2180
2279
  }
@@ -2337,7 +2436,7 @@ function handleTerminalFocusInput(input) {
2337
2436
  return true;
2338
2437
  }
2339
2438
  function isRefreshAllShortcut(input, mode) {
2340
- return (input === "r" || input === "R") && !mode.unavailable && !mode.showPicker && !mode.editingAccount && !mode.editingTimezone && !mode.editingSearch;
2439
+ return (input === "r" || input === "R") && !mode.unavailable && !mode.showPicker && !mode.editingAccount && !mode.editingTimezone && !mode.editingAllowedHosts && !mode.editingSearch;
2341
2440
  }
2342
2441
  function handleOnboarding(input, key, ctx) {
2343
2442
  const { onboarding, global } = ctx;
@@ -2366,11 +2465,13 @@ function handleOnboarding(input, key, ctx) {
2366
2465
  function handleKey(input, key, ctx) {
2367
2466
  const editingAccount = ctx.settings.show && ctx.accountEditor.form !== null;
2368
2467
  const editingTimezone = ctx.settings.show && ctx.timezoneEditor.value !== null;
2468
+ const editingAllowedHosts = ctx.settings.show && ctx.allowedHostsEditor.value !== null;
2369
2469
  const editingSearch = ctx.table.tab === 1 && ctx.table.searchMode;
2370
2470
  if (isRefreshAllShortcut(input, {
2371
2471
  showPicker: ctx.onboarding.show,
2372
2472
  editingAccount,
2373
2473
  editingTimezone,
2474
+ editingAllowedHosts,
2374
2475
  editingSearch,
2375
2476
  unavailable: !ctx.global.configReady
2376
2477
  })) {
@@ -2389,6 +2490,10 @@ function handleKey(input, key, ctx) {
2389
2490
  handleTimezoneEditor(input, key, ctx.timezoneEditor, ctx.textInput, ctx.global.updateConfig);
2390
2491
  return;
2391
2492
  }
2493
+ if (editingAllowedHosts) {
2494
+ handleAllowedHostsEditor(input, key, ctx.allowedHostsEditor, ctx.textInput, ctx.global.updateConfig);
2495
+ return;
2496
+ }
2392
2497
  if (editingSearch) {
2393
2498
  handleSearchEditor(input, key, ctx.table, ctx.textInput);
2394
2499
  return;
@@ -3122,6 +3227,9 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3122
3227
  const [tzEdit, setTzEdit] = useState10(null);
3123
3228
  const [tzError, setTzError] = useState10(null);
3124
3229
  const [tzCaret, setTzCaret] = useState10(0);
3230
+ const [allowedHostsEdit, setAllowedHostsEdit] = useState10(null);
3231
+ const [allowedHostsError, setAllowedHostsError] = useState10(null);
3232
+ const [allowedHostsCaret, setAllowedHostsCaret] = useState10(0);
3125
3233
  const [searchCaret, setSearchCaret] = useState10(0);
3126
3234
  const [onboardSel, setOnboardSel] = useState10(null);
3127
3235
  const [onboardCursor, setOnboardCursor] = useState10(0);
@@ -3149,6 +3257,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3149
3257
  const dashPageCountRef = useRef8(1);
3150
3258
  const tzValueRef = useRef8("");
3151
3259
  const tzCaretRef = useRef8(0);
3260
+ const allowedHostsValueRef = useRef8("");
3261
+ const allowedHostsCaretRef = useRef8(0);
3152
3262
  const searchValueRef = useRef8("");
3153
3263
  const searchCaretRef = useRef8(0);
3154
3264
  const accountsKey = useMemo2(() => accounts.map(acctKey).join("|"), [accounts]);
@@ -3254,6 +3364,8 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3254
3364
  dashPageCountRef.current = dashPageCount;
3255
3365
  tzValueRef.current = tzEdit ?? "";
3256
3366
  tzCaretRef.current = clampCaret(tzCaret, (tzEdit ?? "").length);
3367
+ allowedHostsValueRef.current = allowedHostsEdit ?? "";
3368
+ allowedHostsCaretRef.current = clampCaret(allowedHostsCaret, (allowedHostsEdit ?? "").length);
3257
3369
  searchValueRef.current = search;
3258
3370
  searchCaretRef.current = clampCaret(searchCaret, search.length);
3259
3371
  const isPrintable = (input, key) => !!input && !key.ctrl && !key.meta && !isPasteInput(input);
@@ -3271,6 +3383,13 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3271
3383
  setTzEdit(r.value);
3272
3384
  setTzCaret(r.caret);
3273
3385
  setTzError(null);
3386
+ } else if (showSettings && allowedHostsEdit !== null) {
3387
+ const r = spliceInsert(allowedHostsValueRef.current, allowedHostsCaretRef.current, text);
3388
+ allowedHostsValueRef.current = r.value;
3389
+ allowedHostsCaretRef.current = r.caret;
3390
+ setAllowedHostsEdit(r.value);
3391
+ setAllowedHostsCaret(r.caret);
3392
+ setAllowedHostsError(null);
3274
3393
  } else if (tab === 1 && searchMode) {
3275
3394
  const r = spliceInsert(searchValueRef.current, searchCaretRef.current, text);
3276
3395
  searchValueRef.current = r.value;
@@ -3441,7 +3560,7 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3441
3560
  if (webStartingRef.current) return;
3442
3561
  webStartingRef.current = true;
3443
3562
  try {
3444
- const { startWebServer } = await import("./server-5WIWS27N.js");
3563
+ const { startWebServer } = await import("./server-EUO7CWYH.js");
3445
3564
  const ctrl = await startWebServer({ config: cfg, log: false });
3446
3565
  webRef.current = ctrl;
3447
3566
  openUrl(ctrl.url);
@@ -3515,6 +3634,14 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3515
3634
  valueRef: tzValueRef,
3516
3635
  caretRef: tzCaretRef
3517
3636
  },
3637
+ allowedHostsEditor: {
3638
+ value: allowedHostsEdit,
3639
+ setValue: setAllowedHostsEdit,
3640
+ setError: setAllowedHostsError,
3641
+ setCaret: setAllowedHostsCaret,
3642
+ valueRef: allowedHostsValueRef,
3643
+ caretRef: allowedHostsCaretRef
3644
+ },
3518
3645
  textInput: { isPrintable, insert: insertText },
3519
3646
  settings: {
3520
3647
  show: showSettings,
@@ -3654,6 +3781,9 @@ function App({ interval: cliInterval, initialConfig, baseUrl = null, mode = "deg
3654
3781
  tzEdit,
3655
3782
  tzCaret,
3656
3783
  tzError,
3784
+ allowedHostsEdit,
3785
+ allowedHostsCaret,
3786
+ allowedHostsError,
3657
3787
  resolvedTz: tz,
3658
3788
  accountForm,
3659
3789
  activeAccountId: cfg.activeAccountId,
@@ -3,7 +3,7 @@ import {
3
3
  TOKMON_WS_METHODS,
4
4
  TOKMON_WS_PATH,
5
5
  TokmonRpcGroup
6
- } from "./chunk-RTGMDE7U.js";
6
+ } from "./chunk-CVKCVHS7.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";
@@ -5,7 +5,7 @@ import {
5
5
  readLock,
6
6
  reclaimDeadLock,
7
7
  verifyLock
8
- } from "./chunk-QM5E5RJZ.js";
8
+ } from "./chunk-XDA5RJST.js";
9
9
  import {
10
10
  appVersion
11
11
  } from "./chunk-SMPY52EV.js";