#!/usr/bin/env node // DeepSeek CLI (Node.js version) // Commands: // ds k 设置/替换 API Key // ds s 设置 system prompt // ds v 查看当前 prompt // ds c 使用 deepseek-chat(流式) // ds r 使用 deepseek-reasoner(流式,含思考) import fs from 'fs'; import os from 'os'; import path from 'path'; import { TextDecoder } from 'util'; const CONFIG_DIR = path.join(os.homedir(), '.config', 'deepseek'); const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json'); const DEFAULT_PROMPT = '你是Deepseek人工智能助手'; function ensureConfig() { if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true }); if (!fs.existsSync(CONFIG_FILE)) { fs.writeFileSync(CONFIG_FILE, JSON.stringify({ api_key: '', system_prompt: DEFAULT_PROMPT }, null, 2), 'utf8'); fs.chmodSync(CONFIG_FILE, 0o600); } } function readConfig() { ensureConfig(); try { const raw = fs.readFileSync(CONFIG_FILE, 'utf8'); const data = JSON.parse(raw); return { apiKey: data.api_key || '', prompt: data.system_prompt || DEFAULT_PROMPT, }; } catch { return { apiKey: '', prompt: DEFAULT_PROMPT }; } } function saveConfig(apiKey, prompt) { ensureConfig(); fs.writeFileSync(CONFIG_FILE, JSON.stringify({ api_key: apiKey, system_prompt: prompt }, null, 2), 'utf8'); fs.chmodSync(CONFIG_FILE, 0o600); } function usage() { console.log(`用法: ds k 设置/替换 API 密钥 ds s 设置系统提示词 ds v 查看当前提示词 ds c 使用 deepseek-chat(流式) ds r 使用 deepseek-reasoner(流式,含思考) 环境优先级: 命令行 > 环境变量 DEEPSEEK_API_KEY > 配置文件 默认模型: deepseek-reasoner`); process.exit(1); } const args = process.argv.slice(2); if (args.length === 0 || ['-h', '--help'].includes(args[0])) usage(); const cmd = args.shift(); // ANSI blue for **bold** const BLUE = '\x1b[34m'; const RESET = '\x1b[0m'; let inBold = false; function renderChunk(text) { let out = ''; for (let i = 0; i < text.length; i++) { if (text[i] === '*' && text[i + 1] === '*') { inBold = !inBold; out += inBold ? BLUE : RESET; i++; continue; } out += text[i]; } return out; } async function streamChat({ model, apiKey, prompt, userInput }) { const body = { model, messages: [ { role: 'system', content: prompt }, { role: 'user', content: userInput }, ], stream: true, }; const res = await fetch('https://api.deepseek.com/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify(body), }); if (!res.ok) { const t = await res.text(); console.error(`请求失败 ${res.status}: ${t}`); process.exit(1); } const decoder = new TextDecoder(); let buffer = ''; let sawReason = false; let sawContent = false; const enableReason = model === 'deepseek-reasoner'; process.stdout.write(`发送中 (模型: ${model})...\n`); for await (const chunk of res.body) { buffer += decoder.decode(chunk, { stream: true }); let lines = buffer.split(/\r?\n/); buffer = lines.pop(); // keep incomplete line for (const line of lines) { if (!line.startsWith('data: ')) continue; const data = line.slice(6); if (data === '[DONE]') { process.stdout.write('\n'); return; } let delta; try { delta = JSON.parse(data)?.choices?.[0]?.delta || {}; } catch { continue; } const rc = delta.reasoning_content || ''; const ct = delta.content || ''; if (enableReason && rc) { if (!sawReason) { process.stdout.write('思考:\n'); sawReason = true; } process.stdout.write(renderChunk(rc)); } if (ct) { if (!sawContent) { if (sawReason) process.stdout.write('\n\n'); process.stdout.write('回复:\n'); sawContent = true; } process.stdout.write(renderChunk(ct)); } } } process.stdout.write('\n'); } (async () => { switch (cmd) { case 'k': { if (args.length < 1) { console.error('请输入密钥'); process.exit(1); } const cfg = readConfig(); saveConfig(args[0], cfg.prompt); console.log(`API 密钥已保存到 ${CONFIG_FILE}`); break; } case 's': { if (args.length < 1) { console.error('请输入新的 system prompt'); process.exit(1); } const cfg = readConfig(); const prompt = args.join(' '); saveConfig(cfg.apiKey, prompt); console.log('system prompt 已更新。'); break; } case 'v': { const cfg = readConfig(); console.log('当前 system prompt:'); console.log(cfg.prompt); break; } case 'c': case 'r': { if (args.length < 1) { console.error('请输入要发送的内容'); process.exit(1); } const userInput = args.join(' '); const cfg = readConfig(); const apiKey = process.env.DEEPSEEK_API_KEY || cfg.apiKey; if (!apiKey) { console.error('未设置 API 密钥,请先运行: ds k 或设置环境变量 DEEPSEEK_API_KEY'); process.exit(1); } const model = cmd === 'c' ? 'deepseek-chat' : 'deepseek-reasoner'; await streamChat({ model, apiKey, prompt: cfg.prompt, userInput }); break; } default: usage(); } })();