demian-cli 1.0.8 → 1.1.0

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.
@@ -850,7 +850,7 @@ var TuiStore = class {
850
850
  }
851
851
  markTaskFailed(message) {
852
852
  const lines = [message];
853
- if (this.#lastRetryPrompt()) lines.push("Press ctrl+r or type /retry to run the last task again.");
853
+ if (this.#lastRetryPrompt()) lines.push("Press Esc then r or type /retry to run the last task again.");
854
854
  this.#append({ kind: "warning", title: "Task Failed", lines });
855
855
  this.#state.status.reason = "error";
856
856
  this.#state.inputMode = this.#exitRequested ? "done" : "prompt";
@@ -25964,6 +25964,7 @@ var defaultConfig = {
25964
25964
  defaultProvider: "brave",
25965
25965
  providers: {
25966
25966
  brave: {
25967
+ apiKey: "",
25967
25968
  apiKeyEnv: "BRAVE_SEARCH_API_KEY",
25968
25969
  endpoint: "https://api.search.brave.com/res/v1/web/search",
25969
25970
  count: 10,
@@ -25972,12 +25973,14 @@ var defaultConfig = {
25972
25973
  safeSearch: "moderate"
25973
25974
  },
25974
25975
  tavily: {
25976
+ apiKey: "",
25975
25977
  apiKeyEnv: "TAVILY_API_KEY",
25976
25978
  endpoint: "https://api.tavily.com/search",
25977
25979
  maxResults: 5,
25978
25980
  searchDepth: "basic"
25979
25981
  },
25980
25982
  exa: {
25983
+ apiKey: "",
25981
25984
  apiKeyEnv: "EXA_API_KEY",
25982
25985
  endpoint: "https://api.exa.ai/search",
25983
25986
  numResults: 5,
@@ -26032,6 +26035,7 @@ var defaultConfig = {
26032
26035
  openai: {
26033
26036
  type: "openai-compatible",
26034
26037
  baseURL: "https://api.openai.com/v1",
26038
+ apiKey: "",
26035
26039
  apiKeyEnv: "OPENAI_API_KEY",
26036
26040
  catalog: {
26037
26041
  type: "openai-models",
@@ -26041,6 +26045,7 @@ var defaultConfig = {
26041
26045
  anthropic: {
26042
26046
  type: "anthropic",
26043
26047
  baseURL: "https://api.anthropic.com/v1",
26048
+ apiKey: "",
26044
26049
  apiKeyEnv: "ANTHROPIC_API_KEY",
26045
26050
  catalog: {
26046
26051
  type: "anthropic-models",
@@ -26050,6 +26055,7 @@ var defaultConfig = {
26050
26055
  gemini: {
26051
26056
  type: "openai-compatible",
26052
26057
  baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
26058
+ apiKey: "",
26053
26059
  apiKeyEnv: "GEMINI_API_KEY",
26054
26060
  apiKeyEnvAliases: ["GOOGLE_API_KEY"],
26055
26061
  catalog: {
@@ -26060,6 +26066,7 @@ var defaultConfig = {
26060
26066
  groq: {
26061
26067
  type: "openai-compatible",
26062
26068
  baseURL: "https://api.groq.com/openai/v1",
26069
+ apiKey: "",
26063
26070
  apiKeyEnv: "GROQ_API_KEY",
26064
26071
  catalog: {
26065
26072
  type: "groq-models",
@@ -26075,6 +26082,7 @@ var defaultConfig = {
26075
26082
  displayName: "Azure example",
26076
26083
  model: "azure-deployment-name",
26077
26084
  baseURL: "https://example.openai.azure.com/openai/v1",
26085
+ apiKey: "",
26078
26086
  apiKeyEnv: "AZURE_OPENAI_API_KEY"
26079
26087
  }
26080
26088
  ]
@@ -26083,6 +26091,7 @@ var defaultConfig = {
26083
26091
  type: "openai-compatible",
26084
26092
  baseURL: "http://localhost:1234/v1",
26085
26093
  apiKey: "lm-studio",
26094
+ modelProfiles: [],
26086
26095
  catalog: {
26087
26096
  type: "openai-compatible-models",
26088
26097
  endpoint: "http://localhost:1234/v1/models"
@@ -26092,6 +26101,7 @@ var defaultConfig = {
26092
26101
  type: "openai-compatible",
26093
26102
  baseURL: "http://localhost:11434/v1",
26094
26103
  apiKey: "ollama",
26104
+ modelProfiles: [],
26095
26105
  quirks: { omitTemperature: true },
26096
26106
  catalog: {
26097
26107
  type: "openai-compatible-models",
@@ -26101,7 +26111,9 @@ var defaultConfig = {
26101
26111
  "ollama-cloud": {
26102
26112
  type: "ollama",
26103
26113
  baseURL: "https://ollama.com/api",
26114
+ apiKey: "",
26104
26115
  apiKeyEnv: "OLLAMA_API_KEY",
26116
+ modelProfiles: [],
26105
26117
  catalog: {
26106
26118
  type: "ollama-tags",
26107
26119
  endpoint: "https://ollama.com/api/tags"
@@ -26111,6 +26123,7 @@ var defaultConfig = {
26111
26123
  type: "openai-compatible",
26112
26124
  baseURL: "http://localhost:8080/v1",
26113
26125
  apiKey: "llama.cpp",
26126
+ modelProfiles: [],
26114
26127
  catalog: {
26115
26128
  type: "openai-compatible-models",
26116
26129
  endpoint: "http://localhost:8080/v1/models"
@@ -26120,6 +26133,7 @@ var defaultConfig = {
26120
26133
  type: "openai-compatible",
26121
26134
  baseURL: "http://localhost:8000/v1",
26122
26135
  apiKey: "vllm",
26136
+ modelProfiles: [],
26123
26137
  catalog: {
26124
26138
  type: "openai-compatible-models",
26125
26139
  endpoint: "http://localhost:8000/v1/models"
@@ -26452,11 +26466,18 @@ function providerModelProfiles(providerName, providerConfig) {
26452
26466
  return out;
26453
26467
  }
26454
26468
  function providerModelOptions(providerName, providerConfig) {
26455
- return providerModelProfiles(providerName, providerConfig).filter((profile) => !profile.hidden).map((profile) => profile.displayName ?? profile.name ?? profile.model);
26469
+ return providerModelProfiles(providerName, providerConfig).filter((profile) => !profile.hidden).map((profile) => displayModelForProfile(profile));
26456
26470
  }
26457
26471
  function defaultModelForProvider(providerName, providerConfig) {
26458
26472
  return providerModelProfiles(providerName, providerConfig)[0]?.model ?? "";
26459
26473
  }
26474
+ function displayModelForProfile(profile) {
26475
+ return profile.displayName?.trim() || profile.name?.trim() || profile.model;
26476
+ }
26477
+ function defaultDisplayModelForProvider(providerName, providerConfig) {
26478
+ const profile = providerModelProfiles(providerName, providerConfig)[0];
26479
+ return profile ? displayModelForProfile(profile) : defaultModelForProvider(providerName, providerConfig);
26480
+ }
26460
26481
  function resolveConfiguredApiKey(providerConfig) {
26461
26482
  if (providerConfig.apiKey) return { value: providerConfig.apiKey };
26462
26483
  for (const envName of [providerConfig.apiKeyEnv, ...providerConfig.apiKeyEnvAliases ?? []]) {
@@ -26493,7 +26514,7 @@ function mergeModelProfile(providerConfig, profile) {
26493
26514
  ...providerConfig,
26494
26515
  ...definedOnly({
26495
26516
  baseURL: profile.baseURL,
26496
- apiKey: profile.apiKey,
26517
+ apiKey: nonEmptyString(profile.apiKey),
26497
26518
  apiKeyEnv: profile.apiKeyEnv,
26498
26519
  apiKeyEnvAliases: profile.apiKeyEnvAliases,
26499
26520
  auth: profile.auth,
@@ -26508,6 +26529,9 @@ function mergeModelProfile(providerConfig, profile) {
26508
26529
  function definedOnly(input2) {
26509
26530
  return Object.fromEntries(Object.entries(input2).filter(([, value]) => value !== void 0));
26510
26531
  }
26532
+ function nonEmptyString(value) {
26533
+ return typeof value === "string" && value.length > 0 ? value : void 0;
26534
+ }
26511
26535
  function fallbackModelForProvider(providerName, providerConfig) {
26512
26536
  if (typeof providerConfig.model === "string" && providerConfig.model.trim()) return providerConfig.model.trim();
26513
26537
  if (providerName === "openai") return "gpt-5.5";
@@ -29036,7 +29060,7 @@ async function runExaSearch(config, options2) {
29036
29060
  return searchResult("exa", options2.query, results, json.value);
29037
29061
  }
29038
29062
  function resolveApiKey(provider, config) {
29039
- const value = config.apiKey ?? (config.apiKeyEnv ? process.env[config.apiKeyEnv] : void 0);
29063
+ const value = config.apiKey || (config.apiKeyEnv ? process.env[config.apiKeyEnv] : void 0);
29040
29064
  if (value) return { ok: true, value };
29041
29065
  return toolFail(`Missing API key for web_search provider "${provider}". Set webSearch.providers.${provider}.apiKey or set ${config.apiKeyEnv ?? "the configured apiKeyEnv"}.`);
29042
29066
  }
@@ -32357,7 +32381,7 @@ function providerOptions(config, catalogs = {}) {
32357
32381
  isDefault: model.isDefault
32358
32382
  })) : providerModelProfiles(name, provider);
32359
32383
  const modelProfiles = profiles.map((profile) => {
32360
- const displayName = profile.displayName ?? profile.name ?? profile.model;
32384
+ const displayName = displayModelForProfile(profile);
32361
32385
  return {
32362
32386
  ...profile,
32363
32387
  label: available ? displayName : unavailableLabel(displayName),
@@ -32365,7 +32389,7 @@ function providerOptions(config, catalogs = {}) {
32365
32389
  };
32366
32390
  });
32367
32391
  const models = modelProfiles.length ? [...new Set(modelProfiles.map((item) => item.model).filter((item) => typeof item === "string" && item.trim()))] : providerModelOptions(name, provider);
32368
- const defaultModel = defaultModelForProvider(name, provider);
32392
+ const defaultModel = defaultDisplayModelForProvider(name, provider);
32369
32393
  return {
32370
32394
  name,
32371
32395
  label: available ? name : unavailableLabel(name),
@@ -32429,13 +32453,18 @@ function createInteractiveModelSelection(config, flags = {}, saved) {
32429
32453
  }
32430
32454
  const usesSavedProvider = !flags.provider && savedProviderExists && providerName === savedProviderName;
32431
32455
  const savedModel = usesSavedProvider && saved?.model ? saved.model : void 0;
32432
- const model = flags.model ?? savedModel ?? defaultModelForProvider(providerName, providerConfig);
32456
+ const profiles = providerModelProfiles(providerName, providerConfig);
32457
+ const savedProfile = savedModel ? profiles.find((profile) => profile.model === savedModel || profile.displayName === savedModel || profile.name === savedModel) : void 0;
32458
+ const defaultModel = displayModelForProfile(profiles[0] ?? { name: "", displayName: "", model: "" }) || defaultDisplayModelForProvider(providerName, providerConfig);
32459
+ const flagProfile = flags.model ? profiles.find((profile) => profile.model === flags.model || profile.displayName === flags.model || profile.name === flags.model) : void 0;
32460
+ const selectedProfile = flags.model ? flagProfile : savedModel ? savedProfile : profiles[0];
32461
+ const model = flags.model ?? (savedProfile ? displayModelForProfile(savedProfile) : savedModel) ?? defaultModel;
32433
32462
  return {
32434
32463
  providerName,
32435
32464
  providerSource: flags.provider ? "flag" : usesSavedProvider ? "saved" : "config",
32436
32465
  model,
32437
32466
  modelSource: flags.model ? "flag" : savedModel ? "saved" : "config",
32438
- modelProfileName: providerModelProfiles(providerName, providerConfig).find((profile) => profile.model === model || profile.displayName === model || profile.name === model)?.name
32467
+ modelProfileName: selectedProfile?.name
32439
32468
  };
32440
32469
  }
32441
32470
  function providerCatalogAvailable(catalog) {
@@ -32719,18 +32748,21 @@ function defaultUserConfig(defaultProvider = detectDefaultProvider()) {
32719
32748
  openai: {
32720
32749
  type: "openai-compatible",
32721
32750
  baseURL: "https://api.openai.com/v1",
32751
+ apiKey: "",
32722
32752
  apiKeyEnv: "OPENAI_API_KEY",
32723
32753
  catalog: { type: "openai-models", endpoint: "https://api.openai.com/v1/models" }
32724
32754
  },
32725
32755
  anthropic: {
32726
32756
  type: "anthropic",
32727
32757
  baseURL: "https://api.anthropic.com/v1",
32758
+ apiKey: "",
32728
32759
  apiKeyEnv: "ANTHROPIC_API_KEY",
32729
32760
  catalog: { type: "anthropic-models", endpoint: "https://api.anthropic.com/v1/models" }
32730
32761
  },
32731
32762
  gemini: {
32732
32763
  type: "openai-compatible",
32733
32764
  baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
32765
+ apiKey: "",
32734
32766
  apiKeyEnv: "GEMINI_API_KEY",
32735
32767
  apiKeyEnvAliases: ["GOOGLE_API_KEY"],
32736
32768
  catalog: { type: "gemini-openai-models", endpoint: "https://generativelanguage.googleapis.com/v1beta/openai/models" }
@@ -32738,6 +32770,7 @@ function defaultUserConfig(defaultProvider = detectDefaultProvider()) {
32738
32770
  groq: {
32739
32771
  type: "openai-compatible",
32740
32772
  baseURL: "https://api.groq.com/openai/v1",
32773
+ apiKey: "",
32741
32774
  apiKeyEnv: "GROQ_API_KEY",
32742
32775
  catalog: { type: "groq-models", endpoint: "https://api.groq.com/openai/v1/models" }
32743
32776
  },
@@ -32750,6 +32783,7 @@ function defaultUserConfig(defaultProvider = detectDefaultProvider()) {
32750
32783
  displayName: "Azure example",
32751
32784
  model: "azure-deployment-name",
32752
32785
  baseURL: "https://example.openai.azure.com/openai/v1",
32786
+ apiKey: "",
32753
32787
  apiKeyEnv: "AZURE_OPENAI_API_KEY"
32754
32788
  }
32755
32789
  ]
@@ -32758,31 +32792,37 @@ function defaultUserConfig(defaultProvider = detectDefaultProvider()) {
32758
32792
  type: "openai-compatible",
32759
32793
  baseURL: "http://localhost:1234/v1",
32760
32794
  apiKey: "lm-studio",
32795
+ modelProfiles: [],
32761
32796
  catalog: { type: "openai-compatible-models", endpoint: "http://localhost:1234/v1/models" }
32762
32797
  },
32763
32798
  "ollama-local": {
32764
32799
  type: "openai-compatible",
32765
32800
  baseURL: "http://localhost:11434/v1",
32766
32801
  apiKey: "ollama",
32802
+ modelProfiles: [],
32767
32803
  quirks: { omitTemperature: true },
32768
32804
  catalog: { type: "openai-compatible-models", endpoint: "http://localhost:11434/v1/models" }
32769
32805
  },
32770
32806
  "ollama-cloud": {
32771
32807
  type: "ollama",
32772
32808
  baseURL: "https://ollama.com/api",
32809
+ apiKey: "",
32773
32810
  apiKeyEnv: "OLLAMA_API_KEY",
32811
+ modelProfiles: [],
32774
32812
  catalog: { type: "ollama-tags", endpoint: "https://ollama.com/api/tags" }
32775
32813
  },
32776
32814
  llamacpp: {
32777
32815
  type: "openai-compatible",
32778
32816
  baseURL: "http://localhost:8080/v1",
32779
32817
  apiKey: "llama.cpp",
32818
+ modelProfiles: [],
32780
32819
  catalog: { type: "openai-compatible-models", endpoint: "http://localhost:8080/v1/models" }
32781
32820
  },
32782
32821
  vllm: {
32783
32822
  type: "openai-compatible",
32784
32823
  baseURL: "http://localhost:8000/v1",
32785
32824
  apiKey: "vllm",
32825
+ modelProfiles: [],
32786
32826
  catalog: { type: "openai-compatible-models", endpoint: "http://localhost:8000/v1/models" }
32787
32827
  },
32788
32828
  codex: {
@@ -32854,7 +32894,7 @@ async function addModelProfile(options2) {
32854
32894
  displayName,
32855
32895
  model: options2.model,
32856
32896
  ...options2.baseURL ? { baseURL: options2.baseURL } : {},
32857
- ...options2.apiKey ? { apiKey: options2.apiKey } : {},
32897
+ ...options2.apiKey !== void 0 || options2.apiKeyEnv ? { apiKey: options2.apiKey ?? "" } : {},
32858
32898
  ...options2.apiKeyEnv ? { apiKeyEnv: options2.apiKeyEnv } : {}
32859
32899
  };
32860
32900
  if (existingByName >= 0) profiles[existingByName] = next;
@@ -32889,18 +32929,15 @@ async function readConfigObject(filePath) {
32889
32929
  }
32890
32930
  function providerPreset(options2) {
32891
32931
  const preset = options2.preset ?? options2.name;
32892
- const auth = {
32893
- ...options2.apiKey ? { apiKey: options2.apiKey } : {},
32894
- ...options2.apiKeyEnv ? { apiKeyEnv: options2.apiKeyEnv } : {}
32895
- };
32932
+ const auth = apiKeyAuthFields(options2);
32896
32933
  const openAIAuth = options2.authHeader ? { auth: { type: "api-key", header: options2.authHeader } } : {};
32897
- if (preset === "openai") return { type: "openai-compatible", baseURL: options2.baseURL ?? "https://api.openai.com/v1", apiKeyEnv: "OPENAI_API_KEY", ...auth, ...openAIAuth, catalog: { type: "openai-models", endpoint: `${(options2.baseURL ?? "https://api.openai.com/v1").replace(/\/+$/, "")}/models` } };
32898
- if (preset === "anthropic") return { type: "anthropic", baseURL: options2.baseURL ?? "https://api.anthropic.com/v1", apiKeyEnv: "ANTHROPIC_API_KEY", ...auth, catalog: { type: "anthropic-models", endpoint: `${(options2.baseURL ?? "https://api.anthropic.com/v1").replace(/\/+$/, "")}/models` } };
32934
+ if (preset === "openai") return { type: "openai-compatible", baseURL: options2.baseURL ?? "https://api.openai.com/v1", ...apiKeyAuthFields(options2, "OPENAI_API_KEY"), ...openAIAuth, catalog: { type: "openai-models", endpoint: `${(options2.baseURL ?? "https://api.openai.com/v1").replace(/\/+$/, "")}/models` } };
32935
+ if (preset === "anthropic") return { type: "anthropic", baseURL: options2.baseURL ?? "https://api.anthropic.com/v1", ...apiKeyAuthFields(options2, "ANTHROPIC_API_KEY"), catalog: { type: "anthropic-models", endpoint: `${(options2.baseURL ?? "https://api.anthropic.com/v1").replace(/\/+$/, "")}/models` } };
32899
32936
  if (preset === "gemini") {
32900
32937
  const geminiBase = options2.baseURL ?? "https://generativelanguage.googleapis.com/v1beta/openai/";
32901
- return { type: "openai-compatible", baseURL: geminiBase, apiKeyEnv: "GEMINI_API_KEY", apiKeyEnvAliases: ["GOOGLE_API_KEY"], ...auth, ...openAIAuth, catalog: { type: "gemini-openai-models", endpoint: `${geminiBase.replace(/\/+$/, "")}/models` } };
32938
+ return { type: "openai-compatible", baseURL: geminiBase, ...apiKeyAuthFields(options2, "GEMINI_API_KEY"), apiKeyEnvAliases: ["GOOGLE_API_KEY"], ...openAIAuth, catalog: { type: "gemini-openai-models", endpoint: `${geminiBase.replace(/\/+$/, "")}/models` } };
32902
32939
  }
32903
- if (preset === "groq") return { type: "openai-compatible", baseURL: options2.baseURL ?? "https://api.groq.com/openai/v1", apiKeyEnv: "GROQ_API_KEY", ...auth, ...openAIAuth, catalog: { type: "groq-models", endpoint: `${(options2.baseURL ?? "https://api.groq.com/openai/v1").replace(/\/+$/, "")}/models` } };
32940
+ if (preset === "groq") return { type: "openai-compatible", baseURL: options2.baseURL ?? "https://api.groq.com/openai/v1", ...apiKeyAuthFields(options2, "GROQ_API_KEY"), ...openAIAuth, catalog: { type: "groq-models", endpoint: `${(options2.baseURL ?? "https://api.groq.com/openai/v1").replace(/\/+$/, "")}/models` } };
32904
32941
  if (preset === "azure") {
32905
32942
  return {
32906
32943
  type: "openai-compatible",
@@ -32912,16 +32949,17 @@ function providerPreset(options2) {
32912
32949
  displayName: "Azure example",
32913
32950
  model: "azure-deployment-name",
32914
32951
  baseURL: options2.baseURL ?? "https://example.openai.azure.com/openai/v1",
32952
+ ...options2.apiKey ? {} : { apiKey: "" },
32915
32953
  apiKeyEnv: options2.apiKeyEnv ?? "AZURE_OPENAI_API_KEY"
32916
32954
  }
32917
32955
  ]
32918
32956
  };
32919
32957
  }
32920
- if (preset === "lmstudio") return { type: "openai-compatible", baseURL: options2.baseURL ?? "http://localhost:1234/v1", apiKey: options2.apiKey ?? "lm-studio", catalog: { type: "openai-compatible-models", endpoint: `${(options2.baseURL ?? "http://localhost:1234/v1").replace(/\/+$/, "")}/models` } };
32921
- if (preset === "ollama-local" || preset === "ollama") return { type: "openai-compatible", baseURL: options2.baseURL ?? "http://localhost:11434/v1", apiKey: options2.apiKey ?? "ollama", quirks: { omitTemperature: true }, catalog: { type: "openai-compatible-models", endpoint: `${(options2.baseURL ?? "http://localhost:11434/v1").replace(/\/+$/, "")}/models` } };
32922
- if (preset === "ollama-cloud") return { type: "ollama", baseURL: options2.baseURL ?? "https://ollama.com/api", ...auth, catalog: { type: "ollama-tags", endpoint: `${(options2.baseURL ?? "https://ollama.com/api").replace(/\/+$/, "")}/tags` } };
32923
- if (preset === "llamacpp") return { type: "openai-compatible", baseURL: options2.baseURL ?? "http://localhost:8080/v1", apiKey: options2.apiKey ?? "llama.cpp", catalog: { type: "openai-compatible-models", endpoint: `${(options2.baseURL ?? "http://localhost:8080/v1").replace(/\/+$/, "")}/models` } };
32924
- if (preset === "vllm") return { type: "openai-compatible", baseURL: options2.baseURL ?? "http://localhost:8000/v1", apiKey: options2.apiKey ?? "vllm", catalog: { type: "openai-compatible-models", endpoint: `${(options2.baseURL ?? "http://localhost:8000/v1").replace(/\/+$/, "")}/models` } };
32958
+ if (preset === "lmstudio") return { type: "openai-compatible", baseURL: options2.baseURL ?? "http://localhost:1234/v1", apiKey: options2.apiKey ?? "lm-studio", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options2.baseURL ?? "http://localhost:1234/v1").replace(/\/+$/, "")}/models` } };
32959
+ if (preset === "ollama-local" || preset === "ollama") return { type: "openai-compatible", baseURL: options2.baseURL ?? "http://localhost:11434/v1", apiKey: options2.apiKey ?? "ollama", modelProfiles: [], quirks: { omitTemperature: true }, catalog: { type: "openai-compatible-models", endpoint: `${(options2.baseURL ?? "http://localhost:11434/v1").replace(/\/+$/, "")}/models` } };
32960
+ if (preset === "ollama-cloud") return { type: "ollama", baseURL: options2.baseURL ?? "https://ollama.com/api", ...apiKeyAuthFields(options2, "OLLAMA_API_KEY"), modelProfiles: [], catalog: { type: "ollama-tags", endpoint: `${(options2.baseURL ?? "https://ollama.com/api").replace(/\/+$/, "")}/tags` } };
32961
+ if (preset === "llamacpp") return { type: "openai-compatible", baseURL: options2.baseURL ?? "http://localhost:8080/v1", apiKey: options2.apiKey ?? "llama.cpp", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options2.baseURL ?? "http://localhost:8080/v1").replace(/\/+$/, "")}/models` } };
32962
+ if (preset === "vllm") return { type: "openai-compatible", baseURL: options2.baseURL ?? "http://localhost:8000/v1", apiKey: options2.apiKey ?? "vllm", modelProfiles: [], catalog: { type: "openai-compatible-models", endpoint: `${(options2.baseURL ?? "http://localhost:8000/v1").replace(/\/+$/, "")}/models` } };
32925
32963
  if (preset === "codex") return { type: "codex", baseURL: options2.baseURL ?? "https://chatgpt.com/backend-api/codex", authStore: "auto", allowApiKeyFallback: false, promptCacheKey: "root-session", catalog: { type: "codex-oauth-models" } };
32926
32964
  if (preset === "claudecode") return { type: "claudecode", runtime: "agent-sdk", cliPath: "~/.local/bin/claude", cwdMode: "session", historyPolicy: "passthrough-resume", onInvalidResume: "fresh", attachmentFallback: "block", allowSubagents: false, sanitizeApiKeyEnv: true, authPreflight: true, useBareMode: false, usageLedgerScope: "process", sessionLock: true, abortPolicy: "record-only", catalog: { type: "claudecode-supported-models" } };
32927
32965
  if ((options2.type ?? preset) === "openai-compatible") {
@@ -32930,14 +32968,20 @@ function providerPreset(options2) {
32930
32968
  return {
32931
32969
  type: "openai-compatible",
32932
32970
  baseURL,
32933
- ...options2.apiKey ? { apiKey: options2.apiKey } : {},
32934
- ...options2.apiKeyEnv ? { apiKeyEnv: options2.apiKeyEnv } : {},
32971
+ ...auth,
32935
32972
  ...openAIAuth,
32936
32973
  catalog: { type: "openai-compatible-models", endpoint: `${baseURL.replace(/\/+$/, "")}/models` }
32937
32974
  };
32938
32975
  }
32939
32976
  throw new Error(`Unknown provider preset: ${preset}`);
32940
32977
  }
32978
+ function apiKeyAuthFields(options2, defaultApiKeyEnv) {
32979
+ const apiKeyEnv = options2.apiKeyEnv ?? defaultApiKeyEnv;
32980
+ return {
32981
+ ...options2.apiKey !== void 0 || apiKeyEnv ? { apiKey: options2.apiKey ?? "" } : {},
32982
+ ...apiKeyEnv ? { apiKeyEnv } : {}
32983
+ };
32984
+ }
32941
32985
  async function writeJsonAtomic(filePath, content) {
32942
32986
  await mkdir14(path33.dirname(filePath), { recursive: true, mode: 448 });
32943
32987
  const temp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
@@ -33136,6 +33180,7 @@ if (process.argv.includes("--config-path")) {
33136
33180
  process.exit(0);
33137
33181
  }
33138
33182
  var options = parseOptions();
33183
+ var SNAPSHOT_DEBOUNCE_MS = 80;
33139
33184
  var store = new TuiStore();
33140
33185
  var diffState = normalizeDiffState(options.initialSnapshot?.diff);
33141
33186
  var restoredSnapshot = normalizeInitialPublicState(options.initialSnapshot);
@@ -33361,7 +33406,7 @@ function scheduleSnapshot() {
33361
33406
  snapshotTimer = setTimeout(() => {
33362
33407
  snapshotTimer = void 0;
33363
33408
  send({ type: "snapshot", state: publicState(store.snapshot()) });
33364
- }, 33);
33409
+ }, SNAPSHOT_DEBOUNCE_MS);
33365
33410
  }
33366
33411
  function sendSnapshotNow() {
33367
33412
  if (snapshotTimer) {
@@ -33724,7 +33769,7 @@ async function providerMeta(name, provider) {
33724
33769
  isDefault: model.isDefault
33725
33770
  })) : providerModelProfiles(name, provider);
33726
33771
  const labeledProfiles = modelProfiles.map((profile) => {
33727
- const displayName = profile.displayName ?? profile.name ?? profile.model;
33772
+ const displayName = displayModelForProfile(profile);
33728
33773
  return {
33729
33774
  ...profile,
33730
33775
  label: available ? displayName : unavailableLabel2(displayName),
@@ -33732,7 +33777,7 @@ async function providerMeta(name, provider) {
33732
33777
  };
33733
33778
  });
33734
33779
  const models = modelProfiles.length ? [...new Set(modelProfiles.map((item) => item.model).filter((item) => typeof item === "string" && item.trim()))] : providerModelOptions(name, provider);
33735
- const defaultModel = defaultModelForProvider(name, provider);
33780
+ const defaultModel = defaultDisplayModelForProvider(name, provider);
33736
33781
  return {
33737
33782
  name,
33738
33783
  label: available ? name : unavailableLabel2(name),
package/docs/ko/README.md CHANGED
@@ -52,14 +52,18 @@ Demian은 다음 순서로 설정을 읽습니다.
52
52
  "openai": {
53
53
  "type": "openai-compatible",
54
54
  "baseURL": "https://api.openai.com/v1",
55
+ "apiKey": "",
55
56
  "apiKeyEnv": "OPENAI_API_KEY",
56
- "model": "gpt-5.5"
57
+ "modelProfiles": [
58
+ { "name": "main", "displayName": "GPT 5.5", "model": "gpt-5.5" }
59
+ ]
57
60
  }
58
61
  }
59
62
  }
60
63
  ```
61
64
 
62
- API key는 공유 설정 파일에 직접 쓰기보다 `apiKeyEnv`를 권장합니다.
65
+ 공유 설정에서는 `apiKey`를 비워 두고 `apiKeyEnv`를 권장합니다. 개인 로컬 설정에 직접 붙여넣을 때만 `apiKey`를 채우세요.
66
+ `lmstudio`, `ollama-local`, `ollama-cloud`, `llamacpp`, `vllm`처럼 여러 로컬/자체 호스팅 모델을 노출할 수 있는 provider는 `modelProfiles` 배열에 모델별 항목을 추가하세요.
63
67
 
64
68
  ## Claude 관련 provider
65
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "demian-cli",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Local terminal coding agent with TUI, goals, cowork agents, permissions, and provider switching.",
5
5
  "type": "module",
6
6
  "icon": "media/demian.svg",