@open-audio-stack/core 0.1.37 → 0.1.39

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.
@@ -6,6 +6,7 @@ import { ConfigInterface } from '../types/Config.js';
6
6
  export declare class ManagerLocal extends Manager {
7
7
  protected typeDir: string;
8
8
  constructor(type: RegistryType, config?: ConfigInterface);
9
+ isPackageInstalled(slug: string, version: string): boolean;
9
10
  create(): Promise<void>;
10
11
  scan(ext?: string, installable?: boolean): void;
11
12
  export(dir: string, ext?: string): boolean;
@@ -13,7 +14,7 @@ export declare class ManagerLocal extends Manager {
13
14
  installAll(): Promise<Package[]>;
14
15
  installDependency(slug: string, version?: string, filePath?: string, type?: RegistryType): Promise<any>;
15
16
  installDependencies(filePath: string, type?: RegistryType): Promise<any>;
16
- open(filePath: string): void;
17
+ open(slug: string, version?: string, options?: string[]): boolean;
17
18
  uninstall(slug: string, version?: string): Promise<void | PackageVersion>;
18
19
  uninstallDependency(slug: string, version?: string, filePath?: string, type?: RegistryType): Promise<any>;
19
20
  uninstallDependencies(filePath?: string, type?: RegistryType): Promise<any>;
@@ -27,6 +27,10 @@ export class ManagerLocal extends Manager {
27
27
  this.config = new ConfigLocal(config);
28
28
  this.typeDir = this.config.get(`${type}Dir`);
29
29
  }
30
+ isPackageInstalled(slug, version) {
31
+ const versionDirs = dirRead(path.join(this.typeDir, '**', slug, version));
32
+ return versionDirs.length > 0;
33
+ }
30
34
  async create() {
31
35
  // TODO Rewrite this code after prototype is proven.
32
36
  const pkgQuestions = [
@@ -165,8 +169,9 @@ export class ManagerLocal extends Manager {
165
169
  const pkgVersion = pkg?.getVersion(versionNum);
166
170
  if (!pkgVersion)
167
171
  return this.log(`Package ${slug} version ${versionNum} not found in registry`);
168
- if (pkgVersion.installed) {
172
+ if (this.isPackageInstalled(slug, versionNum)) {
169
173
  this.log(`Package ${slug} version ${versionNum} already installed`);
174
+ pkgVersion.installed = true;
170
175
  return pkgVersion;
171
176
  }
172
177
  // Elevate permissions if not running as admin.
@@ -177,7 +182,14 @@ export class ManagerLocal extends Manager {
177
182
  if (this.debug)
178
183
  command += ` --log`;
179
184
  await runCliAsAdmin(command);
180
- return this.getPackage(slug)?.getVersion(versionNum);
185
+ const returnedPkg = this.getPackage(slug)?.getVersion(versionNum);
186
+ if (returnedPkg) {
187
+ if (this.isPackageInstalled(slug, versionNum))
188
+ returnedPkg.installed = true;
189
+ else
190
+ delete returnedPkg.installed;
191
+ return returnedPkg;
192
+ }
181
193
  }
182
194
  // Create temporary directory to store downloaded files.
183
195
  const dirDownloads = path.join(this.config.get('appDir'), 'downloads', this.type, slug, versionNum);
@@ -305,11 +317,47 @@ export class ManagerLocal extends Manager {
305
317
  pkgFile.installed = true;
306
318
  return pkgFile;
307
319
  }
308
- open(filePath) {
309
- const pkgFile = packageLoadFile(filePath);
310
- // If installer, run the installer.
311
- if (pkgFile.open) {
312
- fileOpen(pkgFile.open);
320
+ open(slug, version, options = []) {
321
+ this.log('open', slug, version, options);
322
+ // Get package information
323
+ const pkg = this.getPackage(slug);
324
+ if (!pkg) {
325
+ this.log(`Package ${slug} not found`);
326
+ return false;
327
+ }
328
+ const versionNum = version || pkg.latestVersion();
329
+ const pkgVersion = pkg.getVersion(versionNum);
330
+ if (!pkgVersion) {
331
+ this.log(`Package ${slug} version ${versionNum} not found`);
332
+ return false;
333
+ }
334
+ // Check if package has open field
335
+ if (!pkgVersion.open) {
336
+ this.log(`Package ${slug} has no open command defined`);
337
+ return false;
338
+ }
339
+ // Check if package is installed
340
+ if (!this.isPackageInstalled(slug, versionNum)) {
341
+ this.log(`Package ${slug} version ${versionNum} not installed`);
342
+ return false;
343
+ }
344
+ try {
345
+ const openPath = pkgVersion.open;
346
+ const packageDir = path.join(this.typeDir, slug, versionNum);
347
+ const fullPath = path.isAbsolute(openPath) ? openPath : path.join(packageDir, openPath);
348
+ const command = `"${fullPath}" ${options.join(' ')}`;
349
+ this.log(`Running: ${command}`);
350
+ if (isTests()) {
351
+ this.log(`Would run: ${command}`);
352
+ }
353
+ else {
354
+ fileOpen(fullPath);
355
+ }
356
+ return true;
357
+ }
358
+ catch (error) {
359
+ this.log(`Error opening package ${slug}:`, error);
360
+ return false;
313
361
  }
314
362
  }
315
363
  async uninstall(slug, version) {
@@ -321,7 +369,7 @@ export class ManagerLocal extends Manager {
321
369
  const pkgVersion = pkg?.getVersion(versionNum);
322
370
  if (!pkgVersion)
323
371
  return this.log(`Package ${slug} version ${versionNum} not found in registry`);
324
- if (!pkgVersion.installed)
372
+ if (!this.isPackageInstalled(slug, versionNum))
325
373
  return this.log(`Package ${slug} version ${versionNum} not installed`);
326
374
  // Elevate permissions if not running as admin.
327
375
  if (!isAdmin() && !isTests()) {
@@ -331,7 +379,14 @@ export class ManagerLocal extends Manager {
331
379
  if (this.debug)
332
380
  command += ` --log`;
333
381
  await runCliAsAdmin(command);
334
- return this.getPackage(slug)?.getVersion(versionNum);
382
+ const returnedPkg = this.getPackage(slug)?.getVersion(versionNum);
383
+ if (returnedPkg) {
384
+ if (this.isPackageInstalled(slug, versionNum))
385
+ returnedPkg.installed = true;
386
+ else
387
+ delete returnedPkg.installed;
388
+ return returnedPkg;
389
+ }
335
390
  }
336
391
  // Delete all directories for this package version.
337
392
  const versionDirs = dirRead(path.join(this.typeDir, '**', slug, versionNum));
@@ -51,5 +51,6 @@ export async function adminInit() {
51
51
  else if (args.operation === 'installAll') {
52
52
  await manager.installAll();
53
53
  }
54
+ console.log('ADMIN_COMPLETE');
54
55
  }
55
56
  adminInit();
@@ -1,9 +1,10 @@
1
1
  import { configDefaults } from './config.js';
2
- import { dirApp, dirPlugins, dirPresets, dirProjects } from './file.js';
2
+ import { dirApp, dirApps, dirPlugins, dirPresets, dirProjects } from './file.js';
3
3
  export function configDefaultsLocal() {
4
4
  return {
5
5
  ...configDefaults(),
6
6
  appDir: dirApp(),
7
+ appsDir: dirApps(),
7
8
  pluginsDir: dirPlugins(),
8
9
  presetsDir: dirPresets(),
9
10
  projectsDir: dirProjects(),
@@ -14,11 +14,12 @@ export declare function dirEmpty(dir: string): boolean;
14
14
  export declare function dirExists(dir: string): boolean;
15
15
  export declare function dirIs(dir: string): boolean;
16
16
  export declare function dirMove(dir: string, dirNew: string): void | boolean;
17
- export declare function dirOpen(dir: string): Buffer;
17
+ export declare function dirOpen(dir: string): Buffer<ArrayBufferLike>;
18
18
  export declare function dirPackage(pkg: PackageInterface): string;
19
19
  export declare function dirPlugins(): string;
20
20
  export declare function dirPresets(): string;
21
21
  export declare function dirProjects(): string;
22
+ export declare function dirApps(): string;
22
23
  export declare function dirRead(dir: string, options?: GlobOptionsWithFileTypesFalse): string[];
23
24
  export declare function dirRename(dir: string, dirNew: string): void | boolean;
24
25
  export declare function fileCreate(filePath: string, data: string | Buffer): void;
@@ -29,10 +30,10 @@ export declare function fileDelete(filePath: string): boolean | void;
29
30
  export declare function fileExec(filePath: string): void;
30
31
  export declare function fileExists(filePath: string): boolean;
31
32
  export declare function fileHash(filePath: string, algorithm?: string): Promise<string>;
32
- export declare function fileInstall(filePath: string): Buffer;
33
+ export declare function fileInstall(filePath: string): Buffer<ArrayBufferLike>;
33
34
  export declare function fileMove(filePath: string, newPath: string): void | boolean;
34
35
  export declare function filesMove(dirSource: string, dirTarget: string, dirSub: string, formatDir: Record<string, string>): string[];
35
- export declare function fileOpen(filePath: string): Buffer;
36
+ export declare function fileOpen(filePath: string): Buffer<ArrayBufferLike>;
36
37
  export declare function fileRead(filePath: string): string;
37
38
  export declare function fileReadJson(filePath: string): any;
38
39
  export declare function fileReadString(filePath: string): string;
@@ -21,7 +21,28 @@ export async function archiveExtract(filePath, dirPath) {
21
21
  const ext = path.extname(filePath).trim().toLowerCase();
22
22
  if (ext === '.zip') {
23
23
  const zip = new AdmZip(filePath);
24
- return zip.extractAllTo(dirPath);
24
+ try {
25
+ return zip.extractAllTo(dirPath);
26
+ }
27
+ catch (error) {
28
+ // Handle Windows special character issues by extracting files manually
29
+ if (getSystem() === SystemType.Win && error.message?.includes('ENOENT')) {
30
+ log('⚠️', 'Extracting files manually due to special characters in filenames');
31
+ const entries = zip.getEntries();
32
+ entries.forEach(entry => {
33
+ const sanitizedName = entry.entryName.replace(/[<>:"|?*]/g, '_').replace(/[\r\n]/g, '');
34
+ if (!entry.isDirectory) {
35
+ const outputPath = path.join(dirPath, sanitizedName);
36
+ dirCreate(path.dirname(outputPath));
37
+ writeFileSync(outputPath, entry.getData());
38
+ }
39
+ else {
40
+ dirCreate(path.join(dirPath, sanitizedName));
41
+ }
42
+ });
43
+ return;
44
+ }
45
+ }
25
46
  }
26
47
  else if (ext === '.tar' || ext === '.gz' || ext === '.tgz') {
27
48
  return await tar.extract({
@@ -120,6 +141,13 @@ export function dirProjects() {
120
141
  return path.join(os.homedir(), 'Documents', 'Audio');
121
142
  return path.join(os.homedir(), 'Documents', 'Audio');
122
143
  }
144
+ export function dirApps() {
145
+ if (getSystem() === SystemType.Win)
146
+ return path.join(os.homedir(), 'AppData', 'Local', 'Programs');
147
+ else if (getSystem() === SystemType.Mac)
148
+ return path.join('/Applications');
149
+ return path.join('/usr', 'local', 'bin');
150
+ }
123
151
  export function dirRead(dir, options) {
124
152
  log('⌕', dir);
125
153
  // Glob now expects forward slashes on Windows
@@ -326,22 +354,24 @@ export function runCliAsAdmin(args) {
326
354
  const logFile = path.join(dirPathClean, 'admin.log');
327
355
  writeFileSync(logFile, ''); // Clear previous logs
328
356
  log(`Running as admin: node "${script}" ${args}`);
329
- // Tail the log file in real-time
357
+ // Monitor log file for completion
358
+ let completed = false;
330
359
  const tail = spawn('tail', ['-f', logFile]);
331
- const grep = spawn('grep', ['.']);
332
- tail.stdout.pipe(grep.stdin);
333
- grep.stdout.on('data', data => {
334
- log(data.toString());
360
+ tail.stdout.on('data', data => {
361
+ const output = data.toString();
362
+ log(output);
363
+ if (output.includes('ADMIN_COMPLETE')) {
364
+ completed = true;
365
+ tail.kill();
366
+ resolve();
367
+ }
335
368
  });
336
369
  // Run the script with sudoPrompt
337
370
  sudoPrompt.exec(`node "${script}" ${args} >> "${logFile}" 2>&1`, { name: 'Open Audio Stack' }, error => {
338
- tail.kill();
339
- if (error) {
371
+ if (error && !completed) {
372
+ tail.kill();
340
373
  reject(error);
341
374
  }
342
- else {
343
- resolve();
344
- }
345
375
  });
346
376
  });
347
377
  }
@@ -1,5 +1,6 @@
1
1
  export interface ConfigInterface {
2
2
  appDir?: string;
3
+ appsDir?: string;
3
4
  pluginsDir?: string;
4
5
  presetsDir?: string;
5
6
  projectsDir?: string;
@@ -3,11 +3,13 @@ export interface RegistryInterface {
3
3
  name: string;
4
4
  url: string;
5
5
  version: string;
6
+ [RegistryType.Apps]?: RegistryPackages;
6
7
  [RegistryType.Plugins]?: RegistryPackages;
7
8
  [RegistryType.Presets]?: RegistryPackages;
8
9
  [RegistryType.Projects]?: RegistryPackages;
9
10
  }
10
11
  export declare enum RegistryType {
12
+ Apps = "apps",
11
13
  Plugins = "plugins",
12
14
  Presets = "presets",
13
15
  Projects = "projects"
@@ -1,5 +1,6 @@
1
1
  export var RegistryType;
2
2
  (function (RegistryType) {
3
+ RegistryType["Apps"] = "apps";
3
4
  RegistryType["Plugins"] = "plugins";
4
5
  RegistryType["Presets"] = "presets";
5
6
  RegistryType["Projects"] = "projects";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-audio-stack/core",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "Open-source audio plugin management software",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",
@@ -49,8 +49,8 @@
49
49
  "globals": "^15.2.0",
50
50
  "prettier": "^3.2.5",
51
51
  "tsx": "^4.19.2",
52
- "typescript": "^5.6.3",
53
- "typescript-eslint": "^8.9.0",
52
+ "typescript": "^5.9.3",
53
+ "typescript-eslint": "^8.48.0",
54
54
  "vitest": "^3.0.5"
55
55
  },
56
56
  "dependencies": {