// dsh-usage 插件 — Host 半 // 通过 cordis_define 定义(动态 Cordis 插件)。 // 提供两个 Package-private RPC: // usage/balance — 读取 %DSH_HOME%\.credentials.yaml 的 DEEPSEEK_API_KEY, // 调用 DeepSeek 官方余额 API(GET /user/balance),返回 JSON。 // usage/openPortal— 用系统默认浏览器打开官网用量页。 // 密钥只在 node 子进程内读取与使用,绝不打印、不入库。 return { inject: ['shell'], apply(ctx) { harness.handle('usage/balance', async (args) => { const script = [ "const fs = require('node:fs');", "const os = require('node:os');", "const path = require('node:path');", "const home = process.env.DSH_HOME || path.join(os.homedir(), '.dsh');", "const file = path.join(home, '.credentials.yaml');", "let key = null;", "try { const txt = fs.readFileSync(file, 'utf8'); const m = txt.match(/DEEPSEEK_API_KEY:\\s*(\\S+)/); if (m) key = m[1]; } catch (e) {}", "if (!key) { console.log(JSON.stringify({ error: 'no-api-key', file: file })); process.exit(0); }", "fetch('https://api.deepseek.com/user/balance', { headers: { Authorization: 'Bearer ' + key, Accept: 'application/json' }, signal: AbortSignal.timeout(15000) })", " .then(r => r.json().then(j => console.log(JSON.stringify(Object.assign({ status: r.status }, j)))))", " .catch(e => console.log(JSON.stringify({ error: String((e && e.message) || e) })));", ].join('\n') try { const spec = ctx.shell.resolve({ command: 'node', args: ['-e', script] }) const res = await ctx.shell.run(spec) const stdout = String(res.stdout || '') const lines = stdout.trim().split(/\r?\n/).filter(Boolean) if (lines.length) return JSON.parse(lines[lines.length - 1]) return { error: 'empty-output', stderr: String(res.stderr || '').slice(0, 400) } } catch (e) { return { error: String((e && e.message) || e) } } }) harness.handle('usage/openPortal', async () => { try { const spec = ctx.shell.resolve({ command: 'cmd', args: ['/c', 'start', '', 'https://platform.deepseek.com/usage'] }) await ctx.shell.run(spec) return { ok: true } } catch (e) { return { ok: false, error: String((e && e.message) || e) } } }) }, }