UNPKG

akm-cli

Version:

akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur

118 lines (117 loc) • 5.51 kB
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. /** * Shared collect-input / probe / derive-config helpers for the wizard's * connection steps (§4.6 dedup of the three near-identical steps in * connection.ts). Prompt copy, validation messages, and the probe's spinner * text are shared VERBATIM so the wizard's observable flow is unchanged; * each step keeps its own option assembly and branching. */ import * as p from "../../cli/clack.js"; import { probeLlmReachable } from "../../llm/client.js"; import { prompt, promptOrBack } from "../prompt.js"; // ── Derive-config ─────────────────────────────────────────────────────────── /** Standard chat-connection config shared by every provider branch. */ export function llmConnection(provider, endpoint, model, extra) { return { provider, endpoint, model, temperature: 0.3, ...(extra ?? {}), }; } /** The "Keep current: ..." select option appended when a connection exists. */ export function keepCurrentOption(current) { return { value: "keep", label: `Keep current: ${current.provider ?? current.endpoint}`, hint: current.model, }; } /** Hint text for the "LM Studio / local server" select option. */ export function lmStudioOptionHint(lmStudio) { return lmStudio?.available ? `${lmStudio.models.length} model${lmStudio.models.length === 1 ? "" : "s"} detected` : "http://localhost:1234"; } // ── Collect-input ─────────────────────────────────────────────────────────── /** Non-empty model-name text prompt shared by every provider branch. */ export async function promptModelName(opts) { return prompt(() => p.text({ message: opts.message ?? "Model name:", placeholder: opts.placeholder, ...(opts.defaultValue ? { defaultValue: opts.defaultValue } : {}), validate: (v) => (!v?.trim() ? "Model name cannot be empty" : undefined), })); } /** http(s)-validated endpoint text prompt shared by every endpoint branch. */ export async function promptEndpointUrl(opts) { const schemeMessage = opts.schemeMessage ?? "Must start with http:// or https://"; return prompt(() => p.text({ message: opts.message ?? "Endpoint URL:", placeholder: opts.placeholder, ...(opts.defaultValue ? { defaultValue: opts.defaultValue } : {}), validate: (v) => { if (!v?.trim()) return "Endpoint cannot be empty"; if (!v.startsWith("http://") && !v.startsWith("https://")) return schemeMessage; }, })); } /** * LM Studio model selection: pick from the detected models (with an * "Enter manually..." escape hatch) or fall back to a free-text prompt when * nothing was detected. */ export async function promptLmStudioModel(lmStudio, currentModel) { const lmsModels = lmStudio?.available && lmStudio.models.length > 0 ? lmStudio.models : []; if (lmsModels.length > 0) { const modelChoice = await prompt(() => p.select({ message: "Model name:", options: [ ...lmsModels.map((m) => ({ value: m, label: m })), { value: "__manual__", label: "Enter manually..." }, ], initialValue: currentModel && lmsModels.includes(currentModel) ? currentModel : lmsModels[0], })); if (modelChoice !== "__manual__") return modelChoice; } return promptModelName({ placeholder: currentModel ?? "local-model", defaultValue: currentModel }); } /** * Optional API-key ENV VAR NAME prompt (never the key value — saveConfig() * strips secrets). Escape backs out with `null`. */ export async function promptApiKeyEnvVarName() { return promptOrBack(() => p.text({ message: "API key environment variable name (optional):", placeholder: "CUSTOM_LLM_API_KEY", validate: (value) => value && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value) ? "Use an environment variable name, not a key value" : undefined, })); } // ── Probe ─────────────────────────────────────────────────────────────────── /** * Best-effort reachability probe — never blocks setup. Does NOT probe or * cache structured-output support: `chatCompletion` now attempts * `response_format: json_schema` fresh on every call that supplies a schema * and falls back once per call on a 4xx (see `llm/client.ts`), so there is * no verdict for setup to determine or persist here. */ export async function probeLlmConnection(llm) { const probeSpin = p.spinner(); probeSpin.start("Probing LLM connectivity..."); const probe = await probeLlmReachable(llm); if (probe.reachable) { probeSpin.stop("LLM reachable."); } else { probeSpin.stop("LLM not reachable."); p.log.warn(`Could not reach the LLM endpoint${probe.error ? ` (${probe.error})` : ""}. Configuration was saved; verify your endpoint and API key, then retry.`); } }