1
/** Profile package management and explicit, exact-version compatibility approvals. */2
import { runPluginCommand, runProfilePnpm, setProfileVersionExemption, type PackageOperationOptions } from '@deepseek-ai/dsh-plugin-manager/operations'3
import { INSTALL_ANCHOR } from './profile-boot.ts'4
import { DEFAULT_PROFILE_BUNDLES, initProfile, PROFILE_TEMPLATES, readProfileCompatibility, resolveProfileDir, type ProfileContext } from '@deepseek-ai/dsh-app-boot'5
import { withFileLock } from '@deepseek-ai/dsh-atomic-write'6
import { existsSync } from 'node:fs'7
import { mkdir } from 'node:fs/promises'8
import { join } from 'node:path'10
function requireDesktopProfile(dir: string): void {11
if (!existsSync(join(dir, 'package.json'))) {12
throw new Error('Open DeepSeek Harness Desktop once to initialize its profile, then fully quit it before running dsh plugin --profile desktop.')13
}14
}16
/** Parse only DSH-owned commands; all other arguments remain pnpm's responsibility. */17
async function versionCommand(profile: string, args: readonly string[]): Promise<number | undefined> {18
const [command, ...rest] = args19
if (command !== 'allow-version' && command !== 'revoke-version' && command !== 'version-exemptions') return undefined20
try {21
let packageVersion: string | undefined22
let runtimeVersion: string | undefined23
let acceptRisk = false24
const argumentsIterator = rest.values()25
for (const argument of argumentsIterator) {26
if (argument === '--accept-risk' && command === 'allow-version' && !acceptRisk) acceptRisk = true27
else if (argument === '--dsh-version' && runtimeVersion === undefined) runtimeVersion = argumentsIterator.next().value28
else if (argument.startsWith('--dsh-version=') && runtimeVersion === undefined) runtimeVersion = argument.slice('--dsh-version='.length)29
else if (!argument.startsWith('-') && packageVersion === undefined) packageVersion = argument30
else throw new Error(`unexpected argument ${JSON.stringify(argument)}`)31
}32
if (command === 'version-exemptions' && rest.length > 0) throw new Error('usage: dsh plugin version-exemptions')33
let request: { packageVersion: string; runtimeVersion: string } | undefined34
if (command !== 'version-exemptions') {35
if (packageVersion === undefined || runtimeVersion === undefined) {36
throw new Error(`usage: dsh plugin ${command} <package@version> --dsh-version <exact>${command === 'allow-version' ? ' --accept-risk' : ''}`)37
}38
request = { packageVersion, runtimeVersion }39
}40
if (command === 'allow-version') {41
process.stderr.write('dsh: warning: allowing incompatible plugin versions can break the application or corrupt data. Approval applies only to the exact package and DSH versions.\n')42
}43
const dir = resolveProfileDir(profile)44
if (profile !== 'desktop') await mkdir(dir, { recursive: true })45
await withFileLock(join(dir, 'package.json'), async () => {46
if (profile === 'desktop') requireDesktopProfile(dir)47
else if (!existsSync(join(dir, 'package.json'))) initProfile(dir, PROFILE_TEMPLATES[profile]?.bundles ?? DEFAULT_PROFILE_BUNDLES)48
if (request === undefined) {49
const { exemptions, warnings } = readProfileCompatibility(dir)50
for (const warning of warnings) process.stderr.write(`dsh: warning: ${warning}\n`)51
process.stdout.write(JSON.stringify(exemptions, undefined, 2) + '\n')52
} else {53
await setProfileVersionExemption(dir, request.packageVersion, request.runtimeVersion, command === 'allow-version', acceptRisk)54
process.stdout.write(`dsh: ${command === 'allow-version' ? 'allowed' : 'revoked'} ${request.packageVersion} for DSH ${request.runtimeVersion}\n`)55
}56
}, { waitMs: 120000 })57
return 058
} catch (error) {59
process.stderr.write(`dsh: ${String(error)}\n`)60
return 161
}62
}64
/** Run package management for a profile.65
* @param profile Profile name; Desktop's reserved profile must already be initialized by the application.66
* @param args DSH exemption command or pnpm arguments relative to the invoking directory.67
* @param packageManager Installation-owned executable and environment for pnpm operations.68
* @returns Zero on success; nonzero on invalid approval or package-manager failure.69
*/70
export async function runPlugin(profile: string, args: readonly string[], packageManager?: ProfileContext['packageManager']): Promise<number> {71
if (profile === 'desktop') {72
try { requireDesktopProfile(resolveProfileDir(profile)) } catch (error) {73
process.stderr.write(`dsh: ${String(error)}\n`)74
return 175
}76
}77
const versionResult = await versionCommand(profile, args)78
if (versionResult !== undefined) return versionResult79
const dir = resolveProfileDir(profile)80
if (existsSync(join(dir, 'package.json'))) {81
for (const warning of readProfileCompatibility(dir).warnings) process.stderr.write(`dsh: warning: ${warning}\n`)82
}83
const context = { profile, dir, installAnchor: INSTALL_ANCHOR, cwd: process.cwd() }84
const options: PackageOperationOptions = {85
...packageManager,86
execution: 'cli',87
outputBytes: 16384,88
lockWaitMs: 120000,89
lookupTimeoutMs: 120000,90
onOutput: (text, stream) => { process[stream].write(text) },91
}92
const result = profile === 'desktop'93
? await withFileLock(join(dir, 'package.json'), async () => {94
requireDesktopProfile(dir)95
return runProfilePnpm(context, args, options)96
}, { waitMs: 120000 })97
: await runPluginCommand(context, args, options)98
if (result.exitCode === 127) process.stderr.write('dsh: pnpm was not found; install pnpm and make it available on PATH.\n')99
for (const { name, version, runtimeVersion } of result.incompatible ?? []) {100
process.stderr.write(`dsh: to accept the risk, run: dsh plugin --profile ${profile} allow-version ${name}@${version} --dsh-version ${runtimeVersion} --accept-risk\n`)101
}102
if (result.exitCode !== 0) process.stderr.write(`dsh: plugin command failed; diagnostics: ${result.logPath}\n`)103
if (result.exitCode !== 0 && args.some(argument => /^git\+|^github:|\.git(?:#|$)/.test(argument))) {104
process.stderr.write(`dsh: git-hosted plugins build on install via their prepare script, which pnpm blocks until allowed — add the exact key pnpm printed above under allowBuilds in ${join(resolveProfileDir(profile), 'pnpm-workspace.yaml')}, then re-run\n`)105
}106
return result.exitCode107
}