mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-10-26 16:55:08 +00:00
362 lines
17 KiB
TypeScript
362 lines
17 KiB
TypeScript
/** Keep experimental packages outside default installations, runtime imports, and shipped compositions. */
|
|
|
|
import { existsSync, globSync, readFileSync, statSync } from 'node:fs'
|
|
import { dirname, relative, resolve } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { JSDOM } from 'jsdom'
|
|
import ts from 'typescript'
|
|
import { applyEntryPatches, type PatchOptions } from '@deepseek-ai/cordis-plugin-include'
|
|
import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader'
|
|
import { loadOverlayPatches } from '../packages/boot/app-boot/src/index.ts'
|
|
import { composeEntries } from '../packages/boot/app-boot/src/profile.ts'
|
|
import { isCordisGroupEntry, loadCordisYaml } from './cordis-yaml.ts'
|
|
import {
|
|
collectRuntimeLocalSourceSpecifiers,
|
|
collectRuntimeSourceSpecifiers,
|
|
} from './verify-client-packages.ts'
|
|
|
|
const EXPERIMENTAL_PREFIX = '@deepseek-ai/dsh-experimental-'
|
|
const PROFILE_SOURCE = 'packages/boot/app-boot/src/profile.ts'
|
|
const PRESET_PATTERN = 'packages/preset/agent-presets/presets/*/agent.cordis.yml'
|
|
const RUNTIME_SECTIONS = ['dependencies', 'optionalDependencies', 'peerDependencies'] as const
|
|
|
|
interface Manifest {
|
|
name: string
|
|
dependencies?: Record<string, string>
|
|
optionalDependencies?: Record<string, string>
|
|
peerDependencies?: Record<string, string>
|
|
devDependencies?: Record<string, string>
|
|
dsh?: { bundle?: { patch?: string }; configTrees?: Array<{ path: string }> }
|
|
}
|
|
|
|
interface Package {
|
|
directory: string
|
|
manifest: Manifest
|
|
}
|
|
|
|
/** Counts and violations from the default product's source and configuration inputs. */
|
|
export interface ProductIsolationResult {
|
|
failures: string[]
|
|
packageCount: number
|
|
sourceCount: number
|
|
configCount: number
|
|
webPluginCount: number
|
|
}
|
|
|
|
/**
|
|
* Check default app installations and their authored runtime/configuration inputs.
|
|
* Experimental opt-in packages and separate Web preview entries are outside these roots.
|
|
* @param root - repository root; no build outputs or installed workspace links are read.
|
|
* @returns violations and the sizes of the checked package, source, and configuration sets.
|
|
*/
|
|
export function verifyDefaultProductIsolation(root: string): ProductIsolationResult {
|
|
const failures: string[] = []
|
|
const packages = new Map<string, Package>()
|
|
const directories = new Map<string, Package>()
|
|
const display = (path: string): string => relative(root, path).replaceAll('\\', '/')
|
|
for (const path of globSync([
|
|
'apps/*/package.json', 'packages/*/*/package.json', 'vendor/*/package.json',
|
|
'native/system/packages/*/package.json', 'python/sdk-runtime/package.json',
|
|
], { cwd: root }).sort()) {
|
|
const manifest = JSON.parse(readFileSync(resolve(root, path), 'utf8')) as Manifest
|
|
if (typeof manifest.name !== 'string' || manifest.name === '') throw new Error(`${path}: missing package name`)
|
|
if (packages.has(manifest.name)) throw new Error(`${path}: duplicate package name ${manifest.name}`)
|
|
const pkg = { directory: dirname(resolve(root, path)), manifest }
|
|
packages.set(manifest.name, pkg)
|
|
directories.set(pkg.directory, pkg)
|
|
}
|
|
for (const path of ['apps/cli/package.json', 'apps/web/package.json', 'python/sdk-runtime/package.json']) {
|
|
if (!existsSync(resolve(root, path))) failures.push(`missing default product root ${path}`)
|
|
}
|
|
if (directories.get(resolve(root, 'apps/cli'))?.manifest.name !== '@deepseek-ai/dsh') {
|
|
failures.push('apps/cli/package.json must identify @deepseek-ai/dsh')
|
|
}
|
|
|
|
const queue: Package[] = []
|
|
const visited = new Set<string>()
|
|
const sources = new Set<string>()
|
|
const configs = new Set<string>()
|
|
let webPluginCount = 0
|
|
const isExperimental = (pkg: Package): boolean => pkg.manifest.name.startsWith(EXPERIMENTAL_PREFIX)
|
|
|| display(pkg.directory).startsWith('packages/experimental/')
|
|
const add = (pkg: Package, origin: string): void => {
|
|
if (isExperimental(pkg)) {
|
|
failures.push(`${origin} -> ${pkg.manifest.name}: default product must not include experimental packages`)
|
|
} else if (!visited.has(pkg.directory)) {
|
|
visited.add(pkg.directory)
|
|
queue.push(pkg)
|
|
}
|
|
}
|
|
const ownerOf = (path: string): Package | undefined => {
|
|
let directory = dirname(path)
|
|
for (;;) {
|
|
const pkg = directories.get(directory)
|
|
if (pkg !== undefined) return pkg
|
|
const parent = dirname(directory)
|
|
if (parent === directory) return undefined
|
|
directory = parent
|
|
}
|
|
}
|
|
const reference = (name: string, origin: string, owner?: Package): void => {
|
|
if (name.startsWith('.') || name.startsWith('/') || /^(?:file|link):/.test(name)) {
|
|
const target = name.startsWith('file://') ? fileURLToPath(name)
|
|
: resolve(owner?.directory ?? root, name.replace(/^(?:file|link):/, ''))
|
|
const pkg = directories.get(target) ?? ownerOf(target)
|
|
if (pkg !== undefined) add(pkg, origin)
|
|
return
|
|
}
|
|
const packageName = barePackageName(name)
|
|
if (packageName.startsWith(EXPERIMENTAL_PREFIX)) {
|
|
failures.push(`${origin} -> ${name}: default product must not include experimental packages`)
|
|
return
|
|
}
|
|
const pkg = packages.get(packageName)
|
|
if (pkg !== undefined) add(pkg, origin)
|
|
else if (packageName.startsWith('@deepseek-ai/')) failures.push(`${origin}: unknown workspace package ${name}`)
|
|
}
|
|
const dependency = (name: string, range: string, owner: Package, origin: string): void => {
|
|
reference(name, origin, owner)
|
|
if (/^(?:file:|link:|workspace:\.)/.test(range)) {
|
|
reference(range.replace(/^workspace:/, ''), origin, owner)
|
|
} else if (/^(?:npm:|workspace:)(?:@|[a-zA-Z])/.test(range)) {
|
|
reference(range.replace(/^(?:npm:|workspace:)/, ''), origin, owner)
|
|
}
|
|
}
|
|
const sourceReference = (specifier: string, path: string): void => {
|
|
const owner = ownerOf(path)
|
|
if (owner === undefined) return
|
|
reference(specifier, display(path), owner)
|
|
const name = barePackageName(specifier)
|
|
const range = [...RUNTIME_SECTIONS, 'devDependencies' as const]
|
|
.map(section => owner.manifest[section]?.[name]).find(value => value !== undefined)
|
|
if (range !== undefined) dependency(name, range, owner, display(path))
|
|
}
|
|
const scanSource = (path: string, followLocal = false, inlineSource?: string): void => {
|
|
if (sources.has(path)) return
|
|
sources.add(path)
|
|
const source = inlineSource ?? readFileSync(path, 'utf8')
|
|
for (const specifier of collectRuntimeSourceSpecifiers(path, source)) sourceReference(specifier, path)
|
|
for (const specifier of runtimeLocalSpecifiers(path, source)) {
|
|
const target = resolveSource(path, specifier)
|
|
const resolved = target ?? resolve(dirname(path), specifier)
|
|
const owner = ownerOf(resolved)
|
|
if (owner !== undefined && isExperimental(owner)) add(owner, display(path))
|
|
if (/[^/]*cordis[^/]*\.ya?ml$/.test(resolved)) scanConfig(resolved)
|
|
if (followLocal && target !== undefined) scanSource(target, true)
|
|
}
|
|
}
|
|
const scanEntries = (
|
|
document: unknown[], path: string, composedWeb = false, includeStack: ReadonlySet<string> = new Set(),
|
|
): void => {
|
|
const visit = (entry: unknown): void => {
|
|
if (!isRecord(entry)) return
|
|
if (typeof entry.name === 'string') {
|
|
if (composedWeb) webPluginCount += 1
|
|
const owner = ownerOf(path)
|
|
if (entry.name.startsWith('.')) {
|
|
const target = resolve(dirname(path), entry.name)
|
|
const targetOwner = ownerOf(target)
|
|
if (targetOwner !== undefined) add(targetOwner, display(path))
|
|
} else reference(entry.name, display(path), owner)
|
|
}
|
|
if (isCordisGroupEntry(entry) || entry.name === 'cordis:group' && Array.isArray(entry.config)) {
|
|
(entry.config as unknown[]).forEach(visit)
|
|
}
|
|
if (Array.isArray(entry.insert)) entry.insert.forEach(visit)
|
|
if ((entry.name === '@deepseek-ai/cordis-plugin-include' || entry.name === 'cordis:include') && isRecord(entry.config)) {
|
|
if (!composedWeb && Array.isArray(entry.config.patches)) entry.config.patches.forEach(visit)
|
|
const paths = typeof entry.config.path === 'string' ? [entry.config.path] : entry.config.path
|
|
if (Array.isArray(paths)) {
|
|
for (const included of paths) {
|
|
if (typeof included !== 'string') continue
|
|
const filename = included.startsWith('file:') ? fileURLToPath(included) : resolve(dirname(path), included)
|
|
if (!composedWeb) {
|
|
scanConfig(filename)
|
|
continue
|
|
}
|
|
if (includeStack.has(filename)) {
|
|
failures.push(`${display(path)}: cyclic Include path ${display(filename)}`)
|
|
continue
|
|
}
|
|
const content = loadCordisYaml(readFileSync(filename, 'utf8'))
|
|
if (!Array.isArray(content)) {
|
|
failures.push(`${display(filename)}: included composition must contain an entry array`)
|
|
continue
|
|
}
|
|
const entries = applyEntryPatches(content as EntryOptions[], entry.config.patches as PatchOptions[] | undefined, () => {})
|
|
scanEntries(entries, filename, true, new Set([...includeStack, filename]))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
document.forEach(visit)
|
|
}
|
|
const scanConfig = (path: string): void => {
|
|
if (configs.has(path)) return
|
|
configs.add(path)
|
|
const document = loadCordisYaml(readFileSync(path, 'utf8'))
|
|
if (!Array.isArray(document)) {
|
|
failures.push(`${display(path)}: shipped composition must contain an entry array`)
|
|
return
|
|
}
|
|
scanEntries(document, path)
|
|
}
|
|
|
|
for (const pkg of packages.values()) {
|
|
const path = display(pkg.directory)
|
|
if (path.startsWith('apps/') || path === 'python/sdk-runtime') add(pkg, path)
|
|
}
|
|
const profilePath = resolve(root, PROFILE_SOURCE)
|
|
if (existsSync(profilePath)) {
|
|
const selection = profilePackages(readFileSync(profilePath, 'utf8'))
|
|
for (const name of selection.packages) {
|
|
reference(name, PROFILE_SOURCE)
|
|
if (packages.get(name)?.manifest.dsh?.bundle?.patch === undefined) {
|
|
failures.push(`${PROFILE_SOURCE}: default bundle ${name} must declare dsh.bundle.patch`)
|
|
}
|
|
}
|
|
const webLayers = selection.webBundles.flatMap((name) => {
|
|
const pkg = packages.get(name)
|
|
const patch = pkg?.manifest.dsh?.bundle?.patch
|
|
if (pkg === undefined || patch === undefined) return []
|
|
return [loadOverlayPatches('verify-default-product-isolation', resolve(pkg.directory, patch))]
|
|
})
|
|
if (webLayers.length !== selection.webBundles.length) {
|
|
failures.push(`${PROFILE_SOURCE}: default Web bundle layers are incomplete`)
|
|
} else {
|
|
const entries = composeEntries(webLayers)
|
|
scanEntries(entries, profilePath, true)
|
|
if (webPluginCount === 0) failures.push(`${PROFILE_SOURCE}: composed Web profile contains no plugins`)
|
|
}
|
|
} else failures.push(`missing default profile source ${PROFILE_SOURCE}`)
|
|
const presets = globSync(PRESET_PATTERN, { cwd: root }).sort()
|
|
if (presets.length === 0) failures.push(`no shipped presets matched ${PRESET_PATTERN}`)
|
|
for (const path of presets) scanConfig(resolve(root, path))
|
|
|
|
const html = resolve(root, 'apps/web/index.html')
|
|
if (existsSync(html)) {
|
|
const dom = new JSDOM(readFileSync(html, 'utf8'))
|
|
try {
|
|
const entries = [...dom.window.document.querySelectorAll('script[type="module"]')]
|
|
if (entries.length === 0) failures.push('apps/web/index.html: missing default module entry')
|
|
for (const [index, entry] of entries.entries()) {
|
|
const src = entry.getAttribute('src')
|
|
if (src === null) {
|
|
scanSource(`${html}.inline-${String(index)}.js`, true, entry.textContent)
|
|
continue
|
|
}
|
|
if (/^(?:https?:)?\/\//.test(src)) {
|
|
failures.push(`apps/web/index.html: external default module entry cannot be checked: ${src}`)
|
|
continue
|
|
}
|
|
scanSource(resolve(root, 'apps/web', src.replace(/^\//, '')), true)
|
|
}
|
|
} finally {
|
|
dom.window.close()
|
|
}
|
|
} else failures.push('missing default Web entry apps/web/index.html')
|
|
|
|
for (const pkg of queue) {
|
|
const { manifest } = pkg
|
|
for (const section of RUNTIME_SECTIONS) {
|
|
for (const [name, range] of Object.entries(manifest[section] ?? {})) {
|
|
dependency(name, range, pkg, `${manifest.name} ${section}`)
|
|
}
|
|
}
|
|
if (manifest.dsh?.bundle?.patch !== undefined) scanConfig(resolve(pkg.directory, manifest.dsh.bundle.patch))
|
|
for (const tree of manifest.dsh?.configTrees ?? []) {
|
|
for (const path of globSync('**/*.{yml,yaml}', { cwd: resolve(pkg.directory, tree.path),
|
|
exclude: ['**/*.i18n.yaml', '**/preset.yml'] })) scanConfig(resolve(pkg.directory, tree.path, path))
|
|
}
|
|
if (display(pkg.directory) === 'apps/web') continue
|
|
const files = globSync('src/**/*.{ts,tsx,mts,cts,js,mjs,cjs}', { cwd: pkg.directory,
|
|
exclude: ['**/*.spec.*', '**/*.test.*', '**/*.d.ts', '**/tests/**', '**/__tests__/**'] })
|
|
if (display(pkg.directory) === 'apps/cli' && files.length === 0) failures.push('apps/cli: no default runtime sources')
|
|
for (const path of files) scanSource(resolve(pkg.directory, path))
|
|
}
|
|
return { failures: [...new Set(failures)], packageCount: visited.size,
|
|
sourceCount: sources.size, configCount: configs.size, webPluginCount }
|
|
}
|
|
|
|
function barePackageName(specifier: string): string {
|
|
return /^(?:@[^/]+\/)?[^/@]+/.exec(specifier)?.[0] ?? specifier
|
|
}
|
|
|
|
function resolveSource(from: string, specifier: string): string | undefined {
|
|
const path = resolve(dirname(from), specifier.replace(/[?#].*$/, ''))
|
|
if (!/\.(?:[cm]?[jt]sx?)$/.test(path) && /\.[^/]+$/.test(path)) return undefined
|
|
const base = path.replace(/\.[cm]?js$/, '')
|
|
return [path, `${base}.ts`, `${base}.tsx`, `${path}/index.ts`, `${path}/index.tsx`]
|
|
.find(candidate => existsSync(candidate) && statSync(candidate).isFile())
|
|
}
|
|
|
|
function runtimeLocalSpecifiers(path: string, source: string): Set<string> {
|
|
const specifiers = collectRuntimeLocalSourceSpecifiers(path, source)
|
|
const file = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true)
|
|
const visit = (node: ts.Node): void => {
|
|
if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'URL') {
|
|
const first = node.arguments?.[0]
|
|
if (first !== undefined && ts.isStringLiteralLike(first) && first.text.startsWith('.')) specifiers.add(first.text)
|
|
}
|
|
ts.forEachChild(node, visit)
|
|
}
|
|
visit(file)
|
|
return specifiers
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
}
|
|
|
|
/** Read the literal package lists that define installation-owned profile defaults. */
|
|
function profilePackages(source: string): { packages: string[]; webBundles: string[] } {
|
|
const file = ts.createSourceFile(PROFILE_SOURCE, source, ts.ScriptTarget.Latest, true)
|
|
const required = new Set(['PROFILE_TEMPLATES', 'DEFAULT_PROFILE_BUNDLES'])
|
|
const found = new Set<string>()
|
|
const packages: string[] = []
|
|
const webBundles: string[] = []
|
|
const literals = (node: ts.Node, path: string[]): void => {
|
|
if (ts.isStringLiteralLike(node)) {
|
|
if (node.text.startsWith('@')) packages.push(node.text)
|
|
if (path.join('.') === 'PROFILE_TEMPLATES.web.bundles') webBundles.push(node.text)
|
|
} else if (ts.isArrayLiteralExpression(node)) node.elements.forEach((child) => { literals(child, path) })
|
|
else if (ts.isObjectLiteralExpression(node)) node.properties.forEach((child) => { literals(child, path) })
|
|
else if (ts.isPropertyAssignment(node)) {
|
|
if (!ts.isIdentifier(node.name) && !ts.isStringLiteralLike(node.name)) {
|
|
throw new Error(`${PROFILE_SOURCE}: default profile keys must be literal names`)
|
|
}
|
|
literals(node.initializer, [...path, node.name.text])
|
|
}
|
|
else if (ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isParenthesizedExpression(node)) {
|
|
literals(node.expression, path)
|
|
} else throw new Error(`${PROFILE_SOURCE}: default profile packages must use static literal lists`)
|
|
}
|
|
for (const statement of file.statements) {
|
|
if (!ts.isVariableStatement(statement)) continue
|
|
for (const declaration of statement.declarationList.declarations) {
|
|
if (!ts.isIdentifier(declaration.name)
|
|
|| !required.has(declaration.name.text) && declaration.name.text !== 'INSTALLATION_OWNED_PROFILE_TUPLES') continue
|
|
if (declaration.initializer === undefined) continue
|
|
if (required.has(declaration.name.text)) found.add(declaration.name.text)
|
|
const before = packages.length
|
|
literals(declaration.initializer, [declaration.name.text])
|
|
if (packages.length === before) throw new Error(`${PROFILE_SOURCE}: ${declaration.name.text} has no default bundles`)
|
|
}
|
|
}
|
|
if (found.size !== required.size) throw new Error(`${PROFILE_SOURCE}: missing default profile declarations`)
|
|
if (webBundles.length === 0) throw new Error(`${PROFILE_SOURCE}: missing default Web bundle list`)
|
|
return { packages, webBundles }
|
|
}
|
|
|
|
if (import.meta.main) {
|
|
const result = verifyDefaultProductIsolation(resolve(import.meta.dirname, '..'))
|
|
if (result.failures.length > 0) {
|
|
for (const failure of result.failures) console.error(`verify-default-product-isolation: ${failure}`)
|
|
process.exitCode = 1
|
|
} else {
|
|
console.log(`verify-default-product-isolation: ${String(result.packageCount)} packages, `
|
|
+ `${String(result.sourceCount)} runtime sources, ${String(result.configCount)} configurations, `
|
|
+ `${String(result.webPluginCount)} composed Web plugins exclude experimental packages.`)
|
|
}
|
|
}
|