#!/usr/bin/env node /** * Standalone recovery: point every stable goosed target at the 18036 no-think proxy. * Works on bundled 103 runtime (no db.mjs / llm-providers.mjs required). */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { Agent, fetch as undiciFetch } from 'undici'; const GOOSED_PROVIDER_ID = 'custom_memind_deepseek_no_think'; const DEFAULT_MODEL = 'deepseek-v4-pro'; const MODELS = ['deepseek-v4-pro', 'deepseek-v4-flash']; const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); const insecureDispatcher = new Agent({ connect: { rejectUnauthorized: false } }); function loadEnvFile(filePath) { const env = {}; if (!fs.existsSync(filePath)) return env; for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const eq = trimmed.indexOf('='); if (eq < 0) continue; const key = trimmed.slice(0, eq).trim(); let value = trimmed.slice(eq + 1).trim(); if ( (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")) ) { value = value.slice(1, -1); } env[key] = value; } return env; } const fileEnv = loadEnvFile(path.join(root, '.env')); const env = { ...fileEnv, ...process.env }; const apiSecret = String(env.TKMIND_SERVER__SECRET_KEY ?? '').trim(); const apiKey = String(env.DEEPSEEK_API_KEY ?? '').trim(); const apiTargets = String( env.TKMIND_API_TARGETS ?? env.TKMIND_API_TARGET ?? 'https://127.0.0.1:18006', ) .split(',') .map((item) => item.trim()) .filter(Boolean); if (!apiSecret) { console.error('missing TKMIND_SERVER__SECRET_KEY'); process.exit(1); } if (!apiKey) { console.error('missing DEEPSEEK_API_KEY'); process.exit(1); } const host = String(env.MEMIND_GOOSED_HOST_GATEWAY ?? 'host.docker.internal').trim() || 'host.docker.internal'; const port = Number(env.MEMIND_DEEPSEEK_NO_THINK_PORT ?? 18036); const proxyBaseUrl = `http://${host}:${port}/v1`; async function goosedFetch(apiTarget, pathname, init = {}) { const url = new URL(pathname, apiTarget); const headers = { ...(init.headers ?? {}), 'X-Secret-Key': apiSecret, }; if (init.body && !headers['Content-Type']) { headers['Content-Type'] = 'application/json'; } const dispatcher = apiTarget.startsWith('https://') ? insecureDispatcher : undefined; return undiciFetch(url, { ...init, headers, dispatcher }); } async function upsertConfig(apiTarget, key, value, isSecret = false) { const res = await goosedFetch(apiTarget, '/config/upsert', { method: 'POST', body: JSON.stringify({ key, value, is_secret: isSecret }), }); if (!res.ok) { const text = await res.text().catch(() => ''); throw new Error(`upsert ${key} on ${apiTarget} failed: ${res.status} ${text}`); } } async function upsertProvider(apiTarget) { const body = { engine: 'openai', display_name: 'memind_deepseek_no_think', api_url: proxyBaseUrl, api_key: apiKey, models: MODELS, supports_streaming: true, requires_auth: true, preserves_thinking: false, }; let res = await goosedFetch( apiTarget, `/config/custom-providers/${encodeURIComponent(GOOSED_PROVIDER_ID)}`, { method: 'PUT', body: JSON.stringify(body) }, ); if (!res.ok) { const text = await res.text().catch(() => ''); const missing = res.status === 404 || /provider not found/i.test(text); if (!missing) { throw new Error(`upsert provider on ${apiTarget} failed: ${res.status} ${text}`); } res = await goosedFetch(apiTarget, '/config/custom-providers', { method: 'POST', body: JSON.stringify(body), }); } if (!res.ok) { const text = await res.text().catch(() => ''); throw new Error(`create provider on ${apiTarget} failed: ${res.status} ${text}`); } await upsertConfig(apiTarget, 'DEEPSEEK_API_KEY', apiKey, true); for (const [key, value] of [ ['GOOSE_PROVIDER', GOOSED_PROVIDER_ID], ['GOOSE_MODEL', DEFAULT_MODEL], ['TKMIND_PROVIDER', GOOSED_PROVIDER_ID], ['TKMIND_MODEL', DEFAULT_MODEL], ['GOOSE_THINKING_EFFORT', 'off'], ]) { await upsertConfig(apiTarget, key, value, false); } } const results = []; for (const target of apiTargets) { await upsertProvider(target); results.push({ target, provider: GOOSED_PROVIDER_ID, model: DEFAULT_MODEL, proxyBaseUrl }); } console.log(JSON.stringify({ ok: true, results }, null, 2));