@thesashadev/girl-agent 0.4.1 → 0.4.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 (4) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +159 -118
  3. package/dist/cli.js +417 -175
  4. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -497,6 +497,161 @@ var init_markdown = __esm({
497
497
  }
498
498
  });
499
499
 
500
+ // src/telegram/reactions.ts
501
+ function normalizeBotReactionEmoji(emoji) {
502
+ if (!emoji) return void 0;
503
+ const trimmed = emoji.trim();
504
+ if (!trimmed) return void 0;
505
+ if (BOT_REACTION_ALLOWED.has(trimmed)) return trimmed;
506
+ const stripped = trimmed.replace(/\uFE0F/g, "");
507
+ if (BOT_REACTION_ALLOWED.has(stripped)) return stripped;
508
+ const mapped = FALLBACK[trimmed] ?? FALLBACK[stripped];
509
+ if (mapped && BOT_REACTION_ALLOWED.has(mapped)) return mapped;
510
+ return void 0;
511
+ }
512
+ var BOT_REACTION_ALLOWED, FALLBACK;
513
+ var init_reactions = __esm({
514
+ "src/telegram/reactions.ts"() {
515
+ "use strict";
516
+ init_esm_shims();
517
+ BOT_REACTION_ALLOWED = /* @__PURE__ */ new Set([
518
+ "\u{1F44D}",
519
+ "\u{1F44E}",
520
+ "\u2764",
521
+ "\u{1F525}",
522
+ "\u{1F970}",
523
+ "\u{1F44F}",
524
+ "\u{1F601}",
525
+ "\u{1F914}",
526
+ "\u{1F92F}",
527
+ "\u{1F631}",
528
+ "\u{1F92C}",
529
+ "\u{1F622}",
530
+ "\u{1F389}",
531
+ "\u{1F929}",
532
+ "\u{1F92E}",
533
+ "\u{1F4A9}",
534
+ "\u{1F64F}",
535
+ "\u{1F44C}",
536
+ "\u{1F54A}",
537
+ "\u{1F921}",
538
+ "\u{1F971}",
539
+ "\u{1F974}",
540
+ "\u{1F60D}",
541
+ "\u{1F433}",
542
+ "\u2764\u200D\u{1F525}",
543
+ "\u{1F31A}",
544
+ "\u{1F32D}",
545
+ "\u{1F4AF}",
546
+ "\u{1F923}",
547
+ "\u26A1",
548
+ "\u{1F34C}",
549
+ "\u{1F3C6}",
550
+ "\u{1F494}",
551
+ "\u{1F928}",
552
+ "\u{1F610}",
553
+ "\u{1F353}",
554
+ "\u{1F37E}",
555
+ "\u{1F48B}",
556
+ "\u{1F595}",
557
+ "\u{1F608}",
558
+ "\u{1F634}",
559
+ "\u{1F62D}",
560
+ "\u{1F913}",
561
+ "\u{1F47B}",
562
+ "\u{1F468}\u200D\u{1F4BB}",
563
+ "\u{1F440}",
564
+ "\u{1F383}",
565
+ "\u{1F648}",
566
+ "\u{1F607}",
567
+ "\u{1F628}",
568
+ "\u{1F91D}",
569
+ "\u270D",
570
+ "\u{1F917}",
571
+ "\u{1FAE1}",
572
+ "\u{1F385}",
573
+ "\u{1F384}",
574
+ "\u2603",
575
+ "\u{1F485}",
576
+ "\u{1F92A}",
577
+ "\u{1F5FF}",
578
+ "\u{1F192}",
579
+ "\u{1F498}",
580
+ "\u{1F649}",
581
+ "\u{1F984}",
582
+ "\u{1F618}",
583
+ "\u{1F48A}",
584
+ "\u{1F64A}",
585
+ "\u{1F60E}",
586
+ "\u{1F47E}",
587
+ "\u{1F937}\u200D\u2642",
588
+ "\u{1F937}",
589
+ "\u{1F937}\u200D\u2640",
590
+ "\u{1F621}"
591
+ ]);
592
+ FALLBACK = {
593
+ "\u2764\uFE0F": "\u2764",
594
+ "\u{1F496}": "\u2764",
595
+ "\u{1F497}": "\u2764",
596
+ "\u{1F495}": "\u2764",
597
+ "\u{1F493}": "\u2764",
598
+ "\u{1F49E}": "\u2764",
599
+ "\u{1FA77}": "\u2764",
600
+ "\u{1F49C}": "\u2764",
601
+ "\u{1F499}": "\u2764",
602
+ "\u{1F49A}": "\u2764",
603
+ "\u{1F9E1}": "\u2764",
604
+ "\u{1F49B}": "\u2764",
605
+ "\u{1F90D}": "\u2764",
606
+ "\u{1F5A4}": "\u2764",
607
+ "\u{1F90E}": "\u2764",
608
+ "\u{1F60F}": "\u{1F60E}",
609
+ "\u{1F644}": "\u{1F928}",
610
+ "\u{1F97A}": "\u{1F979}",
611
+ "\u{1F979}": "\u{1F62D}",
612
+ "\u{1F480}": "\u{1F5FF}",
613
+ "\u{1F602}": "\u{1F923}",
614
+ "\u{1F92D}": "\u{1F917}",
615
+ "\u{1F92B}": "\u{1FAE1}",
616
+ "\u{1F910}": "\u{1FAE1}",
617
+ "\u{1F643}": "\u{1F970}",
618
+ "\u{1F642}": "\u{1F970}",
619
+ "\u{1F60A}": "\u{1F970}",
620
+ "\u{1F605}": "\u{1F601}",
621
+ "\u{1F606}": "\u{1F923}",
622
+ "\u{1F60B}": "\u{1F60D}",
623
+ "\u{1F61C}": "\u{1F92A}",
624
+ "\u{1F61D}": "\u{1F92A}",
625
+ "\u{1F61B}": "\u{1F92A}",
626
+ "\u{1F62C}": "\u{1F628}",
627
+ "\u{1F924}": "\u{1F974}",
628
+ "\u{1F62A}": "\u{1F971}",
629
+ "\u{1F614}": "\u{1F622}",
630
+ "\u{1F61E}": "\u{1F622}",
631
+ "\u{1F61F}": "\u{1F914}",
632
+ "\u{1F615}": "\u{1F914}",
633
+ "\u{1F976}": "\u{1F974}",
634
+ "\u{1F927}": "\u{1F974}",
635
+ "\u{1F912}": "\u{1F48A}",
636
+ "\u{1F637}": "\u{1F48A}",
637
+ "\u{1F915}": "\u{1F48A}",
638
+ "\u{1F922}": "\u{1F92E}",
639
+ "\u{1F44B}": "\u{1F91D}",
640
+ "\u270A": "\u{1F3C6}",
641
+ "\u{1F44A}": "\u{1F3C6}",
642
+ "\u{1F4AA}": "\u{1F3C6}",
643
+ "\u{1F64C}": "\u{1F64F}",
644
+ "\u{1FAF6}": "\u2764",
645
+ "\u{1F973}": "\u{1F389}",
646
+ "\u{1F38A}": "\u{1F389}",
647
+ "\u2728": "\u26A1",
648
+ "\u2B50": "\u{1F3C6}",
649
+ "\u{1F31F}": "\u{1F3C6}",
650
+ "\u{1F972}": "\u{1F62D}"
651
+ };
652
+ }
653
+ });
654
+
500
655
  // src/telegram/bot.ts
501
656
  var bot_exports = {};
502
657
  __export(bot_exports, {
@@ -550,19 +705,23 @@ function makeBotAdapter(cfg) {
550
705
  await onMessage(msg).catch(() => {
551
706
  });
552
707
  });
708
+ try {
709
+ await bot.init();
710
+ } catch (e) {
711
+ throw new Error(`Telegram bot init failed: ${e?.message ?? e}. \u041F\u0440\u043E\u0432\u0435\u0440\u044C BOT_TOKEN (BotFather), \u0434\u043E\u0441\u0442\u0443\u043F \u043A api.telegram.org \u0438 \u0447\u0442\u043E \u0434\u0440\u0443\u0433\u043E\u0439 \u0438\u043D\u0441\u0442\u0430\u043D\u0441 \u0431\u043E\u0442\u0430 \u043D\u0435 \u0434\u0435\u0440\u0436\u0438\u0442 long-polling.`);
712
+ }
713
+ const me = bot.botInfo;
714
+ selfInfo = {
715
+ username: me.username ?? void 0,
716
+ displayName: [me.first_name, me.last_name].filter(Boolean).join(" ") || void 0
717
+ };
553
718
  bot.start({
554
719
  drop_pending_updates: true,
555
720
  allowed_updates: ["message", "edited_message", "callback_query", "message_reaction"]
556
- }).catch(() => {
721
+ }).catch((e) => {
722
+ process.stderr.write(`[bot] long-polling stopped: ${e?.message ?? e}
723
+ `);
557
724
  });
558
- try {
559
- const me = await bot.api.getMe();
560
- selfInfo = {
561
- username: me.username ?? void 0,
562
- displayName: [me.first_name, me.last_name].filter(Boolean).join(" ") || void 0
563
- };
564
- } catch {
565
- }
566
725
  },
567
726
  async sendText(chatId, text) {
568
727
  if (hasSpoilers(text)) {
@@ -584,11 +743,19 @@ function makeBotAdapter(cfg) {
584
743
  }
585
744
  },
586
745
  async setReaction(chatId, messageId, emoji) {
746
+ const normalized = normalizeBotReactionEmoji(emoji);
747
+ if (!normalized) {
748
+ process.stderr.write(`[bot] reaction "${emoji}" \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F Bot API \u0438 \u043D\u0435\u0442 \u0437\u0430\u043C\u0435\u043D\u044B \u2014 \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u043C
749
+ `);
750
+ return;
751
+ }
587
752
  try {
588
753
  await bot.api.setMessageReaction(chatId, messageId, [
589
- { type: "emoji", emoji }
754
+ { type: "emoji", emoji: normalized }
590
755
  ]);
591
- } catch {
756
+ } catch (e) {
757
+ process.stderr.write(`[bot] setMessageReaction("${normalized}", chat=${chatId}, msg=${messageId}) failed: ${e?.message ?? e}
758
+ `);
592
759
  }
593
760
  },
594
761
  async editText(chatId, messageId, newText) {
@@ -656,6 +823,7 @@ var init_bot = __esm({
656
823
  "use strict";
657
824
  init_esm_shims();
658
825
  init_markdown();
826
+ init_reactions();
659
827
  }
660
828
  });
661
829
 
@@ -1856,6 +2024,8 @@ var init_presence = __esm({
1856
2024
  });
1857
2025
 
1858
2026
  // src/engine/daily-life.ts
2027
+ import { promises as fs3 } from "fs";
2028
+ import path5 from "path";
1859
2029
  function localDateStr(tz, now = /* @__PURE__ */ new Date()) {
1860
2030
  try {
1861
2031
  const fmt = new Intl.DateTimeFormat("en-CA", { timeZone: tz, year: "numeric", month: "2-digit", day: "2-digit" });
@@ -1878,7 +2048,7 @@ function localWeekday(tz, now = /* @__PURE__ */ new Date()) {
1878
2048
  return "";
1879
2049
  }
1880
2050
  }
1881
- function buildPrompt(cfg, persona, weekday, dateLocal, conflict) {
2051
+ function buildPrompt(cfg, persona, weekday, dateLocal, conflict, recentEvents) {
1882
2052
  const conflictNote = conflict && conflict.level > 0 ? `
1883
2053
 
1884
2054
  \u0412\u0410\u0416\u041D\u041E: \u0443 \u043D\u0435\u0451 \u0441\u0435\u0439\u0447\u0430\u0441 \u041A\u041E\u041D\u0424\u041B\u0418\u041A\u0422 \u0441 \u043D\u0438\u043C (level ${conflict.level}, \u043F\u0440\u0438\u0447\u0438\u043D\u0430: "${conflict.reason ?? "\u2014"}"). \u042D\u0442\u043E \u0432\u043B\u0438\u044F\u0435\u0442 \u043D\u0430 \u0435\u0451 \u0434\u0435\u043D\u044C:
@@ -1892,7 +2062,13 @@ function buildPrompt(cfg, persona, weekday, dateLocal, conflict) {
1892
2062
  ${cfg.busySchedule.map((s) => `- ${s.label}: ${s.from}-${s.to}${s.days ? ` (${s.days.join(", ")})` : ""}`).join("\n")}
1893
2063
 
1894
2064
  \u0423\u0427\u0418\u0422\u042B\u0412\u0410\u0419 \u044D\u0442\u043E \u043F\u0440\u0438 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 blocks: \u0435\u0441\u043B\u0438 busySlot \u043F\u0435\u0440\u0435\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u0432\u0440\u0435\u043C\u044F, activity \u0434\u043E\u043B\u0436\u043D\u0430 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C. \u0414\u043B\u044F \u0432\u043E\u0437\u0440\u0430\u0441\u0442\u0430 \u0434\u043E 17 \u043B\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 "\u0432 \u0448\u043A\u043E\u043B\u0435", "\u043D\u0430 \u0443\u0440\u043E\u043A\u0435", "\u0443\u0440\u043E\u043A\u0438", "\u043F\u0435\u0440\u0435\u043C\u0435\u043D\u0430"; \u041D\u0415 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 "\u043F\u0430\u0440\u0430", "\u043B\u0435\u043A\u0446\u0438\u044F", "\u0443\u043D\u0438\u0432\u0435\u0440", "\u043F\u0440\u0435\u043F\u043E\u0434". \u0414\u043B\u044F 17+ \u043C\u043E\u0436\u043D\u043E \u043A\u043E\u043B\u043B\u0435\u0434\u0436/\u0443\u043D\u0438\u0432\u0435\u0440 \u0435\u0441\u043B\u0438 \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442 persona. phoneAvailable=false \u0442\u043E\u043B\u044C\u043A\u043E \u043A\u043E\u0433\u0434\u0430 \u0442\u0435\u043B\u0435\u0444\u043E\u043D \u0440\u0435\u0430\u043B\u044C\u043D\u043E \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D.` : "";
1895
- return `\u0418\u043C\u044F: ${cfg.name}, ${cfg.age}. \u0421\u0442\u0430\u0434\u0438\u044F \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439 \u0441 \u043D\u0438\u043C: ${cfg.stage}. \u0427\u0430\u0441\u043E\u0432\u043E\u0439 \u043F\u043E\u044F\u0441: ${cfg.tz}. \u0421\u0435\u0433\u043E\u0434\u043D\u044F: ${weekday}, ${dateLocal}.${conflictNote}${busyNote}
2065
+ const recentNote = recentEvents.length > 0 ? `
2066
+
2067
+ \u0427\u0422\u041E \u0423\u0416\u0415 \u0411\u042B\u041B\u041E \u0412 \u041F\u0420\u0415\u0414\u042B\u0414\u0423\u0429\u0418\u0415 \u0414\u041D\u0418 (\u041D\u0415 \u041F\u041E\u0412\u0422\u041E\u0420\u042F\u0422\u042C \u2014 \u044D\u0442\u043E \u0440\u043E\u0432\u043D\u043E \u0442\u0435 \u0436\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F, \u043D\u0435\u043B\u044C\u0437\u044F \u043D\u0438 \u0438\u0445, \u043D\u0438 \u0438\u0445 \u043F\u0435\u0440\u0435\u0444\u0440\u0430\u0437):
2068
+ ${recentEvents.map((e) => `- ${e}`).join("\n")}
2069
+
2070
+ \u0413\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0439 \u0441\u0435\u0433\u043E\u0434\u043D\u044F \u0421\u0412\u0415\u0416\u0418\u0415 \u0441\u043E\u0431\u044B\u0442\u0438\u044F, \u043E\u0442\u043B\u0438\u0447\u043D\u044B\u0435 \u043E\u0442 \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u043D\u044B\u0445 \u043F\u043E \u0441\u0443\u0442\u0438 (\u0430 \u043D\u0435 \u043F\u043E \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043A\u0435). \u0415\u0441\u043B\u0438 \u0438\u0434\u0435\u044F \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442\u0441\u044F \u2014 \u043F\u0440\u0438\u0434\u0443\u043C\u0430\u0439 \u0434\u0440\u0443\u0433\u0443\u044E.` : "";
2071
+ return `\u0418\u043C\u044F: ${cfg.name}, ${cfg.age}. \u0421\u0442\u0430\u0434\u0438\u044F \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439 \u0441 \u043D\u0438\u043C: ${cfg.stage}. \u0427\u0430\u0441\u043E\u0432\u043E\u0439 \u043F\u043E\u044F\u0441: ${cfg.tz}. \u0421\u0435\u0433\u043E\u0434\u043D\u044F: ${weekday}, ${dateLocal}.${conflictNote}${busyNote}${recentNote}
1896
2072
 
1897
2073
  \u041F\u0435\u0440\u0441\u043E\u043D\u0430 (\u0432\u044B\u0436\u0438\u043C\u043A\u0430):
1898
2074
  ${persona.slice(0, 1200)}
@@ -1925,8 +2101,8 @@ phoneAvailable=false \u043A\u043E\u0433\u0434\u0430: \u0441\u043F\u0438\u0442, \
1925
2101
  }
1926
2102
  async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(), conflict = null) {
1927
2103
  const dateLocal = localDateStr(cfg.tz, now);
1928
- const path14 = `daily-life/${dateLocal}.md`;
1929
- const existing = await readMd(cfg.slug, path14);
2104
+ const path15 = `daily-life/${dateLocal}.md`;
2105
+ const existing = await readMd(cfg.slug, path15);
1930
2106
  if (existing) {
1931
2107
  try {
1932
2108
  const m = existing.match(/<!--daily:(.+?)-->/s);
@@ -1936,12 +2112,13 @@ async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(
1936
2112
  }
1937
2113
  const persona = await readMd(cfg.slug, "persona.md");
1938
2114
  const weekday = localWeekday(cfg.tz, now);
2115
+ const recentEvents = await loadRecentEvents(cfg.slug, dateLocal, 5);
1939
2116
  let dl;
1940
2117
  try {
1941
2118
  const raw = await llm.chat(
1942
2119
  [
1943
2120
  { role: "system", content: SYS },
1944
- { role: "user", content: buildPrompt(cfg, persona, weekday, dateLocal, conflict) }
2121
+ { role: "user", content: buildPrompt(cfg, persona, weekday, dateLocal, conflict, recentEvents) }
1945
2122
  ],
1946
2123
  { temperature: 0.95, maxTokens: 3500, json: true }
1947
2124
  );
@@ -1958,7 +2135,7 @@ async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(
1958
2135
  dl = { dateLocal, vibe: "\u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u0434\u0435\u043D\u044C", blocks: [], events: [], wants: [] };
1959
2136
  }
1960
2137
  const human = renderDailyLifeHuman(dl);
1961
- await writeMd(cfg.slug, path14, `${human}
2138
+ await writeMd(cfg.slug, path15, `${human}
1962
2139
 
1963
2140
  <!--daily:${JSON.stringify(dl)}-->
1964
2141
  `);
@@ -1992,6 +2169,32 @@ function currentBlock(dl, tz, now = /* @__PURE__ */ new Date()) {
1992
2169
  const h = localHour(tz, now);
1993
2170
  return dl.blocks?.find((b) => h >= b.fromHour && h < b.toHour) ?? dl.blocks?.[dl.blocks.length - 1];
1994
2171
  }
2172
+ async function loadRecentEvents(slug, todayLocal, days) {
2173
+ try {
2174
+ const dir = path5.join(profileDir(slug), "daily-life");
2175
+ const files = await fs3.readdir(dir).catch(() => []);
2176
+ const recent = files.filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).map((f) => f.replace(/\.md$/, "")).filter((d) => d < todayLocal).sort().slice(-days);
2177
+ const out = [];
2178
+ for (const day of recent) {
2179
+ const raw = await readMd(slug, `daily-life/${day}.md`);
2180
+ if (!raw) continue;
2181
+ const m = raw.match(/<!--daily:(.+?)-->/s);
2182
+ if (!m || !m[1]) continue;
2183
+ try {
2184
+ const parsed = JSON.parse(m[1]);
2185
+ if (Array.isArray(parsed.events)) {
2186
+ for (const e of parsed.events) {
2187
+ if (typeof e === "string" && e.trim()) out.push(e.trim());
2188
+ }
2189
+ }
2190
+ } catch {
2191
+ }
2192
+ }
2193
+ return out.slice(-20);
2194
+ } catch {
2195
+ return [];
2196
+ }
2197
+ }
1995
2198
  function dailyLifePromptFragment(dl, tz, now = /* @__PURE__ */ new Date()) {
1996
2199
  const b = currentBlock(dl, tz, now);
1997
2200
  const parts = [
@@ -2023,11 +2226,11 @@ var init_daily_life = __esm({
2023
2226
  });
2024
2227
 
2025
2228
  // src/engine/conflict.ts
2026
- import { promises as fs3 } from "fs";
2027
- import path5 from "path";
2229
+ import { promises as fs4 } from "fs";
2230
+ import path6 from "path";
2028
2231
  async function readConflict(slug) {
2029
2232
  try {
2030
- const raw = await fs3.readFile(path5.join(profileDir(slug), "conflict.json"), "utf8");
2233
+ const raw = await fs4.readFile(path6.join(profileDir(slug), "conflict.json"), "utf8");
2031
2234
  const parsed = JSON.parse(raw);
2032
2235
  return { ...empty, ...parsed, history: parsed.history ?? [] };
2033
2236
  } catch {
@@ -2036,7 +2239,7 @@ async function readConflict(slug) {
2036
2239
  }
2037
2240
  async function writeConflict(slug, c) {
2038
2241
  await ensureProfile(slug);
2039
- await fs3.writeFile(path5.join(profileDir(slug), "conflict.json"), JSON.stringify(c, null, 2), "utf8");
2242
+ await fs4.writeFile(path6.join(profileDir(slug), "conflict.json"), JSON.stringify(c, null, 2), "utf8");
2040
2243
  }
2041
2244
  function activeConflict(c, now = /* @__PURE__ */ new Date()) {
2042
2245
  const cold = c.coldUntil ? new Date(c.coldUntil).getTime() > now.getTime() : false;
@@ -2160,9 +2363,9 @@ var init_conflict = __esm({
2160
2363
  });
2161
2364
 
2162
2365
  // src/engine/memory-palace.ts
2163
- import { promises as fs4 } from "fs";
2366
+ import { promises as fs5 } from "fs";
2164
2367
  import { createHash } from "crypto";
2165
- import path6 from "path";
2368
+ import path7 from "path";
2166
2369
  function wordsFrom(text) {
2167
2370
  return [...text.toLowerCase().matchAll(/[a-zа-яё0-9]{3,}/gi)].map((match) => match[0]).filter((token) => !STOP_WORDS.has(token));
2168
2371
  }
@@ -2292,9 +2495,9 @@ async function ensureDefaults(cfg) {
2292
2495
  ["time/promises.md", "# promises\n"],
2293
2496
  ["memory/uncertain.md", "# uncertain\n"]
2294
2497
  ];
2295
- await Promise.all(defaults.map(async ([path14, content]) => {
2296
- const current = await readMd(cfg.slug, path14);
2297
- if (!current.trim()) await writeMd(cfg.slug, path14, content + "\n");
2498
+ await Promise.all(defaults.map(async ([path15, content]) => {
2499
+ const current = await readMd(cfg.slug, path15);
2500
+ if (!current.trim()) await writeMd(cfg.slug, path15, content + "\n");
2298
2501
  }));
2299
2502
  }
2300
2503
  function scoreDrawer(drawer, tokens, query) {
@@ -2331,7 +2534,7 @@ async function listPalaceDrawers(cfg) {
2331
2534
  }
2332
2535
  async function listChildDirs(slug, rel) {
2333
2536
  try {
2334
- const entries = await fs4.readdir(path6.join(profileDir(slug), rel), { withFileTypes: true });
2537
+ const entries = await fs5.readdir(path7.join(profileDir(slug), rel), { withFileTypes: true });
2335
2538
  return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
2336
2539
  } catch {
2337
2540
  return [];
@@ -2339,7 +2542,7 @@ async function listChildDirs(slug, rel) {
2339
2542
  }
2340
2543
  async function listChildFiles(slug, rel) {
2341
2544
  try {
2342
- const entries = await fs4.readdir(path6.join(profileDir(slug), rel), { withFileTypes: true });
2545
+ const entries = await fs5.readdir(path7.join(profileDir(slug), rel), { withFileTypes: true });
2343
2546
  return entries.filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
2344
2547
  } catch {
2345
2548
  return [];
@@ -4359,13 +4562,13 @@ var init_daily_summarizer = __esm({
4359
4562
  });
4360
4563
 
4361
4564
  // src/engine/stickers.ts
4362
- import { promises as fs5 } from "fs";
4363
- import path7 from "path";
4565
+ import { promises as fs6 } from "fs";
4566
+ import path8 from "path";
4364
4567
  async function libraryPath(cfg) {
4365
4568
  const rel = "stickers/library.md";
4366
4569
  const current = await readMd(cfg.slug, rel);
4367
4570
  if (!current.trim()) await writeMd(cfg.slug, rel, DEFAULT_LIBRARY);
4368
- return path7.join(profileDir(cfg.slug), rel);
4571
+ return path8.join(profileDir(cfg.slug), rel);
4369
4572
  }
4370
4573
  async function listStickers(cfg) {
4371
4574
  await libraryPath(cfg);
@@ -4387,8 +4590,8 @@ async function addStickerToLibrary(cfg, fileId, emoji = "", tags = []) {
4387
4590
  await libraryPath(cfg);
4388
4591
  const existing = await listStickers(cfg);
4389
4592
  if (existing.some((s) => s.fileId === fileId)) return;
4390
- const p = path7.join(profileDir(cfg.slug), "stickers/library.md");
4391
- await fs5.appendFile(p, `${fileId} | ${emoji} | ${tags.join(",")}
4593
+ const p = path8.join(profileDir(cfg.slug), "stickers/library.md");
4594
+ await fs6.appendFile(p, `${fileId} | ${emoji} | ${tags.join(",")}
4392
4595
  `, "utf8");
4393
4596
  }
4394
4597
  var DEFAULT_LIBRARY;
@@ -6728,21 +6931,21 @@ var init_memory_palace2 = __esm({
6728
6931
  });
6729
6932
 
6730
6933
  // src/migrations/index.ts
6731
- import { promises as fs6 } from "fs";
6732
- import path8 from "path";
6934
+ import { promises as fs7 } from "fs";
6935
+ import path9 from "path";
6733
6936
  import { readFileSync } from "fs";
6734
6937
  import { fileURLToPath as fileURLToPath3 } from "url";
6735
6938
  async function readMigrationState() {
6736
6939
  try {
6737
- const raw = await fs6.readFile(MIGRATIONS_FILE(), "utf8");
6940
+ const raw = await fs7.readFile(MIGRATIONS_FILE(), "utf8");
6738
6941
  return JSON.parse(raw);
6739
6942
  } catch {
6740
6943
  return { appliedMigrations: [], lastRunVersion: "0.0.0", lastRunAt: "" };
6741
6944
  }
6742
6945
  }
6743
6946
  async function writeMigrationState(state) {
6744
- await fs6.mkdir(DATA_ROOT, { recursive: true });
6745
- await fs6.writeFile(MIGRATIONS_FILE(), JSON.stringify(state, null, 2), "utf8");
6947
+ await fs7.mkdir(DATA_ROOT, { recursive: true });
6948
+ await fs7.writeFile(MIGRATIONS_FILE(), JSON.stringify(state, null, 2), "utf8");
6746
6949
  }
6747
6950
  async function pendingMigrations() {
6748
6951
  const state = await readMigrationState();
@@ -6835,15 +7038,15 @@ function formatUpdateWarnings(warnings) {
6835
7038
  function currentVersion() {
6836
7039
  try {
6837
7040
  const here = fileURLToPath3(import.meta.url);
6838
- let dir = path8.dirname(here);
7041
+ let dir = path9.dirname(here);
6839
7042
  for (let i = 0; i < 5; i++) {
6840
- const candidate = path8.join(dir, "package.json");
7043
+ const candidate = path9.join(dir, "package.json");
6841
7044
  try {
6842
7045
  const pkg = JSON.parse(readFileSync(candidate, "utf8"));
6843
7046
  if (pkg.name === "@thesashadev/girl-agent" && pkg.version) return pkg.version;
6844
7047
  } catch {
6845
7048
  }
6846
- dir = path8.dirname(dir);
7049
+ dir = path9.dirname(dir);
6847
7050
  }
6848
7051
  return "unknown";
6849
7052
  } catch {
@@ -6859,7 +7062,7 @@ var init_migrations = __esm({
6859
7062
  init_add_use_wss_default();
6860
7063
  init_ensure_communication_md();
6861
7064
  init_memory_palace2();
6862
- MIGRATIONS_FILE = () => path8.join(DATA_ROOT, ".migrations.json");
7065
+ MIGRATIONS_FILE = () => path9.join(DATA_ROOT, ".migrations.json");
6863
7066
  ALL_MIGRATIONS = [
6864
7067
  migration0112,
6865
7068
  migration0113,
@@ -7021,23 +7224,23 @@ __export(addons_exports, {
7021
7224
  updateSettings: () => updateSettings,
7022
7225
  validateManifest: () => validateManifest
7023
7226
  });
7024
- import { promises as fs9 } from "fs";
7025
- import path11 from "path";
7227
+ import { promises as fs10 } from "fs";
7228
+ import path12 from "path";
7026
7229
  import os3 from "os";
7027
7230
  import { execFile } from "child_process";
7028
7231
  import { promisify } from "util";
7029
7232
  function addonsDir() {
7030
- const root = process.env.GIRL_AGENT_DATA ? path11.resolve(process.env.GIRL_AGENT_DATA, "..") : path11.join(os3.homedir(), ".local", "share", "girl-agent");
7031
- return path11.join(root, "addons");
7233
+ const root = process.env.GIRL_AGENT_DATA ? path12.resolve(process.env.GIRL_AGENT_DATA, "..") : path12.join(os3.homedir(), ".local", "share", "girl-agent");
7234
+ return path12.join(root, "addons");
7032
7235
  }
7033
7236
  async function ensureDir() {
7034
7237
  const dir = addonsDir();
7035
- await fs9.mkdir(dir, { recursive: true });
7238
+ await fs10.mkdir(dir, { recursive: true });
7036
7239
  return dir;
7037
7240
  }
7038
7241
  async function readJsonOrEmpty(p, fallback) {
7039
7242
  try {
7040
- const raw = await fs9.readFile(p, "utf8");
7243
+ const raw = await fs10.readFile(p, "utf8");
7041
7244
  return JSON.parse(raw);
7042
7245
  } catch {
7043
7246
  return fallback;
@@ -7045,12 +7248,12 @@ async function readJsonOrEmpty(p, fallback) {
7045
7248
  }
7046
7249
  async function listInstalled() {
7047
7250
  const dir = await ensureDir();
7048
- const indexPath = path11.join(dir, "installed.json");
7251
+ const indexPath = path12.join(dir, "installed.json");
7049
7252
  return await readJsonOrEmpty(indexPath, []);
7050
7253
  }
7051
7254
  async function writeInstalled(list) {
7052
7255
  const dir = await ensureDir();
7053
- await fs9.writeFile(path11.join(dir, "installed.json"), JSON.stringify(list, null, 2), "utf8");
7256
+ await fs10.writeFile(path12.join(dir, "installed.json"), JSON.stringify(list, null, 2), "utf8");
7054
7257
  }
7055
7258
  async function fetchRegistry() {
7056
7259
  try {
@@ -7064,17 +7267,17 @@ async function fetchRegistry() {
7064
7267
  }
7065
7268
  }
7066
7269
  async function unpackGaa(gaaPath) {
7067
- const tmpDir = path11.join(os3.tmpdir(), `gaa-${Date.now()}-${Math.random().toString(36).slice(2)}`);
7068
- await fs9.mkdir(tmpDir, { recursive: true });
7270
+ const tmpDir = path12.join(os3.tmpdir(), `gaa-${Date.now()}-${Math.random().toString(36).slice(2)}`);
7271
+ await fs10.mkdir(tmpDir, { recursive: true });
7069
7272
  await execFileAsync("unzip", ["-o", "-q", gaaPath, "-d", tmpDir]);
7070
- const entries = await fs9.readdir(tmpDir);
7273
+ const entries = await fs10.readdir(tmpDir);
7071
7274
  if (entries.length === 1) {
7072
- const sub = path11.join(tmpDir, entries[0]);
7073
- const st = await fs9.stat(sub);
7275
+ const sub = path12.join(tmpDir, entries[0]);
7276
+ const st = await fs10.stat(sub);
7074
7277
  if (st.isDirectory()) {
7075
- const innerManifest = path11.join(sub, "manifest.json");
7278
+ const innerManifest = path12.join(sub, "manifest.json");
7076
7279
  try {
7077
- await fs9.access(innerManifest);
7280
+ await fs10.access(innerManifest);
7078
7281
  return sub;
7079
7282
  } catch {
7080
7283
  }
@@ -7083,34 +7286,34 @@ async function unpackGaa(gaaPath) {
7083
7286
  return tmpDir;
7084
7287
  }
7085
7288
  async function packGaa(addonDir, outputPath) {
7086
- const manifestPath = path11.join(addonDir, "manifest.json");
7087
- const manifestRaw = await fs9.readFile(manifestPath, "utf8");
7289
+ const manifestPath = path12.join(addonDir, "manifest.json");
7290
+ const manifestRaw = await fs10.readFile(manifestPath, "utf8");
7088
7291
  const manifest = JSON.parse(manifestRaw);
7089
7292
  validateManifest(manifest);
7090
- const out = outputPath ?? path11.join(process.cwd(), `${manifest.id}.gaa`);
7293
+ const out = outputPath ?? path12.join(process.cwd(), `${manifest.id}.gaa`);
7091
7294
  try {
7092
- await fs9.unlink(out);
7295
+ await fs10.unlink(out);
7093
7296
  } catch {
7094
7297
  }
7095
- const dirName = path11.basename(addonDir);
7096
- const parentDir = path11.dirname(addonDir);
7298
+ const dirName = path12.basename(addonDir);
7299
+ const parentDir = path12.dirname(addonDir);
7097
7300
  await execFileAsync("zip", ["-r", "-q", out, dirName], { cwd: parentDir });
7098
7301
  return out;
7099
7302
  }
7100
7303
  async function installFromDir(addonDir, profileSlug, source = "local") {
7101
- const manifestPath = path11.join(addonDir, "manifest.json");
7102
- const manifestRaw = await fs9.readFile(manifestPath, "utf8");
7304
+ const manifestPath = path12.join(addonDir, "manifest.json");
7305
+ const manifestRaw = await fs10.readFile(manifestPath, "utf8");
7103
7306
  const manifest = JSON.parse(manifestRaw);
7104
7307
  validateManifest(manifest);
7105
7308
  const applied = [];
7106
7309
  const installedFiles = [];
7107
- const filesDir = path11.join(addonDir, "files");
7310
+ const filesDir = path12.join(addonDir, "files");
7108
7311
  try {
7109
- const fileStat = await fs9.stat(filesDir);
7312
+ const fileStat = await fs10.stat(filesDir);
7110
7313
  if (fileStat.isDirectory() && profileSlug) {
7111
7314
  const fileEntries = await walkDir(filesDir);
7112
7315
  for (const relPath of fileEntries) {
7113
- const content = await fs9.readFile(path11.join(filesDir, relPath), "utf8");
7316
+ const content = await fs10.readFile(path12.join(filesDir, relPath), "utf8");
7114
7317
  await writeMd(profileSlug, relPath, content);
7115
7318
  installedFiles.push(relPath);
7116
7319
  }
@@ -7118,9 +7321,9 @@ async function installFromDir(addonDir, profileSlug, source = "local") {
7118
7321
  }
7119
7322
  } catch {
7120
7323
  }
7121
- const patchPath = path11.join(addonDir, "config.patch.json");
7324
+ const patchPath = path12.join(addonDir, "config.patch.json");
7122
7325
  try {
7123
- const patchRaw = await fs9.readFile(patchPath, "utf8");
7326
+ const patchRaw = await fs10.readFile(patchPath, "utf8");
7124
7327
  const patch = JSON.parse(patchRaw);
7125
7328
  if (profileSlug) {
7126
7329
  const cfg = await readConfig(profileSlug);
@@ -7132,11 +7335,11 @@ async function installFromDir(addonDir, profileSlug, source = "local") {
7132
7335
  }
7133
7336
  } catch {
7134
7337
  }
7135
- const codePatchPath = path11.join(addonDir, "code.patch");
7338
+ const codePatchPath = path12.join(addonDir, "code.patch");
7136
7339
  try {
7137
- const patchContent = await fs9.readFile(codePatchPath, "utf8");
7340
+ const patchContent = await fs10.readFile(codePatchPath, "utf8");
7138
7341
  if (patchContent.trim()) {
7139
- const projectRoot = path11.resolve(import.meta.url.replace("file://", ""), "../../../");
7342
+ const projectRoot = path12.resolve(import.meta.url.replace("file://", ""), "../../../");
7140
7343
  try {
7141
7344
  await execFileAsync("git", ["apply", "--check", codePatchPath], { cwd: projectRoot });
7142
7345
  await execFileAsync("git", ["apply", codePatchPath], { cwd: projectRoot });
@@ -7147,25 +7350,25 @@ async function installFromDir(addonDir, profileSlug, source = "local") {
7147
7350
  }
7148
7351
  } catch {
7149
7352
  }
7150
- const themePath = path11.join(addonDir, "theme.css");
7353
+ const themePath = path12.join(addonDir, "theme.css");
7151
7354
  try {
7152
- const css = await fs9.readFile(themePath, "utf8");
7355
+ const css = await fs10.readFile(themePath, "utf8");
7153
7356
  const dir2 = await ensureDir();
7154
- await fs9.writeFile(path11.join(dir2, `theme-${manifest.id}.css`), css, "utf8");
7357
+ await fs10.writeFile(path12.join(dir2, `theme-${manifest.id}.css`), css, "utf8");
7155
7358
  applied.push("\u0442\u0435\u043C\u0430 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430");
7156
7359
  } catch {
7157
7360
  }
7158
7361
  const dir = await ensureDir();
7159
- const addonStorePath = path11.join(dir, manifest.id);
7160
- await fs9.mkdir(addonStorePath, { recursive: true });
7161
- await fs9.copyFile(manifestPath, path11.join(addonStorePath, "manifest.json"));
7362
+ const addonStorePath = path12.join(dir, manifest.id);
7363
+ await fs10.mkdir(addonStorePath, { recursive: true });
7364
+ await fs10.copyFile(manifestPath, path12.join(addonStorePath, "manifest.json"));
7162
7365
  const allFiles = await walkDir(addonDir);
7163
7366
  for (const f of allFiles) {
7164
7367
  if (f === "manifest.json") continue;
7165
- const src = path11.join(addonDir, f);
7166
- const dst = path11.join(addonStorePath, f);
7167
- await fs9.mkdir(path11.dirname(dst), { recursive: true });
7168
- await fs9.copyFile(src, dst);
7368
+ const src = path12.join(addonDir, f);
7369
+ const dst = path12.join(addonStorePath, f);
7370
+ await fs10.mkdir(path12.dirname(dst), { recursive: true });
7371
+ await fs10.copyFile(src, dst);
7169
7372
  }
7170
7373
  const list = await listInstalled();
7171
7374
  const item = {
@@ -7186,7 +7389,7 @@ async function installFromGaa(gaaPath, profileSlug) {
7186
7389
  try {
7187
7390
  return await installFromDir(dir, profileSlug, "file");
7188
7391
  } finally {
7189
- await fs9.rm(dir, { recursive: true, force: true }).catch(() => {
7392
+ await fs10.rm(dir, { recursive: true, force: true }).catch(() => {
7190
7393
  });
7191
7394
  }
7192
7395
  }
@@ -7209,12 +7412,12 @@ async function installFromRegistry(id, registryManifest, profileSlug) {
7209
7412
  const res = await fetch(url, { signal: AbortSignal.timeout(3e4) });
7210
7413
  if (!res.ok) throw new Error(`\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043A\u0430\u0447\u0430\u0442\u044C \u0430\u0434\u0434\u043E\u043D: HTTP ${res.status}`);
7211
7414
  const buf = Buffer.from(await res.arrayBuffer());
7212
- const tmpGaa = path11.join(os3.tmpdir(), `${id}-${Date.now()}.gaa`);
7213
- await fs9.writeFile(tmpGaa, buf);
7415
+ const tmpGaa = path12.join(os3.tmpdir(), `${id}-${Date.now()}.gaa`);
7416
+ await fs10.writeFile(tmpGaa, buf);
7214
7417
  try {
7215
7418
  return await installFromGaa(tmpGaa, profileSlug);
7216
7419
  } finally {
7217
- await fs9.unlink(tmpGaa).catch(() => {
7420
+ await fs10.unlink(tmpGaa).catch(() => {
7218
7421
  });
7219
7422
  }
7220
7423
  }
@@ -7223,11 +7426,11 @@ async function uninstall(id) {
7223
7426
  const next = list.filter((a) => a.manifest.id !== id);
7224
7427
  if (next.length === list.length) return false;
7225
7428
  const dir = addonsDir();
7226
- const addonStore = path11.join(dir, id);
7227
- await fs9.rm(addonStore, { recursive: true, force: true }).catch(() => {
7429
+ const addonStore = path12.join(dir, id);
7430
+ await fs10.rm(addonStore, { recursive: true, force: true }).catch(() => {
7228
7431
  });
7229
- const themePath = path11.join(dir, `theme-${id}.css`);
7230
- await fs9.unlink(themePath).catch(() => {
7432
+ const themePath = path12.join(dir, `theme-${id}.css`);
7433
+ await fs10.unlink(themePath).catch(() => {
7231
7434
  });
7232
7435
  await writeInstalled(next);
7233
7436
  return true;
@@ -7258,11 +7461,11 @@ function validateManifest(m) {
7258
7461
  }
7259
7462
  async function walkDir(dir, prefix = "") {
7260
7463
  const result = [];
7261
- const entries = await fs9.readdir(dir, { withFileTypes: true });
7464
+ const entries = await fs10.readdir(dir, { withFileTypes: true });
7262
7465
  for (const e of entries) {
7263
7466
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
7264
7467
  if (e.isDirectory()) {
7265
- result.push(...await walkDir(path11.join(dir, e.name), rel));
7468
+ result.push(...await walkDir(path12.join(dir, e.name), rel));
7266
7469
  } else {
7267
7470
  result.push(rel);
7268
7471
  }
@@ -7282,16 +7485,16 @@ function deepMerge(target, source) {
7282
7485
  }
7283
7486
  async function getAddonReadme(id) {
7284
7487
  const dir = addonsDir();
7285
- const readmePath = path11.join(dir, id, "README.md");
7488
+ const readmePath = path12.join(dir, id, "README.md");
7286
7489
  try {
7287
- return await fs9.readFile(readmePath, "utf8");
7490
+ return await fs10.readFile(readmePath, "utf8");
7288
7491
  } catch {
7289
7492
  return null;
7290
7493
  }
7291
7494
  }
7292
7495
  async function getAddonFiles(id) {
7293
7496
  const dir = addonsDir();
7294
- const addonDir = path11.join(dir, id);
7497
+ const addonDir = path12.join(dir, id);
7295
7498
  try {
7296
7499
  return await walkDir(addonDir);
7297
7500
  } catch {
@@ -7335,9 +7538,9 @@ var HttpError = class extends Error {
7335
7538
  };
7336
7539
  var Router = class {
7337
7540
  routes = [];
7338
- add(method, path14, handler) {
7541
+ add(method, path15, handler) {
7339
7542
  const paramNames = [];
7340
- const parts = path14.split("/").map((part) => {
7543
+ const parts = path15.split("/").map((part) => {
7341
7544
  if (part.startsWith(":")) {
7342
7545
  paramNames.push(part.slice(1));
7343
7546
  return "([^/]+)";
@@ -7352,20 +7555,20 @@ var Router = class {
7352
7555
  handler
7353
7556
  });
7354
7557
  }
7355
- get(path14, h) {
7356
- this.add("GET", path14, h);
7558
+ get(path15, h) {
7559
+ this.add("GET", path15, h);
7357
7560
  }
7358
- post(path14, h) {
7359
- this.add("POST", path14, h);
7561
+ post(path15, h) {
7562
+ this.add("POST", path15, h);
7360
7563
  }
7361
- put(path14, h) {
7362
- this.add("PUT", path14, h);
7564
+ put(path15, h) {
7565
+ this.add("PUT", path15, h);
7363
7566
  }
7364
- delete(path14, h) {
7365
- this.add("DELETE", path14, h);
7567
+ delete(path15, h) {
7568
+ this.add("DELETE", path15, h);
7366
7569
  }
7367
- patch(path14, h) {
7368
- this.add("PATCH", path14, h);
7570
+ patch(path15, h) {
7571
+ this.add("PATCH", path15, h);
7369
7572
  }
7370
7573
  match(method, pathname) {
7371
7574
  for (const r of this.routes) {
@@ -7685,6 +7888,60 @@ init_runtime_bus();
7685
7888
  // src/webui/routes/profiles.ts
7686
7889
  init_esm_shims();
7687
7890
  init_md();
7891
+
7892
+ // src/telegram/proxy-parse.ts
7893
+ init_esm_shims();
7894
+ function parseTelegramProxyInput(raw) {
7895
+ if (raw == null) return void 0;
7896
+ if (typeof raw === "object") {
7897
+ if (!raw.ip || !raw.port) return void 0;
7898
+ if (raw.MTProxy && raw.secret) {
7899
+ return { ip: raw.ip, port: raw.port, MTProxy: true, secret: raw.secret, timeout: raw.timeout };
7900
+ }
7901
+ const socksType = raw.socksType === 4 ? 4 : 5;
7902
+ return {
7903
+ ip: raw.ip,
7904
+ port: raw.port,
7905
+ socksType,
7906
+ username: raw.username,
7907
+ password: raw.password,
7908
+ timeout: raw.timeout
7909
+ };
7910
+ }
7911
+ const trimmed = raw.trim();
7912
+ if (!trimmed) return void 0;
7913
+ try {
7914
+ const url = new URL(trimmed);
7915
+ const isMtproxy = url.protocol === "tg:" && url.hostname === "proxy" || /^https?:$/.test(url.protocol) && url.hostname === "t.me" && url.pathname.replace(/^\//, "") === "proxy";
7916
+ if (isMtproxy) {
7917
+ const ip = url.searchParams.get("server")?.trim();
7918
+ const port2 = Number(url.searchParams.get("port"));
7919
+ const secret = url.searchParams.get("secret")?.trim();
7920
+ if (!ip || !Number.isInteger(port2) || port2 <= 0 || !secret) return void 0;
7921
+ return { ip, port: port2, MTProxy: true, secret };
7922
+ }
7923
+ if (url.protocol === "socks4:" || url.protocol === "socks5:") {
7924
+ const socksType = url.protocol === "socks4:" ? 4 : 5;
7925
+ const port2 = Number(url.port);
7926
+ if (!url.hostname || !Number.isInteger(port2) || port2 <= 0) return void 0;
7927
+ return {
7928
+ ip: url.hostname,
7929
+ port: port2,
7930
+ socksType,
7931
+ username: url.username ? decodeURIComponent(url.username) : void 0,
7932
+ password: url.password ? decodeURIComponent(url.password) : void 0
7933
+ };
7934
+ }
7935
+ return void 0;
7936
+ } catch {
7937
+ }
7938
+ const [host, portRaw] = trimmed.split(":");
7939
+ const port = Number(portRaw);
7940
+ if (!host || !Number.isInteger(port) || port <= 0) return void 0;
7941
+ return { ip: host, port, socksType: 5 };
7942
+ }
7943
+
7944
+ // src/webui/routes/profiles.ts
7688
7945
  init_runtime_bus();
7689
7946
  init_stages();
7690
7947
 
@@ -7993,8 +8250,8 @@ function fallbackBusySchedule(name, age) {
7993
8250
  init_llm();
7994
8251
  init_llm_update();
7995
8252
  init_llm2();
7996
- import { promises as fs7 } from "fs";
7997
- import path9 from "path";
8253
+ import { promises as fs8 } from "fs";
8254
+ import path10 from "path";
7998
8255
  var MEMORY_FILES = [
7999
8256
  "persona.md",
8000
8257
  "speech.md",
@@ -8011,7 +8268,7 @@ var MEMORY_FILES = [
8011
8268
  function isAllowedMemoryPath(p) {
8012
8269
  if (!p || typeof p !== "string") return false;
8013
8270
  if (p.includes("..")) return false;
8014
- if (path9.isAbsolute(p)) return false;
8271
+ if (path10.isAbsolute(p)) return false;
8015
8272
  if (p.startsWith("config.json")) return false;
8016
8273
  if (p.startsWith("agenda.json")) return false;
8017
8274
  if (MEMORY_FILES.includes(p)) return true;
@@ -8056,6 +8313,13 @@ function registerProfileRoutes(r) {
8056
8313
  if (!incoming || typeof incoming !== "object") throw new HttpError(400, "invalid body");
8057
8314
  const merged = { ...cur, ...incoming, slug: cur.slug };
8058
8315
  if (incoming.ownerId !== void 0) merged.ownerId = normalizeOwnerId(incoming.ownerId);
8316
+ if (incoming.telegram) {
8317
+ merged.telegram = {
8318
+ ...cur.telegram,
8319
+ ...incoming.telegram,
8320
+ proxy: parseTelegramProxyInput(incoming.telegram.proxy)
8321
+ };
8322
+ }
8059
8323
  await writeConfig(merged);
8060
8324
  return { config: merged };
8061
8325
  });
@@ -8065,6 +8329,7 @@ function registerProfileRoutes(r) {
8065
8329
  const slug = data.slug || slugify(data.name);
8066
8330
  const existing = await readConfig(slug);
8067
8331
  if (existing) throw new HttpError(409, `profile already exists: ${slug}`);
8332
+ const incomingTg = data.telegram ?? {};
8068
8333
  const cfg = {
8069
8334
  slug,
8070
8335
  name: data.name,
@@ -8074,7 +8339,10 @@ function registerProfileRoutes(r) {
8074
8339
  mode: data.mode ?? "bot",
8075
8340
  stage: data.stage ?? "tg-given-cold",
8076
8341
  llm: data.llm ?? { presetId: "claudehub", proto: "anthropic", apiKey: "", model: "claude-sonnet-4.6" },
8077
- telegram: data.telegram ?? {},
8342
+ telegram: {
8343
+ ...incomingTg,
8344
+ proxy: parseTelegramProxyInput(incomingTg.proxy)
8345
+ },
8078
8346
  privacy: data.privacy ?? "owner-only",
8079
8347
  ownerId: normalizeOwnerId(data.ownerId),
8080
8348
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -8186,29 +8454,29 @@ function registerProfileRoutes(r) {
8186
8454
  const entries = [];
8187
8455
  for (const f of MEMORY_FILES) entries.push({ rel: f });
8188
8456
  try {
8189
- const dailyDir = path9.join(dir, "memory", "daily");
8190
- const list = await fs7.readdir(dailyDir);
8457
+ const dailyDir = path10.join(dir, "memory", "daily");
8458
+ const list = await fs8.readdir(dailyDir);
8191
8459
  for (const f of list) if (/^\d{4}-\d{2}-\d{2}\.md$/.test(f)) entries.push({ rel: `memory/daily/${f}` });
8192
8460
  } catch {
8193
8461
  }
8194
8462
  try {
8195
- const epDir = path9.join(dir, "memory", "episodes");
8196
- const list = await fs7.readdir(epDir);
8463
+ const epDir = path10.join(dir, "memory", "episodes");
8464
+ const list = await fs8.readdir(epDir);
8197
8465
  for (const f of list) if (/^[\w\-]{1,80}\.md$/.test(f)) entries.push({ rel: `memory/episodes/${f}` });
8198
8466
  } catch {
8199
8467
  }
8200
8468
  try {
8201
- const palaceDir = path9.join(dir, "memory", "palace");
8202
- const wings = await fs7.readdir(palaceDir, { withFileTypes: true });
8469
+ const palaceDir = path10.join(dir, "memory", "palace");
8470
+ const wings = await fs8.readdir(palaceDir, { withFileTypes: true });
8203
8471
  for (const wing of wings) {
8204
8472
  if (!wing.isDirectory() || !/^[\w\-]{1,80}$/.test(wing.name)) continue;
8205
- const halls = await fs7.readdir(path9.join(palaceDir, wing.name), { withFileTypes: true });
8473
+ const halls = await fs8.readdir(path10.join(palaceDir, wing.name), { withFileTypes: true });
8206
8474
  for (const hall of halls) {
8207
8475
  if (!hall.isDirectory() || !/^[\w\-]{1,80}$/.test(hall.name)) continue;
8208
- const rooms = await fs7.readdir(path9.join(palaceDir, wing.name, hall.name), { withFileTypes: true });
8476
+ const rooms = await fs8.readdir(path10.join(palaceDir, wing.name, hall.name), { withFileTypes: true });
8209
8477
  for (const room of rooms) {
8210
8478
  if (!room.isDirectory() || !/^[\w\-]{1,80}$/.test(room.name)) continue;
8211
- const drawers = await fs7.readdir(path9.join(palaceDir, wing.name, hall.name, room.name));
8479
+ const drawers = await fs8.readdir(path10.join(palaceDir, wing.name, hall.name, room.name));
8212
8480
  for (const drawer of drawers) {
8213
8481
  if (/^[\w\-]{1,120}\.md$/.test(drawer)) entries.push({ rel: `memory/palace/${wing.name}/${hall.name}/${room.name}/${drawer}` });
8214
8482
  }
@@ -8219,7 +8487,7 @@ function registerProfileRoutes(r) {
8219
8487
  }
8220
8488
  for (const e of entries) {
8221
8489
  try {
8222
- const stat = await fs7.stat(path9.join(dir, e.rel));
8490
+ const stat = await fs8.stat(path10.join(dir, e.rel));
8223
8491
  items.push({ path: e.rel, size: stat.size, mtime: stat.mtimeMs });
8224
8492
  } catch {
8225
8493
  }
@@ -8583,9 +8851,9 @@ function registerPresetRoutes(r) {
8583
8851
  // src/webui/routes/system.ts
8584
8852
  init_esm_shims();
8585
8853
  init_md();
8586
- import { promises as fs8 } from "fs";
8854
+ import { promises as fs9 } from "fs";
8587
8855
  import { fileURLToPath as fileURLToPath4 } from "url";
8588
- import path10 from "path";
8856
+ import path11 from "path";
8589
8857
  import os2 from "os";
8590
8858
  var cachedVersion = null;
8591
8859
  async function readPackageVersion() {
@@ -8593,15 +8861,15 @@ async function readPackageVersion() {
8593
8861
  const candidates = [];
8594
8862
  try {
8595
8863
  const here = fileURLToPath4(import.meta.url);
8596
- candidates.push(path10.resolve(path10.dirname(here), "..", "package.json"));
8597
- candidates.push(path10.resolve(path10.dirname(here), "..", "..", "package.json"));
8598
- candidates.push(path10.resolve(path10.dirname(here), "..", "..", "..", "package.json"));
8864
+ candidates.push(path11.resolve(path11.dirname(here), "..", "package.json"));
8865
+ candidates.push(path11.resolve(path11.dirname(here), "..", "..", "package.json"));
8866
+ candidates.push(path11.resolve(path11.dirname(here), "..", "..", "..", "package.json"));
8599
8867
  } catch {
8600
8868
  }
8601
- candidates.push(path10.resolve(process.cwd(), "package.json"));
8869
+ candidates.push(path11.resolve(process.cwd(), "package.json"));
8602
8870
  for (const c of candidates) {
8603
8871
  try {
8604
- const raw = await fs8.readFile(c, "utf8");
8872
+ const raw = await fs9.readFile(c, "utf8");
8605
8873
  const parsed = JSON.parse(raw);
8606
8874
  if (parsed.name === "@thesashadev/girl-agent" && parsed.version) {
8607
8875
  cachedVersion = parsed.version;
@@ -8652,8 +8920,8 @@ function registerSystemRoutes(r) {
8652
8920
  // src/webui/routes/addons.ts
8653
8921
  init_esm_shims();
8654
8922
  init_addons();
8655
- import { promises as fs10 } from "fs";
8656
- import path12 from "path";
8923
+ import { promises as fs11 } from "fs";
8924
+ import path13 from "path";
8657
8925
  import os4 from "os";
8658
8926
  function registerAddonRoutes(r) {
8659
8927
  r.get("/api/addons", async () => {
@@ -8680,13 +8948,13 @@ function registerAddonRoutes(r) {
8680
8948
  const data = body;
8681
8949
  if (!data?.gaaBase64) throw new HttpError(400, "gaaBase64 required");
8682
8950
  const buf = Buffer.from(data.gaaBase64, "base64");
8683
- const tmpPath = path12.join(os4.tmpdir(), `upload-${Date.now()}.gaa`);
8684
- await fs10.writeFile(tmpPath, buf);
8951
+ const tmpPath = path13.join(os4.tmpdir(), `upload-${Date.now()}.gaa`);
8952
+ await fs11.writeFile(tmpPath, buf);
8685
8953
  try {
8686
8954
  const result = await installFromGaa(tmpPath, data.profileSlug);
8687
8955
  return { ok: true, installed: result.addon, applied: result.applied };
8688
8956
  } finally {
8689
- await fs10.unlink(tmpPath).catch(() => {
8957
+ await fs11.unlink(tmpPath).catch(() => {
8690
8958
  });
8691
8959
  }
8692
8960
  });
@@ -8698,13 +8966,13 @@ function registerAddonRoutes(r) {
8698
8966
  const res = await fetch(url, { signal: AbortSignal.timeout(3e4) });
8699
8967
  if (!res.ok) throw new HttpError(502, `fetch failed: HTTP ${res.status}`);
8700
8968
  const buf = Buffer.from(await res.arrayBuffer());
8701
- const tmpPath = path12.join(os4.tmpdir(), `url-${Date.now()}.gaa`);
8702
- await fs10.writeFile(tmpPath, buf);
8969
+ const tmpPath = path13.join(os4.tmpdir(), `url-${Date.now()}.gaa`);
8970
+ await fs11.writeFile(tmpPath, buf);
8703
8971
  try {
8704
8972
  const result = await installFromGaa(tmpPath, data.profileSlug);
8705
8973
  return { ok: true, installed: result.addon, applied: result.applied };
8706
8974
  } finally {
8707
- await fs10.unlink(tmpPath).catch(() => {
8975
+ await fs11.unlink(tmpPath).catch(() => {
8708
8976
  });
8709
8977
  }
8710
8978
  } else {
@@ -9707,8 +9975,8 @@ function tail(text, limit) {
9707
9975
  if (text.length <= limit) return text;
9708
9976
  return text.slice(-limit);
9709
9977
  }
9710
- function setNested(obj, path14, value) {
9711
- const parts = path14.split(".");
9978
+ function setNested(obj, path15, value) {
9979
+ const parts = path15.split(".");
9712
9980
  let cur = obj;
9713
9981
  for (let i = 0; i < parts.length - 1; i++) {
9714
9982
  const p = parts[i];
@@ -9727,14 +9995,14 @@ var DEFAULT_PROXY = "https://tgproxy.girl-agent.com";
9727
9995
  function proxyUrl() {
9728
9996
  return process.env.GIRL_AGENT_AUTH_PROXY ?? DEFAULT_PROXY;
9729
9997
  }
9730
- async function post(path14, body) {
9731
- const res = await fetch(`${proxyUrl()}${path14}`, {
9998
+ async function post(path15, body) {
9999
+ const res = await fetch(`${proxyUrl()}${path15}`, {
9732
10000
  method: "POST",
9733
10001
  headers: { "Content-Type": "application/json" },
9734
10002
  body: JSON.stringify(body)
9735
10003
  });
9736
10004
  const data = await res.json();
9737
- if (!res.ok) throw new Error(data.error ?? `proxy ${path14} failed (${res.status})`);
10005
+ if (!res.ok) throw new Error(data.error ?? `proxy ${path15} failed (${res.status})`);
9738
10006
  return data;
9739
10007
  }
9740
10008
  function remoteSendCode(phone) {
@@ -10165,8 +10433,8 @@ init_esm_shims();
10165
10433
  init_llm2();
10166
10434
  init_stages();
10167
10435
  init_communication();
10168
- import fs11 from "fs/promises";
10169
- import path13 from "path";
10436
+ import fs12 from "fs/promises";
10437
+ import path14 from "path";
10170
10438
  import os6 from "os";
10171
10439
  init_md();
10172
10440
  init_runtime();
@@ -10249,7 +10517,7 @@ async function runServer(rawArgv) {
10249
10517
  }
10250
10518
  if (!args.yes) {
10251
10519
  process.stderr.write(`\u043F\u0440\u043E\u0444\u0438\u043B\u044C \u041D\u0415 \u0443\u0434\u0430\u043B\u0451\u043D: \u0434\u043E\u0431\u0430\u0432\u044C --yes \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F.
10252
- \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043B\u0435\u043D\u043E: ${path13.join(DATA_ROOT, args.profile)}
10520
+ \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043B\u0435\u043D\u043E: ${path14.join(DATA_ROOT, args.profile)}
10253
10521
  `);
10254
10522
  process.exit(1);
10255
10523
  }
@@ -10311,7 +10579,7 @@ data dir: ${DATA_ROOT}
10311
10579
  }
10312
10580
  async function persistAndMaybeStart(cfg, args) {
10313
10581
  await writeConfig(cfg);
10314
- process.stderr.write(`[server] \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D: ${path13.join(DATA_ROOT, cfg.slug)}
10582
+ process.stderr.write(`[server] \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D: ${path14.join(DATA_ROOT, cfg.slug)}
10315
10583
  `);
10316
10584
  if (cfg.llm.apiKey || findPreset(cfg.llm.presetId)?.apiKeyRequired === false) {
10317
10585
  try {
@@ -10427,10 +10695,10 @@ function configFromEnv() {
10427
10695
  };
10428
10696
  }
10429
10697
  async function loadConfigFile(file) {
10430
- const abs = path13.isAbsolute(file) ? file : path13.join(process.cwd(), file);
10698
+ const abs = path14.isAbsolute(file) ? file : path14.join(process.cwd(), file);
10431
10699
  let raw;
10432
10700
  try {
10433
- raw = await fs11.readFile(abs, "utf-8");
10701
+ raw = await fs12.readFile(abs, "utf-8");
10434
10702
  } catch (e) {
10435
10703
  process.stderr.write(`[server] \u043D\u0435 \u043C\u043E\u0433\u0443 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C ${abs}: ${e?.message ?? e}
10436
10704
  `);
@@ -10496,33 +10764,7 @@ function validateConfig(raw) {
10496
10764
  return filled;
10497
10765
  }
10498
10766
  function parseTelegramProxy(raw) {
10499
- if (!raw?.trim()) return void 0;
10500
- try {
10501
- const url = new URL(raw);
10502
- if (url.protocol === "tg:" && url.hostname === "proxy") {
10503
- const ip = url.searchParams.get("server")?.trim();
10504
- const port2 = Number(url.searchParams.get("port"));
10505
- const secret = url.searchParams.get("secret")?.trim();
10506
- if (!ip || !Number.isInteger(port2) || port2 <= 0 || !secret) return void 0;
10507
- return { ip, port: port2, MTProxy: true, secret };
10508
- }
10509
- if (url.protocol !== "socks4:" && url.protocol !== "socks5:") return void 0;
10510
- const socksType = url.protocol === "socks4:" ? 4 : 5;
10511
- const port = Number(url.port);
10512
- if (!url.hostname || !Number.isInteger(port) || port <= 0) return void 0;
10513
- return {
10514
- ip: url.hostname,
10515
- port,
10516
- socksType,
10517
- username: url.username ? decodeURIComponent(url.username) : void 0,
10518
- password: url.password ? decodeURIComponent(url.password) : void 0
10519
- };
10520
- } catch {
10521
- const [host, portRaw] = raw.split(":");
10522
- const port = Number(portRaw);
10523
- if (!host || !Number.isInteger(port) || port <= 0) return void 0;
10524
- return { ip: host, port, socksType: 5 };
10525
- }
10767
+ return parseTelegramProxyInput(raw);
10526
10768
  }
10527
10769
  function buildConfigTemplate() {
10528
10770
  const sample = {