feat(runtime): upgrade DSH and bundled plugins with startup migration

This commit is contained in:
2026-10-18 02:14:27 +08:00
parent eb7e809999
commit 2e31e74972
18 changed files with 501 additions and 80 deletions
+35 -34
View File
@@ -1,36 +1,37 @@
import { mkdirSync, existsSync, writeFileSync, readFileSync, readdirSync, lstatSync } from 'node:fs';
import { join } from 'node:path';
const home = process.env.DSH_HOME;
if (!home) throw new Error('DSH_HOME required');
const profile = join(home, 'profiles/web');
for (const dir of [home, join(home, 'profiles'), profile]) {
try {
const stat = lstatSync(dir);
if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('unsafe profile directory');
} catch (error) { if (error.code !== 'ENOENT') throw error; }
}
// Never rewrite an existing user manifest or patch.
if (!existsSync(profile) || readdirSync(profile).length === 0) {
mkdirSync(profile, { recursive: true });
writeFileSync(join(profile, 'package.json'), JSON.stringify({
name: 'dsh-profile-web', private: true,
dependencies: { 'dsh-preview-download': 'file:/usr/local/lib/dsh-mgr/dsh-preview-download-1.0.1.tgz', 'dsh-chat-manager': 'file:/usr/local/lib/dsh-mgr/dsh-chat-manager-1.5.12-dsh-mgr.1.tgz', 'dsh-mgr-workspace': 'file:/usr/local/lib/dsh-mgr/dsh-mgr-workspace-1.0.0.tgz', 'dsh-web-mobile': 'file:/usr/local/lib/dsh-mgr/dsh-web-mobile-3.0.5.tgz', 'dsh-free-search': 'file:/usr/local/lib/dsh-mgr/dsh-free-search-0.8.5.tgz' },
pnpm: { overrides: {
'dsh-chat-manager>clsx': 'file:/usr/local/lib/dsh-mgr/clsx-2.1.1.tgz',
'dsh-free-search>@deepseek-ai/schemastery': 'file:/usr/local/lib/dsh-mgr/schemastery-3.18.4.tgz',
'@deepseek-ai/schemastery>@deepseek-ai/cosmokit': 'file:/usr/local/lib/dsh-mgr/cosmokit-1.8.5.tgz',
'@deepseek-ai/schemastery>@standard-schema/spec': 'file:/usr/local/lib/dsh-mgr/spec-1.1.0.tgz',
} },
dsh: { profile: { bundles: ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app', 'dsh-web-mobile', 'dsh-free-search', 'dsh-mgr-workspace', 'dsh-chat-manager', 'dsh-preview-download'] } },
}, null, 2), { flag: 'wx', mode: 0o600 });
console.log('dsh-mgr: new web profile includes [email protected] and [email protected] and [email protected]');
}
const manifestPath = join(profile, 'package.json');
const workspacePath = join(profile, 'pnpm-workspace.yaml');
// Backups omit workspace settings; restore host-provided peers before pnpm runs.
if (existsSync(manifestPath) && !existsSync(workspacePath)) {
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
if (manifest.dependencies?.['dsh-chat-manager'] === 'file:/usr/local/lib/dsh-mgr/dsh-chat-manager-1.5.12-dsh-mgr.1.tgz' || manifest.dependencies?.['dsh-web-mobile'] === 'file:/usr/local/lib/dsh-mgr/dsh-web-mobile-3.0.5.tgz' || manifest.dependencies?.['dsh-free-search'] === 'file:/usr/local/lib/dsh-mgr/dsh-free-search-0.8.5.tgz') {
writeFileSync(workspacePath, 'packages:\n - .\n\nnodeLinker: hoisted\nautoInstallPeers: false\n', { flag: 'wx', mode: 0o600 });
import { mkdirSync, existsSync, writeFileSync, readFileSync, readdirSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
import { BUILTIN_DEPENDENCIES, BUILTIN_OVERRIDES, assertSafeProfile, reconcileProfile } from './reconcile-profile.mjs';
export function initProfile(home, options) {
if (!home) throw new Error('DSH_HOME required');
const profile = join(home, 'profiles/web');
assertSafeProfile(profile);
if (!existsSync(profile) || readdirSync(profile).length === 0) {
mkdirSync(profile, { recursive: true });
writeFileSync(join(profile, 'package.json'), JSON.stringify({
name: 'dsh-profile-web', private: true,
dependencies: BUILTIN_DEPENDENCIES,
pnpm: { overrides: BUILTIN_OVERRIDES },
dsh: { profile: { bundles: ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app', 'dsh-web-mobile', 'dsh-free-search', 'dsh-mgr-workspace', 'dsh-chat-manager', 'dsh-preview-download'] } },
}, null, 2), { flag: 'wx', mode: 0o600 });
console.log('dsh-mgr: new web profile includes dsh-web-mobile@3.1.0 and [email protected] and [email protected]');
}
const migration = reconcileProfile(profile, options);
console.log(migration.changed
? "dsh-mgr: builtin profile updated; original saved to " + migration.backupPath
: "dsh-mgr: builtin profile check complete; no changes");
const manifestPath = join(profile, 'package.json');
const workspacePath = join(profile, 'pnpm-workspace.yaml');
// Backups omit workspace settings; restore host-provided peers before pnpm runs.
if (existsSync(manifestPath) && !existsSync(workspacePath)) {
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
if (['dependencies', 'devDependencies', 'optionalDependencies'].some(section =>
['dsh-chat-manager', 'dsh-web-mobile', 'dsh-free-search'].some(name => manifest[section]?.[name] === BUILTIN_DEPENDENCIES[name]))) {
writeFileSync(workspacePath, 'packages:\n - .\n\nnodeLinker: hoisted\nautoInstallPeers: false\n', { flag: 'wx', mode: 0o600 });
}
}
}
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
initProfile(process.env.DSH_HOME);
}