@thesashadev/girl-agent 0.1.12 → 0.1.14

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 (3) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cli.js +512 -29
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.14
4
+
5
+ Дата: 2026-05-08
6
+
7
+ - Merge pull request #59 from TheSashaDev/devin/1778231542-oauth-fixed-port
8
+ - fix: update OAuth client credentials and add client_secret to token requests
9
+ - fix: use fixed port 3000 for OAuth callback
10
+
11
+ ## 0.1.13
12
+
13
+ Дата: 2026-05-08
14
+
15
+ - Merge pull request #56 from TheSashaDev/devin/1778220196-data-migration-system
16
+ - feat: GirlAI OAuth login and token refresh support
17
+ - feat: add GirlAI API preset and recommended status for LLM providers
18
+ - feat: extend migration system with LLM support and auto-run on startup
19
+ - fix: robust version lookup for bundled output, fix AGENTS.md auto-run claim
20
+ - fix: remove unused import, dynamic version from package.json, use static listProfiles
21
+ - feat: AGENTS.md + update command with migration system
22
+
3
23
  ## 0.1.12
4
24
 
5
25
  Дата: 2026-05-08
package/dist/cli.js CHANGED
@@ -376,6 +376,27 @@ import BigText from "ink-big-text";
376
376
  // src/presets/llm.ts
377
377
  init_esm_shims();
378
378
  var LLM_PRESETS = [
379
+ {
380
+ id: "girlai",
381
+ name: "GirlAI",
382
+ proto: "openai",
383
+ baseURL: "https://api.girl-agent.com/v1",
384
+ defaultModel: "GirlAI-test",
385
+ models: ["GirlAI-test"],
386
+ recommended: true,
387
+ oauth: true,
388
+ hint: "\u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u043C\u044B\u0439 \xB7 OpenAI-compatible gateway (\u0420\u0424, \u043E\u043F\u043B\u0430\u0442\u0430)"
389
+ },
390
+ {
391
+ id: "claudehub",
392
+ name: "ClaudeHub",
393
+ proto: "anthropic",
394
+ baseURL: "https://api.claudehub.fun",
395
+ defaultModel: "claude-sonnet-4.6",
396
+ models: ["claude-opus-4.7", "claude-opus-4.6", "claude-opus-4.5", "claude-sonnet-4.6", "claude-sonnet-4.5", "claude-haiku-4.5", "gpt-5.5", "gpt-5.4"],
397
+ recommended: true,
398
+ hint: "\u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u043C\u044B\u0439 \xB7 ClaudeHub proxy for Anthropic & OpenAI (\u0420\u0424, \u0421\u0411\u041F, \u043A\u0440\u0438\u043F\u0442\u0430)"
399
+ },
379
400
  {
380
401
  id: "openai",
381
402
  name: "OpenAI",
@@ -514,15 +535,6 @@ var LLM_PRESETS = [
514
535
  defaultModel: "llama-3.3-70b",
515
536
  models: ["llama-3.3-70b", "llama-4-scout-17b-16e-instruct", "qwen-3-32b"]
516
537
  },
517
- {
518
- id: "claudehub",
519
- name: "ClaudeHub",
520
- proto: "anthropic",
521
- baseURL: "https://api.claudehub.fun",
522
- defaultModel: "claude-sonnet-4.6",
523
- models: ["claude-opus-4.7", "claude-opus-4.6", "claude-opus-4.5", "claude-sonnet-4.6", "claude-sonnet-4.5", "claude-haiku-4.5", "gpt-5.5", "gpt-5.4"],
524
- hint: "ClaudeHub proxy for Anthropic & OpenAI (\u0420\u0424, \u0421\u0411\u041F, \u043A\u0440\u0438\u043F\u0442\u0430)"
525
- },
526
538
  {
527
539
  id: "custom-openai",
528
540
  name: "Custom (OpenAI-compatible)",
@@ -1048,6 +1060,151 @@ async function writeAgenda(slug, items) {
1048
1060
  init_esm_shims();
1049
1061
  import OpenAI from "openai";
1050
1062
  import Anthropic from "@anthropic-ai/sdk";
1063
+
1064
+ // src/oauth/girlai.ts
1065
+ init_esm_shims();
1066
+ import http from "http";
1067
+ import crypto from "crypto";
1068
+ import { exec } from "child_process";
1069
+ var GIRLAI_BASE = "https://api.girl-agent.com";
1070
+ var AUTHORIZE_URL = `${GIRLAI_BASE}/oauth/authorize`;
1071
+ var TOKEN_URL = `${GIRLAI_BASE}/oauth/token`;
1072
+ var REVOKE_URL = `${GIRLAI_BASE}/oauth/revoke`;
1073
+ var CLIENT_ID = "oac_dcce490e74a452a9ed20";
1074
+ var CLIENT_SECRET = "abnfSGmeisM7SFdMn_c1MwFYAHaqzgs7";
1075
+ var CALLBACK_PORT = 3e3;
1076
+ var REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/callback`;
1077
+ async function runOAuthFlow(log) {
1078
+ const state = crypto.randomBytes(16).toString("hex");
1079
+ const { waitForCode, close } = await startCallbackServer(state);
1080
+ const authorizeParams = new URLSearchParams({
1081
+ client_id: CLIENT_ID,
1082
+ redirect_uri: REDIRECT_URI,
1083
+ response_type: "code",
1084
+ state
1085
+ });
1086
+ const url = `${AUTHORIZE_URL}?${authorizeParams}`;
1087
+ log(`\u043E\u0442\u043A\u0440\u044B\u0432\u0430\u044E \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0434\u043B\u044F \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438: ${url}`);
1088
+ openBrowser(url);
1089
+ let code;
1090
+ try {
1091
+ code = await waitForCode;
1092
+ } finally {
1093
+ close();
1094
+ }
1095
+ log("\u043A\u043E\u0434 \u043F\u043E\u043B\u0443\u0447\u0435\u043D, \u043E\u0431\u043C\u0435\u043D\u0438\u0432\u0430\u044E \u043D\u0430 \u0442\u043E\u043A\u0435\u043D...");
1096
+ return exchangeCode(code, REDIRECT_URI);
1097
+ }
1098
+ async function exchangeCode(code, redirectUri = REDIRECT_URI) {
1099
+ const body = new URLSearchParams({
1100
+ grant_type: "authorization_code",
1101
+ code,
1102
+ client_id: CLIENT_ID,
1103
+ client_secret: CLIENT_SECRET,
1104
+ redirect_uri: redirectUri
1105
+ });
1106
+ const res = await fetch(TOKEN_URL, {
1107
+ method: "POST",
1108
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1109
+ body: body.toString()
1110
+ });
1111
+ if (!res.ok) {
1112
+ const text = await res.text();
1113
+ throw new Error(`OAuth token exchange failed (${res.status}): ${text}`);
1114
+ }
1115
+ const data = await res.json();
1116
+ return {
1117
+ accessToken: data.access_token,
1118
+ refreshToken: data.refresh_token,
1119
+ expiresAt: Date.now() + data.expires_in * 1e3
1120
+ };
1121
+ }
1122
+ async function refreshAccessToken(refreshToken) {
1123
+ const body = new URLSearchParams({
1124
+ grant_type: "refresh_token",
1125
+ refresh_token: refreshToken,
1126
+ client_id: CLIENT_ID,
1127
+ client_secret: CLIENT_SECRET
1128
+ });
1129
+ const res = await fetch(TOKEN_URL, {
1130
+ method: "POST",
1131
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1132
+ body: body.toString()
1133
+ });
1134
+ if (!res.ok) {
1135
+ const text = await res.text();
1136
+ throw new Error(`OAuth token refresh failed (${res.status}): ${text}`);
1137
+ }
1138
+ const data = await res.json();
1139
+ return {
1140
+ accessToken: data.access_token,
1141
+ refreshToken: data.refresh_token,
1142
+ expiresAt: Date.now() + data.expires_in * 1e3
1143
+ };
1144
+ }
1145
+ function isTokenExpired(expiresAt) {
1146
+ return Date.now() >= expiresAt - 6e4;
1147
+ }
1148
+ function startCallbackServer(expectedState) {
1149
+ return new Promise((resolveSetup) => {
1150
+ let resolveCode;
1151
+ let rejectCode;
1152
+ const waitForCode = new Promise((res, rej) => {
1153
+ resolveCode = res;
1154
+ rejectCode = rej;
1155
+ });
1156
+ const timeout = setTimeout(() => {
1157
+ rejectCode(new Error("OAuth callback timeout (5 minutes)"));
1158
+ server.close();
1159
+ }, 5 * 60 * 1e3);
1160
+ const server = http.createServer((req, res) => {
1161
+ const url = new URL(req.url ?? "/", `http://localhost`);
1162
+ if (url.pathname !== "/callback") {
1163
+ res.writeHead(404);
1164
+ res.end("not found");
1165
+ return;
1166
+ }
1167
+ const code = url.searchParams.get("code");
1168
+ const state = url.searchParams.get("state");
1169
+ const error = url.searchParams.get("error");
1170
+ if (error) {
1171
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1172
+ res.end("<h2>\u0410\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u044F \u043E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0430</h2><p>\u041C\u043E\u0436\u0435\u0448\u044C \u0437\u0430\u043A\u0440\u044B\u0442\u044C \u044D\u0442\u0443 \u0432\u043A\u043B\u0430\u0434\u043A\u0443.</p>");
1173
+ clearTimeout(timeout);
1174
+ rejectCode(new Error(`OAuth denied: ${error}`));
1175
+ return;
1176
+ }
1177
+ if (!code || state !== expectedState) {
1178
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
1179
+ res.end("<h2>\u041E\u0448\u0438\u0431\u043A\u0430</h2><p>\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 state \u0438\u043B\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 code.</p>");
1180
+ return;
1181
+ }
1182
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1183
+ res.end("<h2>\u0410\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u044F \u0443\u0441\u043F\u0435\u0448\u043D\u0430!</h2><p>\u041C\u043E\u0436\u0435\u0448\u044C \u0437\u0430\u043A\u0440\u044B\u0442\u044C \u044D\u0442\u0443 \u0432\u043A\u043B\u0430\u0434\u043A\u0443 \u0438 \u0432\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B.</p>");
1184
+ clearTimeout(timeout);
1185
+ resolveCode(code);
1186
+ });
1187
+ server.listen(CALLBACK_PORT, "127.0.0.1", () => {
1188
+ resolveSetup({
1189
+ waitForCode,
1190
+ close: () => server.close()
1191
+ });
1192
+ });
1193
+ });
1194
+ }
1195
+ function openBrowser(url) {
1196
+ const platform = process.platform;
1197
+ const cmd = platform === "darwin" ? `open "${url}"` : platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}" 2>/dev/null || sensible-browser "${url}" 2>/dev/null || echo "\u043E\u0442\u043A\u0440\u043E\u0439 \u0432\u0440\u0443\u0447\u043D\u0443\u044E: ${url}"`;
1198
+ exec(cmd, (err) => {
1199
+ if (err) {
1200
+ process.stderr.write(`[girlai-oauth] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u043E\u0442\u043A\u0440\u043E\u0439 \u0432\u0440\u0443\u0447\u043D\u0443\u044E:
1201
+ ${url}
1202
+ `);
1203
+ }
1204
+ });
1205
+ }
1206
+
1207
+ // src/llm/index.ts
1051
1208
  var LLM_TIMEOUT_MS = 12e4;
1052
1209
  var LLM_MAX_RETRIES = 1;
1053
1210
  var OpenAILike = class {
@@ -1070,7 +1227,24 @@ var OpenAILike = class {
1070
1227
  cfg;
1071
1228
  client;
1072
1229
  fetchClient;
1230
+ async ensureFreshToken() {
1231
+ if (!this.cfg.oauthRefreshToken || !this.cfg.oauthExpiresAt) return;
1232
+ if (!isTokenExpired(this.cfg.oauthExpiresAt)) return;
1233
+ try {
1234
+ const tokens = await refreshAccessToken(this.cfg.oauthRefreshToken);
1235
+ this.cfg.apiKey = tokens.accessToken;
1236
+ this.cfg.oauthRefreshToken = tokens.refreshToken;
1237
+ this.cfg.oauthExpiresAt = tokens.expiresAt;
1238
+ const key = tokens.accessToken;
1239
+ this.client = new OpenAI({ apiKey: key, baseURL: normalizeBaseURL(this.cfg.baseURL), timeout: LLM_TIMEOUT_MS, maxRetries: LLM_MAX_RETRIES });
1240
+ this.fetchClient = new OpenAI({ apiKey: key, baseURL: normalizeBaseURL(this.cfg.baseURL), timeout: LLM_TIMEOUT_MS, maxRetries: LLM_MAX_RETRIES, fetch: compatibleFetch });
1241
+ } catch (err) {
1242
+ process.stderr.write(`[oauth] token refresh failed: ${err instanceof Error ? err.message : err}
1243
+ `);
1244
+ }
1245
+ }
1073
1246
  async chat(messages, opts = {}) {
1247
+ await this.ensureFreshToken();
1074
1248
  const params = {
1075
1249
  model: this.cfg.model,
1076
1250
  messages: openAIMessages(messages),
@@ -1559,14 +1733,14 @@ var DEFAULT_PROXY = "https://tgproxy.girl-agent.com";
1559
1733
  function proxyUrl() {
1560
1734
  return process.env.GIRL_AGENT_AUTH_PROXY ?? DEFAULT_PROXY;
1561
1735
  }
1562
- async function post(path6, body) {
1563
- const res = await fetch(`${proxyUrl()}${path6}`, {
1736
+ async function post(path7, body) {
1737
+ const res = await fetch(`${proxyUrl()}${path7}`, {
1564
1738
  method: "POST",
1565
1739
  headers: { "Content-Type": "application/json" },
1566
1740
  body: JSON.stringify(body)
1567
1741
  });
1568
1742
  const data = await res.json();
1569
- if (!res.ok) throw new Error(data.error ?? `proxy ${path6} failed (${res.status})`);
1743
+ if (!res.ok) throw new Error(data.error ?? `proxy ${path7} failed (${res.status})`);
1570
1744
  return data;
1571
1745
  }
1572
1746
  function remoteSendCode(phone) {
@@ -1813,6 +1987,9 @@ function Wizard({ initial, onDone }) {
1813
1987
  const [llmBaseURL, setLlmBaseURL] = useState(initial?.llm?.baseURL ?? "");
1814
1988
  const [llmModel, setLlmModel] = useState(initial?.llm?.model ?? "");
1815
1989
  const [llmKey, setLlmKey] = useState(initial?.llm?.apiKey ?? "");
1990
+ const [oauthRefreshToken, setOauthRefreshToken] = useState(initial?.llm?.oauthRefreshToken ?? "");
1991
+ const [oauthExpiresAt, setOauthExpiresAt] = useState(initial?.llm?.oauthExpiresAt ?? 0);
1992
+ const [oauthStatus, setOauthStatus] = useState("");
1816
1993
  const [nationality, setNationality] = useState(initial?.nationality ?? "RU");
1817
1994
  const [name, setName] = useState(initial?.name ?? "");
1818
1995
  const [ageStr, setAgeStr] = useState(initial?.age ? String(initial.age) : "");
@@ -2035,7 +2212,7 @@ function Wizard({ initial, onDone }) {
2035
2212
  SelectInput,
2036
2213
  {
2037
2214
  limit: 10,
2038
- items: LLM_PRESETS.map((p) => ({ label: `${p.name}${p.hint ? ` \xB7 ${p.hint}` : ""}`, value: p.id })),
2215
+ items: LLM_PRESETS.map((p) => ({ label: `${p.name}${p.recommended ? " \u2605" : ""}${p.hint ? ` \xB7 ${p.hint}` : ""}`, value: p.id })),
2039
2216
  onSelect: (it) => {
2040
2217
  const preset = findPreset(it.value);
2041
2218
  setLlmPresetId(preset.id);
@@ -2044,11 +2221,43 @@ function Wizard({ initial, onDone }) {
2044
2221
  setLlmModel(preset.defaultModel);
2045
2222
  setLlmKey(preset.defaultApiKey ?? "");
2046
2223
  if (preset.custom) setStep("api-base");
2224
+ else if (preset.oauth) setStep("api-auth-method");
2047
2225
  else setStep("api-model");
2048
2226
  }
2049
2227
  }
2050
2228
  )));
2051
2229
  }
2230
+ if (step === "api-auth-method") {
2231
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0441\u043F\u043E\u0441\u043E\u0431 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (GirlAI)" }), /* @__PURE__ */ React.createElement(Bar, { step: 2, total: 9 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React.createElement(
2232
+ SelectInput,
2233
+ {
2234
+ items: [
2235
+ { label: "\u{1F511} \u0412\u043E\u0439\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 GirlAI \u0430\u043A\u043A\u0430\u0443\u043D\u0442 (OAuth)", value: "oauth" },
2236
+ { label: "\u{1F4CB} \u0412\u0432\u0435\u0441\u0442\u0438 API \u043A\u043B\u044E\u0447 \u0432\u0440\u0443\u0447\u043D\u0443\u044E", value: "apikey" }
2237
+ ],
2238
+ onSelect: (it) => {
2239
+ if (it.value === "oauth") {
2240
+ setOauthStatus("\u043E\u0442\u043A\u0440\u044B\u0432\u0430\u044E \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0434\u043B\u044F \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438...");
2241
+ setStep("api-oauth");
2242
+ runOAuthFlow((msg) => setOauthStatus(msg)).then((tokens) => {
2243
+ setLlmKey(tokens.accessToken);
2244
+ setOauthRefreshToken(tokens.refreshToken);
2245
+ setOauthExpiresAt(tokens.expiresAt);
2246
+ setStep("api-model");
2247
+ }).catch((err) => {
2248
+ setError(err.message);
2249
+ setStep("api-auth-method");
2250
+ });
2251
+ } else {
2252
+ setStep("api-model");
2253
+ }
2254
+ }
2255
+ }
2256
+ )));
2257
+ }
2258
+ if (step === "api-oauth") {
2259
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u044F GirlAI" }), /* @__PURE__ */ React.createElement(Bar, { step: 2, total: 9 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" }), /* @__PURE__ */ React.createElement(Text, null, " ", oauthStatus || "\u043E\u0436\u0438\u0434\u0430\u044E \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u044E \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435...")), error && /* @__PURE__ */ React.createElement(Text, { color: "red" }, error), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "\u0432\u043E\u0439\u0434\u0438 \u0432 \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0432 \u043E\u0442\u043A\u0440\u044B\u0432\u0448\u0435\u043C\u0441\u044F \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0438 \u0434\u043E\u0441\u0442\u0443\u043F"));
2260
+ }
2052
2261
  if (step === "api-base") {
2053
2262
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "base URL \u0434\u043B\u044F custom API" }), /* @__PURE__ */ React.createElement(Bar, { step: 2, total: 9 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Base URL: "), /* @__PURE__ */ React.createElement(TextInput, { value: llmBaseURL, onChange: setLlmBaseURL, onSubmit: () => setStep("api-model") })));
2054
2263
  }
@@ -2530,7 +2739,14 @@ function Wizard({ initial, onDone }) {
2530
2739
  tz: tz || defaultTzForNationality(nationality),
2531
2740
  mode,
2532
2741
  stage: overrides.stage ?? stage,
2533
- llm: { presetId: llmPresetId, proto: llmProto, baseURL: llmBaseURL, apiKey: llmKey, model: llmModel },
2742
+ llm: {
2743
+ presetId: llmPresetId,
2744
+ proto: llmProto,
2745
+ baseURL: llmBaseURL,
2746
+ apiKey: llmKey,
2747
+ model: llmModel,
2748
+ ...oauthRefreshToken ? { oauthRefreshToken, oauthExpiresAt } : {}
2749
+ },
2534
2750
  telegram: mode === "bot" ? { botToken } : { apiId: Number(apiId), apiHash, phone, sessionString },
2535
2751
  mcp: overrides.mcp ?? pickedMcp.map((id) => ({ id, secrets: mcpSecrets[id] ?? {} })),
2536
2752
  privacy,
@@ -3168,8 +3384,8 @@ phoneAvailable=false \u043A\u043E\u0433\u0434\u0430: \u0441\u043F\u0438\u0442, \
3168
3384
  }
3169
3385
  async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(), conflict = null) {
3170
3386
  const dateLocal = localDateStr(cfg.tz, now);
3171
- const path6 = `daily-life/${dateLocal}.md`;
3172
- const existing = await readMd(cfg.slug, path6);
3387
+ const path7 = `daily-life/${dateLocal}.md`;
3388
+ const existing = await readMd(cfg.slug, path7);
3173
3389
  if (existing) {
3174
3390
  try {
3175
3391
  const m = existing.match(/<!--daily:(.+?)-->/s);
@@ -3201,7 +3417,7 @@ async function loadOrGenerateDailyLife(llm, cfg, now = /* @__PURE__ */ new Date(
3201
3417
  dl = { dateLocal, vibe: "\u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u0434\u0435\u043D\u044C", blocks: [], events: [], wants: [] };
3202
3418
  }
3203
3419
  const human = renderDailyLifeHuman(dl);
3204
- await writeMd(cfg.slug, path6, `${human}
3420
+ await writeMd(cfg.slug, path7, `${human}
3205
3421
 
3206
3422
  <!--daily:${JSON.stringify(dl)}-->
3207
3423
  `);
@@ -3431,9 +3647,9 @@ async function ensureDefaults(cfg) {
3431
3647
  ["time/promises.md", "# promises\n"],
3432
3648
  ["memory/uncertain.md", "# uncertain\n"]
3433
3649
  ];
3434
- await Promise.all(defaults.map(async ([path6, content]) => {
3435
- const current = await readMd(cfg.slug, path6);
3436
- if (!current.trim()) await writeMd(cfg.slug, path6, content + "\n");
3650
+ await Promise.all(defaults.map(async ([path7, content]) => {
3651
+ const current = await readMd(cfg.slug, path7);
3652
+ if (!current.trim()) await writeMd(cfg.slug, path7, content + "\n");
3437
3653
  }));
3438
3654
  }
3439
3655
  async function loadRealismContext(cfg, incoming) {
@@ -5969,9 +6185,201 @@ function profileSummary(cfg) {
5969
6185
 
5970
6186
  // src/server.ts
5971
6187
  init_esm_shims();
5972
- import fs4 from "fs/promises";
5973
- import path5 from "path";
6188
+ import fs5 from "fs/promises";
6189
+ import path6 from "path";
5974
6190
  import os from "os";
6191
+
6192
+ // src/migrations/index.ts
6193
+ init_esm_shims();
6194
+ import { promises as fs4 } from "fs";
6195
+ import path5 from "path";
6196
+ import { readFileSync } from "fs";
6197
+ import { fileURLToPath as fileURLToPath2 } from "url";
6198
+
6199
+ // src/migrations/0112-add-use-wss-default.ts
6200
+ init_esm_shims();
6201
+ var migration0112 = {
6202
+ id: "0112-add-use-wss-default",
6203
+ description: "\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C WSS \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0445 \u043F\u0440\u043E\u0444\u0438\u043B\u044F\u0445",
6204
+ async migrate(ctx) {
6205
+ if (ctx.config.telegram.useWSS === void 0 || ctx.config.telegram.useWSS === false) {
6206
+ ctx.config.telegram.useWSS = true;
6207
+ }
6208
+ return ctx.config;
6209
+ }
6210
+ };
6211
+
6212
+ // src/migrations/0113-ensure-communication-md.ts
6213
+ init_esm_shims();
6214
+ var migration0113 = {
6215
+ id: "0113-ensure-communication-md",
6216
+ description: "\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C communication.md \u0447\u0435\u0440\u0435\u0437 AI \u0435\u0441\u043B\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442",
6217
+ needsLLM: true,
6218
+ async migrate(ctx) {
6219
+ const { config, llm, log } = ctx;
6220
+ const existing = await readMd(config.slug, "communication.md");
6221
+ if (existing.trim()) return config;
6222
+ if (!llm) {
6223
+ log("communication.md \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u043D\u043E LLM \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u2014 \u043F\u0440\u043E\u043F\u0443\u0441\u043A");
6224
+ return config;
6225
+ }
6226
+ const persona = await readMd(config.slug, "persona.md");
6227
+ if (!persona.trim()) {
6228
+ log("persona.md \u0442\u043E\u0436\u0435 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u2014 \u043F\u0440\u043E\u043F\u0443\u0441\u043A \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 communication.md");
6229
+ return config;
6230
+ }
6231
+ log("\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C communication.md \u0447\u0435\u0440\u0435\u0437 AI...");
6232
+ const prompt = `\u041D\u0430 \u043E\u0441\u043D\u043E\u0432\u0435 persona.md \u043D\u0438\u0436\u0435 \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0439 communication.md \u2014 \u043F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F \u0432 \u043E\u0431\u0449\u0435\u043D\u0438\u0438 ${config.name}, ${config.age} \u043B\u0435\u0442. \u0421\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430:
6233
+
6234
+ # \u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F \u0432 \u043E\u0431\u0449\u0435\u043D\u0438\u0438
6235
+ ## \u041A\u043E\u0433\u0434\u0430 \u043F\u0438\u0448\u0435\u0442 (\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0435 \u0447\u0430\u0441\u044B, \u0447\u0430\u0441\u0442\u043E\u0442\u0430)
6236
+ ## \u0421\u0442\u0438\u043B\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 (\u0434\u043B\u0438\u043D\u0430, \u044D\u043C\u043E\u0434\u0437\u0438, \u0441\u0442\u0438\u043A\u0435\u0440\u044B, \u0432\u043E\u0439\u0441\u044B)
6237
+ ## \u0427\u0442\u043E \u0431\u0435\u0441\u0438\u0442 \u0432 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0435 (\u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u044B)
6238
+ ## \u0422\u0435\u043C\u044B \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0437\u0430\u0445\u043E\u0434\u044F\u0442 / \u043D\u0435 \u0437\u0430\u0445\u043E\u0434\u044F\u0442
6239
+ ## \u0420\u0435\u0430\u043A\u0446\u0438\u044F \u043D\u0430 \u0433\u043E\u043B\u043E\u0441\u043E\u0432\u044B\u0435 / \u0432\u0438\u0434\u0435\u043E / \u0444\u043E\u0442\u043E
6240
+
6241
+ \u041F\u0438\u0448\u0438 \u043E\u0442 \u0442\u0440\u0435\u0442\u044C\u0435\u0433\u043E \u043B\u0438\u0446\u0430, \u043A\u0440\u0430\u0442\u043A\u043E, \u0431\u0435\u0437 \u044D\u043C\u043E\u0434\u0437\u0438. \u041D\u0435 \u0431\u043E\u043B\u0435\u0435 250 \u0441\u043B\u043E\u0432.
6242
+
6243
+ persona.md:
6244
+ ${persona}`;
6245
+ const response = await llm.chat([
6246
+ { role: "system", content: "\u0422\u044B \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u0448\u044C \u0444\u0430\u0439\u043B\u044B \u043F\u0440\u043E\u0444\u0438\u043B\u044F \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u0436\u0430 \u0434\u043B\u044F \u0440\u043E\u043B\u0435\u0432\u043E\u0439 \u0438\u0433\u0440\u044B." },
6247
+ { role: "user", content: prompt }
6248
+ ]);
6249
+ if (response?.trim()) {
6250
+ await writeMd(config.slug, "communication.md", response);
6251
+ log("communication.md \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D");
6252
+ }
6253
+ return config;
6254
+ }
6255
+ };
6256
+
6257
+ // src/migrations/index.ts
6258
+ var MIGRATIONS_FILE = () => path5.join(DATA_ROOT, ".migrations.json");
6259
+ async function readMigrationState() {
6260
+ try {
6261
+ const raw = await fs4.readFile(MIGRATIONS_FILE(), "utf8");
6262
+ return JSON.parse(raw);
6263
+ } catch {
6264
+ return { appliedMigrations: [], lastRunVersion: "0.0.0", lastRunAt: "" };
6265
+ }
6266
+ }
6267
+ async function writeMigrationState(state) {
6268
+ await fs4.mkdir(DATA_ROOT, { recursive: true });
6269
+ await fs4.writeFile(MIGRATIONS_FILE(), JSON.stringify(state, null, 2), "utf8");
6270
+ }
6271
+ var ALL_MIGRATIONS = [
6272
+ migration0112,
6273
+ migration0113
6274
+ ];
6275
+ async function pendingMigrations() {
6276
+ const state = await readMigrationState();
6277
+ return ALL_MIGRATIONS.filter((m) => !state.appliedMigrations.includes(m.id));
6278
+ }
6279
+ async function runMigrations(opts) {
6280
+ const pending = await pendingMigrations();
6281
+ const log = opts?.verbose ? (msg) => process.stderr.write(msg + "\n") : () => {
6282
+ };
6283
+ if (pending.length === 0) {
6284
+ log("\u0432\u0441\u0435 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0443\u0436\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u044B, \u0434\u0430\u043D\u043D\u044B\u0435 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u044B.");
6285
+ return { profilesUpdated: 0, migrationsApplied: [], warnings: [], errors: [] };
6286
+ }
6287
+ const profiles = await listProfiles();
6288
+ const state = await readMigrationState();
6289
+ const result = { profilesUpdated: 0, migrationsApplied: [], warnings: [], errors: [] };
6290
+ for (const migration of pending) {
6291
+ log(`
6292
+ [migration] ${migration.id}: ${migration.description}`);
6293
+ let profilesAffected = 0;
6294
+ for (const slug of profiles) {
6295
+ const cfg = await readConfig(slug);
6296
+ if (!cfg) {
6297
+ log(` \u043F\u0440\u043E\u043F\u0443\u0441\u043A ${slug}: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C config.json`);
6298
+ continue;
6299
+ }
6300
+ if (migration.needsInput?.length) {
6301
+ const missing = migration.needsInput.filter((field) => {
6302
+ const val = cfg[field.key];
6303
+ return val === void 0 || val === null || val === "";
6304
+ });
6305
+ if (missing.length > 0) {
6306
+ result.warnings.push({
6307
+ migrationId: migration.id,
6308
+ description: migration.description,
6309
+ missingInputs: missing
6310
+ });
6311
+ log(` \u26A0 ${slug}: \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0432\u0432\u043E\u0434: ${missing.map((f) => f.label).join(", ")}`);
6312
+ }
6313
+ }
6314
+ let llm;
6315
+ if (migration.needsLLM && opts?.llmFactory) {
6316
+ try {
6317
+ llm = opts.llmFactory(cfg);
6318
+ } catch (e) {
6319
+ log(` ${slug}: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043E\u0437\u0434\u0430\u0442\u044C LLM: ${e.message}`);
6320
+ }
6321
+ }
6322
+ try {
6323
+ const updated = await migration.migrate({
6324
+ profilePath: profileDir(slug),
6325
+ config: cfg,
6326
+ llm,
6327
+ log: (msg) => log(` ${slug}: ${msg}`)
6328
+ });
6329
+ await writeConfig(updated);
6330
+ profilesAffected++;
6331
+ log(` ${slug}: \u043E\u043A`);
6332
+ } catch (e) {
6333
+ const errMsg = e instanceof Error ? e.message : String(e);
6334
+ log(` ${slug}: \u043E\u0448\u0438\u0431\u043A\u0430 \u2014 ${errMsg}`);
6335
+ result.errors.push({ profile: slug, migration: migration.id, error: errMsg });
6336
+ }
6337
+ }
6338
+ state.appliedMigrations.push(migration.id);
6339
+ result.migrationsApplied.push(migration.id);
6340
+ result.profilesUpdated = Math.max(result.profilesUpdated, profilesAffected);
6341
+ log(` \u043F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u043E \u043A ${profilesAffected} \u043F\u0440\u043E\u0444\u0438\u043B\u044F\u043C`);
6342
+ }
6343
+ state.lastRunVersion = currentVersion();
6344
+ state.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
6345
+ await writeMigrationState(state);
6346
+ return result;
6347
+ }
6348
+ async function checkForPendingMigrations() {
6349
+ const pending = await pendingMigrations();
6350
+ return pending.length > 0;
6351
+ }
6352
+ function formatUpdateWarnings(warnings) {
6353
+ if (!warnings.length) return "";
6354
+ const lines = ["[updater] \u26A0 \u0434\u043B\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E:"];
6355
+ for (const w of warnings) {
6356
+ for (const field of w.missingInputs) {
6357
+ const secret = field.secret ? " (\u0441\u0435\u043A\u0440\u0435\u0442)" : "";
6358
+ lines.push(` \u2022 ${field.label}${secret} \u2014 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0434\u043B\u044F: ${w.description}`);
6359
+ }
6360
+ }
6361
+ return lines.join("\n");
6362
+ }
6363
+ function currentVersion() {
6364
+ try {
6365
+ const here = fileURLToPath2(import.meta.url);
6366
+ let dir = path5.dirname(here);
6367
+ for (let i = 0; i < 5; i++) {
6368
+ const candidate = path5.join(dir, "package.json");
6369
+ try {
6370
+ const pkg = JSON.parse(readFileSync(candidate, "utf8"));
6371
+ if (pkg.name === "@thesashadev/girl-agent" && pkg.version) return pkg.version;
6372
+ } catch {
6373
+ }
6374
+ dir = path5.dirname(dir);
6375
+ }
6376
+ return "unknown";
6377
+ } catch {
6378
+ return "unknown";
6379
+ }
6380
+ }
6381
+
6382
+ // src/server.ts
5975
6383
  var SERVER_HELP = `
5976
6384
  girl-agent server \u2014 automation / ops mode (no TTY required)
5977
6385
 
@@ -6065,7 +6473,7 @@ async function runServer(rawArgv) {
6065
6473
  }
6066
6474
  async function persistAndMaybeStart(cfg, args) {
6067
6475
  await writeConfig(cfg);
6068
- 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)}
6476
+ process.stderr.write(`[server] \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D: ${path6.join(DATA_ROOT, cfg.slug)}
6069
6477
  `);
6070
6478
  if (cfg.llm.apiKey || findPreset(cfg.llm.presetId)?.apiKeyRequired === false) {
6071
6479
  try {
@@ -6091,6 +6499,22 @@ async function persistAndMaybeStart(cfg, args) {
6091
6499
  await startRuntime(cfg, args);
6092
6500
  }
6093
6501
  async function startRuntime(cfg, args) {
6502
+ if (await checkForPendingMigrations()) {
6503
+ process.stderr.write("[updater] \u043E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u044B pending-\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u0437\u0430\u043F\u0443\u0441\u043A...\n");
6504
+ const result = await runMigrations({
6505
+ verbose: true,
6506
+ llmFactory: (c) => {
6507
+ try {
6508
+ return makeLLM(c.llm);
6509
+ } catch {
6510
+ return void 0;
6511
+ }
6512
+ }
6513
+ });
6514
+ if (result.warnings.length) {
6515
+ process.stderr.write(formatUpdateWarnings(result.warnings) + "\n");
6516
+ }
6517
+ }
6094
6518
  const rt = new Runtime(cfg);
6095
6519
  await rt.start();
6096
6520
  const wantsHeadless = !!(args.headless || args.jsonEvents);
@@ -6163,10 +6587,10 @@ function configFromEnv() {
6163
6587
  };
6164
6588
  }
6165
6589
  async function loadConfigFile(file) {
6166
- const abs = path5.isAbsolute(file) ? file : path5.join(process.cwd(), file);
6590
+ const abs = path6.isAbsolute(file) ? file : path6.join(process.cwd(), file);
6167
6591
  let raw;
6168
6592
  try {
6169
- raw = await fs4.readFile(abs, "utf-8");
6593
+ raw = await fs5.readFile(abs, "utf-8");
6170
6594
  } catch (e) {
6171
6595
  process.stderr.write(`[server] \u043D\u0435 \u043C\u043E\u0433\u0443 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C ${abs}: ${e?.message ?? e}
6172
6596
  `);
@@ -6360,11 +6784,11 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
6360
6784
  --mode=bot|userbot
6361
6785
  --token=<bot_token> \u0434\u043B\u044F bot
6362
6786
  --api-id=<n> --api-hash=<h> --phone=<+7\u2026> \u0434\u043B\u044F userbot
6363
- --api-preset=<id> openai|anthropic|openrouter|groq|deepseek|...
6787
+ --api-preset=<id> girlai|claudehub (\u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u043C\u044B\u0435) | openai|anthropic|openrouter|groq|deepseek|...
6364
6788
  --base-url=<url> \u0434\u043B\u044F custom
6365
6789
  --proto=openai|anthropic \u0434\u043B\u044F custom
6366
6790
  --model=<model>
6367
- --api-key=<key> \u043D\u0435 \u043D\u0443\u0436\u0435\u043D \u0434\u043B\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0445 LM Studio/Ollama
6791
+ --api-key=<key> \u043D\u0435 \u043D\u0443\u0436\u0435\u043D \u0434\u043B\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0445 LM Studio/Ollama; \u0434\u043B\u044F GirlAI \u043C\u043E\u0436\u043D\u043E \u0432\u043C\u0435\u0441\u0442\u043E \u043A\u043B\u044E\u0447\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C OAuth (\u0442\u043E\u043B\u044C\u043A\u043E \u0432 TUI \u0432\u0438\u0437\u0430\u0440\u0434\u0435)
6368
6792
  --name=<\u0438\u043C\u044F> \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0435 \u0438\u043C\u044F; \u0435\u0441\u043B\u0438 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u2014 \u0441\u043B\u0443\u0447\u0430\u0439\u043D\u043E\u0435 \u0438\u0437 \u043F\u0443\u043B\u0430 \u043F\u043E nationality (\u0442\u0443\u0440\u043D\u0438\u0440 \u0432\u044B\u0431\u043E\u0440\u0430 \u0438\u043C\u0451\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0422\u041E\u041B\u042C\u041A\u041E \u0432 TUI \u0432\u0438\u0437\u0430\u0440\u0434\u0435)
6369
6793
  --age=<n>
6370
6794
  --persona-notes=<text> \u0434\u043E\u043F. \u043F\u043E\u0436\u0435\u043B\u0430\u043D\u0438\u044F \u043A persona/speech/communication \u043F\u0435\u0440\u0435\u0434 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0435\u0439
@@ -6382,6 +6806,10 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
6382
6806
  --list \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u0438
6383
6807
  --help
6384
6808
 
6809
+ update:
6810
+ npx girl-agent update # \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0434\u0430\u043D\u043D\u044B\u0435 (\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438) \u0434\u043E \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438
6811
+ npx girl-agent update --verbose # \u0441 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u044B\u043C \u0432\u044B\u0432\u043E\u0434\u043E\u043C
6812
+
6385
6813
  \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
6386
6814
  `;
6387
6815
  async function main() {
@@ -6424,7 +6852,8 @@ async function main() {
6424
6852
  "print-config",
6425
6853
  "print-systemd",
6426
6854
  "print-docker",
6427
- "no-start"
6855
+ "no-start",
6856
+ "verbose"
6428
6857
  ],
6429
6858
  alias: { h: "help" }
6430
6859
  });
@@ -6434,11 +6863,15 @@ async function main() {
6434
6863
  await runServer(argv);
6435
6864
  return;
6436
6865
  }
6866
+ if (positional[0] === "update") {
6867
+ await runUpdate(!!argv.verbose);
6868
+ return;
6869
+ }
6437
6870
  if (argv.help) {
6438
6871
  process.stdout.write(HELP);
6439
6872
  return;
6440
6873
  }
6441
- const isHeadless = !!(argv["json-events"] || argv.headless || argv.list || argv.help);
6874
+ const isHeadless = !!(argv["json-events"] || argv.headless || argv.list || argv.help || positional[0] === "update");
6442
6875
  if (!isHeadless) {
6443
6876
  const stdin = process.stdin;
6444
6877
  const stdout = process.stdout;
@@ -6619,7 +7052,57 @@ function personaNotesForGeneration2(cfg) {
6619
7052
  ].filter(Boolean);
6620
7053
  return parts.join("\n\n");
6621
7054
  }
7055
+ async function runUpdate(verbose) {
7056
+ const profiles = await listProfiles();
7057
+ if (!profiles.length) {
7058
+ process.stdout.write("\u043D\u0435\u0442 \u043F\u0440\u043E\u0444\u0438\u043B\u0435\u0439 \u2014 \u043D\u0435\u0447\u0435\u0433\u043E \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C.\n");
7059
+ return;
7060
+ }
7061
+ process.stdout.write(`\u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u0440\u043E\u0444\u0438\u043B\u0435\u0439: ${profiles.length}
7062
+ \u0437\u0430\u043F\u0443\u0441\u043A \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439...
7063
+ `);
7064
+ const result = await runMigrations({ verbose });
7065
+ if (!result.migrationsApplied.length) {
7066
+ process.stdout.write("\u0432\u0441\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u044B, \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043D\u0435 \u043D\u0443\u0436\u043D\u044B.\n");
7067
+ return;
7068
+ }
7069
+ process.stdout.write(`
7070
+ \u0433\u043E\u0442\u043E\u0432\u043E:
7071
+ `);
7072
+ process.stdout.write(` \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439 \u043F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u043E: ${result.migrationsApplied.length}
7073
+ `);
7074
+ for (const id of result.migrationsApplied) {
7075
+ process.stdout.write(` - ${id}
7076
+ `);
7077
+ }
7078
+ process.stdout.write(` \u043F\u0440\u043E\u0444\u0438\u043B\u0435\u0439 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u043E: ${result.profilesUpdated}
7079
+ `);
7080
+ if (result.errors.length) {
7081
+ process.stdout.write(` \u043E\u0448\u0438\u0431\u043E\u043A: ${result.errors.length}
7082
+ `);
7083
+ for (const e of result.errors) {
7084
+ process.stdout.write(` ${e.profile} @ ${e.migration}: ${e.error}
7085
+ `);
7086
+ }
7087
+ }
7088
+ }
6622
7089
  async function runRuntime(cfg, opts = {}) {
7090
+ if (await checkForPendingMigrations()) {
7091
+ process.stderr.write("[updater] \u043E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u044B pending-\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u0437\u0430\u043F\u0443\u0441\u043A...\n");
7092
+ const result = await runMigrations({
7093
+ verbose: true,
7094
+ llmFactory: (c) => {
7095
+ try {
7096
+ return makeLLM(c.llm);
7097
+ } catch {
7098
+ return void 0;
7099
+ }
7100
+ }
7101
+ });
7102
+ if (result.warnings.length) {
7103
+ process.stderr.write(formatUpdateWarnings(result.warnings) + "\n");
7104
+ }
7105
+ }
6623
7106
  const rt = new Runtime(cfg);
6624
7107
  await rt.start();
6625
7108
  if (opts.jsonEvents) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thesashadev/girl-agent",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Telegram AI persona engine with memory, schedule, relationship state and MTProto userbot mode.",
5
5
  "type": "module",
6
6
  "bin": {