@open-audio-stack/core 0.1.41 → 0.1.43
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/build/classes/Manager.d.ts +1 -1
- package/build/classes/Manager.js +14 -10
- package/build/classes/ManagerLocal.d.ts +2 -2
- package/build/classes/ManagerLocal.js +51 -38
- package/build/helpers/file.d.ts +1 -1
- package/build/helpers/file.js +30 -7
- package/build/types/PluginFormat.d.ts +3 -1
- package/build/types/PluginFormat.js +1 -0
- package/package.json +1 -1
|
@@ -14,7 +14,7 @@ export declare class Manager extends Base {
|
|
|
14
14
|
getPackage(slug: string): Package | undefined;
|
|
15
15
|
getReport(): ManagerReport;
|
|
16
16
|
outputReport(): void;
|
|
17
|
-
listPackages(installed?: boolean): Package[];
|
|
17
|
+
listPackages(installed?: boolean, showAll?: boolean): Package[];
|
|
18
18
|
removePackage(slug: string): void;
|
|
19
19
|
reset(): void;
|
|
20
20
|
search(query: string): Package[];
|
package/build/classes/Manager.js
CHANGED
|
@@ -2,6 +2,8 @@ import { apiJson } from '../helpers/api.js';
|
|
|
2
2
|
import { Config } from './Config.js';
|
|
3
3
|
import { Package } from './Package.js';
|
|
4
4
|
import { Base } from './Base.js';
|
|
5
|
+
import { packageCompatibleFiles } from '../helpers/package.js';
|
|
6
|
+
import { getArchitecture, getSystem } from '../helpers/utilsLocal.js';
|
|
5
7
|
export class Manager extends Base {
|
|
6
8
|
config;
|
|
7
9
|
packages;
|
|
@@ -52,19 +54,21 @@ export class Manager extends Base {
|
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
|
-
listPackages(installed) {
|
|
57
|
+
listPackages(installed, showAll = false) {
|
|
58
|
+
let packages = Array.from(this.packages.values());
|
|
56
59
|
if (installed !== undefined) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
packages = packages.filter(pkg => Array.from(pkg.versions.values()).some(pkgVersion => (installed === true && pkgVersion.installed) || (installed === false && !pkgVersion.installed)));
|
|
61
|
+
}
|
|
62
|
+
if (!showAll) {
|
|
63
|
+
packages = packages.filter(pkg => {
|
|
64
|
+
const pkgVersion = pkg.getVersionLatest();
|
|
65
|
+
if (!pkgVersion)
|
|
66
|
+
return false;
|
|
67
|
+
const files = packageCompatibleFiles(pkgVersion, [getArchitecture()], [getSystem()], []);
|
|
68
|
+
return files.length > 0;
|
|
64
69
|
});
|
|
65
|
-
return packagesFiltered;
|
|
66
70
|
}
|
|
67
|
-
return
|
|
71
|
+
return packages.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
68
72
|
}
|
|
69
73
|
removePackage(slug) {
|
|
70
74
|
if (!this.packages.has(slug))
|
|
@@ -10,12 +10,12 @@ export declare class ManagerLocal extends Manager {
|
|
|
10
10
|
create(): Promise<void>;
|
|
11
11
|
scan(ext?: string, installable?: boolean): void;
|
|
12
12
|
export(dir: string, ext?: string): boolean;
|
|
13
|
-
install(slug: string, version?: string): Promise<
|
|
13
|
+
install(slug: string, version?: string): Promise<PackageVersion>;
|
|
14
14
|
installAll(): Promise<Package[]>;
|
|
15
15
|
installDependency(slug: string, version?: string, filePath?: string, type?: RegistryType): Promise<any>;
|
|
16
16
|
installDependencies(filePath: string, type?: RegistryType): Promise<any>;
|
|
17
17
|
open(slug: string, version?: string, options?: string[]): boolean;
|
|
18
|
-
uninstall(slug: string, version?: string): Promise<
|
|
18
|
+
uninstall(slug: string, version?: string): Promise<PackageVersion>;
|
|
19
19
|
uninstallDependency(slug: string, version?: string, filePath?: string, type?: RegistryType): Promise<any>;
|
|
20
20
|
uninstallDependencies(filePath?: string, type?: RegistryType): Promise<any>;
|
|
21
21
|
}
|
|
@@ -58,7 +58,10 @@ export class ManagerLocal extends Manager {
|
|
|
58
58
|
];
|
|
59
59
|
const pkgAnswers = await inquirer.prompt(pkgQuestions);
|
|
60
60
|
let types = pluginTypes;
|
|
61
|
-
if (this.type === RegistryType.
|
|
61
|
+
if (this.type === RegistryType.Apps) {
|
|
62
|
+
types = pluginTypes;
|
|
63
|
+
}
|
|
64
|
+
else if (this.type === RegistryType.Presets) {
|
|
62
65
|
types = presetTypes;
|
|
63
66
|
}
|
|
64
67
|
else if (this.type === RegistryType.Projects) {
|
|
@@ -161,16 +164,28 @@ export class ManagerLocal extends Manager {
|
|
|
161
164
|
// Get package information from registry.
|
|
162
165
|
const pkg = this.getPackage(slug);
|
|
163
166
|
if (!pkg)
|
|
164
|
-
|
|
167
|
+
throw new Error(`Package ${slug} not found in registry`);
|
|
165
168
|
const versionNum = version || pkg.latestVersion();
|
|
166
169
|
const pkgVersion = pkg?.getVersion(versionNum);
|
|
167
170
|
if (!pkgVersion)
|
|
168
|
-
|
|
171
|
+
throw new Error(`Package ${slug} version ${versionNum} not found in registry`);
|
|
169
172
|
if (this.isPackageInstalled(slug, versionNum)) {
|
|
170
173
|
this.log(`Package ${slug} version ${versionNum} already installed`);
|
|
171
174
|
pkgVersion.installed = true;
|
|
172
175
|
return pkgVersion;
|
|
173
176
|
}
|
|
177
|
+
// Check for compatible files before running admin command
|
|
178
|
+
const excludedFormats = [];
|
|
179
|
+
const system = getSystem();
|
|
180
|
+
if (system === SystemType.Linux) {
|
|
181
|
+
if (!(await commandExists('dpkg')))
|
|
182
|
+
excludedFormats.push(FileFormat.DebianPackage);
|
|
183
|
+
if (!(await commandExists('rpm')))
|
|
184
|
+
excludedFormats.push(FileFormat.RedHatPackage);
|
|
185
|
+
}
|
|
186
|
+
const files = packageCompatibleFiles(pkgVersion, [getArchitecture()], [getSystem()], excludedFormats);
|
|
187
|
+
if (!files.length)
|
|
188
|
+
throw new Error(`No compatible files found for ${slug}`);
|
|
174
189
|
// Elevate permissions if not running as admin.
|
|
175
190
|
if (!isAdmin() && !isTests()) {
|
|
176
191
|
let command = `--appDir "${this.config.get('appDir')}" --operation "install" --type "${this.type}" --id "${slug}"`;
|
|
@@ -191,19 +206,6 @@ export class ManagerLocal extends Manager {
|
|
|
191
206
|
// Create temporary directory to store downloaded files.
|
|
192
207
|
const dirDownloads = path.join(this.config.get('appDir'), 'downloads', this.type, slug, versionNum);
|
|
193
208
|
dirCreate(dirDownloads);
|
|
194
|
-
// Not all Linux distributions support all file formats.
|
|
195
|
-
const excludedFormats = [];
|
|
196
|
-
const system = getSystem();
|
|
197
|
-
if (system === SystemType.Linux) {
|
|
198
|
-
if (!(await commandExists('dpkg')))
|
|
199
|
-
excludedFormats.push(FileFormat.DebianPackage);
|
|
200
|
-
if (!(await commandExists('rpm')))
|
|
201
|
-
excludedFormats.push(FileFormat.RedHatPackage);
|
|
202
|
-
}
|
|
203
|
-
// Filter for compatible files and download.
|
|
204
|
-
const files = packageCompatibleFiles(pkgVersion, [getArchitecture()], [getSystem()], excludedFormats);
|
|
205
|
-
if (!files.length)
|
|
206
|
-
return this.log(`Error: No compatible files found for ${slug}`);
|
|
207
209
|
for (const key in files) {
|
|
208
210
|
// Download file to temporary directory if not already downloaded.
|
|
209
211
|
const file = files[key];
|
|
@@ -215,9 +217,10 @@ export class ManagerLocal extends Manager {
|
|
|
215
217
|
// Check file hash matches expected hash.
|
|
216
218
|
const hash = await fileHash(filePath);
|
|
217
219
|
if (hash !== file.sha256)
|
|
218
|
-
|
|
220
|
+
throw new Error(`${filePath} hash mismatch`);
|
|
219
221
|
// If installer, run the installer headless (without the user interface).
|
|
220
222
|
if (file.type === FileType.Installer) {
|
|
223
|
+
// Test time out if installing during tests.
|
|
221
224
|
if (isTests())
|
|
222
225
|
fileOpen(filePath);
|
|
223
226
|
else
|
|
@@ -234,9 +237,11 @@ export class ManagerLocal extends Manager {
|
|
|
234
237
|
const dirSource = path.join(this.config.get('appDir'), file.type, this.type, slug, versionNum);
|
|
235
238
|
const dirSub = path.join(slug, versionNum);
|
|
236
239
|
let formatDir = pluginFormatDir;
|
|
237
|
-
if (this.type === RegistryType.
|
|
240
|
+
if (this.type === RegistryType.Apps)
|
|
241
|
+
formatDir = pluginFormatDir;
|
|
242
|
+
else if (this.type === RegistryType.Presets)
|
|
238
243
|
formatDir = presetFormatDir;
|
|
239
|
-
if (this.type === RegistryType.Projects)
|
|
244
|
+
else if (this.type === RegistryType.Projects)
|
|
240
245
|
formatDir = projectFormatDir;
|
|
241
246
|
await archiveExtract(filePath, dirSource);
|
|
242
247
|
// Move entire directory, maintaining the same folder structure.
|
|
@@ -319,41 +324,49 @@ export class ManagerLocal extends Manager {
|
|
|
319
324
|
// Get package information
|
|
320
325
|
const pkg = this.getPackage(slug);
|
|
321
326
|
if (!pkg) {
|
|
322
|
-
|
|
323
|
-
return false;
|
|
327
|
+
throw new Error(`Package ${slug} not found`);
|
|
324
328
|
}
|
|
325
329
|
const versionNum = version || pkg.latestVersion();
|
|
326
330
|
const pkgVersion = pkg.getVersion(versionNum);
|
|
327
331
|
if (!pkgVersion) {
|
|
328
|
-
|
|
329
|
-
return false;
|
|
332
|
+
throw new Error(`Package ${slug} version ${versionNum} not found`);
|
|
330
333
|
}
|
|
331
334
|
// Check if package is installed
|
|
332
335
|
if (!this.isPackageInstalled(slug, versionNum)) {
|
|
333
|
-
|
|
334
|
-
return false;
|
|
336
|
+
throw new Error(`Package ${slug} version ${versionNum} not installed`);
|
|
335
337
|
}
|
|
336
338
|
// Filter compatible files and find one with open field
|
|
337
339
|
const files = packageCompatibleFiles(pkgVersion, [getArchitecture()], [getSystem()], []);
|
|
338
340
|
const openableFile = files.find(file => file.open);
|
|
339
341
|
if (!openableFile) {
|
|
340
|
-
|
|
341
|
-
return false;
|
|
342
|
+
throw new Error(`Package ${slug} has no compatible file with open command defined`);
|
|
342
343
|
}
|
|
343
344
|
try {
|
|
344
345
|
const openPath = openableFile.open;
|
|
345
|
-
const fileExt = path.extname(
|
|
346
|
-
|
|
346
|
+
const fileExt = path.extname(openPath).slice(1).toLowerCase();
|
|
347
|
+
let formatDir = pluginFormatDir[fileExt] || 'Plugin';
|
|
348
|
+
if (this.type === RegistryType.Apps)
|
|
349
|
+
formatDir = pluginFormatDir[fileExt] || 'App';
|
|
350
|
+
else if (this.type === RegistryType.Presets)
|
|
351
|
+
formatDir = presetFormatDir[fileExt] || 'Preset';
|
|
352
|
+
else if (this.type === RegistryType.Projects)
|
|
353
|
+
formatDir = projectFormatDir[fileExt] || 'Project';
|
|
347
354
|
const packageDir = path.join(this.typeDir, formatDir, slug, versionNum);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
355
|
+
let fullPath;
|
|
356
|
+
if (path.isAbsolute(openPath)) {
|
|
357
|
+
fullPath = openPath;
|
|
358
|
+
}
|
|
359
|
+
else if (fileExt === 'app') {
|
|
360
|
+
// For .app bundles, construct path to executable inside Contents/MacOS/
|
|
361
|
+
const appName = path.basename(openPath, '.app');
|
|
362
|
+
fullPath = path.join(packageDir, openPath, 'Contents', 'MacOS', appName);
|
|
353
363
|
}
|
|
354
364
|
else {
|
|
355
|
-
|
|
365
|
+
fullPath = path.join(packageDir, openPath);
|
|
356
366
|
}
|
|
367
|
+
const command = `"${fullPath}" ${options.join(' ')}`;
|
|
368
|
+
this.log(`Running: ${command}`);
|
|
369
|
+
fileOpen(fullPath, options);
|
|
357
370
|
return true;
|
|
358
371
|
}
|
|
359
372
|
catch (error) {
|
|
@@ -365,13 +378,13 @@ export class ManagerLocal extends Manager {
|
|
|
365
378
|
// Get package information from registry.
|
|
366
379
|
const pkg = this.getPackage(slug);
|
|
367
380
|
if (!pkg)
|
|
368
|
-
|
|
381
|
+
throw new Error(`Package ${slug} not found in registry`);
|
|
369
382
|
const versionNum = version || pkg.latestVersion();
|
|
370
383
|
const pkgVersion = pkg?.getVersion(versionNum);
|
|
371
384
|
if (!pkgVersion)
|
|
372
|
-
|
|
385
|
+
throw new Error(`Package ${slug} version ${versionNum} not found in registry`);
|
|
373
386
|
if (!this.isPackageInstalled(slug, versionNum))
|
|
374
|
-
|
|
387
|
+
throw new Error(`Package ${slug} version ${versionNum} not installed`);
|
|
375
388
|
// Elevate permissions if not running as admin.
|
|
376
389
|
if (!isAdmin() && !isTests()) {
|
|
377
390
|
let command = `--appDir "${this.config.get('appDir')}" --operation "uninstall" --type "${this.type}" --id "${slug}"`;
|
package/build/helpers/file.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export declare function fileHash(filePath: string, algorithm?: string): Promise<
|
|
|
33
33
|
export declare function fileInstall(filePath: string): Buffer<ArrayBufferLike>;
|
|
34
34
|
export declare function fileMove(filePath: string, newPath: string): void | boolean;
|
|
35
35
|
export declare function filesMove(dirSource: string, dirTarget: string, dirSub: string, formatDir: Record<string, string>): string[];
|
|
36
|
-
export declare function fileOpen(filePath: string): Buffer<ArrayBufferLike>;
|
|
36
|
+
export declare function fileOpen(filePath: string, options?: string[]): import("child_process").ChildProcess | Buffer<ArrayBufferLike>;
|
|
37
37
|
export declare function fileRead(filePath: string): string;
|
|
38
38
|
export declare function fileReadJson(filePath: string): any;
|
|
39
39
|
export declare function fileReadString(filePath: string): string;
|
package/build/helpers/file.js
CHANGED
|
@@ -247,8 +247,9 @@ export function fileMove(filePath, newPath) {
|
|
|
247
247
|
return false;
|
|
248
248
|
}
|
|
249
249
|
export function filesMove(dirSource, dirTarget, dirSub, formatDir) {
|
|
250
|
-
// Read files from source directory, ignoring Mac Contents files.
|
|
251
|
-
const
|
|
250
|
+
// Read all files from source directory, ignoring Mac Contents files.
|
|
251
|
+
const allFiles = dirRead(`${dirSource}/**/*`);
|
|
252
|
+
const files = allFiles.filter(f => !dirIs(f));
|
|
252
253
|
const filesMoved = [];
|
|
253
254
|
// For each file, move to correct folder based on type
|
|
254
255
|
files.forEach((fileSource) => {
|
|
@@ -256,24 +257,46 @@ export function filesMove(dirSource, dirTarget, dirSub, formatDir) {
|
|
|
256
257
|
const fileExtTarget = formatDir[fileExt];
|
|
257
258
|
// If this is not a supported file format, then ignore.
|
|
258
259
|
if (fileExtTarget === undefined)
|
|
259
|
-
return log(`${fileSource} - ${fileExt} not mapped to a installation folder, skipping.`);
|
|
260
|
+
return log(`${fileSource} - ${fileExt || 'no extension'} not mapped to a installation folder, skipping.`);
|
|
260
261
|
const fileTarget = path.join(dirTarget, fileExtTarget, dirSub, path.basename(fileSource));
|
|
261
262
|
if (fileExists(fileTarget))
|
|
262
263
|
return log(`${fileSource} - ${fileTarget} already exists, skipping.`);
|
|
263
264
|
dirCreate(path.dirname(fileTarget));
|
|
264
265
|
fileMove(fileSource, fileTarget);
|
|
266
|
+
// Set executable permissions for executable file types
|
|
267
|
+
if (fileExt === 'app') {
|
|
268
|
+
// For .app bundles, find and set permissions on the actual executable
|
|
269
|
+
const executablePath = path.join(fileTarget, 'Contents', 'MacOS', path.basename(fileTarget, '.app'));
|
|
270
|
+
if (fileExists(executablePath)) {
|
|
271
|
+
fileExec(executablePath);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
else if (['elf', 'exe', ''].includes(fileExt)) {
|
|
275
|
+
fileExec(fileTarget);
|
|
276
|
+
}
|
|
265
277
|
filesMoved.push(fileTarget);
|
|
266
278
|
});
|
|
267
279
|
return filesMoved;
|
|
268
280
|
}
|
|
269
|
-
export function fileOpen(filePath) {
|
|
270
|
-
let command = '';
|
|
281
|
+
export function fileOpen(filePath, options = []) {
|
|
271
282
|
if (process.env.CI)
|
|
272
283
|
return Buffer.from('');
|
|
284
|
+
if (getSystem() === SystemType.Mac) {
|
|
285
|
+
const isExecutable = !path.extname(filePath);
|
|
286
|
+
if (isExecutable) {
|
|
287
|
+
// Use spawn for executables with stdio inherit to show output
|
|
288
|
+
log('⎋', `spawn "${filePath}" ${options.join(' ')}`);
|
|
289
|
+
const child = spawn(filePath, options, { stdio: 'inherit' });
|
|
290
|
+
return child;
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
log('⎋', `open "${filePath}"`);
|
|
294
|
+
return execSync(`open "${filePath}"`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
let command = '';
|
|
273
298
|
if (getSystem() === SystemType.Win)
|
|
274
299
|
command = 'start ""';
|
|
275
|
-
else if (getSystem() === SystemType.Mac)
|
|
276
|
-
command = 'open';
|
|
277
300
|
else
|
|
278
301
|
command = 'xdg-open';
|
|
279
302
|
log('⎋', `${command} "${filePath}"`);
|
|
@@ -19,7 +19,9 @@ export declare enum PluginFormat {
|
|
|
19
19
|
export type PluginFormatDir = {
|
|
20
20
|
[format in PluginFormat]: string;
|
|
21
21
|
};
|
|
22
|
-
export declare const pluginFormatDir: PluginFormatDir
|
|
22
|
+
export declare const pluginFormatDir: PluginFormatDir & {
|
|
23
|
+
'': string;
|
|
24
|
+
};
|
|
23
25
|
export interface PluginFormatOption {
|
|
24
26
|
description: string;
|
|
25
27
|
value: PluginFormat;
|