@hybridlabor-api/aos 4.0.0 → 4.0.2
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/README.de.md +295 -139
- package/README.md +34 -33
- package/README.pt.md +403 -313
- package/assets/bdb-ascii-frames.js +82 -0
- package/assets/header-builder-prompt.md +37 -0
- package/assets/header-v4.jpg +0 -0
- package/installer.js +105 -142
- package/lib/startup-ui.js +137 -0
- package/package.json +2 -1
- package/skills/global_config/ask-tim/SKILL.md +255 -0
- package/skills/global_config/bdb-updater/SKILL.md +52 -36
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# AOS Header Banner — Regeneration Prompt
|
|
2
|
+
|
|
3
|
+
Reference for regenerating `assets/header-v4.jpg` (or its successor) via
|
|
4
|
+
Nano Banana / Gemini image generation on a future version bump. Update the
|
|
5
|
+
version string in the prompt and re-run rather than re-deriving the brand
|
|
6
|
+
rules from scratch.
|
|
7
|
+
|
|
8
|
+
## Brand constraints (from `~/dev/assets/bdb-brand/bdbagentOS/00_MASTER_BRANDING_SPEC.md`)
|
|
9
|
+
|
|
10
|
+
- Dominant colors: Obsidian Black (`#0a0a0a` / `#141414`) and Base White (`#FFFFFF`).
|
|
11
|
+
- Accent: Vibrant Orchid / Lila (`#9b30c4`) and Deep Plum (`#51116F`) — sparingly (rings, glow, gradient details), never the dominant fill.
|
|
12
|
+
- Strict prohibitions: no mint/cyan, no rainbow/multi-hue gradients, no heartbeat lines, no heavy glassmorphism/blur, no ambient glow halos.
|
|
13
|
+
- No personal names, studio names, or attribution baked into the image — product-level branding only.
|
|
14
|
+
- Target dimensions: 1376x768 (16:9-ish wide banner, matches GitHub README width).
|
|
15
|
+
|
|
16
|
+
## Working prompt (used for v4.0.0)
|
|
17
|
+
|
|
18
|
+
> A wide banner image, 1376x768, pure black background (#0a0a0a). On the
|
|
19
|
+
> left: the bold white wordmark "AOS" in a large, heavy sans-serif font,
|
|
20
|
+
> with a smaller white subtitle "BDB AGENT OS" beneath it, and below that
|
|
21
|
+
> a small rounded pill/badge containing the text "CORE KERNEL · AOS · v4.0.0"
|
|
22
|
+
> in white on a dark badge. On the right: an abstract network/graph made of
|
|
23
|
+
> glossy hexagonal nodes (mix of black and white hexagons, beveled/glossy
|
|
24
|
+
> material) connected by thin magenta/lila (#9b30c4) lines, with a subtle
|
|
25
|
+
> lila glow ring around some of the outer nodes. No other colors — strictly
|
|
26
|
+
> black, white, and the lila accent. No text other than what's specified.
|
|
27
|
+
> Clean, professional, high-contrast — not glassy/blurred, not a rainbow
|
|
28
|
+
> gradient.
|
|
29
|
+
|
|
30
|
+
## Notes for next regeneration
|
|
31
|
+
|
|
32
|
+
- Keep the wordmark + hexagon-network composition (echoes the dispatcher-
|
|
33
|
+
graph architecture) unless the product story changes.
|
|
34
|
+
- Only the version string in the badge needs updating for a routine bump.
|
|
35
|
+
- Re-verify the output against the prohibitions above before committing —
|
|
36
|
+
Nano Banana can drift toward its default glow/gradient aesthetic if the
|
|
37
|
+
constraints aren't repeated explicitly.
|
|
Binary file
|
package/installer.js
CHANGED
|
@@ -44,8 +44,14 @@ const {
|
|
|
44
44
|
log
|
|
45
45
|
} = clack;
|
|
46
46
|
|
|
47
|
+
const {
|
|
48
|
+
renderKineticIntro,
|
|
49
|
+
buildHeroHeader,
|
|
50
|
+
buildTelemetryCard
|
|
51
|
+
} = require('./lib/startup-ui.js');
|
|
52
|
+
|
|
47
53
|
const pkgPath = path.join(__dirname, 'package.json');
|
|
48
|
-
let pkg = { name: '@hybridlabor-api/
|
|
54
|
+
let pkg = { name: '@hybridlabor-api/aos', version: '4.0.0' };
|
|
49
55
|
if (fs.existsSync(pkgPath)) {
|
|
50
56
|
try { pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); } catch (e) { logDebug(e, 'operation'); }
|
|
51
57
|
}
|
|
@@ -56,12 +62,6 @@ const colors = {
|
|
|
56
62
|
cyan: "\x1b[36m",
|
|
57
63
|
purple: "\x1b[38;2;157;78;221m",
|
|
58
64
|
purpleBold: "\x1b[1;\x1b[38;2;157;78;221m",
|
|
59
|
-
// v3.13 "NODEFORGE" release identity. The wordmark itself is drawn with a
|
|
60
|
-
// per-column pink -> yellow -> amethyst gradient computed in buildBanner();
|
|
61
|
-
// these named constants cover the surrounding chrome (divider, tagline) and
|
|
62
|
-
// the animation effects. Deliberately distinct from the purple/cyan beta
|
|
63
|
-
// banner so a push to `latest` is visually obvious, not just a
|
|
64
|
-
// version-number diff someone has to notice on their own.
|
|
65
65
|
amethyst: "\x1b[38;2;155;89;182m",
|
|
66
66
|
beige: "\x1b[38;2;222;202;168m",
|
|
67
67
|
bannerWhite: "\x1b[38;2;255;255;255m",
|
|
@@ -1256,59 +1256,44 @@ async function installOpenWikiDaemon(apiKey, targetSkillDir, openwikiEnv = {}) {
|
|
|
1256
1256
|
});
|
|
1257
1257
|
}
|
|
1258
1258
|
|
|
1259
|
-
|
|
1259
|
+
// Wordmark banner. Rendered after the kinetic intro finishes, before the
|
|
1260
|
+
// hero header -- the user wanted both kept, not one replacing the other.
|
|
1261
|
+
// Built programmatically rather than stored as an escaped string literal:
|
|
1262
|
+
// the wordmark carries a per-column pink -> yellow -> amethyst gradient,
|
|
1263
|
+
// which needs a truecolor escape emitted per character run.
|
|
1264
|
+
const BANNER_WORDMARK = [
|
|
1265
|
+
"█████▄ ████▄ █████▄ ▄████▄ ▄████ ██████ ███ ██ ██████ ▄████▄ ▄█████ ",
|
|
1266
|
+
"██▄▄██ ██ ██ ██▄▄██ ██▄▄██ ██ ▄▄▄ ██▄▄ ██ ▀▄██ ██ ██ ██ ▀▀▀▄▄▄ ",
|
|
1267
|
+
"██▄▄█▀ ████▀ ██▄▄█▀ ██ ██ ▀███▀ ██▄▄▄▄ ██ ██ ██ ▀████▀ █████▀"
|
|
1268
|
+
];
|
|
1260
1269
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1270
|
+
function buildWordmarkBanner() {
|
|
1271
|
+
const pink = [236, 72, 153];
|
|
1272
|
+
const yellow = [255, 208, 66];
|
|
1273
|
+
const amethystRgb = [155, 89, 182];
|
|
1274
|
+
const lerp = (a, b, t) => a.map((v, i) => Math.round(v + (b[i] - v) * t));
|
|
1275
|
+
const gradientAt = (t) => (t < 0.5 ? lerp(pink, yellow, t * 2) : lerp(yellow, amethystRgb, (t - 0.5) * 2));
|
|
1263
1276
|
|
|
1264
|
-
const
|
|
1265
|
-
const
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
for (let i = 0; i < frames; i++) {
|
|
1277
|
-
readline.moveCursor(process.stdout, 0, -lineCount);
|
|
1278
|
-
|
|
1279
|
-
const glitchedLines = lines.map(line => {
|
|
1280
|
-
if (line.trim().length === 0) return line;
|
|
1281
|
-
|
|
1282
|
-
let out = '';
|
|
1283
|
-
let inEscape = false;
|
|
1284
|
-
for (let j = 0; j < line.length; j++) {
|
|
1285
|
-
const c = line[j];
|
|
1286
|
-
if (c === '\x1B') inEscape = true;
|
|
1287
|
-
|
|
1288
|
-
if (inEscape) {
|
|
1289
|
-
out += c;
|
|
1290
|
-
if (c === 'm') inEscape = false;
|
|
1291
|
-
} else {
|
|
1292
|
-
if (c !== ' ' && Math.random() < 0.05) {
|
|
1293
|
-
const gChar = GLITCH_CHARS[Math.floor(Math.random() * GLITCH_CHARS.length)];
|
|
1294
|
-
out += `${colors.magenta}${gChar}${colors.reset}`;
|
|
1295
|
-
} else {
|
|
1296
|
-
out += c;
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
return out;
|
|
1301
|
-
});
|
|
1302
|
-
|
|
1303
|
-
process.stdout.write(glitchedLines.join('\n') + '\n');
|
|
1304
|
-
await new Promise(r => setTimeout(r, frameTime));
|
|
1277
|
+
const width = Math.max(...BANNER_WORDMARK.map((l) => l.length));
|
|
1278
|
+
const wordmark = BANNER_WORDMARK.map((line) => {
|
|
1279
|
+
let out = '';
|
|
1280
|
+
let lastCode = null;
|
|
1281
|
+
for (let i = 0; i < line.length; i++) {
|
|
1282
|
+
const ch = line[i];
|
|
1283
|
+
if (ch === ' ') { out += ch; continue; }
|
|
1284
|
+
const [r, g, b] = gradientAt(i / (width - 1));
|
|
1285
|
+
const code = `\x1b[38;2;${r};${g};${b}m`;
|
|
1286
|
+
if (code !== lastCode) { out += code; lastCode = code; }
|
|
1287
|
+
out += ch;
|
|
1305
1288
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1289
|
+
return out + colors.reset;
|
|
1290
|
+
}).join('\n');
|
|
1291
|
+
|
|
1292
|
+
const label = ' N O D E F O R G E ';
|
|
1293
|
+
const fill = width - label.length;
|
|
1294
|
+
const divider = '─'.repeat(Math.floor(fill / 2)) + label + '─'.repeat(fill - Math.floor(fill / 2));
|
|
1295
|
+
|
|
1296
|
+
return `${colors.bold}\n${wordmark}\n\n${colors.beige}${divider}${colors.reset}`;
|
|
1312
1297
|
}
|
|
1313
1298
|
|
|
1314
1299
|
async function runTopologyAnimation(backgroundTask, label) {
|
|
@@ -1495,20 +1480,35 @@ async function installMemB(interactive) {
|
|
|
1495
1480
|
? path.join(membDir, '.venv', 'Scripts', 'python.exe')
|
|
1496
1481
|
: path.join(membDir, '.venv', 'bin', 'python');
|
|
1497
1482
|
|
|
1483
|
+
let createdViaUv = false;
|
|
1498
1484
|
if (!fs.existsSync(venvPython)) {
|
|
1499
1485
|
try {
|
|
1500
1486
|
execSync(`uv venv --seed .venv`, { cwd: membDir, stdio: 'ignore' });
|
|
1487
|
+
createdViaUv = true;
|
|
1501
1488
|
} catch (e1) {
|
|
1502
1489
|
execSync(`${pythonCmd} -m venv .venv`, { cwd: membDir, stdio: 'ignore' });
|
|
1503
1490
|
}
|
|
1504
1491
|
}
|
|
1505
1492
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1493
|
+
// `uv venv --seed` is supposed to seed pip/setuptools/wheel into the venv, but
|
|
1494
|
+
// this has been observed to silently under-deliver (venv created, pip missing) --
|
|
1495
|
+
// when that happens every `-m pip install` call below fails with "No module named
|
|
1496
|
+
// pip". `uv pip install --python <venv>` never needs pip present in the venv at
|
|
1497
|
+
// all, so prefer it whenever uv itself is on PATH, regardless of which tool
|
|
1498
|
+
// actually created the venv -- it's a strictly more reliable install path.
|
|
1499
|
+
let hasUv = createdViaUv;
|
|
1500
|
+
if (!hasUv) {
|
|
1501
|
+
try { execSync('uv --version', { stdio: 'ignore' }); hasUv = true; } catch (e) { hasUv = false; }
|
|
1502
|
+
}
|
|
1503
|
+
const uvInstall = (pkgsArg) => `uv pip install --python "${venvPython}" ${pkgsArg}`;
|
|
1504
|
+
const pipInstall = (pkgsArg) => `"${venvPython}" -m pip install ${pkgsArg} --timeout 30 --no-input`;
|
|
1505
|
+
const installCmd = (pkgsArg) => hasUv ? uvInstall(pkgsArg) : pipInstall(pkgsArg);
|
|
1506
|
+
|
|
1507
|
+
runPipWithRetry(installCmd('--upgrade setuptools'), { cwd: membDir, stdio: 'ignore' }, 'pip setuptools for memB standalone', 2, 120000);
|
|
1508
|
+
runPipWithRetry(installCmd('-r requirements.txt'), { cwd: membDir, stdio: 'inherit' }, 'pip install for memB standalone', 2, 900000);
|
|
1509
1509
|
|
|
1510
1510
|
if (installWebUI && fs.existsSync(serverPy)) {
|
|
1511
|
-
runPipWithRetry(
|
|
1511
|
+
runPipWithRetry(installCmd('fastapi uvicorn'), { cwd: membDir, stdio: 'ignore' }, 'pip fastapi+uvicorn for memB WebUI', 2, 120000);
|
|
1512
1512
|
}
|
|
1513
1513
|
} catch (e) {
|
|
1514
1514
|
log.warn(`Failed memB standalone venv setup: ${e.message}`);
|
|
@@ -1624,6 +1624,8 @@ async function installSynapse() {
|
|
|
1624
1624
|
<array>
|
|
1625
1625
|
${binaryPath.endsWith('.js') ? `<string>/usr/local/bin/node</string>\n <string>${binaryPath}</string>` : `<string>${binaryPath}</string>`}
|
|
1626
1626
|
<string>serve</string>
|
|
1627
|
+
<string>--port</string>
|
|
1628
|
+
<string>7781</string>
|
|
1627
1629
|
</array>
|
|
1628
1630
|
<key>WorkingDirectory</key>
|
|
1629
1631
|
<string>${synapseDir}</string>
|
|
@@ -1657,7 +1659,7 @@ async function installSynapse() {
|
|
|
1657
1659
|
const stdoutLog = path.join(synapseLogDir, 'daemon.stdout.log');
|
|
1658
1660
|
const stderrLog = path.join(synapseLogDir, 'daemon.stderr.log');
|
|
1659
1661
|
const batPath = path.join(synapseLogDir, 'run-synapse.bat');
|
|
1660
|
-
const runCmd = binaryPath.endsWith('.js') ? `node "${binaryPath}" serve` : `"${binaryPath}" serve`;
|
|
1662
|
+
const runCmd = binaryPath.endsWith('.js') ? `node "${binaryPath}" serve --port 7781` : `"${binaryPath}" serve --port 7781`;
|
|
1661
1663
|
// WshShell.Run has no stdout/stderr redirection of its own, so a crash on launch
|
|
1662
1664
|
// (e.g. a missing dependency the binary shells out to) used to die silently with
|
|
1663
1665
|
// nothing to diagnose short of reading source — route through a .bat wrapper that
|
|
@@ -1678,7 +1680,7 @@ async function installSynapse() {
|
|
|
1678
1680
|
if (isListening) {
|
|
1679
1681
|
log.success('Synapse 3D Windows Background Service registered & started (Port 7781)');
|
|
1680
1682
|
} else {
|
|
1681
|
-
log.warn(`Synapse 3D Windows daemon did not respond on Port 7781 within timeout. Check ${stderrLog} for the actual error
|
|
1683
|
+
log.warn(`Synapse 3D Windows daemon did not respond on Port 7781 within timeout. Check ${stderrLog} for the actual error before assuming it's just slow to start.`);
|
|
1682
1684
|
}
|
|
1683
1685
|
} catch (e) { logDebug(e, 'windows synapse daemon setup'); }
|
|
1684
1686
|
}
|
|
@@ -1904,7 +1906,7 @@ function verifyEcosystemInstallation() {
|
|
|
1904
1906
|
{ name: '4. bdb-dev-creator-extension', pkg: '@hybridlabor-api/bdb-dev-creator-extension', paths: [path.join(moduleBasePath(), 'bdb-dev-creator-extension')] },
|
|
1905
1907
|
{ name: '5. bdb-os-remote', pkg: '@hybridlabor-api/bdb-os-remote', paths: [path.join(moduleBasePath(), 'bdb-os-remote')] },
|
|
1906
1908
|
{ name: '6. bdb-dev-tool-installer', pkg: '@hybridlabor-api/bdb-dev-tool-installer', paths: [path.join(moduleBasePath(), 'bdb-dev-tool-installer')] },
|
|
1907
|
-
{ name: '7. bdb
|
|
1909
|
+
{ name: '7. aos (bdb agent os)', pkg: '@hybridlabor-api/aos', paths: [srcDir] }
|
|
1908
1910
|
];
|
|
1909
1911
|
|
|
1910
1912
|
for (const mod of modules) {
|
|
@@ -3268,6 +3270,20 @@ async function runQuickUpdate(installState) {
|
|
|
3268
3270
|
syncSkillsToGlobalHarnesses(excludeSkills);
|
|
3269
3271
|
s.stop('Skills refreshed');
|
|
3270
3272
|
|
|
3273
|
+
// OpenWiki setup only ever ran on a brand-new install (main()'s fresh-install
|
|
3274
|
+
// branch below) -- an existing install running Quick Update never got offered
|
|
3275
|
+
// it and never had its daemon schedule refreshed. promptCredentials() already
|
|
3276
|
+
// detects an existing key and collapses to a single "keep existing" prompt in
|
|
3277
|
+
// that case, so this is a no-op confirm for anyone already configured and a
|
|
3278
|
+
// real one-time offer for anyone who isn't (including installs from before
|
|
3279
|
+
// this feature existed).
|
|
3280
|
+
if (!isAutoYes) {
|
|
3281
|
+
const creds = await promptCredentials(paths.targetMcpDir);
|
|
3282
|
+
if (creds !== BACK) {
|
|
3283
|
+
await installOpenWikiDaemon(creds.gemini, paths.targetSkillDir, { provider: creds.openwikiProvider, model: creds.openwikiModel, baseUrl: creds.openwikiBaseUrl });
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3271
3287
|
const modulesToUpdate = installState.installedModules || [];
|
|
3272
3288
|
for (const subId of modulesToUpdate) {
|
|
3273
3289
|
if (subId === 'synapse') await installSynapse();
|
|
@@ -3294,61 +3310,24 @@ async function runQuickUpdate(installState) {
|
|
|
3294
3310
|
}
|
|
3295
3311
|
|
|
3296
3312
|
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
// unreadable and near-impossible to edit safely.
|
|
3302
|
-
//
|
|
3303
|
-
// Width discipline: every rendered line stays <= 76 visible columns (the
|
|
3304
|
-
// wordmark's natural width), so the banner never wraps on an 80-column
|
|
3305
|
-
// terminal -- the same class of terminal-width bug this release already fixed
|
|
3306
|
-
// elsewhere. Changing the font means re-checking that number.
|
|
3307
|
-
const BANNER_WORDMARK = [
|
|
3308
|
-
"█████▄ ████▄ █████▄ ▄████▄ ▄████ ██████ ███ ██ ██████ ▄████▄ ▄█████ ",
|
|
3309
|
-
"██▄▄██ ██ ██ ██▄▄██ ██▄▄██ ██ ▄▄▄ ██▄▄ ██ ▀▄██ ██ ██ ██ ▀▀▀▄▄▄ ",
|
|
3310
|
-
"██▄▄█▀ ████▀ ██▄▄█▀ ██ ██ ▀███▀ ██▄▄▄▄ ██ ██ ██ ▀████▀ █████▀"
|
|
3311
|
-
];
|
|
3312
|
-
|
|
3313
|
-
function buildBanner() {
|
|
3314
|
-
const pink = [236, 72, 153];
|
|
3315
|
-
const yellow = [255, 208, 66];
|
|
3316
|
-
const amethystRgb = [155, 89, 182];
|
|
3317
|
-
const lerp = (a, b, t) => a.map((v, i) => Math.round(v + (b[i] - v) * t));
|
|
3318
|
-
const gradientAt = (t) => (t < 0.5 ? lerp(pink, yellow, t * 2) : lerp(yellow, amethystRgb, (t - 0.5) * 2));
|
|
3319
|
-
|
|
3320
|
-
const width = Math.max(...BANNER_WORDMARK.map((l) => l.length));
|
|
3321
|
-
const wordmark = BANNER_WORDMARK.map((line) => {
|
|
3322
|
-
let out = '';
|
|
3323
|
-
let lastCode = null;
|
|
3324
|
-
for (let i = 0; i < line.length; i++) {
|
|
3325
|
-
const ch = line[i];
|
|
3326
|
-
if (ch === ' ') { out += ch; continue; }
|
|
3327
|
-
const [r, g, b] = gradientAt(i / (width - 1));
|
|
3328
|
-
const code = `\x1b[38;2;${r};${g};${b}m`;
|
|
3329
|
-
if (code !== lastCode) { out += code; lastCode = code; }
|
|
3330
|
-
out += ch;
|
|
3331
|
-
}
|
|
3332
|
-
return out + colors.reset;
|
|
3333
|
-
}).join('\n');
|
|
3334
|
-
|
|
3335
|
-
const label = ' N O D E F O R G E ';
|
|
3336
|
-
const fill = width - label.length;
|
|
3337
|
-
const divider = '─'.repeat(Math.floor(fill / 2)) + label + '─'.repeat(fill - Math.floor(fill / 2));
|
|
3338
|
-
|
|
3339
|
-
const tagline = 'BDB AGENT OS · CORE KERNEL · AOS - v4.0.0';
|
|
3340
|
-
const taglinePad = ' '.repeat(Math.max(0, Math.floor((width - tagline.length) / 2)));
|
|
3313
|
+
async function main() {
|
|
3314
|
+
const skipIntro = isAutoYes || process.argv.includes('--no-intro') || process.argv.includes('--no-animation');
|
|
3315
|
+
await renderKineticIntro({ rotations: 1, fps: 12.5, skip: skipIntro });
|
|
3316
|
+
console.log(buildWordmarkBanner());
|
|
3341
3317
|
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
+ `${colors.bannerWhite}${colors.bold}${taglinePad}${tagline}${colors.reset}`;
|
|
3345
|
-
}
|
|
3318
|
+
const installState = detectInstallState();
|
|
3319
|
+
const detections = detectPlatforms();
|
|
3346
3320
|
|
|
3347
|
-
|
|
3348
|
-
const
|
|
3321
|
+
// Fast local daemon checks (150ms budget)
|
|
3322
|
+
const aoOnline = await verifyDaemonListening(3101, 'AO Daemon', 150);
|
|
3323
|
+
const remoteOsOnline = await verifyDaemonListening(9080, 'RemoteOS', 150);
|
|
3324
|
+
const daemonStatus = [
|
|
3325
|
+
{ name: 'AO Orchestrator', port: 3101, online: aoOnline },
|
|
3326
|
+
{ name: 'RemoteOS Gateway', port: 9080, online: remoteOsOnline }
|
|
3327
|
+
];
|
|
3349
3328
|
|
|
3350
|
-
|
|
3351
|
-
|
|
3329
|
+
intro(buildHeroHeader(pkg.version));
|
|
3330
|
+
console.log(buildTelemetryCard({ installState, detections, daemonStatus }));
|
|
3352
3331
|
|
|
3353
3332
|
if (DRY_RUN) {
|
|
3354
3333
|
log.warn('DRY-RUN MODE active - no files will be modified, no commands executed.');
|
|
@@ -3362,8 +3341,6 @@ async function main() {
|
|
|
3362
3341
|
log.warn(`Update available: v${pkg.version} ➔ v${latest} — run: npx ${pkg.name}@latest`);
|
|
3363
3342
|
}
|
|
3364
3343
|
|
|
3365
|
-
const installState = detectInstallState();
|
|
3366
|
-
|
|
3367
3344
|
// Initialize session manifest – all copyDirRecursiveSync calls from here
|
|
3368
3345
|
// forward will use manifest-aware writes automatically.
|
|
3369
3346
|
initSessionManifest(installState.installManifest, [
|
|
@@ -3387,28 +3364,22 @@ async function main() {
|
|
|
3387
3364
|
}
|
|
3388
3365
|
|
|
3389
3366
|
if (installState.isInstalled && !isAutoYes) {
|
|
3390
|
-
if (installState.updateAvailable) {
|
|
3391
|
-
log.warn(`BDB AGENT OS installation detected: v${installState.localVersion} ➔ v${installState.currentVersion}`);
|
|
3392
|
-
} else {
|
|
3393
|
-
log.success(`BDB AGENT OS is already up to date (v${installState.currentVersion}).`);
|
|
3394
|
-
}
|
|
3395
|
-
|
|
3396
3367
|
const options = installState.updateAvailable
|
|
3397
3368
|
? [
|
|
3398
|
-
{ value: 'quick', label: `⚡ Quick Update (v${installState.localVersion} ➔ v${installState.currentVersion})
|
|
3399
|
-
{ value: 'project', label: '📁 Drop Local Project Harness
|
|
3400
|
-
{ value: 'reconfigure', label: '🛠️
|
|
3369
|
+
{ value: 'quick', label: `⚡ Quick Update (v${installState.localVersion} ➔ v${installState.currentVersion})`, hint: 'refresh skills, templates & daemons' },
|
|
3370
|
+
{ value: 'project', label: '📁 Drop Local Project Harness', hint: 'copy dispatcher contract to cwd' },
|
|
3371
|
+
{ value: 'reconfigure', label: '🛠️ Reconfigure System', hint: 'change targets, tier or options' },
|
|
3401
3372
|
{ value: 'cancel', label: '❌ Exit' }
|
|
3402
3373
|
]
|
|
3403
3374
|
: [
|
|
3404
|
-
{ value: 'project', label: '📁 Drop Local Project Harness
|
|
3405
|
-
{ value: '
|
|
3406
|
-
{ value: '
|
|
3375
|
+
{ value: 'project', label: '📁 Drop Local Project Harness', hint: 'copy dispatcher contract to cwd' },
|
|
3376
|
+
{ value: 'quick', label: '🔄 Verify & Refresh All Skills', hint: 're-sync and health check' },
|
|
3377
|
+
{ value: 'reconfigure', label: '🛠️ Reconfigure System', hint: 'switch tier or targets' },
|
|
3407
3378
|
{ value: 'cancel', label: '❌ Exit' }
|
|
3408
3379
|
];
|
|
3409
3380
|
|
|
3410
3381
|
const action = pick(await select({
|
|
3411
|
-
message: '
|
|
3382
|
+
message: 'Select an operation:',
|
|
3412
3383
|
options,
|
|
3413
3384
|
initialValue: options[0].value
|
|
3414
3385
|
}));
|
|
@@ -3429,26 +3400,18 @@ async function main() {
|
|
|
3429
3400
|
}
|
|
3430
3401
|
}
|
|
3431
3402
|
|
|
3432
|
-
const detections = detectPlatforms();
|
|
3433
|
-
if (detections.length > 0) {
|
|
3434
|
-
log.info('Detected Agent Environments on this system:');
|
|
3435
|
-
detections.forEach(d => log.message(`${d.name} (${d.path})`));
|
|
3436
|
-
} else {
|
|
3437
|
-
log.message('No active agent config directories auto-detected in standard locations.');
|
|
3438
|
-
}
|
|
3439
|
-
|
|
3440
3403
|
const detectedNames = detections.map(d => d.name).join(', ');
|
|
3441
3404
|
const platformOptions = [
|
|
3442
|
-
{ value: '9', label: 'Local Project Harness', hint: 'copy dispatcher contract to current project' },
|
|
3443
3405
|
{ value: '0', label: '🌐 Universal Agent Harness', hint: detections.length > 0 ? `sync ALL detected: ${detectedNames}` : 'sync across ALL AI platforms' },
|
|
3444
3406
|
{ value: '1', label: 'Google Antigravity', hint: '~/.gemini/config/skills' },
|
|
3445
3407
|
{ value: '2', label: 'Claude Desktop / Claude Code', hint: '~/.claude/skills' },
|
|
3446
3408
|
{ value: '3', label: 'Cursor / Generic IDE (project-local)', hint: '.cursor/' },
|
|
3447
|
-
{ value: '4', label: 'Custom Installation (specify paths manually)' },
|
|
3448
3409
|
{ value: '5', label: 'ChatGPT Codex CLI', hint: '~/.codex/skills' },
|
|
3449
3410
|
{ value: '6', label: 'Windsurf IDE', hint: '~/.windsurf' },
|
|
3450
3411
|
{ value: '7', label: 'Roo Code / Cline / VS Code', hint: '~/.roo' },
|
|
3451
|
-
{ value: '8', label: 'Aider CLI', hint: '~/.aider' }
|
|
3412
|
+
{ value: '8', label: 'Aider CLI', hint: '~/.aider' },
|
|
3413
|
+
{ value: '9', label: '📁 Local Project Harness', hint: 'copy dispatcher contract to current project' },
|
|
3414
|
+
{ value: '4', label: '⚙️ Custom Installation', hint: 'specify paths manually' }
|
|
3452
3415
|
];
|
|
3453
3416
|
|
|
3454
3417
|
let tier = '1';
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const framesData = require('../assets/bdb-ascii-frames.js');
|
|
5
|
+
|
|
6
|
+
// BDB Master Branding Design Tokens (00_MASTER_BRANDING_SPEC.md)
|
|
7
|
+
const BRAND = {
|
|
8
|
+
lila: "\x1b[38;2;155;48;196m", // #9b30c4 Vibrant Orchid
|
|
9
|
+
lilaDark: "\x1b[38;2;81;17;111m", // #51116F Deep Plum
|
|
10
|
+
white: "\x1b[38;2;255;255;255m", // #FFFFFF Base White
|
|
11
|
+
dim: "\x1b[38;2;115;115;125m", // #73737D Muted / Telemetry Dim
|
|
12
|
+
emerald: "\x1b[38;2;16;185;129m", // Status Success
|
|
13
|
+
amber: "\x1b[38;2;245;158;11m", // Status Warning
|
|
14
|
+
reset: "\x1b[0m",
|
|
15
|
+
bold: "\x1b[1m"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Plays the 3D rotating BDB medallion kinetic intro in terminal.
|
|
20
|
+
* Automatically clears frame buffer upon completion for smooth handoff to Clack.
|
|
21
|
+
*/
|
|
22
|
+
async function renderKineticIntro({ rotations = 1, fps = 12.5, skip = false } = {}) {
|
|
23
|
+
if (skip || !process.stdout.isTTY) return;
|
|
24
|
+
|
|
25
|
+
const frames = framesData.ansi;
|
|
26
|
+
const tickMs = Math.round(1000 / fps);
|
|
27
|
+
const totalFrames = Math.round(rotations * frames.length);
|
|
28
|
+
|
|
29
|
+
// Clear terminal screen and hide cursor for a clean viewport
|
|
30
|
+
process.stdout.write('\x1b]0;BDB Agent OS · AOS Installer v4.0.0\x07');
|
|
31
|
+
process.stdout.write('\x1b[2J\x1b[H\x1b[?25l');
|
|
32
|
+
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
let currentFrame = 0;
|
|
35
|
+
|
|
36
|
+
const timer = setInterval(() => {
|
|
37
|
+
const frame = frames[currentFrame % frames.length];
|
|
38
|
+
const lines = frame.split('\n');
|
|
39
|
+
|
|
40
|
+
const buffer = [
|
|
41
|
+
'\x1b[H',
|
|
42
|
+
...lines.map(line => '\x1b[2K' + line),
|
|
43
|
+
'\x1b[2K',
|
|
44
|
+
`\x1b[2K ${BRAND.lila}${BRAND.bold}━━━ BDB AGENT OS ━━━${BRAND.reset} ${BRAND.white}${BRAND.bold}AOS Kernel v4.0.0${BRAND.reset} ${BRAND.dim}· Initializing runtime...${BRAND.reset}`
|
|
45
|
+
].join('\n');
|
|
46
|
+
|
|
47
|
+
process.stdout.write(buffer);
|
|
48
|
+
currentFrame++;
|
|
49
|
+
|
|
50
|
+
if (currentFrame >= totalFrames) {
|
|
51
|
+
clearInterval(timer);
|
|
52
|
+
|
|
53
|
+
// Cleanly clear the animation lines so Clack has a fresh, tidy viewport
|
|
54
|
+
process.stdout.write('\x1b[2J\x1b[H\x1b[?25h\x1b[0m');
|
|
55
|
+
resolve();
|
|
56
|
+
}
|
|
57
|
+
}, tickMs);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Builds the executive, studio-grade hero header for Clack intro.
|
|
63
|
+
*/
|
|
64
|
+
function buildHeroHeader(pkgVersion = '4.0.0') {
|
|
65
|
+
const title = `${BRAND.lila}${BRAND.bold}BDB AGENT OS${BRAND.reset} ${BRAND.dim}·${BRAND.reset} ${BRAND.white}${BRAND.bold}CORE KERNEL${BRAND.reset} ${BRAND.dim}·${BRAND.reset} ${BRAND.lila}${BRAND.bold}AOS${BRAND.reset} ${BRAND.dim}- v${pkgVersion}${BRAND.reset}`;
|
|
66
|
+
const subtitle = `${BRAND.dim}Multi-Agent Skill & Power-Up Ecosystem · Claude, Antigravity, Codex, Cursor & more${BRAND.reset}`;
|
|
67
|
+
const sysInfo = `${BRAND.dim}Host: ${os.type()} ${os.arch()} · Node ${process.version} · PID ${process.pid}${BRAND.reset}`;
|
|
68
|
+
|
|
69
|
+
return `${title}\n│ ${subtitle}\n│ ${sysInfo}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Formats a clean pre-flight telemetry card to display before user choices.
|
|
74
|
+
*/
|
|
75
|
+
function buildTelemetryCard({ installState, detections, daemonStatus = [] }) {
|
|
76
|
+
const lines = [];
|
|
77
|
+
const G = BRAND.dim + '│' + BRAND.reset;
|
|
78
|
+
lines.push(`${G}`);
|
|
79
|
+
lines.push(`${G} ${BRAND.bold}${BRAND.white}System Pre-Flight Telemetry${BRAND.reset}`);
|
|
80
|
+
|
|
81
|
+
// 1. Agent Platforms
|
|
82
|
+
const SHORT_NAMES = {
|
|
83
|
+
'Google Antigravity': 'Antigravity',
|
|
84
|
+
'ChatGPT Codex CLI': 'Codex CLI',
|
|
85
|
+
'Claude Code CLI': 'Claude Code',
|
|
86
|
+
'Roo Code / Cline / VS Code': 'Roo / Cline',
|
|
87
|
+
'Aider CLI': 'Aider',
|
|
88
|
+
'OpenCode CLI': 'OpenCode',
|
|
89
|
+
'Cursor / Generic IDE': 'Cursor',
|
|
90
|
+
'Windsurf IDE': 'Windsurf'
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (detections && detections.length > 0) {
|
|
94
|
+
const platformBadges = detections.map(d => `${BRAND.emerald}✔${BRAND.reset} ${BRAND.white}${SHORT_NAMES[d.name] || d.name}${BRAND.reset}`);
|
|
95
|
+
const maxPerRow = 4;
|
|
96
|
+
const rows = [];
|
|
97
|
+
for (let i = 0; i < platformBadges.length; i += maxPerRow) {
|
|
98
|
+
rows.push(platformBadges.slice(i, i + maxPerRow).join(' '));
|
|
99
|
+
}
|
|
100
|
+
lines.push(`${G} ${BRAND.lila}├─${BRAND.reset} ${BRAND.dim}Agent Platforms:${BRAND.reset} ${rows[0]}`);
|
|
101
|
+
for (let i = 1; i < rows.length; i++) {
|
|
102
|
+
lines.push(`${G} ${rows[i]}`);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
lines.push(`${G} ${BRAND.lila}├─${BRAND.reset} ${BRAND.dim}Agent Platforms:${BRAND.reset} ${BRAND.dim}None auto-detected (universal harness mode available)${BRAND.reset}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 2. Local Fleet Daemons
|
|
109
|
+
if (daemonStatus && daemonStatus.length > 0) {
|
|
110
|
+
const daemonBadges = daemonStatus
|
|
111
|
+
.map(d => `${d.online ? BRAND.emerald + '✔' : BRAND.dim + '○'} ${BRAND.white}${d.name}${BRAND.reset} ${BRAND.dim}(:${d.port})${BRAND.reset}`)
|
|
112
|
+
.join(' ');
|
|
113
|
+
lines.push(`${G} ${BRAND.lila}├─${BRAND.reset} ${BRAND.dim}Fleet Daemons:${BRAND.reset} ${daemonBadges}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 3. Installation State
|
|
117
|
+
if (installState) {
|
|
118
|
+
if (installState.isInstalled) {
|
|
119
|
+
const versionStatus = installState.updateAvailable
|
|
120
|
+
? `${BRAND.amber}v${installState.localVersion}${BRAND.reset} ➔ ${BRAND.emerald}${BRAND.bold}v${installState.currentVersion}${BRAND.reset} ${BRAND.amber}(Update Available)${BRAND.reset}`
|
|
121
|
+
: `${BRAND.emerald}✔${BRAND.reset} ${BRAND.white}v${installState.currentVersion}${BRAND.reset} ${BRAND.dim}(Current & Up-to-date)${BRAND.reset}`;
|
|
122
|
+
lines.push(`${G} ${BRAND.lila}└─${BRAND.reset} ${BRAND.dim}Kernel State:${BRAND.reset} ${versionStatus}`);
|
|
123
|
+
} else {
|
|
124
|
+
lines.push(`${G} ${BRAND.lila}└─${BRAND.reset} ${BRAND.dim}Kernel State:${BRAND.reset} ${BRAND.lila}Fresh Installation${BRAND.reset} ${BRAND.dim}(v${installState.currentVersion || '4.0.0'})${BRAND.reset}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
lines.push(`${G}`);
|
|
128
|
+
|
|
129
|
+
return lines.join('\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
BRAND,
|
|
134
|
+
renderKineticIntro,
|
|
135
|
+
buildHeroHeader,
|
|
136
|
+
buildTelemetryCard
|
|
137
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hybridlabor-api/aos",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
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
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"installer.js",
|
|
26
26
|
"bin/",
|
|
27
|
+
"lib/",
|
|
27
28
|
"mcp_config.json",
|
|
28
29
|
"GEMINI.md",
|
|
29
30
|
"CODEX.md",
|