mirror of
https://github.com/TommyFang2077/dsh-desktop.git
synced 2026-08-17 09:06:36 +08:00
Add README, plugin citations, and multi-platform release CI.
Ship screenshots and third-party notices for the desktop shell, and package Windows, macOS, deb, rpm, and Flatpak from GitHub Releases. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
181
plugins/dsh-desktop-vision/index.js
Normal file
181
plugins/dsh-desktop-vision/index.js
Normal file
@@ -0,0 +1,181 @@
|
||||
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import { homedir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
|
||||
export const name = 'dsh-desktop-vision'
|
||||
export const inject = []
|
||||
|
||||
const PROVIDERS = [
|
||||
{
|
||||
id: 'openai',
|
||||
label: 'OpenAI 兼容',
|
||||
baseUrl: 'https://api.openai.com/v1',
|
||||
apiUrl: 'https://platform.openai.com/api-keys',
|
||||
example: 'gpt-4o',
|
||||
},
|
||||
{
|
||||
id: 'gemini-api',
|
||||
label: 'Gemini API',
|
||||
baseUrl: 'https://generativelanguage.googleapis.com',
|
||||
apiUrl: 'https://aistudio.google.com/apikey',
|
||||
example: 'gemini-3.6-flash',
|
||||
},
|
||||
{
|
||||
id: 'anthropic',
|
||||
label: 'Anthropic API',
|
||||
baseUrl: 'https://api.anthropic.com',
|
||||
apiUrl: 'https://console.anthropic.com/settings/keys',
|
||||
example: 'claude-haiku-4-5-20251001',
|
||||
},
|
||||
{
|
||||
id: 'antigravity-cli',
|
||||
label: 'Antigravity CLI(免费)',
|
||||
baseUrl: '',
|
||||
apiUrl: 'https://antigravity.google/',
|
||||
example: 'gemini-3.6-flash-low',
|
||||
},
|
||||
{
|
||||
id: 'claude-cli',
|
||||
label: 'Claude Code 登录',
|
||||
baseUrl: '',
|
||||
apiUrl: 'https://code.claude.com',
|
||||
example: 'haiku',
|
||||
},
|
||||
]
|
||||
const PROVIDER_IDS = new Set(PROVIDERS.map((item) => item.id))
|
||||
|
||||
function providerOf(id) {
|
||||
return PROVIDERS.find((entry) => entry.id === id)
|
||||
}
|
||||
|
||||
function officialBaseUrl(provider) {
|
||||
const item = providerOf(provider)
|
||||
return item ? item.baseUrl : ''
|
||||
}
|
||||
|
||||
function officialApiUrl(provider) {
|
||||
const item = providerOf(provider)
|
||||
return item ? item.apiUrl : ''
|
||||
}
|
||||
|
||||
function officialExample(provider) {
|
||||
const item = providerOf(provider)
|
||||
return item ? item.example : ''
|
||||
}
|
||||
const FORM_FIELDS = ['apiKey', 'baseUrl', 'model']
|
||||
|
||||
function configPath() {
|
||||
const home = process.env.MODLENS_HOME || join(homedir(), '.modlens')
|
||||
return join(home, 'config.json')
|
||||
}
|
||||
|
||||
function loadConfig() {
|
||||
try {
|
||||
const data = JSON.parse(readFileSync(configPath(), 'utf8'))
|
||||
return data && typeof data === 'object' ? data : {}
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
function formOf(cfg, provider) {
|
||||
const id = PROVIDER_IDS.has(provider) ? provider : 'openai'
|
||||
const entry =
|
||||
cfg.providers && typeof cfg.providers === 'object' && typeof cfg.providers[id] === 'object'
|
||||
? cfg.providers[id]
|
||||
: {}
|
||||
return {
|
||||
provider: id,
|
||||
apiKey: String(entry.apiKey || ''),
|
||||
baseUrl: String(entry.baseUrl || officialBaseUrl(id)),
|
||||
officialBaseUrl: officialBaseUrl(id),
|
||||
apiUrl: officialApiUrl(id),
|
||||
example: officialExample(id),
|
||||
model: String(entry.model || ''),
|
||||
}
|
||||
}
|
||||
|
||||
function saveForm(values) {
|
||||
const provider = String(values.provider || 'openai').trim()
|
||||
if (!PROVIDER_IDS.has(provider)) {
|
||||
const error = new Error(`unknown provider: ${provider}`)
|
||||
error.status = 400
|
||||
throw error
|
||||
}
|
||||
const cfg = loadConfig()
|
||||
cfg.provider = provider
|
||||
if (!cfg.providers || typeof cfg.providers !== 'object') cfg.providers = {}
|
||||
if (!cfg.providers[provider] || typeof cfg.providers[provider] !== 'object') {
|
||||
cfg.providers[provider] = {}
|
||||
}
|
||||
const entry = cfg.providers[provider]
|
||||
for (const field of FORM_FIELDS) {
|
||||
const raw = String(values[field] || '').trim()
|
||||
if (raw) entry[field] = raw
|
||||
else delete entry[field]
|
||||
}
|
||||
const path = configPath()
|
||||
mkdirSync(dirname(path), { recursive: true })
|
||||
writeFileSync(path, `${JSON.stringify(cfg, null, 2)}\n`, { encoding: 'utf8' })
|
||||
try {
|
||||
chmodSync(path, 0o600)
|
||||
} catch {
|
||||
// best-effort; some filesystems ignore mode
|
||||
}
|
||||
return formOf(cfg, provider)
|
||||
}
|
||||
|
||||
async function readJsonBody(req) {
|
||||
const chunks = []
|
||||
for await (const chunk of req) chunks.push(chunk)
|
||||
const raw = Buffer.concat(chunks).toString('utf8').trim()
|
||||
if (!raw) return {}
|
||||
return JSON.parse(raw)
|
||||
}
|
||||
|
||||
function json(res, status, body) {
|
||||
res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
|
||||
res.end(JSON.stringify(body))
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
if (typeof ctx.inject !== 'function') return
|
||||
ctx.inject(['webServer'], (scope) => {
|
||||
try {
|
||||
scope.webServer.register({
|
||||
name: 'dsh-desktop-modlens',
|
||||
kind: 'exact',
|
||||
path: '/dsh-desktop/modlens',
|
||||
handler: async (req, res) => {
|
||||
try {
|
||||
if (req.method === 'GET') {
|
||||
const cfg = loadConfig()
|
||||
const provider = new URL(req.url, 'http://localhost').searchParams.get('provider')
|
||||
json(res, 200, {
|
||||
...formOf(cfg, provider || cfg.provider || 'openai'),
|
||||
options: PROVIDERS.map((item) => ({
|
||||
id: item.id,
|
||||
label: item.label,
|
||||
officialBaseUrl: item.baseUrl,
|
||||
apiUrl: item.apiUrl,
|
||||
example: item.example,
|
||||
})),
|
||||
})
|
||||
return
|
||||
}
|
||||
if (req.method === 'PUT' || req.method === 'POST') {
|
||||
const body = await readJsonBody(req)
|
||||
json(res, 200, { ...saveForm(body), saved: true })
|
||||
return
|
||||
}
|
||||
res.writeHead(405).end()
|
||||
} catch (error) {
|
||||
json(res, error.status || 500, { error: String(error?.message || error) })
|
||||
}
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`[dsh-desktop-vision] config route skipped: ${error}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user