fix: address PR #111 review feedback
- Extract validateWorkspaceSegment to commands/init/validate.ts - Unify splitProviderModelRef in config/, used by both resolve-model and registry-normalize - Warn on missing models.default during parse (tag Z2KP9NWQ)
This commit is contained in:
@@ -13,19 +13,7 @@ import {
|
||||
templateTsconfigJson,
|
||||
} from "./templates.js";
|
||||
import type { CmdInitTemplateSuccess } from "./types.js";
|
||||
|
||||
function validateWorkspaceSegment(name: string): Result<void, string> {
|
||||
if (name.length === 0) {
|
||||
return err("workspace name must not be empty");
|
||||
}
|
||||
if (name === "." || name === "..") {
|
||||
return err("invalid workspace name");
|
||||
}
|
||||
if (name.includes("/") || name.includes("\\")) {
|
||||
return err("workspace name must not contain path separators");
|
||||
}
|
||||
return ok(undefined);
|
||||
}
|
||||
import { validateWorkspaceSegment } from "./validate.js";
|
||||
|
||||
function hasTemplatesWorkspaceGlob(workspaces: unknown): boolean {
|
||||
return Array.isArray(workspaces) && workspaces.includes("templates/*");
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { err, ok, type Result } from "@uncaged/workflow";
|
||||
|
||||
/** Validates a single path segment for workspace / template names (no separators, not `.` / `..`). */
|
||||
export function validateWorkspaceSegment(name: string): Result<void, string> {
|
||||
if (name.length === 0) {
|
||||
return err("workspace name must not be empty");
|
||||
}
|
||||
if (name === "." || name === "..") {
|
||||
return err("invalid workspace name");
|
||||
}
|
||||
if (name.includes("/") || name.includes("\\")) {
|
||||
return err("workspace name must not contain path separators");
|
||||
}
|
||||
return ok(undefined);
|
||||
}
|
||||
@@ -5,19 +5,7 @@ import { err, ok, type Result } from "@uncaged/workflow";
|
||||
|
||||
import { pathExists } from "../../fs-utils.js";
|
||||
import type { CmdInitWorkspaceSuccess } from "./types.js";
|
||||
|
||||
function validateWorkspaceSegment(name: string): Result<void, string> {
|
||||
if (name.length === 0) {
|
||||
return err("workspace name must not be empty");
|
||||
}
|
||||
if (name === "." || name === "..") {
|
||||
return err("invalid workspace name");
|
||||
}
|
||||
if (name.includes("/") || name.includes("\\")) {
|
||||
return err("workspace name must not contain path separators");
|
||||
}
|
||||
return ok(undefined);
|
||||
}
|
||||
import { validateWorkspaceSegment } from "./validate.js";
|
||||
|
||||
function rootPackageJson(workspaceName: string): string {
|
||||
return `${JSON.stringify(
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export { resolveModel } from "./resolve-model.js";
|
||||
export { splitProviderModelRef } from "./split-provider-model-ref.js";
|
||||
export type { ProviderConfig, ResolvedModel } from "./types.js";
|
||||
|
||||
@@ -1,22 +1,8 @@
|
||||
import type { WorkflowConfig } from "../registry/index.js";
|
||||
import { err, ok, type Result } from "../util/index.js";
|
||||
import { splitProviderModelRef } from "./split-provider-model-ref.js";
|
||||
import type { ResolvedModel } from "./types.js";
|
||||
|
||||
function splitProviderModelRef(
|
||||
ref: string,
|
||||
): Result<{ providerName: string; modelName: string }, string> {
|
||||
const idx = ref.indexOf("/");
|
||||
if (idx <= 0 || idx === ref.length - 1) {
|
||||
return err(`invalid model reference "${ref}": expected providerName/modelName`);
|
||||
}
|
||||
const providerName = ref.slice(0, idx);
|
||||
const modelName = ref.slice(idx + 1);
|
||||
if (providerName === "" || modelName === "") {
|
||||
return err(`invalid model reference "${ref}": expected providerName/modelName`);
|
||||
}
|
||||
return ok({ providerName, modelName });
|
||||
}
|
||||
|
||||
/** Resolves scene → provider endpoint + model using {@link WorkflowConfig.providers} and {@link WorkflowConfig.models}. */
|
||||
export function resolveModel(config: WorkflowConfig, scene: string): Result<ResolvedModel, string> {
|
||||
const models = config.models;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { err, ok, type Result } from "../util/index.js";
|
||||
|
||||
/** Parses `providerName/modelName` references used in {@link WorkflowConfig.models}. */
|
||||
export function splitProviderModelRef(
|
||||
ref: string,
|
||||
): Result<{ providerName: string; modelName: string }, string> {
|
||||
const idx = ref.indexOf("/");
|
||||
if (idx <= 0 || idx === ref.length - 1) {
|
||||
return err(`invalid model reference "${ref}": expected providerName/modelName`);
|
||||
}
|
||||
const providerName = ref.slice(0, idx);
|
||||
const modelName = ref.slice(idx + 1);
|
||||
if (providerName === "" || modelName === "") {
|
||||
return err(`invalid model reference "${ref}": expected providerName/modelName`);
|
||||
}
|
||||
return ok({ providerName, modelName });
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ProviderConfig } from "../config/index.js";
|
||||
import { err, ok, type Result } from "../util/index.js";
|
||||
import { type ProviderConfig, splitProviderModelRef } from "../config/index.js";
|
||||
import { createLogger, err, ok, type Result } from "../util/index.js";
|
||||
import type {
|
||||
WorkflowConfig,
|
||||
WorkflowHistoryEntry,
|
||||
@@ -7,6 +7,8 @@ import type {
|
||||
WorkflowRegistryFile,
|
||||
} from "./types.js";
|
||||
|
||||
const registryNormalizeLog = createLogger({ sink: { kind: "stderr" } });
|
||||
|
||||
function resolveRegistryApiKey(raw: string, ctx: string): Result<string, Error> {
|
||||
if (raw.startsWith("env:")) {
|
||||
const name = raw.slice("env:".length);
|
||||
@@ -22,22 +24,6 @@ function resolveRegistryApiKey(raw: string, ctx: string): Result<string, Error>
|
||||
return ok(raw);
|
||||
}
|
||||
|
||||
function parseModelProviderRef(
|
||||
ref: string,
|
||||
ctx: string,
|
||||
): Result<{ providerName: string; modelName: string }, Error> {
|
||||
const idx = ref.indexOf("/");
|
||||
if (idx <= 0 || idx === ref.length - 1) {
|
||||
return err(new Error(`${ctx}: expected providerName/modelName, got "${ref}"`));
|
||||
}
|
||||
const providerName = ref.slice(0, idx);
|
||||
const modelName = ref.slice(idx + 1);
|
||||
if (providerName === "" || modelName === "") {
|
||||
return err(new Error(`${ctx}: expected providerName/modelName, got "${ref}"`));
|
||||
}
|
||||
return ok({ providerName, modelName });
|
||||
}
|
||||
|
||||
function normalizeProviderEntry(name: string, entryRaw: unknown): Result<ProviderConfig, Error> {
|
||||
if (name === "") {
|
||||
return err(new Error("config.providers must not contain an empty provider name"));
|
||||
@@ -96,9 +82,9 @@ function normalizeModels(
|
||||
return err(new Error(`config.models.${scene} must be a non-empty string (provider/model)`));
|
||||
}
|
||||
const ctx = `config.models.${scene}`;
|
||||
const parsed = parseModelProviderRef(refRaw, ctx);
|
||||
const parsed = splitProviderModelRef(refRaw);
|
||||
if (!parsed.ok) {
|
||||
return parsed;
|
||||
return err(new Error(`${ctx}: ${parsed.error}`));
|
||||
}
|
||||
if (!providerKeys.has(parsed.value.providerName)) {
|
||||
return err(
|
||||
@@ -109,6 +95,12 @@ function normalizeModels(
|
||||
}
|
||||
models[scene] = refRaw;
|
||||
}
|
||||
if (!Object.hasOwn(models, "default")) {
|
||||
registryNormalizeLog(
|
||||
"Z2KP9NWQ",
|
||||
'registry config: models mapping has no "default" key; scenes without explicit model mappings may fail at resolveModel',
|
||||
);
|
||||
}
|
||||
return ok(models);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user