lazyclaw 3.99.14 → 3.99.15
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/cli.mjs +43 -13
- package/package.json +1 -1
- package/providers/registry.mjs +16 -4
package/cli.mjs
CHANGED
|
@@ -1731,12 +1731,19 @@ async function _pickProviderInteractive() {
|
|
|
1731
1731
|
while (!family) {
|
|
1732
1732
|
const familyItems = Object.entries(families)
|
|
1733
1733
|
.filter(([, b]) => b.members.length > 0)
|
|
1734
|
-
.map(([id, b]) =>
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1734
|
+
.map(([id, b]) => {
|
|
1735
|
+
// Show member count + a few names instead of the full list — the
|
|
1736
|
+
// API-key family alone now has 12 vendors and joining all of them
|
|
1737
|
+
// produced an unreadable line.
|
|
1738
|
+
const preview = b.members.slice(0, 3).join(' / ');
|
|
1739
|
+
const more = b.members.length > 3 ? ` … (+${b.members.length - 3} more)` : '';
|
|
1740
|
+
return {
|
|
1741
|
+
id,
|
|
1742
|
+
label: b.label,
|
|
1743
|
+
desc: `${b.desc} · ${preview}${more}`,
|
|
1744
|
+
tag: b.tag,
|
|
1745
|
+
};
|
|
1746
|
+
});
|
|
1740
1747
|
const picked = await _arrowMenu({
|
|
1741
1748
|
title: 'LazyClaw setup — Step 1 of 3: pick how you want to auth',
|
|
1742
1749
|
subtitle: 'API: bring your own key · CLI/Local: use what\'s already on this machine · Mock: offline test',
|
|
@@ -1752,14 +1759,21 @@ async function _pickProviderInteractive() {
|
|
|
1752
1759
|
const memberNames = families[family.id].members;
|
|
1753
1760
|
const provItems = memberNames.map((name) => {
|
|
1754
1761
|
const meta = info[name] || {};
|
|
1755
|
-
const models = (meta.suggestedModels || []).slice(0, 4).join(' · ') || '(default)';
|
|
1756
1762
|
const isCustom = !!meta.custom;
|
|
1763
|
+
const isBuiltinCompat = !!meta.builtinOpenAICompat;
|
|
1764
|
+
// Step-2 desc used to preview four suggested model ids per provider.
|
|
1765
|
+
// That made the row read like "gemini · models: gemini-2.5-pro ·
|
|
1766
|
+
// gemini-2.5-flash · gemini-2.0-flash · gemini-2.0-flash-thinking-exp",
|
|
1767
|
+
// which is too dense and partly redundant — step 3 already shows the
|
|
1768
|
+
// full curated list. Keep the row to a vendor label + endpoint hint.
|
|
1769
|
+
let desc = '';
|
|
1770
|
+
if (isCustom) desc = `custom · ${meta.baseUrl || ''}`;
|
|
1771
|
+
else if (isBuiltinCompat) desc = meta.label || meta.baseUrl || '';
|
|
1772
|
+
else if (meta.label && meta.label !== name) desc = meta.label;
|
|
1757
1773
|
return {
|
|
1758
1774
|
id: name,
|
|
1759
1775
|
label: name,
|
|
1760
|
-
desc
|
|
1761
|
-
? `custom · ${meta.baseUrl || ''}`
|
|
1762
|
-
: `models: ${models}`,
|
|
1776
|
+
desc,
|
|
1763
1777
|
tag: isCustom
|
|
1764
1778
|
? '\x1b[38;5;213m[custom]\x1b[0m'
|
|
1765
1779
|
: (meta.requiresApiKey ? '\x1b[38;5;245m[api key]\x1b[0m' : '\x1b[38;5;208m[no key]\x1b[0m'),
|
|
@@ -1970,7 +1984,7 @@ async function _addCustomProviderInteractive() {
|
|
|
1970
1984
|
process.stdout.write(dim(' · Groq https://api.groq.com/openai/v1') + '\n');
|
|
1971
1985
|
process.stdout.write(dim(' · vLLM / LM Studio http://localhost:8000/v1') + '\n\n');
|
|
1972
1986
|
|
|
1973
|
-
const { validateCustomProviderName, registerCustomProviders, fetchOpenAICompatModels } = _registryMod;
|
|
1987
|
+
const { validateCustomProviderName, registerCustomProviders, fetchOpenAICompatModels, isBuiltinOpenAICompatName } = _registryMod;
|
|
1974
1988
|
let name;
|
|
1975
1989
|
while (true) {
|
|
1976
1990
|
const raw = (await _quickPrompt(` ${bold('name')} ${dim('(short id, e.g. "nim", "openrouter"):')} `)).trim();
|
|
@@ -1978,8 +1992,24 @@ async function _addCustomProviderInteractive() {
|
|
|
1978
1992
|
process.stdout.write(dim(' cancelled — back to the picker.\n'));
|
|
1979
1993
|
return null;
|
|
1980
1994
|
}
|
|
1981
|
-
try { name = validateCustomProviderName(raw);
|
|
1982
|
-
catch (e) {
|
|
1995
|
+
try { name = validateCustomProviderName(raw); }
|
|
1996
|
+
catch (e) {
|
|
1997
|
+
process.stdout.write(` \x1b[33m${e.message}\x1b[0m — try again.\n`);
|
|
1998
|
+
continue;
|
|
1999
|
+
}
|
|
2000
|
+
// OpenAI-compat builtins (nim / openrouter / groq / …) can be overridden
|
|
2001
|
+
// by a custom entry of the same name — both go through
|
|
2002
|
+
// makeOpenAICompatProvider, so the wire format is identical and the
|
|
2003
|
+
// user is just pointing the same alias at a different URL/key. Surface
|
|
2004
|
+
// the override so it isn't a silent surprise.
|
|
2005
|
+
if (typeof isBuiltinOpenAICompatName === 'function' && isBuiltinOpenAICompatName(name)) {
|
|
2006
|
+
process.stdout.write(
|
|
2007
|
+
` \x1b[2mNote: "${name}" is a built-in OpenAI-compatible provider; ` +
|
|
2008
|
+
`your custom entry will override the built-in baseUrl/api-key for this install. ` +
|
|
2009
|
+
`Remove with: lazyclaw providers remove ${name}\x1b[0m\n`
|
|
2010
|
+
);
|
|
2011
|
+
}
|
|
2012
|
+
break;
|
|
1983
2013
|
}
|
|
1984
2014
|
const baseUrlRaw = (await _quickPrompt(` ${bold('baseUrl')} ${dim('(must end in /v1, no trailing slash needed):')} `)).trim();
|
|
1985
2015
|
if (!baseUrlRaw) { process.stdout.write(dim(' cancelled — baseUrl is required.\n')); return null; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lazyclaw",
|
|
3
|
-
"version": "3.99.
|
|
3
|
+
"version": "3.99.15",
|
|
4
4
|
"description": "Lazy, elegant terminal CLI for chatting with Claude / OpenAI / Gemini / Ollama and orchestrating multi-step LLM workflows. Banner-on-launch, slash-command ghost autocomplete, persistent sessions, local HTTP gateway.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
package/providers/registry.mjs
CHANGED
|
@@ -413,15 +413,27 @@ export function parseProviderModel(s) {
|
|
|
413
413
|
* @param {string|undefined|null} key
|
|
414
414
|
* @returns {string}
|
|
415
415
|
*/
|
|
416
|
-
// Reserved provider names —
|
|
417
|
-
//
|
|
416
|
+
// Reserved provider names — names whose factory is bespoke (not the
|
|
417
|
+
// generic OpenAI-compat one) so a custom registration of the same name
|
|
418
|
+
// would silently break the wire format. The OpenAI-compat builtins are
|
|
419
|
+
// deliberately NOT listed: a user can register `nim` / `openrouter` /
|
|
420
|
+
// etc. as a custom entry to override the baseUrl / api-key / headers,
|
|
421
|
+
// because both the built-in and the custom go through
|
|
422
|
+
// `makeOpenAICompatProvider` — overriding is well-defined.
|
|
418
423
|
const RESERVED_PROVIDER_NAMES = new Set([
|
|
419
424
|
'mock', 'claude-cli', 'anthropic', 'openai', 'gemini', 'ollama',
|
|
420
|
-
// OpenAI-compatible builtins (kept in lockstep with OPENAI_COMPAT_BUILTINS).
|
|
421
|
-
...Object.keys(OPENAI_COMPAT_BUILTINS),
|
|
422
425
|
'__add_custom__', '__custom_model__', '__fetch_models__',
|
|
423
426
|
]);
|
|
424
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Whether the supplied name belongs to one of the OpenAI-compatible
|
|
430
|
+
* builtins. Used by the custom-add interactive flow so it can warn the
|
|
431
|
+
* user that their custom entry will shadow the built-in registration.
|
|
432
|
+
*/
|
|
433
|
+
export function isBuiltinOpenAICompatName(name) {
|
|
434
|
+
return Object.prototype.hasOwnProperty.call(OPENAI_COMPAT_BUILTINS, String(name || '').trim().toLowerCase());
|
|
435
|
+
}
|
|
436
|
+
|
|
425
437
|
/**
|
|
426
438
|
* Validate a custom provider name. Allowed: lowercase alnum + dash + dot.
|
|
427
439
|
* Returns the trimmed name on success; throws on collision / bad format.
|