mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 07:51:26 +00:00
chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
@@ -78,10 +78,7 @@ export async function modelsAliasesAddCommand(
|
||||
runtime.log(`Alias ${alias} -> ${resolved.provider}/${resolved.model}`);
|
||||
}
|
||||
|
||||
export async function modelsAliasesRemoveCommand(
|
||||
aliasRaw: string,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
export async function modelsAliasesRemoveCommand(aliasRaw: string, runtime: RuntimeEnv) {
|
||||
const alias = normalizeAlias(aliasRaw);
|
||||
const updated = await updateConfig((cfg) => {
|
||||
const nextModels = { ...cfg.agents?.defaults?.models };
|
||||
@@ -111,9 +108,7 @@ export async function modelsAliasesRemoveCommand(
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
if (
|
||||
!updated.agents?.defaults?.models ||
|
||||
Object.values(updated.agents.defaults.models).every(
|
||||
(entry) => !entry?.alias?.trim(),
|
||||
)
|
||||
Object.values(updated.agents.defaults.models).every((entry) => !entry?.alias?.trim())
|
||||
) {
|
||||
runtime.log("No aliases configured.");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
resolveAgentDir,
|
||||
resolveDefaultAgentId,
|
||||
} from "../../agents/agent-scope.js";
|
||||
import { resolveAgentDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import {
|
||||
type AuthProfileStore,
|
||||
ensureAuthProfileStore,
|
||||
@@ -20,9 +17,7 @@ function resolveTargetAgent(
|
||||
agentId: string;
|
||||
agentDir: string;
|
||||
} {
|
||||
const agentId = raw?.trim()
|
||||
? normalizeAgentId(raw.trim())
|
||||
: resolveDefaultAgentId(cfg);
|
||||
const agentId = raw?.trim() ? normalizeAgentId(raw.trim()) : resolveDefaultAgentId(cfg);
|
||||
const agentDir = resolveAgentDir(cfg, agentId);
|
||||
return { agentId, agentDir };
|
||||
}
|
||||
@@ -67,14 +62,8 @@ export async function modelsAuthOrderGetCommand(
|
||||
|
||||
runtime.log(`Agent: ${agentId}`);
|
||||
runtime.log(`Provider: ${provider}`);
|
||||
runtime.log(
|
||||
`Auth file: ${shortenHomePath(`${agentDir}/auth-profiles.json`)}`,
|
||||
);
|
||||
runtime.log(
|
||||
order.length > 0
|
||||
? `Order override: ${order.join(", ")}`
|
||||
: "Order override: (none)",
|
||||
);
|
||||
runtime.log(`Auth file: ${shortenHomePath(`${agentDir}/auth-profiles.json`)}`);
|
||||
runtime.log(order.length > 0 ? `Order override: ${order.join(", ")}` : "Order override: (none)");
|
||||
}
|
||||
|
||||
export async function modelsAuthOrderClearCommand(
|
||||
@@ -92,8 +81,7 @@ export async function modelsAuthOrderClearCommand(
|
||||
provider,
|
||||
order: null,
|
||||
});
|
||||
if (!updated)
|
||||
throw new Error("Failed to update auth-profiles.json (lock busy?).");
|
||||
if (!updated) throw new Error("Failed to update auth-profiles.json (lock busy?).");
|
||||
|
||||
runtime.log(`Agent: ${agentId}`);
|
||||
runtime.log(`Provider: ${provider}`);
|
||||
@@ -115,9 +103,7 @@ export async function modelsAuthOrderSetCommand(
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
const providerKey = normalizeProviderId(provider);
|
||||
const requested = (opts.order ?? [])
|
||||
.map((entry) => String(entry).trim())
|
||||
.filter(Boolean);
|
||||
const requested = (opts.order ?? []).map((entry) => String(entry).trim()).filter(Boolean);
|
||||
if (requested.length === 0) {
|
||||
throw new Error("Missing profile ids. Provide one or more profile ids.");
|
||||
}
|
||||
@@ -128,9 +114,7 @@ export async function modelsAuthOrderSetCommand(
|
||||
throw new Error(`Auth profile "${profileId}" not found in ${agentDir}.`);
|
||||
}
|
||||
if (normalizeProviderId(cred.provider) !== providerKey) {
|
||||
throw new Error(
|
||||
`Auth profile "${profileId}" is for ${cred.provider}, not ${provider}.`,
|
||||
);
|
||||
throw new Error(`Auth profile "${profileId}" is for ${cred.provider}, not ${provider}.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,8 +123,7 @@ export async function modelsAuthOrderSetCommand(
|
||||
provider,
|
||||
order: requested,
|
||||
});
|
||||
if (!updated)
|
||||
throw new Error("Failed to update auth-profiles.json (lock busy?).");
|
||||
if (!updated) throw new Error("Failed to update auth-profiles.json (lock busy?).");
|
||||
|
||||
runtime.log(`Agent: ${agentId}`);
|
||||
runtime.log(`Provider: ${provider}`);
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
import {
|
||||
confirm as clackConfirm,
|
||||
select as clackSelect,
|
||||
text as clackText,
|
||||
} from "@clack/prompts";
|
||||
import { confirm as clackConfirm, select as clackSelect, text as clackText } from "@clack/prompts";
|
||||
|
||||
import {
|
||||
CLAUDE_CLI_PROFILE_ID,
|
||||
@@ -15,10 +11,7 @@ import { normalizeProviderId } from "../../agents/model-selection.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import { CONFIG_PATH_CLAWDBOT } from "../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import {
|
||||
stylePromptHint,
|
||||
stylePromptMessage,
|
||||
} from "../../terminal/prompt-style.js";
|
||||
import { stylePromptHint, stylePromptMessage } from "../../terminal/prompt-style.js";
|
||||
import { applyAuthProfileConfig } from "../onboard-auth.js";
|
||||
import { updateConfig } from "./shared.js";
|
||||
|
||||
@@ -37,9 +30,7 @@ const select = <T>(params: Parameters<typeof clackSelect<T>>[0]) =>
|
||||
...params,
|
||||
message: stylePromptMessage(params.message),
|
||||
options: params.options.map((opt) =>
|
||||
opt.hint === undefined
|
||||
? opt
|
||||
: { ...opt, hint: stylePromptHint(opt.hint) },
|
||||
opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) },
|
||||
),
|
||||
});
|
||||
|
||||
@@ -121,8 +112,7 @@ export async function modelsAuthPasteTokenCommand(
|
||||
throw new Error("Missing --provider.");
|
||||
}
|
||||
const provider = normalizeProviderId(rawProvider);
|
||||
const profileId =
|
||||
opts.profileId?.trim() || resolveDefaultTokenProfileId(provider);
|
||||
const profileId = opts.profileId?.trim() || resolveDefaultTokenProfileId(provider);
|
||||
|
||||
const tokenInput = await text({
|
||||
message: `Paste token for ${provider}`,
|
||||
@@ -132,8 +122,7 @@ export async function modelsAuthPasteTokenCommand(
|
||||
|
||||
const expires =
|
||||
opts.expiresIn?.trim() && opts.expiresIn.trim().length > 0
|
||||
? Date.now() +
|
||||
parseDurationMs(String(opts.expiresIn).trim(), { defaultUnit: "d" })
|
||||
? Date.now() + parseDurationMs(String(opts.expiresIn).trim(), { defaultUnit: "d" })
|
||||
: undefined;
|
||||
|
||||
upsertAuthProfile({
|
||||
@@ -146,18 +135,13 @@ export async function modelsAuthPasteTokenCommand(
|
||||
},
|
||||
});
|
||||
|
||||
await updateConfig((cfg) =>
|
||||
applyAuthProfileConfig(cfg, { profileId, provider, mode: "token" }),
|
||||
);
|
||||
await updateConfig((cfg) => applyAuthProfileConfig(cfg, { profileId, provider, mode: "token" }));
|
||||
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
runtime.log(`Auth profile: ${profileId} (${provider}/token)`);
|
||||
}
|
||||
|
||||
export async function modelsAuthAddCommand(
|
||||
_opts: Record<string, never>,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
export async function modelsAuthAddCommand(_opts: Record<string, never>, runtime: RuntimeEnv) {
|
||||
const provider = (await select({
|
||||
message: "Token provider",
|
||||
options: [
|
||||
@@ -229,8 +213,5 @@ export async function modelsAuthAddCommand(
|
||||
).trim()
|
||||
: undefined;
|
||||
|
||||
await modelsAuthPasteTokenCommand(
|
||||
{ provider: providerId, profileId, expiresIn },
|
||||
runtime,
|
||||
);
|
||||
await modelsAuthPasteTokenCommand({ provider: providerId, profileId, expiresIn }, runtime);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
buildModelAliasIndex,
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import { buildModelAliasIndex, resolveModelRefFromString } from "../../agents/model-selection.js";
|
||||
import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import {
|
||||
@@ -37,10 +34,7 @@ export async function modelsFallbacksListCommand(
|
||||
for (const entry of fallbacks) runtime.log(`- ${entry}`);
|
||||
}
|
||||
|
||||
export async function modelsFallbacksAddCommand(
|
||||
modelRaw: string,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
export async function modelsFallbacksAddCommand(modelRaw: string, runtime: RuntimeEnv) {
|
||||
const updated = await updateConfig((cfg) => {
|
||||
const resolved = resolveModelTarget({ raw: modelRaw, cfg });
|
||||
const targetKey = modelKey(resolved.provider, resolved.model);
|
||||
@@ -75,9 +69,7 @@ export async function modelsFallbacksAddCommand(
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
model: {
|
||||
...(existingModel?.primary
|
||||
? { primary: existingModel.primary }
|
||||
: undefined),
|
||||
...(existingModel?.primary ? { primary: existingModel.primary } : undefined),
|
||||
fallbacks: [...existing, targetKey],
|
||||
},
|
||||
models: nextModels,
|
||||
@@ -87,15 +79,10 @@ export async function modelsFallbacksAddCommand(
|
||||
});
|
||||
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
runtime.log(
|
||||
`Fallbacks: ${(updated.agents?.defaults?.model?.fallbacks ?? []).join(", ")}`,
|
||||
);
|
||||
runtime.log(`Fallbacks: ${(updated.agents?.defaults?.model?.fallbacks ?? []).join(", ")}`);
|
||||
}
|
||||
|
||||
export async function modelsFallbacksRemoveCommand(
|
||||
modelRaw: string,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
export async function modelsFallbacksRemoveCommand(modelRaw: string, runtime: RuntimeEnv) {
|
||||
const updated = await updateConfig((cfg) => {
|
||||
const resolved = resolveModelTarget({ raw: modelRaw, cfg });
|
||||
const targetKey = modelKey(resolved.provider, resolved.model);
|
||||
@@ -111,10 +98,7 @@ export async function modelsFallbacksRemoveCommand(
|
||||
aliasIndex,
|
||||
});
|
||||
if (!resolvedEntry) return true;
|
||||
return (
|
||||
modelKey(resolvedEntry.ref.provider, resolvedEntry.ref.model) !==
|
||||
targetKey
|
||||
);
|
||||
return modelKey(resolvedEntry.ref.provider, resolvedEntry.ref.model) !== targetKey;
|
||||
});
|
||||
|
||||
if (filtered.length === existing.length) {
|
||||
@@ -132,9 +116,7 @@ export async function modelsFallbacksRemoveCommand(
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
model: {
|
||||
...(existingModel?.primary
|
||||
? { primary: existingModel.primary }
|
||||
: undefined),
|
||||
...(existingModel?.primary ? { primary: existingModel.primary } : undefined),
|
||||
fallbacks: filtered,
|
||||
},
|
||||
},
|
||||
@@ -143,9 +125,7 @@ export async function modelsFallbacksRemoveCommand(
|
||||
});
|
||||
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
runtime.log(
|
||||
`Fallbacks: ${(updated.agents?.defaults?.model?.fallbacks ?? []).join(", ")}`,
|
||||
);
|
||||
runtime.log(`Fallbacks: ${(updated.agents?.defaults?.model?.fallbacks ?? []).join(", ")}`);
|
||||
}
|
||||
|
||||
export async function modelsFallbacksClearCommand(runtime: RuntimeEnv) {
|
||||
@@ -160,9 +140,7 @@ export async function modelsFallbacksClearCommand(runtime: RuntimeEnv) {
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
model: {
|
||||
...(existingModel?.primary
|
||||
? { primary: existingModel.primary }
|
||||
: undefined),
|
||||
...(existingModel?.primary ? { primary: existingModel.primary } : undefined),
|
||||
fallbacks: [],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
buildModelAliasIndex,
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import { buildModelAliasIndex, resolveModelRefFromString } from "../../agents/model-selection.js";
|
||||
import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import {
|
||||
@@ -37,10 +34,7 @@ export async function modelsImageFallbacksListCommand(
|
||||
for (const entry of fallbacks) runtime.log(`- ${entry}`);
|
||||
}
|
||||
|
||||
export async function modelsImageFallbacksAddCommand(
|
||||
modelRaw: string,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
export async function modelsImageFallbacksAddCommand(modelRaw: string, runtime: RuntimeEnv) {
|
||||
const updated = await updateConfig((cfg) => {
|
||||
const resolved = resolveModelTarget({ raw: modelRaw, cfg });
|
||||
const targetKey = modelKey(resolved.provider, resolved.model);
|
||||
@@ -75,9 +69,7 @@ export async function modelsImageFallbacksAddCommand(
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
imageModel: {
|
||||
...(existingModel?.primary
|
||||
? { primary: existingModel.primary }
|
||||
: undefined),
|
||||
...(existingModel?.primary ? { primary: existingModel.primary } : undefined),
|
||||
fallbacks: [...existing, targetKey],
|
||||
},
|
||||
models: nextModels,
|
||||
@@ -92,10 +84,7 @@ export async function modelsImageFallbacksAddCommand(
|
||||
);
|
||||
}
|
||||
|
||||
export async function modelsImageFallbacksRemoveCommand(
|
||||
modelRaw: string,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
export async function modelsImageFallbacksRemoveCommand(modelRaw: string, runtime: RuntimeEnv) {
|
||||
const updated = await updateConfig((cfg) => {
|
||||
const resolved = resolveModelTarget({ raw: modelRaw, cfg });
|
||||
const targetKey = modelKey(resolved.provider, resolved.model);
|
||||
@@ -111,10 +100,7 @@ export async function modelsImageFallbacksRemoveCommand(
|
||||
aliasIndex,
|
||||
});
|
||||
if (!resolvedEntry) return true;
|
||||
return (
|
||||
modelKey(resolvedEntry.ref.provider, resolvedEntry.ref.model) !==
|
||||
targetKey
|
||||
);
|
||||
return modelKey(resolvedEntry.ref.provider, resolvedEntry.ref.model) !== targetKey;
|
||||
});
|
||||
|
||||
if (filtered.length === existing.length) {
|
||||
@@ -132,9 +118,7 @@ export async function modelsImageFallbacksRemoveCommand(
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
imageModel: {
|
||||
...(existingModel?.primary
|
||||
? { primary: existingModel.primary }
|
||||
: undefined),
|
||||
...(existingModel?.primary ? { primary: existingModel.primary } : undefined),
|
||||
fallbacks: filtered,
|
||||
},
|
||||
},
|
||||
@@ -160,9 +144,7 @@ export async function modelsImageFallbacksClearCommand(runtime: RuntimeEnv) {
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
imageModel: {
|
||||
...(existingModel?.primary
|
||||
? { primary: existingModel.primary }
|
||||
: undefined),
|
||||
...(existingModel?.primary ? { primary: existingModel.primary } : undefined),
|
||||
fallbacks: [],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -6,10 +6,7 @@ import {
|
||||
resolveAuthStorePathForDisplay,
|
||||
resolveProfileUnusableUntilForDisplay,
|
||||
} from "../../agents/auth-profiles.js";
|
||||
import {
|
||||
getCustomProviderApiKey,
|
||||
resolveEnvApiKey,
|
||||
} from "../../agents/model-auth.js";
|
||||
import { getCustomProviderApiKey, resolveEnvApiKey } from "../../agents/model-auth.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { shortenHomePath } from "../../utils.js";
|
||||
import { maskApiKey } from "./list.format.js";
|
||||
@@ -25,10 +22,7 @@ export function resolveProviderAuthOverview(params: {
|
||||
const now = Date.now();
|
||||
const profiles = listProfilesForProvider(store, provider);
|
||||
const withUnusableSuffix = (base: string, profileId: string) => {
|
||||
const unusableUntil = resolveProfileUnusableUntilForDisplay(
|
||||
store,
|
||||
profileId,
|
||||
);
|
||||
const unusableUntil = resolveProfileUnusableUntilForDisplay(store, profileId);
|
||||
if (!unusableUntil || now >= unusableUntil) return base;
|
||||
const stats = store.usageStats?.[profileId];
|
||||
const kind =
|
||||
@@ -42,16 +36,10 @@ export function resolveProviderAuthOverview(params: {
|
||||
const profile = store.profiles[profileId];
|
||||
if (!profile) return `${profileId}=missing`;
|
||||
if (profile.type === "api_key") {
|
||||
return withUnusableSuffix(
|
||||
`${profileId}=${maskApiKey(profile.key)}`,
|
||||
profileId,
|
||||
);
|
||||
return withUnusableSuffix(`${profileId}=${maskApiKey(profile.key)}`, profileId);
|
||||
}
|
||||
if (profile.type === "token") {
|
||||
return withUnusableSuffix(
|
||||
`${profileId}=token:${maskApiKey(profile.token)}`,
|
||||
profileId,
|
||||
);
|
||||
return withUnusableSuffix(`${profileId}=token:${maskApiKey(profile.token)}`, profileId);
|
||||
}
|
||||
const display = resolveAuthProfileDisplayLabel({ cfg, store, profileId });
|
||||
const suffix =
|
||||
@@ -63,15 +51,9 @@ export function resolveProviderAuthOverview(params: {
|
||||
const base = `${profileId}=OAuth${suffix ? ` ${suffix}` : ""}`;
|
||||
return withUnusableSuffix(base, profileId);
|
||||
});
|
||||
const oauthCount = profiles.filter(
|
||||
(id) => store.profiles[id]?.type === "oauth",
|
||||
).length;
|
||||
const tokenCount = profiles.filter(
|
||||
(id) => store.profiles[id]?.type === "token",
|
||||
).length;
|
||||
const apiKeyCount = profiles.filter(
|
||||
(id) => store.profiles[id]?.type === "api_key",
|
||||
).length;
|
||||
const oauthCount = profiles.filter((id) => store.profiles[id]?.type === "oauth").length;
|
||||
const tokenCount = profiles.filter((id) => store.profiles[id]?.type === "token").length;
|
||||
const apiKeyCount = profiles.filter((id) => store.profiles[id]?.type === "api_key").length;
|
||||
|
||||
const envKey = resolveEnvApiKey(provider);
|
||||
const customKey = getCustomProviderApiKey(cfg, provider);
|
||||
@@ -85,8 +67,7 @@ export function resolveProviderAuthOverview(params: {
|
||||
}
|
||||
if (envKey) {
|
||||
const isOAuthEnv =
|
||||
envKey.source.includes("OAUTH_TOKEN") ||
|
||||
envKey.source.toLowerCase().includes("oauth");
|
||||
envKey.source.includes("OAUTH_TOKEN") || envKey.source.toLowerCase().includes("oauth");
|
||||
return {
|
||||
kind: "env",
|
||||
detail: isOAuthEnv ? "OAuth (env)" : maskApiKey(envKey.apiKey),
|
||||
@@ -112,8 +93,7 @@ export function resolveProviderAuthOverview(params: {
|
||||
? {
|
||||
env: {
|
||||
value:
|
||||
envKey.source.includes("OAUTH_TOKEN") ||
|
||||
envKey.source.toLowerCase().includes("oauth")
|
||||
envKey.source.includes("OAUTH_TOKEN") || envKey.source.toLowerCase().includes("oauth")
|
||||
? "OAuth (env)"
|
||||
: maskApiKey(envKey.apiKey),
|
||||
source: envKey.source,
|
||||
|
||||
@@ -43,12 +43,9 @@ export function resolveConfiguredEntries(cfg: ClawdbotConfig) {
|
||||
const imageModelConfig = cfg.agents?.defaults?.imageModel as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| undefined;
|
||||
const modelFallbacks =
|
||||
typeof modelConfig === "object" ? (modelConfig?.fallbacks ?? []) : [];
|
||||
const modelFallbacks = typeof modelConfig === "object" ? (modelConfig?.fallbacks ?? []) : [];
|
||||
const imageFallbacks =
|
||||
typeof imageModelConfig === "object"
|
||||
? (imageModelConfig?.fallbacks ?? [])
|
||||
: [];
|
||||
typeof imageModelConfig === "object" ? (imageModelConfig?.fallbacks ?? []) : [];
|
||||
const imagePrimary = imageModelConfig?.primary?.trim() ?? "";
|
||||
|
||||
modelFallbacks.forEach((raw, idx) => {
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import {
|
||||
colorize,
|
||||
isRich as isRichTerminal,
|
||||
theme,
|
||||
} from "../../terminal/theme.js";
|
||||
import { colorize, isRich as isRichTerminal, theme } from "../../terminal/theme.js";
|
||||
|
||||
export const isRich = (opts?: { json?: boolean; plain?: boolean }) =>
|
||||
Boolean(isRichTerminal() && !opts?.json && !opts?.plain);
|
||||
|
||||
export const pad = (value: string, size: number) => value.padEnd(size);
|
||||
|
||||
export const formatKey = (key: string, rich: boolean) =>
|
||||
colorize(rich, theme.warn, key);
|
||||
export const formatKey = (key: string, rich: boolean) => colorize(rich, theme.warn, key);
|
||||
|
||||
export const formatValue = (value: string, rich: boolean) =>
|
||||
colorize(rich, theme.info, value);
|
||||
export const formatValue = (value: string, rich: boolean) => colorize(rich, theme.info, value);
|
||||
|
||||
export const formatKeyValue = (
|
||||
key: string,
|
||||
@@ -22,8 +16,7 @@ export const formatKeyValue = (
|
||||
valueColor: (value: string) => string = theme.info,
|
||||
) => `${formatKey(key, rich)}=${colorize(rich, valueColor, value)}`;
|
||||
|
||||
export const formatSeparator = (rich: boolean) =>
|
||||
colorize(rich, theme.muted, " | ");
|
||||
export const formatSeparator = (rich: boolean) => colorize(rich, theme.muted, " | ");
|
||||
|
||||
export const formatTag = (tag: string, rich: boolean) => {
|
||||
if (!rich) return tag;
|
||||
|
||||
@@ -8,11 +8,7 @@ import { resolveConfiguredEntries } from "./list.configured.js";
|
||||
import { loadModelRegistry, toModelRow } from "./list.registry.js";
|
||||
import { printModelTable } from "./list.table.js";
|
||||
import type { ModelRow } from "./list.types.js";
|
||||
import {
|
||||
DEFAULT_PROVIDER,
|
||||
ensureFlagCompatibility,
|
||||
modelKey,
|
||||
} from "./shared.js";
|
||||
import { DEFAULT_PROVIDER, ensureFlagCompatibility, modelKey } from "./shared.js";
|
||||
|
||||
export async function modelsListCommand(
|
||||
opts: {
|
||||
@@ -44,9 +40,7 @@ export async function modelsListCommand(
|
||||
runtime.error(`Model registry unavailable: ${String(err)}`);
|
||||
}
|
||||
|
||||
const modelByKey = new Map(
|
||||
models.map((model) => [modelKey(model.provider, model.id), model]),
|
||||
);
|
||||
const modelByKey = new Map(models.map((model) => [modelKey(model.provider, model.id), model]));
|
||||
|
||||
const { entries } = resolveConfiguredEntries(cfg);
|
||||
const configuredByKey = new Map(entries.map((entry) => [entry.key, entry]));
|
||||
@@ -97,10 +91,7 @@ export async function modelsListCommand(
|
||||
}
|
||||
} else {
|
||||
for (const entry of entries) {
|
||||
if (
|
||||
providerFilter &&
|
||||
entry.ref.provider.toLowerCase() !== providerFilter
|
||||
) {
|
||||
if (providerFilter && entry.ref.provider.toLowerCase() !== providerFilter) {
|
||||
continue;
|
||||
}
|
||||
const model = modelByKey.get(entry.key);
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import type { Api, Model } from "@mariozechner/pi-ai";
|
||||
import {
|
||||
discoverAuthStorage,
|
||||
discoverModels,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import { discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
import { resolveClawdbotAgentDir } from "../../agents/agent-paths.js";
|
||||
import type { AuthProfileStore } from "../../agents/auth-profiles.js";
|
||||
import { listProfilesForProvider } from "../../agents/auth-profiles.js";
|
||||
import {
|
||||
getCustomProviderApiKey,
|
||||
resolveEnvApiKey,
|
||||
} from "../../agents/model-auth.js";
|
||||
import { getCustomProviderApiKey, resolveEnvApiKey } from "../../agents/model-auth.js";
|
||||
import { ensureClawdbotModelsJson } from "../../agents/models-config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import type { ModelRow } from "./list.types.js";
|
||||
@@ -32,11 +26,7 @@ const isLocalBaseUrl = (baseUrl: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const hasAuthForProvider = (
|
||||
provider: string,
|
||||
cfg: ClawdbotConfig,
|
||||
authStore: AuthProfileStore,
|
||||
) => {
|
||||
const hasAuthForProvider = (provider: string, cfg: ClawdbotConfig, authStore: AuthProfileStore) => {
|
||||
if (listProfilesForProvider(authStore, provider).length > 0) return true;
|
||||
if (resolveEnvApiKey(provider)) return true;
|
||||
if (getCustomProviderApiKey(cfg, provider)) return true;
|
||||
@@ -50,9 +40,7 @@ export async function loadModelRegistry(cfg: ClawdbotConfig) {
|
||||
const registry = discoverModels(authStorage, agentDir);
|
||||
const models = registry.getAll() as Model<Api>[];
|
||||
const availableModels = registry.getAvailable() as Model<Api>[];
|
||||
const availableKeys = new Set(
|
||||
availableModels.map((model) => modelKey(model.provider, model.id)),
|
||||
);
|
||||
const availableKeys = new Set(availableModels.map((model) => modelKey(model.provider, model.id)));
|
||||
return { registry, models, availableKeys };
|
||||
}
|
||||
|
||||
@@ -65,15 +53,7 @@ export function toModelRow(params: {
|
||||
cfg?: ClawdbotConfig;
|
||||
authStore?: AuthProfileStore;
|
||||
}): ModelRow {
|
||||
const {
|
||||
model,
|
||||
key,
|
||||
tags,
|
||||
aliases = [],
|
||||
availableKeys,
|
||||
cfg,
|
||||
authStore,
|
||||
} = params;
|
||||
const { model, key, tags, aliases = [], availableKeys, cfg, authStore } = params;
|
||||
if (!model) {
|
||||
return {
|
||||
key,
|
||||
|
||||
@@ -11,25 +11,15 @@ import {
|
||||
resolveProfileUnusableUntilForDisplay,
|
||||
} from "../../agents/auth-profiles.js";
|
||||
import { resolveEnvApiKey } from "../../agents/model-auth.js";
|
||||
import {
|
||||
parseModelRef,
|
||||
resolveConfiguredModelRef,
|
||||
} from "../../agents/model-selection.js";
|
||||
import { parseModelRef, resolveConfiguredModelRef } from "../../agents/model-selection.js";
|
||||
import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js";
|
||||
import {
|
||||
getShellEnvAppliedKeys,
|
||||
shouldEnableShellEnvFallback,
|
||||
} from "../../infra/shell-env.js";
|
||||
import { getShellEnvAppliedKeys, shouldEnableShellEnvFallback } from "../../infra/shell-env.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { colorize, theme } from "../../terminal/theme.js";
|
||||
import { shortenHomePath } from "../../utils.js";
|
||||
import { resolveProviderAuthOverview } from "./list.auth-overview.js";
|
||||
import { isRich } from "./list.format.js";
|
||||
import {
|
||||
DEFAULT_MODEL,
|
||||
DEFAULT_PROVIDER,
|
||||
ensureFlagCompatibility,
|
||||
} from "./shared.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER, ensureFlagCompatibility } from "./shared.js";
|
||||
|
||||
export async function modelsStatusCommand(
|
||||
opts: { json?: boolean; plain?: boolean; check?: boolean },
|
||||
@@ -52,26 +42,21 @@ export async function modelsStatusCommand(
|
||||
| string
|
||||
| undefined;
|
||||
const rawModel =
|
||||
typeof modelConfig === "string"
|
||||
? modelConfig.trim()
|
||||
: (modelConfig?.primary?.trim() ?? "");
|
||||
typeof modelConfig === "string" ? modelConfig.trim() : (modelConfig?.primary?.trim() ?? "");
|
||||
const resolvedLabel = `${resolved.provider}/${resolved.model}`;
|
||||
const defaultLabel = rawModel || resolvedLabel;
|
||||
const fallbacks =
|
||||
typeof modelConfig === "object" ? (modelConfig?.fallbacks ?? []) : [];
|
||||
const fallbacks = typeof modelConfig === "object" ? (modelConfig?.fallbacks ?? []) : [];
|
||||
const imageModel =
|
||||
typeof imageConfig === "string"
|
||||
? imageConfig.trim()
|
||||
: (imageConfig?.primary?.trim() ?? "");
|
||||
const imageFallbacks =
|
||||
typeof imageConfig === "object" ? (imageConfig?.fallbacks ?? []) : [];
|
||||
const aliases = Object.entries(cfg.agents?.defaults?.models ?? {}).reduce<
|
||||
Record<string, string>
|
||||
>((acc, [key, entry]) => {
|
||||
const alias = entry?.alias?.trim();
|
||||
if (alias) acc[alias] = key;
|
||||
return acc;
|
||||
}, {});
|
||||
typeof imageConfig === "string" ? imageConfig.trim() : (imageConfig?.primary?.trim() ?? "");
|
||||
const imageFallbacks = typeof imageConfig === "object" ? (imageConfig?.fallbacks ?? []) : [];
|
||||
const aliases = Object.entries(cfg.agents?.defaults?.models ?? {}).reduce<Record<string, string>>(
|
||||
(acc, [key, entry]) => {
|
||||
const alias = entry?.alias?.trim();
|
||||
if (alias) acc[alias] = key;
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
const allowed = Object.keys(cfg.agents?.defaults?.models ?? {});
|
||||
|
||||
const agentDir = resolveClawdbotAgentDir();
|
||||
@@ -90,22 +75,11 @@ export async function modelsStatusCommand(
|
||||
);
|
||||
const providersFromModels = new Set<string>();
|
||||
const providersInUse = new Set<string>();
|
||||
for (const raw of [
|
||||
defaultLabel,
|
||||
...fallbacks,
|
||||
imageModel,
|
||||
...imageFallbacks,
|
||||
...allowed,
|
||||
]) {
|
||||
for (const raw of [defaultLabel, ...fallbacks, imageModel, ...imageFallbacks, ...allowed]) {
|
||||
const parsed = parseModelRef(String(raw ?? ""), DEFAULT_PROVIDER);
|
||||
if (parsed?.provider) providersFromModels.add(parsed.provider);
|
||||
}
|
||||
for (const raw of [
|
||||
defaultLabel,
|
||||
...fallbacks,
|
||||
imageModel,
|
||||
...imageFallbacks,
|
||||
]) {
|
||||
for (const raw of [defaultLabel, ...fallbacks, imageModel, ...imageFallbacks]) {
|
||||
const parsed = parseModelRef(String(raw ?? ""), DEFAULT_PROVIDER);
|
||||
if (parsed?.provider) providersInUse.add(parsed.provider);
|
||||
}
|
||||
@@ -145,23 +119,15 @@ export async function modelsStatusCommand(
|
||||
|
||||
const applied = getShellEnvAppliedKeys();
|
||||
const shellFallbackEnabled =
|
||||
shouldEnableShellEnvFallback(process.env) ||
|
||||
cfg.env?.shellEnv?.enabled === true;
|
||||
shouldEnableShellEnvFallback(process.env) || cfg.env?.shellEnv?.enabled === true;
|
||||
|
||||
const providerAuth = providers
|
||||
.map((provider) =>
|
||||
resolveProviderAuthOverview({ provider, cfg, store, modelsPath }),
|
||||
)
|
||||
.map((provider) => resolveProviderAuthOverview({ provider, cfg, store, modelsPath }))
|
||||
.filter((entry) => {
|
||||
const hasAny =
|
||||
entry.profiles.count > 0 ||
|
||||
Boolean(entry.env) ||
|
||||
Boolean(entry.modelsJson);
|
||||
const hasAny = entry.profiles.count > 0 || Boolean(entry.env) || Boolean(entry.modelsJson);
|
||||
return hasAny;
|
||||
});
|
||||
const providerAuthMap = new Map(
|
||||
providerAuth.map((entry) => [entry.provider, entry]),
|
||||
);
|
||||
const providerAuthMap = new Map(providerAuth.map((entry) => [entry.provider, entry]));
|
||||
const missingProvidersInUse = Array.from(providersInUse)
|
||||
.filter((provider) => !providerAuthMap.has(provider))
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
@@ -169,15 +135,11 @@ export async function modelsStatusCommand(
|
||||
const providersWithOauth = providerAuth
|
||||
.filter(
|
||||
(entry) =>
|
||||
entry.profiles.oauth > 0 ||
|
||||
entry.profiles.token > 0 ||
|
||||
entry.env?.value === "OAuth (env)",
|
||||
entry.profiles.oauth > 0 || entry.profiles.token > 0 || entry.env?.value === "OAuth (env)",
|
||||
)
|
||||
.map((entry) => {
|
||||
const count =
|
||||
entry.profiles.oauth +
|
||||
entry.profiles.token +
|
||||
(entry.env?.value === "OAuth (env)" ? 1 : 0);
|
||||
entry.profiles.oauth + entry.profiles.token + (entry.env?.value === "OAuth (env)" ? 1 : 0);
|
||||
return `${entry.provider} (${count})`;
|
||||
});
|
||||
|
||||
@@ -202,10 +164,7 @@ export async function modelsStatusCommand(
|
||||
remainingMs: number;
|
||||
}> = [];
|
||||
for (const profileId of Object.keys(store.usageStats ?? {})) {
|
||||
const unusableUntil = resolveProfileUnusableUntilForDisplay(
|
||||
store,
|
||||
profileId,
|
||||
);
|
||||
const unusableUntil = resolveProfileUnusableUntilForDisplay(store, profileId);
|
||||
if (!unusableUntil || now >= unusableUntil) continue;
|
||||
const stats = store.usageStats?.[profileId];
|
||||
const kind =
|
||||
@@ -226,12 +185,9 @@ export async function modelsStatusCommand(
|
||||
|
||||
const checkStatus = (() => {
|
||||
const hasExpiredOrMissing =
|
||||
oauthProfiles.some((profile) =>
|
||||
["expired", "missing"].includes(profile.status),
|
||||
) || missingProvidersInUse.length > 0;
|
||||
const hasExpiring = oauthProfiles.some(
|
||||
(profile) => profile.status === "expiring",
|
||||
);
|
||||
oauthProfiles.some((profile) => ["expired", "missing"].includes(profile.status)) ||
|
||||
missingProvidersInUse.length > 0;
|
||||
const hasExpiring = oauthProfiles.some((profile) => profile.status === "expiring");
|
||||
if (hasExpiredOrMissing) return 1;
|
||||
if (hasExpiring) return 2;
|
||||
return 0;
|
||||
@@ -282,12 +238,9 @@ export async function modelsStatusCommand(
|
||||
}
|
||||
|
||||
const rich = isRich(opts);
|
||||
const label = (value: string) =>
|
||||
colorize(rich, theme.accent, value.padEnd(14));
|
||||
const label = (value: string) => colorize(rich, theme.accent, value.padEnd(14));
|
||||
const displayDefault =
|
||||
rawModel && rawModel !== resolvedLabel
|
||||
? `${resolvedLabel} (from ${rawModel})`
|
||||
: resolvedLabel;
|
||||
rawModel && rawModel !== resolvedLabel ? `${resolvedLabel} (from ${rawModel})` : resolvedLabel;
|
||||
|
||||
runtime.log(
|
||||
`${label("Config")}${colorize(rich, theme.muted, ":")} ${colorize(rich, theme.info, CONFIG_PATH_CLAWDBOT)}`,
|
||||
@@ -368,11 +321,7 @@ export async function modelsStatusCommand(
|
||||
rich,
|
||||
shellFallbackEnabled ? theme.success : theme.muted,
|
||||
shellFallbackEnabled ? "on" : "off",
|
||||
)}${
|
||||
applied.length
|
||||
? colorize(rich, theme.muted, ` (applied: ${applied.join(", ")})`)
|
||||
: ""
|
||||
}`,
|
||||
)}${applied.length ? colorize(rich, theme.muted, ` (applied: ${applied.join(", ")})`) : ""}`,
|
||||
);
|
||||
runtime.log(
|
||||
`${label(`Providers w/ OAuth/tokens (${providersWithOauth.length || 0})`)}${colorize(
|
||||
@@ -472,9 +421,7 @@ export async function modelsStatusCommand(
|
||||
? ` expires in ${formatRemainingShort(profile.remainingMs)}`
|
||||
: " expires unknown";
|
||||
const source =
|
||||
profile.source !== "store"
|
||||
? colorize(rich, theme.muted, ` (${profile.source})`)
|
||||
: "";
|
||||
profile.source !== "store" ? colorize(rich, theme.muted, ` (${profile.source})`) : "";
|
||||
runtime.log(`- ${label} ${status}${expiry}${source}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,7 @@ const mocks = vi.hoisted(() => {
|
||||
.filter(([, cred]) => cred.provider === provider)
|
||||
.map(([id]) => id);
|
||||
}),
|
||||
resolveAuthProfileDisplayLabel: vi.fn(
|
||||
({ profileId }: { profileId: string }) => profileId,
|
||||
),
|
||||
resolveAuthProfileDisplayLabel: vi.fn(({ profileId }: { profileId: string }) => profileId),
|
||||
resolveAuthStorePathForDisplay: vi
|
||||
.fn()
|
||||
.mockReturnValue("/tmp/clawdbot-agent/auth-profiles.json"),
|
||||
@@ -58,9 +56,7 @@ const mocks = vi.hoisted(() => {
|
||||
return null;
|
||||
}),
|
||||
getCustomProviderApiKey: vi.fn().mockReturnValue(undefined),
|
||||
getShellEnvAppliedKeys: vi
|
||||
.fn()
|
||||
.mockReturnValue(["OPENAI_API_KEY", "ANTHROPIC_OAUTH_TOKEN"]),
|
||||
getShellEnvAppliedKeys: vi.fn().mockReturnValue(["OPENAI_API_KEY", "ANTHROPIC_OAUTH_TOKEN"]),
|
||||
shouldEnableShellEnvFallback: vi.fn().mockReturnValue(true),
|
||||
loadConfig: vi.fn().mockReturnValue({
|
||||
agents: {
|
||||
@@ -80,8 +76,7 @@ vi.mock("../../agents/agent-paths.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/auth-profiles.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../../agents/auth-profiles.js")>();
|
||||
const actual = await importOriginal<typeof import("../../agents/auth-profiles.js")>();
|
||||
return {
|
||||
...actual,
|
||||
ensureAuthProfileStore: mocks.ensureAuthProfileStore,
|
||||
@@ -102,8 +97,7 @@ vi.mock("../../infra/shell-env.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../../config/config.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../../config/config.js")>();
|
||||
const actual = await importOriginal<typeof import("../../config/config.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadConfig: mocks.loadConfig,
|
||||
@@ -121,18 +115,12 @@ const runtime = {
|
||||
describe("modelsStatusCommand auth overview", () => {
|
||||
it("includes masked auth sources in JSON output", async () => {
|
||||
await modelsStatusCommand({ json: true }, runtime as never);
|
||||
const payload = JSON.parse(
|
||||
String((runtime.log as vi.Mock).mock.calls[0][0]),
|
||||
);
|
||||
const payload = JSON.parse(String((runtime.log as vi.Mock).mock.calls[0][0]));
|
||||
|
||||
expect(payload.defaultModel).toBe("anthropic/claude-opus-4-5");
|
||||
expect(payload.auth.storePath).toBe(
|
||||
"/tmp/clawdbot-agent/auth-profiles.json",
|
||||
);
|
||||
expect(payload.auth.storePath).toBe("/tmp/clawdbot-agent/auth-profiles.json");
|
||||
expect(payload.auth.shellEnvFallback.enabled).toBe(true);
|
||||
expect(payload.auth.shellEnvFallback.appliedKeys).toContain(
|
||||
"OPENAI_API_KEY",
|
||||
);
|
||||
expect(payload.auth.shellEnvFallback.appliedKeys).toContain("OPENAI_API_KEY");
|
||||
expect(payload.auth.missingProvidersInUse).toEqual([]);
|
||||
expect(payload.auth.oauth.warnAfterMs).toBeGreaterThan(0);
|
||||
expect(payload.auth.oauth.profiles.length).toBeGreaterThan(0);
|
||||
@@ -152,14 +140,10 @@ describe("modelsStatusCommand auth overview", () => {
|
||||
expect(openai?.env?.value).toContain("...");
|
||||
|
||||
expect(
|
||||
(payload.auth.providersWithOAuth as string[]).some((e) =>
|
||||
e.startsWith("anthropic"),
|
||||
),
|
||||
(payload.auth.providersWithOAuth as string[]).some((e) => e.startsWith("anthropic")),
|
||||
).toBe(true);
|
||||
expect(
|
||||
(payload.auth.providersWithOAuth as string[]).some((e) =>
|
||||
e.startsWith("openai-codex"),
|
||||
),
|
||||
(payload.auth.providersWithOAuth as string[]).some((e) => e.startsWith("openai-codex")),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@@ -175,10 +159,7 @@ describe("modelsStatusCommand auth overview", () => {
|
||||
mocks.resolveEnvApiKey.mockImplementation(() => null);
|
||||
|
||||
try {
|
||||
await modelsStatusCommand(
|
||||
{ check: true, plain: true },
|
||||
localRuntime as never,
|
||||
);
|
||||
await modelsStatusCommand({ check: true, plain: true }, localRuntime as never);
|
||||
expect(localRuntime.exit).toHaveBeenCalledWith(1);
|
||||
} finally {
|
||||
mocks.store.profiles = originalProfiles;
|
||||
|
||||
@@ -51,8 +51,7 @@ export function printModelTable(
|
||||
const ctxLabel = pad(formatTokenK(row.contextWindow), CTX_PAD);
|
||||
const localText = row.local === null ? "-" : row.local ? "yes" : "no";
|
||||
const localLabel = pad(localText, LOCAL_PAD);
|
||||
const authText =
|
||||
row.available === null ? "-" : row.available ? "yes" : "no";
|
||||
const authText = row.available === null ? "-" : row.available ? "yes" : "no";
|
||||
const authLabel = pad(authText, AUTH_PAD);
|
||||
const tagsLabel =
|
||||
row.tags.length > 0
|
||||
@@ -68,20 +67,12 @@ export function printModelTable(
|
||||
);
|
||||
const coloredLocal = colorize(
|
||||
rich,
|
||||
row.local === null
|
||||
? theme.muted
|
||||
: row.local
|
||||
? theme.success
|
||||
: theme.muted,
|
||||
row.local === null ? theme.muted : row.local ? theme.success : theme.muted,
|
||||
localLabel,
|
||||
);
|
||||
const coloredAuth = colorize(
|
||||
rich,
|
||||
row.available === null
|
||||
? theme.muted
|
||||
: row.available
|
||||
? theme.success
|
||||
: theme.error,
|
||||
row.available === null ? theme.muted : row.available ? theme.success : theme.error,
|
||||
authLabel,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import {
|
||||
cancel,
|
||||
multiselect as clackMultiselect,
|
||||
isCancel,
|
||||
} from "@clack/prompts";
|
||||
import { cancel, multiselect as clackMultiselect, isCancel } from "@clack/prompts";
|
||||
import { resolveApiKeyForProvider } from "../../agents/model-auth.js";
|
||||
import {
|
||||
type ModelScanResult,
|
||||
scanOpenRouterModels,
|
||||
} from "../../agents/model-scan.js";
|
||||
import { type ModelScanResult, scanOpenRouterModels } from "../../agents/model-scan.js";
|
||||
import { withProgressTotals } from "../../cli/progress.js";
|
||||
import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
@@ -26,9 +19,7 @@ const multiselect = <T>(params: Parameters<typeof clackMultiselect<T>>[0]) =>
|
||||
...params,
|
||||
message: stylePromptMessage(params.message),
|
||||
options: params.options.map((opt) =>
|
||||
opt.hint === undefined
|
||||
? opt
|
||||
: { ...opt, hint: stylePromptHint(opt.hint) },
|
||||
opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) },
|
||||
),
|
||||
});
|
||||
|
||||
@@ -81,21 +72,15 @@ function sortImageResults(results: ModelScanResult[]): ModelScanResult[] {
|
||||
}
|
||||
|
||||
function buildScanHint(result: ModelScanResult): string {
|
||||
const toolLabel = result.tool.ok
|
||||
? `tool ${formatMs(result.tool.latencyMs)}`
|
||||
: "tool fail";
|
||||
const toolLabel = result.tool.ok ? `tool ${formatMs(result.tool.latencyMs)}` : "tool fail";
|
||||
const imageLabel = result.image.skipped
|
||||
? "img skip"
|
||||
: result.image.ok
|
||||
? `img ${formatMs(result.image.latencyMs)}`
|
||||
: "img fail";
|
||||
const ctxLabel = result.contextLength
|
||||
? `ctx ${formatTokenK(result.contextLength)}`
|
||||
: "ctx ?";
|
||||
const ctxLabel = result.contextLength ? `ctx ${formatTokenK(result.contextLength)}` : "ctx ?";
|
||||
const paramLabel = result.inferredParamB ? `${result.inferredParamB}b` : null;
|
||||
return [toolLabel, imageLabel, ctxLabel, paramLabel]
|
||||
.filter(Boolean)
|
||||
.join(" | ");
|
||||
return [toolLabel, imageLabel, ctxLabel, paramLabel].filter(Boolean).join(" | ");
|
||||
}
|
||||
|
||||
function printScanSummary(results: ModelScanResult[], runtime: RuntimeEnv) {
|
||||
@@ -121,30 +106,16 @@ function printScanTable(results: ModelScanResult[], runtime: RuntimeEnv) {
|
||||
|
||||
for (const entry of results) {
|
||||
const modelLabel = pad(truncate(entry.modelRef, MODEL_PAD), MODEL_PAD);
|
||||
const toolLabel = pad(
|
||||
entry.tool.ok ? formatMs(entry.tool.latencyMs) : "fail",
|
||||
10,
|
||||
);
|
||||
const toolLabel = pad(entry.tool.ok ? formatMs(entry.tool.latencyMs) : "fail", 10);
|
||||
const imageLabel = pad(
|
||||
entry.image.ok
|
||||
? formatMs(entry.image.latencyMs)
|
||||
: entry.image.skipped
|
||||
? "skip"
|
||||
: "fail",
|
||||
entry.image.ok ? formatMs(entry.image.latencyMs) : entry.image.skipped ? "skip" : "fail",
|
||||
10,
|
||||
);
|
||||
const ctxLabel = pad(formatTokenK(entry.contextLength), CTX_PAD);
|
||||
const paramsLabel = pad(
|
||||
entry.inferredParamB ? `${entry.inferredParamB}b` : "-",
|
||||
8,
|
||||
);
|
||||
const paramsLabel = pad(entry.inferredParamB ? `${entry.inferredParamB}b` : "-", 8);
|
||||
const notes = entry.modality ? `modality:${entry.modality}` : "";
|
||||
|
||||
runtime.log(
|
||||
[modelLabel, toolLabel, imageLabel, ctxLabel, paramsLabel, notes].join(
|
||||
" ",
|
||||
),
|
||||
);
|
||||
runtime.log([modelLabel, toolLabel, imageLabel, ctxLabel, paramsLabel, notes].join(" "));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,17 +137,11 @@ export async function modelsScanCommand(
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
const minParams = opts.minParams ? Number(opts.minParams) : undefined;
|
||||
if (
|
||||
minParams !== undefined &&
|
||||
(!Number.isFinite(minParams) || minParams < 0)
|
||||
) {
|
||||
if (minParams !== undefined && (!Number.isFinite(minParams) || minParams < 0)) {
|
||||
throw new Error("--min-params must be >= 0");
|
||||
}
|
||||
const maxAgeDays = opts.maxAgeDays ? Number(opts.maxAgeDays) : undefined;
|
||||
if (
|
||||
maxAgeDays !== undefined &&
|
||||
(!Number.isFinite(maxAgeDays) || maxAgeDays < 0)
|
||||
) {
|
||||
if (maxAgeDays !== undefined && (!Number.isFinite(maxAgeDays) || maxAgeDays < 0)) {
|
||||
throw new Error("--max-age-days must be >= 0");
|
||||
}
|
||||
const maxCandidates = opts.maxCandidates ? Number(opts.maxCandidates) : 6;
|
||||
@@ -188,10 +153,7 @@ export async function modelsScanCommand(
|
||||
throw new Error("--timeout must be > 0");
|
||||
}
|
||||
const concurrency = opts.concurrency ? Number(opts.concurrency) : undefined;
|
||||
if (
|
||||
concurrency !== undefined &&
|
||||
(!Number.isFinite(concurrency) || concurrency <= 0)
|
||||
) {
|
||||
if (concurrency !== undefined && (!Number.isFinite(concurrency) || concurrency <= 0)) {
|
||||
throw new Error("--concurrency must be > 0");
|
||||
}
|
||||
|
||||
@@ -288,9 +250,7 @@ export async function modelsScanCommand(
|
||||
});
|
||||
|
||||
if (isCancel(selection)) {
|
||||
cancel(
|
||||
stylePromptTitle("Model scan cancelled.") ?? "Model scan cancelled.",
|
||||
);
|
||||
cancel(stylePromptTitle("Model scan cancelled.") ?? "Model scan cancelled.");
|
||||
runtime.exit(0);
|
||||
}
|
||||
|
||||
@@ -307,9 +267,7 @@ export async function modelsScanCommand(
|
||||
});
|
||||
|
||||
if (isCancel(imageSelection)) {
|
||||
cancel(
|
||||
stylePromptTitle("Model scan cancelled.") ?? "Model scan cancelled.",
|
||||
);
|
||||
cancel(stylePromptTitle("Model scan cancelled.") ?? "Model scan cancelled.");
|
||||
runtime.exit(0);
|
||||
}
|
||||
|
||||
@@ -340,9 +298,7 @@ export async function modelsScanCommand(
|
||||
const nextImageModel =
|
||||
selectedImages.length > 0
|
||||
? {
|
||||
...(existingImageModel?.primary
|
||||
? { primary: existingImageModel.primary }
|
||||
: undefined),
|
||||
...(existingImageModel?.primary ? { primary: existingImageModel.primary } : undefined),
|
||||
fallbacks: selectedImages,
|
||||
...(opts.setImage ? { primary: selectedImages[0] } : {}),
|
||||
}
|
||||
@@ -353,9 +309,7 @@ export async function modelsScanCommand(
|
||||
const defaults = {
|
||||
...cfg.agents?.defaults,
|
||||
model: {
|
||||
...(existingModel?.primary
|
||||
? { primary: existingModel.primary }
|
||||
: undefined),
|
||||
...(existingModel?.primary ? { primary: existingModel.primary } : undefined),
|
||||
fallbacks: selected,
|
||||
...(opts.setDefault ? { primary: selected[0] } : {}),
|
||||
},
|
||||
|
||||
@@ -2,10 +2,7 @@ import { CONFIG_PATH_CLAWDBOT } from "../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { resolveModelTarget, updateConfig } from "./shared.js";
|
||||
|
||||
export async function modelsSetImageCommand(
|
||||
modelRaw: string,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
export async function modelsSetImageCommand(modelRaw: string, runtime: RuntimeEnv) {
|
||||
const updated = await updateConfig((cfg) => {
|
||||
const resolved = resolveModelTarget({ raw: modelRaw, cfg });
|
||||
const key = `${resolved.provider}/${resolved.model}`;
|
||||
@@ -21,9 +18,7 @@ export async function modelsSetImageCommand(
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
imageModel: {
|
||||
...(existingModel?.fallbacks
|
||||
? { fallbacks: existingModel.fallbacks }
|
||||
: undefined),
|
||||
...(existingModel?.fallbacks ? { fallbacks: existingModel.fallbacks } : undefined),
|
||||
primary: key,
|
||||
},
|
||||
models: nextModels,
|
||||
@@ -33,7 +28,5 @@ export async function modelsSetImageCommand(
|
||||
});
|
||||
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
runtime.log(
|
||||
`Image model: ${updated.agents?.defaults?.imageModel?.primary ?? modelRaw}`,
|
||||
);
|
||||
runtime.log(`Image model: ${updated.agents?.defaults?.imageModel?.primary ?? modelRaw}`);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ export async function modelsSetCommand(modelRaw: string, runtime: RuntimeEnv) {
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
model: {
|
||||
...(existingModel?.fallbacks
|
||||
? { fallbacks: existingModel.fallbacks }
|
||||
: undefined),
|
||||
...(existingModel?.fallbacks ? { fallbacks: existingModel.fallbacks } : undefined),
|
||||
primary: key,
|
||||
},
|
||||
models: nextModels,
|
||||
@@ -30,7 +28,5 @@ export async function modelsSetCommand(modelRaw: string, runtime: RuntimeEnv) {
|
||||
});
|
||||
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
runtime.log(
|
||||
`Default model: ${updated.agents?.defaults?.model?.primary ?? modelRaw}`,
|
||||
);
|
||||
runtime.log(`Default model: ${updated.agents?.defaults?.model?.primary ?? modelRaw}`);
|
||||
}
|
||||
|
||||
@@ -11,10 +11,7 @@ import {
|
||||
writeConfigFile,
|
||||
} from "../../config/config.js";
|
||||
|
||||
export const ensureFlagCompatibility = (opts: {
|
||||
json?: boolean;
|
||||
plain?: boolean;
|
||||
}) => {
|
||||
export const ensureFlagCompatibility = (opts: { json?: boolean; plain?: boolean }) => {
|
||||
if (opts.json && opts.plain) {
|
||||
throw new Error("Choose either --json or --plain, not both.");
|
||||
}
|
||||
@@ -38,9 +35,7 @@ export async function updateConfig(
|
||||
): Promise<ClawdbotConfig> {
|
||||
const snapshot = await readConfigFileSnapshot();
|
||||
if (!snapshot.valid) {
|
||||
const issues = snapshot.issues
|
||||
.map((issue) => `- ${issue.path}: ${issue.message}`)
|
||||
.join("\n");
|
||||
const issues = snapshot.issues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n");
|
||||
throw new Error(`Invalid config at ${snapshot.path}\n${issues}`);
|
||||
}
|
||||
const next = mutator(snapshot.config);
|
||||
@@ -48,10 +43,10 @@ export async function updateConfig(
|
||||
return next;
|
||||
}
|
||||
|
||||
export function resolveModelTarget(params: {
|
||||
raw: string;
|
||||
cfg: ClawdbotConfig;
|
||||
}): { provider: string; model: string } {
|
||||
export function resolveModelTarget(params: { raw: string; cfg: ClawdbotConfig }): {
|
||||
provider: string;
|
||||
model: string;
|
||||
} {
|
||||
const aliasIndex = buildModelAliasIndex({
|
||||
cfg: params.cfg,
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
@@ -82,9 +77,7 @@ export function normalizeAlias(alias: string): string {
|
||||
const trimmed = alias.trim();
|
||||
if (!trimmed) throw new Error("Alias cannot be empty.");
|
||||
if (!/^[A-Za-z0-9_.:-]+$/.test(trimmed)) {
|
||||
throw new Error(
|
||||
"Alias must use letters, numbers, dots, underscores, colons, or dashes.",
|
||||
);
|
||||
throw new Error("Alias must use letters, numbers, dots, underscores, colons, or dashes.");
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user