|
6 | 6 | 3. local config-file: searching from a local `.eca/config.json` file. |
7 | 7 | 4. `initializatonOptions` sent in `initialize` request." |
8 | 8 | (:require |
9 | | - [camel-snake-kebab.core :as csk] |
10 | 9 | [cheshire.core :as json] |
11 | 10 | [cheshire.factory :as json.factory] |
12 | 11 | [clojure.core.memoize :as memoize] |
13 | 12 | [clojure.java.io :as io] |
14 | 13 | [clojure.string :as string] |
15 | 14 | [eca.logger :as logger] |
16 | | - [eca.shared :as shared]) |
| 15 | + [eca.shared :as shared] |
| 16 | + [camel-snake-kebab.core :as csk]) |
17 | 17 | (:import |
18 | 18 | [java.io File])) |
19 | 19 |
|
|
57 | 57 | :excludeCommands []} |
58 | 58 | :editor {:enabled true}} |
59 | 59 | :disabledTools [] |
| 60 | + :toolCall {:approval {:byDefault "ask" |
| 61 | + :allow {} |
| 62 | + :ask {}}} |
60 | 63 | :mcpTimeoutSeconds 60 |
61 | 64 | :lspTimeoutSeconds 30 |
62 | 65 | :mcpServers {} |
|
75 | 78 | (try |
76 | 79 | (binding [json.factory/*json-factory* (json.factory/make-json-factory |
77 | 80 | {:allow-comments true})] |
78 | | - (json/parse-string raw-string true)) |
| 81 | + (json/parse-string raw-string)) |
79 | 82 | (catch Exception e |
80 | 83 | (logger/warn "Error parsing config json:" (.getMessage e))))) |
81 | 84 |
|
|
126 | 129 |
|
127 | 130 | (def ollama-model-prefix "ollama/") |
128 | 131 |
|
129 | | -(defn ^:private normalize-providers [providers] |
130 | | - (letfn [(norm-key [k] |
131 | | - (csk/->kebab-case (string/replace-first (str k) ":" "")))] |
132 | | - (reduce-kv (fn [m k v] |
133 | | - (let [nk (norm-key k)] |
134 | | - (if (contains? m nk) |
135 | | - (update m nk #(deep-merge % v)) |
136 | | - (assoc m nk v)))) |
137 | | - {} |
138 | | - providers))) |
139 | | - |
140 | | -(defn ^:private normalize-provider-models [provider] |
141 | | - (let [models (or (:models provider) |
142 | | - (get provider "models")) |
143 | | - models' (when models |
144 | | - (into {} |
145 | | - (map (fn [[k v]] |
146 | | - [(if (or (keyword? k) (symbol? k)) |
147 | | - (string/replace-first (str k) ":" "") |
148 | | - (str k)) |
149 | | - v]) |
150 | | - models))) |
151 | | - provider' (dissoc provider "models")] |
152 | | - (if models' |
153 | | - (assoc provider' :models models') |
154 | | - provider'))) |
155 | | - |
156 | | -(defn ^:private normalize-fields [config] |
157 | | - (-> config |
158 | | - (update-in [:providers] |
159 | | - (fn [providers] |
160 | | - (when providers |
161 | | - (-> (normalize-providers providers) |
162 | | - (update-vals normalize-provider-models))))))) |
| 132 | +(defn ^:private normalize-fields |
| 133 | + "Converts a deep nested map where keys are strings to keywords. |
| 134 | + normalization-rules follow the nest order, :ANY means any field name. |
| 135 | + :kebab-case means convert field names to kebab-case. |
| 136 | + :stringfy means convert field names to strings." |
| 137 | + [normalization-rules m] |
| 138 | + (let [kc-paths (set (:kebab-case normalization-rules)) |
| 139 | + str-paths (set (:stringfy normalization-rules)) |
| 140 | + ; match a current path against a rule path with :ANY wildcard |
| 141 | + matches-path? (fn [rule-path cur-path] |
| 142 | + (and (= (count rule-path) (count cur-path)) |
| 143 | + (every? true? |
| 144 | + (map (fn [rp cp] |
| 145 | + (or (= rp :ANY) |
| 146 | + (= rp cp))) |
| 147 | + rule-path cur-path)))) |
| 148 | + applies? (fn [paths cur-path] |
| 149 | + (some #(matches-path? % cur-path) paths)) |
| 150 | + normalize-map (fn normalize-map [cur-path m*] |
| 151 | + (cond |
| 152 | + (map? m*) |
| 153 | + (let [apply-kebab? (applies? kc-paths cur-path) |
| 154 | + apply-string? (applies? str-paths cur-path)] |
| 155 | + (into {} |
| 156 | + (map (fn [[k v]] |
| 157 | + (let [base-name (cond |
| 158 | + (keyword? k) (name k) |
| 159 | + (string? k) k |
| 160 | + :else (str k)) |
| 161 | + kebabed (if apply-kebab? |
| 162 | + (csk/->kebab-case base-name) |
| 163 | + base-name) |
| 164 | + new-k (if apply-string? |
| 165 | + kebabed |
| 166 | + (keyword kebabed)) |
| 167 | + new-v (normalize-map (conj cur-path new-k) v)] |
| 168 | + [new-k new-v]))) |
| 169 | + m*)) |
| 170 | + |
| 171 | + (sequential? m*) |
| 172 | + (mapv #(normalize-map cur-path %) m*) |
| 173 | + |
| 174 | + :else m*))] |
| 175 | + (normalize-map [] m))) |
163 | 176 |
|
164 | 177 | (defn all [db] |
165 | 178 | (let [initialization-config @initialization-config* |
166 | 179 | pure-config? (:pureConfig initialization-config)] |
167 | | - (normalize-fields |
168 | | - (deep-merge initial-config |
169 | | - initialization-config |
170 | | - (when-not pure-config? (config-from-envvar)) |
171 | | - (when-not pure-config? (config-from-global-file)) |
172 | | - (when-not pure-config? (config-from-local-file (:workspace-folders db))))))) |
| 180 | + (deep-merge initial-config |
| 181 | + (normalize-fields |
| 182 | + {:kebab-case |
| 183 | + [[:providers]] |
| 184 | + :stringfy |
| 185 | + [[:providers] |
| 186 | + [:providers :ANY :models] |
| 187 | + [:toolCall :approval :allow] |
| 188 | + [:toolCall :approval :allow :ANY :argsMatchers] |
| 189 | + [:toolCall :approval :ask] |
| 190 | + [:toolCall :approval :ask :ANY :argsMatchers] |
| 191 | + [:mcpServers]]} |
| 192 | + (deep-merge initialization-config |
| 193 | + (when-not pure-config? (config-from-envvar)) |
| 194 | + (when-not pure-config? (config-from-global-file)) |
| 195 | + (when-not pure-config? (config-from-local-file (:workspace-folders db)))))))) |
0 commit comments