bcs-mcp 0.2.5

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.
@@ -0,0 +1,209 @@
1
+ import { z } from "zod";
2
+ import { completable } from "@modelcontextprotocol/sdk/server/completable.js";
3
+ import { completeTicker } from "./catalog.js";
4
+ /**
5
+ * Analysis recipes exposed as MCP prompts → slash commands in Claude Code
6
+ * (/bcs:portfolio_review etc.). A prompt instructs the model which tools to
7
+ * call and how to present the result; it never trades.
8
+ *
9
+ * Adapted for the BCS Trade API data set: no cash-operations history
10
+ * (no dividends received / fees / deposits), so no XIRR or tax recipes.
11
+ * Dividend/coupon calendars come from the separate moex-mcp server when available.
12
+ */
13
+ const TARGET_FILE_CONVENTION = `Целевые доли и цели портфеля бери из файла portfolio-target.json в корне проекта.
14
+ Формат:
15
+ {
16
+ "targets": {"shares": 55, "bonds": 30, "etf": 10, "cash": 5},
17
+ "byTicker": {"SBER": 10},
18
+ "goals": [{"name": "FIRE", "targetAmount": 30000000, "targetDate": "2035-01-01",
19
+ "monthlyContribution": 100000, "expectedAnnualReturnPct": 8}]
20
+ }
21
+ (доли в процентах, классы должны суммироваться к 100; etf — отдельный класс, фонды денежного
22
+ рынка допустимо относить к cash — отнесение фиксируй явно; goals — массив, целей может быть несколько).
23
+ Эталон формата доступен как MCP-ресурс bcs://portfolio-target/example.
24
+ Если файла нет — покажи текущую структуру, предложи значения и, после согласования с пользователем, создай файл по эталону.`;
25
+ const BIG_PORTFOLIO_NOTE = `Примечание: если в портфеле много позиций (50+), вызывай get_portfolio с outputPath (например data/portfolio.json) и работай с файлом — большой инлайн-ответ упрётся в лимит.`;
26
+ const UNITS_NOTE = `ВАЖНО про количества у БКС: в портфеле и заявках количество в ШТУКАХ, торговый лот — lotSize из find_instrument; покупки планируй кратно лоту (шт. = лоты × lotSize).`;
27
+ const MOEX_NOTE = `Дивиденды и купоны БКС Trade API не отдаёт — если подключён сервер moex-mcp (MOEX ISS), бери их оттуда (get_dividends, get_coupons); иначе пропусти этот пункт, явно отметив ограничение.`;
28
+ const CASH_NOTE = `Свободные деньги: get_limits → freeByCurrency, НО это снимок на начало дня (loadDate) — сегодняшние пополнения могут отсутствовать. Живой остаток RUB виден позицией RUB в get_portfolio; при расхождении доверяй портфелю и уточни у пользователя.`;
29
+ // ─── Recipe bodies ──────────────────────────────────────────────
30
+ const recipes = {
31
+ portfolioReview: () => `Сделай полный обзор моего портфеля БКС.
32
+
33
+ 1. get_portfolio: общая стоимость (totalRub), структура по классам активов (byType) и по валютам, топ позиций. Свободные деньги — get_limits → freeByCurrency (снимок на начало дня).
34
+ 2. Концентрация: топ-5 позиций по весу (portfolioShare); отметь всё, что выше 15% портфеля. Сгруппируй акции по секторам (businessSector из find_instrument).
35
+ 3. ${TARGET_FILE_CONVENTION}
36
+ 4. Сравни факт с целями, покажи отклонения в п.п.
37
+ 5. Риски: заблокированные бумаги (isBlocked), облигации с погашением в ближайшие 6 месяцев (maturityDate из find_instrument), низкий liquidityRating.
38
+
39
+ Выведи компактно: сводная таблица, отклонения от целей, 3-5 главных наблюдений. Не предлагай сделки без запроса.
40
+ ${BIG_PORTFOLIO_NOTE}`,
41
+ rebalanceCheck: (threshold) => `Проверь, нужна ли ребалансировка портфеля (порог: ${threshold ?? "5"} п.п.).
42
+
43
+ 1. ${TARGET_FILE_CONVENTION}
44
+ 2. get_portfolio → фактические доли по классам активов (и по тикерам, если в целях есть byTicker).
45
+ 3. Посчитай отклонения факт − цель в п.п. Если все в пределах порога — так и скажи, на этом стоп.
46
+ 4. Если есть превышения: предложи план сделок в ЦЕЛЫХ ЛОТАХ (lotSize из find_instrument, цены из get_quotes или currentPrice портфеля; для облигаций цена в % номинала!). Сначала продажи перевеса, потом покупки недовеса; учти освобождающийся кэш.
47
+ 5. Выведи: таблица отклонений, план сделок (тикер | направление | лоты | шт. | ~сумма), остаток кэша. Сделки НЕ исполняй — только план. Исполнение только по моей явной команде через place_order.
48
+ ${CASH_NOTE}
49
+ ${UNITS_NOTE}
50
+ ${BIG_PORTFOLIO_NOTE}`,
51
+ investCash: (amount) => `Пришло пополнение — распредели его докупками, приближая портфель к целевым долям (БЕЗ продаж).
52
+
53
+ 0. Сумма из аргумента команды: «${amount}». ВНИМАНИЕ: аргументы режутся по пробелам — если
54
+ значение выглядит обрезанным (например «10» вместо «10 000») или это не число, СНАЧАЛА
55
+ уточни полную сумму у пользователя и работай с подтверждённой.
56
+ 1. ${TARGET_FILE_CONVENTION}
57
+ 2. get_portfolio → текущие доли; посчитай недовес каждого класса/тикера относительно целей в рублях. ${CASH_NOTE} Если пополнение пришло только что и нигде не видно — работай с суммой из аргумента, отметив это.
58
+ 3. Распредели подтверждённую сумму по недовешенным позициям пропорционально недовесу, в ЦЕЛЫХ ЛОТАХ (lotSize из find_instrument, цены из get_quotes; облигации — цена в % номинала!).
59
+ 4. Алгоритм жадный: покупай лоты самого недовешенного, пока сумма не исчерпана; остаток < минимального лота — оставь кэшем.
60
+ 5. Выведи план: тикер | лоты | шт. | ~сумма | доля после покупки, и остаток. Сделки НЕ исполняй — только план; исполнение по моей явной команде.
61
+ ${UNITS_NOTE}
62
+ ${BIG_PORTFOLIO_NOTE}`,
63
+ bondPicker: (amount, horizon) => `Подбери облигации под сумму и горизонт.
64
+
65
+ 0. Аргументы команды: сумма «${amount}», горизонт «${horizon}». Аргументы режутся по пробелам:
66
+ если сумма выглядит обрезанной (например «10» вместо «10 000»), горизонт пуст или непонятен —
67
+ СНАЧАЛА уточни оба значения у пользователя и работай с подтверждёнными.
68
+ 1. Уточни предпочтения, если не очевидно: ОФЗ / корпоративные / микс; требования к надёжности; квал-статус
69
+ (в карточках есть availableForUnqualified/isQualifiedOnly).
70
+ 2. Каталог: get_instruments_by_type(BONDS) с outputPath (например data/bonds.json) — это тысячи бумаг,
71
+ фильтруй файл ПРОГРАММНО (jq/скрипт), в диалог тащи только отобранных кандидатов.
72
+ 3. Фильтры по карточкам: maturityDate (формат YYYYMMDD) в пределах горизонта; couponRate/couponsPerYear;
73
+ creditRating и liquidityRating (сначала посмотри, какие значения реально встречаются в каталоге — шкалу не выдумывай);
74
+ рублёвые (settlementCurrency), замещающие (isReplacementBond) и complexProduct — только если явно просили.
75
+ 4. По топ-кандидатам (8-15 шт.) возьми живые цены get_quotes (цена облигаций в % номинала!).
76
+ Грязная цена ₽ = цена% × faceValue / 100 + accruedInt.
77
+ 5. Посчитай ПРОСТУЮ доходность к погашению скриптом, не в уме:
78
+ (купоны до погашения + (faceValue − грязная цена)) / грязная цена / лет до погашения;
79
+ купоны в год = faceValue × couponRate / 100. ОГРАНИЧЕНИЕ: оферты/колл-опционы API не отдаёт —
80
+ у корпоративных бумаг реальная доходность может считаться к оферте; пометь это (при подключённом
81
+ moex-mcp можно перепроверить оферты там).
82
+ 6. Выведи топ-5..8: тикер | погашение | грязная цена ₽ | ~доходность | купон %/год | частота | рейтинг/ликвидность | примечания.
83
+ Предложи распределение суммы по 3-5 выпускам с разными сроками (лесенка), в целых лотах. Сделки НЕ исполняй — только план.
84
+ ${UNITS_NOTE}`,
85
+ fireProgress: () => `Покажи прогресс к финансовым целям.
86
+
87
+ 1. ${TARGET_FILE_CONVENTION}
88
+ Целей в goals может быть несколько — отчитайся по КАЖДОЙ.
89
+ 2. get_portfolio → текущий капитал (totalRub; деньги и металлы уже включены в портфель — get_limits не прибавляй, будет двойной счёт).
90
+ 3. Историю пополнений BCS API не отдаёт — фактический темп взносов возьми из monthlyContribution цели
91
+ или спроси у пользователя; допущение отметь явно.
92
+ 4. Для каждой цели посчитай СКРИПТОМ, не в уме: месячная ставка r = (1 + expectedAnnualReturnPct/100)^(1/12) − 1;
93
+ n = месяцев до targetDate; прогноз FV = капитал × (1+r)^n + взнос × ((1+r)^n − 1)/r;
94
+ месяц достижения targetAmount при текущем темпе; необходимый ежемесячный взнос, чтобы успеть точно к targetDate.
95
+ 5. Выведи по каждой цели: прогресс-бар (например ████░░ 62%), капитал/цель, прогнозный год достижения,
96
+ вердикт «успеваем/нет» с зазором в ₽, необходимая корректировка взноса, чувствительность к доходности ±2 п.п.
97
+ Допущения укажи явно. Это расчёт, не рекомендация.
98
+ ${BIG_PORTFOLIO_NOTE}`,
99
+ positionDeepDive: (ticker) => `Сделай глубокий разбор инструмента ${ticker}.
100
+
101
+ 1. find_instrument(["${ticker}"]) → карточка: тип, сектор (businessSector), isin, lotSize, issuerName; для акций — dividendYield, epsGrowthRate, bcsScore, mktcap, peNorm, прогнозы targetPrice/predictedDps и готовая динамика priceChangeMonth/priceChangeHalfYear/priceChangeYear; для облигаций — faceValue, maturityDate (формат YYYYMMDD), couponRate, couponsPerYear, nextCoupon, accruedInt, creditRating, liquidityRating.
102
+ 2. Если есть в моём портфеле (get_portfolio) — моя позиция: количество, средняя (balancePrice), P&L (unrealizedPL/unrealizedPercentPL).
103
+ 3. get_candles (timeFrame=D, 1 год): динамика, мин/макс 52 недель, текущая цена относительно диапазона (сверь с priceChange* из карточки). Текущая цена — get_quotes.
104
+ 4. ${MOEX_NOTE}
105
+ 5. Вывод: 5-7 тезисов — сильные стороны, риски, что мониторить. Это анализ, не рекомендация.`,
106
+ weeklyDigest: () => `Сделай недельный дайджест портфеля БКС.
107
+
108
+ 1. get_portfolio → позиции; у каждой уже есть dailyPL/dailyPercentPL (за день). Для недельной динамики: get_candles (timeFrame=D, последние 7 дней) по топ-позициям → изменение за неделю в % и ₽ с учётом количества.
109
+ 2. Портфель целиком: суммарное изменение за неделю (по свечам взвешенно) и unrealizedPL.
110
+ 3. Топ-3 выросших и топ-3 упавших; для них, если доступен поисковый инструмент (exa/web search) — 1-2 свежие новости по эмитенту.
111
+ 4. ${MOEX_NOTE}
112
+ 5. Выведи компактный дайджест: движение портфеля, таблица топ-движений с причинами, события недели. Без рекомендаций.
113
+ ${BIG_PORTFOLIO_NOTE}`,
114
+ tradesReview: (days) => `Разбери мои сделки за последние ${days ?? "30"} дней.
115
+
116
+ 1. get_trades с серверными фильтрами from/to (from = сегодня минус ${days ?? "30"} дней, ISO 8601). Если totalRecords больше size — либо листай страницы, либо выгрузи всё разом через outputPath (тогда качаются все страницы). Время сделки — поле executionDateTime.
117
+ Если придёт 404 «known limitation» — это известное ограничение (сервис-хост search-эндпоинтов ещё не подтверждён БКС): НЕ ретрай, сообщи об ограничении и заверши.
118
+ 2. Сгруппируй по тикерам: суммарные покупки/продажи в шт. и ₽, средние цены, комиссии (если поле есть в данных).
119
+ 3. Сопоставь с текущим портфелем (get_portfolio): что из купленного уже в плюсе/минусе.
120
+ 4. Выведи: таблица сделок по тикерам, итог по комиссиям, 2-3 наблюдения (например, частые развороты по одной бумаге).
121
+ Помни: API отдаёт только биржевые сделки (данные с 26.01.2026) — пополнений, выводов и дивидендов в этих данных нет.`,
122
+ feedback: (issue) => `Сформируй фидбэк-репорт для разработчиков bcs-mcp${issue ? ` по проблеме: «${issue}»` : ""}.
123
+
124
+ 1. Диагностика: вызови get_server_info (версия, режимы, окружение) и перечисли видимые тебе tools/команды сервера bcs.
125
+ 2. Восстанови картину из текущего диалога: какие tools вызывались и с какими аргументами, ТОЧНЫЕ тексты ошибок и ответов (цитируй дословно), что ожидалось и что получилось. Если проблема не из этого диалога — расспроси пользователя и попробуй воспроизвести (ТОЛЬКО read-only вызовами, никаких сделок).
126
+ 3. Собери markdown-репорт:
127
+ # Feedback: <краткая тема>
128
+ Дата · версия сервера и режимы (из get_server_info) · клиент (Claude Code/Desktop) и ОС
129
+ ## Сводка — таблица: блок | статус (✅/⚠️/❌)
130
+ ## Проблема — шаги воспроизведения, ожидание vs реальность, точные тексты ошибок
131
+ ## Контекст — фрагменты вызовов/ответов, относящиеся к делу
132
+ 4. ПРИВАТНОСТЬ (обязательный шаг): токенов в файле быть не должно ни в каком виде. Покажи пользователю готовый текст репорта и спроси, замаскировать ли номер счёта, суммы и состав портфеля — примени его выбор.
133
+ 5. Сохрани файл в feedback/FEEDBACK-<YYYY-MM-DD>-<краткий-slug>.md и спроси, как передать его разработчикам.`,
134
+ };
135
+ // ─── Umbrella composition ───────────────────────────────────────
136
+ function umbrella(intro, sections) {
137
+ return [
138
+ `${intro}\n\nВыполни последовательно разделы ниже и сведи всё в ОДИН компактный отчёт с заголовками; промежуточные таблицы не дублируй.`,
139
+ ...sections.map((s, i) => `## Раздел ${i + 1}: ${s.title}\n\n${s.body}`),
140
+ ].join("\n\n---\n\n");
141
+ }
142
+ function text(s) {
143
+ return { messages: [{ role: "user", content: { type: "text", text: s } }] };
144
+ }
145
+ export function registerPrompts(server) {
146
+ server.registerPrompt("portfolio_review", {
147
+ title: "Обзор портфеля",
148
+ description: "Полный обзор: структура, концентрация, риски, сравнение с целевыми долями",
149
+ argsSchema: {},
150
+ }, () => text(recipes.portfolioReview()));
151
+ server.registerPrompt("rebalance_check", {
152
+ title: "Проверка ребалансировки",
153
+ description: "Дрейф от целевых долей и план сделок в лотах (без исполнения)",
154
+ argsSchema: { threshold: z.string().optional().describe("Порог отклонения в п.п., по умолчанию 5") },
155
+ }, ({ threshold }) => text(recipes.rebalanceCheck(threshold)));
156
+ server.registerPrompt("invest_cash", {
157
+ title: "Распределить пополнение",
158
+ description: "DCA-докупка: распределить свободный кэш к целевым долям, без продаж",
159
+ argsSchema: { amount: z.string().describe("Сумма в рублях БЕЗ пробелов и валюты, например 100000") },
160
+ }, ({ amount }) => text(recipes.investCash(amount)));
161
+ server.registerPrompt("bond_picker", {
162
+ title: "Подбор облигаций",
163
+ description: "Скрининг каталога облигаций под сумму и горизонт: рейтинг, купон, простая доходность, лесенка",
164
+ argsSchema: {
165
+ amount: z.string().describe("Сумма в рублях БЕЗ пробелов, например 300000"),
166
+ horizon: z.string().describe("Горизонт, например 2года или 2030 (без пробелов)"),
167
+ },
168
+ }, ({ amount, horizon }) => text(recipes.bondPicker(amount, horizon)));
169
+ server.registerPrompt("fire_progress", {
170
+ title: "Прогресс к целям",
171
+ description: "Прогресс к финансовым целям (FIRE) из portfolio-target.json: прогноз, зазор, требуемый взнос",
172
+ argsSchema: {},
173
+ }, () => text(recipes.fireProgress()));
174
+ server.registerPrompt("position_deep_dive", {
175
+ title: "Разбор позиции",
176
+ description: "Глубокий разбор одного инструмента: карточка, динамика, моя позиция",
177
+ argsSchema: { ticker: completable(z.string().describe("Тикер, например SBER"), completeTicker) },
178
+ }, ({ ticker }) => text(recipes.positionDeepDive(ticker)));
179
+ server.registerPrompt("trades_review", {
180
+ title: "Разбор сделок",
181
+ description: "Мои сделки за период: обороты, средние цены, комиссии",
182
+ argsSchema: { days: z.string().optional().describe("Период в днях, по умолчанию 30") },
183
+ }, ({ days }) => text(recipes.tradesReview(days)));
184
+ server.registerPrompt("feedback", {
185
+ title: "Фидбэк разработчикам",
186
+ description: "Сформировать репорт о проблеме: диагностика сервера, шаги воспроизведения, точные ошибки → feedback/*.md",
187
+ argsSchema: { issue: z.string().optional().describe("Краткое описание проблемы") },
188
+ }, ({ issue }) => text(recipes.feedback(issue)));
189
+ // ─── Umbrella commands: composed from the same recipes ───
190
+ server.registerPrompt("weekly", {
191
+ title: "Недельный ритуал",
192
+ description: "Дайджест недели + распределение пополнения (если указана сумма)",
193
+ argsSchema: {
194
+ amount: z.string().optional().describe("Сумма ₽ без пробелов, напр. 100000 (без неё раздел докупки пропускается)"),
195
+ },
196
+ }, ({ amount }) => text(umbrella("Недельный ритуал по портфелю БКС.", [
197
+ { title: "Дайджест недели", body: recipes.weeklyDigest() },
198
+ ...(amount ? [{ title: "Распределение пополнения", body: recipes.investCash(amount) }] : []),
199
+ ])));
200
+ server.registerPrompt("quarterly", {
201
+ title: "Квартальный ритуал",
202
+ description: "Обзор портфеля + проверка ребалансировки",
203
+ argsSchema: {},
204
+ }, () => text(umbrella("Квартальный ритуал по портфелю БКС.", [
205
+ { title: "Обзор портфеля", body: recipes.portfolioReview() },
206
+ { title: "Проверка ребалансировки", body: recipes.rebalanceCheck() },
207
+ ])));
208
+ }
209
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** Reference artifacts exposed as MCP resources (readable by both humans and agents) */
2
+ const PORTFOLIO_TARGET_EXAMPLE = {
3
+ $comment: "Эталон portfolio-target.json: скопируйте в корень проекта и поправьте значения. targets/byTicker — проценты, классы targets должны суммироваться к 100 (etf — отдельный класс; фонды денежного рынка при желании относите к cash, фиксируя это в byTicker). goals — массив, целей может быть несколько.",
4
+ targets: { shares: 55, bonds: 30, etf: 10, cash: 5 },
5
+ byTicker: { SBER: 10 },
6
+ goals: [
7
+ {
8
+ name: "FIRE",
9
+ targetAmount: 30_000_000,
10
+ targetDate: "2035-01-01",
11
+ monthlyContribution: 100_000,
12
+ expectedAnnualReturnPct: 8,
13
+ },
14
+ ],
15
+ };
16
+ export function registerResources(server) {
17
+ server.registerResource("portfolio-target-example", "bcs://portfolio-target/example", {
18
+ title: "portfolio-target.json — эталон",
19
+ description: "Образец файла целевых долей и финансовых целей для slash-команд (rebalance_check, invest_cash, ритуалы). Скопируйте в корень проекта как portfolio-target.json.",
20
+ mimeType: "application/json",
21
+ }, async (uri) => ({
22
+ contents: [
23
+ {
24
+ uri: uri.href,
25
+ mimeType: "application/json",
26
+ text: JSON.stringify(PORTFOLIO_TARGET_EXAMPLE, null, 2),
27
+ },
28
+ ],
29
+ }));
30
+ }
31
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAEA,wFAAwF;AAExF,MAAM,wBAAwB,GAAG;IAC/B,QAAQ,EACN,ySAAyS;IAC3S,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;IACpD,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACtB,KAAK,EAAE;QACL;YACE,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,UAAU;YACxB,UAAU,EAAE,YAAY;YACxB,mBAAmB,EAAE,OAAO;YAC5B,uBAAuB,EAAE,CAAC;SAC3B;KACF;CACF,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,CAAC,gBAAgB,CACrB,0BAA0B,EAC1B,gCAAgC,EAChC;QACE,KAAK,EAAE,gCAAgC;QACvC,WAAW,EACT,iKAAiK;QACnK,QAAQ,EAAE,kBAAkB;KAC7B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACd,QAAQ,EAAE;YACR;gBACE,GAAG,EAAE,GAAG,CAAC,IAAI;gBACb,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,wBAAwB,EAAE,IAAI,EAAE,CAAC,CAAC;aACxD;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { config, tokenStatus } from "../client.js";
2
+ import { ok, fail } from "../helpers.js";
3
+ import { outputRoot } from "../output.js";
4
+ import { VERSION } from "../version.js";
5
+ export function registerInfoTools(server) {
6
+ server.registerTool("get_server_info", {
7
+ title: "Get Server Info",
8
+ description: "Server diagnostics for support/feedback reports: version, runtime, mode flags (trading/confirmation), output root. No broker API calls, never includes tokens.",
9
+ inputSchema: {},
10
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
11
+ }, async () => {
12
+ try {
13
+ return ok({
14
+ name: "bcs-mcp-server",
15
+ version: VERSION,
16
+ node: process.version,
17
+ platform: `${process.platform}/${process.arch}`,
18
+ tradingEnabled: config.allowTrading,
19
+ tradeConfirmation: config.confirm,
20
+ outputRoot: outputRoot(),
21
+ ...tokenStatus(),
22
+ });
23
+ }
24
+ catch (e) {
25
+ return fail(e);
26
+ }
27
+ });
28
+ }
29
+ //# sourceMappingURL=info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info.js","sourceRoot":"","sources":["../../src/tools/info.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,gKAAgK;QAClK,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;KACxG,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,OAAO,EAAE,CAAC;gBACR,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE,OAAO,CAAC,OAAO;gBACrB,QAAQ,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE;gBAC/C,cAAc,EAAE,MAAM,CAAC,YAAY;gBACnC,iBAAiB,EAAE,MAAM,CAAC,OAAO;gBACjC,UAAU,EAAE,UAAU,EAAE;gBACxB,GAAG,WAAW,EAAE;aACjB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}