@thesashadev/girl-agent 0.1.8 → 0.1.10
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.
- package/CHANGELOG.md +40 -0
- package/README.md +79 -15
- package/dist/cli.js +678 -44
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.10
|
|
4
|
+
|
|
5
|
+
Дата: 2026-05-07
|
|
6
|
+
|
|
7
|
+
- Merge pull request #51 from TheSashaDev/devin/1778179420-proxy-wss-support
|
|
8
|
+
- refactor: remove proxy support, WSS enabled by default
|
|
9
|
+
- feat: add numeric identifiers to stages for convenience
|
|
10
|
+
- feat: add proxy/WSS support for Telegram (fixes #38, #32)
|
|
11
|
+
|
|
12
|
+
## 0.1.9
|
|
13
|
+
|
|
14
|
+
Дата: 2026-05-07
|
|
15
|
+
|
|
16
|
+
- Merge pull request #48 from TheSashaDev/devin/1778156776-auto-release-workflow
|
|
17
|
+
- Merge pull request #50 from TheSashaDev/devin/1778176335-fix-markdown-escape
|
|
18
|
+
- fix: also switch userbot editLastMessage to HTML spoilers, remove dead escapeMarkdownV2
|
|
19
|
+
- fix(telegram): replace MarkdownV2 with HTML spoilers, plain text default (#46)
|
|
20
|
+
- feat(ci): auto-release workflow — hourly patch bump + changelog
|
|
21
|
+
- Merge pull request #47 from TheSashaDev/devin/1778156514-docker-latest-on-master
|
|
22
|
+
- fix(docker): tag latest on master pushes, not main
|
|
23
|
+
- Merge pull request #45 from TheSashaDev/devin/1778149966-fix-dockerfile-build-stage
|
|
24
|
+
- fix(docker): add build tools to build stage for arm64 native modules
|
|
25
|
+
- Merge pull request #44 from TheSashaDev/devin/1778149678-fix-dockerfile-arm64
|
|
26
|
+
- fix(docker): add build tools for native modules on alpine arm64
|
|
27
|
+
- Merge pull request #43 from TheSashaDev/devin/1778149272-fix-docker-install
|
|
28
|
+
- fix: docker install — fix branch ref (main→master), fallback to local on pull failure
|
|
29
|
+
- Merge pull request #37 from TheSashaDev/devin/1778090781-windows-installer-webui
|
|
30
|
+
- feat(server): curl|sh installer + docker image + headless server mode
|
|
31
|
+
- fix(cli): fail loudly on non-TTY terminals + catch unhandled rejections
|
|
32
|
+
- feat(installer/desktop): paste, ClaudeHub referral, tournament names, custom sleep, profile picker
|
|
33
|
+
- fix(installer): silent crash on Windows — panic=abort + windowed subsystem hid the panic
|
|
34
|
+
- fix(installer): replace empty-text widgets in progress header with Space
|
|
35
|
+
- feat(installer): bundle portable Node + cli.js, full TS-wizard parity, Cyrillic fonts
|
|
36
|
+
- perf: add release-fast profile, mold linker, windows_subsystem
|
|
37
|
+
- Добавить ссылки на Telegram канал и сообщество
|
|
38
|
+
- feat: native Windows installer + desktop app + web UI (Rust/iced)
|
|
39
|
+
- Merge pull request #36 from TheSashaDev/devin/1778089384-changelog-pr35
|
|
40
|
+
- docs: add PR 35 to changelog
|
|
41
|
+
|
|
3
42
|
## 0.1.8 — OpenAI-compatible API compatibility
|
|
4
43
|
|
|
5
44
|
Дата: 2026-05-06
|
|
@@ -7,6 +46,7 @@
|
|
|
7
46
|
- JSON-ответы теперь сначала запрашиваются через `json_schema`, с fallback на `json_object` и `text` для разных OpenAI-compatible API. (#33)
|
|
8
47
|
- LM Studio и Ollama больше не требуют реальный API ключ в wizard/headless setup.
|
|
9
48
|
- Добавлена совместимость с OpenAI-compatible прокси, которые возвращают SSE/event-stream даже на обычный chat completions запрос.
|
|
49
|
+
- Добавлена Docker-поддержка для 24/7 запуска на сервере: `Dockerfile`, `docker-compose.yml`, volume для `data` и инструкции в README. (#35)
|
|
10
50
|
|
|
11
51
|
## 0.1.7 — MarkdownV2 escaping fix
|
|
12
52
|
|
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
Со всеми проблемами и багами пишите в Issues.
|
|
11
11
|
ТГ создателя - @voided_net
|
|
12
12
|
|
|
13
|
+
Тг канал: https://t.me/GirlAgentAI/
|
|
14
|
+
Тг сообщество: https://t.me/GirlAgentAI_chat/
|
|
13
15
|
---
|
|
14
16
|
|
|
15
17
|
## Содержание
|
|
@@ -35,35 +37,97 @@
|
|
|
35
37
|
|
|
36
38
|
## Быстрый старт
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
### linux / macos / wsl — одной командой (без node на машине)
|
|
39
41
|
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
+
```sh
|
|
43
|
+
curl -fsSL https://raw.githubusercontent.com/TheSashaDev/girl-agent/master/scripts/install.sh | sh
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Что произойдёт:
|
|
47
|
+
- определит OS + arch (linux x64/arm64, macos x64/arm64, wsl)
|
|
48
|
+
- если есть docker → поставит docker-обёртку (полная изоляция от системы)
|
|
49
|
+
- иначе → скачает [official Node.js 22 LTS](https://nodejs.org) в `~/.local/share/girl-agent/runtime/` и поставит туда же `@thesashadev/girl-agent` (system node не трогается)
|
|
50
|
+
- shim-скрипт `girl-agent` положит в `~/.local/bin/girl-agent`
|
|
51
|
+
- ничего не пишется в `/usr/local/`, `sudo` не нужен
|
|
52
|
+
|
|
53
|
+
Дальше:
|
|
54
|
+
```sh
|
|
55
|
+
girl-agent # ink-визард для интерактивной первичной настройки
|
|
56
|
+
girl-agent --profile=arina # запустить готовый профиль
|
|
57
|
+
girl-agent server --help # серверный режим (без TTY, для systemd / cron / CI)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Опции установщика:
|
|
61
|
+
```sh
|
|
62
|
+
# форсировать docker
|
|
63
|
+
curl -fsSL .../install.sh | sh -s -- --docker
|
|
64
|
+
|
|
65
|
+
# форсировать локальную ноду
|
|
66
|
+
curl -fsSL .../install.sh | sh -s -- --local
|
|
67
|
+
|
|
68
|
+
# конкретная версия пакета
|
|
69
|
+
curl -fsSL .../install.sh | sh -s -- --version=0.1.9
|
|
42
70
|
```
|
|
43
71
|
|
|
44
|
-
|
|
72
|
+
Удаление: `rm -rf ~/.local/share/girl-agent ~/.local/bin/girl-agent`
|
|
45
73
|
|
|
46
|
-
|
|
74
|
+
### windows — десктоп-приложение
|
|
75
|
+
|
|
76
|
+
В папке `desktop-rs/` лежит нативный десктоп-клиент на Rust (iced) и инсталлер-визард: ставит Node-пакет, создаёт профиль, открывает дашборд. Параллельно поднимается локальный веб-UI на `http://127.0.0.1:7777` с тем же дашбордом — открыть из соседнего окна / телефона по локалке. Без WebView, без Electron.
|
|
47
77
|
|
|
48
78
|
```powershell
|
|
79
|
+
cd desktop-rs
|
|
80
|
+
cargo run -p girl-agent-installer # визард настройки персоны
|
|
81
|
+
cargo run -p girl-agent-desktop # открыть дашборд
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Готовые бинари будут собираться в CI чуть позже — пока нужно `cargo build --release`.
|
|
85
|
+
|
|
86
|
+
### если уже есть node ≥ 20
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
npx @thesashadev/girl-agent # ink-визард
|
|
49
90
|
npx @thesashadev/girl-agent --profile=arina
|
|
50
91
|
```
|
|
51
92
|
|
|
52
|
-
|
|
93
|
+
### docker (для серверов; нулевые зависимости на хосте)
|
|
94
|
+
|
|
95
|
+
Интерактивная первичная настройка (ink-визард внутри контейнера):
|
|
96
|
+
```sh
|
|
97
|
+
docker run -it --rm -v girl-agent-data:/data ghcr.io/thesashadev/girl-agent:latest
|
|
98
|
+
```
|
|
53
99
|
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
|
|
100
|
+
Headless (для systemd / docker compose / k8s) — сначала готовим конфиг, потом запускаем без TTY:
|
|
101
|
+
```sh
|
|
102
|
+
# 1) шаблон конфига
|
|
103
|
+
docker run --rm ghcr.io/thesashadev/girl-agent:latest server --print-config > bot.json
|
|
104
|
+
# 2) отредактировать bot.json (token, api-key)
|
|
105
|
+
# 3) поднять в фоне
|
|
106
|
+
docker run -d --name girl-agent --restart=unless-stopped \
|
|
107
|
+
-v girl-agent-data:/data \
|
|
108
|
+
-v $PWD/bot.json:/config/bot.json:ro \
|
|
109
|
+
ghcr.io/thesashadev/girl-agent:latest \
|
|
110
|
+
server --config /config/bot.json --headless
|
|
57
111
|
```
|
|
58
|
-
*(пройдите все шаги и после появления дашборда нажмите `Ctrl+C`)*
|
|
59
112
|
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
docker-
|
|
113
|
+
Или совсем без файла, через env-vars (k8s secrets, docker compose):
|
|
114
|
+
```sh
|
|
115
|
+
docker run -d --name girl-agent --restart=unless-stopped \
|
|
116
|
+
-v girl-agent-data:/data \
|
|
117
|
+
-e GIRL_AGENT_MODE=bot \
|
|
118
|
+
-e GIRL_AGENT_TOKEN=... \
|
|
119
|
+
-e GIRL_AGENT_API_PRESET=claudehub \
|
|
120
|
+
-e GIRL_AGENT_API_KEY=... \
|
|
121
|
+
-e GIRL_AGENT_NAME='Аня' -e GIRL_AGENT_AGE=22 \
|
|
122
|
+
ghcr.io/thesashadev/girl-agent:latest \
|
|
123
|
+
server --headless
|
|
63
124
|
```
|
|
64
|
-
*(если профилей несколько, запустите конкретный так: `docker-compose run -d girl-agent node dist/cli.js --profile=arina`)*
|
|
65
125
|
|
|
66
|
-
|
|
126
|
+
Готовые шаблоны:
|
|
127
|
+
- `girl-agent server --print-config` — bot.json
|
|
128
|
+
- `girl-agent server --print-systemd` — `/etc/systemd/system/girl-agent.service`
|
|
129
|
+
- `girl-agent server --print-docker` — Dockerfile / compose / k8s snippets
|
|
130
|
+
- [`docker-compose.example.yml`](./docker-compose.example.yml) в корне репо
|
|
67
131
|
|
|
68
132
|
**Из исходников:**
|
|
69
133
|
|
package/dist/cli.js
CHANGED
|
@@ -19,15 +19,19 @@ var init_esm_shims = __esm({
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
// src/telegram/markdown.ts
|
|
22
|
-
function
|
|
23
|
-
return text
|
|
22
|
+
function hasSpoilers(text) {
|
|
23
|
+
return /\|\|.+?\|\|/.test(text);
|
|
24
|
+
}
|
|
25
|
+
function escapeHtml(text) {
|
|
26
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
27
|
+
}
|
|
28
|
+
function toHtmlWithSpoilers(text) {
|
|
29
|
+
return escapeHtml(text).replace(/\|\|(.+?)\|\|/g, "<tg-spoiler>$1</tg-spoiler>");
|
|
24
30
|
}
|
|
25
|
-
var MD2_RESERVED;
|
|
26
31
|
var init_markdown = __esm({
|
|
27
32
|
"src/telegram/markdown.ts"() {
|
|
28
33
|
"use strict";
|
|
29
34
|
init_esm_shims();
|
|
30
|
-
MD2_RESERVED = /([_*\[\]()~`>#+\-=|{}.!\\])/g;
|
|
31
35
|
}
|
|
32
36
|
});
|
|
33
37
|
|
|
@@ -55,13 +59,15 @@ function makeUserbotAdapter(cfg) {
|
|
|
55
59
|
const apiHash = cfg.telegram.apiHash;
|
|
56
60
|
const session = cfg.telegram.sessionString ?? "";
|
|
57
61
|
if (!apiId || !apiHash) throw new Error("API_ID/API_HASH missing for userbot");
|
|
58
|
-
|
|
62
|
+
const useWSS = cfg.telegram.useWSS !== false;
|
|
63
|
+
debug(`[userbot] creating TelegramClient (useWSS=${useWSS})\u2026`);
|
|
59
64
|
const client = new TelegramClient(new StringSession(session), apiId, apiHash, {
|
|
60
65
|
connectionRetries: 5,
|
|
61
66
|
requestRetries: 5,
|
|
62
67
|
retryDelay: 3e3,
|
|
63
68
|
autoReconnect: true,
|
|
64
|
-
floodSleepThreshold: 120
|
|
69
|
+
floodSleepThreshold: 120,
|
|
70
|
+
useWSS
|
|
65
71
|
});
|
|
66
72
|
client.onError = async () => {
|
|
67
73
|
};
|
|
@@ -190,11 +196,14 @@ function makeUserbotAdapter(cfg) {
|
|
|
190
196
|
},
|
|
191
197
|
async editLastMessage(chatId, messageId, text) {
|
|
192
198
|
const peer = await resolvePeer(chatId);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
if (hasSpoilers(text)) {
|
|
200
|
+
try {
|
|
201
|
+
await client.editMessage(peer, { message: messageId, text: toHtmlWithSpoilers(text), parseMode: "html" });
|
|
202
|
+
return;
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
197
205
|
}
|
|
206
|
+
await client.editMessage(peer, { message: messageId, text });
|
|
198
207
|
},
|
|
199
208
|
async deleteMessages(chatId, messageIds, revoke = false) {
|
|
200
209
|
const peer = await resolvePeer(chatId);
|
|
@@ -207,7 +216,8 @@ function makeUserbotAdapter(cfg) {
|
|
|
207
216
|
}
|
|
208
217
|
async function userbotLogin(opts) {
|
|
209
218
|
const client = new TelegramClient(new StringSession(""), opts.apiId, opts.apiHash, {
|
|
210
|
-
connectionRetries: 5
|
|
219
|
+
connectionRetries: 5,
|
|
220
|
+
useWSS: true
|
|
211
221
|
});
|
|
212
222
|
await client.start({
|
|
213
223
|
phoneNumber: async () => opts.phone,
|
|
@@ -293,13 +303,15 @@ function makeBotAdapter(cfg) {
|
|
|
293
303
|
});
|
|
294
304
|
},
|
|
295
305
|
async sendText(chatId, text) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
306
|
+
if (hasSpoilers(text)) {
|
|
307
|
+
try {
|
|
308
|
+
const msg2 = await bot.api.sendMessage(chatId, toHtmlWithSpoilers(text), { parse_mode: "HTML" });
|
|
309
|
+
return msg2.message_id;
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
302
312
|
}
|
|
313
|
+
const msg = await bot.api.sendMessage(chatId, text);
|
|
314
|
+
return msg.message_id;
|
|
303
315
|
},
|
|
304
316
|
async setTyping(chatId, on) {
|
|
305
317
|
if (on) {
|
|
@@ -581,6 +593,7 @@ init_esm_shims();
|
|
|
581
593
|
var STAGE_PRESETS = [
|
|
582
594
|
{
|
|
583
595
|
id: "met-irl-got-tg",
|
|
596
|
+
num: 1,
|
|
584
597
|
label: "\u0412\u0441\u0442\u0440\u0435\u0442\u0438\u043B\u0438\u0441\u044C \u0432 \u0440\u0435\u0430\u043B\u0435 \u2014 \u0434\u0430\u043B\u0430 \u0442\u0433",
|
|
585
598
|
description: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E \u043E\u0431\u043C\u0435\u043D\u044F\u043B\u0438\u0441\u044C \u0442\u0433. \u041F\u043E\u043C\u043D\u0438\u0442 \u043B\u0438\u0446\u043E, \u0433\u043E\u043B\u043E\u0441. \u041B\u0451\u0433\u043A\u0438\u0439 \u0438\u043D\u0442\u0435\u0440\u0435\u0441.",
|
|
586
599
|
defaults: {
|
|
@@ -595,6 +608,7 @@ var STAGE_PRESETS = [
|
|
|
595
608
|
},
|
|
596
609
|
{
|
|
597
610
|
id: "tg-given-cold",
|
|
611
|
+
num: 2,
|
|
598
612
|
label: "\u0414\u0430\u043B\u0430 \u0442\u0433, \u043D\u043E \u043D\u0435 \u0443\u0431\u0435\u0434\u0438\u043B \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C",
|
|
599
613
|
description: "\u0421\u043E\u043C\u043D\u0435\u0432\u0430\u0435\u0442\u0441\u044F. \u0427\u0430\u0441\u0442\u043E \u0438\u0433\u043D\u043E\u0440\u0438\u0442, \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043E\u0434\u043D\u043E\u0441\u043B\u043E\u0436\u043D\u043E. \u041D\u0443\u0436\u043D\u043E \u0434\u043E\u0431\u0438\u0432\u0430\u0442\u044C\u0441\u044F.",
|
|
600
614
|
defaults: {
|
|
@@ -609,6 +623,7 @@ var STAGE_PRESETS = [
|
|
|
609
623
|
},
|
|
610
624
|
{
|
|
611
625
|
id: "tg-given-warming",
|
|
626
|
+
num: 3,
|
|
612
627
|
label: "\u0414\u0430\u043B\u0430 \u0442\u0433, \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043E\u0441\u0442\u043E\u0440\u043E\u0436\u043D\u043E",
|
|
613
628
|
description: "\u041E\u0442\u0442\u0430\u0438\u0432\u0430\u0435\u0442. \u041E\u0442\u0432\u0435\u0447\u0430\u0435\u0442, \u043D\u043E \u043A\u043E\u0440\u043E\u0442\u043A\u043E. \u0422\u0435\u0441\u0442\u0438\u0442 \u0442\u0435\u0431\u044F.",
|
|
614
629
|
defaults: {
|
|
@@ -623,6 +638,7 @@ var STAGE_PRESETS = [
|
|
|
623
638
|
},
|
|
624
639
|
{
|
|
625
640
|
id: "convinced",
|
|
641
|
+
num: 4,
|
|
626
642
|
label: "\u0423\u0431\u0435\u0434\u0438\u043B \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C \u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u043E",
|
|
627
643
|
description: "\u041E\u0431\u0449\u0430\u0435\u0442\u0435\u0441\u044C \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u043E, \u0444\u043B\u0438\u0440\u0442\u0443\u0435\u0442, \u0435\u0449\u0451 \u043D\u0435 \u0432\u0438\u0434\u0435\u043B\u0438\u0441\u044C \u043F\u043E\u0441\u043B\u0435 \u0437\u043D\u0430\u043A\u043E\u043C\u0441\u0442\u0432\u0430.",
|
|
628
644
|
defaults: {
|
|
@@ -637,6 +653,7 @@ var STAGE_PRESETS = [
|
|
|
637
653
|
},
|
|
638
654
|
{
|
|
639
655
|
id: "first-date-done",
|
|
656
|
+
num: 5,
|
|
640
657
|
label: "\u0421\u0445\u043E\u0434\u0438\u043B\u0438 \u043E\u0434\u0438\u043D \u0440\u0430\u0437",
|
|
641
658
|
description: "\u041F\u0435\u0440\u0432\u043E\u0435 \u0441\u0432\u0438\u0434\u0430\u043D\u0438\u0435 \u0431\u044B\u043B\u043E, \u0432 \u043F\u043E\u0434\u0432\u0435\u0448\u0435\u043D\u043D\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438 \u2014 \u043D\u0440\u0430\u0432\u0438\u0442\u0441\u044F, \u043D\u043E \u043D\u0435 \u043F\u0430\u0440\u0430.",
|
|
642
659
|
defaults: {
|
|
@@ -651,6 +668,7 @@ var STAGE_PRESETS = [
|
|
|
651
668
|
},
|
|
652
669
|
{
|
|
653
670
|
id: "dating-early",
|
|
671
|
+
num: 6,
|
|
654
672
|
label: "\u0422\u043E\u043B\u044C\u043A\u043E \u043D\u0430\u0447\u0430\u043B\u0438 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044C\u0441\u044F",
|
|
655
673
|
description: "\u041E\u043A\u043E\u043B\u043E \u043C\u0435\u0441\u044F\u0446\u0430 \u0432\u043C\u0435\u0441\u0442\u0435. \u0411\u0430\u0431\u043E\u0447\u043A\u0438, \u0432\u0441\u0451 \u0432\u043D\u043E\u0432\u0435, \u043D\u043E \u0433\u0440\u0430\u043D\u0438\u0446\u044B \u0435\u0449\u0451 \u0445\u0440\u0443\u043F\u043A\u0438\u0435.",
|
|
656
674
|
defaults: {
|
|
@@ -665,6 +683,7 @@ var STAGE_PRESETS = [
|
|
|
665
683
|
},
|
|
666
684
|
{
|
|
667
685
|
id: "dating-stable",
|
|
686
|
+
num: 7,
|
|
668
687
|
label: "\u041F\u0430\u0440\u0430, \u043E\u0431\u0449\u0430\u0435\u0442\u0435\u0441\u044C \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u043E",
|
|
669
688
|
description: "\u0421\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u044B\u0435 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u044F, \u0448\u0443\u0442\u043A\u0438, \u0431\u044B\u0442\u043E\u0432\u0443\u0445\u0430, \u0434\u043E\u0432\u0435\u0440\u0438\u0435.",
|
|
670
689
|
defaults: {
|
|
@@ -679,6 +698,7 @@ var STAGE_PRESETS = [
|
|
|
679
698
|
},
|
|
680
699
|
{
|
|
681
700
|
id: "long-term",
|
|
701
|
+
num: 8,
|
|
682
702
|
label: "\u0414\u0430\u0432\u043D\u043E \u0432\u043C\u0435\u0441\u0442\u0435",
|
|
683
703
|
description: "\u0413\u043E\u0434+ \u0432\u043C\u0435\u0441\u0442\u0435. \u0418\u043D\u043E\u0433\u0434\u0430 \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0435\u043D\u0438\u0435, \u0440\u0443\u0442\u0438\u043D\u0430, \u0433\u043B\u0443\u0431\u043E\u043A\u043E\u0435 \u0434\u043E\u0432\u0435\u0440\u0438\u0435.",
|
|
684
704
|
defaults: {
|
|
@@ -693,6 +713,7 @@ var STAGE_PRESETS = [
|
|
|
693
713
|
},
|
|
694
714
|
{
|
|
695
715
|
id: "dumped",
|
|
716
|
+
num: 9,
|
|
696
717
|
label: "\u041E\u0442\u0448\u0438\u043B\u0430 (\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u043E\u0435)",
|
|
697
718
|
description: "\u041D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442. \u0421\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 :reset.",
|
|
698
719
|
defaults: {
|
|
@@ -707,6 +728,10 @@ var STAGE_PRESETS = [
|
|
|
707
728
|
}
|
|
708
729
|
];
|
|
709
730
|
function findStage(id) {
|
|
731
|
+
if (typeof id === "number" || /^\d+$/.test(String(id))) {
|
|
732
|
+
const num = Number(id);
|
|
733
|
+
return STAGE_PRESETS.find((s) => s.num === num) ?? STAGE_PRESETS[1];
|
|
734
|
+
}
|
|
710
735
|
return STAGE_PRESETS.find((s) => s.id === id) ?? STAGE_PRESETS[1];
|
|
711
736
|
}
|
|
712
737
|
|
|
@@ -800,7 +825,7 @@ function sameProfile(a, b) {
|
|
|
800
825
|
init_esm_shims();
|
|
801
826
|
import { promises as fs } from "fs";
|
|
802
827
|
import path2 from "path";
|
|
803
|
-
var DATA_ROOT = path2.resolve(process.cwd(), "data");
|
|
828
|
+
var DATA_ROOT = process.env.GIRL_AGENT_DATA ? path2.resolve(process.env.GIRL_AGENT_DATA) : path2.resolve(process.cwd(), "data");
|
|
804
829
|
function profileDir(slug) {
|
|
805
830
|
return path2.join(DATA_ROOT, slug);
|
|
806
831
|
}
|
|
@@ -1534,14 +1559,14 @@ var DEFAULT_PROXY = "https://tgproxy.girl-agent.com";
|
|
|
1534
1559
|
function proxyUrl() {
|
|
1535
1560
|
return process.env.GIRL_AGENT_AUTH_PROXY ?? DEFAULT_PROXY;
|
|
1536
1561
|
}
|
|
1537
|
-
async function post(
|
|
1538
|
-
const res = await fetch(`${proxyUrl()}${
|
|
1562
|
+
async function post(path6, body) {
|
|
1563
|
+
const res = await fetch(`${proxyUrl()}${path6}`, {
|
|
1539
1564
|
method: "POST",
|
|
1540
1565
|
headers: { "Content-Type": "application/json" },
|
|
1541
1566
|
body: JSON.stringify(body)
|
|
1542
1567
|
});
|
|
1543
1568
|
const data = await res.json();
|
|
1544
|
-
if (!res.ok) throw new Error(data.error ?? `proxy ${
|
|
1569
|
+
if (!res.ok) throw new Error(data.error ?? `proxy ${path6} failed (${res.status})`);
|
|
1545
1570
|
return data;
|
|
1546
1571
|
}
|
|
1547
1572
|
function remoteSendCode(phone) {
|
|
@@ -2425,7 +2450,7 @@ function Wizard({ initial, onDone }) {
|
|
|
2425
2450
|
SelectInput,
|
|
2426
2451
|
{
|
|
2427
2452
|
limit: 10,
|
|
2428
|
-
items: STAGE_PRESETS.filter((s) => s.id !== "dumped").map((s) => ({ label: `${s.label} \xB7 ${s.description}`, value: s.id })),
|
|
2453
|
+
items: STAGE_PRESETS.filter((s) => s.id !== "dumped").map((s) => ({ label: `${s.num}. ${s.label} \xB7 ${s.description}`, value: s.id })),
|
|
2429
2454
|
onSelect: async (it) => {
|
|
2430
2455
|
const nextStage = it.value;
|
|
2431
2456
|
setStage(nextStage);
|
|
@@ -2670,7 +2695,7 @@ function Dashboard({ runtime }) {
|
|
|
2670
2695
|
break;
|
|
2671
2696
|
}
|
|
2672
2697
|
case "help":
|
|
2673
|
-
append(":status :why :amnesia <\u043C\u0438\u043D> [chatId] :reset :stage <id> :wake [chatId] :debug [chatId] :pause :resume :cringe :relationship :persona :log [YYYY-MM-DD] [chars] :block [chatId] :unblock [chatId] :read [chatId] :clear-chat [chatId] [--revoke] :report-spam [chatId] :delete-last [chatId] [--local] :edit-last <text> :sticker [chatId] :quit");
|
|
2698
|
+
append(":status :why :amnesia <\u043C\u0438\u043D> [chatId] :reset :stage <id|num> :wake [chatId] :debug [chatId] :pause :resume :cringe :relationship :persona :log [YYYY-MM-DD] [chars] :block [chatId] :unblock [chatId] :read [chatId] :clear-chat [chatId] [--revoke] :report-spam [chatId] :delete-last [chatId] [--local] :edit-last <text> :sticker [chatId] :quit");
|
|
2674
2699
|
break;
|
|
2675
2700
|
case "quit":
|
|
2676
2701
|
case "exit":
|
|
@@ -2688,7 +2713,7 @@ function Dashboard({ runtime }) {
|
|
|
2688
2713
|
const line = cmd.trim();
|
|
2689
2714
|
setCmd("");
|
|
2690
2715
|
if (line) await execute(line);
|
|
2691
|
-
} })), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u043A\u043E\u043C\u0430\u043D\u0434\u044B: :status :why :amnesia <\u043C\u0438\u043D> :reset :stage <id> :pause :resume :cringe :persona :log [day] :block :unblock :read :clear-chat :delete-last :edit-last :sticker :quit"));
|
|
2716
|
+
} })), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u043A\u043E\u043C\u0430\u043D\u0434\u044B: :status :why :amnesia <\u043C\u0438\u043D> :reset :stage <id|num> :pause :resume :cringe :persona :log [day] :block :unblock :read :clear-chat :delete-last :edit-last :sticker :quit"));
|
|
2692
2717
|
}
|
|
2693
2718
|
|
|
2694
2719
|
// src/engine/runtime.ts
|
|
@@ -3093,8 +3118,8 @@ phoneAvailable=false \u043A\u043E\u0433\u0434\u0430: \u0441\u043F\u0438\u0442, \
|
|
|
3093
3118
|
}
|
|
3094
3119
|
async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(), conflict = null) {
|
|
3095
3120
|
const dateLocal = localDateStr(cfg.tz, now);
|
|
3096
|
-
const
|
|
3097
|
-
const existing = await readMd(cfg.slug,
|
|
3121
|
+
const path6 = `daily-life/${dateLocal}.md`;
|
|
3122
|
+
const existing = await readMd(cfg.slug, path6);
|
|
3098
3123
|
if (existing) {
|
|
3099
3124
|
try {
|
|
3100
3125
|
const m = existing.match(/<!--daily:(.+?)-->/s);
|
|
@@ -3126,7 +3151,7 @@ async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(
|
|
|
3126
3151
|
dl = { dateLocal, vibe: "\u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u0434\u0435\u043D\u044C", blocks: [], events: [], wants: [] };
|
|
3127
3152
|
}
|
|
3128
3153
|
const human = renderDailyLifeHuman(dl);
|
|
3129
|
-
await writeMd(cfg.slug,
|
|
3154
|
+
await writeMd(cfg.slug, path6, `${human}
|
|
3130
3155
|
|
|
3131
3156
|
<!--daily:${JSON.stringify(dl)}-->
|
|
3132
3157
|
`);
|
|
@@ -3356,9 +3381,9 @@ async function ensureDefaults(cfg) {
|
|
|
3356
3381
|
["time/promises.md", "# promises\n"],
|
|
3357
3382
|
["memory/uncertain.md", "# uncertain\n"]
|
|
3358
3383
|
];
|
|
3359
|
-
await Promise.all(defaults.map(async ([
|
|
3360
|
-
const current = await readMd(cfg.slug,
|
|
3361
|
-
if (!current.trim()) await writeMd(cfg.slug,
|
|
3384
|
+
await Promise.all(defaults.map(async ([path6, content]) => {
|
|
3385
|
+
const current = await readMd(cfg.slug, path6);
|
|
3386
|
+
if (!current.trim()) await writeMd(cfg.slug, path6, content + "\n");
|
|
3362
3387
|
}));
|
|
3363
3388
|
}
|
|
3364
3389
|
async function loadRealismContext(cfg, incoming) {
|
|
@@ -5380,10 +5405,11 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
|
|
|
5380
5405
|
}
|
|
5381
5406
|
async cmdSetStage(stageId) {
|
|
5382
5407
|
const prev = this.cfg.stage;
|
|
5383
|
-
|
|
5408
|
+
const resolved = findStage(stageId);
|
|
5409
|
+
this.cfg.stage = resolved.id;
|
|
5384
5410
|
await writeConfig(this.cfg);
|
|
5385
|
-
await maybeAdvanceRelationshipTimeline(this.cfg, prev,
|
|
5386
|
-
return `\u0441\u0442\u0430\u0434\u0438\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430: ${
|
|
5411
|
+
await maybeAdvanceRelationshipTimeline(this.cfg, prev, resolved.id);
|
|
5412
|
+
return `\u0441\u0442\u0430\u0434\u0438\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430: ${resolved.num}=${resolved.id}`;
|
|
5387
5413
|
}
|
|
5388
5414
|
async cmdWake(chatId) {
|
|
5389
5415
|
const now = Date.now();
|
|
@@ -5730,6 +5756,536 @@ function sleep(ms) {
|
|
|
5730
5756
|
return new Promise((r) => setTimeout(r, ms));
|
|
5731
5757
|
}
|
|
5732
5758
|
|
|
5759
|
+
// src/headless.ts
|
|
5760
|
+
init_esm_shims();
|
|
5761
|
+
import readline from "readline";
|
|
5762
|
+
async function runHeadlessJsonEvents(rt) {
|
|
5763
|
+
const out = (obj) => {
|
|
5764
|
+
process.stdout.write(JSON.stringify(obj) + "\n");
|
|
5765
|
+
};
|
|
5766
|
+
out({ type: "ready", profile: profileSummary(rt.cfg) });
|
|
5767
|
+
rt.on("event", (e) => {
|
|
5768
|
+
out({ ...e, t: Date.now() });
|
|
5769
|
+
});
|
|
5770
|
+
try {
|
|
5771
|
+
const r = await readRelationship(rt.cfg.slug);
|
|
5772
|
+
out({ type: "score", score: r.score, t: Date.now() });
|
|
5773
|
+
} catch {
|
|
5774
|
+
}
|
|
5775
|
+
let paused = false;
|
|
5776
|
+
const rl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
5777
|
+
rl.on("line", async (raw) => {
|
|
5778
|
+
const line = raw.trim();
|
|
5779
|
+
if (!line) return;
|
|
5780
|
+
if (!line.startsWith(":")) {
|
|
5781
|
+
out({ type: "response", ok: false, text: "\u043A\u043E\u043C\u0430\u043D\u0434\u044B \u043D\u0430\u0447\u0438\u043D\u0430\u044E\u0442\u0441\u044F \u0441 :" });
|
|
5782
|
+
return;
|
|
5783
|
+
}
|
|
5784
|
+
const [head, ...rest] = line.slice(1).split(" ");
|
|
5785
|
+
try {
|
|
5786
|
+
let text = "";
|
|
5787
|
+
switch (head) {
|
|
5788
|
+
case "status":
|
|
5789
|
+
text = await rt.cmdStatus();
|
|
5790
|
+
break;
|
|
5791
|
+
case "reset":
|
|
5792
|
+
text = await rt.cmdReset();
|
|
5793
|
+
break;
|
|
5794
|
+
case "stage":
|
|
5795
|
+
text = await rt.cmdSetStage(rest.join(" "));
|
|
5796
|
+
break;
|
|
5797
|
+
case "wake":
|
|
5798
|
+
text = await rt.cmdWake(rest[0]);
|
|
5799
|
+
break;
|
|
5800
|
+
case "debug":
|
|
5801
|
+
text = await rt.cmdDebug(rest[0]);
|
|
5802
|
+
break;
|
|
5803
|
+
case "why":
|
|
5804
|
+
text = await rt.cmdWhy(rest[0]);
|
|
5805
|
+
break;
|
|
5806
|
+
case "amnesia":
|
|
5807
|
+
text = await rt.cmdAmnesia(rest[0], rest[1]);
|
|
5808
|
+
break;
|
|
5809
|
+
case "block":
|
|
5810
|
+
text = await rt.cmdBlock(rest[0]);
|
|
5811
|
+
break;
|
|
5812
|
+
case "unblock":
|
|
5813
|
+
text = await rt.cmdUnblock(rest[0]);
|
|
5814
|
+
break;
|
|
5815
|
+
case "read":
|
|
5816
|
+
text = await rt.cmdRead(rest[0]);
|
|
5817
|
+
break;
|
|
5818
|
+
case "clear-chat":
|
|
5819
|
+
text = await rt.cmdClearChat(rest.find((x) => !x.startsWith("--")), rest.includes("--revoke"));
|
|
5820
|
+
break;
|
|
5821
|
+
case "report-spam":
|
|
5822
|
+
text = await rt.cmdReportSpam(rest[0]);
|
|
5823
|
+
break;
|
|
5824
|
+
case "delete-last":
|
|
5825
|
+
text = await rt.cmdDeleteLast(rest.find((x) => !x.startsWith("--")), !rest.includes("--local"));
|
|
5826
|
+
break;
|
|
5827
|
+
case "edit-last":
|
|
5828
|
+
text = await rt.cmdEditLast(rest.join(" "));
|
|
5829
|
+
break;
|
|
5830
|
+
case "sticker":
|
|
5831
|
+
text = await rt.cmdSticker(rest[0]);
|
|
5832
|
+
break;
|
|
5833
|
+
case "pause":
|
|
5834
|
+
rt.pause();
|
|
5835
|
+
paused = true;
|
|
5836
|
+
text = "\u23F8 pause";
|
|
5837
|
+
break;
|
|
5838
|
+
case "resume":
|
|
5839
|
+
rt.resume();
|
|
5840
|
+
paused = false;
|
|
5841
|
+
text = "\u25B6 resume";
|
|
5842
|
+
break;
|
|
5843
|
+
case "cringe": {
|
|
5844
|
+
const r = await readRelationship(rt.cfg.slug);
|
|
5845
|
+
text = `cringe=${r.score.cringe}; \u0441\u043C. memory/long-term.md \u0438 log/`;
|
|
5846
|
+
break;
|
|
5847
|
+
}
|
|
5848
|
+
case "relationship": {
|
|
5849
|
+
const r = await readRelationship(rt.cfg.slug);
|
|
5850
|
+
text = `stage=${r.stage} score=${JSON.stringify(r.score)}`;
|
|
5851
|
+
break;
|
|
5852
|
+
}
|
|
5853
|
+
case "persona": {
|
|
5854
|
+
const p = await readMd(rt.cfg.slug, "persona.md");
|
|
5855
|
+
text = p.slice(0, 4e3);
|
|
5856
|
+
break;
|
|
5857
|
+
}
|
|
5858
|
+
case "log": {
|
|
5859
|
+
const day = /^\d{4}-\d{2}-\d{2}$/.test(rest[0] ?? "") ? rest[0] : sessionDate(rt.cfg.tz);
|
|
5860
|
+
const limit = Number(rest.find((x) => /^\d+$/.test(x)) ?? 3e3);
|
|
5861
|
+
const p = await readSessionLog(rt.cfg.slug, day);
|
|
5862
|
+
text = p.trim() ? p.slice(-Math.max(500, Math.min(limit, 2e4))) : `(log/${day}.md \u043F\u0443\u0441\u0442)`;
|
|
5863
|
+
break;
|
|
5864
|
+
}
|
|
5865
|
+
case "snapshot": {
|
|
5866
|
+
const r = await readRelationship(rt.cfg.slug);
|
|
5867
|
+
out({
|
|
5868
|
+
type: "snapshot",
|
|
5869
|
+
t: Date.now(),
|
|
5870
|
+
paused,
|
|
5871
|
+
profile: profileSummary(rt.cfg),
|
|
5872
|
+
stage: { id: r.stage, num: findStage(r.stage).num, label: findStage(r.stage).label },
|
|
5873
|
+
score: r.score
|
|
5874
|
+
});
|
|
5875
|
+
return;
|
|
5876
|
+
}
|
|
5877
|
+
case "help":
|
|
5878
|
+
text = ":status :why :amnesia :reset :stage :wake :debug :pause :resume :cringe :relationship :persona :log :block :unblock :read :clear-chat :report-spam :delete-last :edit-last :sticker :snapshot :quit";
|
|
5879
|
+
break;
|
|
5880
|
+
case "quit":
|
|
5881
|
+
case "exit":
|
|
5882
|
+
await rt.stop();
|
|
5883
|
+
out({ type: "response", ok: true, text: "bye" });
|
|
5884
|
+
process.exit(0);
|
|
5885
|
+
default:
|
|
5886
|
+
out({ type: "response", ok: false, text: `\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430: ${head}` });
|
|
5887
|
+
return;
|
|
5888
|
+
}
|
|
5889
|
+
out({ type: "response", ok: true, text });
|
|
5890
|
+
} catch (e) {
|
|
5891
|
+
out({ type: "response", ok: false, text: "err: " + e.message });
|
|
5892
|
+
}
|
|
5893
|
+
});
|
|
5894
|
+
const shutdown = async () => {
|
|
5895
|
+
try {
|
|
5896
|
+
await rt.stop();
|
|
5897
|
+
} catch {
|
|
5898
|
+
}
|
|
5899
|
+
out({ type: "stopped", t: Date.now() });
|
|
5900
|
+
process.exit(0);
|
|
5901
|
+
};
|
|
5902
|
+
process.on("SIGINT", shutdown);
|
|
5903
|
+
process.on("SIGTERM", shutdown);
|
|
5904
|
+
await new Promise(() => {
|
|
5905
|
+
});
|
|
5906
|
+
}
|
|
5907
|
+
function profileSummary(cfg) {
|
|
5908
|
+
const stage = findStage(cfg.stage);
|
|
5909
|
+
return {
|
|
5910
|
+
slug: cfg.slug,
|
|
5911
|
+
name: cfg.name,
|
|
5912
|
+
age: cfg.age,
|
|
5913
|
+
mode: cfg.mode,
|
|
5914
|
+
nationality: cfg.nationality,
|
|
5915
|
+
tz: cfg.tz,
|
|
5916
|
+
stage: { id: cfg.stage, num: stage.num, label: stage.label }
|
|
5917
|
+
};
|
|
5918
|
+
}
|
|
5919
|
+
|
|
5920
|
+
// src/server.ts
|
|
5921
|
+
init_esm_shims();
|
|
5922
|
+
import fs4 from "fs/promises";
|
|
5923
|
+
import path5 from "path";
|
|
5924
|
+
import os from "os";
|
|
5925
|
+
var SERVER_HELP = `
|
|
5926
|
+
girl-agent server \u2014 automation / ops mode (no TTY required)
|
|
5927
|
+
|
|
5928
|
+
usage:
|
|
5929
|
+
girl-agent server --print-config > bot.json
|
|
5930
|
+
# \u043E\u0442\u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u0443\u0439 bot.json
|
|
5931
|
+
girl-agent server --config bot.json --headless
|
|
5932
|
+
|
|
5933
|
+
girl-agent server --list
|
|
5934
|
+
girl-agent server --profile=<slug> --headless
|
|
5935
|
+
|
|
5936
|
+
girl-agent server --print-systemd > /etc/systemd/system/girl-agent.service
|
|
5937
|
+
girl-agent server --print-docker
|
|
5938
|
+
|
|
5939
|
+
env-vars (\u0434\u043B\u044F CI / docker secrets / k8s):
|
|
5940
|
+
GIRL_AGENT_DATA \u043F\u0443\u0442\u044C \u043A \u043F\u0440\u043E\u0444\u0438\u043B\u044F\u043C (default: ./data)
|
|
5941
|
+
GIRL_AGENT_MODE bot|userbot
|
|
5942
|
+
GIRL_AGENT_TOKEN telegram bot token
|
|
5943
|
+
GIRL_AGENT_API_PRESET openai|anthropic|claudehub|...
|
|
5944
|
+
GIRL_AGENT_API_KEY \u043A\u043B\u044E\u0447 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430
|
|
5945
|
+
GIRL_AGENT_MODEL, _NAME, _AGE, _NATIONALITY, _TZ, _STAGE (id \u0438\u043B\u0438 \u043D\u043E\u043C\u0435\u0440 1-8), _COMM_PRESET
|
|
5946
|
+
|
|
5947
|
+
\u0434\u043B\u044F \u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0439 \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0439 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0439 \u0431\u0435\u0437 \u0444\u043B\u0430\u0433\u043E\u0432 \u0432 \u043E\u0431\u044B\u0447\u043D\u043E\u043C \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 \u2014
|
|
5948
|
+
\u043E\u0442\u043A\u0440\u043E\u0435\u0442\u0441\u044F ink-\u0432\u0438\u0437\u0430\u0440\u0434.
|
|
5949
|
+
`;
|
|
5950
|
+
function parseServerArgs(argv) {
|
|
5951
|
+
return {
|
|
5952
|
+
config: typeof argv.config === "string" ? argv.config : void 0,
|
|
5953
|
+
printConfig: !!argv["print-config"],
|
|
5954
|
+
printSystemd: !!argv["print-systemd"],
|
|
5955
|
+
printDocker: !!argv["print-docker"],
|
|
5956
|
+
headless: !!argv.headless,
|
|
5957
|
+
jsonEvents: !!argv["json-events"],
|
|
5958
|
+
noStart: !!argv["no-start"] || argv.start === false,
|
|
5959
|
+
profile: typeof argv.profile === "string" ? argv.profile : void 0,
|
|
5960
|
+
list: !!argv.list,
|
|
5961
|
+
help: !!argv.help
|
|
5962
|
+
};
|
|
5963
|
+
}
|
|
5964
|
+
async function runServer(rawArgv) {
|
|
5965
|
+
const args = parseServerArgs(rawArgv);
|
|
5966
|
+
if (args.help) {
|
|
5967
|
+
process.stdout.write(SERVER_HELP);
|
|
5968
|
+
return;
|
|
5969
|
+
}
|
|
5970
|
+
if (args.printConfig) {
|
|
5971
|
+
process.stdout.write(buildConfigTemplate());
|
|
5972
|
+
return;
|
|
5973
|
+
}
|
|
5974
|
+
if (args.printSystemd) {
|
|
5975
|
+
process.stdout.write(buildSystemdUnit());
|
|
5976
|
+
return;
|
|
5977
|
+
}
|
|
5978
|
+
if (args.printDocker) {
|
|
5979
|
+
process.stdout.write(buildDockerArtifacts());
|
|
5980
|
+
return;
|
|
5981
|
+
}
|
|
5982
|
+
if (args.list) {
|
|
5983
|
+
const list = await listProfiles();
|
|
5984
|
+
process.stdout.write(list.length ? list.join("\n") + "\n" : "(\u043D\u0435\u0442 \u043F\u0440\u043E\u0444\u0438\u043B\u0435\u0439)\n");
|
|
5985
|
+
process.stdout.write(`data: ${DATA_ROOT}
|
|
5986
|
+
`);
|
|
5987
|
+
return;
|
|
5988
|
+
}
|
|
5989
|
+
if (args.profile) {
|
|
5990
|
+
const cfg = await readConfig(args.profile);
|
|
5991
|
+
if (!cfg) {
|
|
5992
|
+
process.stderr.write(`profile not found: ${args.profile}
|
|
5993
|
+
`);
|
|
5994
|
+
process.stderr.write(`data dir: ${DATA_ROOT}
|
|
5995
|
+
`);
|
|
5996
|
+
process.exit(1);
|
|
5997
|
+
}
|
|
5998
|
+
await startRuntime(cfg, args);
|
|
5999
|
+
return;
|
|
6000
|
+
}
|
|
6001
|
+
if (args.config) {
|
|
6002
|
+
const cfg = await loadConfigFile(args.config);
|
|
6003
|
+
await persistAndMaybeStart(cfg, args);
|
|
6004
|
+
return;
|
|
6005
|
+
}
|
|
6006
|
+
const cfgFromEnv = configFromEnv();
|
|
6007
|
+
if (cfgFromEnv) {
|
|
6008
|
+
process.stderr.write("[server] \u043F\u0440\u043E\u0432\u0438\u0436\u0443 \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0438\u0437 env vars\n");
|
|
6009
|
+
await persistAndMaybeStart(cfgFromEnv, args);
|
|
6010
|
+
return;
|
|
6011
|
+
}
|
|
6012
|
+
process.stderr.write(SERVER_HELP);
|
|
6013
|
+
process.stderr.write("\n[server] \u0434\u043B\u044F \u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0439 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0437\u0430\u043F\u0443\u0441\u0442\u0438 \u0431\u0435\u0437 \u0444\u043B\u0430\u0433\u043E\u0432 \u0432 TTY-\u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435.\n");
|
|
6014
|
+
process.exit(1);
|
|
6015
|
+
}
|
|
6016
|
+
async function persistAndMaybeStart(cfg, args) {
|
|
6017
|
+
await writeConfig(cfg);
|
|
6018
|
+
process.stderr.write(`[server] \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D: ${path5.join(DATA_ROOT, cfg.slug)}
|
|
6019
|
+
`);
|
|
6020
|
+
if (cfg.llm.apiKey || findPreset(cfg.llm.presetId)?.apiKeyRequired === false) {
|
|
6021
|
+
try {
|
|
6022
|
+
process.stderr.write("[server] \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C persona/speech/communication...\n");
|
|
6023
|
+
const llm = makeLLM(cfg.llm);
|
|
6024
|
+
const generated = await generatePersonaPack(llm, cfg.slug, cfg.name, cfg.age, cfg.nationality, cfg.personaNotes ?? "");
|
|
6025
|
+
cfg.busySchedule = generated.busySchedule;
|
|
6026
|
+
await writeConfig(cfg);
|
|
6027
|
+
process.stderr.write("[server] \u043F\u0435\u0440\u0441\u043E\u043D\u0430 \u0433\u043E\u0442\u043E\u0432\u0430.\n");
|
|
6028
|
+
} catch (e) {
|
|
6029
|
+
process.stderr.write(`[server] \u043E\u0448\u0438\u0431\u043A\u0430 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043F\u0435\u0440\u0441\u043E\u043D\u044B: ${e?.message ?? e}
|
|
6030
|
+
`);
|
|
6031
|
+
process.stderr.write("[server] \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D, \u043D\u043E \u0431\u0435\u0437 persona.md. \u041C\u043E\u0436\u043D\u043E \u043F\u0435\u0440\u0435\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u0437\u0436\u0435.\n");
|
|
6032
|
+
}
|
|
6033
|
+
} else {
|
|
6034
|
+
process.stderr.write("[server] api-\u043A\u043B\u044E\u0447 \u043D\u0435 \u0437\u0430\u0434\u0430\u043D \u2014 \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u043C \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044E \u043F\u0435\u0440\u0441\u043E\u043D\u044B.\n");
|
|
6035
|
+
}
|
|
6036
|
+
if (args.noStart) {
|
|
6037
|
+
process.stderr.write(`[server] --no-start: \u0437\u0430\u043F\u0443\u0441\u043A \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D.
|
|
6038
|
+
`);
|
|
6039
|
+
return;
|
|
6040
|
+
}
|
|
6041
|
+
await startRuntime(cfg, args);
|
|
6042
|
+
}
|
|
6043
|
+
async function startRuntime(cfg, args) {
|
|
6044
|
+
const rt = new Runtime(cfg);
|
|
6045
|
+
await rt.start();
|
|
6046
|
+
const wantsHeadless = !!(args.headless || args.jsonEvents);
|
|
6047
|
+
if (wantsHeadless) {
|
|
6048
|
+
await runHeadlessJsonEvents(rt);
|
|
6049
|
+
return;
|
|
6050
|
+
}
|
|
6051
|
+
process.stderr.write(`[server] \u0431\u043E\u0442 \u0437\u0430\u043F\u0443\u0449\u0435\u043D: ${cfg.name} (${cfg.slug})
|
|
6052
|
+
`);
|
|
6053
|
+
rt.on("event", (e) => {
|
|
6054
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
6055
|
+
const t = e.type ?? "event";
|
|
6056
|
+
process.stdout.write(`${ts} ${t} ${JSON.stringify(e)}
|
|
6057
|
+
`);
|
|
6058
|
+
});
|
|
6059
|
+
const stop = async () => {
|
|
6060
|
+
process.stderr.write("[server] \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430...\n");
|
|
6061
|
+
await rt.stop();
|
|
6062
|
+
process.exit(0);
|
|
6063
|
+
};
|
|
6064
|
+
process.on("SIGINT", stop);
|
|
6065
|
+
process.on("SIGTERM", stop);
|
|
6066
|
+
}
|
|
6067
|
+
function configFromEnv() {
|
|
6068
|
+
const e = process.env;
|
|
6069
|
+
if (!e.GIRL_AGENT_MODE && !e.GIRL_AGENT_TOKEN && !e.GIRL_AGENT_API_KEY) return null;
|
|
6070
|
+
const mode = e.GIRL_AGENT_MODE === "userbot" ? "userbot" : "bot";
|
|
6071
|
+
const presetId = e.GIRL_AGENT_API_PRESET ?? "claudehub";
|
|
6072
|
+
const preset = findPreset(presetId);
|
|
6073
|
+
if (!preset) {
|
|
6074
|
+
process.stderr.write(`[server] unknown api preset in env: ${presetId}
|
|
6075
|
+
`);
|
|
6076
|
+
process.exit(1);
|
|
6077
|
+
}
|
|
6078
|
+
const nationality = e.GIRL_AGENT_NATIONALITY === "UA" ? "UA" : "RU";
|
|
6079
|
+
const name = e.GIRL_AGENT_NAME || pickRandomNames(nationality, 1)[0];
|
|
6080
|
+
const age = Number(e.GIRL_AGENT_AGE ?? 18);
|
|
6081
|
+
const tz = e.GIRL_AGENT_TZ ? parseTzFlag(e.GIRL_AGENT_TZ) ?? defaultTzForNationality(nationality) : defaultTzForNationality(nationality);
|
|
6082
|
+
const stage = e.GIRL_AGENT_STAGE ? findStage(e.GIRL_AGENT_STAGE).id : "tg-given-cold";
|
|
6083
|
+
const commPreset = COMMUNICATION_PRESETS.find((c) => c.id === (e.GIRL_AGENT_COMM_PRESET ?? "normal")) ?? COMMUNICATION_PRESETS[0];
|
|
6084
|
+
return {
|
|
6085
|
+
slug: slugify(name),
|
|
6086
|
+
name,
|
|
6087
|
+
age,
|
|
6088
|
+
nationality,
|
|
6089
|
+
tz,
|
|
6090
|
+
mode,
|
|
6091
|
+
stage,
|
|
6092
|
+
llm: {
|
|
6093
|
+
presetId,
|
|
6094
|
+
proto: preset.proto,
|
|
6095
|
+
baseURL: preset.baseURL,
|
|
6096
|
+
apiKey: e.GIRL_AGENT_API_KEY ?? preset.defaultApiKey ?? "",
|
|
6097
|
+
model: e.GIRL_AGENT_MODEL ?? preset.defaultModel
|
|
6098
|
+
},
|
|
6099
|
+
telegram: mode === "bot" ? { botToken: e.GIRL_AGENT_TOKEN ?? "" } : {
|
|
6100
|
+
apiId: Number(e.GIRL_AGENT_TG_API_ID ?? 0),
|
|
6101
|
+
apiHash: e.GIRL_AGENT_TG_API_HASH ?? "",
|
|
6102
|
+
phone: e.GIRL_AGENT_TG_PHONE ?? ""
|
|
6103
|
+
},
|
|
6104
|
+
mcp: [],
|
|
6105
|
+
privacy: "owner-only",
|
|
6106
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6107
|
+
sleepFrom: Number(e.GIRL_AGENT_SLEEP_FROM ?? 23),
|
|
6108
|
+
sleepTo: Number(e.GIRL_AGENT_SLEEP_TO ?? 8),
|
|
6109
|
+
nightWakeChance: Number(e.GIRL_AGENT_NIGHT_WAKE ?? 0.05),
|
|
6110
|
+
communication: commPreset.profile,
|
|
6111
|
+
vibe: commPreset.profile.messageStyle === "one-liners" ? "short" : "warm",
|
|
6112
|
+
busySchedule: []
|
|
6113
|
+
};
|
|
6114
|
+
}
|
|
6115
|
+
async function loadConfigFile(file) {
|
|
6116
|
+
const abs = path5.isAbsolute(file) ? file : path5.join(process.cwd(), file);
|
|
6117
|
+
let raw;
|
|
6118
|
+
try {
|
|
6119
|
+
raw = await fs4.readFile(abs, "utf-8");
|
|
6120
|
+
} catch (e) {
|
|
6121
|
+
process.stderr.write(`[server] \u043D\u0435 \u043C\u043E\u0433\u0443 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C ${abs}: ${e?.message ?? e}
|
|
6122
|
+
`);
|
|
6123
|
+
process.exit(1);
|
|
6124
|
+
}
|
|
6125
|
+
let parsed;
|
|
6126
|
+
try {
|
|
6127
|
+
parsed = JSON.parse(raw);
|
|
6128
|
+
} catch (e) {
|
|
6129
|
+
process.stderr.write(`[server] ${abs} \u043D\u0435 \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0432\u0430\u043B\u0438\u0434\u043D\u044B\u043C JSON: ${e?.message ?? e}
|
|
6130
|
+
`);
|
|
6131
|
+
process.exit(1);
|
|
6132
|
+
}
|
|
6133
|
+
return validateConfig(parsed);
|
|
6134
|
+
}
|
|
6135
|
+
function validateConfig(raw) {
|
|
6136
|
+
const c = raw;
|
|
6137
|
+
const errs = [];
|
|
6138
|
+
if (!c.name) errs.push("name");
|
|
6139
|
+
if (!c.age || c.age < 14 || c.age > 99) errs.push("age (14..99)");
|
|
6140
|
+
if (!c.nationality || c.nationality !== "RU" && c.nationality !== "UA") errs.push("nationality (RU|UA)");
|
|
6141
|
+
if (!c.tz) errs.push("tz");
|
|
6142
|
+
if (!c.mode || c.mode !== "bot" && c.mode !== "userbot") errs.push("mode (bot|userbot)");
|
|
6143
|
+
if (!c.stage) errs.push("stage");
|
|
6144
|
+
if (!c.llm?.presetId) errs.push("llm.presetId");
|
|
6145
|
+
if (!c.llm?.model) errs.push("llm.model");
|
|
6146
|
+
if (errs.length) {
|
|
6147
|
+
process.stderr.write(`[server] \u043A\u043E\u043D\u0444\u0438\u0433 \u043D\u0435\u0432\u0430\u043B\u0438\u0434\u0435\u043D, \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0438\u0435 \u043F\u043E\u043B\u044F:
|
|
6148
|
+
- ${errs.join("\n - ")}
|
|
6149
|
+
`);
|
|
6150
|
+
process.stderr.write(`[server] \u0441\u043C. \u0448\u0430\u0431\u043B\u043E\u043D: girl-agent server --print-config
|
|
6151
|
+
`);
|
|
6152
|
+
process.exit(1);
|
|
6153
|
+
}
|
|
6154
|
+
const filled = {
|
|
6155
|
+
slug: c.slug || slugify(c.name),
|
|
6156
|
+
name: c.name,
|
|
6157
|
+
age: c.age,
|
|
6158
|
+
nationality: c.nationality,
|
|
6159
|
+
tz: c.tz,
|
|
6160
|
+
mode: c.mode,
|
|
6161
|
+
stage: c.stage,
|
|
6162
|
+
llm: {
|
|
6163
|
+
presetId: c.llm.presetId,
|
|
6164
|
+
proto: c.llm.proto ?? findPreset(c.llm.presetId)?.proto ?? "openai",
|
|
6165
|
+
baseURL: c.llm.baseURL ?? findPreset(c.llm.presetId)?.baseURL,
|
|
6166
|
+
apiKey: c.llm.apiKey ?? "",
|
|
6167
|
+
model: c.llm.model
|
|
6168
|
+
},
|
|
6169
|
+
telegram: c.telegram ?? {},
|
|
6170
|
+
mcp: c.mcp ?? [],
|
|
6171
|
+
privacy: c.privacy ?? "owner-only",
|
|
6172
|
+
createdAt: c.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6173
|
+
sleepFrom: c.sleepFrom ?? 23,
|
|
6174
|
+
sleepTo: c.sleepTo ?? 8,
|
|
6175
|
+
nightWakeChance: c.nightWakeChance ?? 0.05,
|
|
6176
|
+
communication: c.communication ?? COMMUNICATION_PRESETS[0].profile,
|
|
6177
|
+
vibe: c.vibe,
|
|
6178
|
+
personaNotes: c.personaNotes,
|
|
6179
|
+
busySchedule: c.busySchedule ?? []
|
|
6180
|
+
};
|
|
6181
|
+
return filled;
|
|
6182
|
+
}
|
|
6183
|
+
function buildConfigTemplate() {
|
|
6184
|
+
const sample = {
|
|
6185
|
+
slug: "anya",
|
|
6186
|
+
name: "\u0410\u043D\u044F",
|
|
6187
|
+
age: 22,
|
|
6188
|
+
nationality: "RU",
|
|
6189
|
+
tz: "Europe/Moscow",
|
|
6190
|
+
mode: "bot",
|
|
6191
|
+
stage: "tg-given-cold",
|
|
6192
|
+
llm: {
|
|
6193
|
+
presetId: "claudehub",
|
|
6194
|
+
proto: "anthropic",
|
|
6195
|
+
baseURL: "https://api.claudehub.fun",
|
|
6196
|
+
apiKey: "REPLACE_ME",
|
|
6197
|
+
model: "claude-sonnet-4.6"
|
|
6198
|
+
},
|
|
6199
|
+
telegram: { botToken: "REPLACE_ME" },
|
|
6200
|
+
mcp: [],
|
|
6201
|
+
privacy: "owner-only",
|
|
6202
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6203
|
+
sleepFrom: 23,
|
|
6204
|
+
sleepTo: 8,
|
|
6205
|
+
nightWakeChance: 0.05,
|
|
6206
|
+
communication: COMMUNICATION_PRESETS[0].profile,
|
|
6207
|
+
vibe: "warm",
|
|
6208
|
+
busySchedule: []
|
|
6209
|
+
};
|
|
6210
|
+
return JSON.stringify(sample, null, 2) + "\n";
|
|
6211
|
+
}
|
|
6212
|
+
function buildSystemdUnit() {
|
|
6213
|
+
const home = os.homedir();
|
|
6214
|
+
return `# /etc/systemd/system/girl-agent.service
|
|
6215
|
+
# install: sudo cp this.service /etc/systemd/system/girl-agent.service
|
|
6216
|
+
# sudo systemctl daemon-reload
|
|
6217
|
+
# sudo systemctl enable --now girl-agent
|
|
6218
|
+
|
|
6219
|
+
[Unit]
|
|
6220
|
+
Description=girl-agent (Telegram AI girl)
|
|
6221
|
+
After=network-online.target
|
|
6222
|
+
Wants=network-online.target
|
|
6223
|
+
|
|
6224
|
+
[Service]
|
|
6225
|
+
Type=simple
|
|
6226
|
+
User=%i
|
|
6227
|
+
WorkingDirectory=${home}
|
|
6228
|
+
ExecStart=${home}/.local/bin/girl-agent server --config ${home}/.config/girl-agent/bot.json --headless
|
|
6229
|
+
Restart=on-failure
|
|
6230
|
+
RestartSec=10
|
|
6231
|
+
StandardOutput=journal
|
|
6232
|
+
StandardError=journal
|
|
6233
|
+
Environment=NODE_ENV=production
|
|
6234
|
+
# uncomment for env-driven setup:
|
|
6235
|
+
# Environment=GIRL_AGENT_MODE=bot
|
|
6236
|
+
# Environment=GIRL_AGENT_TOKEN=...
|
|
6237
|
+
# Environment=GIRL_AGENT_API_PRESET=claudehub
|
|
6238
|
+
# Environment=GIRL_AGENT_API_KEY=...
|
|
6239
|
+
|
|
6240
|
+
[Install]
|
|
6241
|
+
WantedBy=multi-user.target
|
|
6242
|
+
`;
|
|
6243
|
+
}
|
|
6244
|
+
function buildDockerArtifacts() {
|
|
6245
|
+
return `# === \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 ===
|
|
6246
|
+
docker run -it --rm \\
|
|
6247
|
+
-v girl-agent-data:/data \\
|
|
6248
|
+
-e GIRL_AGENT_DATA=/data \\
|
|
6249
|
+
ghcr.io/thesashadev/girl-agent:latest
|
|
6250
|
+
|
|
6251
|
+
# === headless \u0441 \u0433\u043E\u0442\u043E\u0432\u044B\u043C \u043A\u043E\u043D\u0444\u0438\u0433\u043E\u043C ===
|
|
6252
|
+
docker run -d --name girl-agent --restart=unless-stopped \\
|
|
6253
|
+
-v girl-agent-data:/data \\
|
|
6254
|
+
-v "$PWD/bot.json:/config/bot.json:ro" \\
|
|
6255
|
+
-e GIRL_AGENT_DATA=/data \\
|
|
6256
|
+
ghcr.io/thesashadev/girl-agent:latest \\
|
|
6257
|
+
server --config /config/bot.json --headless
|
|
6258
|
+
|
|
6259
|
+
# === \u0442\u043E\u043B\u044C\u043A\u043E env vars (\u0431\u0435\u0437 \u0444\u0430\u0439\u043B\u0430) ===
|
|
6260
|
+
docker run -d --name girl-agent --restart=unless-stopped \\
|
|
6261
|
+
-v girl-agent-data:/data \\
|
|
6262
|
+
-e GIRL_AGENT_DATA=/data \\
|
|
6263
|
+
-e GIRL_AGENT_MODE=bot \\
|
|
6264
|
+
-e GIRL_AGENT_TOKEN=... \\
|
|
6265
|
+
-e GIRL_AGENT_API_PRESET=claudehub \\
|
|
6266
|
+
-e GIRL_AGENT_API_KEY=... \\
|
|
6267
|
+
-e GIRL_AGENT_NAME='\u0410\u043D\u044F' \\
|
|
6268
|
+
-e GIRL_AGENT_AGE=22 \\
|
|
6269
|
+
ghcr.io/thesashadev/girl-agent:latest \\
|
|
6270
|
+
server --headless
|
|
6271
|
+
|
|
6272
|
+
# === docker-compose.yml ===
|
|
6273
|
+
# version: "3.9"
|
|
6274
|
+
# services:
|
|
6275
|
+
# girl-agent:
|
|
6276
|
+
# image: ghcr.io/thesashadev/girl-agent:latest
|
|
6277
|
+
# command: ["server", "--config", "/config/bot.json", "--headless"]
|
|
6278
|
+
# environment:
|
|
6279
|
+
# GIRL_AGENT_DATA: /data
|
|
6280
|
+
# volumes:
|
|
6281
|
+
# - girl-agent-data:/data
|
|
6282
|
+
# - ./bot.json:/config/bot.json:ro
|
|
6283
|
+
# restart: unless-stopped
|
|
6284
|
+
# volumes:
|
|
6285
|
+
# girl-agent-data:
|
|
6286
|
+
`;
|
|
6287
|
+
}
|
|
6288
|
+
|
|
5733
6289
|
// src/cli.tsx
|
|
5734
6290
|
var HELP = `
|
|
5735
6291
|
girl-agent \u2014 AI girl for Telegram
|
|
@@ -5741,6 +6297,14 @@ usage:
|
|
|
5741
6297
|
npx girl-agent --reset --profile=<slug>
|
|
5742
6298
|
npx girl-agent <flags> # \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0432\u0438\u0437\u0430\u0440\u0434 \u0441 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u0430\u043C\u0438
|
|
5743
6299
|
|
|
6300
|
+
server (\u0434\u043B\u044F \u0441\u0438\u0441\u0442\u0435\u043C \u0431\u0435\u0437 TTY: docker / systemd / cron / CI):
|
|
6301
|
+
npx girl-agent server --print-config > bot.json
|
|
6302
|
+
npx girl-agent server --config bot.json --headless
|
|
6303
|
+
npx girl-agent server --print-systemd | --print-docker | --list
|
|
6304
|
+
|
|
6305
|
+
\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 (\u0431\u0435\u0437 node \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435):
|
|
6306
|
+
curl -fsSL https://raw.githubusercontent.com/TheSashaDev/girl-agent/main/scripts/install.sh | sh
|
|
6307
|
+
|
|
5744
6308
|
required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-preset --mode; --api-key \u043D\u0443\u0436\u0435\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u043E\u0432 \u0441 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439):
|
|
5745
6309
|
--profile=<slug> slug \u043F\u0440\u043E\u0444\u0438\u043B\u044F
|
|
5746
6310
|
--mode=bot|userbot
|
|
@@ -5762,13 +6326,13 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
|
|
|
5762
6326
|
--privacy=<mode> owner-only|allow-strangers (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E owner-only)
|
|
5763
6327
|
--nationality=RU|UA (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E RU)
|
|
5764
6328
|
--tz=<value> IANA "Europe/Moscow" / "GMT+3" / "+3" / "\u041A\u0438\u0435\u0432" \u2014 \u043F\u043E\u0438\u0441\u043A
|
|
5765
|
-
--stage=<id>
|
|
6329
|
+
--stage=<id|num> 1=met-irl-got-tg 2=tg-given-cold 3=tg-given-warming 4=convinced 5=first-date-done 6=dating-early 7=dating-stable 8=long-term
|
|
5766
6330
|
--mcp=exa:KEY \u043C\u043E\u0436\u043D\u043E \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0440\u0430\u0437
|
|
5767
6331
|
--new \u043F\u0440\u0438\u043D\u0443\u0434\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u0432\u0438\u0437\u0430\u0440\u0434 \u0434\u043B\u044F \u043D\u043E\u0432\u043E\u0433\u043E \u043F\u0440\u043E\u0444\u0438\u043B\u044F
|
|
5768
6332
|
--list \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u0438
|
|
5769
6333
|
--help
|
|
5770
6334
|
|
|
5771
|
-
\u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0432 \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0435\u043C \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435: :status :reset :stage <id> :pause :resume :cringe :persona :log :quit
|
|
6335
|
+
\u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0432 \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0435\u043C \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435: :status :reset :stage <id|num> :pause :resume :cringe :persona :log :quit
|
|
5772
6336
|
`;
|
|
5773
6337
|
async function main() {
|
|
5774
6338
|
const argv = mri(process.argv.slice(2), {
|
|
@@ -5796,15 +6360,71 @@ async function main() {
|
|
|
5796
6360
|
"message-style",
|
|
5797
6361
|
"initiative",
|
|
5798
6362
|
"life-sharing",
|
|
5799
|
-
"privacy"
|
|
6363
|
+
"privacy",
|
|
6364
|
+
"config"
|
|
6365
|
+
],
|
|
6366
|
+
boolean: [
|
|
6367
|
+
"help",
|
|
6368
|
+
"list",
|
|
6369
|
+
"reset",
|
|
6370
|
+
"new",
|
|
6371
|
+
"json-events",
|
|
6372
|
+
"headless",
|
|
6373
|
+
"server",
|
|
6374
|
+
"print-config",
|
|
6375
|
+
"print-systemd",
|
|
6376
|
+
"print-docker",
|
|
6377
|
+
"no-start"
|
|
5800
6378
|
],
|
|
5801
|
-
boolean: ["help", "list", "reset", "new"],
|
|
5802
6379
|
alias: { h: "help" }
|
|
5803
6380
|
});
|
|
6381
|
+
const positional = argv._ ?? [];
|
|
6382
|
+
const isServer = positional[0] === "server" || !!argv.server || !!argv["print-config"] || !!argv["print-systemd"] || !!argv["print-docker"];
|
|
6383
|
+
if (isServer) {
|
|
6384
|
+
await runServer(argv);
|
|
6385
|
+
return;
|
|
6386
|
+
}
|
|
5804
6387
|
if (argv.help) {
|
|
5805
6388
|
process.stdout.write(HELP);
|
|
5806
6389
|
return;
|
|
5807
6390
|
}
|
|
6391
|
+
const isHeadless = !!(argv["json-events"] || argv.headless || argv.list || argv.help);
|
|
6392
|
+
if (!isHeadless) {
|
|
6393
|
+
const stdin = process.stdin;
|
|
6394
|
+
const stdout = process.stdout;
|
|
6395
|
+
const stdinOk = !!stdin.isTTY;
|
|
6396
|
+
const stdoutOk = !!stdout.isTTY;
|
|
6397
|
+
if (!stdinOk || !stdoutOk) {
|
|
6398
|
+
process.stderr.write(
|
|
6399
|
+
`
|
|
6400
|
+
[girl-agent] \u044D\u0442\u043E\u0442 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 ink-\u0432\u0438\u0437\u0430\u0440\u0434 (\u043D\u0435\u0442 TTY).
|
|
6401
|
+
stdin.isTTY = ${stdinOk}, stdout.isTTY = ${stdoutOk}
|
|
6402
|
+
|
|
6403
|
+
\u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C (\u0434\u043B\u044F \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u0432 / docker / ssh \u0431\u0435\u0437 -t / cron / CI):
|
|
6404
|
+
|
|
6405
|
+
1. \u043F\u043E\u0441\u0442\u0430\u0432\u044C \u0441\u0435\u0431\u0435 girl-agent \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 (\u0431\u0435\u0437 node \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435):
|
|
6406
|
+
curl -fsSL https://raw.githubusercontent.com/TheSashaDev/girl-agent/main/scripts/install.sh | sh
|
|
6407
|
+
\u0434\u0430\u043B\u044C\u0448\u0435: girl-agent # ink-\u0432\u0438\u0437\u0430\u0440\u0434 \u0432 \u043E\u0431\u044B\u0447\u043D\u043E\u043C tty
|
|
6408
|
+
|
|
6409
|
+
2. \u0433\u043E\u0442\u043E\u0432\u044B\u0439 \u043A\u043E\u043D\u0444\u0438\u0433 + headless (\u0434\u043B\u044F systemd / cron / CI):
|
|
6410
|
+
girl-agent server --print-config > bot.json
|
|
6411
|
+
# \u043E\u0442\u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u0443\u0439 bot.json
|
|
6412
|
+
girl-agent server --config bot.json --headless
|
|
6413
|
+
|
|
6414
|
+
3. docker (\u0432\u0441\u0451 \u0432\u043D\u0443\u0442\u0440\u0438 \u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440\u0430, \u043D\u043E\u043B\u044C \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439 \u043D\u0430 \u0445\u043E\u0441\u0442\u0435):
|
|
6415
|
+
docker run -it --rm -v girl-agent-data:/data \\
|
|
6416
|
+
ghcr.io/thesashadev/girl-agent:latest
|
|
6417
|
+
|
|
6418
|
+
4. systemd: girl-agent server --print-systemd
|
|
6419
|
+
docker: girl-agent server --print-docker
|
|
6420
|
+
|
|
6421
|
+
5. \u043D\u0430 windows \u0431\u044B\u0441\u0442\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043E \u2014 \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0438\u043D\u0441\u0442\u0430\u043B\u043B\u0435\u0440 girl-agent-installer.exe.
|
|
6422
|
+
`
|
|
6423
|
+
);
|
|
6424
|
+
process.exit(2);
|
|
6425
|
+
}
|
|
6426
|
+
}
|
|
6427
|
+
const jsonEvents = !!(argv["json-events"] || argv.headless);
|
|
5808
6428
|
if (argv.age != null) {
|
|
5809
6429
|
const a = Number(argv.age);
|
|
5810
6430
|
if (!Number.isFinite(a) || a < 13 || a > 99) {
|
|
@@ -5834,7 +6454,7 @@ ${profiles.join("\n")}
|
|
|
5834
6454
|
cfg.stage = "tg-given-cold";
|
|
5835
6455
|
await writeConfig(cfg);
|
|
5836
6456
|
}
|
|
5837
|
-
await runRuntime(cfg);
|
|
6457
|
+
await runRuntime(cfg, { jsonEvents });
|
|
5838
6458
|
return;
|
|
5839
6459
|
}
|
|
5840
6460
|
const presetForFlags = argv["api-preset"] ? findPreset(String(argv["api-preset"])) : void 0;
|
|
@@ -5850,7 +6470,7 @@ ${profiles.join("\n")}
|
|
|
5850
6470
|
const generated = await generatePersonaPack(llm, cfg.slug, cfg.name, cfg.age, cfg.nationality, personaNotesForGeneration2(cfg));
|
|
5851
6471
|
cfg.busySchedule = generated.busySchedule;
|
|
5852
6472
|
await writeConfig(cfg);
|
|
5853
|
-
await runRuntime(cfg);
|
|
6473
|
+
await runRuntime(cfg, { jsonEvents });
|
|
5854
6474
|
return;
|
|
5855
6475
|
}
|
|
5856
6476
|
if (!argv.new && !argv.profile && !haveEnoughForFlags) {
|
|
@@ -5860,7 +6480,7 @@ ${profiles.join("\n")}
|
|
|
5860
6480
|
if (cfg) {
|
|
5861
6481
|
process.stdout.write(`\u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044E \u043F\u0440\u043E\u0444\u0438\u043B\u044C: ${cfg.name}
|
|
5862
6482
|
`);
|
|
5863
|
-
await runRuntime(cfg);
|
|
6483
|
+
await runRuntime(cfg, { jsonEvents });
|
|
5864
6484
|
return;
|
|
5865
6485
|
}
|
|
5866
6486
|
} else if (profiles.length > 1) {
|
|
@@ -5876,7 +6496,7 @@ ${profiles.join("\n")}
|
|
|
5876
6496
|
const inst = render(
|
|
5877
6497
|
/* @__PURE__ */ React3.createElement(Wizard, { onDone: async (cfg) => {
|
|
5878
6498
|
inst.unmount();
|
|
5879
|
-
await runRuntime(cfg);
|
|
6499
|
+
await runRuntime(cfg, { jsonEvents });
|
|
5880
6500
|
resolve();
|
|
5881
6501
|
} }),
|
|
5882
6502
|
{ exitOnCtrlC: true }
|
|
@@ -5910,7 +6530,7 @@ async function buildConfigFromFlags(argv) {
|
|
|
5910
6530
|
nationality,
|
|
5911
6531
|
tz,
|
|
5912
6532
|
mode,
|
|
5913
|
-
stage: argv.stage,
|
|
6533
|
+
stage: findStage(argv.stage).id,
|
|
5914
6534
|
llm: { presetId, proto, baseURL, apiKey: String(argv["api-key"] ?? preset?.defaultApiKey ?? ""), model },
|
|
5915
6535
|
telegram: mode === "bot" ? { botToken: String(argv.token ?? "") } : {
|
|
5916
6536
|
apiId: Number(argv["api-id"] ?? 0),
|
|
@@ -5949,9 +6569,13 @@ function personaNotesForGeneration2(cfg) {
|
|
|
5949
6569
|
].filter(Boolean);
|
|
5950
6570
|
return parts.join("\n\n");
|
|
5951
6571
|
}
|
|
5952
|
-
async function runRuntime(cfg) {
|
|
6572
|
+
async function runRuntime(cfg, opts = {}) {
|
|
5953
6573
|
const rt = new Runtime(cfg);
|
|
5954
6574
|
await rt.start();
|
|
6575
|
+
if (opts.jsonEvents) {
|
|
6576
|
+
await runHeadlessJsonEvents(rt);
|
|
6577
|
+
return;
|
|
6578
|
+
}
|
|
5955
6579
|
const inst = render(/* @__PURE__ */ React3.createElement(Dashboard, { runtime: rt }), { exitOnCtrlC: true });
|
|
5956
6580
|
process.on("SIGINT", async () => {
|
|
5957
6581
|
await rt.stop();
|
|
@@ -5961,7 +6585,17 @@ async function runRuntime(cfg) {
|
|
|
5961
6585
|
await inst.waitUntilExit();
|
|
5962
6586
|
await rt.stop();
|
|
5963
6587
|
}
|
|
6588
|
+
process.on("unhandledRejection", (reason) => {
|
|
6589
|
+
const r = reason;
|
|
6590
|
+
const text = typeof r === "object" && r && r.stack ? r.stack : String(reason);
|
|
6591
|
+
process.stderr.write("[girl-agent] unhandled rejection: " + text + "\n");
|
|
6592
|
+
process.exit(1);
|
|
6593
|
+
});
|
|
6594
|
+
process.on("uncaughtException", (err) => {
|
|
6595
|
+
process.stderr.write("[girl-agent] uncaught: " + (err?.stack ?? err) + "\n");
|
|
6596
|
+
process.exit(1);
|
|
6597
|
+
});
|
|
5964
6598
|
main().catch((e) => {
|
|
5965
|
-
process.stderr.write("fatal: " + (e?.stack ?? e) + "\n");
|
|
6599
|
+
process.stderr.write("[girl-agent] fatal: " + (e?.stack ?? e) + "\n");
|
|
5966
6600
|
process.exit(1);
|
|
5967
6601
|
});
|