@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.
@@ -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[];
@@ -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
- const packagesFiltered = [];
58
- Array.from(this.packages.values()).forEach(pkg => {
59
- Array.from(pkg.versions.values()).forEach(pkgVersion => {
60
- if ((installed === true && pkgVersion.installed) || (installed === false && !pkgVersion.installed)) {
61
- packagesFiltered.push(pkg);
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 Array.from(this.packages.values());
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<void | PackageVersion>;
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<void | PackageVersion>;
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.Presets) {
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
- return this.log(`Package ${slug} not found in registry`);
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
- return this.log(`Package ${slug} version ${versionNum} not found in registry`);
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
- return this.log(`Error: ${filePath} hash mismatch`);
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.Presets)
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
- this.log(`Package ${slug} not found`);
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
- this.log(`Package ${slug} version ${versionNum} not found`);
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
- this.log(`Package ${slug} version ${versionNum} not installed`);
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
- this.log(`Package ${slug} has no compatible file with open command defined`);
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(openableFile.url).slice(1).toLowerCase();
346
- const formatDir = pluginFormatDir[fileExt];
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
- const fullPath = path.isAbsolute(openPath) ? openPath : path.join(packageDir, openPath);
349
- const command = `"${fullPath}" ${options.join(' ')}`;
350
- this.log(`Running: ${command}`);
351
- if (isTests()) {
352
- this.log(`Would run: ${command}`);
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
- fileOpen(fullPath);
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
- return this.log(`Package ${slug} not found in registry`);
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
- return this.log(`Package ${slug} version ${versionNum} not found in registry`);
385
+ throw new Error(`Package ${slug} version ${versionNum} not found in registry`);
373
386
  if (!this.isPackageInstalled(slug, versionNum))
374
- return this.log(`Package ${slug} version ${versionNum} not installed`);
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}"`;
@@ -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;
@@ -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 files = dirRead(`${dirSource}/**/*.*`);
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;
@@ -34,6 +34,7 @@ export const pluginFormatDir = {
34
34
  [PluginFormat.VSTMac]: 'VST',
35
35
  [PluginFormat.VSTWin]: 'VST',
36
36
  [PluginFormat.WinStandalone]: 'Exe',
37
+ '': 'App',
37
38
  };
38
39
  export const pluginFormats = [
39
40
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-audio-stack/core",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "Open-source audio plugin management software",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",