@hybridlabor-api/aos 4.4.1 → 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.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 +387 -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 +98 -8
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +21 -2
- 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);
|
|
@@ -524,7 +530,7 @@ function resolveFileConflict(sourcePath, targetPath, manifest, knownSourceHashes
|
|
|
524
530
|
// not in refusing to write. A genuine local edit is recoverable from .bak
|
|
525
531
|
// rather than silently outvoting the shipped version forever.
|
|
526
532
|
if (!manifestEntry && !knownSourceHashes.has(diskHash)) {
|
|
527
|
-
const bakPath = `${targetPath}.bak`;
|
|
533
|
+
const bakPath = `${targetPath}.${timestamp}.bak`;
|
|
528
534
|
try { fs.copyFileSync(targetPath, bakPath); } catch (e) {
|
|
529
535
|
log.warn(`[manifest] Could not create backup ${bakPath}: ${e.message}`);
|
|
530
536
|
}
|
|
@@ -549,7 +555,7 @@ function resolveFileConflict(sourcePath, targetPath, manifest, knownSourceHashes
|
|
|
549
555
|
|
|
550
556
|
// Case: ours + user-edited (manifest recorded our hash, but disk now differs).
|
|
551
557
|
if (manifestEntry && diskHash !== manifestEntry.sha256) {
|
|
552
|
-
const bakPath = `${targetPath}.bak`;
|
|
558
|
+
const bakPath = `${targetPath}.${timestamp}.bak`;
|
|
553
559
|
try { fs.copyFileSync(targetPath, bakPath); } catch (e) {
|
|
554
560
|
log.warn(`[manifest] Could not create backup ${bakPath}: ${e.message}`);
|
|
555
561
|
}
|
|
@@ -637,66 +643,123 @@ function resolveMcpsArg(availableMcps) {
|
|
|
637
643
|
return matched;
|
|
638
644
|
}
|
|
639
645
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const claudeCodeDir = path.join(homeDir, '.claude');
|
|
654
|
-
if (fs.existsSync(claudeCodeConfig) || fs.existsSync(claudeCodeDir)) {
|
|
655
|
-
detections.push({ name: "Claude Code CLI", path: claudeCodeDir, key: "claudecode" });
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
const claudePath = process.platform === 'win32'
|
|
659
|
-
? path.join(process.env.APPDATA || homeDir, 'Claude')
|
|
660
|
-
: path.join(homeDir, 'Library', 'Application Support', 'Claude');
|
|
661
|
-
if (fs.existsSync(claudePath)) {
|
|
662
|
-
detections.push({ name: "Claude Desktop", path: claudePath, key: "claudedesktop" });
|
|
663
|
-
}
|
|
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
|
+
};
|
|
664
659
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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);
|
|
670
665
|
}
|
|
666
|
+
}
|
|
671
667
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (
|
|
676
|
-
|
|
677
|
-
}
|
|
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
|
+
}
|
|
678
675
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
const clinePath = path.join(homeDir, '.cline');
|
|
684
|
-
if (fs.existsSync(vscodePath) || fs.existsSync(rooPath) || fs.existsSync(clinePath)) {
|
|
685
|
-
detections.push({ name: "Roo Code / Cline / VS Code", path: vscodePath, key: "vscode" });
|
|
686
|
-
}
|
|
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
|
+
};
|
|
687
680
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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
|
+
];
|
|
692
754
|
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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
|
+
}
|
|
698
762
|
}
|
|
699
|
-
|
|
700
763
|
return detections;
|
|
701
764
|
}
|
|
702
765
|
|
|
@@ -751,10 +814,9 @@ function detectInstallState() {
|
|
|
751
814
|
{ id: 'synapse', dir: path.join(basePath, 'bdb-synapse') },
|
|
752
815
|
{ id: 'memb', dir: path.join(basePath, 'memB') },
|
|
753
816
|
{ id: 'remote', dir: path.join(basePath, 'bdb-os-remote') },
|
|
754
|
-
|
|
755
|
-
//
|
|
756
|
-
//
|
|
757
|
-
// 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.
|
|
758
820
|
{ id: 'creator', dir: path.join(basePath, 'bdb-dev-creator-extension') },
|
|
759
821
|
{ id: 'installer', dir: path.join(basePath, 'bdb-dev-tool-installer') }
|
|
760
822
|
];
|
|
@@ -952,9 +1014,9 @@ function copyDirRecursiveSync(source, target, excludeList = [], manifest = null,
|
|
|
952
1014
|
// root-level leaf skill dumps into the same flat target, so each subsequent
|
|
953
1015
|
// one silently overwrites the previous one's SKILL.md -- found by tracing
|
|
954
1016
|
// why skills/bdbrainstorm/ and skills/global_config/bdbrainstorm/ both
|
|
955
|
-
// existed with different content; the root-level one (and
|
|
956
|
-
//
|
|
957
|
-
//
|
|
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.
|
|
958
1020
|
function syncSkillEntry(fullPath, dirName, targetSkillDir, excludeSkills) {
|
|
959
1021
|
const isLeafSkill = fs.existsSync(path.join(fullPath, 'SKILL.md'));
|
|
960
1022
|
if (isLeafSkill) {
|
|
@@ -985,15 +1047,20 @@ function syncSkillsToGlobalHarnesses(excludeSkills = []) {
|
|
|
985
1047
|
const skillsBase = path.join(srcDir, 'skills');
|
|
986
1048
|
if (!fs.existsSync(skillsBase)) return;
|
|
987
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));
|
|
988
1055
|
const extraSkillDestinations = [
|
|
989
|
-
path.join(homeDir, '.
|
|
990
|
-
path.join(homeDir, '.
|
|
991
|
-
path.join(homeDir, '.codex', 'skills'),
|
|
992
|
-
path.join(homeDir, '.cursor', 'skills'),
|
|
993
|
-
path.join(homeDir, '.roo', 'skills')
|
|
994
|
-
];
|
|
995
|
-
|
|
996
|
-
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) {
|
|
997
1064
|
try {
|
|
998
1065
|
fs.mkdirSync(dest, { recursive: true });
|
|
999
1066
|
const rawDirs = fs.readdirSync(skillsBase);
|
|
@@ -1414,23 +1481,34 @@ function checkModuleUpdate(pkgName, targetDir) {
|
|
|
1414
1481
|
try {
|
|
1415
1482
|
const localVer = JSON.parse(fs.readFileSync(modulePkgPath, 'utf8')).version;
|
|
1416
1483
|
let remoteVer = null;
|
|
1484
|
+
let checkFailed = false;
|
|
1417
1485
|
try {
|
|
1418
1486
|
remoteVer = execSync(`npm view ${pkgName} version`, { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', timeout: 4000 }).trim();
|
|
1419
|
-
} 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
|
+
}
|
|
1420
1494
|
|
|
1421
1495
|
if (remoteVer && localVer !== remoteVer) {
|
|
1422
|
-
return { installed: true, updateAvailable: true, localVer, remoteVer };
|
|
1496
|
+
return { installed: true, updateAvailable: true, localVer, remoteVer, checkFailed };
|
|
1423
1497
|
}
|
|
1424
|
-
return { installed: true, updateAvailable: false, localVer, remoteVer };
|
|
1498
|
+
return { installed: true, updateAvailable: false, localVer, remoteVer, checkFailed };
|
|
1425
1499
|
} catch (e) {
|
|
1426
|
-
return { installed: true, updateAvailable: false, localVer: null, remoteVer: null };
|
|
1500
|
+
return { installed: true, updateAvailable: false, localVer: null, remoteVer: null, checkFailed: true };
|
|
1427
1501
|
}
|
|
1428
1502
|
}
|
|
1429
1503
|
|
|
1430
1504
|
function downloadOrUpdateModule(pkgName, targetDir, displayName) {
|
|
1431
1505
|
const status = checkModuleUpdate(pkgName, targetDir);
|
|
1432
1506
|
if (status.installed && !status.updateAvailable) {
|
|
1433
|
-
|
|
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
|
+
}
|
|
1434
1512
|
return true;
|
|
1435
1513
|
}
|
|
1436
1514
|
|
|
@@ -1458,14 +1536,42 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
|
|
|
1458
1536
|
const ok = runNpmWithRetry(`npm pack ${pkgName}@latest`, { stdio: 'ignore', cwd: targetDir }, `${displayName} download`);
|
|
1459
1537
|
cleanNpmCacheOnWindows();
|
|
1460
1538
|
const tarball = ok ? fs.readdirSync(targetDir).find(f => f.endsWith('.tgz')) : null;
|
|
1461
|
-
if (tarball)
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
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
|
+
}
|
|
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;
|
|
1468
1570
|
}
|
|
1571
|
+
fs.rmSync(retiredDir, { recursive: true, force: true });
|
|
1572
|
+
|
|
1573
|
+
s.stop(`${displayName} ready (v${status.remoteVer || 'latest'})`);
|
|
1574
|
+
return true;
|
|
1469
1575
|
} catch (e) {
|
|
1470
1576
|
s.stop(`Failed downloading ${displayName}: ${e.message}`);
|
|
1471
1577
|
return false;
|
|
@@ -1485,7 +1591,14 @@ async function installMemB(interactive) {
|
|
|
1485
1591
|
}));
|
|
1486
1592
|
}
|
|
1487
1593
|
const membDir = path.join(moduleBasePath(), 'memB');
|
|
1488
|
-
|
|
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
|
+
}
|
|
1489
1602
|
if (DRY_RUN) {
|
|
1490
1603
|
log.step('[dry-run] would bootstrap memB venv + pip requirements');
|
|
1491
1604
|
return;
|
|
@@ -1597,7 +1710,10 @@ async function installMemB(interactive) {
|
|
|
1597
1710
|
|
|
1598
1711
|
async function installSynapse() {
|
|
1599
1712
|
const synapseDir = path.join(moduleBasePath(), 'bdb-synapse');
|
|
1600
|
-
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
|
+
}
|
|
1601
1717
|
if (DRY_RUN) {
|
|
1602
1718
|
log.step('[dry-run] would link synapse binary into ~/.local/bin/synapse + setup background daemon');
|
|
1603
1719
|
return;
|
|
@@ -1711,7 +1827,10 @@ async function installSynapse() {
|
|
|
1711
1827
|
|
|
1712
1828
|
async function installCreatorExtension() {
|
|
1713
1829
|
const creatorDir = path.join(moduleBasePath(), 'bdb-dev-creator-extension');
|
|
1714
|
-
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
|
+
}
|
|
1715
1834
|
if (DRY_RUN) {
|
|
1716
1835
|
log.step('[dry-run] would run BDB Creator Extension setup');
|
|
1717
1836
|
return;
|
|
@@ -1729,138 +1848,101 @@ async function installCreatorExtension() {
|
|
|
1729
1848
|
|
|
1730
1849
|
async function installOSRemoteGateway() {
|
|
1731
1850
|
const remoteDir = path.join(moduleBasePath(), 'bdb-os-remote');
|
|
1732
|
-
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
|
+
}
|
|
1733
1855
|
}
|
|
1734
1856
|
|
|
1735
1857
|
async function installDevToolInstaller() {
|
|
1736
1858
|
const toolInstallerDir = path.join(moduleBasePath(), 'bdb-dev-tool-installer');
|
|
1737
|
-
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
|
+
}
|
|
1738
1863
|
}
|
|
1739
1864
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
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
|
+
}
|
|
1748
1874
|
|
|
1749
|
-
|
|
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
|
+
}
|
|
1750
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
|
+
}
|
|
1751
1887
|
if (DRY_RUN) {
|
|
1752
|
-
log.step('[dry-run] would link ao
|
|
1888
|
+
log.step('[dry-run] would link ~/.local/bin/ao and run `ao service install`');
|
|
1753
1889
|
return;
|
|
1754
1890
|
}
|
|
1755
1891
|
|
|
1892
|
+
const localBinDir = path.join(homeDir, '.local', 'bin');
|
|
1893
|
+
const binTarget = path.join(localBinDir, 'ao');
|
|
1756
1894
|
const daemonBin = path.join(osAgentDir, 'backend', 'ao-daemon');
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
} catch (e) { logDebug(e, 'operation'); }
|
|
1895
|
+
|
|
1896
|
+
if (!fs.existsSync(daemonBin)) {
|
|
1897
|
+
log.warn(`AO binary missing at ${daemonBin} — package layout changed?`);
|
|
1898
|
+
return;
|
|
1762
1899
|
}
|
|
1763
1900
|
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
<string>${binTarget}</string>
|
|
1775
|
-
</array>
|
|
1776
|
-
<key>EnvironmentVariables</key>
|
|
1777
|
-
<dict>
|
|
1778
|
-
<key>AO_PORT</key>
|
|
1779
|
-
<string>3101</string>
|
|
1780
|
-
<key>AO_DATA_DIR</key>
|
|
1781
|
-
<string>${path.join(homeDir, '.ao', 'data')}</string>
|
|
1782
|
-
<key>PATH</key>
|
|
1783
|
-
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:${localBinDir}</string>
|
|
1784
|
-
</dict>
|
|
1785
|
-
<key>RunAtLoad</key>
|
|
1786
|
-
<true/>
|
|
1787
|
-
<key>KeepAlive</key>
|
|
1788
|
-
<true/>
|
|
1789
|
-
<key>StandardOutPath</key>
|
|
1790
|
-
<string>${path.join(homeDir, '.ao', 'logs', 'daemon.stdout.log')}</string>
|
|
1791
|
-
<key>StandardErrorPath</key>
|
|
1792
|
-
<string>${path.join(homeDir, '.ao', 'logs', 'daemon.stderr.log')}</string>
|
|
1793
|
-
</dict>
|
|
1794
|
-
</plist>`;
|
|
1795
|
-
try {
|
|
1796
|
-
fs.writeFileSync(plistPath, plistContent);
|
|
1797
|
-
execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: 'ignore' });
|
|
1798
|
-
execSync(`launchctl load -w "${plistPath}" 2>/dev/null || true`, { stdio: 'ignore' });
|
|
1799
|
-
const isListening = await verifyDaemonListening(3101, 'Agent Workspace');
|
|
1800
|
-
if (isListening) {
|
|
1801
|
-
log.success('Agent Workspace LaunchAgent active (Port 3101)');
|
|
1802
|
-
} else {
|
|
1803
|
-
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.');
|
|
1804
|
-
}
|
|
1805
|
-
} 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.');
|
|
1806
1911
|
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
logDebug(e, 'write-access probe on /Applications');
|
|
1812
|
-
appDir = path.join(homeDir, 'Applications', 'BDB Agent Workspace.app');
|
|
1813
|
-
log.step('/Applications is not writable - falling back to ~/Applications');
|
|
1814
|
-
}
|
|
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.');
|
|
1815
1916
|
try {
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
const vbsPath = path.join(startupDir, 'com.bdb.agent-workspace.vbs');
|
|
1844
|
-
const daemonBin = path.join(osAgentDir, 'backend', 'ao-daemon.exe');
|
|
1845
|
-
const runBin = fs.existsSync(daemonBin) ? daemonBin : path.join(osAgentDir, 'backend', 'ao.exe');
|
|
1846
|
-
if (fs.existsSync(runBin)) {
|
|
1847
|
-
const vbsContent = `Set WshShell = CreateObject("WScript.Shell")\r\nWshShell.CurrentDirectory = "${osAgentDir}"\r\nWshShell.Run """${runBin}""", 0, False\r\n`;
|
|
1848
|
-
try {
|
|
1849
|
-
fs.writeFileSync(vbsPath, vbsContent, 'utf-8');
|
|
1850
|
-
spawn('wscript.exe', [vbsPath], { detached: true, stdio: 'ignore' }).unref();
|
|
1851
|
-
const isListening = await verifyDaemonListening(3101, 'Agent Workspace');
|
|
1852
|
-
if (isListening) {
|
|
1853
|
-
log.success('Agent Workspace Windows Background Service registered & started (Port 3101)');
|
|
1854
|
-
} else {
|
|
1855
|
-
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.');
|
|
1856
|
-
}
|
|
1857
|
-
} catch (e) { logDebug(e, 'windows workspace daemon setup'); }
|
|
1858
|
-
}
|
|
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`.');
|
|
1859
1944
|
}
|
|
1860
|
-
|
|
1861
|
-
log.step('BDB Agent Workspace WebUI: http://localhost:3101');
|
|
1862
1945
|
}
|
|
1863
|
-
|
|
1864
1946
|
async function promptMemBIngestion(mcpCodeTarget) {
|
|
1865
1947
|
if (isAutoYes || DRY_RUN) return;
|
|
1866
1948
|
|
|
@@ -1903,30 +1985,16 @@ async function promptMemBIngestion(mcpCodeTarget) {
|
|
|
1903
1985
|
}
|
|
1904
1986
|
}
|
|
1905
1987
|
|
|
1906
|
-
async function promptEcosystemHealthScheduler() {
|
|
1907
|
-
if (isAutoYes) return;
|
|
1908
|
-
const doSchedule = pick(await askConfirm({
|
|
1909
|
-
message: 'Enable automated Health Audit Cron Job (2x daily at 01:00 and 12:00)?',
|
|
1910
|
-
initialValue: true
|
|
1911
|
-
}));
|
|
1912
|
-
if (doSchedule) {
|
|
1913
|
-
log.success(`Health Audit Scheduler configured for 01:00 & 12:00. Script: ${path.join(srcDir, 'scripts', 'ecosystem-health-audit.js')}`);
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
1988
|
function verifyEcosystemInstallation() {
|
|
1918
1989
|
const modules = [
|
|
1919
1990
|
{ name: '1. bdb-synapse', pkg: '@hybridlabor-api/bdb-synapse', paths: [path.join(moduleBasePath(), 'bdb-synapse')] },
|
|
1920
1991
|
{ name: '2. memB', pkg: '@hybridlabor-api/memb', paths: [path.join(moduleBasePath(), 'memB'), path.join(geminiDir, 'config', 'mcps', 'memb-mcp')] },
|
|
1921
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')] },
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
{ name: '
|
|
1927
|
-
{ name: '5. bdb-os-remote', pkg: '@hybridlabor-api/bdb-os-remote', paths: [path.join(moduleBasePath(), 'bdb-os-remote')] },
|
|
1928
|
-
{ name: '6. bdb-dev-tool-installer', pkg: '@hybridlabor-api/bdb-dev-tool-installer', paths: [path.join(moduleBasePath(), 'bdb-dev-tool-installer')] },
|
|
1929
|
-
{ 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] }
|
|
1930
1998
|
];
|
|
1931
1999
|
|
|
1932
2000
|
for (const mod of modules) {
|
|
@@ -1969,7 +2037,21 @@ function verifyEcosystemInstallation() {
|
|
|
1969
2037
|
console.log(` • ${colors.bold}${mod.name.padEnd(35)}${colors.reset} ➔ ${colors.green}✅ Installed${colors.reset}`);
|
|
1970
2038
|
}
|
|
1971
2039
|
} else if (mod.name.includes('token-saver') && fs.existsSync(path.join(srcDir, 'vendor', 'token-saver'))) {
|
|
1972
|
-
|
|
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}`);
|
|
1973
2055
|
} else {
|
|
1974
2056
|
console.log(` • ${colors.bold}${mod.name.padEnd(35)}${colors.reset} ➔ ${colors.dim}⚪ Optional / Not downloaded${colors.reset}`);
|
|
1975
2057
|
}
|
|
@@ -2135,7 +2217,13 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2135
2217
|
if (selectedMcps.includes(CORE_MCP)) {
|
|
2136
2218
|
const membMcpTarget = path.join(mcpCodeTarget, CORE_MCP);
|
|
2137
2219
|
installStep(`download/update ${CORE_MCP} from npm`, () => {
|
|
2138
|
-
|
|
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
|
+
}
|
|
2139
2227
|
}, 'memb-mcp installation skipped; MCP config may reference a missing path.');
|
|
2140
2228
|
}
|
|
2141
2229
|
log.step(`Installed selected MCP servers to ${mcpCodeTarget}`);
|
|
@@ -2148,7 +2236,11 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2148
2236
|
const ok = runNpmWithRetry('npm install --no-audit --no-fund', { cwd: targetFolder }, `npm install for ${mcpFolder}`);
|
|
2149
2237
|
if (ok && (fs.existsSync(path.join(targetFolder, 'tsconfig.json')) || fs.existsSync(path.join(targetFolder, 'tsconfig.build.json')))) {
|
|
2150
2238
|
log.step(`Compiling TypeScript for ${mcpFolder}...`);
|
|
2151
|
-
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.`);
|
|
2152
2244
|
}
|
|
2153
2245
|
}
|
|
2154
2246
|
}
|
|
@@ -2180,7 +2272,11 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2180
2272
|
? path.join(membMcpFolder, '.venv', 'Scripts', 'python.exe')
|
|
2181
2273
|
: path.join(membMcpFolder, '.venv', 'bin', 'python');
|
|
2182
2274
|
|
|
2183
|
-
|
|
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)) {
|
|
2184
2280
|
try {
|
|
2185
2281
|
execSync(`uv venv --seed .venv`, { cwd: membMcpFolder, stdio: 'ignore' });
|
|
2186
2282
|
} catch (e1) {
|
|
@@ -2198,12 +2294,12 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2198
2294
|
}
|
|
2199
2295
|
|
|
2200
2296
|
const pythonMcps = [
|
|
2201
|
-
{ folder: 'golem-rhino-mcp', args: ['-m', 'mcp_server', '--help'] },
|
|
2202
|
-
{ 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 },
|
|
2203
2299
|
{ folder: 'davinci-resolve-mcp-free', args: ['-r', 'requirements.txt', 'src/resolve_mcp_bridge.py', '--help'], uvArgs: true },
|
|
2204
|
-
{ folder: 'blender-mcp', args: ['-m', 'blender_mcp.server', '--help'] },
|
|
2300
|
+
{ folder: 'blender-mcp', args: ['-m', 'blender_mcp.server', '--help'], uvArgs: true },
|
|
2205
2301
|
{ folder: 'vectorworks-mcp', args: ['-r', 'requirements.txt', 'app/mcp_server.py', '--help'], uvArgs: true },
|
|
2206
|
-
{ folder: 'windows-computer-use-mcp', args: ['run_server.py', '--help'] }
|
|
2302
|
+
{ folder: 'windows-computer-use-mcp', args: ['run_server.py', '--help'], uvArgs: true }
|
|
2207
2303
|
];
|
|
2208
2304
|
for (const mcp of pythonMcps.filter(m => selectedMcps.includes(m.folder))) {
|
|
2209
2305
|
const targetFolder = path.join(mcpCodeTarget, mcp.folder);
|
|
@@ -2213,7 +2309,18 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2213
2309
|
log.step(`[dry-run] would pre-warm Python deps for ${mcp.folder}`);
|
|
2214
2310
|
continue;
|
|
2215
2311
|
}
|
|
2216
|
-
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
|
+
}
|
|
2217
2324
|
}
|
|
2218
2325
|
}
|
|
2219
2326
|
|
|
@@ -2236,6 +2343,28 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2236
2343
|
const parsedMcpConfig = JSON.parse(mcpConfigStr);
|
|
2237
2344
|
const finalMcpServers = {};
|
|
2238
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
|
+
|
|
2239
2368
|
for (const [key, val] of Object.entries(parsedMcpConfig.mcpServers)) {
|
|
2240
2369
|
let keep = !skippedMcpConfigKeys.includes(key);
|
|
2241
2370
|
for (const available of availableFolders) {
|
|
@@ -2960,10 +3089,13 @@ async function promptOptionalModules(installedModules) {
|
|
|
2960
3089
|
{ id: 'synapse', name: 'BDB Synapse (3D Codebase Visualizer)', fn: installSynapse },
|
|
2961
3090
|
{ id: 'memb', name: 'memB Vector Engine (Local Semantic Memory)', fn: () => installMemB(true) },
|
|
2962
3091
|
{ id: 'remote', name: 'BDB OS Remote Gateway (Zero-Trust Tailscale Multiplexer)', fn: installOSRemoteGateway },
|
|
2963
|
-
//
|
|
2964
|
-
//
|
|
2965
|
-
//
|
|
2966
|
-
//
|
|
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
|
+
: []),
|
|
2967
3099
|
{ id: 'creator', name: 'BDB Creator Extension (Generative 3D, Video & ComfyUI)', fn: installCreatorExtension },
|
|
2968
3100
|
{ id: 'installer', name: 'BDB Dev Tool Installer (Interactive Hub & CLI Launcher)', fn: installDevToolInstaller }
|
|
2969
3101
|
];
|
|
@@ -3334,9 +3466,16 @@ async function runQuickUpdate(installState) {
|
|
|
3334
3466
|
syncSkillsToGlobalHarnesses(excludeSkills);
|
|
3335
3467
|
s.stop('Skills refreshed');
|
|
3336
3468
|
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
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.');
|
|
3340
3479
|
|
|
3341
3480
|
// OpenWiki setup only ever ran on a brand-new install (main()'s fresh-install
|
|
3342
3481
|
// branch below) -- an existing install running Quick Update never got offered
|
|
@@ -3357,7 +3496,7 @@ async function runQuickUpdate(installState) {
|
|
|
3357
3496
|
if (subId === 'synapse') await installSynapse();
|
|
3358
3497
|
else if (subId === 'memb') await installMemB(false);
|
|
3359
3498
|
else if (subId === 'remote') await installOSRemoteGateway();
|
|
3360
|
-
// 'ao'
|
|
3499
|
+
// 'ao' intentionally skipped here even for
|
|
3361
3500
|
// existing installs -- see promptOptionalModules() for why.
|
|
3362
3501
|
else if (subId === 'creator') await installCreatorExtension();
|
|
3363
3502
|
else if (subId === 'installer') await installDevToolInstaller();
|
|
@@ -3398,7 +3537,10 @@ async function main() {
|
|
|
3398
3537
|
console.log(buildTelemetryCard({ installState, detections, daemonStatus }));
|
|
3399
3538
|
|
|
3400
3539
|
if (DRY_RUN) {
|
|
3401
|
-
|
|
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).');
|
|
3402
3544
|
}
|
|
3403
3545
|
if (isAutoYes) {
|
|
3404
3546
|
log.warn('Non-interactive/auto mode (-y): all defaults are accepted automatically.');
|
|
@@ -3490,6 +3632,8 @@ async function main() {
|
|
|
3490
3632
|
let wantsUniversal = true;
|
|
3491
3633
|
let specificPlatforms = ['1'];
|
|
3492
3634
|
|
|
3635
|
+
markPlatformsExplicit(PLATFORMS_ARG || []);
|
|
3636
|
+
|
|
3493
3637
|
if (PLATFORMS_ARG) {
|
|
3494
3638
|
// Explicit targets win over both the menu and the auto-yes default.
|
|
3495
3639
|
// '0' means universal; anything else means exactly those targets, so
|
|
@@ -3529,6 +3673,9 @@ async function main() {
|
|
|
3529
3673
|
});
|
|
3530
3674
|
if (sel === BACK) return 'back';
|
|
3531
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);
|
|
3532
3679
|
};
|
|
3533
3680
|
|
|
3534
3681
|
const stepMode = async () => {
|
|
@@ -3742,7 +3889,6 @@ async function main() {
|
|
|
3742
3889
|
const justInstalled = await promptOptionalModules(installedModulesForPrompt) || [];
|
|
3743
3890
|
|
|
3744
3891
|
await promptMemBIngestion(path.join(primaryTarget.targetMcpDir, 'mcps'));
|
|
3745
|
-
await promptEcosystemHealthScheduler();
|
|
3746
3892
|
|
|
3747
3893
|
const alreadyHandled = justInstalled.map(id => MODULE_ID_TO_DAEMON_NAME[id]).filter(Boolean);
|
|
3748
3894
|
await reloadDaemons(alreadyHandled);
|
|
@@ -3767,6 +3913,10 @@ if (require.main === module) {
|
|
|
3767
3913
|
|
|
3768
3914
|
// Exported for tests -- requiring installer.js must not launch the TUI.
|
|
3769
3915
|
module.exports = {
|
|
3916
|
+
installOSAgentWorkspace,
|
|
3917
|
+
downloadOrUpdateModule,
|
|
3918
|
+
detectPlatforms,
|
|
3919
|
+
markPlatformsExplicit,
|
|
3770
3920
|
mergeBdbSettingsHooks,
|
|
3771
3921
|
installGlobalHooks,
|
|
3772
3922
|
installProjectHarness,
|