dsh-source.mjs 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. import * as fs from 'node:fs'
  2. import * as path from 'node:path'
  3. import { execFileSync } from 'node:child_process'
  4. export const baseline = JSON.parse(fs.readFileSync(new URL('../../../dsh-baseline.json', import.meta.url), 'utf8'))
  5. function directories(root) {
  6. return fs.readdirSync(root, { withFileTypes: true }).filter(item => item.isDirectory()).map(item => path.join(root, item.name))
  7. }
  8. /** 固定官方 commit 的已构建 checkout;不改源码或安装状态。 */
  9. export function loadSourceHost(input) {
  10. const root = fs.realpathSync.native(path.resolve(input))
  11. const git = (...args) => execFileSync('git', args, { cwd: root, encoding: 'utf8', windowsHide: true }).trim()
  12. const commit = git('rev-parse', 'HEAD')
  13. if (commit !== baseline.source.commit) throw new Error(`DSH source must be ${baseline.source.tag} (${baseline.source.commit}); found ${commit}`)
  14. if (git('status', '--porcelain', '--untracked-files=no') !== '') throw new Error('DSH source has tracked changes; use the unmodified baseline checkout')
  15. const packages = new Map()
  16. const candidates = [
  17. ...directories(path.join(root, 'vendor')),
  18. ...directories(path.join(root, 'packages')).flatMap(directories),
  19. ...directories(path.join(root, 'apps')),
  20. ]
  21. for (const directory of candidates) {
  22. const file = path.join(directory, 'package.json')
  23. if (!fs.existsSync(file)) continue
  24. const metadata = JSON.parse(fs.readFileSync(file, 'utf8'))
  25. packages.set(metadata.name, { directory, metadata })
  26. }
  27. const get = name => {
  28. const pkg = packages.get(name)
  29. if (pkg === undefined) throw new Error(`DSH source package missing: ${name}`)
  30. if (name.startsWith('@deepseek-ai/dsh') && pkg.metadata.version !== baseline.source.version) throw new Error(`DSH package version mismatch: ${name}`)
  31. return pkg
  32. }
  33. const entry = name => {
  34. const pkg = get(name)
  35. if (typeof pkg.metadata.main !== 'string') throw new Error(`DSH package has no main entry: ${name}`)
  36. const file = path.resolve(pkg.directory, pkg.metadata.main)
  37. if (!fs.existsSync(file)) throw new Error(`DSH build missing: ${file}; run npm run build:lib:host in the checkout`)
  38. return file
  39. }
  40. const typePaths = {}
  41. for (const [name, pkg] of packages) {
  42. if (typeof pkg.metadata.types === 'string') {
  43. const file = path.resolve(pkg.directory, pkg.metadata.types)
  44. if (fs.existsSync(file)) typePaths[name] = [file]
  45. }
  46. for (const [subpath, definition] of Object.entries(pkg.metadata.exports ?? {})) {
  47. if (!subpath.startsWith('.') || typeof definition?.types !== 'string') continue
  48. const file = path.resolve(pkg.directory, definition.types)
  49. if (fs.existsSync(file)) typePaths[subpath === '.' ? name : name + subpath.slice(1)] = [file]
  50. }
  51. }
  52. return { root, commit, get, entry, typePaths, version: baseline.source.version }
  53. }