@thesashadev/girl-agent 0.1.12 → 0.1.13

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