What plugin management does now lives in `dsh-plugin-manager`, a boot-group package beside `app-boot`: `PluginInstaller` runs pnpm, probes, and judges new packages over the profile on disk, and `PluginManager` adds every operation over the booted tree, each refusal a `PluginOperationError` with a `plugins/*` code and no Remote protocol attached. The manager reads the profile runtime, the preset roster, and the running-agent count through per-call readers, and the roster only as `PresetLayers`, so the package depends on neither the roster nor the agent registry. `dsh-host-plugin-manager` keeps `pluginManager` and the `plugins` Remote as a relay: one Remote method per manager method, and one exhaustive switch turning a failure into the Remote error of the same code (`plugins/bad-request` into `gateway/bad-request`). Its `./types` re-exports the manager's types, so `api-remotes` and the client import one vocabulary unchanged. `dsh plugin add` and `remove` now run the installer instead of a bare pnpm forwarder: every new package is probed, a package that declares neither a bundle nor a plugin module is removed again with its reason, and newly installed bundles are enabled as before. Other pnpm verbs still forward.
51 lines
1.6 KiB
JavaScript
51 lines
1.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Command-line entry for dsh.
|
|
* @module @deepseek-ai/dsh/bin
|
|
*/
|
|
|
|
/* v8 ignore file -- built-bin acceptance exercises this self-executing dispatch. */
|
|
|
|
import { readFileSync } from 'node:fs'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { loadLayeredEnv } from '@deepseek-ai/dsh-app-boot'
|
|
import { parseDshArgs } from './args.ts'
|
|
|
|
// Both the source tree (apps/cli/src) and the bundled bin (apps/cli/lib) sit
|
|
// one directory under apps/cli, so the checked-in manifest resolves with the
|
|
// same relative hop from either artifact.
|
|
function readVersion(): string {
|
|
const manifest = JSON.parse(
|
|
readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf8'),
|
|
) as { version?: unknown }
|
|
return typeof manifest.version === 'string' ? manifest.version : '0.0.0'
|
|
}
|
|
|
|
const invocation = parseDshArgs(process.argv.slice(2), readVersion())
|
|
|
|
switch (invocation.mode) {
|
|
case 'profile': {
|
|
const { runProfile } = await import('./profile-boot.ts')
|
|
await runProfile({
|
|
environment: loadLayeredEnv('dsh'),
|
|
profile: invocation.profile,
|
|
patchFiles: invocation.patches,
|
|
args: invocation.args,
|
|
})
|
|
break
|
|
}
|
|
case 'plugin': {
|
|
const { runPlugin } = await import('./plugin.ts')
|
|
process.exit(await runPlugin(invocation.profile, invocation.args))
|
|
break
|
|
}
|
|
case 'dump-config': {
|
|
const { runDumpConfig } = await import('./dump-config.ts')
|
|
runDumpConfig(invocation.profile, invocation.defaultOnly, invocation.patches)
|
|
break
|
|
}
|
|
default:
|
|
invocation satisfies never
|
|
throw new Error(`dsh: unhandled invocation mode ${JSON.stringify(invocation)}`)
|
|
}
|