@directivegames/genesys.sdk 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/src/asset-pack/eslint.config.js +43 -0
- package/dist/src/asset-pack/scripts/postinstall.js +64 -0
- package/dist/src/asset-pack/src/index.js +1 -0
- package/dist/src/core/cli.js +306 -0
- package/dist/src/core/common.js +324 -0
- package/dist/src/core/index.js +6 -0
- package/dist/src/core/tools/build-project.js +450 -0
- package/dist/src/core/tools/index.js +2 -0
- package/dist/src/core/tools/new-asset-pack.js +150 -0
- package/dist/src/core/tools/new-project.js +292 -0
- package/dist/src/core/types.js +1 -0
- package/dist/src/dependencies.js +82 -0
- package/dist/src/electron/IpcSerializableError.js +38 -0
- package/dist/src/electron/api.js +7 -0
- package/dist/src/electron/backend/actions.js +56 -0
- package/dist/src/electron/backend/handler.js +441 -0
- package/dist/src/electron/backend/logging.js +41 -0
- package/dist/src/electron/backend/main.js +315 -0
- package/dist/src/electron/backend/menu.js +208 -0
- package/dist/src/electron/backend/state.js +201 -0
- package/dist/src/electron/backend/tools/const.js +9 -0
- package/dist/src/electron/backend/tools/file-server.js +383 -0
- package/dist/src/electron/backend/tools/open-project.js +261 -0
- package/dist/src/electron/backend/window.js +161 -0
- package/dist/src/templates/eslint.config.js +43 -0
- package/dist/src/templates/scripts/genesys/build-project.js +42 -0
- package/dist/src/templates/scripts/genesys/calc-bounding-box.js +205 -0
- package/dist/src/templates/scripts/genesys/common.js +36 -0
- package/dist/src/templates/scripts/genesys/const.js +9 -0
- package/dist/src/templates/scripts/genesys/dev/dump-default-scene.js +8 -0
- package/dist/src/templates/scripts/genesys/dev/generate-manifest.js +116 -0
- package/dist/src/templates/scripts/genesys/dev/launcher.js +39 -0
- package/dist/src/templates/scripts/genesys/dev/storage-provider.js +188 -0
- package/dist/src/templates/scripts/genesys/dev/update-template-scenes.js +67 -0
- package/dist/src/templates/scripts/genesys/doc-server.js +12 -0
- package/dist/src/templates/scripts/genesys/genesys-mcp.js +413 -0
- package/dist/src/templates/scripts/genesys/mcp/doc-tools.js +70 -0
- package/dist/src/templates/scripts/genesys/mcp/editor-functions.js +123 -0
- package/dist/src/templates/scripts/genesys/mcp/editor-tools.js +51 -0
- package/dist/src/templates/scripts/genesys/mcp/get-scene-state.js +26 -0
- package/dist/src/templates/scripts/genesys/mcp/run-subprocess.js +23 -0
- package/dist/src/templates/scripts/genesys/mcp/search-actors.js +703 -0
- package/dist/src/templates/scripts/genesys/mcp/search-assets.js +296 -0
- package/dist/src/templates/scripts/genesys/mcp/utils.js +234 -0
- package/dist/src/templates/scripts/genesys/misc.js +32 -0
- package/dist/src/templates/scripts/genesys/mock.js +5 -0
- package/dist/src/templates/scripts/genesys/place-actors.js +112 -0
- package/dist/src/templates/scripts/genesys/post-install.js +25 -0
- package/dist/src/templates/scripts/genesys/remove-engine-comments.js +113 -0
- package/dist/src/templates/scripts/genesys/storageProvider.js +146 -0
- package/dist/src/templates/scripts/genesys/validate-prefabs.js +115 -0
- package/dist/src/templates/src/index.js +20 -0
- package/dist/src/templates/src/templates/firstPerson/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/firstPerson/src/game.js +30 -0
- package/dist/src/templates/src/templates/firstPerson/src/player.js +60 -0
- package/dist/src/templates/src/templates/fps/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/fps/src/game.js +30 -0
- package/dist/src/templates/src/templates/fps/src/player.js +64 -0
- package/dist/src/templates/src/templates/fps/src/weapon.js +62 -0
- package/dist/src/templates/src/templates/freeCamera/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/freeCamera/src/game.js +30 -0
- package/dist/src/templates/src/templates/freeCamera/src/player.js +43 -0
- package/dist/src/templates/src/templates/sideScroller/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/sideScroller/src/const.js +43 -0
- package/dist/src/templates/src/templates/sideScroller/src/game.js +103 -0
- package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +249 -0
- package/dist/src/templates/src/templates/sideScroller/src/player.js +105 -0
- package/dist/src/templates/src/templates/thirdPerson/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/thirdPerson/src/game.js +30 -0
- package/dist/src/templates/src/templates/thirdPerson/src/player.js +63 -0
- package/dist/src/templates/src/templates/vehicle/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +122 -0
- package/dist/src/templates/src/templates/vehicle/src/game.js +33 -0
- package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +189 -0
- package/dist/src/templates/src/templates/vehicle/src/player.js +102 -0
- package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +259 -0
- package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +100 -0
- package/dist/src/templates/src/templates/vr-game/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/vr-game/src/game.js +55 -0
- package/dist/src/templates/src/templates/vr-game/src/sample-vr-actor.js +29 -0
- package/dist/src/templates/vite.config.js +46 -0
- package/package.json +176 -0
- package/scripts/post-install.ts +143 -0
- package/src/asset-pack/.gitattributes +89 -0
- package/src/asset-pack/eslint.config.js +45 -0
- package/src/asset-pack/gitignore +11 -0
- package/src/asset-pack/scripts/postinstall.ts +81 -0
- package/src/asset-pack/src/index.ts +0 -0
- package/src/asset-pack/tsconfig.json +34 -0
- package/src/templates/.cursor/mcp.json +20 -0
- package/src/templates/.cursorignore +2 -0
- package/src/templates/.gitattributes +89 -0
- package/src/templates/.vscode/settings.json +6 -0
- package/src/templates/AGENTS.md +86 -0
- package/src/templates/CLAUDE.md +1 -0
- package/src/templates/README.md +24 -0
- package/src/templates/eslint.config.js +45 -0
- package/src/templates/gitignore +11 -0
- package/src/templates/index.html +34 -0
- package/src/templates/pnpm-lock.yaml +3676 -0
- package/src/templates/scripts/genesys/build-project.ts +51 -0
- package/src/templates/scripts/genesys/calc-bounding-box.ts +272 -0
- package/src/templates/scripts/genesys/common.ts +46 -0
- package/src/templates/scripts/genesys/const.ts +9 -0
- package/src/templates/scripts/genesys/dev/dump-default-scene.ts +11 -0
- package/src/templates/scripts/genesys/dev/generate-manifest.ts +146 -0
- package/src/templates/scripts/genesys/dev/launcher.ts +46 -0
- package/src/templates/scripts/genesys/dev/storage-provider.ts +229 -0
- package/src/templates/scripts/genesys/dev/update-template-scenes.ts +84 -0
- package/src/templates/scripts/genesys/doc-server.ts +16 -0
- package/src/templates/scripts/genesys/genesys-mcp.ts +526 -0
- package/src/templates/scripts/genesys/mcp/doc-tools.ts +86 -0
- package/src/templates/scripts/genesys/mcp/editor-functions.ts +151 -0
- package/src/templates/scripts/genesys/mcp/editor-tools.ts +73 -0
- package/src/templates/scripts/genesys/mcp/get-scene-state.ts +35 -0
- package/src/templates/scripts/genesys/mcp/run-subprocess.ts +30 -0
- package/src/templates/scripts/genesys/mcp/search-actors.ts +858 -0
- package/src/templates/scripts/genesys/mcp/search-assets.ts +380 -0
- package/src/templates/scripts/genesys/mcp/utils.ts +281 -0
- package/src/templates/scripts/genesys/misc.ts +42 -0
- package/src/templates/scripts/genesys/mock.ts +6 -0
- package/src/templates/scripts/genesys/place-actors.ts +179 -0
- package/src/templates/scripts/genesys/post-install.ts +30 -0
- package/src/templates/scripts/genesys/prefab.schema.json +85 -0
- package/src/templates/scripts/genesys/remove-engine-comments.ts +135 -0
- package/src/templates/scripts/genesys/run-mcp-inspector.bat +5 -0
- package/src/templates/scripts/genesys/storageProvider.ts +182 -0
- package/src/templates/scripts/genesys/validate-prefabs.ts +138 -0
- package/src/templates/src/index.ts +22 -0
- package/src/templates/src/templates/firstPerson/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/firstPerson/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/firstPerson/src/game.ts +39 -0
- package/src/templates/src/templates/firstPerson/src/player.ts +63 -0
- package/src/templates/src/templates/fps/assets/default.genesys-scene +9460 -0
- package/src/templates/src/templates/fps/assets/models/SM_Beam_400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_ChamferCube.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Ramp_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Rifle.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/fps/src/game.ts +39 -0
- package/src/templates/src/templates/fps/src/player.ts +69 -0
- package/src/templates/src/templates/fps/src/weapon.ts +54 -0
- package/src/templates/src/templates/freeCamera/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/freeCamera/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/freeCamera/src/game.ts +39 -0
- package/src/templates/src/templates/freeCamera/src/player.ts +45 -0
- package/src/templates/src/templates/sideScroller/assets/default.genesys-scene +122 -0
- package/src/templates/src/templates/sideScroller/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/sideScroller/src/const.ts +46 -0
- package/src/templates/src/templates/sideScroller/src/game.ts +122 -0
- package/src/templates/src/templates/sideScroller/src/level-generator.ts +361 -0
- package/src/templates/src/templates/sideScroller/src/player.ts +125 -0
- package/src/templates/src/templates/thirdPerson/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/thirdPerson/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/thirdPerson/src/game.ts +39 -0
- package/src/templates/src/templates/thirdPerson/src/player.ts +61 -0
- package/src/templates/src/templates/vehicle/assets/default.genesys-scene +226 -0
- package/src/templates/src/templates/vehicle/assets/models/cyberTruck/chassis.glb +0 -0
- package/src/templates/src/templates/vehicle/assets/models/cyberTruck/wheel.glb +0 -0
- package/src/templates/src/templates/vehicle/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/vehicle/src/base-vehicle.ts +145 -0
- package/src/templates/src/templates/vehicle/src/game.ts +43 -0
- package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +191 -0
- package/src/templates/src/templates/vehicle/src/player.ts +109 -0
- package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +266 -0
- package/src/templates/src/templates/vehicle/src/ui-hints.ts +101 -0
- package/src/templates/src/templates/vr-game/assets/default.genesys-scene +247 -0
- package/src/templates/src/templates/vr-game/src/auto-imports.ts +1 -0
- package/src/templates/src/templates/vr-game/src/game.ts +66 -0
- package/src/templates/src/templates/vr-game/src/sample-vr-actor.ts +26 -0
- package/src/templates/tsconfig.json +35 -0
- package/src/templates/vite.config.ts +52 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { exec, execSync, spawn } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import fixPath from 'fix-path';
|
|
7
|
+
import * as ENGINE from 'genesys.js';
|
|
8
|
+
import { JSDOM } from 'jsdom';
|
|
9
|
+
import { ALL_DEPENDENCIES } from '../dependencies.js';
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
// Fix PATH on macOS when running as a GUI app
|
|
12
|
+
fixPath();
|
|
13
|
+
export function mockBrowserEnvironment() {
|
|
14
|
+
const dom = new JSDOM('<!DOCTYPE html><p>Hello world</p>');
|
|
15
|
+
global.window = dom.window;
|
|
16
|
+
global.document = dom.window.document;
|
|
17
|
+
global.HTMLElement = dom.window.HTMLElement;
|
|
18
|
+
if (navigator) {
|
|
19
|
+
navigator.getGamepads = () => [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function getProjectRoot() {
|
|
23
|
+
let currentDir = __dirname;
|
|
24
|
+
while (true) {
|
|
25
|
+
if (fs.existsSync(path.join(currentDir, 'package.json'))) {
|
|
26
|
+
return currentDir;
|
|
27
|
+
}
|
|
28
|
+
const parentDir = path.dirname(currentDir);
|
|
29
|
+
if (parentDir === currentDir) {
|
|
30
|
+
throw new Error('Project root not found');
|
|
31
|
+
}
|
|
32
|
+
currentDir = parentDir;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function checkEngineVersion(engineVersion) {
|
|
36
|
+
const localEngineVersion = getEngineVersion();
|
|
37
|
+
if (engineVersion && engineVersion !== localEngineVersion) {
|
|
38
|
+
throw new Error(`Engine version ${localEngineVersion} does not match ${engineVersion}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function getEngineVersion() {
|
|
42
|
+
return ALL_DEPENDENCIES['genesys.js'];
|
|
43
|
+
}
|
|
44
|
+
export function getAppVersion() {
|
|
45
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(getProjectRoot(), 'package.json'), 'utf8'));
|
|
46
|
+
return pkg.version;
|
|
47
|
+
}
|
|
48
|
+
export function writeFileSync(filePath, content) {
|
|
49
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
50
|
+
fs.writeFileSync(filePath, content);
|
|
51
|
+
}
|
|
52
|
+
export function findFilesByPredicate(folderPath, predicate, excludeFolders) {
|
|
53
|
+
const result = [];
|
|
54
|
+
const excludeSet = excludeFolders ? new Set(excludeFolders) : null;
|
|
55
|
+
function traverse(currentPath) {
|
|
56
|
+
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
if (excludeSet && excludeSet.has(entry.name)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
traverse(fullPath);
|
|
64
|
+
}
|
|
65
|
+
else if (predicate(fullPath)) {
|
|
66
|
+
result.push(fullPath);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
traverse(folderPath);
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
/** Async version of writeFileSync */
|
|
74
|
+
export async function writeFileAsync(filePath, content) {
|
|
75
|
+
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
76
|
+
await fs.promises.writeFile(filePath, content);
|
|
77
|
+
}
|
|
78
|
+
/** Async version of fs.existsSync */
|
|
79
|
+
export async function existsAsync(filePath) {
|
|
80
|
+
try {
|
|
81
|
+
await fs.promises.access(filePath);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Async version of fs.cpSync */
|
|
89
|
+
export async function copyAsync(source, destination, options) {
|
|
90
|
+
await fs.promises.cp(source, destination, options);
|
|
91
|
+
}
|
|
92
|
+
/** Async version of fs.mkdirSync */
|
|
93
|
+
export async function mkdirAsync(dirPath, options) {
|
|
94
|
+
await fs.promises.mkdir(dirPath, options);
|
|
95
|
+
}
|
|
96
|
+
/** Async version of fs.readdirSync */
|
|
97
|
+
export async function readdirAsync(dirPath) {
|
|
98
|
+
return await fs.promises.readdir(dirPath);
|
|
99
|
+
}
|
|
100
|
+
export function getProjectFolderAndFile(projectPath, logger) {
|
|
101
|
+
const isDirectory = fs.lstatSync(projectPath).isDirectory();
|
|
102
|
+
const folder = isDirectory ? projectPath : path.dirname(projectPath);
|
|
103
|
+
if (!fs.existsSync(folder)) {
|
|
104
|
+
return { folder: '', file: '' };
|
|
105
|
+
}
|
|
106
|
+
let projectFile = '';
|
|
107
|
+
// if the path is already a valid project file, use it directly
|
|
108
|
+
if (!isDirectory) {
|
|
109
|
+
if (path.extname(projectPath) === ENGINE.GAME_PROJECT_FILE_EXT && fs.existsSync(projectPath)) {
|
|
110
|
+
projectFile = path.basename(projectPath);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
logger.warn(`Path ${projectPath} is not a valid project file. Ignoring it, and using the folder.`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// check if a project file of the same name as the folder exists
|
|
117
|
+
if (!projectFile) {
|
|
118
|
+
const folderName = path.basename(folder);
|
|
119
|
+
const testProjectFile = `${folderName}${ENGINE.GAME_PROJECT_FILE_EXT}`;
|
|
120
|
+
if (fs.existsSync(path.join(folder, testProjectFile))) {
|
|
121
|
+
projectFile = testProjectFile;
|
|
122
|
+
logger.log(`Using existing project file: ${projectFile}`);
|
|
123
|
+
}
|
|
124
|
+
// check for any existing project files in the folder, and use the latest one
|
|
125
|
+
if (!projectFile) {
|
|
126
|
+
const existingProjectFiles = fs.readdirSync(folder)
|
|
127
|
+
.filter(file => path.extname(file) === ENGINE.GAME_PROJECT_FILE_EXT && fs.statSync(path.join(folder, file)).isFile());
|
|
128
|
+
let latestFile = '';
|
|
129
|
+
let latestTimestamp = 0;
|
|
130
|
+
for (const file of existingProjectFiles) {
|
|
131
|
+
const fullPath = path.join(folder, file);
|
|
132
|
+
const timestamp = fs.statSync(fullPath).mtime.getTime();
|
|
133
|
+
if (timestamp > latestTimestamp) {
|
|
134
|
+
latestTimestamp = timestamp;
|
|
135
|
+
latestFile = file;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (latestFile) {
|
|
139
|
+
projectFile = latestFile;
|
|
140
|
+
logger.log(`Using latest project file: ${projectFile}`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
logger.warn(`No project files found in folder: ${folder}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
folder,
|
|
149
|
+
file: projectFile,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
export function runCommand(command, workingDir, logger) {
|
|
153
|
+
const originalDir = process.cwd();
|
|
154
|
+
try {
|
|
155
|
+
if (workingDir) {
|
|
156
|
+
process.chdir(workingDir);
|
|
157
|
+
}
|
|
158
|
+
logger.log(`Running command: ${command}`);
|
|
159
|
+
execSync(command, { stdio: 'inherit' });
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
process.chdir(originalDir);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const execAsync = promisify(exec);
|
|
166
|
+
/** Strip ANSI escape codes from strings */
|
|
167
|
+
function stripAnsi(str) {
|
|
168
|
+
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '').replace(/\x1B\]/g, '');
|
|
169
|
+
}
|
|
170
|
+
export async function runCommandAsync(command, workingDir, logger, onProgress) {
|
|
171
|
+
const originalDir = process.cwd();
|
|
172
|
+
// If no progress callback is provided, use the old exec method for backwards compatibility
|
|
173
|
+
if (!onProgress) {
|
|
174
|
+
let error = null;
|
|
175
|
+
let stdout = '';
|
|
176
|
+
let stderr = '';
|
|
177
|
+
try {
|
|
178
|
+
if (workingDir) {
|
|
179
|
+
process.chdir(workingDir);
|
|
180
|
+
}
|
|
181
|
+
logger.log(`Running command async: ${command}`);
|
|
182
|
+
const { stdout: execStdout, stderr: execStderr } = await execAsync(command);
|
|
183
|
+
stdout = execStdout;
|
|
184
|
+
stderr = execStderr;
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
error = err;
|
|
188
|
+
stdout = error.stdout ?? '';
|
|
189
|
+
stderr = error.stderr ?? '';
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
process.chdir(originalDir);
|
|
193
|
+
}
|
|
194
|
+
let output = undefined;
|
|
195
|
+
if (stdout.length > 0 || stderr.length > 0) {
|
|
196
|
+
output = `Output of command ${command}:`;
|
|
197
|
+
if (stdout.length > 0) {
|
|
198
|
+
output += `\nstdout:\n${stdout}`;
|
|
199
|
+
}
|
|
200
|
+
if (stderr.length > 0) {
|
|
201
|
+
output += `\nstderr:\n${stderr}`;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (error) {
|
|
205
|
+
logger.error(`Command ${command} exited with code ${error.code}: ${error.message}`);
|
|
206
|
+
if (output) {
|
|
207
|
+
logger.error(output);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
if (output) {
|
|
212
|
+
logger.log(output);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return { error, stdout, stderr };
|
|
216
|
+
}
|
|
217
|
+
// Use spawn for real-time progress tracking
|
|
218
|
+
return new Promise((resolve) => {
|
|
219
|
+
try {
|
|
220
|
+
if (workingDir) {
|
|
221
|
+
process.chdir(workingDir);
|
|
222
|
+
}
|
|
223
|
+
logger.log(`Running command async with progress: ${command}`);
|
|
224
|
+
// Parse command for spawn (handles complex commands with arguments)
|
|
225
|
+
const isWindows = process.platform === 'win32';
|
|
226
|
+
const shell = isWindows ? 'cmd.exe' : '/bin/sh';
|
|
227
|
+
const shellArg = isWindows ? '/c' : '-c';
|
|
228
|
+
const child = spawn(shell, [shellArg, command], {
|
|
229
|
+
cwd: workingDir ?? undefined,
|
|
230
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
231
|
+
env: process.env
|
|
232
|
+
});
|
|
233
|
+
let stdout = '';
|
|
234
|
+
let stderr = '';
|
|
235
|
+
child.stdout?.on('data', (data) => {
|
|
236
|
+
const text = data.toString();
|
|
237
|
+
stdout += text;
|
|
238
|
+
// Extract progress info from pnpm/command output
|
|
239
|
+
const lines = text.split('\n').filter((line) => line.trim());
|
|
240
|
+
for (const line of lines) {
|
|
241
|
+
const cleanedLine = stripAnsi(line.trim());
|
|
242
|
+
// Pass through pnpm progress indicators (verbose output, stages, completion)
|
|
243
|
+
if (cleanedLine.includes('http fetch') || cleanedLine.includes('http 200') ||
|
|
244
|
+
cleanedLine.includes('idealTree') || cleanedLine.includes('reify') ||
|
|
245
|
+
cleanedLine.includes('extract') || cleanedLine.includes('build') ||
|
|
246
|
+
cleanedLine.includes('postinstall') || cleanedLine.includes('timing') ||
|
|
247
|
+
cleanedLine.match(/added \d+|removed \d+|changed \d+|audited \d+/)) {
|
|
248
|
+
onProgress(cleanedLine);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
child.stderr?.on('data', (data) => {
|
|
253
|
+
const text = data.toString();
|
|
254
|
+
stderr += text;
|
|
255
|
+
// pnpm outputs progress info to stderr as well
|
|
256
|
+
const lines = text.split('\n').filter((line) => line.trim());
|
|
257
|
+
for (const line of lines) {
|
|
258
|
+
const cleanedLine = stripAnsi(line.trim());
|
|
259
|
+
// Pass through pnpm progress indicators (filter out deprecated warnings)
|
|
260
|
+
if (cleanedLine && !cleanedLine.startsWith('WARN deprecated')) {
|
|
261
|
+
if (cleanedLine.includes('http fetch') || cleanedLine.includes('http 200') ||
|
|
262
|
+
cleanedLine.includes('idealTree') || cleanedLine.includes('reify') ||
|
|
263
|
+
cleanedLine.includes('extract') || cleanedLine.includes('build') ||
|
|
264
|
+
cleanedLine.includes('timing') || cleanedLine.match(/added \d+/)) {
|
|
265
|
+
onProgress(cleanedLine);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
child.on('close', (code) => {
|
|
271
|
+
process.chdir(originalDir);
|
|
272
|
+
let output = undefined;
|
|
273
|
+
if (stdout.length > 0 || stderr.length > 0) {
|
|
274
|
+
output = `Output of command ${command}:`;
|
|
275
|
+
if (stdout.length > 0) {
|
|
276
|
+
output += `\nstdout:\n${stdout}`;
|
|
277
|
+
}
|
|
278
|
+
if (stderr.length > 0) {
|
|
279
|
+
output += `\nstderr:\n${stderr}`;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const error = code !== 0 ? Object.assign(new Error(`Command failed with exit code ${code}`), {
|
|
283
|
+
code,
|
|
284
|
+
stdout,
|
|
285
|
+
stderr,
|
|
286
|
+
killed: false,
|
|
287
|
+
signal: undefined,
|
|
288
|
+
cmd: command
|
|
289
|
+
}) : null;
|
|
290
|
+
if (error) {
|
|
291
|
+
logger.error(`Command ${command} exited with code ${code}`);
|
|
292
|
+
if (output) {
|
|
293
|
+
logger.error(output);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
if (output) {
|
|
298
|
+
logger.log(output);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
resolve({ error, stdout, stderr });
|
|
302
|
+
});
|
|
303
|
+
child.on('error', (err) => {
|
|
304
|
+
process.chdir(originalDir);
|
|
305
|
+
const execError = err;
|
|
306
|
+
logger.error(`Failed to execute command ${command}: ${execError.message}`);
|
|
307
|
+
resolve({
|
|
308
|
+
error: execError,
|
|
309
|
+
stdout: '',
|
|
310
|
+
stderr: execError.message
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
process.chdir(originalDir);
|
|
316
|
+
const error = err;
|
|
317
|
+
resolve({
|
|
318
|
+
error,
|
|
319
|
+
stdout: error.stdout ?? '',
|
|
320
|
+
stderr: error.stderr ?? ''
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|