livepilot 1.10.7 → 1.10.9
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/CHANGELOG.md +254 -0
- package/README.md +19 -17
- package/bin/livepilot.js +146 -28
- package/installer/install.js +117 -11
- package/m4l_device/LivePilot_Analyzer.amxd +0 -0
- package/m4l_device/livepilot_bridge.js +1 -1
- package/mcp_server/__init__.py +1 -1
- package/mcp_server/atlas/__init__.py +39 -7
- package/mcp_server/atlas/tools.py +56 -15
- package/mcp_server/composer/layer_planner.py +27 -0
- package/mcp_server/composer/prompt_parser.py +15 -6
- package/mcp_server/connection.py +11 -3
- package/mcp_server/corpus/__init__.py +14 -4
- package/mcp_server/evaluation/fabric.py +62 -1
- package/mcp_server/m4l_bridge.py +63 -12
- package/mcp_server/project_brain/automation_graph.py +23 -1
- package/mcp_server/project_brain/builder.py +2 -0
- package/mcp_server/project_brain/models.py +20 -1
- package/mcp_server/project_brain/tools.py +10 -3
- package/mcp_server/runtime/execution_router.py +16 -2
- package/mcp_server/runtime/remote_commands.py +6 -0
- package/mcp_server/sample_engine/models.py +22 -3
- package/mcp_server/semantic_moves/__init__.py +1 -0
- package/mcp_server/semantic_moves/compiler.py +9 -1
- package/mcp_server/semantic_moves/device_creation_compilers.py +47 -0
- package/mcp_server/semantic_moves/mix_compilers.py +170 -0
- package/mcp_server/semantic_moves/mix_moves.py +1 -1
- package/mcp_server/semantic_moves/models.py +5 -0
- package/mcp_server/semantic_moves/tools.py +154 -35
- package/mcp_server/server.py +147 -17
- package/mcp_server/services/singletons.py +68 -0
- package/mcp_server/session_continuity/models.py +13 -0
- package/mcp_server/session_continuity/tools.py +2 -0
- package/mcp_server/session_continuity/tracker.py +93 -0
- package/mcp_server/splice_client/client.py +29 -8
- package/mcp_server/tools/_analyzer_engine/__init__.py +39 -0
- package/mcp_server/tools/_analyzer_engine/context.py +103 -0
- package/mcp_server/tools/_analyzer_engine/flucoma.py +23 -0
- package/mcp_server/tools/_analyzer_engine/sample.py +122 -0
- package/mcp_server/tools/_motif_engine.py +19 -4
- package/mcp_server/tools/analyzer.py +25 -180
- package/mcp_server/tools/clips.py +240 -2
- package/mcp_server/tools/midi_io.py +10 -0
- package/mcp_server/tools/tracks.py +1 -1
- package/mcp_server/tools/transport.py +59 -4
- package/mcp_server/translation_engine/tools.py +8 -4
- package/package.json +25 -3
- package/remote_script/LivePilot/__init__.py +36 -9
- package/remote_script/LivePilot/arrangement.py +12 -2
- package/remote_script/LivePilot/browser.py +16 -6
- package/remote_script/LivePilot/devices.py +10 -5
- package/remote_script/LivePilot/notes.py +13 -2
- package/remote_script/LivePilot/server.py +51 -13
- package/remote_script/LivePilot/version_detect.py +7 -4
- package/server.json +20 -0
- package/.claude-plugin/marketplace.json +0 -21
- package/.mcp.json.disabled +0 -9
- package/.mcpbignore +0 -60
- package/AGENTS.md +0 -46
- package/BUGS.md +0 -1570
- package/CODE_OF_CONDUCT.md +0 -27
- package/CONTRIBUTING.md +0 -131
- package/SECURITY.md +0 -48
- package/livepilot/.Codex-plugin/plugin.json +0 -8
- package/livepilot/.claude-plugin/plugin.json +0 -8
- package/livepilot/agents/livepilot-producer/AGENT.md +0 -313
- package/livepilot/commands/arrange.md +0 -47
- package/livepilot/commands/beat.md +0 -77
- package/livepilot/commands/evaluate.md +0 -49
- package/livepilot/commands/memory.md +0 -22
- package/livepilot/commands/mix.md +0 -44
- package/livepilot/commands/perform.md +0 -42
- package/livepilot/commands/session.md +0 -13
- package/livepilot/commands/sounddesign.md +0 -43
- package/livepilot/skills/livepilot-arrangement/SKILL.md +0 -155
- package/livepilot/skills/livepilot-composition-engine/SKILL.md +0 -107
- package/livepilot/skills/livepilot-composition-engine/references/form-patterns.md +0 -97
- package/livepilot/skills/livepilot-composition-engine/references/transition-archetypes.md +0 -102
- package/livepilot/skills/livepilot-core/SKILL.md +0 -184
- package/livepilot/skills/livepilot-core/references/ableton-workflow-patterns.md +0 -831
- package/livepilot/skills/livepilot-core/references/automation-atlas.md +0 -272
- package/livepilot/skills/livepilot-core/references/device-atlas/00-index.md +0 -110
- package/livepilot/skills/livepilot-core/references/device-atlas/distortion-and-character.md +0 -687
- package/livepilot/skills/livepilot-core/references/device-atlas/drums-and-percussion.md +0 -753
- package/livepilot/skills/livepilot-core/references/device-atlas/dynamics-and-punch.md +0 -525
- package/livepilot/skills/livepilot-core/references/device-atlas/eq-and-filtering.md +0 -402
- package/livepilot/skills/livepilot-core/references/device-atlas/midi-tools.md +0 -963
- package/livepilot/skills/livepilot-core/references/device-atlas/movement-and-modulation.md +0 -874
- package/livepilot/skills/livepilot-core/references/device-atlas/space-and-depth.md +0 -571
- package/livepilot/skills/livepilot-core/references/device-atlas/spectral-and-weird.md +0 -714
- package/livepilot/skills/livepilot-core/references/device-atlas/synths-native.md +0 -953
- package/livepilot/skills/livepilot-core/references/device-knowledge/00-index.md +0 -34
- package/livepilot/skills/livepilot-core/references/device-knowledge/automation-as-music.md +0 -204
- package/livepilot/skills/livepilot-core/references/device-knowledge/chains-genre.md +0 -173
- package/livepilot/skills/livepilot-core/references/device-knowledge/creative-thinking.md +0 -211
- package/livepilot/skills/livepilot-core/references/device-knowledge/effects-distortion.md +0 -188
- package/livepilot/skills/livepilot-core/references/device-knowledge/effects-space.md +0 -162
- package/livepilot/skills/livepilot-core/references/device-knowledge/effects-spectral.md +0 -229
- package/livepilot/skills/livepilot-core/references/device-knowledge/instruments-synths.md +0 -243
- package/livepilot/skills/livepilot-core/references/m4l-devices.md +0 -352
- package/livepilot/skills/livepilot-core/references/memory-guide.md +0 -107
- package/livepilot/skills/livepilot-core/references/midi-recipes.md +0 -402
- package/livepilot/skills/livepilot-core/references/mixing-patterns.md +0 -578
- package/livepilot/skills/livepilot-core/references/overview.md +0 -290
- package/livepilot/skills/livepilot-core/references/sample-manipulation.md +0 -724
- package/livepilot/skills/livepilot-core/references/sound-design-deep.md +0 -140
- package/livepilot/skills/livepilot-core/references/sound-design.md +0 -393
- package/livepilot/skills/livepilot-devices/SKILL.md +0 -169
- package/livepilot/skills/livepilot-evaluation/SKILL.md +0 -156
- package/livepilot/skills/livepilot-evaluation/references/capability-modes.md +0 -118
- package/livepilot/skills/livepilot-evaluation/references/evaluation-contracts.md +0 -121
- package/livepilot/skills/livepilot-evaluation/references/memory-promotion.md +0 -110
- package/livepilot/skills/livepilot-mix-engine/SKILL.md +0 -123
- package/livepilot/skills/livepilot-mix-engine/references/mix-critics.md +0 -143
- package/livepilot/skills/livepilot-mix-engine/references/mix-moves.md +0 -105
- package/livepilot/skills/livepilot-mixing/SKILL.md +0 -157
- package/livepilot/skills/livepilot-notes/SKILL.md +0 -130
- package/livepilot/skills/livepilot-performance-engine/SKILL.md +0 -122
- package/livepilot/skills/livepilot-performance-engine/references/performance-safety.md +0 -98
- package/livepilot/skills/livepilot-release/SKILL.md +0 -130
- package/livepilot/skills/livepilot-sample-engine/SKILL.md +0 -105
- package/livepilot/skills/livepilot-sample-engine/references/sample-critics.md +0 -87
- package/livepilot/skills/livepilot-sample-engine/references/sample-philosophy.md +0 -51
- package/livepilot/skills/livepilot-sample-engine/references/sample-techniques.md +0 -131
- package/livepilot/skills/livepilot-sound-design-engine/SKILL.md +0 -168
- package/livepilot/skills/livepilot-sound-design-engine/references/patch-model.md +0 -119
- package/livepilot/skills/livepilot-sound-design-engine/references/sound-design-critics.md +0 -118
- package/livepilot/skills/livepilot-wonder/SKILL.md +0 -79
- package/m4l_device/LivePilot_Analyzer.amxd.pre-presentation-backup +0 -0
- package/m4l_device/LivePilot_Analyzer.maxpat +0 -2705
- package/m4l_device/LivePilot_Analyzer.maxproj +0 -53
- package/manifest.json +0 -91
- package/mcp_server/splice_client/protos/app_pb2.pyi +0 -1153
- package/scripts/generate_tool_catalog.py +0 -106
- package/scripts/sync_metadata.py +0 -349
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,259 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.10.9 — Second-pass audit + deferred-bugs shipped (April 18 2026)
|
|
4
|
+
|
|
5
|
+
Completes every non-feature item on the v1.10.8 audit backlog. 2116 → 2132
|
|
6
|
+
passing tests (+16 regression guards). 324 → 325 tools (`check_clip_key_consistency`
|
|
7
|
+
lands from BUG-D1). Every deferred BUG-C and BUG-D entry either ships or is
|
|
8
|
+
scoped to a follow-up feature; BUG-C4 is filed upstream as
|
|
9
|
+
[PrefectHQ/fastmcp#3967](https://github.com/PrefectHQ/fastmcp/issues/3967).
|
|
10
|
+
|
|
11
|
+
### Ship-stoppers fixed
|
|
12
|
+
|
|
13
|
+
- **`send_capture` phantom 35s hang when receiver is None** —
|
|
14
|
+
[`mcp_server/m4l_bridge.py:441`](mcp_server/m4l_bridge.py). `send_command`
|
|
15
|
+
had a receiver-None guard; `send_capture` didn't. When UDP 9880 failed
|
|
16
|
+
to bind but the cache still reported connected, the OSC packet was
|
|
17
|
+
sent, the capture future was never registered, and the full 35s
|
|
18
|
+
timeout fired with a misleading "device may be busy" error. Added the
|
|
19
|
+
matching 5-line guard so the real cause surfaces immediately.
|
|
20
|
+
- **`utils.py` stale after Control Surface toggle** —
|
|
21
|
+
[`remote_script/LivePilot/__init__.py:43`](remote_script/LivePilot/__init__.py).
|
|
22
|
+
Every handler does `from .utils import get_track, get_device`.
|
|
23
|
+
`importlib.reload(devices)` rebinds those names, but because `utils`
|
|
24
|
+
was not in `_HANDLER_MODULES`, the re-import resolved against the
|
|
25
|
+
stale `sys.modules["LivePilot.utils"]`. Added `utils` first in the
|
|
26
|
+
reload order so edits to the shared helpers pick up on toggle too,
|
|
27
|
+
honoring the dev-workflow guarantee documented at the top of the file.
|
|
28
|
+
|
|
29
|
+
### New tools
|
|
30
|
+
|
|
31
|
+
- **`check_clip_key_consistency`** (BUG-D1) — parses Splice-style
|
|
32
|
+
filename key tokens (`_D#min`, `_Ebmaj`, `_Cm`, …), cross-checks
|
|
33
|
+
against `get_detected_key`, returns the exact `set_clip_pitch(coarse=±N)`
|
|
34
|
+
call that would realign on mismatch. Handles `#`/`b` accidentals,
|
|
35
|
+
`min`/`m`/`maj` suffixes, absent tokens gracefully.
|
|
36
|
+
- **`get_session_diagnostics(check_clip_keys=True)`** — opt-in scan
|
|
37
|
+
across every audio clip, appending `clip_key_mismatch` warnings with
|
|
38
|
+
the one-call fix attached.
|
|
39
|
+
|
|
40
|
+
### Features shipped
|
|
41
|
+
|
|
42
|
+
- **Session continuity persistence wired at startup** (BUG from external
|
|
43
|
+
audit). `bind_project_store_from_session()` now computes a project
|
|
44
|
+
fingerprint, opens the matching `ProjectStore`, AND rehydrates
|
|
45
|
+
`_threads` + `_turns` from disk. Wired into `server.py` lifespan with
|
|
46
|
+
a lazy rebind on first `record_turn_resolution` / `open_creative_thread`.
|
|
47
|
+
Creative threads and turn history now survive server restarts — the
|
|
48
|
+
README's "return to a project" claim is now load-bearing.
|
|
49
|
+
- **Taste-aware `propose_next_best_move`** — replaces keyword-only
|
|
50
|
+
matching with `0.55 × keyword + 0.30 × taste_alignment + 0.15 ×
|
|
51
|
+
(1 − avoidance) ± 0.10 × family_bonus`. Cold-start users identical to
|
|
52
|
+
before; users with history get personalized ranking via
|
|
53
|
+
`dimension_weights` and `dimension_avoidances`. New return fields:
|
|
54
|
+
`score_breakdown`, `taste_active`, `taste_evidence_count`.
|
|
55
|
+
- **Evaluation `taste_fit`** — previously hardcoded to `0.0`. Now
|
|
56
|
+
computed from `outcome_history` by matching same-direction deltas on
|
|
57
|
+
the same dimensions: ±0.2 per kept/undone match, neutral 0.5 baseline.
|
|
58
|
+
- **`AutomationGraph.coverage_pct`** (BUG-D2, detection half) — new
|
|
59
|
+
fields `coverage_pct`, `clip_envelope_count`, `clips_scanned`
|
|
60
|
+
distinguish "no automation exists" from "we couldn't probe" from
|
|
61
|
+
"sparse but present". Surfaced in `get_project_brain_summary` as
|
|
62
|
+
`automation_coverage_pct`.
|
|
63
|
+
|
|
64
|
+
### Refactor (BUG-C1)
|
|
65
|
+
|
|
66
|
+
- **`mcp_server/tools/analyzer.py`** 1069 → 913 LOC. All 32
|
|
67
|
+
`@mcp.tool()` decorators stay in the module (FastMCP registration
|
|
68
|
+
order unchanged), helpers moved to `_analyzer_engine/`:
|
|
69
|
+
`context.py` (SpectralCache/bridge accessors, analyzer health check),
|
|
70
|
+
`sample.py` (Simpler post-load hygiene, filename heuristics),
|
|
71
|
+
`flucoma.py` (FluCoMa hint text, pitch-name table). Same
|
|
72
|
+
package-facade pattern as `_composition_engine` and `_agent_os_engine`.
|
|
73
|
+
- **`sync_metadata::get_domains()` now skips `_*`** directories + files
|
|
74
|
+
under `mcp_server/tools/`. Matches Python private-package convention,
|
|
75
|
+
prevents internal helpers from registering as false domains.
|
|
76
|
+
|
|
77
|
+
### Resilience (BUG-C3)
|
|
78
|
+
|
|
79
|
+
- **`_get_all_tools()` probe chain extended** in `mcp_server/server.py`
|
|
80
|
+
to 4 paths: existing `_tool_manager._tools` and
|
|
81
|
+
`_local_provider._components`, plus speculative 3.3+ rename
|
|
82
|
+
`_local_provider._tools` and the future public `mcp.list_tools()`.
|
|
83
|
+
Each wrapped in try/except. All-empty fall-through now prints
|
|
84
|
+
`fastmcp.__version__` + the attempted probe labels — prior silent `[]`
|
|
85
|
+
return would disable schema coercion with no signal.
|
|
86
|
+
- **New `_assert_tool_registry_accessible()`** self-test runs at module
|
|
87
|
+
import. Empty registry or a count mismatch against
|
|
88
|
+
`tests/test_tools_contract.py` fails loudly via stderr.
|
|
89
|
+
|
|
90
|
+
### Upstream (BUG-C4)
|
|
91
|
+
|
|
92
|
+
- **FastMCP feature request filed** —
|
|
93
|
+
[PrefectHQ/fastmcp#3967](https://github.com/PrefectHQ/fastmcp/issues/3967)
|
|
94
|
+
"Feature request: public tool-enumeration API". Migration is a
|
|
95
|
+
no-op once upstream lands a `mcp.list_tools()` or `mcp.tools`
|
|
96
|
+
surface — the probe chain already anticipates both.
|
|
97
|
+
|
|
98
|
+
### Metadata + docs
|
|
99
|
+
|
|
100
|
+
- **`sync_metadata.py` extended** — catches both `N tools` and hyphenated
|
|
101
|
+
`N-tool`, now covers `manifest.json` + `docs/manual/intelligence.md` +
|
|
102
|
+
`.claude-plugin/marketplace.json`. New prose-claim checks for
|
|
103
|
+
bridge-command count, enriched-device YAML count, and `GENRE_DEFAULTS`
|
|
104
|
+
key count — every narrative number now traces to a code derivation.
|
|
105
|
+
- **README / CLAUDE.md / docs drift closed** — 28→30 bridge commands,
|
|
106
|
+
81→71 enriched devices, 7→4 genre defaults, 323→325 tool count in
|
|
107
|
+
stale marketplace/plugin/manifest descriptors. Intelligence manual
|
|
108
|
+
example signatures for `record_positive_preference`,
|
|
109
|
+
`record_anti_preference`, `evaluate_move` updated to match the
|
|
110
|
+
shipping APIs.
|
|
111
|
+
- **Skill cleanups** — `livepilot-release` tool-count drift fixed;
|
|
112
|
+
`livepilot-wonder` reference paths corrected to point at their real
|
|
113
|
+
home in `livepilot-core`; arrangement vs composition-engine triggers
|
|
114
|
+
deduplicated (constructive vs analytical split).
|
|
115
|
+
- **New test** `tests/test_claim_consistency.py` — 12 guards running
|
|
116
|
+
`sync_metadata --check` from pytest, verifying `manifest.json` and
|
|
117
|
+
`intelligence.md` stay in the sweep, and asserting that
|
|
118
|
+
`bind_project_store_from_session` keeps a non-test caller.
|
|
119
|
+
|
|
120
|
+
### Quality-of-life
|
|
121
|
+
|
|
122
|
+
- **`scripts/test.sh`** — blessed test entrypoint always uses `.venv/bin/python`,
|
|
123
|
+
closes the contributor trap where bare `pytest` failed 28 tests on
|
|
124
|
+
system Python.
|
|
125
|
+
- **`.gitignore`** additions: `m4l_device/*.pre-presentation-backup`,
|
|
126
|
+
`m4l_device/*.pre-*-backup`, `.mcp.json.disabled`.
|
|
127
|
+
- **Stale `livepilot-1.10.5.tgz`** removed from the repo root.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 1.10.8 — Deep audit fix pass (April 18 2026)
|
|
132
|
+
|
|
133
|
+
Outcome of a cross-subsystem audit (Remote Script, MCP server, M4L bridge,
|
|
134
|
+
Sample/Splice/Atlas, Composer/Router, Installer, Tests). 2104 → 2116
|
|
135
|
+
passing tests (added ~230 regression tests, 324 tools, 45 domains). New
|
|
136
|
+
MCP tool: `reload_atlas`. Three orphan mix moves
|
|
137
|
+
(`make_kick_bass_lock`, `create_buildup_tension`, `smooth_scene_handoff`)
|
|
138
|
+
now produce real executable plans instead of silent zero-step failures.
|
|
139
|
+
A family compiler handles all device-creation moves. CI now enforces
|
|
140
|
+
metadata drift and `.amxd` freeze parity — preventing the class of bug
|
|
141
|
+
that cost two prior releases.
|
|
142
|
+
|
|
143
|
+
### Ship-stoppers
|
|
144
|
+
|
|
145
|
+
- **`capture_audio` backend annotation** fixed in `REDUCE_REPETITION` —
|
|
146
|
+
was declared `mcp_tool`, is actually `bridge_command` (matched your
|
|
147
|
+
memory note about the backend-annotation invariant).
|
|
148
|
+
- **Three orphan mix moves** (`make_kick_bass_lock`,
|
|
149
|
+
`create_buildup_tension`, `smooth_scene_handoff`) had no compilers
|
|
150
|
+
and produced silent zero-step plans — compilers added.
|
|
151
|
+
- **Seven device-creation orphan moves** fixed via a family-level
|
|
152
|
+
compiler that maps `plan_template` → `CompiledStep`.
|
|
153
|
+
- **`logger` used before definition** in `mcp_server/server.py`,
|
|
154
|
+
`mcp_server/tools/analyzer.py`, and
|
|
155
|
+
`mcp_server/translation_engine/tools.py` (the last one had the
|
|
156
|
+
definition buried inside a docstring — a genuine NameError on the
|
|
157
|
+
exception path). All three fixed; new regression test
|
|
158
|
+
`test_import_hygiene.py` will catch recurrences.
|
|
159
|
+
- **FluCoMa SHA256 bypass** removed — `ACCEPT_FIRST_RUN` sentinel is
|
|
160
|
+
gone. Verification is now mandatory; a fresh run with unpinned
|
|
161
|
+
hashes requires explicit
|
|
162
|
+
`LIVEPILOT_ALLOW_UNVERIFIED_FLUCOMA=1` opt-in.
|
|
163
|
+
- **FluCoMa Max 9 vs Max 8 path** fixed — detect whether Max 9 or Max 8
|
|
164
|
+
is actually installed instead of assuming the presence of
|
|
165
|
+
`Packages/` means the corresponding Max is installed. Fresh Max 9
|
|
166
|
+
machines were landing in the Max 8 legacy path.
|
|
167
|
+
|
|
168
|
+
### Correctness
|
|
169
|
+
|
|
170
|
+
- **Remote Script TCP UTF-8 boundary corruption** — accumulate raw bytes,
|
|
171
|
+
decode only on newline-framed lines. Previously a multi-byte sequence
|
|
172
|
+
straddling a 4096-byte recv boundary silently produced `\uFFFD`.
|
|
173
|
+
- **`_command_queue.get_nowait()` race** on `AssertionError` — drain by
|
|
174
|
+
response-queue identity, not blind FIFO pop.
|
|
175
|
+
- **`toggle_device` silent `parameters[0]` fallback** removed — now
|
|
176
|
+
raises `STATE_ERROR` if the device has no "Device On" parameter.
|
|
177
|
+
- **`modify_notes` partial-batch mutation** — two-pass validate-then-apply
|
|
178
|
+
in both `notes.py` and `arrangement.py`.
|
|
179
|
+
- **Browser deep-scan audio-thread stall** — `DEEP_MAX` reduced 200k → 20k,
|
|
180
|
+
clearer error pointing to `search_browser`.
|
|
181
|
+
- **`_force_reload_handlers` silent swallow** — reload exceptions now log
|
|
182
|
+
through the ControlSurface so stale handlers are surfaced.
|
|
183
|
+
- **`version_detect` failure caching** — no longer pins the whole session
|
|
184
|
+
to (12,0,0) on a transient detect failure.
|
|
185
|
+
- **Atlas non-atomic write** — tmp + fsync + rename pattern.
|
|
186
|
+
- **Atlas and corpus check-then-set race** — wrapped in the shared
|
|
187
|
+
`services.singletons.Singleton` helper. Atlas also auto-reloads when
|
|
188
|
+
`device_atlas.json` mtime advances. New `reload_atlas` MCP tool forces
|
|
189
|
+
a manual refresh.
|
|
190
|
+
- **`time.sleep()` inside the TCP connection lock** — moved outside the
|
|
191
|
+
lock so other async handlers aren't blocked on the idle timer.
|
|
192
|
+
- **M4L bridge chunk ordering** — out-of-order first-chunk now starts a
|
|
193
|
+
new bucket with a warning instead of corrupting the previous
|
|
194
|
+
sequence's payload.
|
|
195
|
+
- **M4L bridge `receiver=None`** — fail fast with an explicit error
|
|
196
|
+
instead of sending OSC blind and waiting out the full timeout.
|
|
197
|
+
- **Sample critic `-1.0` sentinel** — `overall_score` now respects the
|
|
198
|
+
`available` flag and averages only usable critics.
|
|
199
|
+
- **Splice gRPC timeouts** added per-call (`SearchSamples`, `SampleInfo`,
|
|
200
|
+
`ValidateLogin`, `SyncSounds`, `DownloadSample`).
|
|
201
|
+
- **Installer path traversal** — `LIVEPILOT_INSTALL_PATH` validated
|
|
202
|
+
against allowed roots.
|
|
203
|
+
- **Installer overlay upgrade** — rename existing install to
|
|
204
|
+
`LivePilot.backup-<ts>/` before fresh copy, auto-prune old backups.
|
|
205
|
+
Stale files from renamed modules no longer survive upgrades.
|
|
206
|
+
- **Installer `process.exit` vs `try/catch` mismatch** — `install()`
|
|
207
|
+
now raises typed `InstallerAbort` so the `--setup` wizard can
|
|
208
|
+
continue past a recoverable failure instead of dying mid-run.
|
|
209
|
+
- **`step_results` non-dict drop** — warns instead of silently losing
|
|
210
|
+
the binding.
|
|
211
|
+
- **Composer `_KEY_RE`** — tightened to require either accidental or
|
|
212
|
+
explicit quality word; "dark ambient" no longer parses as D major.
|
|
213
|
+
- **Composer section-plan overshoot** — final pass trims oversized
|
|
214
|
+
sections so snapping can't push total past `duration_bars`.
|
|
215
|
+
- **`export_clip_midi` extension guard** — refuses to write
|
|
216
|
+
non-`.mid`/`.midi` files after path resolution.
|
|
217
|
+
|
|
218
|
+
### CI + test hygiene
|
|
219
|
+
|
|
220
|
+
- `scripts/sync_metadata.py --check` is now a CI gate (three prior
|
|
221
|
+
drift releases were preventable).
|
|
222
|
+
- `.amxd` version-string guard in CI — refuses PRs where the frozen
|
|
223
|
+
bridge embeds a version that doesn't match the repo.
|
|
224
|
+
- `npm pack` cleanliness gate — fails on `.disabled`, `.backup`,
|
|
225
|
+
`.pre-*`, `.DS_Store`, or `.pyc` entries.
|
|
226
|
+
- `test_tools_contract` now asserts every tool has a non-empty
|
|
227
|
+
description (≥20 chars) and a schema.
|
|
228
|
+
- `test_move_annotations` silent-escape fixed — a declared backend
|
|
229
|
+
that classifies as `unknown` is now a hard failure.
|
|
230
|
+
- `test_bridge_parity` promoted from `INFO:` print to hard assertion.
|
|
231
|
+
- `test_corpus` adds a canary that fails when source files are absent
|
|
232
|
+
(previously 5 tests silently skipped).
|
|
233
|
+
- `tests/test_splice_client.py` scaffolded — credit floor, timeout
|
|
234
|
+
constants, port.conf parsing, graceful-degrade fallback (10 tests).
|
|
235
|
+
- `package.json` `files` allowlist added — npm pack is deterministic
|
|
236
|
+
(780 → 321 files, no dirty artifacts).
|
|
237
|
+
- CHANGELOG + `capability-modes.md` added to `VERSION_FILES` in
|
|
238
|
+
`sync_metadata.py`.
|
|
239
|
+
|
|
240
|
+
### Deferred (follow-up PR)
|
|
241
|
+
|
|
242
|
+
- Mechanical `lifespan_context.setdefault(...)` sweep across 7 files
|
|
243
|
+
(eager constructor issue — small perf impact, not correctness).
|
|
244
|
+
- `safe_call` helper to replace the ~14 remaining `except Exception:
|
|
245
|
+
pass/return None` patterns.
|
|
246
|
+
- FastMCP tool description quality sweep (audit existing 324 tools for
|
|
247
|
+
copy-paste / below-threshold descriptions).
|
|
248
|
+
|
|
249
|
+
### Release-process changes
|
|
250
|
+
|
|
251
|
+
- **FluCoMa SHA256 pinned** to `1a5cb73…6a2` (the universal zip containing
|
|
252
|
+
both macOS `.mxo` and Windows `.mxe64` externals). Previous releases
|
|
253
|
+
shipped with `"ACCEPT_FIRST_RUN"` sentinels that skipped verification.
|
|
254
|
+
- **`.amxd` refrozen** with matching `1.10.8` ping bytes. CI guard
|
|
255
|
+
(`amxd-freeze-drift`) enforces this on every push.
|
|
256
|
+
|
|
3
257
|
## 1.10.7 — npm .amxd parity + domain-count consistency (April 18 2026)
|
|
4
258
|
|
|
5
259
|
Shipping release. Brings npm's tarball back in line with the fresh `.amxd`
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
19
19
|
An agentic production system for Ableton Live 12.<br>
|
|
20
|
-
|
|
20
|
+
325 tools. 45 domains. Device atlas. Splice integration. Auto-composition. Spectral perception. Technique memory.
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
23
|
<br>
|
|
@@ -38,7 +38,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
38
38
|
| Layer | What it provides |
|
|
39
39
|
|-------|-----------------|
|
|
40
40
|
| **Deterministic Tools** | Direct control: transport, tracks, clips, notes, devices, scenes, mixing, arrangement, browser, automation |
|
|
41
|
-
| **Device Atlas** | Knowledge of every device in Ableton's library — 1305 devices indexed by name, URI, category, tag, and genre.
|
|
41
|
+
| **Device Atlas** | Knowledge of every device in Ableton's library — 1305 devices indexed by name, URI, category, tag, and genre. 71 enriched with sonic intelligence. 683 drum kits mapped |
|
|
42
42
|
| **Sample Engine** | Three-source sample intelligence — searches Ableton's browser, your filesystem, and Splice's catalog simultaneously. 6 fitness critics score every result. 29 processing techniques |
|
|
43
43
|
| **Spectral Perception** | Real-time ears via M4L — 8-band FFT, RMS/peak metering, Krumhansl-Schmuckler key detection, pitch tracking. Closes the feedback loop so the AI hears its own changes |
|
|
44
44
|
| **Technique Memory** | Persistent library of production decisions. Save a beat pattern, device chain, or mix template. Recall by mood, genre, or texture across sessions |
|
|
@@ -58,7 +58,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
58
58
|
│ │
|
|
59
59
|
│ Device Atlas 8-band FFT recall by mood, │
|
|
60
60
|
│ 1305 devices RMS / peak genre, texture │
|
|
61
|
-
│
|
|
61
|
+
│ 71 enriched pitch tracking 29 techniques │
|
|
62
62
|
│ 683 drum kits key detection replay into session │
|
|
63
63
|
│ │
|
|
64
64
|
│ Sample Engine Corpus Intelligence Taste Graph │
|
|
@@ -79,7 +79,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
79
79
|
│ └─────────────────┼──────────────────┘ │
|
|
80
80
|
│ ▼ │
|
|
81
81
|
│ ┌─────────────────┐ │
|
|
82
|
-
│ │
|
|
82
|
+
│ │ 325 MCP Tools │ │
|
|
83
83
|
│ │ 45 domains │ │
|
|
84
84
|
│ └────────┬────────┘ │
|
|
85
85
|
│ │ │
|
|
@@ -100,15 +100,15 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
100
100
|
|
|
101
101
|
**MCP Server** (`mcp_server/`) — Python FastMCP server. Validates inputs, routes commands to the Remote Script over TCP, manages the M4L bridge, runs the atlas, sample engine, composer, and all intelligence engines. This is what your AI client connects to.
|
|
102
102
|
|
|
103
|
-
**M4L Bridge** (`m4l_device/`) — Optional Max for Live Audio Effect on the master track. Provides deep LOM access through Max's LiveAPI that the ControlSurface API can't reach. UDP 9880 (M4L to server) carries spectral data and LiveAPI responses. OSC 9881 (server to M4L) sends commands.
|
|
103
|
+
**M4L Bridge** (`m4l_device/`) — Optional Max for Live Audio Effect on the master track. Provides deep LOM access through Max's LiveAPI that the ControlSurface API can't reach. UDP 9880 (M4L to server) carries spectral data and LiveAPI responses. OSC 9881 (server to M4L) sends commands. The 32 spectral/analyzer tools strictly require the bridge; device and sample tools that call the bridge also have graceful fallbacks, so core functionality works without it. Backed by 30 bridge commands for hidden parameters, Simpler internals, warp markers, display values, and Simpler warp / Compressor sidechain writes that live on child objects Python can't reach.
|
|
104
104
|
|
|
105
|
-
**Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 1305 devices with browser URIs,
|
|
105
|
+
**Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 1305 devices with browser URIs, 71 enriched with YAML sonic intelligence profiles (mood, genre, texture, recommended chains). 6 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre. The AI never hallucinates a device name or preset — it always resolves against the atlas first.
|
|
106
106
|
|
|
107
107
|
**Sample Engine** (`mcp_server/sample_engine/`) — Searches three sources simultaneously: BrowserSource (Ableton's library), SpliceSource (local Splice catalog via SQLite), FilesystemSource (user directories). Every result passes through a 6-critic fitness battery (key, tempo, spectral, genre, mood, technical). 29 processing techniques (Surgeon precision vs. Alchemist experimentation). Builds complete sample processing plans with warp, slice, and effect recommendations.
|
|
108
108
|
|
|
109
|
-
**Splice Client** (`mcp_server/splice_client/`) —
|
|
109
|
+
**Splice Client** (`mcp_server/splice_client/`) — Searches Splice's catalog through two layers: the local SQLite database (`sounds.db`, already-downloaded samples) and the live gRPC API (full catalog, including samples you haven't downloaded yet). The gRPC client auto-detects Splice's dynamic port via `port.conf`, handles self-signed TLS, and enforces a 5-credit safety floor before any download. Per-call timeouts (5–10s) prevent a hung Splice process from stalling the MCP event loop. Graceful fallback to SQL-only if grpcio isn't installed. No API key needed — authentication comes from the running Splice desktop app.
|
|
110
110
|
|
|
111
|
-
**Composer** (`mcp_server/composer/`) — Prompt-to-plan pipeline. Parses natural language ("dark minimal techno 128bpm with industrial textures") into a CompositionIntent (genre, mood, tempo, key). Plans layers using role templates (kick, bass, percussion, texture, lead, pad, fx). Compiles to a step-by-step plan of tool calls that the agent executes. Does not execute autonomously — returns the plan.
|
|
111
|
+
**Composer** (`mcp_server/composer/`) — Prompt-to-plan pipeline. Parses natural language ("dark minimal techno 128bpm with industrial textures") into a CompositionIntent (genre, mood, tempo, key). Plans layers using role templates (kick, bass, percussion, texture, lead, pad, fx). Compiles to a step-by-step plan of tool calls that the agent executes. Does not execute autonomously — returns the plan. 4 genre defaults (house, techno, trap, ambient) — genres outside this set fall back to a neutral layer plan.
|
|
112
112
|
|
|
113
113
|
**Corpus** (`mcp_server/corpus/`) — Parsed device-knowledge markdown converted to queryable Python structures: EmotionalRecipe, GenreChain, PhysicalModelRecipe, AutomationGesture. Feeds Wonder Mode, Sound Design critics, and the Composer with deep creative knowledge at runtime — not just LLM prompts, actual structured data.
|
|
114
114
|
|
|
@@ -120,7 +120,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
|
|
|
120
120
|
|
|
121
121
|
## The Intelligence Layer
|
|
122
122
|
|
|
123
|
-
12 engines sit on top of the
|
|
123
|
+
12 engines sit on top of the 325 tools. They give the AI musical judgment, not just musical execution.
|
|
124
124
|
|
|
125
125
|
### SongBrain — What the Song Is
|
|
126
126
|
|
|
@@ -156,7 +156,7 @@ When a session is stuck — repeated undos, overpolished loops, no structural pr
|
|
|
156
156
|
|
|
157
157
|
### Hook Hunter
|
|
158
158
|
|
|
159
|
-
Identifies the most salient musical idea — ranks by
|
|
159
|
+
Identifies the most salient musical idea — ranks candidates by recurrence across scenes, motif salience, and section placement (payoff-section boost). Tracks whether hooks are developed, neglected, or undermined, and flags when a transition fails to deliver expected payoff. Rhythm-side ranking is currently heuristic (drum-track detection + clip reuse); true onset-based rhythmic features are on the roadmap.
|
|
160
160
|
|
|
161
161
|
### Session Continuity
|
|
162
162
|
|
|
@@ -172,7 +172,7 @@ Every engine follows: **measure before → act → measure after → compare**.
|
|
|
172
172
|
|
|
173
173
|
## Tools
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
325 tools across 45 domains. Highlights below — [full catalog here](docs/manual/tool-catalog.md).
|
|
176
176
|
|
|
177
177
|
<br>
|
|
178
178
|
|
|
@@ -204,7 +204,7 @@ Every engine follows: **measure before → act → measure after → compare**.
|
|
|
204
204
|
The M4L Analyzer sits on the master track. UDP 9880 carries spectral data to the server. OSC 9881 sends commands back.
|
|
205
205
|
|
|
206
206
|
> [!TIP]
|
|
207
|
-
>
|
|
207
|
+
> Most tools work without the analyzer — it adds 32 spectral/analyzer tools (frequency, loudness, perception) and closes the feedback loop.
|
|
208
208
|
|
|
209
209
|
```
|
|
210
210
|
SPECTRAL ─────── 8-band frequency decomposition (sub → air)
|
|
@@ -232,7 +232,7 @@ The atlas is an in-memory indexed database of Ableton's entire device library.
|
|
|
232
232
|
|
|
233
233
|
```
|
|
234
234
|
1305 devices total
|
|
235
|
-
|
|
235
|
+
71 enriched with sonic intelligence (mood, genre, texture, chains)
|
|
236
236
|
683 drum kits mapped with note assignments
|
|
237
237
|
6 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre
|
|
238
238
|
```
|
|
@@ -287,7 +287,7 @@ LivePilot reads Splice's local SQLite database to search your downloaded samples
|
|
|
287
287
|
|
|
288
288
|
**How it works:** The Sample Engine's `SpliceSource` reads `~/Library/Application Support/com.splice.Splice/users/default/*/sounds.db` — Splice's local SQLite catalog of downloaded samples. Read-only, no network calls.
|
|
289
289
|
|
|
290
|
-
**Requirements:** Splice desktop app
|
|
290
|
+
**Requirements:** Splice desktop app running (the MCP server talks to it over gRPC at a dynamic port advertised via `port.conf`, with self-signed TLS). For fully offline search, previously-downloaded samples are always searchable via the local SQLite fallback even if the Splice app isn't running.
|
|
291
291
|
|
|
292
292
|
<br>
|
|
293
293
|
|
|
@@ -317,7 +317,7 @@ Prompt-to-plan auto-composition engine.
|
|
|
317
317
|
└─────────────────┘
|
|
318
318
|
```
|
|
319
319
|
|
|
320
|
-
-
|
|
320
|
+
- 4 genre defaults: house, techno, trap, ambient (unknown genres fall back to a neutral plan)
|
|
321
321
|
- Returns step-by-step plans — the agent executes each tool call in sequence
|
|
322
322
|
- `compose` — plan a multi-layer composition from text prompt
|
|
323
323
|
- `augment_with_samples` — plan sample-based layers for existing session
|
|
@@ -360,7 +360,7 @@ The V2 intelligence layer. These tools analyze, diagnose, plan, evaluate, and le
|
|
|
360
360
|
| Creative Constraints | 5 | constraint activation, reference-inspired variants |
|
|
361
361
|
| Preview Studio | 5 | variant creation, preview rendering, comparison, commit |
|
|
362
362
|
|
|
363
|
-
> **[View all
|
|
363
|
+
> **[View all 325 tools →](docs/manual/tool-catalog.md)**
|
|
364
364
|
|
|
365
365
|
<br>
|
|
366
366
|
|
|
@@ -572,6 +572,8 @@ npx livepilot --version # Show version
|
|
|
572
572
|
git clone https://github.com/dreamrec/LivePilot.git
|
|
573
573
|
cd LivePilot
|
|
574
574
|
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
|
|
575
|
+
# Test runner is not in requirements.txt (runtime-only deps) — install it explicitly:
|
|
576
|
+
.venv/bin/pip install pytest pytest-asyncio
|
|
575
577
|
.venv/bin/pytest tests/ -v
|
|
576
578
|
```
|
|
577
579
|
|
|
@@ -585,7 +587,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for architecture details, code guidelines
|
|
|
585
587
|
|
|
586
588
|
| Document | What's inside |
|
|
587
589
|
|----------|---------------|
|
|
588
|
-
| [Manual](docs/manual/index.md) | Complete reference: architecture, all
|
|
590
|
+
| [Manual](docs/manual/index.md) | Complete reference: architecture, all 325 tools, workflows |
|
|
589
591
|
| [Intelligence Layer](docs/manual/intelligence.md) | How the 12 engines connect — conductor, moves, preview, evaluation |
|
|
590
592
|
| [Device Atlas](docs/manual/device-atlas.md) | 1305 devices indexed — search, suggest, chain building |
|
|
591
593
|
| [Samples & Slicing](docs/manual/samples.md) | 3-source search, fitness critics, slice workflows |
|
package/bin/livepilot.js
CHANGED
|
@@ -397,6 +397,44 @@ async function doctor() {
|
|
|
397
397
|
// FluCoMa installer
|
|
398
398
|
// ---------------------------------------------------------------------------
|
|
399
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Detect whether Max (major version) is installed on the system. Returns the
|
|
402
|
+
* highest installed major version number, or 0 if Max is not installed.
|
|
403
|
+
*
|
|
404
|
+
* macOS: checks /Applications/Max.app/Contents/Info.plist for CFBundleShortVersionString.
|
|
405
|
+
* Windows: checks standard install locations under Program Files.
|
|
406
|
+
*/
|
|
407
|
+
function detectMaxMajorVersion() {
|
|
408
|
+
try {
|
|
409
|
+
if (process.platform === "darwin") {
|
|
410
|
+
const infoPlist = "/Applications/Max.app/Contents/Info.plist";
|
|
411
|
+
if (!fs.existsSync(infoPlist)) return 0;
|
|
412
|
+
const out = execFileSync("defaults", ["read", infoPlist, "CFBundleShortVersionString"], {
|
|
413
|
+
encoding: "utf-8",
|
|
414
|
+
timeout: 3000,
|
|
415
|
+
}).trim();
|
|
416
|
+
const m = out.match(/^(\d+)/);
|
|
417
|
+
return m ? parseInt(m[1], 10) : 0;
|
|
418
|
+
}
|
|
419
|
+
if (process.platform === "win32") {
|
|
420
|
+
const candidates = [
|
|
421
|
+
"C:\\Program Files\\Cycling '74\\Max 9",
|
|
422
|
+
"C:\\Program Files\\Cycling '74\\Max 8",
|
|
423
|
+
];
|
|
424
|
+
for (const candidate of candidates) {
|
|
425
|
+
if (fs.existsSync(candidate)) {
|
|
426
|
+
const m = candidate.match(/Max (\d+)/);
|
|
427
|
+
if (m) return parseInt(m[1], 10);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return 0;
|
|
431
|
+
}
|
|
432
|
+
} catch {
|
|
433
|
+
return 0;
|
|
434
|
+
}
|
|
435
|
+
return 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
400
438
|
async function setupFlucoma() {
|
|
401
439
|
const os = require("os");
|
|
402
440
|
const https = require("https");
|
|
@@ -404,9 +442,10 @@ async function setupFlucoma() {
|
|
|
404
442
|
const home = os.homedir();
|
|
405
443
|
|
|
406
444
|
// Max 9 is the current release (the Ableton Live 12.3+ default); Max 8 is
|
|
407
|
-
// the legacy path.
|
|
408
|
-
//
|
|
409
|
-
//
|
|
445
|
+
// the legacy path. Select based on which major Max is actually installed —
|
|
446
|
+
// NOT on whether the Packages directory exists. A fresh Max 9 install often
|
|
447
|
+
// has no Packages folder yet, so the old fs.existsSync() check silently
|
|
448
|
+
// steered fresh Max 9 machines onto the Max 8 legacy path.
|
|
410
449
|
const docsBase = process.platform === "darwin"
|
|
411
450
|
? path.join(home, "Documents")
|
|
412
451
|
: path.join(process.env.USERPROFILE || home, "Documents");
|
|
@@ -414,8 +453,24 @@ async function setupFlucoma() {
|
|
|
414
453
|
const max9PackagesDir = path.join(docsBase, "Max 9", "Packages");
|
|
415
454
|
const max8PackagesDir = path.join(docsBase, "Max 8", "Packages");
|
|
416
455
|
|
|
417
|
-
|
|
418
|
-
|
|
456
|
+
const maxMajor = detectMaxMajorVersion();
|
|
457
|
+
let packagesDir;
|
|
458
|
+
if (maxMajor >= 9) {
|
|
459
|
+
packagesDir = max9PackagesDir;
|
|
460
|
+
} else if (maxMajor === 8) {
|
|
461
|
+
packagesDir = max8PackagesDir;
|
|
462
|
+
} else {
|
|
463
|
+
// Max not detected — fall back to whichever Packages dir already exists,
|
|
464
|
+
// preferring Max 9. If neither exists, default to Max 9 (future-proof).
|
|
465
|
+
if (fs.existsSync(max9PackagesDir)) {
|
|
466
|
+
packagesDir = max9PackagesDir;
|
|
467
|
+
} else if (fs.existsSync(max8PackagesDir)) {
|
|
468
|
+
packagesDir = max8PackagesDir;
|
|
469
|
+
} else {
|
|
470
|
+
console.log("Could not detect Max installation. Defaulting to Max 9 Packages path.");
|
|
471
|
+
packagesDir = max9PackagesDir;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
419
474
|
const flucomaDir = path.join(packagesDir, "FluidCorpusManipulation");
|
|
420
475
|
|
|
421
476
|
// Check BOTH locations for an existing install — a user may have Max 8
|
|
@@ -440,19 +495,29 @@ async function setupFlucoma() {
|
|
|
440
495
|
return;
|
|
441
496
|
}
|
|
442
497
|
|
|
443
|
-
// Ensure the parent Packages directory exists for the install target
|
|
498
|
+
// Ensure the parent Packages directory exists for the install target — Max
|
|
499
|
+
// lazily creates Packages/ on first package install, so it may be absent on
|
|
500
|
+
// a fresh Max 9 system.
|
|
444
501
|
fs.mkdirSync(packagesDir, { recursive: true });
|
|
445
502
|
|
|
446
503
|
console.log("FluCoMa not found. Downloading from GitHub...");
|
|
447
504
|
const crypto = require("crypto");
|
|
448
505
|
|
|
449
506
|
// Pin to a known release tag for reproducibility and security.
|
|
450
|
-
//
|
|
507
|
+
//
|
|
508
|
+
// IMPORTANT: FluCoMa 1.0.7 ships as a single universal zip that contains
|
|
509
|
+
// both the macOS externals (.mxo) and the Windows externals (.mxe64).
|
|
510
|
+
// There is ONE hash to pin, not two. If a future release reverts to
|
|
511
|
+
// per-platform zips, convert FLUCOMA_SHA256 back to a {Mac, Windows}
|
|
512
|
+
// dict and restore the platform-specific asset finder.
|
|
513
|
+
//
|
|
514
|
+
// To re-pin after a version bump:
|
|
515
|
+
// curl -L -o flucoma.zip <release-zip-url>
|
|
516
|
+
// shasum -a 256 flucoma.zip # macOS
|
|
517
|
+
// CertUtil -hashfile flucoma.zip SHA256 # Windows
|
|
518
|
+
// then paste the 64-char hex digest into FLUCOMA_SHA256 below.
|
|
451
519
|
const FLUCOMA_TAG = "1.0.7";
|
|
452
|
-
const FLUCOMA_SHA256 =
|
|
453
|
-
Mac: "ACCEPT_FIRST_RUN", // Set to actual hash after first verified download
|
|
454
|
-
Windows: "ACCEPT_FIRST_RUN",
|
|
455
|
-
};
|
|
520
|
+
const FLUCOMA_SHA256 = "1a5cb7340e8816a9983b981a5a84ddb95b63e6d71446f278b9dc81c3cc1206a2";
|
|
456
521
|
const FLUCOMA_URL = `https://api.github.com/repos/flucoma/flucoma-max/releases/tags/${FLUCOMA_TAG}`;
|
|
457
522
|
|
|
458
523
|
// Fetch pinned release info
|
|
@@ -476,12 +541,16 @@ async function setupFlucoma() {
|
|
|
476
541
|
}).on("error", reject);
|
|
477
542
|
});
|
|
478
543
|
|
|
479
|
-
|
|
480
|
-
|
|
544
|
+
// FluCoMa 1.0.7 publishes a single universal zip — pick the first .zip
|
|
545
|
+
// asset on the release page. If upstream starts shipping per-platform
|
|
546
|
+
// zips again, reintroduce the filename filter here.
|
|
547
|
+
const platform = process.platform === "darwin" ? "macOS" : "Windows";
|
|
548
|
+
const zipAsset = (releaseInfo.assets || []).find(a => a.name.endsWith(".zip"));
|
|
481
549
|
if (!zipAsset) {
|
|
482
|
-
console.error("Error: no
|
|
550
|
+
console.error("Error: no .zip asset found in FluCoMa release %s", FLUCOMA_TAG);
|
|
483
551
|
process.exit(1);
|
|
484
552
|
}
|
|
553
|
+
console.log("Target platform: %s (zip contains externals for both)", platform);
|
|
485
554
|
|
|
486
555
|
console.log("Downloading %s (v%s, %sMB)...", zipAsset.name, FLUCOMA_TAG,
|
|
487
556
|
Math.round(zipAsset.size / 1024 / 1024));
|
|
@@ -512,22 +581,50 @@ async function setupFlucoma() {
|
|
|
512
581
|
const hash = crypto.createHash("sha256");
|
|
513
582
|
hash.update(fs.readFileSync(zipPath));
|
|
514
583
|
const sha256 = hash.digest("hex");
|
|
515
|
-
const expectedHash = FLUCOMA_SHA256
|
|
584
|
+
const expectedHash = FLUCOMA_SHA256;
|
|
516
585
|
console.log("SHA256: %s", sha256);
|
|
517
586
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
587
|
+
const isPinned = expectedHash && expectedHash !== "UNPINNED"
|
|
588
|
+
&& /^[0-9a-f]{64}$/i.test(expectedHash);
|
|
589
|
+
|
|
590
|
+
if (isPinned) {
|
|
591
|
+
if (sha256.toLowerCase() !== expectedHash.toLowerCase()) {
|
|
592
|
+
console.error("");
|
|
593
|
+
console.error(" SHA256 MISMATCH — refusing to install.");
|
|
594
|
+
console.error(" expected: %s", expectedHash);
|
|
595
|
+
console.error(" actual: %s", sha256);
|
|
596
|
+
console.error("");
|
|
597
|
+
console.error(" The downloaded file does not match the pinned hash.");
|
|
598
|
+
console.error(" Either the release changed upstream, or the download was tampered with.");
|
|
599
|
+
console.error(" Verify at https://github.com/flucoma/flucoma-max/releases/tag/%s", FLUCOMA_TAG);
|
|
600
|
+
console.error("");
|
|
523
601
|
try { fs.rmSync(tmpDir, { recursive: true }); } catch {}
|
|
524
602
|
process.exit(1);
|
|
525
603
|
}
|
|
526
604
|
console.log("Checksum verified ✓");
|
|
527
605
|
} else {
|
|
528
|
-
//
|
|
529
|
-
|
|
530
|
-
|
|
606
|
+
// Hash is not yet pinned. Require an explicit opt-in so unverified
|
|
607
|
+
// installs are never silent — the previous "ACCEPT_FIRST_RUN" sentinel
|
|
608
|
+
// auto-accepted every run.
|
|
609
|
+
const allowUnverified = process.env.LIVEPILOT_ALLOW_UNVERIFIED_FLUCOMA === "1";
|
|
610
|
+
if (!allowUnverified) {
|
|
611
|
+
console.error("");
|
|
612
|
+
console.error(" FluCoMa SHA256 is not pinned.");
|
|
613
|
+
console.error(" Downloaded hash: %s", sha256);
|
|
614
|
+
console.error("");
|
|
615
|
+
console.error(" Refusing to install an unverified binary by default.");
|
|
616
|
+
console.error(" To proceed (and help pin the hash), re-run with:");
|
|
617
|
+
console.error(" LIVEPILOT_ALLOW_UNVERIFIED_FLUCOMA=1 npx livepilot --setup-flucoma");
|
|
618
|
+
console.error("");
|
|
619
|
+
console.error(" Then open a PR that sets FLUCOMA_SHA256 in bin/livepilot.js to:");
|
|
620
|
+
console.error(" '%s'", sha256);
|
|
621
|
+
console.error("");
|
|
622
|
+
try { fs.rmSync(tmpDir, { recursive: true }); } catch {}
|
|
623
|
+
process.exit(1);
|
|
624
|
+
}
|
|
625
|
+
console.warn("⚠ Installing unverified FluCoMa — LIVEPILOT_ALLOW_UNVERIFIED_FLUCOMA=1 was set.");
|
|
626
|
+
console.warn(" Record this SHA256 in FLUCOMA_SHA256:");
|
|
627
|
+
console.warn(" '%s'", sha256);
|
|
531
628
|
}
|
|
532
629
|
|
|
533
630
|
console.log("Extracting to %s...", packagesDir);
|
|
@@ -601,9 +698,22 @@ async function setup() {
|
|
|
601
698
|
console.log("");
|
|
602
699
|
console.log("Step 2/5: Installing Remote Script...");
|
|
603
700
|
try {
|
|
604
|
-
const { install } = require(path.join(ROOT, "installer", "install.js"));
|
|
605
|
-
|
|
606
|
-
|
|
701
|
+
const { install, InstallerAbort } = require(path.join(ROOT, "installer", "install.js"));
|
|
702
|
+
try {
|
|
703
|
+
install();
|
|
704
|
+
console.log(" ✓ Remote Script installed");
|
|
705
|
+
} catch (err) {
|
|
706
|
+
if (err instanceof InstallerAbort && err.recoverable) {
|
|
707
|
+
// Recoverable — don't bail the wizard. The user can rerun
|
|
708
|
+
// --install manually, and later steps (Python env, M4L Analyzer,
|
|
709
|
+
// diagnostics) may still succeed or at least inform them.
|
|
710
|
+
console.log(" ⚠ Skipped: %s", err.message.split("\n")[0]);
|
|
711
|
+
console.log(" (Continuing with remaining setup steps.)");
|
|
712
|
+
ok = false;
|
|
713
|
+
} else {
|
|
714
|
+
throw err;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
607
717
|
} catch (err) {
|
|
608
718
|
console.log(" ✗ Failed: %s", err.message);
|
|
609
719
|
ok = false;
|
|
@@ -720,8 +830,16 @@ async function main() {
|
|
|
720
830
|
|
|
721
831
|
// --install
|
|
722
832
|
if (flag === "--install") {
|
|
723
|
-
const { install } = require(path.join(ROOT, "installer", "install.js"));
|
|
724
|
-
|
|
833
|
+
const { install, InstallerAbort } = require(path.join(ROOT, "installer", "install.js"));
|
|
834
|
+
try {
|
|
835
|
+
install();
|
|
836
|
+
} catch (err) {
|
|
837
|
+
if (err instanceof InstallerAbort) {
|
|
838
|
+
console.error(err.message);
|
|
839
|
+
process.exit(err.recoverable ? 2 : 1);
|
|
840
|
+
}
|
|
841
|
+
throw err;
|
|
842
|
+
}
|
|
725
843
|
return;
|
|
726
844
|
}
|
|
727
845
|
|