@hybridlabor-api/aos 4.7.2 → 4.8.0

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.
Files changed (2) hide show
  1. package/installer.js +139 -59
  2. package/package.json +1 -1
package/installer.js CHANGED
@@ -1709,7 +1709,8 @@ function checkModuleUpdate(pkgName, targetDir) {
1709
1709
  let remoteVer = null;
1710
1710
  let checkFailed = false;
1711
1711
  try {
1712
- remoteVer = execSync(`npm view ${pkgName} version`, { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', timeout: 4000 }).trim();
1712
+ const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
1713
+ remoteVer = execFileSync(npmBin, ['view', pkgName, 'version'], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', timeout: 4000, shell: process.platform === 'win32' }).trim();
1713
1714
  } catch (e) {
1714
1715
  // Offline, a proxy, or a misconfigured registry. Swallowing this
1715
1716
  // made every module report "up to date" forever on such a machine,
@@ -2050,13 +2051,23 @@ async function installSynapse() {
2050
2051
  }
2051
2052
 
2052
2053
  const isWin = process.platform === 'win32';
2053
- const binaryName = isWin ? 'synapse.js' : 'synapse';
2054
- let binaryPath = path.join(synapseDir, 'bin', binaryName);
2055
- if (!fs.existsSync(binaryPath)) {
2056
- if (process.platform === 'darwin' && fs.existsSync(path.join(synapseDir, 'bin', 'synapse-darwin-arm64'))) {
2057
- binaryPath = path.join(synapseDir, 'bin', 'synapse-darwin-arm64');
2058
- } else if (process.platform === 'linux' && fs.existsSync(path.join(synapseDir, 'bin', 'synapse-linux-amd64'))) {
2059
- binaryPath = path.join(synapseDir, 'bin', 'synapse-linux-amd64');
2054
+ let binaryPath;
2055
+ if (isWin) {
2056
+ if (fs.existsSync(path.join(synapseDir, 'bin', 'synapse.exe'))) {
2057
+ binaryPath = path.join(synapseDir, 'bin', 'synapse.exe');
2058
+ } else if (fs.existsSync(path.join(synapseDir, 'bin', 'synapse-windows-amd64.exe'))) {
2059
+ binaryPath = path.join(synapseDir, 'bin', 'synapse-windows-amd64.exe');
2060
+ } else {
2061
+ binaryPath = path.join(synapseDir, 'bin', 'synapse.js');
2062
+ }
2063
+ } else {
2064
+ binaryPath = path.join(synapseDir, 'bin', 'synapse');
2065
+ if (!fs.existsSync(binaryPath)) {
2066
+ if (process.platform === 'darwin' && fs.existsSync(path.join(synapseDir, 'bin', 'synapse-darwin-arm64'))) {
2067
+ binaryPath = path.join(synapseDir, 'bin', 'synapse-darwin-arm64');
2068
+ } else if (process.platform === 'linux' && fs.existsSync(path.join(synapseDir, 'bin', 'synapse-linux-amd64'))) {
2069
+ binaryPath = path.join(synapseDir, 'bin', 'synapse-linux-amd64');
2070
+ }
2060
2071
  }
2061
2072
  }
2062
2073
 
@@ -2406,75 +2417,144 @@ async function installDevToolInstaller() {
2406
2417
  }
2407
2418
  }
2408
2419
 
2409
- // AO ships one prebuilt binary: backend/ao-daemon, Mach-O arm64. The package
2410
- // carries Windows and Linux sources but nothing built for them, and its CLI
2411
- // shim picks the first candidate path that EXISTS without checking whether it
2412
- // can run — so on any other platform it hands spawn a Mach-O file and fails
2413
- // with an exec-format error instead of the "go build" hint it means to give.
2414
- // Installing it there would be installing something known not to work.
2420
+ // AO is fully supported across macOS (arm64, amd64), Linux (amd64, arm64), and Windows (amd64).
2415
2421
  function aoSupportedHere() {
2416
- return process.platform === 'darwin' && process.arch === 'arm64';
2422
+ return ['darwin', 'linux', 'win32'].includes(process.platform);
2417
2423
  }
2418
2424
 
2419
2425
  async function installOSAgentWorkspace() {
2420
2426
  if (!aoSupportedHere()) {
2421
- log.warn(`AO ships only a macOS arm64 binary — skipping on ${process.platform}/${process.arch}.`);
2422
- log.warn('Build it from source instead: github.com/hybridlabor-api/bdb-agent-orchestrator');
2427
+ log.warn(`AO is not supported on ${process.platform}/${process.arch}.`);
2423
2428
  return;
2424
2429
  }
2425
2430
 
2426
- const osAgentDir = path.join(moduleBasePath(), 'bdb-agent-orchestrator');
2427
- if (!downloadOrUpdateModule('@hybridlabor-api/bdb-agent-orchestrator', osAgentDir, 'BDB Agent Orchestrator')) {
2428
- log.warn('Skipping AO setup: the module could not be downloaded.');
2429
- return;
2431
+ const isWin = process.platform === 'win32';
2432
+ const isMac = process.platform === 'darwin';
2433
+ const isLinux = process.platform === 'linux';
2434
+
2435
+ // Discover AO directory: check local development checkouts first, then moduleBasePath
2436
+ const candidateDirs = [
2437
+ path.join(homeDir, 'dev', 'agents', 'bdb-agent-orchestrator'),
2438
+ path.join(homeDir, 'dev', 'bdb-dev', 'bdb-agent-orchestrator'),
2439
+ path.join(moduleBasePath(), 'bdb-agent-orchestrator')
2440
+ ];
2441
+ const osAgentDir = candidateDirs.find(d => fs.existsSync(d)) || candidateDirs[candidateDirs.length - 1];
2442
+ const isLocalGitRepo = fs.existsSync(path.join(osAgentDir, '.git'));
2443
+
2444
+ if (isLocalGitRepo) {
2445
+ log.step(`Using local development workspace for AO: ${osAgentDir}`);
2446
+ } else {
2447
+ if (!downloadOrUpdateModule('@hybridlabor-api/bdb-agent-orchestrator', osAgentDir, 'BDB Agent Orchestrator')) {
2448
+ log.warn('Skipping AO setup: the module could not be downloaded.');
2449
+ return;
2450
+ }
2430
2451
  }
2431
2452
  if (DRY_RUN) {
2432
- log.step('[dry-run] would link ~/.local/bin/ao and run `ao service install`');
2453
+ log.step('[dry-run] would link ao binary and run `ao service install`');
2433
2454
  return;
2434
2455
  }
2435
2456
 
2436
- const localBinDir = path.join(homeDir, '.local', 'bin');
2437
- const binTarget = path.join(localBinDir, 'ao');
2438
- const daemonBin = path.join(osAgentDir, 'backend', 'ao-daemon');
2457
+ const localBinDir = isWin
2458
+ ? path.join(process.env.LOCALAPPDATA || homeDir, 'Programs', 'ao')
2459
+ : path.join(homeDir, '.local', 'bin');
2460
+ const binTarget = path.join(localBinDir, isWin ? 'ao.exe' : 'ao');
2461
+
2462
+ // Platform-specific binary lookup
2463
+ const candidateBinaries = [];
2464
+ if (isWin) {
2465
+ candidateBinaries.push(
2466
+ path.join(osAgentDir, 'backend', 'bin', 'ao-windows-amd64.exe'),
2467
+ path.join(osAgentDir, 'backend', 'bin', 'ao.exe'),
2468
+ path.join(osAgentDir, 'backend', 'ao.exe')
2469
+ );
2470
+ } else if (isMac) {
2471
+ if (process.arch === 'arm64') {
2472
+ candidateBinaries.push(path.join(osAgentDir, 'backend', 'bin', 'ao-darwin-arm64'));
2473
+ } else {
2474
+ candidateBinaries.push(path.join(osAgentDir, 'backend', 'bin', 'ao-darwin-amd64'));
2475
+ }
2476
+ candidateBinaries.push(
2477
+ path.join(osAgentDir, 'backend', 'ao-daemon'),
2478
+ path.join(osAgentDir, 'backend', 'bin', 'ao'),
2479
+ path.join(osAgentDir, 'backend', 'ao')
2480
+ );
2481
+ } else if (isLinux) {
2482
+ if (process.arch === 'arm64') {
2483
+ candidateBinaries.push(path.join(osAgentDir, 'backend', 'bin', 'ao-linux-arm64'));
2484
+ } else {
2485
+ candidateBinaries.push(path.join(osAgentDir, 'backend', 'bin', 'ao-linux-amd64'));
2486
+ }
2487
+ candidateBinaries.push(
2488
+ path.join(osAgentDir, 'backend', 'ao-daemon'),
2489
+ path.join(osAgentDir, 'backend', 'bin', 'ao'),
2490
+ path.join(osAgentDir, 'backend', 'ao')
2491
+ );
2492
+ }
2493
+ let daemonBin = candidateBinaries.find(p => fs.existsSync(p));
2439
2494
 
2440
- if (!fs.existsSync(daemonBin)) {
2441
- log.warn(`AO binary missing at ${daemonBin} — package layout changed?`);
2495
+ // Fallback: compile from Go source if Go is installed and binary is not pre-built
2496
+ if (!daemonBin && hasExecutable('go') && fs.existsSync(path.join(osAgentDir, 'backend', 'cmd', 'ao'))) {
2497
+ const s = spinner();
2498
+ s.start('Compiling AO daemon binary from Go source...');
2499
+ try {
2500
+ const buildTarget = path.join(osAgentDir, 'backend', 'bin', isWin ? 'ao.exe' : 'ao');
2501
+ fs.mkdirSync(path.dirname(buildTarget), { recursive: true });
2502
+ execFileSync('go', ['build', '-ldflags=-s -w', '-o', buildTarget, './cmd/ao'], {
2503
+ cwd: path.join(osAgentDir, 'backend'),
2504
+ stdio: 'ignore'
2505
+ });
2506
+ daemonBin = buildTarget;
2507
+ s.stop('Compiled AO daemon binary successfully.');
2508
+ } catch (e) {
2509
+ s.stop(`Failed to compile AO from source: ${e.message}`);
2510
+ }
2511
+ }
2512
+
2513
+ // Fallback: if already installed and functional, keep it
2514
+ if (!daemonBin && fs.existsSync(binTarget)) {
2515
+ try {
2516
+ execFileSync(binTarget, ['--version'], { stdio: 'ignore' });
2517
+ log.step(`Keeping existing working AO binary at ${binTarget}`);
2518
+ daemonBin = binTarget;
2519
+ } catch (_) {}
2520
+ }
2521
+
2522
+ if (!daemonBin) {
2523
+ log.warn(`AO binary missing in ${osAgentDir} — package layout changed or Go build needed.`);
2442
2524
  return;
2443
2525
  }
2444
2526
 
2445
- installStep('place the ao binary', () => {
2446
- fs.mkdirSync(localBinDir, { recursive: true });
2447
- // copyFileSync preserves bytes, so the package's ad-hoc signature
2448
- // survives. That matters: an unsigned binary at this path is SIGKILLed
2449
- // by AMFI on launch (exit 137) and reads as a daemon that simply will
2450
- // not start, with nothing in the log to say why.
2451
- fs.copyFileSync(daemonBin, binTarget);
2452
- fs.chmodSync(binTarget, 0o755);
2453
- log.step(`Installed ao to ${binTarget}`);
2454
- }, 'AO cannot be started without its binary.');
2527
+ if (daemonBin !== binTarget) {
2528
+ installStep('place the ao binary', () => {
2529
+ fs.mkdirSync(localBinDir, { recursive: true });
2530
+ fs.copyFileSync(daemonBin, binTarget);
2531
+ if (!isWin) {
2532
+ fs.chmodSync(binTarget, 0o755);
2533
+ }
2534
+ log.step(`Installed ao to ${binTarget}`);
2535
+ }, 'AO cannot be started without its binary.');
2536
+ }
2455
2537
 
2456
- try {
2457
- execFileSync('codesign', ['-v', binTarget], { stdio: 'ignore' });
2458
- } catch {
2459
- log.warn('The ao binary is not code-signed — AMFI will kill it at launch.');
2538
+ // macOS only: code-sign ad-hoc to prevent AMFI SIGKILL
2539
+ if (isMac) {
2460
2540
  try {
2461
- execFileSync('codesign', ['-s', '-', '-f', binTarget], { stdio: 'ignore' });
2462
- log.step('Signed it ad-hoc.');
2463
- } catch (e) { log.warn(`Could not sign it: ${e.message}`); }
2541
+ execFileSync('codesign', ['-v', binTarget], { stdio: 'ignore' });
2542
+ } catch {
2543
+ log.warn('The ao binary is not code-signed — AMFI will kill it at launch.');
2544
+ try {
2545
+ execFileSync('codesign', ['-s', '-', '-f', binTarget], { stdio: 'ignore' });
2546
+ log.step('Signed it ad-hoc.');
2547
+ } catch (e) { log.warn(`Could not sign it: ${e.message}`); }
2548
+ }
2464
2549
  }
2465
2550
 
2466
2551
  // AO installs its own service, and does it for macOS, Windows and Linux.
2467
- // AOS used to hand-write a LaunchAgent instead, labelled
2468
- // com.bdb.agent-workspace, whose ProgramArguments were just the binary with
2469
- // NO subcommand -- so launchd started `ao`, which prints help and exits,
2470
- // and with KeepAlive kept restarting it. The daemon never ran, on any
2471
- // machine that installed AO through AOS. Delegating to the tool removes
2472
- // both the wrong label and the wrong arguments, and keeps working when the
2473
- // service definition changes upstream.
2474
- const legacyPlist = path.join(homeDir, 'Library', 'LaunchAgents', 'com.bdb.agent-workspace.plist');
2475
- if (fs.existsSync(legacyPlist)) {
2476
- try { execFileSync('launchctl', ['unload', legacyPlist], { stdio: 'ignore' }); } catch (e) { logDebug(e, 'unload legacy ao agent'); }
2477
- try { fs.unlinkSync(legacyPlist); log.step('Removed the old com.bdb.agent-workspace service.'); } catch (e) { logDebug(e, 'remove legacy plist'); }
2552
+ if (isMac) {
2553
+ const legacyPlist = path.join(homeDir, 'Library', 'LaunchAgents', 'com.bdb.agent-workspace.plist');
2554
+ if (fs.existsSync(legacyPlist)) {
2555
+ try { execFileSync('launchctl', ['unload', legacyPlist], { stdio: 'ignore' }); } catch (e) { logDebug(e, 'unload legacy ao agent'); }
2556
+ try { fs.unlinkSync(legacyPlist); log.step('Removed the old com.bdb.agent-workspace service.'); } catch (e) { logDebug(e, 'remove legacy plist'); }
2557
+ }
2478
2558
  }
2479
2559
 
2480
2560
  installStep('register the AO service', () => {
@@ -2534,7 +2614,7 @@ function verifyEcosystemInstallation() {
2534
2614
  { name: '1. bdb-synapse', pkg: '@hybridlabor-api/bdb-synapse', paths: [path.join(moduleBasePath(), 'bdb-synapse')] },
2535
2615
  { name: '2. memB', pkg: '@hybridlabor-api/memb', paths: [path.join(moduleBasePath(), 'memB'), path.join(geminiDir, 'config', 'mcps', 'memb-mcp')] },
2536
2616
  { name: '3. heimdall-token-saver', pkg: '@hybridlabor-api/heimdall-token-saver', paths: [path.join(moduleBasePath(), 'heimdall-token-saver'), path.join(srcDir, 'vendor', 'token-saver')] },
2537
- { name: '4. AO Agent Orchestrator', pkg: '@hybridlabor-api/bdb-agent-orchestrator', paths: [path.join(moduleBasePath(), 'bdb-agent-orchestrator')] },
2617
+ { name: '4. AO Agent Orchestrator', pkg: '@hybridlabor-api/bdb-agent-orchestrator', paths: [path.join(homeDir, 'dev', 'agents', 'bdb-agent-orchestrator'), path.join(moduleBasePath(), 'bdb-agent-orchestrator')] },
2538
2618
  { name: '5. bdb-dev-creator-extension', pkg: '@hybridlabor-api/bdb-dev-creator-extension', paths: [path.join(moduleBasePath(), 'bdb-dev-creator-extension')] },
2539
2619
  { name: '6. bdb-hardware-pcb', pkg: '@hybridlabor-api/bdb-hardware-pcb', paths: [path.join(moduleBasePath(), 'bdb-hardware-pcb')] },
2540
2620
  { name: '7. bdb-os-remote', pkg: '@hybridlabor-api/bdb-os-remote', paths: [path.join(moduleBasePath(), 'bdb-os-remote')] },
@@ -2558,7 +2638,8 @@ function verifyEcosystemInstallation() {
2558
2638
  let newerVersion = null;
2559
2639
  let newerTag = null;
2560
2640
  try {
2561
- const distTagsJson = execSync(`npm view ${mod.pkg} dist-tags --json`, { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', timeout: 4000 }).trim();
2641
+ const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
2642
+ const distTagsJson = execFileSync(npmBin, ['view', mod.pkg, 'dist-tags', '--json'], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', timeout: 4000, shell: process.platform === 'win32' }).trim();
2562
2643
  const distTags = JSON.parse(distTagsJson);
2563
2644
  for (const [tag, ver] of Object.entries(distTags)) {
2564
2645
  if (isNewerVersion(localVer, ver)) {
@@ -4795,8 +4876,7 @@ async function runQuickUpdate(installState) {
4795
4876
  if (subId === 'synapse') await installSynapse();
4796
4877
  else if (subId === 'memb') await installMemB(false);
4797
4878
  else if (subId === 'remote') await installOSRemoteGateway();
4798
- // 'ao' intentionally skipped here even for
4799
- // existing installs -- see promptOptionalModules() for why.
4879
+ else if (subId === 'ao' && aoSupportedHere()) await installOSAgentWorkspace();
4800
4880
  else if (subId === 'creator') await installCreatorExtension();
4801
4881
  else if (subId === 'hardware') await installHardwarePcb();
4802
4882
  else if (subId === 'installer') await installDevToolInstaller();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.7.2",
3
+ "version": "4.8.0",
4
4
  "description": "AOS — A Curated AI AGENT OS. Optimized agent skills and add-ons like memB, OpenWiki, Heimdall Token Saver, and Godmode architectures.",
5
5
  "main": "installer.js",
6
6
  "engines": {