@stjbrown/agent-knowledge 0.1.0-beta.4 → 0.1.0-beta.6

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.
@@ -3,17 +3,21 @@ import { createRequire as __janetCreateRequire } from "node:module";
3
3
  const require = __janetCreateRequire(import.meta.url);
4
4
  import {
5
5
  availableModels,
6
- completeOnboarding,
7
- loadSettings,
8
- normalizeModelSelection,
9
- rememberModel
10
- } from "./chunk-PBRTNSWS.js";
6
+ normalizeModelSelection
7
+ } from "./chunk-XWSZZDL3.js";
11
8
  import {
12
9
  GREETING,
13
10
  bootJanet,
11
+ completeOnboarding,
12
+ formatObservabilityStatus,
14
13
  getAuthStorage,
15
- messageText
16
- } from "./chunk-H4H6S4AV.js";
14
+ loadSettings,
15
+ messageText,
16
+ rememberModel,
17
+ rememberObservability,
18
+ resolveObservabilityConfig,
19
+ safeObservabilityEndpoint
20
+ } from "./chunk-2XPSIYWN.js";
17
21
 
18
22
  // src/tui/index.ts
19
23
  import {
@@ -25,7 +29,8 @@ import {
25
29
  SelectList,
26
30
  Spacer,
27
31
  TUI,
28
- Text
32
+ Text,
33
+ matchesKey
29
34
  } from "@earendil-works/pi-tui";
30
35
 
31
36
  // src/tui/activity.ts
@@ -72,6 +77,80 @@ function toolErrorLabel(result) {
72
77
  return `Tool error: ${detail.slice(0, 140)}`;
73
78
  }
74
79
 
80
+ // src/tui/interrupt.ts
81
+ function createInterruptController(actions, options = {}) {
82
+ const doublePressMs = options.doublePressMs ?? 800;
83
+ const now = options.now ?? Date.now;
84
+ let lastCtrlC;
85
+ const cancelRun = () => {
86
+ if (!actions.isRunning()) return "ignored";
87
+ actions.abortRun();
88
+ actions.notify("cancelled");
89
+ return "cancelled";
90
+ };
91
+ return {
92
+ handleCtrlC() {
93
+ const pressedAt = now();
94
+ if (lastCtrlC !== void 0 && pressedAt - lastCtrlC < doublePressMs) {
95
+ actions.notify("exit");
96
+ actions.exit();
97
+ return "exit";
98
+ }
99
+ lastCtrlC = pressedAt;
100
+ const cancelled = cancelRun();
101
+ if (cancelled !== "ignored") return cancelled;
102
+ if (actions.hasInput()) {
103
+ actions.clearInput();
104
+ actions.notify("cleared");
105
+ return "cleared";
106
+ }
107
+ actions.notify("exit-hint");
108
+ return "exit-hint";
109
+ },
110
+ handleEscape() {
111
+ return cancelRun();
112
+ }
113
+ };
114
+ }
115
+
116
+ // src/tui/traces.ts
117
+ function duration(span) {
118
+ if (!span.endedAt) return "running";
119
+ const elapsed = Math.max(0, span.endedAt.getTime() - span.startedAt.getTime());
120
+ return elapsed >= 1e3 ? `${(elapsed / 1e3).toFixed(1)}s` : `${elapsed}ms`;
121
+ }
122
+ function traceStatus(span) {
123
+ if (span.error) return "error";
124
+ return span.endedAt ? "ok" : "running";
125
+ }
126
+ function formatTraceTree(spans) {
127
+ const byParent = /* @__PURE__ */ new Map();
128
+ const ids = new Set(spans.map((span) => span.spanId));
129
+ for (const span of spans) {
130
+ const parent = span.parentSpanId && ids.has(span.parentSpanId) ? span.parentSpanId : null;
131
+ const children = byParent.get(parent) ?? [];
132
+ children.push(span);
133
+ byParent.set(parent, children);
134
+ }
135
+ for (const children of byParent.values()) {
136
+ children.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime());
137
+ }
138
+ const lines = [];
139
+ const visited = /* @__PURE__ */ new Set();
140
+ const visit = (span, depth) => {
141
+ if (visited.has(span.spanId)) return;
142
+ visited.add(span.spanId);
143
+ const status = traceStatus(span);
144
+ const marker = status === "error" ? "\u2717" : status === "running" ? "\u2026" : "\u2713";
145
+ lines.push(
146
+ `${" ".repeat(depth)}${marker} ${span.name} \xB7 ${span.spanType} \xB7 ${duration(span)}`
147
+ );
148
+ for (const child of byParent.get(span.spanId) ?? []) visit(child, depth + 1);
149
+ };
150
+ for (const root of byParent.get(null) ?? []) visit(root, 0);
151
+ return lines;
152
+ }
153
+
75
154
  // src/tui/theme.ts
76
155
  import chalk from "chalk";
77
156
  var c = {
@@ -113,16 +192,6 @@ var markdownTheme = {
113
192
 
114
193
  // src/tui/index.ts
115
194
  var OAUTH_PROVIDERS = ["anthropic", "openai-codex"];
116
- var JanetEditor = class extends Editor {
117
- onCtrlC;
118
- handleInput(data) {
119
- if (data === "") {
120
- this.onCtrlC?.();
121
- return;
122
- }
123
- super.handleInput(data);
124
- }
125
- };
126
195
  var HELP_TEXT = `Commands:
127
196
  /models Pick a model from a list (arrow keys)
128
197
  /model [provider/id] Open the picker, or switch directly by id
@@ -130,9 +199,13 @@ var HELP_TEXT = `Commands:
130
199
  Log in; OpenAI mode is browser or device
131
200
  /logout <provider> Remove stored credentials for a provider
132
201
  /auth Show which providers are authenticated
202
+ /observability Configure opt-in tracing
203
+ /traces Browse recent local traces
204
+ /cancel Cancel the active run
133
205
  /help This help
134
206
  /quit Exit (double Ctrl+C also works)
135
207
 
208
+ While Janet is working, Esc or Ctrl+C cancels the active run.
136
209
  Anything else is a message to Janet.`;
137
210
  function resolveAnswer(q, text) {
138
211
  if (!q.options?.length) return text;
@@ -153,7 +226,10 @@ function resolveAnswer(q, text) {
153
226
  return pick(text);
154
227
  }
155
228
  async function runTui(opts) {
156
- const { controller, session, paths, herdrDetach } = await bootJanet({ ...opts, interactive: true });
229
+ const { controller, session, paths, herdrDetach, observability } = await bootJanet({
230
+ ...opts,
231
+ interactive: true
232
+ });
157
233
  const persistedModel = process.env["JANET_MODEL"] || loadSettings().defaultModelId;
158
234
  const presetModel = persistedModel ? normalizeModelSelection(persistedModel, availableModels()) : void 0;
159
235
  if (!session.model.hasSelection() && presetModel) {
@@ -163,7 +239,7 @@ async function runTui(opts) {
163
239
  const ui = new TUI(terminal);
164
240
  const chat = new Container();
165
241
  const status = new Text("", 1, 0);
166
- const editor = new JanetEditor(ui, editorTheme);
242
+ const editor = new Editor(ui, editorTheme);
167
243
  const loader = new Loader(ui, c.accent, c.dim, "Janet is thinking\u2026");
168
244
  ui.addChild(chat);
169
245
  ui.addChild(new Spacer(1));
@@ -176,11 +252,15 @@ async function runTui(opts) {
176
252
  let pendingInput = null;
177
253
  let activeSelect = null;
178
254
  let active = null;
255
+ let cancelRequested = false;
179
256
  const activeTools = /* @__PURE__ */ new Map();
180
257
  const updateStatus = () => {
181
258
  const model = session.model.hasSelection() ? session.model.get() : "no model \u2014 /model <id>";
182
- const state = pendingInput ? "enter the requested value" : pendingQuestion || activeSelect ? "answer Janet's question" : pendingApproval ? "awaiting approval" : running ? "working" : "idle";
183
- status.setText(c.dim(`${paths.projectPath} \xB7 `) + c.accent(model) + c.dim(` \xB7 ${state}`));
259
+ const tracing = observability.status.enabled ? c.dim(` \xB7 trace:${observability.status.capture}`) : "";
260
+ const state = pendingInput ? "enter the requested value" : pendingQuestion || activeSelect ? "answer Janet's question" : pendingApproval ? "awaiting approval" : cancelRequested ? "cancelling" : running ? "working \xB7 Esc/Ctrl+C cancels" : "idle";
261
+ status.setText(
262
+ c.dim(`${paths.projectPath} \xB7 `) + c.accent(model) + c.dim(` \xB7 ${state}`) + tracing
263
+ );
184
264
  ui.requestRender();
185
265
  };
186
266
  const appendToChat = (comp) => {
@@ -227,6 +307,7 @@ async function runTui(opts) {
227
307
  switch (event.type) {
228
308
  case "agent_start":
229
309
  running = true;
310
+ cancelRequested = false;
230
311
  active = null;
231
312
  activeTools.clear();
232
313
  loader.setMessage("Janet is thinking\u2026");
@@ -328,6 +409,7 @@ async function runTui(opts) {
328
409
  break;
329
410
  case "agent_end":
330
411
  running = false;
412
+ cancelRequested = false;
331
413
  activeTools.clear();
332
414
  loader.setMessage("Janet is thinking\u2026");
333
415
  if (event.reason !== "suspended") pendingQuestion = null;
@@ -337,20 +419,84 @@ async function runTui(opts) {
337
419
  }
338
420
  };
339
421
  const unsubscribe = session.subscribe(onEvent);
422
+ let removeInputListener = () => {
423
+ };
424
+ let sigintHandler;
340
425
  const shutdown = async (code) => {
426
+ removeInputListener();
427
+ if (sigintHandler) process.off("SIGINT", sigintHandler);
341
428
  unsubscribe();
342
429
  herdrDetach();
343
430
  ui.stop();
431
+ await observability.flush().catch(() => {
432
+ });
344
433
  await controller.destroy().catch(() => {
345
434
  });
346
435
  process.exit(code);
347
436
  };
437
+ const notifyInterrupt = (result) => {
438
+ switch (result) {
439
+ case "cancelled":
440
+ addLine(c.dim(" Cancelling the active run\u2026"));
441
+ break;
442
+ case "cleared":
443
+ break;
444
+ case "exit":
445
+ break;
446
+ case "exit-hint":
447
+ addLine(c.dim(" Press Ctrl+C again to quit."));
448
+ break;
449
+ }
450
+ updateStatus();
451
+ };
452
+ const abortActiveRun = () => {
453
+ if (cancelRequested) return;
454
+ cancelRequested = true;
455
+ pendingApproval = null;
456
+ pendingQuestion = null;
457
+ activeTools.clear();
458
+ if (activeSelect) {
459
+ chat.removeChild(activeSelect);
460
+ activeSelect = null;
461
+ }
462
+ ui.setFocus(editor);
463
+ loader.setMessage("Cancelling\u2026");
464
+ session.abort();
465
+ };
466
+ const interrupts = createInterruptController({
467
+ isRunning: () => running,
468
+ hasInput: () => editor.getText().length > 0,
469
+ abortRun: abortActiveRun,
470
+ clearInput: () => {
471
+ editor.setText("");
472
+ ui.requestRender();
473
+ },
474
+ exit: () => {
475
+ void shutdown(0);
476
+ },
477
+ notify: notifyInterrupt
478
+ });
479
+ removeInputListener = ui.addInputListener((data) => {
480
+ if (matchesKey(data, "ctrl+c")) {
481
+ interrupts.handleCtrlC();
482
+ return { consume: true };
483
+ }
484
+ if (matchesKey(data, "escape") && running) {
485
+ interrupts.handleEscape();
486
+ return { consume: true };
487
+ }
488
+ return void 0;
489
+ });
490
+ sigintHandler = () => {
491
+ interrupts.handleCtrlC();
492
+ };
493
+ process.on("SIGINT", sigintHandler);
348
494
  const promptInput = (message, placeholder) => {
349
495
  addLine(c.accentBold(` ${message}`));
350
496
  if (placeholder) addLine(c.dim(` (${placeholder})`));
351
- updateStatus();
352
497
  return new Promise((resolve) => {
353
498
  pendingInput = resolve;
499
+ updateStatus();
354
500
  });
355
501
  };
356
502
  const showModelPicker = (intro) => {
@@ -390,6 +536,254 @@ async function runTui(opts) {
390
536
  ui.setFocus(select);
391
537
  updateStatus();
392
538
  };
539
+ const savedObservabilitySummary = () => {
540
+ const saved = loadSettings().observability;
541
+ const resolved = resolveObservabilityConfig(saved, {});
542
+ return formatObservabilityStatus({
543
+ enabled: resolved.enabled,
544
+ capture: resolved.capture,
545
+ sampleRate: resolved.sampleRate,
546
+ destinations: [
547
+ ...resolved.local.enabled ? ["local"] : [],
548
+ ...resolved.remote ? [
549
+ resolved.remote.kind === "phoenix" ? `phoenix (${safeObservabilityEndpoint(resolved.remote.endpoint)})` : `otlp (${safeObservabilityEndpoint(resolved.remote.endpoint)})`
550
+ ] : []
551
+ ],
552
+ warnings: resolved.warnings
553
+ });
554
+ };
555
+ const persistObservability = (settings) => {
556
+ rememberObservability(settings);
557
+ addLine(c.accentBold(" \u2713 Observability settings saved."));
558
+ addLine(c.dim(` Saved: ${savedObservabilitySummary()}`));
559
+ addLine(c.dim(" Restart Janet to apply the new setting."));
560
+ updateStatus();
561
+ };
562
+ const closeActiveSelect = (select) => {
563
+ chat.removeChild(select);
564
+ if (activeSelect === select) activeSelect = null;
565
+ ui.setFocus(editor);
566
+ };
567
+ const confirmFullCapture = (base) => {
568
+ addLine(
569
+ c.warn(
570
+ " Full capture includes prompts, responses, and tool payloads. Do not use it with sensitive material."
571
+ )
572
+ );
573
+ const select = new SelectList(
574
+ [
575
+ {
576
+ value: "no",
577
+ label: "Keep metadata-only capture",
578
+ description: "Recommended. Content stays out of traces."
579
+ },
580
+ {
581
+ value: "yes",
582
+ label: "Enable full capture",
583
+ description: "I understand trace content may contain sensitive data."
584
+ }
585
+ ],
586
+ 2,
587
+ editorTheme.selectList
588
+ );
589
+ select.onSelect = (item) => {
590
+ closeActiveSelect(select);
591
+ persistObservability({
592
+ ...base,
593
+ capture: item.value === "yes" ? "full" : "metadata"
594
+ });
595
+ };
596
+ activeSelect = select;
597
+ chat.addChild(select);
598
+ ui.setFocus(select);
599
+ updateStatus();
600
+ };
601
+ const chooseCaptureMode = (base) => {
602
+ addLine(c.accentBold(" What may Janet include in traces?"));
603
+ const select = new SelectList(
604
+ [
605
+ {
606
+ value: "metadata",
607
+ label: "Metadata only",
608
+ description: "Timing, tool names, model, tokens, status, and errors."
609
+ },
610
+ {
611
+ value: "full",
612
+ label: "Full content",
613
+ description: "Also includes prompts, responses, and tool payloads."
614
+ }
615
+ ],
616
+ 2,
617
+ editorTheme.selectList
618
+ );
619
+ select.onSelect = (item) => {
620
+ closeActiveSelect(select);
621
+ const capture = item.value;
622
+ if (capture === "full") {
623
+ confirmFullCapture(base);
624
+ } else {
625
+ persistObservability({ ...base, capture });
626
+ }
627
+ };
628
+ activeSelect = select;
629
+ chat.addChild(select);
630
+ ui.setFocus(select);
631
+ updateStatus();
632
+ };
633
+ const showObservabilityPicker = () => {
634
+ if (running) {
635
+ addLine(c.dim(" Cancel the active run before changing observability settings."));
636
+ return;
637
+ }
638
+ addLine(c.accentBold(" Configure observability"));
639
+ addLine(c.dim(` Active now: ${formatObservabilityStatus(observability.status)}`));
640
+ addLine(c.dim(" Tracing is opt-in and changes apply after restart."));
641
+ const select = new SelectList(
642
+ [
643
+ {
644
+ value: "off",
645
+ label: "Off",
646
+ description: "No spans, trace database, or network export."
647
+ },
648
+ {
649
+ value: "local",
650
+ label: "Local trace history",
651
+ description: "Store traces in ~/.agent-knowledge/observability.db."
652
+ },
653
+ {
654
+ value: "phoenix",
655
+ label: "Phoenix",
656
+ description: "Send OTLP traces to http://localhost:6006."
657
+ },
658
+ {
659
+ value: "otlp",
660
+ label: "Custom OTLP",
661
+ description: "Send OTLP/HTTP protobuf traces to your endpoint."
662
+ }
663
+ ],
664
+ 4,
665
+ editorTheme.selectList
666
+ );
667
+ select.onSelect = (item) => {
668
+ closeActiveSelect(select);
669
+ if (item.value === "off") {
670
+ persistObservability({
671
+ capture: "off",
672
+ sampleRate: 1,
673
+ local: { enabled: false, retentionDays: 7 }
674
+ });
675
+ return;
676
+ }
677
+ if (item.value === "local") {
678
+ chooseCaptureMode({
679
+ sampleRate: 1,
680
+ local: { enabled: true, retentionDays: 7 }
681
+ });
682
+ return;
683
+ }
684
+ if (item.value === "phoenix") {
685
+ chooseCaptureMode({
686
+ sampleRate: 1,
687
+ local: { enabled: false, retentionDays: 7 },
688
+ remote: {
689
+ kind: "phoenix",
690
+ endpoint: "http://localhost:6006",
691
+ projectName: "janet"
692
+ }
693
+ });
694
+ return;
695
+ }
696
+ void promptInput(
697
+ "OTLP endpoint (for example, http://localhost:4318):"
698
+ ).then((endpoint) => {
699
+ try {
700
+ const parsed = new URL(endpoint);
701
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error();
702
+ if (parsed.username || parsed.password || parsed.search || parsed.hash) {
703
+ addLine(
704
+ c.error(
705
+ " Do not put credentials or query parameters in the saved endpoint. Use OTEL_EXPORTER_OTLP_HEADERS."
706
+ )
707
+ );
708
+ return;
709
+ }
710
+ } catch {
711
+ addLine(c.error(" Endpoint must be a valid HTTP or HTTPS URL."));
712
+ return;
713
+ }
714
+ chooseCaptureMode({
715
+ sampleRate: 1,
716
+ local: { enabled: false, retentionDays: 7 },
717
+ remote: {
718
+ kind: "otlp",
719
+ endpoint
720
+ }
721
+ });
722
+ });
723
+ };
724
+ activeSelect = select;
725
+ chat.addChild(select);
726
+ ui.setFocus(select);
727
+ updateStatus();
728
+ };
729
+ const showLocalTraces = async () => {
730
+ if (running) {
731
+ addLine(c.dim(" Cancel the active run before browsing traces."));
732
+ return;
733
+ }
734
+ if (!observability.config.local.enabled) {
735
+ addLine(c.dim(" Local trace history is not active. Use /observability to enable it."));
736
+ return;
737
+ }
738
+ await observability.flush().catch(() => {
739
+ });
740
+ const store = await observability.storage.getStore("observability");
741
+ if (!store) {
742
+ addLine(c.error(" Local trace storage is unavailable."));
743
+ return;
744
+ }
745
+ const recent = await store.listTraces({
746
+ pagination: { page: 0, perPage: 10 },
747
+ orderBy: { field: "startedAt", direction: "DESC" }
748
+ });
749
+ if (!recent.spans.length) {
750
+ addLine(c.dim(" No local traces yet."));
751
+ return;
752
+ }
753
+ addLine(c.accentBold(" Recent local traces"));
754
+ const select = new SelectList(
755
+ recent.spans.map((span) => {
756
+ const state = traceStatus(span);
757
+ const marker = state === "error" ? "\u2717" : state === "running" ? "\u2026" : "\u2713";
758
+ return {
759
+ value: span.traceId,
760
+ label: `${marker} ${span.name}`,
761
+ description: `${span.startedAt.toLocaleString()} \xB7 ${span.traceId}`
762
+ };
763
+ }),
764
+ Math.min(recent.spans.length, 10),
765
+ editorTheme.selectList
766
+ );
767
+ select.onSelect = (item) => {
768
+ closeActiveSelect(select);
769
+ void store.getTrace({ traceId: item.value }).then((trace) => {
770
+ if (!trace) {
771
+ addLine(c.error(` Trace not found: ${item.value}`));
772
+ return;
773
+ }
774
+ addLine(c.accentBold(` Trace ${trace.traceId}`));
775
+ for (const line of formatTraceTree(trace.spans)) {
776
+ addLine(c.dim(` ${line}`));
777
+ }
778
+ }).catch((error) => {
779
+ addLine(c.error(` Could not read trace: ${error.message}`));
780
+ });
781
+ };
782
+ activeSelect = select;
783
+ chat.addChild(select);
784
+ ui.setFocus(select);
785
+ updateStatus();
786
+ };
393
787
  const handleCommand = async (text) => {
394
788
  const [cmd, ...rest] = text.slice(1).split(/\s+/);
395
789
  switch (cmd) {
@@ -400,6 +794,32 @@ async function runTui(opts) {
400
794
  case "help":
401
795
  addLine(c.dim(HELP_TEXT));
402
796
  break;
797
+ case "cancel":
798
+ if (interrupts.handleEscape() === "ignored") {
799
+ addLine(c.dim(" No active run to cancel."));
800
+ }
801
+ break;
802
+ case "observability": {
803
+ const action = rest[0]?.trim().toLowerCase();
804
+ if (action === "status") {
805
+ addLine(c.dim(` Active: ${formatObservabilityStatus(observability.status)}`));
806
+ addLine(c.dim(` Saved: ${savedObservabilitySummary()}`));
807
+ } else if (action === "off") {
808
+ persistObservability({
809
+ capture: "off",
810
+ sampleRate: 1,
811
+ local: { enabled: false, retentionDays: 7 }
812
+ });
813
+ } else if (!action) {
814
+ showObservabilityPicker();
815
+ } else {
816
+ addLine(c.dim("Usage: /observability [status | off]"));
817
+ }
818
+ break;
819
+ }
820
+ case "traces":
821
+ await showLocalTraces();
822
+ break;
403
823
  case "login": {
404
824
  const providerId = (rest[0] || "anthropic").trim();
405
825
  if (!OAUTH_PROVIDERS.includes(providerId)) {
@@ -533,31 +953,21 @@ async function runTui(opts) {
533
953
  showModelPicker(" Pick a model first:");
534
954
  return;
535
955
  }
536
- void session.sendMessage({ content: text }).catch((err) => {
956
+ void session.sendMessage({
957
+ content: text,
958
+ tracingOptions: observability.tracingOptionsForTurn({
959
+ interactive: true,
960
+ operation: "chat",
961
+ resourceId: paths.resourceId,
962
+ threadId: session.thread.getId() ?? void 0
963
+ })
964
+ }).catch((err) => {
537
965
  running = false;
538
966
  setLoader(false);
539
967
  addLine(c.error(` \u2717 ${err.message}`));
540
968
  updateStatus();
541
969
  });
542
970
  };
543
- let lastCtrlC = 0;
544
- editor.onCtrlC = () => {
545
- const now = Date.now();
546
- if (now - lastCtrlC < 800) {
547
- void shutdown(0);
548
- return;
549
- }
550
- lastCtrlC = now;
551
- if (running) {
552
- void session.abort();
553
- addLine(c.dim(" (aborted \u2014 Ctrl+C again to quit)"));
554
- } else if (editor.getText()) {
555
- editor.setText("");
556
- ui.requestRender();
557
- } else {
558
- addLine(c.dim(" (Ctrl+C again to quit)"));
559
- }
560
- };
561
971
  addLine(c.accentBold(GREETING));
562
972
  addLine(
563
973
  c.dim(
@@ -565,6 +975,9 @@ async function runTui(opts) {
565
975
  Ask me anything in the bundle, or say what to ingest. /help for commands.`
566
976
  )
567
977
  );
978
+ for (const warning of observability.status.warnings) {
979
+ addLine(c.warn(`Observability: ${warning}`));
980
+ }
568
981
  updateStatus();
569
982
  ui.start();
570
983
  ui.setFocus(editor);
@@ -576,4 +989,4 @@ Ask me anything in the bundle, or say what to ingest. /help for commands.`
576
989
  export {
577
990
  runTui
578
991
  };
579
- //# sourceMappingURL=tui-6LNJ6LCX.js.map
992
+ //# sourceMappingURL=tui-F6OQNA7T.js.map