Skip to content
Merged
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
41 changes: 41 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# FreeDeepseekAPI — example configuration.
# All variables are optional; the defaults shown below are what the code uses.
# Copy this file to .env and override as needed (a real .env is gitignored).

# ── HTTP server ──
PORT=9655
HOST=0.0.0.0

# ── Account auth source ──
# The pool loads DeepSeek accounts from files. Use either a single file or a
# directory of *.json files (one account per file).
DEEPSEEK_AUTH_PATH=./deepseek-auth.json
# DEEPSEEK_AUTH_DIR=./data/accounts

# ── Account pool ──
# Cooldown in milliseconds applied to an account after HTTP 401/403/429
# (default: 600000 = 10 minutes).
DEEPSEEK_ACCOUNT_COOLDOWN_MS=600000

# ── Startup (deploy / CI) ──
# Set either to 1/true to start the proxy immediately and skip the menu.
NON_INTERACTIVE=0
SKIP_ACCOUNT_MENU=0

# ── Chrome auth helper (scripts/deepseek_chrome_auth.js) ──
# Path to a Chrome / Chrome-for-Testing binary (auto-detected when empty).
CHROME_PATH=
# Remote-debugging port for the auth browser (auto when empty).
DEEPSEEK_CHROME_PORT=
# Chrome profile directory (defaults to a temporary profile).
DEEPSEEK_CHROME_PROFILE=
# Keep the Chrome profile between runs (1/true).
DEEPSEEK_KEEP_CHROME_PROFILE=0
# Reuse an already-running Chrome instance (1/true).
DEEPSEEK_REUSE_CHROME=0

# ── Misc ──
# Pre-set a token for non-interactive auth import.
# DEEPSEEK_TOKEN=
# Run scripts/doctor.js without network checks (1/true).
# DOCTOR_OFFLINE=0
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ node_modules/
*.log
auth.json
deepseek-auth.json
# Runtime-added auth files contain secrets — keep the dir, ignore its contents.
data/accounts/*.json
.chrome-profile-deepseek/
.chrome-for-testing-profile-deepseek/
.chrome-for-testing-profile-deepseek.stale-*/
Expand Down
31 changes: 31 additions & 0 deletions chrome-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# DeepSeek → FreeDeepseekAPI (расширение)

Добавляет аккаунт DeepSeek в локальный FreeDeepseekAPI **одним кликом**:
перехватывает заголовки реального запроса к `chat.deepseek.com/api/...`
(`token` из `Authorization`, все cookie, `hif_*`) и отправляет на
`http://localhost:9655/api/accounts/import`.

Работает в Firefox и Chrome/Edge (Manifest V3).

## Установка

**Firefox**
1. Откройте `about:debugging#/runtime/this-firefox`
2. «Загрузить временное дополнение» → выберите `manifest.json` из этой папки.
(Временное дополнение: после перезапуска Firefox установить заново.)

**Chrome / Edge**
1. Откройте `chrome://extensions`
2. Включите «Режим разработчика».
3. «Загрузить распакованное» → выберите эту папку.

## Использование
1. Запустите FreeDeepseekAPI (порт 9655).
2. Откройте `chat.deepseek.com` и войдите в нужный аккаунт.
3. **Отправьте любое сообщение** (например `ok`) — чтобы прошёл запрос, из которого берутся креды.
4. Клик по иконке расширения → **«➕ Добавить в FreeDeepseekAPI»**.

Для нескольких аккаунтов повторите из разных профилей/логинов браузера.

Вспомогательные кнопки: «Собрать» (показать креды), «Копировать JSON»,
«Скачать файл» (`deepseek-auth.json`) — на случай ручного импорта через дашборд.
138 changes: 43 additions & 95 deletions chrome-extension/background.js
Original file line number Diff line number Diff line change
@@ -1,97 +1,45 @@
// DeepSeek Auth Exporter — Background Service Worker
// Reads cookies from Chrome, forwards content-script localStorage data.

const STORAGE_KEY = 'deepseek_auth';

// Read all needed cookies from chat.deepseek.com
async function readCookies() {
const needed = ['token', 'ds_session_id', 'smidV2'];
const results = {};
for (const name of needed) {
const cookie = await new Promise((resolve) =>
chrome.cookies.get({ url: 'https://chat.deepseek.com', name }, resolve)
);
results[name] = cookie ? cookie.value : '';
}

// Build cookie header string
const parts = [];
if (results.ds_session_id) parts.push(`ds_session_id=${results.ds_session_id}`);
if (results.smidV2) parts.push(`smidV2=${results.smidV2}`);
results.cookie = parts.join('; ');

return results;
}

// Read localStorage values via content script injection
async function readLocalStorage(tabId) {
const keys = ['hif_dliq', 'hif_leim'];
try {
const results = await new Promise((resolve, reject) => {
chrome.tabs.sendMessage(
tabId,
{ action: 'readLocalStorage', keys },
(response) => {
if (chrome.runtime.lastError) reject(chrome.runtime.lastError.message);
else resolve(response.data || {});
// DeepSeek → FreeDeepseekAPI — перехват заголовков реального запроса.
// token (Authorization: Bearer), cookie (все), hif (x-hif-*) берутся из
// настоящего запроса к chat.deepseek.com/api/... — как в HAR/cURL.

const WASM_DEFAULT = 'https://fe-static.deepseek.com/chat/static/sha3_wasm_bg.7b9ca65ddd.wasm';
const KEY = 'deepseek_capture';

// extraHeaders нужен Chrome для доступа к Cookie/Authorization; Firefox даёт их без него.
const opts = ['requestHeaders'];
try {
if (chrome.webRequest.OnBeforeSendHeadersOptions &&
chrome.webRequest.OnBeforeSendHeadersOptions.EXTRA_HEADERS) {
opts.push('extraHeaders');
}
} catch (e) { /* Firefox: опции нет — это нормально */ }

chrome.webRequest.onBeforeSendHeaders.addListener(
(details) => {
const h = {};
for (const x of (details.requestHeaders || [])) h[x.name.toLowerCase()] = x.value;
const auth = h['authorization'] || '';
const token = /^bearer\s+\S/i.test(auth) ? auth.replace(/^bearer\s+/i, '').trim() : '';
const cookie = h['cookie'] || '';
if (token && cookie) {
const cap = {
token,
cookie,
hif_dliq: h['x-hif-dliq'] || '',
hif_leim: h['x-hif-leim'] || '',
wasmUrl: WASM_DEFAULT,
_t: Date.now(),
};
chrome.storage.local.set({ [KEY]: cap });
}
);
});
return results;
} catch (e) {
return {};
}
}

// Find an open DeepSeek tab
function findDeepSeekTab() {
return new Promise((resolve) => {
chrome.tabs.query(
{ url: 'https://chat.deepseek.com/*' },
(tabs) => resolve(tabs.length > 0 ? tabs[0] : null)
);
});
}

async function collectAndStore(tabId) {
const cookies = await readCookies();
let ls = {};
if (tabId) ls = await readLocalStorage(tabId);

const merged = {
token: cookies.token || '',
ds_session_id: cookies.ds_session_id || '',
smidV2: cookies.smidV2 || '',
cookie: cookies.cookie || '',
hif_dliq: ls.hif_dliq || '',
hif_leim: ls.hif_leim || '',
_lastUpdated: new Date().toISOString(),
};

await new Promise((resolve) =>
chrome.storage.local.set({ [STORAGE_KEY]: merged }, resolve)
);
return merged;
}

// Message handler — popup requests
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'collect') {
findDeepSeekTab().then(async (tab) => {
if (!tab) {
sendResponse({ success: false, error: 'No DeepSeek tab open' });
return;
}
const auth = await collectAndStore(tab.id);
sendResponse({ success: true, auth });
});
return true; // keep channel open for async
}

if (request.action === 'export') {
chrome.storage.local.get(STORAGE_KEY, (result) => {
sendResponse({ success: true, auth: result[STORAGE_KEY] || {} });
});
return true;
}
},
{ urls: ['https://chat.deepseek.com/api/*'] },
opts
);

chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
if (req.action === 'get') {
chrome.storage.local.get(KEY, (r) => sendResponse({ success: true, cap: r[KEY] || null }));
return true; // async
}
});
Binary file added chrome-extension/icons/icon128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added chrome-extension/icons/icon16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added chrome-extension/icons/icon48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 20 additions & 17 deletions chrome-extension/manifest.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
{
"manifest_version": 3,
"name": "DeepSeek Auth Exporter",
"version": "1.0",
"description": "Extract DeepSeek Chat credentials from cookies + localStorage for use with the web API proxy",
"permissions": ["cookies", "storage", "downloads"],
"host_permissions": ["https://chat.deepseek.com/*"],
"content_scripts": [
{
"matches": ["https://chat.deepseek.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
"name": "DeepSeek → FreeDeepseekAPI",
"version": "1.4",
"description": "Добавляет аккаунт DeepSeek в локальный FreeDeepseekAPI одним кликом. Перехватывает заголовки запроса chat.deepseek.com (token + cookie + hif).",
"author": "FreeDeepseekAPI",
"browser_specific_settings": {
"gecko": {
"id": "freedeepseek-auth@forgetmeai",
"strict_min_version": "121.0",
"data_collection_permissions": { "required": ["none"] }
}
},
"permissions": ["webRequest", "storage"],
"host_permissions": [
"https://chat.deepseek.com/*",
"http://localhost:9655/*",
"http://127.0.0.1:9655/*"
],
"background": {
"service_worker": "background.js"
"service_worker": "background.js",
"scripts": ["background.js"]
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"48": "icon.png"
}
"default_title": "DeepSeek → FreeDeepseekAPI",
"default_icon": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" }
},
"icons": {
"48": "icon.png"
}
"icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" }
}
57 changes: 52 additions & 5 deletions chrome-extension/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,54 @@
.json-display { background: #0d1117; border: 1px solid #333; border-radius: 4px; padding: 8px; font-size: 10px; font-family: 'Courier New', monospace; color: #7ee787; white-space: pre-wrap; word-break: break-all; max-height: 180px; overflow-y: auto; margin-bottom: 8px; }
.field label { display: block; font-size: 11px; color: #999; margin-bottom: 4px; }
.detail { font-size: 10px; color: #666; text-align: center; }
.pool-section { margin-top: 14px; border-top: 1px solid #333; padding-top: 10px; }
.pool-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.pool-header .title { font-size: 12px; color: #4fc3f7; font-weight: 600; }
.link-btn { background: none; border: 1px solid #444; color: #9ccc65; border-radius: 5px; padding: 4px 8px; font-size: 11px; cursor: pointer; }
.link-btn:hover { background: #ffffff10; }
.link-btn:disabled { opacity: 0.5; cursor: default; }
.pool-list { display: flex; flex-direction: column; gap: 4px; max-height: 220px; overflow-y: auto; }
.pool-row { display: flex; align-items: center; gap: 6px; padding: 6px 8px; background: #0d1117; border: 1px solid #2a2a3a; border-radius: 5px; font-size: 11px; }
.pool-row .id { color: #888; font-family: 'Courier New', monospace; min-width: 40px; }
.pool-row .email { flex: 1; color: #ccc; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.badge { padding: 2px 7px; border-radius: 10px; font-size: 10px; font-weight: 600; white-space: nowrap; }
.badge.ok { background: #1b5e2033; color: #66bb6a; border: 1px solid #2e7d32; }
.badge.invalid { background: #b71c1c33; color: #ef5350; border: 1px solid #c62828; }
.badge.wait { background: #e6510033; color: #ff9800; border: 1px solid #e65100; }
.badge.expired { background: #44444433; color: #999; border: 1px solid #555; }
.badge.checking { background: #1565c033; color: #4fc3f7; border: 1px solid #1565c0; }
.row-actions { display: flex; gap: 4px; }
.acc-btn { background: #ffffff0d; border: 1px solid #444; color: #ddd; border-radius: 4px; padding: 3px 7px; font-size: 11px; cursor: pointer; }
.acc-btn:hover { background: #ffffff1a; }
.acc-btn.danger:hover { background: #b71c1c44; color: #ef5350; }
.acc-btn.confirm { background: #b71c1c; color: #fff; border-color: #c62828; }
.pool-empty { color: #666; font-size: 11px; text-align: center; padding: 10px; }
.hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.hdr h1 { margin: 0; }
.srv-dot { width: 9px; height: 9px; border-radius: 50%; background: #666; flex: none; }
.srv-dot.online { background: #66bb6a; box-shadow: 0 0 5px #2e7d32; }
.srv-dot.offline { background: #ef5350; box-shadow: 0 0 5px #c62828; }
.btn-row button:disabled { opacity: 0.45; cursor: default; }
.pool-row .email { cursor: pointer; }
.pool-row .email:hover { color: #4fc3f7; }
</style>
</head>
<body>
<h1>🔑 DeepSeek Auth Exporter</h1>
<div class="sub">Export credentials for <code>FreeDeepseekAPI</code></div>
<div class="hdr">
<h1>🔑 DeepSeek → FreeDeepseekAPI</h1>
<span id="srvDot" class="srv-dot" title="Статус сервера FreeDeepseekAPI"></span>
</div>
<div class="sub">Откройте <code>chat.deepseek.com</code>, <b>отправьте любое сообщение</b>, затем нажмите кнопку</div>

<div id="status" class="status warn">⏳ Loading...</div>

<div class="btn-row">
<button id="btnCollect" class="btn-primary">🔄 Collect from Tab</button>
<button id="btnCopy" class="btn-success">📋 Copy JSON</button>
<button id="btnSave" class="btn-warn">💾 Download File</button>
<button id="btnAdd" class="btn-primary" style="font-size:13px;padding:11px">➕ Добавить в FreeDeepseekAPI</button>
</div>
<div class="btn-row">
<button id="btnCollect" class="btn-success">🔄 Собрать</button>
<button id="btnCopy" class="btn-success">📋 Копировать JSON</button>
<button id="btnSave" class="btn-warn">💾 Скачать файл</button>
</div>

<div class="field">
Expand All @@ -43,6 +79,17 @@ <h1>🔑 DeepSeek Auth Exporter</h1>

<div class="detail" id="detail">Open chat.deepseek.com, then click Collect</div>

<div class="pool-section">
<div class="pool-header">
<span class="title" id="poolTitle">Пул аккаунтов</span>
<span style="display:flex; gap:6px">
<button id="btnDashboard" class="link-btn">🖥 Дашборд</button>
<button id="btnCheckAll" class="link-btn">↻ Проверить все</button>
</span>
</div>
<div id="pool" class="pool-list"></div>
</div>

<script src="popup.js"></script>
</body>
</html>
Loading