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

85 lines (84 loc) • 4.61 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 atomic Zod schemas and small helpers used across the config-schema * modules. Extracted verbatim from the former `config-schema.ts` monolith — no * behavior change (see `./index` re-export barrel at `../config-schema.ts`). */ import { z } from "zod"; import { validateExtraParams } from "../../extra-params.js"; import { warnOnce } from "../../warn.js"; import { ENGINE_NAME_PATTERN_SOURCE } from "../engine-semantics.js"; /** Persisted config schema version. Package prerelease/patch versions do not change this value. */ export const CURRENT_CONFIG_VERSION = "0.9.0"; // ── Reusable atomic schemas ───────────────────────────────────────────────── /** Positive integer (used for tokens, timeouts, batch sizes). */ export const positiveInt = z.number().int().positive(); /** Non-negative finite number (used for scores, weights, days). */ export const nonNegativeNumber = z.number().finite().min(0); /** Non-empty string (rejects "" and whitespace-only). */ export const nonEmptyString = z .string() .min(1) .refine((v) => v.trim().length > 0, { message: "expected a non-empty string" }); /** HTTP(S) URL string. */ export const httpUrl = z.string().refine((v) => v.startsWith("http://") || v.startsWith("https://"), { message: "endpoint must start with http:// or https://", }); const ENGINE_NAME_PATTERN = new RegExp(ENGINE_NAME_PATTERN_SOURCE); export const ENV_REFERENCE_PATTERN = /^\$[A-Za-z_][A-Za-z0-9_]*$|^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/; /** `secret://<name>` — an apiKey reference into the akm secret store, resolved via `resolveSecretFromStore`. */ export const SECRET_STORE_REFERENCE_PATTERN = /^secret:\/\/(.+)$/; export const engineName = z .string() .max(63) .regex(ENGINE_NAME_PATTERN, "names must be lowercase kebab-case and must not begin with reserved akm-"); /** The two symbolic apiKey forms akm accepts: an env-var reference or a secret-store reference. Never matches a literal key. */ export function isApiKeyReference(value) { return ENV_REFERENCE_PATTERN.test(value) || SECRET_STORE_REFERENCE_PATTERN.test(value); } export function symbolicOrWarnApiKey(label) { return z.string().superRefine((value) => { if (isApiKeyReference(value)) return; warnOnce(`config:literal-api-key:${label}`, `A ${label} in config.json is a literal API key, not a $VAR/\${VAR}/secret:// reference; using it as configured. Prefer \`akm config set ...apiKey '$VAR'\` (with the corresponding env var set) or \`secret://<name>\` (with \`akm secret set <name> ...\`) — see docs/reference/data-and-telemetry.md.`); }); } export const chatCompletionsEndpoint = z.string().superRefine((value, ctx) => { let url; try { url = new URL(value); } catch { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "endpoint must be a complete URL" }); return; } if (url.protocol !== "http:" && url.protocol !== "https:") { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "endpoint must use http:// or https://" }); return; } if (url.username || url.password) { warnOnce(`chatCompletionsEndpoint:userinfo:${value}`, `Config endpoint "${value}" embeds a username/password; consider moving the credential to the engine's apiKey field instead.`); } if (!url.pathname.endsWith("/chat/completions")) { warnOnce(`chatCompletionsEndpoint:path:${value}`, `Config endpoint "${value}" does not end in /chat/completions; using it as configured.`); } }); export const ExtraParamsSchema = z.record(z.unknown()).superRefine((value, ctx) => { for (const issue of validateExtraParams(value)) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: issue.path, message: issue.message }); } }); // ── Shared connection/invocation building blocks ──────────────────────────── export const LlmInvocationOverridesSchema = z .object({ temperature: z.number().finite().optional(), maxTokens: positiveInt.optional(), supportsJsonSchema: z.boolean().optional(), extraParams: ExtraParamsSchema.optional(), contextLength: positiveInt.optional(), enableThinking: z.boolean().optional(), reasoningEffort: nonEmptyString.optional(), }) .passthrough();