mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
89
packages/cli/src/commands/loop/run.test.ts
Normal file
89
packages/cli/src/commands/loop/run.test.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { buildLoopRunInput } from "./run.js";
|
||||
import type { LoopRunOptions } from "./types.js";
|
||||
|
||||
describe("buildLoopRunInput", () => {
|
||||
it("should parse provider/model from provider string", () => {
|
||||
const options: LoopRunOptions = {
|
||||
provider: "opencode/hy3-preview-free",
|
||||
model: undefined,
|
||||
verifyProvider: undefined,
|
||||
verifyModel: undefined,
|
||||
verify: undefined,
|
||||
verifyCheck: [],
|
||||
archive: false,
|
||||
name: undefined,
|
||||
sleep: undefined,
|
||||
maxIterations: undefined,
|
||||
maxTime: undefined,
|
||||
} as LoopRunOptions;
|
||||
|
||||
const result = buildLoopRunInput("test prompt", options);
|
||||
|
||||
expect(result.provider).toBe("opencode");
|
||||
expect(result.model).toBe("hy3-preview-free");
|
||||
});
|
||||
|
||||
it("should use provider only when no model in string", () => {
|
||||
const options = {
|
||||
provider: "opencode",
|
||||
model: undefined,
|
||||
verifyProvider: undefined,
|
||||
verifyModel: undefined,
|
||||
verify: undefined,
|
||||
verifyCheck: [],
|
||||
archive: false,
|
||||
name: undefined,
|
||||
sleep: undefined,
|
||||
maxIterations: undefined,
|
||||
maxTime: undefined,
|
||||
} as LoopRunOptions;
|
||||
|
||||
const result = buildLoopRunInput("test prompt", options);
|
||||
|
||||
expect(result.provider).toBe("opencode");
|
||||
expect(result.model).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should pass verifierProvider with model parsing", () => {
|
||||
const options = {
|
||||
provider: undefined,
|
||||
model: undefined,
|
||||
verifyProvider: "codex/gpt-5.4",
|
||||
verifyModel: undefined,
|
||||
verify: undefined,
|
||||
verifyCheck: [],
|
||||
archive: false,
|
||||
name: undefined,
|
||||
sleep: undefined,
|
||||
maxIterations: undefined,
|
||||
maxTime: undefined,
|
||||
} as LoopRunOptions;
|
||||
|
||||
const result = buildLoopRunInput("test prompt", options);
|
||||
|
||||
expect(result.verifierProvider).toBe("codex");
|
||||
expect(result.verifierModel).toBe("gpt-5.4");
|
||||
});
|
||||
|
||||
it("should prefer explicit model over parsed model", () => {
|
||||
const options = {
|
||||
provider: "opencode/hy3-preview-free",
|
||||
model: "other-model",
|
||||
verifyProvider: undefined,
|
||||
verifyModel: undefined,
|
||||
verify: undefined,
|
||||
verifyCheck: [],
|
||||
archive: false,
|
||||
name: undefined,
|
||||
sleep: undefined,
|
||||
maxIterations: undefined,
|
||||
maxTime: undefined,
|
||||
} as LoopRunOptions;
|
||||
|
||||
const result = buildLoopRunInput("test prompt", options);
|
||||
|
||||
expect(result.provider).toBe("opencode");
|
||||
expect(result.model).toBe("other-model");
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
} from "../../output/index.js";
|
||||
import { collectMultiple } from "../../utils/command-options.js";
|
||||
import { parseDuration } from "../../utils/duration.js";
|
||||
import { resolveProviderAndModel } from "../../utils/provider-model.js";
|
||||
import type { LoopDaemonClient, LoopRecord, LoopRunInput } from "./types.js";
|
||||
|
||||
export interface LoopRunRow {
|
||||
@@ -86,7 +87,8 @@ function parseMaxIterations(value: string | undefined): number | undefined {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function buildLoopRunInput(prompt: string, options: LoopRunOptions): LoopRunInput {
|
||||
// oxlint-disable complexity
|
||||
export function buildLoopRunInput(prompt: string, options: LoopRunOptions): LoopRunInput {
|
||||
const verifyPrompt = options.verify?.trim();
|
||||
if (options.verify !== undefined && !verifyPrompt) {
|
||||
throw {
|
||||
@@ -95,24 +97,54 @@ function buildLoopRunInput(prompt: string, options: LoopRunOptions): LoopRunInpu
|
||||
} satisfies CommandError;
|
||||
}
|
||||
|
||||
return {
|
||||
const result: LoopRunInput = {
|
||||
prompt,
|
||||
cwd: process.cwd(),
|
||||
...(options.provider ? { provider: options.provider } : {}),
|
||||
...(options.model?.trim() ? { model: options.model.trim() } : {}),
|
||||
...(options.verifyProvider ? { verifierProvider: options.verifyProvider } : {}),
|
||||
...(options.verifyModel?.trim() ? { verifierModel: options.verifyModel.trim() } : {}),
|
||||
...(verifyPrompt ? { verifyPrompt } : {}),
|
||||
...(options.verifyCheck && options.verifyCheck.length > 0
|
||||
? { verifyChecks: options.verifyCheck }
|
||||
: {}),
|
||||
...(options.archive ? { archive: true } : {}),
|
||||
...(options.name?.trim() ? { name: options.name.trim() } : {}),
|
||||
...(options.sleep ? { sleepMs: parseDuration(options.sleep) } : {}),
|
||||
...(options.maxIterations ? { maxIterations: parseMaxIterations(options.maxIterations) } : {}),
|
||||
...(options.maxTime ? { maxTimeMs: parseDuration(options.maxTime) } : {}),
|
||||
};
|
||||
|
||||
// Resolve provider/model
|
||||
if (options.provider) {
|
||||
const { provider, model } = resolveProviderAndModel({ provider: options.provider });
|
||||
if (provider) result.provider = provider;
|
||||
// Explicit --model takes precedence over parsed model
|
||||
if (options.model?.trim()) {
|
||||
result.model = options.model.trim();
|
||||
} else if (model) {
|
||||
result.model = model;
|
||||
}
|
||||
} else if (options.model?.trim()) {
|
||||
result.model = options.model.trim();
|
||||
}
|
||||
|
||||
// Resolve verifier provider/model
|
||||
if (options.verifyProvider) {
|
||||
const { provider, model } = resolveProviderAndModel({ provider: options.verifyProvider });
|
||||
if (provider) result.verifierProvider = provider;
|
||||
// Explicit --verify-model takes precedence over parsed model
|
||||
if (options.verifyModel?.trim()) {
|
||||
result.verifierModel = options.verifyModel.trim();
|
||||
} else if (model) {
|
||||
result.verifierModel = model;
|
||||
}
|
||||
} else if (options.verifyModel?.trim()) {
|
||||
result.verifierModel = options.verifyModel.trim();
|
||||
}
|
||||
|
||||
if (verifyPrompt) result.verifyPrompt = verifyPrompt;
|
||||
if (options.verifyCheck && options.verifyCheck.length > 0) {
|
||||
result.verifyChecks = options.verifyCheck;
|
||||
}
|
||||
if (options.archive) result.archive = true;
|
||||
if (options.name?.trim()) result.name = options.name.trim();
|
||||
if (options.sleep) result.sleepMs = parseDuration(options.sleep);
|
||||
if (options.maxIterations) {
|
||||
result.maxIterations = parseMaxIterations(options.maxIterations);
|
||||
}
|
||||
if (options.maxTime) result.maxTimeMs = parseDuration(options.maxTime);
|
||||
|
||||
return result;
|
||||
}
|
||||
// oxlint-enable complexity
|
||||
|
||||
export type LoopRunResult = SingleResult<LoopRunRow>;
|
||||
|
||||
|
||||
@@ -451,6 +451,10 @@ export interface WaitForChatMessagesOptions {
|
||||
export interface RunLoopOptions {
|
||||
prompt: string;
|
||||
cwd: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
verifierProvider?: string;
|
||||
verifierModel?: string;
|
||||
verifyPrompt?: string | null;
|
||||
verifyChecks?: string[];
|
||||
name?: string | null;
|
||||
@@ -3589,6 +3593,10 @@ export class DaemonClient {
|
||||
type: "loop/run",
|
||||
prompt: options.prompt,
|
||||
cwd: options.cwd,
|
||||
...(options.provider ? { provider: options.provider } : {}),
|
||||
...(options.model ? { model: options.model } : {}),
|
||||
...(options.verifierProvider ? { verifierProvider: options.verifierProvider } : {}),
|
||||
...(options.verifierModel ? { verifierModel: options.verifierModel } : {}),
|
||||
...(options.verifyPrompt ? { verifyPrompt: options.verifyPrompt } : {}),
|
||||
...(options.verifyChecks && options.verifyChecks.length > 0
|
||||
? { verifyChecks: options.verifyChecks }
|
||||
|
||||
Reference in New Issue
Block a user