@hybridlabor-api/aos 4.4.2-beta.8 → 4.4.2-beta.9

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/installer.js CHANGED
@@ -3348,6 +3348,9 @@ function generateAndOpenLaunchpad() {
3348
3348
  <meta charset="UTF-8">
3349
3349
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
3350
3350
  <title>BDB Agent OS – Launchpad</title>
3351
+ <link rel="preconnect" href="https://fonts.googleapis.com">
3352
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
3353
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
3351
3354
  <style>
3352
3355
  :root {
3353
3356
  --bg: #090a0f;
@@ -3355,10 +3358,10 @@ function generateAndOpenLaunchpad() {
3355
3358
  --card-border: rgba(255, 255, 255, 0.08);
3356
3359
  --text: #f0f6fc;
3357
3360
  --text-muted: #8b949e;
3358
- --accent: #58a6ff;
3361
+ --accent: #9b30c4;
3359
3362
  --green: #3fb950;
3360
- --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
3361
- --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
3363
+ --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
3364
+ --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
3362
3365
  }
3363
3366
  * { box-sizing: border-box; margin: 0; padding: 0; }
3364
3367
  body {
@@ -3384,12 +3387,13 @@ function generateAndOpenLaunchpad() {
3384
3387
  letter-spacing: -0.5px;
3385
3388
  margin-bottom: 6px;
3386
3389
  }
3390
+ .logo-mark { width: 36px; height: 36px; flex-shrink: 0; }
3387
3391
  .logo-badge {
3388
- background: linear-gradient(135deg, #58a6ff 0%, #bc8cff 100%);
3392
+ background: linear-gradient(135deg, #ffffff 0%, #c77dea 60%, #9b30c4 100%);
3389
3393
  -webkit-background-clip: text;
3390
3394
  -webkit-text-fill-color: transparent;
3391
3395
  }
3392
- .tagline { color: var(--text-muted); font-size: 14px; }
3396
+ .tagline { color: var(--text-muted); font-size: 14px; font-weight: 400; }
3393
3397
  .grid { display: flex; flex-direction: column; gap: 12px; }
3394
3398
  .card {
3395
3399
  background: var(--card-bg);
@@ -3405,15 +3409,15 @@ function generateAndOpenLaunchpad() {
3405
3409
  transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
3406
3410
  }
3407
3411
  .card:hover {
3408
- border-color: rgba(88, 166, 255, 0.4);
3412
+ border-color: rgba(155, 48, 196, 0.45);
3409
3413
  transform: translateY(-2px);
3410
3414
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
3411
3415
  }
3412
3416
  .card-content { display: flex; align-items: center; gap: 14px; }
3413
3417
  .card-icon {
3414
- font-size: 22px;
3415
3418
  width: 40px;
3416
3419
  height: 40px;
3420
+ padding: 6px;
3417
3421
  border-radius: 10px;
3418
3422
  background: rgba(255, 255, 255, 0.04);
3419
3423
  border: 1px solid rgba(255, 255, 255, 0.06);
@@ -3421,6 +3425,7 @@ function generateAndOpenLaunchpad() {
3421
3425
  align-items: center;
3422
3426
  justify-content: center;
3423
3427
  }
3428
+ .card-icon svg { width: 100%; height: 100%; }
3424
3429
  .card-info h2 { font-size: 15px; font-weight: 600; margin-bottom: 2px; }
3425
3430
  .card-info p { font-size: 13px; color: var(--text-muted); }
3426
3431
  .card-meta {
@@ -3434,7 +3439,7 @@ function generateAndOpenLaunchpad() {
3434
3439
  background: rgba(255, 255, 255, 0.06);
3435
3440
  padding: 4px 10px;
3436
3441
  border-radius: 20px;
3437
- color: #79c0ff;
3442
+ color: #c77dea;
3438
3443
  }
3439
3444
  .status-dot {
3440
3445
  width: 8px;
@@ -3460,13 +3465,43 @@ function generateAndOpenLaunchpad() {
3460
3465
  <body>
3461
3466
  <div class="container">
3462
3467
  <div class="header">
3463
- <div class="logo">🚀 <span class="logo-badge">BDB Agent OS</span></div>
3468
+ <div class="logo">
3469
+ <svg class="logo-mark" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
3470
+ <defs>
3471
+ <linearGradient id="bdbPlateLight" x1="0%" y1="0%" x2="100%" y2="100%">
3472
+ <stop offset="0%" stop-color="#FFFFFF"/><stop offset="60%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#E7E7EA"/>
3473
+ </linearGradient>
3474
+ <linearGradient id="bdbPlateDark" x1="0%" y1="0%" x2="0%" y2="100%">
3475
+ <stop offset="0%" stop-color="#1C1C1F"/><stop offset="100%" stop-color="#050505"/>
3476
+ </linearGradient>
3477
+ </defs>
3478
+ <circle cx="256" cy="256" r="195" fill="url(#bdbPlateDark)" stroke="#FFFFFF" stroke-width="2" stroke-opacity="0.6"/>
3479
+ <circle cx="256" cy="256" r="175" fill="#0a0a0a"/>
3480
+ <g transform="translate(70, 68) scale(1.0)">
3481
+ <path fill="url(#bdbPlateLight)" d="M181.63.05c101.39-2.47,184.56,80.09,190.14,179.93,5.53,99.03-66.16,185.42-165.11,195.46-87.39,8.86-171.1-45.56-197.67-128.96C-28.74,128.03,56.78,3.1,181.63.05h0ZM177.5,21.3C69.09,25.31-7.16,129.62,21.76,234.52c18.72,67.88,81.56,116.7,151.53,121.32,106.21,7.03,195.6-81.63,179.68-189.06-12.54-84.65-89.81-148.66-175.47-145.49h0Z"/>
3482
+ <g fill="#FFFFFF">
3483
+ <path d="M147.69,120.05c12.04.36,24.64-.72,36.62-.07,24.29,1.32,41.37,20.13,42.91,43.99-.69,16.35.57,33.42-.28,49.69-1.31,25.42-18.55,43.75-44.02,45.64-9.59.72-20.14.69-29.78.84-4.97.08-9.9.68-10.37-5.66s.43-9.41.34-14.03c12.65-11.84,14.75-31.17,4.75-45.41-.63-.9-2.26-2.34-2.62-3.17s-.46-12.58-.26-14.37,1.1-3.52,1.46-5.16c1.21-5.42,1.06-11.26-.39-16.62-.41-1.54-1.48-3.56-1.63-4.99-.47-4.28-.14-9.67-.26-14.08-.11-3.56-.84-8.43-.57-11.86s1.32-4.35,4.08-4.75h0ZM162.03,135.5c-4.32.73-3.34,6.65-3.26,10.01.17,6.21.91,12.51,1.13,18.73.83,23.13-1.03,45.49-1.41,68.42-.07,3.93-1.27,10.87,4.01,11.46,10.36-.51,21.39,1.89,31.38-1.45,13.74-4.59,20.43-17.79,19.81-31.81-1.77-14.46-1.19-29.09-1.94-43.58-.52-10.12-3.97-20.33-12.44-26.46-10.8-7.82-24.82-4.88-37.29-5.34h0Z"/>
3484
+ <path d="M232.66,132.46c9.53.36,19.74-.72,29.19-.08,22.64,1.52,33.92,28.58,21.93,47.05-1.49,2.3-4.32,4.47-3.85,7.42.47,2.95,5.64,6.21,7.42,8.3,11.88,13.96,8.41,35.69-6.91,45.49-7.85,5.02-14.69,4.43-23.56,4.86-7.67.37-16.24,1.05-23.91.66-1.66-.08-2.98-.73-3.68-2.32s-.52-7.28-.43-9.26c.36-6.63,3.52-13.04,3.89-20.39.58-11.38-.1-23.12-.3-34.49-.1-5.68.32-11.44-.3-17.08-.41-3.82-1.64-7.52-1.89-11.35-.25-3.85-.18-7.99-.26-11.88-.07-2.84-1.26-6.03,2.68-6.95h-.01ZM243.68,144.59c-3.41.77-2.55,6.7-2.66,9.2-.28,6.08-.58,12.36-.29,18.47.1,2.06.36,6.36.9,8.21.54,1.85.84,1.79,1.66,2.19,1.66.83,8-.68,10.26-.88,4.22-.39,8.12.03,12.28-1.24,16.76-5.12,16.53-26.53,1.01-33.02-15.52-6.5-13.5-2.7-18.82-2.98-5.31-.28-3.37-.17-4.36.06h.03ZM243.68,194.52c-3.49.79-2.58,8.66-2.68,11.39-.26,7.05-1.06,17.41.07,24.21,1.13,6.8,2.63,3.38,5.32,3.26,3.96-.19,8.36-1.32,12.4-1.67,5.85-.5,10.68.22,16.24-2.25,13.6-6.04,12.62-25.27-.46-31.46-5.63-2.66-9.89-1.97-15.78-2.43-3.57-.28-7.2-.92-10.76-1.1-1.12-.06-3.35-.17-4.36.06h0Z"/>
3485
+ <path d="M90.59,133.03c8.84-.44,25.24-1.08,33.23,1.92,18.07,6.77,23.99,29.96,13.56,45.7-1.6,2.41-4.52,4.44-3.28,7.75.69,1.85,5.41,5.42,7.03,7.31,11.86,13.82,8.77,35.23-6.25,45.33-15.02,10.1-23.31,5.27-34.44,5.56-4.12.11-10.36.88-14.32.28-2.22-.34-3.45-2.79-3.48-4.83-.11-7.84.77-15.89,1.1-23.71.99-23.09,1.31-45.33.57-68.43-.14-4.15-1.05-9.79-.57-13.79.43-3.52,4.1-2.95,6.84-3.09h0ZM98.3,145.15c-1.71.29-2.98,1.7-3.21,3.41-.15,1.19-.23,3.13-.3,4.39-.39,8.01-.9,19.53.58,27.27.23,1.24.57,2.55,1.82,3.15,1.81.84,7.03-.55,9.32-.76,3.3-.3,6.62-.1,9.89-.59,21.2-3.21,21.02-29.7,1.28-34.86-4.36-1.14-13.16-1.92-17.79-2.07-.55-.01-1.03-.03-1.59.07h0ZM98.03,195.09c-4.25.88-2.46,6.76-3.24,9.72-.06,7.92-.55,15.86,0,23.75.22,3.21.77,5.75,4.57,5.67,3.82-.08,8.46-1.32,12.41-1.67,5.55-.48,11.21.12,16.48-2,14.62-5.88,13.53-26.13-.52-32.17-5.13-2.21-8.25-1.59-13.48-1.97-4.69-.34-9.7-1.23-14.35-1.38-.62-.01-1.26-.07-1.86.06h-.01Z"/>
3486
+ </g>
3487
+ </g>
3488
+ </svg>
3489
+ <span class="logo-badge">BDB Agent OS</span>
3490
+ </div>
3464
3491
  <p class="tagline">Local Multi-Agent Ecosystem Hub</p>
3465
3492
  </div>
3466
3493
  <div class="grid">
3467
3494
  <a class="card" href="http://127.0.0.1:8088" target="_blank">
3468
3495
  <div class="card-content">
3469
- <div class="card-icon">🧠</div>
3496
+ <div class="card-icon"><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
3497
+ <defs><linearGradient id="grad-memb" x1="50%" y1="0%" x2="50%" y2="100%"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#51116F"/></linearGradient></defs>
3498
+ <polygon points="50,10 85,45 50,90 15,45" fill="url(#grad-memb)" opacity="0.9"/>
3499
+ <polyline points="50,10 50,90" stroke="#FFFFFF" stroke-width="2" opacity="0.6"/>
3500
+ <polyline points="15,45 85,45" stroke="#FFFFFF" stroke-width="2" opacity="0.6"/>
3501
+ <line x1="50" y1="45" x2="70" y2="25" stroke="#FFFFFF" stroke-width="1.5" opacity="0.5"/>
3502
+ <line x1="50" y1="45" x2="30" y2="70" stroke="#FFFFFF" stroke-width="1.5" opacity="0.5"/>
3503
+ <circle cx="50" cy="45" r="4" fill="#FFFFFF"/><circle cx="70" cy="25" r="2" fill="#FFFFFF"/><circle cx="30" cy="70" r="2" fill="#FFFFFF"/>
3504
+ </svg></div>
3470
3505
  <div class="card-info">
3471
3506
  <h2>memB Vector Memory</h2>
3472
3507
  <p>Autonomous Vector & Long-Term Memory</p>
@@ -3479,7 +3514,15 @@ function generateAndOpenLaunchpad() {
3479
3514
  </a>
3480
3515
  <a class="card" href="http://127.0.0.1:7781" target="_blank">
3481
3516
  <div class="card-content">
3482
- <div class="card-icon">🏙️</div>
3517
+ <div class="card-icon"><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
3518
+ <defs><linearGradient id="grad-synapse" x1="50%" y1="0%" x2="50%" y2="100%"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#51116F"/></linearGradient></defs>
3519
+ <rect x="20" y="55" width="14" height="30" rx="2" fill="url(#grad-synapse)" opacity="0.85"/>
3520
+ <rect x="43" y="35" width="14" height="50" rx="2" fill="url(#grad-synapse)" opacity="0.9"/>
3521
+ <rect x="66" y="45" width="14" height="40" rx="2" fill="url(#grad-synapse)" opacity="0.8"/>
3522
+ <line x1="27" y1="55" x2="50" y2="35" stroke="#FFFFFF" stroke-width="1.5" opacity="0.5"/>
3523
+ <line x1="50" y1="35" x2="73" y2="45" stroke="#FFFFFF" stroke-width="1.5" opacity="0.5"/>
3524
+ <circle cx="27" cy="55" r="3" fill="#FFFFFF"/><circle cx="50" cy="35" r="3" fill="#FFFFFF"/><circle cx="73" cy="45" r="3" fill="#FFFFFF"/>
3525
+ </svg></div>
3483
3526
  <div class="card-info">
3484
3527
  <h2>Synapse 3D</h2>
3485
3528
  <p>Interactive Codebase Topology & Sessions</p>
@@ -3492,7 +3535,40 @@ function generateAndOpenLaunchpad() {
3492
3535
  </a>
3493
3536
  <a class="card" href="http://127.0.0.1:3101" target="_blank">
3494
3537
  <div class="card-content">
3495
- <div class="card-icon">⚡</div>
3538
+ <div class="card-icon"><svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
3539
+ <defs>
3540
+ <linearGradient id="plateLight" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="60%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#E7E7EA"/></linearGradient>
3541
+ <linearGradient id="plateMid" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#9b30c4"/><stop offset="100%" stop-color="#51116F"/></linearGradient>
3542
+ <linearGradient id="plateDark" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#1C1C1F"/><stop offset="100%" stop-color="#050505"/></linearGradient>
3543
+ </defs>
3544
+ <g id="antennae" stroke="#000000" stroke-width="2.5" stroke-linejoin="round">
3545
+ <rect x="180" y="45" width="20" height="30" rx="2" fill="url(#plateLight)"/><polygon points="180,45 190,25 200,45" fill="#FFFFFF"/><circle cx="190" cy="25" r="4" fill="#FFFFFF"/>
3546
+ <rect x="186" y="80" width="22" height="34" rx="2" fill="url(#plateMid)"/><rect x="194" y="118" width="24" height="38" rx="2" fill="url(#plateDark)"/><rect x="202" y="160" width="26" height="38" rx="2" fill="url(#plateDark)"/>
3547
+ <rect x="312" y="45" width="20" height="30" rx="2" fill="url(#plateLight)"/><polygon points="312,45 322,25 332,45" fill="#FFFFFF"/><circle cx="322" cy="25" r="4" fill="#FFFFFF"/>
3548
+ <rect x="304" y="80" width="22" height="34" rx="2" fill="url(#plateMid)"/><rect x="294" y="118" width="24" height="38" rx="2" fill="url(#plateDark)"/><rect x="284" y="160" width="26" height="38" rx="2" fill="url(#plateDark)"/>
3549
+ </g>
3550
+ <g id="head" stroke="#000000" stroke-width="2.5" stroke-linejoin="round">
3551
+ <rect x="238" y="170" width="36" height="32" rx="3" fill="url(#plateLight)"/>
3552
+ <rect x="156" y="206" width="200" height="88" rx="6" fill="url(#plateDark)"/>
3553
+ <rect x="136" y="230" width="20" height="40" rx="3" fill="url(#plateMid)"/><rect x="356" y="230" width="20" height="40" rx="3" fill="url(#plateMid)"/>
3554
+ </g>
3555
+ <g id="eyes">
3556
+ <rect x="174" y="222" width="56" height="52" rx="5" fill="#000000" stroke="#FFFFFF" stroke-width="2"/><rect x="180" y="228" width="44" height="40" rx="3" fill="url(#plateDark)"/>
3557
+ <circle cx="202" cy="248" r="14" fill="#FFFFFF" opacity="0.3"/><circle cx="202" cy="248" r="7" fill="#FFFFFF"/><circle cx="205" cy="245" r="2.5" fill="#FFFFFF"/>
3558
+ <rect x="282" y="222" width="56" height="52" rx="5" fill="#000000" stroke="#FFFFFF" stroke-width="2"/><rect x="288" y="228" width="44" height="40" rx="3" fill="url(#plateDark)"/>
3559
+ <circle cx="310" cy="248" r="14" fill="#FFFFFF" opacity="0.3"/><circle cx="310" cy="248" r="7" fill="#FFFFFF"/><circle cx="313" cy="245" r="2.5" fill="#FFFFFF"/>
3560
+ </g>
3561
+ <g id="mandibles" stroke="#000000" stroke-width="2.5">
3562
+ <rect x="226" y="298" width="60" height="18" rx="3" fill="url(#plateLight)"/>
3563
+ <rect x="236" y="303" width="10" height="8" rx="1.5" fill="#51116F"/><rect x="251" y="303" width="10" height="8" rx="1.5" fill="#51116F"/><rect x="266" y="303" width="10" height="8" rx="1.5" fill="#51116F"/>
3564
+ <polygon points="196,300 220,300 206,340" fill="url(#plateLight)"/><polygon points="316,300 292,300 306,340" fill="url(#plateLight)"/>
3565
+ </g>
3566
+ <g id="thorax" stroke="#000000" stroke-width="2.5" stroke-linejoin="round">
3567
+ <rect x="180" y="336" width="152" height="74" rx="6" fill="url(#plateMid)"/>
3568
+ <rect x="216" y="352" width="80" height="42" rx="4" fill="#0c0414" stroke="#FFFFFF" stroke-width="1.5"/>
3569
+ <text x="256" y="378" fill="#FFFFFF" font-family="monospace" font-size="14" font-weight="900" text-anchor="middle" letter-spacing="3">BDB</text>
3570
+ </g>
3571
+ </svg></div>
3496
3572
  <div class="card-info">
3497
3573
  <h2>AO — Agent Orchestrator</h2>
3498
3574
  <p>Multi-Agent Cockpit & Git-Worktree Orchestration</p>
@@ -3505,7 +3581,15 @@ function generateAndOpenLaunchpad() {
3505
3581
  </a>
3506
3582
  <a class="card" href="http://127.0.0.1:9080" target="_blank">
3507
3583
  <div class="card-content">
3508
- <div class="card-icon">🌐</div>
3584
+ <div class="card-icon"><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
3585
+ <defs><linearGradient id="grad-remote" x1="50%" y1="0%" x2="50%" y2="100%"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#51116F"/></linearGradient></defs>
3586
+ <circle cx="50" cy="50" r="32" fill="none" stroke="url(#grad-remote)" stroke-width="3" opacity="0.6"/>
3587
+ <circle cx="50" cy="50" r="8" fill="url(#grad-remote)"/>
3588
+ <circle cx="50" cy="14" r="4" fill="#FFFFFF"/><circle cx="82" cy="65" r="4" fill="#FFFFFF"/><circle cx="18" cy="65" r="4" fill="#FFFFFF"/>
3589
+ <line x1="50" y1="50" x2="50" y2="18" stroke="#FFFFFF" stroke-width="1.5" opacity="0.6"/>
3590
+ <line x1="50" y1="50" x2="79" y2="63" stroke="#FFFFFF" stroke-width="1.5" opacity="0.6"/>
3591
+ <line x1="50" y1="50" x2="21" y2="63" stroke="#FFFFFF" stroke-width="1.5" opacity="0.6"/>
3592
+ </svg></div>
3509
3593
  <div class="card-info">
3510
3594
  <h2>RemoteOS Gateway</h2>
3511
3595
  <p>Multi-Cloud Execution & 4-Eyes Approval Gateway</p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.4.2-beta.8",
3
+ "version": "4.4.2-beta.9",
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": {
@@ -26,35 +26,84 @@ try {
26
26
  Write-Host "Warning: pip install failed. Install google-genai manually." -ForegroundColor Yellow
27
27
  }
28
28
 
29
- # 3. Resolve API key
30
- $GeminiKey = $env:GEMINI_API_KEY
31
- if ([string]::IsNullOrWhiteSpace($GeminiKey)) {
32
- Write-Host ""
33
- $GeminiKey = Read-Host "Enter your Gemini API key (or press Enter to skip)"
34
- }
35
-
36
- if (-not [string]::IsNullOrWhiteSpace($GeminiKey)) {
37
- Write-Host "Verifying API key..." -ForegroundColor Yellow
38
- # verify_api_key.py reads GEMINI_API_KEY from its own process environment.
39
- # A key typed at the Read-Host prompt above exists only in $GeminiKey, so it
40
- # has to be published to this process before python is started - otherwise
41
- # the child sees nothing and every interactively entered key fails the check
42
- # and is discarded below.
43
- $env:GEMINI_API_KEY = $GeminiKey
44
- $VerifyScript = Join-Path $PSScriptRoot "verify_api_key.py"
45
- $VerifyOutput = & python $VerifyScript 2>&1
46
- $VerifyCode = $LASTEXITCODE
47
- if ($VerifyCode -eq 0 -and ($VerifyOutput -match "VERIFIED_OK")) {
48
- Write-Host " -> API key verified." -ForegroundColor Green
49
- [System.Environment]::SetEnvironmentVariable("GEMINI_API_KEY", $GeminiKey, "User")
29
+ # 3. Resolve provider and API key
30
+ $OpenwikiProvider = $env:OPENWIKI_PROVIDER
31
+ if ([string]::IsNullOrWhiteSpace($OpenwikiProvider)) {
32
+ $OpenwikiProvider = "google"
33
+ }
34
+
35
+ # Map provider to its API key environment variable name
36
+ $ApiKeyVar = switch ($OpenwikiProvider) {
37
+ "google" { "GEMINI_API_KEY" }
38
+ "groq" { "GROQ_API_KEY" }
39
+ "grok" { "XAI_API_KEY" }
40
+ "xai" { "XAI_API_KEY" }
41
+ "nvidia" { "NVIDIA_API_KEY" }
42
+ "openrouter" { "OPENROUTER_API_KEY" }
43
+ "openai" { "OPENAI_API_KEY" }
44
+ "custom" { "OPENWIKI_API_KEY" }
45
+ "ollama" { $null }
46
+ default { "GEMINI_API_KEY"; $OpenwikiProvider = "google" }
47
+ }
48
+
49
+ # For Google provider, use the existing interactive flow with verification
50
+ if ($OpenwikiProvider -eq "google") {
51
+ $ApiKey = $env:GEMINI_API_KEY
52
+ if ([string]::IsNullOrWhiteSpace($ApiKey)) {
53
+ Write-Host ""
54
+ $ApiKey = Read-Host "Enter your Gemini API key (or press Enter to skip)"
55
+ }
56
+
57
+ if (-not [string]::IsNullOrWhiteSpace($ApiKey)) {
58
+ Write-Host "Verifying API key..." -ForegroundColor Yellow
59
+ # verify_api_key.py reads GEMINI_API_KEY from its own process environment.
60
+ # A key typed at the Read-Host prompt above exists only in $ApiKey, so it
61
+ # has to be published to this process before python is started - otherwise
62
+ # the child sees nothing and every interactively entered key fails the check
63
+ # and is discarded below.
64
+ $env:GEMINI_API_KEY = $ApiKey
65
+ $VerifyScript = Join-Path $PSScriptRoot "verify_api_key.py"
66
+ $VerifyOutput = & python $VerifyScript 2>&1
67
+ $VerifyCode = $LASTEXITCODE
68
+ if ($VerifyCode -eq 0 -and ($VerifyOutput -match "VERIFIED_OK")) {
69
+ Write-Host " -> API key verified." -ForegroundColor Green
70
+ [System.Environment]::SetEnvironmentVariable("GEMINI_API_KEY", $ApiKey, "User")
71
+ } else {
72
+ Write-Host " -> WARNING: API key verification failed (with retry + TLS fallback)." -ForegroundColor Yellow
73
+ $VerifyOutput | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
74
+ Write-Host " The daemon will run in collect-only mode until a valid key is set." -ForegroundColor Yellow
75
+ $ApiKey = ""
76
+ }
50
77
  } else {
51
- Write-Host " -> WARNING: API key verification failed (with retry + TLS fallback)." -ForegroundColor Yellow
52
- $VerifyOutput | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
53
- Write-Host " The daemon will run in collect-only mode until a valid key is set." -ForegroundColor Yellow
54
- $GeminiKey = ""
78
+ Write-Host "No API key provided. Daemon will run in collect-only mode." -ForegroundColor Yellow
55
79
  }
56
80
  } else {
57
- Write-Host "No API key provided. Daemon will run in collect-only mode." -ForegroundColor Yellow
81
+ # For non-Google providers, skip verification and use the configured key
82
+ if ([string]::IsNullOrWhiteSpace($ApiKeyVar)) {
83
+ $ApiKey = ""
84
+ } else {
85
+ $ApiKey = (Get-Item -Path "env:$ApiKeyVar" -ErrorAction SilentlyContinue).Value
86
+ if ([string]::IsNullOrWhiteSpace($ApiKey)) {
87
+ $ApiKey = ""
88
+ }
89
+ }
90
+ if ([string]::IsNullOrWhiteSpace($ApiKey)) {
91
+ Write-Host "No API key provided for provider '$OpenwikiProvider'. Daemon will run in collect-only mode." -ForegroundColor Yellow
92
+ }
93
+ }
94
+
95
+ # Persist provider and model configuration
96
+ [System.Environment]::SetEnvironmentVariable("OPENWIKI_PROVIDER", $OpenwikiProvider, "User")
97
+ if (-not [string]::IsNullOrWhiteSpace($env:OPENWIKI_MODEL)) {
98
+ [System.Environment]::SetEnvironmentVariable("OPENWIKI_MODEL", $env:OPENWIKI_MODEL, "User")
99
+ }
100
+ if (-not [string]::IsNullOrWhiteSpace($env:OPENWIKI_BASE_URL)) {
101
+ [System.Environment]::SetEnvironmentVariable("OPENWIKI_BASE_URL", $env:OPENWIKI_BASE_URL, "User")
102
+ }
103
+
104
+ # Persist the appropriate API key
105
+ if (-not [string]::IsNullOrWhiteSpace($ApiKey) -and -not [string]::IsNullOrWhiteSpace($ApiKeyVar)) {
106
+ [System.Environment]::SetEnvironmentVariable($ApiKeyVar, $ApiKey, "User")
58
107
  }
59
108
 
60
109
  # 4. Ensure log directory
@@ -171,8 +220,12 @@ if ($Registered) {
171
220
  Write-Host " -> Projects config: $DaemonLogDir\projects.json" -ForegroundColor Yellow
172
221
  }
173
222
 
174
- if (-not [string]::IsNullOrWhiteSpace($GeminiKey)) {
175
- Write-Host " -> API key stored as the GEMINI_API_KEY user environment variable" -ForegroundColor Green
223
+ if (-not [string]::IsNullOrWhiteSpace($ApiKey)) {
224
+ if (-not [string]::IsNullOrWhiteSpace($ApiKeyVar)) {
225
+ Write-Host " -> API key stored as the $ApiKeyVar user environment variable" -ForegroundColor Green
226
+ } else {
227
+ Write-Host " -> Provider configured: $OpenwikiProvider" -ForegroundColor Green
228
+ }
176
229
  Write-Host " (not embedded in the task or the startup file)." -ForegroundColor Green
177
230
  }
178
231
 
@@ -92,29 +92,58 @@ else
92
92
  echo " -> google-genai installed; daemon will run under $DAEMON_PYTHON"
93
93
  fi
94
94
 
95
- # 3. Resolve API key
96
- GEMINI_KEY="${GEMINI_API_KEY:-}"
97
- if [ -z "$GEMINI_KEY" ]; then
98
- echo ""
99
- echo "GEMINI_API_KEY is not set in your environment."
100
- read -rp "Enter your Gemini API key (or press Enter to skip): " GEMINI_KEY
101
- fi
95
+ # 3. Resolve provider and API key
96
+ OPENWIKI_PROVIDER="${OPENWIKI_PROVIDER:-google}"
97
+
98
+ # Map provider to its API key environment variable name
99
+ case "$OPENWIKI_PROVIDER" in
100
+ google) API_KEY_VAR="GEMINI_API_KEY" ;;
101
+ groq) API_KEY_VAR="GROQ_API_KEY" ;;
102
+ grok|xai) API_KEY_VAR="XAI_API_KEY" ;;
103
+ nvidia) API_KEY_VAR="NVIDIA_API_KEY" ;;
104
+ openrouter) API_KEY_VAR="OPENROUTER_API_KEY" ;;
105
+ openai) API_KEY_VAR="OPENAI_API_KEY" ;;
106
+ custom) API_KEY_VAR="OPENWIKI_API_KEY" ;;
107
+ ollama) API_KEY_VAR="" ;;
108
+ *) OPENWIKI_PROVIDER="google"; API_KEY_VAR="GEMINI_API_KEY" ;;
109
+ esac
110
+
111
+ # For Google provider, use the existing interactive flow with verification
112
+ if [ "$OPENWIKI_PROVIDER" = "google" ]; then
113
+ GEMINI_KEY="${GEMINI_API_KEY:-}"
114
+ if [ -z "$GEMINI_KEY" ]; then
115
+ echo ""
116
+ echo "GEMINI_API_KEY is not set in your environment."
117
+ read -rp "Enter your Gemini API key (or press Enter to skip): " GEMINI_KEY
118
+ fi
102
119
 
103
- if [ -n "$GEMINI_KEY" ]; then
104
- export GEMINI_API_KEY="$GEMINI_KEY"
105
- echo "Verifying API key..."
106
- VERIFY_OUTPUT=$("$DAEMON_PYTHON" "$SCRIPTS_DIR/verify_api_key.py" 2>&1)
107
- VERIFY_CODE=$?
108
- if [ $VERIFY_CODE -eq 0 ] && echo "$VERIFY_OUTPUT" | grep -q "VERIFIED_OK"; then
109
- echo " -> API key verified successfully."
120
+ if [ -n "$GEMINI_KEY" ]; then
121
+ export GEMINI_API_KEY="$GEMINI_KEY"
122
+ echo "Verifying API key..."
123
+ VERIFY_OUTPUT=$("$DAEMON_PYTHON" "$SCRIPTS_DIR/verify_api_key.py" 2>&1)
124
+ VERIFY_CODE=$?
125
+ if [ $VERIFY_CODE -eq 0 ] && echo "$VERIFY_OUTPUT" | grep -q "VERIFIED_OK"; then
126
+ echo " -> API key verified successfully."
127
+ else
128
+ echo " -> WARNING: API key verification failed (with retry + TLS fallback)."
129
+ echo "$VERIFY_OUTPUT" | sed 's/^/ /'
130
+ echo " The daemon will run in collect-only mode until a valid key is set."
131
+ GEMINI_KEY=""
132
+ fi
110
133
  else
111
- echo " -> WARNING: API key verification failed (with retry + TLS fallback)."
112
- echo "$VERIFY_OUTPUT" | sed 's/^/ /'
113
- echo " The daemon will run in collect-only mode until a valid key is set."
114
- GEMINI_KEY=""
134
+ echo "No API key provided. Daemon will run in collect-only mode."
115
135
  fi
136
+ API_KEY="$GEMINI_KEY"
116
137
  else
117
- echo "No API key provided. Daemon will run in collect-only mode."
138
+ # For non-Google providers, skip verification and use the configured key
139
+ if [ -n "$API_KEY_VAR" ]; then
140
+ API_KEY="${!API_KEY_VAR:-}"
141
+ else
142
+ API_KEY=""
143
+ fi
144
+ if [ -z "$API_KEY" ]; then
145
+ echo "No API key provided for provider '$OPENWIKI_PROVIDER'. Daemon will run in collect-only mode."
146
+ fi
118
147
  fi
119
148
 
120
149
  # --- Helpers -------------------------------------------------------------
@@ -179,12 +208,29 @@ write_launcher() {
179
208
  {
180
209
  printf '#!/bin/sh\n'
181
210
  printf '# Generated by install_daemon.sh - do not edit, re-run the installer.\n'
182
- printf '# Holds the OpenWiki API key so it stays out of the LaunchAgent plist\n'
183
- printf '# and the systemd unit. Mode 700 on purpose.\n'
211
+ printf '# Holds the OpenWiki provider config and API keys so they stay out of\n'
212
+ printf '# the LaunchAgent plist and the systemd unit. Mode 700 on purpose.\n'
184
213
  printf 'HOME=%s; export HOME\n' "$(sq "$HOME")"
185
214
  printf 'PATH=%s; export PATH\n' "$(sq "$path_value")"
186
- if [ -n "$GEMINI_KEY" ]; then
187
- printf 'GEMINI_API_KEY=%s; export GEMINI_API_KEY\n' "$(sq "$GEMINI_KEY")"
215
+ if [ -n "$OPENWIKI_PROVIDER" ]; then
216
+ printf 'OPENWIKI_PROVIDER=%s; export OPENWIKI_PROVIDER\n' "$(sq "$OPENWIKI_PROVIDER")"
217
+ fi
218
+ if [ -n "$OPENWIKI_MODEL" ]; then
219
+ printf 'OPENWIKI_MODEL=%s; export OPENWIKI_MODEL\n' "$(sq "$OPENWIKI_MODEL")"
220
+ fi
221
+ if [ -n "$OPENWIKI_BASE_URL" ]; then
222
+ printf 'OPENWIKI_BASE_URL=%s; export OPENWIKI_BASE_URL\n' "$(sq "$OPENWIKI_BASE_URL")"
223
+ fi
224
+ if [ -n "$API_KEY" ]; then
225
+ case "$OPENWIKI_PROVIDER" in
226
+ google) printf 'GEMINI_API_KEY=%s; export GEMINI_API_KEY\n' "$(sq "$API_KEY")" ;;
227
+ groq) printf 'GROQ_API_KEY=%s; export GROQ_API_KEY\n' "$(sq "$API_KEY")" ;;
228
+ grok|xai) printf 'XAI_API_KEY=%s; export XAI_API_KEY\n' "$(sq "$API_KEY")" ;;
229
+ nvidia) printf 'NVIDIA_API_KEY=%s; export NVIDIA_API_KEY\n' "$(sq "$API_KEY")" ;;
230
+ openrouter) printf 'OPENROUTER_API_KEY=%s; export OPENROUTER_API_KEY\n' "$(sq "$API_KEY")" ;;
231
+ openai) printf 'OPENAI_API_KEY=%s; export OPENAI_API_KEY\n' "$(sq "$API_KEY")" ;;
232
+ custom) printf 'OPENWIKI_API_KEY=%s; export OPENWIKI_API_KEY\n' "$(sq "$API_KEY")" ;;
233
+ esac
188
234
  fi
189
235
  printf 'exec %s %s --one-shot\n' "$(sq "$python_bin")" "$(sq "$SCRIPT_PATH")"
190
236
  } > "$LAUNCHER_PATH"
@@ -214,7 +260,7 @@ if [ "$OS" = "macos" ]; then
214
260
 
215
261
  MACOS_PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
216
262
  write_launcher "$DAEMON_PYTHON" "$MACOS_PATH"
217
- echo " -> Launcher written to $LAUNCHER_PATH (mode 700; holds the API key, if any)."
263
+ echo " -> Launcher written to $LAUNCHER_PATH (mode 700; holds the provider config and API key)."
218
264
 
219
265
  cat <<EOF > "$PLIST_PATH"
220
266
  <?xml version="1.0" encoding="UTF-8"?>
@@ -272,7 +318,7 @@ elif [ "$OS" = "linux" ]; then
272
318
 
273
319
  LINUX_PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
274
320
  write_launcher "$DAEMON_PYTHON" "$LINUX_PATH"
275
- echo " -> Launcher written to $LAUNCHER_PATH (mode 700; holds the API key, if any)."
321
+ echo " -> Launcher written to $LAUNCHER_PATH (mode 700; holds the provider config and API key)."
276
322
 
277
323
  cat > "$SYSTEMD_SERVICE" <<EOF
278
324
  [Unit]
@@ -1,276 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
- const https = require('https');
5
- const crypto = require('crypto');
6
- const { execSync, spawn } = require('child_process');
7
-
8
- const ISSUER = 'https://auth.awsbdb.rcentry.pro';
9
- const TARBALL_URL = 'https://internal.awsbdb.rcentry.pro/aos-internal/aos-internal.tgz';
10
- const CLIENT_ID = 'aos-installer';
11
- const CONFIG_DIR = path.join(os.homedir(), '.agents');
12
- const TOKEN_FILE = path.join(CONFIG_DIR, 'aos-internal.oauth.json');
13
-
14
- function httpsGet(url) {
15
- return new Promise((resolve, reject) => {
16
- https.get(url, { timeout: 10000 }, (res) => {
17
- let data = '';
18
- res.on('data', chunk => data += chunk);
19
- res.on('end', () => {
20
- try {
21
- resolve(JSON.parse(data));
22
- } catch (e) {
23
- reject(new Error(`Failed to parse response: ${e.message}`));
24
- }
25
- });
26
- }).on('error', reject).on('timeout', function() {
27
- this.destroy();
28
- reject(new Error('Request timeout'));
29
- });
30
- });
31
- }
32
-
33
- function httpsPost(url, body) {
34
- return new Promise((resolve, reject) => {
35
- const bodyStr = JSON.stringify(body);
36
- const req = https.request(url, {
37
- method: 'POST',
38
- headers: {
39
- 'Content-Type': 'application/x-www-form-urlencoded',
40
- 'Content-Length': Buffer.byteLength(bodyStr)
41
- },
42
- timeout: 10000
43
- }, (res) => {
44
- let data = '';
45
- res.on('data', chunk => data += chunk);
46
- res.on('end', () => {
47
- try {
48
- resolve({ status: res.statusCode, body: JSON.parse(data) });
49
- } catch (e) {
50
- reject(new Error(`Failed to parse response: ${e.message}`));
51
- }
52
- });
53
- });
54
- req.on('error', reject);
55
- req.on('timeout', function() {
56
- this.destroy();
57
- reject(new Error('Request timeout'));
58
- });
59
-
60
- const params = new URLSearchParams(body);
61
- req.write(params.toString());
62
- req.end();
63
- });
64
- }
65
-
66
- function httpsGetBinary(url, headers) {
67
- return new Promise((resolve, reject) => {
68
- const req = https.get(url, { headers, timeout: 30000 }, (res) => {
69
- if (res.statusCode !== 200) {
70
- reject(new Error(`HTTP ${res.statusCode}`));
71
- return;
72
- }
73
- const chunks = [];
74
- res.on('data', chunk => chunks.push(chunk));
75
- res.on('end', () => resolve(Buffer.concat(chunks)));
76
- });
77
- req.on('error', reject);
78
- req.on('timeout', function() {
79
- this.destroy();
80
- reject(new Error('Request timeout'));
81
- });
82
- });
83
- }
84
-
85
- function generatePKCE() {
86
- const codeVerifier = crypto.randomBytes(32).toString('base64url');
87
- const codeChallenge = crypto.createHash('sha256')
88
- .update(codeVerifier)
89
- .digest('base64url');
90
- return { codeVerifier, codeChallenge };
91
- }
92
-
93
- function tryOpenBrowser(verificationUri) {
94
- const cmd = process.platform === 'darwin' ? 'open' :
95
- process.platform === 'win32' ? 'start' : 'xdg-open';
96
- try {
97
- if (process.platform === 'win32') {
98
- execSync(`start ${verificationUri}`, { stdio: 'ignore', shell: true });
99
- } else {
100
- execSync(`${cmd} "${verificationUri}"`, { stdio: 'ignore' });
101
- }
102
- } catch (e) {
103
- // Browser open failed, but that's OK -- user can type the code manually
104
- }
105
- }
106
-
107
- function loadStoredToken() {
108
- try {
109
- if (!fs.existsSync(TOKEN_FILE)) return null;
110
- const data = JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf8'));
111
- return data.refresh_token || null;
112
- } catch (e) {
113
- return null;
114
- }
115
- }
116
-
117
- function saveToken(refreshToken) {
118
- try {
119
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
120
- fs.writeFileSync(TOKEN_FILE, JSON.stringify({ refresh_token: refreshToken }, null, 2), 'utf8');
121
- fs.chmodSync(TOKEN_FILE, 0o600);
122
- } catch (e) {
123
- // If we can't save the token, continue anyway - the user will just re-auth next time
124
- }
125
- }
126
-
127
- async function refreshAccessToken(refreshToken, discoveryResp) {
128
- try {
129
- const { codeVerifier } = generatePKCE();
130
- const resp = await httpsPost(discoveryResp.token_endpoint, {
131
- grant_type: 'refresh_token',
132
- refresh_token: refreshToken,
133
- client_id: CLIENT_ID,
134
- code_verifier: codeVerifier
135
- });
136
- if (resp.body.access_token) {
137
- if (resp.body.refresh_token) {
138
- saveToken(resp.body.refresh_token);
139
- }
140
- return resp.body.access_token;
141
- }
142
- return null;
143
- } catch (e) {
144
- return null;
145
- }
146
- }
147
-
148
- async function pollForToken(discoveryResp, deviceCode, codeVerifier, interval, expiresIn) {
149
- const deadline = Date.now() + (expiresIn * 1000);
150
- let currentInterval = interval;
151
-
152
- while (Date.now() < deadline) {
153
- await new Promise(r => setTimeout(r, currentInterval * 1000));
154
-
155
- try {
156
- const resp = await httpsPost(discoveryResp.token_endpoint, {
157
- grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
158
- device_code: deviceCode,
159
- client_id: CLIENT_ID,
160
- code_verifier: codeVerifier
161
- });
162
-
163
- const err = resp.body.error;
164
- if (err === 'authorization_pending') {
165
- continue;
166
- } else if (err === 'slow_down') {
167
- currentInterval += 5;
168
- continue;
169
- } else if (err === 'expired_token' || err === 'access_denied') {
170
- return null;
171
- } else if (resp.body.access_token) {
172
- if (resp.body.refresh_token) {
173
- saveToken(resp.body.refresh_token);
174
- }
175
- return resp.body.access_token;
176
- }
177
- } catch (e) {
178
- // Network error, keep polling
179
- continue;
180
- }
181
- }
182
- return null;
183
- }
184
-
185
- async function tryFetchInternalAddon(log) {
186
- try {
187
- const discovery = await httpsGet(`${ISSUER}/.well-known/openid-configuration`);
188
-
189
- if (!discovery.device_authorization_endpoint) {
190
- log.warn('Internal add-on package not available — device_authorization_endpoint missing.');
191
- return true;
192
- }
193
-
194
- const scopes = discovery.scopes_supported || [];
195
- if (!scopes.includes('authelia.bearer.authz')) {
196
- log.warn('Internal add-on package not available — authelia.bearer.authz scope not supported.');
197
- return true;
198
- }
199
-
200
- let accessToken = null;
201
-
202
- const storedRefresh = loadStoredToken();
203
- if (storedRefresh) {
204
- accessToken = await refreshAccessToken(storedRefresh, discovery);
205
- }
206
-
207
- if (!accessToken) {
208
- const { codeVerifier, codeChallenge } = generatePKCE();
209
-
210
- const deviceResp = await httpsPost(discovery.device_authorization_endpoint, {
211
- client_id: CLIENT_ID,
212
- scope: 'openid groups offline_access authelia.bearer.authz',
213
- code_challenge: codeChallenge,
214
- code_challenge_method: 'S256'
215
- });
216
-
217
- const deviceCode = deviceResp.body.device_code;
218
- const userCode = deviceResp.body.user_code;
219
- const verificationUri = deviceResp.body.verification_uri ||
220
- deviceResp.body.verification_uri_complete;
221
- const pollInterval = deviceResp.body.interval || 5;
222
- const expiresIn = deviceResp.body.expires_in || 1800;
223
-
224
- if (!deviceCode || !userCode || !verificationUri) {
225
- log.warn('Internal add-on package not available — device flow not available.');
226
- return true;
227
- }
228
-
229
- log.message('');
230
- log.message(' To authorize the internal add-on package:');
231
- log.message(` Code: ${userCode}`);
232
- log.message(` URL: ${verificationUri}`);
233
- log.message('');
234
-
235
- tryOpenBrowser(verificationUri);
236
-
237
- accessToken = await pollForToken(discovery, deviceCode, codeVerifier, pollInterval, expiresIn);
238
- if (!accessToken) {
239
- log.warn('Internal add-on package not available — authorization timeout.');
240
- return true;
241
- }
242
- }
243
-
244
- const tarballData = await httpsGetBinary(TARBALL_URL, {
245
- 'Authorization': `Bearer ${accessToken}`
246
- });
247
-
248
- const tempDir = path.join(CONFIG_DIR, '.tmp');
249
- fs.mkdirSync(tempDir, { recursive: true });
250
- const tempTarball = path.join(tempDir, 'aos-internal.tgz');
251
- fs.writeFileSync(tempTarball, tarballData);
252
-
253
- try {
254
- const installDir = path.join(CONFIG_DIR, 'aos-internal');
255
- fs.mkdirSync(installDir, { recursive: true });
256
- execSync(`npm install "${tempTarball}"`, {
257
- cwd: installDir,
258
- stdio: 'pipe',
259
- maxBuffer: 16 * 1024 * 1024
260
- });
261
- log.step('Internal add-on package installed');
262
- return true;
263
- } catch (e) {
264
- log.warn('Internal add-on package not available — npm install failed.');
265
- return true;
266
- } finally {
267
- try { fs.unlinkSync(tempTarball); } catch (e) {}
268
- try { fs.rmdirSync(tempDir); } catch (e) {}
269
- }
270
- } catch (e) {
271
- log.warn('Internal add-on package not available — continuing without it.');
272
- return true;
273
- }
274
- }
275
-
276
- module.exports = { tryFetchInternalAddon };