@hybridlabor-api/aos 4.13.0 → 4.13.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/.claude/hooks/go-gate.mjs +5 -3
- package/README.de.md +0 -4
- package/README.md +0 -4
- package/README.pt.md +0 -4
- package/bin/aos-uninstall.mjs +1 -1
- package/installer.js +146 -30
- package/lib/store-ui/index.html +1 -1
- package/lib/store-ui/server.mjs +1 -1
- package/package.json +2 -2
- package/skills/global_config/bdb-memb-mcp/SKILL.md +4 -5
- package/skills/global_config/plan-canvas/SKILL.md +1 -4
|
@@ -129,9 +129,11 @@ function main() {
|
|
|
129
129
|
try {
|
|
130
130
|
input = JSON.parse(rawInput);
|
|
131
131
|
} catch (e) {
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
// Harness unknown: deny JSON on stdout (Antigravity) plus stderr + exit 2 (Claude Code/Codex) closes every path.
|
|
133
|
+
const reason = `could not parse hook input: ${e.message}`;
|
|
134
|
+
console.log(JSON.stringify({ decision: "deny", reason: `[MECHANICAL GO-GATE BLOCKED] ${reason}` }));
|
|
135
|
+
process.stderr.write(`Blocked by go-gate hook: ${reason}\n`);
|
|
136
|
+
process.exit(2);
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
|
package/README.de.md
CHANGED
|
@@ -411,10 +411,6 @@ flowchart LR
|
|
|
411
411
|
|
|
412
412
|
`@hybridlabor-api/heimdall-token-saver`: komprimiert wiederholte CLI-Ausgabe über Ambient Hooks auf jedem Harness. Reduziert Token-Overhead bei großen Projekten. Skill: `token-saver-config`.
|
|
413
413
|
|
|
414
|
-

|
|
415
|
-
|
|
416
|
-
*Illustrative Skizze aus v3.x. Die Prozentwerte sind Schätzungen des Projekts, keine für dieses README durchgeführten Messungen.*
|
|
417
|
-
|
|
418
414
|
---
|
|
419
415
|
|
|
420
416
|
## Aktualisierung
|
package/README.md
CHANGED
|
@@ -411,10 +411,6 @@ flowchart LR
|
|
|
411
411
|
|
|
412
412
|
`@hybridlabor-api/heimdall-token-saver`: compresses repeated CLI output via ambient hooks on every harness. Reduces token overhead on large projects. Skill: `token-saver-config`.
|
|
413
413
|
|
|
414
|
-

|
|
415
|
-
|
|
416
|
-
*Illustrative sketch from v3.x. The percentages are the project's own estimates, not measurements made for this README.*
|
|
417
|
-
|
|
418
414
|
---
|
|
419
415
|
|
|
420
416
|
## Updating
|
package/README.pt.md
CHANGED
|
@@ -411,10 +411,6 @@ flowchart LR
|
|
|
411
411
|
|
|
412
412
|
`@hybridlabor-api/heimdall-token-saver`: comprime saída CLI repetida via hooks ambiente em todo harness. Reduz sobrecarga de token em projetos grandes. Skill: `token-saver-config`.
|
|
413
413
|
|
|
414
|
-

|
|
415
|
-
|
|
416
|
-
*Esboço ilustrativo da v3.x. Os percentuais são estimativas do próprio projeto, não medições feitas para este README.*
|
|
417
|
-
|
|
418
414
|
---
|
|
419
415
|
|
|
420
416
|
## Atualizando
|
package/bin/aos-uninstall.mjs
CHANGED
|
@@ -244,7 +244,7 @@ function execute(p) {
|
|
|
244
244
|
if (existsSync(settings)) {
|
|
245
245
|
try {
|
|
246
246
|
const s = JSON.parse(readFileSync(settings, 'utf8'));
|
|
247
|
-
const bdb = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs'];
|
|
247
|
+
const bdb = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
|
|
248
248
|
let touched = false;
|
|
249
249
|
for (const [event, entries] of Object.entries(s.hooks || {})) {
|
|
250
250
|
const kept = entries.filter((e) => !(e.hooks || []).some((x) => bdb.some((n) => String(x.command).includes(n))));
|
package/installer.js
CHANGED
|
@@ -526,12 +526,24 @@ function loadInstallManifest(customPath = null) {
|
|
|
526
526
|
return {};
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
+
// Temp file in the same directory + rename: a crash mid-write cannot leave a truncated manifest.
|
|
530
|
+
function writeFileAtomic(filePath, data) {
|
|
531
|
+
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
532
|
+
try {
|
|
533
|
+
fs.writeFileSync(tmp, data);
|
|
534
|
+
fs.renameSync(tmp, filePath);
|
|
535
|
+
} catch (e) {
|
|
536
|
+
try { fs.unlinkSync(tmp); } catch (_) {}
|
|
537
|
+
throw e;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
529
541
|
function saveInstallManifest(manifest, customPath = null) {
|
|
530
542
|
if (DRY_RUN) return;
|
|
531
543
|
const manifestPath = customPath || getInstallManifestPath();
|
|
532
544
|
try {
|
|
533
545
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
534
|
-
|
|
546
|
+
writeFileAtomic(manifestPath, JSON.stringify(manifest, null, 2));
|
|
535
547
|
} catch (e) {
|
|
536
548
|
log.warn(`Could not write install manifest: ${e.message}`);
|
|
537
549
|
}
|
|
@@ -978,7 +990,11 @@ function detectInstallState() {
|
|
|
978
990
|
} catch (e) { logDebug(e, 'operation'); }
|
|
979
991
|
}
|
|
980
992
|
}
|
|
981
|
-
if (!localVersion)
|
|
993
|
+
if (!localVersion) {
|
|
994
|
+
// ponytail: placeholder only feeds the version display/compare; pruning is driven by the file manifest, not this value.
|
|
995
|
+
log.step('Previous AOS version is unknown (no manifest); assuming 3.8.0 for the update comparison only.');
|
|
996
|
+
localVersion = '3.8.0';
|
|
997
|
+
}
|
|
982
998
|
}
|
|
983
999
|
|
|
984
1000
|
const basePath = scriptDir.includes('_npx') ? path.join(homeDir, '.agents') : path.dirname(srcDir);
|
|
@@ -1039,7 +1055,7 @@ function saveManifest(data = {}) {
|
|
|
1039
1055
|
platform: process.platform,
|
|
1040
1056
|
...data
|
|
1041
1057
|
});
|
|
1042
|
-
|
|
1058
|
+
writeFileAtomic(manifestPath, JSON.stringify(updated, null, 2));
|
|
1043
1059
|
} catch (e) { logDebug(e, 'manifest read/parse'); }
|
|
1044
1060
|
}
|
|
1045
1061
|
|
|
@@ -1218,6 +1234,20 @@ function syncSkillEntry(fullPath, dirName, targetSkillDir, excludeSkills) {
|
|
|
1218
1234
|
}
|
|
1219
1235
|
}
|
|
1220
1236
|
|
|
1237
|
+
function redactSecrets(text) {
|
|
1238
|
+
return String(text)
|
|
1239
|
+
.replace(/([\w.-]*(?:key|token|secret|passw(?:or)?d|auth|credential)[\w.-]*\s*[=:]\s*)("[^"]*"|'[^']*'|\S+)/gi, '$1[redacted]')
|
|
1240
|
+
.replace(/\bBearer\s+\S+/gi, 'Bearer [redacted]')
|
|
1241
|
+
.replace(/\b(?:sk|ghp|gho|ghs|xox[a-z])[-_][A-Za-z0-9_-]{16,}/g, '[redacted]');
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// execFileSync error -> one message that keeps the child's own stderr/stdout (last 2 KB, redacted).
|
|
1245
|
+
function describeExecError(e) {
|
|
1246
|
+
const out = [e && e.stderr, e && e.stdout].map((b) => (b ? b.toString() : '').trim()).filter(Boolean).join('\n');
|
|
1247
|
+
const head = redactSecrets(String((e && e.message) || e).split('\n')[0]).slice(0, 300);
|
|
1248
|
+
return out ? `${head}\n${redactSecrets(out.slice(-2048))}` : head;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1221
1251
|
function installStep(what, fn, hint) {
|
|
1222
1252
|
try {
|
|
1223
1253
|
return { ok: true, value: fn() };
|
|
@@ -1937,7 +1967,7 @@ async function installMemB(interactive) {
|
|
|
1937
1967
|
// banner. Stop at the module that could not be fetched.
|
|
1938
1968
|
if (!downloadOrUpdateModule('@hybridlabor-api/memb', membDir, 'memB Vector Engine')) {
|
|
1939
1969
|
log.warn('Skipping memB setup: the module could not be downloaded.');
|
|
1940
|
-
return;
|
|
1970
|
+
return false;
|
|
1941
1971
|
}
|
|
1942
1972
|
await installDeja();
|
|
1943
1973
|
if (DRY_RUN) {
|
|
@@ -1945,6 +1975,7 @@ async function installMemB(interactive) {
|
|
|
1945
1975
|
return;
|
|
1946
1976
|
}
|
|
1947
1977
|
|
|
1978
|
+
let membOk = true;
|
|
1948
1979
|
const reqFile = path.join(membDir, 'requirements.txt');
|
|
1949
1980
|
const serverPy = path.join(membDir, 'src', 'backend', 'server.py');
|
|
1950
1981
|
if (fs.existsSync(reqFile)) {
|
|
@@ -2017,10 +2048,12 @@ async function installMemB(interactive) {
|
|
|
2017
2048
|
);
|
|
2018
2049
|
if (repaired && !verifyPythonImports(venvPython, membDir, importCheck)) {
|
|
2019
2050
|
log.warn('memB dependency repair did not pass the import check; inspect the venv before starting the daemon.');
|
|
2051
|
+
membOk = false;
|
|
2020
2052
|
}
|
|
2021
2053
|
}
|
|
2022
2054
|
} catch (e) {
|
|
2023
2055
|
log.warn(`Failed memB standalone venv setup: ${e.message}`);
|
|
2056
|
+
membOk = false;
|
|
2024
2057
|
}
|
|
2025
2058
|
}
|
|
2026
2059
|
|
|
@@ -2082,13 +2115,14 @@ async function installMemB(interactive) {
|
|
|
2082
2115
|
} catch (e) { logDebug(e, 'windows memb daemon setup'); }
|
|
2083
2116
|
}
|
|
2084
2117
|
}
|
|
2118
|
+
return membOk;
|
|
2085
2119
|
}
|
|
2086
2120
|
|
|
2087
2121
|
async function installSynapse() {
|
|
2088
2122
|
const synapseDir = path.join(moduleBasePath(), 'bdb-synapse');
|
|
2089
2123
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-synapse', synapseDir, 'BDB Synapse')) {
|
|
2090
2124
|
log.warn('Skipping Synapse setup: the module could not be downloaded.');
|
|
2091
|
-
return;
|
|
2125
|
+
return false;
|
|
2092
2126
|
}
|
|
2093
2127
|
if (DRY_RUN) {
|
|
2094
2128
|
log.step('[dry-run] would link synapse binary into ~/.local/bin/synapse + setup background daemon');
|
|
@@ -2217,6 +2251,7 @@ async function installSynapse() {
|
|
|
2217
2251
|
}
|
|
2218
2252
|
} else {
|
|
2219
2253
|
log.warn(`No pre-built binary for this platform. Compile with: cd "${synapseDir}" && go build -o ${binaryName} ./cmd/synapse/`);
|
|
2254
|
+
return false;
|
|
2220
2255
|
}
|
|
2221
2256
|
}
|
|
2222
2257
|
|
|
@@ -2410,7 +2445,7 @@ async function installCreatorExtension() {
|
|
|
2410
2445
|
const creatorDir = path.join(moduleBasePath(), 'bdb-dev-creator-extension');
|
|
2411
2446
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-dev-creator-extension', creatorDir, 'BDB Creator Extension')) {
|
|
2412
2447
|
log.warn('Skipping Creator Extension setup: the module could not be downloaded.');
|
|
2413
|
-
return;
|
|
2448
|
+
return false;
|
|
2414
2449
|
}
|
|
2415
2450
|
if (DRY_RUN) {
|
|
2416
2451
|
log.step('[dry-run] would run BDB Creator Extension setup');
|
|
@@ -2423,6 +2458,7 @@ async function installCreatorExtension() {
|
|
|
2423
2458
|
: spawnSync('node', [installerScript, '--auto'], { stdio: 'inherit', cwd: creatorDir });
|
|
2424
2459
|
if (setupResult.status !== 0) {
|
|
2425
2460
|
log.warn(`Creator Extension setup note: exit code ${setupResult.status}`);
|
|
2461
|
+
return false;
|
|
2426
2462
|
}
|
|
2427
2463
|
}
|
|
2428
2464
|
}
|
|
@@ -2431,7 +2467,7 @@ async function installHardwarePcb() {
|
|
|
2431
2467
|
const hwDir = path.join(moduleBasePath(), 'bdb-hardware-pcb');
|
|
2432
2468
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-hardware-pcb', hwDir, 'BDB Hardware & PCB (KiCad + OpenSCAD)')) {
|
|
2433
2469
|
log.warn('Skipping Hardware & PCB setup: the module could not be downloaded.');
|
|
2434
|
-
return;
|
|
2470
|
+
return false;
|
|
2435
2471
|
}
|
|
2436
2472
|
if (DRY_RUN) {
|
|
2437
2473
|
log.step('[dry-run] would run BDB Hardware & PCB setup');
|
|
@@ -2442,6 +2478,7 @@ async function installHardwarePcb() {
|
|
|
2442
2478
|
const setupResult = spawnSync('node', [installerScript, '--auto'], { stdio: 'inherit', cwd: hwDir });
|
|
2443
2479
|
if (setupResult.status !== 0) {
|
|
2444
2480
|
log.warn(`Hardware & PCB setup note: exit code ${setupResult.status}`);
|
|
2481
|
+
return false;
|
|
2445
2482
|
}
|
|
2446
2483
|
}
|
|
2447
2484
|
}
|
|
@@ -2450,7 +2487,7 @@ async function installOSRemoteGateway() {
|
|
|
2450
2487
|
const remoteDir = path.join(moduleBasePath(), 'bdb-os-remote');
|
|
2451
2488
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-os-remote', remoteDir, 'BDB OS Remote Gateway')) {
|
|
2452
2489
|
log.warn('Skipping OS Remote Gateway setup: the module could not be downloaded.');
|
|
2453
|
-
return;
|
|
2490
|
+
return false;
|
|
2454
2491
|
}
|
|
2455
2492
|
}
|
|
2456
2493
|
|
|
@@ -2458,7 +2495,7 @@ async function installDevToolInstaller() {
|
|
|
2458
2495
|
const toolInstallerDir = path.join(moduleBasePath(), 'bdb-dev-tool-installer');
|
|
2459
2496
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-dev-tool-installer', toolInstallerDir, 'BDB Dev Tool Installer')) {
|
|
2460
2497
|
log.warn('Skipping Dev Tool Installer setup: the module could not be downloaded.');
|
|
2461
|
-
return;
|
|
2498
|
+
return false;
|
|
2462
2499
|
}
|
|
2463
2500
|
}
|
|
2464
2501
|
|
|
@@ -2470,7 +2507,7 @@ function aoSupportedHere() {
|
|
|
2470
2507
|
async function installOSAgentWorkspace() {
|
|
2471
2508
|
if (!aoSupportedHere()) {
|
|
2472
2509
|
log.warn(`AO is not supported on ${process.platform}/${process.arch}.`);
|
|
2473
|
-
return;
|
|
2510
|
+
return false;
|
|
2474
2511
|
}
|
|
2475
2512
|
|
|
2476
2513
|
const isWin = process.platform === 'win32';
|
|
@@ -2491,7 +2528,7 @@ async function installOSAgentWorkspace() {
|
|
|
2491
2528
|
} else {
|
|
2492
2529
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-agent-orchestrator', osAgentDir, 'BDB Agent Orchestrator')) {
|
|
2493
2530
|
log.warn('Skipping AO setup: the module could not be downloaded.');
|
|
2494
|
-
return;
|
|
2531
|
+
return false;
|
|
2495
2532
|
}
|
|
2496
2533
|
}
|
|
2497
2534
|
if (DRY_RUN) {
|
|
@@ -2566,7 +2603,7 @@ async function installOSAgentWorkspace() {
|
|
|
2566
2603
|
|
|
2567
2604
|
if (!daemonBin) {
|
|
2568
2605
|
log.warn(`AO binary missing in ${osAgentDir} — package layout changed or Go build needed.`);
|
|
2569
|
-
return;
|
|
2606
|
+
return false;
|
|
2570
2607
|
}
|
|
2571
2608
|
|
|
2572
2609
|
if (daemonBin !== binTarget) {
|
|
@@ -2602,9 +2639,14 @@ async function installOSAgentWorkspace() {
|
|
|
2602
2639
|
}
|
|
2603
2640
|
}
|
|
2604
2641
|
|
|
2605
|
-
installStep('register the AO service', () => {
|
|
2606
|
-
|
|
2642
|
+
const serviceStep = installStep('register the AO service', () => {
|
|
2643
|
+
try {
|
|
2644
|
+
execFileSync(binTarget, ['service', 'install'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
2645
|
+
} catch (e) {
|
|
2646
|
+
throw new Error(describeExecError(e));
|
|
2647
|
+
}
|
|
2607
2648
|
}, 'Start it by hand with: ao service install');
|
|
2649
|
+
if (!serviceStep.ok) return false;
|
|
2608
2650
|
|
|
2609
2651
|
// Windows startup entries fire asynchronously — give the service more time
|
|
2610
2652
|
const aoTimeout = process.platform === 'win32' ? 20000 : 8000;
|
|
@@ -2660,6 +2702,9 @@ async function promptMemBIngestion(mcpCodeTarget) {
|
|
|
2660
2702
|
}
|
|
2661
2703
|
|
|
2662
2704
|
function verifyEcosystemInstallation() {
|
|
2705
|
+
for (const f of moduleFailures) {
|
|
2706
|
+
console.log(` • ${colors.bold}${f.name}${colors.reset} ➔ ${colors.yellow}❌ NOT installed: ${f.reason.split('\n')[0]}${colors.reset}`);
|
|
2707
|
+
}
|
|
2663
2708
|
const modules = [
|
|
2664
2709
|
{ name: '1. bdb-synapse', pkg: '@hybridlabor-api/bdb-synapse', paths: [path.join(moduleBasePath(), 'bdb-synapse')] },
|
|
2665
2710
|
{ name: '2. memB', pkg: '@hybridlabor-api/memb', paths: [path.join(moduleBasePath(), 'memB'), path.join(geminiDir, 'config', 'mcps', 'memb-mcp')] },
|
|
@@ -2839,6 +2884,18 @@ function getTierExcludeSkills(tier) {
|
|
|
2839
2884
|
// primary write above owns its file wholesale, but a secondary store belongs
|
|
2840
2885
|
// to another product and is very likely to already hold the user's own
|
|
2841
2886
|
// servers -- so this merges per server key and never replaces the file.
|
|
2887
|
+
// An unattended run has no credentials of its own (empty strings); those must never
|
|
2888
|
+
// replace a value the user already has in a server's env block.
|
|
2889
|
+
function keepExistingEnvValues(incomingServers, previousServers) {
|
|
2890
|
+
for (const [key, incoming] of Object.entries(incomingServers || {})) {
|
|
2891
|
+
const prevEnv = previousServers && previousServers[key] && previousServers[key].env;
|
|
2892
|
+
if (!incoming || typeof incoming !== 'object' || !incoming.env || !prevEnv || typeof prevEnv !== 'object') continue;
|
|
2893
|
+
for (const [k, v] of Object.entries(incoming.env)) {
|
|
2894
|
+
if (v === '' && typeof prevEnv[k] === 'string' && prevEnv[k] !== '') incoming.env[k] = prevEnv[k];
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2842
2899
|
function mirrorMcpServersTo(extraPaths, mcpConfigStr) {
|
|
2843
2900
|
if (!Array.isArray(extraPaths) || extraPaths.length === 0) return;
|
|
2844
2901
|
|
|
@@ -2862,7 +2919,9 @@ function mirrorMcpServersTo(extraPaths, mcpConfigStr) {
|
|
|
2862
2919
|
}
|
|
2863
2920
|
const data = existing || {};
|
|
2864
2921
|
data.mcpServers = data.mcpServers && typeof data.mcpServers === 'object' ? data.mcpServers : {};
|
|
2865
|
-
|
|
2922
|
+
const mirrored = JSON.parse(JSON.stringify(servers));
|
|
2923
|
+
keepExistingEnvValues(mirrored, data.mcpServers);
|
|
2924
|
+
for (const [key, val] of Object.entries(mirrored)) data.mcpServers[key] = val;
|
|
2866
2925
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
2867
2926
|
// Carries injected API keys, same as the primary config. The `mode`
|
|
2868
2927
|
// option only applies when writeFileSync CREATES the file -- an
|
|
@@ -3182,6 +3241,7 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
3182
3241
|
if (!incoming || typeof incoming !== 'object') return;
|
|
3183
3242
|
if (!incoming.env && previous.env) incoming.env = previous.env;
|
|
3184
3243
|
});
|
|
3244
|
+
keepExistingEnvValues(newServers, oldServers);
|
|
3185
3245
|
oldConfig.mcpServers = Object.assign({}, oldServers, newServers);
|
|
3186
3246
|
fs.writeFileSync(paths.mcpConfigPath, JSON.stringify(oldConfig, null, 2), { mode: 0o600 });
|
|
3187
3247
|
try { fs.chmodSync(paths.mcpConfigPath, 0o600); } catch (e) { logDebug(e, 'chmod mcpConfigPath'); }
|
|
@@ -3920,9 +3980,11 @@ function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
|
|
|
3920
3980
|
.join(' ');
|
|
3921
3981
|
return bdbHookScripts.some((name) => cmds.includes(name));
|
|
3922
3982
|
};
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3983
|
+
// Global installs must point at the copies installGlobalHooks put under
|
|
3984
|
+
// $HOME/.claude/hooks; $CLAUDE_PROJECT_DIR would not exist in other projects.
|
|
3985
|
+
const localize = (cmd) => (projectLocal
|
|
3986
|
+
? (machineGlobalHooks.some((n) => cmd.includes(n)) ? cmd : cmd.split('${HOME}').join('$CLAUDE_PROJECT_DIR'))
|
|
3987
|
+
: cmd.split('$CLAUDE_PROJECT_DIR/.claude/hooks/').join('$HOME/.claude/hooks/'));
|
|
3926
3988
|
const cloneBdbEntries = (entries) =>
|
|
3927
3989
|
JSON.parse(JSON.stringify(entries)).map((e) => ({
|
|
3928
3990
|
...e,
|
|
@@ -3969,7 +4031,16 @@ function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
|
|
|
3969
4031
|
|
|
3970
4032
|
try {
|
|
3971
4033
|
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
3972
|
-
|
|
4034
|
+
const tmpPath = `${settingsPath}.${process.pid}.tmp`;
|
|
4035
|
+
if (existing) {
|
|
4036
|
+
fs.copyFileSync(settingsPath, `${settingsPath}.${process.hrtime.bigint()}.bak`);
|
|
4037
|
+
const dir = path.dirname(settingsPath);
|
|
4038
|
+
const base = path.basename(settingsPath);
|
|
4039
|
+
const baks = fs.readdirSync(dir).filter((f) => f.startsWith(`${base}.`) && /\.\d{13,}\.bak$/.test(f)).sort();
|
|
4040
|
+
for (const old of baks.slice(0, -3)) fs.rmSync(path.join(dir, old), { force: true });
|
|
4041
|
+
}
|
|
4042
|
+
fs.writeFileSync(tmpPath, JSON.stringify(buildMerged(existing), null, 2) + '\n', { mode: existing ? fs.statSync(settingsPath).mode & 0o777 : 0o644 });
|
|
4043
|
+
fs.renameSync(tmpPath, settingsPath);
|
|
3973
4044
|
} catch (e) {
|
|
3974
4045
|
log.warn(`Could not write ${settingsPath}: ${e.message}`);
|
|
3975
4046
|
}
|
|
@@ -4178,13 +4249,35 @@ function mergeCodexTomlHooks(configTomlPath, { projectLocal = false } = {}) {
|
|
|
4178
4249
|
}
|
|
4179
4250
|
}
|
|
4180
4251
|
|
|
4181
|
-
//
|
|
4182
|
-
const
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4252
|
+
// Drop every AOS hook entry (marker block or not, duplicates included), then append one fresh block.
|
|
4253
|
+
const bdbCodexScripts = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'startcycle-dispatch.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
|
|
4254
|
+
const isAosMarker = (l) => /^\s*# AOS:HOOKS:(START|END)\s*$/.test(l);
|
|
4255
|
+
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
|
4256
|
+
const lines = content.split(/\r?\n/).filter((l) => !isAosMarker(l));
|
|
4257
|
+
const sections = [];
|
|
4258
|
+
for (const l of lines) {
|
|
4259
|
+
if (/^\s*\[/.test(l) || !sections.length) sections.push([l]);
|
|
4260
|
+
else sections[sections.length - 1].push(l);
|
|
4261
|
+
}
|
|
4262
|
+
const isHookHeader = (sec) => /^\s*\[\[hooks\.\w+\]\]\s*$/.test(sec[0]);
|
|
4263
|
+
const isHookInner = (sec) => /^\s*\[\[hooks\.\w+\.hooks\]\]\s*$/.test(sec[0]);
|
|
4264
|
+
const kept = [];
|
|
4265
|
+
for (let i = 0; i < sections.length; i++) {
|
|
4266
|
+
if (!isHookHeader(sections[i])) { kept.push(sections[i]); continue; }
|
|
4267
|
+
let j = i + 1;
|
|
4268
|
+
while (j < sections.length && isHookInner(sections[j])) j++;
|
|
4269
|
+
const group = sections.slice(i, j);
|
|
4270
|
+
const ours = group.some((sec) => sec.some((l) => /^\s*command\s*=/.test(l) && bdbCodexScripts.some((n) => l.includes(n))));
|
|
4271
|
+
if (ours) {
|
|
4272
|
+
// keep trailing blanks/comments (they may belong to another tool's marker)
|
|
4273
|
+
const tail = group.flat();
|
|
4274
|
+
let end = tail.length;
|
|
4275
|
+
while (end > 0 && /^\s*(#.*)?$/.test(tail[end - 1])) end--;
|
|
4276
|
+
if (end < tail.length) kept.push(tail.slice(end));
|
|
4277
|
+
} else kept.push(...group);
|
|
4278
|
+
i = j - 1;
|
|
4279
|
+
}
|
|
4280
|
+
content = `${kept.flat().join(eol).trimEnd()}${eol}${eol}${tomlSnippet.split('\n').join(eol)}${eol}`;
|
|
4188
4281
|
|
|
4189
4282
|
try {
|
|
4190
4283
|
fs.mkdirSync(path.dirname(configTomlPath), { recursive: true });
|
|
@@ -4350,13 +4443,33 @@ async function promptOptionalModules(installedModules) {
|
|
|
4350
4443
|
required: false
|
|
4351
4444
|
}));
|
|
4352
4445
|
|
|
4446
|
+
return (await runModuleInstalls(allModules, chosen, installedModules)).installed;
|
|
4447
|
+
}
|
|
4448
|
+
|
|
4449
|
+
// Module installers return false (or throw) on failure; undefined/true mean success.
|
|
4450
|
+
// Only successes are recorded, so the manifest never claims a module that is not there.
|
|
4451
|
+
const moduleFailures = [];
|
|
4452
|
+
async function runModuleInstalls(allModules, chosen, installedModules) {
|
|
4453
|
+
const installed = [];
|
|
4454
|
+
const failed = [];
|
|
4353
4455
|
for (const mod of allModules) {
|
|
4354
|
-
if (chosen.includes(mod.id))
|
|
4355
|
-
|
|
4356
|
-
|
|
4456
|
+
if (!chosen.includes(mod.id)) continue;
|
|
4457
|
+
let reason = null;
|
|
4458
|
+
try {
|
|
4459
|
+
if (await mod.fn() === false) reason = 'installation reported a failure (see warnings above)';
|
|
4460
|
+
} catch (e) {
|
|
4461
|
+
reason = e && e.message ? e.message : String(e);
|
|
4462
|
+
}
|
|
4463
|
+
if (reason === null) {
|
|
4464
|
+
installed.push(mod.id);
|
|
4465
|
+
if (!installedModules.includes(mod.id)) installedModules.push(mod.id);
|
|
4466
|
+
} else {
|
|
4467
|
+
failed.push({ id: mod.id, name: mod.name, reason });
|
|
4468
|
+
moduleFailures.push({ id: mod.id, name: mod.name, reason });
|
|
4469
|
+
log.warn(`${mod.name} was not installed: ${reason}`);
|
|
4357
4470
|
}
|
|
4358
4471
|
}
|
|
4359
|
-
return
|
|
4472
|
+
return { installed, failed };
|
|
4360
4473
|
}
|
|
4361
4474
|
|
|
4362
4475
|
// Module ids whose daemons ship a web interface or background service worth
|
|
@@ -5586,6 +5699,9 @@ module.exports = {
|
|
|
5586
5699
|
downloadOrUpdateModule,
|
|
5587
5700
|
detectPlatforms,
|
|
5588
5701
|
markPlatformsExplicit,
|
|
5702
|
+
runModuleInstalls,
|
|
5703
|
+
describeExecError,
|
|
5704
|
+
keepExistingEnvValues,
|
|
5589
5705
|
mergeBdbSettingsHooks,
|
|
5590
5706
|
mergeAntigravityHooks,
|
|
5591
5707
|
mergeCodexTomlHooks,
|
package/lib/store-ui/index.html
CHANGED
|
@@ -83,7 +83,7 @@ function act(i){
|
|
|
83
83
|
}
|
|
84
84
|
function row(i){
|
|
85
85
|
const k=key(i),open=S.expanded.has(k);
|
|
86
|
-
const badge=(i.fileCount>1?`<span class="badge">${i.fileCount} files</span>`:'')+(i.execBridge?'<span class="badge warn">exec bridge</span>':'')+(i.requires.length?`<span class="badge">+${i.requires.length} required</span>`:'');
|
|
86
|
+
const badge=(i.fileCount>1?`<span class="badge">${i.fileCount} files</span>`:'')+(i.execBridge?'<span class="badge warn">exec bridge</span>':'')+((i.requires||[]).length?`<span class="badge">+${i.requires.length} required</span>`:'');
|
|
87
87
|
let h=`<div class="rowwrap"><div class="row"><span class="dot ${i.installed?'g':''}"></span><span class="n" data-a="exp" data-v="${esc(k)}" style="cursor:pointer" title="Show description">${esc(i.name)}${badge}</span><span class="d" style="color:var(--muted)">${esc(i.description)}</span><span class="c cat">${esc(i.category)}</span><span class="s src">${esc(i.source)}</span><span style="text-align:right">${act(i)}</span></div>`;
|
|
88
88
|
if(open){const d=i.core?i.description:S.detail[k];h+=`<div class="detail">${d===undefined?'Loading...':esc(d)}</div>`}
|
|
89
89
|
return h+'</div>';
|
package/lib/store-ui/server.mjs
CHANGED
|
@@ -24,7 +24,7 @@ function catalog(scope, cwd) {
|
|
|
24
24
|
const core = { skill: localSkills(ROOT), agent: localAgents(ROOT) };
|
|
25
25
|
for (const kind of ['skill', 'agent']) {
|
|
26
26
|
for (const [name, file] of core[kind]) {
|
|
27
|
-
items.push({ name, kind, description: frontDescription(file), category: 'core', source: 'AOS Core', installed: true, core: true });
|
|
27
|
+
items.push({ name, kind, description: frontDescription(file), category: 'core', source: 'AOS Core', installed: true, core: true, requires: [] });
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
for (const src of sources) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hybridlabor-api/aos",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.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
|
"engines": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"plugin:build": "node scripts/build-plugin-manifest.mjs",
|
|
28
28
|
"plugin:check": "node scripts/build-plugin-manifest.mjs --check",
|
|
29
29
|
"doctor": "node bin/aos-doctor.mjs",
|
|
30
|
-
"test": "node scripts/validate-skills.mjs --selftest && node scripts/validate-skills.mjs && node scripts/build-plugin-manifest.mjs --check && node --test tests/opencode-graph-gate.test.js && node --test tests/cross-harness-hooks.test.js && node tests/aos-store.test.mjs && node --test tests/aos-store-ui.test.mjs && node tests/aos-store-scenario.test.mjs && node tests/aos-doctor.test.mjs && node tests/build-plugin-manifest.test.mjs && node --test tests/agent-models.test.js"
|
|
30
|
+
"test": "node scripts/validate-skills.mjs --selftest && node scripts/validate-skills.mjs && node scripts/build-plugin-manifest.mjs --check && node --test tests/opencode-graph-gate.test.js && node --test tests/cross-harness-hooks.test.js && node tests/aos-store.test.mjs && node --test tests/aos-store-ui.test.mjs && node tests/aos-store-scenario.test.mjs && node tests/aos-doctor.test.mjs && node tests/build-plugin-manifest.test.mjs && node --test tests/agent-models.test.js && node --test tests/installer-p3.test.js"
|
|
31
31
|
},
|
|
32
32
|
"publishConfig": {
|
|
33
33
|
"access": "public"
|
|
@@ -58,8 +58,7 @@ Deletes a specific memory segment using its UUID.
|
|
|
58
58
|
|
|
59
59
|
## 🔒 Security Hardening
|
|
60
60
|
|
|
61
|
-
The `memb-mcp` server
|
|
62
|
-
* API keys (Google Cloud, OpenAI, GitHub, etc.)
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
This safeguards your workspace metadata and prevents credentials from leaking into vector repositories.
|
|
61
|
+
The `memb-mcp` server has **no automatic credential filter** today: it stores whatever an agent sends it.
|
|
62
|
+
* Never ingest credentials: API keys (Google Cloud, OpenAI, GitHub, etc.), plaintext passwords, database URLs.
|
|
63
|
+
* The protection is the rule in this skill (and in `memb-skill`) plus human review of stored memories.
|
|
64
|
+
* An automatic pre-ingestion filter is planned but not released.
|
|
@@ -28,16 +28,13 @@ AOS from [affaan-m/ECC](https://github.com/affaan-m/ECC).
|
|
|
28
28
|
decision — the canvas verdict replaces a typed "yes/proceed".
|
|
29
29
|
- **Mandatory, not optional**, at the end of `bdbrainstorm` (before writing
|
|
30
30
|
`state.goal` / handing off to `/startcycle-graph`) and `bdbmediastorm`
|
|
31
|
-
- **Visual Extensions (BuilderIO Integration):** When generating plans, always ask the human which Canvas version they prefer:
|
|
32
|
-
- **Plan-Canvas Preview**: Standard markdown/html review.
|
|
33
|
-
- **Archify Canvas**: For strict architecture schema validation.
|
|
34
|
-
- **Visual Ecosystem**: Use `/visual-plan` (turn text plans into rich visual plans), `/visual-recap` (turn diffs into interactive visual recaps), or `/visual-edit` (open a running local app for visual editing).
|
|
35
31
|
(before the show-control architecture is considered final) — both produce
|
|
36
32
|
a plan/spec artifact a human must approve before anything downstream
|
|
37
33
|
proceeds. See each skill's own "Plan Canvas Review" section.
|
|
38
34
|
- The user should *point at* what to change: reviewing designs, comparisons,
|
|
39
35
|
reports, or any local `.md` / `.html` artifact.
|
|
40
36
|
- The user asks for a visual review, or "open it in the browser".
|
|
37
|
+
- The artifact is an Archify diagram (the standalone HTML that `archify` produces): `.html` artifacts render as-is with the annotation layer, so architecture reviews use the same loop.
|
|
41
38
|
|
|
42
39
|
This tool is a plain Node CLI speaking JSON over a loopback HTTP server —
|
|
43
40
|
it has no dependency on which agent harness invokes it (Claude Code, Codex,
|