Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ForgetMeAI: https://t.me/forgetmeai
- [Что это даёт](#-что-это-даёт)
- [Возможности](#-возможности)
- [Быстрый старт](#-быстрый-старт)
- [Фоновый запуск (PM2)](#-фоновый-запуск-pm2)
- [Проверка работы](#-проверка-работы)
- [Примеры запросов](#-примеры-запросов)
- [Chat Completions](#chat-completions)
Expand Down Expand Up @@ -116,6 +117,78 @@ http://localhost:9655

---

## 🔄 Фоновый запуск (PM2)

Для VPS/сервера без открытого терминала удобно запускать proxy через [PM2](https://pm2.keymetrics.io/). Сначала авторизуйтесь (`npm run auth`), затем поднимите сервер в фоне.

При запуске через pm2 меню **пропускается автоматически** (нет интерактивного TTY). Явно можно задать `NON_INTERACTIVE=1` или `SKIP_ACCOUNT_MENU=1`.

> **Важно:** флаг pm2 `--env` — это имя блока из `ecosystem.config.cjs` (`production`, `development`), а **не** `KEY=VALUE`. Запись `--env NON_INTERACTIVE=1` **не работает**.

### Установка и быстрый старт

```bash
npm install -g pm2
cd FreeDeepseekAPI
npm run auth # deepseek-auth.json должен существовать

pm2 delete deepseek-api 2>/dev/null || true
pm2 start server.js --name deepseek-api
pm2 save
```

С явной переменной окружения (если нужно):

```bash
NON_INTERACTIVE=1 pm2 start server.js --name deepseek-api --update-env
```

Проверка:

```bash
pm2 status
curl http://127.0.0.1:9655/health
```

### Конфиг ecosystem (рекомендуется)

В репозитории есть готовый `ecosystem.config.cjs`:

```bash
pm2 delete deepseek-api 2>/dev/null || true
pm2 start ecosystem.config.cjs
pm2 save
pm2 startup # автозапуск после перезагрузки сервера
```

### Логи

```bash
pm2 logs deepseek-api # live-лог (Ctrl+C не останавливает процесс)
pm2 logs deepseek-api --lines 100 # последние 100 строк
pm2 flush deepseek-api # очистить логи
```

Файлы логов по умолчанию:

```text
~/.pm2/logs/deepseek-api-out.log
~/.pm2/logs/deepseek-api-error.log
```

### Управление процессом

```bash
pm2 restart deepseek-api
pm2 stop deepseek-api
pm2 delete deepseek-api
pm2 monit # CPU/RAM в терминале
```

После обновления auth (`npm run auth`) перезапустите proxy: `pm2 restart deepseek-api`.

---

## ✅ Проверка работы

```bash
Expand Down
11 changes: 11 additions & 0 deletions ecosystem.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
apps: [{
name: 'deepseek-api',
script: 'server.js',
cwd: __dirname,
env: {
NON_INTERACTIVE: '1',
PORT: '9655',
},
}],
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"auth": "node scripts/auth.js",
"deepseek:auth": "node scripts/deepseek_chrome_auth.js",
"client": "node client.js",
"test": "node --check server.js && node --check scripts/auth.js && node --check scripts/deepseek_chrome_auth.js && node --check scripts/probe_deepseek_models.js && node --check client.js && node --check scripts/live_agentic_smoke_tests.mjs",
"test": "node --check server.js && node --check scripts/auth.js && node --check scripts/deepseek_console_auth.js && node --check scripts/deepseek_chrome_auth.js && node --check scripts/probe_deepseek_models.js && node --check client.js && node --check scripts/live_agentic_smoke_tests.mjs",
"test:live": "node scripts/live_agentic_smoke_tests.mjs"
},
"keywords": [
Expand Down
76 changes: 73 additions & 3 deletions scripts/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,53 @@ function prompt(question) {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
return new Promise(resolve => rl.question(question, ans => { rl.close(); resolve(ans); }));
}
function promptHidden(question) {
if (!process.stdin.isTTY) return prompt(question);

return new Promise(resolve => {
const stdin = process.stdin;
const stdout = process.stdout;
let value = '';
stdout.write(question);

const wasRaw = stdin.isRaw;
stdin.setRawMode(true);
stdin.resume();
stdin.setEncoding('utf8');

const cleanup = () => {
stdin.removeListener('data', onData);
stdin.setRawMode(wasRaw);
stdin.pause();
};

const onData = (chunk) => {
for (const c of String(chunk)) {
if (c === '\n' || c === '\r') {
stdout.write('\n');
cleanup();
resolve(value);
return;
}
if (c === '\u0003') {
cleanup();
process.exit(130);
}
if (c === '\b' || c === '\x7f') {
if (value.length > 0) {
value = value.slice(0, -1);
stdout.write('\b \b');
}
continue;
}
value += c;
stdout.write('*');
}
};

stdin.on('data', onData);
});
}
function divider() { console.log('======================================================'); }
function watermark(prefix = 'ForgetMeAI') { return `${prefix}: ${WATERMARK}`; }
function loadAuth() {
Expand All @@ -39,13 +86,33 @@ function removeLocalAuth() {
if (fs.existsSync(AUTH_PATH)) fs.rmSync(AUTH_PATH, { force: true });
console.log('Удалён deepseek-auth.json. Chrome profile оставлен, чтобы не разлогинивать браузер без нужды.');
}
async function runConsoleAuth() {
const login = (process.env.DEEPSEEK_LOGIN ?? '').trim() || (await prompt('DeepSeek логин (email/phone): ')).trim();
const password = (process.env.DEEPSEEK_PASSWORD ?? '').trim() || (await promptHidden('DeepSeek пароль: ')).trim();

if (!login || !password) {
console.error('[auth] Нужны DEEPSEEK_LOGIN и DEEPSEEK_PASSWORD (логин и пароль).');
process.exitCode = 2;
return;
}

const script = path.join(__dirname, 'deepseek_console_auth.js');
const env = {
...process.env,
DEEPSEEK_LOGIN: login,
DEEPSEEK_PASSWORD: password,
};
const result = spawnSync(process.execPath, [script], { stdio: 'inherit', env });
process.exitCode = result.status === 0 ? 0 : 2;
}
function printHelp() {
divider();
console.log('FreeDeepseekAPI — управление DeepSeek Web login');
console.log(watermark());
divider();
console.log('Опции:');
console.log(' --login Открыть Chrome и обновить auth');
console.log(' --login-console Ввести логин/пароль в консоли и обновить auth');
console.log(' --status Показать статус auth');
console.log(' --remove Удалить локальный deepseek-auth.json');
console.log(' --help Справка');
Expand All @@ -62,18 +129,21 @@ async function menu() {
console.log('1 - Авторизоваться / обновить DeepSeek login');
console.log('2 - Показать статус');
console.log('3 - Удалить локальный auth файл');
console.log('4 - Выход');
const choice = (await prompt('Ваш выбор (Enter = 4): ')) || '4';
console.log('4 - Авторизация в консоли (логин/пароль)');
console.log('5 - Выход');
const choice = (await prompt('Ваш выбор (Enter = 5): ')) || '5';
if (choice === '1') runDirectAuth();
else if (choice === '2') { status(); await prompt('\nНажмите Enter, чтобы вернуться в меню...'); }
else if (choice === '3') removeLocalAuth();
else if (choice === '4') break;
else if (choice === '4') await runConsoleAuth();
else if (choice === '5') break;
}
}
(async () => {
const args = new Set(process.argv.slice(2));
if (args.has('--help') || args.has('-h')) return printHelp();
if (args.has('--login') || args.has('--add') || args.has('--relogin')) return void runDirectAuth();
if (args.has('--login-console') || args.has('--console-login')) return void runConsoleAuth();
if (args.has('--status') || args.has('--list')) return status();
if (args.has('--remove')) return removeLocalAuth();
await menu();
Expand Down
Loading