mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-10-26 16:55:08 +00:00
128 lines
6.2 KiB
TypeScript
128 lines
6.2 KiB
TypeScript
/** Installed profile dependencies and their bundle activation after package-manager operations. */
|
|
|
|
import { join } from 'node:path'
|
|
import { readProfileManifest, resolveBundleDir, writeProfileManifest, type ProfileManifest } from './profile.ts'
|
|
|
|
/** Profile directory and installation used by the shared bundle resolver. */
|
|
export interface ProfilePluginLocation {
|
|
/** Diagnostic prefix for profile manifest failures. */
|
|
readonly binName: string
|
|
/** Profile package directory managed by pnpm. */
|
|
readonly profileDir: string
|
|
/** Absolute package.json path of the owning dsh installation. */
|
|
readonly installAnchor: string
|
|
}
|
|
|
|
/** One dependency under its package-manager key, including npm aliases. */
|
|
export interface ProfilePluginDependency {
|
|
/** Dependency key used in package.json and the bundle list. */
|
|
readonly name: string
|
|
/** Installed version, or the dependency spec when installed metadata is unavailable. */
|
|
readonly version: string
|
|
/** Whether the package selected by the bundle resolver declares a patch. */
|
|
readonly bundle: boolean
|
|
/** Whether the dependency appears in the profile's active bundle list. */
|
|
readonly enabled: boolean
|
|
}
|
|
|
|
/** Profile manifest and dependencies in manifest order. */
|
|
export interface ProfilePluginInventory {
|
|
readonly manifest: ProfileManifest
|
|
readonly dependencies: readonly ProfilePluginDependency[]
|
|
}
|
|
|
|
/** Result of reconciling bundle activation after a successful package operation. */
|
|
export interface ProfilePluginReconciliation {
|
|
readonly plugins: ProfilePluginInventory
|
|
/** Newly added ordinary dependencies, for optional caller-owned diagnostics. */
|
|
readonly addedPlainDependencies: readonly string[]
|
|
}
|
|
|
|
/** Unavailable installed metadata must not prevent listing or removing dependencies. */
|
|
function optionalManifest(binName: string, packageDir: string): ProfileManifest | undefined {
|
|
try { return readProfileManifest(binName, packageDir) } catch { return undefined }
|
|
}
|
|
|
|
/** Resolve activation metadata with the same installation precedence as profile loading. */
|
|
function bundleManifest(location: ProfilePluginLocation, name: string): ProfileManifest | undefined {
|
|
let packageDir: string
|
|
try {
|
|
packageDir = resolveBundleDir(location.binName, name, location.installAnchor, location.profileDir)
|
|
} catch {
|
|
// An unresolved dependency remains visible as an ordinary package.
|
|
return undefined
|
|
}
|
|
return optionalManifest(location.binName, packageDir)
|
|
}
|
|
|
|
/**
|
|
* Read installed versions and bundle declarations without requiring loadable plugin code.
|
|
* Missing or unreadable installed metadata leaves the dependency visible for repair or removal.
|
|
* @param location - profile and installation resolution inputs.
|
|
* @returns dependency records in package.json order and the profile manifest.
|
|
*/
|
|
export function readProfilePlugins(location: ProfilePluginLocation): ProfilePluginInventory {
|
|
const manifest = readProfileManifest(location.binName, location.profileDir)
|
|
const bundles = manifest.dsh?.profile?.bundles ?? []
|
|
const dependencies = Object.entries(manifest.dependencies ?? {}).map(([name, spec]) => {
|
|
const installed = optionalManifest(location.binName, join(location.profileDir, 'node_modules', name))
|
|
return {
|
|
name,
|
|
version: typeof installed?.version === 'string' ? installed.version : spec,
|
|
bundle: bundleManifest(location, name)?.dsh?.bundle?.patch !== undefined,
|
|
enabled: bundles.includes(name),
|
|
}
|
|
})
|
|
return { manifest, dependencies }
|
|
}
|
|
|
|
/**
|
|
* Write a bundle list while preserving the supplied profile's other metadata.
|
|
* @param profileDir - directory whose package.json is updated.
|
|
* @param manifest - current profile manifest, read after the package operation when applicable.
|
|
* @param bundles - ordered active bundle names, including any template entries.
|
|
* @returns the written manifest.
|
|
*/
|
|
export function writeProfileBundles(
|
|
profileDir: string, manifest: ProfileManifest, bundles: readonly string[],
|
|
): ProfileManifest {
|
|
const updated = { ...manifest, dsh: { ...manifest.dsh, profile: { ...manifest.dsh?.profile, bundles: [...bundles] } } }
|
|
writeProfileManifest(profileDir, updated)
|
|
return updated
|
|
}
|
|
|
|
/**
|
|
* Reconcile installed bundle declarations after a successful package-manager operation.
|
|
* Dependency-managed entries disappear when removed or when their package loses its declaration;
|
|
* template entries retain their order and duplicates. New bundle dependencies activate automatically.
|
|
* @param options - location, inventory captured before pnpm, and whether explicitly disabled bundles remain disabled.
|
|
* @returns updated inventory and newly added ordinary dependencies for caller-owned warnings.
|
|
*/
|
|
export function reconcileProfilePlugins(options: ProfilePluginLocation & {
|
|
readonly before: ProfilePluginInventory
|
|
readonly preserveDisabled: boolean
|
|
}): ProfilePluginReconciliation {
|
|
const after = readProfilePlugins(options)
|
|
const beforeNames = new Set(options.before.dependencies.map(dependency => dependency.name))
|
|
const afterNames = new Set(after.dependencies.map(dependency => dependency.name))
|
|
const bundleNames = new Set(after.dependencies.filter(dependency => dependency.bundle).map(dependency => dependency.name))
|
|
const disabled = new Set(options.preserveDisabled
|
|
? options.before.dependencies.filter(dependency => dependency.bundle && !dependency.enabled).map(dependency => dependency.name)
|
|
: [])
|
|
const previous = after.manifest.dsh?.profile?.bundles ?? []
|
|
const bundles = previous.filter(name => !(beforeNames.has(name) || afterNames.has(name)) || bundleNames.has(name))
|
|
for (const dependency of after.dependencies) {
|
|
if (dependency.bundle && !disabled.has(dependency.name) && !bundles.includes(dependency.name)) bundles.push(dependency.name)
|
|
}
|
|
const changed = bundles.length !== previous.length || bundles.some((name, index) => name !== previous[index])
|
|
const manifest = changed ? writeProfileBundles(options.profileDir, after.manifest, bundles) : after.manifest
|
|
return {
|
|
plugins: {
|
|
manifest,
|
|
dependencies: after.dependencies.map(dependency => ({ ...dependency, enabled: bundles.includes(dependency.name) })),
|
|
},
|
|
addedPlainDependencies: after.dependencies.filter(dependency => !dependency.bundle && !beforeNames.has(dependency.name))
|
|
.map(dependency => dependency.name),
|
|
}
|
|
}
|