@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: #
|
|
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, #
|
|
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(
|
|
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: #
|
|
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"
|
|
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"
|
|
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"
|
|
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"
|
|
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"
|
|
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.
|
|
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
|
-
$
|
|
31
|
-
if ([string]::IsNullOrWhiteSpace($
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
$
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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 "
|
|
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
|
-
|
|
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($
|
|
175
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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 "
|
|
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
|
-
|
|
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
|
|
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 "$
|
|
187
|
-
printf '
|
|
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
|
|
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
|
|
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 };
|