@hybridlabor-api/aos 4.4.1 → 4.4.2-beta.1
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 +471 -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
|
}
|
|
@@ -602,6 +608,88 @@ function initSessionManifest(existingManifest, sourceDirs) {
|
|
|
602
608
|
_sessionHashes = buildKnownSourceHashes(sourceDirs);
|
|
603
609
|
}
|
|
604
610
|
|
|
611
|
+
// Skills that left the package in 4.4.2 (Plan 02: migrated to the private
|
|
612
|
+
// aos-internal repo, per ~/dev/_plans/aos-2026-09-14/), by their flattened
|
|
613
|
+
// directory name at a sync destination.
|
|
614
|
+
//
|
|
615
|
+
// This is deliberately a fixed list, not a diff against what the current
|
|
616
|
+
// payload ships. A generic "anything on disk that is not in skills/ right
|
|
617
|
+
// now" pass was tried first and swept up two unrelated things: skill folders
|
|
618
|
+
// that MCP servers bundle inside their own package tree (e.g.
|
|
619
|
+
// mcps/tdmcp/.agents/skills/...), which happen to share the literal path
|
|
620
|
+
// segment "skills" but belong to a different install entirely; and skills
|
|
621
|
+
// under skills/workspace_agents/, which syncSkillsToGlobalHarnesses already
|
|
622
|
+
// deliberately excludes from the six global destinations, so a stray global
|
|
623
|
+
// copy of one says nothing about whether it is safe to delete. Both are real
|
|
624
|
+
// cleanup opportunities, but they are a separate investigation, not a side
|
|
625
|
+
// effect of this migration's beta test.
|
|
626
|
+
const SKILLS_REMOVED_IN_4_4_2 = new Set([
|
|
627
|
+
'bdbsaastraining', 'bdb-dev-os-skill', 'bdbsaashost', 'bdb-ecosystem-health', 'bdbsaas-ops',
|
|
628
|
+
]);
|
|
629
|
+
|
|
630
|
+
// The exact roots syncSkillsToGlobalHarnesses and the Gemini/Antigravity
|
|
631
|
+
// config-skills path write into. Pruning only touches a manifest entry whose
|
|
632
|
+
// path starts under one of these -- never one that merely contains a
|
|
633
|
+
// "skills" segment somewhere deeper, which is what let the MCP-bundle and
|
|
634
|
+
// workspace_agents cases above leak in during testing.
|
|
635
|
+
function globalSkillDestRoots() {
|
|
636
|
+
return [
|
|
637
|
+
path.join(homeDir, '.agents', 'skills'),
|
|
638
|
+
path.join(homeDir, '.claude', 'skills'),
|
|
639
|
+
path.join(homeDir, '.codex', 'skills'),
|
|
640
|
+
path.join(homeDir, '.cursor', 'skills'),
|
|
641
|
+
path.join(homeDir, '.roo', 'skills'),
|
|
642
|
+
path.join(geminiDir, 'config', 'skills'),
|
|
643
|
+
];
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// syncSkillsToGlobalHarnesses (and its sibling copy loops) only ever add or
|
|
647
|
+
// update -- nothing in that path ever deletes a skill that the current
|
|
648
|
+
// payload no longer ships. That is fine for new skills and updated ones, but
|
|
649
|
+
// it means a skill removed from a release stays on every user's disk
|
|
650
|
+
// forever, through every future update, because nothing after the removal
|
|
651
|
+
// ever looks at what is on disk that no longer has a source. This walks the
|
|
652
|
+
// install manifest -- which already has one entry per file this installer
|
|
653
|
+
// has ever placed -- for the specific skills 4.4.2 removed, under the six
|
|
654
|
+
// known global skill roots, and deletes them. A user-modified file is backed
|
|
655
|
+
// up first, the same way resolveFileConflict backs up a user edit it is
|
|
656
|
+
// about to overwrite, rather than silently deleting local changes.
|
|
657
|
+
function pruneRemovedSkills(manifest) {
|
|
658
|
+
if (!manifest) return;
|
|
659
|
+
const roots = globalSkillDestRoots();
|
|
660
|
+
|
|
661
|
+
const staleDirs = new Set();
|
|
662
|
+
let removedFiles = 0;
|
|
663
|
+
for (const targetPath of Object.keys(manifest)) {
|
|
664
|
+
const root = roots.find((r) => targetPath.startsWith(r + path.sep));
|
|
665
|
+
if (!root) continue;
|
|
666
|
+
const skillDirName = targetPath.slice(root.length + 1).split(path.sep)[0];
|
|
667
|
+
if (!SKILLS_REMOVED_IN_4_4_2.has(skillDirName)) continue;
|
|
668
|
+
|
|
669
|
+
if (fs.existsSync(targetPath)) {
|
|
670
|
+
const diskHash = computeFileHash(targetPath);
|
|
671
|
+
const manifestEntry = manifest[targetPath];
|
|
672
|
+
if (diskHash !== null && manifestEntry && diskHash !== manifestEntry.sha256) {
|
|
673
|
+
const bakPath = `${targetPath}.${timestamp}.bak`;
|
|
674
|
+
try { fs.copyFileSync(targetPath, bakPath); } catch (e) { logDebug(e, 'prune-removed-skill backup'); }
|
|
675
|
+
log.warn(`[manifest] Retired skill left a user-edited file, backed up to ${bakPath} instead of deleting it.`);
|
|
676
|
+
} else {
|
|
677
|
+
try { fs.unlinkSync(targetPath); removedFiles++; } catch (e) { logDebug(e, 'prune-removed-skill unlink'); }
|
|
678
|
+
}
|
|
679
|
+
staleDirs.add(path.join(root, skillDirName));
|
|
680
|
+
}
|
|
681
|
+
delete manifest[targetPath];
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
for (const dir of staleDirs) {
|
|
685
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); } catch (e) { logDebug(e, 'prune-removed-skill rmdir'); }
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (staleDirs.size > 0) {
|
|
689
|
+
log.step(`Removed ${removedFiles} file(s) from ${staleDirs.size} retired skill install(s) no longer shipped.`);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
605
693
|
function flushSessionManifest() {
|
|
606
694
|
if (_sessionManifest) saveInstallManifest(_sessionManifest);
|
|
607
695
|
}
|
|
@@ -637,66 +725,123 @@ function resolveMcpsArg(availableMcps) {
|
|
|
637
725
|
return matched;
|
|
638
726
|
}
|
|
639
727
|
|
|
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
|
-
}
|
|
728
|
+
// Platforms the user explicitly asked for, by detection key. A harness the
|
|
729
|
+
// user names is installed as far as we are concerned, whether or not we can
|
|
730
|
+
// find its binary -- they know their machine better than a PATH lookup does.
|
|
731
|
+
const explicitPlatformKeys = new Set();
|
|
732
|
+
const PLATFORM_KEYS_BY_OPTION = {
|
|
733
|
+
'1': 'antigravity',
|
|
734
|
+
'2': ['claudecode', 'claudedesktop'],
|
|
735
|
+
'3': 'cursor',
|
|
736
|
+
'5': 'codex',
|
|
737
|
+
'6': 'windsurf',
|
|
738
|
+
'7': 'vscode',
|
|
739
|
+
'8': 'aider',
|
|
740
|
+
};
|
|
664
741
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
742
|
+
function markPlatformsExplicit(optionValues) {
|
|
743
|
+
for (const v of optionValues || []) {
|
|
744
|
+
const keys = PLATFORM_KEYS_BY_OPTION[v];
|
|
745
|
+
if (!keys) continue;
|
|
746
|
+
for (const k of [].concat(keys)) explicitPlatformKeys.add(k);
|
|
670
747
|
}
|
|
748
|
+
}
|
|
671
749
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (
|
|
676
|
-
|
|
677
|
-
}
|
|
750
|
+
// Where an application itself lives, per OS. Presence of the app is proof;
|
|
751
|
+
// presence of a dot-directory is not.
|
|
752
|
+
function appBundle(name) {
|
|
753
|
+
if (process.platform === 'darwin') return [`/Applications/${name}.app`, path.join(homeDir, 'Applications', `${name}.app`)];
|
|
754
|
+
if (process.platform === 'win32') return [path.join(process.env.LOCALAPPDATA || homeDir, 'Programs', name)];
|
|
755
|
+
return [`/usr/share/${name.toLowerCase()}`, `/opt/${name.toLowerCase()}`];
|
|
756
|
+
}
|
|
678
757
|
|
|
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
|
-
}
|
|
758
|
+
const anyExists = (paths) => paths.some((p) => fs.existsSync(p));
|
|
759
|
+
const globExists = (dir, re) => {
|
|
760
|
+
try { return fs.readdirSync(dir).some((e) => re.test(e)); } catch { return false; }
|
|
761
|
+
};
|
|
687
762
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
763
|
+
// Detect installed agent harnesses.
|
|
764
|
+
//
|
|
765
|
+
// This used to test for a config *directory* and nothing else, which was
|
|
766
|
+
// circular: syncSkillsToGlobalHarnesses() creates ~/.claude, ~/.codex,
|
|
767
|
+
// ~/.cursor and ~/.roo unconditionally, so after one install the detector
|
|
768
|
+
// found evidence the installer had planted itself. A machine with neither
|
|
769
|
+
// Cursor nor VS Code nor Aider still reported all three, and skills were
|
|
770
|
+
// written into directories nothing would ever read.
|
|
771
|
+
//
|
|
772
|
+
// Evidence now means the application itself and nothing else: a binary on
|
|
773
|
+
// PATH, an installed app bundle, or an editor extension. Config files are
|
|
774
|
+
// deliberately NOT evidence -- the installer writes ~/.claude.json,
|
|
775
|
+
// ~/.cursor/mcp.json, ~/.roo/mcp_settings.json and the rest of them, so
|
|
776
|
+
// trusting those reintroduces the same circularity one level down. (Written
|
|
777
|
+
// after exactly that: an early cut of this fix still "found" Cursor and
|
|
778
|
+
// Windsurf on a machine with neither, via files AOS had planted.)
|
|
779
|
+
// The user's explicit choice counts too -- see markPlatformsExplicit().
|
|
780
|
+
function detectPlatforms() {
|
|
781
|
+
const candidates = [
|
|
782
|
+
{
|
|
783
|
+
key: 'antigravity', name: 'Google Antigravity', path: geminiDir,
|
|
784
|
+
evidence: () => hasExecutable('agy') || anyExists(appBundle('Antigravity')),
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
key: 'codex', name: 'ChatGPT Codex CLI', path: path.join(homeDir, '.codex'),
|
|
788
|
+
evidence: () => hasExecutable('codex'),
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
key: 'claudecode', name: 'Claude Code CLI', path: path.join(homeDir, '.claude'),
|
|
792
|
+
evidence: () => hasExecutable('claude'),
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
key: 'claudedesktop', name: 'Claude Desktop',
|
|
796
|
+
path: process.platform === 'win32'
|
|
797
|
+
? path.join(process.env.APPDATA || homeDir, 'Claude')
|
|
798
|
+
: path.join(homeDir, 'Library', 'Application Support', 'Claude'),
|
|
799
|
+
evidence: () => anyExists(appBundle('Claude')),
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
key: 'cursor', name: 'Cursor IDE',
|
|
803
|
+
path: process.platform === 'win32'
|
|
804
|
+
? path.join(process.env.APPDATA || homeDir, 'Cursor')
|
|
805
|
+
: path.join(homeDir, 'Library', 'Application Support', 'Cursor'),
|
|
806
|
+
evidence: () => anyExists(appBundle('Cursor')) || hasExecutable('cursor'),
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
key: 'windsurf', name: 'Windsurf IDE',
|
|
810
|
+
path: process.platform === 'win32'
|
|
811
|
+
? path.join(process.env.APPDATA || homeDir, 'Windsurf')
|
|
812
|
+
: path.join(homeDir, 'Library', 'Application Support', 'Windsurf'),
|
|
813
|
+
evidence: () => anyExists(appBundle('Windsurf')) || hasExecutable('windsurf'),
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
key: 'vscode', name: 'Roo Code / Cline / VS Code',
|
|
817
|
+
path: process.platform === 'win32'
|
|
818
|
+
? path.join(process.env.APPDATA || homeDir, 'Code')
|
|
819
|
+
: path.join(homeDir, 'Library', 'Application Support', 'Code'),
|
|
820
|
+
evidence: () => anyExists(appBundle('Visual Studio Code'))
|
|
821
|
+
|| hasExecutable('code')
|
|
822
|
+
|| globExists(path.join(homeDir, '.vscode', 'extensions'), /roo|cline/i),
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
key: 'aider', name: 'Aider CLI', path: homeDir,
|
|
826
|
+
evidence: () => hasExecutable('aider'),
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
key: 'opencode', name: 'OpenCode CLI',
|
|
830
|
+
path: process.platform === 'win32'
|
|
831
|
+
? path.join(process.env.APPDATA || homeDir, 'opencode')
|
|
832
|
+
: path.join(homeDir, '.config', 'opencode'),
|
|
833
|
+
evidence: () => hasExecutable('opencode'),
|
|
834
|
+
},
|
|
835
|
+
];
|
|
692
836
|
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
837
|
+
const detections = [];
|
|
838
|
+
for (const c of candidates) {
|
|
839
|
+
let present = false;
|
|
840
|
+
try { present = c.evidence(); } catch (e) { logDebug(e, `detect ${c.key}`); }
|
|
841
|
+
if (present || explicitPlatformKeys.has(c.key)) {
|
|
842
|
+
detections.push({ name: c.name, path: c.path, key: c.key, chosen: !present });
|
|
843
|
+
}
|
|
698
844
|
}
|
|
699
|
-
|
|
700
845
|
return detections;
|
|
701
846
|
}
|
|
702
847
|
|
|
@@ -751,10 +896,9 @@ function detectInstallState() {
|
|
|
751
896
|
{ id: 'synapse', dir: path.join(basePath, 'bdb-synapse') },
|
|
752
897
|
{ id: 'memb', dir: path.join(basePath, 'memB') },
|
|
753
898
|
{ id: 'remote', dir: path.join(basePath, 'bdb-os-remote') },
|
|
754
|
-
|
|
755
|
-
//
|
|
756
|
-
//
|
|
757
|
-
// update loop further down.
|
|
899
|
+
{ id: 'ao', dir: path.join(basePath, 'bdb-agent-orchestrator') },
|
|
900
|
+
// The archived predecessor is deliberately NOT detected: a copy of it
|
|
901
|
+
// should be removed, not carried forward into another install.
|
|
758
902
|
{ id: 'creator', dir: path.join(basePath, 'bdb-dev-creator-extension') },
|
|
759
903
|
{ id: 'installer', dir: path.join(basePath, 'bdb-dev-tool-installer') }
|
|
760
904
|
];
|
|
@@ -952,9 +1096,9 @@ function copyDirRecursiveSync(source, target, excludeList = [], manifest = null,
|
|
|
952
1096
|
// root-level leaf skill dumps into the same flat target, so each subsequent
|
|
953
1097
|
// one silently overwrites the previous one's SKILL.md -- found by tracing
|
|
954
1098
|
// why skills/bdbrainstorm/ and skills/global_config/bdbrainstorm/ both
|
|
955
|
-
// existed with different content; the root-level one (and
|
|
956
|
-
//
|
|
957
|
-
//
|
|
1099
|
+
// existed with different content; the root-level one (and 3 siblings:
|
|
1100
|
+
// github-repo, memb-ingest, synapse-integration-skill) never survived a
|
|
1101
|
+
// global sync intact.
|
|
958
1102
|
function syncSkillEntry(fullPath, dirName, targetSkillDir, excludeSkills) {
|
|
959
1103
|
const isLeafSkill = fs.existsSync(path.join(fullPath, 'SKILL.md'));
|
|
960
1104
|
if (isLeafSkill) {
|
|
@@ -985,15 +1129,20 @@ function syncSkillsToGlobalHarnesses(excludeSkills = []) {
|
|
|
985
1129
|
const skillsBase = path.join(srcDir, 'skills');
|
|
986
1130
|
if (!fs.existsSync(skillsBase)) return;
|
|
987
1131
|
|
|
1132
|
+
// Mirror only into harnesses that are actually present. This list used to
|
|
1133
|
+
// be unconditional, which both wrote skills nobody would read and planted
|
|
1134
|
+
// the very directories detectPlatforms() then read back as proof the
|
|
1135
|
+
// harness existed. ~/.agents is ours and always written.
|
|
1136
|
+
const detectedKeys = new Set(detectPlatforms().map((d) => d.key));
|
|
988
1137
|
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) {
|
|
1138
|
+
{ dir: path.join(homeDir, '.agents', 'skills'), key: null },
|
|
1139
|
+
{ dir: path.join(homeDir, '.claude', 'skills'), key: 'claudecode' },
|
|
1140
|
+
{ dir: path.join(homeDir, '.codex', 'skills'), key: 'codex' },
|
|
1141
|
+
{ dir: path.join(homeDir, '.cursor', 'skills'), key: 'cursor' },
|
|
1142
|
+
{ dir: path.join(homeDir, '.roo', 'skills'), key: 'vscode' },
|
|
1143
|
+
].filter((d) => d.key === null || detectedKeys.has(d.key) || fs.existsSync(d.dir));
|
|
1144
|
+
|
|
1145
|
+
for (const { dir: dest } of extraSkillDestinations) {
|
|
997
1146
|
try {
|
|
998
1147
|
fs.mkdirSync(dest, { recursive: true });
|
|
999
1148
|
const rawDirs = fs.readdirSync(skillsBase);
|
|
@@ -1414,23 +1563,34 @@ function checkModuleUpdate(pkgName, targetDir) {
|
|
|
1414
1563
|
try {
|
|
1415
1564
|
const localVer = JSON.parse(fs.readFileSync(modulePkgPath, 'utf8')).version;
|
|
1416
1565
|
let remoteVer = null;
|
|
1566
|
+
let checkFailed = false;
|
|
1417
1567
|
try {
|
|
1418
1568
|
remoteVer = execSync(`npm view ${pkgName} version`, { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', timeout: 4000 }).trim();
|
|
1419
|
-
} catch (e) {
|
|
1569
|
+
} catch (e) {
|
|
1570
|
+
// Offline, a proxy, or a misconfigured registry. Swallowing this
|
|
1571
|
+
// made every module report "up to date" forever on such a machine,
|
|
1572
|
+
// indistinguishable from a real answer -- so say which one it is.
|
|
1573
|
+
checkFailed = true;
|
|
1574
|
+
logDebug(e, `npm view ${pkgName}`);
|
|
1575
|
+
}
|
|
1420
1576
|
|
|
1421
1577
|
if (remoteVer && localVer !== remoteVer) {
|
|
1422
|
-
return { installed: true, updateAvailable: true, localVer, remoteVer };
|
|
1578
|
+
return { installed: true, updateAvailable: true, localVer, remoteVer, checkFailed };
|
|
1423
1579
|
}
|
|
1424
|
-
return { installed: true, updateAvailable: false, localVer, remoteVer };
|
|
1580
|
+
return { installed: true, updateAvailable: false, localVer, remoteVer, checkFailed };
|
|
1425
1581
|
} catch (e) {
|
|
1426
|
-
return { installed: true, updateAvailable: false, localVer: null, remoteVer: null };
|
|
1582
|
+
return { installed: true, updateAvailable: false, localVer: null, remoteVer: null, checkFailed: true };
|
|
1427
1583
|
}
|
|
1428
1584
|
}
|
|
1429
1585
|
|
|
1430
1586
|
function downloadOrUpdateModule(pkgName, targetDir, displayName) {
|
|
1431
1587
|
const status = checkModuleUpdate(pkgName, targetDir);
|
|
1432
1588
|
if (status.installed && !status.updateAvailable) {
|
|
1433
|
-
|
|
1589
|
+
if (status.checkFailed) {
|
|
1590
|
+
log.warn(`${displayName}: could not reach the npm registry — keeping the installed v${status.localVer}, which may be stale.`);
|
|
1591
|
+
} else {
|
|
1592
|
+
log.step(`${displayName} is up to date (v${status.localVer})`);
|
|
1593
|
+
}
|
|
1434
1594
|
return true;
|
|
1435
1595
|
}
|
|
1436
1596
|
|
|
@@ -1458,14 +1618,42 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
|
|
|
1458
1618
|
const ok = runNpmWithRetry(`npm pack ${pkgName}@latest`, { stdio: 'ignore', cwd: targetDir }, `${displayName} download`);
|
|
1459
1619
|
cleanNpmCacheOnWindows();
|
|
1460
1620
|
const tarball = ok ? fs.readdirSync(targetDir).find(f => f.endsWith('.tgz')) : null;
|
|
1461
|
-
if (tarball)
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1621
|
+
if (!tarball) throw new Error("NPM pack returned no archive.");
|
|
1622
|
+
|
|
1623
|
+
// Unpacking straight over targetDir made an update a MERGE: a file the
|
|
1624
|
+
// new version dropped stayed on disk forever, while package.json said
|
|
1625
|
+
// the module was current. Unpack beside it and swap, so the tree that
|
|
1626
|
+
// ends up installed is exactly the published one. The previous tree is
|
|
1627
|
+
// kept until the swap succeeds, and restored if it does not.
|
|
1628
|
+
const stagingDir = `${targetDir}.incoming-${timestamp}`;
|
|
1629
|
+
const retiredDir = `${targetDir}.previous-${timestamp}`;
|
|
1630
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
1631
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
1632
|
+
execSync(`tar -xzf "${path.join(targetDir, tarball)}" --strip-components=1 -C "${stagingDir}"`, { stdio: 'ignore' });
|
|
1633
|
+
fs.unlinkSync(path.join(targetDir, tarball));
|
|
1634
|
+
|
|
1635
|
+
// Anything the module generated in place -- venvs, caches, local state
|
|
1636
|
+
// -- is not in the tarball and must survive the swap.
|
|
1637
|
+
const PRESERVE = ['.venv', 'venv', 'node_modules', '.env', 'data', 'logs'];
|
|
1638
|
+
for (const keep of PRESERVE) {
|
|
1639
|
+
const from = path.join(targetDir, keep);
|
|
1640
|
+
if (fs.existsSync(from) && !fs.existsSync(path.join(stagingDir, keep))) {
|
|
1641
|
+
fs.renameSync(from, path.join(stagingDir, keep));
|
|
1642
|
+
}
|
|
1468
1643
|
}
|
|
1644
|
+
|
|
1645
|
+
try {
|
|
1646
|
+
fs.renameSync(targetDir, retiredDir);
|
|
1647
|
+
fs.renameSync(stagingDir, targetDir);
|
|
1648
|
+
} catch (swapError) {
|
|
1649
|
+
if (fs.existsSync(retiredDir) && !fs.existsSync(targetDir)) fs.renameSync(retiredDir, targetDir);
|
|
1650
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
1651
|
+
throw swapError;
|
|
1652
|
+
}
|
|
1653
|
+
fs.rmSync(retiredDir, { recursive: true, force: true });
|
|
1654
|
+
|
|
1655
|
+
s.stop(`${displayName} ready (v${status.remoteVer || 'latest'})`);
|
|
1656
|
+
return true;
|
|
1469
1657
|
} catch (e) {
|
|
1470
1658
|
s.stop(`Failed downloading ${displayName}: ${e.message}`);
|
|
1471
1659
|
return false;
|
|
@@ -1485,7 +1673,14 @@ async function installMemB(interactive) {
|
|
|
1485
1673
|
}));
|
|
1486
1674
|
}
|
|
1487
1675
|
const membDir = path.join(moduleBasePath(), 'memB');
|
|
1488
|
-
|
|
1676
|
+
// A failed download used to fall through into venv setup and daemon
|
|
1677
|
+
// registration, each of which then failed on its own terms or, worse,
|
|
1678
|
+
// "succeeded" against a stale tree -- and the run still ended in a success
|
|
1679
|
+
// banner. Stop at the module that could not be fetched.
|
|
1680
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/memb', membDir, 'memB Vector Engine')) {
|
|
1681
|
+
log.warn('Skipping memB setup: the module could not be downloaded.');
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1489
1684
|
if (DRY_RUN) {
|
|
1490
1685
|
log.step('[dry-run] would bootstrap memB venv + pip requirements');
|
|
1491
1686
|
return;
|
|
@@ -1597,7 +1792,10 @@ async function installMemB(interactive) {
|
|
|
1597
1792
|
|
|
1598
1793
|
async function installSynapse() {
|
|
1599
1794
|
const synapseDir = path.join(moduleBasePath(), 'bdb-synapse');
|
|
1600
|
-
downloadOrUpdateModule('@hybridlabor-api/bdb-synapse', synapseDir, 'BDB Synapse')
|
|
1795
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-synapse', synapseDir, 'BDB Synapse')) {
|
|
1796
|
+
log.warn('Skipping Synapse setup: the module could not be downloaded.');
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1601
1799
|
if (DRY_RUN) {
|
|
1602
1800
|
log.step('[dry-run] would link synapse binary into ~/.local/bin/synapse + setup background daemon');
|
|
1603
1801
|
return;
|
|
@@ -1711,7 +1909,10 @@ async function installSynapse() {
|
|
|
1711
1909
|
|
|
1712
1910
|
async function installCreatorExtension() {
|
|
1713
1911
|
const creatorDir = path.join(moduleBasePath(), 'bdb-dev-creator-extension');
|
|
1714
|
-
downloadOrUpdateModule('@hybridlabor-api/bdb-dev-creator-extension', creatorDir, 'BDB Creator Extension')
|
|
1912
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-dev-creator-extension', creatorDir, 'BDB Creator Extension')) {
|
|
1913
|
+
log.warn('Skipping Creator Extension setup: the module could not be downloaded.');
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1715
1916
|
if (DRY_RUN) {
|
|
1716
1917
|
log.step('[dry-run] would run BDB Creator Extension setup');
|
|
1717
1918
|
return;
|
|
@@ -1729,138 +1930,101 @@ async function installCreatorExtension() {
|
|
|
1729
1930
|
|
|
1730
1931
|
async function installOSRemoteGateway() {
|
|
1731
1932
|
const remoteDir = path.join(moduleBasePath(), 'bdb-os-remote');
|
|
1732
|
-
downloadOrUpdateModule('@hybridlabor-api/bdb-os-remote', remoteDir, 'BDB OS Remote Gateway')
|
|
1933
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-os-remote', remoteDir, 'BDB OS Remote Gateway')) {
|
|
1934
|
+
log.warn('Skipping OS Remote Gateway setup: the module could not be downloaded.');
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1733
1937
|
}
|
|
1734
1938
|
|
|
1735
1939
|
async function installDevToolInstaller() {
|
|
1736
1940
|
const toolInstallerDir = path.join(moduleBasePath(), 'bdb-dev-tool-installer');
|
|
1737
|
-
downloadOrUpdateModule('@hybridlabor-api/bdb-dev-tool-installer', toolInstallerDir, 'BDB Dev Tool Installer')
|
|
1941
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-dev-tool-installer', toolInstallerDir, 'BDB Dev Tool Installer')) {
|
|
1942
|
+
log.warn('Skipping Dev Tool Installer setup: the module could not be downloaded.');
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1738
1945
|
}
|
|
1739
1946
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1947
|
+
// AO ships one prebuilt binary: backend/ao-daemon, Mach-O arm64. The package
|
|
1948
|
+
// carries Windows and Linux sources but nothing built for them, and its CLI
|
|
1949
|
+
// shim picks the first candidate path that EXISTS without checking whether it
|
|
1950
|
+
// can run — so on any other platform it hands spawn a Mach-O file and fails
|
|
1951
|
+
// with an exec-format error instead of the "go build" hint it means to give.
|
|
1952
|
+
// Installing it there would be installing something known not to work.
|
|
1953
|
+
function aoSupportedHere() {
|
|
1954
|
+
return process.platform === 'darwin' && process.arch === 'arm64';
|
|
1955
|
+
}
|
|
1748
1956
|
|
|
1749
|
-
|
|
1957
|
+
async function installOSAgentWorkspace() {
|
|
1958
|
+
if (!aoSupportedHere()) {
|
|
1959
|
+
log.warn(`AO ships only a macOS arm64 binary — skipping on ${process.platform}/${process.arch}.`);
|
|
1960
|
+
log.warn('Build it from source instead: github.com/hybridlabor-api/bdb-agent-orchestrator');
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1750
1963
|
|
|
1964
|
+
const osAgentDir = path.join(moduleBasePath(), 'bdb-agent-orchestrator');
|
|
1965
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-agent-orchestrator', osAgentDir, 'BDB Agent Orchestrator')) {
|
|
1966
|
+
log.warn('Skipping AO setup: the module could not be downloaded.');
|
|
1967
|
+
return;
|
|
1968
|
+
}
|
|
1751
1969
|
if (DRY_RUN) {
|
|
1752
|
-
log.step('[dry-run] would link ao
|
|
1970
|
+
log.step('[dry-run] would link ~/.local/bin/ao and run `ao service install`');
|
|
1753
1971
|
return;
|
|
1754
1972
|
}
|
|
1755
1973
|
|
|
1974
|
+
const localBinDir = path.join(homeDir, '.local', 'bin');
|
|
1975
|
+
const binTarget = path.join(localBinDir, 'ao');
|
|
1756
1976
|
const daemonBin = path.join(osAgentDir, 'backend', 'ao-daemon');
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
} catch (e) { logDebug(e, 'operation'); }
|
|
1977
|
+
|
|
1978
|
+
if (!fs.existsSync(daemonBin)) {
|
|
1979
|
+
log.warn(`AO binary missing at ${daemonBin} — package layout changed?`);
|
|
1980
|
+
return;
|
|
1762
1981
|
}
|
|
1763
1982
|
|
|
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'); }
|
|
1983
|
+
installStep('place the ao binary', () => {
|
|
1984
|
+
fs.mkdirSync(localBinDir, { recursive: true });
|
|
1985
|
+
// copyFileSync preserves bytes, so the package's ad-hoc signature
|
|
1986
|
+
// survives. That matters: an unsigned binary at this path is SIGKILLed
|
|
1987
|
+
// by AMFI on launch (exit 137) and reads as a daemon that simply will
|
|
1988
|
+
// not start, with nothing in the log to say why.
|
|
1989
|
+
fs.copyFileSync(daemonBin, binTarget);
|
|
1990
|
+
fs.chmodSync(binTarget, 0o755);
|
|
1991
|
+
log.step(`Installed ao to ${binTarget}`);
|
|
1992
|
+
}, 'AO cannot be started without its binary.');
|
|
1806
1993
|
|
|
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
|
-
}
|
|
1994
|
+
try {
|
|
1995
|
+
execSync(`codesign -v "${binTarget}"`, { stdio: 'ignore' });
|
|
1996
|
+
} catch {
|
|
1997
|
+
log.warn('The ao binary is not code-signed — AMFI will kill it at launch.');
|
|
1815
1998
|
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
|
-
}
|
|
1999
|
+
execSync(`codesign -s - -f "${binTarget}"`, { stdio: 'ignore' });
|
|
2000
|
+
log.step('Signed it ad-hoc.');
|
|
2001
|
+
} catch (e) { log.warn(`Could not sign it: ${e.message}`); }
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
// AO installs its own service, and does it for macOS, Windows and Linux.
|
|
2005
|
+
// AOS used to hand-write a LaunchAgent instead, labelled
|
|
2006
|
+
// com.bdb.agent-workspace, whose ProgramArguments were just the binary with
|
|
2007
|
+
// NO subcommand -- so launchd started `ao`, which prints help and exits,
|
|
2008
|
+
// and with KeepAlive kept restarting it. The daemon never ran, on any
|
|
2009
|
+
// machine that installed AO through AOS. Delegating to the tool removes
|
|
2010
|
+
// both the wrong label and the wrong arguments, and keeps working when the
|
|
2011
|
+
// service definition changes upstream.
|
|
2012
|
+
const legacyPlist = path.join(homeDir, 'Library', 'LaunchAgents', 'com.bdb.agent-workspace.plist');
|
|
2013
|
+
if (fs.existsSync(legacyPlist)) {
|
|
2014
|
+
try { execSync(`launchctl unload "${legacyPlist}" 2>/dev/null`, { stdio: 'ignore' }); } catch (e) { logDebug(e, 'unload legacy ao agent'); }
|
|
2015
|
+
try { fs.unlinkSync(legacyPlist); log.step('Removed the old com.bdb.agent-workspace service.'); } catch (e) { logDebug(e, 'remove legacy plist'); }
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
installStep('register the AO service', () => {
|
|
2019
|
+
execSync(`"${binTarget}" service install`, { stdio: 'ignore' });
|
|
2020
|
+
}, 'Start it by hand with: ao service install');
|
|
2021
|
+
|
|
2022
|
+
if (await verifyDaemonListening(3101, 'AO Orchestrator', 8000)) {
|
|
2023
|
+
log.success('AO Orchestrator running on http://localhost:3101');
|
|
2024
|
+
} else {
|
|
2025
|
+
log.warn('AO did not answer on :3101 — check `ao service status` and `ao service logs`.');
|
|
1859
2026
|
}
|
|
1860
|
-
|
|
1861
|
-
log.step('BDB Agent Workspace WebUI: http://localhost:3101');
|
|
1862
2027
|
}
|
|
1863
|
-
|
|
1864
2028
|
async function promptMemBIngestion(mcpCodeTarget) {
|
|
1865
2029
|
if (isAutoYes || DRY_RUN) return;
|
|
1866
2030
|
|
|
@@ -1903,30 +2067,16 @@ async function promptMemBIngestion(mcpCodeTarget) {
|
|
|
1903
2067
|
}
|
|
1904
2068
|
}
|
|
1905
2069
|
|
|
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
2070
|
function verifyEcosystemInstallation() {
|
|
1918
2071
|
const modules = [
|
|
1919
2072
|
{ name: '1. bdb-synapse', pkg: '@hybridlabor-api/bdb-synapse', paths: [path.join(moduleBasePath(), 'bdb-synapse')] },
|
|
1920
2073
|
{ name: '2. memB', pkg: '@hybridlabor-api/memb', paths: [path.join(moduleBasePath(), 'memB'), path.join(geminiDir, 'config', 'mcps', 'memb-mcp')] },
|
|
1921
2074
|
{ 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] }
|
|
2075
|
+
{ name: '4. AO Agent Orchestrator', pkg: '@hybridlabor-api/bdb-agent-orchestrator', paths: [path.join(moduleBasePath(), 'bdb-agent-orchestrator')] },
|
|
2076
|
+
{ name: '5. bdb-dev-creator-extension', pkg: '@hybridlabor-api/bdb-dev-creator-extension', paths: [path.join(moduleBasePath(), 'bdb-dev-creator-extension')] },
|
|
2077
|
+
{ name: '6. bdb-os-remote', pkg: '@hybridlabor-api/bdb-os-remote', paths: [path.join(moduleBasePath(), 'bdb-os-remote')] },
|
|
2078
|
+
{ name: '7. bdb-dev-tool-installer', pkg: '@hybridlabor-api/bdb-dev-tool-installer', paths: [path.join(moduleBasePath(), 'bdb-dev-tool-installer')] },
|
|
2079
|
+
{ name: '8. aos (bdb agent os)', pkg: '@hybridlabor-api/aos', paths: [srcDir] }
|
|
1930
2080
|
];
|
|
1931
2081
|
|
|
1932
2082
|
for (const mod of modules) {
|
|
@@ -1969,7 +2119,21 @@ function verifyEcosystemInstallation() {
|
|
|
1969
2119
|
console.log(` • ${colors.bold}${mod.name.padEnd(35)}${colors.reset} ➔ ${colors.green}✅ Installed${colors.reset}`);
|
|
1970
2120
|
}
|
|
1971
2121
|
} else if (mod.name.includes('token-saver') && fs.existsSync(path.join(srcDir, 'vendor', 'token-saver'))) {
|
|
1972
|
-
|
|
2122
|
+
// Was a hardcoded "v2.6.3", which stopped being true the moment the
|
|
2123
|
+
// vendored copy moved. Read it, and say so when it cannot be read.
|
|
2124
|
+
let vendored = null;
|
|
2125
|
+
for (const f of ['package.json', 'pyproject.toml', 'VERSION']) {
|
|
2126
|
+
const vp = path.join(srcDir, 'vendor', 'token-saver', f);
|
|
2127
|
+
if (!fs.existsSync(vp)) continue;
|
|
2128
|
+
try {
|
|
2129
|
+
const raw = fs.readFileSync(vp, 'utf8');
|
|
2130
|
+
vendored = f === 'package.json'
|
|
2131
|
+
? JSON.parse(raw).version
|
|
2132
|
+
: (raw.match(/^\s*version\s*=\s*["']?([\w.\-+]+)/m) || [])[1] || raw.trim().split('\n')[0];
|
|
2133
|
+
} catch (e) { logDebug(e, `token-saver version from ${f}`); }
|
|
2134
|
+
if (vendored) break;
|
|
2135
|
+
}
|
|
2136
|
+
console.log(` • ${colors.bold}${mod.name.padEnd(35)}${colors.reset} ➔ ${colors.green}✅ ${vendored ? `v${vendored} ` : ''}(Integrated)${colors.reset}`);
|
|
1973
2137
|
} else {
|
|
1974
2138
|
console.log(` • ${colors.bold}${mod.name.padEnd(35)}${colors.reset} ➔ ${colors.dim}⚪ Optional / Not downloaded${colors.reset}`);
|
|
1975
2139
|
}
|
|
@@ -2135,7 +2299,13 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2135
2299
|
if (selectedMcps.includes(CORE_MCP)) {
|
|
2136
2300
|
const membMcpTarget = path.join(mcpCodeTarget, CORE_MCP);
|
|
2137
2301
|
installStep(`download/update ${CORE_MCP} from npm`, () => {
|
|
2138
|
-
|
|
2302
|
+
// installStep only catches a throw, and downloadOrUpdateModule
|
|
2303
|
+
// reports failure by returning false -- so without this the hint
|
|
2304
|
+
// below never printed and the MCP config was written pointing at a
|
|
2305
|
+
// path that had not been populated.
|
|
2306
|
+
if (!downloadOrUpdateModule('@hybridlabor-api/memb', membMcpTarget, 'memB MCP')) {
|
|
2307
|
+
throw new Error('npm pack did not produce the memb-mcp payload');
|
|
2308
|
+
}
|
|
2139
2309
|
}, 'memb-mcp installation skipped; MCP config may reference a missing path.');
|
|
2140
2310
|
}
|
|
2141
2311
|
log.step(`Installed selected MCP servers to ${mcpCodeTarget}`);
|
|
@@ -2148,7 +2318,11 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2148
2318
|
const ok = runNpmWithRetry('npm install --no-audit --no-fund', { cwd: targetFolder }, `npm install for ${mcpFolder}`);
|
|
2149
2319
|
if (ok && (fs.existsSync(path.join(targetFolder, 'tsconfig.json')) || fs.existsSync(path.join(targetFolder, 'tsconfig.build.json')))) {
|
|
2150
2320
|
log.step(`Compiling TypeScript for ${mcpFolder}...`);
|
|
2151
|
-
runNpmWithRetry('npm run build', { cwd: targetFolder }, `npm run build for ${mcpFolder}`);
|
|
2321
|
+
const built = runNpmWithRetry('npm run build', { cwd: targetFolder }, `npm run build for ${mcpFolder}`);
|
|
2322
|
+
// runNpmWithRetry already warned with the failure detail; this
|
|
2323
|
+
// just makes the consequence visible now instead of only later,
|
|
2324
|
+
// when the config-entry check below finds the missing dist file.
|
|
2325
|
+
if (!built) log.warn(`${mcpFolder}: build failed -- its MCP config entry will be dropped if the expected output is missing.`);
|
|
2152
2326
|
}
|
|
2153
2327
|
}
|
|
2154
2328
|
}
|
|
@@ -2180,7 +2354,11 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2180
2354
|
? path.join(membMcpFolder, '.venv', 'Scripts', 'python.exe')
|
|
2181
2355
|
: path.join(membMcpFolder, '.venv', 'bin', 'python');
|
|
2182
2356
|
|
|
2183
|
-
|
|
2357
|
+
// The fs.* writes are covered by the global dry-run patch, but
|
|
2358
|
+
// child processes are not -- these two would really create a venv.
|
|
2359
|
+
if (DRY_RUN) {
|
|
2360
|
+
log.step(`[dry-run] would create the memb-mcp venv in ${membMcpFolder}`);
|
|
2361
|
+
} else if (!fs.existsSync(venvPython)) {
|
|
2184
2362
|
try {
|
|
2185
2363
|
execSync(`uv venv --seed .venv`, { cwd: membMcpFolder, stdio: 'ignore' });
|
|
2186
2364
|
} catch (e1) {
|
|
@@ -2198,12 +2376,12 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2198
2376
|
}
|
|
2199
2377
|
|
|
2200
2378
|
const pythonMcps = [
|
|
2201
|
-
{ folder: 'golem-rhino-mcp', args: ['-m', 'mcp_server', '--help'] },
|
|
2202
|
-
{ folder: 'davinci-mcp-professional', args: ['main.py', '--help'] },
|
|
2379
|
+
{ folder: 'golem-rhino-mcp', args: ['-m', 'mcp_server', '--help'], uvArgs: true },
|
|
2380
|
+
{ folder: 'davinci-mcp-professional', args: ['main.py', '--help'], uvArgs: true },
|
|
2203
2381
|
{ 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'] },
|
|
2382
|
+
{ folder: 'blender-mcp', args: ['-m', 'blender_mcp.server', '--help'], uvArgs: true },
|
|
2205
2383
|
{ 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'] }
|
|
2384
|
+
{ folder: 'windows-computer-use-mcp', args: ['run_server.py', '--help'], uvArgs: true }
|
|
2207
2385
|
];
|
|
2208
2386
|
for (const mcp of pythonMcps.filter(m => selectedMcps.includes(m.folder))) {
|
|
2209
2387
|
const targetFolder = path.join(mcpCodeTarget, mcp.folder);
|
|
@@ -2213,7 +2391,18 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2213
2391
|
log.step(`[dry-run] would pre-warm Python deps for ${mcp.folder}`);
|
|
2214
2392
|
continue;
|
|
2215
2393
|
}
|
|
2216
|
-
spawnSync('uv', prewarmArgs, { cwd: targetFolder, stdio: 'ignore' });
|
|
2394
|
+
const result = spawnSync('uv', prewarmArgs, { cwd: targetFolder, stdio: 'ignore' });
|
|
2395
|
+
if (result.error) {
|
|
2396
|
+
// uv itself is missing or unrunnable. That is one machine-level
|
|
2397
|
+
// fact, not six per-MCP failures — say it once and stop trying.
|
|
2398
|
+
log.warn(`uv could not be run (${result.error.message}) — skipping the Python prewarm for the remaining MCPs.`);
|
|
2399
|
+
break;
|
|
2400
|
+
}
|
|
2401
|
+
if (result.status === 0) {
|
|
2402
|
+
log.step(`Pre-warmed Python deps for ${mcp.folder}`);
|
|
2403
|
+
} else {
|
|
2404
|
+
log.warn(`Prewarm failed for ${mcp.folder}: uv exited ${result.status}. The MCP is configured but its dependencies may not resolve.`);
|
|
2405
|
+
}
|
|
2217
2406
|
}
|
|
2218
2407
|
}
|
|
2219
2408
|
|
|
@@ -2236,6 +2425,28 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
2236
2425
|
const parsedMcpConfig = JSON.parse(mcpConfigStr);
|
|
2237
2426
|
const finalMcpServers = {};
|
|
2238
2427
|
const availableFolders = fs.readdirSync(mcpSrcDir);
|
|
2428
|
+
|
|
2429
|
+
// A node MCP's build can fail (see above) or simply never run for a
|
|
2430
|
+
// dist-based server that was skipped -- either way an entry pointing
|
|
2431
|
+
// at a file that was never produced fails at MCP launch, far from the
|
|
2432
|
+
// real cause. Derive the check from each entry's own args (node's
|
|
2433
|
+
// first arg is always its entry script) instead of a hardcoded list of
|
|
2434
|
+
// MCP names, so a fifth dist-based entry added later is covered too.
|
|
2435
|
+
if (!DRY_RUN) {
|
|
2436
|
+
for (const [key, val] of Object.entries(parsedMcpConfig.mcpServers)) {
|
|
2437
|
+
if (val.command !== 'node' || !Array.isArray(val.args) || !val.args[0]) continue;
|
|
2438
|
+
const entryArg = val.args[0];
|
|
2439
|
+
if (!entryArg.startsWith('__MCPS_DIR__/')) continue;
|
|
2440
|
+
const relPath = entryArg.slice('__MCPS_DIR__/'.length);
|
|
2441
|
+
const mcpFolder = relPath.split('/')[0];
|
|
2442
|
+
if (!selectedMcps.includes(mcpFolder)) continue; // not installed at all, already handled below
|
|
2443
|
+
if (!fs.existsSync(path.join(mcpCodeTarget, relPath))) {
|
|
2444
|
+
log.warn(`${mcpFolder}: build produced no ${relPath.slice(mcpFolder.length + 1)} -- not registering ${key}`);
|
|
2445
|
+
skippedMcpConfigKeys.push(key);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2239
2450
|
for (const [key, val] of Object.entries(parsedMcpConfig.mcpServers)) {
|
|
2240
2451
|
let keep = !skippedMcpConfigKeys.includes(key);
|
|
2241
2452
|
for (const available of availableFolders) {
|
|
@@ -2960,10 +3171,13 @@ async function promptOptionalModules(installedModules) {
|
|
|
2960
3171
|
{ id: 'synapse', name: 'BDB Synapse (3D Codebase Visualizer)', fn: installSynapse },
|
|
2961
3172
|
{ id: 'memb', name: 'memB Vector Engine (Local Semantic Memory)', fn: () => installMemB(true) },
|
|
2962
3173
|
{ id: 'remote', name: 'BDB OS Remote Gateway (Zero-Trust Tailscale Multiplexer)', fn: installOSRemoteGateway },
|
|
2963
|
-
//
|
|
2964
|
-
//
|
|
2965
|
-
//
|
|
2966
|
-
//
|
|
3174
|
+
// Offered only where it can actually run -- see aoSupportedHere(). The
|
|
3175
|
+
// predecessor, @hybridlabor-api/bdb-os-agent-workspace, must never come
|
|
3176
|
+
// back: its repository is archived and its last release predates the
|
|
3177
|
+
// archiving, so it hands out the build with the CDC loop defect.
|
|
3178
|
+
...(aoSupportedHere()
|
|
3179
|
+
? [{ id: 'ao', name: 'AO Agent Orchestrator (Session telemetry & WebUI)', fn: installOSAgentWorkspace }]
|
|
3180
|
+
: []),
|
|
2967
3181
|
{ id: 'creator', name: 'BDB Creator Extension (Generative 3D, Video & ComfyUI)', fn: installCreatorExtension },
|
|
2968
3182
|
{ id: 'installer', name: 'BDB Dev Tool Installer (Interactive Hub & CLI Launcher)', fn: installDevToolInstaller }
|
|
2969
3183
|
];
|
|
@@ -3332,11 +3546,19 @@ async function runQuickUpdate(installState) {
|
|
|
3332
3546
|
}
|
|
3333
3547
|
}
|
|
3334
3548
|
syncSkillsToGlobalHarnesses(excludeSkills);
|
|
3549
|
+
pruneRemovedSkills(_sessionManifest);
|
|
3335
3550
|
s.stop('Skills refreshed');
|
|
3336
3551
|
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3552
|
+
// Everything injectHarnessRules() delivers -- GEMINI.md, the dispatcher
|
|
3553
|
+
// workflows the skills point at, the compiled subagent definitions, the
|
|
3554
|
+
// harness rule files and the gate + memory hooks -- used to be
|
|
3555
|
+
// fresh-install-only. v4.4.1 split just the hooks out of it for Quick
|
|
3556
|
+
// Update; the rest stayed behind, so a machine that only ever quick-updates
|
|
3557
|
+
// kept running first-install agent definitions and dispatcher scripts
|
|
3558
|
+
// forever. Same defect as the hook, one layer up. Run the whole thing.
|
|
3559
|
+
installStep('refresh harness rules, workflows, agents and hooks', () => {
|
|
3560
|
+
injectHarnessRules();
|
|
3561
|
+
}, 'Harness files keep whatever version this machine already had.');
|
|
3340
3562
|
|
|
3341
3563
|
// OpenWiki setup only ever ran on a brand-new install (main()'s fresh-install
|
|
3342
3564
|
// branch below) -- an existing install running Quick Update never got offered
|
|
@@ -3357,7 +3579,7 @@ async function runQuickUpdate(installState) {
|
|
|
3357
3579
|
if (subId === 'synapse') await installSynapse();
|
|
3358
3580
|
else if (subId === 'memb') await installMemB(false);
|
|
3359
3581
|
else if (subId === 'remote') await installOSRemoteGateway();
|
|
3360
|
-
// 'ao'
|
|
3582
|
+
// 'ao' intentionally skipped here even for
|
|
3361
3583
|
// existing installs -- see promptOptionalModules() for why.
|
|
3362
3584
|
else if (subId === 'creator') await installCreatorExtension();
|
|
3363
3585
|
else if (subId === 'installer') await installDevToolInstaller();
|
|
@@ -3398,7 +3620,10 @@ async function main() {
|
|
|
3398
3620
|
console.log(buildTelemetryCard({ installState, detections, daemonStatus }));
|
|
3399
3621
|
|
|
3400
3622
|
if (DRY_RUN) {
|
|
3401
|
-
|
|
3623
|
+
// Precise on purpose: version comparison still queries the registry, and
|
|
3624
|
+
// npm writes its own cache and logs under ~/.npm doing so. Nothing AOS
|
|
3625
|
+
// owns is touched -- but claiming "no commands executed" was not true.
|
|
3626
|
+
log.warn('DRY-RUN MODE active - nothing AOS owns is written. Read-only registry lookups still run (npm caches under ~/.npm).');
|
|
3402
3627
|
}
|
|
3403
3628
|
if (isAutoYes) {
|
|
3404
3629
|
log.warn('Non-interactive/auto mode (-y): all defaults are accepted automatically.');
|
|
@@ -3490,6 +3715,8 @@ async function main() {
|
|
|
3490
3715
|
let wantsUniversal = true;
|
|
3491
3716
|
let specificPlatforms = ['1'];
|
|
3492
3717
|
|
|
3718
|
+
markPlatformsExplicit(PLATFORMS_ARG || []);
|
|
3719
|
+
|
|
3493
3720
|
if (PLATFORMS_ARG) {
|
|
3494
3721
|
// Explicit targets win over both the menu and the auto-yes default.
|
|
3495
3722
|
// '0' means universal; anything else means exactly those targets, so
|
|
@@ -3529,6 +3756,9 @@ async function main() {
|
|
|
3529
3756
|
});
|
|
3530
3757
|
if (sel === BACK) return 'back';
|
|
3531
3758
|
ctx.selectedPlatforms = sel;
|
|
3759
|
+
// A harness the user names counts as present even when no binary
|
|
3760
|
+
// or app bundle turns up for it.
|
|
3761
|
+
markPlatformsExplicit(sel);
|
|
3532
3762
|
};
|
|
3533
3763
|
|
|
3534
3764
|
const stepMode = async () => {
|
|
@@ -3721,6 +3951,7 @@ async function main() {
|
|
|
3721
3951
|
}
|
|
3722
3952
|
|
|
3723
3953
|
syncSkillsToGlobalHarnesses(excludeSkills);
|
|
3954
|
+
pruneRemovedSkills(_sessionManifest);
|
|
3724
3955
|
s.stop('Skills installed.');
|
|
3725
3956
|
|
|
3726
3957
|
injectHarnessRules();
|
|
@@ -3742,7 +3973,6 @@ async function main() {
|
|
|
3742
3973
|
const justInstalled = await promptOptionalModules(installedModulesForPrompt) || [];
|
|
3743
3974
|
|
|
3744
3975
|
await promptMemBIngestion(path.join(primaryTarget.targetMcpDir, 'mcps'));
|
|
3745
|
-
await promptEcosystemHealthScheduler();
|
|
3746
3976
|
|
|
3747
3977
|
const alreadyHandled = justInstalled.map(id => MODULE_ID_TO_DAEMON_NAME[id]).filter(Boolean);
|
|
3748
3978
|
await reloadDaemons(alreadyHandled);
|
|
@@ -3767,6 +3997,10 @@ if (require.main === module) {
|
|
|
3767
3997
|
|
|
3768
3998
|
// Exported for tests -- requiring installer.js must not launch the TUI.
|
|
3769
3999
|
module.exports = {
|
|
4000
|
+
installOSAgentWorkspace,
|
|
4001
|
+
downloadOrUpdateModule,
|
|
4002
|
+
detectPlatforms,
|
|
4003
|
+
markPlatformsExplicit,
|
|
3770
4004
|
mergeBdbSettingsHooks,
|
|
3771
4005
|
installGlobalHooks,
|
|
3772
4006
|
installProjectHarness,
|