|
463 | 463 | (some-> (safe-read-json-string (slurp config-file) (var *global-config-error*)) |
464 | 464 | (parse-dynamic-string-values (shared/global-config-dir)))))) |
465 | 465 |
|
466 | | -(defn ^:private config-from-local-file [roots] |
467 | | - (reduce |
468 | | - (fn [final-config {:keys [uri]}] |
469 | | - (merge |
470 | | - final-config |
471 | | - (let [config-dir (io/file (shared/uri->filename uri) ".eca") |
472 | | - config-file (io/file config-dir "config.json")] |
473 | | - (when (.exists config-file) |
474 | | - (some-> (safe-read-json-string (slurp config-file) (var *local-config-error*)) |
475 | | - (parse-dynamic-string-values config-dir)))))) |
476 | | - {} |
477 | | - roots)) |
| 466 | +(declare merge-config) |
| 467 | + |
| 468 | +(defn ^:private config-from-local-file [roots config] |
| 469 | + (let [layers (mapv (fn [{:keys [uri]}] |
| 470 | + (let [config-dir (io/file (shared/uri->filename uri) ".eca") |
| 471 | + config-file (io/file config-dir "config.json")] |
| 472 | + (when (.exists config-file) |
| 473 | + (some-> (safe-read-json-string (slurp config-file) (var *local-config-error*)) |
| 474 | + (parse-dynamic-string-values config-dir))))) |
| 475 | + roots) |
| 476 | + ;; Keep the existing shallow merge across roots for unrelated fields. |
| 477 | + local-config (reduce merge {} (map #(dissoc % "plugins") layers))] |
| 478 | + (reduce merge-config |
| 479 | + (merge-config config local-config) |
| 480 | + (map #(select-keys % ["plugins"]) layers)))) |
478 | 481 |
|
479 | 482 | (def initialization-config* (atom {})) |
480 | 483 |
|
|
517 | 520 | listed order (later entries win). Missing paths are logged and skipped; |
518 | 521 | parse errors are logged, surfaced via `*extra-config-error*` and skipped. |
519 | 522 | Non-recursive: an `:extraConfigs` declared inside an extra file is ignored." |
520 | | - [paths roots] |
| 523 | + [paths roots config] |
521 | 524 | (let [paths (cond |
522 | 525 | (string? paths) [paths] |
523 | 526 | (sequential? paths) paths |
524 | | - :else [])] |
525 | | - (reduce |
526 | | - (fn [final-config path] |
527 | | - (let [^File config-file (resolve-extra-config-file path roots)] |
528 | | - (if (.exists config-file) |
529 | | - (deep-merge final-config |
530 | | - (or (some-> (safe-read-json-string (slurp config-file) (var *extra-config-error*)) |
| 527 | + :else []) |
| 528 | + layers (mapv (fn [path] |
| 529 | + (let [^File config-file (resolve-extra-config-file path roots)] |
| 530 | + (if (.exists config-file) |
| 531 | + (some-> (safe-read-json-string (slurp config-file) (var *extra-config-error*)) |
531 | 532 | (parse-dynamic-string-values (fs/file (fs/parent config-file)))) |
532 | | - {})) |
533 | | - (do |
534 | | - (logger/warn logger-tag (format "extraConfigs path not found, skipping: %s" (.getPath config-file))) |
535 | | - final-config)))) |
536 | | - {} |
537 | | - paths))) |
| 533 | + (do |
| 534 | + (logger/warn logger-tag (format "extraConfigs path not found, skipping: %s" (.getPath config-file))) |
| 535 | + nil)))) |
| 536 | + paths) |
| 537 | + ;; Preserve the existing aggregation for everything except plugins. |
| 538 | + extra-config (reduce deep-merge {} (map #(dissoc % "plugins") layers))] |
| 539 | + (reduce merge-config |
| 540 | + (merge-config config extra-config) |
| 541 | + (map #(select-keys % ["plugins"]) layers)))) |
538 | 542 |
|
539 | 543 | (defn ^:private resolve-agent-inheritance |
540 | 544 | "Resolves :inherit keys in agent configs. When an agent has :inherit \"other\", |
|
703 | 707 | (-> (assoc-in [:chat :defaultAgent] (migrate-legacy-agent-name (get-in config [:chat :defaultBehavior]))) |
704 | 708 | (update :chat dissoc :defaultBehavior)))) |
705 | 709 |
|
| 710 | +(defn ^:private merge-config [config layer] |
| 711 | + (let [layer (normalize-fields normalization-rules layer) |
| 712 | + plugins (:plugins layer) |
| 713 | + merged (deep-merge config layer)] |
| 714 | + (if (contains? plugins "install") |
| 715 | + (assoc-in merged [:plugins "install"] |
| 716 | + (->> (concat (when-not (= "replace" (get plugins "installMode")) |
| 717 | + (get-in config [:plugins "install"])) |
| 718 | + (get plugins "install")) |
| 719 | + reverse distinct reverse vec)) |
| 720 | + merged))) |
| 721 | + |
706 | 722 | (defn ^:private all* [db] |
707 | 723 | (let [initialization-config @initialization-config* |
708 | 724 | pure-config? (:pureConfig initialization-config) |
709 | | - merge-config (fn [c1 c2] |
710 | | - (deep-merge c1 (normalize-fields normalization-rules c2))) |
711 | 725 | plugin-data (when-not pure-config? @plugin-components*) |
712 | 726 | plugin-config (when plugin-data |
713 | 727 | (let [cfg (:config-fragment plugin-data)] |
|
723 | 737 | (config-from-envvar))) |
724 | 738 | (if-let [custom-config (config-from-custom)] |
725 | 739 | (merge-config $ (when-not pure-config? custom-config)) |
726 | | - (-> $ |
727 | | - (merge-config (when-not pure-config? (config-from-global-file))) |
728 | | - (merge-config (when-not pure-config? (config-from-local-file (:workspace-folders db)))))) |
| 740 | + (let [config (merge-config $ (when-not pure-config? (config-from-global-file)))] |
| 741 | + (if pure-config? |
| 742 | + config |
| 743 | + (config-from-local-file (:workspace-folders db) config)))) |
729 | 744 | ;; Plugin config merges after all file configs (user local config wins via later merge) |
730 | 745 | (merge-config $ plugin-config) |
731 | 746 | ;; extraConfigs merge last, overriding all previous sources |
732 | | - (merge-config $ (config-from-extra-configs (:extraConfigs $) (:workspace-folders db)))) |
| 747 | + (config-from-extra-configs (:extraConfigs $) (:workspace-folders db) $)) |
733 | 748 | ;; Append plugin commands/rules (vector concat, not deep-merge replace) |
734 | 749 | (cond-> |
735 | 750 | (seq plugin-commands) (update :commands #(vec (concat % plugin-commands))) |
|
773 | 788 | needed before the server is fully initialized (e.g. network/TLS |
774 | 789 | settings)." |
775 | 790 | [] |
776 | | - (let [merge-config (fn [c1 c2] |
777 | | - (deep-merge c1 (normalize-fields normalization-rules c2)))] |
778 | | - (-> {} |
779 | | - (merge-config (initial-config)) |
780 | | - (merge-config (config-from-envvar)) |
781 | | - (merge-config (if (some? @custom-config-file-path*) |
782 | | - (config-from-custom) |
783 | | - (config-from-global-file)))))) |
| 791 | + (-> {} |
| 792 | + (merge-config (initial-config)) |
| 793 | + (merge-config (config-from-envvar)) |
| 794 | + (merge-config (if (some? @custom-config-file-path*) |
| 795 | + (config-from-custom) |
| 796 | + (config-from-global-file))))) |
784 | 797 |
|
785 | 798 | (defn validation-error [] |
786 | 799 | (cond |
|
0 commit comments