Merge ad6d4f6389 into c41ea252b0
This commit is contained in:
commit
760e003214
@ -3,6 +3,7 @@ import { createServer } from "node:http";
|
||||
import type { AddressInfo } from "node:net";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { WebSocket } from "ws";
|
||||
import { rawDataToString } from "../infra/ws.js";
|
||||
@ -202,6 +203,15 @@ describe("canvas host", () => {
|
||||
|
||||
it("serves the gateway-hosted A2UI scaffold", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-canvas-"));
|
||||
const a2uiDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "a2ui");
|
||||
const bundlePath = path.join(a2uiDir, "a2ui.bundle.js");
|
||||
let wroteBundle = false;
|
||||
try {
|
||||
await fs.stat(bundlePath);
|
||||
} catch {
|
||||
await fs.writeFile(bundlePath, "window.moltbotA2UI = {};", "utf8");
|
||||
wroteBundle = true;
|
||||
}
|
||||
|
||||
const server = await startCanvasHost({
|
||||
runtime: defaultRuntime,
|
||||
@ -227,6 +237,9 @@ describe("canvas host", () => {
|
||||
} finally {
|
||||
await server.close();
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
if (wroteBundle) {
|
||||
await fs.rm(bundlePath, { force: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -39,7 +39,7 @@ const AUTH_CHOICE_GROUP_DEFS: {
|
||||
value: "openai",
|
||||
label: "OpenAI",
|
||||
hint: "Codex OAuth + API key",
|
||||
choices: ["openai-codex", "openai-api-key"],
|
||||
choices: ["openai-codex", "openai-api-key", "openai-custom-endpoint"],
|
||||
},
|
||||
{
|
||||
value: "anthropic",
|
||||
@ -134,6 +134,11 @@ export function buildAuthChoiceOptions(params: {
|
||||
});
|
||||
options.push({ value: "chutes", label: "Chutes (OAuth)" });
|
||||
options.push({ value: "openai-api-key", label: "OpenAI API key" });
|
||||
options.push({
|
||||
value: "openai-custom-endpoint",
|
||||
label: "OpenAI-compatible endpoint",
|
||||
hint: "Use a private base URL + bearer token",
|
||||
});
|
||||
options.push({ value: "openrouter-api-key", label: "OpenRouter API key" });
|
||||
options.push({
|
||||
value: "ai-gateway-api-key",
|
||||
|
||||
@ -16,6 +16,34 @@ import {
|
||||
OPENAI_CODEX_DEFAULT_MODEL,
|
||||
} from "./openai-codex-model-default.js";
|
||||
|
||||
function readConfigEnvValue(cfg: { env?: Record<string, unknown> }, key: string): string | null {
|
||||
const env = cfg.env;
|
||||
if (!env) return null;
|
||||
const direct = env[key];
|
||||
if (typeof direct === "string" && direct.trim()) return direct.trim();
|
||||
const vars = env.vars as Record<string, unknown> | undefined;
|
||||
const fromVars = vars?.[key];
|
||||
if (typeof fromVars === "string" && fromVars.trim()) return fromVars.trim();
|
||||
return null;
|
||||
}
|
||||
|
||||
function setConfigEnvValue<T extends { env?: Record<string, unknown> }>(
|
||||
cfg: T,
|
||||
key: string,
|
||||
value: string,
|
||||
): T {
|
||||
const existing = cfg.env ?? {};
|
||||
const vars =
|
||||
existing.vars && typeof existing.vars === "object"
|
||||
? { ...(existing.vars as Record<string, string>) }
|
||||
: undefined;
|
||||
if (vars && Object.prototype.hasOwnProperty.call(vars, key)) {
|
||||
vars[key] = value;
|
||||
return { ...cfg, env: { ...existing, vars } } as T;
|
||||
}
|
||||
return { ...cfg, env: { ...existing, [key]: value, ...(vars ? { vars } : {}) } } as T;
|
||||
}
|
||||
|
||||
export async function applyAuthChoiceOpenAI(
|
||||
params: ApplyAuthChoiceParams,
|
||||
): Promise<ApplyAuthChoiceResult | null> {
|
||||
@ -24,6 +52,59 @@ export async function applyAuthChoiceOpenAI(
|
||||
authChoice = "openai-api-key";
|
||||
}
|
||||
|
||||
if (authChoice === "openai-custom-endpoint") {
|
||||
let nextConfig = params.config;
|
||||
const existingBaseUrl =
|
||||
readConfigEnvValue(nextConfig, "OPENAI_BASE_URL") ??
|
||||
process.env.OPENAI_BASE_URL?.trim() ??
|
||||
"";
|
||||
const existingToken =
|
||||
readConfigEnvValue(nextConfig, "OPENAI_API_KEY") ?? process.env.OPENAI_API_KEY?.trim() ?? "";
|
||||
|
||||
let baseUrl = existingBaseUrl;
|
||||
if (existingBaseUrl) {
|
||||
const useExisting = await params.prompter.confirm({
|
||||
message: `Use existing OPENAI_BASE_URL (${existingBaseUrl})?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (!useExisting) baseUrl = "";
|
||||
}
|
||||
if (!baseUrl) {
|
||||
const input = await params.prompter.text({
|
||||
message: "OpenAI-compatible base URL",
|
||||
placeholder: "https://api.example.com/v1",
|
||||
validate: (value) => (value?.trim() ? undefined : "Required"),
|
||||
});
|
||||
baseUrl = String(input ?? "").trim();
|
||||
}
|
||||
|
||||
let token = existingToken;
|
||||
if (existingToken) {
|
||||
const useExisting = await params.prompter.confirm({
|
||||
message: `Use existing OPENAI_API_KEY (${formatApiKeyPreview(existingToken)})?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (!useExisting) token = "";
|
||||
}
|
||||
if (!token) {
|
||||
const input = await params.prompter.text({
|
||||
message: "Bearer token",
|
||||
validate: validateApiKeyInput,
|
||||
});
|
||||
token = normalizeApiKeyInput(String(input));
|
||||
}
|
||||
|
||||
nextConfig = setConfigEnvValue(nextConfig, "OPENAI_BASE_URL", baseUrl);
|
||||
nextConfig = setConfigEnvValue(nextConfig, "OPENAI_API_KEY", token);
|
||||
process.env.OPENAI_BASE_URL = baseUrl;
|
||||
process.env.OPENAI_API_KEY = token;
|
||||
await params.prompter.note(
|
||||
"Saved OPENAI_BASE_URL and OPENAI_API_KEY in config env.",
|
||||
"OpenAI endpoint",
|
||||
);
|
||||
return { config: nextConfig };
|
||||
}
|
||||
|
||||
if (authChoice === "openai-api-key") {
|
||||
const envKey = resolveEnvApiKey("openai");
|
||||
if (envKey) {
|
||||
|
||||
@ -10,6 +10,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
|
||||
"codex-cli": "openai-codex",
|
||||
chutes: "chutes",
|
||||
"openai-api-key": "openai",
|
||||
"openai-custom-endpoint": "openai",
|
||||
"openrouter-api-key": "openrouter",
|
||||
"ai-gateway-api-key": "vercel-ai-gateway",
|
||||
"moonshot-api-key": "moonshot",
|
||||
|
||||
@ -11,6 +11,7 @@ export type AuthChoice =
|
||||
| "chutes"
|
||||
| "openai-codex"
|
||||
| "openai-api-key"
|
||||
| "openai-custom-endpoint"
|
||||
| "openrouter-api-key"
|
||||
| "ai-gateway-api-key"
|
||||
| "moonshot-api-key"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user