@arthovlabs/liber 0.5.6 → 0.5.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arthovlabs/liber",
3
- "version": "0.5.6",
3
+ "version": "0.5.7",
4
4
  "description": "LiBER — autonomous agent orchestrator with a machine-enforced grounding gate and a skill library",
5
5
  "bin": {
6
6
  "liber": "bin/liber",
@@ -294,6 +294,42 @@ function status() {
294
294
  return { providers: rows, custom: customs };
295
295
  }
296
296
 
297
+ /**
298
+ * Fill provider/endpoint when liber.yaml leaves them null.
299
+ *
300
+ * A fresh install ships `provider: null` and `endpoint: null` on purpose: the
301
+ * only thing the user is expected to hand over is a key. Nothing used to turn
302
+ * that key back into a base URL, so the adapter built `null/chat/completions`
303
+ * and every turn died — either as a URL parse error or, when a placeholder key
304
+ * from .env.example was sent instead, as HTTP 401. The endpoint has to come from
305
+ * whichever provider is actually configured.
306
+ */
307
+ function engineDefaults(engine = {}) {
308
+ const cfg = { ...engine };
309
+ const missingProvider = !cfg.provider || cfg.provider === 'null';
310
+ const missingEndpoint = !cfg.endpoint || cfg.endpoint === 'null';
311
+ if (!missingProvider && !missingEndpoint) return cfg;
312
+
313
+ const { providers, custom } = status();
314
+ // Rank what is configured: an explicit custom endpoint first, then real API
315
+ // keys, and only then a local server. A machine that happens to run Ollama
316
+ // exports OLLAMA_HOST, which would otherwise hijack the endpoint away from a
317
+ // perfectly good cloud key.
318
+ const rank = p => (p.kind === 'custom' ? 0 : p.kind === 'api' ? 1 : 2);
319
+ const pick = [...custom, ...providers]
320
+ .filter(p => p.configured && p.base && p.adapter)
321
+ .sort((a, b) => rank(a) - rank(b))[0];
322
+ if (!pick) return cfg;
323
+
324
+ if (missingProvider) cfg.provider = pick.adapter;
325
+ if (missingEndpoint) cfg.endpoint = String(pick.base).replace(/\/+$/, '');
326
+ if (!cfg.apiKey) {
327
+ const key = resolve(pick.id, pick.base);
328
+ if (key) cfg.apiKey = key;
329
+ }
330
+ return cfg;
331
+ }
332
+
297
333
  function imports() {
298
334
  return IMPORTS.map(i => {
299
335
  let present = false, when = null;
@@ -499,5 +535,6 @@ module.exports = {
499
535
  status, imports, importFrom,
500
536
  setKey, setToken, clear, setCustom,
501
537
  resolve, resolveStored, test, deviceStart, devicePoll,
538
+ engineDefaults,
502
539
  storePath: () => STORE()
503
540
  };
@@ -22,6 +22,11 @@ class LLMManager {
22
22
  * @param {Object} config - Bagian 'engine' dari liber.yaml
23
23
  */
24
24
  initialize(config) {
25
+ // liber.yaml may leave provider/endpoint null and expect the installed key
26
+ // to supply them; resolve before anything reads config.endpoint, or the
27
+ // adapter ends up requesting null/chat/completions.
28
+ const { engineDefaults } = require('../core/providers');
29
+ config = engineDefaults(config);
25
30
  this.engineConfig = { ...config };
26
31
  const ProviderClass = this.adapters[config.provider];
27
32
  if (!ProviderClass) {