mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-19 23:42:03 +08:00
Add on-demand desktop server bundle
This commit is contained in:
146
scripts/release/prepare-desktop-app.js
Normal file
146
scripts/release/prepare-desktop-app.js
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFileSync } from 'node:fs';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = path.resolve(__dirname, '..', '..');
|
||||
const stageDir = path.join(rootDir, '.desktop-build', 'desktop-app');
|
||||
|
||||
const packageJson = JSON.parse(
|
||||
await fs.readFile(path.join(rootDir, 'package.json'), 'utf8'),
|
||||
);
|
||||
|
||||
function getElectronVersion() {
|
||||
try {
|
||||
return JSON.parse(
|
||||
readFileSync(path.join(rootDir, 'node_modules', 'electron', 'package.json'), 'utf8'),
|
||||
).version;
|
||||
} catch {
|
||||
try {
|
||||
return JSON.parse(
|
||||
readFileSync(path.join(rootDir, 'package-lock.json'), 'utf8'),
|
||||
).packages['node_modules/electron'].version;
|
||||
} catch {
|
||||
throw new Error('Could not resolve an exact Electron version for desktop packaging.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function pathExists(filePath) {
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function copyRequired(relativePath) {
|
||||
const from = path.join(rootDir, relativePath);
|
||||
const to = path.join(stageDir, relativePath);
|
||||
if (!(await pathExists(from))) {
|
||||
throw new Error(`Required desktop build input is missing: ${relativePath}`);
|
||||
}
|
||||
await fs.cp(from, to, { recursive: true });
|
||||
}
|
||||
|
||||
async function copyIfExists(relativePath) {
|
||||
const from = path.join(rootDir, relativePath);
|
||||
if (!(await pathExists(from))) return false;
|
||||
await fs.cp(from, path.join(stageDir, relativePath), { recursive: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function copyNodeModule(packageName) {
|
||||
const parts = packageName.split('/');
|
||||
const source = path.join(rootDir, 'node_modules', ...parts);
|
||||
if (!(await pathExists(source))) return false;
|
||||
|
||||
const target = path.join(stageDir, 'node_modules', ...parts);
|
||||
await fs.mkdir(path.dirname(target), { recursive: true });
|
||||
await fs.cp(source, target, { recursive: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
function buildDesktopPackageJson(copiedOptionalDependencies) {
|
||||
return {
|
||||
name: `${packageJson.name}-desktop`,
|
||||
version: packageJson.version,
|
||||
productName: packageJson.productName,
|
||||
description: `${packageJson.productName} desktop shell`,
|
||||
author: packageJson.author,
|
||||
license: packageJson.license,
|
||||
type: 'module',
|
||||
main: 'electron/main.js',
|
||||
dependencies: {
|
||||
ws: packageJson.dependencies.ws,
|
||||
},
|
||||
optionalDependencies: copiedOptionalDependencies,
|
||||
build: {
|
||||
appId: packageJson.build.appId,
|
||||
productName: packageJson.build.productName,
|
||||
asar: packageJson.build.asar,
|
||||
artifactName: packageJson.build.artifactName,
|
||||
electronVersion: getElectronVersion(),
|
||||
directories: {
|
||||
output: '../../release',
|
||||
},
|
||||
extraMetadata: {
|
||||
main: 'electron/main.js',
|
||||
},
|
||||
files: [
|
||||
'electron/**',
|
||||
'public/**',
|
||||
'dist/**',
|
||||
'dist-server/**',
|
||||
'node_modules/**',
|
||||
'package.json',
|
||||
],
|
||||
protocols: packageJson.build.protocols,
|
||||
mac: packageJson.build.mac,
|
||||
win: packageJson.build.win,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
await fs.rm(stageDir, { recursive: true, force: true });
|
||||
await fs.mkdir(stageDir, { recursive: true });
|
||||
|
||||
await copyRequired('electron');
|
||||
await copyRequired('dist');
|
||||
await copyRequired('public');
|
||||
|
||||
// The desktop app still ships the standalone Computer Use desktop agent, but
|
||||
// not the full local server. Local CloudCLI is downloaded on demand.
|
||||
await copyRequired('dist-server/server/computer-use-agent.js');
|
||||
await copyIfExists('dist-server/server/computer-use-agent.js.map');
|
||||
await copyRequired('dist-server/server/modules/computer-use/computer-executor.js');
|
||||
await copyIfExists('dist-server/server/modules/computer-use/computer-executor.js.map');
|
||||
|
||||
const copiedRuntimeDependencies = [];
|
||||
if (await copyNodeModule('ws')) {
|
||||
copiedRuntimeDependencies.push('ws');
|
||||
} else {
|
||||
throw new Error('Required desktop dependency is missing from node_modules: ws');
|
||||
}
|
||||
|
||||
const copiedOptionalDependencies = {};
|
||||
for (const [name, version] of Object.entries(packageJson.optionalDependencies || {})) {
|
||||
if (await copyNodeModule(name)) {
|
||||
copiedOptionalDependencies[name] = version;
|
||||
}
|
||||
}
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(stageDir, 'package.json'),
|
||||
`${JSON.stringify(buildDesktopPackageJson(copiedOptionalDependencies), null, 2)}\n`,
|
||||
'utf8',
|
||||
);
|
||||
|
||||
console.log(`Prepared thin desktop app at ${path.relative(rootDir, stageDir)}`);
|
||||
console.log(`Runtime dependencies: ${copiedRuntimeDependencies.join(', ')}`);
|
||||
if (Object.keys(copiedOptionalDependencies).length) {
|
||||
console.log(`Optional dependencies: ${Object.keys(copiedOptionalDependencies).join(', ')}`);
|
||||
}
|
||||
Reference in New Issue
Block a user