#!/usr/bin/env node import { readFile } from 'node:fs/promises'; import { homedir, tmpdir } from 'node:os'; import { isAbsolute, join, resolve } from 'node:path'; import { spawnSync } from 'node:child_process'; const PACKAGE_NAME = '@xmanrui/dsh-im'; const DEFAULT_SOURCE = 'github:xmanrui/dsh-im'; const LEGACY_PACKAGES = [ '@xmanrui/dsh-feishu', '@xmanrui/dsh-weixin', '@xmanrui/dsh-dingtalk', ]; function usage() { console.log(`Usage: dsh-im install [--profile web] [--source ] dsh-im uninstall [--profile web] Examples: npx -y github:xmanrui/dsh-im install dsh-im install --source .`); } function takeOption(args, name, fallback) { const index = args.indexOf(name); if (index < 0) return fallback; const value = args[index + 1]; if (!value || value.startsWith('--')) throw new Error(`${name} requires a value`); args.splice(index, 2); return value; } function runDsh(args) { const result = spawnSync('dsh', args, { cwd: tmpdir(), stdio: 'inherit', shell: false, }); if (result.error?.code === 'ENOENT') { throw new Error('找不到 dsh,请先安装 DeepSeek Harness 并确保 dsh 在 PATH 中。'); } if (result.error) throw result.error; if (result.status !== 0) throw new Error(`dsh 退出,状态码 ${result.status ?? 1}`); } async function directProfilePackages(profile) { const dshHome = process.env.DSH_HOME || join(homedir(), '.dsh'); const profilePackage = join(dshHome, 'profiles', profile, 'package.json'); try { const manifest = JSON.parse(await readFile(profilePackage, 'utf8')); const bundles = new Set(manifest.dsh?.profile?.bundles ?? []); const dependencies = manifest.dependencies ?? {}; return new Set(LEGACY_PACKAGES.filter((name) => dependencies[name] || bundles.has(name))); } catch (error) { if (error?.code === 'ENOENT') return new Set(); throw new Error(`无法读取 Harness 配置 ${profilePackage}:${error.message}`); } } const args = process.argv.slice(2); const command = args.shift(); if (!command || command === '--help' || command === '-h') { usage(); process.exit(0); } try { const profile = takeOption(args, '--profile', 'web'); if (command === 'install') { const requested = takeOption(args, '--source', DEFAULT_SOURCE); const source = requested === '.' || requested === '..' || requested.startsWith('./') || requested.startsWith('../') ? resolve(process.cwd(), requested) : (isAbsolute(requested) ? requested : requested); if (args.length > 0) throw new Error(`无法识别的参数:${args.join(' ')}`); const legacy = await directProfilePackages(profile); runDsh(['plugin', '--profile', profile, 'add', '--save-exact', source]); for (const packageName of legacy) { runDsh(['plugin', '--profile', profile, 'remove', packageName]); } console.log('\nIM 机器人插件已安装。请重启 dsh web、刷新浏览器,然后打开「设置 → IM机器人」。'); if (legacy.size > 0) { console.log('已用 dsh-im 替换独立飞书/微信/钉钉插件;原有凭据和扫码绑定保持不变。'); } } else if (command === 'uninstall') { if (args.length > 0) throw new Error(`无法识别的参数:${args.join(' ')}`); runDsh(['plugin', '--profile', profile, 'remove', PACKAGE_NAME]); console.log('\nIM 机器人插件已卸载。请重启 dsh web。'); } else { throw new Error(`无法识别的命令:${command}`); } } catch (error) { console.error(`dsh-im: ${error.message}`); process.exit(1); }