@hybridlabor-api/aos 4.4.0 → 4.4.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/firecrawl/SKILL.md +149 -0
- package/.agents/skills/firecrawl/rules/install.md +82 -0
- package/.agents/skills/firecrawl/rules/security.md +26 -0
- package/.agents/skills/firecrawl-agent/SKILL.md +58 -0
- package/.agents/skills/firecrawl-build/SKILL.md +39 -0
- package/.agents/skills/firecrawl-build-interact/SKILL.md +68 -0
- package/.agents/skills/firecrawl-build-onboarding/SKILL.md +103 -0
- package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +39 -0
- package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +20 -0
- package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +17 -0
- package/.agents/skills/firecrawl-build-scrape/SKILL.md +69 -0
- package/.agents/skills/firecrawl-build-search/SKILL.md +69 -0
- package/.agents/skills/firecrawl-crawl/SKILL.md +59 -0
- package/.agents/skills/firecrawl-download/SKILL.md +70 -0
- package/.agents/skills/firecrawl-interact/SKILL.md +84 -0
- package/.agents/skills/firecrawl-map/SKILL.md +51 -0
- package/.agents/skills/firecrawl-scrape/SKILL.md +69 -0
- package/.agents/skills/firecrawl-search/SKILL.md +60 -0
- package/.claude/hooks/memb-inject.mjs +6 -0
- package/CLAUDE.md +573 -0
- package/README.de.md +0 -8
- package/README.md +0 -8
- package/README.pt.md +0 -8
- package/assets/brand/ao-ant-mark.svg +91 -0
- package/assets/brand/bdb-core-mark.svg +34 -0
- package/assets/brand/heimdall-guardian-mark.svg +16 -0
- package/assets/brand/memb-crystal-mark.svg +18 -0
- package/bin/aos-config.mjs +89 -0
- package/bin/aos-dashboard.mjs +608 -0
- package/bin/aos-uninstall.mjs +227 -0
- package/docs/sessions/audit-2026-09-14-silent-failures.md +114 -0
- package/docs/skills_table.md +0 -3
- package/installer.js +428 -237
- package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +10 -0
- package/mcps/after-effects-mcp/build/index.js +840 -0
- package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +153 -0
- package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +218 -0
- package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +71 -0
- package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +147 -0
- package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +114 -0
- package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +115 -0
- package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +192 -0
- package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +90 -0
- package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +50 -0
- package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +1773 -0
- package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +160 -0
- package/mcps/computer-use-mcp/dist/client.d.ts +150 -0
- package/mcps/computer-use-mcp/dist/client.js +136 -0
- package/mcps/computer-use-mcp/dist/entrypoint.d.ts +16 -0
- package/mcps/computer-use-mcp/dist/entrypoint.js +26 -0
- package/mcps/computer-use-mcp/dist/native.d.ts +212 -0
- package/mcps/computer-use-mcp/dist/native.js +50 -0
- package/mcps/computer-use-mcp/dist/server.d.ts +32 -0
- package/mcps/computer-use-mcp/dist/server.js +342 -0
- package/mcps/computer-use-mcp/dist/session.d.ts +101 -0
- package/mcps/computer-use-mcp/dist/session.js +2372 -0
- package/package.json +13 -4
- package/skills/global_config/aos-project-init/SKILL.md +97 -130
- package/skills/global_config/aos-project-init/assets/openwikiignore.template +62 -0
- package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +38 -1
- package/skills/global_config/aos-setup/SKILL.md +110 -11
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +48 -4
- package/skills/global_config/ask-tim/SKILL.md +2 -7
- package/skills/global_config/openwiki-skill/scripts/openwiki_daemon.py +65 -12
- package/bin/setup-saas.mjs +0 -792
- package/mcps/blender-mcp-server/.dockerignore +0 -19
- package/mcps/blender-mcp-server/.github/workflows/ci.yml +0 -67
- package/mcps/blender-mcp-server/.github/workflows/publish-pypi.yml +0 -54
- package/mcps/blender-mcp-server/CHANGELOG.md +0 -64
- package/mcps/blender-mcp-server/CONTRIBUTING.md +0 -107
- package/mcps/blender-mcp-server/Dockerfile +0 -19
- package/mcps/blender-mcp-server/LICENSE +0 -21
- package/mcps/blender-mcp-server/README.md +0 -409
- package/mcps/blender-mcp-server/addon/__init__.py +0 -1440
- package/mcps/blender-mcp-server/addon/models.py +0 -223
- package/mcps/blender-mcp-server/pyproject.toml +0 -120
- package/mcps/blender-mcp-server/scripts/blender_bridge_request.py +0 -71
- package/mcps/blender-mcp-server/scripts/blender_create_test_cube.py +0 -49
- package/mcps/blender-mcp-server/scripts/blender_scene_info.py +0 -38
- package/mcps/blender-mcp-server/scripts/build_addon_zip.sh +0 -29
- package/mcps/blender-mcp-server/scripts/demos/README.md +0 -186
- package/mcps/blender-mcp-server/scripts/demos/dam_break_scene.py +0 -276
- package/mcps/blender-mcp-server/scripts/demos/open_dam_break_gui.py +0 -33
- package/mcps/blender-mcp-server/scripts/demos/pipe_movement_study.py +0 -518
- package/mcps/blender-mcp-server/scripts/demos/pipe_pressure_failure_scene.py +0 -806
- package/mcps/blender-mcp-server/scripts/demos/procedural_dam_break_scene.py +0 -600
- package/mcps/blender-mcp-server/scripts/demos/run_dam_break.py +0 -510
- package/mcps/blender-mcp-server/scripts/launch_blender_gui.py +0 -41
- package/mcps/blender-mcp-server/scripts/library/README.md +0 -64
- package/mcps/blender-mcp-server/scripts/library/apply_transforms.py +0 -46
- package/mcps/blender-mcp-server/scripts/library/camera.py +0 -51
- package/mcps/blender-mcp-server/scripts/library/collections.py +0 -65
- package/mcps/blender-mcp-server/scripts/library/create_mesh.py +0 -41
- package/mcps/blender-mcp-server/scripts/library/effector.py +0 -48
- package/mcps/blender-mcp-server/scripts/library/fluid_domain.py +0 -80
- package/mcps/blender-mcp-server/scripts/library/fluid_inflow.py +0 -98
- package/mcps/blender-mcp-server/scripts/library/frame_range.py +0 -35
- package/mcps/blender-mcp-server/scripts/library/keyframes.py +0 -59
- package/mcps/blender-mcp-server/scripts/library/rigid_body.py +0 -52
- package/mcps/blender-mcp-server/scripts/library/save_blend.py +0 -40
- package/mcps/blender-mcp-server/scripts/server_start.sh +0 -26
- package/mcps/blender-mcp-server/src/blender_mcp_server/__init__.py +0 -3
- package/mcps/blender-mcp-server/src/blender_mcp_server/headless.py +0 -330
- package/mcps/blender-mcp-server/src/blender_mcp_server/server.py +0 -606
- package/mcps/davinci-mcp-professional/.bandit +0 -35
- package/mcps/davinci-mcp-professional/.claude/commands/security-review.md +0 -191
- package/mcps/davinci-mcp-professional/.claude/settings.json +0 -1
- package/mcps/davinci-mcp-professional/.editorconfig +0 -27
- package/mcps/davinci-mcp-professional/.gitattributes +0 -207
- package/mcps/davinci-mcp-professional/.github/CODEOWNERS +0 -2
- package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/bug_report.md +0 -33
- package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/mcps/davinci-mcp-professional/.github/PULL_REQUEST_TEMPLATE.md +0 -20
- package/mcps/davinci-mcp-professional/.github/SECURITY.md +0 -97
- package/mcps/davinci-mcp-professional/.github/dependabot.yml +0 -27
- package/mcps/davinci-mcp-professional/.github/workflows/ci.yml +0 -39
- package/mcps/davinci-mcp-professional/ATTRIBUTION.md +0 -4
- package/mcps/davinci-mcp-professional/AUTHORS.md +0 -9
- package/mcps/davinci-mcp-professional/BUGS.md +0 -55
- package/mcps/davinci-mcp-professional/CLAUDE.md +0 -229
- package/mcps/davinci-mcp-professional/CONTRIBUTING.md +0 -51
- package/mcps/davinci-mcp-professional/COPYING.md +0 -228
- package/mcps/davinci-mcp-professional/Doxyfile +0 -379
- package/mcps/davinci-mcp-professional/README.md +0 -143
- package/mcps/davinci-mcp-professional/SECURITY.md +0 -40
- package/mcps/davinci-mcp-professional/USING.md +0 -301
- package/mcps/davinci-mcp-professional/VERSION.md +0 -13
- package/mcps/davinci-mcp-professional/build.py +0 -169
- package/mcps/davinci-mcp-professional/claude_desktop_config_template.json +0 -11
- package/mcps/davinci-mcp-professional/hooks/ci-check.sh +0 -29
- package/mcps/davinci-mcp-professional/hooks/pre-push +0 -46
- package/mcps/davinci-mcp-professional/main.py +0 -40
- package/mcps/davinci-mcp-professional/mcp_server.py +0 -30
- package/mcps/davinci-mcp-professional/pyproject.toml +0 -115
- package/mcps/davinci-mcp-professional/security_audit.py +0 -379
- package/mcps/davinci-mcp-professional/src/davinci_mcp/__init__.py +0 -19
- package/mcps/davinci-mcp-professional/src/davinci_mcp/cli.py +0 -138
- package/mcps/davinci-mcp-professional/src/davinci_mcp/resolve_client.py +0 -342
- package/mcps/davinci-mcp-professional/src/davinci_mcp/resources/__init__.py +0 -60
- package/mcps/davinci-mcp-professional/src/davinci_mcp/server.py +0 -185
- package/mcps/davinci-mcp-professional/src/davinci_mcp/tools/__init__.py +0 -146
- package/mcps/davinci-mcp-professional/src/davinci_mcp/types.py +0 -125
- package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/__init__.py +0 -19
- package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/platform.py +0 -239
- package/mcps/davinci-mcp-professional/test.py +0 -186
- package/mcps/davinci-mcp-professional/uv.lock +0 -2073
- package/mcps/davinci-resolve-mcp/.claude/skills/media-analysis.md +0 -44
- package/mcps/davinci-resolve-mcp/.clinerules +0 -17
- package/mcps/davinci-resolve-mcp/.cursorrules +0 -17
- package/mcps/davinci-resolve-mcp/.github/copilot-instructions.md +0 -17
- package/mcps/davinci-resolve-mcp/.github/workflows/npm-publish.yml +0 -109
- package/mcps/davinci-resolve-mcp/.windsurfrules +0 -17
- package/mcps/davinci-resolve-mcp/AGENTS.md +0 -114
- package/mcps/davinci-resolve-mcp/CHANGELOG.md +0 -2648
- package/mcps/davinci-resolve-mcp/CLAUDE.md +0 -15
- package/mcps/davinci-resolve-mcp/LICENSE +0 -21
- package/mcps/davinci-resolve-mcp/README.md +0 -173
- package/mcps/davinci-resolve-mcp/SECURITY.md +0 -53
- package/mcps/davinci-resolve-mcp/bin/davinci-resolve-mcp.mjs +0 -450
- package/mcps/davinci-resolve-mcp/examples/README.md +0 -53
- package/mcps/davinci-resolve-mcp/examples/markers/README.md +0 -81
- package/mcps/davinci-resolve-mcp/examples/media/README.md +0 -94
- package/mcps/davinci-resolve-mcp/examples/timeline/README.md +0 -98
- package/mcps/davinci-resolve-mcp/install.py +0 -1687
- package/mcps/davinci-resolve-mcp/package.json +0 -52
- package/mcps/davinci-resolve-mcp/release-notes/v2.22.0.md +0 -51
- package/mcps/davinci-resolve-mcp/release-notes/v2.23.0.md +0 -51
- package/mcps/davinci-resolve-mcp/release-notes/v2.23.1.md +0 -35
- package/mcps/davinci-resolve-mcp/release-notes/v2.24.1.md +0 -32
- package/mcps/davinci-resolve-mcp/scripts/audit_api_parity.py +0 -275
- package/mcps/davinci-resolve-mcp/scripts/audit_readwrite_symmetry.py +0 -80
- package/mcps/davinci-resolve-mcp/scripts/doctor.py +0 -227
- package/mcps/davinci-resolve-mcp/scripts/gen_api_limitations.py +0 -144
- package/mcps/davinci-resolve-mcp/scripts/live_media_analysis_polish_probe.py +0 -65
- package/mcps/davinci-resolve-mcp/scripts/measure_bridge_cost.py +0 -65
- package/mcps/davinci-resolve-mcp/scripts/regen_panel_screenshots.py +0 -76
- package/mcps/davinci-resolve-mcp/src/__init__.py +0 -3
- package/mcps/davinci-resolve-mcp/src/analysis_dashboard.py +0 -15505
- package/mcps/davinci-resolve-mcp/src/batch_cli.py +0 -540
- package/mcps/davinci-resolve-mcp/src/control_panel.py +0 -13
- package/mcps/davinci-resolve-mcp/src/granular/__init__.py +0 -17
- package/mcps/davinci-resolve-mcp/src/granular/common.py +0 -741
- package/mcps/davinci-resolve-mcp/src/granular/folder.py +0 -420
- package/mcps/davinci-resolve-mcp/src/granular/gallery.py +0 -306
- package/mcps/davinci-resolve-mcp/src/granular/graph.py +0 -309
- package/mcps/davinci-resolve-mcp/src/granular/media_pool.py +0 -679
- package/mcps/davinci-resolve-mcp/src/granular/media_pool_item.py +0 -1020
- package/mcps/davinci-resolve-mcp/src/granular/media_storage.py +0 -179
- package/mcps/davinci-resolve-mcp/src/granular/project.py +0 -1654
- package/mcps/davinci-resolve-mcp/src/granular/resolve_control.py +0 -538
- package/mcps/davinci-resolve-mcp/src/granular/timeline.py +0 -1075
- package/mcps/davinci-resolve-mcp/src/granular/timeline_item.py +0 -2251
- package/mcps/davinci-resolve-mcp/src/resolve_mcp_server.py +0 -43
- package/mcps/davinci-resolve-mcp/src/server.py +0 -24116
- package/mcps/davinci-resolve-mcp/src/utils/__init__.py +0 -3
- package/mcps/davinci-resolve-mcp/src/utils/actor_identity.py +0 -46
- package/mcps/davinci-resolve-mcp/src/utils/analysis_caps.py +0 -678
- package/mcps/davinci-resolve-mcp/src/utils/analysis_memory.py +0 -755
- package/mcps/davinci-resolve-mcp/src/utils/analysis_runs.py +0 -307
- package/mcps/davinci-resolve-mcp/src/utils/analysis_store.py +0 -945
- package/mcps/davinci-resolve-mcp/src/utils/api_truth.py +0 -525
- package/mcps/davinci-resolve-mcp/src/utils/app_control.py +0 -319
- package/mcps/davinci-resolve-mcp/src/utils/audio_fairlight_live_probe.py +0 -263
- package/mcps/davinci-resolve-mcp/src/utils/brain_edits.py +0 -387
- package/mcps/davinci-resolve-mcp/src/utils/bridge_metrics.py +0 -76
- package/mcps/davinci-resolve-mcp/src/utils/cdl.py +0 -20
- package/mcps/davinci-resolve-mcp/src/utils/clip_query.py +0 -85
- package/mcps/davinci-resolve-mcp/src/utils/cloud_operations.py +0 -192
- package/mcps/davinci-resolve-mcp/src/utils/color_grade_live_probe.py +0 -444
- package/mcps/davinci-resolve-mcp/src/utils/contracts.py +0 -102
- package/mcps/davinci-resolve-mcp/src/utils/cut_ir.py +0 -115
- package/mcps/davinci-resolve-mcp/src/utils/dctl_templates.py +0 -368
- package/mcps/davinci-resolve-mcp/src/utils/deep_vision.py +0 -643
- package/mcps/davinci-resolve-mcp/src/utils/destructive_hook.py +0 -606
- package/mcps/davinci-resolve-mcp/src/utils/edit_engine.py +0 -737
- package/mcps/davinci-resolve-mcp/src/utils/embeddings.py +0 -922
- package/mcps/davinci-resolve-mcp/src/utils/entities.py +0 -579
- package/mcps/davinci-resolve-mcp/src/utils/extension_authoring_live_probe.py +0 -292
- package/mcps/davinci-resolve-mcp/src/utils/failure_tracker.py +0 -119
- package/mcps/davinci-resolve-mcp/src/utils/fuse_templates.py +0 -1968
- package/mcps/davinci-resolve-mcp/src/utils/fusion_composition_live_probe.py +0 -284
- package/mcps/davinci-resolve-mcp/src/utils/fusion_group_settings.py +0 -334
- package/mcps/davinci-resolve-mcp/src/utils/layout_presets.py +0 -333
- package/mcps/davinci-resolve-mcp/src/utils/mcp_stdio.py +0 -32
- package/mcps/davinci-resolve-mcp/src/utils/mcp_transport.py +0 -128
- package/mcps/davinci-resolve-mcp/src/utils/media_analysis.py +0 -7690
- package/mcps/davinci-resolve-mcp/src/utils/media_analysis_jobs.py +0 -805
- package/mcps/davinci-resolve-mcp/src/utils/media_pool_changes.py +0 -121
- package/mcps/davinci-resolve-mcp/src/utils/media_pool_ingest_live_probe.py +0 -592
- package/mcps/davinci-resolve-mcp/src/utils/multicam.py +0 -393
- package/mcps/davinci-resolve-mcp/src/utils/object_inspection.py +0 -301
- package/mcps/davinci-resolve-mcp/src/utils/page_lock.py +0 -75
- package/mcps/davinci-resolve-mcp/src/utils/platform.py +0 -157
- package/mcps/davinci-resolve-mcp/src/utils/proc.py +0 -26
- package/mcps/davinci-resolve-mcp/src/utils/project_cleanup.py +0 -73
- package/mcps/davinci-resolve-mcp/src/utils/project_lifecycle_live_probe.py +0 -376
- package/mcps/davinci-resolve-mcp/src/utils/project_lint.py +0 -131
- package/mcps/davinci-resolve-mcp/src/utils/project_properties.py +0 -601
- package/mcps/davinci-resolve-mcp/src/utils/project_spec.py +0 -459
- package/mcps/davinci-resolve-mcp/src/utils/readback.py +0 -91
- package/mcps/davinci-resolve-mcp/src/utils/render_deliver_live_probe.py +0 -384
- package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_governance.py +0 -214
- package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_ledger.py +0 -244
- package/mcps/davinci-resolve-mcp/src/utils/resolve_busy.py +0 -171
- package/mcps/davinci-resolve-mcp/src/utils/resolve_connection.py +0 -77
- package/mcps/davinci-resolve-mcp/src/utils/review_annotation_live_probe.py +0 -352
- package/mcps/davinci-resolve-mcp/src/utils/script_templates.py +0 -1193
- package/mcps/davinci-resolve-mcp/src/utils/shot_relationships.py +0 -544
- package/mcps/davinci-resolve-mcp/src/utils/structural_diff.py +0 -175
- package/mcps/davinci-resolve-mcp/src/utils/sync_detection.py +0 -889
- package/mcps/davinci-resolve-mcp/src/utils/timeline_brain_db.py +0 -826
- package/mcps/davinci-resolve-mcp/src/utils/timeline_conform_live_probe.py +0 -280
- package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_live_probe.py +0 -1092
- package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_probe.py +0 -185
- package/mcps/davinci-resolve-mcp/src/utils/timeline_title_text.py +0 -87
- package/mcps/davinci-resolve-mcp/src/utils/timeline_versioning.py +0 -730
- package/mcps/davinci-resolve-mcp/src/utils/update_check.py +0 -685
- package/mcps/vectorworks-mcp/.dockerignore +0 -8
- package/mcps/vectorworks-mcp/Dockerfile +0 -28
- package/mcps/vectorworks-mcp/README.md +0 -156
- package/mcps/vectorworks-mcp/app/__init__.py +0 -2
- package/mcps/vectorworks-mcp/app/api.py +0 -52
- package/mcps/vectorworks-mcp/app/chunking.py +0 -56
- package/mcps/vectorworks-mcp/app/config.py +0 -29
- package/mcps/vectorworks-mcp/app/embeddings.py +0 -28
- package/mcps/vectorworks-mcp/app/indexer.py +0 -106
- package/mcps/vectorworks-mcp/app/main.py +0 -33
- package/mcps/vectorworks-mcp/app/mcp_server.py +0 -71
- package/mcps/vectorworks-mcp/app/search.py +0 -54
- package/mcps/vectorworks-mcp/app/utils.py +0 -14
- package/mcps/vectorworks-mcp/data/.gitkeep +0 -1
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_PluginScript.htm +0 -95
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_ScriptEditor.htm +0 -266
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_Scripting.htm +0 -106
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2023_CreatingScriptedPlugins.htm +0 -327
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2024_RunningScripts.htm +0 -116
- package/mcps/vectorworks-mcp/data/vw-devwiki/VS_Function_Reference_category.html +0 -2129
- package/mcps/vectorworks-mcp/data/vw-jp-ref/ObjectsSymbols.html +0 -2035
- package/mcps/vectorworks-mcp/data/vw-jp-ref/ScriptFunctionReference.html +0 -29
- package/mcps/vectorworks-mcp/design.md +0 -57
- package/mcps/vectorworks-mcp/docker-compose.yml +0 -18
- package/mcps/vectorworks-mcp/index/.gitkeep +0 -1
- package/mcps/vectorworks-mcp/index/meta.jsonl +0 -3956
- package/mcps/vectorworks-mcp/index/vw.faiss +0 -0
- package/mcps/vectorworks-mcp/requirements.txt +0 -10
- package/mcps/vectorworks-mcp/scripts/fetch_docs_minimal.sh +0 -84
- package/mcps/vectorworks-mcp/scripts/fetch_github_vectorworks.sh +0 -85
- package/mcps/vectorworks-mcp/templates/index.html +0 -40
- package/scripts/ecosystem-health-audit.js +0 -257
- package/skills/bdb-dev-os-skill/SKILL.md +0 -49
- package/skills/bdb-dev-os-skill/package.json +0 -11
- package/skills/bdbsaastraining/SKILL.md +0 -316
- package/skills/bdbsaastraining/references/exam-pool.md +0 -313
- package/skills/bdbsaastraining/references/interaction-modes.md +0 -94
- package/skills/bdbsaastraining/references/track-a-ai-agent.md +0 -111
- package/skills/bdbsaastraining/references/track-b-wordpress.md +0 -111
- package/skills/bdbsaastraining/references/track-c-mailserver.md +0 -107
- package/skills/bdbsaastraining/references/track-d-debian.md +0 -109
- package/skills/bdbsaastraining/references/track-e-custom.md +0 -83
- package/skills/bdbsaastraining/scripts/build_profile.py +0 -529
- package/skills/bdbsaastraining/scripts/generate_certificate.py +0 -161
- package/skills/bdbsaastraining/scripts/preflight_check.sh +0 -150
- package/skills/bdbsaastraining/templates/certificate_template.html +0 -508
- package/skills/global_config/bdb-ecosystem-health/SKILL.md +0 -68
- package/skills/global_config/bdbsaas-ops/SKILL.md +0 -16
- package/skills/global_config/bdbsaashost/SKILL.md +0 -125
package/installer.js
CHANGED
|
@@ -493,7 +493,13 @@ function adoptFileIntoManifest(manifest, targetPath, sourceHash) {
|
|
|
493
493
|
// payload (used to identify files we recognise even before manifest entry).
|
|
494
494
|
function resolveFileConflict(sourcePath, targetPath, manifest, knownSourceHashes) {
|
|
495
495
|
const sourceHash = computeFileHash(sourcePath);
|
|
496
|
-
if (!sourceHash)
|
|
496
|
+
if (!sourceHash) {
|
|
497
|
+
// Every other branch of this function reports what it did; this one
|
|
498
|
+
// used to return silently, so a file that failed to install left no
|
|
499
|
+
// trace behind the enclosing "Synced BDB skills" success line.
|
|
500
|
+
log.warn(`[manifest] Could not read ${sourcePath} from the payload — ${path.basename(targetPath)} not installed.`);
|
|
501
|
+
return 'skipped';
|
|
502
|
+
}
|
|
497
503
|
|
|
498
504
|
const manifestEntry = manifest[targetPath];
|
|
499
505
|
const diskExists = fs.existsSync(targetPath);
|
|
@@ -508,10 +514,30 @@ function resolveFileConflict(sourcePath, targetPath, manifest, knownSourceHashes
|
|
|
508
514
|
|
|
509
515
|
const diskHash = computeFileHash(targetPath);
|
|
510
516
|
|
|
511
|
-
// Case:
|
|
517
|
+
// Case: unrecognised content at a path this payload owns.
|
|
518
|
+
//
|
|
519
|
+
// This used to skip, on the reading that unknown content means a file the
|
|
520
|
+
// installer did not place. But resolveFileConflict only ever sees paths the
|
|
521
|
+
// payload is actively writing, so the path IS ours -- and the commonest
|
|
522
|
+
// cause of unknown content is simply an older release of our own file,
|
|
523
|
+
// installed before manifest tracking existed and therefore never adopted.
|
|
524
|
+
// Skipping froze those permanently: v4.4.0 still found skills on disk
|
|
525
|
+
// carrying pre-reorg ~/bdb-dev paths and a stale `category:`, untouched
|
|
526
|
+
// across every update since, because bootstrap adoption only catches a file
|
|
527
|
+
// that is byte-identical to the current payload.
|
|
528
|
+
//
|
|
529
|
+
// So update it, and keep the protection where it belongs -- in the backup,
|
|
530
|
+
// not in refusing to write. A genuine local edit is recoverable from .bak
|
|
531
|
+
// rather than silently outvoting the shipped version forever.
|
|
512
532
|
if (!manifestEntry && !knownSourceHashes.has(diskHash)) {
|
|
513
|
-
|
|
514
|
-
|
|
533
|
+
const bakPath = `${targetPath}.${timestamp}.bak`;
|
|
534
|
+
try { fs.copyFileSync(targetPath, bakPath); } catch (e) {
|
|
535
|
+
log.warn(`[manifest] Could not create backup ${bakPath}: ${e.message}`);
|
|
536
|
+
}
|
|
537
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
538
|
+
manifest[targetPath] = { path: targetPath, sha256: sourceHash, version: pkg.version, installedAt: new Date().toISOString() };
|
|
539
|
+
log.warn(`[manifest] Unrecognised ${path.basename(targetPath)} backed up to ${bakPath}, shipped version written.`);
|
|
540
|
+
return 'bak';
|
|
515
541
|
}
|
|
516
542
|
|
|
517
543
|
// Case: first-run bootstrap – disk hash matches source hash but no manifest entry.
|
|
@@ -529,7 +555,7 @@ function resolveFileConflict(sourcePath, targetPath, manifest, knownSourceHashes
|
|
|
529
555
|
|
|
530
556
|
// Case: ours + user-edited (manifest recorded our hash, but disk now differs).
|
|
531
557
|
if (manifestEntry && diskHash !== manifestEntry.sha256) {
|
|
532
|
-
const bakPath = `${targetPath}.bak`;
|
|
558
|
+
const bakPath = `${targetPath}.${timestamp}.bak`;
|
|
533
559
|
try { fs.copyFileSync(targetPath, bakPath); } catch (e) {
|
|
534
560
|
log.warn(`[manifest] Could not create backup ${bakPath}: ${e.message}`);
|
|
535
561
|
}
|
|
@@ -617,66 +643,123 @@ function resolveMcpsArg(availableMcps) {
|
|
|
617
643
|
return matched;
|
|
618
644
|
}
|
|
619
645
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const claudeCodeDir = path.join(homeDir, '.claude');
|
|
634
|
-
if (fs.existsSync(claudeCodeConfig) || fs.existsSync(claudeCodeDir)) {
|
|
635
|
-
detections.push({ name: "Claude Code CLI", path: claudeCodeDir, key: "claudecode" });
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
const claudePath = process.platform === 'win32'
|
|
639
|
-
? path.join(process.env.APPDATA || homeDir, 'Claude')
|
|
640
|
-
: path.join(homeDir, 'Library', 'Application Support', 'Claude');
|
|
641
|
-
if (fs.existsSync(claudePath)) {
|
|
642
|
-
detections.push({ name: "Claude Desktop", path: claudePath, key: "claudedesktop" });
|
|
643
|
-
}
|
|
646
|
+
// Platforms the user explicitly asked for, by detection key. A harness the
|
|
647
|
+
// user names is installed as far as we are concerned, whether or not we can
|
|
648
|
+
// find its binary -- they know their machine better than a PATH lookup does.
|
|
649
|
+
const explicitPlatformKeys = new Set();
|
|
650
|
+
const PLATFORM_KEYS_BY_OPTION = {
|
|
651
|
+
'1': 'antigravity',
|
|
652
|
+
'2': ['claudecode', 'claudedesktop'],
|
|
653
|
+
'3': 'cursor',
|
|
654
|
+
'5': 'codex',
|
|
655
|
+
'6': 'windsurf',
|
|
656
|
+
'7': 'vscode',
|
|
657
|
+
'8': 'aider',
|
|
658
|
+
};
|
|
644
659
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
660
|
+
function markPlatformsExplicit(optionValues) {
|
|
661
|
+
for (const v of optionValues || []) {
|
|
662
|
+
const keys = PLATFORM_KEYS_BY_OPTION[v];
|
|
663
|
+
if (!keys) continue;
|
|
664
|
+
for (const k of [].concat(keys)) explicitPlatformKeys.add(k);
|
|
650
665
|
}
|
|
666
|
+
}
|
|
651
667
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (
|
|
656
|
-
|
|
657
|
-
}
|
|
668
|
+
// Where an application itself lives, per OS. Presence of the app is proof;
|
|
669
|
+
// presence of a dot-directory is not.
|
|
670
|
+
function appBundle(name) {
|
|
671
|
+
if (process.platform === 'darwin') return [`/Applications/${name}.app`, path.join(homeDir, 'Applications', `${name}.app`)];
|
|
672
|
+
if (process.platform === 'win32') return [path.join(process.env.LOCALAPPDATA || homeDir, 'Programs', name)];
|
|
673
|
+
return [`/usr/share/${name.toLowerCase()}`, `/opt/${name.toLowerCase()}`];
|
|
674
|
+
}
|
|
658
675
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
const clinePath = path.join(homeDir, '.cline');
|
|
664
|
-
if (fs.existsSync(vscodePath) || fs.existsSync(rooPath) || fs.existsSync(clinePath)) {
|
|
665
|
-
detections.push({ name: "Roo Code / Cline / VS Code", path: vscodePath, key: "vscode" });
|
|
666
|
-
}
|
|
676
|
+
const anyExists = (paths) => paths.some((p) => fs.existsSync(p));
|
|
677
|
+
const globExists = (dir, re) => {
|
|
678
|
+
try { return fs.readdirSync(dir).some((e) => re.test(e)); } catch { return false; }
|
|
679
|
+
};
|
|
667
680
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
681
|
+
// Detect installed agent harnesses.
|
|
682
|
+
//
|
|
683
|
+
// This used to test for a config *directory* and nothing else, which was
|
|
684
|
+
// circular: syncSkillsToGlobalHarnesses() creates ~/.claude, ~/.codex,
|
|
685
|
+
// ~/.cursor and ~/.roo unconditionally, so after one install the detector
|
|
686
|
+
// found evidence the installer had planted itself. A machine with neither
|
|
687
|
+
// Cursor nor VS Code nor Aider still reported all three, and skills were
|
|
688
|
+
// written into directories nothing would ever read.
|
|
689
|
+
//
|
|
690
|
+
// Evidence now means the application itself and nothing else: a binary on
|
|
691
|
+
// PATH, an installed app bundle, or an editor extension. Config files are
|
|
692
|
+
// deliberately NOT evidence -- the installer writes ~/.claude.json,
|
|
693
|
+
// ~/.cursor/mcp.json, ~/.roo/mcp_settings.json and the rest of them, so
|
|
694
|
+
// trusting those reintroduces the same circularity one level down. (Written
|
|
695
|
+
// after exactly that: an early cut of this fix still "found" Cursor and
|
|
696
|
+
// Windsurf on a machine with neither, via files AOS had planted.)
|
|
697
|
+
// The user's explicit choice counts too -- see markPlatformsExplicit().
|
|
698
|
+
function detectPlatforms() {
|
|
699
|
+
const candidates = [
|
|
700
|
+
{
|
|
701
|
+
key: 'antigravity', name: 'Google Antigravity', path: geminiDir,
|
|
702
|
+
evidence: () => hasExecutable('agy') || anyExists(appBundle('Antigravity')),
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
key: 'codex', name: 'ChatGPT Codex CLI', path: path.join(homeDir, '.codex'),
|
|
706
|
+
evidence: () => hasExecutable('codex'),
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
key: 'claudecode', name: 'Claude Code CLI', path: path.join(homeDir, '.claude'),
|
|
710
|
+
evidence: () => hasExecutable('claude'),
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
key: 'claudedesktop', name: 'Claude Desktop',
|
|
714
|
+
path: process.platform === 'win32'
|
|
715
|
+
? path.join(process.env.APPDATA || homeDir, 'Claude')
|
|
716
|
+
: path.join(homeDir, 'Library', 'Application Support', 'Claude'),
|
|
717
|
+
evidence: () => anyExists(appBundle('Claude')),
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
key: 'cursor', name: 'Cursor IDE',
|
|
721
|
+
path: process.platform === 'win32'
|
|
722
|
+
? path.join(process.env.APPDATA || homeDir, 'Cursor')
|
|
723
|
+
: path.join(homeDir, 'Library', 'Application Support', 'Cursor'),
|
|
724
|
+
evidence: () => anyExists(appBundle('Cursor')) || hasExecutable('cursor'),
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
key: 'windsurf', name: 'Windsurf IDE',
|
|
728
|
+
path: process.platform === 'win32'
|
|
729
|
+
? path.join(process.env.APPDATA || homeDir, 'Windsurf')
|
|
730
|
+
: path.join(homeDir, 'Library', 'Application Support', 'Windsurf'),
|
|
731
|
+
evidence: () => anyExists(appBundle('Windsurf')) || hasExecutable('windsurf'),
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
key: 'vscode', name: 'Roo Code / Cline / VS Code',
|
|
735
|
+
path: process.platform === 'win32'
|
|
736
|
+
? path.join(process.env.APPDATA || homeDir, 'Code')
|
|
737
|
+
: path.join(homeDir, 'Library', 'Application Support', 'Code'),
|
|
738
|
+
evidence: () => anyExists(appBundle('Visual Studio Code'))
|
|
739
|
+
|| hasExecutable('code')
|
|
740
|
+
|| globExists(path.join(homeDir, '.vscode', 'extensions'), /roo|cline/i),
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
key: 'aider', name: 'Aider CLI', path: homeDir,
|
|
744
|
+
evidence: () => hasExecutable('aider'),
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
key: 'opencode', name: 'OpenCode CLI',
|
|
748
|
+
path: process.platform === 'win32'
|
|
749
|
+
? path.join(process.env.APPDATA || homeDir, 'opencode')
|
|
750
|
+
: path.join(homeDir, '.config', 'opencode'),
|
|
751
|
+
evidence: () => hasExecutable('opencode'),
|
|
752
|
+
},
|
|
753
|
+
];
|
|
672
754
|
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
755
|
+
const detections = [];
|
|
756
|
+
for (const c of candidates) {
|
|
757
|
+
let present = false;
|
|
758
|
+
try { present = c.evidence(); } catch (e) { logDebug(e, `detect ${c.key}`); }
|
|
759
|
+
if (present || explicitPlatformKeys.has(c.key)) {
|
|
760
|
+
detections.push({ name: c.name, path: c.path, key: c.key, chosen: !present });
|
|
761
|
+
}
|
|
678
762
|
}
|
|
679
|
-
|
|
680
763
|
return detections;
|
|
681
764
|
}
|
|
682
765
|
|
|
@@ -731,10 +814,9 @@ function detectInstallState() {
|
|
|
731
814
|
{ id: 'synapse', dir: path.join(basePath, 'bdb-synapse') },
|
|
732
815
|
{ id: 'memb', dir: path.join(basePath, 'memB') },
|
|
733
816
|
{ id: 'remote', dir: path.join(basePath, 'bdb-os-remote') },
|
|
734
|
-
|
|
735
|
-
//
|
|
736
|
-
//
|
|
737
|
-
// update loop further down.
|
|
817
|
+
{ id: 'ao', dir: path.join(basePath, 'bdb-agent-orchestrator') },
|
|
818
|
+
// The archived predecessor is deliberately NOT detected: a copy of it
|
|
819
|
+
// should be removed, not carried forward into another install.
|
|
738
820
|
{ id: 'creator', dir: path.join(basePath, 'bdb-dev-creator-extension') },
|
|
739
821
|
{ id: 'installer', dir: path.join(basePath, 'bdb-dev-tool-installer') }
|
|
740
822
|
];
|
|
@@ -932,9 +1014,9 @@ function copyDirRecursiveSync(source, target, excludeList = [], manifest = null,
|
|
|
932
1014
|
// root-level leaf skill dumps into the same flat target, so each subsequent
|
|
933
1015
|
// one silently overwrites the previous one's SKILL.md -- found by tracing
|
|
934
1016
|
// why skills/bdbrainstorm/ and skills/global_config/bdbrainstorm/ both
|
|
935
|
-
// existed with different content; the root-level one (and
|
|
936
|
-
//
|
|
937
|
-
//
|
|
1017
|
+
// existed with different content; the root-level one (and 3 siblings:
|
|
1018
|
+
// github-repo, memb-ingest, synapse-integration-skill) never survived a
|
|
1019
|
+
// global sync intact.
|
|
938
1020
|
function syncSkillEntry(fullPath, dirName, targetSkillDir, excludeSkills) {
|
|
939
1021
|
const isLeafSkill = fs.existsSync(path.join(fullPath, 'SKILL.md'));
|
|
940
1022
|
if (isLeafSkill) {
|
|
@@ -965,15 +1047,20 @@ function syncSkillsToGlobalHarnesses(excludeSkills = []) {
|
|
|
965
1047
|
const skillsBase = path.join(srcDir, 'skills');
|
|
966
1048
|
if (!fs.existsSync(skillsBase)) return;
|
|
967
1049
|
|
|
1050
|
+
// Mirror only into harnesses that are actually present. This list used to
|
|
1051
|
+
// be unconditional, which both wrote skills nobody would read and planted
|
|
1052
|
+
// the very directories detectPlatforms() then read back as proof the
|
|
1053
|
+
// harness existed. ~/.agents is ours and always written.
|
|
1054
|
+
const detectedKeys = new Set(detectPlatforms().map((d) => d.key));
|
|
968
1055
|
const extraSkillDestinations = [
|
|
969
|
-
path.join(homeDir, '.
|
|
970
|
-
path.join(homeDir, '.
|
|
971
|
-
path.join(homeDir, '.codex', 'skills'),
|
|
972
|
-
path.join(homeDir, '.cursor', 'skills'),
|
|
973
|
-
path.join(homeDir, '.roo', 'skills')
|
|
974
|
-
];
|
|
975
|
-
|
|
976
|
-
for (const dest of extraSkillDestinations) {
|
|
1056
|
+
{ dir: path.join(homeDir, '.agents', 'skills'), key: null },
|
|
1057
|
+
{ dir: path.join(homeDir, '.claude', 'skills'), key: 'claudecode' },
|
|
1058
|
+
{ dir: path.join(homeDir, '.codex', 'skills'), key: 'codex' },
|
|
1059
|
+
{ dir: path.join(homeDir, '.cursor', 'skills'), key: 'cursor' },
|
|
1060
|
+
{ dir: path.join(homeDir, '.roo', 'skills'), key: 'vscode' },
|
|
1061
|
+
].filter((d) => d.key === null || detectedKeys.has(d.key) || fs.existsSync(d.dir));
|
|
1062
|
+
|
|
1063
|
+
for (const { dir: dest } of extraSkillDestinations) {
|
|
977
1064
|
try {
|
|
978
1065
|
fs.mkdirSync(dest, { recursive: true });
|
|
979
1066
|
const rawDirs = fs.readdirSync(skillsBase);
|
|
@@ -1394,23 +1481,34 @@ function checkModuleUpdate(pkgName, targetDir) {
|
|
|
1394
1481
|
try {
|
|
1395
1482
|
const localVer = JSON.parse(fs.readFileSync(modulePkgPath, 'utf8')).version;
|
|
1396
1483
|
let remoteVer = null;
|
|
1484
|
+
let checkFailed = false;
|
|
1397
1485
|
try {
|
|
1398
1486
|
remoteVer = execSync(`npm view ${pkgName} version`, { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', timeout: 4000 }).trim();
|
|
1399
|
-
} catch (e) {
|
|
1487
|
+
} catch (e) {
|
|
1488
|
+
// Offline, a proxy, or a misconfigured registry. Swallowing this
|
|
1489
|
+
// made every module report "up to date" forever on such a machine,
|
|
1490
|
+
// indistinguishable from a real answer -- so say which one it is.
|
|
1491
|
+
checkFailed = true;
|
|
1492
|
+
logDebug(e, `npm view ${pkgName}`);
|
|
1493
|
+
}
|
|
1400
1494
|
|
|
1401
1495
|
if (remoteVer && localVer !== remoteVer) {
|
|
1402
|
-
return { installed: true, updateAvailable: true, localVer, remoteVer };
|
|
1496
|
+
return { installed: true, updateAvailable: true, localVer, remoteVer, checkFailed };
|
|
1403
1497
|
}
|
|
1404
|
-
return { installed: true, updateAvailable: false, localVer, remoteVer };
|
|
1498
|
+
return { installed: true, updateAvailable: false, localVer, remoteVer, checkFailed };
|
|
1405
1499
|
} catch (e) {
|
|
1406
|
-
return { installed: true, updateAvailable: false, localVer: null, remoteVer: null };
|
|
1500
|
+
return { installed: true, updateAvailable: false, localVer: null, remoteVer: null, checkFailed: true };
|
|
1407
1501
|
}
|
|
1408
1502
|
}
|
|
1409
1503
|
|
|
1410
1504
|
function downloadOrUpdateModule(pkgName, targetDir, displayName) {
|
|
1411
1505
|
const status = checkModuleUpdate(pkgName, targetDir);
|
|
1412
1506
|
if (status.installed && !status.updateAvailable) {
|
|
1413
|
-
|
|
1507
|
+
if (status.checkFailed) {
|
|
1508
|
+
log.warn(`${displayName}: could not reach the npm registry — keeping the installed v${status.localVer}, which may be stale.`);
|
|
1509
|
+
} else {
|
|
1510
|
+
log.step(`${displayName} is up to date (v${status.localVer})`);
|
|
1511
|
+
}
|
|
1414
1512
|
return true;
|
|
1415
1513
|
}
|
|
1416
1514
|
|
|
@@ -1438,14 +1536,42 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
|
|
|
1438
1536
|
const ok = runNpmWithRetry(`npm pack ${pkgName}@latest`, { stdio: 'ignore', cwd: targetDir }, `${displayName} download`);
|
|
1439
1537
|
cleanNpmCacheOnWindows();
|
|
1440
1538
|
const tarball = ok ? fs.readdirSync(targetDir).find(f => f.endsWith('.tgz')) : null;
|
|
1441
|
-
if (tarball)
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1539
|
+
if (!tarball) throw new Error("NPM pack returned no archive.");
|
|
1540
|
+
|
|
1541
|
+
// Unpacking straight over targetDir made an update a MERGE: a file the
|
|
1542
|
+
// new version dropped stayed on disk forever, while package.json said
|
|
1543
|
+
// the module was current. Unpack beside it and swap, so the tree that
|
|
1544
|
+
// ends up installed is exactly the published one. The previous tree is
|
|
1545
|
+
// kept until the swap succeeds, and restored if it does not.
|
|
1546
|
+
const stagingDir = `${targetDir}.incoming-${timestamp}`;
|
|
1547
|
+
const retiredDir = `${targetDir}.previous-${timestamp}`;
|
|
1548
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
1549
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
1550
|
+
execSync(`tar -xzf "${path.join(targetDir, tarball)}" --strip-components=1 -C "${stagingDir}"`, { stdio: 'ignore' });
|
|
1551
|
+
fs.unlinkSync(path.join(targetDir, tarball));
|
|
1552
|
+
|
|
1553
|
+
// Anything the module generated in place -- venvs, caches, local state
|
|
1554
|
+
// -- is not in the tarball and must survive the swap.
|
|
1555
|
+
const PRESERVE = ['.venv', 'venv', 'node_modules', '.env', 'data', 'logs'];
|
|
1556
|
+
for (const keep of PRESERVE) {
|
|
1557
|
+
const from = path.join(targetDir, keep);
|
|
1558
|
+
if (fs.existsSync(from) && !fs.existsSync(path.join(stagingDir, keep))) {
|
|
1559
|
+
fs.renameSync(from, path.join(stagingDir, keep));
|
|
1560
|
+
}
|
|
1448
1561
|
}
|
|
1562
|
+
|
|
1563
|
+
try {
|
|
1564
|
+
fs.renameSync(targetDir, retiredDir);
|
|
1565
|
+
fs.renameSync(stagingDir, targetDir);
|
|
1566
|
+
} catch (swapError) {
|
|
1567
|
+
if (fs.existsSync(retiredDir) && !fs.existsSync(targetDir)) fs.renameSync(retiredDir, targetDir);
|
|
1568
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
1569
|
+
throw swapError;
|
|
1570
|
+
}
|
|
1571
|
+
fs.rmSync(retiredDir, { recursive: true, force: true });
|
|
1572
|
+
|
|
1573
|
+
s.stop(`${displayName} ready (v${status.remoteVer || 'latest'})`);
|
|
1574
|
+
return true;
|
|
1449
1575
|
} catch (e) {
|
|
1450
1576
|
s.stop(`Failed downloading ${displayName}: ${e.message}`);
|
|
1451
1577
|
return false;
|
|
@@ -1465,7 +1591,14 @@ async function installMemB(interactive) {
|
|
|
1465
1591
|
}));
|
|
1466
1592
|
}
|
|
1467
1593
|
const membDir = path.join(moduleBasePath(), 'memB');
|
|
1468
|
-
|
|
1594
|
+
// A failed download used to fall through into venv setup and daemon
|
|
1595
|
+
// registration, each of which then failed on its own terms or, worse,
|
|
1596
|
+
// "succeeded" against a stale tree -- and the run still ended in a success
|
|
1597
|
+
// banner. Stop at the module that could not be fetched.
|
|
1598
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/memb', membDir, 'memB Vector Engine')) {
|
|
1599
|
+
log.warn('Skipping memB setup: the module could not be downloaded.');
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1469
1602
|
if (DRY_RUN) {
|
|
1470
1603
|
log.step('[dry-run] would bootstrap memB venv + pip requirements');
|
|
1471
1604
|
return;
|
|
@@ -1577,7 +1710,10 @@ async function installMemB(interactive) {
|
|
|
1577
1710
|
|
|
1578
1711
|
async function installSynapse() {
|
|
1579
1712
|
const synapseDir = path.join(moduleBasePath(), 'bdb-synapse');
|
|
1580
|
-
downloadOrUpdateModule('@hybridlabor-api/bdb-synapse', synapseDir, 'BDB Synapse')
|
|
1713
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-synapse', synapseDir, 'BDB Synapse')) {
|
|
1714
|
+
log.warn('Skipping Synapse setup: the module could not be downloaded.');
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1581
1717
|
if (DRY_RUN) {
|
|
1582
1718
|
log.step('[dry-run] would link synapse binary into ~/.local/bin/synapse + setup background daemon');
|
|
1583
1719
|
return;
|
|
@@ -1691,7 +1827,10 @@ async function installSynapse() {
|
|
|
1691
1827
|
|
|
1692
1828
|
async function installCreatorExtension() {
|
|
1693
1829
|
const creatorDir = path.join(moduleBasePath(), 'bdb-dev-creator-extension');
|
|
1694
|
-
downloadOrUpdateModule('@hybridlabor-api/bdb-dev-creator-extension', creatorDir, 'BDB Creator Extension')
|
|
1830
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-dev-creator-extension', creatorDir, 'BDB Creator Extension')) {
|
|
1831
|
+
log.warn('Skipping Creator Extension setup: the module could not be downloaded.');
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1695
1834
|
if (DRY_RUN) {
|
|
1696
1835
|
log.step('[dry-run] would run BDB Creator Extension setup');
|
|
1697
1836
|
return;
|
|
@@ -1709,138 +1848,101 @@ async function installCreatorExtension() {
|
|
|
1709
1848
|
|
|
1710
1849
|
async function installOSRemoteGateway() {
|
|
1711
1850
|
const remoteDir = path.join(moduleBasePath(), 'bdb-os-remote');
|
|
1712
|
-
downloadOrUpdateModule('@hybridlabor-api/bdb-os-remote', remoteDir, 'BDB OS Remote Gateway')
|
|
1851
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-os-remote', remoteDir, 'BDB OS Remote Gateway')) {
|
|
1852
|
+
log.warn('Skipping OS Remote Gateway setup: the module could not be downloaded.');
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1713
1855
|
}
|
|
1714
1856
|
|
|
1715
1857
|
async function installDevToolInstaller() {
|
|
1716
1858
|
const toolInstallerDir = path.join(moduleBasePath(), 'bdb-dev-tool-installer');
|
|
1717
|
-
downloadOrUpdateModule('@hybridlabor-api/bdb-dev-tool-installer', toolInstallerDir, 'BDB Dev Tool Installer')
|
|
1859
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-dev-tool-installer', toolInstallerDir, 'BDB Dev Tool Installer')) {
|
|
1860
|
+
log.warn('Skipping Dev Tool Installer setup: the module could not be downloaded.');
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1718
1863
|
}
|
|
1719
1864
|
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1865
|
+
// AO ships one prebuilt binary: backend/ao-daemon, Mach-O arm64. The package
|
|
1866
|
+
// carries Windows and Linux sources but nothing built for them, and its CLI
|
|
1867
|
+
// shim picks the first candidate path that EXISTS without checking whether it
|
|
1868
|
+
// can run — so on any other platform it hands spawn a Mach-O file and fails
|
|
1869
|
+
// with an exec-format error instead of the "go build" hint it means to give.
|
|
1870
|
+
// Installing it there would be installing something known not to work.
|
|
1871
|
+
function aoSupportedHere() {
|
|
1872
|
+
return process.platform === 'darwin' && process.arch === 'arm64';
|
|
1873
|
+
}
|
|
1728
1874
|
|
|
1729
|
-
|
|
1875
|
+
async function installOSAgentWorkspace() {
|
|
1876
|
+
if (!aoSupportedHere()) {
|
|
1877
|
+
log.warn(`AO ships only a macOS arm64 binary — skipping on ${process.platform}/${process.arch}.`);
|
|
1878
|
+
log.warn('Build it from source instead: github.com/hybridlabor-api/bdb-agent-orchestrator');
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1730
1881
|
|
|
1882
|
+
const osAgentDir = path.join(moduleBasePath(), 'bdb-agent-orchestrator');
|
|
1883
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-agent-orchestrator', osAgentDir, 'BDB Agent Orchestrator')) {
|
|
1884
|
+
log.warn('Skipping AO setup: the module could not be downloaded.');
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1731
1887
|
if (DRY_RUN) {
|
|
1732
|
-
log.step('[dry-run] would link ao
|
|
1888
|
+
log.step('[dry-run] would link ~/.local/bin/ao and run `ao service install`');
|
|
1733
1889
|
return;
|
|
1734
1890
|
}
|
|
1735
1891
|
|
|
1892
|
+
const localBinDir = path.join(homeDir, '.local', 'bin');
|
|
1893
|
+
const binTarget = path.join(localBinDir, 'ao');
|
|
1736
1894
|
const daemonBin = path.join(osAgentDir, 'backend', 'ao-daemon');
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
} catch (e) { logDebug(e, 'operation'); }
|
|
1895
|
+
|
|
1896
|
+
if (!fs.existsSync(daemonBin)) {
|
|
1897
|
+
log.warn(`AO binary missing at ${daemonBin} — package layout changed?`);
|
|
1898
|
+
return;
|
|
1742
1899
|
}
|
|
1743
1900
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
<string>${binTarget}</string>
|
|
1755
|
-
</array>
|
|
1756
|
-
<key>EnvironmentVariables</key>
|
|
1757
|
-
<dict>
|
|
1758
|
-
<key>AO_PORT</key>
|
|
1759
|
-
<string>3101</string>
|
|
1760
|
-
<key>AO_DATA_DIR</key>
|
|
1761
|
-
<string>${path.join(homeDir, '.ao', 'data')}</string>
|
|
1762
|
-
<key>PATH</key>
|
|
1763
|
-
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:${localBinDir}</string>
|
|
1764
|
-
</dict>
|
|
1765
|
-
<key>RunAtLoad</key>
|
|
1766
|
-
<true/>
|
|
1767
|
-
<key>KeepAlive</key>
|
|
1768
|
-
<true/>
|
|
1769
|
-
<key>StandardOutPath</key>
|
|
1770
|
-
<string>${path.join(homeDir, '.ao', 'logs', 'daemon.stdout.log')}</string>
|
|
1771
|
-
<key>StandardErrorPath</key>
|
|
1772
|
-
<string>${path.join(homeDir, '.ao', 'logs', 'daemon.stderr.log')}</string>
|
|
1773
|
-
</dict>
|
|
1774
|
-
</plist>`;
|
|
1775
|
-
try {
|
|
1776
|
-
fs.writeFileSync(plistPath, plistContent);
|
|
1777
|
-
execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: 'ignore' });
|
|
1778
|
-
execSync(`launchctl load -w "${plistPath}" 2>/dev/null || true`, { stdio: 'ignore' });
|
|
1779
|
-
const isListening = await verifyDaemonListening(3101, 'Agent Workspace');
|
|
1780
|
-
if (isListening) {
|
|
1781
|
-
log.success('Agent Workspace LaunchAgent active (Port 3101)');
|
|
1782
|
-
} else {
|
|
1783
|
-
log.warn('Agent Workspace daemon did not respond on Port 3101 within timeout. You may need to start it manually or check for port conflicts.');
|
|
1784
|
-
}
|
|
1785
|
-
} catch (e) { logDebug(e, 'operation'); }
|
|
1901
|
+
installStep('place the ao binary', () => {
|
|
1902
|
+
fs.mkdirSync(localBinDir, { recursive: true });
|
|
1903
|
+
// copyFileSync preserves bytes, so the package's ad-hoc signature
|
|
1904
|
+
// survives. That matters: an unsigned binary at this path is SIGKILLed
|
|
1905
|
+
// by AMFI on launch (exit 137) and reads as a daemon that simply will
|
|
1906
|
+
// not start, with nothing in the log to say why.
|
|
1907
|
+
fs.copyFileSync(daemonBin, binTarget);
|
|
1908
|
+
fs.chmodSync(binTarget, 0o755);
|
|
1909
|
+
log.step(`Installed ao to ${binTarget}`);
|
|
1910
|
+
}, 'AO cannot be started without its binary.');
|
|
1786
1911
|
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
logDebug(e, 'write-access probe on /Applications');
|
|
1792
|
-
appDir = path.join(homeDir, 'Applications', 'BDB Agent Workspace.app');
|
|
1793
|
-
log.step('/Applications is not writable - falling back to ~/Applications');
|
|
1794
|
-
}
|
|
1912
|
+
try {
|
|
1913
|
+
execSync(`codesign -v "${binTarget}"`, { stdio: 'ignore' });
|
|
1914
|
+
} catch {
|
|
1915
|
+
log.warn('The ao binary is not code-signed — AMFI will kill it at launch.');
|
|
1795
1916
|
try {
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
const vbsPath = path.join(startupDir, 'com.bdb.agent-workspace.vbs');
|
|
1824
|
-
const daemonBin = path.join(osAgentDir, 'backend', 'ao-daemon.exe');
|
|
1825
|
-
const runBin = fs.existsSync(daemonBin) ? daemonBin : path.join(osAgentDir, 'backend', 'ao.exe');
|
|
1826
|
-
if (fs.existsSync(runBin)) {
|
|
1827
|
-
const vbsContent = `Set WshShell = CreateObject("WScript.Shell")\r\nWshShell.CurrentDirectory = "${osAgentDir}"\r\nWshShell.Run """${runBin}""", 0, False\r\n`;
|
|
1828
|
-
try {
|
|
1829
|
-
fs.writeFileSync(vbsPath, vbsContent, 'utf-8');
|
|
1830
|
-
spawn('wscript.exe', [vbsPath], { detached: true, stdio: 'ignore' }).unref();
|
|
1831
|
-
const isListening = await verifyDaemonListening(3101, 'Agent Workspace');
|
|
1832
|
-
if (isListening) {
|
|
1833
|
-
log.success('Agent Workspace Windows Background Service registered & started (Port 3101)');
|
|
1834
|
-
} else {
|
|
1835
|
-
log.warn('Agent Workspace Windows daemon did not respond on Port 3101 within timeout. You may need to start it manually or check for port conflicts.');
|
|
1836
|
-
}
|
|
1837
|
-
} catch (e) { logDebug(e, 'windows workspace daemon setup'); }
|
|
1838
|
-
}
|
|
1917
|
+
execSync(`codesign -s - -f "${binTarget}"`, { stdio: 'ignore' });
|
|
1918
|
+
log.step('Signed it ad-hoc.');
|
|
1919
|
+
} catch (e) { log.warn(`Could not sign it: ${e.message}`); }
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
// AO installs its own service, and does it for macOS, Windows and Linux.
|
|
1923
|
+
// AOS used to hand-write a LaunchAgent instead, labelled
|
|
1924
|
+
// com.bdb.agent-workspace, whose ProgramArguments were just the binary with
|
|
1925
|
+
// NO subcommand -- so launchd started `ao`, which prints help and exits,
|
|
1926
|
+
// and with KeepAlive kept restarting it. The daemon never ran, on any
|
|
1927
|
+
// machine that installed AO through AOS. Delegating to the tool removes
|
|
1928
|
+
// both the wrong label and the wrong arguments, and keeps working when the
|
|
1929
|
+
// service definition changes upstream.
|
|
1930
|
+
const legacyPlist = path.join(homeDir, 'Library', 'LaunchAgents', 'com.bdb.agent-workspace.plist');
|
|
1931
|
+
if (fs.existsSync(legacyPlist)) {
|
|
1932
|
+
try { execSync(`launchctl unload "${legacyPlist}" 2>/dev/null`, { stdio: 'ignore' }); } catch (e) { logDebug(e, 'unload legacy ao agent'); }
|
|
1933
|
+
try { fs.unlinkSync(legacyPlist); log.step('Removed the old com.bdb.agent-workspace service.'); } catch (e) { logDebug(e, 'remove legacy plist'); }
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
installStep('register the AO service', () => {
|
|
1937
|
+
execSync(`"${binTarget}" service install`, { stdio: 'ignore' });
|
|
1938
|
+
}, 'Start it by hand with: ao service install');
|
|
1939
|
+
|
|
1940
|
+
if (await verifyDaemonListening(3101, 'AO Orchestrator', 8000)) {
|
|
1941
|
+
log.success('AO Orchestrator running on http://localhost:3101');
|
|
1942
|
+
} else {
|
|
1943
|
+
log.warn('AO did not answer on :3101 — check `ao service status` and `ao service logs`.');
|
|
1839
1944
|
}
|
|
1840
|
-
|
|
1841
|
-
log.step('BDB Agent Workspace WebUI: http://localhost:3101');
|
|
1842
1945
|
}
|
|
1843
|
-
|
|
1844
1946
|
async function promptMemBIngestion(mcpCodeTarget) {
|
|
1845
1947
|
if (isAutoYes || DRY_RUN) return;
|
|
1846
1948
|
|
|
@@ -1883,30 +1985,16 @@ async function promptMemBIngestion(mcpCodeTarget) {
|
|
|
1883
1985
|
}
|
|
1884
1986
|
}
|
|
1885
1987
|
|
|
1886
|
-
async function promptEcosystemHealthScheduler() {
|
|
1887
|
-
if (isAutoYes) return;
|
|
1888
|
-
const doSchedule = pick(await askConfirm({
|
|
1889
|
-
message: 'Enable automated Health Audit Cron Job (2x daily at 01:00 and 12:00)?',
|
|
1890
|
-
initialValue: true
|
|
1891
|
-
}));
|
|
1892
|
-
if (doSchedule) {
|
|
1893
|
-
log.success(`Health Audit Scheduler configured for 01:00 & 12:00. Script: ${path.join(srcDir, 'scripts', 'ecosystem-health-audit.js')}`);
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
1988
|
function verifyEcosystemInstallation() {
|
|
1898
1989
|
const modules = [
|
|
1899
1990
|
{ name: '1. bdb-synapse', pkg: '@hybridlabor-api/bdb-synapse', paths: [path.join(moduleBasePath(), 'bdb-synapse')] },
|
|
1900
1991
|
{ name: '2. memB', pkg: '@hybridlabor-api/memb', paths: [path.join(moduleBasePath(), 'memB'), path.join(geminiDir, 'config', 'mcps', 'memb-mcp')] },
|
|
1901
1992
|
{ name: '3. heimdall-token-saver', pkg: '@hybridlabor-api/heimdall-token-saver', paths: [path.join(moduleBasePath(), 'heimdall-token-saver'), path.join(srcDir, 'vendor', 'token-saver')] },
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
{ name: '
|
|
1907
|
-
{ name: '5. bdb-os-remote', pkg: '@hybridlabor-api/bdb-os-remote', paths: [path.join(moduleBasePath(), 'bdb-os-remote')] },
|
|
1908
|
-
{ name: '6. bdb-dev-tool-installer', pkg: '@hybridlabor-api/bdb-dev-tool-installer', paths: [path.join(moduleBasePath(), 'bdb-dev-tool-installer')] },
|
|
1909
|
-
{ name: '7. aos (bdb agent os)', pkg: '@hybridlabor-api/aos', paths: [srcDir] }
|
|
1993
|
+
{ name: '4. AO Agent Orchestrator', pkg: '@hybridlabor-api/bdb-agent-orchestrator', paths: [path.join(moduleBasePath(), 'bdb-agent-orchestrator')] },
|
|
1994
|
+
{ name: '5. bdb-dev-creator-extension', pkg: '@hybridlabor-api/bdb-dev-creator-extension', paths: [path.join(moduleBasePath(), 'bdb-dev-creator-extension')] },
|
|
1995
|
+
{ name: '6. bdb-os-remote', pkg: '@hybridlabor-api/bdb-os-remote', paths: [path.join(moduleBasePath(), 'bdb-os-remote')] },
|
|
1996
|
+
{ name: '7. bdb-dev-tool-installer', pkg: '@hybridlabor-api/bdb-dev-tool-installer', paths: [path.join(moduleBasePath(), 'bdb-dev-tool-installer')] },
|
|
1997
|
+
{ name: '8. aos (bdb agent os)', pkg: '@hybridlabor-api/aos', paths: [srcDir] }
|
|
1910
1998
|
];
|
|
1911
1999
|
|
|
1912
2000
|
for (const mod of modules) {
|
|
@@ -1949,7 +2037,21 @@ function verifyEcosystemInstallation() {
|
|
|
1949
2037
|
console.log(` • ${colors.bold}${mod.name.padEnd(35)}${colors.reset} ➔ ${colors.green}✅ Installed${colors.reset}`);
|
|
1950
2038
|
}
|
|
1951
2039
|
} else if (mod.name.includes('token-saver') && fs.existsSync(path.join(srcDir, 'vendor', 'token-saver'))) {
|
|
1952
|
-
|
|
2040
|
+
// Was a hardcoded "v2.6.3", which stopped being true the moment the
|
|
2041
|
+
// vendored copy moved. Read it, and say so when it cannot be read.
|
|
2042
|
+
let vendored = null;
|
|
2043
|
+
for (const f of ['package.json', 'pyproject.toml', 'VERSION']) {
|
|
2044
|
+
const vp = path.join(srcDir, 'vendor', 'token-saver', f);
|
|
2045
|
+
if (!fs.existsSync(vp)) continue;
|
|
2046
|
+
try {
|
|
2047
|
+
const raw = fs.readFileSync(vp, 'utf8');
|
|
2048
|
+
vendored = f === 'package.json'
|
|
2049
|
+
? JSON.parse(raw).version
|
|
2050
|
+
: (raw.match(/^\s*version\s*=\s*["']?([\w.\-+]+)/m) || [])[1] || raw.trim().split('\n')[0];
|
|
2051
|
+
} catch (e) { logDebug(e, `token-saver version from ${f}`); }
|
|
2052
|
+
if (vendored) break;
|
|
2053
|
+
}
|
|
2054
|
+
console.log(` • ${colors.bold}${mod.name.padEnd(35)}${colors.reset} ➔ ${colors.green}✅ ${vendored ? `v${vendored} ` : ''}(Integrated)${colors.reset}`);
|
|
1953
2055
|
} else {
|
|
1954
2056
|
console.log(` • ${colors.bold}${mod.name.padEnd(35)}${colors.reset} ➔ ${colors.dim}⚪ Optional / Not downloaded${colors.reset}`);
|
|
1955
2057
|
}
|
|
@@ -2115,7 +2217,13 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2115
2217
|
if (selectedMcps.includes(CORE_MCP)) {
|
|
2116
2218
|
const membMcpTarget = path.join(mcpCodeTarget, CORE_MCP);
|
|
2117
2219
|
installStep(`download/update ${CORE_MCP} from npm`, () => {
|
|
2118
|
-
|
|
2220
|
+
// installStep only catches a throw, and downloadOrUpdateModule
|
|
2221
|
+
// reports failure by returning false -- so without this the hint
|
|
2222
|
+
// below never printed and the MCP config was written pointing at a
|
|
2223
|
+
// path that had not been populated.
|
|
2224
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/memb', membMcpTarget, 'memB MCP')) {
|
|
2225
|
+
throw new Error('npm pack did not produce the memb-mcp payload');
|
|
2226
|
+
}
|
|
2119
2227
|
}, 'memb-mcp installation skipped; MCP config may reference a missing path.');
|
|
2120
2228
|
}
|
|
2121
2229
|
log.step(`Installed selected MCP servers to ${mcpCodeTarget}`);
|
|
@@ -2128,7 +2236,11 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2128
2236
|
const ok = runNpmWithRetry('npm install --no-audit --no-fund', { cwd: targetFolder }, `npm install for ${mcpFolder}`);
|
|
2129
2237
|
if (ok && (fs.existsSync(path.join(targetFolder, 'tsconfig.json')) || fs.existsSync(path.join(targetFolder, 'tsconfig.build.json')))) {
|
|
2130
2238
|
log.step(`Compiling TypeScript for ${mcpFolder}...`);
|
|
2131
|
-
runNpmWithRetry('npm run build', { cwd: targetFolder }, `npm run build for ${mcpFolder}`);
|
|
2239
|
+
const built = runNpmWithRetry('npm run build', { cwd: targetFolder }, `npm run build for ${mcpFolder}`);
|
|
2240
|
+
// runNpmWithRetry already warned with the failure detail; this
|
|
2241
|
+
// just makes the consequence visible now instead of only later,
|
|
2242
|
+
// when the config-entry check below finds the missing dist file.
|
|
2243
|
+
if (!built) log.warn(`${mcpFolder}: build failed -- its MCP config entry will be dropped if the expected output is missing.`);
|
|
2132
2244
|
}
|
|
2133
2245
|
}
|
|
2134
2246
|
}
|
|
@@ -2160,7 +2272,11 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2160
2272
|
? path.join(membMcpFolder, '.venv', 'Scripts', 'python.exe')
|
|
2161
2273
|
: path.join(membMcpFolder, '.venv', 'bin', 'python');
|
|
2162
2274
|
|
|
2163
|
-
|
|
2275
|
+
// The fs.* writes are covered by the global dry-run patch, but
|
|
2276
|
+
// child processes are not -- these two would really create a venv.
|
|
2277
|
+
if (DRY_RUN) {
|
|
2278
|
+
log.step(`[dry-run] would create the memb-mcp venv in ${membMcpFolder}`);
|
|
2279
|
+
} else if (!fs.existsSync(venvPython)) {
|
|
2164
2280
|
try {
|
|
2165
2281
|
execSync(`uv venv --seed .venv`, { cwd: membMcpFolder, stdio: 'ignore' });
|
|
2166
2282
|
} catch (e1) {
|
|
@@ -2178,12 +2294,12 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2178
2294
|
}
|
|
2179
2295
|
|
|
2180
2296
|
const pythonMcps = [
|
|
2181
|
-
{ folder: 'golem-rhino-mcp', args: ['-m', 'mcp_server', '--help'] },
|
|
2182
|
-
{ folder: 'davinci-mcp-professional', args: ['main.py', '--help'] },
|
|
2297
|
+
{ folder: 'golem-rhino-mcp', args: ['-m', 'mcp_server', '--help'], uvArgs: true },
|
|
2298
|
+
{ folder: 'davinci-mcp-professional', args: ['main.py', '--help'], uvArgs: true },
|
|
2183
2299
|
{ folder: 'davinci-resolve-mcp-free', args: ['-r', 'requirements.txt', 'src/resolve_mcp_bridge.py', '--help'], uvArgs: true },
|
|
2184
|
-
{ folder: 'blender-mcp', args: ['-m', 'blender_mcp.server', '--help'] },
|
|
2300
|
+
{ folder: 'blender-mcp', args: ['-m', 'blender_mcp.server', '--help'], uvArgs: true },
|
|
2185
2301
|
{ folder: 'vectorworks-mcp', args: ['-r', 'requirements.txt', 'app/mcp_server.py', '--help'], uvArgs: true },
|
|
2186
|
-
{ folder: 'windows-computer-use-mcp', args: ['run_server.py', '--help'] }
|
|
2302
|
+
{ folder: 'windows-computer-use-mcp', args: ['run_server.py', '--help'], uvArgs: true }
|
|
2187
2303
|
];
|
|
2188
2304
|
for (const mcp of pythonMcps.filter(m => selectedMcps.includes(m.folder))) {
|
|
2189
2305
|
const targetFolder = path.join(mcpCodeTarget, mcp.folder);
|
|
@@ -2193,7 +2309,18 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2193
2309
|
log.step(`[dry-run] would pre-warm Python deps for ${mcp.folder}`);
|
|
2194
2310
|
continue;
|
|
2195
2311
|
}
|
|
2196
|
-
spawnSync('uv', prewarmArgs, { cwd: targetFolder, stdio: 'ignore' });
|
|
2312
|
+
const result = spawnSync('uv', prewarmArgs, { cwd: targetFolder, stdio: 'ignore' });
|
|
2313
|
+
if (result.error) {
|
|
2314
|
+
// uv itself is missing or unrunnable. That is one machine-level
|
|
2315
|
+
// fact, not six per-MCP failures — say it once and stop trying.
|
|
2316
|
+
log.warn(`uv could not be run (${result.error.message}) — skipping the Python prewarm for the remaining MCPs.`);
|
|
2317
|
+
break;
|
|
2318
|
+
}
|
|
2319
|
+
if (result.status === 0) {
|
|
2320
|
+
log.step(`Pre-warmed Python deps for ${mcp.folder}`);
|
|
2321
|
+
} else {
|
|
2322
|
+
log.warn(`Prewarm failed for ${mcp.folder}: uv exited ${result.status}. The MCP is configured but its dependencies may not resolve.`);
|
|
2323
|
+
}
|
|
2197
2324
|
}
|
|
2198
2325
|
}
|
|
2199
2326
|
|
|
@@ -2216,6 +2343,28 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2216
2343
|
const parsedMcpConfig = JSON.parse(mcpConfigStr);
|
|
2217
2344
|
const finalMcpServers = {};
|
|
2218
2345
|
const availableFolders = fs.readdirSync(mcpSrcDir);
|
|
2346
|
+
|
|
2347
|
+
// A node MCP's build can fail (see above) or simply never run for a
|
|
2348
|
+
// dist-based server that was skipped -- either way an entry pointing
|
|
2349
|
+
// at a file that was never produced fails at MCP launch, far from the
|
|
2350
|
+
// real cause. Derive the check from each entry's own args (node's
|
|
2351
|
+
// first arg is always its entry script) instead of a hardcoded list of
|
|
2352
|
+
// MCP names, so a fifth dist-based entry added later is covered too.
|
|
2353
|
+
if (!DRY_RUN) {
|
|
2354
|
+
for (const [key, val] of Object.entries(parsedMcpConfig.mcpServers)) {
|
|
2355
|
+
if (val.command !== 'node' || !Array.isArray(val.args) || !val.args[0]) continue;
|
|
2356
|
+
const entryArg = val.args[0];
|
|
2357
|
+
if (!entryArg.startsWith('__MCPS_DIR__/')) continue;
|
|
2358
|
+
const relPath = entryArg.slice('__MCPS_DIR__/'.length);
|
|
2359
|
+
const mcpFolder = relPath.split('/')[0];
|
|
2360
|
+
if (!selectedMcps.includes(mcpFolder)) continue; // not installed at all, already handled below
|
|
2361
|
+
if (!fs.existsSync(path.join(mcpCodeTarget, relPath))) {
|
|
2362
|
+
log.warn(`${mcpFolder}: build produced no ${relPath.slice(mcpFolder.length + 1)} -- not registering ${key}`);
|
|
2363
|
+
skippedMcpConfigKeys.push(key);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2219
2368
|
for (const [key, val] of Object.entries(parsedMcpConfig.mcpServers)) {
|
|
2220
2369
|
let keep = !skippedMcpConfigKeys.includes(key);
|
|
2221
2370
|
for (const available of availableFolders) {
|
|
@@ -2717,7 +2866,7 @@ function injectHarnessRules() {
|
|
|
2717
2866
|
log.step(`Copied ${dir} to ${targetPath}`);
|
|
2718
2867
|
}
|
|
2719
2868
|
});
|
|
2720
|
-
|
|
2869
|
+
installGlobalHooks();
|
|
2721
2870
|
}, '');
|
|
2722
2871
|
|
|
2723
2872
|
installStep('sync global .agents/', () => {
|
|
@@ -2731,6 +2880,22 @@ function injectHarnessRules() {
|
|
|
2731
2880
|
}
|
|
2732
2881
|
}
|
|
2733
2882
|
|
|
2883
|
+
// Deliver the hook scripts to ~/.claude/hooks/ and wire them in settings.json.
|
|
2884
|
+
// Both the full install and Quick Update funnel through here. Quick Update used
|
|
2885
|
+
// to do neither: it refreshes skills and submodules, but hooks are harness
|
|
2886
|
+
// plumbing rather than skills, so an existing install updating to a version
|
|
2887
|
+
// that adds or changes a hook silently never received it -- which is exactly
|
|
2888
|
+
// what happened to memb-inject.mjs in v4.4.0, on the machines that needed it
|
|
2889
|
+
// most (a fresh install got it; every machine that already had AOS did not).
|
|
2890
|
+
function installGlobalHooks() {
|
|
2891
|
+
const hooksSrc = path.join(srcDir, '.claude', 'hooks');
|
|
2892
|
+
if (fs.existsSync(hooksSrc)) {
|
|
2893
|
+
copyDirRecursiveSync(hooksSrc, path.join(homeDir, '.claude', 'hooks'));
|
|
2894
|
+
log.step(`Installed hooks to ${path.join(homeDir, '.claude', 'hooks')}`);
|
|
2895
|
+
}
|
|
2896
|
+
mergeBdbSettingsHooks(path.join(homeDir, '.claude', 'settings.json'));
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2734
2899
|
// Merge the BDB hooks -- the two gates plus the memB ambient-memory hook --
|
|
2735
2900
|
// into a Claude Code settings.json without clobbering
|
|
2736
2901
|
// user-owned keys (v3.13 audit BLOCKER-3: the harness-dir copy used to
|
|
@@ -2924,10 +3089,13 @@ async function promptOptionalModules(installedModules) {
|
|
|
2924
3089
|
{ id: 'synapse', name: 'BDB Synapse (3D Codebase Visualizer)', fn: installSynapse },
|
|
2925
3090
|
{ id: 'memb', name: 'memB Vector Engine (Local Semantic Memory)', fn: () => installMemB(true) },
|
|
2926
3091
|
{ id: 'remote', name: 'BDB OS Remote Gateway (Zero-Trust Tailscale Multiplexer)', fn: installOSRemoteGateway },
|
|
2927
|
-
//
|
|
2928
|
-
//
|
|
2929
|
-
//
|
|
2930
|
-
//
|
|
3092
|
+
// Offered only where it can actually run -- see aoSupportedHere(). The
|
|
3093
|
+
// predecessor, @hybridlabor-api/bdb-os-agent-workspace, must never come
|
|
3094
|
+
// back: its repository is archived and its last release predates the
|
|
3095
|
+
// archiving, so it hands out the build with the CDC loop defect.
|
|
3096
|
+
...(aoSupportedHere()
|
|
3097
|
+
? [{ id: 'ao', name: 'AO Agent Orchestrator (Session telemetry & WebUI)', fn: installOSAgentWorkspace }]
|
|
3098
|
+
: []),
|
|
2931
3099
|
{ id: 'creator', name: 'BDB Creator Extension (Generative 3D, Video & ComfyUI)', fn: installCreatorExtension },
|
|
2932
3100
|
{ id: 'installer', name: 'BDB Dev Tool Installer (Interactive Hub & CLI Launcher)', fn: installDevToolInstaller }
|
|
2933
3101
|
];
|
|
@@ -3298,6 +3466,17 @@ async function runQuickUpdate(installState) {
|
|
|
3298
3466
|
syncSkillsToGlobalHarnesses(excludeSkills);
|
|
3299
3467
|
s.stop('Skills refreshed');
|
|
3300
3468
|
|
|
3469
|
+
// Everything injectHarnessRules() delivers -- GEMINI.md, the dispatcher
|
|
3470
|
+
// workflows the skills point at, the compiled subagent definitions, the
|
|
3471
|
+
// harness rule files and the gate + memory hooks -- used to be
|
|
3472
|
+
// fresh-install-only. v4.4.1 split just the hooks out of it for Quick
|
|
3473
|
+
// Update; the rest stayed behind, so a machine that only ever quick-updates
|
|
3474
|
+
// kept running first-install agent definitions and dispatcher scripts
|
|
3475
|
+
// forever. Same defect as the hook, one layer up. Run the whole thing.
|
|
3476
|
+
installStep('refresh harness rules, workflows, agents and hooks', () => {
|
|
3477
|
+
injectHarnessRules();
|
|
3478
|
+
}, 'Harness files keep whatever version this machine already had.');
|
|
3479
|
+
|
|
3301
3480
|
// OpenWiki setup only ever ran on a brand-new install (main()'s fresh-install
|
|
3302
3481
|
// branch below) -- an existing install running Quick Update never got offered
|
|
3303
3482
|
// it and never had its daemon schedule refreshed. promptCredentials() already
|
|
@@ -3317,7 +3496,7 @@ async function runQuickUpdate(installState) {
|
|
|
3317
3496
|
if (subId === 'synapse') await installSynapse();
|
|
3318
3497
|
else if (subId === 'memb') await installMemB(false);
|
|
3319
3498
|
else if (subId === 'remote') await installOSRemoteGateway();
|
|
3320
|
-
// 'ao'
|
|
3499
|
+
// 'ao' intentionally skipped here even for
|
|
3321
3500
|
// existing installs -- see promptOptionalModules() for why.
|
|
3322
3501
|
else if (subId === 'creator') await installCreatorExtension();
|
|
3323
3502
|
else if (subId === 'installer') await installDevToolInstaller();
|
|
@@ -3358,7 +3537,10 @@ async function main() {
|
|
|
3358
3537
|
console.log(buildTelemetryCard({ installState, detections, daemonStatus }));
|
|
3359
3538
|
|
|
3360
3539
|
if (DRY_RUN) {
|
|
3361
|
-
|
|
3540
|
+
// Precise on purpose: version comparison still queries the registry, and
|
|
3541
|
+
// npm writes its own cache and logs under ~/.npm doing so. Nothing AOS
|
|
3542
|
+
// owns is touched -- but claiming "no commands executed" was not true.
|
|
3543
|
+
log.warn('DRY-RUN MODE active - nothing AOS owns is written. Read-only registry lookups still run (npm caches under ~/.npm).');
|
|
3362
3544
|
}
|
|
3363
3545
|
if (isAutoYes) {
|
|
3364
3546
|
log.warn('Non-interactive/auto mode (-y): all defaults are accepted automatically.');
|
|
@@ -3450,6 +3632,8 @@ async function main() {
|
|
|
3450
3632
|
let wantsUniversal = true;
|
|
3451
3633
|
let specificPlatforms = ['1'];
|
|
3452
3634
|
|
|
3635
|
+
markPlatformsExplicit(PLATFORMS_ARG || []);
|
|
3636
|
+
|
|
3453
3637
|
if (PLATFORMS_ARG) {
|
|
3454
3638
|
// Explicit targets win over both the menu and the auto-yes default.
|
|
3455
3639
|
// '0' means universal; anything else means exactly those targets, so
|
|
@@ -3489,6 +3673,9 @@ async function main() {
|
|
|
3489
3673
|
});
|
|
3490
3674
|
if (sel === BACK) return 'back';
|
|
3491
3675
|
ctx.selectedPlatforms = sel;
|
|
3676
|
+
// A harness the user names counts as present even when no binary
|
|
3677
|
+
// or app bundle turns up for it.
|
|
3678
|
+
markPlatformsExplicit(sel);
|
|
3492
3679
|
};
|
|
3493
3680
|
|
|
3494
3681
|
const stepMode = async () => {
|
|
@@ -3702,7 +3889,6 @@ async function main() {
|
|
|
3702
3889
|
const justInstalled = await promptOptionalModules(installedModulesForPrompt) || [];
|
|
3703
3890
|
|
|
3704
3891
|
await promptMemBIngestion(path.join(primaryTarget.targetMcpDir, 'mcps'));
|
|
3705
|
-
await promptEcosystemHealthScheduler();
|
|
3706
3892
|
|
|
3707
3893
|
const alreadyHandled = justInstalled.map(id => MODULE_ID_TO_DAEMON_NAME[id]).filter(Boolean);
|
|
3708
3894
|
await reloadDaemons(alreadyHandled);
|
|
@@ -3727,7 +3913,12 @@ if (require.main === module) {
|
|
|
3727
3913
|
|
|
3728
3914
|
// Exported for tests -- requiring installer.js must not launch the TUI.
|
|
3729
3915
|
module.exports = {
|
|
3916
|
+
installOSAgentWorkspace,
|
|
3917
|
+
downloadOrUpdateModule,
|
|
3918
|
+
detectPlatforms,
|
|
3919
|
+
markPlatformsExplicit,
|
|
3730
3920
|
mergeBdbSettingsHooks,
|
|
3921
|
+
installGlobalHooks,
|
|
3731
3922
|
installProjectHarness,
|
|
3732
3923
|
mirrorMcpServersTo,
|
|
3733
3924
|
// Manifest store (exported for verification tests)
|